agent-dealer 0.2.0 → 1.0.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 (276) hide show
  1. package/bundle/server/dist/adapters/agent-deck-bind.js +443 -0
  2. package/bundle/server/dist/adapters/agent-deck-bind.test.js +454 -0
  3. package/bundle/server/dist/adapters/agent-deck.js +38 -50
  4. package/bundle/server/dist/adapters/agent-deck.test.js +63 -0
  5. package/bundle/server/dist/adapters/agent-health.js +230 -33
  6. package/bundle/server/dist/adapters/agent-health.test.js +237 -0
  7. package/bundle/server/dist/adapters/authority-lifecycle.js +289 -0
  8. package/bundle/server/dist/adapters/codex-scoped-config.js +103 -0
  9. package/bundle/server/dist/adapters/execution-authority.js +103 -0
  10. package/bundle/server/dist/adapters/execution-authority.test.js +166 -0
  11. package/bundle/server/dist/adapters/external.js +0 -1
  12. package/bundle/server/dist/adapters/git-worktree.js +514 -0
  13. package/bundle/server/dist/adapters/git-worktree.test.js +502 -0
  14. package/bundle/server/dist/adapters/github.js +170 -0
  15. package/bundle/server/dist/adapters/github.test.js +205 -0
  16. package/bundle/server/dist/adapters/linear-inbox.js +199 -30
  17. package/bundle/server/dist/adapters/linear-inbox.test.js +35 -0
  18. package/bundle/server/dist/adapters/linear-sync.js +3 -101
  19. package/bundle/server/dist/adapters/managed-repo.js +168 -0
  20. package/bundle/server/dist/adapters/managed-repo.test.js +63 -0
  21. package/bundle/server/dist/adapters/outbound-delivery.js +83 -0
  22. package/bundle/server/dist/adapters/outbound-delivery.test.js +53 -0
  23. package/bundle/server/dist/adapters/reflect-authority.js +43 -0
  24. package/bundle/server/dist/cli-env.js +26 -0
  25. package/bundle/server/dist/cli-env.test.js +14 -1
  26. package/bundle/server/dist/coordinator/abort.integration.test.js +74 -0
  27. package/bundle/server/dist/coordinator/admission.js +330 -0
  28. package/bundle/server/dist/coordinator/admission.test.js +338 -0
  29. package/bundle/server/dist/coordinator/args.js +128 -0
  30. package/bundle/server/dist/coordinator/args.test.js +132 -0
  31. package/bundle/server/dist/coordinator/authority-recovery.integration.test.js +661 -0
  32. package/bundle/server/dist/coordinator/auto-merge.integration.test.js +363 -0
  33. package/bundle/server/dist/coordinator/auto-merge.js +286 -0
  34. package/bundle/server/dist/coordinator/auto-merge.timeout.test.js +14 -0
  35. package/bundle/server/dist/coordinator/branch-progress.js +81 -0
  36. package/bundle/server/dist/coordinator/clock-jump.js +98 -0
  37. package/bundle/server/dist/coordinator/commands.js +1088 -0
  38. package/bundle/server/dist/coordinator/commands.test.js +554 -0
  39. package/bundle/server/dist/coordinator/deck-outage-config.js +32 -0
  40. package/bundle/server/dist/coordinator/deck-outage.integration.test.js +200 -0
  41. package/bundle/server/dist/coordinator/dependencies.js +242 -0
  42. package/bundle/server/dist/coordinator/dependency-readiness.integration.test.js +535 -0
  43. package/bundle/server/dist/coordinator/developer-effect.js +959 -0
  44. package/bundle/server/dist/coordinator/developer-effect.test.js +1143 -0
  45. package/bundle/server/dist/coordinator/dispatcher.js +58 -0
  46. package/bundle/server/dist/coordinator/dispatcher.test.js +81 -0
  47. package/bundle/server/dist/coordinator/effect-registry.js +17 -0
  48. package/bundle/server/dist/coordinator/execution-env.integration.test.js +122 -0
  49. package/bundle/server/dist/coordinator/failure-reason.js +273 -0
  50. package/bundle/server/dist/coordinator/failure-reason.test.js +234 -0
  51. package/bundle/server/dist/coordinator/guidance.js +33 -0
  52. package/bundle/server/dist/coordinator/guidance.test.js +110 -0
  53. package/bundle/server/dist/coordinator/human-resolution.js +88 -0
  54. package/bundle/server/dist/coordinator/human-resolution.test.js +72 -0
  55. package/bundle/server/dist/coordinator/intent-forecast.js +13 -0
  56. package/bundle/server/dist/coordinator/intent-forecast.test.js +34 -0
  57. package/bundle/server/dist/coordinator/latest-failure.js +100 -0
  58. package/bundle/server/dist/coordinator/latest-failure.test.js +125 -0
  59. package/bundle/server/dist/coordinator/metrics.js +35 -0
  60. package/bundle/server/dist/coordinator/metrics.test.js +52 -0
  61. package/bundle/server/dist/coordinator/permissions.js +127 -0
  62. package/bundle/server/dist/coordinator/permissions.test.js +184 -0
  63. package/bundle/server/dist/coordinator/presumed-dead-budget.integration.test.js +146 -0
  64. package/bundle/server/dist/coordinator/process-liveness.js +191 -0
  65. package/bundle/server/dist/coordinator/profile-snapshot.js +20 -0
  66. package/bundle/server/dist/coordinator/profile-snapshot.test.js +118 -0
  67. package/bundle/server/dist/coordinator/projection.js +147 -0
  68. package/bundle/server/dist/coordinator/projection.test.js +121 -0
  69. package/bundle/server/dist/coordinator/prompts.js +164 -0
  70. package/bundle/server/dist/coordinator/prompts.test.js +177 -0
  71. package/bundle/server/dist/coordinator/queue-by-default.integration.test.js +271 -0
  72. package/bundle/server/dist/coordinator/reclaim-republish.integration.test.js +372 -0
  73. package/bundle/server/dist/coordinator/recovery.js +466 -0
  74. package/bundle/server/dist/coordinator/recovery.test.js +164 -0
  75. package/bundle/server/dist/coordinator/reflect-trigger.js +218 -0
  76. package/bundle/server/dist/coordinator/reflect-trigger.test.js +324 -0
  77. package/bundle/server/dist/coordinator/repair-cycle.integration.test.js +234 -0
  78. package/bundle/server/dist/coordinator/restart-recovery.integration.test.js +363 -0
  79. package/bundle/server/dist/coordinator/reviewer-effect.js +507 -0
  80. package/bundle/server/dist/coordinator/reviewer-effect.test.js +612 -0
  81. package/bundle/server/dist/coordinator/reviewer-result.js +38 -0
  82. package/bundle/server/dist/coordinator/reviewer-result.test.js +45 -0
  83. package/bundle/server/dist/coordinator/routing.js +160 -0
  84. package/bundle/server/dist/coordinator/routing.test.js +336 -0
  85. package/bundle/server/dist/coordinator/session-lifecycle.integration.test.js +127 -0
  86. package/bundle/server/dist/coordinator/session-lifecycle.js +280 -0
  87. package/bundle/server/dist/coordinator/session-lifecycle.test.js +121 -0
  88. package/bundle/server/dist/coordinator/session-progress.js +351 -0
  89. package/bundle/server/dist/coordinator/session-progress.test.js +190 -0
  90. package/bundle/server/dist/coordinator/session-timeouts.js +33 -0
  91. package/bundle/server/dist/coordinator/sleep-recovery.integration.test.js +233 -0
  92. package/bundle/server/dist/coordinator/spawn.js +49 -0
  93. package/bundle/server/dist/coordinator/sync-issue-base-branch.js +18 -0
  94. package/bundle/server/dist/coordinator/usage-cap-config.js +10 -0
  95. package/bundle/server/dist/coordinator/usage-cap-defer.js +189 -0
  96. package/bundle/server/dist/coordinator/usage-cap.integration.test.js +193 -0
  97. package/bundle/server/dist/coordinator/usage.js +40 -0
  98. package/bundle/server/dist/coordinator/usage.test.js +69 -0
  99. package/bundle/server/dist/coordinator/verification-receipt.js +333 -0
  100. package/bundle/server/dist/coordinator/verification-receipt.test.js +382 -0
  101. package/bundle/server/dist/coordinator/worker-loop.js +363 -0
  102. package/bundle/server/dist/coordinator/worker-loop.test.js +327 -0
  103. package/bundle/server/dist/coordinator/workflow-instances.test.js +45 -0
  104. package/bundle/server/dist/coordinator/worktree-owner-liveness.js +82 -0
  105. package/bundle/server/dist/coordinator/worktree-owner-liveness.test.js +199 -0
  106. package/bundle/server/dist/db/artifacts-issue-columns.test.js +18 -0
  107. package/bundle/server/dist/db/authority-attempts-stale-at-migration.test.js +65 -0
  108. package/bundle/server/dist/db/idempotency-index-migration.test.js +57 -0
  109. package/bundle/server/dist/db/index.js +359 -7
  110. package/bundle/server/dist/db/linear-inbox-unlock.test.js +67 -0
  111. package/bundle/server/dist/db/migrate-to-issues.js +824 -0
  112. package/bundle/server/dist/db/migrate-to-issues.test.js +842 -0
  113. package/bundle/server/dist/db/schema.sql +311 -1
  114. package/bundle/server/dist/db/schema.test.js +66 -0
  115. package/bundle/server/dist/db/worker-session-pid-migration.test.js +112 -0
  116. package/bundle/server/dist/dev-review-cli-happy-path.integration.test.js +338 -0
  117. package/bundle/server/dist/direct-start-liveness.integration.test.js +350 -0
  118. package/bundle/server/dist/index.js +58 -5
  119. package/bundle/server/dist/paths.js +40 -4
  120. package/bundle/server/dist/paths.test.js +42 -0
  121. package/bundle/server/dist/pid-marker-claim-child.js +18 -0
  122. package/bundle/server/dist/pid-marker-hold-arbiter-child.js +24 -0
  123. package/bundle/server/dist/pid-marker.js +141 -0
  124. package/bundle/server/dist/pid-marker.test.js +208 -0
  125. package/bundle/server/dist/queue/approve-deliver.js +128 -28
  126. package/bundle/server/dist/queue/reflect-schedule.js +25 -0
  127. package/bundle/server/dist/queue/result-qa.js +1 -1
  128. package/bundle/server/dist/queue/send-gate.test.js +172 -36
  129. package/bundle/server/dist/repository/agents.js +55 -18
  130. package/bundle/server/dist/repository/artifacts-for-issue.js +42 -0
  131. package/bundle/server/dist/repository/artifacts-for-issue.test.js +48 -0
  132. package/bundle/server/dist/repository/artifacts.js +47 -0
  133. package/bundle/server/dist/repository/artifacts.test.js +48 -0
  134. package/bundle/server/dist/repository/authority-attempts.js +189 -0
  135. package/bundle/server/dist/repository/findings.js +70 -0
  136. package/bundle/server/dist/repository/findings.test.js +68 -0
  137. package/bundle/server/dist/repository/human-actions.js +142 -0
  138. package/bundle/server/dist/repository/human-actions.test.js +123 -0
  139. package/bundle/server/dist/repository/intake-settings.js +5 -59
  140. package/bundle/server/dist/repository/issues.js +305 -0
  141. package/bundle/server/dist/repository/issues.test.js +69 -0
  142. package/bundle/server/dist/repository/outbound-drafts.js +28 -0
  143. package/bundle/server/dist/repository/queue-entries.js +162 -0
  144. package/bundle/server/dist/repository/review-publications.js +153 -0
  145. package/bundle/server/dist/repository/review-publications.test.js +78 -0
  146. package/bundle/server/dist/repository/runs-external-id.test.js +5 -3
  147. package/bundle/server/dist/repository/runs.js +4 -211
  148. package/bundle/server/dist/repository/runtime-availability.js +54 -0
  149. package/bundle/server/dist/repository/usage-events.js +60 -0
  150. package/bundle/server/dist/repository/usage-events.test.js +56 -0
  151. package/bundle/server/dist/repository/work-items.js +305 -0
  152. package/bundle/server/dist/repository/work-items.test.js +142 -0
  153. package/bundle/server/dist/repository/worker-sessions.js +219 -0
  154. package/bundle/server/dist/repository/worker-sessions.test.js +60 -0
  155. package/bundle/server/dist/repository/workflow-events.js +193 -0
  156. package/bundle/server/dist/repository/workflow-events.test.js +89 -0
  157. package/bundle/server/dist/resolve-tsx-bin.js +19 -0
  158. package/bundle/server/dist/routes/human-actions.js +68 -0
  159. package/bundle/server/dist/routes/human-actions.test.js +284 -0
  160. package/bundle/server/dist/routes/index.js +29 -496
  161. package/bundle/server/dist/routes/issues.js +240 -0
  162. package/bundle/server/dist/routes/issues.test.js +451 -0
  163. package/bundle/server/dist/routes/queue.js +41 -0
  164. package/bundle/server/dist/routes/queue.test.js +78 -0
  165. package/bundle/server/dist/runners/claude-args.js +18 -30
  166. package/bundle/server/dist/runners/claude.js +19 -163
  167. package/bundle/server/dist/runners/claude.test.js +15 -70
  168. package/bundle/server/dist/runners/codex-args.js +59 -0
  169. package/bundle/server/dist/runners/codex-args.test.js +72 -0
  170. package/bundle/server/dist/runners/codex-jsonl.js +150 -0
  171. package/bundle/server/dist/runners/codex-jsonl.test.js +36 -0
  172. package/bundle/server/dist/runners/codex-resume.test.js +19 -0
  173. package/bundle/server/dist/runners/codex.js +46 -0
  174. package/bundle/server/dist/runners/models-codex.test.js +14 -0
  175. package/bundle/server/dist/runners/models.js +53 -1
  176. package/bundle/server/dist/runners/persist.js +4 -1
  177. package/bundle/server/dist/runners/process-registry.js +11 -0
  178. package/bundle/server/dist/runners/prompts.js +13 -280
  179. package/bundle/server/dist/runners/prompts.test.js +22 -153
  180. package/bundle/server/dist/runners/qa.js +17 -1
  181. package/bundle/server/dist/runners/reflect.js +4 -16
  182. package/bundle/server/dist/runners/spawn-cli.js +197 -0
  183. package/bundle/server/dist/runners/spawn-cli.test.js +130 -0
  184. package/bundle/server/dist/runners/stream-json.js +0 -141
  185. package/bundle/server/dist/runners/usage-cap.js +242 -0
  186. package/bundle/server/dist/runners/usage-cap.test.js +97 -0
  187. package/bundle/server/dist/server-liveness.js +37 -0
  188. package/bundle/server/dist/server-liveness.test.js +82 -0
  189. package/bundle/server/dist/static-ui.test.js +44 -0
  190. package/bundle/server/package.json +4 -2
  191. package/bundle/server/static-ui/assets/index-BTtPGYpY.js +60 -0
  192. package/bundle/server/static-ui/assets/index-BxTx4b1d.css +1 -0
  193. package/bundle/server/static-ui/index.html +2 -2
  194. package/bundle/shared/dist/agents.d.ts +262 -133
  195. package/bundle/shared/dist/agents.js +85 -19
  196. package/bundle/shared/dist/cursor-auth-health.d.ts +12 -0
  197. package/bundle/shared/dist/cursor-auth-health.js +36 -0
  198. package/bundle/shared/dist/cursor-auth-health.test.d.ts +1 -0
  199. package/bundle/shared/dist/cursor-auth-health.test.js +40 -0
  200. package/bundle/shared/dist/findings.d.ts +46 -0
  201. package/bundle/shared/dist/findings.js +17 -0
  202. package/bundle/shared/dist/findings.test.d.ts +1 -0
  203. package/bundle/shared/dist/findings.test.js +20 -0
  204. package/bundle/shared/dist/github-repo.d.ts +28 -0
  205. package/bundle/shared/dist/github-repo.js +98 -0
  206. package/bundle/shared/dist/github-repo.test.d.ts +1 -0
  207. package/bundle/shared/dist/github-repo.test.js +32 -0
  208. package/bundle/shared/dist/human-actions.d.ts +66 -0
  209. package/bundle/shared/dist/human-actions.js +51 -0
  210. package/bundle/shared/dist/human-actions.test.d.ts +1 -0
  211. package/bundle/shared/dist/human-actions.test.js +57 -0
  212. package/bundle/shared/dist/index.d.ts +567 -757
  213. package/bundle/shared/dist/index.js +22 -36
  214. package/bundle/shared/dist/issues.d.ts +385 -0
  215. package/bundle/shared/dist/issues.js +169 -0
  216. package/bundle/shared/dist/issues.test.d.ts +1 -0
  217. package/bundle/shared/dist/issues.test.js +72 -0
  218. package/bundle/shared/dist/outbound-draft.d.ts +6 -0
  219. package/bundle/shared/dist/outbound-draft.js +4 -0
  220. package/bundle/shared/dist/plan-triage.d.ts +6 -6
  221. package/bundle/shared/dist/profile-snapshot.d.ts +154 -0
  222. package/bundle/shared/dist/profile-snapshot.js +145 -0
  223. package/bundle/shared/dist/profile-snapshot.test.d.ts +1 -0
  224. package/bundle/shared/dist/profile-snapshot.test.js +67 -0
  225. package/bundle/shared/dist/queue-entries.d.ts +89 -0
  226. package/bundle/shared/dist/queue-entries.js +19 -0
  227. package/bundle/shared/dist/result-qa.d.ts +2 -2
  228. package/bundle/shared/dist/runtime-auth-health.d.ts +66 -0
  229. package/bundle/shared/dist/runtime-auth-health.js +186 -0
  230. package/bundle/shared/dist/runtime-auth-health.test.d.ts +1 -0
  231. package/bundle/shared/dist/runtime-auth-health.test.js +200 -0
  232. package/bundle/shared/dist/runtime-availability.d.ts +51 -0
  233. package/bundle/shared/dist/runtime-availability.js +13 -0
  234. package/bundle/shared/dist/runtime.d.ts +1 -1
  235. package/bundle/shared/dist/runtime.js +1 -1
  236. package/bundle/shared/dist/usage-events.d.ts +36 -0
  237. package/bundle/shared/dist/usage-events.js +13 -0
  238. package/bundle/shared/dist/worker-sessions.d.ts +140 -0
  239. package/bundle/shared/dist/worker-sessions.js +70 -0
  240. package/bundle/shared/dist/worker-sessions.test.d.ts +1 -0
  241. package/bundle/shared/dist/worker-sessions.test.js +39 -0
  242. package/bundle/shared/dist/workflow.d.ts +78 -0
  243. package/bundle/shared/dist/workflow.js +59 -0
  244. package/bundle/shared/dist/workflow.test.d.ts +1 -0
  245. package/bundle/shared/dist/workflow.test.js +61 -0
  246. package/bundle/shared/package.json +1 -1
  247. package/dist/action.d.ts +1 -0
  248. package/dist/action.js +50 -0
  249. package/dist/action.test.d.ts +1 -0
  250. package/dist/action.test.js +70 -0
  251. package/dist/agent.d.ts +1 -0
  252. package/dist/agent.js +17 -0
  253. package/dist/agent.test.d.ts +1 -0
  254. package/dist/agent.test.js +19 -0
  255. package/dist/cli-check.d.ts +7 -0
  256. package/dist/cli-check.js +40 -0
  257. package/dist/doctor.js +59 -1
  258. package/dist/http.d.ts +5 -0
  259. package/dist/http.js +19 -0
  260. package/dist/index.js +28 -0
  261. package/dist/issue.d.ts +35 -0
  262. package/dist/issue.js +143 -0
  263. package/dist/issue.test.d.ts +1 -0
  264. package/dist/issue.test.js +240 -0
  265. package/dist/lifecycle.contract.test.d.ts +1 -0
  266. package/dist/lifecycle.contract.test.js +135 -0
  267. package/dist/queue.d.ts +11 -0
  268. package/dist/queue.js +60 -0
  269. package/dist/queue.test.d.ts +1 -0
  270. package/dist/queue.test.js +46 -0
  271. package/dist/test-fetch-stub.d.ts +6 -0
  272. package/dist/test-fetch-stub.js +31 -0
  273. package/package.json +2 -1
  274. package/templates/prod.env.example +1 -1
  275. package/bundle/server/static-ui/assets/index-B_7S7xJb.js +0 -79
  276. package/bundle/server/static-ui/assets/index-QYv3zmE6.css +0 -1
@@ -1,39 +1,17 @@
1
- import fs from "node:fs";
2
- import { ApproveRunInput, CreateRunInput, AgentConfigInput, AgentDeckConfigPatch, CreateAgentInput, UpdateAgentInput, DraftPlanInput, KickRunInput, PlanAnswersInput, ResultQaInput, LinearIntakeConfigPatch, PromoteLinearInput, RetryRunInput, Runtime, UpdatePlanInput, BUILTIN_AGENT_CLAUDE_ID, } from "@agent-dealer/shared";
3
- import { parseRunBudget } from "@agent-dealer/shared";
4
- import { addArtifact, createRun, findActiveByExternalId, getRun, listArtifacts, listEvents, listRuns, updateRunFields, transitionRun, getLatestArtifact, markPlanTriageConsumed, patchRunPhaseBudget, updateRunBudget, } from "../repository/runs.js";
5
- import { createAgent, deleteAgent, getAgent, listAgents, updateAgent, } from "../repository/agents.js";
1
+ import { CreateAgentInput, UpdateAgentInput, Runtime } from "@agent-dealer/shared";
2
+ import { createAgent, deleteAgent, listAgents, updateAgent, } from "../repository/agents.js";
6
3
  import { listAgentsWithHealth } from "../adapters/agent-health.js";
7
- import { forceDispatch, getSnapshotAsync, kickRun, cancelActiveRun, schedulePlanDraft, scheduleRedraft, scheduleReflect, submitPlanAnswers, subscribe, } from "../queue/dispatcher.js";
8
- import { approveRunWithDeliver } from "../queue/approve-deliver.js";
9
- import { recordPlanDelegation } from "../queue/plan-delegation.js";
10
- import { askResultQuestion } from "../queue/result-qa.js";
11
- import { rejectPendingOutboundDrafts, deliverInFlight } from "../repository/outbound-drafts.js";
12
- import { getActiveLogPath } from "../runners/claude.js";
13
- import { fetchAgentDeckDecks } from "../adapters/agent-deck.js";
4
+ import { fetchDecks } from "../adapters/agent-deck.js";
14
5
  import { testAgentDeckConnection } from "../adapters/agent-deck.js";
15
- import { getLinearIssue, listLinearCandidates, testLinearConnection, } from "../adapters/linear-inbox.js";
16
- import { syncLinearForRun } from "../adapters/linear-sync.js";
17
- import { buildLineageUsageSummary } from "../usage-summary.js";
18
- import { buildLineageTraceSummary } from "../trace-summary.js";
19
- import { getLinearIntakeConfig, getLinearIntakeConfigView, patchLinearIntakeConfig, getAgentDeckConfig, patchAgentDeckConfig, } from "../repository/intake-settings.js";
20
- import { resolveAgentForIssue } from "../intake/agent-routing.js";
6
+ import { listLinearCandidates, lookupLinearIssue, parseLinearIssueRef, } from "../adapters/linear-inbox.js";
21
7
  import { listRuntimeModels } from "../runners/models.js";
22
8
  async function resolveDeckName(deckId) {
23
9
  if (!deckId)
24
10
  return null;
25
- try {
26
- const decksRes = (await fetchAgentDeckDecks());
27
- return decksRes.data?.find((d) => d.id === deckId)?.name ?? null;
28
- }
29
- catch {
11
+ const result = await fetchDecks();
12
+ if (!result.ok)
30
13
  return null;
31
- }
32
- }
33
- function assertAgentConfigured(run) {
34
- if (!run.runtime) {
35
- throw new Error("Select an agent runtime before plan mode or execution");
36
- }
14
+ return result.decks.find((d) => d.id === deckId)?.name ?? null;
37
15
  }
38
16
  export async function registerRoutes(app) {
39
17
  app.get("/health", async () => ({ ok: true }));
@@ -46,60 +24,8 @@ export async function registerRoutes(app) {
46
24
  const { models, source } = await listRuntimeModels(parsed.data, { refresh });
47
25
  return { runtime: parsed.data, models, source };
48
26
  });
49
- app.get("/api/runs", async (req) => {
50
- const status = req.query.status;
51
- if (!status)
52
- return listRuns();
53
- const statuses = status.split(",");
54
- return listRuns(statuses);
55
- });
56
- app.get("/api/runs/:id", async (req, reply) => {
57
- const { id } = req.params;
58
- const run = getRun(id);
59
- if (!run)
60
- return reply.status(404).send({ error: "Not found" });
61
- const artifacts = listArtifacts(id);
62
- return {
63
- run,
64
- artifacts,
65
- events: listEvents(id),
66
- usageSummary: buildLineageUsageSummary(run, { artifactsByRunId: { [id]: artifacts } }),
67
- traceSummary: buildLineageTraceSummary(run),
68
- };
69
- });
70
- /** Tail NDJSON log — transcript or stream_trace artifact. */
71
- app.get("/api/runs/:id/log-tail", async (req, reply) => {
72
- const { id } = req.params;
73
- const run = getRun(id);
74
- if (!run)
75
- return reply.status(404).send({ error: "Not found" });
76
- const kind = req.query.kind ?? "transcript";
77
- const artifacts = listArtifacts(id);
78
- const artifact = artifacts.find((a) => a.kind === kind && a.blobPath) ??
79
- artifacts.find((a) => a.kind === "transcript" && a.blobPath) ??
80
- artifacts.find((a) => a.kind === "stream_trace" && a.blobPath);
81
- const livePath = getActiveLogPath(id);
82
- const blobPath = artifact?.blobPath ?? livePath;
83
- if (!blobPath)
84
- return reply.status(404).send({ error: "No log artifact" });
85
- if (!fs.existsSync(blobPath)) {
86
- return reply.status(404).send({ error: "Log file missing on disk" });
87
- }
88
- const max = Math.min(Number(req.query.max ?? 50000), 200000);
89
- const raw = fs.readFileSync(blobPath, "utf8");
90
- return { content: raw.slice(-max), path: blobPath, kind: artifact?.kind ?? kind, live: !!livePath };
91
- });
92
- app.get("/api/intake/linear/status", async () => testLinearConnection());
93
- app.get("/api/intake/linear/config", async () => getLinearIntakeConfigView());
94
- app.patch("/api/intake/linear/config", async (req, reply) => {
95
- try {
96
- const patch = LinearIntakeConfigPatch.parse(req.body);
97
- return patchLinearIntakeConfig(patch);
98
- }
99
- catch (e) {
100
- return reply.status(400).send({ error: String(e) });
101
- }
102
- });
27
+ // Linear lookup for the Issues home "New issue" flow — the only intake surface left
28
+ // after NOT-71 removed the Inbox page and its promote-to-run pipeline.
103
29
  app.get("/api/intake/linear", async (_req, reply) => {
104
30
  try {
105
31
  const candidates = await listLinearCandidates();
@@ -109,386 +35,22 @@ export async function registerRoutes(app) {
109
35
  return reply.status(502).send({ error: String(e), candidates: [] });
110
36
  }
111
37
  });
112
- app.post("/api/intake/linear/:issueId/resolve-agent", async (req, reply) => {
113
- const { issueId } = req.params;
114
- let issue;
115
- try {
116
- issue = await getLinearIssue(issueId);
117
- }
118
- catch (e) {
119
- return reply.status(502).send({ error: String(e) });
38
+ /** Free-form kick lookup: `?q=NOT-103` or a Linear issue URL. */
39
+ app.get("/api/intake/linear/lookup", async (req, reply) => {
40
+ const q = typeof req.query.q === "string" ? req.query.q : "";
41
+ if (!parseLinearIssueRef(q)) {
42
+ return reply.status(400).send({ error: "Provide a Linear identifier (e.g. NOT-103) or issue URL" });
120
43
  }
121
- if (!issue)
122
- return reply.status(404).send({ error: "Linear issue not found" });
123
44
  try {
124
- const settings = getLinearIntakeConfig();
125
- const resolved = await resolveAgentForIssue(issue, settings);
126
- return resolved;
127
- }
128
- catch (e) {
129
- return reply.status(400).send({ error: String(e) });
130
- }
131
- });
132
- app.post("/api/intake/linear/:issueId/promote", async (req, reply) => {
133
- const { issueId } = req.params;
134
- const input = PromoteLinearInput.parse(req.body);
135
- let issue;
136
- try {
137
- issue = await getLinearIssue(issueId);
45
+ const candidate = await lookupLinearIssue(q);
46
+ if (!candidate)
47
+ return reply.status(404).send({ error: "Linear issue not found" });
48
+ return { candidate };
138
49
  }
139
50
  catch (e) {
140
51
  return reply.status(502).send({ error: String(e) });
141
52
  }
142
- if (!issue)
143
- return reply.status(404).send({ error: "Linear issue not found" });
144
- const active = findActiveByExternalId("linear", issue.id);
145
- if (active) {
146
- return reply.status(409).send({
147
- error: "Active run already exists for this issue",
148
- runId: active.id,
149
- });
150
- }
151
- let agentId;
152
- try {
153
- const settings = getLinearIntakeConfig();
154
- const resolved = await resolveAgentForIssue(issue, settings, input.agentId);
155
- agentId = resolved.agentId;
156
- }
157
- catch (e) {
158
- return reply.status(400).send({ error: String(e) });
159
- }
160
- if (!getAgent(agentId)) {
161
- return reply.status(400).send({ error: "Agent not found" });
162
- }
163
- let run;
164
- try {
165
- run = createRun({
166
- title: `${issue.identifier}: ${issue.title}`,
167
- description: issue.description,
168
- taskCategory: "code",
169
- status: "plan_pending",
170
- agentId,
171
- planModel: input.planModel ?? undefined,
172
- }, { source: "linear", externalId: issue.id, externalLabel: issue.identifier });
173
- }
174
- catch (e) {
175
- return reply.status(400).send({ error: String(e) });
176
- }
177
- addArtifact(run.id, "task_snapshot", {
178
- title: issue.title,
179
- description: issue.description,
180
- source: "linear",
181
- externalId: issue.identifier,
182
- url: issue.url,
183
- agent: {
184
- agentId: run.agentId,
185
- agentName: run.agentName,
186
- runtime: run.runtime,
187
- deckId: run.deckId,
188
- deckName: run.deckName,
189
- playbookId: run.playbookId,
190
- },
191
- }, "system");
192
- schedulePlanDraft(run);
193
- return reply.status(201).send(run);
194
53
  });
195
- app.post("/api/runs", async (req, reply) => {
196
- const input = CreateRunInput.parse(req.body);
197
- if (!getAgent(input.agentId)) {
198
- return reply.status(400).send({ error: "Agent not found" });
199
- }
200
- let run;
201
- try {
202
- run = createRun(input);
203
- }
204
- catch (e) {
205
- return reply.status(400).send({ error: String(e) });
206
- }
207
- addArtifact(run.id, "task_snapshot", {
208
- title: run.title,
209
- description: run.description,
210
- source: run.source,
211
- externalId: run.externalId,
212
- agent: {
213
- agentId: run.agentId,
214
- agentName: run.agentName,
215
- runtime: run.runtime,
216
- deckId: run.deckId,
217
- deckName: run.deckName,
218
- playbookId: run.playbookId,
219
- },
220
- }, "system");
221
- if (input.acceptanceCriteria) {
222
- addArtifact(run.id, "acceptance_criteria", { markdown: input.acceptanceCriteria }, "human");
223
- }
224
- schedulePlanDraft(run);
225
- return reply.status(201).send(run);
226
- });
227
- app.patch("/api/runs/:id/agent", async (req, reply) => {
228
- const { id } = req.params;
229
- const run = getRun(id);
230
- if (!run)
231
- return reply.status(404).send({ error: "Not found" });
232
- if (!["plan_pending", "queued"].includes(run.status)) {
233
- return reply.status(400).send({ error: "Agent config locked after plan approval" });
234
- }
235
- const input = AgentConfigInput.parse(req.body);
236
- const deckName = await resolveDeckName(input.deckId);
237
- const updated = updateRunFields(id, {
238
- runtime: input.runtime,
239
- deck_id: input.deckId ?? null,
240
- deck_name: deckName,
241
- playbook_id: input.playbookId ?? null,
242
- plan_model: input.planModel !== undefined ? input.planModel : undefined,
243
- execute_model: input.executeModel !== undefined ? input.executeModel : undefined,
244
- });
245
- if (input.budget !== undefined) {
246
- return updateRunBudget(id, input.budget);
247
- }
248
- return updated;
249
- });
250
- app.post("/api/runs/:id/draft-plan", async (req, reply) => {
251
- const { id } = req.params;
252
- const run = getRun(id);
253
- if (!run)
254
- return reply.status(404).send({ error: "Not found" });
255
- if (!["plan_pending", "queued"].includes(run.status)) {
256
- return reply.status(400).send({ error: "Re-draft only in plan_pending" });
257
- }
258
- const input = DraftPlanInput.parse(req.body ?? {});
259
- if (input.planModel !== undefined) {
260
- updateRunFields(id, { plan_model: input.planModel });
261
- }
262
- if (input.planBudget !== undefined) {
263
- patchRunPhaseBudget(id, "plan", input.planBudget);
264
- }
265
- try {
266
- assertAgentConfigured(getRun(id));
267
- const scheduled = scheduleRedraft(getRun(id), {
268
- feedback: input.feedback,
269
- editedMarkdown: input.editedMarkdown,
270
- });
271
- if (!scheduled) {
272
- return reply.status(409).send({ error: "Plan draft already in progress" });
273
- }
274
- return reply.status(202).send(getRun(id));
275
- }
276
- catch (e) {
277
- return reply.status(400).send({ error: String(e) });
278
- }
279
- });
280
- app.patch("/api/runs/:id/plan", async (req, reply) => {
281
- const { id } = req.params;
282
- const run = getRun(id);
283
- if (!run)
284
- return reply.status(404).send({ error: "Not found" });
285
- const input = UpdatePlanInput.parse(req.body);
286
- if (input.approve) {
287
- const hasPlan = listArtifacts(id).some((a) => a.kind === "draft_plan") ||
288
- input.planMarkdown.trim().length > 0;
289
- if (!hasPlan) {
290
- return reply.status(400).send({ error: "Plan not ready — wait for the agent to finish planning" });
291
- }
292
- assertAgentConfigured(run);
293
- const patch = {};
294
- if (input.planModel !== undefined) {
295
- patch.plan_model = input.planModel;
296
- }
297
- // null/empty = "Default" — do not wipe a seeded execute_model; only set explicit picks
298
- if (input.executeModel?.trim()) {
299
- patch.execute_model = input.executeModel.trim();
300
- }
301
- else if (input.planModel?.trim()) {
302
- patch.execute_model = input.planModel.trim();
303
- }
304
- if (Object.keys(patch).length > 0) {
305
- updateRunFields(id, patch);
306
- }
307
- if (input.planBudget !== undefined) {
308
- patchRunPhaseBudget(id, "plan", input.planBudget);
309
- }
310
- if (input.executeBudget !== undefined) {
311
- patchRunPhaseBudget(id, "execute", input.executeBudget);
312
- }
313
- recordPlanDelegation(id);
314
- markPlanTriageConsumed(id);
315
- try {
316
- transitionRun(id, "plan_approved");
317
- }
318
- catch (e) {
319
- return reply.status(409).send({ error: String(e) });
320
- }
321
- addArtifact(id, "approved_plan", { markdown: input.planMarkdown }, "human");
322
- const updated = getRun(id);
323
- syncLinearForRun(updated, "plan_approved").catch((e) => console.error("[linear-sync] plan_approved:", e));
324
- await forceDispatch();
325
- return updated;
326
- }
327
- addArtifact(id, "draft_plan", { markdown: input.planMarkdown }, "human");
328
- markPlanTriageConsumed(id);
329
- if (run.status === "queued") {
330
- return transitionRun(id, "plan_pending");
331
- }
332
- return getRun(id);
333
- });
334
- app.post("/api/runs/:id/plan/answers", async (req, reply) => {
335
- const { id } = req.params;
336
- let input;
337
- try {
338
- input = PlanAnswersInput.parse(req.body);
339
- }
340
- catch (e) {
341
- return reply.status(400).send({ error: String(e) });
342
- }
343
- const result = submitPlanAnswers(id, input);
344
- if (!result.ok)
345
- return reply.status(result.code).send({ error: result.error });
346
- return { run: result.run, outcome: result.outcome };
347
- });
348
- app.post("/api/runs/:id/qa", async (req, reply) => {
349
- const { id } = req.params;
350
- let input;
351
- try {
352
- input = ResultQaInput.parse(req.body);
353
- }
354
- catch (e) {
355
- return reply.status(400).send({ error: String(e) });
356
- }
357
- const result = askResultQuestion(id, input.question);
358
- if (!result.ok)
359
- return reply.status(result.code).send({ error: result.error });
360
- return reply.status(202).send({ exchange: result.exchange });
361
- });
362
- app.post("/api/runs/:id/kick", async (req, reply) => {
363
- const { id } = req.params;
364
- const run = getRun(id);
365
- if (!run)
366
- return reply.status(404).send({ error: "Not found" });
367
- const input = KickRunInput.parse(req.body ?? {});
368
- const runtime = input.runtime ?? run.runtime;
369
- if (!runtime) {
370
- return reply.status(400).send({ error: "Select an agent runtime before execution" });
371
- }
372
- const deckId = input.deckId ?? run.deckId ?? undefined;
373
- const playbookId = input.playbookId ?? run.playbookId ?? undefined;
374
- const deckName = deckId ? await resolveDeckName(deckId) : run.deckName;
375
- const patch = {
376
- deck_id: deckId ?? null,
377
- deck_name: deckName ?? null,
378
- playbook_id: playbookId ?? null,
379
- runtime,
380
- };
381
- if (input.executeModel?.trim()) {
382
- patch.execute_model = input.executeModel.trim();
383
- }
384
- if (run.status === "plan_pending") {
385
- return reply.status(400).send({ error: "Approve the plan before starting execution" });
386
- }
387
- if (run.status !== "plan_approved") {
388
- return reply.status(400).send({ error: `Cannot kick from status ${run.status}` });
389
- }
390
- updateRunFields(id, patch);
391
- if (input.executeBudget !== undefined) {
392
- patchRunPhaseBudget(id, "execute", input.executeBudget);
393
- }
394
- const updated = getRun(id);
395
- await kickRun(updated);
396
- await forceDispatch();
397
- return getRun(id);
398
- });
399
- app.post("/api/runs/:id/approve", async (req, reply) => {
400
- const { id } = req.params;
401
- const input = ApproveRunInput.parse(req.body ?? {});
402
- const result = await approveRunWithDeliver(id, {
403
- outboundBody: input.outboundBody,
404
- });
405
- if (!result.ok) {
406
- return reply.status(result.code).send({ error: result.error });
407
- }
408
- return result.run;
409
- });
410
- app.post("/api/runs/:id/retry", async (req, reply) => {
411
- const { id } = req.params;
412
- const run = getRun(id);
413
- if (!run)
414
- return reply.status(404).send({ error: "Not found" });
415
- if (run.status !== "review" && run.status !== "failed") {
416
- return reply.status(400).send({ error: "Retry only from review or failed" });
417
- }
418
- if (deliverInFlight.has(id)) {
419
- return reply.status(409).send({ error: "Deliver in progress — wait before retrying" });
420
- }
421
- const approvedPlan = getLatestArtifact(id, "approved_plan");
422
- if (!approvedPlan?.contentJson) {
423
- return reply.status(400).send({ error: "No approved plan — cannot retry execution" });
424
- }
425
- const input = RetryRunInput.parse(req.body);
426
- addArtifact(id, "feedback", { markdown: input.feedback }, "human");
427
- rejectPendingOutboundDrafts(id);
428
- const retry = createRun({
429
- title: run.title,
430
- description: run.description ?? undefined,
431
- taskCategory: run.taskCategory,
432
- repo: run.repo ?? undefined,
433
- artifactWorkspace: run.artifactWorkspace ?? undefined,
434
- acceptanceCriteria: run.acceptanceCriteria ?? undefined,
435
- status: "plan_approved",
436
- agentId: run.agentId ?? BUILTIN_AGENT_CLAUDE_ID,
437
- planModel: run.planModel ?? undefined,
438
- budget: parseRunBudget(run.budgetJson),
439
- }, { source: run.source, externalId: run.externalId ?? undefined, externalLabel: run.externalLabel ?? undefined, lineageId: run.lineageId ?? run.id });
440
- updateRunFields(retry.id, {
441
- deck_id: run.deckId,
442
- deck_name: run.deckName,
443
- playbook_id: run.playbookId,
444
- runtime: run.runtime,
445
- execute_model: input.executeModel ?? input.planModel ?? run.executeModel,
446
- });
447
- if (input.executeBudget !== undefined) {
448
- patchRunPhaseBudget(retry.id, "execute", input.executeBudget);
449
- }
450
- try {
451
- addArtifact(retry.id, "approved_plan", JSON.parse(approvedPlan.contentJson), approvedPlan.author);
452
- }
453
- catch {
454
- return reply.status(500).send({ error: "Failed to copy approved plan" });
455
- }
456
- const parentSnap = getLatestArtifact(id, "task_snapshot");
457
- if (parentSnap?.contentJson) {
458
- try {
459
- addArtifact(retry.id, "task_snapshot", JSON.parse(parentSnap.contentJson), "system");
460
- }
461
- catch {
462
- /* keep retry run usable without snapshot */
463
- }
464
- }
465
- addArtifact(retry.id, "feedback", { markdown: input.feedback }, "human");
466
- const parentDoc = getLatestArtifact(id, "document");
467
- if (parentDoc?.contentJson) {
468
- try {
469
- addArtifact(retry.id, "document", JSON.parse(parentDoc.contentJson), "system");
470
- }
471
- catch {
472
- /* deliverable will be seeded at execution if needed */
473
- }
474
- }
475
- const retryRun = getRun(retry.id);
476
- addArtifact(id, "feedback", { supersededBy: retry.id, note: "Retry — replaced by new run" }, "system");
477
- transitionRun(id, "cancelled");
478
- syncLinearForRun(retryRun, "retry").catch((e) => console.error("[linear-sync] retry:", e));
479
- scheduleReflect(run, { trigger: "retry", feedback: input.feedback });
480
- await forceDispatch();
481
- return reply.status(201).send(retryRun);
482
- });
483
- app.post("/api/runs/:id/cancel", async (req, reply) => {
484
- const { id } = req.params;
485
- const run = getRun(id);
486
- if (!run)
487
- return reply.status(404).send({ error: "Not found" });
488
- cancelActiveRun(id);
489
- return transitionRun(id, "cancelled");
490
- });
491
- app.get("/api/snapshot", async () => getSnapshotAsync());
492
54
  app.get("/api/agents", async () => {
493
55
  const agents = await listAgentsWithHealth(listAgents());
494
56
  return {
@@ -521,61 +83,32 @@ export async function registerRoutes(app) {
521
83
  return { ok: true };
522
84
  });
523
85
  app.get("/api/agent-deck/status", async () => testAgentDeckConnection());
524
- app.get("/api/agent-deck/config", async () => getAgentDeckConfig());
525
- app.patch("/api/agent-deck/config", async (req, reply) => {
526
- try {
527
- const patch = AgentDeckConfigPatch.parse(req.body);
528
- const config = patchAgentDeckConfig(patch);
529
- return config;
530
- }
531
- catch (e) {
532
- return reply.status(400).send({ error: String(e) });
533
- }
534
- });
535
86
  app.get("/api/agent-deck/decks", async (_req, reply) => {
536
- try {
537
- return await fetchAgentDeckDecks();
538
- }
539
- catch (e) {
540
- return reply.status(502).send({ error: String(e), data: [] });
87
+ const result = await fetchDecks();
88
+ if (!result.ok) {
89
+ const status = result.code === "DECK_UNAVAILABLE" ? 502 : 403;
90
+ return reply.status(status).send({ error: result.message, code: result.code, data: [] });
541
91
  }
92
+ return { data: result.decks };
542
93
  });
543
94
  app.get("/api/agent-deck/decks/:deckId/playbooks", async (req, reply) => {
544
95
  const { deckId } = req.params;
545
96
  const { getAgentDeckApiUrl } = await import("../adapters/agent-deck.js");
546
97
  const base = getAgentDeckApiUrl();
547
98
  try {
548
- const res = await fetch(`${base}/api/decks/${deckId}/playbooks`, {
99
+ const res = await fetch(`${base}/api/launch/decks/${encodeURIComponent(deckId)}/playbooks`, {
549
100
  signal: AbortSignal.timeout(5000),
550
101
  });
551
102
  if (!res.ok)
552
103
  throw new Error(`Agent Deck API error: ${res.status}`);
553
- return res.json();
104
+ const json = (await res.json());
105
+ if (json.success === false)
106
+ throw new Error(json.message ?? "Agent Deck playbooks request failed");
107
+ const playbooks = Array.isArray(json.data) ? json.data : (json.data?.playbooks ?? []);
108
+ return { data: playbooks };
554
109
  }
555
110
  catch (e) {
556
111
  return reply.status(502).send({ error: String(e), data: [] });
557
112
  }
558
113
  });
559
- app.get("/api/events", async (req, reply) => {
560
- reply.raw.writeHead(200, {
561
- "Content-Type": "text/event-stream",
562
- "Cache-Control": "no-cache",
563
- Connection: "keep-alive",
564
- });
565
- const send = async () => {
566
- const snap = await getSnapshotAsync();
567
- reply.raw.write(`data: ${JSON.stringify(snap)}\n\n`);
568
- };
569
- await send();
570
- const unsub = subscribe(() => {
571
- send().catch(console.error);
572
- });
573
- const heartbeat = setInterval(() => {
574
- reply.raw.write(": heartbeat\n\n");
575
- }, 15000);
576
- req.raw.on("close", () => {
577
- unsub();
578
- clearInterval(heartbeat);
579
- });
580
- });
581
114
  }