@raysonmeng/agentbridge 0.1.17 → 0.1.19

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.
@@ -12,7 +12,7 @@
12
12
  {
13
13
  "name": "agentbridge",
14
14
  "description": "Bridge Claude Code and Codex through a shared daemon, push channel delivery, and reply/get_messages tools.",
15
- "version": "0.1.17",
15
+ "version": "0.1.19",
16
16
  "author": {
17
17
  "name": "AgentBridge Contributors",
18
18
  "email": "raysonmeng@qq.com"
package/dist/cli.js CHANGED
@@ -126,6 +126,9 @@ class StateDirResolver {
126
126
  get killedFile() {
127
127
  return join(this.stateDir, "killed");
128
128
  }
129
+ get admissionQuotaFile() {
130
+ return join(this.stateDir, "admission-quota.json");
131
+ }
129
132
  get updateCheckFile() {
130
133
  return join(this.stateDir, "update-check.json");
131
134
  }
@@ -176,7 +179,7 @@ function parsePositiveIntEnv(name, fallback, log = () => {}, env = process.env)
176
179
  var require_package = __commonJS((exports, module) => {
177
180
  module.exports = {
178
181
  name: "@raysonmeng/agentbridge",
179
- version: "0.1.17",
182
+ version: "0.1.19",
180
183
  description: "Bridge between Claude Code and Codex \u2014 bidirectional agent communication via MCP Channel + JSON-RPC",
181
184
  type: "module",
182
185
  packageManager: "bun@1.3.11",
@@ -511,7 +514,9 @@ function findShapeViolation(raw) {
511
514
  "reserveSlopePctPerHour",
512
515
  "reserveMaxPct",
513
516
  "finishingHorizonMinutes",
514
- "resumeHysteresisPct"
517
+ "resumeHysteresisPct",
518
+ "admissionAt",
519
+ "wrapUpQuota"
515
520
  ]) {
516
521
  if (key in maximize && !isCoercibleNumber(maximize[key])) {
517
522
  return `budget.maximize.${key} is present but not a number`;
@@ -536,7 +541,7 @@ function hasCustomDecisionValues(config) {
536
541
  const d = DEFAULT_CONFIG;
537
542
  const b = config.budget;
538
543
  const db = d.budget;
539
- return config.idleShutdownSeconds !== d.idleShutdownSeconds || config.turnCoordination.attentionWindowSeconds !== d.turnCoordination.attentionWindowSeconds || config.codex.appPort !== d.codex.appPort || config.codex.proxyPort !== d.codex.proxyPort || b.enabled !== db.enabled || b.pollSeconds !== db.pollSeconds || b.pauseAt !== db.pauseAt || b.resumeBelow !== db.resumeBelow || b.syncDriftPct !== db.syncDriftPct || b.parallel.minRemainingPct !== db.parallel.minRemainingPct || b.parallel.timeWindowSec !== db.parallel.timeWindowSec || b.codexTierControl !== db.codexTierControl || b.maximize.targetUtil !== db.maximize.targetUtil || b.maximize.reserveSlopePctPerHour !== db.maximize.reserveSlopePctPerHour || b.maximize.reserveMaxPct !== db.maximize.reserveMaxPct || b.maximize.finishingHorizonMinutes !== db.maximize.finishingHorizonMinutes || b.maximize.resumeHysteresisPct !== db.maximize.resumeHysteresisPct || b.allocation.minRunwayRatio !== db.allocation.minRunwayRatio || b.allocation.minRunwayGapHours !== db.allocation.minRunwayGapHours;
544
+ return config.idleShutdownSeconds !== d.idleShutdownSeconds || config.turnCoordination.attentionWindowSeconds !== d.turnCoordination.attentionWindowSeconds || config.codex.appPort !== d.codex.appPort || config.codex.proxyPort !== d.codex.proxyPort || b.enabled !== db.enabled || b.pollSeconds !== db.pollSeconds || b.pauseAt !== db.pauseAt || b.resumeBelow !== db.resumeBelow || b.syncDriftPct !== db.syncDriftPct || b.parallel.minRemainingPct !== db.parallel.minRemainingPct || b.parallel.timeWindowSec !== db.parallel.timeWindowSec || b.codexTierControl !== db.codexTierControl || b.maximize.targetUtil !== db.maximize.targetUtil || b.maximize.reserveSlopePctPerHour !== db.maximize.reserveSlopePctPerHour || b.maximize.reserveMaxPct !== db.maximize.reserveMaxPct || b.maximize.finishingHorizonMinutes !== db.maximize.finishingHorizonMinutes || b.maximize.resumeHysteresisPct !== db.maximize.resumeHysteresisPct || b.maximize.admissionAt !== db.maximize.admissionAt || b.maximize.wrapUpQuota !== db.maximize.wrapUpQuota || b.allocation.minRunwayRatio !== db.allocation.minRunwayRatio || b.allocation.minRunwayGapHours !== db.allocation.minRunwayGapHours;
540
545
  }
541
546
  function normalizeInteger(value, fallback) {
542
547
  if (typeof value === "number" && Number.isFinite(value))
@@ -576,9 +581,11 @@ function normalizeMaximizeConfig(raw, pauseAt, fallback = DEFAULT_BUDGET_CONFIG.
576
581
  reserveSlopePctPerHour: normalizeBoundedNumber(m.reserveSlopePctPerHour, fallback.reserveSlopePctPerHour, 0, 5),
577
582
  reserveMaxPct: normalizeBoundedInteger(m.reserveMaxPct, fallback.reserveMaxPct, 0, 30),
578
583
  finishingHorizonMinutes: normalizeBoundedInteger(m.finishingHorizonMinutes, fallback.finishingHorizonMinutes, 5, 180),
579
- resumeHysteresisPct: normalizeBoundedInteger(m.resumeHysteresisPct, fallback.resumeHysteresisPct, 1, 30)
584
+ resumeHysteresisPct: normalizeBoundedInteger(m.resumeHysteresisPct, fallback.resumeHysteresisPct, 1, 30),
585
+ admissionAt: normalizeBoundedInteger(m.admissionAt, fallback.admissionAt, 50, 99),
586
+ wrapUpQuota: Math.floor(normalizeBoundedInteger(m.wrapUpQuota, fallback.wrapUpQuota, 0, 10))
580
587
  };
581
- if (normalized.targetUtil <= pauseAt) {
588
+ if (normalized.targetUtil <= pauseAt || normalized.admissionAt >= normalized.targetUtil) {
582
589
  return { ...DEFAULT_BUDGET_CONFIG.maximize };
583
590
  }
584
591
  return normalized;
@@ -661,7 +668,9 @@ function applyBudgetEnvOverrides(budget, env = process.env) {
661
668
  reserveSlopePctPerHour: env.AGENTBRIDGE_BUDGET_RESERVE_SLOPE_PCT_PER_HOUR ?? budget.maximize.reserveSlopePctPerHour,
662
669
  reserveMaxPct: env.AGENTBRIDGE_BUDGET_RESERVE_MAX_PCT ?? budget.maximize.reserveMaxPct,
663
670
  finishingHorizonMinutes: env.AGENTBRIDGE_BUDGET_FINISHING_HORIZON_MINUTES ?? budget.maximize.finishingHorizonMinutes,
664
- resumeHysteresisPct: env.AGENTBRIDGE_BUDGET_RESUME_HYSTERESIS_PCT ?? budget.maximize.resumeHysteresisPct
671
+ resumeHysteresisPct: env.AGENTBRIDGE_BUDGET_RESUME_HYSTERESIS_PCT ?? budget.maximize.resumeHysteresisPct,
672
+ admissionAt: env.AGENTBRIDGE_BUDGET_ADMISSION_AT ?? budget.maximize.admissionAt,
673
+ wrapUpQuota: env.AGENTBRIDGE_BUDGET_WRAP_UP_QUOTA ?? budget.maximize.wrapUpQuota
665
674
  },
666
675
  allocation: {
667
676
  minRunwayRatio: env.AGENTBRIDGE_BUDGET_MIN_RUNWAY_RATIO ?? budget.allocation.minRunwayRatio,
@@ -802,7 +811,9 @@ var init_config_service = __esm(() => {
802
811
  reserveSlopePctPerHour: 0.4,
803
812
  reserveMaxPct: 7,
804
813
  finishingHorizonMinutes: 30,
805
- resumeHysteresisPct: 5
814
+ resumeHysteresisPct: 5,
815
+ admissionAt: 85,
816
+ wrapUpQuota: 2
806
817
  },
807
818
  allocation: {
808
819
  minRunwayRatio: 50,
@@ -904,7 +915,16 @@ ${endMarker}`;
904
915
  var MARKER_START = (id) => `<!-- ${id}:start -->`, MARKER_END = (id) => `<!-- ${id}:end -->`;
905
916
 
906
917
  // src/collaboration-content.ts
907
- var MARKER_ID = "AgentBridge", CLAUDE_MD_SECTION = `## AgentBridge \u2014 Multi-Agent Collaboration
918
+ var MARKER_ID = "AgentBridge", BUDGET_PACING = `### Budget pacing \u2014 drive the WEEKLY quota to ~100% over the week, evenly, without reaching a 5h cap (active when agent-quota-guard is installed)
919
+ - **Core principle: token is the means, value is the end.** Raising intensity means producing more real parallel value (deeper reviews, more independent exploration / verification / genuine subtasks) \u2014 never manufacturing low-value work to consume quota. The budget to MAXIMIZE is the **weekly** quota (refreshed once a week): drive each side's weekly toward ~100% by its weekly reset, and consume it **evenly** across the week \u2014 front-loading then starving, or under-consuming throughout, both leave weekly quota unredeemed (forfeited). The **5h window is NOT a quota bucket to fill \u2014 it is a RATE CAP**: stay under it within any 5h period; reaching it = a forced pause until the 5h resets = wasted time, not progress.
920
+ - **Re-query your budget before EVERY allocation decision** \u2014 Claude: \`get_budget\` \u2192 **rendered text** covering both sides; Codex: \`check_budget\` with \`agent:"claude"|"codex"\` \u2192 **normalized JSON**, per side. (Two different shapes \u2014 read the right one below.) Never reuse remembered numbers: a weekly window can refresh EARLY (resetting both 5h and weekly), fully restoring a side you believed was exhausted.
921
+ - **Even-pacing test (per side \u2014 Claude runs it)** \u2014 compare two quantities: *budget-windows* = how many 5h windows the weekly quota still covers at the current burn rate; *clock-windows* = how many 5h windows physically fit before the weekly reset = (weekly reset \u2212 now) \xF7 5h. **Claude** (\`get_budget\` text) carries BOTH, pre-computed for BOTH sides: the lines "\u6309\u5F53\u524D\u8282\u594F\uFF0C\u5468\u989D\u5EA6\u8FD8\u591F \u2026 \u4E2A 5h \u7A97\u53E3" (budget-windows) and "\u8DDD\u5468\u5237\u65B0\u8FD8\u80FD\u5BB9\u7EB3 \u2026 \u4E2A 5h \u7A97\u53E3\uFF08\u65F6\u949F\uFF09" (clock-windows). **Codex** (\`check_budget\` JSON) today carries only per-bucket \`util\` / \`reset_epoch\` / \`reset_after_seconds\` \u2014 no burn rate, no \`five_hour_windows_left\` \u2014 so Codex CANNOT compute budget-windows itself; it reads its weekly \`util\` and clock-windows only. To locate Codex's weekly bucket: of the \`buckets[]\` entries whose \`id\` contains \`seven_day\` or \`secondary_window\` (there can be several \u2014 e.g. a model-specific \`additional_rate_limits[\u2026]\` one at 0%), take the HIGHEST-util one (the binding account-level window, matching how the bridge parses it); its clock-windows = \`reset_after_seconds\` \xF7 5h (never the top-level \`reset_epoch\`, which tracks the current limiter, not necessarily the weekly window). For the budget-windows half and the raise/hold/reduce verdict, Codex relies on Claude's \`get_budget\` (the burn projection lives there, for both sides) and reports its own weekly \`util\` + reset timing so Claude can run the test. (If a future \`check_budget\` exposes \`five_hour_windows_left\` on the weekly bucket, Codex reads it directly.) **The verdict (Claude computes it, per side):** budget > clock \u2192 **under-consuming** (weekly will be left unused) \u2192 **raise intensity**; budget < clock \u2192 **over-consuming** (won't last to the weekly reset) \u2192 **reduce intensity**; within ~1 window, or no confident rate \u2192 **hold**. **Codex, absent a fresh Claude verdict, holds at its current intensity (it never escalates unilaterally) and stays clear of the 5h cap \u2014 surfacing its weekly \`util\` + reset timing so Claude can issue the verdict.**
922
+ - **Raise intensity \u2014 use the levers your role has.** Orchestrator (Claude): pick larger, more-decomposable tasks; run more parallel subagents at once (3\u20135+ vs 1); raise delegation density; open more concurrent streams (review + explore + verify in parallel). Executor (Codex): go deeper in-turn, take larger chunks, run more verification/repro. Both: deepen quality (multi-angle review, broader test/repro) \u2014 never manufacture make-work. **Reduce intensity:** fewer/serial subagents (Claude), short bounded chunks, defer optional deep work. Stay below the **\u52A8\u6001\u6682\u505C\u7EBF** (shown in \`get_budget\`; its \`\u4F59\u91CF\` = headroom from your current util to that soft line, measured on the resettable hard-winner window \u2014 the 5h OR the weekly window, whichever currently limits you) \u2014 that soft ceiling, not the raw 5h cap, is the "do not cross, avoid a forced pause" line. **If that line is absent, or you only have JSON (Codex),** fall back to the 5h bucket's raw util vs 100% (Codex: of the \`buckets[]\` entries whose \`id\` contains \`five_hour\` or \`primary_window\`, take the HIGHEST-util one) and keep clear of the 5h cap.
923
+ - **Distinguish 5h from weekly:** a 5h window resetting does NOT consume or waste weekly budget \u2014 it only refreshes your rate headroom, so you can keep going when weekly is under-consumed. A near 5h reset is therefore not urgency but the release of a rate limit. The real "unused = forfeited" is the **weekly budget as its WEEKLY reset nears**: if weekly is still under-consumed then, raise intensity (within the 5h cap) to use it. If even pacing needs a rate beyond one 5h window's capacity, you are rate-limited \u2192 keep each 5h window as full as possible (under the cap).
924
+ - **Two-subscription imbalance \u2014 the quotas are INDEPENDENT and differ in BOTH amount AND reset timing** (each side's weekly and 5h windows reset on different clocks). **The cross-side split is the orchestrator's (Claude) decision:** route more work to the side that is MORE under-consuming on the even-pacing test (the larger budget-windows \u2212 clock-windows gap); when EITHER side lacks a confident rate (so the gap can't be compared), fall back to the more budget-rich side (larger absolute weekly headroom). On any tie (equal gap, or equal headroom), prefer the side whose **weekly resets SOONER** (its leftover is forfeited earlier). **As the executor (Codex) you do NOT decide the global split** \u2014 execute what you're assigned, and when your own budget is rich report it (with evidence) so Claude routes more to you. The tighter / over-consuming side carries less.
925
+ - **Side-aware pause (the hard floor the code enforces \u2014 obey, do not reinvent), with each side's own action:** **Codex exhausted** (\`system_budget_pause\`) \u2192 Codex's turns stop (gate closed); **Claude** must not retry replies and continues solo on independent work, checkpointing the split point \u2014 but the SAME \`system_budget_pause\` is ALSO emitted when both sides are exhausted, so do not infer "solo" from the directive name alone: read its content (it names the paused side[s]) or re-check \`get_budget\`, and continue solo ONLY while Claude's own side is healthy; if Claude is also at its line, handle it as **Both** below. **Claude exhausted** (\`system_budget_handoff\`) \u2192 **Claude** sends ONE handoff (remaining tasks / context / artifact locations / acceptance criteria) then stops; **Codex** receives the baton and carries the work forward as far as its remaining quota allows that turn. **Both** \u2192 joint pause; checkpoint and wait for \`resume\` (Claude's own quota-guard also hard-stops Claude independently). A transient probe **429 is NOT exhaustion** \u2192 fall back to cached util and keep working.`, CLAUDE_MD_SECTION, AGENTS_MD_SECTION;
926
+ var init_collaboration_content = __esm(() => {
927
+ CLAUDE_MD_SECTION = `## AgentBridge \u2014 Multi-Agent Collaboration
908
928
 
909
929
  You are working in a **multi-agent environment** powered by AgentBridge.
910
930
  Another AI agent (Codex, by OpenAI) is available in a parallel session on this machine.
@@ -935,14 +955,8 @@ Another AI agent (Codex, by OpenAI) is available in a parallel session on this m
935
955
  3. Ask for Codex's agreement or counter-proposal before proceeding.
936
956
  4. After task completion, **cross-review** each other's work.
937
957
 
938
- ### Budget awareness (active when agent-quota-guard is installed)
939
- - Goal: **keep the task moving while fully using the subscription quota**. The bridge polls both agents' account-level 5h/weekly windows and may send \`system_budget_*\` notices: **balance** (route more work to the side with more runway / remaining work-time), **underutilized** (the account won't use its weekly quota before reset \u2014 split more parallel subtasks / raise delegation density), **pause/handoff/resume**.
940
- - \`get_budget\` shows BOTH sides' quota \u2014 re-check it **before every task-allocation decision**. NEVER rely on quota numbers remembered from earlier in the conversation: the weekly window can refresh EARLY (resetting both 5h and weekly), so a side you remember as nearly exhausted may be fully restored.
941
- - Side-aware pause semantics:
942
- - **Codex exhausted** (\`system_budget_pause\`): the reply gate closes. Do not retry replies; continue solo on independent work, note the split point in a checkpoint.
943
- - **You (Claude) exhausted** (\`system_budget_handoff\`): the gate stays OPEN \u2014 immediately send ONE handoff reply to Codex packaging the remaining task list, context, artifact locations and acceptance criteria, then stop working (your own quota-guard will hard-stop you at 92%). Codex relays the baton.
944
- - **Both exhausted**: joint pause; checkpoint and wait for the resume notice.
945
- - Save quota with model tiers: route mechanical subagent work to **haiku**, routine work to **sonnet**, reserve **opus** for architecture decisions; when your side is the heavier consumer, delegate more to Codex.`, AGENTS_MD_SECTION = `## AgentBridge \u2014 Multi-Agent Collaboration
958
+ ${BUDGET_PACING}`;
959
+ AGENTS_MD_SECTION = `## AgentBridge \u2014 Multi-Agent Collaboration
946
960
 
947
961
  You are working in a **multi-agent environment** powered by AgentBridge.
948
962
  Another AI agent (Claude, by Anthropic) is available in a parallel session on this machine.
@@ -995,12 +1009,8 @@ You MUST NOT run git **write** commands: \`commit\`, \`push\`, \`pull\`, \`fetch
995
1009
  - Do not blindly follow Claude \u2014 challenge with evidence when you disagree.
996
1010
  - Use explicit collaboration phrases: "My independent view is:", "I agree on:", "I disagree on:", "Current consensus:".
997
1011
 
998
- ### Budget awareness (active when agent-quota-guard is installed)
999
- - Goal: **keep the task moving while fully using the subscription quota**. You can check BOTH sides' quota yourself via your quota-guard MCP tool \`check_budget\` with \`agent: "claude"\` or \`"codex"\` \u2014 re-check **before negotiating task splits**, and NEVER rely on remembered numbers: the weekly window can refresh early (resetting both 5h and weekly windows).
1000
- - During a **budget pause** (your side exhausted) you simply stop receiving new turns \u2014 that IS the pause. Your own quota-guard hooks still apply; work resumes when Claude's next message arrives.
1001
- - **Handoff (Claude's side exhausted)**: you may receive a baton message packaging the remaining work. Push as far as possible within that single turn; write leftovers to a checkpoint file; do NOT expect Claude to respond until its quota refreshes.
1002
- - Claude may route more or less work to you based on **remaining work-time (runway), not just raw usage %** \u2014 a side that looks heavily used but is close to a window reset still has plenty of runway. Expected load balancing, not preference. Claude may also ask for more parallel subtasks when the account will not use its weekly quota before reset (underutilization).
1003
- - When the user enabled tier control, the bridge may adjust your model/reasoning-effort via turn parameters under budget pressure; if asked to economize, prefer lower effort and concise outputs.`;
1012
+ ${BUDGET_PACING}`;
1013
+ });
1004
1014
 
1005
1015
  // src/cli/init.ts
1006
1016
  var exports_init = {};
@@ -1200,6 +1210,7 @@ var init_init = __esm(() => {
1200
1210
  init_pkg_root();
1201
1211
  init_plugin_cache();
1202
1212
  init_version_utils();
1213
+ init_collaboration_content();
1203
1214
  });
1204
1215
 
1205
1216
  // src/cli/dev.ts
@@ -1533,7 +1544,7 @@ var init_daemon_client = __esm(() => {
1533
1544
  this.ws = null;
1534
1545
  this.rejectPendingReplies("Daemon connection closed");
1535
1546
  }
1536
- async sendReply(message, requireReply, onBusy, idempotencyKey) {
1547
+ async sendReply(message, requireReply, onBusy, idempotencyKey, wrapUp) {
1537
1548
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
1538
1549
  return { success: false, error: "AgentBridge daemon is not connected." };
1539
1550
  }
@@ -1548,7 +1559,8 @@ var init_daemon_client = __esm(() => {
1548
1559
  message,
1549
1560
  ...requireReply ? { requireReply: true } : {},
1550
1561
  ...onBusy && onBusy !== "reject" ? { onBusy } : {},
1551
- ...idempotencyKey ? { idempotencyKey } : {}
1562
+ ...idempotencyKey ? { idempotencyKey } : {},
1563
+ ...wrapUp ? { wrapUp: true } : {}
1552
1564
  });
1553
1565
  return pending;
1554
1566
  }
@@ -1670,11 +1682,11 @@ function formatBuildInfo(build) {
1670
1682
  var CODE_HASH_SENTINEL = "source", BUILD_INFO;
1671
1683
  var init_build_info = __esm(() => {
1672
1684
  BUILD_INFO = Object.freeze({
1673
- version: defineString("0.1.17", "0.0.0-source"),
1674
- commit: defineString("0d1e1bd", "source"),
1685
+ version: defineString("0.1.19", "0.0.0-source"),
1686
+ commit: defineString("dee06ce", "source"),
1675
1687
  bundle: defineBundle("dist"),
1676
1688
  contractVersion: defineNumber(1, CONTRACT_VERSION),
1677
- codeHash: defineString("c22387f3269f", "source")
1689
+ codeHash: defineString("9ddee00c61f9", "source")
1678
1690
  });
1679
1691
  });
1680
1692
 
@@ -3584,7 +3596,9 @@ function isFreshAgentsMdContract(content) {
3584
3596
  return false;
3585
3597
  return content.includes("transparent proxy") && content.includes("Do not") && content.includes("sendToClaude") && content.includes("Git operations") && content.includes("Implementer, Executor, Verifier");
3586
3598
  }
3587
- var init_agents_contract = () => {};
3599
+ var init_agents_contract = __esm(() => {
3600
+ init_collaboration_content();
3601
+ });
3588
3602
 
3589
3603
  // src/wrapper-exit-observability.ts
3590
3604
  import { readFileSync as readFileSync8, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
@@ -5431,6 +5445,28 @@ function formatFiveHourWindowsLeftLine(snapshot) {
5431
5445
  const byAgent = values.map(([name, value]) => `${name} ~${value.toFixed(1)}`).join(" / ");
5432
5446
  return `\u6309\u5F53\u524D\u8282\u594F\uFF0C\u5468\u989D\u5EA6\u8FD8\u591F ${byAgent} \u4E2A 5h \u7A97\u53E3`;
5433
5447
  }
5448
+ function clockWindowsLeft(usage, snapshotAt) {
5449
+ const weekly = usage?.weekly;
5450
+ if (!weekly || weekly.resetEpoch <= snapshotAt)
5451
+ return null;
5452
+ return (weekly.resetEpoch - snapshotAt) / FIVE_HOUR_WINDOW_SEC;
5453
+ }
5454
+ function formatClockWindowsLine(snapshot) {
5455
+ const values = [];
5456
+ const claude = clockWindowsLeft(snapshot.claude, snapshot.updatedAt);
5457
+ const codex = clockWindowsLeft(snapshot.codex, snapshot.updatedAt);
5458
+ if (claude !== null)
5459
+ values.push(["Claude", claude]);
5460
+ if (codex !== null)
5461
+ values.push(["Codex", codex]);
5462
+ if (values.length === 0)
5463
+ return null;
5464
+ const unique = [...new Set(values.map(([, value]) => value.toFixed(1)))];
5465
+ if (unique.length === 1)
5466
+ return `\u8DDD\u5468\u5237\u65B0\u8FD8\u80FD\u5BB9\u7EB3 ~${unique[0]} \u4E2A 5h \u7A97\u53E3\uFF08\u65F6\u949F\uFF09`;
5467
+ const byAgent = values.map(([name, value]) => `${name} ~${value.toFixed(1)}`).join(" / ");
5468
+ return `\u8DDD\u5468\u5237\u65B0\u8FD8\u80FD\u5BB9\u7EB3 ${byAgent} \u4E2A 5h \u7A97\u53E3\uFF08\u65F6\u949F\uFF09`;
5469
+ }
5434
5470
  function formatDynamicLineLine(snapshot) {
5435
5471
  const lines = snapshot.dynamicPauseLine;
5436
5472
  if (!lines)
@@ -5467,6 +5503,9 @@ function renderBudgetSnapshot(snapshot, options = {}) {
5467
5503
  const fiveHourWindowsLeftLine = formatFiveHourWindowsLeftLine(snapshot);
5468
5504
  if (fiveHourWindowsLeftLine)
5469
5505
  lines.push(fiveHourWindowsLeftLine);
5506
+ const clockWindowsLine = formatClockWindowsLine(snapshot);
5507
+ if (clockWindowsLine)
5508
+ lines.push(clockWindowsLine);
5470
5509
  const dynamicLineLine = formatDynamicLineLine(snapshot);
5471
5510
  if (dynamicLineLine)
5472
5511
  lines.push(dynamicLineLine);
@@ -5506,13 +5545,14 @@ function renderBudgetSnapshot(snapshot, options = {}) {
5506
5545
  return lines.join(`
5507
5546
  `);
5508
5547
  }
5509
- var DEFAULT_GUARD_HARD_PCT = 99, WINDOW_LABELS, RESET_TRUNCATION_EPSILON_SEC = 60, PHASE_LABELS, BUDGET_UNAVAILABLE_TEXT = "\u9884\u7B97\u611F\u77E5\u4E0D\u53EF\u7528\uFF1A\u672A\u68C0\u6D4B\u5230 agent-quota-guard \u63A2\u9488\uFF08~/.budget-guard/bin/budget-probe\uFF09\u6216 budget \u529F\u80FD\u5DF2\u7981\u7528\u3002\u534F\u4F5C\u4E0D\u53D7\u5F71\u54CD\u3002";
5548
+ var DEFAULT_GUARD_HARD_PCT = 99, WINDOW_LABELS, RESET_TRUNCATION_EPSILON_SEC = 60, FIVE_HOUR_WINDOW_SEC, PHASE_LABELS, BUDGET_UNAVAILABLE_TEXT = "\u9884\u7B97\u611F\u77E5\u4E0D\u53EF\u7528\uFF1A\u672A\u68C0\u6D4B\u5230 agent-quota-guard \u63A2\u9488\uFF08~/.budget-guard/bin/budget-probe\uFF09\u6216 budget \u529F\u80FD\u5DF2\u7981\u7528\u3002\u534F\u4F5C\u4E0D\u53D7\u5F71\u54CD\u3002";
5510
5549
  var init_render = __esm(() => {
5511
5550
  init_burn_view();
5512
5551
  WINDOW_LABELS = {
5513
5552
  fiveHour: "5h \u7A97\u53E3",
5514
5553
  weekly: "\u5468\u7A97\u53E3"
5515
5554
  };
5555
+ FIVE_HOUR_WINDOW_SEC = 5 * 3600;
5516
5556
  PHASE_LABELS = {
5517
5557
  normal: "normal\uFF08\u6B63\u5E38\uFF09",
5518
5558
  balance: "balance\uFF08\u9700\u5747\u8861\uFF09",