/**
* `run_pattern` over a pattern whose input is a SQLite database handle — the
* shape a model writes when the harness hands it an injected store.
*
* Two separate things carry that run. The source compiles with `SqliteDb`
* naming an input, which the js-compiler's brand filter settles at declaration
* emit; and the handle cell named as that input reaches the pattern as a
* handle, which turns on how `run_pattern` measures a live-cell input against
* the argument schema before any piece exists.
*/
import { afterEach, beforeEach, describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { createSession, Identity } from "@commonfabric/identity";
import { table } from "@commonfabric/memory/sqlite/schema";
import type { SqliteDbRef } from "@commonfabric/memory/v2";
import { PiecesController } from "@commonfabric/piece/ops";
import { Runtime } from "@commonfabric/runner";
import { createLLMFriendlyLink } from "@commonfabric/runner/shared";
import { StorageManager } from "@commonfabric/runner/storage/cache.deno";
import { CfHarnessEngine } from "../src/engine.ts";
import type { RunPatternToolSuccessOutput } from "../src/tools/run-pattern.ts";
import type {
SandboxCommandRequest,
SandboxCommandResult,
SandboxRuntime,
SandboxRuntimeDescription,
SandboxShellRequest,
} from "../src/sandbox/types.ts";
const signer = await Identity.fromPassphrase("cf-harness sqlite input");
/**
* A pattern typed the way a model types one against an injected store: the
* input is a `SqliteDb`, and the rows come from `query` on it.
*/
const MAIL_PATTERN_SOURCE = [
"import { computed, pattern, type SqliteDb } from 'commonfabric';",
"interface BillRow { id: number; subject: string; }",
"type Input = { mail: SqliteDb };",
"type Output = { subjects: string[]; pending: boolean };",
"export default pattern(({ mail }) => {",
" const bills = mail.query('SELECT id, subject FROM messages', {});",
" return {",
" subjects: computed(() => (bills.result ?? []).map((row) => row.subject)),",
" pending: computed(() => bills.pending === true),",
" };",
"});",
"",
].join("\n");
class FakeSandboxRuntime implements SandboxRuntime {
describe(): SandboxRuntimeDescription {
return {
kind: "runsc-cfc",
defaultWorkingDirectory: this.defaultWorkingDirectory(),
cfc: { runtimeRequested: true, workspaceMountPath: "/workspace" },
};
}
resolvePath(path: string): string {
return path;
}
isPathWithinWorkspace(path: string): boolean {
return path.startsWith("/workspace");
}
isPathWithinAllowedRoots(path: string): boolean {
return this.isPathWithinWorkspace(path);
}
defaultWorkingDirectory(): string {
return "/workspace";
}
run(_request: SandboxCommandRequest): Promise {
return Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
}
runShell(_request: SandboxShellRequest): Promise {
return Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
}
}
describe("run_pattern over a database handle input", () => {
let storageManager: ReturnType;
let runtime: Runtime;
let pieces: PiecesController;
beforeEach(async () => {
storageManager = StorageManager.emulate({ as: signer });
runtime = new Runtime({
apiUrl: new URL("http://toolshed.test"),
storageManager,
});
pieces = new PiecesController(
createSession({
identity: signer,
spaceDid: (await Identity.generate()).did(),
}),
runtime,
);
await pieces.synced();
});
afterEach(async () => {
await runtime?.dispose();
await storageManager?.close();
});
/**
* Seed a database holding one `messages` row and return the LLM-friendly
* link naming its handle cell — the address a model is handed for an
* injected store, and passes straight back as an input.
*/
async function seedDatabaseHandle(subject: string): Promise {
const space = pieces.getSpace();
const dbRef: SqliteDbRef = {
id: `sqlite-input-${crypto.randomUUID()}`,
tables: {
messages: table({ id: "integer primary key", subject: "text" }),
},
};
const tx = runtime.edit();
const handle = runtime.getCell(
space,
"sqlite-input-handle",
undefined,
tx,
);
handle.set(dbRef);
tx.recordSqliteWrite!(space, {
op: "sqlite",
db: dbRef,
sql: "INSERT INTO messages (id, subject) VALUES (?, ?)",
params: [1, subject],
});
expect((await tx.commit().settled).error).toBeUndefined();
await runtime.idle();
return createLLMFriendlyLink(handle.getAsNormalizedFullLink(), space);
}
function createEngine(): CfHarnessEngine {
return new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `sqlite-input-${crypto.randomUUID()}`,
cfcEnforcementMode: "disabled",
fabricSessionFactory: () => Promise.resolve({ pieces }),
});
}
it("runs a pattern whose input is typed `SqliteDb` and queries the database the input names", async () => {
const mail = await seedDatabaseHandle("rent");
const result = await createEngine().invokeBuiltinTool("run_pattern", {
sourceText: MAIL_PATTERN_SOURCE,
inputs: { mail },
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
// The query is a server round-trip, so the run returns while it is still
// in flight; the piece's own result is where it lands.
const piece = await pieces.get(output.pieceId);
await runtime.idle();
await pieces.synced();
await runtime.idle();
expect(await piece.result.get()).toEqual({
pending: false,
subjects: ["rent"],
});
});
});