SPB Git

Repository Public

core.ts 6.1 KB 140 lines typescript

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
Files main
Menu

Theme