@sema-agent/client-core 0.3.0 → 0.5.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 (69) hide show
  1. package/README.md +57 -4
  2. package/dist/adapt.d.ts +30 -3
  3. package/dist/adapt.js +490 -354
  4. package/dist/adapter/downstream/eventToSdkMessage.d.ts +81 -0
  5. package/dist/adapter/downstream/eventToSdkMessage.js +330 -0
  6. package/dist/adapter/downstream/terminalToSdkResult.d.ts +71 -0
  7. package/dist/adapter/downstream/terminalToSdkResult.js +370 -0
  8. package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +26 -0
  9. package/dist/adapter/downstream/turnUsageToModelUsage.js +12 -0
  10. package/dist/adapter/runStream.d.ts +38 -0
  11. package/dist/adapter/runStream.js +177 -0
  12. package/dist/adapter/types.d.ts +73 -0
  13. package/dist/adapter/types.js +23 -0
  14. package/dist/agentsWireCaps.d.ts +112 -0
  15. package/dist/agentsWireCaps.js +319 -0
  16. package/dist/attachmentsWireCaps.d.ts +47 -0
  17. package/dist/attachmentsWireCaps.js +46 -0
  18. package/dist/classifierVerdictWire.d.ts +45 -0
  19. package/dist/classifierVerdictWire.js +76 -0
  20. package/dist/clientContextWireCaps.d.ts +46 -0
  21. package/dist/clientContextWireCaps.js +48 -0
  22. package/dist/cloudConfigWireCaps.d.ts +110 -0
  23. package/dist/cloudConfigWireCaps.js +228 -0
  24. package/dist/controlRouter.d.ts +191 -0
  25. package/dist/controlRouter.js +244 -0
  26. package/dist/effortWire.d.ts +34 -0
  27. package/dist/effortWire.js +38 -0
  28. package/dist/engineWireSdk.d.ts +49 -0
  29. package/dist/engineWireSdk.js +61 -0
  30. package/dist/forkWireCaps.d.ts +25 -0
  31. package/dist/forkWireCaps.js +42 -0
  32. package/dist/hostEnv.d.ts +16 -0
  33. package/dist/hostEnv.js +17 -0
  34. package/dist/imagesWireCaps.d.ts +28 -0
  35. package/dist/imagesWireCaps.js +49 -0
  36. package/dist/index.d.ts +52 -0
  37. package/dist/index.js +62 -0
  38. package/dist/liveModelCatalog.d.ts +62 -0
  39. package/dist/liveModelCatalog.js +79 -0
  40. package/dist/mcpWireCaps.d.ts +41 -0
  41. package/dist/mcpWireCaps.js +51 -0
  42. package/dist/modelBudgetRule.d.ts +44 -0
  43. package/dist/modelBudgetRule.js +73 -0
  44. package/dist/modelWireCaps.d.ts +17 -0
  45. package/dist/modelWireCaps.js +16 -0
  46. package/dist/notifications.d.ts +150 -4
  47. package/dist/notifications.js +472 -0
  48. package/dist/permissionWireCaps.d.ts +37 -0
  49. package/dist/permissionWireCaps.js +46 -0
  50. package/dist/promptProfileWireCaps.d.ts +17 -0
  51. package/dist/promptProfileWireCaps.js +20 -0
  52. package/dist/retainBackgroundWireCaps.d.ts +50 -0
  53. package/dist/retainBackgroundWireCaps.js +58 -0
  54. package/dist/rewindWireCaps.d.ts +36 -0
  55. package/dist/rewindWireCaps.js +41 -0
  56. package/dist/seam.d.ts +97 -2
  57. package/dist/selfOrchestrationWireCaps.d.ts +40 -0
  58. package/dist/selfOrchestrationWireCaps.js +48 -0
  59. package/dist/sessionModelLatch.d.ts +35 -0
  60. package/dist/sessionModelLatch.js +63 -0
  61. package/dist/skillsWireCaps.d.ts +47 -0
  62. package/dist/skillsWireCaps.js +41 -0
  63. package/dist/sseIdleTriage.d.ts +94 -0
  64. package/dist/sseIdleTriage.js +142 -0
  65. package/dist/ultracodeWireCaps.d.ts +90 -0
  66. package/dist/ultracodeWireCaps.js +159 -0
  67. package/dist/webSearchWireCaps.d.ts +54 -0
  68. package/dist/webSearchWireCaps.js +76 -0
  69. package/package.json +2 -2
@@ -0,0 +1,58 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/retainBackgroundWireCaps.ts 逐字搬入(零依赖)。
3
+ */
4
+ /**
5
+ * src/sema/retainBackgroundWireCaps.ts — PURE projection: the shell's "retain background processes past
6
+ * task teardown" opt-in → the TOP-LEVEL `TaskRequest.retainBackgroundProcesses` boolean the SERVICE consumes
7
+ * (spec-fields.js `retainBackgroundProcessesFromBody`: `raw !== true` ⇒ dropped;requirePrincipal 多租户形
8
+ * 服务端另有门)。Mirrors selfOrchestrationWireCaps(the verified top-of-body wire pattern)。
9
+ *
10
+ * 背景([1046] 核谳,TB [991]② reap 族):core 直跑腿(sema-tb)出厂显式
11
+ * `retainBackgroundProcesses:true`(design/128 T1-1——「交付存活服务」任务的 verifier 在进程退出后
12
+ * 才连接),而组合腿产品曾默认关 ⇒ 两腿在 reap 族不是同一场考试。
13
+ *
14
+ * ⚠️ 缺省已翻转为 ON(clay 亲报「后台 shell 命令跑到一半被杀」案,2026-07-26;[1046]④ 的裁量件本次
15
+ * 由真实场景需求兑现)。缺省不发时,引擎 turn finally 的**两道门**都关着:
16
+ * ① `settleKilledForOwner({retainProcesses})` 的 retain 豁免;
17
+ * ② `retainBgBySpec = spec.retainBackgroundProcesses === true && !ownedEnvDying` —— 为真才整块跳过
18
+ * `bgEnv.disposeBackgroundShells(...)`(该 blanket sweep 不经 task-registry,故被它收掉的任务
19
+ * `stopSource` 为空,终态归因落到 `stoppedBy = "system"` 缺省 = clay 观察到的 `stopped-by: system`)。
20
+ *
21
+ * 三问(宪法 NO-SHIP 检查):
22
+ * 谁需要:真人用户 —— `run_in_background` 起的构建/测试/服务在当轮结束后必须还活着,否则
23
+ * BashOutput/TaskOutput 续读到的是尸体(clay 案:输出文件 0 字节 + status killed)。
24
+ * 谁受害:隔离执行环境(e2b/k8s/ssh 等 `isIsolated(env)`)—— 那里 `ownedEnvDying` 为真,retain 被
25
+ * **无条件覆盖**(core 侧 `!opts.envDying` / `&& !ownedEnvDying`),进程随宿主一起走。这是
26
+ * 正确的:进程宿主没了,留 handle 无意义。故本翻转对隔离腿零语义变化。
27
+ * 次级受害面 = 本机资源泄漏(后台进程活过当轮)—— 但这正是 CC 的既定契约,不是回归。
28
+ * 补偿机制:`SEMA_RETAIN_BACKGROUND=0|false|no|off` 逃生口 ⇒ 不 stamp ⇒ 回引擎缺省 reap;
29
+ * 进程始终可由 KillShell/TaskStop 显式收,`/bashes` 面板可见可停;引擎 hard-wall
30
+ * (defaultBgTimeoutSec 30min / maxBgTimeoutSec 2h)仍是兜底上限。
31
+ *
32
+ * CC 逐字锚(对标纲领 = 100% 达 CC 效果):CC 的 Bash `run_in_background` 契约 = 进程活过当轮,
33
+ * 由 BashOutput 续读、KillShell 显式收 —— **不在 turn 边界回收**。缺省 reap 是对 CC 的偏离,本次钉回。
34
+ *
35
+ * 语义:缺省 ON —— unset / 未识别值 ⇒ `true`(stamp);显式 truthy(1/true/yes/on)⇒ `true`;
36
+ * 只有显式 falsy(0/false/no/off)⇒ `undefined`(不 stamp ⇒ 引擎缺省 reap)。
37
+ * 服务端 `retainBackgroundProcessesFromBody` 是 `raw !== true ⇒ 丢弃` 的严格刻度,所以 opt-out 一律
38
+ * 表达为**不 stamp**,绝不发 `false`(形状诚实)。
39
+ * PURE — callers pass `env`;caller gates to LIVE mode(mock request shape 不变)。
40
+ */
41
+ import { hostEnv } from './hostEnv.js';
42
+ /** The env key the shell reads(SEMA_ 命名空间,与引擎自己的 spec∨env 通道正交——这里是壳→
43
+ * TaskRequest 的 per-request 意图面,非引擎部署面)。 */
44
+ export const RETAIN_BACKGROUND_ENV = 'SEMA_RETAIN_BACKGROUND';
45
+ /** FALSY spellings that opt OUT (case-insensitive, trimmed). Conservative allowlist —— 只有这些
46
+ * 拼写能关掉 retain;其它一切(含未识别值/空串)落缺省 ON,绝不静默 opt-out。 */
47
+ function isFalsy(v) {
48
+ const s = typeof v === 'string' ? v.trim().toLowerCase() : '';
49
+ return s === '0' || s === 'false' || s === 'no' || s === 'off';
50
+ }
51
+ /**
52
+ * ENV source → `true`(缺省 ON = CC `run_in_background` 契约:后台进程活过当轮),
53
+ * 只有显式 falsy 逃生口(`SEMA_RETAIN_BACKGROUND=0|false|no|off`)⇒ `undefined`(不 stamp ⇒ 引擎缺省 reap)。
54
+ * 返回型刻意是 `true | undefined` —— 服务端 `raw !== true ⇒ 丢弃`,opt-out 只能表达为「不 stamp」。
55
+ */
56
+ export function retainBackgroundFromEnv(env = hostEnv()) {
57
+ return isFalsy(env[RETAIN_BACKGROUND_ENV]) ? undefined : true;
58
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/rewindWireCaps.ts 逐字搬入(零依赖)。
3
+ */
4
+ /**
5
+ * src/sema/rewindWireCaps.ts — PURE CC Rewind (E18) field projection (no deps, testable). seamQuery imports it.
6
+ *
7
+ * Maps the CC /rewind MessageSelector modes → the engine's rewind fields (SDK TaskRequest, core 1.166.0;
8
+ * resumeAtMode: core 1.292 [833]):
9
+ * both = { resumeAt, resumeAtMode:"before", rewindFiles:true } fork BEFORE the user message + tree
10
+ * conversation = { resumeAt, resumeAtMode:"before" } fork BEFORE the user message only
11
+ * code = { rewindFilesTo } restore the tree only, NO conversation fork (no setLeafId)
12
+ * The target is a USER-MESSAGE SessionTreeEntry.id (server anchors user-message boundaries). Plus the per-turn
13
+ * CAPTURE stamp (rewindFiles:true on a normal turn) so the engine snapshots the tree (else nothing to restore).
14
+ *
15
+ * resumeAtMode "before"(排他截,core 1.292):CC /rewind 的语义是「回到发这条消息之前」——被选中的
16
+ * user message 回填输入框、从对话里移除;engine 侧对应 setLeafId(entry.parentId)。旧的含目标(inclusive
17
+ * "at")形状会把该 user message 留在引擎历史里 → 模型仍记得(暗号复现 bug)。老 server 未透传该字段时
18
+ * 行为退回含目标(version-safe,无回归——等 server 透传)。
19
+ */
20
+ /** A one-shot rewind RESTORE instruction (or the per-turn capture stamp) projected onto a request. */
21
+ export interface SeamRewindSpec {
22
+ resumeAt?: string;
23
+ /** core 1.292([833]):"before" = 排他截(setLeafId(entry.parentId));缺省 "at" = 含目标。 */
24
+ resumeAtMode?: 'at' | 'before';
25
+ rewindFiles?: boolean;
26
+ rewindFilesTo?: string;
27
+ }
28
+ /** The CC /rewind restore modes that hit the engine (summarize = compact, handled elsewhere). */
29
+ export type RewindMode = 'both' | 'conversation' | 'code';
30
+ /** Build the engine rewind fields for a chosen mode + target user-message entryId. */
31
+ export declare function rewindSpecForMode(mode: RewindMode, targetEntryId: string): SeamRewindSpec;
32
+ /**
33
+ * PURE projection (no state): an armed one-shot RESTORE wins; else the per-turn CAPTURE stamp (when enabled);
34
+ * else nothing. The caller consumes (clears) the arm after a restore.
35
+ */
36
+ export declare function projectRewind(pending: SeamRewindSpec | undefined, captureEnabled: boolean): SeamRewindSpec;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/rewindWireCaps.ts 逐字搬入(零依赖)。
3
+ */
4
+ /**
5
+ * src/sema/rewindWireCaps.ts — PURE CC Rewind (E18) field projection (no deps, testable). seamQuery imports it.
6
+ *
7
+ * Maps the CC /rewind MessageSelector modes → the engine's rewind fields (SDK TaskRequest, core 1.166.0;
8
+ * resumeAtMode: core 1.292 [833]):
9
+ * both = { resumeAt, resumeAtMode:"before", rewindFiles:true } fork BEFORE the user message + tree
10
+ * conversation = { resumeAt, resumeAtMode:"before" } fork BEFORE the user message only
11
+ * code = { rewindFilesTo } restore the tree only, NO conversation fork (no setLeafId)
12
+ * The target is a USER-MESSAGE SessionTreeEntry.id (server anchors user-message boundaries). Plus the per-turn
13
+ * CAPTURE stamp (rewindFiles:true on a normal turn) so the engine snapshots the tree (else nothing to restore).
14
+ *
15
+ * resumeAtMode "before"(排他截,core 1.292):CC /rewind 的语义是「回到发这条消息之前」——被选中的
16
+ * user message 回填输入框、从对话里移除;engine 侧对应 setLeafId(entry.parentId)。旧的含目标(inclusive
17
+ * "at")形状会把该 user message 留在引擎历史里 → 模型仍记得(暗号复现 bug)。老 server 未透传该字段时
18
+ * 行为退回含目标(version-safe,无回归——等 server 透传)。
19
+ */
20
+ /** Build the engine rewind fields for a chosen mode + target user-message entryId. */
21
+ export function rewindSpecForMode(mode, targetEntryId) {
22
+ switch (mode) {
23
+ case 'both':
24
+ return { resumeAt: targetEntryId, resumeAtMode: 'before', rewindFiles: true };
25
+ case 'conversation':
26
+ return { resumeAt: targetEntryId, resumeAtMode: 'before' };
27
+ case 'code':
28
+ return { rewindFilesTo: targetEntryId };
29
+ }
30
+ }
31
+ /**
32
+ * PURE projection (no state): an armed one-shot RESTORE wins; else the per-turn CAPTURE stamp (when enabled);
33
+ * else nothing. The caller consumes (clears) the arm after a restore.
34
+ */
35
+ export function projectRewind(pending, captureEnabled) {
36
+ if (pending)
37
+ return { ...pending };
38
+ if (captureEnabled)
39
+ return { rewindFiles: true };
40
+ return {};
41
+ }
package/dist/seam.d.ts CHANGED
@@ -6,13 +6,69 @@
6
6
  * (StampedAgentEvent.id/seq/toolCallId),ctx.uuid() 仅兜底无稳定键合成帧;
7
7
  * 往返守卫不变量:同流重放 ⇒ 同 id 序列。
8
8
  */
9
- import type { SDKMessage } from '@sema-agent/agent-types';
10
- /** 宿主注入的适配上下文(时钟/铸号可测,B 形跨端确定性)。 */
9
+ import type { ModelUsage, SDKMessage } from '@sema-agent/agent-types';
10
+ /**
11
+ * `AbortSignal` 的结构型(B3 扩容,SEAM-GAP-4)。
12
+ *
13
+ * 🔴 **别写成 `AbortSignal`**:本包 tsconfig 是 `"types": []` + 无 DOM lib(浏览器可移植性的
14
+ * 静态保障),`AbortSignal` 这个名字在本包里根本不存在。真 `AbortSignal`(浏览器原生 / Node
15
+ * globalThis)结构上满足本型,直接传即可,零适配。
16
+ */
17
+ export interface AbortSignalLike {
18
+ readonly aborted: boolean;
19
+ }
20
+ /** 宿主定时器句柄(不透明:Node 是 Timeout 对象、浏览器是 number,库侧只负责回传)。 */
21
+ export type TimerHandle = unknown;
22
+ /** `ctx.log` 的级别词表(cli utils/debug logForDebugging 的等价面)。 */
23
+ export type AdapterLogLevel = 'debug' | 'info' | 'warn' | 'error';
24
+ /**
25
+ * 宿主注入的适配上下文(时钟/铸号可测,B 形跨端确定性)。
26
+ *
27
+ * **B3 扩容(0.5.0,契约 minor —— 只加可选位,既有宿主零改动)**:下面五个可选口是
28
+ * 「客户端内核需要、但实现方式因端而异」的能力(设计稿 §5.4①)。浏览器实现全部原生零 polyfill:
29
+ * `log`→console/上报、`probe`→省略、`signal`→AbortController.signal、
30
+ * `setTimer/clearTimer`→setTimeout/clearTimeout。
31
+ *
32
+ * 🔴 **缺席语义都是「该能力不启用」,不是「报错」也不是「换个方式偷偷做」**:
33
+ * 内核绝不 `require('fs')` 写探针、绝不 `console.log`、绝不 `setTimeout` 兜底 ——
34
+ * 那正是这批要切掉的东西(§5.2 的 33 个 `utils/debug` 消费点)。
35
+ */
11
36
  export interface AdapterContext {
12
37
  sessionId: string;
13
38
  /** 兜底铸号——仅当帧无稳定键时使用(见 deriveTranscriptId)。 */
14
39
  uuid(): string;
15
40
  now(): number;
41
+ /**
42
+ * 诊断日志(替 cli `utils/debug.logForDebugging` —— 那一行 import 就把 1565 个文件拉进闭包,
43
+ * 是浏览器可移植性的头号污染源,设计稿 §2.1.3)。缺席 = 内核静默(绝不回落 console)。
44
+ */
45
+ log?(level: AdapterLogLevel, msg: string, data?: unknown): void;
46
+ /**
47
+ * 行式探针(替 cli 的 6 处 `SEMA_*_PROBE` 文件探针 —— Ink 挂载后 console 被吞,壳侧靠写文件
48
+ * 取证)。`channel` = 探针名(如 `'stream'`),`line` = 单行载荷。浏览器宿主直接省略。
49
+ */
50
+ probe?(channel: string, line: string): void;
51
+ /**
52
+ * turn 级中断信号(SEAM-GAP-4)。Esc/abort 后内核立刻停止投影,**不产终答**
53
+ * (CC 自己的 abort 路径渲 InterruptedByUser chrome,由宿主负责)。
54
+ * 缺席 = 该 turn 不可中断(既有行为)。
55
+ */
56
+ signal?: AbortSignalLike;
57
+ /**
58
+ * 宿主定时器(T40 IDLE-FLUSH 竞速需要)。`fn` 到点执行一次。
59
+ * 🔴 **缺席 = IDLE-FLUSH 过渡修不启用**(纯等下一帧,与 0.4.0 逐字同行为)——绝不用
60
+ * `setTimeout` 兜底:那会在没有宏任务概念的宿主上凭空造出一个时钟。
61
+ */
62
+ setTimer?(ms: number, fn: () => void): TimerHandle;
63
+ clearTimer?(handle: TimerHandle): void;
64
+ /**
65
+ * 活体增量攒批节拍(T29)。缺席 = `FLUSH_INTERVAL_MS` 默认值。
66
+ * 🔴 这是**宿主渲染策略**,不是 wire 语义:CC 自己两个端就是两个值(桌面宿主
67
+ * `makeStreamDeltaCoalescer`=16ms 单帧攒批;我们对标的 CC 2.1.207 CLI 口径=100ms,
68
+ * 降重绘频率让长回答滚动更稳)。合并**语义**(先思考后文本、段边界重开块)与节拍无关,
69
+ * 所以节拍下放给宿主、语义留库里。
70
+ */
71
+ coalesceIntervalMs?: number;
16
72
  }
17
73
  /** chrome 平面事件 v1 枚举(cli 现役 UI 面归纳;additive 演进,臂只增不改)。
18
74
  *
@@ -150,6 +206,45 @@ export type ChromeEvent = {
150
206
  toolName: string;
151
207
  toolUseId: string;
152
208
  input: unknown;
209
+ }
210
+ /**
211
+ * B3 新臂 ①(T38 三清的第三清 + T57 五处断闸的第五处)—— E12「下一步可问什么」建议**批**。
212
+ * `suggestions: null` = 作废(turn 开场必发一次:上一轮的建议对新上下文是噪声,挂着不掉会让人
213
+ * 以为是这一轮的产出);非空数组 = 本轮产出。
214
+ * 🔴 UNTRUSTED 模型文本(service 已 redact),**仅 UI**:宿主消费义务 = 推给 composer 上方的
215
+ * chips 面;**绝不回喂模型、绝不落转录、绝不进 /compact 的对话体**。
216
+ * 子流(parentToolCallId 标)的建议不发本臂 —— 断闸在库里,不摊给宿主。
217
+ */
218
+ | {
219
+ kind: 'prompt_suggestions';
220
+ laneProof: LaneProof;
221
+ suggestions: readonly string[] | null;
222
+ }
223
+ /**
224
+ * B3 新臂 ②(runStream 外向边切除,设计稿 §5.2 第二条边)—— 每个 leader `turn_end` 折叠出的
225
+ * 真 usage。此前是 runStream 里一条 `void import('lastTurnUsageStore')` 的壳内动态边。
226
+ * 宿主消费义务:落「最近一次 turn 的真 usage」槽,供 statusline 类消费面在消息面取不到 usage
227
+ * 时回落(不落 = 插件的 Context 条恒 0%,原病复发)。子流 turn_end **不发本臂**(判据同 §E2:
228
+ * statusline 显示的是主会话窗口,子代 usage 会把 Context% 打成孩子的窗口占用)。
229
+ */
230
+ | {
231
+ kind: 'last_turn_usage';
232
+ laneProof: LaneProof;
233
+ usage: ModelUsage;
234
+ }
235
+ /**
236
+ * B3 新臂 ③(runStream 外向边切除,设计稿 §5.2 头号污染源)—— `done{status:"needs_review"}` +
237
+ * gate `plan_review` 的 park 终帧。此前是 runStream 里一条 `await import('planReviewWire')`,
238
+ * 而 planReviewWire → `utils/debug` → **整棵 1565 文件的 CC 树**进闭包(浏览器可移植性归零)。
239
+ * 宿主消费义务:弹审批卡(cli 借 AskUserQuestion overlay 合成 question,决断走
240
+ * `/v1/assistant/tasks/:id/plan_review` resume verb);**fail-soft**——不实现或实现失败都不影响
241
+ * 紧随其后的终态帧照常投影(用户至少看到 plan fence + 一条普通完成)。
242
+ * `result` = done 帧的 RAW `result` 载荷(判形与取 taskId 都由宿主做,库不猜)。
243
+ */
244
+ | {
245
+ kind: 'plan_review_park';
246
+ laneProof: LaneProof;
247
+ result: unknown;
153
248
  };
154
249
  /** 车道纪律一等公民([1617] 家族固化):chrome 事件构造必须携判别证明。 */
155
250
  export type LaneProof = {
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/selfOrchestrationWireCaps.ts 逐字搬入(零依赖)。
3
+ */
4
+ /**
5
+ * src/sema/selfOrchestrationWireCaps.ts — PURE projection: the shell's "enable the LLM-authored workflow engine"
6
+ * opt-in → the TOP-LEVEL `TaskRequest.selfOrchestration` boolean the SERVICE consumes. Mirrors webSearchWireCaps.ts
7
+ * (the verified wire pattern), but the field rides at the TOP of the body (not under `settings`).
8
+ *
9
+ * The SERVICE reads `body.selfOrchestration` STRICTLY (ai-agent-service task-workflow.ts `selfOrchestrationFromBody`:
10
+ * `body.selfOrchestration !== true` ⇒ dropped) → core `TaskSpec.selfOrchestration`, the gate that MOUNTS core's
11
+ * LLM-authored `run_workflow` engine (the workflow.ts agent/parallel/pipeline/phase/nested orchestrator). The
12
+ * service then gates it on the DEPLOYMENT's `SELF_ORCHESTRATION_ENABLED` (else a harmless no-op, dropped) + the
13
+ * MULTI-TENANT per-principal entitlement RESOLVER (fail-closed: no resolver ⇒ dropped); a single-user worker honors
14
+ * it directly. So the shell's job is ONLY to STAMP the intent when the user opted in — the service owns every gate.
15
+ * We mirror the service's strict `=== true` semantics here: only an explicit truthy env opt-in returns `true`;
16
+ * everything else returns `undefined` (⇒ NO stamp ⇒ the engine never mounts run_workflow ⇒ default behaviour
17
+ * unchanged, exactly like an absent webSearch config).
18
+ *
19
+ * Config source (clay 决定 2026-07-01 §4 — DEFAULT-ON,opt-out):
20
+ * - DEFAULT ON: unset / 其它 ⇒ stamp `selfOrchestration:true`(RunWorkflow 默认可用,体验像 CC 超集)。
21
+ * - ENV opt-OUT: `SEMA_SELF_ORCHESTRATION=0/false/no/off` ⇒ undefined(no stamp)。
22
+ *
23
+ * 前身是 opt-in(parity-safe 保守默认 OFF);clay 拍 §4 后翻转为默认 ON(TOC 单用户本机无 entitlement 顾虑,
24
+ * 引擎侧 SELF_ORCHESTRATION_ENABLED 也在 assembleChildEnv 默认开)。可后续接 settings.json 驱动,这里仍是单一投影点。
25
+ *
26
+ * PURE — no env/file IO of its OWN (callers pass `env`), so it bundles + unit-tests without the heavy settings
27
+ * graph, exactly like webSearchWireCaps. The CALLER (seamQuery / seamQueryEngine) gates the env read to LIVE mode
28
+ * so the mock request shape never changes; the selfOrchestration stamp IS the opt-in (no extra flag needed).
29
+ */
30
+ import { type EnvLike } from './hostEnv.js';
31
+ /** The env key the shell reads. SEMA_-namespaced so it never collides with the worker's OWN deploy
32
+ * `SELF_ORCHESTRATION_ENABLED` env (the service's DEPLOYMENT gate — a different axis the service owns). */
33
+ export declare const SELF_ORCHESTRATION_ENV: "SEMA_SELF_ORCHESTRATION";
34
+ /** ENV source → `true` by DEFAULT (clay 决定 2026-07-01 §4:RunWorkflow 默认可用),unless the user explicitly
35
+ * opts OUT (`SEMA_SELF_ORCHESTRATION=0/false/no/off`) ⇒ `undefined` (no stamp; engine never mounts run_workflow).
36
+ * Still `true | undefined` (never `false`) to mirror the service's strict `=== true` read. A stamped `true` only
37
+ * makes run_workflow AVAILABLE — the engine still owns the final gate (its own `SELF_ORCHESTRATION_ENABLED`, set
38
+ * default-on in TOC assembleChildEnv, + per-principal entitlement; TOC single-user honors it directly). The caller
39
+ * gates this to LIVE mode, so the MOCK request shape is unchanged; only the live default path now stamps true. */
40
+ export declare function selfOrchestrationFromEnv(env?: EnvLike): true | undefined;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/selfOrchestrationWireCaps.ts 逐字搬入(零依赖)。
3
+ */
4
+ /**
5
+ * src/sema/selfOrchestrationWireCaps.ts — PURE projection: the shell's "enable the LLM-authored workflow engine"
6
+ * opt-in → the TOP-LEVEL `TaskRequest.selfOrchestration` boolean the SERVICE consumes. Mirrors webSearchWireCaps.ts
7
+ * (the verified wire pattern), but the field rides at the TOP of the body (not under `settings`).
8
+ *
9
+ * The SERVICE reads `body.selfOrchestration` STRICTLY (ai-agent-service task-workflow.ts `selfOrchestrationFromBody`:
10
+ * `body.selfOrchestration !== true` ⇒ dropped) → core `TaskSpec.selfOrchestration`, the gate that MOUNTS core's
11
+ * LLM-authored `run_workflow` engine (the workflow.ts agent/parallel/pipeline/phase/nested orchestrator). The
12
+ * service then gates it on the DEPLOYMENT's `SELF_ORCHESTRATION_ENABLED` (else a harmless no-op, dropped) + the
13
+ * MULTI-TENANT per-principal entitlement RESOLVER (fail-closed: no resolver ⇒ dropped); a single-user worker honors
14
+ * it directly. So the shell's job is ONLY to STAMP the intent when the user opted in — the service owns every gate.
15
+ * We mirror the service's strict `=== true` semantics here: only an explicit truthy env opt-in returns `true`;
16
+ * everything else returns `undefined` (⇒ NO stamp ⇒ the engine never mounts run_workflow ⇒ default behaviour
17
+ * unchanged, exactly like an absent webSearch config).
18
+ *
19
+ * Config source (clay 决定 2026-07-01 §4 — DEFAULT-ON,opt-out):
20
+ * - DEFAULT ON: unset / 其它 ⇒ stamp `selfOrchestration:true`(RunWorkflow 默认可用,体验像 CC 超集)。
21
+ * - ENV opt-OUT: `SEMA_SELF_ORCHESTRATION=0/false/no/off` ⇒ undefined(no stamp)。
22
+ *
23
+ * 前身是 opt-in(parity-safe 保守默认 OFF);clay 拍 §4 后翻转为默认 ON(TOC 单用户本机无 entitlement 顾虑,
24
+ * 引擎侧 SELF_ORCHESTRATION_ENABLED 也在 assembleChildEnv 默认开)。可后续接 settings.json 驱动,这里仍是单一投影点。
25
+ *
26
+ * PURE — no env/file IO of its OWN (callers pass `env`), so it bundles + unit-tests without the heavy settings
27
+ * graph, exactly like webSearchWireCaps. The CALLER (seamQuery / seamQueryEngine) gates the env read to LIVE mode
28
+ * so the mock request shape never changes; the selfOrchestration stamp IS the opt-in (no extra flag needed).
29
+ */
30
+ import { hostEnv } from './hostEnv.js';
31
+ /** The env key the shell reads. SEMA_-namespaced so it never collides with the worker's OWN deploy
32
+ * `SELF_ORCHESTRATION_ENABLED` env (the service's DEPLOYMENT gate — a different axis the service owns). */
33
+ export const SELF_ORCHESTRATION_ENV = 'SEMA_SELF_ORCHESTRATION';
34
+ /** The FALSY spellings that opt OUT (case-insensitive, trimmed). Conservative allowlist — only an explicit
35
+ * `0`/`false`/`no`/`off` disables; everything else (incl. unset/empty) is the DEFAULT-ON path. */
36
+ function isFalsy(v) {
37
+ const s = typeof v === 'string' ? v.trim().toLowerCase() : '';
38
+ return s === '0' || s === 'false' || s === 'no' || s === 'off';
39
+ }
40
+ /** ENV source → `true` by DEFAULT (clay 决定 2026-07-01 §4:RunWorkflow 默认可用),unless the user explicitly
41
+ * opts OUT (`SEMA_SELF_ORCHESTRATION=0/false/no/off`) ⇒ `undefined` (no stamp; engine never mounts run_workflow).
42
+ * Still `true | undefined` (never `false`) to mirror the service's strict `=== true` read. A stamped `true` only
43
+ * makes run_workflow AVAILABLE — the engine still owns the final gate (its own `SELF_ORCHESTRATION_ENABLED`, set
44
+ * default-on in TOC assembleChildEnv, + per-principal entitlement; TOC single-user honors it directly). The caller
45
+ * gates this to LIVE mode, so the MOCK request shape is unchanged; only the live default path now stamps true. */
46
+ export function selfOrchestrationFromEnv(env = hostEnv()) {
47
+ return isFalsy(env[SELF_ORCHESTRATION_ENV]) ? undefined : true;
48
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/sessionModelLatch.ts 逐字搬入(零依赖;🔴 三个模块级 latch 是单实例态)。
3
+ */
4
+ /**
5
+ * sessionModelLatch — 件13 (rev2 2026-07-14, CC 2.1.207 对锚): support module for the /model `s`
6
+ * (use this session only) split, anchored on the 207 mechanism (verified against the 2.1.198 pretty
7
+ * source, semantics identical in 207):
8
+ *
9
+ * wsr(model, get, set, setDefault): // pretty.js:493226 (198 coords)
10
+ * set(a => ({ ...a, mainLoopModel: model, mainLoopModelForSession: null })) // ALWAYS in-memory
11
+ * if (setDefault) RQt(model) // RQt = eo("userSettings",{model}) —
12
+ * // the ONLY settings write (493244)
13
+ * message = `Set model to X${setDefault ? " and saved as your default for new sessions"
14
+ * : " for this session only"}`
15
+ *
16
+ * i.e. in 207 BOTH Enter and `s` write the in-memory mainLoopModel and CLEAR the plan-mode session
17
+ * field; persistence is EXPLICIT (RQt) and only runs on set-as-default. 207 has no auto-persisting
18
+ * model choke point. sema's bundled base DOES auto-persist every mainLoopModel change in
19
+ * state/onChangeAppState.ts, so to reproduce the 207 state shape exactly the session-only commit
20
+ * marks its write with a ONE-SHOT skip-persist flag which the choke consumes: the write then behaves
21
+ * exactly like 207's no-RQt branch (mainLoopModel set in-memory, mainLoopModelForSession null,
22
+ * userSettings untouched → restart reverts to the persisted default).
23
+ *
24
+ * The latch half mirrors "the active model is UNPERSISTED" for NON-React consumers (seamQuery's
25
+ * modelEndpointSwitch runs outside the component tree): a session-only pick must not be promoted to
26
+ * next-boot roster/env state when the engine can route the model by itself. Kept in sync from the
27
+ * onChangeAppState choke (single funnel for every setAppState write): set on a session-only
28
+ * mainLoopModel write OR a plan-mode mainLoopModelForSession write, cleared on a persisted write.
29
+ */
30
+ export declare function setSessionModelLatch(model: string | null): void;
31
+ export declare function getSessionModelLatch(): string | null;
32
+ export declare function markSessionOnlyModelWrite(): void;
33
+ export declare function consumeSessionOnlyModelWrite(): boolean;
34
+ export declare function markUserModelPickThisSession(): void;
35
+ export declare function hasUserModelPickThisSession(): boolean;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/sessionModelLatch.ts 逐字搬入(零依赖;🔴 三个模块级 latch 是单实例态)。
3
+ */
4
+ /**
5
+ * sessionModelLatch — 件13 (rev2 2026-07-14, CC 2.1.207 对锚): support module for the /model `s`
6
+ * (use this session only) split, anchored on the 207 mechanism (verified against the 2.1.198 pretty
7
+ * source, semantics identical in 207):
8
+ *
9
+ * wsr(model, get, set, setDefault): // pretty.js:493226 (198 coords)
10
+ * set(a => ({ ...a, mainLoopModel: model, mainLoopModelForSession: null })) // ALWAYS in-memory
11
+ * if (setDefault) RQt(model) // RQt = eo("userSettings",{model}) —
12
+ * // the ONLY settings write (493244)
13
+ * message = `Set model to X${setDefault ? " and saved as your default for new sessions"
14
+ * : " for this session only"}`
15
+ *
16
+ * i.e. in 207 BOTH Enter and `s` write the in-memory mainLoopModel and CLEAR the plan-mode session
17
+ * field; persistence is EXPLICIT (RQt) and only runs on set-as-default. 207 has no auto-persisting
18
+ * model choke point. sema's bundled base DOES auto-persist every mainLoopModel change in
19
+ * state/onChangeAppState.ts, so to reproduce the 207 state shape exactly the session-only commit
20
+ * marks its write with a ONE-SHOT skip-persist flag which the choke consumes: the write then behaves
21
+ * exactly like 207's no-RQt branch (mainLoopModel set in-memory, mainLoopModelForSession null,
22
+ * userSettings untouched → restart reverts to the persisted default).
23
+ *
24
+ * The latch half mirrors "the active model is UNPERSISTED" for NON-React consumers (seamQuery's
25
+ * modelEndpointSwitch runs outside the component tree): a session-only pick must not be promoted to
26
+ * next-boot roster/env state when the engine can route the model by itself. Kept in sync from the
27
+ * onChangeAppState choke (single funnel for every setAppState write): set on a session-only
28
+ * mainLoopModel write OR a plan-mode mainLoopModelForSession write, cleared on a persisted write.
29
+ */
30
+ /** The model that is currently ACTIVE but UNPERSISTED (session-only pick or plan-mode override). */
31
+ let sessionModelOverride = null;
32
+ export function setSessionModelLatch(model) {
33
+ sessionModelOverride = model;
34
+ }
35
+ export function getSessionModelLatch() {
36
+ return sessionModelOverride;
37
+ }
38
+ /** One-shot mark: the NEXT mainLoopModel change is session-only (207 no-RQt branch) — do not persist. */
39
+ let sessionOnlyWritePending = false;
40
+ export function markSessionOnlyModelWrite() {
41
+ sessionOnlyWritePending = true;
42
+ }
43
+ export function consumeSessionOnlyModelWrite() {
44
+ const v = sessionOnlyWritePending;
45
+ sessionOnlyWritePending = false;
46
+ return v;
47
+ }
48
+ // ── 件3(F1 模型面批,S3 铁律半场):本会话「用户显式选过模型」标记 ─────────────────────
49
+ // boot 目录回读(replEntry 把 /v1/models default 写进 initialState.mainLoopModel)不经过
50
+ // setAppState ⇒ 不置位;任何经 setAppState 的 mainLoopModel 变更(cmd-model Enter、`s` 键、
51
+ // /model <name>、config 面板)都算显式 pick —— 由 onChangeAppState choke(每个 setAppState
52
+ // 写的单一漏斗)置位,散在 UI 调用点零改动。ensureEngineServesModel 据此区分「运行时回声」
53
+ // (boot 回读的目录 default)vs「用户意图」:回声绝不允许持久化写(不 switchRoster 改 env /
54
+ // 不重排 models.json / 不改档位绑定)。注意与上面 latch 的分工:latch=「当前活跃值未持久化」
55
+ // (s 键/plan override),本标记=「本会话内发生过显式选择」——s 键 pick 也置位(它是显式意图,
56
+ // 是否持久化由件13 latch 在 instant 路径拦截,语义不变)。
57
+ let userModelPickThisSession = false;
58
+ export function markUserModelPickThisSession() {
59
+ userModelPickThisSession = true;
60
+ }
61
+ export function hasUserModelPickThisSession() {
62
+ return userModelPickThisSession;
63
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/skillsWireCaps.ts 逐字搬入(只 import SDK)。
3
+ */
4
+ /**
5
+ * src/sema/skillsWireCaps.ts — the PURE projection + server caps for the skills engine wire.
6
+ *
7
+ * Split out from skillsWire.ts so this logic (the part worth pinning) is testable WITHOUT the heavy on-disk
8
+ * loader graph (loadSkillsDir / loadPluginCommands pull analytics/signals/fs). It imports ONLY the `SkillSpec`
9
+ * type (erased at build), so a test can esbuild-bundle THIS file alone — no runtime side effects.
10
+ *
11
+ * The caps mirror the SDK `TaskRequest.skills` server caps (types.ts:82): ≤10 items, name ≤64, description
12
+ * ≤1024, content ≤32768 — a violation is a 400, so we truncate/limit HERE to make the live request well-formed
13
+ * by construction.
14
+ */
15
+ import type { SkillSpec } from '@sema-agent/sdk';
16
+ /** Server caps for `TaskRequest.skills` (SDK types.ts:82 — violations are a 400). */
17
+ export declare const SKILL_CAPS: {
18
+ readonly items: 10;
19
+ readonly name: 64;
20
+ readonly description: 1024;
21
+ readonly content: 32768;
22
+ };
23
+ /** The minimal shape of a loaded skill `Command` this projection reads (a structural subset of CC's Command,
24
+ * cast at the call site so we stay decoupled from the wide Command union). */
25
+ export interface SkillCmdLike {
26
+ type?: string;
27
+ name?: string;
28
+ description?: string;
29
+ whenToUse?: string;
30
+ /** loader origin ('builtin' | 'plugin' | SettingSource…) — read by the skillOverrides four-state
31
+ * resolution when the wire prunes hidden skills (skillsWire.ts). */
32
+ source?: string;
33
+ disableModelInvocation?: boolean;
34
+ /** the skill's own directory (`createSkillCommand` baseDir → command.skillRoot); SKILL.md lives at root/SKILL.md. */
35
+ skillRoot?: string;
36
+ }
37
+ /** Build one capped `SkillSpec`, or null when there is no usable name/body. description folds in `whenToUse`
38
+ * (CC's progressive-disclosure metadata is name+description+whenToUse) so the engine's Skill tool can select
39
+ * it. All three fields are truncated to the server caps. */
40
+ export declare function toCappedSpec(name: string, description: string, whenToUse: string, body: string): SkillSpec | null;
41
+ /**
42
+ * PURE projection: loaded skill commands → `SkillSpec[]` (capped, deduped by name, ≤10 items). `resolveBody`
43
+ * supplies each command's raw SKILL.md body (null ⇒ skip); injecting it keeps this function IO-free and
44
+ * unit-testable. Only MODEL-INVOCABLE prompt skills are sent — UI-only / `disableModelInvocation` commands
45
+ * are excluded (engine injection feeds the model's Skill tool, not the slash menu).
46
+ */
47
+ export declare function skillCommandsToSpecs(cmds: SkillCmdLike[], resolveBody: (cmd: SkillCmdLike) => string | null): SkillSpec[];
@@ -0,0 +1,41 @@
1
+ /** Server caps for `TaskRequest.skills` (SDK types.ts:82 — violations are a 400). */
2
+ export const SKILL_CAPS = { items: 10, name: 64, description: 1024, content: 32768 };
3
+ /** Build one capped `SkillSpec`, or null when there is no usable name/body. description folds in `whenToUse`
4
+ * (CC's progressive-disclosure metadata is name+description+whenToUse) so the engine's Skill tool can select
5
+ * it. All three fields are truncated to the server caps. */
6
+ export function toCappedSpec(name, description, whenToUse, body) {
7
+ const n = name.trim().slice(0, SKILL_CAPS.name);
8
+ const c = body.trim().slice(0, SKILL_CAPS.content);
9
+ if (!n || !c)
10
+ return null;
11
+ const desc = [description.trim(), whenToUse.trim()]
12
+ .filter((s) => s.length > 0)
13
+ .join(' — ')
14
+ .slice(0, SKILL_CAPS.description);
15
+ return { name: n, description: desc, content: c };
16
+ }
17
+ /**
18
+ * PURE projection: loaded skill commands → `SkillSpec[]` (capped, deduped by name, ≤10 items). `resolveBody`
19
+ * supplies each command's raw SKILL.md body (null ⇒ skip); injecting it keeps this function IO-free and
20
+ * unit-testable. Only MODEL-INVOCABLE prompt skills are sent — UI-only / `disableModelInvocation` commands
21
+ * are excluded (engine injection feeds the model's Skill tool, not the slash menu).
22
+ */
23
+ export function skillCommandsToSpecs(cmds, resolveBody) {
24
+ const specs = [];
25
+ const seen = new Set();
26
+ for (const cmd of cmds) {
27
+ if (specs.length >= SKILL_CAPS.items)
28
+ break;
29
+ if (cmd.type !== 'prompt' || cmd.disableModelInvocation)
30
+ continue;
31
+ const body = resolveBody(cmd);
32
+ if (!body)
33
+ continue;
34
+ const spec = toCappedSpec(cmd.name ?? '', cmd.description ?? '', cmd.whenToUse ?? '', body);
35
+ if (!spec || seen.has(spec.name))
36
+ continue;
37
+ seen.add(spec.name);
38
+ specs.push(spec);
39
+ }
40
+ return specs;
41
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/sema/sseIdleTriage.ts 逐字搬入(只 import SDK)。
3
+ */
4
+ /**
5
+ * src/sema/sseIdleTriage.ts — turn-stream SSE idle-silence triage(TB2.0 反馈批 D · P0-2 壳半场,2026-07-15)。
6
+ *
7
+ * WHY: TB2.0 bench 真实案例 —— agent 长跑前台编译 ~9.5min,工具完成前壳侧 45s 空闲监测器
8
+ * (`@sema-agent/sdk` readSseFrames HEARTBEAT_GRACE_MS)命中 `SSE: no data for 45000ms (heartbeat
9
+ * silence)` → 整轮 rc=1 丢弃,引擎不知情继续跑完(access log 499)。分诊报告判决①修正段拍定:
10
+ * 壳侧独立可发 = S1(SDK grace 透传+typed SseIdleError,0.0.47 已发)+ S4(grace 旋钮)+
11
+ * S3 缩水版(engine-alive-idle 分诊,只改报错文案,不重连)。本模块 = S4 + S3 的共享件。
12
+ *
13
+ * S4 · SEMA_SSE_HEARTBEAT_GRACE_MS(settings.json `env` 块 → 1a-envseed → process.env,其它 wire
14
+ * 旋钮同一车道):整数毫秒,合法域 [5000, 600000];非法/缺省 = 不透传(SDK 默认 45s)——静默
15
+ * fail-soft,一个 settings typo 绝不 brick 任何 run(scenarioWire settings-lane 纪律)。
16
+ * ⚠️ 作用面 = 会话 turn 流(tasks.stream / liveClient)一条车道。SDK 控制通道(SSETransport.ts:21
17
+ * LIVENESS_TIMEOUT_MS=45s)自带重连+give-up 自愈,本旋钮【不】达那条车道——这不是全局 grace。
18
+ *
19
+ * S3 缩水版 · engine-alive-idle 分诊:liveClient 的 tasks.stream 包装 catch 到 SseIdleError 时,
20
+ * 先判 signal.aborted(用户中止契约先行,seamQuery.ts:1073——Esc 撕流绝不被分诊吞),再 GET
21
+ * `/health`(非 /healthz;server dist server.js:290)短超时探引擎活性:引擎死 → 原样抛(现行
22
+ * engine-down/not-restart 路径不变);引擎活 → 给同一错误对象打 engine-alive-idle 标记,
23
+ * seamQuery 的 tearVerdict 恢复腿据此渲染诚实分诊文案(engineAliveIdleContent)。
24
+ *
25
+ * 【本模块绝不重连】(判决①修正段圣旨,范围随 M2 精化):非 detach 车道上 tasks/stream 客户端
26
+ * 断连 = server onDisconnect ac.abort() 终止 run(anti token-burn by design),且该车道帧无 id
27
+ * 不可位置续传——空闲监测器抛出的那一刻 finally 已 cancel res.body,run 已被这次断连自身中止,
28
+ * 同 key in-flight 重放拿到的只是 aborted 结局。前台另有既有裁决:mid-turn 工具副作用非幂等,
29
+ * 绝不自动重发。引擎 E2(server ≥1.196 x-detach-on-disconnect,[854]①)已到货:headless `-p`
30
+ * detach 车道的 R1 换道重连 = 重连腿 M2,住在 headlessReconnectWire.ts(liveClient 管线最内层,
31
+ * 只在 detach 头已发出的流上武装);本模块保持「探活+打标,不重连」——交互 REPL 与一切非
32
+ * detach 车道的取消契约一根毛不动,M2 耗尽预算后判死文案也仍由这里的打标驱动。
33
+ */
34
+ import { type EnvLike } from './hostEnv.js';
35
+ /** Settings-lane env knob(settings.json `env` 块 → 1a-envseed → process.env)。整数毫秒。 */
36
+ export declare const SSE_HEARTBEAT_GRACE_ENV = "SEMA_SSE_HEARTBEAT_GRACE_MS";
37
+ /** 合法域下界:低于 5s 会把正常 token 间隙误判成静默。 */
38
+ export declare const SSE_GRACE_MIN_MS = 5000;
39
+ /** 合法域上界:10min——超过引擎自身各级 idle 顶,再长没有意义。 */
40
+ export declare const SSE_GRACE_MAX_MS = 600000;
41
+ /** SDK 默认(readSseFrames HEARTBEAT_GRACE_MS)——文案引用用,不在本模块生效。 */
42
+ export declare const SDK_DEFAULT_GRACE_MS = 45000;
43
+ /**
44
+ * S4 — resolve the turn-stream heartbeat grace override. Unset/empty → undefined(SDK 默认 45s,
45
+ * 不透传);非纯十进制整数 / 超出 [5000,600000] → undefined(静默落默认,fail-soft)。
46
+ */
47
+ export declare function resolveSseHeartbeatGraceMs(env?: EnvLike): number | undefined;
48
+ /** 给 SseIdleError 打 engine-alive-idle 标(引擎 /health 已确认活着)。返回同一对象。 */
49
+ export declare function markEngineAliveIdle<E extends Error>(err: E): E;
50
+ /**
51
+ * engine-alive-idle 判型:命中返回静默时长 elapsedMs(SseIdleError 0.0.47 自带;缺失兜 0),
52
+ * 未命中返回 undefined。seamQuery tearVerdict 腿与 headless `-p` 的 stderr 面共用。
53
+ */
54
+ export declare function engineAliveIdleElapsedMs(err: unknown): number | undefined;
55
+ /**
56
+ * SseIdleError 判型:instanceof 为主;双包风险(bundle 内外两份 SDK class identity 分叉)下
57
+ * name 兜底——name 由 SDK 构造器冻结为 'SseIdleError',非我方铸造。
58
+ */
59
+ export declare function isSseIdleError(err: unknown): boolean;
60
+ /**
61
+ * 引擎活性探针 — SDK probeHealth(GET `${baseUrl}/health`;端点是 /health 非 /healthz),默认 2s
62
+ * 预算,永不抛:alive = 200 且 status==='ok';非 2xx / 不可达 / 超时 = not alive(引擎死 → 现行
63
+ * 失败路径,分诊绝不让真死引擎空等)。与 engineUpgradeCoordinator 的 classifyStreamTear 同款姿势。
64
+ * SDK 宪法迁移批差分记账:原实现只看 res.ok(200 非 JSON / status!=='ok' 也判活);SDK 额外要
65
+ * status==='ok'——service /health 200 恒报 status:'ok',契约面等价,对「200 但引擎自述非 ok」的
66
+ * 病态更严格诚实。
67
+ */
68
+ export declare function probeEngineAlive(baseUrl: string, timeoutMs?: number): Promise<boolean>;
69
+ /**
70
+ * S3 缩水版 — the tasks.stream diagnose wrapper(liveClient 出口整流罩)。
71
+ * 透传 inner 的每个事件;仅当 inner 以 SseIdleError 收场时介入:
72
+ * 1. signal.aborted 先行 — 用户中止(Esc)撕流原样抛,绝不探活/绝不改判(seamQuery.ts:1073 契约);
73
+ * 2. GET /health 探引擎活性(2s 预算);
74
+ * 3. 引擎活 → 同一错误对象打 engine-alive-idle 标后重抛(下游 tearVerdict 腿改文案;detach `-p`
75
+ * 车道的 R1 重连发生在更内层的 headlessReconnectWire——错误能到这里 = 重连已耗尽/不可换道);
76
+ * 引擎死/探测失败 → 原样重抛(现行 classifyStreamTear 会诚实判 engine-down)。
77
+ * 非 SseIdleError 一律原样穿透。
78
+ */
79
+ export declare function diagnoseSseIdleTear<T>(inner: AsyncGenerator<T>, baseUrl: string, signal?: AbortSignal): AsyncGenerator<T>;
80
+ /**
81
+ * engine-alive-idle 的用户可见分诊文案(升级自裸 "API Error: SSE: no data for …" 行)。
82
+ * 措辞如实(判决②契约),按 detach 态分叉:
83
+ * · 非 detach(默认/旋钮关/版本门拒/交互车道):tasks/stream 断连 = server 端 abort 这次 run——
84
+ * 不许说"任务仍在继续"的安慰话;能给的出路只有 grace 旋钮 + 手动重发。字节与批 D 发运稿一致
85
+ * (存量脚本可能 grep)。
86
+ * · detach 态(重连腿 M1,`x-detach-on-disconnect: true` 已随首次 POST 发出,server ≥1.196):
87
+ * 断连【不再】abort run——run 已 detach,引擎会把它后台跑完并落 durable 账本;文案如实改述并
88
+ * 指 `GET /v1/runs/<id>`(taskId 已知时带上)/ `sema resume` 查终局。M1 不重连(M2/M3),
89
+ * 本轮前台 rc 仍非零——这是净改善:结果不再整轮丢弃。
90
+ */
91
+ export declare function engineAliveIdleContent(elapsedMs: number, detach?: {
92
+ detached: true;
93
+ taskId?: string;
94
+ }): string;