indusagi-coding-agent 0.2.5 → 0.2.9

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 (224) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +9 -5
  3. package/dist/entry.js +14267 -24282
  4. package/dist/guardrails.js +924 -7867
  5. package/dist/index.js +13706 -23775
  6. package/package.json +3 -2
  7. package/src/_decls/entry.ts +18 -0
  8. package/src/_decls/guardrails.ts +35 -0
  9. package/src/_decls/index.ts +26 -0
  10. package/src/addons/contract.ts +236 -0
  11. package/src/addons/dispatch/event-dispatcher.ts +164 -0
  12. package/src/addons/dispatch/index.ts +25 -0
  13. package/src/addons/dispatch/tool-interceptor.ts +208 -0
  14. package/src/addons/host.ts +225 -0
  15. package/src/addons/index.ts +112 -0
  16. package/src/addons/manifest.ts +158 -0
  17. package/src/addons/sandbox.ts +170 -0
  18. package/src/addons/surface.ts +78 -0
  19. package/src/boot/auth-vault.ts +195 -0
  20. package/src/boot/boot.ts +138 -0
  21. package/src/boot/contract.ts +238 -0
  22. package/src/boot/heap.ts +59 -0
  23. package/src/boot/index.ts +28 -0
  24. package/src/boot/invocation.ts +93 -0
  25. package/src/boot/runners/addon-wiring.ts +153 -0
  26. package/src/boot/runners/checkpoint.ts +169 -0
  27. package/src/boot/runners/delegate-runner.ts +294 -0
  28. package/src/boot/runners/index.ts +13 -0
  29. package/src/boot/runners/link-runner.ts +45 -0
  30. package/src/boot/runners/memdir.ts +168 -0
  31. package/src/boot/runners/oneshot-runner.ts +58 -0
  32. package/src/boot/runners/read-state.ts +90 -0
  33. package/src/boot/runners/registry.ts +42 -0
  34. package/src/boot/runners/repl-runner.ts +143 -0
  35. package/src/boot/runners/server-mode.ts +121 -0
  36. package/src/boot/runners/session.ts +641 -0
  37. package/src/boot/server-token.ts +148 -0
  38. package/src/boot/stages.ts +167 -0
  39. package/src/boot/upgrade/apply.ts +94 -0
  40. package/src/boot/upgrade/index.ts +13 -0
  41. package/src/boot/upgrade/upgrades.ts +289 -0
  42. package/src/briefing/compose.ts +150 -0
  43. package/src/briefing/context-docs.ts +19 -0
  44. package/src/briefing/contract.ts +717 -0
  45. package/src/briefing/index.ts +31 -0
  46. package/src/briefing/macros.ts +97 -0
  47. package/src/briefing/skills.ts +47 -0
  48. package/src/capability-deck/bridge-ledger/index.ts +27 -0
  49. package/src/capability-deck/bridge-ledger/key.ts +67 -0
  50. package/src/capability-deck/bridge-ledger/ledger.ts +131 -0
  51. package/src/capability-deck/bridge-ledger/network.ts +117 -0
  52. package/src/capability-deck/builtin-bridge.ts +312 -0
  53. package/src/capability-deck/cards/bg-process-card.ts +335 -0
  54. package/src/capability-deck/cards/index.ts +115 -0
  55. package/src/capability-deck/cards/memory-card.ts +146 -0
  56. package/src/capability-deck/cards/plan-file.ts +97 -0
  57. package/src/capability-deck/cards/plan-tools.ts +185 -0
  58. package/src/capability-deck/cards/saas-card.ts +183 -0
  59. package/src/capability-deck/cards/task-card.ts +207 -0
  60. package/src/capability-deck/cards/todo-card.ts +168 -0
  61. package/src/capability-deck/cards/workflow-card.ts +247 -0
  62. package/src/capability-deck/contract.ts +388 -0
  63. package/src/capability-deck/index.ts +48 -0
  64. package/src/capability-deck/manifest.ts +109 -0
  65. package/src/capability-deck/provision.ts +169 -0
  66. package/src/channels/contract.ts +191 -0
  67. package/src/channels/framer.ts +50 -0
  68. package/src/channels/index.ts +101 -0
  69. package/src/channels/link/dialog.ts +129 -0
  70. package/src/channels/link/driver.ts +190 -0
  71. package/src/channels/link/index.ts +34 -0
  72. package/src/channels/link/server.ts +134 -0
  73. package/src/channels/oneshot.ts +90 -0
  74. package/src/channels/ops.ts +65 -0
  75. package/src/channels/session-ops.ts +81 -0
  76. package/src/conductor/bash-guard.ts +599 -0
  77. package/src/conductor/catalog/catalog.ts +116 -0
  78. package/src/conductor/catalog/index.ts +8 -0
  79. package/src/conductor/catalog/matcher.ts +134 -0
  80. package/src/conductor/conductor.ts +234 -0
  81. package/src/conductor/contract.ts +842 -0
  82. package/src/conductor/diagnostics.ts +227 -0
  83. package/src/conductor/index.ts +33 -0
  84. package/src/conductor/permissions.ts +588 -0
  85. package/src/conductor/quota-error.ts +49 -0
  86. package/src/conductor/signal-hub/hub.ts +46 -0
  87. package/src/conductor/signal-hub/index.ts +2 -0
  88. package/src/conductor/signal-hub/translate.test.ts +81 -0
  89. package/src/conductor/signal-hub/translate.ts +74 -0
  90. package/src/conductor/skill-parse/index.ts +2 -0
  91. package/src/conductor/skill-parse/parse.ts +108 -0
  92. package/src/conductor/transcript-store/index.ts +22 -0
  93. package/src/conductor/transcript-store/serialize.ts +116 -0
  94. package/src/conductor/transcript-store/store.ts +205 -0
  95. package/src/console/auth-status.ts +56 -0
  96. package/src/console/components/AgentsView.ts +165 -0
  97. package/src/console/components/BackgroundAgents.ts +155 -0
  98. package/src/console/components/Banner.ts +334 -0
  99. package/src/console/components/Composer.ts +94 -0
  100. package/src/console/components/StatusBar.ts +49 -0
  101. package/src/console/components/TerminalConsole.ts +1090 -0
  102. package/src/console/components/WorkingIndicator.ts +98 -0
  103. package/src/console/components/banner-sweep.ts +24 -0
  104. package/src/console/components/welcome.ts +74 -0
  105. package/src/console/contract.ts +630 -0
  106. package/src/console/index.ts +34 -0
  107. package/src/console/input/complete.ts +127 -0
  108. package/src/console/input/dir-reader.ts +34 -0
  109. package/src/console/input/index.ts +23 -0
  110. package/src/console/input/keymap.ts +159 -0
  111. package/src/console/input/paste.ts +104 -0
  112. package/src/console/mount.ts +56 -0
  113. package/src/console/overlays/approval-queue.ts +57 -0
  114. package/src/console/overlays/approval.ts +130 -0
  115. package/src/console/overlays/auth.ts +342 -0
  116. package/src/console/overlays/boards.ts +308 -0
  117. package/src/console/overlays/host.ts +36 -0
  118. package/src/console/overlays/index.ts +26 -0
  119. package/src/console/overlays/pickers.ts +258 -0
  120. package/src/console/overlays/sessions.ts +190 -0
  121. package/src/console/reducer.ts +182 -0
  122. package/src/console/slash/builtins.ts +81 -0
  123. package/src/console/slash/commands/dynamic.ts +83 -0
  124. package/src/console/slash/commands/integrations.ts +695 -0
  125. package/src/console/slash/commands/shared.ts +75 -0
  126. package/src/console/slash/commands/transcript.ts +263 -0
  127. package/src/console/slash/commands/workbench.ts +246 -0
  128. package/src/console/slash/index.ts +15 -0
  129. package/src/console/slash/registry.ts +70 -0
  130. package/src/console/slash/resolve.ts +63 -0
  131. package/src/console/startup.ts +209 -0
  132. package/src/console/theme/adapter.ts +45 -0
  133. package/src/console/theme/index.ts +7 -0
  134. package/src/console/theme/palette.ts +68 -0
  135. package/src/console/theme/resolve.ts +39 -0
  136. package/src/console/theme/tokens.ts +71 -0
  137. package/src/entry.ts +55 -0
  138. package/src/guardrails.ts +37 -0
  139. package/src/index.ts +18 -0
  140. package/src/insight/channel.ts +88 -0
  141. package/src/insight/contract.ts +185 -0
  142. package/src/insight/index.ts +110 -0
  143. package/src/insight/recorder.ts +213 -0
  144. package/src/insight/redaction.ts +157 -0
  145. package/src/insight/replay.ts +158 -0
  146. package/src/insight/sampling.ts +70 -0
  147. package/src/insight/serialize.ts +50 -0
  148. package/src/insight/sinks/console.ts +64 -0
  149. package/src/insight/sinks/file.ts +40 -0
  150. package/src/insight/sinks/index.ts +24 -0
  151. package/src/insight/sinks/stream.ts +54 -0
  152. package/src/integrations/sarvam/attach.ts +239 -0
  153. package/src/integrations/sarvam/config.ts +156 -0
  154. package/src/integrations/sarvam/index.ts +25 -0
  155. package/src/integrations/sarvam/sarvam.test.ts +60 -0
  156. package/src/integrations/sarvam/types.ts +27 -0
  157. package/src/integrations/zoho/attach.ts +342 -0
  158. package/src/integrations/zoho/config.ts +125 -0
  159. package/src/integrations/zoho/index.ts +27 -0
  160. package/src/integrations/zoho/types.ts +21 -0
  161. package/src/integrations/zoho/zoho.test.ts +50 -0
  162. package/src/kit/clipboard-image.ts +107 -0
  163. package/src/kit/external-editor.ts +48 -0
  164. package/src/kit/image.ts +59 -0
  165. package/src/kit/index.ts +51 -0
  166. package/src/kit/shell.ts +19 -0
  167. package/src/kit/tool-fetch.ts +85 -0
  168. package/src/launch/catalog.ts +148 -0
  169. package/src/launch/contract.ts +187 -0
  170. package/src/launch/credentials.ts +625 -0
  171. package/src/launch/index.ts +98 -0
  172. package/src/launch/invocation/attachments.ts +179 -0
  173. package/src/launch/invocation/flags.ts +196 -0
  174. package/src/launch/invocation/index.ts +25 -0
  175. package/src/launch/invocation/read.ts +260 -0
  176. package/src/launch/invocation/usage.ts +67 -0
  177. package/src/launch/login.ts +324 -0
  178. package/src/launch/oauth.test.ts +18 -0
  179. package/src/launch/oauth.ts +203 -0
  180. package/src/launch/packages.ts +194 -0
  181. package/src/launch/pickers.ts +189 -0
  182. package/src/runtime-bridge/bridges/_drive.ts +96 -0
  183. package/src/runtime-bridge/bridges/builtins.ts +68 -0
  184. package/src/runtime-bridge/bridges/claude-cli.ts +123 -0
  185. package/src/runtime-bridge/bridges/codex-cli.ts +142 -0
  186. package/src/runtime-bridge/bridges/index.ts +33 -0
  187. package/src/runtime-bridge/bridges/indusagi-cli.ts +155 -0
  188. package/src/runtime-bridge/broker.ts +227 -0
  189. package/src/runtime-bridge/contract.ts +122 -0
  190. package/src/runtime-bridge/index.ts +79 -0
  191. package/src/runtime-bridge/sink.ts +180 -0
  192. package/src/sessions/contract.ts +81 -0
  193. package/src/sessions/index.ts +13 -0
  194. package/src/sessions/library.ts +229 -0
  195. package/src/settings/contract.ts +114 -0
  196. package/src/settings/index.ts +32 -0
  197. package/src/settings/manager.ts +117 -0
  198. package/src/transcript-export/index.ts +45 -0
  199. package/src/transcript-export/publish.ts +260 -0
  200. package/src/transcript-export/sgr.ts +315 -0
  201. package/src/transcript-export/template.ts +272 -0
  202. package/src/transcript-export/theme-bridge.ts +150 -0
  203. package/src/window-budget/budget/estimate.ts +135 -0
  204. package/src/window-budget/budget/gate.ts +33 -0
  205. package/src/window-budget/budget/index.ts +16 -0
  206. package/src/window-budget/budget/slice.ts +56 -0
  207. package/src/window-budget/condenser.ts +58 -0
  208. package/src/window-budget/contract.ts +184 -0
  209. package/src/window-budget/index.ts +19 -0
  210. package/src/window-budget/microcompact.ts +95 -0
  211. package/src/window-budget/rehydrate.ts +136 -0
  212. package/src/window-budget/summarize/condense.ts +103 -0
  213. package/src/window-budget/summarize/index.ts +14 -0
  214. package/src/window-budget/summarize/prompt.ts +149 -0
  215. package/src/workflow-engine/agent-runner.ts +181 -0
  216. package/src/workflow-engine/display.ts +224 -0
  217. package/src/workflow-engine/engine.ts +294 -0
  218. package/src/workflow-engine/index.ts +23 -0
  219. package/src/workflow-engine/parse.ts +172 -0
  220. package/src/workflow-engine/structured-output.ts +35 -0
  221. package/src/workspace/brand.ts +29 -0
  222. package/src/workspace/index.ts +18 -0
  223. package/src/workspace/locator.ts +103 -0
  224. package/src/workspace/runtime-detect.ts +64 -0
@@ -0,0 +1,238 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Boot-layer contract — the FROZEN type surface of Phase 1.
5
+ *
6
+ * This module is the single typed seam between the operating-system entry point
7
+ * (`entry.ts`) and everything that turns a command line into a running coding
8
+ * agent. It declares *only* shapes — no behavior, no I/O, no literals beyond the
9
+ * narrow string unions that pin the public modes. Every later boot module
10
+ * (workspace locator, upgrade registry, stage pipeline, runner registry) is
11
+ * written against the names declared here, so this file is intentionally small,
12
+ * append-mostly, and stable.
13
+ *
14
+ * Design stance:
15
+ * - One immutable {@link BootContext} is threaded through an ordered list of
16
+ * {@link Stage} transforms; a stage returns the next context (or the same
17
+ * one) and never mutates in place.
18
+ * - All branding lives in a single {@link Brand} record and all on-disk paths
19
+ * in a single {@link Workspace} object — there are no scattered free getters
20
+ * and no string literals duplicated across the codebase.
21
+ * - Where the rebuilt framework already owns a concept (the model catalog, the
22
+ * user-settings shape), the resolved-resource graph is typed against the
23
+ * framework's published types rather than re-declaring them here.
24
+ *
25
+ * Framework anchors (all from the `indusagi` package):
26
+ * - `ModelRegistry` ← `indusagi/ai` — the resolved model catalog.
27
+ * - `Settings` ← `indusagi/shell-app` — the user-tunable config shape.
28
+ * - `ThinkingLevel` ← `indusagi/agent` — reasoning-effort vocabulary (re-exported
29
+ * for convenience by boot consumers).
30
+ */
31
+ import type { ModelRegistry } from "indusagi/ai";
32
+ import type { Settings } from "indusagi/shell-app";
33
+ import type { ThinkingLevel } from "indusagi/agent";
34
+ /** Re-exported framework reasoning vocabulary, surfaced through the boot contract. */
35
+ export type { ThinkingLevel };
36
+ /**
37
+ * The single source of truth for every branding / identity literal.
38
+ *
39
+ * Nothing else in the app hard-codes the product name, the profile directory
40
+ * name, the bin names, the environment-variable namespace, or the share-viewer
41
+ * origin. Resolving these once into a frozen record means a rebrand touches one
42
+ * value, never a grep across the tree.
43
+ */
44
+ export interface Brand {
45
+ /** Product name used in banners and `process.title` (e.g. `"indusagi"`). */
46
+ readonly name: string;
47
+ /** Display label for human-facing surfaces; defaults to {@link name}. */
48
+ readonly label: string;
49
+ /** Profile directory leaf under the user's home (e.g. `".indusagi"`). */
50
+ readonly profileDirName: string;
51
+ /** Sub-directory of the profile holding the agent's own state (e.g. `"agent"`). */
52
+ readonly stateDirName: string;
53
+ /** The executable names this app installs as (both point at one entry). */
54
+ readonly binNames: readonly [primary: string, alias: string];
55
+ /** Environment-variable name prefix shared by all app-scoped vars (e.g. `"INDUSAGI"`). */
56
+ readonly envPrefix: string;
57
+ /** Env var overriding the resolved profile directory (e.g. `"INDUSAGI_CODING_AGENT_DIR"`). */
58
+ readonly envProfileDir: string;
59
+ /** Env var that, when set, disables transport-noise log filtering (e.g. `"INDUSAGI_DEBUG"`). */
60
+ readonly envDebug: string;
61
+ /** Env var overriding the share-viewer origin (e.g. `"INDUSAGI_SHARE_VIEWER_URL"`). */
62
+ readonly envShareViewer: string;
63
+ /** Default origin a published transcript links to. */
64
+ readonly shareViewerUrl: string;
65
+ }
66
+ /**
67
+ * Every resolved on-disk location the app reads or writes, as one immutable
68
+ * record rather than a family of free `get*Path()` getters.
69
+ *
70
+ * All members are absolute paths already expanded against the user's home and
71
+ * the active {@link Brand}; consumers join nothing further. A single object also
72
+ * makes the layout trivially inspectable and swappable in tests.
73
+ */
74
+ export interface Workspace {
75
+ /** Root profile directory, e.g. `~/.indusagi/agent`. */
76
+ readonly profileDir: string;
77
+ /** Merged-settings file (`settings.json`). */
78
+ readonly settingsPath: string;
79
+ /** Consolidated credential store (`auth.json`). */
80
+ readonly authPath: string;
81
+ /** Per-cwd transcript directory root (`sessions/`). */
82
+ readonly sessionsDir: string;
83
+ /** Custom model-catalog overrides (`models.json`). */
84
+ readonly modelsPath: string;
85
+ /** Provisioned native helper binaries (`bin/`, holds fd / rg). */
86
+ readonly toolsDir: string;
87
+ /** Alias for the managed-binary directory; equals {@link toolsDir}. */
88
+ readonly binDir: string;
89
+ /** User-authored prompt/command templates (`prompts/`). */
90
+ readonly promptsDir: string;
91
+ /** User-installed color themes (`themes/`). */
92
+ readonly themesDir: string;
93
+ /** Bundled HTML transcript-export template directory. */
94
+ readonly exportTemplateDir: string;
95
+ /** Verbose diagnostic log file path. */
96
+ readonly debugLogPath: string;
97
+ /** External MCP server configuration (`mcp-servers.json`). */
98
+ readonly mcpConfigPath: string;
99
+ /** Memory-feature configuration (`memory.json`). */
100
+ readonly memoryConfigPath: string;
101
+ /** Composio-integration configuration (`composio.json`). */
102
+ readonly composioConfigPath: string;
103
+ /** On-disk memory database (`memory.db`). */
104
+ readonly memoryDbPath: string;
105
+ }
106
+ /**
107
+ * The three top-level execution modes the boot layer can dispatch to.
108
+ *
109
+ * - `repl` — interactive terminal session.
110
+ * - `oneshot` — single non-interactive request to stdout (text or JSON).
111
+ * - `link` — headless JSON-RPC link for a driving parent process.
112
+ */
113
+ export type RunnerId = "repl" | "oneshot" | "link";
114
+ /**
115
+ * The parsed command line, reduced to what the boot layer needs to choose and
116
+ * configure a {@link Runner}. This is the minimal Phase-1 shape; Phase 10 enriches
117
+ * it with the full declarative flag surface. Raw, un-consumed tokens survive in
118
+ * {@link rest} and loosely-typed switches in {@link flags} so nothing is lost
119
+ * before the richer parser lands.
120
+ */
121
+ export interface Invocation {
122
+ /** Resolved execution mode. */
123
+ readonly mode: RunnerId;
124
+ /** First user message / request text, when supplied positionally or via stdin. */
125
+ readonly prompt?: string;
126
+ /** Explicit model selector from the command line, if any (`--model` / `-m`). */
127
+ readonly modelId?: string;
128
+ /** Model to fall back to when the bound model is overloaded mid-turn (`--fallback-model`). */
129
+ readonly fallbackModelId?: string;
130
+ /** Working directory the run is scoped to; absent means the process cwd (`--cwd`). */
131
+ readonly cwd?: string;
132
+ /** Named credential account to authenticate with (`--account`). */
133
+ readonly account?: string;
134
+ /** Reasoning effort (`--thinking`): off / minimal / low / medium / high / xhigh. */
135
+ readonly thinking?: string;
136
+ /** Replacement system prompt (`--system`); a path is read as a file, else literal text. */
137
+ readonly system?: string;
138
+ /** Extra text appended after the system prompt (`--append-system`); path-or-literal. */
139
+ readonly appendSystem?: string;
140
+ /** Allow-list of built-in tool names (`--tools`); absent means the full deck. */
141
+ readonly tools?: readonly string[];
142
+ /** Disable every built-in tool (`--no-tools`). */
143
+ readonly noTools?: boolean;
144
+ /** External MCP endpoint config paths to attach (`--mcp`). */
145
+ readonly mcp?: readonly string[];
146
+ /** Open the resume picker before the session starts (`--resume` / `-r`). */
147
+ readonly resume?: boolean;
148
+ /** Auto-resume the most recent session in the cwd (`--continue` / `-c`). */
149
+ readonly continueLatest?: boolean;
150
+ /** Print the model catalog and exit (`--list-models`). */
151
+ readonly listModels?: boolean;
152
+ /** Optional substring filter for `--list-models`. */
153
+ readonly listModelsFilter?: string;
154
+ /** Parsed switches, keyed by canonical flag name (values intentionally loose). */
155
+ readonly flags: Record<string, unknown>;
156
+ /** Positional / pass-through tokens not consumed as flags. */
157
+ readonly rest: string[];
158
+ }
159
+ /**
160
+ * Placeholder for the resolved per-account credential graph.
161
+ *
162
+ * The rebuilt framework publishes no credential type, so the boot contract owns
163
+ * an opaque, forward-declared shape here; Phase 2 replaces it with the concrete
164
+ * multi-account credential vault. Kept deliberately structural (an indexable
165
+ * bag) so a stage may attach it without the vault module existing yet.
166
+ */
167
+ export interface CredentialGraph {
168
+ readonly [account: string]: unknown;
169
+ }
170
+ /**
171
+ * The resolved settings / auth / model-registry graph assembled during startup.
172
+ *
173
+ * This is a Phase-1 placeholder: the fields are typed against the framework's
174
+ * own published types where one exists ({@link Settings}, {@link ModelRegistry}),
175
+ * and against an app-local placeholder ({@link CredentialGraph}) where the
176
+ * framework does not yet own the concept. Later phases attach the loaded
177
+ * extension/MCP graph onto the same object without changing these anchors.
178
+ */
179
+ export interface StartupResources {
180
+ /** Merged user settings (framework-owned shape). */
181
+ readonly settings: Settings;
182
+ /** Resolved credentials per account (app-owned placeholder until Phase 2). */
183
+ readonly auth: CredentialGraph;
184
+ /** The resolved model catalog (framework-owned registry). */
185
+ readonly models: ModelRegistry;
186
+ }
187
+ /**
188
+ * The immutable value threaded through the {@link Stage} pipeline.
189
+ *
190
+ * Each stage receives a context and returns the next one; treat every field as
191
+ * read-only and produce successors by spreading rather than mutating. The
192
+ * resolved-resource graph is optional because early stages run before it exists.
193
+ * {@link closables} accumulates teardown callbacks (open files, servers, MCP
194
+ * clients) that the entry point drains in reverse on exit.
195
+ */
196
+ export interface BootContext {
197
+ /** The process arguments the launch was invoked with (already sliced). */
198
+ readonly argv: string[];
199
+ /** Resolved on-disk layout. */
200
+ readonly workspace: Workspace;
201
+ /** Resolved identity literals. */
202
+ readonly brand: Brand;
203
+ /** Parsed command line. */
204
+ readonly invocation: Invocation;
205
+ /** Resolved settings/auth/model graph; absent until the resource stage runs. */
206
+ readonly resources?: StartupResources;
207
+ /** Teardown callbacks to drain on shutdown (latest-registered first). */
208
+ readonly closables: Array<() => Promise<void>>;
209
+ }
210
+ /**
211
+ * One step of the launch pipeline: a named, pure-ish transform over a context
212
+ * value of type `C` (defaulting to {@link BootContext}).
213
+ *
214
+ * A stage may return its result synchronously or asynchronously. It must not
215
+ * mutate its input; it returns the next context. The `name` is used for tracing
216
+ * and error attribution when a stage throws.
217
+ */
218
+ export interface Stage<C = BootContext> {
219
+ /** Stable identifier for tracing and error messages. */
220
+ readonly name: string;
221
+ /** Transform the context, yielding its successor. */
222
+ apply(ctx: C): Promise<C> | C;
223
+ }
224
+ /**
225
+ * A terminal execution strategy for one {@link RunnerId} mode.
226
+ *
227
+ * The runner registry asks each runner whether it {@link accepts} a parsed
228
+ * {@link Invocation}; the first match runs. {@link run} drives the selected mode
229
+ * to completion and resolves to the process exit code.
230
+ */
231
+ export interface Runner {
232
+ /** The mode this runner serves. */
233
+ readonly id: RunnerId;
234
+ /** Whether this runner handles the given invocation. */
235
+ accepts(inv: Invocation): boolean;
236
+ /** Execute the mode; resolves to the process exit code. */
237
+ run(ctx: BootContext): Promise<number>;
238
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Heap headroom guard.
3
+ *
4
+ * Node's default old-space cap (~2 GB on 64-bit builds) is too small for heavy
5
+ * runs — most acutely a burst of parallel sub-agents whose combined transcripts
6
+ * push the resident heap past the default and abort the whole process with a V8
7
+ * "JavaScript heap out of memory" fatal (the exact crash this guards against).
8
+ *
9
+ * `--max-old-space-size` can only be set at process launch, so before any real
10
+ * work begins we re-exec the *same* program once with a raised cap, inherit its
11
+ * stdio (so the interactive TUI runs unchanged in the child), and adopt its exit
12
+ * code. The re-exec is one level deep: the child carries `INDUS_HEAP_BOOSTED=1`
13
+ * so it skips the guard and runs the agent directly.
14
+ *
15
+ * Controlled by env:
16
+ * INDUS_MAX_HEAP_MB target old-space size in MB (default 4096; 0 disables
17
+ * the guard entirely and runs in-process).
18
+ * INDUS_HEAP_BOOSTED set internally on the child; never set this yourself.
19
+ *
20
+ * If the re-exec cannot spawn (a locked-down sandbox, a missing argv[1]) the
21
+ * guard silently falls back to running in the current, un-boosted process rather
22
+ * than failing to start.
23
+ */
24
+ import { spawnSync } from "node:child_process";
25
+ import { argv, env, execArgv, execPath, exit } from "node:process";
26
+
27
+ function targetHeapMb(): number {
28
+ const raw = parseInt(env.INDUS_MAX_HEAP_MB ?? "", 10);
29
+ if (Number.isFinite(raw)) return Math.max(0, raw);
30
+ return 4096;
31
+ }
32
+
33
+ function alreadyRaised(): boolean {
34
+ return env.INDUS_HEAP_BOOSTED === "1" || (env.NODE_OPTIONS ?? "").includes("--max-old-space-size") || execArgv.some(
35
+ (a) => a.startsWith("--max-old-space-size") || a.startsWith("--max_old_space_size"),
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Ensure the process has heap headroom, re-execing once if it does not.
41
+ *
42
+ * Returns normally in three cases: the guard is disabled, the cap is already
43
+ * raised, or the re-exec could not be spawned. Otherwise it does NOT return —
44
+ * it runs the boosted child to completion and exits with the child's status.
45
+ */
46
+ export function ensureHeapHeadroom(): void {
47
+ const mb = targetHeapMb();
48
+ if (mb === 0 || alreadyRaised()) return;
49
+ const entry = argv[1];
50
+ if (!entry) return;
51
+ const result = spawnSync(
52
+ execPath,
53
+ [`--max-old-space-size=${mb}`, ...execArgv, entry, ...argv.slice(2)],
54
+ { stdio: "inherit", env: { ...env, INDUS_HEAP_BOOSTED: "1" } },
55
+ );
56
+ if (result.error) return;
57
+ if (typeof result.status === "number") exit(result.status);
58
+ exit(result.signal ? 1 : 0);
59
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Boot subsystem — public barrel.
3
+ *
4
+ * Re-exports the frozen Phase-1 contract type surface plus the assembled boot
5
+ * layer: the orchestrator ({@link boot}), the stage pipeline
6
+ * ({@link runStages} / {@link STAGES}), the invocation parser
7
+ * ({@link tokenizeInvocation}), the runner registry ({@link selectRunner} /
8
+ * {@link RUNNERS}), and the upgrade driver ({@link applyUpgrades}). Consumers
9
+ * import the boot type surface and behavior from `src/boot` rather than reaching
10
+ * into individual modules.
11
+ */
12
+ export type { Brand, Workspace, Invocation, RunnerId, BootContext, Stage, Runner, StartupResources, CredentialGraph, ThinkingLevel, } from "./contract.js";
13
+ export { boot } from "./boot.js";
14
+ export { runStages, STAGES, locateWorkspace, upgrade, buildInvocation, resolveResources, selectRunnerStage, } from "./stages.js";
15
+ export { tokenizeInvocation, wantsHelp, wantsVersion, } from "./invocation.js";
16
+ export { replRunner, oneshotRunner, linkRunner, RUNNERS, selectRunner } from "./runners/registry.js";
17
+ export { createAuthVault } from "./auth-vault.js";
18
+ export { ensureHeapHeadroom } from "./heap.js";
19
+ export { serverMode } from "./runners/server-mode.js";
20
+ export { delegateRunner } from "./runners/delegate-runner.js";
21
+ export { buildOverlayServices, applyResume } from "./runners/repl-runner.js";
22
+ export { buildAddonHost, wrapToolsWithAddons, concatAddonTools } from "./runners/addon-wiring.js";
23
+ export { memoryDirFor, DiskMemoryStore } from "./runners/memdir.js";
24
+ export type { Upgrade, UpgradeReport } from "./upgrade/index.js";
25
+ export { applyUpgrades, UPGRADES } from "./upgrade/index.js";
26
+ export { BRAND, VERSION } from "../workspace/brand.js";
27
+ export { createWorkspace, ensureDirs } from "../workspace/locator.js";
28
+ export type { WorkspaceOverrides } from "../workspace/locator.js";
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Invocation reader for the boot layer — the adapter that drives the full
3
+ * Phase-10 declarative flag grammar and projects its rich result down onto the
4
+ * thin boot {@link Invocation} the runner pipeline routes on.
5
+ *
6
+ * The parsing itself is no longer owned here. {@link readInvocation} (from the
7
+ * `launch/` subsystem) walks the single declarative flag table and produces the
8
+ * full {@link LaunchInvocation}; this module's job is purely the *projection*:
9
+ * map the launch {@link OutputMode} onto the boot {@link RunnerId}, rename the
10
+ * resolved fields the boot layer reads (`model` → `modelId`, `positionals` →
11
+ * `rest`), and widen the typed flag bag back to the loose
12
+ * `Record<string, unknown>` the boot {@link Invocation} carries. The result is
13
+ * type-compatible with the rest of the boot pipeline, so the runner registry and
14
+ * the session helpers consume it unchanged.
15
+ *
16
+ * - mode: `text` → `repl`, `json` → `oneshot`, `rpc` → `link`.
17
+ * - the full flag bag survives on {@link Invocation.flags} so the meta checks
18
+ * (`--help` / `--version`) and the oneshot output-shape probe (`json`) keep
19
+ * reading the same canonical keys they always did.
20
+ *
21
+ * The parse is total and never throws: the launch reader tolerates unknown
22
+ * `--flags` as boolean switches in the flag bag rather than rejecting them, so
23
+ * nothing is silently dropped before a later phase can reinterpret it.
24
+ */
25
+ import { readInvocation } from "../launch/index.js";
26
+ import type { Invocation } from "./contract.js";
27
+
28
+ function toRunnerId(mode: string): Invocation["mode"] {
29
+ switch (mode) {
30
+ case "rpc":
31
+ return "link";
32
+ case "json":
33
+ return "oneshot";
34
+ case "text":
35
+ default:
36
+ return "repl";
37
+ }
38
+ }
39
+
40
+ function projectInvocation(launch: ReturnType<typeof readInvocation>): Invocation {
41
+ const listModelsRaw = launch.flags["list-models"];
42
+ const listModels = listModelsRaw !== undefined && listModelsRaw !== false;
43
+ const listModelsFilter = typeof listModelsRaw === "string" && listModelsRaw.length > 0 ? listModelsRaw : undefined;
44
+ return {
45
+ mode: toRunnerId(launch.mode),
46
+ ...launch.prompt !== undefined ? { prompt: launch.prompt } : {},
47
+ ...launch.model !== undefined ? { modelId: launch.model } : {},
48
+ ...launch.fallbackModel !== undefined ? { fallbackModelId: launch.fallbackModel } : {},
49
+ ...launch.cwd !== undefined ? { cwd: launch.cwd } : {},
50
+ ...launch.account !== undefined ? { account: launch.account } : {},
51
+ ...launch.thinking !== undefined ? { thinking: launch.thinking } : {},
52
+ ...launch.system !== undefined ? { system: launch.system } : {},
53
+ ...launch.appendSystem !== undefined ? { appendSystem: launch.appendSystem } : {},
54
+ ...launch.tools !== undefined ? { tools: launch.tools } : {},
55
+ ...launch.noTools ? { noTools: true } : {},
56
+ ...launch.mcp.length > 0 ? { mcp: launch.mcp } : {},
57
+ ...launch.zohoUrl !== undefined && launch.zohoUrl !== "" ? { zohoUrl: launch.zohoUrl } : {},
58
+ ...launch.noZoho ? { noZoho: true } : {},
59
+ ...launch.sarvamKey !== undefined && launch.sarvamKey !== "" ? { sarvamKey: launch.sarvamKey } : {},
60
+ ...launch.noSarvam ? { noSarvam: true } : {},
61
+ ...launch.sarvamBasePath !== undefined && launch.sarvamBasePath !== "" ? { sarvamBasePath: launch.sarvamBasePath } : {},
62
+ ...launch.flags["resume"] === true ? { resume: true } : {},
63
+ ...launch.flags["continue"] === true ? { continueLatest: true } : {},
64
+ ...listModels ? { listModels: true } : {},
65
+ ...listModelsFilter !== undefined ? { listModelsFilter } : {},
66
+ flags: launch.flags,
67
+ rest: launch.positionals,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Read a sliced `process.argv` into the boot {@link Invocation}.
73
+ *
74
+ * Delegates the actual grammar to the launch {@link readInvocation} (the one
75
+ * declarative flag table), then projects its result onto the boot shape. The
76
+ * boot layer thus shares one parser with the launch subsystem — help, parsing,
77
+ * and routing can no longer drift.
78
+ *
79
+ * @param argv The already-sliced argument vector (no node/exec path).
80
+ */
81
+ export function tokenizeInvocation(argv: readonly string[]): Invocation {
82
+ return projectInvocation(readInvocation(argv));
83
+ }
84
+
85
+ /** Whether the parsed invocation is asking for the help banner. */
86
+ export function wantsHelp(inv: Invocation): boolean {
87
+ return inv.flags.help === true;
88
+ }
89
+
90
+ /** Whether the parsed invocation is asking for the version string. */
91
+ export function wantsVersion(inv: Invocation): boolean {
92
+ return inv.flags.version === true;
93
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Boot helper: activate the addon host for a session and fold its tool
3
+ * interceptors around the deck.
4
+ *
5
+ * The {@link createAddonHost addon host} is the product's extension mechanism —
6
+ * locally-authored modules under `<cwd>/.indus/addons` that graft tools, slash
7
+ * commands, lifecycle observers, and tool-boundary interceptors onto a running
8
+ * session. The host itself is fully built and tested, but until this module it
9
+ * was never *instantiated* outside its own unit tests: nothing in the boot path
10
+ * discovered, loaded, or wired addons.
11
+ *
12
+ * This module closes that gap with two narrow seams the runners call once at
13
+ * session-assembly time:
14
+ *
15
+ * 1. {@link buildAddonHost} — construct a host, swallow its fault stream (a
16
+ * broken addon must never sink the session), and {@link AddonHost.loadAll
17
+ * load} every addon discovered under the run's `.indus/addons` directory.
18
+ * With no such directory the returned {@link AddonSurfaceBundle} is empty —
19
+ * no interceptors, no contributed tools — so wiring it is a perfect no-op.
20
+ * 2. {@link wrapToolsWithAddons} — fold the bundle's
21
+ * {@link InterceptorChain} around every tool whose name a stage matches,
22
+ * leaving the rest identity-equal. A wrapped tool runs the chain's
23
+ * `enter` → real `execute` → `exit` reduce; an `enter` block short-circuits
24
+ * to an `isError` result without ever invoking the real tool.
25
+ *
26
+ * Scope (v1): only the per-tool interceptor boundary is wired. The richer
27
+ * lifecycle-event fan-out (`session:start`, `turn:end`, …) needs a conductor
28
+ * seam that does not exist yet and is deferred. The tool interceptor boundary is
29
+ * the load-bearing one — it is where the Wave 4 permission gate later registers
30
+ * as a built-in interceptor rather than a parallel mechanism.
31
+ */
32
+ import {
33
+ type AddonSurfaceBundle,
34
+ type AgentTool,
35
+ type InterceptorChain,
36
+ createAddonHost,
37
+ } from "../../addons/index.js";
38
+ import type { BootContext } from "../contract.js";
39
+
40
+ /**
41
+ * Build and populate the addon host for a run, returning its wired
42
+ * {@link AddonSurfaceBundle}.
43
+ *
44
+ * Constructs a host with an empty {@link FrameworkHandles} bag (v1 supplies no
45
+ * `exec` handle — the runner has no shell-exec callback to hand an addon — and
46
+ * the rest are interactive-only), installs a swallow-everything fault sink so a
47
+ * broken addon degrades silently instead of crashing the boot, then discovers
48
+ * and loads every addon under `<cwd>/.indus/addons` (the contract's default
49
+ * {@link ADDONS_DIR}). With no addons directory the bundle is empty and wiring it
50
+ * downstream is a no-op.
51
+ *
52
+ * @param ctx the boot context whose invocation carries the run cwd
53
+ * @returns the wired bundle (dispatch, interceptors, contributed commands/tools)
54
+ */
55
+ export async function buildAddonHost(ctx: BootContext): Promise<AddonSurfaceBundle> {
56
+ const host = createAddonHost({ handles: {} });
57
+ host.onFault(() => {
58
+ // swallow addon load faults so a broken addon cannot sink the session
59
+ });
60
+ return host.loadAll({ workspace: ctx.invocation.cwd ?? process.cwd() });
61
+ }
62
+
63
+ /**
64
+ * Fold a bundle's {@link InterceptorChain} around every tool a stage matches.
65
+ *
66
+ * Each tool whose `name` the chain matches is replaced with a wrapper that runs
67
+ * the chain around its real `execute`; every other tool is returned *identical*
68
+ * (same object reference) so an empty bundle leaves the deck untouched. The
69
+ * contributed `bundle.tools` are NOT appended here — the caller concatenates
70
+ * them (de-duped against the existing deck) before wrapping, so addon tools are
71
+ * themselves subject to interception.
72
+ *
73
+ * @param tools the run's tool deck (deck + MCP + already-concatenated addon tools)
74
+ * @param bundle the loaded addon bundle whose interceptor chain wraps the deck
75
+ * @returns a new array: matched tools wrapped, unmatched tools identity-equal
76
+ */
77
+ export function wrapToolsWithAddons(tools: AgentTool[], bundle: AddonSurfaceBundle): AgentTool[] {
78
+ const chain = bundle.interceptors;
79
+ return tools.map(
80
+ (tool) => chain.matches(tool.name) ? wrapOne(tool, chain) : tool,
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Wrap one tool so its `execute` runs through the interceptor chain.
86
+ *
87
+ * The wrapper spreads the original tool (preserving `name` / `description` /
88
+ * `parameters` / `label` and any extra fields) and overrides only `execute`. The
89
+ * override:
90
+ *
91
+ * - closes over the live `toolCallId`, `signal`, and `onUpdate` so streaming
92
+ * updates and cancellation still reach the real tool (the chain only threads
93
+ * the decoded `args`, so these must be captured here);
94
+ * - hands the chain a `(args) => realExecute(args)` closure as the inner
95
+ * execution and runs `chain.run({ tool, callId, args }, …)`;
96
+ * - maps the resulting {@link InterceptResult} back to an
97
+ * {@link AgentToolResult}: a `blocked` enter short-circuits to an `isError`
98
+ * result carrying the gate's `reason` (the real tool never ran); otherwise
99
+ * the chain's (possibly exit-rewritten) `result` is returned.
100
+ *
101
+ * The chain — not this wrapper — owns fault isolation and the throw-on-no-recover
102
+ * semantics, so a tool error that no exit stage recovers propagates exactly as it
103
+ * would unwrapped.
104
+ *
105
+ * @param tool the real tool to fold the chain around
106
+ * @param chain the interceptor chain matching this tool's name
107
+ */
108
+ export function wrapOne(tool: AgentTool, chain: InterceptorChain): AgentTool {
109
+ const realExecute = tool.execute.bind(tool);
110
+ return {
111
+ ...tool,
112
+ async execute(toolCallId: string, params: unknown, signal?: AbortSignal, onUpdate?: unknown) {
113
+ const args = params ?? {};
114
+ const outcome = await chain.run(
115
+ { tool: tool.name, callId: toolCallId, args: args as Record<string, unknown> },
116
+ (next) => realExecute(toolCallId, next, signal as never, onUpdate as never),
117
+ );
118
+ if (outcome.blocked !== undefined) {
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: outcome.blocked.reason ?? "Blocked by an addon.",
124
+ },
125
+ ],
126
+ details: undefined,
127
+ isError: true,
128
+ };
129
+ }
130
+ return outcome.result ?? { content: [], details: undefined };
131
+ },
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Concatenate an addon bundle's contributed tools onto an existing deck, dropping
137
+ * any whose name a deck tool already claims.
138
+ *
139
+ * The host de-dupes its OWN tools against each other, but not against the product
140
+ * deck or MCP tools — so an addon tool named `read` would otherwise shadow (or
141
+ * be appended alongside) the core read tool. This filter keeps the first
142
+ * claimant (the existing deck) and admits only addon tools with a fresh name.
143
+ *
144
+ * @param deck the existing tool deck (deck + MCP)
145
+ * @param bundle the loaded addon bundle whose `tools` are appended
146
+ * @returns the deck followed by the non-conflicting addon tools
147
+ */
148
+ export function concatAddonTools(deck: AgentTool[], bundle: AddonSurfaceBundle): AgentTool[] {
149
+ if (bundle.tools.length === 0) return deck;
150
+ const claimed = new Set(deck.map((t) => t.name));
151
+ const fresh = bundle.tools.filter((t) => !claimed.has(t.name));
152
+ return fresh.length === 0 ? deck : [...deck, ...fresh];
153
+ }