Add agent runtime DB schemas and expand shared types

New schemas: agent_runtime_state, agent_wakeup_requests,
heartbeat_run_events. New migrations for runtime tables. Expand
heartbeat types with run events, wakeup reasons, and adapter state.
Add live event types. Update agent schema and shared constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-17 12:24:38 -06:00
parent 13ef123026
commit 2583bf4c43
20 changed files with 5296 additions and 5 deletions

View File

@@ -0,0 +1,87 @@
CREATE TABLE "agent_runtime_state" (
"agent_id" uuid PRIMARY KEY NOT NULL,
"company_id" uuid NOT NULL,
"adapter_type" text NOT NULL,
"session_id" text,
"state_json" jsonb DEFAULT '{}'::jsonb NOT NULL,
"last_run_id" uuid,
"last_run_status" text,
"total_input_tokens" bigint DEFAULT 0 NOT NULL,
"total_output_tokens" bigint DEFAULT 0 NOT NULL,
"total_cached_input_tokens" bigint DEFAULT 0 NOT NULL,
"total_cost_cents" bigint DEFAULT 0 NOT NULL,
"last_error" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "agent_wakeup_requests" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"company_id" uuid NOT NULL,
"agent_id" uuid NOT NULL,
"source" text NOT NULL,
"trigger_detail" text,
"reason" text,
"payload" jsonb,
"status" text DEFAULT 'queued' NOT NULL,
"coalesced_count" integer DEFAULT 0 NOT NULL,
"requested_by_actor_type" text,
"requested_by_actor_id" text,
"idempotency_key" text,
"run_id" uuid,
"requested_at" timestamp with time zone DEFAULT now() NOT NULL,
"claimed_at" timestamp with time zone,
"finished_at" timestamp with time zone,
"error" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "heartbeat_run_events" (
"id" bigserial PRIMARY KEY NOT NULL,
"company_id" uuid NOT NULL,
"run_id" uuid NOT NULL,
"agent_id" uuid NOT NULL,
"seq" integer NOT NULL,
"event_type" text NOT NULL,
"stream" text,
"level" text,
"color" text,
"message" text,
"payload" jsonb,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ALTER COLUMN "invocation_source" SET DEFAULT 'on_demand';--> statement-breakpoint
ALTER TABLE "agents" ADD COLUMN "runtime_config" jsonb DEFAULT '{}'::jsonb NOT NULL;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "trigger_detail" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "wakeup_request_id" uuid;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "exit_code" integer;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "signal" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "usage_json" jsonb;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "result_json" jsonb;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "session_id_before" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "session_id_after" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "log_store" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "log_ref" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "log_bytes" bigint;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "log_sha256" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "log_compressed" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "stdout_excerpt" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "stderr_excerpt" text;--> statement-breakpoint
ALTER TABLE "heartbeat_runs" ADD COLUMN "error_code" text;--> statement-breakpoint
ALTER TABLE "agent_runtime_state" ADD CONSTRAINT "agent_runtime_state_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "agent_runtime_state" ADD CONSTRAINT "agent_runtime_state_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "agent_wakeup_requests" ADD CONSTRAINT "agent_wakeup_requests_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "agent_wakeup_requests" ADD CONSTRAINT "agent_wakeup_requests_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "heartbeat_run_events" ADD CONSTRAINT "heartbeat_run_events_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "heartbeat_run_events" ADD CONSTRAINT "heartbeat_run_events_run_id_heartbeat_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."heartbeat_runs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "heartbeat_run_events" ADD CONSTRAINT "heartbeat_run_events_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "agent_runtime_state_company_agent_idx" ON "agent_runtime_state" USING btree ("company_id","agent_id");--> statement-breakpoint
CREATE INDEX "agent_runtime_state_company_updated_idx" ON "agent_runtime_state" USING btree ("company_id","updated_at");--> statement-breakpoint
CREATE INDEX "agent_wakeup_requests_company_agent_status_idx" ON "agent_wakeup_requests" USING btree ("company_id","agent_id","status");--> statement-breakpoint
CREATE INDEX "agent_wakeup_requests_company_requested_idx" ON "agent_wakeup_requests" USING btree ("company_id","requested_at");--> statement-breakpoint
CREATE INDEX "agent_wakeup_requests_agent_requested_idx" ON "agent_wakeup_requests" USING btree ("agent_id","requested_at");--> statement-breakpoint
CREATE INDEX "heartbeat_run_events_run_seq_idx" ON "heartbeat_run_events" USING btree ("run_id","seq");--> statement-breakpoint
CREATE INDEX "heartbeat_run_events_company_run_idx" ON "heartbeat_run_events" USING btree ("company_id","run_id");--> statement-breakpoint
CREATE INDEX "heartbeat_run_events_company_created_idx" ON "heartbeat_run_events" USING btree ("company_id","created_at");

View File

@@ -0,0 +1 @@
ALTER TABLE "heartbeat_runs" ADD CONSTRAINT "heartbeat_runs_wakeup_request_id_agent_wakeup_requests_id_fk" FOREIGN KEY ("wakeup_request_id") REFERENCES "public"."agent_wakeup_requests"("id") ON DELETE no action ON UPDATE no action;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,20 @@
"when": 1771300567463,
"tag": "0000_mature_masked_marvel",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1771349039633,
"tag": "0001_fast_northstar",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1771349403162,
"tag": "0002_big_zaladane",
"breakpoints": true
}
]
}

View File

@@ -0,0 +1,28 @@
import { pgTable, uuid, text, timestamp, jsonb, bigint, index } from "drizzle-orm/pg-core";
import { agents } from "./agents.js";
import { companies } from "./companies.js";
export const agentRuntimeState = pgTable(
"agent_runtime_state",
{
agentId: uuid("agent_id").primaryKey().references(() => agents.id),
companyId: uuid("company_id").notNull().references(() => companies.id),
adapterType: text("adapter_type").notNull(),
sessionId: text("session_id"),
stateJson: jsonb("state_json").$type<Record<string, unknown>>().notNull().default({}),
lastRunId: uuid("last_run_id"),
lastRunStatus: text("last_run_status"),
totalInputTokens: bigint("total_input_tokens", { mode: "number" }).notNull().default(0),
totalOutputTokens: bigint("total_output_tokens", { mode: "number" }).notNull().default(0),
totalCachedInputTokens: bigint("total_cached_input_tokens", { mode: "number" }).notNull().default(0),
totalCostCents: bigint("total_cost_cents", { mode: "number" }).notNull().default(0),
lastError: text("last_error"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
companyAgentIdx: index("agent_runtime_state_company_agent_idx").on(table.companyId, table.agentId),
companyUpdatedIdx: index("agent_runtime_state_company_updated_idx").on(table.companyId, table.updatedAt),
}),
);

View File

@@ -0,0 +1,40 @@
import { pgTable, uuid, text, timestamp, jsonb, integer, index } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
import { agents } from "./agents.js";
export const agentWakeupRequests = pgTable(
"agent_wakeup_requests",
{
id: uuid("id").primaryKey().defaultRandom(),
companyId: uuid("company_id").notNull().references(() => companies.id),
agentId: uuid("agent_id").notNull().references(() => agents.id),
source: text("source").notNull(),
triggerDetail: text("trigger_detail"),
reason: text("reason"),
payload: jsonb("payload").$type<Record<string, unknown>>(),
status: text("status").notNull().default("queued"),
coalescedCount: integer("coalesced_count").notNull().default(0),
requestedByActorType: text("requested_by_actor_type"),
requestedByActorId: text("requested_by_actor_id"),
idempotencyKey: text("idempotency_key"),
runId: uuid("run_id"),
requestedAt: timestamp("requested_at", { withTimezone: true }).notNull().defaultNow(),
claimedAt: timestamp("claimed_at", { withTimezone: true }),
finishedAt: timestamp("finished_at", { withTimezone: true }),
error: text("error"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
companyAgentStatusIdx: index("agent_wakeup_requests_company_agent_status_idx").on(
table.companyId,
table.agentId,
table.status,
),
companyRequestedIdx: index("agent_wakeup_requests_company_requested_idx").on(
table.companyId,
table.requestedAt,
),
agentRequestedIdx: index("agent_wakeup_requests_agent_requested_idx").on(table.agentId, table.requestedAt),
}),
);

View File

@@ -23,6 +23,7 @@ export const agents = pgTable(
capabilities: text("capabilities"),
adapterType: text("adapter_type").notNull().default("process"),
adapterConfig: jsonb("adapter_config").$type<Record<string, unknown>>().notNull().default({}),
runtimeConfig: jsonb("runtime_config").$type<Record<string, unknown>>().notNull().default({}),
contextMode: text("context_mode").notNull().default("thin"),
budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0),
spentMonthlyCents: integer("spent_monthly_cents").notNull().default(0),

View File

@@ -0,0 +1,28 @@
import { pgTable, uuid, text, timestamp, integer, jsonb, index, bigserial } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
import { agents } from "./agents.js";
import { heartbeatRuns } from "./heartbeat_runs.js";
export const heartbeatRunEvents = pgTable(
"heartbeat_run_events",
{
id: bigserial("id", { mode: "number" }).primaryKey(),
companyId: uuid("company_id").notNull().references(() => companies.id),
runId: uuid("run_id").notNull().references(() => heartbeatRuns.id),
agentId: uuid("agent_id").notNull().references(() => agents.id),
seq: integer("seq").notNull(),
eventType: text("event_type").notNull(),
stream: text("stream"),
level: text("level"),
color: text("color"),
message: text("message"),
payload: jsonb("payload").$type<Record<string, unknown>>(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
runSeqIdx: index("heartbeat_run_events_run_seq_idx").on(table.runId, table.seq),
companyRunIdx: index("heartbeat_run_events_company_run_idx").on(table.companyId, table.runId),
companyCreatedIdx: index("heartbeat_run_events_company_created_idx").on(table.companyId, table.createdAt),
}),
);

View File

@@ -1,6 +1,7 @@
import { pgTable, uuid, text, timestamp, jsonb, index } from "drizzle-orm/pg-core";
import { pgTable, uuid, text, timestamp, jsonb, index, integer, bigint, boolean } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
import { agents } from "./agents.js";
import { agentWakeupRequests } from "./agent_wakeup_requests.js";
export const heartbeatRuns = pgTable(
"heartbeat_runs",
@@ -8,11 +9,27 @@ export const heartbeatRuns = pgTable(
id: uuid("id").primaryKey().defaultRandom(),
companyId: uuid("company_id").notNull().references(() => companies.id),
agentId: uuid("agent_id").notNull().references(() => agents.id),
invocationSource: text("invocation_source").notNull().default("manual"),
invocationSource: text("invocation_source").notNull().default("on_demand"),
triggerDetail: text("trigger_detail"),
status: text("status").notNull().default("queued"),
startedAt: timestamp("started_at", { withTimezone: true }),
finishedAt: timestamp("finished_at", { withTimezone: true }),
error: text("error"),
wakeupRequestId: uuid("wakeup_request_id").references(() => agentWakeupRequests.id),
exitCode: integer("exit_code"),
signal: text("signal"),
usageJson: jsonb("usage_json").$type<Record<string, unknown>>(),
resultJson: jsonb("result_json").$type<Record<string, unknown>>(),
sessionIdBefore: text("session_id_before"),
sessionIdAfter: text("session_id_after"),
logStore: text("log_store"),
logRef: text("log_ref"),
logBytes: bigint("log_bytes", { mode: "number" }),
logSha256: text("log_sha256"),
logCompressed: boolean("log_compressed").notNull().default(false),
stdoutExcerpt: text("stdout_excerpt"),
stderrExcerpt: text("stderr_excerpt"),
errorCode: text("error_code"),
externalRunId: text("external_run_id"),
contextSnapshot: jsonb("context_snapshot").$type<Record<string, unknown>>(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),

View File

@@ -1,11 +1,14 @@
export { companies } from "./companies.js";
export { agents } from "./agents.js";
export { agentApiKeys } from "./agent_api_keys.js";
export { agentRuntimeState } from "./agent_runtime_state.js";
export { agentWakeupRequests } from "./agent_wakeup_requests.js";
export { projects } from "./projects.js";
export { goals } from "./goals.js";
export { issues } from "./issues.js";
export { issueComments } from "./issue_comments.js";
export { heartbeatRuns } from "./heartbeat_runs.js";
export { heartbeatRunEvents } from "./heartbeat_run_events.js";
export { costEvents } from "./cost_events.js";
export { approvals } from "./approvals.js";
export { activityLog } from "./activity_log.js";