@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,328 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { startJsonlProcess, commandExists, describeFailure, processErrorKind, stringify, truncate } from "./process.js";
5
+ const object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
6
+ const string = (value) => typeof value === "string" && value.length > 0 && value.length <= 4e3;
7
+ class AcpError extends Error {
8
+ constructor(message, kind = "cli_incompatible", code) {
9
+ super(message);
10
+ this.kind = kind;
11
+ this.code = code;
12
+ }
13
+ }
14
+ class AcpClient {
15
+ constructor(connection, opts, onUpdate) {
16
+ this.connection = connection;
17
+ this.opts = opts;
18
+ this.onUpdate = onUpdate;
19
+ this.proc = startJsonlProcess({
20
+ command: connection.command,
21
+ args: connection.args,
22
+ cwd: opts.cwd,
23
+ interactive: true,
24
+ timeoutMs: opts.timeoutMs,
25
+ idleTimeoutMs: opts.idleTimeoutMs,
26
+ resultGraceMs: 1e3,
27
+ onJson: (message) => {
28
+ try {
29
+ this.receive(message);
30
+ } catch (error) {
31
+ this.fail(error);
32
+ this.proc.kill();
33
+ }
34
+ },
35
+ onText: () => {
36
+ this.fail(new AcpError("The CLI wrote invalid ACP data to stdout. Configure its ACP subcommand; logs must go to stderr."));
37
+ this.proc.kill();
38
+ }
39
+ });
40
+ void this.proc.outcome.then((outcome) => {
41
+ this.fail(new AcpError(describeFailure(connection.name, outcome), processErrorKind(outcome) ?? "other"));
42
+ });
43
+ opts.signal?.addEventListener("abort", this.cancel, { once: true });
44
+ if (opts.signal?.aborted) this.cancel();
45
+ }
46
+ nextId = 0;
47
+ pending = /* @__PURE__ */ new Map();
48
+ stopped;
49
+ lifetime = new AbortController();
50
+ cancelTimer;
51
+ approvalCount = 0;
52
+ permissionIds = /* @__PURE__ */ new Set();
53
+ permissionTasks = /* @__PURE__ */ new Set();
54
+ toolDetails = /* @__PURE__ */ new Map();
55
+ sessionId;
56
+ prompting = false;
57
+ proc;
58
+ fail(error) {
59
+ this.stopped ??= error;
60
+ this.lifetime.abort();
61
+ for (const waiter of this.pending.values()) {
62
+ clearTimeout(waiter.timer);
63
+ waiter.reject(this.stopped);
64
+ }
65
+ this.pending.clear();
66
+ }
67
+ cancel = () => {
68
+ if (this.sessionId) this.proc.send({ jsonrpc: "2.0", method: "session/cancel", params: { sessionId: this.sessionId } });
69
+ this.fail(new AcpError("ACP turn was cancelled", "cancelled"));
70
+ this.cancelTimer = setTimeout(() => this.proc.kill(), 1e3);
71
+ this.cancelTimer.unref();
72
+ };
73
+ request(method, params, timeoutMs = 2e4) {
74
+ if (this.stopped) return Promise.reject(this.stopped);
75
+ return new Promise((resolve, reject) => {
76
+ const id = ++this.nextId;
77
+ const timer = timeoutMs ? setTimeout(() => {
78
+ this.pending.delete(id);
79
+ reject(new AcpError(`ACP ${method} did not respond in time`, "timeout"));
80
+ }, timeoutMs) : void 0;
81
+ timer?.unref();
82
+ this.pending.set(id, { resolve, reject, timer });
83
+ if (!this.proc.send({ jsonrpc: "2.0", id, method, params })) {
84
+ clearTimeout(timer);
85
+ this.pending.delete(id);
86
+ reject(new AcpError("ACP process closed its input", "other"));
87
+ }
88
+ });
89
+ }
90
+ receive(message) {
91
+ if (!object(message) || message.jsonrpc !== "2.0") throw new AcpError("The CLI did not return ACP JSON-RPC 2.0 messages");
92
+ if (message.method === void 0) {
93
+ const waiter = this.pending.get(message.id);
94
+ if (!waiter) return;
95
+ this.pending.delete(message.id);
96
+ clearTimeout(waiter.timer);
97
+ if (object(message.error)) waiter.reject(new AcpError(truncate(String(message.error.message ?? "ACP request failed")), message.error.code === -32e3 ? "auth" : message.error.code === -32002 ? "session_not_found" : [-32601, -32602].includes(message.error.code) ? "cli_incompatible" : "other", message.error.code));
98
+ else if (object(message.result)) waiter.resolve(message.result);
99
+ else waiter.reject(new AcpError("ACP response is missing its result"));
100
+ return;
101
+ }
102
+ if (message.id !== void 0) {
103
+ if (!(typeof message.id === "number" || typeof message.id === "string")) throw new AcpError("Invalid ACP request ID");
104
+ if (message.method === "session/request_permission") {
105
+ if (this.permissionIds.has(message.id) || this.permissionIds.size >= 16) throw new AcpError("Too many or duplicate ACP permission requests");
106
+ this.permissionIds.add(message.id);
107
+ const task = this.permission(message).finally(() => {
108
+ this.permissionIds.delete(message.id);
109
+ this.permissionTasks.delete(task);
110
+ });
111
+ this.permissionTasks.add(task);
112
+ } else this.proc.send({ jsonrpc: "2.0", id: message.id, error: { code: -32601, message: "Client capability is not supported" } });
113
+ } else if (message.method === "session/update" && this.prompting && message.params?.sessionId === this.sessionId && object(message.params.update)) {
114
+ const update = message.params.update;
115
+ if (["tool_call", "tool_call_update"].includes(update.sessionUpdate) && string(update.toolCallId)) {
116
+ if (!this.toolDetails.has(update.toolCallId) && this.toolDetails.size >= 2e3) throw new AcpError("ACP turn exceeds the tool call limit");
117
+ const previous = this.toolDetails.get(update.toolCallId) ?? {};
118
+ this.toolDetails.set(update.toolCallId, {
119
+ ...previous,
120
+ toolCallId: update.toolCallId,
121
+ ...typeof update.kind === "string" ? { kind: update.kind } : {},
122
+ ...typeof update.title === "string" ? { title: truncate(update.title, 1e3) } : {},
123
+ ...Array.isArray(update.locations) ? { locations: update.locations.slice(0, 50).filter((item) => string(item?.path)).map((item) => ({ path: item.path })) } : {},
124
+ ...update.rawInput !== void 0 ? { rawInput: { description: truncate(stringify(update.rawInput)), ...typeof update.rawInput?.command === "string" ? { command: truncate(update.rawInput.command) } : {}, ...string(update.rawInput?.path) ? { path: update.rawInput.path } : {} } } : {}
125
+ });
126
+ }
127
+ this.onUpdate(update);
128
+ }
129
+ }
130
+ async permission(message) {
131
+ const params = message.params;
132
+ const tool = object(params?.toolCall) ? { ...this.toolDetails.get(params.toolCall.toolCallId), ...params.toolCall } : void 0;
133
+ let optionId;
134
+ const options = Array.isArray(params?.options) ? params.options : [];
135
+ const once = (kind) => options.find((option) => option?.kind === kind && string(option.optionId))?.optionId;
136
+ const eligible = this.prompting && params?.sessionId === this.sessionId && object(tool) && string(tool.toolCallId);
137
+ const canAsk = eligible && (this.opts.mode === "write" || ["read", "search", "think"].includes(tool.kind));
138
+ const controller = new AbortController();
139
+ let waiting = false;
140
+ const abort = () => controller.abort();
141
+ this.lifetime.signal.addEventListener("abort", abort, { once: true });
142
+ if (this.lifetime.signal.aborted) controller.abort();
143
+ const timer = setTimeout(abort, 12e4);
144
+ timer.unref();
145
+ try {
146
+ if (canAsk && once("allow_once") && this.opts.interactiveApprovals && this.opts.onApproval && !controller.signal.aborted) {
147
+ waiting = true;
148
+ this.approvalCount++;
149
+ this.proc.pauseIdle(true);
150
+ const paths = Array.isArray(tool.locations) ? tool.locations.slice(0, 50).map((location) => location?.path).filter(string) : [];
151
+ if (!paths.length && tool.kind === "edit" && string(tool.rawInput?.path)) paths.push(tool.rawInput.path);
152
+ const request = {
153
+ id: `acp:${String(message.id)}`,
154
+ kind: ["edit", "delete", "move"].includes(tool.kind) ? "file-change" : "command",
155
+ paths,
156
+ cwd: this.opts.cwd,
157
+ command: typeof tool.rawInput?.command === "string" ? truncate(tool.rawInput.command) : truncate(stringify(tool.rawInput ?? tool.title ?? tool.kind)),
158
+ reason: truncate(String(tool.title ?? "ACP tool request"))
159
+ };
160
+ const decision = await Promise.race([
161
+ this.opts.onApproval(request, controller.signal),
162
+ new Promise((resolve) => {
163
+ if (controller.signal.aborted) resolve("decline");
164
+ else controller.signal.addEventListener("abort", () => resolve("decline"), { once: true });
165
+ })
166
+ ]);
167
+ if (decision === "approve" && !controller.signal.aborted) optionId = once("allow_once");
168
+ }
169
+ } catch {
170
+ } finally {
171
+ clearTimeout(timer);
172
+ this.lifetime.signal.removeEventListener("abort", abort);
173
+ controller.abort();
174
+ if (waiting) {
175
+ this.approvalCount--;
176
+ if (!this.approvalCount) this.proc.pauseIdle(false);
177
+ }
178
+ optionId ??= once("reject_once");
179
+ this.proc.send({ jsonrpc: "2.0", id: message.id, result: { outcome: this.lifetime.signal.aborted || !optionId ? { outcome: "cancelled" } : { outcome: "selected", optionId } } });
180
+ }
181
+ }
182
+ async initialize() {
183
+ const init = await this.request("initialize", { protocolVersion: 1, clientCapabilities: {}, clientInfo: { name: "code-flow", version: "0.1.0" } });
184
+ if (init.protocolVersion !== 1) throw new AcpError(`Unsupported ACP protocol version: ${String(init.protocolVersion)}; code-flow supports v1`);
185
+ return init;
186
+ }
187
+ async session(init) {
188
+ const params = { cwd: this.opts.cwd, mcpServers: [] };
189
+ let session;
190
+ if (this.opts.resumeSessionId) {
191
+ if (init.agentCapabilities?.loadSession !== true) throw new AcpError("This ACP agent cannot load previous sessions", "session_not_found");
192
+ session = await this.request("session/load", { ...params, sessionId: this.opts.resumeSessionId });
193
+ this.sessionId = this.opts.resumeSessionId;
194
+ } else {
195
+ session = await this.request("session/new", params);
196
+ if (!string(session.sessionId)) throw new AcpError("ACP session/new returned no valid session ID");
197
+ this.sessionId = session.sessionId;
198
+ }
199
+ return session;
200
+ }
201
+ async close() {
202
+ this.opts.signal?.removeEventListener("abort", this.cancel);
203
+ clearTimeout(this.cancelTimer);
204
+ this.lifetime.abort();
205
+ await Promise.allSettled([...this.permissionTasks]);
206
+ this.proc.resultSeen();
207
+ this.proc.endInput();
208
+ await this.proc.outcome;
209
+ }
210
+ }
211
+ function configOption(session, category) {
212
+ const options = Array.isArray(session?.configOptions) ? session.configOptions.filter((option) => option?.type === "select" && option.category === category && string(option.id)) : [];
213
+ return options.find((option) => option.id === category) ?? options.find((option) => category !== "model" || option.id !== "provider");
214
+ }
215
+ function choices(option) {
216
+ const flat = Array.isArray(option?.options) ? option.options.flatMap((item) => Array.isArray(item?.options) ? item.options : [item]) : [];
217
+ return flat.filter((item) => string(item?.value) && string(item?.name)).slice(0, 1e3).map((item) => ({ id: item.value, label: item.name }));
218
+ }
219
+ function acpCatalog(session) {
220
+ const model = configOption(session, "model"), effort = configOption(session, "thought_level");
221
+ const models = model ? choices(model) : Array.isArray(session?.models?.availableModels) ? session.models.availableModels.filter((item) => string(item?.modelId) && string(item?.name)).slice(0, 1e3).map((item) => ({ id: item.modelId, label: item.name })) : [];
222
+ const defaultModel = model?.currentValue ?? session?.models?.currentModelId;
223
+ return {
224
+ models,
225
+ efforts: choices(effort).map((item) => item.id),
226
+ allowCustom: false,
227
+ defaultModel: string(defaultModel) ? defaultModel : void 0,
228
+ defaultEffort: string(effort?.currentValue) ? effort.currentValue : void 0
229
+ };
230
+ }
231
+ async function probeAcp(connection) {
232
+ const cwd = await mkdtemp(join(tmpdir(), "codeflow-acp-probe-"));
233
+ let client;
234
+ try {
235
+ client = new AcpClient(connection, { cwd, prompt: "", mode: "read", timeoutMs: 3e4 }, () => {
236
+ });
237
+ const init = await client.initialize(), session = await client.session(init);
238
+ return {
239
+ protocolVersion: 1,
240
+ agent: string(init.agentInfo?.title) ? init.agentInfo.title : string(init.agentInfo?.name) ? init.agentInfo.name : connection.name,
241
+ version: string(init.agentInfo?.version) ? init.agentInfo.version : void 0,
242
+ sessionResume: init.agentCapabilities?.loadSession === true,
243
+ catalog: acpCatalog(session)
244
+ };
245
+ } finally {
246
+ await client?.close();
247
+ await rm(cwd, { recursive: true, force: true });
248
+ }
249
+ }
250
+ function createAcpAdapter(connection) {
251
+ const saved = structuredClone(connection);
252
+ return {
253
+ id: saved.id,
254
+ displayName: saved.name,
255
+ binary: saved.command,
256
+ available: () => commandExists(saved.command),
257
+ catalog: async () => (await probeAcp(saved)).catalog,
258
+ run: (opts, event) => runAcp(saved, opts, event)
259
+ };
260
+ }
261
+ async function runAcp(connection, opts, event) {
262
+ if (opts.signal?.aborted) return { ok: false, finalText: "", error: "ACP turn was cancelled", errorKind: "cancelled" };
263
+ if (opts.agentContainer || opts.trust !== "full") return { ok: false, finalText: "", error: "ACP connections require Full access on the current host; sandbox and container profiles are not supported", errorKind: "cli_incompatible" };
264
+ let finalText = "", client;
265
+ const calls = /* @__PURE__ */ new Map(), finished = /* @__PURE__ */ new Set();
266
+ try {
267
+ client = new AcpClient(connection, opts, (update) => {
268
+ if (update.sessionUpdate === "agent_message_chunk" || update.sessionUpdate === "agent_thought_chunk") {
269
+ if (update.content?.type !== "text" || typeof update.content.text !== "string") return;
270
+ if (update.sessionUpdate === "agent_message_chunk") {
271
+ if (finalText.length + update.content.text.length > 4 * 1024 * 1024) throw new AcpError("ACP answer exceeds the 4 MiB limit");
272
+ finalText += update.content.text;
273
+ event({ kind: "text", text: update.content.text });
274
+ } else event({ kind: "thinking", text: update.content.text });
275
+ } else if (["tool_call", "tool_call_update"].includes(update.sessionUpdate) && string(update.toolCallId)) {
276
+ const previous = calls.get(update.toolCallId);
277
+ if (!previous && calls.size >= 2e3) throw new AcpError("ACP turn exceeds the tool call limit");
278
+ const name = String(update.title ?? previous?.name ?? update.kind ?? "Tool");
279
+ const input = update.rawInput !== void 0 ? truncate(stringify(update.rawInput), 16e3) : previous?.input ?? "{}";
280
+ if (!previous || previous.name !== name || previous.input !== input) {
281
+ event({ kind: "tool_call", callId: update.toolCallId, name, input });
282
+ }
283
+ const text = Array.isArray(update.content) ? update.content.map((item) => item.type === "content" && item.content?.type === "text" ? item.content.text : item.type === "diff" ? `${item.path}
284
+ ${item.newText ?? ""}` : "").join("\n") : void 0;
285
+ const output = truncate(text ?? (update.rawOutput !== void 0 ? stringify(update.rawOutput) : previous?.output ?? ""), 32e3);
286
+ calls.set(update.toolCallId, { name, input, output });
287
+ if (["completed", "failed"].includes(update.status) && !finished.has(update.toolCallId)) {
288
+ finished.add(update.toolCallId);
289
+ event({ kind: "tool_result", callId: update.toolCallId, output, isError: update.status === "failed" });
290
+ }
291
+ }
292
+ });
293
+ const init = await client.initialize();
294
+ let session = await client.session(init);
295
+ event({ kind: "session", sessionId: client.sessionId });
296
+ for (const [category, value] of [["model", opts.model], ["thought_level", opts.effort]]) {
297
+ if (!value) continue;
298
+ const option = configOption(session, category);
299
+ if (option) {
300
+ if (!choices(option).some((item) => item.id === value)) throw new AcpError(`ACP agent does not advertise ${category} ${value}`);
301
+ session = { ...session, ...await client.request("session/set_config_option", { sessionId: client.sessionId, configId: option.id, value }) };
302
+ } else if (category === "model" && acpCatalog(session).models.some((model) => model.id === value)) {
303
+ await client.request("session/set_model", { sessionId: client.sessionId, modelId: value });
304
+ } else throw new AcpError(`This ACP agent does not support selecting ${category}`);
305
+ }
306
+ const prompt = `${opts.mode === "read" ? "This is a read-only task. Do not edit files or run commands that change the repository.\n\n" : ""}${opts.prompt}${opts.jsonSchema ? `
307
+
308
+ Return your final answer as JSON matching this schema:
309
+ ${JSON.stringify(opts.jsonSchema)}` : ""}`;
310
+ client.prompting = true;
311
+ const result = await client.request("session/prompt", { sessionId: client.sessionId, prompt: [{ type: "text", text: prompt }] }, 0);
312
+ client.prompting = false;
313
+ if (opts.signal?.aborted || result.stopReason === "cancelled") throw new AcpError("ACP turn was cancelled", "cancelled");
314
+ if (result.stopReason !== "end_turn") throw new AcpError(`ACP turn stopped: ${String(result.stopReason ?? "missing stop reason")}`, "other");
315
+ return { ok: true, finalText, sessionId: client.sessionId };
316
+ } catch (error) {
317
+ const failure = error;
318
+ return { ok: false, finalText, sessionId: client?.sessionId, error: failure.message, errorKind: opts.signal?.aborted ? "cancelled" : error instanceof AcpError ? error.kind : "other" };
319
+ } finally {
320
+ await client?.close();
321
+ }
322
+ }
323
+ export {
324
+ acpCatalog,
325
+ createAcpAdapter,
326
+ probeAcp,
327
+ runAcp
328
+ };
@@ -0,0 +1,67 @@
1
+ import { constants } from "node:fs";
2
+ import { open } from "node:fs/promises";
3
+ function agyCredentialFailure(text) {
4
+ return /(?:token refresh failed|failed to refresh loaded token|failed to (?:save|persist|marshal) (?:refreshed (?:OAuth )?token|credentials)|failed to (?:save|persist) token to keyring|failed to load (?:stored )?token from keyring, falling back to file|stored credentials are invalid or expired)/i.test(text);
5
+ }
6
+ const AGY_AUTH_LOG_LIMIT = 4 * 1024 * 1024;
7
+ function observeAgyAuthLine(observation, line) {
8
+ const message = line.match(/\b(?:auth|auth_provider|server_oauth|credentials|keyring)\.go:\d+\]\s+(.*)$/)?.[1];
9
+ if (!message) return;
10
+ if (/token refreshed, new expiry=|Auto-saving refreshed token from token source callback/.test(message)) observation.refreshObserved = true;
11
+ if (agyCredentialFailure(message)) observation.credentialFailure = true;
12
+ if (/OAuth: starting OAuth flow/.test(message)) observation.interactiveAuthObserved = true;
13
+ const expired = message.match(/keyringAuth: loaded token, [^\r\n]*\bexpired=(true|false)\b/)?.[1];
14
+ if (expired) observation.loadedTokenExpired = observation.loadedTokenExpired === true || expired === "true";
15
+ }
16
+ async function inspectAgyAuthLog(path) {
17
+ const result = { log: "unreadable", refreshObserved: false, credentialFailure: false, interactiveAuthObserved: false, loadedTokenExpired: null };
18
+ let file;
19
+ try {
20
+ file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
21
+ const stat = await file.stat();
22
+ if (!stat.isFile() || stat.nlink !== 1) return result;
23
+ if (stat.size > AGY_AUTH_LOG_LIMIT) {
24
+ result.log = "too-large";
25
+ return result;
26
+ }
27
+ const buffer = Buffer.alloc(16 * 1024);
28
+ let consumed = 0, pending = "", dropped = false;
29
+ while (consumed <= AGY_AUTH_LOG_LIMIT) {
30
+ const { bytesRead } = await file.read(buffer, 0, Math.min(buffer.length, AGY_AUTH_LOG_LIMIT + 1 - consumed), consumed);
31
+ if (!bytesRead) break;
32
+ consumed += bytesRead;
33
+ if (consumed > AGY_AUTH_LOG_LIMIT) {
34
+ result.log = "too-large";
35
+ return result;
36
+ }
37
+ for (const part of buffer.subarray(0, bytesRead).toString("latin1").split(/(?<=\n)/)) {
38
+ if (!dropped) {
39
+ pending += part;
40
+ if (pending.length > 8192) {
41
+ pending = "";
42
+ dropped = true;
43
+ }
44
+ }
45
+ if (part.endsWith("\n")) {
46
+ if (!dropped) observeAgyAuthLine(result, pending.trimEnd());
47
+ pending = "";
48
+ dropped = false;
49
+ }
50
+ }
51
+ }
52
+ if (pending && !dropped) observeAgyAuthLine(result, pending);
53
+ result.log = "read";
54
+ return result;
55
+ } catch (error) {
56
+ result.log = error.code === "ENOENT" ? "missing" : "unreadable";
57
+ return result;
58
+ } finally {
59
+ await file?.close();
60
+ }
61
+ }
62
+ export {
63
+ AGY_AUTH_LOG_LIMIT,
64
+ agyCredentialFailure,
65
+ inspectAgyAuthLog,
66
+ observeAgyAuthLine
67
+ };
@@ -0,0 +1,223 @@
1
+ import { execFile } from "node:child_process";
2
+ import { constants } from "node:fs";
3
+ import { access, lstat, mkdir, mkdtemp, realpath, rm, symlink, writeFile } from "node:fs/promises";
4
+ import { tmpdir, userInfo } from "node:os";
5
+ import { delimiter, isAbsolute, join, resolve, sep } from "node:path";
6
+ import { promisify } from "node:util";
7
+ import { agyCredentialFailure, inspectAgyAuthLog } from "./agy-auth.js";
8
+ import { startJsonlProcess } from "./process.js";
9
+ const exec = promisify(execFile);
10
+ const AGY_ISOLATION_VERSION = "macos-file-only-v1";
11
+ const AGY_VERIFIED_CLI_VERSION = "1.4.4";
12
+ const AGY_FILE_ONLY_LIMITATION = "Antigravity is limited to file tools in a fresh macOS sandbox. Terminal, browser, MCP, hooks and native delegation are disabled; Code Flow runs checks separately. Native sessions are not resumed.";
13
+ const within = (path, parent) => path === parent || path.startsWith(parent + sep);
14
+ function validPath(path) {
15
+ if (!isAbsolute(path) || path === "/" || resolve(path) !== path || /[\x00-\x1f\x7f]/.test(path)) throw new Error("Invalid Antigravity isolation path");
16
+ }
17
+ async function inspectAgyIsolationRuntime() {
18
+ if (process.platform !== "darwin") throw new Error("Isolated Antigravity requires macOS; remote and container execution are unsupported");
19
+ await access("/usr/bin/sandbox-exec", constants.X_OK);
20
+ let binary;
21
+ for (const dir of (process.env.PATH ?? "").split(delimiter)) {
22
+ if (!isAbsolute(dir)) continue;
23
+ const candidate = join(dir, "agy");
24
+ try {
25
+ await access(candidate, constants.X_OK);
26
+ binary = await realpath(candidate);
27
+ break;
28
+ } catch {
29
+ }
30
+ }
31
+ if (!binary) throw new Error("Antigravity CLI is not installed");
32
+ await verifyAgyVersion(binary);
33
+ const keychains = await realpath(join(userInfo().homedir, "Library", "Keychains"));
34
+ const cache = await exec("/usr/bin/getconf", ["DARWIN_USER_CACHE_DIR"], { timeout: 5e3, maxBuffer: 4096, env: { PATH: "/usr/bin:/bin" } });
35
+ const securityCache = await realpath(join(cache.stdout.trim(), "mds"));
36
+ const securityLock = join(securityCache, "mds.lock");
37
+ const lock = await lstat(securityLock);
38
+ if (!lock.isFile() || lock.isSymbolicLink() || lock.nlink !== 1) throw new Error("The macOS security cache lock is unavailable or unsafe");
39
+ for (const path of [binary, keychains, securityCache, securityLock]) validPath(path);
40
+ return { binary, keychains, securityCache, securityLock };
41
+ }
42
+ async function isolatedOutput(command, args, cwd, env, timeoutMs, maxBytes) {
43
+ let output = "", overflow = false;
44
+ const append = (line) => {
45
+ if (overflow) return;
46
+ if (Buffer.byteLength(output) + Buffer.byteLength(line) + 1 > maxBytes) {
47
+ overflow = true;
48
+ proc.kill();
49
+ return;
50
+ }
51
+ output += line + "\n";
52
+ };
53
+ const proc = startJsonlProcess({ command, args, cwd, env, timeoutMs, onText: append, onJson: (value) => append(JSON.stringify(value)) });
54
+ const outcome = await proc.outcome;
55
+ if (agyCredentialFailure(outcome.stderr + "\n" + output) || overflow || outcome.exitCode !== 0 || outcome.aborted || outcome.timedOut || outcome.stalled) throw new Error("Isolated Antigravity probe failed");
56
+ return output;
57
+ }
58
+ async function verifyAgyVersion(binary) {
59
+ validPath(binary);
60
+ const root = await realpath(await mkdtemp(join(tmpdir(), "codeflow-agy-version-")));
61
+ try {
62
+ const profile = join(root, "version.sb");
63
+ await writeFile(profile, `(version 1)
64
+ (deny default)
65
+ (allow sysctl-read file-read-metadata)
66
+ (allow process-exec (literal ${JSON.stringify(binary)}))
67
+ (allow file-read* file-map-executable (literal "/") (subpath "/System") (subpath "/usr") (subpath "/bin") (subpath "/Library/Apple") (subpath "/private/etc") (subpath "/dev") (literal ${JSON.stringify(binary)}))
68
+ (allow file-write* (literal "/dev/null"))
69
+ `, { mode: 256 });
70
+ const version = await isolatedOutput(
71
+ "/usr/bin/sandbox-exec",
72
+ ["-f", profile, binary, "--version"],
73
+ root,
74
+ { PATH: "/usr/bin:/bin", HOME: root, TMPDIR: root, AGY_CLI_DISABLE_AUTO_UPDATE: "true" },
75
+ 5e3,
76
+ 4096
77
+ );
78
+ if (version.trim() !== AGY_VERIFIED_CLI_VERSION) throw new Error(`Isolated Antigravity requires verified CLI ${AGY_VERIFIED_CLI_VERSION}`);
79
+ } finally {
80
+ await rm(root, { recursive: true, force: true });
81
+ }
82
+ }
83
+ async function agyIsolationProblem() {
84
+ try {
85
+ await inspectAgyIsolationRuntime();
86
+ } catch {
87
+ return `Isolated Antigravity requires macOS, CLI ${AGY_VERIFIED_CLI_VERSION}, and an existing native Keychain login/security cache. No permissive fallback is available.`;
88
+ }
89
+ }
90
+ async function discoverIsolatedAgyModels() {
91
+ const workspace = await mkdtemp(join(tmpdir(), "codeflow-agy-catalog-"));
92
+ let isolated;
93
+ try {
94
+ isolated = await prepareAgyIsolation(workspace, "read", await inspectAgyIsolationRuntime());
95
+ const logPath = join(isolated.paths.scratch, "native-auth.log");
96
+ const stdout = await isolatedOutput(isolated.command, [...isolated.args, "--log-file", logPath, "models"], isolated.paths.workspace, isolated.env, 2e4, 128 * 1024);
97
+ const auth = await inspectAgyAuthLog(logPath);
98
+ if (auth.credentialFailure || auth.interactiveAuthObserved) throw new Error("Native authentication could not be maintained");
99
+ const models = stdout.split("\n").map((line) => line.split(" ")).filter((cols) => cols.length >= 2 && cols[0].trim()).map(([id, label]) => ({ id: id.trim(), label: label.trim() }));
100
+ if (!models.length) throw new Error("No model candidates");
101
+ return { models, efforts: [], allowCustom: false, discovery: { source: `agy models (${AGY_ISOLATION_VERSION}/1.4.4)`, confidence: "reported", verifiedAt: Date.now() } };
102
+ } catch {
103
+ throw new Error("Isolated Antigravity model discovery failed; check native login and runtime compatibility");
104
+ } finally {
105
+ await isolated?.cleanup();
106
+ await rm(workspace, { recursive: true, force: true });
107
+ }
108
+ }
109
+ function agyIsolationProfile(paths, mode) {
110
+ for (const path of Object.values(paths)) validPath(path);
111
+ const roots = [paths.workspace, paths.home, paths.scratch, paths.keychains, paths.securityCache];
112
+ for (let i = 0; i < roots.length; i++) for (let j = i + 1; j < roots.length; j++) {
113
+ if (within(roots[i], roots[j]) || within(roots[j], roots[i])) throw new Error("Antigravity isolation roots must not overlap");
114
+ }
115
+ if (paths.securityLock !== join(paths.securityCache, "mds.lock") || within(paths.binary, paths.workspace)) throw new Error("Unsafe Antigravity executable or security lock path");
116
+ const q = JSON.stringify;
117
+ const cli = join(paths.home, ".gemini", "antigravity-cli");
118
+ const protectedFiles = [paths.home, join(paths.home, ".gemini"), cli, join(paths.home, "Library"), join(paths.home, "Library", "Keychains"), join(cli, "settings.json"), join(cli, "mcp_config.json")];
119
+ return `(version 1)
120
+ (deny default)
121
+ (allow process-fork signal sysctl-read mach-lookup file-read-metadata)
122
+ (allow process-exec (literal ${q(paths.binary)}) (literal "/usr/bin/security"))
123
+ (allow file-read* file-map-executable
124
+ (literal "/") (subpath "/System") (subpath "/usr") (subpath "/bin") (subpath "/sbin")
125
+ (subpath "/Library/Apple") (subpath "/private/etc") (subpath "/dev")
126
+ (literal ${q(paths.binary)}) (subpath ${q(paths.workspace)}) (subpath ${q(paths.home)}) (subpath ${q(paths.scratch)})
127
+ (subpath ${q(paths.keychains)}) (subpath ${q(paths.securityCache)}) (subpath "/private/var/db/mds"))
128
+ (allow file-write* (subpath ${q(paths.home)}) (subpath ${q(paths.scratch)}) (literal "/dev/null")${mode === "write" ? ` (subpath ${q(paths.workspace)})` : ""})
129
+ (allow file-write-data (literal ${q(paths.securityLock)}))
130
+ (deny file-write* ${protectedFiles.map((p) => `(literal ${q(p)})`).join(" ")}
131
+ (subpath ${q(join(paths.home, ".gemini", "config"))}))
132
+ (deny file-read* file-write* (require-all (subpath ${q(paths.workspace)}) (regex #"/[.](agents|gemini)(/|$)")))
133
+ (deny file-write* (require-all (subpath ${q(paths.workspace)}) (regex #"/[.]git(/|$)")))
134
+ ; Inference and native authentication need networking. Model command/browser/MCP tools
135
+ ; are removed and denied in protected provider policy; this is not whole-process offline mode.
136
+ (allow network*)
137
+ `;
138
+ }
139
+ function agyIsolatedSettings(paths, mode) {
140
+ return {
141
+ enableTerminalSandbox: true,
142
+ toolPermission: "request-review",
143
+ allowNonWorkspaceAccess: false,
144
+ permissions: {
145
+ allow: [`read_file(${paths.workspace})`, ...mode === "write" ? [`write_file(${paths.workspace})`] : []],
146
+ ask: [],
147
+ deny: [
148
+ "unsandboxed(*)",
149
+ "command(*)",
150
+ "mcp(*)",
151
+ "read_url(*)",
152
+ "execute_url(*)",
153
+ ...[paths.home, paths.scratch, paths.keychains, paths.securityCache, "/private/var/db/mds"].map((p) => `read_file(${p})`),
154
+ ...mode === "read" ? ["write_file(*)"] : []
155
+ ]
156
+ }
157
+ };
158
+ }
159
+ async function prepareAgyIsolation(workspace, mode, runtime) {
160
+ const root = await realpath(await mkdtemp(join(tmpdir(), "codeflow-agy-managed-")));
161
+ const cleanup = () => rm(root, { recursive: true, force: true });
162
+ try {
163
+ const home = join(root, "home"), scratch = join(root, "scratch");
164
+ const paths = { ...runtime, workspace: await realpath(workspace), home, scratch };
165
+ const profile = agyIsolationProfile(paths, mode);
166
+ const cli = join(home, ".gemini", "antigravity-cli"), config = join(home, ".gemini", "config");
167
+ for (const path of [scratch, cli, join(config, "agents"), join(config, "projects"), join(home, "Library")]) await mkdir(path, { recursive: true, mode: 448 });
168
+ await writeFile(join(config, "projects", "default-cli-project.json"), JSON.stringify({ id: "default-cli-project", name: "CLI Project", projectResources: {} }), { mode: 256 });
169
+ await writeFile(join(cli, "settings.json"), JSON.stringify(agyIsolatedSettings(paths, mode)), { mode: 256 });
170
+ await writeFile(join(config, "hooks.json"), "{}", { mode: 256 });
171
+ for (const path of [join(config, "mcp_config.json"), join(cli, "mcp_config.json")]) await writeFile(path, '{"mcpServers":{}}', { mode: 256 });
172
+ await writeFile(join(config, "agents", "codeflow-isolated.md"), `---
173
+ name: codeflow-isolated
174
+ description: Code Flow isolated file worker
175
+ tools:
176
+ - view_file
177
+ - list_dir
178
+ ${mode === "write" ? " - write_to_file\n - replace_file_content\n" : ""}mainAgent: true
179
+ subagent: false
180
+ commandExecutionPolicy: off
181
+ mcpServers: []
182
+ skills: []
183
+ plugins: []
184
+ ---
185
+ Use the available file tools for this task. Code Flow runs checks separately. Report limitations when a task needs unavailable tools. Never attempt to bypass a denied tool.
186
+ `, { mode: 256 });
187
+ await symlink(runtime.keychains, join(home, "Library", "Keychains"));
188
+ const profilePath = join(root, "worker.sb");
189
+ await writeFile(profilePath, profile, { mode: 256 });
190
+ const env = {
191
+ HOME: home,
192
+ TMPDIR: scratch,
193
+ PATH: "/usr/bin:/bin:/usr/sbin:/sbin",
194
+ LANG: "en_US.UTF-8",
195
+ AGY_CLI_DISABLE_AUTO_UPDATE: "true",
196
+ GIT_CONFIG_GLOBAL: "/dev/null",
197
+ GIT_CONFIG_SYSTEM: "/dev/null",
198
+ GIT_TERMINAL_PROMPT: "0"
199
+ };
200
+ return { root, paths, env, command: "/usr/bin/sandbox-exec", args: ["-f", profilePath, runtime.binary], cleanup };
201
+ } catch (error) {
202
+ await cleanup();
203
+ throw error;
204
+ }
205
+ }
206
+ function agyManagedOptionsProblem(opts) {
207
+ const p = opts.permissions;
208
+ if (!p || p.provider !== "agy" || p.mode !== opts.mode || p.trust !== (opts.trust ?? "standard") || p.nativeDelegation !== false || p.isolation !== "os-sandbox" || p.implementation !== AGY_ISOLATION_VERSION || p.commandNetwork !== "denied" || p.externalTools !== "disabled") return "Antigravity options do not match the isolated run permission policy";
209
+ if (opts.agentContainer || opts.interactiveApprovals || p.toolApprovals) return "Isolated Antigravity does not support containers or interactive approvals";
210
+ }
211
+ export {
212
+ AGY_FILE_ONLY_LIMITATION,
213
+ AGY_ISOLATION_VERSION,
214
+ AGY_VERIFIED_CLI_VERSION,
215
+ agyIsolatedSettings,
216
+ agyIsolationProblem,
217
+ agyIsolationProfile,
218
+ agyManagedOptionsProblem,
219
+ discoverIsolatedAgyModels,
220
+ inspectAgyIsolationRuntime,
221
+ prepareAgyIsolation,
222
+ verifyAgyVersion
223
+ };