open-multi-agent-kit 0.96.0 → 0.96.1
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/CHANGELOG.md +40 -0
- package/README.md +24 -6
- package/dist/commands/doctor-provider.d.ts.map +1 -1
- package/dist/commands/doctor-provider.js +15 -20
- package/dist/commands/doctor-provider.js.map +1 -1
- package/dist/commands/sdk-session-cli.d.ts +24 -0
- package/dist/commands/sdk-session-cli.d.ts.map +1 -0
- package/dist/commands/sdk-session-cli.js +225 -0
- package/dist/commands/sdk-session-cli.js.map +1 -0
- package/dist/core/advisory-judge-model.d.ts +18 -0
- package/dist/core/advisory-judge-model.d.ts.map +1 -0
- package/dist/core/advisory-judge-model.js +168 -0
- package/dist/core/advisory-judge-model.js.map +1 -0
- package/dist/core/advisory-judge-response.d.ts +8 -0
- package/dist/core/advisory-judge-response.d.ts.map +1 -0
- package/dist/core/advisory-judge-response.js +61 -0
- package/dist/core/advisory-judge-response.js.map +1 -0
- package/dist/core/advisory-judge-types.d.ts +53 -0
- package/dist/core/advisory-judge-types.d.ts.map +1 -0
- package/dist/core/advisory-judge-types.js +2 -0
- package/dist/core/advisory-judge-types.js.map +1 -0
- package/dist/core/advisory-judge.d.ts +8 -0
- package/dist/core/advisory-judge.d.ts.map +1 -0
- package/dist/core/advisory-judge.js +178 -0
- package/dist/core/advisory-judge.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +107 -174
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +28 -4
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction-gate.d.ts +28 -0
- package/dist/core/compaction-gate.d.ts.map +1 -0
- package/dist/core/compaction-gate.js +34 -0
- package/dist/core/compaction-gate.js.map +1 -0
- package/dist/core/context-budget-system-prompt-items.d.ts.map +1 -1
- package/dist/core/context-budget-system-prompt-items.js +5 -3
- package/dist/core/context-budget-system-prompt-items.js.map +1 -1
- package/dist/core/domain-loadouts.d.ts.map +1 -1
- package/dist/core/domain-loadouts.js +2 -2
- package/dist/core/domain-loadouts.js.map +1 -1
- package/dist/core/durable-goal-checkpoint.d.ts +6 -0
- package/dist/core/durable-goal-checkpoint.d.ts.map +1 -0
- package/dist/core/durable-goal-checkpoint.js +195 -0
- package/dist/core/durable-goal-checkpoint.js.map +1 -0
- package/dist/core/durable-goal-parse.d.ts +3 -0
- package/dist/core/durable-goal-parse.d.ts.map +1 -0
- package/dist/core/durable-goal-parse.js +86 -0
- package/dist/core/durable-goal-parse.js.map +1 -0
- package/dist/core/durable-goal-reducer.d.ts +3 -0
- package/dist/core/durable-goal-reducer.d.ts.map +1 -0
- package/dist/core/durable-goal-reducer.js +175 -0
- package/dist/core/durable-goal-reducer.js.map +1 -0
- package/dist/core/durable-goal-store.d.ts +19 -0
- package/dist/core/durable-goal-store.d.ts.map +1 -0
- package/dist/core/durable-goal-store.js +126 -0
- package/dist/core/durable-goal-store.js.map +1 -0
- package/dist/core/durable-goal.d.ts +92 -0
- package/dist/core/durable-goal.d.ts.map +1 -0
- package/dist/core/durable-goal.js +62 -0
- package/dist/core/durable-goal.js.map +1 -0
- package/dist/core/extensions/builtin/goal-controller.d.ts +3 -0
- package/dist/core/extensions/builtin/goal-controller.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal-controller.js +125 -0
- package/dist/core/extensions/builtin/goal-controller.js.map +1 -0
- package/dist/core/extensions/builtin/identical-loop.d.ts +3 -0
- package/dist/core/extensions/builtin/identical-loop.d.ts.map +1 -0
- package/dist/core/extensions/builtin/identical-loop.js +31 -0
- package/dist/core/extensions/builtin/identical-loop.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset.js +13 -0
- package/dist/core/extensions/builtin/prompt-preset.js.map +1 -0
- package/dist/core/extensions/builtin/tool-pair-repair.d.ts +3 -0
- package/dist/core/extensions/builtin/tool-pair-repair.d.ts.map +1 -0
- package/dist/core/extensions/builtin/tool-pair-repair.js +12 -0
- package/dist/core/extensions/builtin/tool-pair-repair.js.map +1 -0
- package/dist/core/extensions/types.d.ts +6 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/goal-continuation.d.ts +13 -0
- package/dist/core/goal-continuation.d.ts.map +1 -0
- package/dist/core/goal-continuation.js +10 -0
- package/dist/core/goal-continuation.js.map +1 -0
- package/dist/core/grok-harness.d.ts +1 -1
- package/dist/core/grok-harness.d.ts.map +1 -1
- package/dist/core/grok-harness.js +1 -1
- package/dist/core/grok-harness.js.map +1 -1
- package/dist/core/grok-playbook.d.ts +1 -1
- package/dist/core/grok-playbook.d.ts.map +1 -1
- package/dist/core/grok-playbook.js +1 -1
- package/dist/core/grok-playbook.js.map +1 -1
- package/dist/core/identical-loop.d.ts +15 -0
- package/dist/core/identical-loop.d.ts.map +1 -0
- package/dist/core/identical-loop.js +34 -0
- package/dist/core/identical-loop.js.map +1 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/model-prompt-policy.d.ts +11 -0
- package/dist/core/model-prompt-policy.d.ts.map +1 -0
- package/dist/core/model-prompt-policy.js +17 -0
- package/dist/core/model-prompt-policy.js.map +1 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +16 -4
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +2 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/prompt-budget.d.ts +21 -0
- package/dist/core/prompt-budget.d.ts.map +1 -0
- package/dist/core/prompt-budget.js +51 -0
- package/dist/core/prompt-budget.js.map +1 -0
- package/dist/core/prompt-cache.d.ts +14 -0
- package/dist/core/prompt-cache.d.ts.map +1 -0
- package/dist/core/prompt-cache.js +16 -0
- package/dist/core/prompt-cache.js.map +1 -0
- package/dist/core/prompt-preset.d.ts +8 -0
- package/dist/core/prompt-preset.d.ts.map +1 -0
- package/dist/core/prompt-preset.js +56 -0
- package/dist/core/prompt-preset.js.map +1 -0
- package/dist/core/provider-resilience.d.ts +16 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +23 -1
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/provider-retry.d.ts +29 -0
- package/dist/core/provider-retry.d.ts.map +1 -0
- package/dist/core/provider-retry.js +49 -0
- package/dist/core/provider-retry.js.map +1 -0
- package/dist/core/provider-usage.d.ts +2 -1
- package/dist/core/provider-usage.d.ts.map +1 -1
- package/dist/core/provider-usage.js +40 -6
- package/dist/core/provider-usage.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +32 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sandbox/backend.d.ts +12 -0
- package/dist/core/sandbox/backend.d.ts.map +1 -1
- package/dist/core/sandbox/backend.js +52 -42
- package/dist/core/sandbox/backend.js.map +1 -1
- package/dist/core/sandbox/policy.d.ts +2 -0
- package/dist/core/sandbox/policy.d.ts.map +1 -1
- package/dist/core/sandbox/policy.js +2 -1
- package/dist/core/sandbox/policy.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-system-prompt.d.ts +28 -0
- package/dist/core/session-system-prompt.d.ts.map +1 -0
- package/dist/core/session-system-prompt.js +37 -0
- package/dist/core/session-system-prompt.js.map +1 -0
- package/dist/core/strict-record.d.ts +2 -0
- package/dist/core/strict-record.d.ts.map +1 -0
- package/dist/core/strict-record.js +9 -0
- package/dist/core/strict-record.js.map +1 -0
- package/dist/core/tool-pair-repair.d.ts +11 -0
- package/dist/core/tool-pair-repair.d.ts.map +1 -0
- package/dist/core/tool-pair-repair.js +38 -0
- package/dist/core/tool-pair-repair.js.map +1 -0
- package/dist/core/tools/artifact-spill.d.ts +13 -0
- package/dist/core/tools/artifact-spill.d.ts.map +1 -0
- package/dist/core/tools/artifact-spill.js +26 -0
- package/dist/core/tools/artifact-spill.js.map +1 -0
- package/dist/core/tools/bash.d.ts +7 -4
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +8 -7
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/read.d.ts +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +10 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +2 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +8 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +14 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/docs/adaptorch-preview-spec.md +1 -1
- package/docs/adaptorch-preview.md +2 -2
- package/docs/book-to-skill.md +1 -1
- package/docs/containerization.md +9 -0
- package/docs/docs.json +11 -3
- package/docs/environment-variables.md +11 -4
- package/docs/extensions.md +14 -1
- package/docs/grok-harness.md +35 -12
- package/docs/index.md +7 -6
- package/docs/loadout-domains/grok-harness.md +2 -2
- package/docs/models.md +10 -9
- package/docs/provider-resilience.md +9 -3
- package/docs/providers.md +9 -4
- package/docs/run-protocol.md +32 -0
- package/docs/sdk.md +96 -3
- package/docs/security.md +22 -32
- package/docs/sessions.md +14 -0
- package/docs/usage.md +37 -15
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +6 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -6
|
@@ -16,7 +16,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
16
16
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { basename, dirname, join as joinPath } from "node:path";
|
|
18
18
|
import { isDeepStrictEqual } from "node:util";
|
|
19
|
-
import { getVisionRouteModel
|
|
19
|
+
import { getVisionRouteModel } from "omk-agent-core";
|
|
20
20
|
import { clampThinkingLevel, cleanupSessionResources, deriveContextPromptCacheKey, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "omk-ai";
|
|
21
21
|
import { APP_NAME, VERSION } from "../config.js";
|
|
22
22
|
import { theme } from "../modes/interactive/theme/theme.js";
|
|
@@ -32,6 +32,7 @@ import { getCompactionHeadroomThreshold } from "./compaction/compaction.js";
|
|
|
32
32
|
import { createCompactionHysteresisConfig, createCompactionHysteresisState, stepCompactionHysteresis, } from "./compaction/hysteresis.js";
|
|
33
33
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateProjectedContextTokens, generateBranchSummary, prepareCompaction, resolveCompactionModel, } from "./compaction/index.js";
|
|
34
34
|
import { compactionEmitWillRetry } from "./compaction/resume-policy.js";
|
|
35
|
+
import { isSessionModelOverflow, shouldSkipCompactionCheck } from "./compaction-gate.js";
|
|
35
36
|
import { estimateToolResultReserve, } from "./context-budget-reserved-tokens.js";
|
|
36
37
|
import { createDiskContextBudgetCacheProviderV2, DiskContextBudgetCacheProviderV2, } from "./context-budget-v2-cache-disk.js";
|
|
37
38
|
import { applyContextCacheInvalidation, createContextCacheInvalidationSnapshot, } from "./context-budget-v2-cache-invalidation.js";
|
|
@@ -70,9 +71,13 @@ function firstTextContent(result) {
|
|
|
70
71
|
return undefined;
|
|
71
72
|
}
|
|
72
73
|
import { redactCredentialShapedContent } from "./compaction/transaction.js";
|
|
74
|
+
import { selectContextFilesForModel } from "./model-prompt-policy.js";
|
|
73
75
|
import { findExactModelReferenceMatch } from "./model-resolver.js";
|
|
76
|
+
import { computePromptTokenBudget } from "./prompt-budget.js";
|
|
77
|
+
import { classifyPromptCacheTransition } from "./prompt-cache.js";
|
|
74
78
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
75
|
-
import {
|
|
79
|
+
import { isQuotaExhaustionMessage, isStickySafetyModel, pickFailoverCandidate, resolveProviderResilience, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
|
|
80
|
+
import { computeRetryDelayMs, failoverModelKey, isFailoverTriggerError, isRetryableAssistantError, nextRetryAttempt, retryBudgetForAssistantError, } from "./provider-retry.js";
|
|
76
81
|
import { getBiasStepsForCell, parseRouterBiasSnapshot } from "./reasoning-router-bias.js";
|
|
77
82
|
import { classifyTaskV4, deriveRouterFeedbackFeaturesV4, resolveThinkingLevelV4WithUncertainty, TASK_CLASS_THINKING_LEVELS_V4, } from "./reasoning-router-v4.js";
|
|
78
83
|
import { redactSensitiveText, redactSensitiveTextForced } from "./redaction.js";
|
|
@@ -84,6 +89,7 @@ import { SessionBashService } from "./session-bash-service.js";
|
|
|
84
89
|
import { SessionCompactionService } from "./session-compaction-service.js";
|
|
85
90
|
import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
|
|
86
91
|
import { acquireSessionOwnerLeaseSync } from "./session-owner-lease.js";
|
|
92
|
+
import { assembleSessionSystemPrompt } from "./session-system-prompt.js";
|
|
87
93
|
import { classifySessionTermination, } from "./session-termination.js";
|
|
88
94
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
89
95
|
import { buildSystemPromptPlan } from "./system-prompt.js";
|
|
@@ -315,6 +321,8 @@ export class AgentSession {
|
|
|
315
321
|
// v10.3-Ω: models that already refused/failed this turn — failover must advance,
|
|
316
322
|
// not re-pick the same candidate (claude-opus-5 → grok → grok → grok loop fix).
|
|
317
323
|
_refusedModels = new Set();
|
|
324
|
+
/** True when `--model` / `--provider` pinned this process to one model. */
|
|
325
|
+
_modelPinned;
|
|
318
326
|
// Bash execution state
|
|
319
327
|
_bashService;
|
|
320
328
|
_compactionService;
|
|
@@ -383,6 +391,7 @@ export class AgentSession {
|
|
|
383
391
|
this.sessionManager = config.sessionManager;
|
|
384
392
|
this.settingsManager = config.settingsManager;
|
|
385
393
|
this._scopedModels = config.scopedModels ?? [];
|
|
394
|
+
this._modelPinned = config.modelPinned === true;
|
|
386
395
|
this._resourceLoader = config.resourceLoader;
|
|
387
396
|
this._customTools = config.customTools ?? [];
|
|
388
397
|
this._cwd = config.cwd;
|
|
@@ -1043,13 +1052,14 @@ export class AgentSession {
|
|
|
1043
1052
|
};
|
|
1044
1053
|
_willRetryAfterAgentEnd(event) {
|
|
1045
1054
|
const settings = this.settingsManager.getRetrySettings();
|
|
1046
|
-
if (!settings.enabled
|
|
1055
|
+
if (!settings.enabled)
|
|
1047
1056
|
return false;
|
|
1048
|
-
}
|
|
1049
1057
|
for (let i = event.messages.length - 1; i >= 0; i--) {
|
|
1050
1058
|
const message = event.messages[i];
|
|
1051
1059
|
if (message.role === "assistant") {
|
|
1052
|
-
|
|
1060
|
+
const assistant = message;
|
|
1061
|
+
const maxRetries = retryBudgetForAssistantError(assistant, settings.maxRetries);
|
|
1062
|
+
return this._retryAttempt < maxRetries && this._isRetryableError(assistant);
|
|
1053
1063
|
}
|
|
1054
1064
|
}
|
|
1055
1065
|
return false;
|
|
@@ -1554,7 +1564,7 @@ export class AgentSession {
|
|
|
1554
1564
|
}
|
|
1555
1565
|
return undefined;
|
|
1556
1566
|
}
|
|
1557
|
-
_getContextBudgetOptions(queryContext = this._extractCurrentQuery()) {
|
|
1567
|
+
_getContextBudgetOptions(queryContext = this._extractCurrentQuery(), model = this.model) {
|
|
1558
1568
|
const contextGovernorOverride = process.env.OMK_CONTEXT_GOVERNOR;
|
|
1559
1569
|
if (contextGovernorOverride === "0") {
|
|
1560
1570
|
return undefined;
|
|
@@ -1562,56 +1572,24 @@ export class AgentSession {
|
|
|
1562
1572
|
if (contextGovernorOverride !== "1" && !this.settingsManager.getContextBudgetEnabled()) {
|
|
1563
1573
|
return undefined;
|
|
1564
1574
|
}
|
|
1565
|
-
const contextWindow =
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
this._computeResponseReserve(contextWindow);
|
|
1577
|
-
}
|
|
1578
|
-
else if (contextWindow > 0) {
|
|
1579
|
-
// Dynamic: derive from model contextWindow
|
|
1580
|
-
const envPromptRatio = parsePositiveFloatEnv("OMK_CONTEXT_GOVERNOR_PROMPT_RATIO");
|
|
1581
|
-
const envResponseRatio = parsePositiveFloatEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RATIO");
|
|
1582
|
-
responseReserveTokens =
|
|
1583
|
-
parsePositiveIntegerEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RESERVE_TOKENS") ??
|
|
1584
|
-
this._computeResponseReserve(contextWindow, envResponseRatio);
|
|
1585
|
-
const safetyMargin = Math.floor(contextWindow * SAFETY_MARGIN_RATIO);
|
|
1586
|
-
if (envPromptRatio !== undefined && envPromptRatio > 0 && envPromptRatio < 1) {
|
|
1587
|
-
maxPromptTokens = Math.floor(contextWindow * envPromptRatio);
|
|
1588
|
-
}
|
|
1589
|
-
else {
|
|
1590
|
-
// Default: contextWindow minus reserves and safety margin
|
|
1591
|
-
maxPromptTokens = contextWindow - responseReserveTokens - safetyMargin;
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
else {
|
|
1595
|
-
// No model info — legacy fallback
|
|
1596
|
-
maxPromptTokens = LEGACY_MAX_PROMPT_TOKENS;
|
|
1597
|
-
responseReserveTokens =
|
|
1598
|
-
parsePositiveIntegerEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RESERVE_TOKENS") ?? LEGACY_RESPONSE_RESERVE_TOKENS;
|
|
1599
|
-
}
|
|
1575
|
+
const contextWindow = model?.contextWindow ?? 0;
|
|
1576
|
+
const budget = computePromptTokenBudget({
|
|
1577
|
+
contextWindow,
|
|
1578
|
+
modelMaxTokens: model?.maxTokens,
|
|
1579
|
+
envMaxPromptTokens: parsePositiveIntegerEnv("OMK_CONTEXT_GOVERNOR_MAX_PROMPT_TOKENS"),
|
|
1580
|
+
envResponseReserveTokens: parsePositiveIntegerEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RESERVE_TOKENS"),
|
|
1581
|
+
envPromptRatio: parsePositiveFloatEnv("OMK_CONTEXT_GOVERNOR_PROMPT_RATIO"),
|
|
1582
|
+
envResponseRatio: parsePositiveFloatEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RATIO"),
|
|
1583
|
+
});
|
|
1584
|
+
const maxPromptTokens = budget.maxPromptTokens;
|
|
1585
|
+
const responseReserveTokens = budget.responseReserveTokens;
|
|
1600
1586
|
const cacheProvider = this._contextBudgetCacheProvider ?? this._createContextBudgetCacheProvider();
|
|
1601
1587
|
cacheProvider.setInvalidationSnapshot?.(this._contextCacheInvalidationSnapshot);
|
|
1602
1588
|
this._contextBudgetCacheProvider = cacheProvider;
|
|
1603
|
-
// Enforce floor
|
|
1604
|
-
if (maxPromptTokens < MIN_PROMPT_TOKENS) {
|
|
1605
|
-
maxPromptTokens = MIN_PROMPT_TOKENS;
|
|
1606
|
-
}
|
|
1607
|
-
// Ensure responseReserve does not exceed maxPromptTokens
|
|
1608
|
-
if (responseReserveTokens >= maxPromptTokens) {
|
|
1609
|
-
responseReserveTokens = Math.max(Math.floor(maxPromptTokens / 4), LEGACY_RESPONSE_RESERVE_TOKENS);
|
|
1610
|
-
}
|
|
1611
1589
|
return {
|
|
1612
1590
|
maxPromptTokens,
|
|
1613
1591
|
responseReserveTokens,
|
|
1614
|
-
modelId:
|
|
1592
|
+
modelId: model?.id ?? "unknown",
|
|
1615
1593
|
tokenizerMode: parseTokenizerModeEnv(process.env.OMK_CONTEXT_GOVERNOR_TOKENIZER),
|
|
1616
1594
|
activeSkillNames: parseCommaSeparatedEnv(process.env.OMK_CONTEXT_GOVERNOR_ACTIVE_SKILLS),
|
|
1617
1595
|
queryContext,
|
|
@@ -1622,15 +1600,6 @@ export class AgentSession {
|
|
|
1622
1600
|
* Compute responseReserveTokens from contextWindow.
|
|
1623
1601
|
* Prefers the model's own maxTokens when available, otherwise uses a ratio.
|
|
1624
1602
|
*/
|
|
1625
|
-
_computeResponseReserve(contextWindow, overrideRatio) {
|
|
1626
|
-
// Prefer model's maxTokens (actual output limit) if available and reasonable
|
|
1627
|
-
const modelMaxTokens = this.model?.maxTokens;
|
|
1628
|
-
if (modelMaxTokens !== undefined && modelMaxTokens > 0 && modelMaxTokens < contextWindow) {
|
|
1629
|
-
return modelMaxTokens;
|
|
1630
|
-
}
|
|
1631
|
-
const ratio = overrideRatio ?? RESPONSE_RESERVE_RATIO;
|
|
1632
|
-
return Math.max(Math.floor(contextWindow * ratio), LEGACY_RESPONSE_RESERVE_TOKENS);
|
|
1633
|
-
}
|
|
1634
1603
|
_recordPromptCachePlan(reason) {
|
|
1635
1604
|
const boundary = this.agent.state.systemPromptCacheBoundary;
|
|
1636
1605
|
const scope = this.model ? `${this.model.provider}/${this.model.id}` : "unknown";
|
|
@@ -1641,60 +1610,40 @@ export class AgentSession {
|
|
|
1641
1610
|
messages: [],
|
|
1642
1611
|
tools: this.agent.state.tools,
|
|
1643
1612
|
}, scope);
|
|
1644
|
-
|
|
1613
|
+
const transition = classifyPromptCacheTransition(this._promptCacheKey, nextKey);
|
|
1614
|
+
if (transition.kind === "bypass") {
|
|
1645
1615
|
this._promptCacheBoundaryBypasses += 1;
|
|
1646
1616
|
this._promptCacheStablePrefixCharacters = 0;
|
|
1647
|
-
if (
|
|
1617
|
+
if (transition.recordBreak) {
|
|
1648
1618
|
this._promptCacheLastBreakReason = reason;
|
|
1649
1619
|
}
|
|
1650
1620
|
this._promptCacheKey = undefined;
|
|
1651
1621
|
return;
|
|
1652
1622
|
}
|
|
1653
|
-
if (
|
|
1623
|
+
if (transition.kind === "changed") {
|
|
1654
1624
|
this._promptCacheKeyChanges += 1;
|
|
1655
1625
|
this._promptCacheLastBreakReason = reason;
|
|
1656
1626
|
}
|
|
1657
1627
|
this._promptCacheKey = nextKey;
|
|
1658
1628
|
this._promptCacheStablePrefixCharacters = boundary ?? 0;
|
|
1659
1629
|
}
|
|
1660
|
-
_rebuildSystemPrompt(toolNames) {
|
|
1661
|
-
const
|
|
1662
|
-
const toolSnippets = {};
|
|
1663
|
-
const promptGuidelines = [];
|
|
1664
|
-
for (const name of validToolNames) {
|
|
1665
|
-
const snippet = this._toolPromptSnippets.get(name);
|
|
1666
|
-
if (snippet) {
|
|
1667
|
-
toolSnippets[name] = snippet;
|
|
1668
|
-
}
|
|
1669
|
-
const toolGuidelines = this._toolPromptGuidelines.get(name);
|
|
1670
|
-
if (toolGuidelines) {
|
|
1671
|
-
promptGuidelines.push(...toolGuidelines);
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
|
|
1675
|
-
const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
|
|
1676
|
-
const grokAppend = grokPlaybookAppendForProvider(this.model?.provider);
|
|
1677
|
-
const appendParts = [...loaderAppendSystemPrompt];
|
|
1678
|
-
if (grokAppend) {
|
|
1679
|
-
appendParts.push(grokAppend);
|
|
1680
|
-
}
|
|
1681
|
-
const appendSystemPrompt = appendParts.length > 0 ? appendParts.join("\n\n") : undefined;
|
|
1682
|
-
const loadedSkills = this._resourceLoader.getSkills().skills;
|
|
1683
|
-
const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
|
|
1684
|
-
this._baseSystemPromptOptions = {
|
|
1630
|
+
_rebuildSystemPrompt(toolNames, model = this.model) {
|
|
1631
|
+
const assembled = assembleSessionSystemPrompt({
|
|
1685
1632
|
cwd: this._cwd,
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1633
|
+
toolNames,
|
|
1634
|
+
hasTool: (name) => this._toolRegistry.has(name),
|
|
1635
|
+
toolPromptSnippets: this._toolPromptSnippets,
|
|
1636
|
+
toolPromptGuidelines: this._toolPromptGuidelines,
|
|
1637
|
+
customPrompt: this._resourceLoader.getSystemPrompt(),
|
|
1638
|
+
appendSystemPrompt: this._resourceLoader.getAppendSystemPrompt(),
|
|
1639
|
+
providerAppend: grokPlaybookAppendForProvider(model?.provider),
|
|
1640
|
+
skills: this._resourceLoader.getSkills().skills,
|
|
1641
|
+
contextFiles: selectContextFilesForModel(this._resourceLoader.getAgentsFiles().agentsFiles, model),
|
|
1642
|
+
contextBudget: this._getContextBudgetOptions(undefined, model),
|
|
1643
|
+
});
|
|
1644
|
+
this._baseSystemPromptOptions = assembled.options;
|
|
1645
|
+
this._baseSystemPromptCacheBoundary = assembled.cacheBoundary;
|
|
1646
|
+
return assembled.prompt;
|
|
1698
1647
|
}
|
|
1699
1648
|
// =========================================================================
|
|
1700
1649
|
// Prompting
|
|
@@ -2234,16 +2183,25 @@ export class AgentSession {
|
|
|
2234
2183
|
if (!this._modelRegistry.hasConfiguredAuth(model)) {
|
|
2235
2184
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
2236
2185
|
}
|
|
2237
|
-
// Root-level: block sticky safety models (Fable) unless
|
|
2186
|
+
// Root-level: block sticky safety models (Fable) unless the user pinned `--model`.
|
|
2238
2187
|
const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
|
|
2239
|
-
if (
|
|
2188
|
+
if (shouldEjectStickySafetyModel({
|
|
2189
|
+
blockStickySafetyModels: resilience.blockStickySafetyModels,
|
|
2190
|
+
modelPinned: this._modelPinned,
|
|
2191
|
+
}) &&
|
|
2192
|
+
isStickySafetyModel(model.id, model.provider)) {
|
|
2240
2193
|
throw new Error(stickySafetyBlockMessage(model.id, model.provider));
|
|
2241
2194
|
}
|
|
2242
2195
|
// Grok OAuth: block selecting Imagine models as the session chat model.
|
|
2243
2196
|
assertTextChatModelForCompletion(model.id, model.provider);
|
|
2244
2197
|
const previousModel = this.model;
|
|
2245
2198
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
2199
|
+
const nextSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames(), model);
|
|
2246
2200
|
this.agent.state.model = model;
|
|
2201
|
+
this._baseSystemPrompt = nextSystemPrompt;
|
|
2202
|
+
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
2203
|
+
this.agent.state.systemPromptCacheBoundary = this._baseSystemPromptCacheBoundary;
|
|
2204
|
+
this.agent.state.systemPromptCacheBoundaryBypass = false;
|
|
2247
2205
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
2248
2206
|
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
2249
2207
|
// Re-clamp thinking level for new model's capabilities
|
|
@@ -2957,34 +2915,29 @@ export class AgentSession {
|
|
|
2957
2915
|
*/
|
|
2958
2916
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
2959
2917
|
const settings = this.settingsManager.getCompactionSettings();
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
|
|
2963
|
-
if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
|
|
2964
|
-
return false;
|
|
2965
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
2966
|
-
// Skip overflow check if the message came from a different model.
|
|
2967
|
-
// This handles the case where user switched from a smaller-context model (e.g. opus)
|
|
2968
|
-
// to a larger-context model (e.g. codex) - the overflow error from the old model
|
|
2969
|
-
// shouldn't trigger compaction for the new model.
|
|
2970
|
-
const sameModel = this.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;
|
|
2971
|
-
// Image-bearing turns are auto-routed to a vision-capable model (gpt-5.6-luna)
|
|
2972
|
-
// while the session model stays text-only (deepseek). An overflow from that
|
|
2973
|
-
// routed model must still count as the session's own overflow: compaction is
|
|
2974
|
-
// the fix, and the raw context_length_exceeded error must never reach the user.
|
|
2975
|
-
const visionRouteOverflow = this.model != null &&
|
|
2976
|
-
!(this.model.input ?? []).includes("image") &&
|
|
2977
|
-
isVisionRouteModel({ provider: assistantMessage.provider, id: assistantMessage.model });
|
|
2978
|
-
// Skip compaction checks if this assistant message is older than the latest
|
|
2979
|
-
// compaction boundary. This prevents a stale pre-compaction usage/error
|
|
2980
|
-
// from retriggering compaction on the first prompt after compaction.
|
|
2918
|
+
// Stale pre-compaction usage/errors must not retrigger compaction on the
|
|
2919
|
+
// first prompt after one finished.
|
|
2981
2920
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
2982
|
-
|
|
2983
|
-
|
|
2921
|
+
if (shouldSkipCompactionCheck({
|
|
2922
|
+
enabled: settings.enabled,
|
|
2923
|
+
skipAbortedCheck,
|
|
2924
|
+
stopReason: assistantMessage.stopReason,
|
|
2925
|
+
messageTimestamp: assistantMessage.timestamp,
|
|
2926
|
+
latestCompactionTimestamp: compactionEntry ? new Date(compactionEntry.timestamp).getTime() : undefined,
|
|
2927
|
+
})) {
|
|
2984
2928
|
return false;
|
|
2985
2929
|
}
|
|
2986
|
-
|
|
2987
|
-
|
|
2930
|
+
const contextWindow = this.model?.contextWindow ?? 0;
|
|
2931
|
+
// Case 1: Overflow - LLM returned context overflow error. Only counts when
|
|
2932
|
+
// the failing message belongs to the session model — or to the auto-routed
|
|
2933
|
+
// vision model while the session model is text-only.
|
|
2934
|
+
if (isSessionModelOverflow({
|
|
2935
|
+
message: assistantMessage,
|
|
2936
|
+
contextWindow,
|
|
2937
|
+
sessionProvider: this.model?.provider,
|
|
2938
|
+
sessionModelId: this.model?.id,
|
|
2939
|
+
sessionInputs: this.model?.input,
|
|
2940
|
+
})) {
|
|
2988
2941
|
if (this._overflowRecoveryAttempts >= MAX_OVERFLOW_RECOVERY_ATTEMPTS) {
|
|
2989
2942
|
this._emit({
|
|
2990
2943
|
type: "compaction_end",
|
|
@@ -3692,20 +3645,7 @@ export class AgentSession {
|
|
|
3692
3645
|
* Context overflow errors are NOT retryable (handled by compaction instead).
|
|
3693
3646
|
*/
|
|
3694
3647
|
_isRetryableError(message) {
|
|
3695
|
-
|
|
3696
|
-
return false;
|
|
3697
|
-
// Context overflow is handled by compaction, not retry
|
|
3698
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
3699
|
-
if (isContextOverflow(message, contextWindow))
|
|
3700
|
-
return false;
|
|
3701
|
-
const err = message.errorMessage;
|
|
3702
|
-
// Billing/quota exhaustion: useless to retry the same model until the
|
|
3703
|
-
// cycle resets, but the turn may still be saved by failing over to the
|
|
3704
|
-
// next candidate, so let _prepareRetry run its failover path.
|
|
3705
|
-
if (isQuotaExhaustionMessage(err))
|
|
3706
|
-
return true;
|
|
3707
|
-
// Shared contract with provider-resilience.ts (Fable safety stop, K3 orphan tool_call_id, terminated).
|
|
3708
|
-
return isTransientProviderErrorMessage(err);
|
|
3648
|
+
return isRetryableAssistantError(message, this.model?.contextWindow ?? 0);
|
|
3709
3649
|
}
|
|
3710
3650
|
/**
|
|
3711
3651
|
* Retry policy + callbacks shared by compaction and branch-summary summarization calls.
|
|
@@ -3742,8 +3682,12 @@ export class AgentSession {
|
|
|
3742
3682
|
/** Eject sticky safety model at prompt boundary (session resume / leftover default). */
|
|
3743
3683
|
async _ejectStickySafetyModelIfNeeded() {
|
|
3744
3684
|
const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
|
|
3745
|
-
if (!
|
|
3685
|
+
if (!shouldEjectStickySafetyModel({
|
|
3686
|
+
blockStickySafetyModels: resilience.blockStickySafetyModels,
|
|
3687
|
+
modelPinned: this._modelPinned,
|
|
3688
|
+
})) {
|
|
3746
3689
|
return;
|
|
3690
|
+
}
|
|
3747
3691
|
const current = this.model;
|
|
3748
3692
|
if (!current || !isStickySafetyModel(current.id, current.provider))
|
|
3749
3693
|
return;
|
|
@@ -3771,11 +3715,17 @@ export class AgentSession {
|
|
|
3771
3715
|
*/
|
|
3772
3716
|
async _maybeFailoverFromSafetyStop(message) {
|
|
3773
3717
|
const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
|
|
3774
|
-
if (!
|
|
3718
|
+
if (!shouldHonorSafetyFailover({
|
|
3719
|
+
autoFailoverOnSafetyStop: resilience.autoFailoverOnSafetyStop,
|
|
3720
|
+
modelPinned: this._modelPinned,
|
|
3721
|
+
modelId: this.model?.id,
|
|
3722
|
+
provider: this.model?.provider,
|
|
3723
|
+
})) {
|
|
3775
3724
|
return undefined;
|
|
3725
|
+
}
|
|
3776
3726
|
// Fire on safety-stop FPs AND on billing/quota exhaustion: both mean the
|
|
3777
3727
|
// current model cannot finish this turn and same-model retry is useless.
|
|
3778
|
-
if (!
|
|
3728
|
+
if (!isFailoverTriggerError(message.errorMessage)) {
|
|
3779
3729
|
return undefined;
|
|
3780
3730
|
}
|
|
3781
3731
|
const current = this.model;
|
|
@@ -3783,10 +3733,10 @@ export class AgentSession {
|
|
|
3783
3733
|
// claude-opus-5 emits the same stop_reason=refusal FP; same-model retry is useless.
|
|
3784
3734
|
// v10.3-Ω: mark the model that just refused so we never re-pick it this turn.
|
|
3785
3735
|
if (current)
|
|
3786
|
-
this._refusedModels.add(
|
|
3736
|
+
this._refusedModels.add(failoverModelKey(current.provider, current.id));
|
|
3787
3737
|
const pick = pickFailoverCandidate(resilience.failoverCandidates, current ? { provider: current.provider, id: current.id } : undefined, (c) => {
|
|
3788
3738
|
// Skip any model that already refused/failed this turn → advance the chain.
|
|
3789
|
-
if (this._refusedModels.has(
|
|
3739
|
+
if (this._refusedModels.has(failoverModelKey(c.provider, c.id)))
|
|
3790
3740
|
return false;
|
|
3791
3741
|
const next = this._modelRegistry.find(c.provider, c.id);
|
|
3792
3742
|
if (!next)
|
|
@@ -3804,34 +3754,32 @@ export class AgentSession {
|
|
|
3804
3754
|
}
|
|
3805
3755
|
catch {
|
|
3806
3756
|
// setModel failed (auth/guard) — blacklist so next retry advances further.
|
|
3807
|
-
this._refusedModels.add(
|
|
3757
|
+
this._refusedModels.add(failoverModelKey(pick.provider, pick.id));
|
|
3808
3758
|
return undefined;
|
|
3809
3759
|
}
|
|
3810
3760
|
}
|
|
3811
3761
|
async _prepareRetry(message) {
|
|
3812
3762
|
const settings = this.settingsManager.getRetrySettings();
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3763
|
+
const maxRetries = retryBudgetForAssistantError(message, settings.maxRetries);
|
|
3764
|
+
const attempt = nextRetryAttempt({
|
|
3765
|
+
enabled: settings.enabled,
|
|
3766
|
+
completedAttempts: this._retryAttempt,
|
|
3767
|
+
maxRetries,
|
|
3768
|
+
});
|
|
3769
|
+
if (attempt === undefined)
|
|
3820
3770
|
return false;
|
|
3821
|
-
|
|
3771
|
+
this._retryAttempt = attempt;
|
|
3822
3772
|
// Content/safety stop (Fable/Opus/Sonnet): switch model BEFORE delay so retry is not same-model refusal.
|
|
3823
3773
|
const failoverTo = await this._maybeFailoverFromSafetyStop(message);
|
|
3824
3774
|
// Safety stops are usually immediate false positives — short delay after failover, full backoff otherwise.
|
|
3825
|
-
const delayMs = failoverTo
|
|
3826
|
-
? Math.min(400, settings.baseDelayMs)
|
|
3827
|
-
: settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
|
|
3775
|
+
const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, failoverTo !== undefined);
|
|
3828
3776
|
const errorMessage = failoverTo
|
|
3829
3777
|
? `${message.errorMessage || "content/safety stop"} → failover ${failoverTo}`
|
|
3830
3778
|
: message.errorMessage || "Unknown error";
|
|
3831
3779
|
this._emit({
|
|
3832
3780
|
type: "auto_retry_start",
|
|
3833
|
-
attempt
|
|
3834
|
-
maxAttempts:
|
|
3781
|
+
attempt,
|
|
3782
|
+
maxAttempts: maxRetries,
|
|
3835
3783
|
delayMs,
|
|
3836
3784
|
errorMessage,
|
|
3837
3785
|
});
|
|
@@ -4343,21 +4291,6 @@ export class AgentSession {
|
|
|
4343
4291
|
return this._extensionRunner;
|
|
4344
4292
|
}
|
|
4345
4293
|
}
|
|
4346
|
-
// ---------------------------------------------------------------------------
|
|
4347
|
-
// Context budget constants
|
|
4348
|
-
// ---------------------------------------------------------------------------
|
|
4349
|
-
/** Fraction of contextWindow reserved for model response generation. */
|
|
4350
|
-
const RESPONSE_RESERVE_RATIO = 0.2;
|
|
4351
|
-
/** Fraction of contextWindow held back as safety margin for token-count imprecision. */
|
|
4352
|
-
const SAFETY_MARGIN_RATIO = 0.1;
|
|
4353
|
-
// Default prompt budget = contextWindow - responseReserve - safetyMargin.
|
|
4354
|
-
// With the defaults above (0.2 + 0.1) this yields ~0.70 of contextWindow, but it
|
|
4355
|
-
// stays correct when responseReserve is overridden by model.maxTokens.
|
|
4356
|
-
/** Absolute floor for maxPromptTokens — below this the budget is meaningless. */
|
|
4357
|
-
const MIN_PROMPT_TOKENS = 4000;
|
|
4358
|
-
/** Legacy defaults when no model contextWindow is known. */
|
|
4359
|
-
const LEGACY_MAX_PROMPT_TOKENS = 60_000;
|
|
4360
|
-
const LEGACY_RESPONSE_RESERVE_TOKENS = 8_192;
|
|
4361
4294
|
function parsePositiveIntegerEnv(name) {
|
|
4362
4295
|
const raw = process.env[name];
|
|
4363
4296
|
if (raw === undefined || raw.trim() === "") {
|