@raysonmeng/agentbridge 0.1.20 → 0.1.21

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.20",
15
+ "version": "0.1.21",
16
16
  "author": {
17
17
  "name": "AgentBridge Contributors",
18
18
  "email": "raysonmeng@qq.com"
package/dist/cli.js CHANGED
@@ -179,7 +179,7 @@ function parsePositiveIntEnv(name, fallback, log = () => {}, env = process.env)
179
179
  var require_package = __commonJS((exports, module) => {
180
180
  module.exports = {
181
181
  name: "@raysonmeng/agentbridge",
182
- version: "0.1.20",
182
+ version: "0.1.21",
183
183
  description: "Bridge between Claude Code and Codex \u2014 bidirectional agent communication via MCP Channel + JSON-RPC",
184
184
  type: "module",
185
185
  packageManager: "bun@1.3.11",
@@ -1682,11 +1682,11 @@ function formatBuildInfo(build) {
1682
1682
  var CODE_HASH_SENTINEL = "source", BUILD_INFO;
1683
1683
  var init_build_info = __esm(() => {
1684
1684
  BUILD_INFO = Object.freeze({
1685
- version: defineString("0.1.20", "0.0.0-source"),
1686
- commit: defineString("d6034c6", "source"),
1685
+ version: defineString("0.1.21", "0.0.0-source"),
1686
+ commit: defineString("ddee9b2", "source"),
1687
1687
  bundle: defineBundle("dist"),
1688
1688
  contractVersion: defineNumber(1, CONTRACT_VERSION),
1689
- codeHash: defineString("d2425ba159fe", "source")
1689
+ codeHash: defineString("8354efa9fcad", "source")
1690
1690
  });
1691
1691
  });
1692
1692
 
@@ -5294,6 +5294,45 @@ var init_pairs = __esm(() => {
5294
5294
  init_thread_state();
5295
5295
  init_kill();
5296
5296
  });
5297
+ // src/budget/format-time.ts
5298
+ function parts(epochSeconds, options) {
5299
+ const fmt = new Intl.DateTimeFormat("en-CA", {
5300
+ timeZone: BEIJING_TZ,
5301
+ hour12: false,
5302
+ ...options
5303
+ });
5304
+ const out = {};
5305
+ for (const part of fmt.formatToParts(new Date(epochSeconds * 1000))) {
5306
+ out[part.type] = part.value;
5307
+ }
5308
+ return out;
5309
+ }
5310
+ function formatBeijing(epochSeconds) {
5311
+ if (!epochSeconds || epochSeconds <= 0)
5312
+ return "\u672A\u77E5";
5313
+ const d = new Date(epochSeconds * 1000);
5314
+ if (Number.isNaN(d.getTime()))
5315
+ return "\u672A\u77E5";
5316
+ const p = parts(epochSeconds, {
5317
+ year: "numeric",
5318
+ month: "2-digit",
5319
+ day: "2-digit",
5320
+ hour: "2-digit",
5321
+ minute: "2-digit"
5322
+ });
5323
+ return `${p.year}-${p.month}-${p.day} ${p.hour}:${p.minute}`;
5324
+ }
5325
+ function formatBeijingClock(epochSeconds) {
5326
+ if (!epochSeconds || epochSeconds <= 0)
5327
+ return "\u672A\u77E5";
5328
+ const d = new Date(epochSeconds * 1000);
5329
+ if (Number.isNaN(d.getTime()))
5330
+ return "\u672A\u77E5";
5331
+ const p = parts(epochSeconds, { hour: "2-digit", minute: "2-digit" });
5332
+ return `${p.hour}:${p.minute}`;
5333
+ }
5334
+ var BEIJING_TZ = "Asia/Shanghai";
5335
+
5297
5336
  // src/budget/types.ts
5298
5337
  var STALE_MAX_AGE_SEC = 600;
5299
5338
 
@@ -5349,9 +5388,7 @@ function resolveGuardHardHint(env = process.env) {
5349
5388
  return parsed;
5350
5389
  }
5351
5390
  function formatEpoch(epochSeconds) {
5352
- if (!epochSeconds || epochSeconds <= 0)
5353
- return "\u672A\u77E5";
5354
- return new Date(epochSeconds * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
5391
+ return formatBeijing(epochSeconds);
5355
5392
  }
5356
5393
  function formatWindow(window, label) {
5357
5394
  if (!window)
@@ -5361,25 +5398,25 @@ function formatWindow(window, label) {
5361
5398
  function formatAgent(name, usage, snapshotAt) {
5362
5399
  if (!usage)
5363
5400
  return `${name}\uFF1A\u672A\u77E5\uFF08\u63A2\u6D4B\u4E0D\u53EF\u7528\uFF09`;
5364
- const parts = [
5401
+ const parts2 = [
5365
5402
  formatWindow(usage.fiveHour, "5h"),
5366
5403
  formatWindow(usage.weekly, "\u5468"),
5367
5404
  `\u95E8\u63A7 ${usage.gateUtil}%`,
5368
5405
  `\u9884\u8B66 ${usage.warnUtil}%`
5369
5406
  ];
5370
5407
  if (usage.rateLimitedUntil > 0) {
5371
- parts.push(`\u9650\u6D41\u81F3 ${formatEpoch(usage.rateLimitedUntil)}`);
5408
+ parts2.push(`\u9650\u6D41\u81F3 ${formatEpoch(usage.rateLimitedUntil)}`);
5372
5409
  }
5373
5410
  if (usage.parsedVia === "positional") {
5374
- parts.push("\u26A0\uFE0F \u7A97\u53E3\u8BC6\u522B\u4F7F\u7528\u4F4D\u7F6E\u515C\u5E95");
5411
+ parts2.push("\u26A0\uFE0F \u7A97\u53E3\u8BC6\u522B\u4F7F\u7528\u4F4D\u7F6E\u515C\u5E95");
5375
5412
  }
5376
5413
  const ageSec = usage.fetchedAt > 0 ? snapshotAt - usage.fetchedAt : 0;
5377
5414
  if (ageSec > 300) {
5378
- parts.push(`\u26A0\uFE0F \u6570\u636E\u91C7\u96C6\u4E8E ${Math.round(ageSec / 60)} \u5206\u949F\u524D`);
5415
+ parts2.push(`\u26A0\uFE0F \u6570\u636E\u91C7\u96C6\u4E8E ${Math.round(ageSec / 60)} \u5206\u949F\u524D`);
5379
5416
  } else if (usage.stale) {
5380
- parts.push("\uFF08\u7F13\u5B58\u6570\u636E\uFF09");
5417
+ parts2.push("\uFF08\u7F13\u5B58\u6570\u636E\uFF09");
5381
5418
  }
5382
- return `${name}\uFF1A${parts.join(" \xB7 ")}`;
5419
+ return `${name}\uFF1A${parts2.join(" \xB7 ")}`;
5383
5420
  }
5384
5421
  function formatDuration(seconds) {
5385
5422
  const totalMinutes = Math.max(0, Math.round(seconds / 60));
@@ -5390,10 +5427,7 @@ function formatDuration(seconds) {
5390
5427
  return `${hours}\u5C0F\u65F6${minutes}\u5206\u949F`;
5391
5428
  }
5392
5429
  function formatClockTime(epochSeconds) {
5393
- const date = new Date(epochSeconds * 1000);
5394
- const hh = String(date.getHours()).padStart(2, "0");
5395
- const mm = String(date.getMinutes()).padStart(2, "0");
5396
- return `${hh}:${mm}`;
5430
+ return formatBeijingClock(epochSeconds);
5397
5431
  }
5398
5432
  function formatWindowRate(label, rate) {
5399
5433
  if (!rate)
@@ -5414,20 +5448,20 @@ function formatRunwaySegment(runway, basisWindow, snapshotAt) {
5414
5448
  return `\u7EA6\u53EF\u518D\u5DE5\u4F5C ${formatDuration(runway.seconds)}\uFF08${clockNote}${WINDOW_LABELS[runway.basis]}\u4E3A\u7EA6\u675F\uFF09`;
5415
5449
  }
5416
5450
  function formatBurnRateLine(name, usage, rates, runway, snapshotAt, guardHardPct) {
5417
- const parts = [
5451
+ const parts2 = [
5418
5452
  formatWindowRate("5h", rates.fiveHour),
5419
5453
  formatWindowRate("\u5468", rates.weekly)
5420
5454
  ].filter((part) => part !== null);
5421
- if (parts.length === 0 && !runway)
5455
+ if (parts2.length === 0 && !runway)
5422
5456
  return null;
5423
5457
  if (runway) {
5424
5458
  const basisWindow = usage ? usage[runway.basis] : null;
5425
- parts.push(formatRunwaySegment(runway, basisWindow, snapshotAt));
5459
+ parts2.push(formatRunwaySegment(runway, basisWindow, snapshotAt));
5426
5460
  }
5427
5461
  if (guardHardPct !== null) {
5428
- parts.push(`\u5916\u5C42 guard \u786C\u7EBF ${guardHardPct}%\uFF08v3 \u4E0D\u53EF\u8D8A\u8FC7\uFF1Brunway \u4E3A\u4E2D\u6027\u53E3\u5F84\uFF0CClaude \u4F1A\u5148\u5728\u786C\u7EBF\u88AB\u5916\u5C42\u505C\u4F4F\uFF09`);
5462
+ parts2.push(`\u5916\u5C42 guard \u786C\u7EBF ${guardHardPct}%\uFF08v3 \u4E0D\u53EF\u8D8A\u8FC7\uFF1Brunway \u4E3A\u4E2D\u6027\u53E3\u5F84\uFF0CClaude \u4F1A\u5148\u5728\u786C\u7EBF\u88AB\u5916\u5C42\u505C\u4F4F\uFF09`);
5429
5463
  }
5430
- return `${name} \u71C3\u5C3D\u7387\uFF1A${parts.join(" \xB7 ")}`;
5464
+ return `${name} \u71C3\u5C3D\u7387\uFF1A${parts2.join(" \xB7 ")}`;
5431
5465
  }
5432
5466
  function formatFiveHourWindowsLeftLine(snapshot) {
5433
5467
  const values = [];
@@ -5471,7 +5505,7 @@ function formatDynamicLineLine(snapshot) {
5471
5505
  const lines = snapshot.dynamicPauseLine;
5472
5506
  if (!lines)
5473
5507
  return null;
5474
- const parts = [];
5508
+ const parts2 = [];
5475
5509
  const entries = [
5476
5510
  ["Claude", lines.claude, snapshot.claude],
5477
5511
  ["Codex", lines.codex, snapshot.codex]
@@ -5480,16 +5514,16 @@ function formatDynamicLineLine(snapshot) {
5480
5514
  if (line === null)
5481
5515
  continue;
5482
5516
  const headroom = usage ? `\uFF08util ${usage.gateUtil}%\uFF0C\u4F59\u91CF ${(line - usage.gateUtil).toFixed(1)}\uFF09` : "";
5483
- parts.push(`${name} ${line.toFixed(1)}%${headroom}`);
5517
+ parts2.push(`${name} ${line.toFixed(1)}%${headroom}`);
5484
5518
  }
5485
- if (parts.length === 0)
5519
+ if (parts2.length === 0)
5486
5520
  return null;
5487
- return `\u52A8\u6001\u6682\u505C\u7EBF\uFF1A${parts.join(" \xB7 ")}`;
5521
+ return `\u52A8\u6001\u6682\u505C\u7EBF\uFF1A${parts2.join(" \xB7 ")}`;
5488
5522
  }
5489
5523
  function renderBudgetSnapshot(snapshot, options = {}) {
5490
5524
  const guardHardPct = options.guardHardPct ?? resolveGuardHardHint();
5491
5525
  const lines = [];
5492
- lines.push(`\u3010\u9884\u7B97\u5FEB\u7167 \xB7 \u8D26\u53F7\u7EA7\u3011\u9636\u6BB5\uFF1A${PHASE_LABELS[snapshot.phase] ?? snapshot.phase} \xB7 \u66F4\u65B0\u4E8E ${formatEpoch(snapshot.updatedAt)}`);
5526
+ lines.push(`\u3010\u9884\u7B97\u5FEB\u7167 \xB7 \u8D26\u53F7\u7EA7\u3011\u9636\u6BB5\uFF1A${PHASE_LABELS[snapshot.phase] ?? snapshot.phase} \xB7 \u66F4\u65B0\u4E8E ${formatEpoch(snapshot.updatedAt)}\uFF08\u65F6\u95F4\u5747\u4E3A\u5317\u4EAC\u65F6\u95F4 UTC+8\uFF09`);
5493
5527
  lines.push(formatAgent("Claude", snapshot.claude, snapshot.updatedAt));
5494
5528
  lines.push(formatAgent("Codex", snapshot.codex, snapshot.updatedAt));
5495
5529
  if (snapshot.burnRate) {
@@ -5733,8 +5767,8 @@ function extractUserText(entry) {
5733
5767
  const content = entry.payload.content;
5734
5768
  if (!Array.isArray(content))
5735
5769
  return null;
5736
- const parts = content.map((item) => typeof item?.text === "string" ? item.text : typeof item?.input_text?.text === "string" ? item.input_text.text : null).filter((part) => !!part);
5737
- return parts.length > 0 ? parts.join(`
5770
+ const parts2 = content.map((item) => typeof item?.text === "string" ? item.text : typeof item?.input_text?.text === "string" ? item.input_text.text : null).filter((part) => !!part);
5771
+ return parts2.length > 0 ? parts2.join(`
5738
5772
  `) : null;
5739
5773
  }
5740
5774
  return null;
package/dist/daemon.js CHANGED
@@ -29,11 +29,11 @@ function defineNumber(value, fallback) {
29
29
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
30
30
  }
31
31
  var BUILD_INFO = Object.freeze({
32
- version: defineString("0.1.20", "0.0.0-source"),
33
- commit: defineString("d6034c6", "source"),
32
+ version: defineString("0.1.21", "0.0.0-source"),
33
+ commit: defineString("ddee9b2", "source"),
34
34
  bundle: defineBundle("dist"),
35
35
  contractVersion: defineNumber(1, CONTRACT_VERSION),
36
- codeHash: defineString("d2425ba159fe", "source")
36
+ codeHash: defineString("8354efa9fcad", "source")
37
37
  });
38
38
  function daemonStatusBuildInfo() {
39
39
  return { ...BUILD_INFO };
@@ -3861,6 +3861,36 @@ function retryAfterMsForResume(resumeAfterEpoch, nowMs) {
3861
3861
  return remainingMs > 0 ? remainingMs : undefined;
3862
3862
  }
3863
3863
 
3864
+ // src/budget/format-time.ts
3865
+ var BEIJING_TZ = "Asia/Shanghai";
3866
+ function parts(epochSeconds, options) {
3867
+ const fmt = new Intl.DateTimeFormat("en-CA", {
3868
+ timeZone: BEIJING_TZ,
3869
+ hour12: false,
3870
+ ...options
3871
+ });
3872
+ const out = {};
3873
+ for (const part of fmt.formatToParts(new Date(epochSeconds * 1000))) {
3874
+ out[part.type] = part.value;
3875
+ }
3876
+ return out;
3877
+ }
3878
+ function formatBeijing(epochSeconds) {
3879
+ if (!epochSeconds || epochSeconds <= 0)
3880
+ return "\u672A\u77E5";
3881
+ const d = new Date(epochSeconds * 1000);
3882
+ if (Number.isNaN(d.getTime()))
3883
+ return "\u672A\u77E5";
3884
+ const p = parts(epochSeconds, {
3885
+ year: "numeric",
3886
+ month: "2-digit",
3887
+ day: "2-digit",
3888
+ hour: "2-digit",
3889
+ minute: "2-digit"
3890
+ });
3891
+ return `${p.year}-${p.month}-${p.day} ${p.hour}:${p.minute}`;
3892
+ }
3893
+
3864
3894
  // src/budget/types.ts
3865
3895
  var STALE_MAX_AGE_SEC = 600;
3866
3896
 
@@ -4149,14 +4179,12 @@ function pct2(value) {
4149
4179
  return `${Math.round(value * 10) / 10}%`;
4150
4180
  }
4151
4181
  function formatEpoch(epoch) {
4152
- if (!epoch || epoch <= 0)
4153
- return "\u672A\u77E5";
4154
- return new Date(epoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
4182
+ return formatBeijing(epoch);
4155
4183
  }
4156
4184
  function usageSummary(name, usage) {
4157
4185
  if (!usage)
4158
4186
  return `${AGENT_LABEL2[name]} \u672A\u77E5`;
4159
- return `${AGENT_LABEL2[name]} gate=${pct2(usage.gateUtil)} warn=${pct2(usage.warnUtil)} 5h\u91CD\u7F6E=${formatEpoch(usage.fiveHour?.resetEpoch ?? 0)}`;
4187
+ return `${AGENT_LABEL2[name]} gate=${pct2(usage.gateUtil)} warn=${pct2(usage.warnUtil)} 5h\u91CD\u7F6E=${formatEpoch(usage.fiveHour?.resetEpoch ?? 0)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
4160
4188
  }
4161
4189
  function resumeAfterEpoch(claude, codex, cfg, now) {
4162
4190
  const epochs = [
@@ -4452,7 +4480,7 @@ function pct3(value) {
4452
4480
  return `${Math.round(value * 10) / 10}%`;
4453
4481
  }
4454
4482
  function formatEpoch2(epoch) {
4455
- return new Date(epoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
4483
+ return formatBeijing(epoch);
4456
4484
  }
4457
4485
  var INITIAL_FINGERPRINT_STATE = {
4458
4486
  side: null,
@@ -4500,7 +4528,7 @@ function activeSideReason(agent, usage, cfg, now) {
4500
4528
  if (!usage)
4501
4529
  return `${AGENT_LABEL3[agent]} \u63A2\u6D4B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u4FDD\u6301\u4E0A\u4E00\u8F6E\u9884\u7B97\u5E72\u9884`;
4502
4530
  if (usage.rateLimitedUntil > now) {
4503
- return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}`;
4531
+ return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
4504
4532
  }
4505
4533
  const decision = agentShouldPause(agent, usage, cfg, now);
4506
4534
  if (decision.pause)
@@ -4643,7 +4671,7 @@ function admissionReason(side, state, cfg) {
4643
4671
  if (!usage)
4644
4672
  return `${AGENT_LABEL3[agent]} \u63A2\u6D4B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u4FDD\u6301\u4E0A\u4E00\u8F6E\u6536\u5C3E\u4FDD\u62A4`;
4645
4673
  if (usage.rateLimitedUntil > state.now) {
4646
- return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}\uFF0C\u4FDD\u6301\u6536\u5C3E\u4FDD\u62A4`;
4674
+ return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09\uFF0C\u4FDD\u6301\u6536\u5C3E\u4FDD\u62A4`;
4647
4675
  }
4648
4676
  const decision = agentShouldAdmitClose(agent, usage, cfg, state.now);
4649
4677
  if (decision.admitClose)
@@ -6778,13 +6806,13 @@ function stopBudgetCoordinator() {
6778
6806
  function budgetPauseGateError() {
6779
6807
  const snapshot = budgetCoordinator?.getSnapshot() ?? null;
6780
6808
  const reason = snapshot?.pauseReason ?? "Codex \u4FA7\u989D\u5EA6\u63A5\u8FD1\u8017\u5C3D";
6781
- const resumeAt = snapshot?.resumeAfterEpoch ? new Date(snapshot.resumeAfterEpoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z") : null;
6809
+ const resumeAt = snapshot?.resumeAfterEpoch ? `${formatBeijing(snapshot.resumeAfterEpoch)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09` : null;
6782
6810
  const sideHint = snapshot?.pauseSide === "both" ? "\u53CC\u4FA7\u989D\u5EA6\u5747\u5DF2\u8017\u5C3D\uFF0C\u8BF7\u5199 checkpoint \u7B49\u5F85\u5237\u65B0" : "\u4F60\u53EF\u7EE7\u7EED solo \u63A8\u8FDB\u53EF\u72EC\u7ACB\u90E8\u5206\uFF0C\u5E76\u5199 checkpoint \u6807\u6CE8\u5206\u5DE5\u65AD\u70B9";
6783
6811
  const reopenText = `Codex \u4FA7\u5404\u7A97\u53E3 util \u56DE\u843D\u81F3\u52A8\u6001\u6682\u505C\u7EBF \u2212 ${BUDGET_CONFIG.maximize.resumeHysteresisPct}% \u4EE5\u4E0B\u6216\u5BF9\u5E94\u7A97\u53E3\u5237\u65B0\u540E\u95F8\u95E8\u81EA\u52A8\u653E\u5F00`;
6784
6812
  return `\u9884\u7B97\u6682\u505C\uFF08\u95F8\u95E8\u5173\u95ED\uFF09\uFF0C\u5DF2\u62D2\u7EDD\u8F6C\u53D1\uFF1A${reason}\u3002` + reopenText + (resumeAt ? `\uFF08\u9884\u8BA1\u6062\u590D ${resumeAt}\uFF0C\u4EE5\u5B9E\u6D4B\u4E3A\u51C6\uFF1B\u63D0\u524D\u5237\u65B0\u4F1A\u66F4\u65E9\u89E3\u9664\uFF09` : "") + `\u3002\u6536\u5230 RESUME \u901A\u77E5\u524D\u8BF7\u52FF\u91CD\u8BD5\u5411 Codex \u53D1\u9001 reply\uFF1B${sideHint}\u3002`;
6785
6813
  }
6786
6814
  function budgetAdmissionGateError(windowResetEpoch, wrapUpLeft, quotaExhausted) {
6787
- const resetAt = windowResetEpoch > 0 ? new Date(windowResetEpoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z") : "\u672A\u77E5";
6815
+ const resetAt = windowResetEpoch > 0 ? `${formatBeijing(windowResetEpoch)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09` : "\u672A\u77E5";
6788
6816
  const quota = BUDGET_CONFIG.maximize.wrapUpQuota;
6789
6817
  if (quotaExhausted) {
6790
6818
  return `\u989D\u5EA6\u7A97\u53E3\u6536\u5C3E\u4FDD\u62A4\u4E2D\uFF08admission-closed\uFF09\uFF1A\u672C\u7A97\u53E3 wrap-up \u914D\u989D\uFF08\u6BCF\u7A97\u53E3 ${quota} \u4E2A\uFF09\u5DF2\u7528\u5C3D\uFF0C\u5DF2\u62D2\u7EDD\u8F6C\u53D1\u3002` + `\u8BF7\u52FF\u518D\u6D3E\u65B0\u4EFB\u52A1\uFF1B\u5199 checkpoint\uFF0C\u7B49\u989D\u5EA6\u7A97\u53E3\u5237\u65B0\uFF08\u7EA6 ${resetAt}\uFF09\u540E\u518D\u7EE7\u7EED\u3002`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raysonmeng/agentbridge",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "Bridge between Claude Code and Codex — bidirectional agent communication via MCP Channel + JSON-RPC",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.11",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentbridge",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "Bridge Claude Code and Codex with a shared daemon, push channel delivery, and bidirectional reply tooling.",
5
5
  "author": {
6
6
  "name": "AgentBridge Contributors",
@@ -13866,6 +13866,45 @@ class StateDirResolver {
13866
13866
  }
13867
13867
  }
13868
13868
 
13869
+ // src/budget/format-time.ts
13870
+ var BEIJING_TZ = "Asia/Shanghai";
13871
+ function parts(epochSeconds, options) {
13872
+ const fmt = new Intl.DateTimeFormat("en-CA", {
13873
+ timeZone: BEIJING_TZ,
13874
+ hour12: false,
13875
+ ...options
13876
+ });
13877
+ const out = {};
13878
+ for (const part of fmt.formatToParts(new Date(epochSeconds * 1000))) {
13879
+ out[part.type] = part.value;
13880
+ }
13881
+ return out;
13882
+ }
13883
+ function formatBeijing(epochSeconds) {
13884
+ if (!epochSeconds || epochSeconds <= 0)
13885
+ return "\u672A\u77E5";
13886
+ const d = new Date(epochSeconds * 1000);
13887
+ if (Number.isNaN(d.getTime()))
13888
+ return "\u672A\u77E5";
13889
+ const p = parts(epochSeconds, {
13890
+ year: "numeric",
13891
+ month: "2-digit",
13892
+ day: "2-digit",
13893
+ hour: "2-digit",
13894
+ minute: "2-digit"
13895
+ });
13896
+ return `${p.year}-${p.month}-${p.day} ${p.hour}:${p.minute}`;
13897
+ }
13898
+ function formatBeijingClock(epochSeconds) {
13899
+ if (!epochSeconds || epochSeconds <= 0)
13900
+ return "\u672A\u77E5";
13901
+ const d = new Date(epochSeconds * 1000);
13902
+ if (Number.isNaN(d.getTime()))
13903
+ return "\u672A\u77E5";
13904
+ const p = parts(epochSeconds, { hour: "2-digit", minute: "2-digit" });
13905
+ return `${p.hour}:${p.minute}`;
13906
+ }
13907
+
13869
13908
  // src/budget/types.ts
13870
13909
  var STALE_MAX_AGE_SEC = 600;
13871
13910
 
@@ -13909,9 +13948,7 @@ function resolveGuardHardHint(env = process.env) {
13909
13948
  return parsed;
13910
13949
  }
13911
13950
  function formatEpoch(epochSeconds) {
13912
- if (!epochSeconds || epochSeconds <= 0)
13913
- return "\u672A\u77E5";
13914
- return new Date(epochSeconds * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
13951
+ return formatBeijing(epochSeconds);
13915
13952
  }
13916
13953
  function formatWindow(window, label) {
13917
13954
  if (!window)
@@ -13921,25 +13958,25 @@ function formatWindow(window, label) {
13921
13958
  function formatAgent(name, usage, snapshotAt) {
13922
13959
  if (!usage)
13923
13960
  return `${name}\uFF1A\u672A\u77E5\uFF08\u63A2\u6D4B\u4E0D\u53EF\u7528\uFF09`;
13924
- const parts = [
13961
+ const parts2 = [
13925
13962
  formatWindow(usage.fiveHour, "5h"),
13926
13963
  formatWindow(usage.weekly, "\u5468"),
13927
13964
  `\u95E8\u63A7 ${usage.gateUtil}%`,
13928
13965
  `\u9884\u8B66 ${usage.warnUtil}%`
13929
13966
  ];
13930
13967
  if (usage.rateLimitedUntil > 0) {
13931
- parts.push(`\u9650\u6D41\u81F3 ${formatEpoch(usage.rateLimitedUntil)}`);
13968
+ parts2.push(`\u9650\u6D41\u81F3 ${formatEpoch(usage.rateLimitedUntil)}`);
13932
13969
  }
13933
13970
  if (usage.parsedVia === "positional") {
13934
- parts.push("\u26A0\uFE0F \u7A97\u53E3\u8BC6\u522B\u4F7F\u7528\u4F4D\u7F6E\u515C\u5E95");
13971
+ parts2.push("\u26A0\uFE0F \u7A97\u53E3\u8BC6\u522B\u4F7F\u7528\u4F4D\u7F6E\u515C\u5E95");
13935
13972
  }
13936
13973
  const ageSec = usage.fetchedAt > 0 ? snapshotAt - usage.fetchedAt : 0;
13937
13974
  if (ageSec > 300) {
13938
- parts.push(`\u26A0\uFE0F \u6570\u636E\u91C7\u96C6\u4E8E ${Math.round(ageSec / 60)} \u5206\u949F\u524D`);
13975
+ parts2.push(`\u26A0\uFE0F \u6570\u636E\u91C7\u96C6\u4E8E ${Math.round(ageSec / 60)} \u5206\u949F\u524D`);
13939
13976
  } else if (usage.stale) {
13940
- parts.push("\uFF08\u7F13\u5B58\u6570\u636E\uFF09");
13977
+ parts2.push("\uFF08\u7F13\u5B58\u6570\u636E\uFF09");
13941
13978
  }
13942
- return `${name}\uFF1A${parts.join(" \xB7 ")}`;
13979
+ return `${name}\uFF1A${parts2.join(" \xB7 ")}`;
13943
13980
  }
13944
13981
  var WINDOW_LABELS = {
13945
13982
  fiveHour: "5h \u7A97\u53E3",
@@ -13955,10 +13992,7 @@ function formatDuration(seconds) {
13955
13992
  return `${hours}\u5C0F\u65F6${minutes}\u5206\u949F`;
13956
13993
  }
13957
13994
  function formatClockTime(epochSeconds) {
13958
- const date4 = new Date(epochSeconds * 1000);
13959
- const hh = String(date4.getHours()).padStart(2, "0");
13960
- const mm = String(date4.getMinutes()).padStart(2, "0");
13961
- return `${hh}:${mm}`;
13995
+ return formatBeijingClock(epochSeconds);
13962
13996
  }
13963
13997
  function formatWindowRate(label, rate) {
13964
13998
  if (!rate)
@@ -13979,20 +14013,20 @@ function formatRunwaySegment(runway, basisWindow, snapshotAt) {
13979
14013
  return `\u7EA6\u53EF\u518D\u5DE5\u4F5C ${formatDuration(runway.seconds)}\uFF08${clockNote}${WINDOW_LABELS[runway.basis]}\u4E3A\u7EA6\u675F\uFF09`;
13980
14014
  }
13981
14015
  function formatBurnRateLine(name, usage, rates, runway, snapshotAt, guardHardPct) {
13982
- const parts = [
14016
+ const parts2 = [
13983
14017
  formatWindowRate("5h", rates.fiveHour),
13984
14018
  formatWindowRate("\u5468", rates.weekly)
13985
14019
  ].filter((part) => part !== null);
13986
- if (parts.length === 0 && !runway)
14020
+ if (parts2.length === 0 && !runway)
13987
14021
  return null;
13988
14022
  if (runway) {
13989
14023
  const basisWindow = usage ? usage[runway.basis] : null;
13990
- parts.push(formatRunwaySegment(runway, basisWindow, snapshotAt));
14024
+ parts2.push(formatRunwaySegment(runway, basisWindow, snapshotAt));
13991
14025
  }
13992
14026
  if (guardHardPct !== null) {
13993
- parts.push(`\u5916\u5C42 guard \u786C\u7EBF ${guardHardPct}%\uFF08v3 \u4E0D\u53EF\u8D8A\u8FC7\uFF1Brunway \u4E3A\u4E2D\u6027\u53E3\u5F84\uFF0CClaude \u4F1A\u5148\u5728\u786C\u7EBF\u88AB\u5916\u5C42\u505C\u4F4F\uFF09`);
14027
+ parts2.push(`\u5916\u5C42 guard \u786C\u7EBF ${guardHardPct}%\uFF08v3 \u4E0D\u53EF\u8D8A\u8FC7\uFF1Brunway \u4E3A\u4E2D\u6027\u53E3\u5F84\uFF0CClaude \u4F1A\u5148\u5728\u786C\u7EBF\u88AB\u5916\u5C42\u505C\u4F4F\uFF09`);
13994
14028
  }
13995
- return `${name} \u71C3\u5C3D\u7387\uFF1A${parts.join(" \xB7 ")}`;
14029
+ return `${name} \u71C3\u5C3D\u7387\uFF1A${parts2.join(" \xB7 ")}`;
13996
14030
  }
13997
14031
  function formatFiveHourWindowsLeftLine(snapshot) {
13998
14032
  const values = [];
@@ -14037,7 +14071,7 @@ function formatDynamicLineLine(snapshot) {
14037
14071
  const lines = snapshot.dynamicPauseLine;
14038
14072
  if (!lines)
14039
14073
  return null;
14040
- const parts = [];
14074
+ const parts2 = [];
14041
14075
  const entries = [
14042
14076
  ["Claude", lines.claude, snapshot.claude],
14043
14077
  ["Codex", lines.codex, snapshot.codex]
@@ -14046,11 +14080,11 @@ function formatDynamicLineLine(snapshot) {
14046
14080
  if (line === null)
14047
14081
  continue;
14048
14082
  const headroom = usage ? `\uFF08util ${usage.gateUtil}%\uFF0C\u4F59\u91CF ${(line - usage.gateUtil).toFixed(1)}\uFF09` : "";
14049
- parts.push(`${name} ${line.toFixed(1)}%${headroom}`);
14083
+ parts2.push(`${name} ${line.toFixed(1)}%${headroom}`);
14050
14084
  }
14051
- if (parts.length === 0)
14085
+ if (parts2.length === 0)
14052
14086
  return null;
14053
- return `\u52A8\u6001\u6682\u505C\u7EBF\uFF1A${parts.join(" \xB7 ")}`;
14087
+ return `\u52A8\u6001\u6682\u505C\u7EBF\uFF1A${parts2.join(" \xB7 ")}`;
14054
14088
  }
14055
14089
  var PHASE_LABELS = {
14056
14090
  normal: "normal\uFF08\u6B63\u5E38\uFF09",
@@ -14062,7 +14096,7 @@ var PHASE_LABELS = {
14062
14096
  function renderBudgetSnapshot(snapshot, options = {}) {
14063
14097
  const guardHardPct = options.guardHardPct ?? resolveGuardHardHint();
14064
14098
  const lines = [];
14065
- lines.push(`\u3010\u9884\u7B97\u5FEB\u7167 \xB7 \u8D26\u53F7\u7EA7\u3011\u9636\u6BB5\uFF1A${PHASE_LABELS[snapshot.phase] ?? snapshot.phase} \xB7 \u66F4\u65B0\u4E8E ${formatEpoch(snapshot.updatedAt)}`);
14099
+ lines.push(`\u3010\u9884\u7B97\u5FEB\u7167 \xB7 \u8D26\u53F7\u7EA7\u3011\u9636\u6BB5\uFF1A${PHASE_LABELS[snapshot.phase] ?? snapshot.phase} \xB7 \u66F4\u65B0\u4E8E ${formatEpoch(snapshot.updatedAt)}\uFF08\u65F6\u95F4\u5747\u4E3A\u5317\u4EAC\u65F6\u95F4 UTC+8\uFF09`);
14066
14100
  lines.push(formatAgent("Claude", snapshot.claude, snapshot.updatedAt));
14067
14101
  lines.push(formatAgent("Codex", snapshot.codex, snapshot.updatedAt));
14068
14102
  if (snapshot.burnRate) {
@@ -14356,21 +14390,21 @@ Codex: ${msg.content}`;
14356
14390
  `);
14357
14391
  const noticeText = notices.map((notice) => `WARNING: ${notice}`).join(`
14358
14392
  `);
14359
- const parts = [];
14393
+ const parts2 = [];
14360
14394
  if (count > 0) {
14361
- parts.push(`[${count} new message${count > 1 ? "s" : ""} from Codex]
14395
+ parts2.push(`[${count} new message${count > 1 ? "s" : ""} from Codex]
14362
14396
  chat_id: ${this.sessionId}`);
14363
14397
  }
14364
14398
  if (noticeText)
14365
- parts.push(noticeText);
14399
+ parts2.push(noticeText);
14366
14400
  if (formatted)
14367
- parts.push(formatted);
14401
+ parts2.push(formatted);
14368
14402
  this.log(`get_messages returning ${count} message(s) ` + `(instance=${this.instanceId}, dropped=${dropped}, oversized=${oversized}, oversizedBytes=${oversizedBytes})`);
14369
14403
  return {
14370
14404
  content: [
14371
14405
  {
14372
14406
  type: "text",
14373
- text: parts.join(`
14407
+ text: parts2.join(`
14374
14408
 
14375
14409
  `)
14376
14410
  }
@@ -14633,11 +14667,11 @@ function defineNumber(value, fallback) {
14633
14667
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14634
14668
  }
14635
14669
  var BUILD_INFO = Object.freeze({
14636
- version: defineString("0.1.20", "0.0.0-source"),
14637
- commit: defineString("d6034c6", "source"),
14670
+ version: defineString("0.1.21", "0.0.0-source"),
14671
+ commit: defineString("ddee9b2", "source"),
14638
14672
  bundle: defineBundle("plugin"),
14639
14673
  contractVersion: defineNumber(1, CONTRACT_VERSION),
14640
- codeHash: defineString("d2425ba159fe", "source")
14674
+ codeHash: defineString("8354efa9fcad", "source")
14641
14675
  });
14642
14676
  function sameRuntimeContract(a, b) {
14643
14677
  if (!a || !b)
@@ -29,11 +29,11 @@ function defineNumber(value, fallback) {
29
29
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
30
30
  }
31
31
  var BUILD_INFO = Object.freeze({
32
- version: defineString("0.1.20", "0.0.0-source"),
33
- commit: defineString("d6034c6", "source"),
32
+ version: defineString("0.1.21", "0.0.0-source"),
33
+ commit: defineString("ddee9b2", "source"),
34
34
  bundle: defineBundle("plugin"),
35
35
  contractVersion: defineNumber(1, CONTRACT_VERSION),
36
- codeHash: defineString("d2425ba159fe", "source")
36
+ codeHash: defineString("8354efa9fcad", "source")
37
37
  });
38
38
  function daemonStatusBuildInfo() {
39
39
  return { ...BUILD_INFO };
@@ -3861,6 +3861,36 @@ function retryAfterMsForResume(resumeAfterEpoch, nowMs) {
3861
3861
  return remainingMs > 0 ? remainingMs : undefined;
3862
3862
  }
3863
3863
 
3864
+ // src/budget/format-time.ts
3865
+ var BEIJING_TZ = "Asia/Shanghai";
3866
+ function parts(epochSeconds, options) {
3867
+ const fmt = new Intl.DateTimeFormat("en-CA", {
3868
+ timeZone: BEIJING_TZ,
3869
+ hour12: false,
3870
+ ...options
3871
+ });
3872
+ const out = {};
3873
+ for (const part of fmt.formatToParts(new Date(epochSeconds * 1000))) {
3874
+ out[part.type] = part.value;
3875
+ }
3876
+ return out;
3877
+ }
3878
+ function formatBeijing(epochSeconds) {
3879
+ if (!epochSeconds || epochSeconds <= 0)
3880
+ return "\u672A\u77E5";
3881
+ const d = new Date(epochSeconds * 1000);
3882
+ if (Number.isNaN(d.getTime()))
3883
+ return "\u672A\u77E5";
3884
+ const p = parts(epochSeconds, {
3885
+ year: "numeric",
3886
+ month: "2-digit",
3887
+ day: "2-digit",
3888
+ hour: "2-digit",
3889
+ minute: "2-digit"
3890
+ });
3891
+ return `${p.year}-${p.month}-${p.day} ${p.hour}:${p.minute}`;
3892
+ }
3893
+
3864
3894
  // src/budget/types.ts
3865
3895
  var STALE_MAX_AGE_SEC = 600;
3866
3896
 
@@ -4149,14 +4179,12 @@ function pct2(value) {
4149
4179
  return `${Math.round(value * 10) / 10}%`;
4150
4180
  }
4151
4181
  function formatEpoch(epoch) {
4152
- if (!epoch || epoch <= 0)
4153
- return "\u672A\u77E5";
4154
- return new Date(epoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
4182
+ return formatBeijing(epoch);
4155
4183
  }
4156
4184
  function usageSummary(name, usage) {
4157
4185
  if (!usage)
4158
4186
  return `${AGENT_LABEL2[name]} \u672A\u77E5`;
4159
- return `${AGENT_LABEL2[name]} gate=${pct2(usage.gateUtil)} warn=${pct2(usage.warnUtil)} 5h\u91CD\u7F6E=${formatEpoch(usage.fiveHour?.resetEpoch ?? 0)}`;
4187
+ return `${AGENT_LABEL2[name]} gate=${pct2(usage.gateUtil)} warn=${pct2(usage.warnUtil)} 5h\u91CD\u7F6E=${formatEpoch(usage.fiveHour?.resetEpoch ?? 0)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
4160
4188
  }
4161
4189
  function resumeAfterEpoch(claude, codex, cfg, now) {
4162
4190
  const epochs = [
@@ -4452,7 +4480,7 @@ function pct3(value) {
4452
4480
  return `${Math.round(value * 10) / 10}%`;
4453
4481
  }
4454
4482
  function formatEpoch2(epoch) {
4455
- return new Date(epoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
4483
+ return formatBeijing(epoch);
4456
4484
  }
4457
4485
  var INITIAL_FINGERPRINT_STATE = {
4458
4486
  side: null,
@@ -4500,7 +4528,7 @@ function activeSideReason(agent, usage, cfg, now) {
4500
4528
  if (!usage)
4501
4529
  return `${AGENT_LABEL3[agent]} \u63A2\u6D4B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u4FDD\u6301\u4E0A\u4E00\u8F6E\u9884\u7B97\u5E72\u9884`;
4502
4530
  if (usage.rateLimitedUntil > now) {
4503
- return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}`;
4531
+ return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
4504
4532
  }
4505
4533
  const decision = agentShouldPause(agent, usage, cfg, now);
4506
4534
  if (decision.pause)
@@ -4643,7 +4671,7 @@ function admissionReason(side, state, cfg) {
4643
4671
  if (!usage)
4644
4672
  return `${AGENT_LABEL3[agent]} \u63A2\u6D4B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u4FDD\u6301\u4E0A\u4E00\u8F6E\u6536\u5C3E\u4FDD\u62A4`;
4645
4673
  if (usage.rateLimitedUntil > state.now) {
4646
- return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}\uFF0C\u4FDD\u6301\u6536\u5C3E\u4FDD\u62A4`;
4674
+ return `${AGENT_LABEL3[agent]} \u63A2\u9488\u88AB\u9650\u6D41\u81F3 ${formatEpoch2(usage.rateLimitedUntil)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09\uFF0C\u4FDD\u6301\u6536\u5C3E\u4FDD\u62A4`;
4647
4675
  }
4648
4676
  const decision = agentShouldAdmitClose(agent, usage, cfg, state.now);
4649
4677
  if (decision.admitClose)
@@ -6778,13 +6806,13 @@ function stopBudgetCoordinator() {
6778
6806
  function budgetPauseGateError() {
6779
6807
  const snapshot = budgetCoordinator?.getSnapshot() ?? null;
6780
6808
  const reason = snapshot?.pauseReason ?? "Codex \u4FA7\u989D\u5EA6\u63A5\u8FD1\u8017\u5C3D";
6781
- const resumeAt = snapshot?.resumeAfterEpoch ? new Date(snapshot.resumeAfterEpoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z") : null;
6809
+ const resumeAt = snapshot?.resumeAfterEpoch ? `${formatBeijing(snapshot.resumeAfterEpoch)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09` : null;
6782
6810
  const sideHint = snapshot?.pauseSide === "both" ? "\u53CC\u4FA7\u989D\u5EA6\u5747\u5DF2\u8017\u5C3D\uFF0C\u8BF7\u5199 checkpoint \u7B49\u5F85\u5237\u65B0" : "\u4F60\u53EF\u7EE7\u7EED solo \u63A8\u8FDB\u53EF\u72EC\u7ACB\u90E8\u5206\uFF0C\u5E76\u5199 checkpoint \u6807\u6CE8\u5206\u5DE5\u65AD\u70B9";
6783
6811
  const reopenText = `Codex \u4FA7\u5404\u7A97\u53E3 util \u56DE\u843D\u81F3\u52A8\u6001\u6682\u505C\u7EBF \u2212 ${BUDGET_CONFIG.maximize.resumeHysteresisPct}% \u4EE5\u4E0B\u6216\u5BF9\u5E94\u7A97\u53E3\u5237\u65B0\u540E\u95F8\u95E8\u81EA\u52A8\u653E\u5F00`;
6784
6812
  return `\u9884\u7B97\u6682\u505C\uFF08\u95F8\u95E8\u5173\u95ED\uFF09\uFF0C\u5DF2\u62D2\u7EDD\u8F6C\u53D1\uFF1A${reason}\u3002` + reopenText + (resumeAt ? `\uFF08\u9884\u8BA1\u6062\u590D ${resumeAt}\uFF0C\u4EE5\u5B9E\u6D4B\u4E3A\u51C6\uFF1B\u63D0\u524D\u5237\u65B0\u4F1A\u66F4\u65E9\u89E3\u9664\uFF09` : "") + `\u3002\u6536\u5230 RESUME \u901A\u77E5\u524D\u8BF7\u52FF\u91CD\u8BD5\u5411 Codex \u53D1\u9001 reply\uFF1B${sideHint}\u3002`;
6785
6813
  }
6786
6814
  function budgetAdmissionGateError(windowResetEpoch, wrapUpLeft, quotaExhausted) {
6787
- const resetAt = windowResetEpoch > 0 ? new Date(windowResetEpoch * 1000).toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z") : "\u672A\u77E5";
6815
+ const resetAt = windowResetEpoch > 0 ? `${formatBeijing(windowResetEpoch)}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09` : "\u672A\u77E5";
6788
6816
  const quota = BUDGET_CONFIG.maximize.wrapUpQuota;
6789
6817
  if (quotaExhausted) {
6790
6818
  return `\u989D\u5EA6\u7A97\u53E3\u6536\u5C3E\u4FDD\u62A4\u4E2D\uFF08admission-closed\uFF09\uFF1A\u672C\u7A97\u53E3 wrap-up \u914D\u989D\uFF08\u6BCF\u7A97\u53E3 ${quota} \u4E2A\uFF09\u5DF2\u7528\u5C3D\uFF0C\u5DF2\u62D2\u7EDD\u8F6C\u53D1\u3002` + `\u8BF7\u52FF\u518D\u6D3E\u65B0\u4EFB\u52A1\uFF1B\u5199 checkpoint\uFF0C\u7B49\u989D\u5EA6\u7A97\u53E3\u5237\u65B0\uFF08\u7EA6 ${resetAt}\uFF09\u540E\u518D\u7EE7\u7EED\u3002`;