@moxxy/cli 0.12.3 → 0.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -195,6 +195,22 @@ var init_log = __esm({
195
195
  listeners = /* @__PURE__ */ new Set();
196
196
  clearListeners = /* @__PURE__ */ new Set();
197
197
  now;
198
+ /**
199
+ * Lazy secondary indexes so `ofType`/`byTurn` are O(matches) instead of an
200
+ * O(n) full-array `filter` per call (these back hot paths: token-accounting's
201
+ * `ofType('provider_response')`, lazy-tool gating's
202
+ * `ofType('tool_call_requested')`, and remote-session's per-turn
203
+ * `byTurn(turnId)` priming). Built lazily on first query so a cold/seeded log
204
+ * pays the one-time O(n) build only if anything ever queries it, then kept
205
+ * O(1) per append/ingest. Reset to `null` (rebuild-on-next-query) by
206
+ * `clear`/`rebase`, which mutate `events` wholesale.
207
+ *
208
+ * Each index holds the SAME event object references in their original
209
+ * append order — so a query returns an array deep-equal to the old
210
+ * `events.filter(...)` for every input.
211
+ */
212
+ byType = null;
213
+ byTurnId = null;
198
214
  /**
199
215
  * Seq of the FIRST event this log holds. 0 for an authoring log; a mirror
200
216
  * primed by a partial attach replay (runner protocol v6 `replay.start`)
@@ -224,11 +240,53 @@ var init_log = __esm({
224
240
  slice(from = 0, to = this.base + this.events.length) {
225
241
  return this.events.slice(Math.max(0, from - this.base), Math.max(0, to - this.base));
226
242
  }
243
+ /** Build both secondary indexes from the current `events` array, preserving
244
+ * append order. Bucket arrays hold the original event references. */
245
+ buildIndexes() {
246
+ const byType = /* @__PURE__ */ new Map();
247
+ const byTurnId = /* @__PURE__ */ new Map();
248
+ for (const e3 of this.events) {
249
+ let typeBucket = byType.get(e3.type);
250
+ if (!typeBucket)
251
+ byType.set(e3.type, typeBucket = []);
252
+ typeBucket.push(e3);
253
+ let turnBucket = byTurnId.get(e3.turnId);
254
+ if (!turnBucket)
255
+ byTurnId.set(e3.turnId, turnBucket = []);
256
+ turnBucket.push(e3);
257
+ }
258
+ this.byType = byType;
259
+ this.byTurnId = byTurnId;
260
+ }
261
+ /** Append one event to the live indexes (no-op while they're cold). Called
262
+ * after the event is pushed to `events`, so order is preserved. */
263
+ indexEvent(e3) {
264
+ if (this.byType) {
265
+ const bucket = this.byType.get(e3.type);
266
+ if (bucket)
267
+ bucket.push(e3);
268
+ else
269
+ this.byType.set(e3.type, [e3]);
270
+ }
271
+ if (this.byTurnId) {
272
+ const bucket = this.byTurnId.get(e3.turnId);
273
+ if (bucket)
274
+ bucket.push(e3);
275
+ else
276
+ this.byTurnId.set(e3.turnId, [e3]);
277
+ }
278
+ }
227
279
  ofType(type) {
228
- return this.events.filter((e3) => e3.type === type);
280
+ if (!this.byType)
281
+ this.buildIndexes();
282
+ const bucket = this.byType.get(type);
283
+ return bucket ? [...bucket] : [];
229
284
  }
230
285
  byTurn(turnId) {
231
- return this.events.filter((e3) => e3.turnId === turnId);
286
+ if (!this.byTurnId)
287
+ this.buildIndexes();
288
+ const bucket = this.byTurnId.get(turnId);
289
+ return bucket ? [...bucket] : [];
232
290
  }
233
291
  toJSON() {
234
292
  return [...this.events];
@@ -236,6 +294,7 @@ var init_log = __esm({
236
294
  async append(partial) {
237
295
  const event = materializeEvent(partial, this.base + this.events.length, this.now);
238
296
  this.events.push(event);
297
+ this.indexEvent(event);
239
298
  const snapshot = [...this.listeners];
240
299
  for (const fn of snapshot) {
241
300
  try {
@@ -262,6 +321,7 @@ var init_log = __esm({
262
321
  if (event.seq !== this.base + this.events.length)
263
322
  return;
264
323
  this.events.push(event);
324
+ this.indexEvent(event);
265
325
  const snapshot = [...this.listeners];
266
326
  for (const fn of snapshot) {
267
327
  try {
@@ -300,9 +360,13 @@ var init_log = __esm({
300
360
  throw new Error(`EventLog.rebase(${seq}): seq must be a non-negative integer`);
301
361
  }
302
362
  this.base = seq;
363
+ this.byType = null;
364
+ this.byTurnId = null;
303
365
  }
304
366
  clear() {
305
367
  this.events.length = 0;
368
+ this.byType = null;
369
+ this.byTurnId = null;
306
370
  this.base = 0;
307
371
  const snapshot = [...this.clearListeners];
308
372
  for (const fn of snapshot) {
@@ -2224,6 +2288,18 @@ var init_host2 = __esm({
2224
2288
  dataListeners = /* @__PURE__ */ new Set();
2225
2289
  /** In-flight opens, so concurrent `open(kind)` calls share one instance. */
2226
2290
  opening = /* @__PURE__ */ new Map();
2291
+ /**
2292
+ * Viewer attach count per kind. A surface is SHARED (the agent's tool + every
2293
+ * attached viewer drive the same PTY/page), so `open` retains and `close`
2294
+ * releases; the instance is only torn down when the last viewer detaches.
2295
+ * Without this, a single viewer's `close` would destroy the resource out from
2296
+ * under the others — and React StrictMode (dev) makes that routine: it
2297
+ * mounts → unmounts → remounts, so the first mount's late-resolving `open`
2298
+ * closes the very instance the remount just attached to, leaving `input`/
2299
+ * `resize` to hit a now-missing instance (output still flows from the
2300
+ * snapshot, but keystrokes/navigation silently vanish).
2301
+ */
2302
+ refs = /* @__PURE__ */ new Map();
2227
2303
  constructor(registry, ctx, logger) {
2228
2304
  this.registry = registry;
2229
2305
  this.ctx = ctx;
@@ -2256,28 +2332,25 @@ var init_host2 = __esm({
2256
2332
  async open(kind3) {
2257
2333
  const existing = this.instances.get(kind3);
2258
2334
  if (existing) {
2259
- return {
2260
- surfaceId: existing.id,
2261
- kind: existing.kind,
2262
- ...existing.snapshot ? { snapshot: existing.snapshot() } : {}
2263
- };
2335
+ this.retain(kind3);
2336
+ return this.describe(existing);
2264
2337
  }
2265
2338
  const pending2 = this.opening.get(kind3);
2266
- if (pending2)
2267
- return pending2;
2339
+ if (pending2) {
2340
+ const res = await pending2;
2341
+ this.retain(kind3);
2342
+ return res;
2343
+ }
2268
2344
  const def = this.registry.get(kind3);
2269
2345
  if (!def)
2270
2346
  throw new Error(`No surface registered for kind: ${kind3}`);
2271
2347
  const promise = (async () => {
2272
2348
  const instance = await def.open(this.ctx);
2273
2349
  this.instances.set(kind3, instance);
2350
+ this.refs.set(kind3, 1);
2274
2351
  const unsub = instance.onData((payload) => this.emit({ surfaceId: instance.id, kind: instance.kind, payload }));
2275
2352
  this.unsubs.set(instance.id, unsub);
2276
- return {
2277
- surfaceId: instance.id,
2278
- kind: instance.kind,
2279
- ...instance.snapshot ? { snapshot: instance.snapshot() } : {}
2280
- };
2353
+ return this.describe(instance);
2281
2354
  })();
2282
2355
  this.opening.set(kind3, promise);
2283
2356
  try {
@@ -2302,9 +2375,16 @@ var init_host2 = __esm({
2302
2375
  const instance = this.byId(surfaceId);
2303
2376
  if (!instance)
2304
2377
  return;
2378
+ const kind3 = instance.kind;
2379
+ const remaining = (this.refs.get(kind3) ?? 1) - 1;
2380
+ if (remaining > 0) {
2381
+ this.refs.set(kind3, remaining);
2382
+ return;
2383
+ }
2384
+ this.refs.delete(kind3);
2305
2385
  this.unsubs.get(surfaceId)?.();
2306
2386
  this.unsubs.delete(surfaceId);
2307
- this.instances.delete(instance.kind);
2387
+ this.instances.delete(kind3);
2308
2388
  try {
2309
2389
  await instance.close();
2310
2390
  } catch (err) {
@@ -2314,11 +2394,24 @@ var init_host2 = __esm({
2314
2394
  });
2315
2395
  }
2316
2396
  }
2397
+ /** Bump the viewer attach count for an already-open kind. */
2398
+ retain(kind3) {
2399
+ this.refs.set(kind3, (this.refs.get(kind3) ?? 0) + 1);
2400
+ }
2401
+ describe(instance) {
2402
+ return {
2403
+ surfaceId: instance.id,
2404
+ kind: instance.kind,
2405
+ ...instance.snapshot ? { snapshot: instance.snapshot() } : {}
2406
+ };
2407
+ }
2317
2408
  onData(cb) {
2318
2409
  this.dataListeners.add(cb);
2319
2410
  return () => this.dataListeners.delete(cb);
2320
2411
  }
2321
2412
  async closeAll() {
2413
+ for (const kind3 of [...this.instances.keys()])
2414
+ this.refs.set(kind3, 1);
2322
2415
  const ids = [...this.unsubs.keys()];
2323
2416
  for (const id of ids)
2324
2417
  await this.close(id);
@@ -3960,6 +4053,14 @@ var init_persistence = __esm({
3960
4053
  * can flush.
3961
4054
  */
3962
4055
  writeQueue = createMutex();
4056
+ /**
4057
+ * Memoized one-time setup: `mkdir -p` the sessions dir and create the empty
4058
+ * `.jsonl` (so resume lists the session before any event). Awaited by both
4059
+ * `attach` and `writeIndex` so an early debounced index flush can't race the
4060
+ * initial creation — but it runs the open+close syscalls ONCE, not on every
4061
+ * 250ms flush as before.
4062
+ */
4063
+ ready = null;
3963
4064
  closed = false;
3964
4065
  logger;
3965
4066
  /**
@@ -3997,7 +4098,7 @@ var init_persistence = __esm({
3997
4098
  * reuses the same Session object.
3998
4099
  */
3999
4100
  attach(log) {
4000
- void this.ensureDir().then(() => this.ensureLogFile()).then(() => this.scheduleIndexWrite()).catch(() => void 0);
4101
+ void this.ensureReady().then(() => this.scheduleIndexWrite()).catch(() => void 0);
4001
4102
  const unsub = log.subscribe((event) => {
4002
4103
  if (this.closed)
4003
4104
  return;
@@ -4112,19 +4213,27 @@ var init_persistence = __esm({
4112
4213
  async writeIndex() {
4113
4214
  try {
4114
4215
  if (!this.closed) {
4115
- await this.ensureDir();
4116
- await this.ensureLogFile();
4216
+ await this.ensureReady();
4117
4217
  }
4118
4218
  await writeJsonAtomic(metaPath(this.dir, this.meta.id), this.meta);
4119
4219
  } catch {
4120
4220
  }
4121
4221
  }
4122
- async ensureDir() {
4123
- await promises.mkdir(this.dir, { recursive: true });
4124
- }
4125
- async ensureLogFile() {
4126
- const handle2 = await promises.open(this.logPath, "a");
4127
- await handle2.close();
4222
+ /** One-time, memoized: `mkdir -p` the dir + create the empty `.jsonl`. On
4223
+ * failure the latch is cleared so a later flush retries (matching the old
4224
+ * per-write ensureDir/ensureLogFile recoverability). */
4225
+ ensureReady() {
4226
+ if (!this.ready) {
4227
+ this.ready = (async () => {
4228
+ await promises.mkdir(this.dir, { recursive: true });
4229
+ const handle2 = await promises.open(this.logPath, "a");
4230
+ await handle2.close();
4231
+ })().catch((err) => {
4232
+ this.ready = null;
4233
+ throw err;
4234
+ });
4235
+ }
4236
+ return this.ready;
4128
4237
  }
4129
4238
  };
4130
4239
  }
@@ -85197,35 +85306,40 @@ function handleSubagentEvent(e3, subagents, root, groupRef) {
85197
85306
  return;
85198
85307
  }
85199
85308
  }
85200
- function pairToolEvents(events, compactByName = EMPTY_COMPACT_MAP) {
85201
- const root = [];
85202
- const callTargets = /* @__PURE__ */ new Map();
85203
- const suppressedCallIds = /* @__PURE__ */ new Set();
85204
- let pendingLoadSkillCallId = null;
85205
- let openScope = null;
85206
- let continuationSkillEvent = null;
85207
- let openLive = null;
85208
- const subagents = /* @__PURE__ */ new Map();
85209
- const subagentGroup = { current: null };
85309
+ function createFoldState(compactByName = EMPTY_COMPACT_MAP) {
85310
+ return {
85311
+ root: [],
85312
+ compactByName,
85313
+ callTargets: /* @__PURE__ */ new Map(),
85314
+ suppressedCallIds: /* @__PURE__ */ new Set(),
85315
+ pendingLoadSkillCallId: null,
85316
+ openScope: null,
85317
+ continuationSkillEvent: null,
85318
+ openLive: null,
85319
+ subagents: /* @__PURE__ */ new Map(),
85320
+ subagentGroup: { current: null }
85321
+ };
85322
+ }
85323
+ function stepFold(s2, e3) {
85210
85324
  const pushBlock = (block) => {
85211
- subagentGroup.current = null;
85212
- if (openScope) {
85213
- openScope.children.push(block);
85325
+ s2.subagentGroup.current = null;
85326
+ if (s2.openScope) {
85327
+ s2.openScope.children.push(block);
85214
85328
  } else {
85215
- root.push(block);
85329
+ s2.root.push(block);
85216
85330
  }
85217
85331
  };
85218
85332
  const closeOpenLive = () => {
85219
- if (openLive) {
85220
- openLive.closed = true;
85221
- openLive = null;
85333
+ if (s2.openLive) {
85334
+ s2.openLive.closed = true;
85335
+ s2.openLive = null;
85222
85336
  }
85223
85337
  };
85224
85338
  const closeOpenScope = () => {
85225
85339
  closeOpenLive();
85226
- if (openScope) {
85227
- openScope.closed = true;
85228
- openScope = null;
85340
+ if (s2.openScope) {
85341
+ s2.openScope.closed = true;
85342
+ s2.openScope = null;
85229
85343
  }
85230
85344
  };
85231
85345
  const removeBlockByCallId = (callId) => {
@@ -85237,12 +85351,12 @@ function pairToolEvents(events, compactByName = EMPTY_COMPACT_MAP) {
85237
85351
  }
85238
85352
  return false;
85239
85353
  };
85240
- if (openScope && removeFrom(openScope.children))
85354
+ if (s2.openScope && removeFrom(s2.openScope.children))
85241
85355
  return;
85242
- removeFrom(root);
85356
+ removeFrom(s2.root);
85243
85357
  };
85244
85358
  const markOrphansAtTurnBoundary = () => {
85245
- for (const target of callTargets.values()) {
85359
+ for (const target of s2.callTargets.values()) {
85246
85360
  if (target.outcome === null) {
85247
85361
  target.outcome = {
85248
85362
  type: "denied",
@@ -85250,116 +85364,119 @@ function pairToolEvents(events, compactByName = EMPTY_COMPACT_MAP) {
85250
85364
  };
85251
85365
  }
85252
85366
  }
85253
- callTargets.clear();
85367
+ s2.callTargets.clear();
85254
85368
  };
85255
- for (const e3 of events) {
85256
- if (e3.type === "user_prompt") {
85257
- closeOpenScope();
85258
- markOrphansAtTurnBoundary();
85259
- pendingLoadSkillCallId = null;
85260
- continuationSkillEvent = null;
85261
- suppressedCallIds.clear();
85262
- subagentGroup.current = null;
85263
- root.push({ kind: "event", id: e3.id, event: e3 });
85264
- continue;
85369
+ if (e3.type === "user_prompt") {
85370
+ closeOpenScope();
85371
+ markOrphansAtTurnBoundary();
85372
+ s2.pendingLoadSkillCallId = null;
85373
+ s2.continuationSkillEvent = null;
85374
+ s2.suppressedCallIds.clear();
85375
+ s2.subagentGroup.current = null;
85376
+ s2.root.push({ kind: "event", id: e3.id, event: e3 });
85377
+ return;
85378
+ }
85379
+ if (e3.type === "skill_invoked") {
85380
+ closeOpenScope();
85381
+ s2.continuationSkillEvent = null;
85382
+ if (s2.pendingLoadSkillCallId) {
85383
+ s2.suppressedCallIds.add(s2.pendingLoadSkillCallId);
85384
+ removeBlockByCallId(s2.pendingLoadSkillCallId);
85385
+ s2.pendingLoadSkillCallId = null;
85386
+ }
85387
+ s2.subagentGroup.current = null;
85388
+ s2.openScope = {
85389
+ kind: "skill-scope",
85390
+ id: e3.id,
85391
+ skillEvent: e3,
85392
+ children: [],
85393
+ closed: false
85394
+ };
85395
+ s2.root.push(s2.openScope);
85396
+ return;
85397
+ }
85398
+ if (e3.type === "tool_call_requested") {
85399
+ if (e3.name === "load_skill") {
85400
+ s2.pendingLoadSkillCallId = e3.callId;
85265
85401
  }
85266
- if (e3.type === "skill_invoked") {
85267
- closeOpenScope();
85268
- continuationSkillEvent = null;
85269
- if (pendingLoadSkillCallId) {
85270
- suppressedCallIds.add(pendingLoadSkillCallId);
85271
- removeBlockByCallId(pendingLoadSkillCallId);
85272
- pendingLoadSkillCallId = null;
85273
- }
85274
- subagentGroup.current = null;
85275
- openScope = {
85402
+ if (!s2.openScope && s2.continuationSkillEvent) {
85403
+ s2.openScope = {
85276
85404
  kind: "skill-scope",
85277
- id: e3.id,
85278
- skillEvent: e3,
85405
+ id: `${s2.continuationSkillEvent.id}:cont:${e3.id}`,
85406
+ skillEvent: s2.continuationSkillEvent,
85279
85407
  children: [],
85280
85408
  closed: false
85281
85409
  };
85282
- root.push(openScope);
85283
- continue;
85284
- }
85285
- if (e3.type === "tool_call_requested") {
85286
- if (e3.name === "load_skill") {
85287
- pendingLoadSkillCallId = e3.callId;
85288
- }
85289
- if (!openScope && continuationSkillEvent) {
85290
- openScope = {
85291
- kind: "skill-scope",
85292
- id: `${continuationSkillEvent.id}:cont:${e3.id}`,
85293
- skillEvent: continuationSkillEvent,
85294
- children: [],
85295
- closed: false
85296
- };
85297
- root.push(openScope);
85298
- continuationSkillEvent = null;
85299
- }
85300
- const compact = FILE_DIFF_TOOL_NAMES.has(e3.name) ? void 0 : compactByName.get(e3.name);
85301
- if (compact) {
85302
- if (!openLive) {
85303
- openLive = { kind: "live-tools", id: e3.id, calls: [], closed: false };
85304
- pushBlock(openLive);
85305
- }
85306
- const call = { id: e3.id, request: e3, compact, outcome: null };
85307
- openLive.calls.push(call);
85308
- callTargets.set(e3.callId, call);
85309
- continue;
85310
- }
85311
- closeOpenLive();
85312
- const block = {
85313
- kind: "tool-call",
85314
- id: e3.id,
85315
- request: e3,
85316
- outcome: null
85317
- };
85318
- callTargets.set(e3.callId, block);
85319
- pushBlock(block);
85320
- continue;
85321
- }
85322
- if (e3.type === "tool_result") {
85323
- if (suppressedCallIds.has(e3.callId))
85324
- continue;
85325
- const target = callTargets.get(e3.callId);
85326
- if (target) {
85327
- target.outcome = e3;
85328
- continue;
85329
- }
85330
- }
85331
- if (e3.type === "tool_call_denied") {
85332
- if (suppressedCallIds.has(e3.callId))
85333
- continue;
85334
- const target = callTargets.get(e3.callId);
85335
- if (target) {
85336
- target.outcome = { type: "denied", reason: e3.reason };
85337
- continue;
85338
- }
85410
+ s2.root.push(s2.openScope);
85411
+ s2.continuationSkillEvent = null;
85412
+ }
85413
+ const compact = FILE_DIFF_TOOL_NAMES.has(e3.name) ? void 0 : s2.compactByName.get(e3.name);
85414
+ if (compact) {
85415
+ if (!s2.openLive) {
85416
+ s2.openLive = { kind: "live-tools", id: e3.id, calls: [], closed: false };
85417
+ pushBlock(s2.openLive);
85418
+ }
85419
+ const call = { id: e3.id, request: e3, compact, outcome: null };
85420
+ s2.openLive.calls.push(call);
85421
+ s2.callTargets.set(e3.callId, call);
85422
+ return;
85339
85423
  }
85340
- if (e3.type === "tool_call_approved") {
85341
- continue;
85424
+ closeOpenLive();
85425
+ const block = {
85426
+ kind: "tool-call",
85427
+ id: e3.id,
85428
+ request: e3,
85429
+ outcome: null
85430
+ };
85431
+ s2.callTargets.set(e3.callId, block);
85432
+ pushBlock(block);
85433
+ return;
85434
+ }
85435
+ if (e3.type === "tool_result") {
85436
+ if (s2.suppressedCallIds.has(e3.callId))
85437
+ return;
85438
+ const target = s2.callTargets.get(e3.callId);
85439
+ if (target) {
85440
+ target.outcome = e3;
85441
+ return;
85342
85442
  }
85343
- if (e3.type === "assistant_message") {
85344
- closeOpenLive();
85345
- if (openScope) {
85346
- continuationSkillEvent = openScope.skillEvent;
85347
- openScope.closed = true;
85348
- openScope = null;
85349
- } else {
85350
- continuationSkillEvent = null;
85351
- }
85352
- subagentGroup.current = null;
85353
- root.push({ kind: "event", id: e3.id, event: e3 });
85354
- continue;
85443
+ }
85444
+ if (e3.type === "tool_call_denied") {
85445
+ if (s2.suppressedCallIds.has(e3.callId))
85446
+ return;
85447
+ const target = s2.callTargets.get(e3.callId);
85448
+ if (target) {
85449
+ target.outcome = { type: "denied", reason: e3.reason };
85450
+ return;
85355
85451
  }
85356
- if (e3.type === "plugin_event" && e3.pluginId === SUBAGENT_PLUGIN_ID2) {
85357
- handleSubagentEvent(e3, subagents, root, subagentGroup);
85358
- continue;
85452
+ }
85453
+ if (e3.type === "tool_call_approved") {
85454
+ return;
85455
+ }
85456
+ if (e3.type === "assistant_message") {
85457
+ closeOpenLive();
85458
+ if (s2.openScope) {
85459
+ s2.continuationSkillEvent = s2.openScope.skillEvent;
85460
+ s2.openScope.closed = true;
85461
+ s2.openScope = null;
85462
+ } else {
85463
+ s2.continuationSkillEvent = null;
85359
85464
  }
85360
- pushBlock({ kind: "event", id: e3.id, event: e3 });
85465
+ s2.subagentGroup.current = null;
85466
+ s2.root.push({ kind: "event", id: e3.id, event: e3 });
85467
+ return;
85361
85468
  }
85362
- return root;
85469
+ if (e3.type === "plugin_event" && e3.pluginId === SUBAGENT_PLUGIN_ID2) {
85470
+ handleSubagentEvent(e3, s2.subagents, s2.root, s2.subagentGroup);
85471
+ return;
85472
+ }
85473
+ pushBlock({ kind: "event", id: e3.id, event: e3 });
85474
+ }
85475
+ function pairToolEvents(events, compactByName = EMPTY_COMPACT_MAP) {
85476
+ const state = createFoldState(compactByName);
85477
+ for (const e3 of events)
85478
+ stepFold(state, e3);
85479
+ return state.root;
85363
85480
  }
85364
85481
  function isSettled(block) {
85365
85482
  if (block.kind === "event")
@@ -85445,13 +85562,97 @@ function countToolCalls(blocks) {
85445
85562
  }
85446
85563
  return n2;
85447
85564
  }
85448
- var SUBAGENT_PLUGIN_ID2, FILE_DIFF_TOOL_NAMES, EMPTY_COMPACT_MAP;
85565
+ var SUBAGENT_PLUGIN_ID2, FILE_DIFF_TOOL_NAMES, EMPTY_COMPACT_MAP, IncrementalFold;
85449
85566
  var init_pair_events = __esm({
85450
85567
  "../chat-model/dist/pair-events.js"() {
85451
85568
  init_format();
85452
85569
  SUBAGENT_PLUGIN_ID2 = "@moxxy/subagents";
85453
85570
  FILE_DIFF_TOOL_NAMES = /* @__PURE__ */ new Set(["Write", "Edit"]);
85454
85571
  EMPTY_COMPACT_MAP = /* @__PURE__ */ new Map();
85572
+ IncrementalFold = class {
85573
+ state;
85574
+ prefixLength = 0;
85575
+ rev = 0;
85576
+ /** `id` of the first / last event folded so far — used to detect when the
85577
+ * source array's prefix has shifted (a scroll-up prepend) or been replaced
85578
+ * (/clear, a fresh session), in which case the carried fold state is no
85579
+ * longer valid and we must rebuild from scratch. */
85580
+ headId = null;
85581
+ tailId = null;
85582
+ constructor(compactByName = EMPTY_COMPACT_MAP) {
85583
+ this.state = createFoldState(compactByName);
85584
+ }
85585
+ /** Number of events folded so far (the high-water mark). */
85586
+ get length() {
85587
+ return this.prefixLength;
85588
+ }
85589
+ /** Bumps whenever the folded tree may have changed (every `push`). Use as
85590
+ * a memo key instead of the (stable) `tree()` reference. */
85591
+ get version() {
85592
+ return this.rev;
85593
+ }
85594
+ /** Fold one freshly-committed event onto the existing tree. */
85595
+ push(event) {
85596
+ stepFold(this.state, event);
85597
+ if (this.prefixLength === 0)
85598
+ this.headId = event.id;
85599
+ this.tailId = event.id;
85600
+ this.prefixLength += 1;
85601
+ this.rev += 1;
85602
+ }
85603
+ /** Fold a batch of newly-committed events (e.g. a replayed page). */
85604
+ pushMany(events) {
85605
+ for (const e3 of events)
85606
+ this.push(e3);
85607
+ }
85608
+ /**
85609
+ * Re-sync to `events` when the source array is the authoritative log. Folds
85610
+ * only the tail past the current high-water mark when `events` extends the
85611
+ * already-folded prefix unchanged (the common live-append case), and
85612
+ * rebuilds from scratch only when that prefix shifted or was replaced (a
85613
+ * scroll-up prepend, /clear, a fresh session). Returns the (stable) root.
85614
+ *
85615
+ * Prefix-unchanged is detected by event `id`: the log never rewrites a
85616
+ * settled event in place (only its tool outcome, which the fold owns), so
85617
+ * matching head+tail ids over an unshrunk length proves the leading
85618
+ * `prefixLength` events are exactly the ones already folded.
85619
+ */
85620
+ syncTo(events) {
85621
+ if (this.canExtend(events)) {
85622
+ for (let i2 = this.prefixLength; i2 < events.length; i2 += 1)
85623
+ this.push(events[i2]);
85624
+ return this.state.root;
85625
+ }
85626
+ this.reset();
85627
+ this.pushMany(events);
85628
+ return this.state.root;
85629
+ }
85630
+ /** Discard all state — folds again from empty. */
85631
+ reset() {
85632
+ this.state = createFoldState(this.state.compactByName);
85633
+ this.prefixLength = 0;
85634
+ this.headId = null;
85635
+ this.tailId = null;
85636
+ this.rev += 1;
85637
+ }
85638
+ /** The folded block tree (stable reference, mutated in place). */
85639
+ tree() {
85640
+ return this.state.root;
85641
+ }
85642
+ /** True when `events` is the already-folded prefix plus zero or more new
85643
+ * tail events — i.e. a pure append. Requires the head id to still match
85644
+ * (no prepend) and the event at `prefixLength-1` to be the last one we
85645
+ * folded (no in-place rewrite or replacement of the prefix). */
85646
+ canExtend(events) {
85647
+ if (this.prefixLength === 0)
85648
+ return true;
85649
+ if (events.length < this.prefixLength)
85650
+ return false;
85651
+ if (events[0].id !== this.headId)
85652
+ return false;
85653
+ return events[this.prefixLength - 1].id === this.tailId;
85654
+ }
85655
+ };
85455
85656
  }
85456
85657
  });
85457
85658
 
@@ -86090,6 +86291,31 @@ var init_BlockLine = __esm({
86090
86291
  });
86091
86292
 
86092
86293
  // ../plugin-cli/dist/components/chat/StreamingPreview.js
86294
+ function lastNonEmptyLineShown(content, innerCols) {
86295
+ let end = content.length;
86296
+ let chosenStart = -1;
86297
+ let chosenEnd = -1;
86298
+ while (end > 0) {
86299
+ const nl = content.lastIndexOf("\n", end - 1);
86300
+ const start = nl + 1;
86301
+ if (content.slice(start, end).trim()) {
86302
+ chosenStart = start;
86303
+ chosenEnd = end;
86304
+ break;
86305
+ }
86306
+ if (start === 0)
86307
+ break;
86308
+ end = nl;
86309
+ }
86310
+ let line;
86311
+ if (chosenStart >= 0) {
86312
+ line = content.slice(chosenStart, chosenEnd);
86313
+ } else {
86314
+ const lastNl = content.lastIndexOf("\n");
86315
+ line = lastNl < 0 ? content : content.slice(lastNl + 1);
86316
+ }
86317
+ return line.length > innerCols ? `\u2026${line.slice(line.length - (innerCols - 1))}` : line;
86318
+ }
86093
86319
  function tailForViewport(content) {
86094
86320
  return content;
86095
86321
  }
@@ -86103,17 +86329,7 @@ var init_StreamingPreview = __esm({
86103
86329
  StreamingPreview = (0, import_react40.memo)(function StreamingPreview2({ content, dim: dim3 }) {
86104
86330
  const cols = process.stdout.columns ?? 80;
86105
86331
  const innerCols = Math.max(20, cols - 4);
86106
- const lines = content.split("\n");
86107
- let line = "";
86108
- for (let i2 = lines.length - 1; i2 >= 0; i2 -= 1) {
86109
- if (lines[i2].trim()) {
86110
- line = lines[i2];
86111
- break;
86112
- }
86113
- }
86114
- if (!line)
86115
- line = lines[lines.length - 1] ?? "";
86116
- const shown = line.length > innerCols ? `\u2026${line.slice(line.length - (innerCols - 1))}` : line;
86332
+ const shown = lastNonEmptyLineShown(content, innerCols);
86117
86333
  return (0, import_jsx_runtime19.jsxs)(Box_default, { flexDirection: "row", marginTop: 1, children: [(0, import_jsx_runtime19.jsx)(Box_default, { marginRight: 1, children: (0, import_jsx_runtime19.jsx)(Text, { dimColor: true, children: Glyphs.filled }) }), (0, import_jsx_runtime19.jsx)(Text, { dimColor: dim3, children: shown || " " })] });
86118
86334
  });
86119
86335
  }
@@ -86130,16 +86346,26 @@ var init_ChatView = __esm({
86130
86346
  init_dist8();
86131
86347
  await init_StreamingPreview();
86132
86348
  ChatView = ({ events, streamingDelta, reasoningDelta, expandToolOutputs, compactTools, hideLive }) => {
86133
- const blocks = (0, import_react41.useMemo)(() => pairToolEvents(events, compactTools), [events, compactTools]);
86349
+ const foldRef = (0, import_react41.useRef)(null);
86350
+ const compactRef = (0, import_react41.useRef)(void 0);
86351
+ const blocks = (0, import_react41.useMemo)(() => {
86352
+ if (typeof IncrementalFold !== "function")
86353
+ return pairToolEvents(events, compactTools);
86354
+ if (!foldRef.current || compactRef.current !== compactTools) {
86355
+ foldRef.current = new IncrementalFold(compactTools);
86356
+ compactRef.current = compactTools;
86357
+ }
86358
+ return foldRef.current.syncTo(events).slice();
86359
+ }, [events, compactTools]);
86134
86360
  const settledRef = (0, import_react41.useRef)([]);
86135
86361
  const clearGenerationRef = (0, import_react41.useRef)(0);
86136
86362
  if (blocks.length < settledRef.current.length) {
86137
86363
  settledRef.current = [];
86138
86364
  clearGenerationRef.current += 1;
86139
86365
  }
86140
- let settledCount = 0;
86141
- for (const b3 of blocks) {
86142
- if (isSettled(b3))
86366
+ let settledCount = settledRef.current.length;
86367
+ for (let i2 = settledRef.current.length; i2 < blocks.length; i2 += 1) {
86368
+ if (isSettled(blocks[i2]))
86143
86369
  settledCount += 1;
86144
86370
  else
86145
86371
  break;
@@ -88213,6 +88439,13 @@ function modelBreakdownRows(file, liveByModel) {
88213
88439
  output: m3.outputTokens
88214
88440
  })).sort((a2, b3) => b3.prompt - a2.prompt);
88215
88441
  }
88442
+ function peak(series, seed = 0) {
88443
+ let m3 = seed;
88444
+ for (const v3 of series)
88445
+ if (v3 > m3)
88446
+ m3 = v3;
88447
+ return m3;
88448
+ }
88216
88449
  function perCallPrompt(events) {
88217
88450
  const out = [];
88218
88451
  for (const e3 of events) {
@@ -88297,7 +88530,7 @@ var init_UsagePanel = __esm({
88297
88530
  const subtitle = hasSession ? `${s2.calls} calls \xB7 ${fmt(s2.totalPrompt)} prompt \xB7 ${fmt(s2.totalOutput)} output` : "saved across sessions";
88298
88531
  const showSession = hasSession && (!tabs || activeTab === "session");
88299
88532
  const showLifetime = hasModels && (!tabs || activeTab === "lifetime");
88300
- return (0, import_jsx_runtime28.jsxs)(Modal, { title: "Usage", subtitle, ...tabs ? { tabs, activeTabId: activeTab, onTabChange: (id) => setActiveTab(id) } : {}, ...onClose ? { onClose } : {}, children: [showSession ? (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [(0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "Prompt composition" }), (0, import_jsx_runtime28.jsx)(CompRow, { label: "cache read", frac: readFrac, value: s2.totalCacheRead, color: Colors.active }), (0, import_jsx_runtime28.jsx)(CompRow, { label: "fresh input", frac: freshFrac, value: s2.totalInput }), (0, import_jsx_runtime28.jsx)(CompRow, { label: "cache write", frac: writeFrac, value: s2.totalCacheCreation, color: Colors.busy }), (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [(0, import_jsx_runtime28.jsx)(MetricRow, { label: "Cache hit", frac: s2.cacheHitRate, color: s2.cacheHitRate >= 0.5 ? Colors.active : Colors.busy }), ctxFrac != null ? (0, import_jsx_runtime28.jsx)(MetricRow, { label: "Context fill", frac: ctxFrac, color: ctxColor, suffix: `${fmt(contextTokens ?? 0)} / ${fmt(contextWindow ?? 0)}` }) : null] }), (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: 1, children: [(0, import_jsx_runtime28.jsx)(Box_default, { width: LABEL_COL, children: (0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "Input cost" }) }), (0, import_jsx_runtime28.jsxs)(Text, { children: [fmt(s2.billedInputEq), " billed-eq"] }), saved > 5e-3 ? (0, import_jsx_runtime28.jsx)(Text, { color: Colors.active, bold: true, children: ` saved ${pct(saved)}` }) : (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: " no cache savings yet" })] }), (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [(0, import_jsx_runtime28.jsxs)(Box_default, { children: [(0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "Per-call prompt " }), (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: `peak ${fmt(Math.max(...series, 0))}` })] }), (0, import_jsx_runtime28.jsxs)(Box_default, { children: [(0, import_jsx_runtime28.jsx)(Text, { children: sparkline(series) }), trend ? (0, import_jsx_runtime28.jsx)(Text, { color: trend === "growing" ? Colors.busy : Colors.active, children: trend === "growing" ? " \u2191 growing" : " \u2248 bounded" }) : null] })] }), !s2.cacheEffective ? (0, import_jsx_runtime28.jsx)(Box_default, { marginTop: 1, children: (0, import_jsx_runtime28.jsx)(Text, { color: Colors.danger, children: "\u26A0 cache ineffective \u2014 writing cache but not reading it back (prefix likely unstable)" }) }) : null] }) : null, showLifetime ? (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: showSession ? 1 : 0, flexDirection: "column", children: [(0, import_jsx_runtime28.jsxs)(Box_default, { children: [(0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "By model " }), (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: `(saved + this session \xB7 ${lifeRows.length} model${lifeRows.length === 1 ? "" : "s"})` })] }), lifeRows.slice(0, 8).map((r2) => (0, import_jsx_runtime28.jsx)(ModelRow, { name: r2.name, calls: r2.calls, prompt: r2.prompt, output: r2.output }, r2.name)), lifeRows.length > 1 ? (0, import_jsx_runtime28.jsx)(ModelRow, { name: "total", calls: lifeTotal.calls, prompt: lifeTotal.prompt, output: lifeTotal.output, dim: true }) : null, (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: " /usage clear resets saved history" })] }) : null] });
88533
+ return (0, import_jsx_runtime28.jsxs)(Modal, { title: "Usage", subtitle, ...tabs ? { tabs, activeTabId: activeTab, onTabChange: (id) => setActiveTab(id) } : {}, ...onClose ? { onClose } : {}, children: [showSession ? (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [(0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "Prompt composition" }), (0, import_jsx_runtime28.jsx)(CompRow, { label: "cache read", frac: readFrac, value: s2.totalCacheRead, color: Colors.active }), (0, import_jsx_runtime28.jsx)(CompRow, { label: "fresh input", frac: freshFrac, value: s2.totalInput }), (0, import_jsx_runtime28.jsx)(CompRow, { label: "cache write", frac: writeFrac, value: s2.totalCacheCreation, color: Colors.busy }), (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [(0, import_jsx_runtime28.jsx)(MetricRow, { label: "Cache hit", frac: s2.cacheHitRate, color: s2.cacheHitRate >= 0.5 ? Colors.active : Colors.busy }), ctxFrac != null ? (0, import_jsx_runtime28.jsx)(MetricRow, { label: "Context fill", frac: ctxFrac, color: ctxColor, suffix: `${fmt(contextTokens ?? 0)} / ${fmt(contextWindow ?? 0)}` }) : null] }), (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: 1, children: [(0, import_jsx_runtime28.jsx)(Box_default, { width: LABEL_COL, children: (0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "Input cost" }) }), (0, import_jsx_runtime28.jsxs)(Text, { children: [fmt(s2.billedInputEq), " billed-eq"] }), saved > 5e-3 ? (0, import_jsx_runtime28.jsx)(Text, { color: Colors.active, bold: true, children: ` saved ${pct(saved)}` }) : (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: " no cache savings yet" })] }), (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [(0, import_jsx_runtime28.jsxs)(Box_default, { children: [(0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "Per-call prompt " }), (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: `peak ${fmt(peak(series))}` })] }), (0, import_jsx_runtime28.jsxs)(Box_default, { children: [(0, import_jsx_runtime28.jsx)(Text, { children: sparkline(series) }), trend ? (0, import_jsx_runtime28.jsx)(Text, { color: trend === "growing" ? Colors.busy : Colors.active, children: trend === "growing" ? " \u2191 growing" : " \u2248 bounded" }) : null] })] }), !s2.cacheEffective ? (0, import_jsx_runtime28.jsx)(Box_default, { marginTop: 1, children: (0, import_jsx_runtime28.jsx)(Text, { color: Colors.danger, children: "\u26A0 cache ineffective \u2014 writing cache but not reading it back (prefix likely unstable)" }) }) : null] }) : null, showLifetime ? (0, import_jsx_runtime28.jsxs)(Box_default, { marginTop: showSession ? 1 : 0, flexDirection: "column", children: [(0, import_jsx_runtime28.jsxs)(Box_default, { children: [(0, import_jsx_runtime28.jsx)(Text, { bold: true, children: "By model " }), (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: `(saved + this session \xB7 ${lifeRows.length} model${lifeRows.length === 1 ? "" : "s"})` })] }), lifeRows.slice(0, 8).map((r2) => (0, import_jsx_runtime28.jsx)(ModelRow, { name: r2.name, calls: r2.calls, prompt: r2.prompt, output: r2.output }, r2.name)), lifeRows.length > 1 ? (0, import_jsx_runtime28.jsx)(ModelRow, { name: "total", calls: lifeTotal.calls, prompt: lifeTotal.prompt, output: lifeTotal.output, dim: true }) : null, (0, import_jsx_runtime28.jsx)(Text, { dimColor: true, children: " /usage clear resets saved history" })] }) : null] });
88301
88534
  };
88302
88535
  }
88303
88536
  });
@@ -129343,6 +129576,15 @@ async function* walk(root, regex2, cursor, signal, visited) {
129343
129576
  }
129344
129577
  }
129345
129578
  }
129579
+ var MAX_FILE_BYTES = 10 * 1024 * 1024;
129580
+ function looksBinary(content) {
129581
+ const limit2 = Math.min(content.length, 8192);
129582
+ for (let i2 = 0; i2 < limit2; i2 += 1) {
129583
+ if (content.charCodeAt(i2) === 0)
129584
+ return true;
129585
+ }
129586
+ return false;
129587
+ }
129346
129588
  var grepTool = defineTool({
129347
129589
  name: "Grep",
129348
129590
  description: "Recursively search files for a regex pattern. Returns lines as `path:line:text`.",
@@ -129406,12 +129648,21 @@ async function walk2(root, cursor, re2, fileRe, matches, max, signal) {
129406
129648
  continue;
129407
129649
  if (fileRe && !fileRe.test(entry.name))
129408
129650
  continue;
129651
+ try {
129652
+ const st3 = await promises.stat(full);
129653
+ if (st3.size > MAX_FILE_BYTES)
129654
+ continue;
129655
+ } catch {
129656
+ continue;
129657
+ }
129409
129658
  let content;
129410
129659
  try {
129411
129660
  content = await promises.readFile(full, "utf8");
129412
129661
  } catch {
129413
129662
  continue;
129414
129663
  }
129664
+ if (looksBinary(content))
129665
+ continue;
129415
129666
  const lines = content.split("\n");
129416
129667
  for (let i2 = 0; i2 < lines.length; i2++) {
129417
129668
  if (re2.test(lines[i2])) {
@@ -135706,9 +135957,38 @@ var workflowResumeParamsSchema = z.object({
135706
135957
  var surfaceOpenParamsSchema = z.object({
135707
135958
  kind: z.string().min(1).max(64)
135708
135959
  });
135960
+ var MAX_SURFACE_INPUT_BYTES = 1e6;
135961
+ function surfaceInputWithinCap(m3) {
135962
+ let upper = 2;
135963
+ let primitiveOnly = true;
135964
+ for (const key in m3) {
135965
+ if (!Object.prototype.hasOwnProperty.call(m3, key))
135966
+ continue;
135967
+ const v3 = m3[key];
135968
+ const t2 = typeof v3;
135969
+ if (v3 === void 0 || t2 === "function")
135970
+ continue;
135971
+ upper += key.length * 6 + 2 + 1 + 1;
135972
+ if (t2 === "string") {
135973
+ upper += v3.length * 6 + 2;
135974
+ } else if (t2 === "number") {
135975
+ upper += 25;
135976
+ } else if (t2 === "boolean" || v3 === null) {
135977
+ upper += 5;
135978
+ } else {
135979
+ primitiveOnly = false;
135980
+ break;
135981
+ }
135982
+ if (upper > MAX_SURFACE_INPUT_BYTES)
135983
+ break;
135984
+ }
135985
+ if (primitiveOnly && upper <= MAX_SURFACE_INPUT_BYTES)
135986
+ return true;
135987
+ return JSON.stringify(m3).length <= MAX_SURFACE_INPUT_BYTES;
135988
+ }
135709
135989
  var surfaceInputParamsSchema = z.object({
135710
135990
  surfaceId: z.string().min(1).max(120),
135711
- message: z.object({ type: z.string().min(1).max(64) }).passthrough().refine((m3) => JSON.stringify(m3).length <= 1e6, {
135991
+ message: z.object({ type: z.string().min(1).max(64) }).passthrough().refine((m3) => surfaceInputWithinCap(m3), {
135712
135992
  message: "surface input message too large"
135713
135993
  })
135714
135994
  });
@@ -138600,6 +138880,8 @@ function defaultShell() {
138600
138880
  return process.env["COMSPEC"] ?? "powershell.exe";
138601
138881
  return process.env["SHELL"] ?? "/bin/bash";
138602
138882
  }
138883
+ var MAX_SCROLLBACK = 2e5;
138884
+ var SCROLLBACK_SLACK = 1e5;
138603
138885
  var TerminalProcessImpl = class {
138604
138886
  backend;
138605
138887
  pty;
@@ -138623,7 +138905,10 @@ var TerminalProcessImpl = class {
138623
138905
  }
138624
138906
  }
138625
138907
  emitData(d2) {
138626
- this.buffer = (this.buffer + d2).slice(-2e5);
138908
+ this.buffer += d2;
138909
+ if (this.buffer.length > MAX_SCROLLBACK + SCROLLBACK_SLACK) {
138910
+ this.buffer = this.buffer.slice(-MAX_SCROLLBACK);
138911
+ }
138627
138912
  for (const cb of this.dataListeners) {
138628
138913
  try {
138629
138914
  cb(d2);
@@ -138651,7 +138936,7 @@ var TerminalProcessImpl = class {
138651
138936
  return () => this.exitListeners.delete(cb);
138652
138937
  }
138653
138938
  scrollback() {
138654
- return this.buffer;
138939
+ return this.buffer.length > MAX_SCROLLBACK ? this.buffer.slice(-MAX_SCROLLBACK) : this.buffer;
138655
138940
  }
138656
138941
  write(data) {
138657
138942
  if (!this.alive)
@@ -138798,11 +139083,17 @@ function runCommand(proc, command, marker, timeoutMs) {
138798
139083
  clearTimeout(timer);
138799
139084
  resolve13({ output: cleanOutput(acc, command, marker), exitCode, timedOut });
138800
139085
  };
139086
+ const sentinel2 = new RegExp(`${marker} (\\d+)`);
139087
+ const carry = marker.length + 32;
139088
+ let scanFrom = 0;
138801
139089
  const unsub = proc.onData((d2) => {
139090
+ const from = Math.max(scanFrom, acc.length - carry, 0);
138802
139091
  acc += d2;
138803
- const m3 = new RegExp(`${marker} (\\d+)`).exec(acc);
139092
+ sentinel2.lastIndex = 0;
139093
+ const m3 = sentinel2.exec(from > 0 ? acc.slice(from) : acc);
138804
139094
  if (m3)
138805
139095
  finish(Number(m3[1]), false);
139096
+ scanFrom = Math.max(0, acc.length - carry);
138806
139097
  });
138807
139098
  const timer = setTimeout(() => finish(null, true), timeoutMs);
138808
139099
  proc.write(`${command}
@@ -139859,7 +140150,7 @@ var IS_DARWIN = process.platform === "darwin";
139859
140150
  function runProcess(cmd, args, opts = {}) {
139860
140151
  return new Promise((resolve13, reject) => {
139861
140152
  const child = spawn(cmd, [...args], { stdio: ["pipe", "pipe", "pipe"] });
139862
- let stdout = Buffer.alloc(0);
140153
+ const stdoutChunks = [];
139863
140154
  let stderr = "";
139864
140155
  let settled = false;
139865
140156
  const onAbort = () => {
@@ -139880,7 +140171,7 @@ function runProcess(cmd, args, opts = {}) {
139880
140171
  }
139881
140172
  }, opts.timeoutMs) : null;
139882
140173
  child.stdout.on("data", (chunk) => {
139883
- stdout = Buffer.concat([stdout, chunk]);
140174
+ stdoutChunks.push(chunk);
139884
140175
  });
139885
140176
  child.stderr.on("data", (chunk) => {
139886
140177
  stderr += chunk.toString("utf8");
@@ -139903,7 +140194,7 @@ function runProcess(cmd, args, opts = {}) {
139903
140194
  opts.signal?.removeEventListener("abort", onAbort);
139904
140195
  resolve13({
139905
140196
  exitCode: code ?? -1,
139906
- stdout: stdout.toString("utf8"),
140197
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
139907
140198
  stderr
139908
140199
  });
139909
140200
  });
@@ -140552,43 +140843,7 @@ async function syncSkillSchedules(registry, store) {
140552
140843
  if (draft)
140553
140844
  wanted.set(skill.frontmatter.name, draft);
140554
140845
  }
140555
- const existing = await store.list();
140556
- const existingSkill = /* @__PURE__ */ new Map();
140557
- for (const e3 of existing) {
140558
- if (e3.source === "skill" && e3.skillName)
140559
- existingSkill.set(e3.skillName, e3);
140560
- }
140561
- let added = 0;
140562
- let removed = 0;
140563
- let updated = 0;
140564
- for (const [skillName2, entry] of existingSkill) {
140565
- if (!wanted.has(skillName2)) {
140566
- const ok = await store.delete(entry.id);
140567
- if (ok)
140568
- removed += 1;
140569
- }
140570
- }
140571
- for (const [skillName2, draft] of wanted) {
140572
- const current = existingSkill.get(skillName2);
140573
- if (!current) {
140574
- await store.create(draft);
140575
- added += 1;
140576
- continue;
140577
- }
140578
- const changed = current.prompt !== draft.prompt || current.cron !== draft.cron || current.runAt !== draft.runAt || current.timeZone !== draft.timeZone || current.channel !== draft.channel || current.enabled !== draft.enabled;
140579
- if (changed) {
140580
- await store.update(current.id, {
140581
- prompt: draft.prompt,
140582
- ...draft.cron ? { cron: draft.cron } : { cron: void 0 },
140583
- ...draft.runAt !== void 0 ? { runAt: draft.runAt } : { runAt: void 0 },
140584
- ...draft.timeZone ? { timeZone: draft.timeZone } : { timeZone: void 0 },
140585
- ...draft.channel ? { channel: draft.channel } : { channel: void 0 },
140586
- enabled: draft.enabled
140587
- });
140588
- updated += 1;
140589
- }
140590
- }
140591
- return { added, removed, updated };
140846
+ return store.reconcileSkillSchedules(wanted);
140592
140847
  }
140593
140848
 
140594
140849
  // ../plugin-scheduler/dist/poller.js
@@ -140820,6 +141075,79 @@ var ScheduleStore = class {
140820
141075
  });
140821
141076
  return removed;
140822
141077
  }
141078
+ /**
141079
+ * Batch-reconcile every `source='skill'` row against `wanted` (skillName →
141080
+ * desired draft) in a SINGLE atomic write, instead of one whole-file
141081
+ * serialization + fsync per changed row. The diff (and the resulting array)
141082
+ * is byte-identical to running the equivalent sequence of
141083
+ * create/update/delete calls:
141084
+ * - skill rows whose skillName is absent from `wanted` are removed;
141085
+ * - present skillNames with no existing row are created (fresh id +
141086
+ * createdAt), appended in `wanted` iteration order;
141087
+ * - present skillNames with an existing row are updated IN PLACE (id,
141088
+ * createdAt, position preserved) only when a field actually changed.
141089
+ * Returns the add/remove/update counts so the caller's telemetry is
141090
+ * unchanged. Manual/workflow rows are never touched.
141091
+ */
141092
+ async reconcileSkillSchedules(wanted) {
141093
+ let added = 0;
141094
+ let removed = 0;
141095
+ let updated = 0;
141096
+ await this.store.mutate((schedules) => {
141097
+ const existingByName = /* @__PURE__ */ new Map();
141098
+ for (let i2 = 0; i2 < schedules.length; i2 += 1) {
141099
+ const s2 = schedules[i2];
141100
+ if (s2.source === "skill" && s2.skillName && !existingByName.has(s2.skillName)) {
141101
+ existingByName.set(s2.skillName, i2);
141102
+ }
141103
+ }
141104
+ const next = [];
141105
+ for (const s2 of schedules) {
141106
+ if (s2.source === "skill" && s2.skillName && !wanted.has(s2.skillName)) {
141107
+ removed += 1;
141108
+ continue;
141109
+ }
141110
+ next.push(s2);
141111
+ }
141112
+ const posInNext = /* @__PURE__ */ new Map();
141113
+ for (let i2 = 0; i2 < next.length; i2 += 1) {
141114
+ const s2 = next[i2];
141115
+ if (s2.source === "skill" && s2.skillName && !posInNext.has(s2.skillName)) {
141116
+ posInNext.set(s2.skillName, i2);
141117
+ }
141118
+ }
141119
+ for (const [skillName2, draft] of wanted) {
141120
+ const idx = posInNext.get(skillName2);
141121
+ if (idx === void 0) {
141122
+ next.push(scheduleEntrySchema.parse({
141123
+ ...draft,
141124
+ id: ulid(),
141125
+ createdAt: Date.now(),
141126
+ enabled: draft.enabled ?? true,
141127
+ source: draft.source ?? "skill"
141128
+ }));
141129
+ added += 1;
141130
+ continue;
141131
+ }
141132
+ const current = next[idx];
141133
+ const patch = {
141134
+ prompt: draft.prompt,
141135
+ ...draft.cron ? { cron: draft.cron } : { cron: void 0 },
141136
+ ...draft.runAt !== void 0 ? { runAt: draft.runAt } : { runAt: void 0 },
141137
+ ...draft.timeZone ? { timeZone: draft.timeZone } : { timeZone: void 0 },
141138
+ ...draft.channel ? { channel: draft.channel } : { channel: void 0 },
141139
+ enabled: draft.enabled ?? true
141140
+ };
141141
+ const changed = current.prompt !== patch.prompt || current.cron !== patch.cron || current.runAt !== patch.runAt || current.timeZone !== patch.timeZone || current.channel !== patch.channel || current.enabled !== patch.enabled;
141142
+ if (changed) {
141143
+ next[idx] = scheduleEntrySchema.parse({ ...current, ...patch });
141144
+ updated += 1;
141145
+ }
141146
+ }
141147
+ return next;
141148
+ });
141149
+ return { added, removed, updated };
141150
+ }
140823
141151
  /**
140824
141152
  * Replace every `source='skill'` schedule for the given `skillName`
140825
141153
  * with the supplied entry, OR remove all of them if `entry` is null.
@@ -141259,14 +141587,17 @@ function readHeader(headers, name) {
141259
141587
  return v3[0] ?? null;
141260
141588
  return v3 ?? null;
141261
141589
  }
141262
- function readJsonPath(body, path60) {
141263
- let parsed;
141590
+ function parseBody(body) {
141264
141591
  try {
141265
- parsed = JSON.parse(body.toString("utf8"));
141592
+ return { ok: true, value: JSON.parse(body.toString("utf8")) };
141266
141593
  } catch {
141267
- return null;
141594
+ return { ok: false, value: null };
141268
141595
  }
141269
- let cur = parsed;
141596
+ }
141597
+ function readJsonPath(parsed, path60) {
141598
+ if (!parsed.ok)
141599
+ return null;
141600
+ let cur = parsed.value;
141270
141601
  for (const seg of path60.split(".")) {
141271
141602
  if (cur === null || cur === void 0 || typeof cur !== "object")
141272
141603
  return null;
@@ -141274,8 +141605,8 @@ function readJsonPath(body, path60) {
141274
141605
  }
141275
141606
  return asString2(cur);
141276
141607
  }
141277
- function ruleMatches(rule, input) {
141278
- const value = rule.source === "header" ? readHeader(input.headers, rule.name) : readJsonPath(input.body, rule.path);
141608
+ function ruleMatches(rule, input, parsed) {
141609
+ const value = rule.source === "header" ? readHeader(input.headers, rule.name) : readJsonPath(parsed, rule.path);
141279
141610
  if (value === null)
141280
141611
  return false;
141281
141612
  if (rule.equals && rule.equals.includes(value))
@@ -141291,11 +141622,12 @@ function ruleMatches(rule, input) {
141291
141622
  return false;
141292
141623
  }
141293
141624
  function shouldFire(filter, input) {
141294
- if (filter.exclude.some((r2) => ruleMatches(r2, input)))
141625
+ const parsed = parseBody(input.body);
141626
+ if (filter.exclude.some((r2) => ruleMatches(r2, input, parsed)))
141295
141627
  return false;
141296
141628
  if (filter.include.length === 0)
141297
141629
  return true;
141298
- return filter.include.some((r2) => ruleMatches(r2, input));
141630
+ return filter.include.some((r2) => ruleMatches(r2, input, parsed));
141299
141631
  }
141300
141632
 
141301
141633
  // ../plugin-webhooks/dist/template.js
@@ -145461,7 +145793,7 @@ function buildSubagentSpecWithDeps(step, scope, deps, opts) {
145461
145793
  }
145462
145794
  var dagExecutor = defineWorkflowExecutor({
145463
145795
  name: DAG_EXECUTOR_NAME,
145464
- description: "Parallel DAG runner: steps with settled dependencies run in waves up to `concurrency`.",
145796
+ description: "DAG runner: steps with settled dependencies are scheduled in waves of up to `concurrency` ready steps, then executed sequentially within each wave (no overlap yet \u2014 `concurrency` caps the batch size, not wall-clock latency).",
145465
145797
  run: runExecutor
145466
145798
  });
145467
145799