@sema-agent/client-core 0.78.2 → 0.79.1
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/CHANGELOG.md +51 -0
- package/README.md +24 -19
- package/dist/adapt/arms.js +7 -0
- package/dist/adapt/textStream.d.ts +1 -0
- package/dist/adapt/textStream.js +10 -0
- package/dist/adapt/toolCards.d.ts +1 -0
- package/dist/adapt/toolCards.js +2 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -0
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +9 -1
- package/dist/adapter/downstream/terminalToSdkResult.js +34 -7
- package/dist/adapter/runStream.js +60 -3
- package/dist/agentSession/contract.d.ts +2 -1
- package/dist/engineNoticeCodes.js +2 -0
- package/dist/gateVocabulary.d.ts +6 -0
- package/dist/gateVocabulary.js +21 -0
- package/dist/hitl/askGateWire.d.ts +3 -0
- package/dist/hitl/askGateWire.js +2 -0
- package/dist/hitl/frameRouter.d.ts +1 -0
- package/dist/hitl/frameRouter.js +39 -8
- package/dist/hitl/gateLedger.d.ts +9 -4
- package/dist/hitl/gateLedger.js +29 -5
- package/dist/hitl/parkResolver.js +6 -1
- package/dist/hitl/persistedRulesWire.d.ts +12 -1
- package/dist/hitl/persistedRulesWire.js +20 -5
- package/dist/hitl/toolApprovalWire.d.ts +15 -1
- package/dist/hitl/toolApprovalWire.js +56 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mcpLiveness.js +3 -0
- package/dist/mcpProbeCapability.d.ts +18 -0
- package/dist/mcpProbeCapability.js +55 -0
- package/dist/mcpProbeWire.d.ts +51 -0
- package/dist/mcpProbeWire.js +217 -0
- package/dist/printToolResultFrame.d.ts +1 -0
- package/dist/printToolResultFrame.js +2 -0
- package/dist/request/taskRequest.d.ts +20 -1
- package/dist/request/taskRequest.js +198 -52
- package/dist/sdkRegistryTransit.d.ts +6 -0
- package/dist/sdkRegistryTransit.js +5 -0
- package/dist/sdkWireTransit.d.ts +2 -1
- package/dist/sdkWireTransit.js +1 -0
- package/dist/toolResult.d.ts +15 -1
- package/dist/toolResult.js +61 -4
- package/dist/wireFailureShape.d.ts +1 -0
- package/dist/wireFailureShape.js +44 -10
- package/docs/INTEGRATION-CLIENTS.md +209 -15
- package/package.json +8 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isCcToolDenialKind } from './gateVocabulary.js';
|
|
1
2
|
export { flattenWireOutput as flattenToolOutput } from './adapt/wireShapes.js';
|
|
2
3
|
import { flattenWireOutput } from './adapt/wireShapes.js';
|
|
3
4
|
function bashExitCodeFailed(toolName, text, structured) {
|
|
@@ -30,6 +31,7 @@ export function toolEndResultToUserFrame(arm) {
|
|
|
30
31
|
],
|
|
31
32
|
},
|
|
32
33
|
parent_tool_use_id: typeof arm.parentToolCallId === 'string' ? arm.parentToolCallId : null,
|
|
34
|
+
...(isCcToolDenialKind(arm._sema_denial_kind) ? { _sema_denial_kind: arm._sema_denial_kind } : {}),
|
|
33
35
|
uuid: arm.uuid,
|
|
34
36
|
session_id: arm.session_id,
|
|
35
37
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TaskRequest } from '@sema-agent/sdk';
|
|
2
|
-
export type RequestLane = 'interactive' | 'print';
|
|
2
|
+
export type RequestLane = 'interactive' | 'print' | 'utility';
|
|
3
3
|
export type TaskRequestLike = Record<string, unknown>;
|
|
4
4
|
export interface RequestFieldSpec {
|
|
5
5
|
field: string;
|
|
@@ -7,6 +7,7 @@ export interface RequestFieldSpec {
|
|
|
7
7
|
live: boolean;
|
|
8
8
|
why: string;
|
|
9
9
|
gap?: true;
|
|
10
|
+
whyUtilityLane: string;
|
|
10
11
|
}
|
|
11
12
|
export declare const REQUEST_FIELD_MATRIX: readonly RequestFieldSpec[];
|
|
12
13
|
export declare const LIVE_DEFAULT_FIELDS: readonly ["cwd", "additionalDirectories", "additionalReadDirectories", "forwardSubagentEvents", "retainSubagentSessions", "agents", "appendSystemPrompt|settings.outputStyle", "suggestNextPrompts", "compactionModel", "sessionId(三态解析)"];
|
|
@@ -39,12 +40,15 @@ export interface TaskRequestInput {
|
|
|
39
40
|
oneShot?: true;
|
|
40
41
|
excludeAllTools?: true;
|
|
41
42
|
outputSchema?: NonNullable<TaskRequest['outputSchema']>;
|
|
43
|
+
outputRetries?: number;
|
|
42
44
|
maxCostUsd?: number;
|
|
45
|
+
maxTokens?: number;
|
|
43
46
|
taskBudget?: number;
|
|
44
47
|
fallbackModel?: string;
|
|
45
48
|
maxThinkingTokens?: number;
|
|
46
49
|
memoryCapture?: 'off';
|
|
47
50
|
memory?: 'off';
|
|
51
|
+
approverPosture?: NonNullable<TaskRequest['approverPosture']>;
|
|
48
52
|
rewind?: Record<string, unknown>;
|
|
49
53
|
repo?: string;
|
|
50
54
|
council?: boolean;
|
|
@@ -65,6 +69,7 @@ export interface TaskRequestOmission {
|
|
|
65
69
|
readonly key: string;
|
|
66
70
|
readonly cause: TaskRequestOmissionCause;
|
|
67
71
|
}
|
|
72
|
+
export declare function taskRequestOmissionCauseDetail(cause: unknown): string;
|
|
68
73
|
export interface TaskRequestAssembly {
|
|
69
74
|
readonly request: TaskRequestLike;
|
|
70
75
|
readonly omitted: readonly TaskRequestOmission[];
|
|
@@ -83,6 +88,7 @@ export interface UpstreamAbsentRequestIntent {
|
|
|
83
88
|
readonly why: string;
|
|
84
89
|
}
|
|
85
90
|
export declare const UPSTREAM_ABSENT_REQUEST_INTENTS: readonly UpstreamAbsentRequestIntent[];
|
|
91
|
+
export declare const REQUEST_LANES: readonly RequestLane[];
|
|
86
92
|
export declare function taskRequestSeatOf(key: string, lane: RequestLane): TaskRequestSeatReading;
|
|
87
93
|
export declare const MEMORY_CAPTURE_OFF: "off";
|
|
88
94
|
export declare function memoryCaptureDeclarationField(declared: boolean): {
|
|
@@ -92,10 +98,23 @@ export declare const MEMORY_OFF: "off";
|
|
|
92
98
|
export declare function memoryOffDeclarationField(declared: boolean): {
|
|
93
99
|
memory: 'off';
|
|
94
100
|
} | Record<string, never>;
|
|
101
|
+
export declare const APPROVER_POSTURE_BYPASS: "bypass";
|
|
102
|
+
export declare function approverPostureDeclarationField(declared: boolean): {
|
|
103
|
+
approverPosture: 'bypass';
|
|
104
|
+
} | Record<string, never>;
|
|
95
105
|
export declare function memoryOffDeclared(input: {
|
|
96
106
|
flag?: unknown;
|
|
97
107
|
settingsMemory?: unknown;
|
|
98
108
|
} | null | undefined): boolean;
|
|
109
|
+
export type MaxCostUsdParseWhy = 'not_text' | 'empty' | 'not_a_number' | 'not_finite' | 'not_positive';
|
|
110
|
+
export type MaxCostUsdParse = {
|
|
111
|
+
readonly ok: true;
|
|
112
|
+
readonly value: number;
|
|
113
|
+
} | {
|
|
114
|
+
readonly ok: false;
|
|
115
|
+
readonly why: MaxCostUsdParseWhy;
|
|
116
|
+
};
|
|
117
|
+
export declare function parseMaxCostUsd(text: unknown): MaxCostUsdParse;
|
|
99
118
|
export declare function assembleTaskRequest(input: TaskRequestInput, lane: RequestLane): TaskRequestAssembly;
|
|
100
119
|
export declare function buildTaskRequest(input: TaskRequestInput, lane: RequestLane): TaskRequestLike;
|
|
101
120
|
export interface LiveDefaultsInput {
|
|
@@ -1,44 +1,47 @@
|
|
|
1
1
|
export const REQUEST_FIELD_MATRIX = [
|
|
2
|
-
{ field: 'objective', lanes: ['interactive', 'print'], live: false, why: '本 turn
|
|
3
|
-
{ field: 'sessionId', lanes: ['interactive', 'print'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量' },
|
|
4
|
-
{ field: 'scenario', lanes: ['interactive', 'print'], live: false, why: 'TUI 走 selectScenario(objective) 嗅探,print 走 --scenario/SEMA_HEADLESS_SCENARIO 显式值' },
|
|
5
|
-
{ field: 'sandboxImageProfile', lanes: ['interactive', 'print'], live: true, why: '[686]② 显式 profile;auto 档只在 TUI 有(一次性 advisory 进 objective)' },
|
|
6
|
-
{ field: 'permissionMode', lanes: ['interactive', 'print'], live: true, why: 'TUI 读 AppState.toolPermissionContext(带 stampDefault 门),print 读 flag>env(headlessPermissionModeWire)' },
|
|
7
|
-
{ field: 'selfOrchestration', lanes: ['interactive', 'print'], live: true, why: 'SEMA_SELF_ORCHESTRATION opt-in → core 挂 run_workflow' },
|
|
8
|
-
{ field: 'deferTools', lanes: ['interactive', 'print'], live: true, why: '[1052]① Workflow 延迟披露;selfOrchestration 前置门' },
|
|
9
|
-
{ field: 'retainBackgroundProcesses', lanes: ['interactive', 'print'], live: true, why: '缺省 ON(2026-07-26 起翻转);SEMA_RETAIN_BACKGROUND=0 逃生' },
|
|
10
|
-
{ field: 'promptProfile', lanes: ['interactive', 'print'], live: true, why: 'core 1.328 呈现轴 A/B 口,缺省不 stamp = 引擎缺省 simple' },
|
|
11
|
-
{ field: 'enableFork', lanes: ['interactive', 'print'], live: true, why: '显式布尔才 stamp([503]② fail-OPEN 教训:off 必须显式)' },
|
|
12
|
-
{ field: 'attachments', lanes: ['interactive', 'print'], live: true, why: 'design/133 + G1 缺省开的 turn 边界 attachment' },
|
|
13
|
-
{ field: 'skills', lanes: ['interactive', 'print'], live: true, why: '本地技能目录投影(布局取 CC 同款);两条车道都由 launchRepl 载入面注入' },
|
|
14
|
-
{ field: 'mcpServers', lanes: ['interactive', 'print'], live: true, why: '本地 .mcp.json 投影;service 单用户门决定是否兑现' },
|
|
15
|
-
{ field: 'settings.webSearch', lanes: ['interactive', 'print'], live: true, why: 'SEMA_WEBSEARCH_* / settings.json;per-request 配置赢过部署 env' },
|
|
16
|
-
{ field: 'settings.hooks', lanes: ['interactive', 'print'], live: true, why: '[495]① 用户 settings 文件 hooks 逐字上 wire' },
|
|
17
|
-
{ field: 'settings.<resolved>', lanes: ['interactive', 'print'], live: true, why: 'settings resolver 的 effective 快照(hooks/env/model + 已解析权限投影)。两条车道同源:交互面 replEntry / print 面 printModeEngine 各跑一遍同一个 resolver,产物经同一个 buildWireSettingsStamp 投影。开放集(子键不可枚举)⇒ unregisteredRequestKeys 放行本车道 settings 的**表外**子键,但表内异车道子键仍点名' },
|
|
18
|
-
{ field: 'repo', lanes: ['interactive', 'print'], live: true, why: 'scan/code-review 场景的仓库坐标(passthrough;server 是权威校验方,本层不铸第二判官)' },
|
|
19
|
-
{ field: 'council', lanes: ['interactive', 'print'], live: true, why: 'code-review council 形开关(server 读 `=== true`;非 true 不 stamp=与缺席同义,发小的那个)' },
|
|
20
|
-
{ field: 'debate', lanes: ['interactive', 'print'], live: true, why: 'council 的 debate 变体开关(server 读 `=== true`)' },
|
|
21
|
-
{ field: 'rounds', lanes: ['interactive', 'print'], live: true, why: 'debate 轮数(server clamp [1,3],非有限数被 server 归 undefined——本层原样透传不预 clamp)' },
|
|
22
|
-
{ field: 'lenses', lanes: ['interactive', 'print'], live: true, why: 'code-review 镜头集(passthrough 同 repo;形状由 server 场景定义,本层不闭集)' },
|
|
23
|
-
{ field: 'memoryCapture', lanes: ['interactive', 'print'], live: true, why: '本会话永远不进长期记忆的**声明形**(server §12.4 body 键,单成员闭集 "off")
|
|
24
|
-
{ field: 'memory', lanes: ['interactive', 'print'], live: true, why: '这一次运行不挂记忆面的**请求级**开关(server §12.7 body 键,单成员闭集 "off")
|
|
25
|
-
{ field: '
|
|
26
|
-
{ field: '
|
|
27
|
-
{ field: '
|
|
28
|
-
{ field: '
|
|
29
|
-
{ field: '
|
|
30
|
-
{ field: '
|
|
31
|
-
{ field: '
|
|
32
|
-
{ field: '
|
|
33
|
-
{ field: '
|
|
34
|
-
{ field: '
|
|
35
|
-
{ field: '
|
|
36
|
-
{ field: '
|
|
37
|
-
{ field: '
|
|
38
|
-
{ field: '
|
|
39
|
-
{ field: '
|
|
40
|
-
{ field: '
|
|
41
|
-
{ field: '
|
|
2
|
+
{ field: 'objective', lanes: ['interactive', 'print', 'utility'], live: false, why: '本 turn 的真实输入,每条车道都必需', whyUtilityLane: '恒座:三条车道都必须带上本次提交的模型面输入' },
|
|
3
|
+
{ field: 'sessionId', lanes: ['interactive', 'print', 'utility'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量', whyUtilityLane: '恒座:side-channel 提交走**独立**会话 id(壳另铸一个,绝不复用用户会话那一个 —— 复用会把这次提交的转录混进用户会话)' },
|
|
4
|
+
{ field: 'scenario', lanes: ['interactive', 'print'], live: false, why: 'TUI 走 selectScenario(objective) 嗅探,print 走 --scenario/SEMA_HEADLESS_SCENARIO 显式值', whyUtilityLane: '无座:场景值的来源只有两条用户车道(交互面按输入嗅探 / 无人值守面显式旗);side-channel 是壳自己的一次性问答,没有场景入口' },
|
|
5
|
+
{ field: 'sandboxImageProfile', lanes: ['interactive', 'print'], live: true, why: '[686]② 显式 profile;auto 档只在 TUI 有(一次性 advisory 进 objective)', whyUtilityLane: '无座:镜像档位是**工具执行面**的位,而 side-channel 提交零工具面 ⇒ 没有要挑的镜像' },
|
|
6
|
+
{ field: 'permissionMode', lanes: ['interactive', 'print'], live: true, why: 'TUI 读 AppState.toolPermissionContext(带 stampDefault 门),print 读 flag>env(headlessPermissionModeWire)', whyUtilityLane: '无座:它选的是**工具门的形状**,而 side-channel 提交零工具面 ⇒ 没有门可管;带上去只会让读请求体的人以为这次提交挂着工具' },
|
|
7
|
+
{ field: 'selfOrchestration', lanes: ['interactive', 'print'], live: true, why: 'SEMA_SELF_ORCHESTRATION opt-in → core 挂 run_workflow', whyUtilityLane: '无座:它挂的是编排工具,与零工具面直接矛盾' },
|
|
8
|
+
{ field: 'deferTools', lanes: ['interactive', 'print'], live: true, why: '[1052]① Workflow 延迟披露;selfOrchestration 前置门', whyUtilityLane: '无座:工具延迟披露是工具面的位,零工具面上没有对象' },
|
|
9
|
+
{ field: 'retainBackgroundProcesses', lanes: ['interactive', 'print'], live: true, why: '缺省 ON(2026-07-26 起翻转);SEMA_RETAIN_BACKGROUND=0 逃生', whyUtilityLane: '无座:后台进程是工具面的产物,零工具面产不出' },
|
|
10
|
+
{ field: 'promptProfile', lanes: ['interactive', 'print'], live: true, why: 'core 1.328 呈现轴 A/B 口,缺省不 stamp = 引擎缺省 simple', whyUtilityLane: '无座:呈现轴是用户会话的偏好;side-channel 的系统提示由壳整段给出(见 systemPrompt 那一行),不再借呈现轴调形' },
|
|
11
|
+
{ field: 'enableFork', lanes: ['interactive', 'print'], live: true, why: '显式布尔才 stamp([503]② fail-OPEN 教训:off 必须显式)', whyUtilityLane: '无座:分叉是会话面的动作,而 side-channel 提交是一次性的独立会话' },
|
|
12
|
+
{ field: 'attachments', lanes: ['interactive', 'print'], live: true, why: 'design/133 + G1 缺省开的 turn 边界 attachment', whyUtilityLane: '无座:turn 边界的附件配置属用户会话;side-channel 提交不带附件' },
|
|
13
|
+
{ field: 'skills', lanes: ['interactive', 'print'], live: true, why: '本地技能目录投影(布局取 CC 同款);两条车道都由 launchRepl 载入面注入', whyUtilityLane: '无座:技能挂的是工具,与零工具面矛盾' },
|
|
14
|
+
{ field: 'mcpServers', lanes: ['interactive', 'print'], live: true, why: '本地 .mcp.json 投影;service 单用户门决定是否兑现', whyUtilityLane: '无座:MCP 服务挂的是工具,与零工具面矛盾' },
|
|
15
|
+
{ field: 'settings.webSearch', lanes: ['interactive', 'print'], live: true, why: 'SEMA_WEBSEARCH_* / settings.json;per-request 配置赢过部署 env', whyUtilityLane: '无座:联网检索是一只工具,与零工具面矛盾' },
|
|
16
|
+
{ field: 'settings.hooks', lanes: ['interactive', 'print'], live: true, why: '[495]① 用户 settings 文件 hooks 逐字上 wire', whyUtilityLane: '无座:hook 挂在工具门上(零工具面没有门),而且 side-channel 是壳自己的提交,不该继承用户那份 hook 表' },
|
|
17
|
+
{ field: 'settings.<resolved>', lanes: ['interactive', 'print'], live: true, why: 'settings resolver 的 effective 快照(hooks/env/model + 已解析权限投影)。两条车道同源:交互面 replEntry / print 面 printModeEngine 各跑一遍同一个 resolver,产物经同一个 buildWireSettingsStamp 投影。开放集(子键不可枚举)⇒ unregisteredRequestKeys 放行本车道 settings 的**表外**子键,但表内异车道子键仍点名', whyUtilityLane: '无座:这一位是**开放集 spread**(子键原样即 wire 键),摊开的是用户 settings 的已解析面(权限 / env / 模型)。side-channel 是壳自己的提交:摊上去既无用(零工具面没有工具门求值权限)又把用户的配置面带进了一条他没参与的提交。🔴 无座 ⇒ 本车道整只快照不产。🔴 **本行无座的前提由构造期校验兜住**:剥掉的是用户显式的 deny / ask,所以 `refuseUtilityWithoutToolUnload` 强制这条车道必须带字面 `excludeAllTools: true` —— 否则产出会是一条「既没卸工具、又没带用户 deny」的请求(比无人值守车道同一份输入更宽)' },
|
|
18
|
+
{ field: 'repo', lanes: ['interactive', 'print'], live: true, why: 'scan/code-review 场景的仓库坐标(passthrough;server 是权威校验方,本层不铸第二判官)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
19
|
+
{ field: 'council', lanes: ['interactive', 'print'], live: true, why: 'code-review council 形开关(server 读 `=== true`;非 true 不 stamp=与缺席同义,发小的那个)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
20
|
+
{ field: 'debate', lanes: ['interactive', 'print'], live: true, why: 'council 的 debate 变体开关(server 读 `=== true`)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
21
|
+
{ field: 'rounds', lanes: ['interactive', 'print'], live: true, why: 'debate 轮数(server clamp [1,3],非有限数被 server 归 undefined——本层原样透传不预 clamp)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
22
|
+
{ field: 'lenses', lanes: ['interactive', 'print'], live: true, why: 'code-review 镜头集(passthrough 同 repo;形状由 server 场景定义,本层不闭集)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
23
|
+
{ field: 'memoryCapture', lanes: ['interactive', 'print', 'utility'], live: true, why: '本会话永远不进长期记忆的**声明形**(server §12.4 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryWrite:false 不是一个轴(那是「本 run 只读平面」)。⚠️ parked 续跑腿结构性拿不到声明(决策/续跑端点从持久 body 重建 spec),那是引擎侧事实,不是本层的漏', whyUtilityLane: '有座:side-channel 提交是壳自己的一次性问答,**绝不该**进用户的长期记忆 —— 这一位正是那句声明,且它是收紧方向,带上去只会更严' },
|
|
24
|
+
{ field: 'memory', lanes: ['interactive', 'print', 'utility'], live: true, why: '这一次运行不挂记忆面的**请求级**开关(server §12.7 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryCapture(采集面)不是一个轴、可并存;与 memoryWrite:false(只读平面)也不是一个轴', whyUtilityLane: '有座:side-channel 提交不需要挂记忆面(不挂记忆段 / 索引 / 三只记忆工具),省 token 且零记忆写入;与 memoryCapture 不是一个轴,两条都带' },
|
|
25
|
+
{ field: 'approverPosture', lanes: ['interactive', 'print'], live: true, why: '宿主对这个 run 的**审批姿态声明**(「这个 run 的审批卡由我自己自动答」;单成员闭集 `"bypass"`)。🔴 它不授权任何东西、也不是一只门:引擎只把这句声明**渲**给模型(它自己的标记块,首个回合),没有任何裁决腿读它 —— 与 `permissionMode` 的 `bypassPermissions`(选门的形状)**不是一回事**,两键在请求体上各自原样过境、**互不派生**:宿主完全可以一边留着缺省权限模式、一边自己自动答这个 run 的审批卡。🔴 **声明它的唯一条件是「宿主确实自己自动答这个 run 的审批卡」,不许据此推导或改动 `permissionMode`** —— 拿这一位当理由去把门放宽,是把一句只渲给模型看的话读成了授权。宿主并不自动答卡却声明了它 ⇒ 那是对模型说了一句与实际不符的话,修法是**别声明**,不是去动门。加座的理由是**一个事实一处铸点**:同样一句话,要么由引擎在它自己的标记块里说,要么由壳把提醒形文本拼进本 turn 的输入 —— 后者在模型眼里是一段**没有引擎标记**的提醒形副本,按标记声明它该当**数据**处理并报出来。所以壳从此停拼,交这一位。两条用户车道都是提交面 ⇒ 都能声明。坏词由引擎响亮拒(单成员闭集,打错字不许被折成缺席),本层同律在构造期就拒(见 `refuseBadApproverPosture`)。⚠️ 上游的**提交体**与**引擎规格**两层都真读这一键,而装着的 sdk 型面尚未镜像它 ⇒ 本层的字面按提交体的形声明,不从 sdk 取型', whyUtilityLane: '无座:审批姿态是**用户**对他自己那条会话的声明,而 side-channel 是壳自己的提交 —— 把用户的 bypass 姿态带进一条他没参与的提交,等于替他重申一句他没说过的话;况且零工具面根本产不出审批卡' },
|
|
26
|
+
{ field: 'finalVerification', lanes: ['print'], live: true, why: 'P1-1 终验:无人值守车道才需要引擎自证;交互 REPL 由人当场看结果。#106 裁 B(让位+告知)后交互面默认关', whyUtilityLane: '无座:终验让引擎再跑一轮自证,而 side-channel 要的是一次小提交的即时答案;「无人值守才需要引擎自证」这条理由在本车道上让位给「小预算优先」' },
|
|
27
|
+
{ field: 'limits', lanes: ['print', 'utility'], live: true, why: 'P2-3-b:`-p` 的预算护栏(--max-* flag 族),交互 REPL 由人随时 Esc', whyUtilityLane: '有座:side-channel 必须带预算护栏(墙钟 / 输出 token / 轮数)—— 它同样没有人看着,而且没有后续回合可以中断' },
|
|
28
|
+
{ field: 'interactiveTools', lanes: ['print', 'utility'], live: true, why: '[909]B 件3:无人值守 stamp false,从 roster 源头灭掉 AskUserQuestion/plan 门。交互车道 stamp false 等于自废武功', whyUtilityLane: '有座:side-channel 绝不该弹交互问答 / plan 门(没有人在看这条提交)。它与 excludeAllTools 是**两条独立**的收紧声明、在这条车道上**并存**(本车道强制带卸载声明,见 `refuseUtilityWithoutToolUnload`;这一位是它旁边那条「连问都别问」的声明,不是它的降级替身)' },
|
|
29
|
+
{ field: 'oneShot', lanes: ['print', 'utility'], live: true, why: 'SDK TaskRequest.oneShot(server ≥7.12.0 消费):`-p` 提交是一次性的——没有后续回合接住异步后台通知,core 据此把「结束回合等通知」指引换成 block-wait;交互车道有后续回合,stamp true 就是谎报。老 worker 静默忽略(开集提交体),故发送侧不设能力位前置门(oneShotWireCaps 头注)', whyUtilityLane: '有座:side-channel 就是一次性提交 —— 没有后续回合接住异步后台通知,引擎据此把「结束回合等通知」换成 block-wait' },
|
|
30
|
+
{ field: 'outputSchema', lanes: ['print', 'utility'], live: true, why: '结构化输出约束(sdk `TaskRequest.outputSchema`:最终答案必须匹配的 JSON Schema)。上游只验形状与序列化尺寸(坏形 400 响亮拒)、深层合法性归引擎 ⇒ 本层原样透传、不预铸第二判官。来源只可能在无人值守车道(schema 由壳的无人值守入口给;哪个壳今天真的接了那条入口是壳侧的事,本表只说座位);交互面今天没有入口 ⇒ 缺席是**没有来源**,不是漏。⚠️ 本行只管**上行**这一位:结果一侧的结构化产出落不落 CC 形 result 帧是下行投影的事,不在本表', whyUtilityLane: '有座:side-channel 的典型形正是「要一个可机读的小答案」(分类 / 摘要 / 抽取),结构化输出约束是它最直接的位,而 schema 由壳自己给 ⇒ 有来源' },
|
|
31
|
+
{ field: 'outputRetries', lanes: ['print', 'utility'], live: true, why: '结构化输出的重试轮数(sdk `TaskRequest.outputRetries`;与 `outputSchema` 是**成对旋钮** —— 没有 schema 时引擎忽略它)。🔴 座位集与 `outputSchema` 那一行**逐字相同**:一半有座一半没座 = 端调得动重试轮数却调不动它所服务的约束(同座孪生分开定座,正是「主轴改了、孪生漏了」那一族)。上游接受域很窄(有限、≥1、向下取整、封顶 10),**表外一律整键省略**并回落引擎缺省 ⇒ 本层对坏值**响亮拒**(见 `refuseBadOutputRetries`),但**不预夹** [1,10]:夹是上游的事,本层不铸第二判官', whyUtilityLane: '有座:与 outputSchema 同座孪生 —— side-channel 要机读小答案时,重试轮数是同一个旋钮的另一半' },
|
|
32
|
+
{ field: 'maxCostUsd', lanes: ['print', 'utility'], live: true, why: '花费上限声明(sdk `TaskRequest.maxCostUsd`,请求体**顶层**位;上游向下夹到部署上限 —— 只许要得更少)。与 `limits` 同一条理由只在无人值守车道:交互面由人随时中断。🔴 坏值(非数 / 非有限 / 非正)在本层**响亮拒**:上游对这一位的坏值是安静忽略并回落部署上限,不是 4xx(见 `refuseBadMaxCostUsd`)', whyUtilityLane: '有座:小预算是本车道的定义之一(与 limits 同一条理由);上游向下夹到部署上限,只许要得更少' },
|
|
33
|
+
{ field: 'maxTokens', lanes: ['print', 'utility'], live: true, why: '整任务 token 预算上限声明(sdk `TaskRequest.maxTokens`,请求体**顶层**位,与 `maxCostUsd` 同属上游那一族「调用方要求的上限」:向下夹到部署上限 —— 只许要得更少)。🔴 座位集与 `maxCostUsd` 那一行**逐字相同**:同一条预算轴的两种计价单位,分开定座就是让其中一条静默漂。🔴 坏值(非数 / 非有限 / 非正 / 非整)在本层**响亮拒**:上游对这一位的坏值是安静按「调用方没给」处理并回落部署上限,不是 4xx —— 部署没设上限时这一次运行就没有 token 闸,而声明人不会知道(见 `refuseBadMaxTokens`)', whyUtilityLane: '有座:与 maxCostUsd 同轴孪生 —— 小预算是本车道的定义之一,两种计价单位都该能声明' },
|
|
34
|
+
{ field: 'attachmentIds', lanes: ['interactive'], live: true, why: '附件**字节通道**(uploadAttachment 回执 id;server ≥1.289 绑定会话并把文件物化进 run 的 attachments/)。有上传入口的端才有 id 可引用:web/desktop 有,壳走 images / 本地文件路径,`-p` 车道连 uploadAttachment 都没有 ⇒ print 缺席是**没有来源**,不是漏。🔴 与上面的 `attachments` 同名不同物(那是 turn 边界的配置键),两条永远不许合并', whyUtilityLane: '无座:预上传附件句柄要先有上传入口才有 id 可引用,而 side-channel 没有上传面' },
|
|
35
|
+
{ field: 'excludeTools', lanes: ['interactive', 'print', 'utility'], live: true, why: 'design/161 W3(2026-07-30):真 wire 键(SDK types `excludeTools`)且早在 seatContract `START_SESSION_OPTION_KEYS` 里,却是 desktop 手写 taskReq 的第 9 键、矩阵外——而 stamp 门对未登记键**静默丢弃**,真实危险形=「用户显式排除的工具被静默放回」(权限方向回归,类型层不报)。今天只有 desktop 在发;壳的工具面走 roster/interactiveTools 另一条路。0.77.0 补 print 座(三问:① wire 位与车道无关 —— sdk 具名位、提交体真收 `body.excludeTools`;② 来源 —— print 今天没有排除入口,但这是**收紧方向**的声明,传了只进回执 = 端不读回执就把用户排除的工具放回;③ 口径 —— 仍是 live 门后的位,与交互面同一行)', whyUtilityLane: '有座:逐名排除是卸载声明旁边的第二条收紧声明(两键同带各自生效)。⚠️ 它**不是** excludeAllTools 的降级替身 —— 本车道强制带卸载声明(见 `refuseUtilityWithoutToolUnload`),不认那一键的上游会把整条提交拒掉,那时正解是换车道或不发' },
|
|
36
|
+
{ field: 'excludeAllTools', lanes: ['interactive', 'print', 'utility'], live: true, why: '0.78.0(sdk 11.x 具名位 `excludeAllTools`,server ≥7.90.0 请求面):整只工具面卸载 —— 调用方工具 / 脚手架 / 手 / MCP / A2A / skills 一个都不挂,终局必然纯文本(wire 上 `wiring_manifest.tools.count === 0`,整条流零 tool_start / tool_end / tool_disclosure)。与 excludeTools 是两种语义、刻意不折叠(`"*"` 是一个合法的工具名,「卸全部」不能写成 `excludeTools:["*"]`);两键同带各自生效。类型只收字面 `true`(server 只把 literal true 写上任务规格,`false` = 替引擎重申它自己的缺省 ⇒ 不发、与缺席同义、不进回执);坏值响亮拒(收紧方向的声明不许被打字错误安静丢掉)。每条车道都是提交面 ⇒ 都能声明。探测靠键闭集、没有能力位:没接线的 server(≤7.89.x)400 `request.body_shape` 并在 `unknownKeys` 里逐字点名,本层不替它猜;A2A 入站面不承载本键(零工具面走 skill 路由)', whyUtilityLane: '有座,而且**本车道强制**:零工具面就是这条车道的定义 —— side-channel 提交整只工具面卸载,终局必然纯文本。🔴 缺这一位(或给 `false`)⇒ 构造期响亮拒(`refuseUtilityWithoutToolUnload`):本车道上一整片「无座」的理由都压在这个前提上,其中 `settings.<resolved>` 那一行剥掉的是用户显式的 deny / ask ——前提不成立时产出会是一条「既没卸工具、又没带用户 deny」的请求' },
|
|
37
|
+
{ field: 'settings.ultracode', lanes: ['interactive'], live: true, why: 'design/111:sticky `/effort ultracode` 拨盘 + 当轮关键词嗅探,两个来源都只在交互面存在', gap: true, whyUtilityLane: '无座:sticky 拨盘与当轮关键词嗅探两个来源都只在交互面' },
|
|
38
|
+
{ field: 'systemPrompt', lanes: ['interactive', 'print', 'utility'], live: false, why: '整段替换系统提示(sdk `TaskRequest.systemPrompt`;上游字符数超限 400 响亮拒,本层原样透传)。三条车道都有来源:交互面 = CC QueryParams.systemPrompt,无人值守面 = `--system-prompt[-file]` 一类旗,side-channel 面 = 壳整段给出的那一份。与追加位 `appendSystemPrompt` 不是一个位(那一位归 live 兜底层,见 `LIVE_DEFAULT_FIELDS`)', whyUtilityLane: '有座:side-channel 提交的系统提示由壳整段给出 —— 它不是用户会话的续谈,不该继承用户那一份' },
|
|
39
|
+
{ field: 'reasoningEffort', lanes: ['interactive', 'print', 'utility'], live: false, why: '思考档位(sdk `TaskRequest.reasoningEffort` 七词闭集;上游对表外词 400 响亮拒 ⇒ 本层不预铸第二判官)。档位是请求级的位、与车道无关:交互面 = `/effort` 拨盘,无人值守面 = 档位旗。🔴 端交的必须是**档位词**:「思考 token 数」今天没有上游对位(见 `UPSTREAM_ABSENT_REQUEST_INTENTS` 的 `maxThinkingTokens`),别把一个数字折成档位词发出去', whyUtilityLane: '有座:side-channel 该用显式低档(省钱省时);与 model 同一条理由 —— 本车道上这两位都由壳显式给,不跟用户会话的拨盘走' },
|
|
40
|
+
{ field: 'model', lanes: ['interactive', 'print', 'utility'], live: false, why: '模型选择(sdk `TaskRequest.model`;交互面 = `/model` 中途换模型的拨盘,无人值守面 = 模型旗 / 部署缺省那一路,side-channel 面 = 壳显式挑的那一只)。三条车道都有来源、都是提交面。🔴 0.79.0 改口:修前「print 的模型走另一条路」这句理由被用户面实测证伪 —— 无人值守车道传了模型名却整键不上请求体,于是同一个坏名字在交互态被引擎 400 点名、在无人值守态却安静换成部署缺省并在首帧回显一个没生效的名字。一个**选择**类声明被静默丢掉,后果与收紧类声明被静默丢掉同形(端不读回执就以为生效)—— 与 `excludeTools` 补 print 座同律。本层原样透传、不预判模型名(名字的合法性归引擎,本层不铸第二判官)', whyUtilityLane: '有座:side-channel 显式挑便宜的那只模型(不跟用户会话的 `/model` 拨盘走)' },
|
|
41
|
+
{ field: 'images', lanes: ['interactive'], live: false, why: '贴图提交是交互动作,`-p` 的 stdin 没有图片块', whyUtilityLane: '无座:贴图是交互动作,side-channel 的提交体里没有图片块' },
|
|
42
|
+
{ field: 'resumeAt/resumeAtMode/rewindFiles/rewindFilesTo', lanes: ['interactive'], live: false, why: 'E18 `/rewind` 是交互命令;四键同生同灭(resumeAtMode 是 both/conversation 两臂恒带的排他截语义位)', whyUtilityLane: '无座:回退是交互命令,而 side-channel 是一次性的独立会话,没有可回退的历史' },
|
|
43
|
+
{ field: 'clientContext', lanes: ['interactive'], live: false, why: 'IANA 时区 + 可选邮箱 → core 本地化 `# Environment` 的 today;print 同样跑在用户机器上,缺席让引擎误标 (UTC)', gap: true, whyUtilityLane: '无座:本地化 today 的用途在用户会话的环境段,而 side-channel 的系统提示由壳整段给出 —— 这一列我判不准要不要(与本行 `gap` 同一条待办:要补先单独立项)' },
|
|
44
|
+
{ field: 'scratchpadDir', lanes: ['interactive'], live: true, why: '[816]③ per-session 暂存目录;print 也有 session,缺席让 `-p` 的工具写不进 exemptDir', gap: true, whyUtilityLane: '无座:per-session 暂存目录是给**工具**写的,零工具面上没人写' },
|
|
42
45
|
];
|
|
43
46
|
export const LIVE_DEFAULT_FIELDS = [
|
|
44
47
|
'cwd',
|
|
@@ -58,6 +61,19 @@ export const TASK_REQUEST_OMISSION_CAUSES = Object.freeze([
|
|
|
58
61
|
'off_lane',
|
|
59
62
|
'not_live',
|
|
60
63
|
]);
|
|
64
|
+
const TASK_REQUEST_OMISSION_CAUSE_SENTENCES = Object.freeze({
|
|
65
|
+
upstream_absent: 'this build knows the intent but the engine has no request field for it, so nothing was sent and nothing will be until that field exists upstream — retire the knob, or wait for the upstream field',
|
|
66
|
+
other_channel: 'this is a real request field, but the request assembler never sets it — it belongs to the live-defaults layer, so hand the value to that layer instead of to the assembler',
|
|
67
|
+
off_lane: 'this request lane has no seat for the key, so the value was not sent — the lane table records why that lane does not carry it; submit on a lane that seats it, or stop passing it here',
|
|
68
|
+
not_live: 'the key has a seat on this lane but only reaches the wire on a live submission, and this submission was not live — nothing is wrong here: a mock or fixture submission keeps its request shape fixed',
|
|
69
|
+
});
|
|
70
|
+
export function taskRequestOmissionCauseDetail(cause) {
|
|
71
|
+
if (typeof cause === 'string' && Object.hasOwn(TASK_REQUEST_OMISSION_CAUSE_SENTENCES, cause)) {
|
|
72
|
+
return TASK_REQUEST_OMISSION_CAUSE_SENTENCES[cause];
|
|
73
|
+
}
|
|
74
|
+
const word = typeof cause === 'string' && cause.length > 0 ? cause : '(none)';
|
|
75
|
+
return `a cause this build does not know (${word}) — this build does not know this cause: receipt causes come from this build's own frozen vocabulary, so a word outside it was not produced here; check whatever supplied it`;
|
|
76
|
+
}
|
|
61
77
|
const row = (field) => ({ via: 'matrix', field });
|
|
62
78
|
const INPUT_KEY_ROUTES = {
|
|
63
79
|
objective: { via: 'always' },
|
|
@@ -88,7 +104,9 @@ const INPUT_KEY_ROUTES = {
|
|
|
88
104
|
interactiveTools: row('interactiveTools'),
|
|
89
105
|
oneShot: row('oneShot'),
|
|
90
106
|
outputSchema: row('outputSchema'),
|
|
107
|
+
outputRetries: row('outputRetries'),
|
|
91
108
|
maxCostUsd: row('maxCostUsd'),
|
|
109
|
+
maxTokens: row('maxTokens'),
|
|
92
110
|
taskBudget: {
|
|
93
111
|
via: 'fixed',
|
|
94
112
|
cause: 'upstream_absent',
|
|
@@ -106,6 +124,7 @@ const INPUT_KEY_ROUTES = {
|
|
|
106
124
|
},
|
|
107
125
|
memoryCapture: row('memoryCapture'),
|
|
108
126
|
memory: row('memory'),
|
|
127
|
+
approverPosture: row('approverPosture'),
|
|
109
128
|
rewind: row('resumeAt/resumeAtMode/rewindFiles/rewindFilesTo'),
|
|
110
129
|
repo: row('repo'),
|
|
111
130
|
council: row('council'),
|
|
@@ -153,7 +172,8 @@ const OMISSION_JUDGES = {
|
|
|
153
172
|
not_live: (s, _lane, live) => s.via === 'matrix' && s.spec.live && !live,
|
|
154
173
|
};
|
|
155
174
|
const omissionCauseOf = (s, lane, live) => TASK_REQUEST_OMISSION_CAUSES.find(cause => OMISSION_JUDGES[cause](s, lane, live));
|
|
156
|
-
const REQUEST_LANE_SET = { interactive: true, print: true };
|
|
175
|
+
const REQUEST_LANE_SET = { interactive: true, print: true, utility: true };
|
|
176
|
+
export const REQUEST_LANES = Object.freeze(Object.keys(REQUEST_LANE_SET));
|
|
157
177
|
const refuseBadLane = (lane) => {
|
|
158
178
|
if (typeof lane === 'string' && hasOwn(REQUEST_LANE_SET, lane))
|
|
159
179
|
return;
|
|
@@ -176,6 +196,7 @@ const VALUE_LEVEL_IN_PLAY = Object.freeze({
|
|
|
176
196
|
excludeAllTools: (v) => v === true,
|
|
177
197
|
memoryCapture: (v) => v === 'off',
|
|
178
198
|
memory: (v) => v === 'off',
|
|
199
|
+
approverPosture: (v) => v === APPROVER_POSTURE_BYPASS,
|
|
179
200
|
rewind: (v) => typeof v === 'object' && v !== null && rewindSubKeys().some((k) => hasOwn(v, k) && present(v[k])),
|
|
180
201
|
});
|
|
181
202
|
const inPlay = (key, v) => present(v) && (VALUE_LEVEL_IN_PLAY[key]?.(v) ?? true);
|
|
@@ -350,6 +371,12 @@ export const MEMORY_OFF = 'off';
|
|
|
350
371
|
export function memoryOffDeclarationField(declared) {
|
|
351
372
|
return declared ? { memory: MEMORY_OFF } : {};
|
|
352
373
|
}
|
|
374
|
+
export const APPROVER_POSTURE_BYPASS = 'bypass';
|
|
375
|
+
void APPROVER_POSTURE_BYPASS;
|
|
376
|
+
void true;
|
|
377
|
+
export function approverPostureDeclarationField(declared) {
|
|
378
|
+
return declared ? { approverPosture: APPROVER_POSTURE_BYPASS } : {};
|
|
379
|
+
}
|
|
353
380
|
export function memoryOffDeclared(input) {
|
|
354
381
|
if (typeof input !== 'object' || input === null)
|
|
355
382
|
return false;
|
|
@@ -389,6 +416,48 @@ const refuseBadExcludeAllTools = (v) => {
|
|
|
389
416
|
`收到 ${coarseShape(v)}。这一位是**整只工具面卸载**的收紧声明:本层若把坏值静默删键,这一次运行会挂上声明人以为已经卸掉的全部工具。` +
|
|
390
417
|
'错误文本只报形状,不回显值。');
|
|
391
418
|
};
|
|
419
|
+
const refuseBadApproverPosture = (v) => {
|
|
420
|
+
if (v === undefined || v === null)
|
|
421
|
+
return;
|
|
422
|
+
if (v === APPROVER_POSTURE_BYPASS)
|
|
423
|
+
return;
|
|
424
|
+
throw new TypeError('buildTaskRequest: approverPosture 只接受字面量 `\'bypass\'` 或缺席(undefined/null)—— ' +
|
|
425
|
+
`收到 ${coarseShape(v)}。这一位是**审批姿态声明**,上游是**单成员闭集**(没有第二个词)且对表外词` +
|
|
426
|
+
'**响亮拒**,刻意不把打错的词折回缺席。本层若静默删键,这一次提交的模型不会被告知姿态,而声明人不会知道。' +
|
|
427
|
+
'错误文本只报形状,不回显值。');
|
|
428
|
+
};
|
|
429
|
+
const refuseBadMaxTokens = (v) => {
|
|
430
|
+
if (v === undefined || v === null)
|
|
431
|
+
return;
|
|
432
|
+
if (typeof v === 'number' && Number.isInteger(v) && v > 0)
|
|
433
|
+
return;
|
|
434
|
+
throw new TypeError(`buildTaskRequest: maxTokens 只接受有限正整数或缺席(undefined/null)—— 收到 ${coarseShape(v)}。` +
|
|
435
|
+
'这一位是 token 预算上限声明(收紧方向),而上游对坏值(非数 / 非正)不是 4xx,是按「没给」处理并回落' +
|
|
436
|
+
'部署上限:部署没设上限时这一次运行就没有 token 闸,而声明人不会知道。数字串请先在端侧解析成整数。' +
|
|
437
|
+
'错误文本只报形状,不回显值。');
|
|
438
|
+
};
|
|
439
|
+
const refuseBadOutputRetries = (v) => {
|
|
440
|
+
if (v === undefined || v === null)
|
|
441
|
+
return;
|
|
442
|
+
if (typeof v === 'number' && Number.isInteger(v) && v > 0)
|
|
443
|
+
return;
|
|
444
|
+
throw new TypeError(`buildTaskRequest: outputRetries 只接受有限正整数或缺席(undefined/null)—— 收到 ${coarseShape(v)}。` +
|
|
445
|
+
'上游对表外值(非数 / 非有限 / 小于 1)整键省略并回落引擎缺省,不是 4xx:调用方以为自己调了轮数,' +
|
|
446
|
+
'而这一次运行用的是缺省。`0` 同样拒 —— 在上游它是「省略」不是「零次重试」。本层不预夹上限(那是上游的事)。' +
|
|
447
|
+
'错误文本只报形状,不回显值。');
|
|
448
|
+
};
|
|
449
|
+
const refuseUtilityWithoutToolUnload = (lane, excludeAllTools) => {
|
|
450
|
+
if (lane !== 'utility')
|
|
451
|
+
return;
|
|
452
|
+
if (excludeAllTools === true)
|
|
453
|
+
return;
|
|
454
|
+
throw new TypeError('buildTaskRequest: utility 车道必须带字面量 `excludeAllTools: true` —— ' +
|
|
455
|
+
`收到 ${coarseShape(excludeAllTools)}。这条车道上每一条「无座」判断都压在同一个前提上:它是**零工具面**的提交。` +
|
|
456
|
+
'尤其已解析 settings 快照那一行(用户显式的 permissions.deny / ask 就在里面)无座的理由就是' +
|
|
457
|
+
'「零工具面没有工具门求值权限」—— 前提不成立时,产出就是一条**既没卸工具、又没带用户 deny** 的请求,' +
|
|
458
|
+
'比无人值守车道同一份输入更宽,而且没有任何人会知道。处置:补上这一位;不认这一键的老上游会把整条提交 400 拒掉,' +
|
|
459
|
+
'那时正解是换车道或不发,**不是**在这条车道上摘掉它。错误文本只报形状,不回显值。');
|
|
460
|
+
};
|
|
392
461
|
const refuseBadMemoryCapture = (v) => {
|
|
393
462
|
if (v === undefined || v === null)
|
|
394
463
|
return;
|
|
@@ -410,30 +479,100 @@ const refuseBadMaxCostUsd = (v) => {
|
|
|
410
479
|
'这一位是花费上限声明(收紧方向),而上游对坏值(非数 / 非正)不是 4xx,是按「没给」处理并回落部署上限:' +
|
|
411
480
|
'部署没设上限时这一次运行就没有花费闸,而声明人不会知道。数字串请先在端侧解析成数。错误文本只报形状,不回显值。');
|
|
412
481
|
};
|
|
482
|
+
const DECIMAL_AMOUNT = /^\d+(?:\.\d+)?$/;
|
|
483
|
+
export function parseMaxCostUsd(text) {
|
|
484
|
+
if (typeof text !== 'string')
|
|
485
|
+
return { ok: false, why: 'not_text' };
|
|
486
|
+
const trimmed = text.trim();
|
|
487
|
+
if (trimmed.length === 0)
|
|
488
|
+
return { ok: false, why: 'empty' };
|
|
489
|
+
if (!DECIMAL_AMOUNT.test(trimmed))
|
|
490
|
+
return { ok: false, why: 'not_a_number' };
|
|
491
|
+
const value = Number(trimmed);
|
|
492
|
+
if (!Number.isFinite(value))
|
|
493
|
+
return { ok: false, why: 'not_finite' };
|
|
494
|
+
if (value <= 0)
|
|
495
|
+
return { ok: false, why: 'not_positive' };
|
|
496
|
+
return { ok: true, value };
|
|
497
|
+
}
|
|
498
|
+
const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'settings', 'maxCostUsd', 'excludeAllTools', 'approverPosture', 'maxTokens', 'outputRetries'];
|
|
499
|
+
function dataFirstSnapshot(input, afterData) {
|
|
500
|
+
let descs;
|
|
501
|
+
try {
|
|
502
|
+
descs = Object.getOwnPropertyDescriptors(input);
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
throw new TypeError('buildTaskRequest: 入参的键集 / 属性描述符读不出(代理 trap 抛)—— 无法安全快照声明,拒绝装配。错误文本只报形状,不回显值。');
|
|
506
|
+
}
|
|
507
|
+
const bag = Object.create(null);
|
|
508
|
+
const accessors = [];
|
|
509
|
+
for (const k of Object.keys(descs)) {
|
|
510
|
+
const d = descs[k];
|
|
511
|
+
if (d === undefined || d.enumerable !== true)
|
|
512
|
+
continue;
|
|
513
|
+
if ('value' in d)
|
|
514
|
+
bag[k] = d.value;
|
|
515
|
+
else
|
|
516
|
+
accessors.push(k);
|
|
517
|
+
}
|
|
518
|
+
afterData?.(bag);
|
|
519
|
+
const ordered = [
|
|
520
|
+
...ACCESSOR_READ_ORDER.filter((k) => accessors.includes(k)),
|
|
521
|
+
...accessors.filter((k) => !ACCESSOR_READ_ORDER.includes(k)),
|
|
522
|
+
];
|
|
523
|
+
for (const k of ordered)
|
|
524
|
+
bag[k] = Reflect.get(input, k);
|
|
525
|
+
return bag;
|
|
526
|
+
}
|
|
413
527
|
export function assembleTaskRequest(input, lane) {
|
|
414
528
|
refuseBadLane(lane);
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
529
|
+
let resolvedInEarly;
|
|
530
|
+
let resolvedMaterialized;
|
|
531
|
+
let resolvedMaterializeError;
|
|
532
|
+
let resolvedHookRan = false;
|
|
533
|
+
const src = dataFirstSnapshot(input, (bag) => {
|
|
534
|
+
const st = bag['settings'];
|
|
535
|
+
if (present(st) && typeof st === 'object' && !Array.isArray(st)) {
|
|
536
|
+
resolvedHookRan = true;
|
|
537
|
+
resolvedInEarly = st.resolved;
|
|
538
|
+
try {
|
|
539
|
+
resolvedMaterialized = resolvedSnapshotForWire(resolvedInEarly);
|
|
540
|
+
}
|
|
541
|
+
catch (e) {
|
|
542
|
+
resolvedMaterializeError = e;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
const live = src.live;
|
|
547
|
+
const memoryCaptureIn = src.memoryCapture;
|
|
548
|
+
const memoryIn = src.memory;
|
|
549
|
+
const settingsIn = src.settings;
|
|
419
550
|
if (present(settingsIn) && (typeof settingsIn !== 'object' || Array.isArray(settingsIn))) {
|
|
420
551
|
throw new TypeError(`buildTaskRequest: settings 只接受对象或缺席(undefined/null)—— 收到 ${coarseShape(settingsIn)}。` +
|
|
421
552
|
'这一位装着已解析权限面与 hooks:把畸形容器当成「没给」= 请求带着被剥掉的权限面照发。错误文本只报形状,不回显值。');
|
|
422
553
|
}
|
|
423
554
|
const settingsObj = present(settingsIn) ? settingsIn : {};
|
|
424
|
-
const resolvedIn = settingsObj.resolved;
|
|
555
|
+
const resolvedIn = resolvedHookRan ? resolvedInEarly : settingsObj.resolved;
|
|
425
556
|
const resolvedRoute = SETTINGS_KEY_ROUTES.resolved;
|
|
426
557
|
const resolvedOpen = resolvedRoute.via === 'matrix' && omissionCauseOf(standingOf(resolvedRoute), lane, live) === undefined;
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
558
|
+
if (resolvedOpen && resolvedHookRan && resolvedMaterializeError !== undefined)
|
|
559
|
+
throw resolvedMaterializeError;
|
|
560
|
+
const resolvedWire = resolvedOpen ? (resolvedMaterialized ?? resolvedSnapshotForWire(resolvedIn)) : {};
|
|
561
|
+
const maxCostUsdIn = src.maxCostUsd;
|
|
562
|
+
const excludeAllToolsIn = src.excludeAllTools;
|
|
563
|
+
const approverPostureIn = src.approverPosture;
|
|
564
|
+
const maxTokensIn = src.maxTokens;
|
|
565
|
+
const outputRetriesIn = src.outputRetries;
|
|
430
566
|
const INPUT_KEYS = Object.keys(INPUT_KEY_ROUTES);
|
|
431
|
-
const i = snapshotByName(
|
|
567
|
+
const i = snapshotByName(src, INPUT_KEYS, {
|
|
432
568
|
live,
|
|
433
569
|
memoryCapture: memoryCaptureIn,
|
|
434
570
|
memory: memoryIn,
|
|
435
571
|
maxCostUsd: maxCostUsdIn,
|
|
572
|
+
maxTokens: maxTokensIn,
|
|
436
573
|
excludeAllTools: excludeAllToolsIn,
|
|
574
|
+
approverPosture: approverPostureIn,
|
|
575
|
+
outputRetries: outputRetriesIn,
|
|
437
576
|
settings: settingsIn,
|
|
438
577
|
});
|
|
439
578
|
const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
|
|
@@ -442,6 +581,9 @@ export function assembleTaskRequest(input, lane) {
|
|
|
442
581
|
refuseBadMemoryOff(memoryIn);
|
|
443
582
|
refuseBadMaxCostUsd(maxCostUsdIn);
|
|
444
583
|
refuseBadExcludeAllTools(excludeAllToolsIn);
|
|
584
|
+
refuseBadApproverPosture(approverPostureIn);
|
|
585
|
+
refuseBadMaxTokens(maxTokensIn);
|
|
586
|
+
refuseBadOutputRetries(outputRetriesIn);
|
|
445
587
|
const rewindRoute = INPUT_KEY_ROUTES.rewind;
|
|
446
588
|
const REWIND_KEYS = rewindRoute.via === 'matrix' ? rewindRoute.field.split('/') : [];
|
|
447
589
|
const rewindIn = i.rewind;
|
|
@@ -465,10 +607,10 @@ export function assembleTaskRequest(input, lane) {
|
|
|
465
607
|
};
|
|
466
608
|
const unknownKeys = [];
|
|
467
609
|
const lateOmitted = [];
|
|
468
|
-
for (const key of Object.keys(
|
|
610
|
+
for (const key of Object.keys(src)) {
|
|
469
611
|
if (hasOwn(INPUT_KEY_ROUTES, key))
|
|
470
612
|
continue;
|
|
471
|
-
const stray = Reflect.get(
|
|
613
|
+
const stray = Reflect.get(src, key);
|
|
472
614
|
if (!present(stray))
|
|
473
615
|
continue;
|
|
474
616
|
const route = routeOfTopLevelKey(key);
|
|
@@ -508,6 +650,7 @@ export function assembleTaskRequest(input, lane) {
|
|
|
508
650
|
'想问某个键有没有座 ⇒ taskRequestSeatOf(key, lane)。`settings` 快照里的开放集子键放进 settings.resolved。' +
|
|
509
651
|
'错误文本只报键名,不回显值。');
|
|
510
652
|
}
|
|
653
|
+
refuseUtilityWithoutToolUnload(lane, excludeAllToolsIn);
|
|
511
654
|
const carries = (route, value, key) => {
|
|
512
655
|
if (route.via === 'always')
|
|
513
656
|
return present(value);
|
|
@@ -574,9 +717,12 @@ export function assembleTaskRequest(input, lane) {
|
|
|
574
717
|
...(on('interactiveTools') ? { interactiveTools: false } : {}),
|
|
575
718
|
...(on('oneShot') ? { oneShot: true } : {}),
|
|
576
719
|
...(on('outputSchema') ? { outputSchema: i.outputSchema } : {}),
|
|
720
|
+
...(on('outputRetries') ? { outputRetries: i.outputRetries } : {}),
|
|
577
721
|
...(on('maxCostUsd') ? { maxCostUsd: maxCostUsdIn } : {}),
|
|
722
|
+
...(on('maxTokens') ? { maxTokens: i.maxTokens } : {}),
|
|
578
723
|
...(on('memoryCapture') ? { memoryCapture: 'off' } : {}),
|
|
579
724
|
...(on('memory') ? { memory: 'off' } : {}),
|
|
725
|
+
...(on('approverPosture') ? { approverPosture: APPROVER_POSTURE_BYPASS } : {}),
|
|
580
726
|
...(Object.keys(settingsOut).length > 0 ? { settings: settingsOut } : {}),
|
|
581
727
|
};
|
|
582
728
|
return Object.freeze({ request: out, omitted: Object.freeze([...omitted, ...lateOmitted]) });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { RegistryClient } from '@sema-agent/sdk/registry';
|
|
2
|
+
export { getMeConfig, getScopeConfigDraft, getEffective } from '@sema-agent/sdk/registry';
|
|
3
|
+
export { probeRegistryHealth, postFeedback } from '@sema-agent/sdk/registry';
|
|
4
|
+
export { REGISTRY_AUTH_PATHS, skillContentAddress } from '@sema-agent/sdk/registry';
|
|
5
|
+
export { RegistryApiError, OAuthFlowError } from '@sema-agent/sdk/registry';
|
|
6
|
+
export type { RegistryTokens, TokenProvider, } from '@sema-agent/sdk/registry';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { RegistryClient } from '@sema-agent/sdk/registry';
|
|
2
|
+
export { getMeConfig, getScopeConfigDraft, getEffective } from '@sema-agent/sdk/registry';
|
|
3
|
+
export { probeRegistryHealth, postFeedback } from '@sema-agent/sdk/registry';
|
|
4
|
+
export { REGISTRY_AUTH_PATHS, skillContentAddress } from '@sema-agent/sdk/registry';
|
|
5
|
+
export { RegistryApiError, OAuthFlowError } from '@sema-agent/sdk/registry';
|
package/dist/sdkWireTransit.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { AgentClient } from '@sema-agent/sdk';
|
|
2
2
|
export { isApprovalRequestFrameV1, isApprovalRevokeFrameV1 } from '@sema-agent/sdk';
|
|
3
|
-
export
|
|
3
|
+
export { pushSessionBundle, pullSessionBundle, importSessionBundle, APIError } from '@sema-agent/sdk';
|
|
4
|
+
export type { TaskRequest, SessionPermissionRules, SemaSettings, SkillSpec, AgentEvent, RunReceipt, CancelAck, RunRecord, ToolApprovalRespondAck, FleetTaskRow, FleetWorkflowRow, ApprovalCard, ApprovalRequestFrame, ApprovalRiskAxes, AskDecisionAck, AskDecisionBody, PolicyInfo, CcImportLayer, CcImportPrepareResult, CcImportRedeemResult, CcImportSettingsLayer, CcImportSkippedRule, PersistedRule, RuleRevokeRequest, RuleRevokeResult, } from '@sema-agent/sdk';
|
package/dist/sdkWireTransit.js
CHANGED
package/dist/toolResult.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export declare const PROTOCOL_MARKERS: {
|
|
|
7
7
|
readonly bashStderrSep: "\n--- stderr ---\n";
|
|
8
8
|
readonly bashEmptySentinel: "(empty)";
|
|
9
9
|
readonly untrustedFence: RegExp;
|
|
10
|
+
readonly delegationReportHead: "[Sub-agent report";
|
|
11
|
+
readonly delegationResultLabel: "result:";
|
|
12
|
+
readonly delegationPartialFindingsLabel: "partial findings (the child's last assistant text before it stopped):";
|
|
13
|
+
readonly delegationNoTextResult: "(no text result)";
|
|
14
|
+
readonly delegationTrailer: "Verify this result before concluding. If it leaves more to do, continue or delegate again.";
|
|
15
|
+
readonly delegationTrailerFork: "Verify this result before concluding. If it leaves more to do, continue the work yourself or fork again.";
|
|
10
16
|
readonly taskOutputLabel: RegExp;
|
|
11
17
|
readonly taskOutputStatus: RegExp;
|
|
12
18
|
readonly taskOutputNoneSentinel: "(none)";
|
|
@@ -33,11 +39,19 @@ export type DisplayBody = {
|
|
|
33
39
|
fenced: true;
|
|
34
40
|
label: string;
|
|
35
41
|
body: string;
|
|
42
|
+
frame?: DelegationReportFrame;
|
|
36
43
|
} | {
|
|
37
44
|
fenced: false;
|
|
38
45
|
body: string;
|
|
46
|
+
frame?: DelegationReportFrame;
|
|
39
47
|
};
|
|
40
|
-
export
|
|
48
|
+
export type DisplayBodyOrigin = 'any_tool_result' | 'delegation_report';
|
|
49
|
+
export type DelegationReportFrame = {
|
|
50
|
+
before: string;
|
|
51
|
+
after: string;
|
|
52
|
+
section: 'result' | 'partial_findings' | 'no_text_result';
|
|
53
|
+
};
|
|
54
|
+
export declare function displayBody(text: unknown, origin?: DisplayBodyOrigin): DisplayBody | undefined;
|
|
41
55
|
export type ModelFacingTaskOutput = {
|
|
42
56
|
kind: 'bash';
|
|
43
57
|
taskId?: string | undefined;
|