@phren/cli
Version:
Knowledge layer for AI agents - CLI, MCP server, and data layer
435 lines (434 loc) • 16.7 kB
TypeScript
import { z } from "zod";
import { type DispatchHost } from "./dispatch-hosts.js";
import { type Json, type Provider, type Target } from "./protocol.js";
import { type BriefArrival } from "./launch-brief.js";
export declare const projectName: z.ZodString;
/** The harnesses a dispatch, a code note's hand-off or a schedule can start. */
export declare const DISPATCH_HARNESSES: readonly ["codex", "claude", "opencode", "copilot"];
export declare const dispatchSchema: z.ZodObject<{
computer: z.ZodUnion<readonly [z.ZodLiteral<"anywhere">, z.ZodString]>;
project: z.ZodString;
harness: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
}>;
model: z.ZodOptional<z.ZodString>;
effort: z.ZodOptional<z.ZodEnum<{
high: "high";
low: "low";
max: "max";
medium: "medium";
minimal: "minimal";
xhigh: "xhigh";
}>>;
account: z.ZodOptional<z.ZodString>;
permissionMode: z.ZodOptional<z.ZodEnum<{
auto: "auto";
"auto-edits": "auto-edits";
"full-access": "full-access";
supervised: "supervised";
}>>;
releaseActions: z.ZodOptional<z.ZodArray<z.ZodEnum<{
"app-store": "app-store";
deploy: "deploy";
"github-admin": "github-admin";
merge: "merge";
publish: "publish";
}>>>;
prompt: z.ZodString;
label: z.ZodString;
closeOnFinish: z.ZodOptional<z.ZodBoolean>;
integrator: z.ZodOptional<z.ZodObject<{
computer: z.ZodOptional<z.ZodString>;
target: z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
}, z.core.$strip>;
}, z.core.$strict>>;
parent: z.ZodOptional<z.ZodObject<{
provider: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
computer: z.ZodString;
}, z.core.$strict>>;
parentTarget: z.ZodOptional<z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
}, z.core.$strip>>;
}, z.core.$strict>;
export type DispatchInput = z.infer<typeof dispatchSchema>;
/** The local pane that asked for the dispatch, where return notices go. */
export declare const originPaneSchema: z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
}, z.core.$strict>;
export type OriginPane = z.infer<typeof originPaneSchema>;
export declare const workerStates: readonly ["working", "done", "needs-you", "failed", "blocked", "stalled", "gone"];
export type WorkerState = typeof workerStates[number];
declare const receiptSchema: z.ZodObject<{
computer: z.ZodUnion<readonly [z.ZodLiteral<"anywhere">, z.ZodString]>;
project: z.ZodString;
harness: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
}>;
model: z.ZodOptional<z.ZodString>;
effort: z.ZodOptional<z.ZodEnum<{
high: "high";
low: "low";
max: "max";
medium: "medium";
minimal: "minimal";
xhigh: "xhigh";
}>>;
account: z.ZodOptional<z.ZodString>;
permissionMode: z.ZodOptional<z.ZodEnum<{
auto: "auto";
"auto-edits": "auto-edits";
"full-access": "full-access";
supervised: "supervised";
}>>;
releaseActions: z.ZodOptional<z.ZodArray<z.ZodEnum<{
"app-store": "app-store";
deploy: "deploy";
"github-admin": "github-admin";
merge: "merge";
publish: "publish";
}>>>;
label: z.ZodString;
closeOnFinish: z.ZodOptional<z.ZodBoolean>;
integrator: z.ZodOptional<z.ZodObject<{
computer: z.ZodOptional<z.ZodString>;
target: z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
}, z.core.$strip>;
}, z.core.$strict>>;
parent: z.ZodOptional<z.ZodObject<{
provider: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
computer: z.ZodString;
}, z.core.$strict>>;
parentTarget: z.ZodOptional<z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
}, z.core.$strip>>;
id: z.ZodString;
createdAt: z.ZodString;
updatedAt: z.ZodString;
computerId: z.ZodOptional<z.ZodString>;
state: z.ZodEnum<{
accepted: "accepted";
failed: "failed";
launching: "launching";
sending: "sending";
uncertain: "uncertain";
}>;
target: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
}, z.core.$strip>, z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
starting: z.ZodLiteral<true>;
startingToken: z.ZodString;
}, z.core.$strip>]>>;
error: z.ZodOptional<z.ZodString>;
brief: z.ZodOptional<z.ZodEnum<{
launch: "launch";
typed: "typed";
}>>;
granted: z.ZodOptional<z.ZodString>;
authority: z.ZodOptional<z.ZodString>;
authorityConfirmed: z.ZodOptional<z.ZodString>;
skipped: z.ZodOptional<z.ZodArray<z.ZodObject<{
computer: z.ZodString;
reason: z.ZodString;
}, z.core.$strict>>>;
origin: z.ZodOptional<z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
agent: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
terminal: z.ZodString;
}, z.core.$strict>>;
closedAt: z.ZodOptional<z.ZodString>;
closePending: z.ZodOptional<z.ZodObject<{
at: z.ZodString;
turn: z.ZodOptional<z.ZodString>;
}, z.core.$strict>>;
worker: z.ZodOptional<z.ZodObject<{
state: z.ZodEnum<{
blocked: "blocked";
done: "done";
failed: "failed";
gone: "gone";
"needs-you": "needs-you";
stalled: "stalled";
working: "working";
}>;
since: z.ZodString;
checkedAt: z.ZodString;
sawWorking: z.ZodBoolean;
background: z.ZodOptional<z.ZodNumber>;
waitingSince: z.ZodOptional<z.ZodString>;
}, z.core.$strict>>;
returned: z.ZodOptional<z.ZodObject<{
state: z.ZodEnum<{
blocked: "blocked";
done: "done";
failed: "failed";
gone: "gone";
"needs-you": "needs-you";
stalled: "stalled";
}>;
at: z.ZodString;
reply: z.ZodOptional<z.ZodString>;
error: z.ZodOptional<z.ZodString>;
truncated: z.ZodOptional<z.ZodBoolean>;
question: z.ZodOptional<z.ZodString>;
prs: z.ZodOptional<z.ZodArray<z.ZodObject<{
url: z.ZodURL;
repo: z.ZodString;
branch: z.ZodString;
tests: z.ZodString;
notes: z.ZodOptional<z.ZodString>;
}, z.core.$strict>>>;
integratorDelivery: z.ZodOptional<z.ZodObject<{
deliveryId: z.ZodString;
state: z.ZodEnum<{
delivered: "delivered";
failed: "failed";
pending: "pending";
queued: "queued";
uncertain: "uncertain";
}>;
at: z.ZodString;
integrator: z.ZodOptional<z.ZodObject<{
computer: z.ZodOptional<z.ZodString>;
target: z.ZodObject<{
server: z.ZodString;
workspace: z.ZodString;
tab: z.ZodString;
pane: z.ZodString;
source: z.ZodEnum<{
claude: "claude";
codex: "codex";
copilot: "copilot";
opencode: "opencode";
phren: "phren";
}>;
session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
}, z.core.$strip>;
}, z.core.$strict>>;
}, z.core.$strict>>;
stalledSince: z.ZodOptional<z.ZodString>;
stallFor: z.ZodOptional<z.ZodNumber>;
turn: z.ZodOptional<z.ZodString>;
read: z.ZodBoolean;
notifiedAt: z.ZodOptional<z.ZodString>;
background: z.ZodOptional<z.ZodNumber>;
waited: z.ZodOptional<z.ZodNumber>;
}, z.core.$strict>>;
approval: z.ZodOptional<z.ZodObject<{
actionId: z.ZodString;
tool: z.ZodString;
title: z.ZodOptional<z.ZodString>;
request: z.ZodOptional<z.ZodString>;
requestKind: z.ZodOptional<z.ZodEnum<{
command: "command";
edit: "edit";
other: "other";
question: "question";
tool: "tool";
}>>;
terminal: z.ZodOptional<z.ZodBoolean>;
conductor: z.ZodOptional<z.ZodObject<{
action: z.ZodEnum<{
dispatch: "dispatch";
hand_off: "hand_off";
}>;
project: z.ZodOptional<z.ZodString>;
computer: z.ZodOptional<z.ZodString>;
}, z.core.$strict>>;
expiresAt: z.ZodOptional<z.ZodString>;
at: z.ZodString;
pushed: z.ZodOptional<z.ZodBoolean>;
}, z.core.$strict>>;
}, z.core.$strict>;
export type Receipt = z.infer<typeof receiptSchema>;
/**
* The project's folder on this computer. The store syncs between computers,
* so its shared `sourcePath` is often another machine's folder: this
* machine's `sourcePaths` entry wins, as everywhere else in phren. With
* neither here, a git checkout named after the project in a usual project
* root is taken (the owner's layout is `~/Projects/<name>` on every computer).
*/
export declare function dispatchProjectDirectory(project: unknown, home?: string): Promise<string>;
/**
* Change one settled receipt: read it, let `change` edit it, and write it back
* when `change` returns true. Updates run one at a time in this process, and a
* receipt still being placed (launching or sending) is never touched.
*/
export declare function updateReceipt(receiptID: string, change: (receipt: Receipt) => boolean): Promise<Receipt | undefined>;
/** One bounded line for a log or a receipt: a Bridge message, an errno code, or the error's first line. */
export declare function failureReason(error: unknown): string;
export declare function dispatchStatus(): Promise<Receipt[]>;
/** `caller` (a conductor's name and host key) asks a peer whether it links this computer back:
* `outside` is a peer in another set, which a conductor does not dispatch to. */
type Room = {
source: string;
account?: string;
leftPercent?: number;
exhausted?: boolean;
until?: string;
};
/** The account this dispatch would run under there, as its capacity probe reports it: Codex's one, or the named Claude home. */
export declare function roomFor(data: {
harness: string;
account?: string;
}, usage: readonly Room[] | undefined): Room | undefined;
/** Why `anywhere` must not pick a computer for quota: only an account with none left (at 100% or refusing requests).
* Low quota is not a reason; the owner often wants it used before it resets. */
export declare function outOfQuota(data: {
harness: string;
account?: string;
}, usage: readonly Room[] | undefined, now?: number): string | undefined;
/** What the worker's own hooks reported for a brief that went with its
* launch, or undefined when the receiving Hook has no such brief. */
export declare function arrivalOf(peer: Pick<DispatchHost, "request">, id: string): Promise<BriefArrival | undefined>;
export interface DispatchIdentity {
computerID: string;
validateParentTarget: (target: Target) => Promise<unknown>;
/** The agent and terminal running in a local pane, or undefined when the pane has no agent. */
originAgent?: (pane: OriginPane) => Promise<{
agent: Provider;
terminal: string;
} | undefined>;
}
export declare class DispatchService {
private readonly identity?;
private readonly local;
private readonly settleIntervalMs;
/** One placement at a time: choosing a computer, saving the receipt and the
* launch request. Confirming the brief afterwards (up to a minute and a half
* for a slow starting pane) runs outside it, so other dispatches are not
* refused meanwhile; `placing` counts those launches toward their
* computer's load for the next `anywhere` choice. */
private active;
private readonly placing;
/** `local` is this computer as a dispatch destination (its own Hook's
* socket); tests replace it so they never reach a real Hook. */
constructor(identity?: DispatchIdentity | undefined, local?: () => DispatchHost, settleIntervalMs?: number);
/** `originValue` is the local pane the request came from, as its agent's
* Herdr variables name it; a pane without a running agent is left out. */
dispatch(input: unknown, originValue?: unknown): Promise<Json>;
/** Launches placed on `computer` whose brief is still being confirmed. */
private inFlight;
/**
* A brief typed into a pane that has no conversation yet: typed once the
* pane reads ready, then confirmed by the conversation it starts. A brief
* the pane swallowed (still idle on its starting binding) is typed again,
* under its own delivery id, but only after `readyForRetry` finds the pane
* still idle there: a first brief that landed late is never typed twice.
* After BRIEF_TRIES the receipt carries a failed return yet stays watched
* (uncertain), so a brief that lands later still brings the worker's return.
* A Claude prompt left typed but unsubmitted is not typed again.
*/
private typeIntoStarting;
/**
* A brief that went with the launch: wait for the worker's hook to confirm
* it by dispatch id. A startup screen (folder trust, sign-in) holds the
* prompt until the owner answers it, and the harness then submits it by
* itself, so that is not a failure. Neither confirmed nor held reads
* uncertain, and the returns loop keeps asking.
*/
private confirmLaunched;
/** The query a conductor's dispatch sends with each capacity probe; undefined for any other caller. */
private conductorCaller;
private origin;
}
export {};