mixdog 0.9.152 → 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/LICENSES/browser-import-NOTICE.txt +11 -0
- package/NOTICE.md +11 -0
- package/README.md +8 -10
- package/package.json +19 -3
- package/scripts/computer-task-cost-report.mjs +144 -0
- package/scripts/run-office-live-tests.mjs +32 -0
- package/src/agents/front-worker/AGENT.md +13 -0
- package/src/agents/front-worker/agent.json +6 -0
- package/src/agents/heavy-worker/AGENT.md +10 -11
- package/src/agents/heavy-worker/agent.json +1 -1
- package/src/agents/maintainer/AGENT.md +0 -2
- package/src/agents/reviewer/AGENT.md +9 -13
- package/src/agents/reviewer/agent.json +1 -1
- package/src/agents/security/AGENT.md +14 -0
- package/src/agents/security/agent.json +6 -0
- package/src/agents/worker/AGENT.md +8 -8
- package/src/agents/worker/agent.json +1 -1
- package/src/agents/writer/AGENT.md +14 -0
- package/src/agents/writer/agent.json +6 -0
- package/src/rules/agent/00-common.md +4 -7
- package/src/rules/lead/01-general.md +0 -3
- package/src/rules/shared/10-tool-workflow.md +10 -2
- package/src/rules/shared/60-verification.md +2 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
- package/src/runtime/agent/orchestrator/config.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -10
- package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +140 -86
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -13
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -4
- package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
- 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 +11 -23
- 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/context-utils.mjs +86 -11
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +80 -18
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +80 -21
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +33 -72
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -51
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +39 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
- package/src/runtime/attachments/pdf-extract.mjs +3 -0
- package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
- package/src/runtime/browser-bridge/action-schema.mjs +251 -0
- package/src/runtime/browser-bridge/client.mjs +75 -26
- package/src/runtime/browser-bridge/tool-defs.mjs +132 -15
- package/src/runtime/computer-bridge/action-schema.mjs +653 -0
- package/src/runtime/computer-bridge/client.mjs +211 -28
- package/src/runtime/computer-bridge/tool-defs.mjs +25 -45
- package/src/runtime/office/assurance-benchmark.mjs +358 -0
- package/src/runtime/office/assurance.mjs +833 -0
- package/src/runtime/office/benchmark.mjs +496 -0
- package/src/runtime/office/capabilities.mjs +551 -0
- package/src/runtime/office/com-adapter.mjs +283 -0
- package/src/runtime/office/content-model.mjs +183 -0
- package/src/runtime/office/contract-benchmark.mjs +105 -0
- package/src/runtime/office/design-library.mjs +1562 -0
- package/src/runtime/office/design-system.mjs +1966 -0
- package/src/runtime/office/index.mjs +2413 -0
- package/src/runtime/office/journal.mjs +82 -0
- package/src/runtime/office/office-com-host.ps1 +3781 -0
- package/src/runtime/office/office-com-session-host.ps1 +855 -0
- package/src/runtime/office/ooxml-validator.mjs +271 -0
- package/src/runtime/office/pagination.mjs +132 -0
- package/src/runtime/office/pdf-adapter.mjs +591 -0
- package/src/runtime/office/pdf-analysis.mjs +372 -0
- package/src/runtime/office/pdf-render.mjs +167 -0
- package/src/runtime/office/pdf-security.mjs +63 -0
- package/src/runtime/office/polish-benchmark.mjs +190 -0
- package/src/runtime/office/portable-ooxml.mjs +1867 -0
- package/src/runtime/office/quality-live-benchmark.mjs +486 -0
- package/src/runtime/office/quality-pipeline.mjs +136 -0
- package/src/runtime/office/tabular.mjs +302 -0
- package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
- package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
- package/src/runtime/office/tool-defs.mjs +113 -0
- package/src/runtime/office/visual-diff.mjs +89 -0
- package/src/runtime/office/xlsx-assertions.mjs +172 -0
- package/src/runtime/office/xlsx-contract.mjs +147 -0
- package/src/runtime/shared/agent-route-config.mjs +9 -0
- package/src/runtime/shared/tool-execution-contract.mjs +16 -0
- package/src/runtime/shared/tool-surface.mjs +9 -0
- package/src/session-runtime/context-status.mjs +9 -3
- package/src/session-runtime/deferred-tool-delta.mjs +104 -0
- package/src/session-runtime/goal-runtime.mjs +617 -197
- package/src/session-runtime/lifecycle-api.mjs +50 -8
- package/src/session-runtime/provider-request-snapshot.mjs +33 -9
- package/src/session-runtime/resource-api.mjs +31 -94
- package/src/session-runtime/runtime-core.mjs +78 -26
- package/src/session-runtime/session-lifecycle.mjs +34 -12
- package/src/session-runtime/session-title.mjs +2 -2
- package/src/session-runtime/session-turn-api.mjs +15 -14
- package/src/session-runtime/tool-catalog-data.mjs +1 -1
- package/src/session-runtime/tool-catalog.mjs +38 -99
- package/src/session-runtime/tool-defs.mjs +6 -1
- package/src/session-runtime/tool-surface.mjs +18 -8
- package/src/session-runtime/workflow-agents-api.mjs +8 -25
- package/src/session-runtime/workflow.mjs +8 -6
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/job-views.mjs +3 -1
- package/src/standalone/agent-tool/notify.mjs +5 -1
- package/src/standalone/agent-tool/render.mjs +3 -0
- package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
- package/src/standalone/agent-tool/tool-def.mjs +1 -1
- package/src/standalone/agent-tool/worker-index.mjs +123 -0
- package/src/standalone/agent-tool/worker-rows.mjs +2 -1
- package/src/standalone/agent-tool.mjs +57 -10
- package/src/standalone/daemon.mjs +105 -6
- package/src/standalone/session-runtime-host.mjs +61 -48
- package/src/standalone/session-runtime-inline-host.mjs +34 -127
- package/src/standalone/session-runtime-worker.mjs +20 -198
- package/src/standalone/session-service.mjs +632 -7
- package/src/tui/dist/index.mjs +24 -1
- package/src/tui/lib/voice-setup.mjs +4 -1
- package/src/tui/session/context-state.mjs +2 -13
- package/src/tui/session/goal-continuation.mjs +38 -7
- package/src/tui/session/session-api-ext.mjs +9 -0
- package/src/tui/session/session-api.mjs +15 -1
- package/src/tui/session/session-flow.mjs +25 -1
- package/src/tui/session/turn.mjs +88 -16
- package/src/tui/session-local.mjs +25 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -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-trace-timing.test.mjs +0 -57
- 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/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 -142
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -153
- 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 -190
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -235
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -176
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -161
- package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -115
- package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -245
- package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
- 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 -70
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -457
- 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-summary-reader.test.mjs +0 -583
- 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/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/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 -123
- 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/global-extensions.test.mjs +0 -28
- package/src/session-runtime/goal-runtime.test.mjs +0 -108
- package/src/session-runtime/lifecycle-api.test.mjs +0 -21
- 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 -37
- package/src/session-runtime/resource-api-global.test.mjs +0 -69
- 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 -331
- 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 -63
- 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 -83
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
- package/src/standalone/session-runtime-shard-host.test.mjs +0 -569
- package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
- 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/completion-card-restore.test.mjs +0 -111
- package/src/tui/session/goal-continuation.test.mjs +0 -84
- 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 -108
- 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
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
// Session tool schema resolution
|
|
2
|
-
//
|
|
3
|
-
// same dedup, same fail-closed permission intersection.
|
|
1
|
+
// Session tool schema resolution. Lead and Agent share one full-capability
|
|
2
|
+
// catalog; Agent removes only the recursive `agent` control tool.
|
|
4
3
|
import { getMcpTools } from '../../mcp/client.mjs';
|
|
5
4
|
import { getInternalTools } from '../../internal-tools.mjs';
|
|
6
5
|
import { BUILTIN_TOOLS } from '../../tools/builtin/builtin-tools.mjs';
|
|
7
6
|
import { PATCH_TOOL_DEFS } from '../../tools/patch-tool-defs.mjs';
|
|
8
7
|
import { CODE_GRAPH_TOOL_DEFS } from '../../tools/code-graph-tool-defs.mjs';
|
|
9
8
|
import { buildSkillToolDefs } from '../../context/collect.mjs';
|
|
10
|
-
import { getHiddenAgent } from '../../internal-agents.mjs';
|
|
11
9
|
import { filterModelEditTools } from '../../../../shared/edit-tool-dialect.mjs';
|
|
12
10
|
|
|
13
11
|
// Merge externally-connected MCP tools with the plugin's in-process tools
|
|
@@ -81,101 +79,6 @@ const READONLY_TOOL_NAMES = new Set([
|
|
|
81
79
|
'read',
|
|
82
80
|
]);
|
|
83
81
|
|
|
84
|
-
const AGENT_STRING_PERMISSION_READ_ALLOW = Object.freeze([
|
|
85
|
-
'code_graph',
|
|
86
|
-
'find',
|
|
87
|
-
'glob',
|
|
88
|
-
'list',
|
|
89
|
-
'grep',
|
|
90
|
-
'read',
|
|
91
|
-
// shell/task: verifying read-role agents (reviewer) must run their own
|
|
92
|
-
// verification (tests/repro). task is required because shell's
|
|
93
|
-
// auto-background transition settles with a bare jobId — agent sessions
|
|
94
|
-
// get no completion notification, so `task wait/read` is the only way to
|
|
95
|
-
// collect a long-running result. apply_patch stays excluded: read roles
|
|
96
|
-
// keep the no-edit contract.
|
|
97
|
-
'shell',
|
|
98
|
-
'task',
|
|
99
|
-
'web_search',
|
|
100
|
-
'web_fetch',
|
|
101
|
-
'Skill',
|
|
102
|
-
]);
|
|
103
|
-
|
|
104
|
-
// Retrieval roles never self-verify, so they keep the historical no-shell
|
|
105
|
-
// read surface. Covers hidden retrieval agents and invokedBy wrapper roles,
|
|
106
|
-
// the same set
|
|
107
|
-
// pre-dispatch-deny treats as recursive wrappers. Only verifying read roles
|
|
108
|
-
// (reviewer) get shell/task.
|
|
109
|
-
const AGENT_STRING_PERMISSION_READ_RETRIEVAL_ALLOW = Object.freeze(
|
|
110
|
-
AGENT_STRING_PERMISSION_READ_ALLOW.filter((n) => n !== 'shell' && n !== 'task'),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
function isRetrievalToolRole(agent) {
|
|
114
|
-
if (!agent) return false;
|
|
115
|
-
return getHiddenAgent(agent)?.kind === 'retrieval';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function stringToolPermissionAllowList(toolPermission, { retrieval = false } = {}) {
|
|
119
|
-
if (toolPermission === 'read') {
|
|
120
|
-
return retrieval ? AGENT_STRING_PERMISSION_READ_RETRIEVAL_ALLOW : AGENT_STRING_PERMISSION_READ_ALLOW;
|
|
121
|
-
}
|
|
122
|
-
if (toolPermission === 'read-write') return AGENT_STRING_PERMISSION_READ_WRITE_ALLOW;
|
|
123
|
-
if (toolPermission === 'none') return [];
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Read-write agent bundle: full edit surface INCLUDING shell/task so
|
|
128
|
-
// write-role agents can run their own verification (build/test) without
|
|
129
|
-
// bouncing it back to Lead. Deploy/ship remains a workflow-level rule,
|
|
130
|
-
// not a tool-surface restriction.
|
|
131
|
-
const AGENT_STRING_PERMISSION_READ_WRITE_ALLOW = Object.freeze([
|
|
132
|
-
'code_graph',
|
|
133
|
-
'find',
|
|
134
|
-
'glob',
|
|
135
|
-
'list',
|
|
136
|
-
'grep',
|
|
137
|
-
'read',
|
|
138
|
-
'edit',
|
|
139
|
-
'apply_patch',
|
|
140
|
-
'git',
|
|
141
|
-
'git_stage',
|
|
142
|
-
'shell',
|
|
143
|
-
'task',
|
|
144
|
-
'web_search',
|
|
145
|
-
'web_fetch',
|
|
146
|
-
'Skill',
|
|
147
|
-
]);
|
|
148
|
-
|
|
149
|
-
export function applyToolPermissionNarrowing(tools, toolPermission, warnRole = null) {
|
|
150
|
-
if (toolPermission === 'none') return [];
|
|
151
|
-
const allowList = stringToolPermissionAllowList(toolPermission, { retrieval: isRetrievalToolRole(warnRole) });
|
|
152
|
-
if (allowList) {
|
|
153
|
-
const allowSet = new Set(allowList.map((n) => String(n).toLowerCase()));
|
|
154
|
-
return tools.filter((t) => allowSet.has(String(t?.name || '').toLowerCase()));
|
|
155
|
-
}
|
|
156
|
-
if (toolPermission && typeof toolPermission === 'object') {
|
|
157
|
-
const allowSet = Array.isArray(toolPermission.allow) && toolPermission.allow.length > 0
|
|
158
|
-
? new Set(toolPermission.allow.map(n => String(n).toLowerCase()))
|
|
159
|
-
: null;
|
|
160
|
-
const denySet = Array.isArray(toolPermission.deny) && toolPermission.deny.length > 0
|
|
161
|
-
? new Set(toolPermission.deny.map(n => String(n).toLowerCase()))
|
|
162
|
-
: null;
|
|
163
|
-
if (allowSet || denySet) {
|
|
164
|
-
const filtered = tools.filter(t => {
|
|
165
|
-
const name = String(t?.name || '').toLowerCase();
|
|
166
|
-
if (denySet && denySet.has(name)) return false;
|
|
167
|
-
if (allowSet && !allowSet.has(name)) return false;
|
|
168
|
-
return true;
|
|
169
|
-
});
|
|
170
|
-
if (filtered.length === 0) {
|
|
171
|
-
process.stderr.write(`[session] WARN: role permission intersection produced 0 tools — failing closed (role=${warnRole || 'unknown'})\n`);
|
|
172
|
-
}
|
|
173
|
-
return filtered;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return tools;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
82
|
export function finalizeSessionToolList(tools, {
|
|
180
83
|
schemaAllowedTools = null,
|
|
181
84
|
disallowedTools = null,
|
|
@@ -194,7 +97,7 @@ export function finalizeSessionToolList(tools, {
|
|
|
194
97
|
out = out.filter(t => !denySet.has(String(t?.name || '').toLowerCase()));
|
|
195
98
|
}
|
|
196
99
|
if (ownerIsAgent) {
|
|
197
|
-
out = out.filter(t =>
|
|
100
|
+
out = out.filter(t => String(t?.name || '').toLowerCase() !== 'agent');
|
|
198
101
|
out = orderSessionTools(out);
|
|
199
102
|
}
|
|
200
103
|
return out;
|
|
@@ -312,16 +215,7 @@ function _computeBaseTools(toolSpec, mcp, skillTools, { ownerIsAgentSession = fa
|
|
|
312
215
|
return orderSessionTools(_dedupByName([...mcp, ...skillTools]));
|
|
313
216
|
case 'readonly': {
|
|
314
217
|
const readTools = ALL_BUILTIN_SESSION_TOOLS.filter(t => READONLY_TOOL_NAMES.has(t.name));
|
|
315
|
-
|
|
316
|
-
// their base bundle carries shell/task defs. The 'read' permission
|
|
317
|
-
// allowlist (AGENT_STRING_PERMISSION_READ_ALLOW) is a pure FILTER —
|
|
318
|
-
// it can only keep tools already assembled here, so without this the
|
|
319
|
-
// allowlist's shell/task entries were dead letters. Non-agent
|
|
320
|
-
// 'readonly' profiles stay strictly read-only.
|
|
321
|
-
const verifyTools = ownerIsAgentSession
|
|
322
|
-
? ALL_BUILTIN_SESSION_TOOLS.filter(t => t.name === 'shell' || t.name === 'task')
|
|
323
|
-
: [];
|
|
324
|
-
return orderSessionTools(_dedupByName([...readTools, ...verifyTools, ...mcp, ...skillTools]));
|
|
218
|
+
return orderSessionTools(_dedupByName([...readTools, ...mcp, ...skillTools]));
|
|
325
219
|
}
|
|
326
220
|
case 'full':
|
|
327
221
|
default:
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
// before the provider ever runs, and store-summary-reader
|
|
16
16
|
// keeps probing this exact path to detect a live turn.
|
|
17
17
|
// <session>.jsonl append-only JSONL deltas — NEW/CHANGED turn messages (by
|
|
18
|
-
// turn-relative index),
|
|
19
|
-
//
|
|
20
|
-
//
|
|
18
|
+
// turn-relative index), a one-time full replacement when
|
|
19
|
+
// compaction removes the opening prompt, APPENDED partial
|
|
20
|
+
// text/reasoning and small interruption-state transitions.
|
|
21
|
+
// A normal flush costs O(new bytes), never O(turn).
|
|
21
22
|
// Appends ride a bounded coalescing lane (one in-flight write, one pending
|
|
22
23
|
// buffer, synchronous drain above the byte ceiling) so the engine thread never
|
|
23
24
|
// blocks on disk and the queue can never grow without limit.
|
|
@@ -434,7 +435,8 @@ export function replayTurnJournal(header) {
|
|
|
434
435
|
} catch {
|
|
435
436
|
return header;
|
|
436
437
|
}
|
|
437
|
-
|
|
438
|
+
let messages = header.turnMessages.slice();
|
|
439
|
+
let fullTranscript = header.fullTranscript === true;
|
|
438
440
|
const state = interruptionStateFromSnapshot(header.interruption);
|
|
439
441
|
let updatedAt = Number(header.updatedAt) || 0;
|
|
440
442
|
let head = false;
|
|
@@ -469,6 +471,9 @@ export function replayTurnJournal(header) {
|
|
|
469
471
|
if (Number.isInteger(index) && index >= 0 && index <= messages.length) {
|
|
470
472
|
messages[index] = record.m;
|
|
471
473
|
}
|
|
474
|
+
} else if (record.t === 'full' && Array.isArray(record.ms)) {
|
|
475
|
+
messages = record.ms.slice();
|
|
476
|
+
fullTranscript = true;
|
|
472
477
|
} else if (record.t === 'len') {
|
|
473
478
|
const next = Number(record.n);
|
|
474
479
|
if (Number.isInteger(next) && next >= 0 && next <= messages.length) {
|
|
@@ -484,6 +489,7 @@ export function replayTurnJournal(header) {
|
|
|
484
489
|
return {
|
|
485
490
|
...header,
|
|
486
491
|
turnMessages: messages,
|
|
492
|
+
...(fullTranscript ? { fullTranscript: true } : {}),
|
|
487
493
|
interruption: interruptionSnapshotFromState(state),
|
|
488
494
|
updatedAt,
|
|
489
495
|
};
|
|
@@ -496,6 +502,7 @@ export function replayTurnJournal(header) {
|
|
|
496
502
|
*/
|
|
497
503
|
export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
498
504
|
let journaled = [];
|
|
505
|
+
let fullTranscript = false;
|
|
499
506
|
let cursor = null;
|
|
500
507
|
// Per-turn record sequence. It is assigned where records are produced, so
|
|
501
508
|
// the number line is exactly the enqueue order the write lane preserves.
|
|
@@ -513,7 +520,8 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
|
513
520
|
seed(turnOutgoing, currentUserContent, interruption) {
|
|
514
521
|
const source = Array.isArray(turnOutgoing) ? turnOutgoing : [];
|
|
515
522
|
const start = findTurnStart(source, currentUserContent);
|
|
516
|
-
|
|
523
|
+
fullTranscript = start < 0;
|
|
524
|
+
journaled = fullTranscript ? source.slice() : source.slice(start);
|
|
517
525
|
const seeded = typeof interruption?.journalDelta === 'function'
|
|
518
526
|
? interruption.journalDelta(null)
|
|
519
527
|
: null;
|
|
@@ -523,23 +531,39 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
|
523
531
|
if (seeded?.changed) openLines.push(line({ t: 'x', d: seeded.delta, at: Date.now() }));
|
|
524
532
|
return {
|
|
525
533
|
turnMessages: journaled.map(checkpointMessage),
|
|
534
|
+
fullTranscript,
|
|
526
535
|
openLines,
|
|
527
536
|
};
|
|
528
537
|
},
|
|
529
538
|
encode({ turnOutgoing, currentUserContent, interruption }) {
|
|
530
539
|
const source = Array.isArray(turnOutgoing) ? turnOutgoing : [];
|
|
531
540
|
const start = findTurnStart(source, currentUserContent);
|
|
532
|
-
const length = start >= 0 ? source.length - start : 0;
|
|
533
541
|
const records = [];
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
journaled
|
|
542
|
-
records.push({
|
|
542
|
+
if (!fullTranscript && start < 0) {
|
|
543
|
+
// Pre-send compaction may replace the whole history, including
|
|
544
|
+
// the user message that defined our turn-relative origin. The
|
|
545
|
+
// replacement is normally small; persist it once so reconnect
|
|
546
|
+
// and crash recovery never splice an empty turn onto the stale
|
|
547
|
+
// pre-compaction prefix.
|
|
548
|
+
fullTranscript = true;
|
|
549
|
+
journaled = source.slice();
|
|
550
|
+
records.push({
|
|
551
|
+
t: 'full',
|
|
552
|
+
ms: journaled.map(checkpointMessage),
|
|
553
|
+
});
|
|
554
|
+
} else {
|
|
555
|
+
const offset = fullTranscript ? 0 : start;
|
|
556
|
+
const length = offset >= 0 ? source.length - offset : 0;
|
|
557
|
+
for (let index = 0; index < length; index += 1) {
|
|
558
|
+
const raw = source[offset + index];
|
|
559
|
+
if (journaled[index] === raw) continue;
|
|
560
|
+
journaled[index] = raw;
|
|
561
|
+
records.push({ t: 'm', i: index, m: checkpointMessage(raw) });
|
|
562
|
+
}
|
|
563
|
+
if (journaled.length > length) {
|
|
564
|
+
journaled.length = length;
|
|
565
|
+
records.push({ t: 'len', n: length });
|
|
566
|
+
}
|
|
543
567
|
}
|
|
544
568
|
if (typeof interruption?.journalDelta === 'function') {
|
|
545
569
|
const next = interruption.journalDelta(cursor);
|
|
@@ -40,6 +40,9 @@ export function projectTurnCheckpointMessages(session, checkpoint) {
|
|
|
40
40
|
if (checkpointGeneration !== sessionGeneration
|
|
41
41
|
|| (markerToken && !markerMatches)
|
|
42
42
|
|| (!markerToken && !checkpointIsNewer)) return source;
|
|
43
|
+
if (checkpoint.fullTranscript === true) {
|
|
44
|
+
return checkpoint.turnMessages.slice();
|
|
45
|
+
}
|
|
43
46
|
let start = -1;
|
|
44
47
|
for (let index = source.length - 1; index >= 0; index -= 1) {
|
|
45
48
|
if (matchingUserMessage(source[index], checkpoint.currentUserContent)) {
|
|
@@ -80,6 +83,7 @@ export function createTurnCheckpointRecorder({ sessionId, generation, turnToken,
|
|
|
80
83
|
startedAt,
|
|
81
84
|
currentUserContent,
|
|
82
85
|
turnMessages: seed.turnMessages,
|
|
86
|
+
...(seed.fullTranscript ? { fullTranscript: true } : {}),
|
|
83
87
|
interruption: typeof interruption?.snapshot === 'function'
|
|
84
88
|
? interruption.snapshot()
|
|
85
89
|
: emptyInterruptionSnapshot(),
|
|
@@ -196,10 +200,16 @@ export function recoverTurnCheckpoint(session) {
|
|
|
196
200
|
});
|
|
197
201
|
const current = Array.isArray(session.messages) ? session.messages : [];
|
|
198
202
|
const start = findTurnStart(current, checkpoint.currentUserContent);
|
|
199
|
-
session.messages =
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
session.messages = checkpoint.fullTranscript === true
|
|
204
|
+
? finalized.messages
|
|
205
|
+
: [
|
|
206
|
+
...(start >= 0 ? current.slice(0, start) : current),
|
|
207
|
+
...finalized.messages,
|
|
208
|
+
];
|
|
209
|
+
// Keep the last provider reading across restart. Pressure resolution
|
|
210
|
+
// validates its message-prefix signature against this recovered transcript:
|
|
211
|
+
// a matching durable prefix stays authoritative, while any rewritten
|
|
212
|
+
// prefix falls back to a local estimate until the next provider response.
|
|
203
213
|
delete session.activeTurnCheckpoint;
|
|
204
214
|
delete session.providerState;
|
|
205
215
|
delete session._providerPrefixGuardState;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
shouldCompactForSession,
|
|
13
13
|
countPrunedToolOutputs,
|
|
14
14
|
rememberCompactTelemetry,
|
|
15
|
+
recordContextUsageSnapshot,
|
|
15
16
|
emitCompactEvent,
|
|
16
17
|
compactEventType,
|
|
17
18
|
resolveSemanticSummaryModel,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
} from './compact.mjs';
|
|
26
27
|
import { runRecallFastTrackCompact } from './loop/recall-fasttrack.mjs';
|
|
27
28
|
import { estimateMessagesTokensSafe } from './loop/compact-debug.mjs';
|
|
29
|
+
import { hasPendingNativeTokenCounts, withSyncTokenCounts } from './context-utils.mjs';
|
|
28
30
|
import { messagesArrayChanged } from './loop/tool-helpers.mjs';
|
|
29
31
|
import { normalizeUsage, addUsage } from './loop/usage.mjs';
|
|
30
32
|
import { agentContextOverflowError } from './loop/context-overflow.mjs';
|
|
@@ -69,23 +71,46 @@ export async function runPreSendCompactPass(state) {
|
|
|
69
71
|
// a best-effort JSON.stringify length — close enough to the
|
|
70
72
|
// payload we hand the provider for prefix-cache analysis.
|
|
71
73
|
const beforeCount = messages.length;
|
|
72
|
-
|
|
74
|
+
let messageTokensEst = estimateMessagesTokensSafe(messages);
|
|
73
75
|
const reactivePending = reactiveOverflowRetryPending === true;
|
|
74
|
-
|
|
76
|
+
let pressureTokens = compactionTelemetryPressureTokens(messageTokensEst, compactPolicy, {
|
|
75
77
|
reactivePending,
|
|
76
78
|
messages,
|
|
77
79
|
sessionRef,
|
|
78
80
|
});
|
|
79
|
-
|
|
80
|
-
// Reactive overflow recovery floors it at the trigger so the gauge,
|
|
81
|
-
// telemetry, and forced compact still describe the same event.
|
|
82
|
-
const gaugeBeforeTokens = pressureTokens;
|
|
83
|
-
const shouldCompact = shouldCompactForSession(messageTokensEst, compactPolicy, {
|
|
81
|
+
let shouldCompact = shouldCompactForSession(messageTokensEst, compactPolicy, {
|
|
84
82
|
forceReactive: reactivePending,
|
|
85
83
|
messages,
|
|
86
84
|
sessionRef,
|
|
87
85
|
pressureTokens,
|
|
88
86
|
});
|
|
87
|
+
// Large fresh strings (>=32KB) may still be counting on the native
|
|
88
|
+
// token server; until the exact count lands, estimateTokens serves
|
|
89
|
+
// the CONSERVATIVE legacy heuristic. Never fire a compaction off
|
|
90
|
+
// that transient overshoot: when the decision says compact while
|
|
91
|
+
// counts are pending, redo the estimate synchronously (exact BPE)
|
|
92
|
+
// and re-decide. The fast path pays nothing; a genuine trigger
|
|
93
|
+
// pays one exact encode whose result seeds the shared cache.
|
|
94
|
+
if (shouldCompact && !reactivePending && hasPendingNativeTokenCounts()) {
|
|
95
|
+
withSyncTokenCounts(() => {
|
|
96
|
+
messageTokensEst = estimateMessagesTokensSafe(messages);
|
|
97
|
+
pressureTokens = compactionTelemetryPressureTokens(messageTokensEst, compactPolicy, {
|
|
98
|
+
reactivePending,
|
|
99
|
+
messages,
|
|
100
|
+
sessionRef,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
shouldCompact = shouldCompactForSession(messageTokensEst, compactPolicy, {
|
|
104
|
+
forceReactive: reactivePending,
|
|
105
|
+
messages,
|
|
106
|
+
sessionRef,
|
|
107
|
+
pressureTokens,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// This is the exact canonical value used by the decision above.
|
|
111
|
+
// Reactive overflow recovery floors it at the trigger so the gauge,
|
|
112
|
+
// telemetry, and forced compact still describe the same event.
|
|
113
|
+
const gaugeBeforeTokens = pressureTokens;
|
|
89
114
|
// Gauge sync (user: 컴팩트될 때 컨텍스트 표기량이 달랐다). The host
|
|
90
115
|
// refreshes its context readout on provider usage deltas and turn
|
|
91
116
|
// end, so a tool batch that pushed the transcript over the trigger
|
|
@@ -482,7 +507,7 @@ export async function runPreSendCompactPass(state) {
|
|
|
482
507
|
afterTokens,
|
|
483
508
|
messageTokensEst,
|
|
484
509
|
pressureTokens,
|
|
485
|
-
compactChanged,
|
|
510
|
+
compactChanged: compactChanged || summaryChanged,
|
|
486
511
|
semanticCompact: semanticCompactResult?.semantic === true,
|
|
487
512
|
semanticError: semanticCompactError?.message || null,
|
|
488
513
|
recallFastTrack: recallFastTrackResult?.recallFastTrack === true,
|
|
@@ -491,6 +516,14 @@ export async function runPreSendCompactPass(state) {
|
|
|
491
516
|
pruneCount,
|
|
492
517
|
durationMs: compactDurationMs,
|
|
493
518
|
});
|
|
519
|
+
if (compactChanged || summaryChanged || pruneCount > 0) {
|
|
520
|
+
recordContextUsageSnapshot(sessionRef, compactPolicy, {
|
|
521
|
+
messages,
|
|
522
|
+
usedTokens: afterTokens,
|
|
523
|
+
messageTokensEst: afterMessageTokensEst,
|
|
524
|
+
source: 'post_compact',
|
|
525
|
+
});
|
|
526
|
+
}
|
|
494
527
|
let afterBytes = null;
|
|
495
528
|
try { afterBytes = Buffer.byteLength(JSON.stringify(messages), 'utf8'); } catch { afterBytes = null; }
|
|
496
529
|
traceAgentCompact({
|
|
@@ -15,13 +15,63 @@ function digest(value) {
|
|
|
15
15
|
return createHash('sha256').update(encoded).digest('hex');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function
|
|
18
|
+
function cacheRelevantRequestPrefix(requestPrefix, provider) {
|
|
19
|
+
const prefix = requestPrefix && typeof requestPrefix === 'object'
|
|
20
|
+
? requestPrefix
|
|
21
|
+
: {};
|
|
22
|
+
const anthropic = /^(?:anthropic|anthropic-oauth)$/i.test(String(provider || ''));
|
|
23
|
+
const cacheRelevantTools = (tools) => (
|
|
24
|
+
Array.isArray(tools)
|
|
25
|
+
? tools.filter((tool) => !anthropic
|
|
26
|
+
|| (tool?.deferLoading !== true && tool?.defer_loading !== true))
|
|
27
|
+
: []
|
|
28
|
+
);
|
|
29
|
+
return {
|
|
30
|
+
...prefix,
|
|
31
|
+
tools: cacheRelevantTools(prefix.tools),
|
|
32
|
+
nativeTools: cacheRelevantTools(prefix.nativeTools),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function snapshot(messages, requestPrefix, options = {}) {
|
|
37
|
+
const provider = String(options.provider || '');
|
|
38
|
+
const model = String(options.model || '');
|
|
39
|
+
const relevantPrefix = cacheRelevantRequestPrefix(requestPrefix, provider);
|
|
19
40
|
return {
|
|
20
41
|
messageHashes: messages.map(digest),
|
|
21
|
-
requestPrefixHash: digest(requestPrefix),
|
|
42
|
+
requestPrefixHash: digest({ provider, model, requestPrefix: relevantPrefix }),
|
|
43
|
+
provider,
|
|
44
|
+
model,
|
|
45
|
+
toolSchemaHash: digest(relevantPrefix.tools),
|
|
46
|
+
nativeToolSchemaHash: digest(relevantPrefix.nativeTools),
|
|
22
47
|
};
|
|
23
48
|
}
|
|
24
49
|
|
|
50
|
+
function firstChangedMessageIndex(previousHashes, nextHashes) {
|
|
51
|
+
const limit = Math.min(previousHashes.length, nextHashes.length);
|
|
52
|
+
for (let index = 0; index < limit; index += 1) {
|
|
53
|
+
if (previousHashes[index] !== nextHashes[index]) return index;
|
|
54
|
+
}
|
|
55
|
+
return nextHashes.length < previousHashes.length ? nextHashes.length : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function notifyCacheBreak(options, details) {
|
|
59
|
+
try { options.onCacheBreak?.(details); } catch { /* observability only */ }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function requestPrefixChangeReason(previous, next) {
|
|
63
|
+
if (previous.provider !== undefined && previous.provider !== next.provider) return 'provider_changed';
|
|
64
|
+
if (previous.model !== undefined && previous.model !== next.model) return 'model_changed';
|
|
65
|
+
if (previous.toolSchemaHash !== undefined && previous.toolSchemaHash !== next.toolSchemaHash) {
|
|
66
|
+
return 'tool_schema_changed';
|
|
67
|
+
}
|
|
68
|
+
if (previous.nativeToolSchemaHash !== undefined
|
|
69
|
+
&& previous.nativeToolSchemaHash !== next.nativeToolSchemaHash) {
|
|
70
|
+
return 'native_tool_schema_changed';
|
|
71
|
+
}
|
|
72
|
+
return 'request_properties_changed';
|
|
73
|
+
}
|
|
74
|
+
|
|
25
75
|
export class ProviderPrefixMutationError extends Error {
|
|
26
76
|
constructor(message, details = {}) {
|
|
27
77
|
super(message);
|
|
@@ -36,24 +86,91 @@ export function isCompactionPrefixReset(intent) {
|
|
|
36
86
|
}
|
|
37
87
|
|
|
38
88
|
export function prepareProviderPrefixGuard(previous, messages, requestPrefix, options = {}) {
|
|
39
|
-
const next = snapshot(Array.isArray(messages) ? messages : [], requestPrefix);
|
|
40
|
-
if (!previous
|
|
89
|
+
const next = snapshot(Array.isArray(messages) ? messages : [], requestPrefix, options);
|
|
90
|
+
if (!previous) return next;
|
|
91
|
+
|
|
92
|
+
const previousHashes = Array.isArray(previous.messageHashes) ? previous.messageHashes : [];
|
|
93
|
+
const nextHashes = next.messageHashes;
|
|
94
|
+
const changedIndex = firstChangedMessageIndex(previousHashes, nextHashes);
|
|
95
|
+
const requestPrefixChanged = previous.requestPrefixHash !== next.requestPrefixHash;
|
|
96
|
+
if (isCompactionPrefixReset(options.cacheBreakIntent)) {
|
|
97
|
+
if (changedIndex !== null || requestPrefixChanged) {
|
|
98
|
+
notifyCacheBreak(options, {
|
|
99
|
+
classification: 'intentional',
|
|
100
|
+
reason: String(options.cacheBreakIntent),
|
|
101
|
+
source: 'compaction',
|
|
102
|
+
provider: options.provider || null,
|
|
103
|
+
model: options.model || null,
|
|
104
|
+
index: changedIndex,
|
|
105
|
+
previousCount: previousHashes.length,
|
|
106
|
+
nextCount: nextHashes.length,
|
|
107
|
+
previousHash: changedIndex === null ? null : previousHashes[changedIndex],
|
|
108
|
+
nextHash: changedIndex === null ? null : nextHashes[changedIndex],
|
|
109
|
+
previousRequestPrefixHash: previous.requestPrefixHash,
|
|
110
|
+
nextRequestPrefixHash: next.requestPrefixHash,
|
|
111
|
+
requestPrefixChanged,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return next;
|
|
115
|
+
}
|
|
41
116
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
117
|
+
if (nextHashes.length < previousHashes.length) {
|
|
118
|
+
const details = {
|
|
119
|
+
classification: 'unexpected',
|
|
120
|
+
reason: 'history_shrink',
|
|
44
121
|
provider: options.provider || null,
|
|
122
|
+
model: options.model || null,
|
|
45
123
|
kind: 'history_shrink',
|
|
46
|
-
|
|
124
|
+
source: options.mutationSource || null,
|
|
125
|
+
index: nextHashes.length,
|
|
126
|
+
previousCount: previousHashes.length,
|
|
127
|
+
nextCount: nextHashes.length,
|
|
128
|
+
previousHash: previousHashes[nextHashes.length] || null,
|
|
129
|
+
nextHash: null,
|
|
130
|
+
previousRequestPrefixHash: previous.requestPrefixHash,
|
|
131
|
+
nextRequestPrefixHash: next.requestPrefixHash,
|
|
132
|
+
requestPrefixChanged,
|
|
133
|
+
};
|
|
134
|
+
notifyCacheBreak(options, details);
|
|
135
|
+
throw new ProviderPrefixMutationError('provider message history shrank outside compaction', details);
|
|
47
136
|
}
|
|
48
|
-
for (let index = 0; index <
|
|
49
|
-
if (
|
|
50
|
-
|
|
137
|
+
for (let index = 0; index < previousHashes.length; index += 1) {
|
|
138
|
+
if (previousHashes[index] === nextHashes[index]) continue;
|
|
139
|
+
const details = {
|
|
140
|
+
classification: 'unexpected',
|
|
141
|
+
reason: 'message_prefix',
|
|
51
142
|
provider: options.provider || null,
|
|
143
|
+
model: options.model || null,
|
|
52
144
|
kind: 'message_prefix',
|
|
145
|
+
source: options.mutationSource || null,
|
|
53
146
|
index,
|
|
54
|
-
|
|
147
|
+
previousCount: previousHashes.length,
|
|
148
|
+
nextCount: nextHashes.length,
|
|
149
|
+
previousHash: previousHashes[index],
|
|
150
|
+
nextHash: nextHashes[index],
|
|
151
|
+
previousRequestPrefixHash: previous.requestPrefixHash,
|
|
152
|
+
nextRequestPrefixHash: next.requestPrefixHash,
|
|
153
|
+
requestPrefixChanged,
|
|
154
|
+
};
|
|
155
|
+
notifyCacheBreak(options, details);
|
|
156
|
+
throw new ProviderPrefixMutationError('provider message prefix changed outside compaction', details);
|
|
55
157
|
}
|
|
56
|
-
if (
|
|
158
|
+
if (requestPrefixChanged) {
|
|
159
|
+
notifyCacheBreak(options, {
|
|
160
|
+
classification: 'intentional',
|
|
161
|
+
reason: requestPrefixChangeReason(previous, next),
|
|
162
|
+
source: 'request_configuration',
|
|
163
|
+
provider: options.provider || null,
|
|
164
|
+
model: options.model || null,
|
|
165
|
+
index: null,
|
|
166
|
+
previousCount: previousHashes.length,
|
|
167
|
+
nextCount: nextHashes.length,
|
|
168
|
+
previousHash: null,
|
|
169
|
+
nextHash: null,
|
|
170
|
+
previousRequestPrefixHash: previous.requestPrefixHash,
|
|
171
|
+
nextRequestPrefixHash: next.requestPrefixHash,
|
|
172
|
+
requestPrefixChanged: true,
|
|
173
|
+
});
|
|
57
174
|
// Tool schemas are request metadata, not durable conversation state.
|
|
58
175
|
// App updates may change them between turns, so rebaseline the provider
|
|
59
176
|
// cache prefix after the transcript itself has passed integrity checks.
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
persistenceMessagesForConfirmedImageRejection,
|
|
21
21
|
promptHasInlineImages,
|
|
22
22
|
shouldStripImagesForRetry,
|
|
23
|
-
|
|
23
|
+
stripInlineImagesFromLatestTurn,
|
|
24
24
|
} from './image-strip-recovery.mjs';
|
|
25
25
|
import { setTimeout as sleepMs } from 'timers/promises';
|
|
26
26
|
import { readStreamOutcome } from '../providers/lib/stream-outcome.mjs';
|
|
@@ -508,7 +508,7 @@ export async function sendWithRecovery(ctx) {
|
|
|
508
508
|
&& relayWitness.toolCallsDispatched === 0
|
|
509
509
|
&& (outcome.replaySafe === true || await retractExposedTextForReplay())
|
|
510
510
|
) {
|
|
511
|
-
const stripped =
|
|
511
|
+
const stripped = stripInlineImagesFromLatestTurn(recoveryMessages);
|
|
512
512
|
if (stripped.stripped > 0) {
|
|
513
513
|
try {
|
|
514
514
|
process.stderr.write(
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
import { _saveAsyncQueued, _saveWorkerPending } from './save-worker.mjs';
|
|
35
35
|
import { STORED_SESSION_UNREADABLE, _ensureLifecycleFields, _storedSessionFromFile } from './serialize.mjs';
|
|
36
36
|
import { _savePending, deleteSession, markSessionClosed } from '../store.mjs';
|
|
37
|
+
import { isOrdinarySession } from '../store-summary-visibility.mjs';
|
|
37
38
|
|
|
38
39
|
// Disk mtime of the summary index when the in-memory cache was last refreshed
|
|
39
40
|
// from it — the cross-process staleness detector.
|
|
@@ -98,9 +99,15 @@ export function listStoredSessions(options = {}) {
|
|
|
98
99
|
if (/^[A-Za-z0-9_-]+$/.test(storageId)) invalidStorageIds.add(storageId);
|
|
99
100
|
}
|
|
100
101
|
const stored = [...sessionsById.values()];
|
|
101
|
-
|
|
102
|
+
const listed = options.includeLive === true
|
|
102
103
|
? _withUnpersistedSessions(stored, invalidStorageIds)
|
|
103
104
|
: stored.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
105
|
+
// This full-record API is the ordinary catalog boundary. Agent discovery
|
|
106
|
+
// uses the durable summary/worker indexes instead; callers that explicitly
|
|
107
|
+
// own an Agent surface can request the unfiltered internal rows.
|
|
108
|
+
return options.includeAgentOnly === true
|
|
109
|
+
? listed
|
|
110
|
+
: listed.filter(isOrdinarySession);
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
function _withUnpersistedSessions(stored, invalidStorageIds = new Set()) {
|
|
@@ -139,11 +139,20 @@ let _faultSyncedKey = null;
|
|
|
139
139
|
// optimistic summary row is published: foreign/ambiguous records must never be
|
|
140
140
|
// cached as owned, not even transiently.
|
|
141
141
|
let _writeAuthorityCheck = null;
|
|
142
|
+
let _liveSessionPublisher = null;
|
|
142
143
|
|
|
143
144
|
export function _setSessionWriteAuthorityCheck(check) {
|
|
144
145
|
_writeAuthorityCheck = typeof check === 'function' ? check : null;
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
export function _setLiveSessionPublisher(publish) {
|
|
149
|
+
_liveSessionPublisher = typeof publish === 'function' ? publish : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function _publishAdmittedLiveSession(session) {
|
|
153
|
+
try { _liveSessionPublisher?.(session); } catch { /* observers never affect persistence */ }
|
|
154
|
+
}
|
|
155
|
+
|
|
147
156
|
function _refuseUnownedWrite(id) {
|
|
148
157
|
if (!_writeAuthorityCheck || !id) return null;
|
|
149
158
|
let refusal = null;
|
|
@@ -528,6 +537,7 @@ export function saveSessionAsync(session, opts, options = {}) {
|
|
|
528
537
|
// Pin the live snapshot BEFORE anything can fail: every failure below
|
|
529
538
|
// leaves this process's memory as the only copy of the newest turn.
|
|
530
539
|
setLiveSession(session);
|
|
540
|
+
if (options.publishLive !== false) _publishAdmittedLiveSession(session);
|
|
531
541
|
summaryVersion = _cacheSessionSummary(session);
|
|
532
542
|
// The guard carries this snapshot's epoch across the thread boundary,
|
|
533
543
|
// so the worker's own commit is fenced against a newer landed write.
|
|
@@ -601,6 +611,7 @@ export function saveSessionAsyncDeferred(session, opts) {
|
|
|
601
611
|
try {
|
|
602
612
|
_ensureLifecycleFields(session);
|
|
603
613
|
setLiveSession(session);
|
|
614
|
+
_publishAdmittedLiveSession(session);
|
|
604
615
|
_cacheSessionSummary(session);
|
|
605
616
|
} catch (err) {
|
|
606
617
|
_failWorkerWrite(session?.id, null, [], err, epoch, null, err?.attemptSnapshot ?? null);
|
|
@@ -625,7 +636,10 @@ export function saveSessionAsyncDeferred(session, opts) {
|
|
|
625
636
|
// The promoted save takes its own stamp.
|
|
626
637
|
_releaseSessionIncarnation(pending.incarnation);
|
|
627
638
|
try {
|
|
628
|
-
saveSessionAsync(pending.session, pending.opts, {
|
|
639
|
+
saveSessionAsync(pending.session, pending.opts, {
|
|
640
|
+
epoch: pending.epoch,
|
|
641
|
+
publishLive: false,
|
|
642
|
+
}).then(resolve, reject);
|
|
629
643
|
} catch (err) {
|
|
630
644
|
_failWorkerWrite(session?.id, null, [], err, epoch, null, err?.attemptSnapshot ?? null);
|
|
631
645
|
reject(err);
|