@agntk/agent-harness 0.1.4 → 0.1.6

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 (178) hide show
  1. package/README.md +104 -43
  2. package/dist/{analytics-RPT73WNM.js → analytics-L24W3B7U.js} +1 -2
  3. package/dist/auto-processor-QIRUOGEI.js +12 -0
  4. package/dist/{chunk-UPLBF4RZ.js → chunk-2UVWCTAY.js} +2 -2
  5. package/dist/{chunk-CSL3ERUI.js → chunk-4P6TRFPZ.js} +3 -3
  6. package/dist/chunk-4P6TRFPZ.js.map +1 -0
  7. package/dist/{chunk-4CWAGBNS.js → chunk-4TQQZILG.js} +73 -3
  8. package/dist/chunk-4TQQZILG.js.map +1 -0
  9. package/dist/{chunk-DA7IKHC4.js → chunk-5CO5JTYT.js} +2 -2
  10. package/dist/chunk-5CO5JTYT.js.map +1 -0
  11. package/dist/{chunk-A7BJPQQ6.js → chunk-5O5OGOOQ.js} +2 -2
  12. package/dist/{chunk-UWQTZMNI.js → chunk-7GZ4D6V6.js} +2 -2
  13. package/dist/{chunk-FLZU44SV.js → chunk-AN6Y4MDD.js} +6 -6
  14. package/dist/{chunk-4FDUOGSZ.js → chunk-D7AWV24Z.js} +3 -3
  15. package/dist/{chunk-CHJ5GNZC.js → chunk-EC42HQQH.js} +2 -2
  16. package/dist/{chunk-YIJY5DBV.js → chunk-KLYMGWQJ.js} +4 -4
  17. package/dist/chunk-KLYMGWQJ.js.map +1 -0
  18. package/dist/{chunk-YUFNYN2H.js → chunk-M62KLIEK.js} +4 -4
  19. package/dist/chunk-M62KLIEK.js.map +1 -0
  20. package/dist/{chunk-GJNNR2RA.js → chunk-M6PDMK2O.js} +3 -3
  21. package/dist/{chunk-M7NXUK55.js → chunk-NVC2WY4K.js} +2 -2
  22. package/dist/{chunk-XTBKL5BI.js → chunk-PMFAYKBD.js} +2 -2
  23. package/dist/chunk-PMFAYKBD.js.map +1 -0
  24. package/dist/{chunk-274RV3YO.js → chunk-QMOIVORH.js} +3 -3
  25. package/dist/chunk-QMOIVORH.js.map +1 -0
  26. package/dist/{chunk-RY3ZFII7.js → chunk-SEHAQTBO.js} +6 -6
  27. package/dist/{chunk-MPZ3BPUI.js → chunk-UMXPOYZR.js} +4 -4
  28. package/dist/{chunk-W4T7PGI2.js → chunk-UXCHAS3Z.js} +4 -4
  29. package/dist/cli/index.js +153 -167
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/config-PYSS3QY6.js +12 -0
  32. package/dist/context-loader-RSXXFW5R.js +12 -0
  33. package/dist/{conversation-QDEIDQPH.js → conversation-TBTFIJVU.js} +6 -7
  34. package/dist/{cost-tracker-RS3W7SVY.js → cost-tracker-NZRZEHVA.js} +1 -2
  35. package/dist/{delegate-VJCJLYEK.js → delegate-3KJAL4NZ.js} +7 -8
  36. package/dist/{emotional-state-VQVRA6ED.js → emotional-state-IN4ZUL2Q.js} +1 -2
  37. package/dist/{emotional-state-VQVRA6ED.js.map → emotional-state-IN4ZUL2Q.js.map} +1 -1
  38. package/dist/{env-discovery-2BLVMAIM.js → env-discovery-PXBRE5FX.js} +1 -2
  39. package/dist/{env-discovery-2BLVMAIM.js.map → env-discovery-PXBRE5FX.js.map} +1 -1
  40. package/dist/{export-6GCYHEHQ.js → export-GYLWROMB.js} +3 -4
  41. package/dist/{export-6GCYHEHQ.js.map → export-GYLWROMB.js.map} +1 -1
  42. package/dist/graph-LEEO37L3.js +13 -0
  43. package/dist/{harness-WE4SLCML.js → harness-R5FKRICG.js} +8 -9
  44. package/dist/{health-NZ6WNIMV.js → health-HL2JYHIY.js} +1 -2
  45. package/dist/indexer-L5UC6J2V.js +15 -0
  46. package/dist/{instinct-learner-SRM72DHF.js → instinct-learner-QGAMIS3X.js} +5 -6
  47. package/dist/{intake-4M3HNU43.js → intake-SVJKFHTL.js} +5 -6
  48. package/dist/{intelligence-HJOCA4SJ.js → intelligence-XPV3MC5U.js} +10 -13
  49. package/dist/intelligence-XPV3MC5U.js.map +1 -0
  50. package/dist/{journal-WANJL3MI.js → journal-ITUMKT6U.js} +5 -6
  51. package/dist/{loader-C3TKIKZR.js → loader-27PLDCOJ.js} +3 -4
  52. package/dist/{mcp-WTQJJZAO.js → mcp-JSIUJJZV.js} +1 -2
  53. package/dist/{mcp-discovery-WPAQFL6S.js → mcp-discovery-DG3RQYLM.js} +1 -2
  54. package/dist/{mcp-discovery-WPAQFL6S.js.map → mcp-discovery-DG3RQYLM.js.map} +1 -1
  55. package/dist/{mcp-installer-6O2XXD3V.js → mcp-installer-X2TJ2S2G.js} +3 -4
  56. package/dist/{mcp-installer-6O2XXD3V.js.map → mcp-installer-X2TJ2S2G.js.map} +1 -1
  57. package/dist/{metrics-KXGNFAAB.js → metrics-2MNINXNQ.js} +1 -2
  58. package/dist/{primitive-registry-I6VTIR4W.js → primitive-registry-ZMGGXSO5.js} +3 -4
  59. package/dist/{primitive-registry-I6VTIR4W.js.map → primitive-registry-ZMGGXSO5.js.map} +1 -1
  60. package/dist/{project-discovery-C4UMD7JI.js → project-discovery-FQLAZKEM.js} +1 -2
  61. package/dist/project-discovery-FQLAZKEM.js.map +1 -0
  62. package/dist/{provider-SXPQZ74H.js → provider-HQY6SPZI.js} +1 -2
  63. package/dist/{rate-limiter-RLRVM325.js → rate-limiter-PH5DCVU4.js} +1 -2
  64. package/dist/{rule-engine-YGQ3RYZM.js → rule-engine-DM26S77N.js} +3 -4
  65. package/dist/{rule-engine-YGQ3RYZM.js.map → rule-engine-DM26S77N.js.map} +1 -1
  66. package/dist/{scaffold-A3VRRCBV.js → scaffold-2F36YVW6.js} +5 -6
  67. package/dist/{scaffold-A3VRRCBV.js.map → scaffold-2F36YVW6.js.map} +1 -1
  68. package/dist/{scheduler-XHHIVHRI.js → scheduler-Q7GB2KCW.js} +11 -12
  69. package/dist/{scheduler-XHHIVHRI.js.map → scheduler-Q7GB2KCW.js.map} +1 -1
  70. package/dist/{search-V3W5JMJG.js → search-6Y6NCOLQ.js} +3 -4
  71. package/dist/search-6Y6NCOLQ.js.map +1 -0
  72. package/dist/{semantic-search-2DTOO5UX.js → semantic-search-FN6FZIXI.js} +3 -4
  73. package/dist/semantic-search-FN6FZIXI.js.map +1 -0
  74. package/dist/{serve-DTQ3HENY.js → serve-MXRTP2HE.js} +10 -11
  75. package/dist/serve-MXRTP2HE.js.map +1 -0
  76. package/dist/{sessions-CZGVXKQE.js → sessions-G6SZZXWS.js} +1 -2
  77. package/dist/{sources-RW5DT56F.js → sources-7LDYO5GK.js} +1 -2
  78. package/dist/{starter-packs-76YUVHEU.js → starter-packs-OR7NI5NA.js} +1 -2
  79. package/dist/{starter-packs-76YUVHEU.js.map → starter-packs-OR7NI5NA.js.map} +1 -1
  80. package/dist/{state-GMXILIHW.js → state-25IQEC5C.js} +1 -2
  81. package/dist/{state-merge-NKO5FRBA.js → state-merge-E333OEIQ.js} +1 -2
  82. package/dist/{state-merge-NKO5FRBA.js.map → state-merge-E333OEIQ.js.map} +1 -1
  83. package/dist/{telemetry-UC6PBXC7.js → telemetry-RS2JZUZP.js} +4 -5
  84. package/dist/{tool-executor-MJ7IG7PQ.js → tool-executor-6I5PHQDY.js} +5 -6
  85. package/dist/{tools-DZ4KETET.js → tools-NDFJNVHK.js} +4 -5
  86. package/dist/{types-EW7AIB3R.js → types-NPJZAI72.js} +2 -3
  87. package/dist/{universal-installer-AAXXYM5A.js → universal-installer-LCAZHFZR.js} +91 -7
  88. package/dist/universal-installer-LCAZHFZR.js.map +1 -0
  89. package/dist/validator-LM7RZWSH.js +21 -0
  90. package/dist/{verification-gate-FYXUX6LH.js → verification-gate-2O6DF2B7.js} +3 -4
  91. package/dist/verification-gate-2O6DF2B7.js.map +1 -0
  92. package/dist/{versioning-Z3XNE2Q2.js → versioning-WEGF6KJG.js} +1 -2
  93. package/dist/versioning-WEGF6KJG.js.map +1 -0
  94. package/dist/{watcher-ISJC7YKL.js → watcher-GZWQSWZ6.js} +5 -6
  95. package/dist/{watcher-ISJC7YKL.js.map → watcher-GZWQSWZ6.js.map} +1 -1
  96. package/dist/{web-server-DD7ZOP46.js → web-server-2Y4CHD2W.js} +8 -9
  97. package/package.json +1 -9
  98. package/dist/agent-framework-K4GUIICH.js +0 -344
  99. package/dist/agent-framework-K4GUIICH.js.map +0 -1
  100. package/dist/auto-processor-OLE45UI3.js +0 -13
  101. package/dist/chunk-274RV3YO.js.map +0 -1
  102. package/dist/chunk-4CWAGBNS.js.map +0 -1
  103. package/dist/chunk-CSL3ERUI.js.map +0 -1
  104. package/dist/chunk-DA7IKHC4.js.map +0 -1
  105. package/dist/chunk-DGUM43GV.js +0 -11
  106. package/dist/chunk-FD55B3IO.js +0 -204
  107. package/dist/chunk-FD55B3IO.js.map +0 -1
  108. package/dist/chunk-GUJTBGVS.js +0 -2212
  109. package/dist/chunk-GUJTBGVS.js.map +0 -1
  110. package/dist/chunk-KFX54TQM.js +0 -165
  111. package/dist/chunk-KFX54TQM.js.map +0 -1
  112. package/dist/chunk-XTBKL5BI.js.map +0 -1
  113. package/dist/chunk-YIJY5DBV.js.map +0 -1
  114. package/dist/chunk-YUFNYN2H.js.map +0 -1
  115. package/dist/chunk-ZZJOFKAT.js +0 -13
  116. package/dist/config-WVMRUOCA.js +0 -13
  117. package/dist/context-loader-3ORBPMHJ.js +0 -13
  118. package/dist/graph-YUIPOSOO.js +0 -14
  119. package/dist/harness-LCHA3DWP.js +0 -10
  120. package/dist/index.d.ts +0 -3612
  121. package/dist/index.js +0 -13713
  122. package/dist/index.js.map +0 -1
  123. package/dist/indexer-LONANRRM.js +0 -16
  124. package/dist/intelligence-HJOCA4SJ.js.map +0 -1
  125. package/dist/project-discovery-C4UMD7JI.js.map +0 -1
  126. package/dist/provider-LQHQX7Z7.js +0 -26
  127. package/dist/search-V3W5JMJG.js.map +0 -1
  128. package/dist/semantic-search-2DTOO5UX.js.map +0 -1
  129. package/dist/serve-DTQ3HENY.js.map +0 -1
  130. package/dist/tools-DZ4KETET.js.map +0 -1
  131. package/dist/types-EW7AIB3R.js.map +0 -1
  132. package/dist/types-WGDLSPO6.js +0 -16
  133. package/dist/types-WGDLSPO6.js.map +0 -1
  134. package/dist/universal-installer-AAXXYM5A.js.map +0 -1
  135. package/dist/validator-7WXMDIHH.js +0 -22
  136. package/dist/validator-7WXMDIHH.js.map +0 -1
  137. package/dist/verification-gate-FYXUX6LH.js.map +0 -1
  138. package/dist/versioning-Z3XNE2Q2.js.map +0 -1
  139. package/dist/web-server-DD7ZOP46.js.map +0 -1
  140. /package/dist/{analytics-RPT73WNM.js.map → analytics-L24W3B7U.js.map} +0 -0
  141. /package/dist/{auto-processor-OLE45UI3.js.map → auto-processor-QIRUOGEI.js.map} +0 -0
  142. /package/dist/{chunk-UPLBF4RZ.js.map → chunk-2UVWCTAY.js.map} +0 -0
  143. /package/dist/{chunk-A7BJPQQ6.js.map → chunk-5O5OGOOQ.js.map} +0 -0
  144. /package/dist/{chunk-UWQTZMNI.js.map → chunk-7GZ4D6V6.js.map} +0 -0
  145. /package/dist/{chunk-FLZU44SV.js.map → chunk-AN6Y4MDD.js.map} +0 -0
  146. /package/dist/{chunk-4FDUOGSZ.js.map → chunk-D7AWV24Z.js.map} +0 -0
  147. /package/dist/{chunk-CHJ5GNZC.js.map → chunk-EC42HQQH.js.map} +0 -0
  148. /package/dist/{chunk-GJNNR2RA.js.map → chunk-M6PDMK2O.js.map} +0 -0
  149. /package/dist/{chunk-M7NXUK55.js.map → chunk-NVC2WY4K.js.map} +0 -0
  150. /package/dist/{chunk-RY3ZFII7.js.map → chunk-SEHAQTBO.js.map} +0 -0
  151. /package/dist/{chunk-MPZ3BPUI.js.map → chunk-UMXPOYZR.js.map} +0 -0
  152. /package/dist/{chunk-W4T7PGI2.js.map → chunk-UXCHAS3Z.js.map} +0 -0
  153. /package/dist/{chunk-DGUM43GV.js.map → config-PYSS3QY6.js.map} +0 -0
  154. /package/dist/{chunk-ZZJOFKAT.js.map → context-loader-RSXXFW5R.js.map} +0 -0
  155. /package/dist/{config-WVMRUOCA.js.map → conversation-TBTFIJVU.js.map} +0 -0
  156. /package/dist/{context-loader-3ORBPMHJ.js.map → cost-tracker-NZRZEHVA.js.map} +0 -0
  157. /package/dist/{conversation-QDEIDQPH.js.map → delegate-3KJAL4NZ.js.map} +0 -0
  158. /package/dist/{cost-tracker-RS3W7SVY.js.map → graph-LEEO37L3.js.map} +0 -0
  159. /package/dist/{delegate-VJCJLYEK.js.map → harness-R5FKRICG.js.map} +0 -0
  160. /package/dist/{graph-YUIPOSOO.js.map → health-HL2JYHIY.js.map} +0 -0
  161. /package/dist/{harness-LCHA3DWP.js.map → indexer-L5UC6J2V.js.map} +0 -0
  162. /package/dist/{harness-WE4SLCML.js.map → instinct-learner-QGAMIS3X.js.map} +0 -0
  163. /package/dist/{health-NZ6WNIMV.js.map → intake-SVJKFHTL.js.map} +0 -0
  164. /package/dist/{indexer-LONANRRM.js.map → journal-ITUMKT6U.js.map} +0 -0
  165. /package/dist/{instinct-learner-SRM72DHF.js.map → loader-27PLDCOJ.js.map} +0 -0
  166. /package/dist/{intake-4M3HNU43.js.map → mcp-JSIUJJZV.js.map} +0 -0
  167. /package/dist/{journal-WANJL3MI.js.map → metrics-2MNINXNQ.js.map} +0 -0
  168. /package/dist/{loader-C3TKIKZR.js.map → provider-HQY6SPZI.js.map} +0 -0
  169. /package/dist/{mcp-WTQJJZAO.js.map → rate-limiter-PH5DCVU4.js.map} +0 -0
  170. /package/dist/{metrics-KXGNFAAB.js.map → sessions-G6SZZXWS.js.map} +0 -0
  171. /package/dist/{provider-LQHQX7Z7.js.map → sources-7LDYO5GK.js.map} +0 -0
  172. /package/dist/{provider-SXPQZ74H.js.map → state-25IQEC5C.js.map} +0 -0
  173. /package/dist/{rate-limiter-RLRVM325.js.map → telemetry-RS2JZUZP.js.map} +0 -0
  174. /package/dist/{sessions-CZGVXKQE.js.map → tool-executor-6I5PHQDY.js.map} +0 -0
  175. /package/dist/{sources-RW5DT56F.js.map → tools-NDFJNVHK.js.map} +0 -0
  176. /package/dist/{state-GMXILIHW.js.map → types-NPJZAI72.js.map} +0 -0
  177. /package/dist/{telemetry-UC6PBXC7.js.map → validator-LM7RZWSH.js.map} +0 -0
  178. /package/dist/{tool-executor-MJ7IG7PQ.js.map → web-server-2Y4CHD2W.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/versioning.ts"],"sourcesContent":["import { existsSync, writeFileSync } from 'fs';\nimport { join, relative } from 'path';\nimport { execSync } from 'child_process';\nimport { log } from '../core/logger.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface VersionEntry {\n /** Git commit hash (short) */\n hash: string;\n /** Full commit hash */\n fullHash: string;\n /** Commit message */\n message: string;\n /** Timestamp (ISO string) */\n timestamp: string;\n /** Files changed in this version */\n filesChanged: string[];\n /** Author name */\n author: string;\n /** Tag if one was applied */\n tag?: string;\n}\n\nexport interface VersionLog {\n /** Ordered list of versions (newest first) */\n entries: VersionEntry[];\n /** Current HEAD hash */\n currentHash: string;\n /** Current tag if any */\n currentTag?: string;\n}\n\nexport interface RollbackResult {\n success: boolean;\n /** Hash we rolled back to */\n targetHash: string;\n /** Files that were restored */\n restoredFiles: string[];\n /** Error message if failed */\n error?: string;\n}\n\nexport interface SnapshotResult {\n success: boolean;\n /** New commit hash */\n hash: string;\n /** Files included in snapshot */\n files: string[];\n /** Error if failed */\n error?: string;\n}\n\nexport interface DiffEntry {\n file: string;\n status: 'added' | 'modified' | 'deleted' | 'renamed';\n /** Lines added */\n additions?: number;\n /** Lines deleted */\n deletions?: number;\n}\n\nexport interface VersionDiff {\n from: string;\n to: string;\n entries: DiffEntry[];\n summary: string;\n}\n\n// ─── Git Helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Identity used for internal harness versioning commits. This keeps harness\n * snapshots functional on machines (and CI runners) where the user has no\n * global `user.name` / `user.email` configured. Harness versioning is an\n * internal bookkeeping feature; the commits never leave the user's disk,\n * so a synthetic identity is appropriate and doesn't pollute user git history.\n */\nconst HARNESS_GIT_AUTHOR_NAME = 'agent-harness';\nconst HARNESS_GIT_AUTHOR_EMAIL = 'versioning@agent-harness.local';\n\n/**\n * Execute a git command in the harness directory.\n * Returns stdout as a string, or null if the command failed.\n *\n * All invocations automatically inject a synthetic author identity so commits\n * succeed on machines without global git user config (e.g. CI runners, Docker\n * containers, fresh VMs).\n */\nfunction gitExec(harnessDir: string, args: string): string | null {\n try {\n const result = execSync(\n `git -c user.name=\"${HARNESS_GIT_AUTHOR_NAME}\" -c user.email=\"${HARNESS_GIT_AUTHOR_EMAIL}\" ${args}`,\n {\n cwd: harnessDir,\n encoding: 'utf-8',\n timeout: 10000,\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n return result.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Check if the harness directory is a git repository.\n */\nexport function isGitRepo(harnessDir: string): boolean {\n return gitExec(harnessDir, 'rev-parse --is-inside-work-tree') === 'true';\n}\n\n/**\n * Initialize a git repository in the harness directory.\n * If already initialized, this is a no-op.\n */\nexport function initVersioning(harnessDir: string): boolean {\n if (isGitRepo(harnessDir)) return true;\n\n const result = gitExec(harnessDir, 'init');\n if (result === null) {\n log.warn('Failed to initialize git repository for versioning');\n return false;\n }\n\n // Create .gitignore for non-versioned files\n const gitignorePath = join(harnessDir, '.gitignore');\n if (!existsSync(gitignorePath)) {\n const gitignoreContent = [\n '# Agent harness versioning',\n 'memory/sessions/',\n 'memory/metrics.json',\n 'memory/health.json',\n 'memory/costs.json',\n 'memory/rate-limits.json',\n 'memory/emotional-history.jsonl',\n 'memory/locks/',\n '.installed/',\n 'archive/',\n 'node_modules/',\n ].join('\\n') + '\\n';\n writeFileSync(gitignorePath, gitignoreContent, 'utf-8');\n }\n\n // Initial commit\n gitExec(harnessDir, 'add -A');\n gitExec(harnessDir, 'commit -m \"Initial harness version\" --allow-empty');\n\n return true;\n}\n\n// ─── Snapshot (Commit) ──────────────────────────────────────────────────────\n\n/**\n * Take a versioned snapshot of the current harness state.\n * Stages all changes and creates a git commit.\n *\n * @param harnessDir - Harness directory path\n * @param message - Commit message describing the change\n * @param options.tag - Optional tag to apply to this version\n */\nexport function snapshot(\n harnessDir: string,\n message: string,\n options?: { tag?: string },\n): SnapshotResult {\n if (!isGitRepo(harnessDir)) {\n if (!initVersioning(harnessDir)) {\n return { success: false, hash: '', files: [], error: 'Failed to initialize git repository' };\n }\n }\n\n // Stage all tracked primitive directories\n const dirsToTrack = [\n 'rules', 'instincts', 'skills', 'playbooks', 'workflows',\n 'tools', 'agents', 'memory/journal', 'memory/state.md',\n 'memory/scratch.md', 'memory/state-ownership.json',\n 'memory/emotional-state.json',\n 'CORE.md', 'SYSTEM.md', 'config.yaml',\n ];\n\n const stagedFiles: string[] = [];\n for (const dir of dirsToTrack) {\n const fullPath = join(harnessDir, dir);\n if (existsSync(fullPath)) {\n gitExec(harnessDir, `add \"${dir}\"`);\n stagedFiles.push(dir);\n }\n }\n\n // Check if there are staged changes\n const status = gitExec(harnessDir, 'diff --cached --name-only');\n if (!status || status.length === 0) {\n return { success: true, hash: getHeadHash(harnessDir) ?? '', files: [], error: 'No changes to commit' };\n }\n\n const changedFiles = status.split('\\n').filter(Boolean);\n\n // Commit\n const commitResult = gitExec(harnessDir, `commit -m \"${escapeMessage(message)}\"`);\n if (commitResult === null) {\n return { success: false, hash: '', files: changedFiles, error: 'Git commit failed' };\n }\n\n const hash = getHeadHash(harnessDir) ?? '';\n\n // Tag if requested\n if (options?.tag) {\n gitExec(harnessDir, `tag \"${escapeMessage(options.tag)}\"`);\n }\n\n return { success: true, hash, files: changedFiles };\n}\n\n// ─── Version Log ────────────────────────────────────────────────────────────\n\n/**\n * Get the version history of the harness.\n *\n * @param harnessDir - Harness directory path\n * @param options.limit - Maximum entries to return (default: 50)\n * @param options.file - Filter to a specific file path\n */\nexport function getVersionLog(\n harnessDir: string,\n options?: { limit?: number; file?: string },\n): VersionLog {\n if (!isGitRepo(harnessDir)) {\n return { entries: [], currentHash: '' };\n }\n\n const limit = options?.limit ?? 50;\n const fileFilter = options?.file ? ` -- \"${options.file}\"` : '';\n const format = '%H|%h|%s|%aI|%an';\n\n const logOutput = gitExec(harnessDir, `log --format=\"${format}\" -n ${limit}${fileFilter}`);\n if (!logOutput) {\n return { entries: [], currentHash: getHeadHash(harnessDir) ?? '' };\n }\n\n const entries: VersionEntry[] = [];\n\n for (const line of logOutput.split('\\n')) {\n if (!line.trim()) continue;\n const parts = line.split('|');\n if (parts.length < 5) continue;\n\n const fullHash = parts[0];\n const hash = parts[1];\n\n // Get files changed in this commit\n const filesOutput = gitExec(harnessDir, `diff-tree --no-commit-id --name-only -r ${fullHash}`);\n const filesChanged = filesOutput ? filesOutput.split('\\n').filter(Boolean) : [];\n\n // Check for tags\n const tagOutput = gitExec(harnessDir, `tag --points-at ${fullHash}`);\n const tag = tagOutput && tagOutput.length > 0 ? tagOutput.split('\\n')[0] : undefined;\n\n entries.push({\n hash,\n fullHash,\n message: parts[2],\n timestamp: parts[3],\n author: parts.slice(4).join('|'),\n filesChanged,\n tag,\n });\n }\n\n const currentHash = getHeadHash(harnessDir) ?? '';\n const currentTag = entries.length > 0 && entries[0].tag ? entries[0].tag : undefined;\n\n return { entries, currentHash, currentTag };\n}\n\n// ─── Diff ───────────────────────────────────────────────────────────────────\n\n/**\n * Get the diff between two versions (or between a version and HEAD).\n */\nexport function getVersionDiff(\n harnessDir: string,\n from: string,\n to?: string,\n): VersionDiff {\n if (!isGitRepo(harnessDir)) {\n return { from, to: to ?? 'HEAD', entries: [], summary: 'Not a git repository' };\n }\n\n const target = to ?? 'HEAD';\n const diffOutput = gitExec(harnessDir, `diff --numstat ${from} ${target}`);\n const nameOutput = gitExec(harnessDir, `diff --name-status ${from} ${target}`);\n\n const entries: DiffEntry[] = [];\n\n if (nameOutput) {\n const nameLines = nameOutput.split('\\n').filter(Boolean);\n const statLines = (diffOutput ?? '').split('\\n').filter(Boolean);\n\n for (let i = 0; i < nameLines.length; i++) {\n const nameParts = nameLines[i].split('\\t');\n const statusChar = nameParts[0];\n const file = nameParts[nameParts.length - 1];\n\n let status: DiffEntry['status'] = 'modified';\n if (statusChar === 'A') status = 'added';\n else if (statusChar === 'D') status = 'deleted';\n else if (statusChar.startsWith('R')) status = 'renamed';\n\n const entry: DiffEntry = { file, status };\n\n // Parse numstat for additions/deletions\n if (i < statLines.length) {\n const statParts = statLines[i].split('\\t');\n if (statParts.length >= 2) {\n const adds = parseInt(statParts[0], 10);\n const dels = parseInt(statParts[1], 10);\n if (!isNaN(adds)) entry.additions = adds;\n if (!isNaN(dels)) entry.deletions = dels;\n }\n }\n\n entries.push(entry);\n }\n }\n\n const added = entries.filter((e) => e.status === 'added').length;\n const modified = entries.filter((e) => e.status === 'modified').length;\n const deleted = entries.filter((e) => e.status === 'deleted').length;\n const summary = `${entries.length} file(s) changed: ${added} added, ${modified} modified, ${deleted} deleted`;\n\n return { from, to: target, entries, summary };\n}\n\n// ─── Rollback ───────────────────────────────────────────────────────────────\n\n/**\n * Roll back the harness to a previous version.\n *\n * Creates a new commit that restores files to the state at `targetHash`,\n * preserving full history (no destructive rewrite).\n *\n * @param harnessDir - Harness directory path\n * @param targetHash - Commit hash or tag to roll back to\n */\nexport function rollback(\n harnessDir: string,\n targetHash: string,\n): RollbackResult {\n if (!isGitRepo(harnessDir)) {\n return { success: false, targetHash, restoredFiles: [], error: 'Not a git repository' };\n }\n\n // Verify the target exists\n const resolvedHash = gitExec(harnessDir, `rev-parse --verify ${targetHash}`);\n if (!resolvedHash) {\n return { success: false, targetHash, restoredFiles: [], error: `Invalid version: ${targetHash}` };\n }\n\n // Get files that will change\n const diff = getVersionDiff(harnessDir, 'HEAD', resolvedHash);\n const restoredFiles = diff.entries.map((e) => e.file);\n\n // Restore all tracked files from the target commit\n const restoreResult = gitExec(harnessDir, `checkout ${resolvedHash} -- .`);\n if (restoreResult === null) {\n return { success: false, targetHash: resolvedHash, restoredFiles: [], error: 'Failed to restore files' };\n }\n\n // Stage and commit the rollback\n gitExec(harnessDir, 'add -A');\n const shortHash = resolvedHash.slice(0, 7);\n const commitResult = gitExec(harnessDir, `commit -m \"Rollback to ${shortHash}\" --allow-empty`);\n if (commitResult === null) {\n return { success: false, targetHash: resolvedHash, restoredFiles, error: 'Failed to commit rollback' };\n }\n\n return { success: true, targetHash: resolvedHash, restoredFiles };\n}\n\n// ─── Tag Management ─────────────────────────────────────────────────────────\n\n/**\n * List all version tags.\n */\nexport function listTags(harnessDir: string): Array<{ tag: string; hash: string; message: string }> {\n if (!isGitRepo(harnessDir)) return [];\n\n const output = gitExec(harnessDir, 'tag -l');\n if (!output) return [];\n\n const tags: Array<{ tag: string; hash: string; message: string }> = [];\n\n for (const tag of output.split('\\n').filter(Boolean)) {\n const hash = gitExec(harnessDir, `rev-parse ${tag}`);\n const message = gitExec(harnessDir, `log -1 --format=%s ${tag}`);\n tags.push({\n tag,\n hash: hash ? hash.slice(0, 7) : '',\n message: message ?? '',\n });\n }\n\n return tags;\n}\n\n/**\n * Tag the current version.\n */\nexport function tagVersion(harnessDir: string, tag: string, message?: string): boolean {\n if (!isGitRepo(harnessDir)) return false;\n\n if (message) {\n return gitExec(harnessDir, `tag -a \"${escapeMessage(tag)}\" -m \"${escapeMessage(message)}\"`) !== null;\n }\n return gitExec(harnessDir, `tag \"${escapeMessage(tag)}\"`) !== null;\n}\n\n// ─── Pending Changes ────────────────────────────────────────────────────────\n\n/**\n * Get uncommitted changes in the harness.\n */\nexport function getPendingChanges(harnessDir: string): DiffEntry[] {\n if (!isGitRepo(harnessDir)) return [];\n\n const output = gitExec(harnessDir, 'status --porcelain');\n if (!output) return [];\n\n const entries: DiffEntry[] = [];\n\n for (const line of output.split('\\n').filter(Boolean)) {\n const statusChar = line.substring(0, 2).trim();\n const file = line.substring(3);\n\n let status: DiffEntry['status'] = 'modified';\n if (statusChar === '??' || statusChar === 'A') status = 'added';\n else if (statusChar === 'D') status = 'deleted';\n else if (statusChar.startsWith('R')) status = 'renamed';\n\n entries.push({ file, status });\n }\n\n return entries;\n}\n\n// ─── File History ───────────────────────────────────────────────────────────\n\n/**\n * Get the version history for a specific file.\n */\nexport function getFileHistory(\n harnessDir: string,\n filePath: string,\n options?: { limit?: number },\n): VersionEntry[] {\n const relPath = relative(harnessDir, join(harnessDir, filePath));\n const log = getVersionLog(harnessDir, { limit: options?.limit ?? 20, file: relPath });\n return log.entries;\n}\n\n/**\n * Get the content of a file at a specific version.\n */\nexport function getFileAtVersion(\n harnessDir: string,\n filePath: string,\n hash: string,\n): string | null {\n if (!isGitRepo(harnessDir)) return null;\n\n const relPath = relative(harnessDir, join(harnessDir, filePath));\n return gitExec(harnessDir, `show ${hash}:\"${relPath}\"`);\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction getHeadHash(harnessDir: string): string | null {\n return gitExec(harnessDir, 'rev-parse HEAD');\n}\n\nfunction escapeMessage(msg: string): string {\n return msg.replace(/\"/g, '\\\\\"').replace(/\\n/g, ' ');\n}\n"],"mappings":"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","names":["log"]}
@@ -2,18 +2,17 @@
2
2
 
3
3
  import {
4
4
  writeIndexFile
5
- } from "./chunk-4FDUOGSZ.js";
5
+ } from "./chunk-D7AWV24Z.js";
6
6
  import {
7
7
  autoProcessFile
8
- } from "./chunk-M7NXUK55.js";
9
- import "./chunk-UPLBF4RZ.js";
8
+ } from "./chunk-NVC2WY4K.js";
9
+ import "./chunk-2UVWCTAY.js";
10
10
  import {
11
11
  log
12
12
  } from "./chunk-BSKDOFRT.js";
13
13
  import {
14
14
  CORE_PRIMITIVE_DIRS
15
- } from "./chunk-4CWAGBNS.js";
16
- import "./chunk-ZZJOFKAT.js";
15
+ } from "./chunk-4TQQZILG.js";
17
16
 
18
17
  // src/runtime/watcher.ts
19
18
  import { watch } from "chokidar";
@@ -106,4 +105,4 @@ function createWatcher(options) {
106
105
  export {
107
106
  createWatcher
108
107
  };
109
- //# sourceMappingURL=watcher-ISJC7YKL.js.map
108
+ //# sourceMappingURL=watcher-GZWQSWZ6.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/watcher.ts"],"sourcesContent":["import { watch, type FSWatcher } from 'chokidar';\nimport { relative } from 'path';\nimport { writeIndexFile } from './indexer.js';\nimport { log } from '../core/logger.js';\nimport { CORE_PRIMITIVE_DIRS } from '../core/types.js';\nimport { autoProcessFile, type AutoProcessResult } from './auto-processor.js';\n\nexport interface WatcherOptions {\n harnessDir: string;\n extraDirs?: string[];\n onChange?: (path: string, event: string) => void;\n onIndexRebuild?: (directory: string) => void;\n onError?: (error: Error) => void;\n /** Also watch config.yaml for changes */\n watchConfig?: boolean;\n onConfigChange?: () => void;\n /** Enable auto-processing of primitives on save (default: false) */\n autoProcess?: boolean;\n /** Called after auto-processing a file */\n onAutoProcess?: (result: AutoProcessResult) => void;\n}\n\nexport function createWatcher(options: WatcherOptions): FSWatcher {\n const { harnessDir, extraDirs, onChange, onIndexRebuild, onError, watchConfig, onConfigChange, autoProcess, onAutoProcess } = options;\n\n const watchedDirs: string[] = [...CORE_PRIMITIVE_DIRS];\n if (extraDirs) {\n for (const dir of extraDirs) {\n if (!watchedDirs.includes(dir)) watchedDirs.push(dir);\n }\n }\n\n const patterns: string[] = watchedDirs.map((dir) => `${harnessDir}/${dir}/**/*.md`);\n\n // Optionally watch config.yaml for live reload\n if (watchConfig) {\n patterns.push(`${harnessDir}/config.yaml`);\n }\n\n const watcher = watch(patterns, {\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 500,\n pollInterval: 100,\n },\n });\n\n const handleChange = (filePath: string, event: string) => {\n // Skip index files\n if (filePath.includes('_index.md')) return;\n\n // Config change handler\n if (filePath.endsWith('config.yaml')) {\n log.info('Config file changed');\n try { onConfigChange?.(); } catch (e) {\n log.warn(`onConfigChange callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n return;\n }\n\n const rel = relative(harnessDir, filePath);\n const dir = rel.split('/')[0];\n\n // Auto-process primitives on add/change (before index rebuild so index sees fixes)\n if (autoProcess && event !== 'unlink' && filePath.endsWith('.md') && watchedDirs.includes(dir)) {\n try {\n const processResult = autoProcessFile(filePath, { harnessDir });\n if (processResult.modified) {\n log.info(`Auto-processed ${rel}: ${processResult.fixes.join(', ')}`);\n }\n try { onAutoProcess?.(processResult); } catch (e) {\n log.warn(`onAutoProcess callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n } catch (err) {\n log.warn(`Auto-process failed for ${rel}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (watchedDirs.includes(dir)) {\n // Rebuild index for this directory — wrapped for resilience\n try {\n writeIndexFile(harnessDir, dir);\n try { onIndexRebuild?.(dir); } catch (e) {\n log.warn(`onIndexRebuild callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n log.warn(`Failed to rebuild index for ${dir}: ${error.message}`);\n try { onError?.(error); } catch (e) {\n log.warn(`Watcher onError callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n try { onChange?.(filePath, event); } catch (e) {\n log.warn(`onChange callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n };\n\n watcher.on('add', (path) => handleChange(path, 'add'));\n watcher.on('change', (path) => handleChange(path, 'change'));\n watcher.on('unlink', (path) => handleChange(path, 'unlink'));\n\n // Handle file system errors gracefully\n watcher.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.warn(`File watcher error: ${error.message}`);\n try { onError?.(error); } catch (e) {\n log.warn(`Watcher onError callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n });\n\n return watcher;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/runtime/watcher.ts"],"sourcesContent":["import { watch, type FSWatcher } from 'chokidar';\nimport { relative } from 'path';\nimport { writeIndexFile } from './indexer.js';\nimport { log } from '../core/logger.js';\nimport { CORE_PRIMITIVE_DIRS } from '../core/types.js';\nimport { autoProcessFile, type AutoProcessResult } from './auto-processor.js';\n\nexport interface WatcherOptions {\n harnessDir: string;\n extraDirs?: string[];\n onChange?: (path: string, event: string) => void;\n onIndexRebuild?: (directory: string) => void;\n onError?: (error: Error) => void;\n /** Also watch config.yaml for changes */\n watchConfig?: boolean;\n onConfigChange?: () => void;\n /** Enable auto-processing of primitives on save (default: false) */\n autoProcess?: boolean;\n /** Called after auto-processing a file */\n onAutoProcess?: (result: AutoProcessResult) => void;\n}\n\nexport function createWatcher(options: WatcherOptions): FSWatcher {\n const { harnessDir, extraDirs, onChange, onIndexRebuild, onError, watchConfig, onConfigChange, autoProcess, onAutoProcess } = options;\n\n const watchedDirs: string[] = [...CORE_PRIMITIVE_DIRS];\n if (extraDirs) {\n for (const dir of extraDirs) {\n if (!watchedDirs.includes(dir)) watchedDirs.push(dir);\n }\n }\n\n const patterns: string[] = watchedDirs.map((dir) => `${harnessDir}/${dir}/**/*.md`);\n\n // Optionally watch config.yaml for live reload\n if (watchConfig) {\n patterns.push(`${harnessDir}/config.yaml`);\n }\n\n const watcher = watch(patterns, {\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 500,\n pollInterval: 100,\n },\n });\n\n const handleChange = (filePath: string, event: string) => {\n // Skip index files\n if (filePath.includes('_index.md')) return;\n\n // Config change handler\n if (filePath.endsWith('config.yaml')) {\n log.info('Config file changed');\n try { onConfigChange?.(); } catch (e) {\n log.warn(`onConfigChange callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n return;\n }\n\n const rel = relative(harnessDir, filePath);\n const dir = rel.split('/')[0];\n\n // Auto-process primitives on add/change (before index rebuild so index sees fixes)\n if (autoProcess && event !== 'unlink' && filePath.endsWith('.md') && watchedDirs.includes(dir)) {\n try {\n const processResult = autoProcessFile(filePath, { harnessDir });\n if (processResult.modified) {\n log.info(`Auto-processed ${rel}: ${processResult.fixes.join(', ')}`);\n }\n try { onAutoProcess?.(processResult); } catch (e) {\n log.warn(`onAutoProcess callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n } catch (err) {\n log.warn(`Auto-process failed for ${rel}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (watchedDirs.includes(dir)) {\n // Rebuild index for this directory — wrapped for resilience\n try {\n writeIndexFile(harnessDir, dir);\n try { onIndexRebuild?.(dir); } catch (e) {\n log.warn(`onIndexRebuild callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n log.warn(`Failed to rebuild index for ${dir}: ${error.message}`);\n try { onError?.(error); } catch (e) {\n log.warn(`Watcher onError callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n try { onChange?.(filePath, event); } catch (e) {\n log.warn(`onChange callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n };\n\n watcher.on('add', (path) => handleChange(path, 'add'));\n watcher.on('change', (path) => handleChange(path, 'change'));\n watcher.on('unlink', (path) => handleChange(path, 'unlink'));\n\n // Handle file system errors gracefully\n watcher.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n log.warn(`File watcher error: ${error.message}`);\n try { onError?.(error); } catch (e) {\n log.warn(`Watcher onError callback failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n });\n\n return watcher;\n}\n"],"mappings":"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","names":[]}
@@ -3,26 +3,25 @@
3
3
  import {
4
4
  createWebApp,
5
5
  startWebServer
6
- } from "./chunk-RY3ZFII7.js";
7
- import "./chunk-A7BJPQQ6.js";
6
+ } from "./chunk-SEHAQTBO.js";
7
+ import "./chunk-5O5OGOOQ.js";
8
8
  import "./chunk-6EMOEYGU.js";
9
9
  import "./chunk-GNUSHD2Y.js";
10
10
  import "./chunk-5H34JPMB.js";
11
- import "./chunk-MPZ3BPUI.js";
12
- import "./chunk-UWQTZMNI.js";
11
+ import "./chunk-UMXPOYZR.js";
12
+ import "./chunk-7GZ4D6V6.js";
13
13
  import "./chunk-UDZIS2AQ.js";
14
14
  import "./chunk-DTTXPHFW.js";
15
15
  import "./chunk-Z2PUCXTZ.js";
16
16
  import "./chunk-TAT6JU3X.js";
17
17
  import "./chunk-JKMGYWXB.js";
18
- import "./chunk-UPLBF4RZ.js";
18
+ import "./chunk-2UVWCTAY.js";
19
19
  import "./chunk-BSKDOFRT.js";
20
20
  import "./chunk-IZ6UZ3ZL.js";
21
- import "./chunk-CHJ5GNZC.js";
22
- import "./chunk-4CWAGBNS.js";
23
- import "./chunk-ZZJOFKAT.js";
21
+ import "./chunk-EC42HQQH.js";
22
+ import "./chunk-4TQQZILG.js";
24
23
  export {
25
24
  createWebApp,
26
25
  startWebServer
27
26
  };
28
- //# sourceMappingURL=web-server-DD7ZOP46.js.map
27
+ //# sourceMappingURL=web-server-2Y4CHD2W.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agntk/agent-harness",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "A file-first agent operating system. Build AI agents by editing markdown files, not writing code.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,17 +11,9 @@
11
11
  "url": "https://github.com/Phoenixrr2113/agent-harness/issues"
12
12
  },
13
13
  "type": "module",
14
- "main": "./dist/index.js",
15
- "types": "./dist/index.d.ts",
16
14
  "bin": {
17
15
  "harness": "./dist/cli/index.js"
18
16
  },
19
- "exports": {
20
- ".": {
21
- "import": "./dist/index.js",
22
- "types": "./dist/index.d.ts"
23
- }
24
- },
25
17
  "scripts": {
26
18
  "build": "tsup",
27
19
  "dev": "tsup --watch",
@@ -1,344 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import {
4
- createHarness
5
- } from "./chunk-YIJY5DBV.js";
6
- import "./chunk-AGAAFJEO.js";
7
- import "./chunk-5H34JPMB.js";
8
- import "./chunk-DA7IKHC4.js";
9
- import "./chunk-UWQTZMNI.js";
10
- import "./chunk-UDZIS2AQ.js";
11
- import "./chunk-DTTXPHFW.js";
12
- import "./chunk-Z2PUCXTZ.js";
13
- import "./chunk-TAT6JU3X.js";
14
- import "./chunk-JKMGYWXB.js";
15
- import "./chunk-OC6YSTDX.js";
16
- import "./chunk-XTBKL5BI.js";
17
- import {
18
- loadDirectory
19
- } from "./chunk-UPLBF4RZ.js";
20
- import {
21
- log
22
- } from "./chunk-BSKDOFRT.js";
23
- import "./chunk-IZ6UZ3ZL.js";
24
- import "./chunk-CHJ5GNZC.js";
25
- import "./chunk-4CWAGBNS.js";
26
- import {
27
- __require
28
- } from "./chunk-ZZJOFKAT.js";
29
-
30
- // src/runtime/agent-framework.ts
31
- import { existsSync } from "fs";
32
- import { join } from "path";
33
- function createAgent(definition) {
34
- let booted = false;
35
- const harnessHooks = {};
36
- if (definition.hooks?.onBoot) harnessHooks.onBoot = definition.hooks.onBoot;
37
- if (definition.hooks?.onSessionEnd) harnessHooks.onSessionEnd = definition.hooks.onSessionEnd;
38
- if (definition.hooks?.onError) harnessHooks.onError = definition.hooks.onError;
39
- if (definition.hooks?.onStateChange) harnessHooks.onStateChange = definition.hooks.onStateChange;
40
- if (definition.hooks?.onShutdown) harnessHooks.onShutdown = definition.hooks.onShutdown;
41
- const harnessOptions = {
42
- dir: definition.dir,
43
- model: definition.model,
44
- provider: definition.provider,
45
- apiKey: definition.apiKey,
46
- config: definition.config,
47
- hooks: harnessHooks
48
- };
49
- const harness = createHarness(harnessOptions);
50
- const middleware = definition.middleware ?? [];
51
- async function runWithPipeline(prompt, isStream) {
52
- if (definition.hooks?.beforeRun) {
53
- const hookResult = await definition.hooks.beforeRun({
54
- agent: definedAgent,
55
- prompt,
56
- isStream
57
- });
58
- if (hookResult) {
59
- if (hookResult.reject) {
60
- throw new Error(`Run rejected: ${hookResult.reason || "beforeRun hook rejected"}`);
61
- }
62
- if (hookResult.prompt) {
63
- prompt = hookResult.prompt;
64
- }
65
- }
66
- }
67
- if (definition.guardrails?.enforceRules) {
68
- const violation = checkRuleViolation(definition.dir, prompt);
69
- if (violation) {
70
- throw new Error(`Guardrail violation: ${violation}`);
71
- }
72
- }
73
- if (definition.approval?.requireApproval) {
74
- const needsApproval = definition.approval.requireApproval(prompt);
75
- if (needsApproval) {
76
- if (definition.approval.onApprovalNeeded) {
77
- const approved = await definition.approval.onApprovalNeeded(prompt);
78
- if (!approved) {
79
- throw new Error("Run requires approval but was not approved");
80
- }
81
- } else {
82
- throw new Error("Run requires approval but no onApprovalNeeded handler is configured");
83
- }
84
- }
85
- }
86
- const execute = async () => {
87
- return harness.run(prompt);
88
- };
89
- let result;
90
- if (middleware.length > 0) {
91
- result = await executeMiddleware(middleware, definedAgent, prompt, isStream, execute);
92
- } else {
93
- result = await execute();
94
- }
95
- if (definition.hooks?.afterRun) {
96
- const transformed = await definition.hooks.afterRun({
97
- agent: definedAgent,
98
- prompt,
99
- result
100
- });
101
- if (transformed) {
102
- result = transformed;
103
- }
104
- }
105
- return result;
106
- }
107
- const definedAgent = {
108
- name: definition.name,
109
- get config() {
110
- return harness.config;
111
- },
112
- harness,
113
- definition,
114
- async boot() {
115
- await harness.boot();
116
- booted = true;
117
- },
118
- async run(prompt) {
119
- if (!booted) await definedAgent.boot();
120
- return runWithPipeline(prompt, false);
121
- },
122
- stream(prompt) {
123
- let resolveResult;
124
- let rejectResult;
125
- const resultPromise = new Promise((res, rej) => {
126
- resolveResult = res;
127
- rejectResult = rej;
128
- });
129
- resultPromise.catch(() => {
130
- });
131
- async function* generateStream() {
132
- if (!booted) await definedAgent.boot();
133
- try {
134
- if (definition.hooks?.beforeRun) {
135
- const hookResult = await definition.hooks.beforeRun({
136
- agent: definedAgent,
137
- prompt,
138
- isStream: true
139
- });
140
- if (hookResult?.reject) {
141
- const error = new Error(`Stream rejected: ${hookResult.reason || "beforeRun hook rejected"}`);
142
- rejectResult(error);
143
- throw error;
144
- }
145
- if (hookResult?.prompt) {
146
- prompt = hookResult.prompt;
147
- }
148
- }
149
- if (definition.guardrails?.enforceRules) {
150
- const violation = checkRuleViolation(definition.dir, prompt);
151
- if (violation) {
152
- const error = new Error(`Guardrail violation: ${violation}`);
153
- rejectResult(error);
154
- throw error;
155
- }
156
- }
157
- if (definition.approval?.requireApproval) {
158
- const needsApproval = definition.approval.requireApproval(prompt);
159
- if (needsApproval) {
160
- if (definition.approval.onApprovalNeeded) {
161
- const approved = await definition.approval.onApprovalNeeded(prompt);
162
- if (!approved) {
163
- const error = new Error("Stream requires approval but was not approved");
164
- rejectResult(error);
165
- throw error;
166
- }
167
- } else {
168
- const error = new Error("Stream requires approval but no onApprovalNeeded handler is configured");
169
- rejectResult(error);
170
- throw error;
171
- }
172
- }
173
- }
174
- } catch (err) {
175
- const error = err instanceof Error ? err : new Error(String(err));
176
- rejectResult(error);
177
- throw error;
178
- }
179
- const streamResult = harness.stream(prompt);
180
- let fullText = "";
181
- for await (const chunk of streamResult.textStream) {
182
- fullText += chunk;
183
- yield chunk;
184
- }
185
- let result = await streamResult.result;
186
- if (definition.hooks?.afterRun) {
187
- const transformed = await definition.hooks.afterRun({
188
- agent: definedAgent,
189
- prompt,
190
- result
191
- });
192
- if (transformed) {
193
- result = transformed;
194
- }
195
- }
196
- resolveResult(result);
197
- }
198
- return {
199
- textStream: generateStream(),
200
- result: resultPromise
201
- };
202
- },
203
- async shutdown() {
204
- await harness.shutdown();
205
- booted = false;
206
- },
207
- getState() {
208
- return harness.getState();
209
- },
210
- getSystemPrompt() {
211
- return harness.getSystemPrompt();
212
- },
213
- isBooted() {
214
- return booted;
215
- }
216
- };
217
- return definedAgent;
218
- }
219
- async function executeMiddleware(middlewares, agent, prompt, isStream, execute) {
220
- let index = 0;
221
- const next = async () => {
222
- if (index >= middlewares.length) {
223
- return execute();
224
- }
225
- const mw = middlewares[index++];
226
- return mw({ agent, prompt, isStream }, next);
227
- };
228
- return next();
229
- }
230
- function checkRuleViolation(harnessDir, prompt, options) {
231
- const rulesDir = join(harnessDir, "rules");
232
- if (!existsSync(rulesDir)) return null;
233
- const rules = loadDirectory(rulesDir);
234
- const promptLower = prompt.toLowerCase();
235
- for (const rule of rules) {
236
- if (options?.ruleTags && options.ruleTags.length > 0) {
237
- const hasTag = rule.frontmatter.tags.some(
238
- (t) => options.ruleTags.includes(t.toLowerCase())
239
- );
240
- if (!hasTag) continue;
241
- }
242
- if (rule.frontmatter.status !== "active") continue;
243
- const text = (rule.l0 + "\n" + rule.body).toLowerCase();
244
- const forbiddenPatterns = extractForbiddenActions(text);
245
- for (const forbidden of forbiddenPatterns) {
246
- const keywords = forbidden.split(/\s+/).filter((w) => w.length > 3);
247
- const matchCount = keywords.filter((k) => promptLower.includes(k)).length;
248
- if (keywords.length > 0 && matchCount >= Math.ceil(keywords.length * 0.6)) {
249
- return `Rule "${rule.frontmatter.id}" forbids: "${forbidden}"  prompt matches ${matchCount}/${keywords.length} keywords`;
250
- }
251
- }
252
- }
253
- return null;
254
- }
255
- function extractForbiddenActions(text) {
256
- const actions = [];
257
- const patterns = [
258
- /never\s+(.+?)(?:\.|$)/gm,
259
- /don'?t\s+(.+?)(?:\.|$)/gm,
260
- /must not\s+(.+?)(?:\.|$)/gm,
261
- /do not\s+(.+?)(?:\.|$)/gm,
262
- /avoid\s+(.+?)(?:\.|$)/gm,
263
- /prohibit(?:ed|s)?\s+(.+?)(?:\.|$)/gm,
264
- /forbidden?:?\s+(.+?)(?:\.|$)/gm
265
- ];
266
- for (const pattern of patterns) {
267
- let match;
268
- while ((match = pattern.exec(text)) !== null) {
269
- const action = match[1].trim();
270
- if (action.length > 3 && action.length < 200) {
271
- actions.push(action);
272
- }
273
- }
274
- }
275
- return actions;
276
- }
277
- function createCliApproval(options) {
278
- const timeoutMs = options?.timeoutMs ?? 3e5;
279
- return (prompt) => {
280
- return new Promise((resolve) => {
281
- const readline = __require("readline");
282
- const rl = readline.createInterface({
283
- input: process.stdin,
284
- output: process.stdout
285
- });
286
- const timer = setTimeout(() => {
287
- log.warn("Approval timeout  rejecting");
288
- rl.close();
289
- resolve(false);
290
- }, timeoutMs);
291
- const preview = prompt.length > 100 ? prompt.slice(0, 100) + "..." : prompt;
292
- rl.question(`
293
- [APPROVAL REQUIRED] "${preview}"
294
- Approve? (y/N): `, (answer) => {
295
- clearTimeout(timer);
296
- rl.close();
297
- resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
298
- });
299
- });
300
- };
301
- }
302
- function createWebhookApproval(options) {
303
- const timeoutMs = options.timeoutMs ?? 3e5;
304
- return async (prompt) => {
305
- try {
306
- const controller = new AbortController();
307
- const timer = setTimeout(() => controller.abort(), timeoutMs);
308
- const response = await fetch(options.url, {
309
- method: "POST",
310
- headers: {
311
- "Content-Type": "application/json",
312
- ...options.headers
313
- },
314
- body: JSON.stringify({ prompt, timestamp: (/* @__PURE__ */ new Date()).toISOString() }),
315
- signal: controller.signal
316
- });
317
- clearTimeout(timer);
318
- if (!response.ok) {
319
- log.warn(`Approval webhook returned ${response.status}`);
320
- return false;
321
- }
322
- const body = await response.json();
323
- return body.approved === true;
324
- } catch (err) {
325
- log.warn(`Approval webhook failed: ${err instanceof Error ? err.message : String(err)}`);
326
- return false;
327
- }
328
- };
329
- }
330
- function checkAction(harnessDir, action, options) {
331
- const violation = checkRuleViolation(harnessDir, action, options);
332
- return {
333
- allowed: violation === null,
334
- reason: violation
335
- };
336
- }
337
- export {
338
- checkAction,
339
- checkRuleViolation,
340
- createAgent,
341
- createCliApproval,
342
- createWebhookApproval
343
- };
344
- //# sourceMappingURL=agent-framework-K4GUIICH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/runtime/agent-framework.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { createHarness } from '../core/harness.js';\nimport { loadDirectory } from '../primitives/loader.js';\nimport { log } from '../core/logger.js';\nimport type {\n CreateHarnessOptions,\n HarnessAgent,\n HarnessConfig,\n HarnessHooks,\n AgentRunResult,\n AgentStreamResult,\n AgentState,\n DeepPartial,\n} from '../core/types.js';\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 1. defineAgent() \u0014 Declarative Agent Definition\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\nexport interface AgentDefinition {\n /** Agent name (used for logging and config resolution) */\n name: string;\n /** Harness directory path */\n dir: string;\n /** Model override */\n model?: string;\n /** Provider override */\n provider?: string;\n /** API key override */\n apiKey?: string;\n /** Config overrides */\n config?: DeepPartial<HarnessConfig>;\n\n /** Lifecycle hooks \u0014 called at various points in the agent lifecycle */\n hooks?: AgentLifecycleHooks;\n\n /** Pre-action rule enforcement configuration */\n guardrails?: GuardrailEnforcementConfig;\n\n /** Human-in-the-loop gate configuration */\n approval?: ApprovalGateConfig;\n\n /** Middleware functions that wrap each run/stream call */\n middleware?: AgentMiddleware[];\n}\n\nexport interface AgentLifecycleHooks extends HarnessHooks {\n /** Called before each run/stream \u0014 can modify prompt or reject */\n beforeRun?: (ctx: BeforeRunContext) => Promise<BeforeRunResult | void>;\n /** Called after each run/stream \u0014 can transform result */\n afterRun?: (ctx: AfterRunContext) => Promise<AgentRunResult | void>;\n /** Called periodically for health checks */\n onHealthCheck?: (ctx: { agent: DefinedAgent }) => Promise<void>;\n}\n\nexport interface BeforeRunContext {\n agent: DefinedAgent;\n prompt: string;\n isStream: boolean;\n}\n\nexport interface BeforeRunResult {\n /** Modified prompt (if changed) */\n prompt?: string;\n /** Set to true to reject the run */\n reject?: boolean;\n /** Reason for rejection */\n reason?: string;\n}\n\nexport interface AfterRunContext {\n agent: DefinedAgent;\n prompt: string;\n result: AgentRunResult;\n}\n\nexport type AgentMiddleware = (\n ctx: { agent: DefinedAgent; prompt: string; isStream: boolean },\n next: () => Promise<AgentRunResult>,\n) => Promise<AgentRunResult>;\n\n/**\n * A defined agent wraps a HarnessAgent with additional capabilities:\n * - Middleware pipeline\n * - Pre-action guardrails\n * - Human-in-the-loop gates\n * - Extended lifecycle hooks\n */\nexport interface DefinedAgent {\n name: string;\n config: HarnessConfig;\n /** The underlying harness agent */\n harness: HarnessAgent;\n /** Boot the agent */\n boot(): Promise<void>;\n /** Run with middleware, guardrails, and approval gates */\n run(prompt: string): Promise<AgentRunResult>;\n /** Stream with middleware, guardrails, and approval gates */\n stream(prompt: string): AgentStreamResult;\n /** Shutdown cleanly */\n shutdown(): Promise<void>;\n /** Get agent state */\n getState(): AgentState;\n /** Get system prompt */\n getSystemPrompt(): string;\n /** Check if agent is booted */\n isBooted(): boolean;\n /** Access the definition */\n definition: AgentDefinition;\n}\n\n/**\n * Define an agent with declarative configuration.\n *\n * This is the high-level API for creating agents with:\n * - Lifecycle hooks (beforeRun, afterRun, onBoot, onSessionEnd, onError, onShutdown)\n * - Middleware pipeline (wrap each run with custom logic)\n * - Guardrail enforcement (check rules before actions)\n * - Human-in-the-loop gates (require approval for certain prompts)\n *\n * Usage:\n * ```typescript\n * const agent = createAgent({\n * name: 'my-agent',\n * dir: './my-harness',\n * hooks: {\n * beforeRun: async ({ prompt }) => {\n * if (prompt.includes('delete')) return { reject: true, reason: 'Destructive action' };\n * },\n * onBoot: async () => console.log('Agent booted!'),\n * },\n * guardrails: { enforceRules: true },\n * approval: { requireApproval: (prompt) => prompt.includes('deploy') },\n * });\n *\n * await agent.boot();\n * const result = await agent.run('Hello world');\n * ```\n */\nexport function createAgent(definition: AgentDefinition): DefinedAgent {\n let booted = false;\n\n // Build HarnessHooks from lifecycle hooks\n const harnessHooks: HarnessHooks = {};\n if (definition.hooks?.onBoot) harnessHooks.onBoot = definition.hooks.onBoot;\n if (definition.hooks?.onSessionEnd) harnessHooks.onSessionEnd = definition.hooks.onSessionEnd;\n if (definition.hooks?.onError) harnessHooks.onError = definition.hooks.onError;\n if (definition.hooks?.onStateChange) harnessHooks.onStateChange = definition.hooks.onStateChange;\n if (definition.hooks?.onShutdown) harnessHooks.onShutdown = definition.hooks.onShutdown;\n\n // Create underlying harness\n const harnessOptions: CreateHarnessOptions = {\n dir: definition.dir,\n model: definition.model,\n provider: definition.provider,\n apiKey: definition.apiKey,\n config: definition.config,\n hooks: harnessHooks,\n };\n\n const harness = createHarness(harnessOptions);\n\n // Build middleware pipeline\n const middleware = definition.middleware ?? [];\n\n async function runWithPipeline(prompt: string, isStream: boolean): Promise<AgentRunResult> {\n // 1. beforeRun hook\n if (definition.hooks?.beforeRun) {\n const hookResult = await definition.hooks.beforeRun({\n agent: definedAgent,\n prompt,\n isStream,\n });\n\n if (hookResult) {\n if (hookResult.reject) {\n throw new Error(`Run rejected: ${hookResult.reason || 'beforeRun hook rejected'}`);\n }\n if (hookResult.prompt) {\n prompt = hookResult.prompt;\n }\n }\n }\n\n // 2. Guardrail enforcement (check rules)\n if (definition.guardrails?.enforceRules) {\n const violation = checkRuleViolation(definition.dir, prompt);\n if (violation) {\n throw new Error(`Guardrail violation: ${violation}`);\n }\n }\n\n // 3. Approval gate\n if (definition.approval?.requireApproval) {\n const needsApproval = definition.approval.requireApproval(prompt);\n if (needsApproval) {\n if (definition.approval.onApprovalNeeded) {\n const approved = await definition.approval.onApprovalNeeded(prompt);\n if (!approved) {\n throw new Error('Run requires approval but was not approved');\n }\n } else {\n // No approval handler \u0014 always reject\n throw new Error('Run requires approval but no onApprovalNeeded handler is configured');\n }\n }\n }\n\n // 4. Run through middleware chain\n const execute = async (): Promise<AgentRunResult> => {\n return harness.run(prompt);\n };\n\n let result: AgentRunResult;\n if (middleware.length > 0) {\n result = await executeMiddleware(middleware, definedAgent, prompt, isStream, execute);\n } else {\n result = await execute();\n }\n\n // 5. afterRun hook\n if (definition.hooks?.afterRun) {\n const transformed = await definition.hooks.afterRun({\n agent: definedAgent,\n prompt,\n result,\n });\n if (transformed) {\n result = transformed;\n }\n }\n\n return result;\n }\n\n const definedAgent: DefinedAgent = {\n name: definition.name,\n get config() { return harness.config; },\n harness,\n definition,\n\n async boot() {\n await harness.boot();\n booted = true;\n },\n\n async run(prompt: string): Promise<AgentRunResult> {\n if (!booted) await definedAgent.boot();\n return runWithPipeline(prompt, false);\n },\n\n stream(prompt: string): AgentStreamResult {\n // For stream, we wrap the textStream with pre-run checks\n let resolveResult: (r: AgentRunResult) => void;\n let rejectResult: (e: Error) => void;\n const resultPromise = new Promise<AgentRunResult>((res, rej) => {\n resolveResult = res;\n rejectResult = rej;\n });\n resultPromise.catch(() => {}); // Prevent unhandled rejection\n\n async function* generateStream(): AsyncIterable<string> {\n if (!booted) await definedAgent.boot();\n\n // Run pre-checks\n try {\n if (definition.hooks?.beforeRun) {\n const hookResult = await definition.hooks.beforeRun({\n agent: definedAgent,\n prompt,\n isStream: true,\n });\n if (hookResult?.reject) {\n const error = new Error(`Stream rejected: ${hookResult.reason || 'beforeRun hook rejected'}`);\n rejectResult(error);\n throw error;\n }\n if (hookResult?.prompt) {\n prompt = hookResult.prompt;\n }\n }\n\n if (definition.guardrails?.enforceRules) {\n const violation = checkRuleViolation(definition.dir, prompt);\n if (violation) {\n const error = new Error(`Guardrail violation: ${violation}`);\n rejectResult(error);\n throw error;\n }\n }\n\n if (definition.approval?.requireApproval) {\n const needsApproval = definition.approval.requireApproval(prompt);\n if (needsApproval) {\n if (definition.approval.onApprovalNeeded) {\n const approved = await definition.approval.onApprovalNeeded(prompt);\n if (!approved) {\n const error = new Error('Stream requires approval but was not approved');\n rejectResult(error);\n throw error;\n }\n } else {\n const error = new Error('Stream requires approval but no onApprovalNeeded handler is configured');\n rejectResult(error);\n throw error;\n }\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n rejectResult(error);\n throw error;\n }\n\n // Delegate to harness stream\n const streamResult = harness.stream(prompt);\n let fullText = '';\n\n for await (const chunk of streamResult.textStream) {\n fullText += chunk;\n yield chunk;\n }\n\n // Await the result and apply afterRun hook\n let result = await streamResult.result;\n\n if (definition.hooks?.afterRun) {\n const transformed = await definition.hooks.afterRun({\n agent: definedAgent,\n prompt,\n result,\n });\n if (transformed) {\n result = transformed;\n }\n }\n\n resolveResult(result);\n }\n\n return {\n textStream: generateStream(),\n result: resultPromise,\n };\n },\n\n async shutdown() {\n await harness.shutdown();\n booted = false;\n },\n\n getState() {\n return harness.getState();\n },\n\n getSystemPrompt() {\n return harness.getSystemPrompt();\n },\n\n isBooted() {\n return booted;\n },\n };\n\n return definedAgent;\n}\n\n/**\n * Execute middleware chain in order.\n */\nasync function executeMiddleware(\n middlewares: AgentMiddleware[],\n agent: DefinedAgent,\n prompt: string,\n isStream: boolean,\n execute: () => Promise<AgentRunResult>,\n): Promise<AgentRunResult> {\n let index = 0;\n\n const next = async (): Promise<AgentRunResult> => {\n if (index >= middlewares.length) {\n return execute();\n }\n const mw = middlewares[index++];\n return mw({ agent, prompt, isStream }, next);\n };\n\n return next();\n}\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 2. Guardrail Enforcement \u0014 Pre-action Rule Checking\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\nexport interface GuardrailEnforcementConfig {\n /** Enable rule-based pre-action checking (default: false) */\n enforceRules?: boolean;\n /** Custom check function (in addition to rule scanning) */\n customCheck?: (prompt: string) => string | null;\n /** Tags to filter rules (only check rules with these tags) */\n ruleTags?: string[];\n}\n\nexport interface AgentRuleViolation {\n ruleId: string;\n rulePath: string;\n violatedDirective: string;\n prompt: string;\n}\n\n/**\n * Check if a prompt violates any rules in the harness.\n *\n * Rules are scanned for \"never\" / \"don't\" / \"must not\" / \"avoid\" directives.\n * If the prompt contains keywords that match a forbidden action,\n * a violation is returned.\n *\n * This is a heuristic check \u0014 not a semantic understanding of the prompt.\n * Returns null if no violation found, or a string describing the violation.\n */\nexport function checkRuleViolation(\n harnessDir: string,\n prompt: string,\n options?: { ruleTags?: string[] },\n): string | null {\n const rulesDir = join(harnessDir, 'rules');\n if (!existsSync(rulesDir)) return null;\n\n const rules = loadDirectory(rulesDir);\n const promptLower = prompt.toLowerCase();\n\n for (const rule of rules) {\n // Filter by tags if specified\n if (options?.ruleTags && options.ruleTags.length > 0) {\n const hasTag = rule.frontmatter.tags.some((t) =>\n options.ruleTags!.includes(t.toLowerCase()),\n );\n if (!hasTag) continue;\n }\n\n // Skip inactive rules\n if (rule.frontmatter.status !== 'active') continue;\n\n const text = (rule.l0 + '\\n' + rule.body).toLowerCase();\n\n // Extract forbidden actions from rules\n const forbiddenPatterns = extractForbiddenActions(text);\n\n for (const forbidden of forbiddenPatterns) {\n // Check if the prompt contains the forbidden action keywords\n const keywords = forbidden.split(/\\s+/).filter((w) => w.length > 3);\n const matchCount = keywords.filter((k) => promptLower.includes(k)).length;\n\n // Need at least 60% keyword match to flag\n if (keywords.length > 0 && matchCount >= Math.ceil(keywords.length * 0.6)) {\n return `Rule \"${rule.frontmatter.id}\" forbids: \"${forbidden}\" \u0014 prompt matches ${matchCount}/${keywords.length} keywords`;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract forbidden actions from rule text.\n * Looks for \"never X\", \"don't X\", \"must not X\", \"avoid X\" patterns.\n */\nfunction extractForbiddenActions(text: string): string[] {\n const actions: string[] = [];\n\n const patterns = [\n /never\\s+(.+?)(?:\\.|$)/gm,\n /don'?t\\s+(.+?)(?:\\.|$)/gm,\n /must not\\s+(.+?)(?:\\.|$)/gm,\n /do not\\s+(.+?)(?:\\.|$)/gm,\n /avoid\\s+(.+?)(?:\\.|$)/gm,\n /prohibit(?:ed|s)?\\s+(.+?)(?:\\.|$)/gm,\n /forbidden?:?\\s+(.+?)(?:\\.|$)/gm,\n ];\n\n for (const pattern of patterns) {\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const action = match[1].trim();\n if (action.length > 3 && action.length < 200) {\n actions.push(action);\n }\n }\n }\n\n return actions;\n}\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 3. Human-in-the-Loop Gates\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\nexport interface ApprovalGateConfig {\n /**\n * Function that decides if a prompt requires human approval.\n * Return true to require approval, false to proceed.\n */\n requireApproval?: (prompt: string) => boolean;\n\n /**\n * Called when approval is needed.\n * Should return true if approved, false if rejected.\n * For CLI: prompt user with readline.\n * For API: send webhook and await response.\n */\n onApprovalNeeded?: (prompt: string) => Promise<boolean>;\n\n /**\n * Timeout in ms for approval (default: 300000 = 5 minutes).\n * If timeout is reached, the run is rejected.\n */\n timeoutMs?: number;\n}\n\n/**\n * Create a simple CLI-based approval handler.\n * Prompts the user on stdin for approval.\n *\n * Usage:\n * ```typescript\n * createAgent({\n * ...\n * approval: {\n * requireApproval: (prompt) => prompt.includes('deploy'),\n * onApprovalNeeded: createCliApproval(),\n * },\n * });\n * ```\n */\nexport function createCliApproval(options?: { timeoutMs?: number }): (prompt: string) => Promise<boolean> {\n const timeoutMs = options?.timeoutMs ?? 300000;\n\n return (prompt: string): Promise<boolean> => {\n return new Promise((resolve) => {\n const readline = require('readline') as typeof import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const timer = setTimeout(() => {\n log.warn('Approval timeout \u0014 rejecting');\n rl.close();\n resolve(false);\n }, timeoutMs);\n\n const preview = prompt.length > 100 ? prompt.slice(0, 100) + '...' : prompt;\n rl.question(`\\n[APPROVAL REQUIRED] \"${preview}\"\\nApprove? (y/N): `, (answer) => {\n clearTimeout(timer);\n rl.close();\n resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');\n });\n });\n };\n}\n\n/**\n * Create a webhook-based approval handler.\n * Sends a POST to the webhook URL and awaits response.\n */\nexport function createWebhookApproval(options: {\n url: string;\n timeoutMs?: number;\n headers?: Record<string, string>;\n}): (prompt: string) => Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? 300000;\n\n return async (prompt: string): Promise<boolean> => {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const response = await fetch(options.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: JSON.stringify({ prompt, timestamp: new Date().toISOString() }),\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n if (!response.ok) {\n log.warn(`Approval webhook returned ${response.status}`);\n return false;\n }\n\n const body = await response.json() as { approved?: boolean };\n return body.approved === true;\n } catch (err) {\n log.warn(`Approval webhook failed: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n };\n}\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 4. Utility: Rule-based Action Guards\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\n/**\n * Check if a specific action is allowed by the harness rules.\n * Returns { allowed: true } or { allowed: false, reason: string }.\n *\n * This is a lightweight version of checkRuleViolation for use outside defineAgent.\n */\nexport function checkAction(\n harnessDir: string,\n action: string,\n options?: { ruleTags?: string[] },\n): { allowed: boolean; reason: string | null } {\n const violation = checkRuleViolation(harnessDir, action, options);\n return {\n allowed: violation === null,\n reason: violation,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import {
4
- autoProcessAll,
5
- autoProcessFile
6
- } from "./chunk-M7NXUK55.js";
7
- import "./chunk-4CWAGBNS.js";
8
- import "./chunk-ZZJOFKAT.js";
9
- export {
10
- autoProcessAll,
11
- autoProcessFile
12
- };
13
- //# sourceMappingURL=auto-processor-OLE45UI3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/runtime/graph.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { join, relative, dirname, basename } from 'path';\nimport { loadDirectoryWithErrors } from '../primitives/loader.js';\nimport { getPrimitiveDirs } from '../core/types.js';\nimport type { HarnessConfig, HarnessDocument } from '../core/types.js';\n\nexport interface GraphNode {\n id: string;\n directory: string;\n path: string;\n tags: string[];\n status: string;\n l0: string;\n}\n\nexport interface GraphEdge {\n from: string;\n to: string;\n type: 'related' | 'with';\n}\n\nexport interface DependencyGraph {\n nodes: GraphNode[];\n edges: GraphEdge[];\n orphans: string[];\n clusters: string[][];\n}\n\nexport interface GraphStats {\n totalNodes: number;\n totalEdges: number;\n orphanCount: number;\n clusterCount: number;\n mostConnected: Array<{ id: string; connections: number }>;\n brokenRefs: Array<{ from: string; ref: string }>;\n}\n\n/**\n * Build a dependency graph from all primitives in the harness.\n * Analyzes `related:` and `with:` frontmatter fields to create edges.\n */\nexport function buildDependencyGraph(harnessDir: string, config?: HarnessConfig): DependencyGraph {\n const dirs = getPrimitiveDirs(config);\n const nodes: GraphNode[] = [];\n const edges: GraphEdge[] = [];\n const nodeIds = new Set<string>();\n\n // Load all primitives\n for (const dir of dirs) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n const id = doc.frontmatter.id;\n nodeIds.add(id);\n nodes.push({\n id,\n directory: dir,\n path: relative(harnessDir, doc.path),\n tags: doc.frontmatter.tags,\n status: doc.frontmatter.status,\n l0: doc.l0,\n });\n }\n }\n\n // Build edges from related: and with: fields\n for (const dir of dirs) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n const fromId = doc.frontmatter.id;\n\n // related: field edges\n for (const ref of doc.frontmatter.related) {\n const targetId = resolveRef(ref, nodeIds, harnessDir);\n if (targetId) {\n edges.push({ from: fromId, to: targetId, type: 'related' });\n }\n }\n\n // with: field edges (agent delegation reference)\n if (doc.frontmatter.with) {\n const withRef = doc.frontmatter.with;\n const targetId = resolveRef(withRef, nodeIds, harnessDir);\n if (targetId) {\n edges.push({ from: fromId, to: targetId, type: 'with' });\n }\n }\n }\n }\n\n // Find orphans (nodes with no edges in or out)\n const connected = new Set<string>();\n for (const edge of edges) {\n connected.add(edge.from);\n connected.add(edge.to);\n }\n const orphans = nodes\n .filter((n) => !connected.has(n.id))\n .map((n) => n.id);\n\n // Find clusters (connected components)\n const clusters = findClusters(nodes, edges);\n\n return { nodes, edges, orphans, clusters };\n}\n\n/**\n * Resolve a reference to a node ID. Handles:\n * - Direct ID match (e.g., \"tool-github\")\n * - Path-style refs (e.g., \"skills/code-review\" or \"agents/reviewer\")\n */\nfunction resolveRef(ref: string, knownIds: Set<string>, harnessDir: string): string | null {\n // Direct ID match\n if (knownIds.has(ref)) return ref;\n\n // Path-style ref: extract the filename part as potential ID\n if (ref.includes('/')) {\n const parts = ref.split('/');\n const filename = parts[parts.length - 1].replace(/\\.md$/, '');\n if (knownIds.has(filename)) return filename;\n\n // Try with directory prefix as part of ID\n const withDir = parts.join('-');\n if (knownIds.has(withDir)) return withDir;\n }\n\n return null;\n}\n\n/**\n * Find connected components using union-find.\n */\nfunction findClusters(nodes: GraphNode[], edges: GraphEdge[]): string[][] {\n const parent = new Map<string, string>();\n\n for (const node of nodes) {\n parent.set(node.id, node.id);\n }\n\n function find(id: string): string {\n let root = id;\n while (parent.get(root) !== root) {\n root = parent.get(root)!;\n }\n // Path compression\n let current = id;\n while (current !== root) {\n const next = parent.get(current)!;\n parent.set(current, root);\n current = next;\n }\n return root;\n }\n\n function union(a: string, b: string): void {\n const rootA = find(a);\n const rootB = find(b);\n if (rootA !== rootB) {\n parent.set(rootA, rootB);\n }\n }\n\n for (const edge of edges) {\n if (parent.has(edge.from) && parent.has(edge.to)) {\n union(edge.from, edge.to);\n }\n }\n\n // Group by root\n const groups = new Map<string, string[]>();\n for (const node of nodes) {\n const root = find(node.id);\n if (!groups.has(root)) {\n groups.set(root, []);\n }\n groups.get(root)!.push(node.id);\n }\n\n // Return only clusters with more than 1 member (singletons are orphans)\n return Array.from(groups.values())\n .filter((g) => g.length > 1)\n .sort((a, b) => b.length - a.length);\n}\n\n/**\n * Get statistics about the dependency graph.\n */\nexport function getGraphStats(harnessDir: string, config?: HarnessConfig): GraphStats {\n const graph = buildDependencyGraph(harnessDir, config);\n\n // Count connections per node\n const connectionCount = new Map<string, number>();\n for (const node of graph.nodes) {\n connectionCount.set(node.id, 0);\n }\n for (const edge of graph.edges) {\n connectionCount.set(edge.from, (connectionCount.get(edge.from) ?? 0) + 1);\n connectionCount.set(edge.to, (connectionCount.get(edge.to) ?? 0) + 1);\n }\n\n const mostConnected = Array.from(connectionCount.entries())\n .filter(([, count]) => count > 0)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([id, connections]) => ({ id, connections }));\n\n // Find broken references (references that didn't resolve to known IDs)\n const brokenRefs: Array<{ from: string; ref: string }> = [];\n const nodeIds = new Set(graph.nodes.map((n) => n.id));\n\n for (const dir of getPrimitiveDirs(config)) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n for (const ref of doc.frontmatter.related) {\n const resolved = resolveRef(ref, nodeIds, harnessDir);\n if (!resolved) {\n brokenRefs.push({ from: doc.frontmatter.id, ref });\n }\n }\n if (doc.frontmatter.with) {\n const resolved = resolveRef(doc.frontmatter.with, nodeIds, harnessDir);\n if (!resolved) {\n brokenRefs.push({ from: doc.frontmatter.id, ref: doc.frontmatter.with });\n }\n }\n }\n }\n\n return {\n totalNodes: graph.nodes.length,\n totalEdges: graph.edges.length,\n orphanCount: graph.orphans.length,\n clusterCount: graph.clusters.length,\n mostConnected,\n brokenRefs,\n };\n}\n"],"mappings":"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","names":[]}