@wrongstack/core 0.306.4 → 0.307.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.
- package/dist/chronicle/index.js +957 -953
- package/dist/chronicle/metrics-ingest.d.ts +55 -0
- package/dist/chronicle/metrics-schema.d.ts +86 -0
- package/dist/chronicle/metrics-store.d.ts +5 -120
- package/dist/chronicle/project-server.js +1083 -1084
- package/dist/chronicle/sqlite-journal-quota.d.ts +39 -0
- package/dist/chronicle/sqlite-journal-schema.d.ts +25 -0
- package/dist/chronicle/sqlite-journal.d.ts +12 -177
- package/dist/coordination/agents/index.js +1738 -1690
- package/dist/coordination/agents/project-agent-capture.d.ts +48 -0
- package/dist/coordination/agents/project-agent-identity.d.ts +3 -129
- package/dist/coordination/agents/project-agent-roster.d.ts +17 -0
- package/dist/coordination/index.js +6011 -5894
- package/dist/coordination/mail-tools.d.ts +1 -1
- package/dist/coordination/mailbox-http-actor-query.d.ts +16 -0
- package/dist/coordination/mailbox-http-router.d.ts +0 -48
- package/dist/coordination/mailbox-http-sse.d.ts +12 -0
- package/dist/coordination/mailbox-message-types.d.ts +71 -0
- package/dist/coordination/mailbox-predicates.d.ts +13 -0
- package/dist/coordination/mailbox-project-server.js +1 -1
- package/dist/coordination/mailbox-session-sync.d.ts +8 -0
- package/dist/coordination/mailbox-types.d.ts +6 -793
- package/dist/core/agent-loop-context.d.ts +28 -0
- package/dist/core/agent-loop-detector.d.ts +25 -0
- package/dist/core/fallback-doctor.d.ts +72 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1159 -888
- package/dist/defaults/index.js +3579 -3545
- package/dist/execution/autonomy-brain-llm.d.ts +55 -0
- package/dist/execution/autonomy-brain.d.ts +3 -156
- package/dist/execution/compaction-budget.d.ts +38 -0
- package/dist/execution/compaction-core.d.ts +3 -170
- package/dist/execution/compaction-elision.d.ts +58 -0
- package/dist/execution/council-orchestrator-helpers.d.ts +39 -0
- package/dist/execution/council-orchestrator.d.ts +3 -42
- package/dist/execution/council-response-parser.d.ts +56 -0
- package/dist/execution/index.js +2415 -2443
- package/dist/execution/prompt-enhancer.js +11 -3
- package/dist/execution/tool-executor-guard.d.ts +18 -0
- package/dist/execution/tool-executor-runner.d.ts +12 -0
- package/dist/execution/tool-executor.d.ts +0 -75
- package/dist/goal/index.js +19 -6
- package/dist/hq/index.js +8 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7813 -7292
- package/dist/infrastructure/index.js +3 -1
- package/dist/kernel/events/agent-events.d.ts +2 -0
- package/dist/kernel/events/provider-events.d.ts +13 -0
- package/dist/plugin/index.js +2590 -2561
- package/dist/plugins/auto-review-config.d.ts +53 -0
- package/dist/plugins/auto-review-git.d.ts +19 -0
- package/dist/plugins/auto-review-plugin.d.ts +2 -140
- package/dist/plugins/review-finding-verification.d.ts +7 -0
- package/dist/prompts/index.d.ts +1 -0
- package/dist/prompts/prompt-journal.d.ts +94 -0
- package/dist/security/index.js +627 -652
- package/dist/security/permission-explain.d.ts +19 -0
- package/dist/security/permission-policy.d.ts +0 -101
- package/dist/security/yolo-risk.d.ts +5 -4
- package/dist/session-catalog/index.js +220 -212
- package/dist/session-catalog/project-server.js +227 -219
- package/dist/session-catalog/store-rebuild.d.ts +9 -0
- package/dist/session-catalog/store-schema.d.ts +48 -0
- package/dist/session-catalog/store.d.ts +0 -17
- package/dist/storage/director-state.d.ts +6 -0
- package/dist/storage/file-session-writer.d.ts +2 -58
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.js +2057 -1925
- package/dist/storage/orphan-lock-cleaner.d.ts +15 -0
- package/dist/storage/session-recovery.d.ts +9 -0
- package/dist/storage/session-store/rename-session.d.ts +16 -0
- package/dist/storage/session-store/resume-session.d.ts +26 -0
- package/dist/storage/session-store/session-store-clear.d.ts +11 -0
- package/dist/storage/session-store/session-store-index.d.ts +13 -0
- package/dist/storage/session-store.d.ts +1 -129
- package/dist/storage/session-summary-tracker.d.ts +39 -0
- package/dist/storage/session-write-buffer.d.ts +43 -0
- package/dist/storage/session-writer/session-writer-flush.d.ts +4 -0
- package/dist/storage/session-writer/session-writer-summary-tracker.d.ts +28 -0
- package/dist/tools/fallback-agent-model-assign-tool.d.ts +18 -0
- package/dist/tools/fallback-leader-model-set-tool.d.ts +18 -0
- package/dist/tools/fallback-manage-tools.d.ts +8 -45
- package/dist/tools/fallback-profile-manage-tool.d.ts +16 -0
- package/dist/tools/fallback-provider-key-set-tool.d.ts +17 -0
- package/dist/tools/fallback-provider-manage-tool.d.ts +26 -0
- package/dist/tools/index.js +5070 -5071
- package/dist/types/config/root.d.ts +7 -2
- package/dist/types/default-config.d.ts +1 -1
- package/dist/types/index.d.ts +54 -55
- package/dist/types/index.js +953 -932
- package/dist/types/runtime-capability-manifest.d.ts +5 -5
- package/dist/types/session-markers.d.ts +12 -0
- package/dist/types/session-markers.js +19 -0
- package/dist/worktree/worktree-manager.d.ts +1 -1
- package/instructions/coordination/subagent-baseline.md +2 -2
- package/instructions/llm/prompt-enhancer.md +2 -1
- package/instructions/sections/tool/common-patterns.md +21 -0
- package/instructions/sections/tool/delegation-compact.md +3 -1
- package/instructions/sections/tool/delegation-full.md +5 -1
- package/instructions/system-lite.md +28 -3
- package/instructions/system-pro.md +37 -8
- package/instructions/system.md +4 -2
- package/package.json +5 -5
package/dist/core/index.js
CHANGED
|
@@ -1158,7 +1158,10 @@ var MAILBOX_TYPE_PROPERTIES = {
|
|
|
1158
1158
|
}
|
|
1159
1159
|
};
|
|
1160
1160
|
|
|
1161
|
-
// src/coordination/mailbox-
|
|
1161
|
+
// src/coordination/mailbox-predicates.ts
|
|
1162
|
+
var SESSION_RECIPIENT_PREFIX = "@session:";
|
|
1163
|
+
|
|
1164
|
+
// src/coordination/mailbox-session-sync.ts
|
|
1162
1165
|
function isAffectedBySessionAffinity(message) {
|
|
1163
1166
|
return message.sessionAffinity !== void 0;
|
|
1164
1167
|
}
|
|
@@ -1189,7 +1192,6 @@ async function acceptMailboxMessageForSession(message, currentSessionId, ctx) {
|
|
|
1189
1192
|
if (ctx?.allowUnscoped === true) return true;
|
|
1190
1193
|
return false;
|
|
1191
1194
|
}
|
|
1192
|
-
var SESSION_RECIPIENT_PREFIX = "@session:";
|
|
1193
1195
|
|
|
1194
1196
|
// src/coordination/sqlite-mailbox.ts
|
|
1195
1197
|
var SQLITE_MAILBOX_FILE = "_mailbox.sqlite";
|
|
@@ -5661,406 +5663,820 @@ function resolveEventSessionId(ctx) {
|
|
|
5661
5663
|
return session?.id ?? "";
|
|
5662
5664
|
}
|
|
5663
5665
|
|
|
5664
|
-
// src/core/
|
|
5665
|
-
function
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
let
|
|
5669
|
-
let
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5666
|
+
// src/core/agent-loop-context.ts
|
|
5667
|
+
function createAgentLoopContextManager(a, handlers) {
|
|
5668
|
+
let _lastEmittedMsgCount = -1;
|
|
5669
|
+
let _lastEmittedToolCount = -1;
|
|
5670
|
+
let _lastEmittedRevision = -1;
|
|
5671
|
+
let _lastEmittedMaxContext = -1;
|
|
5672
|
+
let _lastPreFlightMsgCount = -1;
|
|
5673
|
+
let _lastPreFlightToolCount = -1;
|
|
5674
|
+
let _lastPreFlightRevision = -1;
|
|
5675
|
+
let _lastCompactionMsgCount = -1;
|
|
5676
|
+
let _lastCompactionRevision = -1;
|
|
5677
|
+
let _lastCompactionToolsRef = null;
|
|
5678
|
+
let _lastCompactionSystemRef = null;
|
|
5679
|
+
let _lastCompactionRequestTokens;
|
|
5680
|
+
let _lastCompactionMaxContext = -1;
|
|
5681
|
+
let _lastCompactionWasNoop = false;
|
|
5682
|
+
let _cachedSysRef = null;
|
|
5683
|
+
let _cachedToolsRef = null;
|
|
5684
|
+
let _cachedOverheadTokens = 0;
|
|
5685
|
+
const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
|
|
5686
|
+
function currentMaxContext() {
|
|
5687
|
+
const metaLimit = a.ctx.meta?.["effectiveMaxContext"];
|
|
5688
|
+
const providerMax = a.ctx.provider.capabilities.maxContext;
|
|
5689
|
+
return typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : 2e5;
|
|
5677
5690
|
}
|
|
5678
|
-
|
|
5679
|
-
}
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5691
|
+
async function refreshProviderContextLimit(provider, model, opts = {}) {
|
|
5692
|
+
a.ctx.meta ??= {};
|
|
5693
|
+
const routeKey = `${provider.id}/${model}`;
|
|
5694
|
+
const previousRouteKey = a.ctx.meta["contextLimitRouteKey"];
|
|
5695
|
+
const routeChanged = previousRouteKey !== void 0 && previousRouteKey !== routeKey;
|
|
5696
|
+
const previousMaxContext = currentMaxContext();
|
|
5697
|
+
if (routeChanged) {
|
|
5698
|
+
delete a.ctx.meta["providerOverflowMaxContext"];
|
|
5699
|
+
delete a.ctx.meta["contextLimitBaseline"];
|
|
5700
|
+
delete a.ctx.meta["providerLimitEffectiveMaxContext"];
|
|
5701
|
+
delete a.ctx.meta["effectiveMaxContext"];
|
|
5702
|
+
delete a.ctx.meta["effectiveMaxContextSource"];
|
|
5703
|
+
delete a.ctx.meta["contextLimitLastEmittedMaxContext"];
|
|
5704
|
+
delete a.ctx.meta["contextLimitLastEmittedSource"];
|
|
5705
|
+
}
|
|
5706
|
+
a.ctx.meta["contextLimitRouteKey"] = routeKey;
|
|
5707
|
+
const routeMeta = a.ctx.meta["effectiveMaxContext"];
|
|
5708
|
+
const routeProviderCap = provider.capabilities.maxContext;
|
|
5709
|
+
const currentEffective = typeof routeMeta === "number" && routeMeta > 0 ? routeMeta : typeof routeProviderCap === "number" && routeProviderCap > 0 ? routeProviderCap : 2e5;
|
|
5710
|
+
const emitEffectiveLimit = (effective2, source) => {
|
|
5711
|
+
const lastEmitted = a.ctx.meta["contextLimitLastEmittedMaxContext"];
|
|
5712
|
+
const lastEmittedSource = a.ctx.meta["contextLimitLastEmittedSource"];
|
|
5713
|
+
if (lastEmitted === effective2 && lastEmittedSource === source) return;
|
|
5714
|
+
const eventPrevious = typeof lastEmitted === "number" && Number.isFinite(lastEmitted) && lastEmitted > 0 ? Math.floor(lastEmitted) : previousMaxContext;
|
|
5715
|
+
a.ctx.meta["contextLimitLastEmittedMaxContext"] = effective2;
|
|
5716
|
+
a.ctx.meta["contextLimitLastEmittedSource"] = source;
|
|
5717
|
+
a.events.emit("ctx.max_context", {
|
|
5718
|
+
sessionId: resolveEventSessionId(a.ctx),
|
|
5719
|
+
providerId: provider.id,
|
|
5720
|
+
modelId: model,
|
|
5721
|
+
maxContext: effective2,
|
|
5722
|
+
...eventPrevious > 0 ? { previousMaxContext: eventPrevious } : {},
|
|
5723
|
+
source,
|
|
5724
|
+
decreased: eventPrevious > 0 && effective2 < eventPrevious
|
|
5725
|
+
});
|
|
5707
5726
|
};
|
|
5708
|
-
const
|
|
5709
|
-
if (
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5727
|
+
const emitNativeAfterRouteChange = () => {
|
|
5728
|
+
if (routeChanged && currentEffective > 0) {
|
|
5729
|
+
a.ctx.meta["effectiveMaxContext"] = currentEffective;
|
|
5730
|
+
a.ctx.meta["effectiveMaxContextSource"] = "configured";
|
|
5731
|
+
emitEffectiveLimit(currentEffective, "configured");
|
|
5713
5732
|
}
|
|
5714
5733
|
};
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
grant,
|
|
5720
|
-
deny
|
|
5721
|
-
});
|
|
5722
|
-
if (autoExtend) {
|
|
5723
|
-
setImmediate(() => {
|
|
5724
|
-
if (!resolved) {
|
|
5725
|
-
resolved = true;
|
|
5726
|
-
clearTimeout(timer);
|
|
5727
|
-
resolve10(100);
|
|
5728
|
-
}
|
|
5729
|
-
});
|
|
5734
|
+
const refresh = provider.refreshContextLimit;
|
|
5735
|
+
if (opts.probe === false || !refresh) {
|
|
5736
|
+
emitNativeAfterRouteChange();
|
|
5737
|
+
return;
|
|
5730
5738
|
}
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
(
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
}
|
|
5763
|
-
function hasNextStepsTag(text) {
|
|
5764
|
-
return NEXT_STEPS_TAG_RE.test(text);
|
|
5765
|
-
}
|
|
5766
|
-
function renderNextStepsBlock(steps) {
|
|
5767
|
-
const lines = steps.map((step, i) => {
|
|
5768
|
-
const auto = i === 0 && step.auto === true ? ' auto="true"' : "";
|
|
5769
|
-
return `${i + 1}. ${step.text}${auto}`;
|
|
5770
|
-
});
|
|
5771
|
-
return `<nextsteps>
|
|
5772
|
-
${lines.join("\n")}
|
|
5773
|
-
</nextsteps>`;
|
|
5774
|
-
}
|
|
5775
|
-
function endsTurn(stopReason) {
|
|
5776
|
-
return stopReason !== "tool_use" && stopReason !== "tool_call";
|
|
5777
|
-
}
|
|
5778
|
-
function maybeAppendPendingNextSteps(ctx, res) {
|
|
5779
|
-
if (ctx.agentId !== "leader") return res;
|
|
5780
|
-
if (!endsTurn(res.stopReason)) return res;
|
|
5781
|
-
if (ctx.signal.aborted) return res;
|
|
5782
|
-
const steps = readPendingNextSteps(ctx);
|
|
5783
|
-
if (!steps) return res;
|
|
5784
|
-
if (hasOpenTodos(ctx.todos)) {
|
|
5785
|
-
clearPendingNextSteps(ctx);
|
|
5786
|
-
return res;
|
|
5787
|
-
}
|
|
5788
|
-
const lastTextIndex = res.content.findLastIndex(isTextBlock);
|
|
5789
|
-
if (lastTextIndex >= 0) {
|
|
5790
|
-
const block = res.content[lastTextIndex];
|
|
5791
|
-
if (block && isTextBlock(block) && hasNextStepsTag(block.text)) {
|
|
5792
|
-
clearPendingNextSteps(ctx);
|
|
5793
|
-
return res;
|
|
5739
|
+
let discovered;
|
|
5740
|
+
try {
|
|
5741
|
+
discovered = await refresh.call(provider, model, { signal: a.ctx.signal });
|
|
5742
|
+
} catch {
|
|
5743
|
+
emitNativeAfterRouteChange();
|
|
5744
|
+
return;
|
|
5745
|
+
}
|
|
5746
|
+
const providerLimit = discovered?.maxContext;
|
|
5747
|
+
if (typeof providerLimit !== "number" || !Number.isFinite(providerLimit) || providerLimit <= 0) {
|
|
5748
|
+
emitNativeAfterRouteChange();
|
|
5749
|
+
return;
|
|
5750
|
+
}
|
|
5751
|
+
const providerLimitFloored = Math.floor(providerLimit);
|
|
5752
|
+
const existingBaseline = a.ctx.meta["contextLimitBaseline"];
|
|
5753
|
+
const lastApplied = a.ctx.meta["providerLimitEffectiveMaxContext"];
|
|
5754
|
+
const existingOverflow = a.ctx.meta["providerOverflowMaxContext"];
|
|
5755
|
+
const effectiveChangedOutsideProbe = typeof lastApplied === "number" && Number.isFinite(lastApplied) && currentEffective !== Math.floor(lastApplied) && currentEffective !== existingOverflow;
|
|
5756
|
+
const baseline = effectiveChangedOutsideProbe || typeof existingBaseline !== "number" || !Number.isFinite(existingBaseline) || existingBaseline <= 0 ? currentEffective : Math.floor(existingBaseline);
|
|
5757
|
+
a.ctx.meta["contextLimitBaseline"] = baseline;
|
|
5758
|
+
const overflowLimit = a.ctx.meta["providerOverflowMaxContext"];
|
|
5759
|
+
const overflowFloored = typeof overflowLimit === "number" && Number.isFinite(overflowLimit) && overflowLimit > 0 ? Math.floor(overflowLimit) : void 0;
|
|
5760
|
+
const ceilings = [baseline, providerLimitFloored];
|
|
5761
|
+
if (overflowFloored !== void 0) ceilings.push(overflowFloored);
|
|
5762
|
+
const effective = Math.min(...ceilings.filter((limit) => limit > 0));
|
|
5763
|
+
let effectiveSource;
|
|
5764
|
+
if (overflowFloored !== void 0 && effective === overflowFloored) {
|
|
5765
|
+
effectiveSource = "provider_overflow";
|
|
5766
|
+
} else if (effective === providerLimitFloored) {
|
|
5767
|
+
effectiveSource = "provider";
|
|
5768
|
+
} else {
|
|
5769
|
+
effectiveSource = "configured";
|
|
5794
5770
|
}
|
|
5771
|
+
a.ctx.meta["effectiveMaxContext"] = effective;
|
|
5772
|
+
a.ctx.meta["providerLimitEffectiveMaxContext"] = effective;
|
|
5773
|
+
a.ctx.meta["effectiveMaxContextSource"] = effectiveSource;
|
|
5774
|
+
emitEffectiveLimit(effective, effectiveSource);
|
|
5795
5775
|
}
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5776
|
+
function systemAndToolsOverhead() {
|
|
5777
|
+
const sysRef = a.ctx.systemPrompt;
|
|
5778
|
+
const toolsRef = a.ctx.tools;
|
|
5779
|
+
if (sysRef === _cachedSysRef && toolsRef === _cachedToolsRef && _cachedOverheadTokens > 0) {
|
|
5780
|
+
return _cachedOverheadTokens;
|
|
5781
|
+
}
|
|
5782
|
+
const breakdown = estimateRequestTokens([], sysRef, toolsRef ?? [], calibrationKey());
|
|
5783
|
+
_cachedSysRef = sysRef;
|
|
5784
|
+
_cachedToolsRef = toolsRef;
|
|
5785
|
+
_cachedOverheadTokens = breakdown.systemPrompt + breakdown.tools;
|
|
5786
|
+
return _cachedOverheadTokens;
|
|
5804
5787
|
}
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
const
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
if (
|
|
5835
|
-
|
|
5836
|
-
continue;
|
|
5837
|
-
}
|
|
5838
|
-
if (ch === '"') {
|
|
5839
|
-
inString = false;
|
|
5840
|
-
prevSig = '"';
|
|
5841
|
-
stringBraceDepth = 0;
|
|
5842
|
-
continue;
|
|
5788
|
+
function stashRequestTokens(req) {
|
|
5789
|
+
const preFlight = estimateRequestTokens(
|
|
5790
|
+
req.messages,
|
|
5791
|
+
req.system,
|
|
5792
|
+
req.tools ?? [],
|
|
5793
|
+
calibrationKey(req.model)
|
|
5794
|
+
);
|
|
5795
|
+
a.ctx.lastRequestTokens = preFlight.total;
|
|
5796
|
+
_lastPreFlightMsgCount = req.messages.length;
|
|
5797
|
+
_lastPreFlightToolCount = (req.tools ?? []).length;
|
|
5798
|
+
_lastPreFlightRevision = a.ctx.state.revision;
|
|
5799
|
+
_cachedSysRef = req.system;
|
|
5800
|
+
_cachedToolsRef = req.tools ?? [];
|
|
5801
|
+
_cachedOverheadTokens = preFlight.systemPrompt + preFlight.tools;
|
|
5802
|
+
a.ctx.meta["lastRequestTokensAt"] = {
|
|
5803
|
+
msgCount: req.messages.length,
|
|
5804
|
+
toolCount: (req.tools ?? []).length,
|
|
5805
|
+
revision: a.ctx.state.revision
|
|
5806
|
+
};
|
|
5807
|
+
return preFlight;
|
|
5808
|
+
}
|
|
5809
|
+
function refreshContextRequestTokenStash(opts = {}) {
|
|
5810
|
+
const msgCount = a.ctx.messages.length;
|
|
5811
|
+
const toolCount = (a.ctx.tools ?? []).length;
|
|
5812
|
+
const revision = a.ctx.state.revision;
|
|
5813
|
+
const stashed = a.ctx.lastRequestTokens;
|
|
5814
|
+
const stashedAt = a.ctx.meta?.["lastRequestTokensAt"];
|
|
5815
|
+
if (!opts.force && typeof stashed === "number" && stashed > 0 && typeof stashedAt === "object" && stashedAt !== null) {
|
|
5816
|
+
const meta = stashedAt;
|
|
5817
|
+
if (meta.msgCount === msgCount && meta.toolCount === toolCount && meta.revision === revision && _lastPreFlightRevision === revision) {
|
|
5818
|
+
return stashed;
|
|
5843
5819
|
}
|
|
5844
|
-
if (ch === "{") stringBraceDepth++;
|
|
5845
|
-
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
5846
|
-
continue;
|
|
5847
|
-
}
|
|
5848
|
-
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
5849
|
-
contentEnd = i + 1;
|
|
5850
|
-
if (ch === '"') {
|
|
5851
|
-
inString = true;
|
|
5852
|
-
sawKey = true;
|
|
5853
|
-
stringBraceDepth = 0;
|
|
5854
|
-
prevSig = '"';
|
|
5855
|
-
} else if (ch === "{" || ch === "[") {
|
|
5856
|
-
stack.push(ch);
|
|
5857
|
-
prevSig = ch;
|
|
5858
|
-
} else if (ch === "}" || ch === "]") {
|
|
5859
|
-
stack.pop();
|
|
5860
|
-
prevSig = ch;
|
|
5861
|
-
} else {
|
|
5862
|
-
prevSig = ch;
|
|
5863
5820
|
}
|
|
5821
|
+
const refreshed = estimateMessageTokens(a.ctx.messages) + systemAndToolsOverhead();
|
|
5822
|
+
a.ctx.lastRequestTokens = refreshed;
|
|
5823
|
+
_lastPreFlightMsgCount = msgCount;
|
|
5824
|
+
_lastPreFlightToolCount = toolCount;
|
|
5825
|
+
_lastPreFlightRevision = revision;
|
|
5826
|
+
a.ctx.meta["lastRequestTokensAt"] = { msgCount, toolCount, revision };
|
|
5827
|
+
return refreshed;
|
|
5864
5828
|
}
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5829
|
+
async function compactContextIfNeeded() {
|
|
5830
|
+
const msgCount = a.ctx.messages.length;
|
|
5831
|
+
const maxContext = currentMaxContext();
|
|
5832
|
+
const revision = a.ctx.state.revision;
|
|
5833
|
+
const toolsRef = a.ctx.tools;
|
|
5834
|
+
const systemRef = a.ctx.systemPrompt;
|
|
5835
|
+
const requestTokens = a.ctx.lastRequestTokens;
|
|
5836
|
+
if (_lastCompactionMsgCount === msgCount && _lastCompactionRevision === revision && _lastCompactionToolsRef === toolsRef && _lastCompactionSystemRef === systemRef && _lastCompactionRequestTokens === requestTokens && _lastCompactionWasNoop && _lastCompactionMaxContext === maxContext && maxContext > 0) {
|
|
5837
|
+
return false;
|
|
5872
5838
|
}
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5839
|
+
const beforeMessages = a.ctx.messages;
|
|
5840
|
+
const beforeMsgCount = a.ctx.messages.length;
|
|
5841
|
+
const beforeRevision = a.ctx.state.revision;
|
|
5842
|
+
await a.pipelines.contextWindow.run(a.ctx);
|
|
5843
|
+
_lastCompactionMsgCount = a.ctx.messages.length;
|
|
5844
|
+
_lastCompactionRevision = a.ctx.state.revision;
|
|
5845
|
+
_lastCompactionToolsRef = a.ctx.tools;
|
|
5846
|
+
_lastCompactionSystemRef = a.ctx.systemPrompt;
|
|
5847
|
+
_lastCompactionMaxContext = maxContext;
|
|
5848
|
+
const changed = a.ctx.state.revision !== beforeRevision || a.ctx.messages !== beforeMessages || a.ctx.messages.length !== beforeMsgCount;
|
|
5849
|
+
const tokens = refreshContextRequestTokenStash({ force: changed });
|
|
5850
|
+
_lastCompactionRequestTokens = tokens;
|
|
5851
|
+
const load = maxContext > 0 ? tokens / maxContext : 0;
|
|
5852
|
+
_lastCompactionWasNoop = tokens > 0 && load < 0.5;
|
|
5853
|
+
if (changed) {
|
|
5854
|
+
_lastEmittedMsgCount = -1;
|
|
5855
|
+
_lastEmittedToolCount = -1;
|
|
5856
|
+
_lastEmittedMaxContext = -1;
|
|
5857
|
+
}
|
|
5858
|
+
return changed;
|
|
5877
5859
|
}
|
|
5878
|
-
|
|
5879
|
-
|
|
5860
|
+
async function buildRequestWithPreflightCompaction(opts) {
|
|
5861
|
+
let prepared = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
5862
|
+
let req = prepared.request;
|
|
5863
|
+
let preFlight = stashRequestTokens(req);
|
|
5864
|
+
let probedRouteKey = `${prepared.provider.id}/${req.model}`;
|
|
5865
|
+
await refreshProviderContextLimit(prepared.provider, req.model);
|
|
5866
|
+
if (await compactContextIfNeeded()) {
|
|
5867
|
+
prepared = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
5868
|
+
req = prepared.request;
|
|
5869
|
+
preFlight = stashRequestTokens(req);
|
|
5870
|
+
const rebuiltRouteKey = `${prepared.provider.id}/${req.model}`;
|
|
5871
|
+
if (rebuiltRouteKey !== probedRouteKey) {
|
|
5872
|
+
probedRouteKey = rebuiltRouteKey;
|
|
5873
|
+
await refreshProviderContextLimit(prepared.provider, req.model);
|
|
5874
|
+
if (await compactContextIfNeeded()) {
|
|
5875
|
+
prepared = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
5876
|
+
req = prepared.request;
|
|
5877
|
+
preFlight = stashRequestTokens(req);
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
return { req, provider: prepared.provider, preFlight };
|
|
5880
5882
|
}
|
|
5881
|
-
|
|
5882
|
-
const
|
|
5883
|
-
|
|
5883
|
+
function emitContextPct() {
|
|
5884
|
+
const msgCount = a.ctx.messages.length;
|
|
5885
|
+
const toolCount = (a.ctx.tools ?? []).length;
|
|
5886
|
+
const maxContext = currentMaxContext();
|
|
5887
|
+
const revision = a.ctx.state.revision;
|
|
5888
|
+
if (msgCount === _lastEmittedMsgCount && toolCount === _lastEmittedToolCount && revision === _lastEmittedRevision && maxContext === _lastEmittedMaxContext && maxContext > 0) {
|
|
5889
|
+
return;
|
|
5890
|
+
}
|
|
5891
|
+
_lastEmittedMsgCount = msgCount;
|
|
5892
|
+
_lastEmittedToolCount = toolCount;
|
|
5893
|
+
_lastEmittedRevision = revision;
|
|
5894
|
+
_lastEmittedMaxContext = maxContext;
|
|
5895
|
+
if (msgCount !== _lastPreFlightMsgCount || toolCount !== _lastPreFlightToolCount || revision !== _lastPreFlightRevision) {
|
|
5896
|
+
refreshContextRequestTokenStash({ force: true });
|
|
5897
|
+
}
|
|
5898
|
+
let total;
|
|
5899
|
+
const anchored = realAnchoredInputTokens(
|
|
5900
|
+
a.ctx.messages,
|
|
5901
|
+
a.ctx.lastRealInputTokens,
|
|
5902
|
+
typeof a.ctx.meta?.["realAnchorMsgCount"] === "number" ? a.ctx.meta["realAnchorMsgCount"] : void 0
|
|
5903
|
+
);
|
|
5904
|
+
const stashed = a.ctx.lastRequestTokens;
|
|
5905
|
+
if (anchored !== null) {
|
|
5906
|
+
total = anchored;
|
|
5907
|
+
} else if (typeof stashed === "number" && stashed > 0) {
|
|
5908
|
+
const cal = getCalibrationState(calibrationKey());
|
|
5909
|
+
total = cal.calibrated ? Math.round(stashed * Math.min(1.5, Math.max(0.5, cal.ratio))) : stashed;
|
|
5910
|
+
} else {
|
|
5911
|
+
const raw = refreshContextRequestTokenStash({ force: true });
|
|
5912
|
+
const cal = getCalibrationState(calibrationKey());
|
|
5913
|
+
total = cal.calibrated ? Math.round(raw * Math.min(1.5, Math.max(0.5, cal.ratio))) : raw;
|
|
5914
|
+
}
|
|
5915
|
+
const rawLoad = maxContext > 0 ? total / maxContext : 0;
|
|
5916
|
+
const load = Math.max(0, Math.min(1, rawLoad));
|
|
5917
|
+
a.events.emit("ctx.pct", {
|
|
5918
|
+
sessionId: resolveEventSessionId(a.ctx),
|
|
5919
|
+
load,
|
|
5920
|
+
rawLoad,
|
|
5921
|
+
tokens: total,
|
|
5922
|
+
maxContext
|
|
5923
|
+
});
|
|
5884
5924
|
}
|
|
5885
|
-
return
|
|
5925
|
+
return {
|
|
5926
|
+
refreshProviderContextLimit,
|
|
5927
|
+
compactContextIfNeeded,
|
|
5928
|
+
stashRequestTokens,
|
|
5929
|
+
refreshContextRequestTokenStash,
|
|
5930
|
+
buildRequestWithPreflightCompaction,
|
|
5931
|
+
emitContextPct,
|
|
5932
|
+
currentMaxContext,
|
|
5933
|
+
calibrationKey,
|
|
5934
|
+
get lastPreFlightMsgCount() {
|
|
5935
|
+
return _lastPreFlightMsgCount;
|
|
5936
|
+
}
|
|
5937
|
+
};
|
|
5886
5938
|
}
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5939
|
+
|
|
5940
|
+
// src/core/agent-loop-detector.ts
|
|
5941
|
+
function iterationFingerprint(blocks) {
|
|
5942
|
+
const toolUses = blocks.filter(isToolUseBlock);
|
|
5943
|
+
const texts = blocks.filter(isTextBlock);
|
|
5944
|
+
const toolNameSet = Array.from(new Set(toolUses.map((u) => u.name))).sort();
|
|
5945
|
+
const firstInputHash = toolUses[0] ? hashSmall(stableStringify(toolUses[0].input ?? {})) : "";
|
|
5946
|
+
const textBlob = texts.map((t2) => t2.text).join("").slice(0, 512);
|
|
5947
|
+
const hasContent = toolNameSet.length > 0 || textBlob.length > 0;
|
|
5948
|
+
if (!hasContent) return "__empty__";
|
|
5949
|
+
return [
|
|
5950
|
+
`tools=${toolNameSet.join("+") || "-"}`,
|
|
5951
|
+
`in0=${firstInputHash}`,
|
|
5952
|
+
`txt=${textBlob}`
|
|
5953
|
+
].join("\n");
|
|
5895
5954
|
}
|
|
5896
|
-
function
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5955
|
+
function stableStringify(value) {
|
|
5956
|
+
return JSON.stringify(canonicalize(value));
|
|
5957
|
+
}
|
|
5958
|
+
function canonicalize(value) {
|
|
5959
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
5960
|
+
if (value && typeof value === "object") {
|
|
5961
|
+
const src = value;
|
|
5962
|
+
const out = {};
|
|
5963
|
+
for (const k of Object.keys(src).sort()) out[k] = canonicalize(src[k]);
|
|
5964
|
+
return out;
|
|
5901
5965
|
}
|
|
5966
|
+
return value;
|
|
5902
5967
|
}
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5968
|
+
function hashSmall(s) {
|
|
5969
|
+
let h = 2166136261;
|
|
5970
|
+
for (let i = 0; i < s.length; i++) {
|
|
5971
|
+
h ^= s.charCodeAt(i);
|
|
5972
|
+
h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
|
|
5973
|
+
}
|
|
5974
|
+
return h.toString(36);
|
|
5975
|
+
}
|
|
5976
|
+
var AgentLoopDetector = class {
|
|
5977
|
+
constructor(a) {
|
|
5978
|
+
this.a = a;
|
|
5979
|
+
}
|
|
5980
|
+
a;
|
|
5981
|
+
lastToolSignature = "";
|
|
5982
|
+
toolLoopCount = 0;
|
|
5983
|
+
iterationSteerDone = false;
|
|
5984
|
+
recentCallKeys = [];
|
|
5985
|
+
steeredCallKeys = /* @__PURE__ */ new Set();
|
|
5986
|
+
checkIteration(iterationIndex, content, toolUses, queueSteer) {
|
|
5987
|
+
const loopCfg = this.a.loopDetection;
|
|
5988
|
+
if (loopCfg.mode === "off") return { cut: false };
|
|
5989
|
+
const sig = iterationFingerprint(content);
|
|
5990
|
+
if (sig !== "__empty__") {
|
|
5991
|
+
if (sig === this.lastToolSignature) {
|
|
5992
|
+
this.toolLoopCount++;
|
|
5993
|
+
} else {
|
|
5994
|
+
this.lastToolSignature = sig;
|
|
5995
|
+
this.toolLoopCount = 1;
|
|
5996
|
+
this.iterationSteerDone = false;
|
|
5997
|
+
}
|
|
5998
|
+
const names = toolUses.map((t2) => t2.name).join(", ");
|
|
5999
|
+
const hasText2 = content.some(isTextBlock);
|
|
6000
|
+
const kind = toolUses.length > 0 && hasText2 ? "mixed" : toolUses.length > 0 ? "tool" : "message";
|
|
6001
|
+
const observationRepeat = loopCfg.mode === "steer-then-cut" && toolUses.length > 0 && toolUses.every((use) => {
|
|
6002
|
+
const tool = this.a.tools.get(use.name);
|
|
6003
|
+
return tool?.mutating === false && (tool.riskTier === "safe" || (tool.capabilities?.length ?? 0) > 0);
|
|
6004
|
+
});
|
|
6005
|
+
const repeatMultiplier = observationRepeat ? 2 : 1;
|
|
6006
|
+
const detail = kind === "tool" ? `"${names}" called with effectively identical inputs ${this.toolLoopCount} times in a row` : kind === "mixed" ? `"${names}" + same text repeated ${this.toolLoopCount} times in a row` : `same assistant text repeated ${this.toolLoopCount} times in a row`;
|
|
6007
|
+
const cutAt = (loopCfg.mode === "cut" ? loopCfg.steerThreshold : loopCfg.cutThreshold) * repeatMultiplier;
|
|
6008
|
+
if (this.toolLoopCount >= cutAt) {
|
|
6009
|
+
this.a.logger.warn(`Loop detected: ${detail} \u2014 stopping to prevent infinite loop.`);
|
|
6010
|
+
this.a.events.emit("tool.loop_detected", {
|
|
6011
|
+
sessionId: resolveEventSessionId(this.a.ctx),
|
|
6012
|
+
ctx: this.a.ctx,
|
|
6013
|
+
tools: names,
|
|
6014
|
+
repeatCount: this.toolLoopCount,
|
|
6015
|
+
iteration: iterationIndex,
|
|
6016
|
+
kind,
|
|
6017
|
+
action: "cut",
|
|
6018
|
+
scope: "iteration"
|
|
6019
|
+
});
|
|
6020
|
+
const summary = kind === "message" ? `[Loop detected: same assistant message repeated ${this.toolLoopCount}\xD7 \u2014 stopping to prevent infinite repetition.]` : `[Loop detected: ${detail} \u2014 stopping to prevent infinite repetition.]`;
|
|
6021
|
+
return { cut: true, cutSummary: summary };
|
|
6022
|
+
}
|
|
6023
|
+
if (loopCfg.mode === "steer-then-cut" && this.toolLoopCount >= loopCfg.steerThreshold * repeatMultiplier && !this.iterationSteerDone) {
|
|
6024
|
+
this.iterationSteerDone = true;
|
|
6025
|
+
this.a.logger.warn(`Loop detected: ${detail} \u2014 steering the model to change approach.`);
|
|
6026
|
+
this.a.events.emit("tool.loop_detected", {
|
|
6027
|
+
sessionId: resolveEventSessionId(this.a.ctx),
|
|
6028
|
+
ctx: this.a.ctx,
|
|
6029
|
+
tools: names,
|
|
6030
|
+
repeatCount: this.toolLoopCount,
|
|
6031
|
+
iteration: iterationIndex,
|
|
6032
|
+
kind,
|
|
6033
|
+
action: "steer",
|
|
6034
|
+
scope: "iteration"
|
|
6035
|
+
});
|
|
6036
|
+
queueSteer(
|
|
6037
|
+
`[loop-detector] Your last ${this.toolLoopCount} responses were effectively identical (${detail}). This approach is not working. Change strategy: use a different tool, different arguments, or a different plan \u2014 or explain what is blocking you and stop. Repeating the same response ${cutAt - this.toolLoopCount} more time(s) will terminate the turn.`
|
|
6038
|
+
);
|
|
5920
6039
|
}
|
|
5921
|
-
content.push(block);
|
|
5922
6040
|
} else {
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
if (
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
6041
|
+
this.lastToolSignature = "";
|
|
6042
|
+
this.toolLoopCount = 0;
|
|
6043
|
+
this.iterationSteerDone = false;
|
|
6044
|
+
}
|
|
6045
|
+
if (loopCfg.mode === "steer-then-cut") {
|
|
6046
|
+
for (const u of toolUses) {
|
|
6047
|
+
const key = `${u.name}:${hashSmall(stableStringify(u.input ?? {}))}`;
|
|
6048
|
+
this.recentCallKeys.push(key);
|
|
6049
|
+
if (this.recentCallKeys.length > loopCfg.windowSize) this.recentCallKeys.shift();
|
|
6050
|
+
if (this.steeredCallKeys.has(key)) continue;
|
|
6051
|
+
let count = 0;
|
|
6052
|
+
for (const k of this.recentCallKeys) if (k === key) count++;
|
|
6053
|
+
const tool = this.a.tools.get(u.name);
|
|
6054
|
+
const observationThreshold = tool?.mutating === false && (tool.riskTier === "safe" || (tool.capabilities?.length ?? 0) > 0) ? loopCfg.callRepeatThreshold * 2 : loopCfg.callRepeatThreshold;
|
|
6055
|
+
if (count < observationThreshold) continue;
|
|
6056
|
+
this.steeredCallKeys.add(key);
|
|
6057
|
+
const preview = JSON.stringify(u.input ?? {}).slice(0, 160);
|
|
6058
|
+
this.a.logger.warn(
|
|
6059
|
+
`Loop detected: "${u.name}" called with identical arguments ${count}\xD7 within the last ${loopCfg.windowSize} tool calls \u2014 steering the model to change approach.`
|
|
6060
|
+
);
|
|
6061
|
+
this.a.events.emit("tool.loop_detected", {
|
|
6062
|
+
sessionId: resolveEventSessionId(this.a.ctx),
|
|
6063
|
+
ctx: this.a.ctx,
|
|
6064
|
+
tools: u.name,
|
|
6065
|
+
repeatCount: count,
|
|
6066
|
+
iteration: iterationIndex,
|
|
6067
|
+
kind: "tool",
|
|
6068
|
+
action: "steer",
|
|
6069
|
+
scope: "call"
|
|
6070
|
+
});
|
|
6071
|
+
queueSteer(
|
|
6072
|
+
`[loop-detector] You have called ${u.name}(${preview}) ${count} times with identical arguments within the last ${loopCfg.windowSize} tool calls. The result will not change. Do not repeat this call \u2014 use what you already know, try a different approach, or explain the blocker.`
|
|
6073
|
+
);
|
|
6074
|
+
}
|
|
5936
6075
|
}
|
|
6076
|
+
return { cut: false };
|
|
5937
6077
|
}
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
function
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
6078
|
+
};
|
|
6079
|
+
|
|
6080
|
+
// src/core/continue-to-next-iteration.ts
|
|
6081
|
+
function parseContinueDirective(text) {
|
|
6082
|
+
const LINE_MARKERS = /^\s*\[(continue|next step|proceed|done)\]\s*$/gim;
|
|
6083
|
+
const tail = text.length <= DIRECTIVE_SCAN_WINDOW ? text : text.slice(text.length - DIRECTIVE_SCAN_WINDOW);
|
|
6084
|
+
let match;
|
|
6085
|
+
let lastDirective = "none";
|
|
6086
|
+
while ((match = LINE_MARKERS.exec(tail)) !== null) {
|
|
6087
|
+
const value = (match[1] ?? "").toLowerCase();
|
|
6088
|
+
if (value === "continue" || value === "next step" || value === "proceed") {
|
|
6089
|
+
lastDirective = "continue";
|
|
6090
|
+
} else if (value === "done") {
|
|
6091
|
+
lastDirective = "stop";
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
return lastDirective;
|
|
5953
6095
|
}
|
|
5954
|
-
|
|
5955
|
-
|
|
6096
|
+
var DIRECTIVE_SCAN_WINDOW = 2048;
|
|
6097
|
+
var META_KEY2 = "_autonomousContinue";
|
|
6098
|
+
function consumeAutonomousContinue(ctx) {
|
|
6099
|
+
const val = ctx.meta[META_KEY2] === true;
|
|
6100
|
+
delete ctx.meta[META_KEY2];
|
|
6101
|
+
return val;
|
|
5956
6102
|
}
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
6103
|
+
|
|
6104
|
+
// src/core/iteration-limit.ts
|
|
6105
|
+
function requestLimitExtension(opts) {
|
|
6106
|
+
const { events, sessionId, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
6107
|
+
return new Promise((resolve10) => {
|
|
6108
|
+
let resolved = false;
|
|
6109
|
+
const timerFired = () => {
|
|
6110
|
+
if (!resolved) {
|
|
6111
|
+
resolved = true;
|
|
6112
|
+
resolve10(0);
|
|
6113
|
+
}
|
|
6114
|
+
};
|
|
6115
|
+
const timer = setTimeout(timerFired, timeoutMs);
|
|
6116
|
+
timer.unref();
|
|
6117
|
+
const deny = () => {
|
|
6118
|
+
if (!resolved) {
|
|
6119
|
+
resolved = true;
|
|
6120
|
+
clearTimeout(timer);
|
|
6121
|
+
resolve10(0);
|
|
6122
|
+
}
|
|
6123
|
+
};
|
|
6124
|
+
const grant = (extra) => {
|
|
6125
|
+
if (!resolved) {
|
|
6126
|
+
resolved = true;
|
|
6127
|
+
clearTimeout(timer);
|
|
6128
|
+
resolve10(Math.max(0, extra));
|
|
6129
|
+
}
|
|
6130
|
+
};
|
|
6131
|
+
events.emit("iteration.limit_reached", {
|
|
6132
|
+
sessionId,
|
|
6133
|
+
currentIterations,
|
|
6134
|
+
currentLimit,
|
|
6135
|
+
grant,
|
|
6136
|
+
deny
|
|
5973
6137
|
});
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
6138
|
+
if (autoExtend) {
|
|
6139
|
+
setImmediate(() => {
|
|
6140
|
+
if (!resolved) {
|
|
6141
|
+
resolved = true;
|
|
6142
|
+
clearTimeout(timer);
|
|
6143
|
+
resolve10(100);
|
|
6144
|
+
}
|
|
6145
|
+
});
|
|
6146
|
+
}
|
|
6147
|
+
});
|
|
5981
6148
|
}
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
6149
|
+
|
|
6150
|
+
// src/core/next-steps-slot.ts
|
|
6151
|
+
var SLOT_KEY = "nextsteps.pending";
|
|
6152
|
+
var MAX_PENDING_NEXT_STEPS = 4;
|
|
6153
|
+
var NEXT_STEPS_TAG_RE = /<nextsteps\b[^>]*>/i;
|
|
6154
|
+
function writePendingNextSteps(ctx, steps) {
|
|
6155
|
+
const cleaned = [];
|
|
6156
|
+
for (const step of steps) {
|
|
6157
|
+
const text = typeof step?.text === "string" ? step.text.replace(/\s+/g, " ").trim() : "";
|
|
6158
|
+
if (!text) continue;
|
|
6159
|
+
cleaned.push(step.auto === true && cleaned.length === 0 ? { text, auto: true } : { text });
|
|
6160
|
+
if (cleaned.length >= MAX_PENDING_NEXT_STEPS) break;
|
|
5987
6161
|
}
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
state.blockOrder.push({ kind: "tool", id: ev.id });
|
|
6162
|
+
if (cleaned.length === 0) {
|
|
6163
|
+
clearPendingNextSteps(ctx);
|
|
6164
|
+
return;
|
|
6165
|
+
}
|
|
6166
|
+
ctx.meta[SLOT_KEY] = cleaned;
|
|
5994
6167
|
}
|
|
5995
|
-
function
|
|
5996
|
-
const
|
|
5997
|
-
if (
|
|
6168
|
+
function readPendingNextSteps(ctx) {
|
|
6169
|
+
const raw = ctx.meta[SLOT_KEY];
|
|
6170
|
+
if (!Array.isArray(raw) || raw.length === 0) return void 0;
|
|
6171
|
+
const steps = raw.filter(
|
|
6172
|
+
(s) => typeof s?.text === "string" && s.text.length > 0
|
|
6173
|
+
);
|
|
6174
|
+
return steps.length > 0 ? steps : void 0;
|
|
5998
6175
|
}
|
|
5999
|
-
function
|
|
6000
|
-
|
|
6001
|
-
try {
|
|
6002
|
-
return JSON.parse(s);
|
|
6003
|
-
} catch {
|
|
6004
|
-
const repaired = completePartialObject(s);
|
|
6005
|
-
try {
|
|
6006
|
-
return JSON.parse(repaired);
|
|
6007
|
-
} catch {
|
|
6008
|
-
return { _raw: repaired };
|
|
6009
|
-
}
|
|
6010
|
-
}
|
|
6176
|
+
function clearPendingNextSteps(ctx) {
|
|
6177
|
+
delete ctx.meta[SLOT_KEY];
|
|
6011
6178
|
}
|
|
6012
|
-
function
|
|
6013
|
-
|
|
6014
|
-
if (t2) {
|
|
6015
|
-
t2.input = ev.input !== void 0 ? ev.input : safeJsonOrRaw(t2.partial);
|
|
6016
|
-
if (ev.providerMeta) t2.providerMeta = ev.providerMeta;
|
|
6017
|
-
}
|
|
6018
|
-
state.currentTextIndex = -1;
|
|
6179
|
+
function hasNextStepsTag(text) {
|
|
6180
|
+
return NEXT_STEPS_TAG_RE.test(text);
|
|
6019
6181
|
}
|
|
6020
|
-
function
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
...ev.providerMeta ? { providerMeta: ev.providerMeta } : {}
|
|
6182
|
+
function renderNextStepsBlock(steps) {
|
|
6183
|
+
const lines = steps.map((step, i) => {
|
|
6184
|
+
const auto = i === 0 && step.auto === true ? ' auto="true"' : "";
|
|
6185
|
+
return `${i + 1}. ${step.text}${auto}`;
|
|
6025
6186
|
});
|
|
6026
|
-
|
|
6027
|
-
|
|
6187
|
+
return `<nextsteps>
|
|
6188
|
+
${lines.join("\n")}
|
|
6189
|
+
</nextsteps>`;
|
|
6028
6190
|
}
|
|
6029
|
-
function
|
|
6030
|
-
|
|
6031
|
-
handleThinkingStart(state, {});
|
|
6032
|
-
}
|
|
6033
|
-
const t2 = state.thinking[state.currentThinkingIndex];
|
|
6034
|
-
if (t2) t2.textBuf += text;
|
|
6191
|
+
function endsTurn(stopReason) {
|
|
6192
|
+
return stopReason !== "tool_use" && stopReason !== "tool_call";
|
|
6035
6193
|
}
|
|
6036
|
-
function
|
|
6037
|
-
if (
|
|
6038
|
-
|
|
6194
|
+
function maybeAppendPendingNextSteps(ctx, res) {
|
|
6195
|
+
if (ctx.agentId !== "leader") return res;
|
|
6196
|
+
if (!endsTurn(res.stopReason)) return res;
|
|
6197
|
+
if (ctx.signal.aborted) return res;
|
|
6198
|
+
const steps = readPendingNextSteps(ctx);
|
|
6199
|
+
if (!steps) return res;
|
|
6200
|
+
if (hasOpenTodos(ctx.todos)) {
|
|
6201
|
+
clearPendingNextSteps(ctx);
|
|
6202
|
+
return res;
|
|
6039
6203
|
}
|
|
6040
|
-
const
|
|
6041
|
-
if (
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6204
|
+
const lastTextIndex = res.content.findLastIndex(isTextBlock);
|
|
6205
|
+
if (lastTextIndex >= 0) {
|
|
6206
|
+
const block = res.content[lastTextIndex];
|
|
6207
|
+
if (block && isTextBlock(block) && hasNextStepsTag(block.text)) {
|
|
6208
|
+
clearPendingNextSteps(ctx);
|
|
6209
|
+
return res;
|
|
6210
|
+
}
|
|
6211
|
+
}
|
|
6212
|
+
const rendered = renderNextStepsBlock(steps);
|
|
6213
|
+
const content = [...res.content];
|
|
6214
|
+
const target = lastTextIndex >= 0 ? content[lastTextIndex] : void 0;
|
|
6215
|
+
if (target && isTextBlock(target)) {
|
|
6216
|
+
const separator = target.text.trim() ? "\n\n" : "";
|
|
6217
|
+
content[lastTextIndex] = { ...target, text: `${target.text}${separator}${rendered}` };
|
|
6218
|
+
} else {
|
|
6219
|
+
content.push({ type: "text", text: rendered });
|
|
6220
|
+
}
|
|
6221
|
+
clearPendingNextSteps(ctx);
|
|
6222
|
+
return { ...res, content };
|
|
6045
6223
|
}
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6224
|
+
|
|
6225
|
+
// src/core/streaming-response-builder.ts
|
|
6226
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
6227
|
+
|
|
6228
|
+
// src/utils/json-repair.ts
|
|
6229
|
+
function completePartialObject(s) {
|
|
6230
|
+
if (!s.trim().startsWith("{")) return s;
|
|
6231
|
+
if (tryParse(s).ok) return s;
|
|
6232
|
+
return repairTruncated(s);
|
|
6049
6233
|
}
|
|
6050
|
-
|
|
6051
|
-
const
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
let
|
|
6056
|
-
let
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6234
|
+
function repairTruncated(s) {
|
|
6235
|
+
const stack = [];
|
|
6236
|
+
let inString = false;
|
|
6237
|
+
let escaped = false;
|
|
6238
|
+
let sawKey = false;
|
|
6239
|
+
let prevSig = "";
|
|
6240
|
+
let contentEnd = 0;
|
|
6241
|
+
let stringBraceDepth = 0;
|
|
6242
|
+
for (let i = 0; i < s.length; i++) {
|
|
6243
|
+
const ch = expectDefined(s[i]);
|
|
6244
|
+
if (inString) {
|
|
6245
|
+
contentEnd = i + 1;
|
|
6246
|
+
if (escaped) {
|
|
6247
|
+
escaped = false;
|
|
6248
|
+
continue;
|
|
6249
|
+
}
|
|
6250
|
+
if (ch === "\\") {
|
|
6251
|
+
escaped = true;
|
|
6252
|
+
continue;
|
|
6253
|
+
}
|
|
6254
|
+
if (ch === '"') {
|
|
6255
|
+
inString = false;
|
|
6256
|
+
prevSig = '"';
|
|
6257
|
+
stringBraceDepth = 0;
|
|
6258
|
+
continue;
|
|
6259
|
+
}
|
|
6260
|
+
if (ch === "{") stringBraceDepth++;
|
|
6261
|
+
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
6262
|
+
continue;
|
|
6263
|
+
}
|
|
6264
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
6265
|
+
contentEnd = i + 1;
|
|
6266
|
+
if (ch === '"') {
|
|
6267
|
+
inString = true;
|
|
6268
|
+
sawKey = true;
|
|
6269
|
+
stringBraceDepth = 0;
|
|
6270
|
+
prevSig = '"';
|
|
6271
|
+
} else if (ch === "{" || ch === "[") {
|
|
6272
|
+
stack.push(ch);
|
|
6273
|
+
prevSig = ch;
|
|
6274
|
+
} else if (ch === "}" || ch === "]") {
|
|
6275
|
+
stack.pop();
|
|
6276
|
+
prevSig = ch;
|
|
6277
|
+
} else {
|
|
6278
|
+
prevSig = ch;
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
if (!sawKey && !inString) return s;
|
|
6282
|
+
let result = s.slice(0, contentEnd);
|
|
6283
|
+
if (inString) {
|
|
6284
|
+
if (escaped) {
|
|
6285
|
+
result = result.slice(0, -1);
|
|
6286
|
+
} else if (endsWithInvalidEscape(result)) {
|
|
6287
|
+
result = result.slice(0, -2);
|
|
6288
|
+
}
|
|
6289
|
+
if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
|
|
6290
|
+
result += '"';
|
|
6291
|
+
} else if (prevSig === ":") {
|
|
6292
|
+
result += "null";
|
|
6293
|
+
}
|
|
6294
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
6295
|
+
result += stack[k] === "{" ? "}" : "]";
|
|
6296
|
+
}
|
|
6297
|
+
if (!tryParse(result).ok) {
|
|
6298
|
+
const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
|
|
6299
|
+
if (tryParse(patched).ok) result = patched;
|
|
6300
|
+
}
|
|
6301
|
+
return result;
|
|
6302
|
+
}
|
|
6303
|
+
var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
|
|
6304
|
+
function endsWithInvalidEscape(str) {
|
|
6305
|
+
const last = str[str.length - 1];
|
|
6306
|
+
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
6307
|
+
if (VALID_ESCAPE.has(last)) return false;
|
|
6308
|
+
let backslashes = 0;
|
|
6309
|
+
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
6310
|
+
return backslashes % 2 === 1;
|
|
6311
|
+
}
|
|
6312
|
+
function tryParse(s) {
|
|
6313
|
+
try {
|
|
6314
|
+
return { ok: true, value: JSON.parse(s) };
|
|
6315
|
+
} catch {
|
|
6316
|
+
return { ok: false };
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
|
|
6320
|
+
// src/core/streaming-response-builder.ts
|
|
6321
|
+
var STREAM_DRAIN_TIMEOUT_MS = 500;
|
|
6322
|
+
function buildResponse(state) {
|
|
6323
|
+
const content = [];
|
|
6324
|
+
for (const b of state.blockOrder) {
|
|
6325
|
+
if (b.kind === "text") {
|
|
6326
|
+
const txt = state.textBuffers[b.idx] ?? "";
|
|
6327
|
+
if (txt) content.push({ type: "text", text: txt });
|
|
6328
|
+
} else if (b.kind === "thinking") {
|
|
6329
|
+
const t2 = state.thinking[b.idx];
|
|
6330
|
+
if (!t2) continue;
|
|
6331
|
+
if (!t2.textBuf && !t2.signature) continue;
|
|
6332
|
+
const block = { type: "thinking", thinking: t2.textBuf };
|
|
6333
|
+
if (t2.signature) block.signature = t2.signature;
|
|
6334
|
+
if (t2.providerMeta && Object.keys(t2.providerMeta).length > 0) {
|
|
6335
|
+
block.providerMeta = t2.providerMeta;
|
|
6336
|
+
}
|
|
6337
|
+
content.push(block);
|
|
6338
|
+
} else {
|
|
6339
|
+
const tb = state.tools.get(b.id);
|
|
6340
|
+
if (tb) {
|
|
6341
|
+
const block = {
|
|
6342
|
+
type: "tool_use",
|
|
6343
|
+
id: b.id,
|
|
6344
|
+
name: tb.name,
|
|
6345
|
+
input: tb.input ?? {}
|
|
6346
|
+
};
|
|
6347
|
+
if (tb.providerMeta && Object.keys(tb.providerMeta).length > 0) {
|
|
6348
|
+
block.providerMeta = tb.providerMeta;
|
|
6349
|
+
}
|
|
6350
|
+
content.push(block);
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
if (content.length === 0) content.push({ type: "text", text: "" });
|
|
6355
|
+
return { content, stopReason: state.stopReason, usage: state.usage, model: state.model };
|
|
6356
|
+
}
|
|
6357
|
+
function createStreamingState(model) {
|
|
6358
|
+
return {
|
|
6359
|
+
model,
|
|
6360
|
+
stopReason: "end_turn",
|
|
6361
|
+
usage: { input: 0, output: 0 },
|
|
6362
|
+
textBuffers: [],
|
|
6363
|
+
currentTextIndex: -1,
|
|
6364
|
+
tools: /* @__PURE__ */ new Map(),
|
|
6365
|
+
thinking: [],
|
|
6366
|
+
currentThinkingIndex: -1,
|
|
6367
|
+
blockOrder: []
|
|
6368
|
+
};
|
|
6369
|
+
}
|
|
6370
|
+
function handleMessageStart(state, model) {
|
|
6371
|
+
state.model = model;
|
|
6372
|
+
}
|
|
6373
|
+
function handleContentBlockStart(state, ev) {
|
|
6374
|
+
const kind = ev.kind ?? "text";
|
|
6375
|
+
if (kind === "text") {
|
|
6376
|
+
state.currentTextIndex = state.textBuffers.length;
|
|
6377
|
+
state.textBuffers.push("");
|
|
6378
|
+
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
6379
|
+
} else if (kind === "tool_use") {
|
|
6380
|
+
const id = ev.id ?? randomUUID4();
|
|
6381
|
+
state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
|
|
6382
|
+
state.blockOrder.push({ kind: "tool", id });
|
|
6383
|
+
state.currentTextIndex = -1;
|
|
6384
|
+
} else if (kind === "thinking") {
|
|
6385
|
+
state.currentThinkingIndex = state.thinking.length;
|
|
6386
|
+
state.thinking.push({
|
|
6387
|
+
textBuf: "",
|
|
6388
|
+
...ev.providerMeta ? { providerMeta: ev.providerMeta } : {}
|
|
6389
|
+
});
|
|
6390
|
+
state.blockOrder.push({ kind: "thinking", idx: state.currentThinkingIndex });
|
|
6391
|
+
state.currentTextIndex = -1;
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
function handleContentBlockStop(state, ev) {
|
|
6395
|
+
void state;
|
|
6396
|
+
void ev;
|
|
6397
|
+
}
|
|
6398
|
+
function handleTextDelta(state, text) {
|
|
6399
|
+
if (state.currentTextIndex === -1) {
|
|
6400
|
+
state.currentTextIndex = state.textBuffers.length;
|
|
6401
|
+
state.textBuffers.push("");
|
|
6402
|
+
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
6403
|
+
}
|
|
6404
|
+
state.textBuffers[state.currentTextIndex] = (state.textBuffers[state.currentTextIndex] ?? "") + text;
|
|
6405
|
+
}
|
|
6406
|
+
function handleToolUseStart(state, ev) {
|
|
6407
|
+
state.currentTextIndex = -1;
|
|
6408
|
+
state.tools.set(ev.id, { name: ev.name, partial: "" });
|
|
6409
|
+
state.blockOrder.push({ kind: "tool", id: ev.id });
|
|
6410
|
+
}
|
|
6411
|
+
function handleToolUseInputDelta(state, ev) {
|
|
6412
|
+
const t2 = state.tools.get(ev.id);
|
|
6413
|
+
if (t2) t2.partial += ev.partial;
|
|
6414
|
+
}
|
|
6415
|
+
function safeJsonOrRaw(s) {
|
|
6416
|
+
if (!s) return {};
|
|
6417
|
+
try {
|
|
6418
|
+
return JSON.parse(s);
|
|
6419
|
+
} catch {
|
|
6420
|
+
const repaired = completePartialObject(s);
|
|
6421
|
+
try {
|
|
6422
|
+
return JSON.parse(repaired);
|
|
6423
|
+
} catch {
|
|
6424
|
+
return { _raw: repaired };
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
function handleToolUseStop(state, ev) {
|
|
6429
|
+
const t2 = state.tools.get(ev.id);
|
|
6430
|
+
if (t2) {
|
|
6431
|
+
t2.input = ev.input !== void 0 ? ev.input : safeJsonOrRaw(t2.partial);
|
|
6432
|
+
if (ev.providerMeta) t2.providerMeta = ev.providerMeta;
|
|
6433
|
+
}
|
|
6434
|
+
state.currentTextIndex = -1;
|
|
6435
|
+
}
|
|
6436
|
+
function handleThinkingStart(state, ev) {
|
|
6437
|
+
state.currentThinkingIndex = state.thinking.length;
|
|
6438
|
+
state.thinking.push({
|
|
6439
|
+
textBuf: "",
|
|
6440
|
+
...ev.providerMeta ? { providerMeta: ev.providerMeta } : {}
|
|
6441
|
+
});
|
|
6442
|
+
state.blockOrder.push({ kind: "thinking", idx: state.currentThinkingIndex });
|
|
6443
|
+
state.currentTextIndex = -1;
|
|
6444
|
+
}
|
|
6445
|
+
function handleThinkingDelta(state, text) {
|
|
6446
|
+
if (state.currentThinkingIndex === -1) {
|
|
6447
|
+
handleThinkingStart(state, {});
|
|
6448
|
+
}
|
|
6449
|
+
const t2 = state.thinking[state.currentThinkingIndex];
|
|
6450
|
+
if (t2) t2.textBuf += text;
|
|
6451
|
+
}
|
|
6452
|
+
function handleThinkingSignature(state, signature) {
|
|
6453
|
+
if (state.currentThinkingIndex === -1) {
|
|
6454
|
+
handleThinkingStart(state, {});
|
|
6455
|
+
}
|
|
6456
|
+
const t2 = state.thinking[state.currentThinkingIndex];
|
|
6457
|
+
if (t2) t2.signature = signature;
|
|
6458
|
+
}
|
|
6459
|
+
function handleThinkingStop(state) {
|
|
6460
|
+
state.currentThinkingIndex = -1;
|
|
6461
|
+
}
|
|
6462
|
+
function handleMessageStop(state, ev) {
|
|
6463
|
+
state.stopReason = ev.stopReason ?? "end_turn";
|
|
6464
|
+
state.usage = ev.usage ?? { input: 0, output: 0 };
|
|
6465
|
+
}
|
|
6466
|
+
async function streamProviderToResponse(provider, req, signal, ctx, events, logger) {
|
|
6467
|
+
const state = createStreamingState(req.model);
|
|
6468
|
+
const sessionId = resolveEventSessionId(ctx);
|
|
6469
|
+
logger.debug("Stream started", { providerId: provider.id, model: req.model });
|
|
6470
|
+
const TEXT_BATCH_SIZE = 4;
|
|
6471
|
+
let pendingText = "";
|
|
6472
|
+
let pendingCount = 0;
|
|
6473
|
+
const flushText = () => {
|
|
6474
|
+
if (pendingCount > 0) {
|
|
6475
|
+
events.emit("provider.text_delta", { sessionId, ctx, text: pendingText });
|
|
6476
|
+
pendingText = "";
|
|
6477
|
+
pendingCount = 0;
|
|
6478
|
+
}
|
|
6479
|
+
};
|
|
6064
6480
|
const iter = provider.stream(req, { signal })[Symbol.asyncIterator]();
|
|
6065
6481
|
try {
|
|
6066
6482
|
for (; ; ) {
|
|
@@ -6529,413 +6945,122 @@ async function runProviderWithRetry(opts) {
|
|
|
6529
6945
|
signal.removeEventListener("abort", onAbort);
|
|
6530
6946
|
};
|
|
6531
6947
|
const onAbort = () => {
|
|
6532
|
-
if (settled) return;
|
|
6533
|
-
settled = true;
|
|
6534
|
-
cleanup();
|
|
6535
|
-
reject(new Error("aborted"));
|
|
6536
|
-
};
|
|
6537
|
-
const t2 = setTimeout(() => {
|
|
6538
|
-
if (settled) return;
|
|
6539
|
-
settled = true;
|
|
6540
|
-
cleanup();
|
|
6541
|
-
resolve10();
|
|
6542
|
-
}, delay2);
|
|
6543
|
-
if (signal.aborted) {
|
|
6544
|
-
onAbort();
|
|
6545
|
-
return;
|
|
6546
|
-
}
|
|
6547
|
-
signal.addEventListener("abort", onAbort);
|
|
6548
|
-
});
|
|
6549
|
-
attempt++;
|
|
6550
|
-
}
|
|
6551
|
-
}
|
|
6552
|
-
}
|
|
6553
|
-
function elapsedMs(startedNs) {
|
|
6554
|
-
return Number(process.hrtime.bigint() - startedNs) / 1e6;
|
|
6555
|
-
}
|
|
6556
|
-
|
|
6557
|
-
// src/core/queued-messages.ts
|
|
6558
|
-
var META_KEY3 = "_queuedMessagesAwareness";
|
|
6559
|
-
var MAX_ITEMS = 20;
|
|
6560
|
-
var MAX_PREVIEW_CHARS = 500;
|
|
6561
|
-
function read(ctx) {
|
|
6562
|
-
const raw = ctx.meta[META_KEY3];
|
|
6563
|
-
if (!raw || typeof raw !== "object") return void 0;
|
|
6564
|
-
const s = raw;
|
|
6565
|
-
return Array.isArray(s.items) && Array.isArray(s.seen) ? s : void 0;
|
|
6566
|
-
}
|
|
6567
|
-
function sameList(a, b) {
|
|
6568
|
-
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
6569
|
-
}
|
|
6570
|
-
function toPreview(text) {
|
|
6571
|
-
const trimmed = text.trim();
|
|
6572
|
-
if (trimmed.length <= MAX_PREVIEW_CHARS) return trimmed;
|
|
6573
|
-
return `${trimmed.slice(0, MAX_PREVIEW_CHARS - 1)}\u2026`;
|
|
6574
|
-
}
|
|
6575
|
-
function setQueuedMessagesSnapshot(ctx, texts) {
|
|
6576
|
-
const items = texts.map(toPreview).filter((t2) => t2.length > 0).slice(0, MAX_ITEMS);
|
|
6577
|
-
const prev = read(ctx);
|
|
6578
|
-
if (items.length === 0 && (!prev || prev.seen.length === 0)) {
|
|
6579
|
-
if (prev) delete ctx.meta[META_KEY3];
|
|
6580
|
-
return;
|
|
6581
|
-
}
|
|
6582
|
-
ctx.meta[META_KEY3] = { items, seen: prev?.seen ?? [] };
|
|
6583
|
-
}
|
|
6584
|
-
function consumeQueuedMessagesUpdate(ctx) {
|
|
6585
|
-
const s = read(ctx);
|
|
6586
|
-
if (!s) return null;
|
|
6587
|
-
if (sameList(s.items, s.seen)) return null;
|
|
6588
|
-
if (s.items.length === 0) {
|
|
6589
|
-
delete ctx.meta[META_KEY3];
|
|
6590
|
-
return null;
|
|
6591
|
-
}
|
|
6592
|
-
ctx.meta[META_KEY3] = { items: s.items, seen: s.items };
|
|
6593
|
-
return s.items;
|
|
6594
|
-
}
|
|
6595
|
-
function buildQueuedMessagesBlock(items) {
|
|
6596
|
-
const body = items.map((t2, i) => `${i + 1}. ${t2}`).join("\n");
|
|
6597
|
-
return [
|
|
6598
|
-
"[QUEUED MESSAGES \u2014 the user typed these while you were working. They are",
|
|
6599
|
-
"waiting in the input queue and each will be delivered as its own turn",
|
|
6600
|
-
"after you finish, so do NOT answer or act on them as new tasks now. Read",
|
|
6601
|
-
"them as a heads-up: if one changes what you should be doing right now",
|
|
6602
|
-
"(new constraint, invalidated approach), adapt your current work; if one",
|
|
6603
|
-
"is just an FYI, fold it in. The queue may still change before delivery.",
|
|
6604
|
-
"This notice replaces any earlier one \u2014 the queue now holds exactly:",
|
|
6605
|
-
"",
|
|
6606
|
-
body,
|
|
6607
|
-
"]"
|
|
6608
|
-
].join("\n");
|
|
6609
|
-
}
|
|
6610
|
-
|
|
6611
|
-
// src/core/request-provider-binding.ts
|
|
6612
|
-
var requestProviders = /* @__PURE__ */ new WeakMap();
|
|
6613
|
-
function bindRequestProvider(request, provider) {
|
|
6614
|
-
requestProviders.set(request, provider);
|
|
6615
|
-
}
|
|
6616
|
-
function providerBoundToRequest(request) {
|
|
6617
|
-
return requestProviders.get(request);
|
|
6618
|
-
}
|
|
6619
|
-
|
|
6620
|
-
// src/core/agent-loop.ts
|
|
6621
|
-
function toError(err) {
|
|
6622
|
-
return err instanceof Error ? err : new Error(String(err));
|
|
6623
|
-
}
|
|
6624
|
-
function signalAbortReason(signal) {
|
|
6625
|
-
const r = signal.reason;
|
|
6626
|
-
if (r instanceof Error) return r.message || r.name;
|
|
6627
|
-
if (typeof r === "string" && r.length > 0) return r;
|
|
6628
|
-
return "aborted";
|
|
6629
|
-
}
|
|
6630
|
-
function createAgentLoopHandler(a, handlers) {
|
|
6631
|
-
const checkMailbox = attachMailboxChecker(a);
|
|
6632
|
-
const fleetPulseCfg = (() => {
|
|
6633
|
-
try {
|
|
6634
|
-
return typeof a.container?.has === "function" && a.container.has(TOKENS.ConfigStore) ? a.container.resolve(TOKENS.ConfigStore).get().fleet?.pulse : void 0;
|
|
6635
|
-
} catch {
|
|
6636
|
-
return void 0;
|
|
6637
|
-
}
|
|
6638
|
-
})();
|
|
6639
|
-
const getFleetPulse = attachFleetPulse(a, fleetPulseCfg);
|
|
6640
|
-
const pulseEveryN = Math.max(1, fleetPulseCfg?.everyNIterations ?? 5);
|
|
6641
|
-
const backgroundCoordination = () => a.ctx.meta["coordinationContextMode"] === "background";
|
|
6642
|
-
async function refreshProviderContextLimit(provider, model, opts = {}) {
|
|
6643
|
-
a.ctx.meta ??= {};
|
|
6644
|
-
const routeKey = `${provider.id}/${model}`;
|
|
6645
|
-
const previousRouteKey = a.ctx.meta["contextLimitRouteKey"];
|
|
6646
|
-
const routeChanged = previousRouteKey !== void 0 && previousRouteKey !== routeKey;
|
|
6647
|
-
const previousMaxContext = currentMaxContext();
|
|
6648
|
-
if (routeChanged) {
|
|
6649
|
-
delete a.ctx.meta["providerOverflowMaxContext"];
|
|
6650
|
-
delete a.ctx.meta["contextLimitBaseline"];
|
|
6651
|
-
delete a.ctx.meta["providerLimitEffectiveMaxContext"];
|
|
6652
|
-
delete a.ctx.meta["effectiveMaxContext"];
|
|
6653
|
-
delete a.ctx.meta["effectiveMaxContextSource"];
|
|
6654
|
-
delete a.ctx.meta["contextLimitLastEmittedMaxContext"];
|
|
6655
|
-
delete a.ctx.meta["contextLimitLastEmittedSource"];
|
|
6656
|
-
}
|
|
6657
|
-
a.ctx.meta["contextLimitRouteKey"] = routeKey;
|
|
6658
|
-
const routeMeta = a.ctx.meta["effectiveMaxContext"];
|
|
6659
|
-
const routeProviderCap = provider.capabilities.maxContext;
|
|
6660
|
-
const currentEffective = typeof routeMeta === "number" && routeMeta > 0 ? routeMeta : typeof routeProviderCap === "number" && routeProviderCap > 0 ? routeProviderCap : 2e5;
|
|
6661
|
-
const emitEffectiveLimit = (effective2, source) => {
|
|
6662
|
-
const lastEmitted = a.ctx.meta["contextLimitLastEmittedMaxContext"];
|
|
6663
|
-
const lastEmittedSource = a.ctx.meta["contextLimitLastEmittedSource"];
|
|
6664
|
-
if (lastEmitted === effective2 && lastEmittedSource === source) return;
|
|
6665
|
-
const eventPrevious = typeof lastEmitted === "number" && Number.isFinite(lastEmitted) && lastEmitted > 0 ? Math.floor(lastEmitted) : previousMaxContext;
|
|
6666
|
-
a.ctx.meta["contextLimitLastEmittedMaxContext"] = effective2;
|
|
6667
|
-
a.ctx.meta["contextLimitLastEmittedSource"] = source;
|
|
6668
|
-
a.events.emit("ctx.max_context", {
|
|
6669
|
-
sessionId: resolveEventSessionId(a.ctx),
|
|
6670
|
-
providerId: provider.id,
|
|
6671
|
-
modelId: model,
|
|
6672
|
-
maxContext: effective2,
|
|
6673
|
-
...eventPrevious > 0 ? { previousMaxContext: eventPrevious } : {},
|
|
6674
|
-
source,
|
|
6675
|
-
decreased: eventPrevious > 0 && effective2 < eventPrevious
|
|
6676
|
-
});
|
|
6677
|
-
};
|
|
6678
|
-
const emitNativeAfterRouteChange = () => {
|
|
6679
|
-
if (routeChanged && currentEffective > 0) {
|
|
6680
|
-
a.ctx.meta["effectiveMaxContext"] = currentEffective;
|
|
6681
|
-
a.ctx.meta["effectiveMaxContextSource"] = "configured";
|
|
6682
|
-
emitEffectiveLimit(currentEffective, "configured");
|
|
6683
|
-
}
|
|
6684
|
-
};
|
|
6685
|
-
const refresh = provider.refreshContextLimit;
|
|
6686
|
-
if (opts.probe === false || !refresh) {
|
|
6687
|
-
emitNativeAfterRouteChange();
|
|
6688
|
-
return;
|
|
6689
|
-
}
|
|
6690
|
-
let discovered;
|
|
6691
|
-
try {
|
|
6692
|
-
discovered = await refresh.call(provider, model, { signal: a.ctx.signal });
|
|
6693
|
-
} catch {
|
|
6694
|
-
emitNativeAfterRouteChange();
|
|
6695
|
-
return;
|
|
6696
|
-
}
|
|
6697
|
-
const providerLimit = discovered?.maxContext;
|
|
6698
|
-
if (typeof providerLimit !== "number" || !Number.isFinite(providerLimit) || providerLimit <= 0) {
|
|
6699
|
-
emitNativeAfterRouteChange();
|
|
6700
|
-
return;
|
|
6701
|
-
}
|
|
6702
|
-
const providerLimitFloored = Math.floor(providerLimit);
|
|
6703
|
-
const existingBaseline = a.ctx.meta["contextLimitBaseline"];
|
|
6704
|
-
const lastApplied = a.ctx.meta["providerLimitEffectiveMaxContext"];
|
|
6705
|
-
const existingOverflow = a.ctx.meta["providerOverflowMaxContext"];
|
|
6706
|
-
const effectiveChangedOutsideProbe = typeof lastApplied === "number" && Number.isFinite(lastApplied) && currentEffective !== Math.floor(lastApplied) && currentEffective !== existingOverflow;
|
|
6707
|
-
const baseline = effectiveChangedOutsideProbe || typeof existingBaseline !== "number" || !Number.isFinite(existingBaseline) || existingBaseline <= 0 ? currentEffective : Math.floor(existingBaseline);
|
|
6708
|
-
a.ctx.meta["contextLimitBaseline"] = baseline;
|
|
6709
|
-
const overflowLimit = a.ctx.meta["providerOverflowMaxContext"];
|
|
6710
|
-
const overflowFloored = typeof overflowLimit === "number" && Number.isFinite(overflowLimit) && overflowLimit > 0 ? Math.floor(overflowLimit) : void 0;
|
|
6711
|
-
const ceilings = [baseline, providerLimitFloored];
|
|
6712
|
-
if (overflowFloored !== void 0) ceilings.push(overflowFloored);
|
|
6713
|
-
const effective = Math.min(...ceilings.filter((limit) => limit > 0));
|
|
6714
|
-
let effectiveSource;
|
|
6715
|
-
if (overflowFloored !== void 0 && effective === overflowFloored) {
|
|
6716
|
-
effectiveSource = "provider_overflow";
|
|
6717
|
-
} else if (effective === providerLimitFloored) {
|
|
6718
|
-
effectiveSource = "provider";
|
|
6719
|
-
} else {
|
|
6720
|
-
effectiveSource = "configured";
|
|
6721
|
-
}
|
|
6722
|
-
a.ctx.meta["effectiveMaxContext"] = effective;
|
|
6723
|
-
a.ctx.meta["providerLimitEffectiveMaxContext"] = effective;
|
|
6724
|
-
a.ctx.meta["effectiveMaxContextSource"] = effectiveSource;
|
|
6725
|
-
emitEffectiveLimit(effective, effectiveSource);
|
|
6726
|
-
}
|
|
6727
|
-
async function compactContextIfNeeded() {
|
|
6728
|
-
const msgCount = a.ctx.messages.length;
|
|
6729
|
-
const maxContext = currentMaxContext();
|
|
6730
|
-
const revision = a.ctx.state.revision;
|
|
6731
|
-
const toolsRef = a.ctx.tools;
|
|
6732
|
-
const systemRef = a.ctx.systemPrompt;
|
|
6733
|
-
const requestTokens = a.ctx.lastRequestTokens;
|
|
6734
|
-
if (_lastCompactionMsgCount === msgCount && _lastCompactionRevision === revision && _lastCompactionToolsRef === toolsRef && _lastCompactionSystemRef === systemRef && _lastCompactionRequestTokens === requestTokens && _lastCompactionWasNoop && _lastCompactionMaxContext === maxContext && maxContext > 0) {
|
|
6735
|
-
return false;
|
|
6736
|
-
}
|
|
6737
|
-
const beforeMessages = a.ctx.messages;
|
|
6738
|
-
const beforeMsgCount = a.ctx.messages.length;
|
|
6739
|
-
const beforeRevision = a.ctx.state.revision;
|
|
6740
|
-
await a.pipelines.contextWindow.run(a.ctx);
|
|
6741
|
-
_lastCompactionMsgCount = a.ctx.messages.length;
|
|
6742
|
-
_lastCompactionRevision = a.ctx.state.revision;
|
|
6743
|
-
_lastCompactionToolsRef = a.ctx.tools;
|
|
6744
|
-
_lastCompactionSystemRef = a.ctx.systemPrompt;
|
|
6745
|
-
_lastCompactionMaxContext = maxContext;
|
|
6746
|
-
const changed = a.ctx.state.revision !== beforeRevision || a.ctx.messages !== beforeMessages || a.ctx.messages.length !== beforeMsgCount;
|
|
6747
|
-
const tokens = refreshContextRequestTokenStash({ force: changed });
|
|
6748
|
-
_lastCompactionRequestTokens = tokens;
|
|
6749
|
-
const load = maxContext > 0 ? tokens / maxContext : 0;
|
|
6750
|
-
_lastCompactionWasNoop = tokens > 0 && load < 0.5;
|
|
6751
|
-
if (changed) {
|
|
6752
|
-
_lastEmittedMsgCount = -1;
|
|
6753
|
-
_lastEmittedToolCount = -1;
|
|
6754
|
-
_lastEmittedMaxContext = -1;
|
|
6755
|
-
}
|
|
6756
|
-
return changed;
|
|
6757
|
-
}
|
|
6758
|
-
const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
|
|
6759
|
-
let _cachedSysRef = null;
|
|
6760
|
-
let _cachedToolsRef = null;
|
|
6761
|
-
let _cachedOverheadTokens = 0;
|
|
6762
|
-
function systemAndToolsOverhead() {
|
|
6763
|
-
const sysRef = a.ctx.systemPrompt;
|
|
6764
|
-
const toolsRef = a.ctx.tools;
|
|
6765
|
-
if (sysRef === _cachedSysRef && toolsRef === _cachedToolsRef && _cachedOverheadTokens > 0) {
|
|
6766
|
-
return _cachedOverheadTokens;
|
|
6767
|
-
}
|
|
6768
|
-
const breakdown = estimateRequestTokens([], sysRef, toolsRef ?? [], calibrationKey());
|
|
6769
|
-
_cachedSysRef = sysRef;
|
|
6770
|
-
_cachedToolsRef = toolsRef;
|
|
6771
|
-
_cachedOverheadTokens = breakdown.systemPrompt + breakdown.tools;
|
|
6772
|
-
return _cachedOverheadTokens;
|
|
6773
|
-
}
|
|
6774
|
-
function stashRequestTokens(req) {
|
|
6775
|
-
const preFlight = estimateRequestTokens(
|
|
6776
|
-
req.messages,
|
|
6777
|
-
req.system,
|
|
6778
|
-
req.tools ?? [],
|
|
6779
|
-
calibrationKey(req.model)
|
|
6780
|
-
);
|
|
6781
|
-
a.ctx.lastRequestTokens = preFlight.total;
|
|
6782
|
-
_lastPreFlightMsgCount = req.messages.length;
|
|
6783
|
-
_lastPreFlightToolCount = (req.tools ?? []).length;
|
|
6784
|
-
_lastPreFlightRevision = a.ctx.state.revision;
|
|
6785
|
-
_cachedSysRef = req.system;
|
|
6786
|
-
_cachedToolsRef = req.tools ?? [];
|
|
6787
|
-
_cachedOverheadTokens = preFlight.systemPrompt + preFlight.tools;
|
|
6788
|
-
a.ctx.meta["lastRequestTokensAt"] = {
|
|
6789
|
-
msgCount: req.messages.length,
|
|
6790
|
-
toolCount: (req.tools ?? []).length,
|
|
6791
|
-
revision: a.ctx.state.revision
|
|
6792
|
-
};
|
|
6793
|
-
return preFlight;
|
|
6794
|
-
}
|
|
6795
|
-
function refreshContextRequestTokenStash(opts = {}) {
|
|
6796
|
-
const msgCount = a.ctx.messages.length;
|
|
6797
|
-
const toolCount = (a.ctx.tools ?? []).length;
|
|
6798
|
-
const revision = a.ctx.state.revision;
|
|
6799
|
-
const stashed = a.ctx.lastRequestTokens;
|
|
6800
|
-
const stashedAt = a.ctx.meta?.["lastRequestTokensAt"];
|
|
6801
|
-
if (!opts.force && typeof stashed === "number" && stashed > 0 && typeof stashedAt === "object" && stashedAt !== null) {
|
|
6802
|
-
const meta = stashedAt;
|
|
6803
|
-
if (meta.msgCount === msgCount && meta.toolCount === toolCount && meta.revision === revision && _lastPreFlightRevision === revision) {
|
|
6804
|
-
return stashed;
|
|
6805
|
-
}
|
|
6806
|
-
}
|
|
6807
|
-
const refreshed = estimateMessageTokens(a.ctx.messages) + systemAndToolsOverhead();
|
|
6808
|
-
a.ctx.lastRequestTokens = refreshed;
|
|
6809
|
-
_lastPreFlightMsgCount = msgCount;
|
|
6810
|
-
_lastPreFlightToolCount = toolCount;
|
|
6811
|
-
_lastPreFlightRevision = revision;
|
|
6812
|
-
a.ctx.meta["lastRequestTokensAt"] = { msgCount, toolCount, revision };
|
|
6813
|
-
return refreshed;
|
|
6814
|
-
}
|
|
6815
|
-
async function buildRequestWithPreflightCompaction(opts) {
|
|
6816
|
-
let prepared = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
6817
|
-
let req = prepared.request;
|
|
6818
|
-
let preFlight = stashRequestTokens(req);
|
|
6819
|
-
let probedRouteKey = `${prepared.provider.id}/${req.model}`;
|
|
6820
|
-
await refreshProviderContextLimit(prepared.provider, req.model);
|
|
6821
|
-
if (await compactContextIfNeeded()) {
|
|
6822
|
-
prepared = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
6823
|
-
req = prepared.request;
|
|
6824
|
-
preFlight = stashRequestTokens(req);
|
|
6825
|
-
const rebuiltRouteKey = `${prepared.provider.id}/${req.model}`;
|
|
6826
|
-
if (rebuiltRouteKey !== probedRouteKey) {
|
|
6827
|
-
probedRouteKey = rebuiltRouteKey;
|
|
6828
|
-
await refreshProviderContextLimit(prepared.provider, req.model);
|
|
6829
|
-
if (await compactContextIfNeeded()) {
|
|
6830
|
-
prepared = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
6831
|
-
req = prepared.request;
|
|
6832
|
-
preFlight = stashRequestTokens(req);
|
|
6948
|
+
if (settled) return;
|
|
6949
|
+
settled = true;
|
|
6950
|
+
cleanup();
|
|
6951
|
+
reject(new Error("aborted"));
|
|
6952
|
+
};
|
|
6953
|
+
const t2 = setTimeout(() => {
|
|
6954
|
+
if (settled) return;
|
|
6955
|
+
settled = true;
|
|
6956
|
+
cleanup();
|
|
6957
|
+
resolve10();
|
|
6958
|
+
}, delay2);
|
|
6959
|
+
if (signal.aborted) {
|
|
6960
|
+
onAbort();
|
|
6961
|
+
return;
|
|
6833
6962
|
}
|
|
6834
|
-
|
|
6963
|
+
signal.addEventListener("abort", onAbort);
|
|
6964
|
+
});
|
|
6965
|
+
attempt++;
|
|
6835
6966
|
}
|
|
6836
|
-
return { req, provider: prepared.provider, preFlight };
|
|
6837
6967
|
}
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
const cal = getCalibrationState(calibrationKey());
|
|
6868
|
-
total = cal.calibrated ? Math.round(raw * Math.min(1.5, Math.max(0.5, cal.ratio))) : raw;
|
|
6869
|
-
}
|
|
6870
|
-
const rawLoad = maxContext > 0 ? total / maxContext : 0;
|
|
6871
|
-
const load = Math.max(0, Math.min(1, rawLoad));
|
|
6872
|
-
a.events.emit("ctx.pct", {
|
|
6873
|
-
sessionId: resolveEventSessionId(a.ctx),
|
|
6874
|
-
load,
|
|
6875
|
-
rawLoad,
|
|
6876
|
-
tokens: total,
|
|
6877
|
-
maxContext
|
|
6878
|
-
});
|
|
6968
|
+
}
|
|
6969
|
+
function elapsedMs(startedNs) {
|
|
6970
|
+
return Number(process.hrtime.bigint() - startedNs) / 1e6;
|
|
6971
|
+
}
|
|
6972
|
+
|
|
6973
|
+
// src/core/queued-messages.ts
|
|
6974
|
+
var META_KEY3 = "_queuedMessagesAwareness";
|
|
6975
|
+
var MAX_ITEMS = 20;
|
|
6976
|
+
var MAX_PREVIEW_CHARS = 500;
|
|
6977
|
+
function read(ctx) {
|
|
6978
|
+
const raw = ctx.meta[META_KEY3];
|
|
6979
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
6980
|
+
const s = raw;
|
|
6981
|
+
return Array.isArray(s.items) && Array.isArray(s.seen) ? s : void 0;
|
|
6982
|
+
}
|
|
6983
|
+
function sameList(a, b) {
|
|
6984
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
6985
|
+
}
|
|
6986
|
+
function toPreview(text) {
|
|
6987
|
+
const trimmed = text.trim();
|
|
6988
|
+
if (trimmed.length <= MAX_PREVIEW_CHARS) return trimmed;
|
|
6989
|
+
return `${trimmed.slice(0, MAX_PREVIEW_CHARS - 1)}\u2026`;
|
|
6990
|
+
}
|
|
6991
|
+
function setQueuedMessagesSnapshot(ctx, texts) {
|
|
6992
|
+
const items = texts.map(toPreview).filter((t2) => t2.length > 0).slice(0, MAX_ITEMS);
|
|
6993
|
+
const prev = read(ctx);
|
|
6994
|
+
if (items.length === 0 && (!prev || prev.seen.length === 0)) {
|
|
6995
|
+
if (prev) delete ctx.meta[META_KEY3];
|
|
6996
|
+
return;
|
|
6879
6997
|
}
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6998
|
+
ctx.meta[META_KEY3] = { items, seen: prev?.seen ?? [] };
|
|
6999
|
+
}
|
|
7000
|
+
function consumeQueuedMessagesUpdate(ctx) {
|
|
7001
|
+
const s = read(ctx);
|
|
7002
|
+
if (!s) return null;
|
|
7003
|
+
if (sameList(s.items, s.seen)) return null;
|
|
7004
|
+
if (s.items.length === 0) {
|
|
7005
|
+
delete ctx.meta[META_KEY3];
|
|
7006
|
+
return null;
|
|
6884
7007
|
}
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
7008
|
+
ctx.meta[META_KEY3] = { items: s.items, seen: s.items };
|
|
7009
|
+
return s.items;
|
|
7010
|
+
}
|
|
7011
|
+
function buildQueuedMessagesBlock(items) {
|
|
7012
|
+
const body = items.map((t2, i) => `${i + 1}. ${t2}`).join("\n");
|
|
7013
|
+
return [
|
|
7014
|
+
"[QUEUED MESSAGES \u2014 the user typed these while you were working. They are",
|
|
7015
|
+
"waiting in the input queue and each will be delivered as its own turn",
|
|
7016
|
+
"after you finish, so do NOT answer or act on them as new tasks now. Read",
|
|
7017
|
+
"them as a heads-up: if one changes what you should be doing right now",
|
|
7018
|
+
"(new constraint, invalidated approach), adapt your current work; if one",
|
|
7019
|
+
"is just an FYI, fold it in. The queue may still change before delivery.",
|
|
7020
|
+
"This notice replaces any earlier one \u2014 the queue now holds exactly:",
|
|
7021
|
+
"",
|
|
7022
|
+
body,
|
|
7023
|
+
"]"
|
|
7024
|
+
].join("\n");
|
|
7025
|
+
}
|
|
7026
|
+
|
|
7027
|
+
// src/core/request-provider-binding.ts
|
|
7028
|
+
var requestProviders = /* @__PURE__ */ new WeakMap();
|
|
7029
|
+
function bindRequestProvider(request, provider) {
|
|
7030
|
+
requestProviders.set(request, provider);
|
|
7031
|
+
}
|
|
7032
|
+
function providerBoundToRequest(request) {
|
|
7033
|
+
return requestProviders.get(request);
|
|
7034
|
+
}
|
|
7035
|
+
|
|
7036
|
+
// src/core/agent-loop.ts
|
|
7037
|
+
function toError(err) {
|
|
7038
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
7039
|
+
}
|
|
7040
|
+
function signalAbortReason(signal) {
|
|
7041
|
+
const r = signal.reason;
|
|
7042
|
+
if (r instanceof Error) return r.message || r.name;
|
|
7043
|
+
if (typeof r === "string" && r.length > 0) return r;
|
|
7044
|
+
return "aborted";
|
|
7045
|
+
}
|
|
7046
|
+
function createAgentLoopHandler(a, handlers) {
|
|
7047
|
+
const checkMailbox = attachMailboxChecker(a);
|
|
7048
|
+
const fleetPulseCfg = (() => {
|
|
7049
|
+
try {
|
|
7050
|
+
return typeof a.container?.has === "function" && a.container.has(TOKENS.ConfigStore) ? a.container.resolve(TOKENS.ConfigStore).get().fleet?.pulse : void 0;
|
|
7051
|
+
} catch {
|
|
7052
|
+
return void 0;
|
|
7053
|
+
}
|
|
7054
|
+
})();
|
|
7055
|
+
const getFleetPulse = attachFleetPulse(a, fleetPulseCfg);
|
|
7056
|
+
const pulseEveryN = Math.max(1, fleetPulseCfg?.everyNIterations ?? 5);
|
|
7057
|
+
const backgroundCoordination = () => a.ctx.meta["coordinationContextMode"] === "background";
|
|
7058
|
+
const loopContext = createAgentLoopContextManager(a, handlers);
|
|
6899
7059
|
function foldBlockIntoConversation(block) {
|
|
6900
7060
|
if (!a.ctx.state.appendBlockToLastUserMessage(block)) {
|
|
6901
7061
|
a.ctx.state.appendMessage({ role: "user", content: [block], origin: "runtime" });
|
|
6902
7062
|
}
|
|
6903
7063
|
}
|
|
6904
|
-
function iterationFingerprint(blocks) {
|
|
6905
|
-
const toolUses = blocks.filter(isToolUseBlock);
|
|
6906
|
-
const texts = blocks.filter(isTextBlock);
|
|
6907
|
-
const toolNameSet = Array.from(new Set(toolUses.map((u) => u.name))).sort();
|
|
6908
|
-
const firstInputHash = toolUses[0] ? hashSmall(stableStringify(toolUses[0].input ?? {})) : "";
|
|
6909
|
-
const textBlob = texts.map((t2) => t2.text).join("").slice(0, 512);
|
|
6910
|
-
const hasContent = toolNameSet.length > 0 || textBlob.length > 0;
|
|
6911
|
-
if (!hasContent) return "__empty__";
|
|
6912
|
-
return [
|
|
6913
|
-
`tools=${toolNameSet.join("+") || "-"}`,
|
|
6914
|
-
`in0=${firstInputHash}`,
|
|
6915
|
-
`txt=${textBlob}`
|
|
6916
|
-
].join("\n");
|
|
6917
|
-
}
|
|
6918
|
-
function stableStringify(value) {
|
|
6919
|
-
return JSON.stringify(canonicalize(value));
|
|
6920
|
-
}
|
|
6921
|
-
function canonicalize(value) {
|
|
6922
|
-
if (Array.isArray(value)) return value.map(canonicalize);
|
|
6923
|
-
if (value && typeof value === "object") {
|
|
6924
|
-
const src = value;
|
|
6925
|
-
const out = {};
|
|
6926
|
-
for (const k of Object.keys(src).sort()) out[k] = canonicalize(src[k]);
|
|
6927
|
-
return out;
|
|
6928
|
-
}
|
|
6929
|
-
return value;
|
|
6930
|
-
}
|
|
6931
|
-
function hashSmall(s) {
|
|
6932
|
-
let h = 2166136261;
|
|
6933
|
-
for (let i = 0; i < s.length; i++) {
|
|
6934
|
-
h ^= s.charCodeAt(i);
|
|
6935
|
-
h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
|
|
6936
|
-
}
|
|
6937
|
-
return h.toString(36);
|
|
6938
|
-
}
|
|
6939
7064
|
function injectPendingBtwNotes(onMailboxBlock) {
|
|
6940
7065
|
const notes = consumeBtwNotes(a.ctx);
|
|
6941
7066
|
if (notes.length === 0) return;
|
|
@@ -7015,15 +7140,10 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
7015
7140
|
a.ctx.state.replaceMessages(cleaned.messages);
|
|
7016
7141
|
a.ctx.lastRealInputTokens = void 0;
|
|
7017
7142
|
delete a.ctx.meta["realAnchorMsgCount"];
|
|
7018
|
-
refreshContextRequestTokenStash({ force: true });
|
|
7143
|
+
loopContext.refreshContextRequestTokenStash({ force: true });
|
|
7019
7144
|
}
|
|
7020
7145
|
}
|
|
7021
|
-
const
|
|
7022
|
-
let lastToolSignature = "";
|
|
7023
|
-
let toolLoopCount = 0;
|
|
7024
|
-
let iterationSteerDone = false;
|
|
7025
|
-
const recentCallKeys = [];
|
|
7026
|
-
const steeredCallKeys = /* @__PURE__ */ new Set();
|
|
7146
|
+
const loopDetector = new AgentLoopDetector(a);
|
|
7027
7147
|
let pendingLoopSteer = null;
|
|
7028
7148
|
let todoReconcileSteers = 0;
|
|
7029
7149
|
function queueLoopSteer(text) {
|
|
@@ -7130,7 +7250,7 @@ ${text}` : text;
|
|
|
7130
7250
|
req,
|
|
7131
7251
|
provider: requestProvider,
|
|
7132
7252
|
preFlight
|
|
7133
|
-
} = await buildRequestWithPreflightCompaction(opts);
|
|
7253
|
+
} = await loopContext.buildRequestWithPreflightCompaction(opts);
|
|
7134
7254
|
await a.ctx.session.append({
|
|
7135
7255
|
type: "llm_request",
|
|
7136
7256
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -7143,7 +7263,7 @@ ${text}` : text;
|
|
|
7143
7263
|
let res;
|
|
7144
7264
|
try {
|
|
7145
7265
|
res = await customRunner(a.ctx, req);
|
|
7146
|
-
const key = calibrationKey(req.model);
|
|
7266
|
+
const key = loopContext.calibrationKey(req.model);
|
|
7147
7267
|
const cal = getCalibrationState(key);
|
|
7148
7268
|
const calibratedTotal = cal.calibrated ? Math.round(preFlight.total * Math.min(1.5, Math.max(0.5, cal.ratio))) : preFlight.total;
|
|
7149
7269
|
const realInputTokens = effectiveInputTokens(res.usage);
|
|
@@ -7151,10 +7271,10 @@ ${text}` : text;
|
|
|
7151
7271
|
const previousRealInput = a.ctx.lastRealInputTokens;
|
|
7152
7272
|
const previousAnchorMsgCount = typeof a.ctx.meta?.["realAnchorMsgCount"] === "number" ? a.ctx.meta["realAnchorMsgCount"] : void 0;
|
|
7153
7273
|
const anchorIsPlausible = realInputTokens >= calibratedTotal * 0.5;
|
|
7154
|
-
const anchorAdvanced = previousRealInput === void 0 || realInputTokens > previousRealInput || previousAnchorMsgCount !== void 0 &&
|
|
7274
|
+
const anchorAdvanced = previousRealInput === void 0 || realInputTokens > previousRealInput || previousAnchorMsgCount !== void 0 && loopContext.lastPreFlightMsgCount < previousAnchorMsgCount;
|
|
7155
7275
|
if (realInputTokens > 0 && anchorIsPlausible && anchorAdvanced) {
|
|
7156
7276
|
a.ctx.lastRealInputTokens = realInputTokens;
|
|
7157
|
-
a.ctx.meta["realAnchorMsgCount"] =
|
|
7277
|
+
a.ctx.meta["realAnchorMsgCount"] = loopContext.lastPreFlightMsgCount;
|
|
7158
7278
|
}
|
|
7159
7279
|
recoveryRetries = 0;
|
|
7160
7280
|
} catch (err) {
|
|
@@ -7244,7 +7364,9 @@ ${text}` : text;
|
|
|
7244
7364
|
clearEvaluatedMailboxBlocks();
|
|
7245
7365
|
const responseProvider = providerBoundToRequest(req) ?? requestProvider;
|
|
7246
7366
|
const responseResult = await handlers.response.processResponse(res, req, responseProvider);
|
|
7247
|
-
await refreshProviderContextLimit(responseProvider, req.model, {
|
|
7367
|
+
await loopContext.refreshProviderContextLimit(responseProvider, req.model, {
|
|
7368
|
+
probe: false
|
|
7369
|
+
});
|
|
7248
7370
|
if (responseResult.finalText) {
|
|
7249
7371
|
a.ctx.meta["lastAgentOutput"] = responseResult.finalText;
|
|
7250
7372
|
}
|
|
@@ -7267,103 +7389,18 @@ ${text}` : text;
|
|
|
7267
7389
|
}
|
|
7268
7390
|
finalText = responseResult.finalText;
|
|
7269
7391
|
const toolUses = res.content.filter(isToolUseBlock);
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
iterationSteerDone = false;
|
|
7279
|
-
}
|
|
7280
|
-
const names = toolUses.map((t2) => t2.name).join(", ");
|
|
7281
|
-
const hasText2 = res.content.some(isTextBlock);
|
|
7282
|
-
const kind = toolUses.length > 0 && hasText2 ? "mixed" : toolUses.length > 0 ? "tool" : "message";
|
|
7283
|
-
const observationRepeat = loopCfg.mode === "steer-then-cut" && toolUses.length > 0 && toolUses.every((use) => {
|
|
7284
|
-
const tool = a.tools.get(use.name);
|
|
7285
|
-
return tool?.mutating === false && (tool.riskTier === "safe" || (tool.capabilities?.length ?? 0) > 0);
|
|
7286
|
-
});
|
|
7287
|
-
const repeatMultiplier = observationRepeat ? 2 : 1;
|
|
7288
|
-
const detail = kind === "tool" ? `"${names}" called with effectively identical inputs ${toolLoopCount} times in a row` : kind === "mixed" ? `"${names}" + same text repeated ${toolLoopCount} times in a row` : `same assistant text repeated ${toolLoopCount} times in a row`;
|
|
7289
|
-
const cutAt = (loopCfg.mode === "cut" ? loopCfg.steerThreshold : loopCfg.cutThreshold) * repeatMultiplier;
|
|
7290
|
-
if (toolLoopCount >= cutAt) {
|
|
7291
|
-
a.logger.warn(`Loop detected: ${detail} \u2014 stopping to prevent infinite loop.`);
|
|
7292
|
-
a.events.emit("tool.loop_detected", {
|
|
7293
|
-
sessionId: resolveEventSessionId(a.ctx),
|
|
7294
|
-
ctx: a.ctx,
|
|
7295
|
-
tools: names,
|
|
7296
|
-
repeatCount: toolLoopCount,
|
|
7297
|
-
iteration: i,
|
|
7298
|
-
kind,
|
|
7299
|
-
action: "cut",
|
|
7300
|
-
scope: "iteration"
|
|
7301
|
-
});
|
|
7302
|
-
const summary = kind === "message" ? `[Loop detected: same assistant message repeated ${toolLoopCount}\xD7 \u2014 stopping to prevent infinite repetition.]` : `[Loop detected: ${detail} \u2014 stopping to prevent infinite repetition.]`;
|
|
7303
|
-
return {
|
|
7304
|
-
status: "max_iterations",
|
|
7305
|
-
iterations,
|
|
7306
|
-
finalText: finalText || summary,
|
|
7307
|
-
delegateSummaries
|
|
7308
|
-
};
|
|
7309
|
-
}
|
|
7310
|
-
if (loopCfg.mode === "steer-then-cut" && toolLoopCount >= loopCfg.steerThreshold * repeatMultiplier && !iterationSteerDone) {
|
|
7311
|
-
iterationSteerDone = true;
|
|
7312
|
-
a.logger.warn(`Loop detected: ${detail} \u2014 steering the model to change approach.`);
|
|
7313
|
-
a.events.emit("tool.loop_detected", {
|
|
7314
|
-
sessionId: resolveEventSessionId(a.ctx),
|
|
7315
|
-
ctx: a.ctx,
|
|
7316
|
-
tools: names,
|
|
7317
|
-
repeatCount: toolLoopCount,
|
|
7318
|
-
iteration: i,
|
|
7319
|
-
kind,
|
|
7320
|
-
action: "steer",
|
|
7321
|
-
scope: "iteration"
|
|
7322
|
-
});
|
|
7323
|
-
queueLoopSteer(
|
|
7324
|
-
`[loop-detector] Your last ${toolLoopCount} responses were effectively identical (${detail}). This approach is not working. Change strategy: use a different tool, different arguments, or a different plan \u2014 or explain what is blocking you and stop. Repeating the same response ${cutAt - toolLoopCount} more time(s) will terminate the turn.`
|
|
7325
|
-
);
|
|
7326
|
-
}
|
|
7327
|
-
} else {
|
|
7328
|
-
lastToolSignature = "";
|
|
7329
|
-
toolLoopCount = 0;
|
|
7330
|
-
iterationSteerDone = false;
|
|
7331
|
-
}
|
|
7332
|
-
if (loopCfg.mode === "steer-then-cut") {
|
|
7333
|
-
for (const u of toolUses) {
|
|
7334
|
-
const key = `${u.name}:${hashSmall(stableStringify(u.input ?? {}))}`;
|
|
7335
|
-
recentCallKeys.push(key);
|
|
7336
|
-
if (recentCallKeys.length > loopCfg.windowSize) recentCallKeys.shift();
|
|
7337
|
-
if (steeredCallKeys.has(key)) continue;
|
|
7338
|
-
let count = 0;
|
|
7339
|
-
for (const k of recentCallKeys) if (k === key) count++;
|
|
7340
|
-
const tool = a.tools.get(u.name);
|
|
7341
|
-
const observationThreshold = tool?.mutating === false && (tool.riskTier === "safe" || (tool.capabilities?.length ?? 0) > 0) ? loopCfg.callRepeatThreshold * 2 : loopCfg.callRepeatThreshold;
|
|
7342
|
-
if (count < observationThreshold) continue;
|
|
7343
|
-
steeredCallKeys.add(key);
|
|
7344
|
-
const preview2 = JSON.stringify(u.input ?? {}).slice(0, 160);
|
|
7345
|
-
a.logger.warn(
|
|
7346
|
-
`Loop detected: "${u.name}" called with identical arguments ${count}\xD7 within the last ${loopCfg.windowSize} tool calls \u2014 steering the model to change approach.`
|
|
7347
|
-
);
|
|
7348
|
-
a.events.emit("tool.loop_detected", {
|
|
7349
|
-
sessionId: resolveEventSessionId(a.ctx),
|
|
7350
|
-
ctx: a.ctx,
|
|
7351
|
-
tools: u.name,
|
|
7352
|
-
repeatCount: count,
|
|
7353
|
-
iteration: i,
|
|
7354
|
-
kind: "tool",
|
|
7355
|
-
action: "steer",
|
|
7356
|
-
scope: "call"
|
|
7357
|
-
});
|
|
7358
|
-
queueLoopSteer(
|
|
7359
|
-
`[loop-detector] You have called ${u.name}(${preview2}) ${count} times with identical arguments within the last ${loopCfg.windowSize} tool calls. The result will not change. Do not repeat this call \u2014 use what you already know, try a different approach, or explain the blocker.`
|
|
7360
|
-
);
|
|
7361
|
-
}
|
|
7362
|
-
}
|
|
7392
|
+
const loopCheck = loopDetector.checkIteration(i, res.content, toolUses, queueLoopSteer);
|
|
7393
|
+
if (loopCheck.cut) {
|
|
7394
|
+
return {
|
|
7395
|
+
status: "max_iterations",
|
|
7396
|
+
iterations,
|
|
7397
|
+
finalText: finalText || loopCheck.cutSummary || "",
|
|
7398
|
+
delegateSummaries
|
|
7399
|
+
};
|
|
7363
7400
|
}
|
|
7364
7401
|
if (toolUses.length === 0) {
|
|
7365
|
-
await compactContextIfNeeded();
|
|
7366
|
-
emitContextPct();
|
|
7402
|
+
await loopContext.compactContextIfNeeded();
|
|
7403
|
+
loopContext.emitContextPct();
|
|
7367
7404
|
a.events.emit("iteration.completed", {
|
|
7368
7405
|
sessionId: resolveEventSessionId(a.ctx),
|
|
7369
7406
|
ctx: a.ctx,
|
|
@@ -7401,8 +7438,8 @@ ${text}` : text;
|
|
|
7401
7438
|
throw toolErr;
|
|
7402
7439
|
}
|
|
7403
7440
|
if (autonomousContinue && consumeAutonomousContinue(a.ctx)) {
|
|
7404
|
-
await compactContextIfNeeded();
|
|
7405
|
-
emitContextPct();
|
|
7441
|
+
await loopContext.compactContextIfNeeded();
|
|
7442
|
+
loopContext.emitContextPct();
|
|
7406
7443
|
a.events.emit("iteration.completed", {
|
|
7407
7444
|
sessionId: resolveEventSessionId(a.ctx),
|
|
7408
7445
|
ctx: a.ctx,
|
|
@@ -7411,8 +7448,8 @@ ${text}` : text;
|
|
|
7411
7448
|
await a.extensions.runAfterIteration(a.ctx, i);
|
|
7412
7449
|
continue;
|
|
7413
7450
|
}
|
|
7414
|
-
await compactContextIfNeeded();
|
|
7415
|
-
emitContextPct();
|
|
7451
|
+
await loopContext.compactContextIfNeeded();
|
|
7452
|
+
loopContext.emitContextPct();
|
|
7416
7453
|
a.events.emit("iteration.completed", {
|
|
7417
7454
|
sessionId: resolveEventSessionId(a.ctx),
|
|
7418
7455
|
ctx: a.ctx,
|
|
@@ -7611,6 +7648,9 @@ var RUNTIME_CAPABILITY_MANIFEST = [
|
|
|
7611
7648
|
"codebase-incoming-calls",
|
|
7612
7649
|
"codebase-outgoing-calls",
|
|
7613
7650
|
"codebase-index",
|
|
7651
|
+
"codebase-skeleton",
|
|
7652
|
+
"codebase-repo-map",
|
|
7653
|
+
"codebase-impact-analysis",
|
|
7614
7654
|
"dead-code-scan",
|
|
7615
7655
|
"diff",
|
|
7616
7656
|
"json",
|
|
@@ -7621,7 +7661,7 @@ var RUNTIME_CAPABILITY_MANIFEST = [
|
|
|
7621
7661
|
id: "filesystem.write",
|
|
7622
7662
|
pack: "development",
|
|
7623
7663
|
exposure: "direct",
|
|
7624
|
-
tools: ["write", "edit", "replace", "patch"]
|
|
7664
|
+
tools: ["write", "edit", "replace", "patch", "codebase-ast-replace"]
|
|
7625
7665
|
},
|
|
7626
7666
|
{
|
|
7627
7667
|
id: "execution.shell",
|
|
@@ -7633,7 +7673,7 @@ var RUNTIME_CAPABILITY_MANIFEST = [
|
|
|
7633
7673
|
id: "verification.run",
|
|
7634
7674
|
pack: "development",
|
|
7635
7675
|
exposure: "direct",
|
|
7636
|
-
tools: ["lint", "format", "typecheck", "test", "e2e_plan"]
|
|
7676
|
+
tools: ["lint", "format", "typecheck", "test", "e2e_plan", "codebase-targeted-test"]
|
|
7637
7677
|
},
|
|
7638
7678
|
{
|
|
7639
7679
|
id: "version-control.manage",
|
|
@@ -7770,7 +7810,13 @@ var RUNTIME_CAPABILITY_MANIFEST = [
|
|
|
7770
7810
|
id: "quality.analyze",
|
|
7771
7811
|
pack: "development",
|
|
7772
7812
|
exposure: "on-demand",
|
|
7773
|
-
tools: [
|
|
7813
|
+
tools: [
|
|
7814
|
+
"dead_code_scan",
|
|
7815
|
+
"detect_duplicate_code",
|
|
7816
|
+
"secret_scanner_test",
|
|
7817
|
+
"error_lens_history",
|
|
7818
|
+
"security-ast-scan"
|
|
7819
|
+
]
|
|
7774
7820
|
},
|
|
7775
7821
|
{
|
|
7776
7822
|
id: "release.manage",
|
|
@@ -7788,7 +7834,7 @@ var RUNTIME_CAPABILITY_MANIFEST = [
|
|
|
7788
7834
|
id: "tools.discover",
|
|
7789
7835
|
pack: "admin",
|
|
7790
7836
|
exposure: "direct",
|
|
7791
|
-
tools: ["tool_search", "tool_use", "tool_help", "batch_tool_use"]
|
|
7837
|
+
tools: ["tool_search", "tool_use", "tool_help", "batch_tool_use", "clarify"]
|
|
7792
7838
|
},
|
|
7793
7839
|
{
|
|
7794
7840
|
id: "runtime.admin",
|
|
@@ -8228,7 +8274,7 @@ function createAgentResponseHandler(a) {
|
|
|
8228
8274
|
...repaired.report
|
|
8229
8275
|
});
|
|
8230
8276
|
a.logger.warn(
|
|
8231
|
-
`Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ${repaired.report.removedToolResults.length} tool_result block(s), ${repaired.report.removedMessages} empty message(s)`
|
|
8277
|
+
`Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ${repaired.report.removedToolResults.length} tool_result block(s), ${repaired.report.removedMessages} empty message(s)` + formatAdjacencyRepairIds(repaired.report.removedToolUses, repaired.report.removedToolResults)
|
|
8232
8278
|
);
|
|
8233
8279
|
}
|
|
8234
8280
|
}
|
|
@@ -8348,6 +8394,10 @@ function createAgentResponseHandler(a) {
|
|
|
8348
8394
|
}
|
|
8349
8395
|
return { buildAndRunRequestPipeline, processResponse };
|
|
8350
8396
|
}
|
|
8397
|
+
function formatAdjacencyRepairIds(toolUses, toolResults) {
|
|
8398
|
+
const ids = [.../* @__PURE__ */ new Set([...toolUses, ...toolResults])].slice(0, 8);
|
|
8399
|
+
return ids.length > 0 ? `; affected tool ids: ${ids.join(", ")}${toolUses.length + toolResults.length > ids.length ? ", \u2026" : ""}` : "";
|
|
8400
|
+
}
|
|
8351
8401
|
|
|
8352
8402
|
// src/utils/sage-output-block.ts
|
|
8353
8403
|
var SAGE_INJECTOR_HEADINGS = /* @__PURE__ */ new Set([
|
|
@@ -8919,7 +8969,7 @@ var Agent = class {
|
|
|
8919
8969
|
this.maxIterations = init.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
8920
8970
|
this.executionStrategy = init.executionStrategy ?? "smart";
|
|
8921
8971
|
this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
|
|
8922
|
-
this.autoExtendLimit = init.autoExtendLimit ??
|
|
8972
|
+
this.autoExtendLimit = init.autoExtendLimit ?? false;
|
|
8923
8973
|
this.loopDetection = resolveLoopDetection(init.loopDetection);
|
|
8924
8974
|
this.autonomousContinue = init.autonomousContinue ?? false;
|
|
8925
8975
|
this.refreshSystemPrompt = init.refreshSystemPrompt ?? false;
|
|
@@ -9286,9 +9336,6 @@ function resolveContinuation(input) {
|
|
|
9286
9336
|
return { source: "open", text, label: "\u25B6 Continue \u2192 (no pending task \u2014 choosing next step)" };
|
|
9287
9337
|
}
|
|
9288
9338
|
|
|
9289
|
-
// src/core/fallback-model.ts
|
|
9290
|
-
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
9291
|
-
|
|
9292
9339
|
// src/core/model-availability-calendar.ts
|
|
9293
9340
|
function logicalCalendarTarget(providerId, model) {
|
|
9294
9341
|
if (providerId !== "omniroute") return { providerId, model };
|
|
@@ -9776,7 +9823,206 @@ function dedupeChain(entries, current) {
|
|
|
9776
9823
|
);
|
|
9777
9824
|
}
|
|
9778
9825
|
|
|
9826
|
+
// src/core/fallback-doctor.ts
|
|
9827
|
+
function diagnoseFallbackConfig(config, tracker, opts) {
|
|
9828
|
+
const mgr = new FallbackProfileManager(config, { statusTracker: tracker });
|
|
9829
|
+
const primary = { providerId: config.provider, model: config.model };
|
|
9830
|
+
const activeProfile = mgr.activeProfileName();
|
|
9831
|
+
const explicitChain = Object.freeze([...config.fallbackModels ?? []]);
|
|
9832
|
+
const effectiveCandidates = mgr.resolveCandidates(primary, {
|
|
9833
|
+
fallbackProfile: activeProfile
|
|
9834
|
+
});
|
|
9835
|
+
const effectiveOrder = Object.freeze(
|
|
9836
|
+
effectiveCandidates.map((c) => `${c.providerId}/${c.model}`)
|
|
9837
|
+
);
|
|
9838
|
+
const autoEnabled = config.fallbackAuto !== false;
|
|
9839
|
+
const warnings = [];
|
|
9840
|
+
const providersInChain = /* @__PURE__ */ new Set();
|
|
9841
|
+
for (const entry of effectiveCandidates) {
|
|
9842
|
+
providersInChain.add(entry.providerId);
|
|
9843
|
+
}
|
|
9844
|
+
if (effectiveCandidates.length === 0) {
|
|
9845
|
+
warnings.push({
|
|
9846
|
+
code: "EMPTY_CHAIN",
|
|
9847
|
+
severity: "critical",
|
|
9848
|
+
message: "The effective fallback chain is empty. Any rate limit or outage on the primary model will immediately fail without recovery.",
|
|
9849
|
+
recommendation: "Add fallback models using /fallback add <provider/model> or enable smart defaults with /fallback auto on."
|
|
9850
|
+
});
|
|
9851
|
+
}
|
|
9852
|
+
if (effectiveCandidates.length > 0) {
|
|
9853
|
+
const onlyPrimaryProvider = providersInChain.size === 1 && providersInChain.has(primary.providerId);
|
|
9854
|
+
if (onlyPrimaryProvider) {
|
|
9855
|
+
warnings.push({
|
|
9856
|
+
code: "SIBLING_QUARANTINE_RISK",
|
|
9857
|
+
severity: "warning",
|
|
9858
|
+
message: `All fallback models belong to the same provider ("${primary.providerId}"). An account-level quota or token exhaustion will quarantine all sibling models at once.`,
|
|
9859
|
+
recommendation: "Add at least one cross-provider model (e.g. OpenAI, Anthropic, or Google) to your fallback profile."
|
|
9860
|
+
});
|
|
9861
|
+
}
|
|
9862
|
+
}
|
|
9863
|
+
for (const entry of effectiveCandidates) {
|
|
9864
|
+
const health = mgr.checkProvider(entry.providerId);
|
|
9865
|
+
if (!health.usable) {
|
|
9866
|
+
warnings.push({
|
|
9867
|
+
code: "PROVIDER_UNUSABLE",
|
|
9868
|
+
severity: "critical",
|
|
9869
|
+
target: `${entry.providerId}/${entry.model}`,
|
|
9870
|
+
message: `Provider "${entry.providerId}" is missing an API key or baseUrl in configuration.`,
|
|
9871
|
+
recommendation: `Configure API credentials for "${entry.providerId}" or remove it from the fallback chain.`
|
|
9872
|
+
});
|
|
9873
|
+
}
|
|
9874
|
+
if (tracker && !tracker.isAvailable(entry.providerId, entry.model)) {
|
|
9875
|
+
const status2 = tracker.getStatus(entry.providerId, entry.model);
|
|
9876
|
+
const remainingMs = status2?.stateExpiresAt ? Math.max(0, status2.stateExpiresAt - Date.now()) : 0;
|
|
9877
|
+
const remainingSec = Math.round(remainingMs / 1e3);
|
|
9878
|
+
warnings.push({
|
|
9879
|
+
code: "MODEL_QUARANTINED",
|
|
9880
|
+
severity: "warning",
|
|
9881
|
+
target: `${entry.providerId}/${entry.model}`,
|
|
9882
|
+
message: `Model is currently in quarantine (waiting room) due to ${status2?.lastErrorKind ?? "failures"}.${remainingSec > 0 ? ` Resets in ${remainingSec}s.` : ""}`,
|
|
9883
|
+
recommendation: "Model will be automatically re-admitted once cooldown/reset expires."
|
|
9884
|
+
});
|
|
9885
|
+
}
|
|
9886
|
+
const cal = evaluateModelCalendar(
|
|
9887
|
+
config.modelAvailabilitySchedule,
|
|
9888
|
+
entry.providerId,
|
|
9889
|
+
entry.model
|
|
9890
|
+
);
|
|
9891
|
+
if (!cal.allowed) {
|
|
9892
|
+
warnings.push({
|
|
9893
|
+
code: "CALENDAR_BLOCKED",
|
|
9894
|
+
severity: "warning",
|
|
9895
|
+
target: `${entry.providerId}/${entry.model}`,
|
|
9896
|
+
message: `Model is blocked by the model availability calendar (${cal.rule?.label ?? "schedule restriction"}).`
|
|
9897
|
+
});
|
|
9898
|
+
}
|
|
9899
|
+
if (opts?.modelContextLimitMap) {
|
|
9900
|
+
const primaryLimit = opts.modelContextLimitMap[`${primary.providerId}/${primary.model}`] ?? opts.modelContextLimitMap[primary.model];
|
|
9901
|
+
const entryLimit = opts.modelContextLimitMap[`${entry.providerId}/${entry.model}`] ?? opts.modelContextLimitMap[entry.model];
|
|
9902
|
+
if (typeof primaryLimit === "number" && typeof entryLimit === "number" && entryLimit > 0 && primaryLimit > 0 && entryLimit < primaryLimit) {
|
|
9903
|
+
warnings.push({
|
|
9904
|
+
code: "CONTEXT_WINDOW_WARNING",
|
|
9905
|
+
severity: "warning",
|
|
9906
|
+
target: `${entry.providerId}/${entry.model}`,
|
|
9907
|
+
message: `Context window downgrade: primary model has ${primaryLimit.toLocaleString()} tokens capacity, but fallback candidate has only ${entryLimit.toLocaleString()} tokens. Long sessions will fail failover or require aggressive compaction.`,
|
|
9908
|
+
recommendation: `Ensure fallback models have equal or greater context capacity (>= ${primaryLimit.toLocaleString()} tokens) or keep sessions below ${entryLimit.toLocaleString()} tokens.`
|
|
9909
|
+
});
|
|
9910
|
+
}
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
const uniqueProviders = Object.freeze([...providersInChain]);
|
|
9914
|
+
const crossProviderCount = uniqueProviders.filter((p) => p !== primary.providerId).length;
|
|
9915
|
+
const hasCritical = warnings.some((w) => w.severity === "critical");
|
|
9916
|
+
const hasWarning = warnings.some((w) => w.severity === "warning");
|
|
9917
|
+
const status = hasCritical ? "critical" : hasWarning ? "warning" : "healthy";
|
|
9918
|
+
return Object.freeze({
|
|
9919
|
+
primary: Object.freeze(primary),
|
|
9920
|
+
activeProfile,
|
|
9921
|
+
explicitChain,
|
|
9922
|
+
effectiveOrder,
|
|
9923
|
+
autoEnabled,
|
|
9924
|
+
status,
|
|
9925
|
+
warnings: Object.freeze(warnings),
|
|
9926
|
+
crossProviderCount,
|
|
9927
|
+
uniqueProviders
|
|
9928
|
+
});
|
|
9929
|
+
}
|
|
9930
|
+
function simulateFallbackFailover(config, opts = {}) {
|
|
9931
|
+
const errorKind = opts.errorKind ?? "rate_limit";
|
|
9932
|
+
const isEligible = isFallbackWorthy(errorKind);
|
|
9933
|
+
const primary = { providerId: config.provider, model: config.model };
|
|
9934
|
+
if (!isEligible) {
|
|
9935
|
+
return Object.freeze({
|
|
9936
|
+
triggeringError: errorKind,
|
|
9937
|
+
isFallbackEligible: false,
|
|
9938
|
+
primary: Object.freeze(primary),
|
|
9939
|
+
steps: Object.freeze([]),
|
|
9940
|
+
finalTarget: null,
|
|
9941
|
+
summary: `Error kind "${errorKind}" is not fallback-worthy (request-shaped error, e.g. context_overflow/auth). The agent loop will not attempt fallback.`
|
|
9942
|
+
});
|
|
9943
|
+
}
|
|
9944
|
+
const mgr = new FallbackProfileManager(config, { statusTracker: opts.statusTracker });
|
|
9945
|
+
const candidates = mgr.resolveCandidates(primary);
|
|
9946
|
+
const steps = [];
|
|
9947
|
+
let finalTarget = null;
|
|
9948
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
9949
|
+
const entry = candidates[i];
|
|
9950
|
+
const key = `${entry.providerId}/${entry.model}`;
|
|
9951
|
+
const providerSwitched = entry.providerId !== primary.providerId;
|
|
9952
|
+
const cal = evaluateModelCalendar(
|
|
9953
|
+
config.modelAvailabilitySchedule,
|
|
9954
|
+
entry.providerId,
|
|
9955
|
+
entry.model
|
|
9956
|
+
);
|
|
9957
|
+
if (!cal.allowed) {
|
|
9958
|
+
steps.push({
|
|
9959
|
+
index: i + 1,
|
|
9960
|
+
providerId: entry.providerId,
|
|
9961
|
+
model: entry.model,
|
|
9962
|
+
skipped: true,
|
|
9963
|
+
reason: `Blocked by model availability calendar (${cal.rule?.label ?? "schedule"})`,
|
|
9964
|
+
providerSwitched
|
|
9965
|
+
});
|
|
9966
|
+
continue;
|
|
9967
|
+
}
|
|
9968
|
+
if (opts.statusTracker && !opts.statusTracker.isAvailable(entry.providerId, entry.model)) {
|
|
9969
|
+
steps.push({
|
|
9970
|
+
index: i + 1,
|
|
9971
|
+
providerId: entry.providerId,
|
|
9972
|
+
model: entry.model,
|
|
9973
|
+
skipped: true,
|
|
9974
|
+
reason: "Quarantined in status tracker waiting room",
|
|
9975
|
+
providerSwitched
|
|
9976
|
+
});
|
|
9977
|
+
continue;
|
|
9978
|
+
}
|
|
9979
|
+
const contextLimit = opts.modelContextLimitMap?.[key];
|
|
9980
|
+
if (typeof opts.currentTokens === "number" && opts.currentTokens > 0 && typeof contextLimit === "number" && contextLimit > 0 && opts.currentTokens > contextLimit) {
|
|
9981
|
+
steps.push({
|
|
9982
|
+
index: i + 1,
|
|
9983
|
+
providerId: entry.providerId,
|
|
9984
|
+
model: entry.model,
|
|
9985
|
+
skipped: true,
|
|
9986
|
+
reason: `Context pre-filter: request tokens (${opts.currentTokens}) exceeds model window (${contextLimit})`,
|
|
9987
|
+
providerSwitched
|
|
9988
|
+
});
|
|
9989
|
+
continue;
|
|
9990
|
+
}
|
|
9991
|
+
const health = mgr.checkProvider(entry.providerId);
|
|
9992
|
+
if (!health.usable) {
|
|
9993
|
+
steps.push({
|
|
9994
|
+
index: i + 1,
|
|
9995
|
+
providerId: entry.providerId,
|
|
9996
|
+
model: entry.model,
|
|
9997
|
+
skipped: true,
|
|
9998
|
+
reason: `Cannot construct provider "${entry.providerId}" (missing API key/endpoint)`,
|
|
9999
|
+
providerSwitched
|
|
10000
|
+
});
|
|
10001
|
+
continue;
|
|
10002
|
+
}
|
|
10003
|
+
steps.push({
|
|
10004
|
+
index: i + 1,
|
|
10005
|
+
providerId: entry.providerId,
|
|
10006
|
+
model: entry.model,
|
|
10007
|
+
skipped: false,
|
|
10008
|
+
providerSwitched
|
|
10009
|
+
});
|
|
10010
|
+
finalTarget = { providerId: entry.providerId, model: entry.model };
|
|
10011
|
+
break;
|
|
10012
|
+
}
|
|
10013
|
+
const summary = finalTarget ? `Failover succeeded: will rotate from ${primary.providerId}/${primary.model} to ${finalTarget.providerId}/${finalTarget.model}.` : "Failover failed: no usable fallback candidate could be reached.";
|
|
10014
|
+
return Object.freeze({
|
|
10015
|
+
triggeringError: errorKind,
|
|
10016
|
+
isFallbackEligible: true,
|
|
10017
|
+
primary: Object.freeze(primary),
|
|
10018
|
+
steps: Object.freeze(steps),
|
|
10019
|
+
finalTarget: finalTarget ? Object.freeze(finalTarget) : null,
|
|
10020
|
+
summary
|
|
10021
|
+
});
|
|
10022
|
+
}
|
|
10023
|
+
|
|
9779
10024
|
// src/core/fallback-model.ts
|
|
10025
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
9780
10026
|
function fallbackProfileChain(config, profileName) {
|
|
9781
10027
|
if (!profileName) return [];
|
|
9782
10028
|
const mgr = new FallbackProfileManager(config);
|
|
@@ -9911,7 +10157,25 @@ function createFallbackModelExtension(deps) {
|
|
|
9911
10157
|
if (!evaluateModelCalendar(cfg.modelAvailabilitySchedule, primary.providerId, primary.model).allowed || deps.statusTracker && !deps.statusTracker.isAvailable(primary.providerId, primary.model))
|
|
9912
10158
|
return;
|
|
9913
10159
|
try {
|
|
9914
|
-
|
|
10160
|
+
const primaryProvider = await deps.buildProvider(primary.providerId, primary.model);
|
|
10161
|
+
const currentTokens = ctx.lastRequestTokens;
|
|
10162
|
+
const maxContext = maxContextOf(primaryProvider);
|
|
10163
|
+
if (maxContext > 0 && typeof currentTokens === "number" && currentTokens > maxContext) {
|
|
10164
|
+
deps.events.emit("provider.primary_probe_context_blocked", {
|
|
10165
|
+
sessionId: resolveEventSessionId(ctx),
|
|
10166
|
+
providerId: primary.providerId,
|
|
10167
|
+
model: primary.model,
|
|
10168
|
+
currentTokens,
|
|
10169
|
+
maxContext,
|
|
10170
|
+
timestamp: now()
|
|
10171
|
+
});
|
|
10172
|
+
deps.logger?.warn(
|
|
10173
|
+
`fallback-model: deferring primary probe "${primary.providerId}/${primary.model}" \u2014 context (${currentTokens}) exceeds target window (${maxContext})`
|
|
10174
|
+
);
|
|
10175
|
+
markPrimaryFailure(cfg);
|
|
10176
|
+
return;
|
|
10177
|
+
}
|
|
10178
|
+
ctx.provider = primaryProvider;
|
|
9915
10179
|
ctx.model = primary.model;
|
|
9916
10180
|
await deps.onModelSwitch?.(primary.providerId, primary.model);
|
|
9917
10181
|
primaryBlockedUntil = 0;
|
|
@@ -10025,9 +10289,10 @@ function createFallbackModelExtension(deps) {
|
|
|
10025
10289
|
const status = shouldFallback(firstErr_);
|
|
10026
10290
|
if (status === null) throw firstErr_;
|
|
10027
10291
|
let gateRequestId;
|
|
10028
|
-
|
|
10292
|
+
const configuredGateSeconds = cfg.fallbackGateSeconds ?? deps.fallbackGateSeconds;
|
|
10293
|
+
if (configuredGateSeconds !== 0 && deps.fallbackGate && usableChain.length > 0) {
|
|
10029
10294
|
gateRequestId = randomUUID6();
|
|
10030
|
-
const autoSwitchSeconds = Math.max(1,
|
|
10295
|
+
const autoSwitchSeconds = Math.max(1, configuredGateSeconds ?? 7);
|
|
10031
10296
|
const gateCandidates = usableChain.map((e) => ({
|
|
10032
10297
|
providerId: e.providerId,
|
|
10033
10298
|
model: e.model
|
|
@@ -10854,6 +11119,7 @@ function shellGuidanceBlock(shell, detail) {
|
|
|
10854
11119
|
var MAX_ENVIRONMENT_CACHE_ENTRIES = 16;
|
|
10855
11120
|
async function buildEnvironment(ctx, env) {
|
|
10856
11121
|
const modelCapabilities = env.modelCapabilities;
|
|
11122
|
+
const today = env.todayIso ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10857
11123
|
const cacheKey = [
|
|
10858
11124
|
ctx.projectRoot,
|
|
10859
11125
|
ctx.provider ?? "",
|
|
@@ -10862,7 +11128,9 @@ async function buildEnvironment(ctx, env) {
|
|
|
10862
11128
|
modelCapabilities?.supportsTools ? 1 : 0,
|
|
10863
11129
|
modelCapabilities?.supportsVision ? 1 : 0,
|
|
10864
11130
|
modelCapabilities?.supportsReasoning ? 1 : 0,
|
|
10865
|
-
env.skillCache ?? ""
|
|
11131
|
+
env.skillCache ?? "",
|
|
11132
|
+
today,
|
|
11133
|
+
env.modeId ?? ""
|
|
10866
11134
|
].join("\0");
|
|
10867
11135
|
const cached = env.envCacheByRoot.get(cacheKey);
|
|
10868
11136
|
if (cached) {
|
|
@@ -10870,7 +11138,6 @@ async function buildEnvironment(ctx, env) {
|
|
|
10870
11138
|
env.envCacheByRoot.set(cacheKey, cached);
|
|
10871
11139
|
return cached;
|
|
10872
11140
|
}
|
|
10873
|
-
const today = env.todayIso ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10874
11141
|
const platform2 = `${os3.platform()} ${os3.release()}`;
|
|
10875
11142
|
const effShell = effectiveShell(os3.platform(), process.env["WRONGSTACK_SHELL"]);
|
|
10876
11143
|
const shell = effShell === "posix" ? process.env.SHELL ?? process.env.ComSpec ?? "unknown" : SHELL_DISPLAY[effShell];
|
|
@@ -11716,7 +11983,9 @@ ${peers}
|
|
|
11716
11983
|
const tpl = tplCtx ?? this.templateContext(ctx);
|
|
11717
11984
|
const section = (key, vars = {}) => instructionSection(instructions, key, vars, tpl);
|
|
11718
11985
|
const tier = this.tier;
|
|
11719
|
-
|
|
11986
|
+
const subagent = ctx.subagent === true;
|
|
11987
|
+
const maxContextTokens = this.modelCapabilities()?.maxContextTokens ?? 0;
|
|
11988
|
+
if (this._toolsUsageCache?.toolsRef === tools && this._toolsUsageCache?.tier === tier && this._toolsUsageCache?.subagent === subagent && this._toolsUsageCache?.maxContextTokens === maxContextTokens) {
|
|
11720
11989
|
return this._toolsUsageCache.text;
|
|
11721
11990
|
}
|
|
11722
11991
|
const byCat = /* @__PURE__ */ new Map();
|
|
@@ -11843,7 +12112,7 @@ ${hint.trim()}`);
|
|
|
11843
12112
|
}
|
|
11844
12113
|
}
|
|
11845
12114
|
const text = lines.join("\n");
|
|
11846
|
-
this._toolsUsageCache = { toolsRef: tools, tier, text };
|
|
12115
|
+
this._toolsUsageCache = { toolsRef: tools, tier, subagent, maxContextTokens, text };
|
|
11847
12116
|
return text;
|
|
11848
12117
|
}
|
|
11849
12118
|
renderOnlineAgents(agents) {
|
|
@@ -11906,6 +12175,7 @@ export {
|
|
|
11906
12175
|
createDefaultPipelines,
|
|
11907
12176
|
createFallbackModelExtension,
|
|
11908
12177
|
detectContinueIntent,
|
|
12178
|
+
diagnoseFallbackConfig,
|
|
11909
12179
|
effectiveFallbackChain,
|
|
11910
12180
|
fallbackProfileChain,
|
|
11911
12181
|
formatModelRef,
|
|
@@ -11924,6 +12194,7 @@ export {
|
|
|
11924
12194
|
runtimeFallbackChain,
|
|
11925
12195
|
setBtwNote,
|
|
11926
12196
|
setQueuedMessagesSnapshot,
|
|
12197
|
+
simulateFallbackFailover,
|
|
11927
12198
|
smartDefaultFallbackChain,
|
|
11928
12199
|
wrapAsState,
|
|
11929
12200
|
writePendingNextSteps
|