@anhlp/alp 0.0.1-beta.4 → 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.
- package/README.md +14 -412
- package/bin/alp.js +5 -0
- package/lib/launcher.js +136 -0
- package/package.json +18 -105
- package/dist/THIRD-PARTY-NOTICES.txt +0 -626
- package/dist/acp.js +0 -19936
- package/dist/alpd.js +0 -49562
- package/docs/modular-monorepo.md +0 -214
- package/packages/cli/README.md +0 -7
- package/packages/cli/dist/THIRD-PARTY-NOTICES.txt +0 -651
- package/packages/cli/dist/acp/agent.d.ts +0 -37
- package/packages/cli/dist/acp/agent.js +0 -489
- package/packages/cli/dist/acp/main.d.ts +0 -4
- package/packages/cli/dist/acp/main.js +0 -23
- package/packages/cli/dist/acp.js +0 -19936
- package/packages/cli/dist/alp-web/THIRD-PARTY-NOTICES.txt +0 -2590
- package/packages/cli/dist/alp-web/alp-boundary.json +0 -15
- package/packages/cli/dist/alp-web/apple-touch-icon.png +0 -0
- package/packages/cli/dist/alp-web/assets/TerminalPanel-DLuoa74B.css +0 -1
- package/packages/cli/dist/alp-web/assets/TerminalPanel-PrU2l3u5.js +0 -16
- package/packages/cli/dist/alp-web/assets/index-BJdBTDFX.css +0 -1
- package/packages/cli/dist/alp-web/assets/index-DpfiPv5s.js +0 -52
- package/packages/cli/dist/alp-web/favicon-attention.svg +0 -6
- package/packages/cli/dist/alp-web/favicon-running.svg +0 -6
- package/packages/cli/dist/alp-web/favicon.svg +0 -5
- package/packages/cli/dist/alp-web/index.html +0 -20
- package/packages/cli/dist/alp-web.js +0 -9640
- package/packages/cli/dist/alpd.js +0 -49562
- package/packages/cli/dist/backup.d.ts +0 -4
- package/packages/cli/dist/backup.js +0 -146
- package/packages/cli/dist/build-manifest.json +0 -240
- package/packages/cli/dist/cli.d.ts +0 -2
- package/packages/cli/dist/cli.js +0 -30
- package/packages/cli/dist/daemon.d.ts +0 -35
- package/packages/cli/dist/daemon.js +0 -126
- package/packages/cli/dist/index.d.ts +0 -1
- package/packages/cli/dist/index.js +0 -5
- package/packages/cli/dist/interfaces.d.ts +0 -24
- package/packages/cli/dist/interfaces.js +0 -156
- package/packages/cli/dist/library.d.ts +0 -140
- package/packages/cli/dist/library.js +0 -319
- package/packages/cli/dist/paths.d.ts +0 -3
- package/packages/cli/dist/paths.js +0 -18
- package/packages/cli/dist/projects.d.ts +0 -9
- package/packages/cli/dist/projects.js +0 -159
- package/packages/cli/dist/schedules.d.ts +0 -2
- package/packages/cli/dist/schedules.js +0 -45
- package/packages/cli/dist/sessions.d.ts +0 -43
- package/packages/cli/dist/sessions.js +0 -458
- package/packages/cli/dist/tasks.d.ts +0 -29
- package/packages/cli/dist/tasks.js +0 -345
- package/packages/cli/dist/terminal.d.ts +0 -8
- package/packages/cli/dist/terminal.js +0 -79
- package/packages/cli/dist/usage.d.ts +0 -3
- package/packages/cli/dist/usage.js +0 -78
- package/packages/cli/dist/web-service.d.ts +0 -38
- package/packages/cli/dist/web-service.js +0 -119
- package/packages/client/README.md +0 -29
- package/packages/client/dist/acp-probe.d.ts +0 -44
- package/packages/client/dist/acp-probe.js +0 -37
- package/packages/client/dist/antigravity-probe.d.ts +0 -34
- package/packages/client/dist/antigravity-probe.js +0 -95
- package/packages/client/dist/command.d.ts +0 -5
- package/packages/client/dist/command.js +0 -33
- package/packages/client/dist/doctor.d.ts +0 -13
- package/packages/client/dist/doctor.js +0 -587
- package/packages/client/dist/index.d.ts +0 -151
- package/packages/client/dist/index.js +0 -533
- package/packages/client/dist/integration-lock.d.ts +0 -35
- package/packages/client/dist/integration-lock.js +0 -179
- package/packages/client/dist/ipc.d.ts +0 -42
- package/packages/client/dist/ipc.js +0 -69
- package/packages/client/dist/library-test.d.ts +0 -94
- package/packages/client/dist/library-test.js +0 -37
- package/packages/client/dist/mcp-probe.d.ts +0 -24
- package/packages/client/dist/mcp-probe.js +0 -147
- package/packages/client/dist/portable.d.ts +0 -19
- package/packages/client/dist/portable.js +0 -33
- package/packages/client/dist/prompt.d.ts +0 -32
- package/packages/client/dist/prompt.js +0 -59
- package/packages/client/dist/render.d.ts +0 -6
- package/packages/client/dist/render.js +0 -120
- package/packages/client/dist/rpc/client.d.ts +0 -82
- package/packages/client/dist/rpc/client.js +0 -292
- package/packages/client/dist/rpc/errors.d.ts +0 -42
- package/packages/client/dist/rpc/errors.js +0 -34
- package/packages/client/dist/rpc/framing.d.ts +0 -21
- package/packages/client/dist/rpc/framing.js +0 -57
- package/packages/client/dist/rpc/handshake.d.ts +0 -19
- package/packages/client/dist/rpc/handshake.js +0 -8
- package/packages/client/dist/rpc/reconnect.d.ts +0 -97
- package/packages/client/dist/rpc/reconnect.js +0 -227
- package/packages/client/dist/rpc/registry.d.ts +0 -211
- package/packages/client/dist/rpc/registry.js +0 -145
- package/packages/client/dist/rpc/timers.d.ts +0 -11
- package/packages/client/dist/rpc/timers.js +0 -11
- package/packages/client/dist/service.d.ts +0 -50
- package/packages/client/dist/service.js +0 -143
- package/packages/client/dist/setup.d.ts +0 -2
- package/packages/client/dist/setup.js +0 -146
- package/packages/client/dist/supervise.d.ts +0 -14
- package/packages/client/dist/supervise.js +0 -87
- package/packages/client/dist/websocket.d.ts +0 -51
- package/packages/client/dist/websocket.js +0 -190
- package/packages/core/README.md +0 -7
- package/packages/core/dist/adapter.d.ts +0 -20
- package/packages/core/dist/adapter.js +0 -30
- package/packages/core/dist/atomic.d.ts +0 -2
- package/packages/core/dist/atomic.js +0 -28
- package/packages/core/dist/backup.d.ts +0 -27
- package/packages/core/dist/backup.js +0 -681
- package/packages/core/dist/beads.d.ts +0 -65
- package/packages/core/dist/beads.js +0 -213
- package/packages/core/dist/delegation.d.ts +0 -2
- package/packages/core/dist/delegation.js +0 -32
- package/packages/core/dist/errors.d.ts +0 -4
- package/packages/core/dist/errors.js +0 -11
- package/packages/core/dist/formulas.d.ts +0 -25
- package/packages/core/dist/formulas.js +0 -201
- package/packages/core/dist/hook-run.d.ts +0 -44
- package/packages/core/dist/hook-run.js +0 -60
- package/packages/core/dist/hooks.d.ts +0 -35
- package/packages/core/dist/hooks.js +0 -30
- package/packages/core/dist/index.d.ts +0 -31
- package/packages/core/dist/index.js +0 -65
- package/packages/core/dist/init.d.ts +0 -30
- package/packages/core/dist/init.js +0 -89
- package/packages/core/dist/library-edit.d.ts +0 -93
- package/packages/core/dist/library-edit.js +0 -368
- package/packages/core/dist/library.d.ts +0 -28
- package/packages/core/dist/library.js +0 -108
- package/packages/core/dist/log.d.ts +0 -37
- package/packages/core/dist/log.js +0 -156
- package/packages/core/dist/migrations.d.ts +0 -9
- package/packages/core/dist/migrations.js +0 -95
- package/packages/core/dist/models.d.ts +0 -11
- package/packages/core/dist/models.js +0 -44
- package/packages/core/dist/permissions.d.ts +0 -14
- package/packages/core/dist/permissions.js +0 -229
- package/packages/core/dist/profiles.d.ts +0 -27
- package/packages/core/dist/profiles.js +0 -122
- package/packages/core/dist/promptsafe.d.ts +0 -1
- package/packages/core/dist/promptsafe.js +0 -13
- package/packages/core/dist/providers.d.ts +0 -90
- package/packages/core/dist/providers.js +0 -101
- package/packages/core/dist/resolver.d.ts +0 -41
- package/packages/core/dist/resolver.js +0 -240
- package/packages/core/dist/safe-path.d.ts +0 -2
- package/packages/core/dist/safe-path.js +0 -24
- package/packages/core/dist/shell.d.ts +0 -16
- package/packages/core/dist/shell.js +0 -22
- package/packages/core/dist/state-schema.d.ts +0 -9
- package/packages/core/dist/state-schema.js +0 -54
- package/packages/core/dist/task-rows.d.ts +0 -47
- package/packages/core/dist/task-rows.js +0 -45
- package/packages/core/dist/tasks.d.ts +0 -128
- package/packages/core/dist/tasks.js +0 -835
- package/packages/core/dist/teams.d.ts +0 -80
- package/packages/core/dist/teams.js +0 -159
- package/packages/core/dist/template-path.d.ts +0 -2
- package/packages/core/dist/template-path.js +0 -10
- package/packages/core/dist/trust.d.ts +0 -8
- package/packages/core/dist/trust.js +0 -47
- package/packages/core/dist/types.d.ts +0 -51
- package/packages/core/dist/upgrade.d.ts +0 -10
- package/packages/core/dist/upgrade.js +0 -200
- package/packages/core/dist/user-settings.d.ts +0 -6
- package/packages/core/dist/user-settings.js +0 -72
- package/packages/core/dist/user-worktree.d.ts +0 -29
- package/packages/core/dist/user-worktree.js +0 -242
- package/packages/core/dist/validation.d.ts +0 -17
- package/packages/core/dist/validation.js +0 -296
- package/packages/core/dist/verify.d.ts +0 -15
- package/packages/core/dist/verify.js +0 -123
- package/packages/core/dist/workflow.d.ts +0 -45
- package/packages/core/dist/workflow.js +0 -33
- package/templates/ALP.md +0 -52
- package/templates/agents/lead/AGENT.md +0 -90
- package/templates/agents/main/AGENT.md +0 -150
- package/templates/agents/oracle/AGENT.md +0 -44
- package/templates/agents/peer/AGENT.md +0 -55
- package/templates/agents/reviewer/AGENT.md +0 -80
- package/templates/agents/supervisor/AGENT.md +0 -72
- package/templates/providers/antigravity.json +0 -6
- package/templates/providers/opencode.json +0 -7
- package/templates/role-skills.json +0 -8
- package/templates/skills/bug-loop/SKILL.md +0 -44
- package/templates/skills/bug-loop/references/test-proof.md +0 -25
- package/templates/skills/goal-griller/SKILL.md +0 -49
- package/templates/skills/prompt-leverage/SKILL.md +0 -37
- package/templates/skills/prompt-leverage/references/brief-template.md +0 -27
- package/templates/skills/sequence-execution-plan/SKILL.md +0 -42
- package/templates/skills/sequence-execution-plan/references/plan-template.md +0 -22
- package/templates/skills/smart-commits/SKILL.md +0 -40
- package/templates/skills/xia/SKILL.md +0 -42
- package/templates/teams/cafe/HOUSE_RULES.md +0 -1
- package/templates/teams/cafe/team.json +0 -15
- package/templates/teams/pho/HOUSE_RULES.md +0 -1
- 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,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
|
-
};
|