@sema-agent/server 7.71.0 → 7.72.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/USAGE.md +2 -1
- package/dist/approval-card.d.ts +16 -17
- package/dist/approval-card.js +0 -9
- package/dist/boot/runner-deps.d.ts +32 -16
- package/dist/brain.js +2 -9
- package/dist/budget.d.ts +12 -1
- package/dist/budget.js +1 -1
- package/dist/config-catalog.js +12 -12
- package/dist/config-center/apply-effective.js +2 -4
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/restart-signal.js +2 -2
- package/dist/config-center/skills-mcp.js +15 -0
- package/dist/config-types.d.ts +68 -16
- package/dist/config-types.js +19 -1
- package/dist/config.d.ts +42 -0
- package/dist/config.js +58 -21
- package/dist/http/durable-run-leg.d.ts +64 -0
- package/dist/http/durable-run-leg.js +63 -0
- package/dist/http/route-ctx.d.ts +14 -0
- package/dist/http/route-ctx.js +3 -0
- package/dist/http/routes/approvals-assistant.js +3 -3
- package/dist/http/routes/capabilities.js +2 -2
- package/dist/http/routes/runs.js +10 -58
- package/dist/http/routes/workflows.js +1 -0
- package/dist/http/server.js +55 -17
- package/dist/main.js +4 -1
- package/dist/observability/fail-open.d.ts +1 -1
- package/dist/observability/fail-open.js +1 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/tool-trace.d.ts +18 -1
- package/dist/observability/tool-trace.js +4 -3
- package/dist/parked-decide.js +3 -2
- package/dist/plugins/e2b-orphan-reclaim.d.ts +4 -4
- package/dist/plugins/e2b-orphan-reclaim.js +18 -20
- package/dist/plugins/store-contracts.d.ts +27 -0
- package/dist/plugins/store-contracts.js +3 -2
- package/dist/run-cancel-context.js +1 -1
- package/dist/run-local.js +4 -1
- package/dist/runs.d.ts +16 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +5 -3
- package/dist/task-a2a.js +3 -2
- package/dist/task-mcp.js +3 -2
- package/dist/tool-approval.d.ts +26 -21
- package/dist/tool-approval.js +2 -2
- package/dist/trace/core-keyset-guard.d.ts +10 -4
- package/dist/trace/project.d.ts +12 -0
- package/dist/trace/project.js +19 -3
- package/dist/usage-analytics.d.ts +9 -1
- package/dist/usage-analytics.js +8 -0
- package/dist/wire-name-bound.d.ts +14 -0
- package/dist/wire-name-bound.js +13 -0
- package/package.json +2 -2
|
@@ -15,7 +15,8 @@ import type { Metrics } from "./metrics.js";
|
|
|
15
15
|
* (`policy/hook/safety/shellGate/planMode/classifier/org`)换成八词
|
|
16
16
|
* (`policy/hook/org/classifier/plan_mode/compliance/write_protection/ask_resolution`)。仪表盘/告警规则要改。
|
|
17
17
|
*
|
|
18
|
-
* 🔴 **第二个计数器 `
|
|
18
|
+
* 🔴 **第二个计数器 `auto_mode_classifier_deny_cause_total{cause}`(server 7.70.0 生为
|
|
19
|
+
* `auto_mode_classifier_unavailable_total`,core 7.12.0 [ref];**7.72.0 / core 7.14.0 [ref] 改名、换座、换词**,见本段末)**
|
|
19
20
|
* —— 分类器**停摆**的常开信号。7.12.0 之前这件事有两条可观测路:熔断跳闸时的
|
|
20
21
|
* `auto_mode_breaker_open_total`,以及「`unavailable` 的 ask 会走到人面前」(审批 inbox 里看得见)。
|
|
21
22
|
* 该版把熔断族整族退役、又把 `unavailable` 改成**当场 deny** ⇒ 两条路同时没了,而
|
|
@@ -26,6 +27,22 @@ import type { Metrics } from "./metrics.js";
|
|
|
26
27
|
* 两个计数刻意**不合并成一个标签**:它们答两个问题(「谁拒的」vs「这次拒是不是因为分类器停摆、哪一类」),
|
|
27
28
|
* 而运维对停摆要立刻动手(改配置 / 等重试 / 看后端),对安全拦截不要。
|
|
28
29
|
* ⛔ 纯计量:不改任何裁决、不新开观察面、事实缺席时**不铸**(缺席 ≠ 「分类器好着呢」,只是这次拒不是它)。
|
|
30
|
+
*
|
|
31
|
+
* 🔴 **7.72.0(core 7.14.0 [ref] C-a)—— 同一只计数器换了名、换了座、也换了词表,运维面 BREAKING**:
|
|
32
|
+
* · **名**(合并复审 R2):`auto_mode_classifier_unavailable_total` ⇒ `auto_mode_classifier_deny_cause_total`,
|
|
33
|
+
* **无别名** —— 一只也数 `parse_error`(分类器跑了、答得出格)的计数器再叫 unavailable 就是名不副实;
|
|
34
|
+
* 与下面两条是同一次运维 BREAKING,只痛一次。
|
|
35
|
+
* · **座**:`payload.classifierUnavailable.cause`(整族退役)⇒ `payload.gate.disposition.cause`。同一条
|
|
36
|
+
* 门记录的三张脸(`tool_end.gate` / 本观察者的 `payload.gate` / 行的 `resolvedOutcome`)core 一次铸、
|
|
37
|
+
* 三处投,所以这一位与 `deniedBy` 现在**读自同一只对象**——两个标签不可能再各说各话。
|
|
38
|
+
* · **词表**:`AUTO_MODE_UNAVAILABLE_CAUSES`(`error` / `timeout` = 分类器**为什么**没跑成)
|
|
39
|
+
* ⇒ `CLASSIFIER_DENY_CAUSES`(`unavailable` / `parse_error` = **这次 deny 是什么形**)。
|
|
40
|
+
* ⚠️ 两代不是同一个问题的两组词:`unavailable` 覆盖了旧的 error+timeout 两格(「为什么」降级成散文,
|
|
41
|
+
* 只在 deny 句与 `auto_mode.classified` 诊断帧上),而 `parse_error`(分类器跑了、但答得出格 ⇒ 为安全
|
|
42
|
+
* 起见拦下)是**旧计数器根本数不到**的一格。看板/告警的 `cause=` 过滤要改,按旧词 by-cause 分组的
|
|
43
|
+
* 历史序列与新序列**不可直接拼**。
|
|
44
|
+
* · **计数总量方向**:新座只在「分类器那一轮没有裁决」时在场(`<block>yes` 的真裁决恒缺席),与旧座的
|
|
45
|
+
* 「分类器停摆」同向;多出来的那一格是 `parse_error`,它也确实是「分类器没正常工作」的一种。
|
|
29
46
|
*/
|
|
30
47
|
export declare function createPermissionDeniedMeter(metrics: Pick<Metrics, "inc">): Hooks;
|
|
31
48
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isClassifierDenyCause, 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,9 +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
|
|
11
|
+
const disposition = payload.gate?.disposition;
|
|
12
|
+
const cause = disposition?.kind === "denied" ? disposition.cause : undefined;
|
|
12
13
|
if (cause !== undefined) {
|
|
13
|
-
metrics.inc("
|
|
14
|
+
metrics.inc("auto_mode_classifier_deny_cause_total", { cause: isClassifierDenyCause(cause) ? cause : "other" });
|
|
14
15
|
}
|
|
15
16
|
},
|
|
16
17
|
};
|
package/dist/parked-decide.js
CHANGED
|
@@ -132,8 +132,9 @@ export function planWorkflowParkedDecide(origin, req) {
|
|
|
132
132
|
runId: origin.runId,
|
|
133
133
|
outcome: buildParkedResumeOutcome(req),
|
|
134
134
|
message: `The human approval that workflow run "${origin.runId}" parked on (agent callKey "${origin.agent.callKey}") has been ${verdict}. ` +
|
|
135
|
-
`Resume that run now
|
|
136
|
-
`
|
|
135
|
+
`Resume that run now: re-invoke Workflow with the SAME script source you launched it from (script / name / scriptPath) ` +
|
|
136
|
+
`AND resumeFromRunId: "${origin.runId}" — the decision is already held by the deployment and will be applied to the parked ` +
|
|
137
|
+
`agent; completed steps replay from the journal. Do not re-issue the original call.`,
|
|
137
138
|
};
|
|
138
139
|
}
|
|
139
140
|
export async function decideParkedAgent(deps, req, match) {
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* memory `capability-probe-fail-direction`;误杀的代价是杀死一个活任务的手,漏杀的代价只是钱):
|
|
11
11
|
* a. `metadata.semaManaged !== "1"` → 非我方铸造,provider 侧 query 过滤已排除;防御性重验。
|
|
12
12
|
* b. `metadata.taskId` 缺席 → 病态形(factory 恒打标),不杀 + failOpen 留痕。
|
|
13
|
-
* c. run
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* c. run 行在场(词 → 类问 `RUN_STATUS_CLASS` 属主,S-221):
|
|
14
|
+
* 类 live / parked(行还能再动) → 活,跳过;
|
|
15
|
+
* 类 succeeded / unsuccessful(终局) → 孤儿,kill;
|
|
16
|
+
* 属主表外的词(类 undefined) → 词表纪律([ref]):不杀 + failOpen(新词先安全后收编)。
|
|
17
17
|
* d. 行不存在:
|
|
18
18
|
* startedAt 距今 < GRACE → create→行落库窗口,跳过(防误杀新沙盒);
|
|
19
19
|
* ≥ GRACE → 真孤儿,kill(单库 fleet 方向下行跨副本可见)。
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
2
|
+
import { runStatusClassOf } from "./store-contracts.js";
|
|
2
3
|
export const E2B_ORPHAN_GRACE_MS = 10 * 60 * 1000;
|
|
3
4
|
export const SEMA_MANAGED_METADATA_KEY = "semaManaged";
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const CLASS_VERDICT = {
|
|
6
|
+
live: "live",
|
|
7
|
+
parked: "live",
|
|
8
|
+
succeeded: "kill",
|
|
9
|
+
unsuccessful: "kill",
|
|
10
|
+
};
|
|
8
11
|
export async function reclaimOrphanE2bSandboxes(deps) {
|
|
9
12
|
const now = deps.now ?? Date.now;
|
|
10
13
|
const graceMs = deps.graceMs ?? E2B_ORPHAN_GRACE_MS;
|
|
@@ -29,28 +32,15 @@ export async function reclaimOrphanE2bSandboxes(deps) {
|
|
|
29
32
|
if (run === undefined) {
|
|
30
33
|
verdict = now() - sbx.startedAt.getTime() < graceMs ? "grace" : "kill";
|
|
31
34
|
}
|
|
32
|
-
else if (LIVE_STATUSES.has(run.status)) {
|
|
33
|
-
verdict = "live";
|
|
34
|
-
}
|
|
35
|
-
else if (TERMINAL_STATUSES.has(run.status)) {
|
|
36
|
-
verdict = "kill";
|
|
37
|
-
}
|
|
38
35
|
else {
|
|
39
|
-
|
|
36
|
+
const cls = runStatusClassOf(run.status);
|
|
37
|
+
verdict = cls === undefined ? "unknown" : CLASS_VERDICT[cls];
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
catch {
|
|
43
41
|
verdict = "unknown";
|
|
44
42
|
}
|
|
45
|
-
if (verdict === "
|
|
46
|
-
summary.skippedLive++;
|
|
47
|
-
else if (verdict === "grace")
|
|
48
|
-
summary.skippedGrace++;
|
|
49
|
-
else if (verdict === "unknown") {
|
|
50
|
-
summary.skippedUnknown++;
|
|
51
|
-
recordFailOpen("server.e2b.orphan-skip-unknown");
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
43
|
+
if (verdict === "kill") {
|
|
54
44
|
try {
|
|
55
45
|
await deps.provider.kill(sbx.sandboxId);
|
|
56
46
|
summary.reclaimed++;
|
|
@@ -61,6 +51,14 @@ export async function reclaimOrphanE2bSandboxes(deps) {
|
|
|
61
51
|
deps.logger?.warn("e2b_orphan_kill_failed", { sandboxId: sbx.sandboxId, taskId, error: e instanceof Error ? e.message : String(e) });
|
|
62
52
|
}
|
|
63
53
|
}
|
|
54
|
+
else if (verdict === "live")
|
|
55
|
+
summary.skippedLive++;
|
|
56
|
+
else if (verdict === "grace")
|
|
57
|
+
summary.skippedGrace++;
|
|
58
|
+
else {
|
|
59
|
+
summary.skippedUnknown++;
|
|
60
|
+
recordFailOpen("server.e2b.orphan-skip-unknown");
|
|
61
|
+
}
|
|
64
62
|
}
|
|
65
63
|
if (summary.reclaimed > 0 || summary.killFailed > 0) {
|
|
66
64
|
deps.logger?.info("e2b_orphan_reclaim_round", { ...summary });
|
|
@@ -147,6 +147,33 @@ export declare const RUN_STATUS_CLASS: {
|
|
|
147
147
|
export type UnsuccessfulRunStatus = {
|
|
148
148
|
[K in RunRecord["status"]]: (typeof RUN_STATUS_CLASS)[K] extends "unsuccessful" ? K : never;
|
|
149
149
|
}[RunRecord["status"]];
|
|
150
|
+
/**
|
|
151
|
+
* 一个状态词的类;**表里没有的词 ⇒ `undefined`**。
|
|
152
|
+
*
|
|
153
|
+
* 🔴 形参是 `string` 而不是 `RunRecord["status"]`(S-221):本函数的**全部工作**就是「这个词在不在表里」,
|
|
154
|
+
* 把入参收成闭集等于在签名上宣称「传进来的一定是表内词」,而下一行的 `undefined` 臂正是为了不是的那些 ——
|
|
155
|
+
* 签名与实现自相矛盾时,消费点只能靠 `as RunRecord["status"]` 硬转把窄读口(`{ status: string }`,
|
|
156
|
+
* 三形 store 共同面)喂进来,而那正是本文件头注点名的 `rowToRun` 裸 cast 的同一条病。闭集词照样能传
|
|
157
|
+
* (联合 ⊂ string),编译期的执法点在上面那张表的双向钉,不在这里。
|
|
158
|
+
*
|
|
159
|
+
* 导出面(S-221):`plugins/e2b-orphan-reclaim.ts` 的三臂判决(还能再动 / 终局 / 读不懂)需要的正是
|
|
160
|
+
* 「类 + unknown」这一对,而不是三条 boolean 谓词各问一遍 —— 它此前因此自带了第二份词表。
|
|
161
|
+
*
|
|
162
|
+
* 🔴 查的是 **null 原型**的那只表,不是 `RUN_STATUS_CLASS` 本身(codex 对抗复审 r1 [high],亲验后修):
|
|
163
|
+
* 普通对象字面量的查表**穿原型链** —— `status` 落在 `constructor` / `toString` / `__proto__` /
|
|
164
|
+
* `hasOwnProperty` / `valueOf` 上时查到的是 `Object.prototype` 的成员而不是 `undefined`,于是
|
|
165
|
+
* 「读不懂的词」这一臂被整条绕过。亲验读数(修前):`runStatusClassOf("constructor")` 返回
|
|
166
|
+
* `function Object`。这类值是**真可达**的(`rowToRun` 把无约束的 SQL 文本列裸 cast 成本联合),而消费点
|
|
167
|
+
* 据此做的是不可逆动作。修在此处一次,而不是在每个消费点各写一句 `Object.hasOwn` —— 后者要靠每个消费点
|
|
168
|
+
* 各记得一次,属主只有一个。
|
|
169
|
+
*
|
|
170
|
+
* 🔴 运行期未知词是**真可达**的([ref] codex R1 真缺陷,红先复现过):`plugins/run-store-sql.ts` 的
|
|
171
|
+
* `rowToRun` 把无约束的 SQL 文本列**裸 cast** 成本联合,滚动升级里一个更新的副本写入的新状态词会被
|
|
172
|
+
* 旧副本读到。三张派生判据对 `undefined` 一律答 `false`(保守方向,逐条理由见各自头注)——不是静默
|
|
173
|
+
* 兜底,而是「本进程读不懂这个词,少动一次远好过错杀」的显式表态;真正的执法点是上面那张表的编译期
|
|
174
|
+
* 双向钉,新词进词表的**那一刻**就红。
|
|
175
|
+
*/
|
|
176
|
+
export declare function runStatusClassOf(status: string): RunStatusClass | undefined;
|
|
150
177
|
/**
|
|
151
178
|
* 「这一行已经走到**终局**了吗」——终局 = 成功 or 非成功(completed / failed / blocked);
|
|
152
179
|
* 非终局 = running / suspended / needs_review(后两者都还能再动)。
|
|
@@ -7,8 +7,9 @@ export const RUN_STATUS_CLASS = {
|
|
|
7
7
|
suspended: "parked",
|
|
8
8
|
needs_review: "parked",
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const RUN_STATUS_CLASS_LOOKUP = Object.assign(Object.create(null), RUN_STATUS_CLASS);
|
|
11
|
+
export function runStatusClassOf(status) {
|
|
12
|
+
return RUN_STATUS_CLASS_LOOKUP[status];
|
|
12
13
|
}
|
|
13
14
|
export function isTerminalRunStatus(status) {
|
|
14
15
|
const cls = runStatusClassOf(status);
|
|
@@ -30,7 +30,7 @@ export function mintCancelledResult(args) {
|
|
|
30
30
|
sessionId: args.sessionId,
|
|
31
31
|
terminal: { kind: "failed", code: CANCELLED_CODE, message: args.message },
|
|
32
32
|
result: "",
|
|
33
|
-
stats: { turns: 0, tokens: 0 },
|
|
33
|
+
stats: { turns: 0, tokens: 0, usageMissing: true },
|
|
34
34
|
...(ctx ? { cancelContext: ctx } : {}),
|
|
35
35
|
};
|
|
36
36
|
}
|
package/dist/run-local.js
CHANGED
|
@@ -15,7 +15,7 @@ import { ForkRoutingSessionStore } from "./plugins/fork-routing-session-store.js
|
|
|
15
15
|
import { taskWallClockSec } from "./task-workflow.js";
|
|
16
16
|
import { applyCatalogToSource } from "./capabilities/center-prompts.js";
|
|
17
17
|
import { validatePromptsDomain, CORE_ENGINE_VERSION } from "./prompts-domain-validate.js";
|
|
18
|
-
import { loadConfig, logConfigDiagnostics, parseNumOrFailNonNegative } from "./config.js";
|
|
18
|
+
import { buildUnpricedCeilingNotice, loadConfig, logConfigDiagnostics, parseNumOrFailNonNegative } from "./config.js";
|
|
19
19
|
import { createConfigProvider } from "./config-provider.js";
|
|
20
20
|
import { applyEffective, applyCenterReadFace, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./config-center/facade.js";
|
|
21
21
|
import { hostExecutionEnvFactory } from "./plugins/remote-env-host.js";
|
|
@@ -198,6 +198,9 @@ export async function runLocal(argv, deps = {}) {
|
|
|
198
198
|
catch (err) {
|
|
199
199
|
logger.warn("config_source_unreachable_using_env", { source: configProvider.kind, err: String(err) });
|
|
200
200
|
}
|
|
201
|
+
const unpricedCeiling = buildUnpricedCeilingNotice(config.models, config);
|
|
202
|
+
if (unpricedCeiling)
|
|
203
|
+
logger.warn(unpricedCeiling.event, unpricedCeiling.fields);
|
|
201
204
|
try {
|
|
202
205
|
const fileRx = await loadRemoteExec(root);
|
|
203
206
|
if (fileRx?.provider === "host") {
|
package/dist/runs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defaultTaskRegistry, type CascadeConfig, type CheckpointToken, type Runner, type SubagentSteerHandle, type TaskResult, type TaskSpec, type TaskStream, type TerminalCause } from "@sema-agent/core";
|
|
1
|
+
import { defaultTaskRegistry, type CascadeConfig, type CheckpointToken, type RunInternals, type Runner, type SubagentSteerHandle, type TaskResult, type TaskSpec, type TaskStream, type TerminalCause } from "@sema-agent/core";
|
|
2
2
|
import type { SendUserFileEmitter } from "./capabilities/send-user-file-tool.js";
|
|
3
3
|
import type { RunStore } from "./plugins/store-backend.js";
|
|
4
4
|
import type { Metrics } from "./observability/metrics.js";
|
|
@@ -282,7 +282,21 @@ pendingEngineNotices?: readonly {
|
|
|
282
282
|
message: string;
|
|
283
283
|
detail?: Record<string, unknown>;
|
|
284
284
|
sessionId?: unknown;
|
|
285
|
-
}[]
|
|
285
|
+
}[],
|
|
286
|
+
/**
|
|
287
|
+
* 🔴 **S-218:部署方铸的 TRUSTED run internals**(今天唯一成员 `workflowParkedResume`)。
|
|
288
|
+
*
|
|
289
|
+
* 它**不是** `TaskSpec` 字段、不是工具参数、不进请求体 —— 模型永远不能自己决定一次审批。铸点只有
|
|
290
|
+
* 一个:`/decide` 的 workflow 第三条车道(判据属主 = `parked-decide.ts` 的 `planWorkflowParkedDecide`
|
|
291
|
+
* 顶注 + `http/durable-run-leg.ts` 的 `TrustedRunInternals`)。本腿只负责把它摊进**下面那只**共享
|
|
292
|
+
* 供给座席,core 的 `prepareTask` 据它给 Workflow 工具挂 `parkedResume` dep。
|
|
293
|
+
*
|
|
294
|
+
* 位置在座席对象的**最后**:座席里那几位(onActivity/onForwardEvent/onTaskNotification/onSubagentSpawn)
|
|
295
|
+
* 是本腿自己的执行面供给,调用方无权覆盖它们 —— 真要撞名,撞的也只能是本腿自己新加的位,而那时
|
|
296
|
+
* `TrustedRunInternals` 的白名单会在编译期先红一次(它只开部署方可以决定的那一格)。
|
|
297
|
+
* 缺席 ⇒ 座席与本位存在之前逐字相同。
|
|
298
|
+
*/
|
|
299
|
+
trustedInternals?: Pick<RunInternals, "workflowParkedResume">): Promise<void>;
|
|
286
300
|
/** How often a running instance refreshes its run's updated_at (liveness, independent of events).
|
|
287
301
|
* Must stay strictly below `runStaleSec` (asserted at startup AND on every hot config candidate) or the
|
|
288
302
|
* reaper would race live runs. [ref]:数值的单源是 `config-invariants.ts`(那条不变量的属主),两个消费面
|
package/dist/runs.js
CHANGED
|
@@ -160,7 +160,7 @@ export function evictIfConflict(runner, sessionId, result) {
|
|
|
160
160
|
runner.sessions.evict?.(sessionId);
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
-
export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, parkCheckpointReader, pendingEngineNotices) {
|
|
163
|
+
export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, parkCheckpointReader, pendingEngineNotices, trustedInternals) {
|
|
164
164
|
const startedAt = Date.now();
|
|
165
165
|
metrics?.addGauge("runs_active", 1);
|
|
166
166
|
fleetPublisher?.onStart();
|
|
@@ -311,6 +311,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
311
311
|
},
|
|
312
312
|
}
|
|
313
313
|
: {}),
|
|
314
|
+
...(trustedInternals ?? {}),
|
|
314
315
|
};
|
|
315
316
|
const statusSeat = {
|
|
316
317
|
onStatusEvent: (e) => {
|
|
@@ -45,6 +45,8 @@ export function toCoreRuntimeCaps(ent) {
|
|
|
45
45
|
caps.allowObservers = ent.allowObservers;
|
|
46
46
|
if (typeof autoMode === "boolean")
|
|
47
47
|
caps.autoMode = autoMode;
|
|
48
|
+
if (caps.autoMode === false)
|
|
49
|
+
caps.autoModeDenySource = "org";
|
|
48
50
|
if (typeof ent.allowMemoryOptOut === "boolean")
|
|
49
51
|
caps.allowMemoryOptOut = ent.allowMemoryOptOut;
|
|
50
52
|
return Object.keys(caps).length ? caps : undefined;
|
|
@@ -142,7 +144,7 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
142
144
|
catch {
|
|
143
145
|
e = new Error("center fetch failed with an unstringifiable rejection reason");
|
|
144
146
|
}
|
|
145
|
-
const entry = { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, expiresAt: now() + denyTtlMs };
|
|
147
|
+
const entry = { caps: { allowWorkflows: false, allowFork: false, autoMode: false, autoModeDenySource: "org" }, expiresAt: now() + denyTtlMs };
|
|
146
148
|
if (!recordDeny)
|
|
147
149
|
readMintedDeny.add(entry);
|
|
148
150
|
putCache(principal, entry);
|
|
@@ -231,7 +233,7 @@ export function applyLocalAutoModeDeny(base, deny) {
|
|
|
231
233
|
if (!deny)
|
|
232
234
|
return base;
|
|
233
235
|
if (base === undefined)
|
|
234
|
-
return () => ({ autoMode: false });
|
|
235
|
-
return async (principal) => ({ ...(await base(principal)), autoMode: false });
|
|
236
|
+
return () => ({ autoMode: false, autoModeDenySource: "local" });
|
|
237
|
+
return async (principal) => ({ ...(await base(principal)), autoMode: false, autoModeDenySource: "local" });
|
|
236
238
|
}
|
|
237
239
|
//# sourceMappingURL=runtime-caps-resolver.js.map
|
package/dist/task-a2a.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { resolveComplianceDenies
|
|
1
|
+
import { resolveComplianceDenies } from "@sema-agent/core";
|
|
2
|
+
import { isUsableWireName } from "./wire-name-bound.js";
|
|
2
3
|
import { HttpError } from "./security.js";
|
|
3
4
|
export const MAX_REQUEST_A2A_PEERS = 32;
|
|
4
5
|
export function a2aInjectionHonored(config) {
|
|
@@ -19,7 +20,7 @@ function normalizeA2aPeer(e) {
|
|
|
19
20
|
if (!e || typeof e !== "object")
|
|
20
21
|
return null;
|
|
21
22
|
const o = e;
|
|
22
|
-
if (
|
|
23
|
+
if (!isUsableWireName(o.name))
|
|
23
24
|
return null;
|
|
24
25
|
if (typeof o.url !== "string" || !HTTP_URL_RE.test(o.url))
|
|
25
26
|
return null;
|
package/dist/task-mcp.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { mcpInjectionDroppedNotice, resolveComplianceDenies
|
|
1
|
+
import { mcpInjectionDroppedNotice, resolveComplianceDenies } from "@sema-agent/core";
|
|
2
|
+
import { isUsableWireName } from "./wire-name-bound.js";
|
|
2
3
|
import { HttpError } from "./security.js";
|
|
3
4
|
import { CONTENT_ORIGIN_WORDS, describeBadContentOrigin, isContentOriginWord } from "./mcp-content-origin.js";
|
|
4
5
|
import { readMcpSourceLabel } from "./mcp-source-label.js";
|
|
@@ -26,7 +27,7 @@ function normalizeServer(e, noteBadOrigin) {
|
|
|
26
27
|
if (!e || typeof e !== "object")
|
|
27
28
|
return bad("entry");
|
|
28
29
|
const o = e;
|
|
29
|
-
if (
|
|
30
|
+
if (!isUsableWireName(o.name))
|
|
30
31
|
return bad("name");
|
|
31
32
|
const t = o.transport;
|
|
32
33
|
if (!t || typeof t !== "object")
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -126,11 +126,16 @@ export interface ToolApprovalFrame {
|
|
|
126
126
|
requiresRealApproval?: true;
|
|
127
127
|
/**
|
|
128
128
|
* **S-125③/[ref]**(core 7.5.0;**ADDITIVE**,`"tool_approval"` only)—— 这只 ask 的**出身**:
|
|
129
|
-
* 谁提的这一问,一个 core 闭集词(`ASK_ORIGINS` **11 员**,core 7.
|
|
130
|
-
*
|
|
129
|
+
* 谁提的这一问,一个 core 闭集词(`ASK_ORIGINS` **11 员**,core 7.14.0 现值:content_question /
|
|
130
|
+
* ancestor_marked / org_unavailable / org_rule / rule_store_unavailable / hook / ask_rule /
|
|
131
131
|
* denial_limit_fallback / shell_gate_tighten / safety_tighten / policy)。引擎在门上盖章(core d.ts:
|
|
132
132
|
* 「engine-stamped at the gate, never a policy's claim」),`AskRequest.origin` 逐字透传。
|
|
133
133
|
*
|
|
134
|
+
* ⚠️ **core 7.14.0 [ref] C2 BREAKING**:`unresolvable` → `ancestor_marked`,**无别名**。server 侧零形变
|
|
135
|
+
* (本键一直是 `string` 逐字透传),但**消费端的 switch 要改词**;7.14.0 之前 park 下来、行上存着
|
|
136
|
+
* `unresolvable` 的老记录,在 core 每一道 `isAskOrigin` 筛上自此都是非成员(投影处不回显 ⇒ 本键缺席),
|
|
137
|
+
* 而那一行的 `org` 事实不受影响。
|
|
138
|
+
*
|
|
134
139
|
* 🔴 **echo-only,零消费**:自动车道的资格判据全在 core(`classifierMayAnswer` 的闭表),server
|
|
135
140
|
* **不许**据本键自铸第二张资格表 —— 那是同一语义面两个写者(源头修复纪律),与
|
|
136
141
|
* `denialLimitFallback` 顶注那条「不据 autoDenyAfterMs 自铸第二只定时器」同一条禁令。
|
|
@@ -144,26 +149,26 @@ export interface ToolApprovalFrame {
|
|
|
144
149
|
*/
|
|
145
150
|
origin?: string;
|
|
146
151
|
/**
|
|
147
|
-
* **
|
|
148
|
-
*
|
|
149
|
-
* (`
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* 🔴
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* 🔴 **不枚举词表**(值形是 `
|
|
158
|
-
* core
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
152
|
+
* **[ref] C3**(core 7.14.0;**ADDITIVE**,`"tool_approval"` only)—— `origin: "rule_store_unavailable"`
|
|
153
|
+
* 的**机制判别位**:持久规则车道这一次**读不下来的是哪一半**。core 闭二词
|
|
154
|
+
* (`RULE_STORE_UNREADABLE_KINDS`):`"store"` = 接上的规则店读不了(读失败 / 超时);`"call"` = 店读到了,
|
|
155
|
+
* 但**这条命令**按人的 deny/ask 行读不出来(收紧 lexer 的 `unreadable`:展开里含规则词、引号没闭、语法
|
|
156
|
+
* 错——具体那句在 `message` 里)。`AskRequest.ruleStoreUnreadable` 逐字透传。
|
|
157
|
+
*
|
|
158
|
+
* 🔴 **在场 ⇔ `origin === "rule_store_unavailable"`**(core 顶注逐字)。两词对审批人是两件事:`store`
|
|
159
|
+
* 指向「去看规则店 / 网络」,`call` 指向「这条命令的写法让规则判不了」——一个 origin 词对这两件事同形。
|
|
160
|
+
* 缺席 = 车道把店和这条命令都读通了,**或**本部署没接规则车道;消费端禁把缺席读成对规则店健康的断言。
|
|
161
|
+
*
|
|
162
|
+
* 🔴 **不枚举词表**(值形是 `string` 而不是本仓自铸的联合):词表单一属主在 core
|
|
163
|
+
* (`isRuleStoreUnreadable` 是它的成员判),抄一份会把 core 加的新词吞成缺席 —— 与 {@link origin} 逐字同规。
|
|
164
|
+
*
|
|
165
|
+
* 🔴 **echo-only,零消费**:策略自声明的值在 core 的**唯一**盖章点就被抹掉了,server 不重算、不再筛第二遍,
|
|
166
|
+
* 更不许据本键自铸第二张车道资格表 —— 同 `origin` / `denialLimitFallback` 两条顶注的同一条禁令。
|
|
167
|
+
*
|
|
168
|
+
* ⚰️ 本键的前任 `classifierUnavailable`(S-185 / [ref])随 core 7.14.0 [ref] **整族退役**:引擎那份事实改骑
|
|
169
|
+
* `GateDisposition.denied.cause`(`tool_end.gate` / `permissionDenied.gate`),ask 面自此没有这一位。
|
|
163
170
|
*/
|
|
164
|
-
|
|
165
|
-
cause: string;
|
|
166
|
-
};
|
|
171
|
+
ruleStoreUnreadable?: string;
|
|
167
172
|
/**
|
|
168
173
|
* **E-14**([ref]② Trojan Source 族;**ADDITIVE**,`"tool_approval"` only)—— 这只 ask 的**工具输入**
|
|
169
174
|
* (`AskRequest.args`)里含 bidi 控制符(LRM/RLM、嵌入/覆写、隔离符;字符类属主 `text-bidi.ts`)。
|
package/dist/tool-approval.js
CHANGED
|
@@ -8,7 +8,7 @@ import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerpri
|
|
|
8
8
|
import { createLogger } from "./observability/logger.js";
|
|
9
9
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
10
10
|
import { deriveAskId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
11
|
-
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback,
|
|
11
|
+
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readRuleEvidence, readRuleOffersAbsence, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
|
|
12
12
|
import { projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
|
|
13
13
|
import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
|
|
14
14
|
const defaultLogger = createLogger();
|
|
@@ -844,7 +844,7 @@ export class ToolApprovalCoordinator {
|
|
|
844
844
|
...(ruleLaneMaterial !== undefined ? { ruleOffers: ruleLaneMaterial.offers } : {}),
|
|
845
845
|
...((a) => (a !== undefined ? { ruleOffersAbsence: a } : {}))(readRuleOffersAbsence(req)),
|
|
846
846
|
...((f) => (f !== undefined ? { denialLimitFallback: f } : {}))(readDenialLimitFallback(req)),
|
|
847
|
-
...(
|
|
847
|
+
...(typeof req.ruleStoreUnreadable === "string" && req.ruleStoreUnreadable.length > 0 ? { ruleStoreUnreadable: req.ruleStoreUnreadable } : {}),
|
|
848
848
|
...((c) => (c !== undefined ? { probeCause: c } : {}))(readProbeCause(req)),
|
|
849
849
|
...((c) => (c !== undefined ? { ruleEvidence: c } : {}))(readRuleEvidence(req)),
|
|
850
850
|
...(bounded.omitted ? { argsOmitted: true } : { args: bounded.args }),
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
|
|
18
18
|
* TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
|
|
19
19
|
*/
|
|
20
|
-
import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, Settlement, PausedCause } from "@sema-agent/core";
|
|
20
|
+
import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause } from "@sema-agent/core";
|
|
21
21
|
import type { ToolApprovalFrame } from "../tool-approval.js";
|
|
22
22
|
/** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。
|
|
23
23
|
* 🔴 **导出**([ref] U1/U2):本仓「词表闭集,未处置成员=编译红」的钉法只许有**一个**形 ——
|
|
@@ -43,7 +43,7 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
|
|
|
43
43
|
type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
|
|
44
44
|
type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
|
|
45
45
|
type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
|
|
46
|
-
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "
|
|
46
|
+
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
|
|
47
47
|
type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "probeReason" | "hasBidiControls" | "previewWithheld" | "execCwd";
|
|
48
48
|
type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
|
|
49
49
|
type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
|
|
@@ -103,7 +103,7 @@ type _GuardWiringRulesReverse = AssertAllKeysHandled<Exclude<WiringRulesHandled,
|
|
|
103
103
|
type WiringModelGateHandled = "class" | "removed" | "restore";
|
|
104
104
|
type _GuardWiringModelGate = AssertAllKeysHandled<Exclude<keyof WiringSection<"modelGate">, WiringModelGateHandled>>;
|
|
105
105
|
type _GuardWiringModelGateReverse = AssertAllKeysHandled<Exclude<WiringModelGateHandled, keyof WiringSection<"modelGate">>>;
|
|
106
|
-
type WiringAutoModeHandled = "armed" | "reason";
|
|
106
|
+
type WiringAutoModeHandled = "armed" | "reason" | "deniedSource";
|
|
107
107
|
type _GuardWiringAutoMode = AssertAllKeysHandled<Exclude<keyof WiringSection<"autoMode">, WiringAutoModeHandled>>;
|
|
108
108
|
type _GuardWiringAutoModeReverse = AssertAllKeysHandled<Exclude<WiringAutoModeHandled, keyof WiringSection<"autoMode">>>;
|
|
109
109
|
type WiringGovernanceHandled = "audience" | "lockedConfig" | "compliance" | "memoryAdmission" | "retention";
|
|
@@ -117,7 +117,7 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
|
|
|
117
117
|
/** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
|
|
118
118
|
type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
|
|
119
119
|
type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
|
|
120
|
-
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "
|
|
120
|
+
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
|
|
121
121
|
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
|
|
122
122
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
123
123
|
type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
|
|
@@ -138,6 +138,10 @@ type SettlementWhoKeys = Settlement["who"] extends infer W ? (W extends object ?
|
|
|
138
138
|
type GateOutcomeProjected = "disposition" | "settlement" | "origin";
|
|
139
139
|
type _GuardGateOutcome = AssertAllKeysHandled<Exclude<keyof GateOutcome, GateOutcomeProjected>>;
|
|
140
140
|
type _GuardGateOutcomeReverse = AssertAllKeysHandled<Exclude<GateOutcomeProjected, keyof GateOutcome>>;
|
|
141
|
+
type GateDispositionKeys = GateDisposition extends infer D ? (D extends object ? keyof D : never) : never;
|
|
142
|
+
type GateDispositionProjected = "kind" | "deniedBy" | "cause";
|
|
143
|
+
type _GuardGateDisposition = AssertAllKeysHandled<Exclude<GateDispositionKeys, GateDispositionProjected>>;
|
|
144
|
+
type _GuardGateDispositionReverse = AssertAllKeysHandled<Exclude<GateDispositionProjected, GateDispositionKeys>>;
|
|
141
145
|
type SettlementProjected = "kind" | "who" | "when" | "note";
|
|
142
146
|
type _GuardSettlement = AssertAllKeysHandled<Exclude<SettlementKeys, SettlementProjected>>;
|
|
143
147
|
type _GuardSettlementReverse = AssertAllKeysHandled<Exclude<SettlementProjected, SettlementKeys>>;
|
|
@@ -208,6 +212,8 @@ export type CoreKeysetGuards = [
|
|
|
208
212
|
_GuardPausedCauseReverse,
|
|
209
213
|
_GuardGateOutcome,
|
|
210
214
|
_GuardGateOutcomeReverse,
|
|
215
|
+
_GuardGateDisposition,
|
|
216
|
+
_GuardGateDispositionReverse,
|
|
211
217
|
_GuardSettlement,
|
|
212
218
|
_GuardSettlementReverse,
|
|
213
219
|
_GuardSettlementWho,
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -120,6 +120,18 @@ export declare function toolStartEventData(ev: {
|
|
|
120
120
|
parentToolCallId?: string;
|
|
121
121
|
}): Record<string, unknown>;
|
|
122
122
|
export declare function screenedGate(v: unknown): GateOutcome | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* 🔴 **耐久行的**门记录读口(7.72.0;`screenedGate` 的读腿孪生)—— 先把本仓自己写过的**旧拼法**读成它
|
|
125
|
+
* 今天的名字({@link RETIRED_ASK_ORIGIN_SPELLINGS}),再走与写腿**同一只**筛子。
|
|
126
|
+
*
|
|
127
|
+
* 为什么读腿要多这一步而写腿不要:读到的字节可能是**本仓上一个版本**写下的(账本回放、park 行的
|
|
128
|
+
* `resolvedOutcome`),而 core 7.14.0 [ref] C2 把 `ASK_ORIGINS` 的一个词改了名 —— 不认旧拼法,那些行就整条
|
|
129
|
+
* withhold,连 `disposition` / `settlement`(谁拒的、人怎么结的)一起在回放面上消失。写腿面对的则是**当下**
|
|
130
|
+
* 引擎铸的记录,它铸不出退役词,所以那里出现退役词 = producer 契约漂了 = 要 report + withhold 的事。
|
|
131
|
+
*
|
|
132
|
+
* 不改原对象(调用方与账本共享引用):归一时换一只信封,原记录一个字节不动。
|
|
133
|
+
*/
|
|
134
|
+
export declare function screenedStoredGate(v: unknown): GateOutcome | undefined;
|
|
123
135
|
/** The durable `tool_end` payload (shared by both append sites). E1: the model-facing `output` is UNTRUSTED RAW
|
|
124
136
|
* from core (file/shell/API bodies) — core only SIZE-bounds it (`truncated`), REDACTION is ours (same contract as
|
|
125
137
|
* `tool_start.args`); never re-fed to a model. E2: identity. Absent `output` ⇒ the tool produced no content body.
|
package/dist/trace/project.js
CHANGED
|
@@ -86,6 +86,9 @@ export function toolStartEventData(ev) {
|
|
|
86
86
|
return { toolCallId: ev.toolCallId, toolName: ev.toolName, ...(ev.label !== undefined ? { label: ev.label } : {}), args: redactDeep(ev.args), ...identityFields(ev) };
|
|
87
87
|
}
|
|
88
88
|
const SETTLEMENT_NOTE_MAX = 500;
|
|
89
|
+
const RETIRED_ASK_ORIGIN_SPELLINGS = {
|
|
90
|
+
unresolvable: "ancestor_marked",
|
|
91
|
+
};
|
|
89
92
|
export function screenedGate(v) {
|
|
90
93
|
if (v === null || typeof v !== "object")
|
|
91
94
|
return undefined;
|
|
@@ -96,6 +99,7 @@ export function screenedGate(v) {
|
|
|
96
99
|
return undefined;
|
|
97
100
|
}
|
|
98
101
|
const shapeDefect = (d.kind === "denied" && typeof d.deniedBy !== "string" && "disposition.deniedBy is not a string") ||
|
|
102
|
+
(d.kind === "denied" && d.cause !== undefined && typeof d.cause !== "string" && "disposition.cause is not a string") ||
|
|
99
103
|
(g.settlement !== undefined && (g.settlement === null || typeof g.settlement !== "object") && "settlement is not an object") ||
|
|
100
104
|
(g.settlement !== undefined && g.settlement !== null && typeof g.settlement.kind !== "string" && "settlement.kind is not a string") ||
|
|
101
105
|
(g.origin !== undefined && typeof g.origin !== "string" && "origin is not a string") ||
|
|
@@ -115,11 +119,20 @@ export function screenedGate(v) {
|
|
|
115
119
|
return undefined;
|
|
116
120
|
}
|
|
117
121
|
return {
|
|
118
|
-
disposition: d.kind === "denied"
|
|
122
|
+
disposition: d.kind === "denied"
|
|
123
|
+
? { kind: "denied", deniedBy: d.deniedBy, ...(d.cause === undefined ? {} : { cause: d.cause }) }
|
|
124
|
+
: { kind: "allowed" },
|
|
119
125
|
...(settlement === undefined ? {} : { settlement }),
|
|
120
126
|
...(g.origin === undefined ? {} : { origin: g.origin }),
|
|
121
127
|
};
|
|
122
128
|
}
|
|
129
|
+
export function screenedStoredGate(v) {
|
|
130
|
+
if (v === null || typeof v !== "object")
|
|
131
|
+
return undefined;
|
|
132
|
+
const g = v;
|
|
133
|
+
const renamed = typeof g.origin === "string" ? RETIRED_ASK_ORIGIN_SPELLINGS[g.origin] : undefined;
|
|
134
|
+
return screenedGate(renamed === undefined ? g : { ...g, origin: renamed });
|
|
135
|
+
}
|
|
123
136
|
const SETTLEMENT_PARTIES = new Set(["person", "engine", "host", "none"]);
|
|
124
137
|
function projectSettlement(st) {
|
|
125
138
|
const whoV = st.who;
|
|
@@ -329,7 +342,10 @@ export function wiringManifestEventData(ev) {
|
|
|
329
342
|
const reason = str(autoIn.reason);
|
|
330
343
|
if (armed === undefined || reason === undefined)
|
|
331
344
|
return undefined;
|
|
332
|
-
|
|
345
|
+
if (armed !== (reason === "armed"))
|
|
346
|
+
return undefined;
|
|
347
|
+
const deniedSource = str(autoIn.deniedSource);
|
|
348
|
+
return { armed, reason, ...(deniedSource === undefined ? {} : { deniedSource }) };
|
|
333
349
|
})();
|
|
334
350
|
const toolsIn = m.tools;
|
|
335
351
|
const tools = toolsIn === undefined ? undefined : Value.Check(ToolRoster, toolsIn) ? toolsIn : (recordFailOpen("server.trace.tool-roster-malformed", "wiring_manifest.tools did not fit core ToolRoster"), undefined);
|
|
@@ -514,7 +530,7 @@ export function toolCallFieldsOf(d) {
|
|
|
514
530
|
};
|
|
515
531
|
}
|
|
516
532
|
export function toolResultFieldsOf(d) {
|
|
517
|
-
const gate = d.gate === undefined ? undefined :
|
|
533
|
+
const gate = d.gate === undefined ? undefined : screenedStoredGate(d.gate);
|
|
518
534
|
return {
|
|
519
535
|
callId: String(d.toolCallId ?? ""),
|
|
520
536
|
...(typeof d.label === "string" ? { label: d.label } : {}),
|
|
@@ -26,6 +26,11 @@ export interface UsageRow {
|
|
|
26
26
|
stats?: {
|
|
27
27
|
tokens?: number;
|
|
28
28
|
modelUsage?: Record<string, UsageModelEntry>;
|
|
29
|
+
/** core 7.14.0 [ref] C-b(`TaskResult.stats.usageMissing`):这一行的用量数字是**下界**不是测量值
|
|
30
|
+
* —— 至少有一轮模型没报用量,或整条结果是宿主/编排车道自铸的(一个模型轮都没跑过)。
|
|
31
|
+
* `true` 或缺席,**永不 `false`**;缺席 ⇔ 每轮都报了用量。fold 侧翻成既有的
|
|
32
|
+
* {@link UsageTotals.estimated}(见 `foldRow`),**不开第二条不精确轴**。 */
|
|
33
|
+
usageMissing?: true;
|
|
29
34
|
};
|
|
30
35
|
}
|
|
31
36
|
/**
|
|
@@ -57,7 +62,10 @@ export interface UsageTotals {
|
|
|
57
62
|
* ②有 per-model echo 行**缺数值键**(该键按 0 计入,总量偏低;成本键缺席时尤其:少算 ≠ 免费);
|
|
58
63
|
* ③(S10/V2-⑤)有行的 {@link UsageModelEntry.usageBasis} 缺席或非本读端认识的分量制——存量行某族
|
|
59
64
|
* 历史上把 cache 计入 inputTokens,tokensIn 求和会**双算**,口径不可分辨。
|
|
60
|
-
*
|
|
65
|
+
* ④(7.72.0 / core 7.14.0 [ref] C-b)有行自陈 `stats.usageMissing` —— 那一行的数字是**下界**:至少一轮
|
|
66
|
+
* 模型没报用量,或整条结果是宿主自铸的零结果(取消打断 / 零执行的治理拒绝)。`tokens: 0` 在那种行上读作
|
|
67
|
+
* 「未知」而不是「免费」。
|
|
68
|
+
* 四者都只表达「别把这个总数当精确值」,消费端一视同仁。 */
|
|
61
69
|
estimated: boolean;
|
|
62
70
|
}
|
|
63
71
|
/** GET /v1/usage/summary — 窗内总量。 */
|
package/dist/usage-analytics.js
CHANGED
|
@@ -17,8 +17,13 @@ function foldModelEntry(t, d) {
|
|
|
17
17
|
if (d.usageBasis !== "uncached-components-v1")
|
|
18
18
|
t.estimated = true;
|
|
19
19
|
}
|
|
20
|
+
function markRowLowerBound(t, row) {
|
|
21
|
+
if (row.stats?.usageMissing === true)
|
|
22
|
+
t.estimated = true;
|
|
23
|
+
}
|
|
20
24
|
function foldRow(t, row) {
|
|
21
25
|
t.tasks += 1;
|
|
26
|
+
markRowLowerBound(t, row);
|
|
22
27
|
const mu = row.stats?.modelUsage;
|
|
23
28
|
if (mu && Object.keys(mu).length > 0) {
|
|
24
29
|
for (const d of Object.values(mu))
|
|
@@ -70,6 +75,7 @@ export function usageBreakdown(rows, dimension) {
|
|
|
70
75
|
const t = bucket(model);
|
|
71
76
|
t.tasks += 1;
|
|
72
77
|
foldModelEntry(t, d);
|
|
78
|
+
markRowLowerBound(t, r);
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
else {
|
|
@@ -92,6 +98,8 @@ export function projectUsageStats(result) {
|
|
|
92
98
|
const mu = stats.modelUsage;
|
|
93
99
|
if (mu && typeof mu === "object" && !Array.isArray(mu))
|
|
94
100
|
out.modelUsage = mu;
|
|
101
|
+
if (stats.usageMissing === true)
|
|
102
|
+
out.usageMissing = true;
|
|
95
103
|
return out;
|
|
96
104
|
}
|
|
97
105
|
catch {
|