@sema-agent/server 7.138.0 → 7.139.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 +276 -7
- package/README.md +7 -4
- package/README.zh-CN.md +6 -4
- package/USAGE.md +73 -29
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +20 -20
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/run-firm.js +2 -2
- 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 +16 -24
- package/dist/boot/execution-lane.d.ts +1 -1
- package/dist/boot/governance-seams.d.ts +3 -2
- package/dist/boot/governance-seams.js +9 -11
- package/dist/boot/memory-consolidation.js +2 -2
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +8 -9
- package/dist/boot/retention-lane.d.ts +5 -5
- package/dist/boot/retention-lane.js +0 -6
- package/dist/boot/runner-deps.d.ts +3 -1
- package/dist/boot/runner-deps.js +2 -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-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.js +0 -1
- package/dist/boot/stage-09-leader.js +1 -2
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/boot/stores.js +4 -4
- package/dist/boot/web-admin.js +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 +7 -7
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +19 -24
- package/dist/config-provider.js +27 -12
- package/dist/config-types.d.ts +10 -5
- package/dist/config.d.ts +33 -0
- package/dist/config.js +44 -15
- package/dist/delegated-session-owner.d.ts +2 -2
- package/dist/deployment-shape.d.ts +1 -1
- 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 +19 -0
- package/dist/execution-lane-caps.js +10 -0
- 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/resume-legs.js +50 -20
- package/dist/http/route-ctx.d.ts +14 -8
- 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/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +115 -11
- 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 +37 -31
- package/dist/http/server.d.ts +35 -19
- package/dist/http/server.js +11 -0
- 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/index.d.ts +1 -0
- package/dist/memory-sync-client.d.ts +53 -14
- package/dist/memory-sync-client.js +196 -116
- 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/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 +30 -12
- package/dist/plugins/caching-session-store.d.ts +6 -2
- package/dist/plugins/caching-session-store.js +9 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
- package/dist/plugins/checkpoint-store-sql.js +5 -7
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +49 -103
- 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 +7 -6
- package/dist/plugins/file-history-store-sql.js +5 -5
- 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/local-session-store.d.ts +149 -50
- package/dist/plugins/local-session-store.js +382 -107
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/one-time-migrations.d.ts +111 -3
- package/dist/plugins/one-time-migrations.js +380 -10
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
- package/dist/plugins/permission-rule-store-sql.js +13 -16
- 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 +20 -4
- package/dist/plugins/remote-env-adb.js +70 -62
- package/dist/plugins/remote-env-device.d.ts +30 -12
- package/dist/plugins/remote-env-device.js +18 -17
- package/dist/plugins/remote-env-e2b.d.ts +18 -4
- package/dist/plugins/remote-env-e2b.js +41 -42
- package/dist/plugins/remote-env-file-error.d.ts +2 -0
- package/dist/plugins/remote-env-file-error.js +1 -1
- package/dist/plugins/remote-env-host.d.ts +21 -8
- package/dist/plugins/remote-env-host.js +66 -70
- package/dist/plugins/remote-env-k8s.d.ts +11 -16
- package/dist/plugins/remote-env-k8s.js +61 -54
- package/dist/plugins/remote-env-local-docker.d.ts +29 -6
- package/dist/plugins/remote-env-local-docker.js +46 -59
- package/dist/plugins/remote-env-ssh.d.ts +16 -4
- package/dist/plugins/remote-env-ssh.js +72 -56
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/retention-store-sql.d.ts +9 -6
- package/dist/plugins/retention-store-sql.js +7 -1
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +5 -0
- package/dist/plugins/schema-consistency.d.ts +15 -5
- package/dist/plugins/schema-consistency.js +14 -8
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- 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-store.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-backend.js +19 -7
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +9 -3
- package/dist/plugins/tidb-session-store.d.ts +57 -26
- package/dist/plugins/tidb-session-store.js +118 -50
- package/dist/plugins/tool-result-store-sql.d.ts +3 -2
- package/dist/plugins/tool-result-store-sql.js +18 -10
- 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/run-local.d.ts +6 -0
- package/dist/run-local.js +24 -20
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +3 -1
- 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 +19 -9
- package/dist/session-incarnation.js +4 -2
- 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/task-a2a.d.ts +1 -1
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +3 -3
- 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/self-minted-wire.d.ts +111 -2
- package/dist/trace/self-minted-wire.js +38 -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/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/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
|
@@ -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 () => {
|
|
@@ -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,11 +1004,11 @@ 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);
|
|
1003
1009
|
return { status: 200, body: withSyncSubmitKeys(buildSyncSubmitBody(exitResult, { taskId: durableTaskId, sessionId: prepared.spec.sessionId, status: "suspended" })) };
|
|
1004
1010
|
}
|
|
1005
|
-
if (syncPlane.status === "needs_review" && deps.checkpointStore && deps.runStore && durableTaskId
|
|
1011
|
+
if (syncPlane.status === "needs_review" && deps.checkpointStore && deps.runStore && durableTaskId) {
|
|
1006
1012
|
await deps.runStore.setNeedsReview(durableTaskId, exitResult);
|
|
1007
1013
|
return { status: 200, body: withSyncSubmitKeys(buildSyncSubmitBody(exitResult, { taskId: durableTaskId, sessionId: prepared.spec.sessionId, status: "needs_review" })) };
|
|
1008
1014
|
}
|
package/dist/http/server.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { type SessionMirrorRuling } from "../web-admin-client/facade.js";
|
|
|
9
9
|
import type { RestartSignal } from "@sema-agent/settings-schema/fleet";
|
|
10
10
|
import { type OwnerAwareSessionStore } from "../security.js";
|
|
11
11
|
import type { PeerDesk, PeerLease } from "../peer-seat.js";
|
|
12
|
+
import type { ExecutionRoot, ResolvedTaskSpec } from "./execution-context.js";
|
|
12
13
|
import type { RunStore, ImageIndex, ImageBake, CheckpointStoreFull, ResumeAnchorStore, ApprovalExemptionStore, ApprovalNonceStore, ServiceSessionPolicyStore, ServiceFileHistoryStore, ToolResultStoreFull, StoreBackend } from "../plugins/store-backend.js";
|
|
13
14
|
import type { RuleConsentLane } from "../rules-consent.js";
|
|
14
15
|
import { type MemorySyncRequest, type MemorySyncResponse } from "../memory-sync.js";
|
|
@@ -52,6 +53,8 @@ export interface RequestAuth {
|
|
|
52
53
|
* defaultScopes(memory.scopes 种子)。resume 腿(checkpoint 重建 auth)缺席 — 回落持久化的
|
|
53
54
|
* body.projectId(原始提交已验形),那条回落腿自己再折一次小写,两腿查同一个键。 */
|
|
54
55
|
resolvedProjectId?: string;
|
|
56
|
+
/** authorizer 判过的头(`security.ts` `createAuthorizer` 交出;语义见 `AuthContext.head`)⇒ fresh 执行上下文的 `judged`。自建 authorizer 不给 = 解析器不比住客。 */
|
|
57
|
+
head?: import("@sema-agent/core").SessionHead;
|
|
55
58
|
}
|
|
56
59
|
/**
|
|
57
60
|
* [ref] A8 —— ServiceDeps 的**分组窄接口**。
|
|
@@ -60,7 +63,7 @@ export interface RequestAuth {
|
|
|
60
63
|
* 平铺形的直接后果 = main.ts 一个上千行的装配字面量,键与键之间没有任何「属于同一件事」的编译期表达,
|
|
61
64
|
* 键落错家/从未接线只能靠源码钉逐个盯(1.300/1.301/1.304/1.307/1.310 五起真缺陷都是这个病族)。
|
|
62
65
|
*
|
|
63
|
-
* 药:七个**窄**接口 +
|
|
66
|
+
* 药:七个**窄**接口 + 顶层必填八件。装配点按组构造(每组一个带类型标注的中间 const ⇒ 受 TS 多余
|
|
64
67
|
* 属性检查保护),键放错组当场编译红。
|
|
65
68
|
*
|
|
66
69
|
* 兼容形(本批**不动消费点**):`ServiceDeps` = 平铺视图 ∩ 组槽。旧的平铺构造(96 个测试文件 +
|
|
@@ -68,8 +71,8 @@ export interface RequestAuth {
|
|
|
68
71
|
* 入口摊平回平铺视图,server.ts 内的 ~700 处 `deps.x` 消费点原样保留。消费点迁移是后批的事——
|
|
69
72
|
* 一步到位需要同批改 ~700 处 src + ~800 处 test 键位,与「先立形、后搬家」相比没有额外收益。
|
|
70
73
|
*/
|
|
71
|
-
/** createHttpServer
|
|
72
|
-
*
|
|
74
|
+
/** createHttpServer 的**必填八件**——引擎、部署配置、提交的身份与根会话(authorizer)、请求→TaskSpec 的映射、选路席、side-query 执行席、读口场景门、对端座的 desk。
|
|
75
|
+
* 八者恒在场,故留在顶层(不进任何可选组):没有它们 server 根本起不来,包一层可选组只会把编译期
|
|
73
76
|
* 错误推迟到运行期(本仓 E-HIGH-1 病族的成因就是「可选键从未接线、编译期无声」)。 */
|
|
74
77
|
export interface ServiceCoreDeps {
|
|
75
78
|
runner: Runner;
|
|
@@ -83,9 +86,22 @@ export interface ServiceCoreDeps {
|
|
|
83
86
|
sideQuery: (spec: SideQuerySpec) => Promise<SideQueryResult>;
|
|
84
87
|
config: ServiceConfig;
|
|
85
88
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
+
* Resolve identity + the owned root session BEFORE the spec is built (S6). Throw `HttpError` to reject.
|
|
90
|
+
* 🔴 **必填**:它是一次提交的根会话**唯一**的来源与铸点(体里的 `sessionId` 只由它消费:有属主读的店判属主,单用户嵌入原样采用,
|
|
91
|
+
* 不带 ⇒ 现铸并登记)。省掉是编译错;绕过类型造的 deps 在 {@link createHttpServer} 入口拒启。单用户嵌入传
|
|
92
|
+
* `createAuthorizer(config, runner.sessions)`(包根导出)—— 店没有属主读时它退化为只派 principal 与会话 id。
|
|
93
|
+
*/
|
|
94
|
+
authorize: (ctx: {
|
|
95
|
+
req: IncomingMessage;
|
|
96
|
+
body: TaskRequestBody;
|
|
97
|
+
}) => Promise<RequestAuth>;
|
|
98
|
+
/**
|
|
99
|
+
* Map an inbound request body + resolved auth to a TaskSpec. The SERVICE/deployment owns model
|
|
100
|
+
* + tools + mcp here (callers only supply content). `auth` carries the principal-derived memory scope —
|
|
101
|
+
* prefer it over anything in the body.
|
|
102
|
+
* The root session is NOT the resolver's to write: the return type carries no `sessionId`
|
|
103
|
+
* (`ResolvedTaskSpec`); the server stamps the root the authorizer resolved onto every spec it executes
|
|
104
|
+
* (`http/execution-context.ts` `stampSpecRoot`). A resolver that needs the root reads `opts.execution.root`.
|
|
89
105
|
*/
|
|
90
106
|
/** `opts.leg` (codex R4, appendSystemPrompt 案): "fresh" ⇒ a NEW submit — resolveSpec may fail-loud 400 on
|
|
91
107
|
* combos that would silently discard caller payload (append rider / outputStyle on an append-less pack).
|
|
@@ -108,6 +124,8 @@ export interface ServiceCoreDeps {
|
|
|
108
124
|
* `opts.unattendedLeg`([ref] 候裁 18,codex r3 F1;[ref] ① 改形):续跑家族在 ctx 带无人值守标(`CheckpointCtx.unattendedLeg`)时递 `true` ——
|
|
109
125
|
* 装配器在**全部折叠之后**经首跑同一只合并口 `withUnattendedLeg` 重挂那条腿(`default` + 按类作答的 onAsk)。只由续跑腿
|
|
110
126
|
* 按服务端写的 ctx 位递,绝不从请求体读。 */
|
|
127
|
+
/** `opts.execution`(必填):这条腿的执行上下文(`http/execution-context.ts`;只 `fresh` / `continuation` 两形,`root` 恒在)。装配器要用这条腿的根会话
|
|
128
|
+
* (场景设施的分区键、受理见证)读 `opts.execution.root` —— 它与 server 随后盖进 spec 的根是同一个值,装配器自己不写根。 */
|
|
111
129
|
/** `opts.lease`(必填):这条腿的**宿主租约** —— 装配在它之下登记本腿交给引擎的对端声明(`peerDesk.declare(草稿, opts.lease)`)。
|
|
112
130
|
* 租约由两只漏斗(受理 `prepareSpec` / 续跑 `resolveResumeSpecOrBlocked`)在调用本函数**之前**开、由漏斗的失败出口或拿到产物的那段作用域放;
|
|
113
131
|
* 装配只借用,不放、不留。实现方自铸的声明归自己的 desk 时可以不理它。 */
|
|
@@ -117,7 +135,8 @@ export interface ServiceCoreDeps {
|
|
|
117
135
|
onNotice?: (notice: EngineNotice) => void;
|
|
118
136
|
unattendedLeg?: true;
|
|
119
137
|
lease: PeerLease;
|
|
120
|
-
|
|
138
|
+
execution: ExecutionRoot;
|
|
139
|
+
}) => ResolvedTaskSpec | Promise<ResolvedTaskSpec>;
|
|
121
140
|
/**
|
|
122
141
|
* [ref] 场景 hands lane 选路:按 `resolveSpec` 刚产出的 TaskSpec 取本任务的执行 Runner。
|
|
123
142
|
* 装配点(`boot/stage-10-http-server.ts`)传的是 `createHandsRunnerSelector` —— 选路规则只有那一份实现,
|
|
@@ -146,7 +165,7 @@ export interface ServiceCoreDeps {
|
|
|
146
165
|
* 本层用它开**宿主租约**:受理与续跑两只漏斗在装配之前各开一只(`lease("fresh")` / `lease("resume")`),经 `resolveSpec` 的 `opts.lease` 交给装配,
|
|
147
166
|
* 随产物(`PreparedTaskSubmission.peerLease` / 续跑结果)交给拿到它的那段作用域,在那段作用域的 `finally` 放;活过那段作用域的驱动经 `heldFor` 自持一只。
|
|
148
167
|
* 这只 desk 另有三处用法,不止这两只漏斗:① 探测口 `POST /v1/capabilities/mcp/probe` 的调用方臂在走查里开一只 `probe` 租约(三道前置拒之后开,拨号落地才放);
|
|
149
|
-
* ② 两张会话面板(`GET /v1/sessions/:id/mcp` · `…/a2a`)与探测走查拨名册声明时递它的座(`desk.seat
|
|
168
|
+
* ② 两张会话面板(`GET /v1/sessions/:id/mcp` · `…/a2a`)与探测走查拨名册声明时递它的座(`desk.seat`),不开租约;③ 名册声明在起服时登记在一只进程寿命的 `roster` 租约下
|
|
150
169
|
* (stage-07 / run-local 开,从不放),并且每只 Runner 的 `peerSeat` 是它的座。登记表的两个读数是 `/metrics` 的 `peer_registry_entries` / `peer_registry_holders`。
|
|
151
170
|
* 🔴 **必填**:没有 MCP / A2A 凭据的嵌入也给一只 `createPeerDesk()`(零 IO)—— 一个部署只有一张登记表,缺席不是「没有座」的另一种读法。
|
|
152
171
|
*/
|
|
@@ -371,7 +390,7 @@ export interface ServiceSeamDeps {
|
|
|
371
390
|
deviceStore?: import("../device-store.js").DeviceStore;
|
|
372
391
|
/**
|
|
373
392
|
* [ref](车C 余件):device lane 的**准入链**(`createDeviceEnrollment`)—— 管理面三个写动词
|
|
374
|
-
* (签发 / 注册 / 吊销)与首绑写协议(`
|
|
393
|
+
* (签发 / 注册 / 吊销)与首绑写协议(`commitExecutionContext` 的设备那一支)的**唯一**判据属主。
|
|
375
394
|
*
|
|
376
395
|
* 🔴 为什么路由拿的是它而不是 {@link deviceStore}:enrollment 那一层持着两件路由造不出来的东西 ——
|
|
377
396
|
* ①跨副本限速座(`DeviceEnrollRateLimiter`,§7-T5 ⑤:端点车**必须**传一个,而它由 boot 从既有
|
|
@@ -598,6 +617,9 @@ export interface ServiceSeamDeps {
|
|
|
598
617
|
* per-principal 查询走 caps client 的 executionRuling 同车(零额外 RTT);absent = 无 center(单用户/
|
|
599
618
|
* dry-run)⇒ 观测面不接线,零行为差。fail-open:观测失败绝不影响 sync 面本身。 */
|
|
600
619
|
sessionMirrorRuling?: (principal: string | undefined) => Promise<SessionMirrorRuling | undefined>;
|
|
620
|
+
/** 主体的执行车道裁定(caps client 的 `executionRuling`,与场景裁定同车零额外 RTT)—— 执行上下文解析器步 ③ 的唯一输入(`http/execution-context.ts`,
|
|
621
|
+
* 判据 `runtime-caps-resolver.ts` `gateExecutionLane`)。缺席 = 无中心(单用户 / dry-run / 嵌入方)⇒ 不判,与已发同。 */
|
|
622
|
+
executionRuling?: (principal: string | undefined) => Promise<import("../web-admin-client/facade.js").ExecutionRuling | undefined>;
|
|
601
623
|
/** Degenerate-output a/b instrument: on errorCode=output.degenerate, inspect the session's turns to
|
|
602
624
|
* classify where the salvageable answer was (counter + log). Fire-and-forget; no-op otherwise. */
|
|
603
625
|
instrumentDegenerate?: (result: TaskResult) => void;
|
|
@@ -606,7 +628,9 @@ export interface ServiceSeamDeps {
|
|
|
606
628
|
* (手搭测试 deps;生产 main.ts 恒传)。 */
|
|
607
629
|
sideQueryAccounting?: SideQueryAccounting;
|
|
608
630
|
/** [ref](core 7.32.0):parked 赎回面工厂(按行解析驱动哪只 Agent 工具 + 同出一张列表的继承座;属主 `parked-revive-surface.ts`)。
|
|
609
|
-
* 缺席 = 这台部署没有 park 底座(checkpointStore / backgroundAgentStore 任一缺席),bg 赎回臂不接线。
|
|
631
|
+
* 缺席 = 这台部署没有 park 底座(checkpointStore / backgroundAgentStore 任一缺席),bg 赎回臂不接线。
|
|
632
|
+
* 🔴 **在场 ⇒ `sessionStorage` 必须带执行席 `executionPlacement`**:认领之后对根会话住客的唯一判官是执行席上的写口;缺 ⇒ `createHttpServer` 拒启
|
|
633
|
+
* (句子 = `PARKED_REVIVE_EXECUTION_SEAT_REQUIRED`)。 */
|
|
610
634
|
parkedReviveSurface?: import("../parked-revive-surface.js").ParkedReviveSurfaceFactory;
|
|
611
635
|
/** 可选第三件:revive args 的 `subagent_type` roster 成员集([ref] Q1 成员性规则)。当前生产装配
|
|
612
636
|
* **恒缺席**(boot 内建 default 无自定义 agents roster,per-request 的 spec.agents 不进 boot 工具)
|
|
@@ -641,14 +665,6 @@ export interface ServiceObservabilityDeps {
|
|
|
641
665
|
/** **准入/治理面**:身份解析与配额/限流/租约。每一件都是**拒绝**的权力(400/403/429),
|
|
642
666
|
* 缺席 = 该道门不存在(不是「默认放行的门」——两者在事故复盘里语义完全不同)。 */
|
|
643
667
|
export interface ServiceGovernanceDeps {
|
|
644
|
-
/**
|
|
645
|
-
* Resolve identity + the owned session BEFORE the spec is built (S6). Throw `HttpError` to reject.
|
|
646
|
-
* Optional: when omitted, no principal/ownership enforcement (the body's sessionId is used as-is).
|
|
647
|
-
*/
|
|
648
|
-
authorize?: (ctx: {
|
|
649
|
-
req: IncomingMessage;
|
|
650
|
-
body: TaskRequestBody;
|
|
651
|
-
}) => Promise<RequestAuth>;
|
|
652
668
|
/** 鉴权桥(registry-core api/auth-bridge):registry 用户 JWT → principal。缺席=分支不存在
|
|
653
669
|
* (bearer 只认 SERVICE_AUTH_TOKEN(S),现状不变)。 */
|
|
654
670
|
webAdminJwtVerifier?: {
|
|
@@ -867,7 +883,7 @@ export interface ServiceDepGroups {
|
|
|
867
883
|
deployment?: ServiceDeploymentDeps;
|
|
868
884
|
knobs?: ServiceKnobDeps;
|
|
869
885
|
}
|
|
870
|
-
/** 平铺视图 =
|
|
886
|
+
/** 平铺视图 = 必填八件 + 其余七组的全可选并集。**派生**自组接口,故与分组形永不漂。 */
|
|
871
887
|
export type FlatServiceDeps = ServiceCoreDeps & Partial<ServiceStoreDeps & ServiceCoordinatorDeps & ServiceSeamDeps & ServiceObservabilityDeps & ServiceGovernanceDeps & ServiceDeploymentDeps & ServiceKnobDeps>;
|
|
872
888
|
/** createHttpServer 的入参:两形皆可(平铺=存量,分组=推荐)。同名键两形都给时,**顶层显式键赢**
|
|
873
889
|
* (含显式 `undefined`)——一处装配点只该用一形,混用时以肉眼最近的那个为准。 */
|
package/dist/http/server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { validatePendingSteer } from "@sema-agent/core";
|
|
|
4
4
|
import {} from "../web-admin-client/facade.js";
|
|
5
5
|
import { describeValueShape } from "@sema-agent/settings-schema";
|
|
6
6
|
import { HttpError } from "../security.js";
|
|
7
|
+
import { SessionForkInProgressError } from "../execution-placement.js";
|
|
7
8
|
import {} from "../memory-sync.js";
|
|
8
9
|
import {} from "../orchestration/workflow-agent-steer.js";
|
|
9
10
|
import {} from "../orchestration/workflow-completion-inbox.js";
|
|
@@ -22,6 +23,7 @@ import { createRequestGuards } from "./request-guards.js";
|
|
|
22
23
|
import { sqlEngineSeatIssue } from "../sql-engine-posture.js";
|
|
23
24
|
import { parkDecisionStoreRefusal } from "../orchestration/workflow-park-decision.js";
|
|
24
25
|
import { CHECKPOINT_CTX_SEALING_REQUIRED } from "./checkpoint-ctx.js";
|
|
26
|
+
import { PARKED_REVIVE_EXECUTION_SEAT_REQUIRED } from "../parked-decide.js";
|
|
25
27
|
import { retiredServiceDepsRefusal } from "./retired-service-deps.js";
|
|
26
28
|
import { createAdmission, MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills } from "./admission.js";
|
|
27
29
|
import { createResumeLegs } from "./resume-legs.js";
|
|
@@ -61,6 +63,9 @@ export function createHttpServer(rawDeps) {
|
|
|
61
63
|
if (typeof deps.runnerFor !== "function") {
|
|
62
64
|
throw new Error("createHttpServer: the runnerFor seat is required (it picks each task's Runner from its execution lane) — pass the hands-lane selector, or `runnerFor: () => runner` for a single-Runner embedding; refusing to start rather than run every task on the widest Runner");
|
|
63
65
|
}
|
|
66
|
+
if (typeof deps.authorize !== "function") {
|
|
67
|
+
throw new Error("createHttpServer: the authorize seat is required — it is the only resolver of a submission's root session; pass `createAuthorizer(config, runner.sessions)` for a single-user embedding; refusing to start rather than run submissions on a root nobody attested");
|
|
68
|
+
}
|
|
64
69
|
{
|
|
65
70
|
const retired = retiredServiceDepsRefusal(deps);
|
|
66
71
|
if (retired !== undefined)
|
|
@@ -88,6 +93,8 @@ export function createHttpServer(rawDeps) {
|
|
|
88
93
|
}
|
|
89
94
|
if (deps.checkpointStore !== undefined && deps.checkpointCtxSealing === undefined)
|
|
90
95
|
throw new Error(`createHttpServer: ${CHECKPOINT_CTX_SEALING_REQUIRED}`);
|
|
96
|
+
if (deps.parkedReviveSurface !== undefined && deps.sessionStorage?.executionPlacement === undefined)
|
|
97
|
+
throw new Error(`createHttpServer: ${PARKED_REVIVE_EXECUTION_SEAT_REQUIRED}`);
|
|
91
98
|
const idemCache = new IdempotencyCache();
|
|
92
99
|
const inflightRuns = new Map();
|
|
93
100
|
const preemptableRuns = new DrainYieldingPreemptBook(() => deps.drainState?.draining === true);
|
|
@@ -164,6 +171,10 @@ export function createHttpServer(rawDeps) {
|
|
|
164
171
|
}
|
|
165
172
|
if (sendSessionError(res, err, "open"))
|
|
166
173
|
return;
|
|
174
|
+
if (err instanceof SessionForkInProgressError) {
|
|
175
|
+
sendError(res, 409, err.code, err.message);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
167
178
|
sendError(res, 500, "internal.error", "internal error");
|
|
168
179
|
});
|
|
169
180
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* [ref] × 7.106.0 合并复审 X1(gpt-6 腿 A F1 [high],验证腿 B verified)—— 请求键 `inheritEnv` 的**会话绑定唯一提交点**。
|
|
3
3
|
*
|
|
4
|
-
* ── 分界(与 `
|
|
4
|
+
* ── 分界(与 `execution-context.ts` 认领之后的执行放置提交同一句判据)──────────────────────────────────────────────────
|
|
5
5
|
* 凡需要「这条会话此刻归我这条 run」才成立的事,排在 run 认领之后;其余全排在建任何东西之前。`inheritEnv` 的三道判
|
|
6
6
|
* (值形 / 车道 / 射程)是**读**,住在 `resolveSpec`(两腿同判,任何副作用之前);而「本会话的 host 工厂下一次造环境按哪个词」
|
|
7
7
|
* 是**写**,它只属于一条**被受理**的 run —— 修前它写在 `resolveSpec` 的最后一步,仍先于路由层的拒(`/v1/tasks/stream` 的
|
|
@@ -28,10 +28,9 @@ export interface SessionInheritEnvCommitCtx {
|
|
|
28
28
|
bindSessionInheritEnv?: ((sessionId: string, policy: TaskInheritEnv | undefined) => void) | undefined;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* 在 run 认领之后调用:把受理的那条请求的 `inheritEnv`(缺席 = 清)
|
|
32
|
-
* 不写 —— `"all"` 在那一形已被 `resolveSpec` 响亮拒,`"scrub"` / 缺席与工厂缺省同形。
|
|
31
|
+
* 在 run 认领之后调用:把受理的那条请求的 `inheritEnv`(缺席 = 清)写进本会话(这条 run 的根会话)的绑定。
|
|
33
32
|
*/
|
|
34
|
-
export declare function commitSessionInheritEnv(ctx: SessionInheritEnvCommitCtx, sessionId: string
|
|
33
|
+
export declare function commitSessionInheritEnv(ctx: SessionInheritEnvCommitCtx, sessionId: string, body: {
|
|
35
34
|
readonly inheritEnv?: TaskInheritEnv | undefined;
|
|
36
35
|
}): void;
|
|
37
36
|
//# sourceMappingURL=session-inherit-env.d.ts.map
|
|
@@ -195,7 +195,7 @@ export interface TaskRequestBody {
|
|
|
195
195
|
* after it"). core enforces the "before" edges and the service passes its rejection codes through UNCHANGED for the
|
|
196
196
|
* shell to render: `resume_at.before_target_not_user` (only plain user-message targets),
|
|
197
197
|
* `resume_at.before_root_unsupported` (can't rewind past the session root — start a new session),
|
|
198
|
-
* `rewind_snapshot.unresolvable` (
|
|
198
|
+
* `rewind_snapshot.unresolvable` (restoreFiles: no snapshot at/above the "before" branch point). Only meaningful
|
|
199
199
|
* WITH `resumeAt` — sent alone it's a 400 (fail-loud, not a silent no-op). */
|
|
200
200
|
resumeAtMode?: string;
|
|
201
201
|
/** [ref] Phase3 (reuse-path warm-resume): require the `sessionId` to ALREADY exist. When `true` and the session
|
|
@@ -221,11 +221,6 @@ export interface TaskRequestBody {
|
|
|
221
221
|
/** DV-15: opt-in tolerance for a PARTIAL file restore (default = loud terminal failure carrying the per-file
|
|
222
222
|
* ledger; `true` = keep going and disclose the same ledger on the result's rewindNotes). */
|
|
223
223
|
acceptPartialRestore?: boolean;
|
|
224
|
-
/** RETIRED spelling (whole-tree epoch). Still accepted on the wire: its capture sense (true, no resumeAt) is
|
|
225
|
-
* a tolerated no-op with a deprecation disclosure; its restore sense (true + resumeAt) is a typed rejection
|
|
226
|
-
* naming `restoreFiles` — the restore semantics genuinely changed (whole tree ⇒ tracked set), so that
|
|
227
|
-
* migration is explicit by design (core adjudicates; the server never remaps it silently). */
|
|
228
|
-
rewindFiles?: boolean;
|
|
229
224
|
/** R8 code-only rewind (CC "Restore code" mode): converge the TRACKED set to the history boundary at a prior
|
|
230
225
|
* USER message WITHOUT forking the conversation (the session leaf is untouched — only the files move). The
|
|
231
226
|
* value is the same handle as `resumeAt` (the prompt's taskId); the service resolves it → the entry id. Honored ONLY
|
|
@@ -235,7 +230,7 @@ export interface TaskRequestBody {
|
|
|
235
230
|
* 与 `checkpointStore` 同;没有 `"enabled"` —— 缺席就是「部署说了算」的唯一形,声明只能单向收紧)。`"disabled"` ⇒
|
|
236
231
|
* 这条 run 零历史店调用:编辑照常落盘,但不做首次触碰备份、不铸回合边界、不做 fork 携带;委派出去的子代继承
|
|
237
232
|
* (core `ParentSpecSeat` 从本位铸,本仓零接线)。`wiring_manifest.fileHistory` 报 `"disabled"`。
|
|
238
|
-
* 同一个提交里再要还原(`restoreFiles` / `rewindFilesTo`
|
|
233
|
+
* 同一个提交里再要还原(`restoreFiles` / `rewindFilesTo`)= 体内矛盾 ⇒ **受理时** 400
|
|
239
234
|
* `request.field_conflict`(`request-value-closure.ts` 的 `FILE_HISTORY_DISABLED_NO_RESTORE`):立门时 core 的 `rewind.capture_disabled`
|
|
240
235
|
* 落在会话按 `resumeAt` 分叉之后、拒后不回滚,所以本层在副作用之前拒([ref] / core 7.35.0 [ref] 起 core 在分叉**之前**拒,
|
|
241
236
|
* 本层 400 照旧有效)。续跑腿不过这道门。
|
|
@@ -472,7 +467,7 @@ export interface TaskRequestBody {
|
|
|
472
467
|
* · 仅 `REMOTE_EXEC=device` 的部署接受;其它车道收到它 = **400 响亮拒**(不静默忽略 —— 调用方会
|
|
473
468
|
* 以为自己指定了执行设备,而那条声明从未存在);
|
|
474
469
|
* · 仅**根任务**提交携带(子代无条件继承根绑定,core 为 subagent/cascade/verify 另铸 sessionId);
|
|
475
|
-
* · 首绑 =
|
|
470
|
+
* · 首绑 = 根会话行执行格的 absent → writer(device, D)(按受理那一读的住客定址,并发单赢家);后续根提交:缺席 = 沿用绑定 / 携且同 = 幂等 /
|
|
476
471
|
* 携且异 = 403 `device.identity_mismatch`(**v1 无隐式重绑**,换设备走
|
|
477
472
|
* `POST /v1/devices/sessions/:rootSessionId/rebind`);
|
|
478
473
|
* · resume 家族(`/decide` / `/answer` / `/plan_review` / `/wake`)**不收**这个键:车道随树固定。
|
|
@@ -491,10 +486,11 @@ export interface TaskRequestBody {
|
|
|
491
486
|
/** [ref]⑧/[ref]/[ref] (core 5.23.0 `TaskSpec.oneShot`): this SUBMISSION is one-shot — no later turn exists
|
|
492
487
|
* in which an async background notification could land (the archetypal case is a headless `sema -p` whose
|
|
493
488
|
* process exits when the turn ends). PER-REQUEST on purpose: "does this submission expect to be continued" is
|
|
494
|
-
* a property of the submission, not of the connection it arrived on. core
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
489
|
+
* a property of the submission, not of the connection it arrived on. Since core 7.60.0 it changes when the
|
|
490
|
+
* submission ends: where a one-shot run would end, the engine waits for the background agents and workflow runs
|
|
491
|
+
* it started (a background command or Monitor watch is never waited for), bounded by the run's walltime, a cancel
|
|
492
|
+
* and a 600 s ceiling; a sync submission therefore returns after that background work. It grants nothing, so
|
|
493
|
+
* there is no tenancy gate. Sibling of `interactiveTools` (the same `-p` posture) and passed
|
|
498
494
|
* through the same way: a boolean rides, absent/garbage ⇒ key omitted (core's default = interactive). Rides the
|
|
499
495
|
* persisted body onto resume legs. */
|
|
500
496
|
oneShot?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export type { TaskRequestBody } from "./http/wire-types.js";
|
|
|
26
26
|
export { ensureCheckpointCtxSealing } from "./sealed-key.js";
|
|
27
27
|
export type { CheckpointCtxSealing } from "./http/checkpoint-ctx.js";
|
|
28
28
|
export { createHttpServer, type ServiceDeps, type RequestAuth, type FlatServiceDeps, type ServiceDepGroups, type ServiceCoreDeps, type ServiceStoreDeps, type ServiceCoordinatorDeps, type ServiceSeamDeps, type ServiceObservabilityDeps, type ServiceGovernanceDeps, type ServiceDeploymentDeps, type ServiceKnobDeps, } from "./http/server.js";
|
|
29
|
+
export type { ExecutionContext, ResolvedTaskSpec, RootedTaskSpec } from "./http/execution-context.js";
|
|
29
30
|
export { createPeerDesk } from "./peer-seat.js";
|
|
30
31
|
export type { PeerDesk, PeerLease, McpDraft, A2aDraft } from "./peer-seat.js";
|
|
31
32
|
export { createSideQueryLane, type KeyResolver, type SideQueryLane, type SideQueryLaneCtx } from "./boot/side-query-lane.js";
|
|
@@ -1,29 +1,68 @@
|
|
|
1
1
|
import { type MemoryBackend, type MemorySyncClientResult, type MemorySyncTransport } from "@sema-agent/core";
|
|
2
|
+
import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
|
|
2
3
|
/** 结构化日志面(src/observability/logger.ts Logger 的结构子集——测试可注入捕获器)。 */
|
|
3
4
|
export interface MemorySyncLog {
|
|
4
5
|
info(msg: string, fields?: Record<string, unknown>): void;
|
|
5
6
|
warn(msg: string, fields?: Record<string, unknown>): void;
|
|
6
7
|
}
|
|
7
|
-
/** 缺省对端名(core `syncMemoryScope` 同款缺省;cursor 按 (scope,peer) 分区)。 */
|
|
8
|
-
export declare const DEFAULT_MEMORY_SYNC_PEER = "central";
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* 中心基址 → 同步对端的身份(规范化基址)。本仓唯一的铸点:`config.ts` 读 env 时调它拒启(引擎关着也拒),transport 建时再调它铸身份 —— 同一只纯函数,同一个答案。
|
|
10
|
+
* 两道判,各归各的属主,本函数不另写规则:
|
|
11
|
+
* ① 地址的形 —— 这只地址是「后面要拼路径的基址」座位(post 把同步路径拼在它后面),判官与拒句就是那一座位的那一只({@link pathBaseUrlRefusal}:层级式 http(s),
|
|
12
|
+
* 不带 userinfo / query / fragment;拒句只点名规则、值只到 origin);
|
|
13
|
+
* ② 身份里有没有凭据 —— 判官是引擎包根的 `carriesCredential`(引擎同步客户端判 `transport.peer` 的同一只:任何 userinfo,或同主体出口脱敏会遮掉的任何一段,
|
|
14
|
+
* 例如路径段里的令牌形),判的就是规范化之后的身份 —— 引擎第一轮判的那个值。身份随每个请求体与落盘游标走,所以带凭据的地址拒而不清洗
|
|
15
|
+
* (清洗过的身份是另一个身份;令牌只走 `MEMORY_SYNC_TOKEN`);拒句不引地址的任何部分。
|
|
16
|
+
* 规范化 = WHATWG URL 的 `origin`(协议与主机小写、缺省端口省掉)+ 路径去尾斜杠;规范化之后装不进中心侧 `peer` 列({@link MEMORY_SYNC_PEER_COLUMN_CHARS},
|
|
17
|
+
* 每一轮都会被中心拒)⇒ 同样拒。
|
|
18
|
+
*/
|
|
19
|
+
export declare function memorySyncPeerOf(url: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer` 是运维改绑游标时要抄的那个值:
|
|
22
|
+
* 它已过 {@link memorySyncPeerOf} 的两道判(带凭据的身份起不来);仍过展示铸点({@link buildDisplaySafeUrl}),日志里的 URL 值一律经它 —— 两只判官
|
|
23
|
+
* (引擎的出口脱敏与本仓的展示形状律)不是同一份实现,本仓的那一只若遮了引擎放行的一段,这一行带遮记号而不是逐字(响亮,不漏)。
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildMemorySyncEnabledFields(peer: string, scope: string, cursorPath: string): {
|
|
26
|
+
readonly peer: string;
|
|
27
|
+
readonly scope: string;
|
|
28
|
+
readonly cursorPath: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* fetch transport(core `MemorySyncTransport` 的部署实现)。`peer` = {@link memorySyncPeerOf}(`url`)(建时铸一次;拒的地址在这里抛,一个请求都不发),
|
|
32
|
+
* `post` 打 `${peer}${path}`(core 传来的 path 以 `/` 开头);`token` 走 Authorization: Bearer(与中心侧 gatedPrincipal 的 verified-principal 门对齐)。
|
|
33
|
+
* 非 2xx throw(status + body 前 200 字节——中心 4xx/5xx 的 typed error body 就是最好的诊断线索);2xx 返回解码后的 JSON(形状校验在 core
|
|
34
|
+
* parseMemorySyncResponse,职责不重复)。`timeoutMs`(缺省 60s,AbortSignal.timeout)是活性保护:fire-and-forget 轮挂死会把 inflight 节流永久卡住
|
|
35
|
+
* (undici 默认 headers 超时 300s 太钝)。
|
|
16
36
|
*/
|
|
17
37
|
export declare function createMemorySyncTransport(opts: {
|
|
18
38
|
url: string;
|
|
19
39
|
token: string;
|
|
20
40
|
timeoutMs?: number;
|
|
21
|
-
|
|
41
|
+
/** The one call shape this transport makes (`fetch` itself fits it). */
|
|
42
|
+
fetchImpl?: (url: string, init: RequestInit) => Promise<Response>;
|
|
22
43
|
}): MemorySyncTransport;
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
/** 一层的游标文件路径:`memoryRoot`(= RunnerDeps.memoryEngineDir 的 resolved config root)下 `sync-cursors/<scope 摘要>.json`。一层一只,不随对端变。
|
|
45
|
+
* 落在子目录里:与更早的根下一层文件名 `sync-cursor-<peer>.json` 的空间不相交。 */
|
|
46
|
+
export declare function memorySyncCursorPath(memoryRoot: string, scope: string): string;
|
|
47
|
+
/** M31 一次的读数(测试按它断言)。`window: "closed"` ⇒ 一行都没读。 */
|
|
48
|
+
export interface SyncCursorRelocationM31Report {
|
|
49
|
+
readonly window: "open" | "closed";
|
|
50
|
+
readonly moved: number;
|
|
51
|
+
readonly left: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* ⑤ 一次性迁移 M31:把升级前的游标搬到各自那一层的路径(`rename`,字节不变;只搬不改,判归引擎)。`today` = UTC 日历日;窗关 ⇒ 不搬。
|
|
55
|
+
* · 上一版的分区目录 `sync-cursors/central/<scope 摘要>.json` ⇒ `sync-cursors/<同名>`(两种布局的文件名是同一只 scope 摘要);搬空的目录删掉;
|
|
56
|
+
* 名字不是摘要形的(写游标的 staging 残片)原地不动。
|
|
57
|
+
* · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(没法说它是哪一层的基线)。
|
|
58
|
+
* · 目标路径上已有游标 ⇒ 不搬、不删,一行 warn(这一层用路径上的那一份;留下的那只没有读者)。
|
|
59
|
+
* 与同一数据根上并跑的另一个进程(server + run-local)同搬:源已被对方搬走(`ENOENT`)⇒ 跳过。残余(如实):先查目标再 `rename` 之间若有写者在目标路径上
|
|
60
|
+
* 落下游标,会被盖掉 —— 写者只有本模块的 runner,而 runner 写任何游标之前先跑完本步。别的读写错 ⇒ 抛(这一轮整轮失败、下一轮再搬)。
|
|
61
|
+
*/
|
|
62
|
+
export declare function relocateSyncCursorsM31(memoryRoot: string, log: MemorySyncLog, opts: {
|
|
63
|
+
readonly today: string;
|
|
64
|
+
readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
|
|
65
|
+
}): Promise<SyncCursorRelocationM31Report>;
|
|
27
66
|
/** 一轮里一层的结果。 */
|
|
28
67
|
export interface MemorySyncLayerResult {
|
|
29
68
|
readonly scope: string;
|
|
@@ -59,9 +98,9 @@ export declare function createMemorySyncRunner(opts: {
|
|
|
59
98
|
/** 同步层(`config.memorySync.scope`);它同属主的项目层随它一起同步。 */
|
|
60
99
|
scope: string;
|
|
61
100
|
memoryRoot: string;
|
|
101
|
+
/** 对端 = 它的 `peer`(这一轮的对端只有这一个来源;游标绑的对端由引擎对它判)。 */
|
|
62
102
|
transport: MemorySyncTransport;
|
|
63
103
|
log: MemorySyncLog;
|
|
64
|
-
peer?: string;
|
|
65
104
|
now?: () => number;
|
|
66
105
|
/** S2.5 分批(core 1.284 两 seam):push 集/pull 集单轮上限。未设=不分批(wire 字节不变)。 */
|
|
67
106
|
maxPushEntries?: number;
|