@anhlp/alp 0.0.1-beta.3 → 0.0.1-beta.6

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 (175) hide show
  1. package/README.md +14 -410
  2. package/bin/alp.js +5 -0
  3. package/lib/launcher.js +136 -0
  4. package/package.json +19 -104
  5. package/dist/THIRD-PARTY-NOTICES.txt +0 -626
  6. package/dist/acp.js +0 -19903
  7. package/dist/alpd.js +0 -42558
  8. package/docs/modular-monorepo.md +0 -210
  9. package/packages/cli/README.md +0 -7
  10. package/packages/cli/dist/THIRD-PARTY-NOTICES.txt +0 -626
  11. package/packages/cli/dist/acp/agent.d.ts +0 -37
  12. package/packages/cli/dist/acp/agent.js +0 -489
  13. package/packages/cli/dist/acp/main.d.ts +0 -4
  14. package/packages/cli/dist/acp/main.js +0 -23
  15. package/packages/cli/dist/acp.js +0 -19903
  16. package/packages/cli/dist/alpd.js +0 -42558
  17. package/packages/cli/dist/backup.d.ts +0 -4
  18. package/packages/cli/dist/backup.js +0 -146
  19. package/packages/cli/dist/build-manifest.json +0 -142
  20. package/packages/cli/dist/cli.d.ts +0 -2
  21. package/packages/cli/dist/cli.js +0 -28
  22. package/packages/cli/dist/daemon.d.ts +0 -35
  23. package/packages/cli/dist/daemon.js +0 -126
  24. package/packages/cli/dist/index.d.ts +0 -1
  25. package/packages/cli/dist/index.js +0 -5
  26. package/packages/cli/dist/interfaces.d.ts +0 -16
  27. package/packages/cli/dist/interfaces.js +0 -96
  28. package/packages/cli/dist/library.d.ts +0 -140
  29. package/packages/cli/dist/library.js +0 -319
  30. package/packages/cli/dist/paths.d.ts +0 -3
  31. package/packages/cli/dist/paths.js +0 -18
  32. package/packages/cli/dist/projects.d.ts +0 -9
  33. package/packages/cli/dist/projects.js +0 -159
  34. package/packages/cli/dist/sessions.d.ts +0 -41
  35. package/packages/cli/dist/sessions.js +0 -447
  36. package/packages/cli/dist/tasks.d.ts +0 -29
  37. package/packages/cli/dist/tasks.js +0 -345
  38. package/packages/cli/dist/usage.d.ts +0 -3
  39. package/packages/cli/dist/usage.js +0 -72
  40. package/packages/client/README.md +0 -24
  41. package/packages/client/dist/acp-probe.d.ts +0 -44
  42. package/packages/client/dist/acp-probe.js +0 -37
  43. package/packages/client/dist/antigravity-probe.d.ts +0 -34
  44. package/packages/client/dist/antigravity-probe.js +0 -95
  45. package/packages/client/dist/command.d.ts +0 -5
  46. package/packages/client/dist/command.js +0 -33
  47. package/packages/client/dist/doctor.d.ts +0 -13
  48. package/packages/client/dist/doctor.js +0 -587
  49. package/packages/client/dist/index.d.ts +0 -116
  50. package/packages/client/dist/index.js +0 -500
  51. package/packages/client/dist/ipc.d.ts +0 -42
  52. package/packages/client/dist/ipc.js +0 -69
  53. package/packages/client/dist/library-test.d.ts +0 -94
  54. package/packages/client/dist/library-test.js +0 -37
  55. package/packages/client/dist/mcp-probe.d.ts +0 -24
  56. package/packages/client/dist/mcp-probe.js +0 -147
  57. package/packages/client/dist/portable.d.ts +0 -18
  58. package/packages/client/dist/portable.js +0 -27
  59. package/packages/client/dist/prompt.d.ts +0 -32
  60. package/packages/client/dist/prompt.js +0 -59
  61. package/packages/client/dist/render.d.ts +0 -6
  62. package/packages/client/dist/render.js +0 -120
  63. package/packages/client/dist/rpc/client.d.ts +0 -82
  64. package/packages/client/dist/rpc/client.js +0 -292
  65. package/packages/client/dist/rpc/errors.d.ts +0 -42
  66. package/packages/client/dist/rpc/errors.js +0 -34
  67. package/packages/client/dist/rpc/framing.d.ts +0 -21
  68. package/packages/client/dist/rpc/framing.js +0 -57
  69. package/packages/client/dist/rpc/handshake.d.ts +0 -19
  70. package/packages/client/dist/rpc/handshake.js +0 -8
  71. package/packages/client/dist/rpc/reconnect.d.ts +0 -97
  72. package/packages/client/dist/rpc/reconnect.js +0 -227
  73. package/packages/client/dist/rpc/timers.d.ts +0 -11
  74. package/packages/client/dist/rpc/timers.js +0 -11
  75. package/packages/client/dist/service.d.ts +0 -50
  76. package/packages/client/dist/service.js +0 -143
  77. package/packages/client/dist/setup.d.ts +0 -2
  78. package/packages/client/dist/setup.js +0 -146
  79. package/packages/client/dist/supervise.d.ts +0 -12
  80. package/packages/client/dist/supervise.js +0 -87
  81. package/packages/client/dist/websocket.d.ts +0 -51
  82. package/packages/client/dist/websocket.js +0 -190
  83. package/packages/core/README.md +0 -7
  84. package/packages/core/dist/adapter.d.ts +0 -20
  85. package/packages/core/dist/adapter.js +0 -30
  86. package/packages/core/dist/atomic.d.ts +0 -2
  87. package/packages/core/dist/atomic.js +0 -28
  88. package/packages/core/dist/backup.d.ts +0 -27
  89. package/packages/core/dist/backup.js +0 -582
  90. package/packages/core/dist/beads.d.ts +0 -65
  91. package/packages/core/dist/beads.js +0 -213
  92. package/packages/core/dist/delegation.d.ts +0 -2
  93. package/packages/core/dist/delegation.js +0 -32
  94. package/packages/core/dist/errors.d.ts +0 -4
  95. package/packages/core/dist/errors.js +0 -11
  96. package/packages/core/dist/formulas.d.ts +0 -25
  97. package/packages/core/dist/formulas.js +0 -201
  98. package/packages/core/dist/hook-run.d.ts +0 -44
  99. package/packages/core/dist/hook-run.js +0 -60
  100. package/packages/core/dist/hooks.d.ts +0 -35
  101. package/packages/core/dist/hooks.js +0 -30
  102. package/packages/core/dist/index.d.ts +0 -30
  103. package/packages/core/dist/index.js +0 -63
  104. package/packages/core/dist/init.d.ts +0 -30
  105. package/packages/core/dist/init.js +0 -89
  106. package/packages/core/dist/library-edit.d.ts +0 -93
  107. package/packages/core/dist/library-edit.js +0 -368
  108. package/packages/core/dist/library.d.ts +0 -28
  109. package/packages/core/dist/library.js +0 -108
  110. package/packages/core/dist/log.d.ts +0 -37
  111. package/packages/core/dist/log.js +0 -156
  112. package/packages/core/dist/migrations.d.ts +0 -9
  113. package/packages/core/dist/migrations.js +0 -95
  114. package/packages/core/dist/models.d.ts +0 -11
  115. package/packages/core/dist/models.js +0 -44
  116. package/packages/core/dist/permissions.d.ts +0 -14
  117. package/packages/core/dist/permissions.js +0 -229
  118. package/packages/core/dist/profiles.d.ts +0 -27
  119. package/packages/core/dist/profiles.js +0 -122
  120. package/packages/core/dist/promptsafe.d.ts +0 -1
  121. package/packages/core/dist/promptsafe.js +0 -13
  122. package/packages/core/dist/providers.d.ts +0 -90
  123. package/packages/core/dist/providers.js +0 -101
  124. package/packages/core/dist/resolver.d.ts +0 -41
  125. package/packages/core/dist/resolver.js +0 -240
  126. package/packages/core/dist/safe-path.d.ts +0 -2
  127. package/packages/core/dist/safe-path.js +0 -24
  128. package/packages/core/dist/shell.d.ts +0 -16
  129. package/packages/core/dist/shell.js +0 -22
  130. package/packages/core/dist/state-schema.d.ts +0 -9
  131. package/packages/core/dist/state-schema.js +0 -54
  132. package/packages/core/dist/task-rows.d.ts +0 -47
  133. package/packages/core/dist/task-rows.js +0 -45
  134. package/packages/core/dist/tasks.d.ts +0 -128
  135. package/packages/core/dist/tasks.js +0 -835
  136. package/packages/core/dist/teams.d.ts +0 -80
  137. package/packages/core/dist/teams.js +0 -159
  138. package/packages/core/dist/template-path.d.ts +0 -2
  139. package/packages/core/dist/template-path.js +0 -10
  140. package/packages/core/dist/trust.d.ts +0 -8
  141. package/packages/core/dist/trust.js +0 -47
  142. package/packages/core/dist/types.d.ts +0 -51
  143. package/packages/core/dist/upgrade.d.ts +0 -10
  144. package/packages/core/dist/upgrade.js +0 -200
  145. package/packages/core/dist/user-settings.d.ts +0 -6
  146. package/packages/core/dist/user-settings.js +0 -72
  147. package/packages/core/dist/validation.d.ts +0 -17
  148. package/packages/core/dist/validation.js +0 -295
  149. package/packages/core/dist/verify.d.ts +0 -15
  150. package/packages/core/dist/verify.js +0 -123
  151. package/packages/core/dist/workflow.d.ts +0 -45
  152. package/packages/core/dist/workflow.js +0 -33
  153. package/templates/ALP.md +0 -52
  154. package/templates/agents/lead/AGENT.md +0 -90
  155. package/templates/agents/main/AGENT.md +0 -150
  156. package/templates/agents/oracle/AGENT.md +0 -44
  157. package/templates/agents/peer/AGENT.md +0 -55
  158. package/templates/agents/reviewer/AGENT.md +0 -80
  159. package/templates/agents/supervisor/AGENT.md +0 -72
  160. package/templates/providers/antigravity.json +0 -6
  161. package/templates/providers/opencode.json +0 -7
  162. package/templates/role-skills.json +0 -8
  163. package/templates/skills/bug-loop/SKILL.md +0 -44
  164. package/templates/skills/bug-loop/references/test-proof.md +0 -25
  165. package/templates/skills/goal-griller/SKILL.md +0 -49
  166. package/templates/skills/prompt-leverage/SKILL.md +0 -37
  167. package/templates/skills/prompt-leverage/references/brief-template.md +0 -27
  168. package/templates/skills/sequence-execution-plan/SKILL.md +0 -42
  169. package/templates/skills/sequence-execution-plan/references/plan-template.md +0 -22
  170. package/templates/skills/smart-commits/SKILL.md +0 -40
  171. package/templates/skills/xia/SKILL.md +0 -42
  172. package/templates/teams/cafe/HOUSE_RULES.md +0 -1
  173. package/templates/teams/cafe/team.json +0 -15
  174. package/templates/teams/pho/HOUSE_RULES.md +0 -1
  175. package/templates/teams/pho/team.json +0 -14
@@ -1,489 +0,0 @@
1
- // packages/cli/src/acp/agent.ts
2
- import * as acp from "@agentclientprotocol/sdk";
3
- import { randomUUID } from "node:crypto";
4
- import path from "node:path";
5
- var unlistedOf = ({ teams, team, teamLabel }) => teams.some((entry) => entry.id === team) ? void 0 : { id: team, label: teamLabel ?? (team === "custom" ? "Custom" : team) };
6
- var MODES = [
7
- { id: "read-only", name: "Read only", description: "Agents read and search; nothing is changed" },
8
- { id: "workspace-write", name: "Workspace write", description: "Agents change files and run commands inside the project" },
9
- { id: "full-access", name: "Full access", description: "Agents may do anything, without a sandbox" }
10
- ];
11
- var OUTPUT_CHARS = 2e4;
12
- var COMMANDS = [
13
- { name: "answer", description: "Answer the question an agent asked you", input: { hint: "your answer" } },
14
- { name: "dismiss", description: "Dismiss the question an agent asked you", input: { hint: "why (optional)" } }
15
- ];
16
- function hostServers(servers = []) {
17
- const host = {};
18
- for (const server of servers) {
19
- if ("command" in server) host[server.name] = { type: "stdio", command: server.command, args: server.args, env: Object.fromEntries(server.env.map((variable) => [variable.name, variable.value])) };
20
- else if (server.type === "http" || server.type === "sse") host[server.name] = { type: server.type, url: server.url, headers: Object.fromEntries(server.headers.map((header) => [header.name, header.value])) };
21
- }
22
- return host;
23
- }
24
- function promptText(blocks) {
25
- let text = "";
26
- for (const block of blocks) {
27
- if (block.type === "text") {
28
- text += block.text;
29
- continue;
30
- }
31
- let part = "";
32
- if (block.type === "resource_link") part = `@${block.uri.replace(/^file:\/\//, "")}`;
33
- else if (block.type === "resource") {
34
- const { resource } = block;
35
- const where = resource.uri.replace(/^file:\/\//, "");
36
- part = "text" in resource ? `
37
- <file path="${where}">
38
- ${resource.text}
39
- </file>
40
- ` : `@${where}`;
41
- }
42
- if (part && text && !/\s$/.test(text) && !part.startsWith("\n")) text += " ";
43
- text += part;
44
- }
45
- return text.trim();
46
- }
47
- function shown(file, cwd) {
48
- if (!cwd || !path.isAbsolute(file)) return file;
49
- const relative = path.relative(cwd, file);
50
- return relative && !relative.startsWith("..") && !path.isAbsolute(relative) ? relative : file;
51
- }
52
- function toolKind(item) {
53
- const detail = item.detail;
54
- switch (detail.type) {
55
- case "read":
56
- return "read";
57
- case "edit":
58
- case "write":
59
- return "edit";
60
- case "search":
61
- return detail.toolName === "web_search" ? "fetch" : "search";
62
- case "fetch":
63
- return "fetch";
64
- case "plan":
65
- return "think";
66
- case "sub_agent":
67
- case "plain_text":
68
- return "other";
69
- }
70
- const word = detail.type === "shell" ? String(detail.command ?? "").trim().split(/[\s{(]/)[0] : item.name;
71
- if (/^(Read|NotebookRead|LS)$/.test(word)) return "read";
72
- if (/^(Edit|MultiEdit|Write|NotebookEdit)$/.test(word)) return "edit";
73
- if (/^(Grep|Glob)$/.test(word)) return "search";
74
- if (/^(WebFetch|WebSearch)$/.test(word)) return "fetch";
75
- if (/^(Skill|Task|TodoWrite)$/.test(word) || item.name.startsWith("alp_")) return "other";
76
- return detail.type === "shell" ? "execute" : "other";
77
- }
78
- function toolTitle(item, cwd) {
79
- const detail = item.detail;
80
- switch (detail.type) {
81
- case "shell":
82
- return clip(String(detail.command ?? "").split("\n")[0], 160);
83
- case "read":
84
- return clip(`Read ${shown(detail.filePath, cwd)}`, 160);
85
- case "edit":
86
- return clip(`Edit ${shown(detail.filePath, cwd)}`, 160);
87
- case "write":
88
- return clip(`Write ${shown(detail.filePath, cwd)}`, 160);
89
- case "search":
90
- return clip(`${detail.toolName === "web_search" ? "Search the web" : "Search"} ${detail.query}`, 160);
91
- case "fetch":
92
- return clip(`Fetch ${detail.url}`, 160);
93
- case "sub_agent":
94
- return clip(`${detail.subAgentType ?? "Sub-agent"}${detail.description ? `: ${detail.description.split("\n")[0]}` : ""}`, 160);
95
- case "plan":
96
- return "Plan";
97
- case "plain_text":
98
- return clip(`${item.name}${detail.label ? `: ${detail.label}` : ""}`, 160);
99
- }
100
- const input = detail.input ?? {};
101
- if (item.name === "alp_delegate") return clip(`Delegate to ${input.agent ?? "an agent"}: ${String(input.task ?? "").split("\n")[0]}`, 160);
102
- return clip(`${item.name} ${JSON.stringify(input)}`, 160);
103
- }
104
- var bounded = (text) => text.length > OUTPUT_CHARS ? `${text.slice(0, OUTPUT_CHARS)}
105
- \u2026 (${text.length - OUTPUT_CHARS} more characters)` : text;
106
- function toolOutput(item) {
107
- const detail = item.detail;
108
- const parts = [];
109
- let text = "";
110
- switch (detail.type) {
111
- case "shell":
112
- text = detail.output ?? "";
113
- break;
114
- case "read":
115
- text = detail.content ?? "";
116
- break;
117
- case "edit":
118
- if (detail.newString !== void 0 || detail.oldString !== void 0) parts.push({ type: "diff", path: detail.filePath, oldText: detail.oldString ?? null, newText: detail.newString ?? "" });
119
- else if (detail.unifiedDiff) text = "```diff\n" + detail.unifiedDiff + (detail.unifiedDiff.endsWith("\n") ? "" : "\n") + "```";
120
- break;
121
- case "write":
122
- if (detail.content !== void 0) parts.push({ type: "diff", path: detail.filePath, oldText: null, newText: detail.content });
123
- break;
124
- case "search":
125
- text = detail.content ?? [...detail.filePaths ?? [], ...(detail.webResults ?? []).map((hit) => `${hit.title} \u2014 ${hit.url}`)].join("\n");
126
- break;
127
- case "fetch":
128
- text = detail.result ?? "";
129
- break;
130
- case "sub_agent":
131
- text = detail.log || detail.description || "";
132
- break;
133
- case "plan":
134
- text = detail.text;
135
- break;
136
- case "plain_text":
137
- text = detail.text ?? "";
138
- break;
139
- default: {
140
- const output = detail.output;
141
- text = Array.isArray(output) ? output.map((part) => typeof part?.text === "string" ? part.text : JSON.stringify(part)).join("\n") : output == null ? "" : typeof output === "string" ? output : JSON.stringify(output, null, 2);
142
- }
143
- }
144
- if (item.error) text = text ? `${text}
145
- ${item.error}` : item.error;
146
- if (text) parts.push({ type: "content", content: { type: "text", text: bounded(text) } });
147
- return parts.map((part) => part.type === "diff" ? { ...part, newText: bounded(part.newText), ...part.oldText ? { oldText: bounded(part.oldText) } : {} } : part);
148
- }
149
- function toolInput(item) {
150
- const detail = item.detail;
151
- if (detail.type === "shell") return { command: detail.command, cwd: detail.cwd };
152
- if (detail.type === "unknown" || !["read", "edit", "write", "search", "fetch", "sub_agent", "plan", "plain_text"].includes(detail.type)) return detail.input;
153
- const { type: _type, content: _content, oldString: _old, newString: _new, unifiedDiff: _diff, result: _result, log: _log, text: _text, filePaths: _paths, webResults: _hits, ...rest } = detail;
154
- return rest;
155
- }
156
- function toolLocations(item) {
157
- const detail = item.detail;
158
- if (detail.type !== "read" && detail.type !== "edit" && detail.type !== "write") return void 0;
159
- const filePath = String(detail.filePath);
160
- return [{ path: filePath, ...detail.type === "read" && typeof detail.offset === "number" ? { line: detail.offset } : {} }];
161
- }
162
- var clip = (text, chars) => text.length > chars ? `${text.slice(0, chars - 1)}\u2026` : text;
163
- var status = (state) => state === "running" ? "in_progress" : state;
164
- function createAcpAgent(options) {
165
- const log = options.log ?? (() => {
166
- });
167
- const sessions = /* @__PURE__ */ new Map();
168
- let alpd;
169
- let editor;
170
- let leaving = false;
171
- async function daemon() {
172
- alpd ??= options.connect().then((connection) => {
173
- connection.onEvent(project);
174
- connection.onClose((error) => {
175
- if (leaving) return;
176
- log("lost the connection to alpd", { code: "ALP_IPC_LOST", error });
177
- alpd = void 0;
178
- for (const session of sessions.values()) {
179
- session.opened = false;
180
- session.waiting?.done({ state: "failed", error: `ALP lost its connection to alpd: ${error.message}` });
181
- }
182
- });
183
- return connection;
184
- }, (error) => {
185
- alpd = void 0;
186
- log("could not reach alpd", { code: "ALP_IPC_CONNECT_FAILED", error });
187
- throw error;
188
- });
189
- return alpd;
190
- }
191
- const request = async (method, params) => (await daemon()).request(method, params);
192
- function send(session, update) {
193
- const client = editor;
194
- if (!client) return;
195
- session.queue = session.queue.then(() => client.notify("session/update", { sessionId: session.id, update })).catch((error) => log("could not send an update to the editor", { code: "ALP_ACP_UPDATE_FAILED", error }));
196
- }
197
- const say = (session, text) => send(session, { sessionUpdate: "agent_message_chunk", content: { type: "text", text } });
198
- function configOptions(session) {
199
- const teams = session.opened ? choices(session).filter((team) => team.id === session.preview.team) : choices(session);
200
- return [
201
- {
202
- id: "team",
203
- name: "Team",
204
- category: "model",
205
- type: "select",
206
- currentValue: session.preview.team,
207
- description: session.opened ? "A session keeps its team; start a new one for another team" : "The team that works on this session",
208
- options: teams.map((team) => ({ value: team.id, name: team.label, ...team.description ? { description: team.description } : {} }))
209
- },
210
- { id: "mode", name: "Permissions", category: "mode", type: "select", currentValue: session.preview.mode, options: MODES.map((mode) => ({ value: mode.id, name: mode.name, description: mode.description })) }
211
- ];
212
- }
213
- function choices(session) {
214
- const { teams, team } = session.preview;
215
- const own = unlistedOf(session.preview);
216
- const first = session.unlisted && session.unlisted.id !== team && !teams.some((entry) => entry.id === session.unlisted.id) ? [session.unlisted] : [];
217
- return [...first, ...own ? [own] : [], ...teams];
218
- }
219
- const modeState = (session) => ({ currentModeId: session.preview.mode, availableModes: MODES });
220
- function track(id, cwd, mcpServers, preview2, opened) {
221
- const unlisted = unlistedOf(preview2);
222
- const session = { id, cwd, mcpServers, preview: preview2, opened, ...unlisted ? { unlisted } : {}, ours: /* @__PURE__ */ new Set(), texts: /* @__PURE__ */ new Map(), tools: /* @__PURE__ */ new Set(), questions: /* @__PURE__ */ new Map(), queue: Promise.resolve() };
223
- sessions.set(id, session);
224
- return session;
225
- }
226
- function known(sessionId) {
227
- const session = sessions.get(sessionId);
228
- if (!session) throw acp.RequestError.resourceNotFound(sessionId);
229
- return session;
230
- }
231
- function project(envelope) {
232
- const session = sessions.get(envelope.sessionId);
233
- const { event } = envelope;
234
- if (event.type === "question") {
235
- const root = sessions.get(event.question.rootId);
236
- if (!root || root.questions.has(event.question.id)) return;
237
- root.questions.set(event.question.id, event.question);
238
- const options2 = event.question.options?.length ? `
239
- Options: ${event.question.options.join(" \xB7 ")}` : "";
240
- say(root, `
241
-
242
- **${event.question.agent} asks you:** ${event.question.body}${options2}
243
-
244
- Reply with /answer <your answer>, or /dismiss.
245
-
246
- `);
247
- return;
248
- }
249
- if (event.type === "question.resolved") {
250
- for (const root of sessions.values()) root.questions.delete(event.questionId);
251
- return;
252
- }
253
- if (!session) return;
254
- switch (event.type) {
255
- case "session.updated":
256
- if (event.session.mode !== session.preview.mode) {
257
- session.preview = { ...session.preview, mode: event.session.mode };
258
- send(session, { sessionUpdate: "current_mode_update", currentModeId: event.session.mode });
259
- send(session, { sessionUpdate: "config_option_update", configOptions: configOptions(session) });
260
- }
261
- return;
262
- case "prompt.accepted":
263
- if (session.waiting?.clientMessageId === event.clientMessageId) session.waiting.turnId = event.turnId;
264
- return;
265
- case "prompt.failed":
266
- if (session.waiting?.clientMessageId === event.clientMessageId) session.waiting.done({ state: "failed", error: event.error.message });
267
- return;
268
- case "turn.ended":
269
- if (session.waiting?.turnId === event.turnId) session.waiting.done({ state: event.state, ...event.error ? { error: event.error.message } : {} });
270
- return;
271
- case "session.failed":
272
- session.opened = false;
273
- session.waiting?.done({ state: "failed", error: event.error.message });
274
- return;
275
- case "session.closed":
276
- session.opened = false;
277
- return;
278
- case "item":
279
- item(session, event.item);
280
- return;
281
- }
282
- }
283
- function item(session, item2) {
284
- switch (item2.kind) {
285
- case "user_message": {
286
- if (item2.clientMessageId && session.ours.has(item2.clientMessageId)) return;
287
- if (item2.clientMessageId?.startsWith("alp-") || item2.id.startsWith("user:alp-")) {
288
- send(session, { sessionUpdate: "tool_call", toolCallId: item2.id, title: clip(`ALP: ${item2.text.split("\n").find((line) => line.trim()) ?? "note"}`, 160), kind: "other", status: "completed", content: [{ type: "content", content: { type: "text", text: item2.text } }] });
289
- return;
290
- }
291
- send(session, { sessionUpdate: "user_message_chunk", content: { type: "text", text: item2.text } });
292
- return;
293
- }
294
- case "assistant_message": {
295
- const before = session.texts.get(item2.id) ?? "";
296
- session.texts.set(item2.id, item2.text);
297
- const fresh = item2.text.startsWith(before) ? item2.text.slice(before.length) : `
298
- ${item2.text}`;
299
- if (fresh) say(session, fresh);
300
- return;
301
- }
302
- case "notice":
303
- say(session, `
304
-
305
- > ALP: ${item2.text}
306
-
307
- `);
308
- return;
309
- case "compaction":
310
- if (item2.status !== "running") say(session, `
311
-
312
- > ALP: ${item2.status === "completed" ? `the context was compacted${item2.preTokens ? ` from ${Math.round(item2.preTokens / 1e3)}k tokens` : ""}` : "compacting the context failed"}
313
-
314
- `);
315
- return;
316
- case "todo":
317
- send(session, { sessionUpdate: "plan", entries: item2.items.map((entry) => ({ content: entry.text, priority: "medium", status: entry.status })) });
318
- return;
319
- case "tool_call": {
320
- const locations = toolLocations(item2);
321
- const fields = { title: toolTitle(item2, session.cwd), kind: toolKind(item2), status: status(item2.status), content: toolOutput(item2), rawInput: toolInput(item2), ...locations ? { locations } : {} };
322
- if (session.tools.has(item2.id)) send(session, { sessionUpdate: "tool_call_update", toolCallId: item2.id, ...fields });
323
- else {
324
- session.tools.add(item2.id);
325
- send(session, { sessionUpdate: "tool_call", toolCallId: item2.id, ...fields });
326
- }
327
- return;
328
- }
329
- }
330
- }
331
- async function open(session, resume) {
332
- await request("session.create", {
333
- sessionId: session.id,
334
- spec: { cwd: session.cwd, persist: true, workflow: session.preview.team, mode: session.preview.mode, mcpServers: session.mcpServers },
335
- history: "skip",
336
- ...resume ? { resume: true } : {}
337
- });
338
- session.opened = true;
339
- }
340
- async function prompt(session, blocks) {
341
- const text = promptText(blocks);
342
- const command = /^\/(answer|dismiss)\b\s*([\s\S]*)$/.exec(text);
343
- if (command) {
344
- const [question] = session.questions.values();
345
- if (!question) say(session, "No agent is waiting for an answer from you.");
346
- else {
347
- await request("question.answer", command[1] === "answer" ? { questionId: question.id, text: command[2].trim() || "yes" } : { questionId: question.id, dismiss: true, ...command[2].trim() ? { reason: command[2].trim() } : {} });
348
- session.questions.delete(question.id);
349
- say(session, command[1] === "answer" ? `Sent your answer to ${question.agent}.` : `Dismissed ${question.agent}'s question.`);
350
- }
351
- await session.queue;
352
- return { stopReason: "end_turn" };
353
- }
354
- if (!text) throw acp.RequestError.invalidParams(void 0, "The prompt is empty");
355
- if (!session.opened) {
356
- const { session: snapshot } = await request("session.get", { sessionId: session.id }).catch(() => ({ session: void 0 }));
357
- await open(session, !!snapshot);
358
- send(session, { sessionUpdate: "config_option_update", configOptions: configOptions(session) });
359
- }
360
- if (session.waiting) throw acp.RequestError.invalidRequest(void 0, "A prompt is already running in this session");
361
- const clientMessageId = randomUUID();
362
- session.ours.add(clientMessageId);
363
- let done;
364
- const ended = new Promise((resolve) => {
365
- done = (result2) => {
366
- if (session.waiting?.clientMessageId === clientMessageId) session.waiting = void 0;
367
- resolve(result2);
368
- };
369
- });
370
- session.waiting = { clientMessageId, done };
371
- try {
372
- await request("session.prompt", { sessionId: session.id, clientMessageId, delivery: "auto", content: [{ type: "text", text }] });
373
- } catch (error) {
374
- done({ state: "failed", error: error instanceof Error ? error.message : String(error) });
375
- }
376
- const result = await ended;
377
- await session.queue;
378
- if (result.state === "canceled") return { stopReason: "cancelled" };
379
- if (result.state === "failed") throw acp.RequestError.internalError(void 0, result.error ?? "The turn failed");
380
- return { stopReason: "end_turn" };
381
- }
382
- async function preview(cwd, mcpServers, team, mode) {
383
- const { preview: preview2 } = await request("session.preview", { spec: { cwd, mcpServers, ...team ? { workflow: team } : {}, ...mode ? { mode } : {} } });
384
- return preview2;
385
- }
386
- const absolute = (cwd) => {
387
- if (!path.isAbsolute(cwd)) throw acp.RequestError.invalidParams(void 0, "cwd must be an absolute path");
388
- return path.resolve(cwd);
389
- };
390
- return acp.agent({ name: "alp" }).onConnect((connection) => {
391
- editor = connection.client;
392
- void connection.closed.then(() => release());
393
- }).onRequest("initialize", () => ({
394
- protocolVersion: acp.PROTOCOL_VERSION,
395
- agentCapabilities: {
396
- loadSession: true,
397
- promptCapabilities: { embeddedContext: true },
398
- mcpCapabilities: { http: true, sse: true },
399
- sessionCapabilities: { list: {}, close: {} }
400
- },
401
- agentInfo: { name: "alp", title: "ALP", version: options.version },
402
- authMethods: []
403
- })).onRequest("authenticate", () => ({})).onRequest("session/new", async ({ params }) => {
404
- const cwd = absolute(params.cwd);
405
- const mcpServers = hostServers(params.mcpServers);
406
- const session = track(`acp-${randomUUID()}`, cwd, mcpServers, await preview(cwd, mcpServers), false);
407
- queueMicrotask(() => send(session, { sessionUpdate: "available_commands_update", availableCommands: COMMANDS }));
408
- return { sessionId: session.id, configOptions: configOptions(session), modes: modeState(session) };
409
- }).onRequest("session/load", async ({ params }) => {
410
- const cwd = absolute(params.cwd);
411
- const { session: record } = await request("session.get", { sessionId: params.sessionId }).catch(() => {
412
- throw acp.RequestError.resourceNotFound(params.sessionId);
413
- });
414
- if (record.parentId || path.resolve(record.projectRoot) !== cwd) throw acp.RequestError.invalidParams(void 0, "That ALP session belongs to another project");
415
- const session = track(params.sessionId, cwd, hostServers(params.mcpServers), {
416
- teams: [{ id: record.workflow?.mode ?? "pho", label: record.teamLabel ?? record.workflow?.mode ?? "pho" }],
417
- team: record.workflow?.mode ?? "pho",
418
- ...record.teamLabel ? { teamLabel: record.teamLabel } : {},
419
- agent: record.agent,
420
- runtime: record.runtime,
421
- model: record.model,
422
- mode: record.mode,
423
- thinking: record.thinking
424
- }, true);
425
- const live = record.status === "idle" || record.status === "running";
426
- await request("session.attach", { sessionId: session.id, replay: true });
427
- session.opened = live;
428
- await session.queue;
429
- send(session, { sessionUpdate: "available_commands_update", availableCommands: COMMANDS });
430
- return { configOptions: configOptions(session), modes: modeState(session) };
431
- }).onRequest("session/list", async ({ params }) => {
432
- const { sessions: rows } = await request("session.list", { ...params.cwd ? { projectRoot: path.resolve(params.cwd) } : {}, rootsOnly: true, includeClosed: true });
433
- return {
434
- sessions: rows.filter((row) => row.persistent).slice(0, 100).map((row) => ({
435
- sessionId: row.id,
436
- cwd: row.projectRoot,
437
- title: row.title ?? `ALP ${row.agent}`,
438
- ...row.updatedAt ? { updatedAt: row.updatedAt } : {}
439
- }))
440
- };
441
- }).onRequest("session/prompt", ({ params }) => prompt(known(params.sessionId), params.prompt)).onNotification("session/cancel", async ({ params }) => {
442
- const session = sessions.get(params.sessionId);
443
- if (!session?.opened) return;
444
- await request("session.interrupt", { sessionId: session.id }).catch((error) => log("session.interrupt failed", { code: "ALP_ACP_INTERRUPT_FAILED", error }));
445
- }).onRequest("session/set_mode", async ({ params }) => {
446
- await setMode(known(params.sessionId), params.modeId);
447
- return {};
448
- }).onRequest("session/set_config_option", async ({ params }) => {
449
- const session = known(params.sessionId);
450
- const value = String(params.value);
451
- if (params.configId === "mode") await setMode(session, value);
452
- else if (params.configId === "team") {
453
- if (session.opened && value !== session.preview.team) throw acp.RequestError.invalidParams(void 0, "A session keeps its team; start a new one for another team");
454
- if (!choices(session).some((team) => team.id === value)) throw acp.RequestError.invalidParams(void 0, `Unknown team ${value}`);
455
- session.preview = { ...await preview(session.cwd, session.mcpServers, value), mode: session.preview.mode };
456
- } else throw acp.RequestError.invalidParams(void 0, `Unknown option ${params.configId}`);
457
- return { configOptions: configOptions(session) };
458
- }).onRequest("session/close", async ({ params }) => {
459
- const session = known(params.sessionId);
460
- sessions.delete(session.id);
461
- if (session.opened) await request("session.release", { sessionId: session.id }).catch(() => {
462
- });
463
- return {};
464
- });
465
- async function setMode(session, mode) {
466
- if (!MODES.some((entry) => entry.id === mode)) throw acp.RequestError.invalidParams(void 0, `Unknown mode ${mode}`);
467
- if (session.opened) await request("session.configure", { sessionId: session.id, mode });
468
- session.preview = { ...session.preview, mode };
469
- }
470
- async function release() {
471
- leaving = true;
472
- const connection = await alpd?.catch(() => void 0);
473
- if (!connection) return;
474
- await Promise.all([...sessions.values()].filter((session) => session.opened).map((session) => connection.request("session.release", { sessionId: session.id }).catch(() => {
475
- })));
476
- sessions.clear();
477
- connection.close();
478
- }
479
- }
480
- export {
481
- createAcpAgent,
482
- hostServers,
483
- promptText,
484
- toolInput,
485
- toolKind,
486
- toolLocations,
487
- toolOutput,
488
- toolTitle
489
- };
@@ -1,4 +0,0 @@
1
- /** `alp acp`: serves the Agent Client Protocol on stdio until the editor closes it (ALPD §60). */
2
- export declare function runAcp({ daemonEntry }: {
3
- daemonEntry: string;
4
- }): Promise<void>;
@@ -1,23 +0,0 @@
1
- // packages/cli/src/acp/main.ts
2
- import * as acp from "@agentclientprotocol/sdk";
3
- import { Readable, Writable } from "node:stream";
4
- import { alpHome, connect } from "@anhlp/alp-client/index";
5
- import { startDaemon } from "@anhlp/alp-client/supervise";
6
- import { createLogger } from "@anhlp/alp-core/log";
7
- import { createAcpAgent } from "./agent.js";
8
- async function runAcp({ daemonEntry }) {
9
- const version = true ? "0.0.1-beta.3" : "0.0.0";
10
- const logger = createLogger({ component: "acp" });
11
- const log = (message, fields) => logger.warn(message, fields);
12
- const app = createAcpAgent({
13
- version,
14
- log,
15
- connect: async () => connect(await startDaemon({ home: alpHome(), entry: daemonEntry }), { name: "alp-acp", version })
16
- });
17
- const stream = acp.ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
18
- const connection = app.connect(stream);
19
- await connection.closed;
20
- }
21
- export {
22
- runAcp
23
- };