@sema-agent/core 7.51.2 → 7.52.0
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 +504 -2
- package/dist/agents/agent-transcript-tool.d.ts +1 -1
- package/dist/agents/cascade.js +4 -0
- package/dist/agents/cumulative-stats.d.ts +3 -0
- package/dist/agents/cumulative-stats.js +3 -0
- package/dist/agents/list-agents-tool.d.ts +1 -1
- package/dist/agents/read-inbox-tool.d.ts +1 -1
- package/dist/agents/repair-loop.js +4 -0
- package/dist/agents/send-message-tool.d.ts +11 -16
- package/dist/agents/send-message-tool.js +11 -13
- package/dist/agents/subagent.js +6 -4
- package/dist/agents/tool-filter.d.ts +40 -13
- package/dist/agents/tool-filter.js +28 -14
- package/dist/bench/metrics.d.ts +1 -0
- package/dist/brain/anthropic.js +16 -12
- package/dist/brain/model-presets.d.ts +4 -5
- package/dist/brain/model-presets.js +4 -4
- package/dist/brain/open-responses.js +16 -12
- package/dist/brain/openai.js +16 -12
- package/dist/brain/request-params.d.ts +12 -33
- package/dist/brain/request-params.js +5 -2
- package/dist/brain/request-route.d.ts +9 -8
- package/dist/brain/request-route.js +1 -0
- package/dist/brain/route-adjudicator.d.ts +117 -47
- package/dist/brain/route-adjudicator.js +171 -42
- package/dist/brain/route-conformance.js +2 -2
- package/dist/brain/status-sink.d.ts +18 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/core/allow-entry.d.ts +40 -8
- package/dist/core/allow-entry.js +23 -16
- package/dist/core/auto-compaction.d.ts +23 -20
- package/dist/core/auto-compaction.js +37 -16
- package/dist/core/auto-promote.d.ts +2 -1
- package/dist/core/auto-promote.js +2 -2
- package/dist/core/caller-tool-name.d.ts +9 -0
- package/dist/core/caller-tool-name.js +13 -0
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +36 -12
- package/dist/core/checkpoint-store.js +1 -1
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +2 -1
- package/dist/core/credential-vocabulary.d.ts +33 -8
- package/dist/core/credential-vocabulary.js +32 -7
- package/dist/core/decision-receipt.d.ts +2 -2
- package/dist/core/delegation-frames.d.ts +9 -2
- package/dist/core/delegation-name.d.ts +18 -0
- package/dist/core/delegation-name.js +47 -0
- package/dist/core/engine-capabilities.d.ts +8 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +36 -19
- package/dist/core/frontmatter-fence.d.ts +10 -0
- package/dist/core/frontmatter-fence.js +6 -0
- package/dist/core/gate-fold.js +5 -23
- package/dist/core/gate-lanes.js +356 -270
- package/dist/core/governance-codes.d.ts +9 -4
- package/dist/core/governance-codes.js +9 -3
- package/dist/core/hooks.d.ts +276 -734
- package/dist/core/hooks.js +97 -192
- package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
- package/dist/{tools/fs → core}/image-admission.js +24 -6
- package/dist/core/mcp-connection-pool.d.ts +4 -1
- package/dist/core/mcp-connection-pool.js +7 -2
- package/dist/core/mcp-probe.d.ts +12 -9
- package/dist/core/mcp-probe.js +2 -2
- package/dist/core/mcp-server-spec.d.ts +16 -0
- package/dist/core/mcp.d.ts +11 -7
- package/dist/core/mcp.js +11 -5
- package/dist/core/memory-engine/data-plane.js +5 -1
- package/dist/core/memory-engine/distiller.js +16 -2
- package/dist/core/memory-engine/engine.d.ts +8 -11
- package/dist/core/memory-engine/engine.js +9 -5
- package/dist/core/memory-engine/frontmatter.d.ts +6 -0
- package/dist/core/memory-engine/frontmatter.js +19 -16
- package/dist/core/memory.d.ts +2 -2
- package/dist/core/mode-policy.d.ts +4 -0
- package/dist/core/mode-policy.js +7 -5
- package/dist/core/model-seat.d.ts +4 -1
- package/dist/core/mods/dollar.d.ts +109 -0
- package/dist/core/mods/dollar.js +154 -0
- package/dist/core/mods/host.d.ts +23 -0
- package/dist/core/mods/host.js +21 -0
- package/dist/core/mods/kernel/budget.d.ts +43 -0
- package/dist/core/mods/kernel/budget.js +83 -0
- package/dist/core/mods/kernel/dispatch.d.ts +100 -0
- package/dist/core/mods/kernel/dispatch.js +701 -0
- package/dist/core/mods/kernel/events.d.ts +242 -0
- package/dist/core/mods/kernel/events.js +210 -0
- package/dist/core/mods/kernel/index.d.ts +6 -0
- package/dist/core/mods/kernel/index.js +5 -0
- package/dist/core/mods/kernel/receive.d.ts +65 -0
- package/dist/core/mods/kernel/receive.js +123 -0
- package/dist/core/mods/kernel/registration.d.ts +131 -0
- package/dist/core/mods/kernel/registration.js +102 -0
- package/dist/core/mods/kernel/site-rules.d.ts +84 -0
- package/dist/core/mods/kernel/site-rules.js +104 -0
- package/dist/core/mods/stations.d.ts +350 -0
- package/dist/core/mods/stations.js +444 -0
- package/dist/core/own-property-receipt.js +11 -23
- package/dist/core/parent-spec-seat.d.ts +18 -7
- package/dist/core/parent-spec-seat.js +6 -2
- package/dist/core/path-identity.js +2 -1
- package/dist/core/permission-rule-syntax.d.ts +15 -0
- package/dist/core/permission-rule-syntax.js +24 -16
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +26 -15
- package/dist/core/prompt-section-seat.d.ts +15 -27
- package/dist/core/prompt-section-seat.js +73 -40
- package/dist/core/protocol-http-auth.d.ts +3 -1
- package/dist/core/protocol-http-auth.js +2 -0
- package/dist/core/reminder-mint.d.ts +2 -2
- package/dist/core/remote-env.d.ts +24 -12
- package/dist/core/remote-env.js +1 -0
- package/dist/core/resume-startup-cause.d.ts +16 -1
- package/dist/core/resume-startup-cause.js +4 -1
- package/dist/core/retired-keys.d.ts +4 -0
- package/dist/core/retired-keys.js +3 -0
- package/dist/core/runner/announce-once-ledger.d.ts +31 -1
- package/dist/core/runner/announce-once-ledger.js +22 -0
- package/dist/core/runner/assemble-result.d.ts +9 -0
- package/dist/core/runner/assemble-result.js +3 -1
- package/dist/core/runner/call-hook-stations.d.ts +23 -27
- package/dist/core/runner/call-hook-stations.js +58 -42
- package/dist/core/runner/caller-stop-gate.d.ts +25 -0
- package/dist/core/runner/caller-stop-gate.js +7 -0
- package/dist/core/runner/compaction-seams.d.ts +8 -22
- package/dist/core/runner/compaction-seams.js +28 -53
- package/dist/core/runner/construction-doors.js +11 -1
- package/dist/core/runner/contracts.d.ts +85 -83
- package/dist/core/runner/credential-origin-seat.d.ts +2 -0
- package/dist/core/runner/credential-origin-seat.js +19 -0
- package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
- package/dist/core/runner/gate-exit.d.ts +47 -36
- package/dist/core/runner/gate-exit.js +8 -2
- package/dist/core/runner/image.js +1 -2
- package/dist/core/runner/inline-image.js +1 -1
- package/dist/core/runner/notice-seat-input.d.ts +11 -0
- package/dist/core/runner/notice-seat-input.js +1 -0
- package/dist/core/runner/person-path-rows.js +1 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
- package/dist/core/runner/prepare-ask-lane.js +6 -15
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
- package/dist/core/runner/prepare-config-doors.d.ts +5 -0
- package/dist/core/runner/prepare-config-doors.js +9 -0
- package/dist/core/runner/prepare-defer-classify.js +3 -4
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
- package/dist/core/runner/prepare-gate-stations.js +11 -10
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
- package/dist/core/runner/prepare-inherited-gate.js +8 -21
- package/dist/core/runner/prepare-memory-engine-session.js +1 -1
- package/dist/core/runner/prepare-park-ask.js +10 -14
- package/dist/core/runner/prepare-policy-chain.d.ts +17 -15
- package/dist/core/runner/prepare-policy-chain.js +95 -100
- package/dist/core/runner/prepare-project-context.js +7 -10
- package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
- package/dist/core/runner/prepare-prompt-assembly.js +134 -101
- package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
- package/dist/core/runner/prepare-prompt-inputs.js +5 -7
- package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
- package/dist/core/runner/prepare-protocol-tools.js +2 -2
- package/dist/core/runner/prepare-safety-scan.js +6 -10
- package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
- package/dist/core/runner/prepare-suspend-saga.js +7 -5
- package/dist/core/runner/prepare-task.js +33 -15
- package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
- package/dist/core/runner/prepare-turn-wiring.js +29 -27
- package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
- package/dist/core/runner/prepare-wiring-manifest.js +7 -7
- package/dist/core/runner/resume-apply.d.ts +5 -2
- package/dist/core/runner/resume-apply.js +24 -42
- package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
- package/dist/core/runner/resume-checkpoint-screen.js +9 -1
- package/dist/core/runner/resume-preflight.js +38 -16
- package/dist/core/runner/retired-runner-deps.js +3 -1
- package/dist/core/runner/run-brain-sinks.d.ts +3 -2
- package/dist/core/runner/run-brain-sinks.js +5 -1
- package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
- package/dist/core/runner/run-compaction-machinery.js +7 -33
- package/dist/core/runner/run-harness-handlers.js +4 -2
- package/dist/core/runner/run-identity-wiring.d.ts +3 -1
- package/dist/core/runner/run-identity-wiring.js +3 -2
- package/dist/core/runner/run-leg.js +62 -84
- package/dist/core/runner/run-notification-lane.js +1 -0
- package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
- package/dist/core/runner/run-recovery-lanes.js +11 -10
- package/dist/core/runner/run-settle-and-teardown.js +9 -28
- package/dist/core/runner/run-stop-and-final-verify.js +18 -25
- package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
- package/dist/core/runner/run-terminal-adoption.js +33 -22
- package/dist/core/runner/run-tool-mount-facts.js +5 -3
- package/dist/core/runner/run-turn-boundary.js +23 -34
- package/dist/core/runner/runtask.d.ts +6 -7
- package/dist/core/runner/runtask.js +14 -72
- package/dist/core/runner/session-rule-policy.d.ts +5 -5
- package/dist/core/runner/session-rule-policy.js +13 -8
- package/dist/core/runner/side-call-account.d.ts +68 -0
- package/dist/core/runner/side-call-account.js +61 -0
- package/dist/core/runner/stream-halt-verbs.js +12 -17
- package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
- package/dist/core/runner/stream-steer-verb.d.ts +1 -1
- package/dist/core/runner/stream-steer-verb.js +20 -38
- package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
- package/dist/core/runner/sub-agent-deny-lists.js +228 -71
- package/dist/core/runner/tool-end-body.d.ts +5 -6
- package/dist/core/runner/tool-end-body.js +6 -4
- package/dist/core/runner/tool-not-executed.d.ts +15 -0
- package/dist/core/runner/tool-not-executed.js +30 -0
- package/dist/core/runner/tool-occurrence.d.ts +8 -7
- package/dist/core/runner/tool-occurrence.js +5 -5
- package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
- package/dist/core/runner-deps.d.ts +20 -28
- package/dist/core/safety-merge-corpus.js +18 -2
- package/dist/core/secret-env.d.ts +88 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +52 -16
- package/dist/core/session-policy-store.js +118 -28
- package/dist/core/session-reconcile.d.ts +4 -0
- package/dist/core/session-reconcile.js +2 -1
- package/dist/core/shell-read-silence.js +3 -32
- package/dist/core/side-query.d.ts +5 -3
- package/dist/core/skill-grant.d.ts +3 -2
- package/dist/core/skill-grant.js +17 -9
- package/dist/core/skills-directory.d.ts +31 -11
- package/dist/core/skills-directory.js +655 -104
- package/dist/core/task-event.d.ts +13 -3
- package/dist/core/task-result.d.ts +8 -0
- package/dist/core/task-spec.d.ts +20 -19
- package/dist/core/task-stream.d.ts +14 -4
- package/dist/core/tighten-task-spec.d.ts +4 -3
- package/dist/core/tighten-task-spec.js +2 -4
- package/dist/core/tool-check-reading.d.ts +74 -0
- package/dist/core/tool-check-reading.js +80 -0
- package/dist/core/tool-policy.d.ts +59 -18
- package/dist/core/tool-policy.js +77 -12
- package/dist/core/tool-registry.d.ts +20 -0
- package/dist/core/tool-registry.js +4 -0
- package/dist/core/tool-spec.d.ts +10 -6
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +51 -32
- package/dist/core/unc-spelling.d.ts +10 -0
- package/dist/core/unc-spelling.js +3 -0
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-egress.js +4 -1
- package/dist/core/user-attachment.d.ts +12 -2
- package/dist/core/user-attachment.js +17 -2
- package/dist/core/wiring-manifest.d.ts +14 -13
- package/dist/core/wiring-manifest.js +2 -2
- package/dist/engine/compaction/utils.js +1 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
- package/dist/engine/execution-env/symlink-chain.js +1 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/engine/harness/agent-harness.js +3 -3
- package/dist/engine/harness/types.d.ts +17 -16
- package/dist/engine/harness/types.js +7 -1
- package/dist/engine/llm/types.d.ts +12 -7
- package/dist/engine/loop/agent-loop.js +1 -1
- package/dist/engine/loop/types.d.ts +1 -1
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/import-validate.js +3 -10
- package/dist/engine/session/session.d.ts +4 -4
- package/dist/engine/session/session.js +4 -4
- package/dist/index.d.ts +20 -16
- package/dist/index.js +13 -11
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/run-spec.d.ts +4 -6
- package/dist/orchestration/run-spec.js +39 -63
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow.d.ts +11 -3
- package/dist/orchestration/workflow.js +11 -2
- package/dist/prompt-assembly/artifact-store.d.ts +37 -56
- package/dist/prompt-assembly/artifact-store.js +31 -49
- package/dist/prompt-assembly/artifact.d.ts +89 -76
- package/dist/prompt-assembly/artifact.js +167 -146
- package/dist/prompt-assembly/assemble.d.ts +34 -29
- package/dist/prompt-assembly/assemble.js +33 -220
- package/dist/prompt-assembly/bands.d.ts +20 -0
- package/dist/prompt-assembly/bands.js +18 -0
- package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
- package/dist/prompt-assembly/catalog/constitution.js +69 -0
- package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
- package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
- package/dist/prompt-assembly/catalog/index.d.ts +11 -0
- package/dist/prompt-assembly/catalog/index.js +59 -0
- package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
- package/dist/prompt-assembly/catalog/modes.js +137 -0
- package/dist/prompt-assembly/catalog/role.d.ts +10 -0
- package/dist/prompt-assembly/catalog/role.js +157 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
- package/dist/prompt-assembly/catalog/schema.js +25 -0
- package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
- package/dist/prompt-assembly/catalog/sections.js +98 -0
- package/dist/prompt-assembly/catalog/team.d.ts +4 -0
- package/dist/prompt-assembly/catalog/team.js +258 -0
- package/dist/prompt-assembly/composer.d.ts +24 -28
- package/dist/prompt-assembly/composer.js +124 -46
- package/dist/prompt-assembly/epoch.d.ts +89 -79
- package/dist/prompt-assembly/epoch.js +115 -142
- package/dist/prompt-assembly/explain.d.ts +58 -34
- package/dist/prompt-assembly/explain.js +21 -24
- package/dist/prompt-assembly/overlay.d.ts +127 -0
- package/dist/prompt-assembly/overlay.js +301 -0
- package/dist/prompt-assembly/renderers.d.ts +21 -0
- package/dist/prompt-assembly/renderers.js +39 -0
- package/dist/prompt-assembly/section-usage.d.ts +3 -3
- package/dist/prompt-assembly/section-usage.js +1 -1
- package/dist/prompt-assembly/select.d.ts +56 -0
- package/dist/prompt-assembly/select.js +69 -0
- package/dist/prompt-assembly/sugar.d.ts +31 -0
- package/dist/prompt-assembly/sugar.js +20 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
- package/dist/prompt-assembly/types.d.ts +45 -148
- package/dist/prompts/coordinator.d.ts +2 -21
- package/dist/prompts/coordinator.js +3 -255
- package/dist/prompts/cowork.d.ts +27 -16
- package/dist/prompts/cowork.js +2 -23
- package/dist/prompts/default.d.ts +38 -500
- package/dist/prompts/default.js +20 -285
- package/dist/prompts/supervisor.d.ts +6 -71
- package/dist/prompts/supervisor.js +6 -117
- package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
- package/dist/tools/fs/dir-entries.d.ts +4 -2
- package/dist/tools/fs/dir-entries.js +16 -4
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +22 -11
- package/dist/tools/fs/fs-pdf.js +17 -13
- package/dist/tools/fs/fs-read.js +4 -3
- package/dist/tools/fs/fs-shared.d.ts +1 -10
- package/dist/tools/fs/fs-shared.js +3 -4
- package/dist/tools/fs/fs-write.js +2 -1
- package/dist/tools/fs/notebook.js +1 -1
- package/dist/tools/fs/pdf.d.ts +20 -10
- package/dist/tools/fs/pdf.js +19 -10
- package/dist/tools/fs/program-read-model.d.ts +68 -8
- package/dist/tools/fs/program-read-model.js +112 -33
- package/dist/tools/fs/program-read-model.rows.js +30 -10
- package/dist/tools/fs/read-deny-home.d.ts +25 -9
- package/dist/tools/fs/read-deny-home.js +28 -27
- package/dist/tools/fs/read-deny.d.ts +41 -11
- package/dist/tools/fs/read-deny.js +51 -5
- package/dist/tools/fs/safety.d.ts +4 -26
- package/dist/tools/fs/safety.js +10 -32
- package/dist/tools/fs/search.js +15 -10
- package/dist/tools/fs/shell-read-argv.d.ts +3 -2
- package/dist/tools/fs/shell-read-argv.js +118 -38
- package/dist/tools/fs/shell-read-establish.d.ts +5 -5
- package/dist/tools/fs/shell-read-establish.js +11 -61
- package/dist/tools/fs/shell-read-identity.js +6 -3
- package/dist/tools/fs/shell-read-judge.d.ts +13 -15
- package/dist/tools/fs/shell-read-judge.js +18 -4
- package/dist/tools/fs/shell-read-plan.d.ts +2 -2
- package/dist/tools/web.js +1 -1
- package/package.json +13 -1
- package/test/export-surface.snapshot.json +242 -99
- package/wire-changes/core@7.52.0.json +5859 -0
- package/dist/core/hook-seats.d.ts +0 -66
- package/dist/core/hook-seats.js +0 -37
- package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
- package/dist/prompt-assembly/packs/sema-default.js +0 -147
- package/dist/prompts/simple-sections.d.ts +0 -58
- package/dist/prompts/simple-sections.js +0 -43
|
@@ -1,25 +1,19 @@
|
|
|
1
|
+
function escapedAt(s, i) {
|
|
2
|
+
let backslashes = 0;
|
|
3
|
+
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--)
|
|
4
|
+
backslashes++;
|
|
5
|
+
return backslashes % 2 === 1;
|
|
6
|
+
}
|
|
1
7
|
export function indexOfUnescaped(s, ch) {
|
|
2
|
-
for (let i = 0; i < s.length; i++)
|
|
3
|
-
if (s[i]
|
|
4
|
-
continue;
|
|
5
|
-
let backslashes = 0;
|
|
6
|
-
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--)
|
|
7
|
-
backslashes++;
|
|
8
|
-
if (backslashes % 2 === 0)
|
|
8
|
+
for (let i = 0; i < s.length; i++)
|
|
9
|
+
if (s[i] === ch && !escapedAt(s, i))
|
|
9
10
|
return i;
|
|
10
|
-
}
|
|
11
11
|
return -1;
|
|
12
12
|
}
|
|
13
13
|
export function lastIndexOfUnescaped(s, ch) {
|
|
14
|
-
for (let i = s.length - 1; i >= 0; i--)
|
|
15
|
-
if (s[i]
|
|
16
|
-
continue;
|
|
17
|
-
let backslashes = 0;
|
|
18
|
-
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--)
|
|
19
|
-
backslashes++;
|
|
20
|
-
if (backslashes % 2 === 0)
|
|
14
|
+
for (let i = s.length - 1; i >= 0; i--)
|
|
15
|
+
if (s[i] === ch && !escapedAt(s, i))
|
|
21
16
|
return i;
|
|
22
|
-
}
|
|
23
17
|
return -1;
|
|
24
18
|
}
|
|
25
19
|
function unescapeRuleContent(s) {
|
|
@@ -42,3 +36,17 @@ export function parsePermissionRule(rule) {
|
|
|
42
36
|
return { toolName: name.trim() };
|
|
43
37
|
return { toolName: name.trim(), ruleContent: unescapeRuleContent(content) };
|
|
44
38
|
}
|
|
39
|
+
const RULE_TOOL_NAME = /^(?:\*|[A-Za-z0-9_](?:[A-Za-z0-9_.:*-]*[A-Za-z0-9_*-])?)$/;
|
|
40
|
+
export function isNameableToolName(name) {
|
|
41
|
+
return typeof name === "string" && name !== "*" && RULE_TOOL_NAME.test(name);
|
|
42
|
+
}
|
|
43
|
+
export function ruleSpellingOf(rule) {
|
|
44
|
+
if (rule !== rule.trim())
|
|
45
|
+
return undefined;
|
|
46
|
+
const open = indexOfUnescaped(rule, "(");
|
|
47
|
+
const name = open < 0 ? rule : rule.slice(0, open);
|
|
48
|
+
if (!RULE_TOOL_NAME.test(name))
|
|
49
|
+
return undefined;
|
|
50
|
+
const parsed = parsePermissionRule(rule);
|
|
51
|
+
return parsed.toolName === name ? parsed : undefined;
|
|
52
|
+
}
|
|
@@ -73,7 +73,13 @@ export interface PermissionRuleIssue {
|
|
|
73
73
|
rule: string;
|
|
74
74
|
/** `invalid.*` = 语法/语义错;`unsupported.*` = CC 合法但 v1 不支持的 lane(独立
|
|
75
75
|
* 分类,绝不静默 no-op —— `Bash(npm run:*)` 被当 generic 参数规则接受后永不匹配是最危险的失败形态)。 */
|
|
76
|
-
code: "invalid.empty_tool" | "invalid.paren"
|
|
76
|
+
code: "invalid.empty_tool" | "invalid.paren"
|
|
77
|
+
/** A rule the rule syntax reads no rule in (`ruleSpellingOf`) whose name is the reason: blanks around the rule, a
|
|
78
|
+
* character outside a tool name's spelling (a variation selector, a filler, a zero-width space), a blank or an invisible
|
|
79
|
+
* character before the `(`, a leading `-`. Refused — the lenient reading would deny a tool under another spelling, or none.
|
|
80
|
+
* Also a rule whose tool name the syntax reads as the wildcard `*` (`*`, `*()`, `*(*)`): a rule reaches the tool it
|
|
81
|
+
* names, and `*` names none. */
|
|
82
|
+
| "invalid.tool_name" | "invalid.param_split" | "invalid.primary_field" | "invalid.cap_exceeded"
|
|
77
83
|
/** An ALLOW rule (any spelling): a policy fold says deny / ask only — "this does not need asking" is the allow
|
|
78
84
|
* layer's (`TaskSpec.allowedTools`, `RunnerDeps.allowedTools`). One issue per allow rule. */
|
|
79
85
|
| "unsupported.allow"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { attest } from "./tool-policy.js";
|
|
2
2
|
import { MCP_NAMESPACE, protocolOf } from "./protocol-table.js";
|
|
3
3
|
import { catalogRuleFaceOf, isShellFaceToolName, pathTargetOf } from "./tool-registry.js";
|
|
4
|
-
import { indexOfUnescaped, lastIndexOfUnescaped, parsePermissionRule } from "./permission-rule-syntax.js";
|
|
4
|
+
import { indexOfUnescaped, lastIndexOfUnescaped, parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
5
5
|
import { COMMAND_RULE_TOOL, NAME_RULE_FACES, PATH_RULE_BASE_LABEL, PATH_RULE_FACES, callPathFaceOf, isUsablePathBase, laneToolNamesOf, nameRuleAdmits, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleBasesNeeded, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
6
6
|
import { pathFamilyOf } from "../tools/fs/safety.js";
|
|
7
7
|
import { callTargetReadingOf, callTargetViewsOf } from "./effective-path-target.js";
|
|
@@ -50,6 +50,25 @@ function countStars(s) {
|
|
|
50
50
|
n++;
|
|
51
51
|
return n;
|
|
52
52
|
}
|
|
53
|
+
function unreadableRuleIssue(text) {
|
|
54
|
+
const lenient = parsePermissionRule(text);
|
|
55
|
+
if (!lenient.toolName)
|
|
56
|
+
return { code: "invalid.empty_tool", message: "tool name cannot be empty" };
|
|
57
|
+
if (lenient.ruleContent === undefined && /[()\\]/.test(lenient.toolName)) {
|
|
58
|
+
return { code: "invalid.paren", message: "tool name still contains paren/escape characters — a mis-escaped rule would compile to a dead bare rule" };
|
|
59
|
+
}
|
|
60
|
+
const open = indexOfUnescaped(text, "(");
|
|
61
|
+
if (lenient.ruleContent === undefined && open !== -1) {
|
|
62
|
+
const close = lastIndexOfUnescaped(text, ")");
|
|
63
|
+
const legitimateBare = close === text.length - 1 && ["", "*"].includes(text.substring(open + 1, close));
|
|
64
|
+
if (!legitimateBare)
|
|
65
|
+
return { code: "invalid.paren", message: "malformed parentheses / trailing characters (would silently downgrade to a bare-tool rule)" };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
code: "invalid.tool_name",
|
|
69
|
+
message: `the tool name is a spelling no tool name can have — the rule syntax reads no rule in it (blanks around the rule, a character outside a tool name's spelling, or one before the "("), so the rule would name no tool; fix the entry`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
53
72
|
function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
|
|
54
73
|
const faceOf = (toolName) => {
|
|
55
74
|
if (ruleFaces !== undefined) {
|
|
@@ -84,24 +103,16 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
|
|
|
84
103
|
bad(text, "invalid.cap_exceeded", `rule longer than ${caps.maxRuleLength} chars`);
|
|
85
104
|
continue;
|
|
86
105
|
}
|
|
87
|
-
const parsed =
|
|
88
|
-
if (
|
|
89
|
-
|
|
106
|
+
const parsed = ruleSpellingOf(text);
|
|
107
|
+
if (parsed === undefined) {
|
|
108
|
+
const why = unreadableRuleIssue(text);
|
|
109
|
+
bad(text, why.code, why.message);
|
|
90
110
|
continue;
|
|
91
111
|
}
|
|
92
|
-
if (parsed.
|
|
93
|
-
bad(text, "invalid.
|
|
112
|
+
if (parsed.toolName === "*") {
|
|
113
|
+
bad(text, "invalid.tool_name", `\`*\` names no tool — a rule reaches the tool it names, so this one would compile and never match; name the tool, or state the answer for every call no rule reaches with \`defaultAction\``);
|
|
94
114
|
continue;
|
|
95
115
|
}
|
|
96
|
-
if (parsed.ruleContent === undefined && indexOfUnescaped(text, "(") !== -1) {
|
|
97
|
-
const open = indexOfUnescaped(text, "(");
|
|
98
|
-
const close = lastIndexOfUnescaped(text, ")");
|
|
99
|
-
const legitimateBare = close === text.length - 1 && ["", "*"].includes(text.substring(open + 1, close));
|
|
100
|
-
if (!legitimateBare) {
|
|
101
|
-
bad(text, "invalid.paren", "malformed parentheses / trailing characters (would silently downgrade to a bare-tool rule)");
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
116
|
const toolName = parsed.toolName;
|
|
106
117
|
const parenthesised = parsed.ruleContent !== undefined || indexOfUnescaped(text, "(") !== -1;
|
|
107
118
|
if (toolName.startsWith(MCP_NAMESPACE.prefix) && parenthesised) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type { LegModBus } from "./mods/stations.js";
|
|
2
|
+
/** The named sections whose entries the host may dispose of — a closed set (the event's `name`). */
|
|
2
3
|
export declare const DISPOSABLE_PROMPT_SECTIONS: readonly ["core/discovery.mcp-instructions"];
|
|
3
4
|
export type DisposablePromptSection = (typeof DISPOSABLE_PROMPT_SECTIONS)[number];
|
|
4
|
-
/** One entry of a named section, as the engine computed it, before it reaches the model.
|
|
5
|
-
* writing to it changes nothing. */
|
|
5
|
+
/** One entry of a named section, as the engine computed it, before it reaches the model. */
|
|
6
6
|
export interface PromptSectionEntry {
|
|
7
7
|
readonly section: DisposablePromptSection;
|
|
8
8
|
/** The entry's key within the section: the MCP server's declared name. */
|
|
@@ -12,35 +12,16 @@ export interface PromptSectionEntry {
|
|
|
12
12
|
/** The tools this leg mounted from that server, by their namespaced names. */
|
|
13
13
|
readonly mountedTools: readonly string[];
|
|
14
14
|
}
|
|
15
|
-
/** The host's answer. `undefined` = include the entry as computed. */
|
|
16
|
-
export type PromptSectionDisposition = {
|
|
17
|
-
readonly op: "drop";
|
|
18
|
-
} | {
|
|
19
|
-
readonly op: "replace";
|
|
20
|
-
readonly text: string;
|
|
21
|
-
};
|
|
22
15
|
/** Who wrote the text an entry carries into the model's view. */
|
|
23
16
|
export type PromptSectionAuthor = "server" | "host";
|
|
24
|
-
export interface PromptSectionSeat {
|
|
25
|
-
/**
|
|
26
|
-
* Answer the disposition of one entry of a named section ({@link DISPOSABLE_PROMPT_SECTIONS}): return
|
|
27
|
-
* nothing to include it, `{ op: "drop" }` to leave it out of the model's view on every lane, or
|
|
28
|
-
* `{ op: "replace", text }` to place the host's own non-blank text instead — host text, rendered
|
|
29
|
-
* without the untrusted-data fence the server's text rides in, under the same per-entry bound and the same
|
|
30
|
-
* disclosed cut. Called once each time the entry's text
|
|
31
|
-
* enters the run (a re-dial asks again). Synchronous. A throw, a Promise, or any other value includes
|
|
32
|
-
* the entry and raises a coded operator warning (`prompt.section_answer_invalid`).
|
|
33
|
-
*/
|
|
34
|
-
section?(entry: PromptSectionEntry): PromptSectionDisposition | undefined;
|
|
35
|
-
}
|
|
36
17
|
/** One server's entry of the MCP instructions section as the run carries it, after the host's disposition. */
|
|
37
18
|
export interface McpInstructionsEntry {
|
|
38
19
|
readonly server: string;
|
|
39
20
|
readonly text: string;
|
|
40
21
|
readonly author: PromptSectionAuthor;
|
|
41
22
|
}
|
|
42
|
-
/** What one
|
|
43
|
-
* sentence when
|
|
23
|
+
/** What one disposition leaves in the run: the entry's text and author, absent when dropped, plus the defect
|
|
24
|
+
* sentence when an answer could not be taken. */
|
|
44
25
|
export interface DisposedSectionEntry {
|
|
45
26
|
readonly kept?: {
|
|
46
27
|
readonly text: string;
|
|
@@ -48,7 +29,14 @@ export interface DisposedSectionEntry {
|
|
|
48
29
|
};
|
|
49
30
|
readonly defect?: string;
|
|
50
31
|
}
|
|
51
|
-
/**
|
|
52
|
-
export
|
|
53
|
-
/**
|
|
32
|
+
/** The disposition the dial reads: one entry in, its disposition out. */
|
|
33
|
+
export type PromptSectionDisposer = (entry: PromptSectionEntry) => Promise<DisposedSectionEntry>;
|
|
34
|
+
/**
|
|
35
|
+
* The disposer over the leg's bus, or undefined when no handler hangs on `prompt.section` (the dial then keeps
|
|
36
|
+
* every entry as computed, with nothing raised).
|
|
37
|
+
*/
|
|
38
|
+
export declare function sectionDisposerOf(bus: LegModBus | undefined, signal?: AbortSignal): PromptSectionDisposer | undefined;
|
|
39
|
+
/** One entry's disposition at its dial: the leg's disposer when one is wired, else the entry as the engine computed it. */
|
|
40
|
+
export declare function disposeSectionEntry(disposer: PromptSectionDisposer | undefined, entry: PromptSectionEntry): Promise<DisposedSectionEntry>;
|
|
41
|
+
/** The operator warning for an answer the station could not take. */
|
|
54
42
|
export declare function sectionAnswerWarning(defect: string): Error;
|
|
@@ -1,48 +1,81 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bandOf } from "../prompt-assembly/bands.js";
|
|
2
|
+
import { lockedTextIn, receivePromptData } from "../prompt-assembly/overlay.js";
|
|
2
3
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
3
|
-
import {
|
|
4
|
+
import { egressThrownText, extensionWords } from "./untrusted-egress.js";
|
|
4
5
|
export const DISPOSABLE_PROMPT_SECTIONS = Object.freeze(["core/discovery.mcp-instructions"]);
|
|
5
|
-
const ANSWER_KEYS = { drop: ["op"], replace: ["op", "text"] };
|
|
6
|
-
const FOREIGN_VALUE_MAX = 200;
|
|
7
|
-
const STRAY_KEYS_NAMED = 5;
|
|
8
6
|
const DEFECT_MAX = 600;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
7
|
+
function readAnswer(answer, seat) {
|
|
8
|
+
if (typeof answer !== "object" || answer === null || Array.isArray(answer))
|
|
9
|
+
return `answered ${answer === null ? "null" : Array.isArray(answer) ? "an array" : typeof answer}; prompt.section takes { text: string | null }`;
|
|
10
|
+
const received = receivePromptData(answer);
|
|
11
|
+
if ("notData" in received)
|
|
12
|
+
return `a prompt.section answer is data (plain objects, arrays, strings, booleans); it holds ${received.notData}`;
|
|
13
|
+
const data = received.value;
|
|
14
|
+
const keys = Object.keys(data);
|
|
15
|
+
const stray = keys.filter((k) => k !== "text");
|
|
16
|
+
if (stray.length > 0)
|
|
17
|
+
return `the answer carries ${stray.slice(0, 5).map((k) => JSON.stringify(extensionWords(k, 40))).join(", ")} (text only)`;
|
|
18
|
+
if (!keys.includes("text"))
|
|
19
|
+
return "the answer has no text (a string to place, null to leave the entry out)";
|
|
20
|
+
const text = data["text"];
|
|
21
|
+
const op = text === null ? "drop" : "replace";
|
|
22
|
+
if (text !== null && (typeof text !== "string" || text.trim() === ""))
|
|
23
|
+
return `text is ${typeof text === "string" ? "blank" : typeof text}; it is a non-blank string or null`;
|
|
24
|
+
const source = seat === "deployment" ? "deploy" : "task";
|
|
25
|
+
if (!bandOf("discovery").ops[source].includes(op))
|
|
26
|
+
return `a ${source} answer may not ${op} a discovery entry`;
|
|
27
|
+
if (typeof text === "string") {
|
|
28
|
+
const hits = lockedTextIn(text);
|
|
29
|
+
if (hits.length > 0)
|
|
30
|
+
return `the text contains, verbatim, the locked text of ${[...new Set(hits.map((h) => h.split("#")[0]))].join(", ")}`;
|
|
31
|
+
}
|
|
32
|
+
return { text };
|
|
33
|
+
}
|
|
34
|
+
export function sectionDisposerOf(bus, signal) {
|
|
35
|
+
if (bus === undefined || !bus.hasHandlers("prompt.section"))
|
|
36
|
+
return undefined;
|
|
37
|
+
return async (entry) => {
|
|
38
|
+
const computed = { text: entry.text, author: "server" };
|
|
39
|
+
const where = `prompt section "${entry.section}" entry "${inlineUntrusted(entry.server, 160)}"`;
|
|
40
|
+
const e = { name: entry.section, key: entry.server, mountedTools: Object.freeze([...entry.mountedTools]), text: entry.text };
|
|
41
|
+
const bottom = Object.freeze({ text: entry.text });
|
|
42
|
+
const taken = new WeakMap([[bottom, entry.text]]);
|
|
43
|
+
let outcome;
|
|
44
|
+
try {
|
|
45
|
+
outcome = await bus.dispatch("prompt.section", e, {
|
|
46
|
+
bottom: () => bottom,
|
|
47
|
+
...(signal !== undefined ? { signal } : {}),
|
|
48
|
+
checkAnswer: (a, _e, by) => {
|
|
49
|
+
if (typeof a === "object" && a !== null && taken.has(a))
|
|
50
|
+
return undefined;
|
|
51
|
+
const read = readAnswer(a, by.seat);
|
|
52
|
+
if (typeof read === "string")
|
|
53
|
+
return read;
|
|
54
|
+
taken.set(a, read.text);
|
|
55
|
+
return undefined;
|
|
56
|
+
},
|
|
57
|
+
});
|
|
27
58
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (op !== "drop" && op !== "replace")
|
|
31
|
-
return unread(`the answer's op is ${typeof op === "string" ? `"${foreign(op, 40)}"` : typeof op}, not "drop" or "replace"`);
|
|
32
|
-
const stray = Object.keys(record).filter((k) => !ANSWER_KEYS[op].includes(k));
|
|
33
|
-
if (stray.length > 0) {
|
|
34
|
-
const named = stray.slice(0, STRAY_KEYS_NAMED).map((k) => foreign(k, 40)).join(", ");
|
|
35
|
-
return unread(`a "${op}" answer carries ${named}${stray.length > STRAY_KEYS_NAMED ? ` and ${stray.length - STRAY_KEYS_NAMED} more` : ""}`);
|
|
59
|
+
catch (err) {
|
|
60
|
+
return { kept: computed, defect: inlineUntrusted(`${where}: the station could not be raised (${inlineUntrusted(egressThrownText("same-principal", err), 200)}) — the entry is included as the engine computed it`, DEFECT_MAX) };
|
|
36
61
|
}
|
|
37
|
-
if (
|
|
38
|
-
return {};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
if (outcome.kind === "aborted")
|
|
63
|
+
return { kept: computed };
|
|
64
|
+
const skipped = outcome.skipped.map((s) => `extension "${extensionWords(s.registration, 80)}" ${s.cause === "violation" ? "gave an answer the station cannot take" : s.cause}: ${extensionWords(s.reason, 200)}`);
|
|
65
|
+
const defect = skipped.length > 0 ? inlineUntrusted(`${where}: ${skipped.join("; ")} — that answer is skipped; what is below it stands`, DEFECT_MAX) : undefined;
|
|
66
|
+
if (outcome.kind === "failed")
|
|
67
|
+
return { kept: computed, ...(defect !== undefined ? { defect } : {}) };
|
|
68
|
+
const answer = outcome.answer;
|
|
69
|
+
const text = typeof answer === "object" && answer !== null ? taken.get(answer) : undefined;
|
|
70
|
+
if (text === undefined) {
|
|
71
|
+
return { kept: computed, defect: inlineUntrusted(`${where}: the chain's answer is not one the station took — the entry is included as the engine computed it`, DEFECT_MAX) };
|
|
72
|
+
}
|
|
73
|
+
const disposed = text === null ? {} : typeof text === "string" && text !== entry.text ? { kept: { text, author: "host" } } : { kept: computed };
|
|
74
|
+
return { ...disposed, ...(defect !== undefined ? { defect } : {}) };
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function disposeSectionEntry(disposer, entry) {
|
|
78
|
+
return disposer === undefined ? Promise.resolve({ kept: { text: entry.text, author: "server" } }) : disposer(entry);
|
|
46
79
|
}
|
|
47
80
|
export function sectionAnswerWarning(defect) {
|
|
48
81
|
return Object.assign(new Error(defect), { code: "prompt.section_answer_invalid" });
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* header (`principalHeader`) — and the one rule they obey, for MCP and A2A alike: they travel only to the
|
|
4
4
|
* endpoints the declaration names, and every header name has exactly one owner, compared the way HTTP
|
|
5
5
|
* compares names (case-insensitively). The principal header's owner is the engine: it sends the task's
|
|
6
|
-
* principal under that name, or nothing under it when the task has none.
|
|
6
|
+
* principal under that name, or nothing under it when the task has none. A static header the credential vocabulary
|
|
7
|
+
* reads as a credential is one the engine holds from the moment it resolves it: no child spawned on this host inherits
|
|
8
|
+
* a variable whose value equals it (`holdOwnCredentialValues`, src/core/secret-env.ts).
|
|
7
9
|
*
|
|
8
10
|
* A declaration that breaks the rule is that peer's defect: the resolver throws inside the peer's own dial,
|
|
9
11
|
* so the peer is skipped with a coded warning and its siblings still mount. The MCP transport binds its
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { credentialHeaderValues, holdOwnCredentialValues } from "./secret-env.js";
|
|
1
2
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
2
3
|
export function headerOwnershipCollision(t) {
|
|
3
4
|
const owners = new Map();
|
|
@@ -13,6 +14,7 @@ export function resolveProtocolHttpHeaders(protocol, t, principal) {
|
|
|
13
14
|
const principalHeader = t.principalHeader;
|
|
14
15
|
const declared = t.headers;
|
|
15
16
|
const own = { ...(declared !== undefined ? { headers: { ...declared } } : {}), ...(principalHeader !== undefined ? { principalHeader } : {}) };
|
|
17
|
+
holdOwnCredentialValues(credentialHeaderValues(own.headers));
|
|
16
18
|
const collision = headerOwnershipCollision(own);
|
|
17
19
|
if (collision !== undefined) {
|
|
18
20
|
throw Object.assign(new Error(`${protocol}: the declaration names the header "${inlineUntrusted(collision.first, 80)}" twice ("${inlineUntrusted(collision.second, 80)}" — header names are case-insensitive); ${principalHeader !== undefined && collision.second === principalHeader ? "the principal header is the engine's to send, so remove the static entry" : "keep one spelling"} — this peer is not dialed`), { code: "config.protocol_header_collision" });
|
|
@@ -79,8 +79,8 @@ export declare function formatHookFeedback(text: string, mark?: string): string;
|
|
|
79
79
|
* DECLARE port — the system-prompt declaration that gives the mark meaning. STRICT, single form
|
|
80
80
|
* (design/319 D-2, no grace clause): reminder-shaped text without the current mark is data wherever
|
|
81
81
|
* it appears — including TRUE pre-319 engine mints in a resumed transcript, a deliberate fail-safe
|
|
82
|
-
* downgrade (stale guidance loses authority; forgery gains none). Composed
|
|
83
|
-
*
|
|
82
|
+
* downgrade (stale guidance loses authority; forgery gains none). Composed as its own section
|
|
83
|
+
* (`core/harness.reminder-mark`) when a mark is threaded; a fork inherits the
|
|
84
84
|
* parent's prompt bytes and therefore the parent's declaration — never a second one.
|
|
85
85
|
*
|
|
86
86
|
* A block carrying the current mark is the engine's own wherever it stands, tool results included:
|
|
@@ -162,7 +162,7 @@ export interface ExecStreamOptions {
|
|
|
162
162
|
signal?: AbortSignal;
|
|
163
163
|
/**
|
|
164
164
|
* Idle/liveness read timeout (ms). If no {@link OutputChunk} arrives within this window, `execStream` ends
|
|
165
|
-
* with a {@link RemoteExecutionError} (code `"
|
|
165
|
+
* with a {@link RemoteExecutionError} (code `"liveness_no_output"`) instead of hanging. **Needed in practice for remote
|
|
166
166
|
* backends** (design/48 §5, service [7]): E2B issue #1128 — a *streaming* call sets no read timeout (only unary
|
|
167
167
|
* does), so an unreachable sandbox hangs the worker forever. Distinct from `timeout` (total wall-clock).
|
|
168
168
|
*
|
|
@@ -170,8 +170,9 @@ export interface ExecStreamOptions {
|
|
|
170
170
|
* when this is unset (a missing idle timeout MUST NOT mean "wait forever / until the env dies"), and that bound
|
|
171
171
|
* MUST cover the **command-creation/handshake RPC**, not only the post-first-chunk loop — a provider hang
|
|
172
172
|
* typically occurs at creation, before any chunk arrives, so an idle timer started only after the first chunk
|
|
173
|
-
* misses it. On trigger, throw {@link RemoteExecutionError} with code `"
|
|
174
|
-
* the command's idempotency — the adapter MUST NOT blind-retry).
|
|
173
|
+
* misses it. On trigger, throw {@link RemoteExecutionError} with code `"liveness_no_output"` (retryable by the
|
|
174
|
+
* caller per the command's idempotency — the adapter MUST NOT blind-retry). The wall-clock {@link timeout}
|
|
175
|
+
* keeps `"timeout"`: the two cuts are different words because they explain themselves with different numbers.
|
|
175
176
|
*/
|
|
176
177
|
readTimeoutMs?: number;
|
|
177
178
|
/** Per-call output cap (design/48 §5 gap 8): stop streaming after this many bytes to avoid a `find /` flooding the control plane. */
|
|
@@ -190,12 +191,22 @@ export type RemoteExecutionErrorCode =
|
|
|
190
191
|
/** The command/stream was aborted via its `AbortSignal`. */
|
|
191
192
|
| "aborted"
|
|
192
193
|
/**
|
|
193
|
-
* A command's wall-clock {@link ExecStreamOptions.timeout} or
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
194
|
+
* A bound on elapsed time was exceeded: a command's wall-clock {@link ExecStreamOptions.timeout}, or the idle
|
|
195
|
+
* bound of a lifecycle / filesystem RPC (service [45]/[59]). **Retryable** by the caller per the op's
|
|
196
|
+
* idempotency (the adapter MUST NOT blind-retry). Distinct from `"aborted"` (a caller-driven `AbortSignal`),
|
|
197
|
+
* `"connect_failed"` (workspace provisioning, not a per-command hang) and `"liveness_no_output"` (an exec
|
|
198
|
+
* stream's silence window — below).
|
|
197
199
|
*/
|
|
198
200
|
| "timeout"
|
|
201
|
+
/**
|
|
202
|
+
* An exec stream produced no {@link OutputChunk} for its liveness window ({@link ExecStreamOptions.readTimeoutMs},
|
|
203
|
+
* or the adapter's default bound) — including a provider RPC hang at command creation, before the first chunk
|
|
204
|
+
* (service [45]/[59]). A hang detector, not a budget: it is the word for the cut that used to share `"timeout"`
|
|
205
|
+
* with the wall-clock budget, and the two explain themselves with different numbers (the window versus the
|
|
206
|
+
* budget the caller asked for), so a consumer must be able to tell them apart by code. **Retryable** by the
|
|
207
|
+
* caller per the command's idempotency (the adapter MUST NOT blind-retry), exactly as `"timeout"` is.
|
|
208
|
+
*/
|
|
209
|
+
| "liveness_no_output"
|
|
199
210
|
/**
|
|
200
211
|
* The op is not supported by this adapter's capabilities (design/61 §9 A) — e.g. `suspendVM`/`resumeVM` on a
|
|
201
212
|
* non-suspendable SSH/ADB env. Permanent for this env; check {@link RemoteExecutionEnv.capabilities} first.
|
|
@@ -235,8 +246,8 @@ export type RemoteExecutionErrorCode =
|
|
|
235
246
|
/**
|
|
236
247
|
* The RETRYABLE half of {@link RemoteExecutionErrorCode} — the codes whose own contract above says a retry
|
|
237
248
|
* can succeed (`auth_transient`: retry-exactly-once after the device is authorized; `connect_failed`: the
|
|
238
|
-
* workspace could not be reached/provisioned this attempt; `timeout`: a
|
|
239
|
-
* `transport_lost`: re-establish the connection). Every OTHER code is permanent for this attempt
|
|
249
|
+
* workspace could not be reached/provisioned this attempt; `timeout` / `liveness_no_output`: a time bound
|
|
250
|
+
* tripped; `transport_lost`: re-establish the connection). Every OTHER code is permanent for this attempt
|
|
240
251
|
* (`auth_failed`/`unsupported`), caller-driven (`aborted`), unclassifiable (`unknown`), or
|
|
241
252
|
* outcome-unknowable (`outcome_unknown` — design/380 O12: the op may have ALREADY executed, so a retry
|
|
242
253
|
* re-drives committed side effects) — retrying them burns budget or, worse, re-drives a rejected
|
|
@@ -284,8 +295,8 @@ export declare class RemoteExecutionError extends Error {
|
|
|
284
295
|
* wall-clock would false-kill the (durable-checkpoint *命门*) path. So such ops bound on an **idle/no-heartbeat**
|
|
285
296
|
* window (reset by observable progress), not total elapsed time. Cheap control RPCs (`isRunning`, a small
|
|
286
297
|
* `connect` handshake) may use a short wall-clock. The adapter sizes the bound per op class.
|
|
287
|
-
* 3. **A liveness breach surfaces as a typed, retryable error**: `RemoteExecutionError` code `"
|
|
288
|
-
*
|
|
298
|
+
* 3. **A liveness breach surfaces as a typed, retryable error**: `RemoteExecutionError` code `"liveness_no_output"`
|
|
299
|
+
* for an exec stream's silence window and `"timeout"` for the lifecycle ops' idle bound; for the inherited filesystem ops (which return the vendored `Result<_, FileError>`),
|
|
289
300
|
* a `FileError` — but still **bounded, never an unbounded wait**. Retry is the caller's decision per the op's
|
|
290
301
|
* idempotency; the adapter MUST NOT blind-retry.
|
|
291
302
|
*/
|
|
@@ -418,7 +429,8 @@ export interface RemoteExecutionEnv extends ExecutionEnv {
|
|
|
418
429
|
* per-command timeout is **independent of the env/sandbox lifetime** and an adapter MUST NOT fall back to the
|
|
419
430
|
* lifetime when none is given (else a hung provider RPC pins the worker for the whole sandbox lifetime). An
|
|
420
431
|
* adapter SHOULD enforce a bounded default liveness/idle timeout that also covers the command-creation RPC, and
|
|
421
|
-
* surface a
|
|
432
|
+
* surface a silence breach as a typed, retryable {@link RemoteExecutionError} code `"liveness_no_output"` and a
|
|
433
|
+
* wall-clock breach as `"timeout"`. See
|
|
422
434
|
* {@link ExecStreamOptions.timeout} / {@link ExecStreamOptions.readTimeoutMs}.
|
|
423
435
|
*/
|
|
424
436
|
execStream(command: string, options?: ExecStreamOptions): AsyncIterable<OutputChunk>;
|
package/dist/core/remote-env.js
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* · `environment` — the leg's infrastructure failed (the workspace restore, an abort): the decision is
|
|
10
10
|
* untouched, and a retry replays it;
|
|
11
11
|
* · `session_gone` — the resumed agent's transcript session is missing;
|
|
12
|
+
* · `transcript_corrupt` — the resumed agent's transcript session is THERE but cannot be read as a session: its log
|
|
13
|
+
* has no header its store wrote (`corrupt_session`), or its entry tree is structurally unusable — a leaf or parent
|
|
14
|
+
* entry that is not in the log (`invalid_session`). Distinct from `session_gone`: the bytes exist, so deleting or
|
|
15
|
+
* restoring the transcript is the remedy, not re-creating a session;
|
|
12
16
|
* · `tool_unavailable` — the approved tool is not available to the resumed leg (gone, replaced by a
|
|
13
17
|
* different contract, or an inherited tool the drive cannot hand over): this approval cannot run, and a
|
|
14
18
|
* fresh decision resolves it;
|
|
@@ -18,6 +22,7 @@
|
|
|
18
22
|
* · `decision_rejected` — the supplied decision does not fit the checkpoint (another gate's answer, an
|
|
19
23
|
* unsafe payload, an input the constraints refuse): correct the decision and resubmit.
|
|
20
24
|
*/
|
|
25
|
+
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
21
26
|
/**
|
|
22
27
|
* design/80 D-1: why a consumed checkpoint was {@link import("./checkpoint-store.js").CheckpointStore.reopen | reopened}
|
|
23
28
|
* (resolved → pending). The reason drives re-resume validation (§3 invariant #1): an `env_failed` reopen is a SYSTEM
|
|
@@ -41,11 +46,20 @@
|
|
|
41
46
|
*/
|
|
42
47
|
export declare const REOPEN_REASONS: readonly ["env_failed", "tool_unavailable", "constraint_rejected", "config_refused"];
|
|
43
48
|
export type ReopenReason = (typeof REOPEN_REASONS)[number];
|
|
44
|
-
export declare const RESUME_STARTUP_CAUSES: readonly ["environment", "session_gone", "tool_unavailable", "configuration", "decision_rejected"];
|
|
49
|
+
export declare const RESUME_STARTUP_CAUSES: readonly ["environment", "session_gone", "transcript_corrupt", "tool_unavailable", "configuration", "decision_rejected"];
|
|
45
50
|
export type ResumeStartupCause = (typeof RESUME_STARTUP_CAUSES)[number];
|
|
46
51
|
/** The cause a resume failure's code names; `undefined` when the code is absent or the table does not
|
|
47
52
|
* name it. Every `config.*` code is a configuration door's refusal. */
|
|
48
53
|
export declare function resumeStartupCauseOf(code: string | undefined): ResumeStartupCause | undefined;
|
|
54
|
+
declare const REOPEN_REASON_OF_CAUSE: {
|
|
55
|
+
readonly environment: "env_failed";
|
|
56
|
+
readonly session_gone: "env_failed";
|
|
57
|
+
readonly transcript_corrupt: "env_failed";
|
|
58
|
+
readonly tool_unavailable: "tool_unavailable";
|
|
59
|
+
readonly configuration: "config_refused";
|
|
60
|
+
readonly decision_rejected: "constraint_rejected";
|
|
61
|
+
};
|
|
62
|
+
export type ReopenReasonTableCoversEveryCause = AssertAllKeysHandled<Exclude<ResumeStartupCause, keyof typeof REOPEN_REASON_OF_CAUSE>>;
|
|
49
63
|
/**
|
|
50
64
|
* The reason a consumed checkpoint whose approved action never started returns to pending under. A failure
|
|
51
65
|
* whose cause is not known reopens as `env_failed`: the leg went away before the action ran, the human's
|
|
@@ -54,3 +68,4 @@ export declare function resumeStartupCauseOf(code: string | undefined): ResumeSt
|
|
|
54
68
|
export declare function reopenReasonOf(cause: ResumeStartupCause | undefined): ReopenReason;
|
|
55
69
|
/** What the result of a returned resume leg says after its checkpoint was reopened, per reason. */
|
|
56
70
|
export declare function reopenedUnstartedSentence(reason: ReopenReason): string;
|
|
71
|
+
export {};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export const REOPEN_REASONS = Object.freeze(["env_failed", "tool_unavailable", "constraint_rejected", "config_refused"]);
|
|
2
|
-
export const RESUME_STARTUP_CAUSES = Object.freeze(["environment", "session_gone", "tool_unavailable", "configuration", "decision_rejected"]);
|
|
2
|
+
export const RESUME_STARTUP_CAUSES = Object.freeze(["environment", "session_gone", "transcript_corrupt", "tool_unavailable", "configuration", "decision_rejected"]);
|
|
3
3
|
const CAUSE_OF_CODE = new Map([
|
|
4
4
|
["resume.env_failed", "environment"],
|
|
5
5
|
["checkpoint.resume_aborted", "environment"],
|
|
6
6
|
["resume.session_not_found", "session_gone"],
|
|
7
|
+
["corrupt_session", "transcript_corrupt"],
|
|
8
|
+
["invalid_session", "transcript_corrupt"],
|
|
7
9
|
["resume.tool_unavailable", "tool_unavailable"],
|
|
8
10
|
["resume.tool_contract_mismatch", "tool_unavailable"],
|
|
9
11
|
["checkpoint.config_refused", "configuration"],
|
|
@@ -22,6 +24,7 @@ export function resumeStartupCauseOf(code) {
|
|
|
22
24
|
const REOPEN_REASON_OF_CAUSE = {
|
|
23
25
|
environment: "env_failed",
|
|
24
26
|
session_gone: "env_failed",
|
|
27
|
+
transcript_corrupt: "env_failed",
|
|
25
28
|
tool_unavailable: "tool_unavailable",
|
|
26
29
|
configuration: "config_refused",
|
|
27
30
|
decision_rejected: "constraint_rejected",
|
|
@@ -20,6 +20,10 @@ export declare function retiredKeyOf(bag: object, table: Readonly<Record<string,
|
|
|
20
20
|
* `RunWorkflowToolDeps`, `TaskSpec`).
|
|
21
21
|
*/
|
|
22
22
|
export declare function assertNoRetiredKeys(bag: object, table: Readonly<Record<string, string>>, owner: string): void;
|
|
23
|
+
/** What replaced the `hooks` record, on a deps bag and on a `TaskSpec` alike: one seat name, one sentence, quoted by both
|
|
24
|
+
* tables (this one and `runner/retired-runner-deps.ts`). The record's callbacks never run, so a deny one carried would pass
|
|
25
|
+
* unseen — the key is refused by name instead. */
|
|
26
|
+
export declare const RETIRED_HOOKS_REPLACEMENT = "the callback seats are `mods` registrations now \u2014 register the extension on `RunnerDeps.mods` (a deployment's) or `TaskSpec.mods` (a task's), hung on the `classic.*` events (`classic.PreToolUse`, `classic.PostToolUse`, `classic.PostToolBatch`, `classic.UserPromptSubmit`, `classic.Stop`, `classic.SubagentStop`, \u2026); a callback that denied is a handler answering `{ action: \"deny\", message }`";
|
|
23
27
|
/** The RETIRED `TaskSpec` keys, each with the sentence that names its replacement — the table the spec door reads
|
|
24
28
|
* ({@link assertNoRetiredKeys} with owner `TaskSpec`). A caller still writing one of these is refused by name
|
|
25
29
|
* (`config.spec_retired_key`), never read as absence: a mode a caller believes it declared must not silently run
|
|
@@ -24,11 +24,14 @@ export function assertNoRetiredKeys(bag, table, owner) {
|
|
|
24
24
|
e.code = owner === "TaskSpec" ? "config.spec_retired_key" : "config.deps_retired_key";
|
|
25
25
|
throw e;
|
|
26
26
|
}
|
|
27
|
+
export const RETIRED_HOOKS_REPLACEMENT = 'the callback seats are `mods` registrations now — register the extension on `RunnerDeps.mods` (a deployment\'s) or `TaskSpec.mods` (a task\'s), hung on the `classic.*` events (`classic.PreToolUse`, `classic.PostToolUse`, `classic.PostToolBatch`, `classic.UserPromptSubmit`, `classic.Stop`, `classic.SubagentStop`, …); a callback that denied is a handler answering `{ action: "deny", message }`';
|
|
27
28
|
export const RETIRED_TASK_SPEC_KEYS = Object.freeze({
|
|
29
|
+
hooks: RETIRED_HOOKS_REPLACEMENT,
|
|
28
30
|
autoModeRequested: 'the permission mode is one key now — write `permissionMode: "auto"`',
|
|
29
31
|
approverPosture: 'a posture never authorized anything; drop the key (the engine renders the posture from the effective mode). Releasing asks is a separate decision: `permissionMode: "bypassPermissions"`',
|
|
30
32
|
"attachments.planModeReminder": "the read-only planning posture is stated whenever the leg's mode is `plan` (first turn, every 5 turns, and an exit frame when it ends) — drop the key",
|
|
31
33
|
"skills[].manifest": "a skill's tools are its `allowedTools` / `disallowedTools` now — an allow-rule and a deny-rule source for the run the skill is activated in, never a fence; move `manifest.allowTools` to `allowedTools`. To fence tools write deny rules (or a delegated agent definition's `allowTools`); to fence write paths use session `allowDirs`",
|
|
34
|
+
promptProvider: "a task changes prompt text through overlays: its role text is `systemPrompt` (it replaces the role base `promptRole` selects), its added block `appendSystemPrompt`, the minimal form `promptForm: \"minimal\"`; anything else is a `prompt.compose` registration on the task's own `mods` answering `{ overlays }` (an already-assembled prompt is refused, never passed through)",
|
|
32
35
|
});
|
|
33
36
|
export const RETIRED_TOOL_POLICY_KEYS = Object.freeze({
|
|
34
37
|
writeTargetGuard: "the guarded write targets are stated as rows now — `ToolPolicy.writeGuardRows`, an async provider of the one path table's `PathGuardRow`s the leg's ONE write judgment reads for every writer the policy cannot see as a call (the shell's file commands). A wrapper carries it through `wrapToolPolicy`, which reads every member a policy declares",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AnnouncedListingSnapshot, AnnouncedListingSnapshotInput, AnnouncedMcpFailure } from "../../internal/harness.js";
|
|
2
2
|
import type { SessionTreeEntry } from "../../internal/harness-types.js";
|
|
3
|
-
import type { DateAnchorRead } from "./contracts.js";
|
|
3
|
+
import type { AnnounceOnceSink, DateAnchorRead } from "./contracts.js";
|
|
4
4
|
import type { McpDroppedTool, McpServerStatus } from "../mcp.js";
|
|
5
5
|
import type { RunnerDeps } from "../types.js";
|
|
6
6
|
/** Upper bound on the advisory-digest arm kept in the record (oldest dropped past it). A digest is
|
|
@@ -18,6 +18,31 @@ export declare function announceDigest(lane: string, text: string): string;
|
|
|
18
18
|
* an equality oracle on a secret is still an oracle. The forwarded line itself is untouched (the sink
|
|
19
19
|
* owns its own redaction posture); only the identity is judged on the redacted form. */
|
|
20
20
|
export declare function warningDigest(err: unknown): string;
|
|
21
|
+
/** The two built-in memory pairs a mount can decline, each with its sentence frame and the `classification`
|
|
22
|
+
* word its advisory carries on the operator sink. */
|
|
23
|
+
declare const MEMORY_MOUNT_ADVISORY: {
|
|
24
|
+
readonly shared: {
|
|
25
|
+
readonly label: "Shared memory tools";
|
|
26
|
+
readonly classification: "shared-memory-not-mounted";
|
|
27
|
+
readonly tail: "the pair mounts together or not at all";
|
|
28
|
+
};
|
|
29
|
+
readonly engine: {
|
|
30
|
+
readonly label: "Memory tools";
|
|
31
|
+
readonly classification: "memory-tools-not-mounted";
|
|
32
|
+
readonly tail: "these tools mount together or not at all";
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/** Which built-in memory pair an advisory is about. */
|
|
36
|
+
export type MemoryMountPair = keyof typeof MEMORY_MOUNT_ADVISORY;
|
|
37
|
+
/**
|
|
38
|
+
* THE one mint for "a built-in memory pair was NOT mounted": the shared-memory pair and the engine memory trio each
|
|
39
|
+
* decline to mount when the deployment's selector excludes a name, when a caller tool already occupies one, or when
|
|
40
|
+
* mounting them would inject the ToolSearch tool under a name the task already declares — four stations, one sentence
|
|
41
|
+
* frame, one scope. The fact belongs to the deployment's configuration, so it is told once per RUNNER
|
|
42
|
+
* ({@link AnnounceOnceLedger.onErrorOncePerRunner}), not once per conversation. `reason` is the clause after the
|
|
43
|
+
* colon; the pair's `names` are the tools that stay unmounted together.
|
|
44
|
+
*/
|
|
45
|
+
export declare function announceMemoryNotMounted(sink: Pick<AnnounceOnceSink, "onErrorOncePerRunner">, sessionId: string, pair: MemoryMountPair, names: readonly string[], reason: string): void;
|
|
21
46
|
/** The digest of one model-facing dropped-tool entry (server, tool, reason — the three fields the
|
|
22
47
|
* frame prints). */
|
|
23
48
|
export declare function droppedToolDigest(d: McpDroppedTool): string;
|
|
@@ -60,6 +85,10 @@ export interface AnnounceOnceLedger {
|
|
|
60
85
|
* the record is dropped; a new one is forwarded to `deps.onError` and recorded. With no sink wired
|
|
61
86
|
* nothing is recorded either — "announced" means delivered. Same signature as `RunnerDeps.onError`. */
|
|
62
87
|
onError: NonNullable<RunnerDeps["onError"]>;
|
|
88
|
+
/** The operator-lane sink for a DEPLOYMENT fact (see {@link AnnounceOnceSink.onErrorOncePerRunner}): the same
|
|
89
|
+
* `(code, message)` digest, judged against what THIS RUNNER's sink has already been told rather than against
|
|
90
|
+
* the session's record. Writes no digest into the record and owes the transcript nothing. */
|
|
91
|
+
onErrorOncePerRunner: NonNullable<RunnerDeps["onError"]>;
|
|
63
92
|
/** The MODEL-face pending set for this run: the currently failed servers (name/error neutralized +
|
|
64
93
|
* bounded at intake, the reminder's own bounds) MINUS the pairs already announced on this branch.
|
|
65
94
|
* As a side effect the record's `mcpFailed` arm is pruned to the pairs that are STILL failing —
|
|
@@ -122,3 +151,4 @@ export declare function owedDateAnchor(session: {
|
|
|
122
151
|
} | undefined, state: {
|
|
123
152
|
announcedDate: string;
|
|
124
153
|
} | undefined): (() => Promise<void>) | undefined;
|
|
154
|
+
export {};
|