@anhlp/alp-runtime 0.0.0-beta.1

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.
Files changed (94) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +7 -0
  4. package/dist/acp-transport.d.ts +114 -0
  5. package/dist/acp-transport.js +478 -0
  6. package/dist/antigravity-transport.d.ts +80 -0
  7. package/dist/antigravity-transport.js +609 -0
  8. package/dist/board-domain.d.ts +56 -0
  9. package/dist/board-domain.js +157 -0
  10. package/dist/board.d.ts +21 -0
  11. package/dist/board.js +69 -0
  12. package/dist/catalog.d.ts +31 -0
  13. package/dist/catalog.js +38 -0
  14. package/dist/claude-transport.d.ts +157 -0
  15. package/dist/claude-transport.js +671 -0
  16. package/dist/contracts.d.ts +163 -0
  17. package/dist/contracts.js +5 -0
  18. package/dist/events.d.ts +1 -0
  19. package/dist/events.js +0 -0
  20. package/dist/github.d.ts +10 -0
  21. package/dist/github.js +39 -0
  22. package/dist/handoff.d.ts +36 -0
  23. package/dist/handoff.js +73 -0
  24. package/dist/index.d.ts +24 -0
  25. package/dist/index.js +64 -0
  26. package/dist/language.d.ts +41 -0
  27. package/dist/language.js +68 -0
  28. package/dist/live.d.ts +48 -0
  29. package/dist/live.js +45 -0
  30. package/dist/mailbox.d.ts +24 -0
  31. package/dist/mailbox.js +36 -0
  32. package/dist/mcp-tools.d.ts +115 -0
  33. package/dist/mcp-tools.js +71 -0
  34. package/dist/permission-path.d.ts +2 -0
  35. package/dist/permission-path.js +44 -0
  36. package/dist/process-info.d.ts +14 -0
  37. package/dist/process-info.js +32 -0
  38. package/dist/providers.d.ts +28 -0
  39. package/dist/providers.js +71 -0
  40. package/dist/recall.d.ts +42 -0
  41. package/dist/recall.js +60 -0
  42. package/dist/resolve.d.ts +121 -0
  43. package/dist/resolve.js +238 -0
  44. package/dist/runtime-context.d.ts +43 -0
  45. package/dist/runtime-context.js +61 -0
  46. package/dist/runtime.d.ts +4 -0
  47. package/dist/runtime.js +2979 -0
  48. package/dist/session-config.d.ts +1973 -0
  49. package/dist/session-config.js +205 -0
  50. package/dist/session-state.d.ts +140 -0
  51. package/dist/session-state.js +0 -0
  52. package/dist/tool-results.d.ts +12 -0
  53. package/dist/tool-results.js +25 -0
  54. package/dist/tools.d.ts +674 -0
  55. package/dist/tools.js +375 -0
  56. package/dist/transport.d.ts +47 -0
  57. package/dist/transport.js +169 -0
  58. package/dist/usage.d.ts +15 -0
  59. package/dist/usage.js +35 -0
  60. package/dist/workspace.d.ts +101 -0
  61. package/dist/workspace.js +259 -0
  62. package/dist/worktree-domain.d.ts +88 -0
  63. package/dist/worktree-domain.js +196 -0
  64. package/package.json +256 -0
  65. package/src/acp-transport.ts +473 -0
  66. package/src/antigravity-transport.ts +658 -0
  67. package/src/board-domain.ts +185 -0
  68. package/src/board.ts +80 -0
  69. package/src/catalog.ts +32 -0
  70. package/src/claude-transport.ts +771 -0
  71. package/src/contracts.ts +166 -0
  72. package/src/events.ts +1 -0
  73. package/src/github.ts +37 -0
  74. package/src/handoff.ts +86 -0
  75. package/src/index.ts +26 -0
  76. package/src/language.ts +102 -0
  77. package/src/live.ts +86 -0
  78. package/src/mailbox.ts +46 -0
  79. package/src/mcp-tools.ts +73 -0
  80. package/src/permission-path.ts +37 -0
  81. package/src/process-info.ts +40 -0
  82. package/src/providers.ts +84 -0
  83. package/src/recall.ts +85 -0
  84. package/src/resolve.ts +230 -0
  85. package/src/runtime-context.ts +49 -0
  86. package/src/runtime.ts +3579 -0
  87. package/src/session-config.ts +301 -0
  88. package/src/session-state.ts +141 -0
  89. package/src/tool-results.ts +26 -0
  90. package/src/tools.ts +379 -0
  91. package/src/transport.ts +116 -0
  92. package/src/usage.ts +42 -0
  93. package/src/workspace.ts +318 -0
  94. package/src/worktree-domain.ts +216 -0
@@ -0,0 +1,163 @@
1
+ import type { ProviderFactories, ProviderRegistry } from './providers.js';
2
+ import type { RuntimeKind, SessionSpec } from './resolve.js';
3
+ import type { Envelope, SessionSnapshot, TreeStatus, UserQuestion } from './events.js';
4
+ import type { Pin } from './board.js';
5
+ import type { LiveEntry } from './live.js';
6
+ import type { ProviderUsage } from './usage.js';
7
+ import type { AcpProvider } from './acp-transport.js';
8
+ import type { AntigravityProvider } from './antigravity-transport.js';
9
+ import type { GitHubRunner } from './github.js';
10
+ export type RuntimeTransport = {
11
+ request(method: string, params: any): Promise<any>;
12
+ initialize(): Promise<void>;
13
+ orchestrationContext?: () => Promise<unknown>;
14
+ onNotification(listener: (method: string, params: any) => void): void;
15
+ onFailure(listener: (error: unknown) => void): void;
16
+ close(): Promise<void>;
17
+ onRequest?: (listener: (method: string, params: any) => Promise<unknown>) => void;
18
+ };
19
+ export type RuntimeOptions = {
20
+ /** Native adapter overrides, isolated to this runtime; the legacy transport hook takes precedence. */
21
+ providers?: ProviderFactories;
22
+ /** An explicit registry, including custom adapter factories registered by the host. */
23
+ providerRegistry?: ProviderRegistry;
24
+ codexCommand?: string;
25
+ claudeCommand?: string;
26
+ antigravityCommand?: string;
27
+ environment?: NodeJS.ProcessEnv;
28
+ /**
29
+ * Optional transport factory for tests/custom runtimes.
30
+ * When omitted, ALP creates CodexTransport, ClaudeTransport, or AntigravityTransport itself.
31
+ */
32
+ transport?: (cwd: string, env: NodeJS.ProcessEnv, runtime?: RuntimeKind,
33
+ /** The ACP provider, when runtime is acp. */
34
+ acp?: AcpProvider,
35
+ /** The Antigravity provider, when runtime is antigravity. */
36
+ antigravity?: AntigravityProvider) => RuntimeTransport;
37
+ /** An assignment with no activity this long is reported stalled; twice this long, it fails. */
38
+ silentForMs?: number;
39
+ /** How often main gets a check-in on its running assignments. Default 10 minutes; 0 turns it off. */
40
+ checkInMs?: number;
41
+ /** How often the watchdog looks at running assignments; derived from silentForMs and checkInMs when omitted. */
42
+ watchMs?: number;
43
+ /** How long alp_ask waits for the requester before returning unanswered. */
44
+ askTimeoutMs?: number;
45
+ /** How long alp_ask waits for the user before returning unanswered. Default 30 minutes. */
46
+ userAskTimeoutMs?: number;
47
+ /** Directory for per-root-session assignment logs (JSONL). Omitted disables logging. */
48
+ runLogDir?: string;
49
+ /** Starter files for projects without ALP; omitted reads the repository templates. */
50
+ templates?: Record<string, string>;
51
+ /** Where isolated assignments get their git worktrees. Default: a directory in the temp directory. */
52
+ worktreeDir?: string;
53
+ /** Where assignments whose profile has workdir copy get their disposable copies. Default: a directory in the temp directory. */
54
+ copyDir?: string;
55
+ /** Where project boards are kept (JSONL per project). Omitted keeps them in memory only. */
56
+ boardDir?: string;
57
+ /** The user's skill library and lessons (ALP_HOME). Omitted: only skills inside the project, and project lessons. */
58
+ libraryDir?: string;
59
+ /** The language the user reads; omitted, their settings.json's language, else Vietnamese (ALPD §54). */
60
+ language?: string;
61
+ /** Where finished assignments' native threads are recorded for alp_recall. Omitted keeps the record in memory. */
62
+ recallFile?: string;
63
+ /** Where pauses are kept across restarts. Omitted keeps them in memory. */
64
+ pauseFile?: string;
65
+ /**
66
+ * Resume a runtime a usage limit paused as soon as the runtime says the limit lifted
67
+ * (ALPD §58). Default true; false tells the user instead, who resumes it.
68
+ */
69
+ autoResume?: boolean;
70
+ /** How often alpd asks a runtime a usage limit paused whether the limit lifted. Default LIMIT_CHECK_MS. */
71
+ limitCheckMs?: number;
72
+ /** Where running assignments are kept, so the next alpd continues them (ALPD §31). Omitted keeps them in memory. */
73
+ liveFile?: string;
74
+ /** False parks assignments an earlier alpd left running until the user runs alp resume. Default true: they continue. */
75
+ recoveryResume?: boolean;
76
+ /** How the previous alpd ended, for what recovered sessions are told. */
77
+ previousExit?: {
78
+ kind: 'clean' | 'crash';
79
+ at?: string;
80
+ };
81
+ /** False starts no supervisors, for hosts and tests that do not want them. Default true. */
82
+ supervisor?: boolean;
83
+ /** Runs the GitHub CLI for alp_issue. Default: `gh` (or ALP_GH_BIN) on PATH. */
84
+ github?: GitHubRunner;
85
+ };
86
+ import type { OpenOptions, PromptInput } from '@anhlp/alp-protocol/session';
87
+ export type { OpenOptions, PromptContent, PromptInput } from '@anhlp/alp-protocol/session';
88
+ export type AlpRuntime = {
89
+ onEvent(listener: (envelope: Envelope) => void): () => void;
90
+ /** Opens a root session under a client-chosen id. */
91
+ open(sessionId: string, spec: SessionSpec, options?: OpenOptions): Promise<SessionSnapshot>;
92
+ /** What a new root would run as, before it opens, and the teams it may choose (ALPD §60). */
93
+ preview(spec: SessionSpec): Promise<SessionPreview>;
94
+ /** Failures are reported as prompt.failed, once per clientMessageId. */
95
+ prompt(sessionId: string, input: PromptInput): Promise<void>;
96
+ /** Cancels the running turn and closes the whole subtree. */
97
+ interrupt(sessionId: string): Promise<void>;
98
+ /** Changes permission mode while idle; undefined keeps the current mode. */
99
+ configure(sessionId: string, changes: {
100
+ mode?: string;
101
+ }): Promise<SessionSnapshot>;
102
+ close(sessionId: string): Promise<void>;
103
+ snapshot(sessionId: string): SessionSnapshot | undefined;
104
+ /** Live sessions, roots before their children. */
105
+ list(): SessionSnapshot[];
106
+ /** The live tree containing a session: sessions, assignments, questions, worktrees and leases. */
107
+ status(sessionId: string): TreeStatus | undefined;
108
+ /** Questions agents asked the user that wait for an answer. */
109
+ questions(): UserQuestion[];
110
+ /** Answers or dismisses a question to the user. */
111
+ answer(questionId: string, reply: {
112
+ text?: string;
113
+ dismiss?: boolean;
114
+ reason?: string;
115
+ }): void;
116
+ /** Mails a note from the user to any live session, such as an assignment the user wants to redirect. */
117
+ message(sessionId: string, text: string): void;
118
+ /** The project board: live claims, then decisions and findings, oldest first. */
119
+ board(projectRoot: string): Promise<Pin[]>;
120
+ /** Holds delegation and wakes on one runtime, or all; now also parks running assignments there. */
121
+ pause(input?: {
122
+ runtime?: RuntimeKind;
123
+ now?: boolean;
124
+ reason?: string;
125
+ }): PauseState;
126
+ /** Lifts a pause and continues the assignments it parked. Without a runtime, lifts every pause. */
127
+ resume(input?: {
128
+ runtime?: RuntimeKind;
129
+ }): PauseState;
130
+ /** Current pauses and parked assignments. */
131
+ pauses(): PauseState;
132
+ /** Read-only, cached account usage for the web and desktop viewers. */
133
+ usage(refresh?: boolean): Promise<{
134
+ providers: Array<ProviderUsage & {
135
+ pause: Pause | null;
136
+ parked: number;
137
+ }>;
138
+ }>;
139
+ /** The user asks a finished assignment, or the last one on a task of the project, about its work. */
140
+ recall(target: {
141
+ assignmentId?: string;
142
+ taskId?: string;
143
+ projectRoot?: string;
144
+ }, question: string): Promise<RecallAnswer>;
145
+ /** Assignments an earlier alpd left running, which recover() can continue once their root is open again. */
146
+ recoverable(): LiveEntry[];
147
+ /** Continues the assignments an earlier alpd left running in an open root's tree; with continueRoot, the root's own turn too. */
148
+ recover(rootId: string, options?: {
149
+ continueRoot?: boolean;
150
+ }): Promise<RecoveryOutcome[]>;
151
+ /** Gives up the assignments of a tree whose root cannot be opened again: their tasks go back to open. */
152
+ abandon(rootId: string, reason: string): Promise<RecoveryOutcome[]>;
153
+ /** Closes everything; assignments still running stay in the live file, so the next alpd continues them. */
154
+ shutdown(): Promise<void>;
155
+ };
156
+ import type { SessionPreview } from '@anhlp/alp-protocol/session';
157
+ export type { SessionPreview } from '@anhlp/alp-protocol/session';
158
+ /** The team a viewer starts a root in when neither it nor the project's settings choose one, as Paseo does. */
159
+ export declare const DEFAULT_TEAM = "pho";
160
+ import type { RecoveryOutcome, Pause, PauseState } from '@anhlp/alp-protocol/session';
161
+ export type { RecoveryOutcome, Pause, PauseState } from '@anhlp/alp-protocol/session';
162
+ import type { RecallAnswer } from '@anhlp/alp-protocol/session';
163
+ export type { RecallAnswer } from '@anhlp/alp-protocol/session';
@@ -0,0 +1,5 @@
1
+ // packages/runtime/src/contracts.ts
2
+ var DEFAULT_TEAM = "pho";
3
+ export {
4
+ DEFAULT_TEAM
5
+ };
@@ -0,0 +1 @@
1
+ export type * from '@anhlp/alp-protocol/events';
package/dist/events.js ADDED
File without changes
@@ -0,0 +1,10 @@
1
+ /** Where agents send feedback about ALP itself: its process, tools and agent instructions. */
2
+ export declare const ALP_REPO = "phucanh08/alp";
3
+ /** Runs the GitHub CLI with optional standard input and returns its standard output. Tests replace it. */
4
+ export type GitHubRunner = (args: string[], options: {
5
+ cwd: string;
6
+ input?: string;
7
+ }) => Promise<string>;
8
+ export declare const gh: GitHubRunner;
9
+ /** The owner/name of the GitHub repository a project's origin remote points to. */
10
+ export declare function projectRepo(projectRoot: string): Promise<string>;
package/dist/github.js ADDED
@@ -0,0 +1,39 @@
1
+ // packages/runtime/src/github.ts
2
+ import { spawn } from "node:child_process";
3
+ import { gitEnvironment } from "./workspace.js";
4
+ var ALP_REPO = "phucanh08/alp";
5
+ function run(command, args, { cwd, input }) {
6
+ return new Promise((resolve, reject) => {
7
+ const child = spawn(command, args, { cwd, windowsHide: true, stdio: ["pipe", "pipe", "pipe"], env: gitEnvironment() });
8
+ let stdout = "";
9
+ let stderr = "";
10
+ child.stdout.on("data", (chunk) => {
11
+ stdout += chunk;
12
+ });
13
+ child.stderr.on("data", (chunk) => {
14
+ stderr += chunk;
15
+ });
16
+ child.stdin.on("error", () => {
17
+ });
18
+ child.on("error", (error) => reject(error.code === "ENOENT" ? new Error(`${command} is not installed or not on PATH`) : error));
19
+ child.on("close", (code) => code === 0 ? resolve(stdout) : reject(new Error((stderr || stdout).trim() || `${command} exited with ${code}`)));
20
+ child.stdin.end(input ?? "");
21
+ });
22
+ }
23
+ var gh = (args, options) => run(process.env.ALP_GH_BIN ?? "gh", args, options);
24
+ async function projectRepo(projectRoot) {
25
+ let url;
26
+ try {
27
+ url = (await run("git", ["remote", "get-url", "origin"], { cwd: projectRoot })).trim();
28
+ } catch {
29
+ throw new Error('This project has no git remote named origin; use target "alp" for feedback about ALP, or tell the user');
30
+ }
31
+ const match = /github\.com[:/]([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(url);
32
+ if (!match) throw new Error(`The origin remote is not on GitHub (${url}); issues are filed with the GitHub CLI only`);
33
+ return `${match[1]}/${match[2]}`;
34
+ }
35
+ export {
36
+ ALP_REPO,
37
+ gh,
38
+ projectRepo
39
+ };
@@ -0,0 +1,36 @@
1
+ export declare const HANDOFF_OUTCOMES: readonly ["complete", "partial", "blocked", "reconsider"];
2
+ export declare const HANDOFF_LISTS: readonly ["candidate", "scope", "verification", "risks", "discovered"];
3
+ /**
4
+ * A review's verdict (ALPD §38): each criterion judged with its evidence, findings
5
+ * by severity, and one result that follows from them, so requesters, tasks and
6
+ * later a review quorum read every review the same way.
7
+ */
8
+ export declare const VERDICT_RESULTS: readonly ["pass", "pass_with_findings", "fail", "blocked"];
9
+ export declare const CRITERION_RESULTS: readonly ["pass", "fail", "not_checked"];
10
+ export declare const SEVERITIES: readonly ["critical", "high", "medium", "low"];
11
+ export type Verdict = {
12
+ result: typeof VERDICT_RESULTS[number];
13
+ criteria: Array<{
14
+ criterion: string;
15
+ result: typeof CRITERION_RESULTS[number];
16
+ evidence: string;
17
+ }>;
18
+ findings?: Array<{
19
+ severity: typeof SEVERITIES[number];
20
+ where: string;
21
+ problem: string;
22
+ fix?: string;
23
+ }>;
24
+ };
25
+ export type Handoff = {
26
+ outcome: typeof HANDOFF_OUTCOMES[number];
27
+ summary: string;
28
+ ownership?: string;
29
+ verdict?: Verdict;
30
+ } & Partial<Record<typeof HANDOFF_LISTS[number], string[]>>;
31
+ /** Returns the normalized handoff, or an error message for the child. */
32
+ /** Returns the verdict, or what is wrong with it; its result must follow from its criteria and findings. */
33
+ export declare function parseVerdict(value: any): Verdict | string;
34
+ /** A verdict in one line: its result and the criteria counts. */
35
+ export declare function verdictLine(verdict: Verdict): string;
36
+ export declare function parseHandoff(args: any): Handoff | string;
@@ -0,0 +1,73 @@
1
+ // packages/runtime/src/handoff.ts
2
+ var HANDOFF_OUTCOMES = ["complete", "partial", "blocked", "reconsider"];
3
+ var HANDOFF_LISTS = ["candidate", "scope", "verification", "risks", "discovered"];
4
+ var VERDICT_RESULTS = ["pass", "pass_with_findings", "fail", "blocked"];
5
+ var CRITERION_RESULTS = ["pass", "fail", "not_checked"];
6
+ var SEVERITIES = ["critical", "high", "medium", "low"];
7
+ function parseVerdict(value) {
8
+ const text = (item) => typeof item === "string" && !!item.trim();
9
+ const only = (item, keys) => !!item && typeof item === "object" && !Array.isArray(item) && Object.keys(item).every((key) => keys.includes(key));
10
+ if (!only(value, ["result", "criteria", "findings"])) return "verdict must be an object with result, criteria and findings";
11
+ if (!VERDICT_RESULTS.includes(value.result)) return `verdict.result must be one of ${VERDICT_RESULTS.join(", ")}`;
12
+ if (!Array.isArray(value.criteria) || !value.criteria.length || value.criteria.length > 50) return "verdict.criteria must list 1 to 50 criteria";
13
+ for (const item of value.criteria) {
14
+ if (!only(item, ["criterion", "result", "evidence"]) || !text(item.criterion) || !text(item.evidence) || !CRITERION_RESULTS.includes(item.result)) {
15
+ return `each verdict criterion needs criterion, result (${CRITERION_RESULTS.join(", ")}) and evidence`;
16
+ }
17
+ }
18
+ const findings = value.findings ?? [];
19
+ if (!Array.isArray(findings) || findings.length > 100) return "verdict.findings must be a list of at most 100 findings";
20
+ for (const item of findings) {
21
+ if (!only(item, ["severity", "where", "problem", "fix"]) || !SEVERITIES.includes(item.severity) || !text(item.where) || !text(item.problem) || item.fix !== void 0 && !text(item.fix)) {
22
+ return `each verdict finding needs severity (${SEVERITIES.join(", ")}), where and problem, and optionally fix`;
23
+ }
24
+ }
25
+ const failed = value.criteria.filter((item) => item.result === "fail").length;
26
+ const serious = findings.filter((item) => item.severity === "critical" || item.severity === "high").length;
27
+ if (value.result === "fail" && !failed && !serious) return "verdict fail needs a failed criterion or a critical or high finding";
28
+ if ((value.result === "pass" || value.result === "pass_with_findings") && (failed || serious)) {
29
+ return `verdict ${value.result} cannot have ${failed ? "a failed criterion" : "a critical or high finding"}; the result is fail`;
30
+ }
31
+ if (value.result === "pass" && findings.length) return "verdict pass cannot have findings; use pass_with_findings";
32
+ if (value.result === "pass_with_findings" && !findings.length) return "verdict pass_with_findings needs findings";
33
+ return { result: value.result, criteria: value.criteria, ...findings.length ? { findings } : {} };
34
+ }
35
+ function verdictLine(verdict) {
36
+ const count = (result) => verdict.criteria.filter((item) => item.result === result).length;
37
+ const parts = [`${count("pass")} passed`, count("fail") ? `${count("fail")} failed` : "", count("not_checked") ? `${count("not_checked")} not checked` : "", verdict.findings?.length ? `${verdict.findings.length} findings` : ""];
38
+ return `verdict ${verdict.result.toUpperCase()} (${parts.filter(Boolean).join(", ")})`;
39
+ }
40
+ function parseHandoff(args) {
41
+ if (!args || typeof args !== "object" || Array.isArray(args)) return "Handoff must be an object";
42
+ const allowed = ["outcome", "summary", "ownership", "verdict", ...HANDOFF_LISTS];
43
+ if (Object.keys(args).some((key) => !allowed.includes(key))) return "Unknown handoff field";
44
+ if (!HANDOFF_OUTCOMES.includes(args.outcome)) return `outcome must be one of ${HANDOFF_OUTCOMES.join(", ")}`;
45
+ if (typeof args.summary !== "string" || !args.summary.trim()) return "summary is required";
46
+ if (args.ownership !== void 0 && typeof args.ownership !== "string") return "ownership must be a string";
47
+ const handoff = { outcome: args.outcome, summary: args.summary };
48
+ for (const key of HANDOFF_LISTS) {
49
+ if (args[key] === void 0) continue;
50
+ if (!Array.isArray(args[key]) || args[key].length > 100 || !args[key].every((item) => typeof item === "string" && item.trim())) {
51
+ return `${key} must be a list of at most 100 nonempty strings`;
52
+ }
53
+ handoff[key] = args[key];
54
+ }
55
+ if (args.ownership?.trim()) handoff.ownership = args.ownership;
56
+ if (args.verdict !== void 0) {
57
+ const verdict = parseVerdict(args.verdict);
58
+ if (typeof verdict === "string") return verdict;
59
+ handoff.verdict = verdict;
60
+ }
61
+ if (JSON.stringify(handoff).length > 32e3) return "Handoff exceeds 32000 characters; summarize and point to files instead";
62
+ return handoff;
63
+ }
64
+ export {
65
+ CRITERION_RESULTS,
66
+ HANDOFF_LISTS,
67
+ HANDOFF_OUTCOMES,
68
+ SEVERITIES,
69
+ VERDICT_RESULTS,
70
+ parseHandoff,
71
+ parseVerdict,
72
+ verdictLine
73
+ };
@@ -0,0 +1,24 @@
1
+ export { createAlpRuntime, DEFAULT_TEAM } from './runtime.js';
2
+ export type { AlpRuntime, RuntimeOptions, OpenOptions, RuntimeTransport, PromptInput, PromptContent, SessionPreview } from './runtime.js';
3
+ export type { AlpEvent, AlpError, Envelope, SessionSnapshot, TimelineItem, TurnOrigin, AssignmentSnapshot, UserQuestion, TreeStatus, SessionState } from './events.js';
4
+ export { resolveSession, InstructionsAdapter, skillDescription } from './resolve.js';
5
+ export type { SessionSpec, HostMcpServer, ResolvedSession, RuntimeKind } from './resolve.js';
6
+ export { DEFAULT_MODEL, DEFAULT_CLAUDE_MODEL, models, modes, thinkingOptions, thinkingOptionsFor } from './catalog.js';
7
+ export { CodexTransport } from './transport.js';
8
+ export { usageAllows } from './runtime-context.js';
9
+ export { AcpTransport, acpDecision, acpEnv, acpPermission } from './acp-transport.js';
10
+ export type { AcpProvider } from './acp-transport.js';
11
+ export { AntigravityTransport } from './antigravity-transport.js';
12
+ export type { AntigravityProvider } from './antigravity-transport.js';
13
+ export { ClaudeTransport, claudePermissions, claudeSandboxAvailable, skillPlugin, SKILL_PLUGIN, toolShapes as claudeToolShapes } from './claude-transport.js';
14
+ export { MAIL_BATCH_CHARS, MAIL_BODY_CHARS, publicEvent, renderMail, takeBatch, USER } from './mailbox.js';
15
+ export type { MailEvent, MailKind } from './mailbox.js';
16
+ export { createCopy, gitEnvironment, reclaimCopies, reclaimWorktrees, removeCopy } from './workspace.js';
17
+ export { OWN_START, processStartedAt, sameProcessAlive } from './process-info.js';
18
+ export type { Worktree, WorktreeChange } from './workspace.js';
19
+ export { PIN_KINDS, renderBoard, renderPin } from './board.js';
20
+ export { CHOICES, isVietnamese, languageInstruction, words } from './language.js';
21
+ export type { Words } from './language.js';
22
+ export type { Pin, PinKind } from './board.js';
23
+ export { ProviderRegistry, createProviderRegistry } from './providers.js';
24
+ export type { ProviderContext, ProviderFactory, ProviderFactories } from './providers.js';
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ // packages/runtime/src/index.ts
2
+ import { createAlpRuntime, DEFAULT_TEAM } from "./runtime.js";
3
+ import { resolveSession, InstructionsAdapter, skillDescription } from "./resolve.js";
4
+ import { DEFAULT_MODEL, DEFAULT_CLAUDE_MODEL, models, modes, thinkingOptions, thinkingOptionsFor } from "./catalog.js";
5
+ import { CodexTransport } from "./transport.js";
6
+ import { usageAllows } from "./runtime-context.js";
7
+ import { AcpTransport, acpDecision, acpEnv, acpPermission } from "./acp-transport.js";
8
+ import { AntigravityTransport } from "./antigravity-transport.js";
9
+ import { ClaudeTransport, claudePermissions, claudeSandboxAvailable, skillPlugin, SKILL_PLUGIN, toolShapes } from "./claude-transport.js";
10
+ import { MAIL_BATCH_CHARS, MAIL_BODY_CHARS, publicEvent, renderMail, takeBatch, USER } from "./mailbox.js";
11
+ import { createCopy, gitEnvironment, reclaimCopies, reclaimWorktrees, removeCopy } from "./workspace.js";
12
+ import { OWN_START, processStartedAt, sameProcessAlive } from "./process-info.js";
13
+ import { PIN_KINDS, renderBoard, renderPin } from "./board.js";
14
+ import { CHOICES, isVietnamese, languageInstruction, words } from "./language.js";
15
+ import { ProviderRegistry, createProviderRegistry } from "./providers.js";
16
+ export {
17
+ AcpTransport,
18
+ AntigravityTransport,
19
+ CHOICES,
20
+ ClaudeTransport,
21
+ CodexTransport,
22
+ DEFAULT_CLAUDE_MODEL,
23
+ DEFAULT_MODEL,
24
+ DEFAULT_TEAM,
25
+ InstructionsAdapter,
26
+ MAIL_BATCH_CHARS,
27
+ MAIL_BODY_CHARS,
28
+ OWN_START,
29
+ PIN_KINDS,
30
+ ProviderRegistry,
31
+ SKILL_PLUGIN,
32
+ USER,
33
+ acpDecision,
34
+ acpEnv,
35
+ acpPermission,
36
+ claudePermissions,
37
+ claudeSandboxAvailable,
38
+ toolShapes as claudeToolShapes,
39
+ createAlpRuntime,
40
+ createCopy,
41
+ createProviderRegistry,
42
+ gitEnvironment,
43
+ isVietnamese,
44
+ languageInstruction,
45
+ models,
46
+ modes,
47
+ processStartedAt,
48
+ publicEvent,
49
+ reclaimCopies,
50
+ reclaimWorktrees,
51
+ removeCopy,
52
+ renderBoard,
53
+ renderMail,
54
+ renderPin,
55
+ resolveSession,
56
+ sameProcessAlive,
57
+ skillDescription,
58
+ skillPlugin,
59
+ takeBatch,
60
+ thinkingOptions,
61
+ thinkingOptionsFor,
62
+ usageAllows,
63
+ words
64
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * What ALP itself writes for the user, in the user's language (ALPD §54): approvals,
3
+ * permission and trust questions, and notices. Vietnamese and English are written
4
+ * out; any other language gets English from ALP while agents write in it.
5
+ */
6
+ export declare const isVietnamese: (language: string) => boolean;
7
+ declare const en: {
8
+ approve: string;
9
+ reject: string;
10
+ answerLine: (agent: string) => string;
11
+ skill: (agent: string, replacing: boolean, file: string, roles: string, lessons: string[]) => string;
12
+ issue: (agent: string, create: boolean, issue: number | undefined, where: string) => string;
13
+ issueTitle: string;
14
+ issueLabels: string;
15
+ trust: (project: string, names: string) => string;
16
+ trustYes: string;
17
+ trustNo: string;
18
+ allowOnce: string;
19
+ alwaysAllow: string;
20
+ deny: string;
21
+ permission: (agent: string, what: string, byRule: boolean, profile: string | undefined, mode: string, always: string | undefined) => string;
22
+ limitReached: (runtime: string, resetsAt: string | undefined, other: string | undefined, autoResume: boolean, kind: string) => string;
23
+ limitLifted: (runtime: string, kind: string) => string;
24
+ usageWarning: (runtime: string, used: string, resetsAt: string | undefined) => string;
25
+ /** The web app's answer to a Paseo feature ALP has not got yet (ALPD §62). */
26
+ inDevelopment: string;
27
+ resumed: (what: string, by: string, parked: boolean) => string;
28
+ paused: (what: string, reason: string | undefined, runtime: string | undefined, now: boolean) => string;
29
+ };
30
+ export type Words = typeof en;
31
+ export declare function words(language: string): Words;
32
+ /** Every label a choice may be answered with, in any language ALP writes. */
33
+ export declare const CHOICES: {
34
+ trust: string[];
35
+ allowOnce: string[];
36
+ alwaysAllow: string[];
37
+ deny: string[];
38
+ };
39
+ /** The line every agent reads about the user's language. */
40
+ export declare function languageInstruction(language: string, agent: string, main: boolean): string;
41
+ export {};
@@ -0,0 +1,68 @@
1
+ // packages/runtime/src/language.ts
2
+ var isVietnamese = (language) => /^(vi|vie|vietnamese|tiếng việt|tieng viet)$/i.test(language.trim());
3
+ var en = {
4
+ approve: "Approve",
5
+ reject: "Reject",
6
+ answerLine: (agent) => `Answer Approve or Reject; any other answer goes back to ${agent} as feedback.`,
7
+ skill: (agent, replacing, file, roles, lessons) => `${agent} proposes a skill distilled from its lessons${replacing ? ", replacing the skill of that name" : ""}. Approve to save it as ${file}. Roles that get it: ${roles}.` + (lessons.length ? ` These lessons move into it and leave the lessons files:
8
+ ${lessons.map((lesson) => `- ${lesson}`).join("\n")}` : ""),
9
+ issue: (agent, create, issue, where) => `${agent} wants to ${create ? `open an issue in ${where}` : `comment on issue #${issue} in ${where}`}. It is posted with your GitHub account, and anyone who can see the repository can read it.`,
10
+ issueTitle: "Title",
11
+ issueLabels: "Labels",
12
+ trust: (project, names) => `This project's hooks run shell commands from ${project} on your machine: ${names}. Trust this workspace's hooks? ALP asks once: after you agree, its hooks run without asking, including hooks added or changed later.`,
13
+ trustYes: "Trust this workspace",
14
+ trustNo: "Not now",
15
+ allowOnce: "Allow once",
16
+ alwaysAllow: "Always allow",
17
+ deny: "Deny",
18
+ permission: (agent, what, byRule, profile, mode, always) => `${agent} wants to ${what}. ` + (byRule ? `Its permission profile ${profile} asks you each time.` : `Its ${mode} mode does not allow that.`) + (always ? ` Always allow adds ${always} to profile ${profile}.` : ""),
19
+ limitReached: (runtime, resetsAt, other, autoResume, kind) => `${runtime} usage limit reached${resetsAt ? `; it resets ${resetsAt}` : ""}. ALP paused delegation to ${runtime} agents and parked their assignments${other ? `; ${other} agents keep working` : ""}. ${autoResume ? `ALP asks ${runtime} every minute and resumes it as soon as the limit lifts.` : `ALP asks ${runtime} every minute and tells you when the limit lifts; then run alp resume ${kind}.`}`,
20
+ limitLifted: (runtime, kind) => `${runtime} says its usage limit has lifted. Run alp resume ${kind} to continue its work.`,
21
+ usageWarning: (runtime, used, resetsAt) => `${runtime} has used ${used} of a usage window${resetsAt ? ` that resets ${resetsAt}` : ""}.`,
22
+ /** The web app's answer to a Paseo feature ALP has not got yet (ALPD §62). */
23
+ inDevelopment: "This feature is in development",
24
+ resumed: (what, by, parked) => `${what} resumed by ${by}.${parked ? " Parked assignments continue." : ""}`,
25
+ paused: (what, reason, runtime, now) => `${what} paused by the user${reason ? `: ${reason}` : ""}. Delegation${runtime ? ` to ${runtime} agents` : ""} waits${now ? ", and running assignments park where they are" : "; running turns finish"}. alp resume continues.`
26
+ };
27
+ var viWhat = (what) => what.replace(/^run /, "ch\u1EA1y ").replace(/^change files under /, "s\u1EEDa file trong ").replace(/^change files/, "s\u1EEDa file").replace(/^use /, "d\xF9ng ");
28
+ var vi = {
29
+ approve: "Duy\u1EC7t",
30
+ reject: "T\u1EEB ch\u1ED1i",
31
+ answerLine: (agent) => `Ch\u1ECDn Duy\u1EC7t ho\u1EB7c T\u1EEB ch\u1ED1i; c\xE2u tr\u1EA3 l\u1EDDi kh\xE1c s\u1EBD \u0111\u01B0\u1EE3c g\u1EEDi l\u1EA1i cho ${agent} l\xE0m g\xF3p \xFD.`,
32
+ skill: (agent, replacing, file, roles, lessons) => `${agent} \u0111\u1EC1 xu\u1EA5t m\u1ED9t skill \u0111\xFAc k\u1EBFt t\u1EEB c\xE1c b\xE0i h\u1ECDc${replacing ? ", thay cho skill c\xF9ng t\xEAn" : ""}. Duy\u1EC7t \u0111\u1EC3 l\u01B0u th\xE0nh ${file}. C\xE1c vai \u0111\u01B0\u1EE3c d\xF9ng: ${roles}.` + (lessons.length ? ` C\xE1c b\xE0i h\u1ECDc sau chuy\u1EC3n v\xE0o skill v\xE0 r\u1EDDi kh\u1ECFi file b\xE0i h\u1ECDc:
33
+ ${lessons.map((lesson) => `- ${lesson}`).join("\n")}` : ""),
34
+ issue: (agent, create, issue, where) => `${agent} mu\u1ED1n ${create ? `m\u1EDF m\u1ED9t issue trong ${where}` : `b\xECnh lu\u1EADn v\xE0o issue #${issue} trong ${where}`}. N\u1ED9i dung \u0111\u01B0\u1EE3c \u0111\u0103ng b\u1EB1ng t\xE0i kho\u1EA3n GitHub c\u1EE7a b\u1EA1n, ai xem \u0111\u01B0\u1EE3c repository \u0111\u1EC1u \u0111\u1ECDc \u0111\u01B0\u1EE3c.`,
35
+ issueTitle: "Ti\xEAu \u0111\u1EC1",
36
+ issueLabels: "Nh\xE3n",
37
+ trust: (project, names) => `C\xE1c hook c\u1EE7a project n\xE0y ch\u1EA1y l\u1EC7nh shell t\u1EEB ${project} tr\xEAn m\xE1y c\u1EE7a b\u1EA1n: ${names}. Tin c\u1EADy c\xE1c hook c\u1EE7a workspace n\xE0y? ALP ch\u1EC9 h\u1ECFi m\u1ED9t l\u1EA7n: khi b\u1EA1n \u0111\u1ED3ng \xFD, c\xE1c hook ch\u1EA1y m\xE0 kh\xF4ng h\u1ECFi l\u1EA1i, k\u1EC3 c\u1EA3 hook th\xEAm ho\u1EB7c s\u1EEDa sau n\xE0y.`,
38
+ trustYes: "Tin c\u1EADy workspace n\xE0y",
39
+ trustNo: "\u0110\u1EC3 sau",
40
+ allowOnce: "Cho ph\xE9p l\u1EA7n n\xE0y",
41
+ alwaysAllow: "Lu\xF4n cho ph\xE9p",
42
+ deny: "T\u1EEB ch\u1ED1i",
43
+ permission: (agent, what, byRule, profile, mode, always) => `${agent} mu\u1ED1n ${viWhat(what)}. ` + (byRule ? `H\u1ED3 s\u01A1 quy\u1EC1n ${profile} c\u1EE7a n\xF3 y\xEAu c\u1EA7u h\u1ECFi b\u1EA1n m\u1ED7i l\u1EA7n.` : `Ch\u1EBF \u0111\u1ED9 ${mode} c\u1EE7a n\xF3 kh\xF4ng cho ph\xE9p vi\u1EC7c n\xE0y.`) + (always ? ` Lu\xF4n cho ph\xE9p s\u1EBD th\xEAm ${always} v\xE0o h\u1ED3 s\u01A1 ${profile}.` : ""),
44
+ limitReached: (runtime, resetsAt, other, autoResume, kind) => `${runtime} \u0111\xE3 h\u1EBFt h\u1EA1n m\u1EE9c s\u1EED d\u1EE5ng${resetsAt ? `; h\u1EA1n m\u1EE9c \u0111\u1EB7t l\u1EA1i l\xFAc ${resetsAt}` : ""}. ALP t\u1EA1m d\u1EEBng giao vi\u1EC7c cho c\xE1c agent ${runtime} v\xE0 t\u1EA1m gi\u1EEF vi\u1EC7c c\u1EE7a ch\xFAng${other ? `; c\xE1c agent ${other} v\u1EABn l\xE0m ti\u1EBFp` : ""}. ${autoResume ? `ALP h\u1ECFi ${runtime} m\u1ED7i ph\xFAt v\xE0 t\u1EF1 ch\u1EA1y l\u1EA1i ngay khi c\xF3 h\u1EA1n m\u1EE9c.` : `ALP h\u1ECFi ${runtime} m\u1ED7i ph\xFAt v\xE0 b\xE1o khi c\xF3 h\u1EA1n m\u1EE9c tr\u1EDF l\u1EA1i; khi \u0111\xF3 ch\u1EA1y alp resume ${kind}.`}`,
45
+ limitLifted: (runtime, kind) => `${runtime} b\xE1o \u0111\xE3 c\xF3 h\u1EA1n m\u1EE9c tr\u1EDF l\u1EA1i. Ch\u1EA1y alp resume ${kind} \u0111\u1EC3 l\xE0m ti\u1EBFp vi\u1EC7c c\u1EE7a n\xF3.`,
46
+ usageWarning: (runtime, used, resetsAt) => `${runtime} \u0111\xE3 d\xF9ng ${used} h\u1EA1n m\u1EE9c c\u1EE7a k\u1EF3 n\xE0y${resetsAt ? `, k\u1EF3 \u0111\u1EB7t l\u1EA1i l\xFAc ${resetsAt}` : ""}.`,
47
+ inDevelopment: "T\xEDnh n\u0103ng \u0111ang ph\xE1t tri\u1EC3n",
48
+ resumed: (what, by, parked) => `${what} \u0111\xE3 ch\u1EA1y l\u1EA1i, b\u1EDFi ${by}.${parked ? " C\xE1c vi\u1EC7c \u0111ang t\u1EA1m gi\u1EEF \u0111\u01B0\u1EE3c l\xE0m ti\u1EBFp." : ""}`,
49
+ paused: (what, reason, runtime, now) => `${what} \u0111\xE3 b\u1ECB ng\u01B0\u1EDDi d\xF9ng t\u1EA1m d\u1EEBng${reason ? `: ${reason}` : ""}. Vi\u1EC7c giao${runtime ? ` cho c\xE1c agent ${runtime}` : ""} ph\u1EA3i ch\u1EDD${now ? ", c\xE1c vi\u1EC7c \u0111ang ch\u1EA1y \u0111\u01B0\u1EE3c t\u1EA1m gi\u1EEF t\u1EA1i ch\u1ED7" : "; c\xE1c l\u01B0\u1EE3t \u0111ang ch\u1EA1y v\u1EABn ch\u1EA1y h\u1EBFt"}. Ch\u1EA1y alp resume \u0111\u1EC3 ti\u1EBFp t\u1EE5c.`
50
+ };
51
+ function words(language) {
52
+ return isVietnamese(language) ? vi : en;
53
+ }
54
+ var CHOICES = {
55
+ trust: [en.trustYes, vi.trustYes].map((label) => label.toLowerCase()),
56
+ allowOnce: [en.allowOnce, vi.allowOnce].map((label) => label.toLowerCase()),
57
+ alwaysAllow: [en.alwaysAllow, vi.alwaysAllow].map((label) => label.toLowerCase()),
58
+ deny: [en.deny, vi.deny].map((label) => label.toLowerCase())
59
+ };
60
+ function languageInstruction(language, agent, main) {
61
+ return main ? `The user reads ${language}. Write in ${language} everything the user reads: your replies, questions you ask with alp_ask (and their options), approval requests and what you say about them, gate notes, and the titles and descriptions of tasks you create. Briefs, handoffs and mail between agents may use any language. GitHub issues and comments follow the repository's language; tell the user about them in ${language}.` : agent === "supervisor" ? `The user reads ${language}: main must write everything the user reads in ${language}.` : `The user reads ${language}. A question you put to the user with alp_ask, and its options, must be in ${language}; briefs, handoffs and mail between agents may use any language.`;
62
+ }
63
+ export {
64
+ CHOICES,
65
+ isVietnamese,
66
+ languageInstruction,
67
+ words
68
+ };
package/dist/live.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import type { RuntimeKind, SessionSpec } from './resolve.js';
2
+ import type { Worktree } from './workspace.js';
3
+ /**
4
+ * Assignments that are running, kept on disk so the next alpd can continue them
5
+ * after a crash or a restart (plans/reference/ALPD.md §31). An entry is written once
6
+ * its native thread exists and removed when the assignment starts to finish, so a
7
+ * crash while finishing never runs finished work again.
8
+ */
9
+ export type LiveEntry = {
10
+ assignmentId: string;
11
+ rootId: string;
12
+ /** The requester's session, which must be open again before this one continues. */
13
+ parentId: string;
14
+ callId: string;
15
+ agent: string;
16
+ /** The project root, whose tasks and board the assignment uses. */
17
+ project: string;
18
+ ancestry: string[];
19
+ runtime: RuntimeKind;
20
+ /** The model without its runtime prefix, as the session resolved it. */
21
+ model: string;
22
+ threadId: string;
23
+ /** The spec the assignment's session was opened with. */
24
+ spec: SessionSpec;
25
+ delegation: boolean;
26
+ mode: string;
27
+ isolation: 'shared' | 'worktree';
28
+ taskId?: string;
29
+ worktree?: Worktree;
30
+ /** The requester's directory, when the assignment worked in a disposable copy of it. */
31
+ copyOf?: string;
32
+ lease?: string;
33
+ fingerprint?: string;
34
+ startedAt: number;
35
+ /** The brief it was given, repeated to it after a compaction (ALPD §57). */
36
+ brief?: string;
37
+ /** The alpd process that wrote the entry; another epoch means an earlier alpd. */
38
+ epoch: string;
39
+ };
40
+ /** The book of running assignments, in `file` when given, else in memory. */
41
+ export declare function createLiveBook(file?: string): {
42
+ all: () => LiveEntry[];
43
+ get: (assignmentId: string) => LiveEntry | undefined;
44
+ put(entry: LiveEntry): Promise<void>;
45
+ remove(assignmentId: string): Promise<void>;
46
+ flush: () => Promise<void>;
47
+ };
48
+ export type LiveBook = ReturnType<typeof createLiveBook>;
package/dist/live.js ADDED
@@ -0,0 +1,45 @@
1
+ // packages/runtime/src/live.ts
2
+ import { readFileSync } from "node:fs";
3
+ import { mkdir, rename, writeFile } from "node:fs/promises";
4
+ import { randomBytes } from "node:crypto";
5
+ import path from "node:path";
6
+ import { createLogger } from "@anhlp/alp-core/log";
7
+ var valid = (entry) => !!entry && typeof entry === "object" && ["assignmentId", "rootId", "parentId", "agent", "project", "runtime", "model", "threadId", "mode", "isolation", "epoch"].every((key) => typeof entry[key] === "string") && Array.isArray(entry.ancestry) && !!entry.spec && typeof entry.spec === "object" && typeof entry.startedAt === "number";
8
+ function createLiveBook(file) {
9
+ let entries = [];
10
+ if (file) {
11
+ try {
12
+ entries = JSON.parse(readFileSync(file, "utf8")).filter(valid);
13
+ } catch {
14
+ }
15
+ }
16
+ let writes = Promise.resolve();
17
+ const save = () => {
18
+ if (!file) return writes;
19
+ const body = JSON.stringify(entries, null, 2) + "\n";
20
+ writes = writes.then(async () => {
21
+ await mkdir(path.dirname(file), { recursive: true, mode: 448 });
22
+ const temporary = `${file}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
23
+ await writeFile(temporary, body, { mode: 384 });
24
+ await rename(temporary, file);
25
+ }).catch((error) => createLogger({ component: "runtime" }).error("could not save the live assignments", { code: "ALP_PERSISTENCE_WRITE_FAILED", operation: "live assignments", error }));
26
+ return writes;
27
+ };
28
+ return {
29
+ all: () => [...entries],
30
+ get: (assignmentId) => entries.find((entry) => entry.assignmentId === assignmentId),
31
+ put(entry) {
32
+ entries = [...entries.filter((existing) => existing.assignmentId !== entry.assignmentId), entry];
33
+ return save();
34
+ },
35
+ remove(assignmentId) {
36
+ if (!entries.some((entry) => entry.assignmentId === assignmentId)) return writes;
37
+ entries = entries.filter((entry) => entry.assignmentId !== assignmentId);
38
+ return save();
39
+ },
40
+ flush: () => writes
41
+ };
42
+ }
43
+ export {
44
+ createLiveBook
45
+ };