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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indusagi-coding-agent",
3
- "version": "0.2.5",
3
+ "version": "0.2.9",
4
4
  "description": "Indusagi coding agent — a terminal-first AI coding agent, built from scratch on the indusagi framework.",
5
5
  "author": "Varun Israni",
6
6
  "license": "AGPL-3.0",
@@ -40,6 +40,7 @@
40
40
  },
41
41
  "files": [
42
42
  "dist",
43
+ "src",
43
44
  "CHANGELOG.md",
44
45
  "CREDITS.md",
45
46
  "NOTICE",
@@ -58,7 +59,7 @@
58
59
  "@sinclair/typebox": "^0.34.49",
59
60
  "chalk": "^5.6.2",
60
61
  "highlight.js": "^11.11.1",
61
- "indusagi": "^0.13.8",
62
+ "indusagi": "^0.13.9",
62
63
  "ink": "^5.2.1",
63
64
  "jiti": "^2.7.0",
64
65
  "marked": "^18.0.4",
@@ -0,0 +1,18 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Process entry point — the bin the OS launches.
5
+ *
6
+ * Responsibilities, in order:
7
+ * 1. Set `process.title` to the brand's primary bin name so the process is
8
+ * identifiable in `ps` / activity monitors.
9
+ * 2. Install exactly one console filter that suppresses the transport-layer
10
+ * `[MCP]` noise the MCP stack emits on stdout/stderr (unless the brand's
11
+ * debug env var is set, in which case nothing is filtered).
12
+ * 3. Hand the sliced `argv` to {@link boot} and adopt its resolved exit code.
13
+ *
14
+ * The boot call is guarded by an `import.meta` entry check so importing this
15
+ * module (e.g. from a test) does not launch the agent — only running it as the
16
+ * program entry does.
17
+ */
18
+ export {};
@@ -0,0 +1,35 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Thin guardrails entry — the headless, library-consumable surface of the
5
+ * indusagi coding agent.
6
+ *
7
+ * This barrel re-exports ONLY the deterministic guardrail primitives (the
8
+ * permission rule engine, the bash blocklist, prompt assembly, deck
9
+ * provisioning, and the read-before-edit / checkpoint stores). It deliberately
10
+ * imports from the LEAF modules — never the root `src/index.ts` barrel — so a
11
+ * consumer can pull in the guardrails without dragging in the Ink/React console
12
+ * tree (`export * as consoleUi from "./console.js"` lives only on the root barrel).
13
+ *
14
+ * Reachable from a published install as `indusagi-coding-agent/guardrails`.
15
+ */
16
+ export { resolveRuleDecision, makeRule, createPermissionGate, READ_ONLY_TOOL_NAMES, EDIT_TOOL_NAMES, } from "./conductor/permissions";
17
+ export type { PermissionRule, PermissionMode, PermissionBehavior, PermissionDecision, } from "./conductor/permissions";
18
+ export { evaluateCatastrophic, bashSubcommandSubjects } from "./conductor/bash-guard";
19
+ export { composeBriefing } from "./briefing/compose";
20
+ export { gatherContextDocs } from "./briefing/context-docs";
21
+ export type { AgentTool, BriefingContext, ContextDoc } from "./briefing/contract";
22
+ export { provisionDeck } from "./capability-deck/provision";
23
+ export { createReadStateStore, READ_STATE_HANDLE_KEY, ReadStateStore } from "./boot/runners/read-state";
24
+ export { createCheckpointStore, CHECKPOINT_HANDLE_KEY, CheckpointStore } from "./boot/runners/checkpoint";
25
+ /**
26
+ * The byte-stable refusal emitted when an edit/write is attempted on a file the
27
+ * agent has not read this session.
28
+ *
29
+ * Surfaced here as the guardrails contract for consumers (e.g. eval fixtures)
30
+ * that assert the refusal verbatim. The literal is intentionally duplicated
31
+ * rather than re-exported because the upstream message lives only as a private
32
+ * module-local const in the framework's read-state gate — it is not part of any
33
+ * public barrel. A reword on either side must update both sites, by design.
34
+ */
35
+ export declare const READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
@@ -0,0 +1,26 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Package root barrel — the public library surface of the indusagi coding agent.
5
+ *
6
+ * The terminal binary is `entry.ts` (the `indus` / `indusagi` bin). This barrel
7
+ * exposes each subsystem as a namespace for embedding or testing. The agent
8
+ * depends only on the `indusagi` framework package.
9
+ */
10
+ export * as boot from "./boot.js";
11
+ export * as workspace from "./workspace.js";
12
+ export * as conductor from "./conductor.js";
13
+ export * as windowBudget from "./window-budget.js";
14
+ export * as capabilityDeck from "./capability-deck.js";
15
+ export * as runtimeBridge from "./runtime-bridge.js";
16
+ export * as addons from "./addons.js";
17
+ export * as consoleUi from "./console.js";
18
+ export * as channels from "./channels.js";
19
+ export * as briefing from "./briefing.js";
20
+ export * as transcriptExport from "./transcript-export.js";
21
+ export * as launch from "./launch.js";
22
+ export * as insight from "./insight.js";
23
+ export * as kit from "./kit.js";
24
+ export * as settings from "./settings.js";
25
+ export * as sessions from "./sessions.js";
26
+ export { VERSION } from "./workspace.js";
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Addons contract — the FROZEN type surface of the third-party customization
3
+ * layer.
4
+ *
5
+ * This module is the single typed seam between the coding-agent *product* and
6
+ * code that does not ship with it.
7
+ */
8
+ import type { AgentMessage, AgentTool, AgentToolResult, ThinkingLevel } from "indusagi/agent";
9
+ import type { Model } from "indusagi/ai";
10
+ import type { Component, KeyId } from "indusagi/tui";
11
+ import type { Static, TSchema } from "@sinclair/typebox";
12
+
13
+ export type {
14
+ AgentMessage,
15
+ AgentTool,
16
+ AgentToolResult,
17
+ ThinkingLevel,
18
+ Model,
19
+ Component,
20
+ KeyId,
21
+ TSchema,
22
+ Static,
23
+ };
24
+
25
+ export type BundledNamespace =
26
+ | "indusagi/agent"
27
+ | "indusagi/ai"
28
+ | "indusagi/tui"
29
+ | "@sinclair/typebox";
30
+
31
+ export const BUNDLED_NAMESPACES: readonly BundledNamespace[] = [
32
+ "indusagi/agent",
33
+ "indusagi/ai",
34
+ "indusagi/tui",
35
+ "@sinclair/typebox",
36
+ ];
37
+
38
+ export type AddonId = string & { readonly __addon: unique symbol };
39
+
40
+ export function addonId(raw: string): AddonId {
41
+ return raw as AddonId;
42
+ }
43
+
44
+ export interface AddonManifest {
45
+ readonly id?: string;
46
+ readonly version?: string;
47
+ register(surface: AddonSurface): void | Promise<void>;
48
+ }
49
+
50
+ export interface RegisteredManifest {
51
+ readonly addon: AddonId;
52
+ readonly version?: string;
53
+ readonly subscriptions: readonly EventSubscription[];
54
+ readonly interceptors: readonly ToolInterceptor[];
55
+ readonly commands: readonly AddonCommand[];
56
+ readonly tools: readonly AddonTool[];
57
+ }
58
+
59
+ export function emptyManifest(addon: AddonId, version?: string): RegisteredManifest {
60
+ return version === undefined
61
+ ? { addon, subscriptions: [], interceptors: [], commands: [], tools: [] }
62
+ : { addon, version, subscriptions: [], interceptors: [], commands: [], tools: [] };
63
+ }
64
+
65
+ export type HookEvent =
66
+ | "session:start"
67
+ | "session:end"
68
+ | "turn:start"
69
+ | "turn:end"
70
+ | "tool:before"
71
+ | "tool:after"
72
+ | "chat:params"
73
+ | "chat:message"
74
+ | "shell:env"
75
+ | "input:submit"
76
+ | "context:build"
77
+ | "compact:build"
78
+ | "compact:before";
79
+
80
+ export type HookKind = "observe" | "transform" | "gate";
81
+
82
+ export interface GateDecision {
83
+ readonly stop: boolean;
84
+ readonly reason?: string;
85
+ }
86
+
87
+ export type HookHandler<TPayload = unknown> =
88
+ | { readonly kind: "observe"; run(payload: TPayload): void | Promise<void> }
89
+ | { readonly kind: "transform"; run(payload: TPayload): TPayload | Promise<TPayload> }
90
+ | {
91
+ readonly kind: "gate";
92
+ run(payload: TPayload): GateDecision | void | Promise<GateDecision | void>;
93
+ };
94
+
95
+ export interface EventSubscription {
96
+ readonly event: HookEvent;
97
+ readonly handler: HookHandler;
98
+ readonly addon: AddonId;
99
+ }
100
+
101
+ export interface DispatchOutcome<TPayload> {
102
+ readonly payload: TPayload;
103
+ readonly gate?: GateDecision;
104
+ }
105
+
106
+ export interface EventDispatcher {
107
+ dispatch<TPayload>(event: HookEvent, payload: TPayload): Promise<DispatchOutcome<TPayload>>;
108
+ has(event: HookEvent): boolean;
109
+ onFault(listener: AddonFaultListener): () => void;
110
+ }
111
+
112
+ export interface ToolEnterContext {
113
+ readonly tool: string;
114
+ readonly callId: string;
115
+ readonly args: Readonly<Record<string, unknown>>;
116
+ }
117
+
118
+ export interface ToolExitContext {
119
+ readonly tool: string;
120
+ readonly callId: string;
121
+ readonly result?: AgentToolResult<unknown>;
122
+ readonly error?: unknown;
123
+ }
124
+
125
+ export type EnterOutcome =
126
+ | void
127
+ | { readonly args: Readonly<Record<string, unknown>> }
128
+ | GateDecision;
129
+
130
+ export type ExitOutcome = void | AgentToolResult<unknown>;
131
+
132
+ export interface ToolInterceptor {
133
+ readonly match: string | "*";
134
+ readonly addon: AddonId;
135
+ enter?(ctx: ToolEnterContext): EnterOutcome | Promise<EnterOutcome>;
136
+ exit?(ctx: ToolExitContext): ExitOutcome | Promise<ExitOutcome>;
137
+ }
138
+
139
+ export interface InterceptResult {
140
+ readonly result?: AgentToolResult<unknown>;
141
+ readonly blocked?: GateDecision;
142
+ }
143
+
144
+ export interface InterceptorChain {
145
+ run(
146
+ ctx: ToolEnterContext,
147
+ execute: (args: Readonly<Record<string, unknown>>) => Promise<AgentToolResult<unknown>>,
148
+ ): Promise<InterceptResult>;
149
+ matches(tool: string): boolean;
150
+ }
151
+
152
+ export type AddonTool<TParameters extends TSchema = TSchema, TDetails = unknown> = AgentTool<
153
+ TParameters,
154
+ TDetails
155
+ >;
156
+
157
+ export interface CommandContext {
158
+ readonly args: string;
159
+ readonly cwd: string;
160
+ readonly handles: FrameworkHandles;
161
+ }
162
+
163
+ export interface AddonCommand {
164
+ readonly name: string;
165
+ readonly summary: string;
166
+ readonly addon: AddonId;
167
+ run(ctx: CommandContext): void | Promise<void>;
168
+ }
169
+
170
+ export interface ExecOutcome {
171
+ readonly stdout: string;
172
+ readonly stderr: string;
173
+ readonly code: number | null;
174
+ }
175
+
176
+ export interface FrameworkHandles {
177
+ readonly sendMessage?: (text: string) => void | Promise<void>;
178
+ readonly setModel?: (id: string) => void | Promise<void>;
179
+ readonly setThinking?: (level: ThinkingLevel) => void;
180
+ readonly render?: (component: Component) => void;
181
+ readonly exec?: (command: string) => Promise<ExecOutcome>;
182
+ }
183
+
184
+ export interface AddonSurface {
185
+ readonly id: AddonId;
186
+ on<TPayload>(event: HookEvent, handler: HookHandler<TPayload>): void;
187
+ interceptTool(name: string | "*", interceptor: Omit<ToolInterceptor, "match" | "addon">): void;
188
+ addCommand(name: string, handler: Omit<AddonCommand, "name" | "addon">): void;
189
+ addTool(card: AddonTool): void;
190
+ readonly handles: FrameworkHandles;
191
+ manifest(): RegisteredManifest;
192
+ }
193
+
194
+ export interface ModuleLoader {
195
+ load(path: string): Promise<AddonManifest>;
196
+ }
197
+
198
+ export interface AddonDiscovery {
199
+ readonly workspace?: string;
200
+ readonly dir?: string;
201
+ readonly explicitPaths?: readonly string[];
202
+ }
203
+
204
+ export const ADDONS_DIR = ".indus/addons" as const;
205
+ export const ADDON_MANIFEST_FIELD = "indusAddon" as const;
206
+
207
+ export interface AddonSource {
208
+ readonly id: AddonId;
209
+ readonly path: string;
210
+ }
211
+
212
+ export type AddonFaultKind = "load" | "register" | "handler" | "command" | "conflict";
213
+
214
+ export interface AddonFault {
215
+ readonly kind: AddonFaultKind;
216
+ readonly message: string;
217
+ readonly addon?: AddonId;
218
+ readonly cause?: unknown;
219
+ }
220
+
221
+ export type AddonFaultListener = (fault: AddonFault) => void;
222
+
223
+ export function addonFault(
224
+ kind: AddonFaultKind,
225
+ message: string,
226
+ detail?: { addon?: AddonId; cause?: unknown },
227
+ ): AddonFault {
228
+ const fault = { kind, message };
229
+ const addon = detail?.addon;
230
+ const cause = detail?.cause;
231
+ return {
232
+ ...fault,
233
+ ...(addon !== undefined ? { addon } : {}),
234
+ ...(cause !== undefined ? { cause } : {}),
235
+ };
236
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * EventDispatcher — the single fan-out / transform / veto engine for the unified
3
+ * colon-named event taxonomy.
4
+ */
5
+ import {
6
+ addonFault,
7
+ type AddonFault,
8
+ type AddonFaultListener,
9
+ type AddonId,
10
+ type DispatchOutcome,
11
+ type EventDispatcher,
12
+ type EventSubscription,
13
+ type HookEvent,
14
+ type HookHandler,
15
+ } from "../contract.js";
16
+
17
+ interface EventTrait {
18
+ guards: boolean;
19
+ }
20
+
21
+ const EVENT_TRAITS: Readonly<Record<HookEvent, EventTrait>> = Object.freeze({
22
+ "session:start": { guards: false },
23
+ "session:end": { guards: false },
24
+ "turn:start": { guards: false },
25
+ "turn:end": { guards: false },
26
+ "tool:before": { guards: true },
27
+ "tool:after": { guards: false },
28
+ "chat:params": { guards: false },
29
+ "chat:message": { guards: false },
30
+ "shell:env": { guards: false },
31
+ "input:submit": { guards: true },
32
+ "context:build": { guards: false },
33
+ "compact:build": { guards: false },
34
+ "compact:before": { guards: true },
35
+ });
36
+
37
+ const RESERVED_EVENTS: ReadonlySet<HookEvent> = new Set(
38
+ (Object.keys(EVENT_TRAITS) as HookEvent[]).filter(
39
+ (event) => EVENT_TRAITS[event].guards,
40
+ ),
41
+ );
42
+
43
+ function groupByEvent(
44
+ subscriptions: readonly EventSubscription[],
45
+ ): Map<HookEvent, EventSubscription[]> {
46
+ const grouped = new Map<HookEvent, EventSubscription[]>();
47
+ for (const sub of subscriptions) {
48
+ const list = grouped.get(sub.event);
49
+ if (list === undefined) {
50
+ grouped.set(sub.event, [sub]);
51
+ } else {
52
+ list.push(sub);
53
+ }
54
+ }
55
+ return grouped;
56
+ }
57
+
58
+ export class AddonEventDispatcher implements EventDispatcher {
59
+ private readonly byEvent: Map<HookEvent, EventSubscription[]>;
60
+ private readonly faultListeners: Set<AddonFaultListener>;
61
+
62
+ constructor(subscriptions: readonly EventSubscription[]) {
63
+ this.byEvent = groupByEvent(subscriptions);
64
+ this.faultListeners = new Set();
65
+ }
66
+
67
+ static from(subscriptions: readonly EventSubscription[]): AddonEventDispatcher {
68
+ return new AddonEventDispatcher(subscriptions);
69
+ }
70
+
71
+ get reserved(): ReadonlySet<HookEvent> {
72
+ return RESERVED_EVENTS;
73
+ }
74
+
75
+ async dispatch<TPayload>(
76
+ event: HookEvent,
77
+ payload: TPayload,
78
+ ): Promise<DispatchOutcome<TPayload>> {
79
+ const subscriptions = this.byEvent.get(event);
80
+ if (subscriptions === undefined || subscriptions.length === 0) {
81
+ return { payload };
82
+ }
83
+ let current = payload;
84
+ for (const sub of subscriptions) {
85
+ const gate = await this.run(event, sub, current, (next) => {
86
+ current = next;
87
+ });
88
+ if (gate !== undefined && gate.stop) {
89
+ return { payload: current, gate };
90
+ }
91
+ }
92
+ return { payload: current };
93
+ }
94
+
95
+ has(event: HookEvent): boolean {
96
+ const subscriptions = this.byEvent.get(event);
97
+ return subscriptions !== undefined && subscriptions.length > 0;
98
+ }
99
+
100
+ onFault(listener: AddonFaultListener): () => void {
101
+ this.faultListeners.add(listener);
102
+ let active = true;
103
+ return () => {
104
+ if (!active) return;
105
+ active = false;
106
+ this.faultListeners.delete(listener);
107
+ };
108
+ }
109
+
110
+ private async run<TPayload>(
111
+ event: HookEvent,
112
+ sub: EventSubscription,
113
+ payload: TPayload,
114
+ commit: (next: TPayload) => void,
115
+ ): Promise<{ stop: boolean } | undefined> {
116
+ const handler = sub.handler;
117
+ try {
118
+ switch (handler.kind) {
119
+ case "observe": {
120
+ await handler.run(payload);
121
+ return undefined;
122
+ }
123
+ case "transform": {
124
+ const next = await handler.run(payload);
125
+ commit(next);
126
+ return undefined;
127
+ }
128
+ case "gate": {
129
+ const decision = await handler.run(payload);
130
+ return decision ?? undefined;
131
+ }
132
+ }
133
+ } catch (cause) {
134
+ this.report(
135
+ addonFault("handler", `Handler for "${event}" threw during dispatch.`, {
136
+ addon: sub.addon,
137
+ cause,
138
+ }),
139
+ );
140
+ return undefined;
141
+ }
142
+ }
143
+
144
+ private report(fault: AddonFault): void {
145
+ for (const listener of [...this.faultListeners]) {
146
+ try {
147
+ listener(fault);
148
+ } catch {
149
+ /* ignore listener errors */
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Record one event subscription, stamping it with the owning addon.
157
+ */
158
+ export function subscription(
159
+ addon: AddonId,
160
+ event: HookEvent,
161
+ handler: HookHandler,
162
+ ): EventSubscription {
163
+ return { event, handler, addon };
164
+ }
@@ -0,0 +1,25 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Dispatch subsystem — public barrel.
5
+ *
6
+ * The unified hook + tool-interception runtime, written against the frozen
7
+ * {@link module:../contract} surface:
8
+ *
9
+ * - {@link AddonEventDispatcher} — the single fan-out / transform / veto engine
10
+ * for the colon-named {@link HookEvent} taxonomy. Folds every addon's
11
+ * {@link EventSubscription}s into one ordered middleware pass (observe /
12
+ * transform / gate), short-circuits on the first veto, and derives its
13
+ * reserved (gate-bearing) event set from a data table rather than a literal.
14
+ * - {@link AddonInterceptorChain} — the composable tool-call boundary. Folds
15
+ * every addon's {@link ToolInterceptor} stages around one tool execution
16
+ * (enter forward, exit reverse), rewriting args/results or blocking the call,
17
+ * replacing the old tool wrapper plus before/after hook runner.
18
+ *
19
+ * Both isolate handler/stage faults into {@link AddonFault}s routed to listeners
20
+ * so a misbehaving addon never crashes the agent loop. The `subscription` and
21
+ * `interceptor` helpers mint the contract's recorded shapes for hosts/tests that
22
+ * assemble them outside the full surface builder.
23
+ */
24
+ export { AddonEventDispatcher, subscription } from "./event-dispatcher.js";
25
+ export { AddonInterceptorChain, interceptor } from "./tool-interceptor.js";