@anhlp/alp-core 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/dist/init.js +2 -1
- package/dist/teams.d.ts +13 -2
- package/dist/workflow.d.ts +35 -4
- package/dist/workflow.js +14 -5
- package/package.json +2 -2
- package/src/init.js +2 -1
- package/src/teams.js +5 -0
- package/src/workflow.js +33 -8
package/dist/init.js
CHANGED
|
@@ -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 = [];
|
package/dist/teams.d.ts
CHANGED
|
@@ -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;
|
package/dist/workflow.d.ts
CHANGED
|
@@ -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';
|
package/dist/workflow.js
CHANGED
|
@@ -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
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anhlp/alp-core",
|
|
3
|
-
"version": "0.0.0-beta.
|
|
3
|
+
"version": "0.0.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ALP core module",
|
|
6
6
|
"files": [
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
"node": ">=22.12"
|
|
285
285
|
},
|
|
286
286
|
"dependencies": {
|
|
287
|
-
"@anhlp/alp-protocol": "0.0.0-beta.
|
|
287
|
+
"@anhlp/alp-protocol": "0.0.0-beta.2"
|
|
288
288
|
},
|
|
289
289
|
"scripts": {
|
|
290
290
|
"build": "node ../../scripts/build-workspace.mjs core",
|
package/src/init.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
5
|
import { projectProfiles, profileDefaults, syncTemplateProfiles } from './profiles.js';
|
|
6
6
|
import { validateSettings } from './validation.js';
|
|
7
|
+
import { INIT_TEAM } from './workflow.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Fill missing scaffold files without replacing existing user content: ALP.md and
|
|
@@ -24,7 +25,7 @@ export async function initProject(projectRoot, { templateRoot, templates, librar
|
|
|
24
25
|
const team = await projectProfiles({ root, templates, library, teamIds });
|
|
25
26
|
const files = {
|
|
26
27
|
'ALP.md': await template('ALP.md'),
|
|
27
|
-
'.alp/settings.json': JSON.stringify({ defaultAgent: 'main', workflow: { mode:
|
|
28
|
+
'.alp/settings.json': JSON.stringify({ defaultAgent: 'main', workflow: { mode: INIT_TEAM }, team, templateDefaults: profileDefaults(team) }, null, 2) + '\n',
|
|
28
29
|
};
|
|
29
30
|
const directories = ['.alp'];
|
|
30
31
|
const created = [];
|
package/src/teams.js
CHANGED
|
@@ -100,6 +100,8 @@ async function directories(directory) {
|
|
|
100
100
|
/**
|
|
101
101
|
* Where each team comes from: built in, then the library, then the project. A team
|
|
102
102
|
* directory counts only with a team.json. `root` may be undefined outside a project.
|
|
103
|
+
* @param {string | undefined} root
|
|
104
|
+
* @param {{ library?: string, templates?: Record<string, string> }} [options]
|
|
103
105
|
* @returns {Promise<Map<string, { source: 'builtin' | 'library' | 'project', directory?: string, overrides?: 'builtin' | 'library' }>>}
|
|
104
106
|
*/
|
|
105
107
|
export async function teamSources(root, { library, templates } = {}) {
|
|
@@ -120,6 +122,9 @@ export async function teamSources(root, { library, templates } = {}) {
|
|
|
120
122
|
|
|
121
123
|
/**
|
|
122
124
|
* A team by id, with its house rules.
|
|
125
|
+
* @param {string | undefined} root
|
|
126
|
+
* @param {string} id
|
|
127
|
+
* @param {{ library?: string, templates?: Record<string, string> }} [options]
|
|
123
128
|
* @returns {Promise<{ id: string, source: 'builtin' | 'library' | 'project', label: string, description?: string, main: string,
|
|
124
129
|
* members: Record<string, { role?: string, model?: string, thinking?: string }>, delegation: Record<string, string[]>,
|
|
125
130
|
* oracleThinking?: Record<string, string>, oracleModels?: string[], maxPeers?: number, supervisor: false | { agent: string, model?: string, thinking?: string }, houseRules: string }>}
|
package/src/workflow.js
CHANGED
|
@@ -1,23 +1,48 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { jsonObject } from './resolver.js';
|
|
2
|
+
import { jsonObject, optionalText } from './resolver.js';
|
|
3
3
|
import { applyProfile } from './profiles.js';
|
|
4
4
|
import { validateSettings } from './validation.js';
|
|
5
5
|
import { resolveTeam, teamOf } from './teams.js';
|
|
6
6
|
|
|
7
7
|
export { teamOf as profileOf } from './teams.js';
|
|
8
8
|
|
|
9
|
+
/** The team `alp init` names in a new project's settings (`init.js`). */
|
|
10
|
+
export const INIT_TEAM = 'pho';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The team a session runs in when the caller selects none (ALPD §42, §60): the one a resumed
|
|
14
|
+
* session ran in, else settings' workflow.mode, else `custom` (settings' delegation graph, no
|
|
15
|
+
* supervisor). A project without .alp/settings.json gets init's settings when a session is
|
|
16
|
+
* created, so with `scaffolded` a missing file counts as naming INIT_TEAM. session.create and
|
|
17
|
+
* session.preview both resolve through here, so a preview names the team that would run.
|
|
18
|
+
* @param {Record<string, any>} settings the project's settings, `{}` when the file is missing
|
|
19
|
+
* @param {{ restored?: { mode: string }, scaffolded?: boolean }} [options]
|
|
20
|
+
*/
|
|
21
|
+
export function defaultTeam(settings, { restored, scaffolded = false } = {}) {
|
|
22
|
+
return teamOf(restored?.mode ?? settings.workflow?.mode ?? (scaffolded ? INIT_TEAM : 'custom'));
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
/**
|
|
10
|
-
* The team a session runs in (ALPD §42): the one the caller selects,
|
|
11
|
-
*
|
|
12
|
-
*
|
|
26
|
+
* The team a session runs in (ALPD §42): the one the caller selects, else defaultTeam. Without
|
|
27
|
+
* any, the session is `custom`: its graph is settings' delegation and no supervisor watches it.
|
|
28
|
+
* Selecting `custom` is refused unless it is the team that would run anyway, so a client may send
|
|
29
|
+
* back the team session.preview named.
|
|
30
|
+
* @param {string} root
|
|
31
|
+
* @param {string | undefined} selected
|
|
32
|
+
* @param {{ mode: string, maxPeers?: number, supervisor?: boolean } | undefined} restored
|
|
33
|
+
* @param {{ library?: string, templates?: Record<string, string>, scaffold?: boolean }} [options]
|
|
34
|
+
* scaffold: a missing settings file will be written by init before the session runs (a preview).
|
|
13
35
|
* @returns {Promise<{ mode: string, maxPeers: number, supervisor: boolean, team?: Awaited<ReturnType<typeof resolveTeam>> }>}
|
|
14
36
|
*/
|
|
15
|
-
export async function resolveWorkflow(root, selected, restored, { library, templates } = {}) {
|
|
16
|
-
const
|
|
37
|
+
export async function resolveWorkflow(root, selected, restored, { library, templates, scaffold = false } = {}) {
|
|
38
|
+
const file = path.join(root, '.alp/settings.json');
|
|
39
|
+
const settings = await jsonObject(file, 'INVALID_SETTINGS');
|
|
17
40
|
validateSettings(settings, '.alp/settings.json');
|
|
41
|
+
const scaffolded = scaffold && await optionalText(file) === undefined;
|
|
42
|
+
const fallback = defaultTeam(settings, { restored, scaffolded });
|
|
18
43
|
const chosen = selected === undefined ? undefined : teamOf(selected);
|
|
19
|
-
if (chosen === 'custom') throw new Error('Choose a team, such as pho (Phở) or cafe (Cafe)');
|
|
20
|
-
const mode = chosen ??
|
|
44
|
+
if (chosen === 'custom' && fallback !== 'custom') throw new Error('Choose a team, such as pho (Phở) or cafe (Cafe)');
|
|
45
|
+
const mode = chosen ?? fallback;
|
|
21
46
|
if (restored && mode !== teamOf(restored.mode)) throw new Error('Cannot change team in an existing session');
|
|
22
47
|
const team = mode === 'custom' ? undefined : applyProfile(await resolveTeam(root, mode, { library, templates }), (settings.team ?? settings.profile)?.[mode]);
|
|
23
48
|
const maxPeers = restored?.maxPeers ?? settings.workflow?.maxPeers ?? team?.maxPeers ?? 2;
|