avorelo 0.1.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 (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+
3
+ const { spawnSync } = require("child_process");
4
+ const { classifyTask } = require("./risk-classifier");
5
+ const {
6
+ appendJsonl,
7
+ tryAcquireRunLock,
8
+ releaseRunLock,
9
+ } = require("./state");
10
+ const { recordWorkerFailure } = require("./model-discovery");
11
+ const { verifyWorkspace } = require("./verification-manager");
12
+
13
+ function runCommand(command, args, options = {}) {
14
+ const runner = options.commandRunner;
15
+ if (typeof runner === "function") {
16
+ return runner(command, args, options);
17
+ }
18
+ return spawnSync(command, args, {
19
+ cwd: options.cwd,
20
+ env: options.env || process.env,
21
+ encoding: "utf8",
22
+ timeout: options.timeoutMs || 20_000,
23
+ });
24
+ }
25
+
26
+ function summarizeGitDiff(cwd, options = {}) {
27
+ const result = runCommand("git", ["diff", "--stat", "--no-ext-diff"], {
28
+ cwd,
29
+ env: options.env || process.env,
30
+ timeoutMs: 10_000,
31
+ commandRunner: options.commandRunner,
32
+ });
33
+ return String(result.stdout || result.stderr || "").trim() || "No diff output captured.";
34
+ }
35
+
36
+ function highRiskTouchedFiles(taskContext, touchedFiles) {
37
+ const next = classifyTask(taskContext.taskText, { changedFiles: touchedFiles || [] });
38
+ return next.risk === "high" && taskContext.classification.risk !== "high";
39
+ }
40
+
41
+ function unrelatedFilesChanged(taskContext, touchedFiles) {
42
+ if (!Array.isArray(taskContext.selectedFiles) || taskContext.selectedFiles.length === 0) return false;
43
+ const allowed = new Set(taskContext.selectedFiles.map((item) => String(item).replace(/\\/g, "/")));
44
+ return (touchedFiles || []).some((item) => !allowed.has(String(item).replace(/\\/g, "/")));
45
+ }
46
+
47
+ function executeRoute({ cwd, runId, route, taskContext, adapters, settings, dryRun = false, executionLogPath = null, options = {} }) {
48
+ const lockResult = tryAcquireRunLock(cwd, {
49
+ runId,
50
+ task: taskContext.taskText,
51
+ surface: taskContext.surface,
52
+ }, {
53
+ timeoutMs: (settings.lockTimeoutMinutes || 20) * 60 * 1000,
54
+ });
55
+ if (!lockResult.ok) {
56
+ return {
57
+ finalStatus: "blocked",
58
+ failureReason: lockResult.reason,
59
+ steps: [],
60
+ failures: [{ stepId: "lock", reason: lockResult.reason }],
61
+ verification: { status: "skipped", reason: "Another orchestration run is still active." },
62
+ diffSummary: "Run blocked by active orchestration lock.",
63
+ };
64
+ }
65
+
66
+ const steps = [];
67
+ const failures = [];
68
+ let finalStatus = dryRun ? "dry_run" : "completed";
69
+ let failureReason = null;
70
+ let verification = { status: "skipped", reason: "No verification stage ran." };
71
+
72
+ try {
73
+ for (const stage of route.stageList || []) {
74
+ if (stage.role === "verify") {
75
+ if (dryRun) {
76
+ const result = {
77
+ stepId: stage.id,
78
+ toolId: stage.chosenTool,
79
+ selectedModel: stage.chosenModel || null,
80
+ modelProfile: stage.chosenModelProfile,
81
+ executionMode: "dry_run",
82
+ status: "skipped",
83
+ reason: "dry_run",
84
+ };
85
+ steps.push(result);
86
+ if (executionLogPath) appendJsonl(executionLogPath, result);
87
+ continue;
88
+ }
89
+
90
+ verification = verifyWorkspace(cwd, taskContext, options);
91
+ const result = {
92
+ stepId: stage.id,
93
+ toolId: stage.chosenTool,
94
+ selectedModel: stage.chosenModel || null,
95
+ modelProfile: stage.chosenModelProfile,
96
+ executionMode: "direct",
97
+ status: verification.status,
98
+ reason: verification.reason,
99
+ };
100
+ steps.push(result);
101
+ if (executionLogPath) appendJsonl(executionLogPath, result);
102
+ if (verification.status === "failed") {
103
+ recordWorkerFailure(cwd, {
104
+ workerId: stage.chosenTool,
105
+ modelId: stage.chosenModel || null,
106
+ reason: verification.reason,
107
+ taskType: taskContext.classification.taskType,
108
+ risk: taskContext.classification.risk,
109
+ });
110
+ finalStatus = "failed";
111
+ failureReason = verification.reason;
112
+ failures.push({ stepId: stage.id, reason: verification.reason });
113
+ break;
114
+ }
115
+ continue;
116
+ }
117
+
118
+ if (stage.role === "handoff" || stage.chosenTool === "avorelo_handoff") {
119
+ const result = {
120
+ stepId: stage.id,
121
+ toolId: stage.chosenTool,
122
+ selectedModel: null,
123
+ modelProfile: stage.chosenModelProfile,
124
+ executionMode: "local_command",
125
+ status: dryRun ? "skipped" : "pending",
126
+ reason: dryRun ? "dry_run" : "handoff_prompt_not_auto_executed",
127
+ };
128
+ steps.push(result);
129
+ if (executionLogPath) appendJsonl(executionLogPath, result);
130
+ continue;
131
+ }
132
+
133
+ const adapter = adapters[stage.chosenTool];
134
+ if (!adapter) {
135
+ finalStatus = "failed";
136
+ failureReason = `missing_adapter:${stage.chosenTool}`;
137
+ failures.push({ stepId: stage.id, reason: failureReason });
138
+ break;
139
+ }
140
+
141
+ if (dryRun) {
142
+ const result = {
143
+ stepId: stage.id,
144
+ toolId: stage.chosenTool,
145
+ selectedModel: stage.chosenModel || null,
146
+ modelProfile: stage.chosenModelProfile,
147
+ executionMode: "dry_run",
148
+ status: "skipped",
149
+ reason: "dry_run",
150
+ };
151
+ steps.push(result);
152
+ if (executionLogPath) appendJsonl(executionLogPath, result);
153
+ continue;
154
+ }
155
+
156
+ const primary = adapter.execute(stage, taskContext, options) || {};
157
+ const primaryResult = {
158
+ stepId: stage.id,
159
+ toolId: stage.chosenTool,
160
+ selectedModel: stage.chosenModel || primary.selectedModel || null,
161
+ modelProfile: stage.chosenModelProfile,
162
+ executionMode: primary.executionMode || adapter.executionMode || "handoff",
163
+ status: primary.status || "completed",
164
+ summary: primary.summary || null,
165
+ touchedFiles: primary.touchedFiles || [],
166
+ handoffInstruction: primary.handoffInstruction || null,
167
+ };
168
+ steps.push(primaryResult);
169
+ if (executionLogPath) appendJsonl(executionLogPath, primaryResult);
170
+
171
+ if (primary.unrelatedFilesChanged || unrelatedFilesChanged(taskContext, primaryResult.touchedFiles)) {
172
+ finalStatus = "blocked";
173
+ failureReason = "unrelated_files_changed";
174
+ failures.push({ stepId: stage.id, reason: failureReason });
175
+ break;
176
+ }
177
+
178
+ if (highRiskTouchedFiles(taskContext, primaryResult.touchedFiles)) {
179
+ finalStatus = "blocked";
180
+ failureReason = "route_escalated_high_risk";
181
+ failures.push({ stepId: stage.id, reason: failureReason });
182
+ break;
183
+ }
184
+
185
+ if (primary.status === "failed") {
186
+ recordWorkerFailure(cwd, {
187
+ workerId: stage.chosenTool,
188
+ modelId: stage.chosenModel || primary.selectedModel || null,
189
+ reason: primary.summary || "worker_failed",
190
+ taskType: taskContext.classification.taskType,
191
+ risk: taskContext.classification.risk,
192
+ });
193
+ const fallbackAdapter = stage.fallbackTool ? adapters[stage.fallbackTool] : null;
194
+ if (fallbackAdapter && settings.safety.stopAfterRepeatedFailures) {
195
+ const fallback = fallbackAdapter.execute({
196
+ ...stage,
197
+ chosenTool: stage.fallbackTool,
198
+ chosenModel: stage.fallbackModel,
199
+ chosenModelProfile: stage.fallbackModelProfile,
200
+ }, taskContext, options) || {};
201
+ const fallbackResult = {
202
+ stepId: `${stage.id}:fallback`,
203
+ toolId: stage.fallbackTool,
204
+ selectedModel: stage.fallbackModel || fallback.selectedModel || null,
205
+ modelProfile: stage.fallbackModelProfile,
206
+ executionMode: fallback.executionMode || fallbackAdapter.executionMode || "handoff",
207
+ status: fallback.status || "completed",
208
+ summary: fallback.summary || null,
209
+ touchedFiles: fallback.touchedFiles || [],
210
+ handoffInstruction: fallback.handoffInstruction || null,
211
+ };
212
+ steps.push(fallbackResult);
213
+ if (executionLogPath) appendJsonl(executionLogPath, fallbackResult);
214
+ if (fallback.status === "failed") {
215
+ recordWorkerFailure(cwd, {
216
+ workerId: stage.fallbackTool,
217
+ modelId: stage.fallbackModel || fallback.selectedModel || null,
218
+ reason: fallback.summary || "fallback_failed",
219
+ taskType: taskContext.classification.taskType,
220
+ risk: taskContext.classification.risk,
221
+ });
222
+ finalStatus = "failed";
223
+ failureReason = fallback.summary || "fallback_failed";
224
+ failures.push({ stepId: stage.id, reason: failureReason });
225
+ break;
226
+ }
227
+ if (fallback.status === "handoff_required" || fallback.status === "delegated_to_current_surface") {
228
+ finalStatus = "partial";
229
+ }
230
+ continue;
231
+ }
232
+ finalStatus = "failed";
233
+ failureReason = primary.summary || "worker_failed";
234
+ failures.push({ stepId: stage.id, reason: failureReason });
235
+ break;
236
+ }
237
+
238
+ if (primary.status === "handoff_required" || primary.status === "delegated_to_current_surface") {
239
+ finalStatus = "partial";
240
+ }
241
+ }
242
+
243
+ if (finalStatus === "completed" && verification.status === "skipped") {
244
+ verification = verifyWorkspace(cwd, taskContext, options);
245
+ steps.push({
246
+ stepId: "verify:auto",
247
+ toolId: "test_runner",
248
+ selectedModel: null,
249
+ modelProfile: null,
250
+ executionMode: "direct",
251
+ status: verification.status,
252
+ reason: verification.reason,
253
+ });
254
+ if (verification.status === "failed") {
255
+ finalStatus = "failed";
256
+ failureReason = verification.reason;
257
+ } else if (verification.status === "insufficient") {
258
+ finalStatus = "partial";
259
+ } else {
260
+ finalStatus = "verified";
261
+ }
262
+ }
263
+ } finally {
264
+ releaseRunLock(cwd, lockResult.lock.runId);
265
+ }
266
+
267
+ return {
268
+ finalStatus,
269
+ failureReason,
270
+ steps,
271
+ failures,
272
+ verification,
273
+ diffSummary: summarizeGitDiff(cwd, options),
274
+ };
275
+ }
276
+
277
+ module.exports = {
278
+ executeRoute,
279
+ };
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+ const crypto = require("crypto");
5
+ const {
6
+ getCurrentPaths,
7
+ writeJson,
8
+ writeText,
9
+ relativeFromCwd,
10
+ } = require("./state");
11
+
12
+ const HANDOFF_SCHEMA_VERSION = 1;
13
+
14
+ const TARGET_LABELS = {
15
+ claude: "Claude Code",
16
+ codex: "Codex / OpenAI CLI",
17
+ "local-model": "Local model (LM Studio / Ollama)",
18
+ generic: "Generic AI session",
19
+ };
20
+
21
+ function redactHandoffValue(value) {
22
+ if (typeof value !== "string") return value;
23
+ return value
24
+ .replace(/(OPENROUTER_API_KEY|ANTHROPIC_AUTH_TOKEN|OPENAI_API_KEY|GEMINI_API_KEY|REQUESTY_API_KEY|LITELLM_API_KEY)=\S+/gi, "$1=[REDACTED]")
25
+ .replace(/sk-[A-Za-z0-9_-]+/g, "[REDACTED]")
26
+ .replace(/ghp_[A-Za-z0-9]+/g, "[REDACTED]")
27
+ .replace(/ghs_[A-Za-z0-9]+/g, "[REDACTED]");
28
+ }
29
+
30
+ function safeRedact(obj) {
31
+ if (typeof obj === "string") return redactHandoffValue(obj);
32
+ if (Array.isArray(obj)) return obj.map(safeRedact);
33
+ if (obj && typeof obj === "object") {
34
+ return Object.fromEntries(
35
+ Object.entries(obj).map(([k, v]) => [k, safeRedact(v)])
36
+ );
37
+ }
38
+ return obj;
39
+ }
40
+
41
+ function summarizeChangedFiles(changedFiles = []) {
42
+ if (!changedFiles.length) return [];
43
+ const limit = 20;
44
+ const shown = changedFiles.slice(0, limit).map((f) => {
45
+ const p = typeof f === "string" ? f : f.path || f.file || String(f);
46
+ return redactHandoffValue(p);
47
+ });
48
+ if (changedFiles.length > limit) shown.push(`...and ${changedFiles.length - limit} more`);
49
+ return shown;
50
+ }
51
+
52
+ function buildQuotaSignalSummary(inventory) {
53
+ const tools = Array.isArray(inventory?.tools) ? inventory.tools : [];
54
+ const limited = tools.filter((t) => t.workerState === "recently_limited").map((t) => t.label);
55
+ const needsLogin = tools.filter((t) => t.workerState === "needs_login").map((t) => t.label);
56
+ const local = tools.filter((t) => t.quotaSignal === "local" || t.costTier === "free_local").map((t) => t.label);
57
+ return { limited, needsLogin, local };
58
+ }
59
+
60
+ function buildStageSummary(route) {
61
+ const stages = Array.isArray(route?.stageList) ? route.stageList : [];
62
+ return stages.map((s) => ({
63
+ id: s.id,
64
+ role: s.role,
65
+ worker: s.chosenLabel || s.chosenTool || "unknown",
66
+ model: s.chosenModel || s.chosenModelProfile || "unknown",
67
+ executionMode: s.executionMode || "unknown",
68
+ fallback: s.fallbackTool || null,
69
+ }));
70
+ }
71
+
72
+ function generateHandoffId() {
73
+ return `handoff-${Date.now()}-${crypto.randomBytes(3).toString("hex")}`;
74
+ }
75
+
76
+ function buildHandoffPrompt(params) {
77
+ const {
78
+ target = "claude",
79
+ taskText = "",
80
+ route = null,
81
+ inventory = null,
82
+ changedFiles = [],
83
+ testsRun = [],
84
+ validationStatus = "unknown",
85
+ dogfoodStatus = "not_run",
86
+ whatWasDone = [],
87
+ doNotRedo = [],
88
+ remainingGaps = [],
89
+ exactNextAction = "",
90
+ safePathConstraints = [],
91
+ intakeWarnings = [],
92
+ selectedSkills = [],
93
+ nonGoals = [],
94
+ branchName = null,
95
+ prUrl = null,
96
+ lastCommit = null,
97
+ currentProof = null,
98
+ contextPackSummary = null,
99
+ } = params;
100
+
101
+ const handoffId = generateHandoffId();
102
+ const targetLabel = TARGET_LABELS[target] || target;
103
+ const quotaSignals = buildQuotaSignalSummary(inventory);
104
+ const stageSummary = buildStageSummary(route);
105
+ const recommendedWorkerForNext = TARGET_LABELS[target] || target;
106
+
107
+ const sections = [
108
+ `# Avorelo Handoff Prompt`,
109
+ ``,
110
+ `Handoff ID: ${handoffId}`,
111
+ `Target: ${targetLabel}`,
112
+ `Generated: ${new Date().toISOString()}`,
113
+ ``,
114
+ `## Mission`,
115
+ redactHandoffValue(taskText) || "Continue current work task.",
116
+ ``,
117
+ ];
118
+
119
+ if (branchName || prUrl || lastCommit) {
120
+ sections.push(`## Branch / PR State`);
121
+ if (branchName) sections.push(`Branch: ${branchName}`);
122
+ if (prUrl) sections.push(`PR: ${prUrl}`);
123
+ if (lastCommit) sections.push(`Last commit: ${lastCommit}`);
124
+ sections.push(``);
125
+ }
126
+
127
+ if (changedFiles.length) {
128
+ sections.push(`## Files Changed`);
129
+ summarizeChangedFiles(changedFiles).forEach((f) => sections.push(`- ${f}`));
130
+ sections.push(``);
131
+ }
132
+
133
+ if (testsRun.length) {
134
+ sections.push(`## Tests Run`);
135
+ testsRun.forEach((t) => sections.push(`- ${redactHandoffValue(String(t))}`));
136
+ sections.push(``);
137
+ }
138
+
139
+ sections.push(`## Validation Status`);
140
+ sections.push(redactHandoffValue(String(validationStatus)));
141
+ sections.push(``);
142
+
143
+ if (whatWasDone.length) {
144
+ sections.push(`## What Was Already Done`);
145
+ whatWasDone.forEach((item) => sections.push(`- ${redactHandoffValue(String(item))}`));
146
+ sections.push(``);
147
+ }
148
+
149
+ if (doNotRedo.length) {
150
+ sections.push(`## Do NOT Redo`);
151
+ doNotRedo.forEach((item) => sections.push(`- ${redactHandoffValue(String(item))}`));
152
+ sections.push(``);
153
+ }
154
+
155
+ if (remainingGaps.length) {
156
+ sections.push(`## Remaining Gaps`);
157
+ remainingGaps.forEach((item) => sections.push(`- ${redactHandoffValue(String(item))}`));
158
+ sections.push(``);
159
+ }
160
+
161
+ if (exactNextAction) {
162
+ sections.push(`## Exact Next Action`);
163
+ sections.push(redactHandoffValue(exactNextAction));
164
+ sections.push(``);
165
+ }
166
+
167
+ if (stageSummary.length) {
168
+ sections.push(`## Stage Plan (from Avorelo route)`);
169
+ stageSummary.forEach((s) => {
170
+ sections.push(`- ${s.id} (${s.role}): ${s.worker} / ${s.model} via ${s.executionMode}${s.fallback ? ` · fallback: ${s.fallback}` : ""}`);
171
+ });
172
+ sections.push(``);
173
+ }
174
+
175
+ if (safePathConstraints.length) {
176
+ sections.push(`## Safe Path Constraints`);
177
+ safePathConstraints.forEach((c) => sections.push(`- ${redactHandoffValue(String(c))}`));
178
+ sections.push(``);
179
+ }
180
+
181
+ if (intakeWarnings.length) {
182
+ sections.push(`## Intake Warnings`);
183
+ intakeWarnings.forEach((w) => sections.push(`- ${redactHandoffValue(String(w))}`));
184
+ sections.push(``);
185
+ }
186
+
187
+ if (quotaSignals.limited.length) {
188
+ sections.push(`## Worker Quota Signals`);
189
+ quotaSignals.limited.forEach((label) => sections.push(`- ${label}: RECENTLY_LIMITED — avoid for implementation`));
190
+ quotaSignals.needsLogin.forEach((label) => sections.push(`- ${label}: NEEDS_LOGIN — requires auth before use`));
191
+ if (quotaSignals.local.length) {
192
+ sections.push(`Local workers available: ${quotaSignals.local.join(", ")}`);
193
+ }
194
+ sections.push(``);
195
+ }
196
+
197
+ if (selectedSkills.length) {
198
+ sections.push(`## Selected Skills / Capabilities`);
199
+ selectedSkills.forEach((s) => sections.push(`- ${redactHandoffValue(String(s))}`));
200
+ sections.push(``);
201
+ }
202
+
203
+ if (contextPackSummary) {
204
+ sections.push(`## Context Pack`);
205
+ sections.push(`Path: ${contextPackSummary.contextPackPath || "unknown"}`);
206
+ sections.push(`Stage: ${contextPackSummary.stageId || "unknown"} · Budget: ${contextPackSummary.budgetClass || "unknown"} · ~${contextPackSummary.tokenEstimate || 0} tokens`);
207
+ if (contextPackSummary.budgetWarning && contextPackSummary.budgetWarning !== "within_budget") {
208
+ sections.push(`Warning: ${contextPackSummary.budgetWarning}`);
209
+ }
210
+ sections.push(`Included: ${contextPackSummary.includedCount || 0} items · Excluded: ${contextPackSummary.excludedCount || 0} items`);
211
+ if (contextPackSummary.nextAction) {
212
+ sections.push(`Next: ${redactHandoffValue(contextPackSummary.nextAction)}`);
213
+ }
214
+ sections.push(``);
215
+ }
216
+
217
+ sections.push(`## Recommended Worker for Next Stage`);
218
+ sections.push(recommendedWorkerForNext);
219
+ sections.push(``);
220
+
221
+ if (nonGoals.length) {
222
+ sections.push(`## Explicit Non-Goals (do not implement)`);
223
+ nonGoals.forEach((g) => sections.push(`- ${redactHandoffValue(String(g))}`));
224
+ sections.push(``);
225
+ }
226
+
227
+ sections.push(`---`);
228
+ sections.push(`*Compact handoff generated by Avorelo — not a generic gateway. Work-aware routing only.*`);
229
+ sections.push(``);
230
+
231
+ const promptText = sections.join("\n");
232
+
233
+ const receipt = {
234
+ schemaVersion: HANDOFF_SCHEMA_VERSION,
235
+ contract: "avorelo.handoff.v1",
236
+ handoffId,
237
+ generatedAt: new Date().toISOString(),
238
+ target,
239
+ targetLabel,
240
+ taskSummary: redactHandoffValue(taskText).slice(0, 200),
241
+ branchName: branchName || null,
242
+ prUrl: prUrl || null,
243
+ lastCommit: lastCommit || null,
244
+ changedFileCount: changedFiles.length,
245
+ testsRun: testsRun.map((t) => redactHandoffValue(String(t))),
246
+ validationStatus: redactHandoffValue(String(validationStatus)),
247
+ whatWasDone: whatWasDone.map((i) => redactHandoffValue(String(i))),
248
+ doNotRedo: doNotRedo.map((i) => redactHandoffValue(String(i))),
249
+ remainingGaps: remainingGaps.map((i) => redactHandoffValue(String(i))),
250
+ exactNextAction: redactHandoffValue(exactNextAction),
251
+ stageSummary,
252
+ quotaSignals,
253
+ safePathConstraints: safePathConstraints.map((c) => redactHandoffValue(String(c))),
254
+ intakeWarnings: intakeWarnings.map((w) => redactHandoffValue(String(w))),
255
+ selectedSkills: selectedSkills.map((s) => redactHandoffValue(String(s))),
256
+ nonGoals: nonGoals.map((g) => redactHandoffValue(String(g))),
257
+ recommendedWorkerForNext,
258
+ contextPackSummary: contextPackSummary || null,
259
+ redacted: true,
260
+ };
261
+
262
+ return { handoffId, promptText, receipt };
263
+ }
264
+
265
+ function writeHandoff(cwd, params) {
266
+ const result = buildHandoffPrompt(params);
267
+ const paths = getCurrentPaths(cwd);
268
+ const handoffsDir = path.join(paths.root, "handoffs");
269
+
270
+ const promptPath = path.join(handoffsDir, `${result.handoffId}.md`);
271
+ const receiptPath = path.join(handoffsDir, `${result.handoffId}.json`);
272
+ const currentPromptPath = path.join(handoffsDir, "current-handoff.md");
273
+ const currentReceiptPath = path.join(handoffsDir, "current-handoff.json");
274
+
275
+ writeText(promptPath, result.promptText);
276
+ writeJson(receiptPath, result.receipt);
277
+ writeText(currentPromptPath, result.promptText);
278
+ writeJson(currentReceiptPath, result.receipt);
279
+
280
+ return {
281
+ handoffId: result.handoffId,
282
+ promptPath: relativeFromCwd(cwd, promptPath),
283
+ receiptPath: relativeFromCwd(cwd, receiptPath),
284
+ currentPromptPath: relativeFromCwd(cwd, currentPromptPath),
285
+ currentReceiptPath: relativeFromCwd(cwd, currentReceiptPath),
286
+ promptText: result.promptText,
287
+ receipt: result.receipt,
288
+ };
289
+ }
290
+
291
+ function readCurrentHandoff(cwd) {
292
+ const paths = getCurrentPaths(cwd);
293
+ const handoffsDir = path.join(paths.root, "handoffs");
294
+ const currentReceiptPath = path.join(handoffsDir, "current-handoff.json");
295
+ const currentPromptPath = path.join(handoffsDir, "current-handoff.md");
296
+
297
+ let receipt = null;
298
+ let promptText = null;
299
+ try {
300
+ receipt = JSON.parse(require("fs").readFileSync(currentReceiptPath, "utf8"));
301
+ } catch {}
302
+ try {
303
+ promptText = require("fs").readFileSync(currentPromptPath, "utf8");
304
+ } catch {}
305
+
306
+ return { receipt, promptText };
307
+ }
308
+
309
+ module.exports = {
310
+ buildHandoffPrompt,
311
+ writeHandoff,
312
+ readCurrentHandoff,
313
+ HANDOFF_SCHEMA_VERSION,
314
+ };