@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/scheduler.ts"],"sourcesContent":["import cron from 'node-cron';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { loadDirectory, parseHarnessDocument } from '../primitives/loader.js';\nimport { loadConfig } from '../core/config.js';\nimport { createHarness } from '../core/harness.js';\nimport { delegateTo } from './delegate.js';\nimport { archiveOldFiles } from './sessions.js';\nimport { recordRun } from './metrics.js';\nimport { log } from '../core/logger.js';\nimport { recordSuccess, recordFailure } from './health.js';\nimport { synthesizeJournal, listUnjournaled } from './journal.js';\nimport { learnFromSessions } from './instinct-learner.js';\nimport type { HarnessConfig, HarnessDocument } from '../core/types.js';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Check if the current time falls within quiet hours.\n * Quiet hours wrap around midnight (e.g. start: 23, end: 6 means 23:00–05:59).\n * Returns true if the agent should be quiet (no scheduled workflows).\n */\nexport function isQuietHours(\n config: HarnessConfig,\n now?: Date,\n): boolean {\n const { start, end } = config.runtime.quiet_hours;\n const tz = config.runtime.timezone;\n\n // Get current hour in the configured timezone\n let hour: number;\n try {\n const formatter = new Intl.DateTimeFormat('en-US', {\n hour: 'numeric',\n hour12: false,\n timeZone: tz,\n });\n hour = parseInt(formatter.format(now ?? new Date()), 10);\n } catch {\n // Fallback to local time if timezone is invalid\n hour = (now ?? new Date()).getHours();\n }\n\n if (start === end) return false; // No quiet hours configured\n if (start < end) {\n // Simple range (e.g., start: 8, end: 17 means 8:00–16:59)\n return hour >= start && hour < end;\n }\n // Wraps midnight (e.g., start: 23, end: 6 means 23:00–05:59)\n return hour >= start || hour < end;\n}\n\nexport interface ScheduledWorkflow {\n doc: HarnessDocument;\n cronExpression: string;\n task: ReturnType<typeof cron.schedule> | null;\n}\n\nexport interface SchedulerOptions {\n harnessDir: string;\n apiKey?: string;\n /** Enable daily auto-archival of expired sessions/journals (default: true) */\n autoArchival?: boolean;\n /** Cron expression for auto-archival (default: \"0 23 * * *\" = daily at 23:00) */\n archivalCron?: string;\n /** Enable auto-journal synthesis (cron string or true for default \"0 22 * * *\") */\n autoJournal?: boolean | string;\n /** Enable auto-learn after journal synthesis (default: false) */\n autoLearn?: boolean;\n onRun?: (workflowId: string, result: string) => void;\n onError?: (workflowId: string, error: Error) => void;\n onSchedule?: (workflowId: string, cron: string) => void;\n onSkipQuietHours?: (workflowId: string) => void;\n onArchival?: (sessionsArchived: number, journalsArchived: number) => void;\n onRetry?: (workflowId: string, attempt: number, maxRetries: number, error: Error) => void;\n onJournal?: (date: string, sessionsCount: number) => void;\n onLearn?: (installed: number, skipped: number) => void;\n}\n\nexport class Scheduler {\n private workflows: Map<string, ScheduledWorkflow> = new Map();\n private harnessDir: string;\n private apiKey?: string;\n private autoArchival: boolean;\n private archivalCron: string;\n private archivalTask: ReturnType<typeof cron.schedule> | null = null;\n private autoJournal: boolean | string;\n private autoLearn: boolean;\n private journalTask: ReturnType<typeof cron.schedule> | null = null;\n /** Tracks proactive executions: workflowId → timestamps of recent runs */\n private proactiveHistory: Map<string, number[]> = new Map();\n private onRun?: (workflowId: string, result: string) => void;\n private onError?: (workflowId: string, error: Error) => void;\n private onSchedule?: (workflowId: string, cron: string) => void;\n private onSkipQuietHours?: (workflowId: string) => void;\n private onArchival?: (sessionsArchived: number, journalsArchived: number) => void;\n private onRetry?: (workflowId: string, attempt: number, maxRetries: number, error: Error) => void;\n private onJournal?: (date: string, sessionsCount: number) => void;\n private onLearn?: (installed: number, skipped: number) => void;\n private running = false;\n\n constructor(options: SchedulerOptions) {\n this.harnessDir = options.harnessDir;\n this.apiKey = options.apiKey;\n this.autoArchival = options.autoArchival ?? true;\n this.archivalCron = options.archivalCron ?? '0 23 * * *';\n this.autoJournal = options.autoJournal ?? false;\n this.autoLearn = options.autoLearn ?? false;\n this.onRun = options.onRun;\n this.onError = options.onError;\n this.onSchedule = options.onSchedule;\n this.onSkipQuietHours = options.onSkipQuietHours;\n this.onArchival = options.onArchival;\n this.onRetry = options.onRetry;\n this.onJournal = options.onJournal;\n this.onLearn = options.onLearn;\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n // Schedule auto-archival\n if (this.autoArchival && cron.validate(this.archivalCron)) {\n this.archivalTask = cron.schedule(this.archivalCron, () => {\n this.runArchival();\n });\n log.debug(`Auto-archival scheduled: ${this.archivalCron}`);\n }\n\n // Schedule auto-journal synthesis\n if (this.autoJournal) {\n const journalCron = typeof this.autoJournal === 'string' ? this.autoJournal : '0 22 * * *';\n if (cron.validate(journalCron)) {\n this.journalTask = cron.schedule(journalCron, () => {\n void this.runJournalSynthesis();\n });\n log.debug(`Auto-journal scheduled: ${journalCron}${this.autoLearn ? ' (with auto-learn)' : ''}`);\n } else {\n log.warn(`Invalid auto_journal cron: ${journalCron}`);\n }\n }\n\n // Load all workflows\n const workflowDir = join(this.harnessDir, 'workflows');\n if (!existsSync(workflowDir)) return;\n\n const docs = loadDirectory(workflowDir);\n\n for (const doc of docs) {\n const cronExpr = doc.frontmatter.schedule;\n if (!cronExpr) continue;\n\n if (!cron.validate(cronExpr)) {\n try { this.onError?.(doc.frontmatter.id, new Error(`Invalid cron expression: ${cronExpr}`)); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n continue;\n }\n\n const task = cron.schedule(cronExpr, async () => {\n await this.executeWorkflow(doc);\n });\n\n this.workflows.set(doc.frontmatter.id, { doc, cronExpression: cronExpr, task });\n try { this.onSchedule?.(doc.frontmatter.id, cronExpr); } catch (e) {\n log.warn(`onSchedule hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n stop(): void {\n if (!this.running) return;\n this.running = false;\n\n if (this.archivalTask) {\n this.archivalTask.stop();\n this.archivalTask = null;\n }\n\n if (this.journalTask) {\n this.journalTask.stop();\n this.journalTask = null;\n }\n\n for (const [, workflow] of this.workflows) {\n workflow.task?.stop();\n }\n this.workflows.clear();\n this.proactiveHistory.clear();\n }\n\n async executeWorkflow(doc: HarnessDocument): Promise<string> {\n const workflowId = doc.frontmatter.id;\n\n // Check quiet hours — skip scheduled workflows during quiet time\n const config = loadConfig(this.harnessDir);\n if (isQuietHours(config)) {\n log.debug(`Skipping workflow \"${workflowId}\" — quiet hours active`);\n try { this.onSkipQuietHours?.(workflowId); } catch (e) {\n log.warn(`onSkipQuietHours hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n return '';\n }\n\n // Check proactive cooldown — if workflow has proactive: true in frontmatter\n const isProactive = (doc.frontmatter as Record<string, unknown>)['proactive'] === true;\n if (isProactive && !this.checkProactiveCooldown(workflowId, config)) {\n log.debug(`Skipping proactive workflow \"${workflowId}\" — rate limited or in cooldown`);\n return '';\n }\n\n const maxRetries = doc.frontmatter.max_retries ?? 0;\n const baseDelay = doc.frontmatter.retry_delay_ms ?? 1000;\n let lastError: Error | null = null;\n const startTime = Date.now();\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // The workflow body IS the prompt — it describes what to do\n const prompt = `Execute this workflow:\\n\\n${doc.body}`;\n\n let resultText: string;\n let tokensUsed: number;\n\n // If workflow has a `with:` field, delegate to that sub-agent\n const delegateAgentId = doc.frontmatter.with;\n if (delegateAgentId) {\n log.debug(`Workflow \"${workflowId}\" delegating to agent \"${delegateAgentId}\"`);\n const delegateResult = await delegateTo({\n harnessDir: this.harnessDir,\n agentId: delegateAgentId,\n prompt,\n apiKey: this.apiKey,\n });\n resultText = delegateResult.text;\n tokensUsed = delegateResult.usage.totalTokens;\n } else {\n const agent = createHarness({\n dir: this.harnessDir,\n apiKey: this.apiKey,\n });\n const result = await agent.run(prompt);\n await agent.shutdown();\n resultText = result.text;\n tokensUsed = result.usage.totalTokens;\n }\n\n // Record success in health metrics\n recordSuccess(this.harnessDir);\n\n // Record successful run\n const endTime = Date.now();\n recordRun(this.harnessDir, {\n workflow_id: workflowId,\n started: new Date(startTime).toISOString(),\n ended: new Date(endTime).toISOString(),\n duration_ms: endTime - startTime,\n success: true,\n tokens_used: tokensUsed,\n attempt: attempt + 1,\n max_retries: maxRetries,\n });\n\n try { this.onRun?.(workflowId, resultText); } catch (e) {\n log.warn(`onRun hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n return resultText;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (attempt < maxRetries) {\n // Exponential backoff: baseDelay * 2^attempt\n const delay = baseDelay * Math.pow(2, attempt);\n log.debug(`Workflow \"${workflowId}\" failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms`);\n try { this.onRetry?.(workflowId, attempt + 1, maxRetries, lastError); } catch (e) {\n log.warn(`onRetry hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n await sleep(delay);\n }\n }\n }\n\n // Record failure in health metrics\n recordFailure(this.harnessDir, lastError?.message);\n\n // Record failed run\n const endTime = Date.now();\n recordRun(this.harnessDir, {\n workflow_id: workflowId,\n started: new Date(startTime).toISOString(),\n ended: new Date(endTime).toISOString(),\n duration_ms: endTime - startTime,\n success: false,\n error: lastError?.message,\n attempt: maxRetries + 1,\n max_retries: maxRetries,\n });\n\n // All attempts exhausted\n try { this.onError?.(workflowId, lastError!); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n throw lastError;\n }\n\n async runOnce(workflowId: string): Promise<string> {\n const workflowDir = join(this.harnessDir, 'workflows');\n const filePath = join(workflowDir, `${workflowId}.md`);\n\n if (!existsSync(filePath)) {\n throw new Error(`Workflow not found: ${workflowId}`);\n }\n\n const doc = parseHarnessDocument(filePath);\n return this.executeWorkflow(doc);\n }\n\n listScheduled(): Array<{ id: string; cron: string; path: string }> {\n return Array.from(this.workflows.entries()).map(([id, w]) => ({\n id,\n cron: w.cronExpression,\n path: w.doc.path,\n }));\n }\n\n /** Run archival of expired sessions/journals based on config retention policy. */\n runArchival(): void {\n try {\n const config = loadConfig(this.harnessDir);\n const result = archiveOldFiles(\n this.harnessDir,\n config.memory.session_retention_days,\n config.memory.journal_retention_days,\n );\n const total = result.sessionsArchived + result.journalsArchived;\n if (total > 0) {\n log.info(`Archived ${result.sessionsArchived} session(s), ${result.journalsArchived} journal(s)`);\n }\n try { this.onArchival?.(result.sessionsArchived, result.journalsArchived); } catch (e) {\n log.warn(`onArchival hook 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.error(`Archival failed: ${error.message}`);\n try { this.onError?.('__archival__', error); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n /**\n * Synthesize today's journal from unjournaled sessions.\n * Optionally runs instinct learning after synthesis if auto_learn is enabled.\n */\n async runJournalSynthesis(): Promise<void> {\n try {\n const unjournaled = listUnjournaled(this.harnessDir);\n if (unjournaled.length === 0) {\n log.debug('Auto-journal: no unjournaled sessions, skipping');\n return;\n }\n\n // Synthesize today's journal\n const today = new Date().toISOString().slice(0, 10);\n log.info(`Auto-journal: synthesizing ${unjournaled.length} unjournaled date(s)`);\n const entry = await synthesizeJournal(this.harnessDir, today, this.apiKey);\n\n try { this.onJournal?.(today, entry.sessions.length); } catch (e) {\n log.warn(`onJournal hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n\n // Auto-learn if enabled\n if (this.autoLearn) {\n log.info('Auto-learn: running instinct learning after journal synthesis');\n const learnResult = await learnFromSessions(this.harnessDir, true, this.apiKey);\n try { this.onLearn?.(learnResult.installed.length, learnResult.skipped.length); } catch (e) {\n log.warn(`onLearn hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(`Auto-journal failed: ${error.message}`);\n try { this.onError?.('__auto_journal__', error); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n /**\n * Check if a proactive workflow is allowed to run based on rate limits and cooldown.\n * Returns true if the workflow should proceed, false if it should be skipped.\n */\n checkProactiveCooldown(workflowId: string, config: HarnessConfig): boolean {\n const proactive = config.proactive;\n if (!proactive?.enabled) return true; // proactive not enabled — no restrictions\n\n const now = Date.now();\n const oneHourAgo = now - 3_600_000;\n const cooldownMs = (proactive.cooldown_minutes ?? 30) * 60_000;\n const maxPerHour = proactive.max_per_hour ?? 5;\n\n // Get or create history for this workflow\n let history = this.proactiveHistory.get(workflowId);\n if (!history) {\n history = [];\n this.proactiveHistory.set(workflowId, history);\n }\n\n // Prune entries older than 1 hour\n const recent = history.filter(ts => ts > oneHourAgo);\n this.proactiveHistory.set(workflowId, recent);\n\n // Check hourly rate limit\n if (recent.length >= maxPerHour) {\n log.debug(`Proactive cooldown: ${workflowId} hit max_per_hour (${maxPerHour})`);\n return false;\n }\n\n // Check cooldown since last run\n if (recent.length > 0) {\n const lastRun = recent[recent.length - 1];\n if (now - lastRun < cooldownMs) {\n log.debug(`Proactive cooldown: ${workflowId} within cooldown (${proactive.cooldown_minutes}min)`);\n return false;\n }\n }\n\n // Allowed — record this execution\n recent.push(now);\n return true;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n"],"mappings":"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","names":["endTime"]}
1
+ {"version":3,"sources":["../src/runtime/scheduler.ts"],"sourcesContent":["import cron from 'node-cron';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { loadDirectory, parseHarnessDocument } from '../primitives/loader.js';\nimport { loadConfig } from '../core/config.js';\nimport { createHarness } from '../core/harness.js';\nimport { delegateTo } from './delegate.js';\nimport { archiveOldFiles } from './sessions.js';\nimport { recordRun } from './metrics.js';\nimport { log } from '../core/logger.js';\nimport { recordSuccess, recordFailure } from './health.js';\nimport { synthesizeJournal, listUnjournaled } from './journal.js';\nimport { learnFromSessions } from './instinct-learner.js';\nimport type { HarnessConfig, HarnessDocument } from '../core/types.js';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Check if the current time falls within quiet hours.\n * Quiet hours wrap around midnight (e.g. start: 23, end: 6 means 23:00–05:59).\n * Returns true if the agent should be quiet (no scheduled workflows).\n */\nexport function isQuietHours(\n config: HarnessConfig,\n now?: Date,\n): boolean {\n const { start, end } = config.runtime.quiet_hours;\n const tz = config.runtime.timezone;\n\n // Get current hour in the configured timezone\n let hour: number;\n try {\n const formatter = new Intl.DateTimeFormat('en-US', {\n hour: 'numeric',\n hour12: false,\n timeZone: tz,\n });\n hour = parseInt(formatter.format(now ?? new Date()), 10);\n } catch {\n // Fallback to local time if timezone is invalid\n hour = (now ?? new Date()).getHours();\n }\n\n if (start === end) return false; // No quiet hours configured\n if (start < end) {\n // Simple range (e.g., start: 8, end: 17 means 8:00–16:59)\n return hour >= start && hour < end;\n }\n // Wraps midnight (e.g., start: 23, end: 6 means 23:00–05:59)\n return hour >= start || hour < end;\n}\n\nexport interface ScheduledWorkflow {\n doc: HarnessDocument;\n cronExpression: string;\n task: ReturnType<typeof cron.schedule> | null;\n}\n\nexport interface SchedulerOptions {\n harnessDir: string;\n apiKey?: string;\n /** Enable daily auto-archival of expired sessions/journals (default: true) */\n autoArchival?: boolean;\n /** Cron expression for auto-archival (default: \"0 23 * * *\" = daily at 23:00) */\n archivalCron?: string;\n /** Enable auto-journal synthesis (cron string or true for default \"0 22 * * *\") */\n autoJournal?: boolean | string;\n /** Enable auto-learn after journal synthesis (default: false) */\n autoLearn?: boolean;\n onRun?: (workflowId: string, result: string) => void;\n onError?: (workflowId: string, error: Error) => void;\n onSchedule?: (workflowId: string, cron: string) => void;\n onSkipQuietHours?: (workflowId: string) => void;\n onArchival?: (sessionsArchived: number, journalsArchived: number) => void;\n onRetry?: (workflowId: string, attempt: number, maxRetries: number, error: Error) => void;\n onJournal?: (date: string, sessionsCount: number) => void;\n onLearn?: (installed: number, skipped: number) => void;\n}\n\nexport class Scheduler {\n private workflows: Map<string, ScheduledWorkflow> = new Map();\n private harnessDir: string;\n private apiKey?: string;\n private autoArchival: boolean;\n private archivalCron: string;\n private archivalTask: ReturnType<typeof cron.schedule> | null = null;\n private autoJournal: boolean | string;\n private autoLearn: boolean;\n private journalTask: ReturnType<typeof cron.schedule> | null = null;\n /** Tracks proactive executions: workflowId → timestamps of recent runs */\n private proactiveHistory: Map<string, number[]> = new Map();\n private onRun?: (workflowId: string, result: string) => void;\n private onError?: (workflowId: string, error: Error) => void;\n private onSchedule?: (workflowId: string, cron: string) => void;\n private onSkipQuietHours?: (workflowId: string) => void;\n private onArchival?: (sessionsArchived: number, journalsArchived: number) => void;\n private onRetry?: (workflowId: string, attempt: number, maxRetries: number, error: Error) => void;\n private onJournal?: (date: string, sessionsCount: number) => void;\n private onLearn?: (installed: number, skipped: number) => void;\n private running = false;\n\n constructor(options: SchedulerOptions) {\n this.harnessDir = options.harnessDir;\n this.apiKey = options.apiKey;\n this.autoArchival = options.autoArchival ?? true;\n this.archivalCron = options.archivalCron ?? '0 23 * * *';\n this.autoJournal = options.autoJournal ?? false;\n this.autoLearn = options.autoLearn ?? false;\n this.onRun = options.onRun;\n this.onError = options.onError;\n this.onSchedule = options.onSchedule;\n this.onSkipQuietHours = options.onSkipQuietHours;\n this.onArchival = options.onArchival;\n this.onRetry = options.onRetry;\n this.onJournal = options.onJournal;\n this.onLearn = options.onLearn;\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n // Schedule auto-archival\n if (this.autoArchival && cron.validate(this.archivalCron)) {\n this.archivalTask = cron.schedule(this.archivalCron, () => {\n this.runArchival();\n });\n log.debug(`Auto-archival scheduled: ${this.archivalCron}`);\n }\n\n // Schedule auto-journal synthesis\n if (this.autoJournal) {\n const journalCron = typeof this.autoJournal === 'string' ? this.autoJournal : '0 22 * * *';\n if (cron.validate(journalCron)) {\n this.journalTask = cron.schedule(journalCron, () => {\n void this.runJournalSynthesis();\n });\n log.debug(`Auto-journal scheduled: ${journalCron}${this.autoLearn ? ' (with auto-learn)' : ''}`);\n } else {\n log.warn(`Invalid auto_journal cron: ${journalCron}`);\n }\n }\n\n // Load all workflows\n const workflowDir = join(this.harnessDir, 'workflows');\n if (!existsSync(workflowDir)) return;\n\n const docs = loadDirectory(workflowDir);\n\n for (const doc of docs) {\n const cronExpr = doc.frontmatter.schedule;\n if (!cronExpr) continue;\n\n if (!cron.validate(cronExpr)) {\n try { this.onError?.(doc.frontmatter.id, new Error(`Invalid cron expression: ${cronExpr}`)); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n continue;\n }\n\n const task = cron.schedule(cronExpr, async () => {\n await this.executeWorkflow(doc);\n });\n\n this.workflows.set(doc.frontmatter.id, { doc, cronExpression: cronExpr, task });\n try { this.onSchedule?.(doc.frontmatter.id, cronExpr); } catch (e) {\n log.warn(`onSchedule hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n stop(): void {\n if (!this.running) return;\n this.running = false;\n\n if (this.archivalTask) {\n this.archivalTask.stop();\n this.archivalTask = null;\n }\n\n if (this.journalTask) {\n this.journalTask.stop();\n this.journalTask = null;\n }\n\n for (const [, workflow] of this.workflows) {\n workflow.task?.stop();\n }\n this.workflows.clear();\n this.proactiveHistory.clear();\n }\n\n async executeWorkflow(doc: HarnessDocument): Promise<string> {\n const workflowId = doc.frontmatter.id;\n\n // Check quiet hours — skip scheduled workflows during quiet time\n const config = loadConfig(this.harnessDir);\n if (isQuietHours(config)) {\n log.debug(`Skipping workflow \"${workflowId}\" — quiet hours active`);\n try { this.onSkipQuietHours?.(workflowId); } catch (e) {\n log.warn(`onSkipQuietHours hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n return '';\n }\n\n // Check proactive cooldown — if workflow has proactive: true in frontmatter\n const isProactive = (doc.frontmatter as Record<string, unknown>)['proactive'] === true;\n if (isProactive && !this.checkProactiveCooldown(workflowId, config)) {\n log.debug(`Skipping proactive workflow \"${workflowId}\" — rate limited or in cooldown`);\n return '';\n }\n\n const maxRetries = doc.frontmatter.max_retries ?? 0;\n const baseDelay = doc.frontmatter.retry_delay_ms ?? 1000;\n let lastError: Error | null = null;\n const startTime = Date.now();\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // The workflow body IS the prompt — it describes what to do\n const prompt = `Execute this workflow:\\n\\n${doc.body}`;\n\n let resultText: string;\n let tokensUsed: number;\n\n // If workflow has a `with:` field, delegate to that sub-agent\n const delegateAgentId = doc.frontmatter.with;\n if (delegateAgentId) {\n log.debug(`Workflow \"${workflowId}\" delegating to agent \"${delegateAgentId}\"`);\n const delegateResult = await delegateTo({\n harnessDir: this.harnessDir,\n agentId: delegateAgentId,\n prompt,\n apiKey: this.apiKey,\n });\n resultText = delegateResult.text;\n tokensUsed = delegateResult.usage.totalTokens;\n } else {\n const agent = createHarness({\n dir: this.harnessDir,\n apiKey: this.apiKey,\n });\n const result = await agent.run(prompt);\n await agent.shutdown();\n resultText = result.text;\n tokensUsed = result.usage.totalTokens;\n }\n\n // Record success in health metrics\n recordSuccess(this.harnessDir);\n\n // Record successful run\n const endTime = Date.now();\n recordRun(this.harnessDir, {\n workflow_id: workflowId,\n started: new Date(startTime).toISOString(),\n ended: new Date(endTime).toISOString(),\n duration_ms: endTime - startTime,\n success: true,\n tokens_used: tokensUsed,\n attempt: attempt + 1,\n max_retries: maxRetries,\n });\n\n try { this.onRun?.(workflowId, resultText); } catch (e) {\n log.warn(`onRun hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n return resultText;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (attempt < maxRetries) {\n // Exponential backoff: baseDelay * 2^attempt\n const delay = baseDelay * Math.pow(2, attempt);\n log.debug(`Workflow \"${workflowId}\" failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms`);\n try { this.onRetry?.(workflowId, attempt + 1, maxRetries, lastError); } catch (e) {\n log.warn(`onRetry hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n await sleep(delay);\n }\n }\n }\n\n // Record failure in health metrics\n recordFailure(this.harnessDir, lastError?.message);\n\n // Record failed run\n const endTime = Date.now();\n recordRun(this.harnessDir, {\n workflow_id: workflowId,\n started: new Date(startTime).toISOString(),\n ended: new Date(endTime).toISOString(),\n duration_ms: endTime - startTime,\n success: false,\n error: lastError?.message,\n attempt: maxRetries + 1,\n max_retries: maxRetries,\n });\n\n // All attempts exhausted\n try { this.onError?.(workflowId, lastError!); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n throw lastError;\n }\n\n async runOnce(workflowId: string): Promise<string> {\n const workflowDir = join(this.harnessDir, 'workflows');\n const filePath = join(workflowDir, `${workflowId}.md`);\n\n if (!existsSync(filePath)) {\n throw new Error(`Workflow not found: ${workflowId}`);\n }\n\n const doc = parseHarnessDocument(filePath);\n return this.executeWorkflow(doc);\n }\n\n listScheduled(): Array<{ id: string; cron: string; path: string }> {\n return Array.from(this.workflows.entries()).map(([id, w]) => ({\n id,\n cron: w.cronExpression,\n path: w.doc.path,\n }));\n }\n\n /** Run archival of expired sessions/journals based on config retention policy. */\n runArchival(): void {\n try {\n const config = loadConfig(this.harnessDir);\n const result = archiveOldFiles(\n this.harnessDir,\n config.memory.session_retention_days,\n config.memory.journal_retention_days,\n );\n const total = result.sessionsArchived + result.journalsArchived;\n if (total > 0) {\n log.info(`Archived ${result.sessionsArchived} session(s), ${result.journalsArchived} journal(s)`);\n }\n try { this.onArchival?.(result.sessionsArchived, result.journalsArchived); } catch (e) {\n log.warn(`onArchival hook 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.error(`Archival failed: ${error.message}`);\n try { this.onError?.('__archival__', error); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n /**\n * Synthesize today's journal from unjournaled sessions.\n * Optionally runs instinct learning after synthesis if auto_learn is enabled.\n */\n async runJournalSynthesis(): Promise<void> {\n try {\n const unjournaled = listUnjournaled(this.harnessDir);\n if (unjournaled.length === 0) {\n log.debug('Auto-journal: no unjournaled sessions, skipping');\n return;\n }\n\n // Synthesize today's journal\n const today = new Date().toISOString().slice(0, 10);\n log.info(`Auto-journal: synthesizing ${unjournaled.length} unjournaled date(s)`);\n const entry = await synthesizeJournal(this.harnessDir, today, this.apiKey);\n\n try { this.onJournal?.(today, entry.sessions.length); } catch (e) {\n log.warn(`onJournal hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n\n // Auto-learn if enabled\n if (this.autoLearn) {\n log.info('Auto-learn: running instinct learning after journal synthesis');\n const learnResult = await learnFromSessions(this.harnessDir, true, this.apiKey);\n try { this.onLearn?.(learnResult.installed.length, learnResult.skipped.length); } catch (e) {\n log.warn(`onLearn hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(`Auto-journal failed: ${error.message}`);\n try { this.onError?.('__auto_journal__', error); } catch (e) {\n log.warn(`onError hook failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n\n /**\n * Check if a proactive workflow is allowed to run based on rate limits and cooldown.\n * Returns true if the workflow should proceed, false if it should be skipped.\n */\n checkProactiveCooldown(workflowId: string, config: HarnessConfig): boolean {\n const proactive = config.proactive;\n if (!proactive?.enabled) return true; // proactive not enabled — no restrictions\n\n const now = Date.now();\n const oneHourAgo = now - 3_600_000;\n const cooldownMs = (proactive.cooldown_minutes ?? 30) * 60_000;\n const maxPerHour = proactive.max_per_hour ?? 5;\n\n // Get or create history for this workflow\n let history = this.proactiveHistory.get(workflowId);\n if (!history) {\n history = [];\n this.proactiveHistory.set(workflowId, history);\n }\n\n // Prune entries older than 1 hour\n const recent = history.filter(ts => ts > oneHourAgo);\n this.proactiveHistory.set(workflowId, recent);\n\n // Check hourly rate limit\n if (recent.length >= maxPerHour) {\n log.debug(`Proactive cooldown: ${workflowId} hit max_per_hour (${maxPerHour})`);\n return false;\n }\n\n // Check cooldown since last run\n if (recent.length > 0) {\n const lastRun = recent[recent.length - 1];\n if (now - lastRun < cooldownMs) {\n log.debug(`Proactive cooldown: ${workflowId} within cooldown (${proactive.cooldown_minutes}min)`);\n return false;\n }\n }\n\n // Allowed — record this execution\n recent.push(now);\n return true;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n"],"mappings":"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","names":["endTime"]}
@@ -2,11 +2,10 @@
2
2
 
3
3
  import {
4
4
  loadDirectory
5
- } from "./chunk-UPLBF4RZ.js";
5
+ } from "./chunk-2UVWCTAY.js";
6
6
  import {
7
7
  getPrimitiveDirs
8
- } from "./chunk-4CWAGBNS.js";
9
- import "./chunk-ZZJOFKAT.js";
8
+ } from "./chunk-4TQQZILG.js";
10
9
 
11
10
  // src/runtime/search.ts
12
11
  import { existsSync } from "fs";
@@ -72,4 +71,4 @@ function matchDocument(doc, queryLower) {
72
71
  export {
73
72
  searchPrimitives
74
73
  };
75
- //# sourceMappingURL=search-V3W5JMJG.js.map
74
+ //# sourceMappingURL=search-6Y6NCOLQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/search.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { join } from 'path';\nimport { loadDirectory } from '../primitives/loader.js';\nimport { getPrimitiveDirs } from '../core/types.js';\nimport type { HarnessConfig, HarnessDocument } from '../core/types.js';\n\nexport interface SearchOptions {\n /** Filter by tag (case-insensitive) */\n tag?: string;\n /** Filter by primitive type directory (e.g., \"rules\", \"skills\") */\n type?: string;\n /** Filter by status (e.g., \"active\", \"draft\") */\n status?: string;\n /** Filter by author (e.g., \"human\", \"agent\") */\n author?: string;\n}\n\nexport interface SearchResult {\n doc: HarnessDocument;\n directory: string;\n matchReason: string;\n}\n\n/**\n * Search primitives across all directories by query text and/or filters.\n * Query matches against: id, tags, L0 summary, L1 summary, body content.\n */\nexport function searchPrimitives(\n harnessDir: string,\n query?: string,\n options?: SearchOptions,\n config?: HarnessConfig,\n): SearchResult[] {\n const results: SearchResult[] = [];\n const dirs = getPrimitiveDirs(config);\n const queryLower = query?.toLowerCase();\n\n for (const dir of dirs) {\n // Filter by type directory if specified\n if (options?.type) {\n const typeNorm = options.type.toLowerCase();\n // Accept both singular (\"rule\") and plural (\"rules\")\n if (dir !== typeNorm && dir !== typeNorm + 's' && dir.replace(/s$/, '') !== typeNorm) {\n continue;\n }\n }\n\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const docs = loadDirectory(fullPath);\n\n for (const doc of docs) {\n // Filter by status\n if (options?.status && doc.frontmatter.status !== options.status) continue;\n\n // Filter by author\n if (options?.author && doc.frontmatter.author !== options.author) continue;\n\n // Filter by tag\n if (options?.tag) {\n const tagLower = options.tag.toLowerCase();\n const hasTag = doc.frontmatter.tags.some((t) => t.toLowerCase() === tagLower);\n if (!hasTag) continue;\n }\n\n // Match query text\n if (queryLower) {\n const matchReason = matchDocument(doc, queryLower);\n if (!matchReason) continue;\n results.push({ doc, directory: dir, matchReason });\n } else {\n // No query — return all matching filters\n results.push({ doc, directory: dir, matchReason: 'filter match' });\n }\n }\n }\n\n return results;\n}\n\nfunction matchDocument(doc: HarnessDocument, queryLower: string): string | null {\n // Check id\n if (doc.frontmatter.id.toLowerCase().includes(queryLower)) {\n return `id: ${doc.frontmatter.id}`;\n }\n\n // Check tags\n for (const tag of doc.frontmatter.tags) {\n if (tag.toLowerCase().includes(queryLower)) {\n return `tag: ${tag}`;\n }\n }\n\n // Check L0\n if (doc.l0.toLowerCase().includes(queryLower)) {\n return `L0: ${doc.l0.slice(0, 80)}`;\n }\n\n // Check L1\n if (doc.l1.toLowerCase().includes(queryLower)) {\n return `L1 match`;\n }\n\n // Check body content\n const bodyLower = doc.body.toLowerCase();\n const idx = bodyLower.indexOf(queryLower);\n if (idx !== -1) {\n const start = Math.max(0, idx - 20);\n const end = Math.min(bodyLower.length, idx + queryLower.length + 30);\n const snippet = doc.body.slice(start, end).replace(/\\n/g, ' ').trim();\n return `body: ...${snippet}...`;\n }\n\n return null;\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AA0Bd,SAAS,iBACd,YACA,OACA,SACA,QACgB;AAChB,QAAM,UAA0B,CAAC;AACjC,QAAM,OAAO,iBAAiB,MAAM;AACpC,QAAM,aAAa,OAAO,YAAY;AAEtC,aAAW,OAAO,MAAM;AAEtB,QAAI,SAAS,MAAM;AACjB,YAAM,WAAW,QAAQ,KAAK,YAAY;AAE1C,UAAI,QAAQ,YAAY,QAAQ,WAAW,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,UAAU;AACpF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,YAAY,GAAG;AACrC,QAAI,CAAC,WAAW,QAAQ,EAAG;AAE3B,UAAM,OAAO,cAAc,QAAQ;AAEnC,eAAW,OAAO,MAAM;AAEtB,UAAI,SAAS,UAAU,IAAI,YAAY,WAAW,QAAQ,OAAQ;AAGlE,UAAI,SAAS,UAAU,IAAI,YAAY,WAAW,QAAQ,OAAQ;AAGlE,UAAI,SAAS,KAAK;AAChB,cAAM,WAAW,QAAQ,IAAI,YAAY;AACzC,cAAM,SAAS,IAAI,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,QAAQ;AAC5E,YAAI,CAAC,OAAQ;AAAA,MACf;AAGA,UAAI,YAAY;AACd,cAAM,cAAc,cAAc,KAAK,UAAU;AACjD,YAAI,CAAC,YAAa;AAClB,gBAAQ,KAAK,EAAE,KAAK,WAAW,KAAK,YAAY,CAAC;AAAA,MACnD,OAAO;AAEL,gBAAQ,KAAK,EAAE,KAAK,WAAW,KAAK,aAAa,eAAe,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,KAAsB,YAAmC;AAE9E,MAAI,IAAI,YAAY,GAAG,YAAY,EAAE,SAAS,UAAU,GAAG;AACzD,WAAO,OAAO,IAAI,YAAY,EAAE;AAAA,EAClC;AAGA,aAAW,OAAO,IAAI,YAAY,MAAM;AACtC,QAAI,IAAI,YAAY,EAAE,SAAS,UAAU,GAAG;AAC1C,aAAO,QAAQ,GAAG;AAAA,IACpB;AAAA,EACF;AAGA,MAAI,IAAI,GAAG,YAAY,EAAE,SAAS,UAAU,GAAG;AAC7C,WAAO,OAAO,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC;AAAA,EACnC;AAGA,MAAI,IAAI,GAAG,YAAY,EAAE,SAAS,UAAU,GAAG;AAC7C,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,IAAI,KAAK,YAAY;AACvC,QAAM,MAAM,UAAU,QAAQ,UAAU;AACxC,MAAI,QAAQ,IAAI;AACd,UAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,EAAE;AAClC,UAAM,MAAM,KAAK,IAAI,UAAU,QAAQ,MAAM,WAAW,SAAS,EAAE;AACnE,UAAM,UAAU,IAAI,KAAK,MAAM,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,KAAK;AACpE,WAAO,YAAY,OAAO;AAAA,EAC5B;AAEA,SAAO;AACT;","names":[]}
@@ -5,9 +5,8 @@ import {
5
5
  } from "./chunk-Z2PUCXTZ.js";
6
6
  import {
7
7
  loadAllPrimitives
8
- } from "./chunk-UPLBF4RZ.js";
9
- import "./chunk-4CWAGBNS.js";
10
- import "./chunk-ZZJOFKAT.js";
8
+ } from "./chunk-2UVWCTAY.js";
9
+ import "./chunk-4TQQZILG.js";
11
10
 
12
11
  // src/runtime/semantic-search.ts
13
12
  import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
@@ -238,4 +237,4 @@ export {
238
237
  saveEmbeddingStore,
239
238
  semanticSearch
240
239
  };
241
- //# sourceMappingURL=semantic-search-2DTOO5UX.js.map
240
+ //# sourceMappingURL=semantic-search-FN6FZIXI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/semantic-search.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'fs';\nimport { join } from 'path';\nimport { loadAllPrimitives } from '../primitives/loader.js';\nimport type { HarnessDocument, HarnessConfig } from '../core/types.js';\nimport { withFileLockSync } from './file-lock.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** A stored embedding for a single primitive document. */\nexport interface EmbeddingRecord {\n /** Document ID from frontmatter */\n id: string;\n /** Path to the source markdown file */\n path: string;\n /** Primitive directory (rules, skills, etc.) */\n directory: string;\n /** Text that was embedded (L0 + L1 + tags) */\n embeddedText: string;\n /** The embedding vector */\n vector: number[];\n /** File modification time (to detect stale embeddings) */\n mtime: string;\n /** When the embedding was generated */\n createdAt: string;\n}\n\n/** Embedding store format — persisted as JSON. */\nexport interface EmbeddingStore {\n /** Model ID used for embeddings (invalidate cache if changed) */\n modelId: string;\n /** Embedding vector dimension */\n dimensions: number;\n /** Map of document ID → embedding record */\n records: Record<string, EmbeddingRecord>;\n /** Last full index time */\n lastIndexedAt: string;\n}\n\n/** Result of a semantic search query. */\nexport interface SemanticSearchResult {\n doc: HarnessDocument;\n directory: string;\n /** Cosine similarity score (0-1, higher is more relevant) */\n score: number;\n /** The embedded text that matched */\n embeddedText: string;\n}\n\n/** Function signature for embedding text → vector. */\nexport type EmbedFunction = (texts: string[]) => Promise<number[][]>;\n\n/** Configuration for the semantic search module. */\nexport interface SemanticSearchConfig {\n /** Function to embed text (wraps Vercel AI SDK embed/embedMany) */\n embed: EmbedFunction;\n /** Embedding model identifier (for cache invalidation) */\n modelId: string;\n /** Maximum results to return (default: 10) */\n maxResults?: number;\n /** Minimum similarity threshold (default: 0.3) */\n minScore?: number;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\nconst STORE_FILE = 'embeddings.json';\nconst STORE_DIR = 'memory';\n\n// ─── Store Management ────────────────────────────────────────────────────────\n\n/** Load the embedding store from disk. Returns null if not found or invalid. */\nexport function loadEmbeddingStore(harnessDir: string): EmbeddingStore | null {\n const storePath = join(harnessDir, STORE_DIR, STORE_FILE);\n if (!existsSync(storePath)) return null;\n\n try {\n const raw = readFileSync(storePath, 'utf-8');\n return JSON.parse(raw) as EmbeddingStore;\n } catch {\n return null;\n }\n}\n\n/** Save the embedding store to disk. */\nexport function saveEmbeddingStore(harnessDir: string, store: EmbeddingStore): void {\n const storeDir = join(harnessDir, STORE_DIR);\n if (!existsSync(storeDir)) {\n mkdirSync(storeDir, { recursive: true });\n }\n\n const storePath = join(storeDir, STORE_FILE);\n withFileLockSync(harnessDir, storePath, () => {\n writeFileSync(storePath, JSON.stringify(store), 'utf-8');\n });\n}\n\n// ─── Text Extraction ─────────────────────────────────────────────────────────\n\n/**\n * Extract embeddable text from a document.\n * Combines: tags, L0 summary, L1 summary, and first 500 chars of body.\n * This gives a compact representation for embedding.\n */\nexport function extractEmbeddableText(doc: HarnessDocument): string {\n const parts: string[] = [];\n\n // Tags provide topical context\n if (doc.frontmatter.tags.length > 0) {\n parts.push(`Tags: ${doc.frontmatter.tags.join(', ')}`);\n }\n\n // L0 — one-liner\n if (doc.l0) {\n parts.push(doc.l0);\n }\n\n // L1 — paragraph summary\n if (doc.l1) {\n parts.push(doc.l1);\n }\n\n // Truncated body for additional context\n const bodyPreview = doc.body.slice(0, 500).trim();\n if (bodyPreview) {\n parts.push(bodyPreview);\n }\n\n return parts.join('\\n').trim();\n}\n\n// ─── Indexing ────────────────────────────────────────────────────────────────\n\n/**\n * Detect which primitives need re-embedding.\n * A primitive is stale if:\n * - It doesn't exist in the store\n * - Its file mtime has changed since last embedding\n * - The embedding model has changed\n */\nexport function detectStalePrimitives(\n harnessDir: string,\n store: EmbeddingStore | null,\n modelId: string,\n config?: HarnessConfig,\n): Array<{ doc: HarnessDocument; directory: string }> {\n const stale: Array<{ doc: HarnessDocument; directory: string }> = [];\n const allPrimitives = loadAllPrimitives(harnessDir, config?.extensions?.directories);\n\n // If model changed, everything is stale\n const modelChanged = store !== null && store.modelId !== modelId;\n\n for (const [directory, docs] of allPrimitives) {\n for (const doc of docs) {\n if (doc.frontmatter.status !== 'active') continue;\n\n const id = doc.frontmatter.id;\n\n if (modelChanged || !store) {\n stale.push({ doc, directory });\n continue;\n }\n\n const existing = store.records[id];\n if (!existing) {\n stale.push({ doc, directory });\n continue;\n }\n\n // Check if file changed\n try {\n const stat = statSync(doc.path);\n if (stat.mtime.toISOString() !== existing.mtime) {\n stale.push({ doc, directory });\n }\n } catch {\n stale.push({ doc, directory });\n }\n }\n }\n\n return stale;\n}\n\n/**\n * Index (or re-index) all primitives that need embeddings.\n * Incrementally updates the store — only re-embeds stale documents.\n *\n * @param harnessDir - Harness directory path\n * @param config - Semantic search configuration with embed function\n * @param harnessConfig - Optional harness config for extension directories\n * @returns Updated embedding store\n */\nexport async function indexPrimitives(\n harnessDir: string,\n searchConfig: SemanticSearchConfig,\n harnessConfig?: HarnessConfig,\n): Promise<EmbeddingStore> {\n let store = loadEmbeddingStore(harnessDir);\n\n const stale = detectStalePrimitives(harnessDir, store, searchConfig.modelId, harnessConfig);\n\n if (stale.length === 0 && store) {\n return store;\n }\n\n // Initialize store if needed\n if (!store || store.modelId !== searchConfig.modelId) {\n store = {\n modelId: searchConfig.modelId,\n dimensions: 0,\n records: {},\n lastIndexedAt: new Date().toISOString(),\n };\n }\n\n // Extract texts to embed\n const textsToEmbed: string[] = [];\n const docInfos: Array<{ doc: HarnessDocument; directory: string }> = [];\n\n for (const item of stale) {\n const text = extractEmbeddableText(item.doc);\n if (!text) continue;\n textsToEmbed.push(text);\n docInfos.push(item);\n }\n\n if (textsToEmbed.length === 0) {\n return store;\n }\n\n // Batch embed (chunked to avoid hitting rate limits)\n const batchSize = 50;\n for (let i = 0; i < textsToEmbed.length; i += batchSize) {\n const batch = textsToEmbed.slice(i, i + batchSize);\n const batchDocs = docInfos.slice(i, i + batchSize);\n\n const vectors = await searchConfig.embed(batch);\n\n for (let j = 0; j < vectors.length; j++) {\n const doc = batchDocs[j].doc;\n const vector = vectors[j];\n\n if (store.dimensions === 0 && vector.length > 0) {\n store.dimensions = vector.length;\n }\n\n let mtime: string;\n try {\n const stat = statSync(doc.path);\n mtime = stat.mtime.toISOString();\n } catch {\n mtime = new Date().toISOString();\n }\n\n store.records[doc.frontmatter.id] = {\n id: doc.frontmatter.id,\n path: doc.path,\n directory: batchDocs[j].directory,\n embeddedText: batch[j],\n vector,\n mtime,\n createdAt: new Date().toISOString(),\n };\n }\n }\n\n store.lastIndexedAt = new Date().toISOString();\n\n // Clean up deleted docs\n const allIds = new Set<string>();\n const allPrimitives = loadAllPrimitives(harnessDir, harnessConfig?.extensions?.directories);\n for (const [, docs] of allPrimitives) {\n for (const doc of docs) {\n allIds.add(doc.frontmatter.id);\n }\n }\n\n for (const id of Object.keys(store.records)) {\n if (!allIds.has(id)) {\n delete store.records[id];\n }\n }\n\n saveEmbeddingStore(harnessDir, store);\n return store;\n}\n\n// ─── Search ──────────────────────────────────────────────────────────────────\n\n/**\n * Compute cosine similarity between two vectors.\n * Returns a value between -1 and 1 (1 = identical, 0 = orthogonal).\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0;\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n\n const denominator = Math.sqrt(normA) * Math.sqrt(normB);\n if (denominator === 0) return 0;\n\n return dotProduct / denominator;\n}\n\n/**\n * Perform semantic search over indexed primitives.\n *\n * @param harnessDir - Harness directory path\n * @param query - Natural language query\n * @param searchConfig - Search configuration with embed function\n * @param harnessConfig - Optional harness config\n * @returns Ranked search results by cosine similarity\n */\nexport async function semanticSearch(\n harnessDir: string,\n query: string,\n searchConfig: SemanticSearchConfig,\n harnessConfig?: HarnessConfig,\n): Promise<SemanticSearchResult[]> {\n const store = loadEmbeddingStore(harnessDir);\n if (!store || Object.keys(store.records).length === 0) {\n return [];\n }\n\n const maxResults = searchConfig.maxResults ?? 10;\n const minScore = searchConfig.minScore ?? 0.3;\n\n // Embed the query\n const [queryVector] = await searchConfig.embed([query]);\n if (!queryVector || queryVector.length === 0) {\n return [];\n }\n\n // Score all documents\n const scored: Array<{ record: EmbeddingRecord; score: number }> = [];\n\n for (const record of Object.values(store.records)) {\n const score = cosineSimilarity(queryVector, record.vector);\n if (score >= minScore) {\n scored.push({ record, score });\n }\n }\n\n // Sort by score descending\n scored.sort((a, b) => b.score - a.score);\n\n // Load the actual documents for results\n const allPrimitives = loadAllPrimitives(harnessDir, harnessConfig?.extensions?.directories);\n const docMap = new Map<string, { doc: HarnessDocument; directory: string }>();\n for (const [directory, docs] of allPrimitives) {\n for (const doc of docs) {\n docMap.set(doc.frontmatter.id, { doc, directory });\n }\n }\n\n const results: SemanticSearchResult[] = [];\n\n for (const { record, score } of scored.slice(0, maxResults)) {\n const entry = docMap.get(record.id);\n if (!entry) continue;\n\n results.push({\n doc: entry.doc,\n directory: entry.directory,\n score,\n embeddedText: record.embeddedText,\n });\n }\n\n return results;\n}\n\n/**\n * Get embedding stats for the harness.\n */\nexport function getEmbeddingStats(harnessDir: string): {\n indexed: number;\n modelId: string | null;\n dimensions: number;\n lastIndexedAt: string | null;\n storeSize: number;\n} {\n const store = loadEmbeddingStore(harnessDir);\n if (!store) {\n return {\n indexed: 0,\n modelId: null,\n dimensions: 0,\n lastIndexedAt: null,\n storeSize: 0,\n };\n }\n\n const storePath = join(harnessDir, STORE_DIR, STORE_FILE);\n let storeSize = 0;\n try {\n storeSize = statSync(storePath).size;\n } catch {\n // Ignore\n }\n\n return {\n indexed: Object.keys(store.records).length,\n modelId: store.modelId,\n dimensions: store.dimensions,\n lastIndexedAt: store.lastIndexedAt,\n storeSize,\n };\n}\n"],"mappings":"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","names":[]}
@@ -5,13 +5,13 @@ import {
5
5
  cors,
6
6
  createWebApp,
7
7
  serve
8
- } from "./chunk-RY3ZFII7.js";
9
- import "./chunk-A7BJPQQ6.js";
8
+ } from "./chunk-SEHAQTBO.js";
9
+ import "./chunk-5O5OGOOQ.js";
10
10
  import "./chunk-6EMOEYGU.js";
11
11
  import "./chunk-GNUSHD2Y.js";
12
12
  import "./chunk-5H34JPMB.js";
13
- import "./chunk-MPZ3BPUI.js";
14
- import "./chunk-UWQTZMNI.js";
13
+ import "./chunk-UMXPOYZR.js";
14
+ import "./chunk-7GZ4D6V6.js";
15
15
  import "./chunk-UDZIS2AQ.js";
16
16
  import "./chunk-DTTXPHFW.js";
17
17
  import {
@@ -19,16 +19,15 @@ import {
19
19
  } from "./chunk-Z2PUCXTZ.js";
20
20
  import "./chunk-TAT6JU3X.js";
21
21
  import "./chunk-JKMGYWXB.js";
22
- import "./chunk-UPLBF4RZ.js";
22
+ import "./chunk-2UVWCTAY.js";
23
23
  import {
24
24
  log
25
25
  } from "./chunk-BSKDOFRT.js";
26
26
  import "./chunk-IZ6UZ3ZL.js";
27
27
  import {
28
28
  loadConfig
29
- } from "./chunk-CHJ5GNZC.js";
30
- import "./chunk-4CWAGBNS.js";
31
- import "./chunk-ZZJOFKAT.js";
29
+ } from "./chunk-EC42HQQH.js";
30
+ import "./chunk-4TQQZILG.js";
32
31
 
33
32
  // src/runtime/serve.ts
34
33
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -102,7 +101,7 @@ function startServe(options) {
102
101
  apiKey,
103
102
  webhookSecret
104
103
  } = options;
105
- const { app: baseApp, broadcaster } = createWebApp(harnessDir, { apiKey });
104
+ const { app: baseApp } = createWebApp(harnessDir, { apiKey });
106
105
  const app = new Hono();
107
106
  app.use("*", cors());
108
107
  const requireAuth = (secret) => {
@@ -141,7 +140,7 @@ function startServe(options) {
141
140
  return c.json({ error: "prompt is required" }, 400);
142
141
  }
143
142
  try {
144
- const { createHarness } = await import("./harness-WE4SLCML.js");
143
+ const { createHarness } = await import("./harness-R5FKRICG.js");
145
144
  const harness = createHarness({
146
145
  dir: harnessDir,
147
146
  model: body.model,
@@ -288,4 +287,4 @@ function startServe(options) {
288
287
  export {
289
288
  startServe
290
289
  };
291
- //# sourceMappingURL=serve-DTQ3HENY.js.map
290
+ //# sourceMappingURL=serve-MXRTP2HE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/serve.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { serve as honoServe } from '@hono/node-server';\nimport type { Server } from 'http';\nimport { createWebApp } from './web-server.js';\nimport { log } from '../core/logger.js';\nimport { loadConfig } from '../core/config.js';\nimport { withFileLockSync } from './file-lock.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface ServeOptions {\n harnessDir: string;\n port?: number;\n apiKey?: string;\n /** Secret for authenticating incoming webhooks */\n webhookSecret?: string;\n /** Enable CORS for all origins (default: true) */\n corsEnabled?: boolean;\n}\n\nexport interface WebhookRegistration {\n /** Unique webhook ID */\n id: string;\n /** URL to send events to */\n url: string;\n /** Events to subscribe to (e.g., ['session_end', 'state_change']) */\n events: string[];\n /** Optional secret for signing payloads */\n secret?: string;\n /** Whether this webhook is active */\n active: boolean;\n /** Created timestamp */\n createdAt: string;\n}\n\nexport interface WebhookPayload {\n event: string;\n timestamp: string;\n data: unknown;\n webhookId: string;\n}\n\nexport interface WebhookStore {\n webhooks: WebhookRegistration[];\n}\n\nexport interface ServeResult {\n server: Server;\n port: number;\n /** Function to fire a webhook event */\n fireEvent: (event: string, data: unknown) => Promise<void>;\n /** Function to stop the server */\n stop: () => void;\n}\n\n// ─── Webhook Store ──────────────────────────────────────────────────────────\n\nconst WEBHOOK_FILE = 'webhooks.json';\n\nfunction loadWebhooks(harnessDir: string): WebhookStore {\n const filePath = join(harnessDir, 'memory', WEBHOOK_FILE);\n if (!existsSync(filePath)) return { webhooks: [] };\n\n try {\n const raw = readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as WebhookStore;\n } catch {\n return { webhooks: [] };\n }\n}\n\nfunction saveWebhooks(harnessDir: string, store: WebhookStore): void {\n const memDir = join(harnessDir, 'memory');\n if (!existsSync(memDir)) mkdirSync(memDir, { recursive: true });\n\n const filePath = join(memDir, WEBHOOK_FILE);\n withFileLockSync(harnessDir, filePath, () => {\n writeFileSync(filePath, JSON.stringify(store, null, 2), 'utf-8');\n });\n}\n\n// ─── Webhook Delivery ───────────────────────────────────────────────────────\n\n/**\n * Fire an event to all subscribed webhooks.\n * Non-blocking — logs failures but never throws.\n */\nasync function fireWebhookEvent(\n harnessDir: string,\n event: string,\n data: unknown,\n): Promise<void> {\n const store = loadWebhooks(harnessDir);\n const subscribers = store.webhooks.filter(\n (w) => w.active && (w.events.includes('*') || w.events.includes(event)),\n );\n\n if (subscribers.length === 0) return;\n\n const payload: Omit<WebhookPayload, 'webhookId'> = {\n event,\n timestamp: new Date().toISOString(),\n data,\n };\n\n const deliveries = subscribers.map(async (webhook) => {\n try {\n const body = JSON.stringify({ ...payload, webhookId: webhook.id });\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Harness-Event': event,\n 'X-Webhook-ID': webhook.id,\n };\n\n // HMAC signing if secret is configured\n if (webhook.secret) {\n const crypto = await import('crypto');\n const hmac = crypto.createHmac('sha256', webhook.secret);\n hmac.update(body);\n headers['X-Harness-Signature'] = `sha256=${hmac.digest('hex')}`;\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 10000);\n\n const response = await fetch(webhook.url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n if (!response.ok) {\n log.warn(`Webhook ${webhook.id} delivery failed: HTTP ${response.status}`);\n }\n } catch (err) {\n log.warn(`Webhook ${webhook.id} delivery failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n });\n\n await Promise.allSettled(deliveries);\n}\n\n// ─── Server Factory ─────────────────────────────────────────────────────────\n\n/**\n * Create and start the harness API server.\n *\n * Includes:\n * - All dashboard endpoints from web-server.ts\n * - Webhook registration and management API\n * - Prompt execution endpoint (POST /api/run)\n * - Health check endpoint (GET /api/health)\n * - Version information endpoint (GET /api/info)\n *\n * Usage:\n * ```typescript\n * const result = startServe({\n * harnessDir: './my-harness',\n * port: 8080,\n * webhookSecret: 'my-secret',\n * });\n *\n * // Fire events to registered webhooks\n * await result.fireEvent('custom_event', { key: 'value' });\n *\n * // Stop the server\n * result.stop();\n * ```\n */\nexport function startServe(options: ServeOptions): ServeResult {\n const {\n harnessDir,\n port = 8080,\n apiKey,\n webhookSecret,\n } = options;\n\n // Build the base web app (dashboard + primitives + sessions + chat + SSE)\n const { app: baseApp } = createWebApp(harnessDir, { apiKey });\n\n // Create a new Hono app that wraps the base with additional endpoints\n const app = new Hono();\n app.use('*', cors());\n\n // ── Authentication middleware for webhook management ──\n const requireAuth = (secret: string | undefined) => {\n return async (c: { req: { header: (name: string) => string | undefined }; json: (body: unknown, status: number) => Response }, next: () => Promise<void>): Promise<Response | void> => {\n if (!secret) return next();\n const auth = c.req.header('Authorization');\n if (!auth || auth !== `Bearer ${secret}`) {\n return c.json({ error: 'Unauthorized' }, 401);\n }\n return next();\n };\n };\n\n // ── Health Check ──\n app.get('/api/health', (c) => {\n return c.json({\n status: 'ok',\n timestamp: new Date().toISOString(),\n harnessDir,\n });\n });\n\n // ── Agent Info ──\n app.get('/api/info', (c) => {\n try {\n const config = loadConfig(harnessDir);\n return c.json({\n name: config.agent.name,\n version: config.agent.version,\n model: config.model.id,\n provider: config.model.provider,\n });\n } catch {\n return c.json({ error: 'Failed to load config' }, 500);\n }\n });\n\n // ── Run prompt ──\n app.post('/api/run', async (c) => {\n const body = await c.req.json<{ prompt?: string; model?: string }>().catch(() => ({} as { prompt?: string; model?: string }));\n if (!body.prompt || body.prompt.trim().length === 0) {\n return c.json({ error: 'prompt is required' }, 400);\n }\n\n try {\n const { createHarness } = await import('../core/harness.js');\n const harness = createHarness({\n dir: harnessDir,\n model: body.model,\n apiKey,\n });\n\n await harness.boot();\n const result = await harness.run(body.prompt);\n await harness.shutdown();\n\n // Fire webhook\n await fireWebhookEvent(harnessDir, 'run_complete', {\n prompt: body.prompt,\n text: result.text,\n });\n\n return c.json({\n text: result.text,\n usage: result.usage,\n steps: result.steps,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await fireWebhookEvent(harnessDir, 'run_error', {\n prompt: body.prompt,\n error: message,\n });\n return c.json({ error: message }, 500);\n }\n });\n\n // ── Webhook Registration API ──\n\n // List registered webhooks\n app.get('/api/webhooks', requireAuth(webhookSecret) as never, (c) => {\n const store = loadWebhooks(harnessDir);\n return c.json(store.webhooks.map((w) => ({\n id: w.id,\n url: w.url,\n events: w.events,\n active: w.active,\n createdAt: w.createdAt,\n })));\n });\n\n // Register a new webhook\n app.post('/api/webhooks', requireAuth(webhookSecret) as never, async (c) => {\n const body = await c.req.json<{\n url?: string;\n events?: string[];\n secret?: string;\n }>().catch(() => ({} as { url?: string; events?: string[]; secret?: string }));\n\n if (!body.url) {\n return c.json({ error: 'url is required' }, 400);\n }\n\n // Validate URL\n try {\n new URL(body.url);\n } catch {\n return c.json({ error: 'Invalid URL' }, 400);\n }\n\n const store = loadWebhooks(harnessDir);\n const id = `wh_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n\n const webhook: WebhookRegistration = {\n id,\n url: body.url,\n events: body.events ?? ['*'],\n secret: body.secret,\n active: true,\n createdAt: new Date().toISOString(),\n };\n\n store.webhooks.push(webhook);\n saveWebhooks(harnessDir, store);\n\n return c.json({ id, url: webhook.url, events: webhook.events }, 201);\n });\n\n // Delete a webhook\n app.delete('/api/webhooks/:id', requireAuth(webhookSecret) as never, (c) => {\n const id = c.req.param('id');\n const store = loadWebhooks(harnessDir);\n const index = store.webhooks.findIndex((w) => w.id === id);\n\n if (index === -1) {\n return c.json({ error: 'Webhook not found' }, 404);\n }\n\n store.webhooks.splice(index, 1);\n saveWebhooks(harnessDir, store);\n\n return c.json({ deleted: id });\n });\n\n // Toggle webhook active/inactive\n app.patch('/api/webhooks/:id', requireAuth(webhookSecret) as never, async (c) => {\n const id = c.req.param('id');\n const body = await c.req.json<{ active?: boolean }>().catch(() => ({} as { active?: boolean }));\n\n const store = loadWebhooks(harnessDir);\n const webhook = store.webhooks.find((w) => w.id === id);\n\n if (!webhook) {\n return c.json({ error: 'Webhook not found' }, 404);\n }\n\n if (body.active !== undefined) {\n webhook.active = body.active;\n }\n\n saveWebhooks(harnessDir, store);\n return c.json({ id, active: webhook.active });\n });\n\n // Test a webhook (sends a test event)\n app.post('/api/webhooks/:id/test', requireAuth(webhookSecret) as never, async (c) => {\n const id = c.req.param('id');\n const store = loadWebhooks(harnessDir);\n const webhook = store.webhooks.find((w) => w.id === id);\n\n if (!webhook) {\n return c.json({ error: 'Webhook not found' }, 404);\n }\n\n try {\n const body = JSON.stringify({\n event: 'test',\n timestamp: new Date().toISOString(),\n data: { message: 'Webhook test from harness serve' },\n webhookId: webhook.id,\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Harness-Event': 'test',\n 'X-Webhook-ID': webhook.id,\n };\n\n if (webhook.secret) {\n const crypto = await import('crypto');\n const hmac = crypto.createHmac('sha256', webhook.secret);\n hmac.update(body);\n headers['X-Harness-Signature'] = `sha256=${hmac.digest('hex')}`;\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 10000);\n\n const response = await fetch(webhook.url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n return c.json({\n success: response.ok,\n status: response.status,\n statusText: response.statusText,\n });\n } catch (err) {\n return c.json({\n success: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n\n // ── Mount base web app routes ──\n app.route('/', baseApp);\n\n // Start server\n const server = honoServe({ fetch: app.fetch, port }) as Server;\n\n const stop = (): void => {\n server.close();\n };\n\n return {\n server,\n port,\n fireEvent: (event: string, data: unknown) => fireWebhookEvent(harnessDir, event, data),\n stop,\n };\n}\n"],"mappings":"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","names":[]}
@@ -9,7 +9,6 @@ import {
9
9
  writeSession
10
10
  } from "./chunk-DTTXPHFW.js";
11
11
  import "./chunk-Z2PUCXTZ.js";
12
- import "./chunk-ZZJOFKAT.js";
13
12
  export {
14
13
  archiveOldFiles,
15
14
  cleanupOldFiles,
@@ -18,4 +17,4 @@ export {
18
17
  listSessions,
19
18
  writeSession
20
19
  };
21
- //# sourceMappingURL=sessions-CZGVXKQE.js.map
20
+ //# sourceMappingURL=sessions-G6SZZXWS.js.map
@@ -15,7 +15,6 @@ import {
15
15
  } from "./chunk-RC6MEZB6.js";
16
16
  import "./chunk-Z2PUCXTZ.js";
17
17
  import "./chunk-BSKDOFRT.js";
18
- import "./chunk-ZZJOFKAT.js";
19
18
  export {
20
19
  addSource,
21
20
  discoverRemote,
@@ -29,4 +28,4 @@ export {
29
28
  removeSource,
30
29
  saveUserSources
31
30
  };
32
- //# sourceMappingURL=sources-RW5DT56F.js.map
31
+ //# sourceMappingURL=sources-7LDYO5GK.js.map
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import "./chunk-ZZJOFKAT.js";
4
3
 
5
4
  // src/runtime/starter-packs.ts
6
5
  var PACKS = {
@@ -890,4 +889,4 @@ export {
890
889
  listStarterPacks,
891
890
  parsePackName
892
891
  };
893
- //# sourceMappingURL=starter-packs-76YUVHEU.js.map
892
+ //# sourceMappingURL=starter-packs-OR7NI5NA.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/starter-packs.ts"],"sourcesContent":["/**\n * Builtin starter packs — installable bundles of primitives (workflows,\n * rules, instincts, skills) that users can customize after installation.\n *\n * Install via: `harness install pack:<name>`\n * List available: `harness install pack:list`\n */\n\nimport type { PackedBundle, BundleManifest, BundleFileEntry } from './primitive-registry.js';\n\nexport interface StarterPack {\n name: string;\n description: string;\n tags: string[];\n files: Array<{ path: string; content: string; id: string; l0: string }>;\n}\n\nconst PACKS: Record<string, StarterPack> = {\n 'daily-briefs': {\n name: 'daily-briefs',\n description: 'Morning briefing and evening review workflows — summarize upcoming tasks, review completed work, and plan next steps.',\n tags: ['daily', 'briefing', 'review', 'productivity'],\n files: [\n {\n path: 'workflows/morning-brief.md',\n id: 'morning-brief',\n l0: 'Morning briefing workflow that summarizes recent sessions and plans the day.',\n content: `---\nid: morning-brief\ntags: [workflow, daily, morning]\nauthor: infrastructure\nstatus: active\nschedule: \"0 8 * * 1-5\"\n---\n# Morning Brief\n\nReview yesterday's sessions and journal, then produce a concise daily brief.\n\n## Instructions\n\n1. Load the most recent journal entry and any sessions from the last 24 hours.\n2. Summarize key accomplishments, open questions, and blockers.\n3. List the top 3 priorities for today based on recent activity patterns.\n4. Note any instincts or rules that were frequently triggered.\n5. Keep the output under 500 words — this is a quick scan, not a deep analysis.\n\n## Output Format\n\n**Yesterday's highlights:**\n- [bullet points]\n\n**Today's priorities:**\n1. [priority]\n2. [priority]\n3. [priority]\n\n**Open questions / blockers:**\n- [if any]\n`,\n },\n {\n path: 'workflows/evening-review.md',\n id: 'evening-review',\n l0: 'Evening review workflow that synthesizes the day and prepares for tomorrow.',\n content: `---\nid: evening-review\ntags: [workflow, daily, evening, review]\nauthor: infrastructure\nstatus: active\nschedule: \"0 18 * * 1-5\"\n---\n# Evening Review\n\nSynthesize today's work and prepare handoff notes for tomorrow.\n\n## Instructions\n\n1. Load all sessions from today.\n2. Identify what was accomplished vs. what was planned (from morning brief if available).\n3. Note any recurring patterns, surprises, or friction points.\n4. Suggest 1-2 instinct candidates if behavioral patterns emerge.\n5. Write a brief handoff note for tomorrow's morning brief.\n\n## Output Format\n\n**Completed today:**\n- [bullet points]\n\n**Planned but not completed:**\n- [if any, with reasons]\n\n**Observations:**\n- [patterns, friction, surprises]\n\n**Tomorrow's handoff:**\n- [brief note for morning brief]\n`,\n },\n ],\n },\n\n 'weekly-review': {\n name: 'weekly-review',\n description: 'End-of-week review workflow — analyze the week, compress journals, surface trends, and set goals for next week.',\n tags: ['weekly', 'review', 'retrospective', 'planning'],\n files: [\n {\n path: 'workflows/weekly-review.md',\n id: 'weekly-review',\n l0: 'Weekly review workflow that analyzes the week and sets goals.',\n content: `---\nid: weekly-review\ntags: [workflow, weekly, review, retrospective]\nauthor: infrastructure\nstatus: active\nschedule: \"0 17 * * 5\"\n---\n# Weekly Review\n\nAnalyze the past week's work, compress journals, and plan next week.\n\n## Instructions\n\n1. Load all journal entries from this week (Monday through today).\n2. Identify the top 3-5 themes across the week's work.\n3. Note which instincts fired most often and whether they were helpful.\n4. Identify any skills or playbooks that were missing or underperforming.\n5. Suggest concrete goals for next week (2-3 maximum).\n6. Flag any rules that seem outdated or contradictory.\n\n## Output Format\n\n**Week of [date range]**\n\n**Key themes:**\n1. [theme with brief explanation]\n2. [theme]\n3. [theme]\n\n**Instinct effectiveness:**\n- [instinct name]: [helpful / needs tuning / remove]\n\n**Gaps identified:**\n- [missing skill or playbook suggestion]\n\n**Next week's goals:**\n1. [specific, actionable goal]\n2. [goal]\n\n**Maintenance notes:**\n- [rules to review, primitives to archive, etc.]\n`,\n },\n ],\n },\n\n 'code-review': {\n name: 'code-review',\n description: 'Code review workflow — analyzes recent code changes, checks for patterns and anti-patterns, and generates review notes.',\n tags: ['code-review', 'development', 'quality'],\n files: [\n {\n path: 'workflows/code-review.md',\n id: 'code-review-workflow',\n l0: 'Code review workflow that analyzes recent changes and generates review notes.',\n content: `---\nid: code-review-workflow\ntags: [workflow, code-review, development]\nauthor: infrastructure\nstatus: active\n---\n# Code Review Workflow\n\nAnalyze recent code changes and generate structured review notes.\n\n## Instructions\n\n1. Review the most recent session where code was written or modified.\n2. Check for common issues:\n - Missing error handling (empty catches, unhandled promises)\n - Type safety violations (any usage, missing return types)\n - Security concerns (unsanitized input, hardcoded secrets)\n - Code duplication or missed reuse opportunities\n3. Check adherence to project rules and instincts.\n4. Note positive patterns worth reinforcing as instincts.\n5. Generate a structured review with severity levels.\n\n## Output Format\n\n**Review of [session/change description]**\n\n**Critical issues:**\n- [severity: high] [description]\n\n**Improvements:**\n- [severity: medium] [suggestion]\n\n**Good patterns:**\n- [pattern worth keeping / promoting to instinct]\n\n**Summary:**\n[1-2 sentence overall assessment]\n`,\n },\n {\n path: 'workflows/pr-checklist.md',\n id: 'pr-checklist',\n l0: 'PR checklist workflow that generates a pre-merge review checklist.',\n content: `---\nid: pr-checklist\ntags: [workflow, code-review, pr, checklist]\nauthor: infrastructure\nstatus: active\n---\n# PR Checklist\n\nGenerate a pre-merge checklist based on project rules and recent changes.\n\n## Instructions\n\n1. Load all active rules from the harness.\n2. For each rule category, generate a checklist item.\n3. Add standard items: tests pass, no type errors, no lint warnings.\n4. Include project-specific checks based on instincts.\n5. Output as a copy-pasteable markdown checklist.\n\n## Output Format\n\n**Pre-merge checklist:**\n\n- [ ] All tests pass\n- [ ] No TypeScript errors (tsc --noEmit)\n- [ ] No lint warnings\n- [ ] Error handling: no empty catches, async errors handled\n- [ ] Types: no \\`any\\`, explicit return types on exports\n- [ ] Security: no hardcoded secrets, input validated\n- [rule-specific items from harness rules]\n`,\n },\n ],\n },\n\n 'code-reviewer': {\n name: 'code-reviewer',\n description: 'Multi-primitive code review pack — rules for code quality, instincts for review patterns, and a skill for structured review technique.',\n tags: ['code-review', 'quality', 'rules', 'instincts', 'skills'],\n files: [\n {\n path: 'rules/code-quality.md',\n id: 'code-quality-rules',\n l0: 'Code quality rules enforcing error handling, type safety, and security.',\n content: `---\nid: code-quality-rules\ntags: [rule, code-quality, error-handling, type-safety, security]\nauthor: infrastructure\nstatus: active\n---\n# Code Quality Rules\n\nEnforceable rules for maintaining code quality across the codebase.\n\n## Error Handling\n\n- **Never** leave a catch block empty. Every catch must log or handle the error meaningfully.\n- **Always** handle async errors — every async function must have error handling at its boundary.\n- **Never** use generic \\`catch (e) { throw e }\\` — either handle or let it propagate naturally.\n\n## Type Safety\n\n- **Never** use \\`any\\` — use \\`unknown\\` with type narrowing, generics, or explicit types.\n- **Always** add explicit return types on exported functions.\n- **Never** trust external data at runtime — validate at system boundaries.\n\n## Security\n\n- **Never** hardcode secrets, API keys, or tokens in source code.\n- **Always** parameterize database queries — never concatenate user input.\n- **Never** use \\`eval()\\`, \\`innerHTML\\`, or \\`document.write()\\` with unsanitized input.\n- **Always** validate and sanitize user input at the boundary where it enters the system.\n\n## Structure\n\n- **Prefer** early returns and guard clauses — happy path last.\n- **Never** create a new utility function without first searching for an existing one.\n- **Always** prefer editing existing files over creating new ones.\n`,\n },\n {\n path: 'rules/review-standards.md',\n id: 'review-standards',\n l0: 'Review standards for consistent, actionable code review feedback.',\n content: `---\nid: review-standards\ntags: [rule, code-review, standards, feedback]\nauthor: infrastructure\nstatus: active\n---\n# Review Standards\n\nStandards for producing consistent, actionable code review feedback.\n\n## Severity Levels\n\n- **Critical**: Security vulnerability, data loss risk, or crash. Must fix before merge.\n- **High**: Logic error, missing error handling, or broken contract. Should fix before merge.\n- **Medium**: Code smell, duplication, or missed optimization. Fix soon.\n- **Low**: Style preference, naming suggestion, or minor improvement. Optional.\n\n## Review Checklist\n\n- Every review must note at least one positive pattern (reinforcement).\n- Every issue must include a suggested fix, not just a complaint.\n- Reviews should reference specific rules or instincts when applicable.\n- Avoid vague feedback like \"this could be better\" — be specific and actionable.\n\n## Scope\n\n- Review only what changed — do not nitpick unrelated code.\n- Flag pre-existing issues separately from new issues.\n- If a change is too large to review effectively, request it be split.\n`,\n },\n {\n path: 'instincts/review-pattern-detection.md',\n id: 'review-pattern-detection',\n l0: 'Instinct for detecting common code review patterns and anti-patterns.',\n content: `---\nid: review-pattern-detection\ntags: [instinct, code-review, patterns, anti-patterns]\nauthor: infrastructure\nstatus: active\n---\n# Review Pattern Detection\n\nBehavioral instinct for recognizing patterns during code review.\n\n## Trigger\n\nWhen reviewing code changes or analyzing session output that includes code modifications.\n\n## Patterns to Watch For\n\n- **Copy-paste duplication**: Same logic appearing in multiple places — suggest extraction.\n- **Error swallowing**: Catch blocks that log but don't re-throw or handle — flag as silent failure.\n- **Missing edge cases**: Functions that handle the happy path but not nulls, empty arrays, or errors.\n- **Leaky abstractions**: Implementation details exposed through public interfaces.\n- **Premature optimization**: Complex code with no measured performance need.\n- **Magic values**: Hardcoded numbers or strings that should be named constants.\n\n## Response\n\nWhen a pattern is detected, note it in the review with the pattern name and a brief explanation. Suggest the specific fix, not just the problem.\n`,\n },\n {\n path: 'instincts/refactor-opportunity.md',\n id: 'refactor-opportunity',\n l0: 'Instinct for spotting refactoring opportunities during review.',\n content: `---\nid: refactor-opportunity\ntags: [instinct, refactoring, code-review, improvement]\nauthor: infrastructure\nstatus: active\n---\n# Refactor Opportunity Detection\n\nBehavioral instinct for identifying refactoring opportunities.\n\n## Trigger\n\nWhen code changes reveal structural issues or when the same area is modified repeatedly.\n\n## Signals\n\n- **Shotgun surgery**: A single logical change requires touching 3+ files — extract shared logic.\n- **Feature envy**: A function that mostly uses data from another module — move it.\n- **Long parameter lists**: Functions with 4+ parameters — consider an options object.\n- **Nested conditionals**: 3+ levels of nesting — extract early returns or helper functions.\n- **God objects**: Classes or modules with 10+ responsibilities — split by concern.\n\n## Response\n\nNote the refactoring opportunity with the specific smell name. Only suggest refactoring if it improves clarity — not every code smell needs immediate action.\n`,\n },\n {\n path: 'skills/structured-review.md',\n id: 'structured-review-skill',\n l0: 'Skill for conducting structured, multi-pass code reviews.',\n content: `---\nid: structured-review-skill\ntags: [skill, code-review, technique, methodology]\nauthor: infrastructure\nstatus: active\n---\n# Structured Code Review\n\nA systematic approach to reviewing code changes in multiple passes.\n\n## Technique: Three-Pass Review\n\n### Pass 1: Correctness (5 minutes)\n- Does the code do what the author intended?\n- Are there logic errors, off-by-one issues, or race conditions?\n- Do all error paths terminate correctly?\n\n### Pass 2: Quality (3 minutes)\n- Does it follow project rules and coding standards?\n- Are there opportunities for reuse or simplification?\n- Is the code testable? Are there missing tests?\n\n### Pass 3: Design (2 minutes)\n- Does the change fit the existing architecture?\n- Are the abstractions at the right level?\n- Will this be easy to modify in the future?\n\n## Output Template\n\n\\`\\`\\`\n**Correctness**: [pass/issues found]\n**Quality**: [pass/improvements suggested]\n**Design**: [pass/concerns noted]\n**Verdict**: [approve / request changes / discuss]\n\\`\\`\\`\n\n## Tips\n\n- Time-box each pass to avoid rabbit holes.\n- Record the first pass findings before moving to the next — fresh eyes find different things.\n- If you find a critical issue in pass 1, stop and report it immediately.\n`,\n },\n ],\n },\n\n 'personal-assistant': {\n name: 'personal-assistant',\n description: 'Personal assistant pack — daily planning workflow, communication instincts, and a task management skill for organizing priorities.',\n tags: ['productivity', 'planning', 'communication', 'task-management'],\n files: [\n {\n path: 'workflows/daily-planner.md',\n id: 'daily-planner',\n l0: 'Daily planning workflow that organizes priorities and schedules tasks.',\n content: `---\nid: daily-planner\ntags: [workflow, daily, planning, productivity]\nauthor: infrastructure\nstatus: active\nschedule: \"0 7 * * 1-5\"\n---\n# Daily Planner\n\nCreate a structured daily plan from open tasks, calendar items, and recent context.\n\n## Instructions\n\n1. Load the most recent journal entry and state.md.\n2. Identify all open tasks from \\`unfinished_business\\` in state.\n3. Check for any scheduled workflows firing today.\n4. Categorize tasks by urgency and importance (Eisenhower matrix).\n5. Produce a time-blocked plan for the day.\n6. Estimate total focus hours needed vs. available.\n\n## Output Format\n\n**Date: [today]**\n\n**Must do today (urgent + important):**\n1. [task with estimated time]\n\n**Should do today (important, not urgent):**\n1. [task]\n\n**Quick wins (< 15 min):**\n- [task]\n\n**Scheduled:**\n- [time] [event or workflow]\n\n**Focus hours needed:** [N] / **Available:** [M]\n`,\n },\n {\n path: 'workflows/inbox-triage.md',\n id: 'inbox-triage',\n l0: 'Inbox triage workflow that processes and categorizes incoming items.',\n content: `---\nid: inbox-triage\ntags: [workflow, triage, inbox, productivity]\nauthor: infrastructure\nstatus: active\nproactive: true\n---\n# Inbox Triage\n\nProcess incoming items and categorize them for action.\n\n## Instructions\n\n1. Scan recent sessions and events for unprocessed items.\n2. For each item, determine:\n - **Action required?** Yes/No\n - **Urgency:** Now / Today / This week / Someday\n - **Category:** Task / Question / Reference / Noise\n3. Items requiring action go to state.md unfinished_business.\n4. Questions get queued for the next interactive session.\n5. Reference items get filed as session notes.\n6. Noise gets acknowledged and dropped.\n\n## Output Format\n\n**Processed [N] items:**\n\n| Item | Action | Urgency | Category |\n|------|--------|---------|----------|\n| [description] | [yes/no] | [urgency] | [category] |\n\n**Added to queue:** [count]\n**Filed as reference:** [count]\n**Dropped:** [count]\n`,\n },\n {\n path: 'instincts/clear-communication.md',\n id: 'clear-communication',\n l0: 'Instinct for clear, concise communication in responses.',\n content: `---\nid: clear-communication\ntags: [instinct, communication, clarity, writing]\nauthor: infrastructure\nstatus: active\n---\n# Clear Communication\n\nBehavioral instinct for producing clear, actionable communication.\n\n## Trigger\n\nWhen generating any output that will be read by a human — responses, summaries, reports, plans.\n\n## Principles\n\n- **Lead with the answer.** State the conclusion first, then provide supporting detail.\n- **Be specific.** Replace \"soon\" with dates, \"some\" with counts, \"improve\" with metrics.\n- **One idea per paragraph.** Dense paragraphs with multiple ideas are hard to scan.\n- **Use structure.** Headers, lists, and tables are faster to process than prose.\n- **Cut filler.** Remove \"I think\", \"basically\", \"in order to\", \"it should be noted that\".\n\n## Anti-patterns\n\n- Restating the question before answering it.\n- Using jargon without context.\n- Providing information the reader didn't ask for.\n- Hedging when confidence is high.\n\n## Response\n\nApply these principles automatically. If a draft is unclear, restructure before outputting.\n`,\n },\n {\n path: 'instincts/context-awareness.md',\n id: 'context-awareness',\n l0: 'Instinct for maintaining awareness of user context and recent history.',\n content: `---\nid: context-awareness\ntags: [instinct, context, memory, personalization]\nauthor: infrastructure\nstatus: active\n---\n# Context Awareness\n\nBehavioral instinct for maintaining awareness of what the user is working on.\n\n## Trigger\n\nAt the start of every session and when switching topics.\n\n## Behavior\n\n- Check state.md for current goals and active workflows before responding.\n- Reference recent session history when it adds value (not gratuitously).\n- Notice when the user returns to a topic from a previous session — offer continuity.\n- Track which tools, files, and topics appear frequently — these are the user's active context.\n- When the user's request is ambiguous, use recent context to disambiguate rather than asking.\n\n## Anti-patterns\n\n- Treating every session as a fresh start with no history.\n- Asking questions that were already answered in a recent session.\n- Ignoring state.md goals when prioritizing tasks.\n`,\n },\n {\n path: 'skills/task-prioritization.md',\n id: 'task-prioritization-skill',\n l0: 'Skill for prioritizing tasks using the Eisenhower matrix and energy mapping.',\n content: `---\nid: task-prioritization-skill\ntags: [skill, productivity, prioritization, planning]\nauthor: infrastructure\nstatus: active\n---\n# Task Prioritization\n\nSystematic approach to prioritizing tasks when everything feels urgent.\n\n## Technique: Eisenhower + Energy Mapping\n\n### Step 1: Classify by Urgency and Importance\n\n| | Urgent | Not Urgent |\n|---|--------|------------|\n| **Important** | Do first | Schedule |\n| **Not Important** | Delegate/batch | Drop or defer |\n\n### Step 2: Map to Energy Levels\n\n- **High energy tasks** (creative, complex decisions): Morning block\n- **Medium energy tasks** (meetings, reviews): Midday\n- **Low energy tasks** (admin, filing, routine): Late afternoon\n\n### Step 3: Apply Constraints\n\n- Maximum 3 \"must do\" items per day — more than 3 means nothing is truly prioritized.\n- If everything is urgent, ask: \"What happens if this waits 24 hours?\" If the answer is \"nothing\", it's not urgent.\n- Group similar tasks to reduce context-switching cost.\n\n## Output\n\nProduce a prioritized list with:\n1. Task name\n2. Quadrant (urgent-important, important, urgent, neither)\n3. Estimated time\n4. Suggested time block (morning/midday/afternoon)\n`,\n },\n ],\n },\n\n 'devops': {\n name: 'devops',\n description: 'DevOps safety pack — deployment rules, monitoring instincts, and an incident response skill for handling production issues.',\n tags: ['devops', 'deployment', 'monitoring', 'incident-response', 'safety'],\n files: [\n {\n path: 'rules/deployment-safety.md',\n id: 'deployment-safety-rules',\n l0: 'Deployment safety rules preventing common production failures.',\n content: `---\nid: deployment-safety-rules\ntags: [rule, devops, deployment, safety, production]\nauthor: infrastructure\nstatus: active\n---\n# Deployment Safety Rules\n\nRules to prevent common deployment failures and production incidents.\n\n## Pre-Deployment\n\n- **Never** deploy without all tests passing — no exceptions, no \"just this once\".\n- **Never** deploy directly to production — always go through staging first.\n- **Always** review the diff before deploying — automated deployments must still be human-approved.\n- **Never** deploy on Fridays after 2 PM or before holidays without explicit approval.\n- **Always** have a rollback plan before deploying — know the exact command to revert.\n\n## During Deployment\n\n- **Always** deploy incrementally — canary or blue-green, never all-at-once.\n- **Never** deploy multiple unrelated changes in a single deployment.\n- **Always** monitor error rates for 15 minutes after deployment — do not walk away.\n\n## Secrets and Config\n\n- **Never** hardcode environment-specific values — use environment variables or config maps.\n- **Never** commit secrets to version control — use secret management tools.\n- **Always** rotate credentials after any suspected exposure — assume compromise.\n\n## Database\n\n- **Never** run destructive migrations without a backup taken in the last hour.\n- **Always** test migrations on a copy of production data before running on production.\n- **Never** drop columns or tables without confirming zero references in running code.\n`,\n },\n {\n path: 'rules/infrastructure-standards.md',\n id: 'infrastructure-standards',\n l0: 'Infrastructure standards for consistent, maintainable deployments.',\n content: `---\nid: infrastructure-standards\ntags: [rule, devops, infrastructure, standards]\nauthor: infrastructure\nstatus: active\n---\n# Infrastructure Standards\n\nStandards for maintaining consistent, auditable infrastructure.\n\n## Configuration\n\n- All infrastructure must be defined as code (Terraform, Pulumi, CloudFormation, or similar).\n- Manual changes to production infrastructure require a follow-up PR within 24 hours.\n- Every service must have health check endpoints (/health, /ready).\n- Every service must emit structured logs (JSON) with request IDs for tracing.\n\n## Monitoring\n\n- Every service must have alerts for: error rate > 1%, latency p99 > 2s, availability < 99.9%.\n- Alerts must page on-call for critical issues — never rely on email-only alerts.\n- Dashboard must show: request rate, error rate, latency percentiles, resource utilization.\n\n## Access Control\n\n- Production access requires MFA and is logged.\n- Prefer role-based access over individual permissions.\n- Review access lists quarterly — remove unused permissions.\n\n## Backups\n\n- All persistent data must be backed up daily with 30-day retention.\n- Test backup restoration quarterly — untested backups are not backups.\n`,\n },\n {\n path: 'instincts/anomaly-detection.md',\n id: 'anomaly-detection',\n l0: 'Instinct for detecting anomalies in metrics, logs, and deployment behavior.',\n content: `---\nid: anomaly-detection\ntags: [instinct, devops, monitoring, anomaly, alerting]\nauthor: infrastructure\nstatus: active\n---\n# Anomaly Detection\n\nBehavioral instinct for noticing when something is off in operational metrics.\n\n## Trigger\n\nWhen reviewing deployment output, log summaries, or metric dashboards.\n\n## Signals\n\n- **Error rate spike**: Any increase > 2x baseline within 5 minutes of a deployment.\n- **Latency creep**: p99 latency increasing steadily over hours — memory leak or connection exhaustion.\n- **Silent failures**: Success rate stays high but throughput drops — upstream is failing to send.\n- **Resource divergence**: CPU/memory usage differs significantly between replicas — one is stuck.\n- **Clock skew**: Timestamps in logs jumping backward or forward — NTP issues.\n- **Cascade pattern**: Multiple unrelated services degrading simultaneously — shared dependency.\n\n## Response\n\nWhen an anomaly is detected:\n1. Note the specific metric and timeframe.\n2. Correlate with recent deployments or config changes.\n3. If post-deployment: recommend immediate rollback, investigate after.\n4. If no recent change: check upstream dependencies and shared infrastructure.\n`,\n },\n {\n path: 'instincts/change-risk-assessment.md',\n id: 'change-risk-assessment',\n l0: 'Instinct for assessing risk before infrastructure or deployment changes.',\n content: `---\nid: change-risk-assessment\ntags: [instinct, devops, risk, change-management]\nauthor: infrastructure\nstatus: active\n---\n# Change Risk Assessment\n\nBehavioral instinct for evaluating the risk of operational changes before executing them.\n\n## Trigger\n\nBefore any deployment, configuration change, or infrastructure modification.\n\n## Risk Factors\n\n- **Blast radius**: How many users or services are affected if this fails?\n- **Reversibility**: Can this be rolled back in under 5 minutes?\n- **Observability**: Will we know if this breaks something? Are there alerts?\n- **Timing**: Is this during peak traffic? During on-call handoff?\n- **Novelty**: Has this type of change been done before? By this team?\n- **Dependencies**: Does this change require coordinated changes in other services?\n\n## Risk Levels\n\n- **Low**: Small blast radius, instantly reversible, well-observed, done before.\n- **Medium**: Moderate blast radius, reversible with effort, some unknowns.\n- **High**: Large blast radius, difficult to reverse, novel, or during peak.\n\n## Response\n\nState the risk level and the primary risk factor before proceeding. For high-risk changes, suggest breaking the change into smaller, lower-risk steps.\n`,\n },\n {\n path: 'skills/incident-response.md',\n id: 'incident-response-skill',\n l0: 'Skill for structured incident response and post-incident analysis.',\n content: `---\nid: incident-response-skill\ntags: [skill, devops, incident-response, runbook]\nauthor: infrastructure\nstatus: active\n---\n# Incident Response\n\nStructured approach to handling production incidents.\n\n## Phase 1: Detect and Assess (0-5 minutes)\n\n1. **Confirm the incident**: Verify the alert is real, not a false positive.\n2. **Assess impact**: How many users affected? Is data at risk?\n3. **Classify severity**:\n - **SEV1**: Service down, data loss, or security breach. Page everyone.\n - **SEV2**: Significant degradation, partial outage. Page on-call.\n - **SEV3**: Minor degradation, workaround available. Notify team.\n4. **Communicate**: Post in incident channel with: what's happening, who's investigating, ETA for update.\n\n## Phase 2: Mitigate (5-30 minutes)\n\n1. **Check recent changes**: Was anything deployed in the last 2 hours? Roll it back.\n2. **Check dependencies**: Are upstream services healthy?\n3. **Scale or redirect**: Can traffic be shifted to healthy instances?\n4. **Apply known fix**: Check runbooks for this failure mode.\n5. **Update communication** every 15 minutes, even if no progress.\n\n## Phase 3: Resolve and Recover\n\n1. **Confirm resolution**: Metrics return to baseline for 15+ minutes.\n2. **Communicate all-clear** with summary of what happened.\n3. **Schedule post-mortem** within 48 hours.\n\n## Phase 4: Post-Mortem Template\n\n\\`\\`\\`\n**Incident**: [title]\n**Date**: [date] | **Duration**: [time]\n**Severity**: [SEV1/2/3]\n**Impact**: [users/services affected]\n\n**Timeline:**\n- [HH:MM] Alert fired\n- [HH:MM] Investigation started\n- [HH:MM] Root cause identified\n- [HH:MM] Fix applied\n- [HH:MM] Confirmed resolved\n\n**Root cause:** [description]\n**Contributing factors:** [list]\n**Action items:**\n- [ ] [preventive measure with owner and due date]\n\\`\\`\\`\n`,\n },\n ],\n },\n};\n\n/**\n * Get a builtin starter pack by name.\n * Returns null if the pack doesn't exist.\n */\nexport function getStarterPack(name: string): PackedBundle | null {\n const pack = PACKS[name];\n if (!pack) return null;\n\n const now = new Date().toISOString();\n const fileEntries: BundleFileEntry[] = pack.files.map(f => ({\n path: f.path,\n type: f.path.split('/')[0],\n id: f.id,\n l0: f.l0,\n }));\n\n const manifest: BundleManifest = {\n version: '1',\n name: `pack:${pack.name}`,\n description: pack.description,\n author: 'agent-harness',\n bundle_version: '1.0.0',\n created: now,\n types: [...new Set(fileEntries.map(f => f.type))],\n tags: pack.tags,\n files: fileEntries,\n };\n\n return {\n manifest,\n files: pack.files.map(f => ({ path: f.path, content: f.content })),\n };\n}\n\n/**\n * List all available builtin starter packs.\n */\nexport function listStarterPacks(): Array<{ name: string; description: string; fileCount: number; tags: string[] }> {\n return Object.values(PACKS).map(p => ({\n name: p.name,\n description: p.description,\n fileCount: p.files.length,\n tags: p.tags,\n }));\n}\n\n/**\n * Check if a source string is a pack reference (starts with \"pack:\").\n */\nexport function isPackReference(source: string): boolean {\n return source.startsWith('pack:');\n}\n\n/**\n * Parse the pack name from a \"pack:<name>\" reference.\n */\nexport function parsePackName(source: string): string {\n return source.slice(5); // Remove \"pack:\" prefix\n}\n"],"mappings":";;;;;AAiBA,IAAM,QAAqC;AAAA,EACzC,gBAAgB;AAAA,IACd,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,SAAS,YAAY,UAAU,cAAc;AAAA,IACpD,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAiCX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,UAAU,UAAU,iBAAiB,UAAU;AAAA,IACtD,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA0CX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,eAAe,eAAe,SAAS;AAAA,IAC9C,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAsCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA8BX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,eAAe,WAAW,SAAS,aAAa,QAAQ;AAAA,IAC/D,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAmCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA8BX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA2BX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA0BX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA0CX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,sBAAsB;AAAA,IACpB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,gBAAgB,YAAY,iBAAiB,iBAAiB;AAAA,IACrE,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAsCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAmCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAiCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA4BX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAuCX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,UAAU,cAAc,cAAc,qBAAqB,QAAQ;AAAA,IAC1E,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAoCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAkCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA+BX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAiCX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAuDX;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,eAAe,MAAmC;AAChE,QAAM,OAAO,MAAM,IAAI;AACvB,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,QAAM,cAAiC,KAAK,MAAM,IAAI,QAAM;AAAA,IAC1D,MAAM,EAAE;AAAA,IACR,MAAM,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,IACzB,IAAI,EAAE;AAAA,IACN,IAAI,EAAE;AAAA,EACR,EAAE;AAEF,QAAM,WAA2B;AAAA,IAC/B,SAAS;AAAA,IACT,MAAM,QAAQ,KAAK,IAAI;AAAA,IACvB,aAAa,KAAK;AAAA,IAClB,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,OAAO,CAAC,GAAG,IAAI,IAAI,YAAY,IAAI,OAAK,EAAE,IAAI,CAAC,CAAC;AAAA,IAChD,MAAM,KAAK;AAAA,IACX,OAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,KAAK,MAAM,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE;AAAA,EACnE;AACF;AAKO,SAAS,mBAAoG;AAClH,SAAO,OAAO,OAAO,KAAK,EAAE,IAAI,QAAM;AAAA,IACpC,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,IACf,WAAW,EAAE,MAAM;AAAA,IACnB,MAAM,EAAE;AAAA,EACV,EAAE;AACJ;AAKO,SAAS,gBAAgB,QAAyB;AACvD,SAAO,OAAO,WAAW,OAAO;AAClC;AAKO,SAAS,cAAc,QAAwB;AACpD,SAAO,OAAO,MAAM,CAAC;AACvB;","names":[]}
1
+ {"version":3,"sources":["../src/runtime/starter-packs.ts"],"sourcesContent":["/**\n * Builtin starter packs — installable bundles of primitives (workflows,\n * rules, instincts, skills) that users can customize after installation.\n *\n * Install via: `harness install pack:<name>`\n * List available: `harness install pack:list`\n */\n\nimport type { PackedBundle, BundleManifest, BundleFileEntry } from './primitive-registry.js';\n\nexport interface StarterPack {\n name: string;\n description: string;\n tags: string[];\n files: Array<{ path: string; content: string; id: string; l0: string }>;\n}\n\nconst PACKS: Record<string, StarterPack> = {\n 'daily-briefs': {\n name: 'daily-briefs',\n description: 'Morning briefing and evening review workflows — summarize upcoming tasks, review completed work, and plan next steps.',\n tags: ['daily', 'briefing', 'review', 'productivity'],\n files: [\n {\n path: 'workflows/morning-brief.md',\n id: 'morning-brief',\n l0: 'Morning briefing workflow that summarizes recent sessions and plans the day.',\n content: `---\nid: morning-brief\ntags: [workflow, daily, morning]\nauthor: infrastructure\nstatus: active\nschedule: \"0 8 * * 1-5\"\n---\n# Morning Brief\n\nReview yesterday's sessions and journal, then produce a concise daily brief.\n\n## Instructions\n\n1. Load the most recent journal entry and any sessions from the last 24 hours.\n2. Summarize key accomplishments, open questions, and blockers.\n3. List the top 3 priorities for today based on recent activity patterns.\n4. Note any instincts or rules that were frequently triggered.\n5. Keep the output under 500 words — this is a quick scan, not a deep analysis.\n\n## Output Format\n\n**Yesterday's highlights:**\n- [bullet points]\n\n**Today's priorities:**\n1. [priority]\n2. [priority]\n3. [priority]\n\n**Open questions / blockers:**\n- [if any]\n`,\n },\n {\n path: 'workflows/evening-review.md',\n id: 'evening-review',\n l0: 'Evening review workflow that synthesizes the day and prepares for tomorrow.',\n content: `---\nid: evening-review\ntags: [workflow, daily, evening, review]\nauthor: infrastructure\nstatus: active\nschedule: \"0 18 * * 1-5\"\n---\n# Evening Review\n\nSynthesize today's work and prepare handoff notes for tomorrow.\n\n## Instructions\n\n1. Load all sessions from today.\n2. Identify what was accomplished vs. what was planned (from morning brief if available).\n3. Note any recurring patterns, surprises, or friction points.\n4. Suggest 1-2 instinct candidates if behavioral patterns emerge.\n5. Write a brief handoff note for tomorrow's morning brief.\n\n## Output Format\n\n**Completed today:**\n- [bullet points]\n\n**Planned but not completed:**\n- [if any, with reasons]\n\n**Observations:**\n- [patterns, friction, surprises]\n\n**Tomorrow's handoff:**\n- [brief note for morning brief]\n`,\n },\n ],\n },\n\n 'weekly-review': {\n name: 'weekly-review',\n description: 'End-of-week review workflow — analyze the week, compress journals, surface trends, and set goals for next week.',\n tags: ['weekly', 'review', 'retrospective', 'planning'],\n files: [\n {\n path: 'workflows/weekly-review.md',\n id: 'weekly-review',\n l0: 'Weekly review workflow that analyzes the week and sets goals.',\n content: `---\nid: weekly-review\ntags: [workflow, weekly, review, retrospective]\nauthor: infrastructure\nstatus: active\nschedule: \"0 17 * * 5\"\n---\n# Weekly Review\n\nAnalyze the past week's work, compress journals, and plan next week.\n\n## Instructions\n\n1. Load all journal entries from this week (Monday through today).\n2. Identify the top 3-5 themes across the week's work.\n3. Note which instincts fired most often and whether they were helpful.\n4. Identify any skills or playbooks that were missing or underperforming.\n5. Suggest concrete goals for next week (2-3 maximum).\n6. Flag any rules that seem outdated or contradictory.\n\n## Output Format\n\n**Week of [date range]**\n\n**Key themes:**\n1. [theme with brief explanation]\n2. [theme]\n3. [theme]\n\n**Instinct effectiveness:**\n- [instinct name]: [helpful / needs tuning / remove]\n\n**Gaps identified:**\n- [missing skill or playbook suggestion]\n\n**Next week's goals:**\n1. [specific, actionable goal]\n2. [goal]\n\n**Maintenance notes:**\n- [rules to review, primitives to archive, etc.]\n`,\n },\n ],\n },\n\n 'code-review': {\n name: 'code-review',\n description: 'Code review workflow — analyzes recent code changes, checks for patterns and anti-patterns, and generates review notes.',\n tags: ['code-review', 'development', 'quality'],\n files: [\n {\n path: 'workflows/code-review.md',\n id: 'code-review-workflow',\n l0: 'Code review workflow that analyzes recent changes and generates review notes.',\n content: `---\nid: code-review-workflow\ntags: [workflow, code-review, development]\nauthor: infrastructure\nstatus: active\n---\n# Code Review Workflow\n\nAnalyze recent code changes and generate structured review notes.\n\n## Instructions\n\n1. Review the most recent session where code was written or modified.\n2. Check for common issues:\n - Missing error handling (empty catches, unhandled promises)\n - Type safety violations (any usage, missing return types)\n - Security concerns (unsanitized input, hardcoded secrets)\n - Code duplication or missed reuse opportunities\n3. Check adherence to project rules and instincts.\n4. Note positive patterns worth reinforcing as instincts.\n5. Generate a structured review with severity levels.\n\n## Output Format\n\n**Review of [session/change description]**\n\n**Critical issues:**\n- [severity: high] [description]\n\n**Improvements:**\n- [severity: medium] [suggestion]\n\n**Good patterns:**\n- [pattern worth keeping / promoting to instinct]\n\n**Summary:**\n[1-2 sentence overall assessment]\n`,\n },\n {\n path: 'workflows/pr-checklist.md',\n id: 'pr-checklist',\n l0: 'PR checklist workflow that generates a pre-merge review checklist.',\n content: `---\nid: pr-checklist\ntags: [workflow, code-review, pr, checklist]\nauthor: infrastructure\nstatus: active\n---\n# PR Checklist\n\nGenerate a pre-merge checklist based on project rules and recent changes.\n\n## Instructions\n\n1. Load all active rules from the harness.\n2. For each rule category, generate a checklist item.\n3. Add standard items: tests pass, no type errors, no lint warnings.\n4. Include project-specific checks based on instincts.\n5. Output as a copy-pasteable markdown checklist.\n\n## Output Format\n\n**Pre-merge checklist:**\n\n- [ ] All tests pass\n- [ ] No TypeScript errors (tsc --noEmit)\n- [ ] No lint warnings\n- [ ] Error handling: no empty catches, async errors handled\n- [ ] Types: no \\`any\\`, explicit return types on exports\n- [ ] Security: no hardcoded secrets, input validated\n- [rule-specific items from harness rules]\n`,\n },\n ],\n },\n\n 'code-reviewer': {\n name: 'code-reviewer',\n description: 'Multi-primitive code review pack — rules for code quality, instincts for review patterns, and a skill for structured review technique.',\n tags: ['code-review', 'quality', 'rules', 'instincts', 'skills'],\n files: [\n {\n path: 'rules/code-quality.md',\n id: 'code-quality-rules',\n l0: 'Code quality rules enforcing error handling, type safety, and security.',\n content: `---\nid: code-quality-rules\ntags: [rule, code-quality, error-handling, type-safety, security]\nauthor: infrastructure\nstatus: active\n---\n# Code Quality Rules\n\nEnforceable rules for maintaining code quality across the codebase.\n\n## Error Handling\n\n- **Never** leave a catch block empty. Every catch must log or handle the error meaningfully.\n- **Always** handle async errors — every async function must have error handling at its boundary.\n- **Never** use generic \\`catch (e) { throw e }\\` — either handle or let it propagate naturally.\n\n## Type Safety\n\n- **Never** use \\`any\\` — use \\`unknown\\` with type narrowing, generics, or explicit types.\n- **Always** add explicit return types on exported functions.\n- **Never** trust external data at runtime — validate at system boundaries.\n\n## Security\n\n- **Never** hardcode secrets, API keys, or tokens in source code.\n- **Always** parameterize database queries — never concatenate user input.\n- **Never** use \\`eval()\\`, \\`innerHTML\\`, or \\`document.write()\\` with unsanitized input.\n- **Always** validate and sanitize user input at the boundary where it enters the system.\n\n## Structure\n\n- **Prefer** early returns and guard clauses — happy path last.\n- **Never** create a new utility function without first searching for an existing one.\n- **Always** prefer editing existing files over creating new ones.\n`,\n },\n {\n path: 'rules/review-standards.md',\n id: 'review-standards',\n l0: 'Review standards for consistent, actionable code review feedback.',\n content: `---\nid: review-standards\ntags: [rule, code-review, standards, feedback]\nauthor: infrastructure\nstatus: active\n---\n# Review Standards\n\nStandards for producing consistent, actionable code review feedback.\n\n## Severity Levels\n\n- **Critical**: Security vulnerability, data loss risk, or crash. Must fix before merge.\n- **High**: Logic error, missing error handling, or broken contract. Should fix before merge.\n- **Medium**: Code smell, duplication, or missed optimization. Fix soon.\n- **Low**: Style preference, naming suggestion, or minor improvement. Optional.\n\n## Review Checklist\n\n- Every review must note at least one positive pattern (reinforcement).\n- Every issue must include a suggested fix, not just a complaint.\n- Reviews should reference specific rules or instincts when applicable.\n- Avoid vague feedback like \"this could be better\" — be specific and actionable.\n\n## Scope\n\n- Review only what changed — do not nitpick unrelated code.\n- Flag pre-existing issues separately from new issues.\n- If a change is too large to review effectively, request it be split.\n`,\n },\n {\n path: 'instincts/review-pattern-detection.md',\n id: 'review-pattern-detection',\n l0: 'Instinct for detecting common code review patterns and anti-patterns.',\n content: `---\nid: review-pattern-detection\ntags: [instinct, code-review, patterns, anti-patterns]\nauthor: infrastructure\nstatus: active\n---\n# Review Pattern Detection\n\nBehavioral instinct for recognizing patterns during code review.\n\n## Trigger\n\nWhen reviewing code changes or analyzing session output that includes code modifications.\n\n## Patterns to Watch For\n\n- **Copy-paste duplication**: Same logic appearing in multiple places — suggest extraction.\n- **Error swallowing**: Catch blocks that log but don't re-throw or handle — flag as silent failure.\n- **Missing edge cases**: Functions that handle the happy path but not nulls, empty arrays, or errors.\n- **Leaky abstractions**: Implementation details exposed through public interfaces.\n- **Premature optimization**: Complex code with no measured performance need.\n- **Magic values**: Hardcoded numbers or strings that should be named constants.\n\n## Response\n\nWhen a pattern is detected, note it in the review with the pattern name and a brief explanation. Suggest the specific fix, not just the problem.\n`,\n },\n {\n path: 'instincts/refactor-opportunity.md',\n id: 'refactor-opportunity',\n l0: 'Instinct for spotting refactoring opportunities during review.',\n content: `---\nid: refactor-opportunity\ntags: [instinct, refactoring, code-review, improvement]\nauthor: infrastructure\nstatus: active\n---\n# Refactor Opportunity Detection\n\nBehavioral instinct for identifying refactoring opportunities.\n\n## Trigger\n\nWhen code changes reveal structural issues or when the same area is modified repeatedly.\n\n## Signals\n\n- **Shotgun surgery**: A single logical change requires touching 3+ files — extract shared logic.\n- **Feature envy**: A function that mostly uses data from another module — move it.\n- **Long parameter lists**: Functions with 4+ parameters — consider an options object.\n- **Nested conditionals**: 3+ levels of nesting — extract early returns or helper functions.\n- **God objects**: Classes or modules with 10+ responsibilities — split by concern.\n\n## Response\n\nNote the refactoring opportunity with the specific smell name. Only suggest refactoring if it improves clarity — not every code smell needs immediate action.\n`,\n },\n {\n path: 'skills/structured-review.md',\n id: 'structured-review-skill',\n l0: 'Skill for conducting structured, multi-pass code reviews.',\n content: `---\nid: structured-review-skill\ntags: [skill, code-review, technique, methodology]\nauthor: infrastructure\nstatus: active\n---\n# Structured Code Review\n\nA systematic approach to reviewing code changes in multiple passes.\n\n## Technique: Three-Pass Review\n\n### Pass 1: Correctness (5 minutes)\n- Does the code do what the author intended?\n- Are there logic errors, off-by-one issues, or race conditions?\n- Do all error paths terminate correctly?\n\n### Pass 2: Quality (3 minutes)\n- Does it follow project rules and coding standards?\n- Are there opportunities for reuse or simplification?\n- Is the code testable? Are there missing tests?\n\n### Pass 3: Design (2 minutes)\n- Does the change fit the existing architecture?\n- Are the abstractions at the right level?\n- Will this be easy to modify in the future?\n\n## Output Template\n\n\\`\\`\\`\n**Correctness**: [pass/issues found]\n**Quality**: [pass/improvements suggested]\n**Design**: [pass/concerns noted]\n**Verdict**: [approve / request changes / discuss]\n\\`\\`\\`\n\n## Tips\n\n- Time-box each pass to avoid rabbit holes.\n- Record the first pass findings before moving to the next — fresh eyes find different things.\n- If you find a critical issue in pass 1, stop and report it immediately.\n`,\n },\n ],\n },\n\n 'personal-assistant': {\n name: 'personal-assistant',\n description: 'Personal assistant pack — daily planning workflow, communication instincts, and a task management skill for organizing priorities.',\n tags: ['productivity', 'planning', 'communication', 'task-management'],\n files: [\n {\n path: 'workflows/daily-planner.md',\n id: 'daily-planner',\n l0: 'Daily planning workflow that organizes priorities and schedules tasks.',\n content: `---\nid: daily-planner\ntags: [workflow, daily, planning, productivity]\nauthor: infrastructure\nstatus: active\nschedule: \"0 7 * * 1-5\"\n---\n# Daily Planner\n\nCreate a structured daily plan from open tasks, calendar items, and recent context.\n\n## Instructions\n\n1. Load the most recent journal entry and state.md.\n2. Identify all open tasks from \\`unfinished_business\\` in state.\n3. Check for any scheduled workflows firing today.\n4. Categorize tasks by urgency and importance (Eisenhower matrix).\n5. Produce a time-blocked plan for the day.\n6. Estimate total focus hours needed vs. available.\n\n## Output Format\n\n**Date: [today]**\n\n**Must do today (urgent + important):**\n1. [task with estimated time]\n\n**Should do today (important, not urgent):**\n1. [task]\n\n**Quick wins (< 15 min):**\n- [task]\n\n**Scheduled:**\n- [time] [event or workflow]\n\n**Focus hours needed:** [N] / **Available:** [M]\n`,\n },\n {\n path: 'workflows/inbox-triage.md',\n id: 'inbox-triage',\n l0: 'Inbox triage workflow that processes and categorizes incoming items.',\n content: `---\nid: inbox-triage\ntags: [workflow, triage, inbox, productivity]\nauthor: infrastructure\nstatus: active\nproactive: true\n---\n# Inbox Triage\n\nProcess incoming items and categorize them for action.\n\n## Instructions\n\n1. Scan recent sessions and events for unprocessed items.\n2. For each item, determine:\n - **Action required?** Yes/No\n - **Urgency:** Now / Today / This week / Someday\n - **Category:** Task / Question / Reference / Noise\n3. Items requiring action go to state.md unfinished_business.\n4. Questions get queued for the next interactive session.\n5. Reference items get filed as session notes.\n6. Noise gets acknowledged and dropped.\n\n## Output Format\n\n**Processed [N] items:**\n\n| Item | Action | Urgency | Category |\n|------|--------|---------|----------|\n| [description] | [yes/no] | [urgency] | [category] |\n\n**Added to queue:** [count]\n**Filed as reference:** [count]\n**Dropped:** [count]\n`,\n },\n {\n path: 'instincts/clear-communication.md',\n id: 'clear-communication',\n l0: 'Instinct for clear, concise communication in responses.',\n content: `---\nid: clear-communication\ntags: [instinct, communication, clarity, writing]\nauthor: infrastructure\nstatus: active\n---\n# Clear Communication\n\nBehavioral instinct for producing clear, actionable communication.\n\n## Trigger\n\nWhen generating any output that will be read by a human — responses, summaries, reports, plans.\n\n## Principles\n\n- **Lead with the answer.** State the conclusion first, then provide supporting detail.\n- **Be specific.** Replace \"soon\" with dates, \"some\" with counts, \"improve\" with metrics.\n- **One idea per paragraph.** Dense paragraphs with multiple ideas are hard to scan.\n- **Use structure.** Headers, lists, and tables are faster to process than prose.\n- **Cut filler.** Remove \"I think\", \"basically\", \"in order to\", \"it should be noted that\".\n\n## Anti-patterns\n\n- Restating the question before answering it.\n- Using jargon without context.\n- Providing information the reader didn't ask for.\n- Hedging when confidence is high.\n\n## Response\n\nApply these principles automatically. If a draft is unclear, restructure before outputting.\n`,\n },\n {\n path: 'instincts/context-awareness.md',\n id: 'context-awareness',\n l0: 'Instinct for maintaining awareness of user context and recent history.',\n content: `---\nid: context-awareness\ntags: [instinct, context, memory, personalization]\nauthor: infrastructure\nstatus: active\n---\n# Context Awareness\n\nBehavioral instinct for maintaining awareness of what the user is working on.\n\n## Trigger\n\nAt the start of every session and when switching topics.\n\n## Behavior\n\n- Check state.md for current goals and active workflows before responding.\n- Reference recent session history when it adds value (not gratuitously).\n- Notice when the user returns to a topic from a previous session — offer continuity.\n- Track which tools, files, and topics appear frequently — these are the user's active context.\n- When the user's request is ambiguous, use recent context to disambiguate rather than asking.\n\n## Anti-patterns\n\n- Treating every session as a fresh start with no history.\n- Asking questions that were already answered in a recent session.\n- Ignoring state.md goals when prioritizing tasks.\n`,\n },\n {\n path: 'skills/task-prioritization.md',\n id: 'task-prioritization-skill',\n l0: 'Skill for prioritizing tasks using the Eisenhower matrix and energy mapping.',\n content: `---\nid: task-prioritization-skill\ntags: [skill, productivity, prioritization, planning]\nauthor: infrastructure\nstatus: active\n---\n# Task Prioritization\n\nSystematic approach to prioritizing tasks when everything feels urgent.\n\n## Technique: Eisenhower + Energy Mapping\n\n### Step 1: Classify by Urgency and Importance\n\n| | Urgent | Not Urgent |\n|---|--------|------------|\n| **Important** | Do first | Schedule |\n| **Not Important** | Delegate/batch | Drop or defer |\n\n### Step 2: Map to Energy Levels\n\n- **High energy tasks** (creative, complex decisions): Morning block\n- **Medium energy tasks** (meetings, reviews): Midday\n- **Low energy tasks** (admin, filing, routine): Late afternoon\n\n### Step 3: Apply Constraints\n\n- Maximum 3 \"must do\" items per day — more than 3 means nothing is truly prioritized.\n- If everything is urgent, ask: \"What happens if this waits 24 hours?\" If the answer is \"nothing\", it's not urgent.\n- Group similar tasks to reduce context-switching cost.\n\n## Output\n\nProduce a prioritized list with:\n1. Task name\n2. Quadrant (urgent-important, important, urgent, neither)\n3. Estimated time\n4. Suggested time block (morning/midday/afternoon)\n`,\n },\n ],\n },\n\n 'devops': {\n name: 'devops',\n description: 'DevOps safety pack — deployment rules, monitoring instincts, and an incident response skill for handling production issues.',\n tags: ['devops', 'deployment', 'monitoring', 'incident-response', 'safety'],\n files: [\n {\n path: 'rules/deployment-safety.md',\n id: 'deployment-safety-rules',\n l0: 'Deployment safety rules preventing common production failures.',\n content: `---\nid: deployment-safety-rules\ntags: [rule, devops, deployment, safety, production]\nauthor: infrastructure\nstatus: active\n---\n# Deployment Safety Rules\n\nRules to prevent common deployment failures and production incidents.\n\n## Pre-Deployment\n\n- **Never** deploy without all tests passing — no exceptions, no \"just this once\".\n- **Never** deploy directly to production — always go through staging first.\n- **Always** review the diff before deploying — automated deployments must still be human-approved.\n- **Never** deploy on Fridays after 2 PM or before holidays without explicit approval.\n- **Always** have a rollback plan before deploying — know the exact command to revert.\n\n## During Deployment\n\n- **Always** deploy incrementally — canary or blue-green, never all-at-once.\n- **Never** deploy multiple unrelated changes in a single deployment.\n- **Always** monitor error rates for 15 minutes after deployment — do not walk away.\n\n## Secrets and Config\n\n- **Never** hardcode environment-specific values — use environment variables or config maps.\n- **Never** commit secrets to version control — use secret management tools.\n- **Always** rotate credentials after any suspected exposure — assume compromise.\n\n## Database\n\n- **Never** run destructive migrations without a backup taken in the last hour.\n- **Always** test migrations on a copy of production data before running on production.\n- **Never** drop columns or tables without confirming zero references in running code.\n`,\n },\n {\n path: 'rules/infrastructure-standards.md',\n id: 'infrastructure-standards',\n l0: 'Infrastructure standards for consistent, maintainable deployments.',\n content: `---\nid: infrastructure-standards\ntags: [rule, devops, infrastructure, standards]\nauthor: infrastructure\nstatus: active\n---\n# Infrastructure Standards\n\nStandards for maintaining consistent, auditable infrastructure.\n\n## Configuration\n\n- All infrastructure must be defined as code (Terraform, Pulumi, CloudFormation, or similar).\n- Manual changes to production infrastructure require a follow-up PR within 24 hours.\n- Every service must have health check endpoints (/health, /ready).\n- Every service must emit structured logs (JSON) with request IDs for tracing.\n\n## Monitoring\n\n- Every service must have alerts for: error rate > 1%, latency p99 > 2s, availability < 99.9%.\n- Alerts must page on-call for critical issues — never rely on email-only alerts.\n- Dashboard must show: request rate, error rate, latency percentiles, resource utilization.\n\n## Access Control\n\n- Production access requires MFA and is logged.\n- Prefer role-based access over individual permissions.\n- Review access lists quarterly — remove unused permissions.\n\n## Backups\n\n- All persistent data must be backed up daily with 30-day retention.\n- Test backup restoration quarterly — untested backups are not backups.\n`,\n },\n {\n path: 'instincts/anomaly-detection.md',\n id: 'anomaly-detection',\n l0: 'Instinct for detecting anomalies in metrics, logs, and deployment behavior.',\n content: `---\nid: anomaly-detection\ntags: [instinct, devops, monitoring, anomaly, alerting]\nauthor: infrastructure\nstatus: active\n---\n# Anomaly Detection\n\nBehavioral instinct for noticing when something is off in operational metrics.\n\n## Trigger\n\nWhen reviewing deployment output, log summaries, or metric dashboards.\n\n## Signals\n\n- **Error rate spike**: Any increase > 2x baseline within 5 minutes of a deployment.\n- **Latency creep**: p99 latency increasing steadily over hours — memory leak or connection exhaustion.\n- **Silent failures**: Success rate stays high but throughput drops — upstream is failing to send.\n- **Resource divergence**: CPU/memory usage differs significantly between replicas — one is stuck.\n- **Clock skew**: Timestamps in logs jumping backward or forward — NTP issues.\n- **Cascade pattern**: Multiple unrelated services degrading simultaneously — shared dependency.\n\n## Response\n\nWhen an anomaly is detected:\n1. Note the specific metric and timeframe.\n2. Correlate with recent deployments or config changes.\n3. If post-deployment: recommend immediate rollback, investigate after.\n4. If no recent change: check upstream dependencies and shared infrastructure.\n`,\n },\n {\n path: 'instincts/change-risk-assessment.md',\n id: 'change-risk-assessment',\n l0: 'Instinct for assessing risk before infrastructure or deployment changes.',\n content: `---\nid: change-risk-assessment\ntags: [instinct, devops, risk, change-management]\nauthor: infrastructure\nstatus: active\n---\n# Change Risk Assessment\n\nBehavioral instinct for evaluating the risk of operational changes before executing them.\n\n## Trigger\n\nBefore any deployment, configuration change, or infrastructure modification.\n\n## Risk Factors\n\n- **Blast radius**: How many users or services are affected if this fails?\n- **Reversibility**: Can this be rolled back in under 5 minutes?\n- **Observability**: Will we know if this breaks something? Are there alerts?\n- **Timing**: Is this during peak traffic? During on-call handoff?\n- **Novelty**: Has this type of change been done before? By this team?\n- **Dependencies**: Does this change require coordinated changes in other services?\n\n## Risk Levels\n\n- **Low**: Small blast radius, instantly reversible, well-observed, done before.\n- **Medium**: Moderate blast radius, reversible with effort, some unknowns.\n- **High**: Large blast radius, difficult to reverse, novel, or during peak.\n\n## Response\n\nState the risk level and the primary risk factor before proceeding. For high-risk changes, suggest breaking the change into smaller, lower-risk steps.\n`,\n },\n {\n path: 'skills/incident-response.md',\n id: 'incident-response-skill',\n l0: 'Skill for structured incident response and post-incident analysis.',\n content: `---\nid: incident-response-skill\ntags: [skill, devops, incident-response, runbook]\nauthor: infrastructure\nstatus: active\n---\n# Incident Response\n\nStructured approach to handling production incidents.\n\n## Phase 1: Detect and Assess (0-5 minutes)\n\n1. **Confirm the incident**: Verify the alert is real, not a false positive.\n2. **Assess impact**: How many users affected? Is data at risk?\n3. **Classify severity**:\n - **SEV1**: Service down, data loss, or security breach. Page everyone.\n - **SEV2**: Significant degradation, partial outage. Page on-call.\n - **SEV3**: Minor degradation, workaround available. Notify team.\n4. **Communicate**: Post in incident channel with: what's happening, who's investigating, ETA for update.\n\n## Phase 2: Mitigate (5-30 minutes)\n\n1. **Check recent changes**: Was anything deployed in the last 2 hours? Roll it back.\n2. **Check dependencies**: Are upstream services healthy?\n3. **Scale or redirect**: Can traffic be shifted to healthy instances?\n4. **Apply known fix**: Check runbooks for this failure mode.\n5. **Update communication** every 15 minutes, even if no progress.\n\n## Phase 3: Resolve and Recover\n\n1. **Confirm resolution**: Metrics return to baseline for 15+ minutes.\n2. **Communicate all-clear** with summary of what happened.\n3. **Schedule post-mortem** within 48 hours.\n\n## Phase 4: Post-Mortem Template\n\n\\`\\`\\`\n**Incident**: [title]\n**Date**: [date] | **Duration**: [time]\n**Severity**: [SEV1/2/3]\n**Impact**: [users/services affected]\n\n**Timeline:**\n- [HH:MM] Alert fired\n- [HH:MM] Investigation started\n- [HH:MM] Root cause identified\n- [HH:MM] Fix applied\n- [HH:MM] Confirmed resolved\n\n**Root cause:** [description]\n**Contributing factors:** [list]\n**Action items:**\n- [ ] [preventive measure with owner and due date]\n\\`\\`\\`\n`,\n },\n ],\n },\n};\n\n/**\n * Get a builtin starter pack by name.\n * Returns null if the pack doesn't exist.\n */\nexport function getStarterPack(name: string): PackedBundle | null {\n const pack = PACKS[name];\n if (!pack) return null;\n\n const now = new Date().toISOString();\n const fileEntries: BundleFileEntry[] = pack.files.map(f => ({\n path: f.path,\n type: f.path.split('/')[0],\n id: f.id,\n l0: f.l0,\n }));\n\n const manifest: BundleManifest = {\n version: '1',\n name: `pack:${pack.name}`,\n description: pack.description,\n author: 'agent-harness',\n bundle_version: '1.0.0',\n created: now,\n types: [...new Set(fileEntries.map(f => f.type))],\n tags: pack.tags,\n files: fileEntries,\n };\n\n return {\n manifest,\n files: pack.files.map(f => ({ path: f.path, content: f.content })),\n };\n}\n\n/**\n * List all available builtin starter packs.\n */\nexport function listStarterPacks(): Array<{ name: string; description: string; fileCount: number; tags: string[] }> {\n return Object.values(PACKS).map(p => ({\n name: p.name,\n description: p.description,\n fileCount: p.files.length,\n tags: p.tags,\n }));\n}\n\n/**\n * Check if a source string is a pack reference (starts with \"pack:\").\n */\nexport function isPackReference(source: string): boolean {\n return source.startsWith('pack:');\n}\n\n/**\n * Parse the pack name from a \"pack:<name>\" reference.\n */\nexport function parsePackName(source: string): string {\n return source.slice(5); // Remove \"pack:\" prefix\n}\n"],"mappings":"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","names":[]}
@@ -5,9 +5,8 @@ import {
5
5
  saveState
6
6
  } from "./chunk-UDZIS2AQ.js";
7
7
  import "./chunk-Z2PUCXTZ.js";
8
- import "./chunk-ZZJOFKAT.js";
9
8
  export {
10
9
  loadState,
11
10
  saveState
12
11
  };
13
- //# sourceMappingURL=state-GMXILIHW.js.map
12
+ //# sourceMappingURL=state-25IQEC5C.js.map
@@ -7,7 +7,6 @@ import {
7
7
  import {
8
8
  withFileLockSync
9
9
  } from "./chunk-Z2PUCXTZ.js";
10
- import "./chunk-ZZJOFKAT.js";
11
10
 
12
11
  // src/runtime/state-merge.ts
13
12
  import { readFileSync, writeFileSync, existsSync } from "fs";
@@ -171,4 +170,4 @@ export {
171
170
  mergeState,
172
171
  saveOwnership
173
172
  };
174
- //# sourceMappingURL=state-merge-NKO5FRBA.js.map
173
+ //# sourceMappingURL=state-merge-E333OEIQ.js.map