@midhunxavier/code-flow 0.0.0-stage → 0.1.0-preview.1

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 (284) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +122 -2
  3. package/bin/codeflow.mjs +8 -0
  4. package/bin/launcher.mjs +127 -0
  5. package/dist/remote-host.json +1 -0
  6. package/licenses/inter.txt +92 -0
  7. package/licenses/preact.txt +21 -0
  8. package/licenses/signals-core.txt +21 -0
  9. package/licenses/signals.txt +21 -0
  10. package/licenses/typescript-notices.txt +193 -0
  11. package/licenses/typescript.txt +55 -0
  12. package/licenses/uqr.txt +22 -0
  13. package/licenses/xterm-fit.txt +19 -0
  14. package/licenses/xterm.txt +21 -0
  15. package/package.json +34 -4
  16. package/public/assets/browser-7XI52A6V.js +8 -0
  17. package/public/assets/changes-EYFPASEI.js +1 -0
  18. package/public/assets/chunk-7H3ZLTKA.js +2 -0
  19. package/public/assets/chunk-L7TUCIYF.js +1 -0
  20. package/public/assets/chunk-LLMQG23J.js +163 -0
  21. package/public/assets/main-KIRM2OEK.js +1 -0
  22. package/public/assets/settings-QJZVW22Y.js +9 -0
  23. package/public/assets/styles-615EBB20.css +32 -0
  24. package/public/assets/terminal-A2FI2NAX.js +62 -0
  25. package/public/assets.json +1 -0
  26. package/public/bundle-meta.json +1 -0
  27. package/public/docs/.nojekyll +0 -0
  28. package/public/docs/accessibility.html +19 -0
  29. package/public/docs/acp-agents.html +117 -0
  30. package/public/docs/agent-containers.html +79 -0
  31. package/public/docs/api.html +1130 -0
  32. package/public/docs/app.js +342 -0
  33. package/public/docs/application-improvements.html +275 -0
  34. package/public/docs/architecture-explorer.html +19 -0
  35. package/public/docs/architecture.html +149 -0
  36. package/public/docs/auto-team.html +142 -0
  37. package/public/docs/automations.html +109 -0
  38. package/public/docs/checkpoint-review.html +37 -0
  39. package/public/docs/code-hosts.html +35 -0
  40. package/public/docs/code-intelligence.html +35 -0
  41. package/public/docs/configuration.html +346 -0
  42. package/public/docs/context-inspection.html +23 -0
  43. package/public/docs/coordinated-changes.html +28 -0
  44. package/public/docs/deferred-runs.html +25 -0
  45. package/public/docs/dependency-discovery.html +19 -0
  46. package/public/docs/documentation.html +84 -0
  47. package/public/docs/dynamic-teams-evaluation.html +174 -0
  48. package/public/docs/dynamic-teams-implementation-plan.html +882 -0
  49. package/public/docs/dynamic-teams-implementation-review-1.html +141 -0
  50. package/public/docs/dynamic-teams-plan-review-0.html +204 -0
  51. package/public/docs/extensions.html +42 -0
  52. package/public/docs/external-sources.html +96 -0
  53. package/public/docs/filesystem-policy.html +26 -0
  54. package/public/docs/filtered-tasks.html +44 -0
  55. package/public/docs/font-license.txt +92 -0
  56. package/public/docs/getting-started.html +118 -0
  57. package/public/docs/github-review-threads.html +26 -0
  58. package/public/docs/github.html +157 -0
  59. package/public/docs/help.html +69 -0
  60. package/public/docs/index.html +77 -0
  61. package/public/docs/installation.html +74 -0
  62. package/public/docs/launch-profiles.html +48 -0
  63. package/public/docs/maintenance.html +60 -0
  64. package/public/docs/memory-claims.html +31 -0
  65. package/public/docs/memory-storage.html +58 -0
  66. package/public/docs/notifications.html +39 -0
  67. package/public/docs/patterns.html +195 -0
  68. package/public/docs/performance-results.html +210 -0
  69. package/public/docs/performance.html +166 -0
  70. package/public/docs/project-conventions.html +22 -0
  71. package/public/docs/project-first-navigation-plan.html +587 -0
  72. package/public/docs/project-teams-implementation-plan.html +772 -0
  73. package/public/docs/project-teams.html +99 -0
  74. package/public/docs/provider-compatibility.html +20 -0
  75. package/public/docs/releasing.html +61 -0
  76. package/public/docs/remote-workers.html +64 -0
  77. package/public/docs/review-inbox.html +26 -0
  78. package/public/docs/run-storage.html +23 -0
  79. package/public/docs/runs.html +141 -0
  80. package/public/docs/search.json +1 -0
  81. package/public/docs/security.html +133 -0
  82. package/public/docs/shared-projects.html +31 -0
  83. package/public/docs/ssh-projects.html +60 -0
  84. package/public/docs/style.css +768 -0
  85. package/public/docs/task-reruns.html +30 -0
  86. package/public/docs/team-collaboration.html +379 -0
  87. package/public/docs/verification.html +22 -0
  88. package/public/docs/vscode.html +53 -0
  89. package/public/docs/worker-permission-verification.html +122 -0
  90. package/public/index.html +17 -0
  91. package/scripts/check-node.mjs +7 -0
  92. package/scripts/codeflow.mjs +55 -0
  93. package/scripts/pty.py +273 -0
  94. package/src/acp-connections.js +110 -0
  95. package/src/adapters/acp-presets.js +37 -0
  96. package/src/adapters/acp-types.js +0 -0
  97. package/src/adapters/acp.js +328 -0
  98. package/src/adapters/agy-auth.js +67 -0
  99. package/src/adapters/agy-isolation.js +223 -0
  100. package/src/adapters/agy.js +297 -0
  101. package/src/adapters/claude.js +220 -0
  102. package/src/adapters/codex-app-server.js +518 -0
  103. package/src/adapters/codex.js +299 -0
  104. package/src/adapters/errors.js +57 -0
  105. package/src/adapters/index.js +91 -0
  106. package/src/adapters/managed-permissions.js +37 -0
  107. package/src/adapters/model-discovery.js +100 -0
  108. package/src/adapters/process.js +418 -0
  109. package/src/adapters/team-tools.js +102 -0
  110. package/src/adapters/types.js +0 -0
  111. package/src/agent-container.js +155 -0
  112. package/src/approvals.js +121 -0
  113. package/src/architecture.js +456 -0
  114. package/src/assets.js +44 -0
  115. package/src/automation.js +901 -0
  116. package/src/certificate-renewal.js +75 -0
  117. package/src/chat-handoff.js +55 -0
  118. package/src/checkpoint-review.js +72 -0
  119. package/src/checks.js +303 -0
  120. package/src/code-host-git.js +102 -0
  121. package/src/code-hosts-api.js +99 -0
  122. package/src/code-hosts.js +482 -0
  123. package/src/code-intelligence-worker.mjs +452 -0
  124. package/src/code-intelligence.js +141 -0
  125. package/src/collab/api.js +365 -0
  126. package/src/collab/checkpoints.js +198 -0
  127. package/src/collab/connections.js +139 -0
  128. package/src/collab/host-execution.js +347 -0
  129. package/src/collab/hub-client.js +210 -0
  130. package/src/collab/link-api.js +229 -0
  131. package/src/collab/link.js +595 -0
  132. package/src/collab/model.js +496 -0
  133. package/src/collab/runner.js +1014 -0
  134. package/src/collab/store.js +1452 -0
  135. package/src/collab/terminals.js +361 -0
  136. package/src/collaboration-reviews.js +116 -0
  137. package/src/config.js +48 -0
  138. package/src/context-budget.js +14 -0
  139. package/src/context-inspection.js +204 -0
  140. package/src/context.js +154 -0
  141. package/src/conventions.js +283 -0
  142. package/src/coordinated-api.js +247 -0
  143. package/src/deferred-launch.js +82 -0
  144. package/src/deferred-runs.js +515 -0
  145. package/src/dependency-health.js +35 -0
  146. package/src/diff.js +43 -0
  147. package/src/docs-help.js +316 -0
  148. package/src/doctor.js +232 -0
  149. package/src/engine.js +4366 -0
  150. package/src/env.js +44 -0
  151. package/src/experience.js +50 -0
  152. package/src/extensions.js +144 -0
  153. package/src/filesystem-access.js +107 -0
  154. package/src/filesystem-policy.js +238 -0
  155. package/src/filtered-task-limits.js +93 -0
  156. package/src/filtered-tasks-api.js +53 -0
  157. package/src/filtered-tasks.js +576 -0
  158. package/src/fs.js +57 -0
  159. package/src/git.js +626 -0
  160. package/src/github-review-threads.js +181 -0
  161. package/src/github.js +538 -0
  162. package/src/http.js +69 -0
  163. package/src/improve-settings.js +38 -0
  164. package/src/improve.js +187 -0
  165. package/src/integrations.js +373 -0
  166. package/src/integrity.js +259 -0
  167. package/src/journey-api.js +77 -0
  168. package/src/journey-replay.js +65 -0
  169. package/src/journey-store.js +97 -0
  170. package/src/journeys.js +60 -0
  171. package/src/json.js +81 -0
  172. package/src/launch-profiles.js +292 -0
  173. package/src/learnings.js +80 -0
  174. package/src/log.js +39 -0
  175. package/src/maintenance.js +335 -0
  176. package/src/memory-analysis.js +182 -0
  177. package/src/memory-claims.js +153 -0
  178. package/src/memory-sqlite.js +236 -0
  179. package/src/memory.js +552 -0
  180. package/src/merge-validation.js +58 -0
  181. package/src/model-catalog.js +238 -0
  182. package/src/model-research.js +91 -0
  183. package/src/notes.js +176 -0
  184. package/src/notifications.js +413 -0
  185. package/src/patterns/auto-team-model.js +212 -0
  186. package/src/patterns/auto-team.js +659 -0
  187. package/src/patterns/council.js +199 -0
  188. package/src/patterns/debate.js +188 -0
  189. package/src/patterns/findings.js +141 -0
  190. package/src/patterns/index.js +19 -0
  191. package/src/patterns/loop.js +158 -0
  192. package/src/patterns/pipeline.js +125 -0
  193. package/src/patterns/prompts.js +181 -0
  194. package/src/patterns/race.js +147 -0
  195. package/src/patterns/schemas.js +40 -0
  196. package/src/patterns/solo.js +49 -0
  197. package/src/patterns/supervisor.js +235 -0
  198. package/src/patterns/task-draft.js +29 -0
  199. package/src/patterns/tdd.js +231 -0
  200. package/src/patterns/team-strategies.js +67 -0
  201. package/src/performance.js +43 -0
  202. package/src/persistence-status.js +35 -0
  203. package/src/phone-sessions.js +96 -0
  204. package/src/preview-overlay.js +383 -0
  205. package/src/preview.js +219 -0
  206. package/src/privacy.js +23 -0
  207. package/src/project-report.js +22 -0
  208. package/src/project-team-model.js +178 -0
  209. package/src/project-team-policy.js +42 -0
  210. package/src/project-team-store.js +213 -0
  211. package/src/project-teams-api.js +36 -0
  212. package/src/project-teams.js +551 -0
  213. package/src/projects.js +286 -0
  214. package/src/provider-eligibility.js +9 -0
  215. package/src/readiness.js +98 -0
  216. package/src/remote-tls.js +66 -0
  217. package/src/remote-workers/bundles.js +198 -0
  218. package/src/remote-workers/client.js +332 -0
  219. package/src/remote-workers/durability.js +37 -0
  220. package/src/remote-workers/protocol.js +68 -0
  221. package/src/remote-workers/stdio.js +138 -0
  222. package/src/remote-workers/worker.js +304 -0
  223. package/src/remote.js +204 -0
  224. package/src/repo-config.js +201 -0
  225. package/src/repo-coordination.js +732 -0
  226. package/src/repo-tree.js +106 -0
  227. package/src/retention-policy.js +466 -0
  228. package/src/review-inbox.js +38 -0
  229. package/src/routing.js +49 -0
  230. package/src/run-analysis.js +111 -0
  231. package/src/run-cold-store.js +231 -0
  232. package/src/run-environment.js +15 -0
  233. package/src/run-history.js +91 -0
  234. package/src/run-log.js +160 -0
  235. package/src/run-permissions.js +110 -0
  236. package/src/run-projection.js +75 -0
  237. package/src/run-recovery.js +53 -0
  238. package/src/run-status.js +54 -0
  239. package/src/serve.js +138 -0
  240. package/src/server.js +2738 -0
  241. package/src/shared-access.js +103 -0
  242. package/src/shared-accounts.js +301 -0
  243. package/src/shared-api.js +239 -0
  244. package/src/shell.js +346 -0
  245. package/src/ship.js +344 -0
  246. package/src/slots.js +74 -0
  247. package/src/snapshot.js +388 -0
  248. package/src/source-context.js +17 -0
  249. package/src/sse-writer.js +70 -0
  250. package/src/ssh-projects/bootstrap.mjs +95 -0
  251. package/src/ssh-projects/config.js +45 -0
  252. package/src/ssh-projects/gateway.js +143 -0
  253. package/src/ssh-projects/manager.js +320 -0
  254. package/src/ssh-projects/team-autostart.mjs +93 -0
  255. package/src/ssh-projects/team-remove.mjs +69 -0
  256. package/src/ssh-projects/team-status.mjs +39 -0
  257. package/src/ssh-projects/transport.js +116 -0
  258. package/src/stats.js +139 -0
  259. package/src/store.js +544 -0
  260. package/src/stream.js +91 -0
  261. package/src/sweeps.js +241 -0
  262. package/src/tailscale.js +101 -0
  263. package/src/task-draft.js +23 -0
  264. package/src/team-evaluation.js +242 -0
  265. package/src/team-mailbox.js +160 -0
  266. package/src/team-memory.js +84 -0
  267. package/src/team-rerun-graph.js +15 -0
  268. package/src/team-reruns.js +130 -0
  269. package/src/team-scheduler.js +121 -0
  270. package/src/team-template-store.js +98 -0
  271. package/src/team-templates.js +117 -0
  272. package/src/team-tool-client.mjs +79 -0
  273. package/src/team-tools.js +176 -0
  274. package/src/teams.js +0 -0
  275. package/src/templates.js +62 -0
  276. package/src/transcript.js +134 -0
  277. package/src/verification-integrity.js +27 -0
  278. package/src/verification.js +183 -0
  279. package/src/watch.js +195 -0
  280. package/src/work-queue.js +47 -0
  281. package/src/workflow-control.js +83 -0
  282. package/src/workspace-api.js +207 -0
  283. package/src/workspace-changes.js +61 -0
  284. package/src/workspace.js +146 -0
@@ -0,0 +1,110 @@
1
+ function antigravityExecution(value, parent) {
2
+ if (value !== void 0 && value !== "isolated" && value !== "native") throw new Error("Invalid Antigravity execution mode");
3
+ if (parent && value === "native" && parent.antigravityExecution !== "native") throw new Error("A child run cannot enable unrestricted Antigravity permissions");
4
+ return value ?? parent?.antigravityExecution;
5
+ }
6
+ const AGY_NATIVE_LIMITATION = "Unrestricted Antigravity uses your existing login and normal provider configuration. Terminal, browser, MCP, hooks and native agents are not restricted by Code Flow. Read-only requests are advisory; native child usage is outside Code Flow's worker budget. A valid saved login is required.";
7
+ const levels = ["safe", "standard", "trusted", "full"];
8
+ function narrowerTrust(run, turn) {
9
+ if (!levels.includes(run) || turn !== void 0 && !levels.includes(turn)) throw new Error("Unknown turn permissions");
10
+ return levels[Math.min(levels.indexOf(run), levels.indexOf(turn ?? run))];
11
+ }
12
+ function permissionRequirements(value) {
13
+ if (value === void 0) return [];
14
+ if (!Array.isArray(value) || value.length > 3 || value.some((v) => !["workspace-writes", "no-command-network", "no-external-tools"].includes(v))) throw new Error("Unknown required permission enforcement");
15
+ return [...new Set(value)];
16
+ }
17
+ function validateRunPermissions(value) {
18
+ const p = value;
19
+ if (!p || typeof p !== "object" || Array.isArray(p) || Object.keys(p).some((k) => !["version", "revision", "trust", "toolApprovals", "delegation", "required", "antigravityExecution"].includes(k)) || p.version !== 1 || !Number.isSafeInteger(p.revision) || p.revision < 1 || !levels.includes(p.trust) || typeof p.toolApprovals !== "boolean" || !["managed", "none"].includes(p.delegation) || !Array.isArray(p.required)) throw new Error("Invalid run permission policy");
20
+ antigravityExecution(p.antigravityExecution);
21
+ return { ...p, required: permissionRequirements(p.required) };
22
+ }
23
+ function createRunPermissions(trust, tools, solo, required, execution) {
24
+ return validateRunPermissions({ version: 1, revision: 1, trust, toolApprovals: tools, delegation: solo ? "none" : "managed", required: permissionRequirements(required), ...execution !== void 0 ? { antigravityExecution: antigravityExecution(execution) } : {} });
25
+ }
26
+ const supportsRunPermissions = (provider) => provider === "claude" || provider === "codex" || provider === "agy";
27
+ const AGY_MANAGED_PERMISSION_PROBLEM = "Antigravity managed execution requires the verified local macOS file-only executor and existing Keychain login. Remote/container execution and interactive approvals are unsupported. Choose Claude or Codex for other environments.";
28
+ function permissionProviderProblem(provider, policy) {
29
+ if (policy && provider === "agy" && policy.antigravityExecution !== "native" && (typeof process === "undefined" || process.platform !== "darwin")) return AGY_MANAGED_PERMISSION_PROBLEM;
30
+ if (policy && !supportsRunPermissions(provider)) return `${provider} cannot enforce Code Flow's native-delegation restriction. Choose Claude or Codex for this run.`;
31
+ }
32
+ function resolveRunPermissions(input) {
33
+ const { provider, mode } = input;
34
+ const policy = validateRunPermissions(input.policy);
35
+ if (!["read", "write"].includes(mode)) throw new Error("Invalid turn access mode");
36
+ const problem = permissionProviderProblem(provider, policy);
37
+ if (problem) throw new Error(problem);
38
+ const trust = narrowerTrust(narrowerTrust(policy.trust, input.currentTrust), input.turnTrust);
39
+ if (provider === "agy" && policy.antigravityExecution === "native") {
40
+ if (input.remote || input.container || input.unattended || policy.toolApprovals || policy.required.length || trust === "safe" || trust !== policy.trust) throw new Error("Unrestricted Antigravity requires a local attended run without mandatory boundaries, Safe mode, narrowed trust or interactive approvals");
41
+ return {
42
+ required: [],
43
+ delegation: policy.delegation,
44
+ policyRevision: policy.revision,
45
+ provider,
46
+ trust: "full",
47
+ mode,
48
+ nativeDelegation: true,
49
+ toolApprovals: false,
50
+ isolation: "none",
51
+ implementation: "agy-native-v1",
52
+ commandNetwork: "allowed",
53
+ externalTools: "provider-configured",
54
+ limitations: [AGY_NATIVE_LIMITATION, "Each managed turn starts fresh using portable context; native sessions are not resumed."]
55
+ };
56
+ }
57
+ if (provider === "agy") {
58
+ if (input.remote || input.container || policy.toolApprovals && input.currentTools && input.tools !== false && mode === "write") throw new Error(AGY_MANAGED_PERMISSION_PROBLEM);
59
+ return {
60
+ required: [...policy.required],
61
+ delegation: policy.delegation,
62
+ policyRevision: policy.revision,
63
+ provider,
64
+ trust,
65
+ mode,
66
+ nativeDelegation: false,
67
+ toolApprovals: false,
68
+ isolation: "os-sandbox",
69
+ implementation: "macos-file-only-v1",
70
+ commandNetwork: "denied",
71
+ externalTools: "disabled",
72
+ limitations: ["Antigravity has file tools only; terminal, browser, MCP, hooks and native delegation are disabled.", "The official CLI uses the existing macOS Keychain login; inference networking remains enabled.", "System runtime files and the native security helper remain accessible; model file tools are denied authentication and runtime state.", "Code Flow runs setup and checks in their separately configured command environment.", "Every turn starts with a fresh provider profile and portable context; native sessions are not resumed."]
73
+ };
74
+ }
75
+ const externalTools = policy.required.includes("no-external-tools") ? "disabled" : "provider-configured";
76
+ if (externalTools === "disabled" && provider !== "claude") throw new Error(`${provider} has no verified external-tool isolation in this adapter`);
77
+ const isolation = input.container ? "container" : trust === "full" && mode === "write" ? "none" : provider === "codex" ? "provider-sandbox" : "tool-rules";
78
+ const commandNetwork = provider === "codex" && (mode === "read" || trust === "safe" || trust === "standard") ? "denied" : trust === "full" || trust === "trusted" ? "allowed" : "provider-configured";
79
+ if (policy.required.includes("workspace-writes") && (isolation === "none" || isolation === "tool-rules" || input.remote || input.container)) throw new Error(`${provider} cannot enforce the required workspace write boundary in this environment`);
80
+ if (policy.required.includes("no-command-network") && (commandNetwork !== "denied" || input.remote || input.container)) throw new Error(`${provider} cannot enforce the required command network restriction in this environment`);
81
+ const wantsApprovals = policy.toolApprovals && input.currentTools && input.tools !== false && mode === "write";
82
+ if (wantsApprovals && policy.required.some((r) => r === "workspace-writes" || r === "no-command-network")) throw new Error("Interactive command approvals cannot be combined with mandatory sandbox boundaries");
83
+ if (wantsApprovals && (provider !== "codex" || input.remote || input.container || input.unattended)) throw new Error(`${provider} cannot provide the run's required interactive approvals in this environment`);
84
+ return {
85
+ required: [...policy.required],
86
+ delegation: policy.delegation,
87
+ policyRevision: policy.revision,
88
+ provider,
89
+ trust,
90
+ mode,
91
+ nativeDelegation: false,
92
+ toolApprovals: policy.toolApprovals && input.currentTools && input.tools !== false && !input.container && !input.remote && !input.unattended && mode === "write",
93
+ isolation,
94
+ commandNetwork,
95
+ externalTools,
96
+ limitations: ["Worktrees do not restrict file reads.", "Setup and check commands use the run\u2019s separately configured command environment.", ...isolation === "tool-rules" ? ["CLI tool rules are not an operating-system filesystem sandbox."] : [], ...externalTools === "provider-configured" ? ["Browser and external MCP permissions follow the provider configuration; terminal network policy does not restrict them."] : []]
97
+ };
98
+ }
99
+ export {
100
+ AGY_MANAGED_PERMISSION_PROBLEM,
101
+ AGY_NATIVE_LIMITATION,
102
+ antigravityExecution,
103
+ createRunPermissions,
104
+ narrowerTrust,
105
+ permissionProviderProblem,
106
+ permissionRequirements,
107
+ resolveRunPermissions,
108
+ supportsRunPermissions,
109
+ validateRunPermissions
110
+ };
@@ -0,0 +1,75 @@
1
+ import { join } from "node:path";
2
+ import { worktreeRoot } from "./git.js";
3
+ import { RunLogReader } from "./run-log.js";
4
+ const DEFAULT_LOOP = { maxMinutes: 60, maxSteps: 40, checkTimeoutMin: 10 };
5
+ class RunProjection {
6
+ snapshot;
7
+ steps = /* @__PURE__ */ new Map();
8
+ reader = new RunLogReader();
9
+ seq = 0;
10
+ records = 0;
11
+ fullSnapshots = 0;
12
+ patches = 0;
13
+ apply(line) {
14
+ this.records++;
15
+ if (Number.isSafeInteger(line.seq) && line.seq > this.seq) this.seq = line.seq;
16
+ if (line.type === "run") this.fullSnapshots++;
17
+ else if (line.type === "run-patch") this.patches++;
18
+ const event = this.reader.decode(line);
19
+ if (!event) return;
20
+ if (event.type === "run" && event.run) this.snapshot = event.run;
21
+ else if (event.type === "step" && event.step) this.steps.set(event.step.id, { ...event.step, kind: event.step.kind ?? "agent" });
22
+ }
23
+ finish() {
24
+ return rebuildSnapshot(this.snapshot, this.steps);
25
+ }
26
+ }
27
+ function rebuildRun(events) {
28
+ const projection = new RunProjection();
29
+ for (const event of events) projection.apply(event);
30
+ return projection.finish();
31
+ }
32
+ function rebuildSnapshot(snapshot, steps) {
33
+ if (!snapshot) return void 0;
34
+ const legacy = snapshot;
35
+ return {
36
+ ...legacy,
37
+ context: legacy.context ?? { paths: [], attachments: [] },
38
+ agentSettings: legacy.agentSettings ?? {},
39
+ loop: { ...DEFAULT_LOOP, ...legacy.loop ?? {} },
40
+ usage: legacy.usage ?? { agentSteps: 0, checks: 0 },
41
+ // Runs saved before patternDone existed: a finished run's pattern finished.
42
+ patternDone: legacy.patternDone ?? (legacy.status === "done" ? true : void 0),
43
+ patternSummary: legacy.patternSummary ?? (legacy.status === "done" && legacy.summary ? withoutFollowUps(legacy.summary) : void 0),
44
+ worktrees: legacy.worktrees ?? [],
45
+ baseLabel: legacy.baseLabel ?? "HEAD",
46
+ branchPrefix: legacy.branchPrefix ?? `codeflow/${legacy.id}`,
47
+ worktreeDir: legacy.worktreeDir ?? join(worktreeRoot(legacy.repo), legacy.id),
48
+ followUps: legacy.followUps ?? [],
49
+ notes: legacy.notes ?? [],
50
+ roleSettings: legacy.roleSettings ?? {},
51
+ trust: legacy.trust ?? "standard",
52
+ approvals: legacy.approvals ?? {},
53
+ merges: legacy.merges ?? [],
54
+ prs: legacy.prs ?? {},
55
+ watches: legacy.watches ?? {},
56
+ seen: legacy.seen ?? true,
57
+ archived: legacy.archived ?? false,
58
+ artifacts: legacy.artifacts ?? [],
59
+ steps: [...steps.values()]
60
+ };
61
+ }
62
+ function strip(run) {
63
+ const { steps: _steps, ...rest } = run;
64
+ return structuredClone(rest);
65
+ }
66
+ function withoutFollowUps(summary) {
67
+ return summary.split("\n\n## Follow-ups\n")[0];
68
+ }
69
+ export {
70
+ DEFAULT_LOOP,
71
+ RunProjection,
72
+ rebuildRun,
73
+ strip,
74
+ withoutFollowUps
75
+ };
@@ -0,0 +1,53 @@
1
+ import { constants, closeSync, fstatSync, openSync, readSync } from "node:fs";
2
+ import { StringDecoder } from "node:string_decoder";
3
+ import { join } from "node:path";
4
+ import { RunProjection, strip } from "./run-projection.js";
5
+ import { readRunCheckpoint, RUNS_DIR, visitJsonLines } from "./store.js";
6
+ async function recoverRun(id) {
7
+ const projection = new RunProjection(), checkpoint = await readRunCheckpoint(id);
8
+ if (checkpoint) {
9
+ projection.apply({ seq: checkpoint.seq, ts: checkpoint.run.createdAt, runId: id, type: "run", run: strip(checkpoint.run) });
10
+ for (const step of checkpoint.run.steps) projection.apply({ seq: checkpoint.seq, ts: step.startedAt, runId: id, type: "step", step });
11
+ } else await visitJsonLines(`${id}.jsonl`, (event) => projection.apply(event));
12
+ return { run: projection.finish(), seq: projection.seq, records: projection.records, fullSnapshots: projection.fullSnapshots, patches: projection.patches };
13
+ }
14
+ function recoverRunSync(id) {
15
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,199}$/.test(id)) throw new Error("Invalid run ID");
16
+ const file = openSync(join(RUNS_DIR, `${id}.jsonl`), constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0)), projection = new RunProjection(), decoder = new StringDecoder("utf8"), chunk = Buffer.alloc(64 * 1024);
17
+ let pending = "";
18
+ const parse = (line) => {
19
+ if (Buffer.byteLength(line) > 64 * 1024 * 1024) throw new Error("Run log record exceeds the 64 MiB recovery bound");
20
+ if (!line.trim()) return;
21
+ let event;
22
+ try {
23
+ event = JSON.parse(line);
24
+ } catch {
25
+ return;
26
+ }
27
+ projection.apply(event);
28
+ };
29
+ try {
30
+ const before = fstatSync(file, { bigint: true });
31
+ if (!before.isFile()) throw new Error("Run log must be a regular file");
32
+ let count;
33
+ while (count = readSync(file, chunk, 0, chunk.length, null)) {
34
+ pending += decoder.write(chunk.subarray(0, count));
35
+ let index;
36
+ while ((index = pending.indexOf("\n")) >= 0) {
37
+ parse(pending.slice(0, index));
38
+ pending = pending.slice(index + 1);
39
+ }
40
+ if (Buffer.byteLength(pending) > 64 * 1024 * 1024) throw new Error("Run log record exceeds the 64 MiB recovery bound");
41
+ }
42
+ parse(pending + decoder.end());
43
+ const after = fstatSync(file, { bigint: true });
44
+ if (before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs) throw new Error("Run log changed while recovering; retry the read");
45
+ return { run: projection.finish(), seq: projection.seq };
46
+ } finally {
47
+ closeSync(file);
48
+ }
49
+ }
50
+ export {
51
+ recoverRun,
52
+ recoverRunSync
53
+ };
@@ -0,0 +1,54 @@
1
+ function checksLabel(run, checking = false, branch) {
2
+ if (checking) return "Running";
3
+ if (run.status === "running") return "Pending";
4
+ const checkedBranch = run.verification?.branch ?? run.worktrees?.find((tree) => tree.name === run.lastCheck?.worktree)?.branch;
5
+ if (branch && checkedBranch !== branch) return "Not verified for this branch";
6
+ if (run.checkCantRun) return "Blocked";
7
+ if (run.verdict === "failing") return "Failed";
8
+ if (run.verification) return { verified: "Passed", "needs-changes": "Failed", blocked: "Blocked", unchecked: "Unverified" }[run.verification.state];
9
+ if (run.verdict === "passed") return "Passed";
10
+ return run.loop?.check ? "Not verified" : "Not configured";
11
+ }
12
+ const runPassed = (run) => run.status === "done" && checksLabel(run) === "Passed";
13
+ function runState(run) {
14
+ if (run.status !== "done") return run.status;
15
+ const check = checksLabel(run);
16
+ return check === "Failed" ? "failing" : check === "Blocked" ? "unchecked" : check === "Passed" ? "done" : "unverified";
17
+ }
18
+ function needsYou(run) {
19
+ if (run.archived) return false;
20
+ const question = run.status === "running" && !!run.team?.awaitingRequestId;
21
+ if (run.awaiting || question) return true;
22
+ if (run.projectTeam) return false;
23
+ return run.team?.status === "blocked" || run.triage?.state === "pending" || Object.values(run.watches ?? {}).some((watch) => watch.state === "needs-you") || run.status !== "running" && (run.status === "interrupted" || !run.seen);
24
+ }
25
+ const isFlagged = (run) => Object.keys(run.integrity ?? {}).length > 0 || !!run.followUps?.some((turn) => turn.integrity?.length);
26
+ const HISTORY_STATUSES = ["all", "attention", "running", "passed", "failing", "flagged"];
27
+ function matchesHistoryFilter(run, filter) {
28
+ if (filter.repo && run.repo !== filter.repo || filter.pattern && run.pattern !== filter.pattern) return false;
29
+ if (filter.agent && !Object.values(run.roles).some((ids) => ids.some((id) => id === filter.agent))) return false;
30
+ if (filter.section && (run.archived || (filter.section === "waiting" ? !needsYou(run) : needsYou(run) || (filter.section === "running" ? run.status !== "running" : run.status === "running")))) return false;
31
+ switch (filter.status) {
32
+ case "attention":
33
+ return needsYou(run);
34
+ case "running":
35
+ return run.status === "running";
36
+ case "passed":
37
+ return runPassed(run);
38
+ case "failing":
39
+ return checksLabel(run) === "Failed" || ["failed", "stopped", "interrupted"].includes(run.status);
40
+ case "flagged":
41
+ return isFlagged(run);
42
+ default:
43
+ return true;
44
+ }
45
+ }
46
+ export {
47
+ HISTORY_STATUSES,
48
+ checksLabel,
49
+ isFlagged,
50
+ matchesHistoryFilter,
51
+ needsYou,
52
+ runPassed,
53
+ runState
54
+ };
package/src/serve.js ADDED
@@ -0,0 +1,138 @@
1
+ import { spawn, execFile } from "node:child_process";
2
+ import { createServer, createConnection } from "node:net";
3
+ import { platform } from "node:os";
4
+ import { killGroup, trackGroup, untrackGroup } from "./adapters/process.js";
5
+ import { childEnv } from "./env.js";
6
+ const MAX_LOG_LINES = 400;
7
+ class DevServers {
8
+ servers = /* @__PURE__ */ new Map();
9
+ get(key) {
10
+ const s = this.servers.get(key);
11
+ if (!s) return void 0;
12
+ const { child: _child, ...rest } = s;
13
+ return { ...rest, logs: [...rest.logs] };
14
+ }
15
+ /** Every server, without their logs: what the side panel's Browser can open. */
16
+ list() {
17
+ return [...this.servers.values()].map(({ child: _child, logs: _logs, ...rest }) => ({ ...rest }));
18
+ }
19
+ async ready(key) {
20
+ const server = this.servers.get(key);
21
+ if (!server || server.status !== "running") return false;
22
+ return new Promise((resolve) => {
23
+ const socket = createConnection({ host: "localhost", port: server.port, autoSelectFamily: true });
24
+ let settled = false;
25
+ const finish = (ready) => {
26
+ if (settled) return;
27
+ settled = true;
28
+ socket.destroy();
29
+ resolve(ready);
30
+ };
31
+ socket.setTimeout(500, () => finish(false));
32
+ socket.once("connect", () => finish(true));
33
+ socket.once("error", () => finish(false));
34
+ });
35
+ }
36
+ async start(key, cwd, command, preferredPort, generatedEnv = {}, signal) {
37
+ this.stop(key);
38
+ const port = await freePort(preferredPort);
39
+ if (signal?.aborted) throw new Error("Service startup was cancelled before spawning");
40
+ if (generatedEnv.PORT !== void 0 && generatedEnv.PORT !== String(port)) throw new Error("The allocated service port became busy. Restart the launch profile to allocate fresh ports.");
41
+ const child = spawn("/bin/sh", ["-c", command], {
42
+ cwd,
43
+ detached: true,
44
+ // own process group: stopping it stops whatever the command started
45
+ stdio: ["ignore", "pipe", "pipe"],
46
+ env: { ...childEnv(generatedEnv), PORT: String(port), BROWSER: "none", FORCE_COLOR: "0" }
47
+ });
48
+ trackGroup(child.pid);
49
+ const server = { key, command, cwd, port, url: `http://localhost:${port}`, status: "running", startedAt: Date.now(), logs: [], child };
50
+ const collect = (chunk) => {
51
+ for (const line of chunk.toString().split("\n")) {
52
+ if (!line.trim()) continue;
53
+ server.logs.push(line.slice(-4e3));
54
+ if (server.logs.length > MAX_LOG_LINES) server.logs.shift();
55
+ }
56
+ };
57
+ child.stdout.on("data", collect);
58
+ child.stderr.on("data", collect);
59
+ child.on("exit", (code) => {
60
+ untrackGroup(child.pid);
61
+ server.status = "exited";
62
+ server.exitCode = code;
63
+ });
64
+ child.on("error", (err) => {
65
+ server.status = "exited";
66
+ server.logs.push(`Failed to start: ${err.message}`);
67
+ });
68
+ this.servers.set(key, server);
69
+ return this.get(key);
70
+ }
71
+ stop(key) {
72
+ const s = this.servers.get(key);
73
+ if (!s) return false;
74
+ killGroup(s.child.pid);
75
+ untrackGroup(s.child.pid);
76
+ this.servers.delete(key);
77
+ return true;
78
+ }
79
+ /** Stop every server whose key starts with `prefix` (e.g. one run's). */
80
+ stopMatching(prefix) {
81
+ for (const key of [...this.servers.keys()]) if (key.startsWith(prefix)) this.stop(key);
82
+ }
83
+ stopAll() {
84
+ for (const key of [...this.servers.keys()]) this.stop(key);
85
+ }
86
+ }
87
+ async function freePort(preferred) {
88
+ const tryPort = (port) => new Promise((resolve) => {
89
+ const srv = createServer();
90
+ srv.once("error", () => resolve(null));
91
+ srv.listen(port, "127.0.0.1", () => {
92
+ const actual = srv.address().port;
93
+ srv.close(() => resolve(actual));
94
+ });
95
+ });
96
+ if (preferred) {
97
+ for (let p = preferred; p < preferred + 20; p++) {
98
+ const ok = await tryPort(p);
99
+ if (ok) return ok;
100
+ }
101
+ }
102
+ return await tryPort(0);
103
+ }
104
+ async function openFolder(path, target) {
105
+ const os = platform();
106
+ if (target === "editor") {
107
+ for (const cli of ["cursor", "code", "zed", "subl"]) {
108
+ if (await has(cli)) {
109
+ await run(cli, [path]);
110
+ return cli;
111
+ }
112
+ }
113
+ throw new Error("No editor command found (cursor, code, zed or subl on PATH)");
114
+ }
115
+ if (os === "darwin") {
116
+ if (target === "terminal") await run("open", ["-a", "Terminal", path]);
117
+ else await run("open", [path]);
118
+ return target === "terminal" ? "Terminal" : "Finder";
119
+ }
120
+ if (target === "terminal") {
121
+ for (const term of ["x-terminal-emulator", "gnome-terminal", "konsole"]) {
122
+ if (await has(term)) {
123
+ spawn(term, [], { cwd: path, detached: true, stdio: "ignore" }).unref();
124
+ return term;
125
+ }
126
+ }
127
+ throw new Error("No terminal emulator found");
128
+ }
129
+ await run("xdg-open", [path]);
130
+ return "file manager";
131
+ }
132
+ const has = (cmd) => new Promise((resolve) => execFile("which", [cmd], (err) => resolve(!err)));
133
+ const run = (cmd, args) => new Promise((resolve, reject) => execFile(cmd, args, (err) => err ? reject(new Error(`${cmd} failed: ${err.message}`)) : resolve()));
134
+ export {
135
+ DevServers,
136
+ freePort,
137
+ openFolder
138
+ };