maka-agent 0.2.0-dev.14.20260902 → 0.2.0-dev.16.20260903
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-core.js +0 -1
- package/dist/pi-transcript.js +48 -0
- package/dist/pi-tui-form-interaction.js +497 -0
- package/dist/pi-tui-pickers.js +256 -55
- package/dist/pi-tui-runner.js +215 -12
- package/dist/runtime-host-cli.js +17 -42
- package/dist/runtime-host-lifecycle-transaction.js +31 -13
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -1
- package/dist/runtime-host-onboarding.js +3 -1
- package/dist/runtime-host-openrc-service.js +469 -0
- package/dist/runtime-host-run-command.js +6 -2
- package/dist/runtime-host-service-command.js +3 -3
- package/dist/runtime-host-service-management-command.js +43 -15
- package/dist/runtime-host-session-channel.js +3 -0
- package/dist/runtime-host-session-driver.js +21 -6
- package/dist/runtime-host-setup-command.js +30 -31
- package/dist/tui-context-refresh.js +103 -0
- package/dist/tui-copy-catalog.js +73 -0
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/node_modules/@maka/core/dist/chat-model-choice.js +4 -0
- package/node_modules/@maka/core/dist/computer-use.js +27 -92
- package/node_modules/@maka/core/dist/interaction.js +577 -3
- package/node_modules/@maka/core/dist/llm-connections.js +4 -0
- package/node_modules/@maka/core/dist/local-memory.js +2 -2
- package/node_modules/@maka/core/dist/model-call-usage-projection.js +3 -0
- package/node_modules/@maka/core/dist/model-metadata.js +26 -7
- package/node_modules/@maka/core/dist/model-thinking.js +17 -0
- package/node_modules/@maka/core/dist/provider-registry.js +1 -0
- package/node_modules/@maka/core/dist/runtime-event.js +23 -1
- package/node_modules/@maka/core/dist/scheduled-task.js +4 -0
- package/node_modules/@maka/core/dist/session.js +95 -2
- package/node_modules/@maka/core/dist/text-sanitize.js +19 -0
- package/node_modules/@maka/core/dist/usage-ledger-merge.js +3 -0
- package/node_modules/@maka/core/dist/usage-record-schema.js +11 -5
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +75 -9
- package/node_modules/@maka/core/dist/workhub-session-resolver.js +50 -0
- package/node_modules/@maka/core/package.json +2 -0
- package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -1
- package/node_modules/@maka/eval/dist/harness-executor.js +3 -2
- package/node_modules/@maka/eval/dist/install-preflight.js +39 -10
- package/node_modules/@maka/eval/dist/process-termination.js +83 -0
- package/node_modules/@maka/mcp/dist/sep-2243.js +3 -3
- package/node_modules/@maka/runtime/dist/agent-run.js +6 -3
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +332 -118
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction-contract.js +6 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +171 -341
- package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +3 -26
- package/node_modules/@maka/runtime/dist/bundled-skill-catalog.generated.js +1 -1
- package/node_modules/@maka/runtime/dist/computer-use-codec.js +1 -141
- package/node_modules/@maka/runtime/dist/computer-use-tools.js +23 -105
- package/node_modules/@maka/runtime/dist/context-budget-policy.js +20 -26
- package/node_modules/@maka/runtime/dist/context-budget.js +18 -20
- package/node_modules/@maka/runtime/dist/conversation-copy.js +23 -19
- package/node_modules/@maka/runtime/dist/cua-frame-state.js +1 -69
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +267 -0
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +6 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +67 -23
- package/node_modules/@maka/runtime/dist/history-compact-summary-validation.js +11 -18
- package/node_modules/@maka/runtime/dist/history-compaction.js +66 -73
- package/node_modules/@maka/runtime/dist/interaction-authority.js +69 -0
- package/node_modules/@maka/runtime/dist/mcp-tools.js +13 -1
- package/node_modules/@maka/runtime/dist/memory-extraction.js +11 -11
- package/node_modules/@maka/runtime/dist/model-adapter.js +24 -6
- package/node_modules/@maka/runtime/dist/model-factory.js +14 -5
- package/node_modules/@maka/runtime/dist/oauth-provider-contracts.js +28 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +7 -6
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +29 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +11 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +12 -11
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +51 -13
- package/node_modules/@maka/runtime/dist/scheduled-task-tools.js +3 -0
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +26 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +1 -0
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +16 -1
- package/node_modules/@maka/runtime/dist/stream-graph-projection.js +6 -0
- package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +5 -0
- package/node_modules/@maka/runtime/dist/stream-usage-fallback-fetch.js +74 -0
- package/node_modules/@maka/runtime/dist/subscription-credentials.js +51 -4
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +47 -3
- package/node_modules/@maka/runtime/dist/system-prompt/main-session-prompt.js +20 -1
- package/node_modules/@maka/runtime/dist/test-connection.js +18 -11
- package/node_modules/@maka/runtime/dist/tool-runtime.js +183 -0
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +14 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +84 -1
- package/node_modules/@maka/runtime-host/dist/client/host-profile.js +68 -51
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +8 -5
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +342 -36
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +60 -4
- package/node_modules/@maka/runtime-host/dist/peer-mesh/index.js +3 -3
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +48 -60
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +847 -431
- package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +33 -74
- package/node_modules/@maka/runtime-host/dist/peer-mesh/store.js +82 -69
- package/node_modules/@maka/runtime-host/dist/peer-reachability/index.js +21 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/model.js +202 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/owner.js +119 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +245 -0
- package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +4 -1
- package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +68 -0
- package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +15 -8
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +7 -23
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +47 -1
- package/node_modules/@maka/runtime-host/dist/protocol/oauth.js +25 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/peer-mesh.js +14 -31
- package/node_modules/@maka/runtime-host/dist/protocol/scheduled-task.js +19 -6
- package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +36 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-revision.js +11 -2
- package/node_modules/@maka/runtime-host/dist/protocol/usage-pricing.js +16 -2
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +51 -2
- package/node_modules/@maka/runtime-host/dist/server/access-authority.js +35 -2
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +12 -10
- package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +163 -30
- package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +38 -14
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +45 -22
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +78 -20
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +4 -2
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +49 -28
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -6
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +130 -27
- package/node_modules/@maka/runtime-host/dist/server/interaction-projection.js +30 -1
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -4
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +16 -3
- package/node_modules/@maka/runtime-host/dist/server/oauth-coordinator.js +66 -3
- package/node_modules/@maka/runtime-host/dist/server/oauth-execution-authority.js +20 -3
- package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +8 -21
- package/node_modules/@maka/runtime-host/dist/server/peer-mesh-authority.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +6 -3
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +72 -12
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +48 -18
- package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +43 -26
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +15 -1
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +229 -4
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +146 -8
- package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +32 -6
- package/node_modules/@maka/runtime-host/package.json +1 -0
- package/node_modules/@maka/storage/dist/agent-run-store.js +18 -5
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +14 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +5 -0
- package/node_modules/@maka/storage/dist/interaction-store.js +13 -1
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +9 -3
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +49 -19
- package/node_modules/@maka/storage/dist/runtime-policy/oauth-login-receipt-document.js +3 -1
- package/node_modules/@maka/storage/dist/runtime-policy/onboarding-transaction.js +13 -5
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +4 -2
- package/node_modules/@maka/storage/dist/session-store.js +39 -6
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +16 -9
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +30 -3
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +117 -0
- package/node_modules/@maka/storage/dist/sqlite-usage-store.js +29 -6
- package/node_modules/@maka/storage/dist/usage-stores.js +3 -1
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ import { ToolRuntime, formatSyntheticToolErrorText, formatToolArgsViolationText,
|
|
|
35
35
|
import { ModelAdapter, } from './model-adapter.js';
|
|
36
36
|
import { buildProviderOptions } from './model-factory.js';
|
|
37
37
|
import { persistedOpenAiResponsesStepMessages } from './openai-responses-continuation.js';
|
|
38
|
+
import { nonCanonicalContentOrder } from './runtime-event-read-model.js';
|
|
38
39
|
import { composeRequestProjection, } from './request-projection.js';
|
|
39
40
|
import { decodePlaintextResponsesReasoningState, replayPlaintextResponsesProviderOptions, responsesReasoningItemId, } from './responses-reasoning-state.js';
|
|
40
41
|
import { toolResultOutput } from './tool-result-output.js';
|
|
@@ -58,7 +59,7 @@ import { MEMORY_EXTRACT_TOOL_NAME, MEMORY_REMEMBER_TOOL_NAME, buildMemoryExtract
|
|
|
58
59
|
import { modelUsesNativeOpenAiResponses, resolveModelRuntime } from './model-runtime.js';
|
|
59
60
|
import { applyPatchReplayFactText, normalizeApplyPatchReplayInput, routeApplyPatchTools, } from './apply-patch-profile.js';
|
|
60
61
|
import { applyRuntimeEventContextBudget, buildContextBudgetDiagnosticShell, estimateRuntimeEventsTokens, mergeContextBudgetDiagnostic, mergeContextBudgetDiagnosticPatches, minimalContextBudgetDiagnostic, shouldAppendContextCompactedNote, shouldAppendContextCompactionFailedOpenNote, } from './context-budget.js';
|
|
61
|
-
import {
|
|
62
|
+
import { isHistoryCompactContentEvent } from './history-compaction.js';
|
|
62
63
|
import { canContinueHistoryCompactCheckpointForModel, historyCompactCheckpointToModelMessage, isProviderHistoryCompactCheckpoint, matchHistoryCompactCheckpointPrefix, projectHistoryCompactCheckpointReplay, } from './history-compact-checkpoint.js';
|
|
63
64
|
import { resolveSelectedModelContextWindow } from './context-budget-policy.js';
|
|
64
65
|
export { DEFAULT_PERMISSION_TIMEOUT_MS, MAX_ACTIVE_CHILD_AGENT_RUNS_PER_TURN, MAX_ACTIVE_SUBAGENT_TOOLS_PER_TURN, TOOL_ERROR_RESULT_MAX_CHARS, formatSyntheticToolErrorText, } from './tool-runtime.js';
|
|
@@ -439,6 +440,11 @@ const MAX_WAITING_CODE_MODE_CELLS = 1;
|
|
|
439
440
|
const MAX_PROVIDER_ATTEMPTS_PER_STEP = 10;
|
|
440
441
|
const MAX_IDLE_WATCHDOG_RETRIES_PER_STEP = 1;
|
|
441
442
|
const MAX_INCOMPLETE_STREAM_RETRIES_PER_STEP = 1;
|
|
443
|
+
// A mid-stream cut after partial thinking seals one transcript fragment per
|
|
444
|
+
// retry. A gateway that systematically kills long thinking streams (the
|
|
445
|
+
// 2026-08-28 incident shape) would otherwise spend the full attempt budget
|
|
446
|
+
// accumulating fragments before failing anyway, so fail fast after one.
|
|
447
|
+
const MAX_SEALED_THINKING_RETRIES_PER_STEP = 1;
|
|
442
448
|
const PROVIDER_RETRY_BASE_DELAY_MS = 1_000;
|
|
443
449
|
const PROVIDER_RETRY_MAX_DELAY_MS = 32_000;
|
|
444
450
|
const PROVIDER_RETRY_JITTER_FACTOR = 0.25;
|
|
@@ -597,6 +603,15 @@ export class AiSdkBackend {
|
|
|
597
603
|
*/
|
|
598
604
|
activeTurns = new Set();
|
|
599
605
|
compaction;
|
|
606
|
+
/**
|
|
607
|
+
* The provider has been reported dropping context, for this backend.
|
|
608
|
+
*
|
|
609
|
+
* Not per send: the condition persists once a provider starts truncating, so
|
|
610
|
+
* a note on every later turn would repeat one fact the user has already been
|
|
611
|
+
* told. The scope is this backend's lifetime rather than the Session's, so a
|
|
612
|
+
* backend that is disposed and rebuilt may say it once more.
|
|
613
|
+
*/
|
|
614
|
+
contextProviderDroppingReported = false;
|
|
600
615
|
/** Session-scoped running total, deliberately accumulated across turns. */
|
|
601
616
|
cumulativeUsageCheckpoint;
|
|
602
617
|
memoryReplayMessageEvents = new WeakMap();
|
|
@@ -899,7 +914,12 @@ export class AiSdkBackend {
|
|
|
899
914
|
let stepTextPartStartOffset = 0;
|
|
900
915
|
let stepThinkingParts = [];
|
|
901
916
|
let stepThinkingPartsById = new Map();
|
|
917
|
+
let stepContentOrder = [];
|
|
902
918
|
const startedAt = this.now();
|
|
919
|
+
const recordStepContent = (kind) => {
|
|
920
|
+
if (!stepContentOrder.includes(kind))
|
|
921
|
+
stepContentOrder.push(kind);
|
|
922
|
+
};
|
|
903
923
|
// Flush the current step's AssistantMessage (text + thinking) and the paired
|
|
904
924
|
// terminal thinking/text events, then clear the per-step accumulators.
|
|
905
925
|
// Persist when the step produced text OR reasoning — a thinking-only step
|
|
@@ -909,12 +929,23 @@ export class AiSdkBackend {
|
|
|
909
929
|
// precedes text_complete so the read-model attaches this step's reasoning to
|
|
910
930
|
// this step's assistant row. Hoisted to send() scope so both the streaming
|
|
911
931
|
// path and the abort/error handler can flush a partial step.
|
|
932
|
+
const resetStep = () => {
|
|
933
|
+
stepText = '';
|
|
934
|
+
stepTextProviderOptions = undefined;
|
|
935
|
+
stepTextPartStartOffset = 0;
|
|
936
|
+
stepThinkingParts = [];
|
|
937
|
+
stepThinkingPartsById = new Map();
|
|
938
|
+
stepContentOrder = [];
|
|
939
|
+
};
|
|
912
940
|
const flushStep = async () => {
|
|
913
941
|
const hasThinking = stepThinkingParts.length > 0;
|
|
914
|
-
if (stepText.length === 0 && !hasThinking)
|
|
942
|
+
if (stepText.length === 0 && !hasThinking) {
|
|
943
|
+
resetStep();
|
|
915
944
|
return;
|
|
945
|
+
}
|
|
916
946
|
const stepId = currentStepMessageId;
|
|
917
947
|
const thinkingText = stepThinkingParts.map((part) => part.text).join('');
|
|
948
|
+
const contentOrder = nonCanonicalContentOrder(stepContentOrder);
|
|
918
949
|
const msg = {
|
|
919
950
|
type: 'assistant',
|
|
920
951
|
id: stepId,
|
|
@@ -924,6 +955,7 @@ export class AiSdkBackend {
|
|
|
924
955
|
...(stepTextProviderOptions !== undefined
|
|
925
956
|
? { providerOptions: stepTextProviderOptions }
|
|
926
957
|
: {}),
|
|
958
|
+
...(contentOrder ? { contentOrder } : {}),
|
|
927
959
|
modelId: this.input.modelId,
|
|
928
960
|
...(hasThinking
|
|
929
961
|
? {
|
|
@@ -976,11 +1008,7 @@ export class AiSdkBackend {
|
|
|
976
1008
|
: {}),
|
|
977
1009
|
});
|
|
978
1010
|
scope.finalAssistantText = stepText.length > 0 ? stepText : undefined;
|
|
979
|
-
|
|
980
|
-
stepTextProviderOptions = undefined;
|
|
981
|
-
stepTextPartStartOffset = 0;
|
|
982
|
-
stepThinkingParts = [];
|
|
983
|
-
stepThinkingPartsById = new Map();
|
|
1011
|
+
resetStep();
|
|
984
1012
|
};
|
|
985
1013
|
let tokenUsage;
|
|
986
1014
|
let tokenUsageCostUsd;
|
|
@@ -999,6 +1027,11 @@ export class AiSdkBackend {
|
|
|
999
1027
|
// result.usage.inputTokens is cumulative across steps and would produce
|
|
1000
1028
|
// misleading >100% percentages, so the per-step value is captured here.
|
|
1001
1029
|
let lastStepInputTokens;
|
|
1030
|
+
/** Tool count of the request that produced `lastStepInputTokens`. */
|
|
1031
|
+
let lastStepActiveToolCount;
|
|
1032
|
+
// Output tokens of the same step: with the input they are the baseline the
|
|
1033
|
+
// next request is judged from (everything the model produced is re-sent).
|
|
1034
|
+
let lastStepOutputTokens;
|
|
1002
1035
|
let streamStatus = 'success';
|
|
1003
1036
|
let streamErrorClass;
|
|
1004
1037
|
let runtimeSteps = 0;
|
|
@@ -1006,6 +1039,14 @@ export class AiSdkBackend {
|
|
|
1006
1039
|
let contextBudgetForTelemetry;
|
|
1007
1040
|
let contextCompactedNoteWritten = false;
|
|
1008
1041
|
let contextCompactionFailedOpenNoteWritten = false;
|
|
1042
|
+
let contextWindowOverrunNoteWritten = false;
|
|
1043
|
+
let contextReportedWindowNoteWritten = false;
|
|
1044
|
+
let contextOverflowAfterCompactionNoteWritten = false;
|
|
1045
|
+
let contextWindowSuggestionNoteWritten = false;
|
|
1046
|
+
// Request index (0-based) at which the active prune last rewrote the
|
|
1047
|
+
// request. A step Maka pruned is not append-only, so usage may legitimately
|
|
1048
|
+
// shrink.
|
|
1049
|
+
let pruneAppliedAtStep;
|
|
1009
1050
|
const trace = new RunTrace({
|
|
1010
1051
|
sessionId: this.sessionId,
|
|
1011
1052
|
turnId,
|
|
@@ -1332,22 +1373,19 @@ export class AiSdkBackend {
|
|
|
1332
1373
|
// the newest completed step; see collectPrunableCompletedStepToolCallIds.
|
|
1333
1374
|
const activeToolResultPruneIncludesNewestStep = midTurnState !== undefined;
|
|
1334
1375
|
const activeToolResultPruneHook = this.compaction.buildActiveToolResultPruneProjection(turnId, activeToolResultPruneIncludesNewestStep, (patch) => {
|
|
1376
|
+
pruneAppliedAtStep = runtimeSteps;
|
|
1335
1377
|
activeToolResultPruneDiagnosticPatch = mergeActiveToolResultPruneDiagnosticPatches(activeToolResultPruneDiagnosticPatch, patch);
|
|
1336
1378
|
});
|
|
1337
1379
|
const shapedProjection = composeRequestProjection(plan.projectActiveTools, midTurnCapacityHook, activeToolResultPruneHook);
|
|
1338
|
-
//
|
|
1339
|
-
//
|
|
1340
|
-
const requestProjection =
|
|
1341
|
-
? this.compaction.buildMidTurnFinalRequestRescue({
|
|
1342
|
-
shaped: shapedProjection,
|
|
1343
|
-
reentry: composeRequestProjection(undefined, midTurnCapacityHook, activeToolResultPruneHook),
|
|
1344
|
-
state: midTurnState,
|
|
1345
|
-
providerTools,
|
|
1346
|
-
charsPerToken: this.input.contextBudget?.charsPerToken ?? 4,
|
|
1347
|
-
})
|
|
1348
|
-
: shapedProjection;
|
|
1380
|
+
// Hooks shape; nothing measures the final payload. Whether it fits is
|
|
1381
|
+
// the provider's answer (#4559).
|
|
1382
|
+
const requestProjection = shapedProjection;
|
|
1349
1383
|
const completedProviderSteps = [];
|
|
1350
1384
|
let requestMessages = messages;
|
|
1385
|
+
// The compaction module runs at most once per send. This tracks the
|
|
1386
|
+
// reactive entry; the proactive one sets the same flag on the mid-turn
|
|
1387
|
+
// state, and each consults the other, so a send that already folded
|
|
1388
|
+
// reports the oversized message instead of folding again (#4559).
|
|
1351
1389
|
let overflowRetryUsed = false;
|
|
1352
1390
|
let result;
|
|
1353
1391
|
let providerOutcome;
|
|
@@ -1407,6 +1445,7 @@ export class AiSdkBackend {
|
|
|
1407
1445
|
let providerAttempt = 1;
|
|
1408
1446
|
let idleWatchdogRetryCount = 0;
|
|
1409
1447
|
let incompleteStreamRetryCount = 0;
|
|
1448
|
+
let sealedThinkingRetryCount = 0;
|
|
1410
1449
|
const returnedToolCalls = [];
|
|
1411
1450
|
let providerToolActivityCount = 0;
|
|
1412
1451
|
const providerToolInputs = new Map();
|
|
@@ -1428,7 +1467,13 @@ export class AiSdkBackend {
|
|
|
1428
1467
|
!attemptSawToolActivity &&
|
|
1429
1468
|
!attemptSawContinuationMetadata &&
|
|
1430
1469
|
!attemptReachedStepBoundary;
|
|
1431
|
-
|
|
1470
|
+
// Thinking is the only output that can be sealed into its own
|
|
1471
|
+
// message before a retry: flushStep() closes the fragment under
|
|
1472
|
+
// the current message id and the retry streams into a fresh one,
|
|
1473
|
+
// so the user never sees spliced or duplicated content. Text,
|
|
1474
|
+
// tool activity, continuation metadata, and step boundaries stay
|
|
1475
|
+
// non-recoverable for the reasons each of them is tracked.
|
|
1476
|
+
const attemptCanRecoverWithSealedThinking = () => !attemptSawText &&
|
|
1432
1477
|
!attemptSawToolActivity &&
|
|
1433
1478
|
!attemptSawContinuationMetadata &&
|
|
1434
1479
|
!attemptReachedStepBoundary;
|
|
@@ -1508,9 +1553,159 @@ export class AiSdkBackend {
|
|
|
1508
1553
|
providerStepUsage = stepUsage;
|
|
1509
1554
|
if (!stepUsage)
|
|
1510
1555
|
sawUnusableStepUsage = true;
|
|
1556
|
+
// Silent eviction / rewrite check (#4559): this step only
|
|
1557
|
+
// appended (no fold, no prune, no image omission) yet the
|
|
1558
|
+
// provider counted no more input tokens than for the previous
|
|
1559
|
+
// request. Not-greater, not strictly-fewer: a provider that
|
|
1560
|
+
// truncates to a fixed window (Ollama's `num_ctx`) reports the
|
|
1561
|
+
// same total on every later request while Maka keeps
|
|
1562
|
+
// appending, so a plateau is the signal, and an equal count
|
|
1563
|
+
// after an append is already impossible without provider-side
|
|
1564
|
+
// eviction or rewriting. Input against input: the previous
|
|
1565
|
+
// reply's reasoning may not be resent, so input + output is
|
|
1566
|
+
// not the floor of the next input on every wire.
|
|
1567
|
+
const completedRequestIndex = runtimeSteps - 1;
|
|
1568
|
+
// A finalization step resolves an empty tool set, so its
|
|
1569
|
+
// request legitimately drops several thousand schema tokens
|
|
1570
|
+
// with no fold, prune or image omission. Maka shaped that
|
|
1571
|
+
// request; the provider did not drop anything.
|
|
1572
|
+
const toolSchemaShrank = lastStepActiveToolCount !== undefined &&
|
|
1573
|
+
activeToolsForRequest.length < lastStepActiveToolCount;
|
|
1574
|
+
// Across the send boundary the comparison is the same one,
|
|
1575
|
+
// against the last request a provider accepted before this
|
|
1576
|
+
// send. A provider that truncates to a fixed window reports
|
|
1577
|
+
// the same input on every later request while the user keeps
|
|
1578
|
+
// adding turns, and a send of one or two steps never sees
|
|
1579
|
+
// that from the inside: the live evidence plateaus at 3,716
|
|
1580
|
+
// input tokens across eight turns with nothing reported
|
|
1581
|
+
// (#4623). The first request of a send therefore compares
|
|
1582
|
+
// against the persisted anchor, which is route-validated
|
|
1583
|
+
// where it is read; a fold before that request would explain
|
|
1584
|
+
// a smaller input by itself, so it disables the comparison.
|
|
1585
|
+
const acrossSends = completedRequestIndex === 0;
|
|
1586
|
+
const priorInput = acrossSends
|
|
1587
|
+
? midTurnState?.compactionAppliedThisSend === true
|
|
1588
|
+
? undefined
|
|
1589
|
+
: midTurnState?.priorAcceptedInputTokens
|
|
1590
|
+
: lastStepInputTokens;
|
|
1591
|
+
if (!this.contextProviderDroppingReported &&
|
|
1592
|
+
!toolSchemaShrank &&
|
|
1593
|
+
midTurnState &&
|
|
1594
|
+
priorInput !== undefined &&
|
|
1595
|
+
midTurnState.replacedStepNumber !== completedRequestIndex &&
|
|
1596
|
+
pruneAppliedAtStep !== completedRequestIndex &&
|
|
1597
|
+
midTurnState.omittedImageToolResults.size === 0 &&
|
|
1598
|
+
stepUsage !== undefined &&
|
|
1599
|
+
Number.isFinite(stepUsage.inputTokens) &&
|
|
1600
|
+
stepUsage.inputTokens > 0 &&
|
|
1601
|
+
// Across sends the test is equality, not "did not grow".
|
|
1602
|
+
// Inside a send Maka knows it only appended, so any
|
|
1603
|
+
// shortfall is the provider's. Across the boundary it does
|
|
1604
|
+
// not: a manual compaction leaves the pre-compaction anchor
|
|
1605
|
+
// behind, a turn can carry a smaller tool set, and a user
|
|
1606
|
+
// can edit or branch history. All three shrink the input
|
|
1607
|
+
// legitimately, and none of them lands on exactly the same
|
|
1608
|
+
// count. A provider truncating to a fixed window does, on
|
|
1609
|
+
// every later request.
|
|
1610
|
+
(acrossSends
|
|
1611
|
+
? stepUsage.inputTokens === priorInput
|
|
1612
|
+
: stepUsage.inputTokens <= priorInput)) {
|
|
1613
|
+
this.contextProviderDroppingReported = true;
|
|
1614
|
+
const note = {
|
|
1615
|
+
type: 'system_note',
|
|
1616
|
+
id: this.newId(),
|
|
1617
|
+
turnId,
|
|
1618
|
+
ts: this.now(),
|
|
1619
|
+
kind: 'context_provider_dropping',
|
|
1620
|
+
data: { inputTokens: stepUsage.inputTokens, priorInputTokens: priorInput },
|
|
1621
|
+
};
|
|
1622
|
+
await this.input.appendMessage(note).catch(() => { });
|
|
1623
|
+
}
|
|
1511
1624
|
// Fail closed: reset on every step boundary so a missing final
|
|
1512
1625
|
// step's usage does not leave a stale value from an earlier step.
|
|
1626
|
+
// The reply needed more room than the declared window had
|
|
1627
|
+
// left after this request's own input. Both halves are the
|
|
1628
|
+
// provider's numbers, read after the fact: the reserve that
|
|
1629
|
+
// should have kept them apart was measured from a smaller
|
|
1630
|
+
// previous reply. Say so once per send; the next request
|
|
1631
|
+
// folds anyway because the baseline now exceeds the window.
|
|
1632
|
+
if (!contextWindowOverrunNoteWritten &&
|
|
1633
|
+
midTurnState?.capacity !== undefined &&
|
|
1634
|
+
stepUsage !== undefined &&
|
|
1635
|
+
Number.isFinite(stepUsage.inputTokens) &&
|
|
1636
|
+
stepUsage.inputTokens > 0 &&
|
|
1637
|
+
Number.isFinite(stepUsage.outputTokens) &&
|
|
1638
|
+
stepUsage.outputTokens > 0 &&
|
|
1639
|
+
stepUsage.inputTokens + stepUsage.outputTokens > midTurnState.capacity) {
|
|
1640
|
+
contextWindowOverrunNoteWritten = true;
|
|
1641
|
+
const note = {
|
|
1642
|
+
type: 'system_note',
|
|
1643
|
+
id: this.newId(),
|
|
1644
|
+
turnId,
|
|
1645
|
+
ts: this.now(),
|
|
1646
|
+
kind: 'context_window_overrun',
|
|
1647
|
+
data: {
|
|
1648
|
+
usedTokens: stepUsage.inputTokens + stepUsage.outputTokens,
|
|
1649
|
+
declaredContextWindow: midTurnState.capacity,
|
|
1650
|
+
},
|
|
1651
|
+
};
|
|
1652
|
+
await this.input.appendMessage(note).catch(() => { });
|
|
1653
|
+
}
|
|
1654
|
+
// Nothing declared, and the provider accepted a request past
|
|
1655
|
+
// the window this model reports. Every other signal in this
|
|
1656
|
+
// design stays dark there: no rejection to recover from, no
|
|
1657
|
+
// plateau to read, and no declaration to arm the proactive
|
|
1658
|
+
// threshold, so the session degrades quietly and
|
|
1659
|
+
// indefinitely (#4634). Report the two real numbers and
|
|
1660
|
+
// leave the decision with the user: a reported window is a
|
|
1661
|
+
// hint, and Maka still declares nothing on their behalf.
|
|
1662
|
+
//
|
|
1663
|
+
// Once per crossing, not once per send. On these providers
|
|
1664
|
+
// usage keeps growing past the line (305K → 322K observed),
|
|
1665
|
+
// so the note fires on the transition: the previous accepted
|
|
1666
|
+
// total was still inside the reported window and this one is
|
|
1667
|
+
// not. The baseline carries that previous total across
|
|
1668
|
+
// sessions through the persisted anchor, so a resumed
|
|
1669
|
+
// session does not repeat a crossing it already reported.
|
|
1670
|
+
if (!contextReportedWindowNoteWritten &&
|
|
1671
|
+
midTurnState !== undefined &&
|
|
1672
|
+
midTurnState.capacity === undefined &&
|
|
1673
|
+
stepUsage !== undefined &&
|
|
1674
|
+
Number.isFinite(stepUsage.inputTokens) &&
|
|
1675
|
+
stepUsage.inputTokens > 0 &&
|
|
1676
|
+
Number.isFinite(stepUsage.outputTokens)) {
|
|
1677
|
+
const reported = resolveSelectedModelContextWindow(this.input.connection, this.input.modelId);
|
|
1678
|
+
const used = stepUsage.inputTokens + Math.max(0, stepUsage.outputTokens);
|
|
1679
|
+
// `baselineTokens` still describes the request before this
|
|
1680
|
+
// one: the capacity hook sets it from the previous step, or
|
|
1681
|
+
// from the persisted anchor on a send's first request.
|
|
1682
|
+
const previousTotal = midTurnState.baselineTokens;
|
|
1683
|
+
const crossedNow = reported !== undefined &&
|
|
1684
|
+
used > reported &&
|
|
1685
|
+
(previousTotal === undefined || previousTotal <= reported);
|
|
1686
|
+
if (reported !== undefined && crossedNow) {
|
|
1687
|
+
contextReportedWindowNoteWritten = true;
|
|
1688
|
+
const note = {
|
|
1689
|
+
type: 'system_note',
|
|
1690
|
+
id: this.newId(),
|
|
1691
|
+
turnId,
|
|
1692
|
+
ts: this.now(),
|
|
1693
|
+
kind: 'context_reported_window_exceeded',
|
|
1694
|
+
data: { usedTokens: used, reportedContextWindow: reported },
|
|
1695
|
+
};
|
|
1696
|
+
await this.input.appendMessage(note).catch(() => { });
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1513
1699
|
lastStepInputTokens = stepUsage?.inputTokens;
|
|
1700
|
+
lastStepOutputTokens = stepUsage?.outputTokens;
|
|
1701
|
+
lastStepActiveToolCount = activeToolsForRequest.length;
|
|
1702
|
+
// A `finishReason: length` is deliberately not a trigger. The
|
|
1703
|
+
// reply may have been cut because the provider ran out of
|
|
1704
|
+
// window room, or because the provider's own output cap is
|
|
1705
|
+
// lower than the one Maka sends. Those are indistinguishable
|
|
1706
|
+
// from outside, and an indistinguishable signal must not
|
|
1707
|
+
// drive an action; the cut reply is visible to the user
|
|
1708
|
+
// either way (#4559).
|
|
1514
1709
|
if (stepUsage) {
|
|
1515
1710
|
completedStepUsage = mergeNormalizedUsage(completedStepUsage, stepUsage);
|
|
1516
1711
|
this.cumulativeUsageCheckpoint = mergeNormalizedUsage(this.cumulativeUsageCheckpoint, stepUsage);
|
|
@@ -1522,9 +1717,15 @@ export class AiSdkBackend {
|
|
|
1522
1717
|
}
|
|
1523
1718
|
}
|
|
1524
1719
|
if (event.kind === 'text-start') {
|
|
1720
|
+
if (stepText.length > 0 && event.providerItemBoundary === true) {
|
|
1721
|
+
await flushStep();
|
|
1722
|
+
currentStepMessageId = this.newId();
|
|
1723
|
+
}
|
|
1525
1724
|
stepTextPartStartOffset = stepText.length;
|
|
1526
1725
|
}
|
|
1527
1726
|
else if (event.kind === 'text') {
|
|
1727
|
+
if (event.text.length > 0)
|
|
1728
|
+
recordStepContent('text');
|
|
1528
1729
|
stepText += event.text;
|
|
1529
1730
|
if (event.text.length > 0)
|
|
1530
1731
|
attemptSawText = true;
|
|
@@ -1537,9 +1738,15 @@ export class AiSdkBackend {
|
|
|
1537
1738
|
text: event.text,
|
|
1538
1739
|
});
|
|
1539
1740
|
}
|
|
1540
|
-
else if (event.kind === 'text-
|
|
1541
|
-
|
|
1542
|
-
|
|
1741
|
+
else if (event.kind === 'text-end') {
|
|
1742
|
+
if (event.providerOptions !== undefined) {
|
|
1743
|
+
attemptSawContinuationMetadata = true;
|
|
1744
|
+
stepTextProviderOptions = mergeTextProviderOptions(stepTextProviderOptions, stripUndefinedDeep(event.providerOptions), stepTextPartStartOffset);
|
|
1745
|
+
}
|
|
1746
|
+
if (event.providerItemBoundary === true) {
|
|
1747
|
+
await flushStep();
|
|
1748
|
+
currentStepMessageId = this.newId();
|
|
1749
|
+
}
|
|
1543
1750
|
}
|
|
1544
1751
|
else if (event.kind === 'thinking-start') {
|
|
1545
1752
|
if (event.providerOptions !== undefined) {
|
|
@@ -1557,6 +1764,8 @@ export class AiSdkBackend {
|
|
|
1557
1764
|
}
|
|
1558
1765
|
}
|
|
1559
1766
|
else if (event.kind === 'thinking') {
|
|
1767
|
+
if (event.text.length > 0)
|
|
1768
|
+
recordStepContent('thinking');
|
|
1560
1769
|
if (event.text.length > 0)
|
|
1561
1770
|
attemptSawThinking = true;
|
|
1562
1771
|
if (event.providerOptions !== undefined) {
|
|
@@ -1638,6 +1847,7 @@ export class AiSdkBackend {
|
|
|
1638
1847
|
}
|
|
1639
1848
|
else if (event.kind === 'tool-call') {
|
|
1640
1849
|
attemptSawToolActivity = true;
|
|
1850
|
+
recordStepContent('tools');
|
|
1641
1851
|
if (event.toolCall.providerExecuted) {
|
|
1642
1852
|
providerToolActivityCount += 1;
|
|
1643
1853
|
providerToolInputs.set(event.toolCall.toolCallId, event.toolCall.input);
|
|
@@ -1732,14 +1942,12 @@ export class AiSdkBackend {
|
|
|
1732
1942
|
const recovered = stepBudgetRemains && attemptHasNoObservableOutput()
|
|
1733
1943
|
? await this.compaction.recoverFromOverflowError({
|
|
1734
1944
|
error: attemptFailure,
|
|
1735
|
-
retryAlreadyUsed: overflowRetryUsed,
|
|
1945
|
+
retryAlreadyUsed: overflowRetryUsed || (midTurnState?.compactionAttemptedThisSend ?? false),
|
|
1736
1946
|
midTurnState,
|
|
1737
1947
|
turnId,
|
|
1738
1948
|
stepNumber: runtimeSteps,
|
|
1739
1949
|
currentMessages: attemptMessages,
|
|
1740
|
-
providerTools,
|
|
1741
1950
|
activeTools: activeToolsForRequest,
|
|
1742
|
-
systemPromptChars: requestSystemPrompt?.length ?? 0,
|
|
1743
1951
|
queue,
|
|
1744
1952
|
onDiagnosticPatch: onMidTurnDiagnosticPatch,
|
|
1745
1953
|
origin: scope,
|
|
@@ -1771,32 +1979,98 @@ export class AiSdkBackend {
|
|
|
1771
1979
|
attemptMessages = recoveredProjection?.messages ?? recovered.messages;
|
|
1772
1980
|
continue;
|
|
1773
1981
|
}
|
|
1982
|
+
// Window suggestion (#4559): the provider rejected a request and
|
|
1983
|
+
// no recovery is left — the one fold is spent, or there was no
|
|
1984
|
+
// seam. The baseline is a proven-fit total (input + output of an
|
|
1985
|
+
// accepted request), so it is a number the user can declare; the
|
|
1986
|
+
// trigger is `>=`, so declaring exactly it folds before this
|
|
1987
|
+
// point next time. Once per send, and only when the turn is
|
|
1988
|
+
// about to surface the error rather than continue.
|
|
1989
|
+
const acceptedTotal = midTurnState?.lastAcceptedTotalTokens;
|
|
1990
|
+
if (!contextWindowSuggestionNoteWritten &&
|
|
1991
|
+
failure.kind === 'context_overflow' &&
|
|
1992
|
+
midTurnState &&
|
|
1993
|
+
acceptedTotal !== undefined &&
|
|
1994
|
+
(midTurnState.capacity === undefined || acceptedTotal < midTurnState.capacity)) {
|
|
1995
|
+
contextWindowSuggestionNoteWritten = true;
|
|
1996
|
+
const note = {
|
|
1997
|
+
type: 'system_note',
|
|
1998
|
+
id: this.newId(),
|
|
1999
|
+
turnId,
|
|
2000
|
+
ts: this.now(),
|
|
2001
|
+
kind: 'context_window_suggestion',
|
|
2002
|
+
data: {
|
|
2003
|
+
suggestedContextWindow: acceptedTotal,
|
|
2004
|
+
...(midTurnState.capacity !== undefined
|
|
2005
|
+
? { declaredContextWindow: midTurnState.capacity }
|
|
2006
|
+
: {}),
|
|
2007
|
+
},
|
|
2008
|
+
};
|
|
2009
|
+
await this.input.appendMessage(note).catch(() => { });
|
|
2010
|
+
}
|
|
2011
|
+
// A folded projection was selected in this send and the provider
|
|
2012
|
+
// still rejects the request. That is worth saying, because the
|
|
2013
|
+
// usual remedy has already been applied; it is NOT proof that the
|
|
2014
|
+
// new message alone is the cause, since what remains also carries
|
|
2015
|
+
// the system prompt, the tool schemas, the summary and the recent
|
|
2016
|
+
// tail. A fold that failed open is deliberately excluded: that
|
|
2017
|
+
// request went out with its full raw history, so nothing about
|
|
2018
|
+
// its size can be concluded (#4559).
|
|
2019
|
+
if (!contextOverflowAfterCompactionNoteWritten &&
|
|
2020
|
+
failure.kind === 'context_overflow' &&
|
|
2021
|
+
midTurnState?.compactionAppliedThisSend === true) {
|
|
2022
|
+
contextOverflowAfterCompactionNoteWritten = true;
|
|
2023
|
+
const note = {
|
|
2024
|
+
type: 'system_note',
|
|
2025
|
+
id: this.newId(),
|
|
2026
|
+
turnId,
|
|
2027
|
+
ts: this.now(),
|
|
2028
|
+
kind: 'context_overflow_after_compaction',
|
|
2029
|
+
};
|
|
2030
|
+
await this.input.appendMessage(note).catch(() => { });
|
|
2031
|
+
}
|
|
1774
2032
|
const idleWatchdogRecovery = settledWatchdogTimeout?.phase === 'idle' &&
|
|
1775
2033
|
idleWatchdogRetryCount < MAX_IDLE_WATCHDOG_RETRIES_PER_STEP &&
|
|
1776
|
-
|
|
2034
|
+
attemptCanRecoverWithSealedThinking();
|
|
1777
2035
|
const incompleteStreamRecovery = incompleteStreamTerminal &&
|
|
1778
2036
|
incompleteStreamRetryCount < MAX_INCOMPLETE_STREAM_RETRIES_PER_STEP &&
|
|
1779
2037
|
incompleteStreamHasNoObservableOutput;
|
|
2038
|
+
// Same seal-and-retry contract as the watchdog path, entered when
|
|
2039
|
+
// the failure arrives as a retryable provider/network error
|
|
2040
|
+
// instead of a local idle timeout. `!idleWatchdogRecovery` keeps
|
|
2041
|
+
// every watchdog-shaped outcome on its existing path, and
|
|
2042
|
+
// `!attemptHasNoObservableOutput()` keeps no-output retries on
|
|
2043
|
+
// the plain budget so this one is spent only on sealed fragments.
|
|
2044
|
+
const sealedThinkingRecovery = !idleWatchdogRecovery &&
|
|
2045
|
+
failure.retryable &&
|
|
2046
|
+
sealedThinkingRetryCount < MAX_SEALED_THINKING_RETRIES_PER_STEP &&
|
|
2047
|
+
attemptCanRecoverWithSealedThinking() &&
|
|
2048
|
+
!attemptHasNoObservableOutput();
|
|
1780
2049
|
if ((failure.retryable || idleWatchdogRecovery || incompleteStreamRecovery) &&
|
|
1781
2050
|
failure.kind !== 'context_overflow' &&
|
|
1782
2051
|
providerAttempt < MAX_PROVIDER_ATTEMPTS_PER_STEP &&
|
|
1783
2052
|
stepBudgetRemains &&
|
|
1784
|
-
(attemptHasNoObservableOutput() ||
|
|
1785
|
-
|
|
2053
|
+
(attemptHasNoObservableOutput() ||
|
|
2054
|
+
idleWatchdogRecovery ||
|
|
2055
|
+
incompleteStreamRecovery ||
|
|
2056
|
+
sealedThinkingRecovery)) {
|
|
2057
|
+
if (idleWatchdogRecovery)
|
|
1786
2058
|
idleWatchdogRetryCount += 1;
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
currentStepMessageId = this.newId();
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
2059
|
+
if (sealedThinkingRecovery)
|
|
2060
|
+
sealedThinkingRetryCount += 1;
|
|
1792
2061
|
if (incompleteStreamRecovery)
|
|
1793
2062
|
incompleteStreamRetryCount += 1;
|
|
2063
|
+
if ((idleWatchdogRecovery || sealedThinkingRecovery) &&
|
|
2064
|
+
stepThinkingParts.length > 0) {
|
|
2065
|
+
await flushStep();
|
|
2066
|
+
currentStepMessageId = this.newId();
|
|
2067
|
+
}
|
|
1794
2068
|
// The failed request did not return authoritative usage. Keep
|
|
1795
2069
|
// effectiveness recoverable, but fail final metering closed.
|
|
1796
2070
|
sawUnusableStepUsage = true;
|
|
1797
2071
|
const delayMs = providerRetryDelayMs(providerAttempt, failure.retryAfterMs);
|
|
1798
2072
|
const nextAttempt = providerAttempt + 1;
|
|
1799
|
-
const maxAttempts = idleWatchdogRecovery || incompleteStreamRecovery
|
|
2073
|
+
const maxAttempts = idleWatchdogRecovery || incompleteStreamRecovery || sealedThinkingRecovery
|
|
1800
2074
|
? nextAttempt
|
|
1801
2075
|
: MAX_PROVIDER_ATTEMPTS_PER_STEP;
|
|
1802
2076
|
const reason = providerRetryReason(failure.kind);
|
|
@@ -2030,13 +2304,16 @@ export class AiSdkBackend {
|
|
|
2030
2304
|
}
|
|
2031
2305
|
return undefined;
|
|
2032
2306
|
})();
|
|
2033
|
-
// The anchor the NEXT turn
|
|
2307
|
+
// The anchor the NEXT turn judges its first request from — see
|
|
2034
2308
|
// `LastRequestAnchor`. `input` below is the sum across this send's
|
|
2035
|
-
// steps and anchors nothing
|
|
2036
|
-
//
|
|
2037
|
-
//
|
|
2309
|
+
// steps and anchors nothing; the LAST step's real input and output
|
|
2310
|
+
// are what the next request re-sends. No usable input count, no
|
|
2311
|
+
// anchor: the next turn then has no proactive fold until its first
|
|
2312
|
+
// accepted request.
|
|
2038
2313
|
const anchorInputTokens = finitePositive(lastStepInputTokens);
|
|
2039
|
-
const
|
|
2314
|
+
const anchorOutputTokens = lastStepOutputTokens !== undefined && Number.isFinite(lastStepOutputTokens)
|
|
2315
|
+
? Math.max(0, lastStepOutputTokens)
|
|
2316
|
+
: undefined;
|
|
2040
2317
|
// One shared usage payload for the durable message and the live
|
|
2041
2318
|
// event: twin per-field literals drifted before (#4019), so a field
|
|
2042
2319
|
// now has exactly one definition site.
|
|
@@ -2065,11 +2342,17 @@ export class AiSdkBackend {
|
|
|
2065
2342
|
? { contextRemaining: contextRemainingForUsage }
|
|
2066
2343
|
: {}),
|
|
2067
2344
|
...(providerRequestTraceId ? { providerRequestTraceId } : {}),
|
|
2068
|
-
...(anchorInputTokens !== undefined
|
|
2345
|
+
...(anchorInputTokens !== undefined
|
|
2069
2346
|
? {
|
|
2070
2347
|
lastRequestAnchor: {
|
|
2071
2348
|
inputTokens: anchorInputTokens,
|
|
2072
|
-
|
|
2349
|
+
...(anchorOutputTokens !== undefined
|
|
2350
|
+
? { outputTokens: anchorOutputTokens }
|
|
2351
|
+
: {}),
|
|
2352
|
+
modelId: this.input.modelId,
|
|
2353
|
+
...(this.input.header.llmConnectionId !== undefined
|
|
2354
|
+
? { connectionId: this.input.header.llmConnectionId }
|
|
2355
|
+
: {}),
|
|
2073
2356
|
},
|
|
2074
2357
|
}
|
|
2075
2358
|
: {}),
|
|
@@ -2604,63 +2887,9 @@ export class AiSdkBackend {
|
|
|
2604
2887
|
contextBudgetDiagnostic = mergeContextBudgetDiagnostic(contextBudgetDiagnostic ??
|
|
2605
2888
|
buildContextBudgetDiagnosticShell(priorRuntimeContext, runtimeContext, contextBudget), preparedContextBudget.diagnosticPatch);
|
|
2606
2889
|
}
|
|
2607
|
-
|
|
2608
|
-
//
|
|
2609
|
-
//
|
|
2610
|
-
// weighs prior history events at chars/4. It stands in only where that
|
|
2611
|
-
// estimate cannot reach — no anchor, no mid-turn seam, or no declared
|
|
2612
|
-
// window — so an oversized history never goes out unshaped.
|
|
2613
|
-
const needsCompaction = !(midTurnState?.capacity !== undefined && midTurnState.lastRequestInputTokens !== undefined) &&
|
|
2614
|
-
maxHistoryTokens !== undefined &&
|
|
2615
|
-
estimateRuntimeEventsTokens(runtimeContext, contextBudget?.charsPerToken) > maxHistoryTokens;
|
|
2616
|
-
if (needsCompaction &&
|
|
2617
|
-
contextBudget?.historyCompact?.enabled === true &&
|
|
2618
|
-
this.compaction.hasHistoryCompactCheckpointWriter()) {
|
|
2619
|
-
const automaticMemoryDecision = automaticMemory
|
|
2620
|
-
? this.automaticMemoryCompactionDecision()
|
|
2621
|
-
: undefined;
|
|
2622
|
-
const automaticMemorySource = automaticMemoryDecision
|
|
2623
|
-
? lastNonCompactRuntimeEvent(priorRuntimeContext)
|
|
2624
|
-
: undefined;
|
|
2625
|
-
const compactResult = await this.compaction.compactHistory({
|
|
2626
|
-
turnId: input.turnId,
|
|
2627
|
-
runId: scope.runId,
|
|
2628
|
-
runtimeContext: priorRuntimeContext,
|
|
2629
|
-
runtimeContextRunHeaders: input.runtimeContextRunHeaders,
|
|
2630
|
-
}, automaticMemorySource
|
|
2631
|
-
? {
|
|
2632
|
-
runId: automaticMemorySource.runId,
|
|
2633
|
-
turnId: automaticMemorySource.turnId,
|
|
2634
|
-
runtimeEventId: automaticMemorySource.id,
|
|
2635
|
-
disposition: automaticMemoryDecision.disposition,
|
|
2636
|
-
}
|
|
2637
|
-
: undefined);
|
|
2638
|
-
let durableCheckpoint = compactResult.checkpoint;
|
|
2639
|
-
if (!durableCheckpoint && compactResult.outcome.kind === 'unchanged') {
|
|
2640
|
-
try {
|
|
2641
|
-
durableCheckpoint = await Promise.resolve(this.input.loadHistoryCompactCheckpoint?.());
|
|
2642
|
-
}
|
|
2643
|
-
catch {
|
|
2644
|
-
durableCheckpoint = undefined;
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
if (durableCheckpoint) {
|
|
2648
|
-
const replay = buildHistoryCompactCheckpointFailOpenContext(durableCheckpoint, priorRuntimeContext, contextBudget, priorRuntimeContext);
|
|
2649
|
-
runtimeContext = replay.events;
|
|
2650
|
-
projectedHistoryCompactCheckpoint = replay.checkpoint;
|
|
2651
|
-
if (replay.checkpoint &&
|
|
2652
|
-
compactResult.outcome.kind === 'compacted' &&
|
|
2653
|
-
automaticMemoryDecision?.dispatch &&
|
|
2654
|
-
automaticMemory) {
|
|
2655
|
-
this.dispatchAutomaticMemoryCompaction(scope, {
|
|
2656
|
-
checkpoint: replay.checkpoint,
|
|
2657
|
-
activeTools: [],
|
|
2658
|
-
});
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
contextBudgetDiagnostic = mergeContextBudgetDiagnostic(contextBudgetDiagnostic ??
|
|
2662
|
-
buildContextBudgetDiagnosticShell(priorRuntimeContext, runtimeContext, contextBudget), compactResult.contextBudget ?? {});
|
|
2663
|
-
}
|
|
2890
|
+
// No pre-turn estimate gate: the turn's first request is judged by the
|
|
2891
|
+
// request-projection hook from the previous request's real usage, and by
|
|
2892
|
+
// the provider when it goes out (#4559).
|
|
2664
2893
|
// The boundary belongs to the runtime-event projection above. A gate that
|
|
2665
2894
|
// falls back to the stored-message projection returns a prompt no
|
|
2666
2895
|
// checkpoint shaped, so it reports none rather than one the request never
|
|
@@ -3811,28 +4040,13 @@ function buildHistoryCompactCheckpointFailOpenContext(checkpoint, priorRuntimeCo
|
|
|
3811
4040
|
byTurn.set(event.turnId, [event]);
|
|
3812
4041
|
}
|
|
3813
4042
|
}
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
: estimateRuntimeEventsTokens(replayPrefix, charsPerToken);
|
|
3819
|
-
const selectedGroups = [];
|
|
3820
|
-
for (let index = turnOrder.length - 1; index >= 0; index -= 1) {
|
|
3821
|
-
const group = byTurn.get(turnOrder[index]) ?? [];
|
|
3822
|
-
const groupTokens = estimateRuntimeEventsTokens(group, charsPerToken);
|
|
3823
|
-
if (selectedTokens + groupTokens > maxTokens)
|
|
3824
|
-
break;
|
|
3825
|
-
selectedGroups.unshift(group);
|
|
3826
|
-
selectedTokens += groupTokens;
|
|
3827
|
-
}
|
|
4043
|
+
// Replay is structural: the checkpoint plus everything after its boundary.
|
|
4044
|
+
// No size-based selection stands between them and dispatch; whether the
|
|
4045
|
+
// result fits is the provider's answer (#4559).
|
|
4046
|
+
const selectedGroups = turnOrder.map((turnId) => byTurn.get(turnId) ?? []);
|
|
3828
4047
|
const replayTail = selectedGroups.flat();
|
|
3829
4048
|
const replayEvents = projectHistoryCompactCheckpointReplay(checkpoint, match.coveredRuntimeEvents, replayTail);
|
|
3830
|
-
|
|
3831
|
-
return evaluateHistoryCompactCheckpointReplay(checkpoint, replayTailForFit, policy?.charsPerToken, policy?.maxHistoryEstimatedTokens, {
|
|
3832
|
-
sourceReplayEvents: [...match.coveredRuntimeEvents, ...replayTail],
|
|
3833
|
-
}).fits
|
|
3834
|
-
? { events: replayEvents, checkpoint }
|
|
3835
|
-
: { events: [...retainedCandidates] };
|
|
4049
|
+
return { events: replayEvents, checkpoint };
|
|
3836
4050
|
}
|
|
3837
4051
|
function projectMemoryConversationPrefix(messages, eventMessagePositions) {
|
|
3838
4052
|
// Context visibility and durable evidence authority are separate boundaries.
|