@sema-agent/client-core 0.16.0 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/hitl/frameRouter.js +24 -2
- package/dist/limitsWire.d.ts +14 -4
- package/dist/limitsWire.js +17 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
23
23
|
|
|
24
24
|
## Scope
|
|
25
25
|
|
|
26
|
-
**Version:** 0.16.
|
|
26
|
+
**Version:** 0.16.2
|
|
27
27
|
|
|
28
28
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
29
29
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
|
@@ -220,6 +220,7 @@ public-surface guard checks that last one).
|
|
|
220
220
|
| `scripts/run-print-bash-iserror-test.mjs` | The print lane's Bash `is_error` authority (structured over regex) |
|
|
221
221
|
| `scripts/run-sdk-floor-test.mjs` | The SDK version floor — and, more to the point, that the *installed* type declarations still carry the keys this package reads |
|
|
222
222
|
| `scripts/run-engine-vocab-floor-test.mjs` | Engine-mirrored vocabularies (structured card whitelist, self-reported tool face, control verbs, recogniser sets) against the *installed* `@sema-agent/core` |
|
|
223
|
+
| `scripts/run-limits-env-failloud-test.mjs` | `SEMA_HEADLESS_*` env-lane limits reject invalid values as loudly as the flag lane (no silent "no budget" runs) |
|
|
223
224
|
| `scripts/run-streamjson-timing-honesty-test.mjs` | Stream timing & terminal honesty ([2084]): held errored fs-write results release on model progress; a wall-clock stop maps to `error_during_execution` with a truthful salvage note; the synthetic API-error assistant row carries the `<synthetic>` in-message sentinel. Also ([2489], core 5.8.0): the run-limit `errorCode` -> CC subtype map is pinned code by code (`limits.max_{cost,turns,tokens,walltime}_exceeded`), token/wall-clock stops keep the text the engine already produced, and the `failed` event arm shares the one mapping point. The 5.7 dual-vocabulary legs retired with server 6.0.0 (which bundles core 5.8.0); four **retirement negative controls** stand in their place — the retired `status:'timeout'` and the retired codes must fall to the honest fallback subtype and must never drop back to an empty success, so putting any of them back turns the gate red |
|
|
224
225
|
| `scripts/run-background-view-test.mjs` | `createBackgroundView` lifecycle: polling/notify pairing, per-source degrade (`501 → not-configured` vs `unavailable`), the capabilities `scheduler` probe, and dispose really aborting the in-flight fleet snapshot (pure projection lives in the pure suite's W-A segment) |
|
|
225
226
|
| `scripts/run-fleet-view-keys-test.mjs` | The fleet projection views, **both directions**: `FleetTaskView`/`FleetWorkflowView` ⇄ their key lists (compile-pinned) ⇄ what a maximal/minimal row really projects, plus a wire-key coverage ledger (every `FleetTaskRow` key is either projected or carries a written reason why not) and a drift ledger against the shell's render contract. A one-directional assignability check is blind to optional keys — which is how `startedAt` was silently dropped |
|
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -14,6 +14,27 @@ function rejectMessageForRender() {
|
|
|
14
14
|
/** core 对被 gate/连坐 abort 的 call 铸的 tool_end 载体(逐字;desktop session-host 真引擎实测
|
|
15
15
|
* 同款)——HOLD 谓词锚它做**精确等值**,普通工具错的输出是各自错误文案,永不进 HOLD。 */
|
|
16
16
|
export const ENGINE_ABORT_TOOL_RESULT = 'Operation aborted';
|
|
17
|
+
/**
|
|
18
|
+
* tool_end.output 的文本归一(P0 案B,2026-08-03,server 6.0.0 wire 实测):sync leg 的毒化帧
|
|
19
|
+
* `output` 不再是裸 string,而是 content-block 数组 `[{"type":"text","text":"Operation aborted"}]`
|
|
20
|
+
* (8790 裸引擎 POST /v1/tasks/stream 逐帧记录字节形)。abort 标记的**等值锚**必须先归一再比,
|
|
21
|
+
* 否则 6.0.0 下 hold-poison 臂全盲:毒化帧当场上屏 + generic-close 记 ended ⇒ decide 后重放的
|
|
22
|
+
* 真收口帧被 isEnded 早退去重 —— 「按 Yes 屏幕永远停在 Error: Operation aborted」整案复发。
|
|
23
|
+
* 只归一**判定**,帧本身一个字节不改;等值语义不放宽([2084]①-b 收窄仍然成立,负控见 gate ⑩)。
|
|
24
|
+
*/
|
|
25
|
+
function toolEndOutputText(output) {
|
|
26
|
+
if (typeof output === 'string')
|
|
27
|
+
return output;
|
|
28
|
+
if (!Array.isArray(output))
|
|
29
|
+
return undefined;
|
|
30
|
+
const parts = [];
|
|
31
|
+
for (const b of output) {
|
|
32
|
+
const bb = b;
|
|
33
|
+
if (bb?.type === 'text' && typeof bb.text === 'string')
|
|
34
|
+
parts.push(bb.text);
|
|
35
|
+
}
|
|
36
|
+
return parts.length > 0 ? parts.join('') : undefined;
|
|
37
|
+
}
|
|
17
38
|
// ── gate 词汇(两把刀共享)──────────────────────────────────────────────────────────────────────
|
|
18
39
|
/** 工具名是不是 AskUserQuestion(空白/下划线/连字符与大小写都不敏感)。
|
|
19
40
|
* **宽口**:帧上的 `toolName` 位是 UNTRUSTED 的任意值,判据自己吃 `unknown`,本模块内部用。 */
|
|
@@ -81,7 +102,7 @@ function isGatedToolName(name) {
|
|
|
81
102
|
function isGatedToolEnd(ev) {
|
|
82
103
|
if (isGatedToolName(ev.toolName))
|
|
83
104
|
return true;
|
|
84
|
-
return ev.output === ENGINE_ABORT_TOOL_RESULT;
|
|
105
|
+
return toolEndOutputText(ev.output) === ENGINE_ABORT_TOOL_RESULT;
|
|
85
106
|
}
|
|
86
107
|
/** done 帧的 AskUserQuestion park 形状(结构性读;别的终态一律 false)。 */
|
|
87
108
|
function isAskGatePark(result) {
|
|
@@ -200,7 +221,8 @@ const TOOL_END_ARMS = [
|
|
|
200
221
|
run(ev, callId, led) {
|
|
201
222
|
if (ev.isError !== true || !isGatedToolEnd(ev))
|
|
202
223
|
return undefined;
|
|
203
|
-
|
|
224
|
+
// 6.0.0 wire:output 可为 content-block 数组形(见 toolEndOutputText 头注)—— 归一后仍做精确等值。
|
|
225
|
+
if (toolEndOutputText(ev.output) !== ENGINE_ABORT_TOOL_RESULT)
|
|
204
226
|
return undefined;
|
|
205
227
|
led.hold(callId, ev); // park 毒化帧,先 HOLD(gate 定性前不渲 Error 卡)
|
|
206
228
|
return { kind: 'skip' };
|
package/dist/limitsWire.d.ts
CHANGED
|
@@ -117,11 +117,21 @@ export type LimitsParseResult = {
|
|
|
117
117
|
*/
|
|
118
118
|
export declare function parseLimitsArgv(argv: string[]): LimitsParseResult;
|
|
119
119
|
/**
|
|
120
|
-
* The settings-lane defaults.
|
|
121
|
-
*
|
|
122
|
-
*
|
|
120
|
+
* The settings-lane defaults. FAIL-LOUD on a bad value (#132 / test [2508],2026-08-04,推翻旧
|
|
121
|
+
* batch-E「静默降级」形):设置了 SEMA_HEADLESS_* 却是非法值时,旧行为=不开 SEMA_DEBUG 完全无声、
|
|
122
|
+
* 按「无限期限」跑——用户以为设了预算,实际什么都没设,这正是 [honest-absence] 禁的那类静默谎报。
|
|
123
|
+
* 现与 flag 路径同拍响亮拒(同文案形,caller exit 1):配置错了要在第一次跑就知道,而不是在一个
|
|
124
|
+
* 本该 300s 停的任务跑了一夜之后。返回判别联合;唯一消费者是下面的 limitsForPrint(仓际零外部
|
|
125
|
+
* 消费,干净切不设兼容)。
|
|
123
126
|
*/
|
|
124
|
-
export
|
|
127
|
+
export type HeadlessEnvLimitsResult = {
|
|
128
|
+
ok: true;
|
|
129
|
+
limits: HeadlessLimits | undefined;
|
|
130
|
+
} | {
|
|
131
|
+
ok: false;
|
|
132
|
+
error: string;
|
|
133
|
+
};
|
|
134
|
+
export declare function headlessLimitsFromEnv(env?: EnvLike): HeadlessEnvLimitsResult;
|
|
125
135
|
/**
|
|
126
136
|
* Resolve the limits for a headless `-p` submit: explicit flags > settings env defaults > none
|
|
127
137
|
* (= no stamp, today's behaviour). Merge is PER-KNOB: `--deadline 300` + SEMA_HEADLESS_MAX_TURNS=50
|
package/dist/limitsWire.js
CHANGED
|
@@ -165,44 +165,36 @@ export function parseLimitsArgv(argv) {
|
|
|
165
165
|
flaggedFlags,
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
|
-
/**
|
|
169
|
-
* The settings-lane defaults. SILENT degrade on a bad value (batch-E triage shape — stricter than
|
|
170
|
-
* scenarioWire's warn-and-ignore: a settings typo must neither brick nor spam every headless run).
|
|
171
|
-
* SEMA_DEBUG surfaces the drop.
|
|
172
|
-
*/
|
|
173
168
|
export function headlessLimitsFromEnv(env = hostEnv()) {
|
|
174
169
|
const out = {};
|
|
175
170
|
const dl = env[HEADLESS_DEADLINE_ENV]?.trim();
|
|
176
171
|
if (dl) {
|
|
177
172
|
const n = parseIntInDomain(dl, DEADLINE_SEC_MIN, DEADLINE_SEC_MAX);
|
|
178
|
-
if (n
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
if (n === null) {
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
error: `Error: ${HEADLESS_DEADLINE_ENV}="${dl}" is not an integer in ${DEADLINE_SEC_MIN}..${DEADLINE_SEC_MAX} (seconds). Fix or unset it.`,
|
|
177
|
+
};
|
|
183
178
|
}
|
|
179
|
+
out.maxWalltimeMs = n * SEC_TO_MS; // env 面同样是秒(变量名带 _SEC),换算同一口
|
|
184
180
|
}
|
|
185
181
|
const mt = env[HEADLESS_MAX_TURNS_ENV]?.trim();
|
|
186
182
|
if (mt) {
|
|
187
183
|
const n = parseIntInDomain(mt, MAX_TURNS_MIN, MAX_TURNS_MAX);
|
|
188
|
-
if (n
|
|
189
|
-
|
|
190
|
-
else if (env.SEMA_DEBUG) {
|
|
191
|
-
// eslint-disable-next-line no-console
|
|
192
|
-
console.error(`[sema] ${HEADLESS_MAX_TURNS_ENV}="${mt}" is not a positive integer — ignoring the settings default`);
|
|
184
|
+
if (n === null) {
|
|
185
|
+
return { ok: false, error: `Error: ${HEADLESS_MAX_TURNS_ENV}="${mt}" is not a positive integer. Fix or unset it.` };
|
|
193
186
|
}
|
|
187
|
+
out.maxTurns = n;
|
|
194
188
|
}
|
|
195
189
|
const mk = env[HEADLESS_MAX_TOKENS_ENV]?.trim();
|
|
196
190
|
if (mk) {
|
|
197
191
|
const n = parseIntInDomain(mk, MAX_TOKENS_MIN, MAX_TOKENS_MAX);
|
|
198
|
-
if (n
|
|
199
|
-
|
|
200
|
-
else if (env.SEMA_DEBUG) {
|
|
201
|
-
// eslint-disable-next-line no-console
|
|
202
|
-
console.error(`[sema] ${HEADLESS_MAX_TOKENS_ENV}="${mk}" is not a positive integer — ignoring the settings default`);
|
|
192
|
+
if (n === null) {
|
|
193
|
+
return { ok: false, error: `Error: ${HEADLESS_MAX_TOKENS_ENV}="${mk}" is not a positive integer. Fix or unset it.` };
|
|
203
194
|
}
|
|
195
|
+
out.maxTokens = n;
|
|
204
196
|
}
|
|
205
|
-
return hasAnyLimit(out) ? out : undefined;
|
|
197
|
+
return { ok: true, limits: hasAnyLimit(out) ? out : undefined };
|
|
206
198
|
}
|
|
207
199
|
/**
|
|
208
200
|
* Resolve the limits for a headless `-p` submit: explicit flags > settings env defaults > none
|
|
@@ -213,8 +205,11 @@ export function limitsForPrint(argv, env = hostEnv()) {
|
|
|
213
205
|
const parsed = parseLimitsArgv(argv);
|
|
214
206
|
if (!parsed.ok)
|
|
215
207
|
return parsed;
|
|
208
|
+
// #132:env 面非法值与 flag 同拍响亮拒(见 headlessLimitsFromEnv 头注)。
|
|
216
209
|
const fromEnv = headlessLimitsFromEnv(env);
|
|
217
|
-
|
|
210
|
+
if (!fromEnv.ok)
|
|
211
|
+
return { ok: false, error: fromEnv.error };
|
|
212
|
+
const merged = { ...(fromEnv.limits ?? {}), ...(parsed.limits ?? {}) };
|
|
218
213
|
return { ok: true, ...(hasAnyLimit(merged) ? { limits: merged } : {}), flaggedFlags: parsed.flaggedFlags };
|
|
219
214
|
}
|
|
220
215
|
/** True when the engine version string satisfies the limits-wire minimum (≥1.196.0). Unparsable ⇒ false. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Blackboard [1832] design axioms; [1651]/[1652]/[1653] signed seam design. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|