@sema-agent/server 7.55.0 → 7.57.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 +3 -3
- package/README.md +14 -7
- package/README.zh-CN.md +10 -6
- package/USAGE.md +207 -59
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/dist/approval-ask-audit-store.d.ts +100 -1
- package/dist/approval-ask-audit-store.js +103 -2
- package/dist/approval-card.d.ts +148 -13
- package/dist/approval-card.js +82 -13
- package/dist/approval-content-kind.d.ts +22 -0
- package/dist/approval-content-kind.js +5 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +18 -0
- package/dist/auto-mode-face.d.ts +111 -0
- package/dist/auto-mode-face.js +99 -0
- package/dist/bench/s1/arms.js +5 -5
- package/dist/bench/s1/live-deps.js +11 -11
- package/dist/bench/s1/run-firm.js +3 -0
- package/dist/bench/s1/runner-ctx.d.ts +4 -0
- package/dist/bench/s1/runner-ctx.js +7 -0
- package/dist/boot/config-center.js +23 -1
- package/dist/boot/coordinators.js +13 -1
- package/dist/boot/leader.d.ts +21 -0
- package/dist/boot/leader.js +6 -0
- package/dist/boot/parked-revive-gate.d.ts +7 -2
- package/dist/boot/parked-revive-gate.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +3 -1
- package/dist/boot/runner-deps.d.ts +12 -0
- package/dist/boot/runner-deps.js +28 -3
- package/dist/boot/runtime-caps.d.ts +19 -8
- package/dist/boot/runtime-caps.js +50 -20
- package/dist/boot/session-shell-gate-registry.d.ts +39 -0
- package/dist/boot/session-shell-gate-registry.js +21 -0
- package/dist/config-catalog.js +14 -5
- package/dist/config-center/types.d.ts +2 -1
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +41 -10
- package/dist/config.d.ts +21 -0
- package/dist/config.js +40 -5
- package/dist/http/route-ctx.d.ts +90 -3
- package/dist/http/routes/a2a-serve.js +5 -3
- package/dist/http/routes/approvals-assistant.js +21 -8
- package/dist/http/routes/capabilities.js +21 -4
- package/dist/http/routes/leader.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +2 -2
- package/dist/http/routes/rules.js +3 -2
- package/dist/http/routes/runs.d.ts +0 -14
- package/dist/http/routes/runs.js +20 -8
- package/dist/http/routes/tasks.js +34 -8
- package/dist/http/routes/workflows.js +20 -5
- package/dist/http/server.d.ts +11 -1
- package/dist/http/server.js +220 -34
- package/dist/http/wire-types.d.ts +9 -4
- package/dist/leader/diffout.js +2 -1
- package/dist/leader/endpoint.js +49 -15
- package/dist/leader/fanout.js +6 -5
- package/dist/leader/leader.js +17 -14
- package/dist/leader/merge.js +17 -12
- package/dist/leader/planner.js +3 -2
- package/dist/leader/repair-oracle.js +6 -4
- package/dist/leader/repair-wire.js +5 -3
- package/dist/leader/wire.d.ts +80 -1
- package/dist/leader/wire.js +55 -26
- package/dist/main.js +44 -8
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +10 -0
- package/dist/observability/fail-open.d.ts +40 -0
- package/dist/observability/fail-open.js +19 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.d.ts +120 -0
- package/dist/observability/run-terminal-log.js +360 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
- package/dist/orchestration/workflow-completion-inbox.js +8 -0
- package/dist/permission-rule-vocab.d.ts +40 -0
- package/dist/permission-rule-vocab.js +17 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +26 -0
- package/dist/plugins/checkpoint-store-sql.js +23 -4
- package/dist/plugins/file-run-store.d.ts +3 -34
- package/dist/plugins/file-run-store.js +21 -0
- package/dist/plugins/local-checkpoint-store.d.ts +10 -0
- package/dist/plugins/local-checkpoint-store.js +5 -0
- package/dist/plugins/local-session-store.d.ts +9 -9
- package/dist/plugins/local-session-store.js +5 -3
- package/dist/plugins/memory-run-store.d.ts +3 -15
- package/dist/plugins/memory-run-store.js +21 -0
- package/dist/plugins/permission-rule-store-file.d.ts +22 -4
- package/dist/plugins/permission-rule-store-file.js +25 -11
- package/dist/plugins/permission-rule-store-sql.d.ts +60 -30
- package/dist/plugins/permission-rule-store-sql.js +39 -23
- package/dist/plugins/pg-session-storage.js +17 -13
- package/dist/plugins/remote-scratchpad.js +3 -2
- package/dist/plugins/run-store-sql.d.ts +3 -42
- package/dist/plugins/run-store-sql.js +36 -5
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +70 -1
- package/dist/plugins/tidb-session-store.js +18 -14
- package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
- package/dist/plugins/workflow-run-store-sql.js +10 -2
- package/dist/rules-consent.d.ts +5 -4
- package/dist/rules-consent.js +25 -46
- package/dist/run-cancel-context.d.ts +13 -0
- package/dist/run-cancel-context.js +15 -0
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +4 -1
- package/dist/runs.js +41 -10
- package/dist/runtime-caps-resolver.d.ts +133 -17
- package/dist/runtime-caps-resolver.js +44 -4
- package/dist/security.d.ts +7 -0
- package/dist/task-settings.d.ts +57 -3
- package/dist/task-settings.js +78 -5
- package/dist/task-workflow.d.ts +32 -2
- package/dist/task-workflow.js +8 -3
- package/dist/tool-approval.d.ts +38 -63
- package/dist/tool-approval.js +102 -74
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +13 -4
- package/dist/trace/ledger-sink.js +14 -6
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +10 -1
- package/dist/trace/redact.d.ts +22 -11
- package/dist/trace/redact.js +655 -20
- package/dist/trace/sema-provenance.d.ts +26 -0
- package/dist/trace/sema-provenance.js +11 -0
- package/dist/turn-activity.d.ts +32 -2
- package/dist/turn-activity.js +29 -4
- package/package.json +3 -3
- package/skills/find-skills.md +1 -1
- package/skills/loop.md +1 -1
package/dist/approval-card.js
CHANGED
|
@@ -1,38 +1,97 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ASK_EVIDENCE_ABSENCE_VALUES, MAX_RULE_TEXT_CHARS } from "@sema-agent/core";
|
|
3
3
|
import { redactSecrets } from "./trace/redact.js";
|
|
4
|
+
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
|
+
import { PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS } from "./permission-rule-vocab.js";
|
|
4
6
|
export const MAX_AGENT_NAME = 200;
|
|
5
7
|
const MAX_IDENT = 200;
|
|
6
8
|
export const MAX_RULE_OFFERS = 4;
|
|
7
9
|
export const MAX_RULE_OFFER_BATCH_MEMBERS = 8;
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
export const MAX_UNCOVERED_DETAIL_ROWS = 32;
|
|
11
|
+
const RuleMatchSchema = z.enum(PERSISTED_RULE_MATCHES);
|
|
12
|
+
const UncoveredReasonSchema = z.enum(UNCOVERED_SEGMENT_REASONS);
|
|
13
|
+
const RuleOfferBatchMemberSchema = z.union([
|
|
14
|
+
z
|
|
15
|
+
.object({
|
|
16
|
+
kind: z.literal("command"),
|
|
17
|
+
rule: z.string().max(MAX_RULE_TEXT_CHARS),
|
|
18
|
+
match: RuleMatchSchema,
|
|
19
|
+
command: z.string().max(MAX_RULE_TEXT_CHARS),
|
|
20
|
+
segment: z.string().max(MAX_RULE_TEXT_CHARS),
|
|
21
|
+
})
|
|
22
|
+
.strict(),
|
|
23
|
+
z
|
|
24
|
+
.object({
|
|
25
|
+
kind: z.literal("directoryRead"),
|
|
26
|
+
rule: z.string().max(MAX_RULE_TEXT_CHARS),
|
|
27
|
+
directory: z.string().max(MAX_RULE_TEXT_CHARS),
|
|
28
|
+
segment: z.string().max(MAX_RULE_TEXT_CHARS),
|
|
29
|
+
})
|
|
30
|
+
.strict(),
|
|
31
|
+
]);
|
|
32
|
+
const UncoveredSegmentDetailSchema = z.object({ segment: z.string().max(MAX_RULE_TEXT_CHARS), reason: UncoveredReasonSchema }).strict();
|
|
33
|
+
const UncoveredSegmentDetailRawSchema = z.object({ segment: z.string(), reason: UncoveredReasonSchema }).strip();
|
|
16
34
|
export const RuleOfferSchema = z.union([
|
|
17
|
-
z.object({ kind: z.literal("single"), rule: z.string().max(MAX_RULE_TEXT_CHARS), match:
|
|
35
|
+
z.object({ kind: z.literal("single"), rule: z.string().max(MAX_RULE_TEXT_CHARS), match: RuleMatchSchema, command: z.string().max(MAX_RULE_TEXT_CHARS) }).strict(),
|
|
18
36
|
z
|
|
19
37
|
.object({
|
|
20
38
|
kind: z.literal("batch"),
|
|
21
|
-
rules: z.array(
|
|
39
|
+
rules: z.array(RuleOfferBatchMemberSchema).min(1).max(MAX_RULE_OFFER_BATCH_MEMBERS),
|
|
22
40
|
uncoveredSegments: z.number().int().nonnegative().safe(),
|
|
41
|
+
uncoveredDetail: z.array(UncoveredSegmentDetailSchema).max(MAX_UNCOVERED_DETAIL_ROWS).optional(),
|
|
23
42
|
})
|
|
24
43
|
.strict(),
|
|
25
44
|
]);
|
|
26
45
|
export const RuleOfferRawSchema = z.union([
|
|
27
|
-
z.object({ kind: z.literal("single"), rule: z.string(), match:
|
|
46
|
+
z.object({ kind: z.literal("single"), rule: z.string(), match: RuleMatchSchema, command: z.string() }).strip(),
|
|
28
47
|
z
|
|
29
48
|
.object({
|
|
30
49
|
kind: z.literal("batch"),
|
|
31
|
-
rules: z
|
|
50
|
+
rules: z
|
|
51
|
+
.array(z.union([
|
|
52
|
+
z.object({ kind: z.literal("command"), rule: z.string(), match: RuleMatchSchema, command: z.string(), segment: z.string() }).strip(),
|
|
53
|
+
z.object({ kind: z.literal("directoryRead"), rule: z.string(), directory: z.string(), segment: z.string() }).strip(),
|
|
54
|
+
]))
|
|
55
|
+
.min(1),
|
|
32
56
|
uncoveredSegments: z.number().int().nonnegative().safe(),
|
|
57
|
+
uncoveredDetail: z.array(UncoveredSegmentDetailRawSchema).optional(),
|
|
33
58
|
})
|
|
34
59
|
.strip(),
|
|
35
60
|
]);
|
|
61
|
+
export function screenUncoveredDetailSeat(item) {
|
|
62
|
+
if (typeof item !== "object" || item === null)
|
|
63
|
+
return item;
|
|
64
|
+
const candidate = item;
|
|
65
|
+
if (candidate.kind !== "batch" || !("uncoveredDetail" in candidate) || candidate.uncoveredDetail === undefined)
|
|
66
|
+
return item;
|
|
67
|
+
const rows = candidate.uncoveredDetail;
|
|
68
|
+
const wellFormed = Array.isArray(rows) &&
|
|
69
|
+
rows.length <= MAX_UNCOVERED_DETAIL_ROWS &&
|
|
70
|
+
rows.length === candidate.uncoveredSegments &&
|
|
71
|
+
rows.every((r) => UncoveredSegmentDetailRawSchema.safeParse(r).success);
|
|
72
|
+
if (wellFormed)
|
|
73
|
+
return item;
|
|
74
|
+
recordFailOpen("server.rule-offers.uncovered-detail-dropped", Array.isArray(rows) ? `rows=${rows.length} count=${String(candidate.uncoveredSegments)}` : `seat is ${typeof rows}`);
|
|
75
|
+
const { uncoveredDetail: _dropped, ...rest } = candidate;
|
|
76
|
+
return rest;
|
|
77
|
+
}
|
|
78
|
+
export function backfillLegacyBatchMemberKind(item) {
|
|
79
|
+
if (typeof item !== "object" || item === null)
|
|
80
|
+
return item;
|
|
81
|
+
const candidate = item;
|
|
82
|
+
if (candidate.kind !== "batch" || !Array.isArray(candidate.rules))
|
|
83
|
+
return item;
|
|
84
|
+
const isLegacy = (m) => typeof m === "object" &&
|
|
85
|
+
m !== null &&
|
|
86
|
+
!("kind" in m) &&
|
|
87
|
+
typeof m.rule === "string" &&
|
|
88
|
+
typeof m.match === "string" &&
|
|
89
|
+
typeof m.command === "string" &&
|
|
90
|
+
typeof m.segment === "string";
|
|
91
|
+
if (!candidate.rules.some(isLegacy))
|
|
92
|
+
return item;
|
|
93
|
+
return { ...candidate, rules: candidate.rules.map((m) => (isLegacy(m) ? { kind: "command", ...m } : m)) };
|
|
94
|
+
}
|
|
36
95
|
export function copyRuleOffer(o) {
|
|
37
96
|
switch (o.kind) {
|
|
38
97
|
case "single":
|
|
@@ -40,8 +99,18 @@ export function copyRuleOffer(o) {
|
|
|
40
99
|
case "batch":
|
|
41
100
|
return {
|
|
42
101
|
kind: "batch",
|
|
43
|
-
rules: o.rules.map((r) =>
|
|
102
|
+
rules: o.rules.map((r) => {
|
|
103
|
+
switch (r.kind) {
|
|
104
|
+
case "command":
|
|
105
|
+
return { kind: "command", rule: r.rule, match: r.match, command: r.command, segment: r.segment };
|
|
106
|
+
case "directoryRead":
|
|
107
|
+
return { kind: "directoryRead", rule: r.rule, directory: r.directory, segment: r.segment };
|
|
108
|
+
default:
|
|
109
|
+
throw new Error(`unknown rule-offer batch member kind: ${JSON.stringify(r)}`);
|
|
110
|
+
}
|
|
111
|
+
}),
|
|
44
112
|
uncoveredSegments: o.uncoveredSegments,
|
|
113
|
+
...(o.uncoveredDetail !== undefined ? { uncoveredDetail: o.uncoveredDetail.map((d) => ({ segment: d.segment, reason: d.reason })) } : {}),
|
|
45
114
|
};
|
|
46
115
|
default:
|
|
47
116
|
throw new Error(`unknown rule-offer kind: ${JSON.stringify(o)}`);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [ref]([ref]② core 点名 → [ref] 认领):`contentKind` 投影键的**单一词属主**(server 侧)。
|
|
3
|
+
*
|
|
4
|
+
* core 在 `summarizeCheckpoint`(dist/core/checkpoint-store.js)对 tool_approval 门铸
|
|
5
|
+
* `contentKind: "content_ask"`,判据一行:`tool.toolName === ASK_USER_QUESTION_TOOL_NAME`
|
|
6
|
+
* (常量声明 = core `dist/core/ask-question.d.ts:14`,值 `"AskUserQuestion"`;该常量**未从 core 包根
|
|
7
|
+
* 导出**(index.d.ts 零命中,exports map 无深路径口)⇒ 本仓落一份同字节常量,不做深路径导入)。
|
|
8
|
+
* 三条 server 投影面(SQL `listPending` 列读 / LOCAL `listPending` blob 读 / live `listLivePending`)
|
|
9
|
+
* 全部经 {@link contentKindOf} 派生,恒与 core 的 summarize 面同词 —— 词漂移由 parity 钉看住
|
|
10
|
+
* (`test/local-checkpoint-store.test.ts` [ref] parity 格:core `summarizeCheckpoint` 对 AskUserQuestion
|
|
11
|
+
* checkpoint 的铸值必须逐字等于本函数的答案)。
|
|
12
|
+
*
|
|
13
|
+
* 🔴 与 `hasBidiControls` 的「server 不重算」纪律**不冲突**:那一位是有界扫描的结果(重算可分家),
|
|
14
|
+
* 而 contentKind 是行上 core 铸的 `toolName` 的**全函数**(一行确定性判)——派生 ≠ 重扫。
|
|
15
|
+
* 🔴 OMIT 契约与同族键一致(`boundCallId`/`gateKind`/`hasBidiControls` 的 [ref]② 纪律):
|
|
16
|
+
* `"content_ask"` 或**缺席**,恒不铸 `null`/`false`;缺席 = 不是内容问句门(普通工具门 / 非工具 park /
|
|
17
|
+
* toolName 缺席的存量行)。展示/分诊/渲染分型用,永不参与 resume / gate / CAS。
|
|
18
|
+
*/
|
|
19
|
+
export declare const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
|
|
20
|
+
/** 三读面共用的派生:AskUserQuestion 工具门 ⇒ `"content_ask"`;其余(含 null/缺席)⇒ undefined(键缺席)。 */
|
|
21
|
+
export declare function contentKindOf(toolName: string | null | undefined): "content_ask" | undefined;
|
|
22
|
+
//# sourceMappingURL=approval-content-kind.d.ts.map
|
package/dist/approval.d.ts
CHANGED
|
@@ -36,6 +36,37 @@ export declare function assertGateIntentServiceable(config: {
|
|
|
36
36
|
approvalNeverAuto: readonly string[];
|
|
37
37
|
durableApproval: boolean;
|
|
38
38
|
}, checkpointStorePresent: boolean): void;
|
|
39
|
+
/**
|
|
40
|
+
* [ref] 件⑩ —— **center 的 `forceDurableGate` 在无 checkpoint 店的部署上会静默 inert**,补一条 boot 披露。
|
|
41
|
+
*
|
|
42
|
+
* 事实链(core 7.2.0 亲读):`RuntimeCaps.forceDurableGate:true` 让引擎**合成**一个
|
|
43
|
+
* `durableApproval{scope: principal}`(`dist/core/types.d.ts:5279-5283`),好让 policy 的 `ask` 挂到
|
|
44
|
+
* durable wire 上。但「挂得上去」的前提是这台 worker 真有 checkpoint 店 —— 而本仓的构造条件逐字是
|
|
45
|
+
* `backend?.checkpoint && config.durableApproval`(main.ts)。`DURABLE_APPROVAL` 关 ⇒ 店缺席 ⇒ 合成出来
|
|
46
|
+
* 的那把锁**没有锁孔**:core 侧退化成非 durable 的 ask 链,center 那条「fleet-wide interactive runs gate」
|
|
47
|
+
* 的授权在这台机器上**一个字节都没兑现**,且两侧都不出声(core 侧归 core 的 C-22)。
|
|
48
|
+
*
|
|
49
|
+
* 为什么是 warn 不是拒启:`forceDurableGate` 是 **per-principal** 的授权,boot 期没有 principal,判不出
|
|
50
|
+
* 「今天真的会有人被下发」——能判的只有「这台机器的下发口开着(有 entitlement 源)而承接面关着」。
|
|
51
|
+
* 拿一个**可能**的下发去挡死一台本来跑得好好的 worker,是本条诊断带来的新故障,不是它治的病
|
|
52
|
+
* (与 `noticeAutoCompactAboveWindow` 的同一条取舍)。所以:响亮一行,不拒启。
|
|
53
|
+
*
|
|
54
|
+
* 为什么在 **boot** 判而不是 resolve 时判:两个入参都是**部署级**的(entitlement 源在不在、durable 门开
|
|
55
|
+
* 不开),per-principal 的那一半改变不了结论;放 resolve 里就得自己造去重,而 boot 天然只跑一次。
|
|
56
|
+
*
|
|
57
|
+
* 纯函数(返回一行或 undefined,不自己写日志)= 红先测得动;调用点在 main.ts 的
|
|
58
|
+
* {@link assertGateIntentServiceable} 之后(同一段门装配)。
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildForceDurableGateInertNotice(input: {
|
|
61
|
+
/** center per-principal entitlement 源在不在(`centerEntitlementSourceWired` 的结果 —— 判据单源在那里)。 */
|
|
62
|
+
entitlementSourceWired: boolean;
|
|
63
|
+
/** 本进程真建出 checkpoint 店了吗(= `backend?.checkpoint && DURABLE_APPROVAL`)。 */
|
|
64
|
+
checkpointStorePresent: boolean;
|
|
65
|
+
durableApproval: boolean;
|
|
66
|
+
}): {
|
|
67
|
+
event: string;
|
|
68
|
+
fields: Record<string, unknown>;
|
|
69
|
+
} | undefined;
|
|
39
70
|
/**
|
|
40
71
|
* Durable approval policy (core 1.67 / [ref]): returns three-state **`ask`** for gated tools instead of
|
|
41
72
|
* polling. With `TaskSpec.durableApproval` set + a `checkpointStore` on the Runner, core turns that `ask` into
|
package/dist/approval.js
CHANGED
|
@@ -13,6 +13,24 @@ export function assertGateIntentServiceable(config, checkpointStorePresent) {
|
|
|
13
13
|
}
|
|
14
14
|
throw new Error("DURABLE_APPROVAL=true but no checkpoint store is available — the configured DB backend has no checkpoint face (or no backend is configured). Wire a MySQL-protocol/PostgreSQL/file backend, or unset DURABLE_APPROVAL.");
|
|
15
15
|
}
|
|
16
|
+
export function buildForceDurableGateInertNotice(input) {
|
|
17
|
+
if (!input.entitlementSourceWired || input.checkpointStorePresent)
|
|
18
|
+
return undefined;
|
|
19
|
+
return {
|
|
20
|
+
event: "center_force_durable_gate_inert",
|
|
21
|
+
fields: {
|
|
22
|
+
durableApproval: input.durableApproval,
|
|
23
|
+
note: "this deployment resolves per-principal entitlements from config-center, but it has NO checkpoint store " +
|
|
24
|
+
(input.durableApproval
|
|
25
|
+
? "(DURABLE_APPROVAL=true, yet the configured backend exposes no checkpoint face)"
|
|
26
|
+
: "(DURABLE_APPROVAL is off)") +
|
|
27
|
+
" — a center-issued RuntimeCaps.forceDurableGate=true will be INERT here: the engine synthesizes a " +
|
|
28
|
+
"durableApproval scope, but with no store to suspend into, gated asks fall back to the non-durable chain " +
|
|
29
|
+
"and the fleet-wide interactive-runs mandate is NOT enforced on this worker. Set DURABLE_APPROVAL=true with " +
|
|
30
|
+
"a backend that has a checkpoint face (MySQL-protocol / PostgreSQL / file), or stop issuing forceDurableGate for these principals.",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
16
34
|
function createNameSetMatcher(names) {
|
|
17
35
|
const exact = new Set(names);
|
|
18
36
|
const covering = names.filter(isNamespacedCoveringRuleName);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type Model, type ModelRoles, type RuntimeCaps } from "@sema-agent/core";
|
|
2
|
+
import type { SettingsPermissionMode } from "./task-settings.js";
|
|
3
|
+
/**
|
|
4
|
+
* 分类器**配置**的模型路由 —— 直接复用 core 的 `resolveTaskModel({ modelRole: "classifier" })`(prepare-task 武装时
|
|
5
|
+
* 走的同一函数:role 回落链 `classifier → summarize → default`,且 `{select}` 形 RoleSpec 按目录选型)。
|
|
6
|
+
* codex r4 [medium] 验真后改:首版按名字级自算,遇 center 下发的 `{select:{tier}}` 形会错落到 summarize/default,
|
|
7
|
+
* 对 `sema doctor permissions` 报另一个模型。答的是「配置了什么」;运行期 `derivedRouteFallsBack` 的回落不在射程
|
|
8
|
+
* (那是 core 状态座的事)。什么都解不出 ⇒ `{id:undefined, fault}`(core 的拒句当数据交出,读面不写 model 键):
|
|
9
|
+
* 本面是纯披露、不是执法——读面 500 掉整张能力表比少一个 model 键更伤。
|
|
10
|
+
*/
|
|
11
|
+
export declare function classifierRouteFor(deps: {
|
|
12
|
+
models?: Record<string, Model>;
|
|
13
|
+
roles?: ModelRoles;
|
|
14
|
+
}): ClassifierRoute;
|
|
15
|
+
/** `classifierRouteFor` 的答:解出 ⇒ `{id}`;解不出 ⇒ `{id:undefined, fault}`(core 的拒句原文,不吞)。 */
|
|
16
|
+
export type ClassifierRoute = {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly fault?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
readonly id: undefined;
|
|
21
|
+
readonly fault: string;
|
|
22
|
+
};
|
|
23
|
+
/** core [ref] seam ⑤ 的未武装原因闭集(六词,[ref] 加 settings_denied;顺序 = 判序)。 */
|
|
24
|
+
export declare const AUTO_MODE_UNARMED_REASONS: readonly ["mode_not_auto", "deployment_incapable", "org_denied", "local_denied", "settings_denied", "resolver_fault"];
|
|
25
|
+
export type AutoModeUnarmedReason = (typeof AUTO_MODE_UNARMED_REASONS)[number];
|
|
26
|
+
/** 锁步表:词表与语义一一对应(加员/删员任一侧不同步 = tsc 红)。语义只供文档/测试,不上 wire。 */
|
|
27
|
+
export declare const AUTO_MODE_UNARMED_REASON_SEMANTICS: {
|
|
28
|
+
readonly mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\";座缺席)——只属意图武装式,旧式 core 不读模式";
|
|
29
|
+
readonly deployment_incapable: "装配面没挂 RunnerDeps.autoMode 分类器席位(本部署/本进程形不具备分类器);旧式 core 下另含「无 center 源 = 零授予路径」";
|
|
30
|
+
readonly org_denied: "per-principal caps.autoMode === false 且本地 deny 未设:center 显式 deny / center 硬失败 fail-closed 拒项;旧式 core 下另含「center 在场但未授予」";
|
|
31
|
+
readonly local_denied: "本地 catalog 键 PERMISSIONS_DISABLE_AUTO_MODE=true(center/settings 键名 permissions.disableAutoMode)把 caps.autoMode 折成 false";
|
|
32
|
+
readonly settings_denied: "settings 层 permissions.disableAutoMode 取 disable 或 true(两套已发布拼写同义;用户/managed 层 kill-switch)把本 run 折成非 auto —— capabilities 查询串不带 settings,本词由 run 级面产([6128] PM 裁定第六员)";
|
|
33
|
+
readonly resolver_fault: "per-principal caps resolver 抛错/答非记录(core 同形全拒:autoMode:false + onError phase config)";
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* 安装包探针:**这台机器上装的 core 用哪套武装极性**。
|
|
37
|
+
*
|
|
38
|
+
* 判据([ref] 改,原先用「854b202b 同批的运行时导出 `mailboxCrossProcessMountVerdict`」作代理 —— 那是 385 片2a 的
|
|
39
|
+
* mailbox 面,与 auto 武装**语义无关**,只是恰好同一个 dist;core 若把 [ref] 单独发一个补丁版,代理就答错):
|
|
40
|
+
*
|
|
41
|
+
* · **主判(语义精确)= [ref] 自己的座**:武装点 `dist/core/runner/prepare-task.js` 里是否出现 `autoModeRequested`。
|
|
42
|
+
* 亲验(2026-09-03,`git grep autoModeRequested` 两 ref 对拍):v7.2.0 全树**零**处提及,854b202b 起 prepare-task
|
|
43
|
+
* 有 8 处 —— 单调、且探的就是「武装点读不读意图座」这件事本身。dist 未 mangle(属性名从不被 tsc/esbuild 改名),
|
|
44
|
+
* d.ts 不参与判断(类型面运行期探不到,读 js 才是「引擎真这么跑」)。
|
|
45
|
+
* · **佐证(只在主判为 false 时读)= 同批到货的运行时导出** `mailboxCrossProcessMountVerdict`(包内随发的
|
|
46
|
+
* `node_modules/@sema-agent/core/test/export-surface.snapshot.json`(core 包内随发),core `files` 字段含它 —— 消费账本门 A 的同一真源)。
|
|
47
|
+
* - 座缺席 ∧ 标记缺席 ⇒ 就是 npm 7.2.0 那一档 ⇒ `false`(旧式臂,兼容形);
|
|
48
|
+
* - 座缺席 ∧ 标记**在场** ⇒ **矛盾**:dist 已是 854b202b 之后,而武装点不再提这个座名(core 重构/换文件)
|
|
49
|
+
* ⇒ 判据失效,**抛**(不猜。静默回落旧式臂正是「854 之后的部署悄悄退回 org 授予极性」那一形)。
|
|
50
|
+
* - 座在场 ⇒ `true`,不看佐证([ref] 单发的补丁版合法)。
|
|
51
|
+
*
|
|
52
|
+
* 🔴 不用 `import * as core`(消费账本门禁命名空间导入:每个消费名必须可按名对账);两个文件都经主入口 resolve
|
|
53
|
+
* 定位(package `exports` 映射不暴露这些子路径)。**任何读不到 = 抛**,由 boot 装配点承接成拒启(见
|
|
54
|
+
* {@link coreArmsAutoOnIntent})。
|
|
55
|
+
*
|
|
56
|
+
* @param distDir 测试注入的假 `dist/` 根;生产不传(从安装的 core 主入口解)。
|
|
57
|
+
*/
|
|
58
|
+
export declare function coreArmsAutoOnIntentProbe(distDir?: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* {@link coreArmsAutoOnIntentProbe} 的**惰性记忆化**访问器 —— 生产装配点(`main.ts` 的 `autoModeFace` 组装,
|
|
61
|
+
* 早于 `server.listen`)调一次,探针抛就是**boot 期响亮拒启**并带指路。
|
|
62
|
+
*
|
|
63
|
+
* 🔴 为什么必须惰性([ref]):此前是模块顶层 `const … = coreArmsAutoOnIntentProbe()`,而 `main.ts` 顶部 import 本模块
|
|
64
|
+
* ⇒ 探针在 **import 期**求值,读不到快照就在任何日志器/诊断存在之前把整台 server 抛死(一个 npm 安装瑕疵 =
|
|
65
|
+
* 一台起不来且没有遗言的机器)。惰性之后,同一条失败落在 boot 装配点,遗言完整、退出码照旧。
|
|
66
|
+
*/
|
|
67
|
+
export declare function coreArmsAutoOnIntent(): boolean;
|
|
68
|
+
/** 装配面交给读面的四件事实(`main.ts` 从 runnerDeps / 合成后的 caps resolver / 到货位取,读面不自算)。 */
|
|
69
|
+
export interface AutoModeFaceDeps {
|
|
70
|
+
/** `RunnerDeps.autoMode !== undefined`——**读**装配点那一位,不写字面 true(与 parked-revive 的
|
|
71
|
+
* `autoModeSeatMounted` 同一纪律:席位哪天改成有条件挂载,这里不能静默错供)。 */
|
|
72
|
+
readonly seatMounted: boolean;
|
|
73
|
+
/** {@link coreArmsAutoOnIntent}(生产装配递探针访问器;测试可递 true 走正控)。false ⇒ `deployment_incapable`。 */
|
|
74
|
+
readonly engineArmsOnIntent: boolean;
|
|
75
|
+
/** 合成后的 per-principal caps resolver(= 递给 `RunnerDeps.runtimeCapsResolver` 的同一只:center 客户端
|
|
76
|
+
* → observer env 基线 → memory grant → 本地 auto deny,单一属主 boot/runtime-caps.ts)。缺席 = 无任何
|
|
77
|
+
* per-principal 源(无 center、无本地 deny)⇒ caps undefined ⇒ 三态之「缺席=不阻」。 */
|
|
78
|
+
readonly resolveCaps?: ((principal: string | undefined) => RuntimeCaps | undefined | Promise<RuntimeCaps | undefined>) | undefined;
|
|
79
|
+
/** 分类器**配置**的模型路由(server 侧按 core 的 role 回落序 `classifier → summarize → default` 从
|
|
80
|
+
* `config.roles` 读到的 catalog 名)。这是「配置了什么」,不是「这次真用了什么」——core 的
|
|
81
|
+
* `derivedRouteFallsBack` 可能在运行期回落到主模型;真值候 core 状态座。
|
|
82
|
+
* 🔴 codex r3 [medium]:是 **getter** 不是快照——config-center 会原地热更新 `config.roles`(Runner 每任务从活引用
|
|
83
|
+
* 重解),boot 期铸的字符串会在热切换后永久陈旧;生产装配递的 getter 调 `classifierRouteFor({ models: config.models, roles: config.roles })` 并把 fault 记 debug。 */
|
|
84
|
+
readonly classifierModel?: (() => string | undefined) | undefined;
|
|
85
|
+
}
|
|
86
|
+
export type AutoModeVerdict = {
|
|
87
|
+
readonly armed: true;
|
|
88
|
+
readonly model?: string;
|
|
89
|
+
readonly fault?: undefined;
|
|
90
|
+
} | {
|
|
91
|
+
readonly armed: false;
|
|
92
|
+
readonly reason: AutoModeUnarmedReason;
|
|
93
|
+
readonly model?: string; /** 仅 resolver_fault 臂:错误原文(不上 wire,路由记 warn)。 */
|
|
94
|
+
readonly fault?: string;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* 三项合取的 server 侧裁决。`requestedMode` 缺席 = 按 auto 意图答(「若请求会不会武装」);`principal` =
|
|
98
|
+
* 调用方经门验证的身份(匿名 ⇒ undefined ⇒ center 客户端答 undefined,本地 deny 层照样罩)。
|
|
99
|
+
*
|
|
100
|
+
* 🔴 resolver 抛错**不吞**:合成腿的每一层都自带 fail-closed/缺席臂(center 客户端 catch-all、grant 腿 onFault),
|
|
101
|
+
* 走到这里还在抛 = 装配缺陷,读面 500 比一句编出来的 reason 诚实。
|
|
102
|
+
*/
|
|
103
|
+
export declare function judgeAutoModeArming(face: AutoModeFaceDeps | undefined, input: {
|
|
104
|
+
readonly requestedMode: SettingsPermissionMode | undefined;
|
|
105
|
+
readonly principal: string | undefined;
|
|
106
|
+
readonly entitlementSource: boolean;
|
|
107
|
+
/** 本地 `PERMISSIONS_DISABLE_AUTO_MODE`(= catalog/settings 键 `permissions.disableAutoMode`)是否置 true —— caps false 时分词
|
|
108
|
+
* `local_denied` / `org_denied`(seam ⑤;两者对壳的指路不同:改本机 env vs 找组织)。 */
|
|
109
|
+
readonly localDeny: boolean;
|
|
110
|
+
}): Promise<AutoModeVerdict>;
|
|
111
|
+
//# sourceMappingURL=auto-mode-face.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { resolveTaskModel } from "@sema-agent/core";
|
|
6
|
+
export function classifierRouteFor(deps) {
|
|
7
|
+
try {
|
|
8
|
+
return { id: resolveTaskModel({ modelRole: "classifier" }, deps).model.id };
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
return { id: undefined, fault: err instanceof Error ? err.message : String(err) };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const AUTO_MODE_UNARMED_REASONS = ["mode_not_auto", "deployment_incapable", "org_denied", "local_denied", "settings_denied", "resolver_fault"];
|
|
15
|
+
export const AUTO_MODE_UNARMED_REASON_SEMANTICS = {
|
|
16
|
+
mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\";座缺席)——只属意图武装式,旧式 core 不读模式",
|
|
17
|
+
deployment_incapable: "装配面没挂 RunnerDeps.autoMode 分类器席位(本部署/本进程形不具备分类器);旧式 core 下另含「无 center 源 = 零授予路径」",
|
|
18
|
+
org_denied: "per-principal caps.autoMode === false 且本地 deny 未设:center 显式 deny / center 硬失败 fail-closed 拒项;旧式 core 下另含「center 在场但未授予」",
|
|
19
|
+
local_denied: "本地 catalog 键 PERMISSIONS_DISABLE_AUTO_MODE=true(center/settings 键名 permissions.disableAutoMode)把 caps.autoMode 折成 false",
|
|
20
|
+
settings_denied: "settings 层 permissions.disableAutoMode 取 disable 或 true(两套已发布拼写同义;用户/managed 层 kill-switch)把本 run 折成非 auto —— capabilities 查询串不带 settings,本词由 run 级面产([6128] PM 裁定第六员)",
|
|
21
|
+
resolver_fault: "per-principal caps resolver 抛错/答非记录(core 同形全拒:autoMode:false + onError phase config)",
|
|
22
|
+
};
|
|
23
|
+
function armingProbeFailure(what) {
|
|
24
|
+
return new Error(`auto-mode arming probe cannot decide (${what}). This probe answers ONE question — does the INSTALLED @sema-agent/core arm ` +
|
|
25
|
+
`auto mode from the task's INTENT seat (core #521: spec.autoModeRequested ∧ seat ∧ caps !== false), or from the older ` +
|
|
26
|
+
`"org grants it" polarity (runtimeCaps.autoMode === true ∧ seat)? The two polarities are OPPOSITE on the permission axis, ` +
|
|
27
|
+
`so guessing would silently put this deployment on the wrong one — either arming runs the org never granted, or reporting ` +
|
|
28
|
+
`"not armed" at /v1/capabilities while the engine really is arming (#157: no silent fail-open on the permission axis). ` +
|
|
29
|
+
`Refusing to start instead. Fix: reinstall the engine (rm -rf node_modules && npm ci) so the package ships its full dist, ` +
|
|
30
|
+
`or — if core moved/renamed the arming site — update the probe in src/auto-mode-face.ts to read the new one.`);
|
|
31
|
+
}
|
|
32
|
+
export function coreArmsAutoOnIntentProbe(distDir) {
|
|
33
|
+
const dist = distDir ?? dirname(createRequire(import.meta.url).resolve("@sema-agent/core"));
|
|
34
|
+
const armingSite = join(dist, "core", "runner", "prepare-task.js");
|
|
35
|
+
let armingSrc;
|
|
36
|
+
try {
|
|
37
|
+
armingSrc = readFileSync(armingSite, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw armingProbeFailure(`the engine's arming site is unreadable at ${armingSite}: ${err instanceof Error ? err.message : String(err)}`);
|
|
41
|
+
}
|
|
42
|
+
if (armingSrc.includes(CORE_AUTO_MODE_INTENT_SEAT))
|
|
43
|
+
return true;
|
|
44
|
+
const snapPath = join(dist, "..", "test", "export-surface.snapshot.json");
|
|
45
|
+
let snap;
|
|
46
|
+
try {
|
|
47
|
+
const parsed = ExportSurfaceSnapshotSchema.safeParse(JSON.parse(readFileSync(snapPath, "utf8")));
|
|
48
|
+
if (!parsed.success)
|
|
49
|
+
throw new Error(`export-surface snapshot shape mismatch: ${parsed.error.issues.map((i) => i.path.join(".") + ":" + i.message).join("; ")}`);
|
|
50
|
+
snap = parsed.data;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
throw armingProbeFailure(`the arming seat is absent from ${armingSite} and the corroborating export-surface snapshot is unreadable at ${snapPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
54
|
+
}
|
|
55
|
+
if (!snap.exports || typeof snap.exports !== "object")
|
|
56
|
+
throw armingProbeFailure(`core export-surface snapshot malformed at ${snapPath}`);
|
|
57
|
+
if (typeof snap.exports.mailboxCrossProcessMountVerdict === "string") {
|
|
58
|
+
throw armingProbeFailure(`contradiction — the installed core's export surface is at/after the batch that shipped #521, but its arming site ` +
|
|
59
|
+
`(${armingSite}) never names \`${CORE_AUTO_MODE_INTENT_SEAT}\`, so the engine either moved the arming site or renamed the seat`);
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const CORE_AUTO_MODE_INTENT_SEAT = "autoModeRequested";
|
|
64
|
+
let armsOnIntentMemo;
|
|
65
|
+
const ExportSurfaceSnapshotSchema = z.object({ exports: z.record(z.string(), z.string()).optional() }).passthrough();
|
|
66
|
+
export function coreArmsAutoOnIntent() {
|
|
67
|
+
if (armsOnIntentMemo === undefined)
|
|
68
|
+
armsOnIntentMemo = coreArmsAutoOnIntentProbe();
|
|
69
|
+
return armsOnIntentMemo;
|
|
70
|
+
}
|
|
71
|
+
export async function judgeAutoModeArming(face, input) {
|
|
72
|
+
const route = face?.seatMounted ? face.classifierModel?.() : undefined;
|
|
73
|
+
const model = route !== undefined ? { model: route } : {};
|
|
74
|
+
if (face?.engineArmsOnIntent && input.requestedMode !== undefined && input.requestedMode !== "auto")
|
|
75
|
+
return { armed: false, reason: "mode_not_auto", ...model };
|
|
76
|
+
if (face === undefined || !face.seatMounted)
|
|
77
|
+
return { armed: false, reason: "deployment_incapable" };
|
|
78
|
+
let caps;
|
|
79
|
+
try {
|
|
80
|
+
caps = face.resolveCaps !== undefined ? await face.resolveCaps(input.principal) : undefined;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return { armed: false, reason: "resolver_fault", ...model, fault: err instanceof Error ? err.message : String(err) };
|
|
84
|
+
}
|
|
85
|
+
const deniedWord = input.localDeny ? "local_denied" : "org_denied";
|
|
86
|
+
if (face.engineArmsOnIntent) {
|
|
87
|
+
if (caps?.autoMode === false)
|
|
88
|
+
return { armed: false, reason: deniedWord, ...model };
|
|
89
|
+
return { armed: true, ...model };
|
|
90
|
+
}
|
|
91
|
+
if (caps?.autoMode === true)
|
|
92
|
+
return { armed: true, ...model };
|
|
93
|
+
if (caps?.autoMode === false)
|
|
94
|
+
return { armed: false, reason: deniedWord, ...model };
|
|
95
|
+
if (!input.entitlementSource)
|
|
96
|
+
return { armed: false, reason: "deployment_incapable", ...model };
|
|
97
|
+
return { armed: false, reason: "org_denied", ...model };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=auto-mode-face.js.map
|
package/dist/bench/s1/arms.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { leafBudgetFields } from "./runner-ctx.js";
|
|
1
|
+
import { leafBudgetFields, s1DebugGit } from "./runner-ctx.js";
|
|
2
2
|
import {} from "./tasks.js";
|
|
3
3
|
import { decideApproval, decidePlan } from "./reviewer.js";
|
|
4
4
|
import { oracleTrulyCorrect, pickOracleVerdicts, armTag, S1_SCHEMA_VERSION, S1_CORE_VERSION, } from "./row.js";
|
|
@@ -164,7 +164,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
164
164
|
vr = await deps.runWithVerification(implSpec);
|
|
165
165
|
}
|
|
166
166
|
catch (e) {
|
|
167
|
-
if (
|
|
167
|
+
if (s1DebugGit())
|
|
168
168
|
console.error("[S1 SOLO infra throw]", e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
169
169
|
return assembleRow({
|
|
170
170
|
arm, trap, seed, ctx,
|
|
@@ -176,7 +176,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
176
176
|
finishedAt: ctx.clock.now(),
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
|
-
if (
|
|
179
|
+
if (s1DebugGit())
|
|
180
180
|
console.error("[S1 SOLO vr.status]", vr.status, "| stats:", JSON.stringify(vr.stats)?.slice(0, 200));
|
|
181
181
|
const oracle = pickOracleVerdicts(await deps.runOracle());
|
|
182
182
|
return assembleRow({
|
|
@@ -210,7 +210,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
catch (e) {
|
|
213
|
-
if (
|
|
213
|
+
if (s1DebugGit())
|
|
214
214
|
console.error(`[S1 SUP infra throw driver=${trap.supDriver}]`, e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
215
215
|
return assembleRow({
|
|
216
216
|
arm, trap, seed, ctx,
|
|
@@ -222,7 +222,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
222
222
|
finishedAt: ctx.clock.now(),
|
|
223
223
|
});
|
|
224
224
|
}
|
|
225
|
-
if (
|
|
225
|
+
if (s1DebugGit())
|
|
226
226
|
console.error(`[S1 SUP vr.status driver=${trap.supDriver}]`, vr.status, "| repairTerminal:", repairTerminal, "| verdict:", vr.verification?.verdict, "| unverifiedReason:", vr.verification?.unverifiedReason, "| checkpointGate:", JSON.stringify(vr.checkpointGate), "| error:", vr.error, "| stats:", JSON.stringify(vr.stats)?.slice(0, 180));
|
|
227
227
|
const oracle = pickOracleVerdicts(await deps.runOracle());
|
|
228
228
|
const runStatus = classifyRunStatus({ status: vr.status, repairTerminal });
|
|
@@ -7,7 +7,7 @@ import { createDurableAskPolicy } from "../../approval.js";
|
|
|
7
7
|
import { createLeaderRunner } from "../../leader/wire.js";
|
|
8
8
|
import { runOracle as runStandaloneOracle } from "./oracle.js";
|
|
9
9
|
import { makeRepairOracle } from "./repair-oracle-adapter.js";
|
|
10
|
-
import { assertDistinctEnvs } from "./runner-ctx.js";
|
|
10
|
+
import { assertDistinctEnvs, s1DebugGit } from "./runner-ctx.js";
|
|
11
11
|
const W = "/home/user";
|
|
12
12
|
const REPO = `${W}/repo`;
|
|
13
13
|
const BASE_REF = "s1-base";
|
|
@@ -112,7 +112,7 @@ async function transferWorkerTreeToGrader(worker, grader) {
|
|
|
112
112
|
const ext = await sh(grader)(`set -e; test -s /tmp/s1-wt.tar; rm -rf ${REPO}; mkdir -p ${REPO}; tar -xf /tmp/s1-wt.tar -C ${REPO}; echo "EXTRACTED=$(ls -A ${REPO} | wc -l)"`);
|
|
113
113
|
if (ext.code !== 0 || !ext.out.includes("EXTRACTED="))
|
|
114
114
|
throw new Error(`s1 grader tar extract failed: ${ext.out.slice(-400)}`);
|
|
115
|
-
if (
|
|
115
|
+
if (s1DebugGit())
|
|
116
116
|
console.error(`[S1_DEBUG_TAR] head=${head.slice(0, 8)} base=${base.slice(0, 8)} tarBytes=${buf.length} ${ext.out.slice(-60)}`);
|
|
117
117
|
return { head, base };
|
|
118
118
|
}
|
|
@@ -141,7 +141,7 @@ async function importBundleBinaryToGrader(grader, tarBuffer, shas) {
|
|
|
141
141
|
const ext = await sh(grader)(`set -e; test -s /tmp/s1-wt.tar; rm -rf ${REPO}; mkdir -p ${REPO}; tar -xf /tmp/s1-wt.tar -C ${REPO}; echo "EXTRACTED=$(ls -A ${REPO} | wc -l)"`);
|
|
142
142
|
if (ext.code !== 0 || !ext.out.includes("EXTRACTED="))
|
|
143
143
|
throw new Error(`s1 grader tar extract failed: ${ext.out.slice(-400)}`);
|
|
144
|
-
if (
|
|
144
|
+
if (s1DebugGit())
|
|
145
145
|
console.error(`[S1_DEBUG_TAR team] head=${shas.head.slice(0, 8)} base=${shas.base.slice(0, 8)} ${ext.out.slice(-60)}`);
|
|
146
146
|
return shas;
|
|
147
147
|
}
|
|
@@ -196,7 +196,7 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
|
|
|
196
196
|
state.shas = await transferWorkerTreeToGrader(worker, grader);
|
|
197
197
|
const task = { ...oracleTaskFromTrap(trap), ...(state.shas ? { deliveredShas: state.shas } : {}) };
|
|
198
198
|
const v = await runStandaloneOracle(grader, graderTransport(grader), worker, task);
|
|
199
|
-
if (
|
|
199
|
+
if (s1DebugGit())
|
|
200
200
|
console.error(`[S1_DEBUG_ORACLE ${trap.id}] delivered=${v.delivered} tests=${v.hiddenTestsGreen} build=${v.buildPassed} inv=${v.invariantsOk} | raw=`, JSON.stringify(v.raw)?.slice(0, 700));
|
|
201
201
|
return v;
|
|
202
202
|
};
|
|
@@ -250,27 +250,27 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
|
|
|
250
250
|
execFileSync("git", ["clone", "-q", "--branch", branch, remoteUrl, cloneDir], { stdio: ["ignore", "ignore", "pipe"] });
|
|
251
251
|
}
|
|
252
252
|
catch (e1) {
|
|
253
|
-
if (
|
|
253
|
+
if (s1DebugGit())
|
|
254
254
|
console.error(`[S1_DEBUG_IMPORT] cp clone --branch ${branch} failed: ${e1.stderr?.toString().slice(-200)}; retrying default`);
|
|
255
255
|
execFileSync("git", ["clone", "-q", remoteUrl, cloneDir], { stdio: ["ignore", "ignore", "pipe"] });
|
|
256
256
|
}
|
|
257
257
|
const baseHash = execFileSync("git", ["-C", cloneDir, "rev-list", "--max-parents=0", "HEAD"]).toString().trim().split("\n").pop().trim();
|
|
258
258
|
const head = execFileSync("git", ["-C", cloneDir, "rev-parse", "HEAD"]).toString().trim();
|
|
259
259
|
if (!/^[0-9a-f]{40}$/.test(baseHash) || head === baseHash) {
|
|
260
|
-
if (
|
|
260
|
+
if (s1DebugGit())
|
|
261
261
|
console.error(`[S1_DEBUG_IMPORT] cp integrated tree not delivered: head=${head} base=${baseHash}`);
|
|
262
262
|
return false;
|
|
263
263
|
}
|
|
264
264
|
const bundlePath = join(tmp, "team.bundle");
|
|
265
265
|
execFileSync("git", ["-C", cloneDir, "archive", "--format=tar", "-o", bundlePath, "HEAD"], { stdio: ["ignore", "ignore", "pipe"] });
|
|
266
266
|
const buffer = readFileSync(bundlePath);
|
|
267
|
-
if (
|
|
267
|
+
if (s1DebugGit())
|
|
268
268
|
console.error(`[S1_DEBUG_IMPORT] cp integrated head=${head} base=${baseHash} tar bytes=${buffer.length}`);
|
|
269
269
|
state.teamBundle = { buffer, head, base: baseHash };
|
|
270
270
|
return true;
|
|
271
271
|
}
|
|
272
272
|
catch (e) {
|
|
273
|
-
if (
|
|
273
|
+
if (s1DebugGit())
|
|
274
274
|
console.error(`[S1_DEBUG_IMPORT] control-plane THREW:`, e instanceof Error ? e.message : String(e));
|
|
275
275
|
return false;
|
|
276
276
|
}
|
|
@@ -361,7 +361,7 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
|
|
|
361
361
|
process.env.LEADER_CONFLICT_ROUNDS = process.env.LEADER_CONFLICT_ROUNDS ?? "2";
|
|
362
362
|
const run = createLeaderRunner({
|
|
363
363
|
brain, models, roles, pricing, e2bApiKey: rt.e2bApiKey, workspace: W,
|
|
364
|
-
...(
|
|
364
|
+
...(s1DebugGit() ? { logger: { warn: (m, x) => console.error(`[S1_DEBUG_LEADER warn] ${m}`, x ? JSON.stringify(x).slice(0, 300) : ""), info: (m, x) => console.error(`[S1_DEBUG_LEADER info] ${m}`, x ? JSON.stringify(x).slice(0, 200) : "") } } : {}),
|
|
365
365
|
});
|
|
366
366
|
const teamSeedWrite = (trap.seedFiles ?? [])
|
|
367
367
|
.map((f) => {
|
|
@@ -415,13 +415,13 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
|
|
|
415
415
|
});
|
|
416
416
|
}
|
|
417
417
|
catch (e) {
|
|
418
|
-
if (
|
|
418
|
+
if (s1DebugGit())
|
|
419
419
|
console.error(`[S1_DEBUG_LEADER ${trap.id}] run() THREW:`, e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
420
420
|
killDaemon();
|
|
421
421
|
rmSync(baseDir, { recursive: true, force: true });
|
|
422
422
|
return { ok: false, reports: [], infraFailed: true };
|
|
423
423
|
}
|
|
424
|
-
if (
|
|
424
|
+
if (s1DebugGit())
|
|
425
425
|
console.error(`[S1_DEBUG_LEADER ${trap.id}] ok=${res.ok} error=${JSON.stringify(res.error)} cancelled=${res.cancelled} merge=${JSON.stringify(res.merge)} repairTerminal=${res.repairTerminal} reports=${(res.reports ?? []).length} reportsDetail=${JSON.stringify((res.reports ?? []).map((r) => ({ w: r.workerId, ok: r.ok, err: r.error })))?.slice(0, 400)}`);
|
|
426
426
|
let imported = false;
|
|
427
427
|
const merged = res.merge?.ok === true;
|
|
@@ -7,6 +7,7 @@ import { makeBenchClock } from "./runner-ctx.js";
|
|
|
7
7
|
import { buildS1Report, S1_SCHEMA_VERSION, S1_CORE_VERSION } from "./row.js";
|
|
8
8
|
import { armTag } from "./row.js";
|
|
9
9
|
import { buildLiveDeps, liveRuntimeConfigFromEnv } from "./live-deps.js";
|
|
10
|
+
import { setS1DebugGit } from "./runner-ctx.js";
|
|
10
11
|
export function enumerateCells(seeds, filter = {}) {
|
|
11
12
|
const cells = [];
|
|
12
13
|
const source = filter.only ? ALL_TRAPS : FIRM_TRAPS;
|
|
@@ -184,6 +185,7 @@ function parseArgs(argv) {
|
|
|
184
185
|
const cellTimeoutMs = positiveIntArg(get("cell-timeout-ms"), 15 * 60_000, "--cell-timeout-ms");
|
|
185
186
|
return {
|
|
186
187
|
dryRun: argv.includes("--dry-run"),
|
|
188
|
+
debugGit: argv.includes("--debug-git"),
|
|
187
189
|
seeds,
|
|
188
190
|
cellTimeoutMs,
|
|
189
191
|
ledgerPath: get("ledger") ?? path.resolve(`./s1-out/${runId}.ledger.jsonl`),
|
|
@@ -234,6 +236,7 @@ function dryRunMockFactory() {
|
|
|
234
236
|
}
|
|
235
237
|
export async function main(argv = process.argv.slice(2)) {
|
|
236
238
|
const args = parseArgs(argv);
|
|
239
|
+
setS1DebugGit(args.debugGit);
|
|
237
240
|
const liveRuntime = liveRuntimeConfigFromEnv();
|
|
238
241
|
if (!args.dryRun && !liveRuntime) {
|
|
239
242
|
console.error("[s1 run-firm] LIVE path refused: E2B_API_KEY + DEEPSEEK_API_KEY are required (the brain gateway). " +
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
* + the clock; a deterministic shape test exercises it with a MOCK runner (no real agents, no keys).
|
|
18
18
|
*/
|
|
19
19
|
import type { ExecutionEnv } from "@sema-agent/core";
|
|
20
|
+
/** 由 bench 入口(`run-firm.ts` 的 `--debug-git`)翻开;不调 = 关。 */
|
|
21
|
+
export declare function setS1DebugGit(on: boolean): void;
|
|
22
|
+
/** tar/import/oracle/leader 四族 stderr 追踪开着没有(读侧单点)。 */
|
|
23
|
+
export declare function s1DebugGit(): boolean;
|
|
20
24
|
/**
|
|
21
25
|
* The budget block the budget-match guard compares. NOT a s1.v1 RawRow field (the contract carries no budget) —
|
|
22
26
|
* it is a PRODUCER-side fairness descriptor (one per emitted cell). `teamWorkerBudgetSumUsd` is the bigger-pie
|