inferoa 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 (200) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +154 -0
  3. package/dist/src/app.d.ts +16 -0
  4. package/dist/src/app.js +17 -0
  5. package/dist/src/app.js.map +1 -0
  6. package/dist/src/autoresearch/state.d.ts +106 -0
  7. package/dist/src/autoresearch/state.js +469 -0
  8. package/dist/src/autoresearch/state.js.map +1 -0
  9. package/dist/src/cli.d.ts +2 -0
  10. package/dist/src/cli.js +415 -0
  11. package/dist/src/cli.js.map +1 -0
  12. package/dist/src/code-intelligence/codegraph-engine.d.ts +55 -0
  13. package/dist/src/code-intelligence/codegraph-engine.js +593 -0
  14. package/dist/src/code-intelligence/codegraph-engine.js.map +1 -0
  15. package/dist/src/code-intelligence/hub.d.ts +37 -0
  16. package/dist/src/code-intelligence/hub.js +65 -0
  17. package/dist/src/code-intelligence/hub.js.map +1 -0
  18. package/dist/src/config/config.d.ts +12 -0
  19. package/dist/src/config/config.js +229 -0
  20. package/dist/src/config/config.js.map +1 -0
  21. package/dist/src/config/defaults.d.ts +2 -0
  22. package/dist/src/config/defaults.js +44 -0
  23. package/dist/src/config/defaults.js.map +1 -0
  24. package/dist/src/config/secret-vault.d.ts +3 -0
  25. package/dist/src/config/secret-vault.js +106 -0
  26. package/dist/src/config/secret-vault.js.map +1 -0
  27. package/dist/src/context/compressor.d.ts +33 -0
  28. package/dist/src/context/compressor.js +501 -0
  29. package/dist/src/context/compressor.js.map +1 -0
  30. package/dist/src/context/prompt.d.ts +26 -0
  31. package/dist/src/context/prompt.js +572 -0
  32. package/dist/src/context/prompt.js.map +1 -0
  33. package/dist/src/daemon/serve.d.ts +2 -0
  34. package/dist/src/daemon/serve.js +11 -0
  35. package/dist/src/daemon/serve.js.map +1 -0
  36. package/dist/src/daemon/supervisor.d.ts +33 -0
  37. package/dist/src/daemon/supervisor.js +252 -0
  38. package/dist/src/daemon/supervisor.js.map +1 -0
  39. package/dist/src/goals/state.d.ts +105 -0
  40. package/dist/src/goals/state.js +736 -0
  41. package/dist/src/goals/state.js.map +1 -0
  42. package/dist/src/model/endpoint-signals.d.ts +15 -0
  43. package/dist/src/model/endpoint-signals.js +186 -0
  44. package/dist/src/model/endpoint-signals.js.map +1 -0
  45. package/dist/src/model/gateway.d.ts +11 -0
  46. package/dist/src/model/gateway.js +455 -0
  47. package/dist/src/model/gateway.js.map +1 -0
  48. package/dist/src/plans/state.d.ts +28 -0
  49. package/dist/src/plans/state.js +123 -0
  50. package/dist/src/plans/state.js.map +1 -0
  51. package/dist/src/runtime.d.ts +92 -0
  52. package/dist/src/runtime.js +757 -0
  53. package/dist/src/runtime.js.map +1 -0
  54. package/dist/src/session/store.d.ts +84 -0
  55. package/dist/src/session/store.js +593 -0
  56. package/dist/src/session/store.js.map +1 -0
  57. package/dist/src/session/workspace.d.ts +2 -0
  58. package/dist/src/session/workspace.js +14 -0
  59. package/dist/src/session/workspace.js.map +1 -0
  60. package/dist/src/skills/registry.d.ts +24 -0
  61. package/dist/src/skills/registry.js +203 -0
  62. package/dist/src/skills/registry.js.map +1 -0
  63. package/dist/src/tools/autoresearch-tools.d.ts +6 -0
  64. package/dist/src/tools/autoresearch-tools.js +412 -0
  65. package/dist/src/tools/autoresearch-tools.js.map +1 -0
  66. package/dist/src/tools/clarify-tool.d.ts +3 -0
  67. package/dist/src/tools/clarify-tool.js +107 -0
  68. package/dist/src/tools/clarify-tool.js.map +1 -0
  69. package/dist/src/tools/code-intelligence.d.ts +15 -0
  70. package/dist/src/tools/code-intelligence.js +391 -0
  71. package/dist/src/tools/code-intelligence.js.map +1 -0
  72. package/dist/src/tools/context.d.ts +11 -0
  73. package/dist/src/tools/context.js +2 -0
  74. package/dist/src/tools/context.js.map +1 -0
  75. package/dist/src/tools/goal-tools.d.ts +3 -0
  76. package/dist/src/tools/goal-tools.js +279 -0
  77. package/dist/src/tools/goal-tools.js.map +1 -0
  78. package/dist/src/tools/omni-tools.d.ts +8 -0
  79. package/dist/src/tools/omni-tools.js +349 -0
  80. package/dist/src/tools/omni-tools.js.map +1 -0
  81. package/dist/src/tools/permissions.d.ts +11 -0
  82. package/dist/src/tools/permissions.js +74 -0
  83. package/dist/src/tools/permissions.js.map +1 -0
  84. package/dist/src/tools/plan-tools.d.ts +3 -0
  85. package/dist/src/tools/plan-tools.js +314 -0
  86. package/dist/src/tools/plan-tools.js.map +1 -0
  87. package/dist/src/tools/process-tools.d.ts +6 -0
  88. package/dist/src/tools/process-tools.js +199 -0
  89. package/dist/src/tools/process-tools.js.map +1 -0
  90. package/dist/src/tools/registry.d.ts +20 -0
  91. package/dist/src/tools/registry.js +187 -0
  92. package/dist/src/tools/registry.js.map +1 -0
  93. package/dist/src/tools/schemas.d.ts +3 -0
  94. package/dist/src/tools/schemas.js +500 -0
  95. package/dist/src/tools/schemas.js.map +1 -0
  96. package/dist/src/tools/skill-tools.d.ts +6 -0
  97. package/dist/src/tools/skill-tools.js +124 -0
  98. package/dist/src/tools/skill-tools.js.map +1 -0
  99. package/dist/src/tools/text-args.d.ts +5 -0
  100. package/dist/src/tools/text-args.js +22 -0
  101. package/dist/src/tools/text-args.js.map +1 -0
  102. package/dist/src/tools/web-search.d.ts +5 -0
  103. package/dist/src/tools/web-search.js +602 -0
  104. package/dist/src/tools/web-search.js.map +1 -0
  105. package/dist/src/tools/workspace-tools.d.ts +17 -0
  106. package/dist/src/tools/workspace-tools.js +561 -0
  107. package/dist/src/tools/workspace-tools.js.map +1 -0
  108. package/dist/src/tui/activity.d.ts +11 -0
  109. package/dist/src/tui/activity.js +75 -0
  110. package/dist/src/tui/activity.js.map +1 -0
  111. package/dist/src/tui/ansi.d.ts +24 -0
  112. package/dist/src/tui/ansi.js +131 -0
  113. package/dist/src/tui/ansi.js.map +1 -0
  114. package/dist/src/tui/app.d.ts +163 -0
  115. package/dist/src/tui/app.js +4204 -0
  116. package/dist/src/tui/app.js.map +1 -0
  117. package/dist/src/tui/cache-footer.d.ts +21 -0
  118. package/dist/src/tui/cache-footer.js +75 -0
  119. package/dist/src/tui/cache-footer.js.map +1 -0
  120. package/dist/src/tui/clarify.d.ts +14 -0
  121. package/dist/src/tui/clarify.js +187 -0
  122. package/dist/src/tui/clarify.js.map +1 -0
  123. package/dist/src/tui/composer.d.ts +79 -0
  124. package/dist/src/tui/composer.js +592 -0
  125. package/dist/src/tui/composer.js.map +1 -0
  126. package/dist/src/tui/event-view.d.ts +5 -0
  127. package/dist/src/tui/event-view.js +392 -0
  128. package/dist/src/tui/event-view.js.map +1 -0
  129. package/dist/src/tui/home.d.ts +7 -0
  130. package/dist/src/tui/home.js +92 -0
  131. package/dist/src/tui/home.js.map +1 -0
  132. package/dist/src/tui/markdown.d.ts +18 -0
  133. package/dist/src/tui/markdown.js +271 -0
  134. package/dist/src/tui/markdown.js.map +1 -0
  135. package/dist/src/tui/mode-footer.d.ts +9 -0
  136. package/dist/src/tui/mode-footer.js +62 -0
  137. package/dist/src/tui/mode-footer.js.map +1 -0
  138. package/dist/src/tui/plan-view.d.ts +8 -0
  139. package/dist/src/tui/plan-view.js +45 -0
  140. package/dist/src/tui/plan-view.js.map +1 -0
  141. package/dist/src/tui/prompt-queue.d.ts +18 -0
  142. package/dist/src/tui/prompt-queue.js +27 -0
  143. package/dist/src/tui/prompt-queue.js.map +1 -0
  144. package/dist/src/tui/resize.d.ts +7 -0
  145. package/dist/src/tui/resize.js +15 -0
  146. package/dist/src/tui/resize.js.map +1 -0
  147. package/dist/src/tui/session-picker.d.ts +10 -0
  148. package/dist/src/tui/session-picker.js +17 -0
  149. package/dist/src/tui/session-picker.js.map +1 -0
  150. package/dist/src/tui/session-transcript.d.ts +2 -0
  151. package/dist/src/tui/session-transcript.js +44 -0
  152. package/dist/src/tui/session-transcript.js.map +1 -0
  153. package/dist/src/tui/slash-notice.d.ts +2 -0
  154. package/dist/src/tui/slash-notice.js +9 -0
  155. package/dist/src/tui/slash-notice.js.map +1 -0
  156. package/dist/src/tui/slash.d.ts +21 -0
  157. package/dist/src/tui/slash.js +103 -0
  158. package/dist/src/tui/slash.js.map +1 -0
  159. package/dist/src/tui/splash.d.ts +4 -0
  160. package/dist/src/tui/splash.js +64 -0
  161. package/dist/src/tui/splash.js.map +1 -0
  162. package/dist/src/tui/tool-renderer.d.ts +6 -0
  163. package/dist/src/tui/tool-renderer.js +1024 -0
  164. package/dist/src/tui/tool-renderer.js.map +1 -0
  165. package/dist/src/tui/transcript-spacing.d.ts +1 -0
  166. package/dist/src/tui/transcript-spacing.js +4 -0
  167. package/dist/src/tui/transcript-spacing.js.map +1 -0
  168. package/dist/src/types.d.ts +220 -0
  169. package/dist/src/types.js +2 -0
  170. package/dist/src/types.js.map +1 -0
  171. package/dist/src/util/abort.d.ts +3 -0
  172. package/dist/src/util/abort.js +19 -0
  173. package/dist/src/util/abort.js.map +1 -0
  174. package/dist/src/util/clock.d.ts +2 -0
  175. package/dist/src/util/clock.js +7 -0
  176. package/dist/src/util/clock.js.map +1 -0
  177. package/dist/src/util/fs.d.ts +13 -0
  178. package/dist/src/util/fs.js +75 -0
  179. package/dist/src/util/fs.js.map +1 -0
  180. package/dist/src/util/hash.d.ts +6 -0
  181. package/dist/src/util/hash.js +50 -0
  182. package/dist/src/util/hash.js.map +1 -0
  183. package/dist/src/util/limit.d.ts +11 -0
  184. package/dist/src/util/limit.js +29 -0
  185. package/dist/src/util/limit.js.map +1 -0
  186. package/dist/src/util/types.d.ts +22 -0
  187. package/dist/src/util/types.js +33 -0
  188. package/dist/src/util/types.js.map +1 -0
  189. package/dist/src/validation/acceptance.d.ts +12 -0
  190. package/dist/src/validation/acceptance.js +251 -0
  191. package/dist/src/validation/acceptance.js.map +1 -0
  192. package/dist/src/validation/milestone.d.ts +2 -0
  193. package/dist/src/validation/milestone.js +141 -0
  194. package/dist/src/validation/milestone.js.map +1 -0
  195. package/docs/final-acceptance-task.md +193 -0
  196. package/docs/public-source-hygiene.md +21 -0
  197. package/docs/roadmap.md +265 -0
  198. package/docs/tui-product-design.md +270 -0
  199. package/package.json +67 -0
  200. package/skills/coding-workflow/SKILL.md +16 -0
@@ -0,0 +1,757 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { randomId } from "./util/hash.js";
3
+ import { PromptBuilder } from "./context/prompt.js";
4
+ import { ContextCompressor } from "./context/compressor.js";
5
+ import { ModelGateway } from "./model/gateway.js";
6
+ import { EndpointSignals, providerId } from "./model/endpoint-signals.js";
7
+ import { ToolRegistry } from "./tools/registry.js";
8
+ import { SkillRegistry } from "./skills/registry.js";
9
+ import { CodeIntelligenceHub } from "./code-intelligence/hub.js";
10
+ import { fail } from "./util/limit.js";
11
+ import { isAbortError, throwIfAborted } from "./util/abort.js";
12
+ import { applyGoalUsage, goalCompletionReportForRun, modelUsageTokenCost, recordGoalCompletionReport } from "./goals/state.js";
13
+ export class Runtime {
14
+ config;
15
+ workspace;
16
+ store;
17
+ gateway;
18
+ endpointSignals;
19
+ tools;
20
+ promptBuilder;
21
+ compressor;
22
+ skills;
23
+ codeIntelligence;
24
+ constructor(config, workspace, store) {
25
+ this.config = config;
26
+ this.workspace = workspace;
27
+ this.store = store;
28
+ this.gateway = new ModelGateway(config);
29
+ this.endpointSignals = new EndpointSignals(config);
30
+ this.codeIntelligence = new CodeIntelligenceHub(config, workspace);
31
+ this.tools = new ToolRegistry(config, workspace, store, this.codeIntelligence);
32
+ this.promptBuilder = new PromptBuilder(config, store, workspace);
33
+ this.compressor = new ContextCompressor(config, store, workspace, this.gateway);
34
+ this.skills = new SkillRegistry(workspace, config);
35
+ }
36
+ async createSession(title) {
37
+ return this.store.createSession(this.workspace, title);
38
+ }
39
+ async run(options) {
40
+ const session = options.session_id ? this.requiredSession(options.session_id) : await this.createSession(options.title ?? titleFromPrompt(options.prompt));
41
+ const clientId = options.client_id ?? randomId("c");
42
+ const runId = randomId("run");
43
+ const startedAt = Date.now();
44
+ let goalTokenUsage = 0;
45
+ let toolRounds = 0;
46
+ let toolCalls = 0;
47
+ this.store.acquireLock(session.session_id, clientId, options.owner_kind ?? "cli");
48
+ const heartbeat = setInterval(() => {
49
+ this.store.heartbeatLock(session.session_id, clientId);
50
+ }, 15_000);
51
+ heartbeat.unref();
52
+ try {
53
+ throwIfAborted(options.signal);
54
+ const discoveredSkills = await this.skills.discover();
55
+ const enabledSkillNames = this.config.skills.enabled.slice().sort();
56
+ const loadedSkills = await this.skills.loadEnabled(discoveredSkills);
57
+ const availableTools = this.tools.list();
58
+ this.store.appendEvent({
59
+ session_id: session.session_id,
60
+ run_id: runId,
61
+ type: "session.resumed",
62
+ data: {
63
+ title: session.title,
64
+ skill_count: discoveredSkills.length,
65
+ enabled_skill_count: loadedSkills.length,
66
+ enabled_skills: loadedSkills.map((skill) => skill.name),
67
+ },
68
+ });
69
+ this.store.appendEvent({
70
+ session_id: session.session_id,
71
+ run_id: runId,
72
+ type: "user.prompt",
73
+ data: { prompt: options.prompt },
74
+ });
75
+ toolCalls += await this.prefetchPromptUrls(options.prompt, session.session_id, runId, options.onStatus, options.signal);
76
+ let currentPrompt = options.prompt;
77
+ let response;
78
+ const maxToolRounds = normalizeMaxToolRounds(options.max_tool_rounds);
79
+ let stopped;
80
+ let compressedThisRun = false;
81
+ while (true) {
82
+ throwIfAborted(options.signal);
83
+ const sessionNow = this.requiredSession(session.session_id);
84
+ const promptContext = this.promptBuilder.build(sessionNow, currentPrompt, availableTools, discoveredSkills, runId, enabledSkillNames);
85
+ const pressure = await this.compressor.assess(promptContext);
86
+ if (pressure.should_compact && (pressure.reason !== "forced-by-config" || !compressedThisRun)) {
87
+ options.onStatus?.({
88
+ type: "compression_start",
89
+ reason: pressure.reason,
90
+ estimated_tokens: pressure.estimated_tokens,
91
+ threshold_tokens: pressure.threshold_tokens,
92
+ });
93
+ const compacted = await this.compressor.compact(sessionNow, promptContext, availableTools, pressure.reason, {
94
+ activeRunId: runId,
95
+ currentPrompt,
96
+ skills: discoveredSkills,
97
+ enabledSkillNames,
98
+ });
99
+ compressedThisRun = true;
100
+ this.store.appendEvent({
101
+ session_id: session.session_id,
102
+ run_id: runId,
103
+ type: "evidence.context_compression",
104
+ data: {
105
+ reason: pressure.reason,
106
+ estimated_tokens: pressure.estimated_tokens,
107
+ threshold_tokens: pressure.threshold_tokens,
108
+ epoch_id: compacted.epoch_id,
109
+ archive_resource_uri: compacted.resource_uri,
110
+ archived_events: compacted.archived_events,
111
+ protected_tail_events: compacted.protected_tail_events,
112
+ protected_prompt_count: compacted.protected_user_prompts.length,
113
+ protected_user_prompts: compacted.protected_user_prompts,
114
+ },
115
+ });
116
+ options.onStatus?.({
117
+ type: "compression_end",
118
+ reason: pressure.reason,
119
+ estimated_tokens: pressure.estimated_tokens,
120
+ threshold_tokens: pressure.threshold_tokens,
121
+ archive_resource_uri: compacted.resource_uri,
122
+ archived_events: compacted.archived_events,
123
+ protected_tail_events: compacted.protected_tail_events,
124
+ summary: compacted.summary,
125
+ protected_user_prompts: compacted.protected_user_prompts,
126
+ });
127
+ }
128
+ throwIfAborted(options.signal);
129
+ const rebuilt = this.promptBuilder.build(this.requiredSession(session.session_id), currentPrompt, availableTools, discoveredSkills, runId, enabledSkillNames);
130
+ const request = {
131
+ session_id: session.session_id,
132
+ run_id: runId,
133
+ mode: this.config.model_setup.mode,
134
+ provider_id: providerId(this.config),
135
+ model: this.config.model_setup.model ?? "",
136
+ messages: rebuilt.messages,
137
+ tools: availableTools,
138
+ request_class: options.request_class ?? "interactive",
139
+ prompt_hash: rebuilt.prompt_hash,
140
+ tool_schema_hash: rebuilt.tool_schema_hash,
141
+ prompt_epoch_id: rebuilt.epoch.prompt_epoch_id,
142
+ cache_salt: rebuilt.epoch.cache_salt,
143
+ };
144
+ this.store.appendEvent({
145
+ session_id: session.session_id,
146
+ run_id: runId,
147
+ type: "model.request.started",
148
+ data: {
149
+ provider_id: request.provider_id,
150
+ mode: request.mode,
151
+ model: request.model,
152
+ request_class: request.request_class,
153
+ prompt_hash: request.prompt_hash,
154
+ tool_schema_hash: request.tool_schema_hash,
155
+ estimated_tokens: rebuilt.estimated_tokens,
156
+ prompt_epoch_id: request.prompt_epoch_id,
157
+ },
158
+ });
159
+ options.onStatus?.({ type: "model_start", model: request.model });
160
+ response = await this.streamModelWithRetry(request, options.onDelta, options.onStatus, options.signal);
161
+ throwIfAborted(options.signal);
162
+ const endpointSnapshot = await this.endpointSignals.snapshot().catch((error) => ({
163
+ mode: request.mode,
164
+ provider_id: request.provider_id,
165
+ base_url: this.config.model_setup.base_url,
166
+ model: request.model,
167
+ errors: [`endpoint snapshot unavailable: ${error instanceof Error ? error.message : String(error)}`],
168
+ }));
169
+ this.store.recordEndpointEvidence(session.session_id, runId, request.provider_id, mergeEndpointEvidence(endpointSnapshot, this.gateway.evidenceFromResponse(request, response)), request.prompt_hash, request.tool_schema_hash);
170
+ this.store.appendEvent({
171
+ session_id: session.session_id,
172
+ run_id: runId,
173
+ type: "model.response.settled",
174
+ data: {
175
+ content: response.content,
176
+ tool_calls: response.tool_calls,
177
+ usage: response.usage,
178
+ request_id: response.request_id,
179
+ response_id: response.response_id,
180
+ model: response.model,
181
+ },
182
+ });
183
+ goalTokenUsage += modelUsageTokenCost(response.usage);
184
+ if (!response.tool_calls.length) {
185
+ break;
186
+ }
187
+ if (maxToolRounds !== undefined && toolRounds >= maxToolRounds) {
188
+ stopped = { reason: "max_tool_rounds", max_tool_rounds: maxToolRounds };
189
+ break;
190
+ }
191
+ toolRounds += 1;
192
+ for (const call of response.tool_calls) {
193
+ throwIfAborted(options.signal);
194
+ await this.executeToolCall(call, session.session_id, runId, options.onStatus, options.onClarify);
195
+ toolCalls += 1;
196
+ throwIfAborted(options.signal);
197
+ }
198
+ currentPrompt =
199
+ "Continue the task using the tool results. Failed tool results are evidence, not a reason to stop; use corrected arguments or another available tool when useful. Do not repeat the exact same failed call unless the arguments change. If independent reads, searches, edits, tests, or web fetches remain, keep calling tools; otherwise finish with a concise evidence-based summary.";
200
+ }
201
+ const finalSessionNow = this.requiredSession(session.session_id);
202
+ const finalPromptContext = this.promptBuilder.build(finalSessionNow, currentPrompt, availableTools, discoveredSkills, runId, enabledSkillNames);
203
+ const finalPressure = await this.compressor.assess(finalPromptContext);
204
+ if (finalPressure.should_compact && (finalPressure.reason !== "forced-by-config" || !compressedThisRun)) {
205
+ options.onStatus?.({
206
+ type: "compression_start",
207
+ reason: `post-run:${finalPressure.reason}`,
208
+ estimated_tokens: finalPressure.estimated_tokens,
209
+ threshold_tokens: finalPressure.threshold_tokens,
210
+ });
211
+ const compacted = await this.compressor.compact(finalSessionNow, finalPromptContext, availableTools, `post-run:${finalPressure.reason}`, {
212
+ activeRunId: runId,
213
+ currentPrompt,
214
+ skills: discoveredSkills,
215
+ enabledSkillNames,
216
+ });
217
+ compressedThisRun = true;
218
+ this.store.appendEvent({
219
+ session_id: session.session_id,
220
+ run_id: runId,
221
+ type: "evidence.context_compression",
222
+ data: {
223
+ reason: `post-run:${finalPressure.reason}`,
224
+ estimated_tokens: finalPressure.estimated_tokens,
225
+ threshold_tokens: finalPressure.threshold_tokens,
226
+ epoch_id: compacted.epoch_id,
227
+ archive_resource_uri: compacted.resource_uri,
228
+ archived_events: compacted.archived_events,
229
+ protected_tail_events: compacted.protected_tail_events,
230
+ protected_prompt_count: compacted.protected_user_prompts.length,
231
+ protected_user_prompts: compacted.protected_user_prompts,
232
+ },
233
+ });
234
+ options.onStatus?.({
235
+ type: "compression_end",
236
+ reason: `post-run:${finalPressure.reason}`,
237
+ estimated_tokens: finalPressure.estimated_tokens,
238
+ threshold_tokens: finalPressure.threshold_tokens,
239
+ archive_resource_uri: compacted.resource_uri,
240
+ archived_events: compacted.archived_events,
241
+ protected_tail_events: compacted.protected_tail_events,
242
+ summary: compacted.summary,
243
+ protected_user_prompts: compacted.protected_user_prompts,
244
+ });
245
+ }
246
+ const metrics = runMetrics(startedAt, goalTokenUsage, toolRounds, toolCalls);
247
+ if (stopped) {
248
+ applyGoalUsage(this.store, session.session_id, metrics, runId);
249
+ const goalReport = recordGoalCompletionReport(this.store, session.session_id, runId);
250
+ if (goalReport) {
251
+ const reportBlock = renderGoalReportBlock(goalReport);
252
+ response = response ? { ...response, content: appendGoalReport(response.content, reportBlock) } : response;
253
+ options.onDelta?.(reportBlock);
254
+ }
255
+ this.store.appendEvent({
256
+ session_id: session.session_id,
257
+ run_id: runId,
258
+ type: "run.stopped",
259
+ data: {
260
+ ...stopped,
261
+ tool_rounds: toolRounds,
262
+ tool_calls: toolCalls,
263
+ tokens: goalTokenUsage,
264
+ duration_ms: metrics.duration_ms,
265
+ },
266
+ });
267
+ }
268
+ else {
269
+ applyGoalUsage(this.store, session.session_id, metrics, runId);
270
+ const goalReport = recordGoalCompletionReport(this.store, session.session_id, runId);
271
+ if (goalReport) {
272
+ const reportBlock = renderGoalReportBlock(goalReport);
273
+ response = response ? { ...response, content: appendGoalReport(response.content, reportBlock) } : response;
274
+ options.onDelta?.(reportBlock);
275
+ }
276
+ this.store.appendEvent({
277
+ session_id: session.session_id,
278
+ run_id: runId,
279
+ type: "run.completed",
280
+ data: {
281
+ tool_rounds: toolRounds,
282
+ tool_calls: toolCalls,
283
+ tokens: goalTokenUsage,
284
+ duration_ms: metrics.duration_ms,
285
+ },
286
+ });
287
+ }
288
+ return {
289
+ session: this.requiredSession(session.session_id),
290
+ run_id: runId,
291
+ content: response?.content ?? "",
292
+ tool_rounds: toolRounds,
293
+ tool_calls: toolCalls,
294
+ duration_ms: metrics.duration_ms,
295
+ tokens_used: goalTokenUsage,
296
+ goal_report: goalCompletionReportForRun(this.store, session.session_id, runId),
297
+ };
298
+ }
299
+ catch (error) {
300
+ const metrics = runMetrics(startedAt, goalTokenUsage, toolRounds, toolCalls);
301
+ applyGoalUsage(this.store, session.session_id, metrics, runId);
302
+ const goalReport = recordGoalCompletionReport(this.store, session.session_id, runId);
303
+ if (goalReport) {
304
+ options.onDelta?.(renderGoalReportBlock(goalReport));
305
+ }
306
+ this.store.appendEvent({
307
+ session_id: session.session_id,
308
+ run_id: runId,
309
+ type: "run.failed",
310
+ data: {
311
+ error: errorMessage(error),
312
+ tool_rounds: toolRounds,
313
+ tool_calls: toolCalls,
314
+ tokens: goalTokenUsage,
315
+ duration_ms: metrics.duration_ms,
316
+ },
317
+ });
318
+ throw error;
319
+ }
320
+ finally {
321
+ clearInterval(heartbeat);
322
+ this.store.releaseLock(session.session_id, clientId);
323
+ }
324
+ }
325
+ async streamModelWithRetry(request, onDelta, onStatus, signal) {
326
+ const retry = normalizeModelRetryConfig(this.config.model_retry);
327
+ let attempt = 1;
328
+ while (true) {
329
+ throwIfAborted(signal);
330
+ let emittedDelta = false;
331
+ try {
332
+ return await this.streamModelAttempt(request, onDelta
333
+ ? (text) => {
334
+ emittedDelta = true;
335
+ onDelta(text);
336
+ }
337
+ : undefined, signal, retry.request_timeout_ms);
338
+ }
339
+ catch (error) {
340
+ throwIfAborted(signal);
341
+ if (isAbortError(error)) {
342
+ throw error;
343
+ }
344
+ const retryable = isRetryableModelError(error);
345
+ const canRetry = retryable && !emittedDelta && hasRemainingRetryAttempt(attempt, retry.max_attempts);
346
+ if (!canRetry) {
347
+ this.store.appendEvent({
348
+ session_id: request.session_id,
349
+ run_id: request.run_id,
350
+ type: "model.request.failed",
351
+ data: {
352
+ provider_id: request.provider_id,
353
+ mode: request.mode,
354
+ model: request.model,
355
+ request_class: request.request_class,
356
+ prompt_hash: request.prompt_hash,
357
+ tool_schema_hash: request.tool_schema_hash,
358
+ prompt_epoch_id: request.prompt_epoch_id,
359
+ attempt,
360
+ retryable,
361
+ streamed_delta: emittedDelta,
362
+ error: errorMessage(error),
363
+ },
364
+ });
365
+ throw error;
366
+ }
367
+ const delayMs = retryDelayMs(attempt, retry);
368
+ this.store.appendEvent({
369
+ session_id: request.session_id,
370
+ run_id: request.run_id,
371
+ type: "model.request.retry",
372
+ data: {
373
+ provider_id: request.provider_id,
374
+ mode: request.mode,
375
+ model: request.model,
376
+ request_class: request.request_class,
377
+ prompt_hash: request.prompt_hash,
378
+ tool_schema_hash: request.tool_schema_hash,
379
+ prompt_epoch_id: request.prompt_epoch_id,
380
+ attempt,
381
+ next_attempt: attempt + 1,
382
+ delay_ms: delayMs,
383
+ max_attempts: retry.max_attempts,
384
+ error: errorMessage(error),
385
+ },
386
+ });
387
+ onStatus?.({
388
+ type: "model_retry",
389
+ model: request.model,
390
+ attempt,
391
+ next_attempt: attempt + 1,
392
+ delay_ms: delayMs,
393
+ max_attempts: retry.max_attempts,
394
+ error: errorMessage(error),
395
+ });
396
+ await delay(delayMs, undefined, { signal });
397
+ attempt += 1;
398
+ }
399
+ }
400
+ }
401
+ async streamModelAttempt(request, onDelta, signal, timeoutMs) {
402
+ if (timeoutMs <= 0) {
403
+ return await this.gateway.stream(request, onDelta, signal);
404
+ }
405
+ const controller = new AbortController();
406
+ let timedOut = false;
407
+ const timeout = setTimeout(() => {
408
+ timedOut = true;
409
+ controller.abort(modelRequestTimeoutError(timeoutMs));
410
+ }, timeoutMs);
411
+ timeout.unref();
412
+ const abortFromParent = () => controller.abort(signal?.reason);
413
+ if (signal?.aborted) {
414
+ abortFromParent();
415
+ }
416
+ else {
417
+ signal?.addEventListener("abort", abortFromParent, { once: true });
418
+ }
419
+ try {
420
+ return await this.gateway.stream(request, onDelta, controller.signal);
421
+ }
422
+ catch (error) {
423
+ if (timedOut) {
424
+ throw modelRequestTimeoutError(timeoutMs);
425
+ }
426
+ throw error;
427
+ }
428
+ finally {
429
+ clearTimeout(timeout);
430
+ signal?.removeEventListener("abort", abortFromParent);
431
+ }
432
+ }
433
+ async status() {
434
+ const signal = await this.endpointSignals.snapshot();
435
+ return {
436
+ workspace: {
437
+ alias: this.workspace.alias,
438
+ root: this.workspace.root,
439
+ },
440
+ model: this.gateway.capabilities(),
441
+ endpoint_signals: signal,
442
+ omni: {
443
+ enabled: this.config.omni.enabled,
444
+ endpoints: Object.fromEntries(Object.entries(this.config.omni.endpoints).map(([name, value]) => [
445
+ name,
446
+ {
447
+ configured: Boolean(value?.base_url && value.model),
448
+ base_url: value?.base_url,
449
+ model: value?.model,
450
+ },
451
+ ])),
452
+ },
453
+ tools: this.tools.list().map((tool) => tool.name),
454
+ };
455
+ }
456
+ dispose() {
457
+ this.codeIntelligence.dispose();
458
+ }
459
+ async prefetchPromptUrls(prompt, sessionId, runId, onStatus, signal) {
460
+ const urls = directHttpUrls(prompt).slice(0, 3);
461
+ let toolCalls = 0;
462
+ for (const url of urls) {
463
+ throwIfAborted(signal);
464
+ const result = await this.executeToolCall({ id: randomId("prefetch"), name: "web_fetch", arguments: { url, max_bytes: 1_000_000 } }, sessionId, runId, onStatus);
465
+ toolCalls += 1;
466
+ throwIfAborted(signal);
467
+ this.store.appendEvent({
468
+ session_id: sessionId,
469
+ run_id: runId,
470
+ type: "web.prefetch",
471
+ data: {
472
+ url,
473
+ ok: result.ok,
474
+ summary: result.summary,
475
+ resource_uri: result.resource_uri,
476
+ data: result.data,
477
+ error: result.error,
478
+ },
479
+ });
480
+ }
481
+ return toolCalls;
482
+ }
483
+ async executeToolCall(call, sessionId, runId, onStatus, onClarify) {
484
+ const startedAt = Date.now();
485
+ onStatus?.({
486
+ type: "tool_start",
487
+ session_id: sessionId,
488
+ run_id: runId,
489
+ tool_name: call.name,
490
+ tool_call_id: call.id,
491
+ summary: summarizeToolStart(call.name, call.arguments),
492
+ });
493
+ let result;
494
+ try {
495
+ result = await this.tools.call(call, { session_id: sessionId, run_id: runId, clarify: onClarify });
496
+ }
497
+ catch (error) {
498
+ result = fail("tool_runtime_exception", error instanceof Error ? error.message : String(error));
499
+ this.store.appendEvent({
500
+ session_id: sessionId,
501
+ run_id: runId,
502
+ type: "tool.result",
503
+ data: {
504
+ tool_call_id: call.id,
505
+ tool_name: call.name,
506
+ result: result,
507
+ },
508
+ });
509
+ }
510
+ onStatus?.({
511
+ type: "tool_end",
512
+ session_id: sessionId,
513
+ run_id: runId,
514
+ tool_name: call.name,
515
+ tool_call_id: call.id,
516
+ ok: result.ok,
517
+ summary: result.summary,
518
+ duration_ms: Date.now() - startedAt,
519
+ });
520
+ return result;
521
+ }
522
+ requiredSession(sessionId) {
523
+ const session = this.store.getSession(sessionId);
524
+ if (!session) {
525
+ throw new Error(`Unknown session: ${sessionId}`);
526
+ }
527
+ return session;
528
+ }
529
+ }
530
+ function titleFromPrompt(prompt) {
531
+ return prompt.trim().replace(/\s+/g, " ").slice(0, 80) || "New session";
532
+ }
533
+ function runMetrics(startedAt, tokens, toolRounds, toolCalls) {
534
+ const durationMs = Date.now() - startedAt;
535
+ return {
536
+ tokens,
537
+ time_seconds: Math.floor(durationMs / 1000),
538
+ tool_rounds: toolRounds,
539
+ tool_calls: toolCalls,
540
+ duration_ms: durationMs,
541
+ };
542
+ }
543
+ function normalizeMaxToolRounds(value) {
544
+ if (value === undefined) {
545
+ return undefined;
546
+ }
547
+ if (!Number.isFinite(value) || value < 0) {
548
+ throw new Error("max_tool_rounds must be a non-negative finite number when provided");
549
+ }
550
+ return Math.floor(value);
551
+ }
552
+ function normalizeModelRetryConfig(config) {
553
+ const maxAttempts = config?.max_attempts === undefined || config.max_attempts <= 0 || !Number.isFinite(config.max_attempts) ? undefined : Math.floor(config.max_attempts);
554
+ return {
555
+ max_attempts: maxAttempts,
556
+ initial_delay_ms: positiveFinite(config?.initial_delay_ms, 1000),
557
+ max_delay_ms: positiveFinite(config?.max_delay_ms, 60_000),
558
+ backoff_factor: Math.max(1, positiveFinite(config?.backoff_factor, 2)),
559
+ jitter_ratio: Math.max(0, Math.min(1, finiteNumber(config?.jitter_ratio, 0.2))),
560
+ request_timeout_ms: positiveFinite(config?.request_timeout_ms, 300_000),
561
+ };
562
+ }
563
+ function positiveFinite(value, fallback) {
564
+ return value !== undefined && Number.isFinite(value) && value >= 0 ? value : fallback;
565
+ }
566
+ function finiteNumber(value, fallback) {
567
+ return value !== undefined && Number.isFinite(value) ? value : fallback;
568
+ }
569
+ function hasRemainingRetryAttempt(attempt, maxAttempts) {
570
+ return maxAttempts === undefined || attempt < maxAttempts;
571
+ }
572
+ function retryDelayMs(attempt, config) {
573
+ const base = Math.min(config.max_delay_ms, config.initial_delay_ms * config.backoff_factor ** Math.max(0, attempt - 1));
574
+ if (base <= 0 || config.jitter_ratio <= 0) {
575
+ return Math.round(base);
576
+ }
577
+ const jitter = base * config.jitter_ratio * Math.random();
578
+ return Math.round(Math.min(config.max_delay_ms, base + jitter));
579
+ }
580
+ function isRetryableModelError(error) {
581
+ if (isAbortError(error)) {
582
+ return false;
583
+ }
584
+ const message = errorMessage(error);
585
+ const status = modelErrorStatus(message);
586
+ if (status !== undefined) {
587
+ return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
588
+ }
589
+ return /fetch failed|network|socket|timeout|timed out|ECONNRESET|ECONNREFUSED|EPIPE|ETIMEDOUT|UND_ERR|terminated/i.test(message);
590
+ }
591
+ function modelErrorStatus(message) {
592
+ const match = message.match(/\b(?:request failed|failed)\s+(\d{3})\b/i);
593
+ if (!match?.[1]) {
594
+ return undefined;
595
+ }
596
+ const status = Number(match[1]);
597
+ return Number.isFinite(status) ? status : undefined;
598
+ }
599
+ function errorMessage(error) {
600
+ const message = error instanceof Error ? error.message : String(error);
601
+ return message.length > 600 ? `${message.slice(0, 597)}...` : message;
602
+ }
603
+ function modelRequestTimeoutError(timeoutMs) {
604
+ const error = new Error(`Model request timed out after ${timeoutMs}ms`);
605
+ error.name = "ModelRequestTimeoutError";
606
+ return error;
607
+ }
608
+ function renderGoalReportBlock(completion) {
609
+ return `\n\n${grayText(`Goal: ${completion.objective}\n${completion.report}`)}`;
610
+ }
611
+ function appendGoalReport(content, reportBlock) {
612
+ const trimmed = content.trimEnd();
613
+ return trimmed ? `${trimmed}${reportBlock}` : reportBlock.trimStart();
614
+ }
615
+ function grayText(text) {
616
+ return `\x1b[38;5;244m${text}\x1b[0m`;
617
+ }
618
+ export function modelMessagesForDisplay(messages) {
619
+ return messages.map((message) => `${message.role}: ${typeof message.content === "string" ? message.content : JSON.stringify(message.content)}`).join("\n");
620
+ }
621
+ function summarizeToolStart(name, args) {
622
+ switch (name) {
623
+ case "run_command":
624
+ return startSummary("Running", stringField(args.command));
625
+ case "run_experiment":
626
+ return "Running autoresearch benchmark";
627
+ case "clarify":
628
+ return startSummary("Waiting for your answer", stringField(args.question));
629
+ case "file_search":
630
+ return startSummary("Searching", stringField(args.query));
631
+ case "glob":
632
+ return startSummary("Scanning", stringField(args.pattern));
633
+ case "goal":
634
+ return summarizeGoalStart(args);
635
+ case "plan":
636
+ return startSummary("Updating plan", stringField(args.op));
637
+ case "init_experiment":
638
+ return startSummary("Initializing experiment", stringField(args.name));
639
+ case "list_dir":
640
+ return startSummary("Listing", stringField(args.path) ?? ".");
641
+ case "log_experiment":
642
+ return startSummary("Logging experiment", stringField(args.status));
643
+ case "read_file":
644
+ case "read_resource":
645
+ return startSummary("Reading", stringField(args.path) ?? stringField(args.uri));
646
+ case "write_file":
647
+ return startSummary("Writing", stringField(args.path));
648
+ case "edit_file":
649
+ case "ast_edit":
650
+ return startSummary("Editing", stringField(args.path));
651
+ case "apply_patch":
652
+ return "Applying patch";
653
+ case "git_status":
654
+ return "Checking git status";
655
+ case "git_diff":
656
+ return startSummary("Reading diff", stringField(args.path));
657
+ case "git_show":
658
+ return startSummary("Reading revision", stringField(args.rev) ?? stringField(args.revision));
659
+ case "todo_write":
660
+ return "Updating todo list";
661
+ case "update_notes":
662
+ return "Updating autoresearch notes";
663
+ case "skill_list":
664
+ return "Listing skills";
665
+ case "skill_read":
666
+ return startSummary("Reading skill", stringField(args.skill) ?? stringField(args.id));
667
+ case "skill_enable":
668
+ case "skill_disable":
669
+ return startSummary("Updating skill", stringField(args.skill) ?? stringField(args.id));
670
+ case "web_search":
671
+ return startSummary("Searching web", stringField(args.query));
672
+ case "web_fetch":
673
+ return startSummary("Fetching", stringField(args.url));
674
+ case "web_open":
675
+ return startSummary("Opening", stringField(args.url));
676
+ default:
677
+ if (name.includes("image") || name.includes("vision") || name.includes("video") || name.includes("audio")) {
678
+ return "Calling Omni endpoint";
679
+ }
680
+ return startSummary("Running tool", name);
681
+ }
682
+ }
683
+ function startSummary(verb, detail) {
684
+ return detail ? `${verb} ${detail}` : verb;
685
+ }
686
+ function summarizeGoalStart(args) {
687
+ switch (stringField(args.op)) {
688
+ case "create":
689
+ return "Starting goal";
690
+ case "decompose":
691
+ case "update_plan":
692
+ return "Planning goal";
693
+ case "update_step":
694
+ return "Updating goal step";
695
+ case "complete":
696
+ return "Completing goal";
697
+ case "drop":
698
+ return "Dropping goal";
699
+ case "resume":
700
+ return "Resuming goal";
701
+ case "get":
702
+ return "Reading goal";
703
+ default:
704
+ return "Updating goal";
705
+ }
706
+ }
707
+ function directHttpUrls(text) {
708
+ const seen = new Set();
709
+ const urls = [];
710
+ for (const match of text.matchAll(/https?:\/\/[^\s<>"'`)\]]+/gi)) {
711
+ const raw = match[0]?.replace(/[.,;:!?。,、;:!?]+$/g, "");
712
+ if (!raw || seen.has(raw)) {
713
+ continue;
714
+ }
715
+ try {
716
+ const parsed = new URL(raw);
717
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
718
+ continue;
719
+ }
720
+ const value = parsed.toString();
721
+ seen.add(value);
722
+ urls.push(value);
723
+ }
724
+ catch {
725
+ continue;
726
+ }
727
+ }
728
+ return urls;
729
+ }
730
+ function mergeEndpointEvidence(snapshot, response) {
731
+ return {
732
+ ...snapshot,
733
+ ...response,
734
+ headers: {
735
+ ...objectField(snapshot.headers),
736
+ ...objectField(response.headers),
737
+ },
738
+ errors: mergeStringArrays(snapshot.errors, response.errors),
739
+ models: snapshot.models ?? response.models,
740
+ load: snapshot.load ?? response.load,
741
+ cache_metrics: snapshot.cache_metrics ?? response.cache_metrics,
742
+ };
743
+ }
744
+ function objectField(value) {
745
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
746
+ }
747
+ function mergeStringArrays(a, b) {
748
+ const out = [...stringArray(a), ...stringArray(b)];
749
+ return out.length ? out : undefined;
750
+ }
751
+ function stringArray(value) {
752
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
753
+ }
754
+ function stringField(value) {
755
+ return typeof value === "string" && value.length > 0 ? value : undefined;
756
+ }
757
+ //# sourceMappingURL=runtime.js.map