@raysonmeng/agentbridge 0.1.18 → 0.1.20

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.18",
3
+ "version": "0.1.20",
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.18",
3
+ "version": "0.1.20",
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
  }
@@ -14099,6 +14102,8 @@ function renderBudgetSnapshot(snapshot, options = {}) {
14099
14102
  } else {
14100
14103
  lines.push(`\u6682\u505C\uFF1A\u53CC\u4FA7\u8054\u5408\u6682\u505C\uFF08\u95F8\u95E8\u5173\u95ED\uFF09 \u2014 ${reason}${resume}`);
14101
14104
  }
14105
+ } else if (snapshot.gateState === "admission-closed") {
14106
+ lines.push("\u6682\u505C\uFF1A\u5426 \xB7 \u6536\u5C3E\u4FDD\u62A4\uFF1Aadmission-closed\uFF08Codex \u4FA7\u4EC5\u653E\u884C wrap-up/steer\uFF0C\u65B0\u4EFB\u52A1\u4F1A\u88AB\u62D2 budget_admission\uFF09");
14102
14107
  } else {
14103
14108
  lines.push("\u6682\u505C\uFF1A\u5426");
14104
14109
  }
@@ -14156,7 +14161,8 @@ var CLAUDE_INSTRUCTIONS = [
14156
14161
  "",
14157
14162
  "## Budget awareness",
14158
14163
  "- Use the get_budget tool to check both agents' subscription quota (5h/weekly windows, drift, pause state).",
14159
- "- If the reply tool returns a budget-pause error, do NOT retry; checkpoint your work and wait for the resume notice."
14164
+ "- If the reply tool returns a budget-pause error (code budget_paused), do NOT retry; checkpoint your work and wait for the resume notice.",
14165
+ "- 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."
14160
14166
  ].join(`
14161
14167
  `);
14162
14168
 
@@ -14400,6 +14406,10 @@ chat_id: ${this.sessionId}`);
14400
14406
  idempotency_key: {
14401
14407
  type: "string",
14402
14408
  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."
14409
+ },
14410
+ wrap_up: {
14411
+ type: "boolean",
14412
+ 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."
14403
14413
  }
14404
14414
  },
14405
14415
  required: ["text"]
@@ -14552,7 +14562,8 @@ chat_id: ${this.sessionId}`);
14552
14562
  isError: true
14553
14563
  };
14554
14564
  }
14555
- const result = await this.replySender(bridgeMsg, requireReply, onBusy, idempotencyKey);
14565
+ const wrapUp = args?.wrap_up === true;
14566
+ const result = await this.replySender(bridgeMsg, requireReply, onBusy, idempotencyKey, wrapUp);
14556
14567
  if (!result.success) {
14557
14568
  this.log(`Reply delivery failed: ${result.error}${result.code ? ` (code=${result.code})` : ""}`);
14558
14569
  const codePrefix = result.code ? ` [${result.code}]` : "";
@@ -14622,11 +14633,11 @@ function defineNumber(value, fallback) {
14622
14633
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14623
14634
  }
14624
14635
  var BUILD_INFO = Object.freeze({
14625
- version: defineString("0.1.18", "0.0.0-source"),
14626
- commit: defineString("9db0aa3", "source"),
14636
+ version: defineString("0.1.20", "0.0.0-source"),
14637
+ commit: defineString("d6034c6", "source"),
14627
14638
  bundle: defineBundle("plugin"),
14628
14639
  contractVersion: defineNumber(1, CONTRACT_VERSION),
14629
- codeHash: defineString("46a6407023f0", "source")
14640
+ codeHash: defineString("d2425ba159fe", "source")
14630
14641
  });
14631
14642
  function sameRuntimeContract(a, b) {
14632
14643
  if (!a || !b)
@@ -14873,7 +14884,7 @@ class DaemonClient extends EventEmitter2 {
14873
14884
  this.ws = null;
14874
14885
  this.rejectPendingReplies("Daemon connection closed");
14875
14886
  }
14876
- async sendReply(message, requireReply, onBusy, idempotencyKey) {
14887
+ async sendReply(message, requireReply, onBusy, idempotencyKey, wrapUp) {
14877
14888
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
14878
14889
  return { success: false, error: "AgentBridge daemon is not connected." };
14879
14890
  }
@@ -14888,7 +14899,8 @@ class DaemonClient extends EventEmitter2 {
14888
14899
  message,
14889
14900
  ...requireReply ? { requireReply: true } : {},
14890
14901
  ...onBusy && onBusy !== "reject" ? { onBusy } : {},
14891
- ...idempotencyKey ? { idempotencyKey } : {}
14902
+ ...idempotencyKey ? { idempotencyKey } : {},
14903
+ ...wrapUp ? { wrapUp: true } : {}
14892
14904
  });
14893
14905
  return pending;
14894
14906
  }
@@ -15666,7 +15678,9 @@ var DEFAULT_BUDGET_CONFIG = {
15666
15678
  reserveSlopePctPerHour: 0.4,
15667
15679
  reserveMaxPct: 7,
15668
15680
  finishingHorizonMinutes: 30,
15669
- resumeHysteresisPct: 5
15681
+ resumeHysteresisPct: 5,
15682
+ admissionAt: 85,
15683
+ wrapUpQuota: 2
15670
15684
  },
15671
15685
  allocation: {
15672
15686
  minRunwayRatio: 50,
@@ -15734,7 +15748,9 @@ function findShapeViolation(raw) {
15734
15748
  "reserveSlopePctPerHour",
15735
15749
  "reserveMaxPct",
15736
15750
  "finishingHorizonMinutes",
15737
- "resumeHysteresisPct"
15751
+ "resumeHysteresisPct",
15752
+ "admissionAt",
15753
+ "wrapUpQuota"
15738
15754
  ]) {
15739
15755
  if (key in maximize && !isCoercibleNumber(maximize[key])) {
15740
15756
  return `budget.maximize.${key} is present but not a number`;
@@ -15759,7 +15775,7 @@ function hasCustomDecisionValues(config2) {
15759
15775
  const d = DEFAULT_CONFIG;
15760
15776
  const b = config2.budget;
15761
15777
  const db = d.budget;
15762
- 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;
15778
+ 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;
15763
15779
  }
15764
15780
  function normalizeInteger(value, fallback) {
15765
15781
  if (typeof value === "number" && Number.isFinite(value))
@@ -15799,9 +15815,11 @@ function normalizeMaximizeConfig(raw, pauseAt, fallback = DEFAULT_BUDGET_CONFIG.
15799
15815
  reserveSlopePctPerHour: normalizeBoundedNumber(m.reserveSlopePctPerHour, fallback.reserveSlopePctPerHour, 0, 5),
15800
15816
  reserveMaxPct: normalizeBoundedInteger(m.reserveMaxPct, fallback.reserveMaxPct, 0, 30),
15801
15817
  finishingHorizonMinutes: normalizeBoundedInteger(m.finishingHorizonMinutes, fallback.finishingHorizonMinutes, 5, 180),
15802
- resumeHysteresisPct: normalizeBoundedInteger(m.resumeHysteresisPct, fallback.resumeHysteresisPct, 1, 30)
15818
+ resumeHysteresisPct: normalizeBoundedInteger(m.resumeHysteresisPct, fallback.resumeHysteresisPct, 1, 30),
15819
+ admissionAt: normalizeBoundedInteger(m.admissionAt, fallback.admissionAt, 50, 99),
15820
+ wrapUpQuota: Math.floor(normalizeBoundedInteger(m.wrapUpQuota, fallback.wrapUpQuota, 0, 10))
15803
15821
  };
15804
- if (normalized.targetUtil <= pauseAt) {
15822
+ if (normalized.targetUtil <= pauseAt || normalized.admissionAt >= normalized.targetUtil) {
15805
15823
  return { ...DEFAULT_BUDGET_CONFIG.maximize };
15806
15824
  }
15807
15825
  return normalized;
@@ -16383,7 +16401,7 @@ if (process.env.AGENTBRIDGE_TRACE === "1") {
16383
16401
  });
16384
16402
  } catch {}
16385
16403
  }
16386
- claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey) => {
16404
+ claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey, wrapUp) => {
16387
16405
  if (msg.source !== "claude") {
16388
16406
  return { success: false, error: "Invalid message source" };
16389
16407
  }
@@ -16393,7 +16411,7 @@ claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey) => {
16393
16411
  error: disabledReplyError(daemonDisabledReason ?? "killed")
16394
16412
  };
16395
16413
  }
16396
- return daemonClient.sendReply(msg, requireReply, onBusy, idempotencyKey);
16414
+ return daemonClient.sendReply(msg, requireReply, onBusy, idempotencyKey, wrapUp);
16397
16415
  });
16398
16416
  claude.setResumeAckHandler((resumeId, status) => {
16399
16417
  if (daemonDisabled) {