@raysonmeng/agentbridge 0.1.18 → 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.18",
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.18",
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
  }
@@ -14156,7 +14159,8 @@ var CLAUDE_INSTRUCTIONS = [
14156
14159
  "",
14157
14160
  "## Budget awareness",
14158
14161
  "- 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."
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."
14160
14164
  ].join(`
14161
14165
  `);
14162
14166
 
@@ -14400,6 +14404,10 @@ chat_id: ${this.sessionId}`);
14400
14404
  idempotency_key: {
14401
14405
  type: "string",
14402
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."
14403
14411
  }
14404
14412
  },
14405
14413
  required: ["text"]
@@ -14552,7 +14560,8 @@ chat_id: ${this.sessionId}`);
14552
14560
  isError: true
14553
14561
  };
14554
14562
  }
14555
- 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);
14556
14565
  if (!result.success) {
14557
14566
  this.log(`Reply delivery failed: ${result.error}${result.code ? ` (code=${result.code})` : ""}`);
14558
14567
  const codePrefix = result.code ? ` [${result.code}]` : "";
@@ -14622,11 +14631,11 @@ function defineNumber(value, fallback) {
14622
14631
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14623
14632
  }
14624
14633
  var BUILD_INFO = Object.freeze({
14625
- version: defineString("0.1.18", "0.0.0-source"),
14626
- commit: defineString("9db0aa3", "source"),
14634
+ version: defineString("0.1.19", "0.0.0-source"),
14635
+ commit: defineString("dee06ce", "source"),
14627
14636
  bundle: defineBundle("plugin"),
14628
14637
  contractVersion: defineNumber(1, CONTRACT_VERSION),
14629
- codeHash: defineString("46a6407023f0", "source")
14638
+ codeHash: defineString("9ddee00c61f9", "source")
14630
14639
  });
14631
14640
  function sameRuntimeContract(a, b) {
14632
14641
  if (!a || !b)
@@ -14873,7 +14882,7 @@ class DaemonClient extends EventEmitter2 {
14873
14882
  this.ws = null;
14874
14883
  this.rejectPendingReplies("Daemon connection closed");
14875
14884
  }
14876
- async sendReply(message, requireReply, onBusy, idempotencyKey) {
14885
+ async sendReply(message, requireReply, onBusy, idempotencyKey, wrapUp) {
14877
14886
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
14878
14887
  return { success: false, error: "AgentBridge daemon is not connected." };
14879
14888
  }
@@ -14888,7 +14897,8 @@ class DaemonClient extends EventEmitter2 {
14888
14897
  message,
14889
14898
  ...requireReply ? { requireReply: true } : {},
14890
14899
  ...onBusy && onBusy !== "reject" ? { onBusy } : {},
14891
- ...idempotencyKey ? { idempotencyKey } : {}
14900
+ ...idempotencyKey ? { idempotencyKey } : {},
14901
+ ...wrapUp ? { wrapUp: true } : {}
14892
14902
  });
14893
14903
  return pending;
14894
14904
  }
@@ -15666,7 +15676,9 @@ var DEFAULT_BUDGET_CONFIG = {
15666
15676
  reserveSlopePctPerHour: 0.4,
15667
15677
  reserveMaxPct: 7,
15668
15678
  finishingHorizonMinutes: 30,
15669
- resumeHysteresisPct: 5
15679
+ resumeHysteresisPct: 5,
15680
+ admissionAt: 85,
15681
+ wrapUpQuota: 2
15670
15682
  },
15671
15683
  allocation: {
15672
15684
  minRunwayRatio: 50,
@@ -15734,7 +15746,9 @@ function findShapeViolation(raw) {
15734
15746
  "reserveSlopePctPerHour",
15735
15747
  "reserveMaxPct",
15736
15748
  "finishingHorizonMinutes",
15737
- "resumeHysteresisPct"
15749
+ "resumeHysteresisPct",
15750
+ "admissionAt",
15751
+ "wrapUpQuota"
15738
15752
  ]) {
15739
15753
  if (key in maximize && !isCoercibleNumber(maximize[key])) {
15740
15754
  return `budget.maximize.${key} is present but not a number`;
@@ -15759,7 +15773,7 @@ function hasCustomDecisionValues(config2) {
15759
15773
  const d = DEFAULT_CONFIG;
15760
15774
  const b = config2.budget;
15761
15775
  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;
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;
15763
15777
  }
15764
15778
  function normalizeInteger(value, fallback) {
15765
15779
  if (typeof value === "number" && Number.isFinite(value))
@@ -15799,9 +15813,11 @@ function normalizeMaximizeConfig(raw, pauseAt, fallback = DEFAULT_BUDGET_CONFIG.
15799
15813
  reserveSlopePctPerHour: normalizeBoundedNumber(m.reserveSlopePctPerHour, fallback.reserveSlopePctPerHour, 0, 5),
15800
15814
  reserveMaxPct: normalizeBoundedInteger(m.reserveMaxPct, fallback.reserveMaxPct, 0, 30),
15801
15815
  finishingHorizonMinutes: normalizeBoundedInteger(m.finishingHorizonMinutes, fallback.finishingHorizonMinutes, 5, 180),
15802
- 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))
15803
15819
  };
15804
- if (normalized.targetUtil <= pauseAt) {
15820
+ if (normalized.targetUtil <= pauseAt || normalized.admissionAt >= normalized.targetUtil) {
15805
15821
  return { ...DEFAULT_BUDGET_CONFIG.maximize };
15806
15822
  }
15807
15823
  return normalized;
@@ -16383,7 +16399,7 @@ if (process.env.AGENTBRIDGE_TRACE === "1") {
16383
16399
  });
16384
16400
  } catch {}
16385
16401
  }
16386
- claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey) => {
16402
+ claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey, wrapUp) => {
16387
16403
  if (msg.source !== "claude") {
16388
16404
  return { success: false, error: "Invalid message source" };
16389
16405
  }
@@ -16393,7 +16409,7 @@ claude.setReplySender(async (msg, requireReply, onBusy, idempotencyKey) => {
16393
16409
  error: disabledReplyError(daemonDisabledReason ?? "killed")
16394
16410
  };
16395
16411
  }
16396
- return daemonClient.sendReply(msg, requireReply, onBusy, idempotencyKey);
16412
+ return daemonClient.sendReply(msg, requireReply, onBusy, idempotencyKey, wrapUp);
16397
16413
  });
16398
16414
  claude.setResumeAckHandler((resumeId, status) => {
16399
16415
  if (daemonDisabled) {