@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
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
2
|
import { uuidv7 } from "../../internal/harness.js";
|
|
3
|
-
import { hasSessionFork, isSessionConflict } from "../session.js";
|
|
3
|
+
import { SESSION_LIST_LIMITS, hasSessionFork, isSessionConflict } from "../session.js";
|
|
4
4
|
import { readMember } from "../thrown-value.js";
|
|
5
5
|
import { receiptRefusal, receiveOwnProperties } from "../own-property-receipt.js";
|
|
6
6
|
import { beginContract } from "./contract-harness.js";
|
|
7
|
-
import { registerSessionIncarnationCases } from "./session-store-incarnation-contract.js";
|
|
8
7
|
const AT = "2026-10-18T00:00:00.000Z";
|
|
9
8
|
const AT_MS = Date.parse(AT);
|
|
10
9
|
const STAMP = { api: "openai-completions", provider: "contract-kit", model: "contract-kit-model" };
|
|
@@ -12,9 +11,9 @@ const ZERO_USAGE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalToke
|
|
|
12
11
|
function userEntry(id, parentId, text = `t-${id}`) {
|
|
13
12
|
return { id, parentId, type: "message", timestamp: AT, message: { role: "user", content: [{ type: "text", text }], timestamp: AT_MS } };
|
|
14
13
|
}
|
|
15
|
-
function assistantEntry(id, parentId,
|
|
16
|
-
const content =
|
|
17
|
-
return { id, parentId, type: "message", timestamp: AT, message: { role: "assistant", content, ...STAMP, usage: ZERO_USAGE, stopReason:
|
|
14
|
+
function assistantEntry(id, parentId, ...toolCalls) {
|
|
15
|
+
const content = toolCalls.length > 0 ? toolCalls.map((call) => ({ type: "toolCall", id: call, name: "Read", arguments: { file_path: "/kit/x" } })) : [{ type: "text", text: `a-${id}` }];
|
|
16
|
+
return { id, parentId, type: "message", timestamp: AT, message: { role: "assistant", content, ...STAMP, usage: ZERO_USAGE, stopReason: toolCalls.length > 0 ? "toolUse" : "stop", timestamp: AT_MS } };
|
|
18
17
|
}
|
|
19
18
|
function toolResultEntry(id, parentId, toolCallId) {
|
|
20
19
|
return { id, parentId, type: "message", timestamp: AT, message: { role: "toolResult", toolCallId, toolName: "Read", content: [{ type: "text", text: "ok" }], isError: false, timestamp: AT_MS } };
|
|
@@ -89,8 +88,22 @@ function assertNewestFirst(rows) {
|
|
|
89
88
|
function registerUntyped(store, sessionId, owner) {
|
|
90
89
|
return Reflect.apply(store.register, store, [sessionId, owner]);
|
|
91
90
|
}
|
|
92
|
-
const
|
|
91
|
+
const hasHead = (s) => typeof s.headOf === "function";
|
|
92
|
+
const hasOwnership = (s) => hasHead(s) && typeof s.register === "function";
|
|
93
93
|
const declaresPlacements = (s) => s.placements?.subagent !== undefined;
|
|
94
|
+
const headOf = (store, sessionId) => store.headOf(sessionId);
|
|
95
|
+
async function ownerOf(store, sessionId, what) {
|
|
96
|
+
const head = await headOf(store, sessionId);
|
|
97
|
+
assert.ok(head !== undefined, `${what}: the row exists, so headOf must answer it`);
|
|
98
|
+
return head.owner;
|
|
99
|
+
}
|
|
100
|
+
async function tokenOf(store, sessionId, what) {
|
|
101
|
+
const head = await headOf(store, sessionId);
|
|
102
|
+
assert.ok(head !== undefined, `${what}: the row exists, so headOf must answer it`);
|
|
103
|
+
assert.equal(typeof head.incarnation, "string", `${what}: a session row created through this store must carry an incarnation`);
|
|
104
|
+
assert.notEqual(head.incarnation, "", `${what}: an empty token names nobody`);
|
|
105
|
+
return head.incarnation;
|
|
106
|
+
}
|
|
94
107
|
async function honorsRequireExisting(store) {
|
|
95
108
|
const probe = freshId();
|
|
96
109
|
await store.acquire(probe);
|
|
@@ -165,8 +178,8 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
165
178
|
noteTaskRun: (s) => typeof s.noteTaskRun === "function",
|
|
166
179
|
listPlaced: (s) => declaresPlacements(s) && typeof s.listPlaced === "function",
|
|
167
180
|
placements: declaresPlacements,
|
|
181
|
+
headOf: hasHead,
|
|
168
182
|
ownership: hasOwnership,
|
|
169
|
-
incarnationOf: (s) => typeof s.incarnationOf === "function",
|
|
170
183
|
replica: () => hooks.replica !== undefined,
|
|
171
184
|
};
|
|
172
185
|
const faceRun = (name, needs, fn) => run(name, async (store, open) => {
|
|
@@ -214,8 +227,8 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
214
227
|
}
|
|
215
228
|
if (typeof store.list === "function")
|
|
216
229
|
assert.equal(rowOf(await store.list(), id), undefined, "the refused acquire must not surface on the session list");
|
|
217
|
-
if (
|
|
218
|
-
assert.equal(await store
|
|
230
|
+
if (hasHead(store))
|
|
231
|
+
assert.equal(await headOf(store, id), undefined, "nor have a row");
|
|
219
232
|
report("requireExisting", honored ? "verified" : "absent");
|
|
220
233
|
});
|
|
221
234
|
run("[acquire] two concurrent acquires of one id are one session: one log, and the second write on the same parent loses the CAS", async (store) => {
|
|
@@ -438,10 +451,10 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
438
451
|
assert.equal(await store.fork(missing), null);
|
|
439
452
|
if (await honorsRequireExisting(store))
|
|
440
453
|
await rejectsWithCode(() => store.acquire(missing, { requireExisting: true }), "not_found", "a fork of a missing source must not create it");
|
|
441
|
-
if (
|
|
442
|
-
assert.equal(await store
|
|
454
|
+
if (hasHead(store))
|
|
455
|
+
assert.equal(await headOf(store, missing), undefined, "a fork of a missing source must not create it");
|
|
443
456
|
});
|
|
444
|
-
faceRun("[fork] a fork never inherits an in-flight assistant turn: a
|
|
457
|
+
faceRun("[fork] a fork never inherits an in-flight assistant turn: a turn with a tool call no result answers yet (with or without sibling results already in) is left out; a turn every call of which is answered is kept", ["fork"], async (store) => {
|
|
445
458
|
const parallel = freshId();
|
|
446
459
|
const p = (await store.acquire(parallel)).session;
|
|
447
460
|
await append(p, userEntry("u1", null));
|
|
@@ -451,10 +464,18 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
451
464
|
const sequential = freshId();
|
|
452
465
|
const q = (await store.acquire(sequential)).session;
|
|
453
466
|
await append(q, userEntry("u1", null));
|
|
454
|
-
await append(q, assistantEntry("a1", "u1", "call-1"));
|
|
467
|
+
await append(q, assistantEntry("a1", "u1", "call-1", "call-2"));
|
|
455
468
|
await append(q, toolResultEntry("r1", "a1", "call-1"));
|
|
456
469
|
const qf = await store.fork(sequential);
|
|
457
|
-
assert.deepEqual(await pathIds((await readBack(store, qf))), ["u1"], "a sibling call's committed result does not settle the turn");
|
|
470
|
+
assert.deepEqual(await pathIds((await readBack(store, qf))), ["u1"], "a sibling call's committed result does not settle the turn: call-2 has none");
|
|
471
|
+
const answered = freshId();
|
|
472
|
+
const t = (await store.acquire(answered)).session;
|
|
473
|
+
await append(t, userEntry("u1", null));
|
|
474
|
+
await append(t, assistantEntry("a1", "u1", "call-1", "call-2"));
|
|
475
|
+
await append(t, toolResultEntry("r1", "a1", "call-2"));
|
|
476
|
+
await append(t, toolResultEntry("r2", "r1", "call-1"));
|
|
477
|
+
const tf = await store.fork(answered);
|
|
478
|
+
assert.deepEqual(await pathIds((await readBack(store, tf))), ["u1", "a1", "r1", "r2"], "a tool turn every call of which has its result is settled: a fork keeps it");
|
|
458
479
|
const settled = freshId();
|
|
459
480
|
const s = (await store.acquire(settled)).session;
|
|
460
481
|
await append(s, userEntry("u1", null));
|
|
@@ -462,7 +483,7 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
462
483
|
const sf = await store.fork(settled);
|
|
463
484
|
assert.deepEqual(await pathIds((await readBack(store, sf))), ["u1", "a1"], "a settled text turn forks whole");
|
|
464
485
|
});
|
|
465
|
-
faceRun("[list] list projects each
|
|
486
|
+
faceRun("[list] list projects each session row once, newest-first by lastActiveAt, and a touch counts as activity", ["list"], async (store) => {
|
|
466
487
|
const [a, b, c] = [freshId(), freshId(), freshId()];
|
|
467
488
|
await store.acquire(a);
|
|
468
489
|
await tick();
|
|
@@ -473,7 +494,7 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
473
494
|
assertNewestFirst(rows);
|
|
474
495
|
for (const id of [a, b, c])
|
|
475
496
|
assert.equal(rows.filter((r) => r.sessionId === id).length, 1, `session ${id} is listed exactly once`);
|
|
476
|
-
assertActivityOrder(rows, [c, b, a], "
|
|
497
|
+
assertActivityOrder(rows, [c, b, a], "a session created later is more recent");
|
|
477
498
|
for (const r of rows)
|
|
478
499
|
assert.ok(r.createdAt === undefined || (typeof r.createdAt === "string" && r.createdAt !== ""), "createdAt is a real timestamp or absent, never an empty placeholder");
|
|
479
500
|
const before = rowOf(rows, a).lastActiveAt;
|
|
@@ -492,25 +513,148 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
492
513
|
assert.equal(rowOf(rows, forkedId)?.forkedFrom, src, "the fork's row carries its lineage");
|
|
493
514
|
assert.equal(rowOf(rows, src)?.forkedFrom, undefined, "the source is no fork");
|
|
494
515
|
});
|
|
495
|
-
faceRun("[list] noteTaskRun lands on the summary
|
|
516
|
+
faceRun("[list] noteTaskRun lands on the summary; a call without a run id clears lastTaskRunId; an unknown id never throws", ["noteTaskRun"], async (store) => {
|
|
496
517
|
await store.noteTaskRun(freshId(), "kit-task", "kit-run");
|
|
497
518
|
if (typeof store.list !== "function")
|
|
498
519
|
return;
|
|
499
|
-
const
|
|
520
|
+
const a = freshId();
|
|
500
521
|
await store.acquire(a);
|
|
501
|
-
await tick();
|
|
502
|
-
await store.acquire(b);
|
|
503
|
-
await tick();
|
|
504
522
|
await store.noteTaskRun(a, "task-1", "run-1");
|
|
505
523
|
let rows = await store.list();
|
|
506
524
|
assert.equal(rowOf(rows, a)?.lastTaskId, "task-1");
|
|
507
525
|
assert.equal(rowOf(rows, a)?.lastTaskRunId, "run-1");
|
|
508
|
-
assertActivityOrder(rows, [a, b], "a run is activity: the session that just ran is the most recently active");
|
|
509
526
|
await store.noteTaskRun(a, "task-2");
|
|
510
527
|
rows = await store.list();
|
|
511
528
|
assert.equal(rowOf(rows, a)?.lastTaskId, "task-2");
|
|
512
529
|
assert.equal(rowOf(rows, a)?.lastTaskRunId, undefined, "the two seats describe one run: a newer task with no run id must not keep the older run's id");
|
|
513
530
|
});
|
|
531
|
+
faceRun("[list] the list reads the rows, not a cache: sessions are listed after their cached view is dropped and on a store reopened over the same rows (a restart), newest first", ["list"], async (store, open) => {
|
|
532
|
+
const [a, b] = [freshId(), freshId()];
|
|
533
|
+
await append((await store.acquire(a)).session, userEntry("e1", null));
|
|
534
|
+
await tick();
|
|
535
|
+
await append((await store.acquire(b)).session, userEntry("e1", null));
|
|
536
|
+
if (typeof store.forget === "function") {
|
|
537
|
+
await store.forget(a);
|
|
538
|
+
assert.ok(rowOf(await store.list({ limit: SESSION_LIST_LIMITS.max }), a) !== undefined, "a session whose cached view was dropped is still a row");
|
|
539
|
+
}
|
|
540
|
+
const second = await open(hooks.reopen(store));
|
|
541
|
+
const rows = await second.list({ limit: SESSION_LIST_LIMITS.max });
|
|
542
|
+
assertNewestFirst(rows);
|
|
543
|
+
assertActivityOrder(rows, [b, a], "a restart keeps the activity order the rows recorded");
|
|
544
|
+
});
|
|
545
|
+
faceRun("[list] a touch is recorded on the row: the touched session is still the most recent on a store reopened over the same rows (a restart)", ["list"], async (store, open) => {
|
|
546
|
+
const [a, b] = [freshId(), freshId()];
|
|
547
|
+
await append((await store.acquire(a)).session, userEntry("e1", null));
|
|
548
|
+
await tick();
|
|
549
|
+
await append((await store.acquire(b)).session, userEntry("e1", null));
|
|
550
|
+
await tick();
|
|
551
|
+
await store.touch(a);
|
|
552
|
+
const second = await open(hooks.reopen(store));
|
|
553
|
+
assertActivityOrder(await second.list({ limit: SESSION_LIST_LIMITS.max }), [a, b], "the touch outlives the process that made it");
|
|
554
|
+
});
|
|
555
|
+
faceRun("[list] a cached view of an occupant the listed row does not name lends it nothing: after another instance deleted the row and registered the id to another owner, the row lists without the old occupant's run", ["list", "noteTaskRun", "ownership", "replica"], async (store, open) => {
|
|
556
|
+
const id = freshId();
|
|
557
|
+
const other = await open(hooks.replica(store));
|
|
558
|
+
await store.register(id, "kit-alice");
|
|
559
|
+
await store.acquire(id);
|
|
560
|
+
await store.noteTaskRun(id, "alice-task", "alice-run");
|
|
561
|
+
await other.acquire(id);
|
|
562
|
+
await hooks.deleteSession(other, id);
|
|
563
|
+
await other.register(id, "kit-bob");
|
|
564
|
+
const row = rowOf(await store.list({ owner: "kit-bob", limit: SESSION_LIST_LIMITS.max }), id);
|
|
565
|
+
assert.ok(row !== undefined, "the new owner's row is listed");
|
|
566
|
+
assert.equal(row.lastTaskId, undefined, "the previous occupant's run is not lent to the new owner's row");
|
|
567
|
+
assert.equal(row.lastTaskRunId, undefined);
|
|
568
|
+
});
|
|
569
|
+
faceRun("[list] paging survives a dropped cache: a session whose cached view is dropped between two pages is not read again", ["list", "ownership", "forget"], async (store) => {
|
|
570
|
+
const owner = `kit-forget-${uuidv7().slice(-12)}`;
|
|
571
|
+
const ids = [freshId(), freshId(), freshId()];
|
|
572
|
+
for (const id of ids) {
|
|
573
|
+
await store.register(id, owner);
|
|
574
|
+
await tick();
|
|
575
|
+
}
|
|
576
|
+
for (const id of ids)
|
|
577
|
+
await store.acquire(id);
|
|
578
|
+
const first = await store.list({ owner, limit: 1 });
|
|
579
|
+
assert.equal(first.length, 1);
|
|
580
|
+
const top = first[0];
|
|
581
|
+
await store.forget(top.sessionId);
|
|
582
|
+
const rest = await store.list({ owner, after: { lastActiveAt: top.lastActiveAt, sessionId: top.sessionId } });
|
|
583
|
+
assert.ok(!rest.some((r) => r.sessionId === top.sessionId), "the first page's row is not on the next page");
|
|
584
|
+
assert.equal(rest.length, ids.length - 1, "and every other row is");
|
|
585
|
+
});
|
|
586
|
+
faceRun("[list] owner filters by the row's owner cell: an owner's rows only; with includeUnowned the unowned rows too; no owner = every owner's rows", ["list", "ownership"], async (store) => {
|
|
587
|
+
const [alice, bob] = [`kit-alice-${uuidv7().slice(-12)}`, `kit-bob-${uuidv7().slice(-12)}`];
|
|
588
|
+
const [a1, a2, b1, u1] = [freshId(), freshId(), freshId(), freshId()];
|
|
589
|
+
await store.register(a1, alice);
|
|
590
|
+
await store.register(a2, alice);
|
|
591
|
+
await store.register(b1, bob);
|
|
592
|
+
await store.acquire(u1);
|
|
593
|
+
const ids = (rows) => new Set(rows.map((r) => r.sessionId));
|
|
594
|
+
const own = ids(await store.list({ owner: alice, limit: SESSION_LIST_LIMITS.max }));
|
|
595
|
+
assert.ok(own.has(a1) && own.has(a2), "the owner's rows are listed");
|
|
596
|
+
assert.ok(!own.has(b1) && !own.has(u1), "another owner's row and an unowned row are not");
|
|
597
|
+
const withUnowned = ids(await store.list({ owner: alice, includeUnowned: true, limit: SESSION_LIST_LIMITS.max }));
|
|
598
|
+
assert.ok(withUnowned.has(a1) && withUnowned.has(a2) && withUnowned.has(u1), "includeUnowned adds the unowned rows");
|
|
599
|
+
assert.ok(!withUnowned.has(b1), "and never another owner's");
|
|
600
|
+
const all = ids(await store.list({ limit: SESSION_LIST_LIMITS.max }));
|
|
601
|
+
for (const id of [a1, a2, b1, u1])
|
|
602
|
+
assert.ok(all.has(id), `no owner filter lists every owner's rows (${id})`);
|
|
603
|
+
});
|
|
604
|
+
faceRun("[list] keyset paging reads each row once: pages cut with limit and the after cursor, concatenated, are the one-page list — ties ordered by session id, descending", ["list", "ownership"], async (store) => {
|
|
605
|
+
const owner = `kit-pager-${uuidv7().slice(-12)}`;
|
|
606
|
+
const ids = [freshId(), freshId(), freshId(), freshId(), freshId()];
|
|
607
|
+
for (const id of ids)
|
|
608
|
+
await store.register(id, owner);
|
|
609
|
+
const whole = await store.list({ owner, limit: SESSION_LIST_LIMITS.max });
|
|
610
|
+
assert.deepEqual(new Set(whole.map((r) => r.sessionId)), new Set(ids), "the owner's five rows");
|
|
611
|
+
for (let i = 1; i < whole.length; i++) {
|
|
612
|
+
const [prev, row] = [whole[i - 1], whole[i]];
|
|
613
|
+
assert.ok(prev.lastActiveAt > row.lastActiveAt || (prev.lastActiveAt === row.lastActiveAt && prev.sessionId > row.sessionId), `row ${i} follows row ${i - 1} in (lastActiveAt, sessionId) descending order`);
|
|
614
|
+
}
|
|
615
|
+
const paged = [];
|
|
616
|
+
let after;
|
|
617
|
+
for (let pages = 0; pages < 10; pages++) {
|
|
618
|
+
const page = await store.list({ owner, limit: 2, ...(after !== undefined ? { after } : {}) });
|
|
619
|
+
assert.ok(page.length <= 2, "a page holds at most limit rows");
|
|
620
|
+
if (page.length === 0)
|
|
621
|
+
break;
|
|
622
|
+
paged.push(...page.map((r) => r.sessionId));
|
|
623
|
+
const last = page[page.length - 1];
|
|
624
|
+
after = { lastActiveAt: last.lastActiveAt, sessionId: last.sessionId };
|
|
625
|
+
}
|
|
626
|
+
assert.deepEqual(paged, whole.map((r) => r.sessionId), "the pages, in order, are the one-page list: no row twice, none skipped");
|
|
627
|
+
});
|
|
628
|
+
faceRun("[list] a query without limit reads the default page; an explicit limit reads that many", ["list", "ownership"], async (store) => {
|
|
629
|
+
const owner = `kit-page-${uuidv7().slice(-12)}`;
|
|
630
|
+
const total = SESSION_LIST_LIMITS.default + 1;
|
|
631
|
+
for (let i = 0; i < total; i++)
|
|
632
|
+
await store.register(freshId(), owner);
|
|
633
|
+
assert.equal((await store.list({ owner })).length, SESSION_LIST_LIMITS.default, `no limit = the first ${SESSION_LIST_LIMITS.default} rows`);
|
|
634
|
+
assert.equal((await store.list({ owner, limit: total })).length, total);
|
|
635
|
+
assert.equal((await store.list({ owner, limit: 1 })).length, 1);
|
|
636
|
+
});
|
|
637
|
+
faceRun("[list] a bad query is refused config.invalid_argument, never read as a default: a limit out of 1..max or not an integer, a member of the wrong type, includeUnowned without owner, a malformed cursor, a member the store does not read", ["list"], async (store) => {
|
|
638
|
+
const bad = [
|
|
639
|
+
["limit 0", { limit: 0 }],
|
|
640
|
+
["a negative limit", { limit: -1 }],
|
|
641
|
+
["a fractional limit", { limit: 1.5 }],
|
|
642
|
+
["a limit past the bound", { limit: SESSION_LIST_LIMITS.max + 1 }],
|
|
643
|
+
["a limit spelled as a string", { limit: "10" }],
|
|
644
|
+
["a NaN limit", { limit: Number.NaN }],
|
|
645
|
+
["an owner that is not a string", { owner: 7 }],
|
|
646
|
+
["includeUnowned that is not a boolean", { owner: "kit-x", includeUnowned: "yes" }],
|
|
647
|
+
["includeUnowned without owner", { includeUnowned: true }],
|
|
648
|
+
["a cursor without a session id", { after: { lastActiveAt: 1 } }],
|
|
649
|
+
["a cursor whose time is not a finite number", { after: { lastActiveAt: Number.POSITIVE_INFINITY, sessionId: "x" } }],
|
|
650
|
+
["a cursor carrying another member", { after: { lastActiveAt: 1, sessionId: "x", page: 2 } }],
|
|
651
|
+
["a member the store does not read", { kitUnreadFilter: "x" }],
|
|
652
|
+
["a query that is not an object", 7],
|
|
653
|
+
];
|
|
654
|
+
for (const [what, query] of bad) {
|
|
655
|
+
await rejectsWithCode(() => Reflect.apply(store.list, store, [query]), "config.invalid_argument", `list(${what})`);
|
|
656
|
+
}
|
|
657
|
+
});
|
|
514
658
|
faceRun("[placements] a placed session stays off the host list — before and after a restart, also when read back with require-existing", ["placements"], async (store, open) => {
|
|
515
659
|
const placed = freshId();
|
|
516
660
|
const plain = freshId();
|
|
@@ -620,64 +764,230 @@ async function runSessionStoreContract(make, hooks) {
|
|
|
620
764
|
assert.ok(scoped.includes(complete) && scoped.includes(incomplete) && !scoped.includes(elsewhere), "scope narrows to one tenant");
|
|
621
765
|
assert.ok(!(await store.listPlaced("subagent", { olderThanMs: 60 * 60 * 1000 })).some((r) => r.sessionId === complete), "a session placed just now is not an hour old");
|
|
622
766
|
});
|
|
623
|
-
faceRun("[
|
|
624
|
-
const
|
|
625
|
-
assert.equal(await store
|
|
767
|
+
faceRun("[head] headOf answers undefined for an id with no row; for a row, its owner, its occupant and its active leaf — the leaf follows appends and a branch switch", ["headOf"], async (store) => {
|
|
768
|
+
const id = freshId();
|
|
769
|
+
assert.equal(await headOf(store, id), undefined, "no row ⇒ undefined");
|
|
770
|
+
const { session } = await store.acquire(id);
|
|
771
|
+
assert.deepEqual(await headOf(store, id), { owner: null, incarnation: (await session.getMetadata()).incarnation, leafId: null }, "a row acquire created: no owner, its occupant, no leaf");
|
|
772
|
+
await append(session, userEntry("e1", null));
|
|
773
|
+
await append(session, assistantEntry("e2", "e1"));
|
|
774
|
+
assert.equal((await headOf(store, id))?.leafId, "e2", "an append moves the head's leaf");
|
|
775
|
+
await session.getStorage().setLeafId("e1");
|
|
776
|
+
assert.equal((await headOf(store, id))?.leafId, "e1", "a branch switch moves it back");
|
|
777
|
+
await session.getStorage().setLeafId(null);
|
|
778
|
+
assert.equal((await headOf(store, id))?.leafId, null);
|
|
779
|
+
});
|
|
780
|
+
faceRun("[head] the head lives in the row: a store reopened over the same rows (a restart) answers the same owner, occupant and leaf", ["headOf"], async (store, open) => {
|
|
781
|
+
const id = freshId();
|
|
782
|
+
const { session } = await store.acquire(id);
|
|
783
|
+
await append(session, userEntry("e1", null));
|
|
784
|
+
await append(session, assistantEntry("e2", "e1"));
|
|
785
|
+
await session.getStorage().setLeafId("e1");
|
|
786
|
+
const before = await headOf(store, id);
|
|
787
|
+
const second = await open(hooks.reopen(store));
|
|
788
|
+
assert.deepEqual(await headOf(second, id), before, "a head an instance kept for itself would answer differently after every restart");
|
|
789
|
+
});
|
|
790
|
+
faceRun("[head] one read, one generation: while an id is deleted and created again under other owners, no head pairs one generation's owner with another's occupant", ["ownership"], async (store) => {
|
|
791
|
+
const id = freshId();
|
|
792
|
+
const generations = [];
|
|
793
|
+
const reads = [];
|
|
794
|
+
const probe = () => {
|
|
795
|
+
for (let k = 0; k < 3; k++)
|
|
796
|
+
reads.push(headOf(store, id));
|
|
797
|
+
};
|
|
798
|
+
for (let i = 0; i < 12; i++) {
|
|
799
|
+
if (i > 0) {
|
|
800
|
+
probe();
|
|
801
|
+
await hooks.deleteSession(store, id);
|
|
802
|
+
}
|
|
803
|
+
probe();
|
|
804
|
+
await store.register(id, `kit-gen-${i}`);
|
|
805
|
+
probe();
|
|
806
|
+
await store.acquire(id);
|
|
807
|
+
generations.push((await headOf(store, id)));
|
|
808
|
+
}
|
|
809
|
+
const whole = new Set(generations.map((g) => `${g.owner}|${g.incarnation}`));
|
|
810
|
+
assert.equal(whole.size, generations.length, "each generation is its own owner and occupant");
|
|
811
|
+
for (const head of await Promise.all(reads)) {
|
|
812
|
+
if (head === undefined)
|
|
813
|
+
continue;
|
|
814
|
+
assert.ok(whole.has(`${head.owner}|${head.incarnation}`), `a head read ${head.owner} with occupant ${head.incarnation}, a pair no generation of the row ever held`);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
faceRun("[ownership] register on an id with no row creates its row recording the owner — a string, or null for anonymous; a row acquire created records null", ["ownership"], async (store) => {
|
|
818
|
+
const [owned, anonymous, acquired] = [freshId(), freshId(), freshId()];
|
|
626
819
|
await store.register(owned, "kit-alice");
|
|
627
820
|
await store.register(anonymous, null);
|
|
628
|
-
|
|
629
|
-
assert.equal(await
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const
|
|
821
|
+
await store.acquire(acquired);
|
|
822
|
+
assert.equal(await ownerOf(store, owned, "a registered id"), "kit-alice");
|
|
823
|
+
assert.equal(await ownerOf(store, anonymous, "an anonymous registration"), null);
|
|
824
|
+
assert.equal(await ownerOf(store, acquired, "a row acquire created"), null, "acquire names no owner");
|
|
825
|
+
const token = await tokenOf(store, owned, "a registered row");
|
|
826
|
+
assert.equal((await (await readBack(store, owned)).getMetadata()).incarnation, token, "the registered row is the session acquire then hands out");
|
|
827
|
+
});
|
|
828
|
+
faceRun("[ownership] register on a row that exists does nothing, whatever it carries — an owned row, an anonymous row, a row acquire created, a placed row", ["ownership"], async (store) => {
|
|
829
|
+
const [owned, anonymous, acquired] = [freshId(), freshId(), freshId()];
|
|
633
830
|
await store.register(owned, "kit-alice");
|
|
634
831
|
await store.register(owned, "kit-bob");
|
|
635
832
|
await store.register(owned, null);
|
|
636
833
|
await registerUntyped(store, owned, undefined);
|
|
637
|
-
assert.equal(await
|
|
834
|
+
assert.equal(await ownerOf(store, owned, "an owned row"), "kit-alice");
|
|
638
835
|
await store.register(anonymous, null);
|
|
639
836
|
await store.register(anonymous, "kit-bob");
|
|
640
|
-
assert.equal(await
|
|
837
|
+
assert.equal(await ownerOf(store, anonymous, "an anonymous row"), null, "an anonymous row is not claimable by a later register");
|
|
838
|
+
await store.acquire(acquired);
|
|
839
|
+
await store.register(acquired, "kit-carol");
|
|
840
|
+
assert.equal(await ownerOf(store, acquired, "a row acquire created"), null, "there is no claiming of a row that exists");
|
|
841
|
+
if (declaresPlacements(store)) {
|
|
842
|
+
for (const owner of [`kit-claimant-${freshId()}`, null]) {
|
|
843
|
+
const placed = freshId();
|
|
844
|
+
await store.acquire(placed, { placement: placementFor() });
|
|
845
|
+
const recorded = await ownerOf(store, placed, "a placed row");
|
|
846
|
+
await store.register(placed, owner);
|
|
847
|
+
assert.equal(await ownerOf(store, placed, "a placed row after a register"), recorded, `a register carrying ${owner === null ? "null" : "an owner"} left the placed row's owner as its placing write recorded it`);
|
|
848
|
+
}
|
|
849
|
+
const sequenced = freshId();
|
|
850
|
+
await store.acquire(sequenced, { placement: placementFor() });
|
|
851
|
+
const recordedSeq = await ownerOf(store, sequenced, "a placed row (sequence)");
|
|
852
|
+
await store.register(sequenced, null);
|
|
853
|
+
await store.register(sequenced, `kit-claimant-${freshId()}`);
|
|
854
|
+
assert.equal(await ownerOf(store, sequenced, "a placed row after two registers"), recordedSeq, "two registers in sequence (null, then a name) left the placed row's owner as its placing write recorded it");
|
|
855
|
+
}
|
|
641
856
|
});
|
|
642
|
-
faceRun("[ownership] a FIRST register whose owner is neither a
|
|
857
|
+
faceRun("[ownership] a FIRST register whose owner is neither a principal name nor null is refused loudly and leaves no row behind — a blank string, or one holding a control or invisible character, names nobody", ["ownership"], async (store) => {
|
|
643
858
|
const id = freshId();
|
|
644
859
|
await thrownBy(() => registerUntyped(store, id, undefined), "a first register with owner undefined");
|
|
645
860
|
await thrownBy(() => registerUntyped(store, id, 42), "a first register with a numeric owner");
|
|
646
|
-
|
|
861
|
+
await thrownBy(() => registerUntyped(store, id, ""), "a first register with the empty string as owner");
|
|
862
|
+
await thrownBy(() => registerUntyped(store, id, " \t "), "a first register with a blank string as owner");
|
|
863
|
+
await thrownBy(() => registerUntyped(store, id, "ali\u0000ce"), "a first register with a control character inside the owner");
|
|
864
|
+
await thrownBy(() => registerUntyped(store, id, "\u200b"), "a first register with an invisible character as owner");
|
|
865
|
+
assert.equal(await headOf(store, id), undefined, "the refused writes stored nothing");
|
|
647
866
|
await store.register(id, "kit-carol");
|
|
648
|
-
assert.equal(await
|
|
867
|
+
assert.equal(await ownerOf(store, id, "the first well-formed register"), "kit-carol", "the id is still ownable");
|
|
649
868
|
});
|
|
650
869
|
faceRun("[ownership] two concurrent first registers: each caller's own re-read sees the same winner, one of the two", ["ownership"], async (store) => {
|
|
651
870
|
const id = freshId();
|
|
652
871
|
const caller = async (owner) => {
|
|
653
872
|
await store.register(id, owner);
|
|
654
|
-
return
|
|
873
|
+
return ownerOf(store, id, "after a register");
|
|
655
874
|
};
|
|
656
875
|
const [x, y] = await Promise.all([caller("kit-x"), caller("kit-y")]);
|
|
657
876
|
assert.equal(x, y, "the loser must see the winner on its own re-read, or both callers attach to one session");
|
|
658
877
|
assert.ok(x === "kit-x" || x === "kit-y");
|
|
659
|
-
assert.equal(await
|
|
878
|
+
assert.equal(await ownerOf(store, id, "the settled row"), x, "and the row keeps that winner");
|
|
660
879
|
});
|
|
661
|
-
faceRun("[ownership] the
|
|
662
|
-
const
|
|
663
|
-
await store.register(
|
|
664
|
-
await append((await store.acquire(
|
|
665
|
-
await store.
|
|
666
|
-
|
|
880
|
+
faceRun("[ownership] the owner is a cell of the row: it reads back after the cached view is dropped and on a store reopened over the same rows (a restart)", ["ownership"], async (store, open) => {
|
|
881
|
+
const [owned, acquired] = [freshId(), freshId()];
|
|
882
|
+
await store.register(owned, "kit-alice");
|
|
883
|
+
await append((await store.acquire(owned)).session, userEntry("e1", null));
|
|
884
|
+
await store.acquire(acquired);
|
|
885
|
+
if (typeof store.forget === "function") {
|
|
886
|
+
await store.forget(owned);
|
|
887
|
+
assert.equal(await ownerOf(store, owned, "a forgotten session"), "kit-alice", "a live session must never lose its owner with the cached view");
|
|
888
|
+
}
|
|
889
|
+
const second = await open(hooks.reopen(store));
|
|
890
|
+
assert.equal(await ownerOf(second, owned, "after a restart"), "kit-alice", "an owner an instance kept for itself is lost on every restart, and the session is then anyone's");
|
|
891
|
+
assert.equal(await ownerOf(second, acquired, "after a restart"), null);
|
|
892
|
+
await second.register(owned, "kit-bob");
|
|
893
|
+
assert.equal(await ownerOf(second, owned, "a register after a restart"), "kit-alice", "a restart opens no claiming window");
|
|
667
894
|
});
|
|
668
|
-
faceRun("[ownership] a fork
|
|
895
|
+
faceRun("[ownership] a fork records its owner in its own row: no owner or null ⇒ the source's (an owned copy is never downgraded), an explicit owner ⇒ that owner, an anonymous source ⇒ anonymous", ["ownership", "fork"], async (store) => {
|
|
669
896
|
const owned = freshId();
|
|
670
897
|
await store.register(owned, "kit-alice");
|
|
671
898
|
await append((await store.acquire(owned)).session, userEntry("e1", null));
|
|
672
|
-
assert.equal(await
|
|
673
|
-
assert.equal(await
|
|
674
|
-
assert.equal(await
|
|
899
|
+
assert.equal(await ownerOf(store, (await store.fork(owned)), "a fork"), "kit-alice", "no owner argument ⇒ the source's owner");
|
|
900
|
+
assert.equal(await ownerOf(store, (await store.fork(owned, null)), "a fork"), "kit-alice", "an explicit null never downgrades a copy of an owned history to anonymous");
|
|
901
|
+
assert.equal(await ownerOf(store, (await store.fork(owned, "kit-bob")), "a fork"), "kit-bob", "an explicit owner scopes the fork");
|
|
675
902
|
const anonymous = freshId();
|
|
676
|
-
await store.register(anonymous, null);
|
|
677
903
|
await append((await store.acquire(anonymous)).session, userEntry("e1", null));
|
|
678
|
-
assert.equal(await
|
|
679
|
-
assert.equal(await
|
|
904
|
+
assert.equal(await ownerOf(store, (await store.fork(anonymous)), "a fork"), null);
|
|
905
|
+
assert.equal(await ownerOf(store, (await store.fork(anonymous, null)), "a fork"), null);
|
|
906
|
+
});
|
|
907
|
+
faceRun("[ownership] an id registered first is an ordinary row: a placement-carrying acquire of it is refused placement_refused", ["ownership", "placements"], async (store) => {
|
|
908
|
+
const id = freshId();
|
|
909
|
+
await store.register(id, "kit-alice");
|
|
910
|
+
await rejectsWithCode(() => store.acquire(id, { placement: placementFor() }), "placement_refused", "a placed creation onto an id a register already created");
|
|
911
|
+
assert.equal(await ownerOf(store, id, "the registered row"), "kit-alice", "the refused acquire rewrote nothing");
|
|
912
|
+
});
|
|
913
|
+
faceRun("[ownership] acquire serves only the occupant the row holds: after another instance deleted the row and registered the id to another owner, this instance's next acquire serves the new occupant, never its cached view of the old one", ["ownership", "replica"], async (store, open) => {
|
|
914
|
+
const id = freshId();
|
|
915
|
+
const other = await open(hooks.replica(store));
|
|
916
|
+
await store.register(id, "kit-alice");
|
|
917
|
+
await append((await store.acquire(id)).session, userEntry("alice-only", null));
|
|
918
|
+
await other.acquire(id);
|
|
919
|
+
await hooks.deleteSession(other, id);
|
|
920
|
+
await other.register(id, "kit-bob");
|
|
921
|
+
const head = await headOf(store, id);
|
|
922
|
+
assert.equal(head?.owner, "kit-bob", "this instance's head reads the row");
|
|
923
|
+
const served = (await store.acquire(id)).session;
|
|
924
|
+
assert.equal((await served.getMetadata()).incarnation, head?.incarnation, "the session handed out is the row the owner was read from");
|
|
925
|
+
assert.deepEqual(await entryIds(served), [], "the previous occupant's transcript is not served under the new owner");
|
|
926
|
+
});
|
|
927
|
+
faceRun("[ownership] the metadata a session hands out is not the row: writing to it changes no owner or occupant the head answers", ["ownership"], async (store) => {
|
|
928
|
+
const id = freshId();
|
|
929
|
+
await store.register(id, "kit-alice");
|
|
930
|
+
const before = await headOf(store, id);
|
|
931
|
+
try {
|
|
932
|
+
Object.assign(await (await store.acquire(id)).session.getMetadata(), { owner: null, incarnation: "kit-forged" });
|
|
933
|
+
}
|
|
934
|
+
catch {
|
|
935
|
+
}
|
|
936
|
+
assert.deepEqual(await headOf(store, id), before, "admission changes only through a write of the row");
|
|
937
|
+
});
|
|
938
|
+
faceRun("[incarnation] a session created through acquire carries a token, and every later read of the row answers the same one", ["headOf", "requireExisting"], async (store) => {
|
|
939
|
+
const id = freshId();
|
|
940
|
+
await store.acquire(id);
|
|
941
|
+
const first = await tokenOf(store, id, "after acquire");
|
|
942
|
+
await store.acquire(id);
|
|
943
|
+
assert.equal(await tokenOf(store, id, "a re-acquire"), first, "a re-acquire reopens the same occupant");
|
|
944
|
+
await store.forget?.(id);
|
|
945
|
+
assert.equal(await tokenOf(store, id, "after forget"), first, "the token is the row's, not the cache's: forgetting the cached view must not change it");
|
|
946
|
+
await store.acquire(id, { requireExisting: true });
|
|
947
|
+
assert.equal(await tokenOf(store, id, "a require-existing reopen"), first, "a require-existing reopen after the cache forgot it is still the same occupant");
|
|
948
|
+
});
|
|
949
|
+
faceRun("[incarnation] the session acquire hands back carries the token headOf answers — a run's memory seat reads the former", ["headOf", "requireExisting"], async (store) => {
|
|
950
|
+
const id = freshId();
|
|
951
|
+
const acquired = await store.acquire(id);
|
|
952
|
+
const byId = await tokenOf(store, id, "by-id read");
|
|
953
|
+
assert.equal((await acquired.session.getMetadata()).incarnation, byId, "two faces of one row cell must agree, or the seat and the by-id read name two occupants");
|
|
954
|
+
});
|
|
955
|
+
faceRun("[incarnation] a second store over the same rows (a restart, a replica) answers the same token — the token lives in the row, not in an instance", ["headOf", "requireExisting"], async (store, open) => {
|
|
956
|
+
const id = freshId();
|
|
957
|
+
await store.acquire(id);
|
|
958
|
+
const first = await tokenOf(store, id, "first instance");
|
|
959
|
+
const second = await open(hooks.reopen(store));
|
|
960
|
+
assert.equal(await tokenOf(second, id, "second instance"), first, "an instance-local token would name a different occupant after every restart");
|
|
961
|
+
await second.acquire(id, { requireExisting: true });
|
|
962
|
+
assert.equal(await tokenOf(second, id, "reopened on the second instance"), first, "reopening the session on the second instance is still the same occupant");
|
|
963
|
+
});
|
|
964
|
+
faceRun("[incarnation] two sessions never share a token, and an id with no session has no head (unknown — not an error, not a token)", ["headOf", "requireExisting"], async (store) => {
|
|
965
|
+
const id = freshId();
|
|
966
|
+
await store.acquire(id);
|
|
967
|
+
const generated = await store.acquire();
|
|
968
|
+
const a = await tokenOf(store, id, "explicit id");
|
|
969
|
+
const g = await tokenOf(store, generated.sessionId, "generated id");
|
|
970
|
+
assert.notEqual(a, g, "two sessions are two occupants");
|
|
971
|
+
assert.equal(await headOf(store, freshId()), undefined, "no row ⇒ unknown");
|
|
972
|
+
});
|
|
973
|
+
faceRun("[incarnation] a deleted id handed out again is a NEW occupant: a fresh token, never the previous occupant's", ["headOf", "requireExisting"], async (store) => {
|
|
974
|
+
const id = freshId();
|
|
975
|
+
await store.acquire(id);
|
|
976
|
+
const before = await tokenOf(store, id, "first occupant");
|
|
977
|
+
await hooks.deleteSession(store, id);
|
|
978
|
+
assert.equal(await headOf(store, id), undefined, "a deleted row has no occupant");
|
|
979
|
+
await store.acquire(id);
|
|
980
|
+
const after = await tokenOf(store, id, "second occupant");
|
|
981
|
+
assert.notEqual(after, before, "the id was recycled — the previous occupant's records must not bind this one");
|
|
982
|
+
});
|
|
983
|
+
faceRun("[incarnation] a fork is a new occupant: a fresh token, and the source keeps its own", ["headOf", "requireExisting", "fork"], async (store) => {
|
|
984
|
+
const src = freshId();
|
|
985
|
+
await store.acquire(src);
|
|
986
|
+
const source = await tokenOf(store, src, "fork source");
|
|
987
|
+
const forkedId = await store.fork(src);
|
|
988
|
+
assert.ok(forkedId !== null, "forking an existing session returns the fork's id");
|
|
989
|
+
assert.notEqual(await tokenOf(store, forkedId, "fork"), source, "a fork copies the history, never the occupant");
|
|
990
|
+
assert.equal(await tokenOf(store, src, "fork source after"), source, "forking does not touch the source's occupant");
|
|
680
991
|
});
|
|
681
|
-
registerSessionIncarnationCases((name, fn) => faceRun(`[incarnation] ${name}`, ["incarnationOf", "requireExisting"], (store) => fn(store)), hooks);
|
|
682
992
|
await settle();
|
|
683
993
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
|
-
import { JOURNAL_OVERSIZE_ERROR_CODE, MAX_JOURNAL_RESULT_BYTES, } from "../workflow-journal-store.js";
|
|
2
|
+
import { JOURNAL_INCOMPATIBLE_ERROR_CODE, JOURNAL_OVERSIZE_ERROR_CODE, MAX_JOURNAL_RESULT_BYTES, } from "../workflow-journal-store.js";
|
|
3
3
|
import { beginContract } from "./contract-harness.js";
|
|
4
4
|
const key = (ordinal) => `${ordinal}:h`;
|
|
5
5
|
const completed = (ordinal, over = {}) => ({
|
|
@@ -22,6 +22,7 @@ const parked = (ordinal, over = {}) => ({
|
|
|
22
22
|
});
|
|
23
23
|
const OVER_CAP = "x".repeat(MAX_JOURNAL_RESULT_BYTES + 1024);
|
|
24
24
|
const isOversizeRefusal = (e) => e instanceof Error && e.code === JOURNAL_OVERSIZE_ERROR_CODE;
|
|
25
|
+
const isIncompatibleRefusal = (e) => e instanceof Error && e.code === JOURNAL_INCOMPATIBLE_ERROR_CODE;
|
|
25
26
|
export async function workflowJournalStoreContract(make, runAssertion) {
|
|
26
27
|
const { run, settle } = beginContract(runAssertion);
|
|
27
28
|
run("append → load returns entries ASCENDING by ordinal, both arms verbatim; a re-append per ordinal OVERWRITES", async () => {
|
|
@@ -64,6 +65,30 @@ export async function workflowJournalStoreContract(make, runAssertion) {
|
|
|
64
65
|
await store.append("run-1", "tenant-a", { callKey: key(1), parked: parked(1) });
|
|
65
66
|
assert.equal((await store.load("run-1", "tenant-a"))[1].parked?.terminal.kind, "paused");
|
|
66
67
|
});
|
|
68
|
+
run("a PARKED row whose payload is not a paused result is refused — at append, or by the whole load — never read back as an entry, never dropped", async () => {
|
|
69
|
+
const bad = [
|
|
70
|
+
["terminal completed", { ...parked(1), terminal: { kind: "completed" } }],
|
|
71
|
+
["paused terminal without a checkpoint token", { ...parked(1), terminal: { kind: "paused", gate: { kind: "human", reason: "approve", toolName: "Write" } } }],
|
|
72
|
+
["parked arm a string", "paused"],
|
|
73
|
+
];
|
|
74
|
+
for (const [what, payload] of bad) {
|
|
75
|
+
const store = make();
|
|
76
|
+
await store.append("run-1", "tenant-a", { callKey: key(0), result: completed(0) });
|
|
77
|
+
let appended = true;
|
|
78
|
+
try {
|
|
79
|
+
await store.append("run-1", "tenant-a", { callKey: key(1), parked: payload });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
appended = false;
|
|
83
|
+
}
|
|
84
|
+
if (appended) {
|
|
85
|
+
await assert.rejects(store.load("run-1", "tenant-a"), isIncompatibleRefusal, `${what}: the row was journaled, so the load must refuse — with code "${JOURNAL_INCOMPATIBLE_ERROR_CODE}" — rather than answer around it`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
assert.deepEqual((await store.load("run-1", "tenant-a")).map((e) => e.callKey), [key(0)], `${what}: an append that refused must have journaled nothing`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
67
92
|
run("the cap is inclusive: a payload of EXACTLY the cap's byte count is accepted on both arms", async () => {
|
|
68
93
|
const store = make();
|
|
69
94
|
const fit = (payload) => {
|
package/dist/core/stub-env.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare class StubExecutionEnv implements ExecutionEnv {
|
|
|
14
14
|
readonly externalContentTarget?: boolean;
|
|
15
15
|
/** `ExecutionEnv.cdPath`: the stub runs no shell, so no shell receives a `CDPATH`. */
|
|
16
16
|
readonly cdPath: readonly string[];
|
|
17
|
+
/** `ExecutionEnv.withheldEnv`: no shell child, so nothing is withheld from one. */
|
|
18
|
+
readonly withheldEnv: readonly [];
|
|
17
19
|
constructor(cwd?: string);
|
|
18
20
|
private fsErr;
|
|
19
21
|
absolutePath(path: string): Promise<Result<string, FileError>>;
|