@sema-agent/server 7.149.0 → 7.150.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 +122 -9
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +39 -15
- package/dist/approval-card.d.ts +1 -1
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +61 -25
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-10-http-server.d.ts +33 -1
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capability-facts.d.ts +65 -56
- package/dist/capability-facts.js +42 -35
- package/dist/config-catalog.js +1 -1
- package/dist/config-types.d.ts +12 -11
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +2 -2
- package/dist/http/approval-sweeps.js +2 -1
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +4 -4
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +4 -4
- package/dist/http/owner-access.d.ts +243 -19
- package/dist/http/owner-access.js +184 -5
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.js +16 -1
- package/dist/http/resume-legs.js +18 -14
- package/dist/http/route-ctx.d.ts +23 -11
- package/dist/http/routes/a2a-serve.js +15 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +8 -9
- package/dist/http/routes/capabilities.js +4 -3
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +23 -30
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.js +3 -7
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +9 -8
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +8 -4
- package/dist/http/server.js +5 -1
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +1 -2
- package/dist/http/submit-ctx.js +3 -2
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +8 -2
- package/dist/leg-facts.js +1 -0
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +5 -5
- package/dist/observability/fail-open.js +5 -5
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/owner-faces.d.ts +27 -3
- package/dist/owner-faces.js +2 -0
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/local-checkpoint-store.d.ts +4 -1
- package/dist/plugins/local-checkpoint-store.js +3 -0
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +74 -49
- package/dist/plugins/one-time-migrations.js +134 -69
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +3 -1
- package/dist/plugins/remote-env-e2b.js +11 -8
- package/dist/plugins/remote-env-host.d.ts +1 -1
- package/dist/plugins/remote-env-host.js +20 -11
- package/dist/plugins/remote-env-k8s.js +3 -2
- package/dist/plugins/remote-env-local-docker.js +3 -2
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
- package/dist/plugins/schema-consistency.d.ts +7 -5
- package/dist/plugins/schema-consistency.js +29 -16
- package/dist/plugins/schema-drift-sentence.js +1 -1
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +42 -3
- package/dist/plugins/stop-then-start-sentence.js +44 -0
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +12 -2
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/tidb-pool.js +4 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/run-cancel-card.d.ts +1 -1
- package/dist/run-cancel-card.js +2 -1
- package/dist/security.d.ts +1 -1
- package/dist/session-agent.d.ts +1 -10
- package/dist/session-agent.js +0 -6
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
- package/dist/stores-sql/checkpoint-store-sql.js +106 -18
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/task-cwd.d.ts +4 -3
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +13 -7
- package/dist/tool-approval.d.ts +7 -6
- package/dist/tool-approval.js +41 -37
- package/dist/trace/engine-notice-wire.d.ts +14 -0
- package/dist/trace/engine-notice-wire.js +18 -1
- package/dist/trace/project.d.ts +4 -3
- package/dist/trace/project.js +3 -2
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/self-minted-wire.d.ts +150 -17
- package/dist/trace/self-minted-wire.js +54 -16
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/package.json +2 -2
- package/wire-changes/server@7.150.0.json +335 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { createLogger } from "../observability/logger.js";
|
|
2
2
|
import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
|
|
3
|
-
import {
|
|
3
|
+
import { engineScopeOf } from "../engine-identity.js";
|
|
4
|
+
import { sameOccupant } from "../execution-placement.js";
|
|
5
|
+
import { sessionAgentOf } from "../session-agent.js";
|
|
6
|
+
import { deletionHeadReadOf, sessionFaceOfHead } from "../session-incarnation.js";
|
|
7
|
+
import { FACES, fleetOn, listFilterOf, ownerCellOf, qualsOf, recoveredSubject, visibleOn } from "../owner-faces.js";
|
|
4
8
|
import { mintOwnedRow, partsOf, personOf, subjectOf } from "../owner-brands.js";
|
|
5
|
-
import { sessionCellFor } from "../security.js";
|
|
9
|
+
import { claimCallerOf, ownerOfCheckpointScope, sessionCellFor } from "../security.js";
|
|
6
10
|
import { openFaceFaultWire, SessionOwnerUnreadableError } from "../session-error-wire.js";
|
|
7
11
|
import { seatText } from "../trace/exit-seat.js";
|
|
8
12
|
import { actsForDeployment, explicitOperatorOk, gatedSubject, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented } from "./principal-gate.js";
|
|
@@ -96,6 +100,11 @@ export const ROUTE_FACES = {
|
|
|
96
100
|
"assistant-task-preempt": "run.preempt",
|
|
97
101
|
"assistant-task-resume": "run.resume",
|
|
98
102
|
"assistant-task-plan-review": "run.decision",
|
|
103
|
+
"workflows-list": "workflow.read",
|
|
104
|
+
"workflow-detail": "workflow.read",
|
|
105
|
+
"workflow-journal": "workflow.read",
|
|
106
|
+
"workflow-stream": "workflow.read",
|
|
107
|
+
"workflow-agent-steer": "workflow.steer",
|
|
99
108
|
"sessions-list": "session.list",
|
|
100
109
|
"sendfile-links": "list.read",
|
|
101
110
|
"fleet-stream": "list.read",
|
|
@@ -276,6 +285,9 @@ export function refuse(entry, res, reason, id) {
|
|
|
276
285
|
case "deleted_false":
|
|
277
286
|
sendJson(res, 200, { deleted: false });
|
|
278
287
|
return;
|
|
288
|
+
case "workflow_missing":
|
|
289
|
+
sendError(res, 404, "not_found.workflow", "workflow not found");
|
|
290
|
+
return;
|
|
279
291
|
case "run_exists":
|
|
280
292
|
case "attach_absent":
|
|
281
293
|
case "omit":
|
|
@@ -290,8 +302,10 @@ function rowRead(reading) {
|
|
|
290
302
|
return reading !== undefined && reading !== null;
|
|
291
303
|
}
|
|
292
304
|
function verdictOn(entry, cell) {
|
|
305
|
+
return verdictOnQuals(entry, qualsOf(entry.caller, cell));
|
|
306
|
+
}
|
|
307
|
+
function verdictOnQuals(entry, quals) {
|
|
293
308
|
const face = FACES[entry.face];
|
|
294
|
-
const quals = qualsOf(entry.caller, cell);
|
|
295
309
|
if (face.operator && quals.has("operator"))
|
|
296
310
|
return quals;
|
|
297
311
|
if (doorRefusal(face, entry.face, entry.caller) !== undefined)
|
|
@@ -325,14 +339,23 @@ export async function admitRunRow(entry, res, runStore, row, id) {
|
|
|
325
339
|
refuse(entry, res, "session_mismatch", id);
|
|
326
340
|
return undefined;
|
|
327
341
|
}
|
|
342
|
+
return observedAdmission(entry, runStore, admitted);
|
|
343
|
+
}
|
|
344
|
+
async function observedAdmission(entry, runStore, admitted) {
|
|
328
345
|
return { ...admitted, row: mintOwnedRow(await runRowAt(entry.mouth, runStore, admitted.row)) };
|
|
329
346
|
}
|
|
330
347
|
export async function admitRunById(entry, res, runStore, taskId) {
|
|
331
348
|
return admitRunRow(entry, res, runStore, await runStore.getRun(taskId), taskId);
|
|
332
349
|
}
|
|
350
|
+
export async function admitRunByIdem(entry, res, runStore, idemKey) {
|
|
351
|
+
return admitRunRow(entry, res, runStore, await runStore.getRunByIdem(idemKey));
|
|
352
|
+
}
|
|
333
353
|
export async function readVisibleRun(entry, runStore, taskId) {
|
|
334
354
|
const row = await runStore.getRun(taskId);
|
|
335
|
-
|
|
355
|
+
if (!rowRead(row))
|
|
356
|
+
return undefined;
|
|
357
|
+
const admitted = judgeRun(entry, row);
|
|
358
|
+
return admitted === undefined ? undefined : observedAdmission(entry, runStore, admitted);
|
|
336
359
|
}
|
|
337
360
|
export function seesRun(entry, owner) {
|
|
338
361
|
const verdict = verdictOn(entry, ownerCellOf(owner));
|
|
@@ -371,9 +394,25 @@ export async function admitSessionRow(entry, res, store, sessionId, reading) {
|
|
|
371
394
|
export async function judgeSessionRow(entry, store, sessionId, reading) {
|
|
372
395
|
return judge(entry, reading, await sessionCellFor(entry.face, entry.caller, store, sessionId, reading.owner));
|
|
373
396
|
}
|
|
397
|
+
export async function judgeSessionById(entry, store, sessionId) {
|
|
398
|
+
const head = await readOwnerCell(entry, sessionId, () => store.headOf(sessionId));
|
|
399
|
+
return sessionRowPresent(head) ? judgeSessionRow(entry, store, sessionId, head) : undefined;
|
|
400
|
+
}
|
|
374
401
|
export async function admitSessionById(entry, res, store, sessionId) {
|
|
402
|
+
const verdict = await judgeSessionById(entry, store, sessionId);
|
|
403
|
+
if (verdict === undefined) {
|
|
404
|
+
refuse(entry, res, "missing");
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
return deliverVerdict(entry, res, verdict);
|
|
408
|
+
}
|
|
409
|
+
export async function readVisibleSession(entry, store, sessionId) {
|
|
410
|
+
const verdict = await judgeSessionById(entry, store, sessionId);
|
|
411
|
+
return verdict === undefined || verdict === "door" || verdict === "not_visible" ? undefined : verdict;
|
|
412
|
+
}
|
|
413
|
+
export async function sessionOwnerUnchanged(entry, store, sessionId, admittedOwner) {
|
|
375
414
|
const head = await readOwnerCell(entry, sessionId, () => store.headOf(sessionId));
|
|
376
|
-
return
|
|
415
|
+
return sessionRowPresent(head) && head.owner === admittedOwner;
|
|
377
416
|
}
|
|
378
417
|
export async function judgeSession(entry, store, sessionId, reading) {
|
|
379
418
|
if (!sessionRowPresent(reading))
|
|
@@ -390,4 +429,144 @@ export function entryIsFleet(entry) {
|
|
|
390
429
|
export function routeListFilter(req, w, route) {
|
|
391
430
|
return listFilterOf(faceOfRoute(route), callerOf(req, w));
|
|
392
431
|
}
|
|
432
|
+
export function sealRouteDeps(deps) {
|
|
433
|
+
return deps;
|
|
434
|
+
}
|
|
435
|
+
export async function runFactsOf(store, taskId) {
|
|
436
|
+
const row = await store.getRun(taskId);
|
|
437
|
+
return rowRead(row) ? { status: row.status, instanceId: row.instanceId, sessionId: row.sessionId } : undefined;
|
|
438
|
+
}
|
|
439
|
+
export async function latestRunIdOf(store, sessionId) {
|
|
440
|
+
return (await store.latestRunForSession(sessionId))?.taskId;
|
|
441
|
+
}
|
|
442
|
+
export function sessionFactsReadOf(store) {
|
|
443
|
+
const headOf = store?.headOf;
|
|
444
|
+
if (typeof headOf !== "function")
|
|
445
|
+
return undefined;
|
|
446
|
+
return async (sessionId) => {
|
|
447
|
+
const head = await headOf.call(store, sessionId);
|
|
448
|
+
return rowRead(head) ? { get leafId() { return head.leafId; } } : undefined;
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
export async function readSessionAgent(store, sessionId) {
|
|
452
|
+
const placementOf = store?.placementOf;
|
|
453
|
+
if (placementOf === undefined)
|
|
454
|
+
return undefined;
|
|
455
|
+
return sessionAgentOf(await placementOf.call(store, sessionId));
|
|
456
|
+
}
|
|
457
|
+
export function occupantRecheckOf(store) {
|
|
458
|
+
const headOf = store?.headOf;
|
|
459
|
+
if (typeof headOf !== "function")
|
|
460
|
+
return undefined;
|
|
461
|
+
return async (sessionId, occupant) => {
|
|
462
|
+
const head = await headOf.call(store, sessionId);
|
|
463
|
+
if (!rowRead(head))
|
|
464
|
+
return "gone";
|
|
465
|
+
return sameOccupant(occupant, head) ? "same" : "replaced";
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
export async function engineRulesKeyOf(store, sessionId, principal) {
|
|
469
|
+
if (principal === undefined || typeof store?.headOf !== "function")
|
|
470
|
+
return principal;
|
|
471
|
+
const owner = (await store.headOf(sessionId))?.owner;
|
|
472
|
+
if (owner === undefined || owner === null)
|
|
473
|
+
return principal;
|
|
474
|
+
return personOf(recoveredSubject(owner)) === principal ? owner : principal;
|
|
475
|
+
}
|
|
476
|
+
export async function readVisibleLatestRun(entry, store, sessionId) {
|
|
477
|
+
const latest = await store.latestRunForSession(sessionId);
|
|
478
|
+
return rowRead(latest) && seesRun(entry, latest.owner) ? latest : undefined;
|
|
479
|
+
}
|
|
480
|
+
export function runListOwnerOf(filter, named) {
|
|
481
|
+
return filter.gateOwner ?? named;
|
|
482
|
+
}
|
|
483
|
+
export function listVisibleRuns(store, filter, named, query) {
|
|
484
|
+
const owner = runListOwnerOf(filter, named);
|
|
485
|
+
return store.listRuns({ ...query, ...(owner ? { owner } : {}) });
|
|
486
|
+
}
|
|
487
|
+
export function listRunsOfScope(store, scope, limit) {
|
|
488
|
+
return store.listRuns({ owner: ownerOfCheckpointScope(scope), limit });
|
|
489
|
+
}
|
|
490
|
+
export function replayHitOf(subject, config, row, clientTaskId) {
|
|
491
|
+
const sameRun = clientTaskId === undefined || clientTaskId === row.taskId;
|
|
492
|
+
return sameRun && seesRunAs("run.replay", claimCallerOf(subject, config), row.owner) ? { kind: "visible", row } : { kind: "occupied" };
|
|
493
|
+
}
|
|
494
|
+
export async function readReplayHit(subject, config, store, key, clientTaskId) {
|
|
495
|
+
const row = "idemKey" in key ? await store.getRunByIdem(key.idemKey) : await store.getRun(key.taskId);
|
|
496
|
+
return rowRead(row) ? replayHitOf(subject, config, row, clientTaskId) : undefined;
|
|
497
|
+
}
|
|
498
|
+
export async function admitSessionView(entry, res, store, sessionAudit, sessionId, readOpts) {
|
|
499
|
+
const gated = store !== undefined && typeof store.headOf === "function";
|
|
500
|
+
if (gated && (await admitSessionById(entry, res, store, sessionId)) === undefined)
|
|
501
|
+
return undefined;
|
|
502
|
+
let audit;
|
|
503
|
+
try {
|
|
504
|
+
audit = await sessionAudit(sessionId, readOpts);
|
|
505
|
+
}
|
|
506
|
+
catch (e) {
|
|
507
|
+
if (gated)
|
|
508
|
+
throw e;
|
|
509
|
+
throw new Error(`session view read failed before ownership could be established: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
510
|
+
}
|
|
511
|
+
if ((await admitSessionRow(entry, res, store, sessionId, audit)) === undefined)
|
|
512
|
+
return undefined;
|
|
513
|
+
return audit;
|
|
514
|
+
}
|
|
515
|
+
export async function readSyncGate(entry, store, sessionId) {
|
|
516
|
+
const live = await store.headOf(sessionId);
|
|
517
|
+
const face = rowRead(live) ? sessionFaceOfHead(sessionId, live) : undefined;
|
|
518
|
+
const held = face === undefined ? await deletionHeadReadOf(store)(sessionId) : undefined;
|
|
519
|
+
const occupant = face ?? (rowRead(held) ? sessionFaceOfHead(sessionId, held) : undefined);
|
|
520
|
+
const mayTouch = occupant !== undefined && (await judgeSession(entry, store, sessionId, occupant)) !== undefined;
|
|
521
|
+
return { face, occupant, mayTouch };
|
|
522
|
+
}
|
|
523
|
+
export async function toolResultRefOwned(store, ref, run) {
|
|
524
|
+
const provenance = store?.ownerOf ? await store.ownerOf(ref) : undefined;
|
|
525
|
+
return provenance !== undefined && provenance.sessionId === run.sessionId && (provenance.taskId === undefined || provenance.taskId === run.taskId);
|
|
526
|
+
}
|
|
527
|
+
export async function readVisibleLeaderRow(store, id, visible) {
|
|
528
|
+
const row = await store.getRun(id);
|
|
529
|
+
return row !== undefined && visible(row.owner) ? row : undefined;
|
|
530
|
+
}
|
|
531
|
+
export const WORKFLOW_SESSION_ASSERTION = {
|
|
532
|
+
"workflow-detail": { phase: "accept", label: "workflow.detail" },
|
|
533
|
+
"workflow-journal": { phase: "accept", label: "workflow.journal" },
|
|
534
|
+
"workflow-stream": { phase: "accept", label: "workflow.stream" },
|
|
535
|
+
"workflow-agent-steer": { phase: "accept", label: "workflow.agent-steer" },
|
|
536
|
+
};
|
|
537
|
+
export function listVisibleWorkflows(entry, store, opts) {
|
|
538
|
+
return store.listByScope(engineScopeOf(entry.caller.subject), opts);
|
|
539
|
+
}
|
|
540
|
+
export async function judgeWorkflowRun(entry, store, runId) {
|
|
541
|
+
const row = await store.get(runId);
|
|
542
|
+
if (!rowRead(row))
|
|
543
|
+
return "missing";
|
|
544
|
+
const engineCaller = { ...entry.caller, subject: recoveredSubject(engineScopeOf(entry.caller.subject)) };
|
|
545
|
+
const verdict = verdictOnQuals(entry, qualsOf(engineCaller, ownerCellOf(row.scope)));
|
|
546
|
+
if (verdict === "door" || verdict === "not_visible")
|
|
547
|
+
return verdict;
|
|
548
|
+
const rule = Object.hasOwn(WORKFLOW_SESSION_ASSERTION, entry.mouth) ? WORKFLOW_SESSION_ASSERTION[entry.mouth] : undefined;
|
|
549
|
+
if (rule === undefined)
|
|
550
|
+
throw new Error(`owner access: mouth ${entry.mouth} judges a workflow row but is not registered in WORKFLOW_SESSION_ASSERTION`);
|
|
551
|
+
return sessionAsserted(rule, entry.req, row.originatingSessionId, verdict.has("operator")) ? { row, quals: verdict } : "session_mismatch";
|
|
552
|
+
}
|
|
553
|
+
export async function admitWorkflowRun(entry, res, store, runId) {
|
|
554
|
+
const verdict = await judgeWorkflowRun(entry, store, runId);
|
|
555
|
+
switch (verdict) {
|
|
556
|
+
case "missing":
|
|
557
|
+
case "not_visible":
|
|
558
|
+
case "session_mismatch":
|
|
559
|
+
refuse(entry, res, verdict);
|
|
560
|
+
return undefined;
|
|
561
|
+
case "door":
|
|
562
|
+
sendDoorRefusal(entry, res, doorRefusal(FACES[entry.face], entry.face, entry.caller));
|
|
563
|
+
return undefined;
|
|
564
|
+
default:
|
|
565
|
+
return verdict;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
export async function admitSessionForDeletion(entry, res, store, sessionId) {
|
|
569
|
+
const placementOf = store.placementOf?.bind(store);
|
|
570
|
+
return admitSessionById(entry, res, { headOf: deletionHeadReadOf(store), ...(placementOf !== undefined ? { placementOf } : {}) }, sessionId);
|
|
571
|
+
}
|
|
393
572
|
//# sourceMappingURL=owner-access.js.map
|
package/dist/http/readiness.d.ts
CHANGED
|
@@ -72,7 +72,8 @@ export declare function sendReadyz(res: ServerResponse, v: ReadinessVerdict): vo
|
|
|
72
72
|
export declare const BILLABLE_KINDS: readonly ["submit", "hitl-answer", "resume"];
|
|
73
73
|
export type BillableKind = (typeof BILLABLE_KINDS)[number];
|
|
74
74
|
/**
|
|
75
|
-
* 计费面的拒因(**数组序 = 优先序**):就绪原因({@link READINESS_REASONS})∪ `lease_expired`(服务租约过期且中心不可达满 `T_max`
|
|
75
|
+
* 计费面的拒因(**数组序 = 优先序**):就绪原因({@link READINESS_REASONS})∪ `lease_expired`(服务租约过期且中心不可达满 `T_max` —— 含从起步起一直没听到
|
|
76
|
+
* 中心的成员:推定在围栏内,钟从本进程起点)。
|
|
76
77
|
* `lease_expired` 刻意不是就绪原因:它不翻 `/readyz`(中心不可达不是本实例的病,摘它出路由只会把取消 / HITL / 读面一起摘掉)。
|
|
77
78
|
*/
|
|
78
79
|
export declare const ADMISSION_REFUSALS: readonly ["draining", "model_roster_pending", "lease_expired"];
|
|
@@ -80,7 +81,8 @@ export type AdmissionRefusal = (typeof ADMISSION_REFUSALS)[number];
|
|
|
80
81
|
/**
|
|
81
82
|
* 计费面请求的**唯一**准入判(dispatch 的计费门、a2a `message/send`、PARKED 赎回席同调):按 {@link ADMISSION_REFUSALS} 序找第一条
|
|
82
83
|
* 成立且按 {@link ADMISSION_TABLE} 拒这一种的拒因;没有 ⇒ `undefined`(放行)。入参是**判决**而不是 deps:就绪判由调用点经唯一谓词
|
|
83
|
-
* {@link readinessOf} 现读(模块边界上那一只 —— 谓词单源钉注入它,任何读点自己读 `drainState` 都会当场红)
|
|
84
|
+
* {@link readinessOf} 现读(模块边界上那一只 —— 谓词单源钉注入它,任何读点自己读 `drainState` 都会当场红),租约读数经唯一读者
|
|
85
|
+
* `fleet/engine-signal.ts` 的 `memberLeaseOf`(只有 fleet 成员有租约;成员没有任何中心读数之前是推定的 `expired`,同一臂)。
|
|
84
86
|
*/
|
|
85
87
|
export declare function admissionRefusalOf(verdict: ReadinessVerdict, lease: ServingLeaseView | undefined, kind: BillableKind): BillableRefusal | undefined;
|
|
86
88
|
/** 计费提交门的拒绝(首条 reason 定码)。`retryAfterSec` 由发送形(`sendBillableRefusal`)镜像成头;交回
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { openCheckpointCtx } from "./checkpoint-ctx.js";
|
|
2
2
|
import { storeFaultClassOf } from "../plugins/store-fault.js";
|
|
3
3
|
import { refusalBody } from "./refusal-body.js";
|
|
4
|
+
import { frozenProjectContextOf } from "../project-context.js";
|
|
4
5
|
export async function readResumeCtx(load, opening) {
|
|
5
6
|
const read = await readStoredCtx(load);
|
|
6
7
|
if (read.kind !== "ok")
|
|
7
8
|
return read;
|
|
8
9
|
const opened = openCheckpointCtx(read.ctx, opening.opener, opening.sessionId);
|
|
9
|
-
|
|
10
|
+
if (opened.kind !== "ok")
|
|
11
|
+
return { kind: "unsealable", field: opened.field, reason: opened.reason };
|
|
12
|
+
const frozen = frozenProjectContextOf(opened.ctx.projectContext);
|
|
13
|
+
switch (frozen.kind) {
|
|
14
|
+
case "absent":
|
|
15
|
+
return { kind: "ok", ctx: opened.ctx };
|
|
16
|
+
case "malformed":
|
|
17
|
+
return { kind: "corrupt", where: `checkpoint ctx for session ${JSON.stringify(opening.sessionId)}: projectContext` };
|
|
18
|
+
case "ok":
|
|
19
|
+
return { kind: "ok", ctx: { ...opened.ctx, projectContext: frozen.context } };
|
|
20
|
+
default: {
|
|
21
|
+
const unhandled = frozen;
|
|
22
|
+
throw new Error(`resume-ctx-read: unplaced frozen project context reading ${JSON.stringify(unhandled)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
10
25
|
}
|
|
11
26
|
async function readStoredCtx(load) {
|
|
12
27
|
let stored;
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { uuidv7, resumeWithVerification, resumeCascade, CheckpointError, judgeCheckpointRow, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY } from "@sema-agent/core";
|
|
4
4
|
import { decideParkedAgent, deliverToHoldingRun, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
5
5
|
import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
|
|
6
|
-
import {
|
|
6
|
+
import { runFactsOf, sealRouteDeps, sessionFactsReadOf } from "./owner-access.js";
|
|
7
7
|
import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../session-workspace.js";
|
|
8
8
|
const DELIVERY_NOT_RECORDED = "workflow park decide: this delivery no longer holds the host session (its claim was released) — the decision was not recorded and this delivery was not started";
|
|
9
9
|
import { HttpError, checkpointPartsOf, decodeCheckpointScope, scopeCellOf, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
@@ -15,7 +15,7 @@ import { commitParkRow } from "../park-row-commit.js";
|
|
|
15
15
|
import { atHostBoundary, failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane, outwardThrownMessage } from "../terminal.js";
|
|
16
16
|
import { voidVerifierPauseCard } from "../verifier-pause-card.js";
|
|
17
17
|
import { buildCancelledLedgerRow, CANCELLED_CODE, CARD_SETTLED_BY_CANCEL_MESSAGE, createRunAbort, mintCancelledResult, withCardSettledByCancelMessage } from "../run-cancel-context.js";
|
|
18
|
-
import {
|
|
18
|
+
import { createLegCtxClaim, isTerminalRunStatus } from "../plugins/store-contracts.js";
|
|
19
19
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
20
20
|
import { recordResumeResourceWindow } from "../resource-window.js";
|
|
21
21
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
@@ -66,7 +66,7 @@ import { deploymentListAsks } from "../approval.js";
|
|
|
66
66
|
import { isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
67
67
|
import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnreadableAnswer, resumeCtxUnsealableAnswer, resumeStoreFailureOf } from "./resume-ctx-read.js";
|
|
68
68
|
import { objectiveWithheldIn } from "./submit-ctx.js";
|
|
69
|
-
import { buildCheckpointVersionKeys, resumeAssemblyRefusalAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields
|
|
69
|
+
import { buildCheckpointVersionKeys, resumeAssemblyRefusalAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields } from "./checkpoint-ctx.js";
|
|
70
70
|
import { refusalBody, resumeRefusalFacts } from "./refusal-body.js";
|
|
71
71
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
72
72
|
import { restateResumeCtx } from "./resume-restatement.js";
|
|
@@ -580,7 +580,7 @@ export function createResumeLegs(ctx) {
|
|
|
580
580
|
const liveReceiptOf = async (held) => {
|
|
581
581
|
if (held === null || held.fingerprint !== fingerprint)
|
|
582
582
|
return undefined;
|
|
583
|
-
const run = await runStore
|
|
583
|
+
const run = await runFactsOf(runStore, held.deliveredTaskId);
|
|
584
584
|
return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId, NO_SETTLEMENT_EVIDENCE) : undefined;
|
|
585
585
|
};
|
|
586
586
|
const replay = await liveReceiptOf(await decisions.get(token));
|
|
@@ -690,6 +690,7 @@ export function createResumeLegs(ctx) {
|
|
|
690
690
|
leg: "resume",
|
|
691
691
|
onNotice: (n) => engineNotices.push(n),
|
|
692
692
|
...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
|
|
693
|
+
...(ctx.projectContext !== undefined ? { projectContext: ctx.projectContext } : {}),
|
|
693
694
|
lease,
|
|
694
695
|
execution,
|
|
695
696
|
});
|
|
@@ -762,10 +763,10 @@ export function createResumeLegs(ctx) {
|
|
|
762
763
|
};
|
|
763
764
|
const commitResumeCtx = () => Promise.resolve().then(async () => {
|
|
764
765
|
const sealed = sealCheckpointCtx({ ...resumeCtx.ctx, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, resumeCtx.sessionId);
|
|
765
|
-
const claim =
|
|
766
|
+
const claim = heldLeg !== undefined && taskId !== undefined && deps.runStore !== undefined ? createLegCtxClaim(deps.runStore, taskId, heldLeg) : undefined;
|
|
766
767
|
const landed = await deps.checkpointStore.putCtx(resumeCtx.sessionId, sealed, claim);
|
|
767
768
|
if (landed === "claim_released")
|
|
768
|
-
resumeCtxNotPersisted("claim_released: this
|
|
769
|
+
resumeCtxNotPersisted("claim_released: this leg no longer holds the run (settled, reaped, or claimed again by a later leg before the write-back landed) — nothing written");
|
|
769
770
|
}).catch((e) => resumeCtxNotPersisted(errorText(e)));
|
|
770
771
|
const legKey = createHash("sha256").update(String(token)).digest("hex");
|
|
771
772
|
const streamApprovalOn = resolveStreamApprovalGate({
|
|
@@ -1148,9 +1149,9 @@ export function createResumeLegs(ctx) {
|
|
|
1148
1149
|
cancelLedgerAppend = append;
|
|
1149
1150
|
await openEngineNoticePort(append);
|
|
1150
1151
|
const anchorOwner = subject ?? null;
|
|
1151
|
-
const
|
|
1152
|
-
const captureTurnAnchor = deps.resumeAnchorStore &&
|
|
1153
|
-
? async (eventId) => { const leaf = (await
|
|
1152
|
+
const sessionFactsOf = sessionFactsReadOf(deps.sessionStorage);
|
|
1153
|
+
const captureTurnAnchor = deps.resumeAnchorStore && sessionFactsOf
|
|
1154
|
+
? async (eventId) => { const leaf = (await sessionFactsOf(sessionId))?.leafId; if (leaf)
|
|
1154
1155
|
await deps.resumeAnchorStore.put(sessionId, eventId, leaf, anchorOwner); }
|
|
1155
1156
|
: undefined;
|
|
1156
1157
|
const captureUserMessageAnchor = deps.resumeAnchorStore && taskId && freshRunClaimed
|
|
@@ -1416,9 +1417,12 @@ export function createResumeLegs(ctx) {
|
|
|
1416
1417
|
const withElicitation = () => deps.elicitation && taskId
|
|
1417
1418
|
? deps.elicitation.runWithContext({ taskId, owner: subject ?? null, transport: hitlTransport, abortSignal: cancelCtrl.signal }, withApproval)
|
|
1418
1419
|
: withApproval();
|
|
1420
|
+
const withQuestion = () => deps.question && taskId
|
|
1421
|
+
? deps.question.runWithContext({ taskId, owner: subject ?? null, transport: hitlTransport, abortSignal: cancelCtrl.signal }, withElicitation)
|
|
1422
|
+
: withElicitation();
|
|
1419
1423
|
return await (deps.sendUserFile && taskId
|
|
1420
|
-
? deps.sendUserFile.runWithContext({ taskId, emit: async (frame) => { const { type, ...rest } = frame; await append(type, rest); } },
|
|
1421
|
-
:
|
|
1424
|
+
? deps.sendUserFile.runWithContext({ taskId, emit: async (frame) => { const { type, ...rest } = frame; await append(type, rest); } }, withQuestion)
|
|
1425
|
+
: withQuestion());
|
|
1422
1426
|
})));
|
|
1423
1427
|
}
|
|
1424
1428
|
catch (e) {
|
|
@@ -1877,7 +1881,7 @@ export function createResumeLegs(ctx) {
|
|
|
1877
1881
|
throw new Error("drivePlanReview: permissionModeAfter was passed for a task whose stored permission mode is not \"plan\" — the caller must refuse this before any state changes (see the plan_review route's pre-nonce gate)");
|
|
1878
1882
|
}
|
|
1879
1883
|
const approvedMode = planReviewDecisionExitsPlan(decision) && effMode === "plan" ? (permissionModeAfter ?? "default") : undefined;
|
|
1880
|
-
const rebuildCtx = approvedMode === undefined ? ctx :
|
|
1884
|
+
const rebuildCtx = approvedMode === undefined ? ctx : { ...ctx, body: { ...ctx.body, permissionMode: approvedMode } };
|
|
1881
1885
|
const resolved = await resolveResumeSpecOrBlocked(rebuildCtx, req, auth);
|
|
1882
1886
|
if (!resolved.ok)
|
|
1883
1887
|
return resolved.answer;
|
|
@@ -1907,10 +1911,10 @@ export function createResumeLegs(ctx) {
|
|
|
1907
1911
|
activeTaskIdOf: async (sessionId) => deps.runStore?.getActiveTaskId?.(sessionId),
|
|
1908
1912
|
liveRuns: steerableRuns,
|
|
1909
1913
|
inbox: deps.workflowCompletionInbox,
|
|
1910
|
-
|
|
1914
|
+
sessionStorage: deps.sessionStorage,
|
|
1911
1915
|
});
|
|
1912
1916
|
const routeCtxBase = {
|
|
1913
|
-
deps,
|
|
1917
|
+
deps: sealRouteDeps(deps),
|
|
1914
1918
|
registry: { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters },
|
|
1915
1919
|
helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, admitRun, safeDecode },
|
|
1916
1920
|
local: { images: createImagesLocal(deps), sessions: createSessionsLocal(), sessionSync: createSessionSyncLocal(), capabilities: createCapabilitiesLocal() },
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ import type { RememberLaneCannotRecord } from "../parked-decide.js";
|
|
|
39
39
|
import type { RowWayOut } from "./resume-legs.js";
|
|
40
40
|
import type { KeyCaller } from "../owner-faces.js";
|
|
41
41
|
import type { Subject } from "../owner-brands.js";
|
|
42
|
+
import type { RouteBackend, RouteRunStore, RouteSessionStore } from "./owner-access.js";
|
|
42
43
|
/** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
|
|
43
44
|
* [ref]:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
|
|
44
45
|
* {@link RunAbort} 句柄上(`inflightRuns` 的值),同寿同删,不再有一张与腿不同寿的旁表。 */
|
|
@@ -199,14 +200,6 @@ export interface CheckpointCtx {
|
|
|
199
200
|
* 传原 ctx / 其展开 ⇒ 标随行。缺席 = 不是无人值守腿(HTTP 提交腿;7.106.0 前落盘的行 —— 那时这些腿不铸模式,checkpoint 里也没有)。
|
|
200
201
|
*/
|
|
201
202
|
unattendedLeg?: true;
|
|
202
|
-
/**
|
|
203
|
-
* 体里**模式词的读法记号**(闭集只有当前一词 `"entry_word"`):在场 = 这一行体的顶层 `permissionMode` 是这条 run 模式词唯一的源(提交入口盖过运维
|
|
204
|
-
* `AUTONOMY=plan` 的地板;`plan_review` 写回改它),续跑照它读。**服务端写、不来自请求体**。缺席(7.121.x 及以前写下的行 —— 那时运维的 plan 由治理层每段重钳)
|
|
205
|
-
* 而本部署此刻有入口地板、体的词被地板钳会变 ⇒ 续跑腿响亮拒、行字节不动(规则与删除条件的属主 = `http/checkpoint-ctx.ts` 顶注「模式词问」)。
|
|
206
|
-
* 写点两个(写模式词的人盖记号,同模块):`buildCheckpointCtx`(每一次新的持久化写都写)与 `withPermissionModeRewritten`(`plan_review` 改词的写回);
|
|
207
|
-
* 不改词的续跑腿写回传原 ctx ⇒ 记号随行。
|
|
208
|
-
*/
|
|
209
|
-
permissionModeReading?: import("./checkpoint-ctx.js").PermissionModeReading;
|
|
210
203
|
/**
|
|
211
204
|
* 🔴 [ref] —— **写这份上下文的服务版本**(`package.json` 的 `version`),**服务端写、不来自请求体**(与 {@link unattendedLeg} 同为 `body` 的兄弟键)。
|
|
212
205
|
* 写点两个:`buildCheckpointCtx`(每一次新的持久化写)与续跑腿的写回({@link DriveResumeArgs.resumeCtx} 的唯一写点,CAS 赢点盖本进程的版本 —— 接下来写下一张停车行的就是它)。
|
|
@@ -226,6 +219,14 @@ export interface CheckpointCtx {
|
|
|
226
219
|
* 缺席 = 体里的目标就是交付的那一份(腿上没有屏)。
|
|
227
220
|
*/
|
|
228
221
|
promptScreened?: true;
|
|
222
|
+
/**
|
|
223
|
+
* 这条 run 继承到的**项目上下文**(`project-context.ts` 的 {@link import("../project-context.js").ProjectContext}:项目 id · 继承的 `cwd` · 项目简报)——
|
|
224
|
+
* 新提交腿在装配器头部判一次,**服务端写、不来自请求体**(与 {@link memoryScope} 同为 `body` 的兄弟键,先例同形:提交腿推导、落盘,续跑原样读回)。
|
|
225
|
+
* 写点:`buildCheckpointCtx`(提交腿那一笔,经 `persistSubmissionCtx` 读这条腿的部署事实);leader worker 的写者不带(它的 spec 不经新提交腿的判)。续跑腿的写回传原 ctx / 其展开 ⇒ 随行。
|
|
226
|
+
* 读者只有一只:`resume-legs.ts` `resolveResumeSpec` 把它原样递给装配器(`opts.projectContext`)—— 续跑腿不再读登记簿、不再推观者(停驻期间登记被改 / 被删 / 看不见了,续跑照旧用这一份)。
|
|
227
|
+
* 缺席 = 没继承(含 7.150.0 之前写下的行:不回填,续跑不继承 = 那时的行为)。
|
|
228
|
+
*/
|
|
229
|
+
projectContext?: import("../project-context.js").ProjectContext;
|
|
229
230
|
}
|
|
230
231
|
/**
|
|
231
232
|
* `/decide` 第三条车道交给宿主投递腿(wake:`DriveResumeArgs` / fresh:`DurableRunLegArgs`)的**一件**:这一跑在内存里带着的决定,
|
|
@@ -442,13 +443,13 @@ export interface RouteLegs {
|
|
|
442
443
|
/** POST /v1/tasks · /v1/tasks/stream · /v1/runs 的共同前段:读体 → 校验 → resolveSpec。null = 已应答(400/…)。
|
|
443
444
|
*
|
|
444
445
|
* `body` = **已解析好的**请求体,给那些体不是「这条 HTTP 请求的 JSON 正文」的调用方
|
|
445
|
-
* (
|
|
446
|
+
* ([ref] 车2:A2A JSON-RPC 腿已经把正文读成了一条 `message/send` 请求,再 `readJson(req)`
|
|
446
447
|
* 一次只会读到空流)。缺席 ⇒ 照旧自己读体,既有三个调用方一字不改。
|
|
447
448
|
* ⚠️ 这是**入口形**的分歧,不是校验面的分歧:传进来的体与自己读的体走**同一段**校验/授权/
|
|
448
449
|
* resolveSpec —— 分叉出第二条校验路径正是本参数存在的理由的反面。
|
|
449
450
|
*
|
|
450
451
|
* `onTypedFailure` = 「typed 拒绝**别写响应**,交回给我」。默认(缺席)行为不变:`HttpError` 走
|
|
451
|
-
* `sendError` 连 `extra` 一起回显。
|
|
452
|
+
* `sendError` 连 `extra` 一起回显。[ref] 车2 的 A2A 腿必须传它 —— 那些 `extra`(尤其
|
|
452
453
|
* `scenario_unknown` 的**全部场景名**)对壳是指路材料,对一个外部 peer 是内部词表外泄。 */
|
|
453
454
|
prepareSpec(req: IncomingMessage, res: ServerResponse, body?: TaskRequestBody, onTypedFailure?: (err: import("../security.js").HttpError) => void,
|
|
454
455
|
/** [ref] 件C:**腿身份**(不是能力表态)。`liveStream: true` 只由 `POST /v1/tasks/stream` 的装配点按
|
|
@@ -600,8 +601,19 @@ export declare function sendResumeOutcome(res: ServerResponse, out: {
|
|
|
600
601
|
}, logger: {
|
|
601
602
|
info?: (msg: string, fields?: Record<string, unknown>) => void;
|
|
602
603
|
} | undefined, extraBody?: Record<string, unknown>): void;
|
|
604
|
+
/**
|
|
605
|
+
* 🔴 **路由层的服务 deps(类型墙,[ref] 第二阶段)**:run 店 / 会话店上带属主的读口(按 id / 按键 / 按会话 / 列表 —— `owner-access.ts` 的 {@link RouteRunStore} /
|
|
606
|
+
* {@link RouteSessionStore})、会话视图读 `sessionAudit` 与后端铸这两只店的工厂(`backend.run()` / `backend.session()`,{@link RouteBackend})在型上调不了:路由在不在照判(能力事实只问方法在不在,见证随视图封着),带属主的行只经属主存取器读。
|
|
607
|
+
* 值上就是服务的那一只 deps,只在 `owner-access.ts` 的 `sealRouteDeps` 铸一次;视图是完整 deps 的子型(交给持认领腿的写口照常)。
|
|
608
|
+
*/
|
|
609
|
+
export type RouteDeps = Omit<FlatServiceDeps, "runStore" | "sessionStorage" | "sessionAudit" | "backend"> & {
|
|
610
|
+
runStore?: RouteRunStore;
|
|
611
|
+
sessionStorage?: RouteSessionStore;
|
|
612
|
+
sessionAudit?: never;
|
|
613
|
+
backend?: RouteBackend;
|
|
614
|
+
};
|
|
603
615
|
export interface RouteCtxBase {
|
|
604
|
-
deps:
|
|
616
|
+
deps: RouteDeps;
|
|
605
617
|
registry: RunRegistry;
|
|
606
618
|
helpers: RouteHelpers;
|
|
607
619
|
local: RouteLocals;
|
|
@@ -4,12 +4,13 @@ import { memoRedactedRead, redactErrorMessage } from "../../observability/run-te
|
|
|
4
4
|
import { isParkedRunStatus, isTerminalRunStatus } from "../../plugins/store-contracts.js";
|
|
5
5
|
import { HttpError } from "../../security.js";
|
|
6
6
|
import { idemCallerOf, idemIdentityOf, idemKeyOf, inflightKeyOf } from "../idempotency.js";
|
|
7
|
-
import {
|
|
7
|
+
import { answerReplay, isRunExistsConflictBody } from "../submit-replay.js";
|
|
8
8
|
import { isActiveRunConflictBody } from "../active-run-conflict.js";
|
|
9
9
|
import { sendJson, sendError, sendHttpError } from "../send.js";
|
|
10
10
|
import { admissionRefusalOf, readinessOf, sendBillableRefusal } from "../readiness.js";
|
|
11
|
+
import { memberLeaseOf } from "../../fleet/engine-signal.js";
|
|
11
12
|
import { gatedPerson, gatedSubject, sendPrincipalRefusal, verifiedIdentitySource } from "../principal-gate.js";
|
|
12
|
-
import { callerOf, protocolEntry, readVisibleRun, runRowAt } from "../owner-access.js";
|
|
13
|
+
import { callerOf, protocolEntry, readReplayHit, readVisibleRun, runRowAt } from "../owner-access.js";
|
|
13
14
|
import { assertNever } from "../route-ctx.js";
|
|
14
15
|
import * as facts from "../../capability-facts.js";
|
|
15
16
|
import { createDurableRun } from "./runs.js";
|
|
@@ -181,7 +182,7 @@ function rpcErrorFromTypedFailure(err) {
|
|
|
181
182
|
}
|
|
182
183
|
function submitUnavailable(res, ctx) {
|
|
183
184
|
const { deps } = ctx;
|
|
184
|
-
const refusal = admissionRefusalOf(readinessOf(deps), deps
|
|
185
|
+
const refusal = admissionRefusalOf(readinessOf(deps), memberLeaseOf(deps), "submit");
|
|
185
186
|
if (refusal === undefined)
|
|
186
187
|
return false;
|
|
187
188
|
sendBillableRefusal(res, refusal);
|
|
@@ -197,15 +198,14 @@ function readBlocking(params) {
|
|
|
197
198
|
throw new RpcError(JSONRPC_INVALID_PARAMS, "params.configuration.blocking must be a boolean");
|
|
198
199
|
return c.blocking;
|
|
199
200
|
}
|
|
200
|
-
async function waitForRun(ctx, store, res, taskId, budgetMs) {
|
|
201
|
+
async function waitForRun(ctx, entry, store, res, taskId, budgetMs) {
|
|
201
202
|
const { deps } = ctx;
|
|
202
203
|
const deadline = Date.now() + budgetMs;
|
|
203
204
|
let last = null;
|
|
204
205
|
for (;;) {
|
|
205
206
|
let run;
|
|
206
207
|
try {
|
|
207
|
-
|
|
208
|
-
run = raw ? await runRowAt("run.a2a", store, raw) : null;
|
|
208
|
+
run = (await readVisibleRun(entry, store, taskId))?.row ?? null;
|
|
209
209
|
}
|
|
210
210
|
catch (e) {
|
|
211
211
|
deps.logger?.warn?.("a2a_serve_wait_read_failed", { error: e?.message });
|
|
@@ -254,10 +254,11 @@ async function handleMessageSend({ req, res, ctx, params, serve, runStore }) {
|
|
|
254
254
|
...(contextId !== undefined ? { sessionId: contextId } : {}),
|
|
255
255
|
...(scenario !== undefined ? { scenario } : {}),
|
|
256
256
|
};
|
|
257
|
-
const keyed = (await
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
257
|
+
const keyed = (await readReplayHit(submitter, deps.config, runStore, { idemKey: idem.key }, body.taskId).catch(() => undefined)) ??
|
|
258
|
+
(await readReplayHit(submitter, deps.config, runStore, { taskId: body.taskId }, body.taskId).catch(() => undefined));
|
|
259
|
+
const replay = keyed ? await answerReplay(keyed, { subject: submitter, clientTaskId: body.taskId, idem }, "durable", runStore) : undefined;
|
|
260
|
+
if (keyed?.kind === "visible" && replay?.status === 202) {
|
|
261
|
+
return taskFromRun(await runRowAt("run.a2a", runStore, keyed.row));
|
|
261
262
|
}
|
|
262
263
|
const inflight = replay === undefined ? ctx.registry.idemCache.peek(inflightKeyOf(idem, "durable", body.taskId)) : undefined;
|
|
263
264
|
let receipt;
|
|
@@ -298,7 +299,7 @@ async function handleMessageSend({ req, res, ctx, params, serve, runStore }) {
|
|
|
298
299
|
}
|
|
299
300
|
const taskId = receiptBody.taskId;
|
|
300
301
|
const sessionId = receiptBody.sessionId;
|
|
301
|
-
const run = await waitForRun(ctx, runStore, res, taskId, blocking ? serve.blockingWaitMs : 0);
|
|
302
|
+
const run = await waitForRun(ctx, protocolEntry(req, deps, "run.a2a", callerOf(req, deps)), runStore, res, taskId, blocking ? serve.blockingWaitMs : 0);
|
|
302
303
|
if (run)
|
|
303
304
|
return taskFromRun(run);
|
|
304
305
|
return {
|
|
@@ -330,10 +331,10 @@ function durableIdempotencyTaskId(scope, messageId) {
|
|
|
330
331
|
async function handleTasksGet({ req, ctx, params, runStore }) {
|
|
331
332
|
const { deps } = ctx;
|
|
332
333
|
const id = asNonEmptyString(params.id, "params.id", MAX_TASK_ID_CHARS);
|
|
333
|
-
const
|
|
334
|
-
if (!
|
|
334
|
+
const admitted = await readVisibleRun(protocolEntry(req, deps, "run.a2a", callerOf(req, deps)), runStore, id);
|
|
335
|
+
if (!admitted)
|
|
335
336
|
throw new RpcError(A2A_TASK_NOT_FOUND, A2A_TASK_NOT_FOUND_MESSAGE);
|
|
336
|
-
return taskFromRun(
|
|
337
|
+
return taskFromRun(admitted.row);
|
|
337
338
|
}
|
|
338
339
|
async function handleExtendedCard({ req, ctx, serve }) {
|
|
339
340
|
return buildAgentCard(serve, cardReaderOf(req, ctx.deps.config), A2A_METHODS);
|
|
@@ -2,12 +2,13 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
2
2
|
import type { QuestionAnswer } from "@sema-agent/core";
|
|
3
3
|
import type { CheckpointStoreFull } from "../../plugins/store-backend.js";
|
|
4
4
|
import { type ToolApprovalCoordinator } from "../../tool-approval.js";
|
|
5
|
+
import { type RouteSessionStore } from "../owner-access.js";
|
|
5
6
|
import { type GovernancePosture } from "../../runtime-governance.js";
|
|
6
7
|
import { type GateIntentView } from "../../approval.js";
|
|
7
8
|
import { type RouteCtx, type RouteDecl, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
8
9
|
import { type KeyClosureIssue } from "../../request-key-closure.js";
|
|
9
10
|
import { type PeerInboundPendingRow } from "../../peer-inbound-face.js";
|
|
10
|
-
import
|
|
11
|
+
import type { SessionAgent } from "../../session-agent.js";
|
|
11
12
|
/** 族的成员 = 路由表行 id(改名 / 删行 ⇒ 下表的键当场编译红)。 */
|
|
12
13
|
export type DecisionDoorId = Extract<ApprovalsAssistantRouteId, "approval-decide" | "assistant-task-plan-review">;
|
|
13
14
|
/** 一道门的静态描述 —— 只写**这道门自己**的东西;路径、路径段、别门的键一概不在这里写(从来源读)。 */
|
|
@@ -164,7 +165,7 @@ peerInbound: (() => readonly PeerInboundPendingRow[]) | undefined,
|
|
|
164
165
|
* 🔴 [ref]:停驻行 `agent` 的会话店面 —— 与 `GET /v1/approvals` 同一只(`deps.sessionStorage`),经同一只整批投影 {@link projectDurableQueue}。
|
|
165
166
|
* `undefined` / 店不交 `placementOf` = 这台部署不存委派元组 ⇒ 没有一帧带 `agent`(帧字节与 [ref] 之前逐字相同)。位置在 `scope` 之前、与 `live` 并列:每个生产调用点都必须表态。
|
|
166
167
|
*/
|
|
167
|
-
sessions:
|
|
168
|
+
sessions: Pick<RouteSessionStore, "placementOf"> | undefined, scope: string | undefined,
|
|
168
169
|
/** [ref]②:出身归因的部署侧一半 + 三张审批名单(问的属主)。缺席 ⇒ 归不出治理出身(键缺席,与 best-effort 同方向)、没有一行读作
|
|
169
170
|
* 部署名单的问(记不住,方向是多问)。**位置在 `pollMs` 之前**是刻意的:`pollMs` 只有测试传,而本参数是每个生产调用点都必须给的那一个。 */
|
|
170
171
|
governance?: PendingProjectionDeployment, pollMs?: number): Promise<void>;
|