@sema-agent/server 7.138.0 → 7.140.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/MIGRATION.md +457 -7
- package/README.md +7 -4
- package/README.zh-CN.md +6 -4
- package/USAGE.md +91 -41
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +26 -26
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/live-deps.js +3 -2
- package/dist/bench/s1/row.d.ts +5 -2
- package/dist/bench/s1/row.js +2 -22
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/adoption.js +1 -1
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +28 -34
- package/dist/boot/execution-lane.d.ts +9 -1
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/governance-seams.d.ts +3 -2
- package/dist/boot/governance-seams.js +9 -11
- package/dist/boot/memory-boundary.d.ts +14 -51
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +15 -11
- package/dist/boot/memory-consolidation.js +25 -14
- package/dist/boot/resolve-spec.d.ts +4 -6
- package/dist/boot/resolve-spec.js +10 -13
- package/dist/boot/retention-lane.d.ts +5 -5
- package/dist/boot/retention-lane.js +0 -6
- package/dist/boot/runner-deps.d.ts +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +2 -6
- package/dist/boot/stage-03-store-backend.js +2 -3
- package/dist/boot/stage-04-stores.d.ts +2 -6
- package/dist/boot/stage-04-stores.js +5 -5
- package/dist/boot/stage-05-execution-env.d.ts +2 -6
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +2 -6
- package/dist/boot/stage-06-runners.js +2 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -6
- package/dist/boot/stage-08-reapers.d.ts +2 -6
- package/dist/boot/stage-09-leader.d.ts +2 -6
- package/dist/boot/stage-09-leader.js +1 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -6
- package/dist/boot/stage-10-http-server.js +7 -6
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +27 -40
- package/dist/boot/task-list-lane.d.ts +1 -1
- package/dist/boot/task-list-lane.js +1 -1
- package/dist/boot/web-admin.js +37 -11
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/capabilities/mcp-probe.js +1 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +15 -7
- package/dist/capability-facts.js +8 -3
- package/dist/config-catalog.js +9 -8
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +33 -37
- package/dist/config-provider.js +51 -30
- package/dist/config-types.d.ts +21 -7
- package/dist/config.d.ts +33 -0
- package/dist/config.js +46 -16
- package/dist/delegated-session-owner.d.ts +4 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/deployment-shape.d.ts +10 -9
- package/dist/deployment-shape.js +4 -4
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +115 -105
- package/dist/execution-lane-caps.js +76 -82
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/durable-run-leg.js +9 -5
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
- package/dist/http/memory-compliance-failure-reply.js +5 -0
- package/dist/http/memory-layer-reply.d.ts +30 -0
- package/dist/http/memory-layer-reply.js +24 -0
- package/dist/http/resume-legs.js +63 -36
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-ctx.d.ts +14 -8
- package/dist/http/routes/adoption.js +1 -1
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +8 -1
- package/dist/http/routes/memory-consolidation.js +12 -1
- package/dist/http/routes/memory-origin.js +14 -2
- package/dist/http/routes/memory-policy.js +5 -0
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +143 -22
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +4 -5
- package/dist/http/routes/tasks.js +41 -33
- package/dist/http/server.d.ts +40 -44
- package/dist/http/server.js +13 -2
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +8 -12
- package/dist/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/lane-home-probe.d.ts +68 -42
- package/dist/lane-home-probe.js +53 -30
- package/dist/leader/wire.js +5 -4
- package/dist/memory-bundle-engine.d.ts +16 -9
- package/dist/memory-bundle-engine.js +29 -6
- package/dist/memory-operator-faces.d.ts +85 -30
- package/dist/memory-operator-faces.js +136 -30
- package/dist/memory-project-layer.d.ts +3 -21
- package/dist/memory-project-layer.js +11 -8
- package/dist/memory-scope-stores.d.ts +73 -0
- package/dist/memory-scope-stores.js +86 -0
- package/dist/memory-scope.d.ts +29 -5
- package/dist/memory-scope.js +16 -4
- package/dist/memory-sync-client.d.ts +70 -18
- package/dist/memory-sync-client.js +215 -117
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.js +0 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +34 -9
- package/dist/parked-decide.js +12 -19
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +13 -7
- package/dist/peer-seat.js +37 -3
- package/dist/plugins/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +44 -26
- package/dist/plugins/approval-nonce-store.js +4 -4
- package/dist/plugins/blob-backend.d.ts +4 -4
- package/dist/plugins/blob-backend.js +7 -7
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/caching-session-store.d.ts +6 -2
- package/dist/plugins/caching-session-store.js +9 -3
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
- package/dist/plugins/checkpoint-store-sql.js +21 -23
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +68 -122
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +9 -8
- package/dist/plugins/file-history-store-sql.js +9 -9
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/leader-run-store-sql.js +4 -4
- package/dist/plugins/local-session-store.d.ts +149 -50
- package/dist/plugins/local-session-store.js +382 -107
- package/dist/plugins/mailbox-store-sql.d.ts +2 -1
- package/dist/plugins/mailbox-store-sql.js +4 -4
- package/dist/plugins/memory-engine-tidb.js +5 -5
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +117 -8
- package/dist/plugins/one-time-migrations.js +408 -38
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
- package/dist/plugins/permission-rule-store-sql.js +29 -32
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +31 -20
- package/dist/plugins/pg-session-storage.js +120 -52
- package/dist/plugins/remote-env-adb.d.ts +18 -19
- package/dist/plugins/remote-env-adb.js +91 -66
- package/dist/plugins/remote-env-device.d.ts +23 -31
- package/dist/plugins/remote-env-device.js +19 -17
- package/dist/plugins/remote-env-e2b.d.ts +27 -21
- package/dist/plugins/remote-env-e2b.js +91 -45
- package/dist/plugins/remote-env-file-error.d.ts +3 -1
- package/dist/plugins/remote-env-file-error.js +9 -3
- package/dist/plugins/remote-env-host.d.ts +31 -34
- package/dist/plugins/remote-env-host.js +91 -72
- package/dist/plugins/remote-env-k8s.d.ts +13 -29
- package/dist/plugins/remote-env-k8s.js +63 -55
- package/dist/plugins/remote-env-local-docker.d.ts +24 -12
- package/dist/plugins/remote-env-local-docker.js +49 -61
- package/dist/plugins/remote-env-ssh.d.ts +11 -19
- package/dist/plugins/remote-env-ssh.js +74 -57
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/resume-anchor-store-sql.js +2 -2
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +2 -2
- package/dist/plugins/retention-store-sql.d.ts +10 -7
- package/dist/plugins/retention-store-sql.js +13 -7
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +20 -15
- package/dist/plugins/schema-consistency.d.ts +21 -11
- package/dist/plugins/schema-consistency.js +15 -9
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/schema-ensure-legs.d.ts +2 -3
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-capture-record-store-sql.js +1 -1
- package/dist/plugins/session-fork.d.ts +54 -0
- package/dist/plugins/session-fork.js +38 -1
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +1 -1
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.js +1 -1
- package/dist/plugins/shared-memory-store-sql.js +2 -2
- package/dist/plugins/sql-driver.d.ts +10 -5
- package/dist/plugins/sql-driver.js +6 -6
- package/dist/plugins/sql-errors.js +1 -1
- package/dist/plugins/store-backend.d.ts +5 -7
- package/dist/plugins/store-backend.js +23 -11
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +13 -7
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +57 -26
- package/dist/plugins/tidb-session-store.js +127 -59
- package/dist/plugins/tool-result-store-sql.d.ts +5 -4
- package/dist/plugins/tool-result-store-sql.js +21 -13
- package/dist/plugins/user-input-ledger-sql.js +2 -2
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.js +19 -19
- package/dist/plugins/write-behind-counter.d.ts +2 -3
- package/dist/plugins/write-behind-counter.js +5 -5
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +34 -35
- package/dist/runs.d.ts +10 -5
- package/dist/runs.js +52 -66
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +9 -5
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +20 -10
- package/dist/session-incarnation.js +5 -3
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
- package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
- package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
- package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
- package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-a2a.js +5 -6
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +22 -9
- package/dist/task-mcp.js +17 -9
- package/dist/terminal.d.ts +27 -0
- package/dist/terminal.js +6 -0
- package/dist/tool-approval.d.ts +6 -0
- package/dist/tool-approval.js +8 -1
- package/dist/trace/project.d.ts +1 -0
- package/dist/trace/project.js +1 -0
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +80 -28
- package/dist/trace/self-minted-wire.d.ts +169 -2
- package/dist/trace/self-minted-wire.js +54 -1
- package/dist/url-refusal.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/candidate-gate.d.ts +11 -7
- package/dist/web-admin-client/candidate-gate.js +1 -1
- package/dist/web-admin-client/http-client.d.ts +5 -4
- package/dist/web-admin-client/http-client.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +7 -17
- package/dist/web-admin-client/read-warnings.js +5 -9
- package/dist/web-admin-client/skills-mcp.js +3 -2
- package/dist/web-admin-client/types.d.ts +6 -0
- package/package.json +3 -3
- package/wire-changes/server@7.139.0.json +348 -0
- package/wire-changes/server@7.140.0.json +184 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
|
@@ -7,6 +7,8 @@ import { seatText } from "../../trace/exit-seat.js";
|
|
|
7
7
|
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
8
8
|
import { MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
|
|
9
9
|
import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
|
|
10
|
+
import { readLayerSeat, sentMemoryLayerRefusal } from "../memory-layer-reply.js";
|
|
11
|
+
import { foldPlaneStatuses } from "../../memory-operator-faces.js";
|
|
10
12
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
11
13
|
import { readWritableSessionPermissionRules } from "../../session-policy-wire.js";
|
|
12
14
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
|
|
@@ -18,6 +20,10 @@ import * as facts from "../../capability-facts.js";
|
|
|
18
20
|
import { rosterAgentId } from "./agents-roster.js";
|
|
19
21
|
import { projectSessionListRow } from "./sessions-list.js";
|
|
20
22
|
import { readSessionRowPatch } from "../../session-row.js";
|
|
23
|
+
import { forkPathOf } from "../execution-context.js";
|
|
24
|
+
import { executionPlacementView } from "../../execution-placement.js";
|
|
25
|
+
import { ForkTargetUnsettledError, forkKeyOf } from "../../plugins/session-fork.js";
|
|
26
|
+
import { fieldConflictFacts } from "../../field-conflict.js";
|
|
21
27
|
import { buildBoundedToolNames } from "../../panel-tool-names.js";
|
|
22
28
|
import { projectMcpAuthorization } from "../../mcp-authorization-face.js";
|
|
23
29
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
@@ -172,7 +178,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
172
178
|
mcpStatusCache.clear();
|
|
173
179
|
const panel = (async () => {
|
|
174
180
|
const stopDial = new AbortController();
|
|
175
|
-
const matP = materializeMcpTools(specs, principal, undefined, undefined, undefined, deps.mcpRevocations, undefined, undefined, undefined, stopDial.signal, undefined, deps.peerDesk.seat
|
|
181
|
+
const matP = materializeMcpTools(specs, principal, undefined, undefined, undefined, deps.mcpRevocations, undefined, undefined, undefined, stopDial.signal, undefined, deps.peerDesk.seat);
|
|
176
182
|
const mat = await raceDeadline(matP, MCP_DIAL_DEADLINE_MS).catch(() => null);
|
|
177
183
|
if (!mat) {
|
|
178
184
|
stopDial.abort();
|
|
@@ -221,7 +227,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
221
227
|
if (a2aStatusCache.size > 1024)
|
|
222
228
|
a2aStatusCache.clear();
|
|
223
229
|
const panel = (async () => {
|
|
224
|
-
const matP = materializeA2aTools(specs, principal, undefined, undefined, deps.peerDesk.seat
|
|
230
|
+
const matP = materializeA2aTools(specs, principal, undefined, undefined, deps.peerDesk.seat);
|
|
225
231
|
let timer;
|
|
226
232
|
const mat = await Promise.race([matP.catch(() => null), new Promise((r) => { timer = setTimeout(() => r(null), A2A_MATERIALIZE_TIMEOUT_MS); })]);
|
|
227
233
|
if (timer)
|
|
@@ -286,6 +292,29 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
286
292
|
return;
|
|
287
293
|
}
|
|
288
294
|
}
|
|
295
|
+
{
|
|
296
|
+
if (match.decl.id === "session-execution-placement") {
|
|
297
|
+
const placementFact = facts.executionPlacementRead(deps);
|
|
298
|
+
if (!placementFact.available) {
|
|
299
|
+
sendError(res, 501, "capability.session_store_required", "the execution placement read requires a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const placementSession = safeDecode(match.params.sessionId);
|
|
303
|
+
if (placementSession === null) {
|
|
304
|
+
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const reading = await placementFact.seat.read(placementSession);
|
|
308
|
+
if (reading === undefined) {
|
|
309
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config) && !sessionOwnerOk(req, res, await sessionOwnerCellOf(deps.sessionStorage, placementSession, reading.occupant.owner)))
|
|
313
|
+
return;
|
|
314
|
+
sendJson(res, 200, executionPlacementView(reading.cell));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
289
318
|
{
|
|
290
319
|
if (match.decl.id === "session-events") {
|
|
291
320
|
const evFact = facts.sessionEventStream(deps);
|
|
@@ -524,11 +553,17 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
524
553
|
sendError(res, 400, "request.field_invalid", "allowUnevidenced must be a boolean when present");
|
|
525
554
|
return;
|
|
526
555
|
}
|
|
556
|
+
const at = readLayerSeat(res, Object.prototype.hasOwnProperty.call(raw, "scope"), raw.scope);
|
|
557
|
+
if (at === null)
|
|
558
|
+
return;
|
|
559
|
+
const withheld = (coreMessage) => deps.logger?.warn?.("session_memory_erase_refused", { sessionId, err: coreMessage });
|
|
527
560
|
try {
|
|
528
|
-
|
|
561
|
+
const input = { requestId: raw.requestId, select: { sessionId }, ...(raw.allowUnevidenced !== undefined ? { allowUnevidenced: raw.allowUnevidenced } : {}) };
|
|
562
|
+
sendJson(res, 200, await (at !== undefined ? faces.erase(input, at) : faces.erase(input)));
|
|
529
563
|
}
|
|
530
564
|
catch (err) {
|
|
531
|
-
|
|
565
|
+
if (sentMemoryLayerRefusal(res, err, { kind: "session_owner", withheld }))
|
|
566
|
+
return;
|
|
532
567
|
if (sentMemoryComplianceFailure(res, err, { kind: "session_owner", withheld }, deps.logger))
|
|
533
568
|
return;
|
|
534
569
|
deps.logger?.warn?.("session_memory_erase_failed", { sessionId, err: String(err) });
|
|
@@ -560,20 +595,27 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
560
595
|
sendError(res, 501, "capability.memory_engine_required", "session memory status is not available on this deployment — it requires a wired memory engine (MEMORY_ENGINE=on) whose backend owns the engine control plane (controlPlaneRoot); the file memory backend does, the pg/tidb memory backends do not (their control plane would land on a replica's local disk)");
|
|
561
596
|
return;
|
|
562
597
|
}
|
|
598
|
+
const query = new URLSearchParams(match.search);
|
|
599
|
+
const at = readLayerSeat(res, query.has("scope"), query.get("scope"));
|
|
600
|
+
if (at === null)
|
|
601
|
+
return;
|
|
563
602
|
try {
|
|
564
603
|
const { memoryStatusCache } = ctx.local.sessions;
|
|
565
|
-
|
|
566
|
-
|
|
604
|
+
const statusOf = (store) => {
|
|
605
|
+
const cacheKey = JSON.stringify([sessionId, store.controlPlane]);
|
|
606
|
+
const cached = memoryStatusCache.get(cacheKey);
|
|
607
|
+
if (cached && Date.now() - cached.at <= MEMORY_STATUS_TTL_MS)
|
|
608
|
+
return cached.status;
|
|
567
609
|
if (memoryStatusCache.size > 1024)
|
|
568
610
|
memoryStatusCache.clear();
|
|
569
|
-
const status =
|
|
611
|
+
const status = store.status(sessionId);
|
|
570
612
|
const fresh = { at: Date.now(), status };
|
|
571
|
-
memoryStatusCache.set(
|
|
572
|
-
status.catch(() => { if (memoryStatusCache.get(
|
|
573
|
-
memoryStatusCache.delete(
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
const s = await
|
|
613
|
+
memoryStatusCache.set(cacheKey, fresh);
|
|
614
|
+
status.catch(() => { if (memoryStatusCache.get(cacheKey) === fresh)
|
|
615
|
+
memoryStatusCache.delete(cacheKey); });
|
|
616
|
+
return status;
|
|
617
|
+
};
|
|
618
|
+
const s = foldPlaneStatuses(await Promise.all(face.storesAt(at).map(statusOf)));
|
|
577
619
|
sendJson(res, 200, {
|
|
578
620
|
sessionId,
|
|
579
621
|
autoConsolidationArmed: deps.memoryAutoConsolidationArmed,
|
|
@@ -585,6 +627,8 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
585
627
|
});
|
|
586
628
|
}
|
|
587
629
|
catch (err) {
|
|
630
|
+
if (sentMemoryLayerRefusal(res, err, { kind: "session_owner", withheld: (message) => deps.logger?.warn?.("session_memory_status_refused", { sessionId, err: message }) }))
|
|
631
|
+
return;
|
|
588
632
|
deps.logger?.warn?.("session_memory_status_failed", { sessionId, err: String(err) });
|
|
589
633
|
sendError(res, 500, "internal.error", "session memory status read failed");
|
|
590
634
|
}
|
|
@@ -675,13 +719,25 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
675
719
|
sendError(res, 400, read.problem.code, read.problem.message);
|
|
676
720
|
return;
|
|
677
721
|
}
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
722
|
+
const outcome = await store.writeSessionRowCells(sessionId, read.patch, { writer: "client", row: { row: face }, ...(read.expectedRowTag !== undefined ? { rowTag: read.expectedRowTag } : {}) });
|
|
723
|
+
switch (outcome.kind) {
|
|
724
|
+
case "written":
|
|
725
|
+
sendJson(res, 200, projectSessionListRow(outcome.row));
|
|
726
|
+
return;
|
|
727
|
+
case "changed":
|
|
728
|
+
sendError(res, 409, "conflict.session_row_changed", "the session row changed since the rowTag you sent was read — nothing was written; currentRow is the row as it stands now: decide again, then send its rowTag", { currentRow: projectSessionListRow(outcome.row) });
|
|
729
|
+
return;
|
|
730
|
+
case "mirror":
|
|
731
|
+
sendError(res, 409, "conflict.session_mirror", "this session is a read-only mirror of a session whose writer is elsewhere — it cannot be renamed or pinned here; fork it (forkPath) to continue here; nothing was written", { forkPath: forkPathOf(sessionId) });
|
|
732
|
+
return;
|
|
733
|
+
case "absent":
|
|
734
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
735
|
+
return;
|
|
736
|
+
default: {
|
|
737
|
+
const unknownOutcome = outcome;
|
|
738
|
+
throw new Error(`PATCH /v1/sessions/:id: no answer for the row write outcome ${JSON.stringify(unknownOutcome)}`);
|
|
739
|
+
}
|
|
682
740
|
}
|
|
683
|
-
sendJson(res, 200, projectSessionListRow(row));
|
|
684
|
-
return;
|
|
685
741
|
}
|
|
686
742
|
{
|
|
687
743
|
if (match.decl.id === "session-fork") {
|
|
@@ -712,12 +768,57 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
712
768
|
return;
|
|
713
769
|
}
|
|
714
770
|
const newOwner = scope.fleetWide ? (owner ?? null) : scope.gateOwner;
|
|
715
|
-
const
|
|
716
|
-
if (
|
|
771
|
+
const keyed = readForkTarget(await readJson(req), sourceId);
|
|
772
|
+
if (keyed.kind === "refused") {
|
|
773
|
+
sendError(res, 400, keyed.errorCode, keyed.message, keyed.facts);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (keyed.kind === "none") {
|
|
777
|
+
const newId = await fork(sourceId, newOwner);
|
|
778
|
+
if (newId === null) {
|
|
779
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
sendJson(res, 200, { sessionId: newId });
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const forkTo = forkFact.sessionStorage.forkTo?.bind(forkFact.sessionStorage);
|
|
786
|
+
if (forkTo === undefined) {
|
|
787
|
+
sendError(res, 501, "capability.session_store_required", "a keyed fork (forkId + forkNonce) requires a session store that creates the target row atomically (DB_BACKEND=mysql|pg|local)");
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
let outcome;
|
|
791
|
+
try {
|
|
792
|
+
outcome = await forkTo(sourceId, newOwner, { id: keyed.forkId, key: forkKeyOf(sourceId, keyed.forkNonce) });
|
|
793
|
+
}
|
|
794
|
+
catch (e) {
|
|
795
|
+
if (e instanceof ForkTargetUnsettledError) {
|
|
796
|
+
sendError(res, 503, "state.fork_target_unsettled", e.message, { permanence: "transient" });
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
throw e;
|
|
800
|
+
}
|
|
801
|
+
if (outcome === null) {
|
|
717
802
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
718
803
|
return;
|
|
719
804
|
}
|
|
720
|
-
|
|
805
|
+
switch (outcome.outcome) {
|
|
806
|
+
case "created":
|
|
807
|
+
sendJson(res, 200, { sessionId: outcome.id });
|
|
808
|
+
return;
|
|
809
|
+
case "replayed":
|
|
810
|
+
sendJson(res, 200, { sessionId: outcome.id, replayed: true });
|
|
811
|
+
return;
|
|
812
|
+
case "occupied":
|
|
813
|
+
if (!outcome.sameOwner) {
|
|
814
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
sendError(res, 409, "conflict.fork_id_taken", "forkId already names another session of yours (a different fork, or a session that was not created by this fork request) — pick a new forkId and forkNonce; nothing was forked", { forkId: keyed.forkId });
|
|
818
|
+
return;
|
|
819
|
+
default:
|
|
820
|
+
assertNever(outcome);
|
|
821
|
+
}
|
|
721
822
|
return;
|
|
722
823
|
}
|
|
723
824
|
}
|
|
@@ -853,6 +954,7 @@ export const SESSIONS_ROUTES = [
|
|
|
853
954
|
{ id: "session-mcp", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/mcp$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
854
955
|
{ id: "session-a2a", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/a2a$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
855
956
|
{ id: "session-head", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/head$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
957
|
+
{ id: "session-execution-placement", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/execution-placement$/, label: "/v1/sessions/:id/execution-placement", credentialGated: ["GET"], methods: ["GET"] },
|
|
856
958
|
{ id: "session-events", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/events$/, label: "/v1/sessions/:id/$verb", methods: ["GET"], subscription: true },
|
|
857
959
|
{ id: "session-fork", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/fork$/, label: "/v1/sessions/:id/$verb", methods: ["POST"] },
|
|
858
960
|
{ id: "session-policy-get", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/policy$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
@@ -864,4 +966,23 @@ export const SESSIONS_ROUTES = [
|
|
|
864
966
|
{ id: "session-patch", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)$/, label: "/v1/sessions/:id", methods: ["PATCH"] },
|
|
865
967
|
{ id: "session-delete", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)$/, label: "/v1/sessions/:id", methods: ["DELETE"] },
|
|
866
968
|
];
|
|
969
|
+
function readForkTarget(raw, sourceId) {
|
|
970
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
971
|
+
return { kind: "refused", errorCode: "request.field_invalid", message: "the fork body must be an object { forkId?, forkNonce? }" };
|
|
972
|
+
const { forkId, forkNonce } = raw;
|
|
973
|
+
if (forkId === undefined && forkNonce === undefined)
|
|
974
|
+
return { kind: "none" };
|
|
975
|
+
if (forkId === undefined || forkNonce === undefined) {
|
|
976
|
+
return { kind: "refused", errorCode: "request.field_conflict", message: "forkId and forkNonce come together — send both (a keyed, retryable fork) or neither (a fork under a freshly minted id)", facts: fieldConflictFacts("fork_target", ["forkId", "forkNonce"]) };
|
|
977
|
+
}
|
|
978
|
+
if (typeof forkId !== "string" || !isUuidShape(forkId))
|
|
979
|
+
return { kind: "refused", errorCode: "request.id_invalid", message: "forkId must be a canonical uuid (the id the forked session will have)" };
|
|
980
|
+
if (forkId === sourceId)
|
|
981
|
+
return { kind: "refused", errorCode: "request.field_invalid", message: "forkId must name a new session — it equals the session being forked" };
|
|
982
|
+
if (typeof forkNonce !== "string" || forkNonce.length === 0 || forkNonce.length > FORK_NONCE_MAX_CHARS) {
|
|
983
|
+
return { kind: "refused", errorCode: "request.field_invalid", message: `forkNonce must be a non-empty string of at most ${FORK_NONCE_MAX_CHARS} characters (the caller's key for this fork request; a retry sends the same one)` };
|
|
984
|
+
}
|
|
985
|
+
return { kind: "keyed", forkId, forkNonce };
|
|
986
|
+
}
|
|
987
|
+
const FORK_NONCE_MAX_CHARS = 255;
|
|
867
988
|
//# sourceMappingURL=sessions.js.map
|
|
@@ -8,10 +8,18 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
10
10
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
/**
|
|
12
|
+
* [ref] —— core 前置校验异常的分类(400 客户端可改 / 500 我方缺陷):**只读引擎的码**。
|
|
13
|
+
* 引擎角色族的拒(未知模型引用 / 不是模型引用 / 角色词出表 / 角色链给不出模型)一律带码 `config.unknown_role`(`detail.reason` 分臂;
|
|
14
|
+
* core 7.60.0 [ref] 起最后一臂也带码)—— 这是引擎写明的判别位,本路由的 400 只认它;句子不读(修前是一张按英文开头匹配的两行前缀表,
|
|
15
|
+
* 引擎改一个字就会把客户端错误静默翻成 500)。失败方向:别的码 / 没有码一律 500(响亮、可告警、可重试),绝不把不认识的异常猜成客户端错误。
|
|
16
|
+
* 跨仓锚:test/side-query-http.test.ts 调装机 core 的两个导出解析器真抛,拿真错误过本谓词。
|
|
17
|
+
*
|
|
18
|
+
* 🪦 原表还有第三臂 `requires a non-empty messages array`(core `core/side-query.js`)—— 不可达(本路由在调 core 之前就有
|
|
19
|
+
* `!Array.isArray(body.messages) || body.messages.length === 0 ⇒ 400 request.body_shape` 的前拦),已删;前拦一旦被摘,
|
|
20
|
+
* side-query-http 的「空/非数组 messages → 400 request.body_shape」两格先红。
|
|
21
|
+
*/
|
|
22
|
+
export declare function isCorePreflightValidationError(err: unknown): boolean;
|
|
15
23
|
/** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。 */
|
|
16
24
|
export declare function handleSideQuery(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SideQueryRouteId>, ctx: RouteCtx): Promise<void>;
|
|
17
25
|
export declare const SIDE_QUERY_ROUTES: readonly [{
|
|
@@ -4,15 +4,14 @@ import { echoCallerText } from "../../caller-text-echo.js";
|
|
|
4
4
|
import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../../model-select.js";
|
|
5
5
|
import { echoModelRef, modelUnavailableMessage } from "../../model-entry-refusal.js";
|
|
6
6
|
import { redactSecrets } from "../../trace/redact.js";
|
|
7
|
+
import { errorCodeOf } from "../../observability/err-text.js";
|
|
7
8
|
import { admitProviderEcho, redactErrorMessage } from "../../observability/run-terminal.js";
|
|
8
9
|
import { sendJson, sendError, sendHttpError, msg } from "../send.js";
|
|
9
10
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
10
11
|
import { assertNever } from "../route-ctx.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return CORE_PREFLIGHT_VALIDATION_PREFIXES.some((prefix) => message.startsWith(prefix));
|
|
12
|
+
export function isCorePreflightValidationError(err) {
|
|
13
|
+
return errorCodeOf(err) === "config.unknown_role";
|
|
14
14
|
}
|
|
15
|
-
export const CORE_PREFLIGHT_VALIDATION_ANCHORS = CORE_PREFLIGHT_VALIDATION_PREFIXES;
|
|
16
15
|
const MODEL_ROLE_ECHO_CHARS = 80;
|
|
17
16
|
function isModelRole(value) {
|
|
18
17
|
return typeof value === "string" && MODEL_ROLES.includes(value);
|
|
@@ -105,7 +104,7 @@ export async function handleSideQuery(req, res, match, ctx) {
|
|
|
105
104
|
deps.metrics?.inc?.("side_query_total", { result: "error" });
|
|
106
105
|
if (!res.writableEnded && !ac.signal.aborted) {
|
|
107
106
|
const m = msg(e);
|
|
108
|
-
const isValidation = isCorePreflightValidationError(
|
|
107
|
+
const isValidation = isCorePreflightValidationError(e);
|
|
109
108
|
sendError(res, isValidation ? 400 : 500, isValidation ? "request.field_invalid" : "internal.error", redactSecrets(m));
|
|
110
109
|
}
|
|
111
110
|
}
|
|
@@ -2,7 +2,7 @@ import { redactErrorMessage, redactHead, redactTerminalResult } from "../../obse
|
|
|
2
2
|
import { runWithVerification, runCascade, taskNotificationDedupKey, uuidv7 } from "@sema-agent/core";
|
|
3
3
|
import { heldFor } from "../../peer-seat.js";
|
|
4
4
|
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, zeroWorkRefusalFacts, releaseKeyIfZeroWorkRefusal, createPromptCommitReader, HEARTBEAT_MS } from "../../runs.js";
|
|
5
|
-
import { sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
5
|
+
import { atHostBoundary, sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
6
6
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
7
7
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
|
|
8
8
|
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
@@ -24,7 +24,8 @@ import { collectSsePreamble } from "../sse-log.js";
|
|
|
24
24
|
import { buildApprovalPreamble, buildApprovalPreambleSseFrames } from "../../approval-card.js";
|
|
25
25
|
import { approvalPreambleReplays, createApprovalCardEmitter, declaredApprovalFrameFamilies, resolveApprovalLeg, resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
26
26
|
import { buildActiveRunConflict, isActiveRunConflictBody, toDoneFrameResult } from "../active-run-conflict.js";
|
|
27
|
-
import {
|
|
27
|
+
import { commitExecutionContext } from "../execution-context.js";
|
|
28
|
+
import { rootSessionAbsentAnswer } from "../execution-context.js";
|
|
28
29
|
import { commitSessionInheritEnv } from "../session-inherit-env.js";
|
|
29
30
|
import { headerStr, gatedPrincipal, explicitOperatorOk, runIdentityOf } from "../principal-gate.js";
|
|
30
31
|
import { failOpenTagForDroppedFrame, recordFailOpen } from "../../observability/fail-open.js";
|
|
@@ -177,7 +178,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
177
178
|
sendError(res, 400, "request.field_conflict", "verify / cascade are not supported on /v1/tasks/stream (both are multi-attempt, not a single stream) — use /v1/tasks or /v1/runs", fieldConflictFacts("cascade_verify", prepared.verify ? (prepared.cascade ? ["verify", "cascade"] : ["verify"]) : ["cascade"]));
|
|
178
179
|
return;
|
|
179
180
|
}
|
|
180
|
-
const earlyDurableTid = facts.durableRuns(deps).available
|
|
181
|
+
const earlyDurableTid = facts.durableRuns(deps).available ? uuidv7() : undefined;
|
|
181
182
|
const streamLegStartedAt = Date.now();
|
|
182
183
|
const detachOnDisconnect = headerStr(req.headers["x-detach-on-disconnect"]) === "true";
|
|
183
184
|
if (detachOnDisconnect && earlyDurableTid === undefined) {
|
|
@@ -289,19 +290,19 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
289
290
|
let runRowSettled = false;
|
|
290
291
|
let durableTaskId;
|
|
291
292
|
let durableHeartbeat;
|
|
292
|
-
const cancelledResult = (errorMessage, cause) => mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId
|
|
293
|
+
const cancelledResult = (errorMessage, cause) => mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId, startedAtMs: streamLegStartedAt, message: errorMessage, cause });
|
|
293
294
|
let userMsgEntryId;
|
|
294
295
|
const promptCommit = createPromptCommitReader();
|
|
295
296
|
let anchorPut = false;
|
|
296
297
|
const putRewindAnchor = () => {
|
|
297
|
-
if (anchorPut || !userMsgEntryId || !durableTaskId || !
|
|
298
|
+
if (anchorPut || !userMsgEntryId || !durableTaskId || !deps.resumeAnchorStore)
|
|
298
299
|
return;
|
|
299
300
|
anchorPut = true;
|
|
300
301
|
void deps.resumeAnchorStore
|
|
301
302
|
.put(prepared.spec.sessionId, durableTaskId, userMsgEntryId, principal ?? null)
|
|
302
303
|
.catch(() => deps.metrics?.inc("resume_anchor_capture_failed"));
|
|
303
304
|
};
|
|
304
|
-
if (deps.runStore
|
|
305
|
+
if (deps.runStore) {
|
|
305
306
|
const tid = earlyDurableTid ?? uuidv7();
|
|
306
307
|
const created = await deps.runStore.createRun(tid, prepared.spec.sessionId, principal ?? null, deps.instanceId ?? "default", { ...runMeta(prepared, source), identity: runIdentityOf(req, deps.config, principal ?? null), idem: idem ?? null });
|
|
307
308
|
if (created.ok)
|
|
@@ -313,22 +314,25 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
313
314
|
sseData(res, { type: "done", result: doneResultOfOutcome(replay), replay: true });
|
|
314
315
|
return replay;
|
|
315
316
|
}
|
|
317
|
+
if (created.sessionAbsent) {
|
|
318
|
+
const refused = rootSessionAbsentAnswer();
|
|
319
|
+
sseData(res, { type: "done", result: doneResultOfOutcome(refused) });
|
|
320
|
+
return refused;
|
|
321
|
+
}
|
|
316
322
|
if (!created.ok) {
|
|
317
323
|
const conflict = await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, prepared.spec.sessionId, created.activeTaskId);
|
|
318
324
|
const refused = { status: 409, body: conflict };
|
|
319
325
|
sseData(res, { type: "done", result: doneResultOfOutcome(refused) });
|
|
320
326
|
return refused;
|
|
321
327
|
}
|
|
322
|
-
const placementReject = await
|
|
328
|
+
const placementReject = await commitExecutionContext(deps, { kind: "run", runStore: deps.runStore, taskId: tid }, prepared.execution);
|
|
323
329
|
if (placementReject) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
sseData(res, { type: "done", result: doneResultOfOutcome(refused) });
|
|
327
|
-
return refused;
|
|
330
|
+
sseData(res, { type: "done", result: doneResultOfOutcome(placementReject) });
|
|
331
|
+
return placementReject;
|
|
328
332
|
}
|
|
329
333
|
durableTaskId = tid;
|
|
330
334
|
}
|
|
331
|
-
if (deps.checkpointStore
|
|
335
|
+
if (deps.checkpointStore) {
|
|
332
336
|
const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() });
|
|
333
337
|
if (ctxRefusal) {
|
|
334
338
|
sseData(res, { type: "done", result: doneResultOfOutcome(ctxRefusal) });
|
|
@@ -336,8 +340,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
336
340
|
}
|
|
337
341
|
}
|
|
338
342
|
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
339
|
-
|
|
340
|
-
markChildrenStoppedByUserOnAbort(ac.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
343
|
+
markChildrenStoppedByUserOnAbort(ac.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
341
344
|
let ledgerSink;
|
|
342
345
|
let ledgerTerminal = false;
|
|
343
346
|
if (durableTaskId && deps.runStore) {
|
|
@@ -412,7 +415,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
412
415
|
}
|
|
413
416
|
: {}),
|
|
414
417
|
});
|
|
415
|
-
if (deps.userInputLedger && durableTaskId
|
|
418
|
+
if (deps.userInputLedger && durableTaskId)
|
|
416
419
|
await deps.userInputLedger.announceUndrained({ sessionId: prepared.spec.sessionId, taskId: durableTaskId });
|
|
417
420
|
try {
|
|
418
421
|
const streamBody = async () => {
|
|
@@ -420,7 +423,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
420
423
|
const tailContentMode = prepared.spec.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
421
424
|
const childReasoning = createReasoningSegments();
|
|
422
425
|
const topReasoning = createReasoningSegments();
|
|
423
|
-
const heldDecisions =
|
|
426
|
+
const heldDecisions = await heldWorkflowParkDecisions(deps, prepared.spec.sessionId);
|
|
424
427
|
const fwdInternals = {
|
|
425
428
|
...heldDecisions,
|
|
426
429
|
onForwardEvent: (e) => {
|
|
@@ -476,7 +479,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
476
479
|
}
|
|
477
480
|
const hadRow = isFleetAgentTerminalNotification(n) ? (fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false) : false;
|
|
478
481
|
const deliverable = syncLegLive && !res.writableEnded && !res.destroyed;
|
|
479
|
-
const parked = !deliverable &&
|
|
482
|
+
const parked = !deliverable && deps.workflowCompletionInbox !== undefined;
|
|
480
483
|
deps.logger?.info?.("task_notification_observed", { route: "sync-stream", taskId: n.task_id, taskType: n.task_type, status: n.status, hadFleetRow: hadRow, legLive: syncLegLive, parkedDurable: parked });
|
|
481
484
|
if (parked) {
|
|
482
485
|
void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(prepared.spec.sessionId, askOwner, n, Date.now(), durableTaskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "sync-stream", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
|
|
@@ -521,7 +524,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
521
524
|
if (durableTaskId && finalResult)
|
|
522
525
|
finalResult = { ...finalResult, taskId: durableTaskId };
|
|
523
526
|
if (causeAtDone !== undefined && finalResult !== undefined) {
|
|
524
|
-
finalResult = mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId
|
|
527
|
+
finalResult = mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId, startedAtMs: streamLegStartedAt, message: "cancelled by user", cause: causeAtDone, engineResult: finalResult });
|
|
525
528
|
}
|
|
526
529
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective);
|
|
527
530
|
noteRunOutcome(finalResult);
|
|
@@ -714,7 +717,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
714
717
|
taskId: askTaskId,
|
|
715
718
|
owner: askOwner,
|
|
716
719
|
abortSignal: ac.signal,
|
|
717
|
-
|
|
720
|
+
sessionId: prepared.spec.sessionId,
|
|
721
|
+
executionLane: prepared.execution.lane,
|
|
718
722
|
...(streamApprovalOn
|
|
719
723
|
? {
|
|
720
724
|
emitCard,
|
|
@@ -728,11 +732,12 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
728
732
|
if (deps.toolApproval) {
|
|
729
733
|
const toolApproval = deps.toolApproval;
|
|
730
734
|
prepared.spec.onAsk = unattended
|
|
731
|
-
? toolApproval.boundUnattendedAsk({ owner: askOwner, taskId: askTaskId,
|
|
735
|
+
? toolApproval.boundUnattendedAsk({ owner: askOwner, taskId: askTaskId, sessionId: prepared.spec.sessionId, executionLane: prepared.execution.lane }, unattended)
|
|
732
736
|
: toolApproval.boundAsk({
|
|
733
737
|
owner: askOwner,
|
|
734
738
|
taskId: askTaskId,
|
|
735
|
-
|
|
739
|
+
sessionId: prepared.spec.sessionId,
|
|
740
|
+
executionLane: prepared.execution.lane,
|
|
736
741
|
...(streamApprovalOn ? { legKey: "", ...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}) } : {}),
|
|
737
742
|
});
|
|
738
743
|
}
|
|
@@ -815,7 +820,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
815
820
|
try {
|
|
816
821
|
const row = await deps.runStore.getRun(durableTaskId);
|
|
817
822
|
if (row?.status === "running") {
|
|
818
|
-
const pending =
|
|
823
|
+
const pending = await deps.checkpointStore?.findPendingTokenBySession(prepared.spec.sessionId);
|
|
819
824
|
const backstopCause = ac.cause();
|
|
820
825
|
if (pending) {
|
|
821
826
|
if (ledgerSink && !ledgerTerminal) {
|
|
@@ -896,7 +901,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
896
901
|
const syncLegStartedAt = Date.now();
|
|
897
902
|
let durableTaskId;
|
|
898
903
|
let durableHeartbeat;
|
|
899
|
-
if (deps.runStore
|
|
904
|
+
if (deps.runStore) {
|
|
900
905
|
const tid = uuidv7();
|
|
901
906
|
const created = await deps.runStore.createRun(tid, prepared.spec.sessionId, principal ?? null, deps.instanceId ?? "default", { ...runMeta(prepared, source), identity: runIdentityOf(req, deps.config, principal ?? null), idem: idem ?? null });
|
|
902
907
|
if (created.ok)
|
|
@@ -905,22 +910,23 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
905
910
|
recordResourceWindow(tid, prepared.spec.resourceSuspend);
|
|
906
911
|
if (created.existing)
|
|
907
912
|
return await replayOfExistingRun(created.existing, { principal: submitter, idem }, "sync", deps, deps.runStore);
|
|
913
|
+
if (created.sessionAbsent)
|
|
914
|
+
return rootSessionAbsentAnswer();
|
|
908
915
|
if (!created.ok)
|
|
909
916
|
return { status: 409, body: await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, prepared.spec.sessionId, created.activeTaskId) };
|
|
910
|
-
const placementReject = await
|
|
917
|
+
const placementReject = await commitExecutionContext(deps, { kind: "run", runStore: deps.runStore, taskId: tid }, prepared.execution);
|
|
911
918
|
if (placementReject)
|
|
912
|
-
return
|
|
919
|
+
return placementReject;
|
|
913
920
|
durableTaskId = tid;
|
|
914
921
|
}
|
|
915
|
-
if (deps.checkpointStore
|
|
922
|
+
if (deps.checkpointStore) {
|
|
916
923
|
const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() });
|
|
917
924
|
if (ctxRefusal)
|
|
918
925
|
return ctxRefusal;
|
|
919
926
|
}
|
|
920
927
|
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
921
928
|
const syncCancelCtrl = createRunAbort();
|
|
922
|
-
|
|
923
|
-
markChildrenStoppedByUserOnAbort(syncCancelCtrl.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
929
|
+
markChildrenStoppedByUserOnAbort(syncCancelCtrl.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
924
930
|
if (durableTaskId && deps.runStore) {
|
|
925
931
|
const rs = deps.runStore;
|
|
926
932
|
const tid = durableTaskId;
|
|
@@ -950,7 +956,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
950
956
|
let result;
|
|
951
957
|
counters.uncountedBillableInflight++;
|
|
952
958
|
try {
|
|
953
|
-
const heldDecisions =
|
|
959
|
+
const heldDecisions = await heldWorkflowParkDecisions(deps, prepared.spec.sessionId);
|
|
954
960
|
const verifyLeg = (v) => runWithVerification(prepared.runner, specWithSignal, v, heldDecisions);
|
|
955
961
|
const cascadeLeg = () => runCascade(prepared.runner, specWithSignal, cascadeConfig(deps.config.cascadeLadder, prepared.spec.limits?.maxCostUsd), heldDecisions);
|
|
956
962
|
const plainLeg = async () => {
|
|
@@ -963,7 +969,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
963
969
|
}
|
|
964
970
|
return stream.result();
|
|
965
971
|
};
|
|
966
|
-
result = await instructionLeg.run(prepared.spec.sessionId, () => withPrincipal(principal, () => prepared.verify ? verifyLeg(prepared.verify) : prepared.cascade ? cascadeLeg() : plainLeg()));
|
|
972
|
+
result = atHostBoundary(await instructionLeg.run(prepared.spec.sessionId, () => withPrincipal(principal, () => prepared.verify ? verifyLeg(prepared.verify) : prepared.cascade ? cascadeLeg() : plainLeg())), prepared.spec.sessionId);
|
|
967
973
|
}
|
|
968
974
|
finally {
|
|
969
975
|
counters.uncountedBillableInflight--;
|
|
@@ -976,7 +982,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
976
982
|
if (syncAbortCause !== undefined) {
|
|
977
983
|
result = mintCancelledResult({
|
|
978
984
|
taskId: durableTaskId ?? "",
|
|
979
|
-
sessionId: prepared.spec.sessionId
|
|
985
|
+
sessionId: prepared.spec.sessionId,
|
|
980
986
|
startedAtMs: syncLegStartedAt,
|
|
981
987
|
message: "cancelled by user",
|
|
982
988
|
cause: syncAbortCause,
|
|
@@ -998,12 +1004,14 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
998
1004
|
}
|
|
999
1005
|
const exitResult = await mintSubmitExitResult(result, deps.checkpointStore);
|
|
1000
1006
|
const syncPlane = outwardPlane(result.terminal);
|
|
1001
|
-
if (syncPlane.status === "suspended" && deps.checkpointStore && deps.runStore && durableTaskId
|
|
1007
|
+
if (syncPlane.status === "suspended" && deps.checkpointStore && deps.runStore && durableTaskId) {
|
|
1002
1008
|
await deps.runStore.setSuspended(durableTaskId, exitResult);
|
|
1009
|
+
prepared.acceptance.settle(result);
|
|
1003
1010
|
return { status: 200, body: withSyncSubmitKeys(buildSyncSubmitBody(exitResult, { taskId: durableTaskId, sessionId: prepared.spec.sessionId, status: "suspended" })) };
|
|
1004
1011
|
}
|
|
1005
|
-
if (syncPlane.status === "needs_review" && deps.checkpointStore && deps.runStore && durableTaskId
|
|
1012
|
+
if (syncPlane.status === "needs_review" && deps.checkpointStore && deps.runStore && durableTaskId) {
|
|
1006
1013
|
await deps.runStore.setNeedsReview(durableTaskId, exitResult);
|
|
1014
|
+
prepared.acceptance.settle(result);
|
|
1007
1015
|
return { status: 200, body: withSyncSubmitKeys(buildSyncSubmitBody(exitResult, { taskId: durableTaskId, sessionId: prepared.spec.sessionId, status: "needs_review" })) };
|
|
1008
1016
|
}
|
|
1009
1017
|
if (durableTaskId && deps.runStore)
|