voratiq 0.1.0-beta.1 → 0.1.0-beta.2
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/dist/cli/errors.d.ts +3 -0
- package/dist/cli/errors.js +6 -0
- package/dist/cli/init.js +2 -1
- package/dist/cli/prune.js +2 -2
- package/dist/commands/apply/errors.js +1 -1
- package/dist/commands/prune/command.js +6 -5
- package/dist/commands/prune/errors.d.ts +0 -3
- package/dist/commands/prune/errors.js +1 -7
- package/dist/configs/agents/defaults.d.ts +1 -0
- package/dist/configs/agents/defaults.js +5 -1
- package/dist/configs/agents/errors.d.ts +9 -0
- package/dist/configs/agents/errors.js +21 -0
- package/dist/configs/agents/loader.d.ts +2 -1
- package/dist/configs/agents/loader.js +23 -6
- package/dist/configs/sandbox/defaults.js +1 -1
- package/dist/records/persistence.d.ts +1 -1
- package/dist/records/persistence.js +37 -27
- package/dist/utils/git.d.ts +1 -0
- package/dist/utils/git.js +18 -8
- package/dist/workspace/agents.js +1 -0
- package/dist/workspace/layout.js +3 -1
- package/dist/workspace/setup.js +10 -2
- package/dist/workspace/structure.d.ts +1 -0
- package/dist/workspace/structure.js +3 -2
- package/package.json +1 -1
package/dist/cli/errors.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ import { HintedError } from "../utils/errors.js";
|
|
|
2
2
|
export declare class CliError extends HintedError {
|
|
3
3
|
constructor(headline: string, detailLines?: readonly string[], hintLines?: readonly string[]);
|
|
4
4
|
}
|
|
5
|
+
export declare class NonInteractiveShellError extends CliError {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
5
8
|
export declare function toCliError(error: unknown): CliError;
|
package/dist/cli/errors.js
CHANGED
|
@@ -5,6 +5,12 @@ export class CliError extends HintedError {
|
|
|
5
5
|
this.name = "CliError";
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
+
export class NonInteractiveShellError extends CliError {
|
|
9
|
+
constructor() {
|
|
10
|
+
super("Non-interactive shell detected; re-run with --yes to accept defaults.");
|
|
11
|
+
this.name = "NonInteractiveShellError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
8
14
|
export function toCliError(error) {
|
|
9
15
|
if (error instanceof CliError) {
|
|
10
16
|
return error;
|
package/dist/cli/init.js
CHANGED
|
@@ -3,6 +3,7 @@ import { executeInitCommand } from "../commands/init/command.js";
|
|
|
3
3
|
import { resolveCliContext } from "../preflight/index.js";
|
|
4
4
|
import { renderInitTranscript } from "../render/transcripts/init.js";
|
|
5
5
|
import { createConfirmationWorkflow } from "./confirmation.js";
|
|
6
|
+
import { NonInteractiveShellError } from "./errors.js";
|
|
6
7
|
import { writeCommandOutput } from "./output.js";
|
|
7
8
|
export async function runInitCommand(options = {}) {
|
|
8
9
|
const { root } = await resolveCliContext({ requireWorkspace: false });
|
|
@@ -10,7 +11,7 @@ export async function runInitCommand(options = {}) {
|
|
|
10
11
|
const confirmation = createConfirmationWorkflow({
|
|
11
12
|
assumeYes,
|
|
12
13
|
onUnavailable: () => {
|
|
13
|
-
throw new
|
|
14
|
+
throw new NonInteractiveShellError();
|
|
14
15
|
},
|
|
15
16
|
});
|
|
16
17
|
try {
|
package/dist/cli/prune.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { executePruneCommand } from "../commands/prune/command.js";
|
|
3
|
-
import { InteractiveConfirmationRequiredError } from "../commands/prune/errors.js";
|
|
4
3
|
import { resolveCliContext } from "../preflight/index.js";
|
|
5
4
|
import { renderPruneTranscript } from "../render/transcripts/prune.js";
|
|
6
5
|
import { createConfirmationWorkflow } from "./confirmation.js";
|
|
6
|
+
import { NonInteractiveShellError } from "./errors.js";
|
|
7
7
|
import { writeCommandOutput } from "./output.js";
|
|
8
8
|
export async function runPruneCommand(options) {
|
|
9
9
|
const { runId } = options;
|
|
@@ -13,7 +13,7 @@ export async function runPruneCommand(options) {
|
|
|
13
13
|
const confirmation = createConfirmationWorkflow({
|
|
14
14
|
assumeYes,
|
|
15
15
|
onUnavailable: () => {
|
|
16
|
-
throw new
|
|
16
|
+
throw new NonInteractiveShellError();
|
|
17
17
|
},
|
|
18
18
|
});
|
|
19
19
|
try {
|
|
@@ -16,7 +16,7 @@ export class ApplyRunDeletedError extends ApplyError {
|
|
|
16
16
|
export class ApplyRunMetadataCorruptedError extends ApplyError {
|
|
17
17
|
constructor(detail) {
|
|
18
18
|
super("Run history is corrupted; cannot apply.", [detail], [
|
|
19
|
-
"Inspect `.voratiq/runs/index.json` and the affected run directory under `.voratiq/runs/<id>` or regenerate the run with `voratiq run`.",
|
|
19
|
+
"Inspect `.voratiq/runs/index.json` and the affected run directory under `.voratiq/runs/sessions/<id>` or regenerate the run with `voratiq run`.",
|
|
20
20
|
]);
|
|
21
21
|
this.name = "ApplyRunMetadataCorruptedError";
|
|
22
22
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
+
import { NonInteractiveShellError } from "../../cli/errors.js";
|
|
2
3
|
import { RunRecordNotFoundError, RunRecordParseError, } from "../../records/errors.js";
|
|
3
4
|
import { rewriteRunRecord, RUN_RECORD_FILENAME, } from "../../records/persistence.js";
|
|
4
5
|
import { buildPruneConfirmationPreface } from "../../render/transcripts/prune.js";
|
|
@@ -7,14 +8,14 @@ import { pathExists } from "../../utils/fs.js";
|
|
|
7
8
|
import { getGitStderr, runGitCommand } from "../../utils/git.js";
|
|
8
9
|
import { normalizePathForDisplay, resolveDisplayPath, resolvePath, } from "../../utils/path.js";
|
|
9
10
|
import { deriveAgentBranches, removeRunDirectory, removeWorkspaceEntry, } from "../../workspace/prune.js";
|
|
10
|
-
import { buildAgentArtifactPaths,
|
|
11
|
+
import { buildAgentArtifactPaths, getAgentDirectoryPath, getAgentEvalsDirectoryPath, getAgentWorkspaceDirectoryPath, getRunDirectoryPath, resolveWorkspacePath, VORATIQ_RUNS_SESSIONS_DIR, } from "../../workspace/structure.js";
|
|
11
12
|
import { fetchRunSafely } from "../fetch.js";
|
|
12
|
-
import {
|
|
13
|
+
import { PruneBranchDeletionError, PruneRunDeletedError, RunMetadataMissingError, } from "./errors.js";
|
|
13
14
|
export async function executePruneCommand(input) {
|
|
14
15
|
const { root, runsFilePath, runId, confirm, clock, purge: purgeInput, } = input;
|
|
15
16
|
const purge = purgeInput ?? false;
|
|
16
17
|
if (!confirm) {
|
|
17
|
-
throw new
|
|
18
|
+
throw new NonInteractiveShellError();
|
|
18
19
|
}
|
|
19
20
|
const runRecord = await fetchRunSafely({
|
|
20
21
|
root,
|
|
@@ -22,7 +23,7 @@ export async function executePruneCommand(input) {
|
|
|
22
23
|
runId,
|
|
23
24
|
onDeleted: (record) => new PruneRunDeletedError(record.runId),
|
|
24
25
|
});
|
|
25
|
-
const runPathDisplay =
|
|
26
|
+
const runPathDisplay = getRunDirectoryPath(runRecord.runId);
|
|
26
27
|
const branches = deriveAgentBranches(runRecord);
|
|
27
28
|
const workspaceTargets = buildWorkspaceTargets({
|
|
28
29
|
root,
|
|
@@ -263,7 +264,7 @@ async function pruneArtifacts(options) {
|
|
|
263
264
|
}
|
|
264
265
|
async function purgeRunDirectoryExceptRecord(options) {
|
|
265
266
|
const { root, runRecord } = options;
|
|
266
|
-
const runDir = resolveWorkspacePath(root,
|
|
267
|
+
const runDir = resolveWorkspacePath(root, VORATIQ_RUNS_SESSIONS_DIR, runRecord.runId);
|
|
267
268
|
if (!(await pathExists(runDir))) {
|
|
268
269
|
return;
|
|
269
270
|
}
|
|
@@ -6,9 +6,6 @@ export declare class RunMetadataMissingError extends PruneError {
|
|
|
6
6
|
readonly runId: string;
|
|
7
7
|
constructor(runId: string);
|
|
8
8
|
}
|
|
9
|
-
export declare class InteractiveConfirmationRequiredError extends PruneError {
|
|
10
|
-
constructor();
|
|
11
|
-
}
|
|
12
9
|
export declare class PruneBranchDeletionError extends PruneError {
|
|
13
10
|
readonly branch: string;
|
|
14
11
|
readonly detail: string;
|
|
@@ -8,17 +8,11 @@ export class PruneError extends CliError {
|
|
|
8
8
|
export class RunMetadataMissingError extends PruneError {
|
|
9
9
|
runId;
|
|
10
10
|
constructor(runId) {
|
|
11
|
-
super(`Run metadata for ${runId} is missing from .voratiq/runs/${runId}/record.json; prune cannot proceed.`);
|
|
11
|
+
super(`Run metadata for ${runId} is missing from .voratiq/runs/sessions/${runId}/record.json; prune cannot proceed.`);
|
|
12
12
|
this.runId = runId;
|
|
13
13
|
this.name = "RunMetadataMissingError";
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
export class InteractiveConfirmationRequiredError extends PruneError {
|
|
17
|
-
constructor() {
|
|
18
|
-
super("Interactive confirmation required; re-run with --yes or from a tty.");
|
|
19
|
-
this.name = "InteractiveConfirmationRequiredError";
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
16
|
export class PruneBranchDeletionError extends PruneError {
|
|
23
17
|
branch;
|
|
24
18
|
detail;
|
|
@@ -6,4 +6,5 @@ export interface AgentDefault {
|
|
|
6
6
|
}
|
|
7
7
|
export declare function sanitizeAgentIdFromModel(model: string): string;
|
|
8
8
|
export declare const DEFAULT_AGENT_DEFAULTS: readonly AgentDefault[];
|
|
9
|
+
export declare function getDefaultAgentIdByProvider(provider: string): string | undefined;
|
|
9
10
|
export declare function getAgentDefault(provider: string): AgentDefault | undefined;
|
|
@@ -24,7 +24,7 @@ export const DEFAULT_AGENT_DEFAULTS = [
|
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
provider: "codex",
|
|
27
|
-
model: "gpt-5.
|
|
27
|
+
model: "gpt-5.2-codex",
|
|
28
28
|
argv: [
|
|
29
29
|
"exec",
|
|
30
30
|
"--model",
|
|
@@ -42,6 +42,10 @@ export const DEFAULT_AGENT_DEFAULTS = [
|
|
|
42
42
|
},
|
|
43
43
|
];
|
|
44
44
|
const AGENT_DEFAULTS_BY_PROVIDER = new Map(DEFAULT_AGENT_DEFAULTS.map((agentDefault) => [agentDefault.provider, agentDefault]));
|
|
45
|
+
export function getDefaultAgentIdByProvider(provider) {
|
|
46
|
+
const entry = AGENT_DEFAULTS_BY_PROVIDER.get(provider);
|
|
47
|
+
return entry ? sanitizeAgentIdFromModel(entry.model) : undefined;
|
|
48
|
+
}
|
|
45
49
|
export function getAgentDefault(provider) {
|
|
46
50
|
const agentDefault = AGENT_DEFAULTS_BY_PROVIDER.get(provider);
|
|
47
51
|
return agentDefault ? cloneAgentDefault(agentDefault) : undefined;
|
|
@@ -18,6 +18,15 @@ export declare class DuplicateAgentIdError extends AgentsConfigError {
|
|
|
18
18
|
readonly displayPath: string;
|
|
19
19
|
constructor(agentId: string, displayPath: string);
|
|
20
20
|
}
|
|
21
|
+
export declare class AgentNotFoundError extends AgentsConfigError {
|
|
22
|
+
readonly agentId: string;
|
|
23
|
+
readonly enabledAgentIds: readonly string[];
|
|
24
|
+
constructor(agentId: string, enabledAgentIds: readonly string[]);
|
|
25
|
+
}
|
|
26
|
+
export declare class AgentDisabledError extends AgentsConfigError {
|
|
27
|
+
readonly agentId: string;
|
|
28
|
+
constructor(agentId: string);
|
|
29
|
+
}
|
|
21
30
|
export declare class ModelPlaceholderMissingError extends AgentsError {
|
|
22
31
|
readonly agentId: string;
|
|
23
32
|
readonly placeholder: string;
|
|
@@ -36,6 +36,27 @@ export class DuplicateAgentIdError extends AgentsConfigError {
|
|
|
36
36
|
this.name = "DuplicateAgentIdError";
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
export class AgentNotFoundError extends AgentsConfigError {
|
|
40
|
+
agentId;
|
|
41
|
+
enabledAgentIds;
|
|
42
|
+
constructor(agentId, enabledAgentIds) {
|
|
43
|
+
const enabledList = enabledAgentIds.length > 0
|
|
44
|
+
? enabledAgentIds.slice().sort().join(", ")
|
|
45
|
+
: "none";
|
|
46
|
+
super(`${DEFAULT_ERROR_CONTEXT}: Agent "${agentId}" is not defined in agents.yaml. Enabled agents: ${enabledList}.`);
|
|
47
|
+
this.agentId = agentId;
|
|
48
|
+
this.enabledAgentIds = enabledAgentIds;
|
|
49
|
+
this.name = "AgentNotFoundError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class AgentDisabledError extends AgentsConfigError {
|
|
53
|
+
agentId;
|
|
54
|
+
constructor(agentId) {
|
|
55
|
+
super(`${DEFAULT_ERROR_CONTEXT}: Agent "${agentId}" is disabled in agents.yaml.`);
|
|
56
|
+
this.agentId = agentId;
|
|
57
|
+
this.name = "AgentDisabledError";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
39
60
|
export class ModelPlaceholderMissingError extends AgentsError {
|
|
40
61
|
agentId;
|
|
41
62
|
placeholder;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentCatalog, type AgentsConfig } from "./types.js";
|
|
1
|
+
import { type AgentCatalog, type AgentDefinition, type AgentsConfig } from "./types.js";
|
|
2
2
|
export declare function readAgentsConfig(content: string): AgentsConfig;
|
|
3
3
|
export interface LoadAgentCatalogOptions {
|
|
4
4
|
root?: string;
|
|
@@ -6,3 +6,4 @@ export interface LoadAgentCatalogOptions {
|
|
|
6
6
|
readFile?: (path: string) => string;
|
|
7
7
|
}
|
|
8
8
|
export declare function loadAgentCatalog(options?: LoadAgentCatalogOptions): AgentCatalog;
|
|
9
|
+
export declare function loadAgentById(id: string, options?: LoadAgentCatalogOptions): AgentDefinition;
|
|
@@ -6,7 +6,7 @@ import { resolveWorkspacePath } from "../../workspace/structure.js";
|
|
|
6
6
|
import { createConfigLoader } from "../shared/loader-factory.js";
|
|
7
7
|
import { formatYamlErrorMessage } from "../shared/yaml-error-formatter.js";
|
|
8
8
|
import { getAgentDefault, MODEL_PLACEHOLDER, } from "./defaults.js";
|
|
9
|
-
import { AgentBinaryAccessError, AgentBinaryMissingError, AgentsYamlParseError, DEFAULT_ERROR_CONTEXT, DuplicateAgentIdError, MissingAgentsConfigError, ModelPlaceholderMissingError, UnknownAgentProviderTemplateError, } from "./errors.js";
|
|
9
|
+
import { AgentBinaryAccessError, AgentBinaryMissingError, AgentDisabledError, AgentNotFoundError, AgentsYamlParseError, DEFAULT_ERROR_CONTEXT, DuplicateAgentIdError, MissingAgentsConfigError, ModelPlaceholderMissingError, UnknownAgentProviderTemplateError, } from "./errors.js";
|
|
10
10
|
import { agentsConfigSchema, } from "./types.js";
|
|
11
11
|
const AGENTS_CONFIG_FILENAME = "agents.yaml";
|
|
12
12
|
export function readAgentsConfig(content) {
|
|
@@ -21,7 +21,7 @@ function formatAgentsYamlError(detail) {
|
|
|
21
21
|
});
|
|
22
22
|
return new AgentsYamlParseError(message);
|
|
23
23
|
}
|
|
24
|
-
const
|
|
24
|
+
const loadAgentsConfigInternal = createConfigLoader({
|
|
25
25
|
resolveFilePath: (root, options) => options.filePath ?? resolveWorkspacePath(root, AGENTS_CONFIG_FILENAME),
|
|
26
26
|
selectReadFile: (options) => options.readFile,
|
|
27
27
|
handleMissing: ({ filePath }) => {
|
|
@@ -38,13 +38,30 @@ const loadAgentCatalogInternal = createConfigLoader({
|
|
|
38
38
|
}
|
|
39
39
|
seenAgentIds.add(entry.id);
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
validateAgentBinaries(catalog);
|
|
43
|
-
return catalog;
|
|
41
|
+
return { config, displayPath, enabledAgents };
|
|
44
42
|
},
|
|
45
43
|
});
|
|
44
|
+
function loadAgentsConfig(options = {}) {
|
|
45
|
+
return loadAgentsConfigInternal(options);
|
|
46
|
+
}
|
|
46
47
|
export function loadAgentCatalog(options = {}) {
|
|
47
|
-
|
|
48
|
+
const { enabledAgents } = loadAgentsConfig(options);
|
|
49
|
+
const catalog = enabledAgents.map((entry) => buildAgentDefinition(entry));
|
|
50
|
+
validateAgentBinaries(catalog);
|
|
51
|
+
return catalog;
|
|
52
|
+
}
|
|
53
|
+
export function loadAgentById(id, options = {}) {
|
|
54
|
+
const { config, enabledAgents } = loadAgentsConfig(options);
|
|
55
|
+
const entry = config.agents.find((agent) => agent.id === id);
|
|
56
|
+
if (!entry) {
|
|
57
|
+
throw new AgentNotFoundError(id, enabledAgents.map((agent) => agent.id));
|
|
58
|
+
}
|
|
59
|
+
if (entry.enabled === false) {
|
|
60
|
+
throw new AgentDisabledError(entry.id);
|
|
61
|
+
}
|
|
62
|
+
const definition = buildAgentDefinition(entry);
|
|
63
|
+
assertAgentBinary(definition);
|
|
64
|
+
return definition;
|
|
48
65
|
}
|
|
49
66
|
function validateAgentBinaries(agents) {
|
|
50
67
|
for (const agent of agents) {
|
|
@@ -10,7 +10,7 @@ export const DEFAULT_SANDBOX_PROVIDERS = [
|
|
|
10
10
|
{
|
|
11
11
|
id: "codex",
|
|
12
12
|
network: {
|
|
13
|
-
allowedDomains: ["api.openai.com", "chatgpt.com"],
|
|
13
|
+
allowedDomains: ["api.openai.com", "chatgpt.com", "auth.openai.com"],
|
|
14
14
|
deniedDomains: [],
|
|
15
15
|
allowLocalBinding: false,
|
|
16
16
|
},
|
|
@@ -54,7 +54,7 @@ type ReadRunRecordsFn = (options: ReadRunRecordsOptions) => Promise<RunRecord[]>
|
|
|
54
54
|
export type RunIndexEntry = Pick<RunRecord, "runId" | "createdAt" | "status">;
|
|
55
55
|
export interface RunIndexPayload {
|
|
56
56
|
version: number;
|
|
57
|
-
|
|
57
|
+
sessions: RunIndexEntry[];
|
|
58
58
|
}
|
|
59
59
|
export declare const RUN_RECORD_FILENAME = "record.json";
|
|
60
60
|
export type RunRecordBufferSnapshotEntry = {
|
|
@@ -8,7 +8,7 @@ import { RunOptionValidationError, RunRecordMutationError, RunRecordNotFoundErro
|
|
|
8
8
|
import { acquireHistoryLock } from "./history-lock.js";
|
|
9
9
|
import { runRecordSchema } from "./types.js";
|
|
10
10
|
export { HISTORY_LOCK_STALE_GRACE_MS } from "./history-lock.js";
|
|
11
|
-
const RUN_INDEX_VERSION =
|
|
11
|
+
const RUN_INDEX_VERSION = 2;
|
|
12
12
|
export const RUN_RECORD_FILENAME = "record.json";
|
|
13
13
|
const HISTORY_LOCK_FILENAME = "history.lock";
|
|
14
14
|
const BUFFER_FLUSH_DELAY_MS = 250;
|
|
@@ -18,7 +18,7 @@ const readRunRecordsInternal = async (options) => {
|
|
|
18
18
|
if (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {
|
|
19
19
|
throw new RunOptionValidationError("limit", "must be a positive integer");
|
|
20
20
|
}
|
|
21
|
-
const
|
|
21
|
+
const sessionsDir = getSessionsDirectory(runsFilePath);
|
|
22
22
|
let index;
|
|
23
23
|
try {
|
|
24
24
|
index = await readRunIndex(runsFilePath);
|
|
@@ -31,12 +31,12 @@ const readRunRecordsInternal = async (options) => {
|
|
|
31
31
|
throw error;
|
|
32
32
|
}
|
|
33
33
|
const matches = [];
|
|
34
|
-
for (let i = index.
|
|
35
|
-
const entry = index.
|
|
34
|
+
for (let i = index.sessions.length - 1; i >= 0; i -= 1) {
|
|
35
|
+
const entry = index.sessions[i];
|
|
36
36
|
if (!entry) {
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
39
|
-
const recordPath = join(
|
|
39
|
+
const recordPath = join(sessionsDir, entry.runId, RUN_RECORD_FILENAME);
|
|
40
40
|
try {
|
|
41
41
|
const record = await readRunRecordFromDisk(recordPath);
|
|
42
42
|
if (predicate && !predicate(record)) {
|
|
@@ -86,11 +86,12 @@ export async function readRunRecords(options) {
|
|
|
86
86
|
}
|
|
87
87
|
export async function appendRunRecord(options) {
|
|
88
88
|
const { root, runsFilePath, record } = options;
|
|
89
|
-
const
|
|
90
|
-
const
|
|
89
|
+
const runsRoot = getRunsDirectory(runsFilePath);
|
|
90
|
+
const sessionsDir = getSessionsDirectory(runsFilePath);
|
|
91
|
+
const recordDir = join(sessionsDir, record.runId);
|
|
91
92
|
const recordPath = join(recordDir, RUN_RECORD_FILENAME);
|
|
92
93
|
const displayPath = relativeToRoot(root, recordPath);
|
|
93
|
-
const lockPath = join(
|
|
94
|
+
const lockPath = join(runsRoot, HISTORY_LOCK_FILENAME);
|
|
94
95
|
await mkdir(recordDir, { recursive: true });
|
|
95
96
|
const releaseLock = await acquireHistoryLock(lockPath);
|
|
96
97
|
try {
|
|
@@ -108,7 +109,7 @@ export async function appendRunRecord(options) {
|
|
|
108
109
|
runId: record.runId,
|
|
109
110
|
recordPath,
|
|
110
111
|
lockPath,
|
|
111
|
-
runsDir,
|
|
112
|
+
runsDir: runsRoot,
|
|
112
113
|
runsFilePath,
|
|
113
114
|
root,
|
|
114
115
|
record,
|
|
@@ -131,15 +132,16 @@ export async function appendRunRecord(options) {
|
|
|
131
132
|
}
|
|
132
133
|
export async function rewriteRunRecord(options) {
|
|
133
134
|
const { root, runsFilePath, runId, mutate } = options;
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const
|
|
135
|
+
const runsRoot = getRunsDirectory(runsFilePath);
|
|
136
|
+
const sessionsDir = getSessionsDirectory(runsFilePath);
|
|
137
|
+
const recordPath = join(sessionsDir, runId, RUN_RECORD_FILENAME);
|
|
138
|
+
const lockPath = join(runsRoot, HISTORY_LOCK_FILENAME);
|
|
137
139
|
const entry = await getOrLoadBufferEntry({
|
|
138
140
|
key: recordPath,
|
|
139
141
|
runId,
|
|
140
142
|
recordPath,
|
|
141
143
|
lockPath,
|
|
142
|
-
runsDir,
|
|
144
|
+
runsDir: runsRoot,
|
|
143
145
|
runsFilePath,
|
|
144
146
|
root,
|
|
145
147
|
});
|
|
@@ -161,8 +163,8 @@ export async function rewriteRunRecord(options) {
|
|
|
161
163
|
}
|
|
162
164
|
export async function getRunRecordSnapshot(options) {
|
|
163
165
|
const { runsFilePath, runId } = options;
|
|
164
|
-
const
|
|
165
|
-
const recordPath = join(
|
|
166
|
+
const sessionsDir = getSessionsDirectory(runsFilePath);
|
|
167
|
+
const recordPath = join(sessionsDir, runId, RUN_RECORD_FILENAME);
|
|
166
168
|
const buffered = runRecordBuffers.get(recordPath);
|
|
167
169
|
if (buffered) {
|
|
168
170
|
return structuredClone(buffered.record);
|
|
@@ -189,7 +191,7 @@ export async function flushAllRunRecordBuffers() {
|
|
|
189
191
|
}
|
|
190
192
|
export async function disposeRunRecordBuffer(options) {
|
|
191
193
|
const { runsFilePath, runId } = options;
|
|
192
|
-
const recordPath = join(
|
|
194
|
+
const recordPath = join(getSessionsDirectory(runsFilePath), runId, RUN_RECORD_FILENAME);
|
|
193
195
|
const entry = runRecordBuffers.get(recordPath);
|
|
194
196
|
if (!entry) {
|
|
195
197
|
return;
|
|
@@ -198,7 +200,7 @@ export async function disposeRunRecordBuffer(options) {
|
|
|
198
200
|
}
|
|
199
201
|
export async function flushRunRecordBuffer(options) {
|
|
200
202
|
const { runsFilePath, runId } = options;
|
|
201
|
-
const recordPath = join(
|
|
203
|
+
const recordPath = join(getSessionsDirectory(runsFilePath), runId, RUN_RECORD_FILENAME);
|
|
202
204
|
const entry = runRecordBuffers.get(recordPath);
|
|
203
205
|
if (!entry) {
|
|
204
206
|
return;
|
|
@@ -390,22 +392,30 @@ async function atomicWriteJson(path, payload) {
|
|
|
390
392
|
function getRunsDirectory(runsFilePath) {
|
|
391
393
|
return dirname(runsFilePath);
|
|
392
394
|
}
|
|
395
|
+
function getSessionsDirectory(runsFilePath) {
|
|
396
|
+
return join(getRunsDirectory(runsFilePath), "sessions");
|
|
397
|
+
}
|
|
393
398
|
async function readRunIndex(path) {
|
|
394
399
|
try {
|
|
395
400
|
const raw = await readFile(path, "utf8");
|
|
396
401
|
const trimmed = raw.trim();
|
|
397
402
|
if (!trimmed) {
|
|
398
|
-
return { version: RUN_INDEX_VERSION,
|
|
403
|
+
return { version: RUN_INDEX_VERSION, sessions: [] };
|
|
399
404
|
}
|
|
400
405
|
const parsed = JSON.parse(trimmed);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
406
|
+
const sessions = Array.isArray(parsed.sessions)
|
|
407
|
+
? parsed.sessions
|
|
408
|
+
: Array.isArray(parsed.runs)
|
|
409
|
+
? parsed.runs
|
|
410
|
+
: [];
|
|
411
|
+
return {
|
|
412
|
+
version: parsed.version ?? RUN_INDEX_VERSION,
|
|
413
|
+
sessions,
|
|
414
|
+
};
|
|
405
415
|
}
|
|
406
416
|
catch (error) {
|
|
407
417
|
if (isFileSystemError(error) && error.code === "ENOENT") {
|
|
408
|
-
return { version: RUN_INDEX_VERSION,
|
|
418
|
+
return { version: RUN_INDEX_VERSION, sessions: [] };
|
|
409
419
|
}
|
|
410
420
|
if (error instanceof SyntaxError) {
|
|
411
421
|
throw new RunRecordParseError(path, error.message);
|
|
@@ -415,15 +425,15 @@ async function readRunIndex(path) {
|
|
|
415
425
|
}
|
|
416
426
|
async function upsertRunIndexEntry(indexPath, entry) {
|
|
417
427
|
const payload = await readRunIndex(indexPath);
|
|
418
|
-
const existingIndex = payload.
|
|
428
|
+
const existingIndex = payload.sessions.findIndex((run) => run.runId === entry.runId);
|
|
419
429
|
if (existingIndex >= 0) {
|
|
420
|
-
payload.
|
|
421
|
-
...payload.
|
|
430
|
+
payload.sessions[existingIndex] = {
|
|
431
|
+
...payload.sessions[existingIndex],
|
|
422
432
|
...entry,
|
|
423
433
|
};
|
|
424
434
|
}
|
|
425
435
|
else {
|
|
426
|
-
payload.
|
|
436
|
+
payload.sessions.push(entry);
|
|
427
437
|
}
|
|
428
438
|
payload.version = RUN_INDEX_VERSION;
|
|
429
439
|
await atomicWriteJson(indexPath, payload);
|
package/dist/utils/git.d.ts
CHANGED
package/dist/utils/git.js
CHANGED
|
@@ -79,15 +79,25 @@ export async function gitHasStagedChanges(cwd) {
|
|
|
79
79
|
return output.length > 0;
|
|
80
80
|
}
|
|
81
81
|
export async function gitCommitAll(options) {
|
|
82
|
-
const { cwd, message, authorName = GIT_AUTHOR_NAME, authorEmail = GIT_AUTHOR_EMAIL, } = options;
|
|
83
|
-
|
|
82
|
+
const { cwd, message, authorName = GIT_AUTHOR_NAME, authorEmail = GIT_AUTHOR_EMAIL, bypassHooks = false, } = options;
|
|
83
|
+
const args = ["commit", "-m", message];
|
|
84
|
+
if (bypassHooks) {
|
|
85
|
+
args.push("--no-verify");
|
|
86
|
+
}
|
|
87
|
+
const env = {
|
|
88
|
+
GIT_AUTHOR_NAME: authorName,
|
|
89
|
+
GIT_AUTHOR_EMAIL: authorEmail,
|
|
90
|
+
GIT_COMMITTER_NAME: authorName,
|
|
91
|
+
GIT_COMMITTER_EMAIL: authorEmail,
|
|
92
|
+
};
|
|
93
|
+
if (bypassHooks) {
|
|
94
|
+
env.HUSKY = "0";
|
|
95
|
+
env.HUSKY_SKIP_HOOKS = "1";
|
|
96
|
+
env.LEFTHOOK = "0";
|
|
97
|
+
}
|
|
98
|
+
await runGitCommand(args, {
|
|
84
99
|
cwd,
|
|
85
|
-
env
|
|
86
|
-
GIT_AUTHOR_NAME: authorName,
|
|
87
|
-
GIT_AUTHOR_EMAIL: authorEmail,
|
|
88
|
-
GIT_COMMITTER_NAME: authorName,
|
|
89
|
-
GIT_COMMITTER_EMAIL: authorEmail,
|
|
90
|
-
},
|
|
100
|
+
env,
|
|
91
101
|
});
|
|
92
102
|
}
|
|
93
103
|
export async function gitDiffShortStat(options) {
|
package/dist/workspace/agents.js
CHANGED
|
@@ -89,6 +89,7 @@ export async function collectAgentArtifacts(options) {
|
|
|
89
89
|
message: summary,
|
|
90
90
|
authorName: persona.authorName,
|
|
91
91
|
authorEmail: persona.authorEmail,
|
|
92
|
+
bypassHooks: true,
|
|
92
93
|
}));
|
|
93
94
|
commitSha = await runGitStep("Git rev-parse failed", async () => runGitCommand(["rev-parse", "HEAD"], { cwd: workspacePath }));
|
|
94
95
|
const diffContent = await runGitStep("Git diff failed", async () => gitDiff({
|
package/dist/workspace/layout.js
CHANGED
|
@@ -114,11 +114,13 @@ export async function scaffoldAgentWorkspace(paths) {
|
|
|
114
114
|
filesToInitialize.add(absolutePath);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
for (const filePath of filesToInitialize) {
|
|
118
|
+
directoriesToEnsure.add(dirname(filePath));
|
|
119
|
+
}
|
|
117
120
|
for (const dirPath of directoriesToEnsure) {
|
|
118
121
|
await mkdir(dirPath, { recursive: true });
|
|
119
122
|
}
|
|
120
123
|
for (const filePath of filesToInitialize) {
|
|
121
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
122
124
|
await writeFile(filePath, "", { encoding: "utf8" });
|
|
123
125
|
}
|
|
124
126
|
}
|
package/dist/workspace/setup.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { ensureDirectoryExists, ensureFileExists, isDirectory, pathExists, } from "../utils/fs.js";
|
|
3
3
|
import { relativeToRoot } from "../utils/path.js";
|
|
4
4
|
import { WorkspaceMissingEntryError, WorkspaceNotInitializedError, } from "./errors.js";
|
|
5
|
-
import { resolveWorkspacePath, VORATIQ_AGENTS_FILE, VORATIQ_ENVIRONMENT_FILE, VORATIQ_EVALS_FILE, VORATIQ_RUNS_DIR, VORATIQ_RUNS_FILE, VORATIQ_SANDBOX_FILE, } from "./structure.js";
|
|
5
|
+
import { resolveWorkspacePath, VORATIQ_AGENTS_FILE, VORATIQ_ENVIRONMENT_FILE, VORATIQ_EVALS_FILE, VORATIQ_RUNS_DIR, VORATIQ_RUNS_FILE, VORATIQ_RUNS_SESSIONS_DIR, VORATIQ_SANDBOX_FILE, } from "./structure.js";
|
|
6
6
|
import { buildDefaultAgentsTemplate, buildDefaultEnvironmentTemplate, buildDefaultEvalsTemplate, buildDefaultSandboxTemplate, } from "./templates.js";
|
|
7
7
|
export async function createWorkspace(root) {
|
|
8
8
|
const createdDirectories = [];
|
|
@@ -17,9 +17,14 @@ export async function createWorkspace(root) {
|
|
|
17
17
|
await mkdir(runsDir, { recursive: true });
|
|
18
18
|
createdDirectories.push(relativeToRoot(root, runsDir));
|
|
19
19
|
}
|
|
20
|
+
const sessionsDir = resolveWorkspacePath(root, VORATIQ_RUNS_SESSIONS_DIR);
|
|
21
|
+
if (!(await pathExists(sessionsDir))) {
|
|
22
|
+
await mkdir(sessionsDir, { recursive: true });
|
|
23
|
+
createdDirectories.push(relativeToRoot(root, sessionsDir));
|
|
24
|
+
}
|
|
20
25
|
const runsIndexPath = resolveWorkspacePath(root, VORATIQ_RUNS_FILE);
|
|
21
26
|
if (!(await pathExists(runsIndexPath))) {
|
|
22
|
-
const initialIndex = JSON.stringify({ version:
|
|
27
|
+
const initialIndex = JSON.stringify({ version: 2, sessions: [] }, null, 2);
|
|
23
28
|
await writeFile(runsIndexPath, `${initialIndex}\n`, { encoding: "utf8" });
|
|
24
29
|
createdFiles.push(relativeToRoot(root, runsIndexPath));
|
|
25
30
|
}
|
|
@@ -56,6 +61,7 @@ export async function createWorkspace(root) {
|
|
|
56
61
|
export async function validateWorkspace(root) {
|
|
57
62
|
const workspaceDir = resolveWorkspacePath(root);
|
|
58
63
|
const runsDir = resolveWorkspacePath(root, VORATIQ_RUNS_DIR);
|
|
64
|
+
const sessionsDir = resolveWorkspacePath(root, VORATIQ_RUNS_SESSIONS_DIR);
|
|
59
65
|
const runsIndexPath = resolveWorkspacePath(root, VORATIQ_RUNS_FILE);
|
|
60
66
|
const agentsConfigPath = resolveWorkspacePath(root, VORATIQ_AGENTS_FILE);
|
|
61
67
|
const evalsConfigPath = resolveWorkspacePath(root, VORATIQ_EVALS_FILE);
|
|
@@ -65,6 +71,7 @@ export async function validateWorkspace(root) {
|
|
|
65
71
|
const missingEntries = [
|
|
66
72
|
`${relativeToRoot(root, workspaceDir)}/`,
|
|
67
73
|
`${relativeToRoot(root, runsDir)}/`,
|
|
74
|
+
`${relativeToRoot(root, sessionsDir)}/`,
|
|
68
75
|
relativeToRoot(root, runsIndexPath),
|
|
69
76
|
relativeToRoot(root, agentsConfigPath),
|
|
70
77
|
relativeToRoot(root, environmentConfigPath),
|
|
@@ -73,6 +80,7 @@ export async function validateWorkspace(root) {
|
|
|
73
80
|
throw new WorkspaceNotInitializedError(missingEntries);
|
|
74
81
|
}
|
|
75
82
|
await ensureDirectoryExists(runsDir, () => new WorkspaceMissingEntryError(relativeToRoot(root, runsDir)));
|
|
83
|
+
await ensureDirectoryExists(sessionsDir, () => new WorkspaceMissingEntryError(relativeToRoot(root, sessionsDir)));
|
|
76
84
|
await ensureFileExists(runsIndexPath, () => new WorkspaceMissingEntryError(relativeToRoot(root, runsIndexPath)));
|
|
77
85
|
await ensureFileExists(agentsConfigPath, () => new WorkspaceMissingEntryError(relativeToRoot(root, agentsConfigPath)));
|
|
78
86
|
await ensureFileExists(evalsConfigPath, () => new WorkspaceMissingEntryError(relativeToRoot(root, evalsConfigPath)));
|
|
@@ -3,6 +3,7 @@ import type { ChatArtifactFormat } from "./chat/types.js";
|
|
|
3
3
|
export declare const VORATIQ_DIR = ".voratiq";
|
|
4
4
|
export declare const VORATIQ_RUNS_DIR = "runs";
|
|
5
5
|
export declare const VORATIQ_RUNS_FILE = "runs/index.json";
|
|
6
|
+
export declare const VORATIQ_RUNS_SESSIONS_DIR = "runs/sessions";
|
|
6
7
|
export declare const VORATIQ_AGENTS_FILE = "agents.yaml";
|
|
7
8
|
export declare const VORATIQ_EVALS_FILE = "evals.yaml";
|
|
8
9
|
export declare const VORATIQ_ENVIRONMENT_FILE = "environment.yaml";
|
|
@@ -4,6 +4,7 @@ import { assertRepoRelativePath, resolvePathWithinRoot, } from "../utils/path.js
|
|
|
4
4
|
export const VORATIQ_DIR = ".voratiq";
|
|
5
5
|
export const VORATIQ_RUNS_DIR = "runs";
|
|
6
6
|
export const VORATIQ_RUNS_FILE = "runs/index.json";
|
|
7
|
+
export const VORATIQ_RUNS_SESSIONS_DIR = "runs/sessions";
|
|
7
8
|
export const VORATIQ_AGENTS_FILE = "agents.yaml";
|
|
8
9
|
export const VORATIQ_EVALS_FILE = "evals.yaml";
|
|
9
10
|
export const VORATIQ_ENVIRONMENT_FILE = "environment.yaml";
|
|
@@ -28,7 +29,7 @@ export function resolveWorkspacePath(root, ...segments) {
|
|
|
28
29
|
export function formatWorkspacePath(...segments) {
|
|
29
30
|
return [VORATIQ_DIR, ...segments].join("/");
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
+
const RUNS_SESSION_SEGMENTS = [VORATIQ_RUNS_DIR, "sessions"];
|
|
32
33
|
function assertPathSegment(label, value) {
|
|
33
34
|
if (typeof value !== "string") {
|
|
34
35
|
throw new Error(`${label} must be a string`);
|
|
@@ -43,7 +44,7 @@ function assertPathSegment(label, value) {
|
|
|
43
44
|
}
|
|
44
45
|
function formatRunScopedPath(runId, ...segments) {
|
|
45
46
|
const safeRunId = assertPathSegment("runId", runId);
|
|
46
|
-
const scoped = formatWorkspacePath(
|
|
47
|
+
const scoped = formatWorkspacePath(...RUNS_SESSION_SEGMENTS, safeRunId, ...segments);
|
|
47
48
|
return assertRepoRelativePath(scoped);
|
|
48
49
|
}
|
|
49
50
|
function formatAgentScopedPath(runId, agentId, ...segments) {
|