@the-open-engine/zeroshot 5.4.0

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 (295) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/cli/commands/inspect-render.js +135 -0
  5. package/cli/commands/inspect.js +294 -0
  6. package/cli/commands/providers.js +149 -0
  7. package/cli/index.js +5431 -0
  8. package/cli/lib/first-run.js +211 -0
  9. package/cli/lib/update-checker.js +281 -0
  10. package/cli/message-formatter-utils.js +75 -0
  11. package/cli/message-formatters-normal.js +275 -0
  12. package/cli/message-formatters-watch.js +185 -0
  13. package/cluster-templates/base-templates/debug-workflow.json +422 -0
  14. package/cluster-templates/base-templates/full-workflow.json +727 -0
  15. package/cluster-templates/base-templates/heavy-validation.json +272 -0
  16. package/cluster-templates/base-templates/quick-validation.json +285 -0
  17. package/cluster-templates/base-templates/single-worker.json +71 -0
  18. package/cluster-templates/base-templates/worker-validator.json +230 -0
  19. package/cluster-templates/conductor-bootstrap.json +130 -0
  20. package/docker/zeroshot-cluster/Dockerfile +153 -0
  21. package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
  22. package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
  23. package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
  24. package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
  25. package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
  26. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
  27. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
  28. package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
  29. package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
  30. package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
  31. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
  32. package/lib/agent-cli-provider/adapters/claude.js +181 -0
  33. package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
  34. package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
  35. package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
  36. package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
  37. package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
  38. package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
  39. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
  40. package/lib/agent-cli-provider/adapters/codex.js +151 -0
  41. package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
  42. package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
  43. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
  44. package/lib/agent-cli-provider/adapters/common.js +71 -0
  45. package/lib/agent-cli-provider/adapters/common.js.map +1 -0
  46. package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
  47. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
  48. package/lib/agent-cli-provider/adapters/gemini.js +176 -0
  49. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
  50. package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
  51. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
  52. package/lib/agent-cli-provider/adapters/index.js +95 -0
  53. package/lib/agent-cli-provider/adapters/index.js.map +1 -0
  54. package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
  55. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
  56. package/lib/agent-cli-provider/adapters/opencode.js +228 -0
  57. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
  58. package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
  59. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
  60. package/lib/agent-cli-provider/contract-actions.js +140 -0
  61. package/lib/agent-cli-provider/contract-actions.js.map +1 -0
  62. package/lib/agent-cli-provider/contract-env.d.ts +11 -0
  63. package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/contract-env.js +113 -0
  65. package/lib/agent-cli-provider/contract-env.js.map +1 -0
  66. package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
  67. package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/contract-envelope.js +52 -0
  69. package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
  70. package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
  71. package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/contract-errors.js +71 -0
  73. package/lib/agent-cli-provider/contract-errors.js.map +1 -0
  74. package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
  75. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
  76. package/lib/agent-cli-provider/contract-fallback.js +102 -0
  77. package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
  78. package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
  79. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
  80. package/lib/agent-cli-provider/contract-invoke.js +89 -0
  81. package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
  82. package/lib/agent-cli-provider/contract-options.d.ts +3 -0
  83. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/contract-options.js +140 -0
  85. package/lib/agent-cli-provider/contract-options.js.map +1 -0
  86. package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
  87. package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
  88. package/lib/agent-cli-provider/contract-parse.js +81 -0
  89. package/lib/agent-cli-provider/contract-parse.js.map +1 -0
  90. package/lib/agent-cli-provider/contract-support.d.ts +18 -0
  91. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
  92. package/lib/agent-cli-provider/contract-support.js +154 -0
  93. package/lib/agent-cli-provider/contract-support.js.map +1 -0
  94. package/lib/agent-cli-provider/contract.d.ts +13 -0
  95. package/lib/agent-cli-provider/contract.d.ts.map +1 -0
  96. package/lib/agent-cli-provider/contract.js +30 -0
  97. package/lib/agent-cli-provider/contract.js.map +1 -0
  98. package/lib/agent-cli-provider/env-safety.d.ts +11 -0
  99. package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
  100. package/lib/agent-cli-provider/env-safety.js +83 -0
  101. package/lib/agent-cli-provider/env-safety.js.map +1 -0
  102. package/lib/agent-cli-provider/errors.d.ts +5 -0
  103. package/lib/agent-cli-provider/errors.d.ts.map +1 -0
  104. package/lib/agent-cli-provider/errors.js +115 -0
  105. package/lib/agent-cli-provider/errors.js.map +1 -0
  106. package/lib/agent-cli-provider/executable.d.ts +3 -0
  107. package/lib/agent-cli-provider/executable.d.ts.map +1 -0
  108. package/lib/agent-cli-provider/executable.js +24 -0
  109. package/lib/agent-cli-provider/executable.js.map +1 -0
  110. package/lib/agent-cli-provider/index.d.ts +8 -0
  111. package/lib/agent-cli-provider/index.d.ts.map +1 -0
  112. package/lib/agent-cli-provider/index.js +31 -0
  113. package/lib/agent-cli-provider/index.js.map +1 -0
  114. package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
  115. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
  116. package/lib/agent-cli-provider/invoke-evidence.js +17 -0
  117. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
  118. package/lib/agent-cli-provider/json.d.ts +16 -0
  119. package/lib/agent-cli-provider/json.d.ts.map +1 -0
  120. package/lib/agent-cli-provider/json.js +110 -0
  121. package/lib/agent-cli-provider/json.js.map +1 -0
  122. package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
  123. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
  124. package/lib/agent-cli-provider/log-prefix.js +22 -0
  125. package/lib/agent-cli-provider/log-prefix.js.map +1 -0
  126. package/lib/agent-cli-provider/process-runner.d.ts +17 -0
  127. package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
  128. package/lib/agent-cli-provider/process-runner.js +89 -0
  129. package/lib/agent-cli-provider/process-runner.js.map +1 -0
  130. package/lib/agent-cli-provider/redaction.d.ts +9 -0
  131. package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
  132. package/lib/agent-cli-provider/redaction.js +227 -0
  133. package/lib/agent-cli-provider/redaction.js.map +1 -0
  134. package/lib/agent-cli-provider/schema.d.ts +5 -0
  135. package/lib/agent-cli-provider/schema.d.ts.map +1 -0
  136. package/lib/agent-cli-provider/schema.js +136 -0
  137. package/lib/agent-cli-provider/schema.js.map +1 -0
  138. package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
  139. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
  140. package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
  141. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
  142. package/lib/agent-cli-provider/types.d.ts +194 -0
  143. package/lib/agent-cli-provider/types.d.ts.map +1 -0
  144. package/lib/agent-cli-provider/types.js +12 -0
  145. package/lib/agent-cli-provider/types.js.map +1 -0
  146. package/lib/completion.js +174 -0
  147. package/lib/detached-startup.js +220 -0
  148. package/lib/docker-config.js +220 -0
  149. package/lib/git-remote-utils.js +165 -0
  150. package/lib/id-detector.js +55 -0
  151. package/lib/provider-defaults.js +62 -0
  152. package/lib/provider-detection.js +59 -0
  153. package/lib/provider-names.js +57 -0
  154. package/lib/repo-settings.js +69 -0
  155. package/lib/settings/claude-auth.js +78 -0
  156. package/lib/settings.js +542 -0
  157. package/lib/start-cluster.js +321 -0
  158. package/lib/stream-json-parser.js +67 -0
  159. package/package.json +162 -0
  160. package/scripts/fix-node-pty-permissions.js +75 -0
  161. package/scripts/record-demo.sh +279 -0
  162. package/scripts/setup-merge-queue.sh +170 -0
  163. package/scripts/test-install.sh +40 -0
  164. package/scripts/validate-templates.js +107 -0
  165. package/src/agent/agent-config.js +266 -0
  166. package/src/agent/agent-context-builder.js +189 -0
  167. package/src/agent/agent-context-sections.js +338 -0
  168. package/src/agent/agent-context-sources.js +147 -0
  169. package/src/agent/agent-hook-executor.js +721 -0
  170. package/src/agent/agent-input-injector.js +141 -0
  171. package/src/agent/agent-lifecycle.js +982 -0
  172. package/src/agent/agent-quality-gate-schema.js +93 -0
  173. package/src/agent/agent-quality-gates-context.js +51 -0
  174. package/src/agent/agent-stuck-detector.js +256 -0
  175. package/src/agent/agent-task-executor.js +2028 -0
  176. package/src/agent/agent-trigger-evaluator.js +67 -0
  177. package/src/agent/context-metrics.js +160 -0
  178. package/src/agent/context-pack-builder.js +367 -0
  179. package/src/agent/context-replay-policy.js +51 -0
  180. package/src/agent/guidance-queue.js +77 -0
  181. package/src/agent/output-extraction.js +367 -0
  182. package/src/agent/output-reformatter.js +175 -0
  183. package/src/agent/pr-verification.js +653 -0
  184. package/src/agent/rate-limit-backoff.js +82 -0
  185. package/src/agent/schema-utils.js +146 -0
  186. package/src/agent/validation-platform.js +35 -0
  187. package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
  188. package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
  189. package/src/agent-cli-provider/adapters/claude.ts +247 -0
  190. package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
  191. package/src/agent-cli-provider/adapters/codex.ts +217 -0
  192. package/src/agent-cli-provider/adapters/common.ts +124 -0
  193. package/src/agent-cli-provider/adapters/gemini.ts +243 -0
  194. package/src/agent-cli-provider/adapters/index.ts +126 -0
  195. package/src/agent-cli-provider/adapters/opencode.ts +286 -0
  196. package/src/agent-cli-provider/contract-actions.ts +150 -0
  197. package/src/agent-cli-provider/contract-env.ts +111 -0
  198. package/src/agent-cli-provider/contract-envelope.ts +110 -0
  199. package/src/agent-cli-provider/contract-errors.ts +66 -0
  200. package/src/agent-cli-provider/contract-fallback.ts +121 -0
  201. package/src/agent-cli-provider/contract-invoke.ts +104 -0
  202. package/src/agent-cli-provider/contract-options.ts +173 -0
  203. package/src/agent-cli-provider/contract-parse.ts +94 -0
  204. package/src/agent-cli-provider/contract-support.ts +167 -0
  205. package/src/agent-cli-provider/contract.ts +56 -0
  206. package/src/agent-cli-provider/env-safety.ts +82 -0
  207. package/src/agent-cli-provider/errors.ts +122 -0
  208. package/src/agent-cli-provider/executable.ts +24 -0
  209. package/src/agent-cli-provider/index.ts +83 -0
  210. package/src/agent-cli-provider/invoke-evidence.ts +18 -0
  211. package/src/agent-cli-provider/json.ts +114 -0
  212. package/src/agent-cli-provider/log-prefix.ts +20 -0
  213. package/src/agent-cli-provider/process-runner.ts +145 -0
  214. package/src/agent-cli-provider/redaction.ts +282 -0
  215. package/src/agent-cli-provider/schema.ts +115 -0
  216. package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
  217. package/src/agent-cli-provider/types.ts +237 -0
  218. package/src/agent-wrapper.js +615 -0
  219. package/src/agents/git-pusher-template.js +705 -0
  220. package/src/attach/attach-client.js +438 -0
  221. package/src/attach/attach-server.js +543 -0
  222. package/src/attach/index.js +37 -0
  223. package/src/attach/protocol.js +220 -0
  224. package/src/attach/ring-buffer.js +121 -0
  225. package/src/attach/send-input.js +88 -0
  226. package/src/attach/socket-discovery.js +267 -0
  227. package/src/claude-task-runner.js +661 -0
  228. package/src/config-router.js +89 -0
  229. package/src/config-validator.js +2202 -0
  230. package/src/copy-worker.js +43 -0
  231. package/src/guidance-topics.js +10 -0
  232. package/src/input-helpers.js +65 -0
  233. package/src/isolation-manager.js +1734 -0
  234. package/src/issue-providers/README.md +305 -0
  235. package/src/issue-providers/azure-devops-provider.js +307 -0
  236. package/src/issue-providers/base-provider.js +232 -0
  237. package/src/issue-providers/github-provider.js +210 -0
  238. package/src/issue-providers/gitlab-provider.js +262 -0
  239. package/src/issue-providers/index.js +196 -0
  240. package/src/issue-providers/jira-provider.js +260 -0
  241. package/src/ledger.js +692 -0
  242. package/src/lib/gc.js +232 -0
  243. package/src/lib/safe-exec.js +88 -0
  244. package/src/logic-engine.js +201 -0
  245. package/src/message-buffer.js +81 -0
  246. package/src/message-bus-bridge.js +144 -0
  247. package/src/message-bus.js +256 -0
  248. package/src/name-generator.js +232 -0
  249. package/src/orchestrator.js +3924 -0
  250. package/src/preflight.js +712 -0
  251. package/src/process-metrics.js +608 -0
  252. package/src/providers/anthropic/index.js +3 -0
  253. package/src/providers/base-provider.js +355 -0
  254. package/src/providers/capabilities.js +60 -0
  255. package/src/providers/google/index.js +3 -0
  256. package/src/providers/index.js +293 -0
  257. package/src/providers/openai/index.js +3 -0
  258. package/src/providers/opencode/index.js +3 -0
  259. package/src/quality-gates.js +143 -0
  260. package/src/schemas/sub-cluster.js +208 -0
  261. package/src/state-snapshot.js +398 -0
  262. package/src/state-snapshotter.js +142 -0
  263. package/src/status-footer.js +1026 -0
  264. package/src/sub-cluster-wrapper.js +693 -0
  265. package/src/task-runner.js +30 -0
  266. package/src/template-resolver.js +425 -0
  267. package/src/template-validation/index.js +338 -0
  268. package/src/template-validation/simulate-consensus-gates.js +324 -0
  269. package/src/template-validation/simulate-random-topology.js +541 -0
  270. package/src/template-validation/simulate-two-stage-validation.js +270 -0
  271. package/src/worktree-claude-config.js +135 -0
  272. package/src/worktree-tooling-env.js +150 -0
  273. package/task-lib/attachable-watcher.js +381 -0
  274. package/task-lib/commands/clean.js +50 -0
  275. package/task-lib/commands/episodes.js +105 -0
  276. package/task-lib/commands/get-log-path.js +23 -0
  277. package/task-lib/commands/kill.js +32 -0
  278. package/task-lib/commands/list.js +105 -0
  279. package/task-lib/commands/logs.js +439 -0
  280. package/task-lib/commands/resume.js +42 -0
  281. package/task-lib/commands/run.js +57 -0
  282. package/task-lib/commands/schedule.js +105 -0
  283. package/task-lib/commands/scheduler-cmd.js +96 -0
  284. package/task-lib/commands/schedules.js +148 -0
  285. package/task-lib/commands/status.js +44 -0
  286. package/task-lib/commands/unschedule.js +16 -0
  287. package/task-lib/completion.js +9 -0
  288. package/task-lib/config.js +11 -0
  289. package/task-lib/name-generator.js +230 -0
  290. package/task-lib/package.json +3 -0
  291. package/task-lib/provider-helper-runtime.js +29 -0
  292. package/task-lib/runner.js +190 -0
  293. package/task-lib/scheduler.js +252 -0
  294. package/task-lib/store.js +529 -0
  295. package/task-lib/watcher.js +305 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * AgentTriggerEvaluator - Trigger matching and logic evaluation
3
+ *
4
+ * Provides:
5
+ * - Trigger matching based on message topics
6
+ * - Logic evaluation via LogicEngine
7
+ * - Trigger action determination
8
+ */
9
+
10
+ /**
11
+ * Find trigger matching the message topic
12
+ * @param {Object} params - Evaluation parameters
13
+ * @param {Array} params.triggers - Agent triggers configuration
14
+ * @param {Object} params.message - Message to match against
15
+ * @returns {Object|null} Matching trigger or null
16
+ */
17
+ function findMatchingTrigger({ triggers, message }) {
18
+ if (!triggers) {
19
+ return null;
20
+ }
21
+
22
+ return triggers.find((trigger) => {
23
+ // Match exact topic or wildcard
24
+ if (trigger.topic === '*' || trigger.topic === message.topic) {
25
+ return true;
26
+ }
27
+ // Match topic prefix (e.g., "VALIDATION_*")
28
+ if (trigger.topic.endsWith('*')) {
29
+ const prefix = trigger.topic.slice(0, -1);
30
+ return message.topic.startsWith(prefix);
31
+ }
32
+ return false;
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Evaluate trigger logic
38
+ * @param {Object} params - Evaluation parameters
39
+ * @param {Object} params.trigger - Trigger to evaluate
40
+ * @param {Object} params.message - Triggering message
41
+ * @param {Object} params.agent - Agent context (id, role, iteration, cluster_id)
42
+ * @param {Object} params.logicEngine - LogicEngine instance
43
+ * @returns {boolean} Whether trigger logic passed
44
+ */
45
+ function evaluateTrigger({ trigger, message, agent, logicEngine }) {
46
+ if (!trigger.logic || !trigger.logic.script) {
47
+ return true; // No logic = always true
48
+ }
49
+
50
+ // NO TRY/CATCH - let errors propagate and crash
51
+ return logicEngine.evaluate(trigger.logic.script, agent, message);
52
+ }
53
+
54
+ /**
55
+ * Get trigger action type
56
+ * @param {Object} trigger - Trigger object
57
+ * @returns {string} Action type ('execute_task' or 'stop_cluster')
58
+ */
59
+ function getTriggerAction(trigger) {
60
+ return trigger.action || 'execute_task';
61
+ }
62
+
63
+ module.exports = {
64
+ findMatchingTrigger,
65
+ evaluateTrigger,
66
+ getTriggerAction,
67
+ };
@@ -0,0 +1,160 @@
1
+ const TOKENS_PER_CHAR_ESTIMATE = 4;
2
+
3
+ function estimateTokensFromChars(chars) {
4
+ if (!Number.isFinite(chars) || chars <= 0) {
5
+ return 0;
6
+ }
7
+
8
+ return Math.ceil(chars / TOKENS_PER_CHAR_ESTIMATE);
9
+ }
10
+
11
+ function buildSectionMetrics(sections) {
12
+ const sectionMetrics = {};
13
+ let totalChars = 0;
14
+
15
+ for (const [sectionName, text] of Object.entries(sections)) {
16
+ const safeText = typeof text === 'string' ? text : '';
17
+ const chars = safeText.length;
18
+ const estimatedTokens = estimateTokensFromChars(chars);
19
+ sectionMetrics[sectionName] = { chars, estimatedTokens };
20
+ totalChars += chars;
21
+ }
22
+
23
+ return { sectionMetrics, totalChars };
24
+ }
25
+
26
+ function buildSectionMetricsFromPacks(packs) {
27
+ const sectionMetrics = {};
28
+ let totalChars = 0;
29
+
30
+ for (const pack of packs) {
31
+ if (pack.status !== 'included') continue;
32
+ const sectionName = pack.section || pack.id || 'unknown';
33
+ const chars = Number.isFinite(pack.chars) ? pack.chars : 0;
34
+ if (!sectionMetrics[sectionName]) {
35
+ sectionMetrics[sectionName] = { chars: 0, estimatedTokens: 0 };
36
+ }
37
+ sectionMetrics[sectionName].chars += chars;
38
+ totalChars += chars;
39
+ }
40
+
41
+ for (const section of Object.values(sectionMetrics)) {
42
+ section.estimatedTokens = estimateTokensFromChars(section.chars);
43
+ }
44
+
45
+ return { sectionMetrics, totalChars };
46
+ }
47
+
48
+ function resolveLegacyMaxTokens(strategy) {
49
+ if (!strategy) {
50
+ return 100000;
51
+ }
52
+
53
+ return strategy.maxTokens || 100000;
54
+ }
55
+
56
+ function buildContextMetrics({
57
+ clusterId,
58
+ agentId,
59
+ role,
60
+ iteration,
61
+ triggeringMessage,
62
+ strategy,
63
+ sections,
64
+ packs,
65
+ budget,
66
+ truncation,
67
+ }) {
68
+ const maxTokens = resolveLegacyMaxTokens(strategy);
69
+ const sourcesCount = Array.isArray(strategy?.sources) ? strategy.sources.length : 0;
70
+ const packMetrics = Array.isArray(packs) ? packs : [];
71
+
72
+ let sectionMetrics = {};
73
+ let totalChars = 0;
74
+ if (packMetrics.length > 0) {
75
+ const packTotals = buildSectionMetricsFromPacks(packMetrics);
76
+ sectionMetrics = packTotals.sectionMetrics;
77
+ totalChars = packTotals.totalChars;
78
+ } else if (sections) {
79
+ const sectionTotals = buildSectionMetrics(sections);
80
+ sectionMetrics = sectionTotals.sectionMetrics;
81
+ totalChars = sectionTotals.totalChars;
82
+ }
83
+
84
+ return {
85
+ clusterId,
86
+ agentId,
87
+ role,
88
+ iteration,
89
+ triggeredBy: triggeringMessage?.topic || null,
90
+ triggerFrom: triggeringMessage?.sender || null,
91
+ strategy: {
92
+ maxTokens,
93
+ sourcesCount,
94
+ },
95
+ budget: {
96
+ maxTokens: budget?.maxTokens ?? maxTokens,
97
+ remainingTokens: budget?.remainingTokens === undefined ? null : budget?.remainingTokens,
98
+ overBudgetTokens: budget?.overBudgetTokens ?? 0,
99
+ finalTokens: budget?.finalTokens ?? estimateTokensFromChars(totalChars),
100
+ },
101
+ packs: packMetrics,
102
+ sections: sectionMetrics,
103
+ total: {
104
+ chars: totalChars,
105
+ estimatedTokens: estimateTokensFromChars(totalChars),
106
+ },
107
+ truncation: {
108
+ maxContextChars: truncation?.maxContextChars || {
109
+ applied: false,
110
+ beforeChars: totalChars,
111
+ afterChars: totalChars,
112
+ },
113
+ },
114
+ };
115
+ }
116
+
117
+ function updateTotalMetrics(metrics, chars) {
118
+ if (!metrics || !Number.isFinite(chars)) {
119
+ return;
120
+ }
121
+
122
+ metrics.total = {
123
+ chars,
124
+ estimatedTokens: estimateTokensFromChars(chars),
125
+ };
126
+
127
+ if (metrics.budget) {
128
+ metrics.budget.finalTokens = estimateTokensFromChars(chars);
129
+ }
130
+
131
+ if (metrics.truncation?.maxContextChars) {
132
+ metrics.truncation.maxContextChars.afterChars = chars;
133
+ }
134
+ }
135
+
136
+ function emitContextMetrics(metrics, { messageBus, clusterId, agentId }) {
137
+ if (process.env.ZEROSHOT_CONTEXT_METRICS === '1') {
138
+ console.log('[ContextMetrics]', JSON.stringify(metrics));
139
+ }
140
+
141
+ if (process.env.ZEROSHOT_CONTEXT_METRICS_LEDGER === '1' && messageBus?.publish) {
142
+ messageBus.publish({
143
+ cluster_id: clusterId,
144
+ topic: 'CONTEXT_METRICS',
145
+ sender: agentId,
146
+ receiver: 'system',
147
+ content: {
148
+ data: metrics,
149
+ },
150
+ });
151
+ }
152
+ }
153
+
154
+ module.exports = {
155
+ estimateTokensFromChars,
156
+ resolveLegacyMaxTokens,
157
+ buildContextMetrics,
158
+ updateTotalMetrics,
159
+ emitContextMetrics,
160
+ };
@@ -0,0 +1,367 @@
1
+ const { estimateTokensFromChars } = require('./context-metrics');
2
+
3
+ const PRIORITY_RANK = {
4
+ required: 0,
5
+ high: 1,
6
+ medium: 2,
7
+ low: 3,
8
+ };
9
+
10
+ const DEFAULT_PRIORITY = 'medium';
11
+ const TRUNCATION_SUFFIX = '\n\n[Context truncated to fit limit]\n';
12
+
13
+ function normalizePriority(priority, required) {
14
+ if (required) return 'required';
15
+ if (priority && PRIORITY_RANK[priority] !== undefined) return priority;
16
+ return DEFAULT_PRIORITY;
17
+ }
18
+
19
+ function normalizePack(pack, index) {
20
+ const priority = normalizePriority(pack.priority, pack.required);
21
+ return {
22
+ ...pack,
23
+ priority,
24
+ required: pack.required || priority === 'required',
25
+ order: pack.order ?? index,
26
+ };
27
+ }
28
+
29
+ function renderVariant(pack, variant, cache) {
30
+ const cacheKey = `${pack.id}:${variant}`;
31
+ if (cache.has(cacheKey)) return cache.get(cacheKey);
32
+
33
+ let text = '';
34
+ if (variant === 'full') {
35
+ text = typeof pack.render === 'function' ? pack.render() : '';
36
+ } else if (variant === 'compact') {
37
+ text = typeof pack.compact === 'function' ? pack.compact() : '';
38
+ }
39
+
40
+ if (typeof text !== 'string') {
41
+ text = '';
42
+ }
43
+
44
+ const chars = text.length;
45
+ const estimatedTokens = estimateTokensFromChars(chars);
46
+ const rendered = { text, chars, estimatedTokens };
47
+ cache.set(cacheKey, rendered);
48
+ return rendered;
49
+ }
50
+
51
+ function sortByPriorityThenOrder(a, b) {
52
+ const priorityDelta = PRIORITY_RANK[a.priority] - PRIORITY_RANK[b.priority];
53
+ if (priorityDelta !== 0) return priorityDelta;
54
+ return a.order - b.order;
55
+ }
56
+
57
+ function sortByOrder(a, b) {
58
+ return a.order - b.order;
59
+ }
60
+
61
+ function sortByPriorityDescThenOrderDesc(a, b) {
62
+ const priorityDelta = PRIORITY_RANK[b.priority] - PRIORITY_RANK[a.priority];
63
+ if (priorityDelta !== 0) return priorityDelta;
64
+ return b.order - a.order;
65
+ }
66
+
67
+ function selectVariant(pack, remainingTokens, cache) {
68
+ const full = renderVariant(pack, 'full', cache);
69
+ const compact = pack.compact ? renderVariant(pack, 'compact', cache) : null;
70
+
71
+ const hasFull = full.chars > 0;
72
+ const hasCompact = compact && compact.chars > 0;
73
+
74
+ if (!hasFull && !hasCompact) {
75
+ return {
76
+ status: 'skipped',
77
+ variant: null,
78
+ reason: 'empty',
79
+ chars: 0,
80
+ estimatedTokens: 0,
81
+ };
82
+ }
83
+
84
+ if (pack.required) {
85
+ let chosen = full;
86
+ let variant = 'full';
87
+
88
+ if (!hasFull && hasCompact) {
89
+ chosen = compact;
90
+ variant = 'compact';
91
+ } else if (
92
+ Number.isFinite(remainingTokens) &&
93
+ hasCompact &&
94
+ full.estimatedTokens > remainingTokens
95
+ ) {
96
+ if (
97
+ compact.estimatedTokens <= remainingTokens ||
98
+ compact.estimatedTokens < full.estimatedTokens
99
+ ) {
100
+ chosen = compact;
101
+ variant = 'compact';
102
+ }
103
+ }
104
+
105
+ return {
106
+ status: 'included',
107
+ variant,
108
+ chars: chosen.chars,
109
+ estimatedTokens: chosen.estimatedTokens,
110
+ text: chosen.text,
111
+ };
112
+ }
113
+
114
+ if (!Number.isFinite(remainingTokens) || full.estimatedTokens <= remainingTokens) {
115
+ return {
116
+ status: 'included',
117
+ variant: 'full',
118
+ chars: full.chars,
119
+ estimatedTokens: full.estimatedTokens,
120
+ text: full.text,
121
+ };
122
+ }
123
+
124
+ if (hasCompact && compact.estimatedTokens <= remainingTokens) {
125
+ return {
126
+ status: 'included',
127
+ variant: 'compact',
128
+ chars: compact.chars,
129
+ estimatedTokens: compact.estimatedTokens,
130
+ text: compact.text,
131
+ };
132
+ }
133
+
134
+ return {
135
+ status: 'skipped',
136
+ variant: null,
137
+ reason: 'budget',
138
+ chars: 0,
139
+ estimatedTokens: 0,
140
+ };
141
+ }
142
+
143
+ function truncateText(text, targetChars) {
144
+ if (text.length <= targetChars) {
145
+ return { text, truncated: false };
146
+ }
147
+
148
+ if (targetChars <= 0) {
149
+ return { text: '', truncated: true };
150
+ }
151
+
152
+ if (targetChars <= TRUNCATION_SUFFIX.length) {
153
+ return { text: text.slice(0, targetChars), truncated: true };
154
+ }
155
+
156
+ const sliceLength = targetChars - TRUNCATION_SUFFIX.length;
157
+ return { text: text.slice(0, sliceLength) + TRUNCATION_SUFFIX, truncated: true };
158
+ }
159
+
160
+ function applyMaxCharsGuard({ packs, selected, decisions, cache, maxChars, totalChars }) {
161
+ let currentChars = totalChars;
162
+ if (!Number.isFinite(maxChars) || currentChars <= maxChars) {
163
+ return { applied: false, beforeChars: totalChars, afterChars: totalChars };
164
+ }
165
+
166
+ const includedOptional = packs
167
+ .filter((pack) => selected.has(pack.id) && !pack.required)
168
+ .sort(sortByPriorityDescThenOrderDesc);
169
+
170
+ for (const pack of includedOptional) {
171
+ if (currentChars <= maxChars) break;
172
+ const decision = decisions.get(pack.id);
173
+ if (!decision || decision.variant === 'compact' || !pack.compact) continue;
174
+
175
+ const compact = renderVariant(pack, 'compact', cache);
176
+ if (compact.chars === 0 || compact.chars >= decision.chars) continue;
177
+
178
+ const previousChars = decision.chars;
179
+ selected.set(pack.id, { ...compact, variant: 'compact' });
180
+ decision.variant = 'compact';
181
+ decision.chars = compact.chars;
182
+ decision.estimatedTokens = compact.estimatedTokens;
183
+ decision.reason = decision.reason || 'max_chars';
184
+
185
+ currentChars -= previousChars - compact.chars;
186
+ currentChars = Math.max(0, currentChars);
187
+ }
188
+
189
+ for (const pack of includedOptional) {
190
+ if (currentChars <= maxChars) break;
191
+ if (!selected.has(pack.id)) continue;
192
+
193
+ const decision = decisions.get(pack.id);
194
+ currentChars -= decision?.chars || 0;
195
+ selected.delete(pack.id);
196
+ if (decision) {
197
+ decision.status = 'skipped';
198
+ decision.reason = decision.reason || 'max_chars';
199
+ decision.chars = 0;
200
+ decision.estimatedTokens = 0;
201
+ }
202
+ }
203
+
204
+ if (currentChars > maxChars) {
205
+ let overage = currentChars - maxChars;
206
+ const requiredCandidates = packs
207
+ .filter((pack) => selected.has(pack.id) && pack.required)
208
+ .sort((a, b) => {
209
+ const preserveDelta = (a.preserve ? 1 : 0) - (b.preserve ? 1 : 0);
210
+ if (preserveDelta !== 0) return preserveDelta;
211
+ const sizeDelta = (selected.get(b.id)?.chars || 0) - (selected.get(a.id)?.chars || 0);
212
+ if (sizeDelta !== 0) return sizeDelta;
213
+ return b.order - a.order;
214
+ });
215
+
216
+ for (const pack of requiredCandidates) {
217
+ if (overage <= 0) break;
218
+ const decision = decisions.get(pack.id);
219
+ const selectedPack = selected.get(pack.id);
220
+ if (!decision || !selectedPack) continue;
221
+
222
+ const targetChars = Math.max(0, selectedPack.chars - overage);
223
+ const truncated = truncateText(selectedPack.text, targetChars);
224
+ if (truncated.text.length === selectedPack.chars) continue;
225
+
226
+ const newChars = truncated.text.length;
227
+ const reduced = selectedPack.chars - newChars;
228
+ overage -= reduced;
229
+
230
+ selected.set(pack.id, {
231
+ text: truncated.text,
232
+ chars: newChars,
233
+ estimatedTokens: estimateTokensFromChars(newChars),
234
+ variant: selectedPack.variant,
235
+ });
236
+
237
+ decision.chars = newChars;
238
+ decision.estimatedTokens = estimateTokensFromChars(newChars);
239
+ decision.truncated = true;
240
+ decision.reason = decision.reason || 'max_chars';
241
+ }
242
+ }
243
+
244
+ const afterChars = Array.from(selected.values()).reduce((sum, item) => sum + item.chars, 0);
245
+ return { applied: true, beforeChars: totalChars, afterChars };
246
+ }
247
+
248
+ function buildContextPacks({ packs, maxTokens, maxChars }) {
249
+ const normalized = packs.map(normalizePack);
250
+ const selectionOrder = normalized.slice().sort(sortByPriorityThenOrder);
251
+ const renderCache = new Map();
252
+ const decisions = new Map();
253
+ const selected = new Map();
254
+
255
+ let remainingTokens = Number.isFinite(maxTokens) ? maxTokens : Infinity;
256
+ let overBudgetTokens = 0;
257
+
258
+ for (const pack of selectionOrder) {
259
+ const selection = selectVariant(pack, remainingTokens, renderCache);
260
+ const decision = {
261
+ id: pack.id,
262
+ section: pack.section || null,
263
+ priority: pack.priority,
264
+ required: pack.required,
265
+ status: selection.status,
266
+ variant: selection.variant,
267
+ chars: selection.chars,
268
+ estimatedTokens: selection.estimatedTokens,
269
+ order: pack.order,
270
+ reason: selection.reason || null,
271
+ };
272
+
273
+ decisions.set(pack.id, decision);
274
+
275
+ if (selection.status !== 'included') {
276
+ continue;
277
+ }
278
+
279
+ selected.set(pack.id, {
280
+ text: selection.text,
281
+ chars: selection.chars,
282
+ estimatedTokens: selection.estimatedTokens,
283
+ variant: selection.variant,
284
+ });
285
+
286
+ if (!Number.isFinite(remainingTokens)) {
287
+ continue;
288
+ }
289
+
290
+ if (selection.estimatedTokens > remainingTokens) {
291
+ overBudgetTokens += selection.estimatedTokens - remainingTokens;
292
+ remainingTokens = 0;
293
+ } else {
294
+ remainingTokens -= selection.estimatedTokens;
295
+ }
296
+ }
297
+
298
+ const ordered = normalized.slice().sort(sortByOrder);
299
+ let context = '';
300
+ for (const pack of ordered) {
301
+ const selectedPack = selected.get(pack.id);
302
+ if (selectedPack) {
303
+ context += selectedPack.text;
304
+ }
305
+ }
306
+
307
+ const totalChars = context.length;
308
+ const truncation = applyMaxCharsGuard({
309
+ packs: ordered,
310
+ selected,
311
+ decisions,
312
+ cache: renderCache,
313
+ maxChars,
314
+ totalChars,
315
+ });
316
+
317
+ if (truncation.applied) {
318
+ context = '';
319
+ for (const pack of ordered) {
320
+ const selectedPack = selected.get(pack.id);
321
+ if (selectedPack) {
322
+ context += selectedPack.text;
323
+ }
324
+ }
325
+ }
326
+
327
+ const finalChars = context.length;
328
+ const finalTokens = estimateTokensFromChars(finalChars);
329
+ const packDecisions = ordered.map((pack) => {
330
+ const decision = decisions.get(pack.id);
331
+ return {
332
+ id: decision.id,
333
+ section: decision.section,
334
+ priority: decision.priority,
335
+ required: decision.required,
336
+ status: decision.status,
337
+ variant: decision.variant,
338
+ chars: decision.chars,
339
+ estimatedTokens: decision.estimatedTokens,
340
+ order: decision.order,
341
+ reason: decision.reason,
342
+ truncated: decision.truncated || false,
343
+ };
344
+ });
345
+
346
+ return {
347
+ context,
348
+ packDecisions,
349
+ budget: {
350
+ maxTokens,
351
+ remainingTokens: Number.isFinite(remainingTokens) ? remainingTokens : null,
352
+ overBudgetTokens,
353
+ finalTokens,
354
+ },
355
+ truncation: {
356
+ maxContextChars: {
357
+ applied: truncation.applied,
358
+ beforeChars: truncation.beforeChars,
359
+ afterChars: truncation.afterChars,
360
+ },
361
+ },
362
+ };
363
+ }
364
+
365
+ module.exports = {
366
+ buildContextPacks,
367
+ };
@@ -0,0 +1,51 @@
1
+ const RAW_LOG_ONLY_REPLAY_POLICY = 'raw_log_only';
2
+ const CONTEXT_REPLAY_POLICY = 'context';
3
+
4
+ function buildRawLogOnlyMetadata(extra = {}) {
5
+ return {
6
+ ...extra,
7
+ contextSafe: false,
8
+ replayPolicy: RAW_LOG_ONLY_REPLAY_POLICY,
9
+ };
10
+ }
11
+
12
+ function getReplayPolicy(message) {
13
+ return message?.metadata?.replayPolicy ?? message?.content?.data?.replayPolicy;
14
+ }
15
+
16
+ function getContextSafe(message) {
17
+ if (typeof message?.metadata?.contextSafe === 'boolean') {
18
+ return message.metadata.contextSafe;
19
+ }
20
+
21
+ if (typeof message?.content?.data?.contextSafe === 'boolean') {
22
+ return message.content.data.contextSafe;
23
+ }
24
+
25
+ return null;
26
+ }
27
+
28
+ function isReplayableMessage(message) {
29
+ const contextSafe = getContextSafe(message);
30
+ if (contextSafe !== null) {
31
+ return contextSafe;
32
+ }
33
+
34
+ const replayPolicy = getReplayPolicy(message);
35
+ if (replayPolicy === RAW_LOG_ONLY_REPLAY_POLICY) {
36
+ return false;
37
+ }
38
+
39
+ if (replayPolicy === CONTEXT_REPLAY_POLICY) {
40
+ return true;
41
+ }
42
+
43
+ return message?.topic !== 'AGENT_OUTPUT';
44
+ }
45
+
46
+ module.exports = {
47
+ RAW_LOG_ONLY_REPLAY_POLICY,
48
+ CONTEXT_REPLAY_POLICY,
49
+ buildRawLogOnlyMetadata,
50
+ isReplayableMessage,
51
+ };