@skein-code/cli 0.3.10 → 0.3.11

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.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { createInterface as createInterface2 } from "node:readline/promises";
5
5
  import { stdin as input, stdout as output } from "node:process";
6
6
  import { writeFile as writeFile3 } from "node:fs/promises";
7
- import { basename as basename12, resolve as resolve22 } from "node:path";
7
+ import { basename as basename12, resolve as resolve23 } from "node:path";
8
8
  import { Command, Option } from "commander";
9
9
  import chalk4 from "chalk";
10
10
 
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
220
220
  // package.json
221
221
  var package_default = {
222
222
  name: "@skein-code/cli",
223
- version: "0.3.10",
223
+ version: "0.3.11",
224
224
  description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
225
225
  type: "module",
226
226
  license: "MIT",
@@ -236,9 +236,9 @@ var package_default = {
236
236
  },
237
237
  skein: {
238
238
  releaseNotes: [
239
- "Complex executable requests now use a durable Task Contract with evidence-bound acceptance criteria",
240
- "Completion stays unverified while required acceptance or final verification remains unresolved",
241
- "Structured tool failure receipts add concise repair hints, retry budgets, cancellation handling, and identical-call circuit breaking"
239
+ "Large tool results now use a language-aware dynamic token budget with status-preserving head and tail receipts",
240
+ "Complete captured output can be read back through redacted, expiring, session-scoped local artifacts",
241
+ "Shell and MCP source truncation is explicit, and session deletion removes retained output"
242
242
  ]
243
243
  },
244
244
  bin: {
@@ -990,16 +990,16 @@ async function hashRegularFile(path) {
990
990
  try {
991
991
  const info = await handle.stat();
992
992
  if (!info.isFile()) throw new Error(`Namespace entry is not a regular file: ${path}`);
993
- const hash = createHash2("sha256");
993
+ const hash2 = createHash2("sha256");
994
994
  const buffer = Buffer.allocUnsafe(64 * 1024);
995
995
  let position = 0;
996
996
  while (true) {
997
997
  const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
998
998
  if (!bytesRead) break;
999
- hash.update(buffer.subarray(0, bytesRead));
999
+ hash2.update(buffer.subarray(0, bytesRead));
1000
1000
  position += bytesRead;
1001
1001
  }
1002
- return { sha256: hash.digest("hex"), size: position };
1002
+ return { sha256: hash2.digest("hex"), size: position };
1003
1003
  } finally {
1004
1004
  await handle.close();
1005
1005
  }
@@ -1200,18 +1200,18 @@ var MemoryStore = class {
1200
1200
  input2.lastVerifiedAt ?? (explicit ? now : void 0),
1201
1201
  "Memory verification time"
1202
1202
  );
1203
- const hash = createHash3("sha256").update(`${input2.scope}\0${scopeKey2}\0${content.toLocaleLowerCase()}`).digest("hex");
1203
+ const hash2 = createHash3("sha256").update(`${input2.scope}\0${scopeKey2}\0${content.toLocaleLowerCase()}`).digest("hex");
1204
1204
  database.exec("BEGIN IMMEDIATE");
1205
1205
  try {
1206
1206
  const existing = database.prepare(
1207
1207
  "SELECT * FROM memories WHERE content_hash = ?"
1208
- ).get(hash);
1208
+ ).get(hash2);
1209
1209
  const conflicting = conflictKey ? database.prepare(`
1210
1210
  SELECT * FROM memories
1211
1211
  WHERE scope = ? AND scope_key = ? AND conflict_key = ?
1212
1212
  AND status = 'active' AND content_hash <> ?
1213
1213
  ORDER BY updated_at DESC
1214
- `).all(input2.scope, scopeKey2, conflictKey, hash) : [];
1214
+ `).all(input2.scope, scopeKey2, conflictKey, hash2) : [];
1215
1215
  const supersedesId = normalizeOptional(input2.supersedesId, 80) ?? conflicting[0]?.id;
1216
1216
  let id;
1217
1217
  if (existing) {
@@ -1256,7 +1256,7 @@ var MemoryStore = class {
1256
1256
  importance,
1257
1257
  confidence,
1258
1258
  source,
1259
- hash,
1259
+ hash2,
1260
1260
  now,
1261
1261
  now,
1262
1262
  now,
@@ -1359,10 +1359,10 @@ var MemoryStore = class {
1359
1359
  const conflictKey = normalizeOptional(input2.conflictKey, 240);
1360
1360
  const candidateDefaultExpiry = !input2.expiresAt ? new Date(Date.now() + MEMORY_CANDIDATE_TTL_MS).toISOString() : void 0;
1361
1361
  const expiresAt = normalizeTimestamp(input2.expiresAt ?? candidateDefaultExpiry, "Memory expiration");
1362
- const hash = createHash3("sha256").update(`${input2.scope}\0${scopeKey2}\0${content.toLocaleLowerCase()}`).digest("hex");
1362
+ const hash2 = createHash3("sha256").update(`${input2.scope}\0${scopeKey2}\0${content.toLocaleLowerCase()}`).digest("hex");
1363
1363
  const existing = database.prepare(
1364
1364
  "SELECT * FROM memory_candidates WHERE content_hash = ?"
1365
- ).get(hash);
1365
+ ).get(hash2);
1366
1366
  const now = (/* @__PURE__ */ new Date()).toISOString();
1367
1367
  if (existing) {
1368
1368
  if (existing.status === "approved" && existing.approved_memory_id) {
@@ -1409,7 +1409,7 @@ var MemoryStore = class {
1409
1409
  confidence,
1410
1410
  source,
1411
1411
  rationale,
1412
- hash,
1412
+ hash2,
1413
1413
  now,
1414
1414
  now,
1415
1415
  revision ?? null,
@@ -4755,7 +4755,7 @@ function executableNames(command2) {
4755
4755
  return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
4756
4756
  }
4757
4757
  function runProcess(command2, args, options) {
4758
- return new Promise((resolve23, reject) => {
4758
+ return new Promise((resolve24, reject) => {
4759
4759
  const started = Date.now();
4760
4760
  const environment = options.inheritEnv === false ? {} : { ...process.env };
4761
4761
  for (const name of options.unsetEnv ?? []) delete environment[name];
@@ -4776,6 +4776,8 @@ function runProcess(command2, args, options) {
4776
4776
  let stderr = "";
4777
4777
  let stdoutBytes = 0;
4778
4778
  let stderrBytes = 0;
4779
+ let stdoutObservedBytes = 0;
4780
+ let stderrObservedBytes = 0;
4779
4781
  let timedOut = false;
4780
4782
  let callbackError;
4781
4783
  const stdoutDecoder = new StringDecoder("utf8");
@@ -4798,12 +4800,14 @@ function runProcess(command2, args, options) {
4798
4800
  }
4799
4801
  };
4800
4802
  child.stdout.on("data", (chunk) => {
4803
+ stdoutObservedBytes += chunk.length;
4801
4804
  const appended = append(stdoutDecoder, chunk, stdoutBytes);
4802
4805
  stdout += appended.text;
4803
4806
  stdoutBytes = appended.usedBytes;
4804
4807
  notify(options.onStdout, stdoutCallbackDecoder, chunk);
4805
4808
  });
4806
4809
  child.stderr.on("data", (chunk) => {
4810
+ stderrObservedBytes += chunk.length;
4807
4811
  const appended = append(stderrDecoder, chunk, stderrBytes);
4808
4812
  stderr += appended.text;
4809
4813
  stderrBytes = appended.usedBytes;
@@ -4834,13 +4838,17 @@ function runProcess(command2, args, options) {
4834
4838
  reject(callbackError);
4835
4839
  return;
4836
4840
  }
4837
- resolve23({
4841
+ resolve24({
4838
4842
  command: [command2, ...args].join(" "),
4839
4843
  exitCode: code ?? (timedOut ? 124 : 1),
4840
4844
  stdout,
4841
4845
  stderr,
4842
4846
  timedOut,
4843
- durationMs: Date.now() - started
4847
+ durationMs: Date.now() - started,
4848
+ stdoutBytes: stdoutObservedBytes,
4849
+ stderrBytes: stderrObservedBytes,
4850
+ stdoutTruncated: stdoutObservedBytes > stdoutBytes,
4851
+ stderrTruncated: stderrObservedBytes > stderrBytes
4844
4852
  });
4845
4853
  });
4846
4854
  if (options.stdin) child.stdin.end(options.stdin);
@@ -4961,6 +4969,14 @@ var contextSourceSchema = z5.object({
4961
4969
  tokens: z5.number().int().nonnegative(),
4962
4970
  addedAt: z5.string()
4963
4971
  }).strict();
4972
+ var toolArtifactSchema = z5.object({
4973
+ toolCallId: z5.string().min(1).max(512).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/u.test(value)),
4974
+ sha256: z5.string().regex(/^[a-f0-9]{64}$/u),
4975
+ bytes: z5.number().int().nonnegative().max(5 * 1024 * 1024),
4976
+ createdAt: z5.string().datetime(),
4977
+ expiresAt: z5.string().datetime(),
4978
+ redacted: z5.boolean()
4979
+ }).strict();
4964
4980
  var verificationEvidenceSchema = z5.object({
4965
4981
  toolCallId: z5.string(),
4966
4982
  tool: z5.enum(["shell", "git"]),
@@ -5037,6 +5053,7 @@ var sessionSchema = z5.object({
5037
5053
  compactedThroughMessageId: z5.string().optional(),
5038
5054
  workingMemory: workingMemorySchema.optional(),
5039
5055
  contextSources: z5.array(contextSourceSchema).max(64).optional(),
5056
+ toolArtifacts: z5.array(toolArtifactSchema).max(200).optional(),
5040
5057
  taskContract: taskContractSchema.optional(),
5041
5058
  lastRun: lastRunSchema.optional(),
5042
5059
  usage: z5.object({
@@ -5298,13 +5315,390 @@ async function exists2(path) {
5298
5315
  }
5299
5316
  }
5300
5317
 
5318
+ // src/session/tool-artifacts.ts
5319
+ import { createHash as createHash7 } from "node:crypto";
5320
+ import { lstat as lstat12, readFile as readFile7, readdir as readdir4, rm as rm2 } from "node:fs/promises";
5321
+ import { join as join11, resolve as resolve13 } from "node:path";
5322
+ import { z as z6 } from "zod";
5323
+ var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
5324
+ var MAX_TOTAL_BYTES = 20 * 1024 * 1024;
5325
+ var RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
5326
+ var sessionIdSchema2 = z6.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/);
5327
+ var toolCallIdSchema = z6.string().min(1).max(512).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/u.test(value), {
5328
+ message: "Tool call id cannot contain control characters."
5329
+ });
5330
+ var artifactSchema = z6.object({
5331
+ version: z6.literal(1),
5332
+ sessionId: sessionIdSchema2,
5333
+ toolCallId: toolCallIdSchema,
5334
+ sha256: z6.string().regex(/^[a-f0-9]{64}$/u),
5335
+ bytes: z6.number().int().nonnegative().max(MAX_ARTIFACT_BYTES),
5336
+ createdAt: z6.string().datetime(),
5337
+ expiresAt: z6.string().datetime(),
5338
+ redacted: z6.boolean(),
5339
+ content: z6.string()
5340
+ }).strict();
5341
+ var ToolArtifactStore = class {
5342
+ workspace;
5343
+ directory;
5344
+ managedDirectory;
5345
+ now;
5346
+ maxArtifactBytes;
5347
+ maxTotalBytes;
5348
+ retentionMs;
5349
+ writes = Promise.resolve();
5350
+ constructor(workspace, options = {}) {
5351
+ this.workspace = resolve13(workspace);
5352
+ this.managedDirectory = options.directory === void 0;
5353
+ this.directory = options.directory ? resolve13(options.directory) : join11(resolveProjectNamespaceSync(this.workspace).active, "tool-artifacts");
5354
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
5355
+ this.maxArtifactBytes = options.maxArtifactBytes ?? MAX_ARTIFACT_BYTES;
5356
+ this.maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
5357
+ this.retentionMs = options.retentionMs ?? RETENTION_MS;
5358
+ if (!Number.isSafeInteger(this.maxArtifactBytes) || this.maxArtifactBytes < 1 || this.maxArtifactBytes > MAX_ARTIFACT_BYTES) {
5359
+ throw new Error(`maxArtifactBytes must be between 1 and ${MAX_ARTIFACT_BYTES}.`);
5360
+ }
5361
+ if (!Number.isSafeInteger(this.maxTotalBytes) || this.maxTotalBytes < this.maxArtifactBytes) {
5362
+ throw new Error("maxTotalBytes must be at least maxArtifactBytes.");
5363
+ }
5364
+ if (!Number.isSafeInteger(this.retentionMs) || this.retentionMs < 1) {
5365
+ throw new Error("retentionMs must be a positive integer.");
5366
+ }
5367
+ }
5368
+ async archive(sessionId, toolCallId, content, options) {
5369
+ sessionIdSchema2.parse(sessionId);
5370
+ toolCallIdSchema.parse(toolCallId);
5371
+ const bytes = Buffer.byteLength(content);
5372
+ if (bytes > this.maxArtifactBytes) return { stored: false, reason: "too_large" };
5373
+ return this.enqueue(() => this.withManagedLease(async () => {
5374
+ await this.ensureDirectory();
5375
+ const now = this.now();
5376
+ const artifacts = await this.loadArtifacts();
5377
+ await this.removeExpired(artifacts, now);
5378
+ const active = artifacts.filter((artifact2) => artifact2.expiresAt > now.toISOString());
5379
+ const sha256 = hash(content);
5380
+ const existing = active.find((artifact2) => artifact2.sessionId === sessionId && artifact2.toolCallId === toolCallId);
5381
+ if (existing) {
5382
+ if (existing.sha256 === sha256 && existing.bytes === bytes && existing.redacted === options.redacted) {
5383
+ return { stored: true, artifact: reference(existing) };
5384
+ }
5385
+ return { stored: false, reason: "conflict" };
5386
+ }
5387
+ const artifact = {
5388
+ version: 1,
5389
+ sessionId,
5390
+ toolCallId,
5391
+ sha256,
5392
+ bytes,
5393
+ createdAt: now.toISOString(),
5394
+ expiresAt: new Date(now.getTime() + this.retentionMs).toISOString(),
5395
+ redacted: options.redacted,
5396
+ content
5397
+ };
5398
+ const storageBytes = storedBytes(artifact);
5399
+ let retainedBytes = active.reduce((total, retained) => total + storedBytes(retained), 0);
5400
+ const evictable = active.slice().sort(
5401
+ (left, right) => left.createdAt.localeCompare(right.createdAt)
5402
+ );
5403
+ while (retainedBytes + storageBytes > this.maxTotalBytes && evictable.length) {
5404
+ const oldest = evictable.shift();
5405
+ await this.removeArtifact(oldest);
5406
+ retainedBytes -= storedBytes(oldest);
5407
+ }
5408
+ if (retainedBytes + storageBytes > this.maxTotalBytes) return { stored: false, reason: "total_limit" };
5409
+ const path = this.pathFor(sessionId, toolCallId);
5410
+ await ensureWorkspaceStorageDirectory(this.workspace, resolve13(path, ".."), {
5411
+ requireActiveNamespace: this.managedDirectory
5412
+ });
5413
+ await atomicWrite(path, `${JSON.stringify(artifact)}
5414
+ `, 384);
5415
+ return { stored: true, artifact: reference(artifact) };
5416
+ }));
5417
+ }
5418
+ async read(sessionId, toolCallId, options = {}) {
5419
+ sessionIdSchema2.parse(sessionId);
5420
+ toolCallIdSchema.parse(toolCallId);
5421
+ const startLine = options.startLine ?? 1;
5422
+ const startByte = options.startByte;
5423
+ const maxLines = options.maxLines ?? 200;
5424
+ const maxBytes = options.maxBytes ?? 3e3;
5425
+ if (startByte !== void 0 && options.startLine !== void 0) {
5426
+ throw new Error("Use either startLine or startByte, not both.");
5427
+ }
5428
+ if (!Number.isInteger(startLine) || startLine < 1) throw new Error("startLine must be a positive integer.");
5429
+ if (startByte !== void 0 && (!Number.isInteger(startByte) || startByte < 0)) {
5430
+ throw new Error("startByte must be a non-negative integer.");
5431
+ }
5432
+ if (!Number.isInteger(maxLines) || maxLines < 1 || maxLines > 1e3) {
5433
+ throw new Error("maxLines must be between 1 and 1000.");
5434
+ }
5435
+ if (!Number.isInteger(maxBytes) || maxBytes < 256 || maxBytes > 32e3) {
5436
+ throw new Error("maxBytes must be between 256 and 32000.");
5437
+ }
5438
+ await this.writes;
5439
+ return this.withManagedLease(async () => {
5440
+ const artifact = await this.readArtifact(sessionId, toolCallId);
5441
+ const now = this.now();
5442
+ if (artifact.expiresAt <= now.toISOString()) {
5443
+ await this.removeArtifact(artifact);
5444
+ throw new Error("The retained tool output has expired. Re-run the tool if it is still needed.");
5445
+ }
5446
+ const page = startByte === void 0 ? readLinePage(artifact.content, startLine, maxLines, maxBytes) : readBytePage(artifact.content, startByte, maxBytes);
5447
+ return {
5448
+ ...reference(artifact),
5449
+ ...page
5450
+ };
5451
+ });
5452
+ }
5453
+ /** Remove expired output and return the still-valid receipts for one session. */
5454
+ async prune(sessionId) {
5455
+ sessionIdSchema2.parse(sessionId);
5456
+ return this.enqueue(() => this.withManagedLease(async () => {
5457
+ const artifacts = await this.loadArtifacts();
5458
+ const now = this.now();
5459
+ await this.removeExpired(artifacts, now);
5460
+ return artifacts.filter((artifact) => artifact.sessionId === sessionId && artifact.expiresAt > now.toISOString()).sort((left, right) => left.createdAt.localeCompare(right.createdAt)).map(reference);
5461
+ }));
5462
+ }
5463
+ /** Delete every retained result owned by a removed session. */
5464
+ async removeSession(sessionId) {
5465
+ sessionIdSchema2.parse(sessionId);
5466
+ await this.enqueue(() => this.withManagedLease(async () => {
5467
+ if (!await this.directoryAvailable()) return;
5468
+ const directory = this.sessionDirectoryFor(sessionId);
5469
+ try {
5470
+ const info = await lstat12(directory);
5471
+ if (info.isSymbolicLink() || !info.isDirectory()) {
5472
+ throw new Error(`Tool artifact session storage is not a regular directory: ${directory}`);
5473
+ }
5474
+ await assertNoSymlinkPath(this.workspace, directory);
5475
+ } catch (error) {
5476
+ if (error.code === "ENOENT") return;
5477
+ throw error;
5478
+ }
5479
+ await rm2(directory, { recursive: true, force: true });
5480
+ }));
5481
+ }
5482
+ async readArtifact(sessionId, toolCallId) {
5483
+ const path = this.pathFor(sessionId, toolCallId);
5484
+ await this.assertRegularFile(path);
5485
+ let artifact;
5486
+ try {
5487
+ artifact = artifactSchema.parse(JSON.parse(await readFile7(path, "utf8")));
5488
+ } catch {
5489
+ throw new Error("Retained tool output is unreadable or corrupt.");
5490
+ }
5491
+ if (artifact.sessionId !== sessionId || artifact.toolCallId !== toolCallId) {
5492
+ throw new Error("Retained tool output does not belong to this session.");
5493
+ }
5494
+ if (artifact.bytes !== Buffer.byteLength(artifact.content) || artifact.sha256 !== hash(artifact.content)) {
5495
+ throw new Error("Retained tool output failed its integrity check.");
5496
+ }
5497
+ return artifact;
5498
+ }
5499
+ async loadArtifacts() {
5500
+ if (!await this.directoryAvailable()) return [];
5501
+ const artifacts = [];
5502
+ for (const entry of await readdir4(this.directory, { withFileTypes: true })) {
5503
+ const sessionDirectory = join11(this.directory, entry.name);
5504
+ if (entry.isSymbolicLink()) throw new Error(`Tool artifact storage contains a symbolic link: ${sessionDirectory}`);
5505
+ if (!entry.isDirectory() || !/^[a-f0-9]{64}$/u.test(entry.name)) continue;
5506
+ await assertNoSymlinkPath(this.workspace, sessionDirectory);
5507
+ for (const file of await readdir4(sessionDirectory, { withFileTypes: true })) {
5508
+ const path = join11(sessionDirectory, file.name);
5509
+ if (file.isSymbolicLink()) throw new Error(`Tool artifact storage contains a symbolic link: ${path}`);
5510
+ if (!file.isFile() || !/^[a-f0-9]{64}\.json$/u.test(file.name)) continue;
5511
+ await this.assertRegularFile(path);
5512
+ try {
5513
+ const artifact = artifactSchema.parse(JSON.parse(await readFile7(path, "utf8")));
5514
+ if (artifact.bytes !== Buffer.byteLength(artifact.content) || artifact.sha256 !== hash(artifact.content)) {
5515
+ throw new Error("integrity");
5516
+ }
5517
+ if (sessionDirectory !== this.sessionDirectoryFor(artifact.sessionId) || path !== this.pathFor(artifact.sessionId, artifact.toolCallId)) {
5518
+ throw new Error("identity");
5519
+ }
5520
+ artifacts.push(artifact);
5521
+ } catch {
5522
+ throw new Error(`Tool artifact is unreadable or corrupt: ${path}`);
5523
+ }
5524
+ }
5525
+ }
5526
+ return artifacts;
5527
+ }
5528
+ async removeExpired(artifacts, now) {
5529
+ for (const artifact of artifacts) {
5530
+ if (artifact.expiresAt <= now.toISOString()) await this.removeArtifact(artifact);
5531
+ }
5532
+ }
5533
+ async removeArtifact(artifact) {
5534
+ await rm2(this.pathFor(artifact.sessionId, artifact.toolCallId), { force: true });
5535
+ }
5536
+ pathFor(sessionId, toolCallId) {
5537
+ return join11(this.sessionDirectoryFor(sessionId), `${hash(`call\0${toolCallId}`)}.json`);
5538
+ }
5539
+ sessionDirectoryFor(sessionId) {
5540
+ return join11(this.directory, hash(`session\0${sessionId}`));
5541
+ }
5542
+ async ensureDirectory() {
5543
+ await ensureWorkspaceStorageDirectory(this.workspace, this.directory, {
5544
+ requireActiveNamespace: this.managedDirectory
5545
+ });
5546
+ }
5547
+ async directoryAvailable() {
5548
+ if (this.managedDirectory) await assertNoSymlinkPath(this.workspace, this.directory);
5549
+ try {
5550
+ const info = await lstat12(this.directory);
5551
+ if (info.isSymbolicLink() || !info.isDirectory()) {
5552
+ throw new Error(`Tool artifact storage is not a regular directory: ${this.directory}`);
5553
+ }
5554
+ return true;
5555
+ } catch (error) {
5556
+ if (error.code === "ENOENT") return false;
5557
+ throw error;
5558
+ }
5559
+ }
5560
+ async assertRegularFile(path) {
5561
+ await assertNoSymlinkPath(this.workspace, resolve13(path, ".."));
5562
+ try {
5563
+ const info = await lstat12(path);
5564
+ if (info.isSymbolicLink() || !info.isFile()) {
5565
+ throw new Error(`Tool artifact cannot be a symbolic link or non-regular file: ${path}`);
5566
+ }
5567
+ } catch (error) {
5568
+ if (error.code === "ENOENT") {
5569
+ throw new Error("No retained tool output matches this tool call in the current session.");
5570
+ }
5571
+ throw error;
5572
+ }
5573
+ }
5574
+ async withManagedLease(operation) {
5575
+ if (!this.managedDirectory) return operation();
5576
+ return withNamespaceLease(projectNamespacePaths(this.workspace).canonical, "shared", async () => {
5577
+ assertActiveProjectNamespacePath(this.workspace, this.directory);
5578
+ return operation();
5579
+ });
5580
+ }
5581
+ async enqueue(operation) {
5582
+ const next = this.writes.then(operation);
5583
+ this.writes = next.then(() => void 0, () => void 0);
5584
+ return next;
5585
+ }
5586
+ };
5587
+ function reference(artifact) {
5588
+ return {
5589
+ toolCallId: artifact.toolCallId,
5590
+ sha256: artifact.sha256,
5591
+ bytes: artifact.bytes,
5592
+ createdAt: artifact.createdAt,
5593
+ expiresAt: artifact.expiresAt,
5594
+ redacted: artifact.redacted
5595
+ };
5596
+ }
5597
+ function hash(value) {
5598
+ return createHash7("sha256").update(value).digest("hex");
5599
+ }
5600
+ function storedBytes(artifact) {
5601
+ return Buffer.byteLength(JSON.stringify(artifact)) + 1;
5602
+ }
5603
+ function readLinePage(content, startLine, maxLines, maxBytes) {
5604
+ const ranges = lineRanges(content);
5605
+ const startIndex = startLine - 1;
5606
+ if (startIndex >= ranges.length) {
5607
+ const totalBytes = Buffer.byteLength(content);
5608
+ return {
5609
+ content: "",
5610
+ startLine,
5611
+ endLine: startLine - 1,
5612
+ totalLines: ranges.length,
5613
+ startByte: totalBytes,
5614
+ endByte: totalBytes,
5615
+ hasMore: false
5616
+ };
5617
+ }
5618
+ const endIndex = Math.min(ranges.length - 1, startIndex + maxLines - 1);
5619
+ const startCharacter = ranges[startIndex]?.start ?? content.length;
5620
+ const endCharacter = ranges[endIndex]?.end ?? content.length;
5621
+ const startByte = Buffer.byteLength(content.slice(0, startCharacter));
5622
+ const selected = Buffer.from(content.slice(startCharacter, endCharacter));
5623
+ if (selected.length > maxBytes) {
5624
+ const page = sliceUtf8Buffer(selected, 0, maxBytes);
5625
+ const pageContent = page.content;
5626
+ return {
5627
+ content: pageContent,
5628
+ startLine,
5629
+ endLine: startLine + countNewlines(pageContent),
5630
+ totalLines: ranges.length,
5631
+ startByte,
5632
+ endByte: startByte + page.bytes,
5633
+ hasMore: true,
5634
+ nextStartByte: startByte + page.bytes
5635
+ };
5636
+ }
5637
+ const endLine = endIndex + 1;
5638
+ const hasMore = endLine < ranges.length;
5639
+ return {
5640
+ content: selected.toString("utf8"),
5641
+ startLine,
5642
+ endLine,
5643
+ totalLines: ranges.length,
5644
+ startByte,
5645
+ endByte: startByte + selected.length,
5646
+ hasMore,
5647
+ ...hasMore ? { nextStartLine: endLine + 1 } : {}
5648
+ };
5649
+ }
5650
+ function readBytePage(content, requestedStart, maxBytes) {
5651
+ const buffer = Buffer.from(content);
5652
+ const startByte = Math.min(requestedStart, buffer.length);
5653
+ if (startByte < buffer.length && isUtf8Continuation(buffer[startByte] ?? 0)) {
5654
+ throw new Error("startByte must be an exact UTF-8 boundary shown by a previous page.");
5655
+ }
5656
+ const page = sliceUtf8Buffer(buffer, startByte, maxBytes);
5657
+ const prefix = buffer.subarray(0, startByte).toString("utf8");
5658
+ const startLine = countNewlines(prefix) + 1;
5659
+ const endByte = startByte + page.bytes;
5660
+ const hasMore = endByte < buffer.length;
5661
+ return {
5662
+ content: page.content,
5663
+ startLine,
5664
+ endLine: startLine + countNewlines(page.content),
5665
+ totalLines: countNewlines(content) + 1,
5666
+ startByte,
5667
+ endByte,
5668
+ hasMore,
5669
+ ...hasMore ? { nextStartByte: endByte } : {}
5670
+ };
5671
+ }
5672
+ function sliceUtf8Buffer(buffer, start, maxBytes) {
5673
+ let end = Math.min(buffer.length, start + maxBytes);
5674
+ while (end > start && isUtf8Continuation(buffer[end] ?? 0)) end -= 1;
5675
+ return { content: buffer.subarray(start, end).toString("utf8"), bytes: end - start };
5676
+ }
5677
+ function lineRanges(content) {
5678
+ const ranges = [];
5679
+ const expression = /\r?\n/gu;
5680
+ let start = 0;
5681
+ for (const match of content.matchAll(expression)) {
5682
+ ranges.push({ start, end: match.index });
5683
+ start = match.index + match[0].length;
5684
+ }
5685
+ ranges.push({ start, end: content.length });
5686
+ return ranges;
5687
+ }
5688
+ function countNewlines(value) {
5689
+ return value.match(/\n/gu)?.length ?? 0;
5690
+ }
5691
+ function isUtf8Continuation(byte) {
5692
+ return byte >= 128 && byte < 192;
5693
+ }
5694
+
5301
5695
  // src/tools/apply-patch.ts
5302
- import { lstat as lstat12, readFile as readFile7, unlink as unlink4 } from "node:fs/promises";
5696
+ import { lstat as lstat13, readFile as readFile8, unlink as unlink4 } from "node:fs/promises";
5303
5697
  import { applyPatch as applyUnifiedPatch, parsePatch } from "diff";
5304
- import { z as z6 } from "zod";
5305
- var inputSchema2 = z6.object({
5306
- patch: z6.string().min(1).max(1e7),
5307
- dry_run: z6.boolean().optional()
5698
+ import { z as z7 } from "zod";
5699
+ var inputSchema2 = z7.object({
5700
+ patch: z7.string().min(1).max(1e7),
5701
+ dry_run: z7.boolean().optional()
5308
5702
  }).strict();
5309
5703
  var applyPatchTool = {
5310
5704
  definition: {
@@ -5560,10 +5954,10 @@ function findSequence(haystack, needle, hint, minimum) {
5560
5954
  }
5561
5955
  async function readSnapshot2(path) {
5562
5956
  try {
5563
- const info = await lstat12(path);
5957
+ const info = await lstat13(path);
5564
5958
  if (info.isSymbolicLink()) throw new Error(`Refusing to patch a symbolic link: ${path}`);
5565
5959
  if (!info.isFile()) throw new Error(`Patch target is not a regular file: ${path}`);
5566
- return { before: await readFile7(path), mode: info.mode };
5960
+ return { before: await readFile8(path), mode: info.mode };
5567
5961
  } catch (error) {
5568
5962
  if (error.code === "ENOENT") return { before: null };
5569
5963
  throw error;
@@ -5612,13 +6006,13 @@ function buffersEqual(left, right) {
5612
6006
  }
5613
6007
 
5614
6008
  // src/tools/git.ts
5615
- import { join as join11 } from "node:path";
5616
- import { z as z7 } from "zod";
5617
- var inputSchema3 = z7.object({
5618
- args: z7.array(z7.string().max(1e4)).min(1).max(200),
5619
- cwd: z7.string().min(1).optional(),
5620
- timeout_ms: z7.number().int().min(100).max(6e5).optional(),
5621
- stdin: z7.string().max(5e6).optional()
6009
+ import { join as join12 } from "node:path";
6010
+ import { z as z8 } from "zod";
6011
+ var inputSchema3 = z8.object({
6012
+ args: z8.array(z8.string().max(1e4)).min(1).max(200),
6013
+ cwd: z8.string().min(1).optional(),
6014
+ timeout_ms: z8.number().int().min(100).max(6e5).optional(),
6015
+ stdin: z8.string().max(5e6).optional()
5622
6016
  }).strict();
5623
6017
  var networkCommands = /* @__PURE__ */ new Set([
5624
6018
  "clone",
@@ -5831,7 +6225,7 @@ var gitTool = {
5831
6225
  for (const candidate of explicit) {
5832
6226
  if (!candidate || candidate.startsWith("-")) continue;
5833
6227
  try {
5834
- paths.add(await context.workspace.resolvePath(join11(cwd, candidate), { allowMissing: true }));
6228
+ paths.add(await context.workspace.resolvePath(join12(cwd, candidate), { allowMissing: true }));
5835
6229
  } catch {
5836
6230
  }
5837
6231
  }
@@ -5852,7 +6246,7 @@ var gitTool = {
5852
6246
  const candidates = candidate.includes(" -> ") ? candidate.split(" -> ") : [candidate];
5853
6247
  for (const value of candidates) {
5854
6248
  try {
5855
- paths.add(await context.workspace.resolvePath(join11(cwd, value), { allowMissing: true }));
6249
+ paths.add(await context.workspace.resolvePath(join12(cwd, value), { allowMissing: true }));
5856
6250
  } catch {
5857
6251
  }
5858
6252
  }
@@ -5933,7 +6327,7 @@ async function collectGitChanges(before, after, runtime, cwd, context) {
5933
6327
  const paths = [];
5934
6328
  for (const candidate of [...candidates].slice(0, 2e3)) {
5935
6329
  try {
5936
- paths.push(await context.workspace.resolvePath(join11(cwd, candidate), { allowMissing: true }));
6330
+ paths.push(await context.workspace.resolvePath(join12(cwd, candidate), { allowMissing: true }));
5937
6331
  } catch {
5938
6332
  }
5939
6333
  }
@@ -6049,7 +6443,7 @@ async function validateGitWorkspaceArguments(args, command2, cwd, context) {
6049
6443
  if (/^(?:[a-z]+:\/\/|[a-z]+@[^:]+:)/i.test(candidate)) continue;
6050
6444
  if (command2 === "clone" && index === 0 && !candidate) continue;
6051
6445
  await context.workspace.resolvePath(
6052
- candidate.startsWith("/") ? candidate : join11(cwd, candidate),
6446
+ candidate.startsWith("/") ? candidate : join12(cwd, candidate),
6053
6447
  { allowMissing: true }
6054
6448
  );
6055
6449
  }
@@ -6102,17 +6496,17 @@ function positionalArguments(args, command2) {
6102
6496
  }
6103
6497
 
6104
6498
  // src/tools/list.ts
6105
- import { lstat as lstat13 } from "node:fs/promises";
6106
- import { relative as relative6, resolve as resolve13 } from "node:path";
6499
+ import { lstat as lstat14 } from "node:fs/promises";
6500
+ import { relative as relative6, resolve as resolve14 } from "node:path";
6107
6501
  import fg3 from "fast-glob";
6108
- import { z as z8 } from "zod";
6109
- var inputSchema4 = z8.object({
6110
- path: z8.string().min(1).optional(),
6111
- pattern: z8.string().min(1).optional(),
6112
- depth: z8.number().int().min(1).max(20).optional(),
6113
- include_hidden: z8.boolean().optional(),
6114
- include_directories: z8.boolean().optional(),
6115
- limit: z8.number().int().min(1).max(5e3).optional()
6502
+ import { z as z9 } from "zod";
6503
+ var inputSchema4 = z9.object({
6504
+ path: z9.string().min(1).optional(),
6505
+ pattern: z9.string().min(1).optional(),
6506
+ depth: z9.number().int().min(1).max(20).optional(),
6507
+ include_hidden: z9.boolean().optional(),
6508
+ include_directories: z9.boolean().optional(),
6509
+ limit: z9.number().int().min(1).max(5e3).optional()
6116
6510
  }).strict();
6117
6511
  var ignored = [
6118
6512
  "**/.git/**",
@@ -6161,11 +6555,11 @@ var listFilesTool = {
6161
6555
  for (const path of selected) {
6162
6556
  let safePath;
6163
6557
  try {
6164
- safePath = await context.workspace.resolvePath(resolve13(directory, path), { expect: "any" });
6558
+ safePath = await context.workspace.resolvePath(resolve14(directory, path), { expect: "any" });
6165
6559
  } catch {
6166
6560
  continue;
6167
6561
  }
6168
- const info = await lstat13(safePath);
6562
+ const info = await lstat14(safePath);
6169
6563
  rendered.push(`${info.isDirectory() ? "d" : "f"} ${relative6(directory, safePath)}${info.isDirectory() ? "/" : ""}`);
6170
6564
  }
6171
6565
  const base = workspaceAliasPath(directory, context.workspace.roots);
@@ -6182,14 +6576,14 @@ var listFilesTool = {
6182
6576
  };
6183
6577
 
6184
6578
  // src/tools/read.ts
6185
- import { readFile as readFile8, stat as stat7 } from "node:fs/promises";
6186
- import { z as z9 } from "zod";
6187
- var inputSchema5 = z9.object({
6188
- path: z9.string().min(1),
6189
- start_line: z9.number().int().positive().optional(),
6190
- end_line: z9.number().int().positive().optional(),
6191
- line_numbers: z9.boolean().optional(),
6192
- max_bytes: z9.number().int().positive().max(1e6).optional()
6579
+ import { readFile as readFile9, stat as stat7 } from "node:fs/promises";
6580
+ import { z as z10 } from "zod";
6581
+ var inputSchema5 = z10.object({
6582
+ path: z10.string().min(1),
6583
+ start_line: z10.number().int().positive().optional(),
6584
+ end_line: z10.number().int().positive().optional(),
6585
+ line_numbers: z10.boolean().optional(),
6586
+ max_bytes: z10.number().int().positive().max(1e6).optional()
6193
6587
  }).strict();
6194
6588
  var readFileTool = {
6195
6589
  definition: {
@@ -6212,7 +6606,7 @@ var readFileTool = {
6212
6606
  if (info.size > 1e7) {
6213
6607
  throw new Error(`File is too large to read safely (${info.size} bytes).`);
6214
6608
  }
6215
- const buffer = await readFile8(path);
6609
+ const buffer = await readFile9(path);
6216
6610
  if (looksBinary(buffer)) throw new Error("Binary files cannot be read with read_file.");
6217
6611
  const raw = buffer.toString("utf8");
6218
6612
  const lines = raw.split("\n");
@@ -6256,6 +6650,73 @@ function truncateUtf8(input2, maxBytes) {
6256
6650
  return buffer.subarray(0, end).toString("utf8");
6257
6651
  }
6258
6652
 
6653
+ // src/tools/read-artifact.ts
6654
+ import { z as z11 } from "zod";
6655
+ var inputSchema6 = z11.object({
6656
+ sha256: z11.string().regex(/^[a-f0-9]{64}$/u),
6657
+ start_line: z11.number().int().positive().optional(),
6658
+ start_byte: z11.number().int().nonnegative().optional(),
6659
+ max_lines: z11.number().int().min(1).max(1e3).optional(),
6660
+ max_bytes: z11.number().int().min(256).max(768).optional()
6661
+ }).strict().refine((input2) => input2.start_line === void 0 || input2.start_byte === void 0, {
6662
+ message: "Use either start_line or start_byte, not both."
6663
+ });
6664
+ var readToolArtifactTool = {
6665
+ definition: {
6666
+ name: "read_tool_artifact",
6667
+ description: "Read a bounded page of oversized tool output retained for this session. Use only the SHA-256 shown in a tool-output receipt.",
6668
+ category: "read",
6669
+ inputSchema: jsonSchema({
6670
+ sha256: { type: "string", description: "The exact 64-character SHA-256 from a retained-output receipt." },
6671
+ start_line: { type: "integer", minimum: 1, default: 1 },
6672
+ start_byte: { type: "integer", minimum: 0, description: "Exact UTF-8 continuation byte shown by a previous page." },
6673
+ max_lines: { type: "integer", minimum: 1, maximum: 1e3, default: 200 },
6674
+ max_bytes: { type: "integer", minimum: 256, maximum: 768, default: 768 }
6675
+ }, ["sha256"])
6676
+ },
6677
+ async execute(arguments_, context) {
6678
+ const input2 = inputSchema6.parse(arguments_);
6679
+ const artifact = context.session.toolArtifacts?.find(
6680
+ (candidate) => candidate.sha256 === input2.sha256 && Date.parse(candidate.expiresAt) > Date.now()
6681
+ );
6682
+ if (!artifact) {
6683
+ throw new Error("No retained tool output matches this tool call in the current session.");
6684
+ }
6685
+ const store = context.toolArtifactStore ?? new ToolArtifactStore(context.workspace.primaryRoot);
6686
+ const page = await store.read(context.session.id, artifact.toolCallId, {
6687
+ ...input2.start_line !== void 0 ? { startLine: input2.start_line } : {},
6688
+ ...input2.start_byte !== void 0 ? { startByte: input2.start_byte } : {},
6689
+ ...input2.max_lines !== void 0 ? { maxLines: input2.max_lines } : {},
6690
+ maxBytes: input2.max_bytes ?? 768
6691
+ });
6692
+ if (page.sha256 !== artifact.sha256 || page.bytes !== artifact.bytes) {
6693
+ throw new Error("Retained tool output no longer matches the session receipt.");
6694
+ }
6695
+ const heading = `Retained output page: lines ${page.startLine}-${page.endLine} of ${page.totalLines}; bytes ${page.startByte}-${page.endByte} of ${page.bytes}`;
6696
+ const continuation = page.nextStartByte !== void 0 ? `continue with start_byte=${page.nextStartByte}` : page.nextStartLine !== void 0 ? `continue with start_line=${page.nextStartLine}` : "";
6697
+ return {
6698
+ content: `${heading}
6699
+ ${page.content}${page.hasMore ? `
6700
+ \u2026 more retained output; ${continuation}` : ""}`,
6701
+ metadata: {
6702
+ artifact: {
6703
+ toolCallId: page.toolCallId,
6704
+ sha256: page.sha256,
6705
+ bytes: page.bytes,
6706
+ expiresAt: page.expiresAt,
6707
+ redacted: page.redacted
6708
+ },
6709
+ startLine: page.startLine,
6710
+ endLine: page.endLine,
6711
+ totalLines: page.totalLines,
6712
+ startByte: page.startByte,
6713
+ endByte: page.endByte,
6714
+ truncated: page.hasMore
6715
+ }
6716
+ };
6717
+ }
6718
+ };
6719
+
6259
6720
  // src/tools/registry.ts
6260
6721
  var ToolRegistry = class {
6261
6722
  tools = /* @__PURE__ */ new Map();
@@ -6303,19 +6764,19 @@ function assertToolName(name) {
6303
6764
  }
6304
6765
 
6305
6766
  // src/tools/search.ts
6306
- import { readFile as readFile9, stat as stat8 } from "node:fs/promises";
6307
- import { resolve as resolve14 } from "node:path";
6767
+ import { readFile as readFile10, stat as stat8 } from "node:fs/promises";
6768
+ import { resolve as resolve15 } from "node:path";
6308
6769
  import fg4 from "fast-glob";
6309
- import { z as z10 } from "zod";
6310
- var inputSchema6 = z10.object({
6311
- query: z10.string().min(1).max(1e4),
6312
- path: z10.string().min(1).optional(),
6313
- pattern: z10.string().min(1).optional(),
6314
- mode: z10.enum(["text", "ranked"]).optional(),
6315
- literal: z10.boolean().optional(),
6316
- case_sensitive: z10.boolean().optional(),
6317
- context_lines: z10.number().int().min(0).max(20).optional(),
6318
- max_results: z10.number().int().min(1).max(500).optional()
6770
+ import { z as z12 } from "zod";
6771
+ var inputSchema7 = z12.object({
6772
+ query: z12.string().min(1).max(1e4),
6773
+ path: z12.string().min(1).optional(),
6774
+ pattern: z12.string().min(1).optional(),
6775
+ mode: z12.enum(["text", "ranked"]).optional(),
6776
+ literal: z12.boolean().optional(),
6777
+ case_sensitive: z12.boolean().optional(),
6778
+ context_lines: z12.number().int().min(0).max(20).optional(),
6779
+ max_results: z12.number().int().min(1).max(500).optional()
6319
6780
  }).strict();
6320
6781
  var ignore = [
6321
6782
  "**/.git/**",
@@ -6350,7 +6811,7 @@ var searchCodeTool = {
6350
6811
  }, ["query"])
6351
6812
  },
6352
6813
  async execute(arguments_, context) {
6353
- const input2 = inputSchema6.parse(arguments_);
6814
+ const input2 = inputSchema7.parse(arguments_);
6354
6815
  const directory = await context.workspace.resolveDirectory(input2.path ?? ".");
6355
6816
  if ((input2.mode ?? "text") === "ranked") {
6356
6817
  if (!context.contextEngine) throw new Error("Local ranked retrieval is unavailable.");
@@ -6374,7 +6835,7 @@ var searchCodeTool = {
6374
6835
  if (results.length >= maxResults) break;
6375
6836
  let path;
6376
6837
  try {
6377
- path = await context.workspace.resolvePath(resolve14(directory, file), { expect: "file" });
6838
+ path = await context.workspace.resolvePath(resolve15(directory, file), { expect: "file" });
6378
6839
  } catch {
6379
6840
  skipped += 1;
6380
6841
  continue;
@@ -6384,7 +6845,7 @@ var searchCodeTool = {
6384
6845
  skipped += 1;
6385
6846
  continue;
6386
6847
  }
6387
- const buffer = await readFile9(path);
6848
+ const buffer = await readFile10(path);
6388
6849
  if (buffer.subarray(0, 8192).includes(0)) {
6389
6850
  skipped += 1;
6390
6851
  continue;
@@ -6456,17 +6917,17 @@ ${hit.content}`
6456
6917
  }
6457
6918
 
6458
6919
  // src/tools/shell.ts
6459
- import { createHash as createHash7 } from "node:crypto";
6460
- import { lstat as lstat14, readFile as readFile10, readdir as readdir4 } from "node:fs/promises";
6461
- import { join as join12 } from "node:path";
6462
- import { z as z11 } from "zod";
6463
- var inputSchema7 = z11.object({
6464
- command: z11.string().min(1).max(1e5),
6465
- cwd: z11.string().min(1).optional(),
6466
- timeout_ms: z11.number().int().min(100).max(6e5).optional(),
6467
- max_output_bytes: z11.number().int().min(1e3).max(5e6).optional(),
6468
- env: z11.record(z11.string(), z11.string()).optional(),
6469
- stdin: z11.string().max(5e6).optional()
6920
+ import { createHash as createHash8 } from "node:crypto";
6921
+ import { lstat as lstat15, readFile as readFile11, readdir as readdir5 } from "node:fs/promises";
6922
+ import { join as join13 } from "node:path";
6923
+ import { z as z13 } from "zod";
6924
+ var inputSchema8 = z13.object({
6925
+ command: z13.string().min(1).max(1e5),
6926
+ cwd: z13.string().min(1).optional(),
6927
+ timeout_ms: z13.number().int().min(100).max(6e5).optional(),
6928
+ max_output_bytes: z13.number().int().min(1e3).max(5e6).optional(),
6929
+ env: z13.record(z13.string(), z13.string()).optional(),
6930
+ stdin: z13.string().max(5e6).optional()
6470
6931
  }).strict();
6471
6932
  var shellTool = {
6472
6933
  definition: {
@@ -6483,7 +6944,7 @@ var shellTool = {
6483
6944
  }, ["command"])
6484
6945
  },
6485
6946
  permissionCategories(arguments_) {
6486
- const input2 = inputSchema7.parse(arguments_);
6947
+ const input2 = inputSchema8.parse(arguments_);
6487
6948
  validateEnvironment(input2.env);
6488
6949
  return [
6489
6950
  "shell",
@@ -6493,12 +6954,12 @@ var shellTool = {
6493
6954
  ];
6494
6955
  },
6495
6956
  async affectedPaths(arguments_, context) {
6496
- const input2 = inputSchema7.parse(arguments_);
6957
+ const input2 = inputSchema8.parse(arguments_);
6497
6958
  if (!appearsToModifyWorkspace(input2.command)) return [];
6498
6959
  return collectAffectedPaths(input2.command, input2.cwd ?? ".", context);
6499
6960
  },
6500
6961
  async execute(arguments_, context) {
6501
- const input2 = inputSchema7.parse(arguments_);
6962
+ const input2 = inputSchema8.parse(arguments_);
6502
6963
  validateEnvironment(input2.env);
6503
6964
  const cwd = await context.workspace.resolveDirectory(input2.cwd ?? ".");
6504
6965
  const candidates = appearsToModifyWorkspace(input2.command) ? await collectAffectedPaths(input2.command, input2.cwd ?? ".", context) : [];
@@ -6538,7 +6999,8 @@ Execution interrupted: ${error instanceof Error ? error.message : String(error)}
6538
6999
  result.stdout ? `stdout:
6539
7000
  ${result.stdout}` : "",
6540
7001
  result.stderr ? `stderr:
6541
- ${result.stderr}` : ""
7002
+ ${result.stderr}` : "",
7003
+ result.stdoutTruncated || result.stderrTruncated ? `Source output exceeded the command capture limit (${result.stdoutBytes} stdout bytes, ${result.stderrBytes} stderr bytes observed). Rerun with max_output_bytes up to 5000000 or narrow the command; omitted source bytes are not recoverable from this result.` : ""
6542
7004
  ].filter(Boolean);
6543
7005
  return {
6544
7006
  ok: result.exitCode === 0 && !result.timedOut,
@@ -6548,6 +7010,9 @@ ${result.stderr}` : ""
6548
7010
  exitCode: result.exitCode,
6549
7011
  timedOut: result.timedOut,
6550
7012
  durationMs: result.durationMs,
7013
+ stdoutBytes: result.stdoutBytes,
7014
+ stderrBytes: result.stderrBytes,
7015
+ sourceTruncated: result.stdoutTruncated || result.stderrTruncated,
6551
7016
  changeTracking: candidates.length ? "targeted" : beforeWorkspace && afterWorkspace && beforeWorkspace.complete && afterWorkspace.complete ? "workspace-snapshot" : beforeWorkspace ? "unresolved" : "read-only"
6552
7017
  },
6553
7018
  ...changedFiles.length ? { changedFiles } : {}
@@ -6611,7 +7076,7 @@ async function collectAffectedPaths(command2, cwdInput, context) {
6611
7076
  allowMissing: true
6612
7077
  });
6613
7078
  try {
6614
- if ((await lstat14(path)).isDirectory()) continue;
7079
+ if ((await lstat15(path)).isDirectory()) continue;
6615
7080
  } catch (error) {
6616
7081
  if (error.code !== "ENOENT") throw error;
6617
7082
  }
@@ -6639,7 +7104,7 @@ async function changedPaths(paths, before) {
6639
7104
  }
6640
7105
  async function snapshotPath(path) {
6641
7106
  try {
6642
- const info = await lstat14(path);
7107
+ const info = await lstat15(path);
6643
7108
  return { exists: true, size: info.size, mtimeMs: info.mtimeMs };
6644
7109
  } catch (error) {
6645
7110
  if (error.code === "ENOENT") return { exists: false };
@@ -6655,18 +7120,18 @@ async function captureWorkspaceSnapshot(roots) {
6655
7120
  if (!discovered.complete) complete = false;
6656
7121
  for (const path of discovered.files) {
6657
7122
  try {
6658
- const info = await lstat14(path);
7123
+ const info = await lstat15(path);
6659
7124
  if (!info.isFile() || info.isSymbolicLink()) continue;
6660
7125
  if (files.size >= MAX_SNAPSHOT_FILES || info.size > MAX_SNAPSHOT_FILE_BYTES || totalBytes + info.size > MAX_SNAPSHOT_TOTAL_BYTES) {
6661
7126
  complete = false;
6662
7127
  continue;
6663
7128
  }
6664
- const content = await readFile10(path);
7129
+ const content = await readFile11(path);
6665
7130
  totalBytes += content.length;
6666
7131
  files.set(path, {
6667
7132
  size: info.size,
6668
7133
  mtimeMs: info.mtimeMs,
6669
- hash: createHash7("sha256").update(content).digest("hex")
7134
+ hash: createHash8("sha256").update(content).digest("hex")
6670
7135
  });
6671
7136
  } catch (error) {
6672
7137
  if (error.code !== "ENOENT") complete = false;
@@ -6687,14 +7152,14 @@ async function discoverSnapshotFiles(root) {
6687
7152
  const directory = pending.pop();
6688
7153
  if (!directory) break;
6689
7154
  try {
6690
- const entries = await readdir4(directory, { withFileTypes: true, encoding: "utf8" });
7155
+ const entries = await readdir5(directory, { withFileTypes: true, encoding: "utf8" });
6691
7156
  for (const entry of entries) {
6692
7157
  if (ignored2.has(entry.name) || /^\.skein\.(?:migrating|rollback)-/u.test(entry.name)) continue;
6693
7158
  if (entry.isSymbolicLink()) {
6694
7159
  complete = false;
6695
7160
  continue;
6696
7161
  }
6697
- const path = join12(directory, entry.name);
7162
+ const path = join13(directory, entry.name);
6698
7163
  if (entry.isDirectory()) pending.push(path);
6699
7164
  else if (entry.isFile()) files.push(path);
6700
7165
  }
@@ -6708,18 +7173,18 @@ async function discoverSnapshotFiles(root) {
6708
7173
 
6709
7174
  // src/tools/task.ts
6710
7175
  import { randomUUID as randomUUID9 } from "node:crypto";
6711
- import { z as z12 } from "zod";
6712
- var taskSchema2 = z12.object({
6713
- id: z12.string().min(1).optional(),
6714
- title: z12.string().min(1).max(500),
6715
- status: z12.enum(["pending", "in_progress", "completed"])
7176
+ import { z as z14 } from "zod";
7177
+ var taskSchema2 = z14.object({
7178
+ id: z14.string().min(1).optional(),
7179
+ title: z14.string().min(1).max(500),
7180
+ status: z14.enum(["pending", "in_progress", "completed"])
6716
7181
  }).strict();
6717
- var inputSchema8 = z12.discriminatedUnion("action", [
6718
- z12.object({ action: z12.literal("list") }).strict(),
6719
- z12.object({ action: z12.literal("add"), title: z12.string().min(1).max(500), status: z12.enum(["pending", "in_progress", "completed"]).optional() }).strict(),
6720
- z12.object({ action: z12.literal("update"), id: z12.string().min(1), title: z12.string().min(1).max(500).optional(), status: z12.enum(["pending", "in_progress", "completed"]).optional() }).strict(),
6721
- z12.object({ action: z12.literal("remove"), id: z12.string().min(1) }).strict(),
6722
- z12.object({ action: z12.literal("replace"), tasks: z12.array(taskSchema2).max(100) }).strict()
7182
+ var inputSchema9 = z14.discriminatedUnion("action", [
7183
+ z14.object({ action: z14.literal("list") }).strict(),
7184
+ z14.object({ action: z14.literal("add"), title: z14.string().min(1).max(500), status: z14.enum(["pending", "in_progress", "completed"]).optional() }).strict(),
7185
+ z14.object({ action: z14.literal("update"), id: z14.string().min(1), title: z14.string().min(1).max(500).optional(), status: z14.enum(["pending", "in_progress", "completed"]).optional() }).strict(),
7186
+ z14.object({ action: z14.literal("remove"), id: z14.string().min(1) }).strict(),
7187
+ z14.object({ action: z14.literal("replace"), tasks: z14.array(taskSchema2).max(100) }).strict()
6723
7188
  ]);
6724
7189
  var taskTool = {
6725
7190
  definition: {
@@ -6750,7 +7215,7 @@ var taskTool = {
6750
7215
  }
6751
7216
  },
6752
7217
  async execute(arguments_, context) {
6753
- const input2 = inputSchema8.parse(arguments_);
7218
+ const input2 = inputSchema9.parse(arguments_);
6754
7219
  const tasks = context.session.tasks;
6755
7220
  switch (input2.action) {
6756
7221
  case "list":
@@ -6788,7 +7253,7 @@ var taskTool = {
6788
7253
 
6789
7254
  // src/tools/task-contract.ts
6790
7255
  import { randomUUID as randomUUID11 } from "node:crypto";
6791
- import { z as z13 } from "zod";
7256
+ import { z as z15 } from "zod";
6792
7257
 
6793
7258
  // src/agent/task-contract.ts
6794
7259
  import { randomUUID as randomUUID10 } from "node:crypto";
@@ -6869,14 +7334,14 @@ function compact(value, limit) {
6869
7334
  }
6870
7335
 
6871
7336
  // src/agent/completion-gate.ts
6872
- import { createHash as createHash9 } from "node:crypto";
7337
+ import { createHash as createHash10 } from "node:crypto";
6873
7338
 
6874
7339
  // src/tools/permissions.ts
6875
- import { createHash as createHash8, createHmac, randomBytes } from "node:crypto";
7340
+ import { createHash as createHash9, createHmac, randomBytes } from "node:crypto";
6876
7341
  var permissionScopeSecret = randomBytes(32);
6877
7342
  function permissionKey(call, category) {
6878
7343
  const categoryScope = category === "network" ? `\0network-request:${scopeFingerprint(normalizeRequestState(call.arguments))}` : "";
6879
- const digest = createHash8("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
7344
+ const digest = createHash9("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
6880
7345
  return `${category}:${call.name}:${digest}`;
6881
7346
  }
6882
7347
  function permissionTarget(call) {
@@ -7070,7 +7535,7 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
7070
7535
  kind,
7071
7536
  ok: result.ok,
7072
7537
  changeSequence,
7073
- commandKey: createHash9("sha256").update(normalized).digest("hex")
7538
+ commandKey: createHash10("sha256").update(normalized).digest("hex")
7074
7539
  };
7075
7540
  }
7076
7541
  function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = []) {
@@ -7230,7 +7695,7 @@ function verificationRequirementMet(requirement, checks) {
7230
7695
  const normalized = normalizeCommand2(requirement);
7231
7696
  const broad = normalized.match(/^Record at least one successful (.+) after the final mutation\.?$/iu);
7232
7697
  if (broad) return checks.length > 0;
7233
- const commandKey = createHash9("sha256").update(normalized).digest("hex");
7698
+ const commandKey = createHash10("sha256").update(normalized).digest("hex");
7234
7699
  return checks.some((item) => item.commandKey === commandKey);
7235
7700
  }
7236
7701
  function acceptanceUnresolved(acceptance) {
@@ -7282,29 +7747,29 @@ function fileCount(count) {
7282
7747
  }
7283
7748
 
7284
7749
  // src/tools/task-contract.ts
7285
- var criterionSchema = z13.object({
7286
- id: z13.string().min(1).max(128).optional(),
7287
- description: z13.string().min(1).max(2e3),
7288
- required: z13.boolean().optional()
7750
+ var criterionSchema = z15.object({
7751
+ id: z15.string().min(1).max(128).optional(),
7752
+ description: z15.string().min(1).max(2e3),
7753
+ required: z15.boolean().optional()
7289
7754
  }).strict();
7290
- var inputSchema9 = z13.discriminatedUnion("action", [
7291
- z13.object({ action: z13.literal("show") }).strict(),
7292
- z13.object({
7293
- action: z13.literal("replace"),
7294
- objective: z13.string().min(1).max(2e4),
7295
- scope: z13.array(z13.string().min(1).max(2e3)).max(64),
7296
- constraints: z13.array(z13.string().min(1).max(2e3)).max(64),
7297
- non_goals: z13.array(z13.string().min(1).max(2e3)).max(64),
7298
- acceptance_criteria: z13.array(criterionSchema).min(1).max(64),
7299
- verification_requirements: z13.array(z13.string().min(1).max(2e3)).min(1).max(64)
7755
+ var inputSchema10 = z15.discriminatedUnion("action", [
7756
+ z15.object({ action: z15.literal("show") }).strict(),
7757
+ z15.object({
7758
+ action: z15.literal("replace"),
7759
+ objective: z15.string().min(1).max(2e4),
7760
+ scope: z15.array(z15.string().min(1).max(2e3)).max(64),
7761
+ constraints: z15.array(z15.string().min(1).max(2e3)).max(64),
7762
+ non_goals: z15.array(z15.string().min(1).max(2e3)).max(64),
7763
+ acceptance_criteria: z15.array(criterionSchema).min(1).max(64),
7764
+ verification_requirements: z15.array(z15.string().min(1).max(2e3)).min(1).max(64)
7300
7765
  }).strict(),
7301
- z13.object({ action: z13.literal("activate") }).strict(),
7302
- z13.object({
7303
- action: z13.literal("update_criterion"),
7304
- id: z13.string().min(1).max(128),
7305
- status: z13.enum(["pending", "satisfied", "blocked"]),
7306
- evidence_refs: z13.array(z13.string().min(1).max(256)).max(64).optional(),
7307
- note: z13.string().max(2e3).optional()
7766
+ z15.object({ action: z15.literal("activate") }).strict(),
7767
+ z15.object({
7768
+ action: z15.literal("update_criterion"),
7769
+ id: z15.string().min(1).max(128),
7770
+ status: z15.enum(["pending", "satisfied", "blocked"]),
7771
+ evidence_refs: z15.array(z15.string().min(1).max(256)).max(64).optional(),
7772
+ note: z15.string().max(2e3).optional()
7308
7773
  }).strict()
7309
7774
  ]);
7310
7775
  var taskContractTool = {
@@ -7346,7 +7811,7 @@ var taskContractTool = {
7346
7811
  }
7347
7812
  },
7348
7813
  async execute(arguments_, context) {
7349
- const input2 = inputSchema9.parse(arguments_);
7814
+ const input2 = inputSchema10.parse(arguments_);
7350
7815
  let contract = context.session.taskContract;
7351
7816
  if (!contract) throw new Error("No Task Contract is active for this session.");
7352
7817
  if (input2.action === "replace") {
@@ -7443,17 +7908,17 @@ function validateVerificationRequirement(requirement) {
7443
7908
  }
7444
7909
 
7445
7910
  // src/tools/working-memory.ts
7446
- import { z as z14 } from "zod";
7447
- var inputSchema10 = z14.discriminatedUnion("action", [
7448
- z14.object({ action: z14.literal("show") }).strict(),
7449
- z14.object({ action: z14.literal("set_goal"), value: z14.string().min(1).max(1e3) }).strict(),
7450
- z14.object({ action: z14.literal("set_focus"), value: z14.string().min(1).max(1e3) }).strict(),
7451
- z14.object({ action: z14.literal("add_constraint"), value: z14.string().min(1).max(1e3) }).strict(),
7452
- z14.object({ action: z14.literal("add_decision"), value: z14.string().min(1).max(1e3) }).strict(),
7453
- z14.object({ action: z14.literal("add_question"), value: z14.string().min(1).max(1e3) }).strict(),
7454
- z14.object({ action: z14.literal("resolve_question"), value: z14.string().min(1).max(1e3) }).strict(),
7455
- z14.object({ action: z14.literal("add_file"), path: z14.string().min(1).max(4e3) }).strict(),
7456
- z14.object({ action: z14.literal("clear"), field: z14.enum(["constraints", "decisions", "openQuestions", "relevantFiles"]) }).strict()
7911
+ import { z as z16 } from "zod";
7912
+ var inputSchema11 = z16.discriminatedUnion("action", [
7913
+ z16.object({ action: z16.literal("show") }).strict(),
7914
+ z16.object({ action: z16.literal("set_goal"), value: z16.string().min(1).max(1e3) }).strict(),
7915
+ z16.object({ action: z16.literal("set_focus"), value: z16.string().min(1).max(1e3) }).strict(),
7916
+ z16.object({ action: z16.literal("add_constraint"), value: z16.string().min(1).max(1e3) }).strict(),
7917
+ z16.object({ action: z16.literal("add_decision"), value: z16.string().min(1).max(1e3) }).strict(),
7918
+ z16.object({ action: z16.literal("add_question"), value: z16.string().min(1).max(1e3) }).strict(),
7919
+ z16.object({ action: z16.literal("resolve_question"), value: z16.string().min(1).max(1e3) }).strict(),
7920
+ z16.object({ action: z16.literal("add_file"), path: z16.string().min(1).max(4e3) }).strict(),
7921
+ z16.object({ action: z16.literal("clear"), field: z16.enum(["constraints", "decisions", "openQuestions", "relevantFiles"]) }).strict()
7457
7922
  ]);
7458
7923
  var workingMemoryTool = {
7459
7924
  definition: {
@@ -7478,7 +7943,7 @@ var workingMemoryTool = {
7478
7943
  }, ["action"])
7479
7944
  },
7480
7945
  async execute(arguments_, context) {
7481
- const input2 = inputSchema10.parse(arguments_);
7946
+ const input2 = inputSchema11.parse(arguments_);
7482
7947
  const memory = context.session.workingMemory ?? emptyWorkingMemory2();
7483
7948
  switch (input2.action) {
7484
7949
  case "show":
@@ -7560,6 +8025,7 @@ function render(memory) {
7560
8025
  function createDefaultToolRegistry(_options = {}) {
7561
8026
  return new ToolRegistry([
7562
8027
  readFileTool,
8028
+ readToolArtifactTool,
7563
8029
  listFilesTool,
7564
8030
  searchCodeTool,
7565
8031
  writeFileTool,
@@ -7688,7 +8154,7 @@ function escapeAttribute3(value) {
7688
8154
  }
7689
8155
 
7690
8156
  // src/agent/tool-recovery.ts
7691
- import { createHash as createHash10 } from "node:crypto";
8157
+ import { createHash as createHash11 } from "node:crypto";
7692
8158
  var RETRY_BUDGET = {
7693
8159
  schema_input: 3,
7694
8160
  unknown_tool: 2,
@@ -7783,10 +8249,10 @@ function isRetryable(failureClass) {
7783
8249
  return RETRY_BUDGET[failureClass] > 0;
7784
8250
  }
7785
8251
  function callSignature(call) {
7786
- return createHash10("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
8252
+ return createHash11("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
7787
8253
  }
7788
8254
  function failureSignature(call, failureClass) {
7789
- return createHash10("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
8255
+ return createHash11("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
7790
8256
  }
7791
8257
  function redact(value, key = "") {
7792
8258
  if (/api[_-]?key|authorization|cookie|password|secret|token/i.test(key)) return "[redacted]";
@@ -7803,10 +8269,212 @@ function isFailureClass(value) {
7803
8269
  return typeof value === "string" && Object.hasOwn(RETRY_BUDGET, value);
7804
8270
  }
7805
8271
 
8272
+ // src/agent/tool-output.ts
8273
+ import stripAnsi from "strip-ansi";
8274
+ var MIN_TOOL_OUTPUT_TOKENS = 1024;
8275
+ var MAX_TOOL_OUTPUT_TOKENS = 8192;
8276
+ async function protectToolOutput(options) {
8277
+ const safe = sanitizeToolOutput(options.content);
8278
+ const sanitized = redactToolOutput(safe.text);
8279
+ const estimatedTokens = estimateToolOutputTokens(sanitized.text);
8280
+ const budgetTokens = clamp2(options.budgetTokens, MIN_TOOL_OUTPUT_TOKENS, MAX_TOOL_OUTPUT_TOKENS);
8281
+ const base = {
8282
+ originalChars: options.content.length,
8283
+ originalBytes: Buffer.byteLength(options.content),
8284
+ estimatedTokens,
8285
+ budgetTokens,
8286
+ truncated: estimatedTokens > budgetTokens,
8287
+ redacted: sanitized.redacted,
8288
+ sanitized: safe.sanitized
8289
+ };
8290
+ if (estimatedTokens <= budgetTokens) return { content: sanitized.text, metadata: base };
8291
+ let archived;
8292
+ try {
8293
+ archived = await options.artifacts.archive(
8294
+ options.sessionId,
8295
+ options.toolCallId,
8296
+ sanitized.text,
8297
+ { redacted: sanitized.redacted }
8298
+ );
8299
+ } catch {
8300
+ archived = { stored: false, reason: "storage_error" };
8301
+ }
8302
+ const artifact = archived.stored ? archived.artifact : void 0;
8303
+ const artifactUnavailable = archived.stored ? void 0 : archived.reason;
8304
+ const metadata = {
8305
+ ...base,
8306
+ ...artifact ? {
8307
+ artifact: {
8308
+ toolCallId: artifact.toolCallId,
8309
+ sha256: artifact.sha256,
8310
+ bytes: artifact.bytes,
8311
+ createdAt: artifact.createdAt,
8312
+ expiresAt: artifact.expiresAt
8313
+ }
8314
+ } : artifactUnavailable ? { artifactUnavailable } : {}
8315
+ };
8316
+ const receipt = formatReceipt(options, metadata, "");
8317
+ const previewBudget = Math.max(0, budgetTokens - estimateToolOutputTokens(receipt));
8318
+ const preview = boundedPreview(sanitized.text, previewBudget);
8319
+ return {
8320
+ content: formatReceipt(options, metadata, preview),
8321
+ metadata
8322
+ };
8323
+ }
8324
+ function dynamicToolOutputBudget(contextWindowTokens, activeContextTokens, remainingSessionTokens) {
8325
+ const contextHeadroom = Math.max(0, contextWindowTokens - activeContextTokens);
8326
+ const sessionHeadroom = Math.max(0, remainingSessionTokens);
8327
+ const budget = Math.min(
8328
+ Math.floor(contextHeadroom * 0.35),
8329
+ Math.floor(sessionHeadroom * 0.12)
8330
+ );
8331
+ return clamp2(budget, MIN_TOOL_OUTPUT_TOKENS, MAX_TOOL_OUTPUT_TOKENS);
8332
+ }
8333
+ function estimateToolOutputTokens(value) {
8334
+ let tokens2 = 0;
8335
+ for (const character of value) {
8336
+ tokens2 += tokenCost(character);
8337
+ }
8338
+ return Math.max(1, Math.ceil(tokens2));
8339
+ }
8340
+ function formatReceipt(options, metadata, preview) {
8341
+ const lines = [
8342
+ "[Oversized tool output bounded for the model transcript]",
8343
+ `tool: ${boundedInlineByTokens(options.tool, 64)}`,
8344
+ `tool-call-id: ${boundedInlineByTokens(options.toolCallId, 96)}`,
8345
+ `status: ${options.ok ? "success" : "failure"}`,
8346
+ `original-output: ${metadata.originalChars} chars, ${metadata.originalBytes} bytes, ~${metadata.estimatedTokens} tokens`,
8347
+ `model-budget: ${metadata.budgetTokens} tokens (head + tail preview)`,
8348
+ ...metadata.redacted ? ["sensitive-values: redacted before display and retention"] : [],
8349
+ ...preservedSignals(options.metadata),
8350
+ ...metadata.artifact ? [
8351
+ `artifact: session-scoped; sha256 ${metadata.artifact.sha256}; ${metadata.artifact.bytes} bytes`,
8352
+ `read-back: read_tool_artifact({"sha256":"${metadata.artifact.sha256}","start_line":1,"max_lines":200}) before ${metadata.artifact.expiresAt}`
8353
+ ] : [`artifact: unavailable (${metadata.artifactUnavailable ?? "unknown"}); rerun the tool with a narrower result if more detail is needed`],
8354
+ "preview:",
8355
+ preview
8356
+ ];
8357
+ return lines.join("\n");
8358
+ }
8359
+ function preservedSignals(metadata) {
8360
+ const lines = [];
8361
+ if (typeof metadata.exitCode === "number") lines.push(`exit-code: ${metadata.exitCode}`);
8362
+ if (metadata.timedOut === true) lines.push("timed-out: true");
8363
+ if (metadata.sourceTruncated === true) {
8364
+ lines.push("source-truncated: true; the producing tool reached its capture limit before this firewall");
8365
+ }
8366
+ const changedFiles = metadata.changedFiles;
8367
+ if (Array.isArray(changedFiles)) {
8368
+ const allPaths = changedFiles.filter((path) => typeof path === "string");
8369
+ const paths = allPaths.slice(0, 3).map((path) => boundedInlineByTokens(path, 24));
8370
+ if (paths.length) {
8371
+ lines.push(`changed-files: ${paths.join(", ")}${allPaths.length > paths.length ? ` (+${allPaths.length - paths.length} more in metadata)` : ""}`);
8372
+ }
8373
+ }
8374
+ const failure = metadata.failure;
8375
+ if (isFailureReceipt(failure)) {
8376
+ lines.push(`failure-class: ${failure.class}; attempt ${failure.attempt}; ${failure.remaining} retries remain`);
8377
+ }
8378
+ return lines;
8379
+ }
8380
+ function isFailureReceipt(value) {
8381
+ return typeof value === "object" && value !== null && typeof value.class === "string" && typeof value.attempt === "number" && typeof value.remaining === "number";
8382
+ }
8383
+ function redactToolOutput(value) {
8384
+ let redacted = false;
8385
+ const replace = (expression, replacement) => {
8386
+ expression.lastIndex = 0;
8387
+ if (!expression.test(value)) return;
8388
+ redacted = true;
8389
+ expression.lastIndex = 0;
8390
+ value = value.replace(expression, replacement);
8391
+ };
8392
+ replace(/\b(sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,})\b/gu, "[redacted-secret]");
8393
+ replace(/\b(authorization\s*:\s*(?:bearer\s+|basic\s+)?)\S+/giu, "$1[redacted]");
8394
+ replace(/\b((?:api[_-]?key|access[_-]?token|authorization|cookie|password|client[_-]?secret)\s*[:=]\s*)(?:"[A-Za-z0-9+/_=-]{8,}"|'[A-Za-z0-9+/_=-]{8,}'|[A-Za-z0-9+/_=-]{8,})/giu, "$1[redacted]");
8395
+ replace(/(--(?:api[_-]?key|access[_-]?token|password|client[_-]?secret)(?:=|\s+))[A-Za-z0-9+/_=-]{8,}/giu, "$1[redacted]");
8396
+ return { text: value, redacted };
8397
+ }
8398
+ function sanitizeToolOutput(value) {
8399
+ const text = stripAnsi(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu, "");
8400
+ return { text, sanitized: text !== value };
8401
+ }
8402
+ function sliceStartByTokens(value, budget) {
8403
+ let used = 0;
8404
+ let end = 0;
8405
+ for (const character of value) {
8406
+ const cost = tokenCost(character);
8407
+ if (used + cost > budget) break;
8408
+ used += cost;
8409
+ end += character.length;
8410
+ }
8411
+ return value.slice(0, end);
8412
+ }
8413
+ function sliceEndByTokens(value, budget) {
8414
+ let used = 0;
8415
+ let start = value.length;
8416
+ while (start > 0) {
8417
+ let next = start - 1;
8418
+ const code = value.charCodeAt(next);
8419
+ if (code >= 56320 && code <= 57343 && next > 0) {
8420
+ const previous = value.charCodeAt(next - 1);
8421
+ if (previous >= 55296 && previous <= 56319) next -= 1;
8422
+ }
8423
+ const character = value.slice(next, start);
8424
+ const cost = tokenCost(character);
8425
+ if (used + cost > budget) break;
8426
+ used += cost;
8427
+ start = next;
8428
+ }
8429
+ return value.slice(start);
8430
+ }
8431
+ function boundedPreview(value, budget) {
8432
+ if (budget <= 0) return "";
8433
+ if (estimateToolOutputTokens(value) <= budget) return value;
8434
+ let low = 0;
8435
+ let high = budget;
8436
+ let best = "";
8437
+ while (low <= high) {
8438
+ const contentBudget = Math.floor((low + high) / 2);
8439
+ const headBudget = Math.ceil(contentBudget * 0.6);
8440
+ const tailBudget = Math.max(0, contentBudget - headBudget);
8441
+ const head = sliceStartByTokens(value, headBudget);
8442
+ const tail = sliceEndByTokens(value, tailBudget);
8443
+ const preview = `${head}
8444
+ \u2026 ${Math.max(0, value.length - head.length - tail.length)} chars omitted \u2026
8445
+ ${tail}`;
8446
+ if (estimateToolOutputTokens(preview) <= budget) {
8447
+ best = preview;
8448
+ low = contentBudget + 1;
8449
+ } else {
8450
+ high = contentBudget - 1;
8451
+ }
8452
+ }
8453
+ return best || sliceStartByTokens(value, budget);
8454
+ }
8455
+ function tokenCost(character) {
8456
+ const code = character.codePointAt(0) ?? 0;
8457
+ if (isCjkCharacter(character) || code > 127) return 2;
8458
+ return /[A-Z0-9\p{P}\p{S}]/u.test(character) ? 0.5 : 0.25;
8459
+ }
8460
+ function isCjkCharacter(character) {
8461
+ const code = character.codePointAt(0) ?? 0;
8462
+ return code >= 11904 && code <= 40959 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 131072 && code <= 195103;
8463
+ }
8464
+ function clamp2(value, minimum, maximum) {
8465
+ return Math.max(minimum, Math.min(maximum, value));
8466
+ }
8467
+ function boundedInlineByTokens(value, maxTokens) {
8468
+ const normalized = value.replace(/[\r\n\t]+/gu, " ").trim();
8469
+ if (estimateToolOutputTokens(normalized) <= maxTokens) return normalized;
8470
+ const suffix = "\u2026";
8471
+ return `${sliceStartByTokens(normalized, Math.max(0, maxTokens - tokenCost(suffix)))}${suffix}`;
8472
+ }
8473
+
7806
8474
  // src/agent/rules.ts
7807
8475
  import { existsSync as existsSync2 } from "node:fs";
7808
- import { join as join13 } from "node:path";
7809
- import { lstat as lstat15, readFile as readFile11 } from "node:fs/promises";
8476
+ import { join as join14 } from "node:path";
8477
+ import { lstat as lstat16, readFile as readFile12 } from "node:fs/promises";
7810
8478
  var workspaceRulePaths = [
7811
8479
  "AGENTS.md",
7812
8480
  "CLAUDE.md",
@@ -7815,14 +8483,14 @@ var workspaceRulePaths = [
7815
8483
  ];
7816
8484
  async function discoverWorkspaceRules(workspace, maxChars = 12e4) {
7817
8485
  const workspaceAccess = new WorkspaceAccess([workspace]);
7818
- const activeProjectRules = join13(resolveProjectNamespaceSync(workspace).active, "rules.md");
8486
+ const activeProjectRules = join14(resolveProjectNamespaceSync(workspace).active, "rules.md");
7819
8487
  const projectCandidates = [
7820
- ...workspaceRulePaths.slice(0, 3).map((path) => join13(workspace, path)),
8488
+ ...workspaceRulePaths.slice(0, 3).map((path) => join14(workspace, path)),
7821
8489
  activeProjectRules,
7822
- ...workspaceRulePaths.slice(3).map((path) => join13(workspace, path))
8490
+ ...workspaceRulePaths.slice(3).map((path) => join14(workspace, path))
7823
8491
  ];
7824
8492
  const candidates = [
7825
- { path: join13(resolveHomeNamespace(), "rules.md"), scope: "user" },
8493
+ { path: join14(resolveHomeNamespace(), "rules.md"), scope: "user" },
7826
8494
  ...projectCandidates.map((path) => ({
7827
8495
  path,
7828
8496
  scope: "workspace"
@@ -7833,13 +8501,13 @@ async function discoverWorkspaceRules(workspace, maxChars = 12e4) {
7833
8501
  for (const candidate of candidates) {
7834
8502
  if (remaining <= 0 || !existsSync2(candidate.path)) continue;
7835
8503
  try {
7836
- const info = await lstat15(candidate.path);
8504
+ const info = await lstat16(candidate.path);
7837
8505
  if (!info.isFile() || info.size > 1e6) continue;
7838
8506
  if (candidate.scope === "workspace") {
7839
8507
  const safePath = await workspaceAccess.resolvePath(candidate.path, { expect: "file" });
7840
8508
  if (safePath !== candidate.path) continue;
7841
8509
  }
7842
- const raw = await readFile11(candidate.path, "utf8");
8510
+ const raw = await readFile12(candidate.path, "utf8");
7843
8511
  if (raw.includes("\0")) continue;
7844
8512
  const content = raw.slice(0, remaining);
7845
8513
  rules.push({
@@ -7871,7 +8539,7 @@ function escapeAttribute4(value) {
7871
8539
  }
7872
8540
 
7873
8541
  // src/context/context-sources.ts
7874
- import { readFile as readFile12, stat as stat9 } from "node:fs/promises";
8542
+ import { readFile as readFile13, stat as stat9 } from "node:fs/promises";
7875
8543
  var MAX_SOURCE_CHARS = 6e4;
7876
8544
  var MAX_PINNED_CHARS = 16e4;
7877
8545
  var MAX_CONTEXT_SOURCES = 32;
@@ -7885,7 +8553,7 @@ async function pinContextSource(session, workspace, requested) {
7885
8553
  const resolved = await workspace.resolvePath(requested, { expect: "file" });
7886
8554
  const info = await stat9(resolved);
7887
8555
  const alias = workspace.display(resolved);
7888
- const tokens2 = estimateTokens3((await readFile12(resolved, "utf8")).slice(0, MAX_SOURCE_CHARS));
8556
+ const tokens2 = estimateTokens3((await readFile13(resolved, "utf8")).slice(0, MAX_SOURCE_CHARS));
7889
8557
  const list2 = sources(session);
7890
8558
  const existing = list2.find((source2) => source2.path === alias);
7891
8559
  if (existing) {
@@ -7935,7 +8603,7 @@ async function resolvePinnedContent(session, workspace) {
7935
8603
  if (remaining <= 0) break;
7936
8604
  try {
7937
8605
  const safe = await workspace.resolvePath(source.path, { expect: "file" });
7938
- const raw = await readFile12(safe, "utf8");
8606
+ const raw = await readFile13(safe, "utf8");
7939
8607
  const capped = raw.slice(0, Math.min(MAX_SOURCE_CHARS, remaining));
7940
8608
  source.tokens = estimateTokens3(capped);
7941
8609
  resolved.push({
@@ -7983,6 +8651,7 @@ var AgentRunner = class {
7983
8651
  contextEngine;
7984
8652
  tools;
7985
8653
  sessionStore;
8654
+ toolArtifactStore;
7986
8655
  checkpointStore;
7987
8656
  workspace;
7988
8657
  hooks;
@@ -8004,6 +8673,7 @@ var AgentRunner = class {
8004
8673
  contextEngine: this.contextEngine
8005
8674
  });
8006
8675
  this.sessionStore = options.sessionStore ?? new SessionStore(this.workspace.primaryRoot);
8676
+ this.toolArtifactStore = options.toolArtifactStore ?? new ToolArtifactStore(this.workspace.primaryRoot);
8007
8677
  this.checkpointStore = options.checkpointStore ?? new CheckpointStore(this.workspace);
8008
8678
  this.hooks = new HookRunner(options.config.hooks, this.workspace);
8009
8679
  this.contextManager = options.contextManager ?? new ContextManager(options.config);
@@ -8096,6 +8766,7 @@ var AgentRunner = class {
8096
8766
  };
8097
8767
  try {
8098
8768
  throwIfAborted(options.signal);
8769
+ await this.reconcileToolArtifacts();
8099
8770
  if (this.session.messages.length === 0 && this.session.title === "New session") {
8100
8771
  this.session.title = titleFromInput(request);
8101
8772
  }
@@ -8202,9 +8873,13 @@ var AgentRunner = class {
8202
8873
  contextBudget
8203
8874
  );
8204
8875
  const availableTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
8205
- const estimatedInputTokens = estimateMessages2(messages) + estimateToolDefinitions(
8206
- visibleToolDefinitions(this.tools, options.askMode === true, contractEnabled)
8876
+ const visibleTools = visibleToolDefinitions(
8877
+ this.tools,
8878
+ options.askMode === true,
8879
+ contractEnabled,
8880
+ this.hasReadableToolArtifact()
8207
8881
  );
8882
+ const estimatedInputTokens = estimateMessages2(messages) + estimateToolDefinitions(visibleTools);
8208
8883
  if (availableTokens <= 0 || estimatedInputTokens >= availableTokens) {
8209
8884
  return finishRun("token_budget");
8210
8885
  }
@@ -8212,7 +8887,6 @@ var AgentRunner = class {
8212
8887
  this.config.model.maxTokens ?? 8192,
8213
8888
  availableTokens - estimatedInputTokens
8214
8889
  ));
8215
- const visibleTools = visibleToolDefinitions(this.tools, options.askMode === true, contractEnabled);
8216
8890
  const assistantId = randomUUID12();
8217
8891
  const response = await this.completeModel(
8218
8892
  messages,
@@ -8370,17 +9044,17 @@ ${input2}`
8370
9044
  async executeTool(call, options, emit, recovery = new ToolRecoveryController(), visibleToolNames) {
8371
9045
  const preflight = recovery.preflight(call);
8372
9046
  if (preflight) {
8373
- const result = failedResult(call, "Tool call rejected by the recovery circuit.", preflight);
9047
+ const result = await this.protectToolResult(
9048
+ failedResult(call, "Tool call rejected by the recovery circuit.", preflight)
9049
+ );
8374
9050
  this.recordToolResult(result);
8375
9051
  await emit({ type: "tool_result", result });
8376
9052
  return result;
8377
9053
  }
8378
9054
  if (visibleToolNames && !visibleToolNames.has(call.name)) {
8379
9055
  const receipt = recovery.recordFailure(call, "unknown_tool");
8380
- const result = failedResult(
8381
- call,
8382
- `Tool is not exposed for this turn: ${call.name}`,
8383
- receipt
9056
+ const result = await this.protectToolResult(
9057
+ failedResult(call, `Tool is not exposed for this turn: ${call.name}`, receipt)
8384
9058
  );
8385
9059
  this.recordToolResult(result);
8386
9060
  await emit({ type: "tool_result", result });
@@ -8389,7 +9063,7 @@ ${input2}`
8389
9063
  const tool = this.tools.get(call.name);
8390
9064
  if (!tool) {
8391
9065
  const receipt = recovery.recordFailure(call, "unknown_tool");
8392
- const result = failedResult(call, `Unknown tool: ${call.name}`, receipt);
9066
+ const result = await this.protectToolResult(failedResult(call, `Unknown tool: ${call.name}`, receipt));
8393
9067
  this.recordToolResult(result);
8394
9068
  await emit({ type: "tool_result", result });
8395
9069
  return result;
@@ -8402,17 +9076,15 @@ ${input2}`
8402
9076
  } catch (error) {
8403
9077
  const failureClass = classifyThrownToolFailure(error, options.signal);
8404
9078
  const receipt = recovery.recordFailure(call, failureClass);
8405
- const result = failedResult(call, formatToolError(error), receipt);
9079
+ const result = await this.protectToolResult(failedResult(call, formatToolError(error), receipt));
8406
9080
  this.recordToolResult(result, tool.definition.category);
8407
9081
  await emit({ type: "tool_result", result });
8408
9082
  return result;
8409
9083
  }
8410
9084
  if (categories.some((category) => category !== "read") && this.session.taskContract?.state === "draft") {
8411
9085
  const receipt = recovery.recordFailure(call, "contract_required");
8412
- const result = failedResult(
8413
- call,
8414
- "Potentially mutating work is paused until the draft Task Contract is activated.",
8415
- receipt
9086
+ const result = await this.protectToolResult(
9087
+ failedResult(call, "Potentially mutating work is paused until the draft Task Contract is activated.", receipt)
8416
9088
  );
8417
9089
  this.recordToolResult(result, tool.definition.category);
8418
9090
  await emit({ type: "tool_result", result });
@@ -8422,7 +9094,9 @@ ${input2}`
8422
9094
  const allowed = await this.authorize(call, category, options, emit);
8423
9095
  if (!allowed) {
8424
9096
  const receipt = recovery.recordFailure(call, "permission_denied");
8425
- const result = failedResult(call, `Permission denied for ${category} operation.`, receipt);
9097
+ const result = await this.protectToolResult(
9098
+ failedResult(call, `Permission denied for ${category} operation.`, receipt)
9099
+ );
8426
9100
  this.recordToolResult(result, category);
8427
9101
  await emit({ type: "tool_result", result });
8428
9102
  return result;
@@ -8436,6 +9110,7 @@ ${input2}`
8436
9110
  workspace: this.workspace,
8437
9111
  session: this.session,
8438
9112
  contextEngine: this.contextEngine,
9113
+ toolArtifactStore: this.toolArtifactStore,
8439
9114
  emit,
8440
9115
  ...options.signal ? { signal: options.signal } : {}
8441
9116
  };
@@ -8469,30 +9144,33 @@ ${input2}`
8469
9144
  } catch (error) {
8470
9145
  afterHookError = toError(error);
8471
9146
  }
8472
- const result = {
8473
- toolCallId: call.id,
8474
- name: call.name,
8475
- ok: execution.ok !== false && !afterHookError,
8476
- content: truncateToolOutput(afterHookError ? `${execution.content}
9147
+ let completeContent = afterHookError ? `${execution.content}
8477
9148
 
8478
- Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : execution.content),
8479
- metadata: {
8480
- ...execution.metadata ?? {},
8481
- ...changedFiles.length ? { changedFiles } : {},
8482
- ...checkpointId ? { checkpointId } : {},
8483
- ...beforeHooks.length || afterHooks.length ? { hooks: { before: beforeHooks.length, after: afterHooks.length } } : {},
8484
- ...afterHookError ? { toolSucceeded: true, hookError: afterHookError.message } : {}
8485
- }
9149
+ Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : execution.content;
9150
+ const metadata = {
9151
+ ...execution.metadata ?? {},
9152
+ ...changedFiles.length ? { changedFiles } : {},
9153
+ ...checkpointId ? { checkpointId } : {},
9154
+ ...beforeHooks.length || afterHooks.length ? { hooks: { before: beforeHooks.length, after: afterHooks.length } } : {},
9155
+ ...afterHookError ? { toolSucceeded: true, hookError: afterHookError.message } : {}
8486
9156
  };
8487
- if (!result.ok) {
8488
- const failureClass = options.signal?.aborted ? "cancelled" : classifyToolFailure(result);
9157
+ const ok = execution.ok !== false && !afterHookError;
9158
+ if (!ok) {
9159
+ const failureClass = options.signal?.aborted ? "cancelled" : classifyToolFailure({ toolCallId: call.id, name: call.name, ok, content: completeContent, metadata });
8489
9160
  const receipt = recovery.recordFailure(call, failureClass);
8490
- result.content = truncateToolOutput(`${formatFailureReceipt(receipt)}
8491
- ${result.content}`);
8492
- result.metadata = { ...result.metadata, failure: receipt };
9161
+ completeContent = `${formatFailureReceipt(receipt)}
9162
+ ${completeContent}`;
9163
+ metadata.failure = receipt;
8493
9164
  } else {
8494
9165
  recovery.recordSuccess(call);
8495
9166
  }
9167
+ const result = await this.protectToolResult({
9168
+ toolCallId: call.id,
9169
+ name: call.name,
9170
+ ok,
9171
+ content: completeContent,
9172
+ metadata
9173
+ });
8496
9174
  this.contextManager.recordTool(this.session, call, result);
8497
9175
  if (JSON.stringify(this.session.tasks) !== tasksBefore || call.name === "task") {
8498
9176
  await emit({ type: "tasks", tasks: this.session.tasks.map((task) => ({ ...task })) });
@@ -8507,7 +9185,7 @@ ${result.content}`);
8507
9185
  const normalized = toError(error);
8508
9186
  const failureClass = classifyThrownToolFailure(normalized, options.signal);
8509
9187
  const receipt = recovery.recordFailure(call, failureClass);
8510
- const result = failedResult(call, formatToolError(error), receipt);
9188
+ const result = await this.protectToolResult(failedResult(call, formatToolError(error), receipt));
8511
9189
  this.recordToolResult(result, tool.definition.category);
8512
9190
  await emit({ type: "tool_result", result });
8513
9191
  return result;
@@ -8661,6 +9339,63 @@ ${result.content}`);
8661
9339
  persist() {
8662
9340
  return this.persistSession ? this.sessionStore.save(this.session) : Promise.resolve();
8663
9341
  }
9342
+ toolOutputBudget() {
9343
+ const contextWindowTokens = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
9344
+ const activeContextTokens = this.contextManager.status(this.session, contextWindowTokens).activeTokens;
9345
+ const remainingSessionTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
9346
+ return dynamicToolOutputBudget(contextWindowTokens, activeContextTokens, remainingSessionTokens);
9347
+ }
9348
+ async protectToolResult(result) {
9349
+ const metadata = { ...result.metadata ?? {} };
9350
+ const output2 = await protectToolOutput({
9351
+ content: result.content,
9352
+ sessionId: this.session.id,
9353
+ toolCallId: result.toolCallId,
9354
+ tool: result.name,
9355
+ ok: result.ok,
9356
+ budgetTokens: this.toolOutputBudget(),
9357
+ metadata,
9358
+ artifacts: this.toolArtifactStore
9359
+ });
9360
+ if (output2.metadata.artifact) this.registerToolArtifact({
9361
+ ...output2.metadata.artifact,
9362
+ redacted: output2.metadata.redacted
9363
+ });
9364
+ return {
9365
+ ...result,
9366
+ content: output2.content,
9367
+ ...output2.metadata.truncated || output2.metadata.redacted || output2.metadata.sanitized || output2.metadata.artifact || output2.metadata.artifactUnavailable ? { metadata: { ...metadata, toolOutput: output2.metadata } } : Object.keys(metadata).length ? { metadata } : {}
9368
+ };
9369
+ }
9370
+ hasReadableToolArtifact() {
9371
+ const now = Date.now();
9372
+ return (this.session.toolArtifacts ?? []).some(
9373
+ (artifact) => Date.parse(artifact.expiresAt) > now
9374
+ );
9375
+ }
9376
+ registerToolArtifact(artifact) {
9377
+ const now = Date.now();
9378
+ const artifacts = (this.session.toolArtifacts ?? []).filter((candidate) => Date.parse(candidate.expiresAt) > now && candidate.toolCallId !== artifact.toolCallId);
9379
+ artifacts.push(artifact);
9380
+ artifacts.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
9381
+ if (artifacts.length > 200) artifacts.splice(0, artifacts.length - 200);
9382
+ this.session.toolArtifacts = artifacts;
9383
+ }
9384
+ async reconcileToolArtifacts() {
9385
+ let stored;
9386
+ try {
9387
+ stored = await this.toolArtifactStore.prune(this.session.id);
9388
+ } catch {
9389
+ this.session.toolArtifacts = [];
9390
+ return;
9391
+ }
9392
+ const valid = new Map(stored.map((artifact) => [artifact.toolCallId, artifact]));
9393
+ const reconciled = (this.session.toolArtifacts ?? []).filter((receipt) => {
9394
+ const artifact = valid.get(receipt.toolCallId);
9395
+ return artifact !== void 0 && artifact.sha256 === receipt.sha256 && artifact.bytes === receipt.bytes && artifact.createdAt === receipt.createdAt && artifact.expiresAt === receipt.expiresAt && artifact.redacted === receipt.redacted;
9396
+ });
9397
+ this.session.toolArtifacts = reconciled;
9398
+ }
8664
9399
  };
8665
9400
  function message(role, content, extra = {}) {
8666
9401
  return {
@@ -8740,21 +9475,16 @@ function failedResult(call, content, failure) {
8740
9475
  toolCallId: call.id,
8741
9476
  name: call.name,
8742
9477
  ok: false,
8743
- content: truncateToolOutput(failure ? `${formatFailureReceipt(failure)}
8744
- ${content}` : content),
9478
+ content: failure ? `${formatFailureReceipt(failure)}
9479
+ ${content}` : content,
8745
9480
  ...failure ? { metadata: { failure } } : {}
8746
9481
  };
8747
9482
  }
8748
- function visibleToolDefinitions(tools, askMode, contractEnabled) {
9483
+ function visibleToolDefinitions(tools, askMode, contractEnabled, artifactReadAvailable) {
8749
9484
  return tools.definitions().filter(
8750
- (tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract")
9485
+ (tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract") && (artifactReadAvailable || tool.name !== "read_tool_artifact")
8751
9486
  );
8752
9487
  }
8753
- function truncateToolOutput(content) {
8754
- const max = 8e4;
8755
- return content.length <= max ? content : `${content.slice(0, max)}
8756
- \u2026 tool output truncated`;
8757
- }
8758
9488
  function formatToolError(error) {
8759
9489
  const normalized = toError(error);
8760
9490
  if (normalized.name === "ZodError") return `Invalid tool arguments: ${normalized.message}`;
@@ -8787,9 +9517,9 @@ async function safeEmit(emit, event) {
8787
9517
  }
8788
9518
 
8789
9519
  // src/agent/profiles.ts
8790
- import { lstat as lstat16, readFile as readFile13, readdir as readdir5 } from "node:fs/promises";
9520
+ import { lstat as lstat17, readFile as readFile14, readdir as readdir6 } from "node:fs/promises";
8791
9521
  import { homedir as homedir3 } from "node:os";
8792
- import { basename as basename8, join as join14 } from "node:path";
9522
+ import { basename as basename8, join as join15 } from "node:path";
8793
9523
  import { parse as parseYaml2 } from "yaml";
8794
9524
  var builtInProfiles = [
8795
9525
  {
@@ -8881,14 +9611,14 @@ var AgentProfileCatalog = class {
8881
9611
  profiles = new Map(builtInProfiles.map((profile) => [profile.name, profile]));
8882
9612
  async discover() {
8883
9613
  const locations = [
8884
- { path: join14(resolveHomeNamespace(), "agents"), source: "user" },
8885
- { path: join14(homedir3(), ".claude", "agents"), source: "user" },
8886
- { path: join14(resolveProjectNamespaceSync(this.workspace).active, "agents"), source: "workspace" },
8887
- { path: join14(this.workspace, ".claude", "agents"), source: "workspace" }
9614
+ { path: join15(resolveHomeNamespace(), "agents"), source: "user" },
9615
+ { path: join15(homedir3(), ".claude", "agents"), source: "user" },
9616
+ { path: join15(resolveProjectNamespaceSync(this.workspace).active, "agents"), source: "workspace" },
9617
+ { path: join15(this.workspace, ".claude", "agents"), source: "workspace" }
8888
9618
  ];
8889
9619
  for (const location of locations) {
8890
9620
  for (const file of await markdownFiles(location.path)) {
8891
- const profile = await readProfile(join14(location.path, file), location.source);
9621
+ const profile = await readProfile(join15(location.path, file), location.source);
8892
9622
  if (profile) this.profiles.set(profile.name, profile);
8893
9623
  }
8894
9624
  }
@@ -8904,18 +9634,18 @@ var AgentProfileCatalog = class {
8904
9634
  };
8905
9635
  async function markdownFiles(directory) {
8906
9636
  try {
8907
- const info = await lstat16(directory);
9637
+ const info = await lstat17(directory);
8908
9638
  if (!info.isDirectory() || info.isSymbolicLink()) return [];
8909
- return (await readdir5(directory, { withFileTypes: true })).filter((entry) => entry.isFile() && !entry.isSymbolicLink() && entry.name.endsWith(".md")).map((entry) => entry.name).slice(0, 200);
9639
+ return (await readdir6(directory, { withFileTypes: true })).filter((entry) => entry.isFile() && !entry.isSymbolicLink() && entry.name.endsWith(".md")).map((entry) => entry.name).slice(0, 200);
8910
9640
  } catch {
8911
9641
  return [];
8912
9642
  }
8913
9643
  }
8914
9644
  async function readProfile(path, source) {
8915
9645
  try {
8916
- const info = await lstat16(path);
9646
+ const info = await lstat17(path);
8917
9647
  if (!info.isFile() || info.isSymbolicLink() || info.size > 1e5) return void 0;
8918
- const raw = await readFile13(path, "utf8");
9648
+ const raw = await readFile14(path, "utf8");
8919
9649
  const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
8920
9650
  const frontmatter = match ? parseYaml2(match[1] ?? "") : {};
8921
9651
  const prompt = (match ? raw.slice(match[0].length) : raw).trim();
@@ -8943,8 +9673,8 @@ function integer(value, fallback, min, max) {
8943
9673
 
8944
9674
  // src/agent/delegation.ts
8945
9675
  import { randomUUID as randomUUID14 } from "node:crypto";
8946
- import { join as join17 } from "node:path";
8947
- import { z as z16 } from "zod";
9676
+ import { join as join18 } from "node:path";
9677
+ import { z as z18 } from "zod";
8948
9678
 
8949
9679
  // src/agent/external-runtime.ts
8950
9680
  async function runExternalAgent(request) {
@@ -9071,95 +9801,95 @@ function numeric(...values) {
9071
9801
  }
9072
9802
 
9073
9803
  // src/agent/team-store.ts
9074
- import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
9075
- import { lstat as lstat17, readFile as readFile14, readdir as readdir6, rm as rm2 } from "node:fs/promises";
9076
- import { join as join15, resolve as resolve15 } from "node:path";
9077
- import { z as z15 } from "zod";
9078
- var runIdSchema = z15.string().uuid();
9079
- var hashSchema = z15.string().regex(/^[a-f0-9]{64}$/u);
9080
- var artifactSchema = z15.object({
9804
+ import { createHash as createHash12, randomUUID as randomUUID13 } from "node:crypto";
9805
+ import { lstat as lstat18, readFile as readFile15, readdir as readdir7, rm as rm3 } from "node:fs/promises";
9806
+ import { join as join16, resolve as resolve16 } from "node:path";
9807
+ import { z as z17 } from "zod";
9808
+ var runIdSchema = z17.string().uuid();
9809
+ var hashSchema = z17.string().regex(/^[a-f0-9]{64}$/u);
9810
+ var artifactSchema2 = z17.object({
9081
9811
  sha256: hashSchema,
9082
- bytes: z15.number().int().nonnegative().max(5e5)
9812
+ bytes: z17.number().int().nonnegative().max(5e5)
9083
9813
  }).strict();
9084
- var phaseSchema = z15.enum(["work", "review", "revision", "write"]);
9085
- var agentRecordSchema = z15.object({
9086
- id: z15.string().uuid(),
9087
- profile: z15.string(),
9088
- provider: z15.string(),
9089
- model: z15.string(),
9814
+ var phaseSchema = z17.enum(["work", "review", "revision", "write"]);
9815
+ var agentRecordSchema = z17.object({
9816
+ id: z17.string().uuid(),
9817
+ profile: z17.string(),
9818
+ provider: z17.string(),
9819
+ model: z17.string(),
9090
9820
  phase: phaseSchema,
9091
- ok: z15.boolean(),
9092
- createdAt: z15.string(),
9093
- startedAt: z15.string().optional(),
9094
- endedAt: z15.string().optional(),
9095
- durationMs: z15.number().int().nonnegative().optional(),
9096
- toolCalls: z15.number().int().nonnegative().optional(),
9097
- usage: z15.object({
9098
- inputTokens: z15.number().int().nonnegative(),
9099
- outputTokens: z15.number().int().nonnegative()
9821
+ ok: z17.boolean(),
9822
+ createdAt: z17.string(),
9823
+ startedAt: z17.string().optional(),
9824
+ endedAt: z17.string().optional(),
9825
+ durationMs: z17.number().int().nonnegative().optional(),
9826
+ toolCalls: z17.number().int().nonnegative().optional(),
9827
+ usage: z17.object({
9828
+ inputTokens: z17.number().int().nonnegative(),
9829
+ outputTokens: z17.number().int().nonnegative()
9100
9830
  }).strict().optional(),
9101
- report: artifactSchema
9831
+ report: artifactSchema2
9102
9832
  }).strict();
9103
- var messageRecordSchema = z15.object({
9104
- id: z15.string().uuid(),
9105
- from: z15.string(),
9106
- to: z15.string(),
9107
- createdAt: z15.string(),
9108
- content: artifactSchema
9833
+ var messageRecordSchema = z17.object({
9834
+ id: z17.string().uuid(),
9835
+ from: z17.string(),
9836
+ to: z17.string(),
9837
+ createdAt: z17.string(),
9838
+ content: artifactSchema2
9109
9839
  }).strict();
9110
- var writerIntegrationSchema = z15.object({
9111
- status: z15.enum(["ready", "conflict", "integrated"]),
9112
- checkedAt: z15.string(),
9113
- detail: z15.string().max(2e4),
9114
- checkpoint: z15.object({
9115
- sessionId: z15.string(),
9116
- checkpointId: z15.string()
9840
+ var writerIntegrationSchema = z17.object({
9841
+ status: z17.enum(["ready", "conflict", "integrated"]),
9842
+ checkedAt: z17.string(),
9843
+ detail: z17.string().max(2e4),
9844
+ checkpoint: z17.object({
9845
+ sessionId: z17.string(),
9846
+ checkpointId: z17.string()
9117
9847
  }).strict().optional(),
9118
- integratedAt: z15.string().optional()
9848
+ integratedAt: z17.string().optional()
9119
9849
  }).strict();
9120
- var writerLaneSchema = z15.object({
9121
- profile: z15.string(),
9122
- reviewer: z15.string(),
9123
- baseCommit: z15.string().regex(/^[a-f0-9]{40,64}$/u),
9124
- outcome: z15.enum(["accepted", "rejected", "failed", "cancelled"]),
9125
- patch: artifactSchema,
9126
- files: z15.array(z15.string().min(1).max(4e3)).max(2e3),
9127
- worktreeCleaned: z15.boolean(),
9128
- review: artifactSchema.optional(),
9850
+ var writerLaneSchema = z17.object({
9851
+ profile: z17.string(),
9852
+ reviewer: z17.string(),
9853
+ baseCommit: z17.string().regex(/^[a-f0-9]{40,64}$/u),
9854
+ outcome: z17.enum(["accepted", "rejected", "failed", "cancelled"]),
9855
+ patch: artifactSchema2,
9856
+ files: z17.array(z17.string().min(1).max(4e3)).max(2e3),
9857
+ worktreeCleaned: z17.boolean(),
9858
+ review: artifactSchema2.optional(),
9129
9859
  integration: writerIntegrationSchema.optional()
9130
9860
  }).strict();
9131
9861
  var manifestFields = {
9132
9862
  id: runIdSchema,
9133
- workspace: z15.string(),
9134
- objective: z15.string().max(3e4),
9135
- reviewer: z15.string(),
9136
- createdAt: z15.string(),
9137
- updatedAt: z15.string(),
9138
- status: z15.enum(["running", "accepted", "rejected", "failed"]),
9139
- maxReviewRounds: z15.number().int().min(0).max(3),
9140
- reviewRounds: z15.number().int().min(0).max(3),
9141
- agents: z15.array(agentRecordSchema).max(256),
9142
- messages: z15.array(messageRecordSchema).max(512)
9863
+ workspace: z17.string(),
9864
+ objective: z17.string().max(3e4),
9865
+ reviewer: z17.string(),
9866
+ createdAt: z17.string(),
9867
+ updatedAt: z17.string(),
9868
+ status: z17.enum(["running", "accepted", "rejected", "failed"]),
9869
+ maxReviewRounds: z17.number().int().min(0).max(3),
9870
+ reviewRounds: z17.number().int().min(0).max(3),
9871
+ agents: z17.array(agentRecordSchema).max(256),
9872
+ messages: z17.array(messageRecordSchema).max(512)
9143
9873
  };
9144
- var manifestV1Schema = z15.object({
9145
- version: z15.literal(1),
9874
+ var manifestV1Schema = z17.object({
9875
+ version: z17.literal(1),
9146
9876
  ...manifestFields
9147
9877
  }).strict();
9148
- var manifestV2Schema = z15.object({
9149
- version: z15.literal(2),
9878
+ var manifestV2Schema = z17.object({
9879
+ version: z17.literal(2),
9150
9880
  ...manifestFields,
9151
9881
  writer: writerLaneSchema.optional()
9152
9882
  }).strict();
9153
- var manifestSchema2 = z15.discriminatedUnion("version", [manifestV1Schema, manifestV2Schema]);
9883
+ var manifestSchema2 = z17.discriminatedUnion("version", [manifestV1Schema, manifestV2Schema]);
9154
9884
  var TeamRunStore = class {
9155
9885
  workspace;
9156
9886
  directory;
9157
9887
  managedDirectory;
9158
9888
  writes = Promise.resolve();
9159
9889
  constructor(workspace, directory) {
9160
- this.workspace = resolve15(workspace);
9890
+ this.workspace = resolve16(workspace);
9161
9891
  this.managedDirectory = directory === void 0;
9162
- this.directory = directory ? resolve15(directory) : join15(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
9892
+ this.directory = directory ? resolve16(directory) : join16(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
9163
9893
  }
9164
9894
  async create(input2) {
9165
9895
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -9243,10 +9973,10 @@ var TeamRunStore = class {
9243
9973
  runIdSchema.parse(runId);
9244
9974
  await this.writes;
9245
9975
  await this.assertRunDirectory(runId);
9246
- const path = join15(this.runDirectory(runId), "manifest.json");
9976
+ const path = join16(this.runDirectory(runId), "manifest.json");
9247
9977
  await this.assertRegularFile(path);
9248
- const manifest = manifestSchema2.parse(JSON.parse(await readFile14(path, "utf8")));
9249
- if (manifest.id !== runId || resolve15(manifest.workspace) !== this.workspace) {
9978
+ const manifest = manifestSchema2.parse(JSON.parse(await readFile15(path, "utf8")));
9979
+ if (manifest.id !== runId || resolve16(manifest.workspace) !== this.workspace) {
9250
9980
  throw new Error("Team run manifest identity does not match its location.");
9251
9981
  }
9252
9982
  if (verify) {
@@ -9260,13 +9990,13 @@ var TeamRunStore = class {
9260
9990
  }
9261
9991
  async readArtifact(runId, artifact) {
9262
9992
  await this.verifyArtifact(runId, artifact);
9263
- return readFile14(this.artifactPath(runId, artifact.sha256), "utf8");
9993
+ return readFile15(this.artifactPath(runId, artifact.sha256), "utf8");
9264
9994
  }
9265
9995
  async list() {
9266
9996
  await this.writes;
9267
9997
  try {
9268
9998
  await assertNoSymlinkPath(this.workspace, this.directory);
9269
- const entries = await readdir6(this.directory, { withFileTypes: true });
9999
+ const entries = await readdir7(this.directory, { withFileTypes: true });
9270
10000
  const summaries = [];
9271
10001
  for (const entry of entries) {
9272
10002
  if (!entry.isDirectory() || entry.isSymbolicLink() || !runIdSchema.safeParse(entry.name).success) continue;
@@ -9289,9 +10019,9 @@ var TeamRunStore = class {
9289
10019
  const directory = this.runDirectory(runId);
9290
10020
  await assertNoSymlinkPath(this.workspace, directory);
9291
10021
  try {
9292
- const info = await lstat17(directory);
10022
+ const info = await lstat18(directory);
9293
10023
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
9294
- await rm2(directory, { recursive: true });
10024
+ await rm3(directory, { recursive: true });
9295
10025
  return true;
9296
10026
  } catch (error) {
9297
10027
  if (error.code === "ENOENT") return false;
@@ -9314,31 +10044,31 @@ var TeamRunStore = class {
9314
10044
  }
9315
10045
  async loadUnlocked(runId) {
9316
10046
  await this.assertRunDirectory(runId);
9317
- const path = join15(this.runDirectory(runId), "manifest.json");
10047
+ const path = join16(this.runDirectory(runId), "manifest.json");
9318
10048
  await this.assertRegularFile(path);
9319
- return manifestSchema2.parse(JSON.parse(await readFile14(path, "utf8")));
10049
+ return manifestSchema2.parse(JSON.parse(await readFile15(path, "utf8")));
9320
10050
  }
9321
10051
  async writeManifest(manifest) {
9322
10052
  const directory = this.runDirectory(manifest.id);
9323
10053
  await ensureWorkspaceStorageDirectory(this.workspace, directory, {
9324
10054
  requireActiveNamespace: this.managedDirectory
9325
10055
  });
9326
- await atomicWrite(join15(directory, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
10056
+ await atomicWrite(join16(directory, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
9327
10057
  `, 384);
9328
10058
  }
9329
10059
  async writeArtifact(runId, content, truncate = true) {
9330
10060
  const data = boundedArtifactText(content, 5e5, truncate);
9331
10061
  const bytes = Buffer.byteLength(data);
9332
- const sha256 = createHash11("sha256").update(data).digest("hex");
9333
- const directory = join15(this.runDirectory(runId), "blobs");
10062
+ const sha256 = createHash12("sha256").update(data).digest("hex");
10063
+ const directory = join16(this.runDirectory(runId), "blobs");
9334
10064
  await ensureWorkspaceStorageDirectory(this.workspace, directory, {
9335
10065
  requireActiveNamespace: this.managedDirectory
9336
10066
  });
9337
- const path = join15(directory, `${sha256}.txt`);
10067
+ const path = join16(directory, `${sha256}.txt`);
9338
10068
  try {
9339
10069
  await this.assertRegularFile(path);
9340
- const existing = await readFile14(path);
9341
- if (createHash11("sha256").update(existing).digest("hex") !== sha256) {
10070
+ const existing = await readFile15(path);
10071
+ if (createHash12("sha256").update(existing).digest("hex") !== sha256) {
9342
10072
  throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
9343
10073
  }
9344
10074
  } catch (error) {
@@ -9358,27 +10088,27 @@ var TeamRunStore = class {
9358
10088
  hashSchema.parse(artifact.sha256);
9359
10089
  const path = this.artifactPath(runId, artifact.sha256);
9360
10090
  await this.assertRegularFile(path);
9361
- const data = await readFile14(path);
9362
- const hash = createHash11("sha256").update(data).digest("hex");
9363
- if (hash !== artifact.sha256 || data.byteLength !== artifact.bytes) {
10091
+ const data = await readFile15(path);
10092
+ const hash2 = createHash12("sha256").update(data).digest("hex");
10093
+ if (hash2 !== artifact.sha256 || data.byteLength !== artifact.bytes) {
9364
10094
  throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
9365
10095
  }
9366
10096
  }
9367
10097
  artifactPath(runId, sha256) {
9368
- return join15(this.runDirectory(runId), "blobs", `${sha256}.txt`);
10098
+ return join16(this.runDirectory(runId), "blobs", `${sha256}.txt`);
9369
10099
  }
9370
10100
  runDirectory(runId) {
9371
- return join15(this.directory, runId);
10101
+ return join16(this.directory, runId);
9372
10102
  }
9373
10103
  async assertRunDirectory(runId) {
9374
10104
  const directory = this.runDirectory(runId);
9375
10105
  await assertNoSymlinkPath(this.workspace, directory);
9376
- const info = await lstat17(directory);
10106
+ const info = await lstat18(directory);
9377
10107
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
9378
10108
  }
9379
10109
  async assertRegularFile(path) {
9380
- await assertNoSymlinkPath(this.workspace, resolve15(path, ".."));
9381
- const info = await lstat17(path);
10110
+ await assertNoSymlinkPath(this.workspace, resolve16(path, ".."));
10111
+ const info = await lstat18(path);
9382
10112
  if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Team run file is not a regular file: ${path}`);
9383
10113
  }
9384
10114
  };
@@ -9422,10 +10152,10 @@ function resolveAgentModelRoute(team, parent, profile) {
9422
10152
  }
9423
10153
 
9424
10154
  // src/agent/writer-lane.ts
9425
- import { createHash as createHash12 } from "node:crypto";
9426
- import { lstat as lstat18, mkdtemp, realpath as realpath7, rm as rm3 } from "node:fs/promises";
10155
+ import { createHash as createHash13 } from "node:crypto";
10156
+ import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
9427
10157
  import { tmpdir as tmpdir2 } from "node:os";
9428
- import { isAbsolute as isAbsolute5, join as join16, resolve as resolve16 } from "node:path";
10158
+ import { isAbsolute as isAbsolute5, join as join17, resolve as resolve17 } from "node:path";
9429
10159
  var WriterLaneApplyError = class extends Error {
9430
10160
  constructor(message2, attempted, cause) {
9431
10161
  super(message2, cause === void 0 ? void 0 : { cause });
@@ -9438,18 +10168,18 @@ var WriterLane = class {
9438
10168
  workspace;
9439
10169
  constructor(workspace, workspaceRoots) {
9440
10170
  this.workspace = new WorkspaceAccess([
9441
- resolve16(workspace),
9442
- ...workspaceRoots.map((root) => resolve16(root))
10171
+ resolve17(workspace),
10172
+ ...workspaceRoots.map((root) => resolve17(root))
9443
10173
  ]);
9444
10174
  }
9445
10175
  async createDraft(maxPatchBytes, operation, signal) {
9446
10176
  const repository = await this.repository();
9447
10177
  const baseCommit = await this.head(repository);
9448
10178
  const lease = await acquireNamespaceLease(
9449
- join16(repository.commonDirectory, "skein-writer-lane"),
10179
+ join17(repository.commonDirectory, "skein-writer-lane"),
9450
10180
  "exclusive"
9451
10181
  );
9452
- const worktree = await mkdtemp(join16(tmpdir2(), "skein-writer-"));
10182
+ const worktree = await mkdtemp(join17(tmpdir2(), "skein-writer-"));
9453
10183
  let added = false;
9454
10184
  let value;
9455
10185
  let patch = "";
@@ -9520,7 +10250,7 @@ var WriterLane = class {
9520
10250
  return {
9521
10251
  baseCommit,
9522
10252
  patch,
9523
- patchSha256: createHash12("sha256").update(patch).digest("hex"),
10253
+ patchSha256: createHash13("sha256").update(patch).digest("hex"),
9524
10254
  files,
9525
10255
  worktreeCleaned,
9526
10256
  value
@@ -9539,7 +10269,7 @@ var WriterLane = class {
9539
10269
  try {
9540
10270
  const repository = await this.repository();
9541
10271
  const lease = await acquireNamespaceLease(
9542
- join16(repository.commonDirectory, "skein-writer-lane"),
10272
+ join17(repository.commonDirectory, "skein-writer-lane"),
9543
10273
  "exclusive"
9544
10274
  );
9545
10275
  try {
@@ -9647,7 +10377,7 @@ var WriterLane = class {
9647
10377
  if (isAbsolute5(file) || file === ".git" || file.startsWith(".git/") || file.includes("\0")) {
9648
10378
  throw new Error(`Writer patch contains an unsafe path: ${file}`);
9649
10379
  }
9650
- await this.workspace.resolvePath(join16(repository.root, file), { allowMissing: true });
10380
+ await this.workspace.resolvePath(join17(repository.root, file), { allowMissing: true });
9651
10381
  }
9652
10382
  if (expectedFiles && !sameSet(files, expectedFiles)) {
9653
10383
  throw new Error("Writer patch paths do not match the persisted file manifest.");
@@ -9662,7 +10392,7 @@ var WriterLane = class {
9662
10392
  if (topLevel.exitCode !== 0 || !topLevel.stdout.trim()) {
9663
10393
  throw new Error("The primary workspace must be a Git repository root for writer lanes.");
9664
10394
  }
9665
- const discoveredRoot = resolve16(topLevel.stdout.trim());
10395
+ const discoveredRoot = resolve17(topLevel.stdout.trim());
9666
10396
  if (await realpath7(discoveredRoot) !== await realpath7(root)) {
9667
10397
  throw new Error("The primary workspace must be the Git repository root for writer lanes.");
9668
10398
  }
@@ -9671,7 +10401,7 @@ var WriterLane = class {
9671
10401
  if (common.exitCode !== 0 || !common.stdout.trim()) {
9672
10402
  throw new Error(`Unable to resolve the Git common directory: ${processDetail(common)}`);
9673
10403
  }
9674
- const commonDirectory = await realpath7(isAbsolute5(common.stdout.trim()) ? common.stdout.trim() : resolve16(repositoryRoot, common.stdout.trim()));
10404
+ const commonDirectory = await realpath7(isAbsolute5(common.stdout.trim()) ? common.stdout.trim() : resolve17(repositoryRoot, common.stdout.trim()));
9675
10405
  return { root: repositoryRoot, commonDirectory, runtime };
9676
10406
  }
9677
10407
  async head(repository) {
@@ -9683,7 +10413,7 @@ var WriterLane = class {
9683
10413
  return value;
9684
10414
  }
9685
10415
  async cleanupWorktree(repository, worktree, added) {
9686
- const physicalWorktree = await realpath7(worktree).catch(() => resolve16(worktree));
10416
+ const physicalWorktree = await realpath7(worktree).catch(() => resolve17(worktree));
9687
10417
  if (added) {
9688
10418
  await runIsolatedGit(repository.runtime, [
9689
10419
  "worktree",
@@ -9692,7 +10422,7 @@ var WriterLane = class {
9692
10422
  worktree
9693
10423
  ], repository.root, { timeoutMs: 6e4 }).catch(() => void 0);
9694
10424
  }
9695
- await rm3(worktree, { recursive: true, force: true }).catch(() => void 0);
10425
+ await rm4(worktree, { recursive: true, force: true }).catch(() => void 0);
9696
10426
  await runIsolatedGit(repository.runtime, [
9697
10427
  "worktree",
9698
10428
  "prune",
@@ -9741,7 +10471,7 @@ function processDetail(result) {
9741
10471
  }
9742
10472
  async function pathExists2(path) {
9743
10473
  try {
9744
- await lstat18(path);
10474
+ await lstat19(path);
9745
10475
  return true;
9746
10476
  } catch (error) {
9747
10477
  if (error.code === "ENOENT") return false;
@@ -9750,14 +10480,14 @@ async function pathExists2(path) {
9750
10480
  }
9751
10481
 
9752
10482
  // src/agent/delegation.ts
9753
- var writerRunInputSchema = z16.object({
9754
- task: z16.string().min(1).max(2e4),
9755
- profile: z16.string().max(64).optional(),
9756
- reviewer: z16.string().max(64).optional()
10483
+ var writerRunInputSchema = z18.object({
10484
+ task: z18.string().min(1).max(2e4),
10485
+ profile: z18.string().max(64).optional(),
10486
+ reviewer: z18.string().max(64).optional()
9757
10487
  }).strict();
9758
- var writerIntegrateInputSchema = z16.object({
9759
- run_id: z16.string().uuid(),
9760
- patch_sha256: z16.string().regex(/^[a-f0-9]{64}$/u)
10488
+ var writerIntegrateInputSchema = z18.object({
10489
+ run_id: z18.string().uuid(),
10490
+ patch_sha256: z18.string().regex(/^[a-f0-9]{64}$/u)
9761
10491
  }).strict();
9762
10492
  var DelegationManager = class {
9763
10493
  constructor(options) {
@@ -9818,10 +10548,10 @@ var DelegationManager = class {
9818
10548
  },
9819
10549
  async execute(arguments_, context) {
9820
10550
  if (!manager.team.enabled) return { ok: false, content: "Multi-agent delegation is disabled." };
9821
- const input2 = z16.object({
9822
- tasks: z16.array(z16.object({
9823
- profile: z16.string().max(64).optional(),
9824
- task: z16.string().min(1).max(2e4)
10551
+ const input2 = z18.object({
10552
+ tasks: z18.array(z18.object({
10553
+ profile: z18.string().max(64).optional(),
10554
+ task: z18.string().min(1).max(2e4)
9825
10555
  })).min(1).max(manager.team.maxDelegations)
9826
10556
  }).parse(arguments_);
9827
10557
  const tasks = input2.tasks.map((task) => ({
@@ -9867,13 +10597,13 @@ var DelegationManager = class {
9867
10597
  },
9868
10598
  async execute(arguments_, context) {
9869
10599
  if (!manager.team.enabled) return { ok: false, content: "Multi-agent teams are disabled." };
9870
- const input2 = z16.object({
9871
- objective: z16.string().min(1).max(3e4),
9872
- tasks: z16.array(z16.object({
9873
- profile: z16.string().max(64).optional(),
9874
- task: z16.string().min(1).max(2e4)
10600
+ const input2 = z18.object({
10601
+ objective: z18.string().min(1).max(3e4),
10602
+ tasks: z18.array(z18.object({
10603
+ profile: z18.string().max(64).optional(),
10604
+ task: z18.string().min(1).max(2e4)
9875
10605
  })).min(1).max(manager.team.maxDelegations),
9876
- reviewer: z16.string().max(64).optional()
10606
+ reviewer: z18.string().max(64).optional()
9877
10607
  }).parse(arguments_);
9878
10608
  const tasks = input2.tasks.map((task) => ({
9879
10609
  profile: task.profile ?? manager.team.defaultProfile,
@@ -9936,7 +10666,7 @@ var DelegationManager = class {
9936
10666
  const plan = await manager.loadWriterPlan(input2.run_id, input2.patch_sha256);
9937
10667
  const files = await manager.writerLane.inspectPatch(plan.patch, plan.writer.files);
9938
10668
  return Promise.all(files.map(
9939
- (file) => context.workspace.resolvePath(join17(context.workspace.primaryRoot, file), { allowMissing: true })
10669
+ (file) => context.workspace.resolvePath(join18(context.workspace.primaryRoot, file), { allowMissing: true })
9940
10670
  ));
9941
10671
  },
9942
10672
  async execute(arguments_, context) {
@@ -10123,7 +10853,7 @@ ${review.summary}`,
10123
10853
  const plan = await this.loadWriterPlan(runId, patchSha256);
10124
10854
  const files = await this.writerLane.inspectPatch(plan.patch, plan.writer.files);
10125
10855
  const paths = await Promise.all(files.map(
10126
- (file) => context.workspace.resolvePath(join17(context.workspace.primaryRoot, file), { allowMissing: true })
10856
+ (file) => context.workspace.resolvePath(join18(context.workspace.primaryRoot, file), { allowMissing: true })
10127
10857
  ));
10128
10858
  const checkpointStore = new CheckpointStore(context.workspace);
10129
10859
  let checkpointId = context.checkpointId;
@@ -11598,7 +12328,7 @@ function formatResultDetail(result, paint = chalk2, glyphs = resolveCliGlyphs())
11598
12328
  }
11599
12329
 
11600
12330
  // src/cli/namespace-leases.ts
11601
- import { resolve as resolve17 } from "node:path";
12331
+ import { resolve as resolve18 } from "node:path";
11602
12332
  function cliNamespaceLeaseScopes(actionCommand) {
11603
12333
  const names = commandNames(actionCommand);
11604
12334
  const topLevel = names[1];
@@ -11620,7 +12350,7 @@ async function acquireCliNamespaceLeases(actionCommand) {
11620
12350
  const scopes = cliNamespaceLeaseScopes(actionCommand);
11621
12351
  const localOptions = actionCommand.opts();
11622
12352
  const globalOptions = actionCommand.optsWithGlobals();
11623
- const workspace = resolve17(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
12353
+ const workspace = resolve18(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
11624
12354
  const targets = scopes.map((scope) => scope === "project" ? projectNamespacePaths(workspace).canonical : homeNamespacePaths().canonical);
11625
12355
  const leases = [];
11626
12356
  try {
@@ -11774,11 +12504,11 @@ function command(name, description, usage, aliases) {
11774
12504
 
11775
12505
  // src/ui/text.ts
11776
12506
  import stringWidth from "string-width";
11777
- import stripAnsi from "strip-ansi";
12507
+ import stripAnsi2 from "strip-ansi";
11778
12508
  var controlCharacters = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu;
11779
12509
  var escapeSequences = /[[\]][0-9;?=>!]*[ -/]*[@-~]|[[\]][?=>][0-9;?=>!]*[a-zA-Z~]/gu;
11780
12510
  function sanitizeTerminalText(value) {
11781
- return stripAnsi(value).replace(escapeSequences, "").replace(/\r\n?/g, "\n").replace(controlCharacters, "");
12511
+ return stripAnsi2(value).replace(escapeSequences, "").replace(/\r\n?/g, "\n").replace(controlCharacters, "");
11782
12512
  }
11783
12513
  function terminalEllipsis() {
11784
12514
  return process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii" ? "..." : "\u2026";
@@ -11871,8 +12601,8 @@ function graphemes(value) {
11871
12601
  }
11872
12602
 
11873
12603
  // src/ui/theme.ts
11874
- import { lstat as lstat19, readdir as readdir7, readFile as readFile15 } from "node:fs/promises";
11875
- import { basename as basename9, join as join18, resolve as resolve18 } from "node:path";
12604
+ import { lstat as lstat20, readdir as readdir8, readFile as readFile16 } from "node:fs/promises";
12605
+ import { basename as basename9, join as join19, resolve as resolve19 } from "node:path";
11876
12606
  import React, { createContext, useContext } from "react";
11877
12607
  function defineTheme(seed) {
11878
12608
  return {
@@ -11994,10 +12724,10 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
11994
12724
  userThemeNames.clear();
11995
12725
  const loaded = [];
11996
12726
  const errors = [];
11997
- const resolvedDirectory = resolve18(directory);
12727
+ const resolvedDirectory = resolve19(directory);
11998
12728
  let entries;
11999
12729
  try {
12000
- entries = await readdir7(resolvedDirectory, { encoding: "utf8" });
12730
+ entries = await readdir8(resolvedDirectory, { encoding: "utf8" });
12001
12731
  } catch (error) {
12002
12732
  if (error.code === "ENOENT") {
12003
12733
  return { directory: resolvedDirectory, loaded, errors };
@@ -12006,13 +12736,13 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
12006
12736
  }
12007
12737
  for (const entry of entries) {
12008
12738
  if (!entry.endsWith(".json")) continue;
12009
- const path = join18(resolvedDirectory, entry);
12739
+ const path = join19(resolvedDirectory, entry);
12010
12740
  try {
12011
- const info = await lstat19(path);
12741
+ const info = await lstat20(path);
12012
12742
  if (!info.isFile() || info.isSymbolicLink() || info.size > 64e3) {
12013
12743
  throw new Error("must be a regular JSON file smaller than 64 KB");
12014
12744
  }
12015
- const parsed = JSON.parse(await readFile15(path, "utf8"));
12745
+ const parsed = JSON.parse(await readFile16(path, "utf8"));
12016
12746
  const name = themeName(parsed, basename9(entry, ".json"));
12017
12747
  if (builtInThemeNames.has(name)) throw new Error(`cannot replace built-in theme \`${name}\``);
12018
12748
  themes[name] = defineTheme(themeSeed(parsed, name));
@@ -12025,7 +12755,7 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
12025
12755
  return { directory: resolvedDirectory, loaded, errors };
12026
12756
  }
12027
12757
  function userThemeDirectory(environment = process.env) {
12028
- return environment.SKEIN_THEME_DIR ?? environment.MOSAIC_THEME_DIR ?? join18(resolveHomeNamespace(environment), "themes");
12758
+ return environment.SKEIN_THEME_DIR ?? environment.MOSAIC_THEME_DIR ?? join19(resolveHomeNamespace(environment), "themes");
12029
12759
  }
12030
12760
  var defaultTheme = themes.graphite;
12031
12761
  var palette = {
@@ -13252,9 +13982,9 @@ function safeWidth(width) {
13252
13982
  }
13253
13983
 
13254
13984
  // src/utils/update-check.ts
13255
- import { mkdir as mkdir9, readFile as readFile16, writeFile as writeFile2 } from "node:fs/promises";
13256
- import { join as join19 } from "node:path";
13257
- import stripAnsi2 from "strip-ansi";
13985
+ import { mkdir as mkdir9, readFile as readFile17, writeFile as writeFile2 } from "node:fs/promises";
13986
+ import { join as join20 } from "node:path";
13987
+ import stripAnsi3 from "strip-ansi";
13258
13988
  var PACKAGE_NAME = "@skein-code/cli";
13259
13989
  var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
13260
13990
  var CACHE_FILE = "update-check.json";
@@ -13269,7 +13999,7 @@ function sanitizeHighlights(value) {
13269
13999
  const cleaned = [];
13270
14000
  for (const entry of value) {
13271
14001
  if (typeof entry !== "string") continue;
13272
- const flattened = stripAnsi2(entry).replace(BIDI_CONTROLS, "").replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ").replace(/\s+/gu, " ").trim();
14002
+ const flattened = stripAnsi3(entry).replace(BIDI_CONTROLS, "").replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ").replace(/\s+/gu, " ").trim();
13273
14003
  if (!flattened || flattened.length > MAX_HIGHLIGHT_LENGTH) continue;
13274
14004
  cleaned.push(flattened);
13275
14005
  if (cleaned.length >= MAX_HIGHLIGHTS) break;
@@ -13333,7 +14063,7 @@ function compareVersions(a, b) {
13333
14063
  return 0;
13334
14064
  }
13335
14065
  function updateCachePath(env = process.env) {
13336
- return join19(resolveHomeNamespace(env), CACHE_FILE);
14066
+ return join20(resolveHomeNamespace(env), CACHE_FILE);
13337
14067
  }
13338
14068
  function upgradeCommand() {
13339
14069
  return `npm i -g ${PACKAGE_NAME}`;
@@ -13350,7 +14080,7 @@ function noticeIfNewer(latest, current, highlights) {
13350
14080
  }
13351
14081
  async function readUpdateCache(env = process.env) {
13352
14082
  try {
13353
- const raw = await readFile16(updateCachePath(env), "utf8");
14083
+ const raw = await readFile17(updateCachePath(env), "utf8");
13354
14084
  const parsed = JSON.parse(raw);
13355
14085
  if (typeof parsed?.checkedAt === "number" && Number.isFinite(parsed.checkedAt) && parsed.checkedAt >= 0 && (parsed.latest === null || typeof parsed.latest === "string" && parseVersion(parsed.latest))) {
13356
14086
  const latest = typeof parsed.latest === "string" ? parsed.latest : null;
@@ -14431,7 +15161,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
14431
15161
  return () => clearInterval(timer);
14432
15162
  }, [busy]);
14433
15163
  const requestPermission = useCallback((call, category) => {
14434
- return new Promise((resolve23) => setPermission({ call, category, resolve: resolve23 }));
15164
+ return new Promise((resolve24) => setPermission({ call, category, resolve: resolve24 }));
14435
15165
  }, []);
14436
15166
  const onEvent = useCallback((event) => {
14437
15167
  switch (event.type) {
@@ -15255,8 +15985,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
15255
15985
  }, [append]);
15256
15986
  function settlePermission(grant, stop = false) {
15257
15987
  if (!permission) return;
15258
- const { call, category, resolve: resolve23 } = permission;
15259
- resolve23(grant);
15988
+ const { call, category, resolve: resolve24 } = permission;
15989
+ resolve24(grant);
15260
15990
  setPermission(void 0);
15261
15991
  if (grant === "session") {
15262
15992
  append({
@@ -16591,24 +17321,24 @@ async function runFirstRunOnboarding(initialConfig, options = {}) {
16591
17321
  }
16592
17322
 
16593
17323
  // src/runtime/extensions.ts
16594
- import { resolve as resolve21 } from "node:path";
17324
+ import { resolve as resolve22 } from "node:path";
16595
17325
 
16596
17326
  // src/mcp/manager.ts
16597
17327
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
16598
17328
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
16599
17329
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
16600
- import stripAnsi4 from "strip-ansi";
17330
+ import stripAnsi5 from "strip-ansi";
16601
17331
 
16602
17332
  // src/mcp/tool.ts
16603
- import { createHash as createHash13 } from "node:crypto";
16604
- import stripAnsi3 from "strip-ansi";
17333
+ import { createHash as createHash14 } from "node:crypto";
17334
+ import stripAnsi4 from "strip-ansi";
16605
17335
  var MAX_ARGUMENT_BYTES = 256e3;
16606
17336
  var MAX_DESCRIPTION_LENGTH = 4e3;
16607
- var MAX_RESULT_LENGTH = 8e4;
17337
+ var MAX_RESULT_BYTES = 5 * 1024 * 1024;
16608
17338
  var MAX_SCHEMA_BYTES = 1e5;
16609
17339
  function createMcpToolAdapter(options) {
16610
17340
  const { remoteTool } = options;
16611
- const inputSchema11 = copyInputSchema(remoteTool.inputSchema);
17341
+ const inputSchema12 = copyInputSchema(remoteTool.inputSchema);
16612
17342
  return {
16613
17343
  definition: {
16614
17344
  name: options.exposedName,
@@ -16616,7 +17346,7 @@ function createMcpToolAdapter(options) {
16616
17346
  // MCP servers are an external trust boundary. Read-only annotations are
16617
17347
  // hints from that server and must not lower the local permission level.
16618
17348
  category: "network",
16619
- inputSchema: inputSchema11
17349
+ inputSchema: inputSchema12
16620
17350
  },
16621
17351
  permissionCategories: () => ["network"],
16622
17352
  async execute(arguments_, context) {
@@ -16636,6 +17366,8 @@ function createMcpToolAdapter(options) {
16636
17366
  metadata: {
16637
17367
  mcpServer: options.serverName,
16638
17368
  mcpTool: remoteTool.name,
17369
+ sourceBytes: normalized.sourceBytes,
17370
+ sourceTruncated: normalized.sourceTruncated,
16639
17371
  ...normalized.isError ? { mcpError: true } : {}
16640
17372
  }
16641
17373
  };
@@ -16665,8 +17397,8 @@ function isUsableRemoteTool(tool) {
16665
17397
  }
16666
17398
  }
16667
17399
  function describeTool(serverName, tool) {
16668
- const label = stripAnsi3(tool.title?.trim() || tool.name).replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 200);
16669
- const description = tool.description ? stripAnsi3(tool.description).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim().slice(0, MAX_DESCRIPTION_LENGTH) : void 0;
17400
+ const label = stripAnsi4(tool.title?.trim() || tool.name).replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 200);
17401
+ const description = tool.description ? stripAnsi4(tool.description).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim().slice(0, MAX_DESCRIPTION_LENGTH) : void 0;
16670
17402
  return description ? `[MCP ${serverName}/${label}] ${description}` : `Call the ${label} tool provided by the ${serverName} MCP server.`;
16671
17403
  }
16672
17404
  function copyInputSchema(schema) {
@@ -16689,7 +17421,7 @@ function assertArguments(arguments_) {
16689
17421
  }
16690
17422
  function normalizeCallResult(result) {
16691
17423
  if (!isRecord2(result)) {
16692
- return { content: truncateResult(safeJson(result)), isError: false };
17424
+ return boundResult(safeJson(result), false);
16693
17425
  }
16694
17426
  const isError = result.isError === true;
16695
17427
  const sections = [];
@@ -16705,7 +17437,22 @@ ${safeJson(result.structuredContent)}`);
16705
17437
  ${safeJson(result.toolResult)}`);
16706
17438
  }
16707
17439
  const content = sections.filter(Boolean).join("\n\n") || (isError ? "The MCP tool reported an error." : "The MCP tool completed without output.");
16708
- return { content: truncateResult(content), isError };
17440
+ return boundResult(content, isError);
17441
+ }
17442
+ function boundResult(content, isError) {
17443
+ const sourceBytes = Buffer.byteLength(content);
17444
+ if (sourceBytes <= MAX_RESULT_BYTES) {
17445
+ return { content, isError, sourceBytes, sourceTruncated: false };
17446
+ }
17447
+ const marker = `
17448
+ ... ${sourceBytes - MAX_RESULT_BYTES} or more source bytes omitted by the MCP safety limit ...
17449
+ `;
17450
+ const markerBytes = Buffer.byteLength(marker);
17451
+ const available = MAX_RESULT_BYTES - markerBytes;
17452
+ const headBytes = Math.floor(available * 0.6);
17453
+ const tailBytes = available - headBytes;
17454
+ const bounded = `${sliceUtf8Start(content, headBytes)}${marker}${sliceUtf8End(content, tailBytes)}`;
17455
+ return { content: bounded, isError, sourceBytes, sourceTruncated: true };
16709
17456
  }
16710
17457
  function formatContentBlock(block) {
16711
17458
  if (!isRecord2(block) || typeof block.type !== "string") return safeJson(block);
@@ -16753,15 +17500,10 @@ function fitToolName(name, identity) {
16753
17500
  return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
16754
17501
  }
16755
17502
  function shortHash(value) {
16756
- return createHash13("sha256").update(value).digest("hex").slice(0, 8);
16757
- }
16758
- function truncateResult(content) {
16759
- if (content.length <= MAX_RESULT_LENGTH) return content;
16760
- return `${content.slice(0, MAX_RESULT_LENGTH)}
16761
- ... MCP result truncated`;
17503
+ return createHash14("sha256").update(value).digest("hex").slice(0, 8);
16762
17504
  }
16763
17505
  function sanitizeOutputText(value) {
16764
- return stripAnsi3(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
17506
+ return stripAnsi4(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
16765
17507
  }
16766
17508
  function sanitizeInlineText(value) {
16767
17509
  return sanitizeOutputText(value).replace(/[\r\n\t]+/g, " ").trim().slice(0, 2e3);
@@ -16773,13 +17515,25 @@ function safeJson(value) {
16773
17515
  return String(value);
16774
17516
  }
16775
17517
  }
17518
+ function sliceUtf8Start(value, maxBytes) {
17519
+ const buffer = Buffer.from(value);
17520
+ let end = Math.min(buffer.length, maxBytes);
17521
+ while (end > 0 && (buffer[end] ?? 0) >= 128 && (buffer[end] ?? 0) < 192) end -= 1;
17522
+ return buffer.subarray(0, end).toString("utf8");
17523
+ }
17524
+ function sliceUtf8End(value, maxBytes) {
17525
+ const buffer = Buffer.from(value);
17526
+ let start = Math.max(0, buffer.length - maxBytes);
17527
+ while (start < buffer.length && (buffer[start] ?? 0) >= 128 && (buffer[start] ?? 0) < 192) start += 1;
17528
+ return buffer.subarray(start).toString("utf8");
17529
+ }
16776
17530
  function isRecord2(value) {
16777
17531
  return typeof value === "object" && value !== null && !Array.isArray(value);
16778
17532
  }
16779
17533
 
16780
17534
  // src/mcp/validation.ts
16781
17535
  import { stat as stat10, realpath as realpath8 } from "node:fs/promises";
16782
- import { isAbsolute as isAbsolute6, resolve as resolve19 } from "node:path";
17536
+ import { isAbsolute as isAbsolute6, resolve as resolve20 } from "node:path";
16783
17537
  var SERVER_NAME = /^[a-z][a-z0-9_-]{0,63}$/;
16784
17538
  var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
16785
17539
  var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
@@ -16851,9 +17605,9 @@ async function validateCwd(configured, options) {
16851
17605
  if (configured !== void 0 && (configured.length > 4e3 || CONTROL_CHARACTERS.test(configured))) {
16852
17606
  throw new Error("MCP working directory is invalid or too long");
16853
17607
  }
16854
- const defaultCwd = resolve19(options.cwd ?? process.cwd());
16855
- const candidate = configured ? isAbsolute6(configured) ? resolve19(configured) : resolve19(defaultCwd, configured) : defaultCwd;
16856
- const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve19(root)) : [defaultCwd];
17608
+ const defaultCwd = resolve20(options.cwd ?? process.cwd());
17609
+ const candidate = configured ? isAbsolute6(configured) ? resolve20(configured) : resolve20(defaultCwd, configured) : defaultCwd;
17610
+ const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve20(root)) : [defaultCwd];
16857
17611
  const resolvedCandidate = await realpath8(candidate).catch(() => {
16858
17612
  throw new Error(`MCP working directory does not exist: ${candidate}`);
16859
17613
  });
@@ -17191,7 +17945,7 @@ var McpManager = class {
17191
17945
  stderr: "pipe"
17192
17946
  });
17193
17947
  transport.stderr?.on("data", (chunk) => {
17194
- const text = stripAnsi4(chunk.toString()).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "").trim();
17948
+ const text = stripAnsi5(chunk.toString()).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "").trim();
17195
17949
  if (text) this.options.logger?.(`MCP ${name} stderr`, { text: text.slice(0, 2e3) });
17196
17950
  });
17197
17951
  return transport;
@@ -17350,7 +18104,7 @@ function errorMessage2(error) {
17350
18104
  return sanitizeStatusText(message2) || "Unknown MCP error";
17351
18105
  }
17352
18106
  function sanitizeStatusText(value) {
17353
- return stripAnsi4(value).replace(/[\u0000-\u001f\u007f]/g, " ").trim().slice(0, 1e3);
18107
+ return stripAnsi5(value).replace(/[\u0000-\u001f\u007f]/g, " ").trim().slice(0, 1e3);
17354
18108
  }
17355
18109
  function timeoutError(timeoutMs) {
17356
18110
  const error = new Error(`MCP request timed out after ${timeoutMs} ms`);
@@ -17376,9 +18130,9 @@ async function closeTransportQuietly(transport) {
17376
18130
  }
17377
18131
 
17378
18132
  // src/memory/tools.ts
17379
- import { z as z17 } from "zod";
17380
- var scopeSchema = z17.enum(["user", "workspace", "session", "agent"]);
17381
- var kindSchema = z17.enum(["semantic", "episodic", "procedural"]);
18133
+ import { z as z19 } from "zod";
18134
+ var scopeSchema = z19.enum(["user", "workspace", "session", "agent"]);
18135
+ var kindSchema = z19.enum(["semantic", "episodic", "procedural"]);
17382
18136
  function createMemoryTools(store) {
17383
18137
  return [
17384
18138
  {
@@ -17393,10 +18147,10 @@ function createMemoryTools(store) {
17393
18147
  }, ["query"])
17394
18148
  },
17395
18149
  async execute(arguments_, context) {
17396
- const input2 = z17.object({
17397
- query: z17.string().max(4e3),
18150
+ const input2 = z19.object({
18151
+ query: z19.string().max(4e3),
17398
18152
  scope: scopeSchema.optional(),
17399
- limit: z17.number().int().min(1).max(20).optional()
18153
+ limit: z19.number().int().min(1).max(20).optional()
17400
18154
  }).parse(arguments_);
17401
18155
  const scopes = input2.scope ? [{ scope: input2.scope, scopeKey: scopeKey(input2.scope, context) }] : availableScopes(context);
17402
18156
  const records = store.search(input2.query, { scopes, limit: input2.limit ?? 8 });
@@ -17428,17 +18182,17 @@ ${record.content}`
17428
18182
  }, ["content", "rationale"])
17429
18183
  },
17430
18184
  async execute(arguments_, context) {
17431
- const input2 = z17.object({
17432
- content: z17.string().min(1).max(12e3),
17433
- rationale: z17.string().min(1).max(1e3),
18185
+ const input2 = z19.object({
18186
+ content: z19.string().min(1).max(12e3),
18187
+ rationale: z19.string().min(1).max(1e3),
17434
18188
  scope: scopeSchema.optional(),
17435
18189
  kind: kindSchema.optional(),
17436
- tags: z17.array(z17.string()).max(24).optional(),
17437
- importance: z17.number().min(0).max(1).optional(),
17438
- confidence: z17.number().min(0).max(1).optional(),
17439
- agent: z17.string().max(64).optional(),
17440
- revision: z17.string().max(240).optional(),
17441
- conflictKey: z17.string().max(240).optional()
18190
+ tags: z19.array(z19.string()).max(24).optional(),
18191
+ importance: z19.number().min(0).max(1).optional(),
18192
+ confidence: z19.number().min(0).max(1).optional(),
18193
+ agent: z19.string().max(64).optional(),
18194
+ revision: z19.string().max(240).optional(),
18195
+ conflictKey: z19.string().max(240).optional()
17442
18196
  }).strict().parse(arguments_);
17443
18197
  const scope = input2.scope ?? "workspace";
17444
18198
  const candidate = store.propose({
@@ -17476,9 +18230,9 @@ ${record.content}`
17476
18230
  }, ["id"])
17477
18231
  },
17478
18232
  async execute(arguments_) {
17479
- const input2 = z17.object({
17480
- id: z17.string().uuid(),
17481
- permanent: z17.boolean().optional()
18233
+ const input2 = z19.object({
18234
+ id: z19.string().uuid(),
18235
+ permanent: z19.boolean().optional()
17482
18236
  }).parse(arguments_);
17483
18237
  const changed = input2.permanent ? store.remove(input2.id) : store.archive(input2.id);
17484
18238
  return {
@@ -17505,9 +18259,9 @@ function scopeKey(scope, context) {
17505
18259
  }
17506
18260
 
17507
18261
  // src/skills/catalog.ts
17508
- import { lstat as lstat20, readFile as readFile17, readdir as readdir8, realpath as realpath9 } from "node:fs/promises";
18262
+ import { lstat as lstat21, readFile as readFile18, readdir as readdir9, realpath as realpath9 } from "node:fs/promises";
17509
18263
  import { homedir as homedir4 } from "node:os";
17510
- import { basename as basename11, join as join20, resolve as resolve20 } from "node:path";
18264
+ import { basename as basename11, join as join21, resolve as resolve21 } from "node:path";
17511
18265
  import { parse as parseYaml3 } from "yaml";
17512
18266
  var SkillCatalog = class {
17513
18267
  constructor(workspace, config) {
@@ -17527,7 +18281,7 @@ var SkillCatalog = class {
17527
18281
  for (const location of locations) {
17528
18282
  const entries = await safeDirectories(location.path);
17529
18283
  for (const entry of entries) {
17530
- const skillPath = join20(location.path, entry, "SKILL.md");
18284
+ const skillPath = join21(location.path, entry, "SKILL.md");
17531
18285
  const metadata = await readMetadata(skillPath);
17532
18286
  if (!metadata) continue;
17533
18287
  const descriptor = {
@@ -17576,31 +18330,31 @@ ${skill.content}
17576
18330
  }
17577
18331
  function discoveryLocations(workspace, configured) {
17578
18332
  const home = homedir4();
17579
- const workspaceRoot = resolve20(workspace);
18333
+ const workspaceRoot = resolve21(workspace);
17580
18334
  return [
17581
- { path: join20(home, ".agents", "skills"), scope: "user", trusted: true },
17582
- { path: join20(home, ".claude", "skills"), scope: "user", trusted: true },
17583
- { path: join20(home, ".augment", "skills"), scope: "user", trusted: true },
17584
- { path: join20(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
18335
+ { path: join21(home, ".agents", "skills"), scope: "user", trusted: true },
18336
+ { path: join21(home, ".claude", "skills"), scope: "user", trusted: true },
18337
+ { path: join21(home, ".augment", "skills"), scope: "user", trusted: true },
18338
+ { path: join21(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
17585
18339
  ...configured.map((path) => {
17586
- const resolved = resolve20(workspaceRoot, path);
18340
+ const resolved = resolve21(workspaceRoot, path);
17587
18341
  return {
17588
18342
  path: resolved,
17589
18343
  scope: "configured",
17590
18344
  trusted: !isInside(workspaceRoot, resolved)
17591
18345
  };
17592
18346
  }),
17593
- { path: join20(workspace, ".agents", "skills"), scope: "workspace", trusted: false },
17594
- { path: join20(workspace, ".claude", "skills"), scope: "workspace", trusted: false },
17595
- { path: join20(workspace, ".augment", "skills"), scope: "workspace", trusted: false },
17596
- { path: join20(resolveProjectNamespaceSync(workspaceRoot).active, "skills"), scope: "workspace", trusted: false }
18347
+ { path: join21(workspace, ".agents", "skills"), scope: "workspace", trusted: false },
18348
+ { path: join21(workspace, ".claude", "skills"), scope: "workspace", trusted: false },
18349
+ { path: join21(workspace, ".augment", "skills"), scope: "workspace", trusted: false },
18350
+ { path: join21(resolveProjectNamespaceSync(workspaceRoot).active, "skills"), scope: "workspace", trusted: false }
17597
18351
  ];
17598
18352
  }
17599
18353
  async function safeDirectories(path) {
17600
18354
  try {
17601
- const info = await lstat20(path);
18355
+ const info = await lstat21(path);
17602
18356
  if (!info.isDirectory() || info.isSymbolicLink()) return [];
17603
- const entries = await readdir8(path, { withFileTypes: true });
18357
+ const entries = await readdir9(path, { withFileTypes: true });
17604
18358
  return entries.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => entry.name).slice(0, 500);
17605
18359
  } catch {
17606
18360
  return [];
@@ -17612,7 +18366,7 @@ async function readMetadata(path) {
17612
18366
  const { frontmatter } = splitFrontmatter(raw);
17613
18367
  if (!frontmatter) return void 0;
17614
18368
  const parsed = parseYaml3(frontmatter);
17615
- const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename11(resolve20(path, ".."));
18369
+ const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename11(resolve21(path, ".."));
17616
18370
  const description = typeof parsed?.description === "string" ? parsed.description.trim() : "";
17617
18371
  if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || !description || description.length > 1e3) {
17618
18372
  return void 0;
@@ -17631,13 +18385,13 @@ async function readSkill(path, maxChars) {
17631
18385
  }
17632
18386
  async function safeRead(path, maxBytes) {
17633
18387
  try {
17634
- const info = await lstat20(path);
18388
+ const info = await lstat21(path);
17635
18389
  if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes) return void 0;
17636
- const parent = resolve20(path, "..");
18390
+ const parent = resolve21(path, "..");
17637
18391
  const resolvedParent = await realpath9(parent);
17638
18392
  const resolvedPath = await realpath9(path);
17639
18393
  if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
17640
- return await readFile17(path, "utf8");
18394
+ return await readFile18(path, "utf8");
17641
18395
  } catch {
17642
18396
  return void 0;
17643
18397
  }
@@ -17682,7 +18436,7 @@ function escapeAttribute6(value) {
17682
18436
  }
17683
18437
 
17684
18438
  // src/workflows/catalog.ts
17685
- import { z as z18 } from "zod";
18439
+ import { z as z20 } from "zod";
17686
18440
  var builtInWorkflows = [
17687
18441
  {
17688
18442
  name: "implement",
@@ -17773,7 +18527,7 @@ function createWorkflowTool(catalog) {
17773
18527
  }, ["name", "task"])
17774
18528
  },
17775
18529
  async execute(arguments_) {
17776
- const input2 = z18.object({ name: z18.string(), task: z18.string().min(1).max(2e4) }).parse(arguments_);
18530
+ const input2 = z20.object({ name: z20.string(), task: z20.string().min(1).max(2e4) }).parse(arguments_);
17777
18531
  const workflow = catalog.get(input2.name);
17778
18532
  if (!workflow) return { ok: false, content: `Unknown workflow: ${input2.name}` };
17779
18533
  return {
@@ -17818,7 +18572,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
17818
18572
  delegation;
17819
18573
  initialized = false;
17820
18574
  static async create(config, registry, options = {}) {
17821
- const runtime = new _ExtensionRuntime(config, resolve21(config.workspaceRoots[0] ?? process.cwd()), options);
18575
+ const runtime = new _ExtensionRuntime(config, resolve22(config.workspaceRoots[0] ?? process.cwd()), options);
17822
18576
  try {
17823
18577
  await runtime.initialize(registry, options.signal);
17824
18578
  return runtime;
@@ -18023,15 +18777,15 @@ function upgradeCommandOverride(env = process.env) {
18023
18777
  return trimmed ? trimmed : void 0;
18024
18778
  }
18025
18779
  function runUpgrade(plan) {
18026
- return new Promise((resolve23) => {
18780
+ return new Promise((resolve24) => {
18027
18781
  const child = spawn2(plan.command, plan.args, {
18028
18782
  stdio: "inherit",
18029
18783
  shell: plan.shell ?? false
18030
18784
  });
18031
- child.on("error", () => resolve23({ ok: false, exitCode: 127, display: plan.display }));
18785
+ child.on("error", () => resolve24({ ok: false, exitCode: 127, display: plan.display }));
18032
18786
  child.on("close", (code) => {
18033
18787
  const exitCode = code ?? 1;
18034
- resolve23({ ok: exitCode === 0, exitCode, display: plan.display });
18788
+ resolve24({ ok: exitCode === 0, exitCode, display: plan.display });
18035
18789
  });
18036
18790
  });
18037
18791
  }
@@ -18280,9 +19034,11 @@ sessionCommand.command("show <id>").description("Show a saved session transcript
18280
19034
  else process.stdout.write(sessionMarkdown(session));
18281
19035
  });
18282
19036
  sessionCommand.command("delete <id>").description("Delete a saved session").option("-w, --workspace <path>", "workspace root").option("--yes", "skip confirmation").action(async (id, options) => {
18283
- const store = new SessionStore(workspaceOption(options.workspace));
19037
+ const workspace = workspaceOption(options.workspace);
19038
+ const store = new SessionStore(workspace);
18284
19039
  const session = await requireSessionSelector(store, id);
18285
19040
  if (!options.yes && !await confirm(`Delete session ${session.id.slice(0, 8)}?`)) return;
19041
+ await new ToolArtifactStore(workspace).removeSession(session.id);
18286
19042
  await store.remove(session.id);
18287
19043
  process.stdout.write(`Deleted ${session.id}
18288
19044
  `);
@@ -18291,7 +19047,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
18291
19047
  const store = new SessionStore(workspaceOption(options.workspace));
18292
19048
  const session = await requireSessionSelector(store, id);
18293
19049
  const markdown = sessionMarkdown(session);
18294
- if (options.output) await writeFile3(resolve22(options.output), markdown, "utf8");
19050
+ if (options.output) await writeFile3(resolve23(options.output), markdown, "utf8");
18295
19051
  else process.stdout.write(markdown);
18296
19052
  });
18297
19053
  var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
@@ -18668,7 +19424,7 @@ async function runChat(prompts, options) {
18668
19424
  const stdinPrompt = !process.stdin.isTTY ? await readStdin() : "";
18669
19425
  const firstPrompt = [...prompts, stdinPrompt].filter(Boolean).join("\n\n").trim();
18670
19426
  if (shouldPrint && !firstPrompt) throw new Error("Provide a prompt argument or pipe input on stdin.");
18671
- const workspace = resolve22(options.workspace);
19427
+ const workspace = resolve23(options.workspace);
18672
19428
  let config = await runtimeConfig(workspace, options);
18673
19429
  let completedOnboarding = false;
18674
19430
  if (!shouldPrint && needsFirstRunOnboarding(config)) {
@@ -18893,14 +19649,14 @@ async function runAgentSetup(options) {
18893
19649
  `);
18894
19650
  }
18895
19651
  async function runtimeConfig(workspaceInput, options) {
18896
- const workspace = resolve22(workspaceInput);
19652
+ const workspace = resolve23(workspaceInput);
18897
19653
  const loaded = await loadConfig(workspace, options.config, {
18898
19654
  trustProjectConfig: options.trustProjectConfig === true
18899
19655
  });
18900
19656
  const roots = [
18901
19657
  workspace,
18902
19658
  ...loaded.workspaceRoots,
18903
- ...(options.addWorkspace ?? []).map((root) => resolve22(workspace, root))
19659
+ ...(options.addWorkspace ?? []).map((root) => resolve23(workspace, root))
18904
19660
  ];
18905
19661
  const provider = options.provider ? validateProvider(options.provider) : loaded.model.provider;
18906
19662
  return {
@@ -19056,7 +19812,7 @@ function collect(value, previous) {
19056
19812
  }
19057
19813
  function workspaceOption(value) {
19058
19814
  const rootOptions = program.opts();
19059
- return resolve22(value ?? rootOptions.workspace ?? process.cwd());
19815
+ return resolve23(value ?? rootOptions.workspace ?? process.cwd());
19060
19816
  }
19061
19817
  function runtimeOptions(options) {
19062
19818
  const root = program.opts();