@cortexkit/opencode-magic-context 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/agent-disable.d.ts +22 -0
- package/dist/config/agent-disable.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +7 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +144 -24
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/transform-mode.d.ts +2 -0
- package/dist/config/transform-mode.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-bwa99a04.js → conflict-warning-hook-fvpfvt0w.js} +1 -1
- package/dist/{derive-budgets-e9j09sjr.js → derive-budgets-9crp0cvd.js} +1 -1
- package/dist/features/builtin-commands/commands.d.ts +1 -1
- package/dist/features/builtin-commands/commands.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +106 -0
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +0 -2
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.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-registry.d.ts +0 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/fail-closed-block.d.ts +10 -0
- package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/index.d.ts +2 -2
- package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts +0 -2
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts +0 -13
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +7 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts +1 -2
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/memory-migration.d.ts +0 -6
- package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +2 -6
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +0 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory.d.ts +1 -6
- package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
- package/dist/features/magic-context/memory/verification-paths.d.ts +0 -1
- package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
- package/dist/features/magic-context/mural/render-mural.d.ts.map +1 -1
- package/dist/features/magic-context/mural/storage-mural.d.ts +0 -1
- package/dist/features/magic-context/mural/storage-mural.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +8 -1
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +16 -2
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/schema-fence-probe.d.ts +28 -0
- package/dist/features/magic-context/schema-fence-probe.d.ts.map +1 -0
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/capabilities.d.ts +0 -2
- package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts +0 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/types.d.ts +0 -16
- package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +35 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +38 -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.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +0 -2
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/types.d.ts +0 -3
- package/dist/hooks/auto-update-checker/types.d.ts.map +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/child-session-spawn.d.ts +26 -0
- package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +2 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts +49 -0
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-off-transition.d.ts +96 -0
- package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -0
- package/dist/hooks/magic-context/compartment-parser.d.ts +12 -0
- package/dist/hooks/magic-context/compartment-parser.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +2 -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-mapping.d.ts +0 -12
- package/dist/hooks/magic-context/compartment-runner-mapping.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-trigger.d.ts +12 -5
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +47 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -2
- package/dist/hooks/magic-context/event-handler.d.ts +7 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +13 -21
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +2 -0
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -1
- package/dist/hooks/magic-context/historian-prompt.generated.d.ts +1 -1
- package/dist/hooks/magic-context/historian-prompt.generated.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +1 -0
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +11 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +18 -0
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +3 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts +20 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/sentinel.d.ts +39 -0
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +19 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +30 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +27 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-kxz4tn6m.js → index-56yjaeb2.js} +56 -4
- package/dist/{index-bmkea2dq.js → index-b563sga5.js} +40 -287
- package/dist/{index-y1c9tcm3.js → index-cn960ffd.js} +101 -20
- package/dist/{index-k5y60ymm.js → index-dtas91s5.js} +27 -6
- package/dist/index-fcyvznch.js +279 -0
- package/dist/{index-63dq2g4d.js → index-ry8rh8cv.js} +138 -24
- package/dist/{index-h86twgsp.js → index-s0mdn690.js} +2113 -1067
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1356 -627
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +19 -8
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts +10 -0
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +3 -7
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts +8 -0
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-yh7pepvx.js → recomp-orchestrator-8e82vmvb.js} +6 -5
- package/dist/send-session-notification-6njqewzk.js +20 -0
- package/dist/shared/conflict-detector.d.ts +38 -1
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/conflict-fixer.d.ts +18 -1
- package/dist/shared/conflict-fixer.d.ts.map +1 -1
- package/dist/shared/context-limit-provenance.d.ts +3 -0
- package/dist/shared/context-limit-provenance.d.ts.map +1 -0
- package/dist/shared/escalation-bands.d.ts +9 -0
- package/dist/shared/escalation-bands.d.ts.map +1 -0
- package/dist/shared/format-threshold.d.ts +1 -1
- package/dist/shared/models-dev-cache.d.ts +29 -3
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +46 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +9 -4
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/storage-permissions.d.ts +5 -0
- package/dist/shared/storage-permissions.d.ts.map +1 -0
- package/dist/{storage-meta-session-1phy0b1z.js → storage-meta-session-xt41zbw4.js} +2 -1
- package/dist/{task-config-dn9k9szr.js → task-config-5nx3qfz8.js} +1 -2
- package/dist/tui/compaction-off.d.ts +13 -0
- package/dist/tui/compaction-off.d.ts.map +1 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/dist/tui-compiled/compaction-off.d.ts +13 -0
- package/dist/tui-compiled/compaction-off.d.ts.map +1 -0
- package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
- package/package.json +8 -3
- package/src/agents/dreamer.ts +68 -0
- package/src/agents/hidden-agent-registrations.ts +331 -0
- package/src/agents/historian.ts +3 -0
- package/src/agents/language-directive.test.ts +138 -0
- package/src/agents/language-directive.ts +121 -0
- package/src/agents/magic-context-prompt.language.test.ts +35 -0
- package/src/agents/magic-context-prompt.test.ts +292 -0
- package/src/agents/magic-context-prompt.ts +182 -0
- package/src/agents/permissions.test.ts +252 -0
- package/src/agents/permissions.ts +186 -0
- package/src/agents/sidekick.ts +1 -0
- package/src/agents/smart-note-compiler.ts +1 -0
- package/src/config/agent-disable.ts +106 -0
- package/src/config/compaction-accessor-guard.test.ts +82 -0
- package/src/config/compaction-backcompat.test.ts +52 -0
- package/src/config/index.test.ts +904 -0
- package/src/config/index.ts +601 -0
- package/src/config/migrate-config-location.test.ts +332 -0
- package/src/config/migrate-config-location.ts +635 -0
- package/src/config/migrate-dreamer-v2.test.ts +287 -0
- package/src/config/migrate-dreamer-v2.ts +336 -0
- package/src/config/migrate-experimental.ts +159 -0
- package/src/config/project-security.test.ts +366 -0
- package/src/config/project-security.ts +588 -0
- package/src/config/prune-config-leaf.test.ts +78 -0
- package/src/config/prune-config-leaf.ts +68 -0
- package/src/config/removed-config-keys.test.ts +116 -0
- package/src/config/schema/agent-overrides.ts +45 -0
- package/src/config/schema/magic-context.test.ts +358 -0
- package/src/config/schema/magic-context.ts +1002 -0
- package/src/config/transform-mode.test.ts +62 -0
- package/src/config/transform-mode.ts +34 -0
- package/src/config/variable.test.ts +323 -0
- package/src/config/variable.ts +192 -0
- package/src/features/magic-context/defaults.ts +1 -0
- package/src/features/magic-context/dreamer/cron.ts +243 -0
- package/src/features/magic-context/dreamer/task-registry.ts +104 -0
- package/src/shared/conflict-detector.test.ts +251 -1
- package/src/shared/conflict-detector.ts +84 -5
- package/src/shared/conflict-fixer.test.ts +309 -0
- package/src/shared/conflict-fixer.ts +82 -5
- package/src/shared/context-limit-provenance.ts +6 -0
- package/src/shared/escalation-bands.test.ts +23 -0
- package/src/shared/escalation-bands.ts +18 -0
- package/src/shared/format-threshold.ts +1 -1
- package/src/shared/models-dev-cache.test.ts +171 -0
- package/src/shared/models-dev-cache.ts +195 -38
- package/src/shared/rpc-server.ts +27 -18
- package/src/shared/rpc-types.ts +46 -1
- package/src/shared/sqlite.ts +13 -33
- package/src/shared/storage-permissions.ts +20 -0
- package/src/tui/compaction-off.test.ts +56 -0
- package/src/tui/compaction-off.ts +29 -0
- package/src/tui/data/context-db.ts +6 -0
- package/src/tui/index.tsx +113 -65
- package/src/tui/slots/sidebar-content.tsx +139 -83
- package/src/tui-compiled/compaction-off.ts +29 -0
- package/src/tui-compiled/data/context-db.ts +6 -0
- package/src/tui-compiled/index.tsx +558 -455
- package/src/tui-compiled/slots/sidebar-content.tsx +237 -161
- package/dist/config/schema.d.ts +0 -3
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/features/magic-context/plugin-messages.d.ts +0 -75
- package/dist/features/magic-context/plugin-messages.d.ts.map +0 -1
- package/dist/hooks/magic-context/compartment-runner-state-xml.d.ts +0 -21
- package/dist/hooks/magic-context/compartment-runner-state-xml.d.ts.map +0 -1
- package/dist/index-anhrfygk.js +0 -57
- package/dist/send-session-notification-tghq9gnh.js +0 -11
- package/dist/shared/opencode-compaction-detector.d.ts +0 -2
- package/dist/shared/opencode-compaction-detector.d.ts.map +0 -1
- package/dist/shared/transcript-opencode.d.ts +0 -71
- package/dist/shared/transcript-opencode.d.ts.map +0 -1
- package/dist/tools/index.d.ts +0 -6
- package/dist/tools/index.d.ts.map +0 -1
- package/src/shared/opencode-compaction-detector.test.ts +0 -239
- package/src/shared/opencode-compaction-detector.ts +0 -81
- package/src/shared/transcript-opencode.ts +0 -302
- /package/dist/{index-v7fmc1s2.js → index-ygcng9t8.js} +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deriveTriggerBudget,
|
|
3
|
-
modelSupportsVision
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./index-anhrfygk.js";
|
|
3
|
+
modelSupportsVision,
|
|
4
|
+
piModelRefToCanonical,
|
|
5
|
+
shouldEnforcePrivateStoragePermissions
|
|
6
|
+
} from "./index-ry8rh8cv.js";
|
|
8
7
|
import {
|
|
9
8
|
parseProviderModel
|
|
10
9
|
} from "./index-td9depx5.js";
|
|
@@ -12,18 +11,15 @@ import {
|
|
|
12
11
|
describeError,
|
|
13
12
|
getErrorMessage,
|
|
14
13
|
sendIgnoredMessage
|
|
15
|
-
} from "./index-
|
|
14
|
+
} from "./index-cn960ffd.js";
|
|
16
15
|
import {
|
|
17
|
-
Database,
|
|
18
16
|
clearCachedM0M1,
|
|
19
17
|
clearCompressionDepth,
|
|
20
18
|
clearCompressionDepthRange,
|
|
21
|
-
closeQuietly,
|
|
22
19
|
ensureSessionMetaRow,
|
|
23
20
|
estimateTokens,
|
|
24
21
|
getCachedAbsoluteMessageCount,
|
|
25
22
|
getLegacyProtectedTailStartOrdinal,
|
|
26
|
-
getMessageTimesFromOpenCodeDb,
|
|
27
23
|
getRawSessionMessageCount,
|
|
28
24
|
getRawSessionTagKeysThrough,
|
|
29
25
|
hasMeaningfulUserText,
|
|
@@ -32,10 +28,17 @@ import {
|
|
|
32
28
|
persistCachedM0,
|
|
33
29
|
readRawSessionMessages,
|
|
34
30
|
readSessionChunk,
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
updateSessionMeta
|
|
32
|
+
} from "./index-b563sga5.js";
|
|
33
|
+
import {
|
|
34
|
+
Database,
|
|
35
|
+
closeQuietly,
|
|
36
|
+
getMessageTimesFromOpenCodeDb,
|
|
37
37
|
withPrivilegedWriter
|
|
38
|
-
} from "./index-
|
|
38
|
+
} from "./index-fcyvznch.js";
|
|
39
|
+
import {
|
|
40
|
+
pushNotification
|
|
41
|
+
} from "./index-d62ypazr.js";
|
|
39
42
|
import {
|
|
40
43
|
log,
|
|
41
44
|
sessionLog
|
|
@@ -376,462 +379,95 @@ function buildPrimaryLanguageDirective(language) {
|
|
|
376
379
|
return "";
|
|
377
380
|
return `Use ${target} for your natural-language replies to the user unless the user explicitly asks for another language. Keep code, identifiers, file paths, commands, logs, and quoted text verbatim.`;
|
|
378
381
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
/context size has been exceeded/i
|
|
401
|
-
];
|
|
402
|
-
var LIMIT_EXTRACTION_PATTERNS = [
|
|
403
|
-
/maximum prompt length is (\d+)/i,
|
|
404
|
-
/maximum context length is (\d+) tokens?/i,
|
|
405
|
-
/context length is only (\d+) tokens?/i,
|
|
406
|
-
/exceeds the limit of (\d+)/i,
|
|
407
|
-
/too large for model with (\d+) maximum context length/i,
|
|
408
|
-
/context size.*(\d+) tokens?/i,
|
|
409
|
-
/exceeds? the context length of (\d+)/i,
|
|
410
|
-
/>\s*(\d+)\s*(?:tokens?\s*)?(?:maximum|max|limit)\b/i,
|
|
411
|
-
/max(?:imum)?.*context.*?(\d+)/i
|
|
412
|
-
];
|
|
413
|
-
var MIN_PLAUSIBLE_LIMIT = 1024;
|
|
414
|
-
var MAX_PLAUSIBLE_LIMIT = 1e7;
|
|
415
|
-
function extractErrorMessage(error) {
|
|
416
|
-
if (!error)
|
|
417
|
-
return "";
|
|
418
|
-
if (typeof error === "string")
|
|
419
|
-
return error;
|
|
420
|
-
if (typeof error === "object") {
|
|
421
|
-
const obj = error;
|
|
422
|
-
const nested = obj.error;
|
|
423
|
-
if (nested && typeof nested.message === "string" && nested.message.length > 0) {
|
|
424
|
-
return nested.message;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
if (error instanceof Error)
|
|
428
|
-
return error.message;
|
|
429
|
-
if (typeof error === "object") {
|
|
430
|
-
const obj = error;
|
|
431
|
-
if (typeof obj.message === "string")
|
|
432
|
-
return obj.message;
|
|
433
|
-
if (typeof obj.responseBody === "string")
|
|
434
|
-
return obj.responseBody;
|
|
435
|
-
try {
|
|
436
|
-
return JSON.stringify(error);
|
|
437
|
-
} catch {
|
|
438
|
-
return String(error);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
return String(error);
|
|
382
|
+
|
|
383
|
+
// src/features/magic-context/storage-db.ts
|
|
384
|
+
import {
|
|
385
|
+
chmodSync,
|
|
386
|
+
copyFileSync,
|
|
387
|
+
cpSync,
|
|
388
|
+
existsSync as existsSync3,
|
|
389
|
+
mkdirSync,
|
|
390
|
+
mkdtempSync,
|
|
391
|
+
readdirSync,
|
|
392
|
+
readFileSync,
|
|
393
|
+
unlinkSync
|
|
394
|
+
} from "node:fs";
|
|
395
|
+
import { basename, dirname, join as join3 } from "node:path";
|
|
396
|
+
|
|
397
|
+
// src/plugin/boot-quiet.ts
|
|
398
|
+
var BOOT_QUIET_MS = 120000;
|
|
399
|
+
var bootQuietUntilMs = 0;
|
|
400
|
+
function beginBootQuietPeriod(now = Date.now()) {
|
|
401
|
+
if (bootQuietUntilMs === 0)
|
|
402
|
+
bootQuietUntilMs = now + BOOT_QUIET_MS;
|
|
442
403
|
}
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
if (!message) {
|
|
446
|
-
return { isOverflow: false };
|
|
447
|
-
}
|
|
448
|
-
const hasStatus413 = /\b413\b/.test(message) && /(entity|payload|context|prompt)/i.test(message);
|
|
449
|
-
let matched;
|
|
450
|
-
for (const pattern of OVERFLOW_PATTERNS) {
|
|
451
|
-
if (pattern.test(message)) {
|
|
452
|
-
matched = pattern;
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
if (!matched && !hasStatus413) {
|
|
457
|
-
return { isOverflow: false };
|
|
458
|
-
}
|
|
459
|
-
const reportedLimit = parseReportedLimit(message);
|
|
460
|
-
return {
|
|
461
|
-
isOverflow: true,
|
|
462
|
-
reportedLimit,
|
|
463
|
-
matchedPattern: matched?.source
|
|
464
|
-
};
|
|
404
|
+
function bootQuietRemainingMs(now = Date.now()) {
|
|
405
|
+
return Math.max(0, bootQuietUntilMs - now);
|
|
465
406
|
}
|
|
466
|
-
function
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const match = message.match(pattern);
|
|
471
|
-
if (!match)
|
|
472
|
-
continue;
|
|
473
|
-
const raw = match[1];
|
|
474
|
-
if (!raw)
|
|
475
|
-
continue;
|
|
476
|
-
const value = Number.parseInt(raw, 10);
|
|
477
|
-
if (!Number.isFinite(value))
|
|
478
|
-
continue;
|
|
479
|
-
if (value < MIN_PLAUSIBLE_LIMIT || value > MAX_PLAUSIBLE_LIMIT)
|
|
480
|
-
continue;
|
|
481
|
-
return value;
|
|
482
|
-
}
|
|
483
|
-
return;
|
|
407
|
+
function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
|
|
408
|
+
const timer = setTimeout(task, bootQuietRemainingMs() + Math.max(0, additionalDelayMs));
|
|
409
|
+
timer.unref?.();
|
|
410
|
+
return timer;
|
|
484
411
|
}
|
|
485
412
|
|
|
486
|
-
// src/shared/
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
413
|
+
// src/shared/rpc-utils.ts
|
|
414
|
+
import { createHash } from "node:crypto";
|
|
415
|
+
import { join } from "node:path";
|
|
416
|
+
function projectHash(directory) {
|
|
417
|
+
const normalized = directory.replace(/\/+$/, "");
|
|
418
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
490
419
|
}
|
|
491
|
-
function
|
|
492
|
-
|
|
493
|
-
return error;
|
|
494
|
-
if (error instanceof Error)
|
|
495
|
-
return error.message;
|
|
496
|
-
if (typeof error === "object" && error !== null) {
|
|
497
|
-
const obj = error;
|
|
498
|
-
if (typeof obj.message === "string")
|
|
499
|
-
return obj.message;
|
|
500
|
-
}
|
|
501
|
-
try {
|
|
502
|
-
return JSON.stringify(error);
|
|
503
|
-
} catch (_error) {
|
|
504
|
-
return String(error);
|
|
505
|
-
}
|
|
420
|
+
function rpcPortDir(storageDir, directory) {
|
|
421
|
+
return join(storageDir, "rpc", projectHash(directory));
|
|
506
422
|
}
|
|
507
|
-
function
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (typeof error === "object" && error !== null) {
|
|
511
|
-
const errObj = error;
|
|
512
|
-
if (errObj.name === "ProviderModelNotFoundError" && typeof errObj.data === "object" && errObj.data !== null) {
|
|
513
|
-
const data = errObj.data;
|
|
514
|
-
const suggestions = data.suggestions;
|
|
515
|
-
if (Array.isArray(suggestions) && typeof suggestions[0] === "string") {
|
|
516
|
-
return {
|
|
517
|
-
providerID: String(data.providerID ?? ""),
|
|
518
|
-
modelID: String(data.modelID ?? ""),
|
|
519
|
-
suggestion: suggestions[0]
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
for (const key of ["data", "error", "cause"]) {
|
|
524
|
-
const nested = errObj[key];
|
|
525
|
-
if (nested && typeof nested === "object") {
|
|
526
|
-
const result = parseModelSuggestion(nested);
|
|
527
|
-
if (result)
|
|
528
|
-
return result;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const message = extractMessage(error);
|
|
533
|
-
const modelMatch = message.match(/model not found:\s*([^/\s]+)\s*\/\s*([^.,\s]+)/i);
|
|
534
|
-
const suggestionMatch = message.match(/did you mean:\s*([^,?]+)/i);
|
|
535
|
-
if (!modelMatch || !suggestionMatch) {
|
|
536
|
-
return null;
|
|
537
|
-
}
|
|
538
|
-
return {
|
|
539
|
-
providerID: modelMatch[1].trim(),
|
|
540
|
-
modelID: modelMatch[2].trim(),
|
|
541
|
-
suggestion: suggestionMatch[1].trim()
|
|
542
|
-
};
|
|
423
|
+
function rpcPortFilePath(storageDir, directory, pid = process.pid, instanceId) {
|
|
424
|
+
const suffix = instanceId ? `-${instanceId}` : "";
|
|
425
|
+
return join(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
|
|
543
426
|
}
|
|
544
|
-
|
|
545
|
-
if (
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
const controller = new AbortController;
|
|
549
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
550
|
-
const onExternalAbort = () => controller.abort();
|
|
551
|
-
signal?.addEventListener("abort", onExternalAbort);
|
|
552
|
-
try {
|
|
553
|
-
await client.session.prompt({
|
|
554
|
-
...args,
|
|
555
|
-
signal: controller.signal
|
|
556
|
-
});
|
|
557
|
-
} catch (error) {
|
|
558
|
-
if (signal?.aborted) {
|
|
559
|
-
await abortChildRun(client, args.path.id);
|
|
560
|
-
throw new Error("prompt aborted by external signal");
|
|
561
|
-
}
|
|
562
|
-
if (controller.signal.aborted) {
|
|
563
|
-
await abortChildRun(client, args.path.id);
|
|
564
|
-
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
565
|
-
}
|
|
566
|
-
throw error;
|
|
567
|
-
} finally {
|
|
568
|
-
clearTimeout(timeout);
|
|
569
|
-
signal?.removeEventListener("abort", onExternalAbort);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
async function abortChildRun(client, sessionId) {
|
|
427
|
+
function isPidAlive(pid) {
|
|
428
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
429
|
+
return false;
|
|
573
430
|
try {
|
|
574
|
-
|
|
575
|
-
client.session.abort({ path: { id: sessionId } }),
|
|
576
|
-
new Promise((resolve) => setTimeout(resolve, ABORT_CALL_TIMEOUT_MS))
|
|
577
|
-
]);
|
|
578
|
-
} catch (error) {
|
|
579
|
-
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error)}`);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
function isNonRetryable(error, externalSignal) {
|
|
583
|
-
if (externalSignal?.aborted)
|
|
584
|
-
return true;
|
|
585
|
-
if (error instanceof Error) {
|
|
586
|
-
if (error.name === "AbortError")
|
|
587
|
-
return true;
|
|
588
|
-
if (error.message === "prompt aborted by external signal")
|
|
589
|
-
return true;
|
|
590
|
-
if (/^prompt timed out after \d+ms$/.test(error.message))
|
|
591
|
-
return true;
|
|
592
|
-
}
|
|
593
|
-
if (detectOverflow(error).isOverflow)
|
|
431
|
+
process.kill(pid, 0);
|
|
594
432
|
return true;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
function shortErr(error) {
|
|
598
|
-
if (error instanceof Error) {
|
|
599
|
-
return error.name && error.name !== "Error" ? `${error.name}: ${error.message}` : error.message;
|
|
600
|
-
}
|
|
601
|
-
return extractMessage(error);
|
|
602
|
-
}
|
|
603
|
-
async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
|
|
604
|
-
const originalBody = { ...args.body };
|
|
605
|
-
const attemptArgs = copyPromptArgs(args, originalBody);
|
|
606
|
-
try {
|
|
607
|
-
await promptWithTimeout(client, attemptArgs, timeoutMs, signal);
|
|
608
|
-
return;
|
|
609
|
-
} catch (error) {
|
|
610
|
-
if (isNonRetryable(error, signal))
|
|
611
|
-
throw error;
|
|
612
|
-
const suggestion = parseModelSuggestion(error);
|
|
613
|
-
if (!suggestion || !originalBody.model) {
|
|
614
|
-
throw error;
|
|
615
|
-
}
|
|
616
|
-
log(`[${callContext}] ${label}: model not found, retrying with suggestion`, {
|
|
617
|
-
original: `${suggestion.providerID}/${suggestion.modelID}`,
|
|
618
|
-
suggested: suggestion.suggestion
|
|
619
|
-
});
|
|
620
|
-
await promptWithTimeout(client, copyPromptArgs(args, {
|
|
621
|
-
...originalBody,
|
|
622
|
-
model: {
|
|
623
|
-
providerID: suggestion.providerID,
|
|
624
|
-
modelID: suggestion.suggestion
|
|
625
|
-
}
|
|
626
|
-
}), timeoutMs, signal);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
|
|
630
|
-
const timeoutMs = options.timeoutMs ?? 300000;
|
|
631
|
-
const callContext = options.callContext ?? "subagent";
|
|
632
|
-
const fallbacks = options.fallbackModels ?? [];
|
|
633
|
-
const baseBody = { ...args.body };
|
|
634
|
-
const baseArgs = copyPromptArgs(args, baseBody);
|
|
635
|
-
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
636
|
-
let lastError = null;
|
|
637
|
-
try {
|
|
638
|
-
await attemptOnce(client, baseArgs, timeoutMs, options.signal, callContext, explicitPrimaryLabel);
|
|
639
|
-
return;
|
|
640
|
-
} catch (error) {
|
|
641
|
-
lastError = error;
|
|
642
|
-
if (isNonRetryable(error, options.signal))
|
|
643
|
-
throw error;
|
|
644
|
-
if (fallbacks.length === 0) {
|
|
645
|
-
throw error;
|
|
646
|
-
}
|
|
647
|
-
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
648
|
-
}
|
|
649
|
-
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
650
|
-
const parsed = parseProviderModel(fallbacks[i]);
|
|
651
|
-
if (!parsed) {
|
|
652
|
-
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
653
|
-
continue;
|
|
654
|
-
}
|
|
655
|
-
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
656
|
-
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
657
|
-
...baseBody,
|
|
658
|
-
model: parsed
|
|
659
|
-
});
|
|
660
|
-
try {
|
|
661
|
-
await attemptOnce(client, attemptArgs, timeoutMs, options.signal, callContext, label);
|
|
662
|
-
log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
|
|
663
|
-
return;
|
|
664
|
-
} catch (error) {
|
|
665
|
-
lastError = error;
|
|
666
|
-
if (isNonRetryable(error, options.signal))
|
|
667
|
-
throw error;
|
|
668
|
-
const remaining = fallbacks.length - i - 1;
|
|
669
|
-
if (remaining > 0) {
|
|
670
|
-
log(`[${callContext}] ${label} failed: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
433
|
+
} catch (err) {
|
|
434
|
+
return err.code === "EPERM";
|
|
673
435
|
}
|
|
674
|
-
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; last error: ${shortErr(lastError)}`);
|
|
675
|
-
throw lastError ?? new Error("All fallback models failed");
|
|
676
436
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
async function promptSyncWithValidatedOutputRetry(client, args, options) {
|
|
684
|
-
const timeoutMs = options.timeoutMs ?? 300000;
|
|
685
|
-
const callContext = options.callContext ?? "subagent";
|
|
686
|
-
const fallbacks = options.fallbackModels ?? [];
|
|
687
|
-
const baseBody = { ...args.body };
|
|
688
|
-
const baseArgs = copyPromptArgs(args, baseBody);
|
|
689
|
-
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
690
|
-
const totalAttempts = fallbacks.length + 1;
|
|
691
|
-
let firstError = null;
|
|
692
|
-
let lastError = null;
|
|
693
|
-
try {
|
|
694
|
-
return await attemptAndValidate(client, baseArgs, timeoutMs, options.signal, callContext, {
|
|
695
|
-
label: explicitPrimaryLabel,
|
|
696
|
-
attemptIndex: 0,
|
|
697
|
-
isFallback: false,
|
|
698
|
-
totalAttempts,
|
|
699
|
-
model: baseBody.model
|
|
700
|
-
}, options);
|
|
701
|
-
} catch (error) {
|
|
702
|
-
firstError = error;
|
|
703
|
-
lastError = error;
|
|
704
|
-
if (isNonRetryable(error, options.signal))
|
|
705
|
-
throw error;
|
|
706
|
-
if (fallbacks.length === 0) {
|
|
707
|
-
throw error;
|
|
708
|
-
}
|
|
709
|
-
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
710
|
-
}
|
|
711
|
-
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
712
|
-
const parsed = parseProviderModel(fallbacks[i]);
|
|
713
|
-
if (!parsed) {
|
|
714
|
-
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
715
|
-
continue;
|
|
716
|
-
}
|
|
717
|
-
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
718
|
-
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
719
|
-
...baseBody,
|
|
720
|
-
model: parsed
|
|
721
|
-
});
|
|
722
|
-
const attempt = {
|
|
723
|
-
label,
|
|
724
|
-
attemptIndex: i + 1,
|
|
725
|
-
isFallback: true,
|
|
726
|
-
totalAttempts,
|
|
727
|
-
model: parsed
|
|
728
|
-
};
|
|
437
|
+
function parseRpcPortFile(content, fallbackPid = 0) {
|
|
438
|
+
const trimmed = content.trim();
|
|
439
|
+
if (!trimmed)
|
|
440
|
+
return null;
|
|
441
|
+
if (trimmed.startsWith("{")) {
|
|
729
442
|
try {
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
if (
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`);
|
|
746
|
-
throw firstError ?? lastError ?? new Error("All fallback models failed validation");
|
|
747
|
-
}
|
|
748
|
-
// src/shared/normalize-sdk-response.ts
|
|
749
|
-
function normalizeSDKResponse(response, fallback, options) {
|
|
750
|
-
if (response === null || response === undefined) {
|
|
751
|
-
return fallback;
|
|
752
|
-
}
|
|
753
|
-
if (Array.isArray(response)) {
|
|
754
|
-
return response;
|
|
755
|
-
}
|
|
756
|
-
if (typeof response === "object" && "data" in response) {
|
|
757
|
-
const data = response.data;
|
|
758
|
-
if (data !== null && data !== undefined) {
|
|
759
|
-
return data;
|
|
760
|
-
}
|
|
761
|
-
if (options?.preferResponseOnMissingData === true) {
|
|
762
|
-
return response;
|
|
443
|
+
const parsed = JSON.parse(trimmed);
|
|
444
|
+
const port2 = Number(parsed.port);
|
|
445
|
+
const pid = Number(parsed.pid);
|
|
446
|
+
const startedAt = Number(parsed.started_at);
|
|
447
|
+
if (!isValidPort(port2) || !Number.isInteger(pid) || pid <= 0)
|
|
448
|
+
return null;
|
|
449
|
+
return {
|
|
450
|
+
port: port2,
|
|
451
|
+
pid,
|
|
452
|
+
started_at: Number.isFinite(startedAt) ? startedAt : 0,
|
|
453
|
+
token: typeof parsed.token === "string" ? parsed.token : undefined,
|
|
454
|
+
instance_id: typeof parsed.instance_id === "string" ? parsed.instance_id : undefined
|
|
455
|
+
};
|
|
456
|
+
} catch {
|
|
457
|
+
return null;
|
|
763
458
|
}
|
|
764
|
-
return fallback;
|
|
765
|
-
}
|
|
766
|
-
if (options?.preferResponseOnMissingData === true) {
|
|
767
|
-
return response;
|
|
768
459
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
// src/shared/assistant-message-extractor.ts
|
|
772
|
-
function asSessionMessage(value) {
|
|
773
|
-
if (!isRecord(value))
|
|
460
|
+
const port = Number.parseInt(trimmed, 10);
|
|
461
|
+
if (!isValidPort(port))
|
|
774
462
|
return null;
|
|
775
|
-
|
|
776
|
-
const parts = value.parts;
|
|
777
|
-
return {
|
|
778
|
-
info: isRecord(info) ? {
|
|
779
|
-
role: typeof info.role === "string" ? info.role : undefined,
|
|
780
|
-
time: isRecord(info.time) ? {
|
|
781
|
-
created: typeof info.time.created === "number" ? info.time.created : undefined
|
|
782
|
-
} : undefined
|
|
783
|
-
} : undefined,
|
|
784
|
-
parts
|
|
785
|
-
};
|
|
463
|
+
return { port, pid: fallbackPid, started_at: 0 };
|
|
786
464
|
}
|
|
787
|
-
function
|
|
788
|
-
return
|
|
789
|
-
}
|
|
790
|
-
function getTextParts(message) {
|
|
791
|
-
if (!Array.isArray(message.parts))
|
|
792
|
-
return [];
|
|
793
|
-
return message.parts.filter((part) => isRecord(part)).map((part) => ({
|
|
794
|
-
type: typeof part.type === "string" ? part.type : undefined,
|
|
795
|
-
text: typeof part.text === "string" ? part.text : undefined
|
|
796
|
-
})).filter((part) => part.type === "text" && Boolean(part.text));
|
|
797
|
-
}
|
|
798
|
-
function extractLatestAssistantText(messages) {
|
|
799
|
-
if (!Array.isArray(messages) || messages.length === 0)
|
|
800
|
-
return null;
|
|
801
|
-
const assistantMessages = messages.map(asSessionMessage).filter((message) => message !== null).filter((message) => message.info?.role === "assistant").sort((a, b) => getCreatedTime(b) - getCreatedTime(a));
|
|
802
|
-
const latest = assistantMessages[0];
|
|
803
|
-
if (!latest)
|
|
804
|
-
return null;
|
|
805
|
-
return getTextParts(latest).map((part) => part.text).join(`
|
|
806
|
-
`) || null;
|
|
807
|
-
}
|
|
808
|
-
function hasLengthCappedOutput(value) {
|
|
809
|
-
if (Array.isArray(value))
|
|
810
|
-
return value.some((item) => hasLengthCappedOutput(item));
|
|
811
|
-
if (!isRecord(value))
|
|
812
|
-
return false;
|
|
813
|
-
if (value.length_capped === true || value.lengthCapped === true)
|
|
814
|
-
return true;
|
|
815
|
-
const finishReason = value.finish_reason ?? value.finishReason;
|
|
816
|
-
if (typeof finishReason === "string") {
|
|
817
|
-
const normalized = finishReason.toLowerCase();
|
|
818
|
-
if (normalized === "length" || normalized === "max_tokens" || normalized === "max_output_tokens") {
|
|
819
|
-
return true;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
return Object.values(value).some((item) => hasLengthCappedOutput(item));
|
|
465
|
+
function isValidPort(port) {
|
|
466
|
+
return Number.isInteger(port) && port > 0 && port <= 65535;
|
|
823
467
|
}
|
|
824
468
|
|
|
825
|
-
// src/shared/keep-subagents.ts
|
|
826
|
-
var keepSubagents = false;
|
|
827
|
-
function setKeepSubagents(value) {
|
|
828
|
-
keepSubagents = value === true;
|
|
829
|
-
}
|
|
830
|
-
function shouldKeepSubagents() {
|
|
831
|
-
return keepSubagents;
|
|
832
|
-
}
|
|
833
469
|
// src/features/magic-context/context-authority.ts
|
|
834
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
470
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
835
471
|
var AUTHORITY_DOMAINS = ["memories", "notes"];
|
|
836
472
|
var moduleNoteEvaluationBridges = new Map;
|
|
837
473
|
function registerModuleNoteEvaluationBridge(projectPath, bridge) {
|
|
@@ -963,7 +599,7 @@ function checksumAuthoritySeedRows(rows) {
|
|
|
963
599
|
const rightId = seedSourceRowId(right) ?? Number.MAX_SAFE_INTEGER;
|
|
964
600
|
return leftId - rightId;
|
|
965
601
|
});
|
|
966
|
-
return
|
|
602
|
+
return createHash2("sha256").update(JSON.stringify(ordered.map(canonicalizeSeedValue))).digest("hex");
|
|
967
603
|
}
|
|
968
604
|
function maxDomainRowId(db, domain, projectPath) {
|
|
969
605
|
const row = domain === "memories" ? db.prepare("SELECT COALESCE(MAX(rowid), 0) AS max_rowid FROM memories WHERE project_path = ?").get(projectPath) : db.prepare(`SELECT COALESCE(MAX(n.rowid), 0) AS max_rowid
|
|
@@ -1051,10 +687,14 @@ async function prepareAuthority(args) {
|
|
|
1051
687
|
domain,
|
|
1052
688
|
rows: page
|
|
1053
689
|
});
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
sourceRowId
|
|
1057
|
-
|
|
690
|
+
const identityByModuleRowId = new Map;
|
|
691
|
+
for (const [index, moduleRowId] of (seedResponse.module_row_ids ?? []).entries()) {
|
|
692
|
+
const sourceRowId = seedSourceRowId(page[index]);
|
|
693
|
+
if (sourceRowId === null)
|
|
694
|
+
continue;
|
|
695
|
+
identityByModuleRowId.set(moduleRowId, { moduleRowId, sourceRowId });
|
|
696
|
+
}
|
|
697
|
+
const identities = [...identityByModuleRowId.values()];
|
|
1058
698
|
if (identities.length > 0) {
|
|
1059
699
|
args.db.transaction(() => {
|
|
1060
700
|
for (const identity of identities) {
|
|
@@ -1955,6 +1595,185 @@ function pullMemoryMirrorOnce(args) {
|
|
|
1955
1595
|
mirrorFlights.set(args.module, flight);
|
|
1956
1596
|
return flight;
|
|
1957
1597
|
}
|
|
1598
|
+
|
|
1599
|
+
// src/hooks/magic-context/compartment-parser.ts
|
|
1600
|
+
var COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
|
|
1601
|
+
var ATTR_START_REGEX = /\bstart="(\d+)"/;
|
|
1602
|
+
var ATTR_END_REGEX = /\bend="(\d+)"/;
|
|
1603
|
+
var ATTR_TITLE_REGEX = /\btitle="([^"]*)"/;
|
|
1604
|
+
var ATTR_EPISODE_REGEX = /\bepisode_type="([^"]*)"/;
|
|
1605
|
+
var ATTR_IMPORTANCE_REGEX = /\bimportance="(\d+)"/;
|
|
1606
|
+
function makeTierOpenRegex(n) {
|
|
1607
|
+
return new RegExp(`<p${n}\\s*(/?)>`);
|
|
1608
|
+
}
|
|
1609
|
+
var TIER_OPEN_REGEXES = [
|
|
1610
|
+
makeTierOpenRegex(1),
|
|
1611
|
+
makeTierOpenRegex(2),
|
|
1612
|
+
makeTierOpenRegex(3),
|
|
1613
|
+
makeTierOpenRegex(4)
|
|
1614
|
+
];
|
|
1615
|
+
var TIER_CLOSE_ANY_REGEX = /<\/p\d/;
|
|
1616
|
+
var TIER_OPEN_ANY_REGEX = /<p\d/;
|
|
1617
|
+
var CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
|
|
1618
|
+
var FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
|
|
1619
|
+
var UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
|
|
1620
|
+
var USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
|
|
1621
|
+
var USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
|
|
1622
|
+
var PRIMER_CANDIDATES_REGEX = /<primer_candidates>(.*?)<\/primer_candidates>/s;
|
|
1623
|
+
var PRIMER_ELEMENT_REGEX = /<primer\s+at_compartment="(\d+)"\s*>(.*?)<\/primer>/gs;
|
|
1624
|
+
var PRIMER_ITEM_REGEX = /^\s*(?:\*|-|\d+\.)\s*(.+)$/gm;
|
|
1625
|
+
var FACTS_BLOCK_REGEX = /<facts>(.*?)<\/facts>/s;
|
|
1626
|
+
var EVENTS_BLOCK_REGEX = /<events>(.*?)<\/events>/s;
|
|
1627
|
+
var EVENT_ELEMENT_REGEX = /<([a-z_]+)\s+at_compartment="(\d+)"\s*>(.*?)<\/\1>/gs;
|
|
1628
|
+
var EVENT_FIELD_REGEX = /<([a-z_]+)\s*>(.*?)<\/\1>/gs;
|
|
1629
|
+
function extractTier(inner, index) {
|
|
1630
|
+
const openMatch = TIER_OPEN_REGEXES[index].exec(inner);
|
|
1631
|
+
if (!openMatch)
|
|
1632
|
+
return;
|
|
1633
|
+
if (openMatch[1] === "/")
|
|
1634
|
+
return "";
|
|
1635
|
+
const rest = inner.slice(openMatch.index + openMatch[0].length);
|
|
1636
|
+
const closeAt = rest.search(TIER_CLOSE_ANY_REGEX);
|
|
1637
|
+
let body = closeAt === -1 ? rest : rest.slice(0, closeAt);
|
|
1638
|
+
const openInside = body.search(TIER_OPEN_ANY_REGEX);
|
|
1639
|
+
if (openInside !== -1)
|
|
1640
|
+
body = body.slice(0, openInside);
|
|
1641
|
+
return unescapeXml(body.trim());
|
|
1642
|
+
}
|
|
1643
|
+
function extractTiersFromInner(inner) {
|
|
1644
|
+
return {
|
|
1645
|
+
p1: extractTier(inner, 0),
|
|
1646
|
+
p2: extractTier(inner, 1),
|
|
1647
|
+
p3: extractTier(inner, 2),
|
|
1648
|
+
p4: extractTier(inner, 3)
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function parseCompartmentOutput(text) {
|
|
1652
|
+
const compartments = [];
|
|
1653
|
+
const facts = [];
|
|
1654
|
+
for (const match of text.matchAll(COMPARTMENT_REGEX)) {
|
|
1655
|
+
const attrs = match[1];
|
|
1656
|
+
const inner = match[2];
|
|
1657
|
+
const startMatch = attrs.match(ATTR_START_REGEX);
|
|
1658
|
+
const endMatch = attrs.match(ATTR_END_REGEX);
|
|
1659
|
+
const titleMatch = attrs.match(ATTR_TITLE_REGEX);
|
|
1660
|
+
if (!startMatch || !endMatch || !titleMatch)
|
|
1661
|
+
continue;
|
|
1662
|
+
const startMessage = parseInt(startMatch[1], 10);
|
|
1663
|
+
const endMessage = parseInt(endMatch[1], 10);
|
|
1664
|
+
const title = unescapeXml(titleMatch[1]);
|
|
1665
|
+
if (Number.isNaN(startMessage) || Number.isNaN(endMessage) || !title)
|
|
1666
|
+
continue;
|
|
1667
|
+
const episodeMatch = attrs.match(ATTR_EPISODE_REGEX);
|
|
1668
|
+
const importanceMatch = attrs.match(ATTR_IMPORTANCE_REGEX);
|
|
1669
|
+
const episodeType = episodeMatch ? unescapeXml(episodeMatch[1]) : undefined;
|
|
1670
|
+
const importance = importanceMatch ? parseInt(importanceMatch[1], 10) : undefined;
|
|
1671
|
+
const p1 = extractTier(inner, 0);
|
|
1672
|
+
if (typeof p1 === "string" && p1.length > 0) {
|
|
1673
|
+
const p2 = extractTier(inner, 1);
|
|
1674
|
+
const p3 = extractTier(inner, 2);
|
|
1675
|
+
const p4 = extractTier(inner, 3);
|
|
1676
|
+
compartments.push({
|
|
1677
|
+
startMessage,
|
|
1678
|
+
endMessage,
|
|
1679
|
+
title,
|
|
1680
|
+
content: p1,
|
|
1681
|
+
p1,
|
|
1682
|
+
p2: typeof p2 === "string" ? p2 : p1,
|
|
1683
|
+
p3: typeof p3 === "string" ? p3 : typeof p2 === "string" ? p2 : p1,
|
|
1684
|
+
p4: typeof p4 === "string" ? p4 : "",
|
|
1685
|
+
importance,
|
|
1686
|
+
episodeType
|
|
1687
|
+
});
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
const content = unescapeXml(inner.trim());
|
|
1691
|
+
if (content) {
|
|
1692
|
+
compartments.push({
|
|
1693
|
+
startMessage,
|
|
1694
|
+
endMessage,
|
|
1695
|
+
title,
|
|
1696
|
+
content,
|
|
1697
|
+
importance,
|
|
1698
|
+
episodeType
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
const factsBlockMatch = text.match(FACTS_BLOCK_REGEX);
|
|
1703
|
+
const factsScope = factsBlockMatch ? factsBlockMatch[1] : text.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
|
|
1704
|
+
for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
|
|
1705
|
+
const category = categoryMatch[1];
|
|
1706
|
+
const blockContent = categoryMatch[2];
|
|
1707
|
+
for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
|
|
1708
|
+
const content = unescapeXml(itemMatch[1].trim());
|
|
1709
|
+
if (content) {
|
|
1710
|
+
facts.push({ category, content });
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
const unprocessedMatch = text.match(UNPROCESSED_REGEX);
|
|
1715
|
+
const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
|
|
1716
|
+
const userObservations = [];
|
|
1717
|
+
const userObsMatch = text.match(USER_OBSERVATIONS_REGEX);
|
|
1718
|
+
if (userObsMatch) {
|
|
1719
|
+
for (const itemMatch of userObsMatch[1].matchAll(USER_OBS_ITEM_REGEX)) {
|
|
1720
|
+
const obs = unescapeXml(itemMatch[1].trim());
|
|
1721
|
+
if (obs)
|
|
1722
|
+
userObservations.push(obs);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
const primerCandidates = [];
|
|
1726
|
+
const primerMatch = text.match(PRIMER_CANDIDATES_REGEX);
|
|
1727
|
+
if (primerMatch) {
|
|
1728
|
+
const block = primerMatch[1];
|
|
1729
|
+
let sawElement = false;
|
|
1730
|
+
for (const el of block.matchAll(PRIMER_ELEMENT_REGEX)) {
|
|
1731
|
+
sawElement = true;
|
|
1732
|
+
const question = unescapeXml(el[2].trim());
|
|
1733
|
+
if (question) {
|
|
1734
|
+
primerCandidates.push({
|
|
1735
|
+
question,
|
|
1736
|
+
originCompartmentIndex: Number.parseInt(el[1], 10)
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
if (!sawElement) {
|
|
1741
|
+
for (const itemMatch of block.matchAll(PRIMER_ITEM_REGEX)) {
|
|
1742
|
+
const question = unescapeXml(itemMatch[1].trim());
|
|
1743
|
+
if (question)
|
|
1744
|
+
primerCandidates.push({ question });
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
const events = parseEvents(text);
|
|
1749
|
+
compartments.sort((a, b) => a.startMessage - b.startMessage);
|
|
1750
|
+
return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
|
|
1751
|
+
}
|
|
1752
|
+
function parseEvents(text) {
|
|
1753
|
+
const blockMatch = text.match(EVENTS_BLOCK_REGEX);
|
|
1754
|
+
if (!blockMatch)
|
|
1755
|
+
return [];
|
|
1756
|
+
const block = blockMatch[1];
|
|
1757
|
+
const events = [];
|
|
1758
|
+
for (const elMatch of block.matchAll(EVENT_ELEMENT_REGEX)) {
|
|
1759
|
+
const kind = elMatch[1];
|
|
1760
|
+
const atRaw = parseInt(elMatch[2], 10);
|
|
1761
|
+
const atCompartment = Number.isNaN(atRaw) ? null : atRaw;
|
|
1762
|
+
const fields = {};
|
|
1763
|
+
for (const fieldMatch of elMatch[3].matchAll(EVENT_FIELD_REGEX)) {
|
|
1764
|
+
const name = fieldMatch[1];
|
|
1765
|
+
const value = unescapeXml(fieldMatch[2].trim());
|
|
1766
|
+
if (value)
|
|
1767
|
+
fields[name] = value;
|
|
1768
|
+
}
|
|
1769
|
+
events.push({ kind, atCompartment, fields });
|
|
1770
|
+
}
|
|
1771
|
+
return events;
|
|
1772
|
+
}
|
|
1773
|
+
function unescapeXml(s) {
|
|
1774
|
+
return s.replace(/&/g, "&").replace(/'/g, "'").replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">");
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1958
1777
|
// src/features/magic-context/storage-schema-helpers.ts
|
|
1959
1778
|
function ensureColumn(db, table, column, definition) {
|
|
1960
1779
|
if (!/^[a-z][a-z0-9_]*$/.test(table) || !/^[a-z][a-z0-9_]*$/.test(column) || !/^[A-Z0-9_"'(),[\]\s]+$/i.test(definition)) {
|
|
@@ -2047,7 +1866,7 @@ function healMissingMemoryBlockIds(db, columns) {
|
|
|
2047
1866
|
}
|
|
2048
1867
|
|
|
2049
1868
|
// src/features/magic-context/workspaces.ts
|
|
2050
|
-
import { createHash as
|
|
1869
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
2051
1870
|
|
|
2052
1871
|
// src/features/magic-context/memory/constants.ts
|
|
2053
1872
|
var V2_MEMORY_CATEGORIES = [
|
|
@@ -2101,7 +1920,7 @@ var CATEGORY_DEFAULT_TTL = {
|
|
|
2101
1920
|
|
|
2102
1921
|
// src/features/magic-context/memory/project-identity.ts
|
|
2103
1922
|
import { execFileSync } from "node:child_process";
|
|
2104
|
-
import { createHash as
|
|
1923
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2105
1924
|
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
2106
1925
|
import { homedir } from "node:os";
|
|
2107
1926
|
import path from "node:path";
|
|
@@ -2116,6 +1935,7 @@ var dubiousOwnershipLoggedDirectories = new Set;
|
|
|
2116
1935
|
var dubiousOwnershipWarnedDirectories = new Set;
|
|
2117
1936
|
var transientGitIdentityReuseLoggedDirectories = new Set;
|
|
2118
1937
|
var execFileSyncForIdentity = execFileSync;
|
|
1938
|
+
var userHomeDirectoryForIdentity = () => homedir();
|
|
2119
1939
|
var nowMs = () => Date.now();
|
|
2120
1940
|
|
|
2121
1941
|
class ProjectIdentityError extends Error {
|
|
@@ -2169,7 +1989,7 @@ function getErrorStderr(error) {
|
|
|
2169
1989
|
}
|
|
2170
1990
|
function directoryFallback(directory) {
|
|
2171
1991
|
const canonical = path.resolve(directory);
|
|
2172
|
-
const hash =
|
|
1992
|
+
const hash = createHash3("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
|
|
2173
1993
|
return `dir:${hash}`;
|
|
2174
1994
|
}
|
|
2175
1995
|
function assertDirectoryUsable(canonicalDirectory, rawDirectory) {
|
|
@@ -2320,9 +2140,12 @@ function takeDubiousOwnershipProjectIdentityWarning(directory) {
|
|
|
2320
2140
|
dubiousOwnershipWarnedDirectories.add(canonical);
|
|
2321
2141
|
return formatDubiousOwnershipWarning(canonical);
|
|
2322
2142
|
}
|
|
2143
|
+
function canonicalUserHomeDirectory() {
|
|
2144
|
+
return realpathSync.native(userHomeDirectoryForIdentity());
|
|
2145
|
+
}
|
|
2323
2146
|
function isUserHomeDirectory(directory) {
|
|
2324
2147
|
try {
|
|
2325
|
-
return realpathSync.native(path.resolve(directory)) ===
|
|
2148
|
+
return realpathSync.native(path.resolve(directory)) === canonicalUserHomeDirectory();
|
|
2326
2149
|
} catch {
|
|
2327
2150
|
return false;
|
|
2328
2151
|
}
|
|
@@ -2391,22 +2214,52 @@ function hasGitDir(canonical) {
|
|
|
2391
2214
|
return false;
|
|
2392
2215
|
}
|
|
2393
2216
|
}
|
|
2394
|
-
function
|
|
2217
|
+
function gitRootInAncestorChain(startDirectory) {
|
|
2395
2218
|
let current = startDirectory;
|
|
2396
2219
|
while (true) {
|
|
2397
2220
|
if (existsSync(path.join(current, ".git"))) {
|
|
2398
|
-
|
|
2221
|
+
try {
|
|
2222
|
+
return realpathSync.native(current);
|
|
2223
|
+
} catch {
|
|
2224
|
+
return path.resolve(current);
|
|
2225
|
+
}
|
|
2399
2226
|
}
|
|
2400
2227
|
const parent = path.dirname(current);
|
|
2401
2228
|
if (parent === current) {
|
|
2402
|
-
return
|
|
2229
|
+
return null;
|
|
2403
2230
|
}
|
|
2404
2231
|
current = parent;
|
|
2405
2232
|
}
|
|
2406
2233
|
}
|
|
2407
|
-
function
|
|
2408
|
-
|
|
2409
|
-
|
|
2234
|
+
function hasGitDirInAncestorChain(startDirectory) {
|
|
2235
|
+
return gitRootInAncestorChain(startDirectory) !== null;
|
|
2236
|
+
}
|
|
2237
|
+
function gitRootDirectory(canonical) {
|
|
2238
|
+
const direct = gitRootInAncestorChain(canonical);
|
|
2239
|
+
if (direct)
|
|
2240
|
+
return direct;
|
|
2241
|
+
try {
|
|
2242
|
+
const realCanonical = realpathSync.native(canonical);
|
|
2243
|
+
return realCanonical === canonical ? null : gitRootInAncestorChain(realCanonical);
|
|
2244
|
+
} catch {
|
|
2245
|
+
return null;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
function resolveProjectIdentityForSession(directory, allowHomeProject = false) {
|
|
2249
|
+
const canonicalHome = canonicalUserHomeDirectory();
|
|
2250
|
+
const canonicalDirectory = (() => {
|
|
2251
|
+
try {
|
|
2252
|
+
return realpathSync.native(path.resolve(directory));
|
|
2253
|
+
} catch {
|
|
2254
|
+
return path.resolve(directory);
|
|
2255
|
+
}
|
|
2256
|
+
})();
|
|
2257
|
+
const inheritsHomeRepository = gitRootDirectory(canonicalDirectory) === canonicalHome;
|
|
2258
|
+
if (canonicalDirectory === canonicalHome || inheritsHomeRepository) {
|
|
2259
|
+
if (!allowHomeProject)
|
|
2260
|
+
return;
|
|
2261
|
+
return directoryFallback(canonicalHome);
|
|
2262
|
+
}
|
|
2410
2263
|
return resolveProjectIdentityOrFallback(directory);
|
|
2411
2264
|
}
|
|
2412
2265
|
function normalizeStoredProjectPath(rawOrStored) {
|
|
@@ -2608,7 +2461,7 @@ function computeWorkspaceEpochFingerprint(db, identities) {
|
|
|
2608
2461
|
const canonical = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
2609
2462
|
const epochs = getEpochMap(db, canonical);
|
|
2610
2463
|
const shareCategories = selectWorkspaceShareCategories(db, canonical);
|
|
2611
|
-
const hash =
|
|
2464
|
+
const hash = createHash4("sha256");
|
|
2612
2465
|
hash.update("share_categories", "utf8");
|
|
2613
2466
|
hash.update("\x00");
|
|
2614
2467
|
hash.update(shareCategories === null ? "NO_WORKSPACE" : JSON.stringify(shareCategories), "utf8");
|
|
@@ -2704,15 +2557,38 @@ function isSqliteLockError(error) {
|
|
|
2704
2557
|
function tableExists2(db, name) {
|
|
2705
2558
|
return Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
|
|
2706
2559
|
}
|
|
2560
|
+
function healMismatchedTierClose(db, table, hasLegacy) {
|
|
2561
|
+
if (!tableExists2(db, table))
|
|
2562
|
+
return;
|
|
2563
|
+
const columns = new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name));
|
|
2564
|
+
for (const required of ["content", "p1", "p2", "p3", "p4"]) {
|
|
2565
|
+
if (!columns.has(required))
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
if (hasLegacy && !columns.has("legacy"))
|
|
2569
|
+
return;
|
|
2570
|
+
const predicate = hasLegacy ? "legacy = 1 AND p1 IS NULL AND content LIKE '%<p1%'" : "p1 IS NULL AND content LIKE '%<p1%'";
|
|
2571
|
+
const rows = db.prepare(`SELECT id, content FROM ${table} WHERE ${predicate}`).all();
|
|
2572
|
+
const update = db.prepare(`UPDATE ${table} SET p1 = ?, p2 = ?, p3 = ?, p4 = ?${hasLegacy ? ", legacy = 0" : ""} WHERE id = ?`);
|
|
2573
|
+
for (const row of rows) {
|
|
2574
|
+
const tiers = extractTiersFromInner(row.content);
|
|
2575
|
+
if (typeof tiers.p1 !== "string" || tiers.p1.length === 0)
|
|
2576
|
+
continue;
|
|
2577
|
+
const p1 = tiers.p1;
|
|
2578
|
+
const p2 = typeof tiers.p2 === "string" ? tiers.p2 : p1;
|
|
2579
|
+
const p3 = typeof tiers.p3 === "string" ? tiers.p3 : p2;
|
|
2580
|
+
const p4 = typeof tiers.p4 === "string" ? tiers.p4 : "";
|
|
2581
|
+
update.run(p1, p2, p3, p4, row.id);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2707
2584
|
function assertForeignKeyIntegrity(db, table) {
|
|
2708
2585
|
const rows = (table ? db.prepare(`PRAGMA foreign_key_check(${table})`) : db.prepare("PRAGMA foreign_key_check")).all();
|
|
2709
2586
|
if (rows.length > 0) {
|
|
2710
2587
|
throw new Error(`foreign_key_check failed after embedding table rebuild${table ? ` (${table})` : ""} (${rows.length} violation(s))`);
|
|
2711
2588
|
}
|
|
2712
2589
|
}
|
|
2713
|
-
function authorityPrivilegeCheck(
|
|
2714
|
-
|
|
2715
|
-
return typeof native.function === "function" || typeof native.createFunction === "function" ? "mc_privileged_writer() = 0" : "COALESCE((SELECT enabled FROM context_privilege_state WHERE id = 1), 0) = 0";
|
|
2590
|
+
function authorityPrivilegeCheck() {
|
|
2591
|
+
return "COALESCE((SELECT enabled FROM context_privilege_state WHERE id = 1), 0) = 0";
|
|
2716
2592
|
}
|
|
2717
2593
|
function managedAuthorityNoteRow(row) {
|
|
2718
2594
|
return `(
|
|
@@ -2731,7 +2607,7 @@ function managedAuthorityNoteRow(row) {
|
|
|
2731
2607
|
)`;
|
|
2732
2608
|
}
|
|
2733
2609
|
function installLatestAuthorityTriggers(db) {
|
|
2734
|
-
const privilegeCheck = authorityPrivilegeCheck(
|
|
2610
|
+
const privilegeCheck = authorityPrivilegeCheck();
|
|
2735
2611
|
if (tableExists2(db, "memories")) {
|
|
2736
2612
|
db.exec(`
|
|
2737
2613
|
DROP TRIGGER IF EXISTS memories_authority_guard_insert;
|
|
@@ -4657,6 +4533,48 @@ var MIGRATIONS = [
|
|
|
4657
4533
|
ON memory_mutation_log(project_path, target_memory_id, id);
|
|
4658
4534
|
`);
|
|
4659
4535
|
}
|
|
4536
|
+
},
|
|
4537
|
+
{
|
|
4538
|
+
version: 70,
|
|
4539
|
+
description: "heal legacy compartments stranded by mismatched tier closing tags (issue #246)",
|
|
4540
|
+
up(db) {
|
|
4541
|
+
healMismatchedTierClose(db, "compartments", true);
|
|
4542
|
+
healMismatchedTierClose(db, "recomp_compartments", false);
|
|
4543
|
+
}
|
|
4544
|
+
},
|
|
4545
|
+
{
|
|
4546
|
+
version: 71,
|
|
4547
|
+
description: "rebuild authority guard triggers to the durable state-table form (issue #253)",
|
|
4548
|
+
up(db) {
|
|
4549
|
+
installLatestAuthorityTriggers(db);
|
|
4550
|
+
}
|
|
4551
|
+
},
|
|
4552
|
+
{
|
|
4553
|
+
version: 72,
|
|
4554
|
+
description: "add per-session compaction mode record column (issue #266)",
|
|
4555
|
+
up(db) {
|
|
4556
|
+
if (tableExists2(db, "session_meta")) {
|
|
4557
|
+
ensureColumn(db, "session_meta", "compaction_mode_record", "TEXT");
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
},
|
|
4561
|
+
{
|
|
4562
|
+
version: 73,
|
|
4563
|
+
description: "persist the last successful todowrite permission verdict",
|
|
4564
|
+
up(db) {
|
|
4565
|
+
if (tableExists2(db, "session_meta")) {
|
|
4566
|
+
ensureColumn(db, "session_meta", "todo_permission_denied", "INTEGER NOT NULL DEFAULT 2");
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
},
|
|
4570
|
+
{
|
|
4571
|
+
version: 74,
|
|
4572
|
+
description: "persist detected context-limit provenance",
|
|
4573
|
+
up(db) {
|
|
4574
|
+
if (tableExists2(db, "session_meta")) {
|
|
4575
|
+
ensureColumn(db, "session_meta", "detected_context_limit_provenance", "TEXT NOT NULL DEFAULT 'unknown'");
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4660
4578
|
}
|
|
4661
4579
|
];
|
|
4662
4580
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -4766,151 +4684,6 @@ async function runMigrationsWithRetry(db, options = {}) {
|
|
|
4766
4684
|
}
|
|
4767
4685
|
}
|
|
4768
4686
|
}
|
|
4769
|
-
// src/features/magic-context/project-docs-hash.ts
|
|
4770
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
4771
|
-
import { lstatSync, readFileSync, statSync as statSync2 } from "node:fs";
|
|
4772
|
-
import path2 from "node:path";
|
|
4773
|
-
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
4774
|
-
var PROJECT_DOCS_DELIMITER = `
|
|
4775
|
-
|
|
4776
|
-
---
|
|
4777
|
-
|
|
4778
|
-
`;
|
|
4779
|
-
var MAX_PROJECT_DOC_BYTES = 256 * 1024;
|
|
4780
|
-
var docsCache = new Map;
|
|
4781
|
-
function canonicalizeDocContent(raw) {
|
|
4782
|
-
return raw.replace(/^\uFEFF/, "").replace(/\r\n/g, `
|
|
4783
|
-
`).split(`
|
|
4784
|
-
`).map((line) => line.replace(/[ \t]+$/, "")).join(`
|
|
4785
|
-
`).replace(/\n+$/, "");
|
|
4786
|
-
}
|
|
4787
|
-
function fingerprintFile(filePath) {
|
|
4788
|
-
try {
|
|
4789
|
-
const stat = lstatSync(filePath);
|
|
4790
|
-
const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
|
|
4791
|
-
return {
|
|
4792
|
-
exists: isReadableDoc,
|
|
4793
|
-
mtimeMs: stat.mtimeMs,
|
|
4794
|
-
size: stat.size
|
|
4795
|
-
};
|
|
4796
|
-
} catch {
|
|
4797
|
-
return { exists: false, mtimeMs: 0, size: 0 };
|
|
4798
|
-
}
|
|
4799
|
-
}
|
|
4800
|
-
function readDirectoryMtimeMs(projectDirectory) {
|
|
4801
|
-
try {
|
|
4802
|
-
return statSync2(projectDirectory).mtimeMs;
|
|
4803
|
-
} catch {
|
|
4804
|
-
return 0;
|
|
4805
|
-
}
|
|
4806
|
-
}
|
|
4807
|
-
function fingerprintsEqual(a, b) {
|
|
4808
|
-
return a?.exists === b.exists && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
4809
|
-
}
|
|
4810
|
-
function cacheFilesEqual(cachedFiles, currentFiles) {
|
|
4811
|
-
for (const [filePath, current] of currentFiles) {
|
|
4812
|
-
if (!fingerprintsEqual(cachedFiles.get(filePath), current)) {
|
|
4813
|
-
return false;
|
|
4814
|
-
}
|
|
4815
|
-
}
|
|
4816
|
-
return true;
|
|
4817
|
-
}
|
|
4818
|
-
function readCurrentFingerprints(projectDirectory) {
|
|
4819
|
-
const files = new Map;
|
|
4820
|
-
for (const filename of PROJECT_DOC_FILES) {
|
|
4821
|
-
const filePath = path2.join(projectDirectory, filename);
|
|
4822
|
-
files.set(filePath, fingerprintFile(filePath));
|
|
4823
|
-
}
|
|
4824
|
-
return { directoryMtimeMs: readDirectoryMtimeMs(projectDirectory), files };
|
|
4825
|
-
}
|
|
4826
|
-
function readCanonicalPieces(projectDirectory, files) {
|
|
4827
|
-
const hashPieces = [];
|
|
4828
|
-
const renderedSections = [];
|
|
4829
|
-
for (const filename of PROJECT_DOC_FILES) {
|
|
4830
|
-
const filePath = path2.join(projectDirectory, filename);
|
|
4831
|
-
const fingerprint = files.get(filePath);
|
|
4832
|
-
if (!fingerprint?.exists) {
|
|
4833
|
-
continue;
|
|
4834
|
-
}
|
|
4835
|
-
let safeToRead = false;
|
|
4836
|
-
try {
|
|
4837
|
-
const st = lstatSync(filePath);
|
|
4838
|
-
safeToRead = st.isFile() && st.size <= MAX_PROJECT_DOC_BYTES;
|
|
4839
|
-
} catch {
|
|
4840
|
-
safeToRead = false;
|
|
4841
|
-
}
|
|
4842
|
-
if (!safeToRead) {
|
|
4843
|
-
continue;
|
|
4844
|
-
}
|
|
4845
|
-
const canonicalContent = canonicalizeDocContent(readFileSync(filePath, "utf8"));
|
|
4846
|
-
hashPieces.push(`file:${filename}
|
|
4847
|
-
${canonicalContent}`);
|
|
4848
|
-
renderedSections.push(`<file name="${escapeXmlAttr(filename)}">
|
|
4849
|
-
${escapeXmlContent(canonicalContent)}
|
|
4850
|
-
</file>`);
|
|
4851
|
-
}
|
|
4852
|
-
return { hashPieces, renderedSections };
|
|
4853
|
-
}
|
|
4854
|
-
function buildRenderedBlock(renderedSections) {
|
|
4855
|
-
if (renderedSections.length === 0) {
|
|
4856
|
-
return "";
|
|
4857
|
-
}
|
|
4858
|
-
return `<project-docs>
|
|
4859
|
-
${renderedSections.join(`
|
|
4860
|
-
|
|
4861
|
-
`)}
|
|
4862
|
-
</project-docs>`;
|
|
4863
|
-
}
|
|
4864
|
-
function hashCanonicalPieces(hashPieces) {
|
|
4865
|
-
if (hashPieces.length === 0) {
|
|
4866
|
-
return "";
|
|
4867
|
-
}
|
|
4868
|
-
return createHash4("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
4869
|
-
}
|
|
4870
|
-
function readProjectDocsCanonical(projectDirectory) {
|
|
4871
|
-
const canonicalDirectory = path2.resolve(projectDirectory);
|
|
4872
|
-
const current = readCurrentFingerprints(canonicalDirectory);
|
|
4873
|
-
const cached = docsCache.get(canonicalDirectory);
|
|
4874
|
-
if (cached && cacheFilesEqual(cached.files, current.files)) {
|
|
4875
|
-
cached.directoryMtimeMs = current.directoryMtimeMs;
|
|
4876
|
-
return {
|
|
4877
|
-
renderedBlock: cached.cachedRendered,
|
|
4878
|
-
canonicalHash: cached.cachedHash
|
|
4879
|
-
};
|
|
4880
|
-
}
|
|
4881
|
-
const { hashPieces, renderedSections } = readCanonicalPieces(canonicalDirectory, current.files);
|
|
4882
|
-
const canonicalHash = hashCanonicalPieces(hashPieces);
|
|
4883
|
-
const renderedBlock = buildRenderedBlock(renderedSections);
|
|
4884
|
-
docsCache.set(canonicalDirectory, {
|
|
4885
|
-
directoryMtimeMs: current.directoryMtimeMs,
|
|
4886
|
-
files: current.files,
|
|
4887
|
-
cachedHash: canonicalHash,
|
|
4888
|
-
cachedRendered: renderedBlock
|
|
4889
|
-
});
|
|
4890
|
-
return { renderedBlock, canonicalHash };
|
|
4891
|
-
}
|
|
4892
|
-
function computeProjectDocsHash(projectDirectory) {
|
|
4893
|
-
return readProjectDocsCanonical(projectDirectory).canonicalHash;
|
|
4894
|
-
}
|
|
4895
|
-
// src/features/magic-context/storage-db.ts
|
|
4896
|
-
import { chmodSync, copyFileSync, cpSync, existsSync as existsSync3, mkdirSync, mkdtempSync } from "node:fs";
|
|
4897
|
-
import { dirname, join as join2 } from "node:path";
|
|
4898
|
-
|
|
4899
|
-
// src/plugin/boot-quiet.ts
|
|
4900
|
-
var BOOT_QUIET_MS = 120000;
|
|
4901
|
-
var bootQuietUntilMs = 0;
|
|
4902
|
-
function beginBootQuietPeriod(now = Date.now()) {
|
|
4903
|
-
if (bootQuietUntilMs === 0)
|
|
4904
|
-
bootQuietUntilMs = now + BOOT_QUIET_MS;
|
|
4905
|
-
}
|
|
4906
|
-
function bootQuietRemainingMs(now = Date.now()) {
|
|
4907
|
-
return Math.max(0, bootQuietUntilMs - now);
|
|
4908
|
-
}
|
|
4909
|
-
function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
|
|
4910
|
-
const timer = setTimeout(task, bootQuietRemainingMs() + Math.max(0, additionalDelayMs));
|
|
4911
|
-
timer.unref?.();
|
|
4912
|
-
return timer;
|
|
4913
|
-
}
|
|
4914
4687
|
|
|
4915
4688
|
// src/features/magic-context/tool-definition-tokens.ts
|
|
4916
4689
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -5024,11 +4797,11 @@ function getMeasuredToolDefinitionTokens(providerID, modelID, agentName) {
|
|
|
5024
4797
|
|
|
5025
4798
|
// src/features/magic-context/tool-owner-backfill.ts
|
|
5026
4799
|
import { existsSync as existsSync2 } from "node:fs";
|
|
5027
|
-
import { join } from "node:path";
|
|
4800
|
+
import { join as join2 } from "node:path";
|
|
5028
4801
|
var LEASE_DURATION_MS = 5 * 60 * 1000;
|
|
5029
4802
|
var LEASE_RENEWAL_MS = 60 * 1000;
|
|
5030
4803
|
function resolveOpencodeDbPath() {
|
|
5031
|
-
return
|
|
4804
|
+
return join2(getDataDir(), "opencode", "opencode.db");
|
|
5032
4805
|
}
|
|
5033
4806
|
function ensureBackfillStateTable(db) {
|
|
5034
4807
|
db.exec(`
|
|
@@ -5283,30 +5056,40 @@ var persistenceByDatabase = new WeakMap;
|
|
|
5283
5056
|
var persistenceErrorByDatabase = new WeakMap;
|
|
5284
5057
|
var pathByDatabase = new WeakMap;
|
|
5285
5058
|
var lastSchemaFenceRejection = null;
|
|
5059
|
+
var lastMigrationOnOpenRefusal = null;
|
|
5286
5060
|
function getSchemaFenceRejection() {
|
|
5287
5061
|
return lastSchemaFenceRejection;
|
|
5288
5062
|
}
|
|
5289
|
-
|
|
5063
|
+
function getMigrationOnOpenRefusal() {
|
|
5064
|
+
return lastMigrationOnOpenRefusal;
|
|
5065
|
+
}
|
|
5066
|
+
var LATEST_SUPPORTED_VERSION = 74;
|
|
5290
5067
|
var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
|
|
5068
|
+
var defaultStoragePermissionFs = { chmodSync, mkdirSync };
|
|
5069
|
+
var storagePermissionFs = defaultStoragePermissionFs;
|
|
5291
5070
|
function ensureSecureStorageDir(dir) {
|
|
5292
|
-
|
|
5071
|
+
if (!shouldEnforcePrivateStoragePermissions()) {
|
|
5072
|
+
storagePermissionFs.mkdirSync(dir, { recursive: true });
|
|
5073
|
+
return;
|
|
5074
|
+
}
|
|
5075
|
+
storagePermissionFs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5293
5076
|
if (!PERMISSIONS_ENFORCEABLE)
|
|
5294
5077
|
return;
|
|
5295
5078
|
try {
|
|
5296
|
-
chmodSync(dir, 448);
|
|
5079
|
+
storagePermissionFs.chmodSync(dir, 448);
|
|
5297
5080
|
} catch (error) {
|
|
5298
5081
|
log(`[magic-context] could not restrict storage dir permissions on ${dir}: ${getErrorMessage(error)}`);
|
|
5299
5082
|
}
|
|
5300
5083
|
}
|
|
5301
5084
|
function restrictDatabaseFilePermissions(dbPath) {
|
|
5302
|
-
if (!PERMISSIONS_ENFORCEABLE)
|
|
5085
|
+
if (!PERMISSIONS_ENFORCEABLE || !shouldEnforcePrivateStoragePermissions())
|
|
5303
5086
|
return;
|
|
5304
5087
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5305
5088
|
const file = `${dbPath}${suffix}`;
|
|
5306
5089
|
if (!existsSync3(file))
|
|
5307
5090
|
continue;
|
|
5308
5091
|
try {
|
|
5309
|
-
chmodSync(file, 384);
|
|
5092
|
+
storagePermissionFs.chmodSync(file, 384);
|
|
5310
5093
|
} catch (error) {
|
|
5311
5094
|
log(`[magic-context] could not restrict DB file permissions on ${file}: ${getErrorMessage(error)}`);
|
|
5312
5095
|
}
|
|
@@ -5318,12 +5101,12 @@ function resolveDatabasePath(dbPathOverride) {
|
|
|
5318
5101
|
}
|
|
5319
5102
|
const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR;
|
|
5320
5103
|
if (testDataDir && !process.env.XDG_DATA_HOME) {
|
|
5321
|
-
const dbDir2 =
|
|
5322
|
-
return { dbDir: dbDir2, dbPath:
|
|
5104
|
+
const dbDir2 = join3(testDataDir, "cortexkit", "magic-context");
|
|
5105
|
+
return { dbDir: dbDir2, dbPath: join3(dbDir2, "context.db") };
|
|
5323
5106
|
}
|
|
5324
5107
|
if (false) {}
|
|
5325
5108
|
const dbDir = getMagicContextStorageDir();
|
|
5326
|
-
return { dbDir, dbPath:
|
|
5109
|
+
return { dbDir, dbPath: join3(dbDir, "context.db") };
|
|
5327
5110
|
}
|
|
5328
5111
|
function getDatabasePath(db) {
|
|
5329
5112
|
return pathByDatabase.get(db) ?? null;
|
|
@@ -5332,7 +5115,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
5332
5115
|
if (existsSync3(targetDbPath))
|
|
5333
5116
|
return;
|
|
5334
5117
|
const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
|
|
5335
|
-
const legacyDbPath =
|
|
5118
|
+
const legacyDbPath = join3(legacyDir, "context.db");
|
|
5336
5119
|
if (!existsSync3(legacyDbPath))
|
|
5337
5120
|
return;
|
|
5338
5121
|
log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
|
|
@@ -5349,7 +5132,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
5349
5132
|
}
|
|
5350
5133
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5351
5134
|
const src = `${legacyDbPath}${suffix}`;
|
|
5352
|
-
const dst =
|
|
5135
|
+
const dst = join3(targetDbDir, `context.db${suffix}`);
|
|
5353
5136
|
if (existsSync3(src)) {
|
|
5354
5137
|
try {
|
|
5355
5138
|
copyFileSync(src, dst);
|
|
@@ -5358,8 +5141,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
5358
5141
|
}
|
|
5359
5142
|
}
|
|
5360
5143
|
}
|
|
5361
|
-
const legacyModelsDir =
|
|
5362
|
-
const targetModelsDir =
|
|
5144
|
+
const legacyModelsDir = join3(legacyDir, "models");
|
|
5145
|
+
const targetModelsDir = join3(targetDbDir, "models");
|
|
5363
5146
|
if (existsSync3(legacyModelsDir) && !existsSync3(targetModelsDir)) {
|
|
5364
5147
|
try {
|
|
5365
5148
|
cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
|
|
@@ -5376,6 +5159,9 @@ function getPersistedSchemaVersion(db) {
|
|
|
5376
5159
|
const row = db.prepare("SELECT MAX(version) AS version FROM schema_migrations").get();
|
|
5377
5160
|
return row?.version ?? 0;
|
|
5378
5161
|
}
|
|
5162
|
+
function formatSchemaFenceBootLog(persistedVersion, supportedVersion) {
|
|
5163
|
+
return `[magic-context] storage schema at boot: database=v${persistedVersion}, supported_fence=v${supportedVersion}`;
|
|
5164
|
+
}
|
|
5379
5165
|
function getRuntimeLatestSupportedVersion(options) {
|
|
5380
5166
|
if (options?.latestSupportedVersion !== undefined) {
|
|
5381
5167
|
return options.latestSupportedVersion;
|
|
@@ -5392,12 +5178,109 @@ function getRuntimeLatestSupportedVersion(options) {
|
|
|
5392
5178
|
function enforceSchemaFence(db, dbPath, latestSupportedVersion) {
|
|
5393
5179
|
const persistedVersion = getPersistedSchemaVersion(db);
|
|
5394
5180
|
if (persistedVersion <= latestSupportedVersion) {
|
|
5181
|
+
lastSchemaFenceRejection = null;
|
|
5395
5182
|
return true;
|
|
5396
5183
|
}
|
|
5397
5184
|
lastSchemaFenceRejection = { persistedVersion, supportedVersion: latestSupportedVersion };
|
|
5398
5185
|
log(`[magic-context] storage fatal: refusing to open ${dbPath}; database schema v${persistedVersion} is newer than this binary supports (max v${latestSupportedVersion}). A pinned or stale plugin is likely sharing this database with a newer instance; update or unpin Magic Context with 'npx @cortexkit/magic-context@latest doctor --force', then restart.`);
|
|
5399
5186
|
return false;
|
|
5400
5187
|
}
|
|
5188
|
+
function unreadableDiscovery(path2) {
|
|
5189
|
+
return {
|
|
5190
|
+
state: "unreadable",
|
|
5191
|
+
serverPids: [],
|
|
5192
|
+
staleFiles: [],
|
|
5193
|
+
unreadableFile: path2
|
|
5194
|
+
};
|
|
5195
|
+
}
|
|
5196
|
+
function inspectRpcServerDiscovery(storageDir) {
|
|
5197
|
+
const rpcRoot = join3(storageDir, "rpc");
|
|
5198
|
+
let projectEntries;
|
|
5199
|
+
try {
|
|
5200
|
+
projectEntries = readdirSync(rpcRoot, { withFileTypes: true });
|
|
5201
|
+
} catch (error) {
|
|
5202
|
+
if (error.code === "ENOENT") {
|
|
5203
|
+
return { state: "absent", serverPids: [], staleFiles: [] };
|
|
5204
|
+
}
|
|
5205
|
+
return unreadableDiscovery(rpcRoot);
|
|
5206
|
+
}
|
|
5207
|
+
const portFiles = [];
|
|
5208
|
+
for (const projectEntry of projectEntries) {
|
|
5209
|
+
if (!projectEntry.isDirectory())
|
|
5210
|
+
continue;
|
|
5211
|
+
const projectDir = join3(rpcRoot, projectEntry.name);
|
|
5212
|
+
let entries;
|
|
5213
|
+
try {
|
|
5214
|
+
entries = readdirSync(projectDir);
|
|
5215
|
+
} catch {
|
|
5216
|
+
return unreadableDiscovery(projectDir);
|
|
5217
|
+
}
|
|
5218
|
+
for (const entry of entries) {
|
|
5219
|
+
if (entry === "port" || entry.startsWith("port-") && entry.endsWith(".json")) {
|
|
5220
|
+
portFiles.push(join3(projectDir, entry));
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
if (portFiles.length === 0) {
|
|
5225
|
+
return { state: "absent", serverPids: [], staleFiles: [] };
|
|
5226
|
+
}
|
|
5227
|
+
const pids = new Set;
|
|
5228
|
+
const staleFiles = [];
|
|
5229
|
+
for (const portFile of portFiles) {
|
|
5230
|
+
let raw;
|
|
5231
|
+
try {
|
|
5232
|
+
raw = readFileSync(portFile, "utf8");
|
|
5233
|
+
} catch {
|
|
5234
|
+
return unreadableDiscovery(portFile);
|
|
5235
|
+
}
|
|
5236
|
+
const filename = basename(portFile);
|
|
5237
|
+
const pidFromName = /^port-(\d+)/.exec(filename)?.[1];
|
|
5238
|
+
const record = parseRpcPortFile(raw, pidFromName ? Number(pidFromName) : 0);
|
|
5239
|
+
if (!record || !Number.isInteger(record.pid) || record.pid <= 0) {
|
|
5240
|
+
return unreadableDiscovery(portFile);
|
|
5241
|
+
}
|
|
5242
|
+
if (isPidAlive(record.pid))
|
|
5243
|
+
pids.add(record.pid);
|
|
5244
|
+
else
|
|
5245
|
+
staleFiles.push(portFile);
|
|
5246
|
+
}
|
|
5247
|
+
const serverPids = [...pids].sort((a, b) => a - b);
|
|
5248
|
+
if (serverPids.length > 0) {
|
|
5249
|
+
return { state: "live", serverPids, staleFiles };
|
|
5250
|
+
}
|
|
5251
|
+
for (const staleFile of staleFiles) {
|
|
5252
|
+
try {
|
|
5253
|
+
unlinkSync(staleFile);
|
|
5254
|
+
} catch {
|
|
5255
|
+
return unreadableDiscovery(staleFile);
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
return { state: "stale", serverPids: [], staleFiles };
|
|
5259
|
+
}
|
|
5260
|
+
function enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion) {
|
|
5261
|
+
const persistedVersion = getPersistedSchemaVersion(db);
|
|
5262
|
+
if (persistedVersion >= latestSupportedVersion) {
|
|
5263
|
+
lastMigrationOnOpenRefusal = null;
|
|
5264
|
+
return true;
|
|
5265
|
+
}
|
|
5266
|
+
const discovery = inspectRpcServerDiscovery(dbDir);
|
|
5267
|
+
if (discovery.state === "absent" || discovery.state === "stale") {
|
|
5268
|
+
lastMigrationOnOpenRefusal = null;
|
|
5269
|
+
return true;
|
|
5270
|
+
}
|
|
5271
|
+
lastMigrationOnOpenRefusal = {
|
|
5272
|
+
persistedVersion,
|
|
5273
|
+
supportedVersion: latestSupportedVersion,
|
|
5274
|
+
serverPids: discovery.serverPids,
|
|
5275
|
+
...discovery.unreadableFile ? { unreadableFile: discovery.unreadableFile } : {}
|
|
5276
|
+
};
|
|
5277
|
+
if (discovery.state === "unreadable") {
|
|
5278
|
+
log(`[magic-context] storage fatal: refusing to migrate ${dbPath} from schema v${persistedVersion} to v${latestSupportedVersion} because RPC discovery file ${discovery.unreadableFile ?? "<unknown>"} is unreadable or invalid, so the absence of a live OpenCode server cannot be proven. Restart OpenCode, remove or repair the named discovery file, then retry this process.`);
|
|
5279
|
+
} else {
|
|
5280
|
+
log(`[magic-context] storage fatal: refusing to migrate ${dbPath} from schema v${persistedVersion} to v${latestSupportedVersion} while live OpenCode server PID(s) ${discovery.serverPids.join(", ")} may still use the old plugin build. Restart OpenCode, then retry this process.`);
|
|
5281
|
+
}
|
|
5282
|
+
return false;
|
|
5283
|
+
}
|
|
5401
5284
|
var sqlitePragmaConfig = {
|
|
5402
5285
|
cacheSizeMb: 64,
|
|
5403
5286
|
mmapSizeMb: 0
|
|
@@ -5442,6 +5325,9 @@ function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion)
|
|
|
5442
5325
|
pathByDatabase.set(db, dbPath);
|
|
5443
5326
|
persistenceByDatabase.set(db, true);
|
|
5444
5327
|
persistenceErrorByDatabase.delete(db);
|
|
5328
|
+
if (!explicitDbPath) {
|
|
5329
|
+
log(formatSchemaFenceBootLog(getPersistedSchemaVersion(db), latestSupportedVersion));
|
|
5330
|
+
}
|
|
5445
5331
|
return db;
|
|
5446
5332
|
}
|
|
5447
5333
|
function initializeDatabase(db) {
|
|
@@ -6042,12 +5928,14 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6042
5928
|
note_nudge_anchors TEXT NOT NULL DEFAULT '[]',
|
|
6043
5929
|
auto_search_hint_decisions TEXT NOT NULL DEFAULT '[]',
|
|
6044
5930
|
last_todo_state TEXT DEFAULT '',
|
|
5931
|
+
todo_permission_denied INTEGER NOT NULL DEFAULT 2,
|
|
6045
5932
|
todo_synthetic_call_id TEXT DEFAULT '',
|
|
6046
5933
|
todo_synthetic_anchor_message_id TEXT DEFAULT '',
|
|
6047
5934
|
todo_synthetic_state_json TEXT DEFAULT '',
|
|
6048
5935
|
is_subagent INTEGER DEFAULT 0,
|
|
6049
5936
|
last_context_percentage REAL DEFAULT 0,
|
|
6050
5937
|
last_input_tokens INTEGER DEFAULT 0,
|
|
5938
|
+
detected_context_limit_provenance TEXT NOT NULL DEFAULT 'unknown',
|
|
6051
5939
|
observed_safe_input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
6052
5940
|
cache_alert_sent INTEGER NOT NULL DEFAULT 0,
|
|
6053
5941
|
times_execute_threshold_reached INTEGER DEFAULT 0,
|
|
@@ -6102,6 +5990,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6102
5990
|
emergency_drain_active INTEGER NOT NULL DEFAULT 0,
|
|
6103
5991
|
historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
|
|
6104
5992
|
wrapup_in_progress_state TEXT,
|
|
5993
|
+
compaction_mode_record TEXT,
|
|
6105
5994
|
cached_m0_materialized_at INTEGER,
|
|
6106
5995
|
cached_m0_session_facts_version INTEGER,
|
|
6107
5996
|
cached_m0_upgrade_state TEXT,
|
|
@@ -6301,6 +6190,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6301
6190
|
ensureColumn(db, "session_meta", "note_nudge_anchors", "TEXT NOT NULL DEFAULT '[]'");
|
|
6302
6191
|
ensureColumn(db, "session_meta", "auto_search_hint_decisions", "TEXT NOT NULL DEFAULT '[]'");
|
|
6303
6192
|
ensureColumn(db, "session_meta", "last_todo_state", "TEXT DEFAULT ''");
|
|
6193
|
+
ensureColumn(db, "session_meta", "todo_permission_denied", "INTEGER NOT NULL DEFAULT 2");
|
|
6304
6194
|
ensureColumn(db, "session_meta", "todo_synthetic_call_id", "TEXT DEFAULT ''");
|
|
6305
6195
|
ensureColumn(db, "session_meta", "todo_synthetic_anchor_message_id", "TEXT DEFAULT ''");
|
|
6306
6196
|
ensureColumn(db, "session_meta", "todo_synthetic_state_json", "TEXT DEFAULT ''");
|
|
@@ -6357,6 +6247,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6357
6247
|
ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
|
|
6358
6248
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
6359
6249
|
ensureColumn(db, "session_meta", "detected_context_limit_model_key", "TEXT");
|
|
6250
|
+
ensureColumn(db, "session_meta", "detected_context_limit_provenance", "TEXT NOT NULL DEFAULT 'unknown'");
|
|
6360
6251
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
6361
6252
|
ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
|
|
6362
6253
|
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
@@ -6407,6 +6298,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6407
6298
|
ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
|
|
6408
6299
|
ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
|
|
6409
6300
|
ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
|
|
6301
|
+
ensureColumn(db, "session_meta", "compaction_mode_record", "TEXT");
|
|
6410
6302
|
ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
|
|
6411
6303
|
ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
|
|
6412
6304
|
ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
|
|
@@ -6546,12 +6438,13 @@ function openDatabase(dbPathOrOptions) {
|
|
|
6546
6438
|
const explicitDbPath = options?.dbPath !== undefined;
|
|
6547
6439
|
const { dbDir, dbPath } = resolveDatabasePath(options?.dbPath);
|
|
6548
6440
|
const latestSupportedVersion = getRuntimeLatestSupportedVersion(options);
|
|
6441
|
+
lastSchemaFenceRejection = null;
|
|
6442
|
+
lastMigrationOnOpenRefusal = null;
|
|
6549
6443
|
const existing = databases.get(dbPath);
|
|
6550
6444
|
if (existing) {
|
|
6551
6445
|
if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion)) {
|
|
6552
6446
|
return null;
|
|
6553
6447
|
}
|
|
6554
|
-
registerPrivilegedWriter(existing);
|
|
6555
6448
|
if (!persistenceByDatabase.has(existing)) {
|
|
6556
6449
|
persistenceByDatabase.set(existing, true);
|
|
6557
6450
|
}
|
|
@@ -6564,11 +6457,14 @@ function openDatabase(dbPathOrOptions) {
|
|
|
6564
6457
|
}
|
|
6565
6458
|
ensureSecureStorageDir(dbDir);
|
|
6566
6459
|
const db = new Database(dbPath);
|
|
6567
|
-
registerPrivilegedWriter(db);
|
|
6568
6460
|
if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
|
|
6569
6461
|
closeQuietly(db);
|
|
6570
6462
|
return null;
|
|
6571
6463
|
}
|
|
6464
|
+
if (!enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion)) {
|
|
6465
|
+
closeQuietly(db);
|
|
6466
|
+
return null;
|
|
6467
|
+
}
|
|
6572
6468
|
initializeDatabase(db);
|
|
6573
6469
|
runMigrations(db);
|
|
6574
6470
|
ensureContextStoreUuid(db);
|
|
@@ -6584,6 +6480,8 @@ async function openDatabaseAsync(dbPathOrOptions) {
|
|
|
6584
6480
|
const explicitDbPath = options?.dbPath !== undefined;
|
|
6585
6481
|
const { dbDir, dbPath } = resolveDatabasePath(options?.dbPath);
|
|
6586
6482
|
const latestSupportedVersion = getRuntimeLatestSupportedVersion(options);
|
|
6483
|
+
lastSchemaFenceRejection = null;
|
|
6484
|
+
lastMigrationOnOpenRefusal = null;
|
|
6587
6485
|
const existing = databases.get(dbPath);
|
|
6588
6486
|
if (existing) {
|
|
6589
6487
|
if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion))
|
|
@@ -6603,11 +6501,14 @@ async function openDatabaseAsync(dbPathOrOptions) {
|
|
|
6603
6501
|
migrateLegacyStorageIfNeeded(dbPath, dbDir);
|
|
6604
6502
|
ensureSecureStorageDir(dbDir);
|
|
6605
6503
|
db = new Database(dbPath);
|
|
6606
|
-
registerPrivilegedWriter(db);
|
|
6607
6504
|
if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
|
|
6608
6505
|
closeQuietly(db);
|
|
6609
6506
|
return null;
|
|
6610
6507
|
}
|
|
6508
|
+
if (!enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion)) {
|
|
6509
|
+
closeQuietly(db);
|
|
6510
|
+
return null;
|
|
6511
|
+
}
|
|
6611
6512
|
initializeDatabase(db);
|
|
6612
6513
|
await runMigrationsWithRetry(db);
|
|
6613
6514
|
ensureContextStoreUuid(db);
|
|
@@ -6638,11 +6539,188 @@ function getDatabasePersistenceError(db) {
|
|
|
6638
6539
|
return null;
|
|
6639
6540
|
return persistenceErrorByDatabase.get(db) ?? null;
|
|
6640
6541
|
}
|
|
6641
|
-
|
|
6542
|
+
|
|
6543
|
+
// src/features/magic-context/schema-fence-probe.ts
|
|
6544
|
+
var STALE_CHILD_SPAWN_FAILURE = "stale_schema_fence";
|
|
6545
|
+
var STALE_CHILD_SPAWN_LATCH_THRESHOLD = 2;
|
|
6546
|
+
var state = {
|
|
6547
|
+
consecutiveFailures: 0,
|
|
6548
|
+
totalFailures: 0,
|
|
6549
|
+
latched: false,
|
|
6550
|
+
noticeEmitted: false,
|
|
6551
|
+
failure: null
|
|
6552
|
+
};
|
|
6553
|
+
function recordStaleFence(persistedVersion, supportedVersion) {
|
|
6554
|
+
state.consecutiveFailures += 1;
|
|
6555
|
+
state.totalFailures += 1;
|
|
6556
|
+
const latched = state.consecutiveFailures >= STALE_CHILD_SPAWN_LATCH_THRESHOLD;
|
|
6557
|
+
state.latched ||= latched;
|
|
6558
|
+
const failure = {
|
|
6559
|
+
failureClass: STALE_CHILD_SPAWN_FAILURE,
|
|
6560
|
+
persistedVersion,
|
|
6561
|
+
supportedVersion,
|
|
6562
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
6563
|
+
totalFailures: state.totalFailures,
|
|
6564
|
+
latched: state.latched
|
|
6565
|
+
};
|
|
6566
|
+
state.failure = failure;
|
|
6567
|
+
const shouldSurface = state.latched && !state.noticeEmitted;
|
|
6568
|
+
if (shouldSurface)
|
|
6569
|
+
state.noticeEmitted = true;
|
|
6570
|
+
return { allowSpawn: false, failure, shouldSurface };
|
|
6571
|
+
}
|
|
6572
|
+
function probeChildSpawnFence(db) {
|
|
6573
|
+
if (!db) {
|
|
6574
|
+
const knownRejection = getSchemaFenceRejection();
|
|
6575
|
+
if (knownRejection) {
|
|
6576
|
+
return recordStaleFence(knownRejection.persistedVersion, knownRejection.supportedVersion);
|
|
6577
|
+
}
|
|
6578
|
+
return { allowSpawn: true };
|
|
6579
|
+
}
|
|
6580
|
+
try {
|
|
6581
|
+
const persistedVersion = getPersistedSchemaVersion(db);
|
|
6582
|
+
if (persistedVersion > LATEST_SUPPORTED_VERSION) {
|
|
6583
|
+
return recordStaleFence(persistedVersion, LATEST_SUPPORTED_VERSION);
|
|
6584
|
+
}
|
|
6585
|
+
} catch {
|
|
6586
|
+
return { allowSpawn: true };
|
|
6587
|
+
}
|
|
6588
|
+
state.consecutiveFailures = 0;
|
|
6589
|
+
state.latched = false;
|
|
6590
|
+
state.noticeEmitted = false;
|
|
6591
|
+
return { allowSpawn: true };
|
|
6592
|
+
}
|
|
6593
|
+
// src/features/magic-context/project-docs-hash.ts
|
|
6642
6594
|
import { createHash as createHash6 } from "node:crypto";
|
|
6595
|
+
import { lstatSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
6596
|
+
import path2 from "node:path";
|
|
6597
|
+
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
6598
|
+
var PROJECT_DOCS_DELIMITER = `
|
|
6599
|
+
|
|
6600
|
+
---
|
|
6601
|
+
|
|
6602
|
+
`;
|
|
6603
|
+
var MAX_PROJECT_DOC_BYTES = 256 * 1024;
|
|
6604
|
+
var docsCache = new Map;
|
|
6605
|
+
function canonicalizeDocContent(raw) {
|
|
6606
|
+
return raw.replace(/^\uFEFF/, "").replace(/\r\n/g, `
|
|
6607
|
+
`).split(`
|
|
6608
|
+
`).map((line) => line.replace(/[ \t]+$/, "")).join(`
|
|
6609
|
+
`).replace(/\n+$/, "");
|
|
6610
|
+
}
|
|
6611
|
+
function fingerprintFile(filePath) {
|
|
6612
|
+
try {
|
|
6613
|
+
const stat = lstatSync(filePath);
|
|
6614
|
+
const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
|
|
6615
|
+
return {
|
|
6616
|
+
exists: isReadableDoc,
|
|
6617
|
+
mtimeMs: stat.mtimeMs,
|
|
6618
|
+
size: stat.size
|
|
6619
|
+
};
|
|
6620
|
+
} catch {
|
|
6621
|
+
return { exists: false, mtimeMs: 0, size: 0 };
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
function readDirectoryMtimeMs(projectDirectory) {
|
|
6625
|
+
try {
|
|
6626
|
+
return statSync2(projectDirectory).mtimeMs;
|
|
6627
|
+
} catch {
|
|
6628
|
+
return 0;
|
|
6629
|
+
}
|
|
6630
|
+
}
|
|
6631
|
+
function fingerprintsEqual(a, b) {
|
|
6632
|
+
return a?.exists === b.exists && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
6633
|
+
}
|
|
6634
|
+
function cacheFilesEqual(cachedFiles, currentFiles) {
|
|
6635
|
+
for (const [filePath, current] of currentFiles) {
|
|
6636
|
+
if (!fingerprintsEqual(cachedFiles.get(filePath), current)) {
|
|
6637
|
+
return false;
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6640
|
+
return true;
|
|
6641
|
+
}
|
|
6642
|
+
function readCurrentFingerprints(projectDirectory) {
|
|
6643
|
+
const files = new Map;
|
|
6644
|
+
for (const filename of PROJECT_DOC_FILES) {
|
|
6645
|
+
const filePath = path2.join(projectDirectory, filename);
|
|
6646
|
+
files.set(filePath, fingerprintFile(filePath));
|
|
6647
|
+
}
|
|
6648
|
+
return { directoryMtimeMs: readDirectoryMtimeMs(projectDirectory), files };
|
|
6649
|
+
}
|
|
6650
|
+
function readCanonicalPieces(projectDirectory, files) {
|
|
6651
|
+
const hashPieces = [];
|
|
6652
|
+
const renderedSections = [];
|
|
6653
|
+
for (const filename of PROJECT_DOC_FILES) {
|
|
6654
|
+
const filePath = path2.join(projectDirectory, filename);
|
|
6655
|
+
const fingerprint = files.get(filePath);
|
|
6656
|
+
if (!fingerprint?.exists) {
|
|
6657
|
+
continue;
|
|
6658
|
+
}
|
|
6659
|
+
let safeToRead = false;
|
|
6660
|
+
try {
|
|
6661
|
+
const st = lstatSync(filePath);
|
|
6662
|
+
safeToRead = st.isFile() && st.size <= MAX_PROJECT_DOC_BYTES;
|
|
6663
|
+
} catch {
|
|
6664
|
+
safeToRead = false;
|
|
6665
|
+
}
|
|
6666
|
+
if (!safeToRead) {
|
|
6667
|
+
continue;
|
|
6668
|
+
}
|
|
6669
|
+
const canonicalContent = canonicalizeDocContent(readFileSync2(filePath, "utf8"));
|
|
6670
|
+
hashPieces.push(`file:${filename}
|
|
6671
|
+
${canonicalContent}`);
|
|
6672
|
+
renderedSections.push(`<file name="${escapeXmlAttr(filename)}">
|
|
6673
|
+
${escapeXmlContent(canonicalContent)}
|
|
6674
|
+
</file>`);
|
|
6675
|
+
}
|
|
6676
|
+
return { hashPieces, renderedSections };
|
|
6677
|
+
}
|
|
6678
|
+
function buildRenderedBlock(renderedSections) {
|
|
6679
|
+
if (renderedSections.length === 0) {
|
|
6680
|
+
return "";
|
|
6681
|
+
}
|
|
6682
|
+
return `<project-docs>
|
|
6683
|
+
${renderedSections.join(`
|
|
6684
|
+
|
|
6685
|
+
`)}
|
|
6686
|
+
</project-docs>`;
|
|
6687
|
+
}
|
|
6688
|
+
function hashCanonicalPieces(hashPieces) {
|
|
6689
|
+
if (hashPieces.length === 0) {
|
|
6690
|
+
return "";
|
|
6691
|
+
}
|
|
6692
|
+
return createHash6("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
6693
|
+
}
|
|
6694
|
+
function readProjectDocsCanonical(projectDirectory) {
|
|
6695
|
+
const canonicalDirectory = path2.resolve(projectDirectory);
|
|
6696
|
+
const current = readCurrentFingerprints(canonicalDirectory);
|
|
6697
|
+
const cached = docsCache.get(canonicalDirectory);
|
|
6698
|
+
if (cached && cacheFilesEqual(cached.files, current.files)) {
|
|
6699
|
+
cached.directoryMtimeMs = current.directoryMtimeMs;
|
|
6700
|
+
return {
|
|
6701
|
+
renderedBlock: cached.cachedRendered,
|
|
6702
|
+
canonicalHash: cached.cachedHash
|
|
6703
|
+
};
|
|
6704
|
+
}
|
|
6705
|
+
const { hashPieces, renderedSections } = readCanonicalPieces(canonicalDirectory, current.files);
|
|
6706
|
+
const canonicalHash = hashCanonicalPieces(hashPieces);
|
|
6707
|
+
const renderedBlock = buildRenderedBlock(renderedSections);
|
|
6708
|
+
docsCache.set(canonicalDirectory, {
|
|
6709
|
+
directoryMtimeMs: current.directoryMtimeMs,
|
|
6710
|
+
files: current.files,
|
|
6711
|
+
cachedHash: canonicalHash,
|
|
6712
|
+
cachedRendered: renderedBlock
|
|
6713
|
+
});
|
|
6714
|
+
return { renderedBlock, canonicalHash };
|
|
6715
|
+
}
|
|
6716
|
+
function computeProjectDocsHash(projectDirectory) {
|
|
6717
|
+
return readProjectDocsCanonical(projectDirectory).canonicalHash;
|
|
6718
|
+
}
|
|
6719
|
+
// src/features/magic-context/storage-embedding-measurements.ts
|
|
6720
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
6643
6721
|
function normalizedQueryHash(query) {
|
|
6644
6722
|
const normalized = query.trim().replace(/\s+/g, " ").toLowerCase();
|
|
6645
|
-
return
|
|
6723
|
+
return createHash7("sha256").update(normalized).digest("hex");
|
|
6646
6724
|
}
|
|
6647
6725
|
var MEASUREMENT_CORPUS_SESSION_ROW_CAP = 2000;
|
|
6648
6726
|
function recordEmbeddingMeasurement(db, input) {
|
|
@@ -6858,7 +6936,7 @@ function getMemoryMutationsForRenderByIdentitySet(db, projectPaths, afterId, ren
|
|
|
6858
6936
|
}
|
|
6859
6937
|
visited.add(terminal);
|
|
6860
6938
|
const next = byTarget.get(terminal);
|
|
6861
|
-
if (
|
|
6939
|
+
if (next?.mutationType !== "superseded")
|
|
6862
6940
|
break;
|
|
6863
6941
|
if (depth >= MAX_MEMORY_REPLACEMENT_DEPTH - 1) {
|
|
6864
6942
|
terminal = null;
|
|
@@ -6892,6 +6970,24 @@ function getMaxMemoryMutationIdForProjects(db, projectPaths) {
|
|
|
6892
6970
|
WHERE project_path IN (${placeholders2(identities)})`).get(...identities);
|
|
6893
6971
|
return row?.max_id ?? null;
|
|
6894
6972
|
}
|
|
6973
|
+
// src/shared/context-limit-provenance.ts
|
|
6974
|
+
function normalizeContextLimitProvenance(value) {
|
|
6975
|
+
if (value === "prompt_only" || value === "combined")
|
|
6976
|
+
return value;
|
|
6977
|
+
return "unknown";
|
|
6978
|
+
}
|
|
6979
|
+
|
|
6980
|
+
// src/shared/escalation-bands.ts
|
|
6981
|
+
var MAX_EXECUTE_THRESHOLD = 90;
|
|
6982
|
+
var ABSOLUTE_EMERGENCY_PERCENTAGE = 95;
|
|
6983
|
+
function escalationBands(effectiveThresholdPercentage) {
|
|
6984
|
+
const threshold = Number.isFinite(effectiveThresholdPercentage) ? Math.min(effectiveThresholdPercentage, MAX_EXECUTE_THRESHOLD) : 65;
|
|
6985
|
+
return {
|
|
6986
|
+
forceMaterializationPercentage: Math.max(85, threshold + 2),
|
|
6987
|
+
emergencyPercentage: ABSOLUTE_EMERGENCY_PERCENTAGE
|
|
6988
|
+
};
|
|
6989
|
+
}
|
|
6990
|
+
|
|
6895
6991
|
// src/features/magic-context/storage-meta-persisted.ts
|
|
6896
6992
|
var emergencyRecoveryArmedSessions = new Set;
|
|
6897
6993
|
var providerOverflowReconfirmedSessions = new Set;
|
|
@@ -7113,11 +7209,11 @@ function readRawWrapupState(db, sessionId) {
|
|
|
7113
7209
|
return parseWrapupState(row?.wrapup_in_progress_state);
|
|
7114
7210
|
}
|
|
7115
7211
|
function getWrapupInProgressState(db, sessionId, now = Date.now()) {
|
|
7116
|
-
const
|
|
7117
|
-
if (!
|
|
7212
|
+
const state2 = readRawWrapupState(db, sessionId);
|
|
7213
|
+
if (!state2)
|
|
7118
7214
|
return null;
|
|
7119
|
-
if (
|
|
7120
|
-
return
|
|
7215
|
+
if (state2.expiresAt > now)
|
|
7216
|
+
return state2;
|
|
7121
7217
|
try {
|
|
7122
7218
|
db.exec("BEGIN IMMEDIATE");
|
|
7123
7219
|
} catch {
|
|
@@ -7143,10 +7239,10 @@ function getWrapupInProgressState(db, sessionId, now = Date.now()) {
|
|
|
7143
7239
|
function isWrapupInProgress(db, sessionId, now = Date.now()) {
|
|
7144
7240
|
return getWrapupInProgressState(db, sessionId, now) !== null;
|
|
7145
7241
|
}
|
|
7146
|
-
function acquireWrapupInProgress(db, sessionId,
|
|
7242
|
+
function acquireWrapupInProgress(db, sessionId, state2, now = Date.now()) {
|
|
7147
7243
|
const acquiredAt = now;
|
|
7148
7244
|
const next = {
|
|
7149
|
-
...
|
|
7245
|
+
...state2,
|
|
7150
7246
|
acquiredAt,
|
|
7151
7247
|
expiresAt: acquiredAt + WRAPUP_IN_PROGRESS_TTL_MS,
|
|
7152
7248
|
updatedAt: acquiredAt
|
|
@@ -7156,7 +7252,7 @@ function acquireWrapupInProgress(db, sessionId, state, now = Date.now()) {
|
|
|
7156
7252
|
try {
|
|
7157
7253
|
ensureSessionMetaRow(db, sessionId);
|
|
7158
7254
|
const current = readRawWrapupState(db, sessionId);
|
|
7159
|
-
if (current && current.expiresAt > now && current.holderId !==
|
|
7255
|
+
if (current && current.expiresAt > now && current.holderId !== state2.holderId) {
|
|
7160
7256
|
db.exec("COMMIT");
|
|
7161
7257
|
finished = true;
|
|
7162
7258
|
return { ok: false, state: current };
|
|
@@ -7220,6 +7316,45 @@ function releaseWrapupInProgress(db, sessionId, holderId) {
|
|
|
7220
7316
|
}
|
|
7221
7317
|
}
|
|
7222
7318
|
}
|
|
7319
|
+
var COMPACTION_MODE_RECORD_VALUES = new Set([
|
|
7320
|
+
"on",
|
|
7321
|
+
"off",
|
|
7322
|
+
"on_notice_pending",
|
|
7323
|
+
"off_notice_pending",
|
|
7324
|
+
"off_cleanup_pending"
|
|
7325
|
+
]);
|
|
7326
|
+
function normalizeCompactionModeRecord(value) {
|
|
7327
|
+
if (value === null || value === undefined)
|
|
7328
|
+
return null;
|
|
7329
|
+
if (typeof value === "string" && COMPACTION_MODE_RECORD_VALUES.has(value)) {
|
|
7330
|
+
return value;
|
|
7331
|
+
}
|
|
7332
|
+
return null;
|
|
7333
|
+
}
|
|
7334
|
+
function resolveCompactionModeRecord(record) {
|
|
7335
|
+
switch (record) {
|
|
7336
|
+
case "on":
|
|
7337
|
+
case "on_notice_pending":
|
|
7338
|
+
return "on";
|
|
7339
|
+
case "off":
|
|
7340
|
+
case "off_notice_pending":
|
|
7341
|
+
case "off_cleanup_pending":
|
|
7342
|
+
return "off";
|
|
7343
|
+
default:
|
|
7344
|
+
return null;
|
|
7345
|
+
}
|
|
7346
|
+
}
|
|
7347
|
+
function getCompactionModeRecord(db, sessionId) {
|
|
7348
|
+
const row = db.prepare("SELECT compaction_mode_record FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7349
|
+
return normalizeCompactionModeRecord(row?.compaction_mode_record);
|
|
7350
|
+
}
|
|
7351
|
+
function setCompactionModeRecord(db, sessionId, value) {
|
|
7352
|
+
if (value !== null && !COMPACTION_MODE_RECORD_VALUES.has(value)) {
|
|
7353
|
+
throw new Error(`Invalid compaction_mode_record value: ${String(value)} (expected a supported compaction mode record or null)`);
|
|
7354
|
+
}
|
|
7355
|
+
ensureSessionMetaRow(db, sessionId);
|
|
7356
|
+
db.prepare("UPDATE session_meta SET compaction_mode_record = ? WHERE session_id = ?").run(value, sessionId);
|
|
7357
|
+
}
|
|
7223
7358
|
function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
|
|
7224
7359
|
if (usagePercentage >= 95)
|
|
7225
7360
|
return Math.min(1e6, Math.max(4 * perRunCap, Math.round(0.5 * usable)));
|
|
@@ -7227,7 +7362,6 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
|
|
|
7227
7362
|
return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
|
|
7228
7363
|
return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
|
|
7229
7364
|
}
|
|
7230
|
-
var EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95;
|
|
7231
7365
|
var EMERGENCY_DRAIN_EXIT_MARGIN = 10;
|
|
7232
7366
|
var EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55;
|
|
7233
7367
|
var EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000;
|
|
@@ -7262,7 +7396,8 @@ function reserveProtectedTailDrainTokens(args) {
|
|
|
7262
7396
|
}
|
|
7263
7397
|
const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
|
|
7264
7398
|
let latchActiveSince = meta.emergencyDrainActive;
|
|
7265
|
-
|
|
7399
|
+
const { forceMaterializationPercentage } = escalationBands(args.executeThresholdPercentage);
|
|
7400
|
+
if (args.usagePercentage >= forceMaterializationPercentage) {
|
|
7266
7401
|
if (latchActiveSince <= 0)
|
|
7267
7402
|
latchActiveSince = now;
|
|
7268
7403
|
} else if (latchActiveSince > 0) {
|
|
@@ -7414,13 +7549,20 @@ function getChannel2NudgeState(db, sessionId) {
|
|
|
7414
7549
|
function getChannel2NudgeClaim(db, sessionId) {
|
|
7415
7550
|
const result = db.prepare("SELECT channel2_nudge_state, channel2_nudge_claimed_at, channel2_nudge_claim_token FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7416
7551
|
const rawState = typeof result?.channel2_nudge_state === "string" ? result.channel2_nudge_state : "";
|
|
7417
|
-
const
|
|
7552
|
+
const state2 = rawState === "pending" || rawState === "claimed" || rawState === "delivered" ? rawState : "";
|
|
7418
7553
|
return {
|
|
7419
|
-
state,
|
|
7554
|
+
state: state2,
|
|
7420
7555
|
claimedAt: typeof result?.channel2_nudge_claimed_at === "number" ? result.channel2_nudge_claimed_at : 0,
|
|
7421
7556
|
claimToken: typeof result?.channel2_nudge_claim_token === "string" ? result.channel2_nudge_claim_token : ""
|
|
7422
7557
|
};
|
|
7423
7558
|
}
|
|
7559
|
+
function setChannel2NudgeState(db, sessionId, state2) {
|
|
7560
|
+
db.transaction(() => {
|
|
7561
|
+
ensureSessionMetaRow(db, sessionId);
|
|
7562
|
+
const claimedAt = state2 === "claimed" ? Date.now() : 0;
|
|
7563
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ?").run(state2, claimedAt, sessionId);
|
|
7564
|
+
})();
|
|
7565
|
+
}
|
|
7424
7566
|
function casChannel2NudgeState(db, sessionId, from, to) {
|
|
7425
7567
|
let changed = false;
|
|
7426
7568
|
db.transaction(() => {
|
|
@@ -7600,6 +7742,18 @@ function removeAutoSearchHintDecisionByMessageId(db, sessionId, messageId) {
|
|
|
7600
7742
|
}, { ensureRow: false });
|
|
7601
7743
|
return ok && removed;
|
|
7602
7744
|
}
|
|
7745
|
+
function getPersistedTodoPermissionDenied(db, sessionId) {
|
|
7746
|
+
const row = db.prepare("SELECT todo_permission_denied FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7747
|
+
if (row?.todo_permission_denied === 1)
|
|
7748
|
+
return true;
|
|
7749
|
+
if (row?.todo_permission_denied === 0)
|
|
7750
|
+
return false;
|
|
7751
|
+
return null;
|
|
7752
|
+
}
|
|
7753
|
+
function setPersistedTodoPermissionDenied(db, sessionId, denied) {
|
|
7754
|
+
ensureSessionMetaRow(db, sessionId);
|
|
7755
|
+
db.prepare("UPDATE session_meta SET todo_permission_denied = ? WHERE session_id = ?").run(denied ? 1 : 0, sessionId);
|
|
7756
|
+
}
|
|
7603
7757
|
function getPersistedTodoSyntheticAnchor(db, sessionId) {
|
|
7604
7758
|
const result = db.prepare("SELECT todo_synthetic_call_id, todo_synthetic_anchor_message_id, todo_synthetic_state_json FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7605
7759
|
if (!isPersistedTodoSyntheticAnchorRow(result)) {
|
|
@@ -7676,17 +7830,19 @@ function normalizeEmergencyRecoveryOrigin(value) {
|
|
|
7676
7830
|
return value === "provider_overflow" || value === "proactive_model_shrink" ? value : null;
|
|
7677
7831
|
}
|
|
7678
7832
|
function getOverflowState(db, sessionId, modelKey) {
|
|
7679
|
-
const result = db.prepare("SELECT detected_context_limit, detected_context_limit_model_key, needs_emergency_recovery, emergency_recovery_origin FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7833
|
+
const result = db.prepare("SELECT detected_context_limit, detected_context_limit_model_key, detected_context_limit_provenance, needs_emergency_recovery, emergency_recovery_origin FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7680
7834
|
if (!result) {
|
|
7681
7835
|
return {
|
|
7682
7836
|
detectedContextLimit: 0,
|
|
7683
7837
|
detectedContextLimitModelKey: null,
|
|
7838
|
+
detectedContextLimitProvenance: "unknown",
|
|
7684
7839
|
needsEmergencyRecovery: false,
|
|
7685
7840
|
emergencyRecoveryOrigin: null
|
|
7686
7841
|
};
|
|
7687
7842
|
}
|
|
7688
7843
|
const storedModelKey = normalizeDetectedLimitModelKey(result.detected_context_limit_model_key);
|
|
7689
7844
|
const requestedModelKey = normalizeDetectedLimitModelKey(modelKey);
|
|
7845
|
+
const provenance = normalizeContextLimitProvenance(result.detected_context_limit_provenance);
|
|
7690
7846
|
const limit = typeof result.detected_context_limit === "number" && result.detected_context_limit > 0 ? result.detected_context_limit : 0;
|
|
7691
7847
|
const modelMatches = limit > 0 && requestedModelKey && storedModelKey ? requestedModelKey === storedModelKey : true;
|
|
7692
7848
|
const needs = typeof result.needs_emergency_recovery === "number" && result.needs_emergency_recovery > 0;
|
|
@@ -7695,11 +7851,12 @@ function getOverflowState(db, sessionId, modelKey) {
|
|
|
7695
7851
|
return {
|
|
7696
7852
|
detectedContextLimit: modelMatches ? limit : 0,
|
|
7697
7853
|
detectedContextLimitModelKey: storedModelKey,
|
|
7854
|
+
detectedContextLimitProvenance: provenance,
|
|
7698
7855
|
needsEmergencyRecovery: needs,
|
|
7699
7856
|
emergencyRecoveryOrigin: recoveryOrigin
|
|
7700
7857
|
};
|
|
7701
7858
|
}
|
|
7702
|
-
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow") {
|
|
7859
|
+
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow", provenance = "unknown") {
|
|
7703
7860
|
emergencyRecoveryArmedSessions.add(sessionId);
|
|
7704
7861
|
db.transaction(() => {
|
|
7705
7862
|
ensureSessionMetaRow(db, sessionId);
|
|
@@ -7708,18 +7865,18 @@ function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin =
|
|
|
7708
7865
|
providerOverflowReconfirmedSessions.add(sessionId);
|
|
7709
7866
|
}
|
|
7710
7867
|
if (typeof reportedLimit === "number" && reportedLimit > 0) {
|
|
7711
|
-
db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), origin, sessionId);
|
|
7868
|
+
db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, detected_context_limit_provenance = ?, needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), normalizeContextLimitProvenance(provenance), origin, sessionId);
|
|
7712
7869
|
} else {
|
|
7713
7870
|
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(origin, sessionId);
|
|
7714
7871
|
}
|
|
7715
7872
|
})();
|
|
7716
7873
|
}
|
|
7717
|
-
function recordDetectedContextLimit(db, sessionId, reportedLimit, modelKey) {
|
|
7874
|
+
function recordDetectedContextLimit(db, sessionId, reportedLimit, modelKey, provenance = "unknown") {
|
|
7718
7875
|
if (!(reportedLimit > 0))
|
|
7719
7876
|
return;
|
|
7720
7877
|
db.transaction(() => {
|
|
7721
7878
|
ensureSessionMetaRow(db, sessionId);
|
|
7722
|
-
db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), sessionId);
|
|
7879
|
+
db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, detected_context_limit_provenance = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), normalizeContextLimitProvenance(provenance), sessionId);
|
|
7723
7880
|
})();
|
|
7724
7881
|
}
|
|
7725
7882
|
function clearEmergencyRecovery(db, sessionId) {
|
|
@@ -7737,7 +7894,7 @@ function clearEmergencyRecovery(db, sessionId) {
|
|
|
7737
7894
|
function clearDetectedContextLimit(db, sessionId) {
|
|
7738
7895
|
db.transaction(() => {
|
|
7739
7896
|
ensureSessionMetaRow(db, sessionId);
|
|
7740
|
-
db.prepare("UPDATE session_meta SET detected_context_limit = 0, detected_context_limit_model_key = NULL WHERE session_id = ?").run(sessionId);
|
|
7897
|
+
db.prepare("UPDATE session_meta SET detected_context_limit = 0, detected_context_limit_model_key = NULL, detected_context_limit_provenance = 'unknown' WHERE session_id = ?").run(sessionId);
|
|
7741
7898
|
})();
|
|
7742
7899
|
}
|
|
7743
7900
|
function getPersistedCompactionMarkerState(db, sessionId) {
|
|
@@ -7757,10 +7914,10 @@ function getPersistedCompactionMarkerState(db, sessionId) {
|
|
|
7757
7914
|
} catch {}
|
|
7758
7915
|
return null;
|
|
7759
7916
|
}
|
|
7760
|
-
function setPersistedCompactionMarkerState(db, sessionId,
|
|
7917
|
+
function setPersistedCompactionMarkerState(db, sessionId, state2) {
|
|
7761
7918
|
ensureSessionMetaRow(db, sessionId);
|
|
7762
|
-
const json =
|
|
7763
|
-
db.prepare("UPDATE session_meta SET compaction_marker_state = ?, compaction_marker_target_end_message_id = ? WHERE session_id = ?").run(json,
|
|
7919
|
+
const json = state2 ? JSON.stringify(state2) : "";
|
|
7920
|
+
db.prepare("UPDATE session_meta SET compaction_marker_state = ?, compaction_marker_target_end_message_id = ? WHERE session_id = ?").run(json, state2?.targetEndMessageId ?? null, sessionId);
|
|
7764
7921
|
}
|
|
7765
7922
|
function getStrippedPlaceholderIds(db, sessionId) {
|
|
7766
7923
|
const row = db.prepare("SELECT stripped_placeholder_ids FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
@@ -7892,9 +8049,9 @@ function getPendingCompactionMarkerState(db, sessionId) {
|
|
|
7892
8049
|
} catch {}
|
|
7893
8050
|
return null;
|
|
7894
8051
|
}
|
|
7895
|
-
function setPendingCompactionMarkerState(db, sessionId,
|
|
8052
|
+
function setPendingCompactionMarkerState(db, sessionId, state2) {
|
|
7896
8053
|
ensureSessionMetaRow(db, sessionId);
|
|
7897
|
-
const blob =
|
|
8054
|
+
const blob = state2 ? stableStringify(state2) : null;
|
|
7898
8055
|
db.prepare("UPDATE session_meta SET pending_compaction_marker_state = ? WHERE session_id = ?").run(blob, sessionId);
|
|
7899
8056
|
}
|
|
7900
8057
|
function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
|
|
@@ -8165,6 +8322,14 @@ function getPendingOpsStatement(db) {
|
|
|
8165
8322
|
}
|
|
8166
8323
|
return stmt;
|
|
8167
8324
|
}
|
|
8325
|
+
function getClearPendingOpsStatement(db) {
|
|
8326
|
+
let stmt = clearPendingOpsStatements.get(db);
|
|
8327
|
+
if (!stmt) {
|
|
8328
|
+
stmt = db.prepare("DELETE FROM pending_ops WHERE session_id = ?");
|
|
8329
|
+
clearPendingOpsStatements.set(db, stmt);
|
|
8330
|
+
}
|
|
8331
|
+
return stmt;
|
|
8332
|
+
}
|
|
8168
8333
|
function getRemovePendingOpStatement(db) {
|
|
8169
8334
|
let stmt = removePendingOpStatements.get(db);
|
|
8170
8335
|
if (!stmt) {
|
|
@@ -8199,6 +8364,9 @@ function getPendingOps(db, sessionId) {
|
|
|
8199
8364
|
const rows = getPendingOpsStatement(db).all(sessionId).filter(isPendingOpRow);
|
|
8200
8365
|
return rows.map(toPendingOp).filter((op) => op !== null);
|
|
8201
8366
|
}
|
|
8367
|
+
function clearPendingOps(db, sessionId) {
|
|
8368
|
+
getClearPendingOpsStatement(db).run(sessionId);
|
|
8369
|
+
}
|
|
8202
8370
|
function removePendingOp(db, sessionId, tagId) {
|
|
8203
8371
|
getRemovePendingOpStatement(db).run(sessionId, tagId);
|
|
8204
8372
|
}
|
|
@@ -8394,11 +8562,11 @@ function bumpProjectMemoryEpoch(db, projectPath, now = Date.now()) {
|
|
|
8394
8562
|
ON CONFLICT(project_path) DO UPDATE SET
|
|
8395
8563
|
project_memory_epoch = project_memory_epoch + 1,
|
|
8396
8564
|
updated_at = excluded.updated_at`).run(projectPath, now);
|
|
8397
|
-
const
|
|
8398
|
-
if (!
|
|
8565
|
+
const state2 = getProjectState(db, projectPath);
|
|
8566
|
+
if (!state2) {
|
|
8399
8567
|
throw new Error(`Failed to bump project memory epoch for ${projectPath}`);
|
|
8400
8568
|
}
|
|
8401
|
-
return
|
|
8569
|
+
return state2;
|
|
8402
8570
|
}
|
|
8403
8571
|
function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PROJECT_PATH, now = Date.now()) {
|
|
8404
8572
|
db.prepare(`INSERT INTO project_state
|
|
@@ -8407,11 +8575,11 @@ function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PRO
|
|
|
8407
8575
|
ON CONFLICT(project_path) DO UPDATE SET
|
|
8408
8576
|
project_user_profile_version = project_user_profile_version + 1,
|
|
8409
8577
|
updated_at = excluded.updated_at`).run(projectPath, now);
|
|
8410
|
-
const
|
|
8411
|
-
if (!
|
|
8578
|
+
const state2 = getProjectState(db, projectPath);
|
|
8579
|
+
if (!state2) {
|
|
8412
8580
|
throw new Error(`Failed to bump project user profile version for ${projectPath}`);
|
|
8413
8581
|
}
|
|
8414
|
-
return
|
|
8582
|
+
return state2;
|
|
8415
8583
|
}
|
|
8416
8584
|
// src/features/magic-context/storage-source.ts
|
|
8417
8585
|
function isSourceContentRow(row) {
|
|
@@ -9001,6 +9169,518 @@ var ERROR_CLASSES = new Set([
|
|
|
9001
9169
|
"permission_denied",
|
|
9002
9170
|
"unknown"
|
|
9003
9171
|
]);
|
|
9172
|
+
// src/hooks/magic-context/child-session-spawn.ts
|
|
9173
|
+
var STALE_PLUGIN_RESTART_NOTICE = "Magic Context: plugin build is older than its database — restart OpenCode";
|
|
9174
|
+
async function surfaceStalePluginBuild(args) {
|
|
9175
|
+
if (!args.parentSessionId)
|
|
9176
|
+
return;
|
|
9177
|
+
try {
|
|
9178
|
+
if (args.db) {
|
|
9179
|
+
updateSessionMeta(args.db, args.parentSessionId, {
|
|
9180
|
+
lastTransformError: STALE_PLUGIN_RESTART_NOTICE
|
|
9181
|
+
});
|
|
9182
|
+
}
|
|
9183
|
+
pushNotification("toast", {
|
|
9184
|
+
title: "Magic Context",
|
|
9185
|
+
message: STALE_PLUGIN_RESTART_NOTICE,
|
|
9186
|
+
variant: "error",
|
|
9187
|
+
duration: 1e4
|
|
9188
|
+
}, args.parentSessionId);
|
|
9189
|
+
pushNotification("action", { action: "refresh-sidebar" }, args.parentSessionId);
|
|
9190
|
+
await sendIgnoredMessage(args.client, args.parentSessionId, STALE_PLUGIN_RESTART_NOTICE, args.notificationParams ?? {}, true);
|
|
9191
|
+
} catch (error) {
|
|
9192
|
+
sessionLog(args.parentSessionId, `stale plugin-build warning delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
9193
|
+
}
|
|
9194
|
+
}
|
|
9195
|
+
async function createChildSessionWithFence(args) {
|
|
9196
|
+
const verdict = probeChildSpawnFence(args.db);
|
|
9197
|
+
if (!verdict.allowSpawn) {
|
|
9198
|
+
if (args.parentSessionId) {
|
|
9199
|
+
sessionLog(args.parentSessionId, `child session skipped (${verdict.failure.failureClass}): database=v${verdict.failure.persistedVersion}, supported_fence=v${verdict.failure.supportedVersion}, consecutive=${verdict.failure.consecutiveFailures}, total=${verdict.failure.totalFailures}`);
|
|
9200
|
+
}
|
|
9201
|
+
if (verdict.shouldSurface) {
|
|
9202
|
+
if (args.onFenceLatched)
|
|
9203
|
+
await args.onFenceLatched(verdict.failure);
|
|
9204
|
+
else
|
|
9205
|
+
await surfaceStalePluginBuild(args);
|
|
9206
|
+
}
|
|
9207
|
+
return null;
|
|
9208
|
+
}
|
|
9209
|
+
return args.client.session.create({
|
|
9210
|
+
body: {
|
|
9211
|
+
...args.parentSessionId ? { parentID: args.parentSessionId } : {},
|
|
9212
|
+
title: args.title
|
|
9213
|
+
},
|
|
9214
|
+
query: { directory: args.directory }
|
|
9215
|
+
});
|
|
9216
|
+
}
|
|
9217
|
+
// src/features/magic-context/overflow-detection.ts
|
|
9218
|
+
var OVERFLOW_PATTERNS = [
|
|
9219
|
+
/prompt is too long/i,
|
|
9220
|
+
/input is too long for requested model/i,
|
|
9221
|
+
/exceeds the context window/i,
|
|
9222
|
+
/input token count.*exceeds the maximum/i,
|
|
9223
|
+
/maximum prompt length is \d+/i,
|
|
9224
|
+
/reduce the length of the messages/i,
|
|
9225
|
+
/maximum context length is \d+ tokens/i,
|
|
9226
|
+
/maximum model length is \d+/i,
|
|
9227
|
+
/exceeds the limit of \d+/i,
|
|
9228
|
+
/exceeds the available context size/i,
|
|
9229
|
+
/greater than the context length/i,
|
|
9230
|
+
/context window exceeds limit/i,
|
|
9231
|
+
/exceeded model token limit/i,
|
|
9232
|
+
/context[_ ]length[_ ]exceeded/i,
|
|
9233
|
+
/request entity too large/i,
|
|
9234
|
+
/context length is only \d+ tokens/i,
|
|
9235
|
+
/input length.*exceeds.*context length/i,
|
|
9236
|
+
/prompt too long; exceeded (?:max )?context length/i,
|
|
9237
|
+
/too large for model with \d+ maximum context length/i,
|
|
9238
|
+
/model_context_window_exceeded/i,
|
|
9239
|
+
/context size has been exceeded/i
|
|
9240
|
+
];
|
|
9241
|
+
var LIMIT_EXTRACTION_PATTERNS = [
|
|
9242
|
+
{ pattern: /maximum prompt length is (\d+)/i, provenance: "prompt_only" },
|
|
9243
|
+
{
|
|
9244
|
+
pattern: /maximum context length is (\d+) tokens?/i,
|
|
9245
|
+
provenance: "combined"
|
|
9246
|
+
},
|
|
9247
|
+
{ pattern: /maximum model length is (\d+)/i, provenance: "combined" },
|
|
9248
|
+
{ pattern: /context length is only (\d+) tokens?/i, provenance: "combined" },
|
|
9249
|
+
{ pattern: /exceeds the limit of (\d+)/i, provenance: "unknown" },
|
|
9250
|
+
{
|
|
9251
|
+
pattern: /too large for model with (\d+) maximum context length/i,
|
|
9252
|
+
provenance: "combined"
|
|
9253
|
+
},
|
|
9254
|
+
{ pattern: /context size.*(\d+) tokens?/i, provenance: "combined" },
|
|
9255
|
+
{ pattern: /exceeds? the context length of (\d+)/i, provenance: "combined" },
|
|
9256
|
+
{
|
|
9257
|
+
pattern: />\s*(\d+)\s*(?:tokens?\s*)?(?:maximum|max|limit)\b/i,
|
|
9258
|
+
provenance: "prompt_only"
|
|
9259
|
+
},
|
|
9260
|
+
{ pattern: /max(?:imum)?.*context.*?(\d+)/i, provenance: "unknown" }
|
|
9261
|
+
];
|
|
9262
|
+
var MIN_PLAUSIBLE_LIMIT = 1024;
|
|
9263
|
+
var MAX_PLAUSIBLE_LIMIT = 1e7;
|
|
9264
|
+
function extractErrorMessage(error) {
|
|
9265
|
+
if (!error)
|
|
9266
|
+
return "";
|
|
9267
|
+
if (typeof error === "string")
|
|
9268
|
+
return error;
|
|
9269
|
+
if (typeof error === "object") {
|
|
9270
|
+
const obj = error;
|
|
9271
|
+
const nested = obj.error;
|
|
9272
|
+
if (nested && typeof nested.message === "string" && nested.message.length > 0) {
|
|
9273
|
+
return nested.message;
|
|
9274
|
+
}
|
|
9275
|
+
}
|
|
9276
|
+
if (error instanceof Error)
|
|
9277
|
+
return error.message;
|
|
9278
|
+
if (typeof error === "object") {
|
|
9279
|
+
const obj = error;
|
|
9280
|
+
if (typeof obj.message === "string")
|
|
9281
|
+
return obj.message;
|
|
9282
|
+
if (typeof obj.responseBody === "string")
|
|
9283
|
+
return obj.responseBody;
|
|
9284
|
+
try {
|
|
9285
|
+
return JSON.stringify(error);
|
|
9286
|
+
} catch {
|
|
9287
|
+
return String(error);
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
return String(error);
|
|
9291
|
+
}
|
|
9292
|
+
function detectOverflow(error) {
|
|
9293
|
+
const message = extractErrorMessage(error);
|
|
9294
|
+
if (!message) {
|
|
9295
|
+
return { isOverflow: false };
|
|
9296
|
+
}
|
|
9297
|
+
const hasStatus413 = /\b413\b/.test(message) && /(entity|payload|context|prompt)/i.test(message);
|
|
9298
|
+
let matched;
|
|
9299
|
+
for (const pattern of OVERFLOW_PATTERNS) {
|
|
9300
|
+
if (pattern.test(message)) {
|
|
9301
|
+
matched = pattern;
|
|
9302
|
+
break;
|
|
9303
|
+
}
|
|
9304
|
+
}
|
|
9305
|
+
if (!matched && !hasStatus413) {
|
|
9306
|
+
return { isOverflow: false };
|
|
9307
|
+
}
|
|
9308
|
+
const reportedLimit = parseReportedLimit(message);
|
|
9309
|
+
return {
|
|
9310
|
+
isOverflow: true,
|
|
9311
|
+
reportedLimit: reportedLimit?.value,
|
|
9312
|
+
reportedLimitProvenance: reportedLimit?.provenance,
|
|
9313
|
+
matchedPattern: matched?.source
|
|
9314
|
+
};
|
|
9315
|
+
}
|
|
9316
|
+
function parseReportedLimit(message) {
|
|
9317
|
+
if (!message)
|
|
9318
|
+
return;
|
|
9319
|
+
for (const { pattern, provenance } of LIMIT_EXTRACTION_PATTERNS) {
|
|
9320
|
+
const match = message.match(pattern);
|
|
9321
|
+
if (!match)
|
|
9322
|
+
continue;
|
|
9323
|
+
const raw = match[1];
|
|
9324
|
+
if (!raw)
|
|
9325
|
+
continue;
|
|
9326
|
+
const value = Number.parseInt(raw, 10);
|
|
9327
|
+
if (!Number.isFinite(value))
|
|
9328
|
+
continue;
|
|
9329
|
+
if (value < MIN_PLAUSIBLE_LIMIT || value > MAX_PLAUSIBLE_LIMIT)
|
|
9330
|
+
continue;
|
|
9331
|
+
return { value, provenance };
|
|
9332
|
+
}
|
|
9333
|
+
return;
|
|
9334
|
+
}
|
|
9335
|
+
|
|
9336
|
+
// src/shared/model-suggestion-retry.ts
|
|
9337
|
+
var ABORT_CALL_TIMEOUT_MS = 3000;
|
|
9338
|
+
function copyPromptArgs(args, body) {
|
|
9339
|
+
return { ...args, body: { ...body } };
|
|
9340
|
+
}
|
|
9341
|
+
function extractMessage(error) {
|
|
9342
|
+
if (typeof error === "string")
|
|
9343
|
+
return error;
|
|
9344
|
+
if (error instanceof Error)
|
|
9345
|
+
return error.message;
|
|
9346
|
+
if (typeof error === "object" && error !== null) {
|
|
9347
|
+
const obj = error;
|
|
9348
|
+
if (typeof obj.message === "string")
|
|
9349
|
+
return obj.message;
|
|
9350
|
+
}
|
|
9351
|
+
try {
|
|
9352
|
+
return JSON.stringify(error);
|
|
9353
|
+
} catch (_error) {
|
|
9354
|
+
return String(error);
|
|
9355
|
+
}
|
|
9356
|
+
}
|
|
9357
|
+
function parseModelSuggestion(error) {
|
|
9358
|
+
if (!error)
|
|
9359
|
+
return null;
|
|
9360
|
+
if (typeof error === "object" && error !== null) {
|
|
9361
|
+
const errObj = error;
|
|
9362
|
+
if (errObj.name === "ProviderModelNotFoundError" && typeof errObj.data === "object" && errObj.data !== null) {
|
|
9363
|
+
const data = errObj.data;
|
|
9364
|
+
const suggestions = data.suggestions;
|
|
9365
|
+
if (Array.isArray(suggestions) && typeof suggestions[0] === "string") {
|
|
9366
|
+
return {
|
|
9367
|
+
providerID: String(data.providerID ?? ""),
|
|
9368
|
+
modelID: String(data.modelID ?? ""),
|
|
9369
|
+
suggestion: suggestions[0]
|
|
9370
|
+
};
|
|
9371
|
+
}
|
|
9372
|
+
}
|
|
9373
|
+
for (const key of ["data", "error", "cause"]) {
|
|
9374
|
+
const nested = errObj[key];
|
|
9375
|
+
if (nested && typeof nested === "object") {
|
|
9376
|
+
const result = parseModelSuggestion(nested);
|
|
9377
|
+
if (result)
|
|
9378
|
+
return result;
|
|
9379
|
+
}
|
|
9380
|
+
}
|
|
9381
|
+
}
|
|
9382
|
+
const message = extractMessage(error);
|
|
9383
|
+
const modelMatch = message.match(/model not found:\s*([^/\s]+)\s*\/\s*([^.,\s]+)/i);
|
|
9384
|
+
const suggestionMatch = message.match(/did you mean:\s*([^,?]+)/i);
|
|
9385
|
+
if (!modelMatch || !suggestionMatch) {
|
|
9386
|
+
return null;
|
|
9387
|
+
}
|
|
9388
|
+
return {
|
|
9389
|
+
providerID: modelMatch[1].trim(),
|
|
9390
|
+
modelID: modelMatch[2].trim(),
|
|
9391
|
+
suggestion: suggestionMatch[1].trim()
|
|
9392
|
+
};
|
|
9393
|
+
}
|
|
9394
|
+
async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
9395
|
+
if (signal?.aborted) {
|
|
9396
|
+
throw new Error("prompt aborted by external signal");
|
|
9397
|
+
}
|
|
9398
|
+
const controller = new AbortController;
|
|
9399
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
9400
|
+
const onExternalAbort = () => controller.abort();
|
|
9401
|
+
signal?.addEventListener("abort", onExternalAbort);
|
|
9402
|
+
try {
|
|
9403
|
+
await client.session.prompt({
|
|
9404
|
+
...args,
|
|
9405
|
+
signal: controller.signal
|
|
9406
|
+
});
|
|
9407
|
+
} catch (error) {
|
|
9408
|
+
if (signal?.aborted) {
|
|
9409
|
+
await abortChildRun(client, args.path.id);
|
|
9410
|
+
throw new Error("prompt aborted by external signal");
|
|
9411
|
+
}
|
|
9412
|
+
if (controller.signal.aborted) {
|
|
9413
|
+
await abortChildRun(client, args.path.id);
|
|
9414
|
+
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
9415
|
+
}
|
|
9416
|
+
throw error;
|
|
9417
|
+
} finally {
|
|
9418
|
+
clearTimeout(timeout);
|
|
9419
|
+
signal?.removeEventListener("abort", onExternalAbort);
|
|
9420
|
+
}
|
|
9421
|
+
}
|
|
9422
|
+
async function abortChildRun(client, sessionId) {
|
|
9423
|
+
try {
|
|
9424
|
+
await Promise.race([
|
|
9425
|
+
client.session.abort({ path: { id: sessionId } }),
|
|
9426
|
+
new Promise((resolve) => setTimeout(resolve, ABORT_CALL_TIMEOUT_MS))
|
|
9427
|
+
]);
|
|
9428
|
+
} catch (error) {
|
|
9429
|
+
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error)}`);
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
function isNonRetryable(error, externalSignal) {
|
|
9433
|
+
if (externalSignal?.aborted)
|
|
9434
|
+
return true;
|
|
9435
|
+
if (error instanceof Error) {
|
|
9436
|
+
if (error.name === "AbortError")
|
|
9437
|
+
return true;
|
|
9438
|
+
if (error.message === "prompt aborted by external signal")
|
|
9439
|
+
return true;
|
|
9440
|
+
if (/^prompt timed out after \d+ms$/.test(error.message))
|
|
9441
|
+
return true;
|
|
9442
|
+
}
|
|
9443
|
+
if (detectOverflow(error).isOverflow)
|
|
9444
|
+
return true;
|
|
9445
|
+
return false;
|
|
9446
|
+
}
|
|
9447
|
+
function shortErr(error) {
|
|
9448
|
+
if (error instanceof Error) {
|
|
9449
|
+
return error.name && error.name !== "Error" ? `${error.name}: ${error.message}` : error.message;
|
|
9450
|
+
}
|
|
9451
|
+
return extractMessage(error);
|
|
9452
|
+
}
|
|
9453
|
+
async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
|
|
9454
|
+
const originalBody = { ...args.body };
|
|
9455
|
+
const attemptArgs = copyPromptArgs(args, originalBody);
|
|
9456
|
+
try {
|
|
9457
|
+
await promptWithTimeout(client, attemptArgs, timeoutMs, signal);
|
|
9458
|
+
return;
|
|
9459
|
+
} catch (error) {
|
|
9460
|
+
if (isNonRetryable(error, signal))
|
|
9461
|
+
throw error;
|
|
9462
|
+
const suggestion = parseModelSuggestion(error);
|
|
9463
|
+
if (!suggestion || !originalBody.model) {
|
|
9464
|
+
throw error;
|
|
9465
|
+
}
|
|
9466
|
+
log(`[${callContext}] ${label}: model not found, retrying with suggestion`, {
|
|
9467
|
+
original: `${suggestion.providerID}/${suggestion.modelID}`,
|
|
9468
|
+
suggested: suggestion.suggestion
|
|
9469
|
+
});
|
|
9470
|
+
await promptWithTimeout(client, copyPromptArgs(args, {
|
|
9471
|
+
...originalBody,
|
|
9472
|
+
model: {
|
|
9473
|
+
providerID: suggestion.providerID,
|
|
9474
|
+
modelID: suggestion.suggestion
|
|
9475
|
+
}
|
|
9476
|
+
}), timeoutMs, signal);
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
|
|
9480
|
+
const timeoutMs = options.timeoutMs ?? 300000;
|
|
9481
|
+
const callContext = options.callContext ?? "subagent";
|
|
9482
|
+
const fallbacks = options.fallbackModels ?? [];
|
|
9483
|
+
const baseBody = { ...args.body };
|
|
9484
|
+
const baseArgs = copyPromptArgs(args, baseBody);
|
|
9485
|
+
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
9486
|
+
let lastError = null;
|
|
9487
|
+
try {
|
|
9488
|
+
await attemptOnce(client, baseArgs, timeoutMs, options.signal, callContext, explicitPrimaryLabel);
|
|
9489
|
+
return;
|
|
9490
|
+
} catch (error) {
|
|
9491
|
+
lastError = error;
|
|
9492
|
+
if (isNonRetryable(error, options.signal))
|
|
9493
|
+
throw error;
|
|
9494
|
+
if (fallbacks.length === 0) {
|
|
9495
|
+
throw error;
|
|
9496
|
+
}
|
|
9497
|
+
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
9498
|
+
}
|
|
9499
|
+
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
9500
|
+
const parsed = parseProviderModel(fallbacks[i]);
|
|
9501
|
+
if (!parsed) {
|
|
9502
|
+
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
9503
|
+
continue;
|
|
9504
|
+
}
|
|
9505
|
+
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
9506
|
+
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
9507
|
+
...baseBody,
|
|
9508
|
+
model: parsed
|
|
9509
|
+
});
|
|
9510
|
+
try {
|
|
9511
|
+
await attemptOnce(client, attemptArgs, timeoutMs, options.signal, callContext, label);
|
|
9512
|
+
log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
|
|
9513
|
+
return;
|
|
9514
|
+
} catch (error) {
|
|
9515
|
+
lastError = error;
|
|
9516
|
+
if (isNonRetryable(error, options.signal))
|
|
9517
|
+
throw error;
|
|
9518
|
+
const remaining = fallbacks.length - i - 1;
|
|
9519
|
+
if (remaining > 0) {
|
|
9520
|
+
log(`[${callContext}] ${label} failed: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
9524
|
+
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; last error: ${shortErr(lastError)}`);
|
|
9525
|
+
throw lastError ?? new Error("All fallback models failed");
|
|
9526
|
+
}
|
|
9527
|
+
async function attemptAndValidate(client, args, timeoutMs, signal, callContext, attempt, options) {
|
|
9528
|
+
await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
|
|
9529
|
+
const output = await options.fetchOutput(args, attempt);
|
|
9530
|
+
const validated = await options.validateOutput(output, attempt);
|
|
9531
|
+
return { output, validated, attempt };
|
|
9532
|
+
}
|
|
9533
|
+
async function promptSyncWithValidatedOutputRetry(client, args, options) {
|
|
9534
|
+
const timeoutMs = options.timeoutMs ?? 300000;
|
|
9535
|
+
const callContext = options.callContext ?? "subagent";
|
|
9536
|
+
const fallbacks = options.fallbackModels ?? [];
|
|
9537
|
+
const baseBody = { ...args.body };
|
|
9538
|
+
const baseArgs = copyPromptArgs(args, baseBody);
|
|
9539
|
+
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
9540
|
+
const totalAttempts = fallbacks.length + 1;
|
|
9541
|
+
let firstError = null;
|
|
9542
|
+
let lastError = null;
|
|
9543
|
+
try {
|
|
9544
|
+
return await attemptAndValidate(client, baseArgs, timeoutMs, options.signal, callContext, {
|
|
9545
|
+
label: explicitPrimaryLabel,
|
|
9546
|
+
attemptIndex: 0,
|
|
9547
|
+
isFallback: false,
|
|
9548
|
+
totalAttempts,
|
|
9549
|
+
model: baseBody.model
|
|
9550
|
+
}, options);
|
|
9551
|
+
} catch (error) {
|
|
9552
|
+
firstError = error;
|
|
9553
|
+
lastError = error;
|
|
9554
|
+
if (isNonRetryable(error, options.signal))
|
|
9555
|
+
throw error;
|
|
9556
|
+
if (fallbacks.length === 0) {
|
|
9557
|
+
throw error;
|
|
9558
|
+
}
|
|
9559
|
+
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
9560
|
+
}
|
|
9561
|
+
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
9562
|
+
const parsed = parseProviderModel(fallbacks[i]);
|
|
9563
|
+
if (!parsed) {
|
|
9564
|
+
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
9565
|
+
continue;
|
|
9566
|
+
}
|
|
9567
|
+
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
9568
|
+
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
9569
|
+
...baseBody,
|
|
9570
|
+
model: parsed
|
|
9571
|
+
});
|
|
9572
|
+
const attempt = {
|
|
9573
|
+
label,
|
|
9574
|
+
attemptIndex: i + 1,
|
|
9575
|
+
isFallback: true,
|
|
9576
|
+
totalAttempts,
|
|
9577
|
+
model: parsed
|
|
9578
|
+
};
|
|
9579
|
+
try {
|
|
9580
|
+
const result = await attemptAndValidate(client, attemptArgs, timeoutMs, options.signal, callContext, attempt, options);
|
|
9581
|
+
log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
|
|
9582
|
+
return result;
|
|
9583
|
+
} catch (error) {
|
|
9584
|
+
if (firstError === null)
|
|
9585
|
+
firstError = error;
|
|
9586
|
+
lastError = error;
|
|
9587
|
+
if (isNonRetryable(error, options.signal))
|
|
9588
|
+
throw error;
|
|
9589
|
+
const remaining = fallbacks.length - i - 1;
|
|
9590
|
+
if (remaining > 0) {
|
|
9591
|
+
log(`[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
9592
|
+
}
|
|
9593
|
+
}
|
|
9594
|
+
}
|
|
9595
|
+
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`);
|
|
9596
|
+
throw firstError ?? lastError ?? new Error("All fallback models failed validation");
|
|
9597
|
+
}
|
|
9598
|
+
// src/shared/normalize-sdk-response.ts
|
|
9599
|
+
function normalizeSDKResponse(response, fallback, options) {
|
|
9600
|
+
if (response === null || response === undefined) {
|
|
9601
|
+
return fallback;
|
|
9602
|
+
}
|
|
9603
|
+
if (Array.isArray(response)) {
|
|
9604
|
+
return response;
|
|
9605
|
+
}
|
|
9606
|
+
if (typeof response === "object" && "data" in response) {
|
|
9607
|
+
const data = response.data;
|
|
9608
|
+
if (data !== null && data !== undefined) {
|
|
9609
|
+
return data;
|
|
9610
|
+
}
|
|
9611
|
+
if (options?.preferResponseOnMissingData === true) {
|
|
9612
|
+
return response;
|
|
9613
|
+
}
|
|
9614
|
+
return fallback;
|
|
9615
|
+
}
|
|
9616
|
+
if (options?.preferResponseOnMissingData === true) {
|
|
9617
|
+
return response;
|
|
9618
|
+
}
|
|
9619
|
+
return fallback;
|
|
9620
|
+
}
|
|
9621
|
+
// src/shared/assistant-message-extractor.ts
|
|
9622
|
+
function asSessionMessage(value) {
|
|
9623
|
+
if (!isRecord(value))
|
|
9624
|
+
return null;
|
|
9625
|
+
const info = value.info;
|
|
9626
|
+
const parts = value.parts;
|
|
9627
|
+
return {
|
|
9628
|
+
info: isRecord(info) ? {
|
|
9629
|
+
role: typeof info.role === "string" ? info.role : undefined,
|
|
9630
|
+
time: isRecord(info.time) ? {
|
|
9631
|
+
created: typeof info.time.created === "number" ? info.time.created : undefined
|
|
9632
|
+
} : undefined
|
|
9633
|
+
} : undefined,
|
|
9634
|
+
parts
|
|
9635
|
+
};
|
|
9636
|
+
}
|
|
9637
|
+
function getCreatedTime(message) {
|
|
9638
|
+
return message.info?.time?.created ?? 0;
|
|
9639
|
+
}
|
|
9640
|
+
function getTextParts(message) {
|
|
9641
|
+
if (!Array.isArray(message.parts))
|
|
9642
|
+
return [];
|
|
9643
|
+
return message.parts.filter((part) => isRecord(part)).map((part) => ({
|
|
9644
|
+
type: typeof part.type === "string" ? part.type : undefined,
|
|
9645
|
+
text: typeof part.text === "string" ? part.text : undefined
|
|
9646
|
+
})).filter((part) => part.type === "text" && Boolean(part.text));
|
|
9647
|
+
}
|
|
9648
|
+
function extractLatestAssistantText(messages) {
|
|
9649
|
+
if (!Array.isArray(messages) || messages.length === 0)
|
|
9650
|
+
return null;
|
|
9651
|
+
const assistantMessages = messages.map(asSessionMessage).filter((message) => message !== null).filter((message) => message.info?.role === "assistant").sort((a, b) => getCreatedTime(b) - getCreatedTime(a));
|
|
9652
|
+
const latest = assistantMessages[0];
|
|
9653
|
+
if (!latest)
|
|
9654
|
+
return null;
|
|
9655
|
+
return getTextParts(latest).map((part) => part.text).join(`
|
|
9656
|
+
`) || null;
|
|
9657
|
+
}
|
|
9658
|
+
function hasLengthCappedOutput(value) {
|
|
9659
|
+
if (Array.isArray(value))
|
|
9660
|
+
return value.some((item) => hasLengthCappedOutput(item));
|
|
9661
|
+
if (!isRecord(value))
|
|
9662
|
+
return false;
|
|
9663
|
+
if (value.length_capped === true || value.lengthCapped === true)
|
|
9664
|
+
return true;
|
|
9665
|
+
const finishReason = value.finish_reason ?? value.finishReason;
|
|
9666
|
+
if (typeof finishReason === "string") {
|
|
9667
|
+
const normalized = finishReason.toLowerCase();
|
|
9668
|
+
if (normalized === "length" || normalized === "max_tokens" || normalized === "max_output_tokens") {
|
|
9669
|
+
return true;
|
|
9670
|
+
}
|
|
9671
|
+
}
|
|
9672
|
+
return Object.values(value).some((item) => hasLengthCappedOutput(item));
|
|
9673
|
+
}
|
|
9674
|
+
|
|
9675
|
+
// src/shared/keep-subagents.ts
|
|
9676
|
+
var keepSubagents = false;
|
|
9677
|
+
function setKeepSubagents(value) {
|
|
9678
|
+
keepSubagents = value === true;
|
|
9679
|
+
}
|
|
9680
|
+
function shouldKeepSubagents() {
|
|
9681
|
+
return keepSubagents;
|
|
9682
|
+
}
|
|
9683
|
+
|
|
9004
9684
|
// src/features/magic-context/user-memory/storage-user-memory.ts
|
|
9005
9685
|
var USER_MEMORY_CANDIDATE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
9006
9686
|
function insertUserMemoryCandidates(db, candidates) {
|
|
@@ -9068,9 +9748,9 @@ function parseUserMemoryRow(row) {
|
|
|
9068
9748
|
}
|
|
9069
9749
|
|
|
9070
9750
|
// src/features/magic-context/mural/storage-mural-cues.ts
|
|
9071
|
-
import { createHash as
|
|
9751
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
9072
9752
|
function computeCueContentHash(content) {
|
|
9073
|
-
return
|
|
9753
|
+
return createHash8("sha256").update(content).digest("hex");
|
|
9074
9754
|
}
|
|
9075
9755
|
var muralCueColumnCache = new WeakMap;
|
|
9076
9756
|
function hasMuralCueColumns(db) {
|
|
@@ -9096,10 +9776,10 @@ function getMuralCueState(db, memoryIds) {
|
|
|
9096
9776
|
}
|
|
9097
9777
|
return out;
|
|
9098
9778
|
}
|
|
9099
|
-
function memoryNeedsCue(
|
|
9100
|
-
if (!
|
|
9779
|
+
function memoryNeedsCue(state2, currentContent) {
|
|
9780
|
+
if (!state2 || state2.cue === null || state2.hash === null)
|
|
9101
9781
|
return true;
|
|
9102
|
-
return
|
|
9782
|
+
return state2.hash !== computeCueContentHash(currentContent);
|
|
9103
9783
|
}
|
|
9104
9784
|
function setMuralCue(db, projectPath, id, cue, contentHash) {
|
|
9105
9785
|
if (!hasMuralCueColumns(db))
|
|
@@ -9121,7 +9801,6 @@ var deleteEmbeddingStatements = new WeakMap;
|
|
|
9121
9801
|
var getStoredModelIdStatements = new WeakMap;
|
|
9122
9802
|
var clearAllEmbeddingsStatements = new WeakMap;
|
|
9123
9803
|
var clearModelEmbeddingsStatements = new WeakMap;
|
|
9124
|
-
var getDistinctStoredModelIdsStatements = new WeakMap;
|
|
9125
9804
|
function isEmbeddingBlob(value) {
|
|
9126
9805
|
return value instanceof Uint8Array || value instanceof ArrayBuffer;
|
|
9127
9806
|
}
|
|
@@ -9238,13 +9917,13 @@ function invalidateMemory(projectPath, memoryId) {
|
|
|
9238
9917
|
}
|
|
9239
9918
|
|
|
9240
9919
|
// src/features/magic-context/memory/normalize-hash.ts
|
|
9241
|
-
import { createHash as
|
|
9920
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
9242
9921
|
function normalizeMemoryContent(content) {
|
|
9243
9922
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
9244
9923
|
}
|
|
9245
9924
|
function computeNormalizedHash(content) {
|
|
9246
9925
|
const normalized = normalizeMemoryContent(content);
|
|
9247
|
-
return
|
|
9926
|
+
return createHash9("md5").update(normalized).digest("hex");
|
|
9248
9927
|
}
|
|
9249
9928
|
|
|
9250
9929
|
// src/features/magic-context/memory/visibility.ts
|
|
@@ -9636,7 +10315,6 @@ class ModuleMemoryAuthorityError extends Error {
|
|
|
9636
10315
|
}
|
|
9637
10316
|
}
|
|
9638
10317
|
function assertTsMemoryWriteAllowed(db, projectPath) {
|
|
9639
|
-
registerPrivilegedWriter(db);
|
|
9640
10318
|
try {
|
|
9641
10319
|
const managed = db.prepare("SELECT 1 FROM authority_managed WHERE project_path = ? UNION SELECT 1 FROM authority_repair_pending WHERE project_path = ? LIMIT 1").get(projectPath, projectPath);
|
|
9642
10320
|
if (managed)
|
|
@@ -10109,9 +10787,12 @@ async function runMemoryMigration(deps) {
|
|
|
10109
10787
|
const modelOverride = modelId ? parseProviderModel(modelId) : null;
|
|
10110
10788
|
await cleanupChildSession(agentSessionId);
|
|
10111
10789
|
agentSessionId = null;
|
|
10112
|
-
const createResponse = await
|
|
10113
|
-
|
|
10114
|
-
|
|
10790
|
+
const createResponse = await createChildSessionWithFence({
|
|
10791
|
+
client,
|
|
10792
|
+
db,
|
|
10793
|
+
parentSessionId,
|
|
10794
|
+
title: "magic-context-memory-migration",
|
|
10795
|
+
directory
|
|
10115
10796
|
});
|
|
10116
10797
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
10117
10798
|
preferResponseOnMissingData: true
|
|
@@ -10211,7 +10892,7 @@ async function runMemoryMigration(deps) {
|
|
|
10211
10892
|
}
|
|
10212
10893
|
|
|
10213
10894
|
// src/features/magic-context/compartment-chunk-embedding.ts
|
|
10214
|
-
import { createHash as
|
|
10895
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
10215
10896
|
|
|
10216
10897
|
// src/features/magic-context/recursive-text-splitter.ts
|
|
10217
10898
|
var DEFAULT_SEPARATORS = [`
|
|
@@ -10306,9 +10987,6 @@ var existingHashStatements = new WeakMap;
|
|
|
10306
10987
|
var existingHashByProjectStatements = new WeakMap;
|
|
10307
10988
|
var deleteByCompartmentStatements = new WeakMap;
|
|
10308
10989
|
var insertEmbeddingStatements = new WeakMap;
|
|
10309
|
-
var distinctModelStatements = new WeakMap;
|
|
10310
|
-
var clearProjectStatements = new WeakMap;
|
|
10311
|
-
var clearProjectModelStatements = new WeakMap;
|
|
10312
10990
|
var searchRowsStatements = new WeakMap;
|
|
10313
10991
|
var searchRowsByModelStatements = new WeakMap;
|
|
10314
10992
|
var searchPoolProbeStatements = new WeakMap;
|
|
@@ -10499,7 +11177,7 @@ function parseCanonicalLineRange(line) {
|
|
|
10499
11177
|
return { start, end };
|
|
10500
11178
|
}
|
|
10501
11179
|
function hashChunkText(text) {
|
|
10502
|
-
return
|
|
11180
|
+
return createHash10("sha256").update(text).digest("hex");
|
|
10503
11181
|
}
|
|
10504
11182
|
function vectorBlob2(vector) {
|
|
10505
11183
|
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
@@ -10890,7 +11568,7 @@ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelI
|
|
|
10890
11568
|
}
|
|
10891
11569
|
|
|
10892
11570
|
// src/features/magic-context/project-embedding-registry.ts
|
|
10893
|
-
import { createHash as
|
|
11571
|
+
import { createHash as createHash12, randomUUID as randomUUID2 } from "node:crypto";
|
|
10894
11572
|
|
|
10895
11573
|
// src/config/schema/magic-context.ts
|
|
10896
11574
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -25342,12 +26020,11 @@ var AgentOverrideConfigSchema = exports_external.object({
|
|
|
25342
26020
|
|
|
25343
26021
|
// src/config/schema/magic-context.ts
|
|
25344
26022
|
var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
|
|
25345
|
-
var EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at
|
|
26023
|
+
var EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 90% for cache safety: output capacity is reserved from the usable context window, and the remaining 10% absorbs mid-turn growth before the absolute 95% emergency wall. Use a value between 20 and 90.";
|
|
25346
26024
|
var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
|
|
25347
26025
|
var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
|
|
25348
26026
|
var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
25349
|
-
var
|
|
25350
|
-
var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional();
|
|
26027
|
+
var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]).optional();
|
|
25351
26028
|
var PiConfigSchema = exports_external.object({
|
|
25352
26029
|
subagent_extensions: exports_external.array(exports_external.string().trim().min(1)).optional().describe("User-only allowlist of Pi extensions for Magic Context subagent children. When set, children use --no-extensions and load only these entries (plus Magic Context's scoped child extension where applicable). Relative paths resolve from ~/.pi/agent, matching Pi's settings.json package location. Unset preserves normal Pi extension discovery.")
|
|
25353
26030
|
}).optional();
|
|
@@ -25420,7 +26097,7 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
|
|
|
25420
26097
|
}).optional();
|
|
25421
26098
|
var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
25422
26099
|
two_pass: exports_external.boolean().default(false).describe("Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)"),
|
|
25423
|
-
thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh"),
|
|
26100
|
+
thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh | max"),
|
|
25424
26101
|
disallowed_tools: exports_external.array(exports_external.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"])).default([]).describe(`OpenCode only. Tools to REMOVE from the historian's default allow-list [read, aft_outline, aft_zoom, aft_search]. Applies to both historian and historian-editor agents. Use ["*"] to strip all tool definitions from the model request — this prevents weak instruction-following models (e.g. mistral-small-latest) from entering tool-calling loops. Individual tool names remove just that tool. Note: a user-supplied historian.permission override can re-allow a tool that disallowed_tools removed — disallowed_tools sets the baseline, permission overrides take precedence. (default: [])`)
|
|
25425
26102
|
}).optional();
|
|
25426
26103
|
var EmbeddingFallbackProviderSchema = exports_external.enum(["local", "openai-compatible", "off"]);
|
|
@@ -25441,7 +26118,22 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
25441
26118
|
input_type: exports_external.string().optional().describe("Default input_type for stored/indexed (passage) embeddings in the request body. Required by some openai-compatible providers (e.g. NVIDIA NIM). Omitted from the request when unset."),
|
|
25442
26119
|
query_input_type: exports_external.string().optional().describe("Optional input_type for query (search) embeddings on asymmetric models (e.g. NVIDIA NIM 'query'). When unset, query embeddings use embedding.input_type. Passage/stored content always uses embedding.input_type."),
|
|
25443
26120
|
truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset."),
|
|
25444
|
-
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
26121
|
+
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted."),
|
|
26122
|
+
local_dtype: exports_external.enum([
|
|
26123
|
+
"auto",
|
|
26124
|
+
"fp32",
|
|
26125
|
+
"fp16",
|
|
26126
|
+
"q8",
|
|
26127
|
+
"int8",
|
|
26128
|
+
"uint8",
|
|
26129
|
+
"q4",
|
|
26130
|
+
"bnb4",
|
|
26131
|
+
"q4f16",
|
|
26132
|
+
"q2",
|
|
26133
|
+
"q2f16",
|
|
26134
|
+
"q1",
|
|
26135
|
+
"q1f16"
|
|
26136
|
+
]).optional().describe("Local provider only: ONNX model dtype passed to the transformers.js feature-extraction pipeline. Accepts the @huggingface/transformers DataType strings (auto, fp32, fp16, q8, int8, uint8, q4, bnb4, q4f16, q2, q2f16, q1, q1f16). Omitted keeps today's behavior (fp32). A non-default value changes the produced vectors and folds into the embedding model identity, so switching dtype re-embeds rather than mixing vector spaces. Useful for selecting a quantized variant (e.g. q8) of a larger multilingual model to cut memory and CPU cost; see issue #259.")
|
|
25445
26137
|
}).superRefine((data, ctx) => {
|
|
25446
26138
|
const validationProvider = data.provider === "synapse" ? data.fallback_provider : data.provider;
|
|
25447
26139
|
if (validationProvider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
@@ -25483,7 +26175,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
25483
26175
|
return {
|
|
25484
26176
|
provider: "local",
|
|
25485
26177
|
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
25486
|
-
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
26178
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {},
|
|
26179
|
+
...data.local_dtype ? { local_dtype: data.local_dtype } : {}
|
|
25487
26180
|
};
|
|
25488
26181
|
}
|
|
25489
26182
|
if (data.provider === "openai-compatible") {
|
|
@@ -25506,6 +26199,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
25506
26199
|
});
|
|
25507
26200
|
var MagicContextConfigSchema = exports_external.object({
|
|
25508
26201
|
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
26202
|
+
allow_home_project: exports_external.boolean().default(false).describe("Allow Magic Context sessions launched from the exact canonical home directory. The home session uses its deterministic dir: identity so pre-gate memories reconnect. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace."),
|
|
25509
26203
|
experimental: exports_external.object({
|
|
25510
26204
|
mural: exports_external.object({
|
|
25511
26205
|
enabled: exports_external.boolean().default(false),
|
|
@@ -25517,15 +26211,19 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
25517
26211
|
language: exports_external.string().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian, dreamer, sidekick, and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
|
|
25518
26212
|
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
25519
26213
|
dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
|
|
25520
|
-
cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m") or per-model object ({ default: "5m", "model-id": "10m" })'),
|
|
26214
|
+
cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "model-id": "10m" }). Set to "never" for lanes kept warm by an external keepwarm proxy — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time.'),
|
|
26215
|
+
output_reserve: exports_external.union([
|
|
26216
|
+
exports_external.number().min(0),
|
|
26217
|
+
exports_external.object({ default: exports_external.number().min(0) }).catchall(exports_external.number().min(0))
|
|
26218
|
+
]).optional().describe('User-only output-token reservation override. Number or per-model object ({ default: 16384, "provider/model": 8192 }); 0 disables reservation. When unset, Magic Context reserves the catalog output limit (capped at 25% of context) for shared-window providers and keeps proven separate-quota Google/Gemini windows unchanged.'),
|
|
25521
26219
|
toast_duration_ms: exports_external.number().min(0).max(60000).default(5000).describe("TUI toast lifetime in milliseconds for Magic Context notifications. Set to 0 to disable Magic Context toasts entirely (min: 0, max: 60000, default: 5000)"),
|
|
25522
26220
|
execute_threshold_percentage: exports_external.union([
|
|
25523
|
-
exports_external.number().min(20).max(
|
|
25524
|
-
exports_external.object({ default: exports_external.number().min(20).max(
|
|
25525
|
-
]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE).describe('Context percentage that forces queued operations to execute. Number or per-model object ({ default: 65, "provider/model": 45 }). Values above
|
|
26221
|
+
exports_external.number().min(20).max(90, EXECUTE_THRESHOLD_CAP_MESSAGE),
|
|
26222
|
+
exports_external.object({ default: exports_external.number().min(20).max(90, EXECUTE_THRESHOLD_CAP_MESSAGE) }).catchall(exports_external.number().min(20).max(90, EXECUTE_THRESHOLD_CAP_MESSAGE))
|
|
26223
|
+
]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE).describe('Context percentage that forces queued operations to execute. Number or per-model object ({ default: 65, "provider/model": 45 }). Values above 90 are rejected because the runtime caps at 90% of the output-reserved safe window (MAX_EXECUTE_THRESHOLD). Default: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE'),
|
|
25526
26224
|
execute_threshold_tokens: exports_external.object({
|
|
25527
26225
|
default: exports_external.number().min(5000).max(2000000).optional()
|
|
25528
|
-
}).catchall(exports_external.number().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above
|
|
26226
|
+
}).catchall(exports_external.number().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above 90% × context_limit are clamped with a warning log. Min 5_000, max 2_000_000."),
|
|
25529
26227
|
protected_tags: exports_external.number().min(1).max(100).optional().describe("Number of recent tags to protect from dropping (min: 1, max: 100, default: 20)"),
|
|
25530
26228
|
clear_reasoning_age: exports_external.number().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
|
|
25531
26229
|
history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE).describe("Fraction of usable context (context_limit × execute_threshold) reserved for the session history block (default: 0.15)"),
|
|
@@ -25545,6 +26243,9 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
25545
26243
|
cache_size_mb: exports_external.number().min(2).max(2048).default(64).describe("Page-cache size in MiB per connection (PRAGMA cache_size). Larger keeps more hot pages resident, cutting re-reads on repeated full-table scans. (min 2, max 2048, default 64)"),
|
|
25546
26244
|
mmap_size_mb: exports_external.number().min(0).max(8192).default(0).describe("Memory-mapped I/O size in MiB (PRAGMA mmap_size). 0 disables mmap (SQLite default). Raising it can cut read overhead on large DBs at the cost of address space. (min 0, max 8192, default 0)")
|
|
25547
26245
|
}).default({ cache_size_mb: 64, mmap_size_mb: 0 }).describe("SQLite connection tuning for Magic Context's own context.db. These are per-connection PRAGMAs applied at open; they do not change the schema or what is stored."),
|
|
26246
|
+
storage: exports_external.object({
|
|
26247
|
+
enforce_private_permissions: exports_external.boolean().default(true).describe("When true (default), Magic Context creates and re-tightens its storage directories to owner-only 0700 and storage files to owner-only 0600. Set false only for a deliberate trusted-group deployment whose operator manages directory, database, WAL/SHM, cache, and RPC file permissions externally; Magic Context then never chmods or supplies restrictive creation modes. USER-LEVEL ONLY — ignored in project config for security. On Windows, POSIX chmod modes are already meaningless, so this setting is a no-op.")
|
|
26248
|
+
}).default({ enforce_private_permissions: true }).describe("Storage permission policy. The default keeps session content and memories owner-private. Disabling enforcement is for trusted shared-group storage managed externally; every group member able to read the storage can read all stored session content and memories."),
|
|
25548
26249
|
embedding: EmbeddingConfigSchema.default({
|
|
25549
26250
|
provider: "local",
|
|
25550
26251
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
@@ -25558,6 +26259,9 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
25558
26259
|
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add compact date ranges to compartment headings. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
|
|
25559
26260
|
keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
|
|
25560
26261
|
fail_closed_blocking: exports_external.boolean().default(true).describe("When Magic Context cannot operate (schema fence mismatch, storage open/migration failure), block the primary-session prompt with a loud recovery error instead of silently degrading to native compaction. Default true. Set false only to restore the old degrade-silently behavior (not recommended). USER-LEVEL ONLY — ignored in project config for security. Requires a restart."),
|
|
26262
|
+
compaction: exports_external.object({
|
|
26263
|
+
enabled: exports_external.boolean().default(true).describe("When false, Magic Context stops managing the context window and keeps its knowledge layer: memory and docs/user-profile/key-files injection through additive m[0]/m[1], raw-message FTS indexing, dreamer, notes, ctx_search, ctx_expand, ctx_memory, and /ctx-embed remain available. MC's historian/compartment preparation, tagging, markers, pruning, folding, drops, strips, splicing, synthetic context-management todos, temporal markers, nudges, and fail-closed blocking stop; ctx_expand remains a knowledge-surface tool. fail_closed_blocking is inert: a transform failure passes the input messages through without blocking or cancelling. This setting does not enable native compaction: OpenCode's compaction.auto / compaction.prune or Pi's equivalent owns the window, or nothing does. MC's compaction.enabled in magic-context.jsonc is distinct from OpenCode's compaction.auto / compaction.prune in opencode.jsonc; they are different files and different owners. On the first turn after disabling, a long session may trigger one native compaction cycle; MC removes only its own marker boundary, leaves native boundaries and stored compartments intact, and does no pre-trimming mitigation. Marker cleanup is lazy per session, so an unresumed session is cleaned when it is next resumed. If compaction is enabled again, run /ctx-wrapup when the historian is runnable to catch up. OpenCode peer verification against v1.18.4 confirms native compaction covers child sessions: subagents receive additive memory/docs injection and no MC reclaim in this mode, so keep subagent tasks small or leave compaction.enabled on for long subagent runs. If transform_mode is rust, compaction-off resolves to the TypeScript transform and emits one frozen boot warning because there is no Rust reduced-mode contract. This is boot-resolved and requires a process restart; project-tier compaction.enabled is stripped so a cloned repository cannot disable the user's setting. The sidebar reports raw usage as Context: <pct>% · native compaction or Context: <pct>% · no active compaction and does not show an MC execute-threshold fill. /ctx-wrapup, /ctx-recomp, /ctx-flush, and /ctx-session-upgrade refuse without context-management side effects; /ctx-embed remains functional. Raw content hidden by a native boundary before Magic Context's first pass is not retroactively indexed.")
|
|
26264
|
+
}).default({ enabled: true }).describe("Compaction-off mode gate. Default true (MC manages the context window as today). Set compaction.enabled=false to keep the knowledge layer while letting native compaction (or nothing) own the window. Boot-resolved; requires a restart to change."),
|
|
25561
26265
|
todowrite: exports_external.object({
|
|
25562
26266
|
enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
|
|
25563
26267
|
overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
|
|
@@ -25604,9 +26308,6 @@ var saveStatements = new WeakMap;
|
|
|
25604
26308
|
var loadProjectStatements = new WeakMap;
|
|
25605
26309
|
var loadUnembeddedStatements = new WeakMap;
|
|
25606
26310
|
var countEmbeddedStatements = new WeakMap;
|
|
25607
|
-
var clearProjectStatements2 = new WeakMap;
|
|
25608
|
-
var clearProjectModelStatements2 = new WeakMap;
|
|
25609
|
-
var distinctModelIdStatements = new WeakMap;
|
|
25610
26311
|
function getSaveStatement(db) {
|
|
25611
26312
|
let stmt = saveStatements.get(db);
|
|
25612
26313
|
if (!stmt) {
|
|
@@ -25677,7 +26378,6 @@ function countEmbeddedCommits(db, projectPath, modelId) {
|
|
|
25677
26378
|
var insertStatements = new WeakMap;
|
|
25678
26379
|
var existingShasStatements = new WeakMap;
|
|
25679
26380
|
var projectCountStatements = new WeakMap;
|
|
25680
|
-
var evictStatements = new WeakMap;
|
|
25681
26381
|
var evictOverflowStatements = new WeakMap;
|
|
25682
26382
|
var latestCommitTimeStatements = new WeakMap;
|
|
25683
26383
|
function getInsertStatement(db) {
|
|
@@ -25919,7 +26619,7 @@ function releaseGitSweepLease(db, projectPath, holderId) {
|
|
|
25919
26619
|
}
|
|
25920
26620
|
|
|
25921
26621
|
// src/features/magic-context/memory/embedding-synapse.ts
|
|
25922
|
-
import { createHash as
|
|
26622
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
25923
26623
|
|
|
25924
26624
|
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
25925
26625
|
import { promises as fs2 } from "node:fs";
|
|
@@ -28132,7 +28832,7 @@ function wait(ms) {
|
|
|
28132
28832
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
28133
28833
|
}
|
|
28134
28834
|
function sha256(value) {
|
|
28135
|
-
return
|
|
28835
|
+
return createHash11("sha256").update(value).digest("hex");
|
|
28136
28836
|
}
|
|
28137
28837
|
function stableJson(value) {
|
|
28138
28838
|
if (Array.isArray(value))
|
|
@@ -28180,7 +28880,7 @@ function extractCatalogEntries(value) {
|
|
|
28180
28880
|
const batchRecord = asRecord(rawBatch);
|
|
28181
28881
|
const recommendedBatch = typeof rawBatch === "number" ? rawBatch : batchRecord ? batchRecord.rows : undefined;
|
|
28182
28882
|
const recommendedTokenBudget = batchRecord ? batchRecord.token_budget : undefined;
|
|
28183
|
-
const
|
|
28883
|
+
const state2 = typeof record2.state === "string" ? record2.state : undefined;
|
|
28184
28884
|
return [
|
|
28185
28885
|
{
|
|
28186
28886
|
model,
|
|
@@ -28191,7 +28891,7 @@ function extractCatalogEntries(value) {
|
|
|
28191
28891
|
...typeof recommendedTokenBudget === "number" && recommendedTokenBudget > 0 ? { recommended_token_budget: Math.floor(recommendedTokenBudget) } : {},
|
|
28192
28892
|
...record2.provenance !== undefined ? { provenance: record2.provenance } : {},
|
|
28193
28893
|
...typeof record2.certified === "boolean" ? { certified: record2.certified } : {},
|
|
28194
|
-
...typeof record2.status === "string" ? { status: record2.status } :
|
|
28894
|
+
...typeof record2.status === "string" ? { status: record2.status } : state2 ? { status: state2 } : {}
|
|
28195
28895
|
}
|
|
28196
28896
|
];
|
|
28197
28897
|
});
|
|
@@ -28577,6 +29277,7 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
28577
29277
|
throw new Error("Unknown embedding provider");
|
|
28578
29278
|
}
|
|
28579
29279
|
const truncate = config2.provider === "openai-compatible" ? config2.truncate?.trim() : undefined;
|
|
29280
|
+
const localDtype = config2.provider === "local" && config2.local_dtype && config2.local_dtype !== "fp32" ? config2.local_dtype : undefined;
|
|
28580
29281
|
const identityInput = config2.provider === "openai-compatible" ? {
|
|
28581
29282
|
provider: "openai-compatible",
|
|
28582
29283
|
model: config2.model.trim(),
|
|
@@ -28588,7 +29289,8 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
28588
29289
|
provider: "local",
|
|
28589
29290
|
model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
28590
29291
|
endpoint: "",
|
|
28591
|
-
apiKeyPresent: false
|
|
29292
|
+
apiKeyPresent: false,
|
|
29293
|
+
...localDtype ? { localDtype } : {}
|
|
28592
29294
|
};
|
|
28593
29295
|
return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
|
|
28594
29296
|
}
|
|
@@ -28596,7 +29298,7 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
28596
29298
|
// src/features/magic-context/memory/embedding-local.ts
|
|
28597
29299
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
28598
29300
|
import { open, stat, unlink, writeFile } from "node:fs/promises";
|
|
28599
|
-
import { dirname as dirname2, join as
|
|
29301
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
28600
29302
|
import { pathToFileURL } from "node:url";
|
|
28601
29303
|
var LOCK_POLL_MS = 150;
|
|
28602
29304
|
var STALE_LOCK_MS = 3 * 60000;
|
|
@@ -28676,6 +29378,7 @@ async function injectWasmOrtForElectron() {
|
|
|
28676
29378
|
return false;
|
|
28677
29379
|
}
|
|
28678
29380
|
}
|
|
29381
|
+
var DEFAULT_LOCAL_DTYPE = "fp32";
|
|
28679
29382
|
async function withQuietConsole(fn) {
|
|
28680
29383
|
const origWarn = console.warn;
|
|
28681
29384
|
const origError = console.error;
|
|
@@ -28752,16 +29455,22 @@ class LocalEmbeddingProvider {
|
|
|
28752
29455
|
modelId;
|
|
28753
29456
|
maxInputTokens;
|
|
28754
29457
|
model;
|
|
29458
|
+
dtype;
|
|
28755
29459
|
pipeline = null;
|
|
28756
29460
|
initPromise = null;
|
|
28757
29461
|
inFlight = 0;
|
|
28758
29462
|
disposing = false;
|
|
28759
29463
|
disposePromise = null;
|
|
28760
29464
|
inFlightWaiters = [];
|
|
28761
|
-
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
|
|
29465
|
+
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512, dtype = DEFAULT_LOCAL_DTYPE) {
|
|
28762
29466
|
this.model = model;
|
|
28763
29467
|
this.maxInputTokens = maxInputTokens;
|
|
28764
|
-
this.
|
|
29468
|
+
this.dtype = dtype || DEFAULT_LOCAL_DTYPE;
|
|
29469
|
+
this.modelId = getEmbeddingProviderIdentity({
|
|
29470
|
+
provider: "local",
|
|
29471
|
+
model,
|
|
29472
|
+
...dtype && dtype !== DEFAULT_LOCAL_DTYPE ? { local_dtype: dtype } : {}
|
|
29473
|
+
});
|
|
28765
29474
|
}
|
|
28766
29475
|
async initialize() {
|
|
28767
29476
|
if (this.disposing) {
|
|
@@ -28790,20 +29499,24 @@ class LocalEmbeddingProvider {
|
|
|
28790
29499
|
if (LogLevel && "ERROR" in LogLevel) {
|
|
28791
29500
|
env.logLevel = LogLevel.ERROR;
|
|
28792
29501
|
}
|
|
28793
|
-
const modelCacheDir =
|
|
29502
|
+
const modelCacheDir = join4(getMagicContextStorageDir(), "models");
|
|
28794
29503
|
try {
|
|
28795
|
-
|
|
28796
|
-
|
|
28797
|
-
|
|
28798
|
-
|
|
28799
|
-
|
|
29504
|
+
if (shouldEnforcePrivateStoragePermissions()) {
|
|
29505
|
+
mkdirSync2(modelCacheDir, { recursive: true, mode: 448 });
|
|
29506
|
+
if (process.platform !== "win32") {
|
|
29507
|
+
try {
|
|
29508
|
+
chmodSync2(modelCacheDir, 448);
|
|
29509
|
+
} catch {}
|
|
29510
|
+
}
|
|
29511
|
+
} else {
|
|
29512
|
+
mkdirSync2(modelCacheDir, { recursive: true });
|
|
28800
29513
|
}
|
|
28801
29514
|
env.cacheDir = modelCacheDir;
|
|
28802
29515
|
} catch {
|
|
28803
29516
|
log("[magic-context] could not create model cache dir, using library default");
|
|
28804
29517
|
}
|
|
28805
29518
|
const createPipeline = transformersModule.pipeline;
|
|
28806
|
-
const lockPath =
|
|
29519
|
+
const lockPath = join4(modelCacheDir, ".load.lock");
|
|
28807
29520
|
const releaseLock = await acquireModelLoadLock(lockPath);
|
|
28808
29521
|
const stopHeartbeat = startLockHeartbeat(lockPath);
|
|
28809
29522
|
try {
|
|
@@ -28812,7 +29525,7 @@ class LocalEmbeddingProvider {
|
|
|
28812
29525
|
for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
|
|
28813
29526
|
try {
|
|
28814
29527
|
const pipeline = await withQuietConsole(() => createPipeline("feature-extraction", this.model, {
|
|
28815
|
-
dtype:
|
|
29528
|
+
dtype: this.dtype,
|
|
28816
29529
|
...injectedWasmOrt ? { device: "auto" } : {}
|
|
28817
29530
|
}));
|
|
28818
29531
|
if (this.disposing) {
|
|
@@ -29376,6 +30089,9 @@ var shadowWorker = null;
|
|
|
29376
30089
|
var SHADOW_MAX_ITEMS_PER_TICK = 64;
|
|
29377
30090
|
var SHADOW_MAX_BYTES_PER_TICK = 512 * 1024;
|
|
29378
30091
|
var SHADOW_MAX_WALL_CLOCK_MS = 2000;
|
|
30092
|
+
var pendingShadowBackfills = new Map;
|
|
30093
|
+
var shadowBackfillLastIds = new Map;
|
|
30094
|
+
var shadowBackfillStopReasons = new Map;
|
|
29379
30095
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
29380
30096
|
var upsertActiveIdentityStatements = new WeakMap;
|
|
29381
30097
|
var backfillActiveIdentityStatements = new Map;
|
|
@@ -29460,7 +30176,8 @@ function resolveEmbeddingConfig(config2) {
|
|
|
29460
30176
|
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
29461
30177
|
...config2?.max_input_tokens ? {
|
|
29462
30178
|
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
29463
|
-
} : {}
|
|
30179
|
+
} : {},
|
|
30180
|
+
...config2?.local_dtype ? { local_dtype: config2.local_dtype } : {}
|
|
29464
30181
|
};
|
|
29465
30182
|
}
|
|
29466
30183
|
if (config2.provider === "openai-compatible") {
|
|
@@ -29519,7 +30236,7 @@ function createProvider(config2, context) {
|
|
|
29519
30236
|
});
|
|
29520
30237
|
}
|
|
29521
30238
|
if (config2.provider === "local") {
|
|
29522
|
-
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
30239
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens, config2.local_dtype);
|
|
29523
30240
|
}
|
|
29524
30241
|
if (config2.provider === "synapse") {
|
|
29525
30242
|
const synapse = config2;
|
|
@@ -29548,10 +30265,10 @@ function stableStringify2(value) {
|
|
|
29548
30265
|
return JSON.stringify(value);
|
|
29549
30266
|
}
|
|
29550
30267
|
function sha256Prefix(value, length = 16) {
|
|
29551
|
-
return
|
|
30268
|
+
return createHash12("sha256").update(value).digest("hex").slice(0, length);
|
|
29552
30269
|
}
|
|
29553
30270
|
function contentSha256(value) {
|
|
29554
|
-
return
|
|
30271
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
29555
30272
|
}
|
|
29556
30273
|
function getRuntimeFingerprint(config2) {
|
|
29557
30274
|
if (config2.provider === "off") {
|
|
@@ -29910,6 +30627,7 @@ function registerProjectShadowEmbedding(db, projectIdentity, config2, sourceDire
|
|
|
29910
30627
|
recordScopeActiveIdentity(db, projectIdentity, "chunk", registration.chunkModelId, now);
|
|
29911
30628
|
persistShadowDescriptor(db, registration);
|
|
29912
30629
|
})();
|
|
30630
|
+
maybeArmShadowBackfill(db, projectIdentity, registration);
|
|
29913
30631
|
return {
|
|
29914
30632
|
projectIdentity,
|
|
29915
30633
|
sourceDirectory,
|
|
@@ -29930,7 +30648,7 @@ function startShadowWorker() {
|
|
|
29930
30648
|
return;
|
|
29931
30649
|
shadowWorker = runShadowWorker().finally(() => {
|
|
29932
30650
|
shadowWorker = null;
|
|
29933
|
-
if (shadowQueue.length > 0)
|
|
30651
|
+
if (shadowQueue.length > 0 || hasPendingShadowBackfill())
|
|
29934
30652
|
startShadowWorker();
|
|
29935
30653
|
});
|
|
29936
30654
|
}
|
|
@@ -29977,6 +30695,117 @@ function enqueueShadowEmbeddingItems(projectIdentity, scope, ids) {
|
|
|
29977
30695
|
shadowQueue.push({ projectIdentity, scope, ids: [...ids] });
|
|
29978
30696
|
startShadowWorker();
|
|
29979
30697
|
}
|
|
30698
|
+
function shadowBackfillMissingBase(scope, primaryModelId, shadowModelId, projectIdentity) {
|
|
30699
|
+
if (scope === "memory") {
|
|
30700
|
+
return {
|
|
30701
|
+
sql: `SELECT m.id AS id
|
|
30702
|
+
FROM memories m
|
|
30703
|
+
JOIN memory_embeddings mp ON mp.memory_id = m.id AND mp.model_id = ?
|
|
30704
|
+
LEFT JOIN memory_embeddings ms ON ms.memory_id = m.id AND ms.model_id = ?
|
|
30705
|
+
WHERE m.project_path = ? AND m.status = 'active' AND ms.memory_id IS NULL`,
|
|
30706
|
+
params: [primaryModelId, shadowModelId, projectIdentity],
|
|
30707
|
+
orderBy: " ORDER BY m.id"
|
|
30708
|
+
};
|
|
30709
|
+
}
|
|
30710
|
+
if (scope === "commit") {
|
|
30711
|
+
return {
|
|
30712
|
+
sql: `SELECT gc.sha AS id
|
|
30713
|
+
FROM git_commits gc
|
|
30714
|
+
JOIN git_commit_embeddings gp ON gp.sha = gc.sha AND gp.model_id = ?
|
|
30715
|
+
LEFT JOIN git_commit_embeddings gs ON gs.sha = gc.sha AND gs.model_id = ?
|
|
30716
|
+
WHERE gc.project_path = ? AND gs.sha IS NULL`,
|
|
30717
|
+
params: [primaryModelId, shadowModelId, projectIdentity],
|
|
30718
|
+
orderBy: " ORDER BY gc.committed_at DESC, gc.sha"
|
|
30719
|
+
};
|
|
30720
|
+
}
|
|
30721
|
+
return {
|
|
30722
|
+
sql: `SELECT DISTINCT cp.compartment_id AS id
|
|
30723
|
+
FROM compartment_chunk_embeddings cp
|
|
30724
|
+
WHERE cp.project_path = ? AND cp.model_id = ?
|
|
30725
|
+
AND NOT EXISTS (
|
|
30726
|
+
SELECT 1 FROM compartment_chunk_embeddings cs
|
|
30727
|
+
WHERE cs.compartment_id = cp.compartment_id AND cs.model_id = ?
|
|
30728
|
+
)`,
|
|
30729
|
+
params: [projectIdentity, primaryModelId, shadowModelId],
|
|
30730
|
+
orderBy: " ORDER BY cp.compartment_id"
|
|
30731
|
+
};
|
|
30732
|
+
}
|
|
30733
|
+
function shadowBackfillMissingIds(db, projectIdentity, scope, primaryModelId, shadowModelId, limit) {
|
|
30734
|
+
const { sql, params, orderBy } = shadowBackfillMissingBase(scope, primaryModelId, shadowModelId, projectIdentity);
|
|
30735
|
+
const rows = db.prepare(`${sql}${orderBy} LIMIT ?`).all(...params, limit);
|
|
30736
|
+
return rows.map((row) => String(row.id));
|
|
30737
|
+
}
|
|
30738
|
+
function shadowModelIdForScope(registration, scope) {
|
|
30739
|
+
return scope === "chunk" ? registration.chunkModelId : registration.modelId;
|
|
30740
|
+
}
|
|
30741
|
+
function hasPendingShadowBackfill(projectIdentity) {
|
|
30742
|
+
if (projectIdentity === undefined)
|
|
30743
|
+
return pendingShadowBackfills.size > 0;
|
|
30744
|
+
const scopes = pendingShadowBackfills.get(projectIdentity);
|
|
30745
|
+
return scopes !== undefined && scopes.size > 0;
|
|
30746
|
+
}
|
|
30747
|
+
function pumpShadowBackfill() {
|
|
30748
|
+
for (const [projectIdentity, scopes] of pendingShadowBackfills) {
|
|
30749
|
+
const db = dbForShadowQueue.get(projectIdentity);
|
|
30750
|
+
const shadow = shadowRegistrations.get(projectIdentity);
|
|
30751
|
+
const primary = projectRegistrations.get(projectIdentity);
|
|
30752
|
+
if (!db || !shadow || !primary) {
|
|
30753
|
+
pendingShadowBackfills.delete(projectIdentity);
|
|
30754
|
+
continue;
|
|
30755
|
+
}
|
|
30756
|
+
for (const scope of [...scopes]) {
|
|
30757
|
+
const primaryModelId = shadowModelIdForScope(primary, scope);
|
|
30758
|
+
const shadowModelId = shadowModelIdForScope(shadow, scope);
|
|
30759
|
+
const stallKey = `${projectIdentity}:${scope}`;
|
|
30760
|
+
if (primaryModelId === "off" || shadowModelId === "off") {
|
|
30761
|
+
scopes.delete(scope);
|
|
30762
|
+
shadowBackfillLastIds.delete(stallKey);
|
|
30763
|
+
continue;
|
|
30764
|
+
}
|
|
30765
|
+
const ids = shadowBackfillMissingIds(db, projectIdentity, scope, primaryModelId, shadowModelId, SHADOW_MAX_ITEMS_PER_TICK);
|
|
30766
|
+
if (ids.length === 0) {
|
|
30767
|
+
shadowBackfillStopReasons.set(stallKey, "drained");
|
|
30768
|
+
scopes.delete(scope);
|
|
30769
|
+
shadowBackfillLastIds.delete(stallKey);
|
|
30770
|
+
continue;
|
|
30771
|
+
}
|
|
30772
|
+
const signature = ids.join(",");
|
|
30773
|
+
if (shadowBackfillLastIds.get(stallKey) === signature) {
|
|
30774
|
+
shadowBackfillStopReasons.set(stallKey, "stalled_no_progress");
|
|
30775
|
+
log(`[shadow] backfill scope ${scope} for ${projectIdentity} retired without progress — ` + `the last batch produced no writes (provider failure or timeout is the usual cause); ` + `${ids.length}+ items remain and retry on the next registration or manual --shadow run`);
|
|
30776
|
+
scopes.delete(scope);
|
|
30777
|
+
shadowBackfillLastIds.delete(stallKey);
|
|
30778
|
+
continue;
|
|
30779
|
+
}
|
|
30780
|
+
shadowBackfillLastIds.set(stallKey, signature);
|
|
30781
|
+
shadowQueue.push({ projectIdentity, scope, ids });
|
|
30782
|
+
}
|
|
30783
|
+
if (scopes.size === 0)
|
|
30784
|
+
pendingShadowBackfills.delete(projectIdentity);
|
|
30785
|
+
}
|
|
30786
|
+
}
|
|
30787
|
+
function maybeArmShadowBackfill(db, projectIdentity, shadow) {
|
|
30788
|
+
if (untrustedLoadProjects.has(projectIdentity))
|
|
30789
|
+
return;
|
|
30790
|
+
const primary = projectRegistrations.get(projectIdentity);
|
|
30791
|
+
if (!primary)
|
|
30792
|
+
return;
|
|
30793
|
+
const pending = new Set;
|
|
30794
|
+
for (const scope of ["memory", "commit", "chunk"]) {
|
|
30795
|
+
const primaryModelId = shadowModelIdForScope(primary, scope);
|
|
30796
|
+
const shadowModelId = shadowModelIdForScope(shadow, scope);
|
|
30797
|
+
if (primaryModelId === "off" || shadowModelId === "off")
|
|
30798
|
+
continue;
|
|
30799
|
+
const probe = shadowBackfillMissingIds(db, projectIdentity, scope, primaryModelId, shadowModelId, 1);
|
|
30800
|
+
if (probe.length > 0)
|
|
30801
|
+
pending.add(scope);
|
|
30802
|
+
}
|
|
30803
|
+
if (pending.size === 0)
|
|
30804
|
+
return;
|
|
30805
|
+
pendingShadowBackfills.set(projectIdentity, pending);
|
|
30806
|
+
pumpShadowBackfill();
|
|
30807
|
+
startShadowWorker();
|
|
30808
|
+
}
|
|
29980
30809
|
async function embedShadowItems(registration, items, db, scope) {
|
|
29981
30810
|
const raw = registration.config;
|
|
29982
30811
|
const fingerprint = typeof raw.synapse_fingerprint === "string" ? raw.synapse_fingerprint : "";
|
|
@@ -30100,7 +30929,15 @@ async function runShadowWorker() {
|
|
|
30100
30929
|
const startedAt = Date.now();
|
|
30101
30930
|
let processed = 0;
|
|
30102
30931
|
let processedBytes = 0;
|
|
30103
|
-
|
|
30932
|
+
for (;; ) {
|
|
30933
|
+
if (shadowQueue.length === 0) {
|
|
30934
|
+
pumpShadowBackfill();
|
|
30935
|
+
if (shadowQueue.length === 0)
|
|
30936
|
+
break;
|
|
30937
|
+
}
|
|
30938
|
+
if (processed >= SHADOW_MAX_ITEMS_PER_TICK || Date.now() - startedAt >= SHADOW_MAX_WALL_CLOCK_MS) {
|
|
30939
|
+
break;
|
|
30940
|
+
}
|
|
30104
30941
|
const item = shadowQueue.shift();
|
|
30105
30942
|
if (!item)
|
|
30106
30943
|
break;
|
|
@@ -30706,12 +31543,12 @@ function parseFields(json2) {
|
|
|
30706
31543
|
}
|
|
30707
31544
|
|
|
30708
31545
|
// src/hooks/magic-context/historian-state-file.ts
|
|
30709
|
-
import { mkdirSync as mkdirSync3, unlinkSync, writeFileSync } from "node:fs";
|
|
31546
|
+
import { mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
30710
31547
|
function cleanupHistorianStateFile(path3) {
|
|
30711
31548
|
if (!path3)
|
|
30712
31549
|
return;
|
|
30713
31550
|
try {
|
|
30714
|
-
|
|
31551
|
+
unlinkSync2(path3);
|
|
30715
31552
|
} catch {}
|
|
30716
31553
|
}
|
|
30717
31554
|
// src/features/magic-context/memory/cosine-similarity.ts
|
|
@@ -30754,7 +31591,7 @@ function createProvider2(config2) {
|
|
|
30754
31591
|
});
|
|
30755
31592
|
}
|
|
30756
31593
|
if (config2.provider === "local") {
|
|
30757
|
-
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
31594
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens, config2.local_dtype);
|
|
30758
31595
|
}
|
|
30759
31596
|
if (config2.provider === "synapse") {
|
|
30760
31597
|
const synapse = config2;
|
|
@@ -31026,8 +31863,8 @@ function getMemoryVerifications(db, memoryIds) {
|
|
|
31026
31863
|
existing.mappedAt = Math.max(existing.mappedAt, row.mapped_at ?? 0);
|
|
31027
31864
|
result.set(row.memory_id, existing);
|
|
31028
31865
|
}
|
|
31029
|
-
for (const
|
|
31030
|
-
|
|
31866
|
+
for (const state2 of result.values()) {
|
|
31867
|
+
state2.files.sort();
|
|
31031
31868
|
}
|
|
31032
31869
|
return result;
|
|
31033
31870
|
}
|
|
@@ -31264,18 +32101,18 @@ function tallyFactsByCategory(facts) {
|
|
|
31264
32101
|
}
|
|
31265
32102
|
|
|
31266
32103
|
// src/hooks/magic-context/compaction-marker-manager.ts
|
|
31267
|
-
import { join as
|
|
32104
|
+
import { join as join6 } from "node:path";
|
|
31268
32105
|
|
|
31269
32106
|
// src/features/magic-context/compaction-marker.ts
|
|
31270
|
-
import { createHash as
|
|
32107
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
31271
32108
|
import { existsSync as existsSync5 } from "node:fs";
|
|
31272
|
-
import { join as
|
|
32109
|
+
import { join as join5 } from "node:path";
|
|
31273
32110
|
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
31274
32111
|
var ID_PREFIX_HEX_LENGTH = 12;
|
|
31275
32112
|
var ID_SUFFIX_LENGTH = 14;
|
|
31276
32113
|
var ID_PREFIX_MASK = (1n << BigInt(ID_PREFIX_HEX_LENGTH * 4)) - 1n;
|
|
31277
32114
|
function deterministicBase62(seed, length) {
|
|
31278
|
-
let value = BigInt(`0x${
|
|
32115
|
+
let value = BigInt(`0x${createHash13("sha256").update(seed).digest("hex")}`);
|
|
31279
32116
|
const chars = Array(length);
|
|
31280
32117
|
for (let index = length - 1;index >= 0; index -= 1) {
|
|
31281
32118
|
chars[index] = BASE62_CHARS[Number(value % 62n)];
|
|
@@ -31295,7 +32132,7 @@ function generatePartId(timestampMs, counter = 0n, identity = "") {
|
|
|
31295
32132
|
return generateId("prt", timestampMs, counter, identity);
|
|
31296
32133
|
}
|
|
31297
32134
|
function getOpenCodeDbPath() {
|
|
31298
|
-
return
|
|
32135
|
+
return join5(getDataDir(), "opencode", "opencode.db");
|
|
31299
32136
|
}
|
|
31300
32137
|
var cachedWriteDb = null;
|
|
31301
32138
|
var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
|
|
@@ -31508,13 +32345,223 @@ function injectCompactionMarker(args) {
|
|
|
31508
32345
|
return null;
|
|
31509
32346
|
}
|
|
31510
32347
|
}
|
|
31511
|
-
function
|
|
32348
|
+
function listSessionCompactionMarkers(sessionId) {
|
|
32349
|
+
const db = getWritableOpenCodeDb();
|
|
32350
|
+
const partRows = db.prepare(`SELECT id, message_id
|
|
32351
|
+
FROM part
|
|
32352
|
+
WHERE session_id = ?
|
|
32353
|
+
AND COALESCE(json_extract(data, '$.type'), '') = 'compaction'`).all(sessionId);
|
|
32354
|
+
const markers = [];
|
|
32355
|
+
const summaryStmt = db.prepare(`SELECT id
|
|
32356
|
+
FROM message
|
|
32357
|
+
WHERE session_id = ?
|
|
32358
|
+
AND COALESCE(json_extract(data, '$.parentID'), '') = ?
|
|
32359
|
+
AND COALESCE(json_extract(data, '$.summary'), 0) = 1
|
|
32360
|
+
AND COALESCE(json_extract(data, '$.finish'), '') = 'stop'
|
|
32361
|
+
AND COALESCE(json_extract(data, '$.providerID'), '') = 'magic-context'`);
|
|
32362
|
+
for (const row of partRows) {
|
|
32363
|
+
if (typeof row.id !== "string" || typeof row.message_id !== "string")
|
|
32364
|
+
continue;
|
|
32365
|
+
const summaryRows = summaryStmt.all(sessionId, row.message_id);
|
|
32366
|
+
const summaryMessageIds = summaryRows.flatMap((summaryRow) => typeof summaryRow.id === "string" ? [summaryRow.id] : []);
|
|
32367
|
+
markers.push({
|
|
32368
|
+
compactionPartId: row.id,
|
|
32369
|
+
boundaryMessageId: row.message_id,
|
|
32370
|
+
summaryMessageIds
|
|
32371
|
+
});
|
|
32372
|
+
}
|
|
32373
|
+
return markers;
|
|
32374
|
+
}
|
|
32375
|
+
function removeForeignCompactionMarker(sessionId, marker, protectedSummaryMessageId) {
|
|
31512
32376
|
try {
|
|
31513
32377
|
const db = getWritableOpenCodeDb();
|
|
31514
32378
|
db.transaction(() => {
|
|
31515
|
-
db.prepare("DELETE FROM part WHERE
|
|
31516
|
-
db.prepare("DELETE FROM message WHERE id = ?")
|
|
31517
|
-
|
|
32379
|
+
const deletePartsOfMessage = db.prepare("DELETE FROM part WHERE session_id = ? AND message_id = ?");
|
|
32380
|
+
const deleteMessage = db.prepare("DELETE FROM message WHERE session_id = ? AND id = ?");
|
|
32381
|
+
for (const summaryMessageId of marker.summaryMessageIds) {
|
|
32382
|
+
if (summaryMessageId === protectedSummaryMessageId)
|
|
32383
|
+
continue;
|
|
32384
|
+
deletePartsOfMessage.run(sessionId, summaryMessageId);
|
|
32385
|
+
deleteMessage.run(sessionId, summaryMessageId);
|
|
32386
|
+
}
|
|
32387
|
+
db.prepare("DELETE FROM part WHERE session_id = ? AND id = ?").run(sessionId, marker.compactionPartId);
|
|
32388
|
+
})();
|
|
32389
|
+
return true;
|
|
32390
|
+
} catch (error51) {
|
|
32391
|
+
log(`[magic-context] compaction-marker: foreign-marker removal failed (${sessionId}, part ${marker.compactionPartId}): ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
32392
|
+
return false;
|
|
32393
|
+
}
|
|
32394
|
+
}
|
|
32395
|
+
function dataReferencesTailStart(data) {
|
|
32396
|
+
if (typeof data !== "object" || data === null)
|
|
32397
|
+
return null;
|
|
32398
|
+
const record2 = data;
|
|
32399
|
+
if (typeof record2.tail_start_id === "string" && record2.tail_start_id.length > 0) {
|
|
32400
|
+
return record2.tail_start_id;
|
|
32401
|
+
}
|
|
32402
|
+
const nested = record2.compaction;
|
|
32403
|
+
if (typeof nested === "object" && nested !== null) {
|
|
32404
|
+
const nestedTail = nested.tail_start_id;
|
|
32405
|
+
if (typeof nestedTail === "string" && nestedTail.length > 0)
|
|
32406
|
+
return nestedTail;
|
|
32407
|
+
}
|
|
32408
|
+
return null;
|
|
32409
|
+
}
|
|
32410
|
+
function isMcCanonicalCompactionPartData(data) {
|
|
32411
|
+
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
32412
|
+
return false;
|
|
32413
|
+
const record2 = data;
|
|
32414
|
+
const keys = Object.keys(record2).sort();
|
|
32415
|
+
return keys.length === 2 && keys[0] === "auto" && keys[1] === "type" && record2.type === "compaction" && record2.auto === true;
|
|
32416
|
+
}
|
|
32417
|
+
function removeMcOwnedCompactionMarkers(sessionId, summaryText) {
|
|
32418
|
+
const db = getWritableOpenCodeDb();
|
|
32419
|
+
if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath())) {
|
|
32420
|
+
return {
|
|
32421
|
+
verified: false,
|
|
32422
|
+
removedLineages: 0,
|
|
32423
|
+
removedRows: 0,
|
|
32424
|
+
retainedLineages: 0
|
|
32425
|
+
};
|
|
32426
|
+
}
|
|
32427
|
+
const canonicalSummaries = db.prepare(`SELECT id, COALESCE(json_extract(data, '$.parentID'), '') AS parent_id
|
|
32428
|
+
FROM message
|
|
32429
|
+
WHERE session_id = ?
|
|
32430
|
+
AND COALESCE(json_extract(data, '$.summary'), 0) = 1
|
|
32431
|
+
AND COALESCE(json_extract(data, '$.finish'), '') = 'stop'
|
|
32432
|
+
AND COALESCE(json_extract(data, '$.providerID'), '') = 'magic-context'`).all(sessionId);
|
|
32433
|
+
const legacySummaries = db.prepare(`SELECT m.id, COALESCE(json_extract(m.data, '$.parentID'), '') AS parent_id
|
|
32434
|
+
FROM message m
|
|
32435
|
+
WHERE m.session_id = ?
|
|
32436
|
+
AND COALESCE(json_extract(m.data, '$.summary'), 0) = 1
|
|
32437
|
+
AND COALESCE(json_extract(m.data, '$.finish'), '') = 'stop'
|
|
32438
|
+
AND COALESCE(json_extract(m.data, '$.providerID'), '') <> 'magic-context'
|
|
32439
|
+
AND EXISTS (
|
|
32440
|
+
SELECT 1
|
|
32441
|
+
FROM part p
|
|
32442
|
+
WHERE p.session_id = m.session_id
|
|
32443
|
+
AND p.message_id = m.id
|
|
32444
|
+
AND COALESCE(json_extract(p.data, '$.type'), '') = 'text'
|
|
32445
|
+
AND COALESCE(json_extract(p.data, '$.text'), '') = ?
|
|
32446
|
+
)`).all(sessionId, summaryText);
|
|
32447
|
+
const summariesByBoundary = new Map;
|
|
32448
|
+
const orphanSummaryIds = new Set;
|
|
32449
|
+
for (const row of [...canonicalSummaries, ...legacySummaries]) {
|
|
32450
|
+
if (typeof row.id !== "string")
|
|
32451
|
+
continue;
|
|
32452
|
+
if (typeof row.parent_id === "string" && row.parent_id.length > 0) {
|
|
32453
|
+
const set2 = summariesByBoundary.get(row.parent_id) ?? new Set;
|
|
32454
|
+
set2.add(row.id);
|
|
32455
|
+
summariesByBoundary.set(row.parent_id, set2);
|
|
32456
|
+
} else {
|
|
32457
|
+
orphanSummaryIds.add(row.id);
|
|
32458
|
+
}
|
|
32459
|
+
}
|
|
32460
|
+
const compactionParts = db.prepare(`SELECT id, message_id, data
|
|
32461
|
+
FROM part
|
|
32462
|
+
WHERE session_id = ?
|
|
32463
|
+
AND COALESCE(json_extract(data, '$.type'), '') = 'compaction'`).all(sessionId);
|
|
32464
|
+
const parsedParts = [];
|
|
32465
|
+
for (const part of compactionParts) {
|
|
32466
|
+
if (typeof part.id !== "string" || typeof part.message_id !== "string")
|
|
32467
|
+
continue;
|
|
32468
|
+
let data;
|
|
32469
|
+
try {
|
|
32470
|
+
data = typeof part.data === "string" ? JSON.parse(part.data) : part.data;
|
|
32471
|
+
} catch {
|
|
32472
|
+
data = null;
|
|
32473
|
+
}
|
|
32474
|
+
parsedParts.push({
|
|
32475
|
+
id: part.id,
|
|
32476
|
+
messageId: part.message_id,
|
|
32477
|
+
data,
|
|
32478
|
+
tailStartId: dataReferencesTailStart(data)
|
|
32479
|
+
});
|
|
32480
|
+
}
|
|
32481
|
+
const messageTailRefs = db.prepare(`SELECT id, data
|
|
32482
|
+
FROM message
|
|
32483
|
+
WHERE session_id = ?
|
|
32484
|
+
AND json_extract(data, '$.compaction.tail_start_id') IS NOT NULL`).all(sessionId);
|
|
32485
|
+
const messageTailStartIds = new Set;
|
|
32486
|
+
for (const row of messageTailRefs) {
|
|
32487
|
+
if (typeof row.id !== "string")
|
|
32488
|
+
continue;
|
|
32489
|
+
let data;
|
|
32490
|
+
try {
|
|
32491
|
+
data = typeof row.data === "string" ? JSON.parse(row.data) : row.data;
|
|
32492
|
+
} catch {
|
|
32493
|
+
data = null;
|
|
32494
|
+
}
|
|
32495
|
+
const ref = dataReferencesTailStart(data);
|
|
32496
|
+
if (ref)
|
|
32497
|
+
messageTailStartIds.add(ref);
|
|
32498
|
+
}
|
|
32499
|
+
let removedLineages = 0;
|
|
32500
|
+
let removedRows = 0;
|
|
32501
|
+
let retainedLineages = 0;
|
|
32502
|
+
const deletePartsOfMessage = db.prepare("DELETE FROM part WHERE session_id = ? AND message_id = ?");
|
|
32503
|
+
const deleteMessage = db.prepare("DELETE FROM message WHERE session_id = ? AND id = ?");
|
|
32504
|
+
const deletePart = db.prepare("DELETE FROM part WHERE session_id = ? AND id = ?");
|
|
32505
|
+
const deleteSummaries = (summaryIds) => {
|
|
32506
|
+
let rows = 0;
|
|
32507
|
+
for (const summaryId of summaryIds) {
|
|
32508
|
+
rows += deletePartsOfMessage.run(sessionId, summaryId).changes;
|
|
32509
|
+
rows += deleteMessage.run(sessionId, summaryId).changes;
|
|
32510
|
+
}
|
|
32511
|
+
return rows;
|
|
32512
|
+
};
|
|
32513
|
+
for (const [boundaryMessageId, summaryIds] of summariesByBoundary) {
|
|
32514
|
+
const boundaryParts = parsedParts.filter((part) => part.messageId === boundaryMessageId);
|
|
32515
|
+
const mcPartIds = boundaryParts.filter((part) => isMcCanonicalCompactionPartData(part.data)).map((part) => part.id);
|
|
32516
|
+
const rowsToDelete = new Set([...summaryIds, ...mcPartIds]);
|
|
32517
|
+
const survivingPartsReferenceDeletion = parsedParts.some((part) => !mcPartIds.includes(part.id) && part.tailStartId !== null && rowsToDelete.has(part.tailStartId));
|
|
32518
|
+
const messageFieldReferencesDeletion = [...rowsToDelete].some((id) => messageTailStartIds.has(id));
|
|
32519
|
+
if (survivingPartsReferenceDeletion || messageFieldReferencesDeletion) {
|
|
32520
|
+
retainedLineages += 1;
|
|
32521
|
+
log(`[magic-context] compaction-marker: flip-off cleanup RETAINED lineage at boundary ${boundaryMessageId} — a surviving tail_start_id references a row the deletion would remove`);
|
|
32522
|
+
continue;
|
|
32523
|
+
}
|
|
32524
|
+
const rows = db.transaction(() => {
|
|
32525
|
+
let changed = deleteSummaries(summaryIds);
|
|
32526
|
+
for (const partId of mcPartIds) {
|
|
32527
|
+
changed += deletePart.run(sessionId, partId).changes;
|
|
32528
|
+
}
|
|
32529
|
+
return changed;
|
|
32530
|
+
})();
|
|
32531
|
+
if (rows > 0 || summaryIds.size > 0 || mcPartIds.length > 0) {
|
|
32532
|
+
removedLineages += 1;
|
|
32533
|
+
removedRows += rows;
|
|
32534
|
+
}
|
|
32535
|
+
}
|
|
32536
|
+
if (orphanSummaryIds.size > 0) {
|
|
32537
|
+
const rowsToDelete = new Set(orphanSummaryIds);
|
|
32538
|
+
const survivingPartsReferenceDeletion = parsedParts.some((part) => part.tailStartId !== null && rowsToDelete.has(part.tailStartId));
|
|
32539
|
+
const messageFieldReferencesDeletion = [...rowsToDelete].some((id) => messageTailStartIds.has(id));
|
|
32540
|
+
if (survivingPartsReferenceDeletion || messageFieldReferencesDeletion) {
|
|
32541
|
+
retainedLineages += 1;
|
|
32542
|
+
} else {
|
|
32543
|
+
const rows = db.transaction(() => deleteSummaries(orphanSummaryIds))();
|
|
32544
|
+
removedLineages += 1;
|
|
32545
|
+
removedRows += rows;
|
|
32546
|
+
}
|
|
32547
|
+
}
|
|
32548
|
+
if (removedLineages > 0 || retainedLineages > 0) {
|
|
32549
|
+
log(`[magic-context] compaction-marker: flip-off cleanup for ${sessionId} removed ${removedLineages} lineage(s) (${removedRows} rows), retained ${retainedLineages}`);
|
|
32550
|
+
}
|
|
32551
|
+
return {
|
|
32552
|
+
verified: retainedLineages === 0,
|
|
32553
|
+
removedLineages,
|
|
32554
|
+
removedRows,
|
|
32555
|
+
retainedLineages
|
|
32556
|
+
};
|
|
32557
|
+
}
|
|
32558
|
+
function removeCompactionMarker(state2) {
|
|
32559
|
+
try {
|
|
32560
|
+
const db = getWritableOpenCodeDb();
|
|
32561
|
+
db.transaction(() => {
|
|
32562
|
+
db.prepare("DELETE FROM part WHERE id = ?").run(state2.summaryPartId);
|
|
32563
|
+
db.prepare("DELETE FROM message WHERE id = ?").run(state2.summaryMessageId);
|
|
32564
|
+
db.prepare("DELETE FROM part WHERE id = ?").run(state2.compactionPartId);
|
|
31518
32565
|
})();
|
|
31519
32566
|
return true;
|
|
31520
32567
|
} catch (error51) {
|
|
@@ -31530,9 +32577,9 @@ function dropMarkerSummaryTag(db, sessionId, summaryMessageId) {
|
|
|
31530
32577
|
if (tagNumber !== null)
|
|
31531
32578
|
updateTagStatus(db, sessionId, tagNumber, "dropped");
|
|
31532
32579
|
}
|
|
31533
|
-
function persistMarkerStateAndDropReplacedTag(db, sessionId,
|
|
32580
|
+
function persistMarkerStateAndDropReplacedTag(db, sessionId, state2, replacedSummaryMessageId) {
|
|
31534
32581
|
db.transaction(() => {
|
|
31535
|
-
setPersistedCompactionMarkerState(db, sessionId,
|
|
32582
|
+
setPersistedCompactionMarkerState(db, sessionId, state2);
|
|
31536
32583
|
if (replacedSummaryMessageId !== null) {
|
|
31537
32584
|
dropMarkerSummaryTag(db, sessionId, replacedSummaryMessageId);
|
|
31538
32585
|
}
|
|
@@ -31708,8 +32755,52 @@ function removeCompactionMarkerForSession(db, sessionId) {
|
|
|
31708
32755
|
}
|
|
31709
32756
|
}
|
|
31710
32757
|
}
|
|
32758
|
+
function reconcileForkOrphanedCompactionMarkers(db, sessionId) {
|
|
32759
|
+
if (getHarness() !== "opencode") {
|
|
32760
|
+
return { removed: 0, failed: false };
|
|
32761
|
+
}
|
|
32762
|
+
try {
|
|
32763
|
+
const owned = getPersistedCompactionMarkerState(db, sessionId);
|
|
32764
|
+
if (!owned) {
|
|
32765
|
+
return { removed: 0, failed: false };
|
|
32766
|
+
}
|
|
32767
|
+
const markers = listSessionCompactionMarkers(sessionId);
|
|
32768
|
+
let removed = 0;
|
|
32769
|
+
let failed = false;
|
|
32770
|
+
for (const marker of markers) {
|
|
32771
|
+
if (marker.compactionPartId === owned.compactionPartId) {
|
|
32772
|
+
continue;
|
|
32773
|
+
}
|
|
32774
|
+
if (marker.summaryMessageIds.length === 0) {
|
|
32775
|
+
continue;
|
|
32776
|
+
}
|
|
32777
|
+
const ordering = compareOpenCodeMessagesByCanonicalOrder(sessionId, marker.boundaryMessageId, owned.boundaryMessageId);
|
|
32778
|
+
if (ordering === null || ordering <= 0) {
|
|
32779
|
+
continue;
|
|
32780
|
+
}
|
|
32781
|
+
const currentOwned = getPersistedCompactionMarkerState(db, sessionId);
|
|
32782
|
+
if (!currentOwned || marker.compactionPartId === currentOwned.compactionPartId) {
|
|
32783
|
+
continue;
|
|
32784
|
+
}
|
|
32785
|
+
const ok = removeForeignCompactionMarker(sessionId, marker, currentOwned.summaryMessageId);
|
|
32786
|
+
if (ok) {
|
|
32787
|
+
removed += 1;
|
|
32788
|
+
sessionLog(sessionId, `compaction-marker hygiene: removed fork-orphaned marker part=${marker.compactionPartId} boundary=${marker.boundaryMessageId} (outranked owned boundary ${currentOwned.boundaryMessageId})`);
|
|
32789
|
+
} else {
|
|
32790
|
+
failed = true;
|
|
32791
|
+
}
|
|
32792
|
+
}
|
|
32793
|
+
if (removed > 0) {
|
|
32794
|
+
log(`[magic-context][${sessionId}] compaction-marker hygiene: removed ${removed} fork-orphaned marker(s); filterCompacted will now stop at this session's own marker`);
|
|
32795
|
+
}
|
|
32796
|
+
return { removed, failed };
|
|
32797
|
+
} catch (error51) {
|
|
32798
|
+
sessionLog(sessionId, "compaction-marker hygiene: scan failed (will retry on next degraded pass):", error51);
|
|
32799
|
+
return { removed: 0, failed: true };
|
|
32800
|
+
}
|
|
32801
|
+
}
|
|
31711
32802
|
function checkCompactionMarkerConsistency(db) {
|
|
31712
|
-
const opencodeDbPath =
|
|
32803
|
+
const opencodeDbPath = join6(getDataDir(), "opencode", "opencode.db");
|
|
31713
32804
|
let opencodeDb;
|
|
31714
32805
|
try {
|
|
31715
32806
|
opencodeDb = new Database(opencodeDbPath, { readonly: true });
|
|
@@ -31725,19 +32816,19 @@ function checkCompactionMarkerConsistency(db) {
|
|
|
31725
32816
|
const checkPart = opencodeDb.prepare("SELECT 1 FROM part WHERE id = ? LIMIT 1");
|
|
31726
32817
|
let reconciledCount = 0;
|
|
31727
32818
|
for (const row of persistedRows) {
|
|
31728
|
-
const
|
|
31729
|
-
if (!
|
|
32819
|
+
const state2 = getPersistedCompactionMarkerState(db, row.session_id);
|
|
32820
|
+
if (!state2)
|
|
31730
32821
|
continue;
|
|
31731
|
-
const boundaryExists = checkMessage.get(
|
|
31732
|
-
const summaryMessageExists = checkMessage.get(
|
|
31733
|
-
const compactionPartExists = checkPart.get(
|
|
31734
|
-
const summaryPartExists = checkPart.get(
|
|
32822
|
+
const boundaryExists = checkMessage.get(state2.boundaryMessageId) != null;
|
|
32823
|
+
const summaryMessageExists = checkMessage.get(state2.summaryMessageId) != null;
|
|
32824
|
+
const compactionPartExists = checkPart.get(state2.compactionPartId) != null;
|
|
32825
|
+
const summaryPartExists = checkPart.get(state2.summaryPartId) != null;
|
|
31735
32826
|
const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
|
|
31736
32827
|
if (allPresent)
|
|
31737
32828
|
continue;
|
|
31738
32829
|
let removedOk = false;
|
|
31739
32830
|
try {
|
|
31740
|
-
removedOk = removeCompactionMarker(
|
|
32831
|
+
removedOk = removeCompactionMarker(state2);
|
|
31741
32832
|
} catch (error51) {
|
|
31742
32833
|
sessionLog(row.session_id, "compaction-marker consistency: partial cleanup of half-written marker failed:", error51);
|
|
31743
32834
|
}
|
|
@@ -32468,6 +33559,8 @@ If no candidates, omit the \`<primer_candidates>\` section entirely.
|
|
|
32468
33559
|
|
|
32469
33560
|
Output valid XML only in this shape:
|
|
32470
33561
|
|
|
33562
|
+
Closing tags must match their opening tier tag (e.g. \`<p1>...</p1>\`, never \`<p1>...</p2>\`).
|
|
33563
|
+
|
|
32471
33564
|
\`\`\`xml
|
|
32472
33565
|
<output>
|
|
32473
33566
|
<compartments>
|
|
@@ -32685,8 +33778,8 @@ function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex)
|
|
|
32685
33778
|
}
|
|
32686
33779
|
|
|
32687
33780
|
// src/hooks/magic-context/compartment-runner-historian.ts
|
|
32688
|
-
import { mkdirSync as mkdirSync4, unlinkSync as
|
|
32689
|
-
import { join as
|
|
33781
|
+
import { mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
33782
|
+
import { join as join7 } from "node:path";
|
|
32690
33783
|
|
|
32691
33784
|
// src/features/magic-context/subagent-token-capture.ts
|
|
32692
33785
|
function asNumber(value) {
|
|
@@ -32778,161 +33871,6 @@ function recordChildInvocation(input) {
|
|
|
32778
33871
|
}
|
|
32779
33872
|
}
|
|
32780
33873
|
|
|
32781
|
-
// src/hooks/magic-context/compartment-parser.ts
|
|
32782
|
-
var COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
|
|
32783
|
-
var ATTR_START_REGEX = /\bstart="(\d+)"/;
|
|
32784
|
-
var ATTR_END_REGEX = /\bend="(\d+)"/;
|
|
32785
|
-
var ATTR_TITLE_REGEX = /\btitle="([^"]*)"/;
|
|
32786
|
-
var ATTR_EPISODE_REGEX = /\bepisode_type="([^"]*)"/;
|
|
32787
|
-
var ATTR_IMPORTANCE_REGEX = /\bimportance="(\d+)"/;
|
|
32788
|
-
function makeTierRegex(n) {
|
|
32789
|
-
return new RegExp(`<p${n}\\s*/>|<p${n}\\s*>(.*?)</p${n}>`, "s");
|
|
32790
|
-
}
|
|
32791
|
-
var TIER_REGEXES = [makeTierRegex(1), makeTierRegex(2), makeTierRegex(3), makeTierRegex(4)];
|
|
32792
|
-
var CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
|
|
32793
|
-
var FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
|
|
32794
|
-
var UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
|
|
32795
|
-
var USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
|
|
32796
|
-
var USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
|
|
32797
|
-
var PRIMER_CANDIDATES_REGEX = /<primer_candidates>(.*?)<\/primer_candidates>/s;
|
|
32798
|
-
var PRIMER_ELEMENT_REGEX = /<primer\s+at_compartment="(\d+)"\s*>(.*?)<\/primer>/gs;
|
|
32799
|
-
var PRIMER_ITEM_REGEX = /^\s*(?:\*|-|\d+\.)\s*(.+)$/gm;
|
|
32800
|
-
var FACTS_BLOCK_REGEX = /<facts>(.*?)<\/facts>/s;
|
|
32801
|
-
var EVENTS_BLOCK_REGEX = /<events>(.*?)<\/events>/s;
|
|
32802
|
-
var EVENT_ELEMENT_REGEX = /<([a-z_]+)\s+at_compartment="(\d+)"\s*>(.*?)<\/\1>/gs;
|
|
32803
|
-
var EVENT_FIELD_REGEX = /<([a-z_]+)\s*>(.*?)<\/\1>/gs;
|
|
32804
|
-
function extractTier(inner, index) {
|
|
32805
|
-
const m = inner.match(TIER_REGEXES[index]);
|
|
32806
|
-
if (!m)
|
|
32807
|
-
return;
|
|
32808
|
-
return unescapeXml((m[1] ?? "").trim());
|
|
32809
|
-
}
|
|
32810
|
-
function parseCompartmentOutput(text) {
|
|
32811
|
-
const compartments = [];
|
|
32812
|
-
const facts = [];
|
|
32813
|
-
for (const match of text.matchAll(COMPARTMENT_REGEX)) {
|
|
32814
|
-
const attrs = match[1];
|
|
32815
|
-
const inner = match[2];
|
|
32816
|
-
const startMatch = attrs.match(ATTR_START_REGEX);
|
|
32817
|
-
const endMatch = attrs.match(ATTR_END_REGEX);
|
|
32818
|
-
const titleMatch = attrs.match(ATTR_TITLE_REGEX);
|
|
32819
|
-
if (!startMatch || !endMatch || !titleMatch)
|
|
32820
|
-
continue;
|
|
32821
|
-
const startMessage = parseInt(startMatch[1], 10);
|
|
32822
|
-
const endMessage = parseInt(endMatch[1], 10);
|
|
32823
|
-
const title = unescapeXml(titleMatch[1]);
|
|
32824
|
-
if (Number.isNaN(startMessage) || Number.isNaN(endMessage) || !title)
|
|
32825
|
-
continue;
|
|
32826
|
-
const episodeMatch = attrs.match(ATTR_EPISODE_REGEX);
|
|
32827
|
-
const importanceMatch = attrs.match(ATTR_IMPORTANCE_REGEX);
|
|
32828
|
-
const episodeType = episodeMatch ? unescapeXml(episodeMatch[1]) : undefined;
|
|
32829
|
-
const importance = importanceMatch ? parseInt(importanceMatch[1], 10) : undefined;
|
|
32830
|
-
const p1 = extractTier(inner, 0);
|
|
32831
|
-
if (typeof p1 === "string" && p1.length > 0) {
|
|
32832
|
-
const p2 = extractTier(inner, 1);
|
|
32833
|
-
const p3 = extractTier(inner, 2);
|
|
32834
|
-
const p4 = extractTier(inner, 3);
|
|
32835
|
-
compartments.push({
|
|
32836
|
-
startMessage,
|
|
32837
|
-
endMessage,
|
|
32838
|
-
title,
|
|
32839
|
-
content: p1,
|
|
32840
|
-
p1,
|
|
32841
|
-
p2: typeof p2 === "string" ? p2 : p1,
|
|
32842
|
-
p3: typeof p3 === "string" ? p3 : typeof p2 === "string" ? p2 : p1,
|
|
32843
|
-
p4: typeof p4 === "string" ? p4 : "",
|
|
32844
|
-
importance,
|
|
32845
|
-
episodeType
|
|
32846
|
-
});
|
|
32847
|
-
continue;
|
|
32848
|
-
}
|
|
32849
|
-
const content = unescapeXml(inner.trim());
|
|
32850
|
-
if (content) {
|
|
32851
|
-
compartments.push({
|
|
32852
|
-
startMessage,
|
|
32853
|
-
endMessage,
|
|
32854
|
-
title,
|
|
32855
|
-
content,
|
|
32856
|
-
importance,
|
|
32857
|
-
episodeType
|
|
32858
|
-
});
|
|
32859
|
-
}
|
|
32860
|
-
}
|
|
32861
|
-
const factsBlockMatch = text.match(FACTS_BLOCK_REGEX);
|
|
32862
|
-
const factsScope = factsBlockMatch ? factsBlockMatch[1] : text.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
|
|
32863
|
-
for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
|
|
32864
|
-
const category = categoryMatch[1];
|
|
32865
|
-
const blockContent = categoryMatch[2];
|
|
32866
|
-
for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
|
|
32867
|
-
const content = unescapeXml(itemMatch[1].trim());
|
|
32868
|
-
if (content) {
|
|
32869
|
-
facts.push({ category, content });
|
|
32870
|
-
}
|
|
32871
|
-
}
|
|
32872
|
-
}
|
|
32873
|
-
const unprocessedMatch = text.match(UNPROCESSED_REGEX);
|
|
32874
|
-
const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
|
|
32875
|
-
const userObservations = [];
|
|
32876
|
-
const userObsMatch = text.match(USER_OBSERVATIONS_REGEX);
|
|
32877
|
-
if (userObsMatch) {
|
|
32878
|
-
for (const itemMatch of userObsMatch[1].matchAll(USER_OBS_ITEM_REGEX)) {
|
|
32879
|
-
const obs = unescapeXml(itemMatch[1].trim());
|
|
32880
|
-
if (obs)
|
|
32881
|
-
userObservations.push(obs);
|
|
32882
|
-
}
|
|
32883
|
-
}
|
|
32884
|
-
const primerCandidates = [];
|
|
32885
|
-
const primerMatch = text.match(PRIMER_CANDIDATES_REGEX);
|
|
32886
|
-
if (primerMatch) {
|
|
32887
|
-
const block = primerMatch[1];
|
|
32888
|
-
let sawElement = false;
|
|
32889
|
-
for (const el of block.matchAll(PRIMER_ELEMENT_REGEX)) {
|
|
32890
|
-
sawElement = true;
|
|
32891
|
-
const question = unescapeXml(el[2].trim());
|
|
32892
|
-
if (question) {
|
|
32893
|
-
primerCandidates.push({
|
|
32894
|
-
question,
|
|
32895
|
-
originCompartmentIndex: Number.parseInt(el[1], 10)
|
|
32896
|
-
});
|
|
32897
|
-
}
|
|
32898
|
-
}
|
|
32899
|
-
if (!sawElement) {
|
|
32900
|
-
for (const itemMatch of block.matchAll(PRIMER_ITEM_REGEX)) {
|
|
32901
|
-
const question = unescapeXml(itemMatch[1].trim());
|
|
32902
|
-
if (question)
|
|
32903
|
-
primerCandidates.push({ question });
|
|
32904
|
-
}
|
|
32905
|
-
}
|
|
32906
|
-
}
|
|
32907
|
-
const events = parseEvents(text);
|
|
32908
|
-
compartments.sort((a, b) => a.startMessage - b.startMessage);
|
|
32909
|
-
return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
|
|
32910
|
-
}
|
|
32911
|
-
function parseEvents(text) {
|
|
32912
|
-
const blockMatch = text.match(EVENTS_BLOCK_REGEX);
|
|
32913
|
-
if (!blockMatch)
|
|
32914
|
-
return [];
|
|
32915
|
-
const block = blockMatch[1];
|
|
32916
|
-
const events = [];
|
|
32917
|
-
for (const elMatch of block.matchAll(EVENT_ELEMENT_REGEX)) {
|
|
32918
|
-
const kind = elMatch[1];
|
|
32919
|
-
const atRaw = parseInt(elMatch[2], 10);
|
|
32920
|
-
const atCompartment = Number.isNaN(atRaw) ? null : atRaw;
|
|
32921
|
-
const fields = {};
|
|
32922
|
-
for (const fieldMatch of elMatch[3].matchAll(EVENT_FIELD_REGEX)) {
|
|
32923
|
-
const name = fieldMatch[1];
|
|
32924
|
-
const value = unescapeXml(fieldMatch[2].trim());
|
|
32925
|
-
if (value)
|
|
32926
|
-
fields[name] = value;
|
|
32927
|
-
}
|
|
32928
|
-
events.push({ kind, atCompartment, fields });
|
|
32929
|
-
}
|
|
32930
|
-
return events;
|
|
32931
|
-
}
|
|
32932
|
-
function unescapeXml(s) {
|
|
32933
|
-
return s.replace(/&/g, "&").replace(/'/g, "'").replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">");
|
|
32934
|
-
}
|
|
32935
|
-
|
|
32936
33874
|
// src/hooks/magic-context/compartment-runner-mapping.ts
|
|
32937
33875
|
function tierFieldsOf(c) {
|
|
32938
33876
|
return {
|
|
@@ -33137,6 +34075,24 @@ function historianResponseDumpDir(directory) {
|
|
|
33137
34075
|
return getProjectMagicContextHistorianDir(directory);
|
|
33138
34076
|
}
|
|
33139
34077
|
var MAX_HISTORIAN_RETRIES = 2;
|
|
34078
|
+
var HISTORIAN_REASONING_PART_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
|
|
34079
|
+
function extractLatestHistorianReasoning(messages) {
|
|
34080
|
+
if (!Array.isArray(messages))
|
|
34081
|
+
return null;
|
|
34082
|
+
const latest = messages.filter((message) => isRecord(message) && isRecord(message.info) && message.info.role === "assistant").sort((left, right) => historianMessageCreatedAt(right) - historianMessageCreatedAt(left))[0];
|
|
34083
|
+
if (!latest || !Array.isArray(latest.parts))
|
|
34084
|
+
return null;
|
|
34085
|
+
return latest.parts.filter(isHistorianReasoningPart).map((part) => part.text).join(`
|
|
34086
|
+
`) || null;
|
|
34087
|
+
}
|
|
34088
|
+
function isHistorianReasoningPart(part) {
|
|
34089
|
+
return isRecord(part) && typeof part.type === "string" && HISTORIAN_REASONING_PART_TYPES.has(part.type) && typeof part.text === "string" && part.text.length > 0;
|
|
34090
|
+
}
|
|
34091
|
+
function historianMessageCreatedAt(message) {
|
|
34092
|
+
if (!isRecord(message.info) || !isRecord(message.info.time))
|
|
34093
|
+
return 0;
|
|
34094
|
+
return typeof message.info.time.created === "number" ? message.info.time.created : 0;
|
|
34095
|
+
}
|
|
33140
34096
|
async function runValidatedHistorianPass(args) {
|
|
33141
34097
|
const firstRun = await runHistorianPrompt({
|
|
33142
34098
|
...args,
|
|
@@ -33202,6 +34158,7 @@ async function runEditorPassOrFallback(args) {
|
|
|
33202
34158
|
sessionLog(args.parentSessionId, "historian two-pass: running editor on draft");
|
|
33203
34159
|
const editorRun = await runHistorianPrompt({
|
|
33204
34160
|
client: args.client,
|
|
34161
|
+
db: args.db,
|
|
33205
34162
|
parentSessionId: args.parentSessionId,
|
|
33206
34163
|
sessionDirectory: args.sessionDirectory,
|
|
33207
34164
|
prompt: buildHistorianEditorPrompt(args.draftXml),
|
|
@@ -33228,6 +34185,7 @@ async function runEditorPassOrFallback(args) {
|
|
|
33228
34185
|
async function runHistorianPrompt(args) {
|
|
33229
34186
|
const {
|
|
33230
34187
|
client,
|
|
34188
|
+
db,
|
|
33231
34189
|
parentSessionId,
|
|
33232
34190
|
sessionDirectory,
|
|
33233
34191
|
prompt,
|
|
@@ -33261,12 +34219,12 @@ async function runHistorianPrompt(args) {
|
|
|
33261
34219
|
};
|
|
33262
34220
|
try {
|
|
33263
34221
|
sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
|
|
33264
|
-
const createResponse = await
|
|
33265
|
-
|
|
33266
|
-
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
34222
|
+
const createResponse = await createChildSessionWithFence({
|
|
34223
|
+
client,
|
|
34224
|
+
db,
|
|
34225
|
+
parentSessionId,
|
|
34226
|
+
title: "magic-context-compartment",
|
|
34227
|
+
directory: sessionDirectory
|
|
33270
34228
|
});
|
|
33271
34229
|
const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
33272
34230
|
agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
|
|
@@ -33314,7 +34272,18 @@ async function runHistorianPrompt(args) {
|
|
|
33314
34272
|
preferResponseOnMissingData: true
|
|
33315
34273
|
});
|
|
33316
34274
|
const invocationId = recordInvocation({ status: "completed", messages });
|
|
33317
|
-
const
|
|
34275
|
+
const lengthCapped = hasLengthCappedOutput(messages);
|
|
34276
|
+
const textResult = extractLatestAssistantText(messages);
|
|
34277
|
+
const reasoningResult = textResult ? null : extractLatestHistorianReasoning(messages);
|
|
34278
|
+
if (!textResult && reasoningResult && lengthCapped) {
|
|
34279
|
+
const outputTokens = sumTokensFromChildMessages(messages).output;
|
|
34280
|
+
return {
|
|
34281
|
+
ok: false,
|
|
34282
|
+
error: `historian output length-capped at ${outputTokens} tokens (all reasoning, no text) — set historian.maxTokens or route historian.model to a low-reasoning lane/variant`,
|
|
34283
|
+
invocationId: invocationId ?? undefined
|
|
34284
|
+
};
|
|
34285
|
+
}
|
|
34286
|
+
const result = textResult ?? reasoningResult;
|
|
33318
34287
|
if (!result) {
|
|
33319
34288
|
return {
|
|
33320
34289
|
ok: false,
|
|
@@ -33366,6 +34335,7 @@ async function runFallbackHistorianPass(args) {
|
|
|
33366
34335
|
args.callbacks?.onModelFallback?.(modelId, i + 1, chain.length);
|
|
33367
34336
|
const fallbackRun = await runHistorianPrompt({
|
|
33368
34337
|
client: args.client,
|
|
34338
|
+
db: args.db,
|
|
33369
34339
|
parentSessionId: args.parentSessionId,
|
|
33370
34340
|
sessionDirectory: args.sessionDirectory,
|
|
33371
34341
|
prompt: args.prompt,
|
|
@@ -33427,7 +34397,7 @@ function cleanupHistorianDump(sessionId, dumpPath) {
|
|
|
33427
34397
|
if (!dumpPath)
|
|
33428
34398
|
return;
|
|
33429
34399
|
try {
|
|
33430
|
-
|
|
34400
|
+
unlinkSync3(dumpPath);
|
|
33431
34401
|
} catch (error51) {
|
|
33432
34402
|
sessionLog(sessionId, "compartment agent: failed to remove historian response dump", {
|
|
33433
34403
|
dumpPath,
|
|
@@ -33442,7 +34412,7 @@ function dumpHistorianResponse(sessionId, directory, label, text) {
|
|
|
33442
34412
|
ensureCortexKitArtifactGitignore(directory);
|
|
33443
34413
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
33444
34414
|
const safeLabel = sanitizeDumpName(label);
|
|
33445
|
-
const dumpPath =
|
|
34415
|
+
const dumpPath = join7(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
33446
34416
|
writeFileSync2(dumpPath, text, "utf8");
|
|
33447
34417
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
33448
34418
|
label,
|
|
@@ -33465,26 +34435,7 @@ function sanitizeDumpName(value) {
|
|
|
33465
34435
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
33466
34436
|
|
|
33467
34437
|
// src/features/magic-context/mural/render-trigger.ts
|
|
33468
|
-
import { createHash as
|
|
33469
|
-
|
|
33470
|
-
// src/shared/harness-provider-map.ts
|
|
33471
|
-
var PI_TO_CANONICAL_PROVIDER = {
|
|
33472
|
-
"openai-codex": "openai",
|
|
33473
|
-
"google-antigravity": "google"
|
|
33474
|
-
};
|
|
33475
|
-
function remapProviderPrefix(ref, map2) {
|
|
33476
|
-
if (typeof ref !== "string")
|
|
33477
|
-
return ref;
|
|
33478
|
-
const slash = ref.indexOf("/");
|
|
33479
|
-
if (slash <= 0)
|
|
33480
|
-
return ref;
|
|
33481
|
-
const provider2 = ref.slice(0, slash);
|
|
33482
|
-
const mapped = map2[provider2];
|
|
33483
|
-
return mapped ? `${mapped}${ref.slice(slash)}` : ref;
|
|
33484
|
-
}
|
|
33485
|
-
function piModelRefToCanonical(ref) {
|
|
33486
|
-
return remapProviderPrefix(ref, PI_TO_CANONICAL_PROVIDER);
|
|
33487
|
-
}
|
|
34438
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
33488
34439
|
|
|
33489
34440
|
// src/features/magic-context/mural/mural-selection.ts
|
|
33490
34441
|
var DEFAULT_MURAL_MEMORY_BUDGET = 8000;
|
|
@@ -33687,6 +34638,8 @@ function naturalLineLengths(entries, roomWidth) {
|
|
|
33687
34638
|
let currentCategory = null;
|
|
33688
34639
|
for (let index = 0;index < entries.length; index++) {
|
|
33689
34640
|
const entry = entries[index];
|
|
34641
|
+
if (!entry)
|
|
34642
|
+
continue;
|
|
33690
34643
|
if (entry.category !== currentCategory) {
|
|
33691
34644
|
currentCategory = entry.category;
|
|
33692
34645
|
lengths.push(codepoints(` <${entry.category}> `));
|
|
@@ -33719,6 +34672,8 @@ function planLines(entries, roomWidth) {
|
|
|
33719
34672
|
let currentCategory = null;
|
|
33720
34673
|
for (let index = 0;index < entries.length; index++) {
|
|
33721
34674
|
const entry = entries[index];
|
|
34675
|
+
if (!entry)
|
|
34676
|
+
continue;
|
|
33722
34677
|
if (entry.category !== currentCategory) {
|
|
33723
34678
|
currentCategory = entry.category;
|
|
33724
34679
|
lines.push({
|
|
@@ -33782,8 +34737,11 @@ function renderLayout(entries, plan, roomWidth, columnCount) {
|
|
|
33782
34737
|
const usage = {};
|
|
33783
34738
|
let filledLineCount = 0;
|
|
33784
34739
|
for (const [column, columnPlan] of splitPlan(plan, columnCount).entries()) {
|
|
34740
|
+
const columnGrid = grid[column];
|
|
34741
|
+
if (!columnGrid)
|
|
34742
|
+
continue;
|
|
33785
34743
|
for (const [row, line] of columnPlan.entries()) {
|
|
33786
|
-
|
|
34744
|
+
columnGrid[row] = line.text;
|
|
33787
34745
|
filledLineCount += 1;
|
|
33788
34746
|
usage[line.category] = (usage[line.category] ?? 0) + 1;
|
|
33789
34747
|
const placementLine = row + 1;
|
|
@@ -33894,6 +34852,7 @@ function glyph(character) {
|
|
|
33894
34852
|
}
|
|
33895
34853
|
function drawGlyph(pixels, width, height, x, y, character, color) {
|
|
33896
34854
|
const glyphData = glyph(character);
|
|
34855
|
+
const [red, green, blue] = color;
|
|
33897
34856
|
for (let row = 0;row < MURAL_CELL_HEIGHT; row++) {
|
|
33898
34857
|
const pattern = glyphData.rows[row] ?? 0;
|
|
33899
34858
|
for (let column = 0;column < glyphData.width; column++) {
|
|
@@ -33904,9 +34863,9 @@ function drawGlyph(pixels, width, height, x, y, character, color) {
|
|
|
33904
34863
|
if (px < 0 || py < 0 || px >= width || py >= height)
|
|
33905
34864
|
continue;
|
|
33906
34865
|
const offset = (py * width + px) * 3;
|
|
33907
|
-
pixels[offset] =
|
|
33908
|
-
pixels[offset + 1] =
|
|
33909
|
-
pixels[offset + 2] =
|
|
34866
|
+
pixels[offset] = red;
|
|
34867
|
+
pixels[offset + 1] = green;
|
|
34868
|
+
pixels[offset + 2] = blue;
|
|
33910
34869
|
}
|
|
33911
34870
|
}
|
|
33912
34871
|
}
|
|
@@ -33918,6 +34877,7 @@ function drawText(pixels, width, height, x, y, text, color) {
|
|
|
33918
34877
|
}
|
|
33919
34878
|
}
|
|
33920
34879
|
function fillRect(pixels, canvasWidth, canvasHeight, x, y, width, height, color) {
|
|
34880
|
+
const [red, green, blue] = color;
|
|
33921
34881
|
const left = Math.max(0, x);
|
|
33922
34882
|
const top = Math.max(0, y);
|
|
33923
34883
|
const right = Math.min(canvasWidth, x + width);
|
|
@@ -33925,9 +34885,9 @@ function fillRect(pixels, canvasWidth, canvasHeight, x, y, width, height, color)
|
|
|
33925
34885
|
for (let py = top;py < bottom; py++) {
|
|
33926
34886
|
for (let px = left;px < right; px++) {
|
|
33927
34887
|
const offset = (py * canvasWidth + px) * 3;
|
|
33928
|
-
pixels[offset] =
|
|
33929
|
-
pixels[offset + 1] =
|
|
33930
|
-
pixels[offset + 2] =
|
|
34888
|
+
pixels[offset] = red;
|
|
34889
|
+
pixels[offset + 1] = green;
|
|
34890
|
+
pixels[offset + 2] = blue;
|
|
33931
34891
|
}
|
|
33932
34892
|
}
|
|
33933
34893
|
}
|
|
@@ -33954,6 +34914,9 @@ function renderMural(entries) {
|
|
|
33954
34914
|
});
|
|
33955
34915
|
const fittingCandidates = candidates.filter((candidate) => candidate.layout.droppedIds.length === 0);
|
|
33956
34916
|
const candidatesToCompare = fittingCandidates.length > 0 ? fittingCandidates : candidates;
|
|
34917
|
+
const firstCandidate = candidatesToCompare[0];
|
|
34918
|
+
if (!firstCandidate)
|
|
34919
|
+
throw new Error("mural layout candidate list is empty");
|
|
33957
34920
|
const selected = candidatesToCompare.reduce((best, candidate) => {
|
|
33958
34921
|
if (!best)
|
|
33959
34922
|
return candidate;
|
|
@@ -33964,7 +34927,7 @@ function renderMural(entries) {
|
|
|
33964
34927
|
return candidate.tileArea < best.tileArea ? candidate : best;
|
|
33965
34928
|
}
|
|
33966
34929
|
return candidate.requestedColumnCount < best.requestedColumnCount ? candidate : best;
|
|
33967
|
-
},
|
|
34930
|
+
}, firstCandidate);
|
|
33968
34931
|
const { layout, width, height } = selected;
|
|
33969
34932
|
const pixels = new Uint8Array(width * height * 3).fill(255);
|
|
33970
34933
|
const contentWidth = layout.columnCount === 0 ? 0 : layout.columnCount * roomWidth * MURAL_CELL_WIDTH + MURAL_COLUMN_GAP * (layout.columnCount - 1) * MURAL_CELL_WIDTH;
|
|
@@ -34008,8 +34971,8 @@ function getMuralCoverage(db, projectIdentity) {
|
|
|
34008
34971
|
const cueState = getMuralCueState(db, memories.map((memory) => memory.id));
|
|
34009
34972
|
let cuedMemoryCount = 0;
|
|
34010
34973
|
for (const memory of memories) {
|
|
34011
|
-
const
|
|
34012
|
-
if (
|
|
34974
|
+
const state2 = cueState.get(memory.id);
|
|
34975
|
+
if (state2 && typeof state2.cue === "string" && state2.cue.trim() !== "" && state2.hash !== null && state2.hash === computeCueContentHash(memory.content)) {
|
|
34013
34976
|
cuedMemoryCount += 1;
|
|
34014
34977
|
}
|
|
34015
34978
|
}
|
|
@@ -34023,16 +34986,16 @@ function resolveMural(db, projectIdentity, budgetTokens = DEFAULT_MURAL_MEMORY_B
|
|
|
34023
34986
|
const cueState = getMuralCueState(db, overflow.map((memory) => memory.id));
|
|
34024
34987
|
const entries = [];
|
|
34025
34988
|
for (const memory of overflow) {
|
|
34026
|
-
const
|
|
34027
|
-
if (!
|
|
34989
|
+
const state2 = cueState.get(memory.id);
|
|
34990
|
+
if (!state2 || state2.cue === null || state2.hash === null)
|
|
34028
34991
|
continue;
|
|
34029
|
-
if (
|
|
34992
|
+
if (state2.hash !== computeCueContentHash(memory.content))
|
|
34030
34993
|
continue;
|
|
34031
34994
|
entries.push({
|
|
34032
34995
|
id: memory.id,
|
|
34033
34996
|
category: memory.category,
|
|
34034
34997
|
importance: memory.importance ?? 50,
|
|
34035
|
-
cue:
|
|
34998
|
+
cue: state2.cue
|
|
34036
34999
|
});
|
|
34037
35000
|
}
|
|
34038
35001
|
entries.sort(compareMuralEntries);
|
|
@@ -34098,7 +35061,7 @@ function ensureMuralRendered(db, projectIdentity, budgetTokens = DEFAULT_MURAL_M
|
|
|
34098
35061
|
return { hasMural: false, rerendered: false };
|
|
34099
35062
|
}
|
|
34100
35063
|
const rendered = renderMural(entries);
|
|
34101
|
-
const textHash =
|
|
35064
|
+
const textHash = createHash14("sha256").update(rendered.sha256Input).digest("hex");
|
|
34102
35065
|
const existing = getMural(db, projectIdentity);
|
|
34103
35066
|
if (existing && existing.contentHash === textHash && existing.width === rendered.width && existing.height === rendered.height) {
|
|
34104
35067
|
return {
|
|
@@ -34540,6 +35503,38 @@ var INJECTION_CACHE_MAX = 100;
|
|
|
34540
35503
|
var injectionCache = new BoundedSessionMap(INJECTION_CACHE_MAX);
|
|
34541
35504
|
function clearInjectionCache(sessionId) {
|
|
34542
35505
|
injectionCache.delete(sessionId);
|
|
35506
|
+
resetDegradedReanchorState(sessionId);
|
|
35507
|
+
}
|
|
35508
|
+
var degradedRebuildCountBySession = new BoundedSessionMap(INJECTION_CACHE_MAX);
|
|
35509
|
+
var reAnchorLoggedBySession = new BoundedSessionMap(INJECTION_CACHE_MAX);
|
|
35510
|
+
var REANCHOR_MIN_DEGRADED_PASSES = 2;
|
|
35511
|
+
function resetDegradedReanchorState(sessionId) {
|
|
35512
|
+
degradedRebuildCountBySession.delete(sessionId);
|
|
35513
|
+
reAnchorLoggedBySession.delete(sessionId);
|
|
35514
|
+
}
|
|
35515
|
+
function noteDegradedRebuild(sessionId) {
|
|
35516
|
+
const next = (degradedRebuildCountBySession.get(sessionId) ?? 0) + 1;
|
|
35517
|
+
degradedRebuildCountBySession.set(sessionId, next);
|
|
35518
|
+
return next;
|
|
35519
|
+
}
|
|
35520
|
+
function clearDegradedRebuild(sessionId) {
|
|
35521
|
+
degradedRebuildCountBySession.delete(sessionId);
|
|
35522
|
+
reAnchorLoggedBySession.delete(sessionId);
|
|
35523
|
+
}
|
|
35524
|
+
function logReanchorOnce(sessionId, message) {
|
|
35525
|
+
if (reAnchorLoggedBySession.get(sessionId))
|
|
35526
|
+
return;
|
|
35527
|
+
reAnchorLoggedBySession.set(sessionId, true);
|
|
35528
|
+
sessionLog(sessionId, message);
|
|
35529
|
+
}
|
|
35530
|
+
function findVisibleReanchorIndex(compartments, visibleMessageIds) {
|
|
35531
|
+
for (let index = compartments.length - 1;index >= 0; index -= 1) {
|
|
35532
|
+
const endMessageId = compartments[index]?.endMessageId;
|
|
35533
|
+
if (typeof endMessageId === "string" && endMessageId.length > 0 && visibleMessageIds.has(endMessageId)) {
|
|
35534
|
+
return index;
|
|
35535
|
+
}
|
|
35536
|
+
}
|
|
35537
|
+
return -1;
|
|
34543
35538
|
}
|
|
34544
35539
|
function getVisibleMemoryIds(db, sessionId) {
|
|
34545
35540
|
try {
|
|
@@ -34729,24 +35724,63 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
34729
35724
|
return result2;
|
|
34730
35725
|
}
|
|
34731
35726
|
let skippedVisibleMessages = 0;
|
|
35727
|
+
let needsFreshMaterialization = false;
|
|
35728
|
+
let resultEndMessage = lastEnd;
|
|
35729
|
+
let resultEndMessageId = null;
|
|
34732
35730
|
const cutoffIndex = messages.findIndex((message) => message.info.id === trimEndMessageId);
|
|
34733
35731
|
if (cutoffIndex >= 0) {
|
|
35732
|
+
clearDegradedRebuild(sessionId);
|
|
34734
35733
|
skippedVisibleMessages = cutoffIndex + 1;
|
|
34735
35734
|
const remaining = messages.slice(cutoffIndex + 1);
|
|
34736
35735
|
messages.splice(0, messages.length, ...remaining);
|
|
35736
|
+
resultEndMessageId = trimEndMessageId;
|
|
34737
35737
|
} else {
|
|
34738
|
-
|
|
35738
|
+
const degradedCount = noteDegradedRebuild(sessionId);
|
|
35739
|
+
if (degradedCount === 1) {
|
|
35740
|
+
reconcileForkOrphanedCompactionMarkers(db, sessionId);
|
|
35741
|
+
}
|
|
35742
|
+
let reAnchored = false;
|
|
35743
|
+
if (degradedCount >= REANCHOR_MIN_DEGRADED_PASSES && isCacheBusting) {
|
|
35744
|
+
const visibleMessageIds = new Set;
|
|
35745
|
+
for (const message of messages) {
|
|
35746
|
+
if (typeof message.info.id === "string")
|
|
35747
|
+
visibleMessageIds.add(message.info.id);
|
|
35748
|
+
}
|
|
35749
|
+
const reAnchorIndex = findVisibleReanchorIndex(compartments, visibleMessageIds);
|
|
35750
|
+
if (reAnchorIndex >= 0) {
|
|
35751
|
+
const reAnchorCompartment = compartments[reAnchorIndex];
|
|
35752
|
+
const reAnchorCutoff = messages.findIndex((message) => message.info.id === reAnchorCompartment.endMessageId);
|
|
35753
|
+
if (reAnchorCutoff >= 0) {
|
|
35754
|
+
skippedVisibleMessages = reAnchorCutoff + 1;
|
|
35755
|
+
const remaining = messages.slice(reAnchorCutoff + 1);
|
|
35756
|
+
messages.splice(0, messages.length, ...remaining);
|
|
35757
|
+
resultEndMessage = reAnchorCompartment.endMessage;
|
|
35758
|
+
resultEndMessageId = reAnchorCompartment.endMessageId;
|
|
35759
|
+
reAnchored = true;
|
|
35760
|
+
logReanchorOnce(sessionId, `compartment injection re-anchored: natural boundary ${trimEndMessageId} not visible for ${degradedCount} passes; splicing at visible compartment boundary ${resultEndMessageId} (ordinal ${resultEndMessage})`);
|
|
35761
|
+
}
|
|
35762
|
+
}
|
|
35763
|
+
if (!reAnchored) {
|
|
35764
|
+
needsFreshMaterialization = true;
|
|
35765
|
+
logReanchorOnce(sessionId, `compartment injection degraded: boundary ${trimEndMessageId} not visible for ${degradedCount} passes and no compartment boundary is visible; requesting fresh materialization to re-cut the baseline`);
|
|
35766
|
+
}
|
|
35767
|
+
} else {
|
|
35768
|
+
sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${trimEndMessageId} not in visible messages (consecutive degraded passes: ${degradedCount})`);
|
|
35769
|
+
}
|
|
34739
35770
|
}
|
|
34740
35771
|
const result = {
|
|
34741
35772
|
block,
|
|
34742
|
-
compartmentEndMessage:
|
|
34743
|
-
compartmentEndMessageId:
|
|
35773
|
+
compartmentEndMessage: resultEndMessage,
|
|
35774
|
+
compartmentEndMessageId: resultEndMessageId,
|
|
34744
35775
|
compartmentCount: compartments.length,
|
|
34745
35776
|
skippedVisibleMessages,
|
|
34746
35777
|
factCount: facts.length,
|
|
34747
35778
|
memoryCount,
|
|
34748
35779
|
rebuiltFromDb: true
|
|
34749
35780
|
};
|
|
35781
|
+
if (needsFreshMaterialization) {
|
|
35782
|
+
result.needsFreshMaterialization = true;
|
|
35783
|
+
}
|
|
34750
35784
|
injectionCache.set(sessionId, { kind: "populated", injection: result });
|
|
34751
35785
|
return result;
|
|
34752
35786
|
}
|
|
@@ -35152,41 +36186,41 @@ function readCurrentM0SnapshotMarkers(args) {
|
|
|
35152
36186
|
});
|
|
35153
36187
|
return fresh.markers;
|
|
35154
36188
|
}
|
|
35155
|
-
function snapshotMarkersFromCachedM0(
|
|
35156
|
-
if (!
|
|
36189
|
+
function snapshotMarkersFromCachedM0(state2) {
|
|
36190
|
+
if (!state2.cachedM0Bytes)
|
|
35157
36191
|
return null;
|
|
35158
|
-
const cachedUpgradeIdentity = decodeCachedM0UpgradeIdentity(
|
|
35159
|
-
if (
|
|
36192
|
+
const cachedUpgradeIdentity = decodeCachedM0UpgradeIdentity(state2.cachedM0UpgradeState);
|
|
36193
|
+
if (state2.cachedM0ProjectMemoryEpoch === null)
|
|
35160
36194
|
return null;
|
|
35161
|
-
if (
|
|
36195
|
+
if (state2.cachedM0ProjectUserProfileVersion === null)
|
|
35162
36196
|
return null;
|
|
35163
|
-
if (
|
|
36197
|
+
if (state2.cachedM0MaxCompartmentSeq === null)
|
|
35164
36198
|
return null;
|
|
35165
|
-
if (
|
|
36199
|
+
if (state2.cachedM0MaxMemoryId === null)
|
|
35166
36200
|
return null;
|
|
35167
|
-
if (
|
|
36201
|
+
if (state2.cachedM0MaxMutationId === null)
|
|
35168
36202
|
return null;
|
|
35169
|
-
if (
|
|
36203
|
+
if (state2.cachedM0MaxMemoryMutationId === null)
|
|
35170
36204
|
return null;
|
|
35171
|
-
if (
|
|
36205
|
+
if (state2.cachedM0SessionFactsVersion === null)
|
|
35172
36206
|
return null;
|
|
35173
36207
|
return {
|
|
35174
|
-
projectMemoryEpoch:
|
|
35175
|
-
workspaceFingerprint:
|
|
35176
|
-
projectUserProfileVersion:
|
|
35177
|
-
maxCompartmentSeq:
|
|
35178
|
-
maxMemoryId:
|
|
35179
|
-
maxMutationId:
|
|
35180
|
-
maxMemoryMutationId:
|
|
35181
|
-
projectDocsHash:
|
|
35182
|
-
materializedAt:
|
|
35183
|
-
sessionFactsVersion:
|
|
36208
|
+
projectMemoryEpoch: state2.cachedM0ProjectMemoryEpoch,
|
|
36209
|
+
workspaceFingerprint: state2.cachedM0WorkspaceFingerprint,
|
|
36210
|
+
projectUserProfileVersion: state2.cachedM0ProjectUserProfileVersion,
|
|
36211
|
+
maxCompartmentSeq: state2.cachedM0MaxCompartmentSeq,
|
|
36212
|
+
maxMemoryId: state2.cachedM0MaxMemoryId,
|
|
36213
|
+
maxMutationId: state2.cachedM0MaxMutationId,
|
|
36214
|
+
maxMemoryMutationId: state2.cachedM0MaxMemoryMutationId,
|
|
36215
|
+
projectDocsHash: state2.cachedM0ProjectDocsHash ?? "",
|
|
36216
|
+
materializedAt: state2.cachedM0MaterializedAt ?? 0,
|
|
36217
|
+
sessionFactsVersion: state2.cachedM0SessionFactsVersion,
|
|
35184
36218
|
upgradeState: cachedUpgradeIdentity.upgradeState,
|
|
35185
36219
|
compartmentRenderEpoch: cachedUpgradeIdentity.compartmentRenderEpoch,
|
|
35186
|
-
systemHash:
|
|
35187
|
-
modelKey:
|
|
35188
|
-
projectIdentity:
|
|
35189
|
-
muralHash:
|
|
36220
|
+
systemHash: state2.cachedM0SystemHash ?? "",
|
|
36221
|
+
modelKey: state2.cachedM0ModelKey ?? "",
|
|
36222
|
+
projectIdentity: state2.cachedM0ProjectIdentity ?? null,
|
|
36223
|
+
muralHash: state2.cachedM0MuralHash ?? null
|
|
35190
36224
|
};
|
|
35191
36225
|
}
|
|
35192
36226
|
function mustMaterialize(args) {
|
|
@@ -35502,26 +36536,26 @@ ${sessionHistory}
|
|
|
35502
36536
|
|
|
35503
36537
|
`).trim();
|
|
35504
36538
|
}
|
|
35505
|
-
function applyMarkersToState(
|
|
35506
|
-
|
|
36539
|
+
function applyMarkersToState(state2, m0Bytes, markers, m1Bytes) {
|
|
36540
|
+
state2.cachedM0Bytes = m0Bytes;
|
|
35507
36541
|
if (m1Bytes)
|
|
35508
|
-
|
|
35509
|
-
|
|
35510
|
-
|
|
35511
|
-
|
|
35512
|
-
|
|
35513
|
-
|
|
35514
|
-
|
|
35515
|
-
|
|
35516
|
-
|
|
35517
|
-
|
|
35518
|
-
|
|
35519
|
-
|
|
35520
|
-
|
|
35521
|
-
|
|
35522
|
-
|
|
35523
|
-
|
|
35524
|
-
|
|
36542
|
+
state2.cachedM1Bytes = m1Bytes;
|
|
36543
|
+
state2.cachedM0ProjectMemoryEpoch = markers.projectMemoryEpoch;
|
|
36544
|
+
state2.cachedM0WorkspaceFingerprint = markers.workspaceFingerprint;
|
|
36545
|
+
state2.cachedM0ProjectUserProfileVersion = markers.projectUserProfileVersion;
|
|
36546
|
+
state2.cachedM0MaxCompartmentSeq = markers.maxCompartmentSeq;
|
|
36547
|
+
state2.cachedM0MaxMemoryId = markers.maxMemoryId;
|
|
36548
|
+
state2.cachedM0MaxMutationId = markers.maxMutationId;
|
|
36549
|
+
state2.cachedM0MaxMemoryMutationId = markers.maxMemoryMutationId;
|
|
36550
|
+
state2.cachedM0ProjectDocsHash = markers.projectDocsHash;
|
|
36551
|
+
state2.cachedM0MaterializedAt = markers.materializedAt;
|
|
36552
|
+
state2.cachedM0SessionFactsVersion = markers.sessionFactsVersion;
|
|
36553
|
+
state2.cachedM0UpgradeState = encodeCachedM0UpgradeIdentity(markers.upgradeState, markers.compartmentRenderEpoch);
|
|
36554
|
+
state2.cachedM0SystemHash = markers.systemHash;
|
|
36555
|
+
state2.cachedM0ModelKey = markers.modelKey;
|
|
36556
|
+
state2.cachedM0ProjectIdentity = markers.projectIdentity;
|
|
36557
|
+
state2.cachedM0MuralHash = markers.muralHash ?? null;
|
|
36558
|
+
state2.snapshotMarkers = markers;
|
|
35525
36559
|
}
|
|
35526
36560
|
function historySliceTokens(m0Text) {
|
|
35527
36561
|
const slice = extractM0Block(m0Text, "session-history");
|
|
@@ -35571,7 +36605,7 @@ function materializeM0(options) {
|
|
|
35571
36605
|
});
|
|
35572
36606
|
docs = readProjectDocsForM0(projectDirectory, options.injectDocs);
|
|
35573
36607
|
snapshotMarkers.projectDocsHash = docs.canonicalHash;
|
|
35574
|
-
compartments = readM0Compartments(options.db, options.sessionId);
|
|
36608
|
+
compartments = options.compactionOff ? [] : readM0Compartments(options.db, options.sessionId);
|
|
35575
36609
|
facts = [];
|
|
35576
36610
|
memories = projectPath ? workspace.isWorkspaced ? getMemoriesByProjects(options.db, workspace.expandedIdentities, ["active", "permanent"], foldMaterializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(options.db, projectPath, ["active", "permanent"], foldMaterializedAt) : [];
|
|
35577
36611
|
userMemories = safeGetActiveUserMemories(options.db);
|
|
@@ -35936,39 +36970,39 @@ function markersFromCachedRow(row) {
|
|
|
35936
36970
|
muralHash: row.cached_m0_mural_hash ?? null
|
|
35937
36971
|
};
|
|
35938
36972
|
}
|
|
35939
|
-
function cachedRowMatchesState(row,
|
|
35940
|
-
return bufferEqualsNullable(row.cached_m0_bytes,
|
|
36973
|
+
function cachedRowMatchesState(row, state2) {
|
|
36974
|
+
return bufferEqualsNullable(row.cached_m0_bytes, state2.cachedM0Bytes) && (row.cached_m0_mural_data_url ?? null) === (state2.cachedM0MuralDataUrl ?? null) && (row.cached_m0_mural_hash ?? null) === (state2.cachedM0MuralHash ?? null) && row.cached_m0_project_memory_epoch === state2.cachedM0ProjectMemoryEpoch && (row.cached_m0_workspace_fingerprint ?? null) === (state2.cachedM0WorkspaceFingerprint ?? null) && row.cached_m0_project_user_profile_version === state2.cachedM0ProjectUserProfileVersion && row.cached_m0_max_compartment_seq === state2.cachedM0MaxCompartmentSeq && row.cached_m0_max_memory_id === state2.cachedM0MaxMemoryId && row.cached_m0_max_mutation_id === state2.cachedM0MaxMutationId && row.cached_m0_max_memory_mutation_id === state2.cachedM0MaxMemoryMutationId && row.cached_m0_materialized_at === state2.cachedM0MaterializedAt && row.cached_m0_session_facts_version === state2.cachedM0SessionFactsVersion && (row.cached_m0_upgrade_state ?? null) === (state2.cachedM0UpgradeState ?? null) && (row.cached_m0_system_hash ?? "") === (state2.cachedM0SystemHash ?? "") && (row.cached_m0_model_key ?? "") === (state2.cachedM0ModelKey ?? "") && (row.cached_m0_project_identity ?? null) === (state2.cachedM0ProjectIdentity ?? null);
|
|
35941
36975
|
}
|
|
35942
|
-
function applyCachedRowToState(
|
|
36976
|
+
function applyCachedRowToState(state2, row) {
|
|
35943
36977
|
const markers = markersFromCachedRow(row);
|
|
35944
36978
|
if (!row.cached_m0_bytes || !row.cached_m1_bytes || !markers) {
|
|
35945
|
-
throw new RenderM1InvalidMarkersError(
|
|
35946
|
-
}
|
|
35947
|
-
|
|
35948
|
-
|
|
35949
|
-
|
|
35950
|
-
|
|
35951
|
-
|
|
35952
|
-
|
|
35953
|
-
|
|
35954
|
-
|
|
35955
|
-
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
|
|
35959
|
-
|
|
35960
|
-
|
|
35961
|
-
|
|
35962
|
-
|
|
35963
|
-
|
|
35964
|
-
|
|
35965
|
-
|
|
35966
|
-
}
|
|
35967
|
-
function replayCachedM1(
|
|
35968
|
-
if (!
|
|
35969
|
-
throw new RenderM1InvalidMarkersError(
|
|
35970
|
-
}
|
|
35971
|
-
return decodeM0Bytes(
|
|
36979
|
+
throw new RenderM1InvalidMarkersError(state2.sessionId);
|
|
36980
|
+
}
|
|
36981
|
+
state2.cachedM0Bytes = toBuffer(row.cached_m0_bytes);
|
|
36982
|
+
state2.cachedM0MuralDataUrl = row.cached_m0_mural_data_url ?? null;
|
|
36983
|
+
state2.cachedM0MuralHash = row.cached_m0_mural_hash ?? null;
|
|
36984
|
+
state2.cachedM1Bytes = toBuffer(row.cached_m1_bytes);
|
|
36985
|
+
state2.cachedM0ProjectMemoryEpoch = markers.projectMemoryEpoch;
|
|
36986
|
+
state2.cachedM0WorkspaceFingerprint = markers.workspaceFingerprint;
|
|
36987
|
+
state2.cachedM0ProjectUserProfileVersion = markers.projectUserProfileVersion;
|
|
36988
|
+
state2.cachedM0MaxCompartmentSeq = markers.maxCompartmentSeq;
|
|
36989
|
+
state2.cachedM0MaxMemoryId = markers.maxMemoryId;
|
|
36990
|
+
state2.cachedM0MaxMutationId = markers.maxMutationId;
|
|
36991
|
+
state2.cachedM0MaxMemoryMutationId = markers.maxMemoryMutationId;
|
|
36992
|
+
state2.cachedM0ProjectDocsHash = markers.projectDocsHash;
|
|
36993
|
+
state2.cachedM0MaterializedAt = markers.materializedAt;
|
|
36994
|
+
state2.cachedM0SessionFactsVersion = markers.sessionFactsVersion;
|
|
36995
|
+
state2.cachedM0UpgradeState = encodeCachedM0UpgradeIdentity(markers.upgradeState, markers.compartmentRenderEpoch);
|
|
36996
|
+
state2.cachedM0SystemHash = markers.systemHash;
|
|
36997
|
+
state2.cachedM0ModelKey = markers.modelKey;
|
|
36998
|
+
state2.cachedM0ProjectIdentity = markers.projectIdentity;
|
|
36999
|
+
state2.snapshotMarkers = markers;
|
|
37000
|
+
}
|
|
37001
|
+
function replayCachedM1(state2) {
|
|
37002
|
+
if (!state2.cachedM1Bytes) {
|
|
37003
|
+
throw new RenderM1InvalidMarkersError(state2.sessionId);
|
|
37004
|
+
}
|
|
37005
|
+
return decodeM0Bytes(state2.cachedM1Bytes) ?? M1_EMPTY_PLACEHOLDER;
|
|
35972
37006
|
}
|
|
35973
37007
|
function softRefreshCachedM1(options) {
|
|
35974
37008
|
options.db.exec("BEGIN IMMEDIATE");
|
|
@@ -36052,7 +37086,7 @@ function renderFreshM0NonPersisted(options) {
|
|
|
36052
37086
|
const docs = readProjectDocsForM0(projectDirectory ?? "", options.injectDocs);
|
|
36053
37087
|
snapshotMarkers.projectDocsHash = docs.canonicalHash;
|
|
36054
37088
|
snapshotMarkers.materializedAt = options.state.cachedM0MaterializedAt ?? 0;
|
|
36055
|
-
const compartments = withCompartmentDates(options.sessionId, readM0Compartments(options.db, options.sessionId), options.temporalAwareness);
|
|
37089
|
+
const compartments = options.compactionOff ? [] : withCompartmentDates(options.sessionId, readM0Compartments(options.db, options.sessionId), options.temporalAwareness);
|
|
36056
37090
|
const memories = projectPath ? workspace.isWorkspaced ? getMemoriesByProjects(options.db, workspace.expandedIdentities, ["active", "permanent"], snapshotMarkers.materializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(options.db, projectPath, ["active", "permanent"], snapshotMarkers.materializedAt) : [];
|
|
36057
37091
|
const userMemories = safeGetActiveUserMemories(options.db);
|
|
36058
37092
|
const memoryBudget = options.memoryInjectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
@@ -36130,7 +37164,7 @@ function injectM0M1(options) {
|
|
|
36130
37164
|
m0Bytes: options.state.cachedM0Bytes,
|
|
36131
37165
|
m1Text: null
|
|
36132
37166
|
};
|
|
36133
|
-
if (options.state.isSubagent)
|
|
37167
|
+
if (options.state.isSubagent && !options.compactionOff)
|
|
36134
37168
|
return skipped;
|
|
36135
37169
|
const decision = mustMaterialize({
|
|
36136
37170
|
db: options.db,
|
|
@@ -36259,14 +37293,14 @@ function onNoteTrigger(db, sessionId, trigger) {
|
|
|
36259
37293
|
sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
|
|
36260
37294
|
}
|
|
36261
37295
|
function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity, noteReadStillVisible) {
|
|
36262
|
-
const
|
|
36263
|
-
if (!
|
|
37296
|
+
const state2 = getPersistedNoteNudge(db, sessionId);
|
|
37297
|
+
if (!state2.triggerPending)
|
|
36264
37298
|
return null;
|
|
36265
|
-
if (!
|
|
37299
|
+
if (!state2.triggerMessageId && currentUserMessageId) {
|
|
36266
37300
|
setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
|
|
36267
|
-
|
|
37301
|
+
state2.triggerMessageId = currentUserMessageId;
|
|
36268
37302
|
}
|
|
36269
|
-
if (
|
|
37303
|
+
if (state2.triggerMessageId && currentUserMessageId && state2.triggerMessageId === currentUserMessageId) {
|
|
36270
37304
|
sessionLog(sessionId, `note-nudge: deferring — current user message ${currentUserMessageId} is same as trigger-time message`);
|
|
36271
37305
|
return null;
|
|
36272
37306
|
}
|
|
@@ -36468,29 +37502,29 @@ function callIdFromPart(part) {
|
|
|
36468
37502
|
const direct = firstStringField(part, ["callID", "callId", "toolCallId", "tool_call_id", "id"]);
|
|
36469
37503
|
if (direct)
|
|
36470
37504
|
return direct;
|
|
36471
|
-
const
|
|
36472
|
-
return
|
|
37505
|
+
const state2 = isRecord(part.state) ? part.state : null;
|
|
37506
|
+
return state2 ? firstStringField(state2, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
|
|
36473
37507
|
}
|
|
36474
37508
|
function toolSignalFromPart(part) {
|
|
36475
37509
|
if (!isRecord(part))
|
|
36476
37510
|
return null;
|
|
36477
37511
|
const type = partType(part);
|
|
36478
|
-
const
|
|
37512
|
+
const state2 = isRecord(part.state) ? part.state : null;
|
|
36479
37513
|
const callId = callIdFromPart(part);
|
|
36480
37514
|
if (!callId && type !== "tool")
|
|
36481
37515
|
return null;
|
|
36482
37516
|
if (type === "tool") {
|
|
36483
|
-
const hasInput =
|
|
36484
|
-
const outputKey =
|
|
37517
|
+
const hasInput = state2 !== null && hasOwn(state2, "input");
|
|
37518
|
+
const outputKey = state2 ? hasOwn(state2, "output") ? "output" : hasOwn(state2, "error") ? "error" : hasOwn(state2, "result") ? "result" : null : null;
|
|
36485
37519
|
const hasOutput = outputKey !== null;
|
|
36486
|
-
const outputValue = outputKey &&
|
|
37520
|
+
const outputValue = outputKey && state2 ? state2[outputKey] : undefined;
|
|
36487
37521
|
const providerExecuted = part.providerExecuted === true;
|
|
36488
37522
|
const openInvocation = !providerExecuted && !hasOutput;
|
|
36489
37523
|
return {
|
|
36490
37524
|
callId,
|
|
36491
37525
|
hasInput: hasInput || openInvocation,
|
|
36492
37526
|
hasOutput,
|
|
36493
|
-
inputText: hasInput &&
|
|
37527
|
+
inputText: hasInput && state2 ? stringValue(state2.input) : "",
|
|
36494
37528
|
outputText: hasOutput ? stringValue(outputValue) : ""
|
|
36495
37529
|
};
|
|
36496
37530
|
}
|
|
@@ -36718,24 +37752,27 @@ function tokenForMessage(message, options) {
|
|
|
36718
37752
|
function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
36719
37753
|
const ordered = [...messages].sort((a, b) => a.ordinal - b.ordinal);
|
|
36720
37754
|
const sliceCount = ordered.length;
|
|
36721
|
-
const
|
|
36722
|
-
const
|
|
37755
|
+
const firstOrdinal = ordered.length > 0 ? ordered[0].ordinal : 1;
|
|
37756
|
+
const terminalOrdinal = ordered.length > 0 ? ordered[ordered.length - 1].ordinal : 0;
|
|
37757
|
+
const rawMessageCount = Math.max(sliceCount, terminalOrdinal, options.absoluteMessageCount ?? sliceCount);
|
|
37758
|
+
const ordinalSpan = terminalOrdinal >= firstOrdinal ? terminalOrdinal - firstOrdinal + 1 : 0;
|
|
36723
37759
|
const tokensByOrdinal = new Map;
|
|
36724
37760
|
const idsByOrdinal = new Map;
|
|
36725
|
-
const prefix = new Array(
|
|
37761
|
+
const prefix = new Array(ordinalSpan + 1).fill(0);
|
|
36726
37762
|
for (const message of ordered) {
|
|
36727
37763
|
const stored = options.storedTotalForMessage?.(message);
|
|
36728
37764
|
const total = stored !== undefined && stored !== null ? stored : tokenForMessage(message, options).total;
|
|
36729
37765
|
tokensByOrdinal.set(message.ordinal, total);
|
|
36730
37766
|
idsByOrdinal.set(message.ordinal, message.id);
|
|
36731
|
-
|
|
36732
|
-
|
|
37767
|
+
const relative = message.ordinal - firstOrdinal + 1;
|
|
37768
|
+
if (relative >= 1 && relative <= ordinalSpan) {
|
|
37769
|
+
prefix[relative] = total;
|
|
36733
37770
|
}
|
|
36734
37771
|
}
|
|
36735
|
-
for (let k = 1;k <=
|
|
37772
|
+
for (let k = 1;k <= ordinalSpan; k += 1) {
|
|
36736
37773
|
prefix[k] += prefix[k - 1];
|
|
36737
37774
|
}
|
|
36738
|
-
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(
|
|
37775
|
+
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(ordinalSpan, ordinal - firstOrdinal));
|
|
36739
37776
|
return {
|
|
36740
37777
|
sessionId,
|
|
36741
37778
|
providerShapeVersion: options.providerShapeVersion,
|
|
@@ -36747,27 +37784,27 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
36747
37784
|
return idsByOrdinal.get(ordinal) ?? null;
|
|
36748
37785
|
},
|
|
36749
37786
|
suffixTokensFromOrdinal(ordinal) {
|
|
36750
|
-
if (ordinal <=
|
|
36751
|
-
return prefix[
|
|
36752
|
-
if (ordinal >
|
|
37787
|
+
if (ordinal <= firstOrdinal)
|
|
37788
|
+
return prefix[ordinalSpan];
|
|
37789
|
+
if (ordinal > terminalOrdinal)
|
|
36753
37790
|
return 0;
|
|
36754
|
-
return prefix[
|
|
37791
|
+
return prefix[ordinalSpan] - prefix[ordinalToIndex(ordinal)];
|
|
36755
37792
|
},
|
|
36756
37793
|
rangeTokens(startInclusive, endExclusive) {
|
|
36757
|
-
const start = Math.max(
|
|
36758
|
-
const end = Math.max(start, Math.min(
|
|
36759
|
-
return prefix[end -
|
|
37794
|
+
const start = Math.max(firstOrdinal, startInclusive);
|
|
37795
|
+
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
37796
|
+
return prefix[end - firstOrdinal] - prefix[start - firstOrdinal];
|
|
36760
37797
|
},
|
|
36761
37798
|
findSuffixStartForTokens(tokens) {
|
|
36762
37799
|
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
36763
|
-
return
|
|
37800
|
+
return terminalOrdinal + 1;
|
|
36764
37801
|
const target = Math.max(0, Math.floor(tokens));
|
|
36765
|
-
const total = prefix[
|
|
37802
|
+
const total = prefix[ordinalSpan];
|
|
36766
37803
|
if (total < target)
|
|
36767
|
-
return
|
|
37804
|
+
return firstOrdinal;
|
|
36768
37805
|
const cut = total - target;
|
|
36769
37806
|
let lo = 0;
|
|
36770
|
-
let hi =
|
|
37807
|
+
let hi = ordinalSpan;
|
|
36771
37808
|
let best = 0;
|
|
36772
37809
|
while (lo <= hi) {
|
|
36773
37810
|
const mid = lo + hi >> 1;
|
|
@@ -36778,29 +37815,31 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
36778
37815
|
hi = mid - 1;
|
|
36779
37816
|
}
|
|
36780
37817
|
}
|
|
36781
|
-
return
|
|
37818
|
+
return firstOrdinal + best;
|
|
36782
37819
|
},
|
|
36783
37820
|
findHeadEndForCap(startInclusive, endExclusive, capTokens) {
|
|
36784
|
-
const start = Math.max(
|
|
36785
|
-
const end = Math.max(start, Math.min(
|
|
37821
|
+
const start = Math.max(firstOrdinal, Math.min(terminalOrdinal + 1, startInclusive));
|
|
37822
|
+
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
36786
37823
|
if (!Number.isFinite(capTokens) || capTokens <= 0)
|
|
36787
37824
|
return start;
|
|
36788
|
-
const
|
|
36789
|
-
const
|
|
36790
|
-
|
|
36791
|
-
let
|
|
36792
|
-
let
|
|
37825
|
+
const startIndex = start - firstOrdinal;
|
|
37826
|
+
const endIndex = end - firstOrdinal;
|
|
37827
|
+
const cut = prefix[startIndex] + Math.floor(capTokens);
|
|
37828
|
+
let lo = startIndex + 1;
|
|
37829
|
+
let hi = endIndex;
|
|
37830
|
+
let bestEndIndex = startIndex;
|
|
36793
37831
|
while (lo <= hi) {
|
|
36794
37832
|
const mid = lo + hi >> 1;
|
|
36795
37833
|
if (prefix[mid] <= cut) {
|
|
36796
|
-
|
|
37834
|
+
bestEndIndex = mid;
|
|
36797
37835
|
lo = mid + 1;
|
|
36798
37836
|
} else {
|
|
36799
37837
|
hi = mid - 1;
|
|
36800
37838
|
}
|
|
36801
37839
|
}
|
|
37840
|
+
let bestEnd = firstOrdinal + bestEndIndex;
|
|
36802
37841
|
if (bestEnd === start && start < end)
|
|
36803
|
-
|
|
37842
|
+
bestEnd = start + 1;
|
|
36804
37843
|
return Math.min(bestEnd, end);
|
|
36805
37844
|
}
|
|
36806
37845
|
};
|
|
@@ -37117,7 +38156,8 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
37117
38156
|
if (ctx.migrationFloorActive)
|
|
37118
38157
|
runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
|
|
37119
38158
|
let protectedTailStart = Math.max(boundary, runtimeFloor);
|
|
37120
|
-
|
|
38159
|
+
const forceMaterializationPercentage = escalationBands(ctx.executeThresholdPercentage).forceMaterializationPercentage;
|
|
38160
|
+
if (!ctx.emergencyTailScale && usagePercentage < forceMaterializationPercentage) {
|
|
37121
38161
|
let lastMeaningfulUserOrdinal = 0;
|
|
37122
38162
|
for (let i = messages.length - 1;i >= 0; i--) {
|
|
37123
38163
|
const message = messages[i];
|
|
@@ -37358,7 +38398,8 @@ function getRawHistoryEligibility(db, sessionId) {
|
|
|
37358
38398
|
function hasRunnableCompartmentWindow(snapshot) {
|
|
37359
38399
|
if (snapshot.offset >= snapshot.protectedTailStart)
|
|
37360
38400
|
return false;
|
|
37361
|
-
|
|
38401
|
+
const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
|
|
38402
|
+
if (snapshot.usagePercentage >= forceMaterializationPercentage || snapshot.emergencyTailScale) {
|
|
37362
38403
|
return snapshot.trueRawEligibleTokens >= deriveMinForceEligibleTokens(snapshot.N) || snapshot.eligibleEndOrdinal > snapshot.offset;
|
|
37363
38404
|
}
|
|
37364
38405
|
return snapshot.eligibleEndOrdinal > snapshot.offset;
|
|
@@ -37421,8 +38462,10 @@ function validateBoundarySnapshot(args) {
|
|
|
37421
38462
|
return { ok: true };
|
|
37422
38463
|
}
|
|
37423
38464
|
function recordHighPressureNoEligibleHead(db, snapshot) {
|
|
37424
|
-
|
|
38465
|
+
const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
|
|
38466
|
+
if (snapshot.usagePercentage < forceMaterializationPercentage && !snapshot.emergencyTailScale) {
|
|
37425
38467
|
return 0;
|
|
38468
|
+
}
|
|
37426
38469
|
return recordProtectedTailNoEligibleHead(db, snapshot.sessionId);
|
|
37427
38470
|
}
|
|
37428
38471
|
|
|
@@ -39453,6 +40496,7 @@ ${chunkText}`,
|
|
|
39453
40496
|
retainDrainReservationForRetryThrottle = true;
|
|
39454
40497
|
const validatedPass = await runValidatedHistorianPass({
|
|
39455
40498
|
client,
|
|
40499
|
+
db,
|
|
39456
40500
|
parentSessionId: sessionId,
|
|
39457
40501
|
sessionDirectory,
|
|
39458
40502
|
prompt,
|
|
@@ -39913,6 +40957,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
|
|
|
39913
40957
|
emitProgress(`Running historian (pass ${passCount + 1})…`);
|
|
39914
40958
|
const validatedPass = await runValidatedHistorianPass({
|
|
39915
40959
|
client,
|
|
40960
|
+
db,
|
|
39916
40961
|
parentSessionId: sessionId,
|
|
39917
40962
|
sessionDirectory,
|
|
39918
40963
|
prompt,
|
|
@@ -40332,6 +41377,7 @@ ${chunk.text}`,
|
|
|
40332
41377
|
Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${chunk.startIndex}-${chunk.endIndex}.`, notifParams());
|
|
40333
41378
|
const validatedPass = await runValidatedHistorianPass({
|
|
40334
41379
|
client,
|
|
41380
|
+
db,
|
|
40335
41381
|
parentSessionId: sessionId,
|
|
40336
41382
|
sessionDirectory,
|
|
40337
41383
|
prompt,
|
|
@@ -40583,7 +41629,7 @@ async function executeContextRecomp(deps, options = {}) {
|
|
|
40583
41629
|
}
|
|
40584
41630
|
|
|
40585
41631
|
// src/hooks/magic-context/lkg-slot.ts
|
|
40586
|
-
import { createHash as
|
|
41632
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
40587
41633
|
var LKG_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
40588
41634
|
var LKG_SINGLE_SLOT_BYTES = 24 * 1024 * 1024;
|
|
40589
41635
|
var LKG_METADATA_BYTES = 256;
|
|
@@ -40594,7 +41640,7 @@ function slotBytes(slot) {
|
|
|
40594
41640
|
return 2 * slot.jsonPrefix.length + digestBytes + LKG_METADATA_BYTES;
|
|
40595
41641
|
}
|
|
40596
41642
|
function lkgContentDigest(message) {
|
|
40597
|
-
const hash2 =
|
|
41643
|
+
const hash2 = createHash15("sha256");
|
|
40598
41644
|
const seen = new WeakSet;
|
|
40599
41645
|
const visit = (value) => {
|
|
40600
41646
|
if (value === null) {
|
|
@@ -40960,4 +42006,4 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
|
|
|
40960
42006
|
}
|
|
40961
42007
|
}
|
|
40962
42008
|
|
|
40963
|
-
export { withContentLanguageDirective, buildPrimaryLanguageDirective, exports_external, DEFAULT_PROTECTED_TAGS, parseCron, nextOccurrence, nextDueAtMs, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, DEFAULT_HISTORIAN_TIMEOUT_MS, DEFAULT_LOCAL_EMBEDDING_MODEL, MagicContextConfigSchema, getOpenCodeDbPath, ProjectIdentityError, resolveProjectIdentityStrict, takeDubiousOwnershipProjectIdentityWarning, resolveProjectIdentity, resolveProjectIdentityForSession, normalizeStoredProjectPath, storedPathBelongsToIdentity, recordSessionProjectIdentity,
|
|
42009
|
+
export { withContentLanguageDirective, buildPrimaryLanguageDirective, exports_external, DEFAULT_PROTECTED_TAGS, parseCron, nextOccurrence, nextDueAtMs, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, DEFAULT_HISTORIAN_TIMEOUT_MS, DEFAULT_LOCAL_EMBEDDING_MODEL, MagicContextConfigSchema, getOpenCodeDbPath, removeMcOwnedCompactionMarkers, ProjectIdentityError, resolveProjectIdentityStrict, takeDubiousOwnershipProjectIdentityWarning, resolveProjectIdentity, resolveProjectIdentityForSession, normalizeStoredProjectPath, storedPathBelongsToIdentity, recordSessionProjectIdentity, beginBootQuietPeriod, bootQuietRemainingMs, scheduleAfterBootQuiet, rpcPortDir, rpcPortFilePath, isPidAlive, parseRpcPortFile, registerModuleNoteEvaluationBridge, getModuleNoteEvaluationBridge, getContextStoreUuid, ensureContextStoreUuid, getAuthorityManagedMarker, reconcileAuthorityProject, checksumAuthoritySeedRows, prepareAuthority, drainAuthority, getMirrorCursor, applyMirrorPage, pullMemoryMirrorOnce, V2_MEMORY_CATEGORIES, CATEGORY_PRIORITY, resolveWorkspaceShareCategories, resolveWorkspaceIdentitySet, expandWorkspaceIdentitySetWithAliases, resolveStoredPathWorkspaceIdentity, storedPathBelongsToWorkspace, sourceNameForMemory, computeWorkspaceEpochFingerprint, recordToolDefinition, getMeasuredToolDefinitionTokens, getSchemaFenceRejection, getMigrationOnOpenRefusal, LATEST_SUPPORTED_VERSION, getDatabasePath, getPersistedSchemaVersion, setSqlitePragmaConfig, runSqliteOptimize, openDatabase, openDatabaseAsync, isDatabasePersisted, getDatabasePersistenceError, COMPARTMENT_LEASE_RENEWAL_MS, acquireCompartmentLease, renewCompartmentLease, releaseCompartmentLease, getCompartments, getLastCompartmentEndMessage, getLastCompartmentEndMessageId, clearRecompStaging, recordEmbeddingMeasurement, getMaxM0MutationId, queueMemoryMutation, getMemoryMutationsForRenderByProjects, getMaxMemoryMutationIdForProjects, MAX_EXECUTE_THRESHOLD, escalationBands, isEmergencyRecoveryArmed, isProviderOverflowReconfirmed, loadPersistedUsage, loadProtectedTailMeta, resetProtectedTailNoEligibleHead, getWrapupInProgressState, acquireWrapupInProgress, updateWrapupInProgress, releaseWrapupInProgress, resolveCompactionModeRecord, getCompactionModeRecord, setCompactionModeRecord, getPersistedReasoningWatermark, setPersistedReasoningWatermark, clearPersistedReasoningWatermark, getEmergencyInputSample, setEmergencyDropSample, clearEmergencyDropSample, getLastNudgeUndropped, setLastNudgeUndropped, getLastNudgeLevel, setLastNudgeLevel, resetLastNudgeCycle, resetLastNudgeCycleIfTailShrank, getChannel2NudgeState, getChannel2NudgeClaim, setChannel2NudgeState, casChannel2NudgeState, claimChannel2NudgeState, casChannel2NudgeClaim, getPersistedNoteNudge, getNoteNudgeAnchors, getAutoSearchHintDecisions, appendAutoSearchHintDecision, pruneNoteNudgeAnchors, pruneAutoSearchHintDecisions, removeNoteNudgeAnchorByMessageId, removeAutoSearchHintDecisionByMessageId, getPersistedTodoPermissionDenied, setPersistedTodoPermissionDenied, getPersistedTodoSyntheticAnchor, setPersistedTodoSyntheticAnchor, clearPersistedTodoSyntheticAnchor, setNoteLastReadAt, getHistorianFailureState, clearHistorianFailureState, getOverflowState, recordOverflowDetected, recordDetectedContextLimit, clearEmergencyRecovery, clearDetectedContextLimit, getPersistedCompactionMarkerState, setPersistedCompactionMarkerState, getStrippedPlaceholderIds, applyStrippedPlaceholderDelta, removeStrippedPlaceholderId, getStaleReduceStrippedIds, addStaleReduceStrippedIds, getProcessedImageStrippedIds, addProcessedImageStrippedIds, getPendingCompactionMarkerState, clearPendingCompactionMarkerStateIf, peekDeferredExecutePending, setDeferredExecutePendingIfAbsent, clearDeferredExecutePendingIfMatches, getSessionsWithPendingMarker, setSessionWorkMetrics, getNotes, addNote, getSessionNotes, getPendingSmartNotes, getReadySmartNotes, updateNote, dismissNote, markNoteReady, markNoteChecked, queuePendingOp, getPendingOps, clearPendingOps, removePendingOp, PRIMER_CANDIDATE_TTL_MS, PRIMER_CANDIDATE_MAX_AGE_MS, primerOccurrenceKey, primerOccurrenceUtcDay, updatePrimerCandidateEmbedding, getPrimerCandidatesByIds, getPrimerCandidatesForPromotion, countPrimerCandidatesForProject, getActivePrimers, createPrimer, updatePrimerSupport, updatePrimerAnswer, GLOBAL_USER_PROFILE_PROJECT_PATH, getProjectState, bumpProjectMemoryEpoch, bumpProjectUserProfileVersion, saveSourceContent, replaceSourceContent, getSourceContents, getActiveTagTokenAggregate, AGE_RECLAIM_MIN_TOKENS, getOldestActiveUnprotectedToolTags, getActiveToolTagsForAgeReclaim, getTriggerTagTokenUpperBound, getActiveTagTokenTotalsByMessage, tagTokenCountIsNull, backfillTagTokenCounts, insertTag, updateTagStatus, updateTagDropMode, updateCavemanDepth, updateTagMessageId, deleteTagsByMessageId, getMaxTagNumberBySession, getTagNumberByMessageId, deriveTagLoadFloor, getTagsBySession, getActiveTagsBySession, getDroppedTagsBySession, getTagsByNumbers, getMaxDroppedTagNumber, getToolTagNumberByOwner, getNullOwnerToolTag, adoptNullOwnerToolTag, getCandidateToolOwners, pickNearestPriorOwner, createChildSessionWithFence, detectOverflow, promptSyncWithValidatedOutputRetry, normalizeSDKResponse, extractLatestAssistantText, hasLengthCappedOutput, setKeepSubagents, shouldKeepSubagents, recordChildInvocation, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_EDITOR_SYSTEM_PROMPT, COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, Priority, AdmissionClass, StaleRouteHandleError, SocketClosedError, SocketTimeoutError, SubcClient, isConsumerReconnectTransient, getCompartmentEvents, loadCompartmentChunkEmbeddingsForSearch, cosineSimilarity, SYNAPSE_DEFAULT_MODEL, SynapseEmbeddingProvider, computeNormalizedHash, saveCommitEmbedding, loadProjectCommitEmbeddings, loadUnembeddedCommits, countEmbeddedCommits, upsertCommits, getLatestIndexedCommitTimeMs, enforceProjectCap, GIT_SWEEP_LEASE_RENEWAL_MS, acquireGitSweepLease, renewGitSweepLease, markGitSweepSuccessAndRelease, parkGitSweepNonIndexable, releaseGitSweepLease, saveEmbeddingIfHashMatches, getProjectEmbeddings, peekProjectEmbeddings, invalidateProject, invalidateMemory, markProjectLoadUntrusted, contentSha256, sweepStaleEmbeddingIdentitiesForProject, registerProjectEmbedding, registerProjectShadowEmbedding, getShadowEmbeddingMeasurementCohort, getPrimaryEmbeddingMeasurementCohort, embedShadowTextForProject, enqueueShadowEmbeddingItems, registerProjectInObservationMode, getProjectEmbeddingSnapshot, embedTextForProject, embedBatchForProject, embedItemsForProject, embedUnembeddedMemoriesForProject, drainCommitBacklogForProject, embedSessionCompartmentChunks, getEmbeddingCoverageStatus, isEmbeddingEnabled, embedText, ensureMemoryEmbeddings, computeCueContentHash, getMuralCueState, memoryNeedsCue, setMuralCue, hasMemoryShareableColumn, hasMemoryClassifiedAtColumn, getUnclassifiedMemoryIds, ModuleMemoryAuthorityError, insertMemory, insertMemoryIdempotent, getMemoryByHash, getMemoriesByProject, buildWorkspaceMemorySqlFilter, getMemoriesByProjects, getMaxMemoryIdForProjects, readNewMemoriesForM1Union, getMemoryById, getMemoriesByIds, updateMemorySeenCount, updateMemoryRetrievalCount, setMemoryClassification, supersededMemory, mergeMemoryStats, archiveMemory, getMemoryCountsByStatus, sanitizeFtsQuery, searchMemoriesFTS, searchMemoriesFTSUnion, recordMemoryMapping, recordMemoryVerifications, getUnmappedMemoryIds, clearMemoryVerifications, getMemoryVerifications, resolveGitTopLevel, readGitHead, readGitChangedFilesSince, readGitFileChangeTimesSince, verificationFileExists, normalizeVerificationFiles, USER_MEMORY_CANDIDATE_TTL_MS, insertUserMemoryCandidates, getUserMemoryCandidates, deleteUserMemoryCandidates, pruneExpiredUserMemoryCandidates, insertUserMemory, getActiveUserMemories, updateUserMemoryContent, dismissUserMemory, MARKER_SUMMARY_TEXT, applyDeferredCompactionMarker, removeCompactionMarkerForSession, checkCompactionMarkerConsistency, getMural, BoundedSessionMap, extractM0Block, formatDate, injectTemporalMarkers, clearInjectionCache, getVisibleMemoryIds, prepareCompartmentInjection, renderCompartmentInjection, mustMaterialize, trimMemoriesToBudgetV2, renderMemoryBlockV2, injectM0M1, onNoteTrigger, peekNoteNudgeText, markNoteNudgeDelivered, clearNoteNudgeTriggerAndCooldown, resetNoteNudgeCooldownOnly, clearNoteNudgeTriggerOnly, estimateTrueRawMessageTokens, invalidateTrueRawTokenCache, RECOVERY_NO_HEAD_LIMIT, resolveOpenCodeProtectedTailBoundary, resolveWrapupProtectedTailBoundary, getRawHistoryEligibility, hasRunnableCompartmentWindow, recordHighPressureNoEligibleHead, runCompartmentAgent, snapRangeToCompartments, lkgContentDigest, captureSlot, getSlot, dropSlot, noteEntry, getActiveCompartmentRun, markActiveCompartmentRunPublished, registerActiveCompartmentRun, startCompartmentAgent, isRecompFailure, isRecompSkip, isRecompComplete, extractRecompReason, contextualizeUpgradeReason, setRecompStarting, setRecompNote, setRecompTerminal, runManagedRecomp, runManagedUpgrade };
|