pi-crew 0.9.28 → 0.9.30

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/index.mjs CHANGED
@@ -1810,7 +1810,7 @@ var init_iterator2 = __esm({
1810
1810
  function RequiredArray(properties) {
1811
1811
  return globalThis.Object.keys(properties).filter((key) => !IsOptional(properties[key]));
1812
1812
  }
1813
- function _Object(properties, options) {
1813
+ function _Object_(properties, options) {
1814
1814
  const required = RequiredArray(properties);
1815
1815
  const schema = required.length > 0 ? { [Kind]: "Object", type: "object", required, properties } : { [Kind]: "Object", type: "object", properties };
1816
1816
  return CreateType(schema, options);
@@ -1821,7 +1821,7 @@ var init_object = __esm({
1821
1821
  init_type2();
1822
1822
  init_symbols2();
1823
1823
  init_kind();
1824
- Object2 = _Object;
1824
+ Object2 = _Object_;
1825
1825
  }
1826
1826
  });
1827
1827
 
@@ -4435,6 +4435,12 @@ var init_esm = __esm({
4435
4435
  }
4436
4436
  });
4437
4437
 
4438
+ // node_modules/@sinclair/typebox/build/esm/system/evaluate.mjs
4439
+ var init_evaluate = __esm({
4440
+ "node_modules/@sinclair/typebox/build/esm/system/evaluate.mjs"() {
4441
+ }
4442
+ });
4443
+
4438
4444
  // node_modules/@sinclair/typebox/build/esm/system/system.mjs
4439
4445
  var TypeSystemDuplicateTypeKind, TypeSystemDuplicateFormat, TypeSystem;
4440
4446
  var init_system = __esm({
@@ -4475,6 +4481,7 @@ var init_system = __esm({
4475
4481
  // node_modules/@sinclair/typebox/build/esm/system/index.mjs
4476
4482
  var init_system2 = __esm({
4477
4483
  "node_modules/@sinclair/typebox/build/esm/system/index.mjs"() {
4484
+ init_evaluate();
4478
4485
  init_policy();
4479
4486
  init_system();
4480
4487
  }
@@ -8486,8 +8493,10 @@ var init_config_schema = __esm({
8486
8493
  });
8487
8494
 
8488
8495
  // src/utils/internal-error.ts
8489
- function logInternalError(scope, error, details) {
8490
- if (!process.env.PI_TEAMS_DEBUG) return;
8496
+ function logInternalError(scope, error, details, severity) {
8497
+ if (!severity || severity === "debug") {
8498
+ if (!process.env.PI_TEAMS_DEBUG) return;
8499
+ }
8491
8500
  const message = error instanceof Error ? error.message : typeof error === "object" && error !== null ? JSON.stringify(error) : String(error);
8492
8501
  const suffix = details ? `: ${details}` : "";
8493
8502
  console.error(`[pi-crew:${scope}] ${message}${suffix}`);
@@ -8522,8 +8531,8 @@ function sleepSync(ms) {
8522
8531
  }
8523
8532
  function sleep(ms, signal) {
8524
8533
  if (signal?.aborted) return Promise.reject(new Error("aborted"));
8525
- return new Promise((resolve21, reject) => {
8526
- const timer = setTimeout(resolve21, ms);
8534
+ return new Promise((resolve22, reject) => {
8535
+ const timer = setTimeout(resolve22, ms);
8527
8536
  signal?.addEventListener(
8528
8537
  "abort",
8529
8538
  () => {
@@ -8567,9 +8576,9 @@ function getWorker() {
8567
8576
  return worker;
8568
8577
  }
8569
8578
  function dispatch(kind, payload) {
8570
- return new Promise((resolve21, reject) => {
8579
+ return new Promise((resolve22, reject) => {
8571
8580
  const id = nextRequestId++;
8572
- pending.set(id, { resolve: resolve21, reject });
8581
+ pending.set(id, { resolve: resolve22, reject });
8573
8582
  try {
8574
8583
  getWorker().postMessage({ kind, id, ...payload });
8575
8584
  } catch (error) {
@@ -8802,7 +8811,7 @@ function isSymlinkSafeDirCached(filePath) {
8802
8811
  return verdict;
8803
8812
  }
8804
8813
  function sleep2(ms) {
8805
- return new Promise((resolve21) => setTimeout(resolve21, ms));
8814
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
8806
8815
  }
8807
8816
  function isRetryableRenameError(error) {
8808
8817
  return Boolean(
@@ -8819,16 +8828,25 @@ function renameWithLinkSync(tempPath, filePath, retries = 8) {
8819
8828
  for (let attempt = 0; attempt <= retries; attempt++) {
8820
8829
  try {
8821
8830
  if (process.platform === "win32") {
8822
- try {
8823
- fs2.unlinkSync(filePath);
8824
- } catch {
8825
- }
8826
8831
  try {
8827
8832
  fs2.renameSync(tempPath, filePath);
8828
8833
  return;
8829
8834
  } catch (renameError) {
8830
8835
  lastError = renameError;
8831
- if (!isRetryableLinkError(renameError) || attempt === retries) break;
8836
+ if (isRetryableLinkError(renameError) && attempt !== retries) {
8837
+ } else {
8838
+ try {
8839
+ fs2.unlinkSync(filePath);
8840
+ } catch {
8841
+ }
8842
+ try {
8843
+ fs2.renameSync(tempPath, filePath);
8844
+ return;
8845
+ } catch (renameError2) {
8846
+ lastError = renameError2;
8847
+ if (!isRetryableLinkError(renameError2) || attempt === retries) break;
8848
+ }
8849
+ }
8832
8850
  }
8833
8851
  } else {
8834
8852
  try {
@@ -8854,16 +8872,25 @@ async function renameWithLinkAsync(tempPath, filePath, retries = 8) {
8854
8872
  for (let attempt = 0; attempt <= retries; attempt++) {
8855
8873
  try {
8856
8874
  if (process.platform === "win32") {
8857
- try {
8858
- await fs2.promises.unlink(filePath);
8859
- } catch {
8860
- }
8861
8875
  try {
8862
8876
  await fs2.promises.rename(tempPath, filePath);
8863
8877
  return;
8864
8878
  } catch (renameError) {
8865
8879
  lastError = renameError;
8866
- if (!isRetryableLinkError(renameError) || attempt === retries) break;
8880
+ if (isRetryableLinkError(renameError) && attempt !== retries) {
8881
+ } else {
8882
+ try {
8883
+ await fs2.promises.unlink(filePath);
8884
+ } catch {
8885
+ }
8886
+ try {
8887
+ await fs2.promises.rename(tempPath, filePath);
8888
+ return;
8889
+ } catch (renameError2) {
8890
+ lastError = renameError2;
8891
+ if (!isRetryableLinkError(renameError2) || attempt === retries) break;
8892
+ }
8893
+ }
8867
8894
  }
8868
8895
  } else {
8869
8896
  try {
@@ -9071,7 +9098,7 @@ function flushOnePendingAtomicWrite(filePath) {
9071
9098
  pendingAtomicWrites.delete(filePath);
9072
9099
  }
9073
9100
  } catch (error) {
9074
- logInternalError("atomic-write.coalesced-flush", error, filePath);
9101
+ logInternalError("atomic-write.coalesced-flush", error, filePath, "error");
9075
9102
  const current2 = pendingAtomicWrites.get(filePath);
9076
9103
  if (current2?.generation === savedGeneration) {
9077
9104
  current2.retryCount++;
@@ -9120,7 +9147,7 @@ var init_atomic_write = __esm({
9120
9147
  init_worker_atomic_writer();
9121
9148
  RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
9122
9149
  RETRYABLE_LINK_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOENT"]);
9123
- SYMLINK_SAFE_TTL_MS = 3e4;
9150
+ SYMLINK_SAFE_TTL_MS = 1e4;
9124
9151
  SYMLINK_SAFE_MAX_ENTRIES = 128;
9125
9152
  symlinkSafeCache = /* @__PURE__ */ new Map();
9126
9153
  __test__renameWithRetry = renameWithRetry;
@@ -9400,7 +9427,7 @@ function acquireLockWithRetry(filePath, staleMs, kind = "file") {
9400
9427
  }
9401
9428
  }
9402
9429
  function sleep3(ms) {
9403
- return new Promise((resolve21) => setTimeout(resolve21, ms));
9430
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
9404
9431
  }
9405
9432
  async function acquireLockWithRetryAsync(filePath, staleMs, kind = "file") {
9406
9433
  let attempt = 0;
@@ -9533,7 +9560,8 @@ function packageRoot() {
9533
9560
  return path4.resolve(path4.dirname(fileURLToPath(import.meta.url)), "..", "..");
9534
9561
  }
9535
9562
  function userPiRoot() {
9536
- const home = process.env.PI_TEAMS_HOME?.trim() || os2.homedir();
9563
+ const rawHome = process.env.PI_TEAMS_HOME?.trim();
9564
+ const home = rawHome && rawHome !== "undefined" ? rawHome : os2.homedir();
9537
9565
  const resolved = path4.join(home, ".pi", "agent");
9538
9566
  let isSymlink2 = false;
9539
9567
  try {
@@ -10254,7 +10282,11 @@ function parseWorktreeConfig(value) {
10254
10282
  const worktree = {
10255
10283
  setupHook: setupHook ? setupHook : void 0,
10256
10284
  setupHookTimeoutMs: parsePositiveInteger(obj.setupHookTimeoutMs, 3e5),
10257
- linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules)
10285
+ linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules),
10286
+ // C6: seedPaths was declared in the type + schema but never parsed here, so
10287
+ // loadedConfig.config.worktree?.seedPaths was always undefined -> the global
10288
+ // worktree seed overlay (worktree-manager.ts) silently never applied.
10289
+ seedPaths: parseStringList(obj.seedPaths)
10258
10290
  };
10259
10291
  return Object.values(worktree).some((entry) => entry !== void 0) ? worktree : void 0;
10260
10292
  }
@@ -11300,7 +11332,7 @@ function redactInlineSecrets(value) {
11300
11332
  if (keyLen > 0 && j < value.length && (value[j] === "=" || value[j] === ":")) {
11301
11333
  const key = value.substring(i2, j);
11302
11334
  if (isSecretKey(key)) {
11303
- const sep9 = value[j];
11335
+ const sep10 = value[j];
11304
11336
  let k = j + 1;
11305
11337
  let valLen = 0;
11306
11338
  while (k < value.length && valLen < 500 && value[k] !== " " && value[k] !== "," && value[k] !== ";" && value[k] !== '"' && value[k] !== "\r" && value[k] !== "\n") {
@@ -11309,7 +11341,7 @@ function redactInlineSecrets(value) {
11309
11341
  }
11310
11342
  if (valLen > 0) {
11311
11343
  result4.push(key);
11312
- result4.push(sep9);
11344
+ result4.push(sep10);
11313
11345
  result4.push("***");
11314
11346
  i2 = k;
11315
11347
  redacted = true;
@@ -11676,6 +11708,19 @@ function persistSequence(eventsPath, seq) {
11676
11708
  logInternalError("event-log.persist-sequence-file", error, `eventsPath=${eventsPath}`);
11677
11709
  }
11678
11710
  }
11711
+ function reserveSequence(eventsPath) {
11712
+ let last = seqCounters.get(eventsPath);
11713
+ if (last === void 0) {
11714
+ last = nextSequence(eventsPath) - 1;
11715
+ }
11716
+ const next = last + 1;
11717
+ seqCounters.set(eventsPath, next);
11718
+ return next;
11719
+ }
11720
+ function advanceSequenceCounter(eventsPath, seq) {
11721
+ const last = seqCounters.get(eventsPath);
11722
+ if (last === void 0 || seq > last) seqCounters.set(eventsPath, seq);
11723
+ }
11679
11724
  function computeEventFingerprint(event) {
11680
11725
  return createHash2("sha256").update(
11681
11726
  JSON.stringify({
@@ -11717,11 +11762,14 @@ async function withEventLogLockAsync(eventsPath, fn) {
11717
11762
  try {
11718
11763
  await next;
11719
11764
  } finally {
11720
- asyncLocks.delete(queueKey);
11765
+ if (asyncLocks.get(queueKey) === next) {
11766
+ asyncLocks.delete(queueKey);
11767
+ }
11721
11768
  }
11722
11769
  }
11723
11770
  function resetEventLogMode() {
11724
11771
  asyncQueues.clear();
11772
+ seqCounters.clear();
11725
11773
  }
11726
11774
  async function appendEventAsync(eventsPath, event) {
11727
11775
  const queueKey = eventsPath;
@@ -11732,8 +11780,9 @@ async function appendEventAsync(eventsPath, event) {
11732
11780
  let seq;
11733
11781
  if (baseMetadata?.seq !== void 0) {
11734
11782
  seq = baseMetadata.seq;
11783
+ advanceSequenceCounter(eventsPath, seq);
11735
11784
  } else {
11736
- seq = nextSequence(eventsPath);
11785
+ seq = reserveSequence(eventsPath);
11737
11786
  }
11738
11787
  let metadata = {
11739
11788
  seq,
@@ -11857,21 +11906,21 @@ async function appendEventAsync(eventsPath, event) {
11857
11906
  }
11858
11907
  return fullEvent;
11859
11908
  });
11860
- asyncQueues.set(
11861
- queueKey,
11862
- next.then(
11863
- () => {
11909
+ const tail = next.then(
11910
+ () => {
11911
+ if (asyncQueues.get(queueKey) === tail) {
11864
11912
  asyncQueues.delete(queueKey);
11865
- },
11866
- (error) => {
11867
- try {
11868
- logInternalError("event-log.async-queue", error, eventsPath);
11869
- } catch {
11870
- }
11871
- asyncQueues.set(queueKey, Promise.resolve());
11872
11913
  }
11873
- )
11914
+ },
11915
+ (error) => {
11916
+ try {
11917
+ logInternalError("event-log.async-queue", error, eventsPath);
11918
+ } catch {
11919
+ }
11920
+ asyncQueues.set(queueKey, Promise.resolve());
11921
+ }
11874
11922
  );
11923
+ asyncQueues.set(queueKey, tail);
11875
11924
  return next;
11876
11925
  }
11877
11926
  async function appendEventBatchInsideLock(eventsPath, queue) {
@@ -11895,7 +11944,7 @@ async function appendEventBatchInsideLock(eventsPath, queue) {
11895
11944
  } catch (error) {
11896
11945
  logInternalError("event-log.batch-size-check", error, `eventsPath=${eventsPath}`);
11897
11946
  }
11898
- const startingSeq = queue[0]?.event.metadata?.seq ?? nextSequence(eventsPath);
11947
+ const startingSeq = queue[0]?.event.metadata?.seq ?? reserveSequence(eventsPath);
11899
11948
  let nextSeq = startingSeq;
11900
11949
  const finalized = [];
11901
11950
  let lastSeq = 0;
@@ -11931,6 +11980,7 @@ async function appendEventBatchInsideLock(eventsPath, queue) {
11931
11980
  `, fullEvent });
11932
11981
  lastSeq = seq;
11933
11982
  }
11983
+ advanceSequenceCounter(eventsPath, lastSeq);
11934
11984
  try {
11935
11985
  if (fs8.existsSync(eventsPath) && fs8.statSync(eventsPath).size > MAX_EVENTS_BYTES) {
11936
11986
  logInternalError(
@@ -11972,8 +12022,11 @@ async function appendEventBatchInsideLock(eventsPath, queue) {
11972
12022
  function appendEventInsideLock(eventsPath, event) {
11973
12023
  fs8.mkdirSync(path6.dirname(eventsPath), { recursive: true });
11974
12024
  const baseMetadata = event.metadata;
12025
+ const explicitSeq = baseMetadata?.seq;
12026
+ const seq = explicitSeq ?? reserveSequence(eventsPath);
12027
+ if (explicitSeq !== void 0) advanceSequenceCounter(eventsPath, seq);
11975
12028
  let metadata = {
11976
- seq: baseMetadata?.seq ?? nextSequence(eventsPath),
12029
+ seq,
11977
12030
  provenance: baseMetadata?.provenance ?? "team_runner",
11978
12031
  ...baseMetadata?.parentEventId ? { parentEventId: baseMetadata.parentEventId } : {},
11979
12032
  ...baseMetadata?.attemptId ? { attemptId: baseMetadata.attemptId } : {},
@@ -12028,7 +12081,6 @@ function appendEventInsideLock(eventsPath, event) {
12028
12081
  } catch (error) {
12029
12082
  logInternalError("event-log.size-check", error, `eventsPath=${eventsPath}`);
12030
12083
  }
12031
- const seq = fullEvent.metadata?.seq ?? 0;
12032
12084
  if (!skippedDueToSize) {
12033
12085
  fs8.appendFileSync(eventsPath, `${JSON.stringify(redactSecrets(fullEvent))}
12034
12086
  `, "utf-8");
@@ -12080,9 +12132,9 @@ function appendEventBuffered(eventsPath, event, bufferMs = DEFAULT_BUFFER_MS) {
12080
12132
  }
12081
12133
  return Promise.resolve(appendEvent(eventsPath, event));
12082
12134
  }
12083
- return new Promise((resolve21, reject) => {
12135
+ return new Promise((resolve22, reject) => {
12084
12136
  const queue = bufferedQueues.get(eventsPath) ?? [];
12085
- queue.push({ event, resolve: resolve21, reject });
12137
+ queue.push({ event, resolve: resolve22, reject });
12086
12138
  bufferedQueues.set(eventsPath, queue);
12087
12139
  if (!bufferedTimers.has(eventsPath)) {
12088
12140
  const timer = setTimeout(() => {
@@ -12195,7 +12247,7 @@ function dedupeTerminalEvents(events) {
12195
12247
  }
12196
12248
  return output;
12197
12249
  }
12198
- var TERMINAL_EVENT_TYPES, MAX_EVENTS_BYTES, sequenceCache, MAX_SEQUENCE_CACHE_ENTRIES, appendCounter, overflowCounter, MAX_SEQUENCE_CACHE_ENTRIES_VALUE, asyncQueues, asyncLocks, bufferedQueues, bufferedTimers, DEFAULT_BUFFER_MS;
12250
+ var TERMINAL_EVENT_TYPES, MAX_EVENTS_BYTES, sequenceCache, MAX_SEQUENCE_CACHE_ENTRIES, appendCounter, overflowCounter, MAX_SEQUENCE_CACHE_ENTRIES_VALUE, seqCounters, asyncQueues, asyncLocks, bufferedQueues, bufferedTimers, DEFAULT_BUFFER_MS;
12199
12251
  var init_event_log = __esm({
12200
12252
  "src/state/event-log.ts"() {
12201
12253
  "use strict";
@@ -12216,6 +12268,7 @@ var init_event_log = __esm({
12216
12268
  appendCounter = 0;
12217
12269
  overflowCounter = 0;
12218
12270
  MAX_SEQUENCE_CACHE_ENTRIES_VALUE = MAX_SEQUENCE_CACHE_ENTRIES;
12271
+ seqCounters = /* @__PURE__ */ new Map();
12219
12272
  asyncQueues = /* @__PURE__ */ new Map();
12220
12273
  asyncLocks = /* @__PURE__ */ new Map();
12221
12274
  bufferedQueues = /* @__PURE__ */ new Map();
@@ -12428,8 +12481,8 @@ function resolveContainedPath(baseDir, targetPath2) {
12428
12481
  const resolved = path7.isAbsolute(targetPath2) ? path7.resolve(targetPath2) : path7.resolve(base, targetPath2);
12429
12482
  const baseNorm = resolveCanonicalPath(base);
12430
12483
  const resolvedNorm = resolveCanonicalPath(resolved);
12431
- const relative8 = process.platform === "win32" ? path7.relative(baseNorm.toLowerCase(), resolvedNorm.toLowerCase()) : path7.relative(baseNorm, resolvedNorm);
12432
- if (relative8.startsWith("..") || path7.isAbsolute(relative8)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12484
+ const relative9 = process.platform === "win32" ? path7.relative(baseNorm.toLowerCase(), resolvedNorm.toLowerCase()) : path7.relative(baseNorm, resolvedNorm);
12485
+ if (relative9.startsWith("..") || path7.isAbsolute(relative9)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12433
12486
  return resolved;
12434
12487
  }
12435
12488
  function resolveCanonicalPath(p) {
@@ -12611,8 +12664,8 @@ function resolveRealContainedPath(baseDir, targetPath2) {
12611
12664
  throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12612
12665
  }
12613
12666
  } else {
12614
- const relative8 = path7.relative(realBase, realTarget);
12615
- if (relative8.startsWith("..") || path7.isAbsolute(relative8)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12667
+ const relative9 = path7.relative(realBase, realTarget);
12668
+ if (relative9.startsWith("..") || path7.isAbsolute(relative9)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12616
12669
  }
12617
12670
  return realTarget;
12618
12671
  }
@@ -14562,7 +14615,7 @@ async function respondAsBackground(targetAgentId, fromId, message, opts) {
14562
14615
  return awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, opts?.signal);
14563
14616
  }
14564
14617
  function awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, signal) {
14565
- return new Promise((resolve21) => {
14618
+ return new Promise((resolve22) => {
14566
14619
  const deadline = Date.now() + timeoutMs;
14567
14620
  let settled = false;
14568
14621
  let timer;
@@ -14576,7 +14629,7 @@ function awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, signal) {
14576
14629
  const set2 = pendingRepliesByTarget.get(targetAgentId);
14577
14630
  set2?.delete(corrId);
14578
14631
  if (set2 && set2.size === 0) pendingRepliesByTarget.delete(targetAgentId);
14579
- resolve21(result4);
14632
+ resolve22(result4);
14580
14633
  };
14581
14634
  timer = setTimeout(() => finish({ ok: false, corrId, timedOut: true }), timeoutMs);
14582
14635
  if (signal) {
@@ -14748,7 +14801,9 @@ function checkResultFile(manifest, tasks) {
14748
14801
  (t2) => t2.status === "completed" || t2.status === "failed" || t2.status === "cancelled" || t2.status === "skipped" || t2.status === "needs_attention"
14749
14802
  );
14750
14803
  if (allTerminal) {
14751
- manifest.status = "completed";
14804
+ const hasFailed = tasks.some((t2) => t2.status === "failed");
14805
+ const onlyCancelledOrSkipped = tasks.every((t2) => t2.status === "cancelled" || t2.status === "skipped");
14806
+ manifest.status = hasFailed ? "failed" : onlyCancelledOrSkipped ? "cancelled" : "completed";
14752
14807
  saveRunManifest(manifest);
14753
14808
  for (const task of tasks) {
14754
14809
  try {
@@ -14994,14 +15049,14 @@ function reconcileOrphanedTempWorkspaces(now = Date.now(), options) {
14994
15049
  const tasks = JSON.parse(fs16.readFileSync(tasksPath, "utf-8"));
14995
15050
  const result4 = reconcileStaleRun(manifest, tasks, now);
14996
15051
  if (result4.repaired && result4.repairedTasks) {
14997
- fs16.writeFileSync(tasksPath, JSON.stringify(result4.repairedTasks, null, 2));
15052
+ atomicWriteJson(tasksPath, result4.repairedTasks);
14998
15053
  const updated = {
14999
15054
  ...manifest,
15000
15055
  status: "cancelled",
15001
15056
  updatedAt: new Date(now).toISOString(),
15002
15057
  summary: `Stale run reconciled: ${result4.detail}`
15003
15058
  };
15004
- fs16.writeFileSync(manifestPath, JSON.stringify(updated, null, 2));
15059
+ atomicWriteJson(manifestPath, updated);
15005
15060
  for (const task of result4.repairedTasks) {
15006
15061
  try {
15007
15062
  upsertCrewAgent(updated, recordFromTask(updated, task, "scaffold"));
@@ -15152,6 +15207,7 @@ var init_stale_reconciler = __esm({
15152
15207
  "src/runtime/stale-reconciler.ts"() {
15153
15208
  "use strict";
15154
15209
  init_errors3();
15210
+ init_atomic_write();
15155
15211
  init_state_store();
15156
15212
  init_crew_agent_records();
15157
15213
  init_process_status();
@@ -15345,7 +15401,7 @@ function cancelOrphanedRuns(cwd, manifestCache2, currentSessionId, staleThreshol
15345
15401
  });
15346
15402
  if (cancelledRun)
15347
15403
  void terminateLiveAgentsForRun(manifest.runId, "cancelled", appendEvent, loaded.manifest.eventsPath).catch(
15348
- (error) => logInternalError("crash-recovery.orphan.terminate", error, `runId=${manifest.runId}`)
15404
+ (error) => logInternalError("crash-recovery.orphan.terminate", error, `runId=${manifest.runId}`, "warn")
15349
15405
  );
15350
15406
  }
15351
15407
  return { cancelled, skipped };
@@ -15440,7 +15496,7 @@ function purgeStaleActiveRunIndex(staleThresholdMs = 3e5, now = Date.now()) {
15440
15496
  appendEvent,
15441
15497
  fullLoaded.manifest.eventsPath
15442
15498
  ).catch(
15443
- (error) => logInternalError("crash-recovery.pid-dead.terminate", error, `runId=${fullLoaded.manifest.runId}`)
15499
+ (error) => logInternalError("crash-recovery.pid-dead.terminate", error, `runId=${fullLoaded.manifest.runId}`, "warn")
15444
15500
  );
15445
15501
  }
15446
15502
  } catch {
@@ -15485,7 +15541,7 @@ function purgeStaleActiveRunIndex(staleThresholdMs = 3e5, now = Date.now()) {
15485
15541
  appendEvent,
15486
15542
  fullLoaded.manifest.eventsPath
15487
15543
  ).catch(
15488
- (error) => logInternalError("crash-recovery.pid-dead.terminate", error, `runId=${fullLoaded.manifest.runId}`)
15544
+ (error) => logInternalError("crash-recovery.pid-dead.terminate", error, `runId=${fullLoaded.manifest.runId}`, "warn")
15489
15545
  );
15490
15546
  }
15491
15547
  } catch {
@@ -15532,7 +15588,7 @@ function reconcileAllStaleRuns(cwd, manifestCache2, now = Date.now()) {
15532
15588
  }
15533
15589
  updateRunStatus(fresh.manifest, "failed", `Stale run reconciled: ${result4.detail}`);
15534
15590
  void terminateLiveAgentsForRun(fresh.manifest.runId, "failed", appendEvent, fresh.manifest.eventsPath).catch(
15535
- (error) => logInternalError("crash-recovery.reconcile.terminate", error, `runId=${fresh.manifest.runId}`)
15591
+ (error) => logInternalError("crash-recovery.reconcile.terminate", error, `runId=${fresh.manifest.runId}`, "warn")
15536
15592
  );
15537
15593
  appendEvent(fresh.manifest.eventsPath, {
15538
15594
  type: "crew.run.reconciled_stale",
@@ -17307,7 +17363,7 @@ var init_subagent_manager = __esm({
17307
17363
  await record.promise.catch((error) => {
17308
17364
  logInternalError("subagent-manager.waitForRecord", error, `id=${id}`);
17309
17365
  });
17310
- else await new Promise((resolve21) => setTimeout(resolve21, 100));
17366
+ else await new Promise((resolve22) => setTimeout(resolve22, 100));
17311
17367
  }
17312
17368
  }
17313
17369
  setMaxConcurrent(value) {
@@ -17405,7 +17461,7 @@ var init_subagent_manager = __esm({
17405
17461
  while (record.runId && (record.status === "running" || record.status === "blocked")) {
17406
17462
  const loaded = loadRunManifestById(cwd, record.runId);
17407
17463
  if (!loaded) {
17408
- await new Promise((resolve21) => setTimeout(resolve21, this.pollIntervalMs));
17464
+ await new Promise((resolve22) => setTimeout(resolve22, this.pollIntervalMs));
17409
17465
  continue;
17410
17466
  }
17411
17467
  if (loaded.manifest.status === "completed") {
@@ -17438,7 +17494,7 @@ var init_subagent_manager = __esm({
17438
17494
  savePersistedSubagentRecord(cwd, record);
17439
17495
  return;
17440
17496
  }
17441
- await new Promise((resolve21) => setTimeout(resolve21, this.pollIntervalMs));
17497
+ await new Promise((resolve22) => setTimeout(resolve22, this.pollIntervalMs));
17442
17498
  }
17443
17499
  }
17444
17500
  scheduleBlockedTerminalPoll(cwd, record) {
@@ -17786,8 +17842,8 @@ var init_deduplicate_stage = __esm({
17786
17842
  const cur = lines[i2];
17787
17843
  if (cur !== out[out.length - 1]) out.push(cur);
17788
17844
  }
17789
- const sep9 = text.includes("\r\n") ? "\r\n" : "\n";
17790
- return out.join(sep9);
17845
+ const sep10 = text.includes("\r\n") ? "\r\n" : "\n";
17846
+ return out.join(sep10);
17791
17847
  }
17792
17848
  };
17793
17849
  DEDUPLICATE_STAGE = new DeduplicateStage();
@@ -18257,13 +18313,20 @@ function attachPostExitStdioGuard(child, options) {
18257
18313
  stderrEnded = true;
18258
18314
  if (stdoutEnded && stderrEnded) clearTimers();
18259
18315
  });
18260
- child.on("exit", () => {
18261
- exited = true;
18262
- armIdleTimer();
18316
+ const armHardTimer = () => {
18263
18317
  if (hardTimer) return;
18264
18318
  hardTimer = setTimeout(destroyUnendedStdio, hardMs);
18265
18319
  hardTimer.unref();
18266
- });
18320
+ };
18321
+ const onExit = () => {
18322
+ exited = true;
18323
+ armIdleTimer();
18324
+ armHardTimer();
18325
+ };
18326
+ if (child.exitCode != null || child.signalCode != null) {
18327
+ onExit();
18328
+ }
18329
+ child.on("exit", onExit);
18267
18330
  child.on("close", clearTimers);
18268
18331
  child.on("error", clearTimers);
18269
18332
  return clearTimers;
@@ -18295,27 +18358,31 @@ function clearHardKillTimer(pid) {
18295
18358
  clearTimeout(timer);
18296
18359
  childHardKillTimers.delete(pid);
18297
18360
  }
18361
+ function spawnTaskkillSafe(pid) {
18362
+ const taskkillChild = spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18363
+ stdio: "ignore",
18364
+ windowsHide: true
18365
+ });
18366
+ taskkillChild.on("error", (err2) => {
18367
+ logInternalError("child-pi.taskkill-spawn-error", err2 instanceof Error ? err2 : new Error(String(err2)), `pid=${pid}`);
18368
+ });
18369
+ }
18298
18370
  function killProcessPid(pid) {
18299
18371
  if (!Number.isInteger(pid) || pid <= 0) return;
18300
18372
  try {
18301
18373
  if (process.platform === "win32") {
18302
- spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18303
- stdio: "ignore",
18304
- windowsHide: true
18305
- });
18374
+ spawnTaskkillSafe(pid);
18306
18375
  const verifyTimer = setTimeout(() => {
18307
18376
  try {
18308
18377
  process.kill(pid, 0);
18309
18378
  logInternalError(
18310
18379
  "child-pi.taskkill-stuck",
18311
18380
  new Error(`process ${pid} still alive 2s after taskkill /T /F; retrying`),
18312
- `pid=${pid}`
18381
+ `pid=${pid}`,
18382
+ "error"
18313
18383
  );
18314
18384
  try {
18315
- spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18316
- stdio: "ignore",
18317
- windowsHide: true
18318
- });
18385
+ spawnTaskkillSafe(pid);
18319
18386
  } catch {
18320
18387
  }
18321
18388
  } catch {
@@ -18716,9 +18783,18 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18716
18783
  role: input.role
18717
18784
  });
18718
18785
  if (input.steeringFile) built.env.PI_CREW_STEERING_FILE = input.steeringFile;
18786
+ if (input.signal?.aborted) {
18787
+ return {
18788
+ exitCode: null,
18789
+ stdout: "",
18790
+ stderr: "",
18791
+ error: "Aborted before spawn (parent AbortSignal already aborted)",
18792
+ aborted: true
18793
+ };
18794
+ }
18719
18795
  const spawnSpec = getPiSpawnCommand(built.args);
18720
18796
  try {
18721
- return await new Promise((resolve21) => {
18797
+ return await new Promise((resolve22) => {
18722
18798
  for (const key of Object.keys(built.env)) {
18723
18799
  if (!key.startsWith("PI_CREW_") && !key.startsWith("PI_TEAMS_")) {
18724
18800
  throw new Error(
@@ -18889,29 +18965,23 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18889
18965
  turnCount += 1;
18890
18966
  if (maxTurns !== void 0 && !softLimitReached && turnCount >= maxTurns) {
18891
18967
  softLimitReached = true;
18892
- if (child.stdin?.writable) {
18893
- const steerPayload = JSON.stringify({
18894
- type: "steer",
18895
- message: "You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now."
18896
- }) + "\n";
18897
- const writeSucceeded = child.stdin.write(steerPayload);
18898
- if (!writeSucceeded) {
18968
+ if (input.steeringFile) {
18969
+ try {
18970
+ fs27.appendFileSync(
18971
+ input.steeringFile,
18972
+ JSON.stringify({
18973
+ type: "steer",
18974
+ message: "You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now."
18975
+ }) + "\n",
18976
+ "utf-8"
18977
+ );
18978
+ } catch (err2) {
18899
18979
  logInternalError(
18900
- "child-pi.steer-backpressure",
18901
- new Error(
18902
- "stdin write returned false (normal backpressure); steer buffered, worker NOT killed"
18903
- ),
18980
+ "child-pi.steer-write-failed",
18981
+ err2 instanceof Error ? err2 : new Error(String(err2)),
18904
18982
  `pid=${child.pid}`
18905
18983
  );
18906
18984
  }
18907
- } else {
18908
- logInternalError(
18909
- "child-pi.steer-not-writable",
18910
- new Error(
18911
- "stdin not writable when attempting steer injection (worker may be done); worker NOT killed"
18912
- ),
18913
- `pid=${child.pid}`
18914
- );
18915
18985
  }
18916
18986
  } else if (maxTurns !== void 0 && softLimitReached && turnCount >= maxTurns + (graceTurns ?? 5)) {
18917
18987
  try {
@@ -19042,7 +19112,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
19042
19112
  cleanupErrors.push(error instanceof Error ? error.message : String(error));
19043
19113
  }
19044
19114
  try {
19045
- resolve21({
19115
+ resolve22({
19046
19116
  ...result4,
19047
19117
  rawFinalText: lineObserver.getRawFinalText(),
19048
19118
  intermediateFindings: lineObserver.getIntermediateFindings(),
@@ -19556,6 +19626,12 @@ function setExtensionWidget(ctx, key, content, options) {
19556
19626
  const { persist: _persist, ...widgetOptions } = options ?? {};
19557
19627
  ctx.ui.setWidget(key, content, widgetOptions);
19558
19628
  }
19629
+ function setFooter(ctx, factory) {
19630
+ if (!ctx) return;
19631
+ const record = maybeRecord(ctx.ui);
19632
+ const fn = record?.setFooter;
19633
+ if (typeof fn === "function") fn.call(ctx.ui, factory);
19634
+ }
19559
19635
  function showCustom(ctx, factory, options) {
19560
19636
  const custom = ctx.ui.custom;
19561
19637
  return custom(factory, options);
@@ -19695,7 +19771,7 @@ var init_cancellation_token = __esm({
19695
19771
  wait(ms) {
19696
19772
  this.throwIfCancelled();
19697
19773
  if (ms <= 0) return Promise.resolve();
19698
- return new Promise((resolve21, reject) => {
19774
+ return new Promise((resolve22, reject) => {
19699
19775
  let timeout;
19700
19776
  const cleanup = () => {
19701
19777
  if (timeout) clearTimeout(timeout);
@@ -19707,7 +19783,7 @@ var init_cancellation_token = __esm({
19707
19783
  };
19708
19784
  timeout = setTimeout(() => {
19709
19785
  cleanup();
19710
- resolve21();
19786
+ resolve22();
19711
19787
  }, ms);
19712
19788
  this.signal.addEventListener("abort", onAbort, { once: true });
19713
19789
  });
@@ -23641,8 +23717,8 @@ function taskMailboxDir(manifest, taskId, create = false) {
23641
23717
  const tasksRoot = safeMailboxTasksRoot(manifest, create);
23642
23718
  const normalizedTaskId = safeTaskId(taskId);
23643
23719
  const resolved = path32.resolve(tasksRoot, normalizedTaskId);
23644
- const relative8 = path32.relative(tasksRoot, resolved);
23645
- if (relative8.startsWith("..") || path32.isAbsolute(relative8)) throw new Error(`Invalid mailbox task id: ${taskId}`);
23720
+ const relative9 = path32.relative(tasksRoot, resolved);
23721
+ if (relative9.startsWith("..") || path32.isAbsolute(relative9)) throw new Error(`Invalid mailbox task id: ${taskId}`);
23646
23722
  if (create) fs37.mkdirSync(resolved, { recursive: true });
23647
23723
  if (fs37.existsSync(resolved) && fs37.lstatSync(resolved).isSymbolicLink()) throw new Error(`Invalid mailbox task directory: ${resolved}`);
23648
23724
  return resolved;
@@ -23876,8 +23952,8 @@ function appendMailboxMessage(manifest, message) {
23876
23952
  `,
23877
23953
  "utf-8"
23878
23954
  );
23955
+ rotateMailboxFileIfNeeded(mailboxFile(manifest, complete.direction, complete.taskId));
23879
23956
  });
23880
- rotateMailboxFileIfNeeded(mailboxFile(manifest, complete.direction, complete.taskId));
23881
23957
  withFileLockSync(deliveryFile(manifest, true), () => {
23882
23958
  const delivery = readDeliveryState(manifest);
23883
23959
  delivery.messages[complete.id] = complete.status;
@@ -26087,9 +26163,9 @@ async function isLiveSessionRuntimeAvailable(timeoutMs = 1500, env = process.env
26087
26163
  try {
26088
26164
  return await Promise.race([
26089
26165
  probe(),
26090
- new Promise((resolve21) => {
26166
+ new Promise((resolve22) => {
26091
26167
  timer = setTimeout(
26092
- () => resolve21({
26168
+ () => resolve22({
26093
26169
  available: false,
26094
26170
  reason: `Timed out probing optional Pi SDK live-session runtime after ${timeoutMs}ms.`
26095
26171
  }),
@@ -28632,9 +28708,9 @@ var require_stringifyNumber = __commonJS({
28632
28708
  function stringifyNumber({ format: format2, minFractionDigits, tag, value }) {
28633
28709
  if (typeof value === "bigint")
28634
28710
  return String(value);
28635
- const num = typeof value === "number" ? value : Number(value);
28636
- if (!isFinite(num))
28637
- return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
28711
+ const num2 = typeof value === "number" ? value : Number(value);
28712
+ if (!isFinite(num2))
28713
+ return isNaN(num2) ? ".nan" : num2 < 0 ? "-.inf" : ".inf";
28638
28714
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
28639
28715
  if (!format2 && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
28640
28716
  let i2 = n.indexOf(".");
@@ -28674,8 +28750,8 @@ var require_float = __commonJS({
28674
28750
  test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
28675
28751
  resolve: (str) => parseFloat(str),
28676
28752
  stringify(node) {
28677
- const num = Number(node.value);
28678
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
28753
+ const num2 = Number(node.value);
28754
+ return isFinite(num2) ? num2.toExponential() : stringifyNumber.stringifyNumber(node);
28679
28755
  }
28680
28756
  };
28681
28757
  var float = {
@@ -29114,8 +29190,8 @@ var require_float2 = __commonJS({
29114
29190
  test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
29115
29191
  resolve: (str) => parseFloat(str.replace(/_/g, "")),
29116
29192
  stringify(node) {
29117
- const num = Number(node.value);
29118
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
29193
+ const num2 = Number(node.value);
29194
+ return isFinite(num2) ? num2.toExponential() : stringifyNumber.stringifyNumber(node);
29119
29195
  }
29120
29196
  };
29121
29197
  var float = {
@@ -29317,23 +29393,23 @@ var require_timestamp = __commonJS({
29317
29393
  function parseSexagesimal(str, asBigInt) {
29318
29394
  const sign = str[0];
29319
29395
  const parts = sign === "-" || sign === "+" ? str.substring(1) : str;
29320
- const num = (n) => asBigInt ? BigInt(n) : Number(n);
29321
- const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0));
29322
- return sign === "-" ? num(-1) * res : res;
29396
+ const num2 = (n) => asBigInt ? BigInt(n) : Number(n);
29397
+ const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num2(60) + num2(p), num2(0));
29398
+ return sign === "-" ? num2(-1) * res : res;
29323
29399
  }
29324
29400
  function stringifySexagesimal(node) {
29325
29401
  let { value } = node;
29326
- let num = (n) => n;
29402
+ let num2 = (n) => n;
29327
29403
  if (typeof value === "bigint")
29328
- num = (n) => BigInt(n);
29404
+ num2 = (n) => BigInt(n);
29329
29405
  else if (isNaN(value) || !isFinite(value))
29330
29406
  return stringifyNumber.stringifyNumber(node);
29331
29407
  let sign = "";
29332
29408
  if (value < 0) {
29333
29409
  sign = "-";
29334
- value *= num(-1);
29410
+ value *= num2(-1);
29335
29411
  }
29336
- const _60 = num(60);
29412
+ const _60 = num2(60);
29337
29413
  const parts = [value % _60];
29338
29414
  if (value < 60) {
29339
29415
  parts.unshift(0);
@@ -30250,10 +30326,10 @@ var require_resolve_block_map = __commonJS({
30250
30326
  let offset2 = bm.offset;
30251
30327
  let commentEnd = null;
30252
30328
  for (const collItem of bm.items) {
30253
- const { start, key, sep: sep9, value } = collItem;
30329
+ const { start, key, sep: sep10, value } = collItem;
30254
30330
  const keyProps = resolveProps.resolveProps(start, {
30255
30331
  indicator: "explicit-key-ind",
30256
- next: key ?? sep9?.[0],
30332
+ next: key ?? sep10?.[0],
30257
30333
  offset: offset2,
30258
30334
  onError,
30259
30335
  parentIndent: bm.indent,
@@ -30267,7 +30343,7 @@ var require_resolve_block_map = __commonJS({
30267
30343
  else if ("indent" in key && key.indent !== bm.indent)
30268
30344
  onError(offset2, "BAD_INDENT", startColMsg);
30269
30345
  }
30270
- if (!keyProps.anchor && !keyProps.tag && !sep9) {
30346
+ if (!keyProps.anchor && !keyProps.tag && !sep10) {
30271
30347
  commentEnd = keyProps.end;
30272
30348
  if (keyProps.comment) {
30273
30349
  if (map3.comment)
@@ -30291,7 +30367,7 @@ var require_resolve_block_map = __commonJS({
30291
30367
  ctx.atKey = false;
30292
30368
  if (utilMapIncludes.mapIncludes(ctx, map3.items, keyNode))
30293
30369
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
30294
- const valueProps = resolveProps.resolveProps(sep9 ?? [], {
30370
+ const valueProps = resolveProps.resolveProps(sep10 ?? [], {
30295
30371
  indicator: "map-value-ind",
30296
30372
  next: value,
30297
30373
  offset: keyNode.range[2],
@@ -30307,7 +30383,7 @@ var require_resolve_block_map = __commonJS({
30307
30383
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
30308
30384
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
30309
30385
  }
30310
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset2, sep9, null, valueProps, onError);
30386
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset2, sep10, null, valueProps, onError);
30311
30387
  if (ctx.schema.compat)
30312
30388
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
30313
30389
  offset2 = valueNode.range[2];
@@ -30398,7 +30474,7 @@ var require_resolve_end = __commonJS({
30398
30474
  let comment = "";
30399
30475
  if (end) {
30400
30476
  let hasSpace = false;
30401
- let sep9 = "";
30477
+ let sep10 = "";
30402
30478
  for (const token of end) {
30403
30479
  const { source, type } = token;
30404
30480
  switch (type) {
@@ -30412,13 +30488,13 @@ var require_resolve_end = __commonJS({
30412
30488
  if (!comment)
30413
30489
  comment = cb;
30414
30490
  else
30415
- comment += sep9 + cb;
30416
- sep9 = "";
30491
+ comment += sep10 + cb;
30492
+ sep10 = "";
30417
30493
  break;
30418
30494
  }
30419
30495
  case "newline":
30420
30496
  if (comment)
30421
- sep9 += source;
30497
+ sep10 += source;
30422
30498
  hasSpace = true;
30423
30499
  break;
30424
30500
  default:
@@ -30461,18 +30537,18 @@ var require_resolve_flow_collection = __commonJS({
30461
30537
  let offset2 = fc.offset + fc.start.source.length;
30462
30538
  for (let i2 = 0; i2 < fc.items.length; ++i2) {
30463
30539
  const collItem = fc.items[i2];
30464
- const { start, key, sep: sep9, value } = collItem;
30540
+ const { start, key, sep: sep10, value } = collItem;
30465
30541
  const props = resolveProps.resolveProps(start, {
30466
30542
  flow: fcName,
30467
30543
  indicator: "explicit-key-ind",
30468
- next: key ?? sep9?.[0],
30544
+ next: key ?? sep10?.[0],
30469
30545
  offset: offset2,
30470
30546
  onError,
30471
30547
  parentIndent: fc.indent,
30472
30548
  startOnNewline: false
30473
30549
  });
30474
30550
  if (!props.found) {
30475
- if (!props.anchor && !props.tag && !sep9 && !value) {
30551
+ if (!props.anchor && !props.tag && !sep10 && !value) {
30476
30552
  if (i2 === 0 && props.comma)
30477
30553
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
30478
30554
  else if (i2 < fc.items.length - 1)
@@ -30526,8 +30602,8 @@ var require_resolve_flow_collection = __commonJS({
30526
30602
  }
30527
30603
  }
30528
30604
  }
30529
- if (!isMap && !sep9 && !props.found) {
30530
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep9, null, props, onError);
30605
+ if (!isMap && !sep10 && !props.found) {
30606
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep10, null, props, onError);
30531
30607
  coll.items.push(valueNode);
30532
30608
  offset2 = valueNode.range[2];
30533
30609
  if (isBlock(value))
@@ -30539,7 +30615,7 @@ var require_resolve_flow_collection = __commonJS({
30539
30615
  if (isBlock(key))
30540
30616
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
30541
30617
  ctx.atKey = false;
30542
- const valueProps = resolveProps.resolveProps(sep9 ?? [], {
30618
+ const valueProps = resolveProps.resolveProps(sep10 ?? [], {
30543
30619
  flow: fcName,
30544
30620
  indicator: "map-value-ind",
30545
30621
  next: value,
@@ -30550,8 +30626,8 @@ var require_resolve_flow_collection = __commonJS({
30550
30626
  });
30551
30627
  if (valueProps.found) {
30552
30628
  if (!isMap && !props.found && ctx.options.strict) {
30553
- if (sep9)
30554
- for (const st of sep9) {
30629
+ if (sep10)
30630
+ for (const st of sep10) {
30555
30631
  if (st === valueProps.found)
30556
30632
  break;
30557
30633
  if (st.type === "newline") {
@@ -30568,7 +30644,7 @@ var require_resolve_flow_collection = __commonJS({
30568
30644
  else
30569
30645
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
30570
30646
  }
30571
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep9, null, valueProps, onError) : null;
30647
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep10, null, valueProps, onError) : null;
30572
30648
  if (valueNode) {
30573
30649
  if (isBlock(value))
30574
30650
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -30748,7 +30824,7 @@ var require_resolve_block_scalar = __commonJS({
30748
30824
  chompStart = i2 + 1;
30749
30825
  }
30750
30826
  let value = "";
30751
- let sep9 = "";
30827
+ let sep10 = "";
30752
30828
  let prevMoreIndented = false;
30753
30829
  for (let i2 = 0; i2 < contentStart; ++i2)
30754
30830
  value += lines[i2][0].slice(trimIndent) + "\n";
@@ -30765,24 +30841,24 @@ var require_resolve_block_scalar = __commonJS({
30765
30841
  indent = "";
30766
30842
  }
30767
30843
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
30768
- value += sep9 + indent.slice(trimIndent) + content;
30769
- sep9 = "\n";
30844
+ value += sep10 + indent.slice(trimIndent) + content;
30845
+ sep10 = "\n";
30770
30846
  } else if (indent.length > trimIndent || content[0] === " ") {
30771
- if (sep9 === " ")
30772
- sep9 = "\n";
30773
- else if (!prevMoreIndented && sep9 === "\n")
30774
- sep9 = "\n\n";
30775
- value += sep9 + indent.slice(trimIndent) + content;
30776
- sep9 = "\n";
30847
+ if (sep10 === " ")
30848
+ sep10 = "\n";
30849
+ else if (!prevMoreIndented && sep10 === "\n")
30850
+ sep10 = "\n\n";
30851
+ value += sep10 + indent.slice(trimIndent) + content;
30852
+ sep10 = "\n";
30777
30853
  prevMoreIndented = true;
30778
30854
  } else if (content === "") {
30779
- if (sep9 === "\n")
30855
+ if (sep10 === "\n")
30780
30856
  value += "\n";
30781
30857
  else
30782
- sep9 = "\n";
30858
+ sep10 = "\n";
30783
30859
  } else {
30784
- value += sep9 + content;
30785
- sep9 = " ";
30860
+ value += sep10 + content;
30861
+ sep10 = " ";
30786
30862
  prevMoreIndented = false;
30787
30863
  }
30788
30864
  }
@@ -30964,25 +31040,25 @@ var require_resolve_flow_scalar = __commonJS({
30964
31040
  if (!match)
30965
31041
  return source;
30966
31042
  let res = match[1];
30967
- let sep9 = " ";
31043
+ let sep10 = " ";
30968
31044
  let pos = first.lastIndex;
30969
31045
  line4.lastIndex = pos;
30970
31046
  while (match = line4.exec(source)) {
30971
31047
  if (match[1] === "") {
30972
- if (sep9 === "\n")
30973
- res += sep9;
31048
+ if (sep10 === "\n")
31049
+ res += sep10;
30974
31050
  else
30975
- sep9 = "\n";
31051
+ sep10 = "\n";
30976
31052
  } else {
30977
- res += sep9 + match[1];
30978
- sep9 = " ";
31053
+ res += sep10 + match[1];
31054
+ sep10 = " ";
30979
31055
  }
30980
31056
  pos = line4.lastIndex;
30981
31057
  }
30982
31058
  const last = /[ \t]*(.*)/sy;
30983
31059
  last.lastIndex = pos;
30984
31060
  match = last.exec(source);
30985
- return res + sep9 + (match?.[1] ?? "");
31061
+ return res + sep10 + (match?.[1] ?? "");
30986
31062
  }
30987
31063
  function doubleQuotedValue(source, onError) {
30988
31064
  let res = "";
@@ -31792,14 +31868,14 @@ var require_cst_stringify = __commonJS({
31792
31868
  }
31793
31869
  }
31794
31870
  }
31795
- function stringifyItem({ start, key, sep: sep9, value }) {
31871
+ function stringifyItem({ start, key, sep: sep10, value }) {
31796
31872
  let res = "";
31797
31873
  for (const st of start)
31798
31874
  res += st.source;
31799
31875
  if (key)
31800
31876
  res += stringifyToken(key);
31801
- if (sep9)
31802
- for (const st of sep9)
31877
+ if (sep10)
31878
+ for (const st of sep10)
31803
31879
  res += st.source;
31804
31880
  if (value)
31805
31881
  res += stringifyToken(value);
@@ -32966,18 +33042,18 @@ var require_parser = __commonJS({
32966
33042
  if (this.type === "map-value-ind") {
32967
33043
  const prev = getPrevProps(this.peek(2));
32968
33044
  const start = getFirstKeyStartProps(prev);
32969
- let sep9;
33045
+ let sep10;
32970
33046
  if (scalar.end) {
32971
- sep9 = scalar.end;
32972
- sep9.push(this.sourceToken);
33047
+ sep10 = scalar.end;
33048
+ sep10.push(this.sourceToken);
32973
33049
  delete scalar.end;
32974
33050
  } else
32975
- sep9 = [this.sourceToken];
33051
+ sep10 = [this.sourceToken];
32976
33052
  const map3 = {
32977
33053
  type: "block-map",
32978
33054
  offset: scalar.offset,
32979
33055
  indent: scalar.indent,
32980
- items: [{ start, key: scalar, sep: sep9 }]
33056
+ items: [{ start, key: scalar, sep: sep10 }]
32981
33057
  };
32982
33058
  this.onKeyLine = true;
32983
33059
  this.stack[this.stack.length - 1] = map3;
@@ -33130,15 +33206,15 @@ var require_parser = __commonJS({
33130
33206
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
33131
33207
  const start2 = getFirstKeyStartProps(it.start);
33132
33208
  const key = it.key;
33133
- const sep9 = it.sep;
33134
- sep9.push(this.sourceToken);
33209
+ const sep10 = it.sep;
33210
+ sep10.push(this.sourceToken);
33135
33211
  delete it.key;
33136
33212
  delete it.sep;
33137
33213
  this.stack.push({
33138
33214
  type: "block-map",
33139
33215
  offset: this.offset,
33140
33216
  indent: this.indent,
33141
- items: [{ start: start2, key, sep: sep9 }]
33217
+ items: [{ start: start2, key, sep: sep10 }]
33142
33218
  });
33143
33219
  } else if (start.length > 0) {
33144
33220
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -33332,13 +33408,13 @@ var require_parser = __commonJS({
33332
33408
  const prev = getPrevProps(parent);
33333
33409
  const start = getFirstKeyStartProps(prev);
33334
33410
  fixFlowSeqItems(fc);
33335
- const sep9 = fc.end.splice(1, fc.end.length);
33336
- sep9.push(this.sourceToken);
33411
+ const sep10 = fc.end.splice(1, fc.end.length);
33412
+ sep10.push(this.sourceToken);
33337
33413
  const map3 = {
33338
33414
  type: "block-map",
33339
33415
  offset: fc.offset,
33340
33416
  indent: fc.indent,
33341
- items: [{ start, key: fc, sep: sep9 }]
33417
+ items: [{ start, key: fc, sep: sep10 }]
33342
33418
  };
33343
33419
  this.onKeyLine = true;
33344
33420
  this.stack[this.stack.length - 1] = map3;
@@ -38245,7 +38321,7 @@ var require_compile = __commonJS({
38245
38321
  const schOrFunc = root.refs[ref2];
38246
38322
  if (schOrFunc)
38247
38323
  return schOrFunc;
38248
- let _sch = resolve21.call(this, root, ref2);
38324
+ let _sch = resolve22.call(this, root, ref2);
38249
38325
  if (_sch === void 0) {
38250
38326
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref2];
38251
38327
  const { schemaId } = this.opts;
@@ -38272,7 +38348,7 @@ var require_compile = __commonJS({
38272
38348
  function sameSchemaEnv(s1, s2) {
38273
38349
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
38274
38350
  }
38275
- function resolve21(root, ref2) {
38351
+ function resolve22(root, ref2) {
38276
38352
  let sch;
38277
38353
  while (typeof (sch = this.refs[ref2]) == "string")
38278
38354
  ref2 = sch;
@@ -38903,55 +38979,55 @@ var require_fast_uri = __commonJS({
38903
38979
  }
38904
38980
  return uri;
38905
38981
  }
38906
- function resolve21(baseURI, relativeURI, options) {
38982
+ function resolve22(baseURI, relativeURI, options) {
38907
38983
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
38908
38984
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
38909
38985
  schemelessOptions.skipEscape = true;
38910
38986
  return serialize(resolved, schemelessOptions);
38911
38987
  }
38912
- function resolveComponent(base, relative8, options, skipNormalization) {
38988
+ function resolveComponent(base, relative9, options, skipNormalization) {
38913
38989
  const target = {};
38914
38990
  if (!skipNormalization) {
38915
38991
  base = parse6(serialize(base, options), options);
38916
- relative8 = parse6(serialize(relative8, options), options);
38992
+ relative9 = parse6(serialize(relative9, options), options);
38917
38993
  }
38918
38994
  options = options || {};
38919
- if (!options.tolerant && relative8.scheme) {
38920
- target.scheme = relative8.scheme;
38921
- target.userinfo = relative8.userinfo;
38922
- target.host = relative8.host;
38923
- target.port = relative8.port;
38924
- target.path = removeDotSegments(relative8.path || "");
38925
- target.query = relative8.query;
38995
+ if (!options.tolerant && relative9.scheme) {
38996
+ target.scheme = relative9.scheme;
38997
+ target.userinfo = relative9.userinfo;
38998
+ target.host = relative9.host;
38999
+ target.port = relative9.port;
39000
+ target.path = removeDotSegments(relative9.path || "");
39001
+ target.query = relative9.query;
38926
39002
  } else {
38927
- if (relative8.userinfo !== void 0 || relative8.host !== void 0 || relative8.port !== void 0) {
38928
- target.userinfo = relative8.userinfo;
38929
- target.host = relative8.host;
38930
- target.port = relative8.port;
38931
- target.path = removeDotSegments(relative8.path || "");
38932
- target.query = relative8.query;
39003
+ if (relative9.userinfo !== void 0 || relative9.host !== void 0 || relative9.port !== void 0) {
39004
+ target.userinfo = relative9.userinfo;
39005
+ target.host = relative9.host;
39006
+ target.port = relative9.port;
39007
+ target.path = removeDotSegments(relative9.path || "");
39008
+ target.query = relative9.query;
38933
39009
  } else {
38934
- if (!relative8.path) {
39010
+ if (!relative9.path) {
38935
39011
  target.path = base.path;
38936
- if (relative8.query !== void 0) {
38937
- target.query = relative8.query;
39012
+ if (relative9.query !== void 0) {
39013
+ target.query = relative9.query;
38938
39014
  } else {
38939
39015
  target.query = base.query;
38940
39016
  }
38941
39017
  } else {
38942
- if (relative8.path[0] === "/") {
38943
- target.path = removeDotSegments(relative8.path);
39018
+ if (relative9.path[0] === "/") {
39019
+ target.path = removeDotSegments(relative9.path);
38944
39020
  } else {
38945
39021
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
38946
- target.path = "/" + relative8.path;
39022
+ target.path = "/" + relative9.path;
38947
39023
  } else if (!base.path) {
38948
- target.path = relative8.path;
39024
+ target.path = relative9.path;
38949
39025
  } else {
38950
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative8.path;
39026
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative9.path;
38951
39027
  }
38952
39028
  target.path = removeDotSegments(target.path);
38953
39029
  }
38954
- target.query = relative8.query;
39030
+ target.query = relative9.query;
38955
39031
  }
38956
39032
  target.userinfo = base.userinfo;
38957
39033
  target.host = base.host;
@@ -38959,7 +39035,7 @@ var require_fast_uri = __commonJS({
38959
39035
  }
38960
39036
  target.scheme = base.scheme;
38961
39037
  }
38962
- target.fragment = relative8.fragment;
39038
+ target.fragment = relative9.fragment;
38963
39039
  return target;
38964
39040
  }
38965
39041
  function equal(uriA, uriB, options) {
@@ -39161,7 +39237,7 @@ var require_fast_uri = __commonJS({
39161
39237
  var fastUri = {
39162
39238
  SCHEMES,
39163
39239
  normalize: normalize2,
39164
- resolve: resolve21,
39240
+ resolve: resolve22,
39165
39241
  resolveComponent,
39166
39242
  equal,
39167
39243
  serialize,
@@ -42638,7 +42714,7 @@ ${input.prompt}` : input.prompt;
42638
42714
  );
42639
42715
  } catch {
42640
42716
  }
42641
- await new Promise((resolve21) => setTimeout(resolve21, DEFAULT_LIVE_SESSION.yieldPollIntervalMs));
42717
+ await new Promise((resolve22) => setTimeout(resolve22, DEFAULT_LIVE_SESSION.yieldPollIntervalMs));
42642
42718
  if (customToolYieldResolved && customToolYieldResult) {
42643
42719
  yieldResult = customToolYieldResult;
42644
42720
  } else {
@@ -42682,7 +42758,7 @@ ${input.prompt}` : input.prompt;
42682
42758
  }
42683
42759
  }
42684
42760
  const pollInterval = DEFAULT_LIVE_SESSION.yieldPollIntervalMs;
42685
- await new Promise((resolve21) => setTimeout(resolve21, pollInterval));
42761
+ await new Promise((resolve22) => setTimeout(resolve22, pollInterval));
42686
42762
  if (customToolYieldResolved && customToolYieldResult) {
42687
42763
  yieldResult = customToolYieldResult;
42688
42764
  break;
@@ -44113,13 +44189,13 @@ var init_checkpoint = __esm({
44113
44189
  import * as fs51 from "node:fs";
44114
44190
  import * as path43 from "node:path";
44115
44191
  function registerRunPromise(runId) {
44116
- let resolve21;
44192
+ let resolve22;
44117
44193
  let reject;
44118
44194
  const promise = new Promise((res, rej) => {
44119
- resolve21 = res;
44195
+ resolve22 = res;
44120
44196
  reject = rej;
44121
44197
  });
44122
- const entry = { promise, resolve: resolve21, reject };
44198
+ const entry = { promise, resolve: resolve22, reject };
44123
44199
  activeRunPromises.set(runId, entry);
44124
44200
  return entry;
44125
44201
  }
@@ -44644,11 +44720,11 @@ function readSkillMarkdown(cwd, name) {
44644
44720
  if (cached) skillReadCache.delete(cacheKey2);
44645
44721
  for (const entry of candidateSkillDirs(cwd)) {
44646
44722
  try {
44647
- const relative8 = path44.join(name, "SKILL.md");
44648
- const contained = resolveContainedPath(entry.root, relative8);
44723
+ const relative9 = path44.join(name, "SKILL.md");
44724
+ const contained = resolveContainedPath(entry.root, relative9);
44649
44725
  if (!fs52.existsSync(contained)) continue;
44650
44726
  if (fs52.lstatSync(contained).isSymbolicLink()) continue;
44651
- const filePath = resolveRealContainedPath(entry.root, relative8);
44727
+ const filePath = resolveRealContainedPath(entry.root, relative9);
44652
44728
  const stat2 = fs52.statSync(filePath);
44653
44729
  return rememberSkill(cacheKey2, {
44654
44730
  path: filePath,
@@ -44787,13 +44863,9 @@ var init_skill_instructions = __esm({
44787
44863
  critic: ["read-only-explorer", "multi-perspective-review"],
44788
44864
  executor: ["state-mutation-locking", "safe-bash", "verification-before-done"],
44789
44865
  reviewer: ["read-only-explorer", "multi-perspective-review"],
44790
- // SECURITY NOTE: The following skill names are trusted package-level skills.
44791
- // If a project has a skills/ directory containing subdirectories with these names,
44792
- // those project-level SKILL.md files will be FOUND FIRST (readSkillMarkdown checks
44793
- // project dir before package dir) and their content injected verbatim into prompts.
44794
- // The "Applicable Skills" block will add an untrusted-content warning for project skills,
44795
- // but be aware this is a potential supply-chain risk in multi-contributor projects.
44796
44866
  "security-reviewer": ["secure-agent-orchestration-review", "ownership-session-security"],
44867
+ // SECURITY NOTE: Package skills are checked FIRST (SEC-003). Project-level
44868
+ // skills with the same name will NOT override the trusted package version.
44797
44869
  "test-engineer": ["verification-before-done", "safe-bash"],
44798
44870
  verifier: ["verification-before-done", "runtime-state-reader"],
44799
44871
  writer: ["context-artifact-hygiene", "verification-before-done"]
@@ -47069,7 +47141,7 @@ function buildTeamDoctorReport(input) {
47069
47141
  );
47070
47142
  const sections = [
47071
47143
  section("Runtime", () => {
47072
- const git3 = commandExists("git", ["--version"]);
47144
+ const git4 = commandExists("git", ["--version"]);
47073
47145
  const pi = piCommandExists();
47074
47146
  return [
47075
47147
  { label: "cwd", ok: true, detail: input.cwd },
@@ -47079,7 +47151,7 @@ function buildTeamDoctorReport(input) {
47079
47151
  detail: `${process.platform}/${process.arch} node=${process.version}`
47080
47152
  },
47081
47153
  { label: "pi command", ok: pi.ok, detail: pi.detail },
47082
- { label: "git command", ok: git3.ok, detail: git3.detail },
47154
+ { label: "git command", ok: git4.ok, detail: git4.detail },
47083
47155
  {
47084
47156
  label: "config",
47085
47157
  ok: input.configErrors.length === 0,
@@ -47926,7 +47998,7 @@ async function spawnBackgroundTeamRun(manifest) {
47926
47998
  const loader = resolveTypeScriptLoader();
47927
47999
  if (!loader) {
47928
48000
  const message = buildLoaderUnavailableMessage(packageRootFromRuntime());
47929
- appendEvent(manifest.eventsPath, {
48001
+ await appendEventAsync(manifest.eventsPath, {
47930
48002
  type: "async.failed",
47931
48003
  runId: manifest.runId,
47932
48004
  message
@@ -49314,6 +49386,21 @@ function cleanupRunWorktrees(manifest, options = {}) {
49314
49386
  const branchName = `pi-crew/${manifest.runId}/${sanitizeBranchPart(entry.name)}`;
49315
49387
  const safeBranchName = sanitizeBranchPart(entry.name);
49316
49388
  if (dirty) {
49389
+ if (!options.force) {
49390
+ const safePreserveName = sanitizeFilename(entry.name);
49391
+ const artifact = writeArtifact(manifest.artifactsRoot, {
49392
+ kind: "diff",
49393
+ relativePath: `cleanup/${safePreserveName}.diff`,
49394
+ content: captureDiff(worktreePath),
49395
+ producer: "worktree-cleanup"
49396
+ });
49397
+ result4.artifactPaths.push(artifact.path);
49398
+ result4.preserved.push({
49399
+ path: worktreePath,
49400
+ reason: "dirty worktree preserved \u2014 pass force=true to auto-commit and remove"
49401
+ });
49402
+ continue;
49403
+ }
49317
49404
  if (options.signal?.aborted) break;
49318
49405
  try {
49319
49406
  const statusBefore = git(worktreePath, ["status", "--porcelain"]);
@@ -54066,8 +54153,8 @@ function safeSharedName(name) {
54066
54153
  function sharedPath(manifest, name) {
54067
54154
  const sharedRoot = path61.resolve(manifest.artifactsRoot, "shared");
54068
54155
  const resolved = path61.resolve(sharedRoot, safeSharedName(name));
54069
- const relative8 = path61.relative(sharedRoot, resolved);
54070
- if (relative8.startsWith("..") || path61.isAbsolute(relative8)) throw new Error(`Invalid shared artifact name: ${name}`);
54156
+ const relative9 = path61.relative(sharedRoot, resolved);
54157
+ if (relative9.startsWith("..") || path61.isAbsolute(relative9)) throw new Error(`Invalid shared artifact name: ${name}`);
54071
54158
  return resolved;
54072
54159
  }
54073
54160
  function tryParseJson(text) {
@@ -54082,8 +54169,8 @@ function listTaskArtifacts(manifest, taskId) {
54082
54169
  const produced = manifest.artifacts.filter((a) => a.producer === taskId);
54083
54170
  if (produced.length === 0) return void 0;
54084
54171
  return produced.map((a) => {
54085
- const relative8 = path61.relative(manifest.artifactsRoot, a.path);
54086
- return relative8.startsWith("..") ? a.path : relative8;
54172
+ const relative9 = path61.relative(manifest.artifactsRoot, a.path);
54173
+ return relative9.startsWith("..") ? a.path : relative9;
54087
54174
  });
54088
54175
  }
54089
54176
  function aggregateUsage2(task) {
@@ -55372,6 +55459,45 @@ import { randomBytes as randomBytes2 } from "node:crypto";
55372
55459
  import * as fs75 from "node:fs";
55373
55460
  import * as path63 from "node:path";
55374
55461
  import { promisify } from "node:util";
55462
+ function git3(cwd, args) {
55463
+ return execFileSync6("git", args, {
55464
+ cwd,
55465
+ encoding: "utf-8",
55466
+ stdio: ["ignore", "pipe", "pipe"],
55467
+ env: {
55468
+ ...sanitizeEnvSecrets(process.env, {
55469
+ allowList: [
55470
+ "PATH",
55471
+ "HOME",
55472
+ "USER",
55473
+ ...WINDOWS_ESSENTIAL_ENV_VARS,
55474
+ "SHELL",
55475
+ "TERM",
55476
+ "LANG",
55477
+ "LC_ALL",
55478
+ "LC_COLLATE",
55479
+ "LC_CTYPE",
55480
+ "LC_MESSAGES",
55481
+ "XDG_CONFIG_HOME",
55482
+ "XDG_DATA_HOME",
55483
+ "XDG_CACHE_HOME",
55484
+ "NVM_BIN",
55485
+ "NVM_DIR",
55486
+ "NODE_PATH",
55487
+ "GIT_CONFIG_GLOBAL",
55488
+ "GIT_CONFIG_SYSTEM",
55489
+ "GIT_AUTHOR_NAME",
55490
+ "GIT_AUTHOR_EMAIL",
55491
+ "GIT_COMMITTER_NAME",
55492
+ "GIT_COMMITTER_EMAIL"
55493
+ ]
55494
+ }),
55495
+ LANG: "en_US.UTF-8",
55496
+ LC_ALL: "en_US.UTF-8"
55497
+ },
55498
+ windowsHide: true
55499
+ }).trim();
55500
+ }
55375
55501
  function gitEnv() {
55376
55502
  return {
55377
55503
  ...sanitizeEnvSecrets(process.env, {
@@ -55458,9 +55584,9 @@ function linkNodeModulesIfPresent(repoRoot, worktreePath) {
55458
55584
  }
55459
55585
  function normalizeSyntheticPath(worktreePath, rawPath) {
55460
55586
  const resolved = path63.resolve(worktreePath, rawPath);
55461
- const relative8 = path63.relative(worktreePath, resolved);
55462
- if (!relative8 || relative8.startsWith("..") || path63.isAbsolute(relative8)) throw new Error(`synthetic path escapes worktree: ${rawPath}`);
55463
- return path63.normalize(relative8);
55587
+ const relative9 = path63.relative(worktreePath, resolved);
55588
+ if (!relative9 || relative9.startsWith("..") || path63.isAbsolute(relative9)) throw new Error(`synthetic path escapes worktree: ${rawPath}`);
55589
+ return path63.normalize(relative9);
55464
55590
  }
55465
55591
  function isAllowedSetupHook(hookPath) {
55466
55592
  if (!hookPath || hookPath.trim().length === 0) return false;
@@ -55690,6 +55816,64 @@ function overlaySeedPaths(repoRoot, worktreePath, seedPaths) {
55690
55816
  });
55691
55817
  }
55692
55818
  }
55819
+ function snapshotDirtyWorktree(manifest, task, worktreePath, dirtyStatus) {
55820
+ try {
55821
+ const parts = [
55822
+ `# Worktree recovery snapshot`,
55823
+ `runId: ${manifest.runId} taskId: ${task.id} path: ${worktreePath}`,
55824
+ `capturedAt: ${(/* @__PURE__ */ new Date()).toISOString()}`,
55825
+ ""
55826
+ ];
55827
+ let trackedDiff = "";
55828
+ try {
55829
+ trackedDiff = git3(worktreePath, ["diff", "HEAD"]);
55830
+ } catch {
55831
+ trackedDiff = "";
55832
+ }
55833
+ if (trackedDiff.trim()) {
55834
+ parts.push("## Tracked changes (`git diff HEAD`)", "```diff", trackedDiff, "```", "");
55835
+ }
55836
+ for (const line4 of dirtyStatus.split("\n")) {
55837
+ if (!line4.startsWith("?? ")) continue;
55838
+ const rel = line4.slice(3).replace(/^"|"$/g, "");
55839
+ if (!rel) continue;
55840
+ try {
55841
+ const abs = path63.join(worktreePath, rel);
55842
+ if (!fs75.existsSync(abs) || fs75.statSync(abs).isDirectory()) continue;
55843
+ const content = fs75.readFileSync(abs, "utf-8");
55844
+ parts.push(`## Untracked file: ${rel}`, "```", content, "```", "");
55845
+ } catch {
55846
+ }
55847
+ }
55848
+ writeArtifact(manifest.artifactsRoot, {
55849
+ kind: "diff",
55850
+ relativePath: `worktree-recovery/${task.id}-${Date.now()}.md`,
55851
+ content: parts.join("\n"),
55852
+ producer: "worktree-manager.snapshotDirtyWorktree",
55853
+ retention: "run"
55854
+ });
55855
+ } catch (err2) {
55856
+ logInternalError(
55857
+ "worktree.recovery.snapshotFailed",
55858
+ err2 instanceof Error ? err2 : new Error(String(err2)),
55859
+ `runId=${manifest.runId}, taskId=${task.id}`
55860
+ );
55861
+ }
55862
+ }
55863
+ async function cleanupCreatedWorktreeAsync(repoRoot, worktreePath, branch) {
55864
+ try {
55865
+ await gitAsync(repoRoot, ["worktree", "remove", "--force", worktreePath]);
55866
+ } catch {
55867
+ try {
55868
+ if (fs75.existsSync(worktreePath)) fs75.rmSync(worktreePath, { recursive: true, force: true });
55869
+ } catch {
55870
+ }
55871
+ }
55872
+ try {
55873
+ await gitAsync(repoRoot, ["branch", "-D", branch]);
55874
+ } catch {
55875
+ }
55876
+ }
55693
55877
  async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55694
55878
  if (manifest.workspaceMode !== "worktree") return { cwd: task.cwd };
55695
55879
  const repoRoot = await findGitRootAsync(manifest.cwd);
@@ -55747,16 +55931,17 @@ async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55747
55931
  }
55748
55932
  const dirtyStatus = await gitAsync(worktreePath, ["status", "--porcelain"]);
55749
55933
  if (dirtyStatus.trim()) {
55934
+ snapshotDirtyWorktree(manifest, task, worktreePath, dirtyStatus);
55750
55935
  logInternalError(
55751
55936
  "worktree.reused.dirty",
55752
- new Error(`Discarding uncommitted changes in reused worktree at ${worktreePath}`),
55937
+ new Error(`Discarding uncommitted changes in reused worktree at ${worktreePath} (snapshot saved to artifacts)`),
55753
55938
  `runId=${manifest.runId}, taskId=${task.id}, dirtyStatus=${dirtyStatus.trim()}`
55754
55939
  );
55755
55940
  await gitAsync(worktreePath, ["checkout", "--", "."]);
55756
55941
  await gitAsync(worktreePath, ["clean", "-fd"]);
55757
55942
  }
55758
- const globalSeedPaths2 = loadedConfig.config.worktree?.seedPaths ?? [];
55759
- const mergedReused = normalizeSeedPaths([...globalSeedPaths2, ...stepSeedPaths ?? []], repoRoot);
55943
+ const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55944
+ const mergedReused = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55760
55945
  if (mergedReused.length > 0) {
55761
55946
  overlaySeedPaths(repoRoot, worktreePath, mergedReused);
55762
55947
  }
@@ -55796,21 +55981,26 @@ async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55796
55981
  }
55797
55982
  throw error;
55798
55983
  }
55799
- const syntheticPaths = runSetupHook(manifest, task, repoRoot, worktreePath, branch);
55800
- const nodeModulesLinked = loadedConfig.config.worktree?.linkNodeModules === true ? linkNodeModulesIfPresent(repoRoot, worktreePath) : false;
55801
- const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55802
- const merged = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55803
- if (merged.length > 0) {
55804
- overlaySeedPaths(repoRoot, worktreePath, merged);
55984
+ try {
55985
+ const syntheticPaths = runSetupHook(manifest, task, repoRoot, worktreePath, branch);
55986
+ const nodeModulesLinked = loadedConfig.config.worktree?.linkNodeModules === true ? linkNodeModulesIfPresent(repoRoot, worktreePath) : false;
55987
+ const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55988
+ const merged = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55989
+ if (merged.length > 0) {
55990
+ overlaySeedPaths(repoRoot, worktreePath, merged);
55991
+ }
55992
+ return {
55993
+ cwd: worktreePath,
55994
+ worktreePath,
55995
+ branch,
55996
+ reused: false,
55997
+ nodeModulesLinked,
55998
+ syntheticPaths
55999
+ };
56000
+ } catch (setupError) {
56001
+ await cleanupCreatedWorktreeAsync(repoRoot, worktreePath, branch);
56002
+ throw setupError;
55805
56003
  }
55806
- return {
55807
- cwd: worktreePath,
55808
- worktreePath,
55809
- branch,
55810
- reused: false,
55811
- nodeModulesLinked,
55812
- syntheticPaths
55813
- };
55814
56004
  }
55815
56005
  async function captureWorktreeDiffStatAsync(worktreePath) {
55816
56006
  try {
@@ -56870,7 +57060,7 @@ import * as fs79 from "node:fs";
56870
57060
  import * as path67 from "node:path";
56871
57061
  async function detectRipgrep() {
56872
57062
  if (cachedRgCheck !== void 0) return cachedRgCheck;
56873
- return await new Promise((resolve21) => {
57063
+ return await new Promise((resolve22) => {
56874
57064
  let settled = false;
56875
57065
  try {
56876
57066
  const child = spawn3("rg", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
@@ -56882,7 +57072,7 @@ async function detectRipgrep() {
56882
57072
  if (settled) return;
56883
57073
  settled = true;
56884
57074
  cachedRgCheck = { available: false };
56885
- resolve21(cachedRgCheck);
57075
+ resolve22(cachedRgCheck);
56886
57076
  });
56887
57077
  child.on("close", (code) => {
56888
57078
  if (settled) return;
@@ -56892,13 +57082,13 @@ async function detectRipgrep() {
56892
57082
  } else {
56893
57083
  cachedRgCheck = { available: false };
56894
57084
  }
56895
- resolve21(cachedRgCheck);
57085
+ resolve22(cachedRgCheck);
56896
57086
  });
56897
57087
  } catch {
56898
57088
  if (settled) return;
56899
57089
  settled = true;
56900
57090
  cachedRgCheck = { available: false };
56901
- resolve21(cachedRgCheck);
57091
+ resolve22(cachedRgCheck);
56902
57092
  }
56903
57093
  });
56904
57094
  }
@@ -56915,7 +57105,7 @@ function reasonFor(file, keywords2) {
56915
57105
  return `keyword match: ${hits.join(", ")}`;
56916
57106
  }
56917
57107
  function runRipgrep(args, cwd) {
56918
- return new Promise((resolve21, reject) => {
57108
+ return new Promise((resolve22, reject) => {
56919
57109
  let settled = false;
56920
57110
  let stdout = "";
56921
57111
  let stderr = "";
@@ -56936,7 +57126,7 @@ function runRipgrep(args, cwd) {
56936
57126
  if (settled) return;
56937
57127
  settled = true;
56938
57128
  if (code === 0 || code === 1) {
56939
- resolve21(stdout);
57129
+ resolve22(stdout);
56940
57130
  } else {
56941
57131
  reject(new Error(`rg exited ${code}: ${stderr.slice(0, 200)}`));
56942
57132
  }
@@ -57213,9 +57403,9 @@ function artifactReference(artifactsRoot, artifact) {
57213
57403
  if (!artifact) return void 0;
57214
57404
  const root = path68.resolve(artifactsRoot);
57215
57405
  const target = path68.resolve(artifact.path);
57216
- const relative8 = path68.relative(root, target);
57217
- if (!relative8 || relative8.startsWith("..") || path68.isAbsolute(relative8)) return void 0;
57218
- return relative8.replaceAll("\\", "/");
57406
+ const relative9 = path68.relative(root, target);
57407
+ if (!relative9 || relative9.startsWith("..") || path68.isAbsolute(relative9)) return void 0;
57408
+ return relative9.replaceAll("\\", "/");
57219
57409
  }
57220
57410
  function buildWorkerPromptPipeline(input) {
57221
57411
  return {
@@ -57292,6 +57482,7 @@ function updateTask(tasks, updated) {
57292
57482
  return tasks.map((task) => task.id === updated.id ? updated : task);
57293
57483
  }
57294
57484
  function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPhase) {
57485
+ const MAX_CAS_ATTEMPTS = 100;
57295
57486
  let baseMtime = 0;
57296
57487
  try {
57297
57488
  baseMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
@@ -57308,7 +57499,7 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
57308
57499
  } : updated;
57309
57500
  try {
57310
57501
  return withRunLockSync(manifest, () => {
57311
- for (let attempt = 0; attempt < 100; attempt++) {
57502
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
57312
57503
  flushPendingAtomicWrites();
57313
57504
  const latest = loadRunManifestById(manifest.cwd, manifest.runId)?.tasks ?? fallbackTasks;
57314
57505
  merged = updateTask(latest, taskWithCheckpoint);
@@ -57325,20 +57516,25 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
57325
57516
  break;
57326
57517
  }
57327
57518
  if (merged === void 0) {
57328
- logInternalError("persistSingleTaskUpdate", new Error("failed to converge after 50 attempts"));
57329
- throw new Error("persistSingleTaskUpdate: failed to converge after 50 attempts");
57519
+ logInternalError(
57520
+ "persistSingleTaskUpdate",
57521
+ new Error(`failed to converge after ${MAX_CAS_ATTEMPTS} attempts`),
57522
+ void 0,
57523
+ "error"
57524
+ );
57525
+ throw new Error(`persistSingleTaskUpdate: failed to converge after ${MAX_CAS_ATTEMPTS} attempts`);
57330
57526
  }
57331
57527
  try {
57332
57528
  saveRunTasksCoalesced(manifest, merged);
57333
57529
  } catch (err2) {
57334
- logInternalError("persistSingleTaskUpdate", err2);
57530
+ logInternalError("persistSingleTaskUpdate", err2, void 0, "error");
57335
57531
  throw err2;
57336
57532
  }
57337
57533
  return merged;
57338
57534
  });
57339
57535
  } catch (err2) {
57340
57536
  if (merged === void 0) {
57341
- logInternalError("persistSingleTaskUpdate", err2);
57537
+ logInternalError("persistSingleTaskUpdate", err2, void 0, "error");
57342
57538
  }
57343
57539
  throw err2;
57344
57540
  }
@@ -57427,7 +57623,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57427
57623
  const start = Date.now();
57428
57624
  let output = "";
57429
57625
  let exitCode = null;
57430
- return new Promise((resolve21) => {
57626
+ return new Promise((resolve22) => {
57431
57627
  const shell = spawn4("sh", ["-c", command], {
57432
57628
  cwd,
57433
57629
  timeout: timeoutMs,
@@ -57441,7 +57637,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57441
57637
  });
57442
57638
  const timer = setTimeout(() => {
57443
57639
  shell.kill("SIGKILL");
57444
- resolve21({
57640
+ resolve22({
57445
57641
  exitCode: -1,
57446
57642
  output: output + "\n[TIMEOUT: Command exceeded limit]",
57447
57643
  durationMs: Date.now() - start
@@ -57452,7 +57648,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57452
57648
  shell.on("close", (code) => {
57453
57649
  clearTimer();
57454
57650
  exitCode = code;
57455
- resolve21({
57651
+ resolve22({
57456
57652
  exitCode,
57457
57653
  output: output.slice(-1e5),
57458
57654
  // Cap at 100KB
@@ -57461,7 +57657,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57461
57657
  });
57462
57658
  shell.on("error", (err2) => {
57463
57659
  clearTimer();
57464
- resolve21({
57660
+ resolve22({
57465
57661
  exitCode: -1,
57466
57662
  output: `Execution error: ${err2.message}`,
57467
57663
  durationMs: Date.now() - start
@@ -60165,7 +60361,8 @@ async function executeTeamRun(input) {
60165
60361
  logInternalError(
60166
60362
  "team-runner.goalAchievement.falseGreen",
60167
60363
  new Error(gaApplied.manifest.goalAchievementNote ?? "false-green detected"),
60168
- `runId=${manifest.runId}`
60364
+ `runId=${manifest.runId}`,
60365
+ "error"
60169
60366
  );
60170
60367
  stopTeamHeartbeat();
60171
60368
  resolveRunPromise(manifest.runId, result4);
@@ -60200,8 +60397,9 @@ async function executeTeamRun(input) {
60200
60397
  } catch (error) {
60201
60398
  stopTeamHeartbeat();
60202
60399
  const message = error instanceof Error ? error.message : String(error);
60203
- const freshManifest = manifest;
60204
- const freshTasks = refreshTaskGraphQueues(input.tasks);
60400
+ const fresh = loadRunManifestById(manifest.cwd, manifest.runId);
60401
+ const freshManifest = fresh?.manifest ?? manifest;
60402
+ const freshTasks = refreshTaskGraphQueues(fresh?.tasks ?? input.tasks);
60205
60403
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
60206
60404
  const tasks = freshTasks.map(
60207
60405
  (task) => task.status === "running" || task.status === "queued" || task.status === "waiting" ? {
@@ -61787,19 +61985,19 @@ function parseRoot(start) {
61787
61985
  function safeJoin(...parts) {
61788
61986
  const filtered = parts.filter(Boolean);
61789
61987
  if (filtered.length === 0) return "";
61790
- const sep9 = filtered.some((p) => p.includes("\\")) ? "\\" : "/";
61988
+ const sep10 = filtered.some((p) => p.includes("\\")) ? "\\" : "/";
61791
61989
  const firstPart = filtered[0];
61792
61990
  let leading = "";
61793
- if (sep9 === "\\") {
61991
+ if (sep10 === "\\") {
61794
61992
  if (firstPart.startsWith("\\\\")) leading = "\\\\";
61795
61993
  else if (firstPart.startsWith("\\")) leading = "\\";
61796
61994
  } else if (firstPart.startsWith("/")) {
61797
61995
  leading = "/";
61798
61996
  }
61799
- const firstPartStripped = sep9 === "\\" ? firstPart.replace(/^\\{1,2}/, "") : firstPart.replace(/^\/+/, "");
61997
+ const firstPartStripped = sep10 === "\\" ? firstPart.replace(/^\\{1,2}/, "") : firstPart.replace(/^\/+/, "");
61800
61998
  const rest = filtered.slice(1);
61801
- const joined = [firstPartStripped, ...rest].filter(Boolean).join(sep9);
61802
- const collapsed = joined.replace(new RegExp(`${sep9 === "\\" ? "\\\\" : "/"}{2,}`, "g"), sep9);
61999
+ const joined = [firstPartStripped, ...rest].filter(Boolean).join(sep10);
62000
+ const collapsed = joined.replace(new RegExp(`${sep10 === "\\" ? "\\\\" : "/"}{2,}`, "g"), sep10);
61803
62001
  return leading + collapsed;
61804
62002
  }
61805
62003
  function safeDirname(p) {
@@ -61956,12 +62154,12 @@ function tokenize(input) {
61956
62154
  continue;
61957
62155
  }
61958
62156
  if (/[0-9]/.test(input[i2])) {
61959
- let num = "";
62157
+ let num2 = "";
61960
62158
  while (i2 < input.length && /[0-9]/.test(input[i2])) {
61961
- num += input[i2];
62159
+ num2 += input[i2];
61962
62160
  i2++;
61963
62161
  }
61964
- tokens.push({ type: "NUMBER", value: num });
62162
+ tokens.push({ type: "NUMBER", value: num2 });
61965
62163
  continue;
61966
62164
  }
61967
62165
  if (/[a-zA-Z_]/.test(input[i2])) {
@@ -62027,8 +62225,8 @@ var init_chain_parser = __esm({
62027
62225
  while (this.pos < this.tokens.length) {
62028
62226
  if (this.peek("COLON")) {
62029
62227
  this.consume("COLON");
62030
- const num = this.consume("NUMBER");
62031
- step.loopCount = Number.parseInt(num.value, 10);
62228
+ const num2 = this.consume("NUMBER");
62229
+ step.loopCount = Number.parseInt(num2.value, 10);
62032
62230
  } else if (this.peek("FLAG", "with-context")) {
62033
62231
  this.consume("FLAG");
62034
62232
  step.withContext = true;
@@ -68932,7 +69130,7 @@ var init_deterministic_ast = __esm({
68932
69130
  });
68933
69131
 
68934
69132
  // src/runtime/dwf-state-store.ts
68935
- import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync68, unlinkSync as unlinkSync11 } from "node:fs";
69133
+ import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync69, unlinkSync as unlinkSync11 } from "node:fs";
68936
69134
  import { dirname as dirname37 } from "node:path";
68937
69135
  var DwfStore;
68938
69136
  var init_dwf_state_store = __esm({
@@ -68953,7 +69151,7 @@ var init_dwf_state_store = __esm({
68953
69151
  const path81 = this.path;
68954
69152
  try {
68955
69153
  if (!existsSync70(path81)) return void 0;
68956
- const raw = readFileSync68(path81, "utf-8");
69154
+ const raw = readFileSync69(path81, "utf-8");
68957
69155
  const parsed = JSON.parse(raw);
68958
69156
  if (!parsed || typeof parsed !== "object" || typeof parsed.runId !== "string") return void 0;
68959
69157
  return parsed;
@@ -69164,14 +69362,14 @@ var init_semaphore = __esm({
69164
69362
  if (this.#queue.length >= _Semaphore.MAX_QUEUE) {
69165
69363
  throw new Error(`Semaphore queue full: ${this.#queue.length} waiters (max ${_Semaphore.MAX_QUEUE}); cannot acquire slot`);
69166
69364
  }
69167
- const { promise, resolve: resolve21 } = (() => {
69365
+ const { promise, resolve: resolve22 } = (() => {
69168
69366
  let res;
69169
69367
  const p = new Promise((r) => {
69170
69368
  res = r;
69171
69369
  });
69172
69370
  return { promise: p, resolve: res };
69173
69371
  })();
69174
- this.#queue.push(resolve21);
69372
+ this.#queue.push(resolve22);
69175
69373
  return promise;
69176
69374
  }
69177
69375
  release() {
@@ -69775,7 +69973,7 @@ var dynamic_workflow_runner_exports = {};
69775
69973
  __export(dynamic_workflow_runner_exports, {
69776
69974
  runDynamicWorkflow: () => runDynamicWorkflow
69777
69975
  });
69778
- import { readFileSync as readFileSync69 } from "node:fs";
69976
+ import { readFileSync as readFileSync70 } from "node:fs";
69779
69977
  import { join as join71 } from "node:path";
69780
69978
  function assertStructuredCloneable(value, name) {
69781
69979
  try {
@@ -69800,7 +69998,7 @@ function resolveScriptPath(workflow, cwd) {
69800
69998
  );
69801
69999
  }
69802
70000
  async function loadWorkflowModule(scriptPath) {
69803
- const scriptSource = readFileSync69(scriptPath, "utf-8");
70001
+ const scriptSource = readFileSync70(scriptPath, "utf-8");
69804
70002
  if (isDeterminismCheckEnabled()) {
69805
70003
  assertDeterministicScript(scriptSource);
69806
70004
  }
@@ -69923,7 +70121,7 @@ async function runDynamicWorkflow(input) {
69923
70121
  }
69924
70122
  function readFinalArtifact(artifactPath) {
69925
70123
  try {
69926
- return readFileSync69(artifactPath, "utf-8");
70124
+ return readFileSync70(artifactPath, "utf-8");
69927
70125
  } catch (error) {
69928
70126
  logInternalError("dynamic-workflow-runner.readFinal", error, `artifactPath=${artifactPath}`);
69929
70127
  return `(failed to read final artifact ${artifactPath})`;
@@ -73818,7 +74016,7 @@ var init_run_dashboard = __esm({
73818
74016
  const borderFill = (count2) => new DynamicCrewBorder(this.theme).render(count2)[0];
73819
74017
  const border2 = (left, right) => `${fg("border", left)}${borderFill(borderWidth)}${fg("border", right)}`;
73820
74018
  const row = (text) => `\u2502 ${pad(truncate(text, innerWidth - 1), innerWidth - 1)}\u2502`;
73821
- const sep9 = () => border2("\u251C", "\u2524");
74019
+ const sep10 = () => border2("\u251C", "\u2524");
73822
74020
  const lines = [];
73823
74021
  if (this.showHelp) {
73824
74022
  lines.push(...new HelpOverlay(this.theme).render(width));
@@ -73828,7 +74026,7 @@ var init_run_dashboard = __esm({
73828
74026
  row(
73829
74027
  `${fg("accent", "\u2590")} ${this.theme.bold("pi-crew")} \xB7 ${this.runs.length} runs ${fg("dim", "1-6 pane \xB7 \u2191\u2193 \xB7 Enter \xB7 ? help \xB7 Esc")}`
73830
74028
  ),
73831
- sep9()
74029
+ sep10()
73832
74030
  );
73833
74031
  if (this.runs.length === 0) {
73834
74032
  lines.push(row(fg("dim", "No runs yet.")));
@@ -73876,7 +74074,7 @@ var init_run_dashboard = __esm({
73876
74074
  const agents = snap?.agents ?? agentsFor2(selectedRun, this.options.snapshotCache);
73877
74075
  const statusStr = isLikelyOrphanedActiveRun(r, agents) ? "stale" : r.status;
73878
74076
  const selectedTasks = snap?.tasks ?? readRunTasks2(r, this.options.snapshotCache);
73879
- lines.push(sep9());
74077
+ lines.push(sep10());
73880
74078
  lines.push(row(`${fg("accent", "\u25B8")} ${truncate(sanitizeLine(r.goal), innerWidth - 6)}`));
73881
74079
  const isTerminal = statusStr === "failed" || statusStr === "cancelled" || statusStr === "stopped";
73882
74080
  const reason = isTerminal ? summarizeTerminalReason(r, selectedTasks, snap?.cancellationReason) : void 0;
@@ -75808,10 +76006,10 @@ var init_settings_overlay = __esm({
75808
76006
  typeof current2 === "number" ? String(current2) : "",
75809
76007
  this.theme,
75810
76008
  (value) => {
75811
- const num = value === "" ? void 0 : Number(value);
75812
- if (num !== void 0 && !Number.isNaN(num)) {
75813
- this.changedValues.set(def.id, num);
75814
- this.callbacks.onChange(def.id, num);
76009
+ const num2 = value === "" ? void 0 : Number(value);
76010
+ if (num2 !== void 0 && !Number.isNaN(num2)) {
76011
+ this.changedValues.set(def.id, num2);
76012
+ this.callbacks.onChange(def.id, num2);
75815
76013
  } else if (value === "") {
75816
76014
  this.changedValues.set(def.id, void 0);
75817
76015
  this.callbacks.onChange(def.id, void 0);
@@ -81702,12 +81900,15 @@ function registerCrewShortcuts(pi) {
81702
81900
  }
81703
81901
  var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
81704
81902
 
81903
+ // src/extension/crew-vibes/index.ts
81904
+ init_pi_ui_compat();
81905
+
81705
81906
  // src/extension/crew-vibes/config.ts
81706
- import { existsSync as existsSync76, mkdirSync as mkdirSync45, readFileSync as readFileSync75, writeFileSync as writeFileSync33 } from "node:fs";
81907
+ import { existsSync as existsSync76, mkdirSync as mkdirSync45, readFileSync as readFileSync76, writeFileSync as writeFileSync33 } from "node:fs";
81707
81908
  import { dirname as dirname39, join as join76 } from "node:path";
81708
81909
 
81709
81910
  // src/extension/crew-vibes/font-detect.ts
81710
- import { existsSync as existsSync75, readFileSync as readFileSync74 } from "node:fs";
81911
+ import { existsSync as existsSync75, readFileSync as readFileSync75 } from "node:fs";
81711
81912
  import { homedir as homedir11, platform } from "node:os";
81712
81913
  import { join as join75 } from "node:path";
81713
81914
  function fontPath() {
@@ -81734,10 +81935,10 @@ function isWebTerminal() {
81734
81935
  try {
81735
81936
  let pid = process.pid;
81736
81937
  for (let i2 = 0; i2 < 6 && pid > 1; i2++) {
81737
- const cgroup = readFileSync74(`/proc/${pid}/cgroup`, "utf8");
81938
+ const cgroup = readFileSync75(`/proc/${pid}/cgroup`, "utf8");
81738
81939
  if (cgroup.includes("gotty") || cgroup.includes("wetty")) return true;
81739
81940
  const match = cgroup.match(/\d+:.*:(.*)/);
81740
- const status = readFileSync74(`/proc/${pid}/status`, "utf8");
81941
+ const status = readFileSync75(`/proc/${pid}/status`, "utf8");
81741
81942
  const ppid = status.match(/^PPid:\s+(\d+)/m);
81742
81943
  pid = ppid ? Number.parseInt(ppid[1], 10) : 1;
81743
81944
  }
@@ -81780,7 +81981,7 @@ var DEFAULT_CONFIG2 = {
81780
81981
  labels: ["Orbit", "Cruise", "Warp", "Black Hole", "Supernova", "Big Bang"],
81781
81982
  icons: ["\uE710", "\uE711", "\uE712", "\uE713", "\uE714", "\uE715"],
81782
81983
  providerUsage: true,
81783
- providerRefreshMs: 3e5
81984
+ providerRefreshMs: 12e4
81784
81985
  }
81785
81986
  };
81786
81987
  var FALLBACK_CAPACITY_ICONS = [
@@ -81869,7 +82070,7 @@ function loadConfig2() {
81869
82070
  try {
81870
82071
  const path81 = configPath2();
81871
82072
  if (!existsSync76(path81)) return normalizeConfig(void 0);
81872
- return normalizeConfig(JSON.parse(readFileSync75(path81, "utf8")));
82073
+ return normalizeConfig(JSON.parse(readFileSync76(path81, "utf8")));
81873
82074
  } catch {
81874
82075
  return normalizeConfig(void 0);
81875
82076
  }
@@ -81881,8 +82082,379 @@ function saveConfig(config) {
81881
82082
  `);
81882
82083
  }
81883
82084
 
82085
+ // src/extension/crew-vibes/figures.ts
82086
+ var BRAILLE_FRAMES = [
82087
+ "\u280B ",
82088
+ // ⠋
82089
+ "\u2819 ",
82090
+ // ⠙
82091
+ "\u2839 ",
82092
+ // ⠹
82093
+ "\u2838 ",
82094
+ // ⠸
82095
+ "\u283C ",
82096
+ // ⠼
82097
+ "\u2834 ",
82098
+ // ⠴
82099
+ "\u2826 ",
82100
+ // ⠦
82101
+ "\u2827 ",
82102
+ // ⠧
82103
+ "\u2807 ",
82104
+ // ⠇
82105
+ "\u280F "
82106
+ // ⠏
82107
+ ];
82108
+ var PUA_CREW_FRAMES = [
82109
+ "\uE700 ",
82110
+ "\uE701 ",
82111
+ "\uE702 ",
82112
+ "\uE703 ",
82113
+ "\uE704 ",
82114
+ "\uE705 ",
82115
+ "\uE706 ",
82116
+ "\uE707 ",
82117
+ "\uE708 ",
82118
+ "\uE709 ",
82119
+ "\uE70A ",
82120
+ "\uE70B ",
82121
+ "\uE70C ",
82122
+ "\uE70D ",
82123
+ "\uE70E ",
82124
+ "\uE70F "
82125
+ ];
82126
+ function crewFrames(style = "pua") {
82127
+ if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
82128
+ return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
82129
+ }
82130
+ function intervalForSpeed(config, speed) {
82131
+ if (speed === null || !Number.isFinite(speed) || speed <= 0) return config.defaultIntervalMs;
82132
+ return Math.max(config.minIntervalMs, Math.min(config.maxIntervalMs, Math.round(config.scale / speed)));
82133
+ }
82134
+ function capacityIndex(percent, levels = 6) {
82135
+ if (percent === null || percent === void 0 || !Number.isFinite(percent)) return 0;
82136
+ return Math.max(0, Math.min(levels - 1, Math.floor(Math.max(0, Math.min(100, percent)) / 100 * levels)));
82137
+ }
82138
+ function isDangerStage(index, levels) {
82139
+ return index >= Math.max(0, levels - 2);
82140
+ }
82141
+
82142
+ // src/extension/crew-vibes/footer.ts
82143
+ init_pi_ui_compat();
82144
+ init_theme_adapter();
82145
+ init_visual();
82146
+ import { isAbsolute as isAbsolute10, relative as relative8, resolve as resolve20, sep as sep9 } from "node:path";
82147
+
82148
+ // src/extension/crew-vibes/render.ts
82149
+ function formatCount(value) {
82150
+ if (value < 1e3) return value.toString();
82151
+ if (value < 1e4) return `${(value / 1e3).toFixed(1)}k`;
82152
+ if (value < 1e6) return `${Math.round(value / 1e3)}k`;
82153
+ if (value < 1e7) return `${(value / 1e6).toFixed(1)}M`;
82154
+ return `${Math.round(value / 1e6)}M`;
82155
+ }
82156
+ function asCrewTheme2(theme) {
82157
+ if (theme && typeof theme === "object" && typeof theme.fg === "function") {
82158
+ return theme;
82159
+ }
82160
+ return void 0;
82161
+ }
82162
+ function getCapacityUsage(ctx) {
82163
+ const fn = ctx.getContextUsage;
82164
+ const usage = typeof fn === "function" ? fn.call(ctx) : null;
82165
+ return {
82166
+ tokens: typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : null,
82167
+ percent: typeof usage?.percent === "number" && Number.isFinite(usage.percent) ? usage.percent : null
82168
+ };
82169
+ }
82170
+ function formatSpeed(config, speed) {
82171
+ return speed === null ? `-- ${config.label}` : `${speed.toFixed(1)} ${config.label}`;
82172
+ }
82173
+ function renderSpeedFooter(theme, config, speed) {
82174
+ const value = speed === null ? "--" : speed.toFixed(1);
82175
+ const valueTone = speed === null ? "dim" : "accent";
82176
+ const styled = theme ? `${theme.fg(valueTone, value)} ${theme.fg("dim", config.label)}` : `${value} ${config.label}`;
82177
+ return styled;
82178
+ }
82179
+ function renderWorkingMessage(theme, config, speed) {
82180
+ const left = "Working";
82181
+ const speedText = theme ? `${theme.fg(speed === null ? "dim" : "accent", speed === null ? "--" : speed.toFixed(1))} ${theme.fg("dim", config.label)}` : `${speed === null ? "--" : speed.toFixed(1)} ${config.label}`;
82182
+ return theme ? `${theme.fg("muted", left)} ${speedText}` : `${left} ${speedText}`;
82183
+ }
82184
+ function crewIndicatorFrames(theme) {
82185
+ const frames = crewFrames();
82186
+ if (!theme) return [...frames];
82187
+ return frames.map((frame) => theme.fg("accent", frame));
82188
+ }
82189
+ function formatCapacityPrefix(config, usage) {
82190
+ const display = config.tokenDisplay;
82191
+ if (display === "off") return "";
82192
+ if (display === "percentage") {
82193
+ return `${usage.percent === null ? "?" : Math.round(Math.max(0, Math.min(999, usage.percent)))}% `;
82194
+ }
82195
+ return `${usage.tokens === null ? "?" : formatCount(usage.tokens)} `;
82196
+ }
82197
+ function colorStage(theme, index, levels, text) {
82198
+ if (!theme || text.length === 0) return text;
82199
+ return theme.fg(isDangerStage(index, levels) ? "error" : "success", text);
82200
+ }
82201
+ function renderCapacity(theme, config, usage) {
82202
+ const icons = capacityIcons();
82203
+ const levels = icons.length;
82204
+ const index = capacityIndex(usage.percent, levels);
82205
+ const icon = icons[index] ?? icons[0];
82206
+ const label = config.labels[index] ?? config.labels[0];
82207
+ const prefix = theme ? theme.fg("muted", formatCapacityPrefix(config, usage)) : formatCapacityPrefix(config, usage);
82208
+ const coloredIcon = colorStage(theme, index, levels, icon);
82209
+ const afterIcon = config.showLabel ? ` ${colorStage(theme, index, levels, label)}` : " ";
82210
+ return `${prefix}${coloredIcon}${afterIcon}`;
82211
+ }
82212
+ function setSpeedStatus(ctx, config, text) {
82213
+ if (!ctx?.hasUI) return;
82214
+ if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
82215
+ ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82216
+ return;
82217
+ }
82218
+ ctx.ui.setStatus(SPEED_STATUS_ID, text);
82219
+ }
82220
+ function clearVibesStatus(ctx) {
82221
+ if (!ctx?.hasUI) return;
82222
+ ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82223
+ ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82224
+ ctx.ui.setStatus(PROVIDER_STATUS_ID, void 0);
82225
+ if (ctx.ui.setWorkingIndicator) ctx.ui.setWorkingIndicator();
82226
+ if (ctx.ui.setWorkingMessage) ctx.ui.setWorkingMessage();
82227
+ }
82228
+ function formatResetTimer(resetAt) {
82229
+ if (!resetAt) return null;
82230
+ const diffMs = new Date(resetAt).getTime() - Date.now();
82231
+ if (diffMs < 0) return null;
82232
+ const mins = Math.floor(diffMs / 6e4);
82233
+ if (mins < 60) return `${mins}m`;
82234
+ const hours = Math.floor(mins / 60);
82235
+ if (hours < 48) {
82236
+ const remMins = mins % 60;
82237
+ return remMins > 0 ? `${hours}h${remMins}m` : `${hours}h`;
82238
+ }
82239
+ const days = Math.floor(hours / 24);
82240
+ const remHours = hours % 24;
82241
+ return remHours > 0 ? `${days}d${remHours}h` : `${days}d`;
82242
+ }
82243
+ function renderBar(percent, width = 8) {
82244
+ const clamped = Math.max(0, Math.min(100, percent));
82245
+ const filled = Math.round(clamped / 100 * width);
82246
+ return `${"\u2501".repeat(filled)}${"\u2504".repeat(width - filled)}`;
82247
+ }
82248
+ function renderProviderUsage(theme, usage) {
82249
+ if (!usage) return void 0;
82250
+ const parts = [];
82251
+ if (usage.providerName) {
82252
+ const nameText = usage.providerName;
82253
+ parts.push(theme ? theme.fg("muted", nameText) : nameText);
82254
+ }
82255
+ const fiveHourBar = renderBar(usage.fiveHourPercent);
82256
+ const fiveHourRounded = Math.round(usage.fiveHourPercent);
82257
+ const fiveHourReset = formatResetTimer(usage.fiveHourResetAt);
82258
+ const fiveHourText = `5h ${fiveHourBar} ${fiveHourRounded}%${fiveHourReset ? " " + fiveHourReset : ""}`;
82259
+ const fiveHourColor = usage.fiveHourPercent >= 80 ? "error" : "accent";
82260
+ parts.push(theme ? theme.fg(fiveHourColor, fiveHourText) : fiveHourText);
82261
+ const weeklyBar = renderBar(usage.weeklyPercent);
82262
+ const weeklyRounded = Math.round(usage.weeklyPercent);
82263
+ const weeklyReset = formatResetTimer(usage.weeklyResetAt);
82264
+ const weeklyText = `Wk ${weeklyBar} ${weeklyRounded}%${weeklyReset ? " " + weeklyReset : ""}`;
82265
+ parts.push(theme ? theme.fg("dim", weeklyText) : weeklyText);
82266
+ if (typeof usage.copilotMonthlyPercent === "number" && Number.isFinite(usage.copilotMonthlyPercent)) {
82267
+ const monthlyRounded = Math.round(usage.copilotMonthlyPercent);
82268
+ const monthlyText = `Mo: ${monthlyRounded}%`;
82269
+ parts.push(theme ? theme.fg("dim", monthlyText) : monthlyText);
82270
+ }
82271
+ return parts.join(" ");
82272
+ }
82273
+
82274
+ // src/extension/crew-vibes/footer.ts
82275
+ function num(value) {
82276
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
82277
+ }
82278
+ function formatCwdForFooter(cwd, home) {
82279
+ if (!home) return cwd;
82280
+ const resolvedCwd = resolve20(cwd);
82281
+ const resolvedHome = resolve20(home);
82282
+ const rel = relative8(resolvedHome, resolvedCwd);
82283
+ const inside = rel === "" || rel !== ".." && !rel.startsWith(`..${sep9}`) && !isAbsolute10(rel);
82284
+ if (!inside) return cwd;
82285
+ return rel === "" ? "~" : `~${sep9}${rel}`;
82286
+ }
82287
+ function sanitizeStatusText(text) {
82288
+ return text.replace(/[\r\n\t]/g, " ").replace(/ +/g, " ").trim();
82289
+ }
82290
+ function asFooterData(value) {
82291
+ if (!value || typeof value !== "object") return void 0;
82292
+ const record = value;
82293
+ if (typeof record.getGitBranch !== "function" || typeof record.getExtensionStatuses !== "function" || typeof record.getAvailableProviderCount !== "function" || typeof record.onBranchChange !== "function") {
82294
+ return void 0;
82295
+ }
82296
+ return value;
82297
+ }
82298
+ function computeTotals(entries) {
82299
+ const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
82300
+ for (const entry of entries) {
82301
+ const rec = entry;
82302
+ if (rec?.type !== "message" || rec.message?.role !== "assistant") continue;
82303
+ const usage = rec.message.usage;
82304
+ if (!usage) continue;
82305
+ totals.input += num(usage.input);
82306
+ totals.output += num(usage.output);
82307
+ totals.cacheRead += num(usage.cacheRead);
82308
+ totals.cacheWrite += num(usage.cacheWrite);
82309
+ totals.cost += num(usage.cost?.total);
82310
+ }
82311
+ return totals;
82312
+ }
82313
+ var CrewVibesFooter = class {
82314
+ theme;
82315
+ footerData;
82316
+ ctx;
82317
+ source;
82318
+ tui;
82319
+ unsubscribeBranch;
82320
+ constructor(deps) {
82321
+ this.theme = asCrewTheme(deps.theme);
82322
+ this.footerData = asFooterData(deps.footerData);
82323
+ this.ctx = deps.ctx;
82324
+ this.source = deps.source;
82325
+ this.tui = deps.tui;
82326
+ this.unsubscribeBranch = this.footerData ? this.footerData.onBranchChange(() => requestRenderTarget(this.tui)) : () => {
82327
+ };
82328
+ }
82329
+ invalidate() {
82330
+ }
82331
+ dispose() {
82332
+ this.unsubscribeBranch();
82333
+ }
82334
+ buildPwdLine(width) {
82335
+ const sm = this.ctx.sessionManager;
82336
+ let pwd = formatCwdForFooter(sm.getCwd(), process.env.HOME || process.env.USERPROFILE);
82337
+ const branch = this.footerData?.getGitBranch();
82338
+ if (branch) pwd = `${pwd} (${branch})`;
82339
+ const sessionName = sm.getSessionName?.();
82340
+ if (sessionName) pwd = `${pwd} \u2022 ${sessionName}`;
82341
+ return truncateToWidth(this.theme.fg("dim", pwd), width, this.theme.fg("dim", "..."));
82342
+ }
82343
+ buildStatsLine(width) {
82344
+ const theme = this.theme;
82345
+ const model = this.ctx.model;
82346
+ const totals = computeTotals(this.ctx.sessionManager.getEntries());
82347
+ const contextUsage = this.ctx.getContextUsage?.();
82348
+ const contextWindow2 = contextUsage?.contextWindow ?? model?.contextWindow ?? 0;
82349
+ const percentValue = contextUsage?.percent ?? 0;
82350
+ const percentKnown = contextUsage?.percent !== null && contextUsage?.percent !== void 0;
82351
+ const statsParts = [];
82352
+ if (totals.input) statsParts.push(`\u2191${formatCount(totals.input)}`);
82353
+ if (totals.output) statsParts.push(`\u2193${formatCount(totals.output)}`);
82354
+ if (totals.cacheRead) statsParts.push(`R${formatCount(totals.cacheRead)}`);
82355
+ if (totals.cacheWrite) statsParts.push(`W${formatCount(totals.cacheWrite)}`);
82356
+ const usingSubscription = !!(model && this.ctx.modelRegistry?.isUsingOAuth?.(this.ctx.model));
82357
+ if (totals.cost || usingSubscription) {
82358
+ statsParts.push(`$${totals.cost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`);
82359
+ }
82360
+ const autoIndicator = " (auto)";
82361
+ const contextDisplay = percentKnown ? `${percentValue.toFixed(1)}%/${formatCount(contextWindow2)}${autoIndicator}` : `?/${formatCount(contextWindow2)}${autoIndicator}`;
82362
+ const contextColored = percentValue > 90 ? theme.fg("error", contextDisplay) : percentValue > 70 ? theme.fg("warning", contextDisplay) : contextDisplay;
82363
+ statsParts.push(contextColored);
82364
+ let statsLeft = statsParts.join(" ");
82365
+ let statsLeftWidth = visibleWidth(statsLeft);
82366
+ if (statsLeftWidth > width) {
82367
+ statsLeft = truncateToWidth(statsLeft, width, "...");
82368
+ statsLeftWidth = visibleWidth(statsLeft);
82369
+ }
82370
+ const minPadding = 2;
82371
+ const modelName = model?.id || "no-model";
82372
+ let rightSideWithoutProvider = modelName;
82373
+ if (model?.reasoning) {
82374
+ let level = this.source.getThinkingLevel();
82375
+ try {
82376
+ const ctx = this.ctx.sessionManager;
82377
+ if (typeof ctx.buildSessionContext === "function") {
82378
+ const resolved = ctx.buildSessionContext()?.thinkingLevel;
82379
+ if (resolved) level = resolved;
82380
+ }
82381
+ } catch {
82382
+ }
82383
+ const finalLevel = level || "off";
82384
+ rightSideWithoutProvider = finalLevel === "off" ? `${modelName} \u2022 thinking off` : `${modelName} \u2022 ${finalLevel}`;
82385
+ }
82386
+ let rightSide = rightSideWithoutProvider;
82387
+ const providerCount = this.footerData?.getAvailableProviderCount() ?? 0;
82388
+ if (providerCount > 1 && model?.provider) {
82389
+ rightSide = `(${model.provider}) ${rightSideWithoutProvider}`;
82390
+ if (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) rightSide = rightSideWithoutProvider;
82391
+ }
82392
+ const rightSideWidth = visibleWidth(rightSide);
82393
+ let statsLine;
82394
+ if (statsLeftWidth + minPadding + rightSideWidth <= width) {
82395
+ statsLine = statsLeft + " ".repeat(width - statsLeftWidth - rightSideWidth) + rightSide;
82396
+ } else {
82397
+ const availableForRight = width - statsLeftWidth - minPadding;
82398
+ if (availableForRight > 0) {
82399
+ const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
82400
+ const padding = " ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncatedRight)));
82401
+ statsLine = statsLeft + padding + truncatedRight;
82402
+ } else {
82403
+ statsLine = statsLeft;
82404
+ }
82405
+ }
82406
+ const dimStatsLeft = theme.fg("dim", statsLeft);
82407
+ const dimRemainder = theme.fg("dim", statsLine.slice(statsLeft.length));
82408
+ return dimStatsLeft + dimRemainder;
82409
+ }
82410
+ buildStatusLine(width) {
82411
+ if (!this.footerData) return void 0;
82412
+ const statuses = this.footerData.getExtensionStatuses();
82413
+ if (statuses.size === 0) return void 0;
82414
+ const joined = Array.from(statuses.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, text]) => sanitizeStatusText(text)).join(" ");
82415
+ if (!joined) return void 0;
82416
+ return truncateToWidth(joined, width, this.theme.fg("dim", "..."));
82417
+ }
82418
+ rightAlign(text, width) {
82419
+ const w = visibleWidth(text);
82420
+ if (w >= width) return truncateToWidth(text, width, "\u2026");
82421
+ return " ".repeat(width - w) + text;
82422
+ }
82423
+ /** Capacity + provider quota. Uses the REAL render width, so the quota is
82424
+ * never chopped. When both do not fit on one line, wrap to two lines
82425
+ * (capacity above, quota right-aligned below) per the chosen behavior. */
82426
+ buildMeterLines(width) {
82427
+ const config = this.source.getConfig();
82428
+ if (!config.enabled) return [];
82429
+ const capText = config.capacity.enabled ? renderCapacity(this.theme, config.capacity, getCapacityUsage(this.ctx)) : void 0;
82430
+ const quotaText = config.capacity.providerUsage ? renderProviderUsage(this.theme, this.source.getQuotaUsage()) : void 0;
82431
+ if (!capText && !quotaText) return [];
82432
+ if (capText && !quotaText) return [truncateToWidth(capText, width, "\u2026")];
82433
+ if (!capText && quotaText) return [this.rightAlign(quotaText, width)];
82434
+ const cap = capText;
82435
+ const quota = quotaText;
82436
+ const capWidth = visibleWidth(cap);
82437
+ const quotaWidth = visibleWidth(quota);
82438
+ if (capWidth + 1 + quotaWidth <= width) {
82439
+ const pad2 = Math.max(1, width - capWidth - quotaWidth);
82440
+ return [cap + " ".repeat(pad2) + quota];
82441
+ }
82442
+ return [truncateToWidth(cap, width, "\u2026"), this.rightAlign(quota, width)];
82443
+ }
82444
+ render(width) {
82445
+ const lines = [this.buildPwdLine(width), this.buildStatsLine(width)];
82446
+ const statusLine = this.buildStatusLine(width);
82447
+ if (statusLine) lines.push(statusLine);
82448
+ lines.push(...this.buildMeterLines(width));
82449
+ return lines;
82450
+ }
82451
+ };
82452
+ function createCrewVibesFooter(deps) {
82453
+ return new CrewVibesFooter(deps);
82454
+ }
82455
+
81884
82456
  // src/extension/crew-vibes/provider-usage.ts
81885
- import { readFileSync as readFileSync76 } from "node:fs";
82457
+ import { readFileSync as readFileSync77 } from "node:fs";
81886
82458
  import { homedir as homedir12 } from "node:os";
81887
82459
  import { join as join77 } from "node:path";
81888
82460
  function withTimeout(ms, fn) {
@@ -81897,7 +82469,7 @@ function loadAnthropicToken() {
81897
82469
  const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
81898
82470
  if (envToken) return envToken;
81899
82471
  try {
81900
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82472
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81901
82473
  const token = data2.anthropic?.access;
81902
82474
  return typeof token === "string" && token.length > 0 ? token : void 0;
81903
82475
  } catch {
@@ -81908,7 +82480,7 @@ function loadZaiToken() {
81908
82480
  const envKey = process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
81909
82481
  if (envKey) return envKey;
81910
82482
  try {
81911
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82483
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81912
82484
  const key = data2["z-ai"]?.access || data2["z-ai"]?.key || data2.zai?.access || data2.zai?.key;
81913
82485
  return typeof key === "string" && key.length > 0 ? key : void 0;
81914
82486
  } catch {
@@ -81919,7 +82491,7 @@ function loadMinimaxToken() {
81919
82491
  const envKey = process.env.MINIMAX_API_KEY?.trim();
81920
82492
  if (envKey) return envKey;
81921
82493
  try {
81922
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82494
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81923
82495
  const key = data2.minimax?.key;
81924
82496
  return typeof key === "string" && key.length > 0 ? key : void 0;
81925
82497
  } catch {
@@ -81940,7 +82512,7 @@ function loadLegacyCopilotToken() {
81940
82512
  const candidates = [join77(configHome, "github-copilot", "hosts.json"), join77(homedir12(), ".github-copilot", "hosts.json")];
81941
82513
  for (const hostsPath of candidates) {
81942
82514
  try {
81943
- const data2 = JSON.parse(readFileSync76(hostsPath, "utf8"));
82515
+ const data2 = JSON.parse(readFileSync77(hostsPath, "utf8"));
81944
82516
  if (!data2 || typeof data2 !== "object") continue;
81945
82517
  const normalized = {};
81946
82518
  for (const [host, entry] of Object.entries(data2)) {
@@ -81961,7 +82533,7 @@ function loadCopilotToken() {
81961
82533
  const envToken = (process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "").trim();
81962
82534
  if (envToken) return envToken;
81963
82535
  try {
81964
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82536
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81965
82537
  const piToken = data2["github-copilot"]?.refresh || data2["github-copilot"]?.access;
81966
82538
  if (typeof piToken === "string" && piToken.length > 0) return piToken;
81967
82539
  } catch {
@@ -82135,197 +82707,6 @@ async function fetchProviderUsage(maxAgeMs = 3e5, provider) {
82135
82707
  }
82136
82708
  }
82137
82709
 
82138
- // src/extension/crew-vibes/figures.ts
82139
- var BRAILLE_FRAMES = [
82140
- "\u280B ",
82141
- // ⠋
82142
- "\u2819 ",
82143
- // ⠙
82144
- "\u2839 ",
82145
- // ⠹
82146
- "\u2838 ",
82147
- // ⠸
82148
- "\u283C ",
82149
- // ⠼
82150
- "\u2834 ",
82151
- // ⠴
82152
- "\u2826 ",
82153
- // ⠦
82154
- "\u2827 ",
82155
- // ⠧
82156
- "\u2807 ",
82157
- // ⠇
82158
- "\u280F "
82159
- // ⠏
82160
- ];
82161
- var PUA_CREW_FRAMES = [
82162
- "\uE700 ",
82163
- "\uE701 ",
82164
- "\uE702 ",
82165
- "\uE703 ",
82166
- "\uE704 ",
82167
- "\uE705 ",
82168
- "\uE706 ",
82169
- "\uE707 ",
82170
- "\uE708 ",
82171
- "\uE709 ",
82172
- "\uE70A ",
82173
- "\uE70B ",
82174
- "\uE70C ",
82175
- "\uE70D ",
82176
- "\uE70E ",
82177
- "\uE70F "
82178
- ];
82179
- function crewFrames(style = "pua") {
82180
- if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
82181
- return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
82182
- }
82183
- function intervalForSpeed(config, speed) {
82184
- if (speed === null || !Number.isFinite(speed) || speed <= 0) return config.defaultIntervalMs;
82185
- return Math.max(config.minIntervalMs, Math.min(config.maxIntervalMs, Math.round(config.scale / speed)));
82186
- }
82187
- function capacityIndex(percent, levels = 6) {
82188
- if (percent === null || percent === void 0 || !Number.isFinite(percent)) return 0;
82189
- return Math.max(0, Math.min(levels - 1, Math.floor(Math.max(0, Math.min(100, percent)) / 100 * levels)));
82190
- }
82191
- function isDangerStage(index, levels) {
82192
- return index >= Math.max(0, levels - 2);
82193
- }
82194
-
82195
- // src/extension/crew-vibes/render.ts
82196
- function formatCount(value) {
82197
- if (value < 1e3) return value.toString();
82198
- if (value < 1e4) return `${(value / 1e3).toFixed(1)}k`;
82199
- if (value < 1e6) return `${Math.round(value / 1e3)}k`;
82200
- if (value < 1e7) return `${(value / 1e6).toFixed(1)}M`;
82201
- return `${Math.round(value / 1e6)}M`;
82202
- }
82203
- function asCrewTheme2(theme) {
82204
- if (theme && typeof theme === "object" && typeof theme.fg === "function") {
82205
- return theme;
82206
- }
82207
- return void 0;
82208
- }
82209
- function getCapacityUsage(ctx) {
82210
- const fn = ctx.getContextUsage;
82211
- const usage = typeof fn === "function" ? fn.call(ctx) : null;
82212
- return {
82213
- tokens: typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : null,
82214
- percent: typeof usage?.percent === "number" && Number.isFinite(usage.percent) ? usage.percent : null
82215
- };
82216
- }
82217
- function formatSpeed(config, speed) {
82218
- return speed === null ? `-- ${config.label}` : `${speed.toFixed(1)} ${config.label}`;
82219
- }
82220
- function renderSpeedFooter(theme, config, speed) {
82221
- const value = speed === null ? "--" : speed.toFixed(1);
82222
- const valueTone = speed === null ? "dim" : "accent";
82223
- const styled = theme ? `${theme.fg(valueTone, value)} ${theme.fg("dim", config.label)}` : `${value} ${config.label}`;
82224
- return styled;
82225
- }
82226
- function renderWorkingMessage(theme, config, speed) {
82227
- const left = "Working";
82228
- const speedText = theme ? `${theme.fg(speed === null ? "dim" : "accent", speed === null ? "--" : speed.toFixed(1))} ${theme.fg("dim", config.label)}` : `${speed === null ? "--" : speed.toFixed(1)} ${config.label}`;
82229
- return theme ? `${theme.fg("muted", left)} ${speedText}` : `${left} ${speedText}`;
82230
- }
82231
- function crewIndicatorFrames(theme) {
82232
- const frames = crewFrames();
82233
- if (!theme) return [...frames];
82234
- return frames.map((frame) => theme.fg("accent", frame));
82235
- }
82236
- function formatCapacityPrefix(config, usage) {
82237
- const display = config.tokenDisplay;
82238
- if (display === "off") return "";
82239
- if (display === "percentage") {
82240
- return `${usage.percent === null ? "?" : Math.round(Math.max(0, Math.min(999, usage.percent)))}% `;
82241
- }
82242
- return `${usage.tokens === null ? "?" : formatCount(usage.tokens)} `;
82243
- }
82244
- function colorStage(theme, index, levels, text) {
82245
- if (!theme || text.length === 0) return text;
82246
- return theme.fg(isDangerStage(index, levels) ? "error" : "success", text);
82247
- }
82248
- function renderCapacity(theme, config, usage) {
82249
- const icons = capacityIcons();
82250
- const levels = icons.length;
82251
- const index = capacityIndex(usage.percent, levels);
82252
- const icon = icons[index] ?? icons[0];
82253
- const label = config.labels[index] ?? config.labels[0];
82254
- const prefix = theme ? theme.fg("muted", formatCapacityPrefix(config, usage)) : formatCapacityPrefix(config, usage);
82255
- const coloredIcon = colorStage(theme, index, levels, icon);
82256
- const afterIcon = config.showLabel ? ` ${colorStage(theme, index, levels, label)}` : " ";
82257
- return `${prefix}${coloredIcon}${afterIcon}`;
82258
- }
82259
- function setSpeedStatus(ctx, config, text) {
82260
- if (!ctx?.hasUI) return;
82261
- if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
82262
- ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82263
- return;
82264
- }
82265
- ctx.ui.setStatus(SPEED_STATUS_ID, text);
82266
- }
82267
- function setCapacityStatus(ctx, config, text) {
82268
- if (!ctx?.hasUI) return;
82269
- if (!config.enabled || !config.capacity.enabled) {
82270
- ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82271
- return;
82272
- }
82273
- ctx.ui.setStatus(CAPACITY_STATUS_ID, text);
82274
- }
82275
- function clearVibesStatus(ctx) {
82276
- if (!ctx?.hasUI) return;
82277
- ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82278
- ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82279
- ctx.ui.setStatus(PROVIDER_STATUS_ID, void 0);
82280
- if (ctx.ui.setWorkingIndicator) ctx.ui.setWorkingIndicator();
82281
- if (ctx.ui.setWorkingMessage) ctx.ui.setWorkingMessage();
82282
- }
82283
- function formatResetTimer(resetAt) {
82284
- if (!resetAt) return null;
82285
- const diffMs = new Date(resetAt).getTime() - Date.now();
82286
- if (diffMs < 0) return null;
82287
- const mins = Math.floor(diffMs / 6e4);
82288
- if (mins < 60) return `${mins}m`;
82289
- const hours = Math.floor(mins / 60);
82290
- if (hours < 48) {
82291
- const remMins = mins % 60;
82292
- return remMins > 0 ? `${hours}h${remMins}m` : `${hours}h`;
82293
- }
82294
- const days = Math.floor(hours / 24);
82295
- const remHours = hours % 24;
82296
- return remHours > 0 ? `${days}d${remHours}h` : `${days}d`;
82297
- }
82298
- function renderBar(percent, width = 8) {
82299
- const clamped = Math.max(0, Math.min(100, percent));
82300
- const filled = Math.round(clamped / 100 * width);
82301
- return `${"\u2501".repeat(filled)}${"\u2504".repeat(width - filled)}`;
82302
- }
82303
- function renderProviderUsage(theme, usage) {
82304
- if (!usage) return void 0;
82305
- const parts = [];
82306
- if (usage.providerName) {
82307
- const nameText = usage.providerName;
82308
- parts.push(theme ? theme.fg("muted", nameText) : nameText);
82309
- }
82310
- const fiveHourBar = renderBar(usage.fiveHourPercent);
82311
- const fiveHourRounded = Math.round(usage.fiveHourPercent);
82312
- const fiveHourReset = formatResetTimer(usage.fiveHourResetAt);
82313
- const fiveHourText = `5h ${fiveHourBar} ${fiveHourRounded}%${fiveHourReset ? " " + fiveHourReset : ""}`;
82314
- const fiveHourColor = usage.fiveHourPercent >= 80 ? "error" : "accent";
82315
- parts.push(theme ? theme.fg(fiveHourColor, fiveHourText) : fiveHourText);
82316
- const weeklyBar = renderBar(usage.weeklyPercent);
82317
- const weeklyRounded = Math.round(usage.weeklyPercent);
82318
- const weeklyReset = formatResetTimer(usage.weeklyResetAt);
82319
- const weeklyText = `Wk ${weeklyBar} ${weeklyRounded}%${weeklyReset ? " " + weeklyReset : ""}`;
82320
- parts.push(theme ? theme.fg("dim", weeklyText) : weeklyText);
82321
- if (typeof usage.copilotMonthlyPercent === "number" && Number.isFinite(usage.copilotMonthlyPercent)) {
82322
- const monthlyRounded = Math.round(usage.copilotMonthlyPercent);
82323
- const monthlyText = `Mo: ${monthlyRounded}%`;
82324
- parts.push(theme ? theme.fg("dim", monthlyText) : monthlyText);
82325
- }
82326
- return parts.join(" ");
82327
- }
82328
-
82329
82710
  // src/extension/crew-vibes/speed.ts
82330
82711
  var COMPACTION_THRESHOLD = 5e3;
82331
82712
  function estimateTokensFromDelta(text) {
@@ -82564,28 +82945,31 @@ function registerCrewVibes(pi) {
82564
82945
  let footerTimer;
82565
82946
  let capacityTimer;
82566
82947
  let providerTimer;
82567
- let lastProviderText;
82948
+ let lastProviderUsage = null;
82568
82949
  let currentProvider;
82569
- function visibleLen(text) {
82570
- return text.replace(/\x1b\[[0-9;]*m/g, "").length;
82571
- }
82572
- function spreadLine(left, right) {
82573
- const cols = process.stdout.columns || 120;
82574
- const padding = Math.max(2, cols - visibleLen(left) - visibleLen(right));
82575
- return left + "\xA0".repeat(padding) + right;
82576
- }
82950
+ let currentThinkingLevel;
82577
82951
  function themeOf(ctx) {
82578
82952
  return asCrewTheme2(ctx.hasUI ? ctx.ui.theme : void 0);
82579
82953
  }
82580
- function publishCapacity(ctx) {
82954
+ const footerSource = {
82955
+ getConfig: () => config,
82956
+ getQuotaUsage: () => lastProviderUsage,
82957
+ getThinkingLevel: () => currentThinkingLevel
82958
+ };
82959
+ function metersActive() {
82960
+ return config.enabled && (config.capacity.enabled || config.capacity.providerUsage);
82961
+ }
82962
+ function installFooter(ctx) {
82581
82963
  if (!ctx?.hasUI) return;
82582
- if (!config.enabled || !config.capacity.enabled) {
82583
- setCapacityStatus(ctx, config, void 0);
82964
+ if (!metersActive()) {
82965
+ setFooter(ctx, void 0);
82584
82966
  return;
82585
82967
  }
82586
- const capText = renderCapacity(themeOf(ctx), config.capacity, getCapacityUsage(ctx));
82587
- const combined = lastProviderText ? spreadLine(capText, lastProviderText) : capText;
82588
- setCapacityStatus(ctx, config, combined);
82968
+ setFooter(ctx, (tui, theme, footerData) => createCrewVibesFooter({ tui, theme, footerData, ctx, source: footerSource }));
82969
+ requestRender(ctx);
82970
+ }
82971
+ function refreshFooter(ctx) {
82972
+ if (ctx?.hasUI) requestRender(ctx);
82589
82973
  }
82590
82974
  function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
82591
82975
  if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
@@ -82660,29 +83044,28 @@ function registerCrewVibes(pi) {
82660
83044
  function startCapacityTimer(ctx) {
82661
83045
  if (capacityTimer) return;
82662
83046
  const interval = Math.max(250, config.capacity.refreshIntervalMs);
82663
- capacityTimer = setInterval(() => publishCapacity(ctx), interval);
83047
+ capacityTimer = setInterval(() => refreshFooter(ctx), interval);
82664
83048
  capacityTimer.unref?.();
82665
83049
  }
83050
+ async function fetchProviderAndRefresh(ctx) {
83051
+ if (!config.enabled || !config.capacity.providerUsage) {
83052
+ lastProviderUsage = null;
83053
+ refreshFooter(ctx);
83054
+ return;
83055
+ }
83056
+ try {
83057
+ lastProviderUsage = await fetchProviderUsage(config.capacity.providerRefreshMs, currentProvider);
83058
+ } catch {
83059
+ lastProviderUsage = null;
83060
+ }
83061
+ refreshFooter(ctx);
83062
+ }
82666
83063
  function startProviderTimer(ctx) {
82667
83064
  if (providerTimer) return;
82668
83065
  if (!config.capacity.providerUsage) return;
82669
83066
  const interval = Math.max(1e4, config.capacity.providerRefreshMs);
82670
- async function tick() {
82671
- if (!config.enabled || !config.capacity.providerUsage) {
82672
- stopProviderTimer();
82673
- return;
82674
- }
82675
- try {
82676
- const usage = await fetchProviderUsage(config.capacity.providerRefreshMs, currentProvider);
82677
- lastProviderText = renderProviderUsage(themeOf(ctx), usage);
82678
- publishCapacity(ctx);
82679
- } catch {
82680
- lastProviderText = void 0;
82681
- publishCapacity(ctx);
82682
- }
82683
- }
82684
- tick();
82685
- providerTimer = setInterval(tick, interval);
83067
+ fetchProviderAndRefresh(ctx);
83068
+ providerTimer = setInterval(() => fetchProviderAndRefresh(ctx), interval);
82686
83069
  providerTimer.unref?.();
82687
83070
  }
82688
83071
  function resetWorking(ctx) {
@@ -82698,11 +83081,13 @@ function registerCrewVibes(pi) {
82698
83081
  stopFooterTimer();
82699
83082
  stopCapacityTimer();
82700
83083
  stopProviderTimer();
83084
+ setFooter(ctx, void 0);
82701
83085
  clearVibesStatus(ctx);
82702
83086
  return;
82703
83087
  }
82704
- publishCapacity(ctx);
83088
+ installFooter(ctx);
82705
83089
  publishSpeedFooter(ctx);
83090
+ startCapacityTimer(ctx);
82706
83091
  if (config.capacity.providerUsage) startProviderTimer(ctx);
82707
83092
  }
82708
83093
  pi.on("session_start", (_event, ctx) => {
@@ -82717,11 +83102,13 @@ function registerCrewVibes(pi) {
82717
83102
  footerAnimator.reset(null);
82718
83103
  clearProviderUsageCache();
82719
83104
  currentProvider = ctx.model?.provider;
83105
+ currentThinkingLevel = void 0;
82720
83106
  if (!config.enabled) {
83107
+ setFooter(ctx, void 0);
82721
83108
  clearVibesStatus(ctx);
82722
83109
  return;
82723
83110
  }
82724
- publishCapacity(ctx);
83111
+ installFooter(ctx);
82725
83112
  publishSpeedFooter(ctx);
82726
83113
  startCapacityTimer(ctx);
82727
83114
  startProviderTimer(ctx);
@@ -82760,7 +83147,7 @@ function registerCrewVibes(pi) {
82760
83147
  });
82761
83148
  pi.on("message_end", (event, ctx) => {
82762
83149
  if (!isAssistantMessage(event.message)) return;
82763
- publishCapacity(ctx);
83150
+ refreshFooter(ctx);
82764
83151
  if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) return;
82765
83152
  const completed = speedTracker.finishMessage(assistantUsageOutput(event.message) ?? 0, assistantStopReason(event.message));
82766
83153
  if (!completed) return;
@@ -82784,15 +83171,20 @@ function registerCrewVibes(pi) {
82784
83171
  pi.on("model_select", (event, ctx) => {
82785
83172
  currentProvider = event.model?.provider;
82786
83173
  clearProviderUsageCache();
82787
- publishCapacity(ctx);
83174
+ fetchProviderAndRefresh(ctx);
83175
+ });
83176
+ pi.on("thinking_level_select", (event, ctx) => {
83177
+ currentThinkingLevel = event.level;
83178
+ refreshFooter(ctx);
82788
83179
  });
82789
- pi.on("session_compact", (_event, ctx) => publishCapacity(ctx));
82790
- pi.on("session_tree", (_event, ctx) => publishCapacity(ctx));
83180
+ pi.on("session_compact", (_event, ctx) => refreshFooter(ctx));
83181
+ pi.on("session_tree", (_event, ctx) => refreshFooter(ctx));
82791
83182
  pi.on("session_shutdown", (_event, ctx) => {
82792
83183
  stopLiveTimer();
82793
83184
  stopFooterTimer();
82794
83185
  stopCapacityTimer();
82795
83186
  stopProviderTimer();
83187
+ setFooter(ctx, void 0);
82796
83188
  clearVibesStatus(ctx);
82797
83189
  });
82798
83190
  async function handleCommand(args, ctx) {
@@ -83687,7 +84079,7 @@ function registerSubagentTools(pi, subagentManager, options = {}) {
83687
84079
  savePersistedSubagentRecord(ctx.cwd, current2);
83688
84080
  break;
83689
84081
  }
83690
- await new Promise((resolve21) => setTimeout(resolve21, 1e3));
84082
+ await new Promise((resolve22) => setTimeout(resolve22, 1e3));
83691
84083
  current2 = refreshPersistedSubagentRecord(ctx, current2);
83692
84084
  if (!current2.runId) break;
83693
84085
  }
@@ -84329,7 +84721,7 @@ Subagent may need manual intervention.`
84329
84721
  if (!loaded) return true;
84330
84722
  return !loaded.tasks.some((t2) => t2.status === "running" || t2.status === "queued");
84331
84723
  };
84332
- while (!check()) await new Promise((resolve21) => setTimeout(resolve21, 500));
84724
+ while (!check()) await new Promise((resolve22) => setTimeout(resolve22, 500));
84333
84725
  };
84334
84726
  validatedRegistry.hasRunning = async (runId) => {
84335
84727
  const manifest = manifestCacheForRegistry.get(runId);