@steerable/agent-shell 0.6.15

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 (204) hide show
  1. package/LICENSE +91 -0
  2. package/contracts/tool-contract.json +326 -0
  3. package/dist/attachments.d.ts +41 -0
  4. package/dist/attachments.js +147 -0
  5. package/dist/brand.d.ts +24 -0
  6. package/dist/brand.js +92 -0
  7. package/dist/host/http-routes.d.ts +21 -0
  8. package/dist/host/http-routes.js +55 -0
  9. package/dist/host/ipc.d.ts +11 -0
  10. package/dist/host/ipc.js +20 -0
  11. package/dist/host/pack-assembly.d.ts +86 -0
  12. package/dist/host/pack-assembly.js +32 -0
  13. package/dist/host/runtime.d.ts +69 -0
  14. package/dist/host/runtime.js +207 -0
  15. package/dist/host/visible-terminal-exec.d.ts +21 -0
  16. package/dist/host/visible-terminal-exec.js +151 -0
  17. package/dist/hosted-web-search.d.ts +13 -0
  18. package/dist/hosted-web-search.js +82 -0
  19. package/dist/image-attachment.d.ts +39 -0
  20. package/dist/image-attachment.js +133 -0
  21. package/dist/insights/flush.d.ts +10 -0
  22. package/dist/insights/flush.js +139 -0
  23. package/dist/insights/record.d.ts +17 -0
  24. package/dist/insights/record.js +44 -0
  25. package/dist/json-store.d.ts +9 -0
  26. package/dist/json-store.js +22 -0
  27. package/dist/llm/index.d.ts +23 -0
  28. package/dist/llm/index.js +105 -0
  29. package/dist/llm/ollama.d.ts +23 -0
  30. package/dist/llm/ollama.js +242 -0
  31. package/dist/llm/openai-compat.d.ts +20 -0
  32. package/dist/llm/openai-compat.js +199 -0
  33. package/dist/llm/sidecar-provider.d.ts +37 -0
  34. package/dist/llm/sidecar-provider.js +163 -0
  35. package/dist/llm/tool-choice.d.ts +35 -0
  36. package/dist/llm/tool-choice.js +85 -0
  37. package/dist/llm/types.d.ts +122 -0
  38. package/dist/llm/types.js +1 -0
  39. package/dist/local-backend/agent-capability.d.ts +101 -0
  40. package/dist/local-backend/agent-capability.js +174 -0
  41. package/dist/local-backend/ai-title.d.ts +43 -0
  42. package/dist/local-backend/ai-title.js +173 -0
  43. package/dist/local-backend/auto-continue-helper.d.ts +80 -0
  44. package/dist/local-backend/auto-continue-helper.js +83 -0
  45. package/dist/local-backend/branch-helper.d.ts +24 -0
  46. package/dist/local-backend/branch-helper.js +27 -0
  47. package/dist/local-backend/context-compactor.d.ts +81 -0
  48. package/dist/local-backend/context-compactor.js +213 -0
  49. package/dist/local-backend/coreloop-stream.d.ts +245 -0
  50. package/dist/local-backend/coreloop-stream.js +277 -0
  51. package/dist/local-backend/deferred-detector.d.ts +15 -0
  52. package/dist/local-backend/deferred-detector.js +124 -0
  53. package/dist/local-backend/history-helper.d.ts +30 -0
  54. package/dist/local-backend/history-helper.js +34 -0
  55. package/dist/local-backend/interrupted-helper.d.ts +29 -0
  56. package/dist/local-backend/interrupted-helper.js +25 -0
  57. package/dist/local-backend/live-stream.d.ts +36 -0
  58. package/dist/local-backend/live-stream.js +23 -0
  59. package/dist/local-backend/llm-diagnose.d.ts +37 -0
  60. package/dist/local-backend/llm-diagnose.js +284 -0
  61. package/dist/local-backend/message-triggers.d.ts +27 -0
  62. package/dist/local-backend/message-triggers.js +67 -0
  63. package/dist/local-backend/pack-backend-routes.d.ts +31 -0
  64. package/dist/local-backend/pack-backend-routes.js +62 -0
  65. package/dist/local-backend/pack-turn-hooks.d.ts +37 -0
  66. package/dist/local-backend/pack-turn-hooks.js +67 -0
  67. package/dist/local-backend/prompt-builder.d.ts +101 -0
  68. package/dist/local-backend/prompt-builder.js +246 -0
  69. package/dist/local-backend/regenerate-helper.d.ts +62 -0
  70. package/dist/local-backend/regenerate-helper.js +75 -0
  71. package/dist/local-backend/router.d.ts +175 -0
  72. package/dist/local-backend/router.js +3139 -0
  73. package/dist/local-backend/skill-install.d.ts +19 -0
  74. package/dist/local-backend/skill-install.js +71 -0
  75. package/dist/local-backend/skill-loader.d.ts +92 -0
  76. package/dist/local-backend/skill-loader.js +146 -0
  77. package/dist/local-backend/skills/00-identity/SKILL.md +32 -0
  78. package/dist/local-backend/skills/10-goal/SKILL.md +59 -0
  79. package/dist/local-backend/skills/11-loop/SKILL.md +71 -0
  80. package/dist/local-backend/skills/12-create-skill/SKILL.md +88 -0
  81. package/dist/local-backend/skills/70-plan-mode/SKILL.md +58 -0
  82. package/dist/local-backend/skills/80-tool-usage/SKILL.md +70 -0
  83. package/dist/local-backend/skills/81-anti-deferred/SKILL.md +53 -0
  84. package/dist/local-backend/skills/82-data-grounding/SKILL.md +56 -0
  85. package/dist/local-backend/skills/85-local-exec/SKILL.md +86 -0
  86. package/dist/local-backend/skills/86-proactive-coding/SKILL.md +51 -0
  87. package/dist/local-backend/subagent-profiles.d.ts +30 -0
  88. package/dist/local-backend/subagent-profiles.js +74 -0
  89. package/dist/local-backend/task-process.d.ts +12 -0
  90. package/dist/local-backend/task-process.js +176 -0
  91. package/dist/local-backend/task-service.d.ts +135 -0
  92. package/dist/local-backend/task-service.js +565 -0
  93. package/dist/local-backend/turn-duration.d.ts +2 -0
  94. package/dist/local-backend/turn-duration.js +9 -0
  95. package/dist/local-backend/turn-timeline.d.ts +16 -0
  96. package/dist/local-backend/turn-timeline.js +42 -0
  97. package/dist/local-backend/worktree-service.d.ts +84 -0
  98. package/dist/local-backend/worktree-service.js +243 -0
  99. package/dist/local-edit.d.ts +48 -0
  100. package/dist/local-edit.js +44 -0
  101. package/dist/local-executor.d.ts +255 -0
  102. package/dist/local-executor.js +881 -0
  103. package/dist/local-script-registry.d.ts +28 -0
  104. package/dist/local-script-registry.js +63 -0
  105. package/dist/log.d.ts +13 -0
  106. package/dist/log.js +12 -0
  107. package/dist/main.d.ts +1 -0
  108. package/dist/main.js +855 -0
  109. package/dist/mcp-executor.d.ts +45 -0
  110. package/dist/mcp-executor.js +241 -0
  111. package/dist/mcp-server-registry.d.ts +104 -0
  112. package/dist/mcp-server-registry.js +234 -0
  113. package/dist/preload-default.d.ts +1 -0
  114. package/dist/preload-default.js +9 -0
  115. package/dist/preload.cjs +395 -0
  116. package/dist/preload.d.ts +20 -0
  117. package/dist/preload.js +411 -0
  118. package/dist/product-config.d.ts +43 -0
  119. package/dist/product-config.js +26 -0
  120. package/dist/project-registry.d.ts +55 -0
  121. package/dist/project-registry.js +106 -0
  122. package/dist/project-rules.d.ts +15 -0
  123. package/dist/project-rules.js +102 -0
  124. package/dist/runtime.d.ts +62 -0
  125. package/dist/runtime.js +217 -0
  126. package/dist/scenario/pack.d.ts +8 -0
  127. package/dist/scenario/pack.js +1 -0
  128. package/dist/scenario/registry.d.ts +24 -0
  129. package/dist/scenario/registry.js +31 -0
  130. package/dist/server/http-server.d.ts +39 -0
  131. package/dist/server/http-server.js +361 -0
  132. package/dist/server/index.d.ts +1 -0
  133. package/dist/server/index.js +107 -0
  134. package/dist/server/sse-bus.d.ts +14 -0
  135. package/dist/server/sse-bus.js +31 -0
  136. package/dist/shell-adapt.d.ts +21 -0
  137. package/dist/shell-adapt.js +104 -0
  138. package/dist/sidecar/boot.d.ts +36 -0
  139. package/dist/sidecar/boot.js +343 -0
  140. package/dist/sidecar/egress-hint.d.ts +15 -0
  141. package/dist/sidecar/egress-hint.js +46 -0
  142. package/dist/sidecar/egress-proxy.d.ts +183 -0
  143. package/dist/sidecar/egress-proxy.js +419 -0
  144. package/dist/sidecar/errors.d.ts +22 -0
  145. package/dist/sidecar/errors.js +38 -0
  146. package/dist/sidecar/exec-sandbox.d.ts +48 -0
  147. package/dist/sidecar/exec-sandbox.js +94 -0
  148. package/dist/sidecar/handle.d.ts +32 -0
  149. package/dist/sidecar/handle.js +53 -0
  150. package/dist/sidecar/index.d.ts +14 -0
  151. package/dist/sidecar/index.js +13 -0
  152. package/dist/sidecar/proxy-detect.d.ts +50 -0
  153. package/dist/sidecar/proxy-detect.js +182 -0
  154. package/dist/sidecar/reverse-approval.d.ts +55 -0
  155. package/dist/sidecar/reverse-approval.js +86 -0
  156. package/dist/sidecar/reverse-ask-user.d.ts +34 -0
  157. package/dist/sidecar/reverse-ask-user.js +59 -0
  158. package/dist/sidecar/reverse-spawn.d.ts +19 -0
  159. package/dist/sidecar/reverse-spawn.js +161 -0
  160. package/dist/sidecar/reverse-tools.d.ts +29 -0
  161. package/dist/sidecar/reverse-tools.js +106 -0
  162. package/dist/sidecar/safety-patterns.d.ts +41 -0
  163. package/dist/sidecar/safety-patterns.js +157 -0
  164. package/dist/sidecar/storage-path.d.ts +14 -0
  165. package/dist/sidecar/storage-path.js +35 -0
  166. package/dist/sidecar/supervisor.d.ts +218 -0
  167. package/dist/sidecar/supervisor.js +932 -0
  168. package/dist/sidecar/types.d.ts +601 -0
  169. package/dist/sidecar/types.js +1 -0
  170. package/dist/single-instance.d.ts +11 -0
  171. package/dist/single-instance.js +21 -0
  172. package/dist/storage/empty-chats.d.ts +9 -0
  173. package/dist/storage/empty-chats.js +16 -0
  174. package/dist/storage/index.d.ts +373 -0
  175. package/dist/storage/index.js +1158 -0
  176. package/dist/storage/insights-redact.d.ts +2 -0
  177. package/dist/storage/insights-redact.js +30 -0
  178. package/dist/storage/insights-settings.d.ts +53 -0
  179. package/dist/storage/insights-settings.js +92 -0
  180. package/dist/storage/llm-settings.d.ts +120 -0
  181. package/dist/storage/llm-settings.js +233 -0
  182. package/dist/storage/local-store-singleton.d.ts +28 -0
  183. package/dist/storage/local-store-singleton.js +38 -0
  184. package/dist/storage/message-order.d.ts +25 -0
  185. package/dist/storage/message-order.js +27 -0
  186. package/dist/storage/pack-migrations.d.ts +22 -0
  187. package/dist/storage/pack-migrations.js +24 -0
  188. package/dist/storage/pack-seeds.d.ts +36 -0
  189. package/dist/storage/pack-seeds.js +42 -0
  190. package/dist/storage/telemetry-settings.d.ts +38 -0
  191. package/dist/storage/telemetry-settings.js +59 -0
  192. package/dist/storage/usage-summary.d.ts +55 -0
  193. package/dist/storage/usage-summary.js +38 -0
  194. package/dist/storage/web-search-settings.d.ts +38 -0
  195. package/dist/storage/web-search-settings.js +74 -0
  196. package/dist/storage/write-lease.d.ts +26 -0
  197. package/dist/storage/write-lease.js +74 -0
  198. package/dist/terminal-manager.d.ts +83 -0
  199. package/dist/terminal-manager.js +506 -0
  200. package/dist/tool-router.d.ts +228 -0
  201. package/dist/tool-router.js +930 -0
  202. package/dist/tool-search-rank.d.ts +42 -0
  203. package/dist/tool-search-rank.js +96 -0
  204. package/package.json +67 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * 单轮上下文压缩辅助(context compaction helpers)。
3
+ *
4
+ * 跨轮历史压缩已下沉框架 CoreLoop(token 压力触发 `CompactionHooks`,压缩
5
+ * 边界持久化到 durable record,桌面发全量原始历史种子、由框架 reconcile)。
6
+ * 桌面不再维护滚动摘要。本模块只剩两类纯函数:
7
+ *
8
+ * 1. **单轮工具结果截断**:`compactToolResultJson` 在把 tool 结果写回
9
+ * messages 前逐字段截断 + 总量封顶(reverse-tools 用),`truncateMiddle`
10
+ * 是通用中间截断原语。
11
+ * 2. **@引用对话摘录**:`formatHistoryForSummary` 把最近消息格式化成
12
+ * "用户:… / 助手:…" 的 transcript 摘录(referenced-chat 上下文用)。
13
+ *
14
+ * 全部是纯函数,不依赖 electron / 数据库,便于单测。
15
+ */
16
+ // ─── token 估算 ──────────────────────────────────────────────────────────────
17
+ /**
18
+ * 粗略 token 估算(无 tokenizer 依赖)。
19
+ * 经验值:CJK 字符 ≈ 0.6 token/字(DeepSeek 官方口径),其它字符 ≈ 0.25 token/字。
20
+ * 用于"要不要压缩"的阈值判断,不追求精确。
21
+ */
22
+ export function estimateTokens(text) {
23
+ if (!text)
24
+ return 0;
25
+ let cjk = 0;
26
+ for (let i = 0; i < text.length; i++) {
27
+ const code = text.charCodeAt(i);
28
+ // CJK 统一表意文字 + 常用中文标点区间
29
+ if ((code >= 0x4e00 && code <= 0x9fff) ||
30
+ (code >= 0x3000 && code <= 0x303f) ||
31
+ (code >= 0xff00 && code <= 0xffef)) {
32
+ cjk++;
33
+ }
34
+ }
35
+ const other = text.length - cjk;
36
+ return Math.ceil(cjk * 0.6 + other * 0.25);
37
+ }
38
+ /** 估算整个 messages 数组的 token(content + reasoningContent + toolCalls 参数)。 */
39
+ export function estimateMessagesTokens(messages) {
40
+ let total = 0;
41
+ for (const msg of messages) {
42
+ total += estimateTokens(msg.content || '');
43
+ if (msg.reasoningContent)
44
+ total += estimateTokens(msg.reasoningContent);
45
+ if (msg.toolCalls) {
46
+ for (const call of msg.toolCalls) {
47
+ total += estimateTokens(call.name) + estimateTokens(JSON.stringify(call.arguments || {}));
48
+ }
49
+ }
50
+ total += 8; // 每条消息的角色 / 分隔符开销
51
+ }
52
+ return total;
53
+ }
54
+ // ─── 字符串截断原语 ──────────────────────────────────────────────────────────
55
+ const TRUNCATION_MARKER = (omitted) => `\n…[已截断 ${omitted} 字符]…\n`;
56
+ /**
57
+ * 中间截断:保留头 60% + 尾 40%(头部通常是结构化字段,尾部通常是 error / 退出码)。
58
+ * `maxChars` 含截断标记本身。
59
+ */
60
+ export function truncateMiddle(text, maxChars) {
61
+ if (text.length <= maxChars)
62
+ return text;
63
+ const marker = TRUNCATION_MARKER(text.length - maxChars);
64
+ const budget = Math.max(40, maxChars - marker.length);
65
+ const head = Math.ceil(budget * 0.6);
66
+ const tail = budget - head;
67
+ return text.slice(0, head) + marker + text.slice(text.length - tail);
68
+ }
69
+ const DEFAULT_TOOL_RESULT_MAX_TOTAL = 8000;
70
+ const DEFAULT_TOOL_RESULT_MAX_FIELD = 2000;
71
+ const DEFAULT_TOOL_RESULT_MAX_ARRAY = 50;
72
+ /** 递归截断对象里的长字符串字段 / 超长数组,返回新对象(不改原值)。 */
73
+ export function deepTruncateStrings(value, maxFieldChars, maxArrayItems = DEFAULT_TOOL_RESULT_MAX_ARRAY) {
74
+ if (typeof value === 'string') {
75
+ return value.length > maxFieldChars ? truncateMiddle(value, maxFieldChars) : value;
76
+ }
77
+ if (Array.isArray(value)) {
78
+ const capped = value.slice(0, maxArrayItems).map((item) => deepTruncateStrings(item, maxFieldChars, maxArrayItems));
79
+ if (value.length > maxArrayItems) {
80
+ capped.push(`…[已省略 ${value.length - maxArrayItems} 项]`);
81
+ }
82
+ return capped;
83
+ }
84
+ if (value && typeof value === 'object') {
85
+ const out = {};
86
+ for (const [k, v] of Object.entries(value)) {
87
+ out[k] = deepTruncateStrings(v, maxFieldChars, maxArrayItems);
88
+ }
89
+ return out;
90
+ }
91
+ return value;
92
+ }
93
+ /**
94
+ * 压缩一条要塞回 LLM 上下文的 tool 结果 JSON。
95
+ * 短的原样返回;超限先逐字段截断,仍超限则整体中间截断并包成合法 JSON 信封,
96
+ * 保证输出永远是合法 JSON(部分 OpenAI 兼容服务器会解析 tool content)。
97
+ *
98
+ * 注意:只影响喂给 LLM 的 messages,**不影响**落库 / 前端展示用的
99
+ * executedActions(那份保留完整结果)。
100
+ */
101
+ export function compactToolResultJson(json, options = {}) {
102
+ const maxTotal = options.maxTotalChars ?? DEFAULT_TOOL_RESULT_MAX_TOTAL;
103
+ const maxField = options.maxFieldChars ?? DEFAULT_TOOL_RESULT_MAX_FIELD;
104
+ const maxArray = options.maxArrayItems ?? DEFAULT_TOOL_RESULT_MAX_ARRAY;
105
+ if (json.length <= maxTotal)
106
+ return json;
107
+ try {
108
+ const parsed = JSON.parse(json);
109
+ let out = JSON.stringify(deepTruncateStrings(parsed, maxField, maxArray));
110
+ if (out.length <= maxTotal)
111
+ return out;
112
+ // 逐字段截断后仍超限(大量小字段 / 深层结构):按比例再收紧字段上限
113
+ const tighterField = Math.max(200, Math.floor((maxField * maxTotal) / out.length));
114
+ out = JSON.stringify(deepTruncateStrings(parsed, tighterField, Math.min(maxArray, 20)));
115
+ if (out.length <= maxTotal)
116
+ return out;
117
+ }
118
+ catch {
119
+ // 非法 JSON:走下面的信封兜底
120
+ }
121
+ return JSON.stringify({
122
+ truncated: true,
123
+ originalChars: json.length,
124
+ note: '工具结果过长,已截断。完整结果已展示给用户,无需复述原文。',
125
+ preview: truncateMiddle(json, Math.max(500, maxTotal - 300)),
126
+ });
127
+ }
128
+ /** 从 tool 结果 JSON 里抽取关键语义字段,生成收缩版 content(合法 JSON)。 */
129
+ function briefToolContent(json, maxChars) {
130
+ const PICK_KEYS = [
131
+ 'success', 'status', 'error', 'message', 'hint',
132
+ 'exitCode', 'code', 'taskId', 'total', 'totalFound',
133
+ ];
134
+ try {
135
+ const parsed = JSON.parse(json);
136
+ const result = parsed && typeof parsed.result === 'object' && parsed.result !== null
137
+ ? parsed.result
138
+ : parsed;
139
+ const picked = {};
140
+ for (const key of PICK_KEYS) {
141
+ if (key in result && result[key] !== undefined && result[key] !== null) {
142
+ const v = result[key];
143
+ picked[key] = typeof v === 'string' && v.length > 300 ? truncateMiddle(v, 300) : v;
144
+ }
145
+ }
146
+ return JSON.stringify({
147
+ compacted: true,
148
+ note: '完整结果已因上下文超限被压缩,仅保留关键字段。',
149
+ result: picked,
150
+ });
151
+ }
152
+ catch {
153
+ return JSON.stringify({
154
+ compacted: true,
155
+ note: '完整结果已因上下文超限被压缩。',
156
+ preview: truncateMiddle(json, maxChars),
157
+ });
158
+ }
159
+ }
160
+ /**
161
+ * 每轮 LLM 调用前对 messages 做**原地**滚动压缩:
162
+ * 估算 token 超出预算时,从最旧的 tool 消息开始把 content 收缩成关键字段摘要,
163
+ * 直到回到预算内或没有可压对象。最近 `keepRecentToolResults` 条 tool 消息
164
+ * 永远保持原文(当前任务大概率还依赖它们)。
165
+ *
166
+ * 只动 tool 消息的 content:
167
+ * - 消息条数 / 顺序 / toolCallId 不变 → 不破坏 assistant(tool_calls)/tool 配对;
168
+ * - 不碰 reasoningContent(DeepSeek thinking 模式要求原样回传)。
169
+ */
170
+ export function compactMessagesForContext(messages, options) {
171
+ const keepRecent = options.keepRecentToolResults ?? 4;
172
+ const compactedMaxChars = options.compactedMaxChars ?? 600;
173
+ let total = estimateMessagesTokens(messages);
174
+ if (total <= options.maxContextTokens) {
175
+ return { compactedCount: 0, estimatedTokens: total };
176
+ }
177
+ const toolIndexes = [];
178
+ for (let i = 0; i < messages.length; i++) {
179
+ if (messages[i].role === 'tool')
180
+ toolIndexes.push(i);
181
+ }
182
+ const protectedIndexes = new Set(toolIndexes.slice(-keepRecent));
183
+ let compactedCount = 0;
184
+ for (const idx of toolIndexes) {
185
+ if (total <= options.maxContextTokens)
186
+ break;
187
+ if (protectedIndexes.has(idx))
188
+ continue;
189
+ const msg = messages[idx];
190
+ if (!msg.content || msg.content.length <= compactedMaxChars)
191
+ continue;
192
+ const before = estimateTokens(msg.content);
193
+ msg.content = briefToolContent(msg.content, compactedMaxChars);
194
+ total -= before - estimateTokens(msg.content);
195
+ compactedCount++;
196
+ }
197
+ return { compactedCount, estimatedTokens: total };
198
+ }
199
+ // ─── @引用对话摘录 ───────────────────────────────────────────────────────────
200
+ /**
201
+ * 把消息列表格式化成 "用户:… / 助手:…" 的 transcript 摘录,单条消息中间
202
+ * 截断防止单条爆掉。referenced-chat(@引用别的对话)上下文用。
203
+ */
204
+ export function formatHistoryForSummary(items, perMessageMaxChars = 1200) {
205
+ return items
206
+ .map((item) => {
207
+ const label = item.role === 'user' ? '用户' : '助手';
208
+ const content = truncateMiddle((item.content || '').trim(), perMessageMaxChars);
209
+ return `${label}:${content}`;
210
+ })
211
+ .filter((line) => line.length > 3)
212
+ .join('\n\n');
213
+ }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * A4: drive one chat turn through the sidecar-hosted Python CoreLoop.
3
+ *
4
+ * This is the default chat path (since 2026-08-26): the sidecar owns
5
+ * think→act→observe, and tool calls come back over the reverse channel
6
+ * (`tool.invoke`, registered in `main.ts`). This module only translates the
7
+ * wire notifications into the existing SSE contract — the renderer, preload
8
+ * and IPC layers are untouched.
9
+ *
10
+ * This is the only chat path (the TS loop was deleted 2026-08-26). If the
11
+ * sidecar failed to start, the router fails loud with a 503 — there is no
12
+ * in-process fallback loop anymore. `STEERABLE_USE_SIDECAR=0` still
13
+ * disables the sidecar process itself (auxiliary LLM calls then fall back
14
+ * to the in-proc providers), but chat requires the sidecar.
15
+ */
16
+ import type { SidecarChatStreamRequest, SidecarChildEvent, SidecarRawChunk, SidecarSupervisor } from '../sidecar/index.js';
17
+ import type { LlmMessage } from '../llm/types.js';
18
+ export interface CoreLoopToolAction {
19
+ id?: string;
20
+ tool: string;
21
+ arguments: Record<string, unknown>;
22
+ success: boolean;
23
+ result?: string;
24
+ error?: string;
25
+ durationMs?: number;
26
+ /**
27
+ * W4-2: per-exec sandbox marker from `SandboxedToolExecutor`
28
+ * (`data._sandbox` on the tool result). `enforcement: full | partial |
29
+ * none` — rendered on the tool card so the confinement actually applied
30
+ * is user-visible.
31
+ */
32
+ sandbox?: {
33
+ backend?: string;
34
+ enforcement: string;
35
+ };
36
+ }
37
+ export interface CoreLoopTurnOutcome {
38
+ status: string;
39
+ reason?: string;
40
+ /** Sidecar trace id for this turn — the router persists it into
41
+ * harness_traces so the CoreLoop path leaves the same audit trail as the
42
+ * TS loop (and dogfood traces can be replayed/diffed). */
43
+ traceId?: string;
44
+ /** W6-9: the run's accumulated billable usage (+ costUsd when priced). */
45
+ usage?: {
46
+ promptTokens: number;
47
+ completionTokens: number;
48
+ totalTokens: number;
49
+ cachedPromptTokens?: number;
50
+ costUsd?: number;
51
+ };
52
+ }
53
+ /**
54
+ * A6 分层披露:本轮技能目录的过滤上下文。sidecar 据此注入 catalog 层
55
+ * (首轮 pre_step 写入系统消息,hook_action 落 trace)并用 `skill` 工具
56
+ * 按需返回技能正文;eager 层正文仍由宿主系统提示词携带。
57
+ */
58
+ export interface SkillTurnContext {
59
+ /** 活跃条件串(tool:* / has-tools / plan-mode),与提示词拼装同源。 */
60
+ conditions: string[];
61
+ /**
62
+ * 目录里不出现的技能:模式级排除(plan 模式排掉执行类技能)、智能体关闭
63
+ * 「允许其他技能」时的未勾选项、以及已随系统提示词常驻的勾选项(避免
64
+ * 模型再调 `skill` 工具加载第二遍)。
65
+ */
66
+ exclude: string[];
67
+ /** 智能体的「加载全部技能」:catalog 无视条件全量列出。 */
68
+ ignoreConditions: boolean;
69
+ }
70
+ export interface StreamCoreLoopTurnOptions {
71
+ supervisor: SidecarSupervisor;
72
+ chatId: string;
73
+ /** W5-2: the chat's active durable record (a branch id after a
74
+ * regenerate-fork). Undefined = the chatId itself. */
75
+ recordId?: string;
76
+ /**
77
+ * W7-1: continue the durable record's interrupted turn. The sidecar
78
+ * replays the record's projection (dangling tool_calls closed) as the
79
+ * loop seed; `messages` must be empty — the record is authoritative and
80
+ * the host neither re-sends the last user message nor fabricates a
81
+ * synthetic continuation prompt.
82
+ */
83
+ resume?: boolean;
84
+ messages: LlmMessage[];
85
+ tools: Array<{
86
+ name: string;
87
+ description: string;
88
+ inputSchema: unknown;
89
+ }>;
90
+ provider: string;
91
+ model: string;
92
+ baseUrl?: string;
93
+ apiKey?: string;
94
+ temperature?: number;
95
+ /** W2.8.2: assembled system prompt, sent as a typed fragment param (cap
96
+ * enforced sidecar-side) instead of a leading system message. */
97
+ systemPrompt?: string;
98
+ /** W1.3.2 explicit compat flag overrides, forwarded verbatim; omitted →
99
+ * the sidecar auto-detects from the base-URL host. */
100
+ compat?: SidecarChatStreamRequest['compat'];
101
+ /** Provider-preset choice (auto/off/pinned), forwarded verbatim; omitted →
102
+ * the sidecar auto-matches the llm.presets registry on base-URL+model. */
103
+ presets?: SidecarChatStreamRequest['presets'];
104
+ /** Per-turn reasoning effort from the chat model picker. Forwarded
105
+ * verbatim; the sidecar validates it strict against the model's catalog
106
+ * entry, so an unsupportable level fails the turn at stream start
107
+ * (invalid_params) instead of being silently dropped. */
108
+ reasoningEffort?: string;
109
+ /** Forwarded on every reverse tool.invoke (host enforces plan-mode etc.). */
110
+ toolContext?: Record<string, unknown>;
111
+ /**
112
+ * 4.6a: advertise the sidecar-hosted ask_user tool. Default on for chat
113
+ * turns; background task streams pass `false` — no user is watching the
114
+ * question card, and the task prompt tells the model not to ask.
115
+ */
116
+ askUser?: boolean;
117
+ /**
118
+ * 分层技能披露:skills 根目录 + 本轮过滤上下文。缺省 = 技能机制整体
119
+ * 旁路(显式自定义系统提示词的回合,与旧行为一致)。
120
+ */
121
+ skills?: SkillTurnContext & {
122
+ roots: string[];
123
+ };
124
+ /**
125
+ * Wave 2 world-state:慢变宿主上下文(当前时间/时区)作为数据节下发。
126
+ * sidecar 首轮注入 <world-state> 片段,后续轮次与记录里的快照 diff——
127
+ * 未变零 token,变了只追加一个 RFC 7386 小补丁。由 buildWorldState()
128
+ * 构建;缺省 = 不注入(与旧行为一致)。
129
+ */
130
+ worldState?: Record<string, unknown>;
131
+ /**
132
+ * W4-2: per-exec sandbox config forwarded verbatim to the sidecar
133
+ * (`SandboxedToolExecutor`). The router builds it from the chat's project
134
+ * binding (writableRoots = project root) and the provider baseUrl
135
+ * (allowedHosts when network is on).
136
+ */
137
+ execSandbox?: SidecarChatStreamRequest['execSandbox'];
138
+ /**
139
+ * W4-1: approval algebra config forwarded verbatim to the sidecar
140
+ * (`ApprovalExecutor` + `HostApprover`). The router enables host mode;
141
+ * the reverse-channel handler in main.ts drives the Electron UI.
142
+ */
143
+ approval?: SidecarChatStreamRequest['approval'];
144
+ /**
145
+ * P3.1 multi-agent orchestration (六件套), forwarded verbatim to the
146
+ * sidecar. OPT-IN advanced mode: only `enabled: true` exposes
147
+ * agent_spawn/send/wait/close/list/interrupt. Default off since the
148
+ * delegate-on-pool unification — the model's multi-agent surface is
149
+ * `delegate_subagent` (see `subagent`).
150
+ */
151
+ orchestration?: SidecarChatStreamRequest['orchestration'];
152
+ /**
153
+ * delegate-on-pool 委派,原样转发给 sidecar。桌面默认开(`subagent:
154
+ * true`):模型获得单工具 delegate_subagent,子代理作为 AgentPool 池化
155
+ * 子运行执行,生命周期经 `agent.child` 通知(由 `onChildEvent` 上抛)
156
+ * 进编排卡片;concurrent profile 的同轮委派在池预算内并行。
157
+ */
158
+ subagent?: SidecarChatStreamRequest['subagent'];
159
+ /** W6-9: per-turn token budget override. When set, forwarded as
160
+ * `budgetTokens` and the sidecar uses it instead of its rounds-scaled
161
+ * default. */
162
+ budgetTokens?: number;
163
+ signal?: AbortSignal;
164
+ /** Fired once the sidecar streamId is known — needed for steerChat. */
165
+ onStreamId?: (streamId: string) => void;
166
+ /** Assistant text chunk, display-cleaned by the sidecar already. */
167
+ onText: (delta: string) => void;
168
+ /** Thinking-model chain-of-thought delta (sidecar `reasoningDelta`). */
169
+ onReasoning?: (delta: string) => void;
170
+ /** Fired when a tool call starts (running row on the timeline). */
171
+ onToolStart?: (call: {
172
+ id: string;
173
+ tool: string;
174
+ arguments: Record<string, unknown>;
175
+ }) => void;
176
+ /** Fired when a tool finishes on the host (for tool-card rendering). */
177
+ onToolAction?: (action: CoreLoopToolAction) => void;
178
+ /** soft_timeout / budget_exhausted notices from the loop. */
179
+ onNotice?: (kind: string, notice?: {
180
+ kind: string;
181
+ [key: string]: unknown;
182
+ }) => void;
183
+ /**
184
+ * Opt-in (`streamRawChunks: true`, default off): every pre-digestion
185
+ * provider chunk the sidecar's `on_stream_chunk` hook observed, before
186
+ * UI-tag stripping produced the display `delta`. For incremental
187
+ * renderers; costs one notification per chunk.
188
+ */
189
+ streamRawChunks?: boolean;
190
+ /** Fired per raw provider chunk when `streamRawChunks` is on. */
191
+ onRawChunk?: (chunk: SidecarRawChunk) => void;
192
+ /** P3.1: child-agent lifecycle events (spawn/complete/fail/interrupt/
193
+ * resume), demuxed from the sidecar's `agent.child` notifications —
194
+ * fired by orchestration children and by delegate_subagent delegations. */
195
+ onChildEvent?: (event: SidecarChildEvent) => void;
196
+ }
197
+ /** Sidecar streamId of the chat's running CoreLoop turn, if any. */
198
+ export declare function getActiveCoreLoopStreamId(chatId: string): string | undefined;
199
+ /**
200
+ * W6-7b world-state 扩面的输入。除 time 外各节都可缺省——缺省的节不出现在
201
+ * 快照里,与"只喂了 time 一节"的旧行为一致。各节都是小型对比数据(不是
202
+ * 正文),sidecar 逐节 merge-patch diff:没变零 token,变了只补一个小补丁。
203
+ */
204
+ export interface WorldStateInput {
205
+ /** 当前时间(可注入以便测试);缺省 = 现在。 */
206
+ now?: Date;
207
+ /** 协作模式(agent/plan)。模型据此明确当前模式,而不只靠系统提示词。 */
208
+ mode?: 'agent' | 'plan';
209
+ /**
210
+ * 权限姿态:approval 模式 + 执行沙箱。让模型事先知道哪些目录可写、网络
211
+ * 是否开、工具调用是否要过审批,避免去尝试必然被拦的操作。
212
+ */
213
+ permissions?: {
214
+ approval: 'host' | 'off';
215
+ sandbox: {
216
+ enabled: boolean;
217
+ writableRoots: string[];
218
+ network: boolean;
219
+ };
220
+ };
221
+ /**
222
+ * 技能姿态(紧凑):本轮活跃条件 + 模式级排除清单。只放过滤上下文,不放
223
+ * 技能正文——正文由分层披露(catalog/eager)机制携带,这里给模型一个
224
+ * "当前技能过滤面"的可 diff 数据视图。
225
+ */
226
+ skills?: {
227
+ conditions: string[];
228
+ exclude: string[];
229
+ };
230
+ /**
231
+ * 场景包附加节(1.2 pack 回合钩子):原样并入输出。包用包前缀键
232
+ * (如 `<pack>_workspace`)避免与 shell 节碰撞。
233
+ */
234
+ extra?: Record<string, unknown>;
235
+ }
236
+ /**
237
+ * 构建本轮的 world-state 节(Wave 2 起;W6-7b 从只有 time 扩到多节)。
238
+ *
239
+ * time 节分钟精度是刻意的:模型不需要秒,而快照在同一分钟内稳定,sidecar
240
+ * 的 merge-patch diff 对连续轮次就是零 token 空转;跨分钟只追加一个几
241
+ * token 的尾部补丁,系统提示词前缀保持字节稳定(cache 净收益)。其余各节
242
+ * (mode/permissions/skills)同理——慢变,变了才补一个小补丁。
243
+ */
244
+ export declare function buildWorldState(input?: WorldStateInput): Record<string, unknown>;
245
+ export declare function streamCoreLoopTurn(options: StreamCoreLoopTurnOptions): Promise<CoreLoopTurnOutcome>;