@agntk/agent-harness 0.1.1

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 (212) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +41 -0
  3. package/README.md +445 -0
  4. package/defaults/agents/summarizer.md +49 -0
  5. package/defaults/instincts/lead-with-answer.md +24 -0
  6. package/defaults/instincts/qualify-before-recommending.md +40 -0
  7. package/defaults/instincts/read-before-edit.md +23 -0
  8. package/defaults/instincts/search-before-create.md +23 -0
  9. package/defaults/playbooks/ship-feature.md +31 -0
  10. package/defaults/rules/ask-before-assuming.md +35 -0
  11. package/defaults/rules/operations.md +35 -0
  12. package/defaults/rules/respect-the-user.md +39 -0
  13. package/defaults/skills/business-analyst.md +181 -0
  14. package/defaults/skills/content-marketer.md +184 -0
  15. package/defaults/skills/research.md +34 -0
  16. package/defaults/tools/example-web-search.md +60 -0
  17. package/defaults/workflows/daily-reflection.md +54 -0
  18. package/dist/agent-framework-K4GUIICH.js +344 -0
  19. package/dist/agent-framework-K4GUIICH.js.map +1 -0
  20. package/dist/analytics-RPT73WNM.js +12 -0
  21. package/dist/analytics-RPT73WNM.js.map +1 -0
  22. package/dist/auto-processor-OLE45UI3.js +13 -0
  23. package/dist/auto-processor-OLE45UI3.js.map +1 -0
  24. package/dist/chunk-274RV3YO.js +162 -0
  25. package/dist/chunk-274RV3YO.js.map +1 -0
  26. package/dist/chunk-4CWAGBNS.js +168 -0
  27. package/dist/chunk-4CWAGBNS.js.map +1 -0
  28. package/dist/chunk-4FDUOGSZ.js +69 -0
  29. package/dist/chunk-4FDUOGSZ.js.map +1 -0
  30. package/dist/chunk-5H34JPMB.js +199 -0
  31. package/dist/chunk-5H34JPMB.js.map +1 -0
  32. package/dist/chunk-6EMOEYGU.js +102 -0
  33. package/dist/chunk-6EMOEYGU.js.map +1 -0
  34. package/dist/chunk-A7BJPQQ6.js +236 -0
  35. package/dist/chunk-A7BJPQQ6.js.map +1 -0
  36. package/dist/chunk-AGAAFJEO.js +76 -0
  37. package/dist/chunk-AGAAFJEO.js.map +1 -0
  38. package/dist/chunk-BSKDOFRT.js +65 -0
  39. package/dist/chunk-BSKDOFRT.js.map +1 -0
  40. package/dist/chunk-CHJ5GNZC.js +100 -0
  41. package/dist/chunk-CHJ5GNZC.js.map +1 -0
  42. package/dist/chunk-CSL3ERUI.js +307 -0
  43. package/dist/chunk-CSL3ERUI.js.map +1 -0
  44. package/dist/chunk-DA7IKHC4.js +229 -0
  45. package/dist/chunk-DA7IKHC4.js.map +1 -0
  46. package/dist/chunk-DGUM43GV.js +11 -0
  47. package/dist/chunk-DGUM43GV.js.map +1 -0
  48. package/dist/chunk-DTTXPHFW.js +211 -0
  49. package/dist/chunk-DTTXPHFW.js.map +1 -0
  50. package/dist/chunk-FD55B3IO.js +204 -0
  51. package/dist/chunk-FD55B3IO.js.map +1 -0
  52. package/dist/chunk-FLZU44SV.js +230 -0
  53. package/dist/chunk-FLZU44SV.js.map +1 -0
  54. package/dist/chunk-GJNNR2RA.js +200 -0
  55. package/dist/chunk-GJNNR2RA.js.map +1 -0
  56. package/dist/chunk-GNUSHD2Y.js +111 -0
  57. package/dist/chunk-GNUSHD2Y.js.map +1 -0
  58. package/dist/chunk-GUJTBGVS.js +2212 -0
  59. package/dist/chunk-GUJTBGVS.js.map +1 -0
  60. package/dist/chunk-IZ6UZ3ZL.js +207 -0
  61. package/dist/chunk-IZ6UZ3ZL.js.map +1 -0
  62. package/dist/chunk-JKMGYWXB.js +197 -0
  63. package/dist/chunk-JKMGYWXB.js.map +1 -0
  64. package/dist/chunk-KFX54TQM.js +165 -0
  65. package/dist/chunk-KFX54TQM.js.map +1 -0
  66. package/dist/chunk-M7NXUK55.js +199 -0
  67. package/dist/chunk-M7NXUK55.js.map +1 -0
  68. package/dist/chunk-MPZ3BPUI.js +374 -0
  69. package/dist/chunk-MPZ3BPUI.js.map +1 -0
  70. package/dist/chunk-OC6YSTDX.js +119 -0
  71. package/dist/chunk-OC6YSTDX.js.map +1 -0
  72. package/dist/chunk-RC6MEZB6.js +469 -0
  73. package/dist/chunk-RC6MEZB6.js.map +1 -0
  74. package/dist/chunk-RY3ZFII7.js +3440 -0
  75. package/dist/chunk-RY3ZFII7.js.map +1 -0
  76. package/dist/chunk-TAT6JU3X.js +167 -0
  77. package/dist/chunk-TAT6JU3X.js.map +1 -0
  78. package/dist/chunk-UDZIS2AQ.js +79 -0
  79. package/dist/chunk-UDZIS2AQ.js.map +1 -0
  80. package/dist/chunk-UPLBF4RZ.js +115 -0
  81. package/dist/chunk-UPLBF4RZ.js.map +1 -0
  82. package/dist/chunk-UWQTZMNI.js +154 -0
  83. package/dist/chunk-UWQTZMNI.js.map +1 -0
  84. package/dist/chunk-W4T7PGI2.js +346 -0
  85. package/dist/chunk-W4T7PGI2.js.map +1 -0
  86. package/dist/chunk-XTBKL5BI.js +111 -0
  87. package/dist/chunk-XTBKL5BI.js.map +1 -0
  88. package/dist/chunk-YIJY5DBV.js +399 -0
  89. package/dist/chunk-YIJY5DBV.js.map +1 -0
  90. package/dist/chunk-YUFNYN2H.js +242 -0
  91. package/dist/chunk-YUFNYN2H.js.map +1 -0
  92. package/dist/chunk-Z2PUCXTZ.js +94 -0
  93. package/dist/chunk-Z2PUCXTZ.js.map +1 -0
  94. package/dist/chunk-ZZJOFKAT.js +13 -0
  95. package/dist/chunk-ZZJOFKAT.js.map +1 -0
  96. package/dist/cli/index.js +3661 -0
  97. package/dist/cli/index.js.map +1 -0
  98. package/dist/config-WVMRUOCA.js +13 -0
  99. package/dist/config-WVMRUOCA.js.map +1 -0
  100. package/dist/context-loader-3ORBPMHJ.js +13 -0
  101. package/dist/context-loader-3ORBPMHJ.js.map +1 -0
  102. package/dist/conversation-QDEIDQPH.js +22 -0
  103. package/dist/conversation-QDEIDQPH.js.map +1 -0
  104. package/dist/cost-tracker-RS3W7SVY.js +24 -0
  105. package/dist/cost-tracker-RS3W7SVY.js.map +1 -0
  106. package/dist/delegate-VJCJLYEK.js +29 -0
  107. package/dist/delegate-VJCJLYEK.js.map +1 -0
  108. package/dist/emotional-state-VQVRA6ED.js +206 -0
  109. package/dist/emotional-state-VQVRA6ED.js.map +1 -0
  110. package/dist/env-discovery-2BLVMAIM.js +251 -0
  111. package/dist/env-discovery-2BLVMAIM.js.map +1 -0
  112. package/dist/export-6GCYHEHQ.js +165 -0
  113. package/dist/export-6GCYHEHQ.js.map +1 -0
  114. package/dist/graph-YUIPOSOO.js +14 -0
  115. package/dist/graph-YUIPOSOO.js.map +1 -0
  116. package/dist/harness-LCHA3DWP.js +10 -0
  117. package/dist/harness-LCHA3DWP.js.map +1 -0
  118. package/dist/harness-WE4SLCML.js +26 -0
  119. package/dist/harness-WE4SLCML.js.map +1 -0
  120. package/dist/health-NZ6WNIMV.js +23 -0
  121. package/dist/health-NZ6WNIMV.js.map +1 -0
  122. package/dist/index.d.ts +3612 -0
  123. package/dist/index.js +13501 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/indexer-LONANRRM.js +16 -0
  126. package/dist/indexer-LONANRRM.js.map +1 -0
  127. package/dist/instinct-learner-SRM72DHF.js +20 -0
  128. package/dist/instinct-learner-SRM72DHF.js.map +1 -0
  129. package/dist/intake-4M3HNU43.js +21 -0
  130. package/dist/intake-4M3HNU43.js.map +1 -0
  131. package/dist/intelligence-HJOCA4SJ.js +1081 -0
  132. package/dist/intelligence-HJOCA4SJ.js.map +1 -0
  133. package/dist/journal-WANJL3MI.js +24 -0
  134. package/dist/journal-WANJL3MI.js.map +1 -0
  135. package/dist/loader-C3TKIKZR.js +23 -0
  136. package/dist/loader-C3TKIKZR.js.map +1 -0
  137. package/dist/mcp-WTQJJZAO.js +15 -0
  138. package/dist/mcp-WTQJJZAO.js.map +1 -0
  139. package/dist/mcp-discovery-WPAQFL6S.js +377 -0
  140. package/dist/mcp-discovery-WPAQFL6S.js.map +1 -0
  141. package/dist/mcp-installer-6O2XXD3V.js +394 -0
  142. package/dist/mcp-installer-6O2XXD3V.js.map +1 -0
  143. package/dist/metrics-KXGNFAAB.js +20 -0
  144. package/dist/metrics-KXGNFAAB.js.map +1 -0
  145. package/dist/primitive-registry-I6VTIR4W.js +512 -0
  146. package/dist/primitive-registry-I6VTIR4W.js.map +1 -0
  147. package/dist/project-discovery-C4UMD7JI.js +246 -0
  148. package/dist/project-discovery-C4UMD7JI.js.map +1 -0
  149. package/dist/provider-LQHQX7Z7.js +26 -0
  150. package/dist/provider-LQHQX7Z7.js.map +1 -0
  151. package/dist/provider-SXPQZ74H.js +28 -0
  152. package/dist/provider-SXPQZ74H.js.map +1 -0
  153. package/dist/rate-limiter-RLRVM325.js +22 -0
  154. package/dist/rate-limiter-RLRVM325.js.map +1 -0
  155. package/dist/rule-engine-YGQ3RYZM.js +182 -0
  156. package/dist/rule-engine-YGQ3RYZM.js.map +1 -0
  157. package/dist/scaffold-A3VRRCBV.js +347 -0
  158. package/dist/scaffold-A3VRRCBV.js.map +1 -0
  159. package/dist/scheduler-XHHIVHRI.js +397 -0
  160. package/dist/scheduler-XHHIVHRI.js.map +1 -0
  161. package/dist/search-V3W5JMJG.js +75 -0
  162. package/dist/search-V3W5JMJG.js.map +1 -0
  163. package/dist/semantic-search-2DTOO5UX.js +241 -0
  164. package/dist/semantic-search-2DTOO5UX.js.map +1 -0
  165. package/dist/serve-DTQ3HENY.js +291 -0
  166. package/dist/serve-DTQ3HENY.js.map +1 -0
  167. package/dist/sessions-CZGVXKQE.js +21 -0
  168. package/dist/sessions-CZGVXKQE.js.map +1 -0
  169. package/dist/sources-RW5DT56F.js +32 -0
  170. package/dist/sources-RW5DT56F.js.map +1 -0
  171. package/dist/starter-packs-76YUVHEU.js +893 -0
  172. package/dist/starter-packs-76YUVHEU.js.map +1 -0
  173. package/dist/state-GMXILIHW.js +13 -0
  174. package/dist/state-GMXILIHW.js.map +1 -0
  175. package/dist/state-merge-NKO5FRBA.js +174 -0
  176. package/dist/state-merge-NKO5FRBA.js.map +1 -0
  177. package/dist/telemetry-UC6PBXC7.js +22 -0
  178. package/dist/telemetry-UC6PBXC7.js.map +1 -0
  179. package/dist/tool-executor-MJ7IG7PQ.js +28 -0
  180. package/dist/tool-executor-MJ7IG7PQ.js.map +1 -0
  181. package/dist/tools-DZ4KETET.js +20 -0
  182. package/dist/tools-DZ4KETET.js.map +1 -0
  183. package/dist/types-EW7AIB3R.js +18 -0
  184. package/dist/types-EW7AIB3R.js.map +1 -0
  185. package/dist/types-WGDLSPO6.js +16 -0
  186. package/dist/types-WGDLSPO6.js.map +1 -0
  187. package/dist/universal-installer-QGS4SJGX.js +578 -0
  188. package/dist/universal-installer-QGS4SJGX.js.map +1 -0
  189. package/dist/validator-7WXMDIHH.js +22 -0
  190. package/dist/validator-7WXMDIHH.js.map +1 -0
  191. package/dist/verification-gate-FYXUX6LH.js +246 -0
  192. package/dist/verification-gate-FYXUX6LH.js.map +1 -0
  193. package/dist/versioning-Z3XNE2Q2.js +271 -0
  194. package/dist/versioning-Z3XNE2Q2.js.map +1 -0
  195. package/dist/watcher-ISJC7YKL.js +109 -0
  196. package/dist/watcher-ISJC7YKL.js.map +1 -0
  197. package/dist/web-server-DD7ZOP46.js +28 -0
  198. package/dist/web-server-DD7ZOP46.js.map +1 -0
  199. package/package.json +76 -0
  200. package/sources.yaml +121 -0
  201. package/templates/assistant/CORE.md +24 -0
  202. package/templates/assistant/SYSTEM.md +24 -0
  203. package/templates/assistant/config.yaml +51 -0
  204. package/templates/base/CORE.md +17 -0
  205. package/templates/base/SYSTEM.md +24 -0
  206. package/templates/base/config.yaml +51 -0
  207. package/templates/claude-opus/config.yaml +51 -0
  208. package/templates/code-reviewer/CORE.md +25 -0
  209. package/templates/code-reviewer/SYSTEM.md +30 -0
  210. package/templates/code-reviewer/config.yaml +51 -0
  211. package/templates/gpt4/config.yaml +51 -0
  212. package/templates/local/config.yaml +51 -0
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ checkGuardrails
5
+ } from "./chunk-AGAAFJEO.js";
6
+ import {
7
+ createMcpManager
8
+ } from "./chunk-5H34JPMB.js";
9
+ import {
10
+ buildToolSet
11
+ } from "./chunk-DA7IKHC4.js";
12
+ import {
13
+ buildSystemPrompt
14
+ } from "./chunk-UWQTZMNI.js";
15
+ import {
16
+ loadState,
17
+ saveState
18
+ } from "./chunk-UDZIS2AQ.js";
19
+ import {
20
+ createSessionId,
21
+ writeSession
22
+ } from "./chunk-DTTXPHFW.js";
23
+ import {
24
+ recordBoot,
25
+ recordFailure,
26
+ recordSuccess
27
+ } from "./chunk-TAT6JU3X.js";
28
+ import {
29
+ recordCost
30
+ } from "./chunk-JKMGYWXB.js";
31
+ import {
32
+ log
33
+ } from "./chunk-BSKDOFRT.js";
34
+ import {
35
+ generate,
36
+ getModel,
37
+ streamGenerateWithDetails
38
+ } from "./chunk-IZ6UZ3ZL.js";
39
+ import {
40
+ loadConfig
41
+ } from "./chunk-CHJ5GNZC.js";
42
+
43
+ // src/core/harness.ts
44
+ import { existsSync } from "fs";
45
+ import { resolve } from "path";
46
+ function createHarness(options) {
47
+ const dir = resolve(options.dir);
48
+ if (!existsSync(dir)) {
49
+ throw new Error(`Harness directory not found: ${dir}`);
50
+ }
51
+ const config = loadConfig(dir, options.config);
52
+ if (options.model) {
53
+ config.model = { ...config.model, id: options.model };
54
+ }
55
+ if (options.provider) {
56
+ config.model = { ...config.model, provider: options.provider };
57
+ }
58
+ const model = getModel(config, options.apiKey);
59
+ const hooks = options.hooks ?? {};
60
+ let state;
61
+ let systemPrompt;
62
+ let booted = false;
63
+ let toolSet = {};
64
+ let mcpManager;
65
+ const agent = {
66
+ name: config.agent.name,
67
+ config,
68
+ async boot() {
69
+ state = loadState(dir);
70
+ const previousMode = state.mode;
71
+ state.mode = "active";
72
+ state.last_interaction = (/* @__PURE__ */ new Date()).toISOString();
73
+ const ctx = buildSystemPrompt(dir, config);
74
+ systemPrompt = ctx.systemPrompt;
75
+ let mcpTools = {};
76
+ mcpManager = createMcpManager(config);
77
+ if (mcpManager.hasServers()) {
78
+ try {
79
+ await mcpManager.connect();
80
+ mcpTools = mcpManager.getTools();
81
+ } catch (err) {
82
+ log.warn(`MCP connection failed during boot: ${err instanceof Error ? err.message : String(err)}. Continuing without MCP tools.`);
83
+ }
84
+ }
85
+ toolSet = buildToolSet(dir, options.toolExecutor, mcpTools);
86
+ const toolCount = Object.keys(toolSet).length;
87
+ booted = true;
88
+ log.info(
89
+ `Booted "${config.agent.name}" | ${ctx.budget.loaded_files.length} files loaded | ~${ctx.budget.used_tokens} tokens used | ${ctx.budget.remaining} remaining` + (toolCount > 0 ? ` | ${toolCount} tools` : "")
90
+ );
91
+ for (const warning of ctx.warnings) {
92
+ log.warn(warning);
93
+ }
94
+ if (previousMode !== "active" && hooks.onStateChange) {
95
+ await hooks.onStateChange({ agent, previous: previousMode, current: "active" });
96
+ }
97
+ try {
98
+ recordBoot(dir);
99
+ } catch {
100
+ }
101
+ if (hooks.onBoot) {
102
+ await hooks.onBoot({ agent, config, state });
103
+ }
104
+ },
105
+ async run(prompt) {
106
+ if (!booted) await agent.boot();
107
+ const guard = checkGuardrails(dir, config);
108
+ if (!guard.allowed) {
109
+ const error = new Error(`Guardrail blocked: ${guard.reason}`);
110
+ try {
111
+ recordFailure(dir, error.message);
112
+ } catch {
113
+ }
114
+ if (hooks.onError) {
115
+ try {
116
+ await hooks.onError({ agent, error, prompt });
117
+ } catch {
118
+ }
119
+ }
120
+ throw error;
121
+ }
122
+ const sessionId = createSessionId();
123
+ const started = (/* @__PURE__ */ new Date()).toISOString();
124
+ const hasTools = Object.keys(toolSet).length > 0;
125
+ let result;
126
+ try {
127
+ result = await generate({
128
+ model,
129
+ system: systemPrompt,
130
+ prompt,
131
+ maxRetries: config.model.max_retries,
132
+ timeoutMs: config.model.timeout_ms,
133
+ ...hasTools ? { tools: toolSet, maxToolSteps: options.toolExecutor?.maxToolCalls ?? 5 } : {}
134
+ });
135
+ } catch (err) {
136
+ const error = err instanceof Error ? err : new Error(String(err));
137
+ try {
138
+ recordFailure(dir, error.message);
139
+ } catch {
140
+ }
141
+ if (hooks.onError) {
142
+ try {
143
+ await hooks.onError({ agent, error, prompt });
144
+ } catch {
145
+ }
146
+ }
147
+ throw error;
148
+ }
149
+ const ended = (/* @__PURE__ */ new Date()).toISOString();
150
+ const session = {
151
+ id: sessionId,
152
+ started,
153
+ ended,
154
+ prompt,
155
+ summary: result.text.slice(0, 200),
156
+ tokens_used: result.usage.totalTokens,
157
+ steps: result.steps,
158
+ model_id: config.model.id,
159
+ tool_calls: result.toolCalls.length > 0 ? result.toolCalls : void 0
160
+ };
161
+ try {
162
+ writeSession(dir, session);
163
+ } catch (err) {
164
+ log.warn(`Failed to write session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
165
+ }
166
+ try {
167
+ recordCost(dir, {
168
+ model_id: config.model.id,
169
+ provider: config.model.provider ?? "openrouter",
170
+ input_tokens: result.usage.inputTokens,
171
+ output_tokens: result.usage.outputTokens,
172
+ source: `run:${sessionId}`
173
+ });
174
+ } catch (err) {
175
+ log.warn(`Failed to record cost: ${err instanceof Error ? err.message : String(err)}`);
176
+ }
177
+ try {
178
+ recordSuccess(dir);
179
+ } catch (err) {
180
+ log.warn(`Failed to record health: ${err instanceof Error ? err.message : String(err)}`);
181
+ }
182
+ try {
183
+ state.last_interaction = ended;
184
+ saveState(dir, state);
185
+ } catch (err) {
186
+ log.warn(`Failed to save state: ${err instanceof Error ? err.message : String(err)}`);
187
+ }
188
+ const runResult = {
189
+ text: result.text,
190
+ usage: result.usage,
191
+ session_id: sessionId,
192
+ steps: result.steps,
193
+ toolCalls: result.toolCalls
194
+ };
195
+ if (hooks.onSessionEnd) {
196
+ try {
197
+ await hooks.onSessionEnd({ agent, sessionId, prompt, result: runResult });
198
+ } catch (err) {
199
+ log.warn(`onSessionEnd hook error: ${err instanceof Error ? err.message : String(err)}`);
200
+ }
201
+ }
202
+ return runResult;
203
+ },
204
+ stream(prompt) {
205
+ const sessionId = createSessionId();
206
+ let resolveResult;
207
+ let rejectResult;
208
+ const resultPromise = new Promise((res, rej) => {
209
+ resolveResult = res;
210
+ rejectResult = rej;
211
+ });
212
+ resultPromise.catch(() => {
213
+ });
214
+ async function* generateStream() {
215
+ if (!booted) await agent.boot();
216
+ const guard = checkGuardrails(dir, config);
217
+ if (!guard.allowed) {
218
+ const error = new Error(`Guardrail blocked: ${guard.reason}`);
219
+ try {
220
+ recordFailure(dir, error.message);
221
+ } catch {
222
+ }
223
+ if (hooks.onError) {
224
+ try {
225
+ await hooks.onError({ agent, error, prompt });
226
+ } catch {
227
+ }
228
+ }
229
+ rejectResult(error);
230
+ throw error;
231
+ }
232
+ const started = (/* @__PURE__ */ new Date()).toISOString();
233
+ let fullText = "";
234
+ const hasTools = Object.keys(toolSet).length > 0;
235
+ let streamResult;
236
+ try {
237
+ streamResult = streamGenerateWithDetails({
238
+ model,
239
+ system: systemPrompt,
240
+ prompt,
241
+ maxRetries: config.model.max_retries,
242
+ timeoutMs: config.model.timeout_ms,
243
+ ...hasTools ? { tools: toolSet, maxToolSteps: options.toolExecutor?.maxToolCalls ?? 5 } : {}
244
+ });
245
+ } catch (err) {
246
+ const error = err instanceof Error ? err : new Error(String(err));
247
+ try {
248
+ recordFailure(dir, error.message);
249
+ } catch {
250
+ }
251
+ if (hooks.onError) {
252
+ try {
253
+ await hooks.onError({ agent, error, prompt });
254
+ } catch {
255
+ }
256
+ }
257
+ rejectResult(error);
258
+ throw error;
259
+ }
260
+ try {
261
+ for await (const chunk of streamResult.textStream) {
262
+ fullText += chunk;
263
+ yield chunk;
264
+ }
265
+ } catch (err) {
266
+ const error = err instanceof Error ? err : new Error(String(err));
267
+ try {
268
+ recordFailure(dir, error.message);
269
+ } catch {
270
+ }
271
+ if (hooks.onError) {
272
+ try {
273
+ await hooks.onError({ agent, error, prompt });
274
+ } catch {
275
+ }
276
+ }
277
+ rejectResult(error);
278
+ throw error;
279
+ }
280
+ let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
281
+ let steps = 1;
282
+ let toolCalls = [];
283
+ try {
284
+ [usage, steps, toolCalls] = await Promise.all([
285
+ streamResult.usage,
286
+ streamResult.steps,
287
+ streamResult.toolCalls
288
+ ]);
289
+ } catch (err) {
290
+ log.warn(`Failed to resolve post-stream metadata: ${err instanceof Error ? err.message : String(err)}`);
291
+ }
292
+ const ended = (/* @__PURE__ */ new Date()).toISOString();
293
+ const session = {
294
+ id: sessionId,
295
+ started,
296
+ ended,
297
+ prompt,
298
+ summary: fullText.slice(0, 200),
299
+ tokens_used: usage.totalTokens,
300
+ steps,
301
+ model_id: config.model.id,
302
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
303
+ };
304
+ try {
305
+ writeSession(dir, session);
306
+ } catch (err) {
307
+ log.warn(`Failed to write session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
308
+ }
309
+ try {
310
+ recordCost(dir, {
311
+ model_id: config.model.id,
312
+ provider: config.model.provider ?? "openrouter",
313
+ input_tokens: usage.inputTokens,
314
+ output_tokens: usage.outputTokens,
315
+ source: `stream:${sessionId}`
316
+ });
317
+ } catch (err) {
318
+ log.warn(`Failed to record cost: ${err instanceof Error ? err.message : String(err)}`);
319
+ }
320
+ try {
321
+ recordSuccess(dir);
322
+ } catch (err) {
323
+ log.warn(`Failed to record health: ${err instanceof Error ? err.message : String(err)}`);
324
+ }
325
+ try {
326
+ state.last_interaction = ended;
327
+ saveState(dir, state);
328
+ } catch (err) {
329
+ log.warn(`Failed to save state: ${err instanceof Error ? err.message : String(err)}`);
330
+ }
331
+ const runResult = {
332
+ text: fullText,
333
+ usage,
334
+ session_id: sessionId,
335
+ steps,
336
+ toolCalls
337
+ };
338
+ if (hooks.onSessionEnd) {
339
+ try {
340
+ await hooks.onSessionEnd({ agent, sessionId, prompt, result: runResult });
341
+ } catch (err) {
342
+ log.warn(`onSessionEnd hook error: ${err instanceof Error ? err.message : String(err)}`);
343
+ }
344
+ }
345
+ resolveResult(runResult);
346
+ }
347
+ return {
348
+ textStream: generateStream(),
349
+ result: resultPromise
350
+ };
351
+ },
352
+ async shutdown() {
353
+ if (!booted) return;
354
+ if (hooks.onShutdown) {
355
+ try {
356
+ await hooks.onShutdown({ agent, state });
357
+ } catch (err) {
358
+ log.warn(`onShutdown hook error: ${err instanceof Error ? err.message : String(err)}`);
359
+ }
360
+ }
361
+ if (mcpManager) {
362
+ try {
363
+ await mcpManager.close();
364
+ } catch (err) {
365
+ log.warn(`MCP shutdown error: ${err instanceof Error ? err.message : String(err)}`);
366
+ }
367
+ mcpManager = void 0;
368
+ }
369
+ const previousMode = state.mode;
370
+ state.mode = "idle";
371
+ try {
372
+ saveState(dir, state);
373
+ } catch (err) {
374
+ log.warn(`Failed to save state during shutdown: ${err instanceof Error ? err.message : String(err)}`);
375
+ }
376
+ booted = false;
377
+ if (previousMode !== "idle" && hooks.onStateChange) {
378
+ try {
379
+ await hooks.onStateChange({ agent, previous: previousMode, current: "idle" });
380
+ } catch (err) {
381
+ log.warn(`onStateChange hook error: ${err instanceof Error ? err.message : String(err)}`);
382
+ }
383
+ }
384
+ log.info(`Shutdown "${config.agent.name}"`);
385
+ },
386
+ getSystemPrompt() {
387
+ return systemPrompt || "";
388
+ },
389
+ getState() {
390
+ return state || loadState(dir);
391
+ }
392
+ };
393
+ return agent;
394
+ }
395
+
396
+ export {
397
+ createHarness
398
+ };
399
+ //# sourceMappingURL=chunk-YIJY5DBV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/harness.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { resolve } from 'path';\nimport { loadConfig } from './config.js';\nimport { log } from './logger.js';\nimport type {\n CreateHarnessOptions,\n HarnessConfig,\n HarnessAgent,\n HarnessHooks,\n AgentRunResult,\n AgentStreamResult,\n AgentState,\n} from './types.js';\nimport { getModel, generate, streamGenerateWithDetails } from '../llm/provider.js';\nimport { buildSystemPrompt } from '../runtime/context-loader.js';\nimport { loadState, saveState } from '../runtime/state.js';\nimport { createSessionId, writeSession, type SessionRecord } from '../runtime/sessions.js';\nimport { recordCost } from '../runtime/cost-tracker.js';\nimport { recordSuccess, recordFailure, recordBoot } from '../runtime/health.js';\nimport { checkGuardrails } from '../runtime/guardrails.js';\nimport { buildToolSet, type AIToolSet } from '../runtime/tool-executor.js';\nimport { createMcpManager, type McpManager } from '../runtime/mcp.js';\n\nexport function createHarness(options: CreateHarnessOptions): HarnessAgent {\n const dir = resolve(options.dir);\n\n if (!existsSync(dir)) {\n throw new Error(`Harness directory not found: ${dir}`);\n }\n\n const config = loadConfig(dir, options.config);\n\n // Apply model and provider overrides from options\n if (options.model) {\n config.model = { ...config.model, id: options.model };\n }\n if (options.provider) {\n config.model = { ...config.model, provider: options.provider };\n }\n\n const model = getModel(config, options.apiKey);\n const hooks: HarnessHooks = options.hooks ?? {};\n\n let state: AgentState;\n let systemPrompt: string;\n let booted = false;\n let toolSet: AIToolSet = {};\n let mcpManager: McpManager | undefined;\n\n const agent: HarnessAgent = {\n name: config.agent.name,\n config,\n\n async boot() {\n // Load state\n state = loadState(dir);\n const previousMode = state.mode;\n state.mode = 'active';\n state.last_interaction = new Date().toISOString();\n\n // Build system prompt from harness files\n const ctx = buildSystemPrompt(dir, config);\n systemPrompt = ctx.systemPrompt;\n\n // Connect to MCP servers and load their tools\n let mcpTools: AIToolSet = {};\n mcpManager = createMcpManager(config);\n if (mcpManager.hasServers()) {\n try {\n await mcpManager.connect();\n mcpTools = mcpManager.getTools();\n } catch (err) {\n log.warn(`MCP connection failed during boot: ${err instanceof Error ? err.message : String(err)}. Continuing without MCP tools.`);\n }\n }\n\n // Load tools and convert to AI SDK format (includes markdown + programmatic + MCP)\n toolSet = buildToolSet(dir, options.toolExecutor, mcpTools);\n const toolCount = Object.keys(toolSet).length;\n\n booted = true;\n\n log.info(\n `Booted \"${config.agent.name}\" | ` +\n `${ctx.budget.loaded_files.length} files loaded | ` +\n `~${ctx.budget.used_tokens} tokens used | ` +\n `${ctx.budget.remaining} remaining` +\n (toolCount > 0 ? ` | ${toolCount} tools` : ''),\n );\n\n for (const warning of ctx.warnings) {\n log.warn(warning);\n }\n\n // Lifecycle: onStateChange\n if (previousMode !== 'active' && hooks.onStateChange) {\n await hooks.onStateChange({ agent, previous: previousMode, current: 'active' });\n }\n\n // Record boot in health metrics\n try { recordBoot(dir); } catch { /* best-effort */ }\n\n // Lifecycle: onBoot\n if (hooks.onBoot) {\n await hooks.onBoot({ agent, config, state });\n }\n },\n\n async run(prompt: string): Promise<AgentRunResult> {\n if (!booted) await agent.boot();\n\n // Check guardrails (rate limits + budget) before LLM call\n const guard = checkGuardrails(dir, config);\n if (!guard.allowed) {\n const error = new Error(`Guardrail blocked: ${guard.reason}`);\n try { recordFailure(dir, error.message); } catch { /* best-effort */ }\n if (hooks.onError) {\n try { await hooks.onError({ agent, error, prompt }); } catch { /* best-effort */ }\n }\n throw error;\n }\n\n const sessionId = createSessionId();\n const started = new Date().toISOString();\n\n const hasTools = Object.keys(toolSet).length > 0;\n let result;\n try {\n result = await generate({\n model,\n system: systemPrompt,\n prompt,\n maxRetries: config.model.max_retries,\n timeoutMs: config.model.timeout_ms,\n ...(hasTools ? { tools: toolSet, maxToolSteps: options.toolExecutor?.maxToolCalls ?? 5 } : {}),\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n try { recordFailure(dir, error.message); } catch { /* best-effort */ }\n if (hooks.onError) {\n try { await hooks.onError({ agent, error, prompt }); } catch { /* best-effort */ }\n }\n throw error;\n }\n\n const ended = new Date().toISOString();\n\n // Write session record\n const session: SessionRecord = {\n id: sessionId,\n started,\n ended,\n prompt,\n summary: result.text.slice(0, 200),\n tokens_used: result.usage.totalTokens,\n steps: result.steps,\n model_id: config.model.id,\n tool_calls: result.toolCalls.length > 0 ? result.toolCalls : undefined,\n };\n\n // Post-LLM recording — wrapped in try-catch so telemetry failures\n // never mask a successful LLM result\n try {\n writeSession(dir, session);\n } catch (err) {\n log.warn(`Failed to write session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n try {\n recordCost(dir, {\n model_id: config.model.id,\n provider: config.model.provider ?? 'openrouter',\n input_tokens: result.usage.inputTokens,\n output_tokens: result.usage.outputTokens,\n source: `run:${sessionId}`,\n });\n } catch (err) {\n log.warn(`Failed to record cost: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n try {\n recordSuccess(dir);\n } catch (err) {\n log.warn(`Failed to record health: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n try {\n state.last_interaction = ended;\n saveState(dir, state);\n } catch (err) {\n log.warn(`Failed to save state: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const runResult: AgentRunResult = {\n text: result.text,\n usage: result.usage,\n session_id: sessionId,\n steps: result.steps,\n toolCalls: result.toolCalls,\n };\n\n // Lifecycle: onSessionEnd — wrapped so hook errors don't lose the result\n if (hooks.onSessionEnd) {\n try {\n await hooks.onSessionEnd({ agent, sessionId, prompt, result: runResult });\n } catch (err) {\n log.warn(`onSessionEnd hook error: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return runResult;\n },\n\n stream(prompt: string): AgentStreamResult {\n const sessionId = createSessionId();\n\n // Deferred result — resolves after stream is fully consumed and recording completes\n let resolveResult: (r: AgentRunResult) => void;\n let rejectResult: (e: Error) => void;\n const resultPromise = new Promise<AgentRunResult>((res, rej) => {\n resolveResult = res;\n rejectResult = rej;\n });\n // Prevent unhandled rejection when error propagates via the generator throw path\n // and consumer doesn't explicitly await .result\n resultPromise.catch(() => {});\n\n async function* generateStream(): AsyncIterable<string> {\n if (!booted) await agent.boot();\n\n // Check guardrails (rate limits + budget) before LLM call\n const guard = checkGuardrails(dir, config);\n if (!guard.allowed) {\n const error = new Error(`Guardrail blocked: ${guard.reason}`);\n try { recordFailure(dir, error.message); } catch { /* best-effort */ }\n if (hooks.onError) {\n try { await hooks.onError({ agent, error, prompt }); } catch { /* best-effort */ }\n }\n rejectResult(error);\n throw error;\n }\n\n const started = new Date().toISOString();\n let fullText = '';\n\n const hasTools = Object.keys(toolSet).length > 0;\n\n let streamResult;\n try {\n streamResult = streamGenerateWithDetails({\n model,\n system: systemPrompt,\n prompt,\n maxRetries: config.model.max_retries,\n timeoutMs: config.model.timeout_ms,\n ...(hasTools ? { tools: toolSet, maxToolSteps: options.toolExecutor?.maxToolCalls ?? 5 } : {}),\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n try { recordFailure(dir, error.message); } catch { /* best-effort */ }\n if (hooks.onError) {\n try { await hooks.onError({ agent, error, prompt }); } catch { /* best-effort */ }\n }\n rejectResult(error);\n throw error;\n }\n\n try {\n for await (const chunk of streamResult.textStream) {\n fullText += chunk;\n yield chunk;\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n try { recordFailure(dir, error.message); } catch { /* best-effort */ }\n if (hooks.onError) {\n try { await hooks.onError({ agent, error, prompt }); } catch { /* best-effort */ }\n }\n rejectResult(error);\n throw error;\n }\n\n // Await post-stream metadata — wrapped so failures don't crash the generator\n let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\n let steps = 1;\n let toolCalls: Array<{ toolName: string; args: Record<string, unknown>; result: unknown }> = [];\n try {\n [usage, steps, toolCalls] = await Promise.all([\n streamResult.usage,\n streamResult.steps,\n streamResult.toolCalls,\n ]);\n } catch (err) {\n log.warn(`Failed to resolve post-stream metadata: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const ended = new Date().toISOString();\n\n const session: SessionRecord = {\n id: sessionId,\n started,\n ended,\n prompt,\n summary: fullText.slice(0, 200),\n tokens_used: usage.totalTokens,\n steps,\n model_id: config.model.id,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n };\n\n // Post-stream recording — wrapped so telemetry failures don't break the caller\n try {\n writeSession(dir, session);\n } catch (err) {\n log.warn(`Failed to write session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n try {\n recordCost(dir, {\n model_id: config.model.id,\n provider: config.model.provider ?? 'openrouter',\n input_tokens: usage.inputTokens,\n output_tokens: usage.outputTokens,\n source: `stream:${sessionId}`,\n });\n } catch (err) {\n log.warn(`Failed to record cost: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n try {\n recordSuccess(dir);\n } catch (err) {\n log.warn(`Failed to record health: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n try {\n state.last_interaction = ended;\n saveState(dir, state);\n } catch (err) {\n log.warn(`Failed to save state: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const runResult: AgentRunResult = {\n text: fullText,\n usage,\n session_id: sessionId,\n steps,\n toolCalls,\n };\n\n // Lifecycle: onSessionEnd\n if (hooks.onSessionEnd) {\n try {\n await hooks.onSessionEnd({ agent, sessionId, prompt, result: runResult });\n } catch (err) {\n log.warn(`onSessionEnd hook error: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n resolveResult(runResult);\n }\n\n return {\n textStream: generateStream(),\n result: resultPromise,\n };\n },\n\n async shutdown() {\n if (!booted) return;\n\n // Lifecycle: onShutdown — wrapped so hook errors don't prevent cleanup\n if (hooks.onShutdown) {\n try {\n await hooks.onShutdown({ agent, state });\n } catch (err) {\n log.warn(`onShutdown hook error: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Close MCP server connections\n if (mcpManager) {\n try {\n await mcpManager.close();\n } catch (err) {\n log.warn(`MCP shutdown error: ${err instanceof Error ? err.message : String(err)}`);\n }\n mcpManager = undefined;\n }\n\n const previousMode = state.mode;\n state.mode = 'idle';\n try {\n saveState(dir, state);\n } catch (err) {\n log.warn(`Failed to save state during shutdown: ${err instanceof Error ? err.message : String(err)}`);\n }\n booted = false;\n\n // Lifecycle: onStateChange\n if (previousMode !== 'idle' && hooks.onStateChange) {\n try {\n await hooks.onStateChange({ agent, previous: previousMode, current: 'idle' });\n } catch (err) {\n log.warn(`onStateChange hook error: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n log.info(`Shutdown \"${config.agent.name}\"`);\n },\n\n getSystemPrompt() {\n return systemPrompt || '';\n },\n\n getState() {\n return state || loadState(dir);\n },\n };\n\n return agent;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ buildToolSet
5
+ } from "./chunk-DA7IKHC4.js";
6
+ import {
7
+ createSessionId,
8
+ writeSession
9
+ } from "./chunk-DTTXPHFW.js";
10
+ import {
11
+ estimateTokens,
12
+ getAtLevel,
13
+ loadDirectory
14
+ } from "./chunk-UPLBF4RZ.js";
15
+ import {
16
+ log
17
+ } from "./chunk-BSKDOFRT.js";
18
+ import {
19
+ generate,
20
+ getModel,
21
+ streamGenerateWithDetails
22
+ } from "./chunk-IZ6UZ3ZL.js";
23
+ import {
24
+ loadConfig
25
+ } from "./chunk-CHJ5GNZC.js";
26
+
27
+ // src/runtime/delegate.ts
28
+ import { existsSync, readFileSync } from "fs";
29
+ import { join } from "path";
30
+ function loadAgentDocs(harnessDir) {
31
+ return loadDirectory(join(harnessDir, "agents"));
32
+ }
33
+ function findAgent(harnessDir, agentId) {
34
+ const agents = loadAgentDocs(harnessDir);
35
+ const byId = agents.find((a) => a.frontmatter.id === agentId);
36
+ if (byId) return byId;
37
+ const prefixed = agents.find((a) => a.frontmatter.id === `agent-${agentId}`);
38
+ if (prefixed) return prefixed;
39
+ const byFilename = agents.find((a) => {
40
+ const filename = a.path.split("/").pop()?.replace(".md", "") || "";
41
+ return filename === agentId || filename === `agent-${agentId}`;
42
+ });
43
+ return byFilename;
44
+ }
45
+ function listAgents(harnessDir) {
46
+ return loadAgentDocs(harnessDir).map((doc) => ({
47
+ id: doc.frontmatter.id,
48
+ l0: doc.l0,
49
+ l1: doc.l1,
50
+ path: doc.path,
51
+ tags: doc.frontmatter.tags,
52
+ status: doc.frontmatter.status
53
+ }));
54
+ }
55
+ function buildAgentPrompt(harnessDir, agentDoc, config) {
56
+ const sections = [];
57
+ const maxTokens = config.model.max_tokens;
58
+ const targetBudget = maxTokens * 0.1;
59
+ let usedTokens = 0;
60
+ const agentBody = agentDoc.body;
61
+ sections.push(`# AGENT: ${agentDoc.frontmatter.id}
62
+
63
+ ${agentBody}`);
64
+ usedTokens += estimateTokens(agentBody);
65
+ const corePath = join(harnessDir, "CORE.md");
66
+ if (existsSync(corePath)) {
67
+ const core = readFileSync(corePath, "utf-8");
68
+ const coreTokens = estimateTokens(core);
69
+ if (usedTokens + coreTokens <= targetBudget) {
70
+ sections.push(`# PRIMARY AGENT CONTEXT
71
+
72
+ ${core}`);
73
+ usedTokens += coreTokens;
74
+ }
75
+ }
76
+ const rulesDir = join(harnessDir, "rules");
77
+ if (existsSync(rulesDir)) {
78
+ const rules = loadDirectory(rulesDir);
79
+ if (rules.length > 0) {
80
+ const ruleDocs = [];
81
+ for (const rule of rules) {
82
+ const remaining = targetBudget - usedTokens;
83
+ if (remaining < 50) break;
84
+ let level = 1;
85
+ let content = getAtLevel(rule, level);
86
+ let tokens = estimateTokens(content);
87
+ if (usedTokens + tokens > targetBudget) {
88
+ level = 0;
89
+ content = getAtLevel(rule, 0);
90
+ tokens = estimateTokens(content);
91
+ }
92
+ if (usedTokens + tokens <= targetBudget) {
93
+ ruleDocs.push(`### ${rule.frontmatter.id}
94
+ ${content}`);
95
+ usedTokens += tokens;
96
+ }
97
+ }
98
+ if (ruleDocs.length > 0) {
99
+ sections.push(`# RULES
100
+
101
+ ${ruleDocs.join("\n\n")}`);
102
+ }
103
+ }
104
+ }
105
+ return sections.join("\n\n---\n\n");
106
+ }
107
+ function prepareDelegation(opts) {
108
+ const { harnessDir, agentId, apiKey } = opts;
109
+ if (!agentId || !agentId.trim()) {
110
+ throw new Error("agentId is required");
111
+ }
112
+ if (!opts.prompt || !opts.prompt.trim()) {
113
+ throw new Error("prompt cannot be empty");
114
+ }
115
+ const agentDoc = findAgent(harnessDir, agentId);
116
+ if (!agentDoc) {
117
+ const available = listAgents(harnessDir);
118
+ const agentList = available.length > 0 ? available.map((a) => ` - ${a.id}: ${a.l0}`).join("\n") : " (none)";
119
+ throw new Error(
120
+ `Agent "${agentId}" not found.
121
+
122
+ Available agents:
123
+ ${agentList}`
124
+ );
125
+ }
126
+ const config = loadConfig(harnessDir, opts.modelOverride ? { model: { id: opts.modelOverride } } : void 0);
127
+ const systemPrompt = buildAgentPrompt(harnessDir, agentDoc, config);
128
+ const model = getModel(config, apiKey);
129
+ const toolSet = buildToolSet(harnessDir);
130
+ const hasTools = Object.keys(toolSet).length > 0;
131
+ return { agentDoc, config, systemPrompt, model, toolSet, hasTools };
132
+ }
133
+ async function delegateTo(opts) {
134
+ const { harnessDir, prompt } = opts;
135
+ const { agentDoc, config, systemPrompt, model, toolSet, hasTools } = prepareDelegation(opts);
136
+ const sessionId = createSessionId();
137
+ const started = (/* @__PURE__ */ new Date()).toISOString();
138
+ const result = await generate({
139
+ model,
140
+ system: systemPrompt,
141
+ prompt,
142
+ maxRetries: config.model.max_retries,
143
+ timeoutMs: config.model.timeout_ms,
144
+ ...hasTools ? { tools: toolSet, maxToolSteps: 5 } : {}
145
+ });
146
+ const ended = (/* @__PURE__ */ new Date()).toISOString();
147
+ const session = {
148
+ id: sessionId,
149
+ started,
150
+ ended,
151
+ prompt,
152
+ summary: result.text.slice(0, 200),
153
+ tokens_used: result.usage.totalTokens,
154
+ model_id: config.model.id,
155
+ delegated_to: agentDoc.frontmatter.id,
156
+ steps: result.steps,
157
+ tool_calls: result.toolCalls.length > 0 ? result.toolCalls : void 0
158
+ };
159
+ try {
160
+ writeSession(harnessDir, session);
161
+ } catch (err) {
162
+ log.warn(`Failed to write delegation session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
163
+ }
164
+ return {
165
+ agentId: agentDoc.frontmatter.id,
166
+ text: result.text,
167
+ usage: result.usage,
168
+ sessionId
169
+ };
170
+ }
171
+ function delegateStream(opts) {
172
+ const { harnessDir, prompt } = opts;
173
+ const { agentDoc, config, systemPrompt, model, toolSet, hasTools } = prepareDelegation(opts);
174
+ const sessionId = createSessionId();
175
+ const started = (/* @__PURE__ */ new Date()).toISOString();
176
+ const result = streamGenerateWithDetails({
177
+ model,
178
+ system: systemPrompt,
179
+ prompt,
180
+ maxRetries: config.model.max_retries,
181
+ timeoutMs: config.model.timeout_ms,
182
+ ...hasTools ? { tools: toolSet, maxToolSteps: 5 } : {}
183
+ });
184
+ async function* wrappedStream() {
185
+ let fullText = "";
186
+ try {
187
+ for await (const chunk of result.textStream) {
188
+ fullText += chunk;
189
+ yield chunk;
190
+ }
191
+ } catch (err) {
192
+ const error = err instanceof Error ? err : new Error(String(err));
193
+ log.warn(`Delegation stream error for agent "${agentDoc.frontmatter.id}": ${error.message}`);
194
+ throw error;
195
+ }
196
+ let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
197
+ let steps = 1;
198
+ let toolCalls = [];
199
+ try {
200
+ [usage, steps, toolCalls] = await Promise.all([
201
+ result.usage,
202
+ result.steps,
203
+ result.toolCalls
204
+ ]);
205
+ } catch (err) {
206
+ log.warn(`Failed to resolve delegation post-stream metadata: ${err instanceof Error ? err.message : String(err)}`);
207
+ }
208
+ const ended = (/* @__PURE__ */ new Date()).toISOString();
209
+ const session = {
210
+ id: sessionId,
211
+ started,
212
+ ended,
213
+ prompt,
214
+ summary: fullText.slice(0, 200),
215
+ tokens_used: usage.totalTokens,
216
+ model_id: config.model.id,
217
+ delegated_to: agentDoc.frontmatter.id,
218
+ steps,
219
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
220
+ };
221
+ try {
222
+ writeSession(harnessDir, session);
223
+ } catch (err) {
224
+ log.warn(`Failed to write delegation session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
225
+ }
226
+ }
227
+ return {
228
+ agentId: agentDoc.frontmatter.id,
229
+ sessionId,
230
+ textStream: wrappedStream()
231
+ };
232
+ }
233
+
234
+ export {
235
+ loadAgentDocs,
236
+ findAgent,
237
+ listAgents,
238
+ buildAgentPrompt,
239
+ delegateTo,
240
+ delegateStream
241
+ };
242
+ //# sourceMappingURL=chunk-YUFNYN2H.js.map