@arnilo/prism-tools 0.0.0-stage → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +418 -0
- package/LICENSE +21 -0
- package/README.md +44 -2
- package/browser/README.md +98 -0
- package/browser/skills/browser-automation/SKILL.md +14 -0
- package/browser/src/actions/cdp.ts +131 -0
- package/browser/src/actions/dialog.ts +25 -0
- package/browser/src/actions/download_release.ts +22 -0
- package/browser/src/actions/index.ts +37 -0
- package/browser/src/actions/locator.ts +70 -0
- package/browser/src/actions/navigate.ts +23 -0
- package/browser/src/actions/screenshot.ts +27 -0
- package/browser/src/actions/scroll.ts +24 -0
- package/browser/src/actions/select_page.ts +21 -0
- package/browser/src/actions/types.ts +114 -0
- package/browser/src/actions/upload.ts +44 -0
- package/browser/src/actions/wait.ts +45 -0
- package/browser/src/browser.ts +197 -0
- package/browser/src/cdp.ts +178 -0
- package/browser/src/checkpoint.ts +189 -0
- package/browser/src/config.ts +72 -0
- package/browser/src/downloads.ts +253 -0
- package/browser/src/errors.ts +31 -0
- package/browser/src/evaluate.ts +101 -0
- package/browser/src/index.ts +129 -0
- package/browser/src/limits.ts +271 -0
- package/browser/src/manager.ts +730 -0
- package/browser/src/network.ts +263 -0
- package/browser/src/observe.ts +178 -0
- package/browser/src/policy.ts +133 -0
- package/browser/src/screenshot.ts +107 -0
- package/browser/src/shared-sandbox.ts +53 -0
- package/browser/src/snapshot.ts +99 -0
- package/browser/src/targets.ts +165 -0
- package/browser/src/tools.ts +480 -0
- package/browser/src/types.ts +316 -0
- package/browser/src/uploads.ts +174 -0
- package/coding/README.md +337 -0
- package/coding/src/agent/acp-operations.ts +146 -0
- package/coding/src/agent/artifacts.ts +38 -0
- package/coding/src/agent/ask-user-decision.ts +623 -0
- package/coding/src/agent/atomic-write.ts +23 -0
- package/coding/src/agent/bounded-file.ts +21 -0
- package/coding/src/agent/checks.ts +301 -0
- package/coding/src/agent/coding-checkpoint.ts +828 -0
- package/coding/src/agent/delete.ts +237 -0
- package/coding/src/agent/diagnostics-tool.ts +163 -0
- package/coding/src/agent/diagnostics.ts +262 -0
- package/coding/src/agent/edit-diff.ts +382 -0
- package/coding/src/agent/edit.ts +322 -0
- package/coding/src/agent/effects.ts +102 -0
- package/coding/src/agent/env.ts +28 -0
- package/coding/src/agent/execution-policy.ts +89 -0
- package/coding/src/agent/file-mutation-queue.ts +72 -0
- package/coding/src/agent/forge/github.ts +603 -0
- package/coding/src/agent/forge/index.ts +16 -0
- package/coding/src/agent/forge/types.ts +185 -0
- package/coding/src/agent/git-aware-repository.ts +305 -0
- package/coding/src/agent/git-exec.ts +326 -0
- package/coding/src/agent/git-status.ts +177 -0
- package/coding/src/agent/git-tools.ts +360 -0
- package/coding/src/agent/git.ts +1074 -0
- package/coding/src/agent/glob-match.ts +146 -0
- package/coding/src/agent/glob.ts +182 -0
- package/coding/src/agent/goal-verify.ts +385 -0
- package/coding/src/agent/grep-worker.ts +74 -0
- package/coding/src/agent/grep.ts +459 -0
- package/coding/src/agent/identity.ts +48 -0
- package/coding/src/agent/index.ts +676 -0
- package/coding/src/agent/language/client.ts +429 -0
- package/coding/src/agent/language/framing.ts +190 -0
- package/coding/src/agent/language/index.ts +23 -0
- package/coding/src/agent/language/intelligence.ts +682 -0
- package/coding/src/agent/language/types.ts +187 -0
- package/coding/src/agent/lifecycle-events.ts +121 -0
- package/coding/src/agent/lifecycle.ts +166 -0
- package/coding/src/agent/limits.ts +214 -0
- package/coding/src/agent/move.ts +178 -0
- package/coding/src/agent/mutation-path.ts +52 -0
- package/coding/src/agent/output-accumulator.ts +304 -0
- package/coding/src/agent/path-utils.ts +120 -0
- package/coding/src/agent/process/index.ts +58 -0
- package/coding/src/agent/process/recovery.ts +508 -0
- package/coding/src/agent/process/sessions-host.ts +222 -0
- package/coding/src/agent/process/sessions-monitor.ts +141 -0
- package/coding/src/agent/process/sessions-recovery.ts +241 -0
- package/coding/src/agent/process/sessions-spawn.ts +613 -0
- package/coding/src/agent/process/sessions-teardown.ts +281 -0
- package/coding/src/agent/process/sessions.ts +30 -0
- package/coding/src/agent/process/types.ts +358 -0
- package/coding/src/agent/read-path-set.ts +126 -0
- package/coding/src/agent/read.ts +713 -0
- package/coding/src/agent/repository/glob.ts +147 -0
- package/coding/src/agent/repository/indexed-search.ts +480 -0
- package/coding/src/agent/repository/list.ts +131 -0
- package/coding/src/agent/repository/operations.ts +22 -0
- package/coding/src/agent/repository/path.ts +98 -0
- package/coding/src/agent/repository/search.ts +277 -0
- package/coding/src/agent/repository/types.ts +243 -0
- package/coding/src/agent/repository/walk.ts +126 -0
- package/coding/src/agent/repository.ts +12 -0
- package/coding/src/agent/shell-background.ts +264 -0
- package/coding/src/agent/shell-output.ts +75 -0
- package/coding/src/agent/shell.ts +526 -0
- package/coding/src/agent/todo.ts +399 -0
- package/coding/src/agent/truncate.ts +251 -0
- package/coding/src/agent/write.ts +170 -0
- package/coding/src/ask-user-host.ts +48 -0
- package/coding/src/config.ts +278 -0
- package/coding/src/hard-deny.ts +97 -0
- package/coding/src/index.ts +318 -0
- package/coding/src/principal-guard.ts +84 -0
- package/coding/src/sandbox.ts +92 -0
- package/coding/src/security/approval.ts +144 -0
- package/coding/src/security/command-rules.ts +57 -0
- package/coding/src/security/docker-cli.ts +222 -0
- package/coding/src/security/docker-sandbox.ts +1205 -0
- package/coding/src/security/e2b-sandbox.ts +853 -0
- package/coding/src/security/egress/dns-pin.ts +88 -0
- package/coding/src/security/egress/index.ts +32 -0
- package/coding/src/security/egress/limits.ts +57 -0
- package/coding/src/security/egress/policy.ts +137 -0
- package/coding/src/security/egress/proxy.ts +599 -0
- package/coding/src/security/egress/types.ts +30 -0
- package/coding/src/security/index.ts +199 -0
- package/coding/src/security/native-sandbox.ts +639 -0
- package/coding/src/security/path-containment.ts +55 -0
- package/coding/src/security/sandbox-coding-operations.ts +392 -0
- package/coding/src/security/sandbox-fs-operations.ts +644 -0
- package/coding/src/security/sandbox-limits.ts +147 -0
- package/coding/src/security/sandbox-tar.ts +308 -0
- package/coding/src/security/sandbox.ts +179 -0
- package/coding/src/security/semaphore.ts +55 -0
- package/coding/src/targets.ts +110 -0
- package/coding/src/workspace/diff.ts +78 -0
- package/coding/src/workspace/errors.ts +29 -0
- package/coding/src/workspace/index.ts +5 -0
- package/coding/src/workspace/merge.ts +151 -0
- package/coding/src/workspace/routing.ts +76 -0
- package/coding/src/workspace/service.ts +584 -0
- package/computer-use/README.md +105 -0
- package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
- package/computer-use/src/classify.ts +53 -0
- package/computer-use/src/command.ts +29 -0
- package/computer-use/src/config.ts +69 -0
- package/computer-use/src/create.ts +170 -0
- package/computer-use/src/devices.ts +247 -0
- package/computer-use/src/env.ts +28 -0
- package/computer-use/src/index.ts +157 -0
- package/computer-use/src/skill.ts +21 -0
- package/openapi/README.md +97 -0
- package/openapi/src/compile.ts +386 -0
- package/openapi/src/config.ts +73 -0
- package/openapi/src/errors.ts +16 -0
- package/openapi/src/execute.ts +241 -0
- package/openapi/src/index.ts +124 -0
- package/openapi/src/limits.ts +63 -0
- package/openapi/src/specs.ts +86 -0
- package/openapi/src/tools.ts +60 -0
- package/openapi/src/types.ts +61 -0
- package/package.json +171 -4
- package/research/README.md +72 -0
- package/research/skills/research-report/SKILL.md +12 -0
- package/research/skills/research-scoping/SKILL.md +10 -0
- package/research/skills/research-synthesis/SKILL.md +10 -0
- package/research/skills/source-triage/SKILL.md +10 -0
- package/research/src/check.ts +71 -0
- package/research/src/config.ts +41 -0
- package/research/src/index.ts +147 -0
- package/research/src/ledger.ts +157 -0
- package/research/src/urls.ts +21 -0
- package/web/README.md +287 -0
- package/web/skills/web-research/SKILL.md +17 -0
- package/web/src/brave.ts +66 -0
- package/web/src/cache.ts +151 -0
- package/web/src/config.ts +210 -0
- package/web/src/controls.ts +128 -0
- package/web/src/domains.ts +114 -0
- package/web/src/exa.ts +60 -0
- package/web/src/firecrawl.ts +160 -0
- package/web/src/index.ts +387 -0
- package/web/src/kagi.ts +73 -0
- package/web/src/limits.ts +55 -0
- package/web/src/native/convert.ts +84 -0
- package/web/src/native/fetch.ts +226 -0
- package/web/src/normalize.ts +131 -0
- package/web/src/obscura/cdp.ts +201 -0
- package/web/src/obscura/classify.ts +35 -0
- package/web/src/obscura/cli.ts +254 -0
- package/web/src/obscura/errors.ts +8 -0
- package/web/src/obscura/index.ts +59 -0
- package/web/src/obscura/limits.ts +92 -0
- package/web/src/obscura/mcp.ts +104 -0
- package/web/src/obscura/playwright.ts +133 -0
- package/web/src/obscura/process.ts +235 -0
- package/web/src/obscura/search-profile.ts +73 -0
- package/web/src/obscura/types.ts +48 -0
- package/web/src/obscura/web.ts +419 -0
- package/web/src/prompt-fetch.ts +88 -0
- package/web/src/render.ts +75 -0
- package/web/src/searxng.ts +78 -0
- package/web/src/slice.ts +139 -0
- package/web/src/tavily.ts +77 -0
- package/web/src/tools.ts +182 -0
- package/web/src/transport.ts +310 -0
- package/web/src/types.ts +199 -0
- package/work/README.md +77 -0
- package/work/THIRD_PARTY_NOTICES +24 -0
- package/work/docling/ocr.py +150 -0
- package/work/sandbox/Dockerfile +36 -0
- package/work/sandbox/soffice.sh +12 -0
- package/work/src/command.ts +29 -0
- package/work/src/config.ts +138 -0
- package/work/src/connectors/cli.ts +343 -0
- package/work/src/connectors/drafts.ts +647 -0
- package/work/src/connectors/errors.ts +8 -0
- package/work/src/connectors/file-bytes.ts +149 -0
- package/work/src/connectors/google-workspace-http.ts +399 -0
- package/work/src/connectors/google-workspace.ts +465 -0
- package/work/src/connectors/gws-tools.ts +378 -0
- package/work/src/connectors/http.ts +123 -0
- package/work/src/connectors/idempotency.ts +235 -0
- package/work/src/connectors/identity.ts +34 -0
- package/work/src/connectors/index.ts +93 -0
- package/work/src/connectors/limits.ts +50 -0
- package/work/src/connectors/m365-tools.ts +308 -0
- package/work/src/connectors/microsoft365-http.ts +428 -0
- package/work/src/connectors/microsoft365.ts +402 -0
- package/work/src/connectors/normalize.ts +120 -0
- package/work/src/connectors/tool-helpers.ts +398 -0
- package/work/src/connectors/tools.ts +42 -0
- package/work/src/connectors/types.ts +399 -0
- package/work/src/diagrams/canonicalize.ts +166 -0
- package/work/src/diagrams/caps.ts +52 -0
- package/work/src/diagrams/embed.ts +340 -0
- package/work/src/diagrams/errors.ts +76 -0
- package/work/src/diagrams/index.ts +81 -0
- package/work/src/diagrams/messages.ts +150 -0
- package/work/src/diagrams/origin.ts +52 -0
- package/work/src/diagrams/telemetry.ts +34 -0
- package/work/src/diagrams/xml.ts +267 -0
- package/work/src/document-extraction/index.ts +563 -0
- package/work/src/document-reader/ambient.d.ts +2 -0
- package/work/src/document-reader/errors.ts +7 -0
- package/work/src/document-reader/index.ts +181 -0
- package/work/src/document-reader/mistral-ocr.ts +318 -0
- package/work/src/document-reader/parsers.ts +171 -0
- package/work/src/documents/caps.ts +135 -0
- package/work/src/documents/diff.ts +269 -0
- package/work/src/documents/errors.ts +44 -0
- package/work/src/documents/fidelity.ts +98 -0
- package/work/src/documents/generate.ts +96 -0
- package/work/src/documents/hash.ts +8 -0
- package/work/src/documents/index.ts +14 -0
- package/work/src/documents/model-schema.ts +639 -0
- package/work/src/documents/parse.ts +252 -0
- package/work/src/documents/patch-history.ts +81 -0
- package/work/src/documents/patch.ts +476 -0
- package/work/src/documents/preview-html.ts +245 -0
- package/work/src/documents/preview.ts +227 -0
- package/work/src/documents/telemetry.ts +36 -0
- package/work/src/documents/translate/docx.ts +292 -0
- package/work/src/documents/translate/pptx.ts +304 -0
- package/work/src/documents/translate/xlsx.ts +137 -0
- package/work/src/documents/types.ts +188 -0
- package/work/src/index.ts +311 -0
- package/work/src/runtime/worker-pool-entry.ts +102 -0
- package/work/src/runtime/worker-pool.ts +267 -0
- package/work/src/sandbox/composition.ts +212 -0
- package/work/src/sandbox/image.ts +6 -0
- package/work/src/sandbox/index.ts +14 -0
- package/work/src/sheets/caps.ts +68 -0
- package/work/src/sheets/csv.ts +351 -0
- package/work/src/sheets/decimal.ts +388 -0
- package/work/src/sheets/errors.ts +52 -0
- package/work/src/sheets/index.ts +62 -0
- package/work/src/sheets/inference.ts +378 -0
- package/work/src/sheets/telemetry.ts +34 -0
- package/work/src/sheets/types.ts +162 -0
- package/work/src/sheets/xlsx.ts +388 -0
- package/work/src/skills/index.ts +8 -0
- package/work/src/skills/load.ts +43 -0
- package/work/src/tools/filesystem.ts +21 -0
- package/work/src/tools/index.ts +1 -0
- package/work/src/tools/local-filesystem.ts +33 -0
- package/work/src/tools/office.ts +311 -0
- package/work/vendor/hermes-agent/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
- package/work/vendor-lock.json +11 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminate / cancel / dispose phases for managed process sessions.
|
|
3
|
+
*/
|
|
4
|
+
import { killProcessTree } from "../shell.js";
|
|
5
|
+
import { acquireRecordLease, loadProcessRecoveryRecords, releaseRecordLease } from "./recovery.js";
|
|
6
|
+
import {
|
|
7
|
+
assertNotDisposed,
|
|
8
|
+
durableSeams,
|
|
9
|
+
emit,
|
|
10
|
+
isTerminalState,
|
|
11
|
+
nowIso,
|
|
12
|
+
type SessionRecord,
|
|
13
|
+
type SessionsHost,
|
|
14
|
+
settleWaiters,
|
|
15
|
+
} from "./sessions-host.js";
|
|
16
|
+
import { persistRecoveryUnknown, persistTransition } from "./sessions-monitor.js";
|
|
17
|
+
import { ProcessSessionError, type ProcessSessionState } from "./types.js";
|
|
18
|
+
|
|
19
|
+
export function reconcileAllUnknown(host: SessionsHost): number {
|
|
20
|
+
let n = 0;
|
|
21
|
+
for (const record of [...host.sessions.values()]) {
|
|
22
|
+
if (record.state !== "running" && record.state !== "starting") continue;
|
|
23
|
+
terminateRecord(host, record, "unknown", null);
|
|
24
|
+
n += 1;
|
|
25
|
+
}
|
|
26
|
+
return n;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function checkSandboxAlive(host: SessionsHost): Promise<void> {
|
|
30
|
+
if (!host.sandbox?.status || host.sandboxLost) return;
|
|
31
|
+
try {
|
|
32
|
+
const status = await host.sandbox.status();
|
|
33
|
+
if (status.state !== "running") {
|
|
34
|
+
host.sandboxLost = true;
|
|
35
|
+
reconcileAllUnknown(host);
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
host.sandboxLost = true;
|
|
39
|
+
reconcileAllUnknown(host);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function sweepExpired(host: SessionsHost): void {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
for (const record of host.sessions.values()) {
|
|
46
|
+
if (record.state !== "running" && record.state !== "starting") continue;
|
|
47
|
+
if (now < record.expiresAt) continue;
|
|
48
|
+
terminateRecord(host, record, "expired", null);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function terminateRecord(host: SessionsHost, record: SessionRecord, state: ProcessSessionState, exitCode: number | null): void {
|
|
53
|
+
if (
|
|
54
|
+
record.state === "exited" ||
|
|
55
|
+
record.state === "killed" ||
|
|
56
|
+
record.state === "released" ||
|
|
57
|
+
record.state === "expired" ||
|
|
58
|
+
record.state === "unknown"
|
|
59
|
+
) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const child = record.child;
|
|
63
|
+
const backend = record.backend;
|
|
64
|
+
const pty = record.pty;
|
|
65
|
+
record.child = undefined;
|
|
66
|
+
record.backend = undefined;
|
|
67
|
+
record.pty = undefined;
|
|
68
|
+
if (state === "released") {
|
|
69
|
+
try {
|
|
70
|
+
child?.stdout.removeAllListeners();
|
|
71
|
+
child?.stderr.removeAllListeners();
|
|
72
|
+
child?.stdout.destroy();
|
|
73
|
+
child?.stderr.destroy();
|
|
74
|
+
child?.stdin.destroy();
|
|
75
|
+
child?.unref();
|
|
76
|
+
} catch {
|
|
77
|
+
// best effort
|
|
78
|
+
}
|
|
79
|
+
void backend?.release().catch(() => undefined);
|
|
80
|
+
void pty?.release().catch(() => undefined);
|
|
81
|
+
} else if (state === "killed" || state === "expired" || state === "unknown") {
|
|
82
|
+
if (child?.pid) {
|
|
83
|
+
try {
|
|
84
|
+
killProcessTree(child.pid);
|
|
85
|
+
} catch {
|
|
86
|
+
// best effort
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (backend) {
|
|
90
|
+
void backend.kill().catch(() => undefined);
|
|
91
|
+
}
|
|
92
|
+
if (pty) {
|
|
93
|
+
void pty.kill().catch(() => undefined);
|
|
94
|
+
}
|
|
95
|
+
} else if (state === "exited" && child) {
|
|
96
|
+
try {
|
|
97
|
+
child.stdout.destroy();
|
|
98
|
+
child.stderr.destroy();
|
|
99
|
+
child.stdin.destroy();
|
|
100
|
+
} catch {
|
|
101
|
+
// best effort
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
record.state = state;
|
|
105
|
+
record.exitCode = state === "unknown" || state === "released" ? null : exitCode;
|
|
106
|
+
record.exitedAt = nowIso();
|
|
107
|
+
record.stdinClosed = true;
|
|
108
|
+
try {
|
|
109
|
+
record.accumulator.finish();
|
|
110
|
+
} catch {
|
|
111
|
+
// ignore
|
|
112
|
+
}
|
|
113
|
+
persistTransition(host, record);
|
|
114
|
+
settleWaiters(record);
|
|
115
|
+
const type =
|
|
116
|
+
state === "exited"
|
|
117
|
+
? "process_exited"
|
|
118
|
+
: state === "killed"
|
|
119
|
+
? "process_killed"
|
|
120
|
+
: state === "released"
|
|
121
|
+
? "process_released"
|
|
122
|
+
: state === "expired"
|
|
123
|
+
? "process_expired"
|
|
124
|
+
: "process_unknown";
|
|
125
|
+
emit(host, {
|
|
126
|
+
type,
|
|
127
|
+
sessionId: record.id,
|
|
128
|
+
processId: record.pid !== undefined ? String(record.pid) : record.id,
|
|
129
|
+
owner: record.owner,
|
|
130
|
+
exitCode: record.exitCode,
|
|
131
|
+
at: record.exitedAt,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function requireRecord(host: SessionsHost, sessionId: string, owner?: string): SessionRecord {
|
|
136
|
+
assertNotDisposed(host);
|
|
137
|
+
sweepExpired(host);
|
|
138
|
+
const record = host.sessions.get(sessionId);
|
|
139
|
+
if (!record) throw new ProcessSessionError("ERR_PRISM_PROCESS_STATE", `unknown session: ${sessionId}`);
|
|
140
|
+
if (owner !== undefined && owner !== record.owner) {
|
|
141
|
+
throw new ProcessSessionError("ERR_PRISM_PROCESS_OWNERSHIP", "session owner mismatch");
|
|
142
|
+
}
|
|
143
|
+
return record;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function cancelOwned(host: SessionsHost, owner: string, cancelOptions?: { release?: boolean }): Promise<void> {
|
|
147
|
+
assertNotDisposed(host);
|
|
148
|
+
sweepExpired(host);
|
|
149
|
+
await checkSandboxAlive(host);
|
|
150
|
+
const release = cancelOptions?.release === true;
|
|
151
|
+
for (const record of [...host.sessions.values()]) {
|
|
152
|
+
if (record.owner !== owner) continue;
|
|
153
|
+
if (record.state !== "running" && record.state !== "starting") continue;
|
|
154
|
+
const cancelPty = record.pty;
|
|
155
|
+
const cancelBackend = record.backend;
|
|
156
|
+
if (release || record.releaseOnCancel) {
|
|
157
|
+
if (cancelPty || cancelBackend) {
|
|
158
|
+
terminateRecord(host, record, "released", null);
|
|
159
|
+
if (cancelPty) {
|
|
160
|
+
try {
|
|
161
|
+
await cancelPty.release();
|
|
162
|
+
} catch {
|
|
163
|
+
// continue
|
|
164
|
+
}
|
|
165
|
+
} else if (cancelBackend) {
|
|
166
|
+
try {
|
|
167
|
+
await cancelBackend.release();
|
|
168
|
+
} catch {
|
|
169
|
+
// continue
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
terminateRecord(host, record, "released", null);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
if (cancelPty || cancelBackend) {
|
|
177
|
+
terminateRecord(host, record, "killed", null);
|
|
178
|
+
if (cancelPty) {
|
|
179
|
+
try {
|
|
180
|
+
await cancelPty.kill();
|
|
181
|
+
} catch {
|
|
182
|
+
// continue
|
|
183
|
+
}
|
|
184
|
+
} else if (cancelBackend) {
|
|
185
|
+
try {
|
|
186
|
+
await cancelBackend.kill();
|
|
187
|
+
} catch {
|
|
188
|
+
// continue
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
terminateRecord(host, record, "killed", null);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Durable pass (plan 026 Task 5): cancellation of a recovered/unattached
|
|
197
|
+
// process either reaches the attached backend (above) or records unknown —
|
|
198
|
+
// never a fabricated exit. Lease + CAS guard every mutation so two replicas
|
|
199
|
+
// cannot cancel the same record into different outcomes.
|
|
200
|
+
if (host.durable) {
|
|
201
|
+
const seams = durableSeams(host);
|
|
202
|
+
const { records } = await loadProcessRecoveryRecords({
|
|
203
|
+
checkpoints: seams.checkpoints,
|
|
204
|
+
limits: host.recoveryLimits,
|
|
205
|
+
ownership: host.ownership,
|
|
206
|
+
});
|
|
207
|
+
for (const { record, version } of records) {
|
|
208
|
+
if (record.owner !== owner) continue;
|
|
209
|
+
if (isTerminalState(record.state)) continue;
|
|
210
|
+
if (host.sessions.has(record.id)) continue; // handled by the live pass above
|
|
211
|
+
const lease = await acquireRecordLease({
|
|
212
|
+
leases: seams.leases,
|
|
213
|
+
id: record.id,
|
|
214
|
+
ownerId: seams.ownerId,
|
|
215
|
+
ttlMs: host.recoveryLimits.leaseTtlMs,
|
|
216
|
+
ownership: host.ownership,
|
|
217
|
+
});
|
|
218
|
+
if (!lease) continue; // another replica owns or is recovering it
|
|
219
|
+
try {
|
|
220
|
+
await persistRecoveryUnknown(host, record, version);
|
|
221
|
+
} finally {
|
|
222
|
+
await releaseRecordLease({
|
|
223
|
+
leases: seams.leases,
|
|
224
|
+
id: record.id,
|
|
225
|
+
ownerId: seams.ownerId,
|
|
226
|
+
token: lease.token,
|
|
227
|
+
ownership: host.ownership,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function disposeSessions(host: SessionsHost): Promise<void> {
|
|
235
|
+
if (host.disposed) return;
|
|
236
|
+
host.disposed = true;
|
|
237
|
+
for (const record of [...host.sessions.values()]) {
|
|
238
|
+
if (record.state === "running" || record.state === "starting") {
|
|
239
|
+
if (record.pty) {
|
|
240
|
+
try {
|
|
241
|
+
await record.pty.kill();
|
|
242
|
+
} catch {
|
|
243
|
+
// best effort
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (record.backend) {
|
|
247
|
+
try {
|
|
248
|
+
await record.backend.kill();
|
|
249
|
+
} catch {
|
|
250
|
+
// best effort
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
terminateRecord(host, record, "killed", null);
|
|
254
|
+
} else if (record.state === "released" && record.pid) {
|
|
255
|
+
try {
|
|
256
|
+
killProcessTree(record.pid);
|
|
257
|
+
} catch {
|
|
258
|
+
// best effort
|
|
259
|
+
}
|
|
260
|
+
record.pid = undefined;
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
await record.accumulator.cleanupTempFile();
|
|
264
|
+
} catch {
|
|
265
|
+
// best effort
|
|
266
|
+
}
|
|
267
|
+
if (record.recoveryLeaseToken) {
|
|
268
|
+
// A lease token only exists on durable hosts (createSessionsHost validates the seams).
|
|
269
|
+
const seams = durableSeams(host);
|
|
270
|
+
void releaseRecordLease({
|
|
271
|
+
leases: seams.leases,
|
|
272
|
+
id: record.id,
|
|
273
|
+
ownerId: seams.ownerId,
|
|
274
|
+
token: record.recoveryLeaseToken,
|
|
275
|
+
ownership: host.ownership,
|
|
276
|
+
});
|
|
277
|
+
record.recoveryLeaseToken = undefined;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
host.sessions.clear();
|
|
281
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed ProcessSession registry — native spawn or optional sandbox startProcess.
|
|
3
|
+
*/
|
|
4
|
+
import { assertNotDisposed, createSessionsHost } from "./sessions-host.js";
|
|
5
|
+
import { recoverSessions } from "./sessions-recovery.js";
|
|
6
|
+
import { startSession } from "./sessions-spawn.js";
|
|
7
|
+
import { cancelOwned, disposeSessions, reconcileAllUnknown, requireRecord, terminateRecord } from "./sessions-teardown.js";
|
|
8
|
+
import { type CreateProcessSessionsOptions, ProcessSessionError, type ProcessSessions } from "./types.js";
|
|
9
|
+
|
|
10
|
+
export function createProcessSessions(options: CreateProcessSessionsOptions): ProcessSessions {
|
|
11
|
+
const host = createSessionsHost(options);
|
|
12
|
+
return {
|
|
13
|
+
start: (request) => startSession(host, request),
|
|
14
|
+
get: (sessionId, owner) => requireRecord(host, sessionId, owner).handle,
|
|
15
|
+
cancelOwned: (owner, cancelOptions) => cancelOwned(host, owner, cancelOptions),
|
|
16
|
+
markUnknown: async (sessionId, owner) => {
|
|
17
|
+
const record = requireRecord(host, sessionId, owner);
|
|
18
|
+
if (record.state !== "running" && record.state !== "starting") {
|
|
19
|
+
throw new ProcessSessionError("ERR_PRISM_PROCESS_STATE", `cannot mark unknown in state ${record.state}`);
|
|
20
|
+
}
|
|
21
|
+
terminateRecord(host, record, "unknown", null);
|
|
22
|
+
},
|
|
23
|
+
reconcile: async () => {
|
|
24
|
+
assertNotDisposed(host);
|
|
25
|
+
return { markedUnknown: reconcileAllUnknown(host) };
|
|
26
|
+
},
|
|
27
|
+
recover: (recoverOptions) => recoverSessions(host, recoverOptions),
|
|
28
|
+
dispose: () => disposeSessions(host),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { OwnershipScope } from "@arnilo/prism";
|
|
2
|
+
import type { CheckpointStore, LeaseStore } from "@arnilo/prism-extensions/workflows";
|
|
3
|
+
/** Duck-typed long-running sandbox handle (mirrors coding-security SandboxProcessHandle). */
|
|
4
|
+
import type { ExecutionPolicy } from "../execution-policy.js";
|
|
5
|
+
import { AgentIdentity } from "../identity.js";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_MAX_PROCESS_INPUT_BYTES,
|
|
8
|
+
DEFAULT_MAX_PROCESS_LIFETIME_MS,
|
|
9
|
+
DEFAULT_MAX_PROCESS_OUTPUT_CHUNK_BYTES,
|
|
10
|
+
DEFAULT_MAX_PROCESS_SESSIONS,
|
|
11
|
+
DEFAULT_MAX_PROCESS_TOTAL_OUTPUT_BYTES,
|
|
12
|
+
DEFAULT_MAX_PTY_ATTACH_TIMEOUT_MS,
|
|
13
|
+
DEFAULT_MAX_PTY_BACKEND_METADATA_BYTES,
|
|
14
|
+
DEFAULT_MAX_TERMINAL_COLUMNS,
|
|
15
|
+
DEFAULT_MAX_TERMINAL_RESIZES_PER_MINUTE,
|
|
16
|
+
DEFAULT_MAX_TERMINAL_ROWS,
|
|
17
|
+
DEFAULT_MAX_TERMINAL_TERM_BYTES,
|
|
18
|
+
HARD_MAX_PROCESS_INPUT_BYTES,
|
|
19
|
+
HARD_MAX_PROCESS_LIFETIME_MS,
|
|
20
|
+
HARD_MAX_PROCESS_OUTPUT_CHUNK_BYTES,
|
|
21
|
+
HARD_MAX_PROCESS_SESSIONS,
|
|
22
|
+
HARD_MAX_PROCESS_TOTAL_OUTPUT_BYTES,
|
|
23
|
+
HARD_MAX_PTY_ATTACH_TIMEOUT_MS,
|
|
24
|
+
HARD_MAX_PTY_BACKEND_METADATA_BYTES,
|
|
25
|
+
HARD_MAX_TERMINAL_COLUMNS,
|
|
26
|
+
HARD_MAX_TERMINAL_RESIZES_PER_MINUTE,
|
|
27
|
+
HARD_MAX_TERMINAL_ROWS,
|
|
28
|
+
HARD_MAX_TERMINAL_TERM_BYTES,
|
|
29
|
+
validateCodingLimit,
|
|
30
|
+
} from "../limits.js";
|
|
31
|
+
export interface ProcessSandboxHandle {
|
|
32
|
+
write(data: Uint8Array): Promise<void>;
|
|
33
|
+
signal(name: string): Promise<void>;
|
|
34
|
+
kill(): Promise<void>;
|
|
35
|
+
release(): Promise<void>;
|
|
36
|
+
wait(options?: { timeoutMs?: number; signal?: AbortSignal }): Promise<{ exitCode: number | null }>;
|
|
37
|
+
/** Opaque non-secret reattachment ref for durable process recovery (plan 026 Task 5). */
|
|
38
|
+
readonly ref?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ProcessSandboxStartRequest {
|
|
42
|
+
readonly file: string;
|
|
43
|
+
readonly args: readonly string[];
|
|
44
|
+
readonly cwd?: string;
|
|
45
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
46
|
+
readonly onData?: (data: Buffer) => void;
|
|
47
|
+
readonly signal?: AbortSignal;
|
|
48
|
+
readonly timeout?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Optional sandbox backend for ProcessSessions.
|
|
52
|
+
* Presence of `startProcess` = long-running capable; absence fails closed.
|
|
53
|
+
*/
|
|
54
|
+
export interface ProcessSandboxBackend {
|
|
55
|
+
startProcess?(request: ProcessSandboxStartRequest): Promise<ProcessSandboxHandle>;
|
|
56
|
+
attachProcess?(ref: string): Promise<ProcessSandboxHandle | null>;
|
|
57
|
+
status?(): Promise<{ readonly state: string }>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Bounded terminal geometry + TERM for interactive PTY sessions (frozen caps in limits.ts). */
|
|
61
|
+
export interface ProcessTerminalRequest {
|
|
62
|
+
/** 1..maxTerminalColumns (default 120; host-configured max wins when smaller). */
|
|
63
|
+
readonly columns?: number;
|
|
64
|
+
/** 1..maxTerminalRows (default 40; host-configured max wins when smaller). */
|
|
65
|
+
readonly rows?: number;
|
|
66
|
+
/** TERM string, UTF-8 bytes <= maxTerminalTermBytes (default "xterm-256color"). */
|
|
67
|
+
readonly term?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Bounded resize dimensions for an interactive PTY session. */
|
|
71
|
+
export interface ProcessTerminalResize {
|
|
72
|
+
readonly columns: number;
|
|
73
|
+
readonly rows: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Host PTY handle (mirrors ProcessSandboxHandle) plus optional bounded resize
|
|
78
|
+
* and backend metadata. Capability is explicit: the session handle exposes
|
|
79
|
+
* `resize` only when the backend declared `capabilities.resize`.
|
|
80
|
+
*/
|
|
81
|
+
export interface ProcessPtyHandle {
|
|
82
|
+
write(data: Uint8Array): Promise<void>;
|
|
83
|
+
signal(name: string): Promise<void>;
|
|
84
|
+
kill(): Promise<void>;
|
|
85
|
+
release(): Promise<void>;
|
|
86
|
+
resize?(dimensions: ProcessTerminalResize): Promise<void>;
|
|
87
|
+
/** Bounded host metadata (UTF-8 JSON bytes <= maxPtyBackendMetadataBytes), surfaced via session metadata(). */
|
|
88
|
+
readonly metadata?: Readonly<Record<string, string>>;
|
|
89
|
+
/** Opaque non-secret reattachment ref for durable process recovery (plan 026 Task 5). */
|
|
90
|
+
readonly ref?: string;
|
|
91
|
+
wait(options?: { timeoutMs?: number; signal?: AbortSignal }): Promise<{ exitCode: number | null }>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ProcessPtyStartRequest {
|
|
95
|
+
readonly file: string;
|
|
96
|
+
readonly args: readonly string[];
|
|
97
|
+
readonly cwd: string;
|
|
98
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
99
|
+
readonly columns: number;
|
|
100
|
+
readonly rows: number;
|
|
101
|
+
readonly term: string;
|
|
102
|
+
readonly onData?: (data: Buffer) => void;
|
|
103
|
+
readonly signal?: AbortSignal;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Host-selected PTY backend. Capability and metadata are explicit, never
|
|
108
|
+
* duck-typed: `pty: true` requires a backend whose `startPty` is present
|
|
109
|
+
* (otherwise `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before process creation) and
|
|
110
|
+
* `capabilities.resize` must be true for the session handle to expose resize.
|
|
111
|
+
*/
|
|
112
|
+
export interface ProcessPtyBackend {
|
|
113
|
+
startPty?(request: ProcessPtyStartRequest): Promise<ProcessPtyHandle>;
|
|
114
|
+
readonly capabilities?: { readonly resize: boolean };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type ProcessSessionState = "starting" | "running" | "exited" | "killed" | "released" | "expired" | "unknown";
|
|
118
|
+
|
|
119
|
+
export type ProcessSessionErrorCode =
|
|
120
|
+
| "ERR_PRISM_PROCESS_POLICY"
|
|
121
|
+
| "ERR_PRISM_PROCESS_OWNERSHIP"
|
|
122
|
+
| "ERR_PRISM_PROCESS_STATE"
|
|
123
|
+
| "ERR_PRISM_PROCESS_LIMIT"
|
|
124
|
+
| "ERR_PRISM_PROCESS_PTY_UNSUPPORTED"
|
|
125
|
+
| "ERR_PRISM_PROCESS_PTY_BACKEND"
|
|
126
|
+
| "ERR_PRISM_PROCESS_PTY_LIMIT"
|
|
127
|
+
| "ERR_PRISM_PROCESS_UNSUPPORTED";
|
|
128
|
+
|
|
129
|
+
export class ProcessSessionError extends Error {
|
|
130
|
+
readonly code: ProcessSessionErrorCode;
|
|
131
|
+
constructor(code: ProcessSessionErrorCode, message: string) {
|
|
132
|
+
super(message);
|
|
133
|
+
this.name = "ProcessSessionError";
|
|
134
|
+
this.code = code;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface ProcessSessionLimits {
|
|
139
|
+
readonly maxSessions?: number;
|
|
140
|
+
readonly maxInputBytes?: number;
|
|
141
|
+
readonly maxLifetimeMs?: number;
|
|
142
|
+
readonly maxOutputChunkBytes?: number;
|
|
143
|
+
readonly maxTotalOutputBytes?: number;
|
|
144
|
+
/** Phase 26: PTY terminal bounds (defaults/hard caps frozen in limits.ts). */
|
|
145
|
+
readonly maxTerminalColumns?: number;
|
|
146
|
+
readonly maxTerminalRows?: number;
|
|
147
|
+
readonly maxTerminalTermBytes?: number;
|
|
148
|
+
readonly maxTerminalResizesPerMinute?: number;
|
|
149
|
+
readonly maxPtyAttachTimeoutMs?: number;
|
|
150
|
+
readonly maxPtyBackendMetadataBytes?: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface ResolvedProcessSessionLimits {
|
|
154
|
+
readonly maxSessions: number;
|
|
155
|
+
readonly maxInputBytes: number;
|
|
156
|
+
readonly maxLifetimeMs: number;
|
|
157
|
+
readonly maxOutputChunkBytes: number;
|
|
158
|
+
readonly maxTotalOutputBytes: number;
|
|
159
|
+
readonly maxTerminalColumns: number;
|
|
160
|
+
readonly maxTerminalRows: number;
|
|
161
|
+
readonly maxTerminalTermBytes: number;
|
|
162
|
+
readonly maxTerminalResizesPerMinute: number;
|
|
163
|
+
readonly maxPtyAttachTimeoutMs: number;
|
|
164
|
+
readonly maxPtyBackendMetadataBytes: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function resolveProcessSessionLimits(limits?: ProcessSessionLimits): ResolvedProcessSessionLimits {
|
|
168
|
+
return {
|
|
169
|
+
maxSessions: validateCodingLimit("maxSessions", limits?.maxSessions ?? DEFAULT_MAX_PROCESS_SESSIONS, HARD_MAX_PROCESS_SESSIONS),
|
|
170
|
+
maxInputBytes: validateCodingLimit(
|
|
171
|
+
"maxInputBytes",
|
|
172
|
+
limits?.maxInputBytes ?? DEFAULT_MAX_PROCESS_INPUT_BYTES,
|
|
173
|
+
HARD_MAX_PROCESS_INPUT_BYTES,
|
|
174
|
+
),
|
|
175
|
+
maxLifetimeMs: validateCodingLimit(
|
|
176
|
+
"maxLifetimeMs",
|
|
177
|
+
limits?.maxLifetimeMs ?? DEFAULT_MAX_PROCESS_LIFETIME_MS,
|
|
178
|
+
HARD_MAX_PROCESS_LIFETIME_MS,
|
|
179
|
+
),
|
|
180
|
+
maxOutputChunkBytes: validateCodingLimit(
|
|
181
|
+
"maxOutputChunkBytes",
|
|
182
|
+
limits?.maxOutputChunkBytes ?? DEFAULT_MAX_PROCESS_OUTPUT_CHUNK_BYTES,
|
|
183
|
+
HARD_MAX_PROCESS_OUTPUT_CHUNK_BYTES,
|
|
184
|
+
),
|
|
185
|
+
maxTotalOutputBytes: validateCodingLimit(
|
|
186
|
+
"maxTotalOutputBytes",
|
|
187
|
+
limits?.maxTotalOutputBytes ?? DEFAULT_MAX_PROCESS_TOTAL_OUTPUT_BYTES,
|
|
188
|
+
HARD_MAX_PROCESS_TOTAL_OUTPUT_BYTES,
|
|
189
|
+
),
|
|
190
|
+
maxTerminalColumns: validateCodingLimit(
|
|
191
|
+
"maxTerminalColumns",
|
|
192
|
+
limits?.maxTerminalColumns ?? DEFAULT_MAX_TERMINAL_COLUMNS,
|
|
193
|
+
HARD_MAX_TERMINAL_COLUMNS,
|
|
194
|
+
),
|
|
195
|
+
maxTerminalRows: validateCodingLimit("maxTerminalRows", limits?.maxTerminalRows ?? DEFAULT_MAX_TERMINAL_ROWS, HARD_MAX_TERMINAL_ROWS),
|
|
196
|
+
maxTerminalTermBytes: validateCodingLimit(
|
|
197
|
+
"maxTerminalTermBytes",
|
|
198
|
+
limits?.maxTerminalTermBytes ?? DEFAULT_MAX_TERMINAL_TERM_BYTES,
|
|
199
|
+
HARD_MAX_TERMINAL_TERM_BYTES,
|
|
200
|
+
),
|
|
201
|
+
maxTerminalResizesPerMinute: validateCodingLimit(
|
|
202
|
+
"maxTerminalResizesPerMinute",
|
|
203
|
+
limits?.maxTerminalResizesPerMinute ?? DEFAULT_MAX_TERMINAL_RESIZES_PER_MINUTE,
|
|
204
|
+
HARD_MAX_TERMINAL_RESIZES_PER_MINUTE,
|
|
205
|
+
),
|
|
206
|
+
maxPtyAttachTimeoutMs: validateCodingLimit(
|
|
207
|
+
"maxPtyAttachTimeoutMs",
|
|
208
|
+
limits?.maxPtyAttachTimeoutMs ?? DEFAULT_MAX_PTY_ATTACH_TIMEOUT_MS,
|
|
209
|
+
HARD_MAX_PTY_ATTACH_TIMEOUT_MS,
|
|
210
|
+
),
|
|
211
|
+
maxPtyBackendMetadataBytes: validateCodingLimit(
|
|
212
|
+
"maxPtyBackendMetadataBytes",
|
|
213
|
+
limits?.maxPtyBackendMetadataBytes ?? DEFAULT_MAX_PTY_BACKEND_METADATA_BYTES,
|
|
214
|
+
HARD_MAX_PTY_BACKEND_METADATA_BYTES,
|
|
215
|
+
),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface ProcessStartRequest {
|
|
220
|
+
readonly command: string;
|
|
221
|
+
readonly args?: readonly string[];
|
|
222
|
+
readonly cwd?: string;
|
|
223
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
224
|
+
/**
|
|
225
|
+
* Default false. true without a host ptyBackend.startPty →
|
|
226
|
+
* ERR_PRISM_PROCESS_PTY_UNSUPPORTED before process creation; backend
|
|
227
|
+
* failures → ERR_PRISM_PROCESS_PTY_BACKEND; terminal/resize/attach bounds →
|
|
228
|
+
* ERR_PRISM_PROCESS_PTY_LIMIT.
|
|
229
|
+
*/
|
|
230
|
+
readonly pty?: boolean;
|
|
231
|
+
/** Bounded terminal geometry + TERM for pty: true sessions. */
|
|
232
|
+
readonly terminal?: ProcessTerminalRequest;
|
|
233
|
+
readonly lifetimeMs?: number;
|
|
234
|
+
/** Owner string for this session; defaults to registry ownership key. */
|
|
235
|
+
readonly owner?: string;
|
|
236
|
+
/** When true, run cancel releases instead of killing (default kill). */
|
|
237
|
+
readonly releaseOnCancel?: boolean;
|
|
238
|
+
/** Abortable start: an aborted signal fails the start before spawn persists. */
|
|
239
|
+
readonly signal?: AbortSignal;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface ProcessOutputChunk {
|
|
243
|
+
readonly data: string;
|
|
244
|
+
/** Next byte cursor for paging. */
|
|
245
|
+
readonly cursor: number;
|
|
246
|
+
readonly eof: boolean;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface ProcessExitResult {
|
|
250
|
+
readonly exitCode: number | null;
|
|
251
|
+
readonly state: ProcessSessionState;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface ProcessSessionMetadata {
|
|
255
|
+
readonly id: string;
|
|
256
|
+
readonly commandFingerprint: string;
|
|
257
|
+
readonly owner: string;
|
|
258
|
+
readonly workspace: string;
|
|
259
|
+
readonly policyDecision: string;
|
|
260
|
+
readonly startedAt: string;
|
|
261
|
+
readonly exitedAt?: string;
|
|
262
|
+
readonly state: ProcessSessionState;
|
|
263
|
+
readonly releaseOnCancel: boolean;
|
|
264
|
+
/** True when the session runs through the host ptyBackend. */
|
|
265
|
+
readonly pty: boolean;
|
|
266
|
+
/** Resolved terminal geometry + TERM for PTY sessions (bounded at start). */
|
|
267
|
+
readonly terminal?: { readonly columns: number; readonly rows: number; readonly term: string };
|
|
268
|
+
/** Bounded backend metadata (validated <= maxPtyBackendMetadataBytes at start). */
|
|
269
|
+
readonly ptyBackendMetadata?: Readonly<Record<string, string>>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface ProcessSession {
|
|
273
|
+
readonly id: string;
|
|
274
|
+
readonly state: ProcessSessionState;
|
|
275
|
+
readonly owner: string;
|
|
276
|
+
metadata(): ProcessSessionMetadata;
|
|
277
|
+
output(request?: { cursor?: number; maxBytes?: number }): Promise<ProcessOutputChunk>;
|
|
278
|
+
/**
|
|
279
|
+
* Fires when new output is published (a chunk was appended) or the session reached a terminal
|
|
280
|
+
* state, so a reader waits instead of polling. The returned function unsubscribes; a listener that
|
|
281
|
+
* throws never fails the process that produced the output.
|
|
282
|
+
*/
|
|
283
|
+
onOutput(listener: () => void): () => void;
|
|
284
|
+
input(data: string | Uint8Array): Promise<void>;
|
|
285
|
+
wait(options?: { timeoutMs?: number; signal?: AbortSignal }): Promise<ProcessExitResult>;
|
|
286
|
+
signal(name: "SIGTERM" | "SIGINT" | "SIGHUP"): Promise<void>;
|
|
287
|
+
kill(): Promise<void>;
|
|
288
|
+
release(): Promise<void>;
|
|
289
|
+
/** Present only when the host backend declared `capabilities.resize` (PTY sessions). */
|
|
290
|
+
resize?(dimensions: ProcessTerminalResize): Promise<void>;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export type CodingProcessEvent = {
|
|
294
|
+
readonly type: "process_started" | "process_exited" | "process_killed" | "process_released" | "process_expired" | "process_unknown";
|
|
295
|
+
readonly sessionId: string;
|
|
296
|
+
readonly processId: string;
|
|
297
|
+
readonly owner: string;
|
|
298
|
+
readonly exitCode?: number | null;
|
|
299
|
+
readonly at: string;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export interface ProcessSessions {
|
|
303
|
+
start(request: ProcessStartRequest): Promise<ProcessSession>;
|
|
304
|
+
get(sessionId: string, owner?: string): ProcessSession;
|
|
305
|
+
/** Kill (default) or release all sessions for `owner`. */
|
|
306
|
+
cancelOwned(owner: string, options?: { release?: boolean }): Promise<void>;
|
|
307
|
+
/** Mark a running session unknown (backend loss); never fabricates exitCode. */
|
|
308
|
+
markUnknown(sessionId: string, owner?: string): Promise<void>;
|
|
309
|
+
/**
|
|
310
|
+
* Host resume / sandbox-loss reconciliation: mark every running/starting session unknown.
|
|
311
|
+
* Never fabricates exitCode. O(owned sessions).
|
|
312
|
+
*/
|
|
313
|
+
reconcile(): Promise<{ readonly markedUnknown: number }>;
|
|
314
|
+
/**
|
|
315
|
+
* Durable process recovery (plan 026 Task 5): reconcile durable recovery
|
|
316
|
+
* records against the live registry — attach-if-attested via the host
|
|
317
|
+
* recovery backend, otherwise `starting|running` records atomically become
|
|
318
|
+
* `unknown`. Never fabricates an exit code and never probes PIDs. Requires
|
|
319
|
+
* `checkpoints` + `leases` + `ownerId` at construction, else
|
|
320
|
+
* ERR_PRISM_RECOVERY_UNSUPPORTED. O(owned records).
|
|
321
|
+
*/
|
|
322
|
+
recover(options?: { signal?: AbortSignal }): Promise<import("./recovery.js").ProcessRecoveryReport>;
|
|
323
|
+
dispose(): Promise<void>;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface CreateProcessSessionsOptions {
|
|
327
|
+
readonly cwd: string;
|
|
328
|
+
readonly policy?: ExecutionPolicy;
|
|
329
|
+
readonly limits?: ProcessSessionLimits;
|
|
330
|
+
readonly onEvent?: (event: CodingProcessEvent) => void;
|
|
331
|
+
readonly ownership?: OwnershipScope;
|
|
332
|
+
/** Host-verified identity; projects onto default owner when ownership omitted. */
|
|
333
|
+
readonly identity?: AgentIdentity;
|
|
334
|
+
/**
|
|
335
|
+
* When set: use `startProcess` if present; otherwise start() fails closed
|
|
336
|
+
* (`ERR_PRISM_PROCESS_UNSUPPORTED`). Native spawn only when sandbox omitted.
|
|
337
|
+
*/
|
|
338
|
+
readonly sandbox?: ProcessSandboxBackend;
|
|
339
|
+
/**
|
|
340
|
+
* Phase 26: optional host-selected PTY backend. `pty: true` delegates only
|
|
341
|
+
* to this backend; absent backend or missing startPty fails closed with
|
|
342
|
+
* `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before process creation.
|
|
343
|
+
*/
|
|
344
|
+
readonly ptyBackend?: ProcessPtyBackend;
|
|
345
|
+
/**
|
|
346
|
+
* Phase 26 durable process recovery: intent is persisted before spawn and
|
|
347
|
+
* lifecycle transitions are CAS-written under LeaseStore fencing. All three
|
|
348
|
+
* of `checkpoints`, `leases`, and `ownerId` must be present together — a
|
|
349
|
+
* partial recovery configuration fails closed at construction. `recover()`
|
|
350
|
+
* is attach-if-attested via `recoveryBackend`; otherwise starting/running
|
|
351
|
+
* records atomically become unknown (no fabricated exit, no PID probing).
|
|
352
|
+
*/
|
|
353
|
+
readonly checkpoints?: CheckpointStore;
|
|
354
|
+
readonly leases?: LeaseStore;
|
|
355
|
+
readonly ownerId?: string;
|
|
356
|
+
readonly recoveryBackend?: import("./recovery.js").ProcessRecoveryBackend;
|
|
357
|
+
readonly recoveryLimits?: import("./recovery.js").ProcessRecoveryLimits;
|
|
358
|
+
}
|