@sema-agent/server 7.69.0 → 7.70.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/README.md +3 -0
- package/README.zh-CN.md +3 -0
- package/USAGE.md +103 -7
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +3 -2
- package/dist/approval-reconciler.d.ts +0 -54
- package/dist/approval-reconciler.js +2 -2
- package/dist/boot/parked-revive-gate.js +10 -9
- package/dist/boot/runner-deps.d.ts +35 -4
- package/dist/boot/runner-deps.js +2 -6
- package/dist/budget.js +3 -2
- package/dist/config-catalog.d.ts +31 -2
- package/dist/config-catalog.js +41 -23
- package/dist/http/routes/approvals-assistant.js +3 -12
- package/dist/http/routes/diagnostics.js +4 -0
- package/dist/http/routes/runs.js +2 -3
- package/dist/http/routes/tasks.js +22 -18
- package/dist/http/server.js +4 -2
- package/dist/main.js +2 -2
- package/dist/observability/metrics.d.ts +0 -6
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/permission-rule-events.d.ts +20 -0
- package/dist/observability/permission-rule-events.js +28 -0
- package/dist/observability/run-terminal-log.d.ts +9 -19
- package/dist/observability/run-terminal-log.js +183 -32
- package/dist/observability/tool-trace.d.ts +12 -0
- package/dist/observability/tool-trace.js +5 -1
- package/dist/plugins/store-backend.d.ts +2 -1
- package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
- package/dist/plugins/workflow-journal-store-sql.js +3 -10
- package/dist/posture-source.d.ts +23 -3
- package/dist/posture-source.js +1 -1
- package/dist/read-face-posture.d.ts +8 -1
- package/dist/read-face-posture.js +1 -0
- package/dist/run-cancel-context.d.ts +50 -1
- package/dist/run-cancel-context.js +22 -0
- package/dist/run-local.d.ts +14 -6
- package/dist/run-local.js +1 -1
- package/dist/runs.d.ts +0 -51
- package/dist/runs.js +40 -25
- package/dist/tool-approval.d.ts +14 -8
- package/dist/tool-approval.js +3 -3
- package/dist/trace/core-keyset-guard.d.ts +7 -3
- package/dist/trace/ledger-events.d.ts +17 -1
- package/dist/trace/ledger-events.js +6 -1
- package/dist/trace/ledger-sink.d.ts +10 -8
- package/dist/trace/ledger-sink.js +2 -1
- package/dist/trace/project.d.ts +3 -2
- package/dist/trace/redact.d.ts +28 -0
- package/dist/trace/redact.js +50 -27
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { redactSecrets } from "../trace/redact.js";
|
|
3
|
+
import { isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
3
4
|
import { errorText } from "./err-text.js";
|
|
4
5
|
import { recordFailOpen } from "./fail-open.js";
|
|
5
6
|
const MESSAGE_CAP = 1024;
|
|
@@ -75,6 +76,37 @@ export function redactErrorMessage(raw) {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
const MAX_EDITED_FILES = 1000;
|
|
79
|
+
const RESULT_KEY_TREATMENT = {
|
|
80
|
+
terminal: "redacted",
|
|
81
|
+
remoteEnvFailures: "redacted",
|
|
82
|
+
editedFiles: "redacted",
|
|
83
|
+
taskId: "verbatim",
|
|
84
|
+
runId: "verbatim",
|
|
85
|
+
sessionId: "verbatim",
|
|
86
|
+
model: "verbatim",
|
|
87
|
+
effectiveReasoning: "verbatim",
|
|
88
|
+
result: "verbatim",
|
|
89
|
+
salvagedOutput: "verbatim",
|
|
90
|
+
apiFailure: "verbatim",
|
|
91
|
+
retryAfterMs: "verbatim",
|
|
92
|
+
degraded: "verbatim",
|
|
93
|
+
structuredOutput: "verbatim",
|
|
94
|
+
rewindNotes: "verbatim",
|
|
95
|
+
haltedOnUserRejection: "verbatim",
|
|
96
|
+
haltedByUser: "verbatim",
|
|
97
|
+
strandedHumanAnswers: "verbatim",
|
|
98
|
+
pendingApproval: "verbatim",
|
|
99
|
+
approvalWaitedMs: "verbatim",
|
|
100
|
+
effectiveReadFace: "verbatim",
|
|
101
|
+
effectiveReadDenyPatterns: "verbatim",
|
|
102
|
+
effectiveMemoryScopes: "verbatim",
|
|
103
|
+
stats: "verbatim",
|
|
104
|
+
};
|
|
105
|
+
const LEGACY_RESULT_LEAVES = ["errorMessage", "blockedReason", "checkpointGate"];
|
|
106
|
+
export const REDACTABLE_RESULT_LEAVES = [
|
|
107
|
+
...Object.entries(RESULT_KEY_TREATMENT).filter(([, t]) => t === "redacted").map(([k]) => k),
|
|
108
|
+
...LEGACY_RESULT_LEAVES,
|
|
109
|
+
];
|
|
78
110
|
export function redactTerminalResult(result) {
|
|
79
111
|
if (result === undefined || result === null)
|
|
80
112
|
return result;
|
|
@@ -89,14 +121,41 @@ export function redactTerminalResult(result) {
|
|
|
89
121
|
out.blockedReason = redactErrorMessage(r.blockedReason);
|
|
90
122
|
changed = true;
|
|
91
123
|
}
|
|
124
|
+
if (r.checkpointGate !== undefined) {
|
|
125
|
+
const legacyGate = redactCheckpointGate(r.checkpointGate);
|
|
126
|
+
if (legacyGate !== r.checkpointGate) {
|
|
127
|
+
out.checkpointGate = legacyGate;
|
|
128
|
+
changed = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
92
131
|
if (r.terminal !== null && typeof r.terminal === "object") {
|
|
93
132
|
const t = r.terminal;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
133
|
+
let nt;
|
|
134
|
+
if (t.kind === "failed" && typeof t.message === "string")
|
|
135
|
+
nt = { ...t, message: redactErrorMessage(t.message) };
|
|
136
|
+
else if (t.kind === "blocked" && typeof t.reason === "string")
|
|
137
|
+
nt = { ...t, reason: redactErrorMessage(t.reason) };
|
|
138
|
+
const gateOf = (cause) => {
|
|
139
|
+
if (cause === null || typeof cause !== "object" || !("gate" in cause))
|
|
140
|
+
return undefined;
|
|
141
|
+
const g = cause.gate;
|
|
142
|
+
const ng = redactCheckpointGate(g);
|
|
143
|
+
return { gate: ng, changed: ng !== g };
|
|
144
|
+
};
|
|
145
|
+
const base = nt ?? t;
|
|
146
|
+
if (t.kind === "paused") {
|
|
147
|
+
const g = gateOf(base);
|
|
148
|
+
if (g?.changed === true)
|
|
149
|
+
nt = { ...base, gate: g.gate };
|
|
150
|
+
}
|
|
151
|
+
else if (t.kind === "failed") {
|
|
152
|
+
const np = base.nestedPause;
|
|
153
|
+
const g = gateOf(np);
|
|
154
|
+
if (g?.changed === true)
|
|
155
|
+
nt = { ...base, nestedPause: { ...np, gate: g.gate } };
|
|
97
156
|
}
|
|
98
|
-
|
|
99
|
-
out.terminal =
|
|
157
|
+
if (nt !== undefined) {
|
|
158
|
+
out.terminal = nt;
|
|
100
159
|
changed = true;
|
|
101
160
|
}
|
|
102
161
|
}
|
|
@@ -136,6 +195,11 @@ export function redactLedgerEventData(type, data) {
|
|
|
136
195
|
return data;
|
|
137
196
|
return { ...data, errorMessage: redactErrorMessage(msg) };
|
|
138
197
|
}
|
|
198
|
+
if (isParkLedgerEventType(type)) {
|
|
199
|
+
const gate = data.gate;
|
|
200
|
+
const next = redactCheckpointGate(gate);
|
|
201
|
+
return next === gate ? data : { ...data, gate: next };
|
|
202
|
+
}
|
|
139
203
|
return data;
|
|
140
204
|
}
|
|
141
205
|
export function redactWorkerReportError(report) {
|
|
@@ -145,38 +209,123 @@ export function redactWorkerReportError(report) {
|
|
|
145
209
|
return report;
|
|
146
210
|
return { ...report, ...(error !== report.error ? { error } : {}), ...(gate !== report.checkpointGate ? { checkpointGate: gate } : {}) };
|
|
147
211
|
}
|
|
212
|
+
const RISK_DESCRIPTOR_TREATMENT = {
|
|
213
|
+
severity: "verbatim",
|
|
214
|
+
axes: "verbatim",
|
|
215
|
+
toolName: "verbatim",
|
|
216
|
+
shellGateDoctrine: "verbatim",
|
|
217
|
+
summary: "text",
|
|
218
|
+
touchedPaths: "text[]",
|
|
219
|
+
shadowedRule: "text",
|
|
220
|
+
probeReason: "text",
|
|
221
|
+
probeCause: "probe-cause",
|
|
222
|
+
};
|
|
223
|
+
const PROBE_CAUSE_TREATMENT = {
|
|
224
|
+
code: "verbatim",
|
|
225
|
+
roots: "operands",
|
|
226
|
+
further: "operands",
|
|
227
|
+
};
|
|
228
|
+
const PROBE_CAUSE_OPERANDS_TREATMENT = {
|
|
229
|
+
shown: "text[]",
|
|
230
|
+
total: "verbatim",
|
|
231
|
+
};
|
|
232
|
+
export function redactRiskDescriptor(rd) {
|
|
233
|
+
if (rd === null || typeof rd !== "object" || Array.isArray(rd))
|
|
234
|
+
return rd;
|
|
235
|
+
const r = rd;
|
|
236
|
+
let changed = false;
|
|
237
|
+
const str = (v) => { const o = redactErrorMessage(v) ?? ""; if (o !== v)
|
|
238
|
+
changed = true; return o; };
|
|
239
|
+
const strs = (v) => (Array.isArray(v) ? v.map((x) => (typeof x === "string" ? str(x) : x)) : v);
|
|
240
|
+
const out = { ...r };
|
|
241
|
+
for (const [key, treatment] of Object.entries(RISK_DESCRIPTOR_TREATMENT)) {
|
|
242
|
+
switch (treatment) {
|
|
243
|
+
case "verbatim":
|
|
244
|
+
break;
|
|
245
|
+
case "text":
|
|
246
|
+
if (typeof r[key] === "string")
|
|
247
|
+
out[key] = str(r[key]);
|
|
248
|
+
break;
|
|
249
|
+
case "text[]":
|
|
250
|
+
if (Array.isArray(r[key]))
|
|
251
|
+
out[key] = strs(r[key]);
|
|
252
|
+
break;
|
|
253
|
+
case "probe-cause": {
|
|
254
|
+
const pc = r[key];
|
|
255
|
+
if (pc === null || typeof pc !== "object" || Array.isArray(pc))
|
|
256
|
+
break;
|
|
257
|
+
const p = pc;
|
|
258
|
+
const np = { ...p };
|
|
259
|
+
for (const [ck, ct] of Object.entries(PROBE_CAUSE_TREATMENT)) {
|
|
260
|
+
if (ct === "verbatim")
|
|
261
|
+
continue;
|
|
262
|
+
const ops = p[ck];
|
|
263
|
+
if (ops === null || typeof ops !== "object" || Array.isArray(ops))
|
|
264
|
+
continue;
|
|
265
|
+
const o = ops;
|
|
266
|
+
const no = { ...o };
|
|
267
|
+
for (const [ok, ot] of Object.entries(PROBE_CAUSE_OPERANDS_TREATMENT)) {
|
|
268
|
+
if (ot === "text[]" && Array.isArray(o[ok]))
|
|
269
|
+
no[ok] = strs(o[ok]);
|
|
270
|
+
}
|
|
271
|
+
np[ck] = no;
|
|
272
|
+
}
|
|
273
|
+
out[key] = np;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
default: {
|
|
277
|
+
const unhandled = treatment;
|
|
278
|
+
throw new Error(`unhandled leaf treatment: ${String(unhandled)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return changed ? out : rd;
|
|
283
|
+
}
|
|
284
|
+
const CHECKPOINT_GATE_TREATMENT = {
|
|
285
|
+
kind: "verbatim",
|
|
286
|
+
reason: "text",
|
|
287
|
+
toolName: "verbatim",
|
|
288
|
+
riskDescriptor: "risk-descriptor",
|
|
289
|
+
safetyAxis: "verbatim",
|
|
290
|
+
realApproval: "verbatim",
|
|
291
|
+
resumeAfterMs: "verbatim",
|
|
292
|
+
};
|
|
148
293
|
export function redactCheckpointGate(gate) {
|
|
149
294
|
if (gate === null || typeof gate !== "object" || Array.isArray(gate))
|
|
150
295
|
return gate;
|
|
151
296
|
const g = gate;
|
|
152
|
-
let changed = false;
|
|
153
297
|
const out = { ...g };
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
175
|
-
|
|
298
|
+
let changed = false;
|
|
299
|
+
for (const [key, treatment] of Object.entries(CHECKPOINT_GATE_TREATMENT)) {
|
|
300
|
+
switch (treatment) {
|
|
301
|
+
case "verbatim":
|
|
302
|
+
break;
|
|
303
|
+
case "text": {
|
|
304
|
+
const v = g[key];
|
|
305
|
+
if (typeof v !== "string")
|
|
306
|
+
break;
|
|
307
|
+
const next = redactErrorMessage(v) ?? "";
|
|
308
|
+
if (next !== v) {
|
|
309
|
+
out[key] = next;
|
|
310
|
+
changed = true;
|
|
311
|
+
}
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case "risk-descriptor": {
|
|
315
|
+
if (!(key in g))
|
|
316
|
+
break;
|
|
317
|
+
const next = redactRiskDescriptor(g[key]);
|
|
318
|
+
if (next !== g[key]) {
|
|
319
|
+
out[key] = next;
|
|
320
|
+
changed = true;
|
|
321
|
+
}
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
default: {
|
|
325
|
+
const unhandled = treatment;
|
|
326
|
+
throw new Error(`unhandled gate field treatment: ${String(unhandled)}`);
|
|
176
327
|
}
|
|
177
|
-
nr.probeCause = np;
|
|
178
328
|
}
|
|
179
|
-
out.riskDescriptor = nr;
|
|
180
329
|
}
|
|
181
330
|
return changed ? out : gate;
|
|
182
331
|
}
|
|
@@ -366,9 +515,11 @@ export function memoRedactTerminalResult(seat, result) {
|
|
|
366
515
|
if (result === undefined || result === null)
|
|
367
516
|
return result;
|
|
368
517
|
const r = result;
|
|
369
|
-
if (
|
|
518
|
+
if (REDACTABLE_RESULT_LEAVES.every((k) => r[k] === undefined))
|
|
370
519
|
return result;
|
|
371
|
-
const leaves = {
|
|
520
|
+
const leaves = {};
|
|
521
|
+
for (const k of REDACTABLE_RESULT_LEAVES)
|
|
522
|
+
leaves[k] = r[k];
|
|
372
523
|
const redacted = memoRedactedRead(seat, leaves, (l) => redactTerminalResult(l));
|
|
373
524
|
if (redacted === leaves)
|
|
374
525
|
return result;
|
|
@@ -14,6 +14,18 @@ import type { Metrics } from "./metrics.js";
|
|
|
14
14
|
* 🔴 BREAKING(server 7.64.0,core 7.6.0 提货):标签名 `source` → `deniedBy`,词表从七词
|
|
15
15
|
* (`policy/hook/safety/shellGate/planMode/classifier/org`)换成八词
|
|
16
16
|
* (`policy/hook/org/classifier/plan_mode/compliance/write_protection/ask_resolution`)。仪表盘/告警规则要改。
|
|
17
|
+
*
|
|
18
|
+
* 🔴 **第二个计数器 `auto_mode_classifier_unavailable_total{cause}`(server 7.70.0,core 7.12.0 [ref])**
|
|
19
|
+
* —— 分类器**停摆**的常开信号。7.12.0 之前这件事有两条可观测路:熔断跳闸时的
|
|
20
|
+
* `auto_mode_breaker_open_total`,以及「`unavailable` 的 ask 会走到人面前」(审批 inbox 里看得见)。
|
|
21
|
+
* 该版把熔断族整族退役、又把 `unavailable` 改成**当场 deny** ⇒ 两条路同时没了,而
|
|
22
|
+
* `permission_denied_total{deniedBy}` 对它是**瞎的**:分类器停摆与分类器正常拦下一次危险调用,
|
|
23
|
+
* 两者都记 `deniedBy:"classifier"`。一次持续停摆于是在默认监控上与「今天安全拦截多了点」不可区分
|
|
24
|
+
* (`TOOL_TRACE` 默认关,拿不到那半边)。core 同批把事实加在了 deny 上
|
|
25
|
+
* (`PermissionDeniedPayload.classifierUnavailable`),本仓就在**这同一只常开观察者**上消费它。
|
|
26
|
+
* 两个计数刻意**不合并成一个标签**:它们答两个问题(「谁拒的」vs「这次拒是不是因为分类器停摆、哪一类」),
|
|
27
|
+
* 而运维对停摆要立刻动手(改配置 / 等重试 / 看后端),对安全拦截不要。
|
|
28
|
+
* ⛔ 纯计量:不改任何裁决、不新开观察面、事实缺席时**不铸**(缺席 ≠ 「分类器好着呢」,只是这次拒不是它)。
|
|
17
29
|
*/
|
|
18
30
|
export declare function createPermissionDeniedMeter(metrics: Pick<Metrics, "inc">): Hooks;
|
|
19
31
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDeniedBy } from "@sema-agent/core";
|
|
1
|
+
import { isAutoModeUnavailableCause, isDeniedBy } from "@sema-agent/core";
|
|
2
2
|
import { redactSecrets } from "../trace/redact.js";
|
|
3
3
|
function deniedByLabel(gate) {
|
|
4
4
|
const d = gate.disposition;
|
|
@@ -8,6 +8,10 @@ export function createPermissionDeniedMeter(metrics) {
|
|
|
8
8
|
return {
|
|
9
9
|
permissionDenied(payload) {
|
|
10
10
|
metrics.inc("permission_denied_total", { deniedBy: deniedByLabel(payload.gate) });
|
|
11
|
+
const cause = payload.classifierUnavailable?.cause;
|
|
12
|
+
if (cause !== undefined) {
|
|
13
|
+
metrics.inc("auto_mode_classifier_unavailable_total", { cause: isAutoModeUnavailableCause(cause) ? cause : "other" });
|
|
14
|
+
}
|
|
11
15
|
},
|
|
12
16
|
};
|
|
13
17
|
}
|
|
@@ -71,7 +71,8 @@ export type ServiceFileHistoryStore = FileHistoryStore & {
|
|
|
71
71
|
/** SVC-2 (core CORE-7) WorkflowJournalStore — core's interface PLUS the service `deleteByRun` (GC extra). tidb/pg =
|
|
72
72
|
* durable SQL twins (cross-replica resume journal); local = the service {@link FileWorkflowJournalStore}
|
|
73
73
|
* (single-box but RESTART-durable crash-safe JSONL, replacing core's InMemory). The shared equivalence suite keeps
|
|
74
|
-
* all three byte-matched (scope guard / idempotent-per-ordinal / oversize
|
|
74
|
+
* all three byte-matched (scope guard / idempotent-per-ordinal / oversize **refusal** —— S-194 / core 7.12.0
|
|
75
|
+
* [ref] 起两臂一律抛 `workflow.journal_oversize`,判长口径归 core `assertJournalEntryFits` 单一属主). */
|
|
75
76
|
export type ServiceWorkflowJournalStore = WorkflowJournalStore & {
|
|
76
77
|
/** codex R1-H2(journal 读面界):分页+单行字节门的投影读——SQL 端 LIMIT/OFFSET 定行数、LENGTH 门
|
|
77
78
|
* 定单行(超 maxResultBytes 的行 resultJson=null 只回 resultBytes,读面标 truncated)。HTTP 读面
|
|
@@ -57,7 +57,7 @@ export declare class SqlWorkflowJournalStore implements WorkflowJournalStore {
|
|
|
57
57
|
/** Record one agent's result. Idempotent per (run_id, ordinal): the PK upsert OVERWRITES (last-write-wins), so a
|
|
58
58
|
* resumed run re-appending its replayed prefix verbatim leaves no duplicate. callKeyOrdinal is the SOLE ordinal
|
|
59
59
|
* source (core's parser — a malformed key collapses to 0, matching InMemory). Throws on a REAL failure
|
|
60
|
-
* (LOAD-BEARING)
|
|
60
|
+
* (LOAD-BEARING) — an oversize entry included, on BOTH arms (S-194 / core 7.12.0 [ref]; see below). */
|
|
61
61
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
62
62
|
/** Entries for `runId` IF its recorded scope === `scope`, ASCENDING by ordinal; otherwise EMPTY. CORE-9 audit
|
|
63
63
|
* BLOCKER: scope in the WHERE (twin of WorkflowRunStore) — the LLM controls `resumeFromRunId`, so a cross-scope
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { callKeyOrdinal } from "@sema-agent/core";
|
|
2
|
-
import {
|
|
3
|
-
import { journalEntryOfRow,
|
|
2
|
+
import { assertJournalEntryFits } from "@sema-agent/core";
|
|
3
|
+
import { journalEntryOfRow, journalRowPayloadOf } from "../orchestration/workflow-journal-entry.js";
|
|
4
4
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
5
|
const DEFAULT_RESUME_CLAIM_TTL_MS = 60 * 60 * 1000;
|
|
6
6
|
export class SqlWorkflowJournalStore {
|
|
@@ -14,15 +14,8 @@ export class SqlWorkflowJournalStore {
|
|
|
14
14
|
return this.db.dialect === "tidb" ? tidb : pg;
|
|
15
15
|
}
|
|
16
16
|
async append(runId, scope, entry) {
|
|
17
|
+
assertJournalEntryFits(entry);
|
|
17
18
|
const serialized = journalRowPayloadOf(entry);
|
|
18
|
-
if (oversizeJournalResult(serialized)) {
|
|
19
|
-
if (journalEntryPayload(entry).parked) {
|
|
20
|
-
throw new Error(`workflow journal: PARKED entry ${entry.callKey} is ${serialized.length} bytes, over the journal result cap — refusing to skip it. ` +
|
|
21
|
-
"Skipping a parked entry would make a resume start a SECOND live agent beside the session already pinned by the pending checkpoint (duplicating pre-approval side effects); " +
|
|
22
|
-
"a completed result may be skipped (resume just re-runs that agent), a park may not.");
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
19
|
await this.db.query(this.q("INSERT INTO workflow_journal (run_id, ordinal, scope, call_key, result, created_at_ms) VALUES (?,?,?,?,?,?) " +
|
|
27
20
|
"ON DUPLICATE KEY UPDATE call_key = VALUES(call_key), result = VALUES(result)", "INSERT INTO workflow_journal (run_id, ordinal, scope, call_key, result, created_at_ms) VALUES ($1,$2,$3,$4,$5,$6) " +
|
|
28
21
|
"ON CONFLICT (run_id, ordinal) DO UPDATE SET call_key = EXCLUDED.call_key, result = EXCLUDED.result"), [runId, callKeyOrdinal(entry.callKey), scope, entry.callKey, serialized, Date.now()]);
|
package/dist/posture-source.d.ts
CHANGED
|
@@ -13,8 +13,16 @@
|
|
|
13
13
|
* `env 显式` > `center 发布` > `posture 派生` > `引擎/服务端默认`。
|
|
14
14
|
*
|
|
15
15
|
* ## 定界
|
|
16
|
-
*
|
|
17
|
-
* ({@link PostureKnobFacts.postureMeaning})
|
|
16
|
+
* 本模块只说**来源**。值本身、以及**两条键专属臂**的句子——「这台机器凭什么派生出这个值」
|
|
17
|
+
* ({@link PostureKnobFacts.postureMeaning})与「组织发布的值在这台机器上怎么生效」
|
|
18
|
+
* ({@link PostureKnobFacts.centerMeaning})——由每个键自己给。那两句话是键专属的事实,共享它们只会
|
|
19
|
+
* 得到一句谁都不认的空话,或者更坏:一句**对某些键是假的**话。
|
|
20
|
+
*
|
|
21
|
+
* 🔴 `centerMeaning` 是 G-7(test [ref])的修复面,成因是一次真的过期断言:这一臂原本硬编码
|
|
22
|
+
* 「applied from the config-center (**restart-to-apply**)」,而 S-174(core 7.10.0 [ref])之后
|
|
23
|
+
* `readFace.face` 是**热换席**(`Runner.swapDeps({readFace})`,下一拍 refresh 当场换进全部 Runner,
|
|
24
|
+
* `read-face` 重启片也不再为它签理由)——运维照那句话去滚动重启一片 fleet,重启的是一个已经生效的变更。
|
|
25
|
+
* 生效时机是**键**的事实,不是四词表的属性,所以它跟 `postureMeaning` 一样归键。
|
|
18
26
|
*/
|
|
19
27
|
/** 旋钮取值来源的闭集词表(顺序 = 优先序)。 */
|
|
20
28
|
export declare const POSTURE_SOURCE_WORDS: readonly ["env", "center", "posture", "engine-default"];
|
|
@@ -25,13 +33,25 @@ export interface PostureKnobFacts {
|
|
|
25
33
|
/** 这台机器上占住这个键的 env 变量名(指路句要逐字点它,运维才知道去改哪个)。 */
|
|
26
34
|
readonly knob: string;
|
|
27
35
|
readonly source: PostureSource;
|
|
36
|
+
/**
|
|
37
|
+
* `center` 那一臂**专属**的一句话:组织发布的这个值在这台机器上**怎么生效**(热换席?等重启?
|
|
38
|
+
* 还是这个键根本没有 center 腿?),以及怎么把这个键取回来。只在 `source === "center"` 时被读到。
|
|
39
|
+
*
|
|
40
|
+
* 🔴 为什么归键而不是共享(G-7 的成因):生效时机是键的事实。同一句「restart-to-apply」对
|
|
41
|
+
* `readFace.face` 是假的(S-174 热换席),对三个 S-178 旋钮则连触发条件都没有(`config.ts` 的
|
|
42
|
+
* `postureSourceOfKnob` / `postureSourceOfNumEnv` 只产 env/posture/engine-default,center 写不到
|
|
43
|
+
* 它们)—— 一句共享的硬编码必然对其中一类说谎。
|
|
44
|
+
*/
|
|
45
|
+
readonly centerMeaning: string;
|
|
28
46
|
/**
|
|
29
47
|
* `posture` 那一臂**专属**的一句话:这台机器为什么派生出了这个值,以及怎么把它钉回去。
|
|
30
48
|
* 只在 `source === "posture"` 时被读到 —— 别的臂的句子是通用的(见 {@link postureSourceNote})。
|
|
31
49
|
*/
|
|
32
50
|
readonly postureMeaning: string;
|
|
33
51
|
}
|
|
34
|
-
/** 运维指路句 —— **穷举 switch,无缺省臂**:四词闭集加一个成员这里就编译红。
|
|
52
|
+
/** 运维指路句 —— **穷举 switch,无缺省臂**:四词闭集加一个成员这里就编译红。
|
|
53
|
+
* 两条**通用**臂(`env` / `engine-default`)的句子在这里写死并点名旋钮;两条**键专属**臂
|
|
54
|
+
* (`center` / `posture`)原样取键给的那句话(理由见 {@link PostureKnobFacts} 的两个字段注)。 */
|
|
35
55
|
export declare function postureSourceNote(facts: PostureKnobFacts): string;
|
|
36
56
|
/**
|
|
37
57
|
* 这个值是**运维显式表态**的吗?
|
package/dist/posture-source.js
CHANGED
|
@@ -4,7 +4,7 @@ export function postureSourceNote(facts) {
|
|
|
4
4
|
case "env":
|
|
5
5
|
return `pinned by this machine's ${facts.knob} env var (deployment sovereignty — it wins over any published value for this key)`;
|
|
6
6
|
case "center":
|
|
7
|
-
return
|
|
7
|
+
return facts.centerMeaning;
|
|
8
8
|
case "posture":
|
|
9
9
|
return facts.postureMeaning;
|
|
10
10
|
case "engine-default":
|
|
@@ -46,7 +46,14 @@ export interface ReadFacePostureFacts {
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function readFaceForPosture(singleUserTurnkey: boolean): ReadFacePostureFacts;
|
|
48
48
|
/** 本档的运维指路句 —— 骨架由共享件 {@link postureSourceNote} 给(四词穷举在那里,加词即编译红);
|
|
49
|
-
*
|
|
49
|
+
* 本函数提供两条**键专属**臂(`center` / `posture`)的事实句。
|
|
50
|
+
*
|
|
51
|
+
* 🔴 `center` 那句话的事实来源是 S-174(core 7.10.0 [ref]):`RunnerDeps.readFace` 进了 core 的可换席
|
|
52
|
+
* 闭集 `SWAPPABLE_DEP_SEATS` ⇒ 组织发布/撤回一个档位,**下一拍 refresh 当场换进全部 Runner**,
|
|
53
|
+
* `config-center/restart-signal.ts` 的 `read-face` 片也不再为它签重启理由(判据同源 =
|
|
54
|
+
* `CENTER_READ_FACE_LIVE_KEYS`,由 core 的 `isSwappableDepSeat` 派生)。本句此前逐字写着
|
|
55
|
+
* 「(restart-to-apply)」—— G-7 抓获的那条过期断言,方向恰好是「让运维为一个已生效的变更重启 fleet」。
|
|
56
|
+
* ⚠️ 只说 `face` 这一席:三个 deny 键仍是 boot-baked,那半场的话不在这条读面上(它们没有来源位)。 */
|
|
50
57
|
export declare function readFaceSourceNote(source: ReadFaceSource): string;
|
|
51
58
|
/** operator 诊断面的投影(`GET /v1/diagnostics/wiring`)。`face:null` = 本部署没钉档,引擎默认接管。 */
|
|
52
59
|
export declare function buildReadFaceWiring(facts: ReadFacePostureFacts): {
|
|
@@ -7,6 +7,7 @@ export function readFaceSourceNote(source) {
|
|
|
7
7
|
return postureSourceNote({
|
|
8
8
|
knob: "READ_FACE",
|
|
9
9
|
source,
|
|
10
|
+
centerMeaning: "published by the config-center and applied HOT — the containment FACE is a swappable engine seat (core 7.10.0), so the next refresh (or POST /v1/admin/config/refresh) swaps it into every Runner with NO restart; set READ_FACE on this machine to take the key back",
|
|
10
11
|
postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set): reads outside the workspace roots are allowed by default — set READ_FACE=roots to pin the containing posture back",
|
|
11
12
|
});
|
|
12
13
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunCancelContext } from "./plugins/store-contracts.js";
|
|
1
|
+
import type { PersistedTaskResult, RunCancelContext } from "./plugins/store-contracts.js";
|
|
2
2
|
/**
|
|
3
3
|
* @param taskId 这条 run 的账本 id(turn 活性登记的键)。
|
|
4
4
|
* @param startedAtMs 这条 run 起跑的墙钟时刻(epoch ms):取消腿手上有行就用行的 `createdAt`,
|
|
@@ -10,4 +10,53 @@ import type { RunCancelContext } from "./plugins/store-contracts.js";
|
|
|
10
10
|
* 断言「我看过现场,什么都没有」,而事实是「我什么都没看见」)。
|
|
11
11
|
*/
|
|
12
12
|
export declare function buildCancelContext(taskId: string, startedAtMs: number, nowMs?: number): RunCancelContext | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* 🔴 **用户取消的机器码 —— 全仓单源**(合并重扫 [5])。
|
|
15
|
+
*
|
|
16
|
+
* run 行的 `errorCode` 列、因由臂上的 `terminal.code`、`tasks_total{status}` 的 metrics 标签、fleet 行的
|
|
17
|
+
* 终态词,是**同一个词**;同一个词就该同一个来源。此前仓里是「一个词两只常量 + 十余处字面量」
|
|
18
|
+
* (`approval-reconciler.ts` 另有一份 `CANCELLED_ERROR_CODE`),改它的值会让 recordDone / fleet 标签 /
|
|
19
|
+
* reconciler 的 VOID 判据 / cancel 端点的幂等判据静默分叉。
|
|
20
|
+
*
|
|
21
|
+
* 机器门(新写者当场红):`test/cancel-terminal-single-owner.test.ts` 的 G2/G3。
|
|
22
|
+
*/
|
|
23
|
+
export declare const CANCELLED_CODE = "cancelled";
|
|
24
|
+
/**
|
|
25
|
+
* S-104 —— **取消终局的唯一铸点**。三条用户取消腿(sync stream 的断连支与 cancel-verb 支、durable bg 腿)
|
|
26
|
+
* 都从这里出结果,于是「一次取消在账本/wire 上长什么样」只有一份口径。
|
|
27
|
+
*
|
|
28
|
+
* ## 两种到达形,一条规则
|
|
29
|
+
* `engineResult` 在场与否 = 「引擎有没有给出终局」,不是两条规则:
|
|
30
|
+
* · **在场**(现行常态):**跑起来之后**的 `signal.aborted` 被 core 当成流内事件优雅收尾 —— 流照常
|
|
31
|
+
* 发 `done`,`terminal` 是一条**不带码**的 `failed`。这里在**它**之上补标,不另造一份结果(另造会把
|
|
32
|
+
* 引擎已经算出的 `stats` / `result` / `editedFiles` 全丢掉)。
|
|
33
|
+
* · **缺席**:根本没读到 done —— 客户端断连后迭代器早收,或**开跑之前**就已经立起的旗
|
|
34
|
+
* (亲核 core 7.11.2 `dist/core/runner/runtask.js:623-626`:`runLocked` 入口对已 aborted 的 signal
|
|
35
|
+
* 抛 `AbortError`)⇒ 合成一条诚实的取消终局。
|
|
36
|
+
*
|
|
37
|
+
* ## 「只补空座」纪律(与 core 的 `amendTerminal` 同规),而且**补码与补快照同进同退**
|
|
38
|
+
* · 引擎给的终局**不是无码的 failed**(它完成了 / 阻塞了 / 自己带了码)⇒ **整只原样返回**。
|
|
39
|
+
* 这一条是本车自查轮逮住的自造缺陷:首版只让**补码**受这条纪律管、快照却无条件挂,于是「取消动词
|
|
40
|
+
* 到了、但活其实已经干完了」会产出一条 `completed` 却带着取消便签的终局 —— 契约 §5.3 逐字写着
|
|
41
|
+
* 「只在用户取消形上铸」,两个方向的读者都会读错。一次与 abort 赛跑的真失败(`brain.network`)同理:
|
|
42
|
+
* 贴成 `cancelled` 会把故障统计洗白。
|
|
43
|
+
* · `cancelContext`:已经有快照就不重铸(重铸会把「上一条引擎事件多久以前」重新计时)。
|
|
44
|
+
*
|
|
45
|
+
* ## 为什么不判「这条 failed 是不是 abort 形」
|
|
46
|
+
* 判据在**调用点**:调用点问的是「本腿这次是不是被取消动词打中了」(`cancelCtrl.signal.aborted` /
|
|
47
|
+
* `cancelledViaVerb`),那是 server 自己的事实。让本函数去嗅 `terminal.message` 的字面就是再造事实
|
|
48
|
+
* —— [ref] 二那次事故的同一个形。
|
|
49
|
+
*/
|
|
50
|
+
export declare function mintCancelledResult(args: {
|
|
51
|
+
/** 这条 run 的账本 id(活性登记的键;账本不在场时给空串,快照按「证不出」处理)。 */
|
|
52
|
+
taskId: string;
|
|
53
|
+
/** 会话 id(合成形要写进结果;引擎给了结果时不用)。 */
|
|
54
|
+
sessionId: string;
|
|
55
|
+
/** 本腿起跑的墙钟时刻(epoch ms)。语义与缺席纪律见 {@link buildCancelContext}。 */
|
|
56
|
+
startedAtMs: number;
|
|
57
|
+
/** 合成形的失败文案(引擎给了终局时不用)。 */
|
|
58
|
+
message: string;
|
|
59
|
+
/** 引擎给出的终局(在场 ⇒ 在它之上补标)。 */
|
|
60
|
+
engineResult?: PersistedTaskResult;
|
|
61
|
+
}): PersistedTaskResult;
|
|
13
62
|
//# sourceMappingURL=run-cancel-context.d.ts.map
|
|
@@ -12,4 +12,26 @@ export function buildCancelContext(taskId, startedAtMs, nowMs = Date.now()) {
|
|
|
12
12
|
...(elapsedMs !== undefined ? { elapsedMs } : {}),
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
export const CANCELLED_CODE = "cancelled";
|
|
16
|
+
export function mintCancelledResult(args) {
|
|
17
|
+
const ctx = args.taskId === "" ? undefined : buildCancelContext(args.taskId, args.startedAtMs);
|
|
18
|
+
const engine = args.engineResult;
|
|
19
|
+
if (engine !== undefined) {
|
|
20
|
+
if (engine.terminal.kind !== "failed" || engine.terminal.code !== undefined)
|
|
21
|
+
return engine;
|
|
22
|
+
return {
|
|
23
|
+
...engine,
|
|
24
|
+
terminal: { ...engine.terminal, code: CANCELLED_CODE },
|
|
25
|
+
...(engine.cancelContext === undefined && ctx !== undefined ? { cancelContext: ctx } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
taskId: args.taskId,
|
|
30
|
+
sessionId: args.sessionId,
|
|
31
|
+
terminal: { kind: "failed", code: CANCELLED_CODE, message: args.message },
|
|
32
|
+
result: "",
|
|
33
|
+
stats: { turns: 0, tokens: 0 },
|
|
34
|
+
...(ctx ? { cancelContext: ctx } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
15
37
|
//# sourceMappingURL=run-cancel-context.js.map
|
package/dist/run-local.d.ts
CHANGED
|
@@ -96,17 +96,25 @@ export interface LocalApproverIo {
|
|
|
96
96
|
/**
|
|
97
97
|
* [ref] 件三:**onAsk 座**(§0 潜雷 / §5 锁 6)。
|
|
98
98
|
*
|
|
99
|
-
* 🔴
|
|
100
|
-
* `AUTONOMY=ask` ⇒ `shellGate:"always"` ⇒ Bash 进
|
|
101
|
-
* shell 调用都 durable park 成一张永远没人 resume
|
|
102
|
-
*
|
|
103
|
-
* 早退(prepare-task dist 亲读),ask
|
|
99
|
+
* 🔴 为什么这个座位是接治理链的**同窗必做项**(成因,留档):彼时 run-local 的主 runner 带着
|
|
100
|
+
* checkpointStore(file backend),于是 `AUTONOMY=ask` ⇒ `shellGate:"always"` ⇒ Bash 进
|
|
101
|
+
* `irreversibleTools` ⇒ park lane effective ⇒ **每一次 shell 调用都 durable park 成一张永远没人 resume
|
|
102
|
+
* 的 checkpoint**,任务挂起、CLI 退 1。一次性 CLI 里没有任何赎回腿,那不是「等人来批」,是卡死。
|
|
103
|
+
* core 的 `isLiveApproverSeat(onAsk)` 为真时 `suspendAsk` 早退(prepare-task dist 亲读),ask 于是落回
|
|
104
|
+
* 本回调 —— 把 park 换成「问人 / 当场拒」的正当接法。
|
|
105
|
+
*
|
|
106
|
+
* 🔴 **本席不是那颗雷的唯一防线,也从来不该是**(B-060 批 codex 交叉复审 [medium] 的教训):它只挡
|
|
107
|
+
* **审批**那条车道;core 的**平台/资源停驻**(`suspendForPlatformLimit`)由「店在不在」一位武装,
|
|
108
|
+
* 压根不经过 onAsk。所以自 B-060 起本腿的结构性防线是**整条腿不接 checkpointStore**(装配点在
|
|
109
|
+
* `createSharedRunnerDeps` 调用处,理由全文在那里),本席退回它本来的职责:**给 TTY 前的人一次真的
|
|
110
|
+
* 问答**。两道并存不是冗余 —— 一道管「谁来答」,一道管「答不了时会不会留下无人兑付的行」。
|
|
104
111
|
*
|
|
105
112
|
* ⛔ **不在 toolPolicy 层做 ask→deny 映射**:core 自己就有 headless auto-deny 的语义与文案,在策略层
|
|
106
113
|
* 复刻一份 = 同源谎(两处判据日后各改各的)。裁决归 core,座位归这里。
|
|
107
114
|
*
|
|
108
115
|
* 🔴 非 TTY 臂答 `false`(真 deny),**绝不答 `"unavailable"`**:后者被 core 读成「席位够不到」并**改道
|
|
109
|
-
* `suspendAsk`**(dist `core/hooks.js:296` 的 `approverUnavailable` 重投)
|
|
116
|
+
* `suspendAsk`**(dist `core/hooks.js:296` 的 `approverUnavailable` 重投)。今天本腿无店 ⇒ 那条改道无处
|
|
117
|
+
* 可去(fail-closed 拒),但这一条仍是本席的契约:答案是「不批」,不是「找不到人」。
|
|
110
118
|
*/
|
|
111
119
|
export declare function createLocalApprover(io: LocalApproverIo): OnAsk;
|
|
112
120
|
/** 默认的 TTY 问人器:一问一答,回答写 **stderr**(stdout 是任务结果的专用管道,别污染它)。
|
package/dist/run-local.js
CHANGED
|
@@ -387,6 +387,7 @@ export async function runLocal(argv, deps = {}) {
|
|
|
387
387
|
...(config.hooksTimeoutMs !== undefined ? { timeoutMs: config.hooksTimeoutMs } : {}),
|
|
388
388
|
},
|
|
389
389
|
toolResultStore: fileBackend.toolResultStore,
|
|
390
|
+
checkpointStore: undefined,
|
|
390
391
|
sessionPolicyStore: fileBackend.sessionPolicyStore,
|
|
391
392
|
usageWindowStore: config.usageWindows ? fileBackend.usageWindowStore : undefined,
|
|
392
393
|
orgMemoryAdmission,
|
|
@@ -397,7 +398,6 @@ export async function runLocal(argv, deps = {}) {
|
|
|
397
398
|
...sharedRunnerDeps,
|
|
398
399
|
sessionStore,
|
|
399
400
|
...(memoryEngine ? { memoryBackend: memoryEngine.backend, memoryEngineDir: memoryEngine.root } : {}),
|
|
400
|
-
checkpointStore: fileBackend.checkpointStore,
|
|
401
401
|
onError: engineOnError,
|
|
402
402
|
onAsk,
|
|
403
403
|
...(config.projectMemoryEnabled
|
package/dist/runs.d.ts
CHANGED
|
@@ -97,57 +97,6 @@ export declare function markChildrenStoppedByUserOnAbort(signal: AbortSignal, ta
|
|
|
97
97
|
export declare function isSessionConflictResult(result: {
|
|
98
98
|
terminal: TerminalCause;
|
|
99
99
|
}): boolean;
|
|
100
|
-
/**
|
|
101
|
-
* E18 resume-at CALLER error → an HTTP 4xx. A bad/stale resume-at target (the resolved entryId is gone or is not a
|
|
102
|
-
* settled message boundary) surfaces as a FAILED TaskResult carrying a `resume_at.*` errorCode (core swallows the
|
|
103
|
-
* prepare-throw into the result, exactly like a session conflict — runtask catch → status:"failed"+errorCode). The
|
|
104
|
-
* sync path otherwise 200s a failed result; map these to 4xx so a caller mistake reads as one. `not_found` (the
|
|
105
|
-
* entry is genuinely absent) → 404; everything else (not_a_message / conflicts_resume / no_session) → 422.
|
|
106
|
-
*
|
|
107
|
-
* [ref] Phase3: `requireExistingSession` on a genuinely-missing/purged session ALSO surfaces as a failed result
|
|
108
|
-
* (core prepare-task maps the store's `not_found` → `resume.session_not_found`); map it to 404 (the session is gone),
|
|
109
|
-
* the same "caller mistake reads as a 4xx, not a silent 200 fresh run" discipline.
|
|
110
|
-
*
|
|
111
|
-
* [ref] rewind exclusive mode (core 1.292 resumeAtMode:"before"): its two edge rejections ride the same `resume_at.*`
|
|
112
|
-
* prefix (before_target_not_user / before_root_unsupported → 422 via the existing branch, errorCode passed through
|
|
113
|
-
* UNCHANGED for the shell). `rewind_snapshot.unresolvable` (rewindFiles + "before": no snapshot at/above the branch
|
|
114
|
-
* point) is core's third rejection with a DIFFERENT prefix — same caller-mistake shape (prepare-throw, nothing
|
|
115
|
-
* billed), map it to 422 explicitly so it doesn't fall through to a 200-with-failed body.
|
|
116
|
-
*
|
|
117
|
-
* [ref] 件A([ref] 件4): core 5.13.0's org-memory admission rides the SAME prepare-throw pipe
|
|
118
|
-
* (GOVERNANCE_CODES). `memory.admission_denied` is TERMINAL — the principal has no grant on that tenant
|
|
119
|
-
* plane → 403 (a retry cannot change the verdict). `memory.admission_required` is TRANSIENT fail-closed —
|
|
120
|
-
* the directory is unreachable / no resolver is wired → 503 (retry later; the sync leg forwards the
|
|
121
|
-
* result's `retryAfterMs` as body `retryAfterSec`, same family shape as usage.window_exhausted). Exact
|
|
122
|
-
* codes only, no memory.* family grab — an unknown sibling stays a 200-with-failed-body until cataloged.
|
|
123
|
-
*
|
|
124
|
-
* [ref] (core 7.0.0 片2/3, [ref] S-3): the session capture opt-out's four prepare-throw refusals ride the
|
|
125
|
-
* same pipe. `memory.capture_optout_denied` is TERMINAL (the principal's `allowMemoryOptOut` verdict is an
|
|
126
|
-
* explicit `false`, or the posture is `capture-required` / `governed`-with-absent-verdict — a retry cannot
|
|
127
|
-
* change it) → 403, the `memory.admission_denied` twin. `memory.capture_optout_unpersisted` is the
|
|
128
|
-
* declaration arm's "control-plane record could not be written" refusal — a store outage, retry may succeed
|
|
129
|
-
* → 503 (transient fail-closed, the `memory.admission_required` twin; no retryAfterMs on this one — core
|
|
130
|
-
* mints none, so the sync leg forwards no hint rather than inventing one). `config.memory_capture_spelling`
|
|
131
|
-
* is a TaskSpec authoring mistake (the only legal spelling is the string "off") → 400, the
|
|
132
|
-
* `config.memory_erasure_request` family shape. `config.memory_capture_unsupported` is the DEPLOYMENT SHAPE
|
|
133
|
-
* refusing the declaration (remote/per-run execution with no durable capture-record carrier: the record
|
|
134
|
-
* would not survive to a resume replica) → 409 — the request is well-formed, the deployment's state
|
|
135
|
-
* conflicts with it, the `config.refresh_unavailable` family shape ("change the deployment, not the request").
|
|
136
|
-
* Status choices are pinned by the `config.`/`memory.` family status sets in error-code-catalog-live.test.ts.
|
|
137
|
-
*
|
|
138
|
-
* S-125 件⑥(core 7.4.0 [ref]):`rewind.child_scope_unsupported` —— 这条腿是一个**委派子代**,它的文件编辑
|
|
139
|
-
* 记在**根 session** 的文件历史里,所以「把文件还原到某个 entry」是根 session 的动作、不是子代的;core 在
|
|
140
|
-
* prepare 期响亮拒(零计费,文件一个字节没动)。映射 **422**:请求形式合法、语义上找错了执行者 —— 与
|
|
141
|
-
* `resume_at.*` 的「调用方指错了锚」同一族同一档,而不是 404(目标在,只是不归你)也不是 409(部署状态没冲突)。
|
|
142
|
-
* ⚠️ **前缀不吞**:本码前缀是 `rewind.`,与下面那条 `resume_at.` **开集前缀臂**不同族 —— 它不会被那一臂顺手
|
|
143
|
-
* 兜走(兜走会把一条「找错执行者」的拒答成「锚解析不了」),也不是 `rewind_snapshot.`(那是「快照找不到」)。
|
|
144
|
-
* 三个前缀各自成族,逐码对表见 `test/resume-anchor.test.ts` 的 S-125 `rewind.` 族格。
|
|
145
|
-
* 🔴 **可达性如实**:本仓的 HTTP 面**今天铸不出**这条腿 —— 子代由 core 内部的 Agent 工具派生,
|
|
146
|
-
* `fileHistoryLineage` 是 core prepare 的 internals(不在 `TaskSpec` 上),server 从不直接 prepare 一条带
|
|
147
|
-
* lineage 的子代腿。本臂是**分诊面的完备性**(core 说 X ⇒ wire 形是 Y),不是一条今天在跑的路;将来的
|
|
148
|
-
* 委派入口接线那天零改动即生效。同族其余五码今天**仍落 200-with-failed-body**,那是本批**之前**就存在的
|
|
149
|
-
* 缺口(不是本批回归),已在上述对表格里逐码显式登记为债务,并由那道门看住不许陈腐。
|
|
150
|
-
*/
|
|
151
100
|
export declare function resumeAtHttpStatus(result: {
|
|
152
101
|
terminal: TerminalCause;
|
|
153
102
|
}): number | undefined;
|