Agent Step Result Normalizer
agent.step_result_normalize
Overview
This advises and does not act: it reshapes a result you already have and calls nothing. Whatever a step returned — an object, an array, a bare string, a number, null — comes back as the same envelope, so an agent branches on one shape instead of one shape per tool. Status is derived by published rules for the profile you pick, and it is never guessed upwards: a payload the profile does not recognise becomes unknown, because inferring success is exactly how an agent carries on after a step that failed. Every ceiling is applied while walking the payload rather than after copying it, so a 2 MB result never becomes a 2 MB envelope, and each reduction is flagged so you know the envelope is partial. redactKeys removes values by exact field name as a convenience for names you already know; it is not a scanner, and security.detect_secrets and security.redact_secrets are the skills for that.
An agent that branches on each tool's own result shape grows a special case per tool, and the worst of those special cases is the one that reads an unfamiliar payload as success and carries on. One envelope with a status that falls back to unknown removes both problems, and bounding the payload keeps a large result from filling the context window.
Reliability
Fully deterministic: the same payload and the same ceilings always produce the same envelope, and field order inside the payload is preserved. Status never rises on a guess — an unrecognised payload is unknown, not success. Failures: RESULT_PAYLOAD_TOO_LARGE means the payload serialises past 2000000 characters, which is the most one request body can carry it in — bound it at the source before sending it; RESULT_PROFILE_UNSUPPORTED means profile named something outside the five published values, and an unknown profile is refused rather than treated as the default — correct the name and retry; RESULT_LIMITS_INVALID means maxTextChars, maxItems, maxDepth, stepId or tool fell outside its published range — bring the value inside the range and retry; RESULT_OUTPUT_TOO_LARGE means the envelope itself would pass 262144 characters at the ceilings you chose, which needs a very high maxTextChars — lower maxTextChars or maxItems and retry; RESULT_REDACT_KEYS_INVALID means redactKeys held more than 200 names, a non-string entry, or an entry longer than 256 characters — fix the list and retry.
Context footprint
The envelope is bounded by construction: with the defaults it stays in single-digit kilobytes no matter how large the payload was, and it can never pass 262144 characters. metrics.payloadChars records the original size so you can see how much was left behind. A successful call costs 1 execution credit.
When to use it
Immediately after a step returns, before the result reaches the agent's branching logic or its prompt.
When the payload must survive intact, or when the real requirement is removing secrets rather than shaping a result.
Scope
Non-goals
- Does not call the tool that produced the payload, or any other tool
- Does not scan for secrets or personal data — redactKeys matches names only
- Does not repair a malformed payload, only describe and bound it
- Does not infer success from a payload its profile cannot read
- Does not keep or cache the payload after the answer is returned
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response may contain submitted or transformed content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| toolResult | object | Yes | Opaque tool payload accepted as any JSON value including null; send the field even when its value is null. |
| profile | string | No | Status-derivation profile; omit it for SQUILLIFY. Published values: SQUILLIFY, HTTP, ERROR_ENVELOPE, GENERIC, TEXT. An unrecognised profile is RESULT_PROFILE_UNSUPPORTED. |
| stepId | string | No | Bounded correlation or tool label copied into the envelope unchanged; omit it when unused. |
| tool | string | No | Bounded correlation or tool label copied into the envelope unchanged; omit it when unused. |
| maxTextChars | integer | No | String ceiling applied while walking; omit it for 4000. |
| maxItems | integer | No | Array-entry ceiling applied while walking; omit it for 50. |
| maxDepth | integer | No | Walk depth ceiling applied while walking; omit it for 6. |
| includeRaw | boolean | No | Whether a fitting serialised payload is echoed; omit it for false. |
| redactKeys | array | No | Bounded array of 0 through 200 exact field names to redact; omit it for none. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| status | string | Yes | Derived envelope status, never guessed upwards to success. |
| stepId | string | No | Copied correlation id, present only when you supplied one. |
| tool | string | No | Copied tool name, present only when you supplied one. |
| summary | string | Yes | One-line shape summary of the bounded payload. |
| data | object | Yes | Bounded object projection of the payload; additional properties preserve remaining fields after ceilings — unconstrained RFC 8259 JSON items. |
| text | string | No | Extracted textual content, present only when the payload or a recognised field holds text. |
| items | array | Yes | Bounded collection projection of 0 through 1000 items taken from the payload or a recognised collection field. |
| error | object | No | Closed normalized error data with a stable code and optional message; its presence does not make this successful normalization invocation fail. |
| metrics | object | Yes | Closed payload-size metrics captured before truncation, with payloadChars, depth and itemCount. |
| truncation | object | Yes | Closed truncation flags naming every ceiling that actually cut the envelope. |
| profile | string | Yes | Resolved profile that decided the status, in lower case. |
| raw | string | No | Serialised payload, present only when includeRaw is true and it fits maxTextChars. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"agent.step_result_normalize","input":{"toolResult":{"slug":"weekly-report"},"profile":"SQUILLIFY"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("agent.step_result_normalize", {"toolResult":{"error":{"message":"The file reference is not accessible with the current credentials.","code":"FILE_REF_NOT_ACCESSIBLE"},"requestId":"req-8813"},"redactKeys":["requestId"],"stepId":"s-3","tool":"file.read"});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| RESULT_PAYLOAD_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| RESULT_PROFILE_UNSUPPORTED | 400 | No | The request payload failed this skill's input validation. |
| RESULT_LIMITS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| RESULT_OUTPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| RESULT_REDACT_KEYS_INVALID | 400 | No | The request payload failed this skill's input validation. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not call the tool that produced the payload, or any other tool
- Does not scan for secrets or personal data — redactKeys matches names only
- Does not repair a malformed payload, only describe and bound it
- Does not infer success from a payload its profile cannot read
- Does not keep or cache the payload after the answer is returned
- Removing secrets or personal data before storage
- Keeping a payload intact — this deliberately bounds and drops
- Deciding what to do next, which is the agent's job
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "agent.step_result_normalize",
"input": {
"toolResult": {
"error": {
"message": "The file reference is not accessible with the current credentials.",
"code": "FILE_REF_NOT_ACCESSIBLE"
},
"requestId": "req-8813"
},
"redactKeys": [
"requestId"
],
"stepId": "s-3",
"tool": "file.read"
}
}Examples
A platform result with a nested error envelope, normalised to status error
{
"toolResult": {
"error": {
"message": "The file reference is not accessible with the current credentials.",
"code": "FILE_REF_NOT_ACCESSIBLE"
},
"requestId": "req-8813"
},
"redactKeys": [
"requestId"
],
"stepId": "s-3",
"tool": "file.read"
}{
"status": "error",
"stepId": "s-3",
"data": {
"error": {
"message": "The file reference is not accessible with the current credentials.",
"code": "FILE_REF_NOT_ACCESSIBLE"
},
"requestId": "<removed>"
},
"metrics": {
"payloadChars": 146,
"depth": 2,
"itemCount": 0
},
"truncation": {
"textTruncated": false,
"itemsTruncated": false,
"depthTruncated": false,
"fieldsDropped": 0
},
"tool": "file.read",
"items": [],
"summary": "2 fields",
"error": {
"code": "FILE_REF_NOT_ACCESSIBLE",
"message": "The file reference is not accessible with the current credentials."
},
"profile": "squillify"
}An HTTP 404 mapped to client_error, with the correlation fields copied through
{
"toolResult": {
"body": {
"invoice": "inv-9912",
"code": "NOT_FOUND"
},
"statusCode": 404,
"message": "No such invoice"
},
"stepId": "s-7",
"tool": "billing.fetch_invoice",
"profile": "HTTP"
}{
"data": {
"body": {
"invoice": "inv-9912",
"code": "NOT_FOUND"
},
"statusCode": 404,
"message": "No such invoice"
},
"summary": "3 fields, 15 characters of text",
"truncation": {
"textTruncated": false,
"itemsTruncated": false,
"depthTruncated": false,
"fieldsDropped": 0
},
"status": "client_error",
"tool": "billing.fetch_invoice",
"error": {
"code": "HTTP_CLIENT_ERROR",
"message": "No such invoice"
},
"stepId": "s-7",
"text": "No such invoice",
"profile": "http",
"metrics": {
"payloadChars": 95,
"depth": 2,
"itemCount": 0
},
"items": []
}Recommended use cases
- Give an agent loop one result shape to branch on across many tools
- Cut a multi-megabyte tool result down to something a prompt can hold
- Turn a raw HTTP response into a status an agent can act on
Avoid for
- Removing secrets or personal data before storage
- Keeping a payload intact — this deliberately bounds and drops
- Deciding what to do next, which is the agent's job
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.