@sema-agent/server 7.141.0 → 7.143.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 +657 -0
- package/USAGE.md +102 -30
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +61 -43
- package/dist/approval-ask-audit-store.js +69 -44
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +126 -27
- package/dist/approval-card.js +103 -23
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +41 -9
- package/dist/approval-reconciler.js +45 -42
- package/dist/audit.js +4 -2
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +17 -1
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +26 -23
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +5 -4
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.js +9 -7
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +19 -10
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.d.ts +10 -7
- package/dist/boot/runner-deps.js +20 -29
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +19 -4
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +6 -7
- package/dist/boot/stage-05-execution-env.d.ts +6 -3
- package/dist/boot/stage-05-execution-env.js +8 -6
- package/dist/boot/stage-06-runners.d.ts +6 -3
- package/dist/boot/stage-06-runners.js +3 -4
- package/dist/boot/stage-07-capability-layer.d.ts +6 -3
- package/dist/boot/stage-07-capability-layer.js +7 -2
- package/dist/boot/stage-08-reapers.d.ts +6 -3
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +6 -3
- package/dist/boot/stage-10-http-server.d.ts +6 -3
- package/dist/boot/stage-10-http-server.js +14 -5
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +10 -9
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +48 -36
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +11 -1
- package/dist/capabilities/scenarios.js +1 -0
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +31 -7
- package/dist/capability-facts.js +27 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +2 -1
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +19 -8
- package/dist/config.d.ts +10 -3
- package/dist/config.js +18 -4
- package/dist/delegated-session-owner.d.ts +5 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/execution-lane-caps.d.ts +2 -1
- package/dist/fleet/fleet-bus.d.ts +34 -55
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +58 -8
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/dispatch.js +4 -2
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +76 -58
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +142 -31
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +16 -4
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +27 -7
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +12 -11
- package/dist/http/routes/session-memory-sync.d.ts +91 -0
- package/dist/http/routes/session-memory-sync.js +174 -0
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +34 -54
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +9 -3
- package/dist/http/send.d.ts +5 -0
- package/dist/http/send.js +9 -2
- package/dist/http/server.d.ts +27 -15
- package/dist/http/server.js +4 -6
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.d.ts +21 -21
- package/dist/memory-sync-client.js +62 -43
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +27 -5
- package/dist/memory-sync.js +29 -7
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/fail-open.d.ts +47 -14
- package/dist/observability/fail-open.js +37 -11
- package/dist/observability/run-terminal.d.ts +92 -34
- package/dist/observability/run-terminal.js +171 -162
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +127 -0
- package/dist/peer-inbound-face.js +158 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +64 -11
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +17 -14
- package/dist/plugins/approval-ask-store-sql.d.ts +70 -23
- package/dist/plugins/approval-ask-store-sql.js +79 -30
- package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
- package/dist/plugins/checkpoint-store-sql.js +9 -7
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +35 -1
- package/dist/plugins/file-run-store.js +108 -9
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.js +2 -2
- package/dist/plugins/local-session-store.d.ts +42 -9
- package/dist/plugins/local-session-store.js +83 -33
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +11 -1
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +58 -2
- package/dist/plugins/one-time-migrations.js +161 -4
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-e2b.d.ts +23 -4
- package/dist/plugins/remote-env-e2b.js +111 -28
- package/dist/plugins/remote-env-k8s.d.ts +61 -22
- package/dist/plugins/remote-env-k8s.js +133 -33
- package/dist/plugins/remote-env-local-docker.d.ts +15 -3
- package/dist/plugins/remote-env-local-docker.js +40 -12
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/run-store-sql.d.ts +9 -1
- package/dist/plugins/run-store-sql.js +9 -1
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/store-backend.d.ts +33 -35
- package/dist/plugins/store-backend.js +10 -1
- package/dist/plugins/store-contracts.d.ts +50 -1
- package/dist/plugins/store-contracts.js +6 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +207 -29
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +11 -13
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +6 -8
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +9 -0
- package/dist/security.js +8 -0
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +36 -0
- package/dist/session-error-wire.js +13 -0
- package/dist/session-incarnation.d.ts +1 -1
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +93 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +21 -10
- package/dist/task-settings.js +46 -13
- package/dist/terminal.d.ts +39 -19
- package/dist/terminal.js +65 -22
- package/dist/tool-approval.d.ts +56 -37
- package/dist/tool-approval.js +78 -71
- package/dist/trace/core-keyset-guard.d.ts +134 -14
- package/dist/trace/engine-notice-wire.d.ts +9 -3
- package/dist/trace/engine-notice-wire.js +7 -3
- package/dist/trace/exit-seat.d.ts +9 -2
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +10 -6
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/model-segment.d.ts +55 -25
- package/dist/trace/model-segment.js +101 -33
- package/dist/trace/project.d.ts +25 -5
- package/dist/trace/project.js +22 -0
- package/dist/trace/redact.d.ts +61 -9
- package/dist/trace/redact.js +1490 -468
- package/dist/trace/self-minted-wire.d.ts +745 -24
- package/dist/trace/self-minted-wire.js +239 -9
- package/dist/trace/wire-projection-faces.d.ts +2 -2
- package/dist/trace/wire-projection-faces.js +2 -2
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +109 -9
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/types.d.ts +4 -0
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.142.0.json +639 -0
- package/wire-changes/server@7.143.0.json +1167 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { memorySyncReportOf } from "@sema-agent/core";
|
|
2
|
+
import { sendJson, sendError } from "../send.js";
|
|
3
|
+
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
|
+
import {} from "../route-ctx.js";
|
|
5
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
6
|
+
import * as facts from "../../capability-facts.js";
|
|
7
|
+
import { sessionOwnerCellOf } from "../../security.js";
|
|
8
|
+
import { leafOfAdmittedHead } from "../../session-incarnation.js";
|
|
9
|
+
import { declaredMemorySyncIdentityFlaw, memorySyncIdentityRule } from "../../memory-sync-identity.js";
|
|
10
|
+
import { MEMORY_OBSERVATION_ABSENT_REASONS } from "../../plugins/store-contracts.js";
|
|
11
|
+
export const SESSION_ACTIVE_SYNC_MESSAGE = "a run in this session is in flight, or the session moved on after the memory it reads was observed (a run was admitted or its transcript advanced) — wait for it to finish on its own, then sync again (do not cancel it to sync sooner)";
|
|
12
|
+
export const SESSION_MEMORY_UNOBSERVED_REASONS = ["no_settled_run", ...MEMORY_OBSERVATION_ABSENT_REASONS, "memory_not_mounted", "no_leaf"];
|
|
13
|
+
const LATEST_RUN_UNOBSERVED_MESSAGE = "the latest settled run in this session has no memory observation on record, and an earlier run is never read in its place — run another task in this session (if that run was refused, fix what refused it first), then sync";
|
|
14
|
+
export const SESSION_MEMORY_UNOBSERVED_MESSAGES = {
|
|
15
|
+
no_settled_run: "this session has no settled run with a memory observation yet, so there is nothing to say about which memory it reads — run a task in it (or let the run in flight finish), then sync",
|
|
16
|
+
run_result_missing: LATEST_RUN_UNOBSERVED_MESSAGE,
|
|
17
|
+
run_result_unobserved: LATEST_RUN_UNOBSERVED_MESSAGE,
|
|
18
|
+
memory_not_mounted: "memory was configured for the latest settled run in this session but did not mount, so which memory this session reads is unknown, and an earlier run is never read in its place — run another task in this session (if its memory fails to mount again, fix that first), then sync",
|
|
19
|
+
no_leaf: "this session has no transcript yet, so there is no revision to sync from — run a task in it (or let the run in flight finish), then sync",
|
|
20
|
+
};
|
|
21
|
+
export function sessionReadSetOf(observed) {
|
|
22
|
+
if (observed === undefined)
|
|
23
|
+
return { kind: "unobserved", reason: "no_settled_run" };
|
|
24
|
+
const { taskId, observation } = observed;
|
|
25
|
+
switch (observation.state) {
|
|
26
|
+
case "mounted":
|
|
27
|
+
return { kind: "read", taskId, scopes: observation.scopes.map((s) => s.scope) };
|
|
28
|
+
case "none":
|
|
29
|
+
return { kind: "read", taskId, scopes: [] };
|
|
30
|
+
case "memoryless":
|
|
31
|
+
return { kind: "unobserved", reason: "memory_not_mounted" };
|
|
32
|
+
case "absent":
|
|
33
|
+
return { kind: "unobserved", reason: observation.reason };
|
|
34
|
+
default:
|
|
35
|
+
return assertNever(observation);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
class ReportMintRefused extends Error {
|
|
39
|
+
}
|
|
40
|
+
function reportOf(round) {
|
|
41
|
+
try {
|
|
42
|
+
return memorySyncReportOf(round);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof TypeError)
|
|
46
|
+
throw new ReportMintRefused(err.message);
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const inflightByLeg = new WeakMap();
|
|
51
|
+
function mergedRound(leg, key, run) {
|
|
52
|
+
let table = inflightByLeg.get(leg);
|
|
53
|
+
if (table === undefined) {
|
|
54
|
+
table = new Map();
|
|
55
|
+
inflightByLeg.set(leg, table);
|
|
56
|
+
}
|
|
57
|
+
const inflight = table.get(key);
|
|
58
|
+
if (inflight !== undefined)
|
|
59
|
+
return inflight;
|
|
60
|
+
const owner = table;
|
|
61
|
+
const round = run().finally(() => {
|
|
62
|
+
if (owner.get(key) === round)
|
|
63
|
+
owner.delete(key);
|
|
64
|
+
});
|
|
65
|
+
table.set(key, round);
|
|
66
|
+
return round;
|
|
67
|
+
}
|
|
68
|
+
export async function handleSessionMemorySync(req, res, match, ctx) {
|
|
69
|
+
const { deps } = ctx;
|
|
70
|
+
switch (match.decl.id) {
|
|
71
|
+
case "session-memory-sync": {
|
|
72
|
+
const fact = facts.sessionMemorySync(deps);
|
|
73
|
+
if (!fact.available) {
|
|
74
|
+
switch (fact.why) {
|
|
75
|
+
case "memory_sync_leg_absent":
|
|
76
|
+
sendError(res, 501, "feature.memory_sync_disabled", "session memory sync is not available on this deployment — it runs only where a file memory engine syncs to a center (MEMORY_SYNC_URL)");
|
|
77
|
+
return;
|
|
78
|
+
case "run_store_absent":
|
|
79
|
+
sendError(res, 501, "capability.run_store_required", "session memory sync requires a durable run store (DB_BACKEND=mysql|pg|local) — the session's read set is the memory its latest settled run observed");
|
|
80
|
+
return;
|
|
81
|
+
case "session_head_face_absent":
|
|
82
|
+
sendError(res, 501, "capability.session_ownership_required", "session memory sync requires the session-ownership face (sessionStorage.headOf)");
|
|
83
|
+
return;
|
|
84
|
+
default:
|
|
85
|
+
return assertNever(fact.why);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const { leg, runStore, sessionStorage } = fact;
|
|
89
|
+
if (deps.config.requirePrincipal && gatedPrincipal(req, deps.config) === undefined) {
|
|
90
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (ctx.helpers.rateLimited(req, res))
|
|
94
|
+
return;
|
|
95
|
+
const sessionId = ctx.helpers.safeDecode(match.params.sessionId);
|
|
96
|
+
if (sessionId === null) {
|
|
97
|
+
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const readHead = () => sessionStorage.headOf(sessionId);
|
|
101
|
+
const head = await readHead();
|
|
102
|
+
if (head === undefined) {
|
|
103
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!ctx.helpers.sessionOwnerOk(req, res, await sessionOwnerCellOf(sessionStorage, sessionId, head.owner)))
|
|
107
|
+
return;
|
|
108
|
+
const sourceRevision = leafOfAdmittedHead(head);
|
|
109
|
+
const body = await ctx.helpers.readJson(req);
|
|
110
|
+
if (body === null || typeof body !== "object" || Array.isArray(body) || Object.keys(body).some((k) => k !== "target") || typeof body.target !== "string") {
|
|
111
|
+
sendError(res, 400, "request.body_shape", "body must be { target: string } — the sync identity the target engine publishes (GET /v1/capabilities → memorySyncServe.identity), verbatim; no other key");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const target = body.target;
|
|
115
|
+
const flaw = declaredMemorySyncIdentityFlaw(target);
|
|
116
|
+
if (flaw !== undefined) {
|
|
117
|
+
sendError(res, 400, "request.field_invalid", `target ${memorySyncIdentityRule(flaw)} — send the target engine's memorySyncServe.identity exactly as GET /v1/capabilities publishes it`, { field: "target", reason: flaw });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const readSet = sessionReadSetOf(await runStore.latestMemoryObservationForSession(sessionId));
|
|
121
|
+
if (readSet.kind === "unobserved" || sourceRevision === null) {
|
|
122
|
+
const reason = readSet.kind === "unobserved" ? readSet.reason : "no_leaf";
|
|
123
|
+
const activeTaskId = await runStore.getActiveTaskId(sessionId);
|
|
124
|
+
sendError(res, 409, "conflict.session_memory_unobserved", SESSION_MEMORY_UNOBSERVED_MESSAGES[reason], { reason, ...(activeTaskId !== undefined ? { activeTaskId } : {}) });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const { taskId: observedTaskId, scopes } = readSet;
|
|
128
|
+
const quiet = async () => {
|
|
129
|
+
if ((await runStore.getActiveTaskId(sessionId)) !== undefined)
|
|
130
|
+
return false;
|
|
131
|
+
if ((await runStore.latestRunForSession(sessionId))?.taskId !== observedTaskId)
|
|
132
|
+
return false;
|
|
133
|
+
const now = await readHead();
|
|
134
|
+
return now !== undefined && leafOfAdmittedHead(now) === sourceRevision;
|
|
135
|
+
};
|
|
136
|
+
const fenced = () => reportOf({ target, sourceRevision, scopes, refused: { code: "session_active", message: SESSION_ACTIVE_SYNC_MESSAGE } });
|
|
137
|
+
let report;
|
|
138
|
+
try {
|
|
139
|
+
if (target !== leg.peer) {
|
|
140
|
+
report = reportOf({ target, sourceRevision, scopes, refused: { code: "config.memory_sync_target_mismatch", reaches: leg.peer } });
|
|
141
|
+
}
|
|
142
|
+
else if (!(await quiet())) {
|
|
143
|
+
report = fenced();
|
|
144
|
+
}
|
|
145
|
+
else if (scopes.length === 0) {
|
|
146
|
+
report = reportOf({ target, sourceRevision, scopes, layers: [] });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
report = await mergedRound(leg, JSON.stringify([sessionId, sourceRevision, observedTaskId, target]), async () => {
|
|
150
|
+
const round = await leg.syncOnce("session-trigger", { reading: scopes });
|
|
151
|
+
if (!(await quiet()))
|
|
152
|
+
return fenced();
|
|
153
|
+
return reportOf({ target, sourceRevision, scopes, layers: round.calls.filter((c) => scopes.includes(c.scope)) });
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
if (!(err instanceof ReportMintRefused))
|
|
159
|
+
throw err;
|
|
160
|
+
deps.logger?.warn?.("session_memory_sync_report_refused", { sessionId, err: err.message });
|
|
161
|
+
sendError(res, 500, "internal.error", "memory sync failed");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
sendJson(res, 200, report);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
return assertNever(match.decl.id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export const SESSION_MEMORY_SYNC_ROUTES = [
|
|
172
|
+
{ id: "session-memory-sync", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/memory\/sync$/, label: "/v1/sessions/:id/memory/sync", methods: ["POST"] },
|
|
173
|
+
];
|
|
174
|
+
//# sourceMappingURL=session-memory-sync.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { once } from "node:events";
|
|
2
|
-
import { randomBytes
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
3
|
import { sessionLogDigest, sessionLogDigestsComparable, StreamingImportValidator, validateFileHistoryExport } from "@sema-agent/core";
|
|
4
4
|
import { StringDecoder } from "node:string_decoder";
|
|
5
5
|
import { FileHistoryUnreadableError, blobGetterOf, exportSessionManifest, exportedFileHistory, fileHistoryImportFace, replayFileHistory, replayPolicyRecords, wipeSessionAttendants } from "../../session-sync.js";
|
|
@@ -222,6 +222,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
222
222
|
return;
|
|
223
223
|
}
|
|
224
224
|
const fs = blobs.historyStore;
|
|
225
|
+
const exchange = blobs.blobExchange;
|
|
225
226
|
let envelope;
|
|
226
227
|
try {
|
|
227
228
|
envelope = await exportedFileHistory(fs, sessionId);
|
|
@@ -238,7 +239,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
238
239
|
}
|
|
239
240
|
let bytes;
|
|
240
241
|
try {
|
|
241
|
-
bytes = await
|
|
242
|
+
bytes = await exchange.get(hash);
|
|
242
243
|
}
|
|
243
244
|
catch (e) {
|
|
244
245
|
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
@@ -268,16 +269,15 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
268
269
|
sendError(res, 400, "request.id_invalid", "blob hash must be 64 lowercase-hex chars");
|
|
269
270
|
return;
|
|
270
271
|
}
|
|
271
|
-
const fs = blobs.historyStore;
|
|
272
272
|
const body = await readRawBody(req, SYNC_BLOB_MAX_BYTES);
|
|
273
|
-
|
|
274
|
-
sendError(res, 400, "request.hash_mismatch", "blob hash mismatch");
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
const put = await fs.putBlob(hash, new Uint8Array(body));
|
|
273
|
+
const put = await blobs.blobExchange.put(hash, new Uint8Array(body));
|
|
278
274
|
if (!put.ok) {
|
|
279
|
-
if (put.error.
|
|
280
|
-
sendError(res, 413, "blob_too_large_for_sql", put.error.message, { wallSource: put.error.wallSource });
|
|
275
|
+
if (put.error.blobCap !== undefined) {
|
|
276
|
+
sendError(res, 413, "blob_too_large_for_sql", put.error.message, { wallSource: put.error.blobCap.wallSource });
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (put.error.code === "adopt_failed") {
|
|
280
|
+
sendError(res, 400, "request.hash_mismatch", "blob hash mismatch");
|
|
281
281
|
return;
|
|
282
282
|
}
|
|
283
283
|
sendError(res, 502, put.error.code, "blob store write failed");
|
|
@@ -751,15 +751,8 @@ async function firstMissingBlob(blobFaces, referenced) {
|
|
|
751
751
|
return undefined;
|
|
752
752
|
if (!blobFaces.available)
|
|
753
753
|
return referenced[0];
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
const present = await store.hasBlobs([...referenced]);
|
|
757
|
-
return referenced.find((h) => !present.has(h));
|
|
758
|
-
}
|
|
759
|
-
for (const hash of referenced)
|
|
760
|
-
if (!(await store.getBlob(hash)))
|
|
761
|
-
return hash;
|
|
762
|
-
return undefined;
|
|
754
|
+
const present = await blobFaces.blobExchange.has(referenced);
|
|
755
|
+
return referenced.find((h) => !present.has(h));
|
|
763
756
|
}
|
|
764
757
|
function sendSessionSyncUnavailable(res, why) {
|
|
765
758
|
switch (why) {
|
|
@@ -779,7 +772,7 @@ function sendSessionSyncUnavailable(res, why) {
|
|
|
779
772
|
case "policy_purge_absent":
|
|
780
773
|
sendError(res, 501, "capability.policy_purge_required", "session sync is not available on this deployment: the session-policy store has no deleteBySession purge verb, so an overwrite-dst import could not wipe the destination's stale policy rows. Every in-tree policy store carries it since core 1.423 — upgrade the policy store");
|
|
781
774
|
return;
|
|
782
|
-
case "
|
|
775
|
+
case "blob_exchange_absent":
|
|
783
776
|
sendError(res, 501, "capability.snapshot_store_required", "file-history blob transfer is not available on this deployment: its file-history store has no blob faces (SQL backends only; capabilities.sessionSyncFileHistory is false here). Sync the conversation without fileHistory");
|
|
784
777
|
return;
|
|
785
778
|
default:
|
|
@@ -16,7 +16,8 @@ export declare function handleSessionsList(req: IncomingMessage, res: ServerResp
|
|
|
16
16
|
* 会话列表行的 **wire 投影** —— `GET /v1/sessions` 的每一行与 `PATCH /v1/sessions/:id` 的 200 回执是**同一只**(店行 → wire 行只有这一处)。
|
|
17
17
|
* · 预览:店行读的是 run 行预览列的事实位(`""` = 有记录、剥完没有用户正文;NULL = 没记录);本面只说「有没有可显示的预览」⇒ 两者都是 `null`。
|
|
18
18
|
* · `tempo`:见上方 [ref] 注(由 `lastStatus` 经唯一一张表派生;omit-when-absent)。
|
|
19
|
-
* · 其余键(含 `title` / `pinned`)零投影透传 ——
|
|
19
|
+
* · 其余键(含 `title` / `pinned` / `rowTag` / `unreadable`)零投影透传 —— 店行是唯一铸点(`unreadable` 的码出自 `session-error-wire.ts` 的那一张表,
|
|
20
|
+
* 与全局兜底对同一只会话答的码同读;SQL 孪生的叶子是行上一列 ⇒ 恒缺席)。
|
|
20
21
|
*/
|
|
21
22
|
export declare function projectSessionListRow(r: SessionListItem): SessionListItem & {
|
|
22
23
|
tempo?: ReturnType<typeof sessionTempoOfRunStatus>;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `sse.sseConnections` / `sse.sseOwnerLookups`——`let` 无法跨模块共享自增。判据/时机/配对释放逐字未动。
|
|
13
13
|
*/
|
|
14
14
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
15
|
-
import {
|
|
15
|
+
import type { SessionMemoryView } from "../../memory-operator-faces.js";
|
|
16
16
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
17
17
|
/** 单域独占状态。每个 server 实例一份——模块级会把连接坑/缓存跨实例串味。 */
|
|
18
18
|
export interface SessionsLocal {
|
|
@@ -42,7 +42,7 @@ export interface SessionsLocal {
|
|
|
42
42
|
* mcp/a2a 按 (scenario, principal) 分键是因为它们的面按人裁,这张面不按人裁,分键只会白扩表)。 */
|
|
43
43
|
memoryStatusCache: Map<string, {
|
|
44
44
|
at: number;
|
|
45
|
-
status: Promise<
|
|
45
|
+
status: Promise<SessionMemoryView>;
|
|
46
46
|
}>;
|
|
47
47
|
}
|
|
48
48
|
export declare function createSessionsLocal(): SessionsLocal;
|
|
@@ -8,13 +8,12 @@ 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
10
|
import { readLayerSeat, sentMemoryLayerRefusal } from "../memory-layer-reply.js";
|
|
11
|
-
import { foldPlaneStatuses } from "../../memory-operator-faces.js";
|
|
12
11
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
13
12
|
import { readWritableSessionPermissionRules } from "../../session-policy-wire.js";
|
|
14
13
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
|
|
15
14
|
import { fleetViewOf, gatedPrincipal, explicitOperatorOk, sessionCallerOf, sessionOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
|
|
16
15
|
import { headReadOf, leafOfAdmittedHead, sessionFaceOfHead } from "../../session-incarnation.js";
|
|
17
|
-
import { runRowReadableOnSessionFace } from "../owner-access.js";
|
|
16
|
+
import { foldOwnerUnreadable, runRowReadableOnSessionFace } from "../owner-access.js";
|
|
18
17
|
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
19
18
|
import * as facts from "../../capability-facts.js";
|
|
20
19
|
import { rosterAgentId } from "./agents-roster.js";
|
|
@@ -84,7 +83,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
84
83
|
const readOwnedSessionView = async (sessionAudit, sessionId, readOpts) => {
|
|
85
84
|
const headOf = headReadOf(deps.sessionStorage);
|
|
86
85
|
if (headOf !== undefined) {
|
|
87
|
-
const owner = (await headOf(sessionId))?.owner;
|
|
86
|
+
const owner = (await foldOwnerUnreadable(deps.logger, match.decl.id, sessionId, () => headOf(sessionId)))?.owner;
|
|
88
87
|
if (owner === undefined) {
|
|
89
88
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
90
89
|
return undefined;
|
|
@@ -274,7 +273,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
274
273
|
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
275
274
|
return;
|
|
276
275
|
}
|
|
277
|
-
const head = await headOf(headSession);
|
|
276
|
+
const head = await foldOwnerUnreadable(deps.logger, match.decl.id, headSession, () => headOf(headSession));
|
|
278
277
|
if (head === undefined) {
|
|
279
278
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
280
279
|
return;
|
|
@@ -304,7 +303,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
304
303
|
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
305
304
|
return;
|
|
306
305
|
}
|
|
307
|
-
const reading = await placementFact.seat.read(placementSession);
|
|
306
|
+
const reading = await foldOwnerUnreadable(deps.logger, match.decl.id, placementSession, () => placementFact.seat.read(placementSession));
|
|
308
307
|
if (reading === undefined) {
|
|
309
308
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
310
309
|
return;
|
|
@@ -322,7 +321,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
322
321
|
sendError(res, 501, "capability.session_store_required", "session events require a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
323
322
|
return;
|
|
324
323
|
}
|
|
325
|
-
const readHead = (id) => evFact.sessionStorage.headOf(id);
|
|
324
|
+
const readHead = (id) => foldOwnerUnreadable(deps.logger, match.decl.id, id, () => evFact.sessionStorage.headOf(id));
|
|
326
325
|
const readOwner = async (id) => (await readHead(id))?.owner;
|
|
327
326
|
const watch = evFact.watch;
|
|
328
327
|
const evSession = safeDecode(match.params.sessionId);
|
|
@@ -541,6 +540,10 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
541
540
|
sendError(res, 400, "request.body_shape", "invalid session memory erase body — expected { requestId: string, allowUnevidenced?: boolean } (no `select`: this face erases exactly this session's own contributions)");
|
|
542
541
|
return;
|
|
543
542
|
}
|
|
543
|
+
if ("scope" in raw) {
|
|
544
|
+
sendError(res, 400, "request.body_shape", "invalid session memory erase body — `scope` is not accepted: this face erases this session's own contributions in every memory store of the deployment, so there is no layer to name; resend without `scope`");
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
544
547
|
if ("select" in raw) {
|
|
545
548
|
sendError(res, 400, "request.body_shape", "invalid session memory erase body — `select` is not accepted on the self-service face: the selector is fixed to { sessionId: <this session> } by the path; the operator face POST /v1/memory/erase takes explicit selectors");
|
|
546
549
|
return;
|
|
@@ -553,17 +556,12 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
553
556
|
sendError(res, 400, "request.field_invalid", "allowUnevidenced must be a boolean when present");
|
|
554
557
|
return;
|
|
555
558
|
}
|
|
556
|
-
const at = readLayerSeat(res, Object.prototype.hasOwnProperty.call(raw, "scope"), raw.scope);
|
|
557
|
-
if (at === null)
|
|
558
|
-
return;
|
|
559
559
|
const withheld = (coreMessage) => deps.logger?.warn?.("session_memory_erase_refused", { sessionId, err: coreMessage });
|
|
560
560
|
try {
|
|
561
561
|
const input = { requestId: raw.requestId, select: { sessionId }, ...(raw.allowUnevidenced !== undefined ? { allowUnevidenced: raw.allowUnevidenced } : {}) };
|
|
562
|
-
sendJson(res, 200, await
|
|
562
|
+
sendJson(res, 200, await faces.erase(input));
|
|
563
563
|
}
|
|
564
564
|
catch (err) {
|
|
565
|
-
if (sentMemoryLayerRefusal(res, err, { kind: "session_owner", withheld }))
|
|
566
|
-
return;
|
|
567
565
|
if (sentMemoryComplianceFailure(res, err, { kind: "session_owner", withheld }, deps.logger))
|
|
568
566
|
return;
|
|
569
567
|
deps.logger?.warn?.("session_memory_erase_failed", { sessionId, err: String(err) });
|
|
@@ -601,21 +599,22 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
601
599
|
return;
|
|
602
600
|
try {
|
|
603
601
|
const { memoryStatusCache } = ctx.local.sessions;
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
602
|
+
const reading = face.readingAt(at);
|
|
603
|
+
const cacheKey = JSON.stringify([sessionId, reading.key]);
|
|
604
|
+
const cached = memoryStatusCache.get(cacheKey);
|
|
605
|
+
let pending;
|
|
606
|
+
if (cached && Date.now() - cached.at <= MEMORY_STATUS_TTL_MS)
|
|
607
|
+
pending = cached.status;
|
|
608
|
+
else {
|
|
609
609
|
if (memoryStatusCache.size > 1024)
|
|
610
610
|
memoryStatusCache.clear();
|
|
611
|
-
|
|
612
|
-
const fresh = { at: Date.now(), status };
|
|
611
|
+
pending = reading.status(sessionId);
|
|
612
|
+
const fresh = { at: Date.now(), status: pending };
|
|
613
613
|
memoryStatusCache.set(cacheKey, fresh);
|
|
614
|
-
|
|
614
|
+
pending.catch(() => { if (memoryStatusCache.get(cacheKey) === fresh)
|
|
615
615
|
memoryStatusCache.delete(cacheKey); });
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const s = foldPlaneStatuses(await Promise.all(face.storesAt(at).map(statusOf)));
|
|
616
|
+
}
|
|
617
|
+
const s = await pending;
|
|
619
618
|
sendJson(res, 200, {
|
|
620
619
|
sessionId,
|
|
621
620
|
autoConsolidationArmed: deps.memoryAutoConsolidationArmed,
|
|
@@ -5,18 +5,18 @@ import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpS
|
|
|
5
5
|
import { atHostBoundary, sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
6
6
|
import { voidVerifierPauseCard } from "../../verifier-pause-card.js";
|
|
7
7
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
8
|
-
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals
|
|
8
|
+
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals } from "../../fleet/fleet-bus.js";
|
|
9
9
|
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
10
|
-
import {
|
|
10
|
+
import { createLiveModelText, modelStreamOfTask } from "../../trace/model-segment.js";
|
|
11
11
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
|
|
12
|
-
import { createLedgerSink, createLedgerTwinEmit } from "../../trace/ledger-sink.js";
|
|
12
|
+
import { createForwardAppendFailedNote, createLedgerSink, createLedgerTwinEmit } from "../../trace/ledger-sink.js";
|
|
13
13
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
14
14
|
import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
|
|
15
15
|
import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../../run-cancel-context.js";
|
|
16
16
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
17
17
|
import { redactSecrets } from "../../trace/redact.js";
|
|
18
18
|
import { writeLiveFrame, writeLiveHeartbeat } from "../live-frame.js";
|
|
19
|
-
import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage,
|
|
19
|
+
import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, liveIdentityFields } from "../../trace/project.js";
|
|
20
20
|
import { cascadeConfig, runMeta } from "../run-meta.js";
|
|
21
21
|
import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
|
|
22
22
|
import { replayOfExistingRun } from "../submit-replay.js";
|
|
@@ -36,12 +36,11 @@ import * as facts from "../../capability-facts.js";
|
|
|
36
36
|
import { persistSubmitCtx } from "../submit-ctx.js";
|
|
37
37
|
import { serviceVersion } from "../../service-version.js";
|
|
38
38
|
import { withModelReachScope } from "../../model-reach-scope.js";
|
|
39
|
-
import { runSkillsOf } from "../../capabilities/skills.js";
|
|
40
39
|
import { createInstructionSelectionLeg } from "../../instruction-selection.js";
|
|
41
40
|
import { fieldConflictFacts } from "../../field-conflict.js";
|
|
42
41
|
import { refusalBody } from "../refusal-body.js";
|
|
43
42
|
import { heldWorkflowParkDecisions } from "../../parked-decide.js";
|
|
44
|
-
import { createSessionWorkspaceRecorder,
|
|
43
|
+
import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../../session-workspace.js";
|
|
45
44
|
const DISCONNECT_PARK_GRACE_MS = 10_000;
|
|
46
45
|
async function mintSubmitExitResult(result, checkpoints) {
|
|
47
46
|
return redactTerminalResult(sealResultForExit(await graftParkToolCallId(result, checkpoints)));
|
|
@@ -166,7 +165,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
166
165
|
const wiringOperatorFace = explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config);
|
|
167
166
|
let wiringReceipt;
|
|
168
167
|
const instructionLeg = createInstructionSelectionLeg();
|
|
169
|
-
const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills:
|
|
168
|
+
const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: prepared.legFacts?.skills });
|
|
170
169
|
const withRunReceipt = (out) => wiringReceipt !== undefined ? { ...out, wiringManifest: wiringReceipt } : out;
|
|
171
170
|
if (liveStream) {
|
|
172
171
|
if (prepared.verify || prepared.cascade) {
|
|
@@ -342,7 +341,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
342
341
|
if (durableTaskId && deps.runStore) {
|
|
343
342
|
const rs = deps.runStore;
|
|
344
343
|
const tid = durableTaskId;
|
|
345
|
-
ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking, onActivity: (kind, statusData) => recordTurnActivity(tid, kind, statusData), legSections: legWiringSections });
|
|
344
|
+
ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking, onActivity: (kind, statusData) => recordTurnActivity(tid, kind, statusData), legSections: legWiringSections, usage: { modelUsage: deps.modelUsage, taskId: tid } });
|
|
346
345
|
usageKey = tid;
|
|
347
346
|
deps.modelUsage?.register(tid);
|
|
348
347
|
const owner = principal ?? null;
|
|
@@ -400,7 +399,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
400
399
|
...(prepared.engineNotices !== undefined ? { pending: prepared.engineNotices } : {}),
|
|
401
400
|
live: (row) => {
|
|
402
401
|
if (res.writableEnded || res.destroyed) {
|
|
403
|
-
|
|
402
|
+
if (noticeLedger === undefined)
|
|
403
|
+
recordFailOpen(failOpenTagForDroppedFrame("engine_notice"), "frame=engine_notice");
|
|
404
404
|
return;
|
|
405
405
|
}
|
|
406
406
|
writeLiveFrame(res, { type: "engine_notice", ...row });
|
|
@@ -417,8 +417,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
417
417
|
const streamBody = async () => {
|
|
418
418
|
await withPrincipal(principal, async () => {
|
|
419
419
|
const tailContentMode = prepared.spec.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
420
|
-
const
|
|
421
|
-
const
|
|
420
|
+
const childText = createLiveModelText();
|
|
421
|
+
const topText = createLiveModelText();
|
|
422
422
|
const heldDecisions = await heldWorkflowParkDecisions(deps, prepared.spec.sessionId);
|
|
423
423
|
const fwdInternals = {
|
|
424
424
|
...heldDecisions,
|
|
@@ -432,8 +432,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
432
432
|
}
|
|
433
433
|
if (res.writableEnded)
|
|
434
434
|
return;
|
|
435
|
-
for (const
|
|
436
|
-
writeLiveFrame(res,
|
|
435
|
+
for (const f of childText.onEvent(e))
|
|
436
|
+
writeLiveFrame(res, f);
|
|
437
437
|
const t = e.type;
|
|
438
438
|
if (t === "task_progress") {
|
|
439
439
|
writeLiveFrame(res, { type: "task_progress", ...taskProgressEventData(e) });
|
|
@@ -449,34 +449,20 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
449
449
|
const te = e;
|
|
450
450
|
writeLiveFrame(res, { type: "tool_end", ...toolEndEventData(te) });
|
|
451
451
|
}
|
|
452
|
-
else if (t === "text_delta") {
|
|
453
|
-
const td = e;
|
|
454
|
-
writeLiveFrame(res, { type: "text_delta", delta: td.delta, ...(td.eventId ? { eventId: td.eventId } : {}), ...(td.parentToolCallId ? { parentToolCallId: td.parentToolCallId } : {}) });
|
|
455
|
-
}
|
|
456
|
-
else if (t === "text_end") {
|
|
457
|
-
const tend = textEndEventData(e);
|
|
458
|
-
if (tend !== null)
|
|
459
|
-
writeLiveFrame(res, { type: "text_end", ...tend });
|
|
460
|
-
}
|
|
461
|
-
else if (t === "reasoning_delta") {
|
|
462
|
-
const rd = e;
|
|
463
|
-
writeLiveFrame(res, { type: "reasoning_delta", delta: redactSecrets(rd.delta), ...(rd.eventId ? { eventId: rd.eventId } : {}), ...(rd.parentToolCallId ? { parentToolCallId: rd.parentToolCallId } : {}) });
|
|
464
|
-
}
|
|
465
452
|
},
|
|
466
453
|
onTaskNotification: (n, opts) => {
|
|
467
454
|
if (n.task_type === "workflow")
|
|
468
455
|
return;
|
|
469
456
|
if (!res.writableEnded)
|
|
470
|
-
for (const
|
|
471
|
-
writeLiveFrame(res,
|
|
457
|
+
for (const f of childText.cut(modelStreamOfTask(n.task_id)))
|
|
458
|
+
writeLiveFrame(res, f);
|
|
472
459
|
defaultSubagentTailBus.forgetHandle(n.task_id);
|
|
473
460
|
if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
|
|
474
461
|
defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
|
|
475
462
|
}
|
|
476
|
-
const hadRow = isFleetAgentTerminalNotification(n) ? (fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false) : false;
|
|
477
463
|
const deliverable = syncLegLive && !res.writableEnded && !res.destroyed;
|
|
478
464
|
const parked = !deliverable && deps.workflowCompletionInbox !== undefined;
|
|
479
|
-
deps.logger?.info?.("task_notification_observed", { route: "sync-stream", taskId: n.task_id, taskType: n.task_type, status: n.status,
|
|
465
|
+
deps.logger?.info?.("task_notification_observed", { route: "sync-stream", taskId: n.task_id, taskType: n.task_type, status: n.status, legLive: syncLegLive, parkedDurable: parked });
|
|
480
466
|
if (parked) {
|
|
481
467
|
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) }));
|
|
482
468
|
}
|
|
@@ -502,12 +488,12 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
502
488
|
if (durableTaskId)
|
|
503
489
|
steerableRuns.set(durableTaskId, liveStream);
|
|
504
490
|
prepared.acceptance.attachStream({ reachable: () => !closed && !res.writableEnded && !res.destroyed, write: (frame) => writeLiveFrame(res, { ...frame }) });
|
|
505
|
-
const liveWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage),
|
|
491
|
+
const liveWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), prepared.legFacts?.sessionWitness);
|
|
506
492
|
for await (const ev of liveStream) {
|
|
507
493
|
if (closed)
|
|
508
494
|
break;
|
|
509
|
-
for (const
|
|
510
|
-
writeLiveFrame(res,
|
|
495
|
+
for (const f of ev.type === "done" ? [...childText.cut(), ...topText.cut()] : topText.onEvent(ev))
|
|
496
|
+
writeLiveFrame(res, f);
|
|
511
497
|
const promptEntryId = promptCommit(ev);
|
|
512
498
|
if (promptEntryId !== undefined)
|
|
513
499
|
userMsgEntryId = promptEntryId;
|
|
@@ -568,8 +554,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
568
554
|
writeLiveFrame(res, { type: "task_notification", ...taskNotificationEventData(ev) });
|
|
569
555
|
}
|
|
570
556
|
}
|
|
571
|
-
else if (ev.type === "
|
|
572
|
-
writeLiveFrame(res, { type: "reasoning_delta", delta: redactSecrets(ev.delta), ...(ev.eventId ? { eventId: ev.eventId } : {}), ...(ev.parentToolCallId ? { parentToolCallId: ev.parentToolCallId } : {}) });
|
|
557
|
+
else if (ev.type === "text_delta" || ev.type === "text_end" || ev.type === "reasoning_delta") {
|
|
573
558
|
}
|
|
574
559
|
else if (ev.type === "task_progress") {
|
|
575
560
|
writeLiveFrame(res, { type: "task_progress", ...taskProgressEventData(ev) });
|
|
@@ -618,25 +603,19 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
618
603
|
else if (ev.type === "human_input") {
|
|
619
604
|
writeLiveFrame(res, { type: "human_input", ...humanInputEventData(ev) });
|
|
620
605
|
}
|
|
621
|
-
else if (ev.type === "
|
|
606
|
+
else if (ev.type === "turn_end" || ev.type === "message_committed" || ev.type === "context_usage") {
|
|
622
607
|
const e = ev;
|
|
623
608
|
const ident = liveIdentityFields(e);
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
: ev.type === "turn_end"
|
|
631
|
-
? { type: "turn_end", ...turnEndEventData(ev), ...ident }
|
|
632
|
-
: ev.type === "message_committed"
|
|
633
|
-
? { type: "message_committed", entryId: e.entryId, role: e.role, ...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}), ...ident }
|
|
634
|
-
: { type: "context_usage", ...contextUsageEventData(ev), ...ident };
|
|
635
|
-
if (arm !== null)
|
|
636
|
-
writeLiveFrame(res, arm);
|
|
609
|
+
const arm = ev.type === "turn_end"
|
|
610
|
+
? { type: "turn_end", ...turnEndEventData(ev), ...ident }
|
|
611
|
+
: ev.type === "message_committed"
|
|
612
|
+
? { type: "message_committed", entryId: e.entryId, role: e.role, ...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}), ...ident }
|
|
613
|
+
: { type: "context_usage", ...contextUsageEventData(ev), ...ident };
|
|
614
|
+
writeLiveFrame(res, arm);
|
|
637
615
|
}
|
|
638
616
|
else {
|
|
639
|
-
|
|
617
|
+
const unknownArm = ev;
|
|
618
|
+
writeLiveFrame(res, { ...unknownArm });
|
|
640
619
|
}
|
|
641
620
|
}
|
|
642
621
|
});
|
|
@@ -742,10 +721,11 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
742
721
|
? deps.sendUserFile.runWithContext({
|
|
743
722
|
taskId: askTaskId,
|
|
744
723
|
emit: async (f) => {
|
|
745
|
-
|
|
724
|
+
if (ledgerSink === undefined || faceAlive())
|
|
725
|
+
void (await emitAsk(f));
|
|
746
726
|
if (ledgerSink) {
|
|
747
727
|
const { type, ...rest } = f;
|
|
748
|
-
await ledgerSink.append(type, rest).catch(()
|
|
728
|
+
await ledgerSink.append(type, rest).catch(createForwardAppendFailedNote(type));
|
|
749
729
|
}
|
|
750
730
|
},
|
|
751
731
|
}, withApproval)
|
|
@@ -957,7 +937,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
957
937
|
const cascadeLeg = () => runCascade(prepared.runner, specWithSignal, cascadeConfig(deps.config.cascadeLadder, prepared.spec.limits?.maxCostUsd), heldDecisions);
|
|
958
938
|
const plainLeg = async () => {
|
|
959
939
|
const stream = prepared.runner.runTaskStream(specWithSignal, undefined, heldDecisions);
|
|
960
|
-
const syncWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage),
|
|
940
|
+
const syncWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), prepared.legFacts?.sessionWitness);
|
|
961
941
|
for await (const ev of stream) {
|
|
962
942
|
observeSyncLegEvent(ev);
|
|
963
943
|
prepared.acceptance.observe(ev);
|
|
@@ -975,7 +955,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
975
955
|
inflightRuns.delete(durableTaskId);
|
|
976
956
|
}
|
|
977
957
|
const syncAbortCause = syncCancelCtrl.cause();
|
|
978
|
-
await voidVerifierPauseCard(result,
|
|
958
|
+
await voidVerifierPauseCard(result, prepared.runner);
|
|
979
959
|
if (syncAbortCause !== undefined) {
|
|
980
960
|
result = mintCancelledResult({
|
|
981
961
|
taskId: durableTaskId ?? "",
|
|
@@ -5,6 +5,13 @@ import type { ServiceConfig } from "../config-types.js";
|
|
|
5
5
|
export interface RunFaceDeps {
|
|
6
6
|
config: Pick<ServiceConfig, "runStaleSec" | "infraCostRates">;
|
|
7
7
|
}
|
|
8
|
+
/** 一条行是不是「看起来失联」:盘上 `running`、而最后一次心跳早于 `runStaleSec` —— 读面把它折成 `failed`(`run.stalled`)。读它的人只此一只判据(读面与同步重放回执同读)。 */
|
|
9
|
+
export declare function runRowStale(deps: RunFaceDeps, run: RunRecord): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* 一条行上的结果**当作出口结果读**(封口 + 脱敏的读入口)—— 读面的 `result` 与同步提交的重放体读的是同一只:
|
|
12
|
+
* 终局行按 taskId+updatedAt 记忆的读入口,非终局行(停驻 / 在跑)走店入口那一套封口的读入口;行上没有结果 ⇒ `undefined`。
|
|
13
|
+
*/
|
|
14
|
+
export declare function runRowExitResult(run: RunRecord): import("../plugins/store-contracts.js").PersistedTaskResult | undefined;
|
|
8
15
|
export declare function readRunFace(deps: RunFaceDeps, runStore: RunStore, run: RunRecord): Promise<{
|
|
9
16
|
result: import("../plugins/store-contracts.js").PersistedTaskResult | undefined;
|
|
10
17
|
supervisorCost: import("../observability/cost-taxonomy.js").SupervisorCostBreakdown | undefined;
|