@sema-agent/server 7.112.0 → 7.114.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 +95 -0
- package/USAGE.md +4 -4
- package/dist/approval-ask-audit-store.d.ts +3 -4
- package/dist/approval-ask-audit-store.js +3 -3
- package/dist/approval-card.d.ts +4 -4
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/memory-boundary.d.ts +2 -1
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/resolve-spec.js +2 -2
- package/dist/boot/stores.js +4 -6
- package/dist/boot/workflow-orchestration.js +4 -4
- package/dist/config-catalog.js +6 -5
- package/dist/config-invariants.d.ts +27 -0
- package/dist/config-invariants.js +54 -39
- package/dist/config-types.d.ts +5 -11
- package/dist/config-types.js +0 -1
- package/dist/config.d.ts +0 -16
- package/dist/config.js +44 -69
- package/dist/env-facts.d.ts +3 -1
- package/dist/env-facts.js +5 -1
- package/dist/execution-lane-caps.d.ts +54 -30
- package/dist/execution-lane-caps.js +48 -20
- package/dist/hooks/hook-runner.d.ts +87 -7
- package/dist/hooks/hook-runner.js +113 -20
- package/dist/host-decision.d.ts +5 -3
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -4
- package/dist/http/request-guards.js +2 -2
- package/dist/http/resume-legs.d.ts +9 -10
- package/dist/http/resume-legs.js +64 -18
- package/dist/http/route-ctx.d.ts +38 -8
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/routes/background.js +2 -2
- package/dist/http/routes/capabilities.js +2 -2
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/notify-wake.js +3 -3
- package/dist/http/routes/runs.js +19 -13
- package/dist/http/routes/session-sync.js +6 -6
- package/dist/http/routes/sessions.js +20 -20
- package/dist/http/routes/tasks.js +1 -0
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +1 -1
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/local-data-root.d.ts +14 -0
- package/dist/local-data-root.js +5 -0
- package/dist/observability/corrupt-read-seat.d.ts +46 -66
- package/dist/observability/corrupt-read-seat.js +79 -63
- package/dist/observability/fail-open.d.ts +28 -0
- package/dist/observability/fail-open.js +28 -0
- package/dist/observability/run-terminal.js +39 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +1 -5
- package/dist/orchestration/workflow-agent-session-index.js +2 -2
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -2
- package/dist/plugins/approval-ask-store-file.d.ts +1 -4
- package/dist/plugins/approval-ask-store-file.js +2 -2
- package/dist/plugins/file-resume-anchor-store.d.ts +1 -6
- package/dist/plugins/file-resume-anchor-store.js +2 -4
- package/dist/plugins/file-run-store.d.ts +0 -4
- package/dist/plugins/file-run-store.js +1 -3
- package/dist/plugins/remote-env-host.d.ts +0 -35
- package/dist/plugins/remote-env-host.js +0 -8
- package/dist/plugins/remote-env-local-docker.d.ts +5 -13
- package/dist/plugins/remote-env-local-docker.js +2 -4
- package/dist/plugins/send-file-ledger.d.ts +1 -5
- package/dist/plugins/send-file-ledger.js +2 -2
- package/dist/plugins/store-backend.js +8 -9
- package/dist/plugins/user-input-ledger-file.d.ts +1 -5
- package/dist/plugins/user-input-ledger-file.js +2 -2
- package/dist/run-local.d.ts +1 -1
- package/dist/run-local.js +2 -2
- package/dist/runs.js +1 -0
- package/dist/spec-fields.js +0 -1
- package/dist/store-posture.d.ts +7 -7
- package/dist/store-posture.js +0 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +3 -0
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.js +2 -3
- package/dist/trace/redact.d.ts +18 -0
- package/dist/trace/redact.js +4 -0
- package/dist/unattended-permission-mode.d.ts +9 -2
- package/dist/unattended-permission-mode.js +5 -1
- package/dist/user-input-ledger.d.ts +24 -5
- package/dist/user-input-ledger.js +46 -11
- package/package.json +4 -3
- package/scripts/retired-mode-seed-drain-local.mjs +317 -0
package/dist/http/resume-legs.js
CHANGED
|
@@ -30,7 +30,7 @@ import { errorText } from "../observability/err-text.js";
|
|
|
30
30
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
31
31
|
import { verifyRoundsFromBody } from "./verify-rounds.js";
|
|
32
32
|
import { storedBodyPermissionMode } from "../task-settings.js";
|
|
33
|
-
import { acceptedResumingReceipt } from "./route-ctx.js";
|
|
33
|
+
import { acceptedResumingReceipt, buildHostLegOutcome } from "./route-ctx.js";
|
|
34
34
|
import { mintAndDriveDurableRun } from "./durable-run-leg.js";
|
|
35
35
|
import { createSessionsLocal } from "./routes/sessions.js";
|
|
36
36
|
import { createSessionSyncLocal } from "./routes/session-sync.js";
|
|
@@ -70,7 +70,63 @@ const WORKER_SWAP_REDEEMABLE = {
|
|
|
70
70
|
function checkpointRowRedeemableElsewhere(reason) {
|
|
71
71
|
return reason !== undefined && WORKER_SWAP_REDEEMABLE[reason] === true;
|
|
72
72
|
}
|
|
73
|
-
const
|
|
73
|
+
const CHECKPOINT_ERROR_DISPOSITION = {
|
|
74
|
+
"checkpoint.resume_aborted": "cancelled",
|
|
75
|
+
"checkpoint.invalid_outcome": "retriable",
|
|
76
|
+
"checkpoint.reopen_revote": "retriable",
|
|
77
|
+
"checkpoint.reopened_concurrently": "retriable",
|
|
78
|
+
"resume.parent_constraint_missing": "retriable",
|
|
79
|
+
"resume.parent_constraint_mismatch": "retriable",
|
|
80
|
+
"resume.constraint_rejected": "retriable",
|
|
81
|
+
"resume.constraint_unprojectable": "retriable",
|
|
82
|
+
"resume.usage_window_exhausted": "retriable",
|
|
83
|
+
"resume.principal_mismatch": "retriable",
|
|
84
|
+
"resume.placement_mismatch": "retriable",
|
|
85
|
+
"resume.preflight_rejected": "retriable",
|
|
86
|
+
"wake.gate_pending": "retriable",
|
|
87
|
+
"wake.nothing_to_deliver": "retriable",
|
|
88
|
+
"checkpoint.config_refused": "retriable",
|
|
89
|
+
"checkpoint.walltime_axis_retired": "retriable",
|
|
90
|
+
"steering.blocked_by_hook": "retriable",
|
|
91
|
+
"checkpoint.unsupported_version": "by_reason",
|
|
92
|
+
"checkpoint.already_resolved": "terminal",
|
|
93
|
+
"checkpoint.not_found": "terminal",
|
|
94
|
+
"checkpoint.gate_mismatch": "terminal",
|
|
95
|
+
"checkpoint.reopen_failed": "terminal",
|
|
96
|
+
"checkpoint.cwd_conflicts_restore": "terminal",
|
|
97
|
+
"checkpoint.already_exists": "terminal",
|
|
98
|
+
"checkpoint.execution_outcome_conflict": "terminal",
|
|
99
|
+
"steering.duplicate_input_id": "terminal",
|
|
100
|
+
"steering.invalid_content": "terminal",
|
|
101
|
+
"steering.queue_full": "terminal",
|
|
102
|
+
};
|
|
103
|
+
export function checkpointErrorDisposition(e) {
|
|
104
|
+
const word = Object.hasOwn(CHECKPOINT_ERROR_DISPOSITION, e.code) ? CHECKPOINT_ERROR_DISPOSITION[e.code] : undefined;
|
|
105
|
+
switch (word) {
|
|
106
|
+
case "cancelled":
|
|
107
|
+
case "retriable":
|
|
108
|
+
case "terminal":
|
|
109
|
+
return word;
|
|
110
|
+
case "by_reason":
|
|
111
|
+
return checkpointRowRedeemableElsewhere(e.detail?.reason) ? "retriable" : "terminal";
|
|
112
|
+
case undefined:
|
|
113
|
+
return "terminal";
|
|
114
|
+
default: {
|
|
115
|
+
const unhandled = word;
|
|
116
|
+
throw new Error(`unhandled checkpoint-error disposition: ${String(unhandled)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const CHECKPOINT_ERROR_FIELD_ROLE = {
|
|
121
|
+
boundCallId: "binding",
|
|
122
|
+
boundInputHash: "binding",
|
|
123
|
+
answer: "answer",
|
|
124
|
+
hostDecision: "outcome_cell",
|
|
125
|
+
approver: "outcome_cell",
|
|
126
|
+
taskConfig: "config",
|
|
127
|
+
inheritedGate: "config",
|
|
128
|
+
};
|
|
129
|
+
const NON_BINDING_INVALID_OUTCOME_FIELDS = new Set(Object.keys(CHECKPOINT_ERROR_FIELD_ROLE).filter((f) => CHECKPOINT_ERROR_FIELD_ROLE[f] === "outcome_cell"));
|
|
74
130
|
function notResumableAnswer() {
|
|
75
131
|
return { status: 409, body: { error: "run is not in a resumable (suspended) state (already resumed, decided, or expired)", errorCode: "conflict.not_resumable" } };
|
|
76
132
|
}
|
|
@@ -726,6 +782,7 @@ export function createResumeLegs(ctx) {
|
|
|
726
782
|
causeAtDone = cancelCtrl.cause();
|
|
727
783
|
recordTurnActivity(taskId);
|
|
728
784
|
fleetPub?.onEvent(ev);
|
|
785
|
+
await deps.userInputLedger?.observeLegEvent(stream, ev);
|
|
729
786
|
await appendPromptManifest(append, deps.promptManifests, taskId);
|
|
730
787
|
switch (ev.type) {
|
|
731
788
|
case "text_delta":
|
|
@@ -891,7 +948,8 @@ export function createResumeLegs(ctx) {
|
|
|
891
948
|
}
|
|
892
949
|
catch (e) {
|
|
893
950
|
if (e instanceof CheckpointError) {
|
|
894
|
-
|
|
951
|
+
const disposition = checkpointErrorDisposition(e);
|
|
952
|
+
if (disposition === "cancelled") {
|
|
895
953
|
const cs2 = deps.checkpointStore;
|
|
896
954
|
if (cs2) {
|
|
897
955
|
try {
|
|
@@ -917,19 +975,7 @@ export function createResumeLegs(ctx) {
|
|
|
917
975
|
settleFleet("failed");
|
|
918
976
|
return { status: 200, body: { taskId, sessionId, status: "failed", errorCode: "cancelled" } };
|
|
919
977
|
}
|
|
920
|
-
const retriable =
|
|
921
|
-
e.code === "checkpoint.reopen_revote" ||
|
|
922
|
-
e.code === "checkpoint.reopened_concurrently" ||
|
|
923
|
-
e.code === "resume.parent_constraint_missing" ||
|
|
924
|
-
e.code === "resume.parent_constraint_mismatch" ||
|
|
925
|
-
e.code === "resume.constraint_rejected" ||
|
|
926
|
-
e.code === "resume.constraint_unprojectable" ||
|
|
927
|
-
e.code === "resume.usage_window_exhausted" ||
|
|
928
|
-
e.code === "resume.principal_mismatch" ||
|
|
929
|
-
e.code === "resume.placement_mismatch" ||
|
|
930
|
-
e.code === "resume.preflight_rejected" ||
|
|
931
|
-
e.code.startsWith("wake.") ||
|
|
932
|
-
(e.code === "checkpoint.unsupported_version" && checkpointRowRedeemableElsewhere(e.detail?.reason));
|
|
978
|
+
const retriable = disposition === "retriable";
|
|
933
979
|
if (claimedRow && taskId && deps.runStore) {
|
|
934
980
|
if (retriable && freshRunClaimed) {
|
|
935
981
|
const c = { taskId, sessionId, terminal: { kind: "failed", code: e.code, message: e.message }, stats: { turns: 0, tokens: 0, usageMissing: true } };
|
|
@@ -1204,7 +1250,7 @@ export function createResumeLegs(ctx) {
|
|
|
1204
1250
|
return resolved.answer;
|
|
1205
1251
|
const { spec, engineNotices } = resolved;
|
|
1206
1252
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
1207
|
-
return driveResumeIntoRunLog({
|
|
1253
|
+
return buildHostLegOutcome(await driveResumeIntoRunLog({
|
|
1208
1254
|
runner: runnerFor(spec),
|
|
1209
1255
|
token,
|
|
1210
1256
|
sessionId: cp.sessionId,
|
|
@@ -1221,7 +1267,7 @@ export function createResumeLegs(ctx) {
|
|
|
1221
1267
|
...(req !== undefined ? { callerInitiated: true } : {}),
|
|
1222
1268
|
...(workflowParkedResume !== undefined ? { workflowParkedResume } : {}),
|
|
1223
1269
|
...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
|
|
1224
|
-
});
|
|
1270
|
+
}));
|
|
1225
1271
|
}
|
|
1226
1272
|
async function preparePlanReview(sessionId, log, decider) {
|
|
1227
1273
|
const cs = deps.checkpointStore;
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -72,8 +72,10 @@ export interface RouteHelpers {
|
|
|
72
72
|
usageWindowDenied(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
|
|
73
73
|
safeDecode(seg: string): string | null;
|
|
74
74
|
isFleetWide(req: IncomingMessage): boolean;
|
|
75
|
-
/** 属主门。`notFoundForm` 选 not-yours 臂的 404 拼法:缺省 `"run"`(not_found.run 长文,[ref])
|
|
76
|
-
*
|
|
75
|
+
/** 属主门。`notFoundForm` 选 not-yours 臂的 404 拼法:缺省 `"run"`(not_found.run 长文,[ref]);**每一个 session
|
|
76
|
+
* 资源面都必须传 `"session"`**(not_found.session + {@link SESSION_NOT_FOUND_MESSAGE})与其 unknown 臂同码同串
|
|
77
|
+
* (G52 [ref];S-989 补上了 detail / head / events 三个读口 —— 它们此前吃缺省 "run",单端点内 unknown / not-yours
|
|
78
|
+
* 两臂码与串都不同)。缺省 `"run"` 的语义不变:run 面不传。 */
|
|
77
79
|
runOwnerOk(req: IncomingMessage, res: ServerResponse, owner: string | null, notFoundForm?: "run" | "session"): boolean;
|
|
78
80
|
sessionOwnerScope(req: IncomingMessage): {
|
|
79
81
|
fleetWide: boolean;
|
|
@@ -408,10 +410,7 @@ export interface RouteLegs {
|
|
|
408
410
|
* 顶注)。HTTP `/wake` 腿**不设**此位 —— 它是 `/decide` 第三条车道的专用席,而两条腿共用这一条驱动
|
|
409
411
|
* 路径正是 `notify-wake.ts` 那条「wake 会烧模型,所以复用 resume 家族的同一条腿、不自建第二条驱动
|
|
410
412
|
* 路径」的成文纪律。 */
|
|
411
|
-
workflowParkedResume?: DriveResumeArgs["workflowParkedResume"]): Promise<
|
|
412
|
-
status: number;
|
|
413
|
-
body: object;
|
|
414
|
-
}>;
|
|
413
|
+
workflowParkedResume?: DriveResumeArgs["workflowParkedResume"]): Promise<HostLegOutcome>;
|
|
415
414
|
/**
|
|
416
415
|
* assistant plan_review 三态腿的**纯读段**(S-609,7.97.0):选卡 → 属主轻读 → 载入 → 行级 scope → 门类 → 这条决断
|
|
417
416
|
* 唯一的一次 ctx 读。任何一步拒 ⇒ `{ok:false, answer}`(不改任何状态;端点在消费一次性 nonce **之前**把它答出去);
|
|
@@ -626,7 +625,7 @@ export type RouteDecl = RouteDispatchDecl | RouteGateDecl;
|
|
|
626
625
|
* api-error-text-freeze 基线流程。
|
|
627
626
|
*/
|
|
628
627
|
/**
|
|
629
|
-
* resume
|
|
628
|
+
* resume 家族**引擎腿**的结局:HTTP 应答(`status` / `body`)+ 一位**进程内**事实(不上 wire)。workflow 宿主腿的结局另型({@link HostLegOutcome}),并集 = {@link ResumeLegOutcome}。
|
|
630
629
|
*
|
|
631
630
|
* `decisionCommitted` —— **这条腿的终局是在引擎受理了这次决定之后才结出来的**([ref] X5,7.112.0):本腿走到了「引擎交回结果」那一步
|
|
632
631
|
* (`resumeStream` / `resumeWithVerification` 解析 = core 的全部 pre-CAS 门 + checkpoint resolve CAS 都过了 —— 受理点的判据属主
|
|
@@ -640,11 +639,34 @@ export type RouteDecl = RouteDispatchDecl | RouteGateDecl;
|
|
|
640
639
|
* 读它」(`plugins/store-contracts.ts` `RunCancelContext` 顶注)—— 这两道门正是判定;而且门读的是 `/decide` 的平面体、看不到结果对象。
|
|
641
640
|
* 受理与否是腿自己的控制流事实,在腿上说,零 wire 变更。
|
|
642
641
|
*/
|
|
643
|
-
export interface
|
|
642
|
+
export interface EngineLegOutcome {
|
|
644
643
|
status: number;
|
|
645
644
|
body: object;
|
|
646
645
|
decisionCommitted?: true;
|
|
647
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* **workflow 宿主腿的结局(wake / fresh)—— 型上就没有 `decisionCommitted`**(S-992 ②,7.113.0;`never` 让「这条路径不铸」成为**编译事实**,不是靠人记)。
|
|
649
|
+
*
|
|
650
|
+
* 为什么这条路径不该有该位:`/decide` 的 workflow 第三条车道(`resumeCheckpoint` 的 parked 分流,`planWorkflowParkedDecide` 之后)与 `POST /v1/sessions/:id/wake`
|
|
651
|
+
* 把决定 / 消息交给**宿主会话的下一跑**(`resumeWake` / `startFreshHostTurn`)。它们的 200 只到「已交给宿主的下一跑」—— 子代 checkpoint 的 resolve CAS 发生在
|
|
652
|
+
* workflow 引擎自己驱动那一步,server 观察不到(`resume-legs.ts` 该分流处「刻意不落 `late_settle`」同源)。「这次决定被引擎受理了」这句话在这里**没有可说的对象**,
|
|
653
|
+
* 于是 `decisionCommitted` 在这条路径上语义含混(修前:`resumeWake` 的收尾是 `driveResumeIntoRunLog` 的 `ResumeLegOutcome`,非受理点早返形(无 `acceptEarly`)下它会把
|
|
654
|
+
* 引擎腿的 `decisionCommitted:true` 原样带出去 —— 型被声明返回型 `{status, body}` 擦掉了,运行期仍在)。读者核实:两道提交门(`noteLateSettleIfAccepted` 迟到结算留痕、
|
|
655
|
+
* `/decide` 的 remember 豁免落店)在这条路径上今天**零读取**(前者该分流直接 `return`、不调;后者的 `pendingGrant` 只由引擎腿的 `onResumeCommitted` 回调置位,宿主腿不调它,且 `planWorkflowParkedDecide` 对 workflow 车道的 `remember` 本就 fail-closed 拒 —— `decide.workflow_remember_unsupported`,`parked-decide.ts`)——
|
|
656
|
+
* 无读者 ⇒ 剥掉该位,而不是留一个「铸了也没人读、读了不知道什么意思」的位。出口经 {@link buildHostLegOutcome} 只带 `status` / `body`。
|
|
657
|
+
*/
|
|
658
|
+
export interface HostLegOutcome {
|
|
659
|
+
status: number;
|
|
660
|
+
body: object;
|
|
661
|
+
decisionCommitted?: never;
|
|
662
|
+
}
|
|
663
|
+
/** resume 家族一条腿的结局:引擎腿(可带 `decisionCommitted`)∪ workflow 宿主腿(型上没有)。`out.decisionCommitted === true` 同时收窄到引擎腿。 */
|
|
664
|
+
export type ResumeLegOutcome = EngineLegOutcome | HostLegOutcome;
|
|
665
|
+
/** 把任意腿的结局折成宿主腿结局:**只带 `status` / `body`**(丢掉引擎腿的 `decisionCommitted` —— 见 {@link HostLegOutcome})。纯数据。 */
|
|
666
|
+
export declare function buildHostLegOutcome(out: {
|
|
667
|
+
status: number;
|
|
668
|
+
body: object;
|
|
669
|
+
}): HostLegOutcome;
|
|
648
670
|
/**
|
|
649
671
|
* **「决定/续跑已受理」那一枚 200 收据的单一铸点**(S-218)。
|
|
650
672
|
*
|
|
@@ -660,6 +682,14 @@ export declare function acceptedResumingReceipt(taskId: string, sessionId: strin
|
|
|
660
682
|
body: object;
|
|
661
683
|
};
|
|
662
684
|
export declare const RUN_NOT_FOUND_MESSAGE = "run not found \u2014 the id belongs to no run in this deployment's run store (a run from another server process, or an in-memory store that did not survive a restart, is not visible here)";
|
|
685
|
+
/**
|
|
686
|
+
* 会话资源面 404 的**唯一一句**(与 {@link RUN_NOT_FOUND_MESSAGE} 同一冻结纪律,常量锚在 `test/api-error-text-freeze.test.ts`)。
|
|
687
|
+
*
|
|
688
|
+
* 为什么是常量不是各点字面量:这句话要在**每一个**会话面的「不存在」臂与「非属主」臂上逐字节同串(G52 [ref] /
|
|
689
|
+
* S-989 —— 两臂差一个码或一个字就是单端点内的存在性 oracle)。十余个出站点各抄一份,漂开的那天没有任何东西会说话;
|
|
690
|
+
* 走一只常量 + `runOwnerOk(..., "session")` 的会话拼法,「改一处」就是改全域。
|
|
691
|
+
*/
|
|
692
|
+
export declare const SESSION_NOT_FOUND_MESSAGE = "session not found";
|
|
663
693
|
/** [ref] 前缀=域判别子既成契约:a\* 与 wa\* 形 id 给 TaskOutput/journal 指路;其余给 {@link RUN_NOT_FOUND_MESSAGE}。 */
|
|
664
694
|
export declare function runNotFoundMessage(id: string): string;
|
|
665
695
|
export {};
|
package/dist/http/route-ctx.js
CHANGED
|
@@ -21,10 +21,14 @@ export function assertNever(x) {
|
|
|
21
21
|
throw new Error(`unreachable route id: ${String(x)}`);
|
|
22
22
|
}
|
|
23
23
|
export const ROUTE_METHOD_ORDER = ["GET", "POST", "PUT", "DELETE"];
|
|
24
|
+
export function buildHostLegOutcome(out) {
|
|
25
|
+
return { status: out.status, body: out.body };
|
|
26
|
+
}
|
|
24
27
|
export function acceptedResumingReceipt(taskId, sessionId) {
|
|
25
28
|
return { status: 200, body: { taskId, sessionId, status: "resuming", bindingEnforced: true } };
|
|
26
29
|
}
|
|
27
30
|
export const RUN_NOT_FOUND_MESSAGE = "run not found — the id belongs to no run in this deployment's run store (a run from another server process, or an in-memory store that did not survive a restart, is not visible here)";
|
|
31
|
+
export const SESSION_NOT_FOUND_MESSAGE = "session not found";
|
|
28
32
|
export function runNotFoundMessage(id) {
|
|
29
33
|
const aStar = /^(?:a|wa)[0-9a-f]{4,}$/i.test(id);
|
|
30
34
|
return aStar
|
|
@@ -5,7 +5,7 @@ import { redactSecrets } from "../../trace/redact.js";
|
|
|
5
5
|
import { sendJson, sendError } from "../send.js";
|
|
6
6
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
7
7
|
import {} from "../route-ctx.js";
|
|
8
|
-
import { assertNever } from "../route-ctx.js";
|
|
8
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
9
9
|
const LIST_LIMIT = 500;
|
|
10
10
|
export const DESCRIPTION_MAX = 500;
|
|
11
11
|
const ROW_KEY_DISPOSITION = {
|
|
@@ -65,7 +65,7 @@ export async function handleBackground(req, res, match, ctx) {
|
|
|
65
65
|
}
|
|
66
66
|
const owner = await deps.sessionStorage.ownerOf(sessionId);
|
|
67
67
|
if (owner === undefined) {
|
|
68
|
-
sendError(res, 404, "not_found.session",
|
|
68
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
const isOperator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
@@ -24,7 +24,7 @@ import { projectApprovalsStreamLiveCapability } from "../../capabilities/approva
|
|
|
24
24
|
import { workflowModelAllowlistFor, selfOrchestrationDenial } from "../../task-workflow.js";
|
|
25
25
|
import { centerEntitlementSourceWired } from "../../runtime-caps-resolver.js";
|
|
26
26
|
import { judgeAutoModeArming } from "../../auto-mode-face.js";
|
|
27
|
-
import { coercePermissionModeStrict, SETTINGS_PERMISSION_MODE_WORDS } from "../../task-settings.js";
|
|
27
|
+
import { coercePermissionModeStrict, SETTINGS_PERMISSION_MODE_WORDS, taskHooksHonored } from "../../task-settings.js";
|
|
28
28
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
29
29
|
import { sessionBackgroundFacesAvailable } from "./background.js";
|
|
30
30
|
function routePairingOf(deps, name, m) {
|
|
@@ -121,7 +121,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
121
121
|
scratchpadDir: true,
|
|
122
122
|
subagentSteer: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
|
|
123
123
|
subagentResume: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
|
|
124
|
-
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: cwdHonored(deps.config) || deviceCwdHonored(deps.config), hooks: deps.config
|
|
124
|
+
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: cwdHonored(deps.config) || deviceCwdHonored(deps.config), hooks: taskHooksHonored(deps.config) },
|
|
125
125
|
permissionModeAuto: {
|
|
126
126
|
accepted: true,
|
|
127
127
|
classifierSeat: deps.autoModeFace?.seatMounted === true,
|
|
@@ -4,7 +4,7 @@ import { redactSecrets } from "../../trace/redact.js";
|
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
5
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
import {} from "../route-ctx.js";
|
|
7
|
-
import { assertNever } from "../route-ctx.js";
|
|
7
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
8
8
|
const BodySchema = z.object({ server: z.string().min(1).max(190) }).strict();
|
|
9
9
|
const NEUTRALIZED_TEXT_MAX = 200;
|
|
10
10
|
const humanText = (s) => redactSecrets(s).slice(0, NEUTRALIZED_TEXT_MAX);
|
|
@@ -82,7 +82,7 @@ export async function handleMcpReconnect(req, res, match, ctx) {
|
|
|
82
82
|
}
|
|
83
83
|
const owner = await deps.sessionStorage.ownerOf(sessionId);
|
|
84
84
|
if (owner === undefined) {
|
|
85
|
-
sendError(res, 404, "not_found.session",
|
|
85
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
const isOperator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
@@ -2,7 +2,7 @@ import { taskNotificationInboxEntry } from "../../orchestration/workflow-complet
|
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
5
|
-
import { assertNever } from "../route-ctx.js";
|
|
5
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
6
6
|
export async function handleNotifyWake(req, res, match, ctx) {
|
|
7
7
|
const { deps } = ctx;
|
|
8
8
|
const { steerableRuns } = ctx.registry;
|
|
@@ -63,12 +63,12 @@ export async function handleNotifyWake(req, res, match, ctx) {
|
|
|
63
63
|
}
|
|
64
64
|
const notifyOwner = await deps.sessionStorage.ownerOf(notifySession);
|
|
65
65
|
if (notifyOwner === undefined) {
|
|
66
|
-
sendError(res, 404, "not_found.session",
|
|
66
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
const notifyOperator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
70
70
|
if (!notifyOperator && notifyOwner !== null && notifyOwner !== principal) {
|
|
71
|
-
sendError(res, 404, "not_found.session",
|
|
71
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
const payload = {
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -146,6 +146,20 @@ async function acceptLiveUserInput(deps, res, at) {
|
|
|
146
146
|
return "refused";
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
+
async function relayAcceptedInput(deps, acceptance, call) {
|
|
150
|
+
let out;
|
|
151
|
+
try {
|
|
152
|
+
out = await call();
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
if (acceptance !== undefined)
|
|
156
|
+
await deps.userInputLedger.withdraw(acceptance);
|
|
157
|
+
throw e;
|
|
158
|
+
}
|
|
159
|
+
if (acceptance !== undefined)
|
|
160
|
+
await deps.userInputLedger.confirm(acceptance);
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
149
163
|
export async function handleRuns(req, res, match, ctx) {
|
|
150
164
|
const { deps } = ctx;
|
|
151
165
|
const { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints } = ctx.registry;
|
|
@@ -481,13 +495,11 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
481
495
|
if (acceptance === "refused")
|
|
482
496
|
return;
|
|
483
497
|
try {
|
|
484
|
-
await live.steer(text, { trusted,
|
|
498
|
+
await relayAcceptedInput(deps, acceptance, () => live.steer(text, { trusted, inputId: messageId }));
|
|
485
499
|
sendJson(res, 200, { taskId, status: "running", delivery: "applied", messageId, ...(priority ? { priority } : {}) });
|
|
486
500
|
return;
|
|
487
501
|
}
|
|
488
502
|
catch (e) {
|
|
489
|
-
if (acceptance !== undefined)
|
|
490
|
-
await deps.userInputLedger.withdraw(acceptance);
|
|
491
503
|
const code = e.code;
|
|
492
504
|
if (code === "steering.invalid_content") {
|
|
493
505
|
sendError(res, 422, "steering.invalid_content", e instanceof Error ? e.message : "invalid steering content");
|
|
@@ -794,11 +806,9 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
794
806
|
if (acceptance === "refused")
|
|
795
807
|
return;
|
|
796
808
|
try {
|
|
797
|
-
await liveStream.steer(text, { trusted, priority: "now", ...(interruptInputId !== undefined ? { inputId: interruptInputId } : {}) });
|
|
809
|
+
await relayAcceptedInput(deps, acceptance, () => liveStream.steer(text, { trusted, priority: "now", ...(interruptInputId !== undefined ? { inputId: interruptInputId } : {}) }));
|
|
798
810
|
}
|
|
799
811
|
catch (e) {
|
|
800
|
-
if (acceptance !== undefined)
|
|
801
|
-
await deps.userInputLedger.withdraw(acceptance);
|
|
802
812
|
const code = e.code;
|
|
803
813
|
if (code === "steering.invalid_content") {
|
|
804
814
|
sendError(res, 422, "steering.invalid_content", e instanceof Error ? e.message : "invalid steering content");
|
|
@@ -1152,7 +1162,6 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1152
1162
|
}
|
|
1153
1163
|
const handle = resolution?.handle;
|
|
1154
1164
|
if (handle) {
|
|
1155
|
-
let acceptance;
|
|
1156
1165
|
try {
|
|
1157
1166
|
if (verb === "resume") {
|
|
1158
1167
|
if (!handle.resume) {
|
|
@@ -1169,11 +1178,10 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1169
1178
|
return;
|
|
1170
1179
|
}
|
|
1171
1180
|
const messageId = uuidv7();
|
|
1172
|
-
const
|
|
1173
|
-
if (
|
|
1181
|
+
const acceptance = await acceptLiveUserInput(deps, res, { messageId, sessionId: run.sessionId, taskId: runId, leg: handle });
|
|
1182
|
+
if (acceptance === "refused")
|
|
1174
1183
|
return;
|
|
1175
|
-
|
|
1176
|
-
const marker = await handle.steer(redacted);
|
|
1184
|
+
const marker = await relayAcceptedInput(deps, acceptance, () => handle.steer(redacted));
|
|
1177
1185
|
if (acceptance !== undefined) {
|
|
1178
1186
|
const ledger = deps.userInputLedger;
|
|
1179
1187
|
const settle = () => ledger.settleLeg(handle);
|
|
@@ -1183,8 +1191,6 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1183
1191
|
return;
|
|
1184
1192
|
}
|
|
1185
1193
|
catch (e) {
|
|
1186
|
-
if (acceptance !== undefined)
|
|
1187
|
-
await deps.userInputLedger.withdraw(acceptance);
|
|
1188
1194
|
const code = e.code;
|
|
1189
1195
|
if (code === "steering.not_running") {
|
|
1190
1196
|
send409(code, "sub-agent just finished — no longer accepting steers");
|
|
@@ -12,7 +12,7 @@ const SYNC_BLOB_MAX_BYTES = 64 * 1024 * 1024;
|
|
|
12
12
|
const SYNC_NDJSON_LINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
13
13
|
const SYNC_IMPORT_BATCH = 500;
|
|
14
14
|
import { SHA256_HEX_RE } from "../../digest-form.js";
|
|
15
|
-
import { assertNever } from "../route-ctx.js";
|
|
15
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
16
16
|
import { readSessionRulesRecords, describeSessionRulesRecordProblem, legacyToolNameProblemOf } from "../../session-policy-wire.js";
|
|
17
17
|
export function createSessionSyncLocal() {
|
|
18
18
|
const pendingStagings = new Map();
|
|
@@ -60,18 +60,18 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
60
60
|
const ownsOrFleet = (o) => scope.fleetWide || o === scope.gateOwner;
|
|
61
61
|
const pullGateOk = () => {
|
|
62
62
|
if (owner === undefined) {
|
|
63
|
-
sendError(res, 404, "not_found.session",
|
|
63
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
66
|
if (!ownsOrFleet(owner)) {
|
|
67
|
-
sendError(res, 404, "not_found.session",
|
|
67
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
68
68
|
return false;
|
|
69
69
|
}
|
|
70
70
|
return true;
|
|
71
71
|
};
|
|
72
72
|
const pushGateOk = () => {
|
|
73
73
|
if (owner !== undefined && !ownsOrFleet(owner)) {
|
|
74
|
-
sendError(res, 404, "not_found.session",
|
|
74
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
77
|
return true;
|
|
@@ -89,7 +89,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
89
89
|
throw e;
|
|
90
90
|
}
|
|
91
91
|
if (out === null) {
|
|
92
|
-
sendError(res, 404, "not_found.session",
|
|
92
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
sendJson(res, 200, { manifest: out.manifest });
|
|
@@ -115,7 +115,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
115
115
|
}
|
|
116
116
|
const it = await stream(sessionId, afterSeq === undefined ? undefined : { afterSeq });
|
|
117
117
|
if (it === null) {
|
|
118
|
-
sendError(res, 404, "not_found.session",
|
|
118
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
121
|
res.writeHead(200, { "content-type": "application/x-ndjson", "transfer-encoding": "chunked" });
|
|
@@ -9,7 +9,7 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SET
|
|
|
9
9
|
import { readWritableSessionPermissionRules, describeSessionPermissionRulesProblem } from "../../session-policy-wire.js";
|
|
10
10
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders } from "../send.js";
|
|
11
11
|
import { gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
|
|
12
|
-
import { assertNever } from "../route-ctx.js";
|
|
12
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
13
13
|
import { rosterAgentId } from "./agents-roster.js";
|
|
14
14
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
15
15
|
const MCP_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
@@ -68,15 +68,15 @@ async function readLastLegMcp(deps, sessionId, principal) {
|
|
|
68
68
|
export async function handleSessions(req, res, match, ctx) {
|
|
69
69
|
const { deps } = ctx;
|
|
70
70
|
const { readJson, safeDecode, runOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite } = ctx.helpers;
|
|
71
|
-
const readOwnedSessionView = async (sessionAudit, sessionId
|
|
71
|
+
const readOwnedSessionView = async (sessionAudit, sessionId) => {
|
|
72
72
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
73
73
|
if (ownerOf !== undefined) {
|
|
74
74
|
const owner = await ownerOf(sessionId);
|
|
75
75
|
if (owner === undefined) {
|
|
76
|
-
sendError(res, 404, "not_found.session",
|
|
76
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
77
77
|
return undefined;
|
|
78
78
|
}
|
|
79
|
-
if (!runOwnerOk(req, res, owner,
|
|
79
|
+
if (!runOwnerOk(req, res, owner, "session"))
|
|
80
80
|
return undefined;
|
|
81
81
|
}
|
|
82
82
|
let audit;
|
|
@@ -89,10 +89,10 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
89
89
|
throw new Error(`session view read failed before ownership could be established: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
90
90
|
}
|
|
91
91
|
if (!audit) {
|
|
92
|
-
sendError(res, 404, "not_found.session",
|
|
92
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
93
93
|
return undefined;
|
|
94
94
|
}
|
|
95
|
-
if (!runOwnerOk(req, res, audit.owner,
|
|
95
|
+
if (!runOwnerOk(req, res, audit.owner, "session"))
|
|
96
96
|
return undefined;
|
|
97
97
|
return audit;
|
|
98
98
|
};
|
|
@@ -261,21 +261,21 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
261
261
|
if (getHead) {
|
|
262
262
|
const head = await getHead(headSession);
|
|
263
263
|
if (head === undefined) {
|
|
264
|
-
sendError(res, 404, "not_found.session",
|
|
264
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
265
265
|
return;
|
|
266
266
|
}
|
|
267
267
|
owner = head.owner;
|
|
268
|
-
if (!runOwnerOk(req, res, owner ?? null))
|
|
268
|
+
if (!runOwnerOk(req, res, owner ?? null, "session"))
|
|
269
269
|
return;
|
|
270
270
|
leafId = head.leafId;
|
|
271
271
|
}
|
|
272
272
|
else {
|
|
273
273
|
owner = await ownerOf(headSession);
|
|
274
274
|
if (owner === undefined) {
|
|
275
|
-
sendError(res, 404, "not_found.session",
|
|
275
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
276
276
|
return;
|
|
277
277
|
}
|
|
278
|
-
if (!runOwnerOk(req, res, owner ?? null))
|
|
278
|
+
if (!runOwnerOk(req, res, owner ?? null, "session"))
|
|
279
279
|
return;
|
|
280
280
|
leafId = await getLeafId(headSession);
|
|
281
281
|
}
|
|
@@ -355,10 +355,10 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
355
355
|
}
|
|
356
356
|
if (owner === undefined) {
|
|
357
357
|
cleanup();
|
|
358
|
-
sendError(res, 404, "not_found.session",
|
|
358
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
359
359
|
return;
|
|
360
360
|
}
|
|
361
|
-
if (!runOwnerOk(req, res, owner ?? null)) {
|
|
361
|
+
if (!runOwnerOk(req, res, owner ?? null, "session")) {
|
|
362
362
|
cleanup();
|
|
363
363
|
return;
|
|
364
364
|
}
|
|
@@ -498,7 +498,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
498
498
|
sendError(res, 501, "capability.memory_engine_required", "memory provenance and erasure are not available on this deployment — they require a wired memory engine (MEMORY_ENGINE=on) whose backend owns the engine control plane (controlPlaneRoot); the file memory backend does, the pg/tidb memory backends do not (their control plane would land on a replica's local disk)");
|
|
499
499
|
return;
|
|
500
500
|
}
|
|
501
|
-
if ((await readOwnedSessionView(deps.sessionAudit, sessionId
|
|
501
|
+
if ((await readOwnedSessionView(deps.sessionAudit, sessionId)) === undefined)
|
|
502
502
|
return;
|
|
503
503
|
const faces = deps.memoryCompliance;
|
|
504
504
|
if (faces === undefined) {
|
|
@@ -550,7 +550,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
550
550
|
sendError(res, 501, "capability.memory_engine_required", "session memory status is not available on this deployment — it requires a wired memory engine (MEMORY_ENGINE=on) whose backend owns the engine control plane (controlPlaneRoot); the file memory backend does, the pg/tidb memory backends do not (their control plane would land on a replica's local disk)");
|
|
551
551
|
return;
|
|
552
552
|
}
|
|
553
|
-
if ((await readOwnedSessionView(deps.sessionAudit, sessionId
|
|
553
|
+
if ((await readOwnedSessionView(deps.sessionAudit, sessionId)) === undefined)
|
|
554
554
|
return;
|
|
555
555
|
const face = deps.sessionMemoryStatus;
|
|
556
556
|
if (face === undefined) {
|
|
@@ -594,7 +594,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
594
594
|
sendError(res, 404, "not_found.route", "not found");
|
|
595
595
|
return;
|
|
596
596
|
}
|
|
597
|
-
const audit = await readOwnedSessionView(deps.sessionAudit, match.params.sessionId
|
|
597
|
+
const audit = await readOwnedSessionView(deps.sessionAudit, match.params.sessionId);
|
|
598
598
|
if (audit === undefined)
|
|
599
599
|
return;
|
|
600
600
|
const q = new URLSearchParams(match.search);
|
|
@@ -665,17 +665,17 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
665
665
|
}
|
|
666
666
|
const owner = await ownerOf(sourceId);
|
|
667
667
|
if (owner === undefined) {
|
|
668
|
-
sendError(res, 404, "not_found.session",
|
|
668
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
669
669
|
return;
|
|
670
670
|
}
|
|
671
671
|
if (!scope.fleetWide && owner !== scope.gateOwner && !legacyUnownedSessionOk(deps.config.requirePrincipal, scope.gateOwner, owner)) {
|
|
672
|
-
sendError(res, 404, "not_found.session",
|
|
672
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
673
673
|
return;
|
|
674
674
|
}
|
|
675
675
|
const newOwner = scope.fleetWide ? (owner ?? null) : scope.gateOwner;
|
|
676
676
|
const newId = await fork(sourceId, newOwner);
|
|
677
677
|
if (newId === null) {
|
|
678
|
-
sendError(res, 404, "not_found.session",
|
|
678
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
679
679
|
return;
|
|
680
680
|
}
|
|
681
681
|
sendJson(res, 200, { sessionId: newId });
|
|
@@ -747,7 +747,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
747
747
|
}
|
|
748
748
|
const owner = await ownerOf(sessionId);
|
|
749
749
|
if (owner === undefined) {
|
|
750
|
-
sendError(res, 404, "not_found.session",
|
|
750
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
751
751
|
return;
|
|
752
752
|
}
|
|
753
753
|
if (owner === null) {
|
|
@@ -756,7 +756,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
756
756
|
}
|
|
757
757
|
const operator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
758
758
|
if (!operator && owner !== (principal ?? null)) {
|
|
759
|
-
sendError(res, 404, "not_found.session",
|
|
759
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
760
760
|
return;
|
|
761
761
|
}
|
|
762
762
|
const target = owner;
|
|
@@ -418,6 +418,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
418
418
|
if (ev.type === "message_committed" && ev.role === "user")
|
|
419
419
|
userMsgEntryId = ev.entryId;
|
|
420
420
|
const causeAtDone = ev.type === "done" ? ac.cause() : undefined;
|
|
421
|
+
await deps.userInputLedger?.observeLegEvent(liveStream, ev);
|
|
421
422
|
if (ev.type === "done") {
|
|
422
423
|
finalResult = await mintSubmitExitResult(ev.result, deps.checkpointStore);
|
|
423
424
|
if (durableTaskId && finalResult)
|
package/dist/http/send.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export declare function sendSessionPolicyError(res: ServerResponse, e: unknown):
|
|
|
80
80
|
* 句子:属主给了固定句就发它(import 面 `invalid_session` 那一句),否则发 core 的原句过既有脱敏(`redactErrorMessage`:
|
|
81
81
|
* 不抛、有上限)。core 的 `corrupt_session` 原句只点名会话 id 与跳过计数(「Session <id> is unreadable: its log has no
|
|
82
82
|
* meta header (N of M records skipped as corrupt)」),SQL 两店的原句点名 `<表>.<列>` 与行号 —— 两者都不含盘上路径
|
|
83
|
-
* (
|
|
83
|
+
* (路径只进运维面:`server.session-repo.corrupt-record-skipped` 的探针行,S-990;SQL 两店走各自的拒绝句)。
|
|
84
84
|
*/
|
|
85
85
|
export declare function sendSessionError(res: ServerResponse, e: unknown, face: SessionErrorFace): boolean;
|
|
86
86
|
export declare function msg(e: unknown): string;
|
package/dist/http/send.js
CHANGED
|
@@ -43,7 +43,7 @@ export function sendSessionPolicyError(res, e) {
|
|
|
43
43
|
if (!(e instanceof SessionPolicyError))
|
|
44
44
|
return false;
|
|
45
45
|
const h = sessionPolicyErrorHttp(e.code);
|
|
46
|
-
sendError(res, h.status, h.errorCode, e.message);
|
|
46
|
+
sendError(res, h.status, h.errorCode, redactErrorMessage(e.message) ?? "session policy error");
|
|
47
47
|
return true;
|
|
48
48
|
}
|
|
49
49
|
export function sendSessionError(res, e, face) {
|
|
@@ -233,13 +233,13 @@ export interface TaskRequestBody {
|
|
|
233
233
|
* core 默认开列表注入的通道)与三个 deadline opt-out 在类型面失踪。agentListing/skillsListing 是
|
|
234
234
|
* boolean 透传(core DEFAULT-ON,explicit false 才关);budgetUsd/mcpInstructions 只认 literal true;
|
|
235
235
|
* limits 三个 opt-out 只认 literal false(normalizer 同判)。 */
|
|
236
|
+
/** core 7.39.0 #1254:`planModeReminder` 退役(硬破坏,零别名)—— 提交即 400 点名退役;plan 姿态由引擎从 `permissionMode:"plan"` 自己陈述。 */
|
|
236
237
|
attachments?: {
|
|
237
238
|
todoReminder?: boolean;
|
|
238
239
|
todoReminderMode?: "baseline" | "off";
|
|
239
240
|
changedFiles?: boolean | {
|
|
240
241
|
maxFiles?: number;
|
|
241
242
|
};
|
|
242
|
-
planModeReminder?: boolean;
|
|
243
243
|
budgetUsd?: true;
|
|
244
244
|
backgroundTasks?: boolean;
|
|
245
245
|
toolsDelta?: boolean;
|