@code-yeongyu/senpi 2026.11.6-2 → 2026.11.9
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 +52 -0
- package/dist/bundle/chunks/{bun-extension-importer-RCGPQTI2.js → bun-extension-importer-GQ6IKEXD.js} +2 -2
- package/dist/bundle/chunks/{chunk-GSKCWXXG.js → chunk-3CRPNPTR.js} +1 -1
- package/dist/bundle/chunks/{chunk-RQ6CHXQ2.js → chunk-3TZQXZKH.js} +1 -1
- package/dist/bundle/chunks/{chunk-QN2BHP3M.js → chunk-44BJPS5I.js} +1 -1
- package/dist/bundle/chunks/{chunk-WGRKM3N6.js → chunk-45MVD7BU.js} +1 -1
- package/dist/bundle/chunks/chunk-4JIBEZZE.js +106 -0
- package/dist/bundle/chunks/{chunk-CF5OGBC4.js → chunk-4NYNMAYF.js} +1 -1
- package/dist/bundle/chunks/{chunk-X5ZVYNOL.js → chunk-4UR6UFBH.js} +1 -1
- package/dist/bundle/chunks/{chunk-LNPAT22L.js → chunk-527Q2UYM.js} +9 -8
- package/dist/bundle/chunks/{chunk-HLM46XEB.js → chunk-622VYTRI.js} +1 -1
- package/dist/bundle/chunks/{chunk-GKAYPJ4E.js → chunk-7PROV6BB.js} +1 -1
- package/dist/bundle/chunks/{chunk-4E7GDG4H.js → chunk-7VMEE62L.js} +1 -1
- package/dist/bundle/chunks/{chunk-ECW7VBMB.js → chunk-HXNXISJD.js} +1 -1
- package/dist/bundle/chunks/{chunk-C7OKUC7L.js → chunk-K3UHGDUQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-47PPZKPO.js → chunk-LM6DOWQH.js} +4 -4
- package/dist/bundle/chunks/chunk-LPYBKMPT.js +2 -0
- package/dist/bundle/chunks/{chunk-2FDW2LQL.js → chunk-M7GF73FO.js} +2 -2
- package/dist/bundle/chunks/{chunk-D5MRJWUK.js → chunk-OCMGRBJ5.js} +221 -229
- package/dist/bundle/chunks/{chunk-TIPKAMSS.js → chunk-SGAFIMAU.js} +1 -1
- package/dist/bundle/chunks/{chunk-7PSBD5KS.js → chunk-STGOYIXC.js} +1 -1
- package/dist/bundle/chunks/{chunk-DSWDJ7HT.js → chunk-WQ4RBPMS.js} +1 -1
- package/dist/bundle/chunks/{chunk-7III44PV.js → chunk-WX2HB4DD.js} +1 -1
- package/dist/bundle/chunks/chunk-XZDCMDUH.js +2 -0
- package/dist/bundle/chunks/{chunk-GNSMNSL7.js → chunk-Y5L4UFMI.js} +2 -2
- package/dist/bundle/chunks/{cli-main-T74YJGIF.js → cli-main-E4NDWAUD.js} +1 -1
- package/dist/bundle/chunks/{dispatch-BAK2YZKD.js → dispatch-VDIYSXQN.js} +1 -1
- package/dist/bundle/chunks/{hand-entry-AYRUGE2A.js → hand-entry-5X5Z5Y3D.js} +1 -1
- package/dist/bundle/chunks/{headless-host-ES74ZB6M.js → headless-host-YXUGONR3.js} +1 -1
- package/dist/bundle/chunks/{host-command-YRXZE4TV.js → host-command-VHX2ILLM.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-N7IN7ZDV.js → host-session-command-P3MRW63Z.js} +1 -1
- package/dist/bundle/chunks/interactive-mode-XUSBLLWI.js +2 -0
- package/dist/bundle/chunks/main-3TWVKTJN.js +2 -0
- package/dist/bundle/chunks/{multi-session-host-XSUJDHFZ.js → multi-session-host-HRGM66PY.js} +20 -17
- package/dist/bundle/chunks/{package-manager-cli-GLUNPQJU.js → package-manager-cli-WSH7U4YE.js} +1 -1
- package/dist/bundle/chunks/{role-RRFFHZF5.js → role-4BUU46VW.js} +3 -3
- package/dist/bundle/chunks/{rotation-stream-BN2YK2MC.js → rotation-stream-TODBIHXJ.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-OAPIV46G.js +2 -0
- package/dist/bundle/chunks/{run-attached-tui-W236SEOR.js → run-attached-tui-MXCC5IZR.js} +1 -1
- package/dist/bundle/chunks/{runtime-session-OBXYK4PI.js → runtime-session-VO3X4OE3.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-B2EJTBOE.js → schedule-command-MX56D6UE.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-YIMLHGLG.js → session-control-endpoint-IL6ERD4U.js} +1 -1
- package/dist/bundle/chunks/{session-picker-E6ZONUJB.js → session-picker-6VRC25MJ.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +114 -110
- 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-runtime.d.ts +2 -0
- package/dist/core/agent-session-runtime.js +9 -2
- package/dist/core/agent-session-services.d.ts +2 -0
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session.d.ts +32 -3
- package/dist/core/agent-session.js +142 -29
- package/dist/core/credential-pool/rotation-slots.d.ts +3 -3
- package/dist/core/credential-pool/state-store.d.ts +1 -1
- package/dist/core/egress/classify.d.ts +8 -3
- package/dist/core/egress/classify.js +177 -158
- package/dist/core/egress/console-redactor.d.ts +7 -0
- package/dist/core/egress/console-redactor.js +74 -0
- package/dist/core/egress/credentials.d.ts +73 -0
- package/dist/core/egress/credentials.js +20 -0
- package/dist/core/egress/device-custody.d.ts +1 -1
- package/dist/core/egress/device-custody.js +18 -15
- package/dist/core/egress/device-proxy-call.d.ts +59 -0
- package/dist/core/egress/device-proxy-call.js +148 -0
- package/dist/core/egress/device-proxy-lost-calls.d.ts +27 -0
- package/dist/core/egress/device-proxy-lost-calls.js +28 -0
- package/dist/core/egress/device-proxy-ports.d.ts +49 -0
- package/dist/core/egress/device-proxy-ports.js +2 -0
- package/dist/core/egress/device-proxy-request.d.ts +34 -0
- package/dist/core/egress/device-proxy-request.js +69 -0
- package/dist/core/egress/device-proxy-waits.d.ts +40 -0
- package/dist/core/egress/device-proxy-waits.js +67 -0
- package/dist/core/egress/device-proxy.d.ts +3 -0
- package/dist/core/egress/device-proxy.js +214 -0
- package/dist/core/egress/endpoint.js +2 -1
- package/dist/core/egress/entry-verify.d.ts +13 -0
- package/dist/core/egress/entry-verify.js +33 -0
- package/dist/core/egress/hpke.d.ts +63 -0
- package/dist/core/egress/hpke.js +192 -0
- package/dist/core/egress/key-policy-template.d.ts +7 -1
- package/dist/core/egress/ledger-schema.d.ts +80 -0
- package/dist/core/egress/ledger-schema.js +117 -0
- package/dist/core/egress/ledger.d.ts +55 -0
- package/dist/core/egress/ledger.js +159 -0
- package/dist/core/egress/pinned-infra.d.ts +23 -2
- package/dist/core/egress/pinned-infra.js +9 -2
- package/dist/core/egress/placement-claim.d.ts +69 -0
- package/dist/core/egress/placement-claim.js +115 -0
- package/dist/core/egress/pool-state.d.ts +18 -0
- package/dist/core/egress/pool-state.js +5 -0
- package/dist/core/egress/redact.d.ts +10 -0
- package/dist/core/egress/redact.js +134 -0
- package/dist/core/egress/sealed-format.d.ts +101 -0
- package/dist/core/egress/sealed-format.js +174 -0
- package/dist/core/egress/sealed-source.d.ts +35 -0
- package/dist/core/egress/sealed-source.js +140 -0
- package/dist/core/egress/verified-keys.js +1 -1
- package/dist/core/egress/wire.d.ts +228 -0
- package/dist/core/egress/wire.js +49 -0
- package/dist/core/extensions/builtin/ask-user/escape-residue.d.ts +50 -0
- package/dist/core/extensions/builtin/ask-user/escape-residue.js +113 -0
- package/dist/core/extensions/builtin/ask-user/extension.js +7 -1
- package/dist/core/extensions/builtin/ask-user/resume.js +2 -1
- package/dist/core/extensions/builtin/ask-user/tool.d.ts +3 -0
- package/dist/core/extensions/builtin/ask-user/tool.js +8 -0
- package/dist/core/extensions/builtin/goal/index.js +46 -0
- package/dist/core/extensions/builtin/goal/stop-pause.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/stop-pause.js +4 -2
- package/dist/core/extensions/builtin/monitor-state-event.d.ts +26 -0
- package/dist/core/extensions/builtin/monitor-state-event.js +2 -0
- package/dist/core/extensions/builtin/permission-system/index.js +10 -4
- package/dist/core/extensions/builtin/permission-system/preset-registry.d.ts +2 -0
- package/dist/core/extensions/builtin/permission-system/preset-registry.js +11 -0
- package/dist/core/extensions/builtin/permission-system/settings.d.ts +2 -0
- package/dist/core/extensions/builtin/permission-system/settings.js +10 -1
- package/dist/core/extensions/builtin/terminal/background-age.d.ts +25 -0
- package/dist/core/extensions/builtin/terminal/background-age.js +91 -0
- package/dist/core/extensions/builtin/terminal/extension-state.d.ts +1 -1
- package/dist/core/extensions/builtin/terminal/extension-state.js +21 -3
- package/dist/core/extensions/builtin/terminal/notify.d.ts +6 -0
- package/dist/core/extensions/builtin/terminal/notify.js +39 -5
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +9 -2
- package/dist/core/extensions/builtin/terminal/session-bundle.js +36 -1
- package/dist/core/extensions/builtin/terminal/settings.d.ts +2 -0
- package/dist/core/extensions/builtin/terminal/settings.js +8 -0
- package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.d.ts +15 -0
- package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.js +87 -0
- package/dist/core/extensions/builtin/terminal/tools/bash.d.ts +3 -1
- package/dist/core/extensions/builtin/terminal/tools/bash.js +44 -15
- package/dist/core/extensions/builtin/terminal/tools/child-placement.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/tools/child-placement.js +43 -6
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +14 -2
- package/dist/core/extensions/bun-extension-importer.js +4 -3
- package/dist/core/extensions/bun-extension-load-turns.d.ts +4 -0
- package/dist/core/extensions/bun-extension-load-turns.js +45 -0
- package/dist/core/extensions/bun-extension-registry.d.ts +1 -0
- package/dist/core/extensions/bun-extension-registry.js +19 -1
- package/dist/core/extensions/loader.js +5 -0
- package/dist/core/extensions/session-control-types.d.ts +15 -0
- package/dist/core/extensions/session-control-types.js +11 -0
- package/dist/core/extensions/session-dialog-api.d.ts +5 -0
- package/dist/core/extensions/session-dialog-api.js +13 -0
- package/dist/core/extensions/types.d.ts +14 -1
- package/dist/core/external-admission.d.ts +16 -2
- package/dist/core/external-admission.js +22 -4
- package/dist/core/permission-decision.d.ts +22 -0
- package/dist/core/permission-decision.js +53 -0
- package/dist/core/permission-mode.d.ts +56 -0
- package/dist/core/permission-mode.js +133 -0
- package/dist/core/retry-fallback/controller-types.d.ts +6 -0
- package/dist/core/retry-fallback/controller.d.ts +9 -0
- package/dist/core/retry-fallback/controller.js +34 -3
- package/dist/core/retry-fallback/unusable.d.ts +25 -1
- package/dist/core/retry-fallback/unusable.js +87 -2
- package/dist/core/retry-fallback/usage-limit.d.ts +6 -1
- package/dist/core/retry-fallback/usage-limit.js +12 -4
- package/dist/core/sdk.d.ts +6 -1
- package/dist/core/sdk.js +3 -0
- package/dist/core/session-log.js +1 -1
- package/dist/core/stall-warning.d.ts +41 -0
- package/dist/core/stall-warning.js +72 -0
- package/dist/core/terminal-settings.d.ts +1 -0
- package/dist/core/turn-origin.d.ts +31 -0
- package/dist/core/turn-origin.js +64 -0
- package/dist/modes/interactive/components/remote-delivery-message.d.ts +2 -1
- package/dist/modes/interactive/components/remote-delivery-message.js +13 -5
- package/dist/modes/rpc/admit-external-message-command.d.ts +37 -0
- package/dist/modes/rpc/admit-external-message-command.js +61 -0
- package/dist/modes/rpc/admit-external-message-wire.d.ts +42 -0
- package/dist/modes/rpc/admit-external-message-wire.js +55 -0
- package/dist/modes/rpc/client-input-handler.d.ts +2 -0
- package/dist/modes/rpc/client-input-handler.js +2 -0
- package/dist/modes/rpc/connection-handler.js +30 -5
- package/dist/modes/rpc/connection-roles.d.ts +6 -0
- package/dist/modes/rpc/connection-roles.js +13 -0
- package/dist/modes/rpc/delegated-drive-origin.d.ts +9 -0
- package/dist/modes/rpc/delegated-drive-origin.js +16 -0
- package/dist/modes/rpc/effect-commands.d.ts +7 -1
- package/dist/modes/rpc/host-bare-endpoint-registration.d.ts +7 -0
- package/dist/modes/rpc/host-bare-endpoint-registration.js +35 -0
- package/dist/modes/rpc/host-capabilities.d.ts +2 -0
- package/dist/modes/rpc/host-capabilities.js +3 -0
- package/dist/modes/rpc/host-handover-parked.d.ts +11 -0
- package/dist/modes/rpc/host-handover-parked.js +17 -0
- package/dist/modes/rpc/host-handover-reopen.js +18 -6
- package/dist/modes/rpc/host-handover-resident-writer.d.ts +16 -0
- package/dist/modes/rpc/host-handover-resident-writer.js +57 -0
- package/dist/modes/rpc/host-idle-handover.js +1 -0
- package/dist/modes/rpc/host-warm.d.ts +16 -1
- package/dist/modes/rpc/host-warm.js +33 -6
- package/dist/modes/rpc/method-classes-schema-delivery.d.ts +32 -0
- package/dist/modes/rpc/method-classes-schema-delivery.js +22 -0
- package/dist/modes/rpc/method-classes-schema-session.d.ts +36 -0
- package/dist/modes/rpc/method-classes-schema-session.js +3 -0
- package/dist/modes/rpc/method-classes-schema.d.ts +36 -0
- package/dist/modes/rpc/method-classes.d.ts +2 -0
- package/dist/modes/rpc/method-classes.js +4 -0
- package/dist/modes/rpc/multi-session-host.js +6 -1
- package/dist/modes/rpc/permission-preset-command.d.ts +6 -0
- package/dist/modes/rpc/permission-preset-command.js +27 -0
- package/dist/modes/rpc/rpc-input-validation.js +4 -3
- package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-response-types.d.ts +2 -1
- package/dist/modes/rpc/rpc-session-command-types.d.ts +6 -1
- package/dist/modes/rpc/rpc-session-event-types.d.ts +5 -2
- package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -1
- package/dist/modes/rpc/rpc-session-state.js +2 -0
- package/dist/modes/rpc/session-attachment-commands.js +3 -3
- package/dist/modes/rpc/session-event-writer-connections.js +1 -0
- package/dist/modes/rpc/session-event-writer.d.ts +1 -1
- package/dist/modes/rpc/session-registry-attach.d.ts +0 -9
- package/dist/modes/rpc/session-registry-attach.js +16 -3
- package/dist/modes/rpc/session-registry-types.d.ts +2 -0
- package/dist/modes/rpc/session-registry.d.ts +2 -0
- package/dist/modes/rpc/session-registry.js +16 -3
- package/dist/modes/rpc/session-router-close.js +43 -6
- package/dist/modes/rpc/session-router-context.js +2 -0
- package/dist/modes/rpc/session-router-occupancy.js +2 -11
- package/dist/modes/rpc/session-router-open-profile.js +4 -0
- package/dist/modes/rpc/session-router-open.d.ts +1 -1
- package/dist/modes/rpc/session-router-open.js +6 -17
- package/dist/modes/rpc/session-router-residency.d.ts +24 -0
- package/dist/modes/rpc/session-router-residency.js +90 -0
- package/dist/modes/rpc/session-router-routing.js +7 -1
- package/dist/modes/rpc/session-status.js +4 -1
- package/dist/modes/rpc/session-worker.js +4 -0
- package/dist/modes/rpc/tool-child/terminal-jobs.d.ts +1 -0
- package/dist/modes/rpc/tool-child/terminal-jobs.js +17 -1
- package/dist/modes/rpc/worker-session-registry.js +5 -2
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/rpc-inbound-samples.json +29 -2
- package/docs/extensions.md +12 -0
- package/docs/rpc.md +129 -12
- package/docs/terminal-tools.md +25 -1
- package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
- package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-C7HEEBFH.js +0 -2
- package/dist/bundle/chunks/chunk-XCMII2VQ.js +0 -98
- package/dist/bundle/chunks/interactive-mode-B5SQDLH7.js +0 -2
- package/dist/bundle/chunks/main-UQFVFCNY.js +0 -2
- package/dist/bundle/chunks/rpc-mode-JYN3E7P7.js +0 -2
package/docs/rpc.md
CHANGED
|
@@ -1286,7 +1286,7 @@ from them that the host itself went away (shutdown) or handed its endpoint to a
|
|
|
1286
1286
|
| `event_queue_failed` | `true` | The socket's event queue failed; follows its `overflow` record when it has one | `overflow`, `stall` or `transport_error` |
|
|
1287
1287
|
| `event_lease_refused` | `true` | An event cannot be leased; follows its `event_lease_refused` record | `lease_limit` or `storage_unavailable` |
|
|
1288
1288
|
| `compact_endpoint_unsupported` | `false` | A compact mirror of a session served only on the legacy wire; follows its `compact_refused` record | none |
|
|
1289
|
-
| `host_shutdown` | `true` | The host is shutting down; follows its sessions' `session_closed` records (`reason: "host_shutdown"`) | none |
|
|
1289
|
+
| `host_shutdown` | `true` | The host is shutting down; follows its sessions' `session_closed` records (`reason: "host_shutdown"`), or `session_parked { reason: "host_shutdown" }` for a retained session on a socket host | none |
|
|
1290
1290
|
| `host_handoff` | `true` | The host drains for a newer generation; follows its sessions' handoff park records | none |
|
|
1291
1291
|
|
|
1292
1292
|
#### interactive_user
|
|
@@ -2514,7 +2514,8 @@ releases the caller's own attachment and closes the session only when no other c
|
|
|
2514
2514
|
|
|
2515
2515
|
Retention limits (what `open`'s `retain_on_disconnect: true` does NOT survive): an explicit `close`, the host's own
|
|
2516
2516
|
idle-eviction window (`SENPI_RPC_SESSION_IDLE_EVICTION_MS`, default 30 minutes, after which the session is parked -
|
|
2517
|
-
any `senpi host session` verb given its path or durable id resumes it)
|
|
2517
|
+
any `senpi host session` verb given its path or durable id resumes it). The host process exiting does not end it: the next
|
|
2518
|
+
host on the same endpoint reopens it (see "Retained sessions across a host restart"). A session that has not
|
|
2518
2519
|
received a message yet may have no JSONL file on disk even though `open` already reported a `sessionPath`.
|
|
2519
2520
|
|
|
2520
2521
|
A minimal orchestration run:
|
|
@@ -2801,8 +2802,8 @@ viewer opened to the next one (senpi#5808). So every `open_session` on a delegat
|
|
|
2801
2802
|
stays `open` with `attachments: 0` and attachable from any connection, exactly like a retained session (see "Retained
|
|
2802
2803
|
sessions"). The host decides this when it reads the line, so a connection that ends while its open is still running
|
|
2803
2804
|
leaves that session retained too. Like any retained session it is still closed by an explicit `close_session`, parked
|
|
2804
|
-
by the idle-eviction window, and recorded at park for the next host
|
|
2805
|
-
|
|
2805
|
+
by the idle-eviction window, and recorded (at open, and again at park) for the next host on the endpoint, which reopens
|
|
2806
|
+
it on startup, so it survives into the next generation and across a host restart. `attach_session` never turns retention on: attaching over a delegated
|
|
2806
2807
|
connection to a session that is not retained leaves it closing with its last attachment. One consequence to know: a
|
|
2807
2808
|
delegated `open_session` by `sessionPath` (which needs `device_admin`) that lands on a session already live and opened by
|
|
2808
2809
|
someone else joins it as an attach that asks for retention, so it turns retention on for that live session and every
|
|
@@ -3297,8 +3298,9 @@ What the host does enforce are lifecycle windows, and they only ever return memo
|
|
|
3297
3298
|
command's `unknown_session`. A GENERATION HANDOFF also parks attached sessions, using the distinct activity
|
|
3298
3299
|
predicate below, and emits `session_closed { sessionId, reason: "handoff_parked", sessionPath }`, so a client can tell "the host handed
|
|
3299
3300
|
over, reopen by path" from "this session ended". An explicit `close_session` is `reason: "client_close"`; the host
|
|
3300
|
-
process exiting is `reason: "host_shutdown"
|
|
3301
|
-
|
|
3301
|
+
process exiting is `reason: "host_shutdown"`, except for a retained session on a socket host, which PARKS instead
|
|
3302
|
+
(`session_parked { reason: "host_shutdown" }`) because the next host on the endpoint reopens it (see "Retained sessions
|
|
3303
|
+
across a host restart"). `reason` is optional on the wire; a client that does not know a
|
|
3302
3304
|
value, or receives a record with no `reason` field, treats the record exactly as a reason-less one. "Session-owned
|
|
3303
3305
|
work" is the complete activity contract, not just a streaming turn: an agent run, a running bash command,
|
|
3304
3306
|
background terminal jobs and any other published wake source (terminal monitors, loop-guard holds), compaction,
|
|
@@ -3334,17 +3336,45 @@ What the host does enforce are lifecycle windows, and they only ever return memo
|
|
|
3334
3336
|
in-flight turn to settlement, keeps its path reservation and worker slot, and stays in `list_sessions`. A later
|
|
3335
3337
|
`open_session` with that `sessionPath` — from any connection — attaches to the same routing handle and returns
|
|
3336
3338
|
`attached: true`. Retention never outranks an explicit teardown: a `close_session` from an attached connection still
|
|
3337
|
-
reaches zero attachments and closes the session, host shutdown
|
|
3339
|
+
reaches zero attachments and closes the session, a socket host's shutdown parks it (see below), and the idle-eviction
|
|
3340
|
+
window above still
|
|
3338
3341
|
parks it (or the completed-detached-worker rule above parks it sooner) — announced as
|
|
3339
3342
|
`session_parked { sessionId, sessionPath }`, after which the file reopens by path like any
|
|
3340
3343
|
evicted session, and the parked session counts as gone for the empty-host exit below. The park is also recorded
|
|
3341
3344
|
for the NEXT generation, which reopens it on startup; the host that parked it never reopens it on its own, so a
|
|
3342
|
-
client reopening the session right after `session_parked` is the only open of that path (senpi#3485).
|
|
3343
|
-
|
|
3345
|
+
client reopening the session right after `session_parked` is the only open of that path (senpi#3485). Retention survives
|
|
3346
|
+
the host process too (next item). Any attach may turn retention
|
|
3344
3347
|
on for a live session; no attach turns it off for clients that already rely on it. Omitting the flag is byte-identical
|
|
3345
3348
|
to the previous behavior — the session is closed with its last connection — except on a delegated connection
|
|
3346
3349
|
(`declare_delegation`), where every `open_session` is retained (see "Delegated connections"; senpi#5808). Probe `retain_on_disconnect` in
|
|
3347
3350
|
`get_protocol_info` capabilities before relying on it: an older host silently ignores the field.
|
|
3351
|
+
- **Retained sessions across a host restart**: every retained session with a session path is recorded in the
|
|
3352
|
+
endpoint's daemon directory (`handover-parked/`, one entry per session path, the same record a handoff park writes)
|
|
3353
|
+
as soon as its open succeeds; the entry follows the session's `set_session_context` patches and its park, and a
|
|
3354
|
+
transcript switch (`new_session`, `switch_session`, a fork) moves it to the new file, so only the transcript the
|
|
3355
|
+
session is on comes back. An explicit `close_session` or a reclaim removes every key the session was recorded
|
|
3356
|
+
under. While the session is open the entry names the process that holds it; a park drops that, and a host never
|
|
3357
|
+
reopens an entry whose holding process is still alive. So during a generation handoff the successor does not open
|
|
3358
|
+
a session its draining predecessor still has loaded, idle and detached ones included: it reopens the predecessor's
|
|
3359
|
+
park once the drain writes it (or the entry, if the predecessor dies without parking). A host
|
|
3360
|
+
that starts on the same socket and agent dir - after a handoff, a clean stop, a crash, or a kill in the middle of a
|
|
3361
|
+
turn - reopens, in the background right after it binds, every entry an earlier process wrote, replaying the
|
|
3362
|
+
recorded launch profile as `open_session { sessionPath, durableSessionId, retain_on_disconnect: true, ... }`: the same
|
|
3363
|
+
open-by-path a TUI resume runs on its session file. The session is `open` in `list_sessions` again with the same
|
|
3364
|
+
`durableSessionId`, `sessionPath` and transcript, announced by a `session_opened` record, and no client has to
|
|
3365
|
+
re-open it. A clean stop (SIGTERM, idle or empty exit) parks each retained session first: attached clients receive
|
|
3366
|
+
`session_parked { sessionId, sessionPath, reason: "host_shutdown" }`, never `session_closed`, and a turn running at
|
|
3367
|
+
the stop ends with `agent_end { aborted: true, abortSource: "system", abortCause: { kind: "host_shutdown" } }`, never
|
|
3368
|
+
a user abort. A killed host sends nothing. Either way the turn the stopped process was running is not resumed: the
|
|
3369
|
+
transcript holds what was persisted before the stop, as after a TUI crash. The routing handle (`sessionId`, `rpc-N`) is new, since handles are unique per process;
|
|
3370
|
+
a client holding the old one gets `session_not_found` and re-resolves the session from `list_sessions` (or
|
|
3371
|
+
`session_opened`) by `durableSessionId`. A relay that mirrors `list_sessions` (relayd keeps no session store of its
|
|
3372
|
+
own) therefore sees the session again unchanged. Not reopened: an entry whose session file is gone (a session that
|
|
3373
|
+
never received a message has none), an explicitly closed or reclaimed session (its entry is removed), and a session
|
|
3374
|
+
whose context `task_status` is terminal (`completed`, `cancelled`, `error`, `failed`, `lost`, `interrupted`). There is
|
|
3375
|
+
no separate resume-by-durable-id request: `open_session { sessionPath }` already resumes a known transcript, but a
|
|
3376
|
+
remote client learns sessions only from `list_sessions`, so a session the restarted host did not list could not be
|
|
3377
|
+
resumed by one at all.
|
|
3348
3378
|
- **Empty-host exit**: when the registry holds zero sessions AND no client is connected, continuously for
|
|
3349
3379
|
`SENPI_RPC_HOST_EMPTY_EXIT_MS` (default 15 minutes), the host exits through its clean shutdown path (flush, socket
|
|
3350
3380
|
removal), for stdio and `--listen` hosts alike. A connected client counts as occupancy even with no session open,
|
|
@@ -4740,11 +4770,26 @@ Response:
|
|
|
4740
4770
|
"autoCompactionEnabled": true,
|
|
4741
4771
|
"messageCount": 5,
|
|
4742
4772
|
"pendingMessageCount": 0,
|
|
4743
|
-
"pendingDialogs": []
|
|
4773
|
+
"pendingDialogs": [],
|
|
4774
|
+
"permissionPreset": "ask",
|
|
4775
|
+
"permissionPresetSource": "client",
|
|
4776
|
+
"permissionPresetStatus": "applied",
|
|
4777
|
+
"effectivePermissionPreset": "ask"
|
|
4744
4778
|
}
|
|
4745
4779
|
}
|
|
4746
4780
|
```
|
|
4747
4781
|
|
|
4782
|
+
`permissionPreset`, `permissionPresetSource`, `permissionPresetStatus` and `effectivePermissionPreset` report the approval mode the session enforces. `permissionPresetSource` is `client` when a client named a valid preset for this session (`open_session.permissionPreset`, an attach naming one, or `set_permission_preset`) and `host_default` when the host's own default applies (its `--permission-preset` flag, then project and global settings, then `full-access`). `permissionPresetStatus` is `invalid` when the named preset or the host's setting cannot be parsed: the open is still accepted, but every gated tool call is refused, and an invalid preset never counts as one the client set. `effectivePermissionPreset` is the preset the next permission decision uses, `null` while the status is `invalid`. A client confirms a mode it asked for only when `permissionPreset` equals it and `permissionPresetStatus` is `applied`. The `open_session` reply carries the first three fields in `data` as well as in `data.state`.
|
|
4783
|
+
|
|
4784
|
+
A preset a client named is recorded in the session file as a `permission_mode` entry. The entry is a record only: a later open that names no preset still uses the host's own default, and the launch flag always wins.
|
|
4785
|
+
|
|
4786
|
+
Each gated decision picks its preset in this order:
|
|
4787
|
+
|
|
4788
|
+
1. A preset a client named for the session (`open_session.permissionPreset`, an attach naming one, or `set_permission_preset`) wins.
|
|
4789
|
+
2. Work a remote device started asks: a turn started by a `prompt`, `follow_up` or `steer` (`whenIdle: "start"`) from a delegated connection, and engine-started work (extension messages, goal and loop turns, `continue_from_leaf`, `wake`, restored input) on a session a remote device has driven. A session counts as remote-driven once a delegated connection sends it any record whose method class is `drive` or `device_admin`; the engine's method-class table decides, so every run starter is covered. That includes records that start no turn, such as `abort`, `set_session_name` or `clear_queue`, and the mark lasts for the session's lifetime on purpose: telling which drive records can shape later engine-started work would need a second hand-kept list beside the table, and a wrong guess there would run remote-influenced work without asking, so the engine errs toward asking. When the operator named no `--permission-preset`, it uses the preset a client recorded in the session file instead. An embedder's host-level preset is the host default, not a launch flag. This rule is decided before `spawnedBy`: a parent named in `open_session`, real or not, never takes a remote device's work off it.
|
|
4790
|
+
3. A child session (`spawnedBy`) follows its live parent's current mode, including later changes to it. A child whose parent is not live in this process asks, unless the operator named a preset for it with `--permission-preset`.
|
|
4791
|
+
4. Everything else uses the host's own default. A local connection's turn keeps the host default even on a session a remote device has driven, and a `steer` never changes the origin of the turn it joins.
|
|
4792
|
+
|
|
4748
4793
|
`currentTurnId` is the id of the running turn, present only while one runs; a steer or follow-up names it as `expectedTurnId` (on `prompt`, `steer` or `follow_up`).
|
|
4749
4794
|
|
|
4750
4795
|
The `model` field is a full [Model](#model) object or `null`. `pendingModelSwitch` is always present: `null` means no held switch, while an object with `provider` and `id` identifies a switch that applies after the next compaction. The `sessionName` field is the display name set via `set_session_name`, or omitted if not set.
|
|
@@ -5198,6 +5243,21 @@ Response:
|
|
|
5198
5243
|
{"type": "response", "command": "set_auto_compaction", "success": true}
|
|
5199
5244
|
```
|
|
5200
5245
|
|
|
5246
|
+
#### set_permission_preset
|
|
5247
|
+
|
|
5248
|
+
Move this session to another approval mode. It works on a classic `--mode rpc` connection, which refuses `open_session`, and on a multi-session host with the `sessionId` envelope, so a client on another device can change a live session's mode without knowing its session file. It is classed `device_admin`: it changes what the agent may do without asking. Hosts that take it advertise `set_permission_preset_v1`.
|
|
5249
|
+
|
|
5250
|
+
```json
|
|
5251
|
+
{"type": "set_permission_preset", "permissionPreset": "ask"}
|
|
5252
|
+
```
|
|
5253
|
+
|
|
5254
|
+
Response:
|
|
5255
|
+
```json
|
|
5256
|
+
{"type": "response", "command": "set_permission_preset", "success": true, "data": {"permissionPreset": "ask", "permissionPresetSource": "client", "permissionPresetStatus": "applied"}}
|
|
5257
|
+
```
|
|
5258
|
+
|
|
5259
|
+
The new preset applies from the next permission decision, and the session file records it like a preset named at open. An unknown preset is refused with `errorCode` `invalid_launch_profile` and changes nothing, so the session keeps the mode it had. Extensions hear a change on the `permission_preset_changed` event and read the enforced preset with `pi.getPermissionPreset()`.
|
|
5260
|
+
|
|
5201
5261
|
### Retry
|
|
5202
5262
|
|
|
5203
5263
|
#### set_auto_retry
|
|
@@ -5893,6 +5953,32 @@ Response:
|
|
|
5893
5953
|
|
|
5894
5954
|
`kind` is the admission outcome (`started`, `queued`, `steered`, `already_admitted`, `held_draft`, `turn_conflict`); `admitted` is `[]` when no drain is registered.
|
|
5895
5955
|
|
|
5956
|
+
#### admit_external_message
|
|
5957
|
+
|
|
5958
|
+
Admits a message another agent sent this session (a relayed peer delivery) exactly as `pi.session.admitExternalMessage` does in process (multi-session hosts: session-scoped). The engine writes its own `session_control_delivery` custom entry, with `details.sender` and `details.display_text` for human surfaces: never a `role: "user"` message, never the extension `input` event, and a `/`-led `text` is not dispatched as a command or expanded as a template. Method class `drive`.
|
|
5959
|
+
|
|
5960
|
+
```json
|
|
5961
|
+
{"type": "admit_external_message", "sessionId": "rpc-1", "deliveryId": "d7", "text": "[from peer] build is green", "sender": {"kind": "agent", "session_id": "s-42", "name": "reviewer"}, "displayText": "build is green", "deliverAs": "followUp"}
|
|
5962
|
+
```
|
|
5963
|
+
|
|
5964
|
+
Response:
|
|
5965
|
+
```json
|
|
5966
|
+
{
|
|
5967
|
+
"type": "response",
|
|
5968
|
+
"command": "admit_external_message",
|
|
5969
|
+
"success": true,
|
|
5970
|
+
"data": { "kind": "started", "turnId": "0" }
|
|
5971
|
+
}
|
|
5972
|
+
```
|
|
5973
|
+
|
|
5974
|
+
- `deliveryId` (1-256 chars), `text` (non-empty) and `deliverAs` (`steer` | `followUp`) are required. `sender` is `{ kind: "agent", session_id, name? }`, `{ kind: "command_line", user? }` or `{ kind: "external", platform, author? }` with no other field.
|
|
5975
|
+
- `whenIdle`: `start` (default) starts a turn on an idle session; `queue` holds it for the next turn.
|
|
5976
|
+
- `expectedTurnId` is `get_state.currentTurnId` (a string). A steer whose `expectedTurnId` is not the current turn, or a mid-turn steer that names none, answers `kind: "turn_conflict"` and admits nothing. The reply's `turnId` is the session's turn at admission, in the same form.
|
|
5977
|
+
- `chainCount` is a thread call's hop count, checked against `threads.chainCountLimit` before anything is admitted (refused with `chain_count_limit` / `invalid_chain_count`, as on `prompt`/`steer`/`follow_up`); the admitted hop is written as `details.chain_count`.
|
|
5978
|
+
- `kind` is the same outcome `wake` reports (`held_draft`: retry; nothing was queued). The ledger keys a local connection's `deliveryId` as `rpc:local/<deliveryId>` and a remote device's as `device:<deviceId>/<deliveryId>`, so RPC ids never collide with in-process deliveries or with another device's. An id the runtime already holds, or that the transcript already holds an entry for (after a restart too), answers `already_admitted`.
|
|
5979
|
+
- On a connection that speaks for a remote device (a delegated connection, or one whose `set_client_info` declared `remoteOrigin`), the engine stamps that device as `details.remote_origin` and writes `text` unchanged, so the model reads exactly what a delivery on a local connection carries and no device (senpi#6250). It keeps `text` as `details.display_text` when no `displayText` is given, and refuses a `command_line` sender (`sender_not_allowed`). A delegated connection that names no remote device is refused (`delegated_origin_unknown`), never admitted as a local delivery. A record that names `remote_origin` is refused on every connection. The turn that consumes the delivery takes the connection's origin, so a remote device's follow-up runs under the delegated approval-mode rule even inside a local turn.
|
|
5980
|
+
- Once `release_session` has closed the session's admission, the record answers `success: false` with the release's message: the delivery stays with its sender.
|
|
5981
|
+
|
|
5896
5982
|
#### release_session
|
|
5897
5983
|
|
|
5898
5984
|
Multi-session hosts only. Hands the session to a runtime outside the host; see "Handing a session over (`release_session`)".
|
|
@@ -6259,7 +6345,7 @@ When a multi-session host ends a routing handle it may name why:
|
|
|
6259
6345
|
| `client_close` | An attached client sent `close_session` |
|
|
6260
6346
|
| `idle_evicted` | The idle sweep ended a session that was not retained |
|
|
6261
6347
|
| `session_dir_removed` | The session's transcript directory was deleted and nothing can reopen it: the idle sweep ended it because no client held it (whatever the idle window), or a generation handoff drain ended it instead of parking it. No `sessionPath` |
|
|
6262
|
-
| `host_shutdown` | The host process is exiting (SIGTERM, idle-exit, empty-host). A retained session
|
|
6348
|
+
| `host_shutdown` | The host process is exiting (SIGTERM, idle-exit, empty-host). A retained session on a socket host is parked instead (`session_parked { reason: "host_shutdown" }`) |
|
|
6263
6349
|
| `replaced` | The routing handle ended because the live session behind it was replaced |
|
|
6264
6350
|
| `handoff_parked` | A generation handoff drained this host; reopen with `open_session { sessionPath }` |
|
|
6265
6351
|
| `released` | `release_session` handed the session to a runtime outside this host. Carries `sessionPath`, the file that runtime now writes: do NOT reopen it here |
|
|
@@ -6387,6 +6473,37 @@ capability:
|
|
|
6387
6473
|
}
|
|
6388
6474
|
```
|
|
6389
6475
|
|
|
6476
|
+
#### Background session age
|
|
6477
|
+
|
|
6478
|
+
The terminal builtin emits `terminal_background_age` on the same path, and on the in-process `pi.events`
|
|
6479
|
+
bus, when a background bash session is still running at 10 minutes and then every 30 minutes (10, 40,
|
|
6480
|
+
70, ...), and when the opt-in auto-reap (`terminal.backgroundAutoReapMinutes` or the `bash` parameter
|
|
6481
|
+
`auto_reap_minutes`) has stopped one, or once when its stop did not end the session. `data` has `kind`
|
|
6482
|
+
(`running`, `reaped`, or `reap_failed`), `id` (the `bash_id`), `description`, optional `command`,
|
|
6483
|
+
`startedAtMs`, `ageMs`, `ageMinutes`, `autoReapMinutes` (the age at which the session is stopped
|
|
6484
|
+
automatically, or `null` when nothing will stop it), `reason` (on `reap_failed` only: why the stop did not
|
|
6485
|
+
end it), and `stop`, the call that stops it. After `reap_failed` the session is still running, the
|
|
6486
|
+
`running` reports go on, and the auto-reap does not try again. Nothing is stopped by this event; a client that wants the session gone calls `kill_bash`
|
|
6487
|
+
with `stop.args`. A report that came due while the host slept arrives once, not once per missed interval.
|
|
6488
|
+
|
|
6489
|
+
```json
|
|
6490
|
+
{
|
|
6491
|
+
"type": "extension_event",
|
|
6492
|
+
"name": "terminal_background_age",
|
|
6493
|
+
"data": {
|
|
6494
|
+
"kind": "running",
|
|
6495
|
+
"id": "bash_3",
|
|
6496
|
+
"description": "npm run dev",
|
|
6497
|
+
"command": "npm run dev",
|
|
6498
|
+
"startedAtMs": 1710000000000,
|
|
6499
|
+
"ageMs": 600000,
|
|
6500
|
+
"ageMinutes": 10,
|
|
6501
|
+
"autoReapMinutes": null,
|
|
6502
|
+
"stop": { "tool": "kill_bash", "args": { "bash_id": "bash_3" } }
|
|
6503
|
+
}
|
|
6504
|
+
}
|
|
6505
|
+
```
|
|
6506
|
+
|
|
6390
6507
|
#### Job widgets
|
|
6391
6508
|
|
|
6392
6509
|
The `job-widgets` builtin emits `job_widget_state` on the same path: live, display-ready widgets for the
|
|
@@ -6450,7 +6567,7 @@ cannot replace the ID on a queued start event. Older engines omit this additive
|
|
|
6450
6567
|
|
|
6451
6568
|
### agent_end
|
|
6452
6569
|
|
|
6453
|
-
Emitted when one low-level agent run completes. Contains all messages generated during this run. If `willRetry` is true, an automatic retry will follow. A run stopped because its session was replaced also carries `abortCause: { kind: "session_replaced", reason, targetSessionFile?, targetSessionId? }` (see `session_replaced`); extensions' own `agent_end` does not carry it, they learn of the replacement from `session_shutdown`.
|
|
6570
|
+
Emitted when one low-level agent run completes. Contains all messages generated during this run. If `willRetry` is true, an automatic retry will follow. A run stopped because its session was replaced also carries `abortCause: { kind: "session_replaced", reason, targetSessionFile?, targetSessionId? }` (see `session_replaced`); a run the host's own stop cut short ends with `aborted: true`, `abortSource: "system"` and `abortCause: { kind: "host_shutdown" }`, never `abortSource: "user"` (senpi#6190); extensions' own `agent_end` does not carry it, they learn of the replacement from `session_shutdown`.
|
|
6454
6571
|
|
|
6455
6572
|
```json
|
|
6456
6573
|
{
|
package/docs/terminal-tools.md
CHANGED
|
@@ -21,6 +21,28 @@ Background sessions are NEVER killed by `timeout` (they live until they exit or
|
|
|
21
21
|
call `kill_bash`), even though the bash-timeout extension injects a default `timeout`
|
|
22
22
|
into every `bash` call. Foreground calls behave like the classic `bash` tool.
|
|
23
23
|
|
|
24
|
+
### Long-running background sessions
|
|
25
|
+
|
|
26
|
+
A background session that is still running (started with `run_in_background`, or a
|
|
27
|
+
foreground command that auto-detached) is reported once it has run 10 minutes, then
|
|
28
|
+
every 30 minutes after that (10, 40, 70, ... minutes). Each report names the session's
|
|
29
|
+
`bash_id` and its command or label, says how long it has been running, and gives the
|
|
30
|
+
call that stops it (`kill_bash({ bash_id })`). It reaches the agent like a completion
|
|
31
|
+
notice (subject to `notify`), shows in the TUI, and goes to RPC clients as the
|
|
32
|
+
`terminal_background_age` event ([RPC](rpc.md#background-session-age)). A report that came
|
|
33
|
+
due while the machine slept is sent once, not once per missed interval.
|
|
34
|
+
|
|
35
|
+
Nothing is stopped automatically by default. To stop long sessions at a set age, opt in
|
|
36
|
+
with the `terminal.backgroundAutoReapMinutes` setting, or per call with the `bash`
|
|
37
|
+
parameter `auto_reap_minutes` (which wins over the setting; `0` turns it off for that
|
|
38
|
+
call). At that age the session is stopped the way `kill_bash` stops it (TERM to its
|
|
39
|
+
process group, then KILL after a grace), its completion notice says it was stopped
|
|
40
|
+
automatically, and a `terminal_background_age` event with `kind: "reaped"` is sent.
|
|
41
|
+
It counts as reaped only once that stop has really ended the session. If the stop fails
|
|
42
|
+
or the session survives it, one notice (and a `kind: "reap_failed"` event) says
|
|
43
|
+
auto-reap failed and why; the session keeps running, its age reports go on, and the
|
|
44
|
+
auto-reap does not retry.
|
|
45
|
+
|
|
24
46
|
Background sessions and monitors also survive a session reload (`/reload`): existing
|
|
25
47
|
`bash_N` ids stay addressable, watchers keep injecting events, and completion
|
|
26
48
|
notifications keep arriving. Quitting or switching sessions still tears everything down.
|
|
@@ -214,7 +236,8 @@ Configure the suite under `terminal` in `settings.json` (global
|
|
|
214
236
|
"scrollback": 10000,
|
|
215
237
|
"maxSessions": 32,
|
|
216
238
|
"timeoutAction": "background",
|
|
217
|
-
"notify": "wake"
|
|
239
|
+
"notify": "wake",
|
|
240
|
+
"backgroundAutoReapMinutes": 120
|
|
218
241
|
}
|
|
219
242
|
}
|
|
220
243
|
```
|
|
@@ -223,6 +246,7 @@ Configure the suite under `terminal` in `settings.json` (global
|
|
|
223
246
|
- `scrollback` — xterm scrollback lines per session (default 10000).
|
|
224
247
|
- `maxSessions` — concurrent sessions before least-recently-used exited sessions are pruned (default 32).
|
|
225
248
|
- `timeoutAction` — fate of a foreground timeout (default `background`).
|
|
249
|
+
- `backgroundAutoReapMinutes` — stop a background session once it has run this many minutes (default unset: never). A whole number of minutes of at least 1 turns it on; `0` or any other value leaves it off. The `bash` parameter `auto_reap_minutes` overrides it per call.
|
|
226
250
|
- `notify` — async completion behavior: `wake` (wake an idle interactive agent once), `next-turn`, or `off` (default `wake`). Non-interactive `-p` / `--print` / `--mode json` runs never wake.
|
|
227
251
|
- `killGuard` — `on` (default) or `off`, read from the global settings file only (a project value is ignored). While on, an agent command or eval cell that would signal every process (`kill -1`, a `pkill` pattern such as `a||b`, `.*` or one letter), a protected process (pid 1, the agent, its ancestors, the user's service manager) or a process group holding one is refused before it runs with a `[senpi:kill-guard]` message naming the rule and a narrow alternative. Narrow kills by pid, exact name (`pkill -x`) or anchored pattern run as before. Values only known when a command runs (`pkill -f "$P"`, `xargs kill`, a script's `kill $PID`) are judged then: on macOS and Linux every agent shell, monitor and eval kernel gets `kill`, `pkill` and `killall` shims first on `PATH` (in `<agent dir>/kill-guard/<version>/`) and a `BASH_ENV` that wraps bash's builtin `kill`, chaining any `BASH_ENV` you already set; an interactive bash the agent starts gets the same wrapper from a `PROMPT_COMMAND` hook before its first prompt, after your startup files, and your own `PROMPT_COMMAND` still runs after it; a refused command exits 126 with the same message. This also judges what the agent types into a running shell with `bash_input` (edited, recalled or history-expanded lines included), because the shell judges the line it actually runs. Builtin `kill` in dash or zsh, in `bash --posix -c` and in `bash -i -c`, in an interactive bash whose startup files replace `PROMPT_COMMAND`, and a `kill` named by absolute path, are judged only from the command text. A command you type yourself with `!` is never judged and gets no shims. `off` restores the unguarded behavior (no shims, no `BASH_ENV`, `PROMPT_COMMAND` left as you set it) and shows one notice per session.
|
|
228
252
|
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.11.
|
|
3
|
+
"version": "2026.11.9",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
94
|
"@earendil-works/chord": "0.99.1",
|
|
95
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.
|
|
96
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.
|
|
97
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.
|
|
95
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.9",
|
|
96
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.9",
|
|
97
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.9",
|
|
98
98
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
99
99
|
"ajv": "8.20.0",
|
|
100
100
|
"chalk": "6.0.0",
|
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
"yaml": "2.9.1",
|
|
116
116
|
"@anthropic-ai/claude-agent-sdk": "0.3.306",
|
|
117
117
|
"@anthropic-ai/sdk": "0.127.0",
|
|
118
|
-
"@code-yeongyu/senpi-codemode": "2026.11.
|
|
119
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.
|
|
118
|
+
"@code-yeongyu/senpi-codemode": "2026.11.9",
|
|
119
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.9",
|
|
120
120
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
121
121
|
"@mozilla/readability": "0.6.0",
|
|
122
122
|
"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{rateLimitModelFamily}from"./chunk-6VWIMHMH.js";import{describeProviderFailureForUser,isQuotaExhaustionMessage}from"./chunk-OKMHSVCA.js";var BILLING_ERROR_PATTERN=new RegExp(["credit[- ]balance","insufficient[_ -]quota","\\bbilling\\b","purchase credits","credits[-_ ]required","credits are required","usage_limit_reached","usage_not_included","usage limit has been reached"].join("|"),"i"),STRONG_BILLING_ERROR_PATTERN=new RegExp(["credit[- ]balance","insufficient[_ -]quota","purchase credits","credits[-_ ]required","credits are required","usage_limit_reached","usage_not_included","usage limit has been reached"].join("|"),"i"),TRANSIENT_SERVER_SIGNAL_PATTERN=/(?:^|[^0-9])(?:500|502|503|504|520|522|524)(?:[^0-9]|$)|"type"\s*:\s*"server_error"/i;function isBillingErrorMessage(errorMessage){return errorMessage===void 0?!1:STRONG_BILLING_ERROR_PATTERN.test(errorMessage)?!0:BILLING_ERROR_PATTERN.test(errorMessage)?!TRANSIENT_SERVER_SIGNAL_PATTERN.test(errorMessage):!1}var AccountUsageLimitError=class extends Error{constructor(provider,raw="usage limit reached",resetsAt){super(describeProviderFailureForUser(raw,{model:provider,usageLimit:"account",...resetsAt===void 0?{}:{resetsAt:formatResetTime(resetsAt)}})),this.name="AccountUsageLimitError"}};function formatResetTime(epochMs){return new Date(epochMs).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",timeZoneName:"short"})}var SUBSCRIPTION_LIMIT_PATTERN=/\bhit\s+your\b[^.]*\blimit\b|\b(?:session|weekly|monthly|daily|hourly|\d+[- ]hour|usage)\s+limit\b|\bblocking_limit\b|\brapid_refill_breaker\b/i,PLAN_MODEL_LIMIT_PATTERN=/\breached\b[^.]*\b(?:free|plan|tier|model)\b[^.]*\blimit\b[\s\S]*\bswitch to (?:a |an )?(?:different|another) model\b/i,MODEL_SCOPE_PATTERN=/\bmodels?\b|\bpremium\b|\b(?:opus|sonnet|haiku|fable|mythos)\b/i;function usageLimitScope(failure){if(typeof failure=="object"&&failure.errorCause?.type==="usage_limit"){let scope=failure.errorCause.scope;if(scope==="account"||scope==="model")return scope}let errorMessage=typeof failure=="object"?failure.errorMessage:failure;if(errorMessage===void 0)return;if(rateLimitModelFamily(errorMessage)!==void 0)return"model";if(isQuotaExhaustionMessage(errorMessage)||isBillingErrorMessage(errorMessage)||SUBSCRIPTION_LIMIT_PATTERN.test(errorMessage)||PLAN_MODEL_LIMIT_PATTERN.test(errorMessage))return MODEL_SCOPE_PATTERN.test(errorMessage)?"model":"account"}function describeUsageLimitMessage(message){let scope=usageLimitScope(message);return!scope||message.errorCause?.type==="usage_limit"&&message.errorCause.scope===scope?message:{...message,errorCause:{type:"usage_limit",scope},...scope==="account"?{errorMessage:describeProviderFailureForUser(message.errorMessage,{model:`${message.provider}/${message.model}`,usageLimit:scope})}:{}}}function describeUsageLimitEntry(entry){if(entry.type!=="message"||entry.message.role!=="assistant")return entry;let message=describeUsageLimitMessage(entry.message);return message===entry.message?entry:{...entry,message}}function usageLimitCause(from,limit){if(limit==="model")return`${from} hit its usage limit`;if(limit==="account")return`the ${from.slice(0,Math.max(0,from.indexOf("/")))||from} account hit its usage limit, so its other models were skipped`}var SecretInputUnavailableError=class extends Error{constructor(reason){super("This client cannot collect a secret; use the TUI or the host's settings route."),this.code="secret_input_unavailable",this.name="SecretInputUnavailableError",this.reason=reason}};function resolveSecretInputTimeout(opts){let requested=opts?.timeout??18e5;if(!Number.isFinite(requested)||requested<=0)throw new Error("Invalid secret input timeout");let timeout=Math.ceil(requested);if(!Number.isSafeInteger(Date.now()+timeout))throw new Error("Invalid secret input timeout");return timeout}function armSecretInputDeadline(deadline,expire){let timer,check=()=>{let remaining=deadline-Date.now();remaining<=0?expire():timer=setTimeout(check,Math.min(remaining,2147483647))};return timer=setTimeout(check,Math.max(0,Math.min(deadline-Date.now(),2**31-1))),()=>clearTimeout(timer)}function isSecretInputValue(value){return!/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/u.test(value)}import{realpathSync,watch}from"node:fs";import{opendir}from"node:fs/promises";var FS_WATCH_RETRY_DELAY_MS=5e3;function closeWatcher(watcher){if(watcher)try{watcher.close()}catch{}}function canonicalWatchPath(path){if(process.platform!=="win32")return path;try{return realpathSync.native(path)}catch{return path}}async function probeDirectoryOpenable(directory){let handle=await opendir(directory);try{await handle.read()}finally{await handle.close()}}function watchWithErrorHandler(path,listener,onError,options){try{let watcher=watch(canonicalWatchPath(path),{...options,encoding:"utf8"},listener);return watcher.on("error",onError),watcher}catch{return onError(),null}}export{FS_WATCH_RETRY_DELAY_MS,closeWatcher,canonicalWatchPath,probeDirectoryOpenable,watchWithErrorHandler,isBillingErrorMessage,AccountUsageLimitError,usageLimitScope,describeUsageLimitMessage,describeUsageLimitEntry,usageLimitCause,SecretInputUnavailableError,resolveSecretInputTimeout,armSecretInputDeadline,isSecretInputValue};
|