@sema-agent/core 7.52.0 → 7.54.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 +470 -0
- package/LICENSE +3 -1
- package/README.md +3 -1
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -0
- package/dist/agents/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -6
- package/dist/agents/peer-session-drain.js +271 -182
- package/dist/agents/read-inbox-tool.d.ts +7 -0
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/send-message-tool.js +3 -4
- package/dist/agents/subagent.d.ts +6 -5
- package/dist/agents/subagent.js +117 -83
- package/dist/agents/tool-filter.d.ts +25 -4
- package/dist/agents/tool-filter.js +13 -3
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +4 -4
- package/dist/brain/model-presets.js +2 -1
- package/dist/brain/route-adjudicator.d.ts +6 -0
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +33 -14
- package/dist/core/allow-entry.d.ts +3 -1
- package/dist/core/allow-entry.js +3 -2
- package/dist/core/approval-object.d.ts +87 -0
- package/dist/core/approval-object.js +85 -0
- package/dist/core/arg-summary.d.ts +3 -2
- package/dist/core/arg-summary.js +17 -11
- package/dist/core/ask-origin.d.ts +43 -27
- package/dist/core/ask-origin.js +20 -11
- package/dist/core/ask-question.d.ts +0 -1
- package/dist/core/ask-question.js +4 -67
- package/dist/core/ask-unresolvable-notice.d.ts +3 -2
- package/dist/core/auto-compaction.d.ts +106 -50
- package/dist/core/auto-compaction.js +69 -53
- package/dist/core/auto-mode-prompt.d.ts +4 -2
- package/dist/core/auto-mode-prompt.js +4 -3
- package/dist/core/canonical-json.d.ts +69 -0
- package/dist/core/canonical-json.js +130 -8
- package/dist/core/checkpoint-requirements.d.ts +13 -7
- package/dist/core/checkpoint-requirements.js +4 -1
- package/dist/core/checkpoint-store.d.ts +60 -36
- package/dist/core/checkpoint-store.js +7 -6
- package/dist/core/child-clamp-seats.d.ts +45 -6
- package/dist/core/child-clamp-seats.js +56 -4
- package/dist/core/compliance.js +3 -1
- package/dist/core/consolidate-scope.d.ts +1 -1
- package/dist/core/cross-boundary-taint.d.ts +213 -0
- package/dist/core/cross-boundary-taint.js +196 -0
- package/dist/core/decision-receipt.d.ts +44 -1
- package/dist/core/decision-receipt.js +16 -0
- package/dist/core/effective-path-target.d.ts +28 -6
- package/dist/core/effective-path-target.js +28 -2
- package/dist/core/engine-notice.d.ts +47 -4
- package/dist/core/engine-notice.js +17 -1
- package/dist/core/engine-storage-backend.d.ts +73 -0
- package/dist/core/engine-storage-backend.js +1 -0
- package/dist/core/file-history-store.d.ts +6 -2
- package/dist/core/file-history-store.js +1 -0
- package/dist/core/gate-fold.js +13 -4
- package/dist/core/gate-lanes.js +41 -34
- package/dist/core/gate-outcome.d.ts +18 -9
- package/dist/core/governance-codes.d.ts +9 -2
- package/dist/core/governance-codes.js +9 -0
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +21 -19
- package/dist/core/hooks.js +6 -5
- package/dist/core/locked-config.d.ts +110 -31
- package/dist/core/locked-config.js +155 -42
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/mcp-probe.js +1 -1
- package/dist/core/mcp-server-spec.d.ts +152 -17
- package/dist/core/mcp-server-spec.js +223 -40
- package/dist/core/mcp.d.ts +21 -14
- package/dist/core/mcp.js +59 -54
- package/dist/core/memory-engine/engine.d.ts +14 -19
- package/dist/core/memory-engine/engine.js +9 -6
- package/dist/core/memory-engine/export-bundle.d.ts +1 -1
- package/dist/core/memory-engine/export-bundle.js +1 -1
- package/dist/core/memory-engine/file-backend.d.ts +2 -6
- package/dist/core/memory-engine/file-backend.js +1 -12
- package/dist/core/memory.d.ts +3 -3
- package/dist/core/mode-policy.d.ts +16 -26
- package/dist/core/mode-policy.js +5 -18
- package/dist/core/mods/kernel/dispatch.js +13 -15
- package/dist/core/mods/kernel/events.d.ts +9 -6
- package/dist/core/mods/kernel/events.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +41 -11
- package/dist/core/mods/kernel/receive.js +124 -54
- package/dist/core/mods/kernel/site-rules.d.ts +80 -15
- package/dist/core/mods/kernel/site-rules.js +232 -40
- package/dist/core/mods/stations.d.ts +0 -6
- package/dist/core/mods/stations.js +0 -7
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +3 -1
- package/dist/core/parent-spec-seat.d.ts +12 -15
- package/dist/core/parent-spec-seat.js +14 -6
- package/dist/core/pause-registry.d.ts +35 -1
- package/dist/core/pause-registry.js +10 -0
- package/dist/core/permission-rule-consent.d.ts +18 -5
- package/dist/core/permission-rule-consent.js +13 -8
- package/dist/core/permission-rule-model.d.ts +87 -15
- package/dist/core/permission-rule-model.js +87 -8
- package/dist/core/permission-rule-org.d.ts +200 -33
- package/dist/core/permission-rule-org.js +217 -91
- package/dist/core/permission-rule-session.js +2 -2
- package/dist/core/permission-rule-store.d.ts +8 -0
- package/dist/core/permission-rule-store.js +1 -0
- package/dist/core/permission-rule-sync.js +3 -2
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +37 -8
- package/dist/core/read-only-shell.d.ts +1 -1
- package/dist/core/read-only-shell.js +30 -9
- package/dist/core/remote-env.d.ts +41 -1
- package/dist/core/remote-env.js +18 -0
- package/dist/core/resume-startup-cause.d.ts +18 -0
- package/dist/core/resume-startup-cause.js +12 -0
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +8 -2
- package/dist/core/retired-keys.js +12 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/approval-feedback.d.ts +46 -0
- package/dist/core/runner/approval-feedback.js +41 -0
- package/dist/core/runner/compaction-call-options.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.js +2 -2
- package/dist/core/runner/contracts.d.ts +34 -41
- package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
- package/dist/core/runner/cross-boundary-lane.js +190 -0
- package/dist/core/runner/decide-continuation.d.ts +4 -4
- package/dist/core/runner/decide-continuation.js +3 -1
- package/dist/core/runner/gate-exit.d.ts +32 -12
- package/dist/core/runner/gate-exit.js +77 -34
- package/dist/core/runner/inherited-ask-grants.js +2 -2
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +5 -3
- package/dist/core/runner/person-path-rows.js +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
- package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
- package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
- package/dist/core/runner/prepare-config-doors.d.ts +58 -15
- package/dist/core/runner/prepare-config-doors.js +167 -146
- package/dist/core/runner/prepare-context-lane.js +6 -3
- package/dist/core/runner/prepare-defer-classify.js +4 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
- package/dist/core/runner/prepare-delegation-surface.js +8 -7
- package/dist/core/runner/prepare-execution-env.d.ts +1 -2
- package/dist/core/runner/prepare-execution-env.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
- package/dist/core/runner/prepare-gate-stations.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -3
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
- package/dist/core/runner/prepare-inherited-gate.js +3 -2
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +19 -6
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-park-ask.d.ts +1 -1
- package/dist/core/runner/prepare-park-ask.js +9 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
- package/dist/core/runner/prepare-policy-chain.js +14 -19
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +6 -4
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-assembly.js +20 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
- package/dist/core/runner/prepare-prompt-inputs.js +6 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
- package/dist/core/runner/prepare-protocol-tools.js +19 -12
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
- package/dist/core/runner/prepare-safety-scan.js +34 -27
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
- package/dist/core/runner/prepare-suspend-saga.js +6 -2
- package/dist/core/runner/prepare-task.js +22 -19
- package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
- package/dist/core/runner/prepare-turn-wiring.js +14 -49
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -5
- package/dist/core/runner/prepare-wiring-manifest.js +9 -6
- package/dist/core/runner/prepare-workspace-restore.js +2 -2
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +16 -14
- package/dist/core/runner/resume-apply.js +4 -4
- package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
- package/dist/core/runner/resume-policy-outcome.js +18 -15
- package/dist/core/runner/resume-preflight.js +2 -2
- package/dist/core/runner/resume-review-outcome.js +3 -3
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +1 -4
- package/dist/core/runner/run-leg.js +97 -43
- package/dist/core/runner/run-recovery-lanes.js +5 -3
- package/dist/core/runner/run-settle-and-teardown.js +2 -0
- package/dist/core/runner/run-terminal-adoption.js +21 -9
- package/dist/core/runner/run-turn-boundary.js +9 -3
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/session-file-state-replay.d.ts +5 -2
- package/dist/core/runner/session-file-state-replay.js +3 -2
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
- package/dist/core/runner/sub-agent-deny-lists.js +7 -5
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner/tool-face-overlay.d.ts +24 -6
- package/dist/core/runner/tool-face-overlay.js +24 -15
- package/dist/core/runner-deps.d.ts +37 -22
- package/dist/core/session-policy-store.d.ts +19 -0
- package/dist/core/session-policy-store.js +22 -2
- package/dist/core/session-store.d.ts +35 -16
- package/dist/core/session-store.js +20 -12
- package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
- package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
- package/dist/core/shell-wrapper-table.d.ts +5 -3
- package/dist/core/shell-wrapper-table.js +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
- package/dist/core/store-contracts/session-store-contract.js +639 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
- package/dist/core/task-event.d.ts +44 -6
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-registry-agent.js +1 -0
- package/dist/core/task-registry-shared.d.ts +5 -0
- package/dist/core/task-registry.d.ts +7 -0
- package/dist/core/task-registry.js +17 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +7 -7
- package/dist/core/tighten-task-spec.d.ts +6 -4
- package/dist/core/tighten-task-spec.js +2 -2
- package/dist/core/tool-catalog-entries.d.ts +1 -1
- package/dist/core/tool-catalog-entries.js +52 -52
- package/dist/core/tool-face-reader.d.ts +49 -0
- package/dist/core/tool-face-reader.js +94 -0
- package/dist/core/tool-face.d.ts +17 -8
- package/dist/core/tool-face.js +1 -0
- package/dist/core/tool-policy.d.ts +67 -23
- package/dist/core/tool-policy.js +72 -60
- package/dist/core/tool-roster.d.ts +46 -28
- package/dist/core/tool-roster.js +91 -14
- package/dist/core/tool-spec-members.js +2 -2
- package/dist/core/tool-spec.d.ts +20 -4
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +24 -2
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +23 -0
- package/dist/core/untrusted-text.js +10 -10
- package/dist/core/user-attachment.d.ts +56 -2
- package/dist/core/user-attachment.js +139 -56
- package/dist/core/wiring-manifest.d.ts +4 -2
- package/dist/engine/compaction/compaction.d.ts +2 -2
- package/dist/engine/compaction/utils.d.ts +2 -2
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +10 -4
- package/dist/engine/llm/types.d.ts +7 -2
- package/dist/engine/llm/types.js +2 -1
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +17 -5
- package/dist/engine/loop/types.d.ts +7 -1
- package/dist/engine/session/repo-utils.d.ts +5 -0
- package/dist/engine/session/repo-utils.js +9 -0
- package/dist/engine/session/storage-base.js +2 -2
- package/dist/index.d.ts +37 -17
- package/dist/index.js +31 -14
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/internal/llm.d.ts +1 -1
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/orchestration/run-workflow-tool.d.ts +3 -0
- package/dist/orchestration/run-workflow-tool.js +1 -0
- package/dist/orchestration/workflow-governance.d.ts +1 -1
- package/dist/orchestration/workflow.d.ts +3 -0
- package/dist/orchestration/workflow.js +2 -1
- package/dist/prompt-assembly/artifact-store.d.ts +44 -9
- package/dist/prompt-assembly/artifact-store.js +38 -12
- package/dist/prompt-assembly/artifact.d.ts +12 -11
- package/dist/prompt-assembly/artifact.js +52 -21
- package/dist/prompt-assembly/assemble.d.ts +2 -0
- package/dist/prompt-assembly/assemble.js +2 -1
- package/dist/prompt-assembly/catalog/index.js +2 -1
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +19 -2
- package/dist/prompt-assembly/catalog/schema.js +3 -0
- package/dist/prompt-assembly/composer.d.ts +2 -0
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/overlay.d.ts +21 -11
- package/dist/prompt-assembly/overlay.js +57 -19
- package/dist/prompt-assembly/select.d.ts +21 -6
- package/dist/prompt-assembly/select.js +41 -4
- package/dist/prompts/default.d.ts +1 -12
- package/dist/prompts/default.js +0 -3
- package/dist/shell/lex.d.ts +87 -8
- package/dist/shell/lex.js +283 -49
- package/dist/stores/file/index.d.ts +8 -6
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +14 -3
- package/dist/tools/fs/bash-program-position.d.ts +3 -15
- package/dist/tools/fs/bash-program-position.js +3 -10
- package/dist/tools/fs/bash-readonly-classifier.js +2 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-bash.js +72 -101
- package/dist/tools/fs/fs-read.d.ts +12 -0
- package/dist/tools/fs/fs-read.js +10 -9
- package/dist/tools/fs/fs-shared.d.ts +27 -31
- package/dist/tools/fs/fs-shared.js +12 -15
- package/dist/tools/fs/fs-write.js +1 -1
- package/dist/tools/fs/index.js +2 -2
- package/dist/tools/fs/read-deny-home.d.ts +1 -1
- package/dist/tools/fs/read-deny-home.js +5 -4
- package/dist/tools/fs/read-deny.d.ts +16 -3
- package/dist/tools/fs/read-deny.js +12 -9
- package/dist/tools/fs/safety.d.ts +7 -6
- package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
- package/dist/tools/fs/shell-read-bindings.js +15 -16
- package/dist/tools/fs/shell-read-device.js +1 -1
- package/dist/tools/fs/shell-read-plan.js +36 -47
- package/dist/tools/fs/shell-read-word.d.ts +1 -6
- package/dist/tools/fs/shell-read-word.js +2 -20
- package/dist/tools/fs/shell-reentry-text.js +1 -1
- package/dist/tools/web.d.ts +2 -2
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +5651 -5457
- package/wire-changes/core@7.53.0.json +1968 -0
- package/wire-changes/core@7.54.0.json +2870 -0
- package/dist/core/shell-scan.d.ts +0 -60
- package/dist/core/shell-scan.js +0 -183
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* source by hand cannot drop a row;
|
|
12
12
|
* · the SCAN — the gate reads this table literally, requires each registered constructor to go through the carrier,
|
|
13
13
|
* requires the published carriers (`ToolExecuteContext`, `ParentSpecSeat`) to declare exactly the rows they carry,
|
|
14
|
-
* and names any hand-written restricting projection outside the carriers
|
|
14
|
+
* and names any hand-written restricting projection outside the carriers (a field row's: the clamped member at its
|
|
15
|
+
* restricting value on an object, `memory: { enabled: false }`).
|
|
15
16
|
*
|
|
16
17
|
* "Tighten-only" here: the value that travels is the RESTRICTING one and only that one, so a parent that clamped nothing
|
|
17
18
|
* changes nothing about its children, and nothing a child can author lifts a clamp its parent ran under.
|
|
@@ -136,6 +137,13 @@ export declare const CLAMP_SET: Readonly<{
|
|
|
136
137
|
readonly script: false;
|
|
137
138
|
readonly carrier: "specSeat";
|
|
138
139
|
}>;
|
|
140
|
+
memory: Readonly<{
|
|
141
|
+
readonly law: "restricting";
|
|
142
|
+
readonly field: "enabled";
|
|
143
|
+
readonly value: false;
|
|
144
|
+
readonly script: false;
|
|
145
|
+
readonly carrier: "specSeat";
|
|
146
|
+
}>;
|
|
139
147
|
}>;
|
|
140
148
|
/** One row's seat name. */
|
|
141
149
|
export type ClampSeat = keyof typeof CLAMP_SET;
|
|
@@ -155,11 +163,19 @@ export type ClampSource = {
|
|
|
155
163
|
};
|
|
156
164
|
/** The clamp source a construction site hands the carrier: EVERY clamp-carrier row as a PRESENT key. */
|
|
157
165
|
export type ChildClampSource = Pick<ClampSource, ChildClampSeat>;
|
|
166
|
+
/** What one row hands on: its restricting value; an add-only row's list; a field row's restricting MEMBER as a partial
|
|
167
|
+
* declaration of the seat (`{ enabled: false }`) — never the whole declaration, which is the caller's own. */
|
|
168
|
+
type SeatTravels<K extends ClampSeat> = (typeof CLAMP_SET)[K] extends {
|
|
169
|
+
readonly field: infer F extends string;
|
|
170
|
+
readonly value: infer V;
|
|
171
|
+
} ? {
|
|
172
|
+
readonly [P in F]: V;
|
|
173
|
+
} : (typeof CLAMP_SET)[K] extends {
|
|
174
|
+
readonly value: infer V;
|
|
175
|
+
} ? V : NonNullable<TaskSpec[K]>;
|
|
158
176
|
/** Only the restricting values (add-only rows: a non-empty list) — the shape a waker's clamp seat and a projection take. */
|
|
159
177
|
export type ClampValues = {
|
|
160
|
-
readonly [K in ClampSeat]?:
|
|
161
|
-
readonly value: infer V;
|
|
162
|
-
} ? V : NonNullable<TaskSpec[K]>;
|
|
178
|
+
readonly [K in ClampSeat]?: SeatTravels<K>;
|
|
163
179
|
};
|
|
164
180
|
/** Every row's seat name, in table order. */
|
|
165
181
|
export declare const CLAMP_SEATS: readonly ClampSeat[];
|
|
@@ -170,7 +186,9 @@ export type ScriptSeat = {
|
|
|
170
186
|
/** {@link ScriptSeat}, in table order — the script whitelist's tighten-only half. */
|
|
171
187
|
export declare const SCRIPT_SEATS: readonly ScriptSeat[];
|
|
172
188
|
/** Two declarations of one seat under its row's law (`b` is the later one): the restricting value if either carries it,
|
|
173
|
-
* else what was authored (`b` over `a`); an add-only list is the union of both, in order, by the row's identity.
|
|
189
|
+
* else what was authored (`b` over `a`); an add-only list is the union of both, in order, by the row's identity. A field
|
|
190
|
+
* row's seat is a declaration the later author writes whole — what is composed is the one member the row clamps: the
|
|
191
|
+
* restricting value stands on the authored declaration if either side carries it, every other member is the author's. */
|
|
174
192
|
export declare function composeClampSeat(seat: ClampSeat, a: unknown, b: unknown): unknown;
|
|
175
193
|
/**
|
|
176
194
|
* THE union of an add-only seat's lists — the ONE merge every add-only row reads, wherever two of its lists meet: two
|
|
@@ -188,8 +206,13 @@ export declare function unionAddOnly<S extends AddOnlySeat>(seat: S, ...lists: R
|
|
|
188
206
|
* list is folded or compiled, so a task-layer value is judged as the caller wrote it. Absent ⇒ nothing to judge.
|
|
189
207
|
*/
|
|
190
208
|
export declare function assertAddOnlySeats(spec: object): void;
|
|
209
|
+
/** The name a refusal gives a seat's clamped cell: the seat, or (field row) `seat.member`. */
|
|
210
|
+
export declare function clampCellPath(seat: ClampSeat): string;
|
|
211
|
+
/** What a declaration of the seat states for the cell its row clamps (the value itself for a whole-value row). */
|
|
212
|
+
export declare function clampCellOf(seat: ClampSeat, declaration: unknown): unknown;
|
|
191
213
|
/** Would `override` LOOSEN `base` on this seat? Only a restricting row can be loosened: a base carrying the restricting
|
|
192
|
-
* value meeting an override that states a different one
|
|
214
|
+
* value meeting an override that states a different one (a field row reads the one member it clamps, so an override that
|
|
215
|
+
* declares the seat without stating that member loosens nothing). */
|
|
193
216
|
export declare function loosensClampSeat(seat: ClampSeat, base: unknown, override: unknown): boolean;
|
|
194
217
|
/** Read every row (`carrier`: that carrier's rows only) off a record that carries the seats under their own names (a spec, a ctx, a seat). */
|
|
195
218
|
export declare function clampSourceOf(record: object | undefined): ClampSource;
|
|
@@ -201,6 +224,22 @@ export declare function foldClampSources(...records: ReadonlyArray<object | unde
|
|
|
201
224
|
/** The restricting values of every row (`carrier` narrows it to one carrier's rows): a row with nothing restricting adds
|
|
202
225
|
* NO KEY, so the result spreads onto a spec without ever writing a loosening value. */
|
|
203
226
|
export declare function clampSeatsOf(source: Partial<Record<ClampSeat, unknown>>, carrier?: ClampCarrier): ClampValues;
|
|
227
|
+
/** The field rows' seats (object-valued seats, one member of each clamped). */
|
|
228
|
+
export type FieldSeat = {
|
|
229
|
+
[K in ClampSeat]: (typeof CLAMP_SET)[K] extends {
|
|
230
|
+
readonly field: string;
|
|
231
|
+
} ? K : never;
|
|
232
|
+
}[ClampSeat];
|
|
233
|
+
/** {@link clampSeatsOf} for the FIELD rows alone: what a field row hands on is a partial declaration, so it is written onto a
|
|
234
|
+
* spec by {@link applyClampSeats}, never spread over it. */
|
|
235
|
+
export declare function clampFieldSeatsOf(source: Partial<Record<ClampSeat, unknown>>): Pick<ClampValues, FieldSeat>;
|
|
236
|
+
/**
|
|
237
|
+
* Write clamp values onto a spec — the ONE way a projection lands on a record. A whole-value row lands as the value; a field
|
|
238
|
+
* row REWRITES the one member it clamps on the spec's own declaration and keeps every other member (a declaration that was
|
|
239
|
+
* deleted instead would leave the next mint nothing to read, and the clamp would end one level down). Returns a new object;
|
|
240
|
+
* `spec` is not mutated; a seat the values do not carry is left as the spec has it.
|
|
241
|
+
*/
|
|
242
|
+
export declare function applyClampSeats<S extends object>(spec: S, values: ClampValues): S;
|
|
204
243
|
/** The coded refusal of a clamp record a caller handed in (`config.invalid_argument`). */
|
|
205
244
|
export declare function clampRecordError(message: string): Error & {
|
|
206
245
|
code: "config.invalid_argument";
|
|
@@ -102,14 +102,31 @@ export const CLAMP_SET = Object.freeze({
|
|
|
102
102
|
memoryPersistenceCapable: Object.freeze({ law: "restricting", value: false, script: true, carrier: "specSeat" }),
|
|
103
103
|
fileHistory: Object.freeze({ law: "restricting", value: "disabled", script: false, carrier: "specSeat" }),
|
|
104
104
|
promptForm: Object.freeze({ law: "restricting", value: "minimal", script: false, carrier: "specSeat" }),
|
|
105
|
+
memory: Object.freeze({ law: "restricting", field: "enabled", value: false, script: false, carrier: "specSeat" }),
|
|
105
106
|
});
|
|
107
|
+
function fieldOf(row) {
|
|
108
|
+
return "field" in row ? row.field : undefined;
|
|
109
|
+
}
|
|
110
|
+
function cellOf(row, declaration) {
|
|
111
|
+
const field = fieldOf(row);
|
|
112
|
+
if (field === undefined)
|
|
113
|
+
return declaration;
|
|
114
|
+
return typeof declaration === "object" && declaration !== null && !Array.isArray(declaration) ? declaration[field] : undefined;
|
|
115
|
+
}
|
|
116
|
+
function withCell(row, declaration, cell) {
|
|
117
|
+
const field = fieldOf(row);
|
|
118
|
+
if (field === undefined)
|
|
119
|
+
return cell;
|
|
120
|
+
const own = typeof declaration === "object" && declaration !== null && !Array.isArray(declaration) ? declaration : {};
|
|
121
|
+
return { ...own, [field]: cell };
|
|
122
|
+
}
|
|
106
123
|
const ROWS = Object.freeze(Object.entries(CLAMP_SET));
|
|
107
124
|
export const CLAMP_SEATS = Object.freeze(ROWS.map(([seat]) => seat));
|
|
108
125
|
export const SCRIPT_SEATS = Object.freeze(ROWS.filter(([, row]) => row.script !== false).map(([seat]) => seat));
|
|
109
126
|
export function composeClampSeat(seat, a, b) {
|
|
110
127
|
const row = CLAMP_SET[seat];
|
|
111
128
|
if (row.law === "restricting")
|
|
112
|
-
return a === row.value || b === row.value ? row.value : (b ?? a);
|
|
129
|
+
return cellOf(row, a) === row.value || cellOf(row, b) === row.value ? withCell(row, b ?? a, row.value) : (b ?? a);
|
|
113
130
|
const lists = [a, b];
|
|
114
131
|
return unionAddOnly(seat, ...lists);
|
|
115
132
|
}
|
|
@@ -156,14 +173,28 @@ export function assertAddOnlySeats(spec) {
|
|
|
156
173
|
}
|
|
157
174
|
}
|
|
158
175
|
}
|
|
176
|
+
export function clampCellPath(seat) {
|
|
177
|
+
const field = fieldOf(CLAMP_SET[seat]);
|
|
178
|
+
return field === undefined ? seat : `${seat}.${field}`;
|
|
179
|
+
}
|
|
180
|
+
export function clampCellOf(seat, declaration) {
|
|
181
|
+
return cellOf(CLAMP_SET[seat], declaration);
|
|
182
|
+
}
|
|
159
183
|
export function loosensClampSeat(seat, base, override) {
|
|
160
184
|
const row = CLAMP_SET[seat];
|
|
161
|
-
|
|
185
|
+
if (row.law !== "restricting")
|
|
186
|
+
return false;
|
|
187
|
+
const stated = cellOf(row, override);
|
|
188
|
+
return cellOf(row, base) === row.value && stated !== undefined && stated !== row.value;
|
|
162
189
|
}
|
|
163
190
|
function projected(seat, v) {
|
|
164
191
|
const row = CLAMP_SET[seat];
|
|
165
|
-
if (row.law === "restricting")
|
|
166
|
-
|
|
192
|
+
if (row.law === "restricting") {
|
|
193
|
+
if (cellOf(row, v) !== row.value)
|
|
194
|
+
return undefined;
|
|
195
|
+
const travels = withCell(row, undefined, row.value);
|
|
196
|
+
return fieldOf(row) === undefined ? travels : Object.freeze(travels);
|
|
197
|
+
}
|
|
167
198
|
return Array.isArray(v) && v.length > 0 ? Object.freeze(v.map((e) => (typeof e === "object" && e !== null ? { ...e } : e))) : undefined;
|
|
168
199
|
}
|
|
169
200
|
export function clampSourceOf(record, carrier) {
|
|
@@ -184,6 +215,27 @@ export function clampSeatsOf(source, carrier) {
|
|
|
184
215
|
}
|
|
185
216
|
return out;
|
|
186
217
|
}
|
|
218
|
+
export function clampFieldSeatsOf(source) {
|
|
219
|
+
const out = {};
|
|
220
|
+
for (const [seat, row] of ROWS) {
|
|
221
|
+
if (fieldOf(row) === undefined)
|
|
222
|
+
continue;
|
|
223
|
+
const v = projected(seat, source[seat]);
|
|
224
|
+
if (v !== undefined)
|
|
225
|
+
out[seat] = v;
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
export function applyClampSeats(spec, values) {
|
|
230
|
+
const out = { ...spec };
|
|
231
|
+
for (const [seat, row] of ROWS) {
|
|
232
|
+
const v = values[seat];
|
|
233
|
+
if (v === undefined)
|
|
234
|
+
continue;
|
|
235
|
+
out[seat] = fieldOf(row) === undefined ? v : withCell(row, out[seat], cellOf(row, v));
|
|
236
|
+
}
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
187
239
|
export function clampRecordError(message) {
|
|
188
240
|
const e = new Error(message);
|
|
189
241
|
e.code = "config.invalid_argument";
|
package/dist/core/compliance.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
1
2
|
export const COMPLIANCE_CAPABILITIES = Object.freeze(["mcp_servers", "workflows", "web_fetch", "org_memory_mount"]);
|
|
2
3
|
export const BUILTIN_COMPLIANCE_DENIES = {
|
|
3
4
|
hipaa: ["web_fetch", "org_memory_mount"],
|
|
@@ -26,7 +27,8 @@ export function resolveComplianceDenies(posture) {
|
|
|
26
27
|
export function complianceCapabilityProblem(cap, owner) {
|
|
27
28
|
if (COMPLIANCE_CAPABILITIES.some((member) => member === cap))
|
|
28
29
|
return undefined;
|
|
29
|
-
|
|
30
|
+
const shown = (typeof cap === "object" && cap !== null) || typeof cap === "function" ? describeRejectedValue(cap) : `"${String(cap)}"`;
|
|
31
|
+
return (`${owner} contains ${shown}, not a member of the closed capability set ` +
|
|
30
32
|
`(${COMPLIANCE_CAPABILITIES.join(", ")}) — a misspelled deny must never silently not-enforce.`);
|
|
31
33
|
}
|
|
32
34
|
export const WEB_FETCH_TOOL_NAME = "WebFetch";
|
|
@@ -86,7 +86,7 @@ export declare function consolidateScope(scope: string, deps: ConsolidateScopeDe
|
|
|
86
86
|
* plain max) would lose `id > cursor` and be exiled from consolidation forever. Pass the pass's
|
|
87
87
|
* `stats.consolidatedIds` (processed), `stats.addedIds`, and `stats.failedIds`.
|
|
88
88
|
*
|
|
89
|
-
* No-op when the store lacks the cursor pair (back-compat: an old store keeps the pure-inline behavior).
|
|
89
|
+
* No-op when the store lacks the cursor pair (back-compat, RL-1654-consolidate-scope: an old store keeps the pure-inline behavior).
|
|
90
90
|
* Never throws — a cursor-advance failure routes to `onWarn` (the inline notes are already durable; the
|
|
91
91
|
* worst case is the next periodic pass re-sees them, which `runMemoryConsolidation` handles idempotently).
|
|
92
92
|
*/
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The device lane's CROSS-BOUNDARY vocabulary: the run tree's two taint bits and its posture fact, the legs one call
|
|
3
|
+
* travels, the one table those answer a call's obligation from, and the session record that carries a tree's state from
|
|
4
|
+
* one leg of a session to the next. Pure data and pure functions — no I/O, no clock, no engine state.
|
|
5
|
+
*
|
|
6
|
+
* THE RULE. A call's cross-boundary obligation is decided by three things and nothing else: the legs THIS call travels
|
|
7
|
+
* (declared by the code that dispatches it — a constant on most definitions, given per call by the two that route by
|
|
8
|
+
* their arguments), whether the environment a leg reaches declares itself the target device right now, and the tree's
|
|
9
|
+
* three bits. Two outcomes, independent of each other: `mandate` — a person confirms this call; `record` — once this
|
|
10
|
+
* call is allowed, the tree's off-target-read bit is set. {@link crossBoundaryObligation} is the one function that
|
|
11
|
+
* answers it; every place that asks calls it and none restates a cell.
|
|
12
|
+
*/
|
|
13
|
+
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
14
|
+
import type { EngineNotice } from "./engine-notice.js";
|
|
15
|
+
import type { HostDecider } from "./gate-outcome.js";
|
|
16
|
+
/** The run tree's two TAINT bits: the tree executes on a target device (`targetExecutionArmed`), and an off-target read
|
|
17
|
+
* was allowed into it since (`offTargetReadObserved`). Presence-coded on every carrier: `true` or absent, never `false`. */
|
|
18
|
+
export declare const CROSS_BOUNDARY_TAINT_BITS: readonly ["targetExecutionArmed", "offTargetReadObserved"];
|
|
19
|
+
export type CrossBoundaryTaintBit = (typeof CROSS_BOUNDARY_TAINT_BITS)[number];
|
|
20
|
+
/**
|
|
21
|
+
* The tree's state: the two taint bits and `governed` — the tree has run under the `multi_tenant` posture since it was
|
|
22
|
+
* first armed. `governed` is a posture fact, not a taint bit; it rides the same carriers and joins the same way. Each
|
|
23
|
+
* member is `true` or absent. The state only grows ({@link joinCrossBoundaryTree}).
|
|
24
|
+
*/
|
|
25
|
+
export interface CrossBoundaryTree {
|
|
26
|
+
readonly targetExecutionArmed?: true;
|
|
27
|
+
readonly offTargetReadObserved?: true;
|
|
28
|
+
readonly governed?: true;
|
|
29
|
+
}
|
|
30
|
+
/** The deployment's posture words (`RunnerDeps.crossBoundaryPosture`): who is hurt when a tree that executes on a device
|
|
31
|
+
* reaches past it — the device's own user (`single_user`), or an organization's other tenants (`multi_tenant`). */
|
|
32
|
+
export declare const CROSS_BOUNDARY_POSTURES: readonly ["single_user", "multi_tenant"];
|
|
33
|
+
export type CrossBoundaryPosture = (typeof CROSS_BOUNDARY_POSTURES)[number];
|
|
34
|
+
/**
|
|
35
|
+
* The legs one call can travel: `env` — into or out of an execution environment (the call's arguments reach it, or its
|
|
36
|
+
* content comes back from it); `outside_read` — content fetched from beyond the boundary; `outside_write` — an effect
|
|
37
|
+
* produced beyond the boundary. An empty set is a call whose effect stays inside the engine (its process or its session
|
|
38
|
+
* store).
|
|
39
|
+
*/
|
|
40
|
+
export declare const BOUNDARY_LEGS: readonly ["env", "outside_read", "outside_write"];
|
|
41
|
+
export type BoundaryLeg = (typeof BOUNDARY_LEGS)[number];
|
|
42
|
+
/** A definition's declaration of its legs: the legs every call travels, or `"per_call"` — the code that dispatches the call
|
|
43
|
+
* gives them per call (the destination is chosen by the call's own arguments). */
|
|
44
|
+
export type BoundaryLegsDeclaration = BoundaryLegList | "per_call";
|
|
45
|
+
/** A list of legs, in any order, each at most once on a roster row. */
|
|
46
|
+
export type BoundaryLegList = readonly BoundaryLeg[];
|
|
47
|
+
/** What one call owes: a person's confirmation (`mandate`), the off-target-read bit once allowed (`record`), both, or neither. */
|
|
48
|
+
export interface CrossBoundaryObligation {
|
|
49
|
+
readonly mandate: boolean;
|
|
50
|
+
readonly record: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** What a `"per_call"` definition's dispatch gives for one call: the legs it travels and, when its `env` leg reaches an
|
|
53
|
+
* environment other than the calling leg's own (a background shell's), that environment — its declaration is read as the call
|
|
54
|
+
* is judged. */
|
|
55
|
+
export interface BoundaryLegsGiven {
|
|
56
|
+
readonly legs: readonly BoundaryLeg[];
|
|
57
|
+
readonly env?: {
|
|
58
|
+
readonly externalContentTarget?: boolean;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
declare const CROSS_BOUNDARY_TREE_TOKEN: unique symbol;
|
|
62
|
+
/** A run tree's cross-boundary IDENTITY — opaque and frozen, minted by the engine alone: a tool is handed it so a delegation or
|
|
63
|
+
* workflow lane can join its children to the same tree and a registry handle can name the tree that spawned it, compared by
|
|
64
|
+
* identity. Nothing reads or writes the tree's state through it; the state stays the engine's. */
|
|
65
|
+
export interface CrossBoundaryTreeToken {
|
|
66
|
+
readonly [CROSS_BOUNDARY_TREE_TOKEN]: true;
|
|
67
|
+
}
|
|
68
|
+
/** A `"per_call"` definition's resolver, written beside the code that dispatches the call: handed the call's arguments as the
|
|
69
|
+
* tool reads them and the calling tree's {@link CrossBoundaryTreeToken} (a handle that recorded one compares it by identity). No I/O. */
|
|
70
|
+
export type BoundaryLegsFor = (args: unknown, at: {
|
|
71
|
+
readonly tree: CrossBoundaryTreeToken;
|
|
72
|
+
}) => BoundaryLegsGiven;
|
|
73
|
+
/** Why an exit refused a call its decision allowed: the call needs a person's confirmation under the tree's state NOW and the
|
|
74
|
+
* decision was not a person's (`unconfirmed`), or the session record the call owes could not be written before it ran
|
|
75
|
+
* (`unrecorded`). */
|
|
76
|
+
export interface CrossBoundaryExitRefusal {
|
|
77
|
+
readonly kind: "unconfirmed" | "unrecorded";
|
|
78
|
+
readonly message: string;
|
|
79
|
+
}
|
|
80
|
+
/** One call as the cross-boundary seat reads it: its wire name, its arguments as they would run, and its id (the trace names it). */
|
|
81
|
+
export interface CrossBoundaryCall {
|
|
82
|
+
readonly toolName: string;
|
|
83
|
+
readonly args: unknown;
|
|
84
|
+
readonly toolCallId?: string;
|
|
85
|
+
}
|
|
86
|
+
/** The gate's cross-boundary seat — ENGINE-BUILT per leg (`runner/cross-boundary-lane.ts`), never a deployment callback. */
|
|
87
|
+
export interface CrossBoundaryCheck {
|
|
88
|
+
/** This call's obligation under the tree's state at this moment (the station reads it on the final arguments). */
|
|
89
|
+
obligation(call: CrossBoundaryCall): CrossBoundaryObligation;
|
|
90
|
+
/** The allow exit's rule, on the bytes that run and the party that decided: re-reads the obligation; a mandate the decider cannot
|
|
91
|
+
* answer refuses; otherwise the off-target-read bit the call owes is joined and the session record is written before it runs. */
|
|
92
|
+
exit(call: CrossBoundaryCall, decidedBy: HostDecider): Promise<CrossBoundaryExitRefusal | undefined>;
|
|
93
|
+
}
|
|
94
|
+
/** The gate input's seat for {@link CrossBoundaryCheck}: present on a leg whose station is ENGAGED (a posture is declared, the leg runs
|
|
95
|
+
* on a target, or its tree is governed); absent ⇒ the decision path is byte-identical. */
|
|
96
|
+
export interface CrossBoundaryGateSeat {
|
|
97
|
+
crossBoundaryCheck?: CrossBoundaryCheck;
|
|
98
|
+
}
|
|
99
|
+
/** The table's rows. An `env` leg is judged by where it lands: a target environment is its own row, any other environment
|
|
100
|
+
* reads as both outside rows ({@link ENV_LEG_ROWS}). */
|
|
101
|
+
type LegRow = "env_to_target" | "outside_read" | "outside_write";
|
|
102
|
+
/** THE table. The host-lane column is empty, so a tree that never armed decides exactly as it would without this table. */
|
|
103
|
+
declare const OBLIGATION_TABLE: {
|
|
104
|
+
readonly env_to_target: {
|
|
105
|
+
readonly host_lane: "none";
|
|
106
|
+
readonly armed: "none";
|
|
107
|
+
readonly armed_observed: "mandate_if_governed";
|
|
108
|
+
};
|
|
109
|
+
readonly outside_read: {
|
|
110
|
+
readonly host_lane: "none";
|
|
111
|
+
readonly armed: "record";
|
|
112
|
+
readonly armed_observed: "none";
|
|
113
|
+
};
|
|
114
|
+
readonly outside_write: {
|
|
115
|
+
readonly host_lane: "none";
|
|
116
|
+
readonly armed: "mandate_if_governed";
|
|
117
|
+
readonly armed_observed: "mandate_if_governed";
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
export type ObligationTableCoversEveryRow = AssertAllKeysHandled<Exclude<LegRow, keyof typeof OBLIGATION_TABLE>>;
|
|
121
|
+
/** The obligation of one call — the union of its legs' cells under the tree's current state. `legs` are the legs this call
|
|
122
|
+
* travels (a definition's constant, or what its dispatch gave for this call); `reachedEnvIsTarget` — the environment its
|
|
123
|
+
* `env` leg reaches declares itself the target right now (read off that environment, never off the tree's history; unread
|
|
124
|
+
* when the call has no `env` leg). A leg outside {@link BOUNDARY_LEGS}, a non-boolean target fact or a malformed tree throws. */
|
|
125
|
+
export declare function crossBoundaryObligation(legs: readonly BoundaryLeg[], reachedEnvIsTarget: boolean, tree: CrossBoundaryTree): CrossBoundaryObligation;
|
|
126
|
+
/** The tree is GOVERNED — what the table's `mandate_if_governed` cells read, and so what makes the session record a carrier some
|
|
127
|
+
* judgment reads (a record that cannot be written refuses its call on a governed tree only). */
|
|
128
|
+
export declare function crossBoundaryTreeGoverned(tree: CrossBoundaryTree): boolean;
|
|
129
|
+
/** The join of two tree states: every bit either holds. Monotone — it never clears a bit — and returns a fresh frozen
|
|
130
|
+
* state, never one of its operands. */
|
|
131
|
+
export declare function joinCrossBoundaryTree(a: CrossBoundaryTree, b: CrossBoundaryTree): CrossBoundaryTree;
|
|
132
|
+
/** Whether `covering` holds every bit `tree` holds — a record that covers a tree's state need not be written again. */
|
|
133
|
+
export declare function crossBoundaryTreeCovers(covering: CrossBoundaryTree, tree: CrossBoundaryTree): boolean;
|
|
134
|
+
/** The strictest state: what a record that cannot be read stands for. */
|
|
135
|
+
export declare const STRICTEST_CROSS_BOUNDARY_TREE: CrossBoundaryTree;
|
|
136
|
+
/** The `customType` of the session entry that records a tree's state: a custom entry, persisted with the transcript and
|
|
137
|
+
* never replayed into model context — the state lives where the content it describes lives. */
|
|
138
|
+
export declare const CROSS_BOUNDARY_RECORD_TYPE = "engine:cross_boundary";
|
|
139
|
+
/** The record's `data`: written only for an armed tree, so `targetExecutionArmed` is always present. */
|
|
140
|
+
export interface CrossBoundaryRecordData {
|
|
141
|
+
readonly targetExecutionArmed: true;
|
|
142
|
+
readonly offTargetReadObserved?: true;
|
|
143
|
+
readonly governed?: true;
|
|
144
|
+
}
|
|
145
|
+
/** The one spelling of a record's `data` for a tree state. A tree that is not armed has no record: refused. */
|
|
146
|
+
export declare function crossBoundaryRecordData(tree: CrossBoundaryTree): CrossBoundaryRecordData;
|
|
147
|
+
/** What a branch says about its tree: no record on it; the join of its records (`entryId` = the newest one's); or a record — or an
|
|
148
|
+
* entry — that cannot be read, which reads as the strictest state, with the sentence a disclosure names. A record covers the content
|
|
149
|
+
* before it: `uncoveredContent` — conversation content (the session layer's `hasConversationContent`: a message, a custom message, a
|
|
150
|
+
* compaction) follows the newest record, or with no record is on the branch at all — is content produced under a state no record says. */
|
|
151
|
+
export type CrossBoundaryRecordRead = {
|
|
152
|
+
readonly kind: "absent";
|
|
153
|
+
readonly uncoveredContent: boolean;
|
|
154
|
+
} | {
|
|
155
|
+
readonly kind: "recorded";
|
|
156
|
+
readonly entryId?: string;
|
|
157
|
+
readonly tree: CrossBoundaryTree;
|
|
158
|
+
readonly uncoveredContent: boolean;
|
|
159
|
+
} | {
|
|
160
|
+
readonly kind: "malformed";
|
|
161
|
+
readonly entryId?: string;
|
|
162
|
+
readonly tree: CrossBoundaryTree;
|
|
163
|
+
readonly why: string;
|
|
164
|
+
};
|
|
165
|
+
/** The members a session entry is read by. */
|
|
166
|
+
export interface CrossBoundaryRecordEntry {
|
|
167
|
+
readonly type: string;
|
|
168
|
+
readonly id?: string;
|
|
169
|
+
readonly customType?: unknown;
|
|
170
|
+
readonly data?: unknown;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Read a branch's tree state: the JOIN of every cross-boundary record on `branch` (entries in path order) — the state only grows, so
|
|
174
|
+
* a later record holding fewer bits never lowers it. Any record whose shape is not a record's, and any entry that cannot be read
|
|
175
|
+
* (one of its members throws), makes the read {@link STRICTEST_CROSS_BOUNDARY_TREE}, never a looser state. No record reads `absent`.
|
|
176
|
+
*/
|
|
177
|
+
export declare function readCrossBoundaryRecord(branch: readonly CrossBoundaryRecordEntry[]): CrossBoundaryRecordRead;
|
|
178
|
+
/** The one mint of `config.cross_boundary_posture_undeclared`: an armed tree on a deployment that configured no posture, so
|
|
179
|
+
* the permissive `single_user` reading is in force. `detail.taint` lists the tree's taint bits. */
|
|
180
|
+
export declare function crossBoundaryPostureUndeclaredNotice(at: {
|
|
181
|
+
readonly sessionId: string;
|
|
182
|
+
readonly runId: string;
|
|
183
|
+
readonly tree: CrossBoundaryTree;
|
|
184
|
+
}): EngineNotice;
|
|
185
|
+
/** Where a session record's write was owed: a leg's prepare, an exit before its call runs, a suspend before its row, a settle. */
|
|
186
|
+
export type CrossBoundaryRecordPhase = "prepare" | "exit" | "suspend" | "settle"; /** The one mint of `config.cross_boundary_record_unwritten`: a session record that could not be written and refused nothing — on a tree
|
|
187
|
+
* that is not governed (no judgment reads the record), or at a governed leg's prepare / settle (no call to refuse; the next leg then
|
|
188
|
+
* reads the content after the newest record as off-target). A governed exit refuses its call and a governed suspend its row instead. */
|
|
189
|
+
export declare function crossBoundaryRecordUnwrittenNotice(at: {
|
|
190
|
+
readonly sessionId: string;
|
|
191
|
+
readonly runId: string;
|
|
192
|
+
readonly phase: CrossBoundaryRecordPhase;
|
|
193
|
+
readonly governed: boolean;
|
|
194
|
+
readonly cause: string;
|
|
195
|
+
}): EngineNotice;
|
|
196
|
+
/** The face a protocol tool (an MCP tool, an A2A skill) carries for its JUDGED `toolAxes` entry (`readToolAxes`' copy): the empty leg
|
|
197
|
+
* set when the entry declares `boundaryFlow: "engine"`, nothing otherwise (its roster row then states the legs its effect implies).
|
|
198
|
+
* The one translation of that declaration onto a mounted instance; the MCP intake and the A2A peer mint both call it. */
|
|
199
|
+
export declare function boundaryFlowFace(entry: {
|
|
200
|
+
readonly boundaryFlow?: "engine";
|
|
201
|
+
} | undefined): {
|
|
202
|
+
boundaryLegs?: readonly BoundaryLeg[];
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* The legs a ROSTER ROW states, from where the instance came from, what it declares and its resolved effect. The engine's
|
|
206
|
+
* own definitions (`builtin` / `synthetic` rows) and the protocol tools the engine materialized (`mcp` / `a2a`) are read by
|
|
207
|
+
* their declaration; any other row — and a row of those sources that declares nothing — by its effect: `read` reaches beyond
|
|
208
|
+
* the boundary to read, anything else to write. A declaration that is neither `"per_call"` nor a list of legs is refused on EVERY
|
|
209
|
+
* row, whoever declared it (`undefined` back, the caller refuses the row loudly): a row that does not read a declaration still does
|
|
210
|
+
* not let a bad one pass silently. The returned list is a fresh array in {@link BOUNDARY_LEGS} order.
|
|
211
|
+
*/
|
|
212
|
+
export declare function rosterBoundaryLegs(source: string, declared: unknown, effect: "read" | "write" | "idempotent"): BoundaryLegsDeclaration | undefined;
|
|
213
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { hasConversationContent } from "./session.js";
|
|
2
|
+
export const CROSS_BOUNDARY_TAINT_BITS = Object.freeze(["targetExecutionArmed", "offTargetReadObserved"]);
|
|
3
|
+
export const CROSS_BOUNDARY_POSTURES = Object.freeze(["single_user", "multi_tenant"]);
|
|
4
|
+
const postureSeatIsTheSet = true;
|
|
5
|
+
void postureSeatIsTheSet;
|
|
6
|
+
export const BOUNDARY_LEGS = Object.freeze(["env", "outside_read", "outside_write"]);
|
|
7
|
+
const LEG_SET = new Set(BOUNDARY_LEGS);
|
|
8
|
+
const OBLIGATION_TABLE = {
|
|
9
|
+
env_to_target: { host_lane: "none", armed: "none", armed_observed: "mandate_if_governed" },
|
|
10
|
+
outside_read: { host_lane: "none", armed: "record", armed_observed: "none" },
|
|
11
|
+
outside_write: { host_lane: "none", armed: "mandate_if_governed", armed_observed: "mandate_if_governed" },
|
|
12
|
+
};
|
|
13
|
+
const ENV_LEG_ROWS = { target: ["env_to_target"], off_target: ["outside_read", "outside_write"] };
|
|
14
|
+
const TREE_MEMBERS = [...CROSS_BOUNDARY_TAINT_BITS, "governed"];
|
|
15
|
+
function assertTree(tree, at) {
|
|
16
|
+
if (typeof tree !== "object" || tree === null || Array.isArray(tree))
|
|
17
|
+
throw new TypeError(`${at}: a cross-boundary tree is a record of presence bits (got ${tree === null ? "null" : Array.isArray(tree) ? "an array" : typeof tree})`);
|
|
18
|
+
for (const member of TREE_MEMBERS) {
|
|
19
|
+
const v = tree[member];
|
|
20
|
+
if (v !== undefined && v !== true)
|
|
21
|
+
throw new TypeError(`${at}: tree member "${member}" is presence-coded (true or absent), got ${typeof v}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function crossBoundaryObligation(legs, reachedEnvIsTarget, tree) {
|
|
25
|
+
if (!Array.isArray(legs))
|
|
26
|
+
throw new TypeError(`crossBoundaryObligation: legs is a list of ${BOUNDARY_LEGS.join(" | ")}`);
|
|
27
|
+
if (typeof reachedEnvIsTarget !== "boolean")
|
|
28
|
+
throw new TypeError("crossBoundaryObligation: reachedEnvIsTarget is a boolean");
|
|
29
|
+
assertTree(tree, "crossBoundaryObligation");
|
|
30
|
+
const column = tree.targetExecutionArmed !== true ? "host_lane" : tree.offTargetReadObserved === true ? "armed_observed" : "armed";
|
|
31
|
+
let mandate = false;
|
|
32
|
+
let record = false;
|
|
33
|
+
for (const leg of legs) {
|
|
34
|
+
if (!LEG_SET.has(leg))
|
|
35
|
+
throw new TypeError(`crossBoundaryObligation: ${JSON.stringify(leg)} is not a boundary leg (${BOUNDARY_LEGS.join(", ")})`);
|
|
36
|
+
const rows = leg === "env" ? ENV_LEG_ROWS[reachedEnvIsTarget ? "target" : "off_target"] : [leg];
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
const cell = OBLIGATION_TABLE[row][column];
|
|
39
|
+
if (cell === "record")
|
|
40
|
+
record = true;
|
|
41
|
+
else if (cell === "mandate_if_governed" && crossBoundaryTreeGoverned(tree))
|
|
42
|
+
mandate = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return Object.freeze({ mandate, record });
|
|
46
|
+
}
|
|
47
|
+
export function crossBoundaryTreeGoverned(tree) {
|
|
48
|
+
return tree.governed === true;
|
|
49
|
+
}
|
|
50
|
+
export function joinCrossBoundaryTree(a, b) {
|
|
51
|
+
assertTree(a, "joinCrossBoundaryTree");
|
|
52
|
+
assertTree(b, "joinCrossBoundaryTree");
|
|
53
|
+
const out = {};
|
|
54
|
+
if (a.targetExecutionArmed === true || b.targetExecutionArmed === true)
|
|
55
|
+
out.targetExecutionArmed = true;
|
|
56
|
+
if (a.offTargetReadObserved === true || b.offTargetReadObserved === true)
|
|
57
|
+
out.offTargetReadObserved = true;
|
|
58
|
+
if (a.governed === true || b.governed === true)
|
|
59
|
+
out.governed = true;
|
|
60
|
+
return Object.freeze(out);
|
|
61
|
+
}
|
|
62
|
+
export function crossBoundaryTreeCovers(covering, tree) {
|
|
63
|
+
assertTree(covering, "crossBoundaryTreeCovers");
|
|
64
|
+
assertTree(tree, "crossBoundaryTreeCovers");
|
|
65
|
+
return TREE_MEMBERS.every((m) => tree[m] !== true || covering[m] === true);
|
|
66
|
+
}
|
|
67
|
+
export const STRICTEST_CROSS_BOUNDARY_TREE = Object.freeze({ targetExecutionArmed: true, offTargetReadObserved: true, governed: true });
|
|
68
|
+
export const CROSS_BOUNDARY_RECORD_TYPE = "engine:cross_boundary";
|
|
69
|
+
export function crossBoundaryRecordData(tree) {
|
|
70
|
+
assertTree(tree, "crossBoundaryRecordData");
|
|
71
|
+
if (tree.targetExecutionArmed !== true)
|
|
72
|
+
throw new TypeError("crossBoundaryRecordData: only an armed tree is recorded");
|
|
73
|
+
return Object.freeze({ targetExecutionArmed: true, ...(tree.offTargetReadObserved === true ? { offTargetReadObserved: true } : {}), ...(tree.governed === true ? { governed: true } : {}) });
|
|
74
|
+
}
|
|
75
|
+
const RECORD_MEMBERS = TREE_MEMBERS;
|
|
76
|
+
function ownedRecordTree(data) {
|
|
77
|
+
if (typeof data !== "object" || data === null || Array.isArray(data) || Object.getPrototypeOf(data) !== Object.prototype)
|
|
78
|
+
return { why: `its data is ${data === null ? "null" : Array.isArray(data) ? "an array" : typeof data === "object" ? "not a plain record" : typeof data}` };
|
|
79
|
+
const owned = new Map();
|
|
80
|
+
for (const key of Reflect.ownKeys(data)) {
|
|
81
|
+
if (typeof key !== "string" || !RECORD_MEMBERS.includes(key))
|
|
82
|
+
return { why: `its data carries ${typeof key === "string" ? JSON.stringify(key.slice(0, 40)) : "a symbol key"}, which is not a member` };
|
|
83
|
+
const d = Object.getOwnPropertyDescriptor(data, key);
|
|
84
|
+
if (d === undefined || !("value" in d))
|
|
85
|
+
return { why: `"${key}" is not a data member` };
|
|
86
|
+
owned.set(key, d.value);
|
|
87
|
+
}
|
|
88
|
+
if (owned.get("targetExecutionArmed") !== true)
|
|
89
|
+
return { why: `"targetExecutionArmed" is not true` };
|
|
90
|
+
for (const m of ["offTargetReadObserved", "governed"])
|
|
91
|
+
if (owned.get(m) !== undefined && owned.get(m) !== true)
|
|
92
|
+
return { why: `"${m}" is neither true nor absent` };
|
|
93
|
+
return { tree: Object.freeze({ targetExecutionArmed: true, ...(owned.get("offTargetReadObserved") === true ? { offTargetReadObserved: true } : {}), ...(owned.get("governed") === true ? { governed: true } : {}) }) };
|
|
94
|
+
}
|
|
95
|
+
function readBranchEntry(branch, i) {
|
|
96
|
+
let id;
|
|
97
|
+
try {
|
|
98
|
+
const e = branch[i];
|
|
99
|
+
if (typeof e !== "object" || e === null)
|
|
100
|
+
return { kind: "other", content: false };
|
|
101
|
+
const { type, customType } = e;
|
|
102
|
+
if (type !== "custom" || customType !== CROSS_BOUNDARY_RECORD_TYPE)
|
|
103
|
+
return { kind: "other", content: typeof type === "string" && hasConversationContent([{ type }]) };
|
|
104
|
+
const rawId = e.id;
|
|
105
|
+
id = typeof rawId === "string" ? rawId : undefined;
|
|
106
|
+
const read = ownedRecordTree(e.data);
|
|
107
|
+
return "why" in read ? { kind: "unreadable", ...(id !== undefined ? { id } : {}), why: read.why } : { kind: "record", ...(id !== undefined ? { id } : {}), tree: read.tree };
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return { kind: "unreadable", ...(id !== undefined ? { id } : {}), why: "an entry on the branch could not be read" };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export function readCrossBoundaryRecord(branch) {
|
|
114
|
+
const strictest = (id, why) => Object.freeze({ kind: "malformed", ...(id !== undefined ? { entryId: id } : {}), tree: STRICTEST_CROSS_BOUNDARY_TREE, why: `the cross-boundary record on this branch cannot be read: ${why}` });
|
|
115
|
+
let isList;
|
|
116
|
+
let length;
|
|
117
|
+
try {
|
|
118
|
+
isList = Array.isArray(branch);
|
|
119
|
+
length = isList ? branch.length : undefined;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return strictest(undefined, "the branch could not be read");
|
|
123
|
+
}
|
|
124
|
+
if (!isList)
|
|
125
|
+
throw new TypeError("readCrossBoundaryRecord: the branch is a list of session entries");
|
|
126
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0)
|
|
127
|
+
return strictest(undefined, "the branch could not be read");
|
|
128
|
+
let joined;
|
|
129
|
+
let newest;
|
|
130
|
+
let uncoveredContent = false;
|
|
131
|
+
for (let i = 0; i < length; i++) {
|
|
132
|
+
const entry = readBranchEntry(branch, i);
|
|
133
|
+
if (entry.kind === "other") {
|
|
134
|
+
uncoveredContent ||= entry.content;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (entry.kind === "unreadable")
|
|
138
|
+
return strictest(entry.id, entry.why);
|
|
139
|
+
joined = joinCrossBoundaryTree(joined ?? {}, entry.tree);
|
|
140
|
+
newest = entry.id;
|
|
141
|
+
uncoveredContent = false;
|
|
142
|
+
}
|
|
143
|
+
return joined === undefined ? Object.freeze({ kind: "absent", uncoveredContent }) : Object.freeze({ kind: "recorded", ...(newest !== undefined ? { entryId: newest } : {}), tree: joined, uncoveredContent });
|
|
144
|
+
}
|
|
145
|
+
export function crossBoundaryPostureUndeclaredNotice(at) {
|
|
146
|
+
assertTree(at.tree, "crossBoundaryPostureUndeclaredNotice");
|
|
147
|
+
return {
|
|
148
|
+
code: "config.cross_boundary_posture_undeclared",
|
|
149
|
+
message: 'This run executes on an external-content target (the device lane) and no RunnerDeps.crossBoundaryPosture is configured — the engine reads the permissive "single_user" posture. ' +
|
|
150
|
+
'On a multi-tenant deployment declare "multi_tenant", so an org-side write from a tainted run is put to a person; on a single-user deployment declare "single_user" to silence this line.',
|
|
151
|
+
detail: { sessionId: at.sessionId, runId: at.runId, taint: CROSS_BOUNDARY_TAINT_BITS.filter((b) => at.tree[b] === true) },
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function crossBoundaryRecordUnwrittenNotice(at) {
|
|
155
|
+
return {
|
|
156
|
+
code: "config.cross_boundary_record_unwritten",
|
|
157
|
+
message: `The session's cross-boundary record could not be written at ${at.phase} (${at.cause.slice(0, 300)}) — ` +
|
|
158
|
+
(at.governed
|
|
159
|
+
? "the tree is governed: the next leg on this session reads the content after its newest record as off-target, so its device calls are put to a person."
|
|
160
|
+
: "the tree is not governed: no judgment reads the record, so its failure refused nothing; the tree's growth, or the leg's end, writes next."),
|
|
161
|
+
detail: { sessionId: at.sessionId, runId: at.runId, phase: at.phase, governed: at.governed, cause: at.cause.slice(0, 300) },
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const NO_LEGS = Object.freeze([]);
|
|
165
|
+
export function boundaryFlowFace(entry) {
|
|
166
|
+
return entry?.boundaryFlow === "engine" ? { boundaryLegs: NO_LEGS } : {};
|
|
167
|
+
}
|
|
168
|
+
function ownedLegList(v) {
|
|
169
|
+
try {
|
|
170
|
+
if (!Array.isArray(v))
|
|
171
|
+
return undefined;
|
|
172
|
+
const length = v.length;
|
|
173
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0)
|
|
174
|
+
return undefined;
|
|
175
|
+
const held = new Set();
|
|
176
|
+
for (let i = 0; i < length; i++) {
|
|
177
|
+
const leg = v[i];
|
|
178
|
+
if (!LEG_SET.has(leg))
|
|
179
|
+
return undefined;
|
|
180
|
+
held.add(leg);
|
|
181
|
+
}
|
|
182
|
+
return BOUNDARY_LEGS.filter((l) => held.has(l));
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export function rosterBoundaryLegs(source, declared, effect) {
|
|
189
|
+
const list = declared === undefined || declared === "per_call" ? undefined : ownedLegList(declared);
|
|
190
|
+
if (declared !== undefined && declared !== "per_call" && list === undefined)
|
|
191
|
+
return undefined;
|
|
192
|
+
const reads = source === "builtin" || source === "synthetic" || source === "mcp" || source === "a2a";
|
|
193
|
+
if (!reads || declared === undefined)
|
|
194
|
+
return effect === "read" ? ["outside_read"] : ["outside_write"];
|
|
195
|
+
return declared === "per_call" ? "per_call" : list;
|
|
196
|
+
}
|