@plico-ai/runtime 0.0.0-reserved.0 → 0.1.0
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/NOTICE +4 -0
- package/README.md +9 -3
- package/dist/artifacts.d.ts +10 -0
- package/dist/artifacts.js +106 -0
- package/dist/engine/context.d.ts +16 -0
- package/dist/engine/context.js +77 -0
- package/dist/engine/engine.d.ts +12 -0
- package/dist/engine/engine.js +235 -0
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +19 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +5 -0
- package/dist/engine/journal.d.ts +65 -0
- package/dist/engine/journal.js +1 -0
- package/dist/engine/memory-journal.d.ts +2 -0
- package/dist/engine/memory-journal.js +125 -0
- package/dist/engine/sqlite-engine.d.ts +7 -0
- package/dist/engine/sqlite-engine.js +6 -0
- package/dist/engine/sqlite-journal.d.ts +5 -0
- package/dist/engine/sqlite-journal.js +136 -0
- package/dist/engine/types.d.ts +97 -0
- package/dist/engine/types.js +1 -0
- package/dist/env.d.ts +7 -0
- package/dist/env.js +19 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +40 -0
- package/dist/evals/discover.d.ts +3 -0
- package/dist/evals/discover.js +49 -0
- package/dist/evals/index.d.ts +3 -0
- package/dist/evals/index.js +3 -0
- package/dist/evals/promote.d.ts +7 -0
- package/dist/evals/promote.js +32 -0
- package/dist/evals/run-evals.d.ts +2 -0
- package/dist/evals/run-evals.js +69 -0
- package/dist/events.d.ts +28 -0
- package/dist/events.js +104 -0
- package/dist/guards.d.ts +1 -0
- package/dist/guards.js +3 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +17 -0
- package/dist/instructions.d.ts +4 -0
- package/dist/instructions.js +23 -0
- package/dist/policy/approval.d.ts +8 -0
- package/dist/policy/approval.js +27 -0
- package/dist/providers/ai-sdk.d.ts +10 -0
- package/dist/providers/ai-sdk.js +154 -0
- package/dist/providers/configured.d.ts +17 -0
- package/dist/providers/configured.js +60 -0
- package/dist/providers/openai-compatible.d.ts +8 -0
- package/dist/providers/openai-compatible.js +14 -0
- package/dist/providers/project-module.d.ts +7 -0
- package/dist/providers/project-module.js +29 -0
- package/dist/providers/registry.d.ts +20 -0
- package/dist/providers/registry.js +53 -0
- package/dist/readiness/check-readiness.d.ts +2 -0
- package/dist/readiness/check-readiness.js +33 -0
- package/dist/readiness/checks/approval-policy.d.ts +5 -0
- package/dist/readiness/checks/approval-policy.js +19 -0
- package/dist/readiness/checks/artifact-access.d.ts +3 -0
- package/dist/readiness/checks/artifact-access.js +5 -0
- package/dist/readiness/checks/artifacts.d.ts +6 -0
- package/dist/readiness/checks/artifacts.js +22 -0
- package/dist/readiness/checks/env.d.ts +3 -0
- package/dist/readiness/checks/env.js +10 -0
- package/dist/readiness/checks/evals.d.ts +2 -0
- package/dist/readiness/checks/evals.js +53 -0
- package/dist/readiness/checks/persistence.d.ts +3 -0
- package/dist/readiness/checks/persistence.js +61 -0
- package/dist/readiness/checks/project.d.ts +7 -0
- package/dist/readiness/checks/project.js +21 -0
- package/dist/readiness/checks/provider.d.ts +2 -0
- package/dist/readiness/checks/provider.js +20 -0
- package/dist/readiness/checks/server-config.d.ts +2 -0
- package/dist/readiness/checks/server-config.js +13 -0
- package/dist/readiness/checks/studio.d.ts +2 -0
- package/dist/readiness/checks/studio.js +16 -0
- package/dist/readiness/checks/thread-store.d.ts +3 -0
- package/dist/readiness/checks/thread-store.js +12 -0
- package/dist/readiness/checks/tools.d.ts +3 -0
- package/dist/readiness/checks/tools.js +42 -0
- package/dist/readiness/diagnostic.d.ts +2 -0
- package/dist/readiness/diagnostic.js +7 -0
- package/dist/readiness/index.d.ts +1 -0
- package/dist/readiness/index.js +1 -0
- package/dist/readiness/open-database.d.ts +8 -0
- package/dist/readiness/open-database.js +15 -0
- package/dist/run/artifact-tool-context.d.ts +19 -0
- package/dist/run/artifact-tool-context.js +74 -0
- package/dist/run/finalize.d.ts +16 -0
- package/dist/run/finalize.js +11 -0
- package/dist/run/index.d.ts +1 -0
- package/dist/run/index.js +1 -0
- package/dist/run/pinned-instructions.d.ts +18 -0
- package/dist/run/pinned-instructions.js +36 -0
- package/dist/run/record-tools.d.ts +8 -0
- package/dist/run/record-tools.js +21 -0
- package/dist/run/run-project.d.ts +17 -0
- package/dist/run/run-project.js +222 -0
- package/dist/run/strategies/approval.d.ts +9 -0
- package/dist/run/strategies/approval.js +42 -0
- package/dist/run/strategies/default-dry.d.ts +3 -0
- package/dist/run/strategies/default-dry.js +5 -0
- package/dist/run/strategies/provider.d.ts +6 -0
- package/dist/run/strategies/provider.js +193 -0
- package/dist/run/strategies/scripted.d.ts +5 -0
- package/dist/run/strategies/scripted.js +106 -0
- package/dist/run/tool-fingerprint.d.ts +2 -0
- package/dist/run/tool-fingerprint.js +26 -0
- package/dist/run/tool-step.d.ts +4 -0
- package/dist/run/tool-step.js +62 -0
- package/dist/run/tracing.d.ts +12 -0
- package/dist/run/tracing.js +92 -0
- package/dist/script.d.ts +4 -0
- package/dist/script.js +51 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/metadata.d.ts +5 -0
- package/dist/storage/metadata.js +26 -0
- package/dist/storage/sqlite/append-event.d.ts +3 -0
- package/dist/storage/sqlite/append-event.js +108 -0
- package/dist/storage/sqlite/driver.d.ts +9 -0
- package/dist/storage/sqlite/driver.js +13 -0
- package/dist/storage/sqlite/index.d.ts +1 -0
- package/dist/storage/sqlite/index.js +1 -0
- package/dist/storage/sqlite/open.d.ts +3 -0
- package/dist/storage/sqlite/open.js +54 -0
- package/dist/storage/sqlite/owner-lock.d.ts +5 -0
- package/dist/storage/sqlite/owner-lock.js +55 -0
- package/dist/storage/sqlite/project-thread.d.ts +4 -0
- package/dist/storage/sqlite/project-thread.js +33 -0
- package/dist/storage/sqlite/queries.d.ts +9 -0
- package/dist/storage/sqlite/queries.js +134 -0
- package/dist/storage/sqlite/recover-interrupted.d.ts +2 -0
- package/dist/storage/sqlite/recover-interrupted.js +46 -0
- package/dist/storage/sqlite/schema.d.ts +10 -0
- package/dist/storage/sqlite/schema.js +205 -0
- package/dist/storage/sqlite/store.d.ts +2 -0
- package/dist/storage/sqlite/store.js +51 -0
- package/dist/storage/sqlite/thread-log.d.ts +4 -0
- package/dist/storage/sqlite/thread-log.js +9 -0
- package/dist/storage/sqlite/thread-title.d.ts +24 -0
- package/dist/storage/sqlite/thread-title.js +85 -0
- package/dist/storage/sqlite/threads.d.ts +47 -0
- package/dist/storage/sqlite/threads.js +156 -0
- package/dist/threads/stopwords.d.ts +1 -0
- package/dist/threads/stopwords.js +16 -0
- package/dist/threads/suggest-title.d.ts +9 -0
- package/dist/threads/suggest-title.js +102 -0
- package/dist/threads/titler.d.ts +10 -0
- package/dist/threads/titler.js +63 -0
- package/dist/threads/words.d.ts +12 -0
- package/dist/threads/words.js +39 -0
- package/dist/tools/artifact-model-tool-operations.d.ts +29 -0
- package/dist/tools/artifact-model-tool-operations.js +124 -0
- package/dist/tools/artifact-model-tools.d.ts +2 -0
- package/dist/tools/artifact-model-tools.js +101 -0
- package/dist/tools/discover.d.ts +4 -0
- package/dist/tools/discover.js +34 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/load.d.ts +2 -0
- package/dist/tools/load.js +83 -0
- package/dist/tools/normalize.d.ts +2 -0
- package/dist/tools/normalize.js +48 -0
- package/dist/tools/schema.d.ts +7 -0
- package/dist/tools/schema.js +101 -0
- package/dist/tools/skill-model-tool.d.ts +7 -0
- package/dist/tools/skill-model-tool.js +38 -0
- package/dist/types/artifacts.d.ts +5 -0
- package/dist/types/artifacts.js +1 -0
- package/dist/types/evals.d.ts +16 -0
- package/dist/types/evals.js +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.js +1 -0
- package/dist/types/provider.d.ts +45 -0
- package/dist/types/provider.js +1 -0
- package/dist/types/readiness.d.ts +9 -0
- package/dist/types/readiness.js +1 -0
- package/dist/types/run.d.ts +28 -0
- package/dist/types/run.js +1 -0
- package/dist/types/script.d.ts +16 -0
- package/dist/types/script.js +1 -0
- package/dist/types/store.d.ts +24 -0
- package/dist/types/store.js +1 -0
- package/dist/types/tools.d.ts +15 -0
- package/dist/types/tools.js +1 -0
- package/package.json +67 -8
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { checkArtifactAccess } from "./checks/artifact-access.js";
|
|
3
|
+
import { checkArtifacts } from "./checks/artifacts.js";
|
|
4
|
+
import { checkEnv } from "./checks/env.js";
|
|
5
|
+
import { checkEvals } from "./checks/evals.js";
|
|
6
|
+
import { checkPersistence } from "./checks/persistence.js";
|
|
7
|
+
import { checkProject } from "./checks/project.js";
|
|
8
|
+
import { checkProvider } from "./checks/provider.js";
|
|
9
|
+
import { checkServerConfig } from "./checks/server-config.js";
|
|
10
|
+
import { checkStudio } from "./checks/studio.js";
|
|
11
|
+
import { checkThreadStore } from "./checks/thread-store.js";
|
|
12
|
+
import { checkTools } from "./checks/tools.js";
|
|
13
|
+
import { openDatabase } from "./open-database.js";
|
|
14
|
+
export async function checkReadiness(projectRoot, options = {}) {
|
|
15
|
+
const projectCheck = await checkProject(projectRoot);
|
|
16
|
+
const diagnostics = [...projectCheck.diagnostics];
|
|
17
|
+
if (!projectCheck.project) {
|
|
18
|
+
return summarize(projectRoot, diagnostics);
|
|
19
|
+
}
|
|
20
|
+
const databasePath = options.databasePath ?? join(projectRoot, ".plico", "plico.sqlite");
|
|
21
|
+
const database = openDatabase(databasePath, options.eventStore);
|
|
22
|
+
diagnostics.push(...checkEnv(projectRoot), ...checkProvider(projectCheck.project.config.model), checkArtifactAccess(projectCheck.project.config), ...(await checkTools(projectCheck.project)), ...(await checkEvals(projectRoot, database.eventStore)), ...checkThreadStore(database.eventStore, options.threadStore), ...(await checkPersistence(databasePath, database)), ...(await checkArtifacts(projectRoot)), ...(await checkServerConfig(projectRoot)), ...(await checkStudio(options.studioPath)));
|
|
23
|
+
return summarize(projectRoot, diagnostics);
|
|
24
|
+
}
|
|
25
|
+
function summarize(projectRoot, diagnostics) {
|
|
26
|
+
const count = (severity) => diagnostics.filter((d) => d.severity === severity).length;
|
|
27
|
+
return {
|
|
28
|
+
projectRoot,
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
diagnostics,
|
|
31
|
+
summary: { ok: count("ok"), warning: count("warning"), error: count("error") },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LoadedProject } from "@plico-ai/core";
|
|
2
|
+
import type { DeclaredTool, ReadinessDiagnostic } from "../../types/index.js";
|
|
3
|
+
export declare function checkApprovalPolicy(project: LoadedProject, tools: DeclaredTool[], options?: {
|
|
4
|
+
toolsComplete?: boolean;
|
|
5
|
+
}): ReadinessDiagnostic[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
2
|
+
export function checkApprovalPolicy(project, tools, options = {}) {
|
|
3
|
+
const toolsComplete = options.toolsComplete ?? true;
|
|
4
|
+
const names = new Set(tools.map((tool) => tool.name));
|
|
5
|
+
const diagnostics = toolsComplete
|
|
6
|
+
? Object.keys(project.config.approvals?.tools ?? {})
|
|
7
|
+
.filter((name) => !names.has(name))
|
|
8
|
+
.map((name) => makeDiagnostic("warning", "approvals.policy", `Approval policy refers to undeclared tool "${name}".`, "plico.config.ts"))
|
|
9
|
+
: [];
|
|
10
|
+
for (const tool of tools) {
|
|
11
|
+
if (tool.approval.required && project.config.approvals?.tools?.[tool.name] === "none") {
|
|
12
|
+
diagnostics.push(makeDiagnostic("warning", "approvals.policy", `Tool "${tool.name}" requires approval in its declaration; the "none" override has no effect.`));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (diagnostics.length === 0 && toolsComplete) {
|
|
16
|
+
diagnostics.push(makeDiagnostic("ok", "approvals.policy", `Approval policy checked against ${tools.length} declared tool(s).`));
|
|
17
|
+
}
|
|
18
|
+
return diagnostics;
|
|
19
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
2
|
+
export function checkArtifactAccess(config) {
|
|
3
|
+
const access = config.artifacts?.access ?? "read-write";
|
|
4
|
+
return makeDiagnostic("ok", "artifacts.access", `Model-facing artifact tools access is "${access}".`, "plico.config.ts");
|
|
5
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReadinessDiagnostic } from "../../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Files a run writes are events in the log (ADR 0002), not files in `artifacts/`, so the folder
|
|
4
|
+
* is only part of the project layout. Its writability says nothing about artifacts and is not checked.
|
|
5
|
+
*/
|
|
6
|
+
export declare function checkArtifacts(projectRoot: string): Promise<ReadinessDiagnostic[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { getProjectPaths, getProjectRelativePaths } from "@plico-ai/core";
|
|
3
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
4
|
+
/**
|
|
5
|
+
* Files a run writes are events in the log (ADR 0002), not files in `artifacts/`, so the folder
|
|
6
|
+
* is only part of the project layout. Its writability says nothing about artifacts and is not checked.
|
|
7
|
+
*/
|
|
8
|
+
export async function checkArtifacts(projectRoot) {
|
|
9
|
+
const directory = getProjectPaths(projectRoot).directories.artifacts;
|
|
10
|
+
const folder = `${getProjectRelativePaths().directories.artifacts}/`;
|
|
11
|
+
try {
|
|
12
|
+
await access(directory);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return [
|
|
16
|
+
makeDiagnostic("warning", "artifacts.workspace", `${folder} directory not found. Create it to keep the project layout complete.`),
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
return [
|
|
20
|
+
makeDiagnostic("ok", "artifacts.workspace", "Artifacts a run writes are stored in the event log, not in this folder", folder),
|
|
21
|
+
];
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { loadedEnvFiles } from "../../env.js";
|
|
3
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
4
|
+
/** Says which `.env` file was loaded. It never reads or prints the values. */
|
|
5
|
+
export function checkEnv(projectRoot) {
|
|
6
|
+
const file = join(projectRoot, ".env");
|
|
7
|
+
if (!loadedEnvFiles().includes(file))
|
|
8
|
+
return [];
|
|
9
|
+
return [makeDiagnostic("ok", "env.loaded", "Environment variables loaded from .env.", ".env")];
|
|
10
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { getProjectPaths, getProjectRelativePaths } from "@plico-ai/core";
|
|
3
|
+
import { digestEvalFixtures, discoverEvalCases } from "../../evals/discover.js";
|
|
4
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
5
|
+
export async function checkEvals(projectRoot, eventStore) {
|
|
6
|
+
const evalsDir = getProjectPaths(projectRoot).directories.evals;
|
|
7
|
+
const folder = `${getProjectRelativePaths().directories.evals}/`;
|
|
8
|
+
try {
|
|
9
|
+
await access(evalsDir);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return [
|
|
13
|
+
makeDiagnostic("warning", "evals.fixtures", `${folder} directory not found. Create ${folder} to add eval fixtures.`),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
let cases;
|
|
17
|
+
try {
|
|
18
|
+
cases = await discoverEvalCases(evalsDir);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return [makeDiagnostic("error", "evals.fixtures", error.message)];
|
|
22
|
+
}
|
|
23
|
+
const fixtures = cases.length === 0
|
|
24
|
+
? makeDiagnostic("warning", "evals.fixtures", "No .script.json eval fixtures found. Add evals to track behavior.")
|
|
25
|
+
: makeDiagnostic("ok", "evals.fixtures", `${cases.length} eval fixture(s) found`);
|
|
26
|
+
return eventStore
|
|
27
|
+
? [fixtures, await readEvalResults(eventStore, await digestEvalFixtures(evalsDir))]
|
|
28
|
+
: [fixtures];
|
|
29
|
+
}
|
|
30
|
+
async function readEvalResults(eventStore, fixturesDigest) {
|
|
31
|
+
try {
|
|
32
|
+
return describeEvalResults(await eventStore.getLatestEvalReport(), fixturesDigest);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return makeDiagnostic("error", "evals.results", `Could not read eval results: ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function describeEvalResults(report, fixturesDigest) {
|
|
39
|
+
if (!report) {
|
|
40
|
+
return makeDiagnostic("warning", "evals.results", "No eval runs have been executed. Run plico eval to see results.");
|
|
41
|
+
}
|
|
42
|
+
if (report.fixturesDigest !== fixturesDigest) {
|
|
43
|
+
return makeDiagnostic("warning", "evals.results", "Eval fixtures changed since the latest run. Run plico eval to refresh results.");
|
|
44
|
+
}
|
|
45
|
+
if (report.total === 0) {
|
|
46
|
+
return makeDiagnostic("warning", "evals.results", "The latest eval run found no fixtures.");
|
|
47
|
+
}
|
|
48
|
+
if (report.failed > 0) {
|
|
49
|
+
const names = report.results.filter((r) => r.status === "fail").map((r) => r.name);
|
|
50
|
+
return makeDiagnostic("error", "evals.results", `${report.failed}/${report.total} eval(s) failed in the latest run: ${names.join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
return makeDiagnostic("ok", "evals.results", `${report.passed}/${report.total} eval(s) passed (${report.evaluatedAt})`);
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { access, constants } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
4
|
+
export async function checkPersistence(dbPath, database) {
|
|
5
|
+
if (database.openError !== undefined) {
|
|
6
|
+
return [cannotRead(database.openError, dbPath)];
|
|
7
|
+
}
|
|
8
|
+
if (database.eventStore) {
|
|
9
|
+
try {
|
|
10
|
+
await database.eventStore.listRuns();
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return [cannotRead(error.message, dbPath)];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// An attached store is already open for writing, so only its reads need proving.
|
|
17
|
+
if (database.attached) {
|
|
18
|
+
return [makeDiagnostic("ok", "persistence.storage", "Event store is attached")];
|
|
19
|
+
}
|
|
20
|
+
if (database.eventStore) {
|
|
21
|
+
const writable = (await exists(dbPath, constants.W_OK)) && (await exists(dirname(dbPath), constants.W_OK));
|
|
22
|
+
return writable
|
|
23
|
+
? [makeDiagnostic("ok", "persistence.storage", "Database is writable", dbPath)]
|
|
24
|
+
: [makeDiagnostic("error", "persistence.storage", "Database is not writable", dbPath)];
|
|
25
|
+
}
|
|
26
|
+
// Read-only probe: the database can be created later if the nearest existing
|
|
27
|
+
// ancestor directory is writable. Readiness must never touch the filesystem.
|
|
28
|
+
if (await isCreatable(dbPath)) {
|
|
29
|
+
return [
|
|
30
|
+
makeDiagnostic("warning", "persistence.storage", "Database file does not exist yet but its location is writable. Run plico run --dry --persist to initialize.", dbPath),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
return [
|
|
34
|
+
makeDiagnostic("error", "persistence.storage", "Persistence path is not writable", dbPath),
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
function cannotRead(reason, dbPath) {
|
|
38
|
+
return makeDiagnostic("error", "persistence.storage", `Database cannot be read: ${reason}`, dbPath);
|
|
39
|
+
}
|
|
40
|
+
async function isCreatable(path) {
|
|
41
|
+
let current = dirname(path);
|
|
42
|
+
while (true) {
|
|
43
|
+
if (await exists(current, constants.W_OK))
|
|
44
|
+
return true;
|
|
45
|
+
if (await exists(current, constants.F_OK))
|
|
46
|
+
return false;
|
|
47
|
+
const parent = dirname(current);
|
|
48
|
+
if (parent === current)
|
|
49
|
+
return false;
|
|
50
|
+
current = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function exists(path, mode) {
|
|
54
|
+
try {
|
|
55
|
+
await access(path, mode);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type LoadedProject } from "@plico-ai/core";
|
|
2
|
+
import type { ReadinessDiagnostic } from "../../types/index.js";
|
|
3
|
+
export interface ProjectCheck {
|
|
4
|
+
diagnostics: ReadinessDiagnostic[];
|
|
5
|
+
project: LoadedProject | undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare function checkProject(projectRoot: string): Promise<ProjectCheck>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { validateProject } from "@plico-ai/core";
|
|
2
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
3
|
+
export async function checkProject(projectRoot) {
|
|
4
|
+
const diagnostics = [];
|
|
5
|
+
const validation = await validateProject(projectRoot);
|
|
6
|
+
if (!validation.ok || !validation.project) {
|
|
7
|
+
for (const issue of validation.errors ?? []) {
|
|
8
|
+
const subsystem = issue.path.startsWith("tools/") ? "tools.discovery" : "project.structure";
|
|
9
|
+
diagnostics.push(makeDiagnostic("error", subsystem, issue.message, issue.path));
|
|
10
|
+
}
|
|
11
|
+
for (const issue of validation.warnings ?? []) {
|
|
12
|
+
diagnostics.push(makeDiagnostic("warning", "project.structure", issue.message, issue.path));
|
|
13
|
+
}
|
|
14
|
+
return { diagnostics, project: validation.project };
|
|
15
|
+
}
|
|
16
|
+
diagnostics.push(makeDiagnostic("ok", "project.structure", "Project is valid", "plico.config.ts"));
|
|
17
|
+
for (const issue of validation.warnings ?? []) {
|
|
18
|
+
diagnostics.push(makeDiagnostic("warning", "project.structure", issue.message, issue.path));
|
|
19
|
+
}
|
|
20
|
+
return { diagnostics, project: validation.project };
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { resolveModelEndpoint } from "../../providers/registry.js";
|
|
2
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
3
|
+
export function checkProvider(model) {
|
|
4
|
+
if (!model)
|
|
5
|
+
return [
|
|
6
|
+
makeDiagnostic("warning", "provider.config", "No model configured. Add model settings for real provider runs.", "plico.config.ts"),
|
|
7
|
+
];
|
|
8
|
+
const { apiKeyEnv } = resolveModelEndpoint(model);
|
|
9
|
+
if (!apiKeyEnv)
|
|
10
|
+
return [
|
|
11
|
+
makeDiagnostic("ok", "provider.credentials", "The project supplies its own provider credentials."),
|
|
12
|
+
];
|
|
13
|
+
if (!process.env[apiKeyEnv])
|
|
14
|
+
return [
|
|
15
|
+
makeDiagnostic("warning", "provider.credentials", `Set ${apiKeyEnv} to run the configured model.`),
|
|
16
|
+
];
|
|
17
|
+
return [
|
|
18
|
+
makeDiagnostic("ok", "provider.credentials", "Model provider credentials are configured."),
|
|
19
|
+
];
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { loadProject } from "@plico-ai/core";
|
|
2
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
3
|
+
export async function checkServerConfig(projectRoot) {
|
|
4
|
+
try {
|
|
5
|
+
await loadProject(projectRoot);
|
|
6
|
+
return [makeDiagnostic("ok", "server.config", "Project config loads successfully for server")];
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return [
|
|
10
|
+
makeDiagnostic("warning", "server.config", "plico.config.ts could not be loaded. Server may not start."),
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
4
|
+
export async function checkStudio(studioPath) {
|
|
5
|
+
if (studioPath === undefined)
|
|
6
|
+
return [];
|
|
7
|
+
try {
|
|
8
|
+
await access(join(studioPath, "index.html"));
|
|
9
|
+
return [makeDiagnostic("ok", "studio.assets", "Studio assets are built", studioPath)];
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return [
|
|
13
|
+
makeDiagnostic("warning", "studio.assets", "Studio assets not found. Build them with: pnpm --filter @plico-ai/studio build", studioPath),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ThreadStore } from "../../storage/sqlite/threads.js";
|
|
2
|
+
import type { EventStore, ReadinessDiagnostic } from "../../types/index.js";
|
|
3
|
+
export declare function checkThreadStore(eventStore: EventStore | undefined, threadStore: Pick<ThreadStore, "storageIdentity"> | undefined): ReadinessDiagnostic[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
2
|
+
export function checkThreadStore(eventStore, threadStore) {
|
|
3
|
+
if (!threadStore)
|
|
4
|
+
return [];
|
|
5
|
+
const sharesIdentity = threadStore.storageIdentity !== undefined &&
|
|
6
|
+
threadStore.storageIdentity === eventStore?.storageIdentity;
|
|
7
|
+
return sharesIdentity
|
|
8
|
+
? [makeDiagnostic("ok", "threads.store", "Thread and run stores share the same database.")]
|
|
9
|
+
: [
|
|
10
|
+
makeDiagnostic("error", "threads.store", "Thread and run stores must use the same database. Thread runs cannot start."),
|
|
11
|
+
];
|
|
12
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { getProjectPaths, getProjectRelativePaths } from "@plico-ai/core";
|
|
3
|
+
import { discoverToolFiles, loadToolFile } from "../../tools/index.js";
|
|
4
|
+
import { makeDiagnostic } from "../diagnostic.js";
|
|
5
|
+
import { checkApprovalPolicy } from "./approval-policy.js";
|
|
6
|
+
export async function checkTools(project) {
|
|
7
|
+
const diagnostics = [];
|
|
8
|
+
const projectPaths = getProjectPaths(project.root);
|
|
9
|
+
const relativePaths = getProjectRelativePaths();
|
|
10
|
+
let toolFiles;
|
|
11
|
+
try {
|
|
12
|
+
toolFiles = await discoverToolFiles(project);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code !== "ENOENT")
|
|
16
|
+
throw error;
|
|
17
|
+
return [
|
|
18
|
+
makeDiagnostic("error", "tools.discovery", "tools/ directory not found. Create tools/ to declare tools.", relativePaths.directories.tools),
|
|
19
|
+
...checkApprovalPolicy(project, [], { toolsComplete: false }),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
if (toolFiles.length === 0) {
|
|
23
|
+
diagnostics.push(makeDiagnostic("warning", "tools.discovery", "No tools declared. Add a .tool.ts module to get started."));
|
|
24
|
+
diagnostics.push(...checkApprovalPolicy(project, []));
|
|
25
|
+
return diagnostics;
|
|
26
|
+
}
|
|
27
|
+
let validCount = 0;
|
|
28
|
+
const tools = [];
|
|
29
|
+
for (const entry of toolFiles) {
|
|
30
|
+
const source = `${relativePaths.directories.tools}/${entry}`;
|
|
31
|
+
try {
|
|
32
|
+
tools.push(await loadToolFile(join(projectPaths.directories.tools, entry), source));
|
|
33
|
+
validCount++;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
diagnostics.push(makeDiagnostic("error", "tools.discovery", `Schema validation failed for ${entry}: ${String(error)}`, source));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
diagnostics.push(makeDiagnostic(validCount === toolFiles.length ? "ok" : "warning", "tools.discovery", `${validCount}/${toolFiles.length} tool(s) valid`));
|
|
40
|
+
diagnostics.push(...checkApprovalPolicy(project, tools, { toolsComplete: validCount === toolFiles.length }));
|
|
41
|
+
return diagnostics;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { checkReadiness } from "./check-readiness.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { checkReadiness } from "./check-readiness.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EventStore } from "../types/index.js";
|
|
2
|
+
export interface ReadinessDatabase {
|
|
3
|
+
eventStore?: EventStore;
|
|
4
|
+
/** True when the caller supplied the store, rather than readiness opening one. */
|
|
5
|
+
attached: boolean;
|
|
6
|
+
openError?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function openDatabase(databasePath: string, eventStore: EventStore | undefined): ReadinessDatabase;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createSqliteEventStore } from "../storage/index.js";
|
|
3
|
+
export function openDatabase(databasePath, eventStore) {
|
|
4
|
+
if (eventStore)
|
|
5
|
+
return { eventStore, attached: true };
|
|
6
|
+
if (!existsSync(databasePath))
|
|
7
|
+
return { attached: false };
|
|
8
|
+
try {
|
|
9
|
+
const store = createSqliteEventStore({ databasePath, readOnly: true });
|
|
10
|
+
return { eventStore: store, attached: false };
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return { attached: false, openError: error.message };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ArtifactContent, PlicoEvent, PlicoEventPayloads } from "@plico-ai/contracts";
|
|
2
|
+
import type { ToolArtifactContext } from "../types/index.js";
|
|
3
|
+
export interface StagedArtifactWrite {
|
|
4
|
+
path: string;
|
|
5
|
+
content: ArtifactContent;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
contentHash: string;
|
|
8
|
+
version: number;
|
|
9
|
+
}
|
|
10
|
+
interface StagedArtifactContext {
|
|
11
|
+
context: ToolArtifactContext;
|
|
12
|
+
writes(): StagedArtifactWrite[];
|
|
13
|
+
workspaceCreated(): boolean;
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function createStagedArtifactContext(runId: string, threadId: string | undefined, history: PlicoEvent[]): StagedArtifactContext;
|
|
17
|
+
export declare function validateArtifactPath(path: string): void;
|
|
18
|
+
export declare function artifactWriteEventPayload(write: StagedArtifactWrite): PlicoEventPayloads["artifact.file.written"];
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { artifactFileMetadata, buildArtifactWorkspace, decodeArtifactContent, encodeArtifactContent, hashArtifactContent, } from "../artifacts.js";
|
|
2
|
+
const maxArtifactContentBytes = 1024 * 1024;
|
|
3
|
+
const maxArtifactPathLength = 1024;
|
|
4
|
+
export function createStagedArtifactContext(runId, threadId, history) {
|
|
5
|
+
const workspaceId = threadId ? `${threadId}:workspace` : `${runId}:workspace`;
|
|
6
|
+
const initialWorkspace = buildArtifactWorkspace(workspaceId, history);
|
|
7
|
+
const files = new Map((initialWorkspace?.files ?? []).map((file) => [file.path, file]));
|
|
8
|
+
const writesList = [];
|
|
9
|
+
const context = {
|
|
10
|
+
async read(path) {
|
|
11
|
+
const file = files.get(path);
|
|
12
|
+
if (!file)
|
|
13
|
+
return undefined;
|
|
14
|
+
return { ...file, content: decodeArtifactContent(file.content) };
|
|
15
|
+
},
|
|
16
|
+
async write(path, content, options) {
|
|
17
|
+
validateArtifactPath(path);
|
|
18
|
+
validateArtifactContent(content);
|
|
19
|
+
const encoded = encodeArtifactContent(content);
|
|
20
|
+
const file = {
|
|
21
|
+
path,
|
|
22
|
+
content: encoded,
|
|
23
|
+
mimeType: options?.mimeType ??
|
|
24
|
+
(typeof content === "string" ? "text/plain" : "application/octet-stream"),
|
|
25
|
+
version: (files.get(path)?.version ?? 0) + 1,
|
|
26
|
+
contentHash: hashArtifactContent(encoded),
|
|
27
|
+
};
|
|
28
|
+
files.set(path, file);
|
|
29
|
+
writesList.push({
|
|
30
|
+
path: file.path,
|
|
31
|
+
content: file.content,
|
|
32
|
+
mimeType: file.mimeType,
|
|
33
|
+
contentHash: file.contentHash,
|
|
34
|
+
version: file.version,
|
|
35
|
+
});
|
|
36
|
+
return { path, version: file.version };
|
|
37
|
+
},
|
|
38
|
+
async list() {
|
|
39
|
+
return [...files.values()].map(artifactFileMetadata);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
context,
|
|
44
|
+
writes: () => [...writesList],
|
|
45
|
+
workspaceCreated: () => initialWorkspace === undefined && writesList.length > 0,
|
|
46
|
+
workspaceId,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function validateArtifactPath(path) {
|
|
50
|
+
const segments = path.split(/[\\/]/);
|
|
51
|
+
if (path.length === 0 ||
|
|
52
|
+
path.length > maxArtifactPathLength ||
|
|
53
|
+
path.startsWith("/") ||
|
|
54
|
+
path.startsWith("\\") ||
|
|
55
|
+
/^[a-zA-Z]:/.test(path) ||
|
|
56
|
+
segments.some((segment) => segment === ".." || segment === "")) {
|
|
57
|
+
throw new Error(`Invalid artifact path: expected a non-empty relative path without '..' and at most ${maxArtifactPathLength} characters.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function validateArtifactContent(content) {
|
|
61
|
+
const sizeBytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : content.byteLength;
|
|
62
|
+
if (sizeBytes > maxArtifactContentBytes)
|
|
63
|
+
throw new Error("Artifact content exceeds the 1 MiB write limit.");
|
|
64
|
+
}
|
|
65
|
+
export function artifactWriteEventPayload(write) {
|
|
66
|
+
const content = write.content;
|
|
67
|
+
return {
|
|
68
|
+
path: write.path,
|
|
69
|
+
content,
|
|
70
|
+
mimeType: write.mimeType,
|
|
71
|
+
contentHash: write.contentHash,
|
|
72
|
+
version: write.version,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PlicoEvent, RunResult, RunStatus } from "@plico-ai/contracts";
|
|
2
|
+
import type { ValidationIssue } from "@plico-ai/core";
|
|
3
|
+
import type { EventStore } from "../types/index.js";
|
|
4
|
+
export interface FinalizeRunInput {
|
|
5
|
+
runId: string;
|
|
6
|
+
projectRoot: string;
|
|
7
|
+
projectName: string;
|
|
8
|
+
status: RunStatus;
|
|
9
|
+
startedAt: string;
|
|
10
|
+
events: PlicoEvent[];
|
|
11
|
+
output?: string | undefined;
|
|
12
|
+
errors?: ValidationIssue[] | undefined;
|
|
13
|
+
eventStore?: EventStore | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** Builds the result after the run has stopped or finished. */
|
|
16
|
+
export declare function finalizeRun(input: FinalizeRunInput): Promise<RunResult>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Builds the result after the run has stopped or finished. */
|
|
2
|
+
export async function finalizeRun(input) {
|
|
3
|
+
const result = {
|
|
4
|
+
runId: input.runId,
|
|
5
|
+
status: input.status,
|
|
6
|
+
...(input.output === undefined ? {} : { output: input.output }),
|
|
7
|
+
events: input.events,
|
|
8
|
+
...(input.errors === undefined ? {} : { errors: input.errors }),
|
|
9
|
+
};
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { recoverRuns, runProject } from "./run-project.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { recoverRuns, runProject } from "./run-project.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ValidationIssue } from "@plico-ai/contracts";
|
|
2
|
+
import { type LoadedProject, type Skill } from "@plico-ai/core";
|
|
3
|
+
import type { InMemoryEventLog } from "../events.js";
|
|
4
|
+
/**
|
|
5
|
+
* Records the instructions and skills a run starts with. A run that resumes reuses what it
|
|
6
|
+
* recorded, so editing `agent.md` or a skill while it waits does not change its behaviour.
|
|
7
|
+
*/
|
|
8
|
+
export declare function pinInstructions(project: LoadedProject, events: InMemoryEventLog): Promise<{
|
|
9
|
+
instructions: {
|
|
10
|
+
content: string;
|
|
11
|
+
};
|
|
12
|
+
skills: Skill[];
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* A run that already recorded its instructions and skills does not read them again, so an
|
|
16
|
+
* `agent.md` or skill that is invalid now must not stop it from resuming.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ignoredWhenPinned(issue: ValidationIssue): boolean;
|