@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,601 @@
1
+ import type { SidecarHealth, ToolResult } from '@steerable/agent-protocol';
2
+ export type SidecarHealthSnapshot = SidecarHealth;
3
+ export type SidecarToolResult = ToolResult;
4
+ /**
5
+ * W4-3: layer-1 (sidecar process sandbox) posture, recorded by the
6
+ * supervisor at spawn-plan time. `enforcement` mirrors the layer-3
7
+ * `_sandbox.enforcement` vocabulary (`full | partial | none`) so both
8
+ * layers read consistently; layer 1 never reports `full` because Seatbelt
9
+ * egress on remote hosts is port-only (docs/spec/safety.md). `reason`
10
+ * distinguishes an explicit opt-out (`disabled_by_option` /
11
+ * `disabled_by_env`) from a refused start
12
+ * (`platform_unsupported` / `seatbelt_missing` / `profile_failed` /
13
+ * `wrap_failed` / `helper_missing`) — confinement requested means the
14
+ * process does not run unsandboxed. The settings UI warns on the latter.
15
+ */
16
+ export interface SidecarSandboxPosture {
17
+ backend: 'seatbelt' | 'bwrap' | 'landlock' | 'windows-restricted-token' | 'none';
18
+ enforcement: 'partial' | 'none';
19
+ reason: 'active' | 'disabled_by_option' | 'disabled_by_env' | 'platform_unsupported' | 'seatbelt_missing' | 'profile_failed' | 'wrap_failed' | 'helper_missing';
20
+ }
21
+ export interface SidecarStartOptions {
22
+ /** Override the python binary; defaults to the bundled portable runtime. */
23
+ pythonExecutable?: string;
24
+ /** Override the entrypoint module; defaults to ``steerable_sidecar``. */
25
+ entryModule?: string;
26
+ /** Extra arguments appended after ``-m <entryModule>``. */
27
+ args?: string[];
28
+ /** Cwd for the spawned process. */
29
+ cwd?: string;
30
+ /** Environment variables to inject. */
31
+ env?: NodeJS.ProcessEnv;
32
+ /** Max ms to wait for the ready handshake before failing. Default 15000. */
33
+ bootTimeoutMs?: number;
34
+ /** ms between health pings. Default 5000. Set <=0 to disable. */
35
+ healthIntervalMs?: number;
36
+ /** consecutive ping failures that trigger an automatic restart. Default 3. */
37
+ restartAfterFailedPings?: number;
38
+ /**
39
+ * Confine the sidecar in an OS sandbox. macOS: Seatbelt. Linux: bwrap
40
+ * then Landlock wrapping the python process. Windows: win-spawn-helper
41
+ * `--passthrough` (restricted token + Job Object) with inherited stdio.
42
+ * If confinement cannot be applied, start() refuses rather than spawning
43
+ * unsandboxed. `false` or `STEERABLE_SIDECAR_SANDBOX=0` is the only
44
+ * unsandboxed path. Default ON.
45
+ */
46
+ sandbox?: boolean;
47
+ /**
48
+ * Egress allow-list for the sandboxed sidecar (entries `host` or
49
+ * `host:port`; bare hosts allow 443+80). Once any entry is given the
50
+ * profile fails closed: outbound is denied except to the declared
51
+ * endpoints. Unset/empty keeps outbound fully open (the default).
52
+ * Seatbelt cannot match hostnames — localhost entries pin
53
+ * `localhost:PORT` exactly, remote entries degrade to their port.
54
+ * Only consulted when `sandbox` is on. Defaults to the
55
+ * `STEERABLE_SIDECAR_SANDBOX_ALLOWED_HOSTS` env (comma-separated).
56
+ */
57
+ sandboxAllowedHosts?: string[];
58
+ /**
59
+ * Also allow what the network-read tools (`web_fetch`/`web_search`) need on
60
+ * top of the allow-list: the system resolver plus outbound http(s) to any
61
+ * host. Their targets are whatever the model asks for, so no host list can
62
+ * name them ahead of time — without this every fetch fails name resolution
63
+ * inside the SSRF pre-check. Set it only where those tools are offered; it
64
+ * grants reach to any host on ports 80 and 443. No effect when the
65
+ * allow-list is unset (outbound already open) or `sandbox` is off.
66
+ */
67
+ sandboxWebEgress?: boolean;
68
+ /**
69
+ * Allow name resolution (the system resolver socket — no IP reach) on top
70
+ * of a fail-closed egress allow-list. Set when egress is pinned to the
71
+ * per-host proxy and the web tools stay offered: `web_fetch`'s SSRF
72
+ * pre-check resolves locally even though the fetch itself tunnels through
73
+ * the proxy. No effect when the allow-list is unset (outbound already
74
+ * open) or `sandboxWebEgress` is on (its profile already grants the
75
+ * resolver). macOS Seatbelt only.
76
+ */
77
+ sandboxAllowResolver?: boolean;
78
+ /**
79
+ * Extra directories the confined sidecar may write to, beyond the default
80
+ * `~/.steerable` root. Each entry is forwarded to the platform sandbox as
81
+ * an additional writable root (macOS Seatbelt `--writable-root`, Linux
82
+ * bwrap/Landlock `--writable-root`, Windows win-spawn-helper
83
+ * `--writable-root`). Needed whenever `--storage-path` (or any other
84
+ * sidecar-written file) lives outside `~/.steerable` — e.g. tests keeping
85
+ * sessions.db in a temp dir, or BS mode with `DEEPPATH_USER_DATA_DIR`.
86
+ */
87
+ sandboxWritableRoots?: string[];
88
+ /** Optional hook invoked whenever the sidecar pushes a stream notification. */
89
+ onStreamChunk?: (params: unknown) => void;
90
+ /** Optional hook for log lines emitted on stderr. */
91
+ onLogLine?: (line: string) => void;
92
+ }
93
+ export interface SidecarMethodOptions {
94
+ /** Per-call timeout in ms. Default 60_000. */
95
+ timeoutMs?: number;
96
+ }
97
+ /**
98
+ * A request the sidecar sends *to* the host over the reverse channel
99
+ * (see spec/sidecar/README.md "Reverse channel"). Distinguished from a
100
+ * response by the presence of both `id` and `method`.
101
+ */
102
+ export interface SidecarReverseRequest {
103
+ /** Reverse-call id; sidecar uses `srv_`-prefixed strings. */
104
+ id: string;
105
+ method: string;
106
+ params?: unknown;
107
+ }
108
+ /**
109
+ * Host-side handler for a reverse request. Receives the params and returns
110
+ * the `result` payload to send back, or throws to return an error.
111
+ */
112
+ export type SidecarReverseHandler = (params: unknown) => Promise<unknown> | unknown;
113
+ /** W5-2: `agent.session.fork` result — the created branch point. */
114
+ export interface SidecarSessionForkResult {
115
+ recordId: string;
116
+ sourceRecordId: string | null;
117
+ sourceUntilSeq: number | null;
118
+ label: string;
119
+ seedMessages: number;
120
+ }
121
+ /**
122
+ * Result of an `agent.session.fork` attempt.
123
+ *
124
+ * `declined` means the sidecar answered and rejected the address — no record, or
125
+ * a fork point it will not split, such as an ordinal inside a branch seed, where
126
+ * the protocol documents host fallback as the expected path. Anything else is a
127
+ * fork that was supposed to happen and did not (transport, timeout, fault), and
128
+ * the caller must not treat the two alike: only the first is a path the
129
+ * framework sanctions.
130
+ */
131
+ export type SidecarSessionForkOutcome = {
132
+ ok: true;
133
+ fork: SidecarSessionForkResult;
134
+ } | {
135
+ ok: false;
136
+ declined: boolean;
137
+ reason: string;
138
+ };
139
+ /** W1.2.1: one node in an `agent.session.branches` lineage/children list. */
140
+ export interface SidecarBranchPoint {
141
+ recordId: string;
142
+ sourceRecordId: string | null;
143
+ sourceUntilSeq: number | null;
144
+ label: string;
145
+ depth?: number;
146
+ }
147
+ /** W1.2.1: `agent.session.branches` result. */
148
+ export interface SidecarSessionBranches {
149
+ lineage: SidecarBranchPoint[];
150
+ children: SidecarBranchPoint[];
151
+ }
152
+ /**
153
+ * Session tree: one node in an `agent.session.tree` family tree
154
+ * (recursive). Same fields as {@link SidecarBranchPoint} plus nested
155
+ * `children`; `depth` is 0 on the family root and matches lineage
156
+ * numbering along the chain.
157
+ */
158
+ export interface SidecarSessionTreeNode {
159
+ recordId: string;
160
+ sourceRecordId: string | null;
161
+ sourceUntilSeq: number | null;
162
+ label: string;
163
+ depth: number;
164
+ children: SidecarSessionTreeNode[];
165
+ }
166
+ /**
167
+ * Session tree: `agent.session.tree` result — the full branch family
168
+ * containing the queried record, expanded from the family root.
169
+ * `truncated` means the sidecar's safety bounds (depth 32 / 500 nodes)
170
+ * cut the expansion; the tree is still valid below the cut.
171
+ */
172
+ export interface SidecarSessionTree {
173
+ recordId: string;
174
+ tree: SidecarSessionTreeNode;
175
+ nodeCount: number;
176
+ truncated: boolean;
177
+ }
178
+ /** W1.2.1: `agent.session.messages` result — the projected visible span. */
179
+ export interface SidecarSessionMessages {
180
+ recordId: string;
181
+ messages: Array<{
182
+ seq: number;
183
+ role: string;
184
+ content: string;
185
+ }>;
186
+ }
187
+ /**
188
+ * W6-1: `workspace.apply_edits` result — the pure structured-edit algorithm
189
+ * run in the sidecar on host-supplied content. The host owns all file I/O.
190
+ */
191
+ export interface SidecarApplyEditsResult {
192
+ content: string;
193
+ diff: string;
194
+ applied: number;
195
+ matches: Array<{
196
+ level: 'exact' | 'trim' | 'unicode';
197
+ startLine: number;
198
+ oldLineCount: number;
199
+ }>;
200
+ }
201
+ /**
202
+ * W6-7/skills: `skills.list` result item — a parsed SKILL.md module, mirroring
203
+ * the desktop's `SkillModule`. Parsing is single-sourced in the framework's
204
+ * `skills.py`; the desktop no longer re-parses frontmatter.
205
+ */
206
+ export interface SidecarSkillModule {
207
+ name: string;
208
+ displayName: string;
209
+ description: string;
210
+ priority: number;
211
+ tags: string[];
212
+ conditions: string[];
213
+ match: 'any' | 'all';
214
+ layer: 'eager' | 'catalog';
215
+ modelInvocable: boolean;
216
+ content: string;
217
+ dirName: string;
218
+ skillsDir: string;
219
+ }
220
+ /**
221
+ * Wire shape for ``agent.chat.stream`` requests, mirrored from
222
+ * ``packages/sidecar/py/src/steerable_sidecar/sidecar.py``.
223
+ */
224
+ export interface SidecarChatStreamRequest {
225
+ provider: string;
226
+ model: string;
227
+ messages: Array<{
228
+ role: string;
229
+ content: string;
230
+ name?: string;
231
+ toolCallId?: string;
232
+ /**
233
+ * Assistant 消息的工具调用回传:缺失时下一轮里的 `role:'tool'` 消息会
234
+ * 被 OpenAI 严格协议判为孤儿(400 "must be a response to a preceding
235
+ * message with 'tool_calls'")。与 in-process openai-compat.ts 的
236
+ * mapMessage 同一约束。
237
+ */
238
+ toolCalls?: Array<{
239
+ id: string;
240
+ name: string;
241
+ arguments: Record<string, unknown>;
242
+ }>;
243
+ /**
244
+ * Thinking 模型的推理回传(DeepSeek thinking 模式强制要求,缺了第二轮
245
+ * 400)。sidecar 侧按 compat.reasoningEchoField 落到正确字段名。
246
+ */
247
+ reasoningContent?: string;
248
+ /**
249
+ * W6-3: structured content parts (multimodal). When present the sidecar
250
+ * treats it as authoritative over `content` (which then is just the text
251
+ * projection). Mirrors `spec/chat/ContentPart.schema.json`.
252
+ */
253
+ parts?: Array<{
254
+ type: 'text';
255
+ text: string;
256
+ } | {
257
+ type: 'image';
258
+ data?: string;
259
+ url?: string;
260
+ mediaType?: string;
261
+ }>;
262
+ }>;
263
+ baseUrl?: string;
264
+ apiKey?: string;
265
+ temperature?: number;
266
+ /** W2.8.2: the host-assembled system prompt as a typed fragment (the
267
+ * sidecar enforces its token cap at the seed boundary). Mutually
268
+ * exclusive with a leading `system` message in `messages` — the sidecar
269
+ * rejects both-present as a host bug. */
270
+ systemPrompt?: string;
271
+ /** W1.3.2 explicit OpenAI-compat flag overrides (camelCase wire keys owned
272
+ * by the framework's `OpenAICompatFlags.from_dict`; unknown keys fail loud
273
+ * there). Omitted → the sidecar auto-detects from the base-URL host. */
274
+ compat?: Record<string, unknown>;
275
+ /** Provider-preset choice (framework `llm.presets`): omitted/{"enabled":
276
+ * true} → auto-match on base-URL+model; {"enabled": false} → layer off;
277
+ * {"override": {...}} → pinned preset (camelCase keys owned by the
278
+ * framework's `ProviderPreset.from_dict`; unknown keys fail loud there). */
279
+ presets?: Record<string, unknown>;
280
+ maxTokens?: number;
281
+ /**
282
+ * Per-turn reasoning effort from the host's model picker. The sidecar
283
+ * validates it strict against the resolved catalog entry (a level the
284
+ * model cannot honor is an `invalid_params` RPC error at stream start,
285
+ * never a silently dropped field); omitted → env/preset defaults apply.
286
+ */
287
+ reasoningEffort?: string;
288
+ tools?: unknown[];
289
+ streamId?: string;
290
+ providerOptions?: Record<string, unknown>;
291
+ /** Per-start RPC timeout (NOT per-chunk). Default 30_000ms. */
292
+ startTimeoutMs?: number;
293
+ /** Route the stream through the sidecar-hosted Python CoreLoop (A4). */
294
+ useCoreLoop?: boolean;
295
+ /**
296
+ * W7-1: continue the durable record's interrupted turn instead of opening
297
+ * a new one. The sidecar substitutes the record's projected transcript
298
+ * (dangling tool_calls closed) as the loop seed; `messages` must be empty
299
+ * and `recordId` (or `chatId`) must name a non-empty record. CoreLoop-only.
300
+ */
301
+ resume?: boolean;
302
+ /**
303
+ * Select which CoreLoop assistant text reaches the host. `all` streams
304
+ * every tool-round narration; `final` emits only the terminal tool-free
305
+ * response while preserving intermediate rounds in the record and trace.
306
+ */
307
+ contentMode?: 'all' | 'final';
308
+ /**
309
+ * Forward every pre-digestion provider chunk (the loop's `on_stream_chunk`
310
+ * hook, before UI-tag stripping) as `stream.chunk` notifications carrying
311
+ * `rawChunk` — for hosts running incremental renderers. Default off: it
312
+ * costs one notification per chunk. CoreLoop-only.
313
+ */
314
+ streamRawChunks?: boolean;
315
+ /** Execute every tool call on the host via the reverse channel. */
316
+ toolsViaHost?: boolean;
317
+ /**
318
+ * W8: advertise the sidecar-hosted `ask_user` tool (structured user
319
+ * questions). The host answers `ask_user.request` reverse calls with the
320
+ * renderer's question card; under `toolsViaHost` the sidecar intercepts
321
+ * `ask_user` locally so it never reaches the host's tool.invoke.
322
+ */
323
+ askUser?: boolean;
324
+ /** Embedder context forwarded on every reverse tool.invoke (e.g. {mode}). */
325
+ toolContext?: Record<string, unknown>;
326
+ /** Enable the anti-hallucination hook layer (routing / deferred-claimed
327
+ * retry / grounding judge / narration). `true` or an options object. */
328
+ antiHallucination?: boolean | {
329
+ maxRetries?: number;
330
+ };
331
+ /** P3.1 multi-agent orchestration: OPT-IN advanced mode (off by default
332
+ * since the delegate-on-pool unification). Only `enabled: true` wraps the
333
+ * executor with the six-tool orchestration family (agent_spawn /
334
+ * agent_send / agent_wait / agent_close / agent_list / agent_interrupt).
335
+ * When delegation is also on (it is by default), both surfaces share one
336
+ * agent pool — one maxParallel budget, one lineage space. */
337
+ orchestration?: {
338
+ enabled?: boolean;
339
+ maxDepth?: number;
340
+ maxParallel?: number;
341
+ childMaxRounds?: number;
342
+ };
343
+ /**
344
+ * Sub-agent delegation (delegate-on-pool). ON BY DEFAULT on the sidecar —
345
+ * pass `false` to disable, or an options object to configure. The model
346
+ * gets the single `delegate_subagent` tool; children run as pooled
347
+ * AgentPool runs emitting `agent.child` lifecycle notifications (the
348
+ * spawn payload carries the resolved `profile` name). `profiles` adds
349
+ * named subagent_type profiles (CC parity) with per-profile tool domains,
350
+ * models, round bounds, and concurrency.
351
+ */
352
+ subagent?: boolean | {
353
+ toolFilter?: string[];
354
+ maxParallel?: number;
355
+ profiles?: Record<string, {
356
+ toolFilter?: string[];
357
+ model?: string;
358
+ maxRounds?: number;
359
+ concurrent?: boolean;
360
+ description?: string;
361
+ /** Profile system prompt, seeded as the child loop's first message
362
+ * (CC `.claude/agents` body parity). */
363
+ systemPrompt?: string;
364
+ }>;
365
+ };
366
+ /** A6 layered skill disclosure: the sidecar injects the catalog layer
367
+ * (first-round pre_step, recorded as a hook_action event) and answers
368
+ * `skill` tool calls with full bodies; the eager layer stays in the
369
+ * host-built system prompt. `mode: 'eager'` keeps everything host-side. */
370
+ skills?: {
371
+ roots: string[];
372
+ conditions?: string[];
373
+ exclude?: string[];
374
+ ignoreConditions?: boolean;
375
+ mode?: 'layered' | 'eager';
376
+ };
377
+ chatId?: string;
378
+ /** W5-2: the durable record this turn appends to. Defaults to `chatId`
379
+ * on the sidecar; after a regenerate-fork the chat's active record is the
380
+ * branch id, so the host must pass it explicitly. */
381
+ recordId?: string;
382
+ /** Wave 2 world-state sections: slow-changing host context (current
383
+ * time, timezone, …) as plain per-section JSON data. The sidecar injects
384
+ * it once as a `<world-state>` fragment; later turns diff against the
385
+ * snapshot embedded in the record — unchanged state costs zero tokens, a
386
+ * change costs one small RFC 7386 tail patch. */
387
+ worldState?: Record<string, unknown>;
388
+ /** CoreLoop tunables, mapped to LoopConfig on the sidecar. */
389
+ maxRounds?: number;
390
+ maxToolErrors?: number;
391
+ budgetTokens?: number;
392
+ softTimeoutMs?: number;
393
+ /** Per-tool-execution timeout (ms). On expiry the call returns a failed
394
+ * ToolResult (error `tool_timeout`) instead of hanging the turn. */
395
+ toolTimeoutMs?: number;
396
+ /**
397
+ * Wave 4 (W4-2): per-exec OS sandbox for shell/subprocess tool calls
398
+ * (`SandboxedToolExecutor` on the sidecar). The command is rewritten into
399
+ * a Seatbelt invocation before it crosses the reverse channel, so the
400
+ * host's shell spawns the confined command without learning sandbox
401
+ * mechanics. Every sandboxed result carries `data._sandbox =
402
+ * {backend, enforcement: full|partial|none}`. `requireFull: true`
403
+ * denies anything weaker than `full` (including honest `partial`).
404
+ * `requireBackend: true` denies only `none` (no OS backend). Absent →
405
+ * unconfined (legacy behavior).
406
+ */
407
+ execSandbox?: {
408
+ enabled: boolean;
409
+ /** Directories the confined command may write into (e.g. project root). */
410
+ writableRoots?: string[];
411
+ /** Allow outbound network from the confined command. Default false. */
412
+ network?: boolean;
413
+ /** Egress allow-list (host[:port]); only consulted when network is on. */
414
+ allowedHosts?: string[];
415
+ /** Deny the call unless enforcement is `full`. Default false (marked). */
416
+ requireFull?: boolean;
417
+ /**
418
+ * Deny the call when there is no sandbox backend (`enforcement: none`).
419
+ * Partial backends (Seatbelt with `network: true`) still run. Default false.
420
+ */
421
+ requireBackend?: boolean;
422
+ /**
423
+ * W4.1.1: when no local rewriter backend exists (Windows), delegate
424
+ * confined spawn to the host over the `host.process.spawn` reverse
425
+ * channel instead of running unconfined. The host reports the
426
+ * enforcement it actually applied; a host without the capability fails
427
+ * closed (tool error, the command never runs).
428
+ */
429
+ hostSpawn?: boolean;
430
+ };
431
+ /**
432
+ * Wave 4 (W4-1): approval algebra in front of every tool call.
433
+ * `mode: 'host'` asks the host UI over the reverse channel
434
+ * (`approval.request`); `storePath` enables the durable scope
435
+ * (`allow_always` / `deny_always` persisted per category);
436
+ * `timeoutMs` fails closed as `timed_out` (a denial) when the UI does
437
+ * not answer in time. Absent → no approval layer (legacy behavior).
438
+ */
439
+ approval?: {
440
+ mode: 'host' | 'auto';
441
+ timeoutMs?: number;
442
+ storePath?: string;
443
+ };
444
+ }
445
+ /**
446
+ * Pre-digestion raw provider chunk, forwarded by the sidecar when the
447
+ * request sets `streamRawChunks: true` (default off). The CoreLoop's
448
+ * `on_stream_chunk` hook observes every `LLMStreamChunk` *before* UI-tag
449
+ * stripping and surrogate splitting turn it into display text — this is the
450
+ * input for incremental renderers (e.g. a streaming UI-tag parser). The
451
+ * digested `delta` / `reasoningDelta` fields on the same notification stay
452
+ * post-stripping display text. Unset fields are omitted; the provider's
453
+ * original wire chunk (`raw`) and per-chunk `usage` are never forwarded.
454
+ *
455
+ * Note: the OpenAI-compat provider buffers tool-call argument fragments
456
+ * into one complete ToolCall, so `toolCallDelta` arrives whole — only
457
+ * content/reasoning are incremental.
458
+ */
459
+ export interface SidecarRawChunk {
460
+ contentDelta?: string;
461
+ reasoningDelta?: string;
462
+ toolCallDelta?: {
463
+ id: string;
464
+ name: string;
465
+ arguments: Record<string, unknown>;
466
+ };
467
+ finishReason?: string;
468
+ }
469
+ export interface SidecarStreamChunk {
470
+ streamId: string;
471
+ delta?: string;
472
+ reasoningDelta?: string;
473
+ toolCall?: {
474
+ id: string;
475
+ name: string;
476
+ arguments: Record<string, unknown>;
477
+ };
478
+ /**
479
+ * Pre-digestion chunk (opt-in via `streamRawChunks`). Fire-and-forget
480
+ * emission on the sidecar: chunk order is preserved, ordering against the
481
+ * digested fields on sibling notifications is not.
482
+ */
483
+ rawChunk?: SidecarRawChunk;
484
+ /** CoreLoop tool progress (A4 path). */
485
+ toolResult?: {
486
+ id: string;
487
+ name: string;
488
+ success: boolean;
489
+ durationMs?: number;
490
+ error?: string;
491
+ resultPreview?: string;
492
+ /** W4-2: `data._sandbox` marker lifted out of the result for the card. */
493
+ sandbox?: {
494
+ backend?: string;
495
+ enforcement: string;
496
+ };
497
+ };
498
+ /** CoreLoop notices: soft_timeout / budget_exhausted. */
499
+ notice?: {
500
+ kind: string;
501
+ [key: string]: unknown;
502
+ };
503
+ finishReason?: string;
504
+ usage?: {
505
+ promptTokens: number;
506
+ completionTokens: number;
507
+ totalTokens: number;
508
+ };
509
+ }
510
+ export interface SidecarStreamDone {
511
+ streamId: string;
512
+ ok: boolean;
513
+ cancelled?: boolean;
514
+ /** CoreLoop terminal status (completed | failed | budget_exhausted). */
515
+ status?: string;
516
+ reason?: string;
517
+ /** Sidecar TraceRecorder id — fetch the persisted run via `trace.fetch`. */
518
+ traceId?: string;
519
+ /**
520
+ * W6-9: the run's accumulated billable usage (summed over every provider
521
+ * request this turn), plus `costUsd` when the model is priced. Absent on
522
+ * paths that don't report usage.
523
+ */
524
+ usage?: {
525
+ promptTokens: number;
526
+ completionTokens: number;
527
+ totalTokens: number;
528
+ cachedPromptTokens?: number;
529
+ costUsd?: number;
530
+ };
531
+ }
532
+ export interface SidecarStreamError {
533
+ streamId: string;
534
+ /** Present on CoreLoop failures — the sidecar recorded the partial trace. */
535
+ traceId?: string;
536
+ kind: string;
537
+ message: string;
538
+ }
539
+ /**
540
+ * `agent.child` notification payload (child-agent lifecycle), demuxed by
541
+ * streamId. Emitted by the orchestration pool and by `delegate_subagent`
542
+ * delegations (delegate-on-pool). `kind` is one of child_spawned /
543
+ * child_completed / child_failed / child_cancelled / child_interrupted /
544
+ * child_resumed; the rest of the fields depend on the kind (childId always
545
+ * present; child_spawned adds `task` and `depth`; delegations add
546
+ * `profile` — the resolved subagent_type, `general-purpose` when untyped).
547
+ */
548
+ export interface SidecarChildEvent {
549
+ kind: string;
550
+ childId: string;
551
+ task?: string;
552
+ depth?: number;
553
+ status?: string;
554
+ error?: string;
555
+ profile?: string;
556
+ }
557
+ export interface SidecarChatStreamHandlers {
558
+ onChunk?: (chunk: SidecarStreamChunk) => void;
559
+ onDone?: (done: SidecarStreamDone) => void;
560
+ onError?: (err: SidecarStreamError) => void;
561
+ onChildEvent?: (event: SidecarChildEvent) => void;
562
+ }
563
+ /**
564
+ * `models.list` result row: one model id the configured gateway accepts,
565
+ * joined with the bundled models.dev capability catalog by the same
566
+ * resolution the request path's reasoning-effort clamp uses. `joinedFrom`
567
+ * keeps the leaf-join provenance; `capabilities: 'unknown'` means no
568
+ * catalog tier matched — reasoning levels are then unavailable rather
569
+ * than empty.
570
+ */
571
+ export interface SidecarModelEntry {
572
+ id: string;
573
+ name: string | null;
574
+ window: number | null;
575
+ modalities: string[];
576
+ reasoningLevels: string[];
577
+ pricing: {
578
+ promptPerMtok: number | null;
579
+ completionPerMtok: number | null;
580
+ } | null;
581
+ joinedFrom: string | null;
582
+ capabilities: 'known' | 'unknown';
583
+ }
584
+ /**
585
+ * `models.list` result. `catalogStatus` is `live` when just fetched from
586
+ * the gateway, `stale` when served from cache after a refresh failure,
587
+ * `offline` when the gateway is unreachable with no cache (then `models`
588
+ * is empty and `error` carries the cause). Discovery only — the catalog
589
+ * is not a routing whitelist; unlisted ids may still be sent.
590
+ */
591
+ export interface SidecarModelCatalog {
592
+ models: SidecarModelEntry[];
593
+ catalogStatus: 'live' | 'stale' | 'offline';
594
+ error?: string;
595
+ fetchedAt?: number;
596
+ /** Absent on the offline path (no gateway/env context to report). */
597
+ current?: {
598
+ model: string | null;
599
+ reasoningEffort: string | null;
600
+ };
601
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Electron single-instance lock. Imported first from main.ts so a second
3
+ * OS process exits before LocalStore opens the product DB.
4
+ *
5
+ * `requestSingleInstanceLock` must run before `app.whenReady`. `process.exit`
6
+ * is required because `app.quit()` is async and later imports would still
7
+ * construct LocalStore against the same userData.
8
+ */
9
+ type SecondInstanceHandler = () => void;
10
+ export declare function setSecondInstanceHandler(handler: SecondInstanceHandler): void;
11
+ export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Electron single-instance lock. Imported first from main.ts so a second
3
+ * OS process exits before LocalStore opens the product DB.
4
+ *
5
+ * `requestSingleInstanceLock` must run before `app.whenReady`. `process.exit`
6
+ * is required because `app.quit()` is async and later imports would still
7
+ * construct LocalStore against the same userData.
8
+ */
9
+ import { app } from 'electron';
10
+ const gotTheLock = app.requestSingleInstanceLock();
11
+ if (!gotTheLock) {
12
+ app.quit();
13
+ process.exit(0);
14
+ }
15
+ let secondInstanceHandler = null;
16
+ export function setSecondInstanceHandler(handler) {
17
+ secondInstanceHandler = handler;
18
+ }
19
+ app.on('second-instance', () => {
20
+ secondInstanceHandler?.();
21
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SQL that lists chat sessions with no rows in `chat_messages`.
3
+ *
4
+ * Bind `exceptChatId` twice. Pass `null` for both placeholders to include
5
+ * every empty session; a concrete id keeps that session (the one currently
6
+ * open in the composer) so a first-send race cannot delete it before the
7
+ * user message lands.
8
+ */
9
+ export declare const LIST_EMPTY_CHAT_IDS_SQL = "\n SELECT id\n FROM chat_sessions\n WHERE (? IS NULL OR id != ?)\n AND NOT EXISTS (\n SELECT 1 FROM chat_messages WHERE chat_id = chat_sessions.id\n )\n";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * SQL that lists chat sessions with no rows in `chat_messages`.
3
+ *
4
+ * Bind `exceptChatId` twice. Pass `null` for both placeholders to include
5
+ * every empty session; a concrete id keeps that session (the one currently
6
+ * open in the composer) so a first-send race cannot delete it before the
7
+ * user message lands.
8
+ */
9
+ export const LIST_EMPTY_CHAT_IDS_SQL = `
10
+ SELECT id
11
+ FROM chat_sessions
12
+ WHERE (? IS NULL OR id != ?)
13
+ AND NOT EXISTS (
14
+ SELECT 1 FROM chat_messages WHERE chat_id = chat_sessions.id
15
+ )
16
+ `;