@cortexkit/opencode-magic-context 0.43.2 → 0.44.1
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/README.md +18 -2
- package/dist/agents/magic-context-prompt.d.ts +2 -0
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/{announcement-1a8bvswd.js → announcement-n8hs7kjw.js} +2 -2
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/live-child-output-cap.d.ts +18 -0
- package/dist/config/live-child-output-cap.d.ts.map +1 -0
- package/dist/config/project-security.d.ts +6 -3
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +28 -1
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-hyk9y29x.js → conflict-warning-hook-d14ah73v.js} +6 -6
- package/dist/{derive-budgets-b8ybssh9.js → derive-budgets-gd2270s0.js} +3 -3
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-embedding.d.ts +3 -2
- package/dist/features/magic-context/compartment-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/curate-category-rotation.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/curate-memory-safety.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/failed-invocation-evidence.d.ts +13 -0
- package/dist/features/magic-context/dreamer/failed-invocation-evidence.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/hidden-single-shot.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/lease.d.ts +5 -2
- package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/memory-claim-safety.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/module-apply.d.ts +1 -0
- package/dist/features/magic-context/dreamer/module-apply.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/open-opencode-db.d.ts +2 -0
- package/dist/features/magic-context/dreamer/open-opencode-db.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/primer-seed.d.ts +11 -5
- package/dist/features/magic-context/dreamer/primer-seed.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +2 -2
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/retrospective-learnings.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +1 -1
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +2 -0
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts +19 -0
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/fail-closed-block.d.ts +7 -0
- package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
- package/dist/features/magic-context/index.d.ts +1 -0
- package/dist/features/magic-context/index.d.ts.map +1 -1
- package/dist/features/magic-context/install-instance-id.d.ts +33 -0
- package/dist/features/magic-context/install-instance-id.d.ts.map +1 -0
- package/dist/features/magic-context/memory/constants.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-backfill.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +7 -0
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/single-store-embedding-drain.d.ts +35 -0
- package/dist/features/magic-context/memory/single-store-embedding-drain.d.ts.map +1 -0
- package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts +23 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +7 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/mural-font.generated.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +31 -8
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +40 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/session-cache-ttl.d.ts +10 -0
- package/dist/features/magic-context/session-cache-ttl.d.ts.map +1 -0
- package/dist/features/magic-context/session-decision-calibration.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +20 -5
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-notes.d.ts +1 -0
- package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
- package/dist/features/magic-context/storage-primers.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage-unavailable-reason.d.ts +17 -0
- package/dist/features/magic-context/storage-unavailable-reason.d.ts.map +1 -0
- package/dist/features/magic-context/storage.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/store-generation-rebase.d.ts +33 -1
- package/dist/features/magic-context/store-generation-rebase.d.ts.map +1 -1
- package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
- package/dist/features/magic-context/transform-decision-log.d.ts +1 -1
- package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
- package/dist/features/magic-context/types.d.ts +8 -3
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +105 -9
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-parser.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-parser.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +1 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
- package/dist/hooks/magic-context/context-limit-resolution.d.ts +31 -0
- package/dist/hooks/magic-context/context-limit-resolution.d.ts.map +1 -0
- package/dist/hooks/magic-context/embed-history-runner.d.ts +0 -1
- package/dist/hooks/magic-context/embed-history-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/historian-host-runner.d.ts +233 -0
- package/dist/hooks/magic-context/historian-host-runner.d.ts.map +1 -0
- package/dist/hooks/magic-context/hook.d.ts +1 -3
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/image-token-estimate.d.ts +9 -0
- package/dist/hooks/magic-context/image-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +60 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-slot.d.ts +6 -0
- package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-client.d.ts +21 -0
- package/dist/hooks/magic-context/module-client.d.ts.map +1 -0
- package/dist/hooks/magic-context/module-state-sync.d.ts +6 -2
- package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-tool-backends.d.ts +47 -0
- package/dist/hooks/magic-context/module-tool-backends.d.ts.map +1 -0
- package/dist/hooks/magic-context/module-transport.d.ts +1 -1
- package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
- package/dist/hooks/magic-context/opencode-v1-store-fixture.d.ts +12 -0
- package/dist/hooks/magic-context/opencode-v1-store-fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/persist-filtered-noise.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +28 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +5 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-raw.d.ts +10 -0
- package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +35 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-refusal-recovery.d.ts +18 -0
- package/dist/hooks/magic-context/rust-refusal-recovery.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-served-m0.d.ts +20 -0
- package/dist/hooks/magic-context/rust-served-m0.d.ts.map +1 -0
- package/dist/hooks/magic-context/store-ahead-refusal.d.ts +40 -0
- package/dist/hooks/magic-context/store-ahead-refusal.d.ts.map +1 -0
- package/dist/hooks/magic-context/strip-content.d.ts +5 -5
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +19 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +14 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-input-size.d.ts +21 -0
- package/dist/hooks/magic-context/tool-input-size.d.ts.map +1 -0
- package/dist/hooks/magic-context/tool-sweep-policy.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +124 -11
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +14 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-pa67brzn.js → index-0rp6pj54.js} +3 -3
- package/dist/{index-4b30nf6z.js → index-32b096cc.js} +1 -1
- package/dist/{index-wgzyeyf5.js → index-3bygzk2d.js} +23 -7
- package/dist/{index-jgnag7sa.js → index-a9vbtdzv.js} +4 -4
- package/dist/{index-bmzt6xgy.js → index-cz88kw0n.js} +13 -3
- package/dist/{index-6j8th2hd.js → index-d107az47.js} +2 -2
- package/dist/{index-g6hpfkqa.js → index-jtdrvmcn.js} +3290 -286
- package/dist/{index-9r1cbh9e.js → index-kr5qehbd.js} +1 -1
- package/dist/{index-febg06dy.js → index-kwe03yde.js} +1 -1
- package/dist/{index-tdrv6nd0.js → index-s6b7h12g.js} +352 -71
- package/dist/{index-6z9g64js.js → index-s70wtd8k.js} +4 -1
- package/dist/{index-0ccmyzbj.js → index-stb5fg12.js} +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5230 -5663
- package/dist/{message-time-backfill-0ccvyk9n.js → message-time-backfill-q374fxc5.js} +4 -4
- package/dist/plugin/dream-timer.d.ts +2 -0
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/native-compaction-guard.d.ts +22 -0
- package/dist/plugin/native-compaction-guard.d.ts.map +1 -0
- package/dist/plugin/rpc-handlers.d.ts +11 -0
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/{read-session-chunk-53a7qbrh.js → read-session-chunk-fkdv2g5b.js} +8 -4
- package/dist/{recomp-orchestrator-adsgqgse.js → recomp-orchestrator-k4h5n0p3.js} +9 -9
- package/dist/{safe-notification-target-vrkjrbgb.js → safe-notification-target-50gm9c58.js} +3 -3
- package/dist/{send-session-notification-e28r4xx2.js → send-session-notification-g2k03zyn.js} +5 -5
- package/dist/shared/cache-ttl-display.d.ts +3 -1
- package/dist/shared/cache-ttl-display.d.ts.map +1 -1
- package/dist/shared/harness.d.ts +11 -0
- package/dist/shared/harness.d.ts.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/logger.d.ts.map +1 -1
- package/dist/shared/model-cache-ttl.d.ts +9 -0
- package/dist/shared/model-cache-ttl.d.ts.map +1 -0
- package/dist/shared/model-suggestion-retry.d.ts +13 -1
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/prompt-async-transport.d.ts +25 -0
- package/dist/shared/prompt-async-transport.d.ts.map +1 -0
- package/dist/shared/rpc-server.d.ts +1 -0
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +17 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +3 -0
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/shared/sqlite-write-locker-test-support.d.ts +7 -0
- package/dist/shared/sqlite-write-locker-test-support.d.ts.map +1 -0
- package/dist/shared/status-summary.d.ts +9 -1
- package/dist/shared/status-summary.d.ts.map +1 -1
- package/dist/shared/status-view.d.ts +1 -1
- package/dist/shared/status-view.d.ts.map +1 -1
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/dist/shared/user-facing-codes.d.ts +10 -5
- package/dist/shared/user-facing-codes.d.ts.map +1 -1
- package/dist/shared/window-geometry.d.ts +6 -3
- package/dist/shared/window-geometry.d.ts.map +1 -1
- package/dist/tools/ctx-expand/render.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/dist/v2/fold/boundary.d.ts +61 -0
- package/dist/v2/fold/boundary.d.ts.map +1 -0
- package/dist/v2/fold/restore.d.ts.map +1 -1
- package/dist/v2/hidden-completion.d.ts +16 -3
- package/dist/v2/hidden-completion.d.ts.map +1 -1
- package/dist/v2/hooks/context.d.ts +45 -14
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/hidden-child.d.ts +6 -0
- package/dist/v2/hooks/hidden-child.d.ts.map +1 -1
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/hooks/rpc-live-state.d.ts +1 -1
- package/dist/v2/hooks/rpc-live-state.d.ts.map +1 -1
- package/dist/v2/hooks/rust-mode.d.ts +47 -0
- package/dist/v2/hooks/rust-mode.d.ts.map +1 -0
- package/dist/v2/hooks/session-project-backfill.d.ts +15 -0
- package/dist/v2/hooks/session-project-backfill.d.ts.map +1 -0
- package/dist/v2/hooks/storage-gate.d.ts +42 -0
- package/dist/v2/hooks/storage-gate.d.ts.map +1 -0
- package/dist/v2/hooks/storage-notice.d.ts +29 -0
- package/dist/v2/hooks/storage-notice.d.ts.map +1 -0
- package/dist/v2/hooks/store.d.ts +16 -0
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/hooks/tools.d.ts +11 -2
- package/dist/v2/hooks/tools.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +4 -0
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/hooks/usage-persist.d.ts +31 -0
- package/dist/v2/hooks/usage-persist.d.ts.map +1 -0
- package/dist/v2/server.js +27201 -22695
- package/dist/v2/store-reader.d.ts +9 -0
- package/dist/v2/store-reader.d.ts.map +1 -1
- package/dist/wrapup-orchestrator-35y56636.js +18 -0
- package/package.json +1 -1
- package/src/agents/magic-context-prompt.test.ts +125 -0
- package/src/agents/magic-context-prompt.ts +15 -10
- package/src/config/index.ts +40 -0
- package/src/config/live-child-output-cap.test.ts +75 -0
- package/src/config/live-child-output-cap.ts +70 -0
- package/src/config/live-run-config.test.ts +24 -11
- package/src/config/misplaced-agent-model.test.ts +61 -0
- package/src/config/project-security.test.ts +17 -0
- package/src/config/project-security.ts +28 -4
- package/src/config/schema/magic-context.test.ts +16 -1
- package/src/config/schema/magic-context.ts +35 -6
- package/src/shared/cache-ttl-display.ts +14 -20
- package/src/shared/harness.ts +14 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/logger.test.ts +63 -2
- package/src/shared/logger.ts +23 -5
- package/src/shared/model-cache-ttl.test.ts +40 -0
- package/src/shared/model-cache-ttl.ts +58 -0
- package/src/shared/model-suggestion-retry.test.ts +115 -4
- package/src/shared/model-suggestion-retry.ts +57 -12
- package/src/shared/prompt-async-transport.test.ts +217 -0
- package/src/shared/prompt-async-transport.ts +242 -0
- package/src/shared/prompt-surface-a1-golden.md +17 -17
- package/src/shared/rpc-server.ts +13 -1
- package/src/shared/rpc-types.ts +18 -1
- package/src/shared/rpc-utils.ts +28 -0
- package/src/shared/sqlite-transaction-mode-guard.test.ts +198 -0
- package/src/shared/sqlite-write-locker-test-support.ts +62 -0
- package/src/shared/status-detail-text.test.ts +23 -0
- package/src/shared/status-summary.ts +25 -1
- package/src/shared/status-view.ts +1 -1
- package/src/shared/tag-transcript.ts +82 -19
- package/src/shared/user-facing-codes.ts +12 -5
- package/src/shared/window-geometry.ts +6 -3
- package/dist/v2/hooks/provider-admission.d.ts +0 -22
- package/dist/v2/hooks/provider-admission.d.ts.map +0 -1
- package/dist/wrapup-orchestrator-8xzbpvm8.js +0 -18
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join, relative, resolve } from "node:path";
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A deferred transaction (`db.transaction(fn)()`, `.deferred()`, `.default()`)
|
|
8
|
+
* starts with no lock. If it reads before it writes, the write must upgrade a read
|
|
9
|
+
* snapshot to a write lock, and SQLite never runs the busy handler for that
|
|
10
|
+
* upgrade: under another process's write it fails at once with SQLITE_BUSY, or
|
|
11
|
+
* with SQLITE_BUSY_SNAPSHOT when another commit landed after the read, and
|
|
12
|
+
* busy_timeout never applies. Many OpenCode and Pi processes share one context.db,
|
|
13
|
+
* so every transaction that writes must start with `.immediate()` (or
|
|
14
|
+
* `.exclusive()`), which takes the write lock at BEGIN where busy_timeout covers it.
|
|
15
|
+
*
|
|
16
|
+
* Only read-only transactions may stay deferred. Each one is listed here, keyed
|
|
17
|
+
* by file and the nearest named enclosing function, with the reason it never
|
|
18
|
+
* writes. A read-only snapshot must not take the write lock, because that would
|
|
19
|
+
* block every writer for the length of the read.
|
|
20
|
+
*/
|
|
21
|
+
const READ_ONLY_DEFERRED_TRANSACTIONS: Record<string, string> = {
|
|
22
|
+
"packages/plugin/src/v2/store-reader.ts#window":
|
|
23
|
+
"reads the latest compaction and the rows after it as one consistent snapshot of the OpenCode 2 store; never writes",
|
|
24
|
+
"packages/pi-plugin/src/inject-compartments-pi.ts#readFrozenM0InputsPi":
|
|
25
|
+
"reads the m[0] render sources and their watermarks as one consistent snapshot; never writes",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const REPOSITORY_ROOT = resolve(import.meta.dir, "../../../..");
|
|
29
|
+
const SOURCE_ROOTS = ["packages/plugin/src", "packages/pi-plugin/src"];
|
|
30
|
+
const LOCKING_MODES = new Set(["immediate", "exclusive"]);
|
|
31
|
+
|
|
32
|
+
function sourceFiles(directory: string): string[] {
|
|
33
|
+
const result: string[] = [];
|
|
34
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
35
|
+
const path = join(directory, entry.name);
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
if (entry.name === "node_modules") continue;
|
|
38
|
+
result.push(...sourceFiles(path));
|
|
39
|
+
} else if (
|
|
40
|
+
/\.tsx?$/.test(entry.name) &&
|
|
41
|
+
!/\.test\.tsx?$/.test(entry.name) &&
|
|
42
|
+
!/\.d\.ts$/.test(entry.name)
|
|
43
|
+
) {
|
|
44
|
+
result.push(path);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isTransactionCall(node: ts.Node): node is ts.CallExpression {
|
|
51
|
+
return (
|
|
52
|
+
ts.isCallExpression(node) &&
|
|
53
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
54
|
+
node.expression.name.text === "transaction"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** True when `node` is the receiver of a called `.immediate()` / `.exclusive()`. */
|
|
59
|
+
function isCalledWithLockingMode(node: ts.Node): boolean {
|
|
60
|
+
const access = node.parent;
|
|
61
|
+
return (
|
|
62
|
+
access !== undefined &&
|
|
63
|
+
ts.isPropertyAccessExpression(access) &&
|
|
64
|
+
access.expression === node &&
|
|
65
|
+
LOCKING_MODES.has(access.name.text) &&
|
|
66
|
+
access.parent !== undefined &&
|
|
67
|
+
ts.isCallExpression(access.parent) &&
|
|
68
|
+
access.parent.expression === access
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function enclosingFunctionName(node: ts.Node): string {
|
|
73
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
74
|
+
if (
|
|
75
|
+
(ts.isFunctionDeclaration(current) || ts.isMethodDeclaration(current)) &&
|
|
76
|
+
current.name
|
|
77
|
+
) {
|
|
78
|
+
return current.name.getText();
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
(ts.isArrowFunction(current) || ts.isFunctionExpression(current)) &&
|
|
82
|
+
current.parent &&
|
|
83
|
+
(ts.isVariableDeclaration(current.parent) || ts.isPropertyAssignment(current.parent))
|
|
84
|
+
) {
|
|
85
|
+
return current.parent.name.getText();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return "<module>";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A transaction stored in a variable is locking only when every use of that
|
|
93
|
+
* variable calls `.immediate()` or `.exclusive()` on it.
|
|
94
|
+
*/
|
|
95
|
+
function storedTransactionLocks(call: ts.CallExpression, source: ts.SourceFile): boolean {
|
|
96
|
+
const declaration = call.parent;
|
|
97
|
+
if (!ts.isVariableDeclaration(declaration) || !ts.isIdentifier(declaration.name)) return false;
|
|
98
|
+
const name = declaration.name.text;
|
|
99
|
+
let scope: ts.Node = source;
|
|
100
|
+
for (let current: ts.Node | undefined = declaration.parent; current; current = current.parent) {
|
|
101
|
+
if (ts.isBlock(current)) {
|
|
102
|
+
scope = current;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const uses: ts.Identifier[] = [];
|
|
107
|
+
const visit = (node: ts.Node): void => {
|
|
108
|
+
if (ts.isIdentifier(node) && node.text === name && node !== declaration.name) {
|
|
109
|
+
uses.push(node);
|
|
110
|
+
}
|
|
111
|
+
ts.forEachChild(node, visit);
|
|
112
|
+
};
|
|
113
|
+
visit(scope);
|
|
114
|
+
return uses.length > 0 && uses.every(isCalledWithLockingMode);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface TransactionScan {
|
|
118
|
+
deferredSites: Map<string, string[]>;
|
|
119
|
+
lockingSites: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function scanTransactionModes(files: Array<{ path: string; text: string }>): TransactionScan {
|
|
123
|
+
const deferredSites = new Map<string, string[]>();
|
|
124
|
+
let lockingSites = 0;
|
|
125
|
+
for (const file of files) {
|
|
126
|
+
if (!file.text.includes(".transaction(")) continue;
|
|
127
|
+
const source = ts.createSourceFile(file.path, file.text, ts.ScriptTarget.Latest, true);
|
|
128
|
+
const visit = (node: ts.Node): void => {
|
|
129
|
+
if (isTransactionCall(node)) {
|
|
130
|
+
if (isCalledWithLockingMode(node) || storedTransactionLocks(node, source)) {
|
|
131
|
+
lockingSites += 1;
|
|
132
|
+
} else {
|
|
133
|
+
const key = `${file.path}#${enclosingFunctionName(node)}`;
|
|
134
|
+
const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line;
|
|
135
|
+
const locations = deferredSites.get(key) ?? [];
|
|
136
|
+
locations.push(`${file.path}:${line + 1}`);
|
|
137
|
+
deferredSites.set(key, locations);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
ts.forEachChild(node, visit);
|
|
141
|
+
};
|
|
142
|
+
visit(source);
|
|
143
|
+
}
|
|
144
|
+
return { deferredSites, lockingSites };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function repositorySources(): Array<{ path: string; text: string }> {
|
|
148
|
+
return SOURCE_ROOTS.flatMap((root) =>
|
|
149
|
+
sourceFiles(resolve(REPOSITORY_ROOT, root)).map((path) => ({
|
|
150
|
+
path: relative(REPOSITORY_ROOT, path),
|
|
151
|
+
text: readFileSync(path, "utf8"),
|
|
152
|
+
})),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
describe("SQLite transaction lock mode", () => {
|
|
157
|
+
it("starts every writing transaction with .immediate() or .exclusive()", () => {
|
|
158
|
+
const { deferredSites, lockingSites } = scanTransactionModes(repositorySources());
|
|
159
|
+
// The scan must actually see the codebase's transactions.
|
|
160
|
+
expect(lockingSites).toBeGreaterThan(50);
|
|
161
|
+
|
|
162
|
+
const unlisted = [...deferredSites]
|
|
163
|
+
.filter(([key]) => !(key in READ_ONLY_DEFERRED_TRANSACTIONS))
|
|
164
|
+
.flatMap(([, locations]) => locations);
|
|
165
|
+
expect(unlisted).toEqual([]);
|
|
166
|
+
|
|
167
|
+
// One deferred transaction per listed function, so a second one added
|
|
168
|
+
// beside a read-only snapshot is not waved through by the same entry.
|
|
169
|
+
const repeated = [...deferredSites].filter(([, locations]) => locations.length > 1);
|
|
170
|
+
expect(repeated).toEqual([]);
|
|
171
|
+
|
|
172
|
+
// A listed entry whose transaction is gone or now locks must be removed.
|
|
173
|
+
const stale = Object.keys(READ_ONLY_DEFERRED_TRANSACTIONS).filter(
|
|
174
|
+
(key) => !deferredSites.has(key),
|
|
175
|
+
);
|
|
176
|
+
expect(stale).toEqual([]);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("recognises each deferred and locking shape", () => {
|
|
180
|
+
const text = `
|
|
181
|
+
function plainCall(db) { db.transaction(() => write())(); }
|
|
182
|
+
function explicitDeferred(db) { db.transaction(() => write()).deferred(); }
|
|
183
|
+
function stored(db) { const tx = db.transaction(() => write()); tx(); }
|
|
184
|
+
function storedMixed(db) { const tx = db.transaction(() => write()); tx.immediate(); tx(); }
|
|
185
|
+
function immediate(db) { db.transaction(() => write()).immediate(); }
|
|
186
|
+
function chained(db) { return db\n.transaction(() => write())\n.exclusive(); }
|
|
187
|
+
function storedImmediate(db) { const tx = db.transaction(() => write()); return tx.immediate(); }
|
|
188
|
+
`;
|
|
189
|
+
const { deferredSites, lockingSites } = scanTransactionModes([{ path: "x.ts", text }]);
|
|
190
|
+
expect([...deferredSites.keys()].sort()).toEqual([
|
|
191
|
+
"x.ts#explicitDeferred",
|
|
192
|
+
"x.ts#plainCall",
|
|
193
|
+
"x.ts#stored",
|
|
194
|
+
"x.ts#storedMixed",
|
|
195
|
+
]);
|
|
196
|
+
expect(lockingSites).toBe(3);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Holds a SQLite write lock from a separate process, for lock-contention tests.
|
|
3
|
+
*
|
|
4
|
+
* Run directly as `bun sqlite-write-locker-test-support.ts <db> <holdMs>`, the file
|
|
5
|
+
* opens the database, takes the write lock with BEGIN IMMEDIATE, prints `LOCKED`,
|
|
6
|
+
* holds the lock for `holdMs`, commits and prints `RELEASED`. Tests start it
|
|
7
|
+
* through `startSqliteWriteLocker`.
|
|
8
|
+
*
|
|
9
|
+
* The lock has to come from a different process. A second connection in the
|
|
10
|
+
* test's own process cannot release its lock while the test's main thread is
|
|
11
|
+
* blocked inside SQLite's busy wait, so the wait would always time out and a test
|
|
12
|
+
* could never observe a writer that waits and then succeeds.
|
|
13
|
+
*/
|
|
14
|
+
import { Database } from "bun:sqlite";
|
|
15
|
+
|
|
16
|
+
export interface SqliteWriteLocker {
|
|
17
|
+
/** Resolves with the locker's exit code after it has committed and exited. */
|
|
18
|
+
exited: Promise<number>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Start the locker and resolve once it reports that it holds the write lock. */
|
|
22
|
+
export async function startSqliteWriteLocker(
|
|
23
|
+
dbPath: string,
|
|
24
|
+
holdMs: number,
|
|
25
|
+
): Promise<SqliteWriteLocker> {
|
|
26
|
+
const child = Bun.spawn(["bun", import.meta.path, dbPath, String(holdMs)], {
|
|
27
|
+
stdout: "pipe",
|
|
28
|
+
stderr: "pipe",
|
|
29
|
+
});
|
|
30
|
+
const reader = child.stdout.getReader();
|
|
31
|
+
const decoder = new TextDecoder();
|
|
32
|
+
let seen = "";
|
|
33
|
+
while (!seen.includes("LOCKED\n")) {
|
|
34
|
+
const chunk = await reader.read();
|
|
35
|
+
if (chunk.done) {
|
|
36
|
+
const stderr = await new Response(child.stderr).text();
|
|
37
|
+
throw new Error(`sqlite write locker exited before locking: ${stderr}`);
|
|
38
|
+
}
|
|
39
|
+
seen += decoder.decode(chunk.value, { stream: true });
|
|
40
|
+
}
|
|
41
|
+
reader.releaseLock();
|
|
42
|
+
return { exited: child.exited };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function main(): void {
|
|
46
|
+
const [dbPath, holdArg] = process.argv.slice(2);
|
|
47
|
+
const holdMs = Number(holdArg);
|
|
48
|
+
if (!dbPath || !Number.isFinite(holdMs) || holdMs < 0) {
|
|
49
|
+
process.stderr.write("usage: sqlite-write-locker-test-support.ts <db> <holdMs>\n");
|
|
50
|
+
process.exit(2);
|
|
51
|
+
}
|
|
52
|
+
const db = new Database(dbPath);
|
|
53
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
54
|
+
db.exec("BEGIN IMMEDIATE");
|
|
55
|
+
process.stdout.write("LOCKED\n");
|
|
56
|
+
Bun.sleepSync(holdMs);
|
|
57
|
+
db.exec("COMMIT");
|
|
58
|
+
db.close();
|
|
59
|
+
process.stdout.write("RELEASED\n");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (import.meta.main) main();
|
|
@@ -217,6 +217,29 @@ describe("status detail text", () => {
|
|
|
217
217
|
);
|
|
218
218
|
});
|
|
219
219
|
|
|
220
|
+
test("says which runner ran the session's completions and why", () => {
|
|
221
|
+
const status = formatStatusDetailMarkdown({
|
|
222
|
+
...STATUS_FIXTURE,
|
|
223
|
+
historianRunner: {
|
|
224
|
+
runner: "host",
|
|
225
|
+
source: "default_for_harness",
|
|
226
|
+
harness: "opencode",
|
|
227
|
+
observed: "last_completion",
|
|
228
|
+
},
|
|
229
|
+
dreamerRunner: {
|
|
230
|
+
runner: "broca",
|
|
231
|
+
source: "configured",
|
|
232
|
+
harness: "opencode",
|
|
233
|
+
observed: "resolved_for_route",
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(status).toMatch(
|
|
238
|
+
/^- \*\*Historian runner:\*\* host \(default for harness opencode\)$/m,
|
|
239
|
+
);
|
|
240
|
+
expect(status).toContain("- **Dreamer runner:** broca (configured) · no completion yet");
|
|
241
|
+
});
|
|
242
|
+
|
|
220
243
|
test("does not expose module routing in the summary", () => {
|
|
221
244
|
const rustStatus = formatStatusDetailMarkdown({
|
|
222
245
|
...STATUS_FIXTURE,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatCacheTtlDisplay } from "./cache-ttl-display";
|
|
2
|
-
import type { StatusDetail } from "./rpc-types";
|
|
2
|
+
import type { RunnerStatus, StatusDetail } from "./rpc-types";
|
|
3
3
|
import { renderUserFacingFailure, type UserFacingFailureKey } from "./user-facing-codes";
|
|
4
4
|
|
|
5
5
|
export type StatusCompressionState = "off" | "compressing" | "ready" | "waiting";
|
|
@@ -32,6 +32,8 @@ export interface UserStatusSummary {
|
|
|
32
32
|
detail: string;
|
|
33
33
|
};
|
|
34
34
|
compactionMarker?: StatusDetail["compactionMarker"];
|
|
35
|
+
historianRunner?: RunnerStatus;
|
|
36
|
+
dreamerRunner?: RunnerStatus;
|
|
35
37
|
warnings: UserFacingFailureKey[];
|
|
36
38
|
hiddenVariantWarnings?: string[];
|
|
37
39
|
}
|
|
@@ -96,6 +98,8 @@ export function statusSummaryFromDetail(detail: StatusDetail): UserStatusSummary
|
|
|
96
98
|
total: 0,
|
|
97
99
|
},
|
|
98
100
|
historianRefusal: detail.historianRefusal,
|
|
101
|
+
historianRunner: detail.historianRunner,
|
|
102
|
+
dreamerRunner: detail.dreamerRunner,
|
|
99
103
|
compactionMarker: detail.compactionMarker,
|
|
100
104
|
warnings: [...new Set(warnings)],
|
|
101
105
|
hiddenVariantWarnings: detail.hiddenVariantWarnings ?? [],
|
|
@@ -122,6 +126,20 @@ function compressionText(summary: UserStatusSummary): string {
|
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
/**
|
|
130
|
+
* `host (default for harness opencode)` or `broca (configured)`, plus a note when
|
|
131
|
+
* no completion has run yet in the module's current process and the value is
|
|
132
|
+
* only what the session's route resolves to.
|
|
133
|
+
*/
|
|
134
|
+
export function runnerText(status: RunnerStatus): string {
|
|
135
|
+
const why =
|
|
136
|
+
status.source === "configured"
|
|
137
|
+
? "configured"
|
|
138
|
+
: `default for harness ${status.harness || "unknown"}`;
|
|
139
|
+
const observed = status.observed === "last_completion" ? "" : " · no completion yet";
|
|
140
|
+
return `${status.runner} (${why})${observed}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
125
143
|
function reclaimableText(summary: UserStatusSummary): string {
|
|
126
144
|
const count = Math.max(0, Math.floor(summary.reclaimable.toolOutputCount));
|
|
127
145
|
if (count === 0) return "none";
|
|
@@ -170,6 +188,12 @@ export function renderUserStatusSummary(
|
|
|
170
188
|
],
|
|
171
189
|
["Search indexing", embeddingText(summary)],
|
|
172
190
|
];
|
|
191
|
+
if (summary.historianRunner) {
|
|
192
|
+
values.push(["Historian runner", runnerText(summary.historianRunner)]);
|
|
193
|
+
}
|
|
194
|
+
if (summary.dreamerRunner) {
|
|
195
|
+
values.push(["Dreamer runner", runnerText(summary.dreamerRunner)]);
|
|
196
|
+
}
|
|
173
197
|
if (summary.historianRefusal) {
|
|
174
198
|
values.push([
|
|
175
199
|
"Historian refusal",
|
|
@@ -124,7 +124,7 @@ export interface StatusViewSource {
|
|
|
124
124
|
readonly protectedTagCount: number;
|
|
125
125
|
readonly isSubagent: boolean;
|
|
126
126
|
readonly cacheTtl: string;
|
|
127
|
-
readonly cacheTtlSource?: "
|
|
127
|
+
readonly cacheTtlSource?: import("./cache-ttl-display").CacheTtlDisplaySource;
|
|
128
128
|
readonly cacheTtlModelKey?: string;
|
|
129
129
|
readonly lastResponseTime: number;
|
|
130
130
|
readonly cacheRemainingMs: number;
|
|
@@ -66,6 +66,7 @@ import {
|
|
|
66
66
|
stripWellFormedLeadingTagPrefix,
|
|
67
67
|
} from "../hooks/magic-context/tag-content-primitives";
|
|
68
68
|
import type { TagTarget } from "../hooks/magic-context/tag-messages";
|
|
69
|
+
import { toolInputStringBytes } from "../hooks/magic-context/tool-input-size";
|
|
69
70
|
import type { Transcript, TranscriptMessage, TranscriptPart } from "./transcript";
|
|
70
71
|
|
|
71
72
|
export const TEXT_TAG_IDENTITY_MARKER = ":mc-text-v1:";
|
|
@@ -161,6 +162,12 @@ interface ToolOccurrence {
|
|
|
161
162
|
message: { info: { id?: string; role: string } };
|
|
162
163
|
part: TranscriptPart;
|
|
163
164
|
kind: "tool_use" | "tool_result";
|
|
165
|
+
/**
|
|
166
|
+
* The call's real input, captured the first time this pass overwrote it
|
|
167
|
+
* with the legacy marker, so a later real-argument render in the same pass
|
|
168
|
+
* can put it back.
|
|
169
|
+
*/
|
|
170
|
+
originalInput?: Record<string, unknown>;
|
|
164
171
|
}
|
|
165
172
|
|
|
166
173
|
interface TagTranscriptTiming {
|
|
@@ -1093,10 +1100,13 @@ function buildAggregateTarget(
|
|
|
1093
1100
|
const truncate = (): "truncated" | "absent" => {
|
|
1094
1101
|
// Keep the tool call and its result, but replace the call arguments with
|
|
1095
1102
|
// a non-executable marker and the result content with the dropped marker.
|
|
1103
|
+
// Legacy render, replayed only for `drop_mode = 'truncated'` tags until a
|
|
1104
|
+
// HARD fold converts them; new drops use skeletonReal().
|
|
1096
1105
|
const sentinel = `[dropped \u00a7${tagId}\u00a7]`;
|
|
1097
1106
|
let any = false;
|
|
1098
1107
|
for (const occ of occurrences) {
|
|
1099
1108
|
if (occ.kind === "tool_use" && occ.part.setToolInput) {
|
|
1109
|
+
occ.originalInput ??= occ.part.getToolInput?.() ?? undefined;
|
|
1100
1110
|
if (occ.part.setToolInput(droppedInputMarker(tagId))) any = true;
|
|
1101
1111
|
} else if (setToolContentOrText(occ.part, sentinel)) {
|
|
1102
1112
|
any = true;
|
|
@@ -1105,6 +1115,38 @@ function buildAggregateTarget(
|
|
|
1105
1115
|
return any ? "truncated" : "absent";
|
|
1106
1116
|
};
|
|
1107
1117
|
|
|
1118
|
+
const complete = (): boolean =>
|
|
1119
|
+
occurrences.some((occ) => occ.kind === "tool_use") &&
|
|
1120
|
+
occurrences.some((occ) => occ.kind === "tool_result");
|
|
1121
|
+
|
|
1122
|
+
const skeletonReal = (): "truncated" | "absent" | "incomplete" => {
|
|
1123
|
+
// Keep the tool call with the arguments the host gave it and replace
|
|
1124
|
+
// only the result content with the dropped marker. Nothing is written
|
|
1125
|
+
// into the argument position unless an earlier render this pass did,
|
|
1126
|
+
// in which case the real input goes back.
|
|
1127
|
+
if (!complete()) return "incomplete";
|
|
1128
|
+
const sentinel = `[dropped \u00a7${tagId}\u00a7]`;
|
|
1129
|
+
let any = false;
|
|
1130
|
+
for (const occ of occurrences) {
|
|
1131
|
+
if (occ.kind === "tool_use") {
|
|
1132
|
+
if (occ.originalInput && occ.part.setToolInput?.(occ.originalInput)) any = true;
|
|
1133
|
+
} else if (setToolContentOrText(occ.part, sentinel)) {
|
|
1134
|
+
any = true;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return any ? "truncated" : "absent";
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
// Adapters without structural removal (or arcs that must keep their pair
|
|
1141
|
+
// beside native reasoning) cannot remove the call; drop() would keep it.
|
|
1142
|
+
const cannotRemove = (): boolean => {
|
|
1143
|
+
if (requiresToolArcSkeleton) return true;
|
|
1144
|
+
const authorization = occurrences.map((occ) => occ.part.canRemove?.());
|
|
1145
|
+
return !occurrences.every(
|
|
1146
|
+
(occ, index) => occ.part.remove && authorization[index] !== false,
|
|
1147
|
+
);
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1108
1150
|
return {
|
|
1109
1151
|
measureReclaim(skeleton) {
|
|
1110
1152
|
let beforeTools = 0;
|
|
@@ -1125,10 +1167,8 @@ function buildAggregateTarget(
|
|
|
1125
1167
|
: (occ.part.getText() ?? "");
|
|
1126
1168
|
beforeTools += estimateTokens(before);
|
|
1127
1169
|
if (skeleton || !removable) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
? JSON.stringify(droppedInputMarker(tagId))
|
|
1131
|
-
: `[dropped §${tagId}§]`;
|
|
1170
|
+
// A kept call keeps its real arguments; only the result shrinks.
|
|
1171
|
+
const after = occ.kind === "tool_use" ? before : `[dropped §${tagId}§]`;
|
|
1132
1172
|
afterTools += estimateTokens(after);
|
|
1133
1173
|
}
|
|
1134
1174
|
}
|
|
@@ -1163,10 +1203,7 @@ function buildAggregateTarget(
|
|
|
1163
1203
|
return occurrences[0]?.part.getText() ?? null;
|
|
1164
1204
|
},
|
|
1165
1205
|
drop(): "removed" | "truncated" | "absent" | "incomplete" {
|
|
1166
|
-
|
|
1167
|
-
occurrences.some((occ) => occ.kind === "tool_use") &&
|
|
1168
|
-
occurrences.some((occ) => occ.kind === "tool_result");
|
|
1169
|
-
if (!complete) return "incomplete";
|
|
1206
|
+
if (!complete()) return "incomplete";
|
|
1170
1207
|
if (!requiresToolArcSkeleton) {
|
|
1171
1208
|
const authorization = occurrences.map((occ) => occ.part.canRemove?.());
|
|
1172
1209
|
// A failed durable marker is not permission to substitute a new
|
|
@@ -1178,19 +1215,23 @@ function buildAggregateTarget(
|
|
|
1178
1215
|
)
|
|
1179
1216
|
) {
|
|
1180
1217
|
// If removing this call's result would leave the provider
|
|
1181
|
-
// request ending on an assistant turn, keep
|
|
1182
|
-
// plus placeholder result instead.
|
|
1183
|
-
// to persist that mode
|
|
1218
|
+
// request ending on an assistant turn, keep the call with its
|
|
1219
|
+
// REAL arguments plus the placeholder result instead.
|
|
1220
|
+
// "truncated" tells callers to persist that mode
|
|
1221
|
+
// (`skeleton_real`), so later passes serve the same bytes.
|
|
1184
1222
|
// (The sentinel fallback below keeps call and result in place,
|
|
1185
1223
|
// so it needs no exception.)
|
|
1186
|
-
if (conversationEnd?.wouldStrand(occurrences)) return
|
|
1224
|
+
if (conversationEnd?.wouldStrand(occurrences)) return skeletonReal();
|
|
1187
1225
|
let removed = false;
|
|
1188
1226
|
for (const occ of occurrences) removed = occ.part.remove?.() || removed;
|
|
1189
1227
|
if (removed) conversationEnd?.noteRemoved(occurrences);
|
|
1190
1228
|
return removed ? "removed" : "absent";
|
|
1191
1229
|
}
|
|
1192
1230
|
}
|
|
1193
|
-
// Adapters without structural removal retain paired sentinels.
|
|
1231
|
+
// Adapters without structural removal retain paired sentinels. This
|
|
1232
|
+
// writes a marker into the call's arguments, so only replays of
|
|
1233
|
+
// existing `full` tags reach it: new drops check cannotRemove() and
|
|
1234
|
+
// use skeletonReal(), and a HARD fold converts the existing ones.
|
|
1194
1235
|
const sentinel = `[dropped \u00a7${tagId}\u00a7]`;
|
|
1195
1236
|
let any = false;
|
|
1196
1237
|
for (const occ of occurrences) {
|
|
@@ -1199,6 +1240,18 @@ function buildAggregateTarget(
|
|
|
1199
1240
|
return any ? "removed" : "absent";
|
|
1200
1241
|
},
|
|
1201
1242
|
truncate,
|
|
1243
|
+
skeletonReal,
|
|
1244
|
+
cannotRemove,
|
|
1245
|
+
inputStringBytes(): number | null {
|
|
1246
|
+
for (const occ of occurrences) {
|
|
1247
|
+
if (occ.kind !== "tool_use") continue;
|
|
1248
|
+
return toolInputStringBytes(occ.originalInput ?? occ.part.getToolInput?.() ?? null);
|
|
1249
|
+
}
|
|
1250
|
+
return null;
|
|
1251
|
+
},
|
|
1252
|
+
wouldStrandConversationEnd(): boolean {
|
|
1253
|
+
return conversationEnd?.wouldStrand(occurrences) ?? false;
|
|
1254
|
+
},
|
|
1202
1255
|
editMarker(): "truncated" | "absent" {
|
|
1203
1256
|
// Edit-marker: preserve the tool_use input's filePath + a region
|
|
1204
1257
|
// hint of the diff, sentinelize the result half. Separate from
|
|
@@ -1222,12 +1275,7 @@ function buildAggregateTarget(
|
|
|
1222
1275
|
return any ? "truncated" : "absent";
|
|
1223
1276
|
},
|
|
1224
1277
|
// Open invocations still belong to the current turn; only complete arcs reclaim.
|
|
1225
|
-
canDrop
|
|
1226
|
-
return (
|
|
1227
|
-
occurrences.some((occ) => occ.kind === "tool_use") &&
|
|
1228
|
-
occurrences.some((occ) => occ.kind === "tool_result")
|
|
1229
|
-
);
|
|
1230
|
-
},
|
|
1278
|
+
canDrop: complete,
|
|
1231
1279
|
requiresToolArcSkeleton,
|
|
1232
1280
|
// Non-mutating read of the invocation input (the tool_use occurrence
|
|
1233
1281
|
// carries the arguments). Used by smart-drops supersession selection.
|
|
@@ -1310,11 +1358,26 @@ function buildToolTarget(
|
|
|
1310
1358
|
return replaced ? "removed" : "absent";
|
|
1311
1359
|
},
|
|
1312
1360
|
truncate(): "truncated" | "absent" {
|
|
1361
|
+
// Legacy render, replayed only for unconverted `truncated` tags.
|
|
1313
1362
|
const changed = part.setToolInput
|
|
1314
1363
|
? part.setToolInput(droppedInputMarker(tagId))
|
|
1315
1364
|
: setToolContentOrText(part, `[dropped \u00a7${tagId}\u00a7]`);
|
|
1316
1365
|
return changed ? "truncated" : "absent";
|
|
1317
1366
|
},
|
|
1367
|
+
skeletonReal(): "truncated" | "absent" {
|
|
1368
|
+
// An invocation part keeps its real arguments untouched; a result
|
|
1369
|
+
// part's content becomes the dropped marker.
|
|
1370
|
+
if (part.setToolInput) return "truncated";
|
|
1371
|
+
return setToolContentOrText(part, `[dropped \u00a7${tagId}\u00a7]`)
|
|
1372
|
+
? "truncated"
|
|
1373
|
+
: "absent";
|
|
1374
|
+
},
|
|
1375
|
+
// drop() replaces an invocation part's arguments with a marker, so a
|
|
1376
|
+
// new drop of one keeps its real arguments instead.
|
|
1377
|
+
cannotRemove: () => part.setToolInput !== undefined,
|
|
1378
|
+
inputStringBytes(): number | null {
|
|
1379
|
+
return part.setToolInput ? toolInputStringBytes(part.getToolInput?.() ?? null) : 0;
|
|
1380
|
+
},
|
|
1318
1381
|
message: {
|
|
1319
1382
|
info: { id: message.info.id, role: message.info.role },
|
|
1320
1383
|
parts: [],
|
|
@@ -33,6 +33,11 @@ export const USER_FACING_FAILURES = {
|
|
|
33
33
|
sentence: "Memory maintenance could not reach its model.",
|
|
34
34
|
action: "Check the model connection, then run /ctx-dream again.",
|
|
35
35
|
},
|
|
36
|
+
dream_step_limit: {
|
|
37
|
+
code: "MC-D10",
|
|
38
|
+
sentence: "Memory maintenance stopped at its hidden agent step limit.",
|
|
39
|
+
action: "This task needs less work per run; changing the model connection will not help.",
|
|
40
|
+
},
|
|
36
41
|
dream_empty_completion: {
|
|
37
42
|
code: "MC-D03",
|
|
38
43
|
sentence: "Memory maintenance received no usable response.",
|
|
@@ -199,6 +204,12 @@ export const USER_FACING_FAILURES = {
|
|
|
199
204
|
sentence: "Magic Context is temporarily unavailable.",
|
|
200
205
|
action: "Retry in a moment.",
|
|
201
206
|
},
|
|
207
|
+
store_ahead_of_binary: {
|
|
208
|
+
code: "MC-C13",
|
|
209
|
+
sentence:
|
|
210
|
+
"Magic Context refused to start: its store (store.db) was migrated by a newer ck-mc build than the one running.",
|
|
211
|
+
action: "Update ck-mc, or roll back by restoring ck-mc together with context.db and store.db from the same backup.",
|
|
212
|
+
},
|
|
202
213
|
compaction_marker_missing: {
|
|
203
214
|
code: "MC-C11",
|
|
204
215
|
sentence:
|
|
@@ -215,11 +226,6 @@ export const USER_FACING_FAILURES = {
|
|
|
215
226
|
sentence: "Memory authority is inconsistent between the host and module.",
|
|
216
227
|
action: "Run `ck doctor drain-authority` before changing Rust mode.",
|
|
217
228
|
},
|
|
218
|
-
rust_mode_unsupported: {
|
|
219
|
-
code: "MC-S06",
|
|
220
|
-
sentence: "Experimental Rust transform mode is not available on this OpenCode host.",
|
|
221
|
-
action: 'Magic Context is running its TypeScript mode instead; set `transform_mode` to "ts" to stop requesting Rust.',
|
|
222
|
-
},
|
|
223
229
|
dreamer_task_failing: {
|
|
224
230
|
code: "MC-S05",
|
|
225
231
|
sentence: "A background maintenance task keeps failing on its schedule.",
|
|
@@ -278,6 +284,7 @@ export function capabilityRefusalCode(capability: CapabilityRefusal): string {
|
|
|
278
284
|
const DREAM_FAILURE_KEYS = {
|
|
279
285
|
provider_timeout: "dream_provider_timeout",
|
|
280
286
|
provider_error: "dream_provider_error",
|
|
287
|
+
step_limit: "dream_step_limit",
|
|
281
288
|
empty_completion: "dream_empty_completion",
|
|
282
289
|
no_models: "dream_no_models",
|
|
283
290
|
child_aborted: "dream_child_aborted",
|
|
@@ -645,9 +645,12 @@ export function hasTrustedAbsoluteWall(geometry: WindowGeometryResult): boolean
|
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
/**
|
|
648
|
-
* Successful requests can disprove static catalog metadata, but
|
|
649
|
-
*
|
|
650
|
-
*
|
|
648
|
+
* Successful requests can disprove static catalog metadata, but do not widen a
|
|
649
|
+
* provider, overlay, or observed-overflow wall here. A provider or overlay wall
|
|
650
|
+
* is the configured limit: a reading beyond it is real pressure against that
|
|
651
|
+
* limit (the usage handlers count it in full), not proof that the geometry
|
|
652
|
+
* should grow. An observed-overflow wall that a larger accepted request
|
|
653
|
+
* disproves is cleared by the usage handlers instead.
|
|
651
654
|
*/
|
|
652
655
|
export function applyProvenInputFloor(
|
|
653
656
|
geometry: WindowGeometryResult,
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export interface ProviderAdmissionInput {
|
|
2
|
-
/** Input + cached-read + cached-write tokens of the newest assistant reply. */
|
|
3
|
-
inputTokens: number;
|
|
4
|
-
/** The provider's own context window for the outgoing model, when the catalog knows it. */
|
|
5
|
-
rawContextLimit: number | undefined;
|
|
6
|
-
/** True when the host wrote a compaction after the reply the tokens were read from. */
|
|
7
|
-
hostCompactionReducedUsage: boolean;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Whether the OpenCode 2 context hook must stop a request before it reaches the
|
|
11
|
-
* provider.
|
|
12
|
-
*
|
|
13
|
-
* The provider's own context window is the only admission boundary here. Every
|
|
14
|
-
* other kind of pressure — in particular usage that fits the window but not the
|
|
15
|
-
* room reserved for the reply — has to be admitted, because the work that
|
|
16
|
-
* relieves it (compacting history into a compartment, and the high-pressure
|
|
17
|
-
* handling around it) all happens further down this same pass. A request stopped
|
|
18
|
-
* here runs none of it, so a session refused for pressure Magic Context could
|
|
19
|
-
* have removed is refused again on every following turn.
|
|
20
|
-
*/
|
|
21
|
-
export declare function refusesBeforeProvider(input: ProviderAdmissionInput): boolean;
|
|
22
|
-
//# sourceMappingURL=provider-admission.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provider-admission.d.ts","sourceRoot":"","sources":["../../../src/v2/hooks/provider-admission.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACnC,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,uFAAuF;IACvF,0BAA0B,EAAE,OAAO,CAAC;CACvC;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAkB5E"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runManagedWrapup2
|
|
3
|
-
} from "./index-pa67brzn.js";
|
|
4
|
-
import"./index-g6hpfkqa.js";
|
|
5
|
-
import"./index-sd3egckz.js";
|
|
6
|
-
import"./index-0ccmyzbj.js";
|
|
7
|
-
import"./index-jgnag7sa.js";
|
|
8
|
-
import"./index-tdrv6nd0.js";
|
|
9
|
-
import"./index-y5145x8k.js";
|
|
10
|
-
import"./index-bmzt6xgy.js";
|
|
11
|
-
import"./index-9r1cbh9e.js";
|
|
12
|
-
import"./index-jdxxq8cz.js";
|
|
13
|
-
import"./index-febg06dy.js";
|
|
14
|
-
import"./index-wgzyeyf5.js";
|
|
15
|
-
import"./index-6z9g64js.js";
|
|
16
|
-
export {
|
|
17
|
-
runManagedWrapup2 as runManagedWrapup
|
|
18
|
-
};
|