Repository Public
1/**2 * Core vocabulary of the Agent OS. Frozen contract shared by kernel, engine, providers, tools,3 * server, worker and web. Additive changes only; report them.4 */5import { z } from 'zod';67export const ENGINE_VERSION = '0.1.0';89export const RunStatus = z.enum([10 'queued', 'running', 'waiting',11 // terminal:12 'done', 'partial', 'blocked', 'failed', 'cancelled', 'budget_exhausted',13]);14export type RunStatus = z.infer<typeof RunStatus>;15export const TERMINAL_STATUSES: readonly RunStatus[] = ['done', 'partial', 'blocked', 'failed', 'cancelled', 'budget_exhausted'];16export const isTerminal = (s: RunStatus): boolean => TERMINAL_STATUSES.includes(s);1718/** How a step interacts with the world; decides what happens to a step found 'started' without a result. */19export const EffectClass = z.enum(['pure', 'read', 'idempotent_write', 'non_idempotent', 'model_call']);20export type EffectClass = z.infer<typeof EffectClass>;2122/** What the policy engine reasons about. */23export const RiskClass = z.enum(['read', 'workspace_write', 'network', 'external']);24export type RiskClass = z.infer<typeof RiskClass>;2526export const Trust = z.enum(['owner', 'system', 'agent', 'tool', 'web']);27export type Trust = z.infer<typeof Trust>;2829export const Provider = z.enum(['anthropic', 'openai']);30export type Provider = z.infer<typeof Provider>;31export const Effort = z.enum(['low', 'medium', 'high', 'xhigh', 'max']);32export type Effort = z.infer<typeof Effort>;3334export const Budget = z.object({35 usd: z.number().positive(),36 tokens: z.number().int().positive().optional(),37 turns: z.number().int().positive().default(200),38 wallSec: z.number().int().positive().default(6 * 3600),39});40export type Budget = z.infer<typeof Budget>;4142export const Usage = z.object({43 usd: z.number().default(0),44 inputTokens: z.number().default(0),45 outputTokens: z.number().default(0),46 cacheReadTokens: z.number().default(0),47 cacheWriteTokens: z.number().default(0),48 turns: z.number().default(0),49 toolCalls: z.number().default(0),50});51export type Usage = z.infer<typeof Usage>;5253/** Typed delegation contract (ARCHITECTURE §5.4). A spawn without done criteria is rejected. */54export const DelegationContract = z.object({55 objective: z.string().min(1).max(8000),56 contextRefs: z.array(z.string().max(500)).max(50).default([]), // file paths / artifact ids / memory ids57 boundaries: z.string().max(4000).default(''),58 outputSchema: z.record(z.string(), z.unknown()).nullable().default(null), // JSON Schema of ResultEnvelope.payload59 tools: z.array(z.string()).default([]), // allowlist; empty = role default60 doneCriteria: z.array(z.string().min(1).max(1000)).min(1).max(50),61 contextMode: z.enum(['fresh', 'fork', 'artifact_only']).default('fresh'),62});63export type DelegationContract = z.infer<typeof DelegationContract>;6465/** What every run returns. A run killed by an error or budget is never 'done'. */66export const ResultEnvelope = z.object({67 status: z.enum(['done', 'partial', 'blocked', 'failed', 'cancelled', 'budget_exhausted']),68 summary: z.string().max(8000),69 payload: z.unknown().nullable().default(null),70 artifacts: z.array(z.object({ name: z.string(), sha256: z.string() })).default([]),71 evidence: z.array(z.object({ criterion: z.string().optional(), kind: z.enum(['command', 'test', 'file', 'source', 'note']), detail: z.string().max(2000) })).default([]),72 openQuestions: z.array(z.string().max(1000)).default([]),73});74export type ResultEnvelope = z.infer<typeof ResultEnvelope>;7576export const DoneCriterion = z.object({ id: z.string(), text: z.string(), passes: z.boolean().default(false), evidence: z.string().optional() });77export type DoneCriterion = z.infer<typeof DoneCriterion>;7879// ---------------------------------------------------------------------------------------------80// Normalised model I/O. Providers keep their NATIVE transcript opaque (`native`) so that thinking81// blocks / encrypted reasoning items are replayed byte-for-byte; the engine only reads the82// normalised view.8384export const ContentBlock = z.discriminatedUnion('type', [85 z.object({ type: z.literal('text'), text: z.string() }),86 z.object({ type: z.literal('reasoning'), summary: z.string() }), // operational reasoning summary, may be empty87 z.object({ type: z.literal('tool_call'), id: z.string(), name: z.string(), input: z.unknown() }),88]);89export type ContentBlock = z.infer<typeof ContentBlock>;9091export const StopReason = z.enum(['end_turn', 'tool_use', 'max_tokens', 'refusal', 'pause', 'error']);92export type StopReason = z.infer<typeof StopReason>;9394export const ModelResponse = z.object({95 blocks: z.array(ContentBlock),96 stop: StopReason,97 usage: Usage,98 model: z.string(), // model that actually answered (fallbacks)99 native: z.unknown(), // provider-native assistant message(s) to append verbatim to the native transcript100});101export type ModelResponse = z.infer<typeof ModelResponse>;102103export const ToolResult = z.object({104 callId: z.string(),105 name: z.string(),106 ok: z.boolean(),107 /** What the model sees (already truncated/offloaded). */108 content: z.string(),109 /** Offloaded full output, if any. */110 artifact: z.object({ name: z.string(), sha256: z.string(), size: z.number() }).nullable().default(null),111 trust: Trust.default('tool'),112});113export type ToolResult = z.infer<typeof ToolResult>;114115/** Events appended to `events` and pushed to the console. `id` is the cursor. */116export const EventKind = z.enum([117 'mission.created', 'mission.status',118 'run.created', 'run.status', 'run.usage',119 'step.started', 'step.completed', 'step.failed', 'step.needs_reconcile',120 'model.delta', // transient streaming text/reasoning; NOT persisted (pushed only)121 'wait.created', 'wait.resolved',122 'owner.message', // steering message from the owner123 'artifact.created',124 'policy.decision',125 'system.notice',126]);127export type EventKind = z.infer<typeof EventKind>;128129export const AosEvent = z.object({130 id: z.number(), // bigserial cursor; -1 for transient deltas131 missionId: z.string(),132 runId: z.string().nullable(),133 seq: z.number().nullable(),134 ts: z.string(),135 kind: EventKind,136 trust: Trust,137 payload: z.record(z.string(), z.unknown()),138});139export type AosEvent = z.infer<typeof AosEvent>;140