@4onstudios/iris-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +690 -0
  3. package/dist/api/acp/acpServer.d.ts +23 -0
  4. package/dist/api/acp/acpServer.js +814 -0
  5. package/dist/api/acp/index.d.ts +2 -0
  6. package/dist/api/acp/index.js +2 -0
  7. package/dist/api/acp/irisClient.d.ts +58 -0
  8. package/dist/api/acp/irisClient.js +214 -0
  9. package/dist/api/agent.d.ts +14 -0
  10. package/dist/api/agent.js +4171 -0
  11. package/dist/api/backendService.d.ts +4 -0
  12. package/dist/api/backendService.js +14 -0
  13. package/dist/api/core/agent/host/AgentContract.d.ts +82 -0
  14. package/dist/api/core/agent/host/AgentContract.js +1 -0
  15. package/dist/api/core/agent/host/AgentRegistry.d.ts +24 -0
  16. package/dist/api/core/agent/host/AgentRegistry.js +125 -0
  17. package/dist/api/core/agent/host/defaultRegistry.d.ts +15 -0
  18. package/dist/api/core/agent/host/defaultRegistry.js +46 -0
  19. package/dist/api/core/agent/host/externalAgentLifecycle.d.ts +65 -0
  20. package/dist/api/core/agent/host/externalAgentLifecycle.js +117 -0
  21. package/dist/api/core/agent/host/hostSessionManager.d.ts +111 -0
  22. package/dist/api/core/agent/host/hostSessionManager.js +352 -0
  23. package/dist/api/core/agent/host/index.d.ts +5 -0
  24. package/dist/api/core/agent/host/index.js +5 -0
  25. package/dist/api/core/agent/index.d.ts +87 -0
  26. package/dist/api/core/agent/index.js +1024 -0
  27. package/dist/api/core/agent/publicContracts.d.ts +35 -0
  28. package/dist/api/core/agent/publicContracts.js +103 -0
  29. package/dist/api/core/agent/tools/applyDiff.d.ts +51 -0
  30. package/dist/api/core/agent/tools/applyDiff.js +131 -0
  31. package/dist/api/core/agent/tools/backgroundTasks.d.ts +240 -0
  32. package/dist/api/core/agent/tools/backgroundTasks.js +313 -0
  33. package/dist/api/core/agent/tools/createDirectory.d.ts +43 -0
  34. package/dist/api/core/agent/tools/createDirectory.js +77 -0
  35. package/dist/api/core/agent/tools/deleteFile.d.ts +37 -0
  36. package/dist/api/core/agent/tools/deleteFile.js +66 -0
  37. package/dist/api/core/agent/tools/editFile.d.ts +73 -0
  38. package/dist/api/core/agent/tools/editFile.js +173 -0
  39. package/dist/api/core/agent/tools/executeCommand.d.ts +59 -0
  40. package/dist/api/core/agent/tools/executeCommand.js +250 -0
  41. package/dist/api/core/agent/tools/fileContent.d.ts +54 -0
  42. package/dist/api/core/agent/tools/fileContent.js +227 -0
  43. package/dist/api/core/agent/tools/findDefinition.d.ts +72 -0
  44. package/dist/api/core/agent/tools/findDefinition.js +109 -0
  45. package/dist/api/core/agent/tools/findReferences.d.ts +86 -0
  46. package/dist/api/core/agent/tools/findReferences.js +135 -0
  47. package/dist/api/core/agent/tools/formatDocument.d.ts +67 -0
  48. package/dist/api/core/agent/tools/formatDocument.js +90 -0
  49. package/dist/api/core/agent/tools/getCodeActions.d.ts +77 -0
  50. package/dist/api/core/agent/tools/getCodeActions.js +97 -0
  51. package/dist/api/core/agent/tools/getCodeCompletion.d.ts +77 -0
  52. package/dist/api/core/agent/tools/getCodeCompletion.js +174 -0
  53. package/dist/api/core/agent/tools/getCodeContext.d.ts +14 -0
  54. package/dist/api/core/agent/tools/getCodeContext.js +282 -0
  55. package/dist/api/core/agent/tools/getSignatureHelp.d.ts +61 -0
  56. package/dist/api/core/agent/tools/getSignatureHelp.js +85 -0
  57. package/dist/api/core/agent/tools/getSymbols.d.ts +50 -0
  58. package/dist/api/core/agent/tools/getSymbols.js +164 -0
  59. package/dist/api/core/agent/tools/getSymbolsLSP.d.ts +58 -0
  60. package/dist/api/core/agent/tools/getSymbolsLSP.js +147 -0
  61. package/dist/api/core/agent/tools/getTypeInfo.d.ts +70 -0
  62. package/dist/api/core/agent/tools/getTypeInfo.js +84 -0
  63. package/dist/api/core/agent/tools/getWorkspaceInfo.d.ts +80 -0
  64. package/dist/api/core/agent/tools/getWorkspaceInfo.js +281 -0
  65. package/dist/api/core/agent/tools/getWorkspaceSymbols.d.ts +53 -0
  66. package/dist/api/core/agent/tools/getWorkspaceSymbols.js +97 -0
  67. package/dist/api/core/agent/tools/grepSearch.d.ts +100 -0
  68. package/dist/api/core/agent/tools/grepSearch.js +211 -0
  69. package/dist/api/core/agent/tools/languageModelToolsIntegration.d.ts +13 -0
  70. package/dist/api/core/agent/tools/languageModelToolsIntegration.js +22 -0
  71. package/dist/api/core/agent/tools/listDirectory.d.ts +66 -0
  72. package/dist/api/core/agent/tools/listDirectory.js +161 -0
  73. package/dist/api/core/agent/tools/mcpTools.d.ts +48 -0
  74. package/dist/api/core/agent/tools/mcpTools.js +442 -0
  75. package/dist/api/core/agent/tools/queryKnowledgeGraph.d.ts +120 -0
  76. package/dist/api/core/agent/tools/queryKnowledgeGraph.js +306 -0
  77. package/dist/api/core/agent/tools/readFile.d.ts +92 -0
  78. package/dist/api/core/agent/tools/readFile.js +337 -0
  79. package/dist/api/core/agent/tools/renameFile.d.ts +49 -0
  80. package/dist/api/core/agent/tools/renameFile.js +86 -0
  81. package/dist/api/core/agent/tools/renameSymbol.d.ts +70 -0
  82. package/dist/api/core/agent/tools/renameSymbol.js +106 -0
  83. package/dist/api/core/agent/tools/runTerminalCommand.d.ts +72 -0
  84. package/dist/api/core/agent/tools/runTerminalCommand.js +69 -0
  85. package/dist/api/core/agent/tools/searchFiles.d.ts +82 -0
  86. package/dist/api/core/agent/tools/searchFiles.js +280 -0
  87. package/dist/api/core/agent/tools/terminalAutoApprove.d.ts +3 -0
  88. package/dist/api/core/agent/tools/terminalAutoApprove.js +676 -0
  89. package/dist/api/core/agent/tools/treeTraversal.d.ts +60 -0
  90. package/dist/api/core/agent/tools/treeTraversal.js +217 -0
  91. package/dist/api/core/agent/tools/webSearch.d.ts +37 -0
  92. package/dist/api/core/agent/tools/webSearch.js +80 -0
  93. package/dist/api/core/agent/tools/writeFile.d.ts +59 -0
  94. package/dist/api/core/agent/tools/writeFile.js +97 -0
  95. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.d.ts +12 -0
  96. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.js +67 -0
  97. package/dist/api/core/agent/utils/diffUtils.d.ts +14 -0
  98. package/dist/api/core/agent/utils/diffUtils.js +39 -0
  99. package/dist/api/core/agent/utils/environmentSnapshot.d.ts +42 -0
  100. package/dist/api/core/agent/utils/environmentSnapshot.js +213 -0
  101. package/dist/api/core/agent/utils/errorRecovery.d.ts +49 -0
  102. package/dist/api/core/agent/utils/errorRecovery.js +229 -0
  103. package/dist/api/core/agent/utils/multimodalTokenLimiter.d.ts +8 -0
  104. package/dist/api/core/agent/utils/multimodalTokenLimiter.js +238 -0
  105. package/dist/api/core/agent/utils/openRouterModelSettings.d.ts +12 -0
  106. package/dist/api/core/agent/utils/openRouterModelSettings.js +34 -0
  107. package/dist/api/core/agent/utils/pathRecovery.d.ts +1 -0
  108. package/dist/api/core/agent/utils/pathRecovery.js +29 -0
  109. package/dist/api/core/agent/utils/repoMapIndex.d.ts +3 -0
  110. package/dist/api/core/agent/utils/repoMapIndex.js +197 -0
  111. package/dist/api/core/agent/utils/skillsDiscovery.d.ts +10 -0
  112. package/dist/api/core/agent/utils/skillsDiscovery.js +50 -0
  113. package/dist/api/core/agent/utils/toolCallBudget.d.ts +9 -0
  114. package/dist/api/core/agent/utils/toolCallBudget.js +55 -0
  115. package/dist/api/core/agent/utils/toolLifecycle.d.ts +30 -0
  116. package/dist/api/core/agent/utils/toolLifecycle.js +361 -0
  117. package/dist/api/core/agent/utils/toolResultSafetyProcessor.d.ts +12 -0
  118. package/dist/api/core/agent/utils/toolResultSafetyProcessor.js +57 -0
  119. package/dist/api/core/agent/utils/workspaceMutationBridge.d.ts +13 -0
  120. package/dist/api/core/agent/utils/workspaceMutationBridge.js +149 -0
  121. package/dist/api/core/agent/utils/workspacePathGuard.d.ts +7 -0
  122. package/dist/api/core/agent/utils/workspacePathGuard.js +79 -0
  123. package/dist/api/core/containers/chat/toolResultSerialization.d.ts +8 -0
  124. package/dist/api/core/containers/chat/toolResultSerialization.js +71 -0
  125. package/dist/api/core/library/BrowserManager.d.ts +11 -0
  126. package/dist/api/core/library/BrowserManager.js +66 -0
  127. package/dist/api/core/library/desktopWorkspace.d.ts +1 -0
  128. package/dist/api/core/library/desktopWorkspace.js +3 -0
  129. package/dist/api/core/library/extensionManager.d.ts +10 -0
  130. package/dist/api/core/library/extensionManager.js +27 -0
  131. package/dist/api/core/library/knowledgeGraph.d.ts +268 -0
  132. package/dist/api/core/library/knowledgeGraph.js +989 -0
  133. package/dist/api/core/library/languageModelTools.d.ts +141 -0
  134. package/dist/api/core/library/languageModelTools.js +207 -0
  135. package/dist/api/core/library/localRuntime.d.ts +23 -0
  136. package/dist/api/core/library/localRuntime.js +144 -0
  137. package/dist/api/core/library/lsp/coreLsp.d.ts +367 -0
  138. package/dist/api/core/library/lsp/coreLsp.js +2076 -0
  139. package/dist/api/core/library/lsp/protocol.d.ts +25 -0
  140. package/dist/api/core/library/lsp/protocol.js +1 -0
  141. package/dist/api/core/library/lsp/serverManager.d.ts +37 -0
  142. package/dist/api/core/library/lsp/serverManager.js +427 -0
  143. package/dist/api/core/library/mcpServerProvider.d.ts +69 -0
  144. package/dist/api/core/library/mcpServerProvider.js +152 -0
  145. package/dist/api/core/library/mcpSettings.d.ts +20 -0
  146. package/dist/api/core/library/mcpSettings.js +131 -0
  147. package/dist/api/core/library/patternMatcher.d.ts +66 -0
  148. package/dist/api/core/library/patternMatcher.js +284 -0
  149. package/dist/api/core/library/regexEscape.d.ts +4 -0
  150. package/dist/api/core/library/regexEscape.js +4 -0
  151. package/dist/api/core/library/runtimeEventBus.d.ts +27 -0
  152. package/dist/api/core/library/runtimeEventBus.js +78 -0
  153. package/dist/api/core/library/safetyMiddleware.d.ts +98 -0
  154. package/dist/api/core/library/safetyMiddleware.js +215 -0
  155. package/dist/api/core/library/tauri.d.ts +71 -0
  156. package/dist/api/core/library/tauri.js +222 -0
  157. package/dist/api/core/library/tauriImport.d.ts +5 -0
  158. package/dist/api/core/library/tauriImport.js +7 -0
  159. package/dist/api/core/library/terminalAutoApproveSettings.d.ts +32 -0
  160. package/dist/api/core/library/terminalAutoApproveSettings.js +529 -0
  161. package/dist/api/core/library/workspaceIdentity.d.ts +10 -0
  162. package/dist/api/core/library/workspaceIdentity.js +49 -0
  163. package/dist/api/core/library/workspaceSummary.d.ts +19 -0
  164. package/dist/api/core/library/workspaceSummary.js +35 -0
  165. package/dist/api/core/skills/agent-customization/SKILL.md +27 -0
  166. package/dist/api/core/skills/bug-fix/SKILL.md +21 -0
  167. package/dist/api/core/skills/create-pr/SKILL.md +32 -0
  168. package/dist/api/core/skills/dev-server/SKILL.md +20 -0
  169. package/dist/api/core/skills/fix-suggestions/SKILL.md +21 -0
  170. package/dist/api/core/skills/github-search/SKILL.md +21 -0
  171. package/dist/api/core/skills/github-summary/SKILL.md +25 -0
  172. package/dist/api/core/skills/integration-tests/SKILL.md +29 -0
  173. package/dist/api/core/skills/pr-comments/SKILL.md +23 -0
  174. package/dist/api/core/skills/project-setup/SKILL.md +20 -0
  175. package/dist/api/core/skills/search-integration/SKILL.md +19 -0
  176. package/dist/api/core/skills/search-results/SKILL.md +19 -0
  177. package/dist/api/core/skills/typescript-upgrade/SKILL.md +21 -0
  178. package/dist/api/data/runStore.d.ts +46 -0
  179. package/dist/api/data/runStore.js +244 -0
  180. package/dist/api/helpers/agentUtils.d.ts +11 -0
  181. package/dist/api/helpers/agentUtils.js +23 -0
  182. package/dist/api/helpers/modelTokenLimits.d.ts +2 -0
  183. package/dist/api/helpers/modelTokenLimits.js +158 -0
  184. package/dist/api/helpers/observationalMemory.d.ts +10 -0
  185. package/dist/api/helpers/observationalMemory.js +36 -0
  186. package/dist/api/helpers/promptBudget.d.ts +30 -0
  187. package/dist/api/helpers/promptBudget.js +216 -0
  188. package/dist/api/helpers/resolveImageMessageParts.d.ts +9 -0
  189. package/dist/api/helpers/resolveImageMessageParts.js +140 -0
  190. package/dist/api/helpers/slashCommands.d.ts +31 -0
  191. package/dist/api/helpers/slashCommands.js +129 -0
  192. package/dist/api/helpers/tokenUsage.d.ts +19 -0
  193. package/dist/api/helpers/tokenUsage.js +81 -0
  194. package/dist/api/routes/fileRoutes.d.ts +2 -0
  195. package/dist/api/routes/fileRoutes.js +108 -0
  196. package/dist/api/routes/lspDocumentRoutes.d.ts +4 -0
  197. package/dist/api/routes/lspDocumentRoutes.js +84 -0
  198. package/dist/api/routes/lspHierarchyRoutes.d.ts +4 -0
  199. package/dist/api/routes/lspHierarchyRoutes.js +122 -0
  200. package/dist/api/routes/lspPositionRoutes.d.ts +4 -0
  201. package/dist/api/routes/lspPositionRoutes.js +263 -0
  202. package/dist/api/routes/lspQueryRoutes.d.ts +4 -0
  203. package/dist/api/routes/lspQueryRoutes.js +679 -0
  204. package/dist/api/routes/lspResolveRoutes.d.ts +4 -0
  205. package/dist/api/routes/lspResolveRoutes.js +143 -0
  206. package/dist/api/routes/lspSemanticDocumentRoutes.d.ts +4 -0
  207. package/dist/api/routes/lspSemanticDocumentRoutes.js +98 -0
  208. package/dist/api/routes/semanticRoutes.d.ts +2 -0
  209. package/dist/api/routes/semanticRoutes.js +126 -0
  210. package/dist/cli.d.ts +8 -0
  211. package/dist/cli.js +160 -0
  212. package/dist/index.d.ts +1 -0
  213. package/dist/index.js +1 -0
  214. package/dist/server.d.ts +2 -0
  215. package/dist/server.js +38 -0
  216. package/package.json +100 -0
package/README.md ADDED
@@ -0,0 +1,690 @@
1
+ <p align="center">
2
+ <img src="assets/iris-agent-logo.svg" alt="Iris Agent logo" width="180">
3
+ </p>
4
+
5
+ <h1 align="center">Iris Agent</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://4onstudios.com/">Built by 4onStudios</a>
9
+ ·
10
+ <a href="https://github.com/4onstudios/iris-agent/issues">Issues</a>
11
+ ·
12
+ <a href="https://github.com/4onstudios/iris-agent/pulls">Contribute</a>
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="https://github.com/4onstudios/iris-agent/actions/workflows/ci.yml"><img src="https://github.com/4onstudios/iris-agent/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
17
+ <a href="https://www.npmjs.com/package/@4onstudios/iris-agent"><img src="https://img.shields.io/npm/v/%404onstudios%2Firis-agent" alt="npm version"></a>
18
+ <a href="https://github.com/4onstudios/iris-agent/blob/main/LICENSE"><img src="https://img.shields.io/github/license/4onstudios/iris-agent" alt="MIT License"></a>
19
+ </p>
20
+
21
+ Iris Agent is the standalone coding-agent service used by [AIRIS](https://github.com/4onstudios/iris).
22
+
23
+ It provides streaming chat, workspace tools, LSP routes, MCP integration, command approvals, and run lifecycle APIs. It can run as:
24
+
25
+ - **HTTP Service** - RESTful API under `/api/agent`
26
+ - **CLI** - Interactive chat in the terminal
27
+ - **ACP Server** - Agent Client Protocol via stdio for seamless IDE integration
28
+
29
+ The SDK and ACP server require Node.js `>=22.13.0`. `IrisClient` is a
30
+ Node.js API for IDE desktop or backend processes; it is not intended to run in
31
+ a browser renderer.
32
+
33
+ ## Quick Start
34
+
35
+ This project can be installed and run with either npm or Yarn. Configure your provider API key (such as `OPENROUTER_API_KEY` or `OPENAI_API_KEY`). By default, Iris Agent routes through OpenRouter (`openrouter/openai/gpt-4o`) when `OPENROUTER_API_KEY` is provided or configured.
36
+
37
+ ```sh
38
+ # npm
39
+ npm install
40
+ OPENROUTER_API_KEY=... npm start
41
+
42
+ # yarn
43
+ yarn install
44
+ yarn start
45
+ ```
46
+
47
+ ### HTTP Service
48
+
49
+ ```sh
50
+ npm install
51
+ OPENROUTER_API_KEY=... npm start
52
+ ```
53
+
54
+ The service listens on port `8080` by default. Set `PORT` to change it. `GET /health` reports service readiness.
55
+
56
+ ### Using Iris Agent in an IDE
57
+
58
+ To run the standalone agent service for an IDE integration:
59
+
60
+ ```bash
61
+ git clone https://github.com/4onstudios/iris-agent.git
62
+ cd iris-agent
63
+ npm install
64
+ npm start
65
+ ```
66
+
67
+ The service listens on port `8080` by default and exposes its API under
68
+ `/api/agent`. Set `PORT` to use another port and configure
69
+ `AGENT_ALLOWED_ORIGINS` with the IDE's origin when browser CORS is required:
70
+
71
+ ```bash
72
+ PORT=8080 AGENT_ALLOWED_ORIGINS=http://localhost:3000 npm start
73
+ ```
74
+
75
+ The HTTP API is mounted below `/api/agent`. The main endpoints are:
76
+
77
+ | Endpoint | Purpose |
78
+ | --- | --- |
79
+ | `POST /api/agent/chat` | Send a message and optional workspace, model, history, tools, skills, MCP, and approval settings. |
80
+ | `GET /api/agent/runs/:runId` | Read the current run lifecycle snapshot. |
81
+ | `GET /api/agent/runs/:runId/events` | Read persisted run events. Supports `afterSequence` and `limit` (1-500). |
82
+ | `POST /api/agent/runs/:runId/cancel` | Request cancellation of a run. |
83
+ | `POST /api/agent/command-confirmation` | Approve or skip a pending command execution. |
84
+ | `GET /api/agent/skills` | List discovered skills. |
85
+ | `GET /api/agent/tools` | List native and MCP tools with their input schemas. |
86
+ | `GET /api/agent/slash-commands` | List enabled slash commands. |
87
+ | `POST /api/agent/mcp/inspect` | Inspect the tools exposed by one MCP server. |
88
+ | `POST /api/agent/mcp/call` | Invoke an MCP tool. |
89
+
90
+ The service also exposes file, chat-session, semantic-search, and LSP routes under
91
+ `/api/agent`. Those routes are intended for the AIRIS desktop client and are
92
+ implemented in [`api/`](./api/); use `GET /api/agent/tools` and
93
+ `GET /api/agent/skills` for runtime discovery.
94
+
95
+ Example chat request:
96
+
97
+ ```sh
98
+ curl -X POST http://localhost:8080/api/agent/chat \
99
+ -H 'Content-Type: application/json' \
100
+ -d '{"message":"Explain this project","workspaceRoot":"/path/to/project"}'
101
+ ```
102
+
103
+ Run lifecycle states and event payloads are returned by the run endpoints. Store
104
+ the returned `runId` from a chat response if the client needs polling,
105
+ progress-event retrieval, or cancellation.
106
+
107
+ Persisted `tool_call` and `tool_result` events carry the redacted action
108
+ details needed to render a replayable tool timeline: `name` (and the deprecated
109
+ compatibility alias `toolName`), `args`, `toolCallId`, and `status`. Completed
110
+ actions also include `result` when its JSON representation is at most 16 KiB.
111
+ Larger or non-JSON-representable results are replaced with a bounded summary:
112
+ `{ truncated: true, reason?: string, originalByteLength?: number, preview?: string }`.
113
+ For oversized object results, scalar metadata such as `success`, `status`,
114
+ `error`, `exitCode`, paths, and search counts may also be retained with long
115
+ strings shortened. Legacy stored action events are normalized when read so both
116
+ tool-name labels remain available. This lets clients show, for example, the
117
+ path and line range read or the search query, matched files, and result counts
118
+ without relying on the live stream.
119
+
120
+ ### CLI Mode
121
+
122
+ ```sh
123
+ # Interactive chat with default model (openrouter/openai/gpt-4o)
124
+ OPENROUTER_API_KEY=... npm run cli -- --workspace /path/to/project --chat
125
+
126
+ # Interactive chat with a specific model
127
+ OPENROUTER_API_KEY=... npm run cli -- --workspace /path/to/project --chat --modelId openrouter/anthropic/claude-3.7-sonnet
128
+ ```
129
+
130
+ This starts an interactive streaming chat session in your terminal with access to the workspace and tools.
131
+
132
+ ### ACP Server
133
+
134
+ ```sh
135
+ OPENROUTER_API_KEY=... npm run cli -- --workspace /path/to/project --acp
136
+ ```
137
+
138
+ This starts an ACP (Agent Client Protocol) server over stdio, allowing IDE
139
+ integrations and other ACP clients to communicate with the agent. Models can be configured at startup or dynamically per-session / per-prompt. Standard
140
+ output is reserved for newline-delimited JSON-RPC messages; logs are written to
141
+ standard error. Each ACP process is bound to the workspace supplied at startup.
142
+ To switch workspaces, close the process and respawn `iris-agent` with the new
143
+ `--workspace` path.
144
+
145
+ ## CLI Usage
146
+
147
+ ```sh
148
+ iris-agent --workspace <path> [--acp | --chat] [--modelId <model>]
149
+ ```
150
+
151
+ **Options:**
152
+
153
+ - `--workspace` (required, `-w`) - Path to the workspace/project root
154
+ - `--acp` (`-a`) - Start ACP protocol server (stdio-based)
155
+ - `--chat` (`-c`) - Start interactive chat mode
156
+ - `--modelId` - Model identifier used for chat/ACP sessions (default: `openrouter/openai/gpt-4o` or `MODEL_ID` / `OPENROUTER_MODEL` env vars)
157
+
158
+ Running the CLI without `--chat` or `--acp` prints help.
159
+
160
+ **Examples:**
161
+
162
+ ```sh
163
+ # Interactive chat
164
+ npm run cli -- --workspace . --chat
165
+
166
+ # Interactive chat with custom model
167
+ npm run cli -- --workspace . --chat --modelId openrouter/anthropic/claude-3.7-sonnet
168
+
169
+ # ACP server for IDE integration
170
+ npm run cli -- --workspace . --acp
171
+
172
+ # ACP server with custom default model
173
+ npm run cli -- --workspace . --acp --modelId openrouter/openai/gpt-4o
174
+
175
+ # HTTP service (default)
176
+ npm start
177
+ ```
178
+
179
+ ## Browser Client
180
+
181
+ Configure the precise browser origins permitted to call this service:
182
+
183
+ ```sh
184
+ AGENT_ALLOWED_ORIGINS=https://airis.4onstudios.com npm start
185
+ ```
186
+
187
+ Multiple origins may be supplied as a comma-separated list. Requests without an `Origin` header (such as a local CLI or reverse proxy) are accepted; browser origins are denied unless explicitly configured.
188
+
189
+ ## Providers
190
+
191
+ Configure the API key for the model provider selected by the client:
192
+
193
+ - `OPENAI_API_KEY`
194
+ - `ANTHROPIC_API_KEY`
195
+ - `GOOGLE_GENERATIVE_AI_API_KEY`
196
+ - `OPENROUTER_API_KEY`
197
+ - `OLLAMA_API_KEY`
198
+
199
+ Provider-specific configuration:
200
+
201
+ | Variable | Description |
202
+ | --- | --- |
203
+ | `OLLAMA_BASE_URL` | Ollama-compatible server URL. |
204
+ | `OPENROUTER_BASE_URL` | OpenRouter-compatible API URL. |
205
+ | `OPENROUTER_SITE_URL` / `OPENROUTER_SITE_NAME` | Optional OpenRouter request metadata. |
206
+ | `ANTHROPIC_BETA` / `ANTHROPIC_BETAS` | Optional Anthropic beta headers. |
207
+ | `HF_TOKEN` | Hugging Face authentication where required by a configured provider. |
208
+
209
+ ### Runtime configuration
210
+
211
+ | Variable | Description |
212
+ | --- | --- |
213
+ | `PORT` | HTTP listening port; defaults to `8080`. |
214
+ | `AGENT_ALLOWED_ORIGINS` | Comma-separated browser origins allowed by CORS. Requests without an `Origin` header are allowed. |
215
+ | `DATABASE_URL` | Database connection URL used by the configured agent storage. |
216
+ | `IRIS_AGENT_RUNS_DB_PATH` | SQLite path for persisted run lifecycle data. |
217
+ | `IRIS_BACKEND_SERVICE` | Selects the backend service integration. |
218
+ | `IRIS_AGENT_PREFERRED_AGENT_ID` | Preferred external agent identifier. |
219
+ | `IRIS_AGENT_EXTERNAL_AGENT_MANIFEST_PATH` | Path to an external-agent manifest. |
220
+ | `IRIS_AGENT_GIT_SAFETY_MODE` | Git safety policy; defaults to `suggest`. |
221
+ | `IRIS_AGENT_AUTO_LINT` / `IRIS_AGENT_AUTO_TEST` | Enable automatic lint/test validation. |
222
+ | `IRIS_AGENT_LINT_CMD` / `IRIS_AGENT_TEST_CMD` | Override validation commands. |
223
+ | `IRIS_AGENT_AUTO_FIX_VALIDATION` | Enable automatic validation fixes. |
224
+ | `IRIS_AGENT_INPUT_TOKEN_LIMIT` / `IRIS_AGENT_MAX_OUTPUT_TOKENS` | Token-budget controls. |
225
+ | `IRIS_AGENT_PROMPT_TOKEN_BUDGET_RATIO` | Prompt budget ratio. |
226
+ | `IRIS_AGENT_MODEL_RETRY_ATTEMPTS` | Model request retry count. |
227
+ | `IRIS_AGENT_REFLECTION_MAX` / `IRIS_AGENT_REFLECTION_MAX_STEPS` | Reflection-loop limits. |
228
+ | `IRIS_AGENT_REFLECTION_RETRY_ATTEMPTS` | Reflection retry count. |
229
+ | `IRIS_AGENT_REFLECTION_NO_PROGRESS_REPEATS` | Maximum repeated no-progress reflection cycles. |
230
+ | `IRIS_ENABLE_SLASH_COMMANDS` | Enable slash-command handling. |
231
+ | `IRIS_DEBUG_TOKEN_USAGE_SOURCE` | Enable token-usage diagnostics. |
232
+ | `IRIS_AGENT_STREAM_RETRY_ENABLED` | Enable stream retries. Related retry delay and limit variables are supported by the runtime. |
233
+ | `IRIS_VERBOSE_SKILL_DISCOVERY` | Enable verbose skill-discovery logging. |
234
+ | `BROWSER_NO_SANDBOX` | Set to `true` only when browser automation must run without a sandbox. |
235
+
236
+ Environment values can be supplied in a local `.env` file for the HTTP server
237
+ because it loads `dotenv/config`. Do not commit `.env` files or API keys.
238
+
239
+ ### Desktop authentication
240
+
241
+ Desktop-only routes require both `TAURI_BUNDLED=1` and `IRIS_DESKTOP_TOKEN`.
242
+ Clients send the token in the `X-Desktop-Token` header. These protected routes
243
+ include key management, file operations, remote chat-session synchronization,
244
+ and MCP inspection/calls. Do not expose the desktop token to browsers.
245
+
246
+ ### Persistence
247
+
248
+ Run lifecycle data is stored in SQLite. Set `IRIS_AGENT_RUNS_DB_PATH` to choose
249
+ the database location; otherwise it is stored at `~/.iris/agent-runs.sqlite`.
250
+ Chat sessions are stored as JSON files under `~/.iris/chat-sessions/` and are
251
+ managed through the desktop synchronization routes.
252
+
253
+ ### MCP and approvals
254
+
255
+ MCP servers are supplied in chat requests or MCP route payloads. Use
256
+ `POST /api/agent/mcp/inspect` to discover tools before calling
257
+ `POST /api/agent/mcp/call`. MCP tool names must start with `mcp_`.
258
+ Commands that require approval pause until the client submits
259
+ `POST /api/agent/command-confirmation` with a `confirmationId` and boolean
260
+ `approved` value.
261
+
262
+ ## ACP Protocol
263
+
264
+ The stdio server implements the standard ACP v1 lifecycle:
265
+
266
+ - `initialize`
267
+ - `session/new` - supports initial `modelId` specification in `_meta` or parameters
268
+ - `session/load` - load existing session history with optional model override
269
+ - `session/set_config_option` - dynamically change session options such as model (`configId: "model"`)
270
+ - `session/prompt` - execute prompts with optional per-turn `modelId` override in `_meta`
271
+ - `session/cancel` - abort active turn
272
+ - `session/close` - clean up session resources
273
+ - `session/update` notifications for assistant text, reasoning, and tool status
274
+
275
+ The server supports dynamic model resolution and caching across ACP requests.
276
+
277
+ ### Specifying Models in ACP Mode
278
+
279
+ Clients can specify and change models at multiple levels:
280
+
281
+ 1. **Server Default**: Pass `--modelId <model>` to `iris-agent --acp` or set the `MODEL_ID` / `OPENROUTER_MODEL` environment variable.
282
+ 2. **Session Creation / Load**: Pass `modelId` in `_meta.iris.modelId` or `_meta.modelId` during `session/new` or `session/load`.
283
+ 3. **Dynamic Switch**: Call `session/set_config_option` with `configId: "model"` and `value: "<modelId>"` to change the active model for subsequent prompts.
284
+ 4. **Per-Prompt Override**: Include `_meta.iris.modelId` or `_meta.modelId` in the `session/prompt` payload to run a single prompt turn with a specific model.
285
+
286
+ ### IrisClient SDK
287
+
288
+ Install the published package in the Node.js process that owns your IDE's
289
+ agent integration:
290
+
291
+ ```sh
292
+ npm install @4onstudios/iris-agent
293
+ ```
294
+
295
+ The spawned agent uses the provider credentials from its environment. For
296
+ example:
297
+
298
+ ```sh
299
+ OPENROUTER_API_KEY=... npm run your-ide-backend
300
+ ```
301
+
302
+ When using a local checkout instead of the published package, build it before
303
+ starting the compiled CLI:
304
+
305
+ ```sh
306
+ npm install
307
+ npm run build
308
+ ```
309
+
310
+ When installing from a Git branch or commit, npm runs this package's
311
+ `prepare` script to build `dist/` during installation, so root exports such as
312
+ `IrisClient` are available without manually committing generated artifacts.
313
+
314
+ The ACP subprocess writes protocol messages to stdout and diagnostic logs to
315
+ stderr. Never merge logs into stdout or pipe stdout through a text logger;
316
+ doing so corrupts the ACP stream.
317
+
318
+ IDE extensions written for Node.js can use the exported `IrisClient` instead of
319
+ managing ACP messages or the agent subprocess directly:
320
+
321
+ ```ts
322
+ import { IrisClient } from "@4onstudios/iris-agent";
323
+
324
+ const { client } = await IrisClient.spawn({
325
+ cwd: workspaceRoot,
326
+ onSessionUpdate(notification) {
327
+ renderAgentUpdate(notification.update);
328
+ },
329
+ });
330
+
331
+ try {
332
+ // Open session with an optional specific model
333
+ await client.openSession(workspaceRoot, {
334
+ modelId: "openrouter/anthropic/claude-3.7-sonnet",
335
+ });
336
+
337
+ // Prompt the agent
338
+ const result = await client.prompt("Explain the selected code");
339
+ console.log(result.stopReason);
340
+
341
+ // Switch model dynamically mid-session
342
+ await client.setModel("openrouter/openai/gpt-4o");
343
+
344
+ // Or override model for a single prompt
345
+ await client.prompt("Refactor this function", {
346
+ modelId: "openrouter/google/gemini-2.0-flash",
347
+ });
348
+ } finally {
349
+ await client.close();
350
+ }
351
+ ```
352
+
353
+ `IrisClient.spawn()` starts `iris-agent --workspace <cwd> --acp`, initializes the
354
+ ACP connection, and owns process cleanup. The `cwd` must be the workspace path
355
+ the agent is allowed to access. The package's `iris-agent` executable must be
356
+ available on `PATH`; use `command` and `args` when your IDE starts a local
357
+ checkout or a custom launcher:
358
+
359
+ ```ts
360
+ const { client } = await IrisClient.spawn({
361
+ command: "node",
362
+ args: ["/path/to/iris-agent/dist/cli.js", "--workspace", workspaceRoot, "--acp"],
363
+ cwd: workspaceRoot,
364
+ env: { OPENAI_API_KEY: process.env.OPENAI_API_KEY },
365
+ });
366
+ ```
367
+
368
+ One `IrisClient` owns one active ACP session. Call `openSession()` before
369
+ `prompt()`, use `cancel()` to stop the active turn, and call `closeSession()`
370
+ to end a session in the current workspace. An ACP process is bound to its
371
+ startup workspace: to switch workspaces, call `close()` and use `spawn()` to
372
+ create a new client for the new workspace. `close()` also terminates a process
373
+ created by `spawn()` and is safe to call repeatedly.
374
+
375
+ `IrisClient.connect()` accepts an existing ACP stream or in-process ACP agent
376
+ when the IDE manages the process or transport itself. Use this for IDEs that
377
+ already have a process supervisor or ACP transport. `spawn()` rejects if the
378
+ agent executable cannot start or initialization fails, so handle startup errors
379
+ before enabling agent commands in the UI.
380
+
381
+ The `onSessionUpdate` callback receives standard ACP session notifications:
382
+
383
+ | Update | Typical UI behavior |
384
+ | --- | --- |
385
+ | `agent_message_chunk` | Append assistant text. |
386
+ | `agent_thought_chunk` | Show or hide reasoning according to IDE policy. |
387
+ | `tool_call` | Show a tool as running. |
388
+ | `tool_call_update` | Update tool status and output. |
389
+
390
+ Do not expose provider API keys or raw ACP stdio streams to a browser renderer.
391
+ Keep the client in the trusted desktop/backend process and forward only the
392
+ events and commands your IDE UI needs.
393
+
394
+ ### Direct ACP JSON-RPC Client
395
+
396
+ Use `IrisClient` unless your integration must own the ACP process and
397
+ JSON-RPC transport. The following Node.js example starts the published CLI
398
+ with `npx`, performs the ACP v1 handshake, streams updates, and closes the
399
+ session and child process reliably:
400
+
401
+ ```ts
402
+ import { spawn } from "node:child_process";
403
+ import readline from "node:readline";
404
+
405
+ type JsonRpcResponse = {
406
+ id?: number;
407
+ result?: unknown;
408
+ error?: { code: number; message: string };
409
+ method?: string;
410
+ params?: {
411
+ update?: {
412
+ sessionUpdate?: string;
413
+ content?: { text?: string };
414
+ title?: string;
415
+ };
416
+ };
417
+ };
418
+
419
+ async function connectToIrisAcp() {
420
+ const workspaceRoot = process.cwd();
421
+ const agentProcess = spawn(
422
+ "npx",
423
+ ["--yes", "@4onstudios/iris-agent", "--workspace", workspaceRoot, "--acp"],
424
+ {
425
+ cwd: workspaceRoot,
426
+ stdio: ["pipe", "pipe", "inherit"],
427
+ env: process.env,
428
+ },
429
+ );
430
+
431
+ let nextMessageId = 1;
432
+ const pendingRequests = new Map<
433
+ number,
434
+ { resolve: (result: unknown) => void; reject: (error: Error) => void }
435
+ >();
436
+
437
+ const rejectPendingRequests = (error: Error) => {
438
+ for (const { reject } of pendingRequests.values()) reject(error);
439
+ pendingRequests.clear();
440
+ };
441
+
442
+ agentProcess.once("error", rejectPendingRequests);
443
+ agentProcess.once("exit", (code, signal) => {
444
+ rejectPendingRequests(
445
+ new Error(`Iris ACP process exited (${signal ?? `code ${code ?? "unknown"}`})`),
446
+ );
447
+ });
448
+
449
+ const sendRequest = (method: string, params?: unknown): Promise<unknown> => {
450
+ const id = nextMessageId++;
451
+ agentProcess.stdin.write(
452
+ `${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`,
453
+ );
454
+ return new Promise((resolve, reject) => {
455
+ pendingRequests.set(id, { resolve, reject });
456
+ });
457
+ };
458
+
459
+ const lines = readline.createInterface({ input: agentProcess.stdout });
460
+ lines.on("line", (line) => {
461
+ if (!line.trim()) return;
462
+
463
+ const message = JSON.parse(line) as JsonRpcResponse;
464
+ if (typeof message.id === "number" && pendingRequests.has(message.id)) {
465
+ const request = pendingRequests.get(message.id)!;
466
+ pendingRequests.delete(message.id);
467
+ if (message.error) {
468
+ request.reject(new Error(message.error.message));
469
+ } else {
470
+ request.resolve(message.result);
471
+ }
472
+ return;
473
+ }
474
+
475
+ const update = message.params?.update;
476
+ if (message.method === "session/update") {
477
+ if (update?.sessionUpdate === "agent_message_chunk") {
478
+ process.stdout.write(update.content?.text ?? "");
479
+ } else if (update?.sessionUpdate === "tool_call") {
480
+ console.log(`\n[Executing tool: ${update.title ?? "unknown"}]`);
481
+ }
482
+ }
483
+ });
484
+
485
+ try {
486
+ await sendRequest("initialize", {
487
+ protocolVersion: 1,
488
+ clientInfo: { name: "my-custom-service", version: "1.0.0" },
489
+ clientCapabilities: {},
490
+ });
491
+
492
+ const session = (await sendRequest("session/new", {
493
+ cwd: workspaceRoot,
494
+ mcpServers: [],
495
+ })) as { sessionId: string };
496
+
497
+ await sendRequest("session/prompt", {
498
+ sessionId: session.sessionId,
499
+ prompt: [
500
+ {
501
+ type: "text",
502
+ text: "List all files in the root folder and summarize the project.",
503
+ },
504
+ ],
505
+ });
506
+
507
+ await sendRequest("session/close", { sessionId: session.sessionId });
508
+ } finally {
509
+ lines.close();
510
+ if (!agentProcess.killed) agentProcess.kill();
511
+ }
512
+ }
513
+
514
+ connectToIrisAcp().catch(console.error);
515
+ ```
516
+
517
+ Set `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, or the API key for the selected
518
+ provider in the environment before starting the client. Keep those credentials
519
+ in the trusted Node.js process; do not pass them to a browser renderer.
520
+
521
+ The CLI does not load MCP definitions from `AIRIS_MCP_SERVERS`. In the current
522
+ ACP server, custom MCP servers are not yet applied from `session/new` either,
523
+ so leave `mcpServers` empty as shown. To use custom MCP tools today, configure
524
+ them through the authenticated HTTP agent API rather than this ACP example.
525
+
526
+ #### Troubleshooting
527
+
528
+ - `ENOENT` when calling `spawn()` means the configured `command` is not on
529
+ `PATH`. Set `command` and `args` to the compiled CLI, or install the package
530
+ globally for the IDE process.
531
+ - An initialization failure usually means the subprocess exited early, the
532
+ provider key is missing, or stdout contains non-ACP output. Inspect stderr
533
+ and verify the provider environment passed through `env`.
534
+ - A prompt requires an open session. Call `openSession()` once per workspace.
535
+ To switch workspaces, call `close()` to terminate the current ACP process,
536
+ then use `spawn()` to create a client bound to the new workspace.
537
+ - `IrisClient` requires a Node.js desktop/backend process. Browser-only IDE
538
+ clients should call their backend over HTTPS/WebSocket/IPC instead of
539
+ spawning the agent in the renderer.
540
+
541
+ ### Custom IDE Backend
542
+
543
+ For a custom IDE, keep `IrisClient` in the desktop or backend process and
544
+ forward agent updates to the UI over WebSocket, IPC, or the IDE's event bus:
545
+
546
+ ```ts
547
+ import { IrisClient } from "@4onstudios/iris-agent";
548
+
549
+ export class IrisAgentController {
550
+ private client?: IrisClient;
551
+
552
+ async start(workspaceRoot: string, onUpdate: (update: unknown) => void) {
553
+ const spawned = await IrisClient.spawn({
554
+ cwd: workspaceRoot,
555
+ clientName: "my-custom-ide",
556
+ clientVersion: "1.0.0",
557
+ onSessionUpdate(notification) {
558
+ onUpdate(notification.update);
559
+ },
560
+ });
561
+
562
+ this.client = spawned.client;
563
+ await this.client.openSession(workspaceRoot);
564
+ }
565
+
566
+ async prompt(prompt: string) {
567
+ if (!this.client) throw new Error("Iris agent is not running");
568
+ return this.client.prompt(prompt);
569
+ }
570
+
571
+ async cancel() {
572
+ await this.client?.cancel();
573
+ }
574
+
575
+ async stop() {
576
+ await this.client?.close();
577
+ this.client = undefined;
578
+ }
579
+ }
580
+ ```
581
+
582
+ Example backend routes can forward updates to the custom IDE client:
583
+
584
+ ```ts
585
+ const iris = new IrisAgentController();
586
+
587
+ await iris.start(workspaceRoot, (update) => {
588
+ websocket.broadcast({ type: "agent-update", update });
589
+ });
590
+
591
+ app.post("/api/agent/prompt", async (request, response) => {
592
+ response.json(await iris.prompt(request.body.prompt));
593
+ });
594
+
595
+ app.post("/api/agent/cancel", async (_request, response) => {
596
+ await iris.cancel();
597
+ response.sendStatus(204);
598
+ });
599
+
600
+ app.post("/api/agent/stop", async (_request, response) => {
601
+ await iris.stop();
602
+ response.sendStatus(204);
603
+ });
604
+ ```
605
+
606
+ Handle forwarded updates in the IDE UI using `sessionUpdate`:
607
+
608
+ ```ts
609
+ function handleAgentUpdate(update: any) {
610
+ switch (update.sessionUpdate) {
611
+ case "agent_message_chunk":
612
+ appendAssistantText(update.content.text);
613
+ break;
614
+ case "agent_thought_chunk":
615
+ appendReasoning(update.content.text);
616
+ break;
617
+ case "tool_call":
618
+ showToolStarted(update.title, update.toolCallId);
619
+ break;
620
+ case "tool_call_update":
621
+ updateToolStatus(update.toolCallId, update.status);
622
+ break;
623
+ }
624
+ }
625
+ ```
626
+
627
+ The integration flow is:
628
+
629
+ ```text
630
+ Custom IDE UI -> IDE backend -> IrisClient.spawn()
631
+ -> iris-agent --acp -> ACP session/update events
632
+ -> IDE backend -> WebSocket/IPC -> Custom IDE UI
633
+ ```
634
+
635
+ ## Development
636
+
637
+ ```sh
638
+ npm run typecheck
639
+ npm run build
640
+ npm test
641
+ ```
642
+
643
+ The repository also provides Make targets:
644
+
645
+ ```sh
646
+ make test # npm test with Jest's serial/forced-exit flags
647
+ make run # npm start
648
+ make dev # npm run dev
649
+ ```
650
+
651
+ For production, run the compiled output from a process supervisor, restrict
652
+ `AGENT_ALLOWED_ORIGINS`, keep provider credentials in a secret store, protect
653
+ the HTTP service behind TLS/authentication, and use a writable persistent
654
+ location for the run database.
655
+
656
+ ## Contributing
657
+
658
+ Contributions are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup,
659
+ validation, and pull request guidance. Use the repository's issue templates for
660
+ bug reports and feature requests. See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
661
+ for community expectations and [SECURITY.md](./SECURITY.md) for private
662
+ vulnerability reporting.
663
+
664
+ The project logo is available at
665
+ [`assets/iris-agent-logo.svg`](./assets/iris-agent-logo.svg) for repository and
666
+ community references. Keep the logo unchanged when using it as the project
667
+ mark.
668
+
669
+ ## Package publishing
670
+
671
+ The package is configured for public npm publication:
672
+
673
+ ```sh
674
+ npm login
675
+ npm publish
676
+ ```
677
+
678
+ Yarn users can install the published package with:
679
+
680
+ ```sh
681
+ yarn global add @4onstudios/iris-agent
682
+ ```
683
+
684
+ Publishing requires access to the `@4onstudios` npm scope. The package is
685
+ configured with public access, but npm credentials and organization
686
+ permissions must be supplied by the publisher.
687
+
688
+ See [RELEASING.md](./RELEASING.md) for npm publication and versioning steps.
689
+
690
+ This project is released under the [MIT License](LICENSE).