Merge pull request 'chore: fix biome check errors (40 → 0)' (#50) from chore/fix-biome-check into main
CI / check (push) Failing after 1m16s
CI / check (push) Failing after 1m16s
chore: fix biome check errors (40 → 0) (#50)
This commit was merged in pull request #50.
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { mkdtemp, rm } from "node:fs/promises";
|
import { mkdtemp, rm } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
||||||
import { createMemoryStore } from "@ocas/core";
|
import { createMemoryStore } from "@ocas/core";
|
||||||
|
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||||
import { storeBuiltinDetail } from "../src/detail.js";
|
import { storeBuiltinDetail } from "../src/detail.js";
|
||||||
import { appendSessionTurn, initSessionDir } from "../src/session.js";
|
import { appendSessionTurn, initSessionDir } from "../src/session.js";
|
||||||
import type { BuiltinTurnPayload } from "../src/types.js";
|
import type { BuiltinTurnPayload } from "../src/types.js";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
||||||
import { readFileTool } from "../src/tools/read-file.js";
|
|
||||||
import { writeFile, mkdir, rm } from "node:fs/promises";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
import { readFileTool } from "../src/tools/read-file.js";
|
||||||
|
|
||||||
const testDir = join(tmpdir(), `read-file-test-${Date.now()}`);
|
const testDir = join(tmpdir(), `read-file-test-${Date.now()}`);
|
||||||
const ctx = { cwd: testDir, storageRoot: testDir };
|
const ctx = { cwd: testDir, storageRoot: testDir };
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
|
||||||
import { runCommandTool } from "../src/tools/run-command.js";
|
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { runCommandTool } from "../src/tools/run-command.js";
|
||||||
|
|
||||||
const ctx = { cwd: tmpdir(), storageRoot: tmpdir() };
|
const ctx = { cwd: tmpdir(), storageRoot: tmpdir() };
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { mkdtemp, rm } from "node:fs/promises";
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||||
import type { BuiltinTurnPayload } from "../src/types.js";
|
|
||||||
import {
|
import {
|
||||||
appendSessionTurn,
|
appendSessionTurn,
|
||||||
initSessionDir,
|
initSessionDir,
|
||||||
readSessionTurns,
|
readSessionTurns,
|
||||||
removeSession,
|
removeSession,
|
||||||
} from "../src/session.js";
|
} from "../src/session.js";
|
||||||
|
import type { BuiltinTurnPayload } from "../src/types.js";
|
||||||
|
|
||||||
describe("session", () => {
|
describe("session", () => {
|
||||||
let storageRoot: string;
|
let storageRoot: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { describe, it, expect, afterAll } from "vitest";
|
|
||||||
import { writeFileTool } from "../src/tools/write-file.js";
|
|
||||||
import { readFile, rm } from "node:fs/promises";
|
import { readFile, rm } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { afterAll, describe, expect, it } from "vitest";
|
||||||
|
import { writeFileTool } from "../src/tools/write-file.js";
|
||||||
|
|
||||||
const testDir = join(tmpdir(), `write-file-test-${Date.now()}`);
|
const testDir = join(tmpdir(), `write-file-test-${Date.now()}`);
|
||||||
const ctx = { cwd: testDir, storageRoot: testDir };
|
const ctx = { cwd: testDir, storageRoot: testDir };
|
||||||
|
|||||||
@@ -6,12 +6,7 @@ import type { CasRef, ThreadId } from "@united-workforce/protocol";
|
|||||||
import { describe, expect, test } from "vitest";
|
import { describe, expect, test } from "vitest";
|
||||||
import { createMarker, deleteMarker } from "../background/index.js";
|
import { createMarker, deleteMarker } from "../background/index.js";
|
||||||
import { cmdThreadList, cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
import { cmdThreadList, cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
||||||
import {
|
import { completeThread, createUwfStore, loadActiveThreads, setThread } from "../store.js";
|
||||||
completeThread,
|
|
||||||
createUwfStore,
|
|
||||||
loadActiveThreads,
|
|
||||||
setThread,
|
|
||||||
} from "../store.js";
|
|
||||||
|
|
||||||
const OUTPUT_SCHEMA = {
|
const OUTPUT_SCHEMA = {
|
||||||
type: "object" as const,
|
type: "object" as const,
|
||||||
@@ -287,11 +282,11 @@ describe("currentRole field", () => {
|
|||||||
try {
|
try {
|
||||||
const wf = join(tmpDir, "test-current-role.yaml");
|
const wf = join(tmpDir, "test-current-role.yaml");
|
||||||
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
||||||
const { thread, workflow } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
||||||
const tid = thread as ThreadId;
|
const tid = thread as ThreadId;
|
||||||
|
|
||||||
const uwfForIndex = await createUwfStore(storageRoot);
|
const uwfForIndex = await createUwfStore(storageRoot);
|
||||||
const head = loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
||||||
completeThread(uwfForIndex.varStore, tid, "completed");
|
completeThread(uwfForIndex.varStore, tid, "completed");
|
||||||
|
|
||||||
const result = await cmdThreadShow(storageRoot, tid);
|
const result = await cmdThreadShow(storageRoot, tid);
|
||||||
@@ -308,11 +303,11 @@ describe("currentRole field", () => {
|
|||||||
try {
|
try {
|
||||||
const wf = join(tmpDir, "test-current-role.yaml");
|
const wf = join(tmpDir, "test-current-role.yaml");
|
||||||
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
||||||
const { thread, workflow } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
||||||
const tid = thread as ThreadId;
|
const tid = thread as ThreadId;
|
||||||
|
|
||||||
const uwfForIndex = await createUwfStore(storageRoot);
|
const uwfForIndex = await createUwfStore(storageRoot);
|
||||||
const head = loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
||||||
completeThread(uwfForIndex.varStore, tid, "cancelled");
|
completeThread(uwfForIndex.varStore, tid, "cancelled");
|
||||||
|
|
||||||
const result = await cmdThreadShow(storageRoot, tid);
|
const result = await cmdThreadShow(storageRoot, tid);
|
||||||
@@ -329,7 +324,7 @@ describe("currentRole field", () => {
|
|||||||
try {
|
try {
|
||||||
const wf = join(tmpDir, "test-current-role.yaml");
|
const wf = join(tmpDir, "test-current-role.yaml");
|
||||||
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
||||||
const { thread, workflow } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
||||||
const tid = thread as ThreadId;
|
const tid = thread as ThreadId;
|
||||||
|
|
||||||
await createMarker(storageRoot, {
|
await createMarker(storageRoot, {
|
||||||
@@ -366,7 +361,7 @@ describe("currentRole field", () => {
|
|||||||
const comp = await cmdThreadStart(storageRoot, wf, "completed", tmpDir);
|
const comp = await cmdThreadStart(storageRoot, wf, "completed", tmpDir);
|
||||||
const compId = comp.thread as ThreadId;
|
const compId = comp.thread as ThreadId;
|
||||||
const uwfForIndex = await createUwfStore(storageRoot);
|
const uwfForIndex = await createUwfStore(storageRoot);
|
||||||
const compHead = loadActiveThreads(uwfForIndex.varStore)[compId]!.head;
|
const _compHead = loadActiveThreads(uwfForIndex.varStore)[compId]!.head;
|
||||||
completeThread(uwfForIndex.varStore, compId, "completed");
|
completeThread(uwfForIndex.varStore, compId, "completed");
|
||||||
|
|
||||||
const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
|
const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ async function markThreadRunning(storageRoot: string, threadId: ThreadId, workfl
|
|||||||
async function completeThread(
|
async function completeThread(
|
||||||
storageRoot: string,
|
storageRoot: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
workflowHash: CasRef,
|
_workflowHash: CasRef,
|
||||||
headHash: CasRef,
|
_headHash: CasRef,
|
||||||
) {
|
) {
|
||||||
const uwfIdx = await createUwfStore(storageRoot);
|
const uwfIdx = await createUwfStore(storageRoot);
|
||||||
completeThreadInStore(uwfIdx.varStore, threadId, "completed");
|
completeThreadInStore(uwfIdx.varStore, threadId, "completed");
|
||||||
|
|||||||
@@ -539,9 +539,9 @@ describe("uwf thread resume - completed threads", () => {
|
|||||||
const { createUwfStore, getThread } = await import("../store.js");
|
const { createUwfStore, getThread } = await import("../store.js");
|
||||||
const verifyUwf = await createUwfStore(tmpDir);
|
const verifyUwf = await createUwfStore(tmpDir);
|
||||||
const verifyEntry = getThread(verifyUwf.varStore, THREAD_ID);
|
const verifyEntry = getThread(verifyUwf.varStore, THREAD_ID);
|
||||||
// biome-ignore lint/nursery/noConsole: test debugging
|
// biome-ignore lint/suspicious/noConsole: test debugging
|
||||||
console.log("Seeded entry status:", verifyEntry?.status);
|
console.log("Seeded entry status:", verifyEntry?.status);
|
||||||
// biome-ignore lint/nursery/noConsole: test debugging
|
// biome-ignore lint/suspicious/noConsole: test debugging
|
||||||
console.log("Seeded entry:", JSON.stringify(verifyEntry, null, 2));
|
console.log("Seeded entry:", JSON.stringify(verifyEntry, null, 2));
|
||||||
|
|
||||||
const promptCapturePath = join(tmpDir, "captured-prompt-completed.txt");
|
const promptCapturePath = join(tmpDir, "captured-prompt-completed.txt");
|
||||||
@@ -601,7 +601,7 @@ echo '${adapterJson}'
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.status !== 0) {
|
if (result.status !== 0) {
|
||||||
// biome-ignore lint/nursery/noConsole: test debugging
|
// biome-ignore lint/suspicious/noConsole: test debugging
|
||||||
console.error("Command failed:", result.stderr);
|
console.error("Command failed:", result.stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,7 @@ import type { CasRef, ThreadId } from "@united-workforce/protocol";
|
|||||||
import { describe, expect, test } from "vitest";
|
import { describe, expect, test } from "vitest";
|
||||||
import { createMarker, deleteMarker } from "../background/index.js";
|
import { createMarker, deleteMarker } from "../background/index.js";
|
||||||
import { cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
import { cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
||||||
import {
|
import { completeThread, createUwfStore, loadAllThreads, setThread } from "../store.js";
|
||||||
completeThread,
|
|
||||||
createUwfStore,
|
|
||||||
loadAllThreads,
|
|
||||||
setThread,
|
|
||||||
} from "../store.js";
|
|
||||||
|
|
||||||
const OUTPUT_SCHEMA = {
|
const OUTPUT_SCHEMA = {
|
||||||
type: "object" as const,
|
type: "object" as const,
|
||||||
@@ -205,7 +200,7 @@ describe("thread show status field", () => {
|
|||||||
// Create a thread
|
// Create a thread
|
||||||
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
||||||
const threadId = startResult.thread as ThreadId;
|
const threadId = startResult.thread as ThreadId;
|
||||||
const workflow = startResult.workflow;
|
const _workflow = startResult.workflow;
|
||||||
|
|
||||||
// Get the head hash before moving to history
|
// Get the head hash before moving to history
|
||||||
const uwfForIndex = await createUwfStore(storageRoot);
|
const uwfForIndex = await createUwfStore(storageRoot);
|
||||||
@@ -234,7 +229,7 @@ describe("thread show status field", () => {
|
|||||||
// Create a thread
|
// Create a thread
|
||||||
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
||||||
const threadId = startResult.thread as ThreadId;
|
const threadId = startResult.thread as ThreadId;
|
||||||
const workflow = startResult.workflow;
|
const _workflow = startResult.workflow;
|
||||||
|
|
||||||
// Get the head hash before moving to history
|
// Get the head hash before moving to history
|
||||||
const uwfForIndex = await createUwfStore(storageRoot);
|
const uwfForIndex = await createUwfStore(storageRoot);
|
||||||
@@ -263,7 +258,7 @@ describe("thread show status field", () => {
|
|||||||
// Create a thread
|
// Create a thread
|
||||||
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
||||||
const threadId = startResult.thread as ThreadId;
|
const threadId = startResult.thread as ThreadId;
|
||||||
const workflow = startResult.workflow;
|
const _workflow = startResult.workflow;
|
||||||
|
|
||||||
// Get the head hash before moving to history
|
// Get the head hash before moving to history
|
||||||
const uwfForIndex = await createUwfStore(storageRoot);
|
const uwfForIndex = await createUwfStore(storageRoot);
|
||||||
|
|||||||
@@ -1031,6 +1031,7 @@ function archiveThread(uwf: UwfStore, threadId: ThreadId, _workflow: CasRef, _he
|
|||||||
completeThread(uwf.varStore, threadId, "completed");
|
completeThread(uwf.varStore, threadId, "completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: orchestration function with inherent branching
|
||||||
export async function cmdThreadResume(
|
export async function cmdThreadResume(
|
||||||
storageRoot: string,
|
storageRoot: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
@@ -1057,13 +1058,7 @@ export async function cmdThreadResume(
|
|||||||
if (entry.status === "completed" || entry.status === "cancelled") {
|
if (entry.status === "completed" || entry.status === "cancelled") {
|
||||||
status = entry.status;
|
status = entry.status;
|
||||||
} else {
|
} else {
|
||||||
status = await resolveActiveThreadStatus(
|
status = await resolveActiveThreadStatus(storageRoot, threadId, uwf, headHash, workflowHash);
|
||||||
storageRoot,
|
|
||||||
threadId,
|
|
||||||
uwf,
|
|
||||||
headHash,
|
|
||||||
workflowHash,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status !== "suspended" && status !== "completed") {
|
if (status !== "suspended" && status !== "completed") {
|
||||||
@@ -1278,7 +1273,7 @@ function resolveResumeStepTarget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resolveModeratorStepTarget(
|
async function resolveModeratorStepTarget(
|
||||||
storageRoot: string,
|
_storageRoot: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
entry: ThreadIndexEntry,
|
entry: ThreadIndexEntry,
|
||||||
headHash: CasRef,
|
headHash: CasRef,
|
||||||
@@ -1347,7 +1342,7 @@ async function resolveModeratorStepTarget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function finalizeAgentStep(
|
async function finalizeAgentStep(
|
||||||
storageRoot: string,
|
_storageRoot: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
workflowHash: CasRef,
|
workflowHash: CasRef,
|
||||||
workflow: WorkflowPayload,
|
workflow: WorkflowPayload,
|
||||||
|
|||||||
@@ -6,13 +6,7 @@ import { join } from "node:path";
|
|||||||
|
|
||||||
import { bootstrap, type Hash, type Store, type VarStore } from "@ocas/core";
|
import { bootstrap, type Hash, type Store, type VarStore } from "@ocas/core";
|
||||||
import { createFsStore, createSqliteVarStore } from "@ocas/fs";
|
import { createFsStore, createSqliteVarStore } from "@ocas/fs";
|
||||||
import type {
|
import type { CasRef, ThreadId, ThreadIndexEntry, ThreadsIndex } from "@united-workforce/protocol";
|
||||||
CasRef,
|
|
||||||
ThreadId,
|
|
||||||
ThreadIndexEntry,
|
|
||||||
ThreadListItem,
|
|
||||||
ThreadsIndex,
|
|
||||||
} from "@united-workforce/protocol";
|
|
||||||
import { parseThreadsIndex } from "@united-workforce/protocol";
|
import { parseThreadsIndex } from "@united-workforce/protocol";
|
||||||
import { parse } from "yaml";
|
import { parse } from "yaml";
|
||||||
|
|
||||||
@@ -26,7 +20,6 @@ export const REGISTRY_VAR_PREFIX = "@uwf/registry/";
|
|||||||
/** Variable name prefix for active thread entries (`@uwf/thread/<thread-id>`). */
|
/** Variable name prefix for active thread entries (`@uwf/thread/<thread-id>`). */
|
||||||
export const THREAD_VAR_PREFIX = "@uwf/thread/";
|
export const THREAD_VAR_PREFIX = "@uwf/thread/";
|
||||||
|
|
||||||
|
|
||||||
/** A workflow entry discovered from the project-local .workflows/ directory. */
|
/** A workflow entry discovered from the project-local .workflows/ directory. */
|
||||||
export type ProjectWorkflowEntry = {
|
export type ProjectWorkflowEntry = {
|
||||||
/** Workflow name (from YAML `name` field, equals filename stem). */
|
/** Workflow name (from YAML `name` field, equals filename stem). */
|
||||||
@@ -154,7 +147,6 @@ export function getThreadsPath(storageRoot: string): string {
|
|||||||
return join(storageRoot, "threads.yaml");
|
return join(storageRoot, "threads.yaml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type UwfStore = {
|
export type UwfStore = {
|
||||||
storageRoot: string;
|
storageRoot: string;
|
||||||
store: Store;
|
store: Store;
|
||||||
@@ -387,7 +379,6 @@ export function completeThread(
|
|||||||
setThread(varStore, threadId, completed);
|
setThread(varStore, threadId, completed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type LegacyHistoryEntry = {
|
type LegacyHistoryEntry = {
|
||||||
thread: ThreadId;
|
thread: ThreadId;
|
||||||
workflow: CasRef;
|
workflow: CasRef;
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export function checkWorkflowFilenameConsistency(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Validate YAML-parsed workflow document shape (outputSchema may be inline JSON Schema). */
|
/** Validate YAML-parsed workflow document shape (outputSchema may be inline JSON Schema). */
|
||||||
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: validation function with many field checks
|
||||||
export function parseWorkflowPayload(raw: unknown): WorkflowPayload | null {
|
export function parseWorkflowPayload(raw: unknown): WorkflowPayload | null {
|
||||||
if (!isRecord(raw)) {
|
if (!isRecord(raw)) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ export function normalizeThreadIndexEntry(raw: unknown): ThreadIndexEntry | null
|
|||||||
head: head as CasRef,
|
head: head as CasRef,
|
||||||
suspendedRole: typeof suspendedRole === "string" ? suspendedRole : null,
|
suspendedRole: typeof suspendedRole === "string" ? suspendedRole : null,
|
||||||
suspendMessage: typeof suspendMessage === "string" ? suspendMessage : null,
|
suspendMessage: typeof suspendMessage === "string" ? suspendMessage : null,
|
||||||
status: typeof status === "string" ? (status as "idle" | "running" | "suspended" | "completed" | "cancelled") : "idle",
|
status:
|
||||||
|
typeof status === "string"
|
||||||
|
? (status as "idle" | "running" | "suspended" | "completed" | "cancelled")
|
||||||
|
: "idle",
|
||||||
completedAt: typeof completedAt === "number" ? completedAt : null,
|
completedAt: typeof completedAt === "number" ? completedAt : null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -79,7 +82,12 @@ export function serializeThreadIndexEntry(
|
|||||||
entry: ThreadIndexEntry,
|
entry: ThreadIndexEntry,
|
||||||
): string | Record<string, string | number> {
|
): string | Record<string, string | number> {
|
||||||
// Compact string only for idle status with no suspend metadata
|
// Compact string only for idle status with no suspend metadata
|
||||||
if (entry.status === "idle" && entry.suspendedRole === null && entry.suspendMessage === null && entry.completedAt === null) {
|
if (
|
||||||
|
entry.status === "idle" &&
|
||||||
|
entry.suspendedRole === null &&
|
||||||
|
entry.suspendMessage === null &&
|
||||||
|
entry.completedAt === null
|
||||||
|
) {
|
||||||
return entry.head;
|
return entry.head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { parseArgv } from "../src/run.js";
|
import { parseArgv } from "../src/run.js";
|
||||||
|
|
||||||
describe("parseArgv", () => {
|
describe("parseArgv", () => {
|
||||||
let exitSpy: ReturnType<typeof vi.spyOn>;
|
let exitSpy: ReturnType<typeof vi.spyOn>;
|
||||||
let stderrSpy: ReturnType<typeof vi.spyOn>;
|
let _stderrSpy: ReturnType<typeof vi.spyOn>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
|
exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
|
||||||
throw new Error("process.exit");
|
throw new Error("process.exit");
|
||||||
}) as never);
|
}) as never);
|
||||||
stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation((() => true) as never);
|
_stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation((() => true) as never);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -17,22 +17,37 @@ describe("parseArgv", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns threadId, role, prompt for valid argv", () => {
|
it("returns threadId, role, prompt for valid argv", () => {
|
||||||
const result = parseArgv(["node", "script", "--thread", "abc123", "--role", "developer", "--prompt", "do stuff"]);
|
const result = parseArgv([
|
||||||
|
"node",
|
||||||
|
"script",
|
||||||
|
"--thread",
|
||||||
|
"abc123",
|
||||||
|
"--role",
|
||||||
|
"developer",
|
||||||
|
"--prompt",
|
||||||
|
"do stuff",
|
||||||
|
]);
|
||||||
expect(result).toEqual({ threadId: "abc123", role: "developer", prompt: "do stuff" });
|
expect(result).toEqual({ threadId: "abc123", role: "developer", prompt: "do stuff" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exits when --thread is missing", () => {
|
it("exits when --thread is missing", () => {
|
||||||
expect(() => parseArgv(["node", "script", "--role", "dev", "--prompt", "x"])).toThrow("process.exit");
|
expect(() => parseArgv(["node", "script", "--role", "dev", "--prompt", "x"])).toThrow(
|
||||||
|
"process.exit",
|
||||||
|
);
|
||||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exits when --role is missing", () => {
|
it("exits when --role is missing", () => {
|
||||||
expect(() => parseArgv(["node", "script", "--thread", "t1", "--prompt", "x"])).toThrow("process.exit");
|
expect(() => parseArgv(["node", "script", "--thread", "t1", "--prompt", "x"])).toThrow(
|
||||||
|
"process.exit",
|
||||||
|
);
|
||||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exits when --prompt is missing", () => {
|
it("exits when --prompt is missing", () => {
|
||||||
expect(() => parseArgv(["node", "script", "--thread", "t1", "--role", "dev"])).toThrow("process.exit");
|
expect(() => parseArgv(["node", "script", "--thread", "t1", "--role", "dev"])).toThrow(
|
||||||
|
"process.exit",
|
||||||
|
);
|
||||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { describe, it, expect } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import {
|
import {
|
||||||
resolveStorageRoot,
|
|
||||||
getDefaultStorageRoot,
|
|
||||||
getCasDir,
|
getCasDir,
|
||||||
getConfigPath,
|
getConfigPath,
|
||||||
|
getDefaultStorageRoot,
|
||||||
getEnvPath,
|
getEnvPath,
|
||||||
getGlobalCasDir,
|
getGlobalCasDir,
|
||||||
normalizeWorkflowConfig,
|
normalizeWorkflowConfig,
|
||||||
|
resolveStorageRoot,
|
||||||
} from "../src/storage.js";
|
} from "../src/storage.js";
|
||||||
|
|
||||||
const VALID_CONFIG = {
|
const VALID_CONFIG = {
|
||||||
@@ -79,28 +79,33 @@ describe("normalizeWorkflowConfig", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("throws when defaultAgent missing", () => {
|
it("throws when defaultAgent missing", () => {
|
||||||
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, defaultAgent: undefined }))
|
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, defaultAgent: undefined })).toThrow(
|
||||||
.toThrow("defaultAgent and defaultModel");
|
"defaultAgent and defaultModel",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws when defaultModel missing", () => {
|
it("throws when defaultModel missing", () => {
|
||||||
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, defaultModel: 42 }))
|
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, defaultModel: 42 })).toThrow(
|
||||||
.toThrow("defaultAgent and defaultModel");
|
"defaultAgent and defaultModel",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws on invalid providers entry", () => {
|
it("throws on invalid providers entry", () => {
|
||||||
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, providers: { bad: "string" } }))
|
expect(() =>
|
||||||
.toThrow("config.providers.bad must be a mapping");
|
normalizeWorkflowConfig({ ...VALID_CONFIG, providers: { bad: "string" } }),
|
||||||
|
).toThrow("config.providers.bad must be a mapping");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws on invalid models entry", () => {
|
it("throws on invalid models entry", () => {
|
||||||
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, models: { m: { provider: 123, name: "x" } } }))
|
expect(() =>
|
||||||
.toThrow("config.models.m requires provider and name");
|
normalizeWorkflowConfig({ ...VALID_CONFIG, models: { m: { provider: 123, name: "x" } } }),
|
||||||
|
).toThrow("config.models.m requires provider and name");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws on invalid agents entry", () => {
|
it("throws on invalid agents entry", () => {
|
||||||
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, agents: "bad" }))
|
expect(() => normalizeWorkflowConfig({ ...VALID_CONFIG, agents: "bad" })).toThrow(
|
||||||
.toThrow("config.agents must be a mapping");
|
"config.agents must be a mapping",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns null for undefined modelOverrides", () => {
|
it("returns null for undefined modelOverrides", () => {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import {
|
import {
|
||||||
CROCKFORD_BASE32_ALPHABET,
|
CROCKFORD_BASE32_ALPHABET,
|
||||||
encodeCrockfordBase32Bits,
|
|
||||||
decodeCrockfordBase32Bits,
|
decodeCrockfordBase32Bits,
|
||||||
encodeUint64AsCrockford,
|
|
||||||
decodeCrockfordToUint64,
|
decodeCrockfordToUint64,
|
||||||
|
encodeCrockfordBase32Bits,
|
||||||
|
encodeUint64AsCrockford,
|
||||||
} from "../src/base32.js";
|
} from "../src/base32.js";
|
||||||
|
|
||||||
describe("CROCKFORD_BASE32_ALPHABET", () => {
|
describe("CROCKFORD_BASE32_ALPHABET", () => {
|
||||||
@@ -105,7 +105,7 @@ describe("encodeUint64AsCrockford / decodeCrockfordToUint64", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("roundtrips arbitrary value", () => {
|
it("roundtrips arbitrary value", () => {
|
||||||
const value = 0xDEAD_BEEF_CAFE_BABEn;
|
const value = 0xdead_beef_cafe_baben;
|
||||||
const encoded = encodeUint64AsCrockford(value);
|
const encoded = encodeUint64AsCrockford(value);
|
||||||
const decoded = decodeCrockfordToUint64(encoded);
|
const decoded = decodeCrockfordToUint64(encoded);
|
||||||
expect(decoded).toEqual({ ok: true, value });
|
expect(decoded).toEqual({ ok: true, value });
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, expect, it } from "vitest";
|
||||||
import { assertValidLogTag } from '../src/process-logger/log-tag.js';
|
import { assertValidLogTag } from "../src/process-logger/log-tag.js";
|
||||||
|
|
||||||
describe('assertValidLogTag', () => {
|
describe("assertValidLogTag", () => {
|
||||||
it('accepts valid 8-char Crockford Base32 tags', () => {
|
it("accepts valid 8-char Crockford Base32 tags", () => {
|
||||||
expect(() => assertValidLogTag('0123ABCD')).not.toThrow();
|
expect(() => assertValidLogTag("0123ABCD")).not.toThrow();
|
||||||
expect(() => assertValidLogTag('VWXYZ789')).not.toThrow();
|
expect(() => assertValidLogTag("VWXYZ789")).not.toThrow();
|
||||||
expect(() => assertValidLogTag('00000000')).not.toThrow();
|
expect(() => assertValidLogTag("00000000")).not.toThrow();
|
||||||
expect(() => assertValidLogTag('ZZZZZZZZ')).not.toThrow();
|
expect(() => assertValidLogTag("ZZZZZZZZ")).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts lowercase (converted via toUpperCase)', () => {
|
it("accepts lowercase (converted via toUpperCase)", () => {
|
||||||
expect(() => assertValidLogTag('abcdefgh')).not.toThrow();
|
expect(() => assertValidLogTag("abcdefgh")).not.toThrow();
|
||||||
expect(() => assertValidLogTag('0a1b2c3d')).not.toThrow();
|
expect(() => assertValidLogTag("0a1b2c3d")).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws on too short', () => {
|
it("throws on too short", () => {
|
||||||
expect(() => assertValidLogTag('1234567')).toThrow();
|
expect(() => assertValidLogTag("1234567")).toThrow();
|
||||||
expect(() => assertValidLogTag('')).toThrow();
|
expect(() => assertValidLogTag("")).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws on too long', () => {
|
it("throws on too long", () => {
|
||||||
expect(() => assertValidLogTag('123456789')).toThrow();
|
expect(() => assertValidLogTag("123456789")).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws on invalid chars I, L, O, U', () => {
|
it("throws on invalid chars I, L, O, U", () => {
|
||||||
expect(() => assertValidLogTag('IIIIIIII')).toThrow();
|
expect(() => assertValidLogTag("IIIIIIII")).toThrow();
|
||||||
expect(() => assertValidLogTag('LLLLLLLL')).toThrow();
|
expect(() => assertValidLogTag("LLLLLLLL")).toThrow();
|
||||||
expect(() => assertValidLogTag('OOOOOOOO')).toThrow();
|
expect(() => assertValidLogTag("OOOOOOOO")).toThrow();
|
||||||
expect(() => assertValidLogTag('UUUUUUUU')).toThrow();
|
expect(() => assertValidLogTag("UUUUUUUU")).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws on special characters', () => {
|
it("throws on special characters", () => {
|
||||||
expect(() => assertValidLogTag('1234567!')).toThrow();
|
expect(() => assertValidLogTag("1234567!")).toThrow();
|
||||||
expect(() => assertValidLogTag('ABCD-EFG')).toThrow();
|
expect(() => assertValidLogTag("ABCD-EFG")).toThrow();
|
||||||
expect(() => assertValidLogTag('ABCD EFG')).toThrow();
|
expect(() => assertValidLogTag("ABCD EFG")).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, expect, it } from "vitest";
|
||||||
import { mergeRefsWithContentHash, normalizeRefsField } from '../src/refs-field.js';
|
import { mergeRefsWithContentHash, normalizeRefsField } from "../src/refs-field.js";
|
||||||
|
|
||||||
describe('mergeRefsWithContentHash', () => {
|
describe("mergeRefsWithContentHash", () => {
|
||||||
it('appends a new content hash', () => {
|
it("appends a new content hash", () => {
|
||||||
expect(mergeRefsWithContentHash(['a', 'b'], 'c')).toEqual(['a', 'b', 'c']);
|
expect(mergeRefsWithContentHash(["a", "b"], "c")).toEqual(["a", "b", "c"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('skips duplicate content hash', () => {
|
it("skips duplicate content hash", () => {
|
||||||
expect(mergeRefsWithContentHash(['a', 'b'], 'b')).toEqual(['a', 'b']);
|
expect(mergeRefsWithContentHash(["a", "b"], "b")).toEqual(["a", "b"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preserves order', () => {
|
it("preserves order", () => {
|
||||||
expect(mergeRefsWithContentHash(['x', 'y'], 'z')).toEqual(['x', 'y', 'z']);
|
expect(mergeRefsWithContentHash(["x", "y"], "z")).toEqual(["x", "y", "z"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty refs', () => {
|
it("handles empty refs", () => {
|
||||||
expect(mergeRefsWithContentHash([], 'a')).toEqual(['a']);
|
expect(mergeRefsWithContentHash([], "a")).toEqual(["a"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('normalizeRefsField', () => {
|
describe("normalizeRefsField", () => {
|
||||||
it('returns empty array for non-array', () => {
|
it("returns empty array for non-array", () => {
|
||||||
expect(normalizeRefsField(null)).toEqual([]);
|
expect(normalizeRefsField(null)).toEqual([]);
|
||||||
expect(normalizeRefsField(undefined)).toEqual([]);
|
expect(normalizeRefsField(undefined)).toEqual([]);
|
||||||
expect(normalizeRefsField(42)).toEqual([]);
|
expect(normalizeRefsField(42)).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes through string array', () => {
|
it("passes through string array", () => {
|
||||||
expect(normalizeRefsField(['a', 'b'])).toEqual(['a', 'b']);
|
expect(normalizeRefsField(["a", "b"])).toEqual(["a", "b"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('filters non-strings from mixed array', () => {
|
it("filters non-strings from mixed array", () => {
|
||||||
expect(normalizeRefsField(['a', 1, 'b', null])).toEqual(['a', 'b']);
|
expect(normalizeRefsField(["a", 1, "b", null])).toEqual(["a", "b"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty array', () => {
|
it("handles empty array", () => {
|
||||||
expect(normalizeRefsField([])).toEqual([]);
|
expect(normalizeRefsField([])).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, expect, it } from "vitest";
|
||||||
import { ok, err } from '../src/result.js';
|
import { err, ok } from "../src/result.js";
|
||||||
|
|
||||||
describe('result', () => {
|
describe("result", () => {
|
||||||
describe('ok', () => {
|
describe("ok", () => {
|
||||||
it('wraps a value', () => {
|
it("wraps a value", () => {
|
||||||
const r = ok(42);
|
const r = ok(42);
|
||||||
expect(r).toEqual({ ok: true, value: 42 });
|
expect(r).toEqual({ ok: true, value: 42 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wraps a string value', () => {
|
it("wraps a string value", () => {
|
||||||
const r = ok('hello');
|
const r = ok("hello");
|
||||||
expect(r.ok).toBe(true);
|
expect(r.ok).toBe(true);
|
||||||
if (r.ok) expect(r.value).toBe('hello');
|
if (r.ok) expect(r.value).toBe("hello");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('err', () => {
|
describe("err", () => {
|
||||||
it('wraps an error', () => {
|
it("wraps an error", () => {
|
||||||
const r = err('fail');
|
const r = err("fail");
|
||||||
expect(r).toEqual({ ok: false, error: 'fail' });
|
expect(r).toEqual({ ok: false, error: "fail" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wraps an Error object', () => {
|
it("wraps an Error object", () => {
|
||||||
const e = new Error('boom');
|
const e = new Error("boom");
|
||||||
const r = err(e);
|
const r = err(e);
|
||||||
expect(r.ok).toBe(false);
|
expect(r.ok).toBe(false);
|
||||||
if (!r.ok) expect(r.error).toBe(e);
|
if (!r.ok) expect(r.error).toBe(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('type narrowing', () => {
|
describe("type narrowing", () => {
|
||||||
it('narrows ok result', () => {
|
it("narrows ok result", () => {
|
||||||
const r = ok(10) as ReturnType<typeof ok<number>> | ReturnType<typeof err<string>>;
|
const r = ok(10) as ReturnType<typeof ok<number>> | ReturnType<typeof err<string>>;
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
expect(r.value).toBe(10);
|
expect(r.value).toBe(10);
|
||||||
@@ -39,10 +39,10 @@ describe('result', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('narrows err result', () => {
|
it("narrows err result", () => {
|
||||||
const r = err('bad') as ReturnType<typeof ok<number>> | ReturnType<typeof err<string>>;
|
const r = err("bad") as ReturnType<typeof ok<number>> | ReturnType<typeof err<string>>;
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
expect(r.error).toBe('bad');
|
expect(r.error).toBe("bad");
|
||||||
} else {
|
} else {
|
||||||
expect.unreachable();
|
expect.unreachable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { homedir } from "node:os";
|
||||||
import { homedir } from 'node:os';
|
import { describe, expect, it } from "vitest";
|
||||||
import { getDefaultStorageRoot, getDefaultWorkflowStorageRoot, getGlobalCasDir } from '../src/storage-root.js';
|
import {
|
||||||
|
getDefaultStorageRoot,
|
||||||
|
getDefaultWorkflowStorageRoot,
|
||||||
|
getGlobalCasDir,
|
||||||
|
} from "../src/storage-root.js";
|
||||||
|
|
||||||
describe('getDefaultStorageRoot', () => {
|
describe("getDefaultStorageRoot", () => {
|
||||||
it('returns homedir + /.uwf', () => {
|
it("returns homedir + /.uwf", () => {
|
||||||
expect(getDefaultStorageRoot()).toBe(homedir() + '/.uwf');
|
expect(getDefaultStorageRoot()).toBe(`${homedir()}/.uwf`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getDefaultWorkflowStorageRoot', () => {
|
describe("getDefaultWorkflowStorageRoot", () => {
|
||||||
it('returns same as getDefaultStorageRoot (deprecated alias)', () => {
|
it("returns same as getDefaultStorageRoot (deprecated alias)", () => {
|
||||||
expect(getDefaultWorkflowStorageRoot()).toBe(getDefaultStorageRoot());
|
expect(getDefaultWorkflowStorageRoot()).toBe(getDefaultStorageRoot());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getGlobalCasDir', () => {
|
describe("getGlobalCasDir", () => {
|
||||||
it('appends /cas to given storage root', () => {
|
it("appends /cas to given storage root", () => {
|
||||||
expect(getGlobalCasDir('/tmp/test')).toBe('/tmp/test/cas');
|
expect(getGlobalCasDir("/tmp/test")).toBe("/tmp/test/cas");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to default when undefined', () => {
|
it("falls back to default when undefined", () => {
|
||||||
expect(getGlobalCasDir(undefined)).toBe(homedir() + '/.uwf/cas');
|
expect(getGlobalCasDir(undefined)).toBe(`${homedir()}/.uwf/cas`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user