open-multi-agent-kit 0.98.1 → 0.98.2

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 (63) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +2 -2
  3. package/dist/cli/mcp-attach.d.ts +15 -0
  4. package/dist/cli/mcp-attach.d.ts.map +1 -0
  5. package/dist/cli/mcp-attach.js +14 -0
  6. package/dist/cli/mcp-attach.js.map +1 -0
  7. package/dist/core/agent-session.d.ts +7 -0
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +66 -14
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/auth-storage.d.ts +19 -2
  12. package/dist/core/auth-storage.d.ts.map +1 -1
  13. package/dist/core/auth-storage.js +57 -6
  14. package/dist/core/auth-storage.js.map +1 -1
  15. package/dist/core/compaction/oauth-recovery.d.ts +29 -0
  16. package/dist/core/compaction/oauth-recovery.d.ts.map +1 -0
  17. package/dist/core/compaction/oauth-recovery.js +40 -0
  18. package/dist/core/compaction/oauth-recovery.js.map +1 -0
  19. package/dist/core/model-registry.d.ts +10 -1
  20. package/dist/core/model-registry.d.ts.map +1 -1
  21. package/dist/core/model-registry.js +16 -2
  22. package/dist/core/model-registry.js.map +1 -1
  23. package/dist/core/provider-resilience.d.ts +8 -0
  24. package/dist/core/provider-resilience.d.ts.map +1 -1
  25. package/dist/core/provider-resilience.js +17 -0
  26. package/dist/core/provider-resilience.js.map +1 -1
  27. package/dist/core/provider-usage.d.ts +1 -1
  28. package/dist/core/provider-usage.d.ts.map +1 -1
  29. package/dist/core/provider-usage.js +3 -2
  30. package/dist/core/provider-usage.js.map +1 -1
  31. package/dist/core/session-failure-cause.d.ts.map +1 -1
  32. package/dist/core/session-failure-cause.js +7 -1
  33. package/dist/core/session-failure-cause.js.map +1 -1
  34. package/dist/core/session-termination.d.ts.map +1 -1
  35. package/dist/core/session-termination.js +3 -1
  36. package/dist/core/session-termination.js.map +1 -1
  37. package/dist/core/verified-bash-runtime.d.ts.map +1 -1
  38. package/dist/core/verified-bash-runtime.js +6 -4
  39. package/dist/core/verified-bash-runtime.js.map +1 -1
  40. package/dist/guardrails/workspace-fingerprint.d.ts +2 -0
  41. package/dist/guardrails/workspace-fingerprint.d.ts.map +1 -1
  42. package/dist/guardrails/workspace-fingerprint.js +12 -10
  43. package/dist/guardrails/workspace-fingerprint.js.map +1 -1
  44. package/dist/main.d.ts.map +1 -1
  45. package/dist/main.js +7 -0
  46. package/dist/main.js.map +1 -1
  47. package/docs/book-to-skill.md +17 -0
  48. package/docs/docs.json +4 -0
  49. package/docs/index.md +2 -0
  50. package/docs/mcp.md +10 -2
  51. package/docs/security.md +4 -9
  52. package/examples/extensions/custom-provider-anthropic/index.ts +4 -1
  53. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  54. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  55. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  56. package/examples/extensions/gondolin/package-lock.json +2 -2
  57. package/examples/extensions/gondolin/package.json +1 -1
  58. package/examples/extensions/sandbox/package-lock.json +2 -2
  59. package/examples/extensions/sandbox/package.json +1 -1
  60. package/examples/extensions/with-deps/package-lock.json +2 -2
  61. package/examples/extensions/with-deps/package.json +1 -1
  62. package/npm-shrinkwrap.json +18 -18
  63. package/package.json +6 -6
@@ -31,6 +31,7 @@ import { parseBangInvocation } from "./bang-skill-invocation.js";
31
31
  import { getCompactionHeadroomThreshold } from "./compaction/compaction.js";
32
32
  import { createCompactionHysteresisConfig, createCompactionHysteresisState, stepCompactionHysteresis, } from "./compaction/hysteresis.js";
33
33
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateProjectedContextTokens, generateBranchSummary, prepareCompaction, resolveCompactionModel, } from "./compaction/index.js";
34
+ import { summarizeWithOAuthRecovery } from "./compaction/oauth-recovery.js";
34
35
  import { compactionEmitWillRetry } from "./compaction/resume-policy.js";
35
36
  import { isSessionModelOverflow, shouldSkipCompactionCheck } from "./compaction-gate.js";
36
37
  import { estimateToolResultReserve, } from "./context-budget-reserved-tokens.js";
@@ -55,6 +56,16 @@ const DISABLED_ENV_VALUES = new Set(["0", "false", "off", "disable", "disabled"]
55
56
  const MAX_OVERFLOW_RECOVERY_ATTEMPTS = 2;
56
57
  const OVERFLOW_RECOVERY_EMERGENCY_TOKENS = 4_096;
57
58
  const MAX_RESOURCE_OBSERVATION_JOURNALS = 32;
59
+ /**
60
+ * Token headroom demanded before a compaction run.
61
+ *
62
+ * Compaction resolves auth once and then reuses that token for a summarization
63
+ * that can stream for minutes and be retried with backoff. A token accepted
64
+ * with only seconds of validity left comes back as a provider 401 mid-run
65
+ * ("authentication token is expired"), which reads as a compaction failure.
66
+ * Refreshing up front is cheap; failing a long compaction is not.
67
+ */
68
+ const COMPACTION_MIN_TOKEN_VALIDITY_MS = 10 * 60 * 1000;
58
69
  function isDisabledEnvValue(value) {
59
70
  return value !== undefined && DISABLED_ENV_VALUES.has(value.trim().toLowerCase());
60
71
  }
@@ -712,8 +723,8 @@ export class AgentSession {
712
723
  _getBashSandboxPreflight(override) {
713
724
  return this._bashRuntime.sandboxPreflight(override);
714
725
  }
715
- async _getRequiredRequestAuth(model) {
716
- const result = await this._modelRegistry.getApiKeyAndHeaders(model);
726
+ async _getRequiredRequestAuth(model, options) {
727
+ const result = await this._modelRegistry.getApiKeyAndHeaders(model, options);
717
728
  if (!result.ok) {
718
729
  if (result.error.startsWith("No API key found")) {
719
730
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
@@ -732,10 +743,11 @@ export class AgentSession {
732
743
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
733
744
  }
734
745
  async _getCompactionRequestAuth(model) {
746
+ const options = { minRemainingMs: COMPACTION_MIN_TOKEN_VALIDITY_MS };
735
747
  if (isBuiltinStreamFn(this.agent.streamFn)) {
736
- return this._getRequiredRequestAuth(model);
748
+ return this._getRequiredRequestAuth(model, options);
737
749
  }
738
- const result = await this._modelRegistry.getApiKeyAndHeaders(model);
750
+ const result = await this._modelRegistry.getApiKeyAndHeaders(model, options);
739
751
  return result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};
740
752
  }
741
753
  /**
@@ -2814,6 +2826,21 @@ export class AgentSession {
2814
2826
  * Used when the primary model's quota is exhausted (same-model retry is
2815
2827
  * useless until reset; another provider can still save the compaction).
2816
2828
  */
2829
+ /**
2830
+ * Summarize for manual and automatic compaction alike. If the provider
2831
+ * rejects the OAuth token as expired despite a still-future stored expiry
2832
+ * (ChatGPT `401 token_expired`), the credential is force-refreshed once and
2833
+ * the summarization retried; the retry classifier rightly never replays a 401.
2834
+ */
2835
+ _summarizeCompaction(input) {
2836
+ const { model } = input;
2837
+ return summarizeWithOAuthRecovery({
2838
+ apiKey: input.apiKey,
2839
+ provider: model.provider,
2840
+ refreshRejectedToken: (rejected) => this._modelRegistry.refreshRejectedOAuthToken(model, rejected),
2841
+ run: (requestApiKey) => compact(input.preparation, model, requestApiKey, input.headers, input.customInstructions, input.signal, this.thinkingLevel, this.agent.streamFn, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: input.reason }), this._compactionFailoverModels(model)),
2842
+ });
2843
+ }
2817
2844
  _compactionFailoverModels(primary) {
2818
2845
  const candidates = resolveFailoverCandidates(this.settingsManager.getProviderResilienceSettings());
2819
2846
  const available = this._modelRegistry.getAvailable();
@@ -2838,11 +2865,14 @@ export class AgentSession {
2838
2865
  this._compactionAbortController = new AbortController();
2839
2866
  this._emit({ type: "compaction_start", reason: "manual" });
2840
2867
  let committedCompaction = false;
2868
+ // Hoisted so a failure is attributed to the model that actually summarized,
2869
+ // which `compaction.model` may make different from the session model.
2870
+ let compactionModel;
2841
2871
  try {
2842
2872
  if (!this.model) {
2843
2873
  throw new Error(formatNoModelSelectedMessage());
2844
2874
  }
2845
- const compactionModel = this._resolveCompactionModel(this.model);
2875
+ compactionModel = this._resolveCompactionModel(this.model);
2846
2876
  const { apiKey, headers } = await this._getCompactionRequestAuth(compactionModel);
2847
2877
  const settings = this.settingsManager.getCompactionSettings();
2848
2878
  const begun = this._beginCompactionTransaction(compactionModel, false);
@@ -2886,8 +2916,15 @@ export class AgentSession {
2886
2916
  details = extensionCompaction.details;
2887
2917
  }
2888
2918
  else {
2889
- // Generate compaction result
2890
- 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));
2919
+ const result = await this._summarizeCompaction({
2920
+ preparation,
2921
+ model: compactionModel,
2922
+ apiKey,
2923
+ headers,
2924
+ customInstructions,
2925
+ signal: this._compactionAbortController.signal,
2926
+ reason: "manual",
2927
+ });
2891
2928
  summary = result.summary;
2892
2929
  firstKeptEntryId = result.firstKeptEntryId;
2893
2930
  tokensBefore = result.tokensBefore;
@@ -2941,7 +2978,11 @@ export class AgentSession {
2941
2978
  message: terminationMessage(message, "Manual compaction failed."),
2942
2979
  cause: { area: "compaction", code: compactionCode },
2943
2980
  sideEffects: committedCompaction ? "confirmed" : "none",
2944
- ...(this.model ? { provider: this.model.provider, model: this.model.id } : {}),
2981
+ ...(compactionModel
2982
+ ? { provider: compactionModel.provider, model: compactionModel.id }
2983
+ : this.model
2984
+ ? { provider: this.model.provider, model: this.model.id }
2985
+ : {}),
2945
2986
  }));
2946
2987
  this._emit({
2947
2988
  type: "compaction_end",
@@ -3134,6 +3175,8 @@ export class AgentSession {
3134
3175
  : configuredSettings;
3135
3176
  this._emit({ type: "compaction_start", reason });
3136
3177
  this._autoCompactionAbortController = new AbortController();
3178
+ // Hoisted so the failure message names the model that actually summarized.
3179
+ let compactionModel;
3137
3180
  try {
3138
3181
  if (!this.model) {
3139
3182
  this._emit({
@@ -3145,11 +3188,13 @@ export class AgentSession {
3145
3188
  });
3146
3189
  return false;
3147
3190
  }
3148
- const compactionModel = this._resolveCompactionModel(this.model);
3191
+ compactionModel = this._resolveCompactionModel(this.model);
3149
3192
  let apiKey;
3150
3193
  let headers;
3151
3194
  if (isBuiltinStreamFn(this.agent.streamFn)) {
3152
- const authResult = await this._modelRegistry.getApiKeyAndHeaders(compactionModel);
3195
+ const authResult = await this._modelRegistry.getApiKeyAndHeaders(compactionModel, {
3196
+ minRemainingMs: COMPACTION_MIN_TOKEN_VALIDITY_MS,
3197
+ });
3153
3198
  if (!authResult.ok || !authResult.apiKey) {
3154
3199
  const providerLabel = compactionModel.provider;
3155
3200
  this._emit({
@@ -3219,8 +3264,14 @@ export class AgentSession {
3219
3264
  details = extensionCompaction.details;
3220
3265
  }
3221
3266
  else {
3222
- // Generate compaction result
3223
- 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));
3267
+ const compactResult = await this._summarizeCompaction({
3268
+ preparation,
3269
+ model: compactionModel,
3270
+ apiKey,
3271
+ headers,
3272
+ signal: this._autoCompactionAbortController.signal,
3273
+ reason,
3274
+ });
3224
3275
  summary = compactResult.summary;
3225
3276
  firstKeptEntryId = compactResult.firstKeptEntryId;
3226
3277
  tokensBefore = compactResult.tokensBefore;
@@ -3288,6 +3339,7 @@ export class AgentSession {
3288
3339
  }
3289
3340
  catch (error) {
3290
3341
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
3342
+ const modelLabel = compactionModel ? ` (${compactionModel.provider}/${compactionModel.id})` : "";
3291
3343
  this._emit({
3292
3344
  type: "compaction_end",
3293
3345
  reason,
@@ -3295,8 +3347,8 @@ export class AgentSession {
3295
3347
  aborted: false,
3296
3348
  willRetry: false,
3297
3349
  errorMessage: reason === "overflow"
3298
- ? `Context overflow recovery failed: ${errorMessage}`
3299
- : `Auto-compaction failed: ${errorMessage}`,
3350
+ ? `Context overflow recovery failed${modelLabel}: ${errorMessage}`
3351
+ : `Auto-compaction failed${modelLabel}: ${errorMessage}`,
3300
3352
  });
3301
3353
  return false;
3302
3354
  }