@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.
@@ -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.1" : "dev";
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.1",
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.detail.type === "shell" ? item.detail.command : item.name === "alp_delegate" ? `delegate \u2192 ${item.detail.input?.agent}` : item.name;
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: "pho" }, team, templateDefaults: profileDefaults(team) }, null, 2) + "\n"
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: any, { library, templates }?: {}): Promise<Map<string, {
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: any, id: any, { library, templates }?: {}): Promise<{
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 the caller selects, the one a resumed
3
- * session ran in, or settings' workflow.mode. Without any, the session is `custom`: its
4
- * graph is settings' delegation and no supervisor watches it.
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: any, selected: any, restored: any, { library, templates }?: {}): Promise<{
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
- async function resolveWorkflow(root, selected, restored, { library, templates } = {}) {
9
- const settings = await jsonObject(path.join(root, ".alp/settings.json"), "INVALID_SETTINGS");
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 ?? teamOf(restored?.mode ?? settings.workflow?.mode ?? "custom");
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
  };