@anhlp/alp 0.0.0-beta.1 → 0.0.0-beta.2
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 +6 -4
- package/dist/acp.js +119 -22
- package/dist/alpd.js +1867 -741
- package/docs/modular-monorepo.md +2 -2
- package/package.json +5 -5
- package/packages/cli/dist/acp/agent.d.ts +11 -3
- package/packages/cli/dist/acp/agent.js +118 -19
- package/packages/cli/dist/acp/main.js +1 -1
- package/packages/cli/dist/acp.js +119 -22
- package/packages/cli/dist/alpd.js +1867 -741
- package/packages/cli/dist/backup.js +1 -1
- package/packages/cli/dist/build-manifest.json +4 -1
- package/packages/cli/dist/sessions.d.ts +2 -0
- package/packages/cli/dist/sessions.js +23 -1
- package/packages/core/dist/init.js +2 -1
- package/packages/core/dist/teams.d.ts +13 -2
- package/packages/core/dist/workflow.d.ts +35 -4
- package/packages/core/dist/workflow.js +14 -5
package/docs/modular-monorepo.md
CHANGED
|
@@ -53,7 +53,7 @@ npm run build --workspace=@anhlp/alp-runtime
|
|
|
53
53
|
npm run test --workspace=@anhlp/alp-runtime
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Root `npm run build` retains the neutral legacy bundle paths and builds the CLI without Paseo. `npm run build:paseo` builds the optional plugin. Workspace builds compile the selected package and its dependencies, preserving individual module imports and emitting declarations. The `alp-source` export condition is only for repository builds/typechecking; published consumers use ordinary JavaScript and declaration exports.
|
|
56
|
+
Root `npm run build` retains the neutral legacy bundle paths and builds the CLI without Paseo. `npm run build:paseo` builds the optional plugin. Workspace builds compile the selected package and its dependencies, preserving individual module imports and emitting declarations. The `alp-source` export condition is only for repository builds/typechecking; published consumers use ordinary JavaScript and declaration exports. The Paseo plugin `@anhlp/alp-paseo` is the exception: it ships no declarations, since Paseo installs an `npm:` plugin without its peers and resolves every type import ([#111](https://github.com/phucanh08/alp/issues/111)).
|
|
57
57
|
|
|
58
58
|
## Test layout
|
|
59
59
|
|
|
@@ -138,7 +138,7 @@ Restarting is non-destructive. `alp daemon restart` (or the plugin's supervision
|
|
|
138
138
|
|
|
139
139
|
## Independent versions and publishing
|
|
140
140
|
|
|
141
|
-
> **For the beta `0.0.0-beta.
|
|
141
|
+
> **For the beta `0.0.0-beta.2`, do not use the commands in this section.** The beta gives all
|
|
142
142
|
> eight published packages one version, packs them once and publishes those tarballs with
|
|
143
143
|
> `--tag beta`, following the manual checklist in [beta-scope.md](alp/beta-scope.md) §7. The
|
|
144
144
|
> `npm publish` below has no `--tag`, so it would set `latest`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anhlp/alp",
|
|
3
|
-
"version": "0.0.0-beta.
|
|
3
|
+
"version": "0.0.0-beta.2",
|
|
4
4
|
"description": "ALP command line and alpd, the per-user daemon that runs filesystem-defined Codex and Claude Code agent teams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"build:alp-web": "npm run build --workspace @anhlp/alp-web",
|
|
68
68
|
"typecheck:alp-web": "npm run typecheck --workspace @anhlp/alp-web",
|
|
69
69
|
"test:alp-web": "npm test --workspace @anhlp/alp-web",
|
|
70
|
-
"test:alp-web:e2e": "
|
|
70
|
+
"test:alp-web:e2e": "npm run test:e2e --workspace @anhlp/alp-web",
|
|
71
71
|
"validate:web": "node scripts/validate.mjs web"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
@@ -91,9 +91,9 @@
|
|
|
91
91
|
"zod": "4.4.3"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@anhlp/alp-client": "0.0.0-beta.
|
|
95
|
-
"@anhlp/alp-core": "0.0.0-beta.
|
|
96
|
-
"@anhlp/alp-protocol": "0.0.0-beta.
|
|
94
|
+
"@anhlp/alp-client": "0.0.0-beta.2",
|
|
95
|
+
"@anhlp/alp-core": "0.0.0-beta.2",
|
|
96
|
+
"@anhlp/alp-protocol": "0.0.0-beta.2",
|
|
97
97
|
"@anthropic-ai/claude-agent-sdk": "^0.3.292",
|
|
98
98
|
"smol-toml": "1.9.0"
|
|
99
99
|
},
|
|
@@ -22,8 +22,16 @@ export type AcpAgentOptions = {
|
|
|
22
22
|
export declare function hostServers(servers?: acp.McpServer[]): Record<string, HostMcpServer>;
|
|
23
23
|
/** A prompt's content as the text ALP gives the agent: files the editor attaches are named or inlined. */
|
|
24
24
|
export declare function promptText(blocks: acp.ContentBlock[]): string;
|
|
25
|
-
|
|
26
|
-
export declare function toolKind(item: Extract<TimelineItem, {
|
|
25
|
+
type ToolItem = Extract<TimelineItem, {
|
|
27
26
|
kind: 'tool_call';
|
|
28
|
-
}
|
|
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;
|
|
29
36
|
export declare function createAcpAgent(options: AcpAgentOptions): acp.AgentApp;
|
|
37
|
+
export {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as acp from "@agentclientprotocol/sdk";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
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) };
|
|
5
6
|
var MODES = [
|
|
6
7
|
{ id: "read-only", name: "Read only", description: "Agents read and search; nothing is changed" },
|
|
7
8
|
{ id: "workspace-write", name: "Workspace write", description: "Agents change files and run commands inside the project" },
|
|
@@ -43,34 +44,120 @@ ${resource.text}
|
|
|
43
44
|
}
|
|
44
45
|
return text.trim();
|
|
45
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
|
+
}
|
|
46
52
|
function toolKind(item) {
|
|
47
|
-
const
|
|
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;
|
|
48
71
|
if (/^(Read|NotebookRead|LS)$/.test(word)) return "read";
|
|
49
72
|
if (/^(Edit|MultiEdit|Write|NotebookEdit)$/.test(word)) return "edit";
|
|
50
73
|
if (/^(Grep|Glob)$/.test(word)) return "search";
|
|
51
74
|
if (/^(WebFetch|WebSearch)$/.test(word)) return "fetch";
|
|
52
75
|
if (/^(Skill|Task|TodoWrite)$/.test(word) || item.name.startsWith("alp_")) return "other";
|
|
53
|
-
return
|
|
76
|
+
return detail.type === "shell" ? "execute" : "other";
|
|
54
77
|
}
|
|
55
|
-
function toolTitle(item) {
|
|
56
|
-
|
|
57
|
-
|
|
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 ?? {};
|
|
58
101
|
if (item.name === "alp_delegate") return clip(`Delegate to ${input.agent ?? "an agent"}: ${String(input.task ?? "").split("\n")[0]}`, 160);
|
|
59
102
|
return clip(`${item.name} ${JSON.stringify(input)}`, 160);
|
|
60
103
|
}
|
|
104
|
+
var bounded = (text) => text.length > OUTPUT_CHARS ? `${text.slice(0, OUTPUT_CHARS)}
|
|
105
|
+
\u2026 (${text.length - OUTPUT_CHARS} more characters)` : text;
|
|
61
106
|
function toolOutput(item) {
|
|
107
|
+
const detail = item.detail;
|
|
108
|
+
const parts = [];
|
|
62
109
|
let text = "";
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
+
}
|
|
67
143
|
}
|
|
68
144
|
if (item.error) text = text ? `${text}
|
|
69
145
|
${item.error}` : item.error;
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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 } : {} }];
|
|
74
161
|
}
|
|
75
162
|
var clip = (text, chars) => text.length > chars ? `${text.slice(0, chars - 1)}\u2026` : text;
|
|
76
163
|
var status = (state) => state === "running" ? "in_progress" : state;
|
|
@@ -109,7 +196,7 @@ function createAcpAgent(options) {
|
|
|
109
196
|
}
|
|
110
197
|
const say = (session, text) => send(session, { sessionUpdate: "agent_message_chunk", content: { type: "text", text } });
|
|
111
198
|
function configOptions(session) {
|
|
112
|
-
const teams = session.opened ? session.
|
|
199
|
+
const teams = session.opened ? choices(session).filter((team) => team.id === session.preview.team) : choices(session);
|
|
113
200
|
return [
|
|
114
201
|
{
|
|
115
202
|
id: "team",
|
|
@@ -118,14 +205,21 @@ function createAcpAgent(options) {
|
|
|
118
205
|
type: "select",
|
|
119
206
|
currentValue: session.preview.team,
|
|
120
207
|
description: session.opened ? "A session keeps its team; start a new one for another team" : "The team that works on this session",
|
|
121
|
-
options:
|
|
208
|
+
options: teams.map((team) => ({ value: team.id, name: team.label, ...team.description ? { description: team.description } : {} }))
|
|
122
209
|
},
|
|
123
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 })) }
|
|
124
211
|
];
|
|
125
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
|
+
}
|
|
126
219
|
const modeState = (session) => ({ currentModeId: session.preview.mode, availableModes: MODES });
|
|
127
220
|
function track(id, cwd, mcpServers, preview2, opened) {
|
|
128
|
-
const
|
|
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() };
|
|
129
223
|
sessions.set(id, session);
|
|
130
224
|
return session;
|
|
131
225
|
}
|
|
@@ -223,7 +317,8 @@ ${item2.text}`;
|
|
|
223
317
|
send(session, { sessionUpdate: "plan", entries: item2.items.map((entry) => ({ content: entry.text, priority: "medium", status: entry.status })) });
|
|
224
318
|
return;
|
|
225
319
|
case "tool_call": {
|
|
226
|
-
const
|
|
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 } : {} };
|
|
227
322
|
if (session.tools.has(item2.id)) send(session, { sessionUpdate: "tool_call_update", toolCallId: item2.id, ...fields });
|
|
228
323
|
else {
|
|
229
324
|
session.tools.add(item2.id);
|
|
@@ -356,7 +451,7 @@ ${item2.text}`;
|
|
|
356
451
|
if (params.configId === "mode") await setMode(session, value);
|
|
357
452
|
else if (params.configId === "team") {
|
|
358
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");
|
|
359
|
-
if (!session.
|
|
454
|
+
if (!choices(session).some((team) => team.id === value)) throw acp.RequestError.invalidParams(void 0, `Unknown team ${value}`);
|
|
360
455
|
session.preview = { ...await preview(session.cwd, session.mcpServers, value), mode: session.preview.mode };
|
|
361
456
|
} else throw acp.RequestError.invalidParams(void 0, `Unknown option ${params.configId}`);
|
|
362
457
|
return { configOptions: configOptions(session) };
|
|
@@ -386,5 +481,9 @@ export {
|
|
|
386
481
|
createAcpAgent,
|
|
387
482
|
hostServers,
|
|
388
483
|
promptText,
|
|
389
|
-
|
|
484
|
+
toolInput,
|
|
485
|
+
toolKind,
|
|
486
|
+
toolLocations,
|
|
487
|
+
toolOutput,
|
|
488
|
+
toolTitle
|
|
390
489
|
};
|
|
@@ -6,7 +6,7 @@ import { startDaemon } from "@anhlp/alp-client/supervise";
|
|
|
6
6
|
import { createLogger } from "@anhlp/alp-core/log";
|
|
7
7
|
import { createAcpAgent } from "./agent.js";
|
|
8
8
|
async function runAcp({ daemonEntry }) {
|
|
9
|
-
const version = true ? "0.0.0-beta.
|
|
9
|
+
const version = true ? "0.0.0-beta.2" : "0.0.0";
|
|
10
10
|
const logger = createLogger({ component: "acp" });
|
|
11
11
|
const log = (message, fields) => logger.warn(message, fields);
|
|
12
12
|
const app = createAcpAgent({
|
package/packages/cli/dist/acp.js
CHANGED
|
@@ -18552,8 +18552,10 @@ var AlpRpcError = class extends Error {
|
|
|
18552
18552
|
};
|
|
18553
18553
|
var ERROR = Object.freeze({ failed: 1e3, notFound: 1001, forbidden: 1003, conflict: 1004, unavailable: 1005, protocol: 1006 });
|
|
18554
18554
|
var RPC_ERROR = Object.freeze({ parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602 });
|
|
18555
|
-
var CAPABILITY = Object.freeze({ attachAfter: "attach.after", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", settingsLanguage: "settings.language" });
|
|
18556
|
-
var
|
|
18555
|
+
var CAPABILITY = Object.freeze({ attachAfter: "attach.after", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images" });
|
|
18556
|
+
var PROMPT_IMAGES = Object.freeze({ mimeTypes: Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]), maxCount: 8, maxBytes: 5 * 1024 * 1024, maxTotalBytes: 11 * 1024 * 1024 });
|
|
18557
|
+
var RPC_METHODS = Object.freeze(["assignment.recall", "board.list", "catalog.get", "daemon.hello", "daemon.pause", "daemon.pauses", "daemon.resume", "daemon.shutdown", "daemon.status", "daemon.usage", "extension.register", "extension.unregister", "library.delete", "library.duplicate", "library.get", "library.list", "library.models", "library.rename", "library.save", "library.skills.set", "question.answer", "question.list", "question.watch", "session.archive", "session.attach", "session.close", "session.configure", "session.create", "session.get", "session.interrupt", "session.list", "session.log", "session.message", "session.preview", "session.prompt", "session.release", "session.rename", "session.status", "settings.language.get", "settings.language.set", "task.create", "task.gate.resolve", "task.list", "task.update", "team.list", "workspace.files.list", "workspace.files.read", "workspace.files.search"]);
|
|
18558
|
+
var WORKSPACE_FILES = Object.freeze({ maxReadBytes: 1024 * 1024, maxListEntries: 1e3, maxPathLength: 4096 });
|
|
18557
18559
|
|
|
18558
18560
|
// packages/client/src/rpc/errors.js
|
|
18559
18561
|
var BRAND = Symbol.for("@anhlp/alp-client/AlpClientError");
|
|
@@ -19323,6 +19325,7 @@ async function startDaemon({ home, entry, env = process.env, timeoutMs = 15e3 })
|
|
|
19323
19325
|
// packages/cli/src/acp/agent.ts
|
|
19324
19326
|
import { randomUUID } from "node:crypto";
|
|
19325
19327
|
import path4 from "node:path";
|
|
19328
|
+
var unlistedOf = ({ teams, team, teamLabel }) => teams.some((entry) => entry.id === team) ? void 0 : { id: team, label: teamLabel ?? (team === "custom" ? "Custom" : team) };
|
|
19326
19329
|
var MODES = [
|
|
19327
19330
|
{ id: "read-only", name: "Read only", description: "Agents read and search; nothing is changed" },
|
|
19328
19331
|
{ id: "workspace-write", name: "Workspace write", description: "Agents change files and run commands inside the project" },
|
|
@@ -19364,34 +19367,120 @@ ${resource.text}
|
|
|
19364
19367
|
}
|
|
19365
19368
|
return text.trim();
|
|
19366
19369
|
}
|
|
19370
|
+
function shown(file2, cwd) {
|
|
19371
|
+
if (!cwd || !path4.isAbsolute(file2)) return file2;
|
|
19372
|
+
const relative = path4.relative(cwd, file2);
|
|
19373
|
+
return relative && !relative.startsWith("..") && !path4.isAbsolute(relative) ? relative : file2;
|
|
19374
|
+
}
|
|
19367
19375
|
function toolKind(item) {
|
|
19368
|
-
const
|
|
19376
|
+
const detail = item.detail;
|
|
19377
|
+
switch (detail.type) {
|
|
19378
|
+
case "read":
|
|
19379
|
+
return "read";
|
|
19380
|
+
case "edit":
|
|
19381
|
+
case "write":
|
|
19382
|
+
return "edit";
|
|
19383
|
+
case "search":
|
|
19384
|
+
return detail.toolName === "web_search" ? "fetch" : "search";
|
|
19385
|
+
case "fetch":
|
|
19386
|
+
return "fetch";
|
|
19387
|
+
case "plan":
|
|
19388
|
+
return "think";
|
|
19389
|
+
case "sub_agent":
|
|
19390
|
+
case "plain_text":
|
|
19391
|
+
return "other";
|
|
19392
|
+
}
|
|
19393
|
+
const word = detail.type === "shell" ? String(detail.command ?? "").trim().split(/[\s{(]/)[0] : item.name;
|
|
19369
19394
|
if (/^(Read|NotebookRead|LS)$/.test(word)) return "read";
|
|
19370
19395
|
if (/^(Edit|MultiEdit|Write|NotebookEdit)$/.test(word)) return "edit";
|
|
19371
19396
|
if (/^(Grep|Glob)$/.test(word)) return "search";
|
|
19372
19397
|
if (/^(WebFetch|WebSearch)$/.test(word)) return "fetch";
|
|
19373
19398
|
if (/^(Skill|Task|TodoWrite)$/.test(word) || item.name.startsWith("alp_")) return "other";
|
|
19374
|
-
return
|
|
19375
|
-
}
|
|
19376
|
-
function toolTitle(item) {
|
|
19377
|
-
|
|
19378
|
-
|
|
19399
|
+
return detail.type === "shell" ? "execute" : "other";
|
|
19400
|
+
}
|
|
19401
|
+
function toolTitle(item, cwd) {
|
|
19402
|
+
const detail = item.detail;
|
|
19403
|
+
switch (detail.type) {
|
|
19404
|
+
case "shell":
|
|
19405
|
+
return clip(String(detail.command ?? "").split("\n")[0], 160);
|
|
19406
|
+
case "read":
|
|
19407
|
+
return clip(`Read ${shown(detail.filePath, cwd)}`, 160);
|
|
19408
|
+
case "edit":
|
|
19409
|
+
return clip(`Edit ${shown(detail.filePath, cwd)}`, 160);
|
|
19410
|
+
case "write":
|
|
19411
|
+
return clip(`Write ${shown(detail.filePath, cwd)}`, 160);
|
|
19412
|
+
case "search":
|
|
19413
|
+
return clip(`${detail.toolName === "web_search" ? "Search the web" : "Search"} ${detail.query}`, 160);
|
|
19414
|
+
case "fetch":
|
|
19415
|
+
return clip(`Fetch ${detail.url}`, 160);
|
|
19416
|
+
case "sub_agent":
|
|
19417
|
+
return clip(`${detail.subAgentType ?? "Sub-agent"}${detail.description ? `: ${detail.description.split("\n")[0]}` : ""}`, 160);
|
|
19418
|
+
case "plan":
|
|
19419
|
+
return "Plan";
|
|
19420
|
+
case "plain_text":
|
|
19421
|
+
return clip(`${item.name}${detail.label ? `: ${detail.label}` : ""}`, 160);
|
|
19422
|
+
}
|
|
19423
|
+
const input = detail.input ?? {};
|
|
19379
19424
|
if (item.name === "alp_delegate") return clip(`Delegate to ${input.agent ?? "an agent"}: ${String(input.task ?? "").split("\n")[0]}`, 160);
|
|
19380
19425
|
return clip(`${item.name} ${JSON.stringify(input)}`, 160);
|
|
19381
19426
|
}
|
|
19427
|
+
var bounded = (text) => text.length > OUTPUT_CHARS ? `${text.slice(0, OUTPUT_CHARS)}
|
|
19428
|
+
\u2026 (${text.length - OUTPUT_CHARS} more characters)` : text;
|
|
19382
19429
|
function toolOutput(item) {
|
|
19430
|
+
const detail = item.detail;
|
|
19431
|
+
const parts = [];
|
|
19383
19432
|
let text = "";
|
|
19384
|
-
|
|
19385
|
-
|
|
19386
|
-
|
|
19387
|
-
|
|
19433
|
+
switch (detail.type) {
|
|
19434
|
+
case "shell":
|
|
19435
|
+
text = detail.output ?? "";
|
|
19436
|
+
break;
|
|
19437
|
+
case "read":
|
|
19438
|
+
text = detail.content ?? "";
|
|
19439
|
+
break;
|
|
19440
|
+
case "edit":
|
|
19441
|
+
if (detail.newString !== void 0 || detail.oldString !== void 0) parts.push({ type: "diff", path: detail.filePath, oldText: detail.oldString ?? null, newText: detail.newString ?? "" });
|
|
19442
|
+
else if (detail.unifiedDiff) text = "```diff\n" + detail.unifiedDiff + (detail.unifiedDiff.endsWith("\n") ? "" : "\n") + "```";
|
|
19443
|
+
break;
|
|
19444
|
+
case "write":
|
|
19445
|
+
if (detail.content !== void 0) parts.push({ type: "diff", path: detail.filePath, oldText: null, newText: detail.content });
|
|
19446
|
+
break;
|
|
19447
|
+
case "search":
|
|
19448
|
+
text = detail.content ?? [...detail.filePaths ?? [], ...(detail.webResults ?? []).map((hit) => `${hit.title} \u2014 ${hit.url}`)].join("\n");
|
|
19449
|
+
break;
|
|
19450
|
+
case "fetch":
|
|
19451
|
+
text = detail.result ?? "";
|
|
19452
|
+
break;
|
|
19453
|
+
case "sub_agent":
|
|
19454
|
+
text = detail.log || detail.description || "";
|
|
19455
|
+
break;
|
|
19456
|
+
case "plan":
|
|
19457
|
+
text = detail.text;
|
|
19458
|
+
break;
|
|
19459
|
+
case "plain_text":
|
|
19460
|
+
text = detail.text ?? "";
|
|
19461
|
+
break;
|
|
19462
|
+
default: {
|
|
19463
|
+
const output = detail.output;
|
|
19464
|
+
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);
|
|
19465
|
+
}
|
|
19388
19466
|
}
|
|
19389
19467
|
if (item.error) text = text ? `${text}
|
|
19390
19468
|
${item.error}` : item.error;
|
|
19391
|
-
if (
|
|
19392
|
-
|
|
19393
|
-
|
|
19394
|
-
|
|
19469
|
+
if (text) parts.push({ type: "content", content: { type: "text", text: bounded(text) } });
|
|
19470
|
+
return parts.map((part) => part.type === "diff" ? { ...part, newText: bounded(part.newText), ...part.oldText ? { oldText: bounded(part.oldText) } : {} } : part);
|
|
19471
|
+
}
|
|
19472
|
+
function toolInput(item) {
|
|
19473
|
+
const detail = item.detail;
|
|
19474
|
+
if (detail.type === "shell") return { command: detail.command, cwd: detail.cwd };
|
|
19475
|
+
if (detail.type === "unknown" || !["read", "edit", "write", "search", "fetch", "sub_agent", "plan", "plain_text"].includes(detail.type)) return detail.input;
|
|
19476
|
+
const { type: _type, content: _content, oldString: _old, newString: _new, unifiedDiff: _diff, result: _result, log: _log, text: _text, filePaths: _paths, webResults: _hits, ...rest } = detail;
|
|
19477
|
+
return rest;
|
|
19478
|
+
}
|
|
19479
|
+
function toolLocations(item) {
|
|
19480
|
+
const detail = item.detail;
|
|
19481
|
+
if (detail.type !== "read" && detail.type !== "edit" && detail.type !== "write") return void 0;
|
|
19482
|
+
const filePath = String(detail.filePath);
|
|
19483
|
+
return [{ path: filePath, ...detail.type === "read" && typeof detail.offset === "number" ? { line: detail.offset } : {} }];
|
|
19395
19484
|
}
|
|
19396
19485
|
var clip = (text, chars) => text.length > chars ? `${text.slice(0, chars - 1)}\u2026` : text;
|
|
19397
19486
|
var status = (state) => state === "running" ? "in_progress" : state;
|
|
@@ -19430,7 +19519,7 @@ function createAcpAgent(options) {
|
|
|
19430
19519
|
}
|
|
19431
19520
|
const say = (session, text) => send(session, { sessionUpdate: "agent_message_chunk", content: { type: "text", text } });
|
|
19432
19521
|
function configOptions(session) {
|
|
19433
|
-
const teams = session.opened ? session.
|
|
19522
|
+
const teams = session.opened ? choices(session).filter((team) => team.id === session.preview.team) : choices(session);
|
|
19434
19523
|
return [
|
|
19435
19524
|
{
|
|
19436
19525
|
id: "team",
|
|
@@ -19439,14 +19528,21 @@ function createAcpAgent(options) {
|
|
|
19439
19528
|
type: "select",
|
|
19440
19529
|
currentValue: session.preview.team,
|
|
19441
19530
|
description: session.opened ? "A session keeps its team; start a new one for another team" : "The team that works on this session",
|
|
19442
|
-
options:
|
|
19531
|
+
options: teams.map((team) => ({ value: team.id, name: team.label, ...team.description ? { description: team.description } : {} }))
|
|
19443
19532
|
},
|
|
19444
19533
|
{ 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 })) }
|
|
19445
19534
|
];
|
|
19446
19535
|
}
|
|
19536
|
+
function choices(session) {
|
|
19537
|
+
const { teams, team } = session.preview;
|
|
19538
|
+
const own2 = unlistedOf(session.preview);
|
|
19539
|
+
const first = session.unlisted && session.unlisted.id !== team && !teams.some((entry) => entry.id === session.unlisted.id) ? [session.unlisted] : [];
|
|
19540
|
+
return [...first, ...own2 ? [own2] : [], ...teams];
|
|
19541
|
+
}
|
|
19447
19542
|
const modeState = (session) => ({ currentModeId: session.preview.mode, availableModes: MODES });
|
|
19448
19543
|
function track(id, cwd, mcpServers, preview2, opened) {
|
|
19449
|
-
const
|
|
19544
|
+
const unlisted = unlistedOf(preview2);
|
|
19545
|
+
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() };
|
|
19450
19546
|
sessions.set(id, session);
|
|
19451
19547
|
return session;
|
|
19452
19548
|
}
|
|
@@ -19544,7 +19640,8 @@ ${item2.text}`;
|
|
|
19544
19640
|
send(session, { sessionUpdate: "plan", entries: item2.items.map((entry) => ({ content: entry.text, priority: "medium", status: entry.status })) });
|
|
19545
19641
|
return;
|
|
19546
19642
|
case "tool_call": {
|
|
19547
|
-
const
|
|
19643
|
+
const locations = toolLocations(item2);
|
|
19644
|
+
const fields = { title: toolTitle(item2, session.cwd), kind: toolKind(item2), status: status(item2.status), content: toolOutput(item2), rawInput: toolInput(item2), ...locations ? { locations } : {} };
|
|
19548
19645
|
if (session.tools.has(item2.id)) send(session, { sessionUpdate: "tool_call_update", toolCallId: item2.id, ...fields });
|
|
19549
19646
|
else {
|
|
19550
19647
|
session.tools.add(item2.id);
|
|
@@ -19677,7 +19774,7 @@ ${item2.text}`;
|
|
|
19677
19774
|
if (params.configId === "mode") await setMode(session, value);
|
|
19678
19775
|
else if (params.configId === "team") {
|
|
19679
19776
|
if (session.opened && value !== session.preview.team) throw RequestError.invalidParams(void 0, "A session keeps its team; start a new one for another team");
|
|
19680
|
-
if (!session.
|
|
19777
|
+
if (!choices(session).some((team) => team.id === value)) throw RequestError.invalidParams(void 0, `Unknown team ${value}`);
|
|
19681
19778
|
session.preview = { ...await preview(session.cwd, session.mcpServers, value), mode: session.preview.mode };
|
|
19682
19779
|
} else throw RequestError.invalidParams(void 0, `Unknown option ${params.configId}`);
|
|
19683
19780
|
return { configOptions: configOptions(session) };
|
|
@@ -19706,7 +19803,7 @@ ${item2.text}`;
|
|
|
19706
19803
|
|
|
19707
19804
|
// packages/cli/src/acp/main.ts
|
|
19708
19805
|
async function runAcp({ daemonEntry }) {
|
|
19709
|
-
const version2 = true ? "0.0.0-beta.
|
|
19806
|
+
const version2 = true ? "0.0.0-beta.2" : "0.0.0";
|
|
19710
19807
|
const logger = createLogger({ component: "acp" });
|
|
19711
19808
|
const log = (message, fields) => logger.warn(message, fields);
|
|
19712
19809
|
const app = createAcpAgent({
|