@code-yeongyu/senpi 2026.11.2-3 → 2026.11.3-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/bundle/chunks/{anthropic-messages-WUPLDYD6.js → anthropic-messages-AFWQB3KN.js} +1 -1
- package/dist/bundle/chunks/{args-AOI4NEA6.js → args-O3WS4WCO.js} +1 -1
- package/dist/bundle/chunks/{chunk-O4GRBX4R.js → chunk-3WJ2DE2G.js} +1 -1
- package/dist/bundle/chunks/{chunk-LMIETUPG.js → chunk-4BF4FLT6.js} +1 -1
- package/dist/bundle/chunks/{chunk-T5ZJ2Q4O.js → chunk-4SENPCEA.js} +1 -1
- package/dist/bundle/chunks/chunk-5DZD623T.js +448 -0
- package/dist/bundle/chunks/{chunk-TXOR5BQL.js → chunk-6BSMXZKQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-VRWJJMHO.js → chunk-7565XQAU.js} +1 -1
- package/dist/bundle/chunks/{chunk-IWM5QRAE.js → chunk-7KLEFHSU.js} +1 -1
- package/dist/bundle/chunks/{chunk-FO56LSUK.js → chunk-BNIZP4OS.js} +1 -1
- package/dist/bundle/chunks/{chunk-L3V65XW7.js → chunk-CJWSQ6CK.js} +1 -1
- package/dist/bundle/chunks/{chunk-QLTQ3UUI.js → chunk-DAPBNRN5.js} +1 -1
- package/dist/bundle/chunks/{chunk-GIY7PGOH.js → chunk-DPBPO5OU.js} +1 -1
- package/dist/bundle/chunks/chunk-ESQBKTNG.js +2 -0
- package/dist/bundle/chunks/{chunk-N4TSAHRL.js → chunk-EVTTLPIY.js} +1 -1
- package/dist/bundle/chunks/{chunk-NYXXKS6D.js → chunk-F4OHTL2K.js} +3 -2
- package/dist/bundle/chunks/{chunk-TVMW2SUP.js → chunk-FJ2UCBOA.js} +2 -2
- package/dist/bundle/chunks/{chunk-KVPRQ5KN.js → chunk-FLM6GZPT.js} +1 -1
- package/dist/bundle/chunks/{chunk-P4AT7SBE.js → chunk-HBCNDC53.js} +1 -1
- package/dist/bundle/chunks/chunk-MOH5HOHX.js +2 -0
- package/dist/bundle/chunks/{chunk-7FUO655J.js → chunk-MOOFLKLI.js} +1 -1
- package/dist/bundle/chunks/{chunk-IHOQR7TU.js → chunk-PX7FF5LE.js} +1 -1
- package/dist/bundle/chunks/{chunk-QAM6AXA2.js → chunk-QTQ3ES2S.js} +12 -9
- package/dist/bundle/chunks/{chunk-FJSJUVQQ.js → chunk-TOOB6OLG.js} +3 -3
- package/dist/bundle/chunks/{chunk-662SCMTH.js → chunk-VPDQ6MOL.js} +1 -1
- package/dist/bundle/chunks/{chunk-BW4YXS7X.js → chunk-WCRNJYHO.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZSVWKOAJ.js → chunk-ZNLCKKF6.js} +170 -226
- package/dist/bundle/chunks/{cli-main-534A3WSC.js → cli-main-XM5FWY3Z.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-OAK2E5O2.js → credential-accounts-YZYSKA7X.js} +1 -1
- package/dist/bundle/chunks/{health-write-EB4CPHXN.js → health-write-45CX64T2.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-U2PSNNWO.js → help-fast-path-3EDGRKQM.js} +1 -1
- package/dist/bundle/chunks/{host-command-WJIMRBGN.js → host-command-PZ4VYQIW.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-PWVPQ2OV.js → host-session-command-OJFARE5Q.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-NPHFDPGH.js → interactive-mode-7JSJVVIS.js} +1 -1
- package/dist/bundle/chunks/{login-health-7G2DZVOV.js → login-health-YOPTYRTU.js} +1 -1
- package/dist/bundle/chunks/{main-I6FJAFUP.js → main-3YGJ2O4I.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-7E26R5JF.js → multi-session-host-N2RQ7JSZ.js} +4 -4
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-VNBMMIL3.js → openai-codex-responses-YTWLG4OL.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-JLR32APM.js → openai-completions-TIFDYHZL.js} +1 -1
- package/dist/bundle/chunks/{openai-images-LEBNERSM.js → openai-images-OAEMY4RG.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-JCDUNMHA.js → openai-responses-L35NIG5Y.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-CBSDV36M.js → package-manager-cli-JPNA27E5.js} +1 -1
- package/dist/bundle/chunks/{role-LP6LVNCY.js → role-45Y7PQFC.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-ZDCNHIBE.js → rotation-stream-WXWJKJZN.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-GBBTN2WP.js → rpc-mode-Z7MN2ZWA.js} +1 -1
- package/dist/bundle/chunks/{run-attached-tui-PVLUHHJX.js → run-attached-tui-TI6MELEI.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-W45NYTTK.js → schedule-command-OZ5ESMIT.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-LFLUNALC.js → session-control-endpoint-5YKBYJOO.js} +1 -1
- package/dist/bundle/chunks/{session-picker-OZUINLD7.js → session-picker-CMRV72VA.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +324 -376
- package/dist/bundle/chunks/{state-store-SZARKD36.js → state-store-Q47AYMRI.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +8 -0
- package/dist/core/agent-session.js +68 -60
- package/dist/core/credential-pool/classify.d.ts +3 -2
- package/dist/core/credential-pool/classify.js +10 -3
- package/dist/core/dynamic-prompt/build.d.ts +2 -1
- package/dist/core/dynamic-prompt/build.js +1 -2
- package/dist/core/dynamic-prompt/handoff.d.ts +1 -1
- package/dist/core/dynamic-prompt/handoff.js +9 -9
- package/dist/core/dynamic-prompt/identity.js +1 -1
- package/dist/core/dynamic-prompt/intent-gate.d.ts +1 -1
- package/dist/core/dynamic-prompt/intent-gate.js +8 -8
- package/dist/core/dynamic-prompt/policies.d.ts +1 -1
- package/dist/core/dynamic-prompt/policies.js +5 -7
- package/dist/core/dynamic-prompt/style.js +5 -5
- package/dist/core/dynamic-prompt/tool-section.d.ts +5 -0
- package/dist/core/dynamic-prompt/tool-section.js +8 -6
- package/dist/core/dynamic-prompt/verification.d.ts +6 -6
- package/dist/core/dynamic-prompt/verification.js +12 -12
- package/dist/core/dynamic-prompt/working-task.d.ts +1 -1
- package/dist/core/dynamic-prompt/working-task.js +3 -3
- package/dist/core/dynamic-prompt/workstation.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.d.ts +13 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.js +64 -2
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +5 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-turn-attempt.d.ts +7 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-turn-attempt.js +28 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-turn-types.d.ts +2 -0
- package/dist/core/extensions/builtin/bash-timeout/index.d.ts +1 -1
- package/dist/core/extensions/builtin/bash-timeout/index.js +3 -28
- package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts +0 -11
- package/dist/core/extensions/builtin/bash-timeout/timeout.js +0 -11
- package/dist/core/extensions/builtin/imagegen/index.d.ts +0 -1
- package/dist/core/extensions/builtin/imagegen/index.js +0 -11
- package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts +5 -5
- package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js +5 -5
- package/dist/core/extensions/builtin/prompt-preset/glm-5.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/glm-5.js +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +8 -1
- package/dist/core/extensions/builtin/terminal/notify.js +9 -3
- package/dist/core/extensions/builtin/terminal/prompt.d.ts +7 -1
- package/dist/core/extensions/builtin/terminal/prompt.js +27 -60
- package/dist/core/extensions/builtin/terminal/tools/bash.js +10 -2
- package/dist/core/extensions/builtin/todotools/normalize.d.ts +20 -3
- package/dist/core/extensions/builtin/todotools/normalize.js +251 -5
- package/dist/core/extensions/builtin/todotools/prompt.d.ts +2 -2
- package/dist/core/extensions/builtin/todotools/prompt.js +5 -5
- package/dist/core/extensions/builtin/todotools/tools/todo.js +9 -8
- package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +0 -5
- package/dist/core/extensions/builtin/websearch/index.js +9 -4
- package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts +2 -0
- package/dist/core/extensions/builtin/websearch/websearch/tool.js +3 -1
- package/dist/core/model-runtime.js +1 -1
- package/dist/core/provider-failure.d.ts +6 -0
- package/dist/core/provider-failure.js +6 -2
- package/dist/core/retry-fallback/exhaustion.d.ts +18 -0
- package/dist/core/retry-fallback/exhaustion.js +20 -0
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/tools/bash.d.ts +3 -1
- package/dist/core/tools/bash.js +14 -4
- package/dist/core/tools/edit.d.ts +1 -1
- package/dist/core/tools/edit.js +1 -6
- package/dist/core/tools/grep/index.js +2 -3
- package/dist/core/tools/powershell.d.ts +1 -1
- package/dist/core/tools/powershell.js +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.js +1 -1
- package/dist/engine-trust/shell-trust.d.ts +36 -0
- package/dist/engine-trust/shell-trust.js +44 -0
- package/dist/engine-trust/transport-failure.d.ts +12 -0
- package/dist/engine-trust/transport-failure.js +48 -0
- package/dist/modes/interactive/interactive-mode.js +9 -3
- package/dist/modes/print-mode.js +2 -1
- package/dist/modes/rpc/compact-catalog-families.d.ts +5 -2
- package/dist/modes/rpc/compact-catalog-families.js +5 -2
- package/dist/modes/rpc/compact-catalog-registry.d.ts +9 -1
- package/dist/modes/rpc/compact-catalog-registry.js +9 -2
- package/dist/modes/rpc/compact-recovery-manifest-kinds.d.ts +17 -0
- package/dist/modes/rpc/compact-recovery-manifest-kinds.js +21 -0
- package/dist/modes/rpc/compact-recovery-manifest-shape.d.ts +7 -0
- package/dist/modes/rpc/compact-recovery-manifest.js +11 -3
- package/dist/modes/rpc/compact-socket-commands.js +6 -0
- package/dist/modes/rpc/connection-handler.js +21 -5
- package/dist/modes/rpc/connection-question-bridge.d.ts +3 -1
- package/dist/modes/rpc/connection-question-bridge.js +4 -1
- package/dist/modes/rpc/connection-roles.d.ts +8 -0
- package/dist/modes/rpc/connection-roles.js +18 -0
- package/dist/modes/rpc/method-classes-schema-session.d.ts +4 -0
- package/dist/modes/rpc/method-classes-schema-session.js +1 -0
- package/dist/modes/rpc/method-classes-schema.d.ts +4 -0
- package/dist/modes/rpc/protocol-identity.js +2 -0
- package/dist/modes/rpc/remote-origin.d.ts +31 -0
- package/dist/modes/rpc/remote-origin.js +58 -0
- package/dist/modes/rpc/rpc-protocol-info-types.d.ts +8 -0
- package/dist/modes/rpc/rpc-question-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-question-types.js +0 -3
- package/dist/modes/rpc/rpc-session-command-types.d.ts +3 -0
- package/dist/modes/rpc/session-registry.js +12 -3
- package/dist/modes/rpc/session-worker-client.js +2 -0
- package/dist/modes/rpc/session-worker-protocol.d.ts +3 -0
- package/dist/modes/rpc/session-worker.js +2 -0
- package/docs/engine-socket/fixtures/admission-routes/c-baseline.json +3 -3
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/admission-routes/i-reset-stream.json +3 -3
- package/docs/engine-socket/fixtures/recovery-manifest/index.json +32 -0
- package/docs/engine-socket/fixtures/recovery-manifest/one-partial-family.json +118 -0
- package/docs/rpc.md +93 -3
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-7UC4W2T2.js +0 -2
- package/dist/bundle/chunks/chunk-R23WXN4L.js +0 -448
- package/dist/bundle/chunks/chunk-W5NLCRB3.js +0 -2
package/docs/rpc.md
CHANGED
|
@@ -789,6 +789,32 @@ synchronous cut requests refuse while that asynchronous reservation is pending.
|
|
|
789
789
|
`base_file_replaced`, `base_file_truncated`, `base_file_missing` or `base_fingerprint_mismatch`, never stale rows. With a base present, the transcript, opaque and source families are
|
|
790
790
|
complete: their members are the catalog's rows of this epoch plus the base stream's rows of their family.
|
|
791
791
|
|
|
792
|
+
**Recovery manifest (`componentRef: "manifest"`, senpi#5397, RULING-128).** Paged under a token with
|
|
793
|
+
`componentRef: "manifest"` (one page), the manifest is one JSON object `{format: "senpi-recovery-manifest", version: 1, socketEpoch, admissionId, routeKind, session,
|
|
794
|
+
sessionEpoch, generation, sequence, historyVersion, token, leaf, activeMessage, turn, checkpointDigest, catalogRoot,
|
|
795
|
+
catalogRootLength, requiredKinds}`. `checkpointDigest` is the `root`-domain digest of the catalog root node that
|
|
796
|
+
`catalogRoot` names. `requiredKinds` is the sorted (code-unit order), de-duplicated list of the catalog families this
|
|
797
|
+
cut's root requires: its `complete` families only; a family the engine produced only partially for this cut (an
|
|
798
|
+
unavailable transcript leaves `transcript`, `opaque` and `source` partial, a refused client correlation leaves
|
|
799
|
+
`identity` partial) is left out. The cut's `families/families` registry component carries the same list as its own
|
|
800
|
+
`requiredKinds`, so the list is part of the content `checkpointDigest` covers; a client that reads the registry can
|
|
801
|
+
compare the two. The field exists only in this flag-on compact manifest; legacy and flag-off output is unchanged.
|
|
802
|
+
A client:
|
|
803
|
+
|
|
804
|
+
- reads `requiredKinds` verbatim and opens exactly those families, never deriving the list from a built-in family
|
|
805
|
+
list or `CATALOG_FAMILIES`;
|
|
806
|
+
- refuses a manifest without `requiredKinds` (an older engine) as `manifest_missing_required_kinds`, with no
|
|
807
|
+
fallback to a built-in list;
|
|
808
|
+
- refuses a manifest naming a kind it does not know as `manifest_unknown_required_kind`, never ignoring it, so a
|
|
809
|
+
family a newer engine adds cannot leave a root half opened;
|
|
810
|
+
- refuses a manifest whose `requiredKinds` differs in any way (a kind added, dropped, repeated or reordered) from the
|
|
811
|
+
complete families of the cut's `families/families` registry component, read and verified against the root entry
|
|
812
|
+
`checkpointDigest` covers, as `manifest_required_kinds_mismatch`. The digest covers that component, not the
|
|
813
|
+
manifest field, so this comparison is what binds the list to the cut. senpi's reference reader is
|
|
814
|
+
`readRequiredKinds` (`compact-recovery-manifest-kinds.ts`).
|
|
815
|
+
|
|
816
|
+
The golden `docs/engine-socket/fixtures/recovery-manifest/` is a manifest with one partial family excluded.
|
|
817
|
+
|
|
792
818
|
**S14 repair policy (RULING-84 and RULING-87).** Success carries `data.repair`; a typed recovery failure carries
|
|
793
819
|
`errorData.repair`, using the same `RecoveryRepairAdviceSchema` as the client package's
|
|
794
820
|
`RecoveryRepairController`. Advice contains `state` (`ok`, `repair_needed`, or `storage_unavailable`),
|
|
@@ -1318,7 +1344,8 @@ parsing a version string:
|
|
|
1318
1344
|
"profile_id": "ca7a4943…",
|
|
1319
1345
|
"core": { "extensions": [], "multi_session": true, "session_runtime": "in-process" }
|
|
1320
1346
|
},
|
|
1321
|
-
"agentDir": "/home/me/.senpi/agent"
|
|
1347
|
+
"agentDir": "/home/me/.senpi/agent",
|
|
1348
|
+
"method_classes": { "version": 1, "digest": "71d68465…" }
|
|
1322
1349
|
}
|
|
1323
1350
|
```
|
|
1324
1351
|
|
|
@@ -1341,6 +1368,11 @@ parsing a version string:
|
|
|
1341
1368
|
- `agentDir` - the absolute agent directory the host reads and writes. A generation handoff is refused
|
|
1342
1369
|
`foreign_agent_dir` when it differs from the caller's own agent dir (see "Generation handoff"). Hosts
|
|
1343
1370
|
released before the field omit it.
|
|
1371
|
+
- `method_classes` - the inbound method-class table this engine runs: `version` is the class-meaning version
|
|
1372
|
+
and `digest` the sha256 (hex) of the table's sorted `type=class\n` lines (`type=dynamic@<n>` for a
|
|
1373
|
+
record-dependent entry). A client that classifies records with its own copy of the table uses that copy only
|
|
1374
|
+
while its digest equals this one, and otherwise treats every record as unclassified. Hosts released before
|
|
1375
|
+
the field omit it.
|
|
1344
1376
|
- `memory_pressure` - multi-session hosts only: `true` while this host's memory sampler reads its memory footprint
|
|
1345
1377
|
above `SENPI_RPC_HOST_RSS_WARN_MB`, else `false`. Not identity and never an admission signal; `senpi host status`
|
|
1346
1378
|
reports it per endpoint. Classic hosts and hosts released before the field omit it.
|
|
@@ -2311,7 +2343,7 @@ An `effect_status` is enqueued on the subscribing connection AFTER the record th
|
|
|
2311
2343
|
|
|
2312
2344
|
### Client information
|
|
2313
2345
|
|
|
2314
|
-
Clients may send `set_client_info` with `{ sessionId?, width, capabilities? }
|
|
2346
|
+
Clients may send `set_client_info` with `{ sessionId?, width, capabilities?, remoteOrigin? }` (`remoteOrigin`: see "Remote origin"). `capabilities` registers the connection's
|
|
2315
2347
|
opt-in client capabilities (see [Client capabilities](#client-capabilities)); a name the host does not know is ignored,
|
|
2316
2348
|
never refused. Registration belongs to the connection that sent it: socket disposal removes it, and clients must
|
|
2317
2349
|
re-register `capabilities` after every reconnect. `width` is accepted for compatibility and has no effect: the host
|
|
@@ -3295,7 +3327,7 @@ names a valid preset.
|
|
|
3295
3327
|
| `attach_session` | `sessionId`, `durableSessionId`, `expectedHostInstance?` | `{ sessionId, attached: true }` | Live-only, identity-fenced attachment; never opens/resumes or changes the launch profile. Capability `attach_session`. |
|
|
3296
3328
|
| `release_attachment` | `sessionId` | `{ deferred: boolean }` | Releases exactly one caller hold with disconnect semantics; `deferred` is true only when settlement owns the final release, and false while draining. Capability `release_attachment`. |
|
|
3297
3329
|
| `reclaim_session` | `sessionPath`, `durableSessionId`, `expectedHostInstance` | `{ reclaimed: true, sessionId }` | Atomic attach-and-claim of every caller hold; refuses other connections before aborting. Capability `reclaim_session`. |
|
|
3298
|
-
| `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion: string, capabilities: string[], mode: "classic"\|"multi", instanceId: string, generation: number, engineVersion: string, engineOrdinal: [y, m, d, n, epoch], launch_profile: { profile_id, core }, memory_pressure?: boolean }` | Answered in BOTH modes; side-effect-free; the capability probe. Multi-session hosts include `multi_session`, `session_held` (the active foreign-holder admission guard, in both runtimes), `retain_on_disconnect`, `session_kind`, `session_context`, `auto_title_per_session`, `durable_session_id`, `moved_path_guard` (senpi resolves and guards paths the OmO desktop moved with its data home), `prompt_surface` and `prompt_surface_chat` plus the negotiated launch capabilities. Those are HOST capabilities (a client never sends them) and are advertised only in multi-session mode, where the host owns the attachment refcount and the per-session launch profile. The identity fields are described under "Host identity" above; compatibility is decided from `protocolVersion`, `capabilities` and `engineOrdinal`, NEVER from `serverVersion`. |
|
|
3330
|
+
| `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion: string, capabilities: string[], mode: "classic"\|"multi", instanceId: string, generation: number, engineVersion: string, engineOrdinal: [y, m, d, n, epoch], launch_profile: { profile_id, core }, method_classes: { version, digest }, memory_pressure?: boolean }` | Answered in BOTH modes; side-effect-free; the capability probe. Multi-session hosts include `multi_session`, `session_held` (the active foreign-holder admission guard, in both runtimes), `retain_on_disconnect`, `session_kind`, `session_context`, `auto_title_per_session`, `durable_session_id`, `moved_path_guard` (senpi resolves and guards paths the OmO desktop moved with its data home), `prompt_surface` and `prompt_surface_chat` plus the negotiated launch capabilities. Those are HOST capabilities (a client never sends them) and are advertised only in multi-session mode, where the host owns the attachment refcount and the per-session launch profile. The identity fields are described under "Host identity" above; compatibility is decided from `protocolVersion`, `capabilities` and `engineOrdinal`, NEVER from `serverVersion`. |
|
|
3299
3331
|
| `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?`, `durableSessionId?`, `promptSurface?`, `browserEngine?`, `retryFallback?`, `env?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: true }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there, `session-manager.ts:926-940`); `provider`/`modelId` applied only on create (resume restores the session's model — mirrors `SenpiSessionRuntime.ts:198-200`); params form the immutable launch profile (D8). When the path is already held by a fully-open session, the open ATTACHES to it: same routing handle, `attached: true`, one more attachment counted; the runtime is torn down only when the last attachment closes. Idle sessions past the eviction window are closed by the host itself. `retain_on_disconnect: true` (default false) makes a dropped connection DETACH from this session instead of closing it — see "Retained sessions" below. `kind` (default `interactive`) and the opaque `context` map are described under "Session kind and context" above; both are stored frozen for the session's life and never influence auth, model or resource resolution. |
|
|
3300
3332
|
| `close_session` | `sessionId` | `{}` | Refused with `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment, and `list_sessions` publishes every handle). Otherwise aborts active work, awaits agent idle + settled persistence for up to the host grace window (default 10s), then quarantines any worker that has not exited without releasing its path reservation; its response is the LAST record tagged with that handle for the first closer — no events after (test-pinned). An admitted concurrent close joins the same teardown and receives its own successful response; output saturation rejects admission with the bounded close-overflow/resync notice described above. |
|
|
3301
3333
|
| `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, retain_on_disconnect, kind, context? }] }` | Includes `opening`/`closing` entries. `retain_on_disconnect` is true when the session outlives its last attachment (an open or attach asked for it; retention is never revoked), so no client's release or disconnect can end it. Internally quarantined workers remain externally `closing` until exit. `attachments` is the session's live client attachment count; `0` on an `open` row is a retained session with no client attached. Every row carries `kind`. Rows with `kind: "worker"` are omitted unless `include_workers: true`, and `context` is published ONLY on that listing — a default listing carries no `context` at all. |
|
|
@@ -3511,6 +3543,11 @@ Strict FIFO per session; one total stdout order; cross-session order unspecified
|
|
|
3511
3543
|
|
|
3512
3544
|
### Duplicate/idempotency
|
|
3513
3545
|
|
|
3546
|
+
If claim removal outlives an in-process close's grace deadline, a reopen waits for that
|
|
3547
|
+
in-flight removal even after the closing entry has been deleted. This wait uses the
|
|
3548
|
+
existing bounded pending-claim wait; a stuck removal still refuses with
|
|
3549
|
+
`session_path_in_use` and `errorData.reason: "claim_in_progress"`.
|
|
3550
|
+
|
|
3514
3551
|
Duplicate `open_session` while a path reservation is held by a fully-open session → ATTACH (`attached: true`, same handle), including when that session is retained with zero attachments; while held by an `opening` entry (or an internally quarantined one) → `session_path_in_use`. A `closing` entry is a session that is ENDING, not one in use: on the in-process runtime (the `--listen` socket host default) the open WAITS for that teardown - bounded by the close grace window that bounds the teardown itself - and then opens the file fresh, so reopening a path after a close never depends on how long disposal takes. The worker runtime still answers `session_path_in_use` there, because a worker's teardown ends with an OS thread exit that no host deadline bounds. A path whose owner has already replaced it with another session file is no longer held: that open allocates a new worker and resumes the file. `close_session` releases one attachment; the runtime is disposed only when the last attachment closes. A close for an entry already `closing` joins its in-flight teardown. `close_session` on unknown/already-closed → `unknown_session` error, and so is a `close_session` from a connection that never attached to that handle — it releases nothing and leaves the session untouched. The grace window is configurable by the host through `SENPI_RPC_CLOSE_GRACE_MS`. Request `id`s are client-owned; the server echoes them without dedup.
|
|
3515
3552
|
|
|
3516
3553
|
## Protocol Overview
|
|
@@ -5446,6 +5483,7 @@ Events are streamed to stdout as JSON lines during agent operation. Events do no
|
|
|
5446
5483
|
| `compaction_end` | Compaction completes |
|
|
5447
5484
|
| `auto_retry_start` | Auto-retry begins (after transient error) |
|
|
5448
5485
|
| `auto_retry_end` | Auto-retry completes (success or final failure) |
|
|
5486
|
+
| `retry_fallback_exhausted` | Every model in the fallback chain failed this turn (`chainKey`, `lastError`, optional `primaryError`, `failures`) |
|
|
5449
5487
|
| `summarization_retry_scheduled` | Retry scheduled for a transient compaction or branch-summary summarization error |
|
|
5450
5488
|
| `summarization_retry_attempt_start` | Retried summarization request starts |
|
|
5451
5489
|
| `summarization_retry_finished` | Summarization retry loop completes |
|
|
@@ -5727,6 +5765,22 @@ When the run ended with a provider error (the last assistant message has `stopRe
|
|
|
5727
5765
|
|
|
5728
5766
|
`unknown` is used when the error carries no diagnostic or its metadata matches no row; its `reason` is the generic `the provider returned an unexpected error`, and the provider's code or status, when it sent one, is only in `httpStatus` and `code`. No `reason` or `action` text carries a code or status. When `resetsAt` is set, `reason` ends with that time in the host's local time zone. A bare 429 stays `unknown` because status alone cannot tell a rate limit from exhausted quota. An `unknown` failure the provider answered with HTTP 404 is `model_unavailable` instead: a model endpoint returns 404 for a model id this account cannot use, as the ChatGPT subscription backend does for a model not yet rolled out to the plan. Retry and fallback decisions do not read `failure`.
|
|
5729
5767
|
|
|
5768
|
+
A `network` failure whose `code` is a TLS chain-verification code (`UNABLE_TO_GET_ISSUER_CERT_LOCALLY`, `UNABLE_TO_GET_ISSUER_CERT`, `UNABLE_TO_VERIFY_LEAF_SIGNATURE`, `SELF_SIGNED_CERT_IN_CHAIN`, `DEPTH_ZERO_SELF_SIGNED_CERT` or `CERT_UNTRUSTED`), in an engine that adopted a fallback trust generation (its operating-system certificate import failed), also carries `trust`:
|
|
5769
|
+
|
|
5770
|
+
```json
|
|
5771
|
+
{
|
|
5772
|
+
"category": "network",
|
|
5773
|
+
"provider": "anthropic",
|
|
5774
|
+
"model": "claude-sonnet-4-5",
|
|
5775
|
+
"code": "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
5776
|
+
"reason": "the connection to the provider failed",
|
|
5777
|
+
"action": "check connectivity",
|
|
5778
|
+
"trust": { "code": "trust-tls-failed-under-fallback", "reason": "helper-missing", "action": "reinstall-omo" }
|
|
5779
|
+
}
|
|
5780
|
+
```
|
|
5781
|
+
|
|
5782
|
+
`reason` is the generation's fallback reason and `action` the id of its repair action. A hostname mismatch, an expired certificate, any other network failure, and an engine whose import completed never carry `trust`. The code comes from the transport error's structured `code`, never from its message. The first such failure for each reason in a process also appends one record to `diagnostics.jsonl` beside the generation file; neither the record nor `trust` holds a host name, a path or a certificate.
|
|
5783
|
+
|
|
5730
5784
|
A turn that never reached the provider because no API key or sign-in is saved for it (the message carries auth resolution's `Provider is not configured: <id>` sentinel) reports `auth`, with a `reason` naming the provider and an `action` to connect it or choose another model; it carries no `httpStatus` or `code`.
|
|
5731
5785
|
|
|
5732
5786
|
`request_refused` is an `auth` failure whose credential (same provider, endpoint and key) served a response within the last 10 minutes: the provider refused this one request, and the key may still be valid. If failures persist, re-authenticate the named provider: a recently working key may have been revoked. The failed assistant message carries `credentialServedRecently: true` and keeps the provider's own `errorMessage`. The in-memory record drops expired entries on each record/check and retains at most 1,024 credentials, evicting the least recently served first. Header-based, ADC and SigV4 authentication do not supply an `apiKey`, so their fingerprint reduces to provider + baseUrl; different credentials on that endpoint share the record.
|
|
@@ -5988,6 +6042,30 @@ On final failure (max retries exceeded):
|
|
|
5988
6042
|
}
|
|
5989
6043
|
```
|
|
5990
6044
|
|
|
6045
|
+
### retry_fallback_exhausted
|
|
6046
|
+
|
|
6047
|
+
Emitted when the turn's model fallback chain runs out: the primary and every fallback rung failed.
|
|
6048
|
+
|
|
6049
|
+
```json
|
|
6050
|
+
{
|
|
6051
|
+
"type": "retry_fallback_exhausted",
|
|
6052
|
+
"chainKey": "anthropic/claude-opus",
|
|
6053
|
+
"lastError": "Connection error.",
|
|
6054
|
+
"primaryError": { "model": "anthropic/claude-opus", "reason": "billing", "error": "400 out of extra usage" },
|
|
6055
|
+
"failures": [
|
|
6056
|
+
{ "model": "anthropic/claude-opus", "reason": "billing", "error": "400 out of extra usage" },
|
|
6057
|
+
{ "model": "openai/gpt", "reason": "transient", "error": "Connection error." }
|
|
6058
|
+
]
|
|
6059
|
+
}
|
|
6060
|
+
```
|
|
6061
|
+
|
|
6062
|
+
- `chainKey`: the `provider/model` key of the fallback chain that ran out.
|
|
6063
|
+
- `lastError`: the error of the rung that ran last.
|
|
6064
|
+
- `primaryError` (optional): the failure that started the fallback walk, the turn's first failed rung, as `{ model, reason, error }`. `reason` is `transient`, `refusal`, `hard-error` or `billing`.
|
|
6065
|
+
- `failures` (optional): every rung that failed this turn, in the order it ran, the primary first. An exact repeat of the previous entry is dropped.
|
|
6066
|
+
|
|
6067
|
+
`primaryError` and `failures` are present together when the session recorded at least one rung failure, and absent otherwise. Account usage-limit text in `lastError` and in each `error` is replaced by its account-limit description. Clients that ignore unknown fields keep working with `chainKey` and `lastError`.
|
|
6068
|
+
|
|
5991
6069
|
### summarization_retry_scheduled / summarization_retry_attempt_start / summarization_retry_finished
|
|
5992
6070
|
|
|
5993
6071
|
Emitted when compaction or branch-summary summarization retries after a transient provider error. These events use the same retry settings as automatic assistant-turn retries.
|
|
@@ -6548,6 +6626,18 @@ Present one or more questions to the user. Requires the `question` client capabi
|
|
|
6548
6626
|
|
|
6549
6627
|
Every `question` frame carries `raisedBy`: the `connectionId` whose command raised it (the same connection a `select`, `confirm`, `input` or `editor` raised at that point is delivered to; for a model tool call, the connection whose prompt started the turn), or `"agent"` when no connection's command started the work (for example a goal continuation, or a classic stdio session). A client uses it to tell a question it raised from one another connection raised; the frame still reaches every attached connection, and any of them may answer. The field is optional and additive: a client that ignores it is unaffected, and frames from older hosts omit it. The replayed frames in `pendingQuestions` carry the same value.
|
|
6550
6628
|
|
|
6629
|
+
#### Remote origin
|
|
6630
|
+
|
|
6631
|
+
A relay forwarder that drives a session on behalf of another device opens its own connection for that device and declares, in that connection's `set_client_info`, which device it speaks for:
|
|
6632
|
+
|
|
6633
|
+
```json
|
|
6634
|
+
{"type":"set_client_info","width":80,"remoteOrigin":{"deviceId":"device-a","deviceName":"Laptop"}}
|
|
6635
|
+
```
|
|
6636
|
+
|
|
6637
|
+
`deviceId` is required and `deviceName` optional; both are non-empty strings of at most 256 characters, and no other key is accepted (`remote_origin_invalid`). A connection's origin is fixed once declared: repeating the same value is accepted, and a different one is refused `remote_origin_fixed` and leaves the declared origin unchanged. A connection that never declares one is local.
|
|
6638
|
+
|
|
6639
|
+
Every `select`, `confirm`, `input`, `editor` and `question` frame raised by that connection's command (for a model tool call, the connection whose prompt started the turn) carries the declared value as `remoteOrigin`, next to `raisedBy`. The mirrored `ui_request_pending` carries it inside `request`, and so do replayed pending dialogs. A dialog raised by a local connection carries no `remoteOrigin`, so absence means local. A client shows the label (for example "Requested from Laptop") so that the person at the host can tell a dialog a remote device's turn raised from their own. `set_client_info` is a connection-local method: a forwarder never forwards a remote device's own `set_client_info`, so a remote device cannot set or change the label. A secret `input` goes only to the connection that raised it and is not labeled.
|
|
6640
|
+
|
|
6551
6641
|
```json
|
|
6552
6642
|
{
|
|
6553
6643
|
"type": "extension_ui_request",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.11.
|
|
3
|
+
"version": "2026.11.3-1",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@earendil-works/chord": "0.99.1",
|
|
86
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.
|
|
87
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.
|
|
88
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.
|
|
86
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.3-1",
|
|
87
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.3-1",
|
|
88
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.3-1",
|
|
89
89
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
90
90
|
"ajv": "8.20.0",
|
|
91
91
|
"chalk": "6.0.0",
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
"yaml": "2.9.1",
|
|
107
107
|
"@anthropic-ai/claude-agent-sdk": "0.3.306",
|
|
108
108
|
"@anthropic-ai/sdk": "0.127.0",
|
|
109
|
-
"@code-yeongyu/senpi-codemode": "2026.11.
|
|
110
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.
|
|
109
|
+
"@code-yeongyu/senpi-codemode": "2026.11.3-1",
|
|
110
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.3-1",
|
|
111
111
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
112
112
|
"@mozilla/readability": "0.6.0",
|
|
113
113
|
"linkedom": "0.18.13",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{peekProviderDiagnostic,siwcErrorForCode}from"./chunk-3SASTB6Y.js";var PROVIDER_DIAGNOSTIC_MAX_BYTES=512;function categoryForProviderCode(code){let siwc=siwcErrorForCode(code);if(siwc!==void 0)return siwc.category;switch(code){case"authentication_error":case"invalid_api_key":return"auth";case"rate_limit_error":case"rate_limit_exceeded":return"rate_limit";case"insufficient_quota":case"exceeded_current_quota_error":case"usage_limit_reached":case"usage_not_included":return"quota";case"context_length_exceeded":return"context_limit";case"invalid_request_error":return"invalid_request";case"overloaded_error":case"api_error":case"timeout_error":return"provider_unavailable";case"billing_error":case"permission_error":case"not_found_error":return"unknown";default:return}}function categoryForTransportCode(code){switch(code){case"ECONNRESET":case"ECONNREFUSED":case"ETIMEDOUT":case"ENOTFOUND":case"EAI_AGAIN":case"EPIPE":case"UND_ERR_SOCKET":case"UND_ERR_CONNECT_TIMEOUT":case"UND_ERR_HEADERS_TIMEOUT":case"UND_ERR_BODY_TIMEOUT":case"UND_ERR_CLOSED":case"APIConnectionError":case"APIConnectionTimeoutError":return"network";default:return}}function isProviderCodeStatusCompatible(code,status){let siwc=siwcErrorForCode(code);if(siwc!==void 0)return status===siwc.status;switch(code){case"authentication_error":case"invalid_api_key":return status===401;case"rate_limit_error":case"rate_limit_exceeded":case"insufficient_quota":case"exceeded_current_quota_error":case"usage_limit_reached":case"usage_not_included":return status===429;case"context_length_exceeded":case"invalid_request_error":return status===400;case"overloaded_error":case"api_error":case"timeout_error":return status>=500&&status<=599;case"billing_error":return status===402;case"permission_error":return status===403;case"not_found_error":return status===404;default:return!1}}var OPAQUE_PROVIDER_CODE=/^[a-z]+(?:[_.][a-z]+)*$/,OPAQUE_PROVIDER_CODE_MAX_LENGTH=64;function isOpaqueProviderCode(code){return code.length<=OPAQUE_PROVIDER_CODE_MAX_LENGTH&&OPAQUE_PROVIDER_CODE.test(code)&&categoryForProviderCode(code)===void 0}function isProviderDiagnosticResetsAt(value){return typeof value=="number"&&Number.isInteger(value)&&value>9466848e5&&value<41024448e5}function categoryForProviderStatus(status){return status===401?"auth":status===400?"invalid_request":status>=500&&status<=599?"provider_unavailable":"unknown"}function isProviderDiagnosticStatus(value){return typeof value=="number"&&Number.isInteger(value)&&value>=400&&value<=599}function isProviderDiagnosticCategory(value){switch(value){case"auth":case"rate_limit":case"quota":case"context_limit":case"invalid_request":case"provider_unavailable":case"network":case"unknown":return!0;default:return!1}}function buildProviderDiagnostic(input){let diagnostic={category:input.category,evidence:input.evidence};return input.httpStatus!==void 0&&(diagnostic.httpStatus=input.httpStatus),input.code!==void 0&&(diagnostic.code=input.code),input.route!==void 0&&(diagnostic.route=input.route),input.resetsAt!==void 0&&(input.category==="quota"||input.category==="rate_limit")&&(diagnostic.resetsAt=input.resetsAt),new TextEncoder().encode(JSON.stringify(diagnostic)).length<=PROVIDER_DIAGNOSTIC_MAX_BYTES?diagnostic:void 0}function classifyProviderFailure(facts){let statusSupplied=facts.status!==void 0&&facts.status!==null;if(statusSupplied&&!isProviderDiagnosticStatus(facts.status))return;let status=statusSupplied&&isProviderDiagnosticStatus(facts.status)?facts.status:void 0,category=facts.code===void 0?void 0:categoryForProviderCode(facts.code);if(facts.code!==void 0&&category!==void 0)return status!==void 0&&!isProviderCodeStatusCompatible(facts.code,status)?void 0:buildProviderDiagnostic({category,...status===void 0?{}:{httpStatus:status},code:facts.code,evidence:"structured_code"});if(status!==void 0)return buildProviderDiagnostic({category:categoryForProviderStatus(status),httpStatus:status,evidence:"structured_status"})}function readField(value,key){return Reflect.get(value,key)}function sanitizeProviderDiagnostic(value){try{if(typeof value!="object"||value===null||Array.isArray(value))return;let category=readField(value,"category"),evidence=readField(value,"evidence"),httpStatus=readField(value,"httpStatus"),code=readField(value,"code"),resetsAt=readField(value,"resetsAt"),route=readField(value,"route");if(route!==void 0&&route!=="siwc")return;let routed=route==="siwc"?{route}:{};if(!isProviderDiagnosticCategory(category)||httpStatus!==void 0&&!isProviderDiagnosticStatus(httpStatus)||resetsAt!==void 0&&!isProviderDiagnosticResetsAt(resetsAt))return;let reset=resetsAt===void 0?{}:{resetsAt};return evidence==="structured_status"?code!==void 0||!isProviderDiagnosticStatus(httpStatus)||category!==categoryForProviderStatus(httpStatus)?void 0:buildProviderDiagnostic({category,httpStatus,evidence,...reset,...routed}):evidence==="structured_transport"?httpStatus!==void 0||typeof code!="string"||categoryForTransportCode(code)!==category?void 0:buildProviderDiagnostic({category,code,evidence}):evidence!=="structured_code"||typeof code!="string"?void 0:isOpaqueProviderCode(code)?category!=="unknown"||httpStatus!==void 0?void 0:buildProviderDiagnostic({category,code,evidence}):categoryForProviderCode(code)!==category||httpStatus!==void 0&&!isProviderCodeStatusCompatible(code,httpStatus)?void 0:buildProviderDiagnostic({category,...httpStatus===void 0?{}:{httpStatus},code,evidence,...reset,...routed})}catch{return}}function readProviderDiagnostic(error){return sanitizeProviderDiagnostic(peekProviderDiagnostic(error))}export{PROVIDER_DIAGNOSTIC_MAX_BYTES,categoryForProviderCode,categoryForTransportCode,isOpaqueProviderCode,isProviderDiagnosticResetsAt,isProviderDiagnosticStatus,buildProviderDiagnostic,classifyProviderFailure,sanitizeProviderDiagnostic,readProviderDiagnostic};
|