@sema-agent/server 7.82.1 → 7.84.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/dist/bench/s1/arms.d.ts +4 -4
- package/dist/bench/s1/arms.js +1 -2
- package/dist/bench/s1/live-deps.js +0 -1
- package/dist/bench/s1/runner-ctx.d.ts +3 -3
- package/dist/bench/s1/tasks.d.ts +8 -4
- package/dist/boot/resolve-spec.js +9 -5
- package/dist/capabilities/memory-notice.d.ts +6 -4
- package/dist/capabilities/memory-notice.js +1 -1
- package/dist/config-catalog.js +0 -1
- package/dist/config.js +1 -0
- package/dist/http/admission.js +7 -3
- package/dist/http/wire-types.d.ts +22 -0
- package/dist/leader/endpoint.js +2 -0
- package/dist/leader/leader.d.ts +33 -6
- package/dist/leader/leader.js +54 -45
- package/dist/leader/merge.d.ts +38 -29
- package/dist/leader/merge.js +45 -38
- package/dist/leader/wire.d.ts +2 -17
- package/dist/leader/wire.js +5 -58
- package/dist/memory-scope.d.ts +33 -5
- package/dist/memory-scope.js +10 -4
- package/dist/observability/fail-open.d.ts +5 -5
- package/dist/observability/fail-open.js +5 -2
- package/dist/observability/run-terminal-log.d.ts +2 -7
- package/dist/observability/run-terminal-log.js +119 -21
- package/dist/plugins/adoption-log-sql.d.ts +3 -0
- package/dist/plugins/adoption-log-sql.js +5 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
- package/dist/plugins/approval-ask-store-sql.js +9 -5
- package/dist/plugins/background-agent-store-sql.d.ts +3 -0
- package/dist/plugins/background-agent-store-sql.js +7 -2
- package/dist/plugins/device-store-sql.d.ts +3 -0
- package/dist/plugins/device-store-sql.js +9 -4
- package/dist/plugins/ensure-index.d.ts +42 -0
- package/dist/plugins/ensure-index.js +81 -0
- package/dist/plugins/ensure-schema-lock-policy.d.ts +29 -0
- package/dist/plugins/ensure-schema-lock-policy.js +31 -0
- package/dist/plugins/image-bake-store-sql.d.ts +3 -0
- package/dist/plugins/image-bake-store-sql.js +6 -2
- package/dist/plugins/image-index-sql.d.ts +3 -0
- package/dist/plugins/image-index-sql.js +8 -4
- package/dist/plugins/leader-run-store-sql.d.ts +3 -0
- package/dist/plugins/leader-run-store-sql.js +6 -2
- package/dist/plugins/mailbox-store-sql.d.ts +3 -0
- package/dist/plugins/mailbox-store-sql.js +6 -1
- package/dist/plugins/memory-engine-pg.d.ts +4 -0
- package/dist/plugins/memory-engine-pg.js +6 -6
- package/dist/plugins/memory-engine-tidb.js +5 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +5 -0
- package/dist/plugins/memory-sync-store-pg.js +9 -2
- package/dist/plugins/memory-sync-store-tidb.js +6 -0
- package/dist/plugins/one-time-migrations.d.ts +1 -1
- package/dist/plugins/one-time-migrations.js +7 -14
- package/dist/plugins/outcome-ledger-sql.d.ts +3 -0
- package/dist/plugins/outcome-ledger-sql.js +7 -3
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +9 -5
- package/dist/plugins/pg-cost-quota.d.ts +3 -0
- package/dist/plugins/pg-cost-quota.js +5 -1
- package/dist/plugins/pg-pool.d.ts +23 -2
- package/dist/plugins/pg-pool.js +22 -37
- package/dist/plugins/pg-rate-limiter.d.ts +3 -0
- package/dist/plugins/pg-rate-limiter.js +5 -1
- package/dist/plugins/pg-session-storage.d.ts +3 -0
- package/dist/plugins/pg-session-storage.js +8 -4
- package/dist/plugins/retention-store-sql.d.ts +3 -0
- package/dist/plugins/retention-store-sql.js +7 -3
- package/dist/plugins/roster-store-sql.d.ts +3 -0
- package/dist/plugins/roster-store-sql.js +6 -1
- package/dist/plugins/schema-indexes.d.ts +21 -0
- package/dist/plugins/schema-indexes.js +25 -0
- package/dist/plugins/task-attachment-store.d.ts +3 -0
- package/dist/plugins/task-attachment-store.js +7 -2
- package/dist/plugins/tidb-pool.d.ts +15 -3
- package/dist/plugins/tidb-pool.js +38 -15
- package/dist/plugins/tool-result-store-sql.d.ts +3 -0
- package/dist/plugins/tool-result-store-sql.js +5 -1
- package/dist/request-key-closure.d.ts +7 -0
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +31 -0
- package/dist/request-value-closure.js +18 -0
- package/dist/trace/project.d.ts +2 -1
- package/dist/trace/redact.d.ts +6 -0
- package/package.json +1 -1
package/dist/leader/merge.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { delimitUntrusted } from "@sema-agent/core";
|
|
2
|
-
import { OVERSIZED_DIAG_PLACEHOLDER, redactErrorMessage, redactHead, redactTail } from "../observability/run-terminal-log.js";
|
|
2
|
+
import { OVERSIZED_DIAG_PLACEHOLDER, redactArtifactText, redactErrorMessage, redactHead, redactTail } from "../observability/run-terminal-log.js";
|
|
3
3
|
import { applyAndGrade, changedFilesOutOfAllowlist } from "./grader-env-factory.js";
|
|
4
4
|
import { shellQuote as Q } from "../plugins/remote-shell.js";
|
|
5
|
+
const CONFLICT_FILES_MAX = 200;
|
|
6
|
+
const CONFLICT_REJ_HEAD_MAX = 2048;
|
|
5
7
|
export async function mergeBranches(reports, baseSha, deps) {
|
|
6
8
|
const repo = deps.repoDir ?? "/repo";
|
|
7
9
|
const mergeable = reports.filter((r) => r.status === "completed");
|
|
@@ -10,7 +12,6 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
10
12
|
}
|
|
11
13
|
let destroy;
|
|
12
14
|
let phase = "diff-out";
|
|
13
|
-
let conflictResolverCostUsd = 0;
|
|
14
15
|
let repairMeta;
|
|
15
16
|
let measure;
|
|
16
17
|
try {
|
|
@@ -28,23 +29,29 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
28
29
|
const { env } = integ;
|
|
29
30
|
const integBase = (await env.exec("git rev-parse HEAD", { cwd: repo })).stdout.trim();
|
|
30
31
|
phase = "apply";
|
|
31
|
-
const
|
|
32
|
+
const applied = [];
|
|
32
33
|
for (const p of patches) {
|
|
33
34
|
const patchPath = `/tmp/${p.workerId}.patch`;
|
|
34
35
|
await env.writeFile(patchPath, p.patch);
|
|
35
36
|
const ap = await env.exec(`git apply --3way --whitespace=nowarn ${Q(patchPath)}`, { cwd: repo });
|
|
36
37
|
if (ap.exitCode !== 0) {
|
|
37
38
|
const rej = await env.exec(`find . -name '*.rej' -print -exec cat {} +`, { cwd: repo }).catch(() => ({ stdout: "", stderr: "", exitCode: -1 }));
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
39
|
+
const listing = await env
|
|
40
|
+
.exec(`grep -rIlE --exclude-dir=.git '^(<<<<<<<|>>>>>>>)' . 2>/dev/null; [ $? -le 1 ] || echo ${PROBE_FAIL_PREFIX}grep; ` +
|
|
41
|
+
`find . -path ./.git -prune -o -name '*.rej' -print 2>/dev/null || echo ${PROBE_FAIL_PREFIX}find`, { cwd: repo })
|
|
42
|
+
.catch(() => ({ exitCode: -1, stdout: "", stderr: "" }));
|
|
43
|
+
const { files, truncated, probeFailed } = conflictFilesOf(listing.stdout);
|
|
44
|
+
if (listing.exitCode !== 0 || probeFailed.length > 0)
|
|
45
|
+
deps.logger?.warn?.("merge_conflict_files_probe_failed", { workerId: p.workerId, exitCode: listing.exitCode, probeFailed });
|
|
46
|
+
deps.logger?.warn?.("merge_apply_failed", { workerId: p.workerId, exitCode: ap.exitCode, files: files.slice(0, 20) });
|
|
47
|
+
const rejHead = redactArtifactText(rej.stdout).slice(0, CONFLICT_REJ_HEAD_MAX);
|
|
48
|
+
return {
|
|
49
|
+
ok: false, phase: "apply", reason: `git apply --3way failed for ${p.workerId} (conflict)`,
|
|
50
|
+
quarantine: [p.workerId], rej: rej.stdout, details: redactHead(ap.stderr, 800),
|
|
51
|
+
conflict: { files, ...(truncated ? { filesTruncated: true } : {}), applied: [...applied], conflicted: p.workerId, ...(rejHead ? { rejHead } : {}) },
|
|
52
|
+
};
|
|
47
53
|
}
|
|
54
|
+
applied.push(p.workerId);
|
|
48
55
|
}
|
|
49
56
|
phase = "test";
|
|
50
57
|
let test = await env.exec(deps.testCmd, { cwd: repo });
|
|
@@ -74,7 +81,7 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
74
81
|
deps.logger?.info?.("integration_repair_done", { ...repairMeta });
|
|
75
82
|
}
|
|
76
83
|
if (test.exitCode !== 0) {
|
|
77
|
-
return { ok: false, phase: "test", reason: `integration test failed (exit ${test.exitCode})`, details: redactTail(test.stdout + "\n" + test.stderr, 2000), ...(repairMeta ? { repair: repairMeta } : {})
|
|
84
|
+
return { ok: false, phase: "test", reason: `integration test failed (exit ${test.exitCode})`, details: redactTail(test.stdout + "\n" + test.stderr, 2000), ...(repairMeta ? { repair: repairMeta } : {}) };
|
|
78
85
|
}
|
|
79
86
|
if (deps.measureCmd) {
|
|
80
87
|
const mr = await env.exec(deps.measureCmd, { cwd: repo });
|
|
@@ -83,7 +90,7 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
83
90
|
phase = "push";
|
|
84
91
|
if (repairMeta)
|
|
85
92
|
await env.exec(`git reset --soft ${Q(integBase)}`, { cwd: repo });
|
|
86
|
-
const costTail = { ...(repairMeta ? { repair: repairMeta } : {})
|
|
93
|
+
const costTail = { ...(repairMeta ? { repair: repairMeta } : {}) };
|
|
87
94
|
const commit = await env.exec(`git add -A && git -c user.email=leader@local -c user.name=leader commit -m ${Q(`merge: integrate ${mergeable.map((r) => r.workerId).join("+")}`)} --allow-empty`, { cwd: repo });
|
|
88
95
|
if (commit.exitCode !== 0) {
|
|
89
96
|
return { ok: false, phase: "push", reason: `failed to create integration commit (exit ${commit.exitCode})`, details: redactHead(commit.stdout + "\n" + commit.stderr, 800), ...costTail };
|
|
@@ -107,7 +114,6 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
107
114
|
candidatePatch: h.candidatePatch,
|
|
108
115
|
...(measure ? { measure } : {}),
|
|
109
116
|
...(repairMeta ? { repair: repairMeta } : {}),
|
|
110
|
-
...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}),
|
|
111
117
|
};
|
|
112
118
|
}
|
|
113
119
|
const push = await deps.push(integrated.stdout, baseSha);
|
|
@@ -117,7 +123,7 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
117
123
|
return { ok: false, phase: "push", reason: `push failed: ${push.error}`, ...costTail };
|
|
118
124
|
}
|
|
119
125
|
deps.logger?.info?.("merge_ok", { ref: push.ref, merged: mergeable.map((r) => r.workerId), ...(repairMeta ? { repairRounds: repairMeta.rounds } : {}) });
|
|
120
|
-
return { ok: true, ref: push.ref, merged: mergeable.map((r) => r.workerId), ...(measure ? { measure } : {}), ...(repairMeta ? { repair: repairMeta } : {})
|
|
126
|
+
return { ok: true, ref: push.ref, merged: mergeable.map((r) => r.workerId), ...(measure ? { measure } : {}), ...(repairMeta ? { repair: repairMeta } : {}) };
|
|
121
127
|
}
|
|
122
128
|
catch (e) {
|
|
123
129
|
return {
|
|
@@ -125,7 +131,6 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
125
131
|
quarantine: mergeable.map((r) => r.workerId),
|
|
126
132
|
...(measure ? { measure } : {}),
|
|
127
133
|
...(repairMeta ? { repair: repairMeta } : {}),
|
|
128
|
-
...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}),
|
|
129
134
|
};
|
|
130
135
|
}
|
|
131
136
|
finally {
|
|
@@ -170,28 +175,30 @@ async function evaluatePushHold(mergeable, observeMeasure, integratedPatch, deps
|
|
|
170
175
|
}
|
|
171
176
|
return { measure };
|
|
172
177
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
const check = await env
|
|
187
|
-
.exec(`grep -rIlE '^(<<<<<<<|>>>>>>>)' . 2>/dev/null; find . -name '*.rej' 2>/dev/null | head -20`, { cwd: repo })
|
|
188
|
-
.catch(() => ({ exitCode: -1, stdout: "DIRTY", stderr: "" }));
|
|
189
|
-
if (!check.stdout.trim()) {
|
|
190
|
-
await env.exec(`find . -name '*.orig' -delete 2>/dev/null || true`, { cwd: repo }).catch(() => { });
|
|
191
|
-
deps.logger?.info?.("merge_conflict_resolved", { workerId: p.workerId, rounds: round + 1 });
|
|
192
|
-
return { resolved: true, costUsd };
|
|
178
|
+
export const PROBE_FAIL_PREFIX = "__PROBE_FAIL_";
|
|
179
|
+
export function conflictFilesOf(stdout) {
|
|
180
|
+
const out = [];
|
|
181
|
+
const probeFailed = [];
|
|
182
|
+
const seen = new Set();
|
|
183
|
+
for (const raw of stdout.split("\n")) {
|
|
184
|
+
let f = raw.trim();
|
|
185
|
+
if (f === "")
|
|
186
|
+
continue;
|
|
187
|
+
if (f.startsWith(PROBE_FAIL_PREFIX)) {
|
|
188
|
+
probeFailed.push(f.slice(PROBE_FAIL_PREFIX.length));
|
|
189
|
+
continue;
|
|
193
190
|
}
|
|
191
|
+
if (f.startsWith("./"))
|
|
192
|
+
f = f.slice(2);
|
|
193
|
+
if (f.endsWith(".rej"))
|
|
194
|
+
f = f.slice(0, -4);
|
|
195
|
+
if (f === "" || seen.has(f))
|
|
196
|
+
continue;
|
|
197
|
+
seen.add(f);
|
|
198
|
+
if (out.length >= CONFLICT_FILES_MAX)
|
|
199
|
+
return { files: out, truncated: true, probeFailed };
|
|
200
|
+
out.push(f);
|
|
194
201
|
}
|
|
195
|
-
return {
|
|
202
|
+
return { files: out, truncated: false, probeFailed };
|
|
196
203
|
}
|
|
197
204
|
//# sourceMappingURL=merge.js.map
|
package/dist/leader/wire.d.ts
CHANGED
|
@@ -129,11 +129,6 @@ export interface LeaderWireConfig {
|
|
|
129
129
|
* 0 / unset = OFF (a failing integration test is terminal, the pre-2026-06-14 behavior). Env override:
|
|
130
130
|
* LEADER_REPAIR_ROUNDS. Repair runs the strong (default) model with hands in the live merged sandbox. */
|
|
131
131
|
repairRounds?: number;
|
|
132
|
-
/** Max bounded merge-conflict-resolution rounds on a `git apply --3way` conflict (see merge.ts ConflictResolver).
|
|
133
|
-
* 0 / unset = OFF (an apply conflict collapses the whole run to a single worker, the pre-2026-06-16 behavior).
|
|
134
|
-
* Env override: LEADER_CONFLICT_ROUNDS. Resolves the conflict in-place + keeps the other workers' work, instead
|
|
135
|
-
* of discarding all fan-out output and redoing single. Runs the strong (default) model with hands in the sandbox. */
|
|
136
|
-
conflictRounds?: number;
|
|
137
132
|
/** [ref] §8 F2(a): when set, workers run on FACTORY envs (runner-owned lifecycle — the durable-suspend-
|
|
138
133
|
* complete path; a suspended worker's paused env is exempt from the leader reap by construction) and diff-out
|
|
139
134
|
* is the worker's mandatory self-upload to this MinIO/S3 store (presigned per worker; creds adapter-held).
|
|
@@ -173,14 +168,6 @@ type WireEnv = Pick<RemoteExecutionEnv, "exec" | "writeFile">;
|
|
|
173
168
|
* k8s、host `RemoteHostExecutionEnv`——`hostExecutionEnvFactory` 恒 `new RemoteHostExecutionEnv`),
|
|
174
169
|
* 所以这条臂是**防御性**的:它真响的那天,意思是有人接了一条新工厂车道而 leader 收不动它的沙盒。
|
|
175
170
|
*/
|
|
176
|
-
/** 冲突解决模型的目标文本——纯函数(第十三轮重扫 [medium]:wire 内联时无法钉「补丁先脱」;抽出来直接钉)。
|
|
177
|
-
* 补丁走补丁头窗(头 256KB 扫再切 6000;解决者仍有工作树里的冲突标记),conflictText 由 merge.ts 先脱后切。 */
|
|
178
|
-
export declare function buildConflictResolverObjective(args: {
|
|
179
|
-
workerId: string;
|
|
180
|
-
repoDir: string;
|
|
181
|
-
patch: string;
|
|
182
|
-
conflictText: string;
|
|
183
|
-
}): string;
|
|
184
171
|
export declare function createEnvReaper(env: ExecutionEnv, what: string): () => Promise<void>;
|
|
185
172
|
/**
|
|
186
173
|
* Resume-safe worker staging (the [ref] F2(a) factory-env path). Runs adapter-side before the agent's
|
|
@@ -247,13 +234,12 @@ export declare function leaderResourceConfig(env?: NodeJS.ProcessEnv): {
|
|
|
247
234
|
maxSuspends: number;
|
|
248
235
|
};
|
|
249
236
|
};
|
|
250
|
-
/** Repair/
|
|
251
|
-
*
|
|
237
|
+
/** Repair/replan-loop knobs derived from env + optional `LeaderWireConfig` fallbacks (`repairRounds` lands in
|
|
238
|
+
* cfg when the caller wires it programmatically instead of via env). Split out of
|
|
252
239
|
* `createLeaderRunner`'s closure so it's a plain, unit-testable data constructor. */
|
|
253
240
|
export interface LeaderLoopConfig {
|
|
254
241
|
repairRounds: number;
|
|
255
242
|
repairBudgetUsd: number;
|
|
256
|
-
conflictRounds: number;
|
|
257
243
|
repairLoopOn: boolean;
|
|
258
244
|
measureGatesOn: boolean;
|
|
259
245
|
repairLoopAttempts: number;
|
|
@@ -264,7 +250,6 @@ export interface LeaderLoopConfig {
|
|
|
264
250
|
}
|
|
265
251
|
export declare function leaderLoopConfig(cfg: {
|
|
266
252
|
repairRounds?: number;
|
|
267
|
-
conflictRounds?: number;
|
|
268
253
|
}, env?: NodeJS.ProcessEnv): LeaderLoopConfig;
|
|
269
254
|
export declare const LEADER_PUSH_NETWORK_TIMEOUT_MS = 120000;
|
|
270
255
|
export declare const LEADER_PUSH_LOCAL_TIMEOUT_MS = 30000;
|
package/dist/leader/wire.js
CHANGED
|
@@ -16,7 +16,7 @@ import { attachRepairLoopDeps } from "./repair-wire.js";
|
|
|
16
16
|
import { routePlanWithFallback, validateSubtasks } from "./planner.js";
|
|
17
17
|
import { createEngineNoticeSeat } from "../boot/runner-deps.js";
|
|
18
18
|
import { currentPrincipal } from "../observability/principal-context.js";
|
|
19
|
-
import { redactHead
|
|
19
|
+
import { redactHead } from "../observability/run-terminal-log.js";
|
|
20
20
|
const REPAIR_LOOP_WIRED = true;
|
|
21
21
|
const sh = (env) => async (cmd) => {
|
|
22
22
|
const r = await env.exec(`bash -lc ${JSON.stringify(cmd)}`);
|
|
@@ -34,18 +34,6 @@ const asIntegrationEnv = (env) => ({
|
|
|
34
34
|
writeFile: async (p, c) => { const r = await env.writeFile(p, c); if (!r.ok)
|
|
35
35
|
throw r.error; },
|
|
36
36
|
});
|
|
37
|
-
export function buildConflictResolverObjective(args) {
|
|
38
|
-
const { workerId, repoDir, patch, conflictText } = args;
|
|
39
|
-
return [
|
|
40
|
-
`A parallel worker '${workerId}' ported a module on its own branch, but applying its patch onto the already-integrated tree at ${repoDir} produced a MERGE CONFLICT (git apply --3way). The OTHER workers' patches already applied cleanly — integrate THIS worker's changes too, resolving the overlap.`,
|
|
41
|
-
`Resolve EVERY conflict in the working tree: open each file containing conflict markers (<<<<<<< / ======= / >>>>>>>) and merge BOTH sides' real intent (keep both workers' behaviour — never drop one side just to make it apply). Then apply any rejected hunks recorded in *.rej files by hand, and DELETE every *.rej and *.orig file.`,
|
|
42
|
-
`When done, NO conflict markers and NO *.rej files may remain under ${repoDir}. Verify yourself before finishing: grep -rIlE '^(<<<<<<<|>>>>>>>)' . ; find . -name '*.rej' — BOTH must print nothing.`,
|
|
43
|
-
`HARD RULES: do NOT git commit / git add / git push (the coordinator squashes at the end); do NOT modify build configuration; do NOT call AskUserQuestion — decide autonomously. Preserve both sides' ported semantics.`,
|
|
44
|
-
``,
|
|
45
|
-
delimitUntrusted("CONFLICTING WORKER PATCH (untrusted)", redactPatchHead(patch, 6000)),
|
|
46
|
-
delimitUntrusted("CONFLICT DETAILS (untrusted)", conflictText),
|
|
47
|
-
].join("\n");
|
|
48
|
-
}
|
|
49
37
|
export function createEnvReaper(env, what) {
|
|
50
38
|
if (!hasDestroy(env)) {
|
|
51
39
|
throw new Error(`${what}: the execution-env factory returned an env with no destroy() — the leader OWNS this dedicated sandbox and this closure is its only teardown, so it would leak (and keep billing) until the backend's own timeout reaps it. Deploy a factory whose env implements RemoteExecutionEnv.destroy for the leader lane.`);
|
|
@@ -98,15 +86,12 @@ export function leaderLoopConfig(cfg, env = process.env) {
|
|
|
98
86
|
? parseNumOrFailNonNegative("LEADER_REPAIR_ROUNDS", env.LEADER_REPAIR_ROUNDS)
|
|
99
87
|
: (cfg.repairRounds ?? 0)) || 0);
|
|
100
88
|
const repairBudgetUsd = parseNumOrFailNonNegative("LEADER_REPAIR_BUDGET_USD", env.LEADER_REPAIR_BUDGET_USD ?? "4") || 4;
|
|
101
|
-
const conflictRounds = Math.max(0, Math.floor(env.LEADER_CONFLICT_ROUNDS !== undefined
|
|
102
|
-
? parseNumOrFailNonNegative("LEADER_CONFLICT_ROUNDS", env.LEADER_CONFLICT_ROUNDS)
|
|
103
|
-
: (cfg.conflictRounds ?? 0)) || 0);
|
|
104
89
|
const repairLoopOn = String(env.LEADER_REPAIR_LOOP ?? "").toLowerCase() === "true";
|
|
105
90
|
const measureGatesOn = String(env.LEADER_MEASURE_GATES ?? "").toLowerCase() === "true";
|
|
106
91
|
const repairLoopAttempts = Math.min(3, Math.max(2, Math.floor(parseNumOrFailNonNegative("LEADER_REPAIR_LOOP_ATTEMPTS", env.LEADER_REPAIR_LOOP_ATTEMPTS ?? "2")) || 2));
|
|
107
92
|
const oracleFlakyK = Math.max(1, Math.floor(parseNumOrFailNonNegative("LEADER_ORACLE_FLAKY_K", env.LEADER_ORACLE_FLAKY_K ?? "2")) || 2);
|
|
108
93
|
return {
|
|
109
|
-
repairRounds, repairBudgetUsd,
|
|
94
|
+
repairRounds, repairBudgetUsd, repairLoopOn, measureGatesOn, repairLoopAttempts, oracleFlakyK,
|
|
110
95
|
...(env.LEADER_BUDGET_USD ? { replanBudgetUsd: parseNumOrFailNonNegative("LEADER_BUDGET_USD", env.LEADER_BUDGET_USD) } : {}),
|
|
111
96
|
};
|
|
112
97
|
}
|
|
@@ -148,7 +133,7 @@ export function createLeaderRunner(cfg) {
|
|
|
148
133
|
const repo = `${W}/repo`;
|
|
149
134
|
const ident = cfg.git ?? { name: "leader", email: "leader@local" };
|
|
150
135
|
const { leaderTimeoutMs, workerLimits, presignTtlSec, resourceSuspend: resourceCfg } = leaderResourceConfig();
|
|
151
|
-
const { repairRounds, repairBudgetUsd,
|
|
136
|
+
const { repairRounds, repairBudgetUsd, repairLoopOn, measureGatesOn, repairLoopAttempts, oracleFlakyK, replanBudgetUsd, } = leaderLoopConfig({ repairRounds: cfg.repairRounds });
|
|
152
137
|
const onNoticeSeat = createEngineNoticeSeat(cfg.logger);
|
|
153
138
|
const governanceSeat = cfg.governance ?? {};
|
|
154
139
|
const deploymentPostureSeat = () => cfg.deploymentPosture?.() ?? {};
|
|
@@ -204,43 +189,6 @@ export function createLeaderRunner(cfg) {
|
|
|
204
189
|
},
|
|
205
190
|
};
|
|
206
191
|
};
|
|
207
|
-
const mkConflictResolver = (rawEnv, repoDir, oracleFiles) => {
|
|
208
|
-
if (conflictRounds <= 0)
|
|
209
|
-
return undefined;
|
|
210
|
-
return {
|
|
211
|
-
maxRounds: conflictRounds,
|
|
212
|
-
run: async ({ round, workerId, conflictText, patch }) => {
|
|
213
|
-
for (const f of oracleFiles) {
|
|
214
|
-
await rawEnv.remove(f.path).catch(() => { });
|
|
215
|
-
const ex = await rawEnv.exists(f.path);
|
|
216
|
-
if (ex.ok && ex.value)
|
|
217
|
-
throw new Error(`oracle still present after remove (${f.path}) — refusing conflict-resolve (measurement integrity)`);
|
|
218
|
-
}
|
|
219
|
-
try {
|
|
220
|
-
const runner = new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, executionEnv: rawEnv, rootPath: repoDir });
|
|
221
|
-
const objective = buildConflictResolverObjective({ workerId, repoDir, patch, conflictText });
|
|
222
|
-
const res = await runner
|
|
223
|
-
.runTaskStream({
|
|
224
|
-
objective,
|
|
225
|
-
...principalSpec(),
|
|
226
|
-
sessionId: `leader-conflict-${Date.now()}-${round}`,
|
|
227
|
-
limits: { maxCostUsd: repairBudgetUsd, maxWalltimeMs: Math.max(300_000, Math.floor(leaderTimeoutMs / 2)) },
|
|
228
|
-
})
|
|
229
|
-
.result();
|
|
230
|
-
if (res.terminal.kind !== "completed")
|
|
231
|
-
throw new Error(`conflict-resolve round ${round} did not complete (status=${terminalProjection(res.terminal).status})`);
|
|
232
|
-
return { costUsd: res.stats?.costMicroUsd !== undefined ? res.stats.costMicroUsd / 1e6 : undefined, note: res.result?.slice(0, 200) };
|
|
233
|
-
}
|
|
234
|
-
finally {
|
|
235
|
-
for (const f of oracleFiles) {
|
|
236
|
-
const wr = await rawEnv.writeFile(f.path, f.content);
|
|
237
|
-
if (!wr.ok)
|
|
238
|
-
throw wr.error;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
};
|
|
243
|
-
};
|
|
244
192
|
return async (raw) => {
|
|
245
193
|
const body = raw;
|
|
246
194
|
if (!body.objective || !body.durableRemote || !body.testCmd || !body.seedCmd || !body.baseSha) {
|
|
@@ -406,8 +354,7 @@ export function createLeaderRunner(cfg) {
|
|
|
406
354
|
const rawEnv = env;
|
|
407
355
|
const oracleFiles = body.oracleFiles ?? [];
|
|
408
356
|
const repair = mkRepair(rawEnv, repo, oracleFiles);
|
|
409
|
-
|
|
410
|
-
return { env: asIntegrationEnv(env), destroy: reap, ...(repair ? { repair } : {}), ...(conflictResolver ? { conflictResolver } : {}) };
|
|
357
|
+
return { env: asIntegrationEnv(env), destroy: reap, ...(repair ? { repair } : {}) };
|
|
411
358
|
}
|
|
412
359
|
if (!cfg.e2bApiKey)
|
|
413
360
|
throw new Error("leader wire: no envFactory and no e2bApiKey");
|
|
@@ -424,7 +371,7 @@ export function createLeaderRunner(cfg) {
|
|
|
424
371
|
await env.destroy().catch(() => { });
|
|
425
372
|
throw e;
|
|
426
373
|
}
|
|
427
|
-
return { env: asIntegrationEnv(env), destroy: () => env.destroy().then(() => { }), ...(mkRepair(env, repo, body.oracleFiles ?? []) ? { repair: mkRepair(env, repo, body.oracleFiles ?? []) } : {})
|
|
374
|
+
return { env: asIntegrationEnv(env), destroy: () => env.destroy().then(() => { }), ...(mkRepair(env, repo, body.oracleFiles ?? []) ? { repair: mkRepair(env, repo, body.oracleFiles ?? []) } : {}) };
|
|
428
375
|
};
|
|
429
376
|
const push = createCoordinatorPush(body.durableRemote, body.targetRef, ident);
|
|
430
377
|
const deps = {
|
package/dist/memory-scope.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileMemoryEngineBackend } from "@sema-agent/core";
|
|
1
|
+
import { FileMemoryEngineBackend, type TaskSpec } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "./config-types.js";
|
|
3
3
|
/** 前缀词表本体(供门/诊断读;判定一律走 {@link isV2ScopeKey},别在别处重写 startsWith 链)。 */
|
|
4
4
|
export declare const V2_SCOPE_PREFIXES: readonly string[];
|
|
@@ -175,6 +175,10 @@ onIncident?: (err: Error & {
|
|
|
175
175
|
* commits nothing). Absent/true ⇒ normal read+write. No scope (engine off / multi-tenant dark) ⇒ undefined
|
|
176
176
|
* (memory feature off for this run). Pure (testable in isolation) — main.ts's resolveSpec composes it with the
|
|
177
177
|
* engine-backend presence guard.
|
|
178
|
+
*
|
|
179
|
+
* 🔴 **三轴正交**(S-405 起;三者可任意并存,互不吞):`memoryWrite:false` = 本 run 只读平面(挂、读、
|
|
180
|
+
* harvest 不提交)· `memoryCapture:"off"` = 采集面关(挂、读、本会话内容永不进长期记忆)· `memory:"off"` =
|
|
181
|
+
* **整面关**(`enabled:false`,这一次连记忆面都不挂)。后两者从 `declarations` 进(键名与 wire 逐字同名)。
|
|
178
182
|
*/
|
|
179
183
|
export declare function memorySpecForRequest(scope: string | undefined, memoryWrite: boolean | undefined, defaultScopes?: string[],
|
|
180
184
|
/** [ref] 件A([ref] 件3④):origin 盖章的部署形态维(N2)。`multiTenant=true`(requirePrincipal)
|
|
@@ -185,14 +189,38 @@ export declare function memorySpecForRequest(scope: string | undefined, memoryWr
|
|
|
185
189
|
originPolicy?: {
|
|
186
190
|
multiTenant: boolean;
|
|
187
191
|
},
|
|
188
|
-
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
memoryCapture
|
|
192
|
+
/** 请求体上的**单成员闭集声明**(键名与 wire 逐字同名,零翻译;拼写门的唯一属主是
|
|
193
|
+
* `request-value-closure.ts`,两腿共用 —— 到这里的值已经过门,只可能是 `"off"` 或缺席)。
|
|
194
|
+
*
|
|
195
|
+
* · `memoryCapture:"off"`([ref] §2.1,[ref])⇒ `TaskSpec.memory.capture:"off"`:**采集面**关 ——
|
|
196
|
+
* 记忆照常挂、照常读,但本会话的内容永远不进长期记忆(core 落一条单向会话记录,按部署姿态 + verdict 裁)。
|
|
197
|
+
* · `memory:"off"`(core [ref] r5 §5 W3,S-405)⇒ `TaskSpec.memory.enabled:false`:**整面**关 ——
|
|
198
|
+
* 这一次运行不挂记忆面。**保留事实不折缺席**(codex r2 #4):core 的终局观测面把「有 spec 但被关」读作
|
|
199
|
+
* `reason:"disabled"`、把「整键缺席」读作 `reason:"no-spec"`,审计上不是一件事;而 `normalizeMemorySpec`
|
|
200
|
+
* 在 scopes 为空时整体返回 `undefined`,所以**关的那一形也必须带正常 scopes**。
|
|
201
|
+
*
|
|
202
|
+
* 两键与 `memoryWrite`(本 run 只读平面)在**铸型上**三轴正交、可并存,本函数不替谁做取舍;
|
|
203
|
+
* 整键缺席 ⇒ 都不铸(形状逐字节不变=additive 锁)。
|
|
204
|
+
* ⚠️ **铸型正交 ≠ 裁决正交**(亲核 core 7.21.1 `dist/core/runner/prepare-memory.js:48/:55`):core 把
|
|
205
|
+
* capture 腿与引擎挂载**合取**(`captureDeclared = captureDeclaredRaw && useMemoryEngine`),所以
|
|
206
|
+
* `memory:"off"` 那一 turn 上 capture 的**单向会话记录不落**(core 发 `phase:"config"` 一行响亮披露)。
|
|
207
|
+
* 两键同发要不要一起送、送了买到什么,口径全文在契约 §12.7;钉在 memory-off-request.test.ts ⑤。 */
|
|
208
|
+
declarations?: {
|
|
209
|
+
memory?: "off";
|
|
210
|
+
memoryCapture?: "off";
|
|
211
|
+
}): {
|
|
192
212
|
scopes: string[];
|
|
193
213
|
writeScope?: string | null;
|
|
194
214
|
scopeContract?: "v2";
|
|
195
215
|
scopeOrigins?: Record<string, "deployment" | "request">;
|
|
196
216
|
capture?: "off";
|
|
217
|
+
enabled?: false;
|
|
197
218
|
} | undefined;
|
|
219
|
+
/**
|
|
220
|
+
* 「这一腿挂不挂记忆面」的**唯一属主**(合并复审 车HF F1,2026-09-18):与 core `prepareMemory` 的判据同源 ——
|
|
221
|
+
* `normalizeMemorySpec(spec)?.enabled === true`。S-405 之后 `memory:"off"` 铸的是「键在场 + `enabled:false`」(保留事实),
|
|
222
|
+
* 任何按「键在不在场」判挂没挂的读者从那一天起都读错(改前 `memory-notice` 正是那样,把无记忆面的披露一起吞掉)。
|
|
223
|
+
* 消费方拿本函数的布尔,不拿 `spec.memory` 自己判。
|
|
224
|
+
*/
|
|
225
|
+
export declare function memoryFaceMounted(memory: TaskSpec["memory"]): boolean;
|
|
198
226
|
//# sourceMappingURL=memory-scope.d.ts.map
|
package/dist/memory-scope.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope } from "@sema-agent/core";
|
|
2
|
+
import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope, normalizeMemorySpec } from "@sema-agent/core";
|
|
3
3
|
import { assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
|
|
4
4
|
const V2_SCOPE_PREFIX_BY_KIND = {
|
|
5
5
|
user: "user:",
|
|
@@ -96,7 +96,7 @@ export function memoryEngineBackendFor(config, fallbackRoot, onIncident) {
|
|
|
96
96
|
root: plane.root,
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
export function memorySpecForRequest(scope, memoryWrite, defaultScopes, originPolicy,
|
|
99
|
+
export function memorySpecForRequest(scope, memoryWrite, defaultScopes, originPolicy, declarations) {
|
|
100
100
|
if (!scope)
|
|
101
101
|
return undefined;
|
|
102
102
|
const isV2 = isV2ScopeKey;
|
|
@@ -113,7 +113,13 @@ export function memorySpecForRequest(scope, memoryWrite, defaultScopes, originPo
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
const origins = Object.keys(stamped).length > 0 ? { scopeOrigins: stamped } : {};
|
|
116
|
-
const capture = memoryCapture === "off" ? { capture: "off" } : {};
|
|
117
|
-
|
|
116
|
+
const capture = declarations?.memoryCapture === "off" ? { capture: "off" } : {};
|
|
117
|
+
const enabled = declarations?.memory === "off" ? { enabled: false } : {};
|
|
118
|
+
return memoryWrite === false
|
|
119
|
+
? { ...layered, writeScope: null, ...v2, ...origins, ...capture, ...enabled }
|
|
120
|
+
: { ...layered, ...v2, ...origins, ...capture, ...enabled };
|
|
121
|
+
}
|
|
122
|
+
export function memoryFaceMounted(memory) {
|
|
123
|
+
return normalizeMemorySpec(memory)?.enabled === true;
|
|
118
124
|
}
|
|
119
125
|
//# sourceMappingURL=memory-scope.js.map
|
|
@@ -215,7 +215,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
215
215
|
};
|
|
216
216
|
readonly "server.run-terminal.redact-threw": {
|
|
217
217
|
readonly cls: "F";
|
|
218
|
-
readonly note: "
|
|
218
|
+
readonly note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形已由 S-219 的 atLeast 铸点闭掉,仍能抛的是 JSON 秘密字段臂的转义对循环,既有未闭,S-106)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。";
|
|
219
219
|
};
|
|
220
220
|
readonly "server.run-terminal.redact-capped": {
|
|
221
221
|
readonly cls: "F";
|
|
@@ -229,10 +229,6 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
229
229
|
readonly cls: "F";
|
|
230
230
|
readonly note: "S-107:诊断文本座(merge details/measure/grader trace、leader 日志座、planner 样本、journal 预览、workflow_run error cut)长度 > DIAG_INPUT_CAP(256KB)⇒ 整段换 «redacted:oversized-diagnostic» [len=N](fail-closed 取证丢弃;固定窗不 fail-closed 故不开窗);计数=多少段诊断文本被整段丢弃";
|
|
231
231
|
};
|
|
232
|
-
readonly "server.run-terminal.patch-head-cut": {
|
|
233
|
-
readonly cls: "F";
|
|
234
|
-
readonly note: "S-107:进模型的 worker 补丁(collapse 目标 / 冲突解决目标)> DIAG_INPUT_CAP(256KB)⇒ **不内联**,整份换 «redacted:oversized-patch-not-inlined» [len=N](第十三轮开的 256KB 头窗及其裁切守卫在五轮复审后撤回);计数=多少份补丁因超限未内联";
|
|
235
|
-
};
|
|
236
232
|
readonly "server.run-terminal.output-truncated": {
|
|
237
233
|
readonly cls: "F";
|
|
238
234
|
readonly note: "S-107:持久 error 脱后 UTF-8 字节 > 65,000(TiDB TEXT 65,535 字节)⇒ 脱后切并带标记(切在占位里不是泄漏);计数=多少条终态文案被字节封顶截断";
|
|
@@ -253,6 +249,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
253
249
|
readonly cls: "F";
|
|
254
250
|
readonly note: "S-55(test [5864] 独立复现):pg 连接池里一条连接被外部终止(pg_terminate_backend / 云端故障切换 / LB 空闲回收)或自身网络错误。**两态一 tag,detail 判别**(`state=idle` / `state=checked-out`;先例=park 墓碑行的两臂 detail 判别):①idle 态 —— node-postgres 语义(pg-pool@3.14.0 makeIdleListener):池在 emit 'error' **之前**已 `_remove` 掉该 client,坏连接自然淘汰,下次 acquire 发新连接,在飞查询零影响;修前 Pool 上无 'error' 监听 ⇒ EventEmitter 把事件转 throw ⇒ uncaughtException ⇒ 整进程 exit 1。②checked-out 态(codex R1-[high] 补,同批修)—— pg-pool 借出时摘 idle 监听(index.js:344),`pool.connect()` 显式持有的连接(事务)在 checkout 期死时 client 直接 emit 'error' 同样打死进程;守卫只观察:中毒 client 的后续查询由 pg 以 `_queryable=false` 响亮拒,归还时池淘汰;同一次 checkout 的死可能双发事件(FATAL 消息 + socket end),warn 逐枚、计数按 checkout 去重。放行的最坏后果 = 下一次 acquire 多付一次建连,故 F 类。必须留痕:「连接不断被外部杀」(故障切换风暴 / 回收策略过激 / 有人在库上清连接)与「一切正常」在服务面同形;每次事件另有逐次结构化 warn(`pg_pool_idle_client_error` / `pg_pool_checked_out_client_error`,含 code/severity),本计数让频率曲线可读。`pool.query()` 快路不在洞内(pg-pool 自带 checkout 期 once('error') 守卫)。mysql2 孪生(tidb-pool)**不同病**:PoolConnection 构造器恒挂 once('error') 库内吞并淘汰、借出期同在(坐标成文在 createTidbPool 头注),故无同族 tag。";
|
|
255
251
|
};
|
|
252
|
+
readonly "server.ensure-index.concurrently-unsupported": {
|
|
253
|
+
readonly cls: "F";
|
|
254
|
+
readonly note: "S-287(`plugins/ensure-index.ts` 的 PG 臂):这口 PG **协议**面不认 `CREATE INDEX CONCURRENTLY`。今天唯一的真消费者是**仓内离线镜像的手搓 `PgQueryFn` 假件**(`pg-mem` 不是本仓依赖,`test/memory-embedder-fingerprint.test.ts` 头注已为此记过一次档,这里不重复那个错)。判据是**能力面**不是名字,且**只认阳性信号**:SQLSTATE ∈ {`0A000` feature_not_supported、`42601` syntax_error},或错误**完全不带 `code`**(= 不是驱动也不是服务端给的错误)⇒ 判「不认」,回退成普通 `CREATE INDEX IF NOT EXISTS`;**带任何其它 `code` 的一律原样抛** —— SQLSTATE 的 `55P03`/`57014`/`23505`/`42501`/`25001` 与 libuv 的 `ECONNRESET`/`EPIPE`/`ETIMEDOUT` 都在这一侧(⚠️ 不要按「5 位大写」的形状去分这两族:`EPIPE` 恰好也是 5 位大写)。放行的最坏后果 = 这一次建索引持表级 `SHARE` 锁阻塞该表的写(纯**性能/可用性**面,索引本身照样建成、语义零差别),而能走到这条臂的面**按定义**不是生产 PG。计数非零 = 有一口本以为是真 PG 的部署其实不认 CONCURRENTLY,运维要去看它到底是什么。detail = `<表>.<索引名>`。";
|
|
255
|
+
};
|
|
256
256
|
};
|
|
257
257
|
/** 词表键推导的闭集类型——未登记的 tag 传不进 {@link recordFailOpen}(编译期拒)。 */
|
|
258
258
|
export type FailOpenTag = keyof typeof FAIL_OPEN_TAGS;
|
|
@@ -206,12 +206,11 @@ export const FAIL_OPEN_TAGS = {
|
|
|
206
206
|
},
|
|
207
207
|
"server.run-terminal.redact-threw": {
|
|
208
208
|
cls: "F",
|
|
209
|
-
note: "
|
|
209
|
+
note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形已由 S-219 的 atLeast 铸点闭掉,仍能抛的是 JSON 秘密字段臂的转义对循环,既有未闭,S-106)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。",
|
|
210
210
|
},
|
|
211
211
|
"server.run-terminal.redact-capped": { cls: "F", note: "S-107:终态自由文本(errorMessage/blockedReason/remoteEnvFailures[].message/error 列/done·failed 行)长度 > REDACT_INPUT_CAP(64KB)⇒ 整段换 «redacted:oversized»(fail-closed 取证丢弃,不截断不扫);计数=多少条终态文案被整段丢弃" },
|
|
212
212
|
"server.run-terminal.artifact-capped": { cls: "F", note: "S-107:leader_run 行上的工件字节(salvaged[].patch / candidatePatch.patch / merge.candidatePatch / merge.rej)长度 > ARTIFACT_INPUT_CAP(256KB)⇒ 整份换 «redacted:oversized-artifact»(fail-closed 取证丢弃;原字节仍在 worker 分支/merge 咽喉);计数=多少份工件被整份丢弃" },
|
|
213
213
|
"server.run-terminal.diag-capped": { cls: "F", note: "S-107:诊断文本座(merge details/measure/grader trace、leader 日志座、planner 样本、journal 预览、workflow_run error cut)长度 > DIAG_INPUT_CAP(256KB)⇒ 整段换 «redacted:oversized-diagnostic» [len=N](fail-closed 取证丢弃;固定窗不 fail-closed 故不开窗);计数=多少段诊断文本被整段丢弃" },
|
|
214
|
-
"server.run-terminal.patch-head-cut": { cls: "F", note: "S-107:进模型的 worker 补丁(collapse 目标 / 冲突解决目标)> DIAG_INPUT_CAP(256KB)⇒ **不内联**,整份换 «redacted:oversized-patch-not-inlined» [len=N](第十三轮开的 256KB 头窗及其裁切守卫在五轮复审后撤回);计数=多少份补丁因超限未内联" },
|
|
215
214
|
"server.run-terminal.output-truncated": { cls: "F", note: "S-107:持久 error 脱后 UTF-8 字节 > 65,000(TiDB TEXT 65,535 字节)⇒ 脱后切并带标记(切在占位里不是泄漏);计数=多少条终态文案被字节封顶截断" },
|
|
216
215
|
"server.runs.terminal-ledger-write-failed": { cls: "F", note: "S-107(全量门 silent-degradation 棘轮抓出的五处静默 `catch {}`):run 终局链里**账本腿**(flush 缓冲 text/reasoning、append `failed` 行)与终态翻转(setTerminal)已不共命运——账本 INSERT 抛不再让 run 卡 running 到 reaper;但那条抛掉的账本行之前是**静默**吞的。detail `arm=` 四臂(flush-clean-cancel / append-clean-cancel / flush-catch / append-thrown-cancel / append-failed):缺的是 run_events 尾巴(缓冲文本或 failed 事件),run 行本身仍是 durable truth,读面/reaper 容忍缺失,故 F 类;计数=多少次终局账本写失败。" },
|
|
217
216
|
"server.redact.ppk-eat-to-end": { cls: "F", note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;计数=多少次整段吃尾。" },
|
|
@@ -223,6 +222,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
223
222
|
cls: "F",
|
|
224
223
|
note: "S-55(test [5864] 独立复现):pg 连接池里一条连接被外部终止(pg_terminate_backend / 云端故障切换 / LB 空闲回收)或自身网络错误。**两态一 tag,detail 判别**(`state=idle` / `state=checked-out`;先例=park 墓碑行的两臂 detail 判别):①idle 态 —— node-postgres 语义(pg-pool@3.14.0 makeIdleListener):池在 emit 'error' **之前**已 `_remove` 掉该 client,坏连接自然淘汰,下次 acquire 发新连接,在飞查询零影响;修前 Pool 上无 'error' 监听 ⇒ EventEmitter 把事件转 throw ⇒ uncaughtException ⇒ 整进程 exit 1。②checked-out 态(codex R1-[high] 补,同批修)—— pg-pool 借出时摘 idle 监听(index.js:344),`pool.connect()` 显式持有的连接(事务)在 checkout 期死时 client 直接 emit 'error' 同样打死进程;守卫只观察:中毒 client 的后续查询由 pg 以 `_queryable=false` 响亮拒,归还时池淘汰;同一次 checkout 的死可能双发事件(FATAL 消息 + socket end),warn 逐枚、计数按 checkout 去重。放行的最坏后果 = 下一次 acquire 多付一次建连,故 F 类。必须留痕:「连接不断被外部杀」(故障切换风暴 / 回收策略过激 / 有人在库上清连接)与「一切正常」在服务面同形;每次事件另有逐次结构化 warn(`pg_pool_idle_client_error` / `pg_pool_checked_out_client_error`,含 code/severity),本计数让频率曲线可读。`pool.query()` 快路不在洞内(pg-pool 自带 checkout 期 once('error') 守卫)。mysql2 孪生(tidb-pool)**不同病**:PoolConnection 构造器恒挂 once('error') 库内吞并淘汰、借出期同在(坐标成文在 createTidbPool 头注),故无同族 tag。",
|
|
225
224
|
},
|
|
225
|
+
"server.ensure-index.concurrently-unsupported": {
|
|
226
|
+
cls: "F",
|
|
227
|
+
note: "S-287(`plugins/ensure-index.ts` 的 PG 臂):这口 PG **协议**面不认 `CREATE INDEX CONCURRENTLY`。今天唯一的真消费者是**仓内离线镜像的手搓 `PgQueryFn` 假件**(`pg-mem` 不是本仓依赖,`test/memory-embedder-fingerprint.test.ts` 头注已为此记过一次档,这里不重复那个错)。判据是**能力面**不是名字,且**只认阳性信号**:SQLSTATE ∈ {`0A000` feature_not_supported、`42601` syntax_error},或错误**完全不带 `code`**(= 不是驱动也不是服务端给的错误)⇒ 判「不认」,回退成普通 `CREATE INDEX IF NOT EXISTS`;**带任何其它 `code` 的一律原样抛** —— SQLSTATE 的 `55P03`/`57014`/`23505`/`42501`/`25001` 与 libuv 的 `ECONNRESET`/`EPIPE`/`ETIMEDOUT` 都在这一侧(⚠️ 不要按「5 位大写」的形状去分这两族:`EPIPE` 恰好也是 5 位大写)。放行的最坏后果 = 这一次建索引持表级 `SHARE` 锁阻塞该表的写(纯**性能/可用性**面,索引本身照样建成、语义零差别),而能走到这条臂的面**按定义**不是生产 PG。计数非零 = 有一口本以为是真 PG 的部署其实不认 CONCURRENTLY,运维要去看它到底是什么。detail = `<表>.<索引名>`。"
|
|
228
|
+
},
|
|
226
229
|
};
|
|
227
230
|
export function failOpenTagForDroppedFrame(frameType) {
|
|
228
231
|
switch (frameType) {
|
|
@@ -102,6 +102,8 @@ export declare function redactTerminalResult<T extends {
|
|
|
102
102
|
* 声明成 `{path,edits}[]` 会逼每一个喂坏形的调用点铸一次宽松断言 —— 而「坏形从哪来」正是本座要
|
|
103
103
|
* 处理的事(同一份不信任在 `remoteEnvFailures` 的实现里也已成文)。运行期逐项判形,不抛。 */
|
|
104
104
|
editedFiles?: unknown;
|
|
105
|
+
/** S-394:形上同样 `unknown` —— 本函数是读盘面,`stats` 的树形逐层现判(见 {@link redactStats})。 */
|
|
106
|
+
stats?: unknown;
|
|
105
107
|
} | null | undefined>(result: T): T;
|
|
106
108
|
/** S-107(第五轮合并重扫 wf_f00c00bc 两根 [high]):同一个 `TaskResult` 除 run 行外还被逐字追加进 run_events 的 `done` 行
|
|
107
109
|
* (bg/verify/cascade/stream/resume 五腿),`failed` 行带原文 `errorMessage`,`GET /v1/runs/:id/events` 原样回放。
|
|
@@ -135,13 +137,6 @@ export declare const DIAG_INPUT_CAP = 262144;
|
|
|
135
137
|
export declare const OVERSIZED_DIAG_PLACEHOLDER = "\u00ABredacted:oversized-diagnostic\u00BB";
|
|
136
138
|
export declare function redactDiagnosticText(text: string): string;
|
|
137
139
|
export declare function redactHead(text: string, keep: number): string;
|
|
138
|
-
/** 进模型的 worker 补丁头(collapse 目标 / 冲突解决目标):**不开窗**。第十三轮为保留大 diff 的可复用头部开了 256KB 头窗,
|
|
139
|
-
* 此后 codex r12–r15 与第十五至十七轮重扫五轮各证一次「窗上的裁切守卫可被打穿」(无上界的 JSON 值/URL 密码臂;脱前/脱后坐标
|
|
140
|
-
* 错位;PEM 逐块;前面一处脱敏会**吃掉**守卫所依赖的锚点)——同 S-103 的教训:猜边界的规则总有反例。终形=补丁 ≤256KB 整段扫
|
|
141
|
-
* 再切 keep(完全正确);>256KB **不内联**(占位带原长,tag `patch-head-cut`):冲突解决者仍有工作树里的冲突标记,collapse solo
|
|
142
|
-
* 对 lockfile 级大 diff 失去内联上下文(worker 分支仍在)——这是有意成文的安全轴取舍。 */
|
|
143
|
-
export declare const OVERSIZED_PATCH_PLACEHOLDER = "\u00ABredacted:oversized-patch-not-inlined\u00BB";
|
|
144
|
-
export declare function redactPatchHead(patch: string, keep: number): string;
|
|
145
140
|
export declare function redactTail(text: string, keep: number): string;
|
|
146
141
|
export declare const ARTIFACT_INPUT_CAP = 262144;
|
|
147
142
|
export declare const OVERSIZED_ARTIFACT_PLACEHOLDER = "\u00ABredacted:oversized-artifact\u00BB";
|