@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raysonmeng/agentbridge",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
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.17",
3
+ "version": "0.1.19",
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",
@@ -13858,6 +13858,9 @@ class StateDirResolver {
13858
13858
  get killedFile() {
13859
13859
  return join(this.stateDir, "killed");
13860
13860
  }
13861
+ get admissionQuotaFile() {
13862
+ return join(this.stateDir, "admission-quota.json");
13863
+ }
13861
13864
  get updateCheckFile() {
13862
13865
  return join(this.stateDir, "update-check.json");
13863
13866
  }
@@ -14007,6 +14010,29 @@ function formatFiveHourWindowsLeftLine(snapshot) {
14007
14010
  const byAgent = values.map(([name, value]) => `${name} ~${value.toFixed(1)}`).join(" / ");
14008
14011
  return `\u6309\u5F53\u524D\u8282\u594F\uFF0C\u5468\u989D\u5EA6\u8FD8\u591F ${byAgent} \u4E2A 5h \u7A97\u53E3`;
14009
14012
  }
14013
+ var FIVE_HOUR_WINDOW_SEC = 5 * 3600;
14014
+ function clockWindowsLeft(usage, snapshotAt) {
14015
+ const weekly = usage?.weekly;
14016
+ if (!weekly || weekly.resetEpoch <= snapshotAt)
14017
+ return null;
14018
+ return (weekly.resetEpoch - snapshotAt) / FIVE_HOUR_WINDOW_SEC;
14019
+ }
14020
+ function formatClockWindowsLine(snapshot) {
14021
+ const values = [];
14022
+ const claude = clockWindowsLeft(snapshot.claude, snapshot.updatedAt);
14023
+ const codex = clockWindowsLeft(snapshot.codex, snapshot.updatedAt);
14024
+ if (claude !== null)
14025
+ values.push(["Claude", claude]);
14026
+ if (codex !== null)
14027
+ values.push(["Codex", codex]);
14028
+ if (values.length === 0)
14029
+ return null;
14030
+ const unique = [...new Set(values.map(([, value]) => value.toFixed(1)))];
14031
+ if (unique.length === 1)
14032
+ return `\u8DDD\u5468\u5237\u65B0\u8FD8\u80FD\u5BB9\u7EB3 ~${unique[0]} \u4E2A 5h \u7A97\u53E3\uFF08\u65F6\u949F\uFF09`;
14033
+ const byAgent = values.map(([name, value]) => `${name} ~${value.toFixed(1)}`).join(" / ");
14034
+ return `\u8DDD\u5468\u5237\u65B0\u8FD8\u80FD\u5BB9\u7EB3 ${byAgent} \u4E2A 5h \u7A97\u53E3\uFF08\u65F6\u949F\uFF09`;
14035
+ }
14010
14036
  function formatDynamicLineLine(snapshot) {
14011
14037
  const lines = snapshot.dynamicPauseLine;
14012
14038
  if (!lines)
@@ -14050,6 +14076,9 @@ function renderBudgetSnapshot(snapshot, options = {}) {
14050
14076
  const fiveHourWindowsLeftLine = formatFiveHourWindowsLeftLine(snapshot);
14051
14077
  if (fiveHourWindowsLeftLine)
14052
14078
  lines.push(fiveHourWindowsLeftLine);
14079
+ const clockWindowsLine = formatClockWindowsLine(snapshot);
14080
+ if (clockWindowsLine)
14081
+ lines.push(clockWindowsLine);
14053
14082
  const dynamicLineLine = formatDynamicLineLine(snapshot);
14054
14083
  if (dynamicLineLine)
14055
14084
  lines.push(dynamicLineLine);
@@ -14130,7 +14159,8 @@ var CLAUDE_INSTRUCTIONS = [
14130
14159
  "",
14131
14160
  "## Budget awareness",
14132
14161
  "- Use the get_budget tool to check both agents' subscription quota (5h/weekly windows, drift, pause state).",
14133
- "- If the reply tool returns a budget-pause error, do NOT retry; checkpoint your work and wait for the resume notice."
14162
+ "- If the reply tool returns a budget-pause error (code budget_paused), do NOT retry; checkpoint your work and wait for the resume notice.",
14163
+ "- If the reply tool returns a budget_admission error, the 5h window is in finishing-protection: new tasks are declined, but you may bring the CURRENT collaboration to a checkpoint by resending with wrap_up=true (a small per-window quota). Do NOT start new work; once the quota is used or you are done, write a checkpoint and wait for the 5h window to refresh."
14134
14164
  ].join(`
14135
14165
  `);
14136
14166
 
@@ -14374,6 +14404,10 @@ chat_id: ${this.sessionId}`);
14374
14404
  idempotency_key: {
14375
14405
  type: "string",
14376
14406
  description: "Optional client-generated key (non-empty, max 128 chars) that makes this reply idempotent: a retry carrying the same key is NOT re-injected \u2014 the bridge answers duplicate_in_flight / duplicate_terminal instead. Use a fresh key per logical message."
14407
+ },
14408
+ wrap_up: {
14409
+ type: "boolean",
14410
+ description: "Set true ONLY to declare a finishing turn when the budget gate is in 5h finishing-protection (you got a budget_admission error or a system_budget_admission notice). A wrap-up reply is let through the admission gate up to a small per-5h-window quota so you can bring the current collaboration to a checkpoint; do NOT use it to start new work. Leave false/unset for normal replies."
14377
14411
  }
14378
14412
  },
14379
14413
  required: ["text"]
@@ -14526,7 +14560,8 @@ chat_id: ${this.sessionId}`);
14526
14560
  isError: true
14527
14561
  };
14528
14562
  }
14529
- const result = await this.replySender(bridgeMsg, requireReply, onBusy, idempotencyKey);
14563
+ const wrapUp = args?.wrap_up === true;
14564
+ const result = await this.replySender(bridgeMsg, requireReply, onBusy, idempotencyKey, wrapUp);
14530
14565
  if (!result.success) {
14531
14566
  this.log(`Reply delivery failed: ${result.error}${result.code ? ` (code=${result.code})` : ""}`);
14532
14567
  const codePrefix = result.code ? ` [${result.code}]` : "";
@@ -14596,11 +14631,11 @@ function defineNumber(value, fallback) {
14596
14631
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14597
14632
  }
14598
14633
  var BUILD_INFO = Object.freeze({
14599
- version: defineString("0.1.17", "0.0.0-source"),
14600
- commit: defineString("0d1e1bd", "source"),
14634
+ version: defineString("0.1.19", "0.0.0-source"),
14635
+ commit: defineString("dee06ce", "source"),
14601
14636
  bundle: defineBundle("plugin"),
14602
14637
  contractVersion: defineNumber(1, CONTRACT_VERSION),
14603
- codeHash: defineString("c22387f3269f", "source")
14638
+ codeHash: defineString("9ddee00c61f9", "source")
14604
14639
  });
14605
14640
  function sameRuntimeContract(a, b) {
14606
14641
  if (!a || !b)
@@ -14847,7 +14882,7 @@ class DaemonClient extends EventEmitter2 {
14847
14882
  this.ws = null;
14848
14883
  this.rejectPendingReplies("Daemon connection closed");
14849
14884
  }
14850
- async sendReply(message, requireReply, onBusy, idempotencyKey) {
14885
+ async sendReply(message, requireReply, onBusy, idempotencyKey, wrapUp) {
14851
14886
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
14852
14887
  return { success: false, error: "AgentBridge daemon is not connected." };
14853
14888
  }
@@ -14862,7 +14897,8 @@ class DaemonClient extends EventEmitter2 {
14862
14897
  message,
14863
14898
  ...requireReply ? { requireReply: true } : {},
14864
14899
  ...onBusy && onBusy !== "reject" ? { onBusy } : {},
14865
- ...idempotencyKey ? { idempotencyKey } : {}
14900
+ ...idempotencyKey ? { idempotencyKey } : {},
14901
+ ...wrapUp ? { wrapUp: true } : {}
14866
14902
  });
14867
14903
  return pending;
14868
14904
  }
@@ -15640,7 +15676,9 @@ var DEFAULT_BUDGET_CONFIG = {
15640
15676
  reserveSlopePctPerHour: 0.4,
15641
15677
  reserveMaxPct: 7,
15642
15678
  finishingHorizonMinutes: 30,
15643
- resumeHysteresisPct: 5
15679
+ resumeHysteresisPct: 5,
15680
+ admissionAt: 85,
15681
+ wrapUpQuota: 2
15644
15682
  },
15645
15683
  allocation: {
15646
15684
  minRunwayRatio: 50,
@@ -15708,7 +15746,9 @@ function findShapeViolation(raw) {
15708
15746
  "reserveSlopePctPerHour",
15709
15747
  "reserveMaxPct",
15710
15748
  "finishingHorizonMinutes",
15711
- "resumeHysteresisPct"
15749
+ "resumeHysteresisPct",
15750
+ "admissionAt",
15751
+ "wrapUpQuota"
15712
15752
  ]) {
15713
15753
  if (key in maximize && !isCoercibleNumber(maximize[key])) {
15714
15754
  return `budget.maximize.${key} is present but not a number`;
@@ -15733,7 +15773,7 @@ function hasCustomDecisionValues(config2) {
15733
15773
  const d = DEFAULT_CONFIG;
15734
15774
  const b = config2.budget;
15735
15775
  const db = d.budget;
15736
- return config2.idleShutdownSeconds !== d.idleShutdownSeconds || config2.turnCoordination.attentionWindowSeconds !== d.turnCoordination.attentionWindowSeconds || config2.codex.appPort !== d.codex.appPort || config2.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;
15776
+ return config2.idleShutdownSeconds !== d.idleShutdownSeconds || config2.turnCoordination.attentionWindowSeconds !== d.turnCoordination.attentionWindowSeconds || config2.codex.appPort !== d.codex.appPort || config2.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;
15737
15777
  }
15738
15778
  function normalizeInteger(value, fallback) {
15739
15779
  if (typeof value === "number" && Number.isFinite(value))
@@ -15773,9 +15813,11 @@ function normalizeMaximizeConfig(raw, pauseAt, fallback = DEFAULT_BUDGET_CONFIG.
15773
15813
  reserveSlopePctPerHour: normalizeBoundedNumber(m.reserveSlopePctPerHour, fallback.reserveSlopePctPerHour, 0, 5),
15774
15814
  reserveMaxPct: normalizeBoundedInteger(m.reserveMaxPct, fallback.reserveMaxPct, 0, 30),
15775
15815
  finishingHorizonMinutes: normalizeBoundedInteger(m.finishingHorizonMinutes, fallback.finishingHorizonMinutes, 5, 180),
15776
- resumeHysteresisPct: normalizeBoundedInteger(m.resumeHysteresisPct, fallback.resumeHysteresisPct, 1, 30)
15816
+ resumeHysteresisPct: normalizeBoundedInteger(m.resumeHysteresisPct, fallback.resumeHysteresisPct, 1, 30),
15817
+ admissionAt: normalizeBoundedInteger(m.admissionAt, fallback.admissionAt, 50, 99),
15818
+ wrapUpQuota: Math.floor(normalizeBoundedInteger(m.wrapUpQuota, fallback.wrapUpQuota, 0, 10))
15777
15819
  };
15778
- if (normalized.targetUtil <= pauseAt) {
15820
+ if (normalized.targetUtil <= pauseAt || normalized.admissionAt >= normalized.targetUtil) {
15779
15821
  return { ...DEFAULT_BUDGET_CONFIG.maximize };
15780
15822
  }
15781
15823
  return normalized;
@@ -16357,7 +16399,7 @@ if (process.env.AGENTBRIDGE_TRACE === "1") {
16357
16399
  });
16358
16400
  } catch {}
16359
16401
  }
16360
- claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey) => {
16402
+ claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey, wrapUp) => {
16361
16403
  if (msg.source !== "claude") {
16362
16404
  return { success: false, error: "Invalid message source" };
16363
16405
  }
@@ -16367,7 +16409,7 @@ claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey) => {
16367
16409
  error: disabledReplyError(daemonDisabledReason ?? "killed")
16368
16410
  };
16369
16411
  }
16370
- return daemonClient.sendReply(msg, requireReply, onBusy, idempotencyKey);
16412
+ return daemonClient.sendReply(msg, requireReply, onBusy, idempotencyKey, wrapUp);
16371
16413
  });
16372
16414
  claude.setResumeAckHandler((resumeId, status) => {
16373
16415
  if (daemonDisabled) {