comisai 1.0.15 → 1.0.17

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 (124) hide show
  1. package/node_modules/@comis/agent/dist/bootstrap/types.d.ts +1 -1
  2. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +12 -11
  3. package/node_modules/@comis/agent/dist/context-engine/constants.d.ts +11 -0
  4. package/node_modules/@comis/agent/dist/context-engine/constants.js +11 -0
  5. package/node_modules/@comis/agent/dist/context-engine/index.d.ts +1 -1
  6. package/node_modules/@comis/agent/dist/context-engine/index.js +1 -1
  7. package/node_modules/@comis/agent/dist/context-engine/llm-compaction.js +32 -18
  8. package/node_modules/@comis/agent/dist/executor/cache-break-diff-writer.d.ts +2 -0
  9. package/node_modules/@comis/agent/dist/executor/cache-break-diff-writer.js +36 -0
  10. package/node_modules/@comis/agent/dist/executor/executor-post-execution.d.ts +12 -0
  11. package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +117 -27
  12. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +1 -0
  13. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +16 -4
  14. package/node_modules/@comis/agent/dist/executor/executor-stream-setup.js +17 -1
  15. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +27 -1
  16. package/node_modules/@comis/agent/dist/executor/session-snapshot-cleanup.js +3 -1
  17. package/node_modules/@comis/agent/dist/executor/stream-wrappers/index.d.ts +2 -0
  18. package/node_modules/@comis/agent/dist/executor/stream-wrappers/index.js +2 -0
  19. package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.d.ts +9 -0
  20. package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +65 -3
  21. package/node_modules/@comis/agent/dist/executor/stream-wrappers/stub-filter-injector.d.ts +28 -0
  22. package/node_modules/@comis/agent/dist/executor/stream-wrappers/stub-filter-injector.js +63 -0
  23. package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +18 -1
  24. package/node_modules/@comis/agent/dist/executor/tool-deferral.js +275 -133
  25. package/node_modules/@comis/agent/dist/executor/ttl-guard.d.ts +6 -0
  26. package/node_modules/@comis/agent/dist/executor/ttl-guard.js +8 -0
  27. package/node_modules/@comis/agent/package.json +1 -1
  28. package/node_modules/@comis/channels/dist/shared/typing-controller.d.ts +7 -0
  29. package/node_modules/@comis/channels/dist/shared/typing-controller.js +33 -0
  30. package/node_modules/@comis/channels/package.json +1 -1
  31. package/node_modules/@comis/cli/dist/commands/daemon.js +116 -28
  32. package/node_modules/@comis/cli/dist/wizard/non-interactive.js +1 -0
  33. package/node_modules/@comis/cli/dist/wizard/steps/01-detect-existing.js +4 -0
  34. package/node_modules/@comis/cli/dist/wizard/steps/07-gateway.js +9 -0
  35. package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +2 -0
  36. package/node_modules/@comis/cli/dist/wizard/steps/12-finish.js +5 -2
  37. package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -0
  38. package/node_modules/@comis/cli/package.json +1 -1
  39. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +10 -0
  40. package/node_modules/@comis/core/dist/config/schema-agent.js +8 -0
  41. package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +17 -2
  42. package/node_modules/@comis/core/dist/config/schema-gateway.js +11 -0
  43. package/node_modules/@comis/core/dist/config/schema-skills.d.ts +16 -2
  44. package/node_modules/@comis/core/dist/config/schema-skills.js +9 -2
  45. package/node_modules/@comis/core/dist/config/schema.d.ts +66 -60
  46. package/node_modules/@comis/core/package.json +1 -1
  47. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +34 -6
  48. package/node_modules/@comis/daemon/package.json +1 -1
  49. package/node_modules/@comis/gateway/package.json +1 -1
  50. package/node_modules/@comis/infra/package.json +1 -1
  51. package/node_modules/@comis/memory/package.json +1 -1
  52. package/node_modules/@comis/scheduler/package.json +1 -1
  53. package/node_modules/@comis/shared/package.json +1 -1
  54. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
  55. package/node_modules/@comis/skills/package.json +1 -1
  56. package/node_modules/@comis/web/dist/android-chrome-192x192.png +0 -0
  57. package/node_modules/@comis/web/dist/android-chrome-512x512.png +0 -0
  58. package/node_modules/@comis/web/dist/apple-touch-icon.png +0 -0
  59. package/node_modules/@comis/web/dist/assets/agent-detail-BG9MGWWj.js +615 -0
  60. package/node_modules/@comis/web/dist/assets/agent-editor-C26Q_xCs.js +2173 -0
  61. package/node_modules/@comis/web/dist/assets/agent-list-LHCJ4rw2.js +504 -0
  62. package/node_modules/@comis/web/dist/assets/approvals-q9VH_IKr.js +54 -0
  63. package/node_modules/@comis/web/dist/assets/billing-view-CtYvBqTE.js +375 -0
  64. package/node_modules/@comis/web/dist/assets/channel-detail-CaInesJM.js +681 -0
  65. package/node_modules/@comis/web/dist/assets/channel-list-B8dj3O9a.js +323 -0
  66. package/node_modules/@comis/web/dist/assets/chat-console-CNmzl0JW.js +943 -0
  67. package/node_modules/@comis/web/dist/assets/config-editor-DX4ITw6y.js +1374 -0
  68. package/node_modules/@comis/web/dist/assets/context-dag-browser-BwiaF5tf.js +393 -0
  69. package/node_modules/@comis/web/dist/assets/context-engine-BZ5Am6hA.js +393 -0
  70. package/node_modules/@comis/web/dist/assets/delivery-view-OfBZof-m.js +392 -0
  71. package/node_modules/@comis/web/dist/assets/diagnostics-view-YFwCxgr2.js +231 -0
  72. package/node_modules/@comis/web/dist/assets/directive-DoeGSK_T.js +1 -0
  73. package/node_modules/@comis/web/dist/assets/extract-variables-BM5qyK-s.js +149 -0
  74. package/node_modules/@comis/web/dist/assets/ic-array-editor-B7m6x7-S.js +118 -0
  75. package/node_modules/@comis/web/dist/assets/ic-breadcrumb-CUMpp3BL.js +75 -0
  76. package/node_modules/@comis/web/dist/assets/ic-budget-segment-bar-BtJ6x5mN.js +64 -0
  77. package/node_modules/@comis/web/dist/assets/ic-chat-message-CFyDJd0z.js +352 -0
  78. package/node_modules/@comis/web/dist/assets/ic-confirm-dialog-CCDbB04e.js +110 -0
  79. package/node_modules/@comis/web/dist/assets/ic-connection-dot-CnT1b8xr.js +54 -0
  80. package/node_modules/@comis/web/dist/assets/ic-data-table-CKUNTxHw.js +277 -0
  81. package/node_modules/@comis/web/dist/assets/ic-delivery-row-GP5Fkygs.js +67 -0
  82. package/node_modules/@comis/web/dist/assets/ic-detail-panel-BF83r-if.js +116 -0
  83. package/node_modules/@comis/web/dist/assets/ic-empty-state-60l2ePhd.js +52 -0
  84. package/node_modules/@comis/web/dist/assets/ic-graph-canvas-C8FuSMe1.js +359 -0
  85. package/node_modules/@comis/web/dist/assets/ic-icon-xeGTVhVG.js +33 -0
  86. package/node_modules/@comis/web/dist/assets/ic-layer-waterfall-COvEYMg5.js +69 -0
  87. package/node_modules/@comis/web/dist/assets/ic-relative-time-3FqpjeAI.js +12 -0
  88. package/node_modules/@comis/web/dist/assets/ic-search-input-CSOxY9g7.js +92 -0
  89. package/node_modules/@comis/web/dist/assets/ic-select-Ce-Raudx.js +127 -0
  90. package/node_modules/@comis/web/dist/assets/ic-tabs-B7QtM_v8.js +95 -0
  91. package/node_modules/@comis/web/dist/assets/ic-tag-CPPUnDLF.js +33 -0
  92. package/node_modules/@comis/web/dist/assets/ic-time-range-picker-CypCT68y.js +93 -0
  93. package/node_modules/@comis/web/dist/assets/ic-tool-call-7MaXSsCW.js +146 -0
  94. package/node_modules/@comis/web/dist/assets/index-CEcM1R_C.js +2830 -0
  95. package/node_modules/@comis/web/dist/assets/index-CIJFuItj.css +1 -0
  96. package/node_modules/@comis/web/dist/assets/mcp-management-BNZPnpDn.js +582 -0
  97. package/node_modules/@comis/web/dist/assets/media-config-BBvTYxOX.js +314 -0
  98. package/node_modules/@comis/web/dist/assets/media-test-BkK3RCRK.js +496 -0
  99. package/node_modules/@comis/web/dist/assets/memory-inspector-1hDGCGat.js +1139 -0
  100. package/node_modules/@comis/web/dist/assets/message-center-CXefwsUu.js +785 -0
  101. package/node_modules/@comis/web/dist/assets/models-C1qcU_j3.js +894 -0
  102. package/node_modules/@comis/web/dist/assets/observability-types-D7jUtSde.js +1 -0
  103. package/node_modules/@comis/web/dist/assets/observe-view-C0VBhX4C.js +814 -0
  104. package/node_modules/@comis/web/dist/assets/pipeline-builder-DcUUIrm0.js +1496 -0
  105. package/node_modules/@comis/web/dist/assets/pipeline-history-DkfOQ6SW.js +317 -0
  106. package/node_modules/@comis/web/dist/assets/pipeline-history-detail-hyHgD0ai.js +287 -0
  107. package/node_modules/@comis/web/dist/assets/pipeline-list-BPW8hV-q.js +518 -0
  108. package/node_modules/@comis/web/dist/assets/pipeline-monitor-Bip16T7e.js +972 -0
  109. package/node_modules/@comis/web/dist/assets/scheduler-BGgwKd06.js +1182 -0
  110. package/node_modules/@comis/web/dist/assets/security-D15st4xx.js +1224 -0
  111. package/node_modules/@comis/web/dist/assets/session-detail-SGEYNJ0M.js +632 -0
  112. package/node_modules/@comis/web/dist/assets/session-key-parser-DPORMVyU.js +1 -0
  113. package/node_modules/@comis/web/dist/assets/session-list-6ybUTxbY.js +231 -0
  114. package/node_modules/@comis/web/dist/assets/setup-wizard-nT0tz9QP.js +853 -0
  115. package/node_modules/@comis/web/dist/assets/skills-D8yVfSUy.js +1051 -0
  116. package/node_modules/@comis/web/dist/assets/subagents-HHXMeHYo.js +193 -0
  117. package/node_modules/@comis/web/dist/assets/workspace-manager-BQlr10iH.js +785 -0
  118. package/node_modules/@comis/web/dist/comis-logo.png +0 -0
  119. package/node_modules/@comis/web/dist/favicon-16x16.png +0 -0
  120. package/node_modules/@comis/web/dist/favicon-32x32.png +0 -0
  121. package/node_modules/@comis/web/dist/favicon.ico +0 -0
  122. package/node_modules/@comis/web/dist/index.html +17 -0
  123. package/node_modules/@comis/web/package.json +29 -0
  124. package/package.json +15 -13
@@ -20,7 +20,7 @@ import { clearSessionTracker } from "./tool-lifecycle.js";
20
20
  import { clearDiscoveryTracker } from "./discovery-tracker.js";
21
21
  import { clearCacheBreakDetectorSession } from "./cache-break-detection.js";
22
22
  import { clearSessionLastResponseTs } from "./ttl-guard.js";
23
- import { clearSessionBetaHeaderLatches } from "./stream-wrappers/request-body-injector.js";
23
+ import { clearSessionBetaHeaderLatches, clearSessionPrefixStability, clearSessionCadenceTracker } from "./stream-wrappers/request-body-injector.js";
24
24
  import { clearSessionRenderedToolCache, clearSessionPerToolCache } from "./stream-wrappers/tool-schema-cache.js";
25
25
  import { clearSessionBlockStability } from "./block-stability-tracker.js";
26
26
  /**
@@ -49,6 +49,8 @@ export function clearSessionState(formattedKey) {
49
49
  clearSessionBlockStability(formattedKey);
50
50
  clearSessionEvictionCooldown(formattedKey);
51
51
  clearSessionCacheSavings(formattedKey);
52
+ clearSessionPrefixStability(formattedKey);
53
+ clearSessionCadenceTracker(formattedKey);
52
54
  }
53
55
  /**
54
56
  * Subscribe to `session:expired` on the provided event bus and clean up
@@ -24,3 +24,5 @@ export { createRequestBodyInjector, addCacheControlToLastBlock, CACHEABLE_BLOCK_
24
24
  export type { RequestBodyInjectorConfig } from "./request-body-injector.js";
25
25
  export { sessionRenderedToolCache, getOrCacheRenderedTool, clearSessionRenderedToolCache, clearSessionPerToolCache, } from "./tool-schema-cache.js";
26
26
  export type { RenderedToolCacheEntry, PerToolCacheEntry } from "./tool-schema-cache.js";
27
+ export { createStubFilterInjector } from "./stub-filter-injector.js";
28
+ export type { StubFilterInjectorConfig } from "./stub-filter-injector.js";
@@ -20,3 +20,5 @@ export { createApiPayloadTraceWriter } from "./api-payload-trace-writer.js";
20
20
  export { createRequestBodyInjector, addCacheControlToLastBlock, CACHEABLE_BLOCK_TYPES, getMinCacheableTokens, resolveCacheRetention, clearSessionBetaHeaderLatches, } from "./request-body-injector.js";
21
21
  // Tool schema cache extracted to leaf module
22
22
  export { sessionRenderedToolCache, getOrCacheRenderedTool, clearSessionRenderedToolCache, clearSessionPerToolCache, } from "./tool-schema-cache.js";
23
+ // Stub filter injector (auto-discovery stubs)
24
+ export { createStubFilterInjector } from "./stub-filter-injector.js";
@@ -105,6 +105,14 @@ export interface RequestBodyInjectorConfig {
105
105
  /** Getter for elapsed ms since last assistant response.
106
106
  * Used for time-based microcompact to detect cold-start scenarios. */
107
107
  getElapsedSinceLastResponse?: () => number | undefined;
108
+ /** When true, the recent-zone message breakpoint may be promoted from
109
+ * "short" to "long" TTL based on observed inter-turn timing.
110
+ * Requires sessionKey, getElapsedSinceLastResponse, AND getLastResponseTs. */
111
+ promoteRecentZoneOnSlowCadence?: boolean;
112
+ /** Getter for the raw `sessionLastResponseTs.ts` value (ms since epoch)
113
+ * for this session, or undefined on cold-start. Used by the cadence
114
+ * tracker to detect turn boundaries within a single execute(). */
115
+ getLastResponseTs?: () => number | undefined;
108
116
  /** Number of recent tool results to preserve during microcompact.
109
117
  * Defaults to 25 (matches observation masker keep window). */
110
118
  observationKeepWindow?: number;
@@ -141,6 +149,7 @@ export { clearSessionRenderedToolCache, getOrCacheRenderedTool, clearSessionPerT
141
149
  export type { RenderedToolCacheEntry } from "./tool-schema-cache.js";
142
150
  export declare function clearSessionPrefixStability(sessionKey: string): void;
143
151
  export declare function clearSessionBetaHeaderLatches(sessionKey: string): void;
152
+ export declare function clearSessionCadenceTracker(sessionKey: string): void;
144
153
  /**
145
154
  * Resolve effective cache retention for a model, considering per-model overrides.
146
155
  * Uses longest-prefix-first matching: "claude-sonnet-4-6" wins over "claude-sonnet".
@@ -43,6 +43,13 @@ export function clearSessionPrefixStability(sessionKey) {
43
43
  export function clearSessionBetaHeaderLatches(sessionKey) {
44
44
  sessionBetaHeaderLatches.delete(sessionKey);
45
45
  }
46
+ const sessionCadenceTracker = new Map();
47
+ export function clearSessionCadenceTracker(sessionKey) {
48
+ sessionCadenceTracker.delete(sessionKey);
49
+ }
50
+ const SLOW_CADENCE_PROMOTION_THRESHOLD = 3;
51
+ const FAST_CADENCE_DEMOTION_THRESHOLD = 5;
52
+ const SLOW_CADENCE_MS = 5 * 60 * 1000; // 5 minutes
46
53
  // ---------------------------------------------------------------------------
47
54
  // Per-model cache retention override resolution.
48
55
  // Enables selectively disabling or changing prompt caching for specific model
@@ -402,7 +409,17 @@ function placeCacheBreakpoints(messages, options) {
402
409
  const startFrom = semiStableIdx >= 0 ? semiStableIdx + 1 : 0;
403
410
  const tokensInRange = estimateTokensInRange(startFrom, secondToLastUserIdx);
404
411
  if (tokensInRange >= minTokens) {
405
- addCacheControlToLastBlock(messages[secondToLastUserIdx], retention);
412
+ // Promote recent-zone to "long" when cadence indicates user pauses exceed 5m.
413
+ // Monotonicity guard: recent zone can only be promoted when
414
+ // resolvedRetention (tool/system) is already "long".
415
+ let recentRetention = retention;
416
+ if (options.promoteRecentZoneOnSlowCadence && options.sessionKey) {
417
+ const cadence = sessionCadenceTracker.get(options.sessionKey);
418
+ if (cadence?.promoted && resolvedRetention === "long") {
419
+ recentRetention = "long";
420
+ }
421
+ }
422
+ addCacheControlToLastBlock(messages[secondToLastUserIdx], recentRetention);
406
423
  placed++;
407
424
  }
408
425
  }
@@ -434,7 +451,7 @@ function placeCacheBreakpoints(messages, options) {
434
451
  // The Anthropic API uses a 20-block lookback window for cache prefix matching.
435
452
  // If any gap exceeds the window and slots remain, place a bridging breakpoint
436
453
  // at the midpoint of the gap to prevent silent cache misses.
437
- if (placed > 0 && placed < Math.min(maxBreakpoints, 3)) {
454
+ if (placed > 0 && placed < maxBreakpoints) {
438
455
  const breakpointPositions = [];
439
456
  for (let i = 0; i < messages.length; i++) {
440
457
  const content = messages[i].content;
@@ -448,7 +465,7 @@ function placeCacheBreakpoints(messages, options) {
448
465
  }
449
466
  }
450
467
  // Check gaps between consecutive breakpoints
451
- for (let g = 1; g < breakpointPositions.length && placed < Math.min(maxBreakpoints, 3); g++) {
468
+ for (let g = 1; g < breakpointPositions.length && placed < maxBreakpoints; g++) {
452
469
  const gap = breakpointPositions[g] - breakpointPositions[g - 1];
453
470
  if (gap > CACHE_LOOKBACK_WINDOW) {
454
471
  // Find a user message near the midpoint of the gap
@@ -1338,6 +1355,8 @@ export function createRequestBodyInjector(config, logger) {
1338
1355
  resolvedRetention: inCooldown ? "short" : messageRetention, // Force "short" during cooldown
1339
1356
  strategy: resolveBreakpointStrategy(config.cacheBreakpointStrategy, model.provider),
1340
1357
  skipCacheWrite: effectiveSkipCacheWrite,
1358
+ promoteRecentZoneOnSlowCadence: config.promoteRecentZoneOnSlowCadence,
1359
+ sessionKey: config.sessionKey,
1341
1360
  });
1342
1361
  if (placed > 0) {
1343
1362
  logger.debug({
@@ -1478,6 +1497,49 @@ export function createRequestBodyInjector(config, logger) {
1478
1497
  if (config.onPayloadForCacheDetection) {
1479
1498
  config.onPayloadForCacheDetection(result, model, mergedHeaders);
1480
1499
  }
1500
+ // Track cadence for recent-zone promotion (symmetric: promote slow, demote on fast).
1501
+ // Runs after onPayloadForCacheDetection so the detection snapshot reflects the
1502
+ // pre-mutation state. Mutation takes effect on the next turn's placeCacheBreakpoints().
1503
+ if (config.promoteRecentZoneOnSlowCadence && config.sessionKey
1504
+ && config.getElapsedSinceLastResponse && config.getLastResponseTs) {
1505
+ const lastResponseTs = config.getLastResponseTs();
1506
+ if (lastResponseTs !== undefined) {
1507
+ let tracker = sessionCadenceTracker.get(config.sessionKey);
1508
+ if (!tracker) {
1509
+ tracker = {
1510
+ consecutiveSlowTurns: 0,
1511
+ consecutiveFastTurns: 0,
1512
+ promoted: false,
1513
+ lastObservedResponseTs: undefined,
1514
+ };
1515
+ sessionCadenceTracker.set(config.sessionKey, tracker);
1516
+ }
1517
+ // Same-turn guard: successive onPayload calls inside one execute() all
1518
+ // observe the same lastResponseTs. Only count once per turn boundary.
1519
+ if (lastResponseTs !== tracker.lastObservedResponseTs) {
1520
+ tracker.lastObservedResponseTs = lastResponseTs;
1521
+ const elapsed = config.getElapsedSinceLastResponse();
1522
+ if (elapsed !== undefined) {
1523
+ if (elapsed > SLOW_CADENCE_MS) {
1524
+ tracker.consecutiveSlowTurns++;
1525
+ tracker.consecutiveFastTurns = 0;
1526
+ if (!tracker.promoted && tracker.consecutiveSlowTurns >= SLOW_CADENCE_PROMOTION_THRESHOLD) {
1527
+ tracker.promoted = true;
1528
+ logger.info({ sessionKey: config.sessionKey, consecutiveSlowTurns: tracker.consecutiveSlowTurns }, "Recent-zone TTL promoted to long: slow cadence detected");
1529
+ }
1530
+ }
1531
+ else {
1532
+ tracker.consecutiveFastTurns++;
1533
+ tracker.consecutiveSlowTurns = 0;
1534
+ if (tracker.promoted && tracker.consecutiveFastTurns >= FAST_CADENCE_DEMOTION_THRESHOLD) {
1535
+ tracker.promoted = false;
1536
+ logger.info({ sessionKey: config.sessionKey, consecutiveFastTurns: tracker.consecutiveFastTurns }, "Recent-zone TTL demoted to short: fast cadence resumed");
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+ }
1542
+ }
1481
1543
  // SDK-UPGRADE: Upgrade SDK auto-placed 5m markers to 1h when retention is long.
1482
1544
  // The pi-ai SDK places cache_control: { type: "ephemeral" } (5m TTL) on the last
1483
1545
  // user message. When the session uses "long" retention, these 5m writes waste money
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Stub-filter injector stream wrapper.
3
+ *
4
+ * Removes auto-discovery stub tools (see tool-deferral.ts createAutoDiscoveryStubs)
5
+ * from the API-ready payload. Stubs are present in the SDK's tools array so
6
+ * agent-loop.js tool lookup succeeds, but must not reach the API:
7
+ * - They consume input tokens (~100 tokens per stub schema).
8
+ * - For Anthropic models where supportsToolSearch() is true, unfiltered stub
9
+ * names would match in the DEFER-TOOL block (request-body-injector.ts
10
+ * lines 1599-1643), causing deferCount > 0 which REMOVES client-side
11
+ * discover_tools and APPENDS server-side tool_search_tool_regex —
12
+ * flipping the session to a different control path unintentionally.
13
+ * - For Google AI Studio, unfiltered stubs would be persisted into the
14
+ * Gemini CachedContent entry (gemini-cache-injector.ts line 191-195),
15
+ * bloating the cache for its entire lifetime.
16
+ *
17
+ * Provider-agnostic: filters both top-level `params.tools` (Anthropic,
18
+ * OpenAI, xAI) and nested `params.config.tools` (Google AI Studio).
19
+ *
20
+ * @module
21
+ */
22
+ import type { ComisLogger } from "@comis/infra";
23
+ import type { StreamFnWrapper } from "./types.js";
24
+ export interface StubFilterInjectorConfig {
25
+ /** Getter for stub tool names. Filtered from the rendered API payload. */
26
+ getStubToolNames: () => ReadonlySet<string>;
27
+ }
28
+ export declare function createStubFilterInjector(config: StubFilterInjectorConfig, logger: ComisLogger): StreamFnWrapper;
@@ -0,0 +1,63 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Stub-filter injector stream wrapper.
4
+ *
5
+ * Removes auto-discovery stub tools (see tool-deferral.ts createAutoDiscoveryStubs)
6
+ * from the API-ready payload. Stubs are present in the SDK's tools array so
7
+ * agent-loop.js tool lookup succeeds, but must not reach the API:
8
+ * - They consume input tokens (~100 tokens per stub schema).
9
+ * - For Anthropic models where supportsToolSearch() is true, unfiltered stub
10
+ * names would match in the DEFER-TOOL block (request-body-injector.ts
11
+ * lines 1599-1643), causing deferCount > 0 which REMOVES client-side
12
+ * discover_tools and APPENDS server-side tool_search_tool_regex —
13
+ * flipping the session to a different control path unintentionally.
14
+ * - For Google AI Studio, unfiltered stubs would be persisted into the
15
+ * Gemini CachedContent entry (gemini-cache-injector.ts line 191-195),
16
+ * bloating the cache for its entire lifetime.
17
+ *
18
+ * Provider-agnostic: filters both top-level `params.tools` (Anthropic,
19
+ * OpenAI, xAI) and nested `params.config.tools` (Google AI Studio).
20
+ *
21
+ * @module
22
+ */
23
+ export function createStubFilterInjector(config, logger) {
24
+ return function stubFilterInjector(next) {
25
+ return (model, context, options) => {
26
+ const existingOnPayload = options?.onPayload;
27
+ const enhancedOptions = {
28
+ ...options,
29
+ onPayload: async (payload, payloadModel) => {
30
+ // Let any upstream onPayload run first (we are the innermost wrapper,
31
+ // so upstream here is the SDK's own or a user-provided one).
32
+ const resolvedParams = (existingOnPayload
33
+ ? await Promise.resolve(existingOnPayload(payload, payloadModel))
34
+ : payload);
35
+ const stubNames = config.getStubToolNames();
36
+ if (stubNames.size === 0)
37
+ return resolvedParams;
38
+ let removed = 0;
39
+ // Top-level tools (Anthropic, OpenAI, xAI)
40
+ if (Array.isArray(resolvedParams.tools)) {
41
+ const before = resolvedParams.tools.length;
42
+ resolvedParams.tools = resolvedParams.tools
43
+ .filter(t => !stubNames.has(t.name));
44
+ removed += before - resolvedParams.tools.length;
45
+ }
46
+ // Nested config.tools (Google AI Studio / Gemini)
47
+ const cfg = resolvedParams.config;
48
+ if (cfg && Array.isArray(cfg.tools)) {
49
+ const before = cfg.tools.length;
50
+ cfg.tools = cfg.tools
51
+ .filter(t => !stubNames.has(t.name));
52
+ removed += before - cfg.tools.length;
53
+ }
54
+ if (removed > 0) {
55
+ logger.debug({ removed, provider: payloadModel?.provider }, "Stub filter removed auto-discovery stubs from payload");
56
+ }
57
+ return resolvedParams;
58
+ },
59
+ };
60
+ return next(model, context, enhancedOptions);
61
+ };
62
+ };
63
+ }
@@ -50,6 +50,11 @@ export interface DeferralContext {
50
50
  * "default") do not inject mid-turn, so MCP tools must be active from the
51
51
  * start. When undefined, defaults to deferring (backward compat). */
52
52
  providerFamily?: string;
53
+ /** Names of tools currently ACTIVE in this session (post-deferral).
54
+ * Consumed by discover_tools to return "already active" guidance when
55
+ * queries re-ask for loaded MCPs. Must NOT include names that were
56
+ * deferred -- pass the post-deferral set, not mergedCustomTools. */
57
+ activeToolNames?: ReadonlySet<string>;
53
58
  }
54
59
  /** Entry describing a deferred tool with its display description and original definition. */
55
60
  export interface DeferredToolEntry {
@@ -153,7 +158,19 @@ export interface ToolDiscoveryScoreConfig {
153
158
  * Receives DeferredToolEntry[] (with original schemas and display descriptions)
154
159
  * so it can serve full schemas and lean descriptions when queried.
155
160
  *
161
+ * BM25 scores are normalized to [0, 1] (fraction of top match) BEFORE the
162
+ * score-floor filter applies, matching the semantics of hybrid-mode scoring.
163
+ * This ensures the top match always clears any floor <= 1.0 whenever any
164
+ * positive signal exists (fixes the srv1593437 08:06:39Z "install MCP"
165
+ * regression where raw BM25 ~0.74 was dropped by the 0.8 raw-score floor).
166
+ *
156
167
  * @param scoreConfig Optional score-floor override (defaults to 0.8 BM25 /
157
168
  * 0.35 hybrid). Zero or negative floors disable the filter.
169
+ * @param activeToolNames Names of tools currently ACTIVE in this session
170
+ * (post-deferral: active + discovered). Used to return "already active"
171
+ * guidance when queries re-ask for loaded MCPs. Must NOT include names
172
+ * that were deferred. Default empty set keeps callers backward-compatible.
158
173
  */
159
- export declare function createDiscoverTool(deferredEntries: DeferredToolEntry[], logger: ComisLogger, embeddingPort?: EmbeddingPort, scoreConfig?: ToolDiscoveryScoreConfig): ToolDefinition;
174
+ export declare function createDiscoverTool(deferredEntries: DeferredToolEntry[], logger: ComisLogger, embeddingPort?: EmbeddingPort, scoreConfig?: ToolDiscoveryScoreConfig, activeToolNames?: ReadonlySet<string>): ToolDefinition;
175
+ export declare const DEFERRAL_STUB_MARKER: "__comis_deferral_stub__";
176
+ export declare function createAutoDiscoveryStubs(deferredEntries: DeferredToolEntry[], discoveryTracker: DiscoveryTracker, logger: ComisLogger): ToolDefinition[];