@sema-agent/server 7.143.0 → 7.145.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 +295 -0
- package/README.md +3 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +34 -16
- package/dist/approval-card.d.ts +31 -14
- package/dist/approval-card.js +8 -8
- package/dist/audit.d.ts +2 -21
- package/dist/audit.js +0 -9
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/runner-deps.d.ts +10 -5
- package/dist/boot/runner-deps.js +5 -4
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +3 -3
- package/dist/boot/shutdown.d.ts +1 -1
- package/dist/boot/shutdown.js +4 -2
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +4 -9
- package/dist/capabilities/scenarios.d.ts +14 -18
- package/dist/capabilities/scenarios.js +1 -2
- package/dist/config-d-migration.d.ts +3 -0
- package/dist/delegated-session-owner.d.ts +33 -151
- package/dist/delegated-session-owner.js +1 -83
- package/dist/entry-mode-clamp-notice.d.ts +1 -1
- package/dist/execution-placement.d.ts +7 -4
- package/dist/fleet/fleet-bus.d.ts +13 -3
- package/dist/fleet/fleet-bus.js +17 -3
- package/dist/hooks/classic-stations.d.ts +3 -1
- package/dist/hooks/classic-stations.js +2 -0
- package/dist/http/admission.js +2 -1
- package/dist/http/checkpoint-ctx.d.ts +1 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/durable-run-leg.js +7 -6
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +7 -3
- package/dist/http/refusal-body.js +1 -0
- package/dist/http/resume-legs.js +16 -7
- package/dist/http/route-ctx.d.ts +15 -0
- package/dist/http/routes/approvals-assistant.d.ts +20 -1
- package/dist/http/routes/approvals-assistant.js +28 -6
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +5 -2
- package/dist/http/routes/session-sync.js +12 -0
- package/dist/http/routes/tasks.js +12 -9
- package/dist/http/run-meta.d.ts +14 -7
- package/dist/http/run-meta.js +5 -2
- package/dist/http/server.d.ts +13 -5
- package/dist/http/server.js +3 -0
- package/dist/http/submit-ctx.d.ts +12 -0
- package/dist/http/submit-ctx.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lane-home-probe.d.ts +15 -0
- package/dist/lane-home-probe.js +4 -0
- package/dist/observability/corrupt-read-seat.d.ts +5 -9
- package/dist/observability/corrupt-read-seat.js +1 -2
- package/dist/observability/fail-open.d.ts +26 -10
- package/dist/observability/fail-open.js +26 -10
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +2 -2
- package/dist/parked-revive-surface.d.ts +6 -7
- package/dist/peer-inbound-face.d.ts +29 -34
- package/dist/peer-inbound-face.js +28 -63
- package/dist/plugins/approval-ask-store-file.d.ts +16 -2
- package/dist/plugins/approval-ask-store-file.js +21 -1
- package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
- package/dist/plugins/approval-ask-store-memory.js +11 -8
- package/dist/plugins/caching-session-store.d.ts +3 -3
- package/dist/plugins/caching-session-store.js +3 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +62 -37
- package/dist/plugins/checkpoint-store-sql.js +123 -40
- package/dist/plugins/file-run-store.d.ts +13 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/fork-routing-session-store.d.ts +8 -1
- package/dist/plugins/fork-routing-session-store.js +6 -0
- package/dist/plugins/local-checkpoint-store.d.ts +17 -8
- package/dist/plugins/local-checkpoint-store.js +26 -7
- package/dist/plugins/local-session-store.d.ts +105 -34
- package/dist/plugins/local-session-store.js +168 -65
- package/dist/plugins/local-session-window.d.ts +4 -2
- package/dist/plugins/memory-run-store.d.ts +2 -0
- package/dist/plugins/memory-run-store.js +6 -0
- package/dist/plugins/one-time-migrations.d.ts +12 -12
- package/dist/plugins/one-time-migrations.js +13 -13
- package/dist/plugins/pg-session-storage.d.ts +1 -9
- package/dist/plugins/pg-session-storage.js +2 -19
- package/dist/plugins/remote-env-host.d.ts +8 -4
- package/dist/plugins/remote-env-host.js +7 -9
- package/dist/plugins/remote-shell.js +12 -2
- package/dist/plugins/run-store-sql.d.ts +4 -0
- package/dist/plugins/run-store-sql.js +3 -0
- package/dist/plugins/schema-consistency.d.ts +5 -1
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-ownership.d.ts +9 -0
- package/dist/plugins/session-ownership.js +6 -0
- package/dist/plugins/session-store.d.ts +13 -16
- package/dist/plugins/session-store.js +9 -10
- package/dist/plugins/sql-json-column.d.ts +24 -2
- package/dist/plugins/sql-json-column.js +38 -3
- package/dist/plugins/store-backend.js +3 -2
- package/dist/plugins/tidb-session-store.d.ts +2 -14
- package/dist/plugins/tidb-session-store.js +2 -20
- package/dist/prompt-screen.d.ts +21 -0
- package/dist/prompt-screen.js +8 -0
- package/dist/rules-consent.d.ts +63 -51
- package/dist/rules-consent.js +6 -10
- package/dist/run-acceptance.d.ts +40 -10
- package/dist/run-acceptance.js +38 -5
- package/dist/run-local.js +2 -1
- package/dist/runs.js +5 -1
- package/dist/security.d.ts +10 -7
- package/dist/session-agent.d.ts +39 -0
- package/dist/session-agent.js +16 -0
- package/dist/session-error-wire.d.ts +25 -26
- package/dist/session-error-wire.js +4 -5
- package/dist/session-incarnation.d.ts +2 -2
- package/dist/session-notify-door.d.ts +2 -2
- package/dist/session-notify-door.js +5 -1
- package/dist/tool-approval.d.ts +3 -1
- package/dist/tool-approval.js +47 -18
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -1
- package/dist/trace/ledger-sink.js +12 -4
- package/dist/trace/model-segment.d.ts +26 -6
- package/dist/trace/model-segment.js +58 -9
- package/dist/trace/project.d.ts +8 -2
- package/dist/trace/project.js +6 -4
- package/dist/trace/redact.d.ts +77 -2
- package/dist/trace/redact.js +234 -99
- package/dist/trace/self-minted-wire.d.ts +160 -9
- package/dist/trace/self-minted-wire.js +52 -3
- package/package.json +3 -3
- package/wire-changes/server@7.144.0.json +291 -0
- package/wire-changes/server@7.145.0.json +146 -0
- package/dist/session-records-skipped-notice.d.ts +0 -21
- package/dist/session-records-skipped-notice.js +0 -27
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* 它是 handler 体的第二个入参 —— 触发之后 handler 不再起新事、在飞的子进程要叫停(`hooks/hook-runner.ts` 的命令钩子腿)。
|
|
12
12
|
*
|
|
13
13
|
* 站的集合 = 引擎事件表里有站的 `classic.*` 名(一条钉从 `MOD_EVENT_TABLE` 派生对拍,不手抄第二份)。
|
|
14
|
-
* 观察站(`StopFailure` / `PostCompact` / `PermissionDenied`)只许观察者:答案形是「无」,core 的迁移配方要求挂 `{ observe: true }`。
|
|
14
|
+
* 观察站(`StopFailure` / `PostCompact` / `PermissionDenied` / `SessionStart` / `SessionEnd`)只许观察者:答案形是「无」,core 的迁移配方要求挂 `{ observe: true }`。
|
|
15
15
|
*/
|
|
16
16
|
import type { ClassicEventMap, ClassicEventName, EngineDollar, On } from "@sema-agent/core";
|
|
17
17
|
/** 引擎有站的 `classic.*` 名(core 的 `ClassicEventMap` 键集)。 */
|
|
@@ -30,6 +30,8 @@ export declare const CLASSIC_STATION_ROLE: {
|
|
|
30
30
|
readonly "classic.PreCompact": "chain";
|
|
31
31
|
readonly "classic.PostCompact": "observe";
|
|
32
32
|
readonly "classic.PermissionDenied": "observe";
|
|
33
|
+
readonly "classic.SessionStart": "observe";
|
|
34
|
+
readonly "classic.SessionEnd": "observe";
|
|
33
35
|
};
|
|
34
36
|
/** 本仓 handler 体对一站作的答:观察站不作答(`void`);链站 = core 的答案形,去掉本仓从不作的 `null` / `false`(PreToolUse 的两个「不表态」词)。 */
|
|
35
37
|
type StationAnswer<K extends ClassicStation> = (typeof CLASSIC_STATION_ROLE)[K] extends "observe" ? void : Exclude<ClassicEventMap[K]["answer"], null | false>;
|
|
@@ -11,6 +11,8 @@ export const CLASSIC_STATION_ROLE = {
|
|
|
11
11
|
"classic.PreCompact": "chain",
|
|
12
12
|
"classic.PostCompact": "observe",
|
|
13
13
|
"classic.PermissionDenied": "observe",
|
|
14
|
+
"classic.SessionStart": "observe",
|
|
15
|
+
"classic.SessionEnd": "observe",
|
|
14
16
|
};
|
|
15
17
|
export function hangClassic(on, station, answer) {
|
|
16
18
|
if (CLASSIC_STATION_ROLE[station] === "observe") {
|
package/dist/http/admission.js
CHANGED
|
@@ -24,6 +24,7 @@ import { legFactsOf } from "../leg-facts.js";
|
|
|
24
24
|
import { resolveExecutionContext, stampSpecRoot } from "./execution-context.js";
|
|
25
25
|
import { callerTrust, requestTrustRefusal } from "./caller-trust.js";
|
|
26
26
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
27
|
+
import { promptScreenDeclared } from "../prompt-screen.js";
|
|
27
28
|
import { sendError, sendHttpError } from "./send.js";
|
|
28
29
|
import { refuseSubmitBody } from "./request-guards.js";
|
|
29
30
|
import { sqlWallsOf } from "../plugins/sql-driver.js";
|
|
@@ -750,7 +751,7 @@ export function createAdmission(ctx) {
|
|
|
750
751
|
const runner = runnerFor(resolved);
|
|
751
752
|
const spec = stampSpecRoot(resolved, execution);
|
|
752
753
|
const model = modelIdOf(spec.model);
|
|
753
|
-
const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { sessionId: spec.sessionId, objective: spec.objective, ...(model !== undefined ? { model } : {}) });
|
|
754
|
+
const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { sessionId: spec.sessionId, objective: spec.objective, screened: promptScreenDeclared([deps.deploymentMods, spec.mods]), ...(model !== undefined ? { model } : {}) });
|
|
754
755
|
const clampNotice = buildEntryModeClampNotice(entryMode, spec.sessionId);
|
|
755
756
|
const pendingNotices = clampNotice !== undefined ? [clampNotice, ...engineNotices] : engineNotices;
|
|
756
757
|
const warnings = allowSelectorWarningsOf(admitted, spec.tools ?? []);
|
|
@@ -96,6 +96,7 @@ export declare function buildCheckpointCtx(fields: {
|
|
|
96
96
|
body: TaskRequestBody;
|
|
97
97
|
memoryScope?: string | undefined;
|
|
98
98
|
unattendedLeg?: true;
|
|
99
|
+
promptScreened?: true;
|
|
99
100
|
writerVersion: string;
|
|
100
101
|
}, sealer: CheckpointCtxSealer | undefined, sessionId: string): SealedCheckpointCtx;
|
|
101
102
|
/**
|
|
@@ -18,6 +18,7 @@ export function buildCheckpointCtx(fields, sealer, sessionId) {
|
|
|
18
18
|
writerVersion: fields.writerVersion,
|
|
19
19
|
...(fields.memoryScope !== undefined ? { memoryScope: fields.memoryScope } : {}),
|
|
20
20
|
...(fields.unattendedLeg === true ? { unattendedLeg: true } : {}),
|
|
21
|
+
...(fields.promptScreened === true ? { promptScreened: true } : {}),
|
|
21
22
|
...(bodyCarriesAllowList(fields.body) ? { permissionsAllowReading: "allow_layer" } : {}),
|
|
22
23
|
permissionModeReading: "entry_word",
|
|
23
24
|
};
|
|
@@ -2,8 +2,8 @@ import { uuidv7 } from "@sema-agent/core";
|
|
|
2
2
|
import { heldWorkflowParkDecisions } from "../parked-decide.js";
|
|
3
3
|
import { HEARTBEAT_MS, runInBackground } from "../runs.js";
|
|
4
4
|
import { heldFor } from "../peer-seat.js";
|
|
5
|
-
import { fleetRunPublisher, fleetRunLabels } from "../fleet/fleet-bus.js";
|
|
6
|
-
import { cascadeConfig, runMeta } from "./run-meta.js";
|
|
5
|
+
import { fleetRunPublisher, fleetRunLabels, nameFleetRowOnDelivery } from "../fleet/fleet-bus.js";
|
|
6
|
+
import { cascadeConfig, fillRunPreviewOnDelivery, runMeta } from "./run-meta.js";
|
|
7
7
|
import { declaredApprovalFrameFamilies, resolveStreamApprovalGate } from "../tool-approval.js";
|
|
8
8
|
import { buildActiveRunConflict } from "./active-run-conflict.js";
|
|
9
9
|
import { commitExecutionContext } from "./execution-context.js";
|
|
@@ -11,9 +11,8 @@ import { rootSessionAbsentAnswer } from "./execution-context.js";
|
|
|
11
11
|
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
12
12
|
import { clearTurnActivity, readTurnActivityMs } from "../turn-activity.js";
|
|
13
13
|
import { recordResourceWindow } from "../resource-window.js";
|
|
14
|
-
import {
|
|
14
|
+
import { persistSubmissionCtx } from "./submit-ctx.js";
|
|
15
15
|
import { releaseClaimedRun } from "./claimed-run.js";
|
|
16
|
-
import { serviceVersion } from "../service-version.js";
|
|
17
16
|
import { headReadOf } from "../session-incarnation.js";
|
|
18
17
|
import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../session-workspace.js";
|
|
19
18
|
export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
@@ -34,6 +33,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
34
33
|
if (!created.ok) {
|
|
35
34
|
return { status: 409, body: await buildActiveRunConflict({ runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, sessionId, created.activeTaskId) };
|
|
36
35
|
}
|
|
36
|
+
fillRunPreviewOnDelivery(args.prepared.acceptance, runStore, taskId);
|
|
37
37
|
const owner = args.prepared.auth?.principal ?? null;
|
|
38
38
|
const preLaunchHeartbeat = setInterval(() => {
|
|
39
39
|
void runStore.heartbeat(taskId, owner).catch(() => { deps.metrics?.inc("run_signal_poll_errors_total", { kind: "heartbeat" }); });
|
|
@@ -44,7 +44,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
44
44
|
if (placementReject)
|
|
45
45
|
return placementReject;
|
|
46
46
|
if (deps.checkpointStore) {
|
|
47
|
-
const ctxRefusal = await
|
|
47
|
+
const ctxRefusal = await persistSubmissionCtx(deps.checkpointStore, deps, { runStore, taskId }, args.prepared);
|
|
48
48
|
if (ctxRefusal)
|
|
49
49
|
return ctxRefusal;
|
|
50
50
|
}
|
|
@@ -81,8 +81,9 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
81
81
|
scope: args.fleetScope,
|
|
82
82
|
rootTaskId: taskId,
|
|
83
83
|
sessionId,
|
|
84
|
-
...fleetRunLabels(args.prepared.
|
|
84
|
+
...fleetRunLabels(args.prepared.acceptance.objectiveAtSubmit),
|
|
85
85
|
});
|
|
86
|
+
nameFleetRowOnDelivery(args.prepared.acceptance, fleetPub);
|
|
86
87
|
void heldFor(args.prepared.peerLease, () => runInBackground(args.prepared.runner, args.prepared.spec, runStore, taskId, deps.metrics, args.prepared.auth?.principal, args.prepared.verify, args.prepared.cascade ? cascadeConfig(deps.config.cascadeLadder, args.prepared.spec.limits?.maxCostUsd) : undefined, deps.instrumentDegenerate, deps.planCacheProbe, deps.config.traceThinking, inflightRuns, preemptableRuns, steerableRuns, deps.modelUsage, deps.elicitation, args.elicitOwner, captureTurnAnchor, fleetPub, captureUserMessageAnchor, deps.question, deps.workflowCompletionInbox, deps.subagentSteerRegistry, (m, x) => deps.logger?.info?.(m, x), deps.sendUserFile, deps.promptManifests, deps.toolApproval
|
|
87
88
|
? {
|
|
88
89
|
coordinator: deps.toolApproval,
|
|
@@ -159,7 +159,7 @@ export declare function fleetViewOf(req: IncomingMessage, w: IdentityWiring, fac
|
|
|
159
159
|
* 有身份边界的部署上:
|
|
160
160
|
* · **无主的行**经无主臂的唯一判据 `ownerlessRowOpen`(`security.ts`):普通会话照旧对任何调用方开放;委派子代的 placed 行只对 fleet 调用方
|
|
161
161
|
* ({@link sessionCallerOf})开放 —— 任何具名主体、没有 service 凭据的无身份调用方都与别人的行同体(调用点答 404)([ref])。
|
|
162
|
-
*
|
|
162
|
+
* 代价如实:升级前写下、属主还没定下的存量子代行连它将来的属主也暂时读不到(SQL 两店由一次性迁移 M23 在窗内每次起服回填;本机文件店没有回填臂;7.144.0 起没有任何店在停驻时定属主 —— 属主只由建行那一笔记下引擎交来的 `placement.owner`);
|
|
163
163
|
* · 有主的行就是 run 面那一条谓词({@link runOwnerReadable}),一字不变。
|
|
164
164
|
* run 面(poll / events / cancel …)不读本谓词 —— 那是 run 资源的门,另一只。
|
|
165
165
|
*/
|
|
@@ -19,15 +19,19 @@ export type RefusalBody = {
|
|
|
19
19
|
/** 拒体:`{ error, errorCode, ...facts }`。 */
|
|
20
20
|
export declare function refusalBody(r: Refusal): RefusalBody;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* `detail.code` ⇒ `blockedBy`(挡住它的准入码)· `detail.field` ⇒ `field`(哪一侧 / 哪一格)· `detail.reason` ⇒ `reason`(同码多臂时哪一臂)
|
|
22
|
+
* 引擎拒一次续跑时的**机读事实位** → 拒体的同名事实键(唯一读法;续跑家族的每一只拒答都读它):
|
|
23
|
+
* `detail.code` ⇒ `blockedBy`(挡住它的准入码)· `detail.field` ⇒ `field`(哪一侧 / 哪一格)· `detail.reason` ⇒ `reason`(同码多臂时哪一臂)·
|
|
24
|
+
* `detail.thrownName` ⇒ `thrownName`(引擎 7.62.0 起,只在 `reason: requirements_damaged` 的拒上:判官读这一行时被抛出的东西的名字,有界、不带 message ——
|
|
25
|
+
* 在场 = 引擎缺陷〔判官被一个读时会抛的成员绊倒〕,缺席 = 行的内容与它自述的对不上〔数据损坏〕,运维凭这一位分诊)。
|
|
24
26
|
* 一员在场 ⇔ 它是非空串;其余(缺席、非串、空串)一律不铸键 —— 引擎没说就是没说,本层不编值,也不把一个非串原样推上 wire。
|
|
25
27
|
* 载体有两种,读法同一只:同步腿的 `CheckpointError.detail`(`http/resume-legs.ts` `checkpointRefusalAnswer`),与后台停驻子代赎回回执的
|
|
26
|
-
* `details.detail`(`parked-decide.ts` 的 422 `parked_resume.startup_failed`;引擎 7.54.0
|
|
28
|
+
* `details.detail`(`parked-decide.ts` 的 422 `parked_resume.startup_failed`;引擎 7.54.0 起它把认领前那只失败的 `code` / `field` / `reason` 交到回执上 ——
|
|
29
|
+
* 回执只带这三位,`thrownName` 今天不在回执里,所以这条腿的体上它恒缺席;回执哪天多带,同一只读法自然过境)。
|
|
27
30
|
*/
|
|
28
31
|
export declare function resumeRefusalFacts(detail: unknown): {
|
|
29
32
|
blockedBy?: string;
|
|
30
33
|
field?: string;
|
|
31
34
|
reason?: string;
|
|
35
|
+
thrownName?: string;
|
|
32
36
|
};
|
|
33
37
|
//# sourceMappingURL=refusal-body.d.ts.map
|
|
@@ -10,6 +10,7 @@ export function resumeRefusalFacts(detail) {
|
|
|
10
10
|
...(word(d.code) ? { blockedBy: d.code } : {}),
|
|
11
11
|
...(word(d.field) ? { field: d.field } : {}),
|
|
12
12
|
...(word(d.reason) ? { reason: d.reason } : {}),
|
|
13
|
+
...(word(d.thrownName) ? { thrownName: d.thrownName } : {}),
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
//# sourceMappingURL=refusal-body.js.map
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -32,7 +32,7 @@ import { redactSecrets } from "../trace/redact.js";
|
|
|
32
32
|
import { buildRunObjectivePreview } from "../user-authored-text.js";
|
|
33
33
|
import { createForwardAppendFailedNote, createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
34
34
|
import { registerEngineNoticeLeg } from "../trace/engine-notice-wire.js";
|
|
35
|
-
import { reasoningDeltaBytes, reasoningSegmentEventId } from "../trace/model-segment.js";
|
|
35
|
+
import { blockText, reasoningDeltaBytes, reasoningSegmentEventId } from "../trace/model-segment.js";
|
|
36
36
|
import { errorText } from "../observability/err-text.js";
|
|
37
37
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
38
38
|
import { verifyRoundsFromBody } from "./verify-rounds.js";
|
|
@@ -54,6 +54,7 @@ import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind, isEngineQuestionRow }
|
|
|
54
54
|
import { deploymentListAsks } from "../approval.js";
|
|
55
55
|
import { isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
56
56
|
import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnsealableAnswer } from "./resume-ctx-read.js";
|
|
57
|
+
import { objectiveWithheldIn } from "./submit-ctx.js";
|
|
57
58
|
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
58
59
|
import { refusalBody, resumeRefusalFacts } from "./refusal-body.js";
|
|
59
60
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
@@ -615,6 +616,7 @@ export function createResumeLegs(ctx) {
|
|
|
615
616
|
async function driveResumeLeg(args, onAccepted) {
|
|
616
617
|
const resumeLegStartedAt = Date.now();
|
|
617
618
|
const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx, execution } = args;
|
|
619
|
+
const heldObjective = objectiveWithheldIn(resumeCtx.ctx) ? undefined : resumeObjective;
|
|
618
620
|
const reparkStatus = outcome.gate === "plan_review" ? "needs_review" : "suspended";
|
|
619
621
|
const commitResumeCtx = () => Promise.resolve().then(() => deps.checkpointStore.putCtx(resumeCtx.sessionId, sealCheckpointCtx({ ...resumeCtx.ctx, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, resumeCtx.sessionId))).catch((e) => {
|
|
620
622
|
deps.metrics?.inc("resume_ctx_not_persisted_total");
|
|
@@ -653,7 +655,7 @@ export function createResumeLegs(ctx) {
|
|
|
653
655
|
const created = await deps.runStore.createRun(freshId, sessionId, principal ?? null, deps.instanceId ?? "default", {
|
|
654
656
|
source: args.mintFreshRun.source,
|
|
655
657
|
identity: args.mintFreshRun.identity,
|
|
656
|
-
objectivePreview: buildRunObjectivePreview(
|
|
658
|
+
objectivePreview: heldObjective === undefined ? null : buildRunObjectivePreview(heldObjective),
|
|
657
659
|
});
|
|
658
660
|
if (created.existing) {
|
|
659
661
|
throw new Error(`wake fresh run: createRun observed an existing row for the freshly minted taskId ${freshId} (no idempotency key was sent) — refusing to treat it as a busy session`);
|
|
@@ -670,7 +672,7 @@ export function createResumeLegs(ctx) {
|
|
|
670
672
|
freshRunClaimed = true;
|
|
671
673
|
}
|
|
672
674
|
const fleetPub = taskId && deps.fleetBus
|
|
673
|
-
? fleetRunPublisher(deps.fleetBus, { runId: taskId, scope: fleetScope, rootTaskId: taskId, sessionId, ...fleetRunLabels(
|
|
675
|
+
? fleetRunPublisher(deps.fleetBus, { runId: taskId, scope: fleetScope, rootTaskId: taskId, sessionId, ...fleetRunLabels(heldObjective) })
|
|
674
676
|
: undefined;
|
|
675
677
|
const resumeTaskConfig = taskId ? { ...taskConfig, taskId } : taskConfig;
|
|
676
678
|
if (deps.toolApproval && taskId) {
|
|
@@ -912,6 +914,7 @@ export function createResumeLegs(ctx) {
|
|
|
912
914
|
const rs = deps.runStore;
|
|
913
915
|
const startSeq = rs && taskId ? await rs.maxSeq(taskId) : 0;
|
|
914
916
|
let text = "";
|
|
917
|
+
let block = "";
|
|
915
918
|
let reasoning = "";
|
|
916
919
|
let reasoningEventId;
|
|
917
920
|
const persistThinking = deps.config.traceThinking;
|
|
@@ -945,9 +948,11 @@ export function createResumeLegs(ctx) {
|
|
|
945
948
|
reasoning = "";
|
|
946
949
|
reasoningEventId = undefined;
|
|
947
950
|
}
|
|
948
|
-
|
|
949
|
-
|
|
951
|
+
const segment = text + blockText(block);
|
|
952
|
+
if (segment) {
|
|
953
|
+
await append("text", modelTextEventData({ text: segment, eventId: anchor.firstTextEventId }));
|
|
950
954
|
text = "";
|
|
955
|
+
block = "";
|
|
951
956
|
}
|
|
952
957
|
};
|
|
953
958
|
const driveResume = async () => {
|
|
@@ -1037,9 +1042,13 @@ export function createResumeLegs(ctx) {
|
|
|
1037
1042
|
await appendPromptManifest(append, deps.promptManifests, taskId);
|
|
1038
1043
|
switch (ev.type) {
|
|
1039
1044
|
case "text_delta":
|
|
1040
|
-
|
|
1045
|
+
block += ev.delta;
|
|
1041
1046
|
anchor.onText(ev.eventId);
|
|
1042
1047
|
break;
|
|
1048
|
+
case "text_end":
|
|
1049
|
+
text += blockText(block, ev);
|
|
1050
|
+
block = "";
|
|
1051
|
+
break;
|
|
1043
1052
|
case "reasoning_delta": {
|
|
1044
1053
|
const rb = reasoningDeltaBytes(ev);
|
|
1045
1054
|
if (persistThinking && rb !== undefined) {
|
|
@@ -1422,7 +1431,7 @@ export function createResumeLegs(ctx) {
|
|
|
1422
1431
|
const { spec, engineNotices } = resolved;
|
|
1423
1432
|
const hostSpec = { ...spec, objective };
|
|
1424
1433
|
const out = await mintAndDriveDurableRun(routeCtxBase, deps.runStore, {
|
|
1425
|
-
prepared: { spec: hostSpec, runner: resolved.runner, legFacts: resolved.legFacts, auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), peerLease: resolved.peerLease, execution: resolved.execution, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
|
|
1434
|
+
prepared: { spec: hostSpec, runner: resolved.runner, legFacts: resolved.legFacts, auth, body: ctx.body, ...(objectiveWithheldIn(ctx) ? { bodyFromScreenedCtx: true } : {}), acceptance: createRunAcceptance({}, { objective, screened: false }), peerLease: resolved.peerLease, execution: resolved.execution, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
|
|
1426
1435
|
fleetScope: resumeFleetScope(req, auth),
|
|
1427
1436
|
elicitOwner: principal ?? null,
|
|
1428
1437
|
source: "workflow_parked_decide",
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -146,6 +146,12 @@ export interface PreparedTaskSubmission {
|
|
|
146
146
|
* 判的是**这一次提交**的体:幂等重放从 run 记录答,不重判、不带。
|
|
147
147
|
*/
|
|
148
148
|
warnings?: readonly EntryProblem[];
|
|
149
|
+
/**
|
|
150
|
+
* `body` 是从一份标着 `promptScreened` 的续跑输入里取回的存量体(今天唯一的这种提交 = `/decide` 的宿主新 turn:这一跑的目标是本服务铸的指路文本,
|
|
151
|
+
* 体是宿主会话自己的那一份)⇒ 这一跑新写的续跑输入照样标上(`submit-ctx.ts` `persistSubmissionCtx`)。体里的目标不是这一跑的目标,
|
|
152
|
+
* 它能不能展示在它第一次落盘时就定了,这一跑的受理座答不了。缺席 = 体是这次请求自己的,标不标由受理座定。
|
|
153
|
+
*/
|
|
154
|
+
bodyFromScreenedCtx?: true;
|
|
149
155
|
/**
|
|
150
156
|
* 这次执行的**执行上下文**(唯一解析器 `http/execution-context.ts` `resolveExecutionContext` 的产物:根 · 住客 · 放置态 · 车道 · device 车道的设备意图)。
|
|
151
157
|
* 提交门在装配之前铸(fresh);`/decide` 的宿主新 turn 交续跑折叠器铸的那一只(continuation)。每个 `runStore.createRun(` 成功点**必须**把它交给
|
|
@@ -208,6 +214,15 @@ export interface CheckpointCtx {
|
|
|
208
214
|
* 缺席 = 本版之前写下的行(不回填):那一码的体不带两键,如实不猜。不是一次性迁移 —— 这一格是常设的事实位,没有删除条件。
|
|
209
215
|
*/
|
|
210
216
|
writerVersion?: string;
|
|
217
|
+
/**
|
|
218
|
+
* 体里的目标**不是可展示的那一份**:写这份续跑输入的提交腿上装着 prompt 屏(判据 `prompt-screen.ts` 的 `promptScreenDeclared`),
|
|
219
|
+
* 体里的 `objective` 不是引擎交付的文本 —— 留空,或 verify 形为验证器留着的请求原文(律的属主 = `run-acceptance.ts`「交付的是哪一份」;
|
|
220
|
+
* 写法与那条 P-DEBT 见 `submit-ctx.ts` 头注)。**服务端写、不来自请求体**(与 {@link unattendedLeg} 同为 `body` 的兄弟键)。
|
|
221
|
+
* 读者只有一只:`submit-ctx.ts` 的 `objectiveWithheldIn`(续跑腿的两张展示面 —— wake 新行的预览、fleet 行名 —— 经它问,在场 ⇒ 缺席)。
|
|
222
|
+
* 写点:`buildCheckpointCtx`(提交腿那一笔,经 `persistSubmissionCtx`;体从一份标着它的续跑输入里取回时照样标上);续跑腿的写回传原 ctx / 其展开 ⇒ 标随行。
|
|
223
|
+
* 缺席 = 体里的目标就是交付的那一份(腿上没有屏)。
|
|
224
|
+
*/
|
|
225
|
+
promptScreened?: true;
|
|
211
226
|
}
|
|
212
227
|
/**
|
|
213
228
|
* `/decide` 第三条车道交给宿主投递腿(wake:`DriveResumeArgs` / fresh:`DurableRunLegArgs`)的**一件**:这一跑在内存里带着的决定,
|
|
@@ -7,6 +7,7 @@ import { type GateIntentView } from "../../approval.js";
|
|
|
7
7
|
import { type RouteCtx, type RouteDecl, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
8
8
|
import { type KeyClosureIssue } from "../../request-key-closure.js";
|
|
9
9
|
import { type PeerInboundPendingRow } from "../../peer-inbound-face.js";
|
|
10
|
+
import { type PlacementReadingSessionStore, type SessionAgent } from "../../session-agent.js";
|
|
10
11
|
/** 族的成员 = 路由表行 id(改名 / 删行 ⇒ 下表的键当场编译红)。 */
|
|
11
12
|
export type DecisionDoorId = Extract<ApprovalsAssistantRouteId, "approval-decide" | "assistant-task-plan-review">;
|
|
12
13
|
/** 一道门的静态描述 —— 只写**这道门自己**的东西;路径、路径段、别门的键一概不在这里写(从来源读)。 */
|
|
@@ -104,6 +105,19 @@ export type QuestionAnswerCheck = {
|
|
|
104
105
|
*/
|
|
105
106
|
export declare function checkQuestionAnswer(v: unknown): QuestionAnswerCheck;
|
|
106
107
|
export declare function handleApprovalsAssistant(req: IncomingMessage, res: ServerResponse, match: RouteMatch<ApprovalsAssistantRouteId>, ctx: RouteCtx): Promise<void>;
|
|
108
|
+
/** `listPending()` 的行形(两条 durable 读面共用;从 store 的返回类型取,不手抄)。 */
|
|
109
|
+
type PendingRow = Awaited<ReturnType<CheckpointStoreFull["listPending"]>>[number];
|
|
110
|
+
/**
|
|
111
|
+
* 两条 durable 读面共用的行形 —— 行 + [ref]② 追加的**出身格**(派生位,不是列)+ [ref] 行族词(`checkpoint`)+ [ref] 行的会话在委派树里的位置 `agent`
|
|
112
|
+
* (会话视图 `GET /v1/sessions/:id` 体顶层的同名键,同一只读口 `readSessionAgent`;在场 ⇔ 这一次列表读到行的会话是一只 placed 委派子代,见 {@link projectDurableQueue})。
|
|
113
|
+
* 导出只为自铸登记表按键绑型(`trace/self-minted-wire.ts` 的 `approvalQueueRowAgent`)。
|
|
114
|
+
*/
|
|
115
|
+
export type PendingRowForWire = PendingRow & {
|
|
116
|
+
kind: "checkpoint";
|
|
117
|
+
governanceForced?: true;
|
|
118
|
+
rememberRefused?: true;
|
|
119
|
+
agent?: SessionAgent;
|
|
120
|
+
};
|
|
107
121
|
/**
|
|
108
122
|
* 🔴 [ref]-2 / [ref]②:两条 durable 读面(`GET /v1/approvals` + `/v1/approvals/stream`)的**唯一**
|
|
109
123
|
* 投影 —— 脱敏([ref] 件4)+ 出身归因([ref] 的另一半)一次做完。
|
|
@@ -145,7 +159,12 @@ live: Pick<ToolApprovalCoordinator, "listLivePending"> | undefined,
|
|
|
145
159
|
* (`pending` / `resolved`):两族同一扇决断口,帧上的 `kind` 判别。`undefined` = 本腿没有这一半场(流字节只差每帧的 `kind`)。
|
|
146
160
|
* 位置在 `scope` 之前、与 `live` 并列:每个生产调用点都必须表态。
|
|
147
161
|
*/
|
|
148
|
-
peerInbound: (() => readonly PeerInboundPendingRow[]) | undefined,
|
|
162
|
+
peerInbound: (() => readonly PeerInboundPendingRow[]) | undefined,
|
|
163
|
+
/**
|
|
164
|
+
* 🔴 [ref]:停驻行 `agent` 的会话店面 —— 与 `GET /v1/approvals` 同一只(`deps.sessionStorage`),经同一只整批投影 {@link projectDurableQueue}。
|
|
165
|
+
* `undefined` / 店不交 `placementOf` = 这台部署不存委派元组 ⇒ 没有一帧带 `agent`(帧字节与 [ref] 之前逐字相同)。位置在 `scope` 之前、与 `live` 并列:每个生产调用点都必须表态。
|
|
166
|
+
*/
|
|
167
|
+
sessions: PlacementReadingSessionStore | undefined, scope: string | undefined,
|
|
149
168
|
/** [ref]②:出身归因的部署侧一半 + 三张审批名单(问的属主)。缺席 ⇒ 归不出治理出身(键缺席,与 best-effort 同方向)、没有一行读作
|
|
150
169
|
* 部署名单的问(记不住,方向是多问)。**位置在 `pollMs` 之前**是刻意的:`pollMs` 只有测试传,而本参数是每个生产调用点都必须给的那一个。 */
|
|
151
170
|
governance?: PendingProjectionDeployment, pollMs?: number): Promise<void>;
|
|
@@ -36,6 +36,7 @@ import { seatText } from "../../trace/exit-seat.js";
|
|
|
36
36
|
import { approvalQueueKindOfDoorId } from "../../approval-queue-kind.js";
|
|
37
37
|
import { defaultPeerInboundFace, parsePeerInboundId, peerBoxScope } from "../../peer-inbound-face.js";
|
|
38
38
|
import { RESUME_RESTATABLE_KEYS, readResumeRestatement } from "../resume-restatement.js";
|
|
39
|
+
import { readSessionAgent } from "../../session-agent.js";
|
|
39
40
|
const DECIDE_BODY_DECLARED_KEYS = [
|
|
40
41
|
"answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decidedVia", "decision", "feedback", "reason", "remember", "updatedInput",
|
|
41
42
|
...RESUME_RESTATABLE_KEYS,
|
|
@@ -315,7 +316,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
315
316
|
if (match.decl.id === "approvals-stream") {
|
|
316
317
|
const ownerParam = operator ? (new URLSearchParams(match.search).get("owner") ?? undefined) : undefined;
|
|
317
318
|
const scope = operator ? ownerParam : ownScope;
|
|
318
|
-
await streamApprovals(req, res, cs, deps.toolApproval, peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam), scope, deps.config);
|
|
319
|
+
await streamApprovals(req, res, cs, deps.toolApproval, peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam), deps.sessionStorage, scope, deps.config);
|
|
319
320
|
return;
|
|
320
321
|
}
|
|
321
322
|
if (match.decl.id === "approvals-list") {
|
|
@@ -331,7 +332,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
331
332
|
sendApprovalsReadFailure(req, res, deps.logger, err);
|
|
332
333
|
return;
|
|
333
334
|
}
|
|
334
|
-
const pending = [...
|
|
335
|
+
const pending = [...(await projectDurableQueue(durableRows, deps.config, deps.sessionStorage)), ...peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam)()];
|
|
335
336
|
sendJson(res, 200, {
|
|
336
337
|
pending,
|
|
337
338
|
serverNowMs: Date.now(),
|
|
@@ -412,6 +413,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
412
413
|
...durableRowIdentity("assistant-inbox", s.checkpointId, s.token),
|
|
413
414
|
scope: s.scope,
|
|
414
415
|
createdAt: s.createdAt,
|
|
416
|
+
rowVerdict: s.rowVerdict,
|
|
415
417
|
...(s.gateKind !== undefined ? { gateKind: s.gateKind } : {}),
|
|
416
418
|
...(s.severity !== undefined ? { severity: s.severity } : {}),
|
|
417
419
|
...(s.spentMicroUsd !== undefined ? { spentMicroUsd: s.spentMicroUsd } : {}),
|
|
@@ -1035,7 +1037,7 @@ function peerInboundRowsFor(face, principal, operatorOwnerParam) {
|
|
|
1035
1037
|
const scope = peerBoxScope(principal);
|
|
1036
1038
|
return () => face.list(scope);
|
|
1037
1039
|
}
|
|
1038
|
-
function projectPendingForWire(row, deployment) {
|
|
1040
|
+
function projectPendingForWire(row, deployment, agent) {
|
|
1039
1041
|
const governance = deployment;
|
|
1040
1042
|
const redacted = redactPendingDisclosures(row);
|
|
1041
1043
|
const governanceForced = governanceOriginOf({ riskDescriptor: row.riskDescriptor ?? undefined }, governance);
|
|
@@ -1048,7 +1050,27 @@ function projectPendingForWire(row, deployment) {
|
|
|
1048
1050
|
classes: row.classes,
|
|
1049
1051
|
deploymentListQuestion,
|
|
1050
1052
|
});
|
|
1051
|
-
return { kind: "checkpoint", ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}) };
|
|
1053
|
+
return { kind: "checkpoint", ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}), ...(agent !== undefined ? { agent } : {}) };
|
|
1054
|
+
}
|
|
1055
|
+
async function projectDurableQueue(rows, deployment, sessions) {
|
|
1056
|
+
const agents = await sessionAgentsOf(rows.map((r) => r.sessionId), sessions);
|
|
1057
|
+
return rows.map((r) => projectPendingForWire(r, deployment, agents.get(r.sessionId)));
|
|
1058
|
+
}
|
|
1059
|
+
async function sessionAgentsOf(sessionIds, sessions) {
|
|
1060
|
+
const out = new Map();
|
|
1061
|
+
if (sessions?.placementOf === undefined)
|
|
1062
|
+
return out;
|
|
1063
|
+
await Promise.all([...new Set(sessionIds)].map(async (sessionId) => {
|
|
1064
|
+
try {
|
|
1065
|
+
const agent = await readSessionAgent(sessions, sessionId);
|
|
1066
|
+
if (agent !== undefined)
|
|
1067
|
+
out.set(sessionId, agent);
|
|
1068
|
+
}
|
|
1069
|
+
catch (err) {
|
|
1070
|
+
recordFailOpen("server.approvals.queue-row-agent-unreadable", `session ${JSON.stringify(sessionId)}: ${seatText("operator-log-text", err instanceof Error ? err.message : String(err))}`);
|
|
1071
|
+
}
|
|
1072
|
+
}));
|
|
1073
|
+
return out;
|
|
1052
1074
|
}
|
|
1053
1075
|
function createSseDeltaLane(res, spec) {
|
|
1054
1076
|
let prev = new Map();
|
|
@@ -1067,7 +1089,7 @@ function createSseDeltaLane(res, spec) {
|
|
|
1067
1089
|
},
|
|
1068
1090
|
};
|
|
1069
1091
|
}
|
|
1070
|
-
export async function streamApprovals(req, res, cs, live, peerInbound, scope, governance, pollMs = APPROVALS_STREAM_POLL_MS) {
|
|
1092
|
+
export async function streamApprovals(req, res, cs, live, peerInbound, sessions, scope, governance, pollMs = APPROVALS_STREAM_POLL_MS) {
|
|
1071
1093
|
const keyOf = (p) => {
|
|
1072
1094
|
switch (p.kind) {
|
|
1073
1095
|
case "checkpoint":
|
|
@@ -1117,7 +1139,7 @@ export async function streamApprovals(req, res, cs, live, peerInbound, scope, go
|
|
|
1117
1139
|
while (!closed) {
|
|
1118
1140
|
let snapshot;
|
|
1119
1141
|
try {
|
|
1120
|
-
const pending = [...(await cs.listPending(scope)
|
|
1142
|
+
const pending = [...(await projectDurableQueue(await cs.listPending(scope), governance, sessions)), ...(peerInbound?.() ?? [])];
|
|
1121
1143
|
snapshot = { pending, livePending: live ? live.listLivePending(scope) : [] };
|
|
1122
1144
|
}
|
|
1123
1145
|
catch (err) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* POST /v1/rules → 写一条**收紧**规则(deny/ask)进 durable 分区,单步、无票仪式
|
|
5
5
|
* POST /v1/rules/cc-import/prepare → 预览 + 一张**一次性、principal 绑定、有期、载荷绑定**的票
|
|
6
|
-
* POST /v1/rules/cc-import/redeem → 原子消费票 → confirm → 批量兑付进店
|
|
6
|
+
* POST /v1/rules/cc-import/redeem → 原子消费票 → confirm → 批量兑付进店 → 回执 `{ members, rev }`(与 local-import 口同一张,{@link sendImportReceipt})
|
|
7
7
|
* GET /v1/rules → 列出自己名下**活着**的规则(keyset 分页,游标绑 rev)
|
|
8
8
|
* DELETE /v1/rules → 按 (rule, scope) 内容撤销,**恒经** core `removePersistedRule`
|
|
9
9
|
*
|
|
@@ -159,7 +159,7 @@ export async function handleRules(req, res, match, ctx) {
|
|
|
159
159
|
}
|
|
160
160
|
const redeemed = await lane.redeemLocalImport(principal, parsed.data.ticket);
|
|
161
161
|
if (redeemed.ok) {
|
|
162
|
-
|
|
162
|
+
sendImportReceipt(res, redeemed.outcome);
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
logger.info("local rule import ticket refused", { reason: redeemed.reason, detail: redeemed.detail, retryable: redeemed.reason !== "ticket-unusable" && redeemed.reason !== "in-flight" && redeemed.retryable === true });
|
|
@@ -218,7 +218,10 @@ export async function handleRules(req, res, match, ctx) {
|
|
|
218
218
|
sendError(res, 404, "not_found.rule_ticket", "no such import ticket");
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
|
-
|
|
221
|
+
sendImportReceipt(res, redeemed.outcome);
|
|
222
|
+
}
|
|
223
|
+
function sendImportReceipt(res, outcome) {
|
|
224
|
+
sendJson(res, 200, { members: outcome.members, rev: outcome.rev });
|
|
222
225
|
}
|
|
223
226
|
function resolveTargetPrincipal(requested, caller, config) {
|
|
224
227
|
if (requested === undefined || requested === caller)
|
|
@@ -7,6 +7,8 @@ import * as facts from "../../capability-facts.js";
|
|
|
7
7
|
import { classifySyncRelationshipByIds, readImportAnchors, SyncConflictError } from "../../session-sync-kernel.js";
|
|
8
8
|
import { isUuidShape, legacyUnownedSessionOk, sessionOwnerCellOf } from "../../security.js";
|
|
9
9
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, logRequestError, msg } from "../send.js";
|
|
10
|
+
import { StampedSessionArchiveError } from "../../session-error-wire.js";
|
|
11
|
+
import { readSessionAgent } from "../../session-agent.js";
|
|
10
12
|
import { buildDisplaySafeUrl } from "../../trace/redact.js";
|
|
11
13
|
import { storeFaultClassOf } from "../../plugins/store-fault.js";
|
|
12
14
|
import { fleetViewOf, gatedPrincipal, explicitOperatorOk, sendUnauthorized, sessionCallerOf } from "../principal-gate.js";
|
|
@@ -359,6 +361,16 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
359
361
|
const dstIds = (await sessionStorage.listEntryIds?.(sessionId)) ?? null;
|
|
360
362
|
const relA = classifySyncRelationshipByIds(entryIds, dstIds);
|
|
361
363
|
if ((relA.relation === "fork" || relA.relation === "stale") && resolution !== "overwrite-dst") {
|
|
364
|
+
if (sessionStorage.assertArchiveAnonymous !== undefined && (await readSessionAgent(sessionStorage, sessionId)) === undefined) {
|
|
365
|
+
try {
|
|
366
|
+
await sessionStorage.assertArchiveAnonymous(sessionId);
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
if (e instanceof StampedSessionArchiveError && sendSessionError(res, e, "import"))
|
|
370
|
+
return;
|
|
371
|
+
throw e;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
362
374
|
sendError(res, 409, "conflict", "sync conflict", { relation: relA });
|
|
363
375
|
return;
|
|
364
376
|
}
|
|
@@ -5,7 +5,7 @@ import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpS
|
|
|
5
5
|
import { atHostBoundary, sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
6
6
|
import { voidVerifierPauseCard } from "../../verifier-pause-card.js";
|
|
7
7
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
8
|
-
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals } from "../../fleet/fleet-bus.js";
|
|
8
|
+
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, nameFleetRowOnDelivery } from "../../fleet/fleet-bus.js";
|
|
9
9
|
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
10
10
|
import { createLiveModelText, modelStreamOfTask } from "../../trace/model-segment.js";
|
|
11
11
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
|
|
@@ -17,7 +17,7 @@ import { recordResourceWindow } from "../../resource-window.js";
|
|
|
17
17
|
import { redactSecrets } from "../../trace/redact.js";
|
|
18
18
|
import { writeLiveFrame, writeLiveHeartbeat } from "../live-frame.js";
|
|
19
19
|
import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, liveIdentityFields } from "../../trace/project.js";
|
|
20
|
-
import { cascadeConfig, runMeta } from "../run-meta.js";
|
|
20
|
+
import { cascadeConfig, fillRunPreviewOnDelivery, runMeta } from "../run-meta.js";
|
|
21
21
|
import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
|
|
22
22
|
import { replayOfExistingRun } from "../submit-replay.js";
|
|
23
23
|
import { httpErrorCode, sendJson, sendError, sseHeaders } from "../send.js";
|
|
@@ -33,8 +33,7 @@ import { failOpenTagForDroppedFrame, recordFailOpen } from "../../observability/
|
|
|
33
33
|
import { buildSyncSubmitBody } from "../sync-submit-body.js";
|
|
34
34
|
import { assertNever } from "../route-ctx.js";
|
|
35
35
|
import * as facts from "../../capability-facts.js";
|
|
36
|
-
import {
|
|
37
|
-
import { serviceVersion } from "../../service-version.js";
|
|
36
|
+
import { persistSubmissionCtx } from "../submit-ctx.js";
|
|
38
37
|
import { withModelReachScope } from "../../model-reach-scope.js";
|
|
39
38
|
import { createInstructionSelectionLeg } from "../../instruction-selection.js";
|
|
40
39
|
import { fieldConflictFacts } from "../../field-conflict.js";
|
|
@@ -326,9 +325,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
326
325
|
return placementReject;
|
|
327
326
|
}
|
|
328
327
|
durableTaskId = tid;
|
|
328
|
+
fillRunPreviewOnDelivery(prepared.acceptance, deps.runStore, tid);
|
|
329
329
|
}
|
|
330
330
|
if (deps.checkpointStore) {
|
|
331
|
-
const ctxRefusal = await
|
|
331
|
+
const ctxRefusal = await persistSubmissionCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared);
|
|
332
332
|
if (ctxRefusal) {
|
|
333
333
|
writeLiveFrame(res, { type: "done", result: doneResultOfOutcome(ctxRefusal) });
|
|
334
334
|
return ctxRefusal;
|
|
@@ -361,8 +361,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
361
361
|
let fleetPub;
|
|
362
362
|
try {
|
|
363
363
|
fleetPub = durableTaskId
|
|
364
|
-
? fleetRunPublisher(deps.fleetBus, { runId: durableTaskId, scope: gatedPrincipal(req, deps.config) ?? "default", rootTaskId: durableTaskId, sessionId: prepared.spec.sessionId, ...fleetRunLabels(prepared.
|
|
364
|
+
? fleetRunPublisher(deps.fleetBus, { runId: durableTaskId, scope: gatedPrincipal(req, deps.config) ?? "default", rootTaskId: durableTaskId, sessionId: prepared.spec.sessionId, ...fleetRunLabels(prepared.acceptance.objectiveAtSubmit) })
|
|
365
365
|
: undefined;
|
|
366
|
+
if (fleetPub)
|
|
367
|
+
nameFleetRowOnDelivery(prepared.acceptance, fleetPub);
|
|
366
368
|
}
|
|
367
369
|
catch (e) {
|
|
368
370
|
if (durableHeartbeat)
|
|
@@ -499,7 +501,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
499
501
|
userMsgEntryId = promptEntryId;
|
|
500
502
|
const causeAtDone = ev.type === "done" ? ac.cause() : undefined;
|
|
501
503
|
await deps.userInputLedger?.observeLegEvent(liveStream, ev);
|
|
502
|
-
prepared.acceptance.observe(ev);
|
|
504
|
+
await prepared.acceptance.observe(ev);
|
|
503
505
|
await liveWorkspace.observe(ev);
|
|
504
506
|
if (ev.type === "done") {
|
|
505
507
|
finalResult = await mintSubmitExitResult(ev.result, deps.checkpointStore);
|
|
@@ -894,9 +896,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
894
896
|
if (placementReject)
|
|
895
897
|
return placementReject;
|
|
896
898
|
durableTaskId = tid;
|
|
899
|
+
fillRunPreviewOnDelivery(prepared.acceptance, deps.runStore, tid);
|
|
897
900
|
}
|
|
898
901
|
if (deps.checkpointStore) {
|
|
899
|
-
const ctxRefusal = await
|
|
902
|
+
const ctxRefusal = await persistSubmissionCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared);
|
|
900
903
|
if (ctxRefusal)
|
|
901
904
|
return ctxRefusal;
|
|
902
905
|
}
|
|
@@ -940,7 +943,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
940
943
|
const syncWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), prepared.legFacts?.sessionWitness);
|
|
941
944
|
for await (const ev of stream) {
|
|
942
945
|
observeSyncLegEvent(ev);
|
|
943
|
-
prepared.acceptance.observe(ev);
|
|
946
|
+
await prepared.acceptance.observe(ev);
|
|
944
947
|
await syncWorkspace.observe(ev);
|
|
945
948
|
}
|
|
946
949
|
return stream.result();
|
package/dist/http/run-meta.d.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* [ref] A9 装配缝的一片小叶子 ——
|
|
2
|
+
* [ref] A9 装配缝的一片小叶子 —— 提交路径共用的三只小件(run 行元数据 + run 行预览的补写口 + cascade 配置)。
|
|
3
3
|
*
|
|
4
|
-
* 为什么单独成文件:`runMeta`
|
|
5
|
-
* 挂在任一域模块上都会造出 routes/* 互相值 import
|
|
4
|
+
* 为什么单独成文件:`runMeta` 与预览补写口被 tasks 与 runs **两个**域模块调用,`cascadeConfig` 同理(还被测试直接引)。
|
|
5
|
+
* 挂在任一域模块上都会造出 routes/* 互相值 import 的第二条边;它们只依赖入参(受理座 / run 店两只口)+ 预览读法叶子,
|
|
6
6
|
* 所以正确归处是叶子。server.ts 转出口 `cascadeConfig` 以保住既有 import 点。
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { CascadeConfig } from "@sema-agent/core";
|
|
9
|
+
import type { RunAcceptance } from "../run-acceptance.js";
|
|
10
|
+
import type { RunStore } from "../plugins/store-backend.js";
|
|
9
11
|
/** The denormalized run-row metadata captured at submit: correlation (jobId), credential-derived source,
|
|
10
|
-
* and a redacted objective glance for lists. One place so the three submit paths can't drift.
|
|
12
|
+
* and a redacted objective glance for lists. One place so the three submit paths can't drift.
|
|
13
|
+
*
|
|
14
|
+
* 预览只取受理座交出的、提交那一刻就知道的交付文本(律的属主 = `run-acceptance.ts`「交付的是哪一份」):腿上没有 prompt 屏 ⇒ 交付的就是请求原文,
|
|
15
|
+
* 建行即写;腿上有屏 ⇒ 建行**不写**(列 NULL = 没建过预览),引擎交付可写的那一份时经 {@link fillRunPreviewOnDelivery} 补写一次。本函数不读请求体。 */
|
|
11
16
|
export declare function runMeta(prepared: {
|
|
12
17
|
jobId?: string;
|
|
13
|
-
|
|
18
|
+
acceptance: Pick<RunAcceptance, "objectiveAtSubmit">;
|
|
14
19
|
}, source: string | null): {
|
|
15
20
|
jobId: string | null;
|
|
16
21
|
source: string | null;
|
|
17
|
-
objectivePreview: string;
|
|
22
|
+
objectivePreview: string | null;
|
|
18
23
|
};
|
|
24
|
+
/** 腿上有屏时 run 行预览的补写口(建行之后、起跑之前登记):交付的那一份 → 同一只读法 → 店的写一次口(列已有值则不动)。腿上没有屏 ⇒ 受理座永不调它。 */
|
|
25
|
+
export declare function fillRunPreviewOnDelivery(acceptance: Pick<RunAcceptance, "onDelivered">, runStore: Pick<RunStore, "fillObjectivePreview">, taskId: string): void;
|
|
19
26
|
/** Build the cascade config from the operator ladder. Default gate = `status==="completed"` (escalate
|
|
20
27
|
* on a cheap-rung failure); cost-ceiling from the task's budget bounds the cold-rerun tax. */
|
|
21
28
|
export declare function cascadeConfig(ladder: readonly string[], maxCostUsd?: number): CascadeConfig;
|
package/dist/http/run-meta.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { buildRunObjectivePreview } from "../user-authored-text.js";
|
|
2
2
|
export function runMeta(prepared, source) {
|
|
3
|
-
const
|
|
3
|
+
const delivered = prepared.acceptance.objectiveAtSubmit;
|
|
4
4
|
return {
|
|
5
5
|
jobId: prepared.jobId ?? null,
|
|
6
6
|
source,
|
|
7
|
-
objectivePreview: buildRunObjectivePreview(
|
|
7
|
+
objectivePreview: delivered === undefined ? null : buildRunObjectivePreview(delivered),
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
+
export function fillRunPreviewOnDelivery(acceptance, runStore, taskId) {
|
|
11
|
+
acceptance.onDelivered("run-preview", (objective) => runStore.fillObjectivePreview(taskId, buildRunObjectivePreview(objective)));
|
|
12
|
+
}
|
|
10
13
|
export function cascadeConfig(ladder, maxCostUsd) {
|
|
11
14
|
return {
|
|
12
15
|
ladder: ladder.map((model) => ({ model })),
|