@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,236 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ getAllWorkflowStats,
5
+ loadMetrics
6
+ } from "./chunk-6EMOEYGU.js";
7
+ import {
8
+ getSessionAnalytics
9
+ } from "./chunk-GNUSHD2Y.js";
10
+ import {
11
+ validateMcpConfig
12
+ } from "./chunk-5H34JPMB.js";
13
+ import {
14
+ loadState
15
+ } from "./chunk-UDZIS2AQ.js";
16
+ import {
17
+ getHealthStatus
18
+ } from "./chunk-TAT6JU3X.js";
19
+ import {
20
+ getSpending
21
+ } from "./chunk-JKMGYWXB.js";
22
+ import {
23
+ loadConfig
24
+ } from "./chunk-CHJ5GNZC.js";
25
+
26
+ // src/runtime/telemetry.ts
27
+ import { existsSync, readdirSync } from "fs";
28
+ import { join } from "path";
29
+ function countFiles(dir, filter) {
30
+ if (!existsSync(dir)) return 0;
31
+ try {
32
+ return readdirSync(dir).filter(filter).length;
33
+ } catch {
34
+ return 0;
35
+ }
36
+ }
37
+ var mdFilter = (f) => f.endsWith(".md") && !f.startsWith(".") && !f.startsWith("_");
38
+ function collectSnapshot(harnessDir, options) {
39
+ const now = (/* @__PURE__ */ new Date()).toISOString();
40
+ let agentName = "unknown";
41
+ let agentVersion = "0.0.0";
42
+ let config;
43
+ try {
44
+ config = loadConfig(harnessDir);
45
+ agentName = config.agent.name;
46
+ agentVersion = config.agent.version;
47
+ } catch {
48
+ }
49
+ let state;
50
+ try {
51
+ state = loadState(harnessDir);
52
+ } catch {
53
+ state = {
54
+ mode: "idle",
55
+ goals: [],
56
+ active_workflows: [],
57
+ unfinished_business: [],
58
+ last_interaction: "never"
59
+ };
60
+ }
61
+ const health = options?.skipHealth ? { status: "healthy", checks: [], metrics: { lastSuccessfulRun: null, lastFailedRun: null, lastError: null, consecutiveFailures: 0, totalRuns: 0, totalSuccesses: 0, totalFailures: 0, bootedAt: null, updatedAt: now }, costToday: 0, costThisMonth: 0 } : getHealthStatus(harnessDir);
62
+ const today = now.split("T")[0];
63
+ const monthStart = `${(/* @__PURE__ */ new Date()).getFullYear()}-${String((/* @__PURE__ */ new Date()).getMonth() + 1).padStart(2, "0")}-01`;
64
+ const emptySummary = { total_cost_usd: 0, total_input_tokens: 0, total_output_tokens: 0, entries: 0, by_model: {}, by_provider: {} };
65
+ let spendingToday = emptySummary;
66
+ let spendingMonth = emptySummary;
67
+ let spendingAll = emptySummary;
68
+ if (!options?.skipSpending) {
69
+ try {
70
+ spendingToday = getSpending(harnessDir, today);
71
+ spendingMonth = getSpending(harnessDir, monthStart);
72
+ spendingAll = getSpending(harnessDir, "2000-01-01");
73
+ } catch {
74
+ }
75
+ }
76
+ let totalSessions = 0;
77
+ let totalTokens = 0;
78
+ let avgTokensPerSession = 0;
79
+ let delegationCount = 0;
80
+ if (!options?.skipSessions) {
81
+ try {
82
+ const analytics = getSessionAnalytics(harnessDir);
83
+ totalSessions = analytics.totalSessions;
84
+ totalTokens = analytics.totalTokens;
85
+ avgTokensPerSession = analytics.avgTokensPerSession;
86
+ delegationCount = analytics.delegationCount;
87
+ } catch {
88
+ }
89
+ }
90
+ let workflowStats = [];
91
+ let wfTotalRuns = 0;
92
+ let wfTotalSuccesses = 0;
93
+ let wfTotalFailures = 0;
94
+ if (!options?.skipWorkflows) {
95
+ try {
96
+ workflowStats = getAllWorkflowStats(harnessDir);
97
+ const metricsStore = loadMetrics(harnessDir);
98
+ wfTotalRuns = metricsStore.runs.length;
99
+ wfTotalSuccesses = metricsStore.runs.filter((r) => r.success).length;
100
+ wfTotalFailures = metricsStore.runs.filter((r) => !r.success).length;
101
+ } catch {
102
+ }
103
+ }
104
+ const sessionCount = countFiles(join(harnessDir, "memory", "sessions"), mdFilter);
105
+ const journalCount = countFiles(join(harnessDir, "memory", "journal"), mdFilter);
106
+ const weeklyCount = countFiles(join(harnessDir, "memory", "journal", "weekly"), mdFilter);
107
+ const primitiveDirs = ["rules", "instincts", "skills", "playbooks", "workflows", "tools", "agents"];
108
+ let primitiveCount = 0;
109
+ for (const dir of primitiveDirs) {
110
+ primitiveCount += countFiles(join(harnessDir, dir), mdFilter);
111
+ }
112
+ const mcpServers = config?.mcp?.servers ?? {};
113
+ const mcpEntries = Object.entries(mcpServers);
114
+ const mcpValidationErrors = config ? validateMcpConfig(config) : [];
115
+ const mcpErrorMap = new Map(mcpValidationErrors.map((e) => [e.server, e.error]));
116
+ const mcpServerList = mcpEntries.map(([name, s]) => ({
117
+ name,
118
+ transport: s.transport,
119
+ enabled: s.enabled !== false,
120
+ valid: !mcpErrorMap.has(name),
121
+ ...mcpErrorMap.has(name) ? { error: mcpErrorMap.get(name) } : {}
122
+ }));
123
+ return {
124
+ timestamp: now,
125
+ agent: {
126
+ name: agentName,
127
+ version: agentVersion,
128
+ mode: state.mode,
129
+ lastInteraction: state.last_interaction
130
+ },
131
+ health,
132
+ spending: {
133
+ today: spendingToday,
134
+ thisMonth: spendingMonth,
135
+ allTime: spendingAll
136
+ },
137
+ sessions: {
138
+ total: totalSessions,
139
+ totalTokens,
140
+ avgTokensPerSession,
141
+ delegationCount
142
+ },
143
+ workflows: {
144
+ totalRuns: wfTotalRuns,
145
+ totalSuccesses: wfTotalSuccesses,
146
+ totalFailures: wfTotalFailures,
147
+ overallSuccessRate: wfTotalRuns > 0 ? wfTotalSuccesses / wfTotalRuns : 0,
148
+ stats: workflowStats
149
+ },
150
+ storage: {
151
+ sessionCount,
152
+ journalCount,
153
+ weeklyCount,
154
+ primitiveCount
155
+ },
156
+ mcp: {
157
+ serverCount: mcpEntries.length,
158
+ enabledCount: mcpServerList.filter((s) => s.enabled).length,
159
+ servers: mcpServerList
160
+ }
161
+ };
162
+ }
163
+ function formatDashboard(snapshot) {
164
+ const lines = [];
165
+ const statusIcon = snapshot.health.status === "healthy" ? "OK" : snapshot.health.status === "degraded" ? "WARN" : "FAIL";
166
+ lines.push(` ${snapshot.agent.name} v${snapshot.agent.version} | ${statusIcon} | mode: ${snapshot.agent.mode}`);
167
+ lines.push(` Last interaction: ${snapshot.agent.lastInteraction}`);
168
+ lines.push("");
169
+ lines.push(" Health Checks");
170
+ for (const check of snapshot.health.checks) {
171
+ const icon = check.status === "pass" ? "+" : check.status === "warn" ? "!" : "x";
172
+ lines.push(` [${icon}] ${check.name}: ${check.message}`);
173
+ }
174
+ lines.push("");
175
+ const todayCost = snapshot.spending.today.total_cost_usd;
176
+ const monthCost = snapshot.spending.thisMonth.total_cost_usd;
177
+ const allTimeCost = snapshot.spending.allTime.total_cost_usd;
178
+ lines.push(" Spending");
179
+ lines.push(` Today: $${todayCost.toFixed(6)} (${snapshot.spending.today.entries} calls)`);
180
+ lines.push(` Month: $${monthCost.toFixed(6)} (${snapshot.spending.thisMonth.entries} calls)`);
181
+ lines.push(` All time: $${allTimeCost.toFixed(6)} (${snapshot.spending.allTime.entries} calls)`);
182
+ const todayModels = Object.entries(snapshot.spending.today.by_model);
183
+ if (todayModels.length > 0) {
184
+ lines.push(" By model (today):");
185
+ for (const [model, data] of todayModels.sort((a, b) => b[1].cost_usd - a[1].cost_usd)) {
186
+ lines.push(` ${model}: $${data.cost_usd.toFixed(6)} (${data.count}x)`);
187
+ }
188
+ }
189
+ lines.push("");
190
+ lines.push(" Sessions");
191
+ lines.push(` Total: ${snapshot.sessions.total} | Tokens: ${snapshot.sessions.totalTokens.toLocaleString()} | Avg: ${snapshot.sessions.avgTokensPerSession.toLocaleString()}/session`);
192
+ if (snapshot.sessions.delegationCount > 0) {
193
+ lines.push(` Delegations: ${snapshot.sessions.delegationCount}`);
194
+ }
195
+ lines.push("");
196
+ if (snapshot.workflows.totalRuns > 0 || snapshot.workflows.stats.length > 0) {
197
+ const successRate = (snapshot.workflows.overallSuccessRate * 100).toFixed(0);
198
+ lines.push(" Workflows");
199
+ lines.push(` Runs: ${snapshot.workflows.totalRuns} (${successRate}% success) | ${snapshot.workflows.totalSuccesses} ok, ${snapshot.workflows.totalFailures} failed`);
200
+ for (const s of snapshot.workflows.stats.slice(0, 5)) {
201
+ const rate = (s.success_rate * 100).toFixed(0);
202
+ lines.push(` ${s.workflow_id}: ${s.total_runs} runs (${rate}%) | last: ${s.last_run}`);
203
+ }
204
+ if (snapshot.workflows.stats.length > 5) {
205
+ lines.push(` ... and ${snapshot.workflows.stats.length - 5} more`);
206
+ }
207
+ lines.push("");
208
+ }
209
+ if (snapshot.mcp.serverCount > 0) {
210
+ lines.push(" MCP Servers");
211
+ lines.push(` Configured: ${snapshot.mcp.serverCount} | Enabled: ${snapshot.mcp.enabledCount}`);
212
+ for (const server of snapshot.mcp.servers) {
213
+ const status = !server.enabled ? "-" : server.valid ? "+" : "!";
214
+ const error = server.error ? ` (${server.error})` : "";
215
+ lines.push(` [${status}] ${server.name} (${server.transport})${error}`);
216
+ }
217
+ lines.push("");
218
+ }
219
+ lines.push(" Storage");
220
+ lines.push(` Sessions: ${snapshot.storage.sessionCount} | Journals: ${snapshot.storage.journalCount} | Weekly: ${snapshot.storage.weeklyCount} | Primitives: ${snapshot.storage.primitiveCount}`);
221
+ lines.push("");
222
+ const m = snapshot.health.metrics;
223
+ lines.push(" Run Health");
224
+ lines.push(` Total: ${m.totalRuns} | Success: ${m.totalSuccesses} | Fail: ${m.totalFailures} | Consecutive failures: ${m.consecutiveFailures}`);
225
+ if (m.bootedAt) lines.push(` Booted: ${m.bootedAt}`);
226
+ if (m.lastSuccessfulRun) lines.push(` Last success: ${m.lastSuccessfulRun}`);
227
+ if (m.lastFailedRun) lines.push(` Last failure: ${m.lastFailedRun}`);
228
+ if (m.lastError) lines.push(` Last error: ${m.lastError.slice(0, 120)}`);
229
+ return lines.join("\n");
230
+ }
231
+
232
+ export {
233
+ collectSnapshot,
234
+ formatDashboard
235
+ };
236
+ //# sourceMappingURL=chunk-A7BJPQQ6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/telemetry.ts"],"sourcesContent":["import { existsSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { getHealthStatus } from './health.js';\nimport { getSpending } from './cost-tracker.js';\nimport { getAllWorkflowStats, loadMetrics } from './metrics.js';\nimport { getSessionAnalytics } from './analytics.js';\nimport { validateMcpConfig } from './mcp.js';\nimport { loadState } from './state.js';\nimport { loadConfig } from '../core/config.js';\nimport type { HealthStatus } from './health.js';\nimport type { SpendingSummary } from './cost-tracker.js';\nimport type { WorkflowStats } from './metrics.js';\nimport type { AgentState, HarnessConfig } from '../core/types.js';\n\n/** A point-in-time snapshot of all system telemetry. */\nexport interface TelemetrySnapshot {\n timestamp: string;\n agent: {\n name: string;\n version: string;\n mode: AgentState['mode'];\n lastInteraction: string;\n };\n health: HealthStatus;\n spending: {\n today: SpendingSummary;\n thisMonth: SpendingSummary;\n allTime: SpendingSummary;\n };\n sessions: {\n total: number;\n totalTokens: number;\n avgTokensPerSession: number;\n delegationCount: number;\n };\n workflows: {\n totalRuns: number;\n totalSuccesses: number;\n totalFailures: number;\n overallSuccessRate: number;\n stats: WorkflowStats[];\n };\n storage: {\n sessionCount: number;\n journalCount: number;\n weeklyCount: number;\n primitiveCount: number;\n };\n mcp: {\n serverCount: number;\n enabledCount: number;\n servers: Array<{ name: string; transport: string; enabled: boolean; valid: boolean; error?: string }>;\n };\n}\n\n/** Options for snapshot collection. */\nexport interface TelemetryOptions {\n /** Skip health checks (default: false) */\n skipHealth?: boolean;\n /** Skip session analytics parsing (default: false) */\n skipSessions?: boolean;\n /** Skip workflow metrics (default: false) */\n skipWorkflows?: boolean;\n /** Skip spending data (default: false) */\n skipSpending?: boolean;\n}\n\n/**\n * Count files in a directory matching a filter (non-recursive).\n */\nfunction countFiles(dir: string, filter: (f: string) => boolean): number {\n if (!existsSync(dir)) return 0;\n try {\n return readdirSync(dir).filter(filter).length;\n } catch {\n return 0;\n }\n}\n\nconst mdFilter = (f: string): boolean => f.endsWith('.md') && !f.startsWith('.') && !f.startsWith('_');\n\n/**\n * Collect a full telemetry snapshot from all system modules.\n * Each section can be skipped via options for performance.\n */\nexport function collectSnapshot(\n harnessDir: string,\n options?: TelemetryOptions,\n): TelemetrySnapshot {\n const now = new Date().toISOString();\n\n // Agent info\n let agentName = 'unknown';\n let agentVersion = '0.0.0';\n let config: HarnessConfig | undefined;\n try {\n config = loadConfig(harnessDir);\n agentName = config.agent.name;\n agentVersion = config.agent.version;\n } catch {\n // Config may not exist\n }\n\n let state: AgentState;\n try {\n state = loadState(harnessDir);\n } catch {\n state = {\n mode: 'idle',\n goals: [],\n active_workflows: [],\n unfinished_business: [],\n last_interaction: 'never',\n };\n }\n\n // Health\n const health: HealthStatus = options?.skipHealth\n ? { status: 'healthy', checks: [], metrics: { lastSuccessfulRun: null, lastFailedRun: null, lastError: null, consecutiveFailures: 0, totalRuns: 0, totalSuccesses: 0, totalFailures: 0, bootedAt: null, updatedAt: now }, costToday: 0, costThisMonth: 0 }\n : getHealthStatus(harnessDir);\n\n // Spending\n const today = now.split('T')[0];\n const monthStart = `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}-01`;\n const emptySummary: SpendingSummary = { total_cost_usd: 0, total_input_tokens: 0, total_output_tokens: 0, entries: 0, by_model: {}, by_provider: {} };\n\n let spendingToday: SpendingSummary = emptySummary;\n let spendingMonth: SpendingSummary = emptySummary;\n let spendingAll: SpendingSummary = emptySummary;\n\n if (!options?.skipSpending) {\n try {\n spendingToday = getSpending(harnessDir, today);\n spendingMonth = getSpending(harnessDir, monthStart);\n spendingAll = getSpending(harnessDir, '2000-01-01');\n } catch {\n // Cost data may not exist\n }\n }\n\n // Session analytics\n let totalSessions = 0;\n let totalTokens = 0;\n let avgTokensPerSession = 0;\n let delegationCount = 0;\n\n if (!options?.skipSessions) {\n try {\n const analytics = getSessionAnalytics(harnessDir);\n totalSessions = analytics.totalSessions;\n totalTokens = analytics.totalTokens;\n avgTokensPerSession = analytics.avgTokensPerSession;\n delegationCount = analytics.delegationCount;\n } catch {\n // Analytics may fail if no sessions\n }\n }\n\n // Workflow metrics\n let workflowStats: WorkflowStats[] = [];\n let wfTotalRuns = 0;\n let wfTotalSuccesses = 0;\n let wfTotalFailures = 0;\n\n if (!options?.skipWorkflows) {\n try {\n workflowStats = getAllWorkflowStats(harnessDir);\n const metricsStore = loadMetrics(harnessDir);\n wfTotalRuns = metricsStore.runs.length;\n wfTotalSuccesses = metricsStore.runs.filter((r) => r.success).length;\n wfTotalFailures = metricsStore.runs.filter((r) => !r.success).length;\n } catch {\n // Metrics may not exist\n }\n }\n\n // Storage counts\n const sessionCount = countFiles(join(harnessDir, 'memory', 'sessions'), mdFilter);\n const journalCount = countFiles(join(harnessDir, 'memory', 'journal'), mdFilter);\n const weeklyCount = countFiles(join(harnessDir, 'memory', 'journal', 'weekly'), mdFilter);\n\n // Count primitives across core dirs\n const primitiveDirs = ['rules', 'instincts', 'skills', 'playbooks', 'workflows', 'tools', 'agents'];\n let primitiveCount = 0;\n for (const dir of primitiveDirs) {\n primitiveCount += countFiles(join(harnessDir, dir), mdFilter);\n }\n\n // MCP server info\n const mcpServers = config?.mcp?.servers ?? {};\n const mcpEntries = Object.entries(mcpServers);\n const mcpValidationErrors = config ? validateMcpConfig(config) : [];\n const mcpErrorMap = new Map(mcpValidationErrors.map((e) => [e.server, e.error]));\n\n const mcpServerList = mcpEntries.map(([name, s]) => ({\n name,\n transport: s.transport,\n enabled: s.enabled !== false,\n valid: !mcpErrorMap.has(name),\n ...(mcpErrorMap.has(name) ? { error: mcpErrorMap.get(name) } : {}),\n }));\n\n return {\n timestamp: now,\n agent: {\n name: agentName,\n version: agentVersion,\n mode: state.mode,\n lastInteraction: state.last_interaction,\n },\n health,\n spending: {\n today: spendingToday,\n thisMonth: spendingMonth,\n allTime: spendingAll,\n },\n sessions: {\n total: totalSessions,\n totalTokens,\n avgTokensPerSession,\n delegationCount,\n },\n workflows: {\n totalRuns: wfTotalRuns,\n totalSuccesses: wfTotalSuccesses,\n totalFailures: wfTotalFailures,\n overallSuccessRate: wfTotalRuns > 0 ? wfTotalSuccesses / wfTotalRuns : 0,\n stats: workflowStats,\n },\n storage: {\n sessionCount,\n journalCount,\n weeklyCount,\n primitiveCount,\n },\n mcp: {\n serverCount: mcpEntries.length,\n enabledCount: mcpServerList.filter((s) => s.enabled).length,\n servers: mcpServerList,\n },\n };\n}\n\n/**\n * Format a telemetry snapshot as a human-readable dashboard string.\n */\nexport function formatDashboard(snapshot: TelemetrySnapshot): string {\n const lines: string[] = [];\n\n // Header\n const statusIcon = snapshot.health.status === 'healthy' ? 'OK' : snapshot.health.status === 'degraded' ? 'WARN' : 'FAIL';\n lines.push(` ${snapshot.agent.name} v${snapshot.agent.version} | ${statusIcon} | mode: ${snapshot.agent.mode}`);\n lines.push(` Last interaction: ${snapshot.agent.lastInteraction}`);\n lines.push('');\n\n // Health checks\n lines.push(' Health Checks');\n for (const check of snapshot.health.checks) {\n const icon = check.status === 'pass' ? '+' : check.status === 'warn' ? '!' : 'x';\n lines.push(` [${icon}] ${check.name}: ${check.message}`);\n }\n lines.push('');\n\n // Spending\n const todayCost = snapshot.spending.today.total_cost_usd;\n const monthCost = snapshot.spending.thisMonth.total_cost_usd;\n const allTimeCost = snapshot.spending.allTime.total_cost_usd;\n lines.push(' Spending');\n lines.push(` Today: $${todayCost.toFixed(6)} (${snapshot.spending.today.entries} calls)`);\n lines.push(` Month: $${monthCost.toFixed(6)} (${snapshot.spending.thisMonth.entries} calls)`);\n lines.push(` All time: $${allTimeCost.toFixed(6)} (${snapshot.spending.allTime.entries} calls)`);\n\n // Model breakdown (today)\n const todayModels = Object.entries(snapshot.spending.today.by_model);\n if (todayModels.length > 0) {\n lines.push(' By model (today):');\n for (const [model, data] of todayModels.sort((a, b) => b[1].cost_usd - a[1].cost_usd)) {\n lines.push(` ${model}: $${data.cost_usd.toFixed(6)} (${data.count}x)`);\n }\n }\n lines.push('');\n\n // Sessions\n lines.push(' Sessions');\n lines.push(` Total: ${snapshot.sessions.total} | Tokens: ${snapshot.sessions.totalTokens.toLocaleString()} | Avg: ${snapshot.sessions.avgTokensPerSession.toLocaleString()}/session`);\n if (snapshot.sessions.delegationCount > 0) {\n lines.push(` Delegations: ${snapshot.sessions.delegationCount}`);\n }\n lines.push('');\n\n // Workflows\n if (snapshot.workflows.totalRuns > 0 || snapshot.workflows.stats.length > 0) {\n const successRate = (snapshot.workflows.overallSuccessRate * 100).toFixed(0);\n lines.push(' Workflows');\n lines.push(` Runs: ${snapshot.workflows.totalRuns} (${successRate}% success) | ${snapshot.workflows.totalSuccesses} ok, ${snapshot.workflows.totalFailures} failed`);\n for (const s of snapshot.workflows.stats.slice(0, 5)) {\n const rate = (s.success_rate * 100).toFixed(0);\n lines.push(` ${s.workflow_id}: ${s.total_runs} runs (${rate}%) | last: ${s.last_run}`);\n }\n if (snapshot.workflows.stats.length > 5) {\n lines.push(` ... and ${snapshot.workflows.stats.length - 5} more`);\n }\n lines.push('');\n }\n\n // MCP\n if (snapshot.mcp.serverCount > 0) {\n lines.push(' MCP Servers');\n lines.push(` Configured: ${snapshot.mcp.serverCount} | Enabled: ${snapshot.mcp.enabledCount}`);\n for (const server of snapshot.mcp.servers) {\n const status = !server.enabled ? '-' : server.valid ? '+' : '!';\n const error = server.error ? ` (${server.error})` : '';\n lines.push(` [${status}] ${server.name} (${server.transport})${error}`);\n }\n lines.push('');\n }\n\n // Storage\n lines.push(' Storage');\n lines.push(` Sessions: ${snapshot.storage.sessionCount} | Journals: ${snapshot.storage.journalCount} | Weekly: ${snapshot.storage.weeklyCount} | Primitives: ${snapshot.storage.primitiveCount}`);\n lines.push('');\n\n // Run health\n const m = snapshot.health.metrics;\n lines.push(' Run Health');\n lines.push(` Total: ${m.totalRuns} | Success: ${m.totalSuccesses} | Fail: ${m.totalFailures} | Consecutive failures: ${m.consecutiveFailures}`);\n if (m.bootedAt) lines.push(` Booted: ${m.bootedAt}`);\n if (m.lastSuccessfulRun) lines.push(` Last success: ${m.lastSuccessfulRun}`);\n if (m.lastFailedRun) lines.push(` Last failure: ${m.lastFailedRun}`);\n if (m.lastError) lines.push(` Last error: ${m.lastError.slice(0, 120)}`);\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ checkBudget
5
+ } from "./chunk-JKMGYWXB.js";
6
+ import {
7
+ checkRateLimit,
8
+ recordEvent
9
+ } from "./chunk-OC6YSTDX.js";
10
+
11
+ // src/runtime/guardrails.ts
12
+ var RATE_KEY = "llm-calls";
13
+ function buildRateLimits(config) {
14
+ const limits = [];
15
+ const rl = config.rate_limits;
16
+ if (rl?.per_minute) {
17
+ limits.push({ key: `${RATE_KEY}:minute`, max_requests: rl.per_minute, window_ms: 6e4 });
18
+ }
19
+ if (rl?.per_hour) {
20
+ limits.push({ key: `${RATE_KEY}:hour`, max_requests: rl.per_hour, window_ms: 36e5 });
21
+ }
22
+ if (rl?.per_day) {
23
+ limits.push({ key: `${RATE_KEY}:day`, max_requests: rl.per_day, window_ms: 864e5 });
24
+ }
25
+ return limits;
26
+ }
27
+ function checkGuardrails(harnessDir, config) {
28
+ const limits = buildRateLimits(config);
29
+ for (const limit of limits) {
30
+ const check = checkRateLimit(harnessDir, limit);
31
+ if (!check.allowed) {
32
+ const windowLabel = limit.window_ms <= 6e4 ? "minute" : limit.window_ms <= 36e5 ? "hour" : "day";
33
+ return {
34
+ allowed: false,
35
+ reason: `Rate limit exceeded: ${check.current}/${check.max} calls per ${windowLabel}. Retry after ${Math.ceil(check.retry_after_ms / 1e3)}s.`,
36
+ rateLimitCheck: check,
37
+ budgetStatus: null,
38
+ retryAfterMs: check.retry_after_ms
39
+ };
40
+ }
41
+ }
42
+ const budgetConfig = config.budget;
43
+ if (budgetConfig?.enforce !== false && (budgetConfig?.daily_limit_usd || budgetConfig?.monthly_limit_usd)) {
44
+ const status = checkBudget(harnessDir, {
45
+ daily_limit_usd: budgetConfig.daily_limit_usd,
46
+ monthly_limit_usd: budgetConfig.monthly_limit_usd
47
+ });
48
+ const exceeded = status.alerts.some((a) => a.includes("exceeded"));
49
+ if (exceeded) {
50
+ return {
51
+ allowed: false,
52
+ reason: status.alerts.filter((a) => a.includes("exceeded")).join("; "),
53
+ rateLimitCheck: null,
54
+ budgetStatus: status,
55
+ retryAfterMs: 0
56
+ };
57
+ }
58
+ }
59
+ const now = Date.now();
60
+ for (const limit of limits) {
61
+ recordEvent(harnessDir, limit.key, now);
62
+ }
63
+ return {
64
+ allowed: true,
65
+ reason: null,
66
+ rateLimitCheck: null,
67
+ budgetStatus: null,
68
+ retryAfterMs: 0
69
+ };
70
+ }
71
+
72
+ export {
73
+ buildRateLimits,
74
+ checkGuardrails
75
+ };
76
+ //# sourceMappingURL=chunk-AGAAFJEO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/guardrails.ts"],"sourcesContent":["import { recordEvent, checkRateLimit } from './rate-limiter.js';\nimport { checkBudget } from './cost-tracker.js';\nimport type { HarnessConfig } from '../core/types.js';\nimport type { RateLimit, RateLimitCheck } from './rate-limiter.js';\nimport type { BudgetStatus } from './cost-tracker.js';\n\n/** Result of a guardrail check before an LLM call. */\nexport interface GuardrailResult {\n allowed: boolean;\n reason: string | null;\n rateLimitCheck: RateLimitCheck | null;\n budgetStatus: BudgetStatus | null;\n /** Suggested wait time in ms if rate-limited (0 otherwise) */\n retryAfterMs: number;\n}\n\nconst RATE_KEY = 'llm-calls';\n\n/**\n * Build rate limit rules from config.\n * Returns an array of limits to check (per-minute, per-hour, per-day).\n */\nexport function buildRateLimits(config: HarnessConfig): RateLimit[] {\n const limits: RateLimit[] = [];\n const rl = config.rate_limits;\n\n if (rl?.per_minute) {\n limits.push({ key: `${RATE_KEY}:minute`, max_requests: rl.per_minute, window_ms: 60_000 });\n }\n if (rl?.per_hour) {\n limits.push({ key: `${RATE_KEY}:hour`, max_requests: rl.per_hour, window_ms: 3_600_000 });\n }\n if (rl?.per_day) {\n limits.push({ key: `${RATE_KEY}:day`, max_requests: rl.per_day, window_ms: 86_400_000 });\n }\n\n return limits;\n}\n\n/**\n * Check all guardrails (rate limits + budget) before an LLM call.\n *\n * Rate limits: Checks all configured limits. If any limit is exceeded,\n * the call is blocked with `retry_after_ms` from the first violated limit.\n *\n * Budget: Checks daily and monthly spending limits. If `budget.enforce` is true\n * and any limit is exceeded, the call is blocked.\n *\n * Returns { allowed: true } if all checks pass.\n */\nexport function checkGuardrails(\n harnessDir: string,\n config: HarnessConfig,\n): GuardrailResult {\n // Check rate limits\n const limits = buildRateLimits(config);\n for (const limit of limits) {\n const check = checkRateLimit(harnessDir, limit);\n if (!check.allowed) {\n const windowLabel = limit.window_ms <= 60_000 ? 'minute' : limit.window_ms <= 3_600_000 ? 'hour' : 'day';\n return {\n allowed: false,\n reason: `Rate limit exceeded: ${check.current}/${check.max} calls per ${windowLabel}. Retry after ${Math.ceil(check.retry_after_ms / 1000)}s.`,\n rateLimitCheck: check,\n budgetStatus: null,\n retryAfterMs: check.retry_after_ms,\n };\n }\n }\n\n // Check budget\n const budgetConfig = config.budget;\n if (budgetConfig?.enforce !== false && (budgetConfig?.daily_limit_usd || budgetConfig?.monthly_limit_usd)) {\n const status = checkBudget(harnessDir, {\n daily_limit_usd: budgetConfig.daily_limit_usd,\n monthly_limit_usd: budgetConfig.monthly_limit_usd,\n });\n\n const exceeded = status.alerts.some((a) => a.includes('exceeded'));\n if (exceeded) {\n return {\n allowed: false,\n reason: status.alerts.filter((a) => a.includes('exceeded')).join('; '),\n rateLimitCheck: null,\n budgetStatus: status,\n retryAfterMs: 0,\n };\n }\n }\n\n // All checks pass — record rate limit events for all configured limits\n const now = Date.now();\n for (const limit of limits) {\n recordEvent(harnessDir, limit.key, now);\n }\n\n return {\n allowed: true,\n reason: null,\n rateLimitCheck: null,\n budgetStatus: null,\n retryAfterMs: 0,\n };\n}\n"],"mappings":";;;;;;;;;;;AAgBA,IAAM,WAAW;AAMV,SAAS,gBAAgB,QAAoC;AAClE,QAAM,SAAsB,CAAC;AAC7B,QAAM,KAAK,OAAO;AAElB,MAAI,IAAI,YAAY;AAClB,WAAO,KAAK,EAAE,KAAK,GAAG,QAAQ,WAAW,cAAc,GAAG,YAAY,WAAW,IAAO,CAAC;AAAA,EAC3F;AACA,MAAI,IAAI,UAAU;AAChB,WAAO,KAAK,EAAE,KAAK,GAAG,QAAQ,SAAS,cAAc,GAAG,UAAU,WAAW,KAAU,CAAC;AAAA,EAC1F;AACA,MAAI,IAAI,SAAS;AACf,WAAO,KAAK,EAAE,KAAK,GAAG,QAAQ,QAAQ,cAAc,GAAG,SAAS,WAAW,MAAW,CAAC;AAAA,EACzF;AAEA,SAAO;AACT;AAaO,SAAS,gBACd,YACA,QACiB;AAEjB,QAAM,SAAS,gBAAgB,MAAM;AACrC,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,eAAe,YAAY,KAAK;AAC9C,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,cAAc,MAAM,aAAa,MAAS,WAAW,MAAM,aAAa,OAAY,SAAS;AACnG,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,wBAAwB,MAAM,OAAO,IAAI,MAAM,GAAG,cAAc,WAAW,iBAAiB,KAAK,KAAK,MAAM,iBAAiB,GAAI,CAAC;AAAA,QAC1I,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,cAAc,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe,OAAO;AAC5B,MAAI,cAAc,YAAY,UAAU,cAAc,mBAAmB,cAAc,oBAAoB;AACzG,UAAM,SAAS,YAAY,YAAY;AAAA,MACrC,iBAAiB,aAAa;AAAA,MAC9B,mBAAmB,aAAa;AAAA,IAClC,CAAC;AAED,UAAM,WAAW,OAAO,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC;AACjE,QAAI,UAAU;AACZ,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC,EAAE,KAAK,IAAI;AAAA,QACrE,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,cAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAM,KAAK,IAAI;AACrB,aAAW,SAAS,QAAQ;AAC1B,gBAAY,YAAY,MAAM,KAAK,GAAG;AAAA,EACxC;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,cAAc;AAAA,EAChB;AACF;","names":[]}
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+
4
+ // src/core/logger.ts
5
+ var LEVEL_ORDER = {
6
+ debug: 0,
7
+ info: 1,
8
+ warn: 2,
9
+ error: 3,
10
+ silent: 4
11
+ };
12
+ var globalLevel = "info";
13
+ function shouldLog(level) {
14
+ return LEVEL_ORDER[level] >= LEVEL_ORDER[globalLevel];
15
+ }
16
+ function formatMessage(prefix, level, msg) {
17
+ const tag = prefix ? `[${prefix}]` : "";
18
+ if (level === "debug") return `${tag} ${msg}`.trimStart();
19
+ if (level === "warn") return `${tag} WARN: ${msg}`.trimStart();
20
+ if (level === "error") return `${tag} ERROR: ${msg}`.trimStart();
21
+ return `${tag} ${msg}`.trimStart();
22
+ }
23
+ function createLoggerWithPrefix(prefix) {
24
+ return {
25
+ debug(msg, ...args) {
26
+ if (shouldLog("debug")) console.error(formatMessage(prefix, "debug", msg), ...args);
27
+ },
28
+ info(msg, ...args) {
29
+ if (shouldLog("info")) console.error(formatMessage(prefix, "info", msg), ...args);
30
+ },
31
+ warn(msg, ...args) {
32
+ if (shouldLog("warn")) console.error(formatMessage(prefix, "warn", msg), ...args);
33
+ },
34
+ error(msg, ...args) {
35
+ if (shouldLog("error")) console.error(formatMessage(prefix, "error", msg), ...args);
36
+ },
37
+ setLevel(level) {
38
+ globalLevel = level;
39
+ },
40
+ getLevel() {
41
+ return globalLevel;
42
+ },
43
+ child(childPrefix) {
44
+ const combined = prefix ? `${prefix}:${childPrefix}` : childPrefix;
45
+ return createLoggerWithPrefix(combined);
46
+ }
47
+ };
48
+ }
49
+ function createLogger(prefix = "") {
50
+ return createLoggerWithPrefix(prefix);
51
+ }
52
+ function setGlobalLogLevel(level) {
53
+ globalLevel = level;
54
+ }
55
+ function getGlobalLogLevel() {
56
+ return globalLevel;
57
+ }
58
+ var log = createLogger("harness");
59
+
60
+ export {
61
+ setGlobalLogLevel,
62
+ getGlobalLogLevel,
63
+ log
64
+ };
65
+ //# sourceMappingURL=chunk-BSKDOFRT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/logger.ts"],"sourcesContent":["export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\nexport interface Logger {\n debug(msg: string, ...args: unknown[]): void;\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n setLevel(level: LogLevel): void;\n getLevel(): LogLevel;\n child(prefix: string): Logger;\n}\n\nlet globalLevel: LogLevel = 'info';\n\nfunction shouldLog(level: LogLevel): boolean {\n return LEVEL_ORDER[level] >= LEVEL_ORDER[globalLevel];\n}\n\nfunction formatMessage(prefix: string, level: LogLevel, msg: string): string {\n const tag = prefix ? `[${prefix}]` : '';\n if (level === 'debug') return `${tag} ${msg}`.trimStart();\n if (level === 'warn') return `${tag} WARN: ${msg}`.trimStart();\n if (level === 'error') return `${tag} ERROR: ${msg}`.trimStart();\n return `${tag} ${msg}`.trimStart();\n}\n\nfunction createLoggerWithPrefix(prefix: string): Logger {\n return {\n debug(msg: string, ...args: unknown[]) {\n if (shouldLog('debug')) console.error(formatMessage(prefix, 'debug', msg), ...args);\n },\n info(msg: string, ...args: unknown[]) {\n if (shouldLog('info')) console.error(formatMessage(prefix, 'info', msg), ...args);\n },\n warn(msg: string, ...args: unknown[]) {\n if (shouldLog('warn')) console.error(formatMessage(prefix, 'warn', msg), ...args);\n },\n error(msg: string, ...args: unknown[]) {\n if (shouldLog('error')) console.error(formatMessage(prefix, 'error', msg), ...args);\n },\n setLevel(level: LogLevel) {\n globalLevel = level;\n },\n getLevel() {\n return globalLevel;\n },\n child(childPrefix: string) {\n const combined = prefix ? `${prefix}:${childPrefix}` : childPrefix;\n return createLoggerWithPrefix(combined);\n },\n };\n}\n\nexport function createLogger(prefix: string = ''): Logger {\n return createLoggerWithPrefix(prefix);\n}\n\nexport function setGlobalLogLevel(level: LogLevel): void {\n globalLevel = level;\n}\n\nexport function getGlobalLogLevel(): LogLevel {\n return globalLevel;\n}\n\nexport const log = createLogger('harness');\n"],"mappings":";;;;AAEA,IAAM,cAAwC;AAAA,EAC5C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;AAYA,IAAI,cAAwB;AAE5B,SAAS,UAAU,OAA0B;AAC3C,SAAO,YAAY,KAAK,KAAK,YAAY,WAAW;AACtD;AAEA,SAAS,cAAc,QAAgB,OAAiB,KAAqB;AAC3E,QAAM,MAAM,SAAS,IAAI,MAAM,MAAM;AACrC,MAAI,UAAU,QAAS,QAAO,GAAG,GAAG,IAAI,GAAG,GAAG,UAAU;AACxD,MAAI,UAAU,OAAQ,QAAO,GAAG,GAAG,UAAU,GAAG,GAAG,UAAU;AAC7D,MAAI,UAAU,QAAS,QAAO,GAAG,GAAG,WAAW,GAAG,GAAG,UAAU;AAC/D,SAAO,GAAG,GAAG,IAAI,GAAG,GAAG,UAAU;AACnC;AAEA,SAAS,uBAAuB,QAAwB;AACtD,SAAO;AAAA,IACL,MAAM,QAAgB,MAAiB;AACrC,UAAI,UAAU,OAAO,EAAG,SAAQ,MAAM,cAAc,QAAQ,SAAS,GAAG,GAAG,GAAG,IAAI;AAAA,IACpF;AAAA,IACA,KAAK,QAAgB,MAAiB;AACpC,UAAI,UAAU,MAAM,EAAG,SAAQ,MAAM,cAAc,QAAQ,QAAQ,GAAG,GAAG,GAAG,IAAI;AAAA,IAClF;AAAA,IACA,KAAK,QAAgB,MAAiB;AACpC,UAAI,UAAU,MAAM,EAAG,SAAQ,MAAM,cAAc,QAAQ,QAAQ,GAAG,GAAG,GAAG,IAAI;AAAA,IAClF;AAAA,IACA,MAAM,QAAgB,MAAiB;AACrC,UAAI,UAAU,OAAO,EAAG,SAAQ,MAAM,cAAc,QAAQ,SAAS,GAAG,GAAG,GAAG,IAAI;AAAA,IACpF;AAAA,IACA,SAAS,OAAiB;AACxB,oBAAc;AAAA,IAChB;AAAA,IACA,WAAW;AACT,aAAO;AAAA,IACT;AAAA,IACA,MAAM,aAAqB;AACzB,YAAM,WAAW,SAAS,GAAG,MAAM,IAAI,WAAW,KAAK;AACvD,aAAO,uBAAuB,QAAQ;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,aAAa,SAAiB,IAAY;AACxD,SAAO,uBAAuB,MAAM;AACtC;AAEO,SAAS,kBAAkB,OAAuB;AACvD,gBAAc;AAChB;AAEO,SAAS,oBAA8B;AAC5C,SAAO;AACT;AAEO,IAAM,MAAM,aAAa,SAAS;","names":[]}
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ CONFIG_DEFAULTS,
5
+ HarnessConfigSchema
6
+ } from "./chunk-4CWAGBNS.js";
7
+
8
+ // src/core/config.ts
9
+ import { readFileSync, existsSync } from "fs";
10
+ import { join } from "path";
11
+ import YAML from "yaml";
12
+ var CONFIG_FILENAMES = ["config.yaml", "config.yml", "harness.yaml", "harness.yml"];
13
+ function loadConfig(dir, overrides) {
14
+ let raw = {};
15
+ for (const filename of CONFIG_FILENAMES) {
16
+ const configPath = join(dir, filename);
17
+ if (existsSync(configPath)) {
18
+ const content = readFileSync(configPath, "utf-8");
19
+ raw = YAML.parse(content) || {};
20
+ break;
21
+ }
22
+ }
23
+ let merged = deepMerge(
24
+ CONFIG_DEFAULTS,
25
+ raw
26
+ );
27
+ if (overrides) {
28
+ merged = deepMerge(
29
+ merged,
30
+ overrides
31
+ );
32
+ }
33
+ const result = HarnessConfigSchema.safeParse(merged);
34
+ if (!result.success) {
35
+ const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
36
+ throw new Error(`Invalid config:
37
+ ${issues}`);
38
+ }
39
+ return result.data;
40
+ }
41
+ function writeDefaultConfig(_dir, agentName = "my-agent") {
42
+ return `# Agent Harness Configuration
43
+ agent:
44
+ name: ${agentName}
45
+ version: "0.1.0"
46
+
47
+ model:
48
+ provider: openrouter
49
+ id: anthropic/claude-sonnet-4
50
+ max_tokens: 200000
51
+ max_retries: 2
52
+ # timeout_ms: 60000
53
+
54
+ runtime:
55
+ scratchpad_budget: 10000
56
+ timezone: America/New_York
57
+ # heartbeat: "0 6-23 * * *" # reserved, not yet implemented
58
+ # daily_summary: "0 22 * * *" # reserved, not yet implemented
59
+
60
+ memory:
61
+ session_retention_days: 7
62
+ journal_retention_days: 365
63
+
64
+ channels:
65
+ primary: cli
66
+
67
+ extensions:
68
+ directories: []
69
+
70
+ # rate_limits:
71
+ # per_minute: 10
72
+ # per_hour: 100
73
+ # per_day: 500
74
+
75
+ # budget:
76
+ # daily_limit_usd: 5.00
77
+ # monthly_limit_usd: 100.00
78
+ # enforce: true
79
+ `;
80
+ }
81
+ function deepMerge(target, source) {
82
+ const result = { ...target };
83
+ for (const key of Object.keys(source)) {
84
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
85
+ result[key] = deepMerge(
86
+ target[key],
87
+ source[key]
88
+ );
89
+ } else {
90
+ result[key] = source[key];
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+
96
+ export {
97
+ loadConfig,
98
+ writeDefaultConfig
99
+ };
100
+ //# sourceMappingURL=chunk-CHJ5GNZC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport YAML from 'yaml';\nimport { CONFIG_DEFAULTS, HarnessConfigSchema, type HarnessConfig, type DeepPartial } from './types.js';\n\nconst CONFIG_FILENAMES = ['config.yaml', 'config.yml', 'harness.yaml', 'harness.yml'];\n\nexport function loadConfig(dir: string, overrides?: DeepPartial<HarnessConfig>): HarnessConfig {\n let raw: Record<string, unknown> = {};\n\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(dir, filename);\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n raw = YAML.parse(content) || {};\n break;\n }\n }\n\n // Deep merge: defaults <- file <- overrides\n let merged = deepMerge(\n CONFIG_DEFAULTS as unknown as Record<string, unknown>,\n raw,\n ) as unknown as Record<string, unknown>;\n\n if (overrides) {\n merged = deepMerge(\n merged,\n overrides as unknown as Record<string, unknown>,\n );\n }\n\n // Validate with Zod — parse applies defaults and coerces types\n const result = HarnessConfigSchema.safeParse(merged);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join('.')}: ${i.message}`)\n .join('\\n');\n throw new Error(`Invalid config:\\n${issues}`);\n }\n\n return result.data;\n}\n\nexport function writeDefaultConfig(_dir: string, agentName: string = 'my-agent'): string {\n return `# Agent Harness Configuration\nagent:\n name: ${agentName}\n version: \"0.1.0\"\n\nmodel:\n provider: openrouter\n id: anthropic/claude-sonnet-4\n max_tokens: 200000\n max_retries: 2\n # timeout_ms: 60000\n\nruntime:\n scratchpad_budget: 10000\n timezone: America/New_York\n # heartbeat: \"0 6-23 * * *\" # reserved, not yet implemented\n # daily_summary: \"0 22 * * *\" # reserved, not yet implemented\n\nmemory:\n session_retention_days: 7\n journal_retention_days: 365\n\nchannels:\n primary: cli\n\nextensions:\n directories: []\n\n# rate_limits:\n# per_minute: 10\n# per_hour: 100\n# per_day: 500\n\n# budget:\n# daily_limit_usd: 5.00\n# monthly_limit_usd: 100.00\n# enforce: true\n`;\n}\n\nfunction deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {\n const result = { ...target };\n for (const key of Object.keys(source)) {\n if (\n source[key] &&\n typeof source[key] === 'object' &&\n !Array.isArray(source[key]) &&\n target[key] &&\n typeof target[key] === 'object' &&\n !Array.isArray(target[key])\n ) {\n result[key] = deepMerge(\n target[key] as Record<string, unknown>,\n source[key] as Record<string, unknown>,\n );\n } else {\n result[key] = source[key];\n }\n }\n return result;\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,cAAc,kBAAkB;AACzC,SAAS,YAAY;AACrB,OAAO,UAAU;AAGjB,IAAM,mBAAmB,CAAC,eAAe,cAAc,gBAAgB,aAAa;AAE7E,SAAS,WAAW,KAAa,WAAuD;AAC7F,MAAI,MAA+B,CAAC;AAEpC,aAAW,YAAY,kBAAkB;AACvC,UAAM,aAAa,KAAK,KAAK,QAAQ;AACrC,QAAI,WAAW,UAAU,GAAG;AAC1B,YAAM,UAAU,aAAa,YAAY,OAAO;AAChD,YAAM,KAAK,MAAM,OAAO,KAAK,CAAC;AAC9B;AAAA,IACF;AAAA,EACF;AAGA,MAAI,SAAS;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAEA,MAAI,WAAW;AACb,aAAS;AAAA,MACP;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,oBAAoB,UAAU,MAAM;AACnD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAChD,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM;AAAA,EAAoB,MAAM,EAAE;AAAA,EAC9C;AAEA,SAAO,OAAO;AAChB;AAEO,SAAS,mBAAmB,MAAc,YAAoB,YAAoB;AACvF,SAAO;AAAA;AAAA,UAEC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCnB;AAEA,SAAS,UAAU,QAAiC,QAA0D;AAC5G,QAAM,SAAS,EAAE,GAAG,OAAO;AAC3B,aAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,QACE,OAAO,GAAG,KACV,OAAO,OAAO,GAAG,MAAM,YACvB,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,KAC1B,OAAO,GAAG,KACV,OAAO,OAAO,GAAG,MAAM,YACvB,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,GAC1B;AACA,aAAO,GAAG,IAAI;AAAA,QACZ,OAAO,GAAG;AAAA,QACV,OAAO,GAAG;AAAA,MACZ;AAAA,IACF,OAAO;AACL,aAAO,GAAG,IAAI,OAAO,GAAG;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;","names":[]}