@anhlp/alp-runtime 0.0.0-stage → 0.0.1-beta.4
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/LICENSE +202 -0
- package/NOTICE +7 -0
- package/README.md +6 -2
- package/dist/acp-transport.d.ts +130 -0
- package/dist/acp-transport.js +511 -0
- package/dist/antigravity-transport.d.ts +82 -0
- package/dist/antigravity-transport.js +619 -0
- package/dist/assignment-results.d.ts +18 -0
- package/dist/assignment-results.js +77 -0
- package/dist/attachments.d.ts +84 -0
- package/dist/attachments.js +213 -0
- package/dist/board-domain.d.ts +56 -0
- package/dist/board-domain.js +157 -0
- package/dist/board.d.ts +21 -0
- package/dist/board.js +69 -0
- package/dist/catalog.d.ts +31 -0
- package/dist/catalog.js +38 -0
- package/dist/claude-transport.d.ts +202 -0
- package/dist/claude-transport.js +817 -0
- package/dist/configure.d.ts +44 -0
- package/dist/configure.js +64 -0
- package/dist/contracts.d.ts +327 -0
- package/dist/contracts.js +16 -0
- package/dist/conversation.d.ts +45 -0
- package/dist/conversation.js +62 -0
- package/dist/events.d.ts +1 -0
- package/dist/events.js +0 -0
- package/dist/github.d.ts +10 -0
- package/dist/github.js +39 -0
- package/dist/handoff.d.ts +36 -0
- package/dist/handoff.js +73 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +87 -0
- package/dist/language.d.ts +41 -0
- package/dist/language.js +68 -0
- package/dist/live.d.ts +48 -0
- package/dist/live.js +45 -0
- package/dist/mailbox.d.ts +26 -0
- package/dist/mailbox.js +38 -0
- package/dist/mcp-tools.d.ts +115 -0
- package/dist/mcp-tools.js +71 -0
- package/dist/permission-path.d.ts +2 -0
- package/dist/permission-path.js +44 -0
- package/dist/process-info.d.ts +14 -0
- package/dist/process-info.js +32 -0
- package/dist/providers.d.ts +28 -0
- package/dist/providers.js +71 -0
- package/dist/recall.d.ts +42 -0
- package/dist/recall.js +60 -0
- package/dist/resolve.d.ts +121 -0
- package/dist/resolve.js +238 -0
- package/dist/runtime-context.d.ts +43 -0
- package/dist/runtime-context.js +61 -0
- package/dist/runtime.d.ts +11 -0
- package/dist/runtime.js +3976 -0
- package/dist/session-config.d.ts +2102 -0
- package/dist/session-config.js +205 -0
- package/dist/session-state.d.ts +166 -0
- package/dist/session-state.js +0 -0
- package/dist/tool-detail.d.ts +81 -0
- package/dist/tool-detail.js +332 -0
- package/dist/tool-results.d.ts +12 -0
- package/dist/tool-results.js +25 -0
- package/dist/tools.d.ts +719 -0
- package/dist/tools.js +396 -0
- package/dist/transport.d.ts +47 -0
- package/dist/transport.js +169 -0
- package/dist/usage.d.ts +25 -0
- package/dist/usage.js +60 -0
- package/dist/workspace.d.ts +101 -0
- package/dist/workspace.js +259 -0
- package/dist/worktree-domain.d.ts +89 -0
- package/dist/worktree-domain.js +199 -0
- package/package.json +262 -4
- package/src/acp-transport.ts +508 -0
- package/src/antigravity-transport.ts +667 -0
- package/src/assignment-results.ts +64 -0
- package/src/attachments.ts +254 -0
- package/src/board-domain.ts +185 -0
- package/src/board.ts +80 -0
- package/src/catalog.ts +32 -0
- package/src/claude-transport.ts +938 -0
- package/src/configure.ts +93 -0
- package/src/contracts.ts +318 -0
- package/src/conversation.ts +81 -0
- package/src/events.ts +1 -0
- package/src/github.ts +37 -0
- package/src/handoff.ts +86 -0
- package/src/index.ts +32 -0
- package/src/language.ts +102 -0
- package/src/live.ts +86 -0
- package/src/mailbox.ts +49 -0
- package/src/mcp-tools.ts +73 -0
- package/src/permission-path.ts +37 -0
- package/src/process-info.ts +40 -0
- package/src/providers.ts +84 -0
- package/src/recall.ts +85 -0
- package/src/resolve.ts +230 -0
- package/src/runtime-context.ts +49 -0
- package/src/runtime.ts +4691 -0
- package/src/session-config.ts +304 -0
- package/src/session-state.ts +164 -0
- package/src/tool-detail.ts +397 -0
- package/src/tool-results.ts +26 -0
- package/src/tools.ts +397 -0
- package/src/transport.ts +116 -0
- package/src/usage.ts +79 -0
- package/src/workspace.ts +318 -0
- package/src/worktree-domain.ts +220 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ConfigureChange, EffectiveConfig } from '@anhlp/alp-protocol/session';
|
|
2
|
+
import type { ResolvedSession, RuntimeKind } from './resolve.js';
|
|
3
|
+
export type { ConfigureChange, EffectiveConfig } from '@anhlp/alp-protocol/session';
|
|
4
|
+
/** What a client may change on a live root; each absent field keeps the session's own. */
|
|
5
|
+
export type ConfigureChanges = {
|
|
6
|
+
model?: string;
|
|
7
|
+
thinking?: string;
|
|
8
|
+
team?: string;
|
|
9
|
+
mode?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Runtimes whose roots alpd reconfigures live (macOS and Linux; Codex and Claude, issue #121 closure scope). */
|
|
12
|
+
export declare const CONFIGURABLE: ReadonlySet<RuntimeKind>;
|
|
13
|
+
/**
|
|
14
|
+
* The native model `model` names for a session of `runtime`: unprefixed, or prefixed with that runtime
|
|
15
|
+
* (`codex:`, `claude:`, or with a slash). A prefix of another runtime is a migration, never taken.
|
|
16
|
+
*/
|
|
17
|
+
export declare function nativeModelOf(runtime: RuntimeKind, model: string): {
|
|
18
|
+
model: string;
|
|
19
|
+
} | {
|
|
20
|
+
runtime: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Whether the host offers `model` on `runtime`: ALP's catalog (core MODELS, what library.models and
|
|
24
|
+
* catalog.get list), a model the project's settings give an agent of the team, or the model the session
|
|
25
|
+
* runs already. Never an arbitrary string.
|
|
26
|
+
*/
|
|
27
|
+
export declare function offered(runtime: RuntimeKind, model: string, mapping: ResolvedSession): boolean;
|
|
28
|
+
/** MCP servers of a resolved session by name and kind: a stdio one runs a command on the host. */
|
|
29
|
+
export declare function mcpOf(mapping: ResolvedSession): EffectiveConfig['mcpServers'];
|
|
30
|
+
/**
|
|
31
|
+
* What of a resolved session runs commands on the daemon host: its stdio MCP servers (command, args,
|
|
32
|
+
* env names) and its hooks (event and command), each as a digest. A change may keep or drop these,
|
|
33
|
+
* never add one (a team change that brings an executable resource is refused).
|
|
34
|
+
*/
|
|
35
|
+
export declare function executablesOf(mapping: ResolvedSession): Set<string>;
|
|
36
|
+
/** The effective configuration of a resolved root, with its delegation targets, ALP tools and instructions digest. */
|
|
37
|
+
export declare function effectiveOf(runtime: RuntimeKind, mapping: ResolvedSession, extra: {
|
|
38
|
+
delegates: string[];
|
|
39
|
+
tools: string[];
|
|
40
|
+
supervisor: boolean;
|
|
41
|
+
instructionsSha?: string;
|
|
42
|
+
}): EffectiveConfig;
|
|
43
|
+
/** Which of model, thinking, team and mode differ between two effective configurations. */
|
|
44
|
+
export declare function changesOf(current: EffectiveConfig, proposed: EffectiveConfig): ConfigureChange[];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// packages/runtime/src/configure.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { MODELS } from "@anhlp/alp-core/models";
|
|
4
|
+
import { profileModel } from "@anhlp/alp-core/profiles";
|
|
5
|
+
var CONFIGURABLE = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
6
|
+
var PREFIXED = /^(codex|claude|antigravity|acp)[:/](.*)$/s;
|
|
7
|
+
function nativeModelOf(runtime, model) {
|
|
8
|
+
const match = PREFIXED.exec(model);
|
|
9
|
+
if (!match) return { model };
|
|
10
|
+
return match[1] === runtime ? { model: match[2] } : { runtime: match[1] };
|
|
11
|
+
}
|
|
12
|
+
function offered(runtime, model, mapping) {
|
|
13
|
+
if (model === mapping.model) return true;
|
|
14
|
+
if (MODELS.some((entry) => entry.runtime === runtime && entry.id === model)) return true;
|
|
15
|
+
return Object.values(mapping.modelProfiles ?? {}).flat().some((profile) => profileModel(profile) === `${runtime}:${model}`);
|
|
16
|
+
}
|
|
17
|
+
function mcpOf(mapping) {
|
|
18
|
+
return Object.entries(mapping.mcp ?? {}).map(([name, server]) => ({ name, kind: server && typeof server === "object" && "command" in server ? "stdio" : "http" }));
|
|
19
|
+
}
|
|
20
|
+
var digest = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
21
|
+
function executablesOf(mapping) {
|
|
22
|
+
const found = /* @__PURE__ */ new Set();
|
|
23
|
+
for (const [name, server] of Object.entries(mapping.mcp ?? {})) {
|
|
24
|
+
if (server && typeof server === "object" && "command" in server) found.add(`mcp:${name}:${digest([server.command, server.args ?? [], Object.keys(server.env ?? {}).sort()])}`);
|
|
25
|
+
}
|
|
26
|
+
for (const hook of mapping.hooks ?? []) found.add(`hook:${hook.name ?? ""}:${digest([hook.event, hook.command])}`);
|
|
27
|
+
return found;
|
|
28
|
+
}
|
|
29
|
+
function effectiveOf(runtime, mapping, extra) {
|
|
30
|
+
return {
|
|
31
|
+
team: mapping.workflow.mode,
|
|
32
|
+
...mapping.team?.label ? { teamLabel: mapping.team.label } : {},
|
|
33
|
+
agent: mapping.agent.name,
|
|
34
|
+
runtime,
|
|
35
|
+
model: mapping.model,
|
|
36
|
+
thinking: mapping.thinking,
|
|
37
|
+
mode: mapping.mode,
|
|
38
|
+
...mapping.permissions?.name ? { permissionProfile: mapping.permissions.name } : {},
|
|
39
|
+
supervisor: extra.supervisor,
|
|
40
|
+
maxPeers: mapping.workflow.maxPeers,
|
|
41
|
+
delegates: [...extra.delegates],
|
|
42
|
+
tools: [...extra.tools],
|
|
43
|
+
mcpServers: mcpOf(mapping),
|
|
44
|
+
hooks: (mapping.hooks ?? []).length,
|
|
45
|
+
...extra.instructionsSha ? { instructionsSha: extra.instructionsSha } : {}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function changesOf(current, proposed) {
|
|
49
|
+
const changes = [];
|
|
50
|
+
if (current.model !== proposed.model) changes.push("model");
|
|
51
|
+
if (current.thinking !== proposed.thinking) changes.push("thinking");
|
|
52
|
+
if (current.team !== proposed.team) changes.push("team");
|
|
53
|
+
if (current.mode !== proposed.mode) changes.push("mode");
|
|
54
|
+
return changes;
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
CONFIGURABLE,
|
|
58
|
+
changesOf,
|
|
59
|
+
effectiveOf,
|
|
60
|
+
executablesOf,
|
|
61
|
+
mcpOf,
|
|
62
|
+
nativeModelOf,
|
|
63
|
+
offered
|
|
64
|
+
};
|
|
@@ -0,0 +1,327 @@
|
|
|
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
|
+
import type { AttachmentReader } from './attachments.js';
|
|
11
|
+
export type RuntimeTransport = {
|
|
12
|
+
request(method: string, params: any): Promise<any>;
|
|
13
|
+
initialize(): Promise<void>;
|
|
14
|
+
orchestrationContext?: () => Promise<unknown>;
|
|
15
|
+
onNotification(listener: (method: string, params: any) => void): void;
|
|
16
|
+
onFailure(listener: (error: unknown) => void): void;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
onRequest?: (listener: (method: string, params: any) => Promise<unknown>) => void;
|
|
19
|
+
/** Whether turn input may hold images (`{ type: 'image', url: 'data:…' }`); omitted, Codex and Claude do. */
|
|
20
|
+
takesImages?: () => boolean;
|
|
21
|
+
};
|
|
22
|
+
export type RuntimeOptions = {
|
|
23
|
+
/** Native adapter overrides, isolated to this runtime; the legacy transport hook takes precedence. */
|
|
24
|
+
providers?: ProviderFactories;
|
|
25
|
+
/** An explicit registry, including custom adapter factories registered by the host. */
|
|
26
|
+
providerRegistry?: ProviderRegistry;
|
|
27
|
+
codexCommand?: string;
|
|
28
|
+
claudeCommand?: string;
|
|
29
|
+
antigravityCommand?: string;
|
|
30
|
+
environment?: NodeJS.ProcessEnv;
|
|
31
|
+
/**
|
|
32
|
+
* Optional transport factory for tests/custom runtimes.
|
|
33
|
+
* When omitted, ALP creates CodexTransport, ClaudeTransport, or AntigravityTransport itself.
|
|
34
|
+
*/
|
|
35
|
+
transport?: (cwd: string, env: NodeJS.ProcessEnv, runtime?: RuntimeKind,
|
|
36
|
+
/** The ACP provider, when runtime is acp. */
|
|
37
|
+
acp?: AcpProvider,
|
|
38
|
+
/** The Antigravity provider, when runtime is antigravity. */
|
|
39
|
+
antigravity?: AntigravityProvider) => RuntimeTransport;
|
|
40
|
+
/** An assignment with no activity this long is reported stalled; twice this long, it fails. */
|
|
41
|
+
silentForMs?: number;
|
|
42
|
+
/** How often main gets a check-in on its running assignments. Default 10 minutes; 0 turns it off. */
|
|
43
|
+
checkInMs?: number;
|
|
44
|
+
/** How often the watchdog looks at running assignments; derived from silentForMs and checkInMs when omitted. */
|
|
45
|
+
watchMs?: number;
|
|
46
|
+
/** How long alp_ask waits for the requester before returning unanswered. */
|
|
47
|
+
askTimeoutMs?: number;
|
|
48
|
+
/** How long alp_ask waits for the user before returning unanswered. Default 30 minutes. */
|
|
49
|
+
userAskTimeoutMs?: number;
|
|
50
|
+
/** Directory for per-root-session assignment logs (JSONL). Omitted disables logging. */
|
|
51
|
+
runLogDir?: string;
|
|
52
|
+
/** Starter files for projects without ALP; omitted reads the repository templates. */
|
|
53
|
+
templates?: Record<string, string>;
|
|
54
|
+
/** Where isolated assignments get their git worktrees. Default: a directory in the temp directory. */
|
|
55
|
+
worktreeDir?: string;
|
|
56
|
+
/** Where assignments whose profile has workdir copy get their disposable copies. Default: a directory in the temp directory. */
|
|
57
|
+
copyDir?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Where the runtime stages read-only copies of attached files for its sessions' tools (§12.6):
|
|
60
|
+
* alpd's `<ALP_HOME>/state/staged`. Omitted, no session takes attachment parts.
|
|
61
|
+
*/
|
|
62
|
+
attachmentStageDir?: string;
|
|
63
|
+
/** Where project boards are kept (JSONL per project). Omitted keeps them in memory only. */
|
|
64
|
+
boardDir?: string;
|
|
65
|
+
/** The user's skill library and lessons (ALP_HOME). Omitted: only skills inside the project, and project lessons. */
|
|
66
|
+
libraryDir?: string;
|
|
67
|
+
/** The language the user reads; omitted, their settings.json's language, else Vietnamese (ALPD §54). */
|
|
68
|
+
language?: string;
|
|
69
|
+
/** Where finished assignments' native threads are recorded for alp_recall. Omitted keeps the record in memory. */
|
|
70
|
+
recallFile?: string;
|
|
71
|
+
/** Permanent assignment ownership; unlike recall, results never expire. */
|
|
72
|
+
assignmentResultFile?: string;
|
|
73
|
+
/** Where pauses are kept across restarts. Omitted keeps them in memory. */
|
|
74
|
+
pauseFile?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Resume a runtime a usage limit paused as soon as the runtime says the limit lifted
|
|
77
|
+
* (ALPD §58). Default true; false tells the user instead, who resumes it.
|
|
78
|
+
*/
|
|
79
|
+
autoResume?: boolean;
|
|
80
|
+
/** How often alpd asks a runtime a usage limit paused whether the limit lifted. Default LIMIT_CHECK_MS. */
|
|
81
|
+
limitCheckMs?: number;
|
|
82
|
+
/** Where running assignments are kept, so the next alpd continues them (ALPD §31). Omitted keeps them in memory. */
|
|
83
|
+
liveFile?: string;
|
|
84
|
+
/** False parks assignments an earlier alpd left running until the user runs alp resume. Default true: they continue. */
|
|
85
|
+
recoveryResume?: boolean;
|
|
86
|
+
/** How the previous alpd ended, for what recovered sessions are told. */
|
|
87
|
+
previousExit?: {
|
|
88
|
+
kind: 'clean' | 'crash';
|
|
89
|
+
at?: string;
|
|
90
|
+
};
|
|
91
|
+
/** False starts no supervisors, for hosts and tests that do not want them. Default true. */
|
|
92
|
+
supervisor?: boolean;
|
|
93
|
+
/** Runs the GitHub CLI for alp_issue. Default: `gh` (or ALP_GH_BIN) on PATH. */
|
|
94
|
+
github?: GitHubRunner;
|
|
95
|
+
};
|
|
96
|
+
import type { NativeBase, OpenOptions, PromptInput } from '@anhlp/alp-protocol/session';
|
|
97
|
+
export type { NativeBase, OpenOptions, PromptContent, PromptInput } from '@anhlp/alp-protocol/session';
|
|
98
|
+
export type ScheduleHandler = (action: 'create' | 'list' | 'get' | 'cancel', params: Record<string, unknown>) => Promise<unknown>;
|
|
99
|
+
export type AlpRuntime = {
|
|
100
|
+
/** The daemon supplies its persistent schedule operations; standalone runtimes have none. */
|
|
101
|
+
bindSchedules?: (handler: ScheduleHandler | undefined) => void;
|
|
102
|
+
/** alpd hands over its attachment reader (§12.6) once, at start; with a staging directory, Codex and Claude sessions then take attachment parts. */
|
|
103
|
+
bindAttachments?: (reader: AttachmentReader | undefined) => void;
|
|
104
|
+
/** Whether this live session's runtime takes `{ type: 'attachment' }` parts (RuntimeAttachment) now. */
|
|
105
|
+
takesAttachments?: (sessionId: string) => boolean;
|
|
106
|
+
/** Removes the staged copy of an attachment alpd no longer keeps. */
|
|
107
|
+
dropAttachment?: (attachmentId: string) => Promise<void>;
|
|
108
|
+
onEvent(listener: (envelope: Envelope) => void): () => void;
|
|
109
|
+
/** Certifies a brand-new generated checkout; never use to adopt an existing workspace. */
|
|
110
|
+
registerWorkspace(directory: string): Promise<void>;
|
|
111
|
+
/** Reserves runtime admission while an external checkout mutation runs. */
|
|
112
|
+
withWorkspaceMutation<T>(directory: string, run: () => Promise<T>): Promise<T>;
|
|
113
|
+
/** Opens a root session under a client-chosen id. */
|
|
114
|
+
open(sessionId: string, spec: SessionSpec, options?: OpenOptions): Promise<SessionSnapshot>;
|
|
115
|
+
/** What a new root would run as, before it opens, and the teams it may choose (ALPD §60). */
|
|
116
|
+
preview(spec: SessionSpec): Promise<SessionPreview>;
|
|
117
|
+
/** Failures are reported as prompt.failed, once per clientMessageId. */
|
|
118
|
+
prompt(sessionId: string, input: PromptInput): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Atomically admits an idle scheduled prompt in the runtime queue. Busy leaves all preparation
|
|
121
|
+
* untouched. The callback may persist its attempt/receipt, then use its single-use sender;
|
|
122
|
+
* the sender is valid only during the callback. Do not enqueue runtime operations in the callback.
|
|
123
|
+
*/
|
|
124
|
+
withIdlePrompt?<T>(sessionId: string, run: (send: (input: PromptInput) => Promise<void>) => Promise<T>): Promise<{
|
|
125
|
+
busy: true;
|
|
126
|
+
} | {
|
|
127
|
+
busy: false;
|
|
128
|
+
value: T;
|
|
129
|
+
}>;
|
|
130
|
+
/** Cancels the running turn and closes the whole subtree. */
|
|
131
|
+
interrupt(sessionId: string): Promise<void>;
|
|
132
|
+
/** Atomically cancels only this active turn; false leaves the session and subtree untouched. */
|
|
133
|
+
interruptTurn?(sessionId: string, turnId: string): Promise<boolean>;
|
|
134
|
+
/** Changes permission mode while idle; undefined keeps the current mode.
|
|
135
|
+
* onApplied is awaited after apply and before session.updated; failure does not roll back permissions. */
|
|
136
|
+
configure(sessionId: string, changes: {
|
|
137
|
+
mode?: string;
|
|
138
|
+
}, options?: {
|
|
139
|
+
onApplied?: (session: SessionSnapshot) => Promise<void>;
|
|
140
|
+
}): Promise<SessionSnapshot>;
|
|
141
|
+
close(sessionId: string): Promise<void>;
|
|
142
|
+
snapshot(sessionId: string): SessionSnapshot | undefined;
|
|
143
|
+
/** Live sessions, roots before their children. */
|
|
144
|
+
list(): SessionSnapshot[];
|
|
145
|
+
/** The live tree containing a session: sessions, assignments, questions, worktrees and leases. */
|
|
146
|
+
status(sessionId: string): TreeStatus | undefined;
|
|
147
|
+
/** Questions agents asked the user that wait for an answer. */
|
|
148
|
+
questions(): UserQuestion[];
|
|
149
|
+
/** Answers or dismisses a question to the user. */
|
|
150
|
+
answer(questionId: string, reply: {
|
|
151
|
+
text?: string;
|
|
152
|
+
dismiss?: boolean;
|
|
153
|
+
reason?: string;
|
|
154
|
+
}): void;
|
|
155
|
+
/** Mails a note from the user to any live session, such as an assignment the user wants to redirect. */
|
|
156
|
+
message(sessionId: string, text: string): void;
|
|
157
|
+
/** The project board: live claims, then decisions and findings, oldest first. */
|
|
158
|
+
board(projectRoot: string): Promise<Pin[]>;
|
|
159
|
+
/** Holds delegation and wakes on one runtime, or all; now also parks running assignments there. */
|
|
160
|
+
pause(input?: {
|
|
161
|
+
runtime?: RuntimeKind;
|
|
162
|
+
now?: boolean;
|
|
163
|
+
reason?: string;
|
|
164
|
+
}): PauseState;
|
|
165
|
+
/** Lifts a pause and continues the assignments it parked. Without a runtime, lifts every pause. */
|
|
166
|
+
resume(input?: {
|
|
167
|
+
runtime?: RuntimeKind;
|
|
168
|
+
}): PauseState;
|
|
169
|
+
/** Current pauses and parked assignments. */
|
|
170
|
+
pauses(): PauseState;
|
|
171
|
+
/** Read-only, cached account usage for the web and desktop viewers. */
|
|
172
|
+
usage(refresh?: boolean): Promise<{
|
|
173
|
+
providers: Array<ProviderUsage & {
|
|
174
|
+
pause: Pause | null;
|
|
175
|
+
parked: number;
|
|
176
|
+
}>;
|
|
177
|
+
}>;
|
|
178
|
+
/** The user asks a finished assignment, or the last one on a task of the project, about its work. */
|
|
179
|
+
recall(target: {
|
|
180
|
+
assignmentId?: string;
|
|
181
|
+
taskId?: string;
|
|
182
|
+
projectRoot?: string;
|
|
183
|
+
}, question: string): Promise<RecallAnswer>;
|
|
184
|
+
/** Assignments an earlier alpd left running, which recover() can continue once their root is open again. */
|
|
185
|
+
recoverable(): LiveEntry[];
|
|
186
|
+
/** Continues work in a reopened tree. continueRoot resumes its running turn; parkedRoot restores its held continuation intent. */
|
|
187
|
+
recover(rootId: string, options?: {
|
|
188
|
+
continueRoot?: boolean;
|
|
189
|
+
parkedRoot?: string;
|
|
190
|
+
}): Promise<RecoveryOutcome[]>;
|
|
191
|
+
/** Gives up the assignments of a tree whose root cannot be opened again: their tasks go back to open. */
|
|
192
|
+
abandon(rootId: string, reason: string): Promise<RecoveryOutcome[]>;
|
|
193
|
+
/** Closes everything; assignments still running stay in the live file, so the next alpd continues them. */
|
|
194
|
+
shutdown(): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Makes the native conversation a fork or rewind continues (docs/protocol/alp-json-rpc-v1.md §12.7): the
|
|
197
|
+
* source's conversation cut at a completed turn, persisted by the runtime and leaving the source
|
|
198
|
+
* unchanged. Touches no live session. Codex forks natively now (thread/fork); Claude validates the
|
|
199
|
+
* cut against the transcript and answers a binding the first prompt forks (NativeBinding.base).
|
|
200
|
+
* Refusals throw a ConversationError; one the runtime may or may not have acted on carries
|
|
201
|
+
* `outcomeUnknown: true`. Absent: the host cannot fork or rewind.
|
|
202
|
+
*/
|
|
203
|
+
branch?(request: BranchRequest): Promise<NativeBinding>;
|
|
204
|
+
/**
|
|
205
|
+
* Moves an idle live root to another native conversation (a rewind), or reopens its own (a reload):
|
|
206
|
+
* its native process restarts and resumes `binding`, keeping the session id, its configuration and
|
|
207
|
+
* attached viewers. Refused (ConversationError session_busy) unless the whole tree is idle: no turn,
|
|
208
|
+
* pending prompt, child, assignment, question, mail or parked work. Emits session.updated.
|
|
209
|
+
* When the new binding cannot be opened, the old one is reopened and the error thrown; when neither
|
|
210
|
+
* opens, the session fails as a provider failure does.
|
|
211
|
+
*/
|
|
212
|
+
rebind?(sessionId: string, binding: NativeBinding): Promise<SessionSnapshot>;
|
|
213
|
+
/**
|
|
214
|
+
* Restarts an idle live root's native process on the conversation it runs now (a reload, §12.8):
|
|
215
|
+
* the same binding, the session's own configuration, id and viewers. Refused as rebind is. The new
|
|
216
|
+
* process must resume that very conversation (a Claude one is refused unless its transcript, or its
|
|
217
|
+
* base, exists), never an empty one in its place; when it cannot, the session closes with its
|
|
218
|
+
* conversation kept (a later open resumes it) and a ConversationError native_refused is thrown.
|
|
219
|
+
* Emits session.updated.
|
|
220
|
+
*/
|
|
221
|
+
reload?(sessionId: string): Promise<SessionSnapshot>;
|
|
222
|
+
/**
|
|
223
|
+
* Holds the turns the runtime would start by itself on this session (mail wakes) while a lifecycle
|
|
224
|
+
* operation changes its conversation (a rewind, later a reload or live configure): mail stays queued
|
|
225
|
+
* and is delivered when the last hold is released, to whatever conversation the session runs then.
|
|
226
|
+
* rebind does not refuse for mail a hold keeps. Client prompts are not held here; alpd holds them.
|
|
227
|
+
* Undefined when the session is not open.
|
|
228
|
+
*/
|
|
229
|
+
hold?(sessionId: string): (() => void) | undefined;
|
|
230
|
+
/**
|
|
231
|
+
* Whether an open session may begin a turn it has no id for yet: its turn/start (a prompt or a mail
|
|
232
|
+
* wake) is reserved, queued or on its way to the native runtime, or its process is opening or restarting. Such a
|
|
233
|
+
* turn shows in `activeTurnId` only once the runtime answers, and a hold taken meanwhile does not
|
|
234
|
+
* stop it. False when the session is not open.
|
|
235
|
+
*/
|
|
236
|
+
starting?(sessionId: string): boolean;
|
|
237
|
+
/**
|
|
238
|
+
* What a live root would run with after a change of model, thinking, team or mode (§12.9), and whether
|
|
239
|
+
* reconfigure would take it now; reads only. `teams` are the teams the host defines for its project.
|
|
240
|
+
*/
|
|
241
|
+
previewConfigure?(sessionId: string, changes: ConfigureChanges): Promise<ConfigurePreview>;
|
|
242
|
+
/**
|
|
243
|
+
* Changes an idle live root's model, thinking, team or mode (§12.9): resolves the new configuration
|
|
244
|
+
* as session.create would (same runtime, agent and conversation; never more permissions or new
|
|
245
|
+
* executable resources), then restarts its native process with it on the same conversation (`fresh`:
|
|
246
|
+
* a root with no turn yet starts a new, empty one). The session takes the new configuration only
|
|
247
|
+
* once the runtime took it (Codex echoes the model it resumed with); otherwise the old one is
|
|
248
|
+
* restarted and a ConversationError thrown (`outcomeUnknown` when the runtime may have acted). A
|
|
249
|
+
* new team also replaces the root's supervisor. Refused as rebind is while the tree is not idle.
|
|
250
|
+
* `onApplied` lets a host persist the configuration the runtime took before session.updated is
|
|
251
|
+
* emitted. It is awaited only for an applied change; its failure leaves the runtime configured
|
|
252
|
+
* and reports outcomeUnknown (never a rollback or a known native refusal).
|
|
253
|
+
* Emits session.updated after that persistence succeeds.
|
|
254
|
+
*/
|
|
255
|
+
reconfigure?(sessionId: string, changes: ConfigureChanges, options?: {
|
|
256
|
+
fresh?: boolean;
|
|
257
|
+
onApplied?: (result: ConfigureResult) => Promise<void>;
|
|
258
|
+
}): Promise<ConfigureResult>;
|
|
259
|
+
};
|
|
260
|
+
import type { ConfigureChanges, EffectiveConfig } from './configure.js';
|
|
261
|
+
export type { ConfigureChanges, EffectiveConfig } from './configure.js';
|
|
262
|
+
import type { ConfigurePreview } from '@anhlp/alp-protocol/session';
|
|
263
|
+
export type { ConfigurePreview } from '@anhlp/alp-protocol/session';
|
|
264
|
+
/** What reconfigure did: the session as it runs now, its effective configuration, and whether its process restarted. */
|
|
265
|
+
export type ConfigureResult = {
|
|
266
|
+
session: SessionSnapshot;
|
|
267
|
+
effective: EffectiveConfig;
|
|
268
|
+
status: 'applied' | 'unchanged';
|
|
269
|
+
restarted: boolean;
|
|
270
|
+
/** What the native runtime answered it runs with (Codex: the resumed thread's model and effort). */
|
|
271
|
+
native?: {
|
|
272
|
+
model?: string;
|
|
273
|
+
reasoningEffort?: string;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
/** The native conversation a session continues: its thread, or for Claude one its first prompt makes from `base`. */
|
|
277
|
+
export type NativeBinding = {
|
|
278
|
+
threadId: string;
|
|
279
|
+
base?: NativeBase;
|
|
280
|
+
};
|
|
281
|
+
export type BranchRequest = {
|
|
282
|
+
runtime: RuntimeKind;
|
|
283
|
+
/** The conversation to branch: the source session's binding. */
|
|
284
|
+
source: NativeBinding;
|
|
285
|
+
/** Where the source session runs (Claude keeps its transcripts by this directory). */
|
|
286
|
+
cwd: string;
|
|
287
|
+
/** Where the branch will run; the source's cwd when absent. */
|
|
288
|
+
targetCwd?: string;
|
|
289
|
+
/** The configuration the branch keeps: Codex otherwise forks with its default model and settings. */
|
|
290
|
+
model: string;
|
|
291
|
+
thinking: string;
|
|
292
|
+
mode: string;
|
|
293
|
+
/**
|
|
294
|
+
* The cut, by ALP turn id (a Codex turn id; a Claude prompt uuid): keep through a completed turn, or
|
|
295
|
+
* everything before a turn. Absent: the whole conversation.
|
|
296
|
+
*/
|
|
297
|
+
cutoff?: {
|
|
298
|
+
throughTurnId: string;
|
|
299
|
+
} | {
|
|
300
|
+
beforeTurnId: string;
|
|
301
|
+
};
|
|
302
|
+
/** The source root's turns in order, as ALP's timeline has them, for the cut's validation. */
|
|
303
|
+
turns: string[];
|
|
304
|
+
};
|
|
305
|
+
export type ConversationErrorReason = 'runtime_unsupported' | 'boundary_unmapped' | 'empty_prefix' | 'native_refused' | 'session_busy' | 'runtime_mismatch' | 'agent_mismatch' | 'permission_escalation' | 'team_not_found' | 'invalid_params' | 'not_root';
|
|
306
|
+
/** A fork, rewind or rebind the runtime refused; `outcomeUnknown` when the native runtime may have acted. */
|
|
307
|
+
export declare class ConversationError extends Error {
|
|
308
|
+
readonly data: {
|
|
309
|
+
reason: ConversationErrorReason;
|
|
310
|
+
};
|
|
311
|
+
readonly outcomeUnknown: boolean;
|
|
312
|
+
constructor(reason: ConversationErrorReason, message: string, { outcomeUnknown }?: {
|
|
313
|
+
outcomeUnknown?: boolean | undefined;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
import type { SessionPreview } from '@anhlp/alp-protocol/session';
|
|
317
|
+
export type { SessionPreview } from '@anhlp/alp-protocol/session';
|
|
318
|
+
/**
|
|
319
|
+
* The team `alp init` names in a new project's settings (core INIT_TEAM). Kept for compatibility;
|
|
320
|
+
* it is not the default of a session: preview and create both resolve that through core
|
|
321
|
+
* `defaultTeam` (settings without workflow.mode run `custom`).
|
|
322
|
+
*/
|
|
323
|
+
export { INIT_TEAM as DEFAULT_TEAM } from '@anhlp/alp-core/workflow';
|
|
324
|
+
import type { RecoveryOutcome, Pause, PauseState } from '@anhlp/alp-protocol/session';
|
|
325
|
+
export type { RecoveryOutcome, Pause, PauseState } from '@anhlp/alp-protocol/session';
|
|
326
|
+
import type { RecallAnswer } from '@anhlp/alp-protocol/session';
|
|
327
|
+
export type { RecallAnswer } from '@anhlp/alp-protocol/session';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// packages/runtime/src/contracts.ts
|
|
2
|
+
import { INIT_TEAM } from "@anhlp/alp-core/workflow";
|
|
3
|
+
var ConversationError = class extends Error {
|
|
4
|
+
data;
|
|
5
|
+
outcomeUnknown;
|
|
6
|
+
constructor(reason, message, { outcomeUnknown = false } = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "ConversationError";
|
|
9
|
+
this.data = { reason };
|
|
10
|
+
this.outcomeUnknown = outcomeUnknown;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
ConversationError,
|
|
15
|
+
INIT_TEAM as DEFAULT_TEAM
|
|
16
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ConversationError, type BranchRequest } from './contracts.js';
|
|
2
|
+
/**
|
|
3
|
+
* Conversation cuts for fork and rewind (docs/protocol/alp-json-rpc-v1.md §12.7). Pure helpers of
|
|
4
|
+
* runtime.ts's branch(): where a Claude transcript is cut, which Codex turn a fork must end at, and
|
|
5
|
+
* what a Codex refusal means.
|
|
6
|
+
*/
|
|
7
|
+
/** One entry of a Claude session's main chain, as the Agent SDK reads it from the transcript. */
|
|
8
|
+
export type ChainEntry = {
|
|
9
|
+
uuid: string;
|
|
10
|
+
type: 'user' | 'assistant';
|
|
11
|
+
toolResult?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/** The turn a Codex fork must end at, after ALP's own validation of the cut against its timeline. */
|
|
14
|
+
export declare function codexForkEnd(request: Pick<BranchRequest, 'cutoff' | 'turns'>): {
|
|
15
|
+
last: string;
|
|
16
|
+
params: {
|
|
17
|
+
beforeTurnId?: undefined;
|
|
18
|
+
lastTurnId?: undefined;
|
|
19
|
+
};
|
|
20
|
+
} | {
|
|
21
|
+
last: string;
|
|
22
|
+
params: {
|
|
23
|
+
beforeTurnId: string;
|
|
24
|
+
lastTurnId?: undefined;
|
|
25
|
+
};
|
|
26
|
+
} | {
|
|
27
|
+
last: string;
|
|
28
|
+
params: {
|
|
29
|
+
lastTurnId: string;
|
|
30
|
+
beforeTurnId?: undefined;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** What a refused thread/fork means; one written but unanswered may have forked. */
|
|
34
|
+
export declare function codexForkRefusal(error: unknown): ConversationError;
|
|
35
|
+
/**
|
|
36
|
+
* The chain uuid a Claude fork resumes at (`resumeSessionAt`): the last entry kept. ALP turn ids are
|
|
37
|
+
* the prompt uuids it gave Claude, so a turn starts at its prompt entry and runs to the next ALP turn's
|
|
38
|
+
* prompt; entries of a turn Claude started itself belong to the turn before it. Always a pinned entry,
|
|
39
|
+
* never "everything", so turns the source adds later never reach a branch made before them.
|
|
40
|
+
*/
|
|
41
|
+
export declare function claudeCut(chain: ChainEntry[], request: Pick<BranchRequest, 'cutoff' | 'turns'>): string;
|
|
42
|
+
/** The chain a binding stands for: a pending fork is its base cut at `at`, until its first prompt makes it. */
|
|
43
|
+
export declare function pendingChain(base: {
|
|
44
|
+
at?: string;
|
|
45
|
+
}, chain: ChainEntry[]): ChainEntry[];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// packages/runtime/src/conversation.ts
|
|
2
|
+
import { ConversationError } from "./contracts.js";
|
|
3
|
+
var refuse = (reason, message, outcomeUnknown = false) => new ConversationError(reason, message, { outcomeUnknown });
|
|
4
|
+
function codexForkEnd(request) {
|
|
5
|
+
const { cutoff, turns } = request;
|
|
6
|
+
if (!cutoff) {
|
|
7
|
+
if (!turns.length) throw refuse("empty_prefix", "The conversation has no turn to fork");
|
|
8
|
+
return { last: turns.at(-1), params: {} };
|
|
9
|
+
}
|
|
10
|
+
const id = "beforeTurnId" in cutoff ? cutoff.beforeTurnId : cutoff.throughTurnId;
|
|
11
|
+
const index = turns.indexOf(id);
|
|
12
|
+
if (index < 0) throw refuse("boundary_unmapped", `Turn ${id} is not a turn of this conversation`);
|
|
13
|
+
if ("beforeTurnId" in cutoff) {
|
|
14
|
+
if (index === 0) throw refuse("empty_prefix", "Nothing comes before the first turn");
|
|
15
|
+
return { last: turns[index - 1], params: { beforeTurnId: id } };
|
|
16
|
+
}
|
|
17
|
+
return { last: id, params: { lastTurnId: id } };
|
|
18
|
+
}
|
|
19
|
+
function codexForkRefusal(error) {
|
|
20
|
+
if (error instanceof ConversationError) return error;
|
|
21
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22
|
+
if (error?.outcomeUnknown) return refuse("native_refused", `Codex did not answer the fork: ${message}`, true);
|
|
23
|
+
if (/^turn not found\b/i.test(message)) return refuse("boundary_unmapped", `Codex does not know that turn: ${message}`);
|
|
24
|
+
if (/identifies an in-progress turn/i.test(message)) return refuse("session_busy", `The turn is still running: ${message}`);
|
|
25
|
+
return refuse("native_refused", `Codex refused the fork: ${message}`);
|
|
26
|
+
}
|
|
27
|
+
function claudeCut(chain, request) {
|
|
28
|
+
const { cutoff, turns } = request;
|
|
29
|
+
const position = new Map(chain.map((entry, index) => [entry.uuid, index]));
|
|
30
|
+
const prompt = (id2) => {
|
|
31
|
+
const index = position.get(id2);
|
|
32
|
+
return index !== void 0 && chain[index].type === "user" && !chain[index].toolResult ? index : void 0;
|
|
33
|
+
};
|
|
34
|
+
if (!cutoff) {
|
|
35
|
+
if (!chain.length) throw refuse("empty_prefix", "The conversation has no turn to fork");
|
|
36
|
+
return chain.at(-1).uuid;
|
|
37
|
+
}
|
|
38
|
+
const id = "beforeTurnId" in cutoff ? cutoff.beforeTurnId : cutoff.throughTurnId;
|
|
39
|
+
const turn = turns.indexOf(id);
|
|
40
|
+
const at = prompt(id);
|
|
41
|
+
if (turn < 0 || at === void 0) throw refuse("boundary_unmapped", `Turn ${id} is not a turn of this conversation's transcript`);
|
|
42
|
+
const found = turns.map(prompt).filter((index) => index !== void 0);
|
|
43
|
+
if (found.some((index, i) => i > 0 && index <= found[i - 1])) throw refuse("boundary_unmapped", "The transcript orders turns differently from ALP");
|
|
44
|
+
if ("beforeTurnId" in cutoff) {
|
|
45
|
+
if (turn === 0 || at === 0) throw refuse("empty_prefix", "Nothing comes before the first turn");
|
|
46
|
+
return chain[at - 1].uuid;
|
|
47
|
+
}
|
|
48
|
+
const next = turns.slice(turn + 1).map(prompt).find((index) => index !== void 0);
|
|
49
|
+
return chain[(next ?? chain.length) - 1].uuid;
|
|
50
|
+
}
|
|
51
|
+
function pendingChain(base, chain) {
|
|
52
|
+
if (!base.at) return chain;
|
|
53
|
+
const index = chain.findIndex((entry) => entry.uuid === base.at);
|
|
54
|
+
if (index < 0) throw refuse("native_refused", `The conversation no longer has the entry ${base.at} it was cut at`);
|
|
55
|
+
return chain.slice(0, index + 1);
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
claudeCut,
|
|
59
|
+
codexForkEnd,
|
|
60
|
+
codexForkRefusal,
|
|
61
|
+
pendingChain
|
|
62
|
+
};
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from '@anhlp/alp-protocol/events';
|
package/dist/events.js
ADDED
|
File without changes
|
package/dist/github.d.ts
ADDED
|
@@ -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;
|