pi-crew 0.9.26 → 0.9.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/NOTICE.md +14 -0
  3. package/assets/crew-vibes.ttf +0 -0
  4. package/assets/runner-spritesheet.png +0 -0
  5. package/dist/build-meta.json +245 -48
  6. package/dist/index.mjs +1611 -243
  7. package/dist/index.mjs.map +4 -4
  8. package/docs/perf/optimization-plan-2026-07-verified.md +396 -0
  9. package/package.json +9 -2
  10. package/scripts/bench-check.mjs +96 -0
  11. package/scripts/bench-cold-start.mjs +216 -0
  12. package/scripts/build-bundle.mjs +84 -0
  13. package/scripts/build-crew-vibes-font.py +328 -0
  14. package/scripts/check-all-skills.ts +294 -0
  15. package/scripts/check-bundle-staleness.mjs +97 -0
  16. package/scripts/check-conflict-markers.mjs +91 -0
  17. package/scripts/check-lazy-imports.mjs +28 -0
  18. package/scripts/install-crew-vibes-font.mjs +91 -0
  19. package/scripts/postinstall.mjs +47 -0
  20. package/scripts/profile-startup.mjs +125 -0
  21. package/scripts/release-smoke.mjs +74 -0
  22. package/scripts/run-bench.mjs +47 -0
  23. package/scripts/run-real-chain.ts +41 -0
  24. package/scripts/test-issue-29-crash.ts +111 -0
  25. package/scripts/test-issue-29-e2e.ts +183 -0
  26. package/scripts/test-issue-29-real-runtime.ts +330 -0
  27. package/scripts/test-issue-29-real-tasks.ts +387 -0
  28. package/scripts/test-issue-29-team-tool.ts +105 -0
  29. package/scripts/test-runner.mjs +74 -0
  30. package/scripts/verify-flicker-fix.ts +109 -0
  31. package/scripts/verify-skill.ts +550 -0
  32. package/scripts/watch-bundle.mjs +259 -0
  33. package/scripts/watchdog-harness.ts +119 -0
  34. package/src/agents/agent-config.ts +2 -0
  35. package/src/agents/discover-agents.ts +10 -1
  36. package/src/config/defaults.ts +6 -0
  37. package/src/extension/crew-vibes/cat-frames.ts +18 -0
  38. package/src/extension/crew-vibes/config.ts +195 -0
  39. package/src/extension/crew-vibes/figures.ts +94 -0
  40. package/src/extension/crew-vibes/font-detect.ts +58 -0
  41. package/src/extension/crew-vibes/index.ts +385 -0
  42. package/src/extension/crew-vibes/provider-usage.ts +396 -0
  43. package/src/extension/crew-vibes/render.ts +206 -0
  44. package/src/extension/crew-vibes/speed.ts +286 -0
  45. package/src/extension/knowledge-injection.ts +12 -3
  46. package/src/extension/management.ts +23 -3
  47. package/src/extension/register.ts +7 -0
  48. package/src/extension/team-tool.ts +11 -0
  49. package/src/prompt/prompt-runtime.ts +65 -0
  50. package/src/runtime/child-pi.ts +123 -10
  51. package/src/runtime/crew-agent-records.ts +10 -3
  52. package/src/runtime/pi-args.ts +2 -0
  53. package/src/runtime/retry-executor.ts +4 -1
  54. package/src/runtime/task-runner/state-helpers.ts +9 -30
  55. package/src/runtime/task-runner.ts +1 -0
  56. package/src/runtime/team-runner.ts +5 -3
  57. package/src/state/atomic-write.ts +153 -49
  58. package/src/state/event-log.ts +16 -10
  59. package/src/state/locks.ts +7 -8
  60. package/src/state/mailbox.ts +15 -4
  61. package/src/state/state-store.ts +39 -10
  62. package/src/teams/discover-teams.ts +56 -1
  63. package/src/utils/safe-paths.ts +2 -1
  64. package/src/workflows/discover-workflows.ts +72 -1
package/dist/index.mjs CHANGED
@@ -8693,6 +8693,7 @@ __export(atomic_write_exports, {
8693
8693
  atomicWriteJsonAsync: () => atomicWriteJsonAsync,
8694
8694
  atomicWriteJsonCoalesced: () => atomicWriteJsonCoalesced,
8695
8695
  flushPendingAtomicWrites: () => flushPendingAtomicWrites,
8696
+ invalidateSymlinkSafeCache: () => invalidateSymlinkSafeCache,
8696
8697
  isSymlinkSafePath: () => isSymlinkSafePath,
8697
8698
  readJsonFile: () => readJsonFile,
8698
8699
  renameWithRetry: () => renameWithRetry,
@@ -8774,6 +8775,32 @@ function isSymlinkSafePath(filePath) {
8774
8775
  return false;
8775
8776
  }
8776
8777
  }
8778
+ function invalidateSymlinkSafeCache(dir) {
8779
+ if (dir) symlinkSafeCache.delete(dir);
8780
+ else symlinkSafeCache.clear();
8781
+ }
8782
+ function isTargetNotSymlink(filePath) {
8783
+ try {
8784
+ if (fs2.lstatSync(filePath).isSymbolicLink()) return false;
8785
+ } catch {
8786
+ }
8787
+ return true;
8788
+ }
8789
+ function isSymlinkSafeDirCached(filePath) {
8790
+ const now = Date.now();
8791
+ const dir = path2.dirname(filePath);
8792
+ if (!isTargetNotSymlink(filePath)) return false;
8793
+ const hit = symlinkSafeCache.get(dir);
8794
+ if (hit && now - hit.at < SYMLINK_SAFE_TTL_MS) return hit.safe;
8795
+ const verdict = isSymlinkSafePath(path2.join(dir, ".symlink-safe-check"));
8796
+ symlinkSafeCache.set(dir, { safe: verdict, at: now });
8797
+ while (symlinkSafeCache.size > SYMLINK_SAFE_MAX_ENTRIES) {
8798
+ const oldest = symlinkSafeCache.keys().next().value;
8799
+ if (oldest === void 0) break;
8800
+ symlinkSafeCache.delete(oldest);
8801
+ }
8802
+ return verdict;
8803
+ }
8777
8804
  function sleep2(ms) {
8778
8805
  return new Promise((resolve21) => setTimeout(resolve21, ms));
8779
8806
  }
@@ -8889,8 +8916,19 @@ async function renameWithRetryAsync(tempPath, filePath, retries = 8, rename = (s
8889
8916
  }
8890
8917
  throw lastError;
8891
8918
  }
8892
- function atomicWriteFile(filePath, content, expectedHash) {
8893
- if (!isSymlinkSafePath(filePath)) throw new Error(`Refusing to write: target is a symlink or inside untrusted directory: ${filePath}`);
8919
+ function normalizeOptions(arg) {
8920
+ if (typeof arg === "string") return { expectedHash: arg, durability: "full" };
8921
+ if (arg && typeof arg === "object") {
8922
+ const o = arg;
8923
+ const durability = o.durability === "best-effort" ? "best-effort" : "full";
8924
+ return { expectedHash: o.expectedHash, durability };
8925
+ }
8926
+ return { durability: "full" };
8927
+ }
8928
+ function atomicWriteFile(filePath, content, options) {
8929
+ const { durability } = normalizeOptions(options);
8930
+ if (!isSymlinkSafeDirCached(filePath))
8931
+ throw new Error(`Refusing to write: target is a symlink or inside untrusted directory: ${filePath}`);
8894
8932
  const canonicalize = (p) => {
8895
8933
  try {
8896
8934
  const r = fs2.realpathSync.native(p);
@@ -8925,14 +8963,14 @@ function atomicWriteFile(filePath, content, expectedHash) {
8925
8963
  throw new Error(`Refusing to write: opened path is not a regular file: ${tempPath}`);
8926
8964
  }
8927
8965
  fs2.writeSync(fd, content, void 0, "utf-8");
8928
- fs2.fsyncSync(fd);
8966
+ if (durability === "full") fs2.fsyncSync(fd);
8929
8967
  fs2.closeSync(fd);
8930
8968
  try {
8931
- if (!isSymlinkSafePath(filePath)) {
8969
+ if (!isSymlinkSafeDirCached(filePath)) {
8932
8970
  throw new Error(`Refusing to rename: target became a symlink or inside untrusted directory: ${filePath}`);
8933
8971
  }
8934
8972
  renameWithLinkSync(tempPath, filePath);
8935
- if (process.platform !== "win32") {
8973
+ if (durability === "full" && process.platform !== "win32") {
8936
8974
  try {
8937
8975
  const dirFd = fs2.openSync(path2.dirname(filePath), "r");
8938
8976
  fs2.fsyncSync(dirFd);
@@ -8963,11 +9001,13 @@ function atomicWriteFile(filePath, content, expectedHash) {
8963
9001
  }
8964
9002
  }
8965
9003
  }
8966
- async function atomicWriteFileAsync(filePath, content) {
9004
+ async function atomicWriteFileAsync(filePath, content, options) {
9005
+ const { durability } = normalizeOptions(options);
8967
9006
  if (isWorkerAtomicWriterEnabled()) {
8968
9007
  return atomicWriteFileViaWorker(filePath, content);
8969
9008
  }
8970
- if (!isSymlinkSafePath(filePath)) throw new Error(`Refusing to write: target is a symlink or inside untrusted directory: ${filePath}`);
9009
+ if (!isSymlinkSafeDirCached(filePath))
9010
+ throw new Error(`Refusing to write: target is a symlink or inside untrusted directory: ${filePath}`);
8971
9011
  await fs2.promises.mkdir(path2.dirname(filePath), { recursive: true });
8972
9012
  const tempPath = `${filePath}.${crypto.randomUUID()}.tmp`;
8973
9013
  try {
@@ -8979,33 +9019,12 @@ async function atomicWriteFileAsync(filePath, content) {
8979
9019
  throw new Error(`Refusing to write: opened path is not a regular file: ${tempPath}`);
8980
9020
  }
8981
9021
  await fd.writeFile(content, "utf-8");
9022
+ if (durability === "full") await fd.sync();
8982
9023
  await fd.close();
8983
- try {
8984
- if (!isSymlinkSafePath(filePath)) {
8985
- try {
8986
- await fs2.promises.rm(tempPath, { force: true });
8987
- } catch {
8988
- }
8989
- throw new Error(`Refusing to rename: target became a symlink or inside untrusted directory: ${filePath}`);
8990
- }
8991
- await renameWithLinkAsync(tempPath, filePath);
8992
- } catch (renameError) {
8993
- let matches = false;
8994
- try {
8995
- const existing = await fs2.promises.readFile(filePath, "utf-8");
8996
- matches = existing === content;
8997
- } catch {
8998
- }
8999
- if (matches) {
9000
- try {
9001
- await fs2.promises.rm(tempPath, { force: true });
9002
- } catch (cleanupError) {
9003
- logInternalError("atomic-write.cleanupAsync", cleanupError, `tempPath=${tempPath}`);
9004
- }
9005
- return;
9006
- }
9007
- throw renameError;
9024
+ if (!isSymlinkSafeDirCached(filePath)) {
9025
+ throw new Error(`Refusing to rename: target became a symlink or inside untrusted directory: ${filePath}`);
9008
9026
  }
9027
+ await renameWithLinkAsync(tempPath, filePath);
9009
9028
  } catch (error) {
9010
9029
  try {
9011
9030
  await fs2.promises.rm(tempPath, { force: true });
@@ -9015,15 +9034,15 @@ async function atomicWriteFileAsync(filePath, content) {
9015
9034
  throw error;
9016
9035
  }
9017
9036
  }
9018
- function atomicWriteJson(filePath, value) {
9037
+ function atomicWriteJson(filePath, value, options) {
9019
9038
  atomicWriteFile(filePath, `${JSON.stringify(value, null, 2)}
9020
- `);
9039
+ `, options);
9021
9040
  }
9022
- async function atomicWriteJsonAsync(filePath, value) {
9041
+ async function atomicWriteJsonAsync(filePath, value, options) {
9023
9042
  await atomicWriteFileAsync(filePath, `${JSON.stringify(value, null, 2)}
9024
- `);
9043
+ `, options);
9025
9044
  }
9026
- function atomicWriteJsonCoalesced(filePath, value, coalesceMs = DEFAULT_ATOMIC_COALESCE_MS) {
9045
+ function atomicWriteJsonCoalesced(filePath, value, coalesceMs = DEFAULT_ATOMIC_COALESCE_MS, options) {
9027
9046
  const content = `${JSON.stringify(value, null, 2)}
9028
9047
  `;
9029
9048
  const previous = pendingAtomicWrites.get(filePath);
@@ -9031,12 +9050,14 @@ function atomicWriteJsonCoalesced(filePath, value, coalesceMs = DEFAULT_ATOMIC_C
9031
9050
  const timer = setTimeout(() => flushOnePendingAtomicWrite(filePath), coalesceMs);
9032
9051
  timer.unref();
9033
9052
  const generation = ++writeGeneration;
9053
+ const normalized = normalizeOptions(options);
9034
9054
  pendingAtomicWrites.set(filePath, {
9035
9055
  content,
9036
9056
  timer,
9037
9057
  coalesceMs,
9038
9058
  retryCount: 0,
9039
- generation
9059
+ generation,
9060
+ durability: normalized.durability
9040
9061
  });
9041
9062
  }
9042
9063
  function flushOnePendingAtomicWrite(filePath) {
@@ -9045,7 +9066,7 @@ function flushOnePendingAtomicWrite(filePath) {
9045
9066
  const savedGeneration = entry.generation;
9046
9067
  clearTimeout(entry.timer);
9047
9068
  try {
9048
- atomicWriteFile(filePath, entry.content);
9069
+ atomicWriteFile(filePath, entry.content, { durability: entry.durability });
9049
9070
  if (pendingAtomicWrites.get(filePath)?.generation === savedGeneration) {
9050
9071
  pendingAtomicWrites.delete(filePath);
9051
9072
  }
@@ -9090,7 +9111,7 @@ function readJsonFile(filePath) {
9090
9111
  }
9091
9112
  }
9092
9113
  }
9093
- var RETRYABLE_RENAME_CODES, RETRYABLE_LINK_CODES, __test__renameWithRetry, __test__renameWithRetryAsync, MAX_FLUSH_RETRIES, pendingAtomicWrites, DEFAULT_ATOMIC_COALESCE_MS, writeGeneration, flushInProgress;
9114
+ var RETRYABLE_RENAME_CODES, RETRYABLE_LINK_CODES, SYMLINK_SAFE_TTL_MS, SYMLINK_SAFE_MAX_ENTRIES, symlinkSafeCache, __test__renameWithRetry, __test__renameWithRetryAsync, MAX_FLUSH_RETRIES, pendingAtomicWrites, DEFAULT_ATOMIC_COALESCE_MS, writeGeneration, flushInProgress;
9094
9115
  var init_atomic_write = __esm({
9095
9116
  "src/state/atomic-write.ts"() {
9096
9117
  "use strict";
@@ -9099,6 +9120,9 @@ var init_atomic_write = __esm({
9099
9120
  init_worker_atomic_writer();
9100
9121
  RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
9101
9122
  RETRYABLE_LINK_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOENT"]);
9123
+ SYMLINK_SAFE_TTL_MS = 3e4;
9124
+ SYMLINK_SAFE_MAX_ENTRIES = 128;
9125
+ symlinkSafeCache = /* @__PURE__ */ new Map();
9102
9126
  __test__renameWithRetry = renameWithRetry;
9103
9127
  __test__renameWithRetryAsync = renameWithRetryAsync;
9104
9128
  MAX_FLUSH_RETRIES = 5;
@@ -9120,6 +9144,7 @@ var init_defaults = __esm({
9120
9144
  DEFAULT_CHILD_PI = {
9121
9145
  postExitStdioGuardMs: 3e3,
9122
9146
  finalDrainMs: 5e3,
9147
+ finalDrainQuietMs: 800,
9123
9148
  hardKillMs: 3e3,
9124
9149
  // Child workers can spend more than a few seconds in provider calls or long-running tools without emitting stdout.
9125
9150
  // Keep this as a coarse stuck-worker guard rather than a short per-message latency budget.
@@ -9325,14 +9350,10 @@ function readLockToken(filePath) {
9325
9350
  }
9326
9351
  }
9327
9352
  function timingSafeTokenMatch(a, b) {
9328
- const bufA = Buffer.from(String(a));
9329
- const bufB = Buffer.from(String(b));
9330
- const len = Math.max(bufA.length, bufB.length);
9331
- const safeA = Buffer.alloc(len);
9332
- const safeB = Buffer.alloc(len);
9333
- bufA.copy(safeA);
9334
- bufB.copy(safeB);
9335
- return timingSafeEqual(safeA, safeB);
9353
+ if (a.length !== b.length) return false;
9354
+ const bufA = Buffer.from(a);
9355
+ const bufB = Buffer.from(b);
9356
+ return timingSafeEqual(bufA, bufB);
9336
9357
  }
9337
9358
  function releaseLock(filePath, token) {
9338
9359
  let isSymlink2 = false;
@@ -11703,9 +11724,6 @@ function resetEventLogMode() {
11703
11724
  asyncQueues.clear();
11704
11725
  }
11705
11726
  async function appendEventAsync(eventsPath, event) {
11706
- if (!TERMINAL_EVENT_TYPES.has(event.type)) {
11707
- return appendEventBuffered(eventsPath, event);
11708
- }
11709
11727
  const queueKey = eventsPath;
11710
11728
  const prev = asyncQueues.get(queueKey) ?? Promise.resolve();
11711
11729
  const next = prev.then(async () => {
@@ -12014,12 +12032,14 @@ function appendEventInsideLock(eventsPath, event) {
12014
12032
  if (!skippedDueToSize) {
12015
12033
  fs8.appendFileSync(eventsPath, `${JSON.stringify(redactSecrets(fullEvent))}
12016
12034
  `, "utf-8");
12017
- const fd = fs8.openSync(eventsPath, "r+");
12018
- try {
12019
- fs8.fsyncSync(fd);
12020
- } catch {
12021
- } finally {
12022
- fs8.closeSync(fd);
12035
+ if (isTerminal) {
12036
+ const fd = fs8.openSync(eventsPath, "r+");
12037
+ try {
12038
+ fs8.fsyncSync(fd);
12039
+ } catch {
12040
+ } finally {
12041
+ fs8.closeSync(fd);
12042
+ }
12023
12043
  }
12024
12044
  persistSequence(eventsPath, seq);
12025
12045
  try {
@@ -12212,6 +12232,20 @@ var init_event_log = __esm({
12212
12232
  }
12213
12233
  asyncQueues.clear();
12214
12234
  });
12235
+ process.on("beforeExit", async () => {
12236
+ if (bufferedQueues.size > 0) {
12237
+ try {
12238
+ await flushEventLogBuffer();
12239
+ } catch {
12240
+ }
12241
+ }
12242
+ if (asyncQueues.size > 0) {
12243
+ try {
12244
+ await drainAsyncQueues();
12245
+ } catch {
12246
+ }
12247
+ }
12248
+ });
12215
12249
  process.on("SIGTERM", () => setImmediate(() => flushEventLogBuffer()));
12216
12250
  process.on("SIGINT", () => setImmediate(() => flushEventLogBuffer()));
12217
12251
  process.on("uncaughtException", (error) => {
@@ -12514,7 +12548,7 @@ function resolveRealContainedPath(baseDir, targetPath2) {
12514
12548
  if (process.platform === "darwin") {
12515
12549
  const resolvedSymlink = resolvedAccumulated;
12516
12550
  const knownDarwinSymlinks = ["/var", "/tmp", "/etc", "/private/var", "/private/tmp", "/private/etc"];
12517
- if (knownDarwinSymlinks.includes(resolvedSymlink)) continue;
12551
+ if (knownDarwinSymlinks.some((s) => resolvedSymlink === s || resolvedSymlink.startsWith(s + "/"))) continue;
12518
12552
  }
12519
12553
  throw new Error("Refusing to resolve: target path ancestor is a symlink: " + resolvedAccumulated);
12520
12554
  }
@@ -13155,6 +13189,9 @@ function statManifestWithWindowsRetry(manifestPath) {
13155
13189
  }
13156
13190
  return fs12.statSync(manifestPath);
13157
13191
  }
13192
+ function genOf(stateRoot) {
13193
+ return manifestCacheGeneration.get(stateRoot) ?? 0;
13194
+ }
13158
13195
  function __test__setManifestCache(stateRoot, entry) {
13159
13196
  setManifestCache(stateRoot, entry);
13160
13197
  }
@@ -13164,7 +13201,7 @@ function __test__getManifestCacheEntry(stateRoot) {
13164
13201
  function setManifestCache(stateRoot, entry) {
13165
13202
  if (manifestCache.has(stateRoot)) manifestCache.delete(stateRoot);
13166
13203
  entry.cachedAt = Date.now();
13167
- entry.generation = manifestCacheGeneration;
13204
+ entry.generation = genOf(stateRoot);
13168
13205
  const now = Date.now();
13169
13206
  for (const [key, val] of manifestCache.entries()) {
13170
13207
  if (val.cachedAt && now - val.cachedAt > MANIFEST_CACHE_TTL_MS) {
@@ -13191,7 +13228,7 @@ function useProjectState(cwd) {
13191
13228
  }
13192
13229
  function invalidateRunCache(stateRoot) {
13193
13230
  manifestCache.delete(stateRoot);
13194
- manifestCacheGeneration++;
13231
+ manifestCacheGeneration.set(stateRoot, genOf(stateRoot) + 1);
13195
13232
  }
13196
13233
  function scopeBaseRoot(cwd) {
13197
13234
  return useProjectState(cwd) ? projectCrewRoot(cwd) : userCrewRoot();
@@ -13332,13 +13369,24 @@ function saveRunManifest(manifest) {
13332
13369
  const manifestPath = path10.join(manifest.stateRoot, "manifest.json");
13333
13370
  atomicWriteJson(manifestPath, manifest);
13334
13371
  const manifestStat = fs12.statSync(manifestPath);
13372
+ let tasks = [];
13373
+ let tasksMtimeMs = 0;
13374
+ let tasksSize = 0;
13375
+ try {
13376
+ const tasksPath = path10.join(manifest.stateRoot, "tasks.json");
13377
+ const tasksStat = fs12.statSync(tasksPath);
13378
+ tasks = JSON.parse(fs12.readFileSync(tasksPath, "utf-8"));
13379
+ tasksMtimeMs = tasksStat.mtimeMs;
13380
+ tasksSize = tasksStat.size;
13381
+ } catch {
13382
+ }
13335
13383
  setManifestCache(manifest.stateRoot, {
13336
13384
  manifest,
13337
- tasks: [],
13385
+ tasks,
13338
13386
  manifestMtimeMs: manifestStat.mtimeMs,
13339
13387
  manifestSize: manifestStat.size,
13340
- tasksMtimeMs: 0,
13341
- tasksSize: 0
13388
+ tasksMtimeMs,
13389
+ tasksSize
13342
13390
  });
13343
13391
  }
13344
13392
  async function saveRunManifestAsync(manifest) {
@@ -13506,7 +13554,7 @@ function loadRunManifestById(cwd, runId) {
13506
13554
  tasksStat = void 0;
13507
13555
  }
13508
13556
  const tasksMtimeMs = tasksStat?.mtimeMs ?? 0;
13509
- if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation === manifestCacheGeneration) {
13557
+ if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation === genOf(stateRoot)) {
13510
13558
  if (!cached.cachedAt || Date.now() - cached.cachedAt > MANIFEST_CACHE_TTL_MS) {
13511
13559
  manifestCache.delete(stateRoot);
13512
13560
  } else if (!validateRunManifestPaths(cwd, runId, cached.manifest, stateRoot, tasksPath)) {
@@ -13569,7 +13617,7 @@ async function loadRunManifestByIdAsync(cwd, runId) {
13569
13617
  tasksStat = void 0;
13570
13618
  }
13571
13619
  const tasksMtimeMs = tasksStat?.mtimeMs ?? 0;
13572
- if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation === manifestCacheGeneration) {
13620
+ if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation === genOf(stateRoot)) {
13573
13621
  if (!cached.cachedAt || Date.now() - cached.cachedAt > MANIFEST_CACHE_TTL_MS) {
13574
13622
  manifestCache.delete(stateRoot);
13575
13623
  } else if (!validateRunManifestPaths(cwd, runId, cached.manifest, stateRoot, tasksPath)) {
@@ -13629,7 +13677,7 @@ var init_state_store = __esm({
13629
13677
  init_contracts();
13630
13678
  init_event_log();
13631
13679
  init_locks();
13632
- manifestCacheGeneration = 0;
13680
+ manifestCacheGeneration = /* @__PURE__ */ new Map();
13633
13681
  MANIFEST_CACHE_TTL_MS = 15 * 1e3;
13634
13682
  LOAD_MANIFEST_RETRY_LIMIT = 5;
13635
13683
  manifestCache = /* @__PURE__ */ new Map();
@@ -13950,18 +13998,20 @@ function upsertCrewAgent(manifest, record) {
13950
13998
  }
13951
13999
  function writeCrewAgentStatus(manifest, record) {
13952
14000
  ensureAgentStateDir(manifest, record.taskId);
13953
- atomicWriteJson(agentStatusPath(manifest, record.taskId), redactSecrets(record));
14001
+ atomicWriteJson(agentStatusPath(manifest, record.taskId), redactSecrets(record), { durability: "full" });
13954
14002
  }
13955
14003
  function saveCrewAgentsCoalesced(manifest, records) {
13956
14004
  const filePath = agentsPath(manifest);
13957
14005
  fs14.mkdirSync(manifest.stateRoot, { recursive: true });
13958
- atomicWriteJsonCoalesced(filePath, redactSecrets(records), AGENT_COALESCE_MS);
14006
+ atomicWriteJsonCoalesced(filePath, redactSecrets(records), AGENT_COALESCE_MS, { durability: "best-effort" });
13959
14007
  asyncAgentReaderCache.delete(filePath);
13960
14008
  for (const record of records) writeCrewAgentStatusCoalesced(manifest, record);
13961
14009
  }
13962
14010
  function writeCrewAgentStatusCoalesced(manifest, record) {
13963
14011
  ensureAgentStateDir(manifest, record.taskId);
13964
- atomicWriteJsonCoalesced(agentStatusPath(manifest, record.taskId), redactSecrets(record), AGENT_COALESCE_MS);
14012
+ atomicWriteJsonCoalesced(agentStatusPath(manifest, record.taskId), redactSecrets(record), AGENT_COALESCE_MS, {
14013
+ durability: "best-effort"
14014
+ });
13965
14015
  }
13966
14016
  function readCrewAgentStatus(manifest, taskOrAgentId) {
13967
14017
  try {
@@ -15549,12 +15599,12 @@ function getProcessStartTime2(pid) {
15549
15599
  } catch {
15550
15600
  return void 0;
15551
15601
  }
15552
- const platform = process.platform;
15553
- if (platform === "linux") {
15602
+ const platform2 = process.platform;
15603
+ if (platform2 === "linux") {
15554
15604
  return getProcessStartTimeLinux(pid);
15555
- } else if (platform === "darwin") {
15605
+ } else if (platform2 === "darwin") {
15556
15606
  return getProcessStartTimeMacOS(pid);
15557
- } else if (platform === "win32") {
15607
+ } else if (platform2 === "win32") {
15558
15608
  return getProcessStartTimeWindows(pid);
15559
15609
  }
15560
15610
  return void 0;
@@ -16440,7 +16490,9 @@ function buildPiWorkerArgs(input) {
16440
16490
  PI_TEAMS_INHERIT_SKILLS: input.agent.inheritSkills ? "1" : "0",
16441
16491
  PI_TEAMS_DEPTH: String(parentDepth + 1),
16442
16492
  PI_TEAMS_MAX_DEPTH: String(maxDepth),
16443
- PI_TEAMS_ROLE: input.agent.name
16493
+ PI_TEAMS_ROLE: input.agent.name,
16494
+ // maxTokens cap for background workers — prompt-runtime reads this to cap API output
16495
+ ...input.agent.maxTokens ? { PI_CREW_MAX_OUTPUT: String(input.agent.maxTokens) } : {}
16444
16496
  },
16445
16497
  tempDir
16446
16498
  };
@@ -18663,6 +18715,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18663
18715
  skillPaths: input.skillPaths,
18664
18716
  role: input.role
18665
18717
  });
18718
+ if (input.steeringFile) built.env.PI_CREW_STEERING_FILE = input.steeringFile;
18666
18719
  const spawnSpec = getPiSpawnCommand(built.args);
18667
18720
  try {
18668
18721
  return await new Promise((resolve21) => {
@@ -18714,6 +18767,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18714
18767
  const finalDrainMs = input.finalDrainMs ?? FINAL_DRAIN_MS;
18715
18768
  const hardKillMs = input.hardKillMs ?? HARD_KILL_MS;
18716
18769
  let finalDrainArmed = false;
18770
+ let lastStdoutActivityMonotonicMs = performance.now();
18717
18771
  let finalDrainFiredMonotonicMs;
18718
18772
  const spawnMonotonicMs = performance.now();
18719
18773
  let finalAssistantEventMonotonicMs;
@@ -18872,10 +18926,59 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18872
18926
  completeOperation(eventOpId);
18873
18927
  throw err2;
18874
18928
  }
18929
+ lastStdoutActivityMonotonicMs = performance.now();
18875
18930
  input.onJsonEvent?.(event);
18876
18931
  if (!isFinalAssistantEvent(event) || childExited || settled || finalDrainTimer) return;
18877
18932
  finalAssistantEventMonotonicMs = performance.now();
18878
18933
  finalDrainArmed = true;
18934
+ const quietMs = input.finalDrainQuietMs ?? DEFAULT_CHILD_PI.finalDrainQuietMs;
18935
+ if (quietMs < (input.finalDrainMs ?? DEFAULT_CHILD_PI.finalDrainMs)) {
18936
+ const pollHandle = setInterval(() => {
18937
+ if (settled || childExited) {
18938
+ clearInterval(pollHandle);
18939
+ pollHandle.unref();
18940
+ return;
18941
+ }
18942
+ const sinceLast = performance.now() - lastStdoutActivityMonotonicMs;
18943
+ if (sinceLast >= quietMs) {
18944
+ clearInterval(pollHandle);
18945
+ pollHandle.unref();
18946
+ forcedFinalDrain = true;
18947
+ finalDrainFiredMonotonicMs = performance.now();
18948
+ input.onLifecycleEvent?.({
18949
+ type: "final_drain",
18950
+ pid: child.pid,
18951
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
18952
+ reason: "stdout-quiet"
18953
+ });
18954
+ try {
18955
+ child.kill(process.platform === "win32" ? void 0 : "SIGTERM");
18956
+ } catch (error) {
18957
+ logInternalError("child-pi.quiet-drain-term", error, `pid=${child.pid}`);
18958
+ }
18959
+ hardKillTimer = setTimeout(() => {
18960
+ if (settled || childExited) return;
18961
+ try {
18962
+ hardKilled = true;
18963
+ input.onLifecycleEvent?.({
18964
+ type: "hard_kill",
18965
+ pid: child.pid,
18966
+ ts: (/* @__PURE__ */ new Date()).toISOString()
18967
+ });
18968
+ child.kill(process.platform === "win32" ? void 0 : "SIGKILL");
18969
+ } catch (error) {
18970
+ logInternalError("child-pi.quiet-drain-hard-kill", error, `pid=${child.pid}`);
18971
+ }
18972
+ }, hardKillMs);
18973
+ hardKillTimer.unref();
18974
+ if (finalDrainTimer) {
18975
+ clearTimeout(finalDrainTimer);
18976
+ finalDrainTimer = void 0;
18977
+ }
18978
+ }
18979
+ }, 200);
18980
+ pollHandle.unref();
18981
+ }
18879
18982
  finalDrainTimer = setTimeout(() => {
18880
18983
  if (settled || childExited) return;
18881
18984
  forcedFinalDrain = true;
@@ -19199,6 +19302,15 @@ var init_child_pi = __esm({
19199
19302
  MAX_COMPACT_CONTENT_CHARS = DEFAULT_CHILD_PI.maxCompactContentChars;
19200
19303
  activeChildProcesses = /* @__PURE__ */ new Map();
19201
19304
  childHardKillTimers = /* @__PURE__ */ new Map();
19305
+ setInterval(() => {
19306
+ for (const [pid, child] of activeChildProcesses) {
19307
+ try {
19308
+ process.kill(pid, 0);
19309
+ } catch {
19310
+ activeChildProcesses.delete(pid);
19311
+ }
19312
+ }
19313
+ }, 6e4).unref();
19202
19314
  BASE_ALLOWLIST = [
19203
19315
  "PATH",
19204
19316
  "HOME",
@@ -19239,22 +19351,29 @@ var init_child_pi = __esm({
19239
19351
  "PI_TEAMS_INHERIT_SKILLS",
19240
19352
  "PI_TEAMS_PI_BIN",
19241
19353
  "PI_TEAMS_MOCK_CHILD_PI",
19242
- "PI_CREW_ALLOW_MOCK"
19354
+ "PI_CREW_ALLOW_MOCK",
19355
+ "PI_CREW_MAX_OUTPUT",
19356
+ "PI_CREW_STEERING_FILE"
19243
19357
  ];
19244
- ChildPiLineObserver = class {
19358
+ ChildPiLineObserver = class _ChildPiLineObserver {
19245
19359
  buffer = "";
19246
19360
  input;
19247
- /** RAW (uncapped) assistant-text fragments, accumulated in arrival order.
19248
- * Mirrors {@link parsePiJsonOutput}'s textEvents/finalText extraction but
19249
- * operates on the RAW event stream instead of the 16K-compacted transcript.
19250
- * This is the source of the AUTHORITATIVE result.txt; the transcript stays
19251
- * compacted (memory bound). */
19361
+ /** F9: bounded ring buffer for RAW assistant-text fragments. Consumers
19362
+ * (getRawFinalText) only read the last element, but the legacy implementation
19363
+ * accumulated every fragment unconditionally, which let a verbose/long-running
19364
+ * worker grow this array linearly with output. We retain the last 2 entries:
19365
+ * the consumer needs the last; we keep the second-to-last only as a defensive
19366
+ * fence against a race where a final event arrives just after the consumer
19367
+ * read (the previous "last" is still the most-recent pre-final text in that
19368
+ * window). 2 is well below any plausible consumer's "tail-only" need while
19369
+ * bounding memory. */
19370
+ static MAX_RAW_TEXT_EVENTS = 2;
19252
19371
  rawTextEvents = [];
19253
- /** #7 hardening: bounded digest of intermediate findings. When a worker spends
19254
- * its entire budget on tool calls (never emits a final assistant text),
19255
- * getRawFinalText() returns undefined but this digest captures the last
19256
- * display lines (tool results, stdout fragments) before budget exhaustion.
19257
- * Capped at MAX_INTERMEDIATE_DIGEST_LINES so result artifacts stay bounded. */
19372
+ /** F9: bounded ring buffer for intermediate findings. The downstream digest
19373
+ * (getIntermediateFindings) slices the last 20, but the array previously grew
19374
+ * to 1000s of entries. We keep MAX_INTERMEDIATE_DIGEST_LINES + headroom so
19375
+ * the public API behaviour is preserved (still returns "last 20 lines"). */
19376
+ static MAX_INTERMEDIATE_FINDINGS = 32;
19258
19377
  intermediateFindings = [];
19259
19378
  constructor(input) {
19260
19379
  this.input = input;
@@ -19298,9 +19417,13 @@ var init_child_pi = __esm({
19298
19417
  const rawTexts = extractText(rawParsed);
19299
19418
  if (rawTexts.length > 0) {
19300
19419
  this.rawTextEvents.push(...rawTexts);
19420
+ const rawOverflow = this.rawTextEvents.length - _ChildPiLineObserver.MAX_RAW_TEXT_EVENTS;
19421
+ if (rawOverflow > 0) this.rawTextEvents.splice(0, rawOverflow);
19301
19422
  const last = rawTexts[rawTexts.length - 1];
19302
19423
  if (last.trim().length > 0) {
19303
19424
  this.intermediateFindings.push(last.trim());
19425
+ const findingsOverflow = this.intermediateFindings.length - _ChildPiLineObserver.MAX_INTERMEDIATE_FINDINGS;
19426
+ if (findingsOverflow > 0) this.intermediateFindings.splice(0, findingsOverflow);
19304
19427
  }
19305
19428
  }
19306
19429
  } catch {
@@ -19321,6 +19444,8 @@ var init_child_pi = __esm({
19321
19444
  logInternalError("child-pi.on-stdout-line", error, `line=${compact.displayLine}`);
19322
19445
  }
19323
19446
  this.intermediateFindings.push(compact.displayLine.trim());
19447
+ const findingsOverflow = this.intermediateFindings.length - _ChildPiLineObserver.MAX_INTERMEDIATE_FINDINGS;
19448
+ if (findingsOverflow > 0) this.intermediateFindings.splice(0, findingsOverflow);
19324
19449
  }
19325
19450
  }
19326
19451
  };
@@ -19970,15 +20095,56 @@ function parseDynamicWorkflowFile(filePath, source) {
19970
20095
  return void 0;
19971
20096
  }
19972
20097
  }
20098
+ function workflowDirStamp(cwd) {
20099
+ const dirs = [
20100
+ path26.join(packageRoot(), "workflows"),
20101
+ path26.join(userPiRoot(), "workflows"),
20102
+ path26.join(projectCrewRoot(cwd), "workflows")
20103
+ ];
20104
+ let out = "";
20105
+ for (const d of dirs) {
20106
+ try {
20107
+ const st = fs30.statSync(d);
20108
+ out += `${st.mtimeMs}|`;
20109
+ } catch {
20110
+ out += "0|";
20111
+ }
20112
+ }
20113
+ return out;
20114
+ }
20115
+ function invalidateWorkflowDiscoveryCache(cwd) {
20116
+ if (cwd) {
20117
+ workflowCache.delete(cwd);
20118
+ } else {
20119
+ workflowCache.clear();
20120
+ }
20121
+ }
19973
20122
  function discoverWorkflows(cwd) {
19974
20123
  if (!cwd || typeof cwd !== "string") {
19975
20124
  return { builtin: [], user: [], project: [] };
19976
20125
  }
19977
- return {
20126
+ const now = Date.now();
20127
+ const stamp = workflowDirStamp(cwd);
20128
+ const cached = workflowCache.get(cwd);
20129
+ if (cached && cached.expiresAt > now && cached.dirStamp === stamp) {
20130
+ return cached.result;
20131
+ }
20132
+ const result4 = {
19978
20133
  builtin: readWorkflowDir(path26.join(packageRoot(), "workflows"), "builtin"),
19979
20134
  user: readWorkflowDir(path26.join(userPiRoot(), "workflows"), "user"),
19980
20135
  project: readWorkflowDir(path26.join(projectCrewRoot(cwd), "workflows"), "project")
19981
20136
  };
20137
+ workflowCache.set(cwd, {
20138
+ result: result4,
20139
+ expiresAt: now + WORKFLOW_DISCOVERY_TTL_MS,
20140
+ dirStamp: stamp
20141
+ });
20142
+ while (workflowCache.size > WORKFLOW_DISCOVERY_MAX_ENTRIES) {
20143
+ const oldest = workflowCache.keys().next().value;
20144
+ if (oldest === void 0) break;
20145
+ workflowCache.delete(oldest);
20146
+ }
20147
+ return result4;
19982
20148
  }
19983
20149
  function allWorkflows(discovery) {
19984
20150
  if (!discovery) return [];
@@ -19988,7 +20154,7 @@ function allWorkflows(discovery) {
19988
20154
  }
19989
20155
  return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
19990
20156
  }
19991
- var STEP_CONFIG_KEYS, parseOptionalInteger, parseOptionalBoolean, parseTopology;
20157
+ var STEP_CONFIG_KEYS, parseOptionalInteger, parseOptionalBoolean, parseTopology, WORKFLOW_DISCOVERY_TTL_MS, WORKFLOW_DISCOVERY_MAX_ENTRIES, workflowCache;
19992
20158
  var init_discover_workflows = __esm({
19993
20159
  "src/workflows/discover-workflows.ts"() {
19994
20160
  "use strict";
@@ -20033,6 +20199,9 @@ var init_discover_workflows = __esm({
20033
20199
  }
20034
20200
  return void 0;
20035
20201
  };
20202
+ WORKFLOW_DISCOVERY_TTL_MS = 5e3;
20203
+ WORKFLOW_DISCOVERY_MAX_ENTRIES = 32;
20204
+ workflowCache = /* @__PURE__ */ new Map();
20036
20205
  }
20037
20206
  });
20038
20207
 
@@ -21993,6 +22162,10 @@ function parseAgentFile(filePath, source) {
21993
22162
  const n = Number.parseInt(frontmatter.maxTurns, 10);
21994
22163
  return Number.isFinite(n) && n > 0 ? n : void 0;
21995
22164
  })(),
22165
+ maxTokens: (() => {
22166
+ const n = Number.parseInt(frontmatter.maxTokens, 10);
22167
+ return Number.isFinite(n) && n > 0 ? n : void 0;
22168
+ })(),
21996
22169
  effort: frontmatter.effort === "low" || frontmatter.effort === "medium" || frontmatter.effort === "high" ? frontmatter.effort : void 0,
21997
22170
  disabled: frontmatter.disabled === "true" || frontmatter.enabled === "false",
21998
22171
  routing: triggers || useWhen || avoidWhen || cost || category ? { triggers, useWhen, avoidWhen, cost, category } : void 0
@@ -22165,7 +22338,7 @@ var init_discover_agents = __esm({
22165
22338
  MAX_SECURITY_LOG_ENTRIES = 1e3;
22166
22339
  securityEventLog = [];
22167
22340
  warnedForkAgents = /* @__PURE__ */ new Set();
22168
- DISCOVERY_CACHE_TTL_MS = 500;
22341
+ DISCOVERY_CACHE_TTL_MS = 5e3;
22169
22342
  discoveryCache = /* @__PURE__ */ new Map();
22170
22343
  DISCOVERY_CACHE_MAX_ENTRIES = 32;
22171
22344
  dynamicAgents = /* @__PURE__ */ new Map();
@@ -22362,7 +22535,8 @@ var init_git = __esm({
22362
22535
  var discover_teams_exports = {};
22363
22536
  __export(discover_teams_exports, {
22364
22537
  allTeams: () => allTeams,
22365
- discoverTeams: () => discoverTeams
22538
+ discoverTeams: () => discoverTeams,
22539
+ invalidateTeamDiscoveryCache: () => invalidateTeamDiscoveryCache
22366
22540
  });
22367
22541
  import * as fs34 from "node:fs";
22368
22542
  import * as path29 from "node:path";
@@ -22446,12 +22620,48 @@ function readTeamDir(dir, source) {
22446
22620
  if (!fs34.existsSync(dir)) return [];
22447
22621
  return fs34.readdirSync(dir).filter((entry) => entry.endsWith(".team.md")).map((entry) => parseTeamFile(path29.join(dir, entry), source)).filter((team) => team !== void 0).sort((a, b) => a.name.localeCompare(b.name));
22448
22622
  }
22623
+ function teamDirStamp(cwd) {
22624
+ const dirs = [path29.join(packageRoot(), "teams"), path29.join(userPiRoot(), "teams"), path29.join(projectCrewRoot(cwd), "teams")];
22625
+ let out = "";
22626
+ for (const d of dirs) {
22627
+ try {
22628
+ out += `${fs34.statSync(d).mtimeMs}|`;
22629
+ } catch {
22630
+ out += "0|";
22631
+ }
22632
+ }
22633
+ return out;
22634
+ }
22635
+ function invalidateTeamDiscoveryCache(cwd) {
22636
+ if (cwd) {
22637
+ teamCache.delete(cwd);
22638
+ } else {
22639
+ teamCache.clear();
22640
+ }
22641
+ }
22449
22642
  function discoverTeams(cwd) {
22450
- return {
22643
+ const now = Date.now();
22644
+ const stamp = teamDirStamp(cwd);
22645
+ const cached = teamCache.get(cwd);
22646
+ if (cached && cached.expiresAt > now && cached.dirStamp === stamp) {
22647
+ return cached.result;
22648
+ }
22649
+ const result4 = {
22451
22650
  builtin: readTeamDir(path29.join(packageRoot(), "teams"), "builtin"),
22452
22651
  user: readTeamDir(path29.join(userPiRoot(), "teams"), "user"),
22453
22652
  project: readTeamDir(path29.join(projectCrewRoot(cwd), "teams"), "project")
22454
22653
  };
22654
+ teamCache.set(cwd, {
22655
+ result: result4,
22656
+ expiresAt: now + TEAM_DISCOVERY_TTL_MS,
22657
+ dirStamp: stamp
22658
+ });
22659
+ while (teamCache.size > TEAM_DISCOVERY_MAX_ENTRIES) {
22660
+ const oldest = teamCache.keys().next().value;
22661
+ if (oldest === void 0) break;
22662
+ teamCache.delete(oldest);
22663
+ }
22664
+ return result4;
22455
22665
  }
22456
22666
  function allTeams(discovery) {
22457
22667
  if (!discovery) return [];
@@ -22461,12 +22671,16 @@ function allTeams(discovery) {
22461
22671
  }
22462
22672
  return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
22463
22673
  }
22674
+ var TEAM_DISCOVERY_TTL_MS, TEAM_DISCOVERY_MAX_ENTRIES, teamCache;
22464
22675
  var init_discover_teams = __esm({
22465
22676
  "src/teams/discover-teams.ts"() {
22466
22677
  "use strict";
22467
22678
  init_frontmatter();
22468
22679
  init_git();
22469
22680
  init_paths();
22681
+ TEAM_DISCOVERY_TTL_MS = 5e3;
22682
+ TEAM_DISCOVERY_MAX_ENTRIES = 32;
22683
+ teamCache = /* @__PURE__ */ new Map();
22470
22684
  }
22471
22685
  });
22472
22686
 
@@ -23615,7 +23829,7 @@ function readDeliveryState(manifest) {
23615
23829
  return { messages: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
23616
23830
  }
23617
23831
  }
23618
- function writeDeliveryState(manifest, state) {
23832
+ function writeDeliveryState(manifest, state, options) {
23619
23833
  ensureRunMailbox(manifest);
23620
23834
  const MAX_DELIVERY_MESSAGES = 1e4;
23621
23835
  if (Object.keys(state.messages).length > MAX_DELIVERY_MESSAGES) {
@@ -23627,7 +23841,9 @@ function writeDeliveryState(manifest, state) {
23627
23841
  state.messages = Object.fromEntries(trimmed);
23628
23842
  }
23629
23843
  atomicWriteFile(deliveryFile(manifest, true), `${JSON.stringify(redactSecrets(state), null, 2)}
23630
- `);
23844
+ `, {
23845
+ durability: options?.durability ?? "best-effort"
23846
+ });
23631
23847
  }
23632
23848
  function appendMailboxMessage(manifest, message) {
23633
23849
  if (message.taskId) ensureTaskMailbox(manifest, message.taskId);
@@ -23666,7 +23882,7 @@ function appendMailboxMessage(manifest, message) {
23666
23882
  const delivery = readDeliveryState(manifest);
23667
23883
  delivery.messages[complete.id] = complete.status;
23668
23884
  delivery.updatedAt = createdAt;
23669
- writeDeliveryState(manifest, delivery);
23885
+ writeDeliveryState(manifest, delivery, { durability: "full" });
23670
23886
  });
23671
23887
  return complete;
23672
23888
  }
@@ -23710,7 +23926,7 @@ function acknowledgeMailboxMessage(manifest, messageId) {
23710
23926
  if (!delivery.messages[messageId]) throw new Error(`Mailbox message '${messageId}' not found.`);
23711
23927
  delivery.messages[messageId] = "acknowledged";
23712
23928
  delivery.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
23713
- writeDeliveryState(manifest, delivery);
23929
+ writeDeliveryState(manifest, delivery, { durability: "full" });
23714
23930
  return delivery;
23715
23931
  });
23716
23932
  }
@@ -24164,6 +24380,11 @@ var init_intent_policy = __esm({
24164
24380
  import * as crypto3 from "node:crypto";
24165
24381
  import * as fs38 from "node:fs";
24166
24382
  import * as path33 from "node:path";
24383
+ function invalidateResourceCaches() {
24384
+ invalidateAgentDiscoveryCache();
24385
+ invalidateTeamDiscoveryCache();
24386
+ invalidateWorkflowDiscoveryCache();
24387
+ }
24167
24388
  function result2(text, status = "ok", isError = false) {
24168
24389
  return toolResult(text, { action: "management", status }, isError);
24169
24390
  }
@@ -24461,6 +24682,7 @@ ${content}`);
24461
24682
  true
24462
24683
  );
24463
24684
  }
24685
+ invalidateResourceCaches();
24464
24686
  return result2(`Created ${params.resource} '${name}' at ${filePath}.`);
24465
24687
  }
24466
24688
  function handleUpdate(params, ctx) {
@@ -24565,6 +24787,7 @@ function handleUpdate(params, ctx) {
24565
24787
  );
24566
24788
  }
24567
24789
  const updatedRefs = params.updateReferences ? updateReferencesForRename(ctx, params.resource, current2.name, nextName, source, false) : [];
24790
+ invalidateResourceCaches();
24568
24791
  return result2(
24569
24792
  [
24570
24793
  `Updated ${params.resource} at ${nextPath}. Backup: ${backupPath}.`,
@@ -24603,6 +24826,7 @@ ${refs.map((ref2) => `- ${ref2}`).join("\n")}` : ""}`
24603
24826
  true
24604
24827
  );
24605
24828
  }
24829
+ invalidateResourceCaches();
24606
24830
  return result2(`Deleted ${params.resource} at ${resolved.resource.filePath}. Backup: ${backupPath}.`);
24607
24831
  }
24608
24832
  var init_management = __esm({
@@ -24658,18 +24882,18 @@ function initializeProject(cwd, options = {}) {
24658
24882
  const teamsDir = path34.join(crewRoot, "teams");
24659
24883
  const workflowsDir = path34.join(crewRoot, "workflows");
24660
24884
  const configScope = options.configScope ?? "global";
24661
- const configPath2 = configScope === "project" ? path34.join(projectPiRoot(cwd), "pi-crew.json") : configScope === "global" ? configPath() : "";
24885
+ const configPath3 = configScope === "project" ? path34.join(projectPiRoot(cwd), "pi-crew.json") : configScope === "global" ? configPath() : "";
24662
24886
  ensureDir(agentsDir, createdDirs);
24663
24887
  ensureDir(teamsDir, createdDirs);
24664
24888
  ensureDir(workflowsDir, createdDirs);
24665
24889
  ensureDir(path34.join(crewRoot, "imports"), createdDirs);
24666
24890
  let configCreated = false;
24667
24891
  let configSkipped = false;
24668
- if (configPath2) {
24669
- if (configScope === "project") ensureDir(path34.dirname(configPath2), createdDirs);
24670
- else fs39.mkdirSync(path34.dirname(configPath2), { recursive: true });
24671
- if (!fs39.existsSync(configPath2) || options.overwrite === true) {
24672
- fs39.writeFileSync(configPath2, `${JSON.stringify(DEFAULT_PI_CREW_CONFIG, null, 2)}
24892
+ if (configPath3) {
24893
+ if (configScope === "project") ensureDir(path34.dirname(configPath3), createdDirs);
24894
+ else fs39.mkdirSync(path34.dirname(configPath3), { recursive: true });
24895
+ if (!fs39.existsSync(configPath3) || options.overwrite === true) {
24896
+ fs39.writeFileSync(configPath3, `${JSON.stringify(DEFAULT_PI_CREW_CONFIG, null, 2)}
24673
24897
  `, "utf-8");
24674
24898
  configCreated = true;
24675
24899
  } else {
@@ -24708,7 +24932,7 @@ ${missing.join("\n")}
24708
24932
  skippedFiles,
24709
24933
  gitignorePath,
24710
24934
  gitignoreUpdated,
24711
- configPath: configPath2,
24935
+ configPath: configPath3,
24712
24936
  configScope,
24713
24937
  configCreated,
24714
24938
  configSkipped
@@ -44086,7 +44310,7 @@ var init_crew_hooks = __esm({
44086
44310
  });
44087
44311
 
44088
44312
  // src/runtime/skill-effectiveness.ts
44089
- import { existsSync as existsSync38, mkdirSync as mkdirSync26, readFileSync as readFileSync40, writeFileSync as writeFileSync18 } from "fs";
44313
+ import { existsSync as existsSync38, mkdirSync as mkdirSync26, readFileSync as readFileSync41, writeFileSync as writeFileSync18 } from "fs";
44090
44314
  import { dirname as dirname24, join as join43 } from "path";
44091
44315
  function getSkillMetricsPath(cwd, runId) {
44092
44316
  return join43(projectCrewRoot(cwd), `state/runs/${runId}/skill-metrics.jsonl`);
@@ -44149,7 +44373,7 @@ function getSkillActivations(cwd, runId) {
44149
44373
  if (!existsSync38(path81)) {
44150
44374
  return [];
44151
44375
  }
44152
- const content = readFileSync40(path81, "utf-8");
44376
+ const content = readFileSync41(path81, "utf-8");
44153
44377
  if (!content.trim()) {
44154
44378
  return [];
44155
44379
  }
@@ -47311,7 +47535,7 @@ var init_explain = __esm({
47311
47535
  });
47312
47536
 
47313
47537
  // src/runtime/goal-state-store.ts
47314
- import { closeSync as closeSync13, existsSync as existsSync45, mkdirSync as mkdirSync29, openSync as openSync13, readdirSync as readdirSync23, readFileSync as readFileSync46, statSync as statSync29, unlinkSync as unlinkSync9 } from "node:fs";
47538
+ import { closeSync as closeSync13, existsSync as existsSync45, mkdirSync as mkdirSync29, openSync as openSync13, readdirSync as readdirSync23, readFileSync as readFileSync47, statSync as statSync31, unlinkSync as unlinkSync9 } from "node:fs";
47315
47539
  import { dirname as dirname26 } from "node:path";
47316
47540
  function resolveGoalsRoot(cwd) {
47317
47541
  const crewRoot = projectCrewRoot(cwd) ?? userCrewRoot();
@@ -47345,7 +47569,7 @@ var init_goal_state_store = __esm({
47345
47569
  const path81 = goalFilePath(this.cwd, goalId);
47346
47570
  try {
47347
47571
  if (!existsSync45(path81)) return void 0;
47348
- const raw = readFileSync46(path81, "utf-8");
47572
+ const raw = readFileSync47(path81, "utf-8");
47349
47573
  const parsed = JSON.parse(raw);
47350
47574
  if (!parsed || typeof parsed !== "object" || typeof parsed.goalId !== "string") return void 0;
47351
47575
  return parsed;
@@ -47442,7 +47666,7 @@ var init_goal_state_store = __esm({
47442
47666
  const code = error.code;
47443
47667
  if (code !== "EEXIST") return false;
47444
47668
  try {
47445
- const stat2 = statSync29(lockPath2);
47669
+ const stat2 = statSync31(lockPath2);
47446
47670
  if (Date.now() - stat2.mtimeMs > 5e3) {
47447
47671
  unlinkSync9(lockPath2);
47448
47672
  const fd = openSync13(lockPath2, "wx");
@@ -47533,7 +47757,7 @@ var init_verification_integrity = __esm({
47533
47757
 
47534
47758
  // src/runtime/workspace-lock.ts
47535
47759
  import { createHash as createHash7 } from "node:crypto";
47536
- import { closeSync as closeSync14, existsSync as existsSync46, mkdirSync as mkdirSync30, openSync as openSync14, readdirSync as readdirSync24, readFileSync as readFileSync48, statSync as statSync31, unlinkSync as unlinkSync10, writeFileSync as writeFileSync23 } from "node:fs";
47760
+ import { closeSync as closeSync14, existsSync as existsSync46, mkdirSync as mkdirSync30, openSync as openSync14, readdirSync as readdirSync24, readFileSync as readFileSync49, statSync as statSync33, unlinkSync as unlinkSync10, writeFileSync as writeFileSync23 } from "node:fs";
47537
47761
  import * as path51 from "node:path";
47538
47762
  function workspaceLockPath(cwd) {
47539
47763
  const absCwd = path51.resolve(cwd);
@@ -47545,7 +47769,7 @@ function workspaceLockPath(cwd) {
47545
47769
  function readLock(lockPath2) {
47546
47770
  if (!existsSync46(lockPath2)) return void 0;
47547
47771
  try {
47548
- const parsed = JSON.parse(readFileSync48(lockPath2, "utf-8"));
47772
+ const parsed = JSON.parse(readFileSync49(lockPath2, "utf-8"));
47549
47773
  if (!parsed || typeof parsed !== "object") return void 0;
47550
47774
  return parsed;
47551
47775
  } catch {
@@ -47584,7 +47808,7 @@ var init_workspace_lock = __esm({
47584
47808
  DEFAULT_HEARTBEAT_STALE_MS = 6e4;
47585
47809
  defaultStartTimeResolver = (pid) => {
47586
47810
  try {
47587
- const stat2 = readFileSync48(`/proc/${pid}/stat`, "utf-8");
47811
+ const stat2 = readFileSync49(`/proc/${pid}/stat`, "utf-8");
47588
47812
  const lastParen = stat2.lastIndexOf(")");
47589
47813
  if (lastParen === -1) return void 0;
47590
47814
  const fieldsAfterComm = stat2.slice(lastParen + 1).trim().split(/\s+/);
@@ -51508,7 +51732,7 @@ var init_tool_progress_formatter = __esm({
51508
51732
  });
51509
51733
 
51510
51734
  // src/extension/registration/team-tool.ts
51511
- import { statSync as statSync34 } from "node:fs";
51735
+ import { statSync as statSync36 } from "node:fs";
51512
51736
  import { Text as Text4 } from "@earendil-works/pi-tui";
51513
51737
  async function handleTeamTool(params, ctx) {
51514
51738
  if (!_cachedHandleTeamTool) {
@@ -51521,7 +51745,7 @@ function resolveCwdOverride(baseCwd, override) {
51521
51745
  if (!override) return { ok: true, cwd: baseCwd };
51522
51746
  try {
51523
51747
  const resolved = resolveRealContainedPath(baseCwd, override);
51524
- const stat2 = statSync34(resolved);
51748
+ const stat2 = statSync36(resolved);
51525
51749
  if (!stat2.isDirectory())
51526
51750
  return {
51527
51751
  ok: false,
@@ -52795,7 +53019,7 @@ var init_status = __esm({
52795
53019
  });
52796
53020
 
52797
53021
  // src/extension/team-tool/workflow-manage.ts
52798
- import { existsSync as existsSync58, readFileSync as readFileSync57, rmSync as rmSync16, writeFileSync as writeFileSync26 } from "node:fs";
53022
+ import { existsSync as existsSync58, readFileSync as readFileSync58, rmSync as rmSync16, writeFileSync as writeFileSync26 } from "node:fs";
52799
53023
  import { dirname as dirname33, join as join59 } from "node:path";
52800
53024
  function allowedWorkflowDirs(cwd) {
52801
53025
  return [join59(projectCrewRoot(cwd), "workflows"), join59(userPiRoot(), "workflows"), join59(packageRoot(), "workflows")];
@@ -52867,7 +53091,7 @@ function handleWorkflowGet(params, ctx) {
52867
53091
  let source = "(static workflow \u2014 no script source)";
52868
53092
  if (isDynamic && wf.filePath && existsSync58(wf.filePath)) {
52869
53093
  try {
52870
- source = readFileSync57(wf.filePath, "utf-8").slice(0, 8e3);
53094
+ source = readFileSync58(wf.filePath, "utf-8").slice(0, 8e3);
52871
53095
  } catch (error) {
52872
53096
  logInternalError("workflow-manage.get", error, `filePath=${wf.filePath}`);
52873
53097
  }
@@ -54413,7 +54637,7 @@ function asError(error) {
54413
54637
  return error instanceof Error ? error : new Error(String(error));
54414
54638
  }
54415
54639
  function globToRegex(pattern) {
54416
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
54640
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
54417
54641
  return new RegExp(`^${escaped}$`, "i");
54418
54642
  }
54419
54643
  function isRetryable(error, policy) {
@@ -56385,6 +56609,10 @@ function readKnowledge(cwd, query) {
56385
56609
  knowledgeCache.delete(p);
56386
56610
  return "";
56387
56611
  }
56612
+ if (stat2.isSymbolicLink) {
56613
+ logInternalError("knowledge-injection.symlink", new Error(`Refusing to read symlink: ${p}`));
56614
+ return "";
56615
+ }
56388
56616
  const cacheKey2 = `${stat2.mtimeMs}:${stat2.size}`;
56389
56617
  if (!query || !query.goal && !query.taskText) {
56390
56618
  const cached = knowledgeCache.get(p);
@@ -56434,8 +56662,8 @@ Full file: ${p} -->`
56434
56662
  }
56435
56663
  function tryStat(p) {
56436
56664
  try {
56437
- const s = fs77.statSync(p);
56438
- return { mtimeMs: s.mtimeMs, size: s.size };
56665
+ const s = fs77.lstatSync(p);
56666
+ return { mtimeMs: s.mtimeMs, size: s.size, isSymbolicLink: s.isSymbolicLink() };
56439
56667
  } catch {
56440
56668
  return void 0;
56441
56669
  }
@@ -56466,6 +56694,7 @@ var KNOWLEDGE_FILENAME, MAX_SESSION_LOG_BYTES, CONVENTION_HEADERS, STOPWORDS, kn
56466
56694
  var init_knowledge_injection = __esm({
56467
56695
  "src/extension/knowledge-injection.ts"() {
56468
56696
  "use strict";
56697
+ init_internal_error();
56469
56698
  init_paths();
56470
56699
  KNOWLEDGE_FILENAME = "knowledge.md";
56471
56700
  MAX_SESSION_LOG_BYTES = 5e3;
@@ -57087,32 +57316,12 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
57087
57316
  try {
57088
57317
  currentMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
57089
57318
  } catch {
57090
- currentMtime = 0;
57319
+ return fallbackTasks;
57091
57320
  }
57092
57321
  if (currentMtime !== baseMtime) {
57093
57322
  baseMtime = currentMtime;
57094
57323
  continue;
57095
57324
  }
57096
- let recheckMtime;
57097
- try {
57098
- recheckMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
57099
- } catch {
57100
- return fallbackTasks;
57101
- }
57102
- if (recheckMtime !== baseMtime) {
57103
- baseMtime = recheckMtime;
57104
- continue;
57105
- }
57106
- let preWriteMtime;
57107
- try {
57108
- preWriteMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
57109
- } catch {
57110
- preWriteMtime = 0;
57111
- }
57112
- if (preWriteMtime !== baseMtime) {
57113
- baseMtime = preWriteMtime;
57114
- continue;
57115
- }
57116
57325
  break;
57117
57326
  }
57118
57327
  if (merged === void 0) {
@@ -57934,6 +58143,7 @@ async function runTeamTask(input) {
57934
58143
  runId: manifest.runId,
57935
58144
  agentId: task.id,
57936
58145
  artifactsRoot: manifest.artifactsRoot,
58146
+ steeringFile: `${manifest.artifactsRoot}/steering/${task.id}.jsonl`,
57937
58147
  onSpawn: (pid) => {
57938
58148
  try {
57939
58149
  ({ task, tasks } = checkpointTask(manifest, tasks, task, "child-spawned", pid));
@@ -59593,8 +59803,7 @@ function checkPerTaskBudget(tasks, budgetTotal, budgetWarning, budgetAbort, fair
59593
59803
  const totalUsed = (usage?.input ?? 0) + (usage?.output ?? 0) + (usage?.cacheWrite ?? 0);
59594
59804
  const abort = totalUsed >= budgetAbort * budgetTotal;
59595
59805
  const warning = !abort && totalUsed >= budgetWarning * budgetTotal;
59596
- const remainingBudget = Math.max(0, budgetTotal - totalUsed);
59597
- const fairShareThreshold = remainingBudget * fairShareFraction;
59806
+ const fairShareThreshold = budgetTotal * fairShareFraction;
59598
59807
  const fairShareViolators = [];
59599
59808
  for (const task of tasks) {
59600
59809
  if (!task.usage) continue;
@@ -60031,6 +60240,8 @@ async function executeTeamRun(input) {
60031
60240
  cleanupUsage();
60032
60241
  await flushEventLogBuffer();
60033
60242
  return result4;
60243
+ } finally {
60244
+ await flushEventLogBuffer();
60034
60245
  }
60035
60246
  }
60036
60247
  async function executeTeamRunCore(input, manifest, workflow) {
@@ -60906,122 +61117,126 @@ var init_pipeline_runner = __esm({
60906
61117
  const stages = [];
60907
61118
  let previousResults = [];
60908
61119
  const startTime = Date.now();
60909
- await appendEventAsync(eventsPath, {
60910
- type: "pipeline:started",
60911
- runId,
60912
- message: `Pipeline '${workflow.name}' started`,
60913
- data: { stages: workflow.stages.map((s) => s.name) }
60914
- });
60915
- for (let i2 = 0; i2 < workflow.stages.length; i2++) {
60916
- const stage = workflow.stages[i2];
60917
- const stageStartTime = Date.now();
60918
- const effectiveStopOnError = stage.stopOnError ?? workflow.stopOnError ?? this.stopOnError;
61120
+ try {
60919
61121
  await appendEventAsync(eventsPath, {
60920
- type: "pipeline:stage_started",
61122
+ type: "pipeline:started",
60921
61123
  runId,
60922
- message: `Stage '${stage.name}' started`,
60923
- data: { stageIndex: i2, stageName: stage.name }
61124
+ message: `Pipeline '${workflow.name}' started`,
61125
+ data: { stages: workflow.stages.map((s) => s.name) }
60924
61126
  });
60925
- try {
60926
- const stageContext = {
60927
- stageIndex: i2,
60928
- stageName: stage.name,
60929
- previousResults,
60930
- totalStages: workflow.stages.length,
60931
- runId
60932
- };
60933
- const inputs = this.resolveInputs(stage.inputs, previousResults, context);
60934
- const results = await this.executeStageInternal(stage, inputs, stageContext, executeStage);
60935
- const duration = Date.now() - stageStartTime;
60936
- stages.push({
60937
- name: stage.name,
60938
- status: "completed",
60939
- results,
60940
- duration,
60941
- fanOutItems: Array.isArray(inputs) ? inputs.length : void 0
60942
- });
60943
- previousResults = results;
61127
+ for (let i2 = 0; i2 < workflow.stages.length; i2++) {
61128
+ const stage = workflow.stages[i2];
61129
+ const stageStartTime = Date.now();
61130
+ const effectiveStopOnError = stage.stopOnError ?? workflow.stopOnError ?? this.stopOnError;
60944
61131
  await appendEventAsync(eventsPath, {
60945
- type: "pipeline:stage_completed",
61132
+ type: "pipeline:stage_started",
60946
61133
  runId,
60947
- message: `Stage '${stage.name}' completed`,
60948
- data: {
61134
+ message: `Stage '${stage.name}' started`,
61135
+ data: { stageIndex: i2, stageName: stage.name }
61136
+ });
61137
+ try {
61138
+ const stageContext = {
60949
61139
  stageIndex: i2,
60950
61140
  stageName: stage.name,
60951
- duration,
60952
- resultCount: results.length
60953
- }
60954
- });
60955
- } catch (error) {
60956
- const duration = Date.now() - stageStartTime;
60957
- const errorMessage = error instanceof Error ? error.message : String(error);
60958
- if (effectiveStopOnError) {
60959
- stages.push({
60960
- name: stage.name,
60961
- status: "failed",
60962
- results: [],
60963
- error: errorMessage,
60964
- duration
60965
- });
60966
- await appendEventAsync(eventsPath, {
60967
- type: "pipeline:stage_failed",
60968
- runId,
60969
- message: `Stage '${stage.name}' failed: ${errorMessage}`,
60970
- data: {
60971
- stageIndex: i2,
60972
- stageName: stage.name,
60973
- duration,
60974
- error: errorMessage
60975
- }
60976
- });
60977
- await appendEventAsync(eventsPath, {
60978
- type: "pipeline:failed",
60979
- runId,
60980
- message: `Pipeline '${workflow.name}' failed at stage '${stage.name}'`,
60981
- data: { failedStage: stage.name, error: errorMessage }
60982
- });
60983
- return {
60984
- stages,
60985
- totalDuration: Date.now() - startTime,
60986
- finalResults: previousResults,
60987
- status: "failed"
61141
+ previousResults,
61142
+ totalStages: workflow.stages.length,
61143
+ runId
60988
61144
  };
60989
- } else {
61145
+ const inputs = this.resolveInputs(stage.inputs, previousResults, context);
61146
+ const results = await this.executeStageInternal(stage, inputs, stageContext, executeStage);
61147
+ const duration = Date.now() - stageStartTime;
60990
61148
  stages.push({
60991
61149
  name: stage.name,
60992
- status: "failed",
60993
- results: [],
60994
- error: errorMessage,
60995
- duration
61150
+ status: "completed",
61151
+ results,
61152
+ duration,
61153
+ fanOutItems: Array.isArray(inputs) ? inputs.length : void 0
60996
61154
  });
61155
+ previousResults = results;
60997
61156
  await appendEventAsync(eventsPath, {
60998
- type: "pipeline:stage_skipped",
61157
+ type: "pipeline:stage_completed",
60999
61158
  runId,
61000
- message: `Stage '${stage.name}' skipped due to error`,
61159
+ message: `Stage '${stage.name}' completed`,
61001
61160
  data: {
61002
61161
  stageIndex: i2,
61003
61162
  stageName: stage.name,
61004
61163
  duration,
61005
- error: errorMessage
61164
+ resultCount: results.length
61006
61165
  }
61007
61166
  });
61167
+ } catch (error) {
61168
+ const duration = Date.now() - stageStartTime;
61169
+ const errorMessage = error instanceof Error ? error.message : String(error);
61170
+ if (effectiveStopOnError) {
61171
+ stages.push({
61172
+ name: stage.name,
61173
+ status: "failed",
61174
+ results: [],
61175
+ error: errorMessage,
61176
+ duration
61177
+ });
61178
+ await appendEventAsync(eventsPath, {
61179
+ type: "pipeline:stage_failed",
61180
+ runId,
61181
+ message: `Stage '${stage.name}' failed: ${errorMessage}`,
61182
+ data: {
61183
+ stageIndex: i2,
61184
+ stageName: stage.name,
61185
+ duration,
61186
+ error: errorMessage
61187
+ }
61188
+ });
61189
+ await appendEventAsync(eventsPath, {
61190
+ type: "pipeline:failed",
61191
+ runId,
61192
+ message: `Pipeline '${workflow.name}' failed at stage '${stage.name}'`,
61193
+ data: { failedStage: stage.name, error: errorMessage }
61194
+ });
61195
+ return {
61196
+ stages,
61197
+ totalDuration: Date.now() - startTime,
61198
+ finalResults: previousResults,
61199
+ status: "failed"
61200
+ };
61201
+ } else {
61202
+ stages.push({
61203
+ name: stage.name,
61204
+ status: "failed",
61205
+ results: [],
61206
+ error: errorMessage,
61207
+ duration
61208
+ });
61209
+ await appendEventAsync(eventsPath, {
61210
+ type: "pipeline:stage_skipped",
61211
+ runId,
61212
+ message: `Stage '${stage.name}' skipped due to error`,
61213
+ data: {
61214
+ stageIndex: i2,
61215
+ stageName: stage.name,
61216
+ duration,
61217
+ error: errorMessage
61218
+ }
61219
+ });
61220
+ }
61008
61221
  }
61009
61222
  }
61223
+ await appendEventAsync(eventsPath, {
61224
+ type: "pipeline:completed",
61225
+ runId,
61226
+ message: `Pipeline '${workflow.name}' completed`,
61227
+ data: {
61228
+ stages: stages.map((s) => ({ name: s.name, status: s.status }))
61229
+ }
61230
+ });
61231
+ return {
61232
+ stages,
61233
+ totalDuration: Date.now() - startTime,
61234
+ finalResults: previousResults,
61235
+ status: stages.some((s) => s.status === "failed") ? "partial" : "completed"
61236
+ };
61237
+ } finally {
61238
+ await flushEventLogBuffer();
61010
61239
  }
61011
- await appendEventAsync(eventsPath, {
61012
- type: "pipeline:completed",
61013
- runId,
61014
- message: `Pipeline '${workflow.name}' completed`,
61015
- data: {
61016
- stages: stages.map((s) => ({ name: s.name, status: s.status }))
61017
- }
61018
- });
61019
- return {
61020
- stages,
61021
- totalDuration: Date.now() - startTime,
61022
- finalResults: previousResults,
61023
- status: stages.some((s) => s.status === "failed") ? "partial" : "completed"
61024
- };
61025
61240
  }
61026
61241
  /**
61027
61242
  * Execute a single stage, handling fan-out for array inputs.
@@ -68717,7 +68932,7 @@ var init_deterministic_ast = __esm({
68717
68932
  });
68718
68933
 
68719
68934
  // src/runtime/dwf-state-store.ts
68720
- import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync67, unlinkSync as unlinkSync11 } from "node:fs";
68935
+ import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync68, unlinkSync as unlinkSync11 } from "node:fs";
68721
68936
  import { dirname as dirname37 } from "node:path";
68722
68937
  var DwfStore;
68723
68938
  var init_dwf_state_store = __esm({
@@ -68738,7 +68953,7 @@ var init_dwf_state_store = __esm({
68738
68953
  const path81 = this.path;
68739
68954
  try {
68740
68955
  if (!existsSync70(path81)) return void 0;
68741
- const raw = readFileSync67(path81, "utf-8");
68956
+ const raw = readFileSync68(path81, "utf-8");
68742
68957
  const parsed = JSON.parse(raw);
68743
68958
  if (!parsed || typeof parsed !== "object" || typeof parsed.runId !== "string") return void 0;
68744
68959
  return parsed;
@@ -69560,7 +69775,7 @@ var dynamic_workflow_runner_exports = {};
69560
69775
  __export(dynamic_workflow_runner_exports, {
69561
69776
  runDynamicWorkflow: () => runDynamicWorkflow
69562
69777
  });
69563
- import { readFileSync as readFileSync68 } from "node:fs";
69778
+ import { readFileSync as readFileSync69 } from "node:fs";
69564
69779
  import { join as join71 } from "node:path";
69565
69780
  function assertStructuredCloneable(value, name) {
69566
69781
  try {
@@ -69585,7 +69800,7 @@ function resolveScriptPath(workflow, cwd) {
69585
69800
  );
69586
69801
  }
69587
69802
  async function loadWorkflowModule(scriptPath) {
69588
- const scriptSource = readFileSync68(scriptPath, "utf-8");
69803
+ const scriptSource = readFileSync69(scriptPath, "utf-8");
69589
69804
  if (isDeterminismCheckEnabled()) {
69590
69805
  assertDeterministicScript(scriptSource);
69591
69806
  }
@@ -69708,7 +69923,7 @@ async function runDynamicWorkflow(input) {
69708
69923
  }
69709
69924
  function readFinalArtifact(artifactPath) {
69710
69925
  try {
69711
- return readFileSync68(artifactPath, "utf-8");
69926
+ return readFileSync69(artifactPath, "utf-8");
69712
69927
  } catch (error) {
69713
69928
  logInternalError("dynamic-workflow-runner.readFinal", error, `artifactPath=${artifactPath}`);
69714
69929
  return `(failed to read final artifact ${artifactPath})`;
@@ -70991,6 +71206,15 @@ function handleSteer(params, ctx) {
70991
71206
  }
70992
71207
  task.pendingSteers.push(message);
70993
71208
  saveRunTasks(loaded.manifest, loaded.tasks);
71209
+ try {
71210
+ const steeringDir = `${loaded.manifest.artifactsRoot}/steering`;
71211
+ fs91.mkdirSync(steeringDir, { recursive: true });
71212
+ fs91.appendFileSync(
71213
+ `${steeringDir}/${taskId}.jsonl`,
71214
+ JSON.stringify({ type: "steer", message, ts: (/* @__PURE__ */ new Date()).toISOString() }) + "\n"
71215
+ );
71216
+ } catch {
71217
+ }
70994
71218
  appendEvent(loaded.manifest.eventsPath, {
70995
71219
  type: "task.steer_queued",
70996
71220
  runId,
@@ -75827,7 +76051,7 @@ function teamCommandContext(ctx) {
75827
76051
  }
75828
76052
  async function openTeamSettingsOverlay(ctx) {
75829
76053
  if (!ctx.hasUI) return;
75830
- const [{ updateConfig: updateConfig2, parseConfig: parseConfig2 }, { asCrewTheme: asCrewTheme2 }, { createSettingsOverlay: createSettingsOverlay2 }] = await Promise.all([
76054
+ const [{ updateConfig: updateConfig2, parseConfig: parseConfig2 }, { asCrewTheme: asCrewTheme3 }, { createSettingsOverlay: createSettingsOverlay2 }] = await Promise.all([
75831
76055
  Promise.resolve().then(() => (init_config(), config_exports)),
75832
76056
  Promise.resolve().then(() => (init_theme_adapter(), theme_adapter_exports)),
75833
76057
  Promise.resolve().then(() => (init_settings_overlay(), settings_overlay_exports))
@@ -75836,7 +76060,7 @@ async function openTeamSettingsOverlay(ctx) {
75836
76060
  const config = loaded.config;
75837
76061
  await ctx.ui.custom(
75838
76062
  (_tui, _theme, _keybindings, done) => {
75839
- const theme = asCrewTheme2(_theme);
76063
+ const theme = asCrewTheme3(_theme);
75840
76064
  const { overlay } = createSettingsOverlay2(
75841
76065
  config,
75842
76066
  theme,
@@ -79776,7 +80000,7 @@ init_orphan_worker_registry();
79776
80000
  init_peer_dep();
79777
80001
 
79778
80002
  // src/runtime/per-write-validator.ts
79779
- import { readFileSync as readFileSync17 } from "node:fs";
80003
+ import { readFileSync as readFileSync18 } from "node:fs";
79780
80004
  import { extname as pathExtname } from "node:path";
79781
80005
  function validateJson(content, _filePath) {
79782
80006
  if (content.trim() === "") return { ok: true };
@@ -79820,7 +80044,7 @@ function validateWrittenFile(filePath) {
79820
80044
  if (!validator) return null;
79821
80045
  let content;
79822
80046
  try {
79823
- content = readFileSync17(filePath, "utf-8");
80047
+ content = readFileSync18(filePath, "utf-8");
79824
80048
  } catch {
79825
80049
  return null;
79826
80050
  }
@@ -81478,6 +81702,1145 @@ function registerCrewShortcuts(pi) {
81478
81702
  }
81479
81703
  var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
81480
81704
 
81705
+ // src/extension/crew-vibes/config.ts
81706
+ import { existsSync as existsSync76, mkdirSync as mkdirSync45, readFileSync as readFileSync75, writeFileSync as writeFileSync33 } from "node:fs";
81707
+ import { dirname as dirname39, join as join76 } from "node:path";
81708
+
81709
+ // src/extension/crew-vibes/font-detect.ts
81710
+ import { existsSync as existsSync75, readFileSync as readFileSync74 } from "node:fs";
81711
+ import { homedir as homedir11, platform } from "node:os";
81712
+ import { join as join75 } from "node:path";
81713
+ function fontPath() {
81714
+ const os16 = platform();
81715
+ const home = homedir11();
81716
+ if (os16 === "darwin") return join75(home, "Library", "Fonts", "crew-vibes.ttf");
81717
+ if (os16 === "linux") return join75(home, ".local", "share", "fonts", "crew-vibes.ttf");
81718
+ if (os16 === "win32") {
81719
+ const local = process.env.LOCALAPPDATA ?? join75(home, "AppData", "Local");
81720
+ return join75(local, "Microsoft", "Windows", "Fonts", "crew-vibes.ttf");
81721
+ }
81722
+ return "";
81723
+ }
81724
+ var _hasFontFile = null;
81725
+ function hasCrewFontFile() {
81726
+ if (_hasFontFile !== null) return _hasFontFile;
81727
+ const p = fontPath();
81728
+ _hasFontFile = p !== "" && existsSync75(p);
81729
+ return _hasFontFile;
81730
+ }
81731
+ function isWebTerminal() {
81732
+ if (process.env.GOTTY || process.env.WEBTERM) return true;
81733
+ if (process.env.TERM === "dumb") return true;
81734
+ try {
81735
+ let pid = process.pid;
81736
+ for (let i2 = 0; i2 < 6 && pid > 1; i2++) {
81737
+ const cgroup = readFileSync74(`/proc/${pid}/cgroup`, "utf8");
81738
+ if (cgroup.includes("gotty") || cgroup.includes("wetty")) return true;
81739
+ const match = cgroup.match(/\d+:.*:(.*)/);
81740
+ const status = readFileSync74(`/proc/${pid}/status`, "utf8");
81741
+ const ppid = status.match(/^PPid:\s+(\d+)/m);
81742
+ pid = ppid ? Number.parseInt(ppid[1], 10) : 1;
81743
+ }
81744
+ } catch {
81745
+ }
81746
+ return false;
81747
+ }
81748
+
81749
+ // src/extension/crew-vibes/config.ts
81750
+ var SPEED_STATUS_ID = "pi-crew-speed";
81751
+ var CAPACITY_STATUS_ID = "pi-crew-bar";
81752
+ var PROVIDER_STATUS_ID = "pi-crew-bar";
81753
+ function resolveHome() {
81754
+ return process.env.PI_TEAMS_HOME?.trim() || process.env.HOME || process.env.USERPROFILE || "";
81755
+ }
81756
+ function configPath2() {
81757
+ return join76(resolveHome(), ".pi", "agent", "pi-crew-vibes.json");
81758
+ }
81759
+ var DEFAULT_CONFIG2 = {
81760
+ enabled: true,
81761
+ speed: {
81762
+ enabled: true,
81763
+ footer: false,
81764
+ indicator: true,
81765
+ label: "tok/s",
81766
+ renderIntervalMs: 250,
81767
+ slidingWindowMs: 1e3,
81768
+ minReliableDurationMs: 1e3,
81769
+ maxDisplayTokS: 500,
81770
+ defaultIntervalMs: 167,
81771
+ minIntervalMs: 50,
81772
+ maxIntervalMs: 250,
81773
+ scale: 6e3
81774
+ },
81775
+ capacity: {
81776
+ enabled: true,
81777
+ tokenDisplay: "tokens",
81778
+ showLabel: true,
81779
+ refreshIntervalMs: 2e3,
81780
+ labels: ["Orbit", "Cruise", "Warp", "Black Hole", "Supernova", "Big Bang"],
81781
+ icons: ["\uE710", "\uE711", "\uE712", "\uE713", "\uE714", "\uE715"],
81782
+ providerUsage: true,
81783
+ providerRefreshMs: 3e5
81784
+ }
81785
+ };
81786
+ var FALLBACK_CAPACITY_ICONS = [
81787
+ "\u25CB ",
81788
+ // ○ empty circle (lean)
81789
+ "\u25D4 ",
81790
+ // ◔ circle with dot (chonking)
81791
+ "\u25D1 ",
81792
+ // ◑ circle half filled (chonky)
81793
+ "\u25CF ",
81794
+ // ● filled circle (big chonk)
81795
+ "\u2B24 ",
81796
+ // ⬤ large filled circle (mega chonk)
81797
+ "\u2B22 "
81798
+ // ⬢ filled hexagon (oh lawd)
81799
+ ];
81800
+ function capacityIcons() {
81801
+ if (isWebTerminal()) return FALLBACK_CAPACITY_ICONS;
81802
+ return hasCrewFontFile() ? DEFAULT_CONFIG2.capacity.icons : FALLBACK_CAPACITY_ICONS;
81803
+ }
81804
+ function asRecord10(value) {
81805
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
81806
+ }
81807
+ function boolFrom(raw, fallback2) {
81808
+ return typeof raw === "boolean" ? raw : fallback2;
81809
+ }
81810
+ function stringFrom(raw, fallback2) {
81811
+ return typeof raw === "string" ? raw : fallback2;
81812
+ }
81813
+ function positiveFrom(raw, fallback2) {
81814
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : fallback2;
81815
+ }
81816
+ function sextet(raw, fallback2) {
81817
+ if (Array.isArray(raw) && raw.length === 6 && raw.every((entry) => typeof entry === "string")) {
81818
+ return raw;
81819
+ }
81820
+ return fallback2;
81821
+ }
81822
+ function tokenDisplayFrom(raw, fallback2) {
81823
+ return raw === "off" || raw === "tokens" || raw === "percentage" ? raw : fallback2;
81824
+ }
81825
+ function normalizeSpeed(raw) {
81826
+ const input = asRecord10(raw);
81827
+ const speed = {
81828
+ enabled: boolFrom(input.enabled, DEFAULT_CONFIG2.speed.enabled),
81829
+ footer: boolFrom(input.footer, DEFAULT_CONFIG2.speed.footer),
81830
+ indicator: boolFrom(input.indicator, DEFAULT_CONFIG2.speed.indicator),
81831
+ label: stringFrom(input.label, DEFAULT_CONFIG2.speed.label),
81832
+ renderIntervalMs: positiveFrom(input.renderIntervalMs, DEFAULT_CONFIG2.speed.renderIntervalMs),
81833
+ slidingWindowMs: positiveFrom(input.slidingWindowMs, DEFAULT_CONFIG2.speed.slidingWindowMs),
81834
+ minReliableDurationMs: positiveFrom(input.minReliableDurationMs, DEFAULT_CONFIG2.speed.minReliableDurationMs),
81835
+ maxDisplayTokS: positiveFrom(input.maxDisplayTokS, DEFAULT_CONFIG2.speed.maxDisplayTokS),
81836
+ defaultIntervalMs: positiveFrom(input.defaultIntervalMs, DEFAULT_CONFIG2.speed.defaultIntervalMs),
81837
+ minIntervalMs: positiveFrom(input.minIntervalMs, DEFAULT_CONFIG2.speed.minIntervalMs),
81838
+ maxIntervalMs: positiveFrom(input.maxIntervalMs, DEFAULT_CONFIG2.speed.maxIntervalMs),
81839
+ scale: positiveFrom(input.scale, DEFAULT_CONFIG2.speed.scale)
81840
+ };
81841
+ if (speed.minIntervalMs > speed.maxIntervalMs) {
81842
+ speed.minIntervalMs = DEFAULT_CONFIG2.speed.minIntervalMs;
81843
+ speed.maxIntervalMs = DEFAULT_CONFIG2.speed.maxIntervalMs;
81844
+ }
81845
+ return speed;
81846
+ }
81847
+ function normalizeCapacity(raw) {
81848
+ const input = asRecord10(raw);
81849
+ return {
81850
+ enabled: boolFrom(input.enabled, DEFAULT_CONFIG2.capacity.enabled),
81851
+ tokenDisplay: tokenDisplayFrom(input.tokenDisplay, DEFAULT_CONFIG2.capacity.tokenDisplay),
81852
+ showLabel: boolFrom(input.showLabel, DEFAULT_CONFIG2.capacity.showLabel),
81853
+ refreshIntervalMs: positiveFrom(input.refreshIntervalMs, DEFAULT_CONFIG2.capacity.refreshIntervalMs),
81854
+ labels: sextet(input.labels, DEFAULT_CONFIG2.capacity.labels),
81855
+ icons: sextet(input.icons, DEFAULT_CONFIG2.capacity.icons),
81856
+ providerUsage: boolFrom(input.providerUsage, DEFAULT_CONFIG2.capacity.providerUsage),
81857
+ providerRefreshMs: positiveFrom(input.providerRefreshMs, DEFAULT_CONFIG2.capacity.providerRefreshMs)
81858
+ };
81859
+ }
81860
+ function normalizeConfig(raw) {
81861
+ const input = asRecord10(raw);
81862
+ return {
81863
+ enabled: boolFrom(input.enabled, DEFAULT_CONFIG2.enabled),
81864
+ speed: normalizeSpeed(input.speed),
81865
+ capacity: normalizeCapacity(input.capacity)
81866
+ };
81867
+ }
81868
+ function loadConfig2() {
81869
+ try {
81870
+ const path81 = configPath2();
81871
+ if (!existsSync76(path81)) return normalizeConfig(void 0);
81872
+ return normalizeConfig(JSON.parse(readFileSync75(path81, "utf8")));
81873
+ } catch {
81874
+ return normalizeConfig(void 0);
81875
+ }
81876
+ }
81877
+ function saveConfig(config) {
81878
+ const path81 = configPath2();
81879
+ mkdirSync45(dirname39(path81), { recursive: true });
81880
+ writeFileSync33(path81, `${JSON.stringify(normalizeConfig(config), null, 2)}
81881
+ `);
81882
+ }
81883
+
81884
+ // src/extension/crew-vibes/provider-usage.ts
81885
+ import { readFileSync as readFileSync76 } from "node:fs";
81886
+ import { homedir as homedir12 } from "node:os";
81887
+ import { join as join77 } from "node:path";
81888
+ function withTimeout(ms, fn) {
81889
+ const controller = new AbortController();
81890
+ const timeoutId = setTimeout(() => controller.abort(), ms);
81891
+ return fn(controller.signal).finally(() => clearTimeout(timeoutId));
81892
+ }
81893
+ function piAuthPath() {
81894
+ return join77(homedir12(), ".pi", "agent", "auth.json");
81895
+ }
81896
+ function loadAnthropicToken() {
81897
+ const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
81898
+ if (envToken) return envToken;
81899
+ try {
81900
+ const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81901
+ const token = data2.anthropic?.access;
81902
+ return typeof token === "string" && token.length > 0 ? token : void 0;
81903
+ } catch {
81904
+ return void 0;
81905
+ }
81906
+ }
81907
+ function loadZaiToken() {
81908
+ const envKey = process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
81909
+ if (envKey) return envKey;
81910
+ try {
81911
+ const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81912
+ const key = data2["z-ai"]?.access || data2["z-ai"]?.key || data2.zai?.access || data2.zai?.key;
81913
+ return typeof key === "string" && key.length > 0 ? key : void 0;
81914
+ } catch {
81915
+ return void 0;
81916
+ }
81917
+ }
81918
+ function loadMinimaxToken() {
81919
+ const envKey = process.env.MINIMAX_API_KEY?.trim();
81920
+ if (envKey) return envKey;
81921
+ try {
81922
+ const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81923
+ const key = data2.minimax?.key;
81924
+ return typeof key === "string" && key.length > 0 ? key : void 0;
81925
+ } catch {
81926
+ return void 0;
81927
+ }
81928
+ }
81929
+ var COPILOT_TOKEN_KEYS = ["oauth_token", "user_token", "github_token", "token"];
81930
+ function tokenFromHostEntry(entry) {
81931
+ if (!entry) return void 0;
81932
+ for (const key of COPILOT_TOKEN_KEYS) {
81933
+ const value = entry[key];
81934
+ if (typeof value === "string" && value.length > 0) return value;
81935
+ }
81936
+ return void 0;
81937
+ }
81938
+ function loadLegacyCopilotToken() {
81939
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || join77(homedir12(), ".config");
81940
+ const candidates = [join77(configHome, "github-copilot", "hosts.json"), join77(homedir12(), ".github-copilot", "hosts.json")];
81941
+ for (const hostsPath of candidates) {
81942
+ try {
81943
+ const data2 = JSON.parse(readFileSync76(hostsPath, "utf8"));
81944
+ if (!data2 || typeof data2 !== "object") continue;
81945
+ const normalized = {};
81946
+ for (const [host, entry] of Object.entries(data2)) {
81947
+ normalized[host.toLowerCase()] = entry;
81948
+ }
81949
+ const preferred = tokenFromHostEntry(normalized["github.com"]) ?? tokenFromHostEntry(normalized["api.github.com"]);
81950
+ if (preferred) return preferred;
81951
+ for (const entry of Object.values(normalized)) {
81952
+ const token = tokenFromHostEntry(entry);
81953
+ if (token) return token;
81954
+ }
81955
+ } catch {
81956
+ }
81957
+ }
81958
+ return void 0;
81959
+ }
81960
+ function loadCopilotToken() {
81961
+ const envToken = (process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "").trim();
81962
+ if (envToken) return envToken;
81963
+ try {
81964
+ const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81965
+ const piToken = data2["github-copilot"]?.refresh || data2["github-copilot"]?.access;
81966
+ if (typeof piToken === "string" && piToken.length > 0) return piToken;
81967
+ } catch {
81968
+ }
81969
+ return loadLegacyCopilotToken();
81970
+ }
81971
+ async function fetchAnthropicUsage(token) {
81972
+ const data2 = await withTimeout(1e4, async (signal) => {
81973
+ const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
81974
+ headers: {
81975
+ Authorization: `Bearer ${token}`,
81976
+ "anthropic-beta": "oauth-2025-04-20"
81977
+ },
81978
+ signal
81979
+ });
81980
+ if (!res.ok) throw new Error(`anthropic usage HTTP ${res.status}`);
81981
+ return await res.json();
81982
+ });
81983
+ return {
81984
+ fiveHourPercent: data2.five_hour?.utilization ?? 0,
81985
+ weeklyPercent: data2.seven_day?.utilization ?? 0,
81986
+ fiveHourResetAt: data2.five_hour?.resets_at ?? null,
81987
+ weeklyResetAt: data2.seven_day?.resets_at ?? null
81988
+ };
81989
+ }
81990
+ async function fetchCopilotMonthlyPercent(token) {
81991
+ const data2 = await withTimeout(1e4, async (signal) => {
81992
+ const res = await fetch("https://api.github.com/copilot_internal/user", {
81993
+ headers: {
81994
+ Authorization: `token ${token}`,
81995
+ "Editor-Version": "vscode/1.96.2",
81996
+ "User-Agent": "GitHubCopilotChat/0.26.7",
81997
+ "X-Github-Api-Version": "2025-04-01",
81998
+ Accept: "application/json"
81999
+ },
82000
+ signal
82001
+ });
82002
+ if (!res.ok) throw new Error(`copilot user HTTP ${res.status}`);
82003
+ return await res.json();
82004
+ });
82005
+ const percentRemaining = data2.quota_snapshots?.premium_interactions?.percent_remaining;
82006
+ if (typeof percentRemaining !== "number") return void 0;
82007
+ return Math.max(0, 100 - percentRemaining);
82008
+ }
82009
+ async function fetchZaiUsage(token) {
82010
+ const data2 = await withTimeout(1e4, async (signal) => {
82011
+ const res = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
82012
+ method: "GET",
82013
+ headers: {
82014
+ Authorization: `Bearer ${token}`,
82015
+ Accept: "application/json"
82016
+ },
82017
+ signal
82018
+ });
82019
+ if (!res.ok) throw new Error(`z.ai usage HTTP ${res.status}`);
82020
+ return await res.json();
82021
+ });
82022
+ if (!data2.success || data2.code !== 200) throw new Error(data2.msg || "z.ai API error");
82023
+ const limits = data2.data?.limits ?? [];
82024
+ let tokensPercent = 0;
82025
+ let monthlyPercent = 0;
82026
+ let tokensResetAt = null;
82027
+ let monthlyResetAt = null;
82028
+ for (const limit of limits) {
82029
+ const pct = limit.percentage ?? 0;
82030
+ const resetIso = typeof limit.nextResetTime === "number" ? new Date(limit.nextResetTime).toISOString() : typeof limit.nextResetTime === "string" ? limit.nextResetTime : null;
82031
+ if (limit.type === "TOKENS_LIMIT") {
82032
+ tokensPercent = pct;
82033
+ tokensResetAt = resetIso;
82034
+ } else if (limit.type === "TIME_LIMIT") {
82035
+ monthlyPercent = pct;
82036
+ monthlyResetAt = resetIso;
82037
+ }
82038
+ }
82039
+ return {
82040
+ providerName: "z.ai",
82041
+ fiveHourPercent: tokensPercent,
82042
+ fiveHourResetAt: tokensResetAt,
82043
+ weeklyPercent: monthlyPercent,
82044
+ weeklyResetAt: monthlyResetAt
82045
+ };
82046
+ }
82047
+ var cachedUsage = null;
82048
+ var cachedAt = 0;
82049
+ function clearProviderUsageCache() {
82050
+ cachedUsage = null;
82051
+ cachedAt = 0;
82052
+ }
82053
+ async function fetchMinimaxUsage(token) {
82054
+ const data2 = await withTimeout(1e4, async (signal) => {
82055
+ const res = await fetch("https://www.minimax.io/v1/token_plan/remains", {
82056
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
82057
+ signal
82058
+ });
82059
+ if (!res.ok) throw new Error(`minimax usage HTTP ${res.status}`);
82060
+ return await res.json();
82061
+ });
82062
+ if (data2.base_resp?.status_code !== 0) throw new Error(data2.base_resp?.status_msg || "minimax API error");
82063
+ const models = data2.model_remains ?? [];
82064
+ const general = models.find((m) => m.model_name === "general") ?? models[0];
82065
+ if (!general) throw new Error("minimax: no model data");
82066
+ const intervalUsed = 100 - (general.current_interval_remaining_percent ?? 100);
82067
+ const weeklyUsed = 100 - (general.current_weekly_remaining_percent ?? 100);
82068
+ const intervalReset = typeof general.end_time === "number" ? new Date(general.end_time).toISOString() : null;
82069
+ const weeklyReset = typeof general.weekly_end_time === "number" ? new Date(general.weekly_end_time).toISOString() : null;
82070
+ return {
82071
+ providerName: "Minimax",
82072
+ fiveHourPercent: intervalUsed,
82073
+ fiveHourResetAt: intervalReset,
82074
+ weeklyPercent: weeklyUsed,
82075
+ weeklyResetAt: weeklyReset
82076
+ };
82077
+ }
82078
+ var QUOTA_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "minimax", "minimax-cn", "zai", "github-copilot"]);
82079
+ async function fetchForProvider(provider) {
82080
+ switch (provider) {
82081
+ case "anthropic": {
82082
+ const token = loadAnthropicToken();
82083
+ if (!token) return null;
82084
+ const base = await fetchAnthropicUsage(token);
82085
+ return { providerName: "Claude", ...base };
82086
+ }
82087
+ case "minimax":
82088
+ case "minimax-cn": {
82089
+ const token = loadMinimaxToken();
82090
+ if (!token) return null;
82091
+ return await fetchMinimaxUsage(token);
82092
+ }
82093
+ case "zai": {
82094
+ const token = loadZaiToken();
82095
+ if (!token) return null;
82096
+ const usage = await fetchZaiUsage(token);
82097
+ usage.providerName = "z.ai";
82098
+ return usage;
82099
+ }
82100
+ case "github-copilot": {
82101
+ const token = loadCopilotToken();
82102
+ if (!token) return null;
82103
+ const pct = await fetchCopilotMonthlyPercent(token);
82104
+ if (pct === void 0) return null;
82105
+ return {
82106
+ providerName: "Copilot",
82107
+ fiveHourPercent: 0,
82108
+ fiveHourResetAt: null,
82109
+ weeklyPercent: pct,
82110
+ weeklyResetAt: null,
82111
+ copilotMonthlyPercent: pct
82112
+ };
82113
+ }
82114
+ default:
82115
+ return null;
82116
+ }
82117
+ }
82118
+ async function fetchProviderUsage(maxAgeMs = 3e5, provider) {
82119
+ if (!provider || !QUOTA_PROVIDERS.has(provider)) {
82120
+ cachedUsage = null;
82121
+ return null;
82122
+ }
82123
+ if (cachedUsage !== null && Date.now() - cachedAt < maxAgeMs) {
82124
+ return cachedUsage;
82125
+ }
82126
+ try {
82127
+ const usage = await fetchForProvider(provider);
82128
+ if (usage) {
82129
+ cachedUsage = usage;
82130
+ cachedAt = Date.now();
82131
+ }
82132
+ return usage;
82133
+ } catch {
82134
+ return null;
82135
+ }
82136
+ }
82137
+
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
+ // src/extension/crew-vibes/speed.ts
82330
+ var COMPACTION_THRESHOLD = 5e3;
82331
+ function estimateTokensFromDelta(text) {
82332
+ if (!text) return 0;
82333
+ const matches = text.match(/\w+|[^\s\w]/g);
82334
+ return matches ? matches.length : 0;
82335
+ }
82336
+ var TokenSpeedEngine = class {
82337
+ _isStreaming = false;
82338
+ _tokenCount = 0;
82339
+ _startTime = 0;
82340
+ _endTime = 0;
82341
+ _events = [];
82342
+ _windowStartIndex = 0;
82343
+ _lastStableTokS = 0;
82344
+ _lastUsageOutput = 0;
82345
+ _config;
82346
+ constructor(config) {
82347
+ this._config = config;
82348
+ }
82349
+ updateConfig(config) {
82350
+ this._config = config;
82351
+ }
82352
+ get isStreaming() {
82353
+ return this._isStreaming;
82354
+ }
82355
+ get tokenCount() {
82356
+ return this._tokenCount;
82357
+ }
82358
+ get elapsedMs() {
82359
+ if (this._startTime === 0) return 0;
82360
+ return this._isStreaming ? Date.now() - this._startTime : this._endTime - this._startTime;
82361
+ }
82362
+ get avgTokS() {
82363
+ const elapsedSec = this.elapsedMs / 1e3;
82364
+ if (elapsedSec <= 0) return 0;
82365
+ return this._tokenCount / elapsedSec;
82366
+ }
82367
+ sanitizeTokS(value, durationMs = this.elapsedMs) {
82368
+ if (value === null || !Number.isFinite(value) || value <= 0) return null;
82369
+ if (durationMs < this._config.minReliableDurationMs) return null;
82370
+ if (value > this._config.maxDisplayTokS) return null;
82371
+ return value;
82372
+ }
82373
+ get tokS() {
82374
+ const candidate = this.rawTokS;
82375
+ const stable = this.sanitizeTokS(candidate);
82376
+ if (stable !== null) this._lastStableTokS = stable;
82377
+ return this._lastStableTokS;
82378
+ }
82379
+ get rawTokS() {
82380
+ if (this.elapsedMs < this._config.slidingWindowMs) return this.avgTokS;
82381
+ if (!this._isStreaming) return this.avgTokS;
82382
+ const now = Date.now();
82383
+ const windowStart = now - this._config.slidingWindowMs;
82384
+ while (this._windowStartIndex < this._events.length && this._events[this._windowStartIndex].time < windowStart) {
82385
+ this._windowStartIndex++;
82386
+ }
82387
+ if (this._windowStartIndex >= this._events.length) return this.avgTokS;
82388
+ let windowTokenCount = 0;
82389
+ for (let i2 = this._windowStartIndex; i2 < this._events.length; i2++) {
82390
+ windowTokenCount += this._events[i2].tokens;
82391
+ }
82392
+ if (windowTokenCount === 0) return this.avgTokS;
82393
+ const windowDuration = (now - this._events[this._windowStartIndex].time) / 1e3;
82394
+ if (windowDuration <= 0) return 0;
82395
+ return windowTokenCount / windowDuration;
82396
+ }
82397
+ start() {
82398
+ this._tokenCount = 0;
82399
+ this._isStreaming = true;
82400
+ this._startTime = Date.now();
82401
+ this._endTime = this._startTime;
82402
+ this._events = [];
82403
+ this._windowStartIndex = 0;
82404
+ this._lastStableTokS = 0;
82405
+ this._lastUsageOutput = 0;
82406
+ }
82407
+ stop() {
82408
+ this._isStreaming = false;
82409
+ this._endTime = Date.now();
82410
+ this._events = [];
82411
+ this._windowStartIndex = 0;
82412
+ }
82413
+ recordDelta(delta, usageOutput) {
82414
+ if (!this._isStreaming) return;
82415
+ if (usageOutput !== void 0 && usageOutput > 0) {
82416
+ const increment = usageOutput - this._lastUsageOutput;
82417
+ this._lastUsageOutput = usageOutput;
82418
+ this.recordTokens(Math.max(0, increment));
82419
+ return;
82420
+ }
82421
+ this.recordTokens(estimateTokensFromDelta(delta));
82422
+ }
82423
+ reconcileTotal(tokens) {
82424
+ if (tokens > 0) this._tokenCount = tokens;
82425
+ }
82426
+ recordTokens(tokens) {
82427
+ if (!this._isStreaming || tokens <= 0) return;
82428
+ this._tokenCount += tokens;
82429
+ this._events.push({ time: Date.now(), tokens });
82430
+ if (this._windowStartIndex >= COMPACTION_THRESHOLD) this.compact();
82431
+ }
82432
+ compact() {
82433
+ if (this._windowStartIndex === 0) return;
82434
+ this._events = this._events.slice(this._windowStartIndex);
82435
+ this._windowStartIndex = 0;
82436
+ }
82437
+ };
82438
+ function isSuccessfulStop(stopReason) {
82439
+ return stopReason !== "error" && stopReason !== "aborted";
82440
+ }
82441
+ var SpeedTracker = class {
82442
+ engine;
82443
+ lastStableTokS = null;
82444
+ sessionOutputTokens = 0;
82445
+ sessionDurationMs = 0;
82446
+ constructor(config) {
82447
+ this.engine = new TokenSpeedEngine(config);
82448
+ }
82449
+ updateConfig(config) {
82450
+ this.engine.updateConfig(config);
82451
+ }
82452
+ get isStreaming() {
82453
+ return this.engine.isStreaming;
82454
+ }
82455
+ get lastTokS() {
82456
+ return this.lastStableTokS;
82457
+ }
82458
+ resetSession() {
82459
+ this.sessionOutputTokens = 0;
82460
+ this.sessionDurationMs = 0;
82461
+ }
82462
+ startMessage() {
82463
+ this.engine.start();
82464
+ }
82465
+ recordDelta(delta, usageOutput) {
82466
+ this.engine.recordDelta(delta, usageOutput);
82467
+ }
82468
+ stopMessage() {
82469
+ if (this.engine.isStreaming) this.engine.stop();
82470
+ }
82471
+ liveTokS() {
82472
+ const speed = this.engine.tokS;
82473
+ return speed > 0 ? speed : this.lastStableTokS;
82474
+ }
82475
+ sessionAvgTokS() {
82476
+ return this.sessionDurationMs > 0 ? this.sessionOutputTokens / (this.sessionDurationMs / 1e3) : null;
82477
+ }
82478
+ finishMessage(outputTokens, stopReason) {
82479
+ if (!this.engine.isStreaming) return null;
82480
+ this.engine.reconcileTotal(outputTokens);
82481
+ const durationMs = this.engine.elapsedMs;
82482
+ const tokens = this.engine.tokenCount;
82483
+ const rawAvgTokS = durationMs > 0 ? tokens / (durationMs / 1e3) : null;
82484
+ const tokS = this.engine.sanitizeTokS(rawAvgTokS, durationMs);
82485
+ this.lastStableTokS = tokS;
82486
+ this.engine.stop();
82487
+ if (tokS !== null && isSuccessfulStop(stopReason)) {
82488
+ this.sessionOutputTokens += tokens;
82489
+ this.sessionDurationMs += durationMs;
82490
+ }
82491
+ return { outputTokens: tokens, durationMs, tokS };
82492
+ }
82493
+ };
82494
+ var SpeedAnimator = class {
82495
+ from = null;
82496
+ target = null;
82497
+ startedAt = 0;
82498
+ durationMs;
82499
+ constructor(durationMs) {
82500
+ this.durationMs = durationMs;
82501
+ }
82502
+ updateDuration(durationMs) {
82503
+ this.durationMs = durationMs;
82504
+ }
82505
+ reset(value = null, now = Date.now()) {
82506
+ this.from = value;
82507
+ this.target = value;
82508
+ this.startedAt = now;
82509
+ }
82510
+ setTarget(target, now = Date.now()) {
82511
+ if (target === null) {
82512
+ this.reset(null, now);
82513
+ return null;
82514
+ }
82515
+ const current2 = this.value(now);
82516
+ if (current2 === null) {
82517
+ this.reset(target, now);
82518
+ return target;
82519
+ }
82520
+ if (this.target !== null && Math.abs(target - this.target) < 0.05) return current2;
82521
+ this.from = current2;
82522
+ this.target = target;
82523
+ this.startedAt = now;
82524
+ return current2;
82525
+ }
82526
+ value(now = Date.now()) {
82527
+ if (this.target === null) return null;
82528
+ if (this.from === null || this.durationMs <= 0) return this.target;
82529
+ const progress = Math.max(0, Math.min(1, (now - this.startedAt) / this.durationMs));
82530
+ if (progress >= 1) {
82531
+ this.from = this.target;
82532
+ return this.target;
82533
+ }
82534
+ return this.from + (this.target - this.from) * progress;
82535
+ }
82536
+ isAnimating(now = Date.now()) {
82537
+ return this.target !== null && this.from !== null && Math.abs(this.target - this.from) >= 0.05 && now - this.startedAt < this.durationMs;
82538
+ }
82539
+ };
82540
+
82541
+ // src/extension/crew-vibes/index.ts
82542
+ function isAssistantMessage(message) {
82543
+ return typeof message === "object" && message !== null && message.role === "assistant";
82544
+ }
82545
+ function assistantUsageOutput(message) {
82546
+ const usage = message.usage;
82547
+ const output = usage?.output;
82548
+ return typeof output === "number" && Number.isFinite(output) ? output : void 0;
82549
+ }
82550
+ function assistantStopReason(message) {
82551
+ const reason = message.stopReason;
82552
+ return typeof reason === "string" ? reason : void 0;
82553
+ }
82554
+ function assistantEventType(event) {
82555
+ return typeof event.type === "string" ? event.type : void 0;
82556
+ }
82557
+ function registerCrewVibes(pi) {
82558
+ let config = loadConfig2();
82559
+ let lastRenderedAt = 0;
82560
+ let currentIntervalMs = 0;
82561
+ const speedTracker = new SpeedTracker(config.speed);
82562
+ const footerAnimator = new SpeedAnimator(config.speed.renderIntervalMs);
82563
+ let liveTimer;
82564
+ let footerTimer;
82565
+ let capacityTimer;
82566
+ let providerTimer;
82567
+ let lastProviderText;
82568
+ 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
+ }
82577
+ function themeOf(ctx) {
82578
+ return asCrewTheme2(ctx.hasUI ? ctx.ui.theme : void 0);
82579
+ }
82580
+ function publishCapacity(ctx) {
82581
+ if (!ctx?.hasUI) return;
82582
+ if (!config.enabled || !config.capacity.enabled) {
82583
+ setCapacityStatus(ctx, config, void 0);
82584
+ return;
82585
+ }
82586
+ const capText = renderCapacity(themeOf(ctx), config.capacity, getCapacityUsage(ctx));
82587
+ const combined = lastProviderText ? spreadLine(capText, lastProviderText) : capText;
82588
+ setCapacityStatus(ctx, config, combined);
82589
+ }
82590
+ function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
82591
+ if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
82592
+ setSpeedStatus(ctx, config, void 0);
82593
+ return;
82594
+ }
82595
+ setSpeedStatus(ctx, config, renderSpeedFooter(themeOf(ctx), config.speed, speed));
82596
+ }
82597
+ function applyIndicator(ctx, speed, force = false) {
82598
+ if (!ctx.hasUI || !ctx.ui.setWorkingIndicator) return;
82599
+ if (!config.enabled || !config.speed.enabled || !config.speed.indicator) {
82600
+ ctx.ui.setWorkingIndicator();
82601
+ return;
82602
+ }
82603
+ const next = intervalForSpeed(config.speed, speed);
82604
+ if (!force && Math.abs(next - currentIntervalMs) < 10) return;
82605
+ ctx.ui.setWorkingIndicator({
82606
+ frames: crewIndicatorFrames(themeOf(ctx)),
82607
+ intervalMs: next
82608
+ });
82609
+ currentIntervalMs = next;
82610
+ }
82611
+ function renderWorking(ctx, speed) {
82612
+ if (!config.enabled || !config.speed.enabled || !ctx.hasUI) return;
82613
+ ctx.ui.setWorkingMessage(renderWorkingMessage(themeOf(ctx), config.speed, speed));
82614
+ }
82615
+ function stopLiveTimer() {
82616
+ if (!liveTimer) return;
82617
+ clearInterval(liveTimer);
82618
+ liveTimer = void 0;
82619
+ }
82620
+ function stopFooterTimer() {
82621
+ if (!footerTimer) return;
82622
+ clearInterval(footerTimer);
82623
+ footerTimer = void 0;
82624
+ }
82625
+ function stopCapacityTimer() {
82626
+ if (!capacityTimer) return;
82627
+ clearInterval(capacityTimer);
82628
+ capacityTimer = void 0;
82629
+ }
82630
+ function stopProviderTimer() {
82631
+ if (!providerTimer) return;
82632
+ clearInterval(providerTimer);
82633
+ providerTimer = void 0;
82634
+ }
82635
+ function startLiveTimer(ctx) {
82636
+ if (liveTimer || !ctx.hasUI) return;
82637
+ liveTimer = setInterval(() => {
82638
+ if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) {
82639
+ stopLiveTimer();
82640
+ return;
82641
+ }
82642
+ const speed = speedTracker.liveTokS();
82643
+ applyIndicator(ctx, speed);
82644
+ renderWorking(ctx, speed);
82645
+ }, config.speed.renderIntervalMs);
82646
+ liveTimer.unref?.();
82647
+ }
82648
+ function startFooterTimer(ctx) {
82649
+ if (footerTimer || !ctx.hasUI) return;
82650
+ footerTimer = setInterval(() => {
82651
+ if (!config.enabled || !config.speed.enabled) {
82652
+ stopFooterTimer();
82653
+ return;
82654
+ }
82655
+ publishSpeedFooter(ctx);
82656
+ if (!footerAnimator.isAnimating()) stopFooterTimer();
82657
+ }, config.speed.renderIntervalMs);
82658
+ footerTimer.unref?.();
82659
+ }
82660
+ function startCapacityTimer(ctx) {
82661
+ if (capacityTimer) return;
82662
+ const interval = Math.max(250, config.capacity.refreshIntervalMs);
82663
+ capacityTimer = setInterval(() => publishCapacity(ctx), interval);
82664
+ capacityTimer.unref?.();
82665
+ }
82666
+ function startProviderTimer(ctx) {
82667
+ if (providerTimer) return;
82668
+ if (!config.capacity.providerUsage) return;
82669
+ 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);
82686
+ providerTimer.unref?.();
82687
+ }
82688
+ function resetWorking(ctx) {
82689
+ applyIndicator(ctx, null, true);
82690
+ renderWorking(ctx, speedTracker.lastTokS);
82691
+ }
82692
+ function applyConfig(ctx) {
82693
+ saveConfig(config);
82694
+ speedTracker.updateConfig(config.speed);
82695
+ footerAnimator.updateDuration(config.speed.renderIntervalMs);
82696
+ if (!config.enabled) {
82697
+ stopLiveTimer();
82698
+ stopFooterTimer();
82699
+ stopCapacityTimer();
82700
+ stopProviderTimer();
82701
+ clearVibesStatus(ctx);
82702
+ return;
82703
+ }
82704
+ publishCapacity(ctx);
82705
+ publishSpeedFooter(ctx);
82706
+ if (config.capacity.providerUsage) startProviderTimer(ctx);
82707
+ }
82708
+ pi.on("session_start", (_event, ctx) => {
82709
+ stopLiveTimer();
82710
+ stopFooterTimer();
82711
+ stopCapacityTimer();
82712
+ stopProviderTimer();
82713
+ config = loadConfig2();
82714
+ speedTracker.updateConfig(config.speed);
82715
+ footerAnimator.updateDuration(config.speed.renderIntervalMs);
82716
+ speedTracker.resetSession();
82717
+ footerAnimator.reset(null);
82718
+ clearProviderUsageCache();
82719
+ currentProvider = ctx.model?.provider;
82720
+ if (!config.enabled) {
82721
+ clearVibesStatus(ctx);
82722
+ return;
82723
+ }
82724
+ publishCapacity(ctx);
82725
+ publishSpeedFooter(ctx);
82726
+ startCapacityTimer(ctx);
82727
+ startProviderTimer(ctx);
82728
+ applyIndicator(ctx, null, true);
82729
+ });
82730
+ pi.on("agent_start", (_event, ctx) => {
82731
+ if (!config.enabled) return;
82732
+ resetWorking(ctx);
82733
+ });
82734
+ pi.on("turn_start", (_event, ctx) => {
82735
+ if (!config.enabled) return;
82736
+ resetWorking(ctx);
82737
+ });
82738
+ pi.on("message_start", (event, ctx) => {
82739
+ if (!config.enabled || !config.speed.enabled || !isAssistantMessage(event.message)) return;
82740
+ speedTracker.startMessage();
82741
+ footerAnimator.reset(speedTracker.lastTokS);
82742
+ startLiveTimer(ctx);
82743
+ lastRenderedAt = 0;
82744
+ });
82745
+ pi.on("message_update", (event, ctx) => {
82746
+ if (!config.enabled || !config.speed.enabled || !isAssistantMessage(event.message) || !speedTracker.isStreaming) return;
82747
+ const ev = event.assistantMessageEvent;
82748
+ const type = assistantEventType(ev);
82749
+ if (type === "text_delta" || type === "thinking_delta") {
82750
+ const delta = ev.delta ?? "";
82751
+ speedTracker.recordDelta(delta, assistantUsageOutput(event.message));
82752
+ }
82753
+ if (type === "start") resetWorking(ctx);
82754
+ const now = Date.now();
82755
+ if (now - lastRenderedAt < config.speed.renderIntervalMs && type !== "done") return;
82756
+ lastRenderedAt = now;
82757
+ const speed = speedTracker.liveTokS();
82758
+ applyIndicator(ctx, speed);
82759
+ renderWorking(ctx, speed);
82760
+ });
82761
+ pi.on("message_end", (event, ctx) => {
82762
+ if (!isAssistantMessage(event.message)) return;
82763
+ publishCapacity(ctx);
82764
+ if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) return;
82765
+ const completed = speedTracker.finishMessage(assistantUsageOutput(event.message) ?? 0, assistantStopReason(event.message));
82766
+ if (!completed) return;
82767
+ footerAnimator.setTarget(speedTracker.sessionAvgTokS());
82768
+ publishSpeedFooter(ctx);
82769
+ startFooterTimer(ctx);
82770
+ applyIndicator(ctx, speedTracker.lastTokS);
82771
+ });
82772
+ pi.on("turn_end", () => {
82773
+ speedTracker.stopMessage();
82774
+ stopLiveTimer();
82775
+ });
82776
+ pi.on("agent_end", (_event, ctx) => {
82777
+ speedTracker.stopMessage();
82778
+ stopLiveTimer();
82779
+ if (ctx && config.enabled && ctx.hasUI) {
82780
+ applyIndicator(ctx, speedTracker.lastTokS);
82781
+ ctx.ui.setWorkingMessage();
82782
+ }
82783
+ });
82784
+ pi.on("model_select", (event, ctx) => {
82785
+ currentProvider = event.model?.provider;
82786
+ clearProviderUsageCache();
82787
+ publishCapacity(ctx);
82788
+ });
82789
+ pi.on("session_compact", (_event, ctx) => publishCapacity(ctx));
82790
+ pi.on("session_tree", (_event, ctx) => publishCapacity(ctx));
82791
+ pi.on("session_shutdown", (_event, ctx) => {
82792
+ stopLiveTimer();
82793
+ stopFooterTimer();
82794
+ stopCapacityTimer();
82795
+ stopProviderTimer();
82796
+ clearVibesStatus(ctx);
82797
+ });
82798
+ async function handleCommand(args, ctx) {
82799
+ const tokens = args.trim().split(/\s+/).filter(Boolean);
82800
+ const [first, second] = tokens;
82801
+ const mutate = (next) => {
82802
+ config = next;
82803
+ applyConfig(ctx);
82804
+ };
82805
+ if (!first) {
82806
+ const speed = speedTracker.liveTokS();
82807
+ const usage = getCapacityUsage(ctx);
82808
+ const stage = config.capacity.icons.length ? config.capacity.labels[Math.max(
82809
+ 0,
82810
+ Math.min(
82811
+ config.capacity.labels.length - 1,
82812
+ Math.floor((usage.percent ?? 0) / 100 * config.capacity.labels.length)
82813
+ )
82814
+ )] : "?";
82815
+ ctx.ui.notify(
82816
+ `crew-vibes: ${config.enabled ? "on" : "off"} \xB7 speed ${config.speed.enabled ? "on" : "off"} (${formatSpeed(config.speed, speed)}) \xB7 capacity ${config.capacity.enabled ? "on" : "off"} (${stage})`,
82817
+ "info"
82818
+ );
82819
+ return;
82820
+ }
82821
+ if (first === "on" || first === "off") {
82822
+ mutate({ ...config, enabled: first === "on" });
82823
+ ctx.ui.notify(`crew-vibes ${first === "on" ? "enabled" : "disabled"}`, "info");
82824
+ return;
82825
+ }
82826
+ if (first === "speed" && (second === "on" || second === "off")) {
82827
+ mutate({ ...config, speed: { ...config.speed, enabled: second === "on" } });
82828
+ ctx.ui.notify(`crew-vibes speed ${second === "on" ? "enabled" : "disabled"}`, "info");
82829
+ return;
82830
+ }
82831
+ if (first === "capacity" && (second === "on" || second === "off")) {
82832
+ mutate({ ...config, capacity: { ...config.capacity, enabled: second === "on" } });
82833
+ ctx.ui.notify(`crew-vibes capacity ${second === "on" ? "enabled" : "disabled"}`, "info");
82834
+ return;
82835
+ }
82836
+ ctx.ui.notify("Usage: /team-vibes [on|off|speed on|off|capacity on|off]", "error");
82837
+ }
82838
+ pi.registerCommand("team-vibes", {
82839
+ description: "Toggle crew-vibes speed + context meters (on/off, speed, capacity)",
82840
+ handler: handleCommand
82841
+ });
82842
+ }
82843
+
81481
82844
  // src/extension/cross-extension-rpc.ts
81482
82845
  init_safe_paths();
81483
82846
 
@@ -83617,6 +84980,11 @@ Subagent may need manual intervention.`
83617
84980
  registerContextStatusInjection(pi, {
83618
84981
  enabled: loadConfig(process.cwd()).config.reliability?.ambientStatusInjection !== false
83619
84982
  });
84983
+ try {
84984
+ registerCrewVibes(pi);
84985
+ } catch (err2) {
84986
+ console.warn("[pi-crew] crew-vibes initialization failed:", err2 instanceof Error ? err2.message : err2);
84987
+ }
83620
84988
  }
83621
84989
 
83622
84990
  // index.bundle.ts