@sema-agent/core 7.54.1 → 7.56.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 +950 -0
- package/dist/agents/agent-transcript-tool.js +2 -1
- package/dist/agents/builtin-agents.d.ts +12 -14
- package/dist/agents/cascade.js +1 -1
- package/dist/agents/cc-peer-directory.d.ts +36 -0
- package/dist/agents/cc-peer-directory.js +139 -0
- package/dist/agents/cc-peer-wire.d.ts +105 -0
- package/dist/agents/cc-peer-wire.js +98 -0
- package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
- package/dist/agents/cc-uds-peer-channel.js +457 -0
- package/dist/agents/child-model-seat.d.ts +0 -11
- package/dist/agents/child-model-seat.js +0 -3
- package/dist/agents/cross-session-envelope.d.ts +4 -1
- package/dist/agents/cross-session-envelope.js +8 -1
- package/dist/agents/cross-session-judge.d.ts +2 -2
- package/dist/agents/list-agents-tool.d.ts +3 -1
- package/dist/agents/list-agents-tool.js +9 -8
- package/dist/agents/observer.d.ts +4 -5
- package/dist/agents/observer.js +2 -1
- package/dist/agents/peer-admission.d.ts +6 -0
- package/dist/agents/peer-admission.js +4 -1
- package/dist/agents/peer-directory.d.ts +65 -13
- package/dist/agents/peer-directory.js +90 -25
- package/dist/agents/peer-notices.d.ts +1 -1
- package/dist/agents/peer-session-drain.d.ts +3 -1
- package/dist/agents/peer-session-drain.js +3 -2
- package/dist/agents/read-inbox-tool.js +2 -1
- package/dist/agents/repair-loop.js +2 -2
- package/dist/agents/retain-ledger.d.ts +1 -1
- package/dist/agents/roster-store.d.ts +1 -1
- package/dist/agents/send-message-tool.d.ts +10 -0
- package/dist/agents/send-message-tool.js +37 -8
- package/dist/agents/subagent.d.ts +39 -82
- package/dist/agents/subagent.js +109 -106
- package/dist/agents/tool-filter.d.ts +31 -46
- package/dist/agents/tool-filter.js +13 -33
- package/dist/agents/verify.d.ts +6 -5
- package/dist/agents/verify.js +3 -3
- package/dist/brain/anthropic.js +43 -81
- package/dist/brain/errors.d.ts +15 -0
- package/dist/brain/errors.js +10 -0
- package/dist/brain/media-degrade.d.ts +18 -7
- package/dist/brain/media-degrade.js +13 -2
- package/dist/brain/model-capability.d.ts +37 -0
- package/dist/brain/model-capability.js +34 -0
- package/dist/brain/model-presets.d.ts +6 -12
- package/dist/brain/model-presets.js +1 -6
- package/dist/brain/open-responses.js +27 -35
- package/dist/brain/openai.js +30 -98
- package/dist/brain/provider-dialect.d.ts +39 -3
- package/dist/brain/provider-dialect.js +28 -0
- package/dist/brain/reasoning-request.d.ts +96 -0
- package/dist/brain/reasoning-request.js +174 -0
- package/dist/brain/reasoning.d.ts +12 -12
- package/dist/brain/reasoning.js +4 -3
- package/dist/brain/request-params.d.ts +62 -9
- package/dist/brain/request-params.js +42 -7
- package/dist/brain/retry.d.ts +1 -1
- package/dist/brain/route-adjudicator.d.ts +15 -0
- package/dist/brain/route-adjudicator.js +15 -0
- package/dist/brain/status-sink.d.ts +20 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/brain/stream-engine.js +6 -5
- package/dist/brain/stream-shared.d.ts +1 -1
- package/dist/core/a2a.d.ts +2 -1
- package/dist/core/a2a.js +19 -13
- package/dist/core/advisor-verdict.d.ts +120 -0
- package/dist/core/advisor-verdict.js +92 -0
- package/dist/core/agent-definition.d.ts +17 -8
- package/dist/core/allow-entry.d.ts +69 -36
- package/dist/core/allow-entry.js +81 -52
- package/dist/core/allow-rule-shadow.d.ts +10 -3
- package/dist/core/approval-object.d.ts +2 -1
- package/dist/core/approval-object.js +6 -6
- package/dist/core/ask-origin.d.ts +127 -29
- package/dist/core/ask-origin.js +87 -25
- package/dist/core/ask-question.d.ts +51 -8
- package/dist/core/ask-question.js +12 -7
- package/dist/core/ask-unresolvable-notice.d.ts +8 -3
- package/dist/core/ask-unresolvable-notice.js +16 -2
- package/dist/core/auto-compaction.d.ts +11 -8
- package/dist/core/auto-compaction.js +4 -2
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/auto-mode-defaults.d.ts +8 -8
- package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
- package/dist/core/auto-mode-prompt.d.ts +1 -1
- package/dist/core/auto-mode.d.ts +17 -9
- package/dist/core/auto-mode.js +9 -6
- package/dist/core/background-agent-store.d.ts +3 -3
- package/dist/core/canonical-json.d.ts +1 -1
- package/dist/core/canonical-json.js +2 -2
- package/dist/core/checkpoint-generation.d.ts +53 -0
- package/dist/core/checkpoint-generation.js +35 -0
- package/dist/core/checkpoint-queries.d.ts +180 -0
- package/dist/core/checkpoint-queries.js +272 -0
- package/dist/core/checkpoint-requirements.d.ts +19 -4
- package/dist/core/checkpoint-requirements.js +8 -14
- package/dist/core/checkpoint-store.d.ts +165 -72
- package/dist/core/checkpoint-store.js +79 -82
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +3 -16
- package/dist/core/compliance.d.ts +2 -2
- package/dist/core/context-edit.d.ts +6 -7
- package/dist/core/cross-boundary-taint.d.ts +11 -4
- package/dist/core/cross-boundary-taint.js +3 -3
- package/dist/core/decision-receipt.d.ts +37 -3
- package/dist/core/decision-receipt.js +19 -19
- package/dist/core/delegation-not-started.d.ts +6 -0
- package/dist/core/delegation-not-started.js +17 -0
- package/dist/core/effective-path-target.d.ts +1 -1
- package/dist/core/engine-attestation.d.ts +93 -0
- package/dist/core/engine-attestation.js +70 -0
- package/dist/core/engine-capabilities.d.ts +4 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +45 -12
- package/dist/core/exec-output-tail.d.ts +1 -1
- package/dist/core/file-history-retention.d.ts +1 -1
- package/dist/core/file-history-store.d.ts +147 -21
- package/dist/core/file-history-store.js +58 -32
- package/dist/core/gate-fold.d.ts +1 -1
- package/dist/core/gate-fold.js +24 -11
- package/dist/core/gate-lanes.js +81 -68
- package/dist/core/gate-outcome.d.ts +40 -6
- package/dist/core/gate-outcome.js +56 -0
- package/dist/core/governance-codes.d.ts +17 -3
- package/dist/core/governance-codes.js +16 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +21 -20
- package/dist/core/hooks.js +3 -1
- package/dist/core/host-floor.d.ts +128 -0
- package/dist/core/host-floor.js +206 -0
- package/dist/core/host-target.d.ts +84 -0
- package/dist/core/host-target.js +92 -0
- package/dist/core/image-downsample.d.ts +1 -1
- package/dist/core/lsp-diagnostics.d.ts +4 -4
- package/dist/core/lsp.d.ts +3 -3
- package/dist/core/mailbox-store.d.ts +4 -0
- package/dist/core/mcp-connection-pool.d.ts +18 -2
- package/dist/core/mcp-connection-pool.js +53 -6
- package/dist/core/mcp-failure.d.ts +23 -4
- package/dist/core/mcp-failure.js +26 -11
- package/dist/core/mcp-listing-drift.d.ts +10 -0
- package/dist/core/mcp-listing-drift.js +6 -0
- package/dist/core/mcp-server-link.d.ts +173 -0
- package/dist/core/mcp-server-link.js +127 -0
- package/dist/core/mcp-server-spec.d.ts +4 -2
- package/dist/core/mcp-session-end.d.ts +26 -0
- package/dist/core/mcp-session-end.js +29 -0
- package/dist/core/mcp-startup-request.d.ts +17 -0
- package/dist/core/mcp-startup-request.js +17 -0
- package/dist/core/mcp.d.ts +17 -13
- package/dist/core/mcp.js +240 -189
- package/dist/core/media-byte-cap.d.ts +1 -1
- package/dist/core/memory-engine/delegation-settlement.js +15 -24
- package/dist/core/memory-engine/engine.d.ts +9 -9
- package/dist/core/memory-engine/engine.js +4 -4
- package/dist/core/memory-engine/layout.d.ts +60 -8
- package/dist/core/memory-engine/layout.js +65 -6
- package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
- package/dist/core/memory-engine/tools.js +4 -1
- package/dist/core/mode-policy.d.ts +1 -1
- package/dist/core/model-seat.d.ts +10 -3
- package/dist/core/mods/classic-events.d.ts +67 -0
- package/dist/core/mods/classic-events.js +1 -0
- package/dist/core/mods/dollar.d.ts +13 -0
- package/dist/core/mods/extension-call.d.ts +31 -0
- package/dist/core/mods/extension-call.js +43 -0
- package/dist/core/mods/kernel/dispatch.d.ts +20 -3
- package/dist/core/mods/kernel/dispatch.js +140 -69
- package/dist/core/mods/kernel/events.d.ts +47 -11
- package/dist/core/mods/kernel/events.js +66 -42
- package/dist/core/mods/kernel/index.d.ts +4 -4
- package/dist/core/mods/kernel/index.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +39 -5
- package/dist/core/mods/kernel/receive.js +290 -17
- package/dist/core/mods/kernel/registration.d.ts +33 -2
- package/dist/core/mods/kernel/registration.js +6 -7
- package/dist/core/mods/kernel/site-rules.d.ts +44 -16
- package/dist/core/mods/kernel/site-rules.js +24 -42
- package/dist/core/mods/spec-seats.d.ts +69 -0
- package/dist/core/mods/spec-seats.js +114 -0
- package/dist/core/mods/stations.d.ts +28 -8
- package/dist/core/mods/stations.js +29 -13
- package/dist/core/mods/tool-call.d.ts +79 -0
- package/dist/core/mods/tool-call.js +152 -0
- package/dist/core/namespaced-rule-name.d.ts +44 -24
- package/dist/core/namespaced-rule-name.js +50 -43
- package/dist/core/output-style.d.ts +1 -1
- package/dist/core/param-rule-reach.d.ts +15 -0
- package/dist/core/param-rule-reach.js +30 -0
- package/dist/core/parent-spec-seat.d.ts +13 -1
- package/dist/core/parent-spec-seat.js +6 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/pause-registry.d.ts +16 -5
- package/dist/core/pause-registry.js +6 -2
- package/dist/core/peer-channel.d.ts +70 -0
- package/dist/core/peer-channel.js +1 -0
- package/dist/core/permission-rule-consent.d.ts +30 -5
- package/dist/core/permission-rule-consent.js +147 -50
- package/dist/core/permission-rule-model.d.ts +245 -65
- package/dist/core/permission-rule-model.js +421 -107
- package/dist/core/permission-rule-org.d.ts +6 -2
- package/dist/core/permission-rule-org.js +7 -3
- package/dist/core/permission-rule-provider.d.ts +6 -2
- package/dist/core/permission-rule-provider.js +41 -11
- package/dist/core/permission-rule-store.d.ts +22 -26
- package/dist/core/permission-rule-store.js +12 -6
- package/dist/core/permission-rule-sync.d.ts +11 -2
- package/dist/core/permission-rule-sync.js +11 -3
- package/dist/core/permission-rule-syntax.d.ts +5 -4
- package/dist/core/permission-rule-syntax.js +2 -2
- package/dist/core/permission-rules.d.ts +53 -15
- package/dist/core/permission-rules.js +125 -100
- package/dist/core/persisted-rule-arms.d.ts +6 -4
- package/dist/core/persisted-rule-arms.js +10 -12
- package/dist/core/present-plan-tool.js +3 -1
- package/dist/core/principal-name.d.ts +20 -0
- package/dist/core/principal-name.js +22 -0
- package/dist/core/prompt-section-seat.js +4 -6
- package/dist/core/protocol-http-auth.d.ts +26 -5
- package/dist/core/protocol-http-auth.js +137 -4
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/read-only-shell-table.d.ts +1 -1
- package/dist/core/read-only-shell.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +16 -0
- package/dist/core/retired-keys.js +16 -4
- package/dist/core/rewrite-rejudge.d.ts +60 -0
- package/dist/core/rewrite-rejudge.js +36 -0
- package/dist/core/roles.d.ts +34 -3
- package/dist/core/roles.js +66 -53
- package/dist/core/rule-lane-reads.d.ts +20 -15
- package/dist/core/rule-lane-reads.js +45 -17
- package/dist/core/rule-offer-read.d.ts +53 -0
- package/dist/core/rule-offer-read.js +146 -0
- package/dist/core/rule-owner.d.ts +46 -0
- package/dist/core/rule-owner.js +29 -0
- package/dist/core/runner/advisor-instructions.d.ts +70 -0
- package/dist/core/runner/advisor-instructions.js +61 -0
- package/dist/core/runner/advisor-tool.d.ts +33 -0
- package/dist/core/runner/advisor-tool.js +112 -0
- package/dist/core/runner/announce-once-ledger.d.ts +18 -26
- package/dist/core/runner/announce-once-ledger.js +19 -46
- package/dist/core/runner/approval-feedback.js +6 -3
- package/dist/core/runner/assemble-result.d.ts +2 -1
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/call-hook-stations.d.ts +3 -2
- package/dist/core/runner/caller-roster.d.ts +21 -2
- package/dist/core/runner/caller-roster.js +38 -0
- package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
- package/dist/core/runner/capability-withheld-seat.js +24 -0
- package/dist/core/runner/classifier-ladder.d.ts +33 -13
- package/dist/core/runner/classifier-ladder.js +15 -14
- package/dist/core/runner/compaction-restate.d.ts +10 -6
- package/dist/core/runner/compaction-restate.js +9 -3
- package/dist/core/runner/construction-doors.js +3 -0
- package/dist/core/runner/contracts.d.ts +40 -33
- package/dist/core/runner/conversation-segment.d.ts +28 -0
- package/dist/core/runner/conversation-segment.js +42 -0
- package/dist/core/runner/cross-boundary-lane.d.ts +21 -16
- package/dist/core/runner/cross-boundary-lane.js +58 -34
- package/dist/core/runner/decide-continuation.d.ts +7 -4
- package/dist/core/runner/decide-continuation.js +2 -2
- package/dist/core/runner/denial-limit-arms.d.ts +4 -4
- package/dist/core/runner/denial-limit-arms.js +1 -1
- package/dist/core/runner/derived-route-fallback.d.ts +24 -24
- package/dist/core/runner/derived-route-fallback.js +9 -7
- package/dist/core/runner/file-history-request.d.ts +18 -0
- package/dist/core/runner/file-history-request.js +21 -13
- package/dist/core/runner/file-history-restore.d.ts +45 -0
- package/dist/core/runner/file-history-restore.js +46 -0
- package/dist/core/runner/gate-exit.d.ts +9 -3
- package/dist/core/runner/gate-exit.js +11 -5
- package/dist/core/runner/image.d.ts +7 -17
- package/dist/core/runner/image.js +9 -60
- package/dist/core/runner/initial-run-state.js +1 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +6 -6
- package/dist/core/runner/permission-rule-lanes.js +38 -15
- package/dist/core/runner/person-path-rows.d.ts +12 -9
- package/dist/core/runner/person-path-rows.js +19 -8
- package/dist/core/runner/prepare-advisor.d.ts +63 -0
- package/dist/core/runner/prepare-advisor.js +77 -0
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
- package/dist/core/runner/prepare-ask-lane.js +5 -2
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
- package/dist/core/runner/prepare-caps-and-workflow.js +5 -4
- package/dist/core/runner/prepare-config-doors.d.ts +31 -38
- package/dist/core/runner/prepare-config-doors.js +57 -34
- package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
- package/dist/core/runner/prepare-defer-classify.js +7 -8
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +1 -1
- package/dist/core/runner/prepare-file-history.d.ts +48 -8
- package/dist/core/runner/prepare-file-history.js +41 -73
- package/dist/core/runner/prepare-gate-stations.d.ts +2 -5
- package/dist/core/runner/prepare-gate-stations.js +12 -8
- package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
- package/dist/core/runner/prepare-hands-readface.js +15 -14
- package/dist/core/runner/prepare-inherited-gate.d.ts +11 -7
- package/dist/core/runner/prepare-inherited-gate.js +8 -6
- package/dist/core/runner/prepare-memory-engine-session.js +1 -1
- package/dist/core/runner/prepare-park-ask.js +12 -12
- package/dist/core/runner/prepare-policy-chain.d.ts +7 -4
- package/dist/core/runner/prepare-policy-chain.js +68 -81
- package/dist/core/runner/prepare-project-context.d.ts +5 -2
- package/dist/core/runner/prepare-project-context.js +6 -3
- package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
- package/dist/core/runner/prepare-prompt-assembly.js +10 -8
- package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
- package/dist/core/runner/prepare-prompt-inputs.js +4 -6
- package/dist/core/runner/prepare-protocol-tools.d.ts +4 -2
- package/dist/core/runner/prepare-protocol-tools.js +5 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
- package/dist/core/runner/prepare-safety-scan.js +8 -23
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
- package/dist/core/runner/prepare-suspend-saga.js +5 -6
- package/dist/core/runner/prepare-task.d.ts +1 -1
- package/dist/core/runner/prepare-task.js +41 -35
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
- package/dist/core/runner/prepare-tool-disclosure-mount.js +24 -18
- package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
- package/dist/core/runner/prepare-turn-wiring.js +2 -7
- package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
- package/dist/core/runner/prepare-wiring-manifest.js +3 -3
- package/dist/core/runner/resume-admission.js +1 -1
- package/dist/core/runner/resume-apply.js +11 -12
- package/dist/core/runner/resume-checkpoint-screen.js +3 -0
- package/dist/core/runner/resume-claim.d.ts +4 -2
- package/dist/core/runner/resume-claim.js +15 -8
- package/dist/core/runner/resume-policy-outcome.js +1 -1
- package/dist/core/runner/retired-runner-deps.js +2 -1
- package/dist/core/runner/rewind-files.d.ts +15 -0
- package/dist/core/runner/rewind-files.js +132 -0
- package/dist/core/runner/run-attachment-seats.js +2 -2
- package/dist/core/runner/run-brain-sinks.d.ts +4 -1
- package/dist/core/runner/run-brain-sinks.js +4 -2
- package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
- package/dist/core/runner/run-compaction-machinery.js +2 -12
- package/dist/core/runner/run-git-lane.d.ts +1 -0
- package/dist/core/runner/run-harness-handlers.js +14 -7
- package/dist/core/runner/run-leg.d.ts +1 -1
- package/dist/core/runner/run-leg.js +43 -18
- package/dist/core/runner/run-settle-and-teardown.js +3 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.d.ts +1 -0
- package/dist/core/runner/run-turn-boundary.js +15 -22
- package/dist/core/runner/runtask.d.ts +3 -0
- package/dist/core/runner/runtask.js +8 -9
- package/dist/core/runner/session-read-file-states.d.ts +15 -0
- package/dist/core/runner/session-read-file-states.js +28 -0
- package/dist/core/runner/session-rule-policy.d.ts +22 -21
- package/dist/core/runner/session-rule-policy.js +17 -31
- package/dist/core/runner/shell-env-withheld.d.ts +33 -0
- package/dist/core/runner/shell-env-withheld.js +51 -0
- package/dist/core/runner/side-call-account.d.ts +32 -8
- package/dist/core/runner/side-call-account.js +23 -1
- package/dist/core/runner/skills-seat.js +6 -2
- package/dist/core/runner/stream-settle-backstop.js +1 -1
- package/dist/core/runner/strict-output-schema.d.ts +1 -1
- package/dist/core/runner/sub-agent-deny-lists.js +4 -6
- package/dist/core/runner/synthetic-tools.d.ts +16 -13
- package/dist/core/runner/synthetic-tools.js +8 -3
- package/dist/core/runner/tool-disclosure.d.ts +10 -12
- package/dist/core/runner/tool-disclosure.js +3 -4
- package/dist/core/runner/tool-end-body.d.ts +1 -1
- package/dist/core/runner/tool-end-body.js +10 -9
- package/dist/core/runner/tool-face-overlay.d.ts +2 -1
- package/dist/core/runner/tool-face-overlay.js +17 -1
- package/dist/core/runner/tool-not-executed.d.ts +5 -0
- package/dist/core/runner/tool-not-executed.js +11 -0
- package/dist/core/runner/tool-occurrence.d.ts +8 -0
- package/dist/core/runner/tool-occurrence.js +7 -0
- package/dist/core/runner/tool-progress-lane.d.ts +2 -2
- package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
- package/dist/core/runner/turn-attachments.d.ts +91 -80
- package/dist/core/runner/turn-attachments.js +62 -37
- package/dist/core/runner-deps.d.ts +42 -42
- package/dist/core/runtime-caps.d.ts +4 -4
- package/dist/core/safety-axis-vocab.d.ts +6 -4
- package/dist/core/safety-axis-vocab.js +6 -5
- package/dist/core/safety-merge-corpus.js +26 -2
- package/dist/core/same-plain-value.d.ts +40 -0
- package/dist/core/same-plain-value.js +49 -0
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secret-env.d.ts +26 -3
- package/dist/core/secret-env.js +44 -8
- package/dist/core/select-model.js +3 -3
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +1 -1
- package/dist/core/session-policy-store.d.ts +102 -35
- package/dist/core/session-policy-store.js +210 -48
- package/dist/core/session-reconcile.d.ts +2 -2
- package/dist/core/session-store.d.ts +106 -37
- package/dist/core/session-store.js +165 -91
- package/dist/core/session.d.ts +101 -57
- package/dist/core/session.js +53 -0
- package/dist/core/shared-memory/tools.js +3 -1
- package/dist/core/shared-memory/types.d.ts +1 -1
- package/dist/core/shell-read-silence.d.ts +11 -12
- package/dist/core/shell-read-silence.js +9 -5
- package/dist/core/shell-run-candidates.js +3 -1
- package/dist/core/shell-wrapper-projection.js +1 -1
- package/dist/core/shell-wrapper-table.d.ts +14 -7
- package/dist/core/shell-wrapper-table.js +5 -4
- package/dist/core/side-query.d.ts +10 -0
- package/dist/core/side-query.js +15 -1
- package/dist/core/skill-grant.d.ts +7 -2
- package/dist/core/skill-grant.js +5 -0
- package/dist/core/skills-directory.js +6 -2
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +168 -3
- package/dist/core/store-contracts/file-history-store-contract.js +74 -4
- package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
- package/dist/core/store-contracts/peer-directory-contract.js +14 -2
- package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
- package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
- package/dist/core/store-contracts/session-repo-contract.js +176 -0
- package/dist/core/store-contracts/session-store-contract.d.ts +6 -8
- package/dist/core/store-contracts/session-store-contract.js +361 -51
- package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
- package/dist/core/stub-env.d.ts +2 -0
- package/dist/core/stub-env.js +1 -0
- package/dist/core/surrogate-safe-slice.d.ts +3 -3
- package/dist/core/synthetic-read-verdict.js +3 -3
- package/dist/core/task-event.d.ts +42 -8
- package/dist/core/task-notification.d.ts +19 -15
- package/dist/core/task-registry-agent.d.ts +4 -4
- package/dist/core/task-registry-shared.d.ts +4 -4
- package/dist/core/task-registry-shared.js +1 -0
- package/dist/core/task-registry.d.ts +13 -4
- package/dist/core/task-registry.js +14 -2
- package/dist/core/task-result.d.ts +21 -3
- package/dist/core/task-spec.d.ts +69 -64
- package/dist/core/task-stream.d.ts +6 -6
- package/dist/core/task-tool-shape.d.ts +4 -4
- package/dist/core/tighten-task-spec.d.ts +1 -1
- package/dist/core/tighten-task-spec.js +2 -1
- package/dist/core/tool-catalog-entries.d.ts +36 -2
- package/dist/core/tool-catalog-entries.js +69 -43
- package/dist/core/tool-check-reading.d.ts +11 -0
- package/dist/core/tool-check-reading.js +11 -6
- package/dist/core/tool-conformance.js +1 -0
- package/dist/core/tool-errors.d.ts +2 -2
- package/dist/core/tool-face-reader.js +5 -3
- package/dist/core/tool-face.d.ts +21 -2
- package/dist/core/tool-face.js +1 -1
- package/dist/core/tool-model-gate.d.ts +5 -5
- package/dist/core/tool-name-aliases.d.ts +29 -8
- package/dist/core/tool-name-aliases.js +22 -4
- package/dist/core/tool-name-readings.d.ts +10 -43
- package/dist/core/tool-name-readings.js +2 -11
- package/dist/core/tool-policy.d.ts +85 -196
- package/dist/core/tool-policy.js +150 -175
- package/dist/core/tool-registry.d.ts +14 -7
- package/dist/core/tool-registry.js +17 -7
- package/dist/core/tool-result-store.js +5 -1
- package/dist/core/tool-roster.d.ts +57 -33
- package/dist/core/tool-roster.js +56 -68
- package/dist/core/tool-spec-members.js +8 -3
- package/dist/core/tool-spec.d.ts +53 -31
- package/dist/core/tools.js +51 -10
- package/dist/core/trace.d.ts +6 -2
- package/dist/core/types.d.ts +1 -1
- package/dist/core/untrusted-egress.d.ts +1 -1
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/user-attachment.d.ts +8 -1
- package/dist/core/user-attachment.js +64 -4
- package/dist/core/warm-resume.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +58 -0
- package/dist/core/wiring-manifest.js +14 -5
- package/dist/core/workflow-journal-store.d.ts +62 -0
- package/dist/core/workflow-journal-store.js +78 -2
- package/dist/core/workflow-run-store.d.ts +6 -0
- package/dist/core/workflow-run-store.js +2 -0
- package/dist/engine/compaction/compaction.d.ts +9 -9
- package/dist/engine/compaction/compaction.js +3 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +26 -8
- package/dist/engine/execution-env/node-execution-env.js +54 -24
- package/dist/engine/harness/agent-harness.d.ts +39 -9
- package/dist/engine/harness/agent-harness.js +87 -33
- package/dist/engine/harness/types.d.ts +58 -9
- package/dist/engine/harness/types.js +12 -1
- package/dist/engine/llm/types.d.ts +36 -19
- package/dist/engine/llm/validation.d.ts +4 -1
- package/dist/engine/llm/validation.js +13 -1
- package/dist/engine/loop/agent-loop.d.ts +23 -1
- package/dist/engine/loop/agent-loop.js +26 -34
- package/dist/engine/loop/types.d.ts +31 -19
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/memory-repo.d.ts +18 -12
- package/dist/engine/session/memory-repo.js +24 -9
- package/dist/engine/session/parent-chain-guard.d.ts +3 -2
- package/dist/engine/session/parent-chain-guard.js +1 -1
- package/dist/engine/session/repo-utils.d.ts +32 -3
- package/dist/engine/session/repo-utils.js +50 -5
- package/dist/index.d.ts +47 -23
- package/dist/index.js +35 -17
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/harness.d.ts +3 -3
- package/dist/internal/harness.js +2 -2
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/goal.js +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +8 -6
- package/dist/orchestration/run-workflow-tool.js +10 -6
- package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
- package/dist/orchestration/workflow-types.d.ts +18 -0
- package/dist/orchestration/workflow-types.js +6 -0
- package/dist/orchestration/workflow.d.ts +5 -13
- package/dist/orchestration/workflow.js +57 -49
- package/dist/prompt-assembly/artifact.d.ts +2 -2
- package/dist/prompt-assembly/artifact.js +5 -3
- package/dist/prompt-assembly/assemble.d.ts +3 -2
- package/dist/prompt-assembly/assemble.js +1 -1
- package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
- package/dist/prompt-assembly/catalog/schema.js +34 -2
- package/dist/prompt-assembly/catalog/sections.js +0 -1
- package/dist/prompt-assembly/composer.d.ts +3 -1
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/event-registry.js +2 -0
- package/dist/prompt-assembly/overlay.js +3 -3
- package/dist/prompt-assembly/renderers.d.ts +1 -1
- package/dist/prompt-assembly/renderers.js +0 -3
- package/dist/prompt-assembly/role-base.d.ts +15 -0
- package/dist/prompt-assembly/role-base.js +9 -0
- package/dist/prompt-assembly/select.d.ts +39 -14
- package/dist/prompt-assembly/select.js +47 -16
- package/dist/prompt-assembly/sugar.d.ts +17 -11
- package/dist/prompt-assembly/sugar.js +8 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +39 -2
- package/dist/prompt-assembly/tool-catalog.js +117 -4
- package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
- package/dist/prompt-assembly/turn-snapshot.js +0 -3
- package/dist/prompt-assembly/types.d.ts +0 -7
- package/dist/prompts/default.d.ts +14 -14
- package/dist/scenarios/full-body.d.ts +15 -15
- package/dist/scenarios/full-body.js +4 -3
- package/dist/shell/lex.js +10 -1
- package/dist/stores/file/adoption/adopt.d.ts +4 -3
- package/dist/stores/file/adoption/adopt.js +27 -33
- package/dist/stores/file/adoption/marker.d.ts +6 -13
- package/dist/stores/file/adoption/marker.js +9 -17
- package/dist/stores/file/checkpoint-store.d.ts +24 -0
- package/dist/stores/file/checkpoint-store.js +45 -7
- package/dist/stores/file/file-history-store.d.ts +20 -19
- package/dist/stores/file/file-history-store.js +157 -141
- package/dist/stores/file/fs-atomic.d.ts +15 -0
- package/dist/stores/file/fs-atomic.js +89 -14
- package/dist/stores/file/index.d.ts +1 -1
- package/dist/stores/file/index.js +2 -1
- package/dist/stores/file/mailbox-store.d.ts +3 -0
- package/dist/stores/file/mailbox-store.js +3 -1
- package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
- package/dist/stores/file/permission-rule-adopt.js +18 -18
- package/dist/stores/file/permission-rule-store.d.ts +18 -2
- package/dist/stores/file/permission-rule-store.js +31 -18
- package/dist/stores/file/session-policy-store.d.ts +3 -3
- package/dist/stores/file/session-store.d.ts +54 -16
- package/dist/stores/file/session-store.js +114 -85
- package/dist/stores/file/workflow-journal-store.d.ts +10 -4
- package/dist/stores/file/workflow-journal-store.js +28 -13
- package/dist/tools/artifact/artifact-text.d.ts +1 -1
- package/dist/tools/artifact/artifact-tool.js +5 -3
- package/dist/tools/fs/bash-program-position.d.ts +19 -2
- package/dist/tools/fs/bash-program-position.js +56 -9
- package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
- package/dist/tools/fs/fs-bash.d.ts +2 -2
- package/dist/tools/fs/fs-bash.js +8 -6
- package/dist/tools/fs/fs-glob.d.ts +86 -0
- package/dist/tools/fs/fs-glob.js +226 -0
- package/dist/tools/fs/fs-read.js +14 -14
- package/dist/tools/fs/fs-search-tools.js +7 -7
- package/dist/tools/fs/fs-shared.d.ts +8 -8
- package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
- package/dist/tools/fs/notebook.d.ts +2 -2
- package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
- package/dist/tools/fs/pattern-kill-guard.js +397 -0
- package/dist/tools/fs/pdf.d.ts +9 -6
- package/dist/tools/fs/pdf.js +3 -3
- package/dist/tools/fs/program-read-model.d.ts +57 -7
- package/dist/tools/fs/program-read-model.js +11 -1
- package/dist/tools/fs/program-read-model.rows.js +78 -21
- package/dist/tools/fs/read-deny-home.js +3 -2
- package/dist/tools/fs/read-deny.d.ts +29 -7
- package/dist/tools/fs/read-deny.js +63 -22
- package/dist/tools/fs/removal-targets.d.ts +18 -8
- package/dist/tools/fs/removal-targets.js +47 -20
- package/dist/tools/fs/safety.d.ts +9 -9
- package/dist/tools/fs/search.d.ts +16 -6
- package/dist/tools/fs/search.js +20 -26
- package/dist/tools/fs/shell-read-argv.js +89 -12
- package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
- package/dist/tools/fs/shell-read-bindings.js +41 -0
- package/dist/tools/fs/shell-read-check.js +12 -20
- package/dist/tools/fs/shell-read-environment.d.ts +5 -1
- package/dist/tools/fs/shell-read-environment.js +3 -2
- package/dist/tools/fs/shell-read-establish.d.ts +15 -0
- package/dist/tools/fs/shell-read-establish.js +19 -20
- package/dist/tools/fs/shell-read-judge.d.ts +4 -0
- package/dist/tools/fs/shell-read-judge.js +5 -2
- package/dist/tools/fs/shell-read-plan.js +24 -16
- package/dist/tools/fs/shell-read-start.d.ts +6 -1
- package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
- package/dist/tools/fs/shell-read-write-targets.js +2 -3
- package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
- package/dist/tools/fs/shell-reentry-text.js +40 -2
- package/dist/tools/loop-tick.d.ts +2 -2
- package/dist/tools/scheduler-tools.js +5 -1
- package/dist/tools/task-list.d.ts +7 -7
- package/dist/tools/task-list.js +5 -1
- package/dist/tools/todo.js +65 -64
- package/dist/tools/web.d.ts +20 -15
- package/dist/tools/web.js +55 -69
- package/dist/tools/worktree.d.ts +3 -3
- package/dist/tools/worktree.js +3 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +5894 -5638
- package/wire-changes/core@7.55.0.json +3318 -0
- package/wire-changes/core@7.56.0.json +3891 -0
- package/wire-changes/mods-kernel@7.56.0.json +303 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
- package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
package/dist/core/side-query.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { ensureToolTurnInvariants } from "../internal/llm.js";
|
|
2
2
|
import { resolveTaskModel } from "./roles.js";
|
|
3
|
+
import { runRenderer } from "../prompt-assembly/renderers.js";
|
|
4
|
+
import { roleBaseOf } from "../prompt-assembly/role-base.js";
|
|
5
|
+
import { effectiveTraitsOf, readModelPromptTraits, selectorsOf } from "../prompt-assembly/select.js";
|
|
3
6
|
import { readDegradation } from "../brain/degrading.js";
|
|
7
|
+
import { extractErrorCode } from "../brain/errors.js";
|
|
4
8
|
import { cacheFamilyOf, uncachedInputTokensOf } from "./runner/usage-accounting.js";
|
|
5
9
|
import { providerReportedModel } from "./message-utils.js";
|
|
6
10
|
import { providerToolCallId } from "../brain/tool-call-id.js";
|
|
@@ -8,6 +12,11 @@ function normalizeSideQueryUsage(model, u) {
|
|
|
8
12
|
const input = uncachedInputTokensOf(cacheFamilyOf(model), u);
|
|
9
13
|
return input === u.input ? u : { ...u, input };
|
|
10
14
|
}
|
|
15
|
+
const NO_RUN_FACTS = Object.freeze({ policyEnabled: false, hooksEnabled: false, isolationEnabled: false, withinTaskCompactionEnabled: false, supervisorEnabled: false, orchestrationEnabled: false, awarenessEnabled: false, goalEnabled: false, worktreeIsolated: false, teammateEnabled: false });
|
|
16
|
+
function sideQueryRoleBase(role, model, tools) {
|
|
17
|
+
const selectors = selectorsOf(NO_RUN_FACTS, { role }, undefined, effectiveTraitsOf(readModelPromptTraits(model), undefined));
|
|
18
|
+
return runRenderer("role-base", { facts: NO_RUN_FACTS, toolNames: (tools ?? []).map((t) => t.name) }, selectors)?.text;
|
|
19
|
+
}
|
|
11
20
|
export async function runSideQuery(spec, deps) {
|
|
12
21
|
if (!Array.isArray(spec.messages) || spec.messages.length === 0) {
|
|
13
22
|
throw new Error("sideQuery requires a non-empty messages array");
|
|
@@ -39,7 +48,8 @@ export async function runSideQuery(spec, deps) {
|
|
|
39
48
|
}
|
|
40
49
|
return { role: "user", content: m.content, timestamp };
|
|
41
50
|
};
|
|
42
|
-
const
|
|
51
|
+
const decided = roleBaseOf([spec.systemPrompt !== undefined ? { systemPrompt: spec.systemPrompt } : undefined, resolved]);
|
|
52
|
+
const effectiveSystemPrompt = decided?.systemPrompt ?? (decided?.promptRole !== undefined ? sideQueryRoleBase(decided.promptRole, resolved.model, spec.tools) : undefined);
|
|
43
53
|
const context = {
|
|
44
54
|
...(effectiveSystemPrompt !== undefined ? { systemPrompt: effectiveSystemPrompt } : {}),
|
|
45
55
|
messages: ensureToolTurnInvariants(spec.messages.map(normalize)).messages,
|
|
@@ -71,6 +81,7 @@ export async function runSideQuery(spec, deps) {
|
|
|
71
81
|
const degraded = readDegradation(msg);
|
|
72
82
|
const servedModel = typeof msg.model === "string" && msg.model.length > 0 ? msg.model : resolved.model.id;
|
|
73
83
|
const reportedModel = providerReportedModel(msg);
|
|
84
|
+
const errorCode = extractErrorCode(msg.errorMessage);
|
|
74
85
|
return {
|
|
75
86
|
text,
|
|
76
87
|
toolCalls,
|
|
@@ -82,5 +93,8 @@ export async function runSideQuery(spec, deps) {
|
|
|
82
93
|
...(msg.usageMissing === true ? { usageMissing: true } : {}),
|
|
83
94
|
stopReason: msg.stopReason,
|
|
84
95
|
...(msg.errorMessage !== undefined ? { errorMessage: msg.errorMessage } : {}),
|
|
96
|
+
...(msg.stopReason === "error" && errorCode !== undefined ? { errorCode } : {}),
|
|
97
|
+
...(msg.errorKind !== undefined ? { errorKind: msg.errorKind } : {}),
|
|
98
|
+
...(msg.inputTooLongRuledOut === true ? { inputTooLongRuledOut: true } : {}),
|
|
85
99
|
};
|
|
86
100
|
}
|
|
@@ -199,6 +199,10 @@ export declare class SkillGrantTable {
|
|
|
199
199
|
private readonly rowKeys;
|
|
200
200
|
private mounted;
|
|
201
201
|
private activations;
|
|
202
|
+
private scopes;
|
|
203
|
+
/** State the leg's skill scopes (the mods bus's record): every activation this table writes opens its skill's scope there. A
|
|
204
|
+
* table never told opens nothing (the resume's pre-claim probe). */
|
|
205
|
+
bindScopes(scopes: Pick<import("./mods/spec-seats.js").SkillScopes, "open">): void;
|
|
202
206
|
/** State where the run's mounted names are read (prepare, once the roster is assembled): a skill's entries are read against
|
|
203
207
|
* them when seeded and whenever the skill is activated. A table never told reads the grammar alone. */
|
|
204
208
|
bindMountedNames(mounted: MountedToolNames): void;
|
|
@@ -211,8 +215,9 @@ export declare class SkillGrantTable {
|
|
|
211
215
|
* capture re-supplied beside the frozen rows that already carry it. Keyed on the ROW, not the skill: a definition that
|
|
212
216
|
* changed across a park writes its new rows when it is activated again. */
|
|
213
217
|
private hold;
|
|
214
|
-
/** Write a skill's rows (rows already held are not held twice) — or
|
|
215
|
-
* when its `disallowed-tools` holds an entry that reads as nothing. Returns the allow
|
|
218
|
+
/** Write a skill's rows (rows already held are not held twice) and open its scope — or neither, refused with
|
|
219
|
+
* {@link SKILL_DENY_UNREADABLE_CODE}, when its `disallowed-tools` holds an entry that reads as nothing. Returns the allow
|
|
220
|
+
* entries that read as nothing. */
|
|
216
221
|
activate(skill: SkillGrantSource, origin: SkillGrantOrigin): readonly SkillInertEntry[];
|
|
217
222
|
/** Seed portable records (a delegated child's capture, a resumed leg's frozen rows): a record the model wrote keeps that
|
|
218
223
|
* word, every other record seeds under `origin` — `user` is written only from this leg's own spec, never seeded. Each
|
package/dist/core/skill-grant.js
CHANGED
|
@@ -180,6 +180,10 @@ export class SkillGrantTable {
|
|
|
180
180
|
rowKeys = new Set();
|
|
181
181
|
mounted;
|
|
182
182
|
activations;
|
|
183
|
+
scopes;
|
|
184
|
+
bindScopes(scopes) {
|
|
185
|
+
this.scopes = scopes;
|
|
186
|
+
}
|
|
183
187
|
bindMountedNames(mounted) {
|
|
184
188
|
this.mounted = mounted;
|
|
185
189
|
}
|
|
@@ -203,6 +207,7 @@ export class SkillGrantTable {
|
|
|
203
207
|
throw skillDenyUnreadable(skill.name, unreadDeny, "activation");
|
|
204
208
|
for (const row of rows)
|
|
205
209
|
this.hold(row);
|
|
210
|
+
this.scopes?.open(skill.name);
|
|
206
211
|
return inert;
|
|
207
212
|
}
|
|
208
213
|
seed(records, origin) {
|
|
@@ -23,13 +23,15 @@ const SEQUENCE_NOT_AN_ENTRY = "its `- name` entries are not indented alike";
|
|
|
23
23
|
const SEQUENCE_NOT_A_NAME = "an entry is not a plain or quoted name (nested, mapping, anchor, tag, flow and block-scalar entries, and number, boolean and null entries, are not read)";
|
|
24
24
|
const VALUE_IS_A_MAPPING = "its value is a mapping or holds one (`key: value` or `{…}`), not a list of names";
|
|
25
25
|
const VALUE_NOT_A_STRING = "its value is a number or a boolean, not a list of names";
|
|
26
|
+
const YAML_ALIAS = 'it holds a YAML alias (an unquoted `*<name>`), which the reader does not resolve — quote a value or entry that is meant as a tool-name pattern ("*Edit")';
|
|
27
|
+
const YAML_ALIAS_START = /^\*[^\s,[\]{}]/;
|
|
26
28
|
const QUOTE_NOT_CLOSED = "a quote in it does not close";
|
|
27
29
|
const flowNotClosed = (opener) => `its \`${opener}\` does not close within its lines`;
|
|
28
30
|
const strayLine = (line) => `a flush-left line that is no key follows it (line ${line})`;
|
|
29
31
|
const NOT_A_KEY_LINE = "a flush-left frontmatter line is a key — letters, digits, `_` and `-`, or one quoted scalar — then `:` and a blank or nothing; a comment starts with `#`";
|
|
30
32
|
const PARENS_NOT_ONE_ZONE = "an entry's parentheses are not one group that closes it (a `(` that does not close, a `)` with no `(`, or text after the closing `)` — `\\(` / `\\)` are a rule's own text)";
|
|
31
33
|
const KEY_REPEATED = "the key is written more than once";
|
|
32
|
-
const notARule = (position) => `entry ${position} is no rule spelling (a tool name of ASCII letters, digits and \`_ - . : *\`, starting with a letter, a digit
|
|
34
|
+
const notARule = (position) => `entry ${position} is no rule spelling (a tool name of ASCII letters, digits and \`_ - . : *\`, starting with a letter, a digit, \`_\` or \`*\`, then at most one parenthesized rule)`;
|
|
33
35
|
const keyInsideValue = (inner) => `an indented line under its value is a mapping key, \`${inlineUntrusted(inner, 64)}\` — YAML reads no key there (under a value that is not a mapping, or indented less than the nested block it follows)`;
|
|
34
36
|
const toolListOutOfPlace = (inner) => `it holds the tool-list key \`${inner}\` — a tool list is read only as a key of the frontmatter, written flush left; anywhere else its list would be lost`;
|
|
35
37
|
const isBlank = (ch) => ch === " " || ch === "\t";
|
|
@@ -434,7 +436,9 @@ function scanValue(text, seat) {
|
|
|
434
436
|
const written = trimBlanks(plain);
|
|
435
437
|
plain = "";
|
|
436
438
|
breaks = 0;
|
|
437
|
-
if (
|
|
439
|
+
if (YAML_ALIAS_START.test(written))
|
|
440
|
+
cut.fail(YAML_ALIAS);
|
|
441
|
+
else if (opened.length > 0 || seat === "item") {
|
|
438
442
|
if (YAML_NULL.test(written) || YAML_BOOL_OR_NUMBER.test(written))
|
|
439
443
|
cut.fail(SEQUENCE_NOT_A_NAME);
|
|
440
444
|
}
|
|
@@ -39,7 +39,17 @@ export declare const POLICY_ALLOW: Extract<ResumeOutcome, {
|
|
|
39
39
|
* The full cross-backend contract for {@link CheckpointStore} — the SAME assertions for every
|
|
40
40
|
* backend. Entries are a 1:1 port of the vitest originals (assertion semantics unchanged).
|
|
41
41
|
*/
|
|
42
|
-
export declare function checkpointStoreContract(make: () => CheckpointStore, runAssertion?: ContractAssertionRunner
|
|
42
|
+
export declare function checkpointStoreContract(make: () => CheckpointStore, runAssertion?: ContractAssertionRunner,
|
|
43
|
+
/** `sessionHeads` = the map the binding's stores read their session heads from (their `deleteSessionCheckpoints` guard): the kit writes
|
|
44
|
+
* the heads its delete case needs into it. Required only to judge a store that offers `deleteSessionCheckpoints`.
|
|
45
|
+
* `holders` = the set of current claim holders the binding's store judges by (its `holderIsCurrent`, or the claim table it
|
|
46
|
+
* reads): the kit adds and removes holders in it. Required only to judge a store that declares `fence.holder`. */
|
|
47
|
+
options?: {
|
|
48
|
+
readonly sessionHeads?: Map<string, {
|
|
49
|
+
readonly owner: string | null;
|
|
50
|
+
}>;
|
|
51
|
+
readonly holders?: Set<string>;
|
|
52
|
+
}): Promise<void>;
|
|
43
53
|
/** Seed the canonical listByScope fixture into `store` and return the sorted summaries. Compare the
|
|
44
54
|
* result against {@link EXPECTED_LISTBYSCOPE_SUMMARIES} (deep equality) to prove the backend's
|
|
45
55
|
* summary projection matches the reference — the caller owns the comparison (harness-specific). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
2
|
import { executionVerdict } from "../checkpoint-execution-record.js";
|
|
3
|
-
import { MAX_PENDING_STEER_ENTRIES, mintCheckpointToken, readPendingSteerQueue, } from "../checkpoint-store.js";
|
|
3
|
+
import { MAX_PENDING_STEER_ENTRIES, checkpointIndexFacts, mintCheckpointToken, readPendingSteerQueue, } from "../checkpoint-store.js";
|
|
4
4
|
import { checkpointFrom } from "../pause-registry.js";
|
|
5
5
|
import { beginContract } from "./contract-harness.js";
|
|
6
6
|
export function createCheckpointFixture(over = {}) {
|
|
@@ -48,7 +48,7 @@ export const POLICY_ALLOWED = Object.freeze({
|
|
|
48
48
|
origin: "policy",
|
|
49
49
|
});
|
|
50
50
|
export const POLICY_ALLOW = { ...ALLOW, hostDecision: { decidedBy: "policy", approver: "unattended_class_answer" }, gateOutcome: POLICY_ALLOWED };
|
|
51
|
-
export async function checkpointStoreContract(make, runAssertion) {
|
|
51
|
+
export async function checkpointStoreContract(make, runAssertion, options = {}) {
|
|
52
52
|
const { run, settle } = beginContract(runAssertion);
|
|
53
53
|
run("kit prerequisites: reopen/setPendingSteer/listByScope are implemented (REQUIRED by this kit)", async () => {
|
|
54
54
|
const probe = make();
|
|
@@ -308,7 +308,7 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
308
308
|
const stored = (await store.get(bitless.token)).pendingAction;
|
|
309
309
|
assert.equal("hasBidiControls" in stored, false, "projection backfill must not write back to the row");
|
|
310
310
|
});
|
|
311
|
-
run("the approval object's members: a row's identity + face group and its targets (capabilityId/contractId/shapeDigest/implementationRevision + family/pathTarget/names + targets) survive the round-trip verbatim; an absent group stays absent", async () => {
|
|
311
|
+
run("the approval object's members: a row's identity + face group and its targets (capabilityId/contractId/shapeDigest/implementationRevision + family/pathTarget/hostTarget/names + targets + engineMounted) survive the round-trip verbatim; an absent group stays absent", async () => {
|
|
312
312
|
const store = make();
|
|
313
313
|
const carried = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-carried" });
|
|
314
314
|
const group = {
|
|
@@ -320,6 +320,7 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
320
320
|
pathTarget: { param: "destination", aliases: ["path"], access: "create", skillScopeEligible: true },
|
|
321
321
|
names: ["AcmeWrite", "acme_write"],
|
|
322
322
|
targets: { protective: "/srv/work/out.txt", declared: "/srv/work/out.txt" },
|
|
323
|
+
engineMounted: true,
|
|
323
324
|
};
|
|
324
325
|
Object.assign(carried.pendingAction, group);
|
|
325
326
|
const bare = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-bare" });
|
|
@@ -331,6 +332,14 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
331
332
|
const bareBack = (await store.get(bare.token)).pendingAction;
|
|
332
333
|
for (const k of Object.keys(group))
|
|
333
334
|
assert.equal(k in bareBack, false, `an absent ${k} must stay OMITTED (a pre-388 row reads name-only, never a partial face)`);
|
|
335
|
+
const fetchRow = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-host" });
|
|
336
|
+
Object.assign(fetchRow.pendingAction, { ...group, family: "web", pathTarget: undefined, targets: undefined, hostTarget: { param: "url" } });
|
|
337
|
+
delete fetchRow.pendingAction.pathTarget;
|
|
338
|
+
delete fetchRow.pendingAction.targets;
|
|
339
|
+
await store.put(fetchRow.token, fetchRow);
|
|
340
|
+
const fetchBack = (await store.get(fetchRow.token)).pendingAction;
|
|
341
|
+
assert.deepEqual(fetchBack.hostTarget, { param: "url" }, "the row's hostTarget must survive the round-trip verbatim (dropped, a resume of the parked fetch reads a face that moved and is vetoed, and its pre-claim recheck judges the catalog's slot for the NAME)");
|
|
342
|
+
assert.equal("hostTarget" in bareBack, false, "an absent hostTarget must stay OMITTED");
|
|
334
343
|
});
|
|
335
344
|
run("#457 previewWithheld three-form matrix: a row's spelling survives + projects; absent stays absent; an out-of-contract spelling reads as absent", async () => {
|
|
336
345
|
const store = make();
|
|
@@ -436,6 +445,162 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
436
445
|
await assert.rejects(store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), (e) => e.code === "checkpoint.execution_outcome_conflict", "a person's record for a row the policy decided is a different record");
|
|
437
446
|
assert.deepEqual((await store.get(cp.token)).executionOutcome, POLICY_ALLOWED);
|
|
438
447
|
});
|
|
448
|
+
run("index facts: every row's checkpointIndexFacts survive the round trip — an approval gate with a card, a word outside the card set, no card, a descriptor, a deadline, the directional-control bit; a tool-less park", async () => {
|
|
449
|
+
const store = make();
|
|
450
|
+
const descriptor = { severity: 4, axes: { egress: true }, toolName: "Write" };
|
|
451
|
+
const laterWord = createCheckpointFixture({ gate: { kind: "human", reason: "approve", toolName: "Write" } });
|
|
452
|
+
Object.assign(laterWord.gate, { approvalCard: "card-of-a-later-engine" });
|
|
453
|
+
const rows = [
|
|
454
|
+
createCheckpointFixture({ gate: { kind: "human", reason: "approve", toolName: "Write", approvalCard: "file-write", riskDescriptor: descriptor }, deadline: 1_900_000_000_000 }),
|
|
455
|
+
createCheckpointFixture({ gate: { kind: "irreversible_ask", reason: "send", toolName: "Write", approvalCard: "question" } }),
|
|
456
|
+
laterWord,
|
|
457
|
+
createCheckpointFixture({ pendingAction: { kind: "tool_approval", origin: "policy", toolCallId: "call-b", toolName: "Write", args: {}, boundInputHash: "hb", hasBidiControls: true, batchToolCallIds: ["call-b"], completedCallIds: [] } }),
|
|
458
|
+
createCheckpointFixture({ gate: { kind: "resource_limit", reason: "turns" }, pendingAction: { kind: "resource_limit", reason: "turns" }, deadline: 1_900_000_000_001 }),
|
|
459
|
+
];
|
|
460
|
+
for (const cp of rows) {
|
|
461
|
+
await store.put(cp.token, cp);
|
|
462
|
+
const back = await store.get(cp.token);
|
|
463
|
+
assert.ok(back !== null, "a row put is a row read");
|
|
464
|
+
assert.deepEqual(checkpointIndexFacts(back), checkpointIndexFacts(cp), `the index facts of the row ${cp.gate.kind} came back different`);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
run("listOverdue (when offered): pending rows of every scope past the cutoff, one facet per call (facetOf, the question card included), deadline-then-token order, the bound cut with a cursor that resumes without a gap or a repeat, a bad query refused", async () => {
|
|
468
|
+
const store = make();
|
|
469
|
+
if (store.listOverdue === undefined)
|
|
470
|
+
return;
|
|
471
|
+
const human = (over) => createCheckpointFixture(over);
|
|
472
|
+
const question = createCheckpointFixture({ token: "ov-q", deadline: 1_000, gate: { kind: "human", reason: "ask", toolName: "Write", approvalCard: "question" } });
|
|
473
|
+
const rows = [
|
|
474
|
+
human({ token: "ov-a2", deadline: 1_500 }),
|
|
475
|
+
human({ token: "ov-a1", deadline: 1_000, scope: "tenant-b" }),
|
|
476
|
+
human({ token: "ov-a0", deadline: 1_000 }),
|
|
477
|
+
question,
|
|
478
|
+
createCheckpointFixture({ token: "ov-r", deadline: 900, gate: { kind: "resource_limit", reason: "turns" }, pendingAction: { kind: "resource_limit", reason: "turns" } }),
|
|
479
|
+
createCheckpointFixture({ token: "ov-t", deadline: 900, gate: { kind: "task_done" }, pendingAction: { kind: "task_done" } }),
|
|
480
|
+
human({ token: "ov-late", deadline: 5_000 }),
|
|
481
|
+
human({ token: "ov-none" }),
|
|
482
|
+
human({ token: "ov-done", deadline: 1_000 }),
|
|
483
|
+
];
|
|
484
|
+
for (const cp of rows)
|
|
485
|
+
await store.put(cp.token, cp);
|
|
486
|
+
await store.resolve("ov-done", "tenant-a", ALLOW);
|
|
487
|
+
const tokensOf = (page) => page.rows.map((r) => r.token);
|
|
488
|
+
assert.deepEqual(tokensOf(await store.listOverdue({ cutoff: 2_000, facet: "A" })), ["ov-a0", "ov-a1", "ov-a2"], "facet A = the approval rows past the cutoff, every scope, (deadline, token) ascending");
|
|
489
|
+
assert.deepEqual(tokensOf(await store.listOverdue({ cutoff: 2_000, facet: "B" })), ["ov-r", "ov-q"], "facet B = the abandonment rows, the question parked under an approval gate kind among them");
|
|
490
|
+
assert.deepEqual(tokensOf(await store.listOverdue({ cutoff: 2_000, facet: "-" })), ["ov-t"], "facet - = the non-gate park");
|
|
491
|
+
const first = await store.listOverdue({ cutoff: 2_000, facet: "A", limit: 2 });
|
|
492
|
+
assert.deepEqual(tokensOf(first), ["ov-a0", "ov-a1"], "the bound cuts the page");
|
|
493
|
+
assert.ok(first.next !== undefined, "a cut page carries the next cursor");
|
|
494
|
+
const second = await store.listOverdue({ cutoff: 2_000, facet: "A", limit: 2, after: first.next });
|
|
495
|
+
assert.deepEqual(tokensOf(second), ["ov-a2"], "the cursor resumes after the last row of the page before");
|
|
496
|
+
assert.equal(second.next, undefined, "the last page carries no cursor");
|
|
497
|
+
assert.equal((await store.get(question.token)).status, "pending", "read-only: the listing mutates nothing");
|
|
498
|
+
for (const bad of [{ cutoff: 2_000, facet: "A", limit: 0 }, { cutoff: 2_000, facet: "A", limit: 501 }, { cutoff: 2_000, facet: "A", limit: 1.5 }, { cutoff: Number.NaN, facet: "A" }, { cutoff: 2_000, facet: "C" }, { cutoff: 2_000, facet: "A", scope: "tenant-a" }]) {
|
|
499
|
+
await assert.rejects(store.listOverdue(bad), (e) => e.code === "config.invalid_argument", `refused loudly: ${JSON.stringify(bad)}`);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
run("listBySession (when offered): one session's rows in any status narrowed by status / gate kinds / tool call / scope / createdSince, oldest or newest first, the bound cut with a resuming cursor, an empty gate-kind list matching nothing, a bad query refused", async () => {
|
|
503
|
+
const store = make();
|
|
504
|
+
if (store.listBySession === undefined)
|
|
505
|
+
return;
|
|
506
|
+
const s = "sess-list";
|
|
507
|
+
const rows = [
|
|
508
|
+
createCheckpointFixture({ token: "ls-1", sessionId: s, createdAt: 1 }),
|
|
509
|
+
createCheckpointFixture({ token: "ls-2", sessionId: s, createdAt: 2, pendingAction: { kind: "tool_approval", origin: "policy", toolCallId: "call-9", toolName: "Write", args: {}, boundInputHash: "h9", boundGrantAbsence: "not_bindable", batchToolCallIds: ["call-9"], completedCallIds: [] } }),
|
|
510
|
+
createCheckpointFixture({ token: "ls-3", sessionId: s, createdAt: 3, gate: { kind: "resource_limit", reason: "turns" }, pendingAction: { kind: "resource_limit", reason: "turns" } }),
|
|
511
|
+
createCheckpointFixture({ token: "ls-4", sessionId: s, createdAt: 4, scope: "tenant-b" }),
|
|
512
|
+
createCheckpointFixture({ token: "ls-x", sessionId: "another-session", createdAt: 1 }),
|
|
513
|
+
];
|
|
514
|
+
for (const cp of rows)
|
|
515
|
+
await store.put(cp.token, cp);
|
|
516
|
+
await store.resolve("ls-1", "tenant-a", ALLOW);
|
|
517
|
+
await store.expire("ls-3", "tenant-a");
|
|
518
|
+
const tokensOf = (page) => page.rows.map((r) => {
|
|
519
|
+
const read = r;
|
|
520
|
+
assert.equal(read.kind, "row", "a readable row is answered as a row");
|
|
521
|
+
return read.checkpoint.token;
|
|
522
|
+
});
|
|
523
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s })), ["ls-1", "ls-2", "ls-3", "ls-4"], "every status, every scope, oldest first; another session's row never");
|
|
524
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, order: "newest" })), ["ls-4", "ls-3", "ls-2", "ls-1"]);
|
|
525
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, status: "pending", gateKinds: ["human"], order: "newest", limit: 1 })), ["ls-4"], "the session's newest pending row of a gate kind");
|
|
526
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, status: "pending", gateKinds: ["human"], scope: "tenant-a" })), ["ls-2"]);
|
|
527
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, gateKinds: [] })), [], "an empty gate-kind list matches no row");
|
|
528
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, status: "expired", limit: 1 })), ["ls-3"]);
|
|
529
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, toolCallId: "call-3" })), ["ls-1", "ls-4"], "the rows that parked this tool call (a park with no tool call never matches)");
|
|
530
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, toolCallId: "call-9", status: "pending" })), ["ls-2"]);
|
|
531
|
+
assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, createdSince: 3 })), ["ls-3", "ls-4"]);
|
|
532
|
+
const first = await store.listBySession({ sessionId: s, limit: 3, order: "newest" });
|
|
533
|
+
assert.deepEqual(tokensOf(first), ["ls-4", "ls-3", "ls-2"]);
|
|
534
|
+
assert.ok(first.next !== undefined, "a cut page carries the next cursor");
|
|
535
|
+
const second = await store.listBySession({ sessionId: s, limit: 3, order: "newest", after: first.next });
|
|
536
|
+
assert.deepEqual(tokensOf(second), ["ls-1"]);
|
|
537
|
+
assert.equal(second.next, undefined);
|
|
538
|
+
const read = (await store.listBySession({ sessionId: s, toolCallId: "call-9" })).rows[0];
|
|
539
|
+
read.checkpoint.status = "expired";
|
|
540
|
+
assert.equal((await store.get("ls-2")).status, "pending", "a listed row is a copy: mutating it changes nothing stored");
|
|
541
|
+
for (const bad of [{ sessionId: "" }, { sessionId: s, limit: 101 }, { sessionId: s, limit: 0 }, { sessionId: s, status: "done" }, { sessionId: s, gateKinds: ["nope"] }, { sessionId: s, order: "latest" }, { sessionId: s, createdSince: Number.POSITIVE_INFINITY }, { sessionId: s, owner: "x" }]) {
|
|
542
|
+
await assert.rejects(store.listBySession(bad), (e) => e.code === "config.invalid_argument", `refused loudly: ${JSON.stringify(bad)}`);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
run("deleteSessionCheckpoints (when offered): every row of the session, in any status, only when the session row exists and its owner equals the guard's (null-safe); otherwise nothing is deleted and the answer is 0", async () => {
|
|
546
|
+
const store = make();
|
|
547
|
+
if (store.deleteSessionCheckpoints === undefined)
|
|
548
|
+
return;
|
|
549
|
+
const heads = options.sessionHeads;
|
|
550
|
+
assert.ok(heads !== undefined, "a store offering deleteSessionCheckpoints is judged with the binding's `sessionHeads` (the map its session head read reads)");
|
|
551
|
+
heads.set("del-owned", { owner: "alice" });
|
|
552
|
+
heads.set("del-anon", { owner: null });
|
|
553
|
+
const owned = [
|
|
554
|
+
createCheckpointFixture({ sessionId: "del-owned" }),
|
|
555
|
+
createCheckpointFixture({ sessionId: "del-owned" }),
|
|
556
|
+
createCheckpointFixture({ sessionId: "del-owned" }),
|
|
557
|
+
createCheckpointFixture({ sessionId: "del-owned", scope: "tenant-b" }),
|
|
558
|
+
];
|
|
559
|
+
const anon = createCheckpointFixture({ sessionId: "del-anon" });
|
|
560
|
+
const orphan = createCheckpointFixture({ sessionId: "del-no-row" });
|
|
561
|
+
const bystander = createCheckpointFixture({ sessionId: "del-other" });
|
|
562
|
+
for (const cp of [...owned, anon, orphan, bystander])
|
|
563
|
+
await store.put(cp.token, cp);
|
|
564
|
+
await store.resolve(owned[1].token, owned[1].scope, ALLOW);
|
|
565
|
+
await store.expire(owned[2].token, owned[2].scope);
|
|
566
|
+
assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: "bob" }), 0, "another owner deletes nothing");
|
|
567
|
+
assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: null }), 0, "the anonymous guard does not match an owned session");
|
|
568
|
+
assert.equal(await store.deleteSessionCheckpoints("del-no-row", { owner: null }), 0, "no session row deletes nothing");
|
|
569
|
+
assert.ok((await store.get(orphan.token)) !== null && (await store.get(owned[0].token)) !== null, "a refused guard leaves every row");
|
|
570
|
+
assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: "alice" }), 4, "the owner's guard deletes the session's rows — pending, resolved, expired, in every scope");
|
|
571
|
+
for (const cp of owned)
|
|
572
|
+
assert.equal(await store.get(cp.token), null);
|
|
573
|
+
assert.equal(await store.deleteSessionCheckpoints("del-anon", { owner: null }), 1, "the anonymous guard deletes an anonymous session's rows");
|
|
574
|
+
assert.ok((await store.get(bystander.token)) !== null, "another session's row stays");
|
|
575
|
+
assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: "alice" }), 0, "a second delete finds nothing");
|
|
576
|
+
const deleteWith = store.deleteSessionCheckpoints;
|
|
577
|
+
for (const guard of [{}, { owner: undefined }, { owner: 7 }]) {
|
|
578
|
+
const call = () => Reflect.apply(deleteWith, store, ["del-other", guard]);
|
|
579
|
+
await assert.rejects(call, (e) => e.code === "config.invalid_argument", `refused loudly: ${JSON.stringify(guard)}`);
|
|
580
|
+
}
|
|
581
|
+
assert.ok((await store.get(bystander.token)) !== null, "a refused guard deletes nothing");
|
|
582
|
+
});
|
|
583
|
+
run("claim holder (when declared `fence.holder`): a resolve carrying `expect.holder` wins only while the holder is current; a superseded holder loses with the row pending at the same rev (claimTerminal, when offered, answers that reading); no holder is unchanged", async () => {
|
|
584
|
+
const store = make();
|
|
585
|
+
if (store.fence?.holder !== true)
|
|
586
|
+
return;
|
|
587
|
+
const holders = options.holders;
|
|
588
|
+
assert.ok(holders !== undefined, "a store declaring `fence.holder` is judged with the binding's `holders` (the set its holder judge reads)");
|
|
589
|
+
holders.add("kit-holder-current");
|
|
590
|
+
const [a, b, c] = [createCheckpointFixture(), createCheckpointFixture(), createCheckpointFixture()];
|
|
591
|
+
for (const cp of [a, b, c])
|
|
592
|
+
await store.put(cp.token, cp);
|
|
593
|
+
assert.equal(await store.resolve(a.token, a.scope, ALLOW, { rev: 0, holder: "kit-holder-gone" }), false, "a holder that is not current loses");
|
|
594
|
+
const after = (await store.get(a.token));
|
|
595
|
+
assert.deepEqual([after.status, after.rev ?? 0], ["pending", 0], "the row is untouched by the lost CAS");
|
|
596
|
+
if (store.claimTerminal !== undefined) {
|
|
597
|
+
assert.deepEqual(await store.claimTerminal(a.token, a.scope, { kind: "resolve", outcome: ALLOW, expect: { rev: 0, holder: "kit-holder-gone" } }), { claimed: false, current: { status: "pending", rev: 0 } });
|
|
598
|
+
}
|
|
599
|
+
assert.equal(await store.resolve(a.token, a.scope, ALLOW, { rev: 0, holder: "kit-holder-current" }), true, "the current holder wins");
|
|
600
|
+
holders.delete("kit-holder-current");
|
|
601
|
+
assert.equal(await store.resolve(b.token, b.scope, ALLOW, { rev: 0, holder: "kit-holder-current" }), false, "a holder released after an earlier win is judged afresh");
|
|
602
|
+
assert.equal(await store.resolve(c.token, c.scope, ALLOW, { rev: 0 }), true, "no holder: the CAS is as before");
|
|
603
|
+
});
|
|
439
604
|
await settle();
|
|
440
605
|
}
|
|
441
606
|
const sortByToken = (a, b) => a.token.localeCompare(b.token);
|
|
@@ -3,6 +3,7 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { chmod, mkdtemp, mkdir, rm, stat, symlink, writeFile, readFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
+
import { isGovernanceRetryClass } from "../governance-codes.js";
|
|
6
7
|
import { NodeExecutionEnv } from "../../internal/harness.js";
|
|
7
8
|
import { beginContract } from "./contract-harness.js";
|
|
8
9
|
function probeOver(inner) {
|
|
@@ -34,6 +35,13 @@ function envWithFailingRead(inner, fail) {
|
|
|
34
35
|
},
|
|
35
36
|
});
|
|
36
37
|
}
|
|
38
|
+
const NAMES_A_FAULT = { track_failed: true, snapshot_failed: true, restore_failed: true, adopt_failed: true, unsupported_format: true, not_found: false, conflict: false, aborted: false };
|
|
39
|
+
function assertPermanenceWord(error, what) {
|
|
40
|
+
const p = error?.permanence;
|
|
41
|
+
assert.ok(p === undefined || isGovernanceRetryClass(p), `${what}: permanence must be absent or a GovernanceRetryClass word, got ${typeof p === "string" ? `"${p}"` : typeof p}`);
|
|
42
|
+
if (p !== undefined)
|
|
43
|
+
assert.ok(error !== undefined && NAMES_A_FAULT[error.code] === true, `${what}: ${error?.code} is not a fault and carries no permanence`);
|
|
44
|
+
}
|
|
37
45
|
async function withTree(fn) {
|
|
38
46
|
const dir = await mkdtemp(join(tmpdir(), "fh-kit-"));
|
|
39
47
|
try {
|
|
@@ -52,9 +60,20 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
52
60
|
const r = await store.restore("sc", "e1", env, dir);
|
|
53
61
|
assert.equal(r.outcome, "failed");
|
|
54
62
|
assert.equal(r.error?.code, "not_found");
|
|
63
|
+
assertPermanenceWord(r.error, "a boundary never made (not a fault)");
|
|
55
64
|
assert.deepEqual(r.filesChanged, []);
|
|
56
65
|
assert.deepEqual(r.refused, []);
|
|
57
66
|
assert.equal(await store.reap("sc", []), 0);
|
|
67
|
+
if (store.exportHistory !== undefined)
|
|
68
|
+
assert.deepEqual(await store.exportHistory("sc"), { ok: true, history: null }, "nothing to export is `ok` with `history: null` — the one meaning of null");
|
|
69
|
+
await writeFile(join(dir, "f.txt"), "v1");
|
|
70
|
+
assert.deepEqual(await store.trackEdit("sc", join(dir, "f.txt"), env, dir), { ok: true, minted: true });
|
|
71
|
+
assert.deepEqual(await store.snapshot("sc", "b1", env, dir), { ok: true });
|
|
72
|
+
await writeFile(join(dir, "f.txt"), "v2-edited");
|
|
73
|
+
const never = await store.restore("sc", "never-made", env, dir);
|
|
74
|
+
assert.equal(never.error?.code, "not_found");
|
|
75
|
+
assert.deepEqual([never.filesChanged, never.identicalSkipped, never.refused, never.failed], [[], [], [], []], "a boundary never made has an empty ledger");
|
|
76
|
+
assert.equal(await readFile(join(dir, "f.txt"), "utf8"), "v2-edited", "a boundary never made touches nothing");
|
|
58
77
|
});
|
|
59
78
|
});
|
|
60
79
|
run("first-touch idempotence: a second trackEdit for the same path is a cheap no-op (ZERO env reads)", async () => {
|
|
@@ -186,6 +205,8 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
186
205
|
assert.equal(busy.ok, false);
|
|
187
206
|
if (!busy.ok)
|
|
188
207
|
assert.equal(busy.error.code, "conflict");
|
|
208
|
+
if (!busy.ok)
|
|
209
|
+
assertPermanenceWord(busy.error, "the target law's conflict");
|
|
189
210
|
assert.equal(await store.canRestore("fresh", "b"), true);
|
|
190
211
|
});
|
|
191
212
|
});
|
|
@@ -272,10 +293,39 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
272
293
|
assert.deepEqual(await store.annulTrack("sc", join(dir, "opaque2.txt"), dir), { ok: true });
|
|
273
294
|
await writeFile(join(dir, "opaque.txt"), "agent-edited");
|
|
274
295
|
const r = await store.restore("sc", "b0", env, dir);
|
|
275
|
-
assert.equal(r.refused.some((f) => f.reason.includes("base_unavailable")), true, `the kept marker must still refuse a below-base restore: ${JSON.stringify(r)}`);
|
|
296
|
+
assert.equal(r.refused.some((f) => f.reason.includes("base_unavailable") && f.class === "base_unavailable"), true, `the kept marker must still refuse a below-base restore, classed base_unavailable: ${JSON.stringify(r)}`);
|
|
276
297
|
assert.equal(await readFile(join(dir, "opaque.txt"), "utf8"), "agent-edited", "never overwritten with a fake baseline");
|
|
277
298
|
});
|
|
278
299
|
});
|
|
300
|
+
run("annulTrack's proof read that cannot re-read the path judged nothing: track_failed (aborted when the signal fired), no permanence, the record kept — never the history law's conflict", async () => {
|
|
301
|
+
await withTree(async (dir, env) => {
|
|
302
|
+
const store = make();
|
|
303
|
+
await writeFile(join(dir, "f.txt"), "pristine");
|
|
304
|
+
await writeFile(join(dir, "g.txt"), "pristine");
|
|
305
|
+
assert.deepEqual(await store.trackEdit("sc", join(dir, "f.txt"), env, dir), { ok: true, minted: true });
|
|
306
|
+
assert.deepEqual(await store.trackEdit("sc", join(dir, "g.txt"), env, dir), { ok: true, minted: true });
|
|
307
|
+
const unreadable = envWithFailingRead(env, async () => ({ ok: false, error: { code: "unknown", message: "transport gone" } }));
|
|
308
|
+
const unjudged = await store.annulTrack("sc", join(dir, "f.txt"), dir, { env: unreadable });
|
|
309
|
+
assert.equal(unjudged.ok, false, "an unread proof never retracts");
|
|
310
|
+
if (!unjudged.ok) {
|
|
311
|
+
assert.equal(unjudged.error.code, "track_failed", `a proof that could not be read is the verb's failure, not the law: ${JSON.stringify(unjudged)}`);
|
|
312
|
+
assert.equal(unjudged.error.permanence, undefined, "an env read error is not the store's own storage fault — unjudged");
|
|
313
|
+
}
|
|
314
|
+
const aborted = new AbortController();
|
|
315
|
+
aborted.abort();
|
|
316
|
+
const cut = await store.annulTrack("sc", join(dir, "g.txt"), dir, { env, signal: aborted.signal });
|
|
317
|
+
assert.equal(cut.ok, false, "a proof read cut by the signal never retracts");
|
|
318
|
+
if (!cut.ok)
|
|
319
|
+
assert.equal(cut.error.code, "aborted", `the signal fired: ${JSON.stringify(cut)}`);
|
|
320
|
+
assert.deepEqual(await store.snapshot("sc", "b1", env, dir), { ok: true });
|
|
321
|
+
await writeFile(join(dir, "f.txt"), "diverged");
|
|
322
|
+
await writeFile(join(dir, "g.txt"), "diverged");
|
|
323
|
+
const r = await store.restore("sc", "b1", env, dir);
|
|
324
|
+
assert.equal(r.outcome, "complete");
|
|
325
|
+
assert.equal(await readFile(join(dir, "f.txt"), "utf8"), "pristine", "the unjudged retraction left the record intact");
|
|
326
|
+
assert.equal(await readFile(join(dir, "g.txt"), "utf8"), "pristine", "the aborted retraction left the record intact");
|
|
327
|
+
});
|
|
328
|
+
});
|
|
279
329
|
run("annulTrack refuses what is no longer a mint: a boundary-referenced record is history (typed conflict, record intact)", async () => {
|
|
280
330
|
await withTree(async (dir, env) => {
|
|
281
331
|
const store = make();
|
|
@@ -301,10 +351,14 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
301
351
|
});
|
|
302
352
|
const t = await store.trackEdit("sc", join(dir, "f.txt"), hostile, dir);
|
|
303
353
|
assert.equal(typeof t.ok, "boolean");
|
|
354
|
+
if (!t.ok)
|
|
355
|
+
assertPermanenceWord(t.error, "trackEdit over a hostile env");
|
|
304
356
|
const sHealthy = await store.trackEdit("sc2", join(dir, "f.txt"), env, dir);
|
|
305
357
|
assert.deepEqual(sHealthy, { ok: true, minted: true });
|
|
306
358
|
const snap = await store.snapshot("sc2", "b", hostile, dir);
|
|
307
359
|
assert.equal(snap.ok, false);
|
|
360
|
+
if (!snap.ok)
|
|
361
|
+
assertPermanenceWord(snap.error, "snapshot over a hostile env");
|
|
308
362
|
const r = await store.restore("sc2", "nope", hostile, dir);
|
|
309
363
|
assert.equal(r.outcome, "failed");
|
|
310
364
|
});
|
|
@@ -364,7 +418,8 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
364
418
|
await shed.write(join(dir, "keep.txt"), "turn-1");
|
|
365
419
|
assert.deepEqual(await src.snapshot("sc", "b1", env, dir), { ok: true });
|
|
366
420
|
assert.equal(await src.reap("sc", ["b0", "b1"]), 0);
|
|
367
|
-
const
|
|
421
|
+
const exported = await src.exportHistory("sc");
|
|
422
|
+
const envelope = exported.ok ? exported.history : null;
|
|
368
423
|
assert.ok(envelope !== null, "a scope with history must export an envelope");
|
|
369
424
|
assert.equal(envelope.formatVersion, "file-history/1");
|
|
370
425
|
assert.ok(envelope.tracked.every((t) => (t.keyKind === "root-relative" || t.keyKind === "absolute") && (t.keyFamily === "posix" || t.keyFamily === "win")), "every tracked row must carry its key's self-description (kind + family) — a bare key cannot be judged after it crosses machines");
|
|
@@ -409,6 +464,17 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
409
464
|
});
|
|
410
465
|
const m = await store.importHistory("sc", missingBlob, async () => undefined);
|
|
411
466
|
assert.equal(m.ok, false);
|
|
467
|
+
if (!m.ok)
|
|
468
|
+
assert.equal(m.error.code, "adopt_failed", "a source that cannot supply a referenced blob");
|
|
469
|
+
if (!m.ok)
|
|
470
|
+
assertPermanenceWord(m.error, "importHistory whose source has no such blob");
|
|
471
|
+
assert.equal(await store.canRestore("sc", "b2"), false);
|
|
472
|
+
const thrown = await store.importHistory("sc", missingBlob, async () => {
|
|
473
|
+
throw Object.assign(new Error("source blob unreadable"), { code: "EACCES" });
|
|
474
|
+
});
|
|
475
|
+
assert.equal(thrown.ok, false);
|
|
476
|
+
if (!thrown.ok)
|
|
477
|
+
assertPermanenceWord(thrown.error, "importHistory whose source fetch threw");
|
|
412
478
|
assert.equal(await store.canRestore("sc", "b2"), false);
|
|
413
479
|
const duplicated = wireEnvelope({
|
|
414
480
|
formatVersion: "file-history/1",
|
|
@@ -507,7 +573,9 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
507
573
|
await shed.write(join(dir, "f.txt"), "source-pristine");
|
|
508
574
|
assert.deepEqual(await src.trackEdit("sc", join(dir, "f.txt"), env, dir), { ok: true, minted: true });
|
|
509
575
|
assert.deepEqual(await src.snapshot("sc", "b1", env, dir), { ok: true });
|
|
510
|
-
const
|
|
576
|
+
const exported = await src.exportHistory("sc");
|
|
577
|
+
assert.ok(exported.ok && exported.history !== null, "a scope with history must export an envelope");
|
|
578
|
+
const envelope = exported.history;
|
|
511
579
|
const dst = make();
|
|
512
580
|
assert.deepEqual(await dst.importHistory("target", envelope, shed.fetch), { ok: true });
|
|
513
581
|
assert.deepEqual(await dst.importHistory("target", envelope, shed.fetch), { ok: true }, "the same envelope arriving twice is a re-publish, not a conflict");
|
|
@@ -515,7 +583,8 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
515
583
|
assert.deepEqual(await dst.trackEdit("target", join(dir, "local.txt"), env, dir), { ok: true, minted: true });
|
|
516
584
|
assert.deepEqual(await dst.snapshot("target", "b-local", env, dir), { ok: true });
|
|
517
585
|
const other = await src.exportHistory("sc");
|
|
518
|
-
|
|
586
|
+
assert.ok(other.ok && other.history !== null);
|
|
587
|
+
const conflict = await dst.importHistory("target", other.history, shed.fetch);
|
|
519
588
|
assert.equal(conflict.ok, false);
|
|
520
589
|
if (!conflict.ok)
|
|
521
590
|
assert.equal(conflict.error.code, "conflict");
|
|
@@ -660,6 +729,7 @@ export async function fileHistoryStoreContract(make, runAssertion) {
|
|
|
660
729
|
const r = await store.restore("sc", "b1", env, dir);
|
|
661
730
|
assert.equal(r.outcome, "partial", JSON.stringify(r));
|
|
662
731
|
assert.equal(r.refused.length, 2, "both kind mismatches are refused, each by name");
|
|
732
|
+
assert.deepEqual(r.refused.map((f) => f.class), ["link_safety", "link_safety"], `a kind mismatch is a link-safety refusal: ${JSON.stringify(r.refused)}`);
|
|
663
733
|
assert.equal(await readFile(join(outside, "victim.txt"), "utf8"), "OUTSIDE-UNTOUCHED", "a leaf link is never written THROUGH");
|
|
664
734
|
assert.deepEqual(r.filesChanged, []);
|
|
665
735
|
}
|
|
@@ -9,7 +9,7 @@ export interface PeerDirectoryContractHooks {
|
|
|
9
9
|
runAssertion?: ContractAssertionRunner;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* The directory contract.
|
|
12
|
+
* The directory contract. Five readings, each the reason the engine can do something:
|
|
13
13
|
* 1. **scope wall** — a listing is the ACCESSOR's scope only; the cross-principal wall's mechanical
|
|
14
14
|
* seat is this filter, so a row leaking across scopes is a tenant leak, not a display bug;
|
|
15
15
|
* 2. **the full set, copies** — live, dead AND deleted rows are all listed (a `name [ref]` is minted
|
|
@@ -18,6 +18,11 @@ export interface PeerDirectoryContractHooks {
|
|
|
18
18
|
* 3. **resolution feeds on the listing** — the engine's own resolver, over the rows as listed, finds
|
|
19
19
|
* a live row by name, still finds a dead one, and answers `deleted` for a tombstone;
|
|
20
20
|
* 4. **restore is CAS-on-absent** (only where the host implements it) — an absent row is restored and
|
|
21
|
-
* then listed; a present row is never overwritten (`false`, the existing row byte-unchanged)
|
|
21
|
+
* then listed; a present row is never overwritten (`false`, the existing row byte-unchanged);
|
|
22
|
+
* 5. **every listed row is one the engine's reader accepts** — the engine reads a listing through
|
|
23
|
+
* `readPeerSessionRecord` and one refused row faults the WHOLE listing (dropping it would resize refs and
|
|
24
|
+
* change resolution over the full set), so a row that does not say `where`, or a non-local row carrying a
|
|
25
|
+
* machine-bound member, takes the directory's peer lane down. (How a host decides `where` is its own;
|
|
26
|
+
* the kit does not require the written value back.)
|
|
22
27
|
*/
|
|
23
28
|
export declare function peerDirectoryContract(mk: () => PeerDirectory, hooks: PeerDirectoryContractHooks): Promise<void>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
|
-
import { resolvePeerSessions } from "../../agents/peer-directory.js";
|
|
2
|
+
import { readPeerSessionRecord, resolvePeerSessions } from "../../agents/peer-directory.js";
|
|
3
3
|
import { beginContract } from "./contract-harness.js";
|
|
4
4
|
export const PEER_DIRECTORY_CONTRACT_SCOPE = "peer-kit-A";
|
|
5
5
|
function row(name, sessionId, over = {}) {
|
|
6
|
-
return { schemaVersion: 1, sessionId, scope: PEER_DIRECTORY_CONTRACT_SCOPE, name, startedAt: 1_000, updatedAt: 2_000, liveness: "live", ...over };
|
|
6
|
+
return { schemaVersion: 1, sessionId, scope: PEER_DIRECTORY_CONTRACT_SCOPE, name, startedAt: 1_000, updatedAt: 2_000, liveness: "live", where: "hosted", ...over };
|
|
7
7
|
}
|
|
8
8
|
async function withDirs(mk, fn) {
|
|
9
9
|
const live = [];
|
|
@@ -78,5 +78,17 @@ export async function peerDirectoryContract(mk, hooks) {
|
|
|
78
78
|
assert.equal(current.liveness, "live");
|
|
79
79
|
assert.equal(current.updatedAt, 9_000, "the existing row is byte-unchanged by a losing restore");
|
|
80
80
|
});
|
|
81
|
+
run("every listed row is one the engine's row reader accepts (live, dead and deleted alike)", async (make) => {
|
|
82
|
+
const d = make();
|
|
83
|
+
await hooks.write(d, row("alpha", "sess-a"));
|
|
84
|
+
await hooks.write(d, row("beta", "sess-b", { liveness: "dead", diedAt: 3_000 }));
|
|
85
|
+
await hooks.write(d, row("gamma", "sess-c", { liveness: "deleted", diedAt: 3_000 }));
|
|
86
|
+
const listed = await d.listPeerSessions({ scope: S });
|
|
87
|
+
assert.equal(listed.length, 3);
|
|
88
|
+
for (const r of listed) {
|
|
89
|
+
const read = readPeerSessionRecord(r);
|
|
90
|
+
assert.ok(read.ok, `listed row ${r.sessionId} is refused by the engine's reader (${read.ok ? "" : `${read.reason}${read.field !== undefined ? `: ${read.field}` : ""}`})`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
81
93
|
await settle();
|
|
82
94
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type SessionPolicyStore } from "../session-policy-store.js";
|
|
2
|
+
import { type ContractAssertionRunner } from "./contract-harness.js";
|
|
3
|
+
/**
|
|
4
|
+
* The full cross-backend contract for the operator part of a {@link SessionPolicyStore} row. Every case builds its store via
|
|
5
|
+
* `make()`; with no runner the cases run sequentially and the first failure throws.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sessionPolicyStoreContract(make: () => SessionPolicyStore, runAssertion?: ContractAssertionRunner): Promise<void>;
|