@sema-agent/client-core 0.85.2 → 0.86.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/README.md +20 -13
  3. package/dist/adapt/arms.js +5 -1
  4. package/dist/adapter/downstream/terminalToSdkResult.d.ts +3 -3
  5. package/dist/agentSession/backgroundView.js +7 -1
  6. package/dist/agentSession/contract.d.ts +1 -1
  7. package/dist/centerWiringCapability.d.ts +38 -0
  8. package/dist/centerWiringCapability.js +202 -0
  9. package/dist/decideFailureNote.js +3 -0
  10. package/dist/engineCapReader.d.ts +8 -0
  11. package/dist/engineCapReader.js +15 -0
  12. package/dist/engineErrorCodes.d.ts +3 -0
  13. package/dist/engineErrorCodes.js +8 -0
  14. package/dist/engineNoticeCodes.d.ts +62 -0
  15. package/dist/engineNoticeCodes.js +247 -29
  16. package/dist/executionLaneCapability.d.ts +6 -0
  17. package/dist/executionLaneCapability.js +55 -2
  18. package/dist/fleet/fleetProjection.js +6 -8
  19. package/dist/fleet/workflowSizeWarning.js +2 -5
  20. package/dist/gateOutcome.d.ts +13 -0
  21. package/dist/gateOutcome.js +48 -1
  22. package/dist/gateVocabulary.d.ts +6 -3
  23. package/dist/gateVocabulary.js +7 -15
  24. package/dist/generated/engineFactTables.d.ts +10 -0
  25. package/dist/generated/engineFactTables.js +113 -0
  26. package/dist/generated/engineNoticeTables.js +14 -0
  27. package/dist/hitl/refusedToolNameProse.d.ts +4 -0
  28. package/dist/hitl/refusedToolNameProse.js +7 -0
  29. package/dist/hitl/sessionPolicyDeliverable.js +9 -5
  30. package/dist/hitl/sessionPolicyWire.d.ts +16 -0
  31. package/dist/hitl/sessionPolicyWire.js +49 -1
  32. package/dist/index.d.ts +5 -1
  33. package/dist/index.js +5 -1
  34. package/dist/inheritEnvWire.d.ts +22 -0
  35. package/dist/inheritEnvWire.js +84 -0
  36. package/dist/peerFrames.js +2 -10
  37. package/dist/printInitToolFace.d.ts +6 -0
  38. package/dist/printInitToolFace.js +115 -2
  39. package/dist/readCredentialRefusal.d.ts +2 -0
  40. package/dist/readCredentialRefusal.js +7 -0
  41. package/dist/request/taskRequest.d.ts +4 -5
  42. package/dist/request/taskRequest.js +33 -23
  43. package/dist/runTerminal.js +2 -6
  44. package/dist/sqlEngineCapability.js +15 -2
  45. package/dist/storePostureCapability.d.ts +25 -0
  46. package/dist/storePostureCapability.js +132 -0
  47. package/dist/toolResult.js +2 -47
  48. package/dist/wireErrorTriage.d.ts +1 -0
  49. package/dist/wireErrorTriage.js +3 -0
  50. package/dist/wireFailureShape.d.ts +3 -0
  51. package/dist/wireFailureShape.js +44 -0
  52. package/dist/wireRefusalCopy.d.ts +39 -1
  53. package/dist/wireRefusalCopy.js +350 -2
  54. package/dist/workflowClient.d.ts +4 -0
  55. package/dist/workflowClient.js +12 -4
  56. package/docs/INTEGRATION-CLIENTS.md +716 -14
  57. package/package.json +2 -2
@@ -28,6 +28,12 @@ export declare function printInitRosterSignal(message: SDKMessage): PrintInitRos
28
28
  export declare function decidePrintInitToolFace(signal: Exclude<PrintInitRosterSignal, {
29
29
  kind: 'pending';
30
30
  }>, fallback: LiveInitToolFace | null): PrintInitToolFace | null;
31
+ export type PrintInitToolsSource = 'engine-roster' | 'estimate' | 'host-static';
32
+ export interface PrintInitToolFaceSupersetKeys {
33
+ _sema_tools_source: PrintInitToolsSource;
34
+ _sema_tools_fallback_reason?: PrintInitRosterFallbackReason;
35
+ }
36
+ export declare function printInitToolFaceSupersetKeys(face: PrintInitToolFace | null): PrintInitToolFaceSupersetKeys;
31
37
  export declare function printInitToolFaceDebugLine(face: PrintInitToolFace | null): string;
32
38
  export type PrintInitGateStep = {
33
39
  kind: 'hold';
@@ -36,8 +36,9 @@ export function decidePrintInitToolFace(signal, fallback) {
36
36
  const tools = fallback === null ? undefined : fallback.tools;
37
37
  if (fallback === null || !Array.isArray(tools))
38
38
  return null;
39
+ const assembly = fallback.source;
39
40
  return {
40
- source: fallback.source,
41
+ source: assembly === 'scenario-probe' ? 'scenario-probe' : 'wire-vocabulary',
41
42
  tools: tools.flatMap((t) => {
42
43
  const name = typeof t === 'object' && t !== null ? t.name : undefined;
43
44
  return typeof name === 'string' ? [{ name }] : [];
@@ -45,6 +46,116 @@ export function decidePrintInitToolFace(signal, fallback) {
45
46
  fallbackReason: signal.reason,
46
47
  };
47
48
  }
49
+ const TOOLS_SOURCE_KEY = '_sema_tools_source';
50
+ const TOOLS_FALLBACK_REASON_KEY = '_sema_tools_fallback_reason';
51
+ const FALLBACK_REASONS = ['manifest-without-roster', 'model-activity-first', 'run-ended', 'wait-timeout'];
52
+ export function printInitToolFaceSupersetKeys(face) {
53
+ const f = face;
54
+ if (typeof f !== 'object' || f === null)
55
+ return { _sema_tools_source: 'host-static' };
56
+ let source;
57
+ try {
58
+ source = f.source;
59
+ }
60
+ catch {
61
+ source = undefined;
62
+ }
63
+ let reason;
64
+ let reasonRead = true;
65
+ try {
66
+ reason = f.fallbackReason;
67
+ }
68
+ catch {
69
+ reason = undefined;
70
+ reasonRead = false;
71
+ }
72
+ if (source === 'engine-roster' && reasonRead && reason === undefined)
73
+ return { _sema_tools_source: 'engine-roster' };
74
+ return typeof reason === 'string' && FALLBACK_REASONS.includes(reason)
75
+ ? { _sema_tools_source: 'estimate', _sema_tools_fallback_reason: reason }
76
+ : { _sema_tools_source: 'estimate' };
77
+ }
78
+ const PROTO_WALK_MAX = 64;
79
+ function snapshotStampKeys(rec) {
80
+ try {
81
+ const saved = [];
82
+ for (const key of [TOOLS_SOURCE_KEY, TOOLS_FALLBACK_REASON_KEY]) {
83
+ let own;
84
+ let o = rec;
85
+ for (let depth = 0; o !== null; depth++) {
86
+ if (depth >= PROTO_WALK_MAX)
87
+ return undefined;
88
+ const d = Reflect.getOwnPropertyDescriptor(o, key);
89
+ if (d !== undefined) {
90
+ if ('get' in d || 'set' in d)
91
+ return undefined;
92
+ if (depth === 0)
93
+ own = d;
94
+ break;
95
+ }
96
+ o = Reflect.getPrototypeOf(o);
97
+ }
98
+ saved.push({ key, own });
99
+ }
100
+ return { keys: saved, extensible: Reflect.isExtensible(rec) };
101
+ }
102
+ catch {
103
+ return undefined;
104
+ }
105
+ }
106
+ function restoreStampKeys(rec, saved) {
107
+ let clean = true;
108
+ for (const s of saved) {
109
+ try {
110
+ const now = Reflect.getOwnPropertyDescriptor(rec, s.key);
111
+ let ok = true;
112
+ if (s.own !== undefined) {
113
+ if (now === undefined)
114
+ ok = Reflect.defineProperty(rec, s.key, s.own);
115
+ else if (!('value' in now) || !Object.is(now.value, s.own.value))
116
+ ok = Reflect.set(rec, s.key, s.own.value);
117
+ }
118
+ else if (now !== undefined) {
119
+ ok = Reflect.deleteProperty(rec, s.key);
120
+ }
121
+ if (!ok)
122
+ clean = false;
123
+ }
124
+ catch {
125
+ clean = false;
126
+ }
127
+ }
128
+ return clean;
129
+ }
130
+ function stampPrintInitToolFaceKeys(frame, face) {
131
+ if (typeof frame !== 'object' || frame === null || Array.isArray(frame))
132
+ return;
133
+ const keys = printInitToolFaceSupersetKeys(face);
134
+ const rec = frame;
135
+ const snap = snapshotStampKeys(rec);
136
+ if (snap === undefined)
137
+ return;
138
+ const saved = snap.keys;
139
+ const hadSource = saved[0]?.own !== undefined;
140
+ const hadReason = saved[1]?.own !== undefined;
141
+ const reason = keys._sema_tools_fallback_reason;
142
+ if (!snap.extensible && (!hadSource || (reason !== undefined && !hadReason)))
143
+ return;
144
+ const writeSource = () => Reflect.set(rec, TOOLS_SOURCE_KEY, keys._sema_tools_source);
145
+ const writeReason = () => reason !== undefined ? Reflect.set(rec, TOOLS_FALLBACK_REASON_KEY, reason) : !hadReason || Reflect.deleteProperty(rec, TOOLS_FALLBACK_REASON_KEY);
146
+ const steps = reason === undefined && hadReason && !hadSource ? [writeReason, writeSource] : [writeSource, writeReason];
147
+ try {
148
+ for (const step of steps) {
149
+ if (!step()) {
150
+ restoreStampKeys(rec, saved);
151
+ return;
152
+ }
153
+ }
154
+ }
155
+ catch {
156
+ restoreStampKeys(rec, saved);
157
+ }
158
+ }
48
159
  export function printInitToolFaceDebugLine(face) {
49
160
  if (face === null) {
50
161
  return "init tools: the host's static list — no tool roster arrived and no estimate was available";
@@ -121,7 +232,9 @@ export async function* withPrintInitFrame(source, opts) {
121
232
  }
122
233
  catch {
123
234
  }
124
- yield await opts.buildInit(step.face);
235
+ const init = await opts.buildInit(step.face);
236
+ stampPrintInitToolFaceKeys(init, step.face);
237
+ yield init;
125
238
  for (const m of step.messages)
126
239
  yield m;
127
240
  };
@@ -0,0 +1,2 @@
1
+ export declare function isReadCredentialRefusal(e: unknown): boolean;
2
+ export declare const READ_CREDENTIAL_NOT_ACCEPTED_NOTICE = "This engine requires a credential to show this, and this client did not present one it accepts.";
@@ -0,0 +1,7 @@
1
+ import { wireFailureShapeOf } from './wireFailureShape.js';
2
+ const AUTH_UNAUTHORIZED = 'auth.unauthorized';
3
+ export function isReadCredentialRefusal(e) {
4
+ const { status, errorCode } = wireFailureShapeOf(e);
5
+ return status === 401 && (errorCode === undefined || errorCode === '' || errorCode === AUTH_UNAUTHORIZED);
6
+ }
7
+ export const READ_CREDENTIAL_NOT_ACCEPTED_NOTICE = 'This engine requires a credential to show this, and this client did not present one it accepts.';
@@ -48,8 +48,8 @@ export interface TaskRequestInput {
48
48
  maxThinkingTokens?: number;
49
49
  memoryCapture?: 'off';
50
50
  memory?: 'off';
51
- approverPosture?: NonNullable<TaskRequest['approverPosture']>;
52
51
  fileHistory?: 'disabled';
52
+ inheritEnv?: InheritEnvWord;
53
53
  rewind?: Record<string, unknown>;
54
54
  repo?: string;
55
55
  council?: boolean;
@@ -101,11 +101,10 @@ export declare const MEMORY_OFF: "off";
101
101
  export declare function memoryOffDeclarationField(declared: boolean): {
102
102
  memory: 'off';
103
103
  } | Record<string, never>;
104
- export declare const APPROVER_POSTURE_BYPASS: "bypass";
105
- export declare function approverPostureDeclarationField(declared: boolean): {
106
- approverPosture: 'bypass';
107
- } | Record<string, never>;
108
104
  export declare const FILE_HISTORY_DISABLED: "disabled";
105
+ export declare const INHERIT_ENV_WORDS: readonly ["all", "scrub"];
106
+ export type InheritEnvWord = (typeof INHERIT_ENV_WORDS)[number];
107
+ export declare function isInheritEnvWord(v: unknown): v is InheritEnvWord;
109
108
  export declare function memoryOffDeclared(input: {
110
109
  flag?: unknown;
111
110
  settingsMemory?: unknown;
@@ -22,8 +22,8 @@ export const REQUEST_FIELD_MATRIX = [
22
22
  { field: 'lenses', lanes: ['interactive', 'print'], live: true, why: 'code-review 镜头数(server 场景按数字读、非数字回 400;本层型面仍按旧形声明为字符串数组 —— 已知不一致,随该族退役处理)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
23
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
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
25
  { field: 'fileHistory', lanes: ['interactive', 'print'], live: true, why: '「这一次运行别捕获文件历史」的**请求级声明**(server 7.104.0 提交体键,单成员闭集 "disabled";`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs` 同收,子代继承)。两条用户车道都是提交面、都跑在用户的文件上 ⇒ 都能声明;「本会话不捕获」= 每个提交都带(续跑腿重放同一个体)。🔴 只在能力读数 `on-optional` 时发(老 server 对表外键 400),值由片段口 `fileHistoryDeclarationField` 唯一铸出。与 rewind 还原键同交 ⇒ 上游 400 `request.field_conflict`', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),不会改任何文件 ⇒ 没有可捕获的东西;带上它只会在不认识这一键的老 server 上给这条提交多一种 400' },
26
+ { field: 'inheritEnv', lanes: ['interactive', 'print'], live: true, why: '模型驱动的宿主 shell「继承多少用户环境」的**请求级声明**(server 7.106.0 提交体键,两词闭集 "all" / "scrub";`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs` 同收,续跑腿重放同判)。两条用户车道都是提交面、都可能在用户机器上跑 shell ⇒ 都能声明。本层两词原样透传、不按车道 / 部署预判(非 host 车道 × "all" 400、托管 × "all" 422 归上游判)。🔴 缺席 = 缺省清洗且清掉本会话上一条声明 ⇒ 要「本会话整份继承」就每个提交都带。🔴 只在服务端版本 ≥ 7.106.0 时发(老 server 对表外键整条 400),值由片段口 `inheritEnvDeclaration` 唯一铸出', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),没有模型驱动的 shell 去继承环境;带上它只会在不认这一键的老服务端上给这条提交多一种 400' },
27
27
  { field: 'finalVerification', lanes: ['print'], live: true, why: 'P1-1 终验:无人值守车道才需要引擎自证;交互 REPL 由人当场看结果。#106 裁 B(让位+告知)后交互面默认关', whyUtilityLane: '无座:终验让引擎再跑一轮自证,而 side-channel 要的是一次小提交的即时答案;「无人值守才需要引擎自证」这条理由在本车道上让位给「小预算优先」' },
28
28
  { field: 'limits', lanes: ['print', 'utility'], live: true, why: 'P2-3-b:`-p` 的预算护栏(--max-* flag 族),交互 REPL 由人随时 Esc', whyUtilityLane: '有座:side-channel 必须带预算护栏(墙钟 / 输出 token / 轮数)—— 它同样没有人看着,而且没有后续回合可以中断' },
29
29
  { field: 'interactiveTools', lanes: ['print', 'utility'], live: true, why: '[909]B 件3:无人值守 stamp false,从 roster 源头灭掉 AskUserQuestion/plan 门。交互车道 stamp false 等于自废武功', whyUtilityLane: '有座:side-channel 绝不该弹交互问答 / plan 门(没有人在看这条提交)。它与 excludeAllTools 是**两条独立**的收紧声明、在这条车道上**并存**(本车道强制带卸载声明,见 `refuseUtilityWithoutToolUnload`;这一位是它旁边那条「连问都别问」的声明,不是它的降级替身)' },
@@ -137,8 +137,8 @@ const INPUT_KEY_ROUTES = {
137
137
  },
138
138
  memoryCapture: row('memoryCapture'),
139
139
  memory: row('memory'),
140
- approverPosture: row('approverPosture'),
141
140
  fileHistory: row('fileHistory'),
141
+ inheritEnv: row('inheritEnv'),
142
142
  rewind: row('resumeAt/resumeAtMode/restoreFiles/rewindFilesTo'),
143
143
  repo: row('repo'),
144
144
  council: row('council'),
@@ -210,7 +210,6 @@ const VALUE_LEVEL_IN_PLAY = Object.freeze({
210
210
  excludeAllTools: (v) => v === true,
211
211
  memoryCapture: (v) => v === 'off',
212
212
  memory: (v) => v === 'off',
213
- approverPosture: (v) => v === APPROVER_POSTURE_BYPASS,
214
213
  fileHistory: (v) => v === FILE_HISTORY_DISABLED,
215
214
  rewind: (v) => typeof v === 'object' && v !== null && rewindSubKeys().some((k) => hasOwn(v, k) && present(v[k])),
216
215
  });
@@ -386,13 +385,11 @@ export const MEMORY_OFF = 'off';
386
385
  export function memoryOffDeclarationField(declared) {
387
386
  return declared ? { memory: MEMORY_OFF } : {};
388
387
  }
389
- export const APPROVER_POSTURE_BYPASS = 'bypass';
390
- void APPROVER_POSTURE_BYPASS;
391
- void true;
392
- export function approverPostureDeclarationField(declared) {
393
- return declared ? { approverPosture: APPROVER_POSTURE_BYPASS } : {};
394
- }
395
388
  export const FILE_HISTORY_DISABLED = 'disabled';
389
+ export const INHERIT_ENV_WORDS = Object.freeze(['all', 'scrub']);
390
+ export function isInheritEnvWord(v) {
391
+ return typeof v === 'string' && INHERIT_ENV_WORDS.includes(v);
392
+ }
396
393
  export function memoryOffDeclared(input) {
397
394
  if (typeof input !== 'object' || input === null)
398
395
  return false;
@@ -432,15 +429,12 @@ const refuseBadExcludeAllTools = (v) => {
432
429
  `收到 ${coarseShape(v)}。这一位是**整只工具面卸载**的收紧声明:本层若把坏值静默删键,这一次运行会挂上声明人以为已经卸掉的全部工具。` +
433
430
  '错误文本只报形状,不回显值。');
434
431
  };
435
- const refuseBadApproverPosture = (v) => {
436
- if (v === undefined || v === null)
437
- return;
438
- if (v === APPROVER_POSTURE_BYPASS)
439
- return;
440
- throw new TypeError('buildTaskRequest: approverPosture 只接受字面量 `\'bypass\'` 或缺席(undefined/null)—— ' +
441
- `收到 ${coarseShape(v)}。这一位是**审批姿态声明**,上游是**单成员闭集**(没有第二个词)且对表外词` +
442
- '**响亮拒**,刻意不把打错的词折回缺席。本层若静默删键,这一次提交的模型不会被告知姿态,而声明人不会知道。' +
443
- '错误文本只报形状,不回显值。');
432
+ const RETIRED_APPROVER_POSTURE_KEY = 'approverPosture';
433
+ const refuseRetiredApproverPosture = () => {
434
+ throw new TypeError('buildTaskRequest: "approverPosture" 是退役键(引擎 core 7.34.0 起在任务规格入口按名拒收;服务端 7.106.0 起提交体带它整条 400 ' +
435
+ 'request.body_shape,unknownKeys 点名它)。它只是一句渲给模型看的审批姿态声明,从不授权任何东西 —— 删掉它不改变这一次运行被允许做什么。' +
436
+ '本包在装配前拒:在场即拒,任何值同判(undefined / null = 缺席)。处置:删掉这个键,不要改发别的键来顶替它;' +
437
+ '若确实要引擎侧放行审批,那是另一件事:显式给权限模式 permissionMode(那是一项授权,受部署策略管)。错误文本只报键名,不回显值。');
444
438
  };
445
439
  const refuseBadFileHistory = (v) => {
446
440
  if (v === undefined || v === null)
@@ -451,6 +445,15 @@ const refuseBadFileHistory = (v) => {
451
445
  `收到 ${coarseShape(v)}。这一位是「这一次运行别捕获文件历史」的收紧声明,上游是**单成员闭集**(没有第二个词)且对坏值` +
452
446
  '**响亮拒**。本层若静默删键,这一次运行照常捕获而声明人不会知道。错误文本只报形状,不回显值。');
453
447
  };
448
+ const refuseBadInheritEnv = (v) => {
449
+ if (v === undefined || v === null)
450
+ return;
451
+ if (isInheritEnvWord(v))
452
+ return;
453
+ throw new TypeError(`buildTaskRequest: inheritEnv 只接受 ${INHERIT_ENV_WORDS.map((w) => `'${w}'`).join(' / ')} 或缺席(undefined/null)—— 收到 ${coarseShape(v)}。` +
454
+ '这一位是「模型驱动的宿主 shell 继承多少用户环境」的两词闭集声明,上游对词表外的值响亮拒;环境变量名单不是请求值。' +
455
+ '本层若静默删键,这一次运行会按缺省清洗跑、还清掉本会话上一条声明,而声明人不会知道。错误文本只报形状,不回显值。');
456
+ };
454
457
  const refuseBadMaxTokens = (v) => {
455
458
  if (v === undefined || v === null)
456
459
  return;
@@ -520,7 +523,7 @@ export function parseMaxCostUsd(text) {
520
523
  return { ok: false, why: 'not_positive' };
521
524
  return { ok: true, value };
522
525
  }
523
- const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'settings', 'maxCostUsd', 'excludeAllTools', 'approverPosture', 'maxTokens', 'outputRetries', 'fileHistory'];
526
+ const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'fileHistory', 'inheritEnv'];
524
527
  function dataFirstSnapshot(input, afterData) {
525
528
  let descs;
526
529
  try {
@@ -585,10 +588,10 @@ export function assembleTaskRequest(input, lane) {
585
588
  const resolvedWire = resolvedOpen ? (resolvedMaterialized ?? resolvedSnapshotForWire(resolvedIn)) : {};
586
589
  const maxCostUsdIn = src.maxCostUsd;
587
590
  const excludeAllToolsIn = src.excludeAllTools;
588
- const approverPostureIn = src.approverPosture;
589
591
  const maxTokensIn = src.maxTokens;
590
592
  const outputRetriesIn = src.outputRetries;
591
593
  const fileHistoryIn = src.fileHistory;
594
+ const inheritEnvIn = src.inheritEnv;
592
595
  const INPUT_KEYS = Object.keys(INPUT_KEY_ROUTES);
593
596
  const i = snapshotByName(src, INPUT_KEYS, {
594
597
  live,
@@ -597,9 +600,9 @@ export function assembleTaskRequest(input, lane) {
597
600
  maxCostUsd: maxCostUsdIn,
598
601
  maxTokens: maxTokensIn,
599
602
  excludeAllTools: excludeAllToolsIn,
600
- approverPosture: approverPostureIn,
601
603
  outputRetries: outputRetriesIn,
602
604
  fileHistory: fileHistoryIn,
605
+ inheritEnv: inheritEnvIn,
603
606
  settings: settingsIn,
604
607
  });
605
608
  const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
@@ -608,10 +611,10 @@ export function assembleTaskRequest(input, lane) {
608
611
  refuseBadMemoryOff(memoryIn);
609
612
  refuseBadMaxCostUsd(maxCostUsdIn);
610
613
  refuseBadExcludeAllTools(excludeAllToolsIn);
611
- refuseBadApproverPosture(approverPostureIn);
612
614
  refuseBadMaxTokens(maxTokensIn);
613
615
  refuseBadOutputRetries(outputRetriesIn);
614
616
  refuseBadFileHistory(fileHistoryIn);
617
+ refuseBadInheritEnv(inheritEnvIn);
615
618
  const rewindRoute = INPUT_KEY_ROUTES.rewind;
616
619
  const RETIRED_REWIND_FILES_KEY = 'rewindFiles';
617
620
  const REWIND_KEYS = rewindRoute.via === 'matrix' ? rewindRoute.field.split('/') : [];
@@ -636,12 +639,17 @@ export function assembleTaskRequest(input, lane) {
636
639
  };
637
640
  const unknownKeys = [];
638
641
  const lateOmitted = [];
642
+ let retiredApproverPosture = false;
639
643
  for (const key of Object.keys(src)) {
640
644
  if (hasOwn(INPUT_KEY_ROUTES, key))
641
645
  continue;
642
646
  const stray = Reflect.get(src, key);
643
647
  if (!present(stray))
644
648
  continue;
649
+ if (key === RETIRED_APPROVER_POSTURE_KEY) {
650
+ retiredApproverPosture = true;
651
+ continue;
652
+ }
645
653
  const route = routeOfTopLevelKey(key);
646
654
  if (route === undefined) {
647
655
  unknownKeys.push(key);
@@ -672,6 +680,8 @@ export function assembleTaskRequest(input, lane) {
672
680
  retiredRewindFiles = true;
673
681
  unknownKeys.push(`rewind.${sub}`);
674
682
  }
683
+ if (retiredApproverPosture)
684
+ refuseRetiredApproverPosture();
675
685
  if (retiredRewindFiles) {
676
686
  throw new TypeError('buildTaskRequest: "rewind.rewindFiles" 是退役键(引擎 core 6.0.0 起退役:「逐轮捕获」那一义被引擎忽略 —— 文件首触追踪恒开,' +
677
687
  '无需请求;「回退时一并还原文件」那一义被引擎拒收、改名为 restoreFiles)。本包在装配前拒:在场即拒,true / false 同判。' +
@@ -760,8 +770,8 @@ export function assembleTaskRequest(input, lane) {
760
770
  ...(on('maxTokens') ? { maxTokens: i.maxTokens } : {}),
761
771
  ...(on('memoryCapture') ? { memoryCapture: 'off' } : {}),
762
772
  ...(on('memory') ? { memory: 'off' } : {}),
763
- ...(on('approverPosture') ? { approverPosture: APPROVER_POSTURE_BYPASS } : {}),
764
773
  ...(on('fileHistory') ? { fileHistory: FILE_HISTORY_DISABLED } : {}),
774
+ ...(on('inheritEnv') ? { inheritEnv: inheritEnvIn } : {}),
765
775
  ...(Object.keys(settingsOut).length > 0 ? { settings: settingsOut } : {}),
766
776
  };
767
777
  return Object.freeze({ request: out, omitted: Object.freeze([...omitted, ...lateOmitted]) });
@@ -1,3 +1,4 @@
1
+ import { CORE_TERMINAL_CAUSE_KINDS } from './generated/engineFactTables.js';
1
2
  function str(v) {
2
3
  return typeof v === 'string' && v.length > 0 ? v : undefined;
3
4
  }
@@ -130,12 +131,7 @@ export function isReviewPark(read) {
130
131
  return true;
131
132
  return read !== null && read.kind === 'paused' && read.flatStatus === FLAT_REVIEW_STATUS;
132
133
  }
133
- export const TERMINAL_CAUSE_KINDS = Object.freeze([
134
- 'completed',
135
- 'failed',
136
- 'blocked',
137
- 'paused',
138
- ]);
134
+ export const TERMINAL_CAUSE_KINDS = CORE_TERMINAL_CAUSE_KINDS;
139
135
  export function isTerminalCauseKind(kind) {
140
136
  return typeof kind === 'string' && TERMINAL_CAUSE_KINDS.includes(kind);
141
137
  }
@@ -1,11 +1,21 @@
1
1
  import { capForDisplay } from './fleetTaskDesc.js';
2
2
  import { engineWireTarget } from './engineWireTarget.js';
3
- import { createEngineCapReader } from './engineCapReader.js';
3
+ import { capsGenerationOf, createEngineCapReader } from './engineCapReader.js';
4
4
  export function projectSqlEngineCapability(caps) {
5
5
  if (caps === null || typeof caps !== 'object')
6
6
  return undefined;
7
7
  if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
8
8
  return undefined;
9
+ const gen = capsGenerationOf(caps);
10
+ if (gen.generation === 'new') {
11
+ const store = gen.store;
12
+ if (!Object.hasOwn(store, 'sql'))
13
+ return { kind: 'none' };
14
+ const nested = store.sql;
15
+ if (nested === undefined)
16
+ return { kind: 'none' };
17
+ return projectSqlEngineView(nested);
18
+ }
9
19
  if (!Object.hasOwn(caps, 'sql'))
10
20
  return { kind: 'not_reported' };
11
21
  const sql = caps.sql;
@@ -13,7 +23,10 @@ export function projectSqlEngineCapability(caps) {
13
23
  return { kind: 'none' };
14
24
  if (sql === undefined)
15
25
  return { kind: 'not_reported' };
16
- if (typeof sql !== 'object')
26
+ return projectSqlEngineView(sql);
27
+ }
28
+ function projectSqlEngineView(sql) {
29
+ if (sql === null || typeof sql !== 'object')
17
30
  return undefined;
18
31
  const s = sql;
19
32
  const engine = s.engine;
@@ -0,0 +1,25 @@
1
+ import { type SqlEngineCapabilityView } from './sqlEngineCapability.js';
2
+ export interface StorePostureView {
3
+ declared: string;
4
+ backend: string;
5
+ durable: boolean;
6
+ degradedReason?: string;
7
+ sql?: SqlEngineCapabilityView;
8
+ }
9
+ export type StorePostureReading = {
10
+ kind: 'unobserved';
11
+ } | {
12
+ kind: 'not_reported';
13
+ } | {
14
+ kind: 'present';
15
+ view: StorePostureView;
16
+ };
17
+ export declare function projectStorePostureCapability(caps: unknown): StorePostureReading | undefined;
18
+ export declare function noteEngineCapsForStorePosture(baseUrl: string, caps: unknown, opts?: {
19
+ generation?: number;
20
+ }): void;
21
+ export declare function observedStorePosture(baseUrl?: string | undefined): StorePostureReading;
22
+ export declare function storePersistence(reading: StorePostureReading): 'durable' | 'not_durable' | 'unknown';
23
+ export declare function storePostureDoctorDetail(reading: StorePostureReading): string;
24
+ export declare function forgetStorePostureReading(baseUrl: string | undefined): void;
25
+ export declare function __resetStorePostureReadingsForTests(): void;
@@ -0,0 +1,132 @@
1
+ import { capForDisplay } from './fleetTaskDesc.js';
2
+ import { engineWireTarget } from './engineWireTarget.js';
3
+ import { capsGenerationOf, createEngineCapReader } from './engineCapReader.js';
4
+ import { projectSqlEngineCapability } from './sqlEngineCapability.js';
5
+ const nonEmpty = (v) => typeof v === 'string' && v !== '';
6
+ export function projectStorePostureCapability(caps) {
7
+ try {
8
+ return projectStorePostureOf(caps);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ function projectStorePostureOf(caps) {
15
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
16
+ return undefined;
17
+ const gen = capsGenerationOf(caps);
18
+ if (gen.generation === 'old')
19
+ return gen.store === 'absent' ? { kind: 'not_reported' } : undefined;
20
+ const store = gen.store;
21
+ const s = store;
22
+ const declared = s.declared;
23
+ const backend = s.backend;
24
+ const durable = s.durable;
25
+ if (!nonEmpty(declared) || !nonEmpty(backend) || typeof durable !== 'boolean')
26
+ return undefined;
27
+ const hasReason = Object.hasOwn(store, 'degradedReason');
28
+ const reason = hasReason ? s.degradedReason : undefined;
29
+ if (hasReason && reason !== undefined && !nonEmpty(reason))
30
+ return undefined;
31
+ const sqlReading = projectSqlEngineCapability({ store });
32
+ if (sqlReading === undefined)
33
+ return undefined;
34
+ return {
35
+ kind: 'present',
36
+ view: {
37
+ declared,
38
+ backend,
39
+ durable,
40
+ ...(nonEmpty(reason) ? { degradedReason: reason } : {}),
41
+ ...(sqlReading.kind === 'present' ? { sql: sqlReading.view } : {}),
42
+ },
43
+ };
44
+ }
45
+ const reader = createEngineCapReader({
46
+ name: 'store',
47
+ project: projectStorePostureCapability,
48
+ makeUnobserved: () => ({ kind: 'unobserved' }),
49
+ });
50
+ export function noteEngineCapsForStorePosture(baseUrl, caps, opts) {
51
+ reader.note(baseUrl, caps, opts);
52
+ }
53
+ export function observedStorePosture(baseUrl = engineWireTarget()?.baseUrl) {
54
+ return reader.observed(baseUrl);
55
+ }
56
+ function presentView(reading) {
57
+ if (typeof reading !== 'object' || reading === null)
58
+ return undefined;
59
+ try {
60
+ const kind = reading.kind;
61
+ if (kind === 'not_reported' || kind === 'unobserved')
62
+ return { kind };
63
+ if (kind !== 'present')
64
+ return undefined;
65
+ const view = reading.view;
66
+ if (typeof view !== 'object' || view === null)
67
+ return undefined;
68
+ return { kind: 'present', view: view };
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ }
74
+ export function storePersistence(reading) {
75
+ const r = presentView(reading);
76
+ if (r?.kind !== 'present')
77
+ return 'unknown';
78
+ let durable;
79
+ try {
80
+ durable = r.view.durable;
81
+ }
82
+ catch {
83
+ return 'unknown';
84
+ }
85
+ return durable === true ? 'durable' : durable === false ? 'not_durable' : 'unknown';
86
+ }
87
+ const WORD_DETAIL_MAX = 40;
88
+ const word = (v) => capForDisplay(v, WORD_DETAIL_MAX);
89
+ export function storePostureDoctorDetail(reading) {
90
+ const unobserved = 'store posture not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this deployment persists its state is unknown';
91
+ const r = presentView(reading);
92
+ if (r?.kind === 'not_reported') {
93
+ return 'store posture not reported by this engine — only newer engines advertise it; this does not say whether this deployment persists its state';
94
+ }
95
+ if (r?.kind !== 'present')
96
+ return unobserved;
97
+ let declared;
98
+ let backend;
99
+ let durable;
100
+ let reason;
101
+ try {
102
+ declared = r.view.declared;
103
+ backend = r.view.backend;
104
+ durable = r.view.durable;
105
+ reason = r.view.degradedReason;
106
+ }
107
+ catch {
108
+ return unobserved;
109
+ }
110
+ if (!nonEmpty(declared) || !nonEmpty(backend) || typeof durable !== 'boolean')
111
+ return unobserved;
112
+ const assembled = declared === backend ? `store ${word(backend)}` : `store declared ${word(declared)}, running on ${word(backend)}`;
113
+ if (durable) {
114
+ return `${assembled} — durable: this replica persists its state, so it survives a restart`;
115
+ }
116
+ const lost = 'NOT durable: this replica holds its state in memory and loses it on restart';
117
+ if (!nonEmpty(reason))
118
+ return `${assembled} — ${lost}`;
119
+ if (reason === 'db_unreachable') {
120
+ return `${assembled} — ${lost} (degraded: the declared database could not be opened at start-up, so the engine fell back rather than refusing to start)`;
121
+ }
122
+ if (reason === 'local_unwritable') {
123
+ return `${assembled} — ${lost} (degraded: the default local file store could not be created, for example a read-only or full data directory)`;
124
+ }
125
+ return `${assembled} — ${lost} (degraded for a reason this client does not know yet: "${word(reason)}")`;
126
+ }
127
+ export function forgetStorePostureReading(baseUrl) {
128
+ reader.forget(baseUrl);
129
+ }
130
+ export function __resetStorePostureReadingsForTests() {
131
+ reader.__resetForTests();
132
+ }
@@ -3,53 +3,8 @@ import { markEngineWorkflowNotified, registerOutstandingBgTask, registerOutstand
3
3
  import { parseWorkflowPollEnvelope, projectWorkflowTaskOutput } from './workflow.js';
4
4
  import { asModelFallbackReason } from './engineErrorCodes.js';
5
5
  import { frozenSet } from './frozenSet.js';
6
- export const STRUCTURED_DETAIL_TYPES = frozenSet([
7
- 'edit',
8
- 'create',
9
- 'update',
10
- 'bash',
11
- 'notebook-edit',
12
- 'notebook',
13
- 'text',
14
- 'grep',
15
- 'glob',
16
- 'mcp',
17
- 'agent',
18
- 'task',
19
- 'task-list',
20
- 'task-output',
21
- 'workflow-run',
22
- 'web-fetch',
23
- 'web-search',
24
- 'todo',
25
- 'cron-create',
26
- 'cron-delete',
27
- 'cron-list',
28
- 'image',
29
- 'task-stop',
30
- 'tool-search',
31
- 'repo-map',
32
- 'fork',
33
- 'enter-plan-mode',
34
- 'exit-plan-mode',
35
- 'file_unchanged',
36
- 'worktree',
37
- 'monitor-start',
38
- 'path_not_in_root',
39
- 'readonly_out_of_root',
40
- 'report-findings',
41
- 'schedule-wakeup',
42
- 'send-message',
43
- 'agent-transcript',
44
- 'a2a',
45
- 'document',
46
- 'ask-question',
47
- 'bash_invalid_timeout',
48
- 'read_path_denied',
49
- 'list-agents',
50
- 'artifact',
51
- 'read-inbox',
52
- ]);
6
+ import { CORE_ENGINE_CARD_TYPES } from './generated/engineFactTables.js';
7
+ export const STRUCTURED_DETAIL_TYPES = frozenSet(CORE_ENGINE_CARD_TYPES);
53
8
  export function structuredDetailType(structured) {
54
9
  if (typeof structured !== 'object' || structured === null)
55
10
  return undefined;
@@ -6,6 +6,7 @@ export type TurnWireErrorVerdict = {
6
6
  message: string;
7
7
  storedRowWhere?: string;
8
8
  sessionUnreadable?: true;
9
+ leaseUnavailable?: true;
9
10
  } | {
10
11
  kind: 'stream-ended-without-terminal';
11
12
  } | {
@@ -1,5 +1,6 @@
1
1
  import { readErrorNumber, readErrorString, readErrorStringList } from './wireFailureShape.js';
2
2
  import { capForDisplay } from './fleetTaskDesc.js';
3
+ import { fleetLeaseUnavailableFromError } from './wireRefusalCopy.js';
3
4
  import { DRAINING_ERROR_CODE, isCorruptSessionCode, RESUME_AT_ERROR_CODE_PREFIX, RESUME_POLICY_FOLD_CODE, RESUME_RETRY_LATER_CODES, RESUME_USAGE_WINDOW_EXHAUSTED, SCENARIO_NOT_ALLOWED_ERROR_CODE, } from './engineErrorCodes.js';
4
5
  export const WIRE_NETWORK_ERROR_PATTERN = /fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|EHOSTUNREACH|ENETUNREACH|EPIPE|socket hang up|UND_ERR|terminated|other side closed/i;
5
6
  function wireErrorDetail(err) {
@@ -16,6 +17,7 @@ export function classifyTurnWireError(err) {
16
17
  const message = typeof e.message === 'string' && e.message.length > 0 ? e.message : String(err);
17
18
  const storedRowWhere = corruptStoredRowWhere(err);
18
19
  const sessionUnreadable = isCorruptSessionCode(effectiveWireErrorCode(err));
20
+ const leaseUnavailable = fleetLeaseUnavailableFromError(err) !== null;
19
21
  return {
20
22
  kind: 'http',
21
23
  status: e.status,
@@ -23,6 +25,7 @@ export function classifyTurnWireError(err) {
23
25
  message,
24
26
  ...(storedRowWhere !== undefined ? { storedRowWhere } : {}),
25
27
  ...(sessionUnreadable ? { sessionUnreadable: true } : {}),
28
+ ...(leaseUnavailable ? { leaseUnavailable: true } : {}),
26
29
  };
27
30
  }
28
31
  const errName = typeof err === 'object' && err !== null && typeof err.name === 'string'