@anhlp/alp 0.0.1-beta.4 → 0.0.1-beta.7

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 (198) hide show
  1. package/README.md +11 -412
  2. package/bin/alp.js +3 -0
  3. package/package.json +11 -103
  4. package/dist/THIRD-PARTY-NOTICES.txt +0 -626
  5. package/dist/acp.js +0 -19936
  6. package/dist/alpd.js +0 -49562
  7. package/docs/modular-monorepo.md +0 -214
  8. package/packages/cli/README.md +0 -7
  9. package/packages/cli/dist/THIRD-PARTY-NOTICES.txt +0 -651
  10. package/packages/cli/dist/acp/agent.d.ts +0 -37
  11. package/packages/cli/dist/acp/agent.js +0 -489
  12. package/packages/cli/dist/acp/main.d.ts +0 -4
  13. package/packages/cli/dist/acp/main.js +0 -23
  14. package/packages/cli/dist/acp.js +0 -19936
  15. package/packages/cli/dist/alp-web/THIRD-PARTY-NOTICES.txt +0 -2590
  16. package/packages/cli/dist/alp-web/alp-boundary.json +0 -15
  17. package/packages/cli/dist/alp-web/apple-touch-icon.png +0 -0
  18. package/packages/cli/dist/alp-web/assets/TerminalPanel-DLuoa74B.css +0 -1
  19. package/packages/cli/dist/alp-web/assets/TerminalPanel-PrU2l3u5.js +0 -16
  20. package/packages/cli/dist/alp-web/assets/index-BJdBTDFX.css +0 -1
  21. package/packages/cli/dist/alp-web/assets/index-DpfiPv5s.js +0 -52
  22. package/packages/cli/dist/alp-web/favicon-attention.svg +0 -6
  23. package/packages/cli/dist/alp-web/favicon-running.svg +0 -6
  24. package/packages/cli/dist/alp-web/favicon.svg +0 -5
  25. package/packages/cli/dist/alp-web/index.html +0 -20
  26. package/packages/cli/dist/alp-web.js +0 -9640
  27. package/packages/cli/dist/alpd.js +0 -49562
  28. package/packages/cli/dist/backup.d.ts +0 -4
  29. package/packages/cli/dist/backup.js +0 -146
  30. package/packages/cli/dist/build-manifest.json +0 -240
  31. package/packages/cli/dist/cli.d.ts +0 -2
  32. package/packages/cli/dist/cli.js +0 -30
  33. package/packages/cli/dist/daemon.d.ts +0 -35
  34. package/packages/cli/dist/daemon.js +0 -126
  35. package/packages/cli/dist/index.d.ts +0 -1
  36. package/packages/cli/dist/index.js +0 -5
  37. package/packages/cli/dist/interfaces.d.ts +0 -24
  38. package/packages/cli/dist/interfaces.js +0 -156
  39. package/packages/cli/dist/library.d.ts +0 -140
  40. package/packages/cli/dist/library.js +0 -319
  41. package/packages/cli/dist/paths.d.ts +0 -3
  42. package/packages/cli/dist/paths.js +0 -18
  43. package/packages/cli/dist/projects.d.ts +0 -9
  44. package/packages/cli/dist/projects.js +0 -159
  45. package/packages/cli/dist/schedules.d.ts +0 -2
  46. package/packages/cli/dist/schedules.js +0 -45
  47. package/packages/cli/dist/sessions.d.ts +0 -43
  48. package/packages/cli/dist/sessions.js +0 -458
  49. package/packages/cli/dist/tasks.d.ts +0 -29
  50. package/packages/cli/dist/tasks.js +0 -345
  51. package/packages/cli/dist/terminal.d.ts +0 -8
  52. package/packages/cli/dist/terminal.js +0 -79
  53. package/packages/cli/dist/usage.d.ts +0 -3
  54. package/packages/cli/dist/usage.js +0 -78
  55. package/packages/cli/dist/web-service.d.ts +0 -38
  56. package/packages/cli/dist/web-service.js +0 -119
  57. package/packages/client/README.md +0 -29
  58. package/packages/client/dist/acp-probe.d.ts +0 -44
  59. package/packages/client/dist/acp-probe.js +0 -37
  60. package/packages/client/dist/antigravity-probe.d.ts +0 -34
  61. package/packages/client/dist/antigravity-probe.js +0 -95
  62. package/packages/client/dist/command.d.ts +0 -5
  63. package/packages/client/dist/command.js +0 -33
  64. package/packages/client/dist/doctor.d.ts +0 -13
  65. package/packages/client/dist/doctor.js +0 -587
  66. package/packages/client/dist/index.d.ts +0 -151
  67. package/packages/client/dist/index.js +0 -533
  68. package/packages/client/dist/integration-lock.d.ts +0 -35
  69. package/packages/client/dist/integration-lock.js +0 -179
  70. package/packages/client/dist/ipc.d.ts +0 -42
  71. package/packages/client/dist/ipc.js +0 -69
  72. package/packages/client/dist/library-test.d.ts +0 -94
  73. package/packages/client/dist/library-test.js +0 -37
  74. package/packages/client/dist/mcp-probe.d.ts +0 -24
  75. package/packages/client/dist/mcp-probe.js +0 -147
  76. package/packages/client/dist/portable.d.ts +0 -19
  77. package/packages/client/dist/portable.js +0 -33
  78. package/packages/client/dist/prompt.d.ts +0 -32
  79. package/packages/client/dist/prompt.js +0 -59
  80. package/packages/client/dist/render.d.ts +0 -6
  81. package/packages/client/dist/render.js +0 -120
  82. package/packages/client/dist/rpc/client.d.ts +0 -82
  83. package/packages/client/dist/rpc/client.js +0 -292
  84. package/packages/client/dist/rpc/errors.d.ts +0 -42
  85. package/packages/client/dist/rpc/errors.js +0 -34
  86. package/packages/client/dist/rpc/framing.d.ts +0 -21
  87. package/packages/client/dist/rpc/framing.js +0 -57
  88. package/packages/client/dist/rpc/handshake.d.ts +0 -19
  89. package/packages/client/dist/rpc/handshake.js +0 -8
  90. package/packages/client/dist/rpc/reconnect.d.ts +0 -97
  91. package/packages/client/dist/rpc/reconnect.js +0 -227
  92. package/packages/client/dist/rpc/registry.d.ts +0 -211
  93. package/packages/client/dist/rpc/registry.js +0 -145
  94. package/packages/client/dist/rpc/timers.d.ts +0 -11
  95. package/packages/client/dist/rpc/timers.js +0 -11
  96. package/packages/client/dist/service.d.ts +0 -50
  97. package/packages/client/dist/service.js +0 -143
  98. package/packages/client/dist/setup.d.ts +0 -2
  99. package/packages/client/dist/setup.js +0 -146
  100. package/packages/client/dist/supervise.d.ts +0 -14
  101. package/packages/client/dist/supervise.js +0 -87
  102. package/packages/client/dist/websocket.d.ts +0 -51
  103. package/packages/client/dist/websocket.js +0 -190
  104. package/packages/core/README.md +0 -7
  105. package/packages/core/dist/adapter.d.ts +0 -20
  106. package/packages/core/dist/adapter.js +0 -30
  107. package/packages/core/dist/atomic.d.ts +0 -2
  108. package/packages/core/dist/atomic.js +0 -28
  109. package/packages/core/dist/backup.d.ts +0 -27
  110. package/packages/core/dist/backup.js +0 -681
  111. package/packages/core/dist/beads.d.ts +0 -65
  112. package/packages/core/dist/beads.js +0 -213
  113. package/packages/core/dist/delegation.d.ts +0 -2
  114. package/packages/core/dist/delegation.js +0 -32
  115. package/packages/core/dist/errors.d.ts +0 -4
  116. package/packages/core/dist/errors.js +0 -11
  117. package/packages/core/dist/formulas.d.ts +0 -25
  118. package/packages/core/dist/formulas.js +0 -201
  119. package/packages/core/dist/hook-run.d.ts +0 -44
  120. package/packages/core/dist/hook-run.js +0 -60
  121. package/packages/core/dist/hooks.d.ts +0 -35
  122. package/packages/core/dist/hooks.js +0 -30
  123. package/packages/core/dist/index.d.ts +0 -31
  124. package/packages/core/dist/index.js +0 -65
  125. package/packages/core/dist/init.d.ts +0 -30
  126. package/packages/core/dist/init.js +0 -89
  127. package/packages/core/dist/library-edit.d.ts +0 -93
  128. package/packages/core/dist/library-edit.js +0 -368
  129. package/packages/core/dist/library.d.ts +0 -28
  130. package/packages/core/dist/library.js +0 -108
  131. package/packages/core/dist/log.d.ts +0 -37
  132. package/packages/core/dist/log.js +0 -156
  133. package/packages/core/dist/migrations.d.ts +0 -9
  134. package/packages/core/dist/migrations.js +0 -95
  135. package/packages/core/dist/models.d.ts +0 -11
  136. package/packages/core/dist/models.js +0 -44
  137. package/packages/core/dist/permissions.d.ts +0 -14
  138. package/packages/core/dist/permissions.js +0 -229
  139. package/packages/core/dist/profiles.d.ts +0 -27
  140. package/packages/core/dist/profiles.js +0 -122
  141. package/packages/core/dist/promptsafe.d.ts +0 -1
  142. package/packages/core/dist/promptsafe.js +0 -13
  143. package/packages/core/dist/providers.d.ts +0 -90
  144. package/packages/core/dist/providers.js +0 -101
  145. package/packages/core/dist/resolver.d.ts +0 -41
  146. package/packages/core/dist/resolver.js +0 -240
  147. package/packages/core/dist/safe-path.d.ts +0 -2
  148. package/packages/core/dist/safe-path.js +0 -24
  149. package/packages/core/dist/shell.d.ts +0 -16
  150. package/packages/core/dist/shell.js +0 -22
  151. package/packages/core/dist/state-schema.d.ts +0 -9
  152. package/packages/core/dist/state-schema.js +0 -54
  153. package/packages/core/dist/task-rows.d.ts +0 -47
  154. package/packages/core/dist/task-rows.js +0 -45
  155. package/packages/core/dist/tasks.d.ts +0 -128
  156. package/packages/core/dist/tasks.js +0 -835
  157. package/packages/core/dist/teams.d.ts +0 -80
  158. package/packages/core/dist/teams.js +0 -159
  159. package/packages/core/dist/template-path.d.ts +0 -2
  160. package/packages/core/dist/template-path.js +0 -10
  161. package/packages/core/dist/trust.d.ts +0 -8
  162. package/packages/core/dist/trust.js +0 -47
  163. package/packages/core/dist/types.d.ts +0 -51
  164. package/packages/core/dist/upgrade.d.ts +0 -10
  165. package/packages/core/dist/upgrade.js +0 -200
  166. package/packages/core/dist/user-settings.d.ts +0 -6
  167. package/packages/core/dist/user-settings.js +0 -72
  168. package/packages/core/dist/user-worktree.d.ts +0 -29
  169. package/packages/core/dist/user-worktree.js +0 -242
  170. package/packages/core/dist/validation.d.ts +0 -17
  171. package/packages/core/dist/validation.js +0 -296
  172. package/packages/core/dist/verify.d.ts +0 -15
  173. package/packages/core/dist/verify.js +0 -123
  174. package/packages/core/dist/workflow.d.ts +0 -45
  175. package/packages/core/dist/workflow.js +0 -33
  176. package/templates/ALP.md +0 -52
  177. package/templates/agents/lead/AGENT.md +0 -90
  178. package/templates/agents/main/AGENT.md +0 -150
  179. package/templates/agents/oracle/AGENT.md +0 -44
  180. package/templates/agents/peer/AGENT.md +0 -55
  181. package/templates/agents/reviewer/AGENT.md +0 -80
  182. package/templates/agents/supervisor/AGENT.md +0 -72
  183. package/templates/providers/antigravity.json +0 -6
  184. package/templates/providers/opencode.json +0 -7
  185. package/templates/role-skills.json +0 -8
  186. package/templates/skills/bug-loop/SKILL.md +0 -44
  187. package/templates/skills/bug-loop/references/test-proof.md +0 -25
  188. package/templates/skills/goal-griller/SKILL.md +0 -49
  189. package/templates/skills/prompt-leverage/SKILL.md +0 -37
  190. package/templates/skills/prompt-leverage/references/brief-template.md +0 -27
  191. package/templates/skills/sequence-execution-plan/SKILL.md +0 -42
  192. package/templates/skills/sequence-execution-plan/references/plan-template.md +0 -22
  193. package/templates/skills/smart-commits/SKILL.md +0 -40
  194. package/templates/skills/xia/SKILL.md +0 -42
  195. package/templates/teams/cafe/HOUSE_RULES.md +0 -1
  196. package/templates/teams/cafe/team.json +0 -15
  197. package/templates/teams/pho/HOUSE_RULES.md +0 -1
  198. package/templates/teams/pho/team.json +0 -14
@@ -1,37 +0,0 @@
1
- import * as acp from '@agentclientprotocol/sdk';
2
- import type { DaemonConnection } from '@anhlp/alp-protocol/connection';
3
- import type { HostMcpServer, TimelineItem } from '@anhlp/alp-protocol';
4
- /**
5
- * ALP as an ACP agent (ALPD §60): an editor such as Zed starts `alp acp` and speaks
6
- * the Agent Client Protocol on stdio. Each ACP session is a root in alpd; this process
7
- * only translates, as the Paseo plugin does. A session opens in alpd on its first
8
- * prompt, so the user can choose its team first; its tree keeps working in alpd after
9
- * the editor goes away, and session/load picks it up again.
10
- */
11
- export type AcpAgentOptions = {
12
- /** Opens a connection to alpd, starting it when needed. */
13
- connect(): Promise<DaemonConnection>;
14
- version: string;
15
- /** Where diagnostics go; stdout belongs to the protocol. */
16
- log?: (message: string, fields?: {
17
- code?: string;
18
- error?: unknown;
19
- }) => void;
20
- };
21
- /** ACP's MCP servers, as ALP hosts them beside the agents' own. */
22
- export declare function hostServers(servers?: acp.McpServer[]): Record<string, HostMcpServer>;
23
- /** A prompt's content as the text ALP gives the agent: files the editor attaches are named or inlined. */
24
- export declare function promptText(blocks: acp.ContentBlock[]): string;
25
- type ToolItem = Extract<TimelineItem, {
26
- kind: 'tool_call';
27
- }>;
28
- /** What kind of work a tool call is: from its typed detail, else its name or its command's first word (older Claude items are `Read {…}` commands). */
29
- export declare function toolKind(item: ToolItem): acp.ToolKind;
30
- export declare function toolTitle(item: ToolItem, cwd?: string): string;
31
- export declare function toolOutput(item: ToolItem): acp.ToolCallContent[];
32
- /** The call's input as the editor shows it raw: a command, a typed call's own fields without its long texts, or the tool's input. */
33
- export declare function toolInput(item: ToolItem): unknown;
34
- /** The file a typed call read or changed, for the editor to follow. */
35
- export declare function toolLocations(item: ToolItem): acp.ToolCallLocation[] | undefined;
36
- export declare function createAcpAgent(options: AcpAgentOptions): acp.AgentApp;
37
- export {};
@@ -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.4" : "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
- };