open-multi-agent-kit 0.96.2 → 0.97.0

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.
Files changed (138) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +4 -2
  3. package/dist/core/agent-session.d.ts +9 -1
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +84 -7
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/attachment-store.d.ts +39 -0
  8. package/dist/core/attachment-store.d.ts.map +1 -0
  9. package/dist/core/attachment-store.js +81 -0
  10. package/dist/core/attachment-store.js.map +1 -0
  11. package/dist/core/command-safety.d.ts +8 -0
  12. package/dist/core/command-safety.d.ts.map +1 -1
  13. package/dist/core/command-safety.js +140 -14
  14. package/dist/core/command-safety.js.map +1 -1
  15. package/dist/core/compaction/compaction.d.ts +6 -1
  16. package/dist/core/compaction/compaction.d.ts.map +1 -1
  17. package/dist/core/compaction/compaction.js +35 -1
  18. package/dist/core/compaction/compaction.js.map +1 -1
  19. package/dist/core/context-budget-governor-v2.d.ts +1 -1
  20. package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
  21. package/dist/core/context-budget-governor-v2.js +1 -1
  22. package/dist/core/context-budget-governor-v2.js.map +1 -1
  23. package/dist/core/context-budget-v2-cache-keys.d.ts +3 -0
  24. package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -1
  25. package/dist/core/context-budget-v2-cache-keys.js +2 -0
  26. package/dist/core/context-budget-v2-cache-keys.js.map +1 -1
  27. package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -1
  28. package/dist/core/context-budget-v2-plan-cache-keys.js +3 -0
  29. package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -1
  30. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  31. package/dist/core/context-budget-v2-planner.js +2 -1
  32. package/dist/core/context-budget-v2-planner.js.map +1 -1
  33. package/dist/core/context-budget-v2-scoring.d.ts +17 -0
  34. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  35. package/dist/core/context-budget-v2-scoring.js +25 -8
  36. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  37. package/dist/core/context-budget-v2-selection.d.ts +2 -2
  38. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  39. package/dist/core/context-budget-v2-selection.js +18 -1
  40. package/dist/core/context-budget-v2-selection.js.map +1 -1
  41. package/dist/core/context-budget-v2-types.d.ts +17 -0
  42. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  43. package/dist/core/context-budget-v2-types.js +15 -0
  44. package/dist/core/context-budget-v2-types.js.map +1 -1
  45. package/dist/core/extensions/builtin/command-safety-gate.d.ts +3 -1
  46. package/dist/core/extensions/builtin/command-safety-gate.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/command-safety-gate.js +7 -0
  48. package/dist/core/extensions/builtin/command-safety-gate.js.map +1 -1
  49. package/dist/core/extensions/types.d.ts +12 -1
  50. package/dist/core/extensions/types.d.ts.map +1 -1
  51. package/dist/core/extensions/types.js.map +1 -1
  52. package/dist/core/model-registry.d.ts +8 -3
  53. package/dist/core/model-registry.d.ts.map +1 -1
  54. package/dist/core/model-registry.js +71 -1
  55. package/dist/core/model-registry.js.map +1 -1
  56. package/dist/core/package-doctor-source-scan.d.ts +9 -0
  57. package/dist/core/package-doctor-source-scan.d.ts.map +1 -1
  58. package/dist/core/package-doctor-source-scan.js +77 -4
  59. package/dist/core/package-doctor-source-scan.js.map +1 -1
  60. package/dist/core/prompt-attachment.d.ts +51 -0
  61. package/dist/core/prompt-attachment.d.ts.map +1 -0
  62. package/dist/core/prompt-attachment.js +186 -0
  63. package/dist/core/prompt-attachment.js.map +1 -0
  64. package/dist/core/provider-resilience.d.ts +28 -0
  65. package/dist/core/provider-resilience.d.ts.map +1 -1
  66. package/dist/core/provider-resilience.js +50 -0
  67. package/dist/core/provider-resilience.js.map +1 -1
  68. package/dist/core/provider-retry.d.ts +1 -1
  69. package/dist/core/provider-retry.d.ts.map +1 -1
  70. package/dist/core/provider-retry.js +1 -1
  71. package/dist/core/provider-retry.js.map +1 -1
  72. package/dist/core/provider-usage.d.ts.map +1 -1
  73. package/dist/core/provider-usage.js +68 -2
  74. package/dist/core/provider-usage.js.map +1 -1
  75. package/dist/core/reasoning-router-promotion.d.ts +84 -0
  76. package/dist/core/reasoning-router-promotion.d.ts.map +1 -0
  77. package/dist/core/reasoning-router-promotion.js +92 -0
  78. package/dist/core/reasoning-router-promotion.js.map +1 -0
  79. package/dist/core/session-bash-service.d.ts.map +1 -1
  80. package/dist/core/session-bash-service.js +4 -3
  81. package/dist/core/session-bash-service.js.map +1 -1
  82. package/dist/core/session-termination.d.ts +1 -1
  83. package/dist/core/session-termination.d.ts.map +1 -1
  84. package/dist/core/session-termination.js +11 -2
  85. package/dist/core/session-termination.js.map +1 -1
  86. package/dist/core/tools/bash.d.ts.map +1 -1
  87. package/dist/core/tools/bash.js +3 -7
  88. package/dist/core/tools/bash.js.map +1 -1
  89. package/dist/core/tools/grep.d.ts.map +1 -1
  90. package/dist/core/tools/grep.js +13 -48
  91. package/dist/core/tools/grep.js.map +1 -1
  92. package/dist/core/tools/read.d.ts.map +1 -1
  93. package/dist/core/tools/read.js +1 -34
  94. package/dist/core/tools/read.js.map +1 -1
  95. package/dist/index.d.ts +1 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/modes/interactive/components/attachment-strip.d.ts +21 -0
  100. package/dist/modes/interactive/components/attachment-strip.d.ts.map +1 -0
  101. package/dist/modes/interactive/components/attachment-strip.js +136 -0
  102. package/dist/modes/interactive/components/attachment-strip.js.map +1 -0
  103. package/dist/modes/interactive/components/footer.d.ts +6 -0
  104. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  105. package/dist/modes/interactive/components/footer.js +26 -1
  106. package/dist/modes/interactive/components/footer.js.map +1 -1
  107. package/dist/modes/interactive/components/status-sidebar.d.ts.map +1 -1
  108. package/dist/modes/interactive/components/status-sidebar.js +12 -1
  109. package/dist/modes/interactive/components/status-sidebar.js.map +1 -1
  110. package/dist/modes/interactive/interactive-mode.d.ts +23 -4
  111. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  112. package/dist/modes/interactive/interactive-mode.js +174 -53
  113. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  114. package/docs/compaction.md +1 -1
  115. package/docs/extensions.md +12 -3
  116. package/docs/index.md +4 -1
  117. package/docs/metrics.md +74 -2
  118. package/docs/provider-resilience.md +11 -0
  119. package/docs/usage.md +1 -1
  120. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  121. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  122. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  123. package/examples/extensions/gondolin/package-lock.json +2 -2
  124. package/examples/extensions/gondolin/package.json +1 -1
  125. package/examples/extensions/sandbox/package-lock.json +2 -2
  126. package/examples/extensions/sandbox/package.json +1 -1
  127. package/examples/extensions/with-deps/package-lock.json +2 -2
  128. package/examples/extensions/with-deps/package.json +1 -1
  129. package/npm-shrinkwrap.json +18 -18
  130. package/package.json +7 -7
  131. package/dist/core/tools/omp-pure-seams.d.ts +0 -41
  132. package/dist/core/tools/omp-pure-seams.d.ts.map +0 -1
  133. package/dist/core/tools/omp-pure-seams.js +0 -142
  134. package/dist/core/tools/omp-pure-seams.js.map +0 -1
  135. package/dist/core/tools/omp-seam-runtime.d.ts +0 -100
  136. package/dist/core/tools/omp-seam-runtime.d.ts.map +0 -1
  137. package/dist/core/tools/omp-seam-runtime.js +0 -40
  138. package/dist/core/tools/omp-seam-runtime.js.map +0 -1
@@ -79,7 +79,7 @@ import { computePromptTokenBudget } from "./prompt-budget.js";
79
79
  import { classifyPromptCacheTransition } from "./prompt-cache.js";
80
80
  import { createPromptSettlementState, reducePromptSettlement, settlePromptIfReady, } from "./prompt-settlement.js";
81
81
  import { expandPromptTemplate } from "./prompt-templates.js";
82
- import { isQuotaExhaustionMessage, isStickySafetyModel, pickFailoverCandidate, resolveProviderResilience, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
82
+ import { isQuotaExhaustionMessage, isStickySafetyModel, isUpstreamUnavailableMessage, pickFailoverCandidate, resolveFailoverCandidates, resolveProviderResilience, sameModelRouteCandidates, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
83
83
  import { computeRetryDelayMs, failoverModelKey, isFailoverTriggerError, isRetryableAssistantError, nextRetryAttempt, retryBudgetForAssistantError, } from "./provider-retry.js";
84
84
  import { getBiasStepsForCell, parseRouterBiasSnapshot } from "./reasoning-router-bias.js";
85
85
  import { classifyTaskV4, deriveRouterFeedbackFeaturesV4, resolveThinkingLevelV4WithUncertainty, TASK_CLASS_THINKING_LEVELS_V4, } from "./reasoning-router-v4.js";
@@ -624,6 +624,12 @@ export class AgentSession {
624
624
  if (/auth|unauthori[sz]ed|forbidden|invalid.?api.?key|no api key|401|403|\/login/i.test(text)) {
625
625
  return { area: "provider", code: "auth" };
626
626
  }
627
+ // Gateway/upstream 5xx and dropped streams are transport failures, not
628
+ // transcript-shape problems. Classify as network so the guidance points to
629
+ // retry/model-switch instead of the protocol sanitize path.
630
+ if (isUpstreamUnavailableMessage(text)) {
631
+ return { area: "provider", code: "network" };
632
+ }
627
633
  // Kimi/K3 + OpenAI-compat: orphan tool results after dropped error assistants.
628
634
  // Sanitize-and-retry (transform-messages drops orphans), not a hard tool fatal.
629
635
  if (/tool_call_id\s+is\s+not\s+found|tool_call_id\s+not\s+found|unknown\s+tool_call_id/i.test(text)) {
@@ -648,6 +654,9 @@ export class AgentSession {
648
654
  if (/auth|api key|unauthori[sz]ed|forbidden|401|403|\/login/i.test(message)) {
649
655
  return { area: "provider", code: "auth" };
650
656
  }
657
+ if (isUpstreamUnavailableMessage(message)) {
658
+ return { area: "provider", code: "network" };
659
+ }
651
660
  if (/context.+overflow|context window|too many tokens/i.test(message)) {
652
661
  return { area: "provider", code: "context_overflow" };
653
662
  }
@@ -1130,6 +1139,11 @@ export class AgentSession {
1130
1139
  }
1131
1140
  else if (event.type === "agent_end") {
1132
1141
  await this._extensionRunner.emit({ type: "agent_end", messages: event.messages });
1142
+ // `agent_end` fires once per attempt. Extensions that release per-run
1143
+ // state need the boundary no retry follows, so mark it explicitly.
1144
+ if (!this._willRetryAfterAgentEnd(event)) {
1145
+ await this._extensionRunner.emit({ type: "agent_settled", messages: event.messages });
1146
+ }
1133
1147
  }
1134
1148
  else if (event.type === "turn_start") {
1135
1149
  this._turnMetricsState = {
@@ -2986,6 +3000,25 @@ export class AgentSession {
2986
3000
  });
2987
3001
  return model;
2988
3002
  }
3003
+ /**
3004
+ * Authenticated failover candidates for compaction summarization, in
3005
+ * resilience-chain order, excluding the primary compaction model itself.
3006
+ * Used when the primary model's quota is exhausted (same-model retry is
3007
+ * useless until reset; another provider can still save the compaction).
3008
+ */
3009
+ _compactionFailoverModels(primary) {
3010
+ const candidates = resolveFailoverCandidates(this.settingsManager.getProviderResilienceSettings());
3011
+ const available = this._modelRegistry.getAvailable();
3012
+ const models = [];
3013
+ for (const candidate of candidates) {
3014
+ if (candidate.provider === primary.provider && candidate.id === primary.id)
3015
+ continue;
3016
+ const resolved = available.find((m) => m.provider === candidate.provider && m.id === candidate.id);
3017
+ if (resolved)
3018
+ models.push(resolved);
3019
+ }
3020
+ return models;
3021
+ }
2989
3022
  /**
2990
3023
  * Manually compact the session context.
2991
3024
  * Aborts current agent operation first.
@@ -3046,7 +3079,7 @@ export class AgentSession {
3046
3079
  }
3047
3080
  else {
3048
3081
  // Generate compaction result
3049
- const result = await compact(preparation, compactionModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }));
3082
+ const result = await compact(preparation, compactionModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }), this._compactionFailoverModels(compactionModel));
3050
3083
  summary = result.summary;
3051
3084
  firstKeptEntryId = result.firstKeptEntryId;
3052
3085
  tokensBefore = result.tokensBefore;
@@ -3089,6 +3122,8 @@ export class AgentSession {
3089
3122
  compactionCode = "aborted";
3090
3123
  else if (stale)
3091
3124
  compactionCode = "stale";
3125
+ else if (isQuotaExhaustionMessage(message))
3126
+ compactionCode = "quota_exhausted";
3092
3127
  const timestamp = new Date().toISOString();
3093
3128
  this._publishTermination(classifySessionTermination({
3094
3129
  sessionId: this.sessionId,
@@ -3142,7 +3177,7 @@ export class AgentSession {
3142
3177
  /**
3143
3178
  * Effective context window for the upcoming turn.
3144
3179
  * Image-bearing turns with a text-only session model are auto-routed to the
3145
- * vision model (gpt-5.6-luna, 400K) — that window is the real limit. Text-only
3180
+ * vision model (gpt-5.6-luna, 1M) — that window is the real limit. Text-only
3146
3181
  * turns keep the session model's (typically much larger) window.
3147
3182
  */
3148
3183
  _effectiveTurnContextWindow(pendingMessages, sessionWindow) {
@@ -3377,7 +3412,7 @@ export class AgentSession {
3377
3412
  }
3378
3413
  else {
3379
3414
  // Generate compaction result
3380
- const compactResult = await compact(preparation, compactionModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
3415
+ const compactResult = await compact(preparation, compactionModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), this._compactionFailoverModels(compactionModel));
3381
3416
  summary = compactResult.summary;
3382
3417
  firstKeptEntryId = compactResult.firstKeptEntryId;
3383
3418
  tokensBefore = compactResult.tokensBefore;
@@ -3992,6 +4027,41 @@ export class AgentSession {
3992
4027
  // setModel itself blocks sticky targets, not sources — safe.
3993
4028
  await this.setModel(next);
3994
4029
  }
4030
+ /**
4031
+ * Rotate to another authenticated provider route serving the SAME model
4032
+ * family (e.g. openrouter/stealth/ox-alpha ↔ opencode-go/ox-alpha-free ↔
4033
+ * opencode/x-preview-f-free). Used when the current route fails with an
4034
+ * upstream availability error — same-model retry would hammer a dead
4035
+ * endpoint while the identical model is one route away. Visited and
4036
+ * unauthenticated routes are marked in {@link _refusedModels} so repeat
4037
+ * failures advance the rotation within the turn.
4038
+ */
4039
+ async _maybeRotateModelRoutes() {
4040
+ const current = this.model;
4041
+ if (!current)
4042
+ return undefined;
4043
+ const candidates = sameModelRouteCandidates(current, this._modelRegistry.getAvailable());
4044
+ for (const candidate of candidates) {
4045
+ const key = failoverModelKey(candidate.provider, candidate.id);
4046
+ if (this._refusedModels.has(key))
4047
+ continue;
4048
+ const next = this._modelRegistry.find(candidate.provider, candidate.id);
4049
+ if (!next || !this._modelRegistry.hasConfiguredAuth(next)) {
4050
+ this._refusedModels.add(key);
4051
+ continue;
4052
+ }
4053
+ try {
4054
+ await this.setModel(next);
4055
+ // The old route just failed upstream — keep it out of this turn's retries.
4056
+ this._refusedModels.add(failoverModelKey(current.provider, current.id));
4057
+ return `${next.provider}/${next.id}`;
4058
+ }
4059
+ catch {
4060
+ this._refusedModels.add(key);
4061
+ }
4062
+ }
4063
+ return undefined;
4064
+ }
3995
4065
  /**
3996
4066
  * Content/safety stops (stop_reason=refusal) are often false positives on coding turns
3997
4067
  * for Fable AND Claude Opus/Sonnet. Retrying the same model usually reprints the refusal —
@@ -4053,12 +4123,19 @@ export class AgentSession {
4053
4123
  if (attempt === undefined)
4054
4124
  return false;
4055
4125
  this._retryAttempt = attempt;
4126
+ // Upstream-unavailable (gateway 5xx / dropped stream): rotate to another
4127
+ // authenticated route of the SAME model family so the retry lands on a
4128
+ // live endpoint instead of hammering the dead one.
4129
+ const rotatedTo = isUpstreamUnavailableMessage(message.errorMessage)
4130
+ ? await this._maybeRotateModelRoutes()
4131
+ : undefined;
4056
4132
  // Content/safety stop (Fable/Opus/Sonnet): switch model BEFORE delay so retry is not same-model refusal.
4057
4133
  const failoverTo = await this._maybeFailoverFromSafetyStop(message);
4134
+ const switchedVia = failoverTo ? `failover ${failoverTo}` : rotatedTo ? `route ${rotatedTo}` : undefined;
4058
4135
  // Safety stops are usually immediate false positives — short delay after failover, full backoff otherwise.
4059
- const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, failoverTo !== undefined);
4060
- const errorMessage = failoverTo
4061
- ? `${message.errorMessage || "content/safety stop"} → failover ${failoverTo}`
4136
+ const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, switchedVia !== undefined);
4137
+ const errorMessage = switchedVia
4138
+ ? `${message.errorMessage || "content/safety stop"} → ${switchedVia}`
4062
4139
  : message.errorMessage || "Unknown error";
4063
4140
  this._emit({
4064
4141
  type: "auto_retry_start",