Stdin JSON Agent Protocol & Strict Exit Code Semantics #32

Closed
opened 2026-09-22 17:58:59 +00:00 by yuris · 0 comments
Owner

Summary

Implement the robust CLI input/output transport and semantic process exit code system for automated agents (OMP, Hermes) and human operators. This includes streaming JSON payloads via standard input (--input -) to eliminate argument vector data leakage, enforcing clean stdout/stderr channel separation (stdout contains strictly a single JSON envelope when --json is supplied), and mapping semantic response states to precise POSIX exit codes (notably exit code 3 for required clarification/approval and exit code 6 for partial, pending, or uncertain outcomes).


Background & Technical Rationale

The Process Boundary & Data Leakage Prevention

Automated agent frameworks frequently invoke CLI tools through child processes (execve / subprocesses). When tools accept structured payloads via command-line arguments (e.g. --title "My Secret Task" or --json '{"title":"..."}'), sensitive user data, private calendar events, and credentials leak into:

  1. The global process table (/bin/ps, /proc/<pid>/cmdline), accessible by other users and monitoring daemons on the host.
  2. Shell history files (.bash_history, .zsh_history).
  3. System auditing logs (e.g. Linux auditd).

To ensure strict privacy and operational safety, the life CLI requires all complex, structured, or sensitive mutation bodies to be passed through standard input via --input -. The stdin stream is read with an upper size bound (1 MB default) to prevent memory exhaustion from runaway processes.

Single Envelope Channel Separation

When --json is passed, the CLI process must guarantee that stdout emits exclusively valid JSON consisting of exactly one top-level envelope. Diagnostics, informational logging, tracing spans, and connection warnings must be routed strictly to stderr. This prevents agent JSON parsers from choking on interspersed log lines.

Semantic Exit Codes vs HTTP Status Mapping

A standard HTTP client typically maps HTTP 200 to exit code 0 and all HTTP 4xx/5xx errors to exit code 1. This is fundamentally inadequate for automated agent orchestration:

  • An HTTP 200 response may convey status: "needs_clarification" or status: "partial". Treating this as exit 0 causes agents to assume total success and proceed incorrectly.
  • An HTTP 403 response may indicate that explicit user approval is required before applying a plan. Treating this as generic permission denied (exit 1) prevents the agent from initiating the required user review flow.
  • A network timeout during a mutation means the remote state is unknown. Failing with exit 1 might prompt the agent to retry blindly, causing duplicate tasks or corrupted calendar blocks.

Therefore, the life CLI evaluates the semantic outcome before applying generic HTTP mappings:

  • Exit 3 (Clarification/Approval Required): Explicitly tells the agent orchestration loop to pause, present options to the user, and obtain guidance.
  • Exit 6 (Partial/Pending/Unknown): Explicitly signals that work is incomplete or uncertain. The CLI outputs the stable operation_id to stdout so the agent can investigate via life operation get <OP_ID> --json rather than re-running the command or declaring failure.

Exact Technical References & Specifications

1. Exit Code Mapping Contract

From docs/technical/cli-api.md (lines 11–22):

Exit Meaning HTTP mapping
0 Complete success 200/201
2 Invalid input/configuration 400/413/422
3 Clarification or approval required 200 with needs_clarification; 403 approval_required
4 Conflict 409/412
5 Dependency unavailable 503
6 Pending, partial or unknown outcome 202 or 200 with partial
1 Other failure, including authentication/authorization 401/403/500

Normative Rule:

"CLI chooses semantic outcome before generic HTTP mapping: a response with partial remote progress exits 6 even if the underlying step encountered a conflict. Client timeout after transmitting a write exits 6 with the locally known operation ID; it must not fabricate a server request ID (use a client-generated request ID sent in X-Request-ID)."

2. Common Envelope Structure

From docs/technical/cli-api.md (lines 7–10):

{
  "schema_version": 1,
  "request_id": "req_01HPX7K9Z...",
  "status": "ok | needs_clarification | partial | error",
  "data": { ... },
  "warnings": [
    {
      "code": "warning_code",
      "message": "Human readable warning message",
      "collection_id": "personal",
      "item_id": "itm_optional"
    }
  ],
  "error": null
}

When error is present:

{
  "code": "error_code",
  "message": "Detailed error explanation",
  "retryable": false,
  "operation_id": "op_optional_id"
}

3. Illustrative Response Envelopes & Exit Scenarios

From docs/technical/cli-api.md (lines 169–174) & docs/technical/verification.md (lines 103–109):

Scenario A: Partial Proposal Application (HTTP 200, Exit 6)

{
  "schema_version": 1,
  "request_id": "req_demo_apply",
  "status": "partial",
  "data": {
    "operation_id": "op_demo_apply",
    "state": "needs_reconciliation",
    "steps": [
      {"step_id": "step_a", "status": "applied", "etag": "\"block-v2\""},
      {"step_id": "step_b", "status": "failed", "code": "etag_conflict"},
      {"step_id": "step_c", "status": "pending"}
    ]
  },
  "warnings": [],
  "error": {
    "code": "partial_apply",
    "message": "One step applied; repair requires a newly reviewed proposal",
    "retryable": false,
    "operation_id": "op_demo_apply"
  }
}

Scenario B: Remote Outcome Uncertain / Timeout (HTTP 200/504/Timeout, Exit 6)

{
  "schema_version": 1,
  "request_id": "req_demo_unknown",
  "status": "partial",
  "data": {
    "operation_id": "op_demo_unknown",
    "operation_state": "unknown"
  },
  "warnings": [],
  "error": {
    "code": "operation_unknown",
    "message": "Remote outcome uncertain; inspect operation before any retry",
    "retryable": false,
    "operation_id": "op_demo_unknown"
  }
}

Scenario C: Baseline Conflict (HTTP 412, Exit 4)

{
  "schema_version": 1,
  "request_id": "req_demo_conflict",
  "status": "error",
  "data": {
    "operation_id": "op_demo_stale",
    "operation_state": "failed"
  },
  "warnings": [],
  "error": {
    "code": "etag_conflict",
    "message": "Item changed; reread before deciding",
    "retryable": false,
    "operation_id": "op_demo_stale"
  }
}

4. Normative Error Identifiers

From docs/technical/cli-api.md (lines 108–127):

  • invalid_input, configuration_invalid -> Exit 2
  • unauthenticated, forbidden -> Exit 1
  • approval_required -> Exit 3
  • clarification_required -> Exit 3
  • not_found -> Exit 2 or 1
  • etag_conflict -> Exit 4
  • idempotency_conflict -> Exit 4
  • proposal_expired -> Exit 2 (HTTP 400); proposal_not_applicable -> Exit 4 (HTTP 409 conflict when re-applying partially applied proposals under fresh keys, Scenario R15) or Exit 2
  • dependency_unavailable -> Exit 5
  • calendar_unsupported -> Exit 2
  • limit_exceeded, cursor_expired -> Exit 2
  • journal_unavailable -> Exit 5
  • operation_unknown, needs_reconciliation -> Exit 6
  • partial_apply, context_pending -> Exit 6
  • judgment_unavailable, judgment_invalid -> Exit 5 or 3
  • internal_error -> Exit 1

Formal Citations

  • docs/technical/cli-api.md:
    • Section "Transport and common contract" (lines 5–22)
    • Table "Exit code contract" (lines 11–20)
    • Section "Command-to-route table" (lines 23–58)
    • Section "Idempotency and approval" (lines 59–68)
    • Table "Error identifiers" (lines 106–127)
    • Section "Complete fictional examples" (lines 129–185)
  • docs/technical/agent-skill.md:
    • Section "Writes and approval" (lines 40–55)
    • Section "Ambiguity and failure" (lines 56–65)
  • docs/technical/verification.md:
    • Table "Recovery scenarios", Scenarios R08, R09 (lines 94–95, 103–109)
    • Table "Integration scenarios", Scenario I10 (lines 124, 156)

What to Build

1. Stdin Payload Streaming Reader (src/cli/input.rs)

Implement the standard input reader for command invocations:

  • When --input - is passed, read std::io::stdin() up to a hard limit of 1,048,576 bytes (1 MB).
  • If the stream exceeds 1 MB, terminate immediately with exit code 2 (invalid_input) and write an error envelope to stdout.
  • Parse the input as serde_json::Value. If the JSON is malformed, terminate with exit code 2 and descriptive JSON error envelope.
  • If --input specifies a file path (for local testing), read that file under the same 1 MB constraint.
  • Prohibit reading sensitive request payloads from command-line arguments.

2. Standard JSON Output & Diagnostic Writer (src/cli/output.rs)

  • Guarantee that all CLI commands executed with --json:
    • Emit exactly one valid JSON serialization of ResponseEnvelope<T> to io::stdout().
    • Append a single trailing newline (\n).
    • Flush stdout cleanly before process exit.
  • Direct all tracing events, debug messages, and network status warnings to io::stderr().

3. Semantic Exit Code Resolver (src/cli/exit_code.rs)

Implement the resolver converting API HTTP responses or client execution errors into semantic exit codes:

pub fn resolve_exit_code(envelope: &ResponseEnvelope<Value>, http_status: StatusCode) -> i32 {
    // 1. Semantic outcome check overrides raw HTTP status
    match envelope.status {
        EnvelopeStatus::NeedsClarification => return 3,
        EnvelopeStatus::Partial => return 6,
        EnvelopeStatus::Ok => {
            if http_status == StatusCode::ACCEPTED {
                return 6;
            }
            return 0;
        }
        EnvelopeStatus::Error => {
            if let Some(ref err) = envelope.error {
                match err.code.as_str() {
                    "approval_required" | "clarification_required" => return 3,
                    "etag_conflict" | "idempotency_conflict" => return 4,
                    "dependency_unavailable" | "journal_unavailable" | "judgment_unavailable" => return 5,
                    "operation_unknown" | "partial_apply" | "needs_reconciliation" => return 6,
                    "invalid_input" | "configuration_invalid" | "calendar_unsupported" | "limit_exceeded" => return 2,
                    _ => {}
                }
            }
        }
    }
    // 2. Fallback to HTTP status mapping if not explicitly mapped by error code
    match http_status.as_u16() {
        200..=201 => 0,
        202 => 6,
        400 | 413 | 422 => 2,
        403 => 1, // Generic forbidden (approval_required handled above)
        409 | 412 => 4,
        503 => 5,
        _ => 1,
    }
}

4. Client Timeout & Local Operation Recovery (src/cli/client.rs)

  • For all state-changing commands, generate a UUIDv7 / high-entropy operation ID if --operation-id was not explicitly supplied.
  • Transmit the operation ID as the Idempotency-Key header and client request ID as X-Request-ID.
  • If the HTTP connection drops, times out, or receives a 504 Gateway Timeout after transmitting the request:
    • Construct a local synthetic envelope with status: "partial", error.code: "operation_unknown", and include operation_id.
    • Print the envelope to stdout.
    • Exit the process with exit code 6.

Acceptance Criteria

Observable Assertions & Verification

  • Supplying --input - consumes JSON from standard input, parsing payloads up to 1 MB without error.
  • Providing input larger than 1 MB aborts with exit code 2 and an invalid_input error envelope.
  • When invoked with --json, stdout emits strictly one parseable JSON object matching the ResponseEnvelope schema, with all tracing/diagnostic messages routed to stderr.
  • Inspecting /proc/<pid>/cmdline or ps aux during execution of life task add --input - reveals zero task titles, descriptions, or bearer tokens.
  • API responses with status: "needs_clarification" or error.code: "approval_required" terminate the CLI process with exit code 3 (Scenario I10).
  • API responses with status: "partial" or error.code: "partial_apply" terminate with exit code 6 (Scenario I10, R09).
  • Simulating a client network timeout during a write command causes the CLI to emit an operation_unknown error envelope containing the generated operation_id to stdout and exit with code 6.
  • HTTP 412 Precondition Failed (etag_conflict) terminates with exit code 4.
  • HTTP 503 Service Unavailable (dependency_unavailable) terminates with exit code 5.
  • Successful commands terminate cleanly with exit code 0.

Notes

  • Real defects fixed by the audit: six error codes that fell through to exit 1 (clarification/unknown/reconciliation/partial-apply/context-pending/limit-exceeded), a non-normative timeout envelope (now partial + operation_unknown + operation id, exit 6), an unbounded stdin body (now 1 MiB), credentials reachable via --token/LIFE_TOKEN (removed), undocumented argv payload flags (removed), and warnings printed to stdout on the human path (now stderr).
  • Ten hand-rolled body readers and eight operation-key generators collapsed into shared helpers.
  • The skill's limit_exceeded/timeout/event cancel entries were corrected in the same commit.
  • life event cancel is documented in the contract but has no CLI verb or route: filed as a follow-up rather than left implied.
  • No dependency added.

Blocked by

  • #031: Portable Agent Skill Package & Progressive Disclosure
## Summary Implement the robust CLI input/output transport and semantic process exit code system for automated agents (OMP, Hermes) and human operators. This includes streaming JSON payloads via standard input (`--input -`) to eliminate argument vector data leakage, enforcing clean stdout/stderr channel separation (stdout contains strictly a single JSON envelope when `--json` is supplied), and mapping semantic response states to precise POSIX exit codes (notably exit code 3 for required clarification/approval and exit code 6 for partial, pending, or uncertain outcomes). --- ## Background & Technical Rationale ### The Process Boundary & Data Leakage Prevention Automated agent frameworks frequently invoke CLI tools through child processes (`execve` / subprocesses). When tools accept structured payloads via command-line arguments (e.g. `--title "My Secret Task"` or `--json '{"title":"..."}'`), sensitive user data, private calendar events, and credentials leak into: 1. The global process table (`/bin/ps`, `/proc/<pid>/cmdline`), accessible by other users and monitoring daemons on the host. 2. Shell history files (`.bash_history`, `.zsh_history`). 3. System auditing logs (e.g. Linux `auditd`). To ensure strict privacy and operational safety, the `life` CLI requires all complex, structured, or sensitive mutation bodies to be passed through standard input via `--input -`. The stdin stream is read with an upper size bound (1 MB default) to prevent memory exhaustion from runaway processes. ### Single Envelope Channel Separation When `--json` is passed, the CLI process must guarantee that `stdout` emits **exclusively valid JSON consisting of exactly one top-level envelope**. Diagnostics, informational logging, tracing spans, and connection warnings must be routed strictly to `stderr`. This prevents agent JSON parsers from choking on interspersed log lines. ### Semantic Exit Codes vs HTTP Status Mapping A standard HTTP client typically maps HTTP 200 to exit code 0 and all HTTP 4xx/5xx errors to exit code 1. This is fundamentally inadequate for automated agent orchestration: - An HTTP 200 response may convey `status: "needs_clarification"` or `status: "partial"`. Treating this as exit 0 causes agents to assume total success and proceed incorrectly. - An HTTP 403 response may indicate that explicit user approval is required before applying a plan. Treating this as generic permission denied (exit 1) prevents the agent from initiating the required user review flow. - A network timeout during a mutation means the remote state is unknown. Failing with exit 1 might prompt the agent to retry blindly, causing duplicate tasks or corrupted calendar blocks. Therefore, the `life` CLI evaluates the **semantic outcome** before applying generic HTTP mappings: - **Exit 3 (Clarification/Approval Required):** Explicitly tells the agent orchestration loop to pause, present options to the user, and obtain guidance. - **Exit 6 (Partial/Pending/Unknown):** Explicitly signals that work is incomplete or uncertain. The CLI outputs the stable `operation_id` to stdout so the agent can investigate via `life operation get <OP_ID> --json` rather than re-running the command or declaring failure. --- ## Exact Technical References & Specifications ### 1. Exit Code Mapping Contract From `docs/technical/cli-api.md` (lines 11–22): | Exit | Meaning | HTTP mapping | |---|---|---| | 0 | Complete success | 200/201 | | 2 | Invalid input/configuration | 400/413/422 | | 3 | Clarification or approval required | 200 with `needs_clarification`; 403 `approval_required` | | 4 | Conflict | 409/412 | | 5 | Dependency unavailable | 503 | | 6 | Pending, partial or unknown outcome | 202 or 200 with `partial` | | 1 | Other failure, including authentication/authorization | 401/403/500 | *Normative Rule:* > "CLI chooses semantic outcome before generic HTTP mapping: a response with partial remote progress exits 6 even if the underlying step encountered a conflict. Client timeout after transmitting a write exits 6 with the locally known operation ID; it must not fabricate a server request ID (use a client-generated request ID sent in `X-Request-ID`)." ### 2. Common Envelope Structure From `docs/technical/cli-api.md` (lines 7–10): ```json { "schema_version": 1, "request_id": "req_01HPX7K9Z...", "status": "ok | needs_clarification | partial | error", "data": { ... }, "warnings": [ { "code": "warning_code", "message": "Human readable warning message", "collection_id": "personal", "item_id": "itm_optional" } ], "error": null } ``` When `error` is present: ```json { "code": "error_code", "message": "Detailed error explanation", "retryable": false, "operation_id": "op_optional_id" } ``` ### 3. Illustrative Response Envelopes & Exit Scenarios From `docs/technical/cli-api.md` (lines 169–174) & `docs/technical/verification.md` (lines 103–109): **Scenario A: Partial Proposal Application (HTTP 200, Exit 6)** ```json { "schema_version": 1, "request_id": "req_demo_apply", "status": "partial", "data": { "operation_id": "op_demo_apply", "state": "needs_reconciliation", "steps": [ {"step_id": "step_a", "status": "applied", "etag": "\"block-v2\""}, {"step_id": "step_b", "status": "failed", "code": "etag_conflict"}, {"step_id": "step_c", "status": "pending"} ] }, "warnings": [], "error": { "code": "partial_apply", "message": "One step applied; repair requires a newly reviewed proposal", "retryable": false, "operation_id": "op_demo_apply" } } ``` **Scenario B: Remote Outcome Uncertain / Timeout (HTTP 200/504/Timeout, Exit 6)** ```json { "schema_version": 1, "request_id": "req_demo_unknown", "status": "partial", "data": { "operation_id": "op_demo_unknown", "operation_state": "unknown" }, "warnings": [], "error": { "code": "operation_unknown", "message": "Remote outcome uncertain; inspect operation before any retry", "retryable": false, "operation_id": "op_demo_unknown" } } ``` **Scenario C: Baseline Conflict (HTTP 412, Exit 4)** ```json { "schema_version": 1, "request_id": "req_demo_conflict", "status": "error", "data": { "operation_id": "op_demo_stale", "operation_state": "failed" }, "warnings": [], "error": { "code": "etag_conflict", "message": "Item changed; reread before deciding", "retryable": false, "operation_id": "op_demo_stale" } } ``` ### 4. Normative Error Identifiers From `docs/technical/cli-api.md` (lines 108–127): - `invalid_input`, `configuration_invalid` -> Exit 2 - `unauthenticated`, `forbidden` -> Exit 1 - `approval_required` -> Exit 3 - `clarification_required` -> Exit 3 - `not_found` -> Exit 2 or 1 - `etag_conflict` -> Exit 4 - `idempotency_conflict` -> Exit 4 - `proposal_expired` -> Exit 2 (HTTP 400); `proposal_not_applicable` -> Exit 4 (HTTP 409 conflict when re-applying partially applied proposals under fresh keys, Scenario R15) or Exit 2 - `dependency_unavailable` -> Exit 5 - `calendar_unsupported` -> Exit 2 - `limit_exceeded`, `cursor_expired` -> Exit 2 - `journal_unavailable` -> Exit 5 - `operation_unknown`, `needs_reconciliation` -> Exit 6 - `partial_apply`, `context_pending` -> Exit 6 - `judgment_unavailable`, `judgment_invalid` -> Exit 5 or 3 - `internal_error` -> Exit 1 --- ## Formal Citations - `docs/technical/cli-api.md`: - Section "Transport and common contract" (lines 5–22) - Table "Exit code contract" (lines 11–20) - Section "Command-to-route table" (lines 23–58) - Section "Idempotency and approval" (lines 59–68) - Table "Error identifiers" (lines 106–127) - Section "Complete fictional examples" (lines 129–185) - `docs/technical/agent-skill.md`: - Section "Writes and approval" (lines 40–55) - Section "Ambiguity and failure" (lines 56–65) - `docs/technical/verification.md`: - Table "Recovery scenarios", Scenarios R08, R09 (lines 94–95, 103–109) - Table "Integration scenarios", Scenario I10 (lines 124, 156) --- ## What to Build ### 1. Stdin Payload Streaming Reader (`src/cli/input.rs`) Implement the standard input reader for command invocations: - When `--input -` is passed, read `std::io::stdin()` up to a hard limit of 1,048,576 bytes (1 MB). - If the stream exceeds 1 MB, terminate immediately with exit code 2 (`invalid_input`) and write an error envelope to stdout. - Parse the input as `serde_json::Value`. If the JSON is malformed, terminate with exit code 2 and descriptive JSON error envelope. - If `--input` specifies a file path (for local testing), read that file under the same 1 MB constraint. - Prohibit reading sensitive request payloads from command-line arguments. ### 2. Standard JSON Output & Diagnostic Writer (`src/cli/output.rs`) - Guarantee that all CLI commands executed with `--json`: - Emit exactly one valid JSON serialization of `ResponseEnvelope<T>` to `io::stdout()`. - Append a single trailing newline (`\n`). - Flush stdout cleanly before process exit. - Direct all `tracing` events, debug messages, and network status warnings to `io::stderr()`. ### 3. Semantic Exit Code Resolver (`src/cli/exit_code.rs`) Implement the resolver converting API HTTP responses or client execution errors into semantic exit codes: ```rust pub fn resolve_exit_code(envelope: &ResponseEnvelope<Value>, http_status: StatusCode) -> i32 { // 1. Semantic outcome check overrides raw HTTP status match envelope.status { EnvelopeStatus::NeedsClarification => return 3, EnvelopeStatus::Partial => return 6, EnvelopeStatus::Ok => { if http_status == StatusCode::ACCEPTED { return 6; } return 0; } EnvelopeStatus::Error => { if let Some(ref err) = envelope.error { match err.code.as_str() { "approval_required" | "clarification_required" => return 3, "etag_conflict" | "idempotency_conflict" => return 4, "dependency_unavailable" | "journal_unavailable" | "judgment_unavailable" => return 5, "operation_unknown" | "partial_apply" | "needs_reconciliation" => return 6, "invalid_input" | "configuration_invalid" | "calendar_unsupported" | "limit_exceeded" => return 2, _ => {} } } } } // 2. Fallback to HTTP status mapping if not explicitly mapped by error code match http_status.as_u16() { 200..=201 => 0, 202 => 6, 400 | 413 | 422 => 2, 403 => 1, // Generic forbidden (approval_required handled above) 409 | 412 => 4, 503 => 5, _ => 1, } } ``` ### 4. Client Timeout & Local Operation Recovery (`src/cli/client.rs`) - For all state-changing commands, generate a UUIDv7 / high-entropy operation ID if `--operation-id` was not explicitly supplied. - Transmit the operation ID as the `Idempotency-Key` header and client request ID as `X-Request-ID`. - If the HTTP connection drops, times out, or receives a 504 Gateway Timeout after transmitting the request: - Construct a local synthetic envelope with `status: "partial"`, `error.code: "operation_unknown"`, and include `operation_id`. - Print the envelope to `stdout`. - Exit the process with exit code 6. --- ## Acceptance Criteria ### Observable Assertions & Verification - [x] Supplying `--input -` consumes JSON from standard input, parsing payloads up to 1 MB without error. - [x] Providing input larger than 1 MB aborts with exit code 2 and an `invalid_input` error envelope. - [x] When invoked with `--json`, `stdout` emits strictly one parseable JSON object matching the `ResponseEnvelope` schema, with all tracing/diagnostic messages routed to `stderr`. - [x] Inspecting `/proc/<pid>/cmdline` or `ps aux` during execution of `life task add --input -` reveals zero task titles, descriptions, or bearer tokens. - [x] API responses with `status: "needs_clarification"` or `error.code: "approval_required"` terminate the CLI process with exit code 3 (Scenario I10). - [x] API responses with `status: "partial"` or `error.code: "partial_apply"` terminate with exit code 6 (Scenario I10, R09). - [x] Simulating a client network timeout during a write command causes the CLI to emit an `operation_unknown` error envelope containing the generated `operation_id` to stdout and exit with code 6. - [x] HTTP 412 Precondition Failed (`etag_conflict`) terminates with exit code 4. - [x] HTTP 503 Service Unavailable (`dependency_unavailable`) terminates with exit code 5. - [x] Successful commands terminate cleanly with exit code 0. --- ## Notes - Real defects fixed by the audit: six error codes that fell through to exit 1 (clarification/unknown/reconciliation/partial-apply/context-pending/limit-exceeded), a non-normative timeout envelope (now `partial` + `operation_unknown` + operation id, exit 6), an unbounded stdin body (now 1 MiB), credentials reachable via `--token`/`LIFE_TOKEN` (removed), undocumented `argv` payload flags (removed), and warnings printed to stdout on the human path (now stderr). - Ten hand-rolled body readers and eight operation-key generators collapsed into shared helpers. - The skill's `limit_exceeded`/timeout/`event cancel` entries were corrected in the same commit. - `life event cancel` is documented in the contract but has no CLI verb or route: filed as a follow-up rather than left implied. - No dependency added. ## Blocked by - #031: Portable Agent Skill Package & Progressive Disclosure
yuris changed title from Issue #032: Stdin JSON Agent Protocol & Strict Exit Code Semantics to Stdin JSON Agent Protocol & Strict Exit Code Semantics 2026-09-22 20:02:33 +00:00
yuris closed this issue 2026-09-23 04:05:52 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/life-service#32
No description provided.