@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
|
@@ -7,7 +7,7 @@ import { acquireDaemonLock, alpHome, assertPrivateDirectory, daemonPaths, lockAl
|
|
|
7
7
|
import { daemonHeld, holdDaemon, releaseHold } from "@anhlp/alp-client/supervise";
|
|
8
8
|
import { start, stop } from "./daemon.js";
|
|
9
9
|
import { UsageError } from "./usage.js";
|
|
10
|
-
var VERSION = true ? "0.0.0-beta.
|
|
10
|
+
var VERSION = true ? "0.0.0-beta.2" : "dev";
|
|
11
11
|
var BY = `alp ${VERSION}`;
|
|
12
12
|
var size = (bytes) => bytes >= 1024 * 1024 ? `${(bytes / 1024 / 1024).toFixed(1)} MB` : bytes >= 1024 ? `${(bytes / 1024).toFixed(1)} KB` : `${bytes} B`;
|
|
13
13
|
var stamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.0-beta.
|
|
2
|
+
"version": "0.0.0-beta.2",
|
|
3
3
|
"entries": {
|
|
4
4
|
"packages/cli/dist/alpd.js": {
|
|
5
5
|
"inputs": [
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"packages/core/src/validation.js",
|
|
45
45
|
"packages/core/src/verify.js",
|
|
46
46
|
"packages/core/src/workflow.js",
|
|
47
|
+
"packages/daemon/src/files.ts",
|
|
48
|
+
"packages/daemon/src/gitignore.ts",
|
|
47
49
|
"packages/daemon/src/lock.ts",
|
|
48
50
|
"packages/daemon/src/main.ts",
|
|
49
51
|
"packages/daemon/src/operations.ts",
|
|
@@ -77,6 +79,7 @@
|
|
|
77
79
|
"packages/runtime/src/runtime-context.ts",
|
|
78
80
|
"packages/runtime/src/runtime.ts",
|
|
79
81
|
"packages/runtime/src/session-config.ts",
|
|
82
|
+
"packages/runtime/src/tool-detail.ts",
|
|
80
83
|
"packages/runtime/src/tool-results.ts",
|
|
81
84
|
"packages/runtime/src/tools.ts",
|
|
82
85
|
"packages/runtime/src/transport.ts",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** One line for a finished tool call: its command, or what a typed call touched (ALPD §4.2). */
|
|
2
|
+
export function toolLine(item: any): any;
|
|
1
3
|
export function printer(json: any): (envelope: any) => void;
|
|
2
4
|
/** Reads answers to questions for the user from the terminal, one question at a time. */
|
|
3
5
|
export function answerer(client: any): {
|
|
@@ -9,6 +9,27 @@ import { connect } from "@anhlp/alp-client/index";
|
|
|
9
9
|
import { UsageError } from "./usage.js";
|
|
10
10
|
import { start, running, stop } from "./daemon.js";
|
|
11
11
|
import { taskProject } from "./tasks.js";
|
|
12
|
+
function toolLine(item) {
|
|
13
|
+
const detail = item.detail ?? {};
|
|
14
|
+
const first = (text) => String(text ?? "").split("\n")[0].slice(0, 200);
|
|
15
|
+
switch (detail.type) {
|
|
16
|
+
case "shell":
|
|
17
|
+
return detail.command;
|
|
18
|
+
case "read":
|
|
19
|
+
case "edit":
|
|
20
|
+
case "write":
|
|
21
|
+
return `${item.name} ${detail.filePath}`;
|
|
22
|
+
case "search":
|
|
23
|
+
return `${item.name} ${first(detail.query)}`;
|
|
24
|
+
case "fetch":
|
|
25
|
+
return `${item.name} ${detail.url}`;
|
|
26
|
+
case "sub_agent":
|
|
27
|
+
return `${item.name}${detail.description ? ` ${first(detail.description)}` : ""}`;
|
|
28
|
+
case "plain_text":
|
|
29
|
+
return `${item.name}${detail.label ? ` ${detail.label}` : ""}`;
|
|
30
|
+
}
|
|
31
|
+
return item.name === "alp_delegate" ? `delegate \u2192 ${detail.input?.agent}` : item.name;
|
|
32
|
+
}
|
|
12
33
|
function printer(json) {
|
|
13
34
|
const depth = /* @__PURE__ */ new Map();
|
|
14
35
|
const agents = /* @__PURE__ */ new Map();
|
|
@@ -31,7 +52,7 @@ function printer(json) {
|
|
|
31
52
|
latest.set(item.id, { sessionId, item });
|
|
32
53
|
if (item.kind === "tool_call" && item.status !== "running" && !printed.has(item.id)) {
|
|
33
54
|
printed.add(item.id);
|
|
34
|
-
const detail = item
|
|
55
|
+
const detail = toolLine(item);
|
|
35
56
|
line(sessionId, ` ${item.status === "failed" ? "\u2717" : "\u2713"} ${detail}`);
|
|
36
57
|
}
|
|
37
58
|
if (item.kind === "notice") line(sessionId, ` ${item.level === "error" ? "\u203C" : item.level === "warning" ? "!" : "\u2139"} ${item.text}`);
|
|
@@ -421,5 +442,6 @@ export {
|
|
|
421
442
|
resume,
|
|
422
443
|
run,
|
|
423
444
|
send,
|
|
445
|
+
toolLine,
|
|
424
446
|
top
|
|
425
447
|
};
|
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { projectProfiles, profileDefaults, syncTemplateProfiles } from "./profiles.js";
|
|
7
7
|
import { validateSettings } from "./validation.js";
|
|
8
|
+
import { INIT_TEAM } from "./workflow.js";
|
|
8
9
|
async function initProject(projectRoot, { templateRoot, templates, library, syncProfiles = true, onlyManagedProfiles = false, teamIds } = {}) {
|
|
9
10
|
const root = path.resolve(projectRoot);
|
|
10
11
|
const sourceRoot = () => templateRoot ?? shippedTemplateRoot();
|
|
@@ -17,7 +18,7 @@ async function initProject(projectRoot, { templateRoot, templates, library, sync
|
|
|
17
18
|
const team = await projectProfiles({ root, templates, library, teamIds });
|
|
18
19
|
const files = {
|
|
19
20
|
"ALP.md": await template("ALP.md"),
|
|
20
|
-
".alp/settings.json": JSON.stringify({ defaultAgent: "main", workflow: { mode:
|
|
21
|
+
".alp/settings.json": JSON.stringify({ defaultAgent: "main", workflow: { mode: INIT_TEAM }, team, templateDefaults: profileDefaults(team) }, null, 2) + "\n"
|
|
21
22
|
};
|
|
22
23
|
const directories = [".alp"];
|
|
23
24
|
const created = [];
|
|
@@ -3,20 +3,31 @@ export function validateTeam(config: any, source: any): any;
|
|
|
3
3
|
/**
|
|
4
4
|
* Where each team comes from: built in, then the library, then the project. A team
|
|
5
5
|
* directory counts only with a team.json. `root` may be undefined outside a project.
|
|
6
|
+
* @param {string | undefined} root
|
|
7
|
+
* @param {{ library?: string, templates?: Record<string, string> }} [options]
|
|
6
8
|
* @returns {Promise<Map<string, { source: 'builtin' | 'library' | 'project', directory?: string, overrides?: 'builtin' | 'library' }>>}
|
|
7
9
|
*/
|
|
8
|
-
export function teamSources(root:
|
|
10
|
+
export function teamSources(root: string | undefined, { library, templates }?: {
|
|
11
|
+
library?: string;
|
|
12
|
+
templates?: Record<string, string>;
|
|
13
|
+
}): Promise<Map<string, {
|
|
9
14
|
source: "builtin" | "library" | "project";
|
|
10
15
|
directory?: string;
|
|
11
16
|
overrides?: "builtin" | "library";
|
|
12
17
|
}>>;
|
|
13
18
|
/**
|
|
14
19
|
* A team by id, with its house rules.
|
|
20
|
+
* @param {string | undefined} root
|
|
21
|
+
* @param {string} id
|
|
22
|
+
* @param {{ library?: string, templates?: Record<string, string> }} [options]
|
|
15
23
|
* @returns {Promise<{ id: string, source: 'builtin' | 'library' | 'project', label: string, description?: string, main: string,
|
|
16
24
|
* members: Record<string, { role?: string, model?: string, thinking?: string }>, delegation: Record<string, string[]>,
|
|
17
25
|
* oracleThinking?: Record<string, string>, oracleModels?: string[], maxPeers?: number, supervisor: false | { agent: string, model?: string, thinking?: string }, houseRules: string }>}
|
|
18
26
|
*/
|
|
19
|
-
export function resolveTeam(root:
|
|
27
|
+
export function resolveTeam(root: string | undefined, id: string, { library, templates }?: {
|
|
28
|
+
library?: string;
|
|
29
|
+
templates?: Record<string, string>;
|
|
30
|
+
}): Promise<{
|
|
20
31
|
id: string;
|
|
21
32
|
source: "builtin" | "library" | "project";
|
|
22
33
|
label: string;
|
|
@@ -1,14 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The team a session runs in (ALPD §42): the one
|
|
3
|
-
* session ran in,
|
|
4
|
-
*
|
|
2
|
+
* The team a session runs in when the caller selects none (ALPD §42, §60): the one a resumed
|
|
3
|
+
* session ran in, else settings' workflow.mode, else `custom` (settings' delegation graph, no
|
|
4
|
+
* supervisor). A project without .alp/settings.json gets init's settings when a session is
|
|
5
|
+
* created, so with `scaffolded` a missing file counts as naming INIT_TEAM. session.create and
|
|
6
|
+
* session.preview both resolve through here, so a preview names the team that would run.
|
|
7
|
+
* @param {Record<string, any>} settings the project's settings, `{}` when the file is missing
|
|
8
|
+
* @param {{ restored?: { mode: string }, scaffolded?: boolean }} [options]
|
|
9
|
+
*/
|
|
10
|
+
export function defaultTeam(settings: Record<string, any>, { restored, scaffolded }?: {
|
|
11
|
+
restored?: {
|
|
12
|
+
mode: string;
|
|
13
|
+
};
|
|
14
|
+
scaffolded?: boolean;
|
|
15
|
+
}): any;
|
|
16
|
+
/**
|
|
17
|
+
* The team a session runs in (ALPD §42): the one the caller selects, else defaultTeam. Without
|
|
18
|
+
* any, the session is `custom`: its graph is settings' delegation and no supervisor watches it.
|
|
19
|
+
* Selecting `custom` is refused unless it is the team that would run anyway, so a client may send
|
|
20
|
+
* back the team session.preview named.
|
|
21
|
+
* @param {string} root
|
|
22
|
+
* @param {string | undefined} selected
|
|
23
|
+
* @param {{ mode: string, maxPeers?: number, supervisor?: boolean } | undefined} restored
|
|
24
|
+
* @param {{ library?: string, templates?: Record<string, string>, scaffold?: boolean }} [options]
|
|
25
|
+
* scaffold: a missing settings file will be written by init before the session runs (a preview).
|
|
5
26
|
* @returns {Promise<{ mode: string, maxPeers: number, supervisor: boolean, team?: Awaited<ReturnType<typeof resolveTeam>> }>}
|
|
6
27
|
*/
|
|
7
|
-
export function resolveWorkflow(root:
|
|
28
|
+
export function resolveWorkflow(root: string, selected: string | undefined, restored: {
|
|
29
|
+
mode: string;
|
|
30
|
+
maxPeers?: number;
|
|
31
|
+
supervisor?: boolean;
|
|
32
|
+
} | undefined, { library, templates, scaffold }?: {
|
|
33
|
+
library?: string;
|
|
34
|
+
templates?: Record<string, string>;
|
|
35
|
+
scaffold?: boolean;
|
|
36
|
+
}): Promise<{
|
|
8
37
|
mode: string;
|
|
9
38
|
maxPeers: number;
|
|
10
39
|
supervisor: boolean;
|
|
11
40
|
team?: Awaited<ReturnType<typeof resolveTeam>>;
|
|
12
41
|
}>;
|
|
13
42
|
export { teamOf as profileOf } from "./teams.js";
|
|
43
|
+
/** The team `alp init` names in a new project's settings (`init.js`). */
|
|
44
|
+
export const INIT_TEAM: "pho";
|
|
14
45
|
import { resolveTeam } from './teams.js';
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
// packages/core/src/workflow.js
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { jsonObject } from "./resolver.js";
|
|
3
|
+
import { jsonObject, optionalText } from "./resolver.js";
|
|
4
4
|
import { applyProfile } from "./profiles.js";
|
|
5
5
|
import { validateSettings } from "./validation.js";
|
|
6
6
|
import { resolveTeam, teamOf } from "./teams.js";
|
|
7
7
|
import { teamOf as teamOf2 } from "./teams.js";
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
var INIT_TEAM = "pho";
|
|
9
|
+
function defaultTeam(settings, { restored, scaffolded = false } = {}) {
|
|
10
|
+
return teamOf(restored?.mode ?? settings.workflow?.mode ?? (scaffolded ? INIT_TEAM : "custom"));
|
|
11
|
+
}
|
|
12
|
+
async function resolveWorkflow(root, selected, restored, { library, templates, scaffold = false } = {}) {
|
|
13
|
+
const file = path.join(root, ".alp/settings.json");
|
|
14
|
+
const settings = await jsonObject(file, "INVALID_SETTINGS");
|
|
10
15
|
validateSettings(settings, ".alp/settings.json");
|
|
16
|
+
const scaffolded = scaffold && await optionalText(file) === void 0;
|
|
17
|
+
const fallback = defaultTeam(settings, { restored, scaffolded });
|
|
11
18
|
const chosen = selected === void 0 ? void 0 : teamOf(selected);
|
|
12
|
-
if (chosen === "custom") throw new Error("Choose a team, such as pho (Ph\u1EDF) or cafe (Cafe)");
|
|
13
|
-
const mode = chosen ??
|
|
19
|
+
if (chosen === "custom" && fallback !== "custom") throw new Error("Choose a team, such as pho (Ph\u1EDF) or cafe (Cafe)");
|
|
20
|
+
const mode = chosen ?? fallback;
|
|
14
21
|
if (restored && mode !== teamOf(restored.mode)) throw new Error("Cannot change team in an existing session");
|
|
15
22
|
const team = mode === "custom" ? void 0 : applyProfile(await resolveTeam(root, mode, { library, templates }), (settings.team ?? settings.profile)?.[mode]);
|
|
16
23
|
const maxPeers = restored?.maxPeers ?? settings.workflow?.maxPeers ?? team?.maxPeers ?? 2;
|
|
@@ -19,6 +26,8 @@ async function resolveWorkflow(root, selected, restored, { library, templates }
|
|
|
19
26
|
return { mode, maxPeers, supervisor, ...team ? { team } : {} };
|
|
20
27
|
}
|
|
21
28
|
export {
|
|
29
|
+
INIT_TEAM,
|
|
30
|
+
defaultTeam,
|
|
22
31
|
teamOf2 as profileOf,
|
|
23
32
|
resolveWorkflow
|
|
24
33
|
};
|