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

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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -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/changes-62ZIEQSN.js +1 -0
  17. package/public/assets/chunk-2PZVOKID.js +1 -0
  18. package/public/assets/chunk-4D5IENEB.js +1 -0
  19. package/public/assets/chunk-5SARP5MU.js +1 -0
  20. package/public/assets/chunk-63YLZ3PR.js +1 -0
  21. package/public/assets/chunk-7FUHKF2M.js +1 -0
  22. package/public/assets/chunk-APQEVJ2K.js +1 -0
  23. package/public/assets/chunk-CGOZQMEZ.js +2 -0
  24. package/public/assets/chunk-J4VYS3KV.js +2 -0
  25. package/public/assets/chunk-L7TUCIYF.js +1 -0
  26. package/public/assets/chunk-SIHDWM2Y.js +154 -0
  27. package/public/assets/chunk-TEJVYIKC.js +1 -0
  28. package/public/assets/code-browser-VB2HGZSG.js +4 -0
  29. package/public/assets/filtered-tasks-ES5CP4KV.js +1 -0
  30. package/public/assets/interactive-browser-7F5ZXRUV.js +7 -0
  31. package/public/assets/join-project-6RTFHNCA.js +1 -0
  32. package/public/assets/main-65EW73SB.js +1 -0
  33. package/public/assets/project-invitations-view-GMPWGWYM.js +1 -0
  34. package/public/assets/settings-UB5Q4AQM.js +9 -0
  35. package/public/assets/styles-18792BB4.css +32 -0
  36. package/public/assets/team-server-wizard-2GLDWS6D.js +8 -0
  37. package/public/assets/terminal-IWAIM4DE.js +62 -0
  38. package/public/assets.json +1 -0
  39. package/public/bundle-meta.json +1 -0
  40. package/public/docs/.nojekyll +0 -0
  41. package/public/docs/accessibility.html +19 -0
  42. package/public/docs/acp-agents.html +117 -0
  43. package/public/docs/agent-containers.html +79 -0
  44. package/public/docs/api.html +1130 -0
  45. package/public/docs/app.js +342 -0
  46. package/public/docs/application-improvements.html +275 -0
  47. package/public/docs/architecture-explorer.html +19 -0
  48. package/public/docs/architecture.html +149 -0
  49. package/public/docs/auto-team.html +148 -0
  50. package/public/docs/automations.html +109 -0
  51. package/public/docs/checkpoint-review.html +37 -0
  52. package/public/docs/code-hosts.html +35 -0
  53. package/public/docs/code-intelligence.html +35 -0
  54. package/public/docs/configuration.html +346 -0
  55. package/public/docs/context-inspection.html +23 -0
  56. package/public/docs/coordinated-changes.html +28 -0
  57. package/public/docs/deferred-runs.html +25 -0
  58. package/public/docs/dependency-discovery.html +19 -0
  59. package/public/docs/documentation.html +84 -0
  60. package/public/docs/dynamic-teams-evaluation.html +174 -0
  61. package/public/docs/dynamic-teams-implementation-plan.html +882 -0
  62. package/public/docs/dynamic-teams-implementation-review-1.html +141 -0
  63. package/public/docs/dynamic-teams-plan-review-0.html +204 -0
  64. package/public/docs/extensions.html +42 -0
  65. package/public/docs/external-sources.html +96 -0
  66. package/public/docs/filesystem-policy.html +26 -0
  67. package/public/docs/filtered-tasks.html +44 -0
  68. package/public/docs/font-license.txt +92 -0
  69. package/public/docs/getting-started.html +143 -0
  70. package/public/docs/github-review-threads.html +26 -0
  71. package/public/docs/github.html +157 -0
  72. package/public/docs/help.html +69 -0
  73. package/public/docs/index.html +88 -0
  74. package/public/docs/installation.html +80 -0
  75. package/public/docs/launch-profiles.html +48 -0
  76. package/public/docs/maintenance.html +60 -0
  77. package/public/docs/memory-claims.html +31 -0
  78. package/public/docs/memory-storage.html +58 -0
  79. package/public/docs/notifications.html +39 -0
  80. package/public/docs/patterns.html +195 -0
  81. package/public/docs/performance-results.html +210 -0
  82. package/public/docs/performance.html +166 -0
  83. package/public/docs/project-conventions.html +22 -0
  84. package/public/docs/project-first-navigation-plan.html +587 -0
  85. package/public/docs/project-teams-implementation-plan.html +772 -0
  86. package/public/docs/project-teams.html +129 -0
  87. package/public/docs/provider-compatibility.html +20 -0
  88. package/public/docs/releasing.html +65 -0
  89. package/public/docs/remote-workers.html +64 -0
  90. package/public/docs/review-inbox.html +26 -0
  91. package/public/docs/run-storage.html +23 -0
  92. package/public/docs/runs.html +141 -0
  93. package/public/docs/search.json +1 -0
  94. package/public/docs/security.html +133 -0
  95. package/public/docs/shared-projects.html +31 -0
  96. package/public/docs/ssh-projects.html +60 -0
  97. package/public/docs/style.css +768 -0
  98. package/public/docs/task-reruns.html +30 -0
  99. package/public/docs/team-collaboration.html +412 -0
  100. package/public/docs/verification.html +22 -0
  101. package/public/docs/vscode.html +53 -0
  102. package/public/docs/worker-permission-verification.html +131 -0
  103. package/public/index.html +17 -0
  104. package/scripts/check-node.mjs +7 -0
  105. package/scripts/codeflow.mjs +55 -0
  106. package/scripts/computer-use.swift +64 -0
  107. package/scripts/pty.py +273 -0
  108. package/src/acp-connections.js +110 -0
  109. package/src/adapters/acp-presets.js +37 -0
  110. package/src/adapters/acp-types.js +0 -0
  111. package/src/adapters/acp.js +328 -0
  112. package/src/adapters/agent-tools.js +28 -0
  113. package/src/adapters/agy-auth.js +67 -0
  114. package/src/adapters/agy-isolation.js +242 -0
  115. package/src/adapters/agy-tools.js +45 -0
  116. package/src/adapters/agy.js +326 -0
  117. package/src/adapters/claude-stream.js +58 -0
  118. package/src/adapters/claude.js +282 -0
  119. package/src/adapters/codex-app-server.js +555 -0
  120. package/src/adapters/codex.js +311 -0
  121. package/src/adapters/errors.js +57 -0
  122. package/src/adapters/index.js +91 -0
  123. package/src/adapters/managed-permissions.js +37 -0
  124. package/src/adapters/model-discovery.js +100 -0
  125. package/src/adapters/process.js +433 -0
  126. package/src/adapters/team-tools.js +103 -0
  127. package/src/adapters/types.js +0 -0
  128. package/src/agent-browser.js +688 -0
  129. package/src/agent-computer.js +88 -0
  130. package/src/agent-container.js +155 -0
  131. package/src/agent-team-definitions.js +131 -0
  132. package/src/agent-tool-client.mjs +79 -0
  133. package/src/agent-tools.js +296 -0
  134. package/src/approvals.js +134 -0
  135. package/src/architecture.js +456 -0
  136. package/src/assets.js +44 -0
  137. package/src/automation.js +901 -0
  138. package/src/browser-files.js +293 -0
  139. package/src/browser-ownership.js +150 -0
  140. package/src/certificate-renewal.js +75 -0
  141. package/src/chat-family.js +25 -0
  142. package/src/chat-handoff.js +87 -0
  143. package/src/checkpoint-review.js +72 -0
  144. package/src/checks.js +303 -0
  145. package/src/code-host-git.js +102 -0
  146. package/src/code-hosts-api.js +99 -0
  147. package/src/code-hosts.js +482 -0
  148. package/src/code-intelligence-worker.mjs +452 -0
  149. package/src/code-intelligence.js +141 -0
  150. package/src/collab/api.js +365 -0
  151. package/src/collab/checkpoints.js +198 -0
  152. package/src/collab/connections.js +139 -0
  153. package/src/collab/host-execution.js +347 -0
  154. package/src/collab/hub-client.js +210 -0
  155. package/src/collab/link-api.js +229 -0
  156. package/src/collab/link.js +595 -0
  157. package/src/collab/model.js +496 -0
  158. package/src/collab/runner.js +1014 -0
  159. package/src/collab/store.js +1452 -0
  160. package/src/collab/terminals.js +361 -0
  161. package/src/collaboration-reviews.js +116 -0
  162. package/src/config.js +48 -0
  163. package/src/context-budget.js +14 -0
  164. package/src/context-inspection.js +204 -0
  165. package/src/context.js +154 -0
  166. package/src/conventions.js +283 -0
  167. package/src/coordinated-api.js +247 -0
  168. package/src/deferred-launch.js +82 -0
  169. package/src/deferred-runs.js +515 -0
  170. package/src/dependency-health.js +35 -0
  171. package/src/diff.js +43 -0
  172. package/src/docs-help.js +316 -0
  173. package/src/doctor.js +232 -0
  174. package/src/documentation-changes.js +20 -0
  175. package/src/engine.js +5258 -0
  176. package/src/env.js +44 -0
  177. package/src/execution-budget.js +8 -0
  178. package/src/experience.js +50 -0
  179. package/src/extensions.js +144 -0
  180. package/src/filesystem-access.js +107 -0
  181. package/src/filesystem-policy.js +238 -0
  182. package/src/filtered-task-limits.js +93 -0
  183. package/src/filtered-tasks-api.js +53 -0
  184. package/src/filtered-tasks.js +576 -0
  185. package/src/frame-policy.js +66 -0
  186. package/src/fs.js +57 -0
  187. package/src/git.js +626 -0
  188. package/src/github-review-threads.js +181 -0
  189. package/src/github.js +538 -0
  190. package/src/http.js +69 -0
  191. package/src/improve-settings.js +38 -0
  192. package/src/improve.js +187 -0
  193. package/src/integrations.js +373 -0
  194. package/src/integrity.js +259 -0
  195. package/src/journey-api.js +77 -0
  196. package/src/journey-replay.js +65 -0
  197. package/src/journey-store.js +97 -0
  198. package/src/journeys.js +60 -0
  199. package/src/json.js +81 -0
  200. package/src/launch-profiles.js +292 -0
  201. package/src/learnings.js +80 -0
  202. package/src/local-app-url.js +19 -0
  203. package/src/log.js +39 -0
  204. package/src/maintenance.js +335 -0
  205. package/src/memory-analysis.js +182 -0
  206. package/src/memory-claims.js +153 -0
  207. package/src/memory-sqlite.js +236 -0
  208. package/src/memory.js +552 -0
  209. package/src/merge-validation.js +58 -0
  210. package/src/model-catalog.js +239 -0
  211. package/src/model-research.js +91 -0
  212. package/src/notes.js +176 -0
  213. package/src/notifications.js +413 -0
  214. package/src/patterns/auto-team-model.js +230 -0
  215. package/src/patterns/auto-team.js +816 -0
  216. package/src/patterns/council.js +199 -0
  217. package/src/patterns/debate.js +188 -0
  218. package/src/patterns/findings.js +141 -0
  219. package/src/patterns/index.js +21 -0
  220. package/src/patterns/loop.js +158 -0
  221. package/src/patterns/pipeline.js +125 -0
  222. package/src/patterns/prompts.js +181 -0
  223. package/src/patterns/race.js +147 -0
  224. package/src/patterns/schemas.js +40 -0
  225. package/src/patterns/solo.js +46 -0
  226. package/src/patterns/supervisor.js +235 -0
  227. package/src/patterns/task-draft.js +29 -0
  228. package/src/patterns/task-review.js +45 -0
  229. package/src/patterns/tdd.js +231 -0
  230. package/src/patterns/team-strategies.js +67 -0
  231. package/src/performance.js +43 -0
  232. package/src/persistence-status.js +35 -0
  233. package/src/phone-sessions.js +96 -0
  234. package/src/preview-overlay.js +392 -0
  235. package/src/preview.js +230 -0
  236. package/src/privacy.js +23 -0
  237. package/src/project-report.js +22 -0
  238. package/src/project-team-budget.js +22 -0
  239. package/src/project-team-model.js +225 -0
  240. package/src/project-team-policy.js +42 -0
  241. package/src/project-team-store.js +234 -0
  242. package/src/project-team-usage.js +24 -0
  243. package/src/project-teams-api.js +43 -0
  244. package/src/project-teams.js +644 -0
  245. package/src/projects.js +290 -0
  246. package/src/provider-eligibility.js +9 -0
  247. package/src/readiness.js +98 -0
  248. package/src/remote-tls.js +66 -0
  249. package/src/remote-workers/bundles.js +198 -0
  250. package/src/remote-workers/client.js +332 -0
  251. package/src/remote-workers/durability.js +37 -0
  252. package/src/remote-workers/protocol.js +68 -0
  253. package/src/remote-workers/stdio.js +138 -0
  254. package/src/remote-workers/worker.js +304 -0
  255. package/src/remote.js +204 -0
  256. package/src/repo-config.js +201 -0
  257. package/src/repo-coordination.js +725 -0
  258. package/src/repo-tree.js +106 -0
  259. package/src/repository-identity.js +13 -0
  260. package/src/retention-policy.js +466 -0
  261. package/src/review-inbox.js +38 -0
  262. package/src/routing.js +49 -0
  263. package/src/run-analysis.js +111 -0
  264. package/src/run-cold-store.js +231 -0
  265. package/src/run-environment.js +15 -0
  266. package/src/run-history.js +91 -0
  267. package/src/run-log.js +160 -0
  268. package/src/run-permissions.js +111 -0
  269. package/src/run-projection.js +77 -0
  270. package/src/run-recovery.js +53 -0
  271. package/src/run-status.js +56 -0
  272. package/src/serve.js +138 -0
  273. package/src/server.js +2871 -0
  274. package/src/shared-access.js +103 -0
  275. package/src/shared-accounts.js +381 -0
  276. package/src/shared-api.js +239 -0
  277. package/src/shell.js +346 -0
  278. package/src/ship.js +344 -0
  279. package/src/slots.js +74 -0
  280. package/src/snapshot.js +388 -0
  281. package/src/source-context.js +17 -0
  282. package/src/sse-writer.js +70 -0
  283. package/src/ssh-projects/bootstrap.mjs +95 -0
  284. package/src/ssh-projects/config.js +45 -0
  285. package/src/ssh-projects/gateway.js +143 -0
  286. package/src/ssh-projects/manager.js +320 -0
  287. package/src/ssh-projects/team-autostart.mjs +93 -0
  288. package/src/ssh-projects/team-remove.mjs +69 -0
  289. package/src/ssh-projects/team-status.mjs +39 -0
  290. package/src/ssh-projects/transport.js +133 -0
  291. package/src/stats.js +139 -0
  292. package/src/store.js +544 -0
  293. package/src/stream.js +91 -0
  294. package/src/sweeps.js +241 -0
  295. package/src/tailscale.js +101 -0
  296. package/src/task-draft.js +23 -0
  297. package/src/task-team.js +164 -0
  298. package/src/team-evaluation.js +243 -0
  299. package/src/team-mailbox.js +160 -0
  300. package/src/team-memory.js +84 -0
  301. package/src/team-rerun-graph.js +15 -0
  302. package/src/team-reruns.js +130 -0
  303. package/src/team-scheduler.js +121 -0
  304. package/src/team-steering.js +21 -0
  305. package/src/team-template-store.js +98 -0
  306. package/src/team-templates.js +117 -0
  307. package/src/team-tool-client.mjs +79 -0
  308. package/src/team-tools.js +235 -0
  309. package/src/teams.js +0 -0
  310. package/src/templates.js +62 -0
  311. package/src/transcript.js +134 -0
  312. package/src/verification-integrity.js +27 -0
  313. package/src/verification.js +183 -0
  314. package/src/watch.js +195 -0
  315. package/src/work-queue.js +47 -0
  316. package/src/workflow-control.js +83 -0
  317. package/src/workspace-api.js +207 -0
  318. package/src/workspace-changes.js +61 -0
  319. package/src/workspace.js +146 -0
@@ -0,0 +1,234 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { mkdir, open, readdir, rename, rm } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { CODEFLOW_HOME } from "./config.js";
6
+ import { PT_LIMITS, validateTeamDocument } from "./project-team-model.js";
7
+ import { repositoryIdentity } from "./repository-identity.js";
8
+ class ProjectTeamConflict extends Error {
9
+ status = 409;
10
+ }
11
+ class ProjectTeamStore {
12
+ constructor(directory = join(CODEFLOW_HOME, "project-teams")) {
13
+ this.directory = directory;
14
+ }
15
+ teams = /* @__PURE__ */ new Map();
16
+ queue = Promise.resolve();
17
+ listeners = /* @__PURE__ */ new Set();
18
+ faults = /* @__PURE__ */ new Map();
19
+ loaded = false;
20
+ feature;
21
+ subscribe(fn) {
22
+ this.listeners.add(fn);
23
+ return () => {
24
+ this.listeners.delete(fn);
25
+ };
26
+ }
27
+ notify() {
28
+ for (const listener of this.listeners) {
29
+ try {
30
+ listener();
31
+ } catch {
32
+ }
33
+ }
34
+ }
35
+ publish(team) {
36
+ this.teams.set(team.id, structuredClone(team));
37
+ this.notify();
38
+ }
39
+ async load() {
40
+ const files = await readdir(this.directory).catch((e) => {
41
+ if (e.code === "ENOENT") return [];
42
+ throw e;
43
+ });
44
+ this.teams.clear();
45
+ this.faults.clear();
46
+ this.feature = void 0;
47
+ try {
48
+ const file = await open(join(this.directory, "settings.json"), constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
49
+ let raw;
50
+ try {
51
+ const stat = await file.stat();
52
+ if (!stat.isFile() || stat.size > 4096) throw new Error("Standing teams settings exceed their regular-file bound");
53
+ const buffer = Buffer.alloc(4097);
54
+ const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
55
+ if (bytesRead > 4096) throw new Error("Standing teams settings grew beyond their limit");
56
+ raw = JSON.parse(buffer.subarray(0, bytesRead).toString());
57
+ } finally {
58
+ await file.close();
59
+ }
60
+ if (!raw || raw.version !== 1 || typeof raw.enabled !== "boolean" || !Number.isSafeInteger(raw.revision) || raw.revision < 1 || Object.keys(raw).some((key) => !["version", "enabled", "revision"].includes(key))) throw new Error("Invalid Standing teams setting");
61
+ this.feature = raw;
62
+ } catch (error) {
63
+ if (error.code !== "ENOENT") this.faults.set("settings", error.message);
64
+ }
65
+ for (const name of files.filter((f) => /^pt-[a-f0-9]{24}\.json$/.test(f))) {
66
+ try {
67
+ const file = await open(join(this.directory, name), constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
68
+ let value;
69
+ try {
70
+ const stat = await file.stat();
71
+ if (!stat.isFile() || stat.size > PT_LIMITS.bytes) throw new Error("Team file exceeds its regular-file bound");
72
+ const buffer = Buffer.alloc(PT_LIMITS.bytes + 1);
73
+ const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
74
+ if (bytesRead > PT_LIMITS.bytes) throw new Error("Team file grew beyond its limit");
75
+ value = JSON.parse(buffer.subarray(0, bytesRead).toString());
76
+ } finally {
77
+ await file.close();
78
+ }
79
+ const team = validateTeamDocument(value);
80
+ if (`${team.id}.json` !== name) throw new Error("Team identity differs from filename");
81
+ this.teams.set(team.id, team);
82
+ } catch (error) {
83
+ this.faults.set(name.slice(0, -5), error.message);
84
+ }
85
+ }
86
+ const owners = /* @__PURE__ */ new Map();
87
+ for (const team of this.teams.values()) if (team.status !== "archived") {
88
+ try {
89
+ const identity = await repositoryIdentity(team.repo);
90
+ if (team.repositoryId && team.repositoryId !== identity) throw new Error("Repository metadata was replaced; inspect ownership before dispatch");
91
+ if (!team.repositoryId) {
92
+ team.repositoryId = identity;
93
+ team.revision++;
94
+ team.updatedAt = Date.now();
95
+ await this.save(team);
96
+ }
97
+ owners.set(identity, [...owners.get(identity) ?? [], team.id]);
98
+ } catch (error) {
99
+ this.faults.set(team.id, error.message);
100
+ this.teams.delete(team.id);
101
+ }
102
+ }
103
+ for (const ids of owners.values()) if (ids.length > 1) for (const id of ids) {
104
+ this.faults.set(id, "Multiple active teams claim this repository; resolve ownership before dispatch");
105
+ this.teams.delete(id);
106
+ }
107
+ this.loaded = true;
108
+ }
109
+ configuration(defaultEnabled) {
110
+ if (!this.loaded || this.faults.has("settings") || this.faults.has("storage")) return { enabled: false, revision: 0, source: "unavailable" };
111
+ return { enabled: this.feature?.enabled ?? defaultEnabled, revision: this.feature?.revision ?? 0, source: this.feature ? "saved" : "default" };
112
+ }
113
+ setEnabled(enabled, revision) {
114
+ return this.serialized(async () => {
115
+ if (!this.loaded || this.faults.has("settings") || this.faults.has("storage")) throw new Error("Repair Standing teams settings before changing them");
116
+ if (revision !== (this.feature?.revision ?? 0)) throw new ProjectTeamConflict("Standing teams settings changed. Reload before trying again.");
117
+ const next = { version: 1, enabled, revision: revision + 1 };
118
+ await mkdir(this.directory, { recursive: true, mode: 448 });
119
+ const temporary = join(this.directory, `.settings-${randomBytes(6).toString("hex")}.tmp`);
120
+ try {
121
+ const file = await open(temporary, "wx", 384);
122
+ try {
123
+ await file.writeFile(JSON.stringify(next));
124
+ await file.sync();
125
+ } finally {
126
+ await file.close();
127
+ }
128
+ await rename(temporary, join(this.directory, "settings.json"));
129
+ if (process.platform !== "win32") {
130
+ const directory = await open(this.directory, "r");
131
+ try {
132
+ await directory.sync();
133
+ } finally {
134
+ await directory.close();
135
+ }
136
+ }
137
+ this.feature = next;
138
+ for (const listener of this.listeners) {
139
+ try {
140
+ listener();
141
+ } catch {
142
+ }
143
+ }
144
+ } finally {
145
+ await rm(temporary, { force: true }).catch(() => void 0);
146
+ }
147
+ });
148
+ }
149
+ list() {
150
+ return [...this.teams.values()].map((t) => structuredClone(t));
151
+ }
152
+ get(id) {
153
+ const error = this.faults.get(id);
154
+ if (error) throw new Error(`Standing team storage is unavailable: ${error}`);
155
+ const team = this.teams.get(id);
156
+ if (!team) throw Object.assign(new Error("Project team not found"), { status: 404 });
157
+ return structuredClone(team);
158
+ }
159
+ serialized(fn) {
160
+ const result = this.queue.then(fn);
161
+ this.queue = result.catch(() => void 0);
162
+ return result;
163
+ }
164
+ async flush() {
165
+ await this.queue;
166
+ }
167
+ create(team) {
168
+ return this.serialized(async () => {
169
+ if (!this.loaded) throw new Error("Load project team storage before writing");
170
+ if (this.faults.size) throw new Error("Repair damaged team records before creating teams; repository ownership cannot be determined");
171
+ const existing = this.teams.get(team.id);
172
+ if (existing) {
173
+ if (existing.operations[0]?.hash !== team.operations[0]?.hash) throw new ProjectTeamConflict("Operation ID was reused with different content");
174
+ return structuredClone(existing);
175
+ }
176
+ team = structuredClone(team);
177
+ team.repositoryId = await repositoryIdentity(team.repo);
178
+ if ([...this.teams.values()].some((t) => t.status !== "archived" && (t.repo === team.repo || t.repositoryId === team.repositoryId))) throw new ProjectTeamConflict("This repository already has a standing team (including its linked worktrees)");
179
+ await this.save(team);
180
+ this.publish(team);
181
+ return structuredClone(team);
182
+ });
183
+ }
184
+ change(id, expected, operation, update) {
185
+ return this.serialized(async () => {
186
+ if (!this.loaded) throw new Error("Load project team storage before writing");
187
+ const team = this.get(id);
188
+ const previous = operation && team.operations.find((o) => o.id === operation.id);
189
+ if (previous) {
190
+ if (previous.hash !== operation.hash) throw new ProjectTeamConflict("Operation ID was reused with different content");
191
+ return team;
192
+ }
193
+ if (expected !== void 0 && expected !== team.revision) throw new ProjectTeamConflict("This team changed. Reload before acting.");
194
+ update(team);
195
+ team.revision++;
196
+ team.updatedAt = Date.now();
197
+ if (operation) team.operations.push({ ...operation, at: team.updatedAt });
198
+ await this.save(team);
199
+ this.publish(team);
200
+ return structuredClone(team);
201
+ });
202
+ }
203
+ async save(team) {
204
+ validateTeamDocument(team);
205
+ const data = JSON.stringify(team);
206
+ if (Buffer.byteLength(data) > PT_LIMITS.bytes) throw new Error("Standing team history exceeds its storage limit");
207
+ await mkdir(this.directory, { recursive: true, mode: 448 });
208
+ const temporary = join(this.directory, `.${team.id}-${randomBytes(6).toString("hex")}.tmp`);
209
+ try {
210
+ const handle = await open(temporary, "wx", 384);
211
+ try {
212
+ await handle.writeFile(data);
213
+ await handle.sync();
214
+ } finally {
215
+ await handle.close();
216
+ }
217
+ await rename(temporary, join(this.directory, `${team.id}.json`));
218
+ if (process.platform !== "win32") {
219
+ const dir = await open(this.directory, "r");
220
+ try {
221
+ await dir.sync();
222
+ } finally {
223
+ await dir.close();
224
+ }
225
+ }
226
+ } finally {
227
+ await rm(temporary, { force: true }).catch(() => void 0);
228
+ }
229
+ }
230
+ }
231
+ export {
232
+ ProjectTeamConflict,
233
+ ProjectTeamStore
234
+ };
@@ -0,0 +1,24 @@
1
+ import { digest } from "./project-team-model.js";
2
+ import { recoverRun } from "./run-recovery.js";
3
+ import { flushWrites } from "./store.js";
4
+ function terminalEvidence(run) {
5
+ if (!["done", "failed", "cancelled", "stopped"].includes(run.status) || !Number.isSafeInteger(run.endedAt) || !Number.isSafeInteger(run.createdAt) || run.endedAt < run.createdAt) return;
6
+ if (!Number.isSafeInteger(run.usage.agentSteps) || run.usage.agentSteps < 0) return;
7
+ const turns = run.steps.filter((step) => step.kind === "agent");
8
+ if (turns.some((step) => !step.endedAt || ["running", "interrupted"].includes(step.status))) return;
9
+ const launches = turns.reduce((sum, step) => sum + (step.invocations ?? 1), 0);
10
+ const calls = Math.max(run.usage.agentSteps, run.team?.metrics.invocations ?? 0, launches);
11
+ if (!Number.isSafeInteger(calls) || calls < 0) return;
12
+ return { id: run.id, repo: run.repo, project: run.project, base: run.baseRef, contract: run.projectTeam, status: run.status, createdAt: run.createdAt, endedAt: run.endedAt, calls, turns: turns.map((step) => ({ id: step.id, status: step.status, invocations: step.invocations, endedAt: step.endedAt })) };
13
+ }
14
+ async function persistProjectTeamUsage(run, launch) {
15
+ const evidence = terminalEvidence(run);
16
+ if (!evidence || evidence.createdAt < launch.at || evidence.calls > launch.calls) return;
17
+ await flushWrites(run.id);
18
+ const saved = (await recoverRun(run.id)).run;
19
+ if (!saved || digest(terminalEvidence(saved) ?? null) !== digest(evidence)) return;
20
+ return { version: 1, calls: evidence.calls, durationMs: Math.min(launch.minutes * 6e4, evidence.endedAt - evidence.createdAt), endedAt: evidence.endedAt, recordedAt: Date.now(), digest: digest(evidence) };
21
+ }
22
+ export {
23
+ persistProjectTeamUsage
24
+ };
@@ -0,0 +1,43 @@
1
+ import { HttpError, readJson, send } from "./http.js";
2
+ import { allowance, closed, text } from "./project-team-model.js";
3
+ import { agentAvailability } from "./adapters/index.js";
4
+ import { providerEligibility } from "./provider-eligibility.js";
5
+ async function handleProjectTeams(service, req, res, parts, url) {
6
+ try {
7
+ const id = parts[2];
8
+ if (id === "definitions") {
9
+ if (req.method === "GET" && parts.length === 3) return send(res, 200, { definitions: service.teamLibrary(), faults: Object.fromEntries(service.definitions.faults) });
10
+ if (req.method === "GET" && parts.length === 4) return send(res, 200, service.definitions.get(parts[3]));
11
+ if (req.method === "POST" && (parts.length === 3 || parts.length === 4)) return send(res, parts.length === 3 ? 201 : 200, await service.saveDefinition(await readJson(req, 128 * 1024), parts[3]));
12
+ throw new HttpError(405, "Use GET or POST for reusable agent teams");
13
+ }
14
+ if (id === "settings" && parts.length === 3) {
15
+ if (req.method === "GET") return send(res, 200, service.configuration());
16
+ if (req.method === "POST") return send(res, 200, await service.setEnabled(await readJson(req, 8192)));
17
+ throw new HttpError(405, "Use GET or POST for Standing teams settings");
18
+ }
19
+ if (req.method === "GET" && !id) return send(res, 200, { ...service.configuration(), teams: service.list(url.searchParams.get("project") ?? void 0), definitions: service.teamLibrary(), definitionFaults: Object.fromEntries(service.definitions.faults), faults: Object.fromEntries(service.store.faults), providers: (await agentAvailability()).map(({ id: id2, displayName, available }) => ({ id: id2, name: displayName, available, reason: providerEligibility(id2, { trust: "standard", unattended: true, projectTeam: true }) })) });
20
+ if (req.method === "GET" && id && parts.length === 3) {
21
+ const team = service.store.get(id);
22
+ return send(res, 200, { team, allowance: allowance(team) });
23
+ }
24
+ if (req.method === "GET" && id && parts[3] === "preview" && parts.length === 4) return send(res, 200, await service.preview(id));
25
+ if (req.method !== "POST") throw new HttpError(405, "Use GET or POST for project teams");
26
+ const body = await readJson(req, 128 * 1024);
27
+ if (!id) return send(res, 201, await service.create(body));
28
+ if (parts.length === 3) return send(res, 200, await service.update(id, body));
29
+ if (parts[3] === "cycle" && parts.length === 4) {
30
+ closed(body, ["operationId"], "cycle request");
31
+ return send(res, 200, await service.cycle(id, text(body.operationId, 150, "operation ID")));
32
+ }
33
+ if (parts[3] === "stop" && parts.length === 4) return send(res, 200, await service.stopCycle(id, body));
34
+ if (parts[3] === "tasks" && parts.length === 4) return send(res, 201, await service.assignTask(id, body));
35
+ if (parts[3] === "opportunities" && parts[4] && parts.length === 5) return send(res, 200, await service.decide(id, parts[4], body));
36
+ throw new HttpError(404, "Unknown project team operation");
37
+ } catch (error) {
38
+ throw new HttpError(error.status ?? 400, error.message);
39
+ }
40
+ }
41
+ export {
42
+ handleProjectTeams
43
+ };