open-multi-agent-kit 0.96.0 → 0.96.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/README.md +34 -6
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- 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/resource-doctor-cli.d.ts +22 -0
- package/dist/commands/resource-doctor-cli.d.ts.map +1 -0
- package/dist/commands/resource-doctor-cli.js +98 -0
- package/dist/commands/resource-doctor-cli.js.map +1 -0
- 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 +41 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +396 -179
- 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/completion-sound.d.ts +109 -0
- package/dist/core/completion-sound.d.ts.map +1 -0
- package/dist/core/completion-sound.js +165 -0
- package/dist/core/completion-sound.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/host-resource-probe.d.ts +63 -0
- package/dist/core/host-resource-probe.d.ts.map +1 -0
- package/dist/core/host-resource-probe.js +126 -0
- package/dist/core/host-resource-probe.js.map +1 -0
- package/dist/core/host-resource-snapshot.d.ts +54 -0
- package/dist/core/host-resource-snapshot.d.ts.map +1 -0
- package/dist/core/host-resource-snapshot.js +139 -0
- package/dist/core/host-resource-snapshot.js.map +1 -0
- 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/prompt-settlement.d.ts +81 -0
- package/dist/core/prompt-settlement.d.ts.map +1 -0
- package/dist/core/prompt-settlement.js +84 -0
- package/dist/core/prompt-settlement.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 +24 -2
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/provider-retry.d.ts +36 -0
- package/dist/core/provider-retry.d.ts.map +1 -0
- package/dist/core/provider-retry.js +73 -0
- package/dist/core/provider-retry.js.map +1 -0
- package/dist/core/provider-usage.d.ts +16 -2
- package/dist/core/provider-usage.d.ts.map +1 -1
- package/dist/core/provider-usage.js +150 -8
- package/dist/core/provider-usage.js.map +1 -1
- package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
- package/dist/core/reasoning-router-resolver.js +1 -1
- package/dist/core/reasoning-router-resolver.js.map +1 -1
- package/dist/core/resource-admission-config.d.ts +55 -0
- package/dist/core/resource-admission-config.d.ts.map +1 -0
- package/dist/core/resource-admission-config.js +97 -0
- package/dist/core/resource-admission-config.js.map +1 -0
- package/dist/core/resource-admission.d.ts +81 -0
- package/dist/core/resource-admission.d.ts.map +1 -0
- package/dist/core/resource-admission.js +128 -0
- package/dist/core/resource-admission.js.map +1 -0
- package/dist/core/resource-governor-format.d.ts +50 -0
- package/dist/core/resource-governor-format.d.ts.map +1 -0
- package/dist/core/resource-governor-format.js +113 -0
- package/dist/core/resource-governor-format.js.map +1 -0
- package/dist/core/resource-governor-settings.d.ts +56 -0
- package/dist/core/resource-governor-settings.d.ts.map +1 -0
- package/dist/core/resource-governor-settings.js +89 -0
- package/dist/core/resource-governor-settings.js.map +1 -0
- 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/resource-observation-journal.d.ts +74 -0
- package/dist/core/resource-observation-journal.d.ts.map +1 -0
- package/dist/core/resource-observation-journal.js +198 -0
- package/dist/core/resource-observation-journal.js.map +1 -0
- package/dist/core/resource-safety-gate.d.ts +54 -0
- package/dist/core/resource-safety-gate.d.ts.map +1 -0
- package/dist/core/resource-safety-gate.js +51 -0
- package/dist/core/resource-safety-gate.js.map +1 -0
- package/dist/core/run-journal.d.ts.map +1 -1
- package/dist/core/run-journal.js +13 -1
- package/dist/core/run-journal.js.map +1 -1
- package/dist/core/run-resource-lease.d.ts +73 -0
- package/dist/core/run-resource-lease.d.ts.map +1 -0
- package/dist/core/run-resource-lease.js +78 -0
- package/dist/core/run-resource-lease.js.map +1 -0
- 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-bash-service.d.ts +12 -0
- package/dist/core/session-bash-service.d.ts.map +1 -1
- package/dist/core/session-bash-service.js +14 -0
- package/dist/core/session-bash-service.js.map +1 -1
- package/dist/core/session-system-prompt.d.ts +30 -0
- package/dist/core/session-system-prompt.d.ts.map +1 -0
- package/dist/core/session-system-prompt.js +39 -0
- package/dist/core/session-system-prompt.js.map +1 -0
- package/dist/core/session-termination.d.ts +7 -2
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +29 -1
- package/dist/core/session-termination.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +36 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- 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/subagent-lane-launcher.d.ts +64 -0
- package/dist/core/subagent-lane-launcher.d.ts.map +1 -0
- package/dist/core/subagent-lane-launcher.js +107 -0
- package/dist/core/subagent-lane-launcher.js.map +1 -0
- package/dist/core/system-cpu-sampler.d.ts +45 -0
- package/dist/core/system-cpu-sampler.d.ts.map +1 -0
- package/dist/core/system-cpu-sampler.js +95 -0
- package/dist/core/system-cpu-sampler.js.map +1 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -8
- package/dist/core/system-prompt.js.map +1 -1
- 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/core/workload-classifier.d.ts +32 -0
- package/dist/core/workload-classifier.d.ts.map +1 -0
- package/dist/core/workload-classifier.js +66 -0
- package/dist/core/workload-classifier.js.map +1 -0
- package/dist/core/workload-family-matcher.d.ts +3 -0
- package/dist/core/workload-family-matcher.d.ts.map +1 -0
- package/dist/core/workload-family-matcher.js +126 -0
- package/dist/core/workload-family-matcher.js.map +1 -0
- package/dist/core/workload-permit-pool.d.ts +78 -0
- package/dist/core/workload-permit-pool.d.ts.map +1 -0
- package/dist/core/workload-permit-pool.js +138 -0
- package/dist/core/workload-permit-pool.js.map +1 -0
- package/dist/core/workload-shard-executor.d.ts +68 -0
- package/dist/core/workload-shard-executor.d.ts.map +1 -0
- package/dist/core/workload-shard-executor.js +156 -0
- package/dist/core/workload-shard-executor.js.map +1 -0
- package/dist/core/workload-shard-plan.d.ts +81 -0
- package/dist/core/workload-shard-plan.d.ts.map +1 -0
- package/dist/core/workload-shard-plan.js +183 -0
- package/dist/core/workload-shard-plan.js.map +1 -0
- package/dist/core/workload-shard-runner.d.ts +11 -0
- package/dist/core/workload-shard-runner.d.ts.map +1 -0
- package/dist/core/workload-shard-runner.js +98 -0
- package/dist/core/workload-shard-runner.js.map +1 -0
- package/dist/core/workload-shard-store.d.ts +44 -0
- package/dist/core/workload-shard-store.d.ts.map +1 -0
- package/dist/core/workload-shard-store.js +189 -0
- package/dist/core/workload-shard-store.js.map +1 -0
- package/dist/core/workload-sharders.d.ts +47 -0
- package/dist/core/workload-sharders.d.ts.map +1 -0
- package/dist/core/workload-sharders.js +162 -0
- package/dist/core/workload-sharders.js.map +1 -0
- package/dist/core/workload-shell-scan.d.ts +12 -0
- package/dist/core/workload-shell-scan.d.ts.map +1 -0
- package/dist/core/workload-shell-scan.js +79 -0
- package/dist/core/workload-shell-scan.js.map +1 -0
- 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 +14 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +81 -0
- package/dist/modes/interactive/interactive-mode.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 +100 -3
- package/docs/security.md +22 -32
- package/docs/sessions.md +14 -0
- package/docs/settings.md +64 -2
- package/docs/skills.md +16 -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/subagent/agent-capability-router.test.ts +7 -0
- package/examples/extensions/subagent/domain-profiles.ts +0 -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 +7 -9
|
@@ -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";
|
|
@@ -43,6 +44,7 @@ import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
|
43
44
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
44
45
|
import { assertTextChatModelForCompletion } from "./grok-harness.js";
|
|
45
46
|
import { grokPlaybookAppendForProvider } from "./grok-playbook.js";
|
|
47
|
+
import { captureHostResourceSnapshot } from "./host-resource-snapshot.js";
|
|
46
48
|
import { decideLoadoutAccess } from "./loadout-access-policy.js";
|
|
47
49
|
import { loadMcpServerConfigs } from "./mcp/config.js";
|
|
48
50
|
import { McpManager } from "./mcp/manager.js";
|
|
@@ -52,6 +54,7 @@ import { TurnMetricsSink } from "./turn-metrics.js";
|
|
|
52
54
|
const DISABLED_ENV_VALUES = new Set(["0", "false", "off", "disable", "disabled"]);
|
|
53
55
|
const MAX_OVERFLOW_RECOVERY_ATTEMPTS = 2;
|
|
54
56
|
const OVERFLOW_RECOVERY_EMERGENCY_TOKENS = 4_096;
|
|
57
|
+
const MAX_RESOURCE_OBSERVATION_JOURNALS = 32;
|
|
55
58
|
function isDisabledEnvValue(value) {
|
|
56
59
|
return value !== undefined && DISABLED_ENV_VALUES.has(value.trim().toLowerCase());
|
|
57
60
|
}
|
|
@@ -70,20 +73,31 @@ function firstTextContent(result) {
|
|
|
70
73
|
return undefined;
|
|
71
74
|
}
|
|
72
75
|
import { redactCredentialShapedContent } from "./compaction/transaction.js";
|
|
76
|
+
import { selectContextFilesForModel } from "./model-prompt-policy.js";
|
|
73
77
|
import { findExactModelReferenceMatch } from "./model-resolver.js";
|
|
78
|
+
import { computePromptTokenBudget } from "./prompt-budget.js";
|
|
79
|
+
import { classifyPromptCacheTransition } from "./prompt-cache.js";
|
|
80
|
+
import { createPromptSettlementState, reducePromptSettlement, settlePromptIfReady, } from "./prompt-settlement.js";
|
|
74
81
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
75
|
-
import {
|
|
82
|
+
import { isQuotaExhaustionMessage, isStickySafetyModel, pickFailoverCandidate, resolveProviderResilience, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
|
|
83
|
+
import { computeRetryDelayMs, failoverModelKey, isFailoverTriggerError, isRetryableAssistantError, nextRetryAttempt, retryBudgetForAssistantError, } from "./provider-retry.js";
|
|
76
84
|
import { getBiasStepsForCell, parseRouterBiasSnapshot } from "./reasoning-router-bias.js";
|
|
77
85
|
import { classifyTaskV4, deriveRouterFeedbackFeaturesV4, resolveThinkingLevelV4WithUncertainty, TASK_CLASS_THINKING_LEVELS_V4, } from "./reasoning-router-v4.js";
|
|
78
86
|
import { redactSensitiveText, redactSensitiveTextForced } from "./redaction.js";
|
|
79
87
|
import { getRepositoryRouterLearningPaths } from "./repository-learning-scope.js";
|
|
88
|
+
import { decideResourceAdmission } from "./resource-admission.js";
|
|
89
|
+
import { resolveResourceGovernorSettings } from "./resource-governor-settings.js";
|
|
90
|
+
import { admissionObservationFacts, classificationObservationFacts, ResourceObservationJournal, settledObservationFacts, snapshotObservationFacts, soundObservationFacts, } from "./resource-observation-journal.js";
|
|
91
|
+
import { decideResourceSafetyGate, RESOURCE_PRESSURE_REQUIRED_ACTION } from "./resource-safety-gate.js";
|
|
80
92
|
import { appendRouterFeedbackRecord, ROUTER_FEEDBACK_LEVELS, } from "./router-feedback-collector.js";
|
|
81
93
|
import { RunJournalStore } from "./run-journal-store.js";
|
|
94
|
+
import { RunResourceLeaseController } from "./run-resource-lease.js";
|
|
82
95
|
import { SessionBashRuntime } from "./session-bash-runtime.js";
|
|
83
96
|
import { SessionBashService } from "./session-bash-service.js";
|
|
84
97
|
import { SessionCompactionService } from "./session-compaction-service.js";
|
|
85
98
|
import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
|
|
86
99
|
import { acquireSessionOwnerLeaseSync } from "./session-owner-lease.js";
|
|
100
|
+
import { assembleSessionSystemPrompt } from "./session-system-prompt.js";
|
|
87
101
|
import { classifySessionTermination, } from "./session-termination.js";
|
|
88
102
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
89
103
|
import { buildSystemPromptPlan } from "./system-prompt.js";
|
|
@@ -92,6 +106,8 @@ import { createAllToolDefinitions } from "./tools/index.js";
|
|
|
92
106
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
93
107
|
import { createVerifiedBashOperations } from "./verified-bash-adapter.js";
|
|
94
108
|
import { resolveSessionWorkspaceScope } from "./verified-bash-runtime.js";
|
|
109
|
+
import { classifyWorkloadCommand } from "./workload-classifier.js";
|
|
110
|
+
import { WorkloadPermitError, WorkloadPermitPool } from "./workload-permit-pool.js";
|
|
95
111
|
/**
|
|
96
112
|
* Parse a skill block from message text.
|
|
97
113
|
* Returns null if the text doesn't contain a skill block.
|
|
@@ -315,6 +331,8 @@ export class AgentSession {
|
|
|
315
331
|
// v10.3-Ω: models that already refused/failed this turn — failover must advance,
|
|
316
332
|
// not re-pick the same candidate (claude-opus-5 → grok → grok → grok loop fix).
|
|
317
333
|
_refusedModels = new Set();
|
|
334
|
+
/** True when `--model` / `--provider` pinned this process to one model. */
|
|
335
|
+
_modelPinned;
|
|
318
336
|
// Bash execution state
|
|
319
337
|
_bashService;
|
|
320
338
|
_compactionService;
|
|
@@ -348,6 +366,14 @@ export class AgentSession {
|
|
|
348
366
|
_runJournalStore;
|
|
349
367
|
_ownedSessionOwnerLease;
|
|
350
368
|
_activeRunId = null;
|
|
369
|
+
// Resource governor (roadmap M2): lease controller owns the temporary tool
|
|
370
|
+
// cap for governed runs; the last decision backs the §19.4 read-only API.
|
|
371
|
+
_resourceLeaseController;
|
|
372
|
+
_resourceObservations = null;
|
|
373
|
+
_resourceObservationJournals = new Map();
|
|
374
|
+
_latestResourcePromptRunId = null;
|
|
375
|
+
_lastResourceAdmission = null;
|
|
376
|
+
_workloadPermitPool;
|
|
351
377
|
_pendingRuntimeTerminationCause;
|
|
352
378
|
_activeRunToolTermination;
|
|
353
379
|
_lastTermination;
|
|
@@ -383,6 +409,7 @@ export class AgentSession {
|
|
|
383
409
|
this.sessionManager = config.sessionManager;
|
|
384
410
|
this.settingsManager = config.settingsManager;
|
|
385
411
|
this._scopedModels = config.scopedModels ?? [];
|
|
412
|
+
this._modelPinned = config.modelPinned === true;
|
|
386
413
|
this._resourceLoader = config.resourceLoader;
|
|
387
414
|
this._customTools = config.customTools ?? [];
|
|
388
415
|
this._cwd = config.cwd;
|
|
@@ -427,6 +454,7 @@ export class AgentSession {
|
|
|
427
454
|
isStreaming: () => this.isStreaming,
|
|
428
455
|
pushMessage: (message) => this.agent.state.messages.push(message),
|
|
429
456
|
appendMessage: (message) => this.sessionManager.appendMessage(message),
|
|
457
|
+
acquireResourcePermit: (command, signal) => this._acquireBashResourcePermit(command, signal),
|
|
430
458
|
});
|
|
431
459
|
this._compactionService = new SessionCompactionService({
|
|
432
460
|
sessionManager: this.sessionManager,
|
|
@@ -1043,13 +1071,14 @@ export class AgentSession {
|
|
|
1043
1071
|
};
|
|
1044
1072
|
_willRetryAfterAgentEnd(event) {
|
|
1045
1073
|
const settings = this.settingsManager.getRetrySettings();
|
|
1046
|
-
if (!settings.enabled
|
|
1074
|
+
if (!settings.enabled)
|
|
1047
1075
|
return false;
|
|
1048
|
-
}
|
|
1049
1076
|
for (let i = event.messages.length - 1; i >= 0; i--) {
|
|
1050
1077
|
const message = event.messages[i];
|
|
1051
1078
|
if (message.role === "assistant") {
|
|
1052
|
-
|
|
1079
|
+
const assistant = message;
|
|
1080
|
+
const maxRetries = retryBudgetForAssistantError(assistant, settings.maxRetries);
|
|
1081
|
+
return this._retryAttempt < maxRetries && this._isRetryableError(assistant);
|
|
1053
1082
|
}
|
|
1054
1083
|
}
|
|
1055
1084
|
return false;
|
|
@@ -1554,7 +1583,7 @@ export class AgentSession {
|
|
|
1554
1583
|
}
|
|
1555
1584
|
return undefined;
|
|
1556
1585
|
}
|
|
1557
|
-
_getContextBudgetOptions(queryContext = this._extractCurrentQuery()) {
|
|
1586
|
+
_getContextBudgetOptions(queryContext = this._extractCurrentQuery(), model = this.model) {
|
|
1558
1587
|
const contextGovernorOverride = process.env.OMK_CONTEXT_GOVERNOR;
|
|
1559
1588
|
if (contextGovernorOverride === "0") {
|
|
1560
1589
|
return undefined;
|
|
@@ -1562,56 +1591,24 @@ export class AgentSession {
|
|
|
1562
1591
|
if (contextGovernorOverride !== "1" && !this.settingsManager.getContextBudgetEnabled()) {
|
|
1563
1592
|
return undefined;
|
|
1564
1593
|
}
|
|
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
|
-
}
|
|
1594
|
+
const contextWindow = model?.contextWindow ?? 0;
|
|
1595
|
+
const budget = computePromptTokenBudget({
|
|
1596
|
+
contextWindow,
|
|
1597
|
+
modelMaxTokens: model?.maxTokens,
|
|
1598
|
+
envMaxPromptTokens: parsePositiveIntegerEnv("OMK_CONTEXT_GOVERNOR_MAX_PROMPT_TOKENS"),
|
|
1599
|
+
envResponseReserveTokens: parsePositiveIntegerEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RESERVE_TOKENS"),
|
|
1600
|
+
envPromptRatio: parsePositiveFloatEnv("OMK_CONTEXT_GOVERNOR_PROMPT_RATIO"),
|
|
1601
|
+
envResponseRatio: parsePositiveFloatEnv("OMK_CONTEXT_GOVERNOR_RESPONSE_RATIO"),
|
|
1602
|
+
});
|
|
1603
|
+
const maxPromptTokens = budget.maxPromptTokens;
|
|
1604
|
+
const responseReserveTokens = budget.responseReserveTokens;
|
|
1600
1605
|
const cacheProvider = this._contextBudgetCacheProvider ?? this._createContextBudgetCacheProvider();
|
|
1601
1606
|
cacheProvider.setInvalidationSnapshot?.(this._contextCacheInvalidationSnapshot);
|
|
1602
1607
|
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
1608
|
return {
|
|
1612
1609
|
maxPromptTokens,
|
|
1613
1610
|
responseReserveTokens,
|
|
1614
|
-
modelId:
|
|
1611
|
+
modelId: model?.id ?? "unknown",
|
|
1615
1612
|
tokenizerMode: parseTokenizerModeEnv(process.env.OMK_CONTEXT_GOVERNOR_TOKENIZER),
|
|
1616
1613
|
activeSkillNames: parseCommaSeparatedEnv(process.env.OMK_CONTEXT_GOVERNOR_ACTIVE_SKILLS),
|
|
1617
1614
|
queryContext,
|
|
@@ -1622,15 +1619,6 @@ export class AgentSession {
|
|
|
1622
1619
|
* Compute responseReserveTokens from contextWindow.
|
|
1623
1620
|
* Prefers the model's own maxTokens when available, otherwise uses a ratio.
|
|
1624
1621
|
*/
|
|
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
1622
|
_recordPromptCachePlan(reason) {
|
|
1635
1623
|
const boundary = this.agent.state.systemPromptCacheBoundary;
|
|
1636
1624
|
const scope = this.model ? `${this.model.provider}/${this.model.id}` : "unknown";
|
|
@@ -1641,65 +1629,64 @@ export class AgentSession {
|
|
|
1641
1629
|
messages: [],
|
|
1642
1630
|
tools: this.agent.state.tools,
|
|
1643
1631
|
}, scope);
|
|
1644
|
-
|
|
1632
|
+
const transition = classifyPromptCacheTransition(this._promptCacheKey, nextKey);
|
|
1633
|
+
if (transition.kind === "bypass") {
|
|
1645
1634
|
this._promptCacheBoundaryBypasses += 1;
|
|
1646
1635
|
this._promptCacheStablePrefixCharacters = 0;
|
|
1647
|
-
if (
|
|
1636
|
+
if (transition.recordBreak) {
|
|
1648
1637
|
this._promptCacheLastBreakReason = reason;
|
|
1649
1638
|
}
|
|
1650
1639
|
this._promptCacheKey = undefined;
|
|
1651
1640
|
return;
|
|
1652
1641
|
}
|
|
1653
|
-
if (
|
|
1642
|
+
if (transition.kind === "changed") {
|
|
1654
1643
|
this._promptCacheKeyChanges += 1;
|
|
1655
1644
|
this._promptCacheLastBreakReason = reason;
|
|
1656
1645
|
}
|
|
1657
1646
|
this._promptCacheKey = nextKey;
|
|
1658
1647
|
this._promptCacheStablePrefixCharacters = boundary ?? 0;
|
|
1659
1648
|
}
|
|
1660
|
-
|
|
1661
|
-
const
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
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 = {
|
|
1649
|
+
_getDefaultActiveSkills() {
|
|
1650
|
+
const trustedSkillNames = new Set(this._resourceLoader
|
|
1651
|
+
.getSkills()
|
|
1652
|
+
.skills.filter((skill) => skill.sourceInfo.scope === "user")
|
|
1653
|
+
.map((skill) => skill.name));
|
|
1654
|
+
return this.settingsManager.getDefaultActiveSkills().filter((name) => trustedSkillNames.has(name));
|
|
1655
|
+
}
|
|
1656
|
+
_rebuildSystemPrompt(toolNames, model = this.model) {
|
|
1657
|
+
const defaultActiveSkills = this._getDefaultActiveSkills();
|
|
1658
|
+
const assembled = assembleSessionSystemPrompt({
|
|
1685
1659
|
cwd: this._cwd,
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1660
|
+
toolNames,
|
|
1661
|
+
hasTool: (name) => this._toolRegistry.has(name),
|
|
1662
|
+
toolPromptSnippets: this._toolPromptSnippets,
|
|
1663
|
+
toolPromptGuidelines: this._toolPromptGuidelines,
|
|
1664
|
+
customPrompt: this._resourceLoader.getSystemPrompt(),
|
|
1665
|
+
appendSystemPrompt: this._resourceLoader.getAppendSystemPrompt(),
|
|
1666
|
+
providerAppend: grokPlaybookAppendForProvider(model?.provider),
|
|
1667
|
+
skills: this._resourceLoader.getSkills().skills,
|
|
1668
|
+
activeSkillNames: defaultActiveSkills,
|
|
1669
|
+
activeSkillSource: defaultActiveSkills.length > 0 ? "settings" : undefined,
|
|
1670
|
+
contextFiles: selectContextFilesForModel(this._resourceLoader.getAgentsFiles().agentsFiles, model),
|
|
1671
|
+
contextBudget: this._getContextBudgetOptions(undefined, model),
|
|
1672
|
+
});
|
|
1673
|
+
this._baseSystemPromptOptions = assembled.options;
|
|
1674
|
+
this._baseSystemPromptCacheBoundary = assembled.cacheBoundary;
|
|
1675
|
+
return assembled.prompt;
|
|
1698
1676
|
}
|
|
1699
1677
|
// =========================================================================
|
|
1700
1678
|
// Prompting
|
|
1701
1679
|
// =========================================================================
|
|
1702
1680
|
async _runAgentPrompt(messages) {
|
|
1681
|
+
// One identity per top-level run (§16.2): internal retries and
|
|
1682
|
+
// continuations inside this call share it, as does the resource lease.
|
|
1683
|
+
const promptRunId = `prompt-run-${randomUUID()}`;
|
|
1684
|
+
const startedAtEpochMs = Date.now();
|
|
1685
|
+
// Roadmap M2: the lease spans the whole run including internal retries
|
|
1686
|
+
// and continuations, so they share one admission decision (§8.3).
|
|
1687
|
+
const resourceLease = await this._beginResourceGovernedRun(promptRunId);
|
|
1688
|
+
const resourceObservations = this._resourceObservationJournals.get(promptRunId) ?? null;
|
|
1689
|
+
let outcome = "completed";
|
|
1703
1690
|
try {
|
|
1704
1691
|
await this.agent.prompt(messages);
|
|
1705
1692
|
while (await this._handlePostAgentRun()) {
|
|
@@ -1707,11 +1694,251 @@ export class AgentSession {
|
|
|
1707
1694
|
}
|
|
1708
1695
|
}
|
|
1709
1696
|
catch (error) {
|
|
1697
|
+
outcome =
|
|
1698
|
+
this._userAbortRequested || (error instanceof Error && /abort/i.test(error.message)) ? "aborted" : "failed";
|
|
1710
1699
|
this._publishRuntimeFailure(error);
|
|
1711
1700
|
throw error;
|
|
1712
1701
|
}
|
|
1713
1702
|
finally {
|
|
1703
|
+
if (resourceLease !== null) {
|
|
1704
|
+
// Generation-safe: a stale release after another run acquired the
|
|
1705
|
+
// lease is a no-op instead of clobbering the newer cap (§8.1).
|
|
1706
|
+
this._resourceLeaseController?.release(resourceLease);
|
|
1707
|
+
resourceObservations?.record("resource_lease_released_v1", { promptRunId });
|
|
1708
|
+
}
|
|
1714
1709
|
this._flushPendingBashMessages();
|
|
1710
|
+
// §16.4 (M4): settle only after every in-run continuation drained.
|
|
1711
|
+
this._emitPromptSettledIfReady({ promptRunId, startedAtEpochMs, outcome, resourceObservations });
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Emit `prompt_settled` when the §16.4 conditions hold. Called exactly
|
|
1716
|
+
* once per promptRunId (from `_runAgentPrompt`'s finally), so the same
|
|
1717
|
+
* run can never emit twice; late-queued messages block emission
|
|
1718
|
+
* conservatively — the continuation run settles under its own id.
|
|
1719
|
+
* Settlement is a UX signal only and must never affect the run (§16.1).
|
|
1720
|
+
*/
|
|
1721
|
+
_emitPromptSettledIfReady(input) {
|
|
1722
|
+
try {
|
|
1723
|
+
let state = createPromptSettlementState(input.promptRunId, input.startedAtEpochMs);
|
|
1724
|
+
state = reducePromptSettlement(state, { kind: "terminal", outcome: input.outcome });
|
|
1725
|
+
if (this.isStreaming) {
|
|
1726
|
+
state = reducePromptSettlement(state, { kind: "tool", delta: 1 });
|
|
1727
|
+
}
|
|
1728
|
+
if (this.agent.hasQueuedMessages()) {
|
|
1729
|
+
state = reducePromptSettlement(state, { kind: "continuation", delta: 1 });
|
|
1730
|
+
}
|
|
1731
|
+
const settled = settlePromptIfReady(state, Date.now());
|
|
1732
|
+
if (settled.event !== null) {
|
|
1733
|
+
input.resourceObservations?.record("prompt_settled_v1", settledObservationFacts(settled.event));
|
|
1734
|
+
this._emit(settled.event);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
catch {
|
|
1738
|
+
// Never let settlement bookkeeping break a run.
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Resource governor preflight for one top-level run (roadmap §8, §25.3 M2).
|
|
1743
|
+
*
|
|
1744
|
+
* - `off`: nothing; v0.96.1 observable behavior is preserved.
|
|
1745
|
+
* - `observe` (default): fire-and-forget probe records a decision for the
|
|
1746
|
+
* §19.4 read-only API; the cap never changes and the prompt is never
|
|
1747
|
+
* delayed. The pending probe is bounded (~cpuSampleMs) and never rejects.
|
|
1748
|
+
* - `adaptive`/`strict`: bounded blocking probe (default 300 ms deadline),
|
|
1749
|
+
* then a generation-safe lease applies the effective tool cap for this
|
|
1750
|
+
* run. The caller's `finally` releases it exactly once.
|
|
1751
|
+
*
|
|
1752
|
+
* Never throws: any probe or policy failure leaves this run ungoverned
|
|
1753
|
+
* (§2.1: probe failure must not crash or block the prompt).
|
|
1754
|
+
*/
|
|
1755
|
+
async _beginResourceGovernedRun(promptRunId) {
|
|
1756
|
+
try {
|
|
1757
|
+
const resolved = resolveResourceGovernorSettings(this.settingsManager.getResourceGovernorSettings());
|
|
1758
|
+
if (resolved.mode === "off") {
|
|
1759
|
+
return null;
|
|
1760
|
+
}
|
|
1761
|
+
const resourceObservations = this._openResourceObservations(promptRunId);
|
|
1762
|
+
const probeOptions = {
|
|
1763
|
+
cwd: this._cwd,
|
|
1764
|
+
maxProbeMs: resolved.maxProbeMs,
|
|
1765
|
+
cpuSampleMs: resolved.cpuSampleMs,
|
|
1766
|
+
};
|
|
1767
|
+
// §7.3: Agent.maxToolConcurrency uses undefined for "unlimited"; the
|
|
1768
|
+
// admission contract uses 0 for the same meaning.
|
|
1769
|
+
const configuredCaps = { maxToolConcurrency: this.agent.maxToolConcurrency ?? 0 };
|
|
1770
|
+
if (resolved.mode === "observe") {
|
|
1771
|
+
void captureHostResourceSnapshot(probeOptions)
|
|
1772
|
+
.then((snapshot) => {
|
|
1773
|
+
const decision = decideResourceAdmission({
|
|
1774
|
+
snapshot,
|
|
1775
|
+
config: resolved.admission,
|
|
1776
|
+
configuredCaps,
|
|
1777
|
+
});
|
|
1778
|
+
if (this._latestResourcePromptRunId === promptRunId) {
|
|
1779
|
+
this._lastResourceAdmission = decision;
|
|
1780
|
+
}
|
|
1781
|
+
resourceObservations?.record("resource_snapshot_v1", snapshotObservationFacts(snapshot));
|
|
1782
|
+
resourceObservations?.record("resource_admission_v1", admissionObservationFacts(decision));
|
|
1783
|
+
})
|
|
1784
|
+
.catch(() => { });
|
|
1785
|
+
return null;
|
|
1786
|
+
}
|
|
1787
|
+
const snapshot = await captureHostResourceSnapshot(probeOptions);
|
|
1788
|
+
const decision = decideResourceAdmission({ snapshot, config: resolved.admission, configuredCaps });
|
|
1789
|
+
if (this._latestResourcePromptRunId === promptRunId) {
|
|
1790
|
+
this._lastResourceAdmission = decision;
|
|
1791
|
+
}
|
|
1792
|
+
resourceObservations?.record("resource_snapshot_v1", snapshotObservationFacts(snapshot));
|
|
1793
|
+
resourceObservations?.record("resource_admission_v1", admissionObservationFacts(decision));
|
|
1794
|
+
this._resourceLeaseController ??= new RunResourceLeaseController({
|
|
1795
|
+
getCap: () => this.agent.maxToolConcurrency,
|
|
1796
|
+
setCap: (cap) => {
|
|
1797
|
+
this.agent.maxToolConcurrency = cap;
|
|
1798
|
+
},
|
|
1799
|
+
});
|
|
1800
|
+
const lease = this._resourceLeaseController.acquire({ promptRunId, decision });
|
|
1801
|
+
resourceObservations?.record("resource_lease_acquired_v1", {
|
|
1802
|
+
decisionId: decision.decisionId,
|
|
1803
|
+
appliedToolCap: decision.maxToolConcurrency,
|
|
1804
|
+
});
|
|
1805
|
+
return lease;
|
|
1806
|
+
}
|
|
1807
|
+
catch {
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
_openResourceObservations(promptRunId) {
|
|
1812
|
+
this._latestResourcePromptRunId = promptRunId;
|
|
1813
|
+
try {
|
|
1814
|
+
const journal = ResourceObservationJournal.open(this._cwd, promptRunId);
|
|
1815
|
+
this._resourceObservations = journal;
|
|
1816
|
+
this._resourceObservationJournals.set(promptRunId, journal);
|
|
1817
|
+
while (this._resourceObservationJournals.size > MAX_RESOURCE_OBSERVATION_JOURNALS) {
|
|
1818
|
+
const oldestPromptRunId = this._resourceObservationJournals.keys().next().value;
|
|
1819
|
+
if (oldestPromptRunId === undefined)
|
|
1820
|
+
break;
|
|
1821
|
+
this._resourceObservationJournals.delete(oldestPromptRunId);
|
|
1822
|
+
}
|
|
1823
|
+
return journal;
|
|
1824
|
+
}
|
|
1825
|
+
catch {
|
|
1826
|
+
this._resourceObservations = null;
|
|
1827
|
+
this._resourceObservationJournals.delete(promptRunId);
|
|
1828
|
+
return null;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
/** §20.2: record the TUI-side completion sound result in its originating run journal. */
|
|
1832
|
+
recordCompletionSoundResult(promptRunId, result) {
|
|
1833
|
+
const journal = this._resourceObservationJournals.get(promptRunId);
|
|
1834
|
+
journal?.record("completion_sound_result_v1", soundObservationFacts(result));
|
|
1835
|
+
this._resourceObservationJournals.delete(promptRunId);
|
|
1836
|
+
if (this._latestResourcePromptRunId === promptRunId) {
|
|
1837
|
+
this._latestResourcePromptRunId = null;
|
|
1838
|
+
if (this._resourceObservations === journal)
|
|
1839
|
+
this._resourceObservations = null;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
/** §19.4 read-only SDK surface: the most recent resource admission decision, if any. */
|
|
1843
|
+
getCurrentResourceAdmission() {
|
|
1844
|
+
return this._lastResourceAdmission;
|
|
1845
|
+
}
|
|
1846
|
+
/** §19.4 read-only SDK surface: capture a fresh host resource snapshot. */
|
|
1847
|
+
async getHostResourceSnapshot() {
|
|
1848
|
+
const resolved = resolveResourceGovernorSettings(this.settingsManager.getResourceGovernorSettings());
|
|
1849
|
+
return captureHostResourceSnapshot({
|
|
1850
|
+
cwd: this._cwd,
|
|
1851
|
+
maxProbeMs: resolved.maxProbeMs,
|
|
1852
|
+
cpuSampleMs: resolved.cpuSampleMs,
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
/** §19.4 read-only SDK surface: shared workload permit pool state, if any. */
|
|
1856
|
+
getWorkloadPermitSnapshot() {
|
|
1857
|
+
return this._workloadPermitPool?.snapshot() ?? null;
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* §14.1 shared-budget seam: child subagents must reuse this instance
|
|
1861
|
+
* instead of creating a private pool (host oversubscription). The live
|
|
1862
|
+
* child launcher wiring lands in M6.
|
|
1863
|
+
*/
|
|
1864
|
+
get workloadPermitPool() {
|
|
1865
|
+
this._workloadPermitPool ??= new WorkloadPermitPool();
|
|
1866
|
+
return this._workloadPermitPool;
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Resource safety gate for the bash boundary (roadmap §9.4/§11, M3).
|
|
1870
|
+
* Runs only in adaptive/strict mode with a recorded admission decision;
|
|
1871
|
+
* observe/off keep v0.96.1 behavior. Never throws: gate machinery
|
|
1872
|
+
* failures leave the command ungated (§2.1), while computed block
|
|
1873
|
+
* verdicts and permit rejections return a bounded §11.3 payload.
|
|
1874
|
+
*/
|
|
1875
|
+
async _acquireBashResourcePermit(command, signal) {
|
|
1876
|
+
try {
|
|
1877
|
+
const resolved = resolveResourceGovernorSettings(this.settingsManager.getResourceGovernorSettings());
|
|
1878
|
+
if (resolved.mode !== "adaptive" && resolved.mode !== "strict") {
|
|
1879
|
+
return {};
|
|
1880
|
+
}
|
|
1881
|
+
const decision = this._lastResourceAdmission;
|
|
1882
|
+
if (decision === null) {
|
|
1883
|
+
return {};
|
|
1884
|
+
}
|
|
1885
|
+
const classification = classifyWorkloadCommand(command);
|
|
1886
|
+
this._resourceObservations?.record("workload_classification_v1", classificationObservationFacts(classification));
|
|
1887
|
+
const verdict = decideResourceSafetyGate({ commandSafety: "allowed", classification, decision });
|
|
1888
|
+
if (verdict.kind === "allow") {
|
|
1889
|
+
return {};
|
|
1890
|
+
}
|
|
1891
|
+
if (verdict.kind === "block") {
|
|
1892
|
+
return { blocked: JSON.stringify(verdict.block) };
|
|
1893
|
+
}
|
|
1894
|
+
this._workloadPermitPool ??= new WorkloadPermitPool({ capacity: decision.maxHeavyProcesses });
|
|
1895
|
+
const pool = this._workloadPermitPool;
|
|
1896
|
+
pool.setCapacity(decision.maxHeavyProcesses);
|
|
1897
|
+
const waitStartedMs = Date.now();
|
|
1898
|
+
this._resourceObservations?.record("workload_permit_wait_v1", {
|
|
1899
|
+
workloadClass: classification.workloadClass,
|
|
1900
|
+
weight: verdict.weight,
|
|
1901
|
+
});
|
|
1902
|
+
try {
|
|
1903
|
+
const permit = await pool.acquire({
|
|
1904
|
+
requestId: `bash-${randomUUID()}`,
|
|
1905
|
+
promptRunId: this._resourceLeaseController?.activeLease?.promptRunId ?? "unleased",
|
|
1906
|
+
workloadClass: classification.workloadClass,
|
|
1907
|
+
weight: verdict.weight,
|
|
1908
|
+
signal,
|
|
1909
|
+
// Bounded §10.3 timeout-aware wait; a settings knob can follow later.
|
|
1910
|
+
timeoutMs: 60_000,
|
|
1911
|
+
});
|
|
1912
|
+
this._resourceObservations?.record("workload_permit_acquired_v1", {
|
|
1913
|
+
workloadClass: classification.workloadClass,
|
|
1914
|
+
weight: verdict.weight,
|
|
1915
|
+
waitMs: Date.now() - waitStartedMs,
|
|
1916
|
+
});
|
|
1917
|
+
return {
|
|
1918
|
+
release: () => {
|
|
1919
|
+
permit.release();
|
|
1920
|
+
this._resourceObservations?.record("workload_permit_released_v1", {
|
|
1921
|
+
workloadClass: classification.workloadClass,
|
|
1922
|
+
weight: verdict.weight,
|
|
1923
|
+
});
|
|
1924
|
+
},
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
catch (error) {
|
|
1928
|
+
const code = error instanceof WorkloadPermitError ? error.code : "queue_overflow";
|
|
1929
|
+
return {
|
|
1930
|
+
blocked: JSON.stringify({
|
|
1931
|
+
kind: "resource_pressure",
|
|
1932
|
+
pressure: decision.pressure,
|
|
1933
|
+
action: "defer-heavy",
|
|
1934
|
+
reasonCodes: [...decision.reasons, `resource.permit.${code}`],
|
|
1935
|
+
requiredAction: RESOURCE_PRESSURE_REQUIRED_ACTION,
|
|
1936
|
+
}),
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
catch {
|
|
1941
|
+
return {};
|
|
1715
1942
|
}
|
|
1716
1943
|
}
|
|
1717
1944
|
async _handlePostAgentRun() {
|
|
@@ -1753,8 +1980,13 @@ export class AgentSession {
|
|
|
1753
1980
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
1754
1981
|
const preflightResult = options?.preflightResult;
|
|
1755
1982
|
let currentText = redactSensitiveText(text);
|
|
1756
|
-
|
|
1757
|
-
let
|
|
1983
|
+
const defaultActiveSkills = this._getDefaultActiveSkills();
|
|
1984
|
+
let promptActiveSkillNames = mergePromptActiveSkillNames(defaultActiveSkills, options?.activeSkillNames ?? []);
|
|
1985
|
+
let promptActiveSkillSource = defaultActiveSkills.length > 0
|
|
1986
|
+
? options?.activeSkillSource
|
|
1987
|
+
? `settings+${options.activeSkillSource}`
|
|
1988
|
+
: "settings"
|
|
1989
|
+
: options?.activeSkillSource;
|
|
1758
1990
|
let isBangSkillInvocation = false;
|
|
1759
1991
|
if (expandPromptTemplates) {
|
|
1760
1992
|
const bangInvocation = parseBangInvocation(text, {
|
|
@@ -1766,7 +1998,8 @@ export class AgentSession {
|
|
|
1766
1998
|
? `/skill:${bangInvocation.skillName} ${bangInvocation.prompt}`
|
|
1767
1999
|
: `/skill:${bangInvocation.skillName}`;
|
|
1768
2000
|
promptActiveSkillNames = mergePromptActiveSkillNames(promptActiveSkillNames, bangInvocation.activeSkillNames);
|
|
1769
|
-
promptActiveSkillSource =
|
|
2001
|
+
promptActiveSkillSource =
|
|
2002
|
+
defaultActiveSkills.length > 0 ? `settings+${bangInvocation.source}` : bangInvocation.source;
|
|
1770
2003
|
}
|
|
1771
2004
|
}
|
|
1772
2005
|
let messages;
|
|
@@ -1870,8 +2103,8 @@ export class AgentSession {
|
|
|
1870
2103
|
const turnSystemPromptOptions = {
|
|
1871
2104
|
...this._baseSystemPromptOptions,
|
|
1872
2105
|
contextBudget: this._getContextBudgetOptions(expandedText),
|
|
1873
|
-
|
|
1874
|
-
|
|
2106
|
+
activeSkillNames: promptActiveSkillNames,
|
|
2107
|
+
activeSkillSource: promptActiveSkillSource,
|
|
1875
2108
|
};
|
|
1876
2109
|
const turnSystemPrompt = buildSystemPromptPlan(turnSystemPromptOptions);
|
|
1877
2110
|
// Emit before_agent_start extension event
|
|
@@ -2234,16 +2467,25 @@ export class AgentSession {
|
|
|
2234
2467
|
if (!this._modelRegistry.hasConfiguredAuth(model)) {
|
|
2235
2468
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
2236
2469
|
}
|
|
2237
|
-
// Root-level: block sticky safety models (Fable) unless
|
|
2470
|
+
// Root-level: block sticky safety models (Fable) unless the user pinned `--model`.
|
|
2238
2471
|
const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
|
|
2239
|
-
if (
|
|
2472
|
+
if (shouldEjectStickySafetyModel({
|
|
2473
|
+
blockStickySafetyModels: resilience.blockStickySafetyModels,
|
|
2474
|
+
modelPinned: this._modelPinned,
|
|
2475
|
+
}) &&
|
|
2476
|
+
isStickySafetyModel(model.id, model.provider)) {
|
|
2240
2477
|
throw new Error(stickySafetyBlockMessage(model.id, model.provider));
|
|
2241
2478
|
}
|
|
2242
2479
|
// Grok OAuth: block selecting Imagine models as the session chat model.
|
|
2243
2480
|
assertTextChatModelForCompletion(model.id, model.provider);
|
|
2244
2481
|
const previousModel = this.model;
|
|
2245
2482
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
2483
|
+
const nextSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames(), model);
|
|
2246
2484
|
this.agent.state.model = model;
|
|
2485
|
+
this._baseSystemPrompt = nextSystemPrompt;
|
|
2486
|
+
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
2487
|
+
this.agent.state.systemPromptCacheBoundary = this._baseSystemPromptCacheBoundary;
|
|
2488
|
+
this.agent.state.systemPromptCacheBoundaryBypass = false;
|
|
2247
2489
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
2248
2490
|
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
2249
2491
|
// Re-clamp thinking level for new model's capabilities
|
|
@@ -2957,34 +3199,29 @@ export class AgentSession {
|
|
|
2957
3199
|
*/
|
|
2958
3200
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
2959
3201
|
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.
|
|
3202
|
+
// Stale pre-compaction usage/errors must not retrigger compaction on the
|
|
3203
|
+
// first prompt after one finished.
|
|
2981
3204
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
2982
|
-
|
|
2983
|
-
|
|
3205
|
+
if (shouldSkipCompactionCheck({
|
|
3206
|
+
enabled: settings.enabled,
|
|
3207
|
+
skipAbortedCheck,
|
|
3208
|
+
stopReason: assistantMessage.stopReason,
|
|
3209
|
+
messageTimestamp: assistantMessage.timestamp,
|
|
3210
|
+
latestCompactionTimestamp: compactionEntry ? new Date(compactionEntry.timestamp).getTime() : undefined,
|
|
3211
|
+
})) {
|
|
2984
3212
|
return false;
|
|
2985
3213
|
}
|
|
2986
|
-
|
|
2987
|
-
|
|
3214
|
+
const contextWindow = this.model?.contextWindow ?? 0;
|
|
3215
|
+
// Case 1: Overflow - LLM returned context overflow error. Only counts when
|
|
3216
|
+
// the failing message belongs to the session model — or to the auto-routed
|
|
3217
|
+
// vision model while the session model is text-only.
|
|
3218
|
+
if (isSessionModelOverflow({
|
|
3219
|
+
message: assistantMessage,
|
|
3220
|
+
contextWindow,
|
|
3221
|
+
sessionProvider: this.model?.provider,
|
|
3222
|
+
sessionModelId: this.model?.id,
|
|
3223
|
+
sessionInputs: this.model?.input,
|
|
3224
|
+
})) {
|
|
2988
3225
|
if (this._overflowRecoveryAttempts >= MAX_OVERFLOW_RECOVERY_ATTEMPTS) {
|
|
2989
3226
|
this._emit({
|
|
2990
3227
|
type: "compaction_end",
|
|
@@ -3692,20 +3929,7 @@ export class AgentSession {
|
|
|
3692
3929
|
* Context overflow errors are NOT retryable (handled by compaction instead).
|
|
3693
3930
|
*/
|
|
3694
3931
|
_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);
|
|
3932
|
+
return isRetryableAssistantError(message, this.model?.contextWindow ?? 0);
|
|
3709
3933
|
}
|
|
3710
3934
|
/**
|
|
3711
3935
|
* Retry policy + callbacks shared by compaction and branch-summary summarization calls.
|
|
@@ -3742,8 +3966,12 @@ export class AgentSession {
|
|
|
3742
3966
|
/** Eject sticky safety model at prompt boundary (session resume / leftover default). */
|
|
3743
3967
|
async _ejectStickySafetyModelIfNeeded() {
|
|
3744
3968
|
const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
|
|
3745
|
-
if (!
|
|
3969
|
+
if (!shouldEjectStickySafetyModel({
|
|
3970
|
+
blockStickySafetyModels: resilience.blockStickySafetyModels,
|
|
3971
|
+
modelPinned: this._modelPinned,
|
|
3972
|
+
})) {
|
|
3746
3973
|
return;
|
|
3974
|
+
}
|
|
3747
3975
|
const current = this.model;
|
|
3748
3976
|
if (!current || !isStickySafetyModel(current.id, current.provider))
|
|
3749
3977
|
return;
|
|
@@ -3771,11 +3999,17 @@ export class AgentSession {
|
|
|
3771
3999
|
*/
|
|
3772
4000
|
async _maybeFailoverFromSafetyStop(message) {
|
|
3773
4001
|
const resilience = resolveProviderResilience(this.settingsManager.getProviderResilienceSettings());
|
|
3774
|
-
if (!
|
|
4002
|
+
if (!shouldHonorSafetyFailover({
|
|
4003
|
+
autoFailoverOnSafetyStop: resilience.autoFailoverOnSafetyStop,
|
|
4004
|
+
modelPinned: this._modelPinned,
|
|
4005
|
+
modelId: this.model?.id,
|
|
4006
|
+
provider: this.model?.provider,
|
|
4007
|
+
})) {
|
|
3775
4008
|
return undefined;
|
|
4009
|
+
}
|
|
3776
4010
|
// Fire on safety-stop FPs AND on billing/quota exhaustion: both mean the
|
|
3777
4011
|
// current model cannot finish this turn and same-model retry is useless.
|
|
3778
|
-
if (!
|
|
4012
|
+
if (!isFailoverTriggerError(message.errorMessage)) {
|
|
3779
4013
|
return undefined;
|
|
3780
4014
|
}
|
|
3781
4015
|
const current = this.model;
|
|
@@ -3783,10 +4017,10 @@ export class AgentSession {
|
|
|
3783
4017
|
// claude-opus-5 emits the same stop_reason=refusal FP; same-model retry is useless.
|
|
3784
4018
|
// v10.3-Ω: mark the model that just refused so we never re-pick it this turn.
|
|
3785
4019
|
if (current)
|
|
3786
|
-
this._refusedModels.add(
|
|
4020
|
+
this._refusedModels.add(failoverModelKey(current.provider, current.id));
|
|
3787
4021
|
const pick = pickFailoverCandidate(resilience.failoverCandidates, current ? { provider: current.provider, id: current.id } : undefined, (c) => {
|
|
3788
4022
|
// Skip any model that already refused/failed this turn → advance the chain.
|
|
3789
|
-
if (this._refusedModels.has(
|
|
4023
|
+
if (this._refusedModels.has(failoverModelKey(c.provider, c.id)))
|
|
3790
4024
|
return false;
|
|
3791
4025
|
const next = this._modelRegistry.find(c.provider, c.id);
|
|
3792
4026
|
if (!next)
|
|
@@ -3804,34 +4038,32 @@ export class AgentSession {
|
|
|
3804
4038
|
}
|
|
3805
4039
|
catch {
|
|
3806
4040
|
// setModel failed (auth/guard) — blacklist so next retry advances further.
|
|
3807
|
-
this._refusedModels.add(
|
|
4041
|
+
this._refusedModels.add(failoverModelKey(pick.provider, pick.id));
|
|
3808
4042
|
return undefined;
|
|
3809
4043
|
}
|
|
3810
4044
|
}
|
|
3811
4045
|
async _prepareRetry(message) {
|
|
3812
4046
|
const settings = this.settingsManager.getRetrySettings();
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
4047
|
+
const maxRetries = retryBudgetForAssistantError(message, settings.maxRetries);
|
|
4048
|
+
const attempt = nextRetryAttempt({
|
|
4049
|
+
enabled: settings.enabled,
|
|
4050
|
+
completedAttempts: this._retryAttempt,
|
|
4051
|
+
maxRetries,
|
|
4052
|
+
});
|
|
4053
|
+
if (attempt === undefined)
|
|
3820
4054
|
return false;
|
|
3821
|
-
|
|
4055
|
+
this._retryAttempt = attempt;
|
|
3822
4056
|
// Content/safety stop (Fable/Opus/Sonnet): switch model BEFORE delay so retry is not same-model refusal.
|
|
3823
4057
|
const failoverTo = await this._maybeFailoverFromSafetyStop(message);
|
|
3824
4058
|
// 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);
|
|
4059
|
+
const delayMs = computeRetryDelayMs(settings.baseDelayMs, attempt, failoverTo !== undefined);
|
|
3828
4060
|
const errorMessage = failoverTo
|
|
3829
4061
|
? `${message.errorMessage || "content/safety stop"} → failover ${failoverTo}`
|
|
3830
4062
|
: message.errorMessage || "Unknown error";
|
|
3831
4063
|
this._emit({
|
|
3832
4064
|
type: "auto_retry_start",
|
|
3833
|
-
attempt
|
|
3834
|
-
maxAttempts:
|
|
4065
|
+
attempt,
|
|
4066
|
+
maxAttempts: maxRetries,
|
|
3835
4067
|
delayMs,
|
|
3836
4068
|
errorMessage,
|
|
3837
4069
|
});
|
|
@@ -4343,21 +4575,6 @@ export class AgentSession {
|
|
|
4343
4575
|
return this._extensionRunner;
|
|
4344
4576
|
}
|
|
4345
4577
|
}
|
|
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
4578
|
function parsePositiveIntegerEnv(name) {
|
|
4362
4579
|
const raw = process.env[name];
|
|
4363
4580
|
if (raw === undefined || raw.trim() === "") {
|