chore: migrate from bun to pnpm + vitest + esbuild
- Replace bun:test with vitest across all packages - Replace bun build with esbuild - Replace bun:sqlite with better-sqlite3 - Fix OCAS Store API: store.put/get → store.cas.put/get - Fix vitest vi.mock hoisting (vi.hoisted) - Add pnpm-workspace.yaml and pnpm-lock.yaml - Update all package.json test/build scripts WIP: 8 failures remain in agent-hermes (bun engines check + sqlite migration) Refs #26
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { createMemoryStore, putSchema } from "@ocas/core";
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { createMemoryStore, putSchema, bootstrap } from "@ocas/core";
|
||||
|
||||
import { tryFrontmatterFastPath } from "../src/frontmatter.js";
|
||||
|
||||
@@ -18,6 +18,7 @@ const PLANNER_SCHEMA = {
|
||||
describe("adapter-stdout: A4 retry loop survives JSON output", () => {
|
||||
test("A4. first extraction fails, second succeeds — final result has correct data", async () => {
|
||||
const store = createMemoryStore();
|
||||
bootstrap(store);
|
||||
const schemaHash = await putSchema(store, PLANNER_SCHEMA);
|
||||
|
||||
// Simulate the retry loop from createAgent (run.ts lines 163-173):
|
||||
@@ -56,6 +57,7 @@ describe("adapter-stdout: A4 retry loop survives JSON output", () => {
|
||||
|
||||
test("A4. all retries fail — extraction returns null on every attempt", async () => {
|
||||
const store = createMemoryStore();
|
||||
bootstrap(store);
|
||||
const schemaHash = await putSchema(store, PLANNER_SCHEMA);
|
||||
|
||||
const MAX_RETRIES = 2;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { createMemoryStore, putSchema } from "@ocas/core";
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { createMemoryStore, putSchema, bootstrap } from "@ocas/core";
|
||||
|
||||
import { tryFrontmatterFastPath } from "../src/frontmatter.js";
|
||||
|
||||
@@ -31,6 +31,7 @@ const FRONTMATTER_SCHEMA = {
|
||||
describe("adapter-stdout: FrontmatterFastPathResult includes frontmatter", () => {
|
||||
test("A2. frontmatter field contains the parsed YAML frontmatter object", async () => {
|
||||
const store = createMemoryStore();
|
||||
bootstrap(store);
|
||||
const schemaHash = await putSchema(store, PLANNER_SCHEMA);
|
||||
|
||||
const raw = `---\n$status: ready\nplan: abc123\n---\nSome body text`;
|
||||
@@ -42,6 +43,7 @@ describe("adapter-stdout: FrontmatterFastPathResult includes frontmatter", () =>
|
||||
|
||||
test("A3. body field contains the markdown body after frontmatter", async () => {
|
||||
const store = createMemoryStore();
|
||||
bootstrap(store);
|
||||
const schemaHash = await putSchema(store, PLANNER_SCHEMA);
|
||||
|
||||
const raw = `---\n$status: ready\nplan: hash123\n---\nHere is the body.\n\nWith multiple paragraphs.`;
|
||||
@@ -53,6 +55,7 @@ describe("adapter-stdout: FrontmatterFastPathResult includes frontmatter", () =>
|
||||
|
||||
test("A1. result contains outputHash as valid CasRef", async () => {
|
||||
const store = createMemoryStore();
|
||||
bootstrap(store);
|
||||
const schemaHash = await putSchema(store, FRONTMATTER_SCHEMA);
|
||||
|
||||
const raw = `---\nstatus: done\nnext: null\nconfidence: 0.9\nartifacts: []\nscope: test\n---\nBody`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import type { StepContext } from "@united-workforce/protocol";
|
||||
import { buildContinuationPrompt } from "../src/build-continuation-prompt.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { buildOutputFormatInstruction } from "../src/build-output-format-instruction.js";
|
||||
|
||||
const PLANNER_SCHEMA = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import type { RoleDefinition } from "@united-workforce/protocol";
|
||||
import { buildRolePrompt } from "../src/build-role-prompt.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { describe, expect, test } from 'vitest';
|
||||
// We need to test buildHistory indirectly through buildContext
|
||||
// since buildHistory is not exported. For now, we'll test the integration
|
||||
// through the public API in a separate integration test.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { createMemoryStore, putSchema } from "@ocas/core";
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { createMemoryStore, putSchema, bootstrap } from "@ocas/core";
|
||||
|
||||
import { tryFrontmatterFastPath } from "../src/frontmatter.js";
|
||||
|
||||
@@ -48,6 +48,7 @@ const PLANNER_SCHEMA = {
|
||||
|
||||
async function makeStoreWithSchema(schema: Record<string, unknown>) {
|
||||
const store = createMemoryStore();
|
||||
bootstrap(store);
|
||||
const schemaHash = await putSchema(store, schema);
|
||||
return { store, schemaHash };
|
||||
}
|
||||
@@ -68,7 +69,7 @@ describe("STANDARD_KEYS contains only status", () => {
|
||||
const result = await tryFrontmatterFastPath(raw, schemaHash, store);
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const node = store.get(result!.outputHash);
|
||||
const node = store.cas.get(result!.outputHash);
|
||||
expect(node).not.toBeNull();
|
||||
const payload = node!.payload as Record<string, unknown>;
|
||||
expect(payload.status).toBe("done");
|
||||
@@ -106,7 +107,7 @@ describe("tryFrontmatterFastPath — happy path", () => {
|
||||
const result = await tryFrontmatterFastPath(raw, schemaHash, store);
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const node = store.get(result!.outputHash);
|
||||
const node = store.cas.get(result!.outputHash);
|
||||
expect(node).not.toBeNull();
|
||||
const payload = node!.payload as Record<string, unknown>;
|
||||
expect(payload.status).toBe("done");
|
||||
@@ -126,7 +127,7 @@ describe("tryFrontmatterFastPath — legacy fields ignored", () => {
|
||||
const result = await tryFrontmatterFastPath(raw, schemaHash, store);
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const node = store.get(result!.outputHash);
|
||||
const node = store.cas.get(result!.outputHash);
|
||||
const payload = node!.payload as Record<string, unknown>;
|
||||
expect(payload.status).toBe("done");
|
||||
expect(payload.next).toBeUndefined();
|
||||
@@ -176,7 +177,7 @@ describe("tryFrontmatterFastPath — role-specific fields", () => {
|
||||
const result = await tryFrontmatterFastPath(raw, schemaHash, store);
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const node = store.get(result!.outputHash);
|
||||
const node = store.cas.get(result!.outputHash);
|
||||
expect(node).not.toBeNull();
|
||||
const payload = node!.payload as Record<string, unknown>;
|
||||
expect(payload).toEqual({ approved: true });
|
||||
@@ -192,7 +193,7 @@ describe("tryFrontmatterFastPath — role-specific fields", () => {
|
||||
const result = await tryFrontmatterFastPath(raw, schemaHash, store);
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const node = store.get(result!.outputHash);
|
||||
const node = store.cas.get(result!.outputHash);
|
||||
expect(node).not.toBeNull();
|
||||
const payload = node!.payload as Record<string, unknown>;
|
||||
expect(payload.status).toBe("ready");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import type { WorkflowConfig } from "@united-workforce/protocol";
|
||||
import { resolveExtractModelAlias } from "../src/extract.js";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
||||
import { mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import type { ThreadId } from "@united-workforce/protocol";
|
||||
|
||||
@@ -9,19 +9,18 @@
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"bun": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"prepublishOnly": "echo 'Use bun run release from repo root' && exit 1",
|
||||
"test": "bun test __tests__/ src/__tests__/",
|
||||
"test:ci": "bun test __tests__/ src/__tests__/"
|
||||
"prepublishOnly": "echo 'Use pnpm run release from repo root' && exit 1",
|
||||
"test": "vitest run __tests__/ src/__tests__/",
|
||||
"test:ci": "vitest run __tests__/ src/__tests__/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ocas/core": "^0.1.1",
|
||||
"@ocas/fs": "^0.1.1",
|
||||
"@ocas/core": "^0.2.2",
|
||||
"@ocas/fs": "^0.2.2",
|
||||
"@united-workforce/protocol": "workspace:^",
|
||||
"@united-workforce/util": "workspace:^",
|
||||
"dotenv": "^16.6.1",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
||||
describe("parseArgv empty prompt error message", () => {
|
||||
let stderrOutput: string;
|
||||
let _exitCode: number | null;
|
||||
|
||||
@@ -22,7 +22,7 @@ function fail(message: string): never {
|
||||
}
|
||||
|
||||
function walkChain(store: Store, schemas: AgentStore["schemas"], headHash: CasRef): ChainState {
|
||||
const headNode = store.get(headHash);
|
||||
const headNode = store.cas.get(headHash);
|
||||
if (headNode === null) {
|
||||
fail(`CAS node not found: ${headHash}`);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ function walkChain(store: Store, schemas: AgentStore["schemas"], headHash: CasRe
|
||||
let hash: CasRef | null = headHash;
|
||||
|
||||
while (hash !== null) {
|
||||
const node = store.get(hash);
|
||||
const node = store.cas.get(hash);
|
||||
if (node === null) {
|
||||
fail(`CAS node not found while walking chain: ${hash}`);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ function walkChain(store: Store, schemas: AgentStore["schemas"], headHash: CasRe
|
||||
fail(`empty step chain at head ${headHash}`);
|
||||
}
|
||||
|
||||
const startNode = store.get(newest.start);
|
||||
const startNode = store.cas.get(newest.start);
|
||||
if (startNode === null || startNode.type !== schemas.startNode) {
|
||||
fail(`StartNode not found: ${newest.start}`);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ function walkChain(store: Store, schemas: AgentStore["schemas"], headHash: CasRe
|
||||
}
|
||||
|
||||
function expandOutput(store: Store, outputRef: CasRef): unknown {
|
||||
const node = store.get(outputRef);
|
||||
const node = store.cas.get(outputRef);
|
||||
if (node === null) {
|
||||
return {};
|
||||
}
|
||||
@@ -83,7 +83,7 @@ function expandOutput(store: Store, outputRef: CasRef): unknown {
|
||||
}
|
||||
|
||||
function extractStepContent(store: Store, detailRef: CasRef): string | null {
|
||||
const detailNode = store.get(detailRef);
|
||||
const detailNode = store.cas.get(detailRef);
|
||||
if (detailNode === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ function extractStepContent(store: Store, detailRef: CasRef): string | null {
|
||||
if (typeof turnRef !== "string") {
|
||||
continue;
|
||||
}
|
||||
const turnNode = store.get(turnRef as CasRef);
|
||||
const turnNode = store.cas.get(turnRef as CasRef);
|
||||
if (turnNode === null) {
|
||||
continue;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ async function buildHistory(
|
||||
}
|
||||
|
||||
async function loadWorkflow(store: Store, schemas: AgentStore["schemas"], workflowRef: CasRef) {
|
||||
const node = store.get(workflowRef);
|
||||
const node = store.cas.get(workflowRef);
|
||||
if (node === null) {
|
||||
fail(`workflow CAS node not found: ${workflowRef}`);
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ export async function extract(
|
||||
throw new Error(`failed to parse extracted JSON: ${message}`);
|
||||
}
|
||||
|
||||
const outputHash = await store.put(outputSchema, structured);
|
||||
const node = store.get(outputHash);
|
||||
const outputHash = await store.cas.put(outputSchema, structured);
|
||||
const node = store.cas.get(outputHash);
|
||||
if (node === null || !validate(store, node)) {
|
||||
throw new Error("extracted output failed JSON Schema validation");
|
||||
}
|
||||
|
||||
@@ -162,13 +162,13 @@ export async function tryFrontmatterFastPath(
|
||||
const candidate = buildCandidate(frontmatter, rawFields, schemaFields);
|
||||
|
||||
let outputHash: CasRef;
|
||||
let node: ReturnType<Store["get"]>;
|
||||
let node: ReturnType<Store["cas"]["get"]>;
|
||||
|
||||
try {
|
||||
outputHash = await store.put(outputSchema, candidate);
|
||||
node = store.get(outputHash);
|
||||
} catch {
|
||||
log("2KMQT7NR", "failed to store frontmatter candidate in CAS");
|
||||
outputHash = await store.cas.put(outputSchema, candidate);
|
||||
node = store.cas.get(outputHash);
|
||||
} catch (e) {
|
||||
log("2KMQT7NR", `failed to store frontmatter candidate in CAS: ${e}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ async function writeStepNode(options: {
|
||||
cwd: process.cwd(),
|
||||
assembledPrompt: options.assembledPromptHash,
|
||||
};
|
||||
const hash = await options.store.put(options.schemas.stepNode, payload);
|
||||
const node = options.store.get(hash);
|
||||
const hash = await options.store.cas.put(options.schemas.stepNode, payload);
|
||||
const node = options.store.cas.get(hash);
|
||||
if (node === null || !validate(options.store, node)) {
|
||||
fail("stored StepNode failed schema validation");
|
||||
}
|
||||
@@ -189,10 +189,14 @@ export function createAgent(options: AgentOptions): () => Promise<void> {
|
||||
|
||||
// Store the assembled prompt in CAS for later inspection via `step read --prompt`
|
||||
const promptText = agentResult.assembledPrompt;
|
||||
const assembledPromptHash =
|
||||
promptText !== ""
|
||||
? await ctx.meta.store.put(ctx.meta.schemas.text, promptText).catch(() => null)
|
||||
: null;
|
||||
let assembledPromptHash: CasRef | null = null;
|
||||
if (promptText !== "") {
|
||||
try {
|
||||
assembledPromptHash = ctx.meta.store.cas.put(ctx.meta.schemas.text, promptText);
|
||||
} catch {
|
||||
assembledPromptHash = null;
|
||||
}
|
||||
}
|
||||
|
||||
const stepHash = await persistStep({
|
||||
ctx,
|
||||
|
||||
@@ -2,8 +2,8 @@ import { readFile } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { createVariableStore, type Store } from "@ocas/core";
|
||||
import { createFsStore } from "@ocas/fs";
|
||||
import { bootstrap, type Store } from "@ocas/core";
|
||||
import { createFsStore, createSqliteVarStore } from "@ocas/fs";
|
||||
import type {
|
||||
AgentAlias,
|
||||
AgentConfig,
|
||||
@@ -79,8 +79,8 @@ export async function getActiveThreadEntry(
|
||||
threadId: ThreadId,
|
||||
): Promise<ThreadIndexEntry | null> {
|
||||
const casDir = getGlobalCasDir();
|
||||
const store = createFsStore(casDir);
|
||||
const varStore = createVariableStore(join(casDir, "variables.db"), store);
|
||||
const cas = createFsStore(casDir);
|
||||
const { var: varStore } = createSqliteVarStore(join(casDir, "vars"), cas);
|
||||
const vars = varStore.list({ exactName: threadVarName(threadId) });
|
||||
const v = vars[0];
|
||||
if (v === undefined) {
|
||||
@@ -100,7 +100,11 @@ export type AgentStore = {
|
||||
};
|
||||
|
||||
export async function createAgentStore(storageRoot: string): Promise<AgentStore> {
|
||||
const store = createFsStore(getGlobalCasDir());
|
||||
const casDir = getGlobalCasDir();
|
||||
const cas = createFsStore(casDir);
|
||||
const { var: varSub, tag } = createSqliteVarStore(join(casDir, "vars"), cas);
|
||||
const store: Store = { cas, var: varSub, tag };
|
||||
bootstrap(store);
|
||||
const schemas = await registerAgentSchemas(store);
|
||||
return { storageRoot, store, schemas };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user