agent-inspect 3.5.4 → 4.0.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.
@@ -0,0 +1,483 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var promises = require('fs/promises');
5
+ var path = require('path');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var path__default = /*#__PURE__*/_interopDefault(path);
10
+
11
+ // packages/core/src/workspace/types.ts
12
+ var WORKSPACE_SCHEMA_VERSION = "1.0";
13
+ var WORKSPACE_DIR_NAME = ".agent-inspect";
14
+ var WORKSPACE_MANIFEST_FILENAME = "workspace.json";
15
+
16
+ // packages/core/src/workspace/manifest.ts
17
+ var DEFAULT_WORKSPACE_LAYOUT = {
18
+ traceDirs: ["runs"],
19
+ reportsDir: "reports",
20
+ artifactsDir: "artifacts",
21
+ bundlesDir: "bundles",
22
+ notesDir: "notes"
23
+ };
24
+ var DEFAULT_REDACTION_PROFILE = "share";
25
+ var REDACTION_PROFILES = [
26
+ "local",
27
+ "share",
28
+ "strict"
29
+ ];
30
+ var INDEX_TYPES = ["none", "sqlite", "custom"];
31
+ var MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;
32
+ function createDefaultWorkspaceManifest(options) {
33
+ const project = typeof options.project === "string" ? options.project.trim() : "";
34
+ const index = {
35
+ enabled: options.index?.enabled ?? false,
36
+ type: options.index?.type ?? "none",
37
+ ...options.index?.path !== void 0 ? { path: options.index.path } : {}
38
+ };
39
+ return {
40
+ schemaVersion: WORKSPACE_SCHEMA_VERSION,
41
+ project,
42
+ createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
43
+ traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
44
+ reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
45
+ artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
46
+ bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
47
+ notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
48
+ redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
49
+ index
50
+ };
51
+ }
52
+ function isPlainObject(value) {
53
+ return typeof value === "object" && value !== null && !Array.isArray(value);
54
+ }
55
+ function isSafeRelativeWorkspacePath(p) {
56
+ if (typeof p !== "string") return false;
57
+ const trimmed = p.trim();
58
+ if (trimmed === "") return false;
59
+ if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
60
+ if (/^[a-zA-Z]:/.test(trimmed)) return false;
61
+ const segments = trimmed.split(/[/\\]+/);
62
+ return !segments.some((seg) => seg === "..");
63
+ }
64
+ function validateDirField(value, field, errors) {
65
+ if (typeof value !== "string" || value.trim() === "") {
66
+ errors.push(`${field} must be a non-empty string`);
67
+ return;
68
+ }
69
+ if (!isSafeRelativeWorkspacePath(value)) {
70
+ errors.push(
71
+ `${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
72
+ );
73
+ }
74
+ }
75
+ function validateIndex(value, errors) {
76
+ if (!isPlainObject(value)) {
77
+ errors.push("index must be an object");
78
+ return void 0;
79
+ }
80
+ if (typeof value.enabled !== "boolean") {
81
+ errors.push("index.enabled must be a boolean");
82
+ }
83
+ if (!INDEX_TYPES.includes(value.type)) {
84
+ errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
85
+ }
86
+ if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
87
+ errors.push(
88
+ 'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
89
+ );
90
+ }
91
+ if (errors.length > 0) return void 0;
92
+ return {
93
+ enabled: value.enabled,
94
+ type: value.type,
95
+ ...value.path !== void 0 ? { path: value.path } : {}
96
+ };
97
+ }
98
+ function validateWorkspaceManifest(input) {
99
+ const errors = [];
100
+ const warnings = [];
101
+ if (!isPlainObject(input)) {
102
+ return { ok: false, errors: ["manifest must be an object"], warnings };
103
+ }
104
+ if (input.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {
105
+ errors.push(
106
+ `schemaVersion must be "${WORKSPACE_SCHEMA_VERSION}" (received ${JSON.stringify(
107
+ input.schemaVersion
108
+ )})`
109
+ );
110
+ }
111
+ if (typeof input.project !== "string" || input.project.trim() === "") {
112
+ errors.push("project must be a non-empty string");
113
+ }
114
+ if (typeof input.createdAt !== "string" || input.createdAt.trim() === "") {
115
+ errors.push("createdAt must be a non-empty ISO-8601 string");
116
+ } else if (Number.isNaN(Date.parse(input.createdAt))) {
117
+ errors.push("createdAt must be a valid ISO-8601 date string");
118
+ }
119
+ if (!Array.isArray(input.traceDirs) || input.traceDirs.length === 0) {
120
+ errors.push("traceDirs must be a non-empty array");
121
+ } else {
122
+ input.traceDirs.forEach((dir, i) => {
123
+ if (typeof dir !== "string" || dir.trim() === "") {
124
+ errors.push(`traceDirs[${i}] must be a non-empty string`);
125
+ } else if (!isSafeRelativeWorkspacePath(dir)) {
126
+ errors.push(
127
+ `traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or ".." traversal)`
128
+ );
129
+ }
130
+ });
131
+ }
132
+ validateDirField(input.reportsDir, "reportsDir", errors);
133
+ validateDirField(input.artifactsDir, "artifactsDir", errors);
134
+ validateDirField(input.bundlesDir, "bundlesDir", errors);
135
+ validateDirField(input.notesDir, "notesDir", errors);
136
+ if (!REDACTION_PROFILES.includes(input.redactionProfile)) {
137
+ errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES.join(", ")}`);
138
+ }
139
+ const indexErrors = [];
140
+ const index = validateIndex(input.index, indexErrors);
141
+ errors.push(...indexErrors);
142
+ if (index && index.type !== "none" && !index.enabled) {
143
+ warnings.push(`index.type is "${index.type}" but index.enabled is false`);
144
+ }
145
+ if (errors.length > 0 || index === void 0) {
146
+ return { ok: false, errors, warnings };
147
+ }
148
+ const manifest = {
149
+ schemaVersion: WORKSPACE_SCHEMA_VERSION,
150
+ project: input.project.trim(),
151
+ createdAt: input.createdAt,
152
+ traceDirs: input.traceDirs.map((d) => d.trim()),
153
+ reportsDir: input.reportsDir.trim(),
154
+ artifactsDir: input.artifactsDir.trim(),
155
+ bundlesDir: input.bundlesDir.trim(),
156
+ notesDir: input.notesDir.trim(),
157
+ redactionProfile: input.redactionProfile,
158
+ index
159
+ };
160
+ return { ok: true, manifest, errors, warnings };
161
+ }
162
+ function parseWorkspaceManifest(json) {
163
+ const warnings = [];
164
+ if (typeof json !== "string") {
165
+ return { ok: false, errors: ["manifest input must be a string"], warnings };
166
+ }
167
+ if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {
168
+ return {
169
+ ok: false,
170
+ errors: [
171
+ `manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`
172
+ ],
173
+ warnings
174
+ };
175
+ }
176
+ let parsed;
177
+ try {
178
+ parsed = JSON.parse(json);
179
+ } catch {
180
+ return { ok: false, errors: ["manifest is not valid JSON"], warnings };
181
+ }
182
+ return validateWorkspaceManifest(parsed);
183
+ }
184
+ function serializeWorkspaceManifest(manifest) {
185
+ return `${JSON.stringify(manifest, null, 2)}
186
+ `;
187
+ }
188
+ var INDEX_DIR_NAME = "index";
189
+ function resolveWorkspaceLocation(cwd = process.cwd()) {
190
+ const projectRoot = path__default.default.resolve(cwd);
191
+ const workspaceDir = path__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
192
+ return {
193
+ projectRoot,
194
+ workspaceDir,
195
+ manifestPath: path__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
196
+ };
197
+ }
198
+ function resolveInsideWorkspace(workspaceDir, relative) {
199
+ const base = path__default.default.resolve(workspaceDir);
200
+ const resolved = path__default.default.resolve(base, relative);
201
+ const rel = path__default.default.relative(base, resolved);
202
+ if (rel === "" || rel === "." || !rel.startsWith("..") && !path__default.default.isAbsolute(rel)) {
203
+ return resolved;
204
+ }
205
+ throw new Error(
206
+ `Workspace path "${relative}" resolves outside the workspace directory`
207
+ );
208
+ }
209
+ async function pathExists(p) {
210
+ try {
211
+ await promises.access(p);
212
+ return true;
213
+ } catch {
214
+ return false;
215
+ }
216
+ }
217
+ async function isWritable(p) {
218
+ try {
219
+ await promises.access(p, fs.constants.W_OK);
220
+ return true;
221
+ } catch {
222
+ return false;
223
+ }
224
+ }
225
+ async function listJsonl(dir) {
226
+ try {
227
+ const entries = await promises.readdir(dir);
228
+ return entries.filter((f) => f.endsWith(".jsonl"));
229
+ } catch {
230
+ return [];
231
+ }
232
+ }
233
+ async function countFiles(dir) {
234
+ try {
235
+ const entries = await promises.readdir(dir, { withFileTypes: true });
236
+ return entries.filter((e) => e.isFile()).length;
237
+ } catch {
238
+ return 0;
239
+ }
240
+ }
241
+ async function readWorkspaceManifestFile(location) {
242
+ let raw;
243
+ try {
244
+ raw = await promises.readFile(location.manifestPath, "utf-8");
245
+ } catch {
246
+ return { exists: false, ok: false, errors: ["workspace.json not found"], warnings: [] };
247
+ }
248
+ const parsed = parseWorkspaceManifest(raw);
249
+ return {
250
+ exists: true,
251
+ ok: parsed.ok,
252
+ ...parsed.manifest ? { manifest: parsed.manifest } : {},
253
+ errors: parsed.errors,
254
+ warnings: parsed.warnings
255
+ };
256
+ }
257
+ async function createWorkspace(options = {}) {
258
+ const location = resolveWorkspaceLocation(options.cwd);
259
+ const dryRun = options.dryRun === true;
260
+ const existing = await readWorkspaceManifestFile(location);
261
+ const topLevelTraces = await listJsonl(location.workspaceDir);
262
+ const detectedExistingTraces = topLevelTraces.length > 0;
263
+ let manifest;
264
+ let created;
265
+ let adopted;
266
+ if (existing.exists && existing.ok && existing.manifest) {
267
+ manifest = existing.manifest;
268
+ created = false;
269
+ adopted = true;
270
+ } else {
271
+ const project = options.project?.trim() || path__default.default.basename(location.projectRoot) || "workspace";
272
+ const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
273
+ manifest = createDefaultWorkspaceManifest({
274
+ project,
275
+ traceDirs,
276
+ ...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
277
+ });
278
+ created = true;
279
+ adopted = detectedExistingTraces || existing.exists && !existing.ok;
280
+ }
281
+ const relDirs = uniqueDirs([
282
+ ...manifest.traceDirs.filter((d) => d !== "."),
283
+ manifest.reportsDir,
284
+ manifest.artifactsDir,
285
+ manifest.bundlesDir,
286
+ manifest.notesDir,
287
+ INDEX_DIR_NAME
288
+ ]);
289
+ const createdDirs = [];
290
+ for (const rel of relDirs) {
291
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
292
+ if (await pathExists(abs)) continue;
293
+ createdDirs.push(rel);
294
+ if (!dryRun) await promises.mkdir(abs, { recursive: true });
295
+ }
296
+ if (!dryRun && created) {
297
+ await promises.mkdir(location.workspaceDir, { recursive: true });
298
+ await promises.writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
299
+ }
300
+ return {
301
+ location,
302
+ manifest,
303
+ created,
304
+ adopted,
305
+ createdDirs,
306
+ detectedExistingTraces,
307
+ dryRun
308
+ };
309
+ }
310
+ function uniqueDirs(dirs) {
311
+ const seen = /* @__PURE__ */ new Set();
312
+ const out = [];
313
+ for (const d of dirs) {
314
+ const t = d.trim();
315
+ if (t === "" || t === "." || seen.has(t)) continue;
316
+ seen.add(t);
317
+ out.push(t);
318
+ }
319
+ return out;
320
+ }
321
+ async function getWorkspaceStatus(location, manifest) {
322
+ let traceFiles = 0;
323
+ for (const rel of manifest.traceDirs) {
324
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
325
+ traceFiles += (await listJsonl(abs)).length;
326
+ }
327
+ const reports = await countFiles(
328
+ resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir)
329
+ );
330
+ const artifacts = await countFiles(
331
+ resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir)
332
+ );
333
+ const bundles = await countFiles(
334
+ resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir)
335
+ );
336
+ const notes = await countFiles(
337
+ resolveInsideWorkspace(location.workspaceDir, manifest.notesDir)
338
+ );
339
+ const indexPath = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
340
+ return {
341
+ project: manifest.project,
342
+ traceFiles,
343
+ reports,
344
+ artifacts,
345
+ bundles,
346
+ notes,
347
+ index: {
348
+ enabled: manifest.index.enabled,
349
+ type: manifest.index.type,
350
+ exists: await pathExists(indexPath)
351
+ }
352
+ };
353
+ }
354
+ async function doctorWorkspace(location) {
355
+ const checks = [];
356
+ const manifestResult = await readWorkspaceManifestFile(location);
357
+ if (!manifestResult.exists) {
358
+ checks.push({
359
+ id: "manifest",
360
+ status: "fail",
361
+ message: "workspace.json not found (run `agent-inspect workspace init`)"
362
+ });
363
+ return { ok: false, checks };
364
+ }
365
+ if (!manifestResult.ok || !manifestResult.manifest) {
366
+ checks.push({
367
+ id: "manifest",
368
+ status: "fail",
369
+ message: `workspace.json is invalid: ${manifestResult.errors.join("; ")}`
370
+ });
371
+ return { ok: false, checks };
372
+ }
373
+ const manifest = manifestResult.manifest;
374
+ checks.push({ id: "manifest", status: "pass", message: "workspace.json is valid" });
375
+ for (const warning of manifestResult.warnings) {
376
+ checks.push({ id: "manifest-warning", status: "warn", message: warning });
377
+ }
378
+ const dirFields = [
379
+ ...manifest.traceDirs.filter((d) => d !== ".").map((d, i) => [`traceDir[${i}]`, d]),
380
+ ["reportsDir", manifest.reportsDir],
381
+ ["artifactsDir", manifest.artifactsDir],
382
+ ["bundlesDir", manifest.bundlesDir],
383
+ ["notesDir", manifest.notesDir]
384
+ ];
385
+ for (const [id, rel] of dirFields) {
386
+ let abs;
387
+ try {
388
+ abs = resolveInsideWorkspace(location.workspaceDir, rel);
389
+ } catch (error) {
390
+ checks.push({
391
+ id,
392
+ status: "fail",
393
+ message: error instanceof Error ? error.message : String(error)
394
+ });
395
+ continue;
396
+ }
397
+ if (!await pathExists(abs)) {
398
+ checks.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
399
+ } else if (!await isWritable(abs)) {
400
+ checks.push({ id, status: "fail", message: `${rel}/ is not writable` });
401
+ } else {
402
+ checks.push({ id, status: "pass", message: `${rel}/ is present and writable` });
403
+ }
404
+ }
405
+ let newestTraceMtime = 0;
406
+ for (const rel of manifest.traceDirs) {
407
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
408
+ for (const file of await listJsonl(abs)) {
409
+ try {
410
+ const s = await promises.stat(path__default.default.join(abs, file));
411
+ newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
412
+ } catch {
413
+ checks.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
414
+ }
415
+ }
416
+ }
417
+ if (manifest.index.enabled) {
418
+ const indexPath = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
419
+ if (!await pathExists(indexPath)) {
420
+ checks.push({ id: "index", status: "warn", message: "index enabled but not built" });
421
+ } else {
422
+ try {
423
+ const s = await promises.stat(indexPath);
424
+ if (newestTraceMtime > s.mtimeMs) {
425
+ checks.push({ id: "index", status: "warn", message: "index is stale (traces are newer)" });
426
+ } else {
427
+ checks.push({ id: "index", status: "pass", message: "index is present" });
428
+ }
429
+ } catch {
430
+ checks.push({ id: "index", status: "warn", message: "cannot stat index" });
431
+ }
432
+ }
433
+ }
434
+ const ok = !checks.some((c) => c.status === "fail");
435
+ return { ok, checks };
436
+ }
437
+ async function cleanWorkspace(location, manifest, options = {}) {
438
+ const dryRun = options.confirm !== true;
439
+ const targets = uniqueDirs([
440
+ manifest.reportsDir,
441
+ manifest.artifactsDir,
442
+ manifest.bundlesDir,
443
+ manifest.index.path ?? INDEX_DIR_NAME
444
+ ]);
445
+ const removed = [];
446
+ for (const rel of targets) {
447
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
448
+ let entries;
449
+ try {
450
+ entries = await promises.readdir(abs);
451
+ } catch {
452
+ continue;
453
+ }
454
+ for (const entry of entries) {
455
+ const relPath = `${rel}/${entry}`;
456
+ removed.push(relPath);
457
+ if (!dryRun) {
458
+ await promises.rm(path__default.default.join(abs, entry), { recursive: true, force: true });
459
+ }
460
+ }
461
+ }
462
+ return { dryRun, removed };
463
+ }
464
+
465
+ exports.DEFAULT_WORKSPACE_LAYOUT = DEFAULT_WORKSPACE_LAYOUT;
466
+ exports.MAX_WORKSPACE_MANIFEST_BYTES = MAX_WORKSPACE_MANIFEST_BYTES;
467
+ exports.WORKSPACE_DIR_NAME = WORKSPACE_DIR_NAME;
468
+ exports.WORKSPACE_MANIFEST_FILENAME = WORKSPACE_MANIFEST_FILENAME;
469
+ exports.WORKSPACE_SCHEMA_VERSION = WORKSPACE_SCHEMA_VERSION;
470
+ exports.cleanWorkspace = cleanWorkspace;
471
+ exports.createDefaultWorkspaceManifest = createDefaultWorkspaceManifest;
472
+ exports.createWorkspace = createWorkspace;
473
+ exports.doctorWorkspace = doctorWorkspace;
474
+ exports.getWorkspaceStatus = getWorkspaceStatus;
475
+ exports.isSafeRelativeWorkspacePath = isSafeRelativeWorkspacePath;
476
+ exports.parseWorkspaceManifest = parseWorkspaceManifest;
477
+ exports.readWorkspaceManifestFile = readWorkspaceManifestFile;
478
+ exports.resolveInsideWorkspace = resolveInsideWorkspace;
479
+ exports.resolveWorkspaceLocation = resolveWorkspaceLocation;
480
+ exports.serializeWorkspaceManifest = serializeWorkspaceManifest;
481
+ exports.validateWorkspaceManifest = validateWorkspaceManifest;
482
+ //# sourceMappingURL=workspace.cjs.map
483
+ //# sourceMappingURL=workspace.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workspace/types.ts","../src/workspace/manifest.ts","../src/workspace/fs.ts"],"names":["path","access","constants","readdir","readFile","mkdir","writeFile","stat","rm"],"mappings":"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","file":"workspace.cjs","sourcesContent":["/**\n * Internal workspace manifest types (v4.0).\n *\n * @remarks\n * Experimental and internal to `@agent-inspect/core`. This module is not part\n * of any published entry point. Adding a public `agent-inspect/workspace`\n * export is a separate, maintainer-gated step (see\n * `docs/proposals/LOCAL-TRACE-WORKSPACE.md`).\n */\n\n/** Fixed manifest schema version for the v4.0 workspace model. */\nexport const WORKSPACE_SCHEMA_VERSION = \"1.0\" as const;\n\n/** Standard workspace directory name at a project root. */\nexport const WORKSPACE_DIR_NAME = \".agent-inspect\" as const;\n\n/** Standard manifest filename inside {@link WORKSPACE_DIR_NAME}. */\nexport const WORKSPACE_MANIFEST_FILENAME = \"workspace.json\" as const;\n\n/** Default share-safety posture applied to a workspace. */\nexport type WorkspaceRedactionProfile = \"local\" | \"share\" | \"strict\";\n\n/** Optional local index kind (SQLite index arrives as an opt-in package in v4.1). */\nexport type WorkspaceIndexType = \"none\" | \"sqlite\" | \"custom\";\n\n/** Optional local index descriptor. */\nexport interface WorkspaceIndexConfig {\n enabled: boolean;\n type: WorkspaceIndexType;\n path?: string;\n}\n\n/**\n * The `.agent-inspect/workspace.json` manifest.\n *\n * @remarks\n * All directory fields are paths relative to the workspace root and must\n * resolve inside it (no absolute paths, no `..` traversal).\n */\nexport interface AgentInspectWorkspaceManifest {\n schemaVersion: typeof WORKSPACE_SCHEMA_VERSION;\n project: string;\n createdAt: string;\n traceDirs: string[];\n reportsDir: string;\n artifactsDir: string;\n bundlesDir: string;\n notesDir: string;\n redactionProfile: WorkspaceRedactionProfile;\n index: WorkspaceIndexConfig;\n}\n\n/** Result of validating unknown input against the manifest contract. */\nexport interface WorkspaceManifestValidationResult {\n ok: boolean;\n manifest?: AgentInspectWorkspaceManifest;\n errors: string[];\n warnings: string[];\n}\n","import {\n WORKSPACE_SCHEMA_VERSION,\n type AgentInspectWorkspaceManifest,\n type WorkspaceIndexConfig,\n type WorkspaceIndexType,\n type WorkspaceManifestValidationResult,\n type WorkspaceRedactionProfile,\n} from \"./types.js\";\n\n/**\n * Internal workspace manifest validation + default generation (v4.0).\n *\n * @remarks\n * Pure, non-throwing helpers. Validation is conservative: unknown or malformed\n * input is rejected with clear messages rather than coerced. No filesystem or\n * network access happens here (filesystem helpers arrive in a later chunk).\n */\n\n/** Default relative layout used when generating a fresh manifest. */\nexport const DEFAULT_WORKSPACE_LAYOUT = {\n traceDirs: [\"runs\"],\n reportsDir: \"reports\",\n artifactsDir: \"artifacts\",\n bundlesDir: \"bundles\",\n notesDir: \"notes\",\n} as const;\n\nconst DEFAULT_REDACTION_PROFILE: WorkspaceRedactionProfile = \"share\";\n\nconst REDACTION_PROFILES: readonly WorkspaceRedactionProfile[] = [\n \"local\",\n \"share\",\n \"strict\",\n];\n\nconst INDEX_TYPES: readonly WorkspaceIndexType[] = [\"none\", \"sqlite\", \"custom\"];\n\n/** Upper bound on serialized manifest input accepted by {@link parseWorkspaceManifest}. */\nexport const MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;\n\n/** Options for {@link createDefaultWorkspaceManifest}. */\nexport interface CreateWorkspaceManifestOptions {\n project: string;\n createdAt?: string;\n traceDirs?: string[];\n reportsDir?: string;\n artifactsDir?: string;\n bundlesDir?: string;\n notesDir?: string;\n redactionProfile?: WorkspaceRedactionProfile;\n index?: Partial<WorkspaceIndexConfig>;\n}\n\n/**\n * Generates a default workspace manifest for a project using the standard\n * layout. The returned object is always shape-valid.\n */\nexport function createDefaultWorkspaceManifest(\n options: CreateWorkspaceManifestOptions,\n): AgentInspectWorkspaceManifest {\n const project = typeof options.project === \"string\" ? options.project.trim() : \"\";\n const index: WorkspaceIndexConfig = {\n enabled: options.index?.enabled ?? false,\n type: options.index?.type ?? \"none\",\n ...(options.index?.path !== undefined ? { path: options.index.path } : {}),\n };\n\n return {\n schemaVersion: WORKSPACE_SCHEMA_VERSION,\n project,\n createdAt: options.createdAt ?? new Date().toISOString(),\n traceDirs: options.traceDirs\n ? [...options.traceDirs]\n : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],\n reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,\n artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,\n bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,\n notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,\n redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,\n index,\n };\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Returns true when `p` is a non-empty relative path that stays within the\n * workspace root: no absolute paths, no `..` traversal, no Windows drive roots.\n */\nexport function isSafeRelativeWorkspacePath(p: unknown): p is string {\n if (typeof p !== \"string\") return false;\n const trimmed = p.trim();\n if (trimmed === \"\") return false;\n if (trimmed.startsWith(\"/\") || trimmed.startsWith(\"\\\\\")) return false;\n if (/^[a-zA-Z]:/.test(trimmed)) return false;\n const segments = trimmed.split(/[/\\\\]+/);\n return !segments.some((seg) => seg === \"..\");\n}\n\nfunction validateDirField(\n value: unknown,\n field: string,\n errors: string[],\n): void {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n errors.push(`${field} must be a non-empty string`);\n return;\n }\n if (!isSafeRelativeWorkspacePath(value)) {\n errors.push(\n `${field} must be a relative path inside the workspace (no absolute paths or \"..\" traversal)`,\n );\n }\n}\n\nfunction validateIndex(value: unknown, errors: string[]): WorkspaceIndexConfig | undefined {\n if (!isPlainObject(value)) {\n errors.push(\"index must be an object\");\n return undefined;\n }\n if (typeof value.enabled !== \"boolean\") {\n errors.push(\"index.enabled must be a boolean\");\n }\n if (!INDEX_TYPES.includes(value.type as WorkspaceIndexType)) {\n errors.push(`index.type must be one of: ${INDEX_TYPES.join(\", \")}`);\n }\n if (value.path !== undefined && !isSafeRelativeWorkspacePath(value.path)) {\n errors.push(\n 'index.path must be a relative path inside the workspace (no absolute paths or \"..\" traversal)',\n );\n }\n if (errors.length > 0) return undefined;\n return {\n enabled: value.enabled as boolean,\n type: value.type as WorkspaceIndexType,\n ...(value.path !== undefined ? { path: value.path as string } : {}),\n };\n}\n\n/**\n * Conservatively validates unknown input against the workspace manifest\n * contract. Never throws; returns a result with `ok`, the normalized\n * `manifest` (when valid), `errors`, and non-fatal `warnings`.\n */\nexport function validateWorkspaceManifest(\n input: unknown,\n): WorkspaceManifestValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!isPlainObject(input)) {\n return { ok: false, errors: [\"manifest must be an object\"], warnings };\n }\n\n if (input.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {\n errors.push(\n `schemaVersion must be \"${WORKSPACE_SCHEMA_VERSION}\" (received ${JSON.stringify(\n input.schemaVersion,\n )})`,\n );\n }\n\n if (typeof input.project !== \"string\" || input.project.trim() === \"\") {\n errors.push(\"project must be a non-empty string\");\n }\n\n if (typeof input.createdAt !== \"string\" || input.createdAt.trim() === \"\") {\n errors.push(\"createdAt must be a non-empty ISO-8601 string\");\n } else if (Number.isNaN(Date.parse(input.createdAt))) {\n errors.push(\"createdAt must be a valid ISO-8601 date string\");\n }\n\n if (!Array.isArray(input.traceDirs) || input.traceDirs.length === 0) {\n errors.push(\"traceDirs must be a non-empty array\");\n } else {\n input.traceDirs.forEach((dir, i) => {\n if (typeof dir !== \"string\" || dir.trim() === \"\") {\n errors.push(`traceDirs[${i}] must be a non-empty string`);\n } else if (!isSafeRelativeWorkspacePath(dir)) {\n errors.push(\n `traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or \"..\" traversal)`,\n );\n }\n });\n }\n\n validateDirField(input.reportsDir, \"reportsDir\", errors);\n validateDirField(input.artifactsDir, \"artifactsDir\", errors);\n validateDirField(input.bundlesDir, \"bundlesDir\", errors);\n validateDirField(input.notesDir, \"notesDir\", errors);\n\n if (!REDACTION_PROFILES.includes(input.redactionProfile as WorkspaceRedactionProfile)) {\n errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES.join(\", \")}`);\n }\n\n const indexErrors: string[] = [];\n const index = validateIndex(input.index, indexErrors);\n errors.push(...indexErrors);\n\n if (index && index.type !== \"none\" && !index.enabled) {\n warnings.push(`index.type is \"${index.type}\" but index.enabled is false`);\n }\n\n if (errors.length > 0 || index === undefined) {\n return { ok: false, errors, warnings };\n }\n\n const manifest: AgentInspectWorkspaceManifest = {\n schemaVersion: WORKSPACE_SCHEMA_VERSION,\n project: (input.project as string).trim(),\n createdAt: input.createdAt as string,\n traceDirs: (input.traceDirs as string[]).map((d) => d.trim()),\n reportsDir: (input.reportsDir as string).trim(),\n artifactsDir: (input.artifactsDir as string).trim(),\n bundlesDir: (input.bundlesDir as string).trim(),\n notesDir: (input.notesDir as string).trim(),\n redactionProfile: input.redactionProfile as WorkspaceRedactionProfile,\n index,\n };\n\n return { ok: true, manifest, errors, warnings };\n}\n\n/**\n * Safely parses a serialized manifest string and validates it. Bounds input\n * size and rejects invalid JSON without throwing.\n */\nexport function parseWorkspaceManifest(\n json: string,\n): WorkspaceManifestValidationResult {\n const warnings: string[] = [];\n\n if (typeof json !== \"string\") {\n return { ok: false, errors: [\"manifest input must be a string\"], warnings };\n }\n if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {\n return {\n ok: false,\n errors: [\n `manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`,\n ],\n warnings,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(json) as unknown;\n } catch {\n return { ok: false, errors: [\"manifest is not valid JSON\"], warnings };\n }\n\n return validateWorkspaceManifest(parsed);\n}\n\n/** Serializes a manifest to deterministic, pretty-printed JSON with a trailing newline. */\nexport function serializeWorkspaceManifest(\n manifest: AgentInspectWorkspaceManifest,\n): string {\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n}\n","import { constants } from \"node:fs\";\nimport { access, mkdir, readdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n createDefaultWorkspaceManifest,\n parseWorkspaceManifest,\n serializeWorkspaceManifest,\n} from \"./manifest.js\";\nimport {\n WORKSPACE_DIR_NAME,\n WORKSPACE_MANIFEST_FILENAME,\n type AgentInspectWorkspaceManifest,\n type WorkspaceRedactionProfile,\n} from \"./types.js\";\n\n/**\n * Internal workspace filesystem helpers (v4.0).\n *\n * @remarks\n * Local-only. Never deletes trace files. All manifest-derived paths are\n * resolved and confirmed to stay within the workspace directory\n * (path-traversal guarded). No network access.\n */\n\n/** Resolved on-disk location of a workspace. */\nexport interface WorkspaceLocation {\n /** Project directory that contains the `.agent-inspect` folder. */\n projectRoot: string;\n /** The `.agent-inspect` workspace directory (root for relative manifest paths). */\n workspaceDir: string;\n /** Absolute path to `workspace.json`. */\n manifestPath: string;\n}\n\nconst INDEX_DIR_NAME = \"index\";\n\n/** Resolves the workspace location for a given project directory. */\nexport function resolveWorkspaceLocation(cwd: string = process.cwd()): WorkspaceLocation {\n const projectRoot = path.resolve(cwd);\n const workspaceDir = path.join(projectRoot, WORKSPACE_DIR_NAME);\n return {\n projectRoot,\n workspaceDir,\n manifestPath: path.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME),\n };\n}\n\n/**\n * Resolves a manifest-relative path against the workspace directory, rejecting\n * any path that escapes it.\n */\nexport function resolveInsideWorkspace(workspaceDir: string, relative: string): string {\n const base = path.resolve(workspaceDir);\n const resolved = path.resolve(base, relative);\n const rel = path.relative(base, resolved);\n if (rel === \"\" || rel === \".\" || (!rel.startsWith(\"..\") && !path.isAbsolute(rel))) {\n return resolved;\n }\n throw new Error(\n `Workspace path \"${relative}\" resolves outside the workspace directory`,\n );\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isWritable(p: string): Promise<boolean> {\n try {\n await access(p, constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function listJsonl(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir);\n return entries.filter((f) => f.endsWith(\".jsonl\"));\n } catch {\n return [];\n }\n}\n\nasync function countFiles(dir: string): Promise<number> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isFile()).length;\n } catch {\n return 0;\n }\n}\n\n/** Result of reading a workspace manifest from disk. */\nexport interface ReadWorkspaceManifestResult {\n exists: boolean;\n ok: boolean;\n manifest?: AgentInspectWorkspaceManifest;\n errors: string[];\n warnings: string[];\n}\n\n/** Reads and validates `workspace.json` at the given location. Never throws. */\nexport async function readWorkspaceManifestFile(\n location: WorkspaceLocation,\n): Promise<ReadWorkspaceManifestResult> {\n let raw: string;\n try {\n raw = await readFile(location.manifestPath, \"utf-8\");\n } catch {\n return { exists: false, ok: false, errors: [\"workspace.json not found\"], warnings: [] };\n }\n const parsed = parseWorkspaceManifest(raw);\n return {\n exists: true,\n ok: parsed.ok,\n ...(parsed.manifest ? { manifest: parsed.manifest } : {}),\n errors: parsed.errors,\n warnings: parsed.warnings,\n };\n}\n\n/** Options for {@link createWorkspace}. */\nexport interface CreateWorkspaceOptions {\n cwd?: string;\n project?: string;\n redactionProfile?: WorkspaceRedactionProfile;\n /** Preview only: do not write anything to disk. */\n dryRun?: boolean;\n}\n\n/** Outcome of {@link createWorkspace}. */\nexport interface CreateWorkspaceResult {\n location: WorkspaceLocation;\n manifest: AgentInspectWorkspaceManifest;\n /** True when a fresh manifest was written. */\n created: boolean;\n /** True when an existing workspace/trace directory was adopted without rewrite. */\n adopted: boolean;\n /** Relative directories created (or that would be created in dry-run). */\n createdDirs: string[];\n /** True when top-level `.jsonl` traces were detected and preserved. */\n detectedExistingTraces: boolean;\n dryRun: boolean;\n}\n\n/**\n * Creates or adopts a workspace. Never deletes or rewrites existing traces.\n * When a manifest already exists it is adopted (missing folders are created,\n * the manifest is left untouched).\n */\nexport async function createWorkspace(\n options: CreateWorkspaceOptions = {},\n): Promise<CreateWorkspaceResult> {\n const location = resolveWorkspaceLocation(options.cwd);\n const dryRun = options.dryRun === true;\n\n const existing = await readWorkspaceManifestFile(location);\n const topLevelTraces = await listJsonl(location.workspaceDir);\n const detectedExistingTraces = topLevelTraces.length > 0;\n\n let manifest: AgentInspectWorkspaceManifest;\n let created: boolean;\n let adopted: boolean;\n\n if (existing.exists && existing.ok && existing.manifest) {\n manifest = existing.manifest;\n created = false;\n adopted = true;\n } else {\n const project =\n options.project?.trim() ||\n path.basename(location.projectRoot) ||\n \"workspace\";\n const traceDirs = detectedExistingTraces ? [\"runs\", \".\"] : [\"runs\"];\n manifest = createDefaultWorkspaceManifest({\n project,\n traceDirs,\n ...(options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}),\n });\n created = true;\n adopted = detectedExistingTraces || (existing.exists && !existing.ok);\n }\n\n const relDirs = uniqueDirs([\n ...manifest.traceDirs.filter((d) => d !== \".\"),\n manifest.reportsDir,\n manifest.artifactsDir,\n manifest.bundlesDir,\n manifest.notesDir,\n INDEX_DIR_NAME,\n ]);\n\n const createdDirs: string[] = [];\n for (const rel of relDirs) {\n const abs = resolveInsideWorkspace(location.workspaceDir, rel);\n if (await pathExists(abs)) continue;\n createdDirs.push(rel);\n if (!dryRun) await mkdir(abs, { recursive: true });\n }\n\n if (!dryRun && created) {\n await mkdir(location.workspaceDir, { recursive: true });\n await writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), \"utf-8\");\n }\n\n return {\n location,\n manifest,\n created,\n adopted,\n createdDirs,\n detectedExistingTraces,\n dryRun,\n };\n}\n\nfunction uniqueDirs(dirs: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const d of dirs) {\n const t = d.trim();\n if (t === \"\" || t === \".\" || seen.has(t)) continue;\n seen.add(t);\n out.push(t);\n }\n return out;\n}\n\n/** Index presence/status for {@link getWorkspaceStatus}. */\nexport interface WorkspaceIndexStatus {\n enabled: boolean;\n type: string;\n exists: boolean;\n}\n\n/** Aggregate, read-only workspace status. */\nexport interface WorkspaceStatus {\n project: string;\n traceFiles: number;\n reports: number;\n artifacts: number;\n bundles: number;\n notes: number;\n index: WorkspaceIndexStatus;\n}\n\n/** Computes read-only counts for a workspace. Requires a valid manifest. */\nexport async function getWorkspaceStatus(\n location: WorkspaceLocation,\n manifest: AgentInspectWorkspaceManifest,\n): Promise<WorkspaceStatus> {\n let traceFiles = 0;\n for (const rel of manifest.traceDirs) {\n const abs = resolveInsideWorkspace(location.workspaceDir, rel);\n traceFiles += (await listJsonl(abs)).length;\n }\n\n const reports = await countFiles(\n resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir),\n );\n const artifacts = await countFiles(\n resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir),\n );\n const bundles = await countFiles(\n resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir),\n );\n const notes = await countFiles(\n resolveInsideWorkspace(location.workspaceDir, manifest.notesDir),\n );\n\n const indexPath = manifest.index.path\n ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path)\n : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);\n\n return {\n project: manifest.project,\n traceFiles,\n reports,\n artifacts,\n bundles,\n notes,\n index: {\n enabled: manifest.index.enabled,\n type: manifest.index.type,\n exists: await pathExists(indexPath),\n },\n };\n}\n\n/** A single workspace doctor check. */\nexport interface WorkspaceDoctorCheck {\n id: string;\n status: \"pass\" | \"warn\" | \"fail\";\n message: string;\n}\n\n/** Result of {@link doctorWorkspace}. */\nexport interface WorkspaceDoctorResult {\n ok: boolean;\n checks: WorkspaceDoctorCheck[];\n}\n\n/**\n * Validates a workspace: manifest presence/shape, folder permissions, trace\n * readability, and index staleness. Read-only; never throws.\n */\nexport async function doctorWorkspace(\n location: WorkspaceLocation,\n): Promise<WorkspaceDoctorResult> {\n const checks: WorkspaceDoctorCheck[] = [];\n const manifestResult = await readWorkspaceManifestFile(location);\n\n if (!manifestResult.exists) {\n checks.push({\n id: \"manifest\",\n status: \"fail\",\n message: \"workspace.json not found (run `agent-inspect workspace init`)\",\n });\n return { ok: false, checks };\n }\n if (!manifestResult.ok || !manifestResult.manifest) {\n checks.push({\n id: \"manifest\",\n status: \"fail\",\n message: `workspace.json is invalid: ${manifestResult.errors.join(\"; \")}`,\n });\n return { ok: false, checks };\n }\n\n const manifest = manifestResult.manifest;\n checks.push({ id: \"manifest\", status: \"pass\", message: \"workspace.json is valid\" });\n for (const warning of manifestResult.warnings) {\n checks.push({ id: \"manifest-warning\", status: \"warn\", message: warning });\n }\n\n const dirFields: Array<[string, string]> = [\n ...manifest.traceDirs\n .filter((d) => d !== \".\")\n .map((d, i) => [`traceDir[${i}]`, d] as [string, string]),\n [\"reportsDir\", manifest.reportsDir],\n [\"artifactsDir\", manifest.artifactsDir],\n [\"bundlesDir\", manifest.bundlesDir],\n [\"notesDir\", manifest.notesDir],\n ];\n\n for (const [id, rel] of dirFields) {\n let abs: string;\n try {\n abs = resolveInsideWorkspace(location.workspaceDir, rel);\n } catch (error) {\n checks.push({\n id,\n status: \"fail\",\n message: error instanceof Error ? error.message : String(error),\n });\n continue;\n }\n if (!(await pathExists(abs))) {\n checks.push({ id, status: \"warn\", message: `${rel}/ does not exist yet` });\n } else if (!(await isWritable(abs))) {\n checks.push({ id, status: \"fail\", message: `${rel}/ is not writable` });\n } else {\n checks.push({ id, status: \"pass\", message: `${rel}/ is present and writable` });\n }\n }\n\n // Trace readability + index staleness.\n let newestTraceMtime = 0;\n for (const rel of manifest.traceDirs) {\n const abs = resolveInsideWorkspace(location.workspaceDir, rel);\n for (const file of await listJsonl(abs)) {\n try {\n const s = await stat(path.join(abs, file));\n newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);\n } catch {\n checks.push({ id: \"trace-readability\", status: \"warn\", message: `cannot stat ${rel}/${file}` });\n }\n }\n }\n\n if (manifest.index.enabled) {\n const indexPath = manifest.index.path\n ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path)\n : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);\n if (!(await pathExists(indexPath))) {\n checks.push({ id: \"index\", status: \"warn\", message: \"index enabled but not built\" });\n } else {\n try {\n const s = await stat(indexPath);\n if (newestTraceMtime > s.mtimeMs) {\n checks.push({ id: \"index\", status: \"warn\", message: \"index is stale (traces are newer)\" });\n } else {\n checks.push({ id: \"index\", status: \"pass\", message: \"index is present\" });\n }\n } catch {\n checks.push({ id: \"index\", status: \"warn\", message: \"cannot stat index\" });\n }\n }\n }\n\n const ok = !checks.some((c) => c.status === \"fail\");\n return { ok, checks };\n}\n\n/** Options for {@link cleanWorkspace}. */\nexport interface CleanWorkspaceOptions {\n /** Actually delete. When false (default), the operation is a dry-run. */\n confirm?: boolean;\n}\n\n/** Result of {@link cleanWorkspace}. */\nexport interface CleanWorkspaceResult {\n dryRun: boolean;\n /** Relative paths removed (or that would be removed in dry-run). */\n removed: string[];\n}\n\n/**\n * Removes generated workspace content (reports, artifacts, bundles, index).\n * Dry-run by default; trace directories are never touched.\n */\nexport async function cleanWorkspace(\n location: WorkspaceLocation,\n manifest: AgentInspectWorkspaceManifest,\n options: CleanWorkspaceOptions = {},\n): Promise<CleanWorkspaceResult> {\n const dryRun = options.confirm !== true;\n const targets = uniqueDirs([\n manifest.reportsDir,\n manifest.artifactsDir,\n manifest.bundlesDir,\n manifest.index.path ?? INDEX_DIR_NAME,\n ]);\n\n const removed: string[] = [];\n for (const rel of targets) {\n const abs = resolveInsideWorkspace(location.workspaceDir, rel);\n let entries: string[];\n try {\n entries = await readdir(abs);\n } catch {\n continue;\n }\n for (const entry of entries) {\n const relPath = `${rel}/${entry}`;\n removed.push(relPath);\n if (!dryRun) {\n await rm(path.join(abs, entry), { recursive: true, force: true });\n }\n }\n }\n\n return { dryRun, removed };\n}\n"]}