UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

531 lines (530 loc) • 21.2 kB
import { z } from "zod"; import { type ConductorRecord } from "./conductor-role.js"; import { type DispatchHost } from "./dispatch-hosts.js"; import { type Json, type Provider, type Target } from "./protocol.js"; import { type HarnessInventory } from "./harnesses.js"; import type { AccountRoom, WindowRoom } from "./account-choice.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 PEER_COPILOT_REASON = "Copilot cannot continue on another computer: its harness does not confirm launch-brief arrival. Continue locally or choose another harness."; 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>; /** Where a session moves to (session-move.ts): a harness with its account, model and effort, as a dispatch names them. */ export declare const moveToSchema: z.ZodObject<{ computer: z.ZodOptional<z.ZodString>; harness: z.ZodEnum<{ claude: "claude"; codex: "codex"; copilot: "copilot"; opencode: "opencode"; }>; account: z.ZodOptional<z.ZodString>; model: z.ZodOptional<z.ZodString>; effort: z.ZodOptional<z.ZodEnum<{ high: "high"; low: "low"; max: "max"; medium: "medium"; minimal: "minimal"; xhigh: "xhigh"; }>>; }, z.core.$strict>; /** 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"; }>>; accountChoice: z.ZodOptional<z.ZodString>; continued: z.ZodOptional<z.ZodObject<{ id: z.ZodOptional<z.ZodString>; computer: z.ZodOptional<z.ZodString>; account: z.ZodOptional<z.ZodString>; at: z.ZodString; error: z.ZodOptional<z.ZodString>; }, z.core.$strict>>; continues: z.ZodOptional<z.ZodString>; moves: z.ZodOptional<z.ZodArray<z.ZodObject<{ id: z.ZodString; at: z.ZodString; handoff: z.ZodOptional<z.ZodString>; computerFingerprint: z.ZodOptional<z.ZodString>; computerId: z.ZodOptional<z.ZodString>; from: z.ZodObject<{ harness: z.ZodEnum<{ claude: "claude"; codex: "codex"; copilot: "copilot"; opencode: "opencode"; phren: "phren"; }>; account: z.ZodOptional<z.ZodString>; }, z.core.$strict>; to: z.ZodObject<{ computer: z.ZodOptional<z.ZodString>; harness: z.ZodEnum<{ claude: "claude"; codex: "codex"; copilot: "copilot"; opencode: "opencode"; }>; account: z.ZodOptional<z.ZodString>; model: z.ZodOptional<z.ZodString>; effort: z.ZodOptional<z.ZodEnum<{ high: "high"; low: "low"; max: "max"; medium: "medium"; minimal: "minimal"; xhigh: "xhigh"; }>>; }, z.core.$strict>; }, z.core.$strict>>>; 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>; checkout: z.ZodOptional<z.ZodObject<{ path: z.ZodString; branch: z.ZodOptional<z.ZodString>; }, z.core.$strict>>; 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>; /** Hand over all open worker routes using the receipt writer's own lock and * update path. Commit the role last; any failure restores the old origins. * Placing receipts are refused because placement can still overwrite them. */ export declare function repointDispatchOrigins(from: Pick<ConductorRecord, "server" | "pane" | "terminal">, to: NonNullable<Receipt["origin"]>, commit: () => Promise<void>): Promise<void>; /** 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; fiveHour?: WindowRoom; week?: WindowRoom; }; /** 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. A Claude dispatch that names no * account runs under whichever has room there, so with several accounts that computer is out only when all are. */ export declare function outOfQuota(data: { harness: string; account?: string; }, usage: readonly Room[] | undefined, now?: number): string | undefined; /** A computer's signed-in Claude accounts with their room, from its capacity probe. */ export declare function capacityClaudeRooms(harnesses: HarnessInventory["harnesses"] | undefined, usage: readonly Room[] | undefined): AccountRoom[]; /** 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. * `continues` names the dispatch whose worker hit its usage limit (account-failover.ts). */ dispatch(input: unknown, originValue?: unknown, continues?: string): Promise<Json>; /** Owner continuation: use the same pinned transport and launch/arrival * protocol as dispatch, without turning the new chat into a worker. */ continueChat(peer: DispatchHost, project: string, to: z.infer<typeof moveToSchema>, brief: { id: string; text: string; }, label: string, placed?: (target: unknown) => Promise<void>, computerId?: string, worker?: boolean, workerDispatch?: string): Promise<Target>; /** 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; /** Every connected computer's signed-in Claude accounts and their room, this computer first: * where a worker that hit its usage limit can continue (account-failover.ts). */ claudeRooms(): Promise<Array<{ computer: string; local: boolean; rooms: AccountRoom[]; }>>; /** The query a conductor's dispatch sends with each capacity probe; undefined for any other caller. */ private conductorCaller; private origin; } export {};