mixdog 0.9.153 → 0.9.154
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 -1
- package/src/rules/shared/60-verification.md +2 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/compact/handoff.mjs +0 -22
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +9 -22
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +28 -269
- package/src/runtime/agent/orchestrator/session/compact.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +80 -18
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +5 -66
- package/src/runtime/computer-bridge/client.mjs +85 -32
- package/src/session-runtime/lifecycle-api.mjs +10 -1
- package/src/session-runtime/runtime-core.mjs +6 -5
- package/src/standalone/daemon.mjs +17 -0
- package/src/standalone/session-runtime-worker.mjs +4 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +0 -135
- package/scripts/native-binary-arch.test.mjs +0 -111
- package/scripts/patch-replay.test.mjs +0 -94
- package/src/headless-exec.test.mjs +0 -539
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.test.mjs +0 -378
- package/src/runtime/agent/orchestrator/agent-trace-timing.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/cache-break-trace.test.mjs +0 -115
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +0 -139
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +0 -66
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +0 -80
- package/src/runtime/agent/orchestrator/providers/admission-scheduler-metrics.test.mjs +0 -47
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +0 -90
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +0 -215
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +0 -165
- package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +0 -201
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +0 -306
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -288
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
- package/src/runtime/agent/orchestrator/session/agent-loop.test.mjs +0 -222
- package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.test.mjs +0 -83
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -288
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -192
- package/src/runtime/agent/orchestrator/session/context-usage-restart.test.mjs +0 -72
- package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -189
- package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -264
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.test.mjs +0 -28
- package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
- package/src/runtime/agent/orchestrator/session/manager/ask-session.test.mjs +0 -46
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +0 -325
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +0 -132
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -593
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +0 -117
- package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +0 -87
- package/src/runtime/agent/orchestrator/session/store/serialize.test.mjs +0 -60
- package/src/runtime/agent/orchestrator/session/store-live-publication.test.mjs +0 -83
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +0 -647
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.test.mjs +0 -219
- package/src/runtime/agent/orchestrator/session/task-wait-control.test.mjs +0 -78
- package/src/runtime/agent/orchestrator/tools/builtin/absence-absorption.test.mjs +0 -43
- package/src/runtime/agent/orchestrator/tools/builtin/absolute-glob-expand.test.mjs +0 -63
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +0 -14
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/tools/builtin/enoent-outside-project.test.mjs +0 -56
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +0 -103
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +0 -30
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +0 -390
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +0 -83
- package/src/runtime/agent/orchestrator/tools/builtin/grep-output-budget.test.mjs +0 -31
- package/src/runtime/agent/orchestrator/tools/builtin/grep-single-file-rescue.test.mjs +0 -106
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.test.mjs +0 -102
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +0 -152
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/tools/builtin/noise-dir-visibility.test.mjs +0 -69
- package/src/runtime/agent/orchestrator/tools/builtin/read-glob-survey.test.mjs +0 -71
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.test.mjs +0 -56
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.test.mjs +0 -16
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-top-k.test.mjs +0 -39
- package/src/runtime/agent/orchestrator/tools/builtin/write-symlink.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-anchor-relocation.test.mjs +0 -55
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +0 -88
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +0 -72
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +0 -54
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +0 -20
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +0 -55
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +0 -268
- package/src/runtime/agent/orchestrator/tools/env-scrub.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.test.mjs +0 -19
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +0 -104
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +0 -661
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +0 -552
- package/src/runtime/agent/orchestrator/tools/patch/patch-symlink.test.mjs +0 -141
- package/src/runtime/agent/orchestrator/tools/patch/v4a-pure-move.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/patch/v4a-section-coalesce.test.mjs +0 -81
- package/src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.test.mjs +0 -52
- package/src/runtime/attachments/store.test.mjs +0 -209
- package/src/runtime/bridge-clients.test.mjs +0 -827
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +0 -17
- package/src/runtime/channels/lib/webhook/deliveries.test.mjs +0 -18
- package/src/runtime/channels/lib/webhook/relay-tunnel.test.mjs +0 -68
- package/src/runtime/media/adapters/codex-image.test.mjs +0 -22
- package/src/runtime/media/download.test.mjs +0 -52
- package/src/runtime/media/renditions.test.mjs +0 -99
- package/src/runtime/media/store.test.mjs +0 -159
- package/src/runtime/memory/lib/archive-security.test.mjs +0 -128
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +0 -33
- package/src/runtime/memory/lib/http-wire.test.mjs +0 -15
- package/src/runtime/memory/lib/memory-cycle-packets.test.mjs +0 -49
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +0 -78
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +0 -50
- package/src/runtime/memory/lib/pg/process.test.mjs +0 -36
- package/src/runtime/memory/lib/trace-store.test.mjs +0 -57
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +0 -52
- package/src/runtime/office/office-assurance.test.mjs +0 -371
- package/src/runtime/office/office-live-runtime.test.mjs +0 -819
- package/src/runtime/office/office-runtime.test.mjs +0 -2070
- package/src/runtime/shared/background-tasks.test.mjs +0 -78
- package/src/runtime/shared/bounded-download.test.mjs +0 -41
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -25
- package/src/runtime/shared/child-spawn-remote.test.mjs +0 -124
- package/src/runtime/shared/remote-intent.test.mjs +0 -40
- package/src/runtime/shared/resource-admission.test.mjs +0 -43
- package/src/runtime/shared/runtime-root.test.mjs +0 -34
- package/src/runtime/shared/schedule-time.test.mjs +0 -29
- package/src/runtime/shared/session-runtime-health.test.mjs +0 -83
- package/src/runtime/shared/skill-document.test.mjs +0 -81
- package/src/runtime/shared/tool-surface.test.mjs +0 -87
- package/src/runtime/shared/update-checker.test.mjs +0 -33
- package/src/runtime/shared/user-cwd.test.mjs +0 -43
- package/src/runtime/shared/webhook-session-run.test.mjs +0 -14
- package/src/session-runtime/agent-disable.test.mjs +0 -142
- package/src/session-runtime/cwd-plugins.test.mjs +0 -75
- package/src/session-runtime/cwd-tool-routing.test.mjs +0 -61
- package/src/session-runtime/deferred-tool-delta.test.mjs +0 -40
- package/src/session-runtime/global-extensions.test.mjs +0 -28
- package/src/session-runtime/global-settings-session-identity.test.mjs +0 -76
- package/src/session-runtime/goal-runtime.test.mjs +0 -645
- package/src/session-runtime/lifecycle-api.test.mjs +0 -183
- package/src/session-runtime/model-settings-persist.test.mjs +0 -105
- package/src/session-runtime/notification-bus.test.mjs +0 -148
- package/src/session-runtime/plugin-mcp-standard.test.mjs +0 -177
- package/src/session-runtime/prewarm.test.mjs +0 -52
- package/src/session-runtime/provider-request-snapshot.test.mjs +0 -59
- package/src/session-runtime/resource-api-global.test.mjs +0 -109
- package/src/session-runtime/session-lifecycle.test.mjs +0 -125
- package/src/session-runtime/session-title.test.mjs +0 -36
- package/src/session-runtime/skills-api.test.mjs +0 -99
- package/src/session-runtime/tool-policy-surface.test.mjs +0 -385
- package/src/standalone/agent-tool/helpers.test.mjs +0 -62
- package/src/standalone/agent-tool/notify.test.mjs +0 -37
- package/src/standalone/agent-tool/spawn-flow.test.mjs +0 -104
- package/src/standalone/agent-tool/worker-index.test.mjs +0 -132
- package/src/standalone/channel-restart.test.mjs +0 -145
- package/src/standalone/channel-session-router.test.mjs +0 -37
- package/src/standalone/channel-worker-heartbeat.test.mjs +0 -30
- package/src/standalone/hook-bus/handlers.test.mjs +0 -88
- package/src/standalone/plugin-admin.test.mjs +0 -173
- package/src/standalone/session-runtime-agent-control-client.test.mjs +0 -71
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +0 -139
- package/src/standalone/session-runtime-host-factory.test.mjs +0 -31
- package/src/standalone/session-runtime-host-health.test.mjs +0 -160
- package/src/standalone/session-runtime-inline-host.test.mjs +0 -385
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
- package/src/standalone/session-runtime-shard-host.test.mjs +0 -642
- package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
- package/src/standalone/session-service-agent-tree.test.mjs +0 -984
- package/src/standalone/session-service-goal-recovery.test.mjs +0 -177
- package/src/tui/app/live-spinner-visibility.test.mjs +0 -44
- package/src/tui/app/panel-epoch.test.mjs +0 -1009
- package/src/tui/app/panel-handoff.test.mjs +0 -311
- package/src/tui/app/prompt-submit.test.mjs +0 -206
- package/src/tui/session/agent-message-source.test.mjs +0 -37
- package/src/tui/session/completion-card-restore.test.mjs +0 -161
- package/src/tui/session/goal-continuation.test.mjs +0 -203
- package/src/tui/session/oauth-flows.test.mjs +0 -134
- package/src/tui/session/queue-helpers.test.mjs +0 -46
- package/src/tui/session/session-action-surface.test.mjs +0 -190
- package/src/tui/session/task-wait-submit.test.mjs +0 -80
- package/src/tui/session/tool-result-status.test.mjs +0 -131
- package/src/tui/session/tui-steering-persist.test.mjs +0 -104
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixdog",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.154",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone mixdog coding-agent CLI/TUI workspace.",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"!scripts/bench/",
|
|
32
32
|
"!scripts/recall-bench-*.txt",
|
|
33
33
|
"!scripts/*-test.mjs",
|
|
34
|
+
"!scripts/*.test.mjs",
|
|
34
35
|
"!scripts/*-smoke.mjs",
|
|
35
36
|
"!scripts/smoke-loop*.mjs",
|
|
36
37
|
"!scripts/*-bench.mjs",
|
|
@@ -40,6 +41,8 @@
|
|
|
40
41
|
"!scripts/*.jsx",
|
|
41
42
|
"src/",
|
|
42
43
|
"!src/tui/dev",
|
|
44
|
+
"!src/**/*.test.mjs",
|
|
45
|
+
"!src/**/*.test.jsx",
|
|
43
46
|
"vendor/"
|
|
44
47
|
],
|
|
45
48
|
"scripts": {
|
|
@@ -68,6 +71,7 @@
|
|
|
68
71
|
"test:spec-advisory": "node scripts/advisory-spec-test.mjs && npm run test:spec-advisory --prefix apps/desktop",
|
|
69
72
|
"test:session-transport": "node --test scripts/session-transport-test.mjs scripts/daemon-bootstrap-test.mjs src/runtime/shared/child-spawn-remote.test.mjs src/standalone/session-runtime-agent-control-client.test.mjs src/standalone/session-runtime-dispatch-cancel.test.mjs src/standalone/session-runtime-host-factory.test.mjs src/standalone/session-runtime-inline-host.test.mjs src/standalone/session-runtime-provider-cooldown.test.mjs src/standalone/session-runtime-shard-host.test.mjs src/standalone/session-runtime-shard-router.test.mjs",
|
|
70
73
|
"test:session": "node --test scripts/runtime-turn-contract-test.mjs scripts/session-save-fault-store-test.mjs scripts/turn-review-revert-test.mjs src/runtime/shared/tool-surface.test.mjs src/runtime/bridge-clients.test.mjs",
|
|
74
|
+
"test:quick": "npm run test:session && npm run test:session-transport && npm run test:desktop-main --prefix apps/desktop",
|
|
71
75
|
"test:office": "node --test src/runtime/office/office-runtime.test.mjs src/runtime/office/office-assurance.test.mjs && npm run test:office-approval --prefix apps/desktop",
|
|
72
76
|
"test:office:live": "node scripts/run-office-live-tests.mjs",
|
|
73
77
|
"test:media": "node --test src/runtime/media/store.test.mjs src/runtime/media/renditions.test.mjs src/runtime/media/adapters/codex-image.test.mjs",
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
buildability invariants. Treat mutable behavior, UX, exact text, snapshots,
|
|
10
10
|
and implementation shape as advisory specifications; update them when the
|
|
11
11
|
requested behavior changes instead of preserving obsolete behavior.
|
|
12
|
+
- A new test asserts observable behavior, never source text or implementation
|
|
13
|
+
shape, and never re-asserts a contract another test already owns.
|
|
12
14
|
- A check runs at the strictness the task requires; never raise a tool's own
|
|
13
15
|
severity beyond it.
|
|
14
16
|
- If verification fails, collect all failures, leave Verification, complete all
|
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
_intraTurnSig,
|
|
28
28
|
} from './loop/tool-classify.mjs';
|
|
29
29
|
import { preDispatchDenyForSession } from './loop/pre-dispatch-deny.mjs';
|
|
30
|
-
import { runRecallFastTrackCompact } from './loop/recall-fasttrack.mjs';
|
|
31
30
|
import { executeTool, _scopedCacheOutcomeForCall, resolveLiveToolCwd } from './loop/tool-exec.mjs';
|
|
32
31
|
|
|
33
32
|
// classifyResultKind is imported from result-classification.mjs at the top of
|
|
@@ -239,28 +239,6 @@ export function collectWorkingFileGroups(messages, cap = Number.POSITIVE_INFINIT
|
|
|
239
239
|
}, prior, limit, cwd);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
export function collectWorkingFiles(messages, cap = Number.POSITIVE_INFINITY, options = {}) {
|
|
243
|
-
const groups = collectWorkingFileGroups(messages, cap, options);
|
|
244
|
-
return [...groups.modified, ...groups.referenced].map((entry) => entry.path);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export function stripWorkingFileSections(text) {
|
|
248
|
-
const out = [];
|
|
249
|
-
let skipping = false;
|
|
250
|
-
for (const raw of String(text || '').split('\n')) {
|
|
251
|
-
const line = raw.trim();
|
|
252
|
-
if (line === '## Working files') {
|
|
253
|
-
skipping = true;
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (skipping && (/^##\s+/.test(line) || /^<\/?prior-compacted-context>$/.test(line))) {
|
|
257
|
-
skipping = false;
|
|
258
|
-
}
|
|
259
|
-
if (!skipping) out.push(raw);
|
|
260
|
-
}
|
|
261
|
-
return out.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
242
|
function indexToolResults(messages) {
|
|
265
243
|
const map = new Map();
|
|
266
244
|
for (const m of messages || []) {
|
|
@@ -43,8 +43,8 @@ export function isProtectedContextUserMessage(m) {
|
|
|
43
43
|
// content prefix (the `<skill>` envelope from buildSkillResultEnvelope) so the
|
|
44
44
|
// check survives even if the synthetic `meta` field is dropped during a tail
|
|
45
45
|
// rebuild. It is NOT the human's latest prompt and must be excluded from
|
|
46
|
-
// "latest human request" selection (deriveCurrentRequest
|
|
47
|
-
//
|
|
46
|
+
// "latest human request" selection (deriveCurrentRequest). The `meta:'skill'`
|
|
47
|
+
// marker is also honoured.
|
|
48
48
|
export function isInjectedSkillBodyMessage(m) {
|
|
49
49
|
if (m?.role !== 'user') return false;
|
|
50
50
|
if (m.meta === 'skill') return true;
|
|
@@ -717,13 +717,6 @@ function truncateTailToCap(messages, cap) {
|
|
|
717
717
|
return estimateMessagesTokens(sanitized) <= cap ? sanitized : [];
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
-
function splitRecallFitInputs(recallText) {
|
|
721
|
-
return {
|
|
722
|
-
recall: String(recallText || '').trim(),
|
|
723
|
-
prior: '',
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
720
|
function recallTailStartIndex(live, tail) {
|
|
728
721
|
if (!tail.length) return live.length;
|
|
729
722
|
const first = tail[0];
|
|
@@ -837,9 +830,9 @@ function _recallFastTrackCompactMessages(messages, budgetTokens, opts = {}) {
|
|
|
837
830
|
head: recallHead,
|
|
838
831
|
lastTurnStartIdx = recallHead.length,
|
|
839
832
|
} = selectRecallPreservedTail(live, recallTailOpts);
|
|
840
|
-
const
|
|
833
|
+
const recallText = String(opts.recallText || '').trim();
|
|
841
834
|
if (recallHead.length === 0
|
|
842
|
-
&& !(
|
|
835
|
+
&& !(recallText || opts.allowEmptyRecall === true)) {
|
|
843
836
|
throw new Error('recallFastTrackCompactMessages: no compactable prior history before preserved tail');
|
|
844
837
|
}
|
|
845
838
|
|
|
@@ -851,7 +844,7 @@ function _recallFastTrackCompactMessages(messages, budgetTokens, opts = {}) {
|
|
|
851
844
|
}
|
|
852
845
|
const budgetRaisedBy = Math.max(0, budget - originalBudget);
|
|
853
846
|
|
|
854
|
-
if (!
|
|
847
|
+
if (!recallText && opts.allowEmptyRecall !== true) {
|
|
855
848
|
throw new Error('recallFastTrackCompactMessages: recall text is empty');
|
|
856
849
|
}
|
|
857
850
|
const oldHistory = recallHead;
|
|
@@ -871,14 +864,14 @@ function _recallFastTrackCompactMessages(messages, budgetTokens, opts = {}) {
|
|
|
871
864
|
? Math.min(recallRoomUncapped, Math.max(512, recallTokenCap - tailTokens))
|
|
872
865
|
: recallRoomUncapped;
|
|
873
866
|
const toolLines = collectToolOutcomeLines(recallHead)
|
|
874
|
-
.filter((line) => !
|
|
867
|
+
.filter((line) => !recallText.includes(String(line || '').trim()));
|
|
875
868
|
const workingFiles = collectWorkingFileGroups(recallHead, undefined, {
|
|
876
869
|
cwd: opts.cwd,
|
|
877
870
|
previousSummary: '',
|
|
878
871
|
now: Date.now(),
|
|
879
872
|
});
|
|
880
873
|
const conversationLines = excludeTailFromConversation(
|
|
881
|
-
conversationLinesFromMemoryText(
|
|
874
|
+
conversationLinesFromMemoryText(recallText),
|
|
882
875
|
recallTail,
|
|
883
876
|
);
|
|
884
877
|
const composedRecall = composeRecallHandoff({
|
|
@@ -894,7 +887,6 @@ function _recallFastTrackCompactMessages(messages, budgetTokens, opts = {}) {
|
|
|
894
887
|
fittedRecall,
|
|
895
888
|
recallRoom,
|
|
896
889
|
recallMeta,
|
|
897
|
-
'',
|
|
898
890
|
);
|
|
899
891
|
if (!summaryMessage) {
|
|
900
892
|
throw new Error(`recallFastTrackCompactMessages: summary cannot fit remaining budget=${recallRoom}`);
|
|
@@ -936,20 +928,15 @@ function _recallFastTrackCompactMessages(messages, budgetTokens, opts = {}) {
|
|
|
936
928
|
remainingTokens: budget - mandatoryCost,
|
|
937
929
|
recallTokenCap: (Number.isFinite(recallTokenCap) && recallTokenCap > 0) ? recallTokenCap : null,
|
|
938
930
|
recallRoom,
|
|
939
|
-
recallChars:
|
|
940
|
-
recallBytes: textByteLength(
|
|
941
|
-
priorChars: 0,
|
|
942
|
-
priorBytes: 0,
|
|
931
|
+
recallChars: recallText.length,
|
|
932
|
+
recallBytes: textByteLength(recallText),
|
|
943
933
|
summaryMessageChars: summaryContent.length,
|
|
944
934
|
summaryMessageBytes: textByteLength(summaryContent),
|
|
945
|
-
recallEmpty: !
|
|
946
|
-
|
|
947
|
-
recallTruncatedInSummary: !!recallFit.recall && !summaryContent.includes(recallFit.recall),
|
|
948
|
-
priorTruncatedInSummary: false,
|
|
935
|
+
recallEmpty: !recallText,
|
|
936
|
+
recallTruncatedInSummary: !!recallText && !summaryContent.includes(recallText),
|
|
949
937
|
tailTruncated: recallTail.some((m) => messageContentHasMarker(m, RECALL_TAIL_TRUNCATION_MARKER) || messageContentHasMarker(m, RECALL_TAIL_SHORT_TRUNCATION_MARKER)),
|
|
950
938
|
fileReattached: reattach.reattached,
|
|
951
939
|
tailOptions: recallTailOpts,
|
|
952
|
-
previousSummary: false,
|
|
953
940
|
durationMs: Date.now() - startedAt,
|
|
954
941
|
};
|
|
955
942
|
compactDebugLog('recall-fasttrack result', diagnostics);
|
|
@@ -346,28 +346,6 @@ function makeSemanticSummaryMessage(oldHistory, summary, semanticMeta = {}, pres
|
|
|
346
346
|
return makeSummaryMessage(parts.join('\n\n'));
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
export function buildRecallFastTrackQuery(messages, opts = {}) {
|
|
350
|
-
const maxChars = Math.max(200, Number(opts.maxChars) || 2_000);
|
|
351
|
-
const hints = String(opts.hints || 'current task decisions constraints file paths changed files verification failures next steps').trim();
|
|
352
|
-
let latestUser = '';
|
|
353
|
-
const recent = [];
|
|
354
|
-
const input = Array.isArray(messages) ? messages : [];
|
|
355
|
-
for (let i = input.length - 1; i >= 0; i -= 1) {
|
|
356
|
-
const m = input[i];
|
|
357
|
-
const text = extractText(m).trim();
|
|
358
|
-
if (!text) continue;
|
|
359
|
-
if (recent.length < 6) recent.unshift(text);
|
|
360
|
-
if (!latestUser && m?.role === 'user' && !isProtectedContextUserMessage(m) && !isInjectedSkillBodyMessage(m)) {
|
|
361
|
-
latestUser = text;
|
|
362
|
-
}
|
|
363
|
-
if (latestUser && recent.length >= 6) break;
|
|
364
|
-
}
|
|
365
|
-
const parts = [latestUser, hints, recent.join('\n')]
|
|
366
|
-
.map((s) => String(s || '').trim())
|
|
367
|
-
.filter(Boolean);
|
|
368
|
-
return truncateMiddle([...new Set(parts)].join('\n'), maxChars);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
349
|
// Fit the structured semantic summary into the remaining token budget WITHOUT
|
|
372
350
|
// dropping any required section. The incoming `summary` is already schema-valid
|
|
373
351
|
// (enforceSemanticSummarySchema ran upstream); here we shrink section bodies via
|
|
@@ -410,106 +388,15 @@ export function fitSemanticSummaryMessage(oldHistory, summary, remainingTokens,
|
|
|
410
388
|
export const RECALL_TAIL_TRUNCATION_MARKER = '[... truncated during recall tail preservation ...]';
|
|
411
389
|
export const RECALL_TAIL_SHORT_TRUNCATION_MARKER = '[truncated]';
|
|
412
390
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
// does NOT match an inline occurrence embedded in real content (e.g. a user note
|
|
418
|
-
// like "keep <prior-compacted-context> literal here"), so flattening the wrapper
|
|
419
|
-
// can never splice words together or corrupt marker-like text. The production
|
|
420
|
-
// wrapper is always emitted on its own line, so boundary-line stripping removes
|
|
421
|
-
// every real wrapper while leaving inline literals verbatim.
|
|
422
|
-
const PRIOR_COMPACTED_CONTEXT_BOUNDARY_RE = /^[ \t]*<\/?prior-compacted-context>[ \t]*$/;
|
|
423
|
-
|
|
424
|
-
// Strip the STRUCTURAL <prior-compacted-context> / </prior-compacted-context>
|
|
425
|
-
// boundary lines from prior text so re-wrapping never nests, while preserving
|
|
426
|
-
// any inline marker-like text inside real content exactly as written. Returns
|
|
427
|
-
// the bare inner content with the blank lines the removed boundary lines leave
|
|
428
|
-
// behind collapsed.
|
|
429
|
-
export function stripPriorCompactedContextWrappers(text) {
|
|
430
|
-
const raw = String(text ?? '');
|
|
431
|
-
if (!raw) return '';
|
|
432
|
-
// Keep every non-structural byte, including leading/trailing and repeated
|
|
433
|
-
// newlines. Removing only the structural line itself (not trim/collapse)
|
|
434
|
-
// preserves the layout of all remaining content.
|
|
435
|
-
return raw
|
|
436
|
-
.split('\n')
|
|
437
|
-
.filter((line) => !PRIOR_COMPACTED_CONTEXT_BOUNDARY_RE.test(line))
|
|
438
|
-
.join('\n');
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Remove only STRUCTURALLY IDENTICAL blank-line-separated blocks (byte-for-byte
|
|
442
|
-
// repeats) so a prior context re-fed across many compaction cycles keeps every
|
|
443
|
-
// distinct requirement/fact and cannot grow without bound. The dedupe is
|
|
444
|
-
// content-preserving: the key is the block's EXACT text — internal whitespace is
|
|
445
|
-
// never collapsed and meaningful block content is never trimmed — so distinct
|
|
446
|
-
// strings such as `printf 'a b'` and `printf 'a b'` are BOTH kept verbatim.
|
|
447
|
-
// Only an exact repeat of a previously emitted block is dropped; blank
|
|
448
|
-
// separators (whitespace-only splits) are skipped, never real content.
|
|
449
|
-
function dedupePriorCompactedBlocks(text) {
|
|
450
|
-
const raw = String(text ?? '');
|
|
451
|
-
if (!raw.trim()) return '';
|
|
452
|
-
const seen = new Set();
|
|
453
|
-
const parts = raw.split(/(\n{2,})/);
|
|
454
|
-
const out = [];
|
|
455
|
-
let separator = '';
|
|
456
|
-
for (let i = 0; i < parts.length; i += 1) {
|
|
457
|
-
const part = parts[i];
|
|
458
|
-
if (i % 2 === 1) {
|
|
459
|
-
separator += part;
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
// Whitespace-only parts are layout, not a duplicate candidate.
|
|
463
|
-
if (!part.trim()) {
|
|
464
|
-
out.push(separator, part);
|
|
465
|
-
separator = '';
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
if (seen.has(part)) {
|
|
469
|
-
// Drop only the exact repeated structural block and its preceding
|
|
470
|
-
// separator; all retained text and whitespace stay byte-for-byte.
|
|
471
|
-
separator = '';
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
out.push(separator, part);
|
|
475
|
-
separator = '';
|
|
476
|
-
seen.add(part);
|
|
477
|
-
}
|
|
478
|
-
out.push(separator);
|
|
479
|
-
return out.join('');
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Canonicalize prior compacted context to AT MOST ONE wrapper: flatten any
|
|
483
|
-
// nested/duplicated wrappers accumulated by earlier cycles, dedupe repeated
|
|
484
|
-
// blocks, then wrap the surviving content exactly once. Repeated compaction can
|
|
485
|
-
// therefore never nest or duplicate the prior context, each distinct
|
|
486
|
-
// requirement/fact is preserved exactly once, and repeated-cycle token size
|
|
487
|
-
// stays bounded.
|
|
488
|
-
//
|
|
489
|
-
// Optimization-safe empty-prior interpretation: when there is NO prior content
|
|
490
|
-
// (empty / blank / boundary-tag-only input) this returns '' so the generated
|
|
491
|
-
// summary carries ZERO wrappers instead of an empty
|
|
492
|
-
// <prior-compacted-context></prior-compacted-context> pair. The production
|
|
493
|
-
// summary body joins only non-empty parts (makeRecall*SummaryMessageParts), so
|
|
494
|
-
// an empty wrapper cannot be carried and would only waste tokens. "Exactly one
|
|
495
|
-
// wrapper" is thus realized as: exactly one when prior content exists, none when
|
|
496
|
-
// it does not — and never more than one, never nested.
|
|
497
|
-
export function formatPriorCompactedContextBlock(priorText) {
|
|
498
|
-
const flattened = dedupePriorCompactedBlocks(stripPriorCompactedContextWrappers(priorText));
|
|
499
|
-
if (!flattened) return '';
|
|
500
|
-
return `${PRIOR_COMPACTED_CONTEXT_OPEN}\n${flattened}\n${PRIOR_COMPACTED_CONTEXT_CLOSE}`;
|
|
501
|
-
}
|
|
502
|
-
|
|
391
|
+
// Peel the structural summary header off a prior summary body so semantic
|
|
392
|
+
// compaction can feed bare text into its <previous-summary> block. Sessions
|
|
393
|
+
// stored before recall-fasttrack stopped chaining summaries may still carry a
|
|
394
|
+
// <prior-compacted-context> wrapper, so those boundary lines are dropped too.
|
|
503
395
|
export function stripNestedSummaryHeaderLines(text) {
|
|
504
396
|
const raw = String(text ?? '');
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
//
|
|
508
|
-
// `X\n` + join + live `X`, generic block dedupe cannot know that one
|
|
509
|
-
// newline belongs to X and must survive dropping the duplicate live X.
|
|
510
|
-
// Peel only the emitted wrapper/header/join bytes and retain the inner
|
|
511
|
-
// prior slice verbatim. The live recall is dropped only when it is the
|
|
512
|
-
// same payload the wrapper already carries.
|
|
397
|
+
// Peel only the emitted wrapper/header/join bytes and keep the inner slice
|
|
398
|
+
// verbatim: splitting into lines would lose ownership of a run of newlines,
|
|
399
|
+
// where one newline belongs to the content and must survive.
|
|
513
400
|
const openMatch = /^[ \t]*<prior-compacted-context>[ \t]*\n/m.exec(raw);
|
|
514
401
|
if (openMatch) {
|
|
515
402
|
const closeRe = /\n[ \t]*<\/prior-compacted-context>[ \t]*(?=\n|$)/g;
|
|
@@ -544,10 +431,9 @@ export function stripNestedSummaryHeaderLines(text) {
|
|
|
544
431
|
followsStructuralHeader = true;
|
|
545
432
|
continue;
|
|
546
433
|
}
|
|
547
|
-
// Summary parts are joined with "\n\n".
|
|
434
|
+
// Summary parts are joined with "\n\n". The first empty line after
|
|
548
435
|
// stripped summary metadata is that join's structural separator, not
|
|
549
|
-
//
|
|
550
|
-
// formatPriorCompactedContextBlock wraps the result again.
|
|
436
|
+
// content.
|
|
551
437
|
if (followsStructuralHeader && line === '') {
|
|
552
438
|
followsStructuralHeader = false;
|
|
553
439
|
continue;
|
|
@@ -570,46 +456,19 @@ export function stripNestedSummaryHeaderLines(text) {
|
|
|
570
456
|
return out.join('\n');
|
|
571
457
|
}
|
|
572
458
|
|
|
573
|
-
function makeRecallFastTrackSummaryMessageParts(oldHistory, recallPart,
|
|
459
|
+
function makeRecallFastTrackSummaryMessageParts(oldHistory, recallPart, recallMeta = {}) {
|
|
574
460
|
const header = compactHeader(oldHistory);
|
|
575
461
|
header.push(`compact_type=${COMPACT_TYPE_RECALL_FASTTRACK} source=recall-fasttrack query_sha=${recallMeta.querySha || 'none'}`);
|
|
576
462
|
const parts = [header.join('\n')];
|
|
577
|
-
const priorBlock = formatPriorCompactedContextBlock(priorPart);
|
|
578
|
-
if (priorBlock) parts.push(priorBlock);
|
|
579
463
|
const recall = String(recallPart || '').trim();
|
|
580
464
|
if (recall) parts.push(recall);
|
|
581
465
|
return makeSummaryMessage(parts.join('\n\n'));
|
|
582
466
|
}
|
|
583
467
|
|
|
584
|
-
export function fitRecallFastTrackSummaryMessage(oldHistory, recallText, remainingTokens, recallMeta = {}
|
|
468
|
+
export function fitRecallFastTrackSummaryMessage(oldHistory, recallText, remainingTokens, recallMeta = {}) {
|
|
585
469
|
const recall = String(recallText || '').trim();
|
|
586
|
-
const prior = String(priorPart || '');
|
|
587
|
-
|
|
588
|
-
let fittedPrior = prior;
|
|
589
|
-
if (prior) {
|
|
590
|
-
let lo = 0;
|
|
591
|
-
let hi = prior.length;
|
|
592
|
-
let bestPriorLen = 0;
|
|
593
|
-
while (lo <= hi) {
|
|
594
|
-
const mid = Math.floor((lo + hi) / 2);
|
|
595
|
-
const candidate = makeRecallFastTrackSummaryMessageParts(oldHistory, '', prior.slice(0, mid), recallMeta);
|
|
596
|
-
if (estimateMessagesTokens([candidate]) <= remainingTokens) {
|
|
597
|
-
bestPriorLen = mid;
|
|
598
|
-
lo = mid + 1;
|
|
599
|
-
} else {
|
|
600
|
-
hi = mid - 1;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
fittedPrior = prior.slice(0, bestPriorLen);
|
|
604
|
-
if (!fittedPrior && prior) {
|
|
605
|
-
const markerOnly = makeRecallFastTrackSummaryMessageParts(oldHistory, '', RECALL_TAIL_TRUNCATION_MARKER, recallMeta);
|
|
606
|
-
if (estimateMessagesTokens([markerOnly]) <= remainingTokens) {
|
|
607
|
-
fittedPrior = RECALL_TAIL_TRUNCATION_MARKER;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
470
|
|
|
612
|
-
const minimal = makeRecallFastTrackSummaryMessageParts(oldHistory, '',
|
|
471
|
+
const minimal = makeRecallFastTrackSummaryMessageParts(oldHistory, '', recallMeta);
|
|
613
472
|
if (estimateMessagesTokens([minimal]) > remainingTokens) return null;
|
|
614
473
|
if (!recall) return minimal;
|
|
615
474
|
|
|
@@ -618,14 +477,14 @@ export function fitRecallFastTrackSummaryMessage(oldHistory, recallText, remaini
|
|
|
618
477
|
// Root-block granularity fit: drop the OLDEST blocks WHOLE (never cut
|
|
619
478
|
// a `# chunk` / `# raw_pending` / `# raw_terminal` block mid-entry);
|
|
620
479
|
// dropping more leading blocks only shrinks the body, so binary-search
|
|
621
|
-
// the minimal drop count
|
|
480
|
+
// the minimal drop count.
|
|
622
481
|
let loB = 0;
|
|
623
482
|
let hiB = blocks.length;
|
|
624
483
|
let bestLo = -1;
|
|
625
484
|
while (loB <= hiB) {
|
|
626
485
|
const midB = Math.floor((loB + hiB) / 2);
|
|
627
486
|
const body = [preamble, ...blocks.slice(midB)].filter(Boolean).join('\n\n');
|
|
628
|
-
const candidate = makeRecallFastTrackSummaryMessageParts(oldHistory, body,
|
|
487
|
+
const candidate = makeRecallFastTrackSummaryMessageParts(oldHistory, body, recallMeta);
|
|
629
488
|
if (estimateMessagesTokens([candidate]) <= remainingTokens) {
|
|
630
489
|
bestLo = midB;
|
|
631
490
|
hiB = midB - 1;
|
|
@@ -635,12 +494,11 @@ export function fitRecallFastTrackSummaryMessage(oldHistory, recallText, remaini
|
|
|
635
494
|
}
|
|
636
495
|
if (bestLo >= 0) {
|
|
637
496
|
const body = [preamble, ...blocks.slice(bestLo)].filter(Boolean).join('\n\n');
|
|
638
|
-
return makeRecallFastTrackSummaryMessageParts(oldHistory, body,
|
|
497
|
+
return makeRecallFastTrackSummaryMessageParts(oldHistory, body, recallMeta);
|
|
639
498
|
}
|
|
640
|
-
// Even
|
|
641
|
-
// prior - try preamble alone, else the no-recall minimal message.
|
|
499
|
+
// Even the preamble alone overflows: try it, else the minimal message.
|
|
642
500
|
if (preamble) {
|
|
643
|
-
const preambleOnly = makeRecallFastTrackSummaryMessageParts(oldHistory, preamble,
|
|
501
|
+
const preambleOnly = makeRecallFastTrackSummaryMessageParts(oldHistory, preamble, recallMeta);
|
|
644
502
|
if (estimateMessagesTokens([preambleOnly]) <= remainingTokens) return preambleOnly;
|
|
645
503
|
}
|
|
646
504
|
return minimal;
|
|
@@ -654,7 +512,7 @@ export function fitRecallFastTrackSummaryMessage(oldHistory, recallText, remaini
|
|
|
654
512
|
let best = minimal;
|
|
655
513
|
while (lo <= hi) {
|
|
656
514
|
const mid = Math.floor((lo + hi) / 2);
|
|
657
|
-
const candidate = makeRecallFastTrackSummaryMessageParts(oldHistory, recall.slice(0, mid),
|
|
515
|
+
const candidate = makeRecallFastTrackSummaryMessageParts(oldHistory, recall.slice(0, mid), recallMeta);
|
|
658
516
|
if (estimateMessagesTokens([candidate]) <= remainingTokens) {
|
|
659
517
|
best = candidate;
|
|
660
518
|
lo = mid + 1;
|
|
@@ -665,28 +523,23 @@ export function fitRecallFastTrackSummaryMessage(oldHistory, recallText, remaini
|
|
|
665
523
|
return best;
|
|
666
524
|
}
|
|
667
525
|
|
|
668
|
-
// ---
|
|
526
|
+
// --- Root-block splitting for the recall digest ----------------------------
|
|
669
527
|
//
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
// each root/raw block starting with one of:
|
|
528
|
+
// A memory digest renders chunks TIME-ORDERED (oldest first), each root/raw
|
|
529
|
+
// block starting with one of:
|
|
673
530
|
// # chunk N root=ID[ category=X]
|
|
674
531
|
// # raw_pending N id=ID
|
|
675
532
|
// # raw_terminal N id=ID
|
|
676
|
-
// and blocks joined by "\n\n"
|
|
677
|
-
//
|
|
678
|
-
// text (also "\n\n"-joined) — preserved verbatim as a non-block segment.
|
|
533
|
+
// and blocks joined by "\n\n", after an optional preamble that is kept verbatim
|
|
534
|
+
// as a non-block segment.
|
|
679
535
|
//
|
|
680
|
-
//
|
|
681
|
-
//
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
// This splitter finds block boundaries by the label pattern (robust to
|
|
685
|
-
// blank lines inside member/raw content, since it anchors on the distinctive
|
|
686
|
-
// "# chunk /raw_pending/raw_terminal" line rather than a blank-line split).
|
|
536
|
+
// fitRecallFastTrackSummaryMessage drops whole blocks instead of cutting one
|
|
537
|
+
// mid-entry, since losing half a root's content silently corrupts that entry.
|
|
538
|
+
// Boundaries come from the label pattern, which stays robust to blank lines
|
|
539
|
+
// inside member/raw content.
|
|
687
540
|
const RECALL_ROOT_BLOCK_HEADER_RE = /^# (?:chunk \d+ root=\d+(?: category=\S+)?|raw_pending \d+ id=\d+|raw_terminal \d+ id=\d+)[ \t]*$/;
|
|
688
541
|
|
|
689
|
-
|
|
542
|
+
function splitRecallRootBlocks(text) {
|
|
690
543
|
const value = String(text || '');
|
|
691
544
|
if (!value.trim()) return { preamble: '', blocks: [] };
|
|
692
545
|
const re = new RegExp(RECALL_ROOT_BLOCK_HEADER_RE.source, 'gm');
|
|
@@ -708,97 +561,3 @@ export function splitRecallRootBlocks(text) {
|
|
|
708
561
|
return { preamble, blocks };
|
|
709
562
|
}
|
|
710
563
|
|
|
711
|
-
// Minimal-header summary-message wrapper for the smart-compact roots path.
|
|
712
|
-
// Keeps the SUMMARY_PREFIX anchor (isSummaryMessage / selectCompactionWindow
|
|
713
|
-
// / clear-preserve / TUI all key off startsWith(SUMMARY_PREFIX)) but skips the
|
|
714
|
-
// full sha256/roleCounts header line that fitRecallFastTrackSummaryMessage
|
|
715
|
-
// computes — smart-arrival replacement is a lightweight prefix swap, not the
|
|
716
|
-
// anchored LLM-summary compact, so a heavy per-call header is unneeded cost.
|
|
717
|
-
function makeRecallRootsSummaryMessageParts(oldHistory, rootsPart, priorPart, recallMeta = {}) {
|
|
718
|
-
const header = `${SUMMARY_PREFIX}\nmessages=${(oldHistory || []).length} compact_type=${COMPACT_TYPE_RECALL_FASTTRACK} source=smart-arrival query_sha=${recallMeta.querySha || 'none'}`;
|
|
719
|
-
const parts = [header];
|
|
720
|
-
const priorBlock = formatPriorCompactedContextBlock(priorPart);
|
|
721
|
-
if (priorBlock) parts.push(priorBlock);
|
|
722
|
-
const roots = String(rootsPart || '').trim();
|
|
723
|
-
if (roots) parts.push(roots);
|
|
724
|
-
return makeSummaryMessage(parts.join('\n\n'));
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Root-block-aware fit for the smart-compact arrival path (Step1). Mirrors
|
|
728
|
-
// fitRecallFastTrackSummaryMessage's prior-block binary-search fit, but the
|
|
729
|
-
// recall body is fit at ROOT-BLOCK granularity: when the full set of root
|
|
730
|
-
// blocks (kept in original time order) exceeds remainingTokens, the OLDEST
|
|
731
|
-
// blocks are dropped WHOLE (never character-truncated mid-block) until the
|
|
732
|
-
// remaining (newest-biased) suffix fits. Because dropping more leading
|
|
733
|
-
// blocks can only shrink (never grow) the serialized size, the minimal-drop
|
|
734
|
-
// threshold is found via binary search on the drop count.
|
|
735
|
-
export function fitRecallRootsMessage(oldHistory, recallText, remainingTokens, recallMeta = {}, priorPart = '') {
|
|
736
|
-
const prior = String(priorPart || '');
|
|
737
|
-
|
|
738
|
-
let fittedPrior = prior;
|
|
739
|
-
if (prior) {
|
|
740
|
-
let lo = 0;
|
|
741
|
-
let hi = prior.length;
|
|
742
|
-
let bestPriorLen = 0;
|
|
743
|
-
while (lo <= hi) {
|
|
744
|
-
const mid = Math.floor((lo + hi) / 2);
|
|
745
|
-
const candidate = makeRecallRootsSummaryMessageParts(oldHistory, '', prior.slice(0, mid), recallMeta);
|
|
746
|
-
if (estimateMessagesTokens([candidate]) <= remainingTokens) {
|
|
747
|
-
bestPriorLen = mid;
|
|
748
|
-
lo = mid + 1;
|
|
749
|
-
} else {
|
|
750
|
-
hi = mid - 1;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
fittedPrior = prior.slice(0, bestPriorLen);
|
|
754
|
-
if (!fittedPrior && prior) {
|
|
755
|
-
const markerOnly = makeRecallRootsSummaryMessageParts(oldHistory, '', RECALL_TAIL_TRUNCATION_MARKER, recallMeta);
|
|
756
|
-
if (estimateMessagesTokens([markerOnly]) <= remainingTokens) {
|
|
757
|
-
fittedPrior = RECALL_TAIL_TRUNCATION_MARKER;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
const minimal = makeRecallRootsSummaryMessageParts(oldHistory, '', fittedPrior, recallMeta);
|
|
763
|
-
if (estimateMessagesTokens([minimal]) > remainingTokens) return null;
|
|
764
|
-
|
|
765
|
-
const { preamble, blocks } = splitRecallRootBlocks(recallText);
|
|
766
|
-
if (blocks.length === 0) {
|
|
767
|
-
// No parseable root-block boundaries (empty / non-dump recallText) —
|
|
768
|
-
// degrade to keep-whole-if-it-fits, else drop entirely. Never
|
|
769
|
-
// mid-truncates: a non-block body is treated as a single atomic unit.
|
|
770
|
-
const whole = String(recallText || '').trim();
|
|
771
|
-
if (!whole) return minimal;
|
|
772
|
-
const full = makeRecallRootsSummaryMessageParts(oldHistory, whole, fittedPrior, recallMeta);
|
|
773
|
-
if (estimateMessagesTokens([full]) <= remainingTokens) return full;
|
|
774
|
-
return minimal;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
let loB = 0;
|
|
778
|
-
let hiB = blocks.length;
|
|
779
|
-
let bestLo = -1;
|
|
780
|
-
while (loB <= hiB) {
|
|
781
|
-
const mid = Math.floor((loB + hiB) / 2);
|
|
782
|
-
const kept = blocks.slice(mid);
|
|
783
|
-
const body = [preamble, ...kept].filter(Boolean).join('\n\n');
|
|
784
|
-
const candidate = makeRecallRootsSummaryMessageParts(oldHistory, body, fittedPrior, recallMeta);
|
|
785
|
-
if (estimateMessagesTokens([candidate]) <= remainingTokens) {
|
|
786
|
-
bestLo = mid;
|
|
787
|
-
hiB = mid - 1;
|
|
788
|
-
} else {
|
|
789
|
-
loB = mid + 1;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
if (bestLo >= 0) {
|
|
793
|
-
const kept = blocks.slice(bestLo);
|
|
794
|
-
const body = [preamble, ...kept].filter(Boolean).join('\n\n');
|
|
795
|
-
return makeRecallRootsSummaryMessageParts(oldHistory, body, fittedPrior, recallMeta);
|
|
796
|
-
}
|
|
797
|
-
// Even zero root blocks (preamble alone) overflows alongside the fitted
|
|
798
|
-
// prior — try preamble alone, else the no-recall minimal header.
|
|
799
|
-
if (preamble) {
|
|
800
|
-
const preambleOnly = makeRecallRootsSummaryMessageParts(oldHistory, preamble, fittedPrior, recallMeta);
|
|
801
|
-
if (estimateMessagesTokens([preambleOnly]) <= remainingTokens) return preambleOnly;
|
|
802
|
-
}
|
|
803
|
-
return minimal;
|
|
804
|
-
}
|
|
@@ -44,12 +44,6 @@ export {
|
|
|
44
44
|
pruneToolOutputsUnanchored,
|
|
45
45
|
} from './compact/budget.mjs';
|
|
46
46
|
|
|
47
|
-
export {
|
|
48
|
-
buildRecallFastTrackQuery,
|
|
49
|
-
splitRecallRootBlocks,
|
|
50
|
-
fitRecallRootsMessage,
|
|
51
|
-
} from './compact/summary.mjs';
|
|
52
|
-
|
|
53
47
|
export {
|
|
54
48
|
semanticCompactMessages,
|
|
55
49
|
recallFastTrackCompactMessages,
|