@codecaine-ai/agent-kernel-db 0.0.0-stage → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Codecaine AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,83 @@
1
- # Temporary Holding Version
1
+ # Agent Kernel
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ - An opinionated system for building **problem-specific agent harnesses** on top of the Pi Agent SDK
4
+ - Designed for high-volume multi-agent systems that do one class of work extremely well
5
+
6
+ ## Why this exists
7
+
8
+ - Mass multi-agent systems are the next step for agentic software:
9
+ - Dozens of workers
10
+ - Long pipelines
11
+ - Deep subagent trees
12
+ - At that scale, token spend becomes a commodity resource:
13
+ - Meter it like water.
14
+ - Map where it flows.
15
+ - Find the leaks.
16
+ - Cut or reroute work that does not earn its cost.
17
+ - General tools like Codex and Claude Code are useful execution environments, but they do not give a purpose-built harness deep observability into spawned agents, token budgets, trace lineage, and model effectiveness.
18
+
19
+ - Agent Kernel is opinionated about that layer:
20
+
21
+ - **Extreme observability.**
22
+ - Every prompt, context load, tool call, subagent spawn, and lifecycle event is captured as a durable, explicitly-linked trace.
23
+ - You can answer "where did the tokens go and what did they produce?" without guesswork or timestamp reconstruction.
24
+ - **Extreme control.**
25
+ - Per-kernel and per-agent concurrency, explicit boundaries, scoped run context, abort/stop semantics, and a spawn pipeline you can audit end to end.
26
+ - You decide how many workers run and how they're contained.
27
+ - **Any model, per agent.**
28
+ - Agents are declarative definitions, not hardcoded to one model.
29
+ - Route each agent in a harness to the model that earns its tokens
30
+
31
+ - The kernel stays deliberately neutral about *what problem your harness solves*.
32
+ - Workflow semantics, domain tools, and product rules belong to your harness.
33
+ - The kernel owns the runtime and observability foundation that makes a harness safe to scale — because scaling blindly is how agent projects get quietly expensive.
34
+
35
+ ## Packages
36
+
37
+ - This is a platform monorepo while the contracts settle:
38
+
39
+ - `@codecaine-ai/agent-kernel-protocol` — trace/event envelopes, event factories, deterministic event ids, and turn usage
40
+ - `@codecaine-ai/agent-kernel-db` — per-kernel SQLite observability store, schema, kernel manifest, and query helpers
41
+ - `@codecaine-ai/agent-kernel` — `createKernel` runtime, container identity, registry, context assembly, spawn pipeline, in-process emitter, subagents, trace doctor, read API, and transcript recovery
42
+ - `@codecaine-ai/prompt-kit` — prompt document model, canonicalization/hashing, renderers, and editor primitives (sibling repo and Core workspace member)
43
+ - `@codecaine-ai/agent-kernel-viewer-core` — viewer DTOs, read/catalog API paths, trace transforms, and prompt diffing
44
+ - `@codecaine-ai/agent-kernel-viewer-ui` — trace tree and card system, the detail panel and its data-only renderer contract, prompt lab components
45
+ - `@codecaine-ai/agent-kernel-viewer-shell` — mountable trace workspace, the tree/detail viewer, and the shared style system
46
+
47
+ ## Building a harness on it
48
+
49
+ - A harness is the problem-specific application layer that makes the kernel good at one task.
50
+ - A host application (the harness) consumes the kernel through a thin adapter.
51
+ - The harness owns workflow sessions, phase semantics, domain tools, app-specific loaders, app DB tables, and custom viewer panels.
52
+ - The kernel owns spawning, context assembly, observability storage, trace reading, and viewer primitives.
53
+ - The identity model behind all of this (containers, sessions, runs, turns, linkage invariants) is documented in [docs/10-system-design/15-identity-model/](docs/10-system-design/15-identity-model/); the overhaul plan that produced it lives in [docs/.drafts/agent-kernel-overhaul.plan.md](docs/.drafts/agent-kernel-overhaul.plan.md).
54
+
55
+ ## Setup
56
+
57
+ - Install dependencies with `bun install`.
58
+
59
+ ## Validation
60
+
61
+ - Run `bun run test:boundaries`.
62
+ - Run `bun run typecheck`.
63
+ - Run `bun run test`.
64
+ - The boundary check is the important portability gate: platform packages must not import the host application or reference host-app paths.
65
+ - Per-package typechecks exist as `bun run typecheck:<name>` for `protocol`, `db`, `kernel`, `tui`, `core-harness`, `viewer-core`, `viewer-ui`, `viewer-shell`, and `examples`.
66
+ - When changing package boundaries, also sweep manually: `rg -n "@spectre|\.spectre|Spectre|SPECTRE|apps/backend|apps/frontend|checkpoint-slice|SessionStateManager" packages docs` — app-specific names may appear in docs only when explaining adapter boundaries, never in package source.
67
+ - To check a kernel trace database against the linkage/usage invariants, run the trace doctor: `bun run packages/kernel/src/doctor-cli.ts <db-path>`. Without an argument it defaults to `.agent-kernel/trace.db` under the current directory; non-zero exit means violations.
68
+
69
+ ## Running this repo's kernel
70
+
71
+ - This repo is itself a kernel; its service leg is the core harness: `bun run core-harness` serves health, the kernel read API, catalog routes, and prompt-edit sessions on `http://127.0.0.1:4860`.
72
+ - The terminal harness (`/kernel` inside pi) is [`packages/tui`](packages/tui/README.md) — setup, usage, and troubleshooting live in its README.
73
+ - To fold marked TUI sessions into the owning kernel's `trace.db`, run `bunx agent-kernel-tui-ingest` (batch, idempotent).
74
+ - To register a kernel with the Observatory, run `bun run registry:check` / `bun run registry:sync` in the observatory repo (Core's doctor warns on drift).
75
+
76
+ ## Examples
77
+
78
+ - The Prompt Kit kernel host has moved to the sibling Prompt Kit repo at `../prompt-kit/packages/prompt-kit-agent`; from `../prompt-kit`, start it with `bun run dev:agent`.
79
+ - `examples/simple-research-kernel` is a runnable standalone Simple Research Kernel — run instructions in [its README](examples/simple-research-kernel/README.md).
80
+ - The example defines agents in a catalog (folder-form bundles: `agent.json` + `prompt/` + `context/` + `tools/`), loads context sidecars, spawns scout subagents, waits for their reports, reviews gaps, queues a report writer, writes working memory, persists kernel observability rows, and renders traces through the viewer shell.
81
+ - Start it with `bun run dev:simple-research` — SQLite only, no service processes.
82
+ - It runs against a single local SQLite file (`examples/simple-research-kernel/.agent-kernel/trace.db`, WAL mode) created on boot, alongside a local kernel manifest (`.agent-kernel/kernel.json`).
83
+ - The launcher starts the API on `http://127.0.0.1:8788` and the viewer on `http://127.0.0.1:5174`.
package/package.json CHANGED
@@ -1,6 +1,40 @@
1
1
  {
2
2
  "name": "@codecaine-ai/agent-kernel-db",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.3",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Codecaine-AI/agent-kernel.git",
8
+ "directory": "packages/db"
9
+ },
10
+ "type": "module",
11
+ "main": "src/index.ts",
12
+ "types": "src/index.ts",
13
+ "exports": {
14
+ ".": "./src/index.ts",
15
+ "./actions": "./src/actions/index.ts",
16
+ "./schema": "./src/schema/index.ts",
17
+ "./client": "./src/client.ts",
18
+ "./manifest": "./src/manifest.ts",
19
+ "./types": "./src/types.ts"
20
+ },
21
+ "dependencies": {
22
+ "@codecaine-ai/agent-kernel-protocol": "^0.0.3",
23
+ "drizzle-orm": "^0.45.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/bun": "latest"
27
+ },
28
+ "files": [
29
+ "src",
30
+ "!src/**/*.test.ts",
31
+ "!src/**/*.test.tsx",
32
+ "!src/**/__tests__",
33
+ "!src/**/__fixtures__",
34
+ "!src/**/__snapshots__",
35
+ "README.md"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }
@@ -0,0 +1,146 @@
1
+ import { asc, eq } from "drizzle-orm";
2
+ import type { KernelDatabase } from "../client";
3
+ import { agentRuns, type RunStatus } from "../schema/agent-runs";
4
+ import type { AgentRun, NewAgentRun } from "../types";
5
+
6
+ export async function createAgentRun(
7
+ db: KernelDatabase,
8
+ data: NewAgentRun,
9
+ ): Promise<AgentRun> {
10
+ const [row] = await db.insert(agentRuns).values(data).returning();
11
+ return row;
12
+ }
13
+
14
+ export async function upsertAgentRun(
15
+ db: KernelDatabase,
16
+ data: NewAgentRun,
17
+ ): Promise<AgentRun> {
18
+ const [row] = await db
19
+ .insert(agentRuns)
20
+ .values(data)
21
+ .onConflictDoUpdate({
22
+ target: agentRuns.id,
23
+ set: {
24
+ piSessionId: data.piSessionId,
25
+ containerId: data.containerId,
26
+ ...(data.parentRunId !== undefined && { parentRunId: data.parentRunId }),
27
+ ...(data.parentToolUseId !== undefined && {
28
+ parentToolUseId: data.parentToolUseId,
29
+ }),
30
+ ...(data.agentName !== undefined && { agentName: data.agentName }),
31
+ ...(data.trigger !== undefined && { trigger: data.trigger }),
32
+ ...(data.inboundEventId !== undefined && {
33
+ inboundEventId: data.inboundEventId,
34
+ }),
35
+ ...(data.outboundEventId !== undefined && {
36
+ outboundEventId: data.outboundEventId,
37
+ }),
38
+ ...(data.displayLabel !== undefined && { displayLabel: data.displayLabel }),
39
+ ...(data.phase !== undefined && { phase: data.phase }),
40
+ ...(data.status !== undefined && { status: data.status }),
41
+ ...(data.startedAt !== undefined && { startedAt: data.startedAt }),
42
+ ...(data.endedAt !== undefined && { endedAt: data.endedAt }),
43
+ },
44
+ })
45
+ .returning();
46
+ return row;
47
+ }
48
+
49
+ /**
50
+ * Close or update a run: status transition plus the outbound event that
51
+ * closed it and the usage rollup (Phase 2 populates usage).
52
+ */
53
+ export async function updateAgentRunStatus(
54
+ db: KernelDatabase,
55
+ runId: string,
56
+ status: RunStatus,
57
+ opts?: {
58
+ endedAt?: string;
59
+ outboundEventId?: string;
60
+ usageInputTokens?: number;
61
+ usageOutputTokens?: number;
62
+ usageCacheRead?: number;
63
+ usageCacheWrite?: number;
64
+ usageCostEstimate?: number;
65
+ },
66
+ ): Promise<AgentRun | undefined> {
67
+ const [row] = await db
68
+ .update(agentRuns)
69
+ .set({
70
+ status,
71
+ ...(opts?.endedAt !== undefined && { endedAt: opts.endedAt }),
72
+ ...(opts?.outboundEventId !== undefined && {
73
+ outboundEventId: opts.outboundEventId,
74
+ }),
75
+ ...(opts?.usageInputTokens !== undefined && {
76
+ usageInputTokens: opts.usageInputTokens,
77
+ }),
78
+ ...(opts?.usageOutputTokens !== undefined && {
79
+ usageOutputTokens: opts.usageOutputTokens,
80
+ }),
81
+ ...(opts?.usageCacheRead !== undefined && {
82
+ usageCacheRead: opts.usageCacheRead,
83
+ }),
84
+ ...(opts?.usageCacheWrite !== undefined && {
85
+ usageCacheWrite: opts.usageCacheWrite,
86
+ }),
87
+ ...(opts?.usageCostEstimate !== undefined && {
88
+ usageCostEstimate: opts.usageCostEstimate,
89
+ }),
90
+ })
91
+ .where(eq(agentRuns.id, runId))
92
+ .returning();
93
+ return row;
94
+ }
95
+
96
+ /**
97
+ * Record the message event that opened the run. With the in-process emitter,
98
+ * the inbound user_message id is only known once the emitter observes it —
99
+ * after the run row was inserted.
100
+ */
101
+ export async function updateAgentRunInboundEvent(
102
+ db: KernelDatabase,
103
+ runId: string,
104
+ inboundEventId: string,
105
+ ): Promise<AgentRun | undefined> {
106
+ const [row] = await db
107
+ .update(agentRuns)
108
+ .set({ inboundEventId })
109
+ .where(eq(agentRuns.id, runId))
110
+ .returning();
111
+ return row;
112
+ }
113
+
114
+ export async function getAgentRun(
115
+ db: KernelDatabase,
116
+ runId: string,
117
+ ): Promise<AgentRun | undefined> {
118
+ const [row] = await db
119
+ .select()
120
+ .from(agentRuns)
121
+ .where(eq(agentRuns.id, runId))
122
+ .limit(1);
123
+ return row;
124
+ }
125
+
126
+ export async function listAgentRunsForPiSession(
127
+ db: KernelDatabase,
128
+ piSessionId: string,
129
+ ): Promise<AgentRun[]> {
130
+ return db
131
+ .select()
132
+ .from(agentRuns)
133
+ .where(eq(agentRuns.piSessionId, piSessionId))
134
+ .orderBy(asc(agentRuns.startedAt));
135
+ }
136
+
137
+ export async function listAgentRunsForContainer(
138
+ db: KernelDatabase,
139
+ containerId: string,
140
+ ): Promise<AgentRun[]> {
141
+ return db
142
+ .select()
143
+ .from(agentRuns)
144
+ .where(eq(agentRuns.containerId, containerId))
145
+ .orderBy(asc(agentRuns.startedAt));
146
+ }
@@ -0,0 +1,138 @@
1
+ import { asc, eq } from "drizzle-orm";
2
+ import type { KernelDatabase } from "../client";
3
+ import { containers, type ContainerStatus } from "../schema/containers";
4
+ import type { Container } from "../types";
5
+
6
+ export interface UpsertContainerInput {
7
+ /**
8
+ * Caller-derived deterministic id (the kernel derives
9
+ * uuidv5(kernelNamespace(kernelId), `${kind}\n${key.join("\n")}`)).
10
+ * On conflict with an existing (kernelId, kind, appKey) row, the existing
11
+ * row's id wins — the same identity always resolves to the same container.
12
+ */
13
+ id: string;
14
+ kernelId: string;
15
+ kind: string;
16
+ appKey: string[];
17
+ label?: string;
18
+ status?: ContainerStatus;
19
+ parentContainerId?: string | null;
20
+ phase?: string | null;
21
+ phaseVocabulary?: string[] | null;
22
+ workingDir?: string | null;
23
+ metadata?: Record<string, unknown> | null;
24
+ createdAt?: string;
25
+ startedAt?: string | null;
26
+ endedAt?: string | null;
27
+ }
28
+
29
+ /**
30
+ * Idempotent container upsert keyed by (kernel_id, kind, app_key).
31
+ * Identity columns and created_at are never updated on conflict; descriptive
32
+ * fields are refreshed when provided.
33
+ */
34
+ export async function upsertContainer(
35
+ db: KernelDatabase,
36
+ input: UpsertContainerInput,
37
+ ): Promise<Container> {
38
+ const set = {
39
+ ...(input.label !== undefined && { label: input.label }),
40
+ ...(input.status !== undefined && { status: input.status }),
41
+ ...(input.parentContainerId !== undefined && {
42
+ parentContainerId: input.parentContainerId,
43
+ }),
44
+ ...(input.phase !== undefined && { phase: input.phase }),
45
+ ...(input.phaseVocabulary !== undefined && {
46
+ phaseVocabulary: input.phaseVocabulary,
47
+ }),
48
+ ...(input.workingDir !== undefined && { workingDir: input.workingDir }),
49
+ ...(input.metadata !== undefined && { metadata: input.metadata }),
50
+ ...(input.startedAt !== undefined && { startedAt: input.startedAt }),
51
+ ...(input.endedAt !== undefined && { endedAt: input.endedAt }),
52
+ };
53
+
54
+ const [row] = await db
55
+ .insert(containers)
56
+ .values({
57
+ id: input.id,
58
+ kernelId: input.kernelId,
59
+ kind: input.kind,
60
+ appKey: input.appKey,
61
+ label: input.label,
62
+ status: input.status,
63
+ parentContainerId: input.parentContainerId,
64
+ phase: input.phase,
65
+ phaseVocabulary: input.phaseVocabulary,
66
+ workingDir: input.workingDir,
67
+ metadata: input.metadata,
68
+ createdAt: input.createdAt ?? new Date().toISOString(),
69
+ startedAt: input.startedAt,
70
+ endedAt: input.endedAt,
71
+ })
72
+ .onConflictDoUpdate({
73
+ target: [containers.kernelId, containers.kind, containers.appKey],
74
+ // DO UPDATE requires a non-empty set; kind = kind is a no-op refresh.
75
+ set: Object.keys(set).length > 0 ? set : { kind: input.kind },
76
+ })
77
+ .returning();
78
+ return row;
79
+ }
80
+
81
+ export async function updateContainerStatus(
82
+ db: KernelDatabase,
83
+ containerId: string,
84
+ status: ContainerStatus,
85
+ opts?: {
86
+ phase?: string;
87
+ startedAt?: string;
88
+ endedAt?: string;
89
+ metadata?: Record<string, unknown>;
90
+ },
91
+ ): Promise<Container | undefined> {
92
+ const [row] = await db
93
+ .update(containers)
94
+ .set({
95
+ status,
96
+ ...(opts?.phase !== undefined && { phase: opts.phase }),
97
+ ...(opts?.startedAt !== undefined && { startedAt: opts.startedAt }),
98
+ ...(opts?.endedAt !== undefined && { endedAt: opts.endedAt }),
99
+ ...(opts?.metadata !== undefined && { metadata: opts.metadata }),
100
+ })
101
+ .where(eq(containers.id, containerId))
102
+ .returning();
103
+ return row;
104
+ }
105
+
106
+ export async function getContainer(
107
+ db: KernelDatabase,
108
+ containerId: string,
109
+ ): Promise<Container | undefined> {
110
+ const [row] = await db
111
+ .select()
112
+ .from(containers)
113
+ .where(eq(containers.id, containerId))
114
+ .limit(1);
115
+ return row;
116
+ }
117
+
118
+ export async function listChildContainers(
119
+ db: KernelDatabase,
120
+ parentContainerId: string,
121
+ ): Promise<Container[]> {
122
+ return db
123
+ .select()
124
+ .from(containers)
125
+ .where(eq(containers.parentContainerId, parentContainerId))
126
+ .orderBy(asc(containers.createdAt));
127
+ }
128
+
129
+ export async function listContainersForKernel(
130
+ db: KernelDatabase,
131
+ kernelId: string,
132
+ ): Promise<Container[]> {
133
+ return db
134
+ .select()
135
+ .from(containers)
136
+ .where(eq(containers.kernelId, kernelId))
137
+ .orderBy(asc(containers.createdAt));
138
+ }
@@ -0,0 +1,10 @@
1
+ export * from "./containers";
2
+ export * from "./pi-agent-sessions";
3
+ export * from "./agent-runs";
4
+ export * from "./trace-events";
5
+ export * from "./trace-blobs";
6
+ export * from "./prompt-revisions";
7
+ export * from "./read-api";
8
+ export * from "./usage";
9
+ export * from "./tx-local";
10
+ export * from "./node-sessions";