@nowcrew/daemon 0.5.30 → 0.5.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,91 @@
1
+ export function createRuntimeStartupGate(limits, now = Date.now) {
2
+ const activeByRuntime = new Map();
3
+ const queue = [];
4
+ let activeTotal = 0;
5
+ let lastLaunchAt = Number.NEGATIVE_INFINITY;
6
+ const promote = () => {
7
+ while (activeTotal < limits.maxStartingTotal) {
8
+ const index = queue.findIndex((entry) => !entry.released
9
+ && (activeByRuntime.get(entry.runtime) ?? 0) < limits.maxStartingPerRuntime);
10
+ if (index < 0)
11
+ return;
12
+ const [next] = queue.splice(index, 1);
13
+ if (!next || next.released)
14
+ continue;
15
+ next.promoted = true;
16
+ activeTotal += 1;
17
+ activeByRuntime.set(next.runtime, (activeByRuntime.get(next.runtime) ?? 0) + 1);
18
+ const grant = () => {
19
+ next.timer = null;
20
+ if (next.released)
21
+ return;
22
+ next.launchGranted = true;
23
+ lastLaunchAt = now();
24
+ next.resolve();
25
+ };
26
+ const delay = Math.max(0, lastLaunchAt + limits.startupGapMs - now());
27
+ if (delay === 0)
28
+ grant();
29
+ else
30
+ next.timer = setTimeout(grant, delay);
31
+ }
32
+ };
33
+ return {
34
+ reserve: (runtime) => {
35
+ let resolveReady;
36
+ const ready = new Promise((resolve) => { resolveReady = resolve; });
37
+ const entry = {
38
+ runtime,
39
+ released: false,
40
+ promoted: false,
41
+ launchGranted: false,
42
+ timer: null,
43
+ resolve: resolveReady,
44
+ };
45
+ queue.push(entry);
46
+ promote();
47
+ return {
48
+ ready,
49
+ isQueued: () => !entry.launchGranted && !entry.released,
50
+ release: () => {
51
+ if (entry.released)
52
+ return;
53
+ entry.released = true;
54
+ if (entry.timer !== null)
55
+ clearTimeout(entry.timer);
56
+ if (entry.promoted) {
57
+ activeTotal = Math.max(0, activeTotal - 1);
58
+ const nextForRuntime = Math.max(0, (activeByRuntime.get(runtime) ?? 1) - 1);
59
+ if (nextForRuntime === 0)
60
+ activeByRuntime.delete(runtime);
61
+ else
62
+ activeByRuntime.set(runtime, nextForRuntime);
63
+ }
64
+ else {
65
+ const index = queue.indexOf(entry);
66
+ if (index >= 0)
67
+ queue.splice(index, 1);
68
+ }
69
+ promote();
70
+ },
71
+ };
72
+ },
73
+ snapshot: () => ({
74
+ startingTotal: activeTotal,
75
+ queuedTotal: queue.length,
76
+ startingByRuntime: {
77
+ claude: activeByRuntime.get("claude") ?? 0,
78
+ codex: activeByRuntime.get("codex") ?? 0,
79
+ kimi: activeByRuntime.get("kimi") ?? 0,
80
+ },
81
+ }),
82
+ };
83
+ }
84
+ export function isRuntimeReadyEvent(runtime, event) {
85
+ if (typeof event !== "object" || event === null)
86
+ return false;
87
+ const value = event;
88
+ if (runtime === "claude")
89
+ return value.type === "system" && value.subtype === "init";
90
+ return value.type === "thread.started";
91
+ }
@@ -24,7 +24,24 @@ const STDERR_TAIL_CAP = 1_200;
24
24
  const STDERR_LINE_CAPTURE_CAP = 1_200;
25
25
  const STDERR_LINE_OMITTED = "[stderr line omitted: exceeded capture limit]\n";
26
26
  const MAX_INITIALIZE_ATTEMPTS = 2;
27
+ // 模型网关瞬态故障(过载/限流)导致 turn 失败时,整轮重试(15s→45s 递进退避):codex 自身
28
+ // 的重试窗口只有 ~10-30s,网关过载往往持续数分钟,这里再兜一层,否则 agent 直接失败
29
+ // 不回复(2026-08-10 事故,普通会话与定时任务都中招)。
30
+ const MAX_TRANSIENT_TURN_RETRIES = 2;
31
+ const TRANSIENT_TURN_RETRY_DELAY_MS = 15_000;
32
+ const TRANSIENT_TURN_RETRY_BACKOFF_FACTOR = 3;
27
33
  const PROCESS_TREE_STOP_TIMEOUT_MS = 1_000;
34
+ const TRANSIENT_TURN_ERROR_PATTERNS = [
35
+ /at capacity/i,
36
+ /overloaded/i,
37
+ /rate.?limit/i,
38
+ /too many requests/i,
39
+ ];
40
+ /** 模型侧瞬态失败(网关过载/限流)→ 换个时间整轮重试大概率成功。
41
+ * interrupted 不算:那是取消信号或 codex collab 连带中断,重跑语义不明确。 */
42
+ export function isTransientTurnFailure(detail) {
43
+ return TRANSIENT_TURN_ERROR_PATTERNS.some((pattern) => pattern.test(detail));
44
+ }
28
45
  class CodexRpcTimeoutError extends Error {
29
46
  method;
30
47
  timeoutMs;
@@ -469,6 +486,9 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
469
486
  return {
470
487
  code: completed.turn?.status === "interrupted" ? 130 : 1,
471
488
  initializeTimedOut: false,
489
+ transientTurnFailure: !cancelling
490
+ && completed.turn?.status === "failed"
491
+ && isTransientTurnFailure(detail),
472
492
  };
473
493
  }
474
494
  catch (error) {
@@ -501,13 +521,27 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
501
521
  export async function runCodexAppServer(bin, options = {}) {
502
522
  const input = await readRunnerInput();
503
523
  const initializeTimeoutMs = Math.min(options.initializeTimeoutMs ?? INITIALIZE_RPC_TIMEOUT_MS, INITIALIZE_RPC_TIMEOUT_MS);
504
- for (let attempt = 1; attempt <= MAX_INITIALIZE_ATTEMPTS; attempt += 1) {
524
+ const turnRetryDelayMs = options.turnRetryDelayMs ?? TRANSIENT_TURN_RETRY_DELAY_MS;
525
+ let initializeTimeouts = 0;
526
+ let turnRetries = 0;
527
+ for (let attempt = 1;; attempt += 1) {
505
528
  const result = await runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs);
506
- if (!result.initializeTimedOut || attempt === MAX_INITIALIZE_ATTEMPTS)
507
- return result.code;
508
- process.stderr.write(`[codex-app-server] stage=retry status=start attempt=${attempt + 1} elapsed_ms=0 reason=initialize_timeout\n`);
529
+ if (result.initializeTimedOut) {
530
+ initializeTimeouts += 1;
531
+ if (initializeTimeouts >= MAX_INITIALIZE_ATTEMPTS)
532
+ return result.code;
533
+ process.stderr.write(`[codex-app-server] stage=retry status=start attempt=${attempt + 1} elapsed_ms=0 reason=initialize_timeout\n`);
534
+ continue;
535
+ }
536
+ if (result.transientTurnFailure && turnRetries < MAX_TRANSIENT_TURN_RETRIES) {
537
+ const delayMs = turnRetryDelayMs * TRANSIENT_TURN_RETRY_BACKOFF_FACTOR ** turnRetries;
538
+ turnRetries += 1;
539
+ process.stderr.write(`[codex-app-server] stage=retry status=start attempt=${attempt + 1} elapsed_ms=0 reason=transient_turn_failure\n`);
540
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
541
+ continue;
542
+ }
543
+ return result.code;
509
544
  }
510
- return 1;
511
545
  }
512
546
  function configFromArgv(argv) {
513
547
  const { values } = parseArgs({
@@ -515,24 +549,34 @@ function configFromArgv(argv) {
515
549
  options: {
516
550
  bin: { type: "string" },
517
551
  "initialize-timeout-ms": { type: "string" },
552
+ "turn-retry-delay-ms": { type: "string" },
518
553
  },
519
554
  });
520
555
  if (!values.bin)
521
556
  throw new Error("--bin is required");
522
- const rawTimeout = values["initialize-timeout-ms"];
523
- if (rawTimeout === undefined)
524
- return { bin: values.bin };
525
- const initializeTimeoutMs = Number(rawTimeout);
526
- if (!Number.isInteger(initializeTimeoutMs) || initializeTimeoutMs <= 0) {
527
- throw new Error("--initialize-timeout-ms must be a positive integer");
528
- }
529
- return { bin: values.bin, initializeTimeoutMs };
557
+ const positiveInteger = (raw, flag) => {
558
+ if (raw === undefined)
559
+ return undefined;
560
+ const value = Number(raw);
561
+ if (!Number.isInteger(value) || value <= 0) {
562
+ throw new Error(`${flag} must be a positive integer`);
563
+ }
564
+ return value;
565
+ };
566
+ const initializeTimeoutMs = positiveInteger(values["initialize-timeout-ms"], "--initialize-timeout-ms");
567
+ const turnRetryDelayMs = positiveInteger(values["turn-retry-delay-ms"], "--turn-retry-delay-ms");
568
+ return {
569
+ bin: values.bin,
570
+ ...(initializeTimeoutMs === undefined ? {} : { initializeTimeoutMs }),
571
+ ...(turnRetryDelayMs === undefined ? {} : { turnRetryDelayMs }),
572
+ };
530
573
  }
531
574
  if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
532
575
  const config = configFromArgv(process.argv.slice(2));
533
- runCodexAppServer(config.bin, config.initializeTimeoutMs === undefined
534
- ? {}
535
- : { initializeTimeoutMs: config.initializeTimeoutMs })
576
+ runCodexAppServer(config.bin, {
577
+ ...(config.initializeTimeoutMs === undefined ? {} : { initializeTimeoutMs: config.initializeTimeoutMs }),
578
+ ...(config.turnRetryDelayMs === undefined ? {} : { turnRetryDelayMs: config.turnRetryDelayMs }),
579
+ })
536
580
  .then((code) => { process.exitCode = code; })
537
581
  .catch((error) => {
538
582
  process.stderr.write(`Codex app-server runner failed: ${safeErrorMessage(error, [])}\n`);
package/dist/serve.js CHANGED
@@ -28,6 +28,9 @@ import { closeWebSocketWithinDeadline } from "./websocket-shutdown.js";
28
28
  import { reconcileExecutionJournal } from "./execution-recovery.js";
29
29
  import { createSharedSlotManager } from "./shared-execution-slots.js";
30
30
  import { createCompletionRetransmitter } from "./completion-retransmitter.js";
31
+ import { createAgentMemoryBridge } from "./agent-memory/bridge.js";
32
+ import { createRuntimeStartupGate } from "./runtime-startup-gate.js";
33
+ import { createHostExecutionCoordinator, hostCoordinatedSlotManager, hostCoordinatedStartupGate, } from "./host-execution-coordinator.js";
31
34
  // normalize.ts 的活动种类 → activity 枚举
32
35
  const ACTIVITY_MAP = {
33
36
  init: "working", text: "thinking", reading: "reading", sending: "sending",
@@ -56,11 +59,15 @@ export function serve(config, opts = {}) {
56
59
  let reconnectTimer = null;
57
60
  let stopPromise = null;
58
61
  const executionJournal = opts.execution?.journal ?? createExecutionJournal(config.agentsRoot);
62
+ const agentMemory = opts.execution?.dependencies?.agentMemory
63
+ ?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
59
64
  const executionTelemetry = createExecutionTelemetryJournal(config.agentsRoot);
60
65
  const executeProtocol = opts.execution?.runExecution ?? runExecution;
61
66
  let detectedExecutionRuntimes = [];
62
67
  let runtimeFacts = null;
63
- const sharedSlots = createSharedSlotManager(config.executionLimits);
68
+ const hostCoordinator = opts.execution?.hostCoordinator ?? createHostExecutionCoordinator();
69
+ const sharedSlots = hostCoordinatedSlotManager(createSharedSlotManager(config.executionLimits), hostCoordinator);
70
+ const runtimeStartupGate = hostCoordinatedStartupGate(createRuntimeStartupGate(config.executionLimits), hostCoordinator);
64
71
  const knownExecutionHashes = new Map();
65
72
  const executionReservations = new Map();
66
73
  const executionRuns = new Map();
@@ -115,8 +122,11 @@ export function serve(config, opts = {}) {
115
122
  if ((entry.state === "completed" || entry.state === "interrupted") && entry.completion !== null) {
116
123
  return { executionId: entry.executionId, state: entry.state, completion: entry.completion, updatedAt: entry.updatedAt };
117
124
  }
125
+ if (entry.state === "running" && entry.runtimeReadyAt !== null) {
126
+ return { executionId: entry.executionId, state: "running", updatedAt: entry.runtimeReadyAt };
127
+ }
118
128
  if (entry.state === "accepted" || entry.state === "running") {
119
- return { executionId: entry.executionId, state: entry.state, updatedAt: entry.updatedAt };
129
+ return { executionId: entry.executionId, state: "accepted", updatedAt: entry.acceptedAt };
120
130
  }
121
131
  return null;
122
132
  };
@@ -137,10 +147,10 @@ export function serve(config, opts = {}) {
137
147
  state: acceptanceState, effectivePermission: entry.effectivePermission ?? "workspace_write",
138
148
  at: entry.acceptedAt,
139
149
  });
140
- if (entry.state === "running" && entry.processStartedAt !== null) {
150
+ if (entry.state === "running" && entry.runtimeReadyAt !== null) {
141
151
  safeExecutionSend({
142
152
  type: "execution:started", protocolVersion: 1,
143
- executionId: entry.executionId, at: entry.processStartedAt,
153
+ executionId: entry.executionId, at: entry.runtimeReadyAt,
144
154
  });
145
155
  }
146
156
  }
@@ -335,8 +345,30 @@ export function serve(config, opts = {}) {
335
345
  }
336
346
  return;
337
347
  }
338
- knownExecutionHashes.set(spec.executionId, hash);
339
348
  const reservation = sharedSlots.reserve(spec.agent.handle, "execution");
349
+ if (!reservation.accepted) {
350
+ dslog("execution.machine_queue_rejected", "机器执行队列已满", {
351
+ level: "WARN",
352
+ execution_id: spec.executionId,
353
+ agent_handle: spec.agent.handle,
354
+ ...reservation.facts,
355
+ });
356
+ safeExecutionSend(ExecutionRejectedSchema.parse({
357
+ type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
358
+ reason: "resource_limit", message: "Local machine execution queue is full",
359
+ at: new Date().toISOString(),
360
+ }));
361
+ return;
362
+ }
363
+ knownExecutionHashes.set(spec.executionId, hash);
364
+ const machineQueueEnteredAt = Date.now();
365
+ if (reservation.isQueued()) {
366
+ dslog("execution.machine_queued", "execution 已进入机器队列", {
367
+ execution_id: spec.executionId,
368
+ agent_handle: spec.agent.handle,
369
+ ...reservation.facts,
370
+ });
371
+ }
340
372
  executionReservations.set(spec.executionId, reservation);
341
373
  const cancellation = cancellationFor(spec.executionId);
342
374
  const cleanupExecutionReservation = () => {
@@ -365,14 +397,29 @@ export function serve(config, opts = {}) {
365
397
  }
366
398
  const execution = executeProtocol(config, spec, {
367
399
  ...opts.execution?.dependencies,
400
+ ...(agentMemory === undefined ? {} : { agentMemory }),
368
401
  journal: executionJournal,
369
402
  facts: {
370
403
  availableRuntimes,
371
404
  ...reservation.facts,
372
405
  },
373
406
  report: reportExecutionFrame,
407
+ startupGate: runtimeStartupGate,
408
+ startupTimeoutMs: config.executionLimits.startupTimeoutMs,
374
409
  ...(reservation.state === undefined ? {} : {
375
- slot: { state: reservation.state, ready: reservation.ready },
410
+ slot: {
411
+ state: reservation.state,
412
+ ready: reservation.ready.then(() => {
413
+ const snapshot = sharedSlots.snapshot();
414
+ dslog("execution.machine_slot_ready", "execution 获得机器执行名额", {
415
+ execution_id: spec.executionId,
416
+ agent_handle: spec.agent.handle,
417
+ queue_ms: Date.now() - machineQueueEnteredAt,
418
+ active_total: snapshot.activeTotal,
419
+ queued_total: snapshot.queuedTotal,
420
+ });
421
+ }),
422
+ },
376
423
  }),
377
424
  cancellation,
378
425
  }).finally(() => {
@@ -539,12 +586,26 @@ export function serve(config, opts = {}) {
539
586
  }
540
587
  running.add(key);
541
588
  legacyReservation = sharedSlots.reserve(msg.agentHandle, "legacy");
589
+ if (legacyReservation.isQueued()) {
590
+ const snapshot = sharedSlots.snapshot();
591
+ dslog("run.machine_queued", "legacy run 已进入机器队列", {
592
+ ...runKeys,
593
+ active_total: snapshot.activeTotal,
594
+ queued_total: snapshot.queuedTotal,
595
+ });
596
+ }
542
597
  await awaitWithCancellation(legacyReservation.ready, controller.cancellation);
543
598
  const queueMs = Date.now() - queueWaitStart;
599
+ const machineSnapshot = sharedSlots.snapshot();
544
600
  const threadLabel = threadId ?? null;
545
601
  const from = msg.wake?.senderHandle ?? "?";
546
602
  const incoming = msg.wake?.content ?? "";
547
- dslog("run.start", `开始运行 ${msg.agentHandle}`, { ...runKeys, queue_ms: queueMs });
603
+ dslog("run.start", `开始运行 ${msg.agentHandle}`, {
604
+ ...runKeys,
605
+ queue_ms: queueMs,
606
+ active_total: machineSnapshot.activeTotal,
607
+ queued_total: machineSnapshot.queuedTotal,
608
+ });
548
609
  log(`\n${"─".repeat(56)}`);
549
610
  log(`🔔 唤醒 agent=${msg.agentHandle} reason=${msg.reason ?? "?"}`);
550
611
  log(` channel = ${msg.channelId}`);
@@ -650,7 +711,11 @@ export function serve(config, opts = {}) {
650
711
  ...(!scheduled && threadId ? { wakeMessageId: threadId } : {}),
651
712
  ...(!scheduled && msg.wake?.seq !== undefined ? { wakeContextUpToSeq: msg.wake.seq } : {}),
652
713
  ...(attemptWake ? { wake: attemptWake } : {}),
653
- }, reportActivity, reportConsole, { cancellation: controller.cancellation });
714
+ }, reportActivity, reportConsole, {
715
+ cancellation: controller.cancellation,
716
+ startupGate: runtimeStartupGate,
717
+ startupTimeoutMs: config.executionLimits.startupTimeoutMs,
718
+ });
654
719
  });
655
720
  const result = mergeRunAgentResults(guarded.results);
656
721
  // 本轮 token 用量上报:runner 已从 result 事件提取(含缓存读/写细分),
@@ -848,6 +913,7 @@ export function serve(config, opts = {}) {
848
913
  for (const run of legacyRuns.values())
849
914
  run.controller.request();
850
915
  await deadline.waitFor(Promise.all(pending));
916
+ await deadline.waitFor(hostCoordinator.drain());
851
917
  await executionJournal.close({ signal: deadline.signal });
852
918
  }
853
919
  finally {
@@ -1,46 +1,58 @@
1
1
  export function createSharedSlotManager(limits) {
2
2
  const activeByHandle = new Map();
3
- const queuesByHandle = new Map();
4
- const promoteNext = (handle) => {
5
- const queue = queuesByHandle.get(handle) ?? [];
6
- while ((activeByHandle.get(handle) ?? 0) < limits.maxParallelPerAgent && queue.length > 0) {
7
- const next = queue.shift();
8
- if (next.released)
3
+ const queue = [];
4
+ let activeTotal = 0;
5
+ const queuedFor = (handle) => queue.filter((entry) => !entry.released && entry.handle === handle).length;
6
+ const promote = () => {
7
+ while (activeTotal < limits.maxParallelTotal) {
8
+ const index = queue.findIndex((entry) => !entry.released
9
+ && (activeByHandle.get(entry.handle) ?? 0) < limits.maxParallelPerAgent);
10
+ if (index < 0)
11
+ return;
12
+ const [next] = queue.splice(index, 1);
13
+ if (!next || next.released)
9
14
  continue;
10
15
  next.promoted = true;
11
- activeByHandle.set(handle, (activeByHandle.get(handle) ?? 0) + 1);
16
+ activeTotal += 1;
17
+ activeByHandle.set(next.handle, (activeByHandle.get(next.handle) ?? 0) + 1);
12
18
  next.resolve();
13
19
  }
14
- if (queue.length === 0)
15
- queuesByHandle.delete(handle);
16
20
  };
17
21
  return {
18
22
  reserve: (handle, kind) => {
19
- const active = activeByHandle.get(handle) ?? 0;
20
- const queued = queuesByHandle.get(handle)?.length ?? 0;
21
- const facts = { activeForAgent: active, queuedForAgent: queued };
22
- if (kind === "execution" && active >= limits.maxParallelPerAgent
23
- && queued >= limits.maxQueuedPerAgent) {
24
- return { facts, ready: Promise.resolve(), isQueued: () => false, release: () => { } };
23
+ const activeForAgent = activeByHandle.get(handle) ?? 0;
24
+ const queuedForAgent = queuedFor(handle);
25
+ const facts = {
26
+ activeForAgent,
27
+ queuedForAgent,
28
+ activeTotal,
29
+ queuedTotal: queue.length,
30
+ };
31
+ const canStartImmediately = activeTotal < limits.maxParallelTotal
32
+ && activeForAgent < limits.maxParallelPerAgent
33
+ && queue.length === 0;
34
+ if (kind === "execution" && !canStartImmediately && (queuedForAgent >= limits.maxQueuedPerAgent
35
+ || queue.length >= limits.maxQueuedTotal)) {
36
+ return {
37
+ accepted: false,
38
+ facts,
39
+ ready: Promise.resolve(),
40
+ isQueued: () => false,
41
+ release: () => { },
42
+ };
25
43
  }
26
44
  let resolveReady;
27
45
  const ready = new Promise((resolve) => { resolveReady = resolve; });
28
46
  const entry = {
29
- kind,
47
+ handle,
30
48
  released: false,
31
- promoted: active < limits.maxParallelPerAgent,
49
+ promoted: false,
32
50
  resolve: resolveReady,
33
51
  };
34
- if (entry.promoted) {
35
- activeByHandle.set(handle, active + 1);
36
- resolveReady();
37
- }
38
- else {
39
- const queue = queuesByHandle.get(handle) ?? [];
40
- queue.push(entry);
41
- queuesByHandle.set(handle, queue);
42
- }
52
+ queue.push(entry);
53
+ promote();
43
54
  return {
55
+ accepted: true,
44
56
  facts,
45
57
  state: entry.promoted ? "ready" : "queued",
46
58
  ready,
@@ -50,19 +62,22 @@ export function createSharedSlotManager(limits) {
50
62
  return;
51
63
  entry.released = true;
52
64
  if (entry.promoted) {
53
- activeByHandle.set(handle, Math.max(0, (activeByHandle.get(handle) ?? 1) - 1));
65
+ activeTotal = Math.max(0, activeTotal - 1);
66
+ const nextForAgent = Math.max(0, (activeByHandle.get(handle) ?? 1) - 1);
67
+ if (nextForAgent === 0)
68
+ activeByHandle.delete(handle);
69
+ else
70
+ activeByHandle.set(handle, nextForAgent);
54
71
  }
55
72
  else {
56
- const queue = queuesByHandle.get(handle);
57
- const index = queue?.indexOf(entry) ?? -1;
58
- if (queue !== undefined && index >= 0)
73
+ const index = queue.indexOf(entry);
74
+ if (index >= 0)
59
75
  queue.splice(index, 1);
60
- if (queue?.length === 0)
61
- queuesByHandle.delete(handle);
62
76
  }
63
- promoteNext(handle);
77
+ promote();
64
78
  },
65
79
  };
66
80
  },
81
+ snapshot: () => ({ activeTotal, queuedTotal: queue.length }),
67
82
  };
68
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.5.30",
3
+ "version": "0.5.32",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",