@namzu/sdk 49.0.0 → 50.0.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 +507 -0
- package/README.md +137 -125
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +1 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/examples/SupervisorAgent.js +1 -0
- package/dist/agents/examples/SupervisorAgent.js.map +1 -1
- package/dist/agents/runAgent.d.ts +2 -0
- package/dist/agents/runAgent.d.ts.map +1 -1
- package/dist/agents/runAgent.js +1 -0
- package/dist/agents/runAgent.js.map +1 -1
- package/dist/authorization/shell-lexer.d.ts.map +1 -1
- package/dist/authorization/shell-lexer.js +16 -1
- package/dist/authorization/shell-lexer.js.map +1 -1
- package/dist/bridge/acp/permission.d.ts +8 -2
- package/dist/bridge/acp/permission.d.ts.map +1 -1
- package/dist/bridge/acp/permission.js +1 -0
- package/dist/bridge/acp/permission.js.map +1 -1
- package/dist/bridge/acp/server.d.ts +25 -1
- package/dist/bridge/acp/server.d.ts.map +1 -1
- package/dist/bridge/acp/server.js +205 -21
- package/dist/bridge/acp/server.js.map +1 -1
- package/dist/bridge/acp/tasks.d.ts +5 -0
- package/dist/bridge/acp/tasks.d.ts.map +1 -0
- package/dist/bridge/acp/tasks.js +19 -0
- package/dist/bridge/acp/tasks.js.map +1 -0
- package/dist/bridge/acp/update.d.ts.map +1 -1
- package/dist/bridge/acp/update.js +148 -13
- package/dist/bridge/acp/update.js.map +1 -1
- package/dist/config/runtime.d.ts +31 -31
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +11 -4
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +6 -3
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/constants/acp/index.d.ts +3 -0
- package/dist/constants/acp/index.d.ts.map +1 -1
- package/dist/constants/acp/index.js +3 -0
- package/dist/constants/acp/index.js.map +1 -1
- package/dist/contracts/a2a.d.ts +80 -80
- package/dist/contracts/schemas.d.ts +2 -2
- package/dist/contracts/session/schemas.d.ts +10 -10
- package/dist/manager/agent/lifecycle.d.ts +11 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +178 -3
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/plan/lifecycle.d.ts +7 -8
- package/dist/manager/plan/lifecycle.d.ts.map +1 -1
- package/dist/manager/plan/lifecycle.js +7 -8
- package/dist/manager/plan/lifecycle.js.map +1 -1
- package/dist/manager/resident/consumption.d.ts +8 -8
- package/dist/manager/resident/learning-cycle.d.ts +2 -2
- package/dist/manager/resident/learning-observation.d.ts +12 -12
- package/dist/manager/resident/learning.d.ts +82 -82
- package/dist/manager/resident/outbox.d.ts +8 -8
- package/dist/manager/resident/proposal.d.ts +2 -2
- package/dist/manager/resident/store.d.ts +10 -10
- package/dist/manager/session/attribution.d.ts +1 -0
- package/dist/manager/session/attribution.d.ts.map +1 -1
- package/dist/manager/session/attribution.js +9 -1
- package/dist/manager/session/attribution.js.map +1 -1
- package/dist/manager/session/streamed-message-identity.d.ts +6 -0
- package/dist/manager/session/streamed-message-identity.d.ts.map +1 -0
- package/dist/manager/session/streamed-message-identity.js +14 -0
- package/dist/manager/session/streamed-message-identity.js.map +1 -0
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +4 -3
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/pals/activity/causality.d.ts +23 -0
- package/dist/pals/activity/causality.d.ts.map +1 -0
- package/dist/pals/activity/causality.js +205 -0
- package/dist/pals/activity/causality.js.map +1 -0
- package/dist/pals/activity/projection.d.ts +5 -0
- package/dist/pals/activity/projection.d.ts.map +1 -0
- package/dist/pals/activity/projection.js +79 -0
- package/dist/pals/activity/projection.js.map +1 -0
- package/dist/pals/activity/source.d.ts +16 -0
- package/dist/pals/activity/source.d.ts.map +1 -0
- package/dist/pals/activity/source.js +235 -0
- package/dist/pals/activity/source.js.map +1 -0
- package/dist/pals/activity/subscription-policy.d.ts +38 -0
- package/dist/pals/activity/subscription-policy.d.ts.map +1 -0
- package/dist/pals/activity/subscription-policy.js +183 -0
- package/dist/pals/activity/subscription-policy.js.map +1 -0
- package/dist/pals/activity/subscription-schema.d.ts +353 -0
- package/dist/pals/activity/subscription-schema.d.ts.map +1 -0
- package/dist/pals/activity/subscription-schema.js +24 -0
- package/dist/pals/activity/subscription-schema.js.map +1 -0
- package/dist/pals/activity/subscription-store.d.ts +25 -0
- package/dist/pals/activity/subscription-store.d.ts.map +1 -0
- package/dist/pals/activity/subscription-store.js +152 -0
- package/dist/pals/activity/subscription-store.js.map +1 -0
- package/dist/pals/activity/subscription-types.d.ts +42 -0
- package/dist/pals/activity/subscription-types.d.ts.map +1 -0
- package/dist/pals/activity/subscription-types.js +2 -0
- package/dist/pals/activity/subscription-types.js.map +1 -0
- package/dist/pals/activity/subscriptions.d.ts +32 -0
- package/dist/pals/activity/subscriptions.d.ts.map +1 -0
- package/dist/pals/activity/subscriptions.js +151 -0
- package/dist/pals/activity/subscriptions.js.map +1 -0
- package/dist/pals/activity/types.d.ts +77 -0
- package/dist/pals/activity/types.d.ts.map +1 -0
- package/dist/pals/activity/types.js +2 -0
- package/dist/pals/activity/types.js.map +1 -0
- package/dist/pals/channels/ingress.d.ts +9 -0
- package/dist/pals/channels/ingress.d.ts.map +1 -0
- package/dist/pals/channels/ingress.js +131 -0
- package/dist/pals/channels/ingress.js.map +1 -0
- package/dist/pals/channels/routes.d.ts +20 -0
- package/dist/pals/channels/routes.d.ts.map +1 -0
- package/dist/pals/channels/routes.js +111 -0
- package/dist/pals/channels/routes.js.map +1 -0
- package/dist/pals/channels/routing.d.ts +37 -0
- package/dist/pals/channels/routing.d.ts.map +1 -0
- package/dist/pals/channels/routing.js +150 -0
- package/dist/pals/channels/routing.js.map +1 -0
- package/dist/pals/channels/schema.d.ts +234 -0
- package/dist/pals/channels/schema.d.ts.map +1 -0
- package/dist/pals/channels/schema.js +94 -0
- package/dist/pals/channels/schema.js.map +1 -0
- package/dist/pals/channels/types.d.ts +115 -0
- package/dist/pals/channels/types.d.ts.map +1 -0
- package/dist/pals/channels/types.js +2 -0
- package/dist/pals/channels/types.js.map +1 -0
- package/dist/pals/communication/broker.d.ts +8 -0
- package/dist/pals/communication/broker.d.ts.map +1 -0
- package/dist/pals/communication/broker.js +121 -0
- package/dist/pals/communication/broker.js.map +1 -0
- package/dist/pals/communication/dispatch.d.ts +4 -0
- package/dist/pals/communication/dispatch.d.ts.map +1 -0
- package/dist/pals/communication/dispatch.js +57 -0
- package/dist/pals/communication/dispatch.js.map +1 -0
- package/dist/pals/communication/inbound.d.ts +9 -0
- package/dist/pals/communication/inbound.d.ts.map +1 -0
- package/dist/pals/communication/inbound.js +155 -0
- package/dist/pals/communication/inbound.js.map +1 -0
- package/dist/pals/communication/ingress-dispatch.d.ts +5 -0
- package/dist/pals/communication/ingress-dispatch.d.ts.map +1 -0
- package/dist/pals/communication/ingress-dispatch.js +51 -0
- package/dist/pals/communication/ingress-dispatch.js.map +1 -0
- package/dist/pals/communication/ingress-inbound.d.ts +9 -0
- package/dist/pals/communication/ingress-inbound.d.ts.map +1 -0
- package/dist/pals/communication/ingress-inbound.js +222 -0
- package/dist/pals/communication/ingress-inbound.js.map +1 -0
- package/dist/pals/communication/ingress-schema.d.ts +7829 -0
- package/dist/pals/communication/ingress-schema.d.ts.map +1 -0
- package/dist/pals/communication/ingress-schema.js +479 -0
- package/dist/pals/communication/ingress-schema.js.map +1 -0
- package/dist/pals/communication/ingress-types.d.ts +187 -0
- package/dist/pals/communication/ingress-types.d.ts.map +1 -0
- package/dist/pals/communication/ingress-types.js +73 -0
- package/dist/pals/communication/ingress-types.js.map +1 -0
- package/dist/pals/communication/operator.d.ts +75 -0
- package/dist/pals/communication/operator.d.ts.map +1 -0
- package/dist/pals/communication/operator.js +217 -0
- package/dist/pals/communication/operator.js.map +1 -0
- package/dist/pals/communication/policy.d.ts +41 -0
- package/dist/pals/communication/policy.d.ts.map +1 -0
- package/dist/pals/communication/policy.js +138 -0
- package/dist/pals/communication/policy.js.map +1 -0
- package/dist/pals/communication/schema.d.ts +2000 -0
- package/dist/pals/communication/schema.d.ts.map +1 -0
- package/dist/pals/communication/schema.js +170 -0
- package/dist/pals/communication/schema.js.map +1 -0
- package/dist/pals/communication/store.d.ts +61 -0
- package/dist/pals/communication/store.d.ts.map +1 -0
- package/dist/pals/communication/store.js +450 -0
- package/dist/pals/communication/store.js.map +1 -0
- package/dist/pals/communication/tools.d.ts +23 -0
- package/dist/pals/communication/tools.d.ts.map +1 -0
- package/dist/pals/communication/tools.js +107 -0
- package/dist/pals/communication/tools.js.map +1 -0
- package/dist/pals/communication/types.d.ts +168 -0
- package/dist/pals/communication/types.d.ts.map +1 -0
- package/dist/pals/communication/types.js +24 -0
- package/dist/pals/communication/types.js.map +1 -0
- package/dist/pals/communication/verify.d.ts +22 -0
- package/dist/pals/communication/verify.d.ts.map +1 -0
- package/dist/pals/communication/verify.js +142 -0
- package/dist/pals/communication/verify.js.map +1 -0
- package/dist/pals/lifecycle.d.ts +37 -0
- package/dist/pals/lifecycle.d.ts.map +1 -0
- package/dist/pals/lifecycle.js +22 -0
- package/dist/pals/lifecycle.js.map +1 -0
- package/dist/pals/prompt.d.ts +30 -0
- package/dist/pals/prompt.d.ts.map +1 -0
- package/dist/pals/prompt.js +65 -0
- package/dist/pals/prompt.js.map +1 -0
- package/dist/pals/reference-images.d.ts +44 -0
- package/dist/pals/reference-images.d.ts.map +1 -0
- package/dist/pals/reference-images.js +248 -0
- package/dist/pals/reference-images.js.map +1 -0
- package/dist/pals/runtime.d.ts +46 -0
- package/dist/pals/runtime.d.ts.map +1 -0
- package/dist/pals/runtime.js +624 -0
- package/dist/pals/runtime.js.map +1 -0
- package/dist/pals/store.d.ts +26 -0
- package/dist/pals/store.d.ts.map +1 -0
- package/dist/pals/store.js +334 -0
- package/dist/pals/store.js.map +1 -0
- package/dist/pals/types.d.ts +145 -0
- package/dist/pals/types.d.ts.map +1 -0
- package/dist/pals/types.js +2 -0
- package/dist/pals/types.js.map +1 -0
- package/dist/peers/envelope.js +1 -1
- package/dist/peers/envelope.js.map +1 -1
- package/dist/peers/protocol.d.ts +83 -83
- package/dist/peers/record.d.ts +10 -10
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +7 -2
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/process/kill-tree.d.ts.map +1 -1
- package/dist/process/kill-tree.js +9 -1
- package/dist/process/kill-tree.js.map +1 -1
- package/dist/provider/errors.d.ts +19 -0
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +123 -2
- package/dist/provider/errors.js.map +1 -1
- package/dist/public-runtime.d.ts +41 -13
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +35 -7
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +8 -3
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +8 -3
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +27 -7
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/tool/presentation.d.ts +1 -0
- package/dist/registry/tool/presentation.d.ts.map +1 -1
- package/dist/registry/tool/presentation.js +9 -7
- package/dist/registry/tool/presentation.js.map +1 -1
- package/dist/runtime/harness-session/index.d.ts +5 -0
- package/dist/runtime/harness-session/index.d.ts.map +1 -0
- package/dist/runtime/harness-session/index.js +928 -0
- package/dist/runtime/harness-session/index.js.map +1 -0
- package/dist/runtime/harness-session/journal.d.ts +37 -0
- package/dist/runtime/harness-session/journal.d.ts.map +1 -0
- package/dist/runtime/harness-session/journal.js +177 -0
- package/dist/runtime/harness-session/journal.js.map +1 -0
- package/dist/runtime/harness-session/saved-view.d.ts +8 -0
- package/dist/runtime/harness-session/saved-view.d.ts.map +1 -0
- package/dist/runtime/harness-session/saved-view.js +112 -0
- package/dist/runtime/harness-session/saved-view.js.map +1 -0
- package/dist/runtime/jobs/awaited-jobs.d.ts +2 -1
- package/dist/runtime/jobs/awaited-jobs.d.ts.map +1 -1
- package/dist/runtime/jobs/awaited-jobs.js +2 -1
- package/dist/runtime/jobs/awaited-jobs.js.map +1 -1
- package/dist/runtime/jobs/registry.d.ts +56 -1
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +351 -34
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/abandon-turn.d.ts +12 -1
- package/dist/runtime/query/abandon-turn.d.ts.map +1 -1
- package/dist/runtime/query/abandon-turn.js +3 -1
- package/dist/runtime/query/abandon-turn.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts +4 -0
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +10 -0
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/durable-inbound.d.ts +5 -0
- package/dist/runtime/query/durable-inbound.d.ts.map +1 -0
- package/dist/runtime/query/durable-inbound.js +89 -0
- package/dist/runtime/query/durable-inbound.js.map +1 -0
- package/dist/runtime/query/events.d.ts +4 -0
- package/dist/runtime/query/events.d.ts.map +1 -1
- package/dist/runtime/query/events.js +22 -5
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.d.ts +5 -1
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +85 -13
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +41 -18
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +227 -114
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +3 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +21 -4
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +18 -6
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +144 -67
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/outstanding-work.d.ts +9 -5
- package/dist/runtime/query/iteration/outstanding-work.d.ts.map +1 -1
- package/dist/runtime/query/iteration/outstanding-work.js +61 -6
- package/dist/runtime/query/iteration/outstanding-work.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +4 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +99 -16
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/observation-context.d.ts +2 -2
- package/dist/runtime/query/observation-context.d.ts.map +1 -1
- package/dist/runtime/query/observation-context.js +20 -13
- package/dist/runtime/query/observation-context.js.map +1 -1
- package/dist/runtime/query/prepare-turn.js +2 -2
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/runtime/query/prompt-cache.d.ts +1 -0
- package/dist/runtime/query/prompt-cache.d.ts.map +1 -1
- package/dist/runtime/query/prompt-cache.js +2 -0
- package/dist/runtime/query/prompt-cache.js.map +1 -1
- package/dist/runtime/query/prompt.d.ts +2 -0
- package/dist/runtime/query/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js +23 -1
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/runtime/query/repeat-call.d.ts +12 -3
- package/dist/runtime/query/repeat-call.d.ts.map +1 -1
- package/dist/runtime/query/repeat-call.js +33 -10
- package/dist/runtime/query/repeat-call.js.map +1 -1
- package/dist/runtime/query/resume-pending.d.ts +5 -6
- package/dist/runtime/query/resume-pending.d.ts.map +1 -1
- package/dist/runtime/query/resume-pending.js +30 -3
- package/dist/runtime/query/resume-pending.js.map +1 -1
- package/dist/runtime/query/resume-session.js +1 -1
- package/dist/runtime/query/resume-session.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +8 -1
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +6 -0
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/saved-presentation.d.ts +13 -0
- package/dist/runtime/query/saved-presentation.d.ts.map +1 -0
- package/dist/runtime/query/saved-presentation.js +63 -0
- package/dist/runtime/query/saved-presentation.js.map +1 -0
- package/dist/runtime/query/steering.d.ts +6 -9
- package/dist/runtime/query/steering.d.ts.map +1 -1
- package/dist/runtime/query/steering.js.map +1 -1
- package/dist/runtime/query/tool-executions.d.ts +7 -3
- package/dist/runtime/query/tool-executions.d.ts.map +1 -1
- package/dist/runtime/query/tool-executions.js +89 -9
- package/dist/runtime/query/tool-executions.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +3 -1
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +3 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/scheduler/completion-inbox.d.ts +25 -1
- package/dist/scheduler/completion-inbox.d.ts.map +1 -1
- package/dist/scheduler/completion-inbox.js +102 -8
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/scheduler/delegating.d.ts.map +1 -1
- package/dist/scheduler/delegating.js +5 -0
- package/dist/scheduler/delegating.js.map +1 -1
- package/dist/scheduler/local.d.ts.map +1 -1
- package/dist/scheduler/local.js +2 -0
- package/dist/scheduler/local.js.map +1 -1
- package/dist/session/summary/materialize.d.ts +10 -6
- package/dist/session/summary/materialize.d.ts.map +1 -1
- package/dist/session/summary/materialize.js +41 -8
- package/dist/session/summary/materialize.js.map +1 -1
- package/dist/store/budget/ledger.d.ts +9 -0
- package/dist/store/budget/ledger.d.ts.map +1 -1
- package/dist/store/budget/ledger.js +58 -6
- package/dist/store/budget/ledger.js.map +1 -1
- package/dist/store/evidence/index-page.d.ts +12 -12
- package/dist/store/evidence/source-text.d.ts +4 -4
- package/dist/store/session/disk.d.ts +9 -4
- package/dist/store/session/disk.d.ts.map +1 -1
- package/dist/store/session/disk.js +57 -11
- package/dist/store/session/disk.js.map +1 -1
- package/dist/store/session/memory.d.ts +5 -3
- package/dist/store/session/memory.d.ts.map +1 -1
- package/dist/store/session/memory.js +24 -7
- package/dist/store/session/memory.js.map +1 -1
- package/dist/store/session-log/core.d.ts +26 -6
- package/dist/store/session-log/core.d.ts.map +1 -1
- package/dist/store/session-log/core.js +61 -8
- package/dist/store/session-log/core.js.map +1 -1
- package/dist/store/session-log/disk.d.ts +2 -2
- package/dist/store/session-log/disk.d.ts.map +1 -1
- package/dist/store/session-log/disk.js +1 -0
- package/dist/store/session-log/disk.js.map +1 -1
- package/dist/store/session-log/fold.d.ts +7 -0
- package/dist/store/session-log/fold.d.ts.map +1 -1
- package/dist/store/session-log/fold.js +21 -0
- package/dist/store/session-log/fold.js.map +1 -1
- package/dist/store/session-log/index.d.ts +5 -3
- package/dist/store/session-log/index.d.ts.map +1 -1
- package/dist/store/session-log/index.js +3 -1
- package/dist/store/session-log/index.js.map +1 -1
- package/dist/store/session-log/memory.d.ts +1 -1
- package/dist/store/session-log/memory.d.ts.map +1 -1
- package/dist/store/session-log/memory.js +1 -0
- package/dist/store/session-log/memory.js.map +1 -1
- package/dist/store/session-log/spill.d.ts +10 -3
- package/dist/store/session-log/spill.d.ts.map +1 -1
- package/dist/store/session-log/spill.js +70 -5
- package/dist/store/session-log/spill.js.map +1 -1
- package/dist/store/session-log/structured-result.d.ts +21 -0
- package/dist/store/session-log/structured-result.d.ts.map +1 -0
- package/dist/store/session-log/structured-result.js +67 -0
- package/dist/store/session-log/structured-result.js.map +1 -0
- package/dist/store/task/disk.d.ts +11 -0
- package/dist/store/task/disk.d.ts.map +1 -1
- package/dist/store/task/disk.js +41 -2
- package/dist/store/task/disk.js.map +1 -1
- package/dist/store/task/memory.d.ts.map +1 -1
- package/dist/store/task/memory.js +15 -3
- package/dist/store/task/memory.js.map +1 -1
- package/dist/tools/admit-facts.d.ts +13 -0
- package/dist/tools/admit-facts.d.ts.map +1 -0
- package/dist/tools/admit-facts.js +142 -0
- package/dist/tools/admit-facts.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts +4 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +108 -22
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/browser.d.ts +10 -10
- package/dist/tools/builtins/computer-use.d.ts +90 -90
- package/dist/tools/builtins/edit.d.ts +17 -0
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +34 -0
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/file-write-result.d.ts.map +1 -1
- package/dist/tools/builtins/file-write-result.js +13 -0
- package/dist/tools/builtins/file-write-result.js.map +1 -1
- package/dist/tools/builtins/glob.d.ts.map +1 -1
- package/dist/tools/builtins/glob.js +12 -0
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +14 -0
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/index.d.ts +2 -0
- package/dist/tools/builtins/index.d.ts.map +1 -1
- package/dist/tools/builtins/index.js +1 -0
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/job.d.ts.map +1 -1
- package/dist/tools/builtins/job.js +4 -1
- package/dist/tools/builtins/job.js.map +1 -1
- package/dist/tools/builtins/lsp.d.ts +2 -2
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +49 -0
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/view-image.d.ts +24 -0
- package/dist/tools/builtins/view-image.d.ts.map +1 -0
- package/dist/tools/builtins/view-image.js +385 -0
- package/dist/tools/builtins/view-image.js.map +1 -0
- package/dist/tools/builtins/wait-for-job-bounds.d.ts +2 -0
- package/dist/tools/builtins/wait-for-job-bounds.d.ts.map +1 -1
- package/dist/tools/builtins/wait-for-job-bounds.js +1 -0
- package/dist/tools/builtins/wait-for-job-bounds.js.map +1 -1
- package/dist/tools/builtins/wait-for-job.d.ts +2 -0
- package/dist/tools/builtins/wait-for-job.d.ts.map +1 -1
- package/dist/tools/builtins/wait-for-job.js +104 -6
- package/dist/tools/builtins/wait-for-job.js.map +1 -1
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +1 -0
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/command-shell.d.ts +37 -2
- package/dist/tools/command-shell.d.ts.map +1 -1
- package/dist/tools/command-shell.js +100 -3
- package/dist/tools/command-shell.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts +9 -19
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +127 -67
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/coordinator/wait-with-idle-bound.d.ts +2 -0
- package/dist/tools/coordinator/wait-with-idle-bound.d.ts.map +1 -1
- package/dist/tools/coordinator/wait-with-idle-bound.js +1 -1
- package/dist/tools/coordinator/wait-with-idle-bound.js.map +1 -1
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +4 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/failure-facts.d.ts +8 -0
- package/dist/tools/failure-facts.d.ts.map +1 -0
- package/dist/tools/failure-facts.js +24 -0
- package/dist/tools/failure-facts.js.map +1 -0
- package/dist/tools/host-output.d.ts +24 -0
- package/dist/tools/host-output.d.ts.map +1 -0
- package/dist/tools/host-output.js +117 -0
- package/dist/tools/host-output.js.map +1 -0
- package/dist/tools/oem-code-pages.d.ts +8 -0
- package/dist/tools/oem-code-pages.d.ts.map +1 -0
- package/dist/tools/oem-code-pages.js +23 -0
- package/dist/tools/oem-code-pages.js.map +1 -0
- package/dist/tools/schedules/schedule-tool.d.ts.map +1 -1
- package/dist/tools/schedules/schedule-tool.js +35 -6
- package/dist/tools/schedules/schedule-tool.js.map +1 -1
- package/dist/tools/schedules/types.d.ts +23 -3
- package/dist/tools/schedules/types.d.ts.map +1 -1
- package/dist/tools/task/list.d.ts.map +1 -1
- package/dist/tools/task/list.js +14 -6
- package/dist/tools/task/list.js.map +1 -1
- package/dist/tools/task/present.d.ts.map +1 -1
- package/dist/tools/task/present.js +26 -6
- package/dist/tools/task/present.js.map +1 -1
- package/dist/tools/task/update.d.ts.map +1 -1
- package/dist/tools/task/update.js +12 -5
- package/dist/tools/task/update.js.map +1 -1
- package/dist/tools/text-window.d.ts +19 -0
- package/dist/tools/text-window.d.ts.map +1 -0
- package/dist/tools/text-window.js +83 -0
- package/dist/tools/text-window.js.map +1 -0
- package/dist/toolsets/manager.d.ts +11 -0
- package/dist/toolsets/manager.d.ts.map +1 -1
- package/dist/toolsets/manager.js +45 -1
- package/dist/toolsets/manager.js.map +1 -1
- package/dist/types/acp/index.d.ts +137 -1
- package/dist/types/acp/index.d.ts.map +1 -1
- package/dist/types/agent/base.d.ts +3 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/manager.d.ts +8 -23
- package/dist/types/agent/manager.d.ts.map +1 -1
- package/dist/types/agent/scheduler.d.ts +10 -1
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +4 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/authorization/index.d.ts +30 -30
- package/dist/types/connector/mcp.d.ts +7 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/errors/catalog.d.ts.map +1 -1
- package/dist/types/errors/catalog.js +4 -2
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/harness/schema.d.ts +354 -0
- package/dist/types/harness/schema.d.ts.map +1 -0
- package/dist/types/harness/schema.js +203 -0
- package/dist/types/harness/schema.js.map +1 -0
- package/dist/types/harness/session.d.ts +278 -0
- package/dist/types/harness/session.d.ts.map +1 -0
- package/dist/types/harness/session.js +2 -0
- package/dist/types/harness/session.js.map +1 -0
- package/dist/types/hitl/index.d.ts +12 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/job/index.d.ts +47 -0
- package/dist/types/job/index.d.ts.map +1 -0
- package/dist/types/job/index.js +2 -0
- package/dist/types/job/index.js.map +1 -0
- package/dist/types/message/inbound-delivery.d.ts +44 -0
- package/dist/types/message/inbound-delivery.d.ts.map +1 -0
- package/dist/types/message/inbound-delivery.js +14 -0
- package/dist/types/message/inbound-delivery.js.map +1 -0
- package/dist/types/message/index.d.ts +4 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +5 -1
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +6 -6
- package/dist/types/sandbox/index.d.ts +39 -2
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/session/checkpoint.d.ts +24 -17
- package/dist/types/session/checkpoint.d.ts.map +1 -1
- package/dist/types/session/checkpoint.js +1 -0
- package/dist/types/session/checkpoint.js.map +1 -1
- package/dist/types/session/events.d.ts +23 -1
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +1514 -565
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +89 -1
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/step.d.ts +2 -0
- package/dist/types/session/step.d.ts.map +1 -1
- package/dist/types/session/step.js.map +1 -1
- package/dist/types/session/store.d.ts +19 -10
- package/dist/types/session/store.d.ts.map +1 -1
- package/dist/types/session/tool-execution.d.ts +6 -0
- package/dist/types/session/tool-execution.d.ts.map +1 -1
- package/dist/types/structured-output/index.d.ts +12 -2
- package/dist/types/structured-output/index.d.ts.map +1 -1
- package/dist/types/summary/ref.d.ts +3 -1
- package/dist/types/summary/ref.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +32 -4
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +106 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/dist/utils/json-snapshot.d.ts +6 -2
- package/dist/utils/json-snapshot.d.ts.map +1 -1
- package/dist/utils/json-snapshot.js +19 -9
- package/dist/utils/json-snapshot.js.map +1 -1
- package/dist/utils/structured-result-json.d.ts +5 -0
- package/dist/utils/structured-result-json.d.ts.map +1 -0
- package/dist/utils/structured-result-json.js +10 -0
- package/dist/utils/structured-result-json.js.map +1 -0
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +1 -0
- package/src/agents/examples/SupervisorAgent.ts +1 -0
- package/src/agents/runAgent.ts +3 -0
- package/src/authorization/shell-lexer.ts +16 -1
- package/src/bridge/acp/permission.ts +17 -3
- package/src/bridge/acp/server.ts +271 -25
- package/src/bridge/acp/tasks.ts +23 -0
- package/src/bridge/acp/update.ts +167 -13
- package/src/connector/mcp/adapter.ts +16 -4
- package/src/connector/mcp/client.ts +6 -2
- package/src/constants/acp/index.ts +4 -0
- package/src/manager/agent/lifecycle.ts +230 -3
- package/src/manager/plan/lifecycle.ts +11 -9
- package/src/manager/session/attribution.ts +12 -2
- package/src/manager/session/streamed-message-identity.ts +21 -0
- package/src/manager/session/turn-recorder.ts +4 -3
- package/src/pals/activity/causality.ts +261 -0
- package/src/pals/activity/projection.ts +89 -0
- package/src/pals/activity/source.ts +240 -0
- package/src/pals/activity/subscription-policy.ts +230 -0
- package/src/pals/activity/subscription-schema.ts +27 -0
- package/src/pals/activity/subscription-store.ts +172 -0
- package/src/pals/activity/subscription-types.ts +48 -0
- package/src/pals/activity/subscriptions.ts +209 -0
- package/src/pals/activity/types.ts +108 -0
- package/src/pals/channels/ingress.ts +153 -0
- package/src/pals/channels/routes.ts +118 -0
- package/src/pals/channels/routing.ts +197 -0
- package/src/pals/channels/schema.ts +108 -0
- package/src/pals/channels/types.ts +156 -0
- package/src/pals/communication/broker.ts +131 -0
- package/src/pals/communication/dispatch.ts +66 -0
- package/src/pals/communication/inbound.ts +193 -0
- package/src/pals/communication/ingress-dispatch.ts +63 -0
- package/src/pals/communication/ingress-inbound.ts +269 -0
- package/src/pals/communication/ingress-schema.ts +526 -0
- package/src/pals/communication/ingress-types.ts +336 -0
- package/src/pals/communication/operator.ts +315 -0
- package/src/pals/communication/policy.ts +174 -0
- package/src/pals/communication/schema.ts +173 -0
- package/src/pals/communication/store.ts +577 -0
- package/src/pals/communication/tools.ts +139 -0
- package/src/pals/communication/types.ts +227 -0
- package/src/pals/communication/verify.ts +220 -0
- package/src/pals/lifecycle.ts +54 -0
- package/src/pals/prompt.ts +101 -0
- package/src/pals/reference-images.ts +322 -0
- package/src/pals/runtime.ts +670 -0
- package/src/pals/store.ts +369 -0
- package/src/pals/types.ts +157 -0
- package/src/peers/envelope.ts +1 -1
- package/src/plugin/shell-hook.ts +8 -2
- package/src/process/kill-tree.ts +10 -1
- package/src/provider/errors.ts +135 -2
- package/src/public-runtime.ts +117 -12
- package/src/public-tools.ts +13 -1
- package/src/public-types.ts +69 -7
- package/src/registry/tool/presentation.ts +9 -4
- package/src/runtime/harness-session/index.ts +1125 -0
- package/src/runtime/harness-session/journal.ts +209 -0
- package/src/runtime/harness-session/saved-view.ts +122 -0
- package/src/runtime/jobs/awaited-jobs.ts +2 -1
- package/src/runtime/jobs/registry.ts +397 -41
- package/src/runtime/query/abandon-turn.ts +18 -2
- package/src/runtime/query/checkpoint.ts +11 -0
- package/src/runtime/query/durable-inbound.ts +101 -0
- package/src/runtime/query/events.ts +22 -5
- package/src/runtime/query/executor/tool-call-admission.ts +125 -32
- package/src/runtime/query/executor.ts +316 -130
- package/src/runtime/query/index.ts +24 -4
- package/src/runtime/query/iteration/index.ts +218 -104
- package/src/runtime/query/iteration/outstanding-work.ts +55 -7
- package/src/runtime/query/iteration/phases/context.ts +4 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +118 -21
- package/src/runtime/query/observation-context.ts +22 -16
- package/src/runtime/query/prepare-turn.ts +2 -2
- package/src/runtime/query/prompt-cache.ts +3 -0
- package/src/runtime/query/prompt.ts +32 -1
- package/src/runtime/query/repeat-call.ts +40 -10
- package/src/runtime/query/resume-pending.ts +60 -7
- package/src/runtime/query/resume-session.ts +1 -1
- package/src/runtime/query/review-policy.ts +23 -2
- package/src/runtime/query/saved-presentation.ts +70 -0
- package/src/runtime/query/steering.ts +6 -9
- package/src/runtime/query/tool-executions.ts +108 -9
- package/src/runtime/query/tooling.ts +6 -1
- package/src/scheduler/completion-inbox.ts +108 -7
- package/src/scheduler/delegating.ts +5 -0
- package/src/scheduler/local.ts +2 -0
- package/src/session/summary/materialize.ts +51 -9
- package/src/store/budget/ledger.ts +60 -5
- package/src/store/session/disk.ts +77 -10
- package/src/store/session/memory.ts +32 -7
- package/src/store/session-log/core.ts +118 -11
- package/src/store/session-log/disk.ts +9 -2
- package/src/store/session-log/fold.ts +22 -0
- package/src/store/session-log/index.ts +5 -0
- package/src/store/session-log/memory.ts +2 -1
- package/src/store/session-log/spill.ts +89 -6
- package/src/store/session-log/structured-result.ts +96 -0
- package/src/store/task/disk.ts +46 -2
- package/src/store/task/memory.ts +13 -3
- package/src/tools/admit-facts.ts +145 -0
- package/src/tools/builtins/bash.ts +132 -24
- package/src/tools/builtins/edit.ts +42 -0
- package/src/tools/builtins/file-write-result.ts +14 -0
- package/src/tools/builtins/glob.ts +12 -0
- package/src/tools/builtins/grep.ts +14 -0
- package/src/tools/builtins/index.ts +2 -0
- package/src/tools/builtins/job.ts +4 -1
- package/src/tools/builtins/read-file.ts +57 -0
- package/src/tools/builtins/view-image.ts +409 -0
- package/src/tools/builtins/wait-for-job-bounds.ts +3 -0
- package/src/tools/builtins/wait-for-job.ts +141 -32
- package/src/tools/builtins/write-file.ts +4 -2
- package/src/tools/command-shell.ts +132 -5
- package/src/tools/coordinator/agent.ts +9 -19
- package/src/tools/coordinator/index.ts +150 -75
- package/src/tools/coordinator/wait-with-idle-bound.ts +3 -1
- package/src/tools/defineTool.ts +4 -0
- package/src/tools/failure-facts.ts +24 -0
- package/src/tools/host-output.ts +136 -0
- package/src/tools/oem-code-pages.ts +22 -0
- package/src/tools/schedules/schedule-tool.ts +38 -5
- package/src/tools/schedules/types.ts +23 -3
- package/src/tools/task/list.ts +15 -7
- package/src/tools/task/present.ts +37 -7
- package/src/tools/task/update.ts +12 -5
- package/src/tools/text-window.ts +87 -0
- package/src/toolsets/manager.ts +58 -1
- package/src/types/acp/index.ts +150 -4
- package/src/types/agent/base.ts +3 -0
- package/src/types/agent/manager.ts +8 -23
- package/src/types/agent/scheduler.ts +10 -1
- package/src/types/agent/task.ts +4 -0
- package/src/types/connector/mcp.ts +7 -0
- package/src/types/errors/catalog.ts +4 -2
- package/src/types/harness/schema.ts +203 -0
- package/src/types/harness/session.ts +306 -0
- package/src/types/hitl/index.ts +17 -1
- package/src/types/job/index.ts +42 -0
- package/src/types/message/inbound-delivery.ts +63 -0
- package/src/types/message/index.ts +8 -1
- package/src/types/sandbox/index.ts +46 -0
- package/src/types/session/checkpoint.ts +4 -1
- package/src/types/session/events.ts +23 -1
- package/src/types/session/records.ts +114 -2
- package/src/types/session/step.ts +2 -0
- package/src/types/session/store.ts +23 -9
- package/src/types/session/tool-execution.ts +6 -0
- package/src/types/structured-output/index.ts +13 -2
- package/src/types/summary/ref.ts +3 -1
- package/src/types/tool/index.ts +39 -5
- package/src/types/tool/presentation.ts +121 -0
- package/src/utils/json-snapshot.ts +26 -9
- package/src/utils/structured-result-json.ts +10 -0
|
@@ -0,0 +1,1125 @@
|
|
|
1
|
+
import { posix, win32 } from 'node:path'
|
|
2
|
+
import {
|
|
3
|
+
HarnessBindingSchema,
|
|
4
|
+
HarnessDecisionSchema,
|
|
5
|
+
HarnessEventSchema,
|
|
6
|
+
HarnessNativeTurnSchema,
|
|
7
|
+
HarnessReviewRequestSchema,
|
|
8
|
+
harnessSnapshot,
|
|
9
|
+
} from '../../types/harness/schema.js'
|
|
10
|
+
import type {
|
|
11
|
+
HarnessAdmissionRequest,
|
|
12
|
+
HarnessBinding,
|
|
13
|
+
HarnessConnection,
|
|
14
|
+
HarnessDecision,
|
|
15
|
+
HarnessEvent,
|
|
16
|
+
HarnessHistorySnapshot,
|
|
17
|
+
HarnessJournalTransition,
|
|
18
|
+
HarnessNativeItem,
|
|
19
|
+
HarnessNativeTurn,
|
|
20
|
+
HarnessPrompt,
|
|
21
|
+
HarnessReviewRequest,
|
|
22
|
+
HarnessSession,
|
|
23
|
+
HarnessSessionOptions,
|
|
24
|
+
HarnessTurnOutcome,
|
|
25
|
+
} from '../../types/harness/session.js'
|
|
26
|
+
import type { MessageId, TurnId } from '../../types/ids/index.js'
|
|
27
|
+
import type { AssistantMessage, Message } from '../../types/message/index.js'
|
|
28
|
+
import type { SessionEvent } from '../../types/session/events.js'
|
|
29
|
+
import type { TurnSettlement } from '../../types/session/turn.js'
|
|
30
|
+
import {
|
|
31
|
+
generateMessageId,
|
|
32
|
+
generateToolCallId,
|
|
33
|
+
generateTurnId,
|
|
34
|
+
isEntityId,
|
|
35
|
+
} from '../../utils/id.js'
|
|
36
|
+
import { HarnessJournal, HarnessSessionError, harnessDigest } from './journal.js'
|
|
37
|
+
import { engineSavedView } from './saved-view.js'
|
|
38
|
+
|
|
39
|
+
type Payload<E> = E extends unknown ? Omit<E, 'sessionId' | 'turnId'> : never
|
|
40
|
+
type EventPayload = Payload<SessionEvent>
|
|
41
|
+
|
|
42
|
+
interface Item {
|
|
43
|
+
messageId?: MessageId
|
|
44
|
+
toolUseId?: string
|
|
45
|
+
started: boolean
|
|
46
|
+
completed?: string
|
|
47
|
+
content: string
|
|
48
|
+
blocks: Map<string, number>
|
|
49
|
+
/** What the call was started with, for the label a saved conversation keeps. */
|
|
50
|
+
input?: unknown
|
|
51
|
+
}
|
|
52
|
+
interface Active {
|
|
53
|
+
turnId: TurnId
|
|
54
|
+
operationId: string
|
|
55
|
+
startedAt: number
|
|
56
|
+
nativeTurn?: HarnessNativeTurn
|
|
57
|
+
items: Map<string, Item>
|
|
58
|
+
reviews: Map<
|
|
59
|
+
string,
|
|
60
|
+
{
|
|
61
|
+
request: HarnessReviewRequest
|
|
62
|
+
digest: string
|
|
63
|
+
decision?: string
|
|
64
|
+
resolved: boolean
|
|
65
|
+
presented?: boolean
|
|
66
|
+
}
|
|
67
|
+
>
|
|
68
|
+
resolve: (outcome: HarnessTurnOutcome) => void
|
|
69
|
+
reject: (error: unknown) => void
|
|
70
|
+
done: Promise<HarnessTurnOutcome>
|
|
71
|
+
abortRequested?: boolean
|
|
72
|
+
removeAbort?: () => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Vendor execution remains outside the Namzu query kernel and tool executor. */
|
|
76
|
+
export function createHarnessSession(options: HarnessSessionOptions): HarnessSession {
|
|
77
|
+
return new ExternalHarnessSession(options)
|
|
78
|
+
}
|
|
79
|
+
export { HarnessSessionError } from './journal.js'
|
|
80
|
+
|
|
81
|
+
class ExternalHarnessSession implements HarnessSession {
|
|
82
|
+
readonly scope: HarnessSession['scope']
|
|
83
|
+
private readonly journal: HarnessJournal
|
|
84
|
+
private readonly openAdapter: HarnessSessionOptions['adapter']['open']
|
|
85
|
+
private readonly admit: HarnessSessionOptions['assertAdmission']
|
|
86
|
+
private readonly publish: HarnessSessionOptions['onEvent']
|
|
87
|
+
private readonly review: HarnessSessionOptions['onReview']
|
|
88
|
+
private currentBinding?: HarnessBinding
|
|
89
|
+
private connection?: HarnessConnection
|
|
90
|
+
private active?: Active
|
|
91
|
+
private phase: HarnessSession['status'] = 'disconnected'
|
|
92
|
+
private operation = false
|
|
93
|
+
private cancelling = false
|
|
94
|
+
private epoch = 0
|
|
95
|
+
private events: Promise<void> = Promise.resolve()
|
|
96
|
+
private eventError?: unknown
|
|
97
|
+
|
|
98
|
+
constructor(options: HarnessSessionOptions) {
|
|
99
|
+
this.scope = harnessSnapshot(options.scope)
|
|
100
|
+
if (
|
|
101
|
+
!isEntityId(this.scope.sessionId, 'session') ||
|
|
102
|
+
!isEntityId(this.scope.projectId, 'project') ||
|
|
103
|
+
!isEntityId(this.scope.tenantId, 'tenant') ||
|
|
104
|
+
!isEntityId(this.scope.topicId, 'topic') ||
|
|
105
|
+
!(posix.isAbsolute(this.scope.cwd) || win32.isAbsolute(this.scope.cwd)) ||
|
|
106
|
+
Array.from(this.scope.cwd).some((c) => c.charCodeAt(0) < 32)
|
|
107
|
+
)
|
|
108
|
+
throw new HarnessSessionError(
|
|
109
|
+
'invalid-scope',
|
|
110
|
+
'An exact owner scope and canonical absolute native directory are required.',
|
|
111
|
+
)
|
|
112
|
+
if (
|
|
113
|
+
typeof options.assertAdmission !== 'function' ||
|
|
114
|
+
typeof options.onEvent !== 'function' ||
|
|
115
|
+
typeof options.onReview !== 'function'
|
|
116
|
+
)
|
|
117
|
+
throw new HarnessSessionError(
|
|
118
|
+
'invalid-host',
|
|
119
|
+
'Explicit admission, event and review host ports are required.',
|
|
120
|
+
)
|
|
121
|
+
this.journal = new HarnessJournal(
|
|
122
|
+
options.sessionLog,
|
|
123
|
+
this.scope,
|
|
124
|
+
options.adapter.engineId,
|
|
125
|
+
options.adapter.profileRef,
|
|
126
|
+
)
|
|
127
|
+
this.openAdapter = options.adapter.open.bind(options.adapter)
|
|
128
|
+
this.admit = options.assertAdmission
|
|
129
|
+
this.publish = options.onEvent
|
|
130
|
+
this.review = options.onReview
|
|
131
|
+
}
|
|
132
|
+
get binding() {
|
|
133
|
+
return this.currentBinding
|
|
134
|
+
}
|
|
135
|
+
get currentTurnId() {
|
|
136
|
+
return this.active?.turnId
|
|
137
|
+
}
|
|
138
|
+
get status() {
|
|
139
|
+
return this.phase
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private hasUnresolved(): boolean {
|
|
143
|
+
return (
|
|
144
|
+
!!this.active || this.phase === 'reconciliation-required' || this.eventError !== undefined
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
private reserve(): () => void {
|
|
148
|
+
if (this.phase === 'closed')
|
|
149
|
+
throw new HarnessSessionError('closed', 'This harness session is closed.')
|
|
150
|
+
if (this.operation || this.cancelling)
|
|
151
|
+
throw new HarnessSessionError('busy', 'Another harness operation is pending.')
|
|
152
|
+
this.operation = true
|
|
153
|
+
let released = false
|
|
154
|
+
return () => {
|
|
155
|
+
if (released) return
|
|
156
|
+
released = true
|
|
157
|
+
this.operation = false
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
private async authorize(
|
|
161
|
+
kind: HarnessAdmissionRequest['kind'],
|
|
162
|
+
signal?: AbortSignal,
|
|
163
|
+
request?: HarnessReviewRequest,
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
if (signal?.aborted)
|
|
166
|
+
throw new HarnessSessionError('aborted', 'The operation was aborted before dispatch.')
|
|
167
|
+
await this.admit(
|
|
168
|
+
harnessSnapshot({
|
|
169
|
+
kind,
|
|
170
|
+
scope: this.scope,
|
|
171
|
+
...(this.currentBinding ? { binding: this.currentBinding } : {}),
|
|
172
|
+
...(this.active?.nativeTurn ? { nativeTurn: this.active.nativeTurn } : {}),
|
|
173
|
+
...(request ? { review: request } : {}),
|
|
174
|
+
}),
|
|
175
|
+
signal,
|
|
176
|
+
)
|
|
177
|
+
if (signal?.aborted)
|
|
178
|
+
throw new HarnessSessionError('aborted', 'The operation was aborted before dispatch.')
|
|
179
|
+
}
|
|
180
|
+
async history(): Promise<readonly Message[]> {
|
|
181
|
+
const state = await this.journal.inspect()
|
|
182
|
+
this.currentBinding ??= state.binding
|
|
183
|
+
return harnessSnapshot(await this.journal.log.messages())
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private requireConnection(): HarnessConnection {
|
|
187
|
+
if (!this.connection)
|
|
188
|
+
throw new HarnessSessionError('disconnected', 'The native connection is not ready.')
|
|
189
|
+
return this.connection
|
|
190
|
+
}
|
|
191
|
+
private capture(connection: HarnessConnection): HarnessConnection {
|
|
192
|
+
const binding = harnessSnapshot(HarnessBindingSchema.parse(connection.binding))
|
|
193
|
+
this.journal.validateBinding(binding)
|
|
194
|
+
if (this.currentBinding && harnessDigest(this.currentBinding) !== harnessDigest(binding))
|
|
195
|
+
throw new HarnessSessionError(
|
|
196
|
+
'binding-mismatch',
|
|
197
|
+
'The adapter resumed a different native session.',
|
|
198
|
+
)
|
|
199
|
+
return Object.freeze({
|
|
200
|
+
binding,
|
|
201
|
+
capabilities: harnessSnapshot(connection.capabilities),
|
|
202
|
+
models: connection.models.bind(connection),
|
|
203
|
+
dispatch: connection.dispatch.bind(connection),
|
|
204
|
+
interrupt: connection.interrupt.bind(connection),
|
|
205
|
+
respond: connection.respond.bind(connection),
|
|
206
|
+
readHistory: connection.readHistory.bind(connection),
|
|
207
|
+
close: connection.close.bind(connection),
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
private async connect(model?: string, signal?: AbortSignal): Promise<void> {
|
|
211
|
+
if (this.connection) return
|
|
212
|
+
const stored = await this.journal.inspect()
|
|
213
|
+
this.currentBinding = stored.binding
|
|
214
|
+
await this.authorize(stored.binding ? 'reconnect' : 'open', signal)
|
|
215
|
+
await this.journal.claim()
|
|
216
|
+
await this.authorize(stored.binding ? 'reconnect' : 'open', signal)
|
|
217
|
+
const epoch = ++this.epoch
|
|
218
|
+
let accepting = false
|
|
219
|
+
const early: HarnessEvent[] = []
|
|
220
|
+
let earlyBytes = 0
|
|
221
|
+
const sink = (input: HarnessEvent): Promise<void> => {
|
|
222
|
+
if (epoch !== this.epoch || this.phase === 'closed') return Promise.resolve()
|
|
223
|
+
let event: HarnessEvent
|
|
224
|
+
try {
|
|
225
|
+
event = harnessSnapshot(HarnessEventSchema.parse(input))
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return this.enqueueTask(() => Promise.reject(error), epoch)
|
|
228
|
+
}
|
|
229
|
+
if (!accepting) {
|
|
230
|
+
earlyBytes += JSON.stringify(event).length
|
|
231
|
+
if (early.length >= 256 || earlyBytes > 1024 * 1024)
|
|
232
|
+
return this.enqueueTask(
|
|
233
|
+
() =>
|
|
234
|
+
Promise.reject(
|
|
235
|
+
new HarnessSessionError(
|
|
236
|
+
'early-events-overflow',
|
|
237
|
+
'The adapter exceeded the bounded pre-binding event queue.',
|
|
238
|
+
),
|
|
239
|
+
),
|
|
240
|
+
epoch,
|
|
241
|
+
)
|
|
242
|
+
early.push(event)
|
|
243
|
+
return Promise.resolve()
|
|
244
|
+
}
|
|
245
|
+
return this.enqueue(event, epoch)
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const opened = await this.openAdapter(
|
|
249
|
+
{
|
|
250
|
+
cwd: this.scope.cwd,
|
|
251
|
+
...(model ? { model } : {}),
|
|
252
|
+
...(stored.binding ? { resume: stored.binding } : {}),
|
|
253
|
+
...(signal ? { signal } : {}),
|
|
254
|
+
},
|
|
255
|
+
sink,
|
|
256
|
+
)
|
|
257
|
+
// Retain even an invalid returned handle until owned cleanup is confirmed.
|
|
258
|
+
this.connection = opened
|
|
259
|
+
this.connection = this.capture(opened)
|
|
260
|
+
this.currentBinding = this.connection.binding
|
|
261
|
+
await this.authorize(stored.binding ? 'reconnect' : 'open', signal)
|
|
262
|
+
await this.journal.start(this.currentBinding)
|
|
263
|
+
await this.restoreActive(stored.transitions)
|
|
264
|
+
accepting = true
|
|
265
|
+
for (const event of early) await this.enqueue(event, epoch)
|
|
266
|
+
if (stored.binding && !this.active && this.connection.capabilities.history === 'snapshot') {
|
|
267
|
+
const snapshot = harnessSnapshot(await this.connection.readHistory(signal))
|
|
268
|
+
if (
|
|
269
|
+
!snapshot.complete ||
|
|
270
|
+
harnessDigest(snapshot.binding) !== harnessDigest(this.currentBinding) ||
|
|
271
|
+
snapshot.activeTurn ||
|
|
272
|
+
snapshot.pendingReviews.length
|
|
273
|
+
)
|
|
274
|
+
throw new HarnessSessionError(
|
|
275
|
+
'reconciliation-required',
|
|
276
|
+
'Native session history did not confirm an idle exact binding.',
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
if (!this.active && this.phase !== 'reconciliation-required') this.phase = 'idle'
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (this.connection) {
|
|
282
|
+
try {
|
|
283
|
+
await this.connection.close()
|
|
284
|
+
this.connection = undefined
|
|
285
|
+
++this.epoch
|
|
286
|
+
await this.journal.release()
|
|
287
|
+
} catch (cleanup) {
|
|
288
|
+
this.phase = 'reconciliation-required'
|
|
289
|
+
throw new AggregateError(
|
|
290
|
+
[error, cleanup],
|
|
291
|
+
'Harness open failed and its process could not be confirmed stopped.',
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
} else await this.journal.release()
|
|
295
|
+
throw error
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
private makeActive(turnId: TurnId, operationId: string, startedAt: number): Active {
|
|
299
|
+
let resolve!: Active['resolve']
|
|
300
|
+
let reject!: Active['reject']
|
|
301
|
+
const done = new Promise<HarnessTurnOutcome>((yes, no) => {
|
|
302
|
+
resolve = yes
|
|
303
|
+
reject = no
|
|
304
|
+
})
|
|
305
|
+
void done.catch(() => undefined)
|
|
306
|
+
return {
|
|
307
|
+
turnId,
|
|
308
|
+
operationId,
|
|
309
|
+
startedAt,
|
|
310
|
+
items: new Map(),
|
|
311
|
+
reviews: new Map(),
|
|
312
|
+
resolve,
|
|
313
|
+
reject,
|
|
314
|
+
done,
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
private async restoreActive(transitions: readonly HarnessJournalTransition[]): Promise<void> {
|
|
318
|
+
const pending = await this.journal.log.activeTurn({ lease: await this.journal.current() })
|
|
319
|
+
if (!pending) {
|
|
320
|
+
this.active?.removeAbort?.()
|
|
321
|
+
this.active = undefined
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
const prepared = [...transitions]
|
|
325
|
+
.reverse()
|
|
326
|
+
.find((e) => e.kind === 'dispatch-prepared' && e.turnId === pending.turnId)
|
|
327
|
+
if (!prepared || prepared.kind !== 'dispatch-prepared')
|
|
328
|
+
throw new HarnessSessionError(
|
|
329
|
+
'invalid-journal',
|
|
330
|
+
'The active harness turn has no prepared dispatch receipt.',
|
|
331
|
+
)
|
|
332
|
+
const active = this.makeActive(pending.turnId, prepared.operationId, Date.now())
|
|
333
|
+
for (const entry of transitions) {
|
|
334
|
+
if (!('turnId' in entry) || entry.turnId !== pending.turnId) continue
|
|
335
|
+
if (entry.kind === 'dispatch-accepted') {
|
|
336
|
+
if (entry.operationId !== active.operationId)
|
|
337
|
+
throw new HarnessSessionError(
|
|
338
|
+
'invalid-journal',
|
|
339
|
+
'Dispatch receipt does not match the prepared operation.',
|
|
340
|
+
)
|
|
341
|
+
this.validateNative(entry.nativeTurn, active)
|
|
342
|
+
active.nativeTurn = harnessSnapshot(entry.nativeTurn)
|
|
343
|
+
} else if (entry.kind === 'item-bound') {
|
|
344
|
+
this.validateNative(entry.nativeItem, active)
|
|
345
|
+
active.items.set(entry.nativeItem.nativeItemId, {
|
|
346
|
+
messageId: entry.messageId,
|
|
347
|
+
toolUseId: entry.toolUseId,
|
|
348
|
+
started: false,
|
|
349
|
+
content: '',
|
|
350
|
+
blocks: new Map(),
|
|
351
|
+
})
|
|
352
|
+
} else if (entry.kind === 'review-requested') {
|
|
353
|
+
this.validateNative(entry.request, active)
|
|
354
|
+
if (harnessDigest(entry.request) !== entry.digest)
|
|
355
|
+
throw new HarnessSessionError(
|
|
356
|
+
'invalid-journal',
|
|
357
|
+
'The native review digest does not match its captured request.',
|
|
358
|
+
)
|
|
359
|
+
active.reviews.set(entry.request.requestId, {
|
|
360
|
+
request: harnessSnapshot(entry.request),
|
|
361
|
+
digest: entry.digest,
|
|
362
|
+
resolved: false,
|
|
363
|
+
})
|
|
364
|
+
} else if (entry.kind === 'review-decided') {
|
|
365
|
+
const review = active.reviews.get(entry.requestId)
|
|
366
|
+
if (
|
|
367
|
+
!review ||
|
|
368
|
+
review.digest !== entry.requestDigest ||
|
|
369
|
+
harnessDigest(entry.decision) !== entry.decisionDigest
|
|
370
|
+
)
|
|
371
|
+
throw new HarnessSessionError(
|
|
372
|
+
'invalid-journal',
|
|
373
|
+
'The native decision does not match its recorded request.',
|
|
374
|
+
)
|
|
375
|
+
review.decision = entry.decisionDigest
|
|
376
|
+
} else if (entry.kind === 'review-resolved') {
|
|
377
|
+
const review = active.reviews.get(entry.requestId)
|
|
378
|
+
if (review) review.resolved = true
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const { entries } = await this.journal.log.readAll()
|
|
382
|
+
for (const { record } of entries) {
|
|
383
|
+
if (record.turnId !== active.turnId) continue
|
|
384
|
+
if (record.type === 'turn_started') active.startedAt = Date.parse(record.ts)
|
|
385
|
+
if (record.type === 'message')
|
|
386
|
+
for (const item of active.items.values())
|
|
387
|
+
if (item.messageId === record.messageId && record.role === 'assistant') {
|
|
388
|
+
item.started = true
|
|
389
|
+
item.completed = harnessDigest(record.content)
|
|
390
|
+
item.content = typeof record.content.content === 'string' ? record.content.content : ''
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
this.active = active
|
|
394
|
+
this.phase = 'reconciliation-required'
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async run(input: Omit<HarnessPrompt, 'operationId'>): Promise<HarnessTurnOutcome> {
|
|
398
|
+
const signal = input.signal
|
|
399
|
+
const prompt = harnessSnapshot({
|
|
400
|
+
prompt: input.prompt,
|
|
401
|
+
model: input.model,
|
|
402
|
+
permissionMode: input.permissionMode,
|
|
403
|
+
...(input.effort ? { effort: input.effort } : {}),
|
|
404
|
+
})
|
|
405
|
+
const release = this.reserve()
|
|
406
|
+
if (
|
|
407
|
+
!prompt.prompt ||
|
|
408
|
+
prompt.prompt.length > 1024 * 1024 ||
|
|
409
|
+
!prompt.model ||
|
|
410
|
+
prompt.model.length > 4096
|
|
411
|
+
) {
|
|
412
|
+
release()
|
|
413
|
+
throw new HarnessSessionError(
|
|
414
|
+
'invalid-prompt',
|
|
415
|
+
'A bounded prompt and explicit model are required.',
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
let active: Active | undefined
|
|
419
|
+
let dispatchInvoked = false
|
|
420
|
+
try {
|
|
421
|
+
if (this.hasUnresolved())
|
|
422
|
+
throw new HarnessSessionError(
|
|
423
|
+
'reconciliation-required',
|
|
424
|
+
'A previous turn must be reconciled before sending another prompt.',
|
|
425
|
+
)
|
|
426
|
+
await this.connect(prompt.model, signal)
|
|
427
|
+
if (this.hasUnresolved())
|
|
428
|
+
throw new HarnessSessionError(
|
|
429
|
+
'reconciliation-required',
|
|
430
|
+
'The journal contains an unresolved native dispatch; it will not be resent.',
|
|
431
|
+
)
|
|
432
|
+
const connection = this.connection as HarnessConnection
|
|
433
|
+
if (!connection.capabilities.reviewModes.includes(prompt.permissionMode))
|
|
434
|
+
throw new HarnessSessionError(
|
|
435
|
+
'unsupported-mode',
|
|
436
|
+
'This harness does not support the selected review mode.',
|
|
437
|
+
)
|
|
438
|
+
if (prompt.effort && !connection.capabilities.effortLevels?.includes(prompt.effort))
|
|
439
|
+
throw new HarnessSessionError(
|
|
440
|
+
'unsupported-effort',
|
|
441
|
+
'This harness does not support the selected effort.',
|
|
442
|
+
)
|
|
443
|
+
await this.authorize('run', signal)
|
|
444
|
+
const lease = await this.journal.current()
|
|
445
|
+
const turnId = generateTurnId()
|
|
446
|
+
const promptId = generateMessageId()
|
|
447
|
+
const operationId = `${turnId}:${promptId}`
|
|
448
|
+
active = this.makeActive(turnId, operationId, Date.now())
|
|
449
|
+
this.active = active
|
|
450
|
+
const entry = await this.journal.log.beginTurn(lease, {
|
|
451
|
+
turnId,
|
|
452
|
+
userMessageId: promptId,
|
|
453
|
+
config: { model: prompt.model, tokenBudget: 0, timeoutMs: 0 },
|
|
454
|
+
origin: { protocol: 'sdk', kind: 'prompt' },
|
|
455
|
+
})
|
|
456
|
+
await this.journal.append({
|
|
457
|
+
type: 'message',
|
|
458
|
+
turnId,
|
|
459
|
+
messageId: promptId,
|
|
460
|
+
role: 'user',
|
|
461
|
+
kind: 'prompt',
|
|
462
|
+
content: { role: 'user', content: prompt.prompt },
|
|
463
|
+
})
|
|
464
|
+
await this.journal.transition({
|
|
465
|
+
kind: 'dispatch-prepared',
|
|
466
|
+
model: prompt.model,
|
|
467
|
+
permissionMode: prompt.permissionMode,
|
|
468
|
+
...(prompt.effort ? { effort: prompt.effort } : {}),
|
|
469
|
+
operationId,
|
|
470
|
+
turnId,
|
|
471
|
+
promptId,
|
|
472
|
+
digest: harnessDigest(prompt),
|
|
473
|
+
})
|
|
474
|
+
this.phase = 'running'
|
|
475
|
+
await this.publish({
|
|
476
|
+
type: 'turn_started',
|
|
477
|
+
sessionId: this.scope.sessionId,
|
|
478
|
+
turnId,
|
|
479
|
+
userMessageId: promptId,
|
|
480
|
+
config: { model: prompt.model, tokenBudget: 0, timeoutMs: 0 },
|
|
481
|
+
v: 1,
|
|
482
|
+
seq: entry.record.seq,
|
|
483
|
+
generation: entry.record.gen,
|
|
484
|
+
})
|
|
485
|
+
// Recheck current host authority and writer immediately before the external effect.
|
|
486
|
+
await this.authorize('run', signal)
|
|
487
|
+
await this.journal.current()
|
|
488
|
+
if (signal) {
|
|
489
|
+
const ownTurn = active
|
|
490
|
+
const abort = () => {
|
|
491
|
+
ownTurn.abortRequested = true
|
|
492
|
+
if (ownTurn.nativeTurn)
|
|
493
|
+
void this.cancel(ownTurn.turnId).catch((error: unknown) => ownTurn.reject(error))
|
|
494
|
+
}
|
|
495
|
+
signal.addEventListener('abort', abort, { once: true })
|
|
496
|
+
active.removeAbort = () => signal.removeEventListener('abort', abort)
|
|
497
|
+
}
|
|
498
|
+
release() // cancellation/review is admitted while the durable native turn runs
|
|
499
|
+
dispatchInvoked = true
|
|
500
|
+
const nativeTurn = harnessSnapshot(
|
|
501
|
+
HarnessNativeTurnSchema.parse(
|
|
502
|
+
await connection.dispatch({ ...prompt, operationId, ...(signal ? { signal } : {}) }),
|
|
503
|
+
),
|
|
504
|
+
)
|
|
505
|
+
await this.events
|
|
506
|
+
if (this.eventError) throw this.eventError
|
|
507
|
+
if (this.active === active) await this.bindNative(nativeTurn, active)
|
|
508
|
+
else this.validateNative(nativeTurn, active) // terminal before ACK must not reactivate
|
|
509
|
+
return await active.done
|
|
510
|
+
} catch (error) {
|
|
511
|
+
if (active && this.active === active) {
|
|
512
|
+
if (dispatchInvoked) await this.uncertain('dispatch-unconfirmed', error)
|
|
513
|
+
else await this.failBeforeDispatch(active)
|
|
514
|
+
}
|
|
515
|
+
throw error
|
|
516
|
+
} finally {
|
|
517
|
+
release()
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
private async failBeforeDispatch(active: Active): Promise<void> {
|
|
521
|
+
const settlement = this.settlement(active, 'failed')
|
|
522
|
+
await this.emit(active, {
|
|
523
|
+
type: 'turn_failed',
|
|
524
|
+
error: 'The host refused this prompt before native dispatch.',
|
|
525
|
+
settlement,
|
|
526
|
+
})
|
|
527
|
+
active.removeAbort?.()
|
|
528
|
+
active.reject(new HarnessSessionError('dispatch-refused', 'No native dispatch occurred.'))
|
|
529
|
+
this.active = undefined
|
|
530
|
+
this.phase = 'idle'
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private validateNative(native: HarnessNativeTurn, active: Active): void {
|
|
534
|
+
if (
|
|
535
|
+
native.nativeSessionId !== this.currentBinding?.nativeSessionId ||
|
|
536
|
+
(native.turnIdSource === 'operation' && native.nativeTurnId !== active.operationId)
|
|
537
|
+
)
|
|
538
|
+
throw new HarnessSessionError(
|
|
539
|
+
'native-owner-mismatch',
|
|
540
|
+
'Native event does not belong to this session and dispatch operation.',
|
|
541
|
+
)
|
|
542
|
+
if (
|
|
543
|
+
active.nativeTurn &&
|
|
544
|
+
(active.nativeTurn.nativeSessionId !== native.nativeSessionId ||
|
|
545
|
+
active.nativeTurn.nativeTurnId !== native.nativeTurnId ||
|
|
546
|
+
(active.nativeTurn.turnIdSource ?? 'engine') !== (native.turnIdSource ?? 'engine'))
|
|
547
|
+
)
|
|
548
|
+
throw new HarnessSessionError('native-turn-mismatch', 'Native event names a different turn.')
|
|
549
|
+
}
|
|
550
|
+
private async bindNative(native: HarnessNativeTurn, active: Active): Promise<void> {
|
|
551
|
+
this.validateNative(native, active)
|
|
552
|
+
if (active.nativeTurn) return
|
|
553
|
+
active.nativeTurn = harnessSnapshot({
|
|
554
|
+
nativeSessionId: native.nativeSessionId,
|
|
555
|
+
nativeTurnId: native.nativeTurnId,
|
|
556
|
+
...(native.turnIdSource ? { turnIdSource: native.turnIdSource } : {}),
|
|
557
|
+
})
|
|
558
|
+
await this.journal.transition({
|
|
559
|
+
kind: 'dispatch-accepted',
|
|
560
|
+
operationId: active.operationId,
|
|
561
|
+
turnId: active.turnId,
|
|
562
|
+
nativeTurn: active.nativeTurn,
|
|
563
|
+
})
|
|
564
|
+
if (active.abortRequested)
|
|
565
|
+
void this.cancel(active.turnId).catch((error: unknown) => active.reject(error))
|
|
566
|
+
}
|
|
567
|
+
private enqueue(event: HarnessEvent, epoch: number): Promise<void> {
|
|
568
|
+
return this.enqueueTask(() => this.consume(event), epoch)
|
|
569
|
+
}
|
|
570
|
+
private enqueueTask(operation: () => Promise<void>, epoch: number): Promise<void> {
|
|
571
|
+
const next = this.events.then(async () => {
|
|
572
|
+
if (epoch !== this.epoch) return
|
|
573
|
+
if (this.eventError !== undefined) throw this.eventError
|
|
574
|
+
await operation()
|
|
575
|
+
})
|
|
576
|
+
this.events = next.catch(async (error: unknown) => {
|
|
577
|
+
if (this.eventError === undefined) {
|
|
578
|
+
this.eventError = error
|
|
579
|
+
await this.uncertain('event-rejected', error)
|
|
580
|
+
}
|
|
581
|
+
})
|
|
582
|
+
return next
|
|
583
|
+
}
|
|
584
|
+
private async uncertain(code: string, error: unknown): Promise<void> {
|
|
585
|
+
this.phase = 'reconciliation-required'
|
|
586
|
+
const active = this.active
|
|
587
|
+
active?.removeAbort?.()
|
|
588
|
+
active?.reject(error)
|
|
589
|
+
try {
|
|
590
|
+
await this.journal.transition({
|
|
591
|
+
kind: 'connection-lost',
|
|
592
|
+
...(active ? { turnId: active.turnId } : {}),
|
|
593
|
+
code,
|
|
594
|
+
})
|
|
595
|
+
} catch {
|
|
596
|
+
/* writer failure remains retained; never report confirmed stop */
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
private async item(
|
|
600
|
+
native: HarnessNativeItem,
|
|
601
|
+
active: Active,
|
|
602
|
+
kind: 'message' | 'tool',
|
|
603
|
+
): Promise<Item> {
|
|
604
|
+
let item = active.items.get(native.nativeItemId)
|
|
605
|
+
if (!item) {
|
|
606
|
+
item = { started: false, content: '', blocks: new Map() }
|
|
607
|
+
active.items.set(native.nativeItemId, item)
|
|
608
|
+
}
|
|
609
|
+
if (kind === 'message' && !item.messageId) item.messageId = generateMessageId()
|
|
610
|
+
else if (kind === 'tool' && !item.toolUseId) item.toolUseId = generateToolCallId()
|
|
611
|
+
else return item
|
|
612
|
+
await this.journal.transition({
|
|
613
|
+
kind: 'item-bound',
|
|
614
|
+
turnId: active.turnId,
|
|
615
|
+
nativeItem: {
|
|
616
|
+
nativeSessionId: native.nativeSessionId,
|
|
617
|
+
nativeTurnId: native.nativeTurnId,
|
|
618
|
+
nativeItemId: native.nativeItemId,
|
|
619
|
+
...(native.turnIdSource ? { turnIdSource: native.turnIdSource } : {}),
|
|
620
|
+
},
|
|
621
|
+
...(item.messageId ? { messageId: item.messageId } : {}),
|
|
622
|
+
...(item.toolUseId ? { toolUseId: item.toolUseId } : {}),
|
|
623
|
+
})
|
|
624
|
+
return item
|
|
625
|
+
}
|
|
626
|
+
private async emit(active: Active, event: EventPayload): Promise<void> {
|
|
627
|
+
await this.journal.event(
|
|
628
|
+
{ ...event, sessionId: this.scope.sessionId, turnId: active.turnId } as SessionEvent,
|
|
629
|
+
this.publish,
|
|
630
|
+
)
|
|
631
|
+
}
|
|
632
|
+
private async consume(event: HarnessEvent): Promise<void> {
|
|
633
|
+
if (event.kind === 'connection-lost') {
|
|
634
|
+
const error = new HarnessSessionError(
|
|
635
|
+
'connection-lost',
|
|
636
|
+
'Native connection was lost; dispatch recovery is required.',
|
|
637
|
+
)
|
|
638
|
+
this.eventError = error
|
|
639
|
+
await this.uncertain(event.code, error)
|
|
640
|
+
return
|
|
641
|
+
}
|
|
642
|
+
const active = this.active
|
|
643
|
+
if (!active) return // never revive a terminal turn from transport tails
|
|
644
|
+
const native = event.kind === 'review-requested' ? event.request : event
|
|
645
|
+
await this.bindNative(native, active)
|
|
646
|
+
switch (event.kind) {
|
|
647
|
+
case 'turn-started':
|
|
648
|
+
return
|
|
649
|
+
case 'review-requested': {
|
|
650
|
+
const digest = harnessDigest(event.request)
|
|
651
|
+
const old = active.reviews.get(event.request.requestId)
|
|
652
|
+
if (old) {
|
|
653
|
+
if (old.digest !== digest)
|
|
654
|
+
throw new HarnessSessionError(
|
|
655
|
+
'review-conflict',
|
|
656
|
+
'Native request ID was reused with different input.',
|
|
657
|
+
)
|
|
658
|
+
return
|
|
659
|
+
}
|
|
660
|
+
active.reviews.set(event.request.requestId, {
|
|
661
|
+
request: event.request,
|
|
662
|
+
digest,
|
|
663
|
+
resolved: false,
|
|
664
|
+
})
|
|
665
|
+
await this.journal.transition({
|
|
666
|
+
kind: 'review-requested',
|
|
667
|
+
turnId: active.turnId,
|
|
668
|
+
request: event.request,
|
|
669
|
+
digest,
|
|
670
|
+
})
|
|
671
|
+
this.phase = 'waiting'
|
|
672
|
+
await this.review(event.request)
|
|
673
|
+
const presented = active.reviews.get(event.request.requestId)
|
|
674
|
+
if (presented) presented.presented = true
|
|
675
|
+
return
|
|
676
|
+
}
|
|
677
|
+
case 'review-resolved': {
|
|
678
|
+
const review = active.reviews.get(event.requestId)
|
|
679
|
+
if (!review || review.resolved) return
|
|
680
|
+
review.resolved = true
|
|
681
|
+
await this.journal.transition({
|
|
682
|
+
kind: 'review-resolved',
|
|
683
|
+
turnId: active.turnId,
|
|
684
|
+
requestId: event.requestId,
|
|
685
|
+
})
|
|
686
|
+
if (![...active.reviews.values()].some((r) => !r.resolved)) this.phase = 'running'
|
|
687
|
+
return
|
|
688
|
+
}
|
|
689
|
+
case 'turn-completed':
|
|
690
|
+
await this.finish(active, event)
|
|
691
|
+
return
|
|
692
|
+
case 'tool-started': {
|
|
693
|
+
const item = await this.item(event, active, 'tool')
|
|
694
|
+
if (item.started) return
|
|
695
|
+
item.started = true
|
|
696
|
+
item.input = event.input
|
|
697
|
+
await this.emit(active, {
|
|
698
|
+
type: 'tool_executing',
|
|
699
|
+
toolUseId: item.toolUseId as string,
|
|
700
|
+
toolName: `${this.currentBinding?.engineId}:${event.name}`,
|
|
701
|
+
input: event.input,
|
|
702
|
+
})
|
|
703
|
+
return
|
|
704
|
+
}
|
|
705
|
+
case 'tool-output': {
|
|
706
|
+
const item = await this.item(event, active, 'tool')
|
|
707
|
+
await this.emit(active, {
|
|
708
|
+
type: 'tool_progress',
|
|
709
|
+
toolUseId: item.toolUseId as string,
|
|
710
|
+
toolName: this.currentBinding?.engineId ?? 'harness',
|
|
711
|
+
message: event.text,
|
|
712
|
+
})
|
|
713
|
+
return
|
|
714
|
+
}
|
|
715
|
+
case 'tool-completed': {
|
|
716
|
+
const item = await this.item(event, active, 'tool')
|
|
717
|
+
const digest = harnessDigest(event)
|
|
718
|
+
if (item.completed === digest) return
|
|
719
|
+
if (item.completed)
|
|
720
|
+
throw new HarnessSessionError(
|
|
721
|
+
'item-conflict',
|
|
722
|
+
'Native tool completion changed after settlement.',
|
|
723
|
+
)
|
|
724
|
+
item.completed = digest
|
|
725
|
+
await this.emit(active, {
|
|
726
|
+
type: 'tool_completed',
|
|
727
|
+
toolUseId: item.toolUseId as string,
|
|
728
|
+
toolName: `${this.currentBinding?.engineId}:${event.name}`,
|
|
729
|
+
result: event.result,
|
|
730
|
+
isError: event.status !== 'completed',
|
|
731
|
+
savedPresentation: engineSavedView(event.name, item.input),
|
|
732
|
+
...(event.durationMs === undefined ? {} : { durationMs: event.durationMs }),
|
|
733
|
+
})
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
case 'message-started':
|
|
737
|
+
case 'text-delta':
|
|
738
|
+
case 'message-completed':
|
|
739
|
+
case 'reasoning': {
|
|
740
|
+
const item = await this.item(event, active, 'message')
|
|
741
|
+
const messageId = item.messageId as MessageId
|
|
742
|
+
if (!item.started) {
|
|
743
|
+
item.started = true
|
|
744
|
+
await this.emit(active, { type: 'message_started', iteration: 0, messageId })
|
|
745
|
+
}
|
|
746
|
+
if (event.kind === 'text-delta') {
|
|
747
|
+
if (item.completed) return
|
|
748
|
+
item.content += event.text
|
|
749
|
+
await this.emit(active, {
|
|
750
|
+
type: 'text_delta',
|
|
751
|
+
iteration: 0,
|
|
752
|
+
messageId,
|
|
753
|
+
text: event.text,
|
|
754
|
+
...(event.part ? { textPart: event.part } : {}),
|
|
755
|
+
})
|
|
756
|
+
} else if (event.kind === 'message-completed') {
|
|
757
|
+
await this.completeMessage(active, item, event.content, event.parts, event.stopReason)
|
|
758
|
+
} else if (event.kind === 'reasoning') {
|
|
759
|
+
let index = item.blocks.get(event.blockId)
|
|
760
|
+
if (index === undefined) {
|
|
761
|
+
index = item.blocks.size
|
|
762
|
+
item.blocks.set(event.blockId, index)
|
|
763
|
+
await this.emit(active, {
|
|
764
|
+
type: 'reasoning_started',
|
|
765
|
+
iteration: 0,
|
|
766
|
+
messageId,
|
|
767
|
+
blockIndex: index,
|
|
768
|
+
reasoningType: event.text === undefined ? 'redacted_thinking' : 'thinking',
|
|
769
|
+
})
|
|
770
|
+
}
|
|
771
|
+
if (event.status === 'pending' && event.text)
|
|
772
|
+
await this.emit(active, {
|
|
773
|
+
type: 'reasoning_delta',
|
|
774
|
+
iteration: 0,
|
|
775
|
+
messageId,
|
|
776
|
+
blockIndex: index,
|
|
777
|
+
text: event.text,
|
|
778
|
+
})
|
|
779
|
+
if (event.status === 'completed')
|
|
780
|
+
await this.emit(active, {
|
|
781
|
+
type: 'reasoning_completed',
|
|
782
|
+
iteration: 0,
|
|
783
|
+
messageId,
|
|
784
|
+
blockIndex: index,
|
|
785
|
+
signed: false,
|
|
786
|
+
...(event.text === undefined ? {} : { text: event.text }),
|
|
787
|
+
})
|
|
788
|
+
}
|
|
789
|
+
return
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
private async completeMessage(
|
|
794
|
+
active: Active,
|
|
795
|
+
item: Item,
|
|
796
|
+
content: string,
|
|
797
|
+
parts: readonly import('../../types/harness/session.js').HarnessPublicTextPart[] | undefined,
|
|
798
|
+
stopReason: import('../../types/session/stop-reason.js').MessageStopReason,
|
|
799
|
+
): Promise<void> {
|
|
800
|
+
const message: AssistantMessage = {
|
|
801
|
+
role: 'assistant',
|
|
802
|
+
content,
|
|
803
|
+
...(parts ? { textParts: parts } : {}),
|
|
804
|
+
}
|
|
805
|
+
const digest = harnessDigest(message)
|
|
806
|
+
if (item.completed === digest) return
|
|
807
|
+
if (item.completed)
|
|
808
|
+
await this.journal.append({
|
|
809
|
+
type: 'message_replaced',
|
|
810
|
+
turnId: active.turnId,
|
|
811
|
+
targetMessageId: item.messageId as MessageId,
|
|
812
|
+
content: message,
|
|
813
|
+
reason: 'history-repair',
|
|
814
|
+
})
|
|
815
|
+
else
|
|
816
|
+
await this.journal.append({
|
|
817
|
+
type: 'message',
|
|
818
|
+
turnId: active.turnId,
|
|
819
|
+
messageId: item.messageId as MessageId,
|
|
820
|
+
role: 'assistant',
|
|
821
|
+
content: message,
|
|
822
|
+
})
|
|
823
|
+
item.completed = digest
|
|
824
|
+
item.content = content
|
|
825
|
+
await this.emit(active, {
|
|
826
|
+
type: 'message_completed',
|
|
827
|
+
iteration: 0,
|
|
828
|
+
messageId: item.messageId as MessageId,
|
|
829
|
+
content,
|
|
830
|
+
...(parts ? { textParts: parts } : {}),
|
|
831
|
+
stopReason,
|
|
832
|
+
})
|
|
833
|
+
}
|
|
834
|
+
private async finish(
|
|
835
|
+
active: Active,
|
|
836
|
+
event: Extract<HarnessEvent, { kind: 'turn-completed' }>,
|
|
837
|
+
): Promise<void> {
|
|
838
|
+
let final = event.finalItemId ? active.items.get(event.finalItemId) : undefined
|
|
839
|
+
if (event.result !== undefined) {
|
|
840
|
+
if (!final && event.finalItemId)
|
|
841
|
+
final = await this.item({ ...event, nativeItemId: event.finalItemId }, active, 'message')
|
|
842
|
+
if (final?.messageId && (final.content !== event.result || !final.completed))
|
|
843
|
+
await this.completeMessage(
|
|
844
|
+
active,
|
|
845
|
+
final,
|
|
846
|
+
event.result,
|
|
847
|
+
undefined,
|
|
848
|
+
event.status === 'cancelled' ? 'cancelled' : 'end_turn',
|
|
849
|
+
)
|
|
850
|
+
}
|
|
851
|
+
const result = event.result ?? final?.content ?? ''
|
|
852
|
+
const settlement: TurnSettlement = this.settlement(active, event.status, final?.messageId)
|
|
853
|
+
if (event.status === 'failed')
|
|
854
|
+
await this.emit(active, {
|
|
855
|
+
type: 'turn_failed',
|
|
856
|
+
error: event.error?.message ?? 'External harness turn failed.',
|
|
857
|
+
settlement,
|
|
858
|
+
})
|
|
859
|
+
else
|
|
860
|
+
await this.emit(active, {
|
|
861
|
+
type: 'turn_completed',
|
|
862
|
+
result,
|
|
863
|
+
stopReason: event.status === 'cancelled' ? 'cancelled' : 'end_turn',
|
|
864
|
+
settlement,
|
|
865
|
+
})
|
|
866
|
+
active.removeAbort?.()
|
|
867
|
+
this.active = undefined
|
|
868
|
+
this.phase = 'idle'
|
|
869
|
+
active.resolve({
|
|
870
|
+
turnId: active.turnId,
|
|
871
|
+
nativeTurn: active.nativeTurn as HarnessNativeTurn,
|
|
872
|
+
status: event.status,
|
|
873
|
+
messages: harnessSnapshot(await this.journal.log.messages()),
|
|
874
|
+
})
|
|
875
|
+
}
|
|
876
|
+
private settlement(
|
|
877
|
+
active: Active,
|
|
878
|
+
status: HarnessTurnOutcome['status'],
|
|
879
|
+
resultMessageId?: MessageId,
|
|
880
|
+
): TurnSettlement {
|
|
881
|
+
return {
|
|
882
|
+
status,
|
|
883
|
+
iterations: 0,
|
|
884
|
+
usage: {
|
|
885
|
+
promptTokens: 0,
|
|
886
|
+
completionTokens: 0,
|
|
887
|
+
totalTokens: 0,
|
|
888
|
+
cachedTokens: 0,
|
|
889
|
+
cacheWriteTokens: 0,
|
|
890
|
+
},
|
|
891
|
+
cost: { totalCost: 0, cacheDiscount: 0, unpricedTokens: 0 },
|
|
892
|
+
durationMs: Math.max(0, Date.now() - active.startedAt),
|
|
893
|
+
resultSource: 'model',
|
|
894
|
+
...(resultMessageId ? { resultMessageId } : {}),
|
|
895
|
+
abandonedJobIds: [],
|
|
896
|
+
abandonedTaskIds: [],
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
async respond(input: HarnessReviewRequest, value: HarnessDecision): Promise<void> {
|
|
901
|
+
const request = harnessSnapshot(HarnessReviewRequestSchema.parse(input))
|
|
902
|
+
const decision = harnessSnapshot(HarnessDecisionSchema.parse(value))
|
|
903
|
+
const release = this.reserve()
|
|
904
|
+
let reserved = false
|
|
905
|
+
try {
|
|
906
|
+
const active = this.active
|
|
907
|
+
const review = active?.reviews.get(request.requestId)
|
|
908
|
+
if (
|
|
909
|
+
!active ||
|
|
910
|
+
!review ||
|
|
911
|
+
review.resolved ||
|
|
912
|
+
review.decision ||
|
|
913
|
+
this.phase !== 'waiting' ||
|
|
914
|
+
review.digest !== harnessDigest(request)
|
|
915
|
+
)
|
|
916
|
+
throw new HarnessSessionError(
|
|
917
|
+
'stale-review',
|
|
918
|
+
'This native review request is stale, altered or already decided.',
|
|
919
|
+
)
|
|
920
|
+
if (!request.decisions.includes(decision.kind))
|
|
921
|
+
throw new HarnessSessionError(
|
|
922
|
+
'unsupported-decision',
|
|
923
|
+
'The native request does not permit this decision.',
|
|
924
|
+
)
|
|
925
|
+
await this.authorize('approval', undefined, request)
|
|
926
|
+
await this.journal.current()
|
|
927
|
+
if (this.active !== active || review.resolved || review.decision)
|
|
928
|
+
throw new HarnessSessionError(
|
|
929
|
+
'stale-review',
|
|
930
|
+
'The native request ended while authorization was pending.',
|
|
931
|
+
)
|
|
932
|
+
const digest = harnessDigest(decision)
|
|
933
|
+
await this.journal.transition({
|
|
934
|
+
kind: 'review-decided',
|
|
935
|
+
turnId: active.turnId,
|
|
936
|
+
requestId: request.requestId,
|
|
937
|
+
requestDigest: review.digest,
|
|
938
|
+
decisionDigest: digest,
|
|
939
|
+
decision,
|
|
940
|
+
})
|
|
941
|
+
reserved = true
|
|
942
|
+
review.decision = digest // durable reservation precedes native answer; unknown ACK is not retried
|
|
943
|
+
await this.authorize('approval', undefined, request)
|
|
944
|
+
await this.journal.current()
|
|
945
|
+
if (this.active !== active || review.resolved)
|
|
946
|
+
throw new HarnessSessionError(
|
|
947
|
+
'stale-review',
|
|
948
|
+
'The request ended before its decision could be sent.',
|
|
949
|
+
)
|
|
950
|
+
await (this.connection as HarnessConnection).respond(request, decision)
|
|
951
|
+
} catch (error) {
|
|
952
|
+
if (reserved) await this.uncertain('decision-unconfirmed', error)
|
|
953
|
+
throw error
|
|
954
|
+
} finally {
|
|
955
|
+
release()
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
async cancel(expectedTurnId: TurnId): Promise<{ stopped: true; turnId: TurnId }> {
|
|
959
|
+
if (this.operation || this.cancelling)
|
|
960
|
+
throw new HarnessSessionError('busy', 'Another harness operation is pending.')
|
|
961
|
+
const active = this.active
|
|
962
|
+
if (!active || active.turnId !== expectedTurnId)
|
|
963
|
+
throw new HarnessSessionError('stale-turn', 'Stop does not name the current admitted turn.')
|
|
964
|
+
if (!active.nativeTurn || !this.connection)
|
|
965
|
+
throw new HarnessSessionError(
|
|
966
|
+
'dispatch-unconfirmed',
|
|
967
|
+
'The native dispatch is not confirmed; reconcile or close this session.',
|
|
968
|
+
)
|
|
969
|
+
this.cancelling = true
|
|
970
|
+
try {
|
|
971
|
+
await this.journal.current()
|
|
972
|
+
if (this.connection.capabilities.interrupt === 'unavailable')
|
|
973
|
+
throw new HarnessSessionError(
|
|
974
|
+
'unsupported-cancel',
|
|
975
|
+
'The native engine cannot confirm interruption.',
|
|
976
|
+
)
|
|
977
|
+
if (this.connection.capabilities.interrupt === 'process-stop') {
|
|
978
|
+
await this.connection.close()
|
|
979
|
+
this.connection = undefined
|
|
980
|
+
++this.epoch
|
|
981
|
+
if (this.active === active)
|
|
982
|
+
await this.finish(active, {
|
|
983
|
+
kind: 'turn-completed',
|
|
984
|
+
...active.nativeTurn,
|
|
985
|
+
status: 'cancelled',
|
|
986
|
+
})
|
|
987
|
+
} else {
|
|
988
|
+
await this.connection.interrupt(active.nativeTurn)
|
|
989
|
+
await active.done // ACK is not completion; exact terminal event is required
|
|
990
|
+
}
|
|
991
|
+
return { stopped: true, turnId: expectedTurnId }
|
|
992
|
+
} finally {
|
|
993
|
+
this.cancelling = false
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
async reconnect(signal?: AbortSignal): Promise<void> {
|
|
997
|
+
const release = this.reserve()
|
|
998
|
+
try {
|
|
999
|
+
if (this.active && this.phase !== 'reconciliation-required')
|
|
1000
|
+
throw new HarnessSessionError('busy', 'A native turn is still running.')
|
|
1001
|
+
await this.authorize('reconnect', signal)
|
|
1002
|
+
if (this.connection) {
|
|
1003
|
+
await this.connection.close()
|
|
1004
|
+
this.connection = undefined
|
|
1005
|
+
++this.epoch
|
|
1006
|
+
}
|
|
1007
|
+
await this.events
|
|
1008
|
+
this.eventError = undefined
|
|
1009
|
+
await this.connect(undefined, signal)
|
|
1010
|
+
if (!this.active) {
|
|
1011
|
+
this.phase = 'idle'
|
|
1012
|
+
return
|
|
1013
|
+
}
|
|
1014
|
+
await this.authorize('reconnect', signal)
|
|
1015
|
+
const snapshot = harnessSnapshot(await this.requireConnection().readHistory(signal))
|
|
1016
|
+
await this.reconcile(snapshot)
|
|
1017
|
+
} finally {
|
|
1018
|
+
release()
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
private async reconcile(snapshot: HarnessHistorySnapshot): Promise<void> {
|
|
1022
|
+
if (
|
|
1023
|
+
!snapshot.complete ||
|
|
1024
|
+
harnessDigest(snapshot.binding) !== harnessDigest(this.currentBinding)
|
|
1025
|
+
)
|
|
1026
|
+
throw new HarnessSessionError(
|
|
1027
|
+
'reconciliation-required',
|
|
1028
|
+
'This engine cannot provide a complete history for the uncertain dispatch.',
|
|
1029
|
+
)
|
|
1030
|
+
const active = this.active
|
|
1031
|
+
if (!active?.nativeTurn)
|
|
1032
|
+
throw new HarnessSessionError(
|
|
1033
|
+
'reconciliation-required',
|
|
1034
|
+
'An unacknowledged dispatch has no trustworthy native turn correlation; it will not be resent.',
|
|
1035
|
+
)
|
|
1036
|
+
if (snapshot.activeTurn) this.validateNative(snapshot.activeTurn, active)
|
|
1037
|
+
for (const input of snapshot.events) {
|
|
1038
|
+
const event = harnessSnapshot(HarnessEventSchema.parse(input))
|
|
1039
|
+
if (event.kind === 'connection-lost') continue
|
|
1040
|
+
const native = event.kind === 'review-requested' ? event.request : event
|
|
1041
|
+
if (native.nativeTurnId !== active.nativeTurn.nativeTurnId) continue
|
|
1042
|
+
await this.consume(event)
|
|
1043
|
+
}
|
|
1044
|
+
if (!this.active) return
|
|
1045
|
+
if (!snapshot.activeTurn)
|
|
1046
|
+
throw new HarnessSessionError(
|
|
1047
|
+
'reconciliation-required',
|
|
1048
|
+
'Native history did not prove a terminal or an active exact turn.',
|
|
1049
|
+
)
|
|
1050
|
+
for (const input of snapshot.pendingReviews) {
|
|
1051
|
+
const request = harnessSnapshot(HarnessReviewRequestSchema.parse(input))
|
|
1052
|
+
this.validateNative(request, active)
|
|
1053
|
+
const pending = active.reviews.get(request.requestId)
|
|
1054
|
+
if (pending?.decision)
|
|
1055
|
+
throw new HarnessSessionError(
|
|
1056
|
+
'reconciliation-required',
|
|
1057
|
+
'A durable decision has no confirmed native resolution; it will not be sent again.',
|
|
1058
|
+
)
|
|
1059
|
+
if (pending) {
|
|
1060
|
+
if (pending.resolved || pending.digest !== harnessDigest(request))
|
|
1061
|
+
throw new HarnessSessionError(
|
|
1062
|
+
'reconciliation-required',
|
|
1063
|
+
'Native pending review contradicts its recorded request or resolution.',
|
|
1064
|
+
)
|
|
1065
|
+
if (!pending.presented) {
|
|
1066
|
+
await this.review(pending.request)
|
|
1067
|
+
pending.presented = true
|
|
1068
|
+
}
|
|
1069
|
+
} else await this.consume({ kind: 'review-requested', request })
|
|
1070
|
+
}
|
|
1071
|
+
const pendingIds = new Set(snapshot.pendingReviews.map((request) => request.requestId))
|
|
1072
|
+
if (
|
|
1073
|
+
[...active.reviews.values()].some(
|
|
1074
|
+
(request) => !request.resolved && !pendingIds.has(request.request.requestId),
|
|
1075
|
+
)
|
|
1076
|
+
)
|
|
1077
|
+
throw new HarnessSessionError(
|
|
1078
|
+
'reconciliation-required',
|
|
1079
|
+
'Native history did not prove the resolution of a recorded pending review.',
|
|
1080
|
+
)
|
|
1081
|
+
this.phase = [...active.reviews.values()].some((r) => !r.resolved) ? 'waiting' : 'running'
|
|
1082
|
+
}
|
|
1083
|
+
async close(): Promise<void> {
|
|
1084
|
+
if (this.phase === 'closed') return
|
|
1085
|
+
const release = this.reserve()
|
|
1086
|
+
try {
|
|
1087
|
+
if (this.connection) {
|
|
1088
|
+
await this.connection.close() // failure retains the exact cleanup authority and writer
|
|
1089
|
+
this.connection = undefined
|
|
1090
|
+
++this.epoch
|
|
1091
|
+
}
|
|
1092
|
+
await this.events
|
|
1093
|
+
if (this.active && !(await this.journal.log.activeTurn())) {
|
|
1094
|
+
this.active.removeAbort?.()
|
|
1095
|
+
this.active.reject(
|
|
1096
|
+
new HarnessSessionError(
|
|
1097
|
+
'terminal-recorded',
|
|
1098
|
+
'The native terminal was recorded before its live notification failed.',
|
|
1099
|
+
),
|
|
1100
|
+
)
|
|
1101
|
+
this.active = undefined
|
|
1102
|
+
}
|
|
1103
|
+
if (this.active?.nativeTurn)
|
|
1104
|
+
await this.finish(this.active, {
|
|
1105
|
+
kind: 'turn-completed',
|
|
1106
|
+
...this.active.nativeTurn,
|
|
1107
|
+
status: 'cancelled',
|
|
1108
|
+
})
|
|
1109
|
+
else if (this.active) {
|
|
1110
|
+
this.active.removeAbort?.()
|
|
1111
|
+
this.active.reject(
|
|
1112
|
+
new HarnessSessionError(
|
|
1113
|
+
'reconciliation-required',
|
|
1114
|
+
'Unacknowledged dispatch remains unresolved in the durable log.',
|
|
1115
|
+
),
|
|
1116
|
+
)
|
|
1117
|
+
this.active = undefined
|
|
1118
|
+
}
|
|
1119
|
+
await this.journal.release()
|
|
1120
|
+
this.phase = 'closed'
|
|
1121
|
+
} finally {
|
|
1122
|
+
release()
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|