mixdog 0.9.51 → 0.9.53
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/package.json +5 -3
- package/scripts/abort-recovery-test.mjs +17 -1
- package/scripts/agent-model-liveness-test.mjs +79 -2
- package/scripts/anthropic-admission-retry-integration-test.mjs +119 -0
- package/scripts/anthropic-transport-policy-test.mjs +466 -0
- package/scripts/atomic-lock-tryonce-test.mjs +60 -1
- package/scripts/bench-run.mjs +2 -2
- package/scripts/build-tui.mjs +13 -1
- package/scripts/channel-daemon-smoke.mjs +630 -10
- package/scripts/code-graph-aggregate-cwd-test.mjs +41 -37
- package/scripts/code-graph-disk-hit-test.mjs +11 -0
- package/scripts/code-graph-root-federation-test.mjs +273 -0
- package/scripts/compact-pressure-test.mjs +159 -1
- package/scripts/compact-smoke.mjs +80 -0
- package/scripts/context-mcp-metering-test.mjs +1350 -19
- package/scripts/deferred-tool-loading-test.mjs +17 -0
- package/scripts/desktop-session-bridge-test.mjs +704 -0
- package/scripts/freevar-smoke.mjs +7 -4
- package/scripts/gemini-provider-test.mjs +1053 -0
- package/scripts/interrupted-turn-history-test.mjs +371 -0
- package/scripts/lifecycle-api-test.mjs +137 -0
- package/scripts/max-output-recovery-test.mjs +86 -0
- package/scripts/mcp-grace-deferred-test.mjs +89 -13
- package/scripts/memory-core-input-test.mjs +10 -0
- package/scripts/memory-pg-recovery-test.mjs +59 -0
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +387 -6
- package/scripts/openai-ws-early-settle-test.mjs +40 -0
- package/scripts/parent-abort-link-test.mjs +24 -0
- package/scripts/process-lifecycle-test.mjs +447 -0
- package/scripts/provider-admission-scheduler-test.mjs +582 -0
- package/scripts/provider-contract-test.mjs +525 -0
- package/scripts/provider-toolcall-test.mjs +492 -11
- package/scripts/reactive-compact-persist-smoke.mjs +59 -0
- package/scripts/resource-admission-test.mjs +789 -0
- package/scripts/session-orphan-sweep-test.mjs +27 -1
- package/scripts/shell-jobs-windows-hide-test.mjs +1 -1
- package/scripts/steering-drain-buckets-test.mjs +18 -0
- package/scripts/toolcall-args-test.mjs +14 -6
- package/scripts/tui-transcript-perf-test.mjs +5 -7
- package/src/cli.mjs +15 -2
- package/src/lib/keychain-cjs.cjs +36 -23
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +27 -13
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +7 -8
- package/src/runtime/agent/orchestrator/agent-trace.mjs +33 -9
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +331 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +114 -308
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +136 -286
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +42 -5
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +554 -42
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +67 -18
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +84 -150
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -119
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +224 -0
- package/src/runtime/agent/orchestrator/providers/lib/env-utils.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +119 -0
- package/src/runtime/agent/orchestrator/providers/lib/grok-tool-schema.mjs +109 -0
- package/src/runtime/agent/orchestrator/providers/lib/openai-tool-args.mjs +70 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +54 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +28 -74
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +10 -80
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +136 -20
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +94 -33
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +201 -123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +37 -28
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +10 -10
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +36 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +50 -45
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +78 -12
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +44 -5
- package/src/runtime/agent/orchestrator/providers/registry.mjs +49 -8
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +229 -111
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +130 -63
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +99 -32
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +170 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +37 -224
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +11 -17
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +81 -42
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +8 -28
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +3 -58
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +30 -7
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +42 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +220 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +57 -16
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +20 -4
- package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +62 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +17 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +353 -108
- package/src/runtime/agent/orchestrator/stall-policy.mjs +2 -12
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +42 -4
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +74 -37
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +46 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-grep-chunks.mjs +173 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +117 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-insights.mjs +199 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-spawn-helpers.mjs +107 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +1 -40
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +19 -277
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +223 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +317 -250
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +176 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +108 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +93 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +12 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-spawn-retry.mjs +67 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +125 -85
- package/src/runtime/channels/backends/discord-gateway.mjs +1 -1
- package/src/runtime/channels/backends/discord.mjs +6 -6
- package/src/runtime/channels/lib/config.mjs +15 -2
- package/src/runtime/channels/lib/memory-client.mjs +20 -3
- package/src/runtime/channels/lib/owned-runtime.mjs +19 -12
- package/src/runtime/channels/lib/status-snapshot.mjs +9 -0
- package/src/runtime/channels/lib/tool-dispatch.mjs +9 -5
- package/src/runtime/channels/lib/worker-main.mjs +16 -5
- package/src/runtime/memory/index.mjs +46 -202
- package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -53
- package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +115 -0
- package/src/runtime/memory/lib/memory-port-advertiser.mjs +105 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +84 -10
- package/src/runtime/memory/lib/pg/process.mjs +91 -47
- package/src/runtime/memory/lib/pg/supervisor.mjs +50 -17
- package/src/runtime/memory/lib/query-handlers.mjs +2 -122
- package/src/runtime/memory/lib/query-maintenance-handlers.mjs +126 -0
- package/src/runtime/memory/lib/tool-call-handler.mjs +57 -0
- package/src/runtime/search/lib/http-fetch.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +28 -150
- package/src/runtime/shared/config.mjs +58 -13
- package/src/runtime/shared/llm/cost.mjs +14 -4
- package/src/runtime/shared/memory-snapshot.mjs +236 -0
- package/src/runtime/shared/process-lifecycle.mjs +436 -0
- package/src/runtime/shared/process-shutdown.mjs +33 -4
- package/src/runtime/shared/resource-admission.mjs +364 -0
- package/src/runtime/shared/staged-child-result.mjs +19 -0
- package/src/session-runtime/channel-config-api.mjs +7 -7
- package/src/session-runtime/config-lifecycle.mjs +20 -17
- package/src/session-runtime/context-status.mjs +38 -27
- package/src/session-runtime/cwd-plugins.mjs +9 -7
- package/src/session-runtime/env.mjs +1 -2
- package/src/session-runtime/hitch-profile.mjs +45 -0
- package/src/session-runtime/lifecycle-api.mjs +60 -13
- package/src/session-runtime/mcp-glue.mjs +6 -11
- package/src/session-runtime/provider-init-key.mjs +17 -0
- package/src/session-runtime/provider-request-tools.mjs +72 -0
- package/src/session-runtime/runtime-core.mjs +49 -105
- package/src/session-runtime/runtime-paths.mjs +20 -0
- package/src/session-runtime/runtime-tool-routing.mjs +55 -0
- package/src/session-runtime/session-turn-api.mjs +5 -4
- package/src/session-runtime/tool-catalog-data.mjs +51 -0
- package/src/session-runtime/tool-catalog.mjs +390 -104
- package/src/standalone/agent-tool/spawn-preset.mjs +73 -0
- package/src/standalone/agent-tool/worker-rows.mjs +93 -0
- package/src/standalone/agent-tool.mjs +29 -200
- package/src/standalone/channel-admin.mjs +29 -0
- package/src/standalone/channel-daemon-client.mjs +200 -38
- package/src/standalone/channel-daemon-transport.mjs +136 -9
- package/src/standalone/channel-worker.mjs +79 -12
- package/src/tui/App.jsx +11 -5
- package/src/tui/dist/index.mjs +275 -343
- package/src/tui/engine/session-api-ext.mjs +50 -6
- package/src/tui/engine/session-api.mjs +1 -1
- package/src/tui/engine/turn.mjs +10 -6
- package/src/tui/engine.mjs +13 -4
- package/src/tui/index.jsx +5 -1
- package/src/tui/lib/voice-setup.mjs +21 -5
- package/scripts/_devtools-stub.mjs +0 -1
- package/src/runtime/lib/keychain-cjs.cjs +0 -288
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { isOffloadedToolResultText } from './tool-result-offload.mjs';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import {
|
|
3
|
+
import { providerNativeToolPrefixCount } from '../../../../session-runtime/provider-request-tools.mjs';
|
|
4
4
|
import { contentImageDescriptors, contentToText } from '../providers/media-normalization.mjs';
|
|
5
|
+
export {
|
|
6
|
+
DEFAULT_COMPACTION_BUFFER_TOKENS,
|
|
7
|
+
DEFAULT_COMPACTION_BUFFER_RATIO,
|
|
8
|
+
DEFAULT_MAIN_COMPACTION_BUFFER_RATIO,
|
|
9
|
+
MAX_COMPACTION_BUFFER_RATIO,
|
|
10
|
+
DEFAULT_COMPACTION_KEEP_TOKENS,
|
|
11
|
+
positiveTokenInt,
|
|
12
|
+
normalizeCompactionBufferRatio,
|
|
13
|
+
resolveBufferRatioCandidate,
|
|
14
|
+
resolveCompactBufferRatio,
|
|
15
|
+
resolveMainCompactBufferRatio,
|
|
16
|
+
compactionBufferTokensForBoundary,
|
|
17
|
+
isPersistedZeroBufferTelemetry,
|
|
18
|
+
isLegacyDefaultBufferTelemetry,
|
|
19
|
+
compactBufferConfigForBoundary,
|
|
20
|
+
resolveCompactBufferTokens,
|
|
21
|
+
resolveMainCompactBufferTokens,
|
|
22
|
+
resolveCompactTriggerTokens,
|
|
23
|
+
resolveSessionCompactPolicy,
|
|
24
|
+
} from './context-compaction-policy.mjs';
|
|
5
25
|
|
|
6
26
|
// ---------------------------------------------------------------------------
|
|
7
27
|
// Conservative, Unicode-aware token estimator.
|
|
@@ -148,6 +168,14 @@ export function messageEstimateText(m) {
|
|
|
148
168
|
if (m.role === 'assistant' && Array.isArray(m.reasoningItems) && m.reasoningItems.length) {
|
|
149
169
|
text += `\n${nativeBlocksEstimateText(m.reasoningItems)}`;
|
|
150
170
|
}
|
|
171
|
+
// Provider-scoped replay metadata is never sent to a different provider,
|
|
172
|
+
// but Gemini replays signed thought parts on subsequent Gemini turns.
|
|
173
|
+
const geminiThoughtParts = m.role === 'assistant'
|
|
174
|
+
? m.providerMetadata?.gemini?.thoughtParts
|
|
175
|
+
: null;
|
|
176
|
+
if (Array.isArray(geminiThoughtParts) && geminiThoughtParts.length) {
|
|
177
|
+
text += `\n${nativeBlocksEstimateText(geminiThoughtParts)}`;
|
|
178
|
+
}
|
|
151
179
|
if (m.role === 'tool' && m.toolCallId) text += `\n${m.toolCallId}`;
|
|
152
180
|
return text;
|
|
153
181
|
}
|
|
@@ -212,6 +240,7 @@ function contextMessageFingerprint(message) {
|
|
|
212
240
|
thinkingBlocks: contextValueFingerprint(null),
|
|
213
241
|
assistantBlocks: contextValueFingerprint(null),
|
|
214
242
|
reasoningItems: contextValueFingerprint(null),
|
|
243
|
+
providerMetadata: contextValueFingerprint(null),
|
|
215
244
|
toolCallId: null,
|
|
216
245
|
};
|
|
217
246
|
}
|
|
@@ -222,6 +251,7 @@ function contextMessageFingerprint(message) {
|
|
|
222
251
|
thinkingBlocks: contextValueFingerprint(message.thinkingBlocks),
|
|
223
252
|
assistantBlocks: contextValueFingerprint(message.assistantBlocks),
|
|
224
253
|
reasoningItems: contextValueFingerprint(message.reasoningItems),
|
|
254
|
+
providerMetadata: contextValueFingerprint(message.providerMetadata),
|
|
225
255
|
toolCallId: message?.toolCallId || null,
|
|
226
256
|
};
|
|
227
257
|
}
|
|
@@ -237,6 +267,7 @@ function sameContextMessageFingerprint(a, b) {
|
|
|
237
267
|
&& sameContextValueFingerprint(a.thinkingBlocks, b.thinkingBlocks)
|
|
238
268
|
&& sameContextValueFingerprint(a.assistantBlocks, b.assistantBlocks)
|
|
239
269
|
&& sameContextValueFingerprint(a.reasoningItems, b.reasoningItems)
|
|
270
|
+
&& sameContextValueFingerprint(a.providerMetadata, b.providerMetadata)
|
|
240
271
|
&& a.toolCallId === b.toolCallId;
|
|
241
272
|
}
|
|
242
273
|
|
|
@@ -365,228 +396,6 @@ function contextSummaryResult(state, count) {
|
|
|
365
396
|
};
|
|
366
397
|
}
|
|
367
398
|
|
|
368
|
-
export const DEFAULT_COMPACTION_BUFFER_TOKENS = 0;
|
|
369
|
-
export const DEFAULT_COMPACTION_BUFFER_RATIO = 0.1;
|
|
370
|
-
// Main/user recall-fasttrack compacts at 95% by default. Lower triggers remain
|
|
371
|
-
// opt-in through the mainBuffer* configuration and MIXDOG_MAIN_COMPACT_BUFFER_*.
|
|
372
|
-
export const DEFAULT_MAIN_COMPACTION_BUFFER_RATIO = 0.05;
|
|
373
|
-
export const MAX_COMPACTION_BUFFER_RATIO = 0.25;
|
|
374
|
-
const MAX_BUFFER_INPUT_RATIO = 0.999_999;
|
|
375
|
-
export const DEFAULT_COMPACTION_KEEP_TOKENS = 8_000;
|
|
376
|
-
const LEGACY_DEFAULT_COMPACTION_BUFFER_RATIO = 0.1;
|
|
377
|
-
|
|
378
|
-
export function positiveTokenInt(value) {
|
|
379
|
-
const n = Number(value);
|
|
380
|
-
return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function envTokenInt(name) {
|
|
384
|
-
return positiveTokenInt(process.env[name]);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
export function normalizeCompactionBufferRatio(value, fallback = DEFAULT_COMPACTION_BUFFER_RATIO) {
|
|
388
|
-
const n = Number(value);
|
|
389
|
-
if (Number.isFinite(n) && n > 0) return n > 1 ? n / 100 : n;
|
|
390
|
-
return fallback;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Percent-named inputs (bufferPercent / bufferPct / *_BUFFER_PERCENT) carry a
|
|
394
|
-
// PERCENT: 1 means 1% (0.01). Ratio-named inputs (bufferRatio / bufferFraction)
|
|
395
|
-
// carry a fraction: 0.01 means 1%, and a legacy value > 1 is read as a percent.
|
|
396
|
-
export function resolveBufferRatioCandidate(percentInputs = [], ratioInputs = []) {
|
|
397
|
-
for (const raw of percentInputs) {
|
|
398
|
-
const n = Number(raw);
|
|
399
|
-
if (Number.isFinite(n) && n > 0) return Math.min(MAX_BUFFER_INPUT_RATIO, n / 100);
|
|
400
|
-
}
|
|
401
|
-
for (const raw of ratioInputs) {
|
|
402
|
-
const n = Number(raw);
|
|
403
|
-
if (Number.isFinite(n) && n > 0) return Math.min(MAX_BUFFER_INPUT_RATIO, n > 1 ? n / 100 : n);
|
|
404
|
-
}
|
|
405
|
-
return null;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export function resolveCompactBufferRatio(cfg = {}) {
|
|
409
|
-
const resolved = resolveBufferRatioCandidate(
|
|
410
|
-
[cfg.bufferPercent, cfg.bufferPct, process.env.MIXDOG_AGENT_COMPACT_BUFFER_PERCENT],
|
|
411
|
-
[cfg.bufferRatio, cfg.bufferFraction, process.env.MIXDOG_AGENT_COMPACT_BUFFER_RATIO],
|
|
412
|
-
);
|
|
413
|
-
return normalizeCompactionBufferRatio(resolved, DEFAULT_COMPACTION_BUFFER_RATIO);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Main/user settings are deliberately separate from the semantic-agent buffer:
|
|
417
|
-
// `buffer*` and MIXDOG_AGENT_COMPACT_BUFFER_* remain agent-only controls.
|
|
418
|
-
// Source precedence is config before environment; within each source the fixed
|
|
419
|
-
// unit precedence is tokens, percent, then ratio. Invalid/zero values are
|
|
420
|
-
// ignored, and percent/ratio inputs are capped below 100%/1.0.
|
|
421
|
-
function positiveTokenCandidate(values = []) {
|
|
422
|
-
for (const value of values) {
|
|
423
|
-
const tokens = positiveTokenInt(value);
|
|
424
|
-
if (tokens) return tokens;
|
|
425
|
-
}
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function resolveMainBufferSetting(cfg = {}) {
|
|
430
|
-
const configTokens = positiveTokenCandidate([cfg.mainBufferTokens, cfg.mainBuffer]);
|
|
431
|
-
if (configTokens) return { tokens: configTokens };
|
|
432
|
-
const configRatio = resolveBufferRatioCandidate(
|
|
433
|
-
[cfg.mainBufferPercent, cfg.mainBufferPct],
|
|
434
|
-
[cfg.mainBufferRatio, cfg.mainBufferFraction],
|
|
435
|
-
);
|
|
436
|
-
if (configRatio !== null) return { ratio: configRatio };
|
|
437
|
-
|
|
438
|
-
const envTokens = positiveTokenCandidate([process.env.MIXDOG_MAIN_COMPACT_BUFFER_TOKENS]);
|
|
439
|
-
if (envTokens) return { tokens: envTokens };
|
|
440
|
-
const envRatio = resolveBufferRatioCandidate(
|
|
441
|
-
[process.env.MIXDOG_MAIN_COMPACT_BUFFER_PERCENT],
|
|
442
|
-
[process.env.MIXDOG_MAIN_COMPACT_BUFFER_RATIO],
|
|
443
|
-
);
|
|
444
|
-
return envRatio === null ? null : { ratio: envRatio };
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
export function resolveMainCompactBufferRatio(cfg = {}) {
|
|
448
|
-
const setting = resolveMainBufferSetting(cfg);
|
|
449
|
-
return setting?.ratio ?? DEFAULT_MAIN_COMPACTION_BUFFER_RATIO;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
export function compactionBufferTokensForBoundary(boundaryTokens, opts = {}) {
|
|
453
|
-
const boundary = Math.max(0, Math.floor(Number(boundaryTokens) || 0));
|
|
454
|
-
const explicit = Math.max(0, Math.floor(Number(opts.explicitTokens) || 0));
|
|
455
|
-
if (!boundary) return explicit;
|
|
456
|
-
const maxRatio = normalizeCompactionBufferRatio(opts.maxRatio, MAX_COMPACTION_BUFFER_RATIO);
|
|
457
|
-
const cap = Math.max(0, Math.floor(boundary * maxRatio));
|
|
458
|
-
if (explicit > 0) return Math.max(0, Math.min(explicit, cap));
|
|
459
|
-
const ratio = normalizeCompactionBufferRatio(opts.ratio, DEFAULT_COMPACTION_BUFFER_RATIO);
|
|
460
|
-
return Math.max(0, Math.min(Math.floor(boundary * ratio), cap));
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export function isPersistedZeroBufferTelemetry(cfg = {}, boundaryTokens = 0) {
|
|
464
|
-
const boundary = positiveTokenInt(boundaryTokens);
|
|
465
|
-
if (!boundary) return false;
|
|
466
|
-
if (envTokenInt('MIXDOG_AGENT_COMPACT_BUFFER_TOKENS')) return false;
|
|
467
|
-
for (const envName of ['MIXDOG_AGENT_COMPACT_BUFFER_PERCENT', 'MIXDOG_AGENT_COMPACT_BUFFER_RATIO']) {
|
|
468
|
-
const n = Number(process.env[envName]);
|
|
469
|
-
if (Number.isFinite(n) && n > 0) return false;
|
|
470
|
-
}
|
|
471
|
-
for (const key of ['bufferPercent', 'bufferPct', 'bufferFraction']) {
|
|
472
|
-
const n = Number(cfg?.[key]);
|
|
473
|
-
if (Number.isFinite(n) && n > 0) return false;
|
|
474
|
-
}
|
|
475
|
-
const ratio = Number(cfg?.bufferRatio);
|
|
476
|
-
if (Number.isFinite(ratio) && ratio > 0) return false;
|
|
477
|
-
const explicitTokens = Number(cfg?.bufferTokens ?? cfg?.buffer);
|
|
478
|
-
if (!Number.isFinite(explicitTokens) || explicitTokens !== 0) return false;
|
|
479
|
-
return true;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
export function isLegacyDefaultBufferTelemetry(cfg = {}, boundaryTokens = 0) {
|
|
483
|
-
const boundary = positiveTokenInt(boundaryTokens);
|
|
484
|
-
if (!boundary) return false;
|
|
485
|
-
if (envTokenInt('MIXDOG_AGENT_COMPACT_BUFFER_TOKENS')) return false;
|
|
486
|
-
for (const envName of ['MIXDOG_AGENT_COMPACT_BUFFER_PERCENT', 'MIXDOG_AGENT_COMPACT_BUFFER_RATIO']) {
|
|
487
|
-
const n = Number(process.env[envName]);
|
|
488
|
-
if (Number.isFinite(n) && n > 0) return false;
|
|
489
|
-
}
|
|
490
|
-
// Percent/fraction-named fields are operator config. Legacy/default
|
|
491
|
-
// telemetry persisted bufferTokens + bufferRatio after a check/compact pass.
|
|
492
|
-
for (const key of ['bufferPercent', 'bufferPct', 'bufferFraction']) {
|
|
493
|
-
const n = Number(cfg?.[key]);
|
|
494
|
-
if (Number.isFinite(n) && n > 0) return false;
|
|
495
|
-
}
|
|
496
|
-
const explicitTokens = positiveTokenInt(cfg?.bufferTokens ?? cfg?.buffer);
|
|
497
|
-
const ratio = Number(cfg?.bufferRatio);
|
|
498
|
-
if (!explicitTokens || !Number.isFinite(ratio) || Math.abs(ratio - LEGACY_DEFAULT_COMPACTION_BUFFER_RATIO) > 1e-9) return false;
|
|
499
|
-
const expectedTokens = Math.floor(boundary * LEGACY_DEFAULT_COMPACTION_BUFFER_RATIO);
|
|
500
|
-
const cfgBoundary = positiveTokenInt(cfg?.boundaryTokens);
|
|
501
|
-
const cfgTrigger = positiveTokenInt(cfg?.triggerTokens);
|
|
502
|
-
return explicitTokens === expectedTokens
|
|
503
|
-
|| (cfgBoundary === boundary && cfgTrigger > 0 && explicitTokens === Math.max(0, boundary - cfgTrigger));
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
export function compactBufferConfigForBoundary(cfg = {}, boundaryTokens = 0) {
|
|
507
|
-
const base = cfg || {};
|
|
508
|
-
if (!isLegacyDefaultBufferTelemetry(base, boundaryTokens)
|
|
509
|
-
&& !isPersistedZeroBufferTelemetry(base, boundaryTokens)) {
|
|
510
|
-
return base;
|
|
511
|
-
}
|
|
512
|
-
return {
|
|
513
|
-
...base,
|
|
514
|
-
bufferTokens: null,
|
|
515
|
-
buffer: null,
|
|
516
|
-
bufferRatio: null,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
export function resolveCompactBufferTokens(boundaryTokens, cfg = {}, opts = {}) {
|
|
521
|
-
const boundary = positiveTokenInt(boundaryTokens);
|
|
522
|
-
const effectiveCfg = compactBufferConfigForBoundary(cfg, boundary);
|
|
523
|
-
const configured = positiveTokenInt(effectiveCfg.bufferTokens ?? effectiveCfg.buffer)
|
|
524
|
-
|| envTokenInt('MIXDOG_AGENT_COMPACT_BUFFER_TOKENS')
|
|
525
|
-
|| 0;
|
|
526
|
-
if (!boundary) return configured || positiveTokenInt(opts.defaultTokens) || DEFAULT_COMPACTION_BUFFER_TOKENS;
|
|
527
|
-
return compactionBufferTokensForBoundary(boundary, {
|
|
528
|
-
explicitTokens: configured,
|
|
529
|
-
ratio: resolveCompactBufferRatio(effectiveCfg),
|
|
530
|
-
maxRatio: opts.maxRatio ?? MAX_COMPACTION_BUFFER_RATIO,
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
export function resolveMainCompactBufferTokens(boundaryTokens, cfg = {}, opts = {}) {
|
|
535
|
-
const boundary = positiveTokenInt(boundaryTokens);
|
|
536
|
-
const setting = resolveMainBufferSetting(cfg);
|
|
537
|
-
const configured = setting?.tokens || 0;
|
|
538
|
-
if (!boundary) return configured || positiveTokenInt(opts.defaultTokens) || DEFAULT_COMPACTION_BUFFER_TOKENS;
|
|
539
|
-
return compactionBufferTokensForBoundary(boundary, {
|
|
540
|
-
explicitTokens: configured,
|
|
541
|
-
ratio: setting?.ratio ?? DEFAULT_MAIN_COMPACTION_BUFFER_RATIO,
|
|
542
|
-
maxRatio: opts.maxRatio ?? MAX_COMPACTION_BUFFER_RATIO,
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
export function resolveCompactTriggerTokens(sessionOrConfig = {}, boundaryTokens = 0) {
|
|
547
|
-
return resolveSessionCompactPolicy(sessionOrConfig, boundaryTokens).triggerTokens;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// Single source of truth for per-session compaction policy math. Manager
|
|
551
|
-
// (compactTriggerForSession), the turn loop (resolveWorkerCompactPolicy), and
|
|
552
|
-
// the /context gauge all derive their trigger/buffer from here so the numbers
|
|
553
|
-
// never diverge. Rules:
|
|
554
|
-
// - a truly-explicit sub-boundary auto-compact limit always wins
|
|
555
|
-
// (trigger = limit) for every session type;
|
|
556
|
-
// - agent-owned semantic sessions otherwise keep the default early-trigger
|
|
557
|
-
// buffer (config-driven, default 10% -> compact at 90% of the boundary);
|
|
558
|
-
// - main/user recall-fasttrack sessions use their independently configurable
|
|
559
|
-
// buffer (default 25% -> compact at 75% of the effective boundary).
|
|
560
|
-
// Returns the sanitized explicit limit (null when absent/legacy full-window)
|
|
561
|
-
// plus triggerTokens / bufferTokens / bufferRatio for the given boundary.
|
|
562
|
-
export function resolveSessionCompactPolicy(sessionOrConfig = {}, boundaryTokens = 0) {
|
|
563
|
-
const cfg = sessionOrConfig?.compaction || sessionOrConfig || {};
|
|
564
|
-
const boundary = positiveTokenInt(boundaryTokens);
|
|
565
|
-
if (!boundary) {
|
|
566
|
-
return {
|
|
567
|
-
autoCompactTokenLimit: null,
|
|
568
|
-
triggerTokens: null,
|
|
569
|
-
bufferTokens: 0,
|
|
570
|
-
bufferRatio: isAgentOwner(sessionOrConfig)
|
|
571
|
-
? resolveCompactBufferRatio(cfg)
|
|
572
|
-
: resolveMainCompactBufferRatio(cfg),
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
const rawLimit = positiveTokenInt(sessionOrConfig?.autoCompactTokenLimit ?? cfg?.autoCompactTokenLimit);
|
|
576
|
-
const explicitLimit = rawLimit && rawLimit < boundary ? rawLimit : null;
|
|
577
|
-
let triggerTokens;
|
|
578
|
-
if (explicitLimit) {
|
|
579
|
-
triggerTokens = explicitLimit;
|
|
580
|
-
} else if (isAgentOwner(sessionOrConfig)) {
|
|
581
|
-
triggerTokens = Math.max(1, boundary - resolveCompactBufferTokens(boundary, cfg));
|
|
582
|
-
} else {
|
|
583
|
-
triggerTokens = Math.max(1, boundary - resolveMainCompactBufferTokens(boundary, cfg));
|
|
584
|
-
}
|
|
585
|
-
const bufferTokens = Math.max(0, boundary - triggerTokens);
|
|
586
|
-
const bufferRatio = bufferTokens / boundary;
|
|
587
|
-
return { autoCompactTokenLimit: explicitLimit, triggerTokens, bufferTokens, bufferRatio };
|
|
588
|
-
}
|
|
589
|
-
|
|
590
399
|
function stripSystemReminder(text) {
|
|
591
400
|
return String(text || '')
|
|
592
401
|
.replace(/^\s*<system-reminder>\s*/i, '')
|
|
@@ -680,8 +489,12 @@ const requestReserveTokenMemo = new WeakMap();
|
|
|
680
489
|
|
|
681
490
|
function serializeToolSchemas(tools) {
|
|
682
491
|
const list = Array.isArray(tools) ? tools : [];
|
|
492
|
+
const nativePrefixCount = providerNativeToolPrefixCount(list);
|
|
683
493
|
try {
|
|
684
|
-
return JSON.stringify(list.map(tool => {
|
|
494
|
+
return JSON.stringify(list.map((tool, index) => {
|
|
495
|
+
if (index < nativePrefixCount) {
|
|
496
|
+
return tool;
|
|
497
|
+
}
|
|
685
498
|
const wireTool = {
|
|
686
499
|
name: tool?.name,
|
|
687
500
|
description: tool?.description,
|
|
@@ -28,16 +28,6 @@ import { providerInputExcludesCache } from '../../providers/registry.mjs';
|
|
|
28
28
|
// the recall-fasttrack injection cap uses (loop.mjs recallTokenCap). One
|
|
29
29
|
// number governs every "share of model context" budget.
|
|
30
30
|
|
|
31
|
-
function resolveSemanticCompactSetting(sessionRef, cfg = {}) {
|
|
32
|
-
// Types are hard-locked (agent -> semantic, main/user -> recall-fasttrack).
|
|
33
|
-
// Semantic must always be available as a compact path or agent sessions
|
|
34
|
-
// would have none (loop.mjs throws when no type is available). Env/cfg
|
|
35
|
-
// off-switches no longer apply.
|
|
36
|
-
void sessionRef;
|
|
37
|
-
void cfg;
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
31
|
function resolveCompactTypeSetting(sessionRef, cfg = {}) {
|
|
42
32
|
// Agent-owned sessions are ALWAYS semantic. recall-fasttrack rebuilds
|
|
43
33
|
// context from Memory recall, which is scoped to the user's main-session
|
|
@@ -171,7 +161,7 @@ export function resolveWorkerCompactPolicy(sessionRef, tools) {
|
|
|
171
161
|
? Number(sessionRef.effectiveContextWindowPercent ?? cfg.effectiveContextWindowPercent)
|
|
172
162
|
: null,
|
|
173
163
|
autoCompactTokenLimit: explicitAutoCompactTokenLimit,
|
|
174
|
-
semantic: compactTypeIsSemantic(compactType)
|
|
164
|
+
semantic: compactTypeIsSemantic(compactType),
|
|
175
165
|
recallFastTrack: compactTypeIsRecallFastTrack(compactType),
|
|
176
166
|
semanticTimeoutMs: positiveTokenInt(cfg.timeoutMs) || envTokenInt('MIXDOG_AGENT_COMPACT_TIMEOUT_MS') || 30_000,
|
|
177
167
|
tailTurns: positiveTokenInt(cfg.tailTurns) || envTokenInt('MIXDOG_AGENT_COMPACT_TAIL_TURNS') || 2,
|
|
@@ -191,15 +181,15 @@ function compactPressureTokens(messageTokensEst, policy) {
|
|
|
191
181
|
|
|
192
182
|
function providerPressureTokens(sessionRef, usage) {
|
|
193
183
|
if (!usage || typeof usage !== 'object') return 0;
|
|
194
|
-
const input = Math.max(0, Number(usage.inputTokens) || 0);
|
|
195
|
-
const cachedRead = Math.max(0, Number(usage.cachedTokens) || 0);
|
|
196
|
-
const cacheWrite = Math.max(0, Number(usage.cacheWriteTokens) || 0);
|
|
197
|
-
const explicitPrompt = Math.max(0, Number(usage.promptTokens) || 0);
|
|
184
|
+
const input = Math.max(0, Number(usage.mainInputTokens ?? usage.inputTokens) || 0);
|
|
185
|
+
const cachedRead = Math.max(0, Number(usage.mainCachedTokens ?? usage.cachedTokens) || 0);
|
|
186
|
+
const cacheWrite = Math.max(0, Number(usage.mainCacheWriteTokens ?? usage.cacheWriteTokens) || 0);
|
|
187
|
+
const explicitPrompt = Math.max(0, Number(usage.mainPromptTokens ?? usage.promptTokens) || 0);
|
|
198
188
|
const normalizedPrompt = providerInputExcludesCache(sessionRef?.provider)
|
|
199
189
|
? input + cachedRead + cacheWrite
|
|
200
190
|
: input;
|
|
201
191
|
const prompt = Math.max(explicitPrompt, normalizedPrompt);
|
|
202
|
-
const output = Math.max(0, Number(usage.outputTokens) || 0);
|
|
192
|
+
const output = Math.max(0, Number(usage.mainOutputTokens ?? usage.outputTokens) || 0);
|
|
203
193
|
return Math.max(0, Math.round(prompt + output));
|
|
204
194
|
}
|
|
205
195
|
|
|
@@ -213,10 +203,14 @@ export function recordProviderContextBaseline(sessionRef, messages, usage, {
|
|
|
213
203
|
sendTools = sessionRef?.tools,
|
|
214
204
|
} = {}) {
|
|
215
205
|
if (!sessionRef || !Array.isArray(messages)) return false;
|
|
206
|
+
if (usage?.mainUsageAvailable === false) {
|
|
207
|
+
invalidateProviderContextBaseline(sessionRef);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
216
210
|
const tokens = providerPressureTokens(sessionRef, usage);
|
|
217
211
|
if (!tokens) return false;
|
|
218
212
|
sessionRef.contextPressureBaselineTokens = tokens;
|
|
219
|
-
sessionRef.contextPressureBaselineOutputTokens = Math.max(0, Math.round(Number(usage?.outputTokens) || 0));
|
|
213
|
+
sessionRef.contextPressureBaselineOutputTokens = Math.max(0, Math.round(Number(usage?.mainOutputTokens ?? usage?.outputTokens) || 0));
|
|
220
214
|
sessionRef.contextPressureBaselineMessageCount = messages.length;
|
|
221
215
|
sessionRef.contextPressureBaselinePrefixSignature = contextMessagesSignature(messages);
|
|
222
216
|
sessionRef.contextPressureBaselineProvider = sessionRef.provider || null;
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
buildCurrentTimeBlock,
|
|
23
23
|
buildSessionStartBlock,
|
|
24
24
|
hasUserConversationMessage,
|
|
25
|
-
isInternalRuntimeNotificationText,
|
|
26
25
|
} from './prompt-utils.mjs';
|
|
27
26
|
import { _mergePendingMessageEntries, drainPendingMessages } from './pending-messages.mjs';
|
|
28
27
|
import { persistIterationMetrics, applyAskTerminalUsageTotals } from './usage-metrics.mjs';
|
|
@@ -38,11 +37,16 @@ import {
|
|
|
38
37
|
_touchRuntime,
|
|
39
38
|
_unlinkParentAbortListener,
|
|
40
39
|
_getRuntimeEntry,
|
|
40
|
+
_evictTerminalSessionRuntime,
|
|
41
41
|
} from './runtime-liveness.mjs';
|
|
42
42
|
import { SessionClosedError } from './session-errors.mjs';
|
|
43
43
|
import { acquireSessionLock } from './session-lock.mjs';
|
|
44
44
|
import { _tryBridgeExplicitPrefetch } from './prefetch-bridge.mjs';
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
filterModelVisibleSessionMessages,
|
|
47
|
+
persistCompactedOutgoingAfterAskFailure,
|
|
48
|
+
} from './message-sanitize.mjs';
|
|
49
|
+
import { createTurnInterruptionTracker } from './turn-interruption.mjs';
|
|
46
50
|
import { _getAgentLoop } from './runtime-loaders.mjs';
|
|
47
51
|
import { getAgentRuntimeSync } from './agent-runtime-singleton.mjs';
|
|
48
52
|
import { recordProviderContextBaseline } from '../loop/compact-policy.mjs';
|
|
@@ -188,6 +192,8 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
188
192
|
let activeSession = preSession;
|
|
189
193
|
let cancelledUserTurnContent = '';
|
|
190
194
|
let _turnOutgoing = null;
|
|
195
|
+
const _turnInterruption = createTurnInterruptionTracker();
|
|
196
|
+
const _sessionStartMetaInjectedBeforeTurn = preSession.sessionStartMetaInjected === true;
|
|
191
197
|
try {
|
|
192
198
|
const session = activeSession;
|
|
193
199
|
const provider = getProvider(session.provider);
|
|
@@ -244,7 +250,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
244
250
|
if (explicitPrefetchResult) {
|
|
245
251
|
_contextBlock += `# Prefetch\n${_capCtx(explicitPrefetchResult)}\n\n`;
|
|
246
252
|
}
|
|
247
|
-
const historyMessages =
|
|
253
|
+
const historyMessages = filterModelVisibleSessionMessages(session.messages);
|
|
248
254
|
const beforeCount = historyMessages.length + 1;
|
|
249
255
|
const promptTextForMetrics = promptContentText(prompt);
|
|
250
256
|
// Soft warning only; real size management (compaction primary,
|
|
@@ -291,7 +297,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
291
297
|
// below overwrites this provisional transcript with the fully mutated
|
|
292
298
|
// outgoing history and appends the assistant result, so no duplicate
|
|
293
299
|
// user turn is introduced.
|
|
294
|
-
session.messages =
|
|
300
|
+
session.messages = filterModelVisibleSessionMessages(outgoing);
|
|
295
301
|
session.liveTurnMessages = outgoing;
|
|
296
302
|
saveSessionAsync(session, { expectedGeneration: askGeneration }).catch((err) => {
|
|
297
303
|
try { process.stderr.write(`[session] preflight user-turn save failed: ${err?.message || err}\n`); } catch {}
|
|
@@ -324,6 +330,27 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
324
330
|
});
|
|
325
331
|
} catch { /* trace must never break the ask path */ }
|
|
326
332
|
const agentLoop = await _getAgentLoop();
|
|
333
|
+
const _trackTextDelta = (chunk) => {
|
|
334
|
+
_turnInterruption.recordTextDelta(chunk);
|
|
335
|
+
if (typeof askOpts?.onTextDelta === 'function') askOpts.onTextDelta(chunk);
|
|
336
|
+
};
|
|
337
|
+
const _trackReasoningDelta = (chunk) => {
|
|
338
|
+
_turnInterruption.recordReasoningDelta(chunk);
|
|
339
|
+
if (typeof askOpts?.onReasoningDelta === 'function') askOpts.onReasoningDelta(chunk);
|
|
340
|
+
};
|
|
341
|
+
const _trackAssistantText = (text) => {
|
|
342
|
+
_turnInterruption.recordAssistantText(text);
|
|
343
|
+
if (typeof askOpts?.onAssistantText === 'function') askOpts.onAssistantText(text);
|
|
344
|
+
};
|
|
345
|
+
const _trackedOnToolCall = async (iteration, calls) => {
|
|
346
|
+
_turnInterruption.recordToolCalls(calls);
|
|
347
|
+
if (typeof onToolCall === 'function') return await onToolCall(iteration, calls);
|
|
348
|
+
return undefined;
|
|
349
|
+
};
|
|
350
|
+
const _trackToolResult = (message) => {
|
|
351
|
+
_turnInterruption.recordToolResult(message);
|
|
352
|
+
if (typeof askOpts?.onToolResult === 'function') askOpts.onToolResult(message);
|
|
353
|
+
};
|
|
327
354
|
const priorToolApprovalHook = session.toolApprovalHook;
|
|
328
355
|
if (typeof askOpts?.onToolApproval === 'function') {
|
|
329
356
|
session.toolApprovalHook = askOpts.onToolApproval;
|
|
@@ -331,13 +358,17 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
331
358
|
let result;
|
|
332
359
|
try {
|
|
333
360
|
result = await _api_call_with_interrupt(sessionId, (signal) =>
|
|
334
|
-
agentLoop(provider, outgoing, session.model, session.tools,
|
|
361
|
+
agentLoop(provider, outgoing, session.model, session.tools, _trackedOnToolCall, effectiveCwd, {
|
|
335
362
|
effort: session.effort || null,
|
|
336
363
|
fast: session.fast === true,
|
|
337
364
|
sessionId,
|
|
338
|
-
onTextDelta:
|
|
339
|
-
onReasoningDelta:
|
|
340
|
-
onAssistantText:
|
|
365
|
+
onTextDelta: _trackTextDelta,
|
|
366
|
+
onReasoningDelta: _trackReasoningDelta,
|
|
367
|
+
onAssistantText: _trackAssistantText,
|
|
368
|
+
onAssistantMessageCommitted: () => _turnInterruption.markAssistantMessageCommitted(),
|
|
369
|
+
onAssistantToolCallObserved: (call, detail) => _turnInterruption.recordToolCalls([call], detail),
|
|
370
|
+
onProviderSendStarted: () => _turnInterruption.markProviderSendStarted(),
|
|
371
|
+
onToolPhaseStarted: () => _turnInterruption.markToolPhaseStarted(),
|
|
341
372
|
onUsageDelta: (d) => {
|
|
342
373
|
persistIterationMetrics(d).catch(() => {});
|
|
343
374
|
// provider_send usage arrives before agentLoop appends
|
|
@@ -348,16 +379,17 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
348
379
|
// tool results/steering.
|
|
349
380
|
if (d?.source === 'provider_send') {
|
|
350
381
|
recordProviderContextBaseline(session, outgoing, {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
382
|
+
mainUsageAvailable: d.contextUsageAvailable,
|
|
383
|
+
inputTokens: d.contextInputTokens ?? d.deltaInput,
|
|
384
|
+
outputTokens: d.contextOutputTokens ?? d.deltaOutput,
|
|
385
|
+
promptTokens: d.contextPromptTokens ?? d.deltaPrompt,
|
|
386
|
+
cachedTokens: d.contextCachedReadTokens ?? d.deltaCachedRead,
|
|
387
|
+
cacheWriteTokens: d.contextCacheWriteTokens ?? d.deltaCacheWrite,
|
|
356
388
|
}, { boundary: 'request', sendTools: d.sendTools });
|
|
357
389
|
}
|
|
358
390
|
try { askOpts?.onUsageDelta?.(d); } catch {}
|
|
359
391
|
},
|
|
360
|
-
onToolResult:
|
|
392
|
+
onToolResult: _trackToolResult,
|
|
361
393
|
onToolApproval: typeof askOpts?.onToolApproval === 'function' ? askOpts.onToolApproval : undefined,
|
|
362
394
|
onCompactEvent: typeof askOpts?.onCompactEvent === 'function' ? askOpts.onCompactEvent : undefined,
|
|
363
395
|
// Claude Code parity: mid-chain queued prompt/notification
|
|
@@ -429,7 +461,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
429
461
|
// Update and save. outgoing is mutated in place by agentLoop
|
|
430
462
|
// (compaction + safety trim), so its length reflects post-loop state.
|
|
431
463
|
const messagesDropped = Math.max(0, beforeCount - outgoing.length);
|
|
432
|
-
session.messages =
|
|
464
|
+
session.messages = filterModelVisibleSessionMessages(outgoing);
|
|
433
465
|
// Turn committed into session.messages; drop the live-turn alias so
|
|
434
466
|
// contextStatus() reverts to the authoritative committed transcript.
|
|
435
467
|
session.liveTurnMessages = null;
|
|
@@ -451,6 +483,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
451
483
|
...(typeof result.reasoningContent === 'string' && result.reasoningContent
|
|
452
484
|
? { reasoningContent: result.reasoningContent }
|
|
453
485
|
: {}),
|
|
486
|
+
...(result.providerMetadata && typeof result.providerMetadata === 'object'
|
|
487
|
+
? { providerMetadata: result.providerMetadata }
|
|
488
|
+
: {}),
|
|
454
489
|
});
|
|
455
490
|
} else {
|
|
456
491
|
// Empty terminal turn: still persist a forensic record so
|
|
@@ -576,12 +611,13 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
576
611
|
});
|
|
577
612
|
recordStandaloneStatusTelemetry(session, result, Date.now() - _askStartedAt);
|
|
578
613
|
}
|
|
579
|
-
// Persist opaque providerState for
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
614
|
+
// Persist opaque providerState for stateful providers. The update
|
|
615
|
+
// bit distinguishes an adapter that emitted no state update from
|
|
616
|
+
// an explicit clear caused by compaction/provider reset.
|
|
617
|
+
if (result.providerStateUpdated === true
|
|
618
|
+
&& (result.providerState === undefined || result.providerState === null)) {
|
|
619
|
+
delete session.providerState;
|
|
620
|
+
} else if (result.providerStateUpdated === true || result.providerState !== undefined) {
|
|
585
621
|
session.providerState = result.providerState;
|
|
586
622
|
}
|
|
587
623
|
const terminalResultPreview = {
|
|
@@ -634,28 +670,26 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
634
670
|
const currentRuntime = _getRuntimeEntry(sessionId);
|
|
635
671
|
if (!currentRuntime?.closed) {
|
|
636
672
|
if (activeSession) {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|| nextMessages.some((message, index) => message !== originalMessages[index]);
|
|
651
|
-
if (messagesChanged) {
|
|
652
|
-
activeSession.messages = nextMessages;
|
|
653
|
-
activeSession.updatedAt = Date.now();
|
|
654
|
-
activeSession.lastUsedAt = Date.now();
|
|
655
|
-
try {
|
|
656
|
-
await saveSessionAsync(activeSession, { expectedGeneration: askGeneration });
|
|
657
|
-
} catch { /* cancellation cleanup is best-effort */ }
|
|
673
|
+
const finalized = _turnInterruption.finalize({
|
|
674
|
+
turnOutgoing: _turnOutgoing || activeSession.messages,
|
|
675
|
+
currentUserContent: cancelledUserTurnContent,
|
|
676
|
+
abortReason: err.reason,
|
|
677
|
+
});
|
|
678
|
+
activeSession.messages = finalized.messages;
|
|
679
|
+
if (!finalized.responsePreserved) {
|
|
680
|
+
activeSession.sessionStartMetaInjected = _sessionStartMetaInjectedBeforeTurn;
|
|
681
|
+
} else {
|
|
682
|
+
// The opaque provider continuation now points at a
|
|
683
|
+
// request that ended mid-turn. Force full transcript
|
|
684
|
+
// replay on the next send instead of reusing it.
|
|
685
|
+
activeSession.providerState = undefined;
|
|
658
686
|
}
|
|
687
|
+
activeSession.updatedAt = Date.now();
|
|
688
|
+
activeSession.lastUsedAt = Date.now();
|
|
689
|
+
try {
|
|
690
|
+
await saveSessionAsync(activeSession, { expectedGeneration: askGeneration });
|
|
691
|
+
} catch { /* cancellation cleanup is best-effort */ }
|
|
692
|
+
if (currentRuntime) currentRuntime.session = activeSession;
|
|
659
693
|
}
|
|
660
694
|
markSessionCancelled(sessionId);
|
|
661
695
|
}
|
|
@@ -715,6 +749,11 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
715
749
|
// Detach the live session reference; ask is over.
|
|
716
750
|
entry.session = null;
|
|
717
751
|
}
|
|
752
|
+
// Final-stage runtime diagnostics are useful only while the turn is
|
|
753
|
+
// unwinding. Once its controller is detached, retaining the full entry
|
|
754
|
+
// (and any accidental references hanging from it) for the host lifetime
|
|
755
|
+
// turns one-shot agent traffic into an unbounded manager Map.
|
|
756
|
+
_evictTerminalSessionRuntime(sessionId);
|
|
718
757
|
unlock();
|
|
719
758
|
}
|
|
720
759
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
effectiveBudget as compactEffectiveBudget,
|
|
13
13
|
compactTypeIsRecallFastTrack,
|
|
14
14
|
compactTypeIsSemantic,
|
|
15
|
+
normalizeCompactType,
|
|
15
16
|
CONTEXT_SHARE_RATIO,
|
|
16
17
|
RECALL_TOKEN_CAP_FLOOR_TOKENS,
|
|
17
18
|
} from '../compact.mjs';
|
|
@@ -271,7 +272,13 @@ export async function runSessionCompaction(session, opts = {}) {
|
|
|
271
272
|
: boundary;
|
|
272
273
|
const pressureTokens = estimateTranscriptContextUsage(messages, session.tools || []);
|
|
273
274
|
const beforeTokens = pressureTokens;
|
|
274
|
-
|
|
275
|
+
// Manual /compact may explicitly request the original semantic path:
|
|
276
|
+
// summarize THIS session's transcript directly without hydrating/searching
|
|
277
|
+
// Memory first. Automatic and auto-clear compaction keep their configured
|
|
278
|
+
// recall-fasttrack behavior unless their caller explicitly changes it.
|
|
279
|
+
const compactType = mode === 'manual' && opts.compactType != null
|
|
280
|
+
? normalizeCompactType(opts.compactType)
|
|
281
|
+
: compactTypeForSession(session);
|
|
275
282
|
if (!force && pressureTokens < triggerTokens) return {
|
|
276
283
|
changed: false,
|
|
277
284
|
reason: 'below threshold',
|
|
@@ -358,6 +365,7 @@ export async function runSessionCompaction(session, opts = {}) {
|
|
|
358
365
|
tailTurns: positiveContextWindow(session.compaction?.tailTurns) || 2,
|
|
359
366
|
keepTokens: positiveContextWindow(session.compaction?.keepTokens ?? session.compaction?.keep?.tokens),
|
|
360
367
|
preserveRecentTokens: positiveContextWindow(session.compaction?.preserveRecentTokens),
|
|
368
|
+
filterOldHistoryForIngest: opts.filterOldHistoryForIngest === true,
|
|
361
369
|
force: true,
|
|
362
370
|
},
|
|
363
371
|
);
|
|
@@ -401,6 +409,7 @@ export async function runSessionCompaction(session, opts = {}) {
|
|
|
401
409
|
tailTurns: positiveContextWindow(session.compaction?.tailTurns) || 2,
|
|
402
410
|
keepTokens: positiveContextWindow(session.compaction?.keepTokens ?? session.compaction?.keep?.tokens),
|
|
403
411
|
preserveRecentTokens: positiveContextWindow(session.compaction?.preserveRecentTokens),
|
|
412
|
+
filterOldHistoryForIngest: opts.filterOldHistoryForIngest === true,
|
|
404
413
|
force: true,
|
|
405
414
|
},
|
|
406
415
|
);
|