voratiq 0.1.0-beta.21 → 0.1.0-beta.22

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.
Files changed (149) hide show
  1. package/README.md +18 -22
  2. package/dist/agents/launch/chat.d.ts +3 -1
  3. package/dist/agents/launch/chat.js +2 -0
  4. package/dist/bin.js +28 -7
  5. package/dist/cli/auto.js +1 -0
  6. package/dist/cli/contract.d.ts +26 -17
  7. package/dist/cli/contract.js +3 -1
  8. package/dist/cli/doctor.d.ts +12 -0
  9. package/dist/cli/doctor.js +115 -0
  10. package/dist/cli/list.js +4 -1
  11. package/dist/cli/operator-envelope.d.ts +19 -6
  12. package/dist/cli/operator-envelope.js +61 -1
  13. package/dist/cli/run.js +2 -0
  14. package/dist/cli/verify.d.ts +1 -1
  15. package/dist/cli/verify.js +48 -9
  16. package/dist/commands/auto/command.d.ts +1 -0
  17. package/dist/commands/auto/command.js +22 -12
  18. package/dist/commands/auto/errors.js +1 -1
  19. package/dist/commands/doctor/agents.d.ts +5 -0
  20. package/dist/commands/{init → doctor}/agents.js +37 -19
  21. package/dist/commands/doctor/command.d.ts +22 -0
  22. package/dist/commands/doctor/command.js +99 -0
  23. package/dist/commands/doctor/environment.d.ts +2 -0
  24. package/dist/commands/{init → doctor}/environment.js +38 -6
  25. package/dist/commands/{init/types.d.ts → doctor/fix-types.d.ts} +30 -9
  26. package/dist/commands/doctor/fix.d.ts +2 -0
  27. package/dist/commands/{init/command.js → doctor/fix.js} +106 -10
  28. package/dist/commands/doctor/reconcile.d.ts +2 -0
  29. package/dist/commands/doctor/reconcile.js +101 -0
  30. package/dist/commands/interactive/lifecycle.d.ts +2 -0
  31. package/dist/commands/interactive/lifecycle.js +8 -0
  32. package/dist/commands/list/command.d.ts +1 -0
  33. package/dist/commands/list/command.js +211 -352
  34. package/dist/commands/list/normalization.d.ts +56 -0
  35. package/dist/commands/list/normalization.js +317 -0
  36. package/dist/commands/message/command.d.ts +2 -1
  37. package/dist/commands/message/command.js +35 -14
  38. package/dist/commands/message/errors.d.ts +12 -3
  39. package/dist/commands/message/errors.js +19 -3
  40. package/dist/commands/reduce/command.js +16 -17
  41. package/dist/commands/reduce/errors.d.ts +2 -2
  42. package/dist/commands/reduce/errors.js +3 -3
  43. package/dist/commands/reduce/targets.js +11 -2
  44. package/dist/commands/root-launcher/command.js +12 -6
  45. package/dist/commands/run/command.d.ts +1 -0
  46. package/dist/commands/run/command.js +4 -1
  47. package/dist/commands/run/record-init.d.ts +2 -0
  48. package/dist/commands/run/record-init.js +2 -1
  49. package/dist/commands/run/spec-provenance.d.ts +37 -0
  50. package/dist/commands/run/spec-provenance.js +384 -0
  51. package/dist/commands/run/validation.d.ts +4 -0
  52. package/dist/commands/run/validation.js +25 -62
  53. package/dist/commands/spec/command.js +19 -6
  54. package/dist/commands/spec/errors.d.ts +5 -0
  55. package/dist/commands/spec/errors.js +9 -0
  56. package/dist/commands/verify/agents.d.ts +4 -2
  57. package/dist/commands/verify/agents.js +4 -11
  58. package/dist/commands/verify/command.js +15 -5
  59. package/dist/commands/verify/errors.d.ts +12 -0
  60. package/dist/commands/verify/errors.js +22 -0
  61. package/dist/commands/verify/targets.js +108 -12
  62. package/dist/competition/shared/preflight.d.ts +1 -1
  63. package/dist/competition/shared/preflight.js +15 -2
  64. package/dist/contracts/list.d.ts +129 -149
  65. package/dist/contracts/list.js +47 -99
  66. package/dist/domain/interactive/persistence/adapter.d.ts +23 -0
  67. package/dist/domain/interactive/persistence/adapter.js +42 -0
  68. package/dist/domain/message/model/types.d.ts +32 -0
  69. package/dist/domain/message/model/types.js +25 -0
  70. package/dist/domain/reduce/competition/adapter.js +21 -7
  71. package/dist/domain/reduce/competition/finalize.d.ts +7 -0
  72. package/dist/domain/reduce/competition/finalize.js +19 -0
  73. package/dist/domain/reduce/model/types.d.ts +3 -3
  74. package/dist/domain/run/competition/agents/artifacts.js +4 -2
  75. package/dist/domain/run/competition/errors.d.ts +1 -1
  76. package/dist/domain/run/competition/errors.js +4 -7
  77. package/dist/domain/run/model/types.d.ts +384 -0
  78. package/dist/domain/run/model/types.js +87 -0
  79. package/dist/domain/spec/competition/adapter.d.ts +1 -0
  80. package/dist/domain/spec/competition/adapter.js +6 -1
  81. package/dist/domain/spec/model/types.d.ts +3 -0
  82. package/dist/domain/spec/model/types.js +5 -0
  83. package/dist/domain/verify/competition/finalize.d.ts +9 -0
  84. package/dist/domain/verify/competition/finalize.js +22 -3
  85. package/dist/domain/verify/model/types.d.ts +2 -2
  86. package/dist/interactive/providers/mcp.d.ts +1 -0
  87. package/dist/interactive/providers/mcp.js +45 -7
  88. package/dist/interactive/substrate.js +20 -3
  89. package/dist/mcp/server.js +26 -9
  90. package/dist/policy/verification.js +18 -1
  91. package/dist/preflight/agents.d.ts +24 -0
  92. package/dist/preflight/agents.js +71 -0
  93. package/dist/preflight/environment.d.ts +6 -0
  94. package/dist/preflight/environment.js +17 -0
  95. package/dist/preflight/formatting.d.ts +5 -0
  96. package/dist/preflight/formatting.js +20 -0
  97. package/dist/preflight/index.d.ts +2 -0
  98. package/dist/preflight/index.js +5 -9
  99. package/dist/preflight/operator.d.ts +32 -0
  100. package/dist/preflight/operator.js +40 -0
  101. package/dist/preflight/settings.d.ts +2 -0
  102. package/dist/preflight/settings.js +17 -0
  103. package/dist/render/transcripts/interactive.d.ts +16 -0
  104. package/dist/render/transcripts/interactive.js +42 -0
  105. package/dist/render/transcripts/list.d.ts +41 -0
  106. package/dist/render/transcripts/list.js +152 -3
  107. package/dist/render/transcripts/message.d.ts +2 -1
  108. package/dist/render/transcripts/message.js +19 -20
  109. package/dist/render/transcripts/reduce.d.ts +1 -0
  110. package/dist/render/transcripts/reduce.js +21 -21
  111. package/dist/render/transcripts/root-launcher.js +2 -12
  112. package/dist/render/transcripts/run.d.ts +3 -0
  113. package/dist/render/transcripts/run.js +30 -4
  114. package/dist/render/transcripts/spec.js +5 -8
  115. package/dist/render/transcripts/verify.d.ts +5 -3
  116. package/dist/render/transcripts/verify.js +44 -31
  117. package/dist/render/utils/duration.d.ts +5 -0
  118. package/dist/render/utils/duration.js +6 -0
  119. package/dist/render/utils/runs.d.ts +1 -0
  120. package/dist/render/utils/runs.js +1 -0
  121. package/dist/render/utils/transcript-shell.d.ts +2 -1
  122. package/dist/render/utils/transcript-shell.js +19 -6
  123. package/dist/utils/errors.d.ts +2 -1
  124. package/dist/utils/errors.js +3 -1
  125. package/dist/utils/git.d.ts +1 -1
  126. package/dist/utils/git.js +25 -2
  127. package/dist/utils/list-target.d.ts +4 -0
  128. package/dist/utils/list-target.js +35 -0
  129. package/dist/utils/terminal.d.ts +1 -0
  130. package/dist/utils/terminal.js +11 -0
  131. package/dist/workspace/chat/artifacts.d.ts +7 -0
  132. package/dist/workspace/chat/artifacts.js +94 -3
  133. package/dist/workspace/errors.js +2 -2
  134. package/dist/workspace/managed-state.d.ts +32 -0
  135. package/dist/workspace/managed-state.js +103 -0
  136. package/dist/workspace/setup.js +66 -2
  137. package/dist/workspace/shim.d.ts +1 -0
  138. package/dist/workspace/shim.js +3 -3
  139. package/dist/workspace/structure.d.ts +1 -0
  140. package/dist/workspace/structure.js +1 -0
  141. package/package.json +2 -2
  142. package/dist/cli/init.d.ts +0 -15
  143. package/dist/cli/init.js +0 -70
  144. package/dist/commands/init/agents.d.ts +0 -4
  145. package/dist/commands/init/command.d.ts +0 -2
  146. package/dist/commands/init/environment.d.ts +0 -2
  147. package/dist/render/transcripts/init.d.ts +0 -7
  148. package/dist/render/transcripts/init.js +0 -83
  149. /package/dist/commands/{init/types.js → doctor/fix-types.js} +0 -0
@@ -31,7 +31,7 @@ export declare function removeWorktree(options: {
31
31
  root: string;
32
32
  worktreePath: string;
33
33
  }): Promise<void>;
34
- export declare function gitAddAll(cwd: string): Promise<void>;
34
+ export declare function gitAddAll(cwd: string, excludedPaths?: readonly string[]): Promise<void>;
35
35
  export declare function gitHasStagedChanges(cwd: string): Promise<boolean>;
36
36
  export interface GitCommitOptions {
37
37
  cwd: string;
package/dist/utils/git.js CHANGED
@@ -20,7 +20,7 @@ export async function assertGitRepository(root) {
20
20
  const repoRoot = await getGitRepositoryRoot(root);
21
21
  if (repoRoot !== null) {
22
22
  // We're in a repo but not at the root
23
- throw new GitRepositoryError("Run `voratiq init` from the repository root.", "not_repository_root");
23
+ throw new GitRepositoryError("Run `voratiq` from the repository root.", "not_repository_root", repoRoot);
24
24
  }
25
25
  // No git repository exists at all
26
26
  throw new GitRepositoryError("No git repository found. Run `git init` or switch to an existing repository.", "no_repository");
@@ -80,8 +80,15 @@ export async function removeWorktree(options) {
80
80
  cwd: root,
81
81
  });
82
82
  }
83
- export async function gitAddAll(cwd) {
83
+ export async function gitAddAll(cwd, excludedPaths) {
84
84
  await runGitCommand(["add", "-A"], { cwd });
85
+ const existingExcludedPaths = await resolveExistingGitPaths(cwd, excludedPaths);
86
+ if (existingExcludedPaths.length === 0) {
87
+ return;
88
+ }
89
+ await runGitCommand(["reset", "--quiet", "HEAD", "--", ...existingExcludedPaths], {
90
+ cwd,
91
+ });
85
92
  }
86
93
  export async function gitHasStagedChanges(cwd) {
87
94
  const output = await runGitCommand(["diff", "--cached", "--name-only"], {
@@ -146,3 +153,19 @@ function isHeadMissing(error) {
146
153
  const isHeadExitCode = code === 128 || code === "128";
147
154
  return headMissingMessage || (isHeadExitCode && normalized.includes("head"));
148
155
  }
156
+ async function resolveExistingGitPaths(cwd, paths) {
157
+ if (!paths || paths.length === 0) {
158
+ return [];
159
+ }
160
+ const existing = [];
161
+ for (const path of paths) {
162
+ try {
163
+ await access(join(cwd, path), F_OK);
164
+ existing.push(path);
165
+ }
166
+ catch {
167
+ // Ignore missing excluded paths so git add works across repos and tests.
168
+ }
169
+ }
170
+ return existing;
171
+ }
@@ -0,0 +1,4 @@
1
+ import type { ListJsonTargetRef } from "../contracts/list.js";
2
+ export declare const TARGET_TABLE_PREVIEW_LENGTH = 32;
3
+ export declare function formatTargetDisplay(target: ListJsonTargetRef): string;
4
+ export declare function formatTargetTablePreview(target: ListJsonTargetRef, maxLength?: number): string;
@@ -0,0 +1,35 @@
1
+ const TARGET_SEPARATOR = ":";
2
+ const TARGET_ELISION = "...";
3
+ export const TARGET_TABLE_PREVIEW_LENGTH = 32;
4
+ export function formatTargetDisplay(target) {
5
+ if (target.kind === "file") {
6
+ return `file:${target.path}`;
7
+ }
8
+ if (target.agentId) {
9
+ return `${target.kind}:${target.sessionId}:${target.agentId}`;
10
+ }
11
+ return `${target.kind}:${target.sessionId}`;
12
+ }
13
+ export function formatTargetTablePreview(target, maxLength = TARGET_TABLE_PREVIEW_LENGTH) {
14
+ const display = formatTargetDisplay(target);
15
+ return middleElideTargetDisplay(display, maxLength);
16
+ }
17
+ function middleElideTargetDisplay(display, maxLength) {
18
+ if (display.length <= maxLength) {
19
+ return display;
20
+ }
21
+ if (maxLength <= TARGET_ELISION.length) {
22
+ return display.slice(0, maxLength);
23
+ }
24
+ const separatorIndex = display.indexOf(TARGET_SEPARATOR);
25
+ const prefixLength = separatorIndex >= 0 ? separatorIndex + 1 : 0;
26
+ const prefix = display.slice(0, prefixLength);
27
+ const suffixSource = display.slice(prefixLength);
28
+ const availableSuffixLength = maxLength - prefix.length - TARGET_ELISION.length;
29
+ if (availableSuffixLength <= 0) {
30
+ const prefixBudget = Math.max(0, maxLength - TARGET_ELISION.length);
31
+ return `${display.slice(0, prefixBudget)}${TARGET_ELISION}`;
32
+ }
33
+ const suffix = suffixSource.slice(-availableSuffixLength);
34
+ return `${prefix}${TARGET_ELISION}${suffix}`;
35
+ }
@@ -3,3 +3,4 @@ export interface InteractiveShellOptions {
3
3
  output?: NodeJS.WriteStream | null;
4
4
  }
5
5
  export declare function isInteractiveShell(options?: InteractiveShellOptions): boolean;
6
+ export declare function normalizeInteractiveTerm(env: NodeJS.ProcessEnv, options?: InteractiveShellOptions): string | undefined;
@@ -1,5 +1,16 @@
1
+ const FALLBACK_INTERACTIVE_TERM = "xterm-256color";
1
2
  export function isInteractiveShell(options = {}) {
2
3
  const input = options.input ?? process.stdin;
3
4
  const output = options.output ?? process.stdout;
4
5
  return Boolean(input?.isTTY && output?.isTTY);
5
6
  }
7
+ export function normalizeInteractiveTerm(env, options = {}) {
8
+ if (!isInteractiveShell(options)) {
9
+ return env.TERM;
10
+ }
11
+ const term = env.TERM?.trim();
12
+ if (!term || term.toLowerCase() === "dumb") {
13
+ return FALLBACK_INTERACTIVE_TERM;
14
+ }
15
+ return env.TERM;
16
+ }
@@ -12,6 +12,7 @@ export interface PreserveChatArtifactsOptions {
12
12
  agentRoot: string;
13
13
  searchEnv?: NodeJS.ProcessEnv;
14
14
  baseline?: ProviderTranscriptBaseline;
15
+ selectionHint?: ProviderTranscriptSelectionHint;
15
16
  }
16
17
  export interface ProviderTranscriptSnapshotEntry {
17
18
  path: string;
@@ -19,5 +20,11 @@ export interface ProviderTranscriptSnapshotEntry {
19
20
  mtimeMs: number;
20
21
  }
21
22
  export type ProviderTranscriptBaseline = readonly ProviderTranscriptSnapshotEntry[];
23
+ export interface CodexSessionMetaSelectionHint {
24
+ strategy: "codex-session-meta";
25
+ cwd: string;
26
+ minStartedAt?: string;
27
+ }
28
+ export type ProviderTranscriptSelectionHint = CodexSessionMetaSelectionHint;
22
29
  export declare function snapshotProviderTranscripts(options: Omit<PreserveChatArtifactsOptions, "baseline">): Promise<ProviderTranscriptBaseline>;
23
30
  export declare function preserveProviderChatTranscripts(options: PreserveChatArtifactsOptions): Promise<ChatArtifactCaptureResult>;
@@ -42,7 +42,20 @@ export async function preserveProviderChatTranscripts(options) {
42
42
  };
43
43
  }
44
44
  const existing = await locateExistingChatArtifact(agentRoot);
45
- const selection = selectTranscriptFiles(candidatePaths);
45
+ let selection;
46
+ try {
47
+ selection = await selectTranscriptFiles({
48
+ providerId,
49
+ transcriptPaths: candidatePaths,
50
+ selectionHint: options.selectionHint,
51
+ });
52
+ }
53
+ catch (error) {
54
+ return {
55
+ status: "error",
56
+ error: error instanceof Error ? error : new Error(String(error)),
57
+ };
58
+ }
46
59
  if (!selection) {
47
60
  if (existing !== undefined) {
48
61
  return {
@@ -101,10 +114,21 @@ async function locateExistingChatArtifact(agentRoot) {
101
114
  }
102
115
  return undefined;
103
116
  }
104
- function selectTranscriptFiles(transcriptPaths) {
117
+ async function selectTranscriptFiles(options) {
118
+ const { providerId, transcriptPaths, selectionHint } = options;
105
119
  const jsonlFiles = transcriptPaths.filter((path) => path.toLowerCase().endsWith(".jsonl"));
106
120
  if (jsonlFiles.length > 0) {
107
- return { format: "jsonl", files: [...jsonlFiles].sort() };
121
+ const selectedJsonlFiles = await filterJsonlTranscriptFilesByHint({
122
+ providerId,
123
+ files: jsonlFiles,
124
+ selectionHint,
125
+ });
126
+ if (selectedJsonlFiles.length > 0) {
127
+ return { format: "jsonl", files: selectedJsonlFiles };
128
+ }
129
+ if (selectionHint) {
130
+ return undefined;
131
+ }
108
132
  }
109
133
  const jsonFiles = transcriptPaths
110
134
  .filter((path) => path.toLowerCase().endsWith(".json"))
@@ -114,6 +138,73 @@ function selectTranscriptFiles(transcriptPaths) {
114
138
  }
115
139
  return undefined;
116
140
  }
141
+ async function filterJsonlTranscriptFilesByHint(options) {
142
+ const sortedFiles = [...options.files].sort();
143
+ const hint = options.selectionHint;
144
+ if (!hint) {
145
+ return sortedFiles;
146
+ }
147
+ if (options.providerId !== "codex" ||
148
+ hint.strategy !== "codex-session-meta") {
149
+ return sortedFiles;
150
+ }
151
+ const matchingFiles = [];
152
+ for (const file of sortedFiles) {
153
+ const metadata = await readCodexSessionMetadata(file);
154
+ if (!metadata) {
155
+ continue;
156
+ }
157
+ if (metadata.cwd !== hint.cwd) {
158
+ continue;
159
+ }
160
+ if (hint.minStartedAt &&
161
+ metadata.startedAt &&
162
+ metadata.startedAt < hint.minStartedAt) {
163
+ continue;
164
+ }
165
+ matchingFiles.push(file);
166
+ }
167
+ if (matchingFiles.length === 1) {
168
+ return matchingFiles;
169
+ }
170
+ if (matchingFiles.length > 1) {
171
+ throw new Error(`Ambiguous Codex transcript provenance for cwd \`${hint.cwd}\`: ${matchingFiles.join(", ")}`);
172
+ }
173
+ return [];
174
+ }
175
+ async function readCodexSessionMetadata(path) {
176
+ let raw;
177
+ try {
178
+ raw = await readFile(path, "utf8");
179
+ }
180
+ catch {
181
+ return undefined;
182
+ }
183
+ const lines = raw.split(/\r?\n/u);
184
+ for (const line of lines) {
185
+ const trimmed = line.trim();
186
+ if (!trimmed) {
187
+ continue;
188
+ }
189
+ let parsed;
190
+ try {
191
+ parsed = JSON.parse(trimmed);
192
+ }
193
+ catch {
194
+ continue;
195
+ }
196
+ const record = parsed;
197
+ if (record.type !== "session_meta") {
198
+ continue;
199
+ }
200
+ const payload = record.payload?.payload ?? record.payload;
201
+ return {
202
+ cwd: typeof payload?.cwd === "string" ? payload.cwd : undefined,
203
+ startedAt: typeof payload?.timestamp === "string" ? payload.timestamp : undefined,
204
+ };
205
+ }
206
+ return undefined;
207
+ }
117
208
  async function bundleJsonTranscripts(options) {
118
209
  const { files, artifactPath, agentRoot, providerId } = options;
119
210
  if (files.length === 0) {
@@ -1,6 +1,6 @@
1
1
  import { HintedError } from "../utils/errors.js";
2
2
  const DEFAULT_WORKSPACE_HINT = [
3
- "Run `voratiq init` to configure the workspace.",
3
+ "Run `voratiq doctor --fix` to repair workspace setup.",
4
4
  ];
5
5
  export class WorkspaceError extends HintedError {
6
6
  constructor(message, detailLines = [], hintLines = DEFAULT_WORKSPACE_HINT) {
@@ -29,7 +29,7 @@ export class WorkspaceWrongTypeEntryError extends WorkspaceError {
29
29
  export class WorkspaceNotInitializedError extends WorkspaceError {
30
30
  missingEntries;
31
31
  constructor(missingEntries) {
32
- super("Voratiq workspace is not initialized.", buildMissingEntryDetailLines(missingEntries), ["Run `voratiq init` from the repository root, then retry."]);
32
+ super("Voratiq workspace is not initialized.", buildMissingEntryDetailLines(missingEntries), ["Run `voratiq doctor --fix` to repair workspace setup."]);
33
33
  this.missingEntries = missingEntries;
34
34
  this.name = "WorkspaceNotInitializedError";
35
35
  }
@@ -0,0 +1,32 @@
1
+ import type { AgentPreset } from "../configs/agents/defaults.js";
2
+ export declare const MANAGED_STATE_VERSION = 1;
3
+ export interface ManagedConfigFingerprint {
4
+ fingerprint: string;
5
+ }
6
+ export interface ManagedAgentsFingerprint extends ManagedConfigFingerprint {
7
+ managedEntriesFingerprint: string;
8
+ }
9
+ export interface ManagedOrchestrationFingerprint extends ManagedConfigFingerprint {
10
+ preset: AgentPreset;
11
+ }
12
+ export interface ManagedStateSnapshot {
13
+ version: number;
14
+ configs: {
15
+ agents?: ManagedAgentsFingerprint;
16
+ orchestration?: ManagedOrchestrationFingerprint;
17
+ };
18
+ }
19
+ export interface UpdateManagedStateOptions {
20
+ agentsContent?: string;
21
+ orchestrationContent?: string;
22
+ orchestrationPreset?: AgentPreset;
23
+ }
24
+ export declare function readManagedState(root: string): Promise<ManagedStateSnapshot | undefined>;
25
+ export declare function updateManagedState(root: string, options: UpdateManagedStateOptions): Promise<{
26
+ path: string;
27
+ created: boolean;
28
+ updated: boolean;
29
+ }>;
30
+ export declare function computeManagedFingerprint(content: string): string;
31
+ export declare function isManagedFingerprintMatch(fingerprint: ManagedConfigFingerprint | undefined, content: string): boolean;
32
+ export declare function isManagedAgentsFingerprintMatch(fingerprint: ManagedAgentsFingerprint | undefined, content: string): boolean;
@@ -0,0 +1,103 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { getAgentDefaultId, getSupportedAgentDefaults, } from "../configs/agents/defaults.js";
4
+ import { readAgentsConfig } from "../configs/agents/loader.js";
5
+ import { isFileSystemError } from "../utils/fs.js";
6
+ import { normalizeConfigText } from "../utils/yaml.js";
7
+ import { resolveWorkspacePath, VORATIQ_MANAGED_STATE_FILE, } from "./structure.js";
8
+ import { serializeAgentsConfigEntries } from "./templates.js";
9
+ export const MANAGED_STATE_VERSION = 1;
10
+ export async function readManagedState(root) {
11
+ const path = resolveWorkspacePath(root, VORATIQ_MANAGED_STATE_FILE);
12
+ try {
13
+ const content = await readFile(path, "utf8");
14
+ return JSON.parse(content);
15
+ }
16
+ catch (error) {
17
+ if (isFileSystemError(error) && error.code === "ENOENT") {
18
+ return undefined;
19
+ }
20
+ throw error;
21
+ }
22
+ }
23
+ export async function updateManagedState(root, options) {
24
+ const path = resolveWorkspacePath(root, VORATIQ_MANAGED_STATE_FILE);
25
+ const existing = await readManagedState(root);
26
+ const next = existing
27
+ ? {
28
+ ...existing,
29
+ version: MANAGED_STATE_VERSION,
30
+ configs: { ...existing.configs },
31
+ }
32
+ : { version: MANAGED_STATE_VERSION, configs: {} };
33
+ if (options.agentsContent !== undefined) {
34
+ next.configs.agents = {
35
+ fingerprint: computeManagedFingerprint(options.agentsContent),
36
+ managedEntriesFingerprint: computeManagedAgentsEntriesFingerprint(options.agentsContent),
37
+ };
38
+ }
39
+ if (options.orchestrationContent !== undefined) {
40
+ next.configs.orchestration = {
41
+ fingerprint: computeManagedFingerprint(options.orchestrationContent),
42
+ preset: options.orchestrationPreset ?? "pro",
43
+ };
44
+ }
45
+ const serialized = serializeManagedState(next);
46
+ const previousSerialized = existing ? serializeManagedState(existing) : "";
47
+ if (serialized === previousSerialized) {
48
+ return { path, created: false, updated: false };
49
+ }
50
+ await writeFile(path, serialized, "utf8");
51
+ return {
52
+ path,
53
+ created: existing === undefined,
54
+ updated: true,
55
+ };
56
+ }
57
+ export function computeManagedFingerprint(content) {
58
+ return createHash("sha256")
59
+ .update(normalizeConfigText(content), "utf8")
60
+ .digest("hex");
61
+ }
62
+ export function isManagedFingerprintMatch(fingerprint, content) {
63
+ if (!fingerprint) {
64
+ return false;
65
+ }
66
+ return fingerprint.fingerprint === computeManagedFingerprint(content);
67
+ }
68
+ export function isManagedAgentsFingerprintMatch(fingerprint, content) {
69
+ if (!fingerprint) {
70
+ return false;
71
+ }
72
+ return (fingerprint.fingerprint === computeManagedFingerprint(content) ||
73
+ fingerprint.managedEntriesFingerprint ===
74
+ computeManagedAgentsEntriesFingerprint(content));
75
+ }
76
+ function serializeManagedState(state) {
77
+ return `${JSON.stringify(state, null, 2)}\n`;
78
+ }
79
+ function computeManagedAgentsEntriesFingerprint(content) {
80
+ const config = readAgentsConfig(content);
81
+ const entriesById = new Map();
82
+ for (const entry of config.agents) {
83
+ entriesById.set(entry.id, normalizeAgentEntry(entry));
84
+ }
85
+ const managedEntries = getSupportedAgentDefaults()
86
+ .map((template) => entriesById.get(getAgentDefaultId(template)))
87
+ .filter((entry) => entry !== undefined);
88
+ return createHash("sha256")
89
+ .update(normalizeConfigText(`agents:\n${serializeAgentsConfigEntries(managedEntries)}`), "utf8")
90
+ .digest("hex");
91
+ }
92
+ function normalizeAgentEntry(entry) {
93
+ return {
94
+ id: entry.id,
95
+ provider: entry.provider,
96
+ model: entry.model,
97
+ enabled: entry.enabled !== false,
98
+ binary: entry.binary ?? "",
99
+ extraArgs: entry.extraArgs && entry.extraArgs.length > 0
100
+ ? [...entry.extraArgs]
101
+ : undefined,
102
+ };
103
+ }
@@ -1,5 +1,5 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { dirname, join } from "node:path";
2
+ import { dirname, join, resolve as resolveAbsolute } from "node:path";
3
3
  import { readAgentsConfig } from "../configs/agents/loader.js";
4
4
  import { loadEnvironmentConfig, } from "../configs/environment/loader.js";
5
5
  import { buildDefaultOrchestrationTemplate } from "../configs/orchestration/bootstrap.js";
@@ -7,7 +7,8 @@ import { toErrorMessage } from "../utils/errors.js";
7
7
  import { isDirectory, isFile, pathExists } from "../utils/fs.js";
8
8
  import { relativeToRoot } from "../utils/path.js";
9
9
  import { WorkspaceMissingEntryError, WorkspaceNotInitializedError, WorkspaceSetupError, WorkspaceWrongTypeEntryError, } from "./errors.js";
10
- import { resolveWorkspacePath, VORATIQ_AGENTS_FILE, VORATIQ_ENVIRONMENT_FILE, VORATIQ_INTERACTIVE_DIR, VORATIQ_INTERACTIVE_FILE, VORATIQ_INTERACTIVE_SESSIONS_DIR, VORATIQ_MESSAGE_DIR, VORATIQ_MESSAGE_FILE, VORATIQ_MESSAGE_SESSIONS_DIR, VORATIQ_ORCHESTRATION_FILE, VORATIQ_REDUCTION_DIR, VORATIQ_REDUCTION_FILE, VORATIQ_REDUCTION_SESSIONS_DIR, VORATIQ_RUN_DIR, VORATIQ_RUN_FILE, VORATIQ_RUN_SESSIONS_DIR, VORATIQ_SANDBOX_FILE, VORATIQ_SPEC_DIR, VORATIQ_SPEC_FILE, VORATIQ_SPEC_SESSIONS_DIR, VORATIQ_VERIFICATION_CONFIG_FILE, VORATIQ_VERIFICATION_DIR, VORATIQ_VERIFICATION_FILE, VORATIQ_VERIFICATION_SESSIONS_DIR, VORATIQ_VERIFICATION_TEMPLATES_DIR, } from "./structure.js";
10
+ import { updateManagedState } from "./managed-state.js";
11
+ import { resolveWorkspacePath, VORATIQ_AGENTS_FILE, VORATIQ_ENVIRONMENT_FILE, VORATIQ_INTERACTIVE_DIR, VORATIQ_INTERACTIVE_FILE, VORATIQ_INTERACTIVE_SESSIONS_DIR, VORATIQ_MANAGED_STATE_FILE, VORATIQ_MESSAGE_DIR, VORATIQ_MESSAGE_FILE, VORATIQ_MESSAGE_SESSIONS_DIR, VORATIQ_ORCHESTRATION_FILE, VORATIQ_REDUCTION_DIR, VORATIQ_REDUCTION_FILE, VORATIQ_REDUCTION_SESSIONS_DIR, VORATIQ_RUN_DIR, VORATIQ_RUN_FILE, VORATIQ_RUN_SESSIONS_DIR, VORATIQ_SANDBOX_FILE, VORATIQ_SPEC_DIR, VORATIQ_SPEC_FILE, VORATIQ_SPEC_SESSIONS_DIR, VORATIQ_VERIFICATION_CONFIG_FILE, VORATIQ_VERIFICATION_DIR, VORATIQ_VERIFICATION_FILE, VORATIQ_VERIFICATION_SESSIONS_DIR, VORATIQ_VERIFICATION_TEMPLATES_DIR, } from "./structure.js";
11
12
  import { buildDefaultAgentsTemplate, buildDefaultEnvironmentTemplate, buildDefaultSandboxTemplate, } from "./templates.js";
12
13
  import { buildDefaultVerificationConfigYaml, SHIPPED_VERIFICATION_TEMPLATES, } from "./verification-defaults.js";
13
14
  async function seedVerificationSurface(root, options = {}) {
@@ -99,6 +100,12 @@ const WORKSPACE_CONFIG_SEGMENTS = [
99
100
  VORATIQ_SANDBOX_FILE,
100
101
  VORATIQ_ORCHESTRATION_FILE,
101
102
  ];
103
+ const VOLATILE_WORKSPACE_EXCLUDE_PATTERNS = [
104
+ "/.voratiq/*/index.json",
105
+ "/.voratiq/*/sessions/",
106
+ "/.voratiq/*/history.lock",
107
+ ];
108
+ const VOLATILE_WORKSPACE_EXCLUDE_HEADER = "# voratiq runtime state (auto-managed)";
102
109
  export async function createWorkspace(root) {
103
110
  const createdDirectories = [];
104
111
  const createdFiles = [];
@@ -109,6 +116,7 @@ export async function createWorkspace(root) {
109
116
  const sandboxConfigPath = resolveWorkspacePath(root, VORATIQ_SANDBOX_FILE);
110
117
  const orchestrationConfigPath = resolveWorkspacePath(root, VORATIQ_ORCHESTRATION_FILE);
111
118
  const verificationConfigPath = resolveWorkspacePath(root, VORATIQ_VERIFICATION_CONFIG_FILE);
119
+ const managedStatePath = resolveWorkspacePath(root, VORATIQ_MANAGED_STATE_FILE);
112
120
  const workspaceExists = await pathExists(workspaceDir);
113
121
  const [agentsConfigExists, environmentConfigExists] = await Promise.all([
114
122
  pathExists(agentsConfigPath),
@@ -123,6 +131,7 @@ export async function createWorkspace(root) {
123
131
  await mkdir(workspaceDir, { recursive: true });
124
132
  createdDirectories.push(relativeToRoot(root, workspaceDir));
125
133
  }
134
+ await ensureWorkspaceGitExclude(root);
126
135
  for (const domain of domainStructures) {
127
136
  if (!(await pathExists(domain.directoryPath))) {
128
137
  await mkdir(domain.directoryPath, { recursive: true });
@@ -165,6 +174,20 @@ export async function createWorkspace(root) {
165
174
  });
166
175
  createdFiles.push(relativeToRoot(root, orchestrationConfigPath));
167
176
  }
177
+ if (!agentsConfigExists || !orchestrationConfigExists) {
178
+ const [agentsContent, orchestrationContent] = await Promise.all([
179
+ readFile(agentsConfigPath, "utf8"),
180
+ readFile(orchestrationConfigPath, "utf8"),
181
+ ]);
182
+ const managedStateResult = await updateManagedState(root, {
183
+ agentsContent,
184
+ orchestrationContent,
185
+ orchestrationPreset: "pro",
186
+ });
187
+ if (managedStateResult.created) {
188
+ createdFiles.push(relativeToRoot(root, managedStatePath));
189
+ }
190
+ }
168
191
  const seededVerification = await seedVerificationSurface(root, {
169
192
  verificationConfigPath,
170
193
  configExists: verificationExists,
@@ -178,6 +201,7 @@ export async function repairWorkspaceStructure(root, options = {}) {
178
201
  const createdFiles = [];
179
202
  const workspaceDir = resolveWorkspacePath(root);
180
203
  await ensureWorkspaceDirectoryEntry(root, workspaceDir);
204
+ await ensureWorkspaceGitExclude(root);
181
205
  // Additive repair must not mutate config semantics.
182
206
  for (const configPath of resolveWorkspaceConfigPaths(root)) {
183
207
  const kind = await detectPathKind(configPath);
@@ -359,6 +383,46 @@ async function detectPathKind(path) {
359
383
  }
360
384
  return "other";
361
385
  }
386
+ async function ensureWorkspaceGitExclude(root) {
387
+ const excludePath = await resolveGitExcludePath(root);
388
+ if (!excludePath) {
389
+ return;
390
+ }
391
+ const existing = (await pathExists(excludePath))
392
+ ? await readFile(excludePath, "utf8")
393
+ : "";
394
+ const lines = existing.split(/\r?\n/u);
395
+ const missingPatterns = VOLATILE_WORKSPACE_EXCLUDE_PATTERNS.filter((pattern) => !lines.includes(pattern));
396
+ if (missingPatterns.length === 0) {
397
+ return;
398
+ }
399
+ const nextLines = existing.length > 0 && !existing.endsWith("\n")
400
+ ? [...lines, VOLATILE_WORKSPACE_EXCLUDE_HEADER, ...missingPatterns]
401
+ : [
402
+ ...lines.filter((line, index, all) => !(index === all.length - 1 && line === "")),
403
+ VOLATILE_WORKSPACE_EXCLUDE_HEADER,
404
+ ...missingPatterns,
405
+ ];
406
+ await mkdir(dirname(excludePath), { recursive: true });
407
+ await writeFile(excludePath, `${nextLines.join("\n")}\n`, "utf8");
408
+ }
409
+ async function resolveGitExcludePath(root) {
410
+ const gitEntryPath = join(root, ".git");
411
+ const kind = await detectPathKind(gitEntryPath);
412
+ if (kind === "directory") {
413
+ return join(gitEntryPath, "info", "exclude");
414
+ }
415
+ if (kind !== "file") {
416
+ return undefined;
417
+ }
418
+ const raw = await readFile(gitEntryPath, "utf8");
419
+ const match = raw.match(/^gitdir:\s*(.+)\s*$/mu);
420
+ if (!match) {
421
+ return undefined;
422
+ }
423
+ const gitDir = resolveAbsolute(root, match[1]);
424
+ return join(gitDir, "info", "exclude");
425
+ }
362
426
  async function validateWorkspaceIndexFile(root, domain) {
363
427
  const displayPath = relativeToRoot(root, domain.indexPath);
364
428
  let raw;
@@ -1,3 +1,4 @@
1
+ export declare const WORKSPACE_SHIM_RELATIVE_PATH: readonly ["dist", "commands", "run", "shim", "run-agent-shim.mjs"];
1
2
  export declare function ensureWorkspaceShim(options: {
2
3
  workspacePath: string;
3
4
  cliInstallRoot?: string;
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  import { isFileSystemError } from "../utils/fs.js";
5
5
  import { resolvePath } from "../utils/path.js";
6
6
  import { WorkspaceSetupError } from "./errors.js";
7
- const SHIM_RELATIVE_PATH = [
7
+ export const WORKSPACE_SHIM_RELATIVE_PATH = [
8
8
  "dist",
9
9
  "commands",
10
10
  "run",
@@ -16,8 +16,8 @@ function resolveCliInstallRoot() {
16
16
  }
17
17
  export async function ensureWorkspaceShim(options) {
18
18
  const { workspacePath, cliInstallRoot = resolveCliInstallRoot() } = options;
19
- const sourcePath = resolvePath(cliInstallRoot, ...SHIM_RELATIVE_PATH);
20
- const targetPath = resolvePath(workspacePath, ...SHIM_RELATIVE_PATH);
19
+ const sourcePath = resolvePath(cliInstallRoot, ...WORKSPACE_SHIM_RELATIVE_PATH);
20
+ const targetPath = resolvePath(workspacePath, ...WORKSPACE_SHIM_RELATIVE_PATH);
21
21
  try {
22
22
  await access(sourcePath);
23
23
  await mkdir(dirname(targetPath), { recursive: true });
@@ -27,6 +27,7 @@ export declare const VORATIQ_VERIFICATION_CONFIG_FILE = "verification.yaml";
27
27
  export declare const VORATIQ_ENVIRONMENT_FILE = "environment.yaml";
28
28
  export declare const VORATIQ_SANDBOX_FILE = "sandbox.yaml";
29
29
  export declare const VORATIQ_ORCHESTRATION_FILE = "orchestration.yaml";
30
+ export declare const VORATIQ_MANAGED_STATE_FILE = "managed-state.json";
30
31
  export declare const WORKSPACE_DIRNAME = "workspace";
31
32
  export declare const CONTEXT_DIRNAME = "context";
32
33
  export declare const STDOUT_FILENAME = "stdout.log";
@@ -28,6 +28,7 @@ export const VORATIQ_VERIFICATION_CONFIG_FILE = "verification.yaml";
28
28
  export const VORATIQ_ENVIRONMENT_FILE = "environment.yaml";
29
29
  export const VORATIQ_SANDBOX_FILE = "sandbox.yaml";
30
30
  export const VORATIQ_ORCHESTRATION_FILE = "orchestration.yaml";
31
+ export const VORATIQ_MANAGED_STATE_FILE = "managed-state.json";
31
32
  export const WORKSPACE_DIRNAME = "workspace";
32
33
  export const CONTEXT_DIRNAME = "context";
33
34
  export const STDOUT_FILENAME = "stdout.log";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "voratiq",
3
- "version": "0.1.0-beta.21",
4
- "description": "Agent ensembles to design, generate, and select the best code for every task.",
3
+ "version": "0.1.0-beta.22",
4
+ "description": "Run workflows, delegate to swarms, and verify outputs before you apply them.",
5
5
  "keywords": [
6
6
  "agent-ensembles",
7
7
  "agent-orchestration",
@@ -1,15 +0,0 @@
1
- import { Command } from "commander";
2
- import type { InitCommandResult } from "../commands/init/types.js";
3
- import type { AgentPreset } from "../workspace/templates.js";
4
- import { type CommandOutputWriter } from "./output.js";
5
- export interface RunInitCommandResult extends InitCommandResult {
6
- body: string;
7
- }
8
- export interface InitCommandOptions {
9
- yes?: boolean;
10
- preset?: AgentPreset;
11
- presetProvided?: boolean;
12
- writeOutput?: CommandOutputWriter;
13
- }
14
- export declare function runInitCommand(options?: InitCommandOptions): Promise<RunInitCommandResult>;
15
- export declare function createInitCommand(): Command;