@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,168 @@
1
+ #!/usr/bin/env node
2
+
3
+
4
+ // src/core/types.ts
5
+ import { z } from "zod";
6
+ var FrontmatterSchema = z.object({
7
+ id: z.string(),
8
+ tags: z.array(z.string()).default([]),
9
+ created: z.string().optional(),
10
+ updated: z.string().optional(),
11
+ author: z.enum(["human", "agent", "infrastructure"]).default("human"),
12
+ status: z.enum(["active", "archived", "deprecated", "draft"]).default("active"),
13
+ related: z.array(z.string()).default([]),
14
+ schedule: z.string().optional(),
15
+ with: z.string().optional(),
16
+ channel: z.string().optional(),
17
+ duration_minutes: z.number().optional(),
18
+ max_retries: z.number().int().nonnegative().optional(),
19
+ retry_delay_ms: z.number().int().positive().optional()
20
+ });
21
+ var HarnessConfigSchema = z.object({
22
+ agent: z.object({
23
+ name: z.string().min(1),
24
+ version: z.string().default("0.1.0")
25
+ }).passthrough(),
26
+ model: z.object({
27
+ provider: z.string().default("openrouter"),
28
+ id: z.string().min(1),
29
+ max_tokens: z.number().int().positive().default(2e5),
30
+ max_retries: z.number().int().nonnegative().default(2),
31
+ timeout_ms: z.number().int().positive().optional(),
32
+ /** Cheap model for auto-generating summaries, tags, frontmatter (e.g. 'google/gemini-flash-1.5') */
33
+ summary_model: z.string().optional(),
34
+ /** Fast model for validation, checks, and quick decisions (e.g. 'google/gemini-flash-1.5') */
35
+ fast_model: z.string().optional()
36
+ }).passthrough(),
37
+ runtime: z.object({
38
+ scratchpad_budget: z.number().int().nonnegative().default(1e4),
39
+ /** Reserved: cron expression for periodic heartbeat check (not yet implemented) */
40
+ heartbeat: z.string().optional(),
41
+ /** Reserved: cron expression for daily summary generation (not yet implemented) */
42
+ daily_summary: z.string().optional(),
43
+ /** Auto-process primitives on save: generate frontmatter, L0/L1 summaries (default: true) */
44
+ auto_process: z.boolean().default(true),
45
+ quiet_hours: z.object({
46
+ start: z.number().int().min(0).max(23).default(23),
47
+ end: z.number().int().min(0).max(23).default(6)
48
+ }).passthrough().default({ start: 23, end: 6 }),
49
+ timezone: z.string().default("America/New_York")
50
+ }).passthrough(),
51
+ memory: z.object({
52
+ session_retention_days: z.number().int().positive().default(7),
53
+ journal_retention_days: z.number().int().positive().default(365)
54
+ }).passthrough(),
55
+ channels: z.object({
56
+ primary: z.string().default("cli")
57
+ }).passthrough(),
58
+ extensions: z.object({
59
+ directories: z.array(z.string()).default([])
60
+ }).passthrough().default({ directories: [] }),
61
+ rate_limits: z.object({
62
+ /** Max LLM calls per minute (default: unlimited) */
63
+ per_minute: z.number().int().positive().optional(),
64
+ /** Max LLM calls per hour (default: unlimited) */
65
+ per_hour: z.number().int().positive().optional(),
66
+ /** Max LLM calls per day (default: unlimited) */
67
+ per_day: z.number().int().positive().optional()
68
+ }).passthrough().default({}),
69
+ budget: z.object({
70
+ /** Max daily spend in USD (default: unlimited) */
71
+ daily_limit_usd: z.number().positive().optional(),
72
+ /** Max monthly spend in USD (default: unlimited) */
73
+ monthly_limit_usd: z.number().positive().optional(),
74
+ /** Block runs when budget exceeded (default: true) */
75
+ enforce: z.boolean().default(true)
76
+ }).passthrough().default({ enforce: true }),
77
+ mcp: z.object({
78
+ /** MCP server definitions keyed by server name */
79
+ servers: z.record(z.string(), z.object({
80
+ /** Transport type: 'stdio' for local processes, 'http' for remote, 'sse' for SSE */
81
+ transport: z.enum(["stdio", "http", "sse"]),
82
+ /** Command to spawn (stdio transport only) */
83
+ command: z.string().optional(),
84
+ /** Command arguments (stdio transport only) */
85
+ args: z.array(z.string()).optional(),
86
+ /** Environment variables for the spawned process (stdio transport only) */
87
+ env: z.record(z.string(), z.string()).optional(),
88
+ /** Working directory for the spawned process (stdio transport only) */
89
+ cwd: z.string().optional(),
90
+ /** URL endpoint (http/sse transport only) */
91
+ url: z.string().optional(),
92
+ /** Additional HTTP headers (http/sse transport only) */
93
+ headers: z.record(z.string(), z.string()).optional(),
94
+ /** Whether this server is enabled (default: true) */
95
+ enabled: z.boolean().default(true)
96
+ }).passthrough()).default({})
97
+ }).passthrough().default({ servers: {} }),
98
+ /** Intelligence & continuous learning config */
99
+ intelligence: z.object({
100
+ /** Auto-run journal synthesis on a cron schedule (default: off). Set to cron string e.g. "0 22 * * *" or true for default "0 22 * * *". */
101
+ auto_journal: z.union([z.boolean(), z.string()]).default(false),
102
+ /** Auto-run instinct learning after journal synthesis (default: off) */
103
+ auto_learn: z.boolean().default(false)
104
+ }).passthrough().default({ auto_journal: false, auto_learn: false }),
105
+ /** Proactive execution config (scheduler rate-limiting) */
106
+ proactive: z.object({
107
+ /** Enable proactive scheduled workflows (default: false) */
108
+ enabled: z.boolean().default(false),
109
+ /** Max proactive workflow executions per hour (default: 5) */
110
+ max_per_hour: z.number().int().positive().default(5),
111
+ /** Cooldown in minutes between proactive runs of the same workflow (default: 30) */
112
+ cooldown_minutes: z.number().int().nonnegative().default(30),
113
+ /** Override quiet hours for proactive execution (start/end hours, inherits runtime.quiet_hours if not set) */
114
+ quiet_hours: z.object({
115
+ start: z.number().int().min(0).max(23).optional(),
116
+ end: z.number().int().min(0).max(23).optional()
117
+ }).passthrough().optional()
118
+ }).passthrough().default({ enabled: false, max_per_hour: 5, cooldown_minutes: 30 }),
119
+ /** Primitive bundle registries for search/install */
120
+ registries: z.array(z.object({
121
+ /** Registry URL (HTTPS endpoint) */
122
+ url: z.string().url(),
123
+ /** Optional display name */
124
+ name: z.string().optional(),
125
+ /** Optional auth token for private registries */
126
+ token: z.string().optional()
127
+ }).passthrough()).default([])
128
+ }).passthrough();
129
+ var CONFIG_DEFAULTS = {
130
+ agent: { name: "agent", version: "0.1.0" },
131
+ model: { provider: "openrouter", id: "anthropic/claude-sonnet-4", max_tokens: 2e5, max_retries: 2 },
132
+ runtime: {
133
+ scratchpad_budget: 1e4,
134
+ auto_process: true,
135
+ quiet_hours: { start: 23, end: 6 },
136
+ timezone: "America/New_York"
137
+ },
138
+ memory: { session_retention_days: 7, journal_retention_days: 365 },
139
+ channels: { primary: "cli" },
140
+ extensions: { directories: [] },
141
+ rate_limits: {},
142
+ budget: { enforce: true },
143
+ intelligence: { auto_journal: false, auto_learn: false },
144
+ proactive: { enabled: false, max_per_hour: 5, cooldown_minutes: 30 },
145
+ mcp: { servers: {} },
146
+ registries: []
147
+ };
148
+ var CORE_PRIMITIVE_DIRS = ["rules", "instincts", "skills", "playbooks", "workflows", "tools", "agents"];
149
+ function getPrimitiveDirs(config) {
150
+ const dirs = [...CORE_PRIMITIVE_DIRS];
151
+ if (config?.extensions?.directories) {
152
+ for (const dir of config.extensions.directories) {
153
+ if (!dirs.includes(dir)) {
154
+ dirs.push(dir);
155
+ }
156
+ }
157
+ }
158
+ return dirs;
159
+ }
160
+
161
+ export {
162
+ FrontmatterSchema,
163
+ HarnessConfigSchema,
164
+ CONFIG_DEFAULTS,
165
+ CORE_PRIMITIVE_DIRS,
166
+ getPrimitiveDirs
167
+ };
168
+ //# sourceMappingURL=chunk-4CWAGBNS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/types.ts"],"sourcesContent":["import { z } from 'zod';\n\n// --- Frontmatter ---\nexport const FrontmatterSchema = z.object({\n id: z.string(),\n tags: z.array(z.string()).default([]),\n created: z.string().optional(),\n updated: z.string().optional(),\n author: z.enum(['human', 'agent', 'infrastructure']).default('human'),\n status: z.enum(['active', 'archived', 'deprecated', 'draft']).default('active'),\n related: z.array(z.string()).default([]),\n schedule: z.string().optional(),\n with: z.string().optional(),\n channel: z.string().optional(),\n duration_minutes: z.number().optional(),\n max_retries: z.number().int().nonnegative().optional(),\n retry_delay_ms: z.number().int().positive().optional(),\n});\n\nexport type Frontmatter = z.infer<typeof FrontmatterSchema>;\n\n// --- Primitive Document ---\nexport interface HarnessDocument {\n path: string;\n frontmatter: Frontmatter;\n l0: string;\n l1: string;\n body: string;\n raw: string;\n}\n\n// --- Primitive Types ---\nexport type PrimitiveType =\n | 'rule'\n | 'instinct'\n | 'skill'\n | 'playbook'\n | 'workflow'\n | 'tool'\n | 'agent'\n | 'session'\n | 'journal';\n\nexport interface Primitive {\n type: PrimitiveType;\n doc: HarnessDocument;\n}\n\n// --- Config ---\nexport const HarnessConfigSchema = z.object({\n agent: z.object({\n name: z.string().min(1),\n version: z.string().default('0.1.0'),\n }).passthrough(),\n model: z.object({\n provider: z.string().default('openrouter'),\n id: z.string().min(1),\n max_tokens: z.number().int().positive().default(200000),\n max_retries: z.number().int().nonnegative().default(2),\n timeout_ms: z.number().int().positive().optional(),\n /** Cheap model for auto-generating summaries, tags, frontmatter (e.g. 'google/gemini-flash-1.5') */\n summary_model: z.string().optional(),\n /** Fast model for validation, checks, and quick decisions (e.g. 'google/gemini-flash-1.5') */\n fast_model: z.string().optional(),\n }).passthrough(),\n runtime: z.object({\n scratchpad_budget: z.number().int().nonnegative().default(10000),\n /** Reserved: cron expression for periodic heartbeat check (not yet implemented) */\n heartbeat: z.string().optional(),\n /** Reserved: cron expression for daily summary generation (not yet implemented) */\n daily_summary: z.string().optional(),\n /** Auto-process primitives on save: generate frontmatter, L0/L1 summaries (default: true) */\n auto_process: z.boolean().default(true),\n quiet_hours: z.object({\n start: z.number().int().min(0).max(23).default(23),\n end: z.number().int().min(0).max(23).default(6),\n }).passthrough().default({ start: 23, end: 6 }),\n timezone: z.string().default('America/New_York'),\n }).passthrough(),\n memory: z.object({\n session_retention_days: z.number().int().positive().default(7),\n journal_retention_days: z.number().int().positive().default(365),\n }).passthrough(),\n channels: z.object({\n primary: z.string().default('cli'),\n }).passthrough(),\n extensions: z.object({\n directories: z.array(z.string()).default([]),\n }).passthrough().default({ directories: [] }),\n rate_limits: z.object({\n /** Max LLM calls per minute (default: unlimited) */\n per_minute: z.number().int().positive().optional(),\n /** Max LLM calls per hour (default: unlimited) */\n per_hour: z.number().int().positive().optional(),\n /** Max LLM calls per day (default: unlimited) */\n per_day: z.number().int().positive().optional(),\n }).passthrough().default({}),\n budget: z.object({\n /** Max daily spend in USD (default: unlimited) */\n daily_limit_usd: z.number().positive().optional(),\n /** Max monthly spend in USD (default: unlimited) */\n monthly_limit_usd: z.number().positive().optional(),\n /** Block runs when budget exceeded (default: true) */\n enforce: z.boolean().default(true),\n }).passthrough().default({ enforce: true }),\n mcp: z.object({\n /** MCP server definitions keyed by server name */\n servers: z.record(z.string(), z.object({\n /** Transport type: 'stdio' for local processes, 'http' for remote, 'sse' for SSE */\n transport: z.enum(['stdio', 'http', 'sse']),\n /** Command to spawn (stdio transport only) */\n command: z.string().optional(),\n /** Command arguments (stdio transport only) */\n args: z.array(z.string()).optional(),\n /** Environment variables for the spawned process (stdio transport only) */\n env: z.record(z.string(), z.string()).optional(),\n /** Working directory for the spawned process (stdio transport only) */\n cwd: z.string().optional(),\n /** URL endpoint (http/sse transport only) */\n url: z.string().optional(),\n /** Additional HTTP headers (http/sse transport only) */\n headers: z.record(z.string(), z.string()).optional(),\n /** Whether this server is enabled (default: true) */\n enabled: z.boolean().default(true),\n }).passthrough()).default({}),\n }).passthrough().default({ servers: {} }),\n /** Intelligence & continuous learning config */\n intelligence: z.object({\n /** Auto-run journal synthesis on a cron schedule (default: off). Set to cron string e.g. \"0 22 * * *\" or true for default \"0 22 * * *\". */\n auto_journal: z.union([z.boolean(), z.string()]).default(false),\n /** Auto-run instinct learning after journal synthesis (default: off) */\n auto_learn: z.boolean().default(false),\n }).passthrough().default({ auto_journal: false, auto_learn: false }),\n /** Proactive execution config (scheduler rate-limiting) */\n proactive: z.object({\n /** Enable proactive scheduled workflows (default: false) */\n enabled: z.boolean().default(false),\n /** Max proactive workflow executions per hour (default: 5) */\n max_per_hour: z.number().int().positive().default(5),\n /** Cooldown in minutes between proactive runs of the same workflow (default: 30) */\n cooldown_minutes: z.number().int().nonnegative().default(30),\n /** Override quiet hours for proactive execution (start/end hours, inherits runtime.quiet_hours if not set) */\n quiet_hours: z.object({\n start: z.number().int().min(0).max(23).optional(),\n end: z.number().int().min(0).max(23).optional(),\n }).passthrough().optional(),\n }).passthrough().default({ enabled: false, max_per_hour: 5, cooldown_minutes: 30 }),\n /** Primitive bundle registries for search/install */\n registries: z.array(z.object({\n /** Registry URL (HTTPS endpoint) */\n url: z.string().url(),\n /** Optional display name */\n name: z.string().optional(),\n /** Optional auth token for private registries */\n token: z.string().optional(),\n }).passthrough()).default([]),\n}).passthrough();\n\nexport type HarnessConfig = z.infer<typeof HarnessConfigSchema>;\n\nexport const CONFIG_DEFAULTS: HarnessConfig = {\n agent: { name: 'agent', version: '0.1.0' },\n model: { provider: 'openrouter', id: 'anthropic/claude-sonnet-4', max_tokens: 200000, max_retries: 2 },\n runtime: {\n scratchpad_budget: 10000,\n auto_process: true,\n quiet_hours: { start: 23, end: 6 },\n timezone: 'America/New_York',\n },\n memory: { session_retention_days: 7, journal_retention_days: 365 },\n channels: { primary: 'cli' },\n extensions: { directories: [] },\n rate_limits: {},\n budget: { enforce: true },\n intelligence: { auto_journal: false, auto_learn: false },\n proactive: { enabled: false, max_per_hour: 5, cooldown_minutes: 30 },\n mcp: { servers: {} },\n registries: [],\n};\n\nexport const CORE_PRIMITIVE_DIRS = ['rules', 'instincts', 'skills', 'playbooks', 'workflows', 'tools', 'agents'] as const;\n\nexport function getPrimitiveDirs(config?: HarnessConfig): string[] {\n const dirs: string[] = [...CORE_PRIMITIVE_DIRS];\n if (config?.extensions?.directories) {\n for (const dir of config.extensions.directories) {\n if (!dirs.includes(dir)) {\n dirs.push(dir);\n }\n }\n }\n return dirs;\n}\n\n// --- Agent State ---\nexport interface AgentState {\n mode: string;\n goals: string[];\n active_workflows: string[];\n last_interaction: string;\n unfinished_business: string[];\n}\n\n// --- Context Budget ---\nexport interface ContextBudget {\n max_tokens: number;\n used_tokens: number;\n remaining: number;\n loaded_files: string[];\n}\n\n// --- Utility Types ---\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n// --- Lifecycle Hooks ---\nexport interface HarnessHooks {\n /** Called after boot completes (context loaded, state ready) */\n onBoot?: (context: { agent: HarnessAgent; config: HarnessConfig; state: AgentState }) => void | Promise<void>;\n /** Called after each session completes (run or stream) */\n onSessionEnd?: (context: { agent: HarnessAgent; sessionId: string; prompt: string; result: AgentRunResult }) => void | Promise<void>;\n /** Called when an error occurs during run/stream */\n onError?: (context: { agent: HarnessAgent; error: Error; prompt?: string }) => void | Promise<void>;\n /** Called when agent state changes (boot, shutdown, after run) */\n onStateChange?: (context: { agent: HarnessAgent; previous: string; current: string }) => void | Promise<void>;\n /** Called before shutdown completes */\n onShutdown?: (context: { agent: HarnessAgent; state: AgentState }) => void | Promise<void>;\n}\n\n// --- Tool Executor Config (inline to avoid circular deps) ---\nexport interface ToolExecutorOptions {\n /** Maximum tool calls per run (default: 5) */\n maxToolCalls?: number;\n /** Timeout per tool call in ms (default: 30000) */\n toolTimeoutMs?: number;\n /** Whether to allow HTTP tool execution (default: true) */\n allowHttpExecution?: boolean;\n}\n\n// --- Agent Options (programmatic API) ---\nexport interface CreateHarnessOptions {\n dir: string;\n /** Model ID override (e.g., \"claude-sonnet-4-20250514\" or \"gpt-4o\") */\n model?: string;\n /** Provider override (e.g., \"anthropic\", \"openai\", \"openrouter\") */\n provider?: string;\n apiKey?: string;\n config?: DeepPartial<HarnessConfig>;\n /** Lifecycle hooks for agent events */\n hooks?: HarnessHooks;\n /** Tool execution configuration */\n toolExecutor?: ToolExecutorOptions;\n}\n\n/** Record of a single tool call made during a run */\nexport interface ToolCallInfo {\n toolName: string;\n args: Record<string, unknown>;\n result: unknown;\n}\n\n// --- Agent Interface ---\nexport interface AgentRunResult {\n text: string;\n usage: { inputTokens: number; outputTokens: number; totalTokens: number };\n session_id: string;\n steps: number;\n /** Tool calls made during the run (empty array if none) */\n toolCalls: ToolCallInfo[];\n}\n\nexport interface AgentStreamResult {\n /** Async iterable of text chunks — consume with for-await */\n textStream: AsyncIterable<string>;\n /** Resolves after the stream is fully consumed with session metadata */\n result: Promise<AgentRunResult>;\n}\n\nexport interface HarnessAgent {\n name: string;\n config: HarnessConfig;\n boot(): Promise<void>;\n run(prompt: string): Promise<AgentRunResult>;\n stream(prompt: string): AgentStreamResult;\n shutdown(): Promise<void>;\n getSystemPrompt(): string;\n getState(): AgentState;\n}\n\n// --- Index Entry ---\nexport interface IndexEntry {\n id: string;\n path: string;\n tags: string[];\n l0: string;\n created: string;\n status: string;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ loadDirectory
5
+ } from "./chunk-UPLBF4RZ.js";
6
+ import {
7
+ CORE_PRIMITIVE_DIRS
8
+ } from "./chunk-4CWAGBNS.js";
9
+
10
+ // src/runtime/indexer.ts
11
+ import { writeFileSync, existsSync, mkdirSync } from "fs";
12
+ import { join } from "path";
13
+ function buildIndex(harnessDir, directory) {
14
+ const dirPath = join(harnessDir, directory);
15
+ if (!existsSync(dirPath)) return [];
16
+ const docs = loadDirectory(dirPath);
17
+ return docs.map((doc) => ({
18
+ id: doc.frontmatter.id,
19
+ path: doc.path,
20
+ tags: doc.frontmatter.tags,
21
+ l0: doc.l0,
22
+ created: doc.frontmatter.created || "",
23
+ status: doc.frontmatter.status
24
+ }));
25
+ }
26
+ function writeIndexFile(harnessDir, directory, options) {
27
+ const entries = buildIndex(harnessDir, directory);
28
+ const dirPath = join(harnessDir, directory);
29
+ const maxLen = options?.summaryMaxLength ?? 120;
30
+ if (!existsSync(dirPath)) {
31
+ mkdirSync(dirPath, { recursive: true });
32
+ }
33
+ const lines = [
34
+ `<!-- Auto-generated index for ${directory}. Do not edit manually. -->`,
35
+ "",
36
+ `# ${directory.charAt(0).toUpperCase() + directory.slice(1)} Index`,
37
+ "",
38
+ `| ID | Tags | Created | Status | Summary |`,
39
+ `|----|------|---------|--------|---------|`
40
+ ];
41
+ for (const entry of entries) {
42
+ const tags = entry.tags.join(", ");
43
+ const summary = entry.l0.length > maxLen ? entry.l0.slice(0, maxLen - 3) + "..." : entry.l0;
44
+ lines.push(`| ${entry.id} | ${tags} | ${entry.created} | ${entry.status} | ${summary} |`);
45
+ }
46
+ lines.push("");
47
+ writeFileSync(join(dirPath, "_index.md"), lines.join("\n"), "utf-8");
48
+ }
49
+ function rebuildAllIndexes(harnessDir, extraDirs) {
50
+ const dirs = [...CORE_PRIMITIVE_DIRS];
51
+ if (extraDirs) {
52
+ for (const dir of extraDirs) {
53
+ if (!dirs.includes(dir)) dirs.push(dir);
54
+ }
55
+ }
56
+ for (const dir of dirs) {
57
+ const dirPath = join(harnessDir, dir);
58
+ if (existsSync(dirPath)) {
59
+ writeIndexFile(harnessDir, dir);
60
+ }
61
+ }
62
+ }
63
+
64
+ export {
65
+ buildIndex,
66
+ writeIndexFile,
67
+ rebuildAllIndexes
68
+ };
69
+ //# sourceMappingURL=chunk-4FDUOGSZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/indexer.ts"],"sourcesContent":["import { writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { loadDirectory } from '../primitives/loader.js';\nimport { CORE_PRIMITIVE_DIRS, type IndexEntry } from '../core/types.js';\n\nexport function buildIndex(harnessDir: string, directory: string): IndexEntry[] {\n const dirPath = join(harnessDir, directory);\n if (!existsSync(dirPath)) return [];\n\n const docs = loadDirectory(dirPath);\n\n return docs.map((doc) => ({\n id: doc.frontmatter.id,\n path: doc.path,\n tags: doc.frontmatter.tags,\n l0: doc.l0,\n created: doc.frontmatter.created || '',\n status: doc.frontmatter.status,\n }));\n}\n\nexport interface IndexOptions {\n /** Max characters for L0 summary in index table. Defaults to 120. */\n summaryMaxLength?: number;\n}\n\nexport function writeIndexFile(harnessDir: string, directory: string, options?: IndexOptions): void {\n const entries = buildIndex(harnessDir, directory);\n const dirPath = join(harnessDir, directory);\n const maxLen = options?.summaryMaxLength ?? 120;\n\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true });\n }\n\n const lines: string[] = [\n `<!-- Auto-generated index for ${directory}. Do not edit manually. -->`,\n '',\n `# ${directory.charAt(0).toUpperCase() + directory.slice(1)} Index`,\n '',\n `| ID | Tags | Created | Status | Summary |`,\n `|----|------|---------|--------|---------|`,\n ];\n\n for (const entry of entries) {\n const tags = entry.tags.join(', ');\n const summary = entry.l0.length > maxLen ? entry.l0.slice(0, maxLen - 3) + '...' : entry.l0;\n lines.push(`| ${entry.id} | ${tags} | ${entry.created} | ${entry.status} | ${summary} |`);\n }\n\n lines.push('');\n\n writeFileSync(join(dirPath, '_index.md'), lines.join('\\n'), 'utf-8');\n}\n\nexport function rebuildAllIndexes(harnessDir: string, extraDirs?: string[]): void {\n const dirs: string[] = [...CORE_PRIMITIVE_DIRS];\n if (extraDirs) {\n for (const dir of extraDirs) {\n if (!dirs.includes(dir)) dirs.push(dir);\n }\n }\n for (const dir of dirs) {\n const dirPath = join(harnessDir, dir);\n if (existsSync(dirPath)) {\n writeIndexFile(harnessDir, dir);\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,eAAe,YAAY,iBAAiB;AACrD,SAAS,YAAY;AAId,SAAS,WAAW,YAAoB,WAAiC;AAC9E,QAAM,UAAU,KAAK,YAAY,SAAS;AAC1C,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO,CAAC;AAElC,QAAM,OAAO,cAAc,OAAO;AAElC,SAAO,KAAK,IAAI,CAAC,SAAS;AAAA,IACxB,IAAI,IAAI,YAAY;AAAA,IACpB,MAAM,IAAI;AAAA,IACV,MAAM,IAAI,YAAY;AAAA,IACtB,IAAI,IAAI;AAAA,IACR,SAAS,IAAI,YAAY,WAAW;AAAA,IACpC,QAAQ,IAAI,YAAY;AAAA,EAC1B,EAAE;AACJ;AAOO,SAAS,eAAe,YAAoB,WAAmB,SAA8B;AAClG,QAAM,UAAU,WAAW,YAAY,SAAS;AAChD,QAAM,UAAU,KAAK,YAAY,SAAS;AAC1C,QAAM,SAAS,SAAS,oBAAoB;AAE5C,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,cAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EACxC;AAEA,QAAM,QAAkB;AAAA,IACtB,iCAAiC,SAAS;AAAA,IAC1C;AAAA,IACA,KAAK,UAAU,OAAO,CAAC,EAAE,YAAY,IAAI,UAAU,MAAM,CAAC,CAAC;AAAA,IAC3D;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,SAAS,SAAS;AAC3B,UAAM,OAAO,MAAM,KAAK,KAAK,IAAI;AACjC,UAAM,UAAU,MAAM,GAAG,SAAS,SAAS,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,IAAI,QAAQ,MAAM;AACzF,UAAM,KAAK,KAAK,MAAM,EAAE,MAAM,IAAI,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM,OAAO,IAAI;AAAA,EAC1F;AAEA,QAAM,KAAK,EAAE;AAEb,gBAAc,KAAK,SAAS,WAAW,GAAG,MAAM,KAAK,IAAI,GAAG,OAAO;AACrE;AAEO,SAAS,kBAAkB,YAAoB,WAA4B;AAChF,QAAM,OAAiB,CAAC,GAAG,mBAAmB;AAC9C,MAAI,WAAW;AACb,eAAW,OAAO,WAAW;AAC3B,UAAI,CAAC,KAAK,SAAS,GAAG,EAAG,MAAK,KAAK,GAAG;AAAA,IACxC;AAAA,EACF;AACA,aAAW,OAAO,MAAM;AACtB,UAAM,UAAU,KAAK,YAAY,GAAG;AACpC,QAAI,WAAW,OAAO,GAAG;AACvB,qBAAe,YAAY,GAAG;AAAA,IAChC;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ getGlobalLogLevel,
5
+ log
6
+ } from "./chunk-BSKDOFRT.js";
7
+
8
+ // src/runtime/mcp.ts
9
+ import { createMCPClient } from "@ai-sdk/mcp";
10
+ import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
11
+ function buildClientConfig(name, serverConfig) {
12
+ switch (serverConfig.transport) {
13
+ case "stdio": {
14
+ if (!serverConfig.command) {
15
+ throw new Error(`MCP server "${name}": stdio transport requires "command" field`);
16
+ }
17
+ const stderr = getGlobalLogLevel() === "debug" ? "inherit" : "pipe";
18
+ return {
19
+ transport: new Experimental_StdioMCPTransport({
20
+ command: serverConfig.command,
21
+ args: serverConfig.args,
22
+ env: serverConfig.env ? { ...process.env, ...serverConfig.env } : void 0,
23
+ cwd: serverConfig.cwd,
24
+ stderr
25
+ }),
26
+ name: `harness-mcp-${name}`
27
+ };
28
+ }
29
+ case "http": {
30
+ if (!serverConfig.url) {
31
+ throw new Error(`MCP server "${name}": http transport requires "url" field`);
32
+ }
33
+ return {
34
+ transport: {
35
+ type: "http",
36
+ url: serverConfig.url,
37
+ headers: serverConfig.headers
38
+ },
39
+ name: `harness-mcp-${name}`
40
+ };
41
+ }
42
+ case "sse": {
43
+ if (!serverConfig.url) {
44
+ throw new Error(`MCP server "${name}": sse transport requires "url" field`);
45
+ }
46
+ return {
47
+ transport: {
48
+ type: "sse",
49
+ url: serverConfig.url,
50
+ headers: serverConfig.headers
51
+ },
52
+ name: `harness-mcp-${name}`
53
+ };
54
+ }
55
+ default:
56
+ throw new Error(`MCP server "${name}": unknown transport "${serverConfig.transport}"`);
57
+ }
58
+ }
59
+ async function connectToServer(name, serverConfig) {
60
+ const clientConfig = buildClientConfig(name, serverConfig);
61
+ const client = await createMCPClient(clientConfig);
62
+ const tools = await client.tools();
63
+ const toolCount = Object.keys(tools).length;
64
+ return { name, client, toolCount, tools };
65
+ }
66
+ function createMcpManager(config) {
67
+ const servers = config.mcp?.servers ?? {};
68
+ const connections = [];
69
+ const summaries = [];
70
+ return {
71
+ hasServers() {
72
+ return Object.keys(servers).length > 0;
73
+ },
74
+ async connect() {
75
+ const entries = Object.entries(servers);
76
+ if (entries.length === 0) {
77
+ return;
78
+ }
79
+ log.info(`Connecting to ${entries.length} MCP server(s)...`);
80
+ for (const [name, serverConfig] of entries) {
81
+ const enabled = serverConfig.enabled !== false;
82
+ if (!enabled) {
83
+ summaries.push({
84
+ name,
85
+ transport: serverConfig.transport,
86
+ enabled: false,
87
+ connected: false,
88
+ toolCount: 0,
89
+ toolNames: []
90
+ });
91
+ log.debug(`MCP server "${name}" is disabled, skipping`);
92
+ continue;
93
+ }
94
+ try {
95
+ const connection = await connectToServer(name, serverConfig);
96
+ connections.push(connection);
97
+ const toolNames = Object.keys(connection.tools);
98
+ summaries.push({
99
+ name,
100
+ transport: serverConfig.transport,
101
+ enabled: true,
102
+ connected: true,
103
+ toolCount: connection.toolCount,
104
+ toolNames
105
+ });
106
+ log.info(`MCP "${name}": connected, ${connection.toolCount} tool(s) [${toolNames.join(", ")}]`);
107
+ } catch (err) {
108
+ const message = err instanceof Error ? err.message : String(err);
109
+ summaries.push({
110
+ name,
111
+ transport: serverConfig.transport,
112
+ enabled: true,
113
+ connected: false,
114
+ toolCount: 0,
115
+ toolNames: [],
116
+ error: message
117
+ });
118
+ log.warn(`MCP "${name}": connection failed \u2014 ${message}`);
119
+ }
120
+ }
121
+ const totalTools = connections.reduce((sum, c) => sum + c.toolCount, 0);
122
+ if (totalTools > 0) {
123
+ log.info(`MCP: ${totalTools} tool(s) loaded from ${connections.length} server(s)`);
124
+ }
125
+ },
126
+ getTools() {
127
+ const merged = {};
128
+ for (const connection of connections) {
129
+ Object.assign(merged, connection.tools);
130
+ }
131
+ return merged;
132
+ },
133
+ getSummaries() {
134
+ const serverNames = Object.keys(servers);
135
+ const checkedNames = new Set(summaries.map((s) => s.name));
136
+ for (const name of serverNames) {
137
+ if (!checkedNames.has(name)) {
138
+ const serverConfig = servers[name];
139
+ summaries.push({
140
+ name,
141
+ transport: serverConfig.transport,
142
+ enabled: serverConfig.enabled !== false,
143
+ connected: false,
144
+ toolCount: 0,
145
+ toolNames: []
146
+ });
147
+ }
148
+ }
149
+ return [...summaries];
150
+ },
151
+ async close() {
152
+ const closePromises = connections.map(async (connection) => {
153
+ try {
154
+ await connection.client.close();
155
+ log.debug(`MCP "${connection.name}": closed`);
156
+ } catch (err) {
157
+ const message = err instanceof Error ? err.message : String(err);
158
+ log.warn(`MCP "${connection.name}": close failed \u2014 ${message}`);
159
+ }
160
+ });
161
+ await Promise.all(closePromises);
162
+ connections.length = 0;
163
+ }
164
+ };
165
+ }
166
+ async function loadMcpTools(config) {
167
+ const manager = createMcpManager(config);
168
+ await manager.connect();
169
+ return {
170
+ tools: manager.getTools(),
171
+ summaries: manager.getSummaries(),
172
+ close: () => manager.close()
173
+ };
174
+ }
175
+ function validateMcpConfig(config) {
176
+ const errors = [];
177
+ const servers = config.mcp?.servers ?? {};
178
+ for (const [name, serverConfig] of Object.entries(servers)) {
179
+ if (serverConfig.transport === "stdio") {
180
+ if (!serverConfig.command) {
181
+ errors.push({ server: name, error: 'stdio transport requires "command" field' });
182
+ }
183
+ } else if (serverConfig.transport === "http" || serverConfig.transport === "sse") {
184
+ if (!serverConfig.url) {
185
+ errors.push({ server: name, error: `${serverConfig.transport} transport requires "url" field` });
186
+ }
187
+ } else {
188
+ errors.push({ server: name, error: `unknown transport "${serverConfig.transport}"` });
189
+ }
190
+ }
191
+ return errors;
192
+ }
193
+
194
+ export {
195
+ createMcpManager,
196
+ loadMcpTools,
197
+ validateMcpConfig
198
+ };
199
+ //# sourceMappingURL=chunk-5H34JPMB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/mcp.ts"],"sourcesContent":["import { createMCPClient, type MCPClient, type MCPClientConfig } from '@ai-sdk/mcp';\nimport { Experimental_StdioMCPTransport } from '@ai-sdk/mcp/mcp-stdio';\nimport type { ToolSet } from 'ai';\nimport type { HarnessConfig } from '../core/types.js';\nimport { log, getGlobalLogLevel } from '../core/logger.js';\n\n// --- Types ---\n\n/** Single MCP server configuration (mirrors config schema) */\nexport interface McpServerConfig {\n transport: 'stdio' | 'http' | 'sse';\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n url?: string;\n headers?: Record<string, string>;\n enabled?: boolean;\n}\n\n/** Result of connecting to an MCP server */\nexport interface McpServerConnection {\n name: string;\n client: MCPClient;\n toolCount: number;\n tools: ToolSet;\n}\n\n/** Summary of an MCP server for display */\nexport interface McpServerSummary {\n name: string;\n transport: string;\n enabled: boolean;\n connected: boolean;\n toolCount: number;\n toolNames: string[];\n error?: string;\n}\n\n/** Manages all MCP server connections for a harness */\nexport interface McpManager {\n /** Connect to all enabled MCP servers and load their tools */\n connect(): Promise<void>;\n /** Get merged tool set from all connected servers */\n getTools(): ToolSet;\n /** Get summary of all configured servers */\n getSummaries(): McpServerSummary[];\n /** Close all connected clients */\n close(): Promise<void>;\n /** Check if any servers are configured */\n hasServers(): boolean;\n}\n\n// --- Transport Factory ---\n\n/**\n * Build an MCPClientConfig from a server config entry.\n * Maps transport type to the appropriate transport configuration.\n */\nfunction buildClientConfig(name: string, serverConfig: McpServerConfig): MCPClientConfig {\n switch (serverConfig.transport) {\n case 'stdio': {\n if (!serverConfig.command) {\n throw new Error(`MCP server \"${name}\": stdio transport requires \"command\" field`);\n }\n // Suppress MCP server stderr noise unless --verbose (log level debug)\n const stderr = getGlobalLogLevel() === 'debug' ? 'inherit' as const : 'pipe' as const;\n return {\n transport: new Experimental_StdioMCPTransport({\n command: serverConfig.command,\n args: serverConfig.args,\n env: serverConfig.env ? { ...process.env, ...serverConfig.env } as Record<string, string> : undefined,\n cwd: serverConfig.cwd,\n stderr,\n }),\n name: `harness-mcp-${name}`,\n };\n }\n case 'http': {\n if (!serverConfig.url) {\n throw new Error(`MCP server \"${name}\": http transport requires \"url\" field`);\n }\n return {\n transport: {\n type: 'http' as const,\n url: serverConfig.url,\n headers: serverConfig.headers,\n },\n name: `harness-mcp-${name}`,\n };\n }\n case 'sse': {\n if (!serverConfig.url) {\n throw new Error(`MCP server \"${name}\": sse transport requires \"url\" field`);\n }\n return {\n transport: {\n type: 'sse' as const,\n url: serverConfig.url,\n headers: serverConfig.headers,\n },\n name: `harness-mcp-${name}`,\n };\n }\n default:\n throw new Error(`MCP server \"${name}\": unknown transport \"${serverConfig.transport}\"`);\n }\n}\n\n// --- Connection ---\n\n/**\n * Connect to a single MCP server and load its tools.\n * Returns the connection with tool set, or throws on failure.\n */\nasync function connectToServer(name: string, serverConfig: McpServerConfig): Promise<McpServerConnection> {\n const clientConfig = buildClientConfig(name, serverConfig);\n const client = await createMCPClient(clientConfig);\n\n // Load tools with auto-discovery (no schema pre-definition needed)\n const tools = await client.tools();\n const toolCount = Object.keys(tools).length;\n\n return { name, client, toolCount, tools };\n}\n\n// --- Manager ---\n\n/**\n * Create an MCP manager from harness config.\n * Manages lifecycle of all MCP server connections.\n */\nexport function createMcpManager(config: HarnessConfig): McpManager {\n const servers = config.mcp?.servers ?? {};\n const connections: McpServerConnection[] = [];\n const summaries: McpServerSummary[] = [];\n\n return {\n hasServers(): boolean {\n return Object.keys(servers).length > 0;\n },\n\n async connect(): Promise<void> {\n const entries = Object.entries(servers);\n\n if (entries.length === 0) {\n return;\n }\n\n log.info(`Connecting to ${entries.length} MCP server(s)...`);\n\n for (const [name, serverConfig] of entries) {\n const enabled = serverConfig.enabled !== false;\n\n if (!enabled) {\n summaries.push({\n name,\n transport: serverConfig.transport,\n enabled: false,\n connected: false,\n toolCount: 0,\n toolNames: [],\n });\n log.debug(`MCP server \"${name}\" is disabled, skipping`);\n continue;\n }\n\n try {\n const connection = await connectToServer(name, serverConfig);\n connections.push(connection);\n\n const toolNames = Object.keys(connection.tools);\n summaries.push({\n name,\n transport: serverConfig.transport,\n enabled: true,\n connected: true,\n toolCount: connection.toolCount,\n toolNames,\n });\n\n log.info(`MCP \"${name}\": connected, ${connection.toolCount} tool(s) [${toolNames.join(', ')}]`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n summaries.push({\n name,\n transport: serverConfig.transport,\n enabled: true,\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: message,\n });\n\n log.warn(`MCP \"${name}\": connection failed — ${message}`);\n }\n }\n\n const totalTools = connections.reduce((sum, c) => sum + c.toolCount, 0);\n if (totalTools > 0) {\n log.info(`MCP: ${totalTools} tool(s) loaded from ${connections.length} server(s)`);\n }\n },\n\n getTools(): ToolSet {\n const merged: ToolSet = {};\n for (const connection of connections) {\n Object.assign(merged, connection.tools);\n }\n return merged;\n },\n\n getSummaries(): McpServerSummary[] {\n // Include unchecked servers that haven't been connected yet\n const serverNames = Object.keys(servers);\n const checkedNames = new Set(summaries.map((s) => s.name));\n\n for (const name of serverNames) {\n if (!checkedNames.has(name)) {\n const serverConfig = servers[name];\n summaries.push({\n name,\n transport: serverConfig.transport,\n enabled: serverConfig.enabled !== false,\n connected: false,\n toolCount: 0,\n toolNames: [],\n });\n }\n }\n\n return [...summaries];\n },\n\n async close(): Promise<void> {\n const closePromises = connections.map(async (connection) => {\n try {\n await connection.client.close();\n log.debug(`MCP \"${connection.name}\": closed`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log.warn(`MCP \"${connection.name}\": close failed — ${message}`);\n }\n });\n\n await Promise.all(closePromises);\n connections.length = 0;\n },\n };\n}\n\n/**\n * Load MCP tools from all enabled servers in config.\n * Convenience function for one-shot usage (connects, loads, returns tools + close fn).\n */\nexport async function loadMcpTools(config: HarnessConfig): Promise<{\n tools: ToolSet;\n summaries: McpServerSummary[];\n close: () => Promise<void>;\n}> {\n const manager = createMcpManager(config);\n await manager.connect();\n\n return {\n tools: manager.getTools(),\n summaries: manager.getSummaries(),\n close: () => manager.close(),\n };\n}\n\n/**\n * Validate MCP server configurations without connecting.\n * Returns validation errors for each server.\n */\nexport function validateMcpConfig(config: HarnessConfig): Array<{ server: string; error: string }> {\n const errors: Array<{ server: string; error: string }> = [];\n const servers = config.mcp?.servers ?? {};\n\n for (const [name, serverConfig] of Object.entries(servers)) {\n if (serverConfig.transport === 'stdio') {\n if (!serverConfig.command) {\n errors.push({ server: name, error: 'stdio transport requires \"command\" field' });\n }\n } else if (serverConfig.transport === 'http' || serverConfig.transport === 'sse') {\n if (!serverConfig.url) {\n errors.push({ server: name, error: `${serverConfig.transport} transport requires \"url\" field` });\n }\n } else {\n errors.push({ server: name, error: `unknown transport \"${serverConfig.transport}\"` });\n }\n }\n\n return errors;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+
3
+
4
+ // src/runtime/metrics.ts
5
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
6
+ import { join } from "path";
7
+ var METRICS_FILE = "metrics.json";
8
+ var MAX_RUNS = 1e3;
9
+ function getMetricsPath(harnessDir) {
10
+ return join(harnessDir, "memory", METRICS_FILE);
11
+ }
12
+ function loadMetrics(harnessDir) {
13
+ const metricsPath = getMetricsPath(harnessDir);
14
+ if (!existsSync(metricsPath)) {
15
+ return { runs: [], updated: (/* @__PURE__ */ new Date()).toISOString() };
16
+ }
17
+ try {
18
+ const content = readFileSync(metricsPath, "utf-8");
19
+ const parsed = JSON.parse(content);
20
+ if (typeof parsed === "object" && parsed !== null && "runs" in parsed && Array.isArray(parsed.runs)) {
21
+ return parsed;
22
+ }
23
+ return { runs: [], updated: (/* @__PURE__ */ new Date()).toISOString() };
24
+ } catch {
25
+ return { runs: [], updated: (/* @__PURE__ */ new Date()).toISOString() };
26
+ }
27
+ }
28
+ function saveMetrics(harnessDir, store) {
29
+ const memoryDir = join(harnessDir, "memory");
30
+ if (!existsSync(memoryDir)) {
31
+ mkdirSync(memoryDir, { recursive: true });
32
+ }
33
+ if (store.runs.length > MAX_RUNS) {
34
+ store.runs = store.runs.slice(store.runs.length - MAX_RUNS);
35
+ }
36
+ store.updated = (/* @__PURE__ */ new Date()).toISOString();
37
+ const metricsPath = getMetricsPath(harnessDir);
38
+ writeFileSync(metricsPath, JSON.stringify(store, null, 2), "utf-8");
39
+ }
40
+ function recordRun(harnessDir, run) {
41
+ const store = loadMetrics(harnessDir);
42
+ store.runs.push(run);
43
+ saveMetrics(harnessDir, store);
44
+ }
45
+ function getWorkflowStats(harnessDir, workflowId) {
46
+ const store = loadMetrics(harnessDir);
47
+ const runs = store.runs.filter((r) => r.workflow_id === workflowId);
48
+ if (runs.length === 0) return null;
49
+ const successes = runs.filter((r) => r.success);
50
+ const failures = runs.filter((r) => !r.success);
51
+ const totalDuration = runs.reduce((sum, r) => sum + r.duration_ms, 0);
52
+ const totalTokens = runs.reduce((sum, r) => sum + (r.tokens_used ?? 0), 0);
53
+ const lastSuccess = successes.length > 0 ? successes[successes.length - 1].ended : null;
54
+ const lastFailure = failures.length > 0 ? failures[failures.length - 1].ended : null;
55
+ return {
56
+ workflow_id: workflowId,
57
+ total_runs: runs.length,
58
+ successes: successes.length,
59
+ failures: failures.length,
60
+ success_rate: runs.length > 0 ? successes.length / runs.length : 0,
61
+ avg_duration_ms: runs.length > 0 ? Math.round(totalDuration / runs.length) : 0,
62
+ total_tokens: totalTokens,
63
+ last_run: runs[runs.length - 1].ended,
64
+ last_success: lastSuccess,
65
+ last_failure: lastFailure
66
+ };
67
+ }
68
+ function getAllWorkflowStats(harnessDir) {
69
+ const store = loadMetrics(harnessDir);
70
+ const workflowIds = /* @__PURE__ */ new Set();
71
+ for (const run of store.runs) {
72
+ workflowIds.add(run.workflow_id);
73
+ }
74
+ const stats = [];
75
+ for (const id of workflowIds) {
76
+ const s = getWorkflowStats(harnessDir, id);
77
+ if (s) stats.push(s);
78
+ }
79
+ stats.sort((a, b) => b.last_run.localeCompare(a.last_run));
80
+ return stats;
81
+ }
82
+ function clearMetrics(harnessDir, workflowId) {
83
+ const store = loadMetrics(harnessDir);
84
+ const before = store.runs.length;
85
+ if (workflowId) {
86
+ store.runs = store.runs.filter((r) => r.workflow_id !== workflowId);
87
+ } else {
88
+ store.runs = [];
89
+ }
90
+ saveMetrics(harnessDir, store);
91
+ return before - store.runs.length;
92
+ }
93
+
94
+ export {
95
+ loadMetrics,
96
+ saveMetrics,
97
+ recordRun,
98
+ getWorkflowStats,
99
+ getAllWorkflowStats,
100
+ clearMetrics
101
+ };
102
+ //# sourceMappingURL=chunk-6EMOEYGU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/metrics.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\n\nexport interface WorkflowRun {\n workflow_id: string;\n started: string;\n ended: string;\n duration_ms: number;\n success: boolean;\n error?: string;\n tokens_used?: number;\n attempt: number;\n max_retries: number;\n}\n\nexport interface MetricsStore {\n runs: WorkflowRun[];\n updated: string;\n}\n\nexport interface WorkflowStats {\n workflow_id: string;\n total_runs: number;\n successes: number;\n failures: number;\n success_rate: number;\n avg_duration_ms: number;\n total_tokens: number;\n last_run: string;\n last_success: string | null;\n last_failure: string | null;\n}\n\nconst METRICS_FILE = 'metrics.json';\nconst MAX_RUNS = 1000;\n\nfunction getMetricsPath(harnessDir: string): string {\n return join(harnessDir, 'memory', METRICS_FILE);\n}\n\n/**\n * Load metrics from the metrics store file.\n * Returns an empty store if the file doesn't exist.\n */\nexport function loadMetrics(harnessDir: string): MetricsStore {\n const metricsPath = getMetricsPath(harnessDir);\n if (!existsSync(metricsPath)) {\n return { runs: [], updated: new Date().toISOString() };\n }\n\n try {\n const content = readFileSync(metricsPath, 'utf-8');\n const parsed: unknown = JSON.parse(content);\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'runs' in parsed &&\n Array.isArray((parsed as MetricsStore).runs)\n ) {\n return parsed as MetricsStore;\n }\n return { runs: [], updated: new Date().toISOString() };\n } catch {\n return { runs: [], updated: new Date().toISOString() };\n }\n}\n\n/**\n * Save metrics to the store file. Trims to MAX_RUNS most recent entries.\n */\nexport function saveMetrics(harnessDir: string, store: MetricsStore): void {\n const memoryDir = join(harnessDir, 'memory');\n if (!existsSync(memoryDir)) {\n mkdirSync(memoryDir, { recursive: true });\n }\n\n // Keep only the most recent runs\n if (store.runs.length > MAX_RUNS) {\n store.runs = store.runs.slice(store.runs.length - MAX_RUNS);\n }\n\n store.updated = new Date().toISOString();\n const metricsPath = getMetricsPath(harnessDir);\n writeFileSync(metricsPath, JSON.stringify(store, null, 2), 'utf-8');\n}\n\n/**\n * Record a workflow run in the metrics store.\n */\nexport function recordRun(harnessDir: string, run: WorkflowRun): void {\n const store = loadMetrics(harnessDir);\n store.runs.push(run);\n saveMetrics(harnessDir, store);\n}\n\n/**\n * Get aggregated stats for a specific workflow.\n */\nexport function getWorkflowStats(harnessDir: string, workflowId: string): WorkflowStats | null {\n const store = loadMetrics(harnessDir);\n const runs = store.runs.filter((r) => r.workflow_id === workflowId);\n\n if (runs.length === 0) return null;\n\n const successes = runs.filter((r) => r.success);\n const failures = runs.filter((r) => !r.success);\n const totalDuration = runs.reduce((sum, r) => sum + r.duration_ms, 0);\n const totalTokens = runs.reduce((sum, r) => sum + (r.tokens_used ?? 0), 0);\n\n const lastSuccess = successes.length > 0 ? successes[successes.length - 1].ended : null;\n const lastFailure = failures.length > 0 ? failures[failures.length - 1].ended : null;\n\n return {\n workflow_id: workflowId,\n total_runs: runs.length,\n successes: successes.length,\n failures: failures.length,\n success_rate: runs.length > 0 ? successes.length / runs.length : 0,\n avg_duration_ms: runs.length > 0 ? Math.round(totalDuration / runs.length) : 0,\n total_tokens: totalTokens,\n last_run: runs[runs.length - 1].ended,\n last_success: lastSuccess,\n last_failure: lastFailure,\n };\n}\n\n/**\n * Get aggregated stats for all workflows that have been run.\n */\nexport function getAllWorkflowStats(harnessDir: string): WorkflowStats[] {\n const store = loadMetrics(harnessDir);\n\n // Collect unique workflow IDs\n const workflowIds = new Set<string>();\n for (const run of store.runs) {\n workflowIds.add(run.workflow_id);\n }\n\n const stats: WorkflowStats[] = [];\n for (const id of workflowIds) {\n const s = getWorkflowStats(harnessDir, id);\n if (s) stats.push(s);\n }\n\n // Sort by most recent run\n stats.sort((a, b) => b.last_run.localeCompare(a.last_run));\n return stats;\n}\n\n/**\n * Clear all metrics for a specific workflow, or all workflows if no ID given.\n */\nexport function clearMetrics(harnessDir: string, workflowId?: string): number {\n const store = loadMetrics(harnessDir);\n const before = store.runs.length;\n\n if (workflowId) {\n store.runs = store.runs.filter((r) => r.workflow_id !== workflowId);\n } else {\n store.runs = [];\n }\n\n saveMetrics(harnessDir, store);\n return before - store.runs.length;\n}\n"],"mappings":"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","names":[]}