@code-yeongyu/senpi 2026.9.24-3 → 2026.9.25
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 +26 -0
- package/dist/bundle/chunks/{app-server-command-OTRHYRIB.js → app-server-command-RB6QUPRI.js} +1 -1
- package/dist/bundle/chunks/{chunk-UM43CBL4.js → chunk-JJ3NJZBD.js} +118 -111
- package/dist/bundle/chunks/chunk-SGYEOIOJ.js +8 -0
- package/dist/bundle/chunks/chunk-YS5YEX4S.js +2 -0
- package/dist/bundle/chunks/{cli-main-TX6I4X4F.js → cli-main-KO53KYDN.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-UI5Q6FGY.js → interactive-mode-ABDNLJSI.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-D6HMI3RQ.js → multi-session-host-JV3MNWXZ.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-3UWH3ZZS.js → package-manager-cli-ZYYAVCXU.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-LXIZHJSJ.js → rpc-mode-Q2OUJURB.js} +1 -1
- package/dist/bundle/chunks/runtime-session-XBT646O5.js +2 -0
- package/dist/bundle/chunks/session-worker.js +184 -177
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts +7 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +23 -4
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/durable-command.d.ts +11 -16
- package/dist/core/extensions/builtin/terminal/durable-command.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/durable-command.js +97 -43
- package/dist/core/extensions/builtin/terminal/durable-command.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/durable-file.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/durable-file.js +11 -4
- package/dist/core/extensions/builtin/terminal/durable-file.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension-state.d.ts +26 -0
- package/dist/core/extensions/builtin/terminal/extension-state.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/extension-state.js +151 -0
- package/dist/core/extensions/builtin/terminal/extension-state.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +32 -308
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/lease-file.d.ts +52 -0
- package/dist/core/extensions/builtin/terminal/lease-file.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/lease-file.js +205 -0
- package/dist/core/extensions/builtin/terminal/lease-file.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/lease-keeper.d.ts +33 -0
- package/dist/core/extensions/builtin/terminal/lease-keeper.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/lease-keeper.js +110 -0
- package/dist/core/extensions/builtin/terminal/lease-keeper.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts +1 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +6 -0
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts +48 -4
- package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manifest-lease.js +141 -59
- package/dist/core/extensions/builtin/terminal/manifest-lease.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +2 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +15 -10
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-state-dir.d.ts +11 -0
- package/dist/core/extensions/builtin/terminal/monitor-state-dir.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-state-dir.js +28 -0
- package/dist/core/extensions/builtin/terminal/monitor-state-dir.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/orphan-reaper.d.ts +41 -0
- package/dist/core/extensions/builtin/terminal/orphan-reaper.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/orphan-reaper.js +131 -0
- package/dist/core/extensions/builtin/terminal/orphan-reaper.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/process-identity.d.ts +21 -0
- package/dist/core/extensions/builtin/terminal/process-identity.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/process-identity.js +34 -0
- package/dist/core/extensions/builtin/terminal/process-identity.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/process-start-probe.d.ts +6 -0
- package/dist/core/extensions/builtin/terminal/process-start-probe.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/process-start-probe.js +60 -0
- package/dist/core/extensions/builtin/terminal/process-start-probe.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/prompt.js +8 -4
- package/dist/core/extensions/builtin/terminal/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/restore-digest.d.ts +54 -0
- package/dist/core/extensions/builtin/terminal/restore-digest.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/restore-digest.js +133 -0
- package/dist/core/extensions/builtin/terminal/restore-digest.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/restore-session.d.ts +56 -0
- package/dist/core/extensions/builtin/terminal/restore-session.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/restore-session.js +238 -0
- package/dist/core/extensions/builtin/terminal/restore-session.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/restore.d.ts +52 -19
- package/dist/core/extensions/builtin/terminal/restore.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/restore.js +63 -138
- package/dist/core/extensions/builtin/terminal/restore.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.js +17 -0
- package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-activity.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/session-activity.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/session-activity.js +41 -0
- package/dist/core/extensions/builtin/terminal/session-activity.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/shared.d.ts +14 -0
- package/dist/core/extensions/builtin/terminal/shared.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/shared.js +14 -0
- package/dist/core/extensions/builtin/terminal/shared.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/terminal-manifest-model.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest-model.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/terminal-manifest-model.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.d.ts +15 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.js +148 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts +15 -2
- package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js +62 -11
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/terminal-state-gc.d.ts +17 -0
- package/dist/core/extensions/builtin/terminal/terminal-state-gc.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-state-gc.js +81 -0
- package/dist/core/extensions/builtin/terminal/terminal-state-gc.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +16 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.d.ts +27 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.js +75 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-schema.d.ts +35 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-schema.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-schema.js +67 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-schema.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +14 -35
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +30 -110
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/first-turn.d.ts +7 -6
- package/dist/core/extensions/builtin/todotools/first-turn.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/first-turn.js +8 -8
- package/dist/core/extensions/builtin/todotools/first-turn.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/index.js +1 -1
- package/dist/core/extensions/builtin/todotools/index.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/todo-format.js +1 -1
- package/dist/core/extensions/builtin/todotools/todo-format.js.map +1 -1
- package/dist/core/extensions/builtin/ttsr/detectors/repetitive-turns.d.ts.map +1 -1
- package/dist/core/extensions/builtin/ttsr/detectors/repetitive-turns.js +4 -0
- package/dist/core/extensions/builtin/ttsr/detectors/repetitive-turns.js.map +1 -1
- package/dist/core/extensions/builtin/ttsr/prompts.js +1 -1
- package/dist/core/extensions/builtin/ttsr/prompts.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +6 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/docs/extensions.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-desktop-engine/README.md +31 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.d.ts +38 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.js +117 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.d.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.js +3 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.d.ts +42 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.js +79 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/native/check-prebuild-fresh.mjs +160 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/native/index.d.ts +20 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/native/index.js +23 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/native/prebuilds/darwin-arm64/senpi-desktop-engine +0 -0
- package/node_modules/@code-yeongyu/senpi-desktop-engine/package.json +53 -0
- package/node_modules/@code-yeongyu/senpi-desktop-prelude/README.md +20 -0
- package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.d.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.js +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-prelude/package.json +44 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/README.md +20 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.d.ts +38 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.js +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.d.ts +28 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.js +102 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.d.ts +14 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.js +71 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.d.ts +7 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.js +4 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.d.ts +66 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.js +8 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.d.ts +44 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.js +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.d.ts +59 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.js +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-protocol/package.json +45 -0
- package/node_modules/@code-yeongyu/senpi-desktop-service/README.md +27 -0
- package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.d.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.js +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-service/package.json +44 -0
- package/node_modules/@code-yeongyu/senpi-desktop-tool/README.md +20 -0
- package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.d.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.d.ts.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.js +2 -0
- package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.js.map +1 -0
- package/node_modules/@code-yeongyu/senpi-desktop-tool/package.json +44 -0
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts +2 -0
- package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js +6 -0
- package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session-bun.d.ts +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-bun.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session-bun.js +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-bun.js.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session-native.js +5 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-native.js.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.d.ts +11 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.js +24 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.js.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-types.d.ts +2 -0
- package/node_modules/@earendil-works/pi-pty/dist/session-types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session-types.js.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session.d.ts +4 -5
- package/node_modules/@earendil-works/pi-pty/dist/session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/session.js +11 -16
- package/node_modules/@earendil-works/pi-pty/dist/session.js.map +1 -1
- package/node_modules/@earendil-works/pi-pty/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +22 -7
- package/dist/bundle/chunks/chunk-GFUZPFHN.js +0 -2
- package/dist/bundle/chunks/chunk-JBOV6VNK.js +0 -8
- package/dist/bundle/chunks/runtime-session-F3EXC44D.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,wBAAwB,EAA2C,MAAM,mCAAmC,CAAC;AACtH,OAAO,EAGN,yBAAyB,GAGzB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,OAAO,4BAA6B,SAAQ,wBAAwB;IACzE,YAAY,OAAe,EAAE,OAA6B;QACzD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC5C,CAAC;CACD;AAID,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,MAAM,CAAU,CAAC;AACnD,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,qBAAqB,EAAE,mBAAmB,CAAU,CAAC;AAC9F,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAClD,MAAM,KAAK,GAAG,CAAC,KAAc,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACjG,MAAM,KAAK,GAAG,CAAC,KAAc,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvG,MAAM,MAAM,GAAG,CAAC,KAAc,EAAoB,EAAE,CAAC,OAAO,KAAK,KAAK,SAAS,CAAC;AAChF,MAAM,KAAK,GAAG,CAAC,KAAc,EAAgB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAErH,SAAS,OAAO,CAAC,OAAe;IAC/B,MAAM,IAAI,4BAA4B,CAAC,iCAAiC,OAAO,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,GAAG,CAAC,GAAQ,EAAE,KAAa;IACnC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,SAAS,KAAK,6BAA6B,CAAC,CAAC;IAC7E,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,GAAG,CAAC,GAAQ,EAAE,KAAa;IACnC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,SAAS,KAAK,0BAA0B,CAAC,CAAC;IAC1E,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,MAAM,CAAC,GAAQ,EAAE,KAAa;IACtC,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,SAAS,KAAK,mBAAmB,CAAC,CAAC;IAC/E,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,IAAI,CAAC,GAAQ,EAAE,KAAa;IACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,SAAS,KAAK,oBAAoB,CAAC,CAAC;IACrE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,GAAG,CAAI,GAAQ,EAAE,KAAa,EAAE,QAAwC;IAChF,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,KAAK,CAAmB,MAAoB,EAAE,GAAQ,EAAE,KAAa;IAC7E,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAU,CAAC;QAC5D,OAAO,CAAC,SAAS,KAAK,oBAAoB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,OAAO,KAAU,CAAC;AACnB,CAAC;AAED,SAAS,UAAU,CAAC,GAAY;IAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,wCAAwC,CAAC,CAAC;IACnE,OAAO;QACN,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;QACpB,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;QACpB,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC;QAC5B,+FAA+F;QAC/F,0FAA0F;QAC1F,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC;QAC7B,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC;KAC7B,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,GAAY;IAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,oCAAoC,CAAC,CAAC;IAC/D,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IACnC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,mCAAmC,CAAC,CAAC;IAChE,OAAO;QACN,SAAS,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC;QAClC,SAAS,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC;QAClC,WAAW,EAAE,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC;QACtC,WAAW,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC;QACvD,eAAe,EAAE,KAAK,CAAC,kBAAkB,EAAE,KAAK,EAAE,iBAAiB,CAAC;QACpE,OAAO,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC;QACnC,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;QAC7B,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1E,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC;QACjC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;QAC3B,cAAc,EAAE,GAAG,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,CAAC;QACjD,SAAS,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC;QAClC,SAAS,EAAE,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC;QACrG,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC;QACrC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC;QACnC,cAAc,EAAE,KAAK,CAAC,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC;QACvF,cAAc,EAAE,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC;QAC7C,sFAAsF;QACtF,+EAA+E;QAC/E,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC;KACxC,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAc;IAC7C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,8CAA8C,CAAC,CAAC;IAC3E,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,CAAC;AACzG,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,qBAAqB,CAAC,GAAY,EAAE,GAAoB;IACvE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC7E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAAE,OAAO,CAAC,2CAA2C,CAAC,CAAC;IACjG,OAAO;QACN,OAAO,EAAE,yBAAyB;QAClC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC;QACxC,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,sBAAsB,CAAC;QACtE,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC;KAChC,CAAC;AACH,CAAC;AAsBD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CACnC,QAA+B,EAC/B,OAAgD,EAChD,SAAiB;IAEjB,IAAI,CAAC,OAAO,CAAC,cAAc;QAAE,OAAO,UAAU,CAAC;IAC/C,QAAQ,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5B,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,gGAAgG;AAChG,MAAM,CAAC,MAAM,mBAAmB,GAAoB;IACnD,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClD,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;CAChD,CAAC;AAiBF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAoC;IAC9E,MAAM,MAAM,GAAkB;QAC7B,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,CAAC;QACR,iBAAiB,EAAE,CAAC;QACpB,UAAU,EAAE,KAAK;KACjB,CAAC;IACF,IAAI,KAA8B,CAAC;IACnC,IAAI,CAAC;QACJ,KAAK,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACR,2FAA2F;QAC3F,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACxC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAoB,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAClF,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACtC,oFAAoF;QACpF,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC;YAC5D,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;YACpB,SAAS;QACV,CAAC;QACD,IAAI,OAAO,CAAC,eAAe,KAAK,WAAW,EAAE,CAAC;YAC7C,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;YACjB,SAAS;QACV,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3E,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,4EAA4E;IAC5E,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC;IAC/C,OAAO,MAAM,CAAC;AACf,CAAC","sourcesContent":["/**\n * The terminal manifest's read side and restore orchestrator: the strict fail-closed\n * parse every persisted manifest must survive, plus `restoreTerminalState`, which\n * replays the manifest after a restart. Each monitor is classified by its durability\n * class and handed to that class's handler; ephemeral monitors and background sessions\n * are always lost, and an expired watch never reaches its handler. Both durable\n * handler slots ship as stubs reporting `lost` — later phases plug the real ones in.\n */\n\nimport { InvalidSidecarStoreError, type SidecarStore, type SidecarStoreRef } from \"../../../session-sidecar-store.ts\";\nimport {\n\ttype ManifestBackgroundSession,\n\ttype ManifestMonitor,\n\tTERMINAL_MANIFEST_VERSION,\n\ttype TerminalManifest,\n\ttype TerminalManifestCheckpoint,\n} from \"./terminal-manifest.ts\";\n\nexport class InvalidTerminalManifestError extends InvalidSidecarStoreError {\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t\tthis.name = \"InvalidTerminalManifestError\";\n\t}\n}\n\ntype Raw = Record<string, unknown>;\n\nconst RUNTIME_KINDS = [\"command\", \"file\"] as const;\nconst DURABILITY_CLASSES = [\"ephemeral\", \"restartable-command\", \"checkpointed-file\"] as const;\nconst FILE_EVENTS = [\"create\", \"modify\"] as const;\nconst isStr = (value: unknown): value is string => typeof value === \"string\" && value.length > 0;\nconst isNum = (value: unknown): value is number => typeof value === \"number\" && Number.isFinite(value);\nconst isBool = (value: unknown): value is boolean => typeof value === \"boolean\";\nconst isObj = (value: unknown): value is Raw => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nfunction invalid(message: string): never {\n\tthrow new InvalidTerminalManifestError(`terminal manifest is invalid: ${message}`);\n}\n\nfunction str(raw: Raw, field: string): string {\n\tif (!isStr(raw[field])) invalid(`field ${field} must be a non-empty string`);\n\treturn raw[field];\n}\n\nfunction num(raw: Raw, field: string): number {\n\tif (!isNum(raw[field])) invalid(`field ${field} must be a finite number`);\n\treturn raw[field];\n}\n\nfunction digest(raw: Raw, field: string): string {\n\tif (typeof raw[field] !== \"string\") invalid(`field ${field} must be a string`);\n\treturn raw[field];\n}\n\nfunction bool(raw: Raw, field: string): boolean {\n\tif (!isBool(raw[field])) invalid(`field ${field} must be a boolean`);\n\treturn raw[field];\n}\n\nfunction opt<T>(raw: Raw, field: string, required: (raw: Raw, field: string) => T): T | undefined {\n\treturn raw[field] === undefined ? undefined : required(raw, field);\n}\n\nfunction oneOf<T extends string>(values: readonly T[], raw: Raw, field: string): T {\n\tconst value = raw[field];\n\tif (typeof value !== \"string\" || !values.includes(value as T))\n\t\tinvalid(`field ${field} must be one of: ${values.join(\", \")}`);\n\treturn value as T;\n}\n\nfunction checkpoint(raw: unknown): TerminalManifestCheckpoint {\n\tif (!isObj(raw)) invalid(\"field lastCheckpoint must be an object\");\n\treturn {\n\t\tdev: num(raw, \"dev\"),\n\t\tino: num(raw, \"ino\"),\n\t\tsize: num(raw, \"size\"),\n\t\tmtimeMs: num(raw, \"mtimeMs\"),\n\t\t// A checkpoint without a digest cannot detect a same-size, same-mtime rewrite, so the field is\n\t\t// required — but an absent file legitimately checkpoints an empty digest, so \"\" is valid.\n\t\tdigest: digest(raw, \"digest\"),\n\t\tpresent: bool(raw, \"present\"),\n\t};\n}\n\nfunction fireWindow(raw: unknown): { startMs: number; count: number } {\n\tif (!isObj(raw)) invalid(\"field fireWindow must be an object\");\n\treturn { startMs: num(raw, \"startMs\"), count: num(raw, \"count\") };\n}\n\nfunction parseMonitor(entry: unknown): ManifestMonitor {\n\tif (!isObj(entry)) invalid(\"a monitor entry must be an object\");\n\treturn {\n\t\tmonitorId: str(entry, \"monitorId\"),\n\t\tsessionId: str(entry, \"sessionId\"),\n\t\tdescription: str(entry, \"description\"),\n\t\truntimeKind: oneOf(RUNTIME_KINDS, entry, \"runtimeKind\"),\n\t\tdurabilityClass: oneOf(DURABILITY_CLASSES, entry, \"durabilityClass\"),\n\t\tcommand: opt(entry, \"command\", str),\n\t\tpath: opt(entry, \"path\", str),\n\t\tevent: opt(entry, \"event\", (raw, field) => oneOf(FILE_EVENTS, raw, field)),\n\t\tfilter: opt(entry, \"filter\", str),\n\t\tcwd: opt(entry, \"cwd\", str),\n\t\tapprovedParent: opt(entry, \"approvedParent\", str),\n\t\tcreatedAt: num(entry, \"createdAt\"),\n\t\texpiresAt: entry.expiresAt === undefined || entry.expiresAt === null ? null : num(entry, \"expiresAt\"),\n\t\tpersistent: bool(entry, \"persistent\"),\n\t\tsuspended: bool(entry, \"suspended\"),\n\t\tlastCheckpoint: entry.lastCheckpoint === null ? null : checkpoint(entry.lastCheckpoint),\n\t\tdeliveryPaused: bool(entry, \"deliveryPaused\"),\n\t\t// Unknown keys are ignored by this field-by-field parse, so a manifest written before\n\t\t// `wakeCount` was dropped still reads back cleanly; no version bump is needed.\n\t\tfireWindow: fireWindow(entry.fireWindow),\n\t};\n}\n\nfunction parseBackgroundSession(entry: unknown): ManifestBackgroundSession {\n\tif (!isObj(entry)) invalid(\"a background session entry must be an object\");\n\treturn { id: str(entry, \"id\"), command: str(entry, \"command\"), startedAtMs: num(entry, \"startedAtMs\") };\n}\n\n/** Strict fail-closed domain parse; the sidecar store has already checked version and session. */\nexport function parseTerminalManifest(raw: unknown, ref: SidecarStoreRef): TerminalManifest {\n\tif (!isObj(raw)) invalid(\"the payload must be an object\");\n\tif (!Array.isArray(raw.monitors)) invalid(\"field monitors must be an array\");\n\tif (!Array.isArray(raw.backgroundSessions)) invalid(\"field backgroundSessions must be an array\");\n\treturn {\n\t\tversion: TERMINAL_MANIFEST_VERSION,\n\t\tsessionId: ref.sessionId,\n\t\tmonitors: raw.monitors.map(parseMonitor),\n\t\tbackgroundSessions: raw.backgroundSessions.map(parseBackgroundSession),\n\t\tupdatedAt: num(raw, \"updatedAt\"),\n\t};\n}\n\nexport type RestoreOutcome = \"restored\" | \"lost\" | \"muted\" | \"attachedElsewhere\";\n\nexport interface RestoreHandlerResult {\n\treadonly outcome: RestoreOutcome;\n\t/** Why a durable monitor could not be restored; diagnostic only, never part of the digest counts. */\n\treadonly reason?: string;\n}\n\nexport type RestoreHandler = (monitor: ManifestMonitor) => RestoreHandlerResult | Promise<RestoreHandlerResult>;\n\nexport interface RestoreHandlers {\n\treadonly \"restartable-command\": RestoreHandler;\n\treadonly \"checkpointed-file\": RestoreHandler;\n}\n\n/** The registry surface a restore handler needs to re-apply a persisted mute. */\nexport interface PersistedMuteRegistry {\n\tpause(ids: readonly string[]): string[];\n}\n\n/**\n * Re-apply a persisted `deliveryPaused` mute to a freshly restored monitor and report the\n * outcome it contributes to the digest. The mute MUST be applied by the FRESH runtime id\n * (bash_N/watch_N) the restore just allocated: `MonitorRegistry.pause` resolves records by\n * runtime id only, so passing the persisted `mon_` id silently no-ops and the mute is lost.\n */\nexport function reapplyPersistedMute(\n\tregistry: PersistedMuteRegistry,\n\tmonitor: Pick<ManifestMonitor, \"deliveryPaused\">,\n\truntimeId: string,\n): RestoreOutcome {\n\tif (!monitor.deliveryPaused) return \"restored\";\n\tregistry.pause([runtimeId]);\n\treturn \"muted\";\n}\n\n/** Stub durability handlers: every durable monitor is reported lost until later phases land. */\nexport const stubRestoreHandlers: RestoreHandlers = {\n\t\"restartable-command\": () => ({ outcome: \"lost\" }),\n\t\"checkpointed-file\": () => ({ outcome: \"lost\" }),\n};\n\nexport interface RestoreDigest {\n\trestored: number;\n\tlost: number;\n\texpired: number;\n\tmuted: number;\n\tattachedElsewhere: number;\n\tstoreError: boolean;\n}\n\nexport interface RestoreTerminalStateOptions {\n\treadonly manifest: SidecarStore<TerminalManifest>;\n\treadonly handlers?: Partial<RestoreHandlers>;\n\treadonly now?: () => number;\n}\n\nexport async function restoreTerminalState(options: RestoreTerminalStateOptions): Promise<RestoreDigest> {\n\tconst digest: RestoreDigest = {\n\t\trestored: 0,\n\t\tlost: 0,\n\t\texpired: 0,\n\t\tmuted: 0,\n\t\tattachedElsewhere: 0,\n\t\tstoreError: false,\n\t};\n\tlet state: TerminalManifest | null;\n\ttry {\n\t\tstate = await options.manifest.read();\n\t} catch {\n\t\t// Fail closed: a corrupt or foreign manifest restores nothing and reports the store error.\n\t\treturn { ...digest, storeError: true };\n\t}\n\tif (state === null) return digest;\n\tconst now = (options.now ?? Date.now)();\n\tconst handlers: RestoreHandlers = { ...stubRestoreHandlers, ...options.handlers };\n\tfor (const monitor of state.monitors) {\n\t\t// At the deadline the entry is already expired: the handler must not see it at all.\n\t\tif (monitor.expiresAt !== null && monitor.expiresAt <= now) {\n\t\t\tdigest.expired += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (monitor.durabilityClass === \"ephemeral\") {\n\t\t\tdigest.lost += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst outcome = (await handlers[monitor.durabilityClass](monitor)).outcome;\n\t\tdigest[outcome] += 1;\n\t}\n\t// Background sessions carry no durable identity: every one of them is lost.\n\tdigest.lost += state.backgroundSessions.length;\n\treturn digest;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/restore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The restore orchestrator: `restoreTerminalState` replays the manifest after a restart. Every\n * monitor is classified up front (expired, ephemeral with or without time left, durable) and the\n * durable and still-live ephemeral ones are handed to their kind's handler, all concurrently.\n * The result carries per-monitor outcomes and reasons, background sessions told apart as still\n * running or exited, and an upper bound on how long the session was detached.\n */\n\nimport type { SidecarStore } from \"../../../session-sidecar-store.ts\";\nimport { confirmOwner } from \"./orphan-reaper.ts\";\nimport type { ChildProcessIdentity } from \"./process-identity.ts\";\nimport { sessionActivityBeforeMs } from \"./session-activity.ts\";\nimport type { ManifestBackgroundSession, ManifestMonitor, TerminalManifest } from \"./terminal-manifest.ts\";\n\nexport { InvalidTerminalManifestError, parseTerminalManifest } from \"./terminal-manifest-parse.ts\";\n\nexport type RestoreOutcome = \"restored\" | \"lost\" | \"muted\" | \"completed\" | \"attachedElsewhere\";\n\nexport interface RestoreHandlerResult {\n\treadonly outcome: RestoreOutcome;\n\t/** Why a monitor could not come back (or what its restore did), shown per monitor in the digest. */\n\treadonly reason?: string;\n\t/** The watcher a crash left running: killed before the respawn, or left alone when unverifiable. */\n\treadonly orphan?: { readonly pid: number; readonly action: \"killed\" | \"unverified\" };\n\t/** The re-spawned process, so the manifest can find it after the next crash. */\n\treadonly runtime?: ChildProcessIdentity;\n}\n\nexport interface RestoreContext {\n\t/** Upper bound on how long the session was detached (never an exact gap). */\n\treadonly downtimeMs: number;\n\t/** Time left for an ephemeral watch whose deadline has not passed; undefined for durable ones. */\n\treadonly remainingMs?: number;\n}\n\nexport type RestoreHandler = (\n\tmonitor: ManifestMonitor,\n\tcontext: RestoreContext,\n) => RestoreHandlerResult | Promise<RestoreHandlerResult>;\n\nexport interface RestoreHandlers {\n\treadonly \"restartable-command\": RestoreHandler;\n\treadonly \"checkpointed-file\": RestoreHandler;\n}\n\n/** The registry surface a restore handler needs to re-apply a persisted mute. */\nexport interface PersistedMuteRegistry {\n\tpause(ids: readonly string[]): string[];\n}\n\n/**\n * Re-apply a persisted `deliveryPaused` mute to a freshly restored monitor and report the\n * outcome it contributes to the digest. The mute MUST be applied by the FRESH runtime id\n * (bash_N/watch_N) the restore just allocated: `MonitorRegistry.pause` resolves records by\n * runtime id only, so passing the persisted `mon_` id silently no-ops and the mute is lost.\n */\nexport function reapplyPersistedMute(\n\tregistry: PersistedMuteRegistry,\n\tmonitor: Pick<ManifestMonitor, \"deliveryPaused\">,\n\truntimeId: string,\n): RestoreOutcome {\n\tif (!monitor.deliveryPaused) return \"restored\";\n\tregistry.pause([runtimeId]);\n\treturn \"muted\";\n}\n\n/** Stub handlers: every monitor is reported lost until a caller plugs the real ones in. */\nexport const stubRestoreHandlers: RestoreHandlers = {\n\t\"restartable-command\": () => ({ outcome: \"lost\", reason: \"no restore handler\" }),\n\t\"checkpointed-file\": () => ({ outcome: \"lost\", reason: \"no restore handler\" }),\n};\n\nexport type MonitorRestoreOutcome = RestoreOutcome | \"expired\";\n\nexport interface MonitorRestoreResult {\n\treadonly monitorId: string;\n\treadonly description: string;\n\treadonly kind: ManifestMonitor[\"runtimeKind\"];\n\treadonly outcome: MonitorRestoreOutcome;\n\treadonly command?: string;\n\treadonly path?: string;\n\treadonly reason?: string;\n\treadonly orphan?: RestoreHandlerResult[\"orphan\"];\n}\n\nexport interface BackgroundRestoreResult {\n\treadonly id: string;\n\treadonly command: string;\n\treadonly outcome: \"running\" | \"exited\";\n\treadonly pid?: number;\n}\n\nexport interface RestoreDigest {\n\trestored: number;\n\tlost: number;\n\texpired: number;\n\tmuted: number;\n\tcompleted: number;\n\tattachedElsewhere: number;\n\tstoreError: boolean;\n\treadonly results: readonly MonitorRestoreResult[];\n\treadonly backgroundSessions: readonly BackgroundRestoreResult[];\n\t/** Upper bound on how long the session was detached; 0 when nothing was persisted. */\n\treadonly downtimeMs: number;\n}\n\nexport interface RestoreTerminalStateOptions {\n\treadonly manifest: SidecarStore<TerminalManifest>;\n\treadonly handlers?: Partial<RestoreHandlers>;\n\treadonly now?: () => number;\n\t/** The session transcript; its last entry written before this process started bounds the downtime. */\n\treadonly sessionFile?: string;\n\treadonly processStartedAtMs?: number;\n\treadonly isBackgroundAlive?: (session: ManifestBackgroundSession) => Promise<boolean>;\n}\n\nfunction emptyDigest(): RestoreDigest {\n\treturn {\n\t\trestored: 0,\n\t\tlost: 0,\n\t\texpired: 0,\n\t\tmuted: 0,\n\t\tcompleted: 0,\n\t\tattachedElsewhere: 0,\n\t\tstoreError: false,\n\t\tresults: [],\n\t\tbackgroundSessions: [],\n\t\tdowntimeMs: 0,\n\t};\n}\n\nasync function defaultBackgroundAlive(session: ManifestBackgroundSession): Promise<boolean> {\n\tif (session.runtime === undefined) return false;\n\treturn (await confirmOwner(session.runtime, undefined)) === \"confirmed\";\n}\n\nfunction resultFor(monitor: ManifestMonitor, outcome: MonitorRestoreOutcome, handled?: RestoreHandlerResult) {\n\treturn {\n\t\tmonitorId: monitor.monitorId,\n\t\tdescription: monitor.description,\n\t\tkind: monitor.runtimeKind,\n\t\toutcome,\n\t\t...(monitor.command !== undefined ? { command: monitor.command } : {}),\n\t\t...(monitor.path !== undefined ? { path: monitor.path } : {}),\n\t\t...(handled?.reason !== undefined ? { reason: handled.reason } : {}),\n\t\t...(handled?.orphan !== undefined ? { orphan: handled.orphan } : {}),\n\t} satisfies MonitorRestoreResult;\n}\n\nexport async function restoreTerminalState(options: RestoreTerminalStateOptions): Promise<RestoreDigest> {\n\tlet state: TerminalManifest | null;\n\ttry {\n\t\tstate = await options.manifest.read();\n\t} catch {\n\t\t// Fail closed: a corrupt or foreign manifest restores nothing and reports the store error.\n\t\treturn { ...emptyDigest(), storeError: true };\n\t}\n\tif (state === null) return emptyDigest();\n\tconst now = (options.now ?? Date.now)();\n\tconst lastSeen = Math.max(\n\t\tstate.updatedAt,\n\t\t(await sessionActivityBeforeMs(options.sessionFile, options.processStartedAtMs ?? now)) ?? 0,\n\t);\n\tconst downtimeMs = Math.max(0, now - lastSeen);\n\tconst handlers: RestoreHandlers = { ...stubRestoreHandlers, ...options.handlers };\n\n\tconst results = await Promise.all(\n\t\tstate.monitors.map(async (monitor): Promise<MonitorRestoreResult> => {\n\t\t\t// At the deadline the entry is already expired: the handler must not see it at all.\n\t\t\tif (monitor.expiresAt !== null && monitor.expiresAt <= now) return resultFor(monitor, \"expired\");\n\t\t\tif (monitor.durabilityClass === \"ephemeral\") {\n\t\t\t\tif (monitor.deadlineMs === undefined || monitor.deadlineMs <= now) {\n\t\t\t\t\treturn resultFor(monitor, \"lost\", { outcome: \"lost\", reason: \"deadline passed while detached\" });\n\t\t\t\t}\n\t\t\t\tconst handler =\n\t\t\t\t\tmonitor.runtimeKind === \"file\" ? handlers[\"checkpointed-file\"] : handlers[\"restartable-command\"];\n\t\t\t\tconst handled = await handler(monitor, { downtimeMs, remainingMs: monitor.deadlineMs - now });\n\t\t\t\treturn resultFor(monitor, handled.outcome, handled);\n\t\t\t}\n\t\t\tconst handled = await handlers[monitor.durabilityClass](monitor, { downtimeMs });\n\t\t\treturn resultFor(monitor, handled.outcome, handled);\n\t\t}),\n\t);\n\n\tconst isAlive = options.isBackgroundAlive ?? defaultBackgroundAlive;\n\tconst backgroundSessions = await Promise.all(\n\t\tstate.backgroundSessions.map(async (session): Promise<BackgroundRestoreResult> => {\n\t\t\tif (session.runtime !== undefined && (await isAlive(session))) {\n\t\t\t\treturn { id: session.id, command: session.command, outcome: \"running\", pid: session.runtime.pid };\n\t\t\t}\n\t\t\treturn { id: session.id, command: session.command, outcome: \"exited\" };\n\t\t}),\n\t);\n\n\tconst digest = { ...emptyDigest(), results, backgroundSessions, downtimeMs };\n\tfor (const result of results) digest[result.outcome] += 1;\n\tdigest.lost += backgroundSessions.length;\n\treturn digest;\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TerminalScreenSnapshot, TerminalSession, TerminalSessionExit, TerminalSessionOptions } from "@earendil-works/pi-pty";
|
|
2
|
+
import { type ChildProcessIdentity } from "./process-identity.ts";
|
|
2
3
|
export interface TerminalRuntimeOptions extends TerminalSessionOptions {
|
|
3
4
|
readonly scrollback?: number;
|
|
4
5
|
}
|
|
@@ -14,6 +15,8 @@ export interface DeltaRead {
|
|
|
14
15
|
export declare class TerminalRuntimeSession {
|
|
15
16
|
readonly session: TerminalSession;
|
|
16
17
|
readonly command: string;
|
|
18
|
+
readonly startedAtMs: number;
|
|
19
|
+
private readonly argv;
|
|
17
20
|
private readonly screen;
|
|
18
21
|
private readonly decoder;
|
|
19
22
|
private buffer;
|
|
@@ -23,6 +26,8 @@ export declare class TerminalRuntimeSession {
|
|
|
23
26
|
private unsubscribeData;
|
|
24
27
|
constructor(command: string, options: TerminalRuntimeOptions);
|
|
25
28
|
get backend(): string | null;
|
|
29
|
+
/** The spawned child as a later process can recognise it; undefined when the backend exposes no pid. */
|
|
30
|
+
identity(): ChildProcessIdentity | undefined;
|
|
26
31
|
get exited(): boolean;
|
|
27
32
|
get exitResult(): TerminalSessionExit | null;
|
|
28
33
|
/** Total decoded chars produced so far (including any dropped from the front). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-session.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/runtime-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-session.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/runtime-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,oBAAoB,EAAmB,MAAM,uBAAuB,CAAC;AAMnF,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,qBAAa,sBAAsB;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8C;IACtE,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;IACtE,OAAO,CAAC,eAAe,CAA6B;IAEpD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,EAsB3D;IAED,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAE3B;IAED,wGAAwG;IACxG,QAAQ,IAAI,oBAAoB,GAAG,SAAS,CAW3C;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,UAAU,IAAI,mBAAmB,GAAG,IAAI,CAE3C;IAED,kFAAkF;IAClF,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,OAAO,CAAC,MAAM;IAad,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAGtD;IAED,8EAA8E;IAC9E,SAAS,IAAI,SAAS,CAMrB;IAED,iFAAiF;IACjF,UAAU,IAAI,MAAM,CAEnB;IAED,QAAQ,IAAI,sBAAsB,CAEjC;IAED,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE7C;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe,CAA8B;IAErD,OAAO,CAAC,cAAc;IAMtB,OAAO,IAAI,IAAI,CAKd;CACD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { processBootAtMs } from "./process-identity.js";
|
|
1
2
|
import { loadPty } from "./pty.lazy.js";
|
|
2
3
|
import { DEFAULT_SCROLLBACK, MAX_SESSION_OUTPUT_CHARS } from "./shared.js";
|
|
3
4
|
const pty = await loadPty();
|
|
@@ -25,6 +26,7 @@ export class TerminalRuntimeSession {
|
|
|
25
26
|
*/
|
|
26
27
|
this.lastScreenWrite = null;
|
|
27
28
|
this.command = command;
|
|
29
|
+
this.argv = [options.command ?? command, ...(options.args ?? [])];
|
|
28
30
|
this.screen = new pty.TerminalScreen({
|
|
29
31
|
cols: options.cols,
|
|
30
32
|
rows: options.rows,
|
|
@@ -45,10 +47,25 @@ export class TerminalRuntimeSession {
|
|
|
45
47
|
}
|
|
46
48
|
});
|
|
47
49
|
this.session.start();
|
|
50
|
+
this.startedAtMs = Date.now();
|
|
48
51
|
}
|
|
49
52
|
get backend() {
|
|
50
53
|
return this.session.backend;
|
|
51
54
|
}
|
|
55
|
+
/** The spawned child as a later process can recognise it; undefined when the backend exposes no pid. */
|
|
56
|
+
identity() {
|
|
57
|
+
const pid = this.session.pid;
|
|
58
|
+
if (pid === undefined)
|
|
59
|
+
return undefined;
|
|
60
|
+
const processGroupId = this.session.processGroupId;
|
|
61
|
+
return {
|
|
62
|
+
pid,
|
|
63
|
+
...(processGroupId === undefined ? {} : { processGroupId }),
|
|
64
|
+
startedAtMs: this.startedAtMs,
|
|
65
|
+
bootAtMs: processBootAtMs(),
|
|
66
|
+
argv: this.argv,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
52
69
|
get exited() {
|
|
53
70
|
return this.session.exited;
|
|
54
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-session.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/runtime-session.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE3E,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;AAW5B;;;;GAIG;AACH,MAAM,OAAO,sBAAsB;
|
|
1
|
+
{"version":3,"file":"runtime-session.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/runtime-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tTerminalScreen,\n\tTerminalScreenSnapshot,\n\tTerminalSession,\n\tTerminalSessionExit,\n\tTerminalSessionOptions,\n} from \"@earendil-works/pi-pty\";\nimport { type ChildProcessIdentity, processBootAtMs } from \"./process-identity.ts\";\nimport { loadPty } from \"./pty.lazy.ts\";\nimport { DEFAULT_SCROLLBACK, MAX_SESSION_OUTPUT_CHARS } from \"./shared.ts\";\n\nconst pty = await loadPty();\n\nexport interface TerminalRuntimeOptions extends TerminalSessionOptions {\n\treadonly scrollback?: number;\n}\n\nexport interface DeltaRead {\n\treadonly text: string;\n\treadonly droppedChars: number;\n}\n\n/**\n * A live terminal session: a pi-pty {@link TerminalSession} plus an xterm screen model\n * and a bounded decoded-output buffer with a per-consumer read cursor. Reads are pure\n * non-blocking peeks; watchers subscribe through `onOutput` (the monitor path).\n */\nexport class TerminalRuntimeSession {\n\treadonly session: TerminalSession;\n\treadonly command: string;\n\treadonly startedAtMs: number;\n\tprivate readonly argv: readonly string[];\n\tprivate readonly screen: TerminalScreen;\n\tprivate readonly decoder = new TextDecoder(\"utf-8\", { fatal: false });\n\tprivate buffer = \"\";\n\tprivate droppedChars = 0;\n\tprivate consumed = 0;\n\tprivate readonly outputListeners = new Set<(chunk: string) => void>();\n\tprivate unsubscribeData: (() => void) | null = null;\n\n\tconstructor(command: string, options: TerminalRuntimeOptions) {\n\t\tthis.command = command;\n\t\tthis.argv = [options.command ?? command, ...(options.args ?? [])];\n\t\tthis.screen = new pty.TerminalScreen({\n\t\t\tcols: options.cols,\n\t\t\trows: options.rows,\n\t\t\tscrollback: options.scrollback ?? DEFAULT_SCROLLBACK,\n\t\t});\n\t\tthis.session = new pty.TerminalSession(options);\n\t\tthis.unsubscribeData = this.session.onData((chunk) => {\n\t\t\tconst text = this.ingest(chunk);\n\t\t\tif (text.length === 0) return;\n\t\t\tfor (const listener of this.outputListeners) {\n\t\t\t\ttry {\n\t\t\t\t\tlistener(text);\n\t\t\t\t} catch {\n\t\t\t\t\t// Output observers must not interfere with the session's ingest path.\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.session.start();\n\t\tthis.startedAtMs = Date.now();\n\t}\n\n\tget backend(): string | null {\n\t\treturn this.session.backend;\n\t}\n\n\t/** The spawned child as a later process can recognise it; undefined when the backend exposes no pid. */\n\tidentity(): ChildProcessIdentity | undefined {\n\t\tconst pid = this.session.pid;\n\t\tif (pid === undefined) return undefined;\n\t\tconst processGroupId = this.session.processGroupId;\n\t\treturn {\n\t\t\tpid,\n\t\t\t...(processGroupId === undefined ? {} : { processGroupId }),\n\t\t\tstartedAtMs: this.startedAtMs,\n\t\t\tbootAtMs: processBootAtMs(),\n\t\t\targv: this.argv,\n\t\t};\n\t}\n\n\tget exited(): boolean {\n\t\treturn this.session.exited;\n\t}\n\n\tget exitResult(): TerminalSessionExit | null {\n\t\treturn this.session.exitResult;\n\t}\n\n\t/** Total decoded chars produced so far (including any dropped from the front). */\n\tget totalChars(): number {\n\t\treturn this.droppedChars + this.buffer.length;\n\t}\n\n\tprivate ingest(chunk: Uint8Array): string {\n\t\tconst text = this.decoder.decode(chunk, { stream: true });\n\t\tif (text.length === 0) return text;\n\t\tthis.ownScreenWrite(this.screen.feed(text));\n\t\tthis.buffer += text;\n\t\tif (this.buffer.length > MAX_SESSION_OUTPUT_CHARS) {\n\t\t\tconst overflow = this.buffer.length - MAX_SESSION_OUTPUT_CHARS;\n\t\t\tthis.buffer = this.buffer.slice(overflow);\n\t\t\tthis.droppedChars += overflow;\n\t\t}\n\t\treturn text;\n\t}\n\n\t/** Subscribe to decoded output without affecting the read cursor. */\n\tonOutput(listener: (chunk: string) => void): () => void {\n\t\tthis.outputListeners.add(listener);\n\t\treturn () => this.outputListeners.delete(listener);\n\t}\n\n\t/** Return output produced since the last read and advance the read cursor. */\n\treadDelta(): DeltaRead {\n\t\tconst start = Math.max(this.consumed, this.droppedChars);\n\t\tconst dropped = Math.max(0, this.droppedChars - this.consumed);\n\t\tconst text = this.buffer.slice(start - this.droppedChars);\n\t\tthis.consumed = this.totalChars;\n\t\treturn { text, droppedChars: dropped };\n\t}\n\n\t/** Full retained decoded output (`view:\"log\"`), without advancing the cursor. */\n\tfullOutput(): string {\n\t\treturn this.buffer;\n\t}\n\n\tsnapshot(): TerminalScreenSnapshot {\n\t\treturn this.screen.snapshot();\n\t}\n\n\tresizeScreen(cols: number, rows: number): void {\n\t\tthis.ownScreenWrite(this.screen.resize(cols, rows));\n\t}\n\n\t/**\n\t * The screen is a best-effort projection; a rejected feed/resize must never\n\t * become an unhandled rejection that kills the host process (#837), and\n\t * `buffer` remains the source of truth for reads. The screen hands every\n\t * caller coalesced into one queued operation the same memoized promise, so\n\t * attach the rejection handler once per distinct promise - re-attaching per\n\t * PTY chunk would grow promise reactions without bound while a replay is\n\t * stalled behind a slow parser.\n\t */\n\tprivate lastScreenWrite: Promise<void> | null = null;\n\n\tprivate ownScreenWrite(written: Promise<void>): void {\n\t\tif (written === this.lastScreenWrite) return;\n\t\tthis.lastScreenWrite = written;\n\t\twritten.catch(() => {});\n\t}\n\n\tdispose(): void {\n\t\tthis.unsubscribeData?.();\n\t\tthis.unsubscribeData = null;\n\t\tthis.outputListeners.clear();\n\t\tthis.screen.dispose();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The newest transcript entry timestamp strictly before `beforeMs`, read from the tail of the
|
|
3
|
+
* session JSONL. It is the tightest free "last alive" stamp a restore has: every turn appends,
|
|
4
|
+
* while the manifest only records lifecycle transitions. Entries written after `beforeMs` belong
|
|
5
|
+
* to the resuming process and are skipped. Undefined when the file is absent or has no stamp.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sessionActivityBeforeMs(sessionFile: string | undefined, beforeMs: number): Promise<number | undefined>;
|
|
8
|
+
//# sourceMappingURL=session-activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-activity.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/session-activity.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC5C,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAyB7B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { open } from "node:fs/promises";
|
|
2
|
+
const TAIL_BYTES = 256 * 1024;
|
|
3
|
+
/**
|
|
4
|
+
* The newest transcript entry timestamp strictly before `beforeMs`, read from the tail of the
|
|
5
|
+
* session JSONL. It is the tightest free "last alive" stamp a restore has: every turn appends,
|
|
6
|
+
* while the manifest only records lifecycle transitions. Entries written after `beforeMs` belong
|
|
7
|
+
* to the resuming process and are skipped. Undefined when the file is absent or has no stamp.
|
|
8
|
+
*/
|
|
9
|
+
export async function sessionActivityBeforeMs(sessionFile, beforeMs) {
|
|
10
|
+
if (sessionFile === undefined)
|
|
11
|
+
return undefined;
|
|
12
|
+
let text;
|
|
13
|
+
try {
|
|
14
|
+
const handle = await open(sessionFile, "r");
|
|
15
|
+
try {
|
|
16
|
+
const { size } = await handle.stat();
|
|
17
|
+
const length = Math.min(size, TAIL_BYTES);
|
|
18
|
+
const buffer = Buffer.alloc(length);
|
|
19
|
+
await handle.read(buffer, 0, length, size - length);
|
|
20
|
+
text = buffer.toString("utf8");
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
await handle.close();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
|
|
28
|
+
return undefined;
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
let newest;
|
|
32
|
+
for (const match of text.matchAll(/"timestamp":"([^"]+)"/g)) {
|
|
33
|
+
const stamp = Date.parse(match[1] ?? "");
|
|
34
|
+
if (!Number.isFinite(stamp) || stamp >= beforeMs)
|
|
35
|
+
continue;
|
|
36
|
+
if (newest === undefined || stamp > newest)
|
|
37
|
+
newest = stamp;
|
|
38
|
+
}
|
|
39
|
+
return newest;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=session-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-activity.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/session-activity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,MAAM,UAAU,GAAG,GAAG,GAAG,IAAI,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,WAA+B,EAC/B,QAAgB;IAEhB,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC;YACJ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACpC,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;YACpD,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;gBAAS,CAAC;YACV,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAChH,MAAM,KAAK,CAAC;IACb,CAAC;IACD,IAAI,MAA0B,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;QAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,QAAQ;YAAE,SAAS;QAC3D,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,GAAG,MAAM;YAAE,MAAM,GAAG,KAAK,CAAC;IAC5D,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst TAIL_BYTES = 256 * 1024;\n\n/**\n * The newest transcript entry timestamp strictly before `beforeMs`, read from the tail of the\n * session JSONL. It is the tightest free \"last alive\" stamp a restore has: every turn appends,\n * while the manifest only records lifecycle transitions. Entries written after `beforeMs` belong\n * to the resuming process and are skipped. Undefined when the file is absent or has no stamp.\n */\nexport async function sessionActivityBeforeMs(\n\tsessionFile: string | undefined,\n\tbeforeMs: number,\n): Promise<number | undefined> {\n\tif (sessionFile === undefined) return undefined;\n\tlet text: string;\n\ttry {\n\t\tconst handle = await open(sessionFile, \"r\");\n\t\ttry {\n\t\t\tconst { size } = await handle.stat();\n\t\t\tconst length = Math.min(size, TAIL_BYTES);\n\t\t\tconst buffer = Buffer.alloc(length);\n\t\t\tawait handle.read(buffer, 0, length, size - length);\n\t\t\ttext = buffer.toString(\"utf8\");\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n\tlet newest: number | undefined;\n\tfor (const match of text.matchAll(/\"timestamp\":\"([^\"]+)\"/g)) {\n\t\tconst stamp = Date.parse(match[1] ?? \"\");\n\t\tif (!Number.isFinite(stamp) || stamp >= beforeMs) continue;\n\t\tif (newest === undefined || stamp > newest) newest = stamp;\n\t}\n\treturn newest;\n}\n"]}
|
|
@@ -17,6 +17,20 @@ export declare const DEFAULT_MAX_SESSIONS = 32;
|
|
|
17
17
|
export declare const MAX_SESSION_OUTPUT_CHARS = 1000000;
|
|
18
18
|
/** Grace window used to capture a background command's early output before returning its id. */
|
|
19
19
|
export declare const BACKGROUND_START_GRACE_MS = 250;
|
|
20
|
+
/**
|
|
21
|
+
* Restore-context environment handed to monitor commands. `ID` and `STATE_DIR` are set on every
|
|
22
|
+
* spawn of a persistent watch (ID alone for ephemeral ones); `RESTORED` and `DOWNTIME_MS` only on
|
|
23
|
+
* the re-spawn after a restart, where the downtime is an upper bound, never an exact gap.
|
|
24
|
+
*/
|
|
25
|
+
export declare const MONITOR_ENV_ID = "SENPI_MONITOR_ID";
|
|
26
|
+
export declare const MONITOR_ENV_STATE_DIR = "SENPI_MONITOR_STATE_DIR";
|
|
27
|
+
export declare const MONITOR_ENV_RESTORED = "SENPI_MONITOR_RESTORED";
|
|
28
|
+
export declare const MONITOR_ENV_DOWNTIME_MS = "SENPI_MONITOR_DOWNTIME_MS";
|
|
29
|
+
/**
|
|
30
|
+
* How long a re-spawned watch must stay up after a restore before it counts as restored. Long
|
|
31
|
+
* enough for `git`/`gh` cold starts to fail visibly, short enough that the digest is not held.
|
|
32
|
+
*/
|
|
33
|
+
export declare const RESTORE_GRACE_MS = 2000;
|
|
20
34
|
/**
|
|
21
35
|
* After a foreground session has been killed (abort or timeout), how long to keep
|
|
22
36
|
* waiting for its exit to settle before releasing the tool anyway. A surviving
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAClD,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAChD,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAClD,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,+EAM3B,CAAC;AAEX,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,2FAA2F;AAC3F,eAAO,MAAM,wBAAwB,UAAY,CAAC;AAClD,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAClD,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAChD,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAClD,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,+EAM3B,CAAC;AAEX,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,2FAA2F;AAC3F,eAAO,MAAM,wBAAwB,UAAY,CAAC;AAClD,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,cAAc,qBAAqB,CAAC;AACjD,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAC/D,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAC7D,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AACnE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAA0B,CAAC;AACjE;;;;;GAKG;AACH,eAAO,MAAM,mCAAmC,MAAM,CAAC;AACvD,+FAA+F;AAC/F,eAAO,MAAM,qBAAqB,QAAsB,CAAC;AACzD;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,+DAAkG,CAAC;AAE7I;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASrE,CAAC;AAiCF,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ7D;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CASvF;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMzD"}
|
|
@@ -23,6 +23,20 @@ export const DEFAULT_MAX_SESSIONS = 32;
|
|
|
23
23
|
export const MAX_SESSION_OUTPUT_CHARS = 1_000_000;
|
|
24
24
|
/** Grace window used to capture a background command's early output before returning its id. */
|
|
25
25
|
export const BACKGROUND_START_GRACE_MS = 250;
|
|
26
|
+
/**
|
|
27
|
+
* Restore-context environment handed to monitor commands. `ID` and `STATE_DIR` are set on every
|
|
28
|
+
* spawn of a persistent watch (ID alone for ephemeral ones); `RESTORED` and `DOWNTIME_MS` only on
|
|
29
|
+
* the re-spawn after a restart, where the downtime is an upper bound, never an exact gap.
|
|
30
|
+
*/
|
|
31
|
+
export const MONITOR_ENV_ID = "SENPI_MONITOR_ID";
|
|
32
|
+
export const MONITOR_ENV_STATE_DIR = "SENPI_MONITOR_STATE_DIR";
|
|
33
|
+
export const MONITOR_ENV_RESTORED = "SENPI_MONITOR_RESTORED";
|
|
34
|
+
export const MONITOR_ENV_DOWNTIME_MS = "SENPI_MONITOR_DOWNTIME_MS";
|
|
35
|
+
/**
|
|
36
|
+
* How long a re-spawned watch must stay up after a restore before it counts as restored. Long
|
|
37
|
+
* enough for `git`/`gh` cold starts to fail visibly, short enough that the digest is not held.
|
|
38
|
+
*/
|
|
39
|
+
export const RESTORE_GRACE_MS = 2_000;
|
|
26
40
|
/**
|
|
27
41
|
* After a foreground session has been killed (abort or timeout), how long to keep
|
|
28
42
|
* waiting for its exit to settle before releasing the tool anyway. A surviving
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/shared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared constants and helpers for the persistent-terminal builtin extension.\n */\n\nexport const TERMINAL_BASH_TOOL = \"bash\";\nexport const TERMINAL_OUTPUT_TOOL = \"bash_output\";\nexport const TERMINAL_KILL_TOOL = \"kill_bash\";\nexport const TERMINAL_INPUT_TOOL = \"bash_input\";\nexport const TERMINAL_RESIZE_TOOL = \"bash_resize\";\nexport const TERMINAL_MONITOR_TOOL = \"monitor\";\n\n/** Companion tools that must never dangle without a live PTY `bash`. */\nexport const TERMINAL_COMPANION_TOOLS = [\n\tTERMINAL_OUTPUT_TOOL,\n\tTERMINAL_KILL_TOOL,\n\tTERMINAL_INPUT_TOOL,\n\tTERMINAL_RESIZE_TOOL,\n\tTERMINAL_MONITOR_TOOL,\n] as const;\n\nexport const DEFAULT_COLS = 120;\nexport const DEFAULT_ROWS = 40;\nexport const DEFAULT_SCROLLBACK = 10_000;\nexport const DEFAULT_MAX_SESSIONS = 32;\n/** Upper bound on decoded output retained per session for delta reads and `view:\"log\"`. */\nexport const MAX_SESSION_OUTPUT_CHARS = 1_000_000;\n/** Grace window used to capture a background command's early output before returning its id. */\nexport const BACKGROUND_START_GRACE_MS = 250;\n/**\n * After a foreground session has been killed (abort or timeout), how long to keep\n * waiting for its exit to settle before releasing the tool anyway. A surviving\n * descendant that holds the PTY open (or a kill that never lands) must not keep\n * the agent blocked forever.\n */\nexport const KILLED_SESSION_EXIT_GRACE_MS = 5000;\n\n/**\n * Admission cap on durable (restart-surviving) monitors per session. Ephemeral monitors\n * never count against it: only entries the manifest keeps across a restart do.\n */\nexport const MAX_DURABLE_MONITORS = 5;\n/**\n * Absolute lifetime of a durable monitor, measured from its registration. It is a deadline,\n * never a sliding window: neither a restore nor a rearm extends it.\n */\nexport const DURABLE_MONITOR_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;\n/**\n * Per-monitor rolling fire budget: one durable command watch may deliver at most this many\n * matched line events inside any FIRE_BUDGET_WINDOW_MS window before the registry auto-mutes\n * it. The session-global wake budget only counts consecutive line-only wakes on an idle\n * session, so a busy session needs this per-monitor backstop against a runaway watch.\n */\nexport const DEFAULT_DURABLE_MONITOR_FIRE_BUDGET = 200;\n/** Width of the rolling fire-budget window: the budget replenishes after this span elapses. */\nexport const FIRE_BUDGET_WINDOW_MS = 24 * 60 * 60 * 1000;\n/**\n * The ONE summary emitted when the fire budget mutes a watch. Machine-consumed: the monitor\n * notifier exempts exactly this summary from its completion handling, so the mute must not\n * clear the session-global wake streak.\n */\nexport const FIRE_BUDGET_AUTO_MUTE_SUMMARY = `auto-muted: fire budget (${DEFAULT_DURABLE_MONITOR_FIRE_BUDGET}/24h) reached; rearm to resume`;\n\n/**\n * Non-interactive environment for foreground one-shot commands (codex-style):\n * cooperative tools (`gh`, `git`, pagers, color libs) skip spinners/colors at\n * the source instead of flooding the captured stream with redraw frames, and\n * `git` never blocks the captured foreground PTY on interactive input.\n * - `GIT_EDITOR: \"true\"`: git spawns `/usr/bin/true` as the editor, which exits 0\n * immediately (git treats a zero-exit editor as accepted), so a `git commit`\n * without `-m` aborts with \"Aborting commit due to empty commit message\" and a\n * `git rebase -i` accepts the todo list instead of parking the tool inside\n * nvim on COMMIT_EDITMSG until the timeout kills it.\n * - `GIT_TERMINAL_PROMPT: \"0\"`: git fails fast (\"could not read Username\",\n * exit 128) instead of prompting for credentials on the captured PTY where\n * nobody can type (same opt-out `package-manager.ts` already uses for its own\n * git calls).\n * Background sessions keep the user's real TERM and git settings for\n * interactive apps.\n */\nexport const FOREGROUND_ENV_OVERRIDES: Readonly<Record<string, string>> = {\n\tNO_COLOR: \"1\",\n\tTERM: \"dumb\",\n\tCOLORTERM: \"\",\n\tPAGER: \"cat\",\n\tGIT_PAGER: \"cat\",\n\tGH_PAGER: \"cat\",\n\tGIT_EDITOR: \"true\",\n\tGIT_TERMINAL_PROMPT: \"0\",\n};\n\n/**\n * Named-key aliases the model can send via `bash_input {keys:[...]}`. Values are the\n * raw byte sequences a PTY expects (control chars + xterm cursor/function escapes).\n */\nconst KEY_SEQUENCES: Record<string, string> = {\n\tenter: \"\\r\",\n\treturn: \"\\r\",\n\ttab: \"\\t\",\n\tescape: \"\\x1b\",\n\tesc: \"\\x1b\",\n\tspace: \" \",\n\tbackspace: \"\\x7f\",\n\tdelete: \"\\x1b[3~\",\n\tup: \"\\x1b[A\",\n\tdown: \"\\x1b[B\",\n\tright: \"\\x1b[C\",\n\tleft: \"\\x1b[D\",\n\thome: \"\\x1b[H\",\n\tend: \"\\x1b[F\",\n\tpageup: \"\\x1b[5~\",\n\tpagedown: \"\\x1b[6~\",\n\t\"ctrl+c\": \"\\x03\",\n\t\"ctrl+d\": \"\\x04\",\n\t\"ctrl+z\": \"\\x1a\",\n\t\"ctrl+l\": \"\\x0c\",\n\t\"ctrl+u\": \"\\x15\",\n\t\"ctrl+a\": \"\\x01\",\n\t\"ctrl+e\": \"\\x05\",\n\t\"ctrl+\\\\\": \"\\x1c\",\n};\n\n/** Resolve a single named key to its PTY byte sequence, or null when unknown. */\nexport function resolveKeySequence(key: string): string | null {\n\tconst normalized = key.trim().toLowerCase();\n\tif (normalized.length === 0) return null;\n\tconst mapped = KEY_SEQUENCES[normalized];\n\tif (mapped !== undefined) return mapped;\n\t// Single printable character passes through verbatim.\n\tif ([...normalized].length === 1) return key;\n\treturn null;\n}\n\n/** Build a PTY-ready payload from a list of named keys, returning the unknown keys too. */\nexport function encodeKeys(keys: readonly string[]): { data: string; unknown: string[] } {\n\tlet data = \"\";\n\tconst unknown: string[] = [];\n\tfor (const key of keys) {\n\t\tconst sequence = resolveKeySequence(key);\n\t\tif (sequence === null) unknown.push(key);\n\t\telse data += sequence;\n\t}\n\treturn { data, unknown };\n}\n\n/** Compile a user-supplied regex, tolerating invalid patterns by returning null. */\nexport function safeRegExp(pattern: string): RegExp | null {\n\ttry {\n\t\treturn new RegExp(pattern);\n\t} catch {\n\t\treturn null;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/shared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared constants and helpers for the persistent-terminal builtin extension.\n */\n\nexport const TERMINAL_BASH_TOOL = \"bash\";\nexport const TERMINAL_OUTPUT_TOOL = \"bash_output\";\nexport const TERMINAL_KILL_TOOL = \"kill_bash\";\nexport const TERMINAL_INPUT_TOOL = \"bash_input\";\nexport const TERMINAL_RESIZE_TOOL = \"bash_resize\";\nexport const TERMINAL_MONITOR_TOOL = \"monitor\";\n\n/** Companion tools that must never dangle without a live PTY `bash`. */\nexport const TERMINAL_COMPANION_TOOLS = [\n\tTERMINAL_OUTPUT_TOOL,\n\tTERMINAL_KILL_TOOL,\n\tTERMINAL_INPUT_TOOL,\n\tTERMINAL_RESIZE_TOOL,\n\tTERMINAL_MONITOR_TOOL,\n] as const;\n\nexport const DEFAULT_COLS = 120;\nexport const DEFAULT_ROWS = 40;\nexport const DEFAULT_SCROLLBACK = 10_000;\nexport const DEFAULT_MAX_SESSIONS = 32;\n/** Upper bound on decoded output retained per session for delta reads and `view:\"log\"`. */\nexport const MAX_SESSION_OUTPUT_CHARS = 1_000_000;\n/** Grace window used to capture a background command's early output before returning its id. */\nexport const BACKGROUND_START_GRACE_MS = 250;\n\n/**\n * Restore-context environment handed to monitor commands. `ID` and `STATE_DIR` are set on every\n * spawn of a persistent watch (ID alone for ephemeral ones); `RESTORED` and `DOWNTIME_MS` only on\n * the re-spawn after a restart, where the downtime is an upper bound, never an exact gap.\n */\nexport const MONITOR_ENV_ID = \"SENPI_MONITOR_ID\";\nexport const MONITOR_ENV_STATE_DIR = \"SENPI_MONITOR_STATE_DIR\";\nexport const MONITOR_ENV_RESTORED = \"SENPI_MONITOR_RESTORED\";\nexport const MONITOR_ENV_DOWNTIME_MS = \"SENPI_MONITOR_DOWNTIME_MS\";\n/**\n * How long a re-spawned watch must stay up after a restore before it counts as restored. Long\n * enough for `git`/`gh` cold starts to fail visibly, short enough that the digest is not held.\n */\nexport const RESTORE_GRACE_MS = 2_000;\n/**\n * After a foreground session has been killed (abort or timeout), how long to keep\n * waiting for its exit to settle before releasing the tool anyway. A surviving\n * descendant that holds the PTY open (or a kill that never lands) must not keep\n * the agent blocked forever.\n */\nexport const KILLED_SESSION_EXIT_GRACE_MS = 5000;\n\n/**\n * Admission cap on durable (restart-surviving) monitors per session. Ephemeral monitors\n * never count against it: only entries the manifest keeps across a restart do.\n */\nexport const MAX_DURABLE_MONITORS = 5;\n/**\n * Absolute lifetime of a durable monitor, measured from its registration. It is a deadline,\n * never a sliding window: neither a restore nor a rearm extends it.\n */\nexport const DURABLE_MONITOR_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;\n/**\n * Per-monitor rolling fire budget: one durable command watch may deliver at most this many\n * matched line events inside any FIRE_BUDGET_WINDOW_MS window before the registry auto-mutes\n * it. The session-global wake budget only counts consecutive line-only wakes on an idle\n * session, so a busy session needs this per-monitor backstop against a runaway watch.\n */\nexport const DEFAULT_DURABLE_MONITOR_FIRE_BUDGET = 200;\n/** Width of the rolling fire-budget window: the budget replenishes after this span elapses. */\nexport const FIRE_BUDGET_WINDOW_MS = 24 * 60 * 60 * 1000;\n/**\n * The ONE summary emitted when the fire budget mutes a watch. Machine-consumed: the monitor\n * notifier exempts exactly this summary from its completion handling, so the mute must not\n * clear the session-global wake streak.\n */\nexport const FIRE_BUDGET_AUTO_MUTE_SUMMARY = `auto-muted: fire budget (${DEFAULT_DURABLE_MONITOR_FIRE_BUDGET}/24h) reached; rearm to resume`;\n\n/**\n * Non-interactive environment for foreground one-shot commands (codex-style):\n * cooperative tools (`gh`, `git`, pagers, color libs) skip spinners/colors at\n * the source instead of flooding the captured stream with redraw frames, and\n * `git` never blocks the captured foreground PTY on interactive input.\n * - `GIT_EDITOR: \"true\"`: git spawns `/usr/bin/true` as the editor, which exits 0\n * immediately (git treats a zero-exit editor as accepted), so a `git commit`\n * without `-m` aborts with \"Aborting commit due to empty commit message\" and a\n * `git rebase -i` accepts the todo list instead of parking the tool inside\n * nvim on COMMIT_EDITMSG until the timeout kills it.\n * - `GIT_TERMINAL_PROMPT: \"0\"`: git fails fast (\"could not read Username\",\n * exit 128) instead of prompting for credentials on the captured PTY where\n * nobody can type (same opt-out `package-manager.ts` already uses for its own\n * git calls).\n * Background sessions keep the user's real TERM and git settings for\n * interactive apps.\n */\nexport const FOREGROUND_ENV_OVERRIDES: Readonly<Record<string, string>> = {\n\tNO_COLOR: \"1\",\n\tTERM: \"dumb\",\n\tCOLORTERM: \"\",\n\tPAGER: \"cat\",\n\tGIT_PAGER: \"cat\",\n\tGH_PAGER: \"cat\",\n\tGIT_EDITOR: \"true\",\n\tGIT_TERMINAL_PROMPT: \"0\",\n};\n\n/**\n * Named-key aliases the model can send via `bash_input {keys:[...]}`. Values are the\n * raw byte sequences a PTY expects (control chars + xterm cursor/function escapes).\n */\nconst KEY_SEQUENCES: Record<string, string> = {\n\tenter: \"\\r\",\n\treturn: \"\\r\",\n\ttab: \"\\t\",\n\tescape: \"\\x1b\",\n\tesc: \"\\x1b\",\n\tspace: \" \",\n\tbackspace: \"\\x7f\",\n\tdelete: \"\\x1b[3~\",\n\tup: \"\\x1b[A\",\n\tdown: \"\\x1b[B\",\n\tright: \"\\x1b[C\",\n\tleft: \"\\x1b[D\",\n\thome: \"\\x1b[H\",\n\tend: \"\\x1b[F\",\n\tpageup: \"\\x1b[5~\",\n\tpagedown: \"\\x1b[6~\",\n\t\"ctrl+c\": \"\\x03\",\n\t\"ctrl+d\": \"\\x04\",\n\t\"ctrl+z\": \"\\x1a\",\n\t\"ctrl+l\": \"\\x0c\",\n\t\"ctrl+u\": \"\\x15\",\n\t\"ctrl+a\": \"\\x01\",\n\t\"ctrl+e\": \"\\x05\",\n\t\"ctrl+\\\\\": \"\\x1c\",\n};\n\n/** Resolve a single named key to its PTY byte sequence, or null when unknown. */\nexport function resolveKeySequence(key: string): string | null {\n\tconst normalized = key.trim().toLowerCase();\n\tif (normalized.length === 0) return null;\n\tconst mapped = KEY_SEQUENCES[normalized];\n\tif (mapped !== undefined) return mapped;\n\t// Single printable character passes through verbatim.\n\tif ([...normalized].length === 1) return key;\n\treturn null;\n}\n\n/** Build a PTY-ready payload from a list of named keys, returning the unknown keys too. */\nexport function encodeKeys(keys: readonly string[]): { data: string; unknown: string[] } {\n\tlet data = \"\";\n\tconst unknown: string[] = [];\n\tfor (const key of keys) {\n\t\tconst sequence = resolveKeySequence(key);\n\t\tif (sequence === null) unknown.push(key);\n\t\telse data += sequence;\n\t}\n\treturn { data, unknown };\n}\n\n/** Compile a user-supplied regex, tolerating invalid patterns by returning null. */\nexport function safeRegExp(pattern: string): RegExp | null {\n\ttry {\n\t\treturn new RegExp(pattern);\n\t} catch {\n\t\treturn null;\n\t}\n}\n"]}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* constants that govern them live here; all runtime behaviour is in `terminal-manifest.ts`.
|
|
5
5
|
*/
|
|
6
6
|
import type { SessionManager } from "../../../session-manager.ts";
|
|
7
|
+
import type { ChildProcessIdentity } from "./process-identity.ts";
|
|
7
8
|
export declare const TERMINAL_MANIFEST_VERSION = 1;
|
|
8
9
|
/** Minimum debounce for checkpoint writes; a burst inside this window collapses to one write. */
|
|
9
10
|
export declare const TERMINAL_MANIFEST_CHECKPOINT_DEBOUNCE_MS = 30000;
|
|
@@ -50,11 +51,16 @@ export interface ManifestMonitor {
|
|
|
50
51
|
startMs: number;
|
|
51
52
|
count: number;
|
|
52
53
|
};
|
|
54
|
+
/** The child process a restore must recognise (and stop) before re-spawning; absent on v1 entries. */
|
|
55
|
+
readonly runtime?: ChildProcessIdentity;
|
|
56
|
+
/** Absolute runtime deadline of an ephemeral watch, so a restore can hand back the time left. */
|
|
57
|
+
readonly deadlineMs?: number;
|
|
53
58
|
}
|
|
54
59
|
export interface ManifestBackgroundSession {
|
|
55
60
|
readonly id: string;
|
|
56
61
|
readonly command: string;
|
|
57
62
|
readonly startedAtMs: number;
|
|
63
|
+
readonly runtime?: ChildProcessIdentity;
|
|
58
64
|
}
|
|
59
65
|
export interface TerminalManifest {
|
|
60
66
|
readonly version: typeof TERMINAL_MANIFEST_VERSION;
|
|
@@ -87,5 +93,7 @@ export type MonitorSpec = CommandMonitorSpec | FileMonitorSpec;
|
|
|
87
93
|
export interface MonitorRegistration {
|
|
88
94
|
readonly monitorId: string;
|
|
89
95
|
readonly spec: MonitorSpec;
|
|
96
|
+
readonly runtime?: ChildProcessIdentity;
|
|
97
|
+
readonly deadlineMs?: number;
|
|
90
98
|
}
|
|
91
99
|
//# sourceMappingURL=terminal-manifest-model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-manifest-model.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/terminal-manifest-model.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,iGAAiG;AACjG,eAAO,MAAM,wCAAwC,QAAS,CAAC;AAC/D;;;GAGG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,cAAc,EAAE,eAAe,GAAG,cAAc,CAAC,CAAC;AAC7F,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,CAAC;AACpD,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,qBAAqB,GAAG,mBAAmB,CAAC;AAC/F;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,sBAAsB,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"terminal-manifest-model.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/terminal-manifest-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-manifest-model.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/terminal-manifest-model.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"terminal-manifest-model.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/terminal-manifest-model.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,iGAAiG;AACjG,MAAM,CAAC,MAAM,wCAAwC,GAAG,MAAM,CAAC;AAC/D;;;GAGG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["/**\n * The terminal manifest's data model: the durable per-session record of live monitors and\n * background sessions that `restore.ts` strict-parses back after a restart. Types and the\n * constants that govern them live here; all runtime behaviour is in `terminal-manifest.ts`.\n */\n\nimport type { SessionManager } from \"../../../session-manager.ts\";\nimport type { ChildProcessIdentity } from \"./process-identity.ts\";\n\nexport const TERMINAL_MANIFEST_VERSION = 1;\n/** Minimum debounce for checkpoint writes; a burst inside this window collapses to one write. */\nexport const TERMINAL_MANIFEST_CHECKPOINT_DEBOUNCE_MS = 30_000;\n/**\n * Re-exported so manifest consumers read one durability deadline. The value lives in\n * `shared.ts` with the rest of the terminal caps; there is exactly one definition of it.\n */\nexport { DURABLE_MONITOR_EXPIRY_MS } from \"./shared.ts\";\nexport type TerminalManifestSession = Pick<SessionManager, \"getSessionDir\" | \"getSessionId\">;\nexport type MonitorRuntimeKind = \"command\" | \"file\";\nexport type MonitorDurabilityClass = \"ephemeral\" | \"restartable-command\" | \"checkpointed-file\";\n/**\n * File-monitor checkpoint persisted by the writer: the registry's live identity tuple. `digest`\n * is required — without it a same-size, same-mtime rewrite is undetectable across a restart.\n */\nexport interface TerminalManifestCheckpoint {\n\treadonly dev: number;\n\treadonly ino: number;\n\treadonly size: number;\n\treadonly mtimeMs: number;\n\treadonly digest: string;\n\treadonly present: boolean;\n}\n\nexport interface ManifestMonitor {\n\treadonly monitorId: string;\n\t/** Owning agent session id — never the bash_N/watch_N runtime id. */\n\treadonly sessionId: string;\n\treadonly description: string;\n\treadonly runtimeKind: MonitorRuntimeKind;\n\treadonly durabilityClass: MonitorDurabilityClass;\n\treadonly command?: string;\n\treadonly path?: string;\n\treadonly event?: \"create\" | \"modify\";\n\treadonly filter?: string;\n\treadonly cwd?: string;\n\treadonly approvedParent?: string;\n\treadonly createdAt: number;\n\treadonly expiresAt: number | null;\n\treadonly persistent: boolean;\n\treadonly suspended: boolean;\n\treadonly lastCheckpoint: TerminalManifestCheckpoint | null;\n\treadonly deliveryPaused: boolean;\n\treadonly fireWindow: { startMs: number; count: number };\n\t/** The child process a restore must recognise (and stop) before re-spawning; absent on v1 entries. */\n\treadonly runtime?: ChildProcessIdentity;\n\t/** Absolute runtime deadline of an ephemeral watch, so a restore can hand back the time left. */\n\treadonly deadlineMs?: number;\n}\n\nexport interface ManifestBackgroundSession {\n\treadonly id: string;\n\treadonly command: string;\n\treadonly startedAtMs: number;\n\treadonly runtime?: ChildProcessIdentity;\n}\n\nexport interface TerminalManifest {\n\treadonly version: typeof TERMINAL_MANIFEST_VERSION;\n\treadonly sessionId: string;\n\treadonly monitors: readonly ManifestMonitor[];\n\treadonly backgroundSessions: readonly ManifestBackgroundSession[];\n\treadonly updatedAt: number;\n}\n\n/** What the monitor tool captures at its call site — the only place the branch inputs live. */\nexport interface CommandMonitorSpec {\n\treadonly kind: \"command\";\n\treadonly description: string;\n\treadonly command: string;\n\treadonly filter?: string;\n\treadonly cwd?: string;\n\treadonly persistent: boolean;\n}\n\nexport interface FileMonitorSpec {\n\treadonly kind: \"file\";\n\treadonly description: string;\n\treadonly path: string;\n\treadonly event: \"create\" | \"modify\";\n\treadonly timeoutMs: number;\n\treadonly cwd: string;\n\treadonly approvedParent?: string;\n\t/** Persistent file watches are the durable `checkpointed-file` class; one-shot ones stay ephemeral. */\n\treadonly persistent: boolean;\n}\nexport type MonitorSpec = CommandMonitorSpec | FileMonitorSpec;\n\nexport interface MonitorRegistration {\n\treadonly monitorId: string;\n\treadonly spec: MonitorSpec;\n\treadonly runtime?: ChildProcessIdentity;\n\treadonly deadlineMs?: number;\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal manifest's strict fail-closed parse: every field is checked, unknown keys are
|
|
3
|
+
* ignored (so optional additions never need a version bump), and any malformed value throws
|
|
4
|
+
* InvalidTerminalManifestError so a restore fails closed instead of guessing.
|
|
5
|
+
*/
|
|
6
|
+
import { InvalidSidecarStoreError, type SidecarStoreRef } from "../../../session-sidecar-store.ts";
|
|
7
|
+
import { type TerminalManifest } from "./terminal-manifest-model.ts";
|
|
8
|
+
export declare class InvalidTerminalManifestError extends InvalidSidecarStoreError {
|
|
9
|
+
constructor(message: string, options?: {
|
|
10
|
+
cause?: unknown;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** Strict fail-closed domain parse; the sidecar store has already checked version and session. */
|
|
14
|
+
export declare function parseTerminalManifest(raw: unknown, ref: SidecarStoreRef): TerminalManifest;
|
|
15
|
+
//# sourceMappingURL=terminal-manifest-parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-manifest-parse.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/terminal-manifest-parse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,wBAAwB,EAAE,KAAK,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEnG,OAAO,EAIN,KAAK,gBAAgB,EAErB,MAAM,8BAA8B,CAAC;AAEtC,qBAAa,4BAA6B,SAAQ,wBAAwB;IACzE,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACD;AA2HD,kGAAkG;AAClG,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,GAAG,gBAAgB,CAW1F"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal manifest's strict fail-closed parse: every field is checked, unknown keys are
|
|
3
|
+
* ignored (so optional additions never need a version bump), and any malformed value throws
|
|
4
|
+
* InvalidTerminalManifestError so a restore fails closed instead of guessing.
|
|
5
|
+
*/
|
|
6
|
+
import { InvalidSidecarStoreError } from "../../../session-sidecar-store.js";
|
|
7
|
+
import { TERMINAL_MANIFEST_VERSION, } from "./terminal-manifest-model.js";
|
|
8
|
+
export class InvalidTerminalManifestError extends InvalidSidecarStoreError {
|
|
9
|
+
constructor(message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.name = "InvalidTerminalManifestError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const RUNTIME_KINDS = ["command", "file"];
|
|
15
|
+
const DURABILITY_CLASSES = ["ephemeral", "restartable-command", "checkpointed-file"];
|
|
16
|
+
const FILE_EVENTS = ["create", "modify"];
|
|
17
|
+
const isStr = (value) => typeof value === "string" && value.length > 0;
|
|
18
|
+
const isNum = (value) => typeof value === "number" && Number.isFinite(value);
|
|
19
|
+
const isBool = (value) => typeof value === "boolean";
|
|
20
|
+
const isObj = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21
|
+
function invalid(message) {
|
|
22
|
+
throw new InvalidTerminalManifestError(`terminal manifest is invalid: ${message}`);
|
|
23
|
+
}
|
|
24
|
+
function str(raw, field) {
|
|
25
|
+
if (!isStr(raw[field]))
|
|
26
|
+
invalid(`field ${field} must be a non-empty string`);
|
|
27
|
+
return raw[field];
|
|
28
|
+
}
|
|
29
|
+
function num(raw, field) {
|
|
30
|
+
if (!isNum(raw[field]))
|
|
31
|
+
invalid(`field ${field} must be a finite number`);
|
|
32
|
+
return raw[field];
|
|
33
|
+
}
|
|
34
|
+
function digest(raw, field) {
|
|
35
|
+
if (typeof raw[field] !== "string")
|
|
36
|
+
invalid(`field ${field} must be a string`);
|
|
37
|
+
return raw[field];
|
|
38
|
+
}
|
|
39
|
+
function bool(raw, field) {
|
|
40
|
+
if (!isBool(raw[field]))
|
|
41
|
+
invalid(`field ${field} must be a boolean`);
|
|
42
|
+
return raw[field];
|
|
43
|
+
}
|
|
44
|
+
function opt(raw, field, required) {
|
|
45
|
+
return raw[field] === undefined ? undefined : required(raw, field);
|
|
46
|
+
}
|
|
47
|
+
function oneOf(values, raw, field) {
|
|
48
|
+
const value = raw[field];
|
|
49
|
+
if (typeof value !== "string" || !values.includes(value))
|
|
50
|
+
invalid(`field ${field} must be one of: ${values.join(", ")}`);
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function checkpoint(raw) {
|
|
54
|
+
if (!isObj(raw))
|
|
55
|
+
invalid("field lastCheckpoint must be an object");
|
|
56
|
+
return {
|
|
57
|
+
dev: num(raw, "dev"),
|
|
58
|
+
ino: num(raw, "ino"),
|
|
59
|
+
size: num(raw, "size"),
|
|
60
|
+
mtimeMs: num(raw, "mtimeMs"),
|
|
61
|
+
// A checkpoint without a digest cannot detect a same-size, same-mtime rewrite, so the field is
|
|
62
|
+
// required — but an absent file legitimately checkpoints an empty digest, so "" is valid.
|
|
63
|
+
digest: digest(raw, "digest"),
|
|
64
|
+
present: bool(raw, "present"),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function fireWindow(raw) {
|
|
68
|
+
if (!isObj(raw))
|
|
69
|
+
invalid("field fireWindow must be an object");
|
|
70
|
+
return { startMs: num(raw, "startMs"), count: num(raw, "count") };
|
|
71
|
+
}
|
|
72
|
+
function runtimeIdentity(raw, field) {
|
|
73
|
+
const value = raw[field];
|
|
74
|
+
if (!isObj(value))
|
|
75
|
+
invalid(`field ${field} must be an object`);
|
|
76
|
+
const argv = value.argv;
|
|
77
|
+
if (!Array.isArray(argv) || !argv.every(isStr))
|
|
78
|
+
invalid(`field ${field}.argv must be an array of strings`);
|
|
79
|
+
const pid = value.pid;
|
|
80
|
+
if (!isNum(pid))
|
|
81
|
+
invalid(`field ${field}.pid must be a finite number`);
|
|
82
|
+
const groupId = value.processGroupId;
|
|
83
|
+
if (groupId !== undefined && !isNum(groupId))
|
|
84
|
+
invalid(`field ${field}.processGroupId must be a finite number`);
|
|
85
|
+
return {
|
|
86
|
+
pid,
|
|
87
|
+
...(groupId !== undefined ? { processGroupId: groupId } : {}),
|
|
88
|
+
startedAtMs: num(value, "startedAtMs"),
|
|
89
|
+
bootAtMs: num(value, "bootAtMs"),
|
|
90
|
+
argv,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function parseMonitor(entry) {
|
|
94
|
+
if (!isObj(entry))
|
|
95
|
+
invalid("a monitor entry must be an object");
|
|
96
|
+
return {
|
|
97
|
+
monitorId: str(entry, "monitorId"),
|
|
98
|
+
sessionId: str(entry, "sessionId"),
|
|
99
|
+
description: str(entry, "description"),
|
|
100
|
+
runtimeKind: oneOf(RUNTIME_KINDS, entry, "runtimeKind"),
|
|
101
|
+
durabilityClass: oneOf(DURABILITY_CLASSES, entry, "durabilityClass"),
|
|
102
|
+
command: opt(entry, "command", str),
|
|
103
|
+
path: opt(entry, "path", str),
|
|
104
|
+
event: opt(entry, "event", (raw, field) => oneOf(FILE_EVENTS, raw, field)),
|
|
105
|
+
filter: opt(entry, "filter", str),
|
|
106
|
+
cwd: opt(entry, "cwd", str),
|
|
107
|
+
approvedParent: opt(entry, "approvedParent", str),
|
|
108
|
+
createdAt: num(entry, "createdAt"),
|
|
109
|
+
expiresAt: entry.expiresAt === undefined || entry.expiresAt === null ? null : num(entry, "expiresAt"),
|
|
110
|
+
persistent: bool(entry, "persistent"),
|
|
111
|
+
suspended: bool(entry, "suspended"),
|
|
112
|
+
lastCheckpoint: entry.lastCheckpoint === null ? null : checkpoint(entry.lastCheckpoint),
|
|
113
|
+
deliveryPaused: bool(entry, "deliveryPaused"),
|
|
114
|
+
// Unknown keys are ignored by this field-by-field parse, so a manifest written before
|
|
115
|
+
// `wakeCount` was dropped still reads back cleanly; no version bump is needed. The same
|
|
116
|
+
// rule makes `runtime` / `deadlineMs` optional additions that v1 readers skip.
|
|
117
|
+
fireWindow: fireWindow(entry.fireWindow),
|
|
118
|
+
...(entry.runtime === undefined ? {} : { runtime: runtimeIdentity(entry, "runtime") }),
|
|
119
|
+
...(entry.deadlineMs === undefined ? {} : { deadlineMs: num(entry, "deadlineMs") }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function parseBackgroundSession(entry) {
|
|
123
|
+
if (!isObj(entry))
|
|
124
|
+
invalid("a background session entry must be an object");
|
|
125
|
+
return {
|
|
126
|
+
id: str(entry, "id"),
|
|
127
|
+
command: str(entry, "command"),
|
|
128
|
+
startedAtMs: num(entry, "startedAtMs"),
|
|
129
|
+
...(entry.runtime === undefined ? {} : { runtime: runtimeIdentity(entry, "runtime") }),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** Strict fail-closed domain parse; the sidecar store has already checked version and session. */
|
|
133
|
+
export function parseTerminalManifest(raw, ref) {
|
|
134
|
+
if (!isObj(raw))
|
|
135
|
+
invalid("the payload must be an object");
|
|
136
|
+
if (!Array.isArray(raw.monitors))
|
|
137
|
+
invalid("field monitors must be an array");
|
|
138
|
+
if (!Array.isArray(raw.backgroundSessions))
|
|
139
|
+
invalid("field backgroundSessions must be an array");
|
|
140
|
+
return {
|
|
141
|
+
version: TERMINAL_MANIFEST_VERSION,
|
|
142
|
+
sessionId: ref.sessionId,
|
|
143
|
+
monitors: raw.monitors.map(parseMonitor),
|
|
144
|
+
backgroundSessions: raw.backgroundSessions.map(parseBackgroundSession),
|
|
145
|
+
updatedAt: num(raw, "updatedAt"),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=terminal-manifest-parse.js.map
|