@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 +21 -0
- package/README.md +82 -2
- package/package.json +38 -4
- package/src/actions/agent-runs.ts +146 -0
- package/src/actions/containers.ts +138 -0
- package/src/actions/index.ts +10 -0
- package/src/actions/node-sessions.ts +414 -0
- package/src/actions/pi-agent-sessions.ts +93 -0
- package/src/actions/prompt-revisions.ts +122 -0
- package/src/actions/read-api.ts +314 -0
- package/src/actions/trace-blobs.ts +71 -0
- package/src/actions/trace-events.ts +158 -0
- package/src/actions/tx-local.ts +254 -0
- package/src/actions/usage.ts +79 -0
- package/src/bootstrap.ts +143 -0
- package/src/client.ts +64 -0
- package/src/index.ts +12 -0
- package/src/manifest.ts +140 -0
- package/src/read-compat.ts +35 -0
- package/src/schema/agent-runs.ts +80 -0
- package/src/schema/containers.ts +72 -0
- package/src/schema/index.ts +31 -0
- package/src/schema/pi-agent-sessions.ts +82 -0
- package/src/schema/prompt-revisions.ts +34 -0
- package/src/schema/trace-blobs.ts +20 -0
- package/src/schema/trace-events.ts +30 -0
- package/src/types.ts +29 -0
- package/src/upgrade.ts +138 -0
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
|
-
#
|
|
1
|
+
# Agent Kernel
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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";
|