unify agent items into a flat file-based store, drop the marketplace

Replace the marketplace service dependency and the native/global/user
scope tiers with a single external directory ($OFFICER_ITEMS_DIR) holding
skills, tools, tasks, processes and extensions as plain files.

- tasks move from Postgres to TASK.md files (new file-backed task layer);
  task editing now works, which the DB path never supported
- skills/tools/processes collapse into one shared file router (single dir)
- remove the marketplace client (sync-marketplace/sync-version) and the
  boot-time sync; pi-bridge/pi-manager/sandbox point at the flat store
- drop the dead tasks + vestigial skills/tools/processes/extensions +
  item_chats tables (migration 0004)
- one-time migration script exports DB tasks and consolidates disk items

Migration verified: all 6 tasks round-trip through the runtime parser
identically to their DB rows (pipeline steps, triggers, script impls and
agentic bodies all intact).

NOTE: not yet functionally tested end-to-end — every item (each task mode,
tool, skill, extension) still needs to be run/exercised in the app before
this is trusted. To be done manually.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 00:39:17 +00:00
co-authored by Claude Opus 4.8
parent 5fd3d4faac
commit f3492512ba
29 changed files with 2366 additions and 1357 deletions
+4 -4
View File
@@ -1,7 +1,7 @@
import type { ServerWebSocket } from 'bun';
import { join, isAbsolute } from 'node:path';
import { mkdirSync, writeFileSync, chmodSync, rmSync } from 'node:fs';
import { getTaskByDirName } from 'officerdb';
import { getTaskByDirName } from './task-files';
import { getHomeDirForRole, DATA_PATH } from '../../data-path';
import { buildSandboxPrefix, buildRunuserSuffix } from '../../sidecar/sandbox';
@@ -90,10 +90,10 @@ function buildArgs(inputs: Record<string, string>, argsOrder?: string[] | null):
}
async function handleRun(ws: ServerWebSocket<WSData>, msg: RunMessage) {
const { email, role, sandboxed, userId } = ws.data;
const { email, role, sandboxed } = ws.data;
// Resolve task from database
const task = await getTaskByDirName(msg.taskDirName, userId);
// Resolve task from the file-backed store
const task = await getTaskByDirName(msg.taskDirName);
if (!task) {
send(ws, { type: 'error', message: `Task not found: ${msg.taskDirName}` });
return;