@cortexkit/opencode-magic-context 0.33.1 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -3
- 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/migrate-experimental.d.ts +2 -1
- package/dist/config/migrate-experimental.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +9 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +90 -17
- 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-n48c783v.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 +57 -0
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts +1 -0
- 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 +1 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +1 -0
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +2 -0
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts +4 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts +19 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts +28 -0
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +7 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts +1 -0
- 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/storage-git-commit-embeddings.d.ts.map +1 -1
- 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.map +1 -1
- 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.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +23 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts +1 -0
- 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/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.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/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +37 -2
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +38 -4
- 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 +2 -2
- 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/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 -0
- package/dist/hooks/magic-context/cache-busting-signals.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 +3 -0
- package/dist/hooks/magic-context/command-handler.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-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.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/execute-status.d.ts +5 -1
- package/dist/hooks/magic-context/execute-status.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/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 +14 -6
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +10 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/live-session-state.d.ts +3 -0
- package/dist/hooks/magic-context/live-session-state.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/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 +31 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +29 -2
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-56yjaeb2.js → index-2y0ybv5x.js} +11 -1
- package/dist/{index-ey1bc14z.js → index-b563sga5.js} +7 -264
- package/dist/{index-y1c9tcm3.js → index-cn960ffd.js} +101 -20
- package/dist/{index-xyaekybw.js → index-dtas91s5.js} +6 -4
- package/dist/index-fcyvznch.js +279 -0
- package/dist/{index-63dq2g4d.js → index-ry8rh8cv.js} +138 -24
- package/dist/{index-c2gahrks.js → index-x8spt5wy.js} +1682 -893
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1677 -723
- package/dist/plugin/dream-timer.d.ts +4 -2
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +12 -1
- 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-2yb2hfh2.js → recomp-orchestrator-k72ejkkh.js} +6 -4
- 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 +51 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +19 -0
- package/dist/shared/rpc-utils.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/shared/tui-runtime-specifiers.d.ts +20 -0
- package/dist/shared/tui-runtime-specifiers.d.ts.map +1 -0
- package/dist/{storage-meta-session-td6bwtky.js → storage-meta-session-xt41zbw4.js} +2 -1
- package/dist/{task-config-5nx3qfz8.js → task-config-gbp7r23g.js} +1 -1
- 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 +6 -1
- 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 +936 -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.test.ts +41 -0
- package/src/config/migrate-experimental.ts +185 -0
- package/src/config/project-security.test.ts +388 -0
- package/src/config/project-security.ts +611 -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 +359 -0
- package/src/config/schema/magic-context.ts +991 -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 +151 -0
- package/src/shared/conflict-detector.test.ts +121 -0
- package/src/shared/conflict-detector.ts +50 -3
- 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 +55 -1
- package/src/shared/rpc-utils.test.ts +157 -0
- package/src/shared/rpc-utils.ts +137 -0
- package/src/shared/storage-permissions.ts +20 -0
- package/src/shared/tui-runtime-specifiers.ts +32 -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 +189 -91
- package/src/tui/tui-compiled-runtime-imports.test.ts +151 -0
- 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 +334 -169
- package/dist/send-session-notification-tghq9gnh.js +0 -11
- /package/dist/{index-v7fmc1s2.js → index-ygcng9t8.js} +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deriveTriggerBudget,
|
|
3
|
-
modelSupportsVision
|
|
4
|
-
|
|
3
|
+
modelSupportsVision,
|
|
4
|
+
piModelRefToCanonical,
|
|
5
|
+
shouldEnforcePrivateStoragePermissions
|
|
6
|
+
} from "./index-ry8rh8cv.js";
|
|
5
7
|
import {
|
|
6
8
|
parseProviderModel
|
|
7
9
|
} from "./index-td9depx5.js";
|
|
@@ -9,18 +11,15 @@ import {
|
|
|
9
11
|
describeError,
|
|
10
12
|
getErrorMessage,
|
|
11
13
|
sendIgnoredMessage
|
|
12
|
-
} from "./index-
|
|
14
|
+
} from "./index-cn960ffd.js";
|
|
13
15
|
import {
|
|
14
|
-
Database,
|
|
15
16
|
clearCachedM0M1,
|
|
16
17
|
clearCompressionDepth,
|
|
17
18
|
clearCompressionDepthRange,
|
|
18
|
-
closeQuietly,
|
|
19
19
|
ensureSessionMetaRow,
|
|
20
20
|
estimateTokens,
|
|
21
21
|
getCachedAbsoluteMessageCount,
|
|
22
22
|
getLegacyProtectedTailStartOrdinal,
|
|
23
|
-
getMessageTimesFromOpenCodeDb,
|
|
24
23
|
getRawSessionMessageCount,
|
|
25
24
|
getRawSessionTagKeysThrough,
|
|
26
25
|
hasMeaningfulUserText,
|
|
@@ -29,9 +28,17 @@ import {
|
|
|
29
28
|
persistCachedM0,
|
|
30
29
|
readRawSessionMessages,
|
|
31
30
|
readSessionChunk,
|
|
32
|
-
updateSessionMeta
|
|
31
|
+
updateSessionMeta
|
|
32
|
+
} from "./index-b563sga5.js";
|
|
33
|
+
import {
|
|
34
|
+
Database,
|
|
35
|
+
closeQuietly,
|
|
36
|
+
getMessageTimesFromOpenCodeDb,
|
|
33
37
|
withPrivilegedWriter
|
|
34
|
-
} from "./index-
|
|
38
|
+
} from "./index-fcyvznch.js";
|
|
39
|
+
import {
|
|
40
|
+
pushNotification
|
|
41
|
+
} from "./index-d62ypazr.js";
|
|
35
42
|
import {
|
|
36
43
|
log,
|
|
37
44
|
sessionLog
|
|
@@ -372,462 +379,175 @@ function buildPrimaryLanguageDirective(language) {
|
|
|
372
379
|
return "";
|
|
373
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.`;
|
|
374
381
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
/context size has been exceeded/i
|
|
397
|
-
];
|
|
398
|
-
var LIMIT_EXTRACTION_PATTERNS = [
|
|
399
|
-
/maximum prompt length is (\d+)/i,
|
|
400
|
-
/maximum context length is (\d+) tokens?/i,
|
|
401
|
-
/context length is only (\d+) tokens?/i,
|
|
402
|
-
/exceeds the limit of (\d+)/i,
|
|
403
|
-
/too large for model with (\d+) maximum context length/i,
|
|
404
|
-
/context size.*(\d+) tokens?/i,
|
|
405
|
-
/exceeds? the context length of (\d+)/i,
|
|
406
|
-
/>\s*(\d+)\s*(?:tokens?\s*)?(?:maximum|max|limit)\b/i,
|
|
407
|
-
/max(?:imum)?.*context.*?(\d+)/i
|
|
408
|
-
];
|
|
409
|
-
var MIN_PLAUSIBLE_LIMIT = 1024;
|
|
410
|
-
var MAX_PLAUSIBLE_LIMIT = 1e7;
|
|
411
|
-
function extractErrorMessage(error) {
|
|
412
|
-
if (!error)
|
|
413
|
-
return "";
|
|
414
|
-
if (typeof error === "string")
|
|
415
|
-
return error;
|
|
416
|
-
if (typeof error === "object") {
|
|
417
|
-
const obj = error;
|
|
418
|
-
const nested = obj.error;
|
|
419
|
-
if (nested && typeof nested.message === "string" && nested.message.length > 0) {
|
|
420
|
-
return nested.message;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
if (error instanceof Error)
|
|
424
|
-
return error.message;
|
|
425
|
-
if (typeof error === "object") {
|
|
426
|
-
const obj = error;
|
|
427
|
-
if (typeof obj.message === "string")
|
|
428
|
-
return obj.message;
|
|
429
|
-
if (typeof obj.responseBody === "string")
|
|
430
|
-
return obj.responseBody;
|
|
431
|
-
try {
|
|
432
|
-
return JSON.stringify(error);
|
|
433
|
-
} catch {
|
|
434
|
-
return String(error);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
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 as readFileSync2,
|
|
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;
|
|
438
403
|
}
|
|
439
|
-
function
|
|
440
|
-
|
|
441
|
-
if (!message) {
|
|
442
|
-
return { isOverflow: false };
|
|
443
|
-
}
|
|
444
|
-
const hasStatus413 = /\b413\b/.test(message) && /(entity|payload|context|prompt)/i.test(message);
|
|
445
|
-
let matched;
|
|
446
|
-
for (const pattern of OVERFLOW_PATTERNS) {
|
|
447
|
-
if (pattern.test(message)) {
|
|
448
|
-
matched = pattern;
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if (!matched && !hasStatus413) {
|
|
453
|
-
return { isOverflow: false };
|
|
454
|
-
}
|
|
455
|
-
const reportedLimit = parseReportedLimit(message);
|
|
456
|
-
return {
|
|
457
|
-
isOverflow: true,
|
|
458
|
-
reportedLimit,
|
|
459
|
-
matchedPattern: matched?.source
|
|
460
|
-
};
|
|
404
|
+
function bootQuietRemainingMs(now = Date.now()) {
|
|
405
|
+
return Math.max(0, bootQuietUntilMs - now);
|
|
461
406
|
}
|
|
462
|
-
function
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const match = message.match(pattern);
|
|
467
|
-
if (!match)
|
|
468
|
-
continue;
|
|
469
|
-
const raw = match[1];
|
|
470
|
-
if (!raw)
|
|
471
|
-
continue;
|
|
472
|
-
const value = Number.parseInt(raw, 10);
|
|
473
|
-
if (!Number.isFinite(value))
|
|
474
|
-
continue;
|
|
475
|
-
if (value < MIN_PLAUSIBLE_LIMIT || value > MAX_PLAUSIBLE_LIMIT)
|
|
476
|
-
continue;
|
|
477
|
-
return value;
|
|
478
|
-
}
|
|
479
|
-
return;
|
|
407
|
+
function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
|
|
408
|
+
const timer = setTimeout(task, bootQuietRemainingMs() + Math.max(0, additionalDelayMs));
|
|
409
|
+
timer.unref?.();
|
|
410
|
+
return timer;
|
|
480
411
|
}
|
|
481
412
|
|
|
482
|
-
// src/shared/
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
413
|
+
// src/shared/rpc-utils.ts
|
|
414
|
+
import { execFileSync } from "node:child_process";
|
|
415
|
+
import { createHash } from "node:crypto";
|
|
416
|
+
import { readFileSync } from "node:fs";
|
|
417
|
+
import { join } from "node:path";
|
|
418
|
+
function projectHash(directory) {
|
|
419
|
+
const normalized = directory.replace(/\/+$/, "");
|
|
420
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
486
421
|
}
|
|
487
|
-
function
|
|
488
|
-
|
|
489
|
-
return error;
|
|
490
|
-
if (error instanceof Error)
|
|
491
|
-
return error.message;
|
|
492
|
-
if (typeof error === "object" && error !== null) {
|
|
493
|
-
const obj = error;
|
|
494
|
-
if (typeof obj.message === "string")
|
|
495
|
-
return obj.message;
|
|
496
|
-
}
|
|
497
|
-
try {
|
|
498
|
-
return JSON.stringify(error);
|
|
499
|
-
} catch (_error) {
|
|
500
|
-
return String(error);
|
|
501
|
-
}
|
|
422
|
+
function rpcPortDir(storageDir, directory) {
|
|
423
|
+
return join(storageDir, "rpc", projectHash(directory));
|
|
502
424
|
}
|
|
503
|
-
function
|
|
504
|
-
|
|
425
|
+
function rpcPortFilePath(storageDir, directory, pid = process.pid, instanceId) {
|
|
426
|
+
const suffix = instanceId ? `-${instanceId}` : "";
|
|
427
|
+
return join(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
|
|
428
|
+
}
|
|
429
|
+
function isPidAlive(pid) {
|
|
430
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
431
|
+
return false;
|
|
432
|
+
try {
|
|
433
|
+
process.kill(pid, 0);
|
|
434
|
+
return true;
|
|
435
|
+
} catch (err) {
|
|
436
|
+
return err.code === "EPERM";
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
var RPC_IDENTITY_SKEW_TOLERANCE_MS = 120000;
|
|
440
|
+
var LINUX_CLOCK_TICKS_PER_SECOND = 100;
|
|
441
|
+
var PS_PROBE_TIMEOUT_MS = 1000;
|
|
442
|
+
var OPEN_CODE_COMMAND_MARKERS = ["opencode", "node", "bun", "electron"];
|
|
443
|
+
var rpcIdentityReadFileSync = readFileSync;
|
|
444
|
+
var rpcIdentityExecFileSync = execFileSync;
|
|
445
|
+
var rpcIdentityPlatform = process.platform;
|
|
446
|
+
var rpcIdentityNowMs = () => Date.now();
|
|
447
|
+
function parseLinuxProcessStartTime(statContent, uptimeContent) {
|
|
448
|
+
const closingCommandName = statContent.lastIndexOf(")");
|
|
449
|
+
if (closingCommandName < 0)
|
|
505
450
|
return null;
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const suggestions = data.suggestions;
|
|
511
|
-
if (Array.isArray(suggestions) && typeof suggestions[0] === "string") {
|
|
512
|
-
return {
|
|
513
|
-
providerID: String(data.providerID ?? ""),
|
|
514
|
-
modelID: String(data.modelID ?? ""),
|
|
515
|
-
suggestion: suggestions[0]
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
for (const key of ["data", "error", "cause"]) {
|
|
520
|
-
const nested = errObj[key];
|
|
521
|
-
if (nested && typeof nested === "object") {
|
|
522
|
-
const result = parseModelSuggestion(nested);
|
|
523
|
-
if (result)
|
|
524
|
-
return result;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
const message = extractMessage(error);
|
|
529
|
-
const modelMatch = message.match(/model not found:\s*([^/\s]+)\s*\/\s*([^.,\s]+)/i);
|
|
530
|
-
const suggestionMatch = message.match(/did you mean:\s*([^,?]+)/i);
|
|
531
|
-
if (!modelMatch || !suggestionMatch) {
|
|
451
|
+
const statFields = statContent.slice(closingCommandName + 1).trim().split(/\s+/);
|
|
452
|
+
const startTimeTicks = Number(statFields[19]);
|
|
453
|
+
const uptimeSeconds = Number(uptimeContent.trim().split(/\s+/)[0]);
|
|
454
|
+
if (!Number.isFinite(startTimeTicks) || startTimeTicks < 0 || !Number.isFinite(uptimeSeconds) || uptimeSeconds < 0) {
|
|
532
455
|
return null;
|
|
533
456
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
modelID: modelMatch[2].trim(),
|
|
537
|
-
suggestion: suggestionMatch[1].trim()
|
|
538
|
-
};
|
|
457
|
+
const processStartTime = rpcIdentityNowMs() - uptimeSeconds * 1000 + startTimeTicks / LINUX_CLOCK_TICKS_PER_SECOND * 1000;
|
|
458
|
+
return Number.isFinite(processStartTime) ? processStartTime : null;
|
|
539
459
|
}
|
|
540
|
-
|
|
541
|
-
if (signal?.aborted) {
|
|
542
|
-
throw new Error("prompt aborted by external signal");
|
|
543
|
-
}
|
|
544
|
-
const controller = new AbortController;
|
|
545
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
546
|
-
const onExternalAbort = () => controller.abort();
|
|
547
|
-
signal?.addEventListener("abort", onExternalAbort);
|
|
460
|
+
function readLinuxProcessStartTime(pid) {
|
|
548
461
|
try {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (signal?.aborted) {
|
|
555
|
-
await abortChildRun(client, args.path.id);
|
|
556
|
-
throw new Error("prompt aborted by external signal");
|
|
557
|
-
}
|
|
558
|
-
if (controller.signal.aborted) {
|
|
559
|
-
await abortChildRun(client, args.path.id);
|
|
560
|
-
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
561
|
-
}
|
|
562
|
-
throw error;
|
|
563
|
-
} finally {
|
|
564
|
-
clearTimeout(timeout);
|
|
565
|
-
signal?.removeEventListener("abort", onExternalAbort);
|
|
462
|
+
const statContent = String(rpcIdentityReadFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
463
|
+
const uptimeContent = String(rpcIdentityReadFileSync("/proc/uptime", "utf8"));
|
|
464
|
+
return parseLinuxProcessStartTime(statContent, uptimeContent);
|
|
465
|
+
} catch {
|
|
466
|
+
return null;
|
|
566
467
|
}
|
|
567
468
|
}
|
|
568
|
-
|
|
469
|
+
function readPsProcessStartTime(pid) {
|
|
569
470
|
try {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function isNonRetryable(error, externalSignal) {
|
|
579
|
-
if (externalSignal?.aborted)
|
|
580
|
-
return true;
|
|
581
|
-
if (error instanceof Error) {
|
|
582
|
-
if (error.name === "AbortError")
|
|
583
|
-
return true;
|
|
584
|
-
if (error.message === "prompt aborted by external signal")
|
|
585
|
-
return true;
|
|
586
|
-
if (/^prompt timed out after \d+ms$/.test(error.message))
|
|
587
|
-
return true;
|
|
471
|
+
const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
472
|
+
encoding: "utf8",
|
|
473
|
+
timeout: PS_PROBE_TIMEOUT_MS
|
|
474
|
+
});
|
|
475
|
+
const processStartTime = Date.parse(String(output).trim());
|
|
476
|
+
return Number.isFinite(processStartTime) ? processStartTime : null;
|
|
477
|
+
} catch {
|
|
478
|
+
return null;
|
|
588
479
|
}
|
|
589
|
-
if (detectOverflow(error).isOverflow)
|
|
590
|
-
return true;
|
|
591
|
-
return false;
|
|
592
480
|
}
|
|
593
|
-
function
|
|
594
|
-
if (error instanceof Error) {
|
|
595
|
-
return error.name && error.name !== "Error" ? `${error.name}: ${error.message}` : error.message;
|
|
596
|
-
}
|
|
597
|
-
return extractMessage(error);
|
|
598
|
-
}
|
|
599
|
-
async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
|
|
600
|
-
const originalBody = { ...args.body };
|
|
601
|
-
const attemptArgs = copyPromptArgs(args, originalBody);
|
|
481
|
+
function readLinuxProcessCommand(pid) {
|
|
602
482
|
try {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
if (isNonRetryable(error, signal))
|
|
607
|
-
throw error;
|
|
608
|
-
const suggestion = parseModelSuggestion(error);
|
|
609
|
-
if (!suggestion || !originalBody.model) {
|
|
610
|
-
throw error;
|
|
611
|
-
}
|
|
612
|
-
log(`[${callContext}] ${label}: model not found, retrying with suggestion`, {
|
|
613
|
-
original: `${suggestion.providerID}/${suggestion.modelID}`,
|
|
614
|
-
suggested: suggestion.suggestion
|
|
615
|
-
});
|
|
616
|
-
await promptWithTimeout(client, copyPromptArgs(args, {
|
|
617
|
-
...originalBody,
|
|
618
|
-
model: {
|
|
619
|
-
providerID: suggestion.providerID,
|
|
620
|
-
modelID: suggestion.suggestion
|
|
621
|
-
}
|
|
622
|
-
}), timeoutMs, signal);
|
|
483
|
+
return String(rpcIdentityReadFileSync(`/proc/${pid}/cmdline`, "utf8"));
|
|
484
|
+
} catch {
|
|
485
|
+
return null;
|
|
623
486
|
}
|
|
624
487
|
}
|
|
625
|
-
|
|
626
|
-
const timeoutMs = options.timeoutMs ?? 300000;
|
|
627
|
-
const callContext = options.callContext ?? "subagent";
|
|
628
|
-
const fallbacks = options.fallbackModels ?? [];
|
|
629
|
-
const baseBody = { ...args.body };
|
|
630
|
-
const baseArgs = copyPromptArgs(args, baseBody);
|
|
631
|
-
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
632
|
-
let lastError = null;
|
|
488
|
+
function readPsProcessCommand(pid) {
|
|
633
489
|
try {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
lastError = error;
|
|
638
|
-
if (isNonRetryable(error, options.signal))
|
|
639
|
-
throw error;
|
|
640
|
-
if (fallbacks.length === 0) {
|
|
641
|
-
throw error;
|
|
642
|
-
}
|
|
643
|
-
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
644
|
-
}
|
|
645
|
-
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
646
|
-
const parsed = parseProviderModel(fallbacks[i]);
|
|
647
|
-
if (!parsed) {
|
|
648
|
-
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
652
|
-
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
653
|
-
...baseBody,
|
|
654
|
-
model: parsed
|
|
490
|
+
const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
491
|
+
encoding: "utf8",
|
|
492
|
+
timeout: PS_PROBE_TIMEOUT_MS
|
|
655
493
|
});
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
return;
|
|
660
|
-
} catch (error) {
|
|
661
|
-
lastError = error;
|
|
662
|
-
if (isNonRetryable(error, options.signal))
|
|
663
|
-
throw error;
|
|
664
|
-
const remaining = fallbacks.length - i - 1;
|
|
665
|
-
if (remaining > 0) {
|
|
666
|
-
log(`[${callContext}] ${label} failed: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
494
|
+
return String(output);
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
669
497
|
}
|
|
670
|
-
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; last error: ${shortErr(lastError)}`);
|
|
671
|
-
throw lastError ?? new Error("All fallback models failed");
|
|
672
498
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const validated = await options.validateOutput(output, attempt);
|
|
677
|
-
return { output, validated, attempt };
|
|
499
|
+
function commandLooksLikeOpenCode(command) {
|
|
500
|
+
const normalized = command.toLowerCase();
|
|
501
|
+
return OPEN_CODE_COMMAND_MARKERS.some((marker) => normalized.includes(marker));
|
|
678
502
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
let firstError = null;
|
|
688
|
-
let lastError = null;
|
|
689
|
-
try {
|
|
690
|
-
return await attemptAndValidate(client, baseArgs, timeoutMs, options.signal, callContext, {
|
|
691
|
-
label: explicitPrimaryLabel,
|
|
692
|
-
attemptIndex: 0,
|
|
693
|
-
isFallback: false,
|
|
694
|
-
totalAttempts,
|
|
695
|
-
model: baseBody.model
|
|
696
|
-
}, options);
|
|
697
|
-
} catch (error) {
|
|
698
|
-
firstError = error;
|
|
699
|
-
lastError = error;
|
|
700
|
-
if (isNonRetryable(error, options.signal))
|
|
701
|
-
throw error;
|
|
702
|
-
if (fallbacks.length === 0) {
|
|
703
|
-
throw error;
|
|
704
|
-
}
|
|
705
|
-
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
706
|
-
}
|
|
707
|
-
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
708
|
-
const parsed = parseProviderModel(fallbacks[i]);
|
|
709
|
-
if (!parsed) {
|
|
710
|
-
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
711
|
-
continue;
|
|
712
|
-
}
|
|
713
|
-
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
714
|
-
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
715
|
-
...baseBody,
|
|
716
|
-
model: parsed
|
|
717
|
-
});
|
|
718
|
-
const attempt = {
|
|
719
|
-
label,
|
|
720
|
-
attemptIndex: i + 1,
|
|
721
|
-
isFallback: true,
|
|
722
|
-
totalAttempts,
|
|
723
|
-
model: parsed
|
|
724
|
-
};
|
|
725
|
-
try {
|
|
726
|
-
const result = await attemptAndValidate(client, attemptArgs, timeoutMs, options.signal, callContext, attempt, options);
|
|
727
|
-
log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
|
|
728
|
-
return result;
|
|
729
|
-
} catch (error) {
|
|
730
|
-
if (firstError === null)
|
|
731
|
-
firstError = error;
|
|
732
|
-
lastError = error;
|
|
733
|
-
if (isNonRetryable(error, options.signal))
|
|
734
|
-
throw error;
|
|
735
|
-
const remaining = fallbacks.length - i - 1;
|
|
736
|
-
if (remaining > 0) {
|
|
737
|
-
log(`[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
503
|
+
function isPidIdentityPlausible(record) {
|
|
504
|
+
if (!Number.isInteger(record.pid) || record.pid <= 0)
|
|
505
|
+
return false;
|
|
506
|
+
if (Number.isFinite(record.started_at) && record.started_at > 0) {
|
|
507
|
+
const processStartTime = rpcIdentityPlatform === "linux" ? readLinuxProcessStartTime(record.pid) : readPsProcessStartTime(record.pid);
|
|
508
|
+
if (processStartTime === null)
|
|
509
|
+
return true;
|
|
510
|
+
return processStartTime <= record.started_at + RPC_IDENTITY_SKEW_TOLERANCE_MS;
|
|
740
511
|
}
|
|
741
|
-
|
|
742
|
-
|
|
512
|
+
const command = rpcIdentityPlatform === "linux" ? readLinuxProcessCommand(record.pid) : readPsProcessCommand(record.pid);
|
|
513
|
+
if (command === null)
|
|
514
|
+
return true;
|
|
515
|
+
return commandLooksLikeOpenCode(command);
|
|
743
516
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (
|
|
747
|
-
return
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
517
|
+
function parseRpcPortFile(content, fallbackPid = 0) {
|
|
518
|
+
const trimmed = content.trim();
|
|
519
|
+
if (!trimmed)
|
|
520
|
+
return null;
|
|
521
|
+
if (trimmed.startsWith("{")) {
|
|
522
|
+
try {
|
|
523
|
+
const parsed = JSON.parse(trimmed);
|
|
524
|
+
const port2 = Number(parsed.port);
|
|
525
|
+
const pid = Number(parsed.pid);
|
|
526
|
+
const startedAt = Number(parsed.started_at);
|
|
527
|
+
if (!isValidPort(port2) || !Number.isInteger(pid) || pid <= 0)
|
|
528
|
+
return null;
|
|
529
|
+
return {
|
|
530
|
+
port: port2,
|
|
531
|
+
pid,
|
|
532
|
+
started_at: Number.isFinite(startedAt) ? startedAt : 0,
|
|
533
|
+
token: typeof parsed.token === "string" ? parsed.token : undefined,
|
|
534
|
+
instance_id: typeof parsed.instance_id === "string" ? parsed.instance_id : undefined
|
|
535
|
+
};
|
|
536
|
+
} catch {
|
|
537
|
+
return null;
|
|
759
538
|
}
|
|
760
|
-
return fallback;
|
|
761
539
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
765
|
-
return fallback;
|
|
766
|
-
}
|
|
767
|
-
// src/shared/assistant-message-extractor.ts
|
|
768
|
-
function asSessionMessage(value) {
|
|
769
|
-
if (!isRecord(value))
|
|
770
|
-
return null;
|
|
771
|
-
const info = value.info;
|
|
772
|
-
const parts = value.parts;
|
|
773
|
-
return {
|
|
774
|
-
info: isRecord(info) ? {
|
|
775
|
-
role: typeof info.role === "string" ? info.role : undefined,
|
|
776
|
-
time: isRecord(info.time) ? {
|
|
777
|
-
created: typeof info.time.created === "number" ? info.time.created : undefined
|
|
778
|
-
} : undefined
|
|
779
|
-
} : undefined,
|
|
780
|
-
parts
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
function getCreatedTime(message) {
|
|
784
|
-
return message.info?.time?.created ?? 0;
|
|
785
|
-
}
|
|
786
|
-
function getTextParts(message) {
|
|
787
|
-
if (!Array.isArray(message.parts))
|
|
788
|
-
return [];
|
|
789
|
-
return message.parts.filter((part) => isRecord(part)).map((part) => ({
|
|
790
|
-
type: typeof part.type === "string" ? part.type : undefined,
|
|
791
|
-
text: typeof part.text === "string" ? part.text : undefined
|
|
792
|
-
})).filter((part) => part.type === "text" && Boolean(part.text));
|
|
793
|
-
}
|
|
794
|
-
function extractLatestAssistantText(messages) {
|
|
795
|
-
if (!Array.isArray(messages) || messages.length === 0)
|
|
796
|
-
return null;
|
|
797
|
-
const assistantMessages = messages.map(asSessionMessage).filter((message) => message !== null).filter((message) => message.info?.role === "assistant").sort((a, b) => getCreatedTime(b) - getCreatedTime(a));
|
|
798
|
-
const latest = assistantMessages[0];
|
|
799
|
-
if (!latest)
|
|
540
|
+
const port = Number.parseInt(trimmed, 10);
|
|
541
|
+
if (!isValidPort(port))
|
|
800
542
|
return null;
|
|
801
|
-
return
|
|
802
|
-
`) || null;
|
|
543
|
+
return { port, pid: fallbackPid, started_at: 0 };
|
|
803
544
|
}
|
|
804
|
-
function
|
|
805
|
-
|
|
806
|
-
return value.some((item) => hasLengthCappedOutput(item));
|
|
807
|
-
if (!isRecord(value))
|
|
808
|
-
return false;
|
|
809
|
-
if (value.length_capped === true || value.lengthCapped === true)
|
|
810
|
-
return true;
|
|
811
|
-
const finishReason = value.finish_reason ?? value.finishReason;
|
|
812
|
-
if (typeof finishReason === "string") {
|
|
813
|
-
const normalized = finishReason.toLowerCase();
|
|
814
|
-
if (normalized === "length" || normalized === "max_tokens" || normalized === "max_output_tokens") {
|
|
815
|
-
return true;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return Object.values(value).some((item) => hasLengthCappedOutput(item));
|
|
545
|
+
function isValidPort(port) {
|
|
546
|
+
return Number.isInteger(port) && port > 0 && port <= 65535;
|
|
819
547
|
}
|
|
820
548
|
|
|
821
|
-
// src/shared/keep-subagents.ts
|
|
822
|
-
var keepSubagents = false;
|
|
823
|
-
function setKeepSubagents(value) {
|
|
824
|
-
keepSubagents = value === true;
|
|
825
|
-
}
|
|
826
|
-
function shouldKeepSubagents() {
|
|
827
|
-
return keepSubagents;
|
|
828
|
-
}
|
|
829
549
|
// src/features/magic-context/context-authority.ts
|
|
830
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
550
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
831
551
|
var AUTHORITY_DOMAINS = ["memories", "notes"];
|
|
832
552
|
var moduleNoteEvaluationBridges = new Map;
|
|
833
553
|
function registerModuleNoteEvaluationBridge(projectPath, bridge) {
|
|
@@ -959,7 +679,7 @@ function checksumAuthoritySeedRows(rows) {
|
|
|
959
679
|
const rightId = seedSourceRowId(right) ?? Number.MAX_SAFE_INTEGER;
|
|
960
680
|
return leftId - rightId;
|
|
961
681
|
});
|
|
962
|
-
return
|
|
682
|
+
return createHash2("sha256").update(JSON.stringify(ordered.map(canonicalizeSeedValue))).digest("hex");
|
|
963
683
|
}
|
|
964
684
|
function maxDomainRowId(db, domain, projectPath) {
|
|
965
685
|
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
|
|
@@ -1047,10 +767,14 @@ async function prepareAuthority(args) {
|
|
|
1047
767
|
domain,
|
|
1048
768
|
rows: page
|
|
1049
769
|
});
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
sourceRowId
|
|
1053
|
-
|
|
770
|
+
const identityByModuleRowId = new Map;
|
|
771
|
+
for (const [index, moduleRowId] of (seedResponse.module_row_ids ?? []).entries()) {
|
|
772
|
+
const sourceRowId = seedSourceRowId(page[index]);
|
|
773
|
+
if (sourceRowId === null)
|
|
774
|
+
continue;
|
|
775
|
+
identityByModuleRowId.set(moduleRowId, { moduleRowId, sourceRowId });
|
|
776
|
+
}
|
|
777
|
+
const identities = [...identityByModuleRowId.values()];
|
|
1054
778
|
if (identities.length > 0) {
|
|
1055
779
|
args.db.transaction(() => {
|
|
1056
780
|
for (const identity of identities) {
|
|
@@ -1951,6 +1675,7 @@ function pullMemoryMirrorOnce(args) {
|
|
|
1951
1675
|
mirrorFlights.set(args.module, flight);
|
|
1952
1676
|
return flight;
|
|
1953
1677
|
}
|
|
1678
|
+
|
|
1954
1679
|
// src/hooks/magic-context/compartment-parser.ts
|
|
1955
1680
|
var COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
|
|
1956
1681
|
var ATTR_START_REGEX = /\bstart="(\d+)"/;
|
|
@@ -2221,7 +1946,7 @@ function healMissingMemoryBlockIds(db, columns) {
|
|
|
2221
1946
|
}
|
|
2222
1947
|
|
|
2223
1948
|
// src/features/magic-context/workspaces.ts
|
|
2224
|
-
import { createHash as
|
|
1949
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
2225
1950
|
|
|
2226
1951
|
// src/features/magic-context/memory/constants.ts
|
|
2227
1952
|
var V2_MEMORY_CATEGORIES = [
|
|
@@ -2274,8 +1999,8 @@ var CATEGORY_DEFAULT_TTL = {
|
|
|
2274
1999
|
};
|
|
2275
2000
|
|
|
2276
2001
|
// src/features/magic-context/memory/project-identity.ts
|
|
2277
|
-
import { execFileSync } from "node:child_process";
|
|
2278
|
-
import { createHash as
|
|
2002
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
2003
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2279
2004
|
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
2280
2005
|
import { homedir } from "node:os";
|
|
2281
2006
|
import path from "node:path";
|
|
@@ -2289,7 +2014,8 @@ var dubiousOwnershipFallbackDirectories = new Set;
|
|
|
2289
2014
|
var dubiousOwnershipLoggedDirectories = new Set;
|
|
2290
2015
|
var dubiousOwnershipWarnedDirectories = new Set;
|
|
2291
2016
|
var transientGitIdentityReuseLoggedDirectories = new Set;
|
|
2292
|
-
var execFileSyncForIdentity =
|
|
2017
|
+
var execFileSyncForIdentity = execFileSync2;
|
|
2018
|
+
var userHomeDirectoryForIdentity = () => homedir();
|
|
2293
2019
|
var nowMs = () => Date.now();
|
|
2294
2020
|
|
|
2295
2021
|
class ProjectIdentityError extends Error {
|
|
@@ -2343,7 +2069,7 @@ function getErrorStderr(error) {
|
|
|
2343
2069
|
}
|
|
2344
2070
|
function directoryFallback(directory) {
|
|
2345
2071
|
const canonical = path.resolve(directory);
|
|
2346
|
-
const hash =
|
|
2072
|
+
const hash = createHash3("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
|
|
2347
2073
|
return `dir:${hash}`;
|
|
2348
2074
|
}
|
|
2349
2075
|
function assertDirectoryUsable(canonicalDirectory, rawDirectory) {
|
|
@@ -2494,9 +2220,12 @@ function takeDubiousOwnershipProjectIdentityWarning(directory) {
|
|
|
2494
2220
|
dubiousOwnershipWarnedDirectories.add(canonical);
|
|
2495
2221
|
return formatDubiousOwnershipWarning(canonical);
|
|
2496
2222
|
}
|
|
2223
|
+
function canonicalUserHomeDirectory() {
|
|
2224
|
+
return realpathSync.native(userHomeDirectoryForIdentity());
|
|
2225
|
+
}
|
|
2497
2226
|
function isUserHomeDirectory(directory) {
|
|
2498
2227
|
try {
|
|
2499
|
-
return realpathSync.native(path.resolve(directory)) ===
|
|
2228
|
+
return realpathSync.native(path.resolve(directory)) === canonicalUserHomeDirectory();
|
|
2500
2229
|
} catch {
|
|
2501
2230
|
return false;
|
|
2502
2231
|
}
|
|
@@ -2565,22 +2294,52 @@ function hasGitDir(canonical) {
|
|
|
2565
2294
|
return false;
|
|
2566
2295
|
}
|
|
2567
2296
|
}
|
|
2568
|
-
function
|
|
2297
|
+
function gitRootInAncestorChain(startDirectory) {
|
|
2569
2298
|
let current = startDirectory;
|
|
2570
2299
|
while (true) {
|
|
2571
2300
|
if (existsSync(path.join(current, ".git"))) {
|
|
2572
|
-
|
|
2301
|
+
try {
|
|
2302
|
+
return realpathSync.native(current);
|
|
2303
|
+
} catch {
|
|
2304
|
+
return path.resolve(current);
|
|
2305
|
+
}
|
|
2573
2306
|
}
|
|
2574
2307
|
const parent = path.dirname(current);
|
|
2575
2308
|
if (parent === current) {
|
|
2576
|
-
return
|
|
2309
|
+
return null;
|
|
2577
2310
|
}
|
|
2578
2311
|
current = parent;
|
|
2579
2312
|
}
|
|
2580
2313
|
}
|
|
2581
|
-
function
|
|
2582
|
-
|
|
2583
|
-
|
|
2314
|
+
function hasGitDirInAncestorChain(startDirectory) {
|
|
2315
|
+
return gitRootInAncestorChain(startDirectory) !== null;
|
|
2316
|
+
}
|
|
2317
|
+
function gitRootDirectory(canonical) {
|
|
2318
|
+
const direct = gitRootInAncestorChain(canonical);
|
|
2319
|
+
if (direct)
|
|
2320
|
+
return direct;
|
|
2321
|
+
try {
|
|
2322
|
+
const realCanonical = realpathSync.native(canonical);
|
|
2323
|
+
return realCanonical === canonical ? null : gitRootInAncestorChain(realCanonical);
|
|
2324
|
+
} catch {
|
|
2325
|
+
return null;
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
function resolveProjectIdentityForSession(directory, allowHomeProject = false) {
|
|
2329
|
+
const canonicalHome = canonicalUserHomeDirectory();
|
|
2330
|
+
const canonicalDirectory = (() => {
|
|
2331
|
+
try {
|
|
2332
|
+
return realpathSync.native(path.resolve(directory));
|
|
2333
|
+
} catch {
|
|
2334
|
+
return path.resolve(directory);
|
|
2335
|
+
}
|
|
2336
|
+
})();
|
|
2337
|
+
const inheritsHomeRepository = gitRootDirectory(canonicalDirectory) === canonicalHome;
|
|
2338
|
+
if (canonicalDirectory === canonicalHome || inheritsHomeRepository) {
|
|
2339
|
+
if (!allowHomeProject)
|
|
2340
|
+
return;
|
|
2341
|
+
return directoryFallback(canonicalHome);
|
|
2342
|
+
}
|
|
2584
2343
|
return resolveProjectIdentityOrFallback(directory);
|
|
2585
2344
|
}
|
|
2586
2345
|
function normalizeStoredProjectPath(rawOrStored) {
|
|
@@ -2782,7 +2541,7 @@ function computeWorkspaceEpochFingerprint(db, identities) {
|
|
|
2782
2541
|
const canonical = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
2783
2542
|
const epochs = getEpochMap(db, canonical);
|
|
2784
2543
|
const shareCategories = selectWorkspaceShareCategories(db, canonical);
|
|
2785
|
-
const hash =
|
|
2544
|
+
const hash = createHash4("sha256");
|
|
2786
2545
|
hash.update("share_categories", "utf8");
|
|
2787
2546
|
hash.update("\x00");
|
|
2788
2547
|
hash.update(shareCategories === null ? "NO_WORKSPACE" : JSON.stringify(shareCategories), "utf8");
|
|
@@ -2859,6 +2618,7 @@ function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
|
|
|
2859
2618
|
}
|
|
2860
2619
|
|
|
2861
2620
|
// src/features/magic-context/migrations.ts
|
|
2621
|
+
var FORK_MIGRATION_VERSION_FLOOR = 1e4;
|
|
2862
2622
|
var MIGRATION_LOCK_RETRY_DELAYS_MS = [1000, 2000, 4000, 8000, 15000];
|
|
2863
2623
|
|
|
2864
2624
|
class MigrationLockBusyError extends Error {
|
|
@@ -4869,6 +4629,33 @@ var MIGRATIONS = [
|
|
|
4869
4629
|
up(db) {
|
|
4870
4630
|
installLatestAuthorityTriggers(db);
|
|
4871
4631
|
}
|
|
4632
|
+
},
|
|
4633
|
+
{
|
|
4634
|
+
version: 72,
|
|
4635
|
+
description: "add per-session compaction mode record column (issue #266)",
|
|
4636
|
+
up(db) {
|
|
4637
|
+
if (tableExists2(db, "session_meta")) {
|
|
4638
|
+
ensureColumn(db, "session_meta", "compaction_mode_record", "TEXT");
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
},
|
|
4642
|
+
{
|
|
4643
|
+
version: 73,
|
|
4644
|
+
description: "persist the last successful todowrite permission verdict",
|
|
4645
|
+
up(db) {
|
|
4646
|
+
if (tableExists2(db, "session_meta")) {
|
|
4647
|
+
ensureColumn(db, "session_meta", "todo_permission_denied", "INTEGER NOT NULL DEFAULT 2");
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
},
|
|
4651
|
+
{
|
|
4652
|
+
version: 74,
|
|
4653
|
+
description: "persist detected context-limit provenance",
|
|
4654
|
+
up(db) {
|
|
4655
|
+
if (tableExists2(db, "session_meta")) {
|
|
4656
|
+
ensureColumn(db, "session_meta", "detected_context_limit_provenance", "TEXT NOT NULL DEFAULT 'unknown'");
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4872
4659
|
}
|
|
4873
4660
|
];
|
|
4874
4661
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -4882,9 +4669,12 @@ function ensureMigrationsTable(db) {
|
|
|
4882
4669
|
`);
|
|
4883
4670
|
}
|
|
4884
4671
|
function getCurrentVersion(db) {
|
|
4885
|
-
const row = db.prepare("SELECT MAX(version)
|
|
4672
|
+
const row = db.prepare("SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations WHERE version < ?").get(FORK_MIGRATION_VERSION_FLOOR);
|
|
4886
4673
|
return row?.version ?? 0;
|
|
4887
4674
|
}
|
|
4675
|
+
function isMigrationApplied(db, version) {
|
|
4676
|
+
return db.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(version) != null;
|
|
4677
|
+
}
|
|
4888
4678
|
function isSiblingMigrationConflict(db, error, version) {
|
|
4889
4679
|
if (!(error instanceof Error))
|
|
4890
4680
|
return false;
|
|
@@ -4913,12 +4703,12 @@ function runMigrations(db) {
|
|
|
4913
4703
|
try {
|
|
4914
4704
|
const applied = db.transaction(() => {
|
|
4915
4705
|
currentVersion = getCurrentVersion(db);
|
|
4916
|
-
migration = MIGRATIONS.find((candidate) => candidate.version > currentVersion);
|
|
4706
|
+
migration = MIGRATIONS.find((candidate) => candidate.version > currentVersion && !isMigrationApplied(db, candidate.version));
|
|
4917
4707
|
if (!migration)
|
|
4918
4708
|
return false;
|
|
4919
4709
|
if (!loggedPlan) {
|
|
4920
|
-
const pendingCount = MIGRATIONS.filter((candidate) => candidate.version > currentVersion).length;
|
|
4921
|
-
log(`[migrations] current
|
|
4710
|
+
const pendingCount = MIGRATIONS.filter((candidate) => candidate.version > currentVersion && !isMigrationApplied(db, candidate.version)).length;
|
|
4711
|
+
log(`[migrations] current upstream migration lane: ${currentVersion}, applying ${pendingCount} migration(s)`);
|
|
4922
4712
|
loggedPlan = true;
|
|
4923
4713
|
}
|
|
4924
4714
|
migration.up(db);
|
|
@@ -4955,7 +4745,7 @@ function runMigrations(db) {
|
|
|
4955
4745
|
}
|
|
4956
4746
|
}
|
|
4957
4747
|
if (loggedPlan) {
|
|
4958
|
-
log(`[migrations]
|
|
4748
|
+
log(`[migrations] upstream migration lane now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
|
|
4959
4749
|
}
|
|
4960
4750
|
}
|
|
4961
4751
|
async function runMigrationsWithRetry(db, options = {}) {
|
|
@@ -4978,151 +4768,6 @@ async function runMigrationsWithRetry(db, options = {}) {
|
|
|
4978
4768
|
}
|
|
4979
4769
|
}
|
|
4980
4770
|
}
|
|
4981
|
-
// src/features/magic-context/project-docs-hash.ts
|
|
4982
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
4983
|
-
import { lstatSync, readFileSync, statSync as statSync2 } from "node:fs";
|
|
4984
|
-
import path2 from "node:path";
|
|
4985
|
-
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
4986
|
-
var PROJECT_DOCS_DELIMITER = `
|
|
4987
|
-
|
|
4988
|
-
---
|
|
4989
|
-
|
|
4990
|
-
`;
|
|
4991
|
-
var MAX_PROJECT_DOC_BYTES = 256 * 1024;
|
|
4992
|
-
var docsCache = new Map;
|
|
4993
|
-
function canonicalizeDocContent(raw) {
|
|
4994
|
-
return raw.replace(/^\uFEFF/, "").replace(/\r\n/g, `
|
|
4995
|
-
`).split(`
|
|
4996
|
-
`).map((line) => line.replace(/[ \t]+$/, "")).join(`
|
|
4997
|
-
`).replace(/\n+$/, "");
|
|
4998
|
-
}
|
|
4999
|
-
function fingerprintFile(filePath) {
|
|
5000
|
-
try {
|
|
5001
|
-
const stat = lstatSync(filePath);
|
|
5002
|
-
const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
|
|
5003
|
-
return {
|
|
5004
|
-
exists: isReadableDoc,
|
|
5005
|
-
mtimeMs: stat.mtimeMs,
|
|
5006
|
-
size: stat.size
|
|
5007
|
-
};
|
|
5008
|
-
} catch {
|
|
5009
|
-
return { exists: false, mtimeMs: 0, size: 0 };
|
|
5010
|
-
}
|
|
5011
|
-
}
|
|
5012
|
-
function readDirectoryMtimeMs(projectDirectory) {
|
|
5013
|
-
try {
|
|
5014
|
-
return statSync2(projectDirectory).mtimeMs;
|
|
5015
|
-
} catch {
|
|
5016
|
-
return 0;
|
|
5017
|
-
}
|
|
5018
|
-
}
|
|
5019
|
-
function fingerprintsEqual(a, b) {
|
|
5020
|
-
return a?.exists === b.exists && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
5021
|
-
}
|
|
5022
|
-
function cacheFilesEqual(cachedFiles, currentFiles) {
|
|
5023
|
-
for (const [filePath, current] of currentFiles) {
|
|
5024
|
-
if (!fingerprintsEqual(cachedFiles.get(filePath), current)) {
|
|
5025
|
-
return false;
|
|
5026
|
-
}
|
|
5027
|
-
}
|
|
5028
|
-
return true;
|
|
5029
|
-
}
|
|
5030
|
-
function readCurrentFingerprints(projectDirectory) {
|
|
5031
|
-
const files = new Map;
|
|
5032
|
-
for (const filename of PROJECT_DOC_FILES) {
|
|
5033
|
-
const filePath = path2.join(projectDirectory, filename);
|
|
5034
|
-
files.set(filePath, fingerprintFile(filePath));
|
|
5035
|
-
}
|
|
5036
|
-
return { directoryMtimeMs: readDirectoryMtimeMs(projectDirectory), files };
|
|
5037
|
-
}
|
|
5038
|
-
function readCanonicalPieces(projectDirectory, files) {
|
|
5039
|
-
const hashPieces = [];
|
|
5040
|
-
const renderedSections = [];
|
|
5041
|
-
for (const filename of PROJECT_DOC_FILES) {
|
|
5042
|
-
const filePath = path2.join(projectDirectory, filename);
|
|
5043
|
-
const fingerprint = files.get(filePath);
|
|
5044
|
-
if (!fingerprint?.exists) {
|
|
5045
|
-
continue;
|
|
5046
|
-
}
|
|
5047
|
-
let safeToRead = false;
|
|
5048
|
-
try {
|
|
5049
|
-
const st = lstatSync(filePath);
|
|
5050
|
-
safeToRead = st.isFile() && st.size <= MAX_PROJECT_DOC_BYTES;
|
|
5051
|
-
} catch {
|
|
5052
|
-
safeToRead = false;
|
|
5053
|
-
}
|
|
5054
|
-
if (!safeToRead) {
|
|
5055
|
-
continue;
|
|
5056
|
-
}
|
|
5057
|
-
const canonicalContent = canonicalizeDocContent(readFileSync(filePath, "utf8"));
|
|
5058
|
-
hashPieces.push(`file:${filename}
|
|
5059
|
-
${canonicalContent}`);
|
|
5060
|
-
renderedSections.push(`<file name="${escapeXmlAttr(filename)}">
|
|
5061
|
-
${escapeXmlContent(canonicalContent)}
|
|
5062
|
-
</file>`);
|
|
5063
|
-
}
|
|
5064
|
-
return { hashPieces, renderedSections };
|
|
5065
|
-
}
|
|
5066
|
-
function buildRenderedBlock(renderedSections) {
|
|
5067
|
-
if (renderedSections.length === 0) {
|
|
5068
|
-
return "";
|
|
5069
|
-
}
|
|
5070
|
-
return `<project-docs>
|
|
5071
|
-
${renderedSections.join(`
|
|
5072
|
-
|
|
5073
|
-
`)}
|
|
5074
|
-
</project-docs>`;
|
|
5075
|
-
}
|
|
5076
|
-
function hashCanonicalPieces(hashPieces) {
|
|
5077
|
-
if (hashPieces.length === 0) {
|
|
5078
|
-
return "";
|
|
5079
|
-
}
|
|
5080
|
-
return createHash4("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
5081
|
-
}
|
|
5082
|
-
function readProjectDocsCanonical(projectDirectory) {
|
|
5083
|
-
const canonicalDirectory = path2.resolve(projectDirectory);
|
|
5084
|
-
const current = readCurrentFingerprints(canonicalDirectory);
|
|
5085
|
-
const cached = docsCache.get(canonicalDirectory);
|
|
5086
|
-
if (cached && cacheFilesEqual(cached.files, current.files)) {
|
|
5087
|
-
cached.directoryMtimeMs = current.directoryMtimeMs;
|
|
5088
|
-
return {
|
|
5089
|
-
renderedBlock: cached.cachedRendered,
|
|
5090
|
-
canonicalHash: cached.cachedHash
|
|
5091
|
-
};
|
|
5092
|
-
}
|
|
5093
|
-
const { hashPieces, renderedSections } = readCanonicalPieces(canonicalDirectory, current.files);
|
|
5094
|
-
const canonicalHash = hashCanonicalPieces(hashPieces);
|
|
5095
|
-
const renderedBlock = buildRenderedBlock(renderedSections);
|
|
5096
|
-
docsCache.set(canonicalDirectory, {
|
|
5097
|
-
directoryMtimeMs: current.directoryMtimeMs,
|
|
5098
|
-
files: current.files,
|
|
5099
|
-
cachedHash: canonicalHash,
|
|
5100
|
-
cachedRendered: renderedBlock
|
|
5101
|
-
});
|
|
5102
|
-
return { renderedBlock, canonicalHash };
|
|
5103
|
-
}
|
|
5104
|
-
function computeProjectDocsHash(projectDirectory) {
|
|
5105
|
-
return readProjectDocsCanonical(projectDirectory).canonicalHash;
|
|
5106
|
-
}
|
|
5107
|
-
// src/features/magic-context/storage-db.ts
|
|
5108
|
-
import { chmodSync, copyFileSync, cpSync, existsSync as existsSync3, mkdirSync, mkdtempSync } from "node:fs";
|
|
5109
|
-
import { dirname, join as join2 } from "node:path";
|
|
5110
|
-
|
|
5111
|
-
// src/plugin/boot-quiet.ts
|
|
5112
|
-
var BOOT_QUIET_MS = 120000;
|
|
5113
|
-
var bootQuietUntilMs = 0;
|
|
5114
|
-
function beginBootQuietPeriod(now = Date.now()) {
|
|
5115
|
-
if (bootQuietUntilMs === 0)
|
|
5116
|
-
bootQuietUntilMs = now + BOOT_QUIET_MS;
|
|
5117
|
-
}
|
|
5118
|
-
function bootQuietRemainingMs(now = Date.now()) {
|
|
5119
|
-
return Math.max(0, bootQuietUntilMs - now);
|
|
5120
|
-
}
|
|
5121
|
-
function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
|
|
5122
|
-
const timer = setTimeout(task, bootQuietRemainingMs() + Math.max(0, additionalDelayMs));
|
|
5123
|
-
timer.unref?.();
|
|
5124
|
-
return timer;
|
|
5125
|
-
}
|
|
5126
4771
|
|
|
5127
4772
|
// src/features/magic-context/tool-definition-tokens.ts
|
|
5128
4773
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -5236,11 +4881,11 @@ function getMeasuredToolDefinitionTokens(providerID, modelID, agentName) {
|
|
|
5236
4881
|
|
|
5237
4882
|
// src/features/magic-context/tool-owner-backfill.ts
|
|
5238
4883
|
import { existsSync as existsSync2 } from "node:fs";
|
|
5239
|
-
import { join } from "node:path";
|
|
4884
|
+
import { join as join2 } from "node:path";
|
|
5240
4885
|
var LEASE_DURATION_MS = 5 * 60 * 1000;
|
|
5241
4886
|
var LEASE_RENEWAL_MS = 60 * 1000;
|
|
5242
4887
|
function resolveOpencodeDbPath() {
|
|
5243
|
-
return
|
|
4888
|
+
return join2(getDataDir(), "opencode", "opencode.db");
|
|
5244
4889
|
}
|
|
5245
4890
|
function ensureBackfillStateTable(db) {
|
|
5246
4891
|
db.exec(`
|
|
@@ -5495,30 +5140,40 @@ var persistenceByDatabase = new WeakMap;
|
|
|
5495
5140
|
var persistenceErrorByDatabase = new WeakMap;
|
|
5496
5141
|
var pathByDatabase = new WeakMap;
|
|
5497
5142
|
var lastSchemaFenceRejection = null;
|
|
5143
|
+
var lastMigrationOnOpenRefusal = null;
|
|
5498
5144
|
function getSchemaFenceRejection() {
|
|
5499
5145
|
return lastSchemaFenceRejection;
|
|
5500
5146
|
}
|
|
5501
|
-
|
|
5147
|
+
function getMigrationOnOpenRefusal() {
|
|
5148
|
+
return lastMigrationOnOpenRefusal;
|
|
5149
|
+
}
|
|
5150
|
+
var LATEST_SUPPORTED_VERSION = 74;
|
|
5502
5151
|
var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
|
|
5152
|
+
var defaultStoragePermissionFs = { chmodSync, mkdirSync };
|
|
5153
|
+
var storagePermissionFs = defaultStoragePermissionFs;
|
|
5503
5154
|
function ensureSecureStorageDir(dir) {
|
|
5504
|
-
|
|
5155
|
+
if (!shouldEnforcePrivateStoragePermissions()) {
|
|
5156
|
+
storagePermissionFs.mkdirSync(dir, { recursive: true });
|
|
5157
|
+
return;
|
|
5158
|
+
}
|
|
5159
|
+
storagePermissionFs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5505
5160
|
if (!PERMISSIONS_ENFORCEABLE)
|
|
5506
5161
|
return;
|
|
5507
5162
|
try {
|
|
5508
|
-
chmodSync(dir, 448);
|
|
5163
|
+
storagePermissionFs.chmodSync(dir, 448);
|
|
5509
5164
|
} catch (error) {
|
|
5510
5165
|
log(`[magic-context] could not restrict storage dir permissions on ${dir}: ${getErrorMessage(error)}`);
|
|
5511
5166
|
}
|
|
5512
5167
|
}
|
|
5513
5168
|
function restrictDatabaseFilePermissions(dbPath) {
|
|
5514
|
-
if (!PERMISSIONS_ENFORCEABLE)
|
|
5169
|
+
if (!PERMISSIONS_ENFORCEABLE || !shouldEnforcePrivateStoragePermissions())
|
|
5515
5170
|
return;
|
|
5516
5171
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5517
5172
|
const file = `${dbPath}${suffix}`;
|
|
5518
5173
|
if (!existsSync3(file))
|
|
5519
5174
|
continue;
|
|
5520
5175
|
try {
|
|
5521
|
-
chmodSync(file, 384);
|
|
5176
|
+
storagePermissionFs.chmodSync(file, 384);
|
|
5522
5177
|
} catch (error) {
|
|
5523
5178
|
log(`[magic-context] could not restrict DB file permissions on ${file}: ${getErrorMessage(error)}`);
|
|
5524
5179
|
}
|
|
@@ -5530,12 +5185,12 @@ function resolveDatabasePath(dbPathOverride) {
|
|
|
5530
5185
|
}
|
|
5531
5186
|
const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR;
|
|
5532
5187
|
if (testDataDir && !process.env.XDG_DATA_HOME) {
|
|
5533
|
-
const dbDir2 =
|
|
5534
|
-
return { dbDir: dbDir2, dbPath:
|
|
5188
|
+
const dbDir2 = join3(testDataDir, "cortexkit", "magic-context");
|
|
5189
|
+
return { dbDir: dbDir2, dbPath: join3(dbDir2, "context.db") };
|
|
5535
5190
|
}
|
|
5536
5191
|
if (false) {}
|
|
5537
5192
|
const dbDir = getMagicContextStorageDir();
|
|
5538
|
-
return { dbDir, dbPath:
|
|
5193
|
+
return { dbDir, dbPath: join3(dbDir, "context.db") };
|
|
5539
5194
|
}
|
|
5540
5195
|
function getDatabasePath(db) {
|
|
5541
5196
|
return pathByDatabase.get(db) ?? null;
|
|
@@ -5544,7 +5199,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
5544
5199
|
if (existsSync3(targetDbPath))
|
|
5545
5200
|
return;
|
|
5546
5201
|
const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
|
|
5547
|
-
const legacyDbPath =
|
|
5202
|
+
const legacyDbPath = join3(legacyDir, "context.db");
|
|
5548
5203
|
if (!existsSync3(legacyDbPath))
|
|
5549
5204
|
return;
|
|
5550
5205
|
log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
|
|
@@ -5561,7 +5216,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
5561
5216
|
}
|
|
5562
5217
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5563
5218
|
const src = `${legacyDbPath}${suffix}`;
|
|
5564
|
-
const dst =
|
|
5219
|
+
const dst = join3(targetDbDir, `context.db${suffix}`);
|
|
5565
5220
|
if (existsSync3(src)) {
|
|
5566
5221
|
try {
|
|
5567
5222
|
copyFileSync(src, dst);
|
|
@@ -5570,8 +5225,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
5570
5225
|
}
|
|
5571
5226
|
}
|
|
5572
5227
|
}
|
|
5573
|
-
const legacyModelsDir =
|
|
5574
|
-
const targetModelsDir =
|
|
5228
|
+
const legacyModelsDir = join3(legacyDir, "models");
|
|
5229
|
+
const targetModelsDir = join3(targetDbDir, "models");
|
|
5575
5230
|
if (existsSync3(legacyModelsDir) && !existsSync3(targetModelsDir)) {
|
|
5576
5231
|
try {
|
|
5577
5232
|
cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
|
|
@@ -5585,9 +5240,12 @@ function getPersistedSchemaVersion(db) {
|
|
|
5585
5240
|
if (!hasMigrationsTable) {
|
|
5586
5241
|
return 0;
|
|
5587
5242
|
}
|
|
5588
|
-
const row = db.prepare("SELECT MAX(version) AS version FROM schema_migrations").get();
|
|
5243
|
+
const row = db.prepare("SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations WHERE version < ?").get(FORK_MIGRATION_VERSION_FLOOR);
|
|
5589
5244
|
return row?.version ?? 0;
|
|
5590
5245
|
}
|
|
5246
|
+
function formatSchemaFenceBootLog(persistedVersion, supportedVersion) {
|
|
5247
|
+
return `[magic-context] upstream migration lane at boot: database=v${persistedVersion}, supported_fence=v${supportedVersion}`;
|
|
5248
|
+
}
|
|
5591
5249
|
function getRuntimeLatestSupportedVersion(options) {
|
|
5592
5250
|
if (options?.latestSupportedVersion !== undefined) {
|
|
5593
5251
|
return options.latestSupportedVersion;
|
|
@@ -5604,10 +5262,107 @@ function getRuntimeLatestSupportedVersion(options) {
|
|
|
5604
5262
|
function enforceSchemaFence(db, dbPath, latestSupportedVersion) {
|
|
5605
5263
|
const persistedVersion = getPersistedSchemaVersion(db);
|
|
5606
5264
|
if (persistedVersion <= latestSupportedVersion) {
|
|
5265
|
+
lastSchemaFenceRejection = null;
|
|
5607
5266
|
return true;
|
|
5608
5267
|
}
|
|
5609
5268
|
lastSchemaFenceRejection = { persistedVersion, supportedVersion: latestSupportedVersion };
|
|
5610
|
-
log(`[magic-context] storage fatal: refusing to open ${dbPath};
|
|
5269
|
+
log(`[magic-context] storage fatal: refusing to open ${dbPath}; upstream migration lane 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.`);
|
|
5270
|
+
return false;
|
|
5271
|
+
}
|
|
5272
|
+
function unreadableDiscovery(path2) {
|
|
5273
|
+
return {
|
|
5274
|
+
state: "unreadable",
|
|
5275
|
+
serverPids: [],
|
|
5276
|
+
staleFiles: [],
|
|
5277
|
+
unreadableFile: path2
|
|
5278
|
+
};
|
|
5279
|
+
}
|
|
5280
|
+
function inspectRpcServerDiscovery(storageDir) {
|
|
5281
|
+
const rpcRoot = join3(storageDir, "rpc");
|
|
5282
|
+
let projectEntries;
|
|
5283
|
+
try {
|
|
5284
|
+
projectEntries = readdirSync(rpcRoot, { withFileTypes: true });
|
|
5285
|
+
} catch (error) {
|
|
5286
|
+
if (error.code === "ENOENT") {
|
|
5287
|
+
return { state: "absent", serverPids: [], staleFiles: [] };
|
|
5288
|
+
}
|
|
5289
|
+
return unreadableDiscovery(rpcRoot);
|
|
5290
|
+
}
|
|
5291
|
+
const portFiles = [];
|
|
5292
|
+
for (const projectEntry of projectEntries) {
|
|
5293
|
+
if (!projectEntry.isDirectory())
|
|
5294
|
+
continue;
|
|
5295
|
+
const projectDir = join3(rpcRoot, projectEntry.name);
|
|
5296
|
+
let entries;
|
|
5297
|
+
try {
|
|
5298
|
+
entries = readdirSync(projectDir);
|
|
5299
|
+
} catch {
|
|
5300
|
+
return unreadableDiscovery(projectDir);
|
|
5301
|
+
}
|
|
5302
|
+
for (const entry of entries) {
|
|
5303
|
+
if (entry === "port" || entry.startsWith("port-") && entry.endsWith(".json")) {
|
|
5304
|
+
portFiles.push(join3(projectDir, entry));
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
if (portFiles.length === 0) {
|
|
5309
|
+
return { state: "absent", serverPids: [], staleFiles: [] };
|
|
5310
|
+
}
|
|
5311
|
+
const pids = new Set;
|
|
5312
|
+
const staleFiles = [];
|
|
5313
|
+
for (const portFile of portFiles) {
|
|
5314
|
+
let raw;
|
|
5315
|
+
try {
|
|
5316
|
+
raw = readFileSync2(portFile, "utf8");
|
|
5317
|
+
} catch {
|
|
5318
|
+
return unreadableDiscovery(portFile);
|
|
5319
|
+
}
|
|
5320
|
+
const filename = basename(portFile);
|
|
5321
|
+
const pidFromName = /^port-(\d+)/.exec(filename)?.[1];
|
|
5322
|
+
const record = parseRpcPortFile(raw, pidFromName ? Number(pidFromName) : 0);
|
|
5323
|
+
if (!record || !Number.isInteger(record.pid) || record.pid <= 0) {
|
|
5324
|
+
return unreadableDiscovery(portFile);
|
|
5325
|
+
}
|
|
5326
|
+
if (isPidAlive(record.pid) && isPidIdentityPlausible(record))
|
|
5327
|
+
pids.add(record.pid);
|
|
5328
|
+
else
|
|
5329
|
+
staleFiles.push(portFile);
|
|
5330
|
+
}
|
|
5331
|
+
for (const staleFile of staleFiles) {
|
|
5332
|
+
try {
|
|
5333
|
+
unlinkSync(staleFile);
|
|
5334
|
+
} catch {
|
|
5335
|
+
return unreadableDiscovery(staleFile);
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
const serverPids = [...pids].sort((a, b) => a - b);
|
|
5339
|
+
if (serverPids.length > 0) {
|
|
5340
|
+
return { state: "live", serverPids, staleFiles };
|
|
5341
|
+
}
|
|
5342
|
+
return { state: "stale", serverPids: [], staleFiles };
|
|
5343
|
+
}
|
|
5344
|
+
function enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion) {
|
|
5345
|
+
const persistedVersion = getPersistedSchemaVersion(db);
|
|
5346
|
+
if (persistedVersion >= latestSupportedVersion) {
|
|
5347
|
+
lastMigrationOnOpenRefusal = null;
|
|
5348
|
+
return true;
|
|
5349
|
+
}
|
|
5350
|
+
const discovery = inspectRpcServerDiscovery(dbDir);
|
|
5351
|
+
if (discovery.state === "absent" || discovery.state === "stale") {
|
|
5352
|
+
lastMigrationOnOpenRefusal = null;
|
|
5353
|
+
return true;
|
|
5354
|
+
}
|
|
5355
|
+
lastMigrationOnOpenRefusal = {
|
|
5356
|
+
persistedVersion,
|
|
5357
|
+
supportedVersion: latestSupportedVersion,
|
|
5358
|
+
serverPids: discovery.serverPids,
|
|
5359
|
+
...discovery.unreadableFile ? { unreadableFile: discovery.unreadableFile } : {}
|
|
5360
|
+
};
|
|
5361
|
+
if (discovery.state === "unreadable") {
|
|
5362
|
+
log(`[magic-context] storage fatal: refusing to migrate ${dbPath} from upstream migration 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.`);
|
|
5363
|
+
} else {
|
|
5364
|
+
log(`[magic-context] storage fatal: refusing to migrate ${dbPath} from upstream migration 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.`);
|
|
5365
|
+
}
|
|
5611
5366
|
return false;
|
|
5612
5367
|
}
|
|
5613
5368
|
var sqlitePragmaConfig = {
|
|
@@ -5654,6 +5409,9 @@ function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion)
|
|
|
5654
5409
|
pathByDatabase.set(db, dbPath);
|
|
5655
5410
|
persistenceByDatabase.set(db, true);
|
|
5656
5411
|
persistenceErrorByDatabase.delete(db);
|
|
5412
|
+
if (!explicitDbPath) {
|
|
5413
|
+
log(formatSchemaFenceBootLog(getPersistedSchemaVersion(db), latestSupportedVersion));
|
|
5414
|
+
}
|
|
5657
5415
|
return db;
|
|
5658
5416
|
}
|
|
5659
5417
|
function initializeDatabase(db) {
|
|
@@ -6254,12 +6012,14 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6254
6012
|
note_nudge_anchors TEXT NOT NULL DEFAULT '[]',
|
|
6255
6013
|
auto_search_hint_decisions TEXT NOT NULL DEFAULT '[]',
|
|
6256
6014
|
last_todo_state TEXT DEFAULT '',
|
|
6015
|
+
todo_permission_denied INTEGER NOT NULL DEFAULT 2,
|
|
6257
6016
|
todo_synthetic_call_id TEXT DEFAULT '',
|
|
6258
6017
|
todo_synthetic_anchor_message_id TEXT DEFAULT '',
|
|
6259
6018
|
todo_synthetic_state_json TEXT DEFAULT '',
|
|
6260
6019
|
is_subagent INTEGER DEFAULT 0,
|
|
6261
6020
|
last_context_percentage REAL DEFAULT 0,
|
|
6262
6021
|
last_input_tokens INTEGER DEFAULT 0,
|
|
6022
|
+
detected_context_limit_provenance TEXT NOT NULL DEFAULT 'unknown',
|
|
6263
6023
|
observed_safe_input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
6264
6024
|
cache_alert_sent INTEGER NOT NULL DEFAULT 0,
|
|
6265
6025
|
times_execute_threshold_reached INTEGER DEFAULT 0,
|
|
@@ -6314,6 +6074,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6314
6074
|
emergency_drain_active INTEGER NOT NULL DEFAULT 0,
|
|
6315
6075
|
historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
|
|
6316
6076
|
wrapup_in_progress_state TEXT,
|
|
6077
|
+
compaction_mode_record TEXT,
|
|
6317
6078
|
cached_m0_materialized_at INTEGER,
|
|
6318
6079
|
cached_m0_session_facts_version INTEGER,
|
|
6319
6080
|
cached_m0_upgrade_state TEXT,
|
|
@@ -6513,6 +6274,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6513
6274
|
ensureColumn(db, "session_meta", "note_nudge_anchors", "TEXT NOT NULL DEFAULT '[]'");
|
|
6514
6275
|
ensureColumn(db, "session_meta", "auto_search_hint_decisions", "TEXT NOT NULL DEFAULT '[]'");
|
|
6515
6276
|
ensureColumn(db, "session_meta", "last_todo_state", "TEXT DEFAULT ''");
|
|
6277
|
+
ensureColumn(db, "session_meta", "todo_permission_denied", "INTEGER NOT NULL DEFAULT 2");
|
|
6516
6278
|
ensureColumn(db, "session_meta", "todo_synthetic_call_id", "TEXT DEFAULT ''");
|
|
6517
6279
|
ensureColumn(db, "session_meta", "todo_synthetic_anchor_message_id", "TEXT DEFAULT ''");
|
|
6518
6280
|
ensureColumn(db, "session_meta", "todo_synthetic_state_json", "TEXT DEFAULT ''");
|
|
@@ -6569,6 +6331,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6569
6331
|
ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
|
|
6570
6332
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
6571
6333
|
ensureColumn(db, "session_meta", "detected_context_limit_model_key", "TEXT");
|
|
6334
|
+
ensureColumn(db, "session_meta", "detected_context_limit_provenance", "TEXT NOT NULL DEFAULT 'unknown'");
|
|
6572
6335
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
6573
6336
|
ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
|
|
6574
6337
|
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
@@ -6619,6 +6382,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
6619
6382
|
ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
|
|
6620
6383
|
ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
|
|
6621
6384
|
ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
|
|
6385
|
+
ensureColumn(db, "session_meta", "compaction_mode_record", "TEXT");
|
|
6622
6386
|
ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
|
|
6623
6387
|
ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
|
|
6624
6388
|
ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
|
|
@@ -6758,6 +6522,8 @@ function openDatabase(dbPathOrOptions) {
|
|
|
6758
6522
|
const explicitDbPath = options?.dbPath !== undefined;
|
|
6759
6523
|
const { dbDir, dbPath } = resolveDatabasePath(options?.dbPath);
|
|
6760
6524
|
const latestSupportedVersion = getRuntimeLatestSupportedVersion(options);
|
|
6525
|
+
lastSchemaFenceRejection = null;
|
|
6526
|
+
lastMigrationOnOpenRefusal = null;
|
|
6761
6527
|
const existing = databases.get(dbPath);
|
|
6762
6528
|
if (existing) {
|
|
6763
6529
|
if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion)) {
|
|
@@ -6779,6 +6545,10 @@ function openDatabase(dbPathOrOptions) {
|
|
|
6779
6545
|
closeQuietly(db);
|
|
6780
6546
|
return null;
|
|
6781
6547
|
}
|
|
6548
|
+
if (!enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion)) {
|
|
6549
|
+
closeQuietly(db);
|
|
6550
|
+
return null;
|
|
6551
|
+
}
|
|
6782
6552
|
initializeDatabase(db);
|
|
6783
6553
|
runMigrations(db);
|
|
6784
6554
|
ensureContextStoreUuid(db);
|
|
@@ -6794,6 +6564,8 @@ async function openDatabaseAsync(dbPathOrOptions) {
|
|
|
6794
6564
|
const explicitDbPath = options?.dbPath !== undefined;
|
|
6795
6565
|
const { dbDir, dbPath } = resolveDatabasePath(options?.dbPath);
|
|
6796
6566
|
const latestSupportedVersion = getRuntimeLatestSupportedVersion(options);
|
|
6567
|
+
lastSchemaFenceRejection = null;
|
|
6568
|
+
lastMigrationOnOpenRefusal = null;
|
|
6797
6569
|
const existing = databases.get(dbPath);
|
|
6798
6570
|
if (existing) {
|
|
6799
6571
|
if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion))
|
|
@@ -6817,6 +6589,10 @@ async function openDatabaseAsync(dbPathOrOptions) {
|
|
|
6817
6589
|
closeQuietly(db);
|
|
6818
6590
|
return null;
|
|
6819
6591
|
}
|
|
6592
|
+
if (!enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion)) {
|
|
6593
|
+
closeQuietly(db);
|
|
6594
|
+
return null;
|
|
6595
|
+
}
|
|
6820
6596
|
initializeDatabase(db);
|
|
6821
6597
|
await runMigrationsWithRetry(db);
|
|
6822
6598
|
ensureContextStoreUuid(db);
|
|
@@ -6847,11 +6623,188 @@ function getDatabasePersistenceError(db) {
|
|
|
6847
6623
|
return null;
|
|
6848
6624
|
return persistenceErrorByDatabase.get(db) ?? null;
|
|
6849
6625
|
}
|
|
6850
|
-
|
|
6626
|
+
|
|
6627
|
+
// src/features/magic-context/schema-fence-probe.ts
|
|
6628
|
+
var STALE_CHILD_SPAWN_FAILURE = "stale_schema_fence";
|
|
6629
|
+
var STALE_CHILD_SPAWN_LATCH_THRESHOLD = 2;
|
|
6630
|
+
var state = {
|
|
6631
|
+
consecutiveFailures: 0,
|
|
6632
|
+
totalFailures: 0,
|
|
6633
|
+
latched: false,
|
|
6634
|
+
noticeEmitted: false,
|
|
6635
|
+
failure: null
|
|
6636
|
+
};
|
|
6637
|
+
function recordStaleFence(persistedVersion, supportedVersion) {
|
|
6638
|
+
state.consecutiveFailures += 1;
|
|
6639
|
+
state.totalFailures += 1;
|
|
6640
|
+
const latched = state.consecutiveFailures >= STALE_CHILD_SPAWN_LATCH_THRESHOLD;
|
|
6641
|
+
state.latched ||= latched;
|
|
6642
|
+
const failure = {
|
|
6643
|
+
failureClass: STALE_CHILD_SPAWN_FAILURE,
|
|
6644
|
+
persistedVersion,
|
|
6645
|
+
supportedVersion,
|
|
6646
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
6647
|
+
totalFailures: state.totalFailures,
|
|
6648
|
+
latched: state.latched
|
|
6649
|
+
};
|
|
6650
|
+
state.failure = failure;
|
|
6651
|
+
const shouldSurface = state.latched && !state.noticeEmitted;
|
|
6652
|
+
if (shouldSurface)
|
|
6653
|
+
state.noticeEmitted = true;
|
|
6654
|
+
return { allowSpawn: false, failure, shouldSurface };
|
|
6655
|
+
}
|
|
6656
|
+
function probeChildSpawnFence(db) {
|
|
6657
|
+
if (!db) {
|
|
6658
|
+
const knownRejection = getSchemaFenceRejection();
|
|
6659
|
+
if (knownRejection) {
|
|
6660
|
+
return recordStaleFence(knownRejection.persistedVersion, knownRejection.supportedVersion);
|
|
6661
|
+
}
|
|
6662
|
+
return { allowSpawn: true };
|
|
6663
|
+
}
|
|
6664
|
+
try {
|
|
6665
|
+
const persistedVersion = getPersistedSchemaVersion(db);
|
|
6666
|
+
if (persistedVersion > LATEST_SUPPORTED_VERSION) {
|
|
6667
|
+
return recordStaleFence(persistedVersion, LATEST_SUPPORTED_VERSION);
|
|
6668
|
+
}
|
|
6669
|
+
} catch {
|
|
6670
|
+
return { allowSpawn: true };
|
|
6671
|
+
}
|
|
6672
|
+
state.consecutiveFailures = 0;
|
|
6673
|
+
state.latched = false;
|
|
6674
|
+
state.noticeEmitted = false;
|
|
6675
|
+
return { allowSpawn: true };
|
|
6676
|
+
}
|
|
6677
|
+
// src/features/magic-context/project-docs-hash.ts
|
|
6851
6678
|
import { createHash as createHash6 } from "node:crypto";
|
|
6679
|
+
import { lstatSync, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
6680
|
+
import path2 from "node:path";
|
|
6681
|
+
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
6682
|
+
var PROJECT_DOCS_DELIMITER = `
|
|
6683
|
+
|
|
6684
|
+
---
|
|
6685
|
+
|
|
6686
|
+
`;
|
|
6687
|
+
var MAX_PROJECT_DOC_BYTES = 256 * 1024;
|
|
6688
|
+
var docsCache = new Map;
|
|
6689
|
+
function canonicalizeDocContent(raw) {
|
|
6690
|
+
return raw.replace(/^\uFEFF/, "").replace(/\r\n/g, `
|
|
6691
|
+
`).split(`
|
|
6692
|
+
`).map((line) => line.replace(/[ \t]+$/, "")).join(`
|
|
6693
|
+
`).replace(/\n+$/, "");
|
|
6694
|
+
}
|
|
6695
|
+
function fingerprintFile(filePath) {
|
|
6696
|
+
try {
|
|
6697
|
+
const stat = lstatSync(filePath);
|
|
6698
|
+
const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
|
|
6699
|
+
return {
|
|
6700
|
+
exists: isReadableDoc,
|
|
6701
|
+
mtimeMs: stat.mtimeMs,
|
|
6702
|
+
size: stat.size
|
|
6703
|
+
};
|
|
6704
|
+
} catch {
|
|
6705
|
+
return { exists: false, mtimeMs: 0, size: 0 };
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6708
|
+
function readDirectoryMtimeMs(projectDirectory) {
|
|
6709
|
+
try {
|
|
6710
|
+
return statSync2(projectDirectory).mtimeMs;
|
|
6711
|
+
} catch {
|
|
6712
|
+
return 0;
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
function fingerprintsEqual(a, b) {
|
|
6716
|
+
return a?.exists === b.exists && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
6717
|
+
}
|
|
6718
|
+
function cacheFilesEqual(cachedFiles, currentFiles) {
|
|
6719
|
+
for (const [filePath, current] of currentFiles) {
|
|
6720
|
+
if (!fingerprintsEqual(cachedFiles.get(filePath), current)) {
|
|
6721
|
+
return false;
|
|
6722
|
+
}
|
|
6723
|
+
}
|
|
6724
|
+
return true;
|
|
6725
|
+
}
|
|
6726
|
+
function readCurrentFingerprints(projectDirectory) {
|
|
6727
|
+
const files = new Map;
|
|
6728
|
+
for (const filename of PROJECT_DOC_FILES) {
|
|
6729
|
+
const filePath = path2.join(projectDirectory, filename);
|
|
6730
|
+
files.set(filePath, fingerprintFile(filePath));
|
|
6731
|
+
}
|
|
6732
|
+
return { directoryMtimeMs: readDirectoryMtimeMs(projectDirectory), files };
|
|
6733
|
+
}
|
|
6734
|
+
function readCanonicalPieces(projectDirectory, files) {
|
|
6735
|
+
const hashPieces = [];
|
|
6736
|
+
const renderedSections = [];
|
|
6737
|
+
for (const filename of PROJECT_DOC_FILES) {
|
|
6738
|
+
const filePath = path2.join(projectDirectory, filename);
|
|
6739
|
+
const fingerprint = files.get(filePath);
|
|
6740
|
+
if (!fingerprint?.exists) {
|
|
6741
|
+
continue;
|
|
6742
|
+
}
|
|
6743
|
+
let safeToRead = false;
|
|
6744
|
+
try {
|
|
6745
|
+
const st = lstatSync(filePath);
|
|
6746
|
+
safeToRead = st.isFile() && st.size <= MAX_PROJECT_DOC_BYTES;
|
|
6747
|
+
} catch {
|
|
6748
|
+
safeToRead = false;
|
|
6749
|
+
}
|
|
6750
|
+
if (!safeToRead) {
|
|
6751
|
+
continue;
|
|
6752
|
+
}
|
|
6753
|
+
const canonicalContent = canonicalizeDocContent(readFileSync3(filePath, "utf8"));
|
|
6754
|
+
hashPieces.push(`file:${filename}
|
|
6755
|
+
${canonicalContent}`);
|
|
6756
|
+
renderedSections.push(`<file name="${escapeXmlAttr(filename)}">
|
|
6757
|
+
${escapeXmlContent(canonicalContent)}
|
|
6758
|
+
</file>`);
|
|
6759
|
+
}
|
|
6760
|
+
return { hashPieces, renderedSections };
|
|
6761
|
+
}
|
|
6762
|
+
function buildRenderedBlock(renderedSections) {
|
|
6763
|
+
if (renderedSections.length === 0) {
|
|
6764
|
+
return "";
|
|
6765
|
+
}
|
|
6766
|
+
return `<project-docs>
|
|
6767
|
+
${renderedSections.join(`
|
|
6768
|
+
|
|
6769
|
+
`)}
|
|
6770
|
+
</project-docs>`;
|
|
6771
|
+
}
|
|
6772
|
+
function hashCanonicalPieces(hashPieces) {
|
|
6773
|
+
if (hashPieces.length === 0) {
|
|
6774
|
+
return "";
|
|
6775
|
+
}
|
|
6776
|
+
return createHash6("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
6777
|
+
}
|
|
6778
|
+
function readProjectDocsCanonical(projectDirectory) {
|
|
6779
|
+
const canonicalDirectory = path2.resolve(projectDirectory);
|
|
6780
|
+
const current = readCurrentFingerprints(canonicalDirectory);
|
|
6781
|
+
const cached = docsCache.get(canonicalDirectory);
|
|
6782
|
+
if (cached && cacheFilesEqual(cached.files, current.files)) {
|
|
6783
|
+
cached.directoryMtimeMs = current.directoryMtimeMs;
|
|
6784
|
+
return {
|
|
6785
|
+
renderedBlock: cached.cachedRendered,
|
|
6786
|
+
canonicalHash: cached.cachedHash
|
|
6787
|
+
};
|
|
6788
|
+
}
|
|
6789
|
+
const { hashPieces, renderedSections } = readCanonicalPieces(canonicalDirectory, current.files);
|
|
6790
|
+
const canonicalHash = hashCanonicalPieces(hashPieces);
|
|
6791
|
+
const renderedBlock = buildRenderedBlock(renderedSections);
|
|
6792
|
+
docsCache.set(canonicalDirectory, {
|
|
6793
|
+
directoryMtimeMs: current.directoryMtimeMs,
|
|
6794
|
+
files: current.files,
|
|
6795
|
+
cachedHash: canonicalHash,
|
|
6796
|
+
cachedRendered: renderedBlock
|
|
6797
|
+
});
|
|
6798
|
+
return { renderedBlock, canonicalHash };
|
|
6799
|
+
}
|
|
6800
|
+
function computeProjectDocsHash(projectDirectory) {
|
|
6801
|
+
return readProjectDocsCanonical(projectDirectory).canonicalHash;
|
|
6802
|
+
}
|
|
6803
|
+
// src/features/magic-context/storage-embedding-measurements.ts
|
|
6804
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
6852
6805
|
function normalizedQueryHash(query) {
|
|
6853
6806
|
const normalized = query.trim().replace(/\s+/g, " ").toLowerCase();
|
|
6854
|
-
return
|
|
6807
|
+
return createHash7("sha256").update(normalized).digest("hex");
|
|
6855
6808
|
}
|
|
6856
6809
|
var MEASUREMENT_CORPUS_SESSION_ROW_CAP = 2000;
|
|
6857
6810
|
function recordEmbeddingMeasurement(db, input) {
|
|
@@ -7067,7 +7020,7 @@ function getMemoryMutationsForRenderByIdentitySet(db, projectPaths, afterId, ren
|
|
|
7067
7020
|
}
|
|
7068
7021
|
visited.add(terminal);
|
|
7069
7022
|
const next = byTarget.get(terminal);
|
|
7070
|
-
if (
|
|
7023
|
+
if (next?.mutationType !== "superseded")
|
|
7071
7024
|
break;
|
|
7072
7025
|
if (depth >= MAX_MEMORY_REPLACEMENT_DEPTH - 1) {
|
|
7073
7026
|
terminal = null;
|
|
@@ -7101,6 +7054,24 @@ function getMaxMemoryMutationIdForProjects(db, projectPaths) {
|
|
|
7101
7054
|
WHERE project_path IN (${placeholders2(identities)})`).get(...identities);
|
|
7102
7055
|
return row?.max_id ?? null;
|
|
7103
7056
|
}
|
|
7057
|
+
// src/shared/context-limit-provenance.ts
|
|
7058
|
+
function normalizeContextLimitProvenance(value) {
|
|
7059
|
+
if (value === "prompt_only" || value === "combined")
|
|
7060
|
+
return value;
|
|
7061
|
+
return "unknown";
|
|
7062
|
+
}
|
|
7063
|
+
|
|
7064
|
+
// src/shared/escalation-bands.ts
|
|
7065
|
+
var MAX_EXECUTE_THRESHOLD = 90;
|
|
7066
|
+
var ABSOLUTE_EMERGENCY_PERCENTAGE = 95;
|
|
7067
|
+
function escalationBands(effectiveThresholdPercentage) {
|
|
7068
|
+
const threshold = Number.isFinite(effectiveThresholdPercentage) ? Math.min(effectiveThresholdPercentage, MAX_EXECUTE_THRESHOLD) : 65;
|
|
7069
|
+
return {
|
|
7070
|
+
forceMaterializationPercentage: Math.max(85, threshold + 2),
|
|
7071
|
+
emergencyPercentage: ABSOLUTE_EMERGENCY_PERCENTAGE
|
|
7072
|
+
};
|
|
7073
|
+
}
|
|
7074
|
+
|
|
7104
7075
|
// src/features/magic-context/storage-meta-persisted.ts
|
|
7105
7076
|
var emergencyRecoveryArmedSessions = new Set;
|
|
7106
7077
|
var providerOverflowReconfirmedSessions = new Set;
|
|
@@ -7322,11 +7293,11 @@ function readRawWrapupState(db, sessionId) {
|
|
|
7322
7293
|
return parseWrapupState(row?.wrapup_in_progress_state);
|
|
7323
7294
|
}
|
|
7324
7295
|
function getWrapupInProgressState(db, sessionId, now = Date.now()) {
|
|
7325
|
-
const
|
|
7326
|
-
if (!
|
|
7296
|
+
const state2 = readRawWrapupState(db, sessionId);
|
|
7297
|
+
if (!state2)
|
|
7327
7298
|
return null;
|
|
7328
|
-
if (
|
|
7329
|
-
return
|
|
7299
|
+
if (state2.expiresAt > now)
|
|
7300
|
+
return state2;
|
|
7330
7301
|
try {
|
|
7331
7302
|
db.exec("BEGIN IMMEDIATE");
|
|
7332
7303
|
} catch {
|
|
@@ -7352,10 +7323,10 @@ function getWrapupInProgressState(db, sessionId, now = Date.now()) {
|
|
|
7352
7323
|
function isWrapupInProgress(db, sessionId, now = Date.now()) {
|
|
7353
7324
|
return getWrapupInProgressState(db, sessionId, now) !== null;
|
|
7354
7325
|
}
|
|
7355
|
-
function acquireWrapupInProgress(db, sessionId,
|
|
7326
|
+
function acquireWrapupInProgress(db, sessionId, state2, now = Date.now()) {
|
|
7356
7327
|
const acquiredAt = now;
|
|
7357
7328
|
const next = {
|
|
7358
|
-
...
|
|
7329
|
+
...state2,
|
|
7359
7330
|
acquiredAt,
|
|
7360
7331
|
expiresAt: acquiredAt + WRAPUP_IN_PROGRESS_TTL_MS,
|
|
7361
7332
|
updatedAt: acquiredAt
|
|
@@ -7365,7 +7336,7 @@ function acquireWrapupInProgress(db, sessionId, state, now = Date.now()) {
|
|
|
7365
7336
|
try {
|
|
7366
7337
|
ensureSessionMetaRow(db, sessionId);
|
|
7367
7338
|
const current = readRawWrapupState(db, sessionId);
|
|
7368
|
-
if (current && current.expiresAt > now && current.holderId !==
|
|
7339
|
+
if (current && current.expiresAt > now && current.holderId !== state2.holderId) {
|
|
7369
7340
|
db.exec("COMMIT");
|
|
7370
7341
|
finished = true;
|
|
7371
7342
|
return { ok: false, state: current };
|
|
@@ -7429,6 +7400,45 @@ function releaseWrapupInProgress(db, sessionId, holderId) {
|
|
|
7429
7400
|
}
|
|
7430
7401
|
}
|
|
7431
7402
|
}
|
|
7403
|
+
var COMPACTION_MODE_RECORD_VALUES = new Set([
|
|
7404
|
+
"on",
|
|
7405
|
+
"off",
|
|
7406
|
+
"on_notice_pending",
|
|
7407
|
+
"off_notice_pending",
|
|
7408
|
+
"off_cleanup_pending"
|
|
7409
|
+
]);
|
|
7410
|
+
function normalizeCompactionModeRecord(value) {
|
|
7411
|
+
if (value === null || value === undefined)
|
|
7412
|
+
return null;
|
|
7413
|
+
if (typeof value === "string" && COMPACTION_MODE_RECORD_VALUES.has(value)) {
|
|
7414
|
+
return value;
|
|
7415
|
+
}
|
|
7416
|
+
return null;
|
|
7417
|
+
}
|
|
7418
|
+
function resolveCompactionModeRecord(record) {
|
|
7419
|
+
switch (record) {
|
|
7420
|
+
case "on":
|
|
7421
|
+
case "on_notice_pending":
|
|
7422
|
+
return "on";
|
|
7423
|
+
case "off":
|
|
7424
|
+
case "off_notice_pending":
|
|
7425
|
+
case "off_cleanup_pending":
|
|
7426
|
+
return "off";
|
|
7427
|
+
default:
|
|
7428
|
+
return null;
|
|
7429
|
+
}
|
|
7430
|
+
}
|
|
7431
|
+
function getCompactionModeRecord(db, sessionId) {
|
|
7432
|
+
const row = db.prepare("SELECT compaction_mode_record FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7433
|
+
return normalizeCompactionModeRecord(row?.compaction_mode_record);
|
|
7434
|
+
}
|
|
7435
|
+
function setCompactionModeRecord(db, sessionId, value) {
|
|
7436
|
+
if (value !== null && !COMPACTION_MODE_RECORD_VALUES.has(value)) {
|
|
7437
|
+
throw new Error(`Invalid compaction_mode_record value: ${String(value)} (expected a supported compaction mode record or null)`);
|
|
7438
|
+
}
|
|
7439
|
+
ensureSessionMetaRow(db, sessionId);
|
|
7440
|
+
db.prepare("UPDATE session_meta SET compaction_mode_record = ? WHERE session_id = ?").run(value, sessionId);
|
|
7441
|
+
}
|
|
7432
7442
|
function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
|
|
7433
7443
|
if (usagePercentage >= 95)
|
|
7434
7444
|
return Math.min(1e6, Math.max(4 * perRunCap, Math.round(0.5 * usable)));
|
|
@@ -7436,7 +7446,6 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
|
|
|
7436
7446
|
return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
|
|
7437
7447
|
return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
|
|
7438
7448
|
}
|
|
7439
|
-
var EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95;
|
|
7440
7449
|
var EMERGENCY_DRAIN_EXIT_MARGIN = 10;
|
|
7441
7450
|
var EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55;
|
|
7442
7451
|
var EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000;
|
|
@@ -7471,7 +7480,8 @@ function reserveProtectedTailDrainTokens(args) {
|
|
|
7471
7480
|
}
|
|
7472
7481
|
const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
|
|
7473
7482
|
let latchActiveSince = meta.emergencyDrainActive;
|
|
7474
|
-
|
|
7483
|
+
const { forceMaterializationPercentage } = escalationBands(args.executeThresholdPercentage);
|
|
7484
|
+
if (args.usagePercentage >= forceMaterializationPercentage) {
|
|
7475
7485
|
if (latchActiveSince <= 0)
|
|
7476
7486
|
latchActiveSince = now;
|
|
7477
7487
|
} else if (latchActiveSince > 0) {
|
|
@@ -7623,13 +7633,20 @@ function getChannel2NudgeState(db, sessionId) {
|
|
|
7623
7633
|
function getChannel2NudgeClaim(db, sessionId) {
|
|
7624
7634
|
const result = db.prepare("SELECT channel2_nudge_state, channel2_nudge_claimed_at, channel2_nudge_claim_token FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7625
7635
|
const rawState = typeof result?.channel2_nudge_state === "string" ? result.channel2_nudge_state : "";
|
|
7626
|
-
const
|
|
7636
|
+
const state2 = rawState === "pending" || rawState === "claimed" || rawState === "delivered" ? rawState : "";
|
|
7627
7637
|
return {
|
|
7628
|
-
state,
|
|
7638
|
+
state: state2,
|
|
7629
7639
|
claimedAt: typeof result?.channel2_nudge_claimed_at === "number" ? result.channel2_nudge_claimed_at : 0,
|
|
7630
7640
|
claimToken: typeof result?.channel2_nudge_claim_token === "string" ? result.channel2_nudge_claim_token : ""
|
|
7631
7641
|
};
|
|
7632
7642
|
}
|
|
7643
|
+
function setChannel2NudgeState(db, sessionId, state2) {
|
|
7644
|
+
db.transaction(() => {
|
|
7645
|
+
ensureSessionMetaRow(db, sessionId);
|
|
7646
|
+
const claimedAt = state2 === "claimed" ? Date.now() : 0;
|
|
7647
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ?").run(state2, claimedAt, sessionId);
|
|
7648
|
+
})();
|
|
7649
|
+
}
|
|
7633
7650
|
function casChannel2NudgeState(db, sessionId, from, to) {
|
|
7634
7651
|
let changed = false;
|
|
7635
7652
|
db.transaction(() => {
|
|
@@ -7809,6 +7826,18 @@ function removeAutoSearchHintDecisionByMessageId(db, sessionId, messageId) {
|
|
|
7809
7826
|
}, { ensureRow: false });
|
|
7810
7827
|
return ok && removed;
|
|
7811
7828
|
}
|
|
7829
|
+
function getPersistedTodoPermissionDenied(db, sessionId) {
|
|
7830
|
+
const row = db.prepare("SELECT todo_permission_denied FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
7831
|
+
if (row?.todo_permission_denied === 1)
|
|
7832
|
+
return true;
|
|
7833
|
+
if (row?.todo_permission_denied === 0)
|
|
7834
|
+
return false;
|
|
7835
|
+
return null;
|
|
7836
|
+
}
|
|
7837
|
+
function setPersistedTodoPermissionDenied(db, sessionId, denied) {
|
|
7838
|
+
ensureSessionMetaRow(db, sessionId);
|
|
7839
|
+
db.prepare("UPDATE session_meta SET todo_permission_denied = ? WHERE session_id = ?").run(denied ? 1 : 0, sessionId);
|
|
7840
|
+
}
|
|
7812
7841
|
function getPersistedTodoSyntheticAnchor(db, sessionId) {
|
|
7813
7842
|
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);
|
|
7814
7843
|
if (!isPersistedTodoSyntheticAnchorRow(result)) {
|
|
@@ -7885,17 +7914,19 @@ function normalizeEmergencyRecoveryOrigin(value) {
|
|
|
7885
7914
|
return value === "provider_overflow" || value === "proactive_model_shrink" ? value : null;
|
|
7886
7915
|
}
|
|
7887
7916
|
function getOverflowState(db, sessionId, modelKey) {
|
|
7888
|
-
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);
|
|
7917
|
+
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);
|
|
7889
7918
|
if (!result) {
|
|
7890
7919
|
return {
|
|
7891
7920
|
detectedContextLimit: 0,
|
|
7892
7921
|
detectedContextLimitModelKey: null,
|
|
7922
|
+
detectedContextLimitProvenance: "unknown",
|
|
7893
7923
|
needsEmergencyRecovery: false,
|
|
7894
7924
|
emergencyRecoveryOrigin: null
|
|
7895
7925
|
};
|
|
7896
7926
|
}
|
|
7897
7927
|
const storedModelKey = normalizeDetectedLimitModelKey(result.detected_context_limit_model_key);
|
|
7898
7928
|
const requestedModelKey = normalizeDetectedLimitModelKey(modelKey);
|
|
7929
|
+
const provenance = normalizeContextLimitProvenance(result.detected_context_limit_provenance);
|
|
7899
7930
|
const limit = typeof result.detected_context_limit === "number" && result.detected_context_limit > 0 ? result.detected_context_limit : 0;
|
|
7900
7931
|
const modelMatches = limit > 0 && requestedModelKey && storedModelKey ? requestedModelKey === storedModelKey : true;
|
|
7901
7932
|
const needs = typeof result.needs_emergency_recovery === "number" && result.needs_emergency_recovery > 0;
|
|
@@ -7904,11 +7935,12 @@ function getOverflowState(db, sessionId, modelKey) {
|
|
|
7904
7935
|
return {
|
|
7905
7936
|
detectedContextLimit: modelMatches ? limit : 0,
|
|
7906
7937
|
detectedContextLimitModelKey: storedModelKey,
|
|
7938
|
+
detectedContextLimitProvenance: provenance,
|
|
7907
7939
|
needsEmergencyRecovery: needs,
|
|
7908
7940
|
emergencyRecoveryOrigin: recoveryOrigin
|
|
7909
7941
|
};
|
|
7910
7942
|
}
|
|
7911
|
-
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow") {
|
|
7943
|
+
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow", provenance = "unknown") {
|
|
7912
7944
|
emergencyRecoveryArmedSessions.add(sessionId);
|
|
7913
7945
|
db.transaction(() => {
|
|
7914
7946
|
ensureSessionMetaRow(db, sessionId);
|
|
@@ -7917,18 +7949,18 @@ function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin =
|
|
|
7917
7949
|
providerOverflowReconfirmedSessions.add(sessionId);
|
|
7918
7950
|
}
|
|
7919
7951
|
if (typeof reportedLimit === "number" && reportedLimit > 0) {
|
|
7920
|
-
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);
|
|
7952
|
+
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);
|
|
7921
7953
|
} else {
|
|
7922
7954
|
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);
|
|
7923
7955
|
}
|
|
7924
7956
|
})();
|
|
7925
7957
|
}
|
|
7926
|
-
function recordDetectedContextLimit(db, sessionId, reportedLimit, modelKey) {
|
|
7958
|
+
function recordDetectedContextLimit(db, sessionId, reportedLimit, modelKey, provenance = "unknown") {
|
|
7927
7959
|
if (!(reportedLimit > 0))
|
|
7928
7960
|
return;
|
|
7929
7961
|
db.transaction(() => {
|
|
7930
7962
|
ensureSessionMetaRow(db, sessionId);
|
|
7931
|
-
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);
|
|
7963
|
+
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);
|
|
7932
7964
|
})();
|
|
7933
7965
|
}
|
|
7934
7966
|
function clearEmergencyRecovery(db, sessionId) {
|
|
@@ -7946,7 +7978,7 @@ function clearEmergencyRecovery(db, sessionId) {
|
|
|
7946
7978
|
function clearDetectedContextLimit(db, sessionId) {
|
|
7947
7979
|
db.transaction(() => {
|
|
7948
7980
|
ensureSessionMetaRow(db, sessionId);
|
|
7949
|
-
db.prepare("UPDATE session_meta SET detected_context_limit = 0, detected_context_limit_model_key = NULL WHERE session_id = ?").run(sessionId);
|
|
7981
|
+
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);
|
|
7950
7982
|
})();
|
|
7951
7983
|
}
|
|
7952
7984
|
function getPersistedCompactionMarkerState(db, sessionId) {
|
|
@@ -7966,10 +7998,10 @@ function getPersistedCompactionMarkerState(db, sessionId) {
|
|
|
7966
7998
|
} catch {}
|
|
7967
7999
|
return null;
|
|
7968
8000
|
}
|
|
7969
|
-
function setPersistedCompactionMarkerState(db, sessionId,
|
|
8001
|
+
function setPersistedCompactionMarkerState(db, sessionId, state2) {
|
|
7970
8002
|
ensureSessionMetaRow(db, sessionId);
|
|
7971
|
-
const json =
|
|
7972
|
-
db.prepare("UPDATE session_meta SET compaction_marker_state = ?, compaction_marker_target_end_message_id = ? WHERE session_id = ?").run(json,
|
|
8003
|
+
const json = state2 ? JSON.stringify(state2) : "";
|
|
8004
|
+
db.prepare("UPDATE session_meta SET compaction_marker_state = ?, compaction_marker_target_end_message_id = ? WHERE session_id = ?").run(json, state2?.targetEndMessageId ?? null, sessionId);
|
|
7973
8005
|
}
|
|
7974
8006
|
function getStrippedPlaceholderIds(db, sessionId) {
|
|
7975
8007
|
const row = db.prepare("SELECT stripped_placeholder_ids FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
@@ -8101,9 +8133,9 @@ function getPendingCompactionMarkerState(db, sessionId) {
|
|
|
8101
8133
|
} catch {}
|
|
8102
8134
|
return null;
|
|
8103
8135
|
}
|
|
8104
|
-
function setPendingCompactionMarkerState(db, sessionId,
|
|
8136
|
+
function setPendingCompactionMarkerState(db, sessionId, state2) {
|
|
8105
8137
|
ensureSessionMetaRow(db, sessionId);
|
|
8106
|
-
const blob =
|
|
8138
|
+
const blob = state2 ? stableStringify(state2) : null;
|
|
8107
8139
|
db.prepare("UPDATE session_meta SET pending_compaction_marker_state = ? WHERE session_id = ?").run(blob, sessionId);
|
|
8108
8140
|
}
|
|
8109
8141
|
function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
|
|
@@ -8374,6 +8406,14 @@ function getPendingOpsStatement(db) {
|
|
|
8374
8406
|
}
|
|
8375
8407
|
return stmt;
|
|
8376
8408
|
}
|
|
8409
|
+
function getClearPendingOpsStatement(db) {
|
|
8410
|
+
let stmt = clearPendingOpsStatements.get(db);
|
|
8411
|
+
if (!stmt) {
|
|
8412
|
+
stmt = db.prepare("DELETE FROM pending_ops WHERE session_id = ?");
|
|
8413
|
+
clearPendingOpsStatements.set(db, stmt);
|
|
8414
|
+
}
|
|
8415
|
+
return stmt;
|
|
8416
|
+
}
|
|
8377
8417
|
function getRemovePendingOpStatement(db) {
|
|
8378
8418
|
let stmt = removePendingOpStatements.get(db);
|
|
8379
8419
|
if (!stmt) {
|
|
@@ -8408,6 +8448,9 @@ function getPendingOps(db, sessionId) {
|
|
|
8408
8448
|
const rows = getPendingOpsStatement(db).all(sessionId).filter(isPendingOpRow);
|
|
8409
8449
|
return rows.map(toPendingOp).filter((op) => op !== null);
|
|
8410
8450
|
}
|
|
8451
|
+
function clearPendingOps(db, sessionId) {
|
|
8452
|
+
getClearPendingOpsStatement(db).run(sessionId);
|
|
8453
|
+
}
|
|
8411
8454
|
function removePendingOp(db, sessionId, tagId) {
|
|
8412
8455
|
getRemovePendingOpStatement(db).run(sessionId, tagId);
|
|
8413
8456
|
}
|
|
@@ -8603,11 +8646,11 @@ function bumpProjectMemoryEpoch(db, projectPath, now = Date.now()) {
|
|
|
8603
8646
|
ON CONFLICT(project_path) DO UPDATE SET
|
|
8604
8647
|
project_memory_epoch = project_memory_epoch + 1,
|
|
8605
8648
|
updated_at = excluded.updated_at`).run(projectPath, now);
|
|
8606
|
-
const
|
|
8607
|
-
if (!
|
|
8649
|
+
const state2 = getProjectState(db, projectPath);
|
|
8650
|
+
if (!state2) {
|
|
8608
8651
|
throw new Error(`Failed to bump project memory epoch for ${projectPath}`);
|
|
8609
8652
|
}
|
|
8610
|
-
return
|
|
8653
|
+
return state2;
|
|
8611
8654
|
}
|
|
8612
8655
|
function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PROJECT_PATH, now = Date.now()) {
|
|
8613
8656
|
db.prepare(`INSERT INTO project_state
|
|
@@ -8616,11 +8659,11 @@ function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PRO
|
|
|
8616
8659
|
ON CONFLICT(project_path) DO UPDATE SET
|
|
8617
8660
|
project_user_profile_version = project_user_profile_version + 1,
|
|
8618
8661
|
updated_at = excluded.updated_at`).run(projectPath, now);
|
|
8619
|
-
const
|
|
8620
|
-
if (!
|
|
8662
|
+
const state2 = getProjectState(db, projectPath);
|
|
8663
|
+
if (!state2) {
|
|
8621
8664
|
throw new Error(`Failed to bump project user profile version for ${projectPath}`);
|
|
8622
8665
|
}
|
|
8623
|
-
return
|
|
8666
|
+
return state2;
|
|
8624
8667
|
}
|
|
8625
8668
|
// src/features/magic-context/storage-source.ts
|
|
8626
8669
|
function isSourceContentRow(row) {
|
|
@@ -9210,6 +9253,518 @@ var ERROR_CLASSES = new Set([
|
|
|
9210
9253
|
"permission_denied",
|
|
9211
9254
|
"unknown"
|
|
9212
9255
|
]);
|
|
9256
|
+
// src/hooks/magic-context/child-session-spawn.ts
|
|
9257
|
+
var STALE_PLUGIN_RESTART_NOTICE = "Magic Context: plugin build is older than its database — restart OpenCode";
|
|
9258
|
+
async function surfaceStalePluginBuild(args) {
|
|
9259
|
+
if (!args.parentSessionId)
|
|
9260
|
+
return;
|
|
9261
|
+
try {
|
|
9262
|
+
if (args.db) {
|
|
9263
|
+
updateSessionMeta(args.db, args.parentSessionId, {
|
|
9264
|
+
lastTransformError: STALE_PLUGIN_RESTART_NOTICE
|
|
9265
|
+
});
|
|
9266
|
+
}
|
|
9267
|
+
pushNotification("toast", {
|
|
9268
|
+
title: "Magic Context",
|
|
9269
|
+
message: STALE_PLUGIN_RESTART_NOTICE,
|
|
9270
|
+
variant: "error",
|
|
9271
|
+
duration: 1e4
|
|
9272
|
+
}, args.parentSessionId);
|
|
9273
|
+
pushNotification("action", { action: "refresh-sidebar" }, args.parentSessionId);
|
|
9274
|
+
await sendIgnoredMessage(args.client, args.parentSessionId, STALE_PLUGIN_RESTART_NOTICE, args.notificationParams ?? {}, true);
|
|
9275
|
+
} catch (error) {
|
|
9276
|
+
sessionLog(args.parentSessionId, `stale plugin-build warning delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
9277
|
+
}
|
|
9278
|
+
}
|
|
9279
|
+
async function createChildSessionWithFence(args) {
|
|
9280
|
+
const verdict = probeChildSpawnFence(args.db);
|
|
9281
|
+
if (!verdict.allowSpawn) {
|
|
9282
|
+
if (args.parentSessionId) {
|
|
9283
|
+
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}`);
|
|
9284
|
+
}
|
|
9285
|
+
if (verdict.shouldSurface) {
|
|
9286
|
+
if (args.onFenceLatched)
|
|
9287
|
+
await args.onFenceLatched(verdict.failure);
|
|
9288
|
+
else
|
|
9289
|
+
await surfaceStalePluginBuild(args);
|
|
9290
|
+
}
|
|
9291
|
+
return null;
|
|
9292
|
+
}
|
|
9293
|
+
return args.client.session.create({
|
|
9294
|
+
body: {
|
|
9295
|
+
...args.parentSessionId ? { parentID: args.parentSessionId } : {},
|
|
9296
|
+
title: args.title
|
|
9297
|
+
},
|
|
9298
|
+
query: { directory: args.directory }
|
|
9299
|
+
});
|
|
9300
|
+
}
|
|
9301
|
+
// src/features/magic-context/overflow-detection.ts
|
|
9302
|
+
var OVERFLOW_PATTERNS = [
|
|
9303
|
+
/prompt is too long/i,
|
|
9304
|
+
/input is too long for requested model/i,
|
|
9305
|
+
/exceeds the context window/i,
|
|
9306
|
+
/input token count.*exceeds the maximum/i,
|
|
9307
|
+
/maximum prompt length is \d+/i,
|
|
9308
|
+
/reduce the length of the messages/i,
|
|
9309
|
+
/maximum context length is \d+ tokens/i,
|
|
9310
|
+
/maximum model length is \d+/i,
|
|
9311
|
+
/exceeds the limit of \d+/i,
|
|
9312
|
+
/exceeds the available context size/i,
|
|
9313
|
+
/greater than the context length/i,
|
|
9314
|
+
/context window exceeds limit/i,
|
|
9315
|
+
/exceeded model token limit/i,
|
|
9316
|
+
/context[_ ]length[_ ]exceeded/i,
|
|
9317
|
+
/request entity too large/i,
|
|
9318
|
+
/context length is only \d+ tokens/i,
|
|
9319
|
+
/input length.*exceeds.*context length/i,
|
|
9320
|
+
/prompt too long; exceeded (?:max )?context length/i,
|
|
9321
|
+
/too large for model with \d+ maximum context length/i,
|
|
9322
|
+
/model_context_window_exceeded/i,
|
|
9323
|
+
/context size has been exceeded/i
|
|
9324
|
+
];
|
|
9325
|
+
var LIMIT_EXTRACTION_PATTERNS = [
|
|
9326
|
+
{ pattern: /maximum prompt length is (\d+)/i, provenance: "prompt_only" },
|
|
9327
|
+
{
|
|
9328
|
+
pattern: /maximum context length is (\d+) tokens?/i,
|
|
9329
|
+
provenance: "combined"
|
|
9330
|
+
},
|
|
9331
|
+
{ pattern: /maximum model length is (\d+)/i, provenance: "combined" },
|
|
9332
|
+
{ pattern: /context length is only (\d+) tokens?/i, provenance: "combined" },
|
|
9333
|
+
{ pattern: /exceeds the limit of (\d+)/i, provenance: "unknown" },
|
|
9334
|
+
{
|
|
9335
|
+
pattern: /too large for model with (\d+) maximum context length/i,
|
|
9336
|
+
provenance: "combined"
|
|
9337
|
+
},
|
|
9338
|
+
{ pattern: /context size.*(\d+) tokens?/i, provenance: "combined" },
|
|
9339
|
+
{ pattern: /exceeds? the context length of (\d+)/i, provenance: "combined" },
|
|
9340
|
+
{
|
|
9341
|
+
pattern: />\s*(\d+)\s*(?:tokens?\s*)?(?:maximum|max|limit)\b/i,
|
|
9342
|
+
provenance: "prompt_only"
|
|
9343
|
+
},
|
|
9344
|
+
{ pattern: /max(?:imum)?.*context.*?(\d+)/i, provenance: "unknown" }
|
|
9345
|
+
];
|
|
9346
|
+
var MIN_PLAUSIBLE_LIMIT = 1024;
|
|
9347
|
+
var MAX_PLAUSIBLE_LIMIT = 1e7;
|
|
9348
|
+
function extractErrorMessage(error) {
|
|
9349
|
+
if (!error)
|
|
9350
|
+
return "";
|
|
9351
|
+
if (typeof error === "string")
|
|
9352
|
+
return error;
|
|
9353
|
+
if (typeof error === "object") {
|
|
9354
|
+
const obj = error;
|
|
9355
|
+
const nested = obj.error;
|
|
9356
|
+
if (nested && typeof nested.message === "string" && nested.message.length > 0) {
|
|
9357
|
+
return nested.message;
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
if (error instanceof Error)
|
|
9361
|
+
return error.message;
|
|
9362
|
+
if (typeof error === "object") {
|
|
9363
|
+
const obj = error;
|
|
9364
|
+
if (typeof obj.message === "string")
|
|
9365
|
+
return obj.message;
|
|
9366
|
+
if (typeof obj.responseBody === "string")
|
|
9367
|
+
return obj.responseBody;
|
|
9368
|
+
try {
|
|
9369
|
+
return JSON.stringify(error);
|
|
9370
|
+
} catch {
|
|
9371
|
+
return String(error);
|
|
9372
|
+
}
|
|
9373
|
+
}
|
|
9374
|
+
return String(error);
|
|
9375
|
+
}
|
|
9376
|
+
function detectOverflow(error) {
|
|
9377
|
+
const message = extractErrorMessage(error);
|
|
9378
|
+
if (!message) {
|
|
9379
|
+
return { isOverflow: false };
|
|
9380
|
+
}
|
|
9381
|
+
const hasStatus413 = /\b413\b/.test(message) && /(entity|payload|context|prompt)/i.test(message);
|
|
9382
|
+
let matched;
|
|
9383
|
+
for (const pattern of OVERFLOW_PATTERNS) {
|
|
9384
|
+
if (pattern.test(message)) {
|
|
9385
|
+
matched = pattern;
|
|
9386
|
+
break;
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
if (!matched && !hasStatus413) {
|
|
9390
|
+
return { isOverflow: false };
|
|
9391
|
+
}
|
|
9392
|
+
const reportedLimit = parseReportedLimit(message);
|
|
9393
|
+
return {
|
|
9394
|
+
isOverflow: true,
|
|
9395
|
+
reportedLimit: reportedLimit?.value,
|
|
9396
|
+
reportedLimitProvenance: reportedLimit?.provenance,
|
|
9397
|
+
matchedPattern: matched?.source
|
|
9398
|
+
};
|
|
9399
|
+
}
|
|
9400
|
+
function parseReportedLimit(message) {
|
|
9401
|
+
if (!message)
|
|
9402
|
+
return;
|
|
9403
|
+
for (const { pattern, provenance } of LIMIT_EXTRACTION_PATTERNS) {
|
|
9404
|
+
const match = message.match(pattern);
|
|
9405
|
+
if (!match)
|
|
9406
|
+
continue;
|
|
9407
|
+
const raw = match[1];
|
|
9408
|
+
if (!raw)
|
|
9409
|
+
continue;
|
|
9410
|
+
const value = Number.parseInt(raw, 10);
|
|
9411
|
+
if (!Number.isFinite(value))
|
|
9412
|
+
continue;
|
|
9413
|
+
if (value < MIN_PLAUSIBLE_LIMIT || value > MAX_PLAUSIBLE_LIMIT)
|
|
9414
|
+
continue;
|
|
9415
|
+
return { value, provenance };
|
|
9416
|
+
}
|
|
9417
|
+
return;
|
|
9418
|
+
}
|
|
9419
|
+
|
|
9420
|
+
// src/shared/model-suggestion-retry.ts
|
|
9421
|
+
var ABORT_CALL_TIMEOUT_MS = 3000;
|
|
9422
|
+
function copyPromptArgs(args, body) {
|
|
9423
|
+
return { ...args, body: { ...body } };
|
|
9424
|
+
}
|
|
9425
|
+
function extractMessage(error) {
|
|
9426
|
+
if (typeof error === "string")
|
|
9427
|
+
return error;
|
|
9428
|
+
if (error instanceof Error)
|
|
9429
|
+
return error.message;
|
|
9430
|
+
if (typeof error === "object" && error !== null) {
|
|
9431
|
+
const obj = error;
|
|
9432
|
+
if (typeof obj.message === "string")
|
|
9433
|
+
return obj.message;
|
|
9434
|
+
}
|
|
9435
|
+
try {
|
|
9436
|
+
return JSON.stringify(error);
|
|
9437
|
+
} catch (_error) {
|
|
9438
|
+
return String(error);
|
|
9439
|
+
}
|
|
9440
|
+
}
|
|
9441
|
+
function parseModelSuggestion(error) {
|
|
9442
|
+
if (!error)
|
|
9443
|
+
return null;
|
|
9444
|
+
if (typeof error === "object" && error !== null) {
|
|
9445
|
+
const errObj = error;
|
|
9446
|
+
if (errObj.name === "ProviderModelNotFoundError" && typeof errObj.data === "object" && errObj.data !== null) {
|
|
9447
|
+
const data = errObj.data;
|
|
9448
|
+
const suggestions = data.suggestions;
|
|
9449
|
+
if (Array.isArray(suggestions) && typeof suggestions[0] === "string") {
|
|
9450
|
+
return {
|
|
9451
|
+
providerID: String(data.providerID ?? ""),
|
|
9452
|
+
modelID: String(data.modelID ?? ""),
|
|
9453
|
+
suggestion: suggestions[0]
|
|
9454
|
+
};
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
for (const key of ["data", "error", "cause"]) {
|
|
9458
|
+
const nested = errObj[key];
|
|
9459
|
+
if (nested && typeof nested === "object") {
|
|
9460
|
+
const result = parseModelSuggestion(nested);
|
|
9461
|
+
if (result)
|
|
9462
|
+
return result;
|
|
9463
|
+
}
|
|
9464
|
+
}
|
|
9465
|
+
}
|
|
9466
|
+
const message = extractMessage(error);
|
|
9467
|
+
const modelMatch = message.match(/model not found:\s*([^/\s]+)\s*\/\s*([^.,\s]+)/i);
|
|
9468
|
+
const suggestionMatch = message.match(/did you mean:\s*([^,?]+)/i);
|
|
9469
|
+
if (!modelMatch || !suggestionMatch) {
|
|
9470
|
+
return null;
|
|
9471
|
+
}
|
|
9472
|
+
return {
|
|
9473
|
+
providerID: modelMatch[1].trim(),
|
|
9474
|
+
modelID: modelMatch[2].trim(),
|
|
9475
|
+
suggestion: suggestionMatch[1].trim()
|
|
9476
|
+
};
|
|
9477
|
+
}
|
|
9478
|
+
async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
9479
|
+
if (signal?.aborted) {
|
|
9480
|
+
throw new Error("prompt aborted by external signal");
|
|
9481
|
+
}
|
|
9482
|
+
const controller = new AbortController;
|
|
9483
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
9484
|
+
const onExternalAbort = () => controller.abort();
|
|
9485
|
+
signal?.addEventListener("abort", onExternalAbort);
|
|
9486
|
+
try {
|
|
9487
|
+
await client.session.prompt({
|
|
9488
|
+
...args,
|
|
9489
|
+
signal: controller.signal
|
|
9490
|
+
});
|
|
9491
|
+
} catch (error) {
|
|
9492
|
+
if (signal?.aborted) {
|
|
9493
|
+
await abortChildRun(client, args.path.id);
|
|
9494
|
+
throw new Error("prompt aborted by external signal");
|
|
9495
|
+
}
|
|
9496
|
+
if (controller.signal.aborted) {
|
|
9497
|
+
await abortChildRun(client, args.path.id);
|
|
9498
|
+
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
9499
|
+
}
|
|
9500
|
+
throw error;
|
|
9501
|
+
} finally {
|
|
9502
|
+
clearTimeout(timeout);
|
|
9503
|
+
signal?.removeEventListener("abort", onExternalAbort);
|
|
9504
|
+
}
|
|
9505
|
+
}
|
|
9506
|
+
async function abortChildRun(client, sessionId) {
|
|
9507
|
+
try {
|
|
9508
|
+
await Promise.race([
|
|
9509
|
+
client.session.abort({ path: { id: sessionId } }),
|
|
9510
|
+
new Promise((resolve) => setTimeout(resolve, ABORT_CALL_TIMEOUT_MS))
|
|
9511
|
+
]);
|
|
9512
|
+
} catch (error) {
|
|
9513
|
+
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error)}`);
|
|
9514
|
+
}
|
|
9515
|
+
}
|
|
9516
|
+
function isNonRetryable(error, externalSignal) {
|
|
9517
|
+
if (externalSignal?.aborted)
|
|
9518
|
+
return true;
|
|
9519
|
+
if (error instanceof Error) {
|
|
9520
|
+
if (error.name === "AbortError")
|
|
9521
|
+
return true;
|
|
9522
|
+
if (error.message === "prompt aborted by external signal")
|
|
9523
|
+
return true;
|
|
9524
|
+
if (/^prompt timed out after \d+ms$/.test(error.message))
|
|
9525
|
+
return true;
|
|
9526
|
+
}
|
|
9527
|
+
if (detectOverflow(error).isOverflow)
|
|
9528
|
+
return true;
|
|
9529
|
+
return false;
|
|
9530
|
+
}
|
|
9531
|
+
function shortErr(error) {
|
|
9532
|
+
if (error instanceof Error) {
|
|
9533
|
+
return error.name && error.name !== "Error" ? `${error.name}: ${error.message}` : error.message;
|
|
9534
|
+
}
|
|
9535
|
+
return extractMessage(error);
|
|
9536
|
+
}
|
|
9537
|
+
async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
|
|
9538
|
+
const originalBody = { ...args.body };
|
|
9539
|
+
const attemptArgs = copyPromptArgs(args, originalBody);
|
|
9540
|
+
try {
|
|
9541
|
+
await promptWithTimeout(client, attemptArgs, timeoutMs, signal);
|
|
9542
|
+
return;
|
|
9543
|
+
} catch (error) {
|
|
9544
|
+
if (isNonRetryable(error, signal))
|
|
9545
|
+
throw error;
|
|
9546
|
+
const suggestion = parseModelSuggestion(error);
|
|
9547
|
+
if (!suggestion || !originalBody.model) {
|
|
9548
|
+
throw error;
|
|
9549
|
+
}
|
|
9550
|
+
log(`[${callContext}] ${label}: model not found, retrying with suggestion`, {
|
|
9551
|
+
original: `${suggestion.providerID}/${suggestion.modelID}`,
|
|
9552
|
+
suggested: suggestion.suggestion
|
|
9553
|
+
});
|
|
9554
|
+
await promptWithTimeout(client, copyPromptArgs(args, {
|
|
9555
|
+
...originalBody,
|
|
9556
|
+
model: {
|
|
9557
|
+
providerID: suggestion.providerID,
|
|
9558
|
+
modelID: suggestion.suggestion
|
|
9559
|
+
}
|
|
9560
|
+
}), timeoutMs, signal);
|
|
9561
|
+
}
|
|
9562
|
+
}
|
|
9563
|
+
async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
|
|
9564
|
+
const timeoutMs = options.timeoutMs ?? 300000;
|
|
9565
|
+
const callContext = options.callContext ?? "subagent";
|
|
9566
|
+
const fallbacks = options.fallbackModels ?? [];
|
|
9567
|
+
const baseBody = { ...args.body };
|
|
9568
|
+
const baseArgs = copyPromptArgs(args, baseBody);
|
|
9569
|
+
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
9570
|
+
let lastError = null;
|
|
9571
|
+
try {
|
|
9572
|
+
await attemptOnce(client, baseArgs, timeoutMs, options.signal, callContext, explicitPrimaryLabel);
|
|
9573
|
+
return;
|
|
9574
|
+
} catch (error) {
|
|
9575
|
+
lastError = error;
|
|
9576
|
+
if (isNonRetryable(error, options.signal))
|
|
9577
|
+
throw error;
|
|
9578
|
+
if (fallbacks.length === 0) {
|
|
9579
|
+
throw error;
|
|
9580
|
+
}
|
|
9581
|
+
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
9582
|
+
}
|
|
9583
|
+
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
9584
|
+
const parsed = parseProviderModel(fallbacks[i]);
|
|
9585
|
+
if (!parsed) {
|
|
9586
|
+
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
9587
|
+
continue;
|
|
9588
|
+
}
|
|
9589
|
+
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
9590
|
+
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
9591
|
+
...baseBody,
|
|
9592
|
+
model: parsed
|
|
9593
|
+
});
|
|
9594
|
+
try {
|
|
9595
|
+
await attemptOnce(client, attemptArgs, timeoutMs, options.signal, callContext, label);
|
|
9596
|
+
log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
|
|
9597
|
+
return;
|
|
9598
|
+
} catch (error) {
|
|
9599
|
+
lastError = error;
|
|
9600
|
+
if (isNonRetryable(error, options.signal))
|
|
9601
|
+
throw error;
|
|
9602
|
+
const remaining = fallbacks.length - i - 1;
|
|
9603
|
+
if (remaining > 0) {
|
|
9604
|
+
log(`[${callContext}] ${label} failed: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
9605
|
+
}
|
|
9606
|
+
}
|
|
9607
|
+
}
|
|
9608
|
+
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; last error: ${shortErr(lastError)}`);
|
|
9609
|
+
throw lastError ?? new Error("All fallback models failed");
|
|
9610
|
+
}
|
|
9611
|
+
async function attemptAndValidate(client, args, timeoutMs, signal, callContext, attempt, options) {
|
|
9612
|
+
await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
|
|
9613
|
+
const output = await options.fetchOutput(args, attempt);
|
|
9614
|
+
const validated = await options.validateOutput(output, attempt);
|
|
9615
|
+
return { output, validated, attempt };
|
|
9616
|
+
}
|
|
9617
|
+
async function promptSyncWithValidatedOutputRetry(client, args, options) {
|
|
9618
|
+
const timeoutMs = options.timeoutMs ?? 300000;
|
|
9619
|
+
const callContext = options.callContext ?? "subagent";
|
|
9620
|
+
const fallbacks = options.fallbackModels ?? [];
|
|
9621
|
+
const baseBody = { ...args.body };
|
|
9622
|
+
const baseArgs = copyPromptArgs(args, baseBody);
|
|
9623
|
+
const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
|
|
9624
|
+
const totalAttempts = fallbacks.length + 1;
|
|
9625
|
+
let firstError = null;
|
|
9626
|
+
let lastError = null;
|
|
9627
|
+
try {
|
|
9628
|
+
return await attemptAndValidate(client, baseArgs, timeoutMs, options.signal, callContext, {
|
|
9629
|
+
label: explicitPrimaryLabel,
|
|
9630
|
+
attemptIndex: 0,
|
|
9631
|
+
isFallback: false,
|
|
9632
|
+
totalAttempts,
|
|
9633
|
+
model: baseBody.model
|
|
9634
|
+
}, options);
|
|
9635
|
+
} catch (error) {
|
|
9636
|
+
firstError = error;
|
|
9637
|
+
lastError = error;
|
|
9638
|
+
if (isNonRetryable(error, options.signal))
|
|
9639
|
+
throw error;
|
|
9640
|
+
if (fallbacks.length === 0) {
|
|
9641
|
+
throw error;
|
|
9642
|
+
}
|
|
9643
|
+
log(`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
|
|
9644
|
+
}
|
|
9645
|
+
for (let i = 0;i < fallbacks.length; i += 1) {
|
|
9646
|
+
const parsed = parseProviderModel(fallbacks[i]);
|
|
9647
|
+
if (!parsed) {
|
|
9648
|
+
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
9649
|
+
continue;
|
|
9650
|
+
}
|
|
9651
|
+
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
9652
|
+
const attemptArgs = copyPromptArgs(baseArgs, {
|
|
9653
|
+
...baseBody,
|
|
9654
|
+
model: parsed
|
|
9655
|
+
});
|
|
9656
|
+
const attempt = {
|
|
9657
|
+
label,
|
|
9658
|
+
attemptIndex: i + 1,
|
|
9659
|
+
isFallback: true,
|
|
9660
|
+
totalAttempts,
|
|
9661
|
+
model: parsed
|
|
9662
|
+
};
|
|
9663
|
+
try {
|
|
9664
|
+
const result = await attemptAndValidate(client, attemptArgs, timeoutMs, options.signal, callContext, attempt, options);
|
|
9665
|
+
log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
|
|
9666
|
+
return result;
|
|
9667
|
+
} catch (error) {
|
|
9668
|
+
if (firstError === null)
|
|
9669
|
+
firstError = error;
|
|
9670
|
+
lastError = error;
|
|
9671
|
+
if (isNonRetryable(error, options.signal))
|
|
9672
|
+
throw error;
|
|
9673
|
+
const remaining = fallbacks.length - i - 1;
|
|
9674
|
+
if (remaining > 0) {
|
|
9675
|
+
log(`[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`);
|
|
9676
|
+
}
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9679
|
+
log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`);
|
|
9680
|
+
throw firstError ?? lastError ?? new Error("All fallback models failed validation");
|
|
9681
|
+
}
|
|
9682
|
+
// src/shared/normalize-sdk-response.ts
|
|
9683
|
+
function normalizeSDKResponse(response, fallback, options) {
|
|
9684
|
+
if (response === null || response === undefined) {
|
|
9685
|
+
return fallback;
|
|
9686
|
+
}
|
|
9687
|
+
if (Array.isArray(response)) {
|
|
9688
|
+
return response;
|
|
9689
|
+
}
|
|
9690
|
+
if (typeof response === "object" && "data" in response) {
|
|
9691
|
+
const data = response.data;
|
|
9692
|
+
if (data !== null && data !== undefined) {
|
|
9693
|
+
return data;
|
|
9694
|
+
}
|
|
9695
|
+
if (options?.preferResponseOnMissingData === true) {
|
|
9696
|
+
return response;
|
|
9697
|
+
}
|
|
9698
|
+
return fallback;
|
|
9699
|
+
}
|
|
9700
|
+
if (options?.preferResponseOnMissingData === true) {
|
|
9701
|
+
return response;
|
|
9702
|
+
}
|
|
9703
|
+
return fallback;
|
|
9704
|
+
}
|
|
9705
|
+
// src/shared/assistant-message-extractor.ts
|
|
9706
|
+
function asSessionMessage(value) {
|
|
9707
|
+
if (!isRecord(value))
|
|
9708
|
+
return null;
|
|
9709
|
+
const info = value.info;
|
|
9710
|
+
const parts = value.parts;
|
|
9711
|
+
return {
|
|
9712
|
+
info: isRecord(info) ? {
|
|
9713
|
+
role: typeof info.role === "string" ? info.role : undefined,
|
|
9714
|
+
time: isRecord(info.time) ? {
|
|
9715
|
+
created: typeof info.time.created === "number" ? info.time.created : undefined
|
|
9716
|
+
} : undefined
|
|
9717
|
+
} : undefined,
|
|
9718
|
+
parts
|
|
9719
|
+
};
|
|
9720
|
+
}
|
|
9721
|
+
function getCreatedTime(message) {
|
|
9722
|
+
return message.info?.time?.created ?? 0;
|
|
9723
|
+
}
|
|
9724
|
+
function getTextParts(message) {
|
|
9725
|
+
if (!Array.isArray(message.parts))
|
|
9726
|
+
return [];
|
|
9727
|
+
return message.parts.filter((part) => isRecord(part)).map((part) => ({
|
|
9728
|
+
type: typeof part.type === "string" ? part.type : undefined,
|
|
9729
|
+
text: typeof part.text === "string" ? part.text : undefined
|
|
9730
|
+
})).filter((part) => part.type === "text" && Boolean(part.text));
|
|
9731
|
+
}
|
|
9732
|
+
function extractLatestAssistantText(messages) {
|
|
9733
|
+
if (!Array.isArray(messages) || messages.length === 0)
|
|
9734
|
+
return null;
|
|
9735
|
+
const assistantMessages = messages.map(asSessionMessage).filter((message) => message !== null).filter((message) => message.info?.role === "assistant").sort((a, b) => getCreatedTime(b) - getCreatedTime(a));
|
|
9736
|
+
const latest = assistantMessages[0];
|
|
9737
|
+
if (!latest)
|
|
9738
|
+
return null;
|
|
9739
|
+
return getTextParts(latest).map((part) => part.text).join(`
|
|
9740
|
+
`) || null;
|
|
9741
|
+
}
|
|
9742
|
+
function hasLengthCappedOutput(value) {
|
|
9743
|
+
if (Array.isArray(value))
|
|
9744
|
+
return value.some((item) => hasLengthCappedOutput(item));
|
|
9745
|
+
if (!isRecord(value))
|
|
9746
|
+
return false;
|
|
9747
|
+
if (value.length_capped === true || value.lengthCapped === true)
|
|
9748
|
+
return true;
|
|
9749
|
+
const finishReason = value.finish_reason ?? value.finishReason;
|
|
9750
|
+
if (typeof finishReason === "string") {
|
|
9751
|
+
const normalized = finishReason.toLowerCase();
|
|
9752
|
+
if (normalized === "length" || normalized === "max_tokens" || normalized === "max_output_tokens") {
|
|
9753
|
+
return true;
|
|
9754
|
+
}
|
|
9755
|
+
}
|
|
9756
|
+
return Object.values(value).some((item) => hasLengthCappedOutput(item));
|
|
9757
|
+
}
|
|
9758
|
+
|
|
9759
|
+
// src/shared/keep-subagents.ts
|
|
9760
|
+
var keepSubagents = false;
|
|
9761
|
+
function setKeepSubagents(value) {
|
|
9762
|
+
keepSubagents = value === true;
|
|
9763
|
+
}
|
|
9764
|
+
function shouldKeepSubagents() {
|
|
9765
|
+
return keepSubagents;
|
|
9766
|
+
}
|
|
9767
|
+
|
|
9213
9768
|
// src/features/magic-context/user-memory/storage-user-memory.ts
|
|
9214
9769
|
var USER_MEMORY_CANDIDATE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
9215
9770
|
function insertUserMemoryCandidates(db, candidates) {
|
|
@@ -9277,9 +9832,9 @@ function parseUserMemoryRow(row) {
|
|
|
9277
9832
|
}
|
|
9278
9833
|
|
|
9279
9834
|
// src/features/magic-context/mural/storage-mural-cues.ts
|
|
9280
|
-
import { createHash as
|
|
9835
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
9281
9836
|
function computeCueContentHash(content) {
|
|
9282
|
-
return
|
|
9837
|
+
return createHash8("sha256").update(content).digest("hex");
|
|
9283
9838
|
}
|
|
9284
9839
|
var muralCueColumnCache = new WeakMap;
|
|
9285
9840
|
function hasMuralCueColumns(db) {
|
|
@@ -9305,10 +9860,10 @@ function getMuralCueState(db, memoryIds) {
|
|
|
9305
9860
|
}
|
|
9306
9861
|
return out;
|
|
9307
9862
|
}
|
|
9308
|
-
function memoryNeedsCue(
|
|
9309
|
-
if (!
|
|
9863
|
+
function memoryNeedsCue(state2, currentContent) {
|
|
9864
|
+
if (!state2 || state2.cue === null || state2.hash === null)
|
|
9310
9865
|
return true;
|
|
9311
|
-
return
|
|
9866
|
+
return state2.hash !== computeCueContentHash(currentContent);
|
|
9312
9867
|
}
|
|
9313
9868
|
function setMuralCue(db, projectPath, id, cue, contentHash) {
|
|
9314
9869
|
if (!hasMuralCueColumns(db))
|
|
@@ -9330,7 +9885,6 @@ var deleteEmbeddingStatements = new WeakMap;
|
|
|
9330
9885
|
var getStoredModelIdStatements = new WeakMap;
|
|
9331
9886
|
var clearAllEmbeddingsStatements = new WeakMap;
|
|
9332
9887
|
var clearModelEmbeddingsStatements = new WeakMap;
|
|
9333
|
-
var getDistinctStoredModelIdsStatements = new WeakMap;
|
|
9334
9888
|
function isEmbeddingBlob(value) {
|
|
9335
9889
|
return value instanceof Uint8Array || value instanceof ArrayBuffer;
|
|
9336
9890
|
}
|
|
@@ -9447,13 +10001,13 @@ function invalidateMemory(projectPath, memoryId) {
|
|
|
9447
10001
|
}
|
|
9448
10002
|
|
|
9449
10003
|
// src/features/magic-context/memory/normalize-hash.ts
|
|
9450
|
-
import { createHash as
|
|
10004
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
9451
10005
|
function normalizeMemoryContent(content) {
|
|
9452
10006
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
9453
10007
|
}
|
|
9454
10008
|
function computeNormalizedHash(content) {
|
|
9455
10009
|
const normalized = normalizeMemoryContent(content);
|
|
9456
|
-
return
|
|
10010
|
+
return createHash9("md5").update(normalized).digest("hex");
|
|
9457
10011
|
}
|
|
9458
10012
|
|
|
9459
10013
|
// src/features/magic-context/memory/visibility.ts
|
|
@@ -10317,9 +10871,12 @@ async function runMemoryMigration(deps) {
|
|
|
10317
10871
|
const modelOverride = modelId ? parseProviderModel(modelId) : null;
|
|
10318
10872
|
await cleanupChildSession(agentSessionId);
|
|
10319
10873
|
agentSessionId = null;
|
|
10320
|
-
const createResponse = await
|
|
10321
|
-
|
|
10322
|
-
|
|
10874
|
+
const createResponse = await createChildSessionWithFence({
|
|
10875
|
+
client,
|
|
10876
|
+
db,
|
|
10877
|
+
parentSessionId,
|
|
10878
|
+
title: "magic-context-memory-migration",
|
|
10879
|
+
directory
|
|
10323
10880
|
});
|
|
10324
10881
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
10325
10882
|
preferResponseOnMissingData: true
|
|
@@ -10419,7 +10976,7 @@ async function runMemoryMigration(deps) {
|
|
|
10419
10976
|
}
|
|
10420
10977
|
|
|
10421
10978
|
// src/features/magic-context/compartment-chunk-embedding.ts
|
|
10422
|
-
import { createHash as
|
|
10979
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
10423
10980
|
|
|
10424
10981
|
// src/features/magic-context/recursive-text-splitter.ts
|
|
10425
10982
|
var DEFAULT_SEPARATORS = [`
|
|
@@ -10514,9 +11071,6 @@ var existingHashStatements = new WeakMap;
|
|
|
10514
11071
|
var existingHashByProjectStatements = new WeakMap;
|
|
10515
11072
|
var deleteByCompartmentStatements = new WeakMap;
|
|
10516
11073
|
var insertEmbeddingStatements = new WeakMap;
|
|
10517
|
-
var distinctModelStatements = new WeakMap;
|
|
10518
|
-
var clearProjectStatements = new WeakMap;
|
|
10519
|
-
var clearProjectModelStatements = new WeakMap;
|
|
10520
11074
|
var searchRowsStatements = new WeakMap;
|
|
10521
11075
|
var searchRowsByModelStatements = new WeakMap;
|
|
10522
11076
|
var searchPoolProbeStatements = new WeakMap;
|
|
@@ -10707,7 +11261,7 @@ function parseCanonicalLineRange(line) {
|
|
|
10707
11261
|
return { start, end };
|
|
10708
11262
|
}
|
|
10709
11263
|
function hashChunkText(text) {
|
|
10710
|
-
return
|
|
11264
|
+
return createHash10("sha256").update(text).digest("hex");
|
|
10711
11265
|
}
|
|
10712
11266
|
function vectorBlob2(vector) {
|
|
10713
11267
|
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
@@ -11098,7 +11652,7 @@ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelI
|
|
|
11098
11652
|
}
|
|
11099
11653
|
|
|
11100
11654
|
// src/features/magic-context/project-embedding-registry.ts
|
|
11101
|
-
import { createHash as
|
|
11655
|
+
import { createHash as createHash12, randomUUID as randomUUID2 } from "node:crypto";
|
|
11102
11656
|
|
|
11103
11657
|
// src/config/schema/magic-context.ts
|
|
11104
11658
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -25550,7 +26104,7 @@ var AgentOverrideConfigSchema = exports_external.object({
|
|
|
25550
26104
|
|
|
25551
26105
|
// src/config/schema/magic-context.ts
|
|
25552
26106
|
var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
|
|
25553
|
-
var EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at
|
|
26107
|
+
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.";
|
|
25554
26108
|
var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
|
|
25555
26109
|
var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
|
|
25556
26110
|
var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
@@ -25648,7 +26202,22 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
25648
26202
|
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."),
|
|
25649
26203
|
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."),
|
|
25650
26204
|
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."),
|
|
25651
|
-
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
26205
|
+
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted."),
|
|
26206
|
+
local_dtype: exports_external.enum([
|
|
26207
|
+
"auto",
|
|
26208
|
+
"fp32",
|
|
26209
|
+
"fp16",
|
|
26210
|
+
"q8",
|
|
26211
|
+
"int8",
|
|
26212
|
+
"uint8",
|
|
26213
|
+
"q4",
|
|
26214
|
+
"bnb4",
|
|
26215
|
+
"q4f16",
|
|
26216
|
+
"q2",
|
|
26217
|
+
"q2f16",
|
|
26218
|
+
"q1",
|
|
26219
|
+
"q1f16"
|
|
26220
|
+
]).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.")
|
|
25652
26221
|
}).superRefine((data, ctx) => {
|
|
25653
26222
|
const validationProvider = data.provider === "synapse" ? data.fallback_provider : data.provider;
|
|
25654
26223
|
if (validationProvider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
@@ -25690,7 +26259,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
25690
26259
|
return {
|
|
25691
26260
|
provider: "local",
|
|
25692
26261
|
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
25693
|
-
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
26262
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {},
|
|
26263
|
+
...data.local_dtype ? { local_dtype: data.local_dtype } : {}
|
|
25694
26264
|
};
|
|
25695
26265
|
}
|
|
25696
26266
|
if (data.provider === "openai-compatible") {
|
|
@@ -25713,26 +26283,29 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
25713
26283
|
});
|
|
25714
26284
|
var MagicContextConfigSchema = exports_external.object({
|
|
25715
26285
|
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
25716
|
-
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
}).default({ mural: { enabled: false } }).describe("Experimental feature switches. User and project configuration are both accepted."),
|
|
26286
|
+
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."),
|
|
26287
|
+
mural: exports_external.object({
|
|
26288
|
+
enabled: exports_external.boolean().default(false),
|
|
26289
|
+
model: exports_external.string().trim().min(1).optional().describe("Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model).")
|
|
26290
|
+
}).default({ enabled: false }).describe("Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task."),
|
|
25722
26291
|
transform_mode: exports_external.enum(["ts", "rust"]).default("ts").describe('Experimental: routes the entire Magic Context runtime for the project through the ck-mc Rust module over subc (requires user-level `subc` config); "ts" is the current TypeScript pipeline.'),
|
|
25723
26292
|
auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
|
|
25724
26293
|
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."),
|
|
25725
26294
|
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
25726
26295
|
dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
|
|
25727
|
-
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" })'),
|
|
26296
|
+
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.'),
|
|
26297
|
+
output_reserve: exports_external.union([
|
|
26298
|
+
exports_external.number().min(0),
|
|
26299
|
+
exports_external.object({ default: exports_external.number().min(0) }).catchall(exports_external.number().min(0))
|
|
26300
|
+
]).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.'),
|
|
25728
26301
|
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)"),
|
|
25729
26302
|
execute_threshold_percentage: exports_external.union([
|
|
25730
|
-
exports_external.number().min(20).max(
|
|
25731
|
-
exports_external.object({ default: exports_external.number().min(20).max(
|
|
25732
|
-
]).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
|
|
26303
|
+
exports_external.number().min(20).max(90, EXECUTE_THRESHOLD_CAP_MESSAGE),
|
|
26304
|
+
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))
|
|
26305
|
+
]).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'),
|
|
25733
26306
|
execute_threshold_tokens: exports_external.object({
|
|
25734
26307
|
default: exports_external.number().min(5000).max(2000000).optional()
|
|
25735
|
-
}).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
|
|
26308
|
+
}).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."),
|
|
25736
26309
|
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)"),
|
|
25737
26310
|
clear_reasoning_age: exports_external.number().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
|
|
25738
26311
|
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)"),
|
|
@@ -25752,6 +26325,9 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
25752
26325
|
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)"),
|
|
25753
26326
|
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)")
|
|
25754
26327
|
}).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."),
|
|
26328
|
+
storage: exports_external.object({
|
|
26329
|
+
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.")
|
|
26330
|
+
}).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."),
|
|
25755
26331
|
embedding: EmbeddingConfigSchema.default({
|
|
25756
26332
|
provider: "local",
|
|
25757
26333
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
@@ -25765,6 +26341,9 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
25765
26341
|
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).'),
|
|
25766
26342
|
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."),
|
|
25767
26343
|
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."),
|
|
26344
|
+
compaction: exports_external.object({
|
|
26345
|
+
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.")
|
|
26346
|
+
}).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."),
|
|
25768
26347
|
todowrite: exports_external.object({
|
|
25769
26348
|
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."),
|
|
25770
26349
|
overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
|
|
@@ -25811,9 +26390,6 @@ var saveStatements = new WeakMap;
|
|
|
25811
26390
|
var loadProjectStatements = new WeakMap;
|
|
25812
26391
|
var loadUnembeddedStatements = new WeakMap;
|
|
25813
26392
|
var countEmbeddedStatements = new WeakMap;
|
|
25814
|
-
var clearProjectStatements2 = new WeakMap;
|
|
25815
|
-
var clearProjectModelStatements2 = new WeakMap;
|
|
25816
|
-
var distinctModelIdStatements = new WeakMap;
|
|
25817
26393
|
function getSaveStatement(db) {
|
|
25818
26394
|
let stmt = saveStatements.get(db);
|
|
25819
26395
|
if (!stmt) {
|
|
@@ -25884,7 +26460,6 @@ function countEmbeddedCommits(db, projectPath, modelId) {
|
|
|
25884
26460
|
var insertStatements = new WeakMap;
|
|
25885
26461
|
var existingShasStatements = new WeakMap;
|
|
25886
26462
|
var projectCountStatements = new WeakMap;
|
|
25887
|
-
var evictStatements = new WeakMap;
|
|
25888
26463
|
var evictOverflowStatements = new WeakMap;
|
|
25889
26464
|
var latestCommitTimeStatements = new WeakMap;
|
|
25890
26465
|
function getInsertStatement(db) {
|
|
@@ -26126,7 +26701,7 @@ function releaseGitSweepLease(db, projectPath, holderId) {
|
|
|
26126
26701
|
}
|
|
26127
26702
|
|
|
26128
26703
|
// src/features/magic-context/memory/embedding-synapse.ts
|
|
26129
|
-
import { createHash as
|
|
26704
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
26130
26705
|
|
|
26131
26706
|
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
26132
26707
|
import { promises as fs2 } from "node:fs";
|
|
@@ -28339,7 +28914,7 @@ function wait(ms) {
|
|
|
28339
28914
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
28340
28915
|
}
|
|
28341
28916
|
function sha256(value) {
|
|
28342
|
-
return
|
|
28917
|
+
return createHash11("sha256").update(value).digest("hex");
|
|
28343
28918
|
}
|
|
28344
28919
|
function stableJson(value) {
|
|
28345
28920
|
if (Array.isArray(value))
|
|
@@ -28387,7 +28962,7 @@ function extractCatalogEntries(value) {
|
|
|
28387
28962
|
const batchRecord = asRecord(rawBatch);
|
|
28388
28963
|
const recommendedBatch = typeof rawBatch === "number" ? rawBatch : batchRecord ? batchRecord.rows : undefined;
|
|
28389
28964
|
const recommendedTokenBudget = batchRecord ? batchRecord.token_budget : undefined;
|
|
28390
|
-
const
|
|
28965
|
+
const state2 = typeof record2.state === "string" ? record2.state : undefined;
|
|
28391
28966
|
return [
|
|
28392
28967
|
{
|
|
28393
28968
|
model,
|
|
@@ -28398,7 +28973,7 @@ function extractCatalogEntries(value) {
|
|
|
28398
28973
|
...typeof recommendedTokenBudget === "number" && recommendedTokenBudget > 0 ? { recommended_token_budget: Math.floor(recommendedTokenBudget) } : {},
|
|
28399
28974
|
...record2.provenance !== undefined ? { provenance: record2.provenance } : {},
|
|
28400
28975
|
...typeof record2.certified === "boolean" ? { certified: record2.certified } : {},
|
|
28401
|
-
...typeof record2.status === "string" ? { status: record2.status } :
|
|
28976
|
+
...typeof record2.status === "string" ? { status: record2.status } : state2 ? { status: state2 } : {}
|
|
28402
28977
|
}
|
|
28403
28978
|
];
|
|
28404
28979
|
});
|
|
@@ -28784,6 +29359,7 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
28784
29359
|
throw new Error("Unknown embedding provider");
|
|
28785
29360
|
}
|
|
28786
29361
|
const truncate = config2.provider === "openai-compatible" ? config2.truncate?.trim() : undefined;
|
|
29362
|
+
const localDtype = config2.provider === "local" && config2.local_dtype && config2.local_dtype !== "fp32" ? config2.local_dtype : undefined;
|
|
28787
29363
|
const identityInput = config2.provider === "openai-compatible" ? {
|
|
28788
29364
|
provider: "openai-compatible",
|
|
28789
29365
|
model: config2.model.trim(),
|
|
@@ -28795,7 +29371,8 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
28795
29371
|
provider: "local",
|
|
28796
29372
|
model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
28797
29373
|
endpoint: "",
|
|
28798
|
-
apiKeyPresent: false
|
|
29374
|
+
apiKeyPresent: false,
|
|
29375
|
+
...localDtype ? { localDtype } : {}
|
|
28799
29376
|
};
|
|
28800
29377
|
return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
|
|
28801
29378
|
}
|
|
@@ -28803,7 +29380,7 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
28803
29380
|
// src/features/magic-context/memory/embedding-local.ts
|
|
28804
29381
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
28805
29382
|
import { open, stat, unlink, writeFile } from "node:fs/promises";
|
|
28806
|
-
import { dirname as dirname2, join as
|
|
29383
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
28807
29384
|
import { pathToFileURL } from "node:url";
|
|
28808
29385
|
var LOCK_POLL_MS = 150;
|
|
28809
29386
|
var STALE_LOCK_MS = 3 * 60000;
|
|
@@ -28883,6 +29460,7 @@ async function injectWasmOrtForElectron() {
|
|
|
28883
29460
|
return false;
|
|
28884
29461
|
}
|
|
28885
29462
|
}
|
|
29463
|
+
var DEFAULT_LOCAL_DTYPE = "fp32";
|
|
28886
29464
|
async function withQuietConsole(fn) {
|
|
28887
29465
|
const origWarn = console.warn;
|
|
28888
29466
|
const origError = console.error;
|
|
@@ -28959,16 +29537,22 @@ class LocalEmbeddingProvider {
|
|
|
28959
29537
|
modelId;
|
|
28960
29538
|
maxInputTokens;
|
|
28961
29539
|
model;
|
|
29540
|
+
dtype;
|
|
28962
29541
|
pipeline = null;
|
|
28963
29542
|
initPromise = null;
|
|
28964
29543
|
inFlight = 0;
|
|
28965
29544
|
disposing = false;
|
|
28966
29545
|
disposePromise = null;
|
|
28967
29546
|
inFlightWaiters = [];
|
|
28968
|
-
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
|
|
29547
|
+
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512, dtype = DEFAULT_LOCAL_DTYPE) {
|
|
28969
29548
|
this.model = model;
|
|
28970
29549
|
this.maxInputTokens = maxInputTokens;
|
|
28971
|
-
this.
|
|
29550
|
+
this.dtype = dtype || DEFAULT_LOCAL_DTYPE;
|
|
29551
|
+
this.modelId = getEmbeddingProviderIdentity({
|
|
29552
|
+
provider: "local",
|
|
29553
|
+
model,
|
|
29554
|
+
...dtype && dtype !== DEFAULT_LOCAL_DTYPE ? { local_dtype: dtype } : {}
|
|
29555
|
+
});
|
|
28972
29556
|
}
|
|
28973
29557
|
async initialize() {
|
|
28974
29558
|
if (this.disposing) {
|
|
@@ -28997,20 +29581,24 @@ class LocalEmbeddingProvider {
|
|
|
28997
29581
|
if (LogLevel && "ERROR" in LogLevel) {
|
|
28998
29582
|
env.logLevel = LogLevel.ERROR;
|
|
28999
29583
|
}
|
|
29000
|
-
const modelCacheDir =
|
|
29584
|
+
const modelCacheDir = join4(getMagicContextStorageDir(), "models");
|
|
29001
29585
|
try {
|
|
29002
|
-
|
|
29003
|
-
|
|
29004
|
-
|
|
29005
|
-
|
|
29006
|
-
|
|
29586
|
+
if (shouldEnforcePrivateStoragePermissions()) {
|
|
29587
|
+
mkdirSync2(modelCacheDir, { recursive: true, mode: 448 });
|
|
29588
|
+
if (process.platform !== "win32") {
|
|
29589
|
+
try {
|
|
29590
|
+
chmodSync2(modelCacheDir, 448);
|
|
29591
|
+
} catch {}
|
|
29592
|
+
}
|
|
29593
|
+
} else {
|
|
29594
|
+
mkdirSync2(modelCacheDir, { recursive: true });
|
|
29007
29595
|
}
|
|
29008
29596
|
env.cacheDir = modelCacheDir;
|
|
29009
29597
|
} catch {
|
|
29010
29598
|
log("[magic-context] could not create model cache dir, using library default");
|
|
29011
29599
|
}
|
|
29012
29600
|
const createPipeline = transformersModule.pipeline;
|
|
29013
|
-
const lockPath =
|
|
29601
|
+
const lockPath = join4(modelCacheDir, ".load.lock");
|
|
29014
29602
|
const releaseLock = await acquireModelLoadLock(lockPath);
|
|
29015
29603
|
const stopHeartbeat = startLockHeartbeat(lockPath);
|
|
29016
29604
|
try {
|
|
@@ -29019,7 +29607,7 @@ class LocalEmbeddingProvider {
|
|
|
29019
29607
|
for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
|
|
29020
29608
|
try {
|
|
29021
29609
|
const pipeline = await withQuietConsole(() => createPipeline("feature-extraction", this.model, {
|
|
29022
|
-
dtype:
|
|
29610
|
+
dtype: this.dtype,
|
|
29023
29611
|
...injectedWasmOrt ? { device: "auto" } : {}
|
|
29024
29612
|
}));
|
|
29025
29613
|
if (this.disposing) {
|
|
@@ -29670,7 +30258,8 @@ function resolveEmbeddingConfig(config2) {
|
|
|
29670
30258
|
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
29671
30259
|
...config2?.max_input_tokens ? {
|
|
29672
30260
|
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
29673
|
-
} : {}
|
|
30261
|
+
} : {},
|
|
30262
|
+
...config2?.local_dtype ? { local_dtype: config2.local_dtype } : {}
|
|
29674
30263
|
};
|
|
29675
30264
|
}
|
|
29676
30265
|
if (config2.provider === "openai-compatible") {
|
|
@@ -29729,7 +30318,7 @@ function createProvider(config2, context) {
|
|
|
29729
30318
|
});
|
|
29730
30319
|
}
|
|
29731
30320
|
if (config2.provider === "local") {
|
|
29732
|
-
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
30321
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens, config2.local_dtype);
|
|
29733
30322
|
}
|
|
29734
30323
|
if (config2.provider === "synapse") {
|
|
29735
30324
|
const synapse = config2;
|
|
@@ -29758,10 +30347,10 @@ function stableStringify2(value) {
|
|
|
29758
30347
|
return JSON.stringify(value);
|
|
29759
30348
|
}
|
|
29760
30349
|
function sha256Prefix(value, length = 16) {
|
|
29761
|
-
return
|
|
30350
|
+
return createHash12("sha256").update(value).digest("hex").slice(0, length);
|
|
29762
30351
|
}
|
|
29763
30352
|
function contentSha256(value) {
|
|
29764
|
-
return
|
|
30353
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
29765
30354
|
}
|
|
29766
30355
|
function getRuntimeFingerprint(config2) {
|
|
29767
30356
|
if (config2.provider === "off") {
|
|
@@ -31036,12 +31625,12 @@ function parseFields(json2) {
|
|
|
31036
31625
|
}
|
|
31037
31626
|
|
|
31038
31627
|
// src/hooks/magic-context/historian-state-file.ts
|
|
31039
|
-
import { mkdirSync as mkdirSync3, unlinkSync, writeFileSync } from "node:fs";
|
|
31628
|
+
import { mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
31040
31629
|
function cleanupHistorianStateFile(path3) {
|
|
31041
31630
|
if (!path3)
|
|
31042
31631
|
return;
|
|
31043
31632
|
try {
|
|
31044
|
-
|
|
31633
|
+
unlinkSync2(path3);
|
|
31045
31634
|
} catch {}
|
|
31046
31635
|
}
|
|
31047
31636
|
// src/features/magic-context/memory/cosine-similarity.ts
|
|
@@ -31084,7 +31673,7 @@ function createProvider2(config2) {
|
|
|
31084
31673
|
});
|
|
31085
31674
|
}
|
|
31086
31675
|
if (config2.provider === "local") {
|
|
31087
|
-
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
31676
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens, config2.local_dtype);
|
|
31088
31677
|
}
|
|
31089
31678
|
if (config2.provider === "synapse") {
|
|
31090
31679
|
const synapse = config2;
|
|
@@ -31356,8 +31945,8 @@ function getMemoryVerifications(db, memoryIds) {
|
|
|
31356
31945
|
existing.mappedAt = Math.max(existing.mappedAt, row.mapped_at ?? 0);
|
|
31357
31946
|
result.set(row.memory_id, existing);
|
|
31358
31947
|
}
|
|
31359
|
-
for (const
|
|
31360
|
-
|
|
31948
|
+
for (const state2 of result.values()) {
|
|
31949
|
+
state2.files.sort();
|
|
31361
31950
|
}
|
|
31362
31951
|
return result;
|
|
31363
31952
|
}
|
|
@@ -31594,18 +32183,18 @@ function tallyFactsByCategory(facts) {
|
|
|
31594
32183
|
}
|
|
31595
32184
|
|
|
31596
32185
|
// src/hooks/magic-context/compaction-marker-manager.ts
|
|
31597
|
-
import { join as
|
|
32186
|
+
import { join as join6 } from "node:path";
|
|
31598
32187
|
|
|
31599
32188
|
// src/features/magic-context/compaction-marker.ts
|
|
31600
|
-
import { createHash as
|
|
32189
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
31601
32190
|
import { existsSync as existsSync5 } from "node:fs";
|
|
31602
|
-
import { join as
|
|
32191
|
+
import { join as join5 } from "node:path";
|
|
31603
32192
|
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
31604
32193
|
var ID_PREFIX_HEX_LENGTH = 12;
|
|
31605
32194
|
var ID_SUFFIX_LENGTH = 14;
|
|
31606
32195
|
var ID_PREFIX_MASK = (1n << BigInt(ID_PREFIX_HEX_LENGTH * 4)) - 1n;
|
|
31607
32196
|
function deterministicBase62(seed, length) {
|
|
31608
|
-
let value = BigInt(`0x${
|
|
32197
|
+
let value = BigInt(`0x${createHash13("sha256").update(seed).digest("hex")}`);
|
|
31609
32198
|
const chars = Array(length);
|
|
31610
32199
|
for (let index = length - 1;index >= 0; index -= 1) {
|
|
31611
32200
|
chars[index] = BASE62_CHARS[Number(value % 62n)];
|
|
@@ -31625,7 +32214,7 @@ function generatePartId(timestampMs, counter = 0n, identity = "") {
|
|
|
31625
32214
|
return generateId("prt", timestampMs, counter, identity);
|
|
31626
32215
|
}
|
|
31627
32216
|
function getOpenCodeDbPath() {
|
|
31628
|
-
return
|
|
32217
|
+
return join5(getDataDir(), "opencode", "opencode.db");
|
|
31629
32218
|
}
|
|
31630
32219
|
var cachedWriteDb = null;
|
|
31631
32220
|
var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
|
|
@@ -31885,13 +32474,176 @@ function removeForeignCompactionMarker(sessionId, marker, protectedSummaryMessag
|
|
|
31885
32474
|
return false;
|
|
31886
32475
|
}
|
|
31887
32476
|
}
|
|
31888
|
-
function
|
|
32477
|
+
function dataReferencesTailStart(data) {
|
|
32478
|
+
if (typeof data !== "object" || data === null)
|
|
32479
|
+
return null;
|
|
32480
|
+
const record2 = data;
|
|
32481
|
+
if (typeof record2.tail_start_id === "string" && record2.tail_start_id.length > 0) {
|
|
32482
|
+
return record2.tail_start_id;
|
|
32483
|
+
}
|
|
32484
|
+
const nested = record2.compaction;
|
|
32485
|
+
if (typeof nested === "object" && nested !== null) {
|
|
32486
|
+
const nestedTail = nested.tail_start_id;
|
|
32487
|
+
if (typeof nestedTail === "string" && nestedTail.length > 0)
|
|
32488
|
+
return nestedTail;
|
|
32489
|
+
}
|
|
32490
|
+
return null;
|
|
32491
|
+
}
|
|
32492
|
+
function isMcCanonicalCompactionPartData(data) {
|
|
32493
|
+
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
32494
|
+
return false;
|
|
32495
|
+
const record2 = data;
|
|
32496
|
+
const keys = Object.keys(record2).sort();
|
|
32497
|
+
return keys.length === 2 && keys[0] === "auto" && keys[1] === "type" && record2.type === "compaction" && record2.auto === true;
|
|
32498
|
+
}
|
|
32499
|
+
function removeMcOwnedCompactionMarkers(sessionId, summaryText) {
|
|
32500
|
+
const db = getWritableOpenCodeDb();
|
|
32501
|
+
if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath())) {
|
|
32502
|
+
return {
|
|
32503
|
+
verified: false,
|
|
32504
|
+
removedLineages: 0,
|
|
32505
|
+
removedRows: 0,
|
|
32506
|
+
retainedLineages: 0
|
|
32507
|
+
};
|
|
32508
|
+
}
|
|
32509
|
+
const canonicalSummaries = db.prepare(`SELECT id, COALESCE(json_extract(data, '$.parentID'), '') AS parent_id
|
|
32510
|
+
FROM message
|
|
32511
|
+
WHERE session_id = ?
|
|
32512
|
+
AND COALESCE(json_extract(data, '$.summary'), 0) = 1
|
|
32513
|
+
AND COALESCE(json_extract(data, '$.finish'), '') = 'stop'
|
|
32514
|
+
AND COALESCE(json_extract(data, '$.providerID'), '') = 'magic-context'`).all(sessionId);
|
|
32515
|
+
const legacySummaries = db.prepare(`SELECT m.id, COALESCE(json_extract(m.data, '$.parentID'), '') AS parent_id
|
|
32516
|
+
FROM message m
|
|
32517
|
+
WHERE m.session_id = ?
|
|
32518
|
+
AND COALESCE(json_extract(m.data, '$.summary'), 0) = 1
|
|
32519
|
+
AND COALESCE(json_extract(m.data, '$.finish'), '') = 'stop'
|
|
32520
|
+
AND COALESCE(json_extract(m.data, '$.providerID'), '') <> 'magic-context'
|
|
32521
|
+
AND EXISTS (
|
|
32522
|
+
SELECT 1
|
|
32523
|
+
FROM part p
|
|
32524
|
+
WHERE p.session_id = m.session_id
|
|
32525
|
+
AND p.message_id = m.id
|
|
32526
|
+
AND COALESCE(json_extract(p.data, '$.type'), '') = 'text'
|
|
32527
|
+
AND COALESCE(json_extract(p.data, '$.text'), '') = ?
|
|
32528
|
+
)`).all(sessionId, summaryText);
|
|
32529
|
+
const summariesByBoundary = new Map;
|
|
32530
|
+
const orphanSummaryIds = new Set;
|
|
32531
|
+
for (const row of [...canonicalSummaries, ...legacySummaries]) {
|
|
32532
|
+
if (typeof row.id !== "string")
|
|
32533
|
+
continue;
|
|
32534
|
+
if (typeof row.parent_id === "string" && row.parent_id.length > 0) {
|
|
32535
|
+
const set2 = summariesByBoundary.get(row.parent_id) ?? new Set;
|
|
32536
|
+
set2.add(row.id);
|
|
32537
|
+
summariesByBoundary.set(row.parent_id, set2);
|
|
32538
|
+
} else {
|
|
32539
|
+
orphanSummaryIds.add(row.id);
|
|
32540
|
+
}
|
|
32541
|
+
}
|
|
32542
|
+
const compactionParts = db.prepare(`SELECT id, message_id, data
|
|
32543
|
+
FROM part
|
|
32544
|
+
WHERE session_id = ?
|
|
32545
|
+
AND COALESCE(json_extract(data, '$.type'), '') = 'compaction'`).all(sessionId);
|
|
32546
|
+
const parsedParts = [];
|
|
32547
|
+
for (const part of compactionParts) {
|
|
32548
|
+
if (typeof part.id !== "string" || typeof part.message_id !== "string")
|
|
32549
|
+
continue;
|
|
32550
|
+
let data;
|
|
32551
|
+
try {
|
|
32552
|
+
data = typeof part.data === "string" ? JSON.parse(part.data) : part.data;
|
|
32553
|
+
} catch {
|
|
32554
|
+
data = null;
|
|
32555
|
+
}
|
|
32556
|
+
parsedParts.push({
|
|
32557
|
+
id: part.id,
|
|
32558
|
+
messageId: part.message_id,
|
|
32559
|
+
data,
|
|
32560
|
+
tailStartId: dataReferencesTailStart(data)
|
|
32561
|
+
});
|
|
32562
|
+
}
|
|
32563
|
+
const messageTailRefs = db.prepare(`SELECT id, data
|
|
32564
|
+
FROM message
|
|
32565
|
+
WHERE session_id = ?
|
|
32566
|
+
AND json_extract(data, '$.compaction.tail_start_id') IS NOT NULL`).all(sessionId);
|
|
32567
|
+
const messageTailStartIds = new Set;
|
|
32568
|
+
for (const row of messageTailRefs) {
|
|
32569
|
+
if (typeof row.id !== "string")
|
|
32570
|
+
continue;
|
|
32571
|
+
let data;
|
|
32572
|
+
try {
|
|
32573
|
+
data = typeof row.data === "string" ? JSON.parse(row.data) : row.data;
|
|
32574
|
+
} catch {
|
|
32575
|
+
data = null;
|
|
32576
|
+
}
|
|
32577
|
+
const ref = dataReferencesTailStart(data);
|
|
32578
|
+
if (ref)
|
|
32579
|
+
messageTailStartIds.add(ref);
|
|
32580
|
+
}
|
|
32581
|
+
let removedLineages = 0;
|
|
32582
|
+
let removedRows = 0;
|
|
32583
|
+
let retainedLineages = 0;
|
|
32584
|
+
const deletePartsOfMessage = db.prepare("DELETE FROM part WHERE session_id = ? AND message_id = ?");
|
|
32585
|
+
const deleteMessage = db.prepare("DELETE FROM message WHERE session_id = ? AND id = ?");
|
|
32586
|
+
const deletePart = db.prepare("DELETE FROM part WHERE session_id = ? AND id = ?");
|
|
32587
|
+
const deleteSummaries = (summaryIds) => {
|
|
32588
|
+
let rows = 0;
|
|
32589
|
+
for (const summaryId of summaryIds) {
|
|
32590
|
+
rows += deletePartsOfMessage.run(sessionId, summaryId).changes;
|
|
32591
|
+
rows += deleteMessage.run(sessionId, summaryId).changes;
|
|
32592
|
+
}
|
|
32593
|
+
return rows;
|
|
32594
|
+
};
|
|
32595
|
+
for (const [boundaryMessageId, summaryIds] of summariesByBoundary) {
|
|
32596
|
+
const boundaryParts = parsedParts.filter((part) => part.messageId === boundaryMessageId);
|
|
32597
|
+
const mcPartIds = boundaryParts.filter((part) => isMcCanonicalCompactionPartData(part.data)).map((part) => part.id);
|
|
32598
|
+
const rowsToDelete = new Set([...summaryIds, ...mcPartIds]);
|
|
32599
|
+
const survivingPartsReferenceDeletion = parsedParts.some((part) => !mcPartIds.includes(part.id) && part.tailStartId !== null && rowsToDelete.has(part.tailStartId));
|
|
32600
|
+
const messageFieldReferencesDeletion = [...rowsToDelete].some((id) => messageTailStartIds.has(id));
|
|
32601
|
+
if (survivingPartsReferenceDeletion || messageFieldReferencesDeletion) {
|
|
32602
|
+
retainedLineages += 1;
|
|
32603
|
+
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`);
|
|
32604
|
+
continue;
|
|
32605
|
+
}
|
|
32606
|
+
const rows = db.transaction(() => {
|
|
32607
|
+
let changed = deleteSummaries(summaryIds);
|
|
32608
|
+
for (const partId of mcPartIds) {
|
|
32609
|
+
changed += deletePart.run(sessionId, partId).changes;
|
|
32610
|
+
}
|
|
32611
|
+
return changed;
|
|
32612
|
+
})();
|
|
32613
|
+
if (rows > 0 || summaryIds.size > 0 || mcPartIds.length > 0) {
|
|
32614
|
+
removedLineages += 1;
|
|
32615
|
+
removedRows += rows;
|
|
32616
|
+
}
|
|
32617
|
+
}
|
|
32618
|
+
if (orphanSummaryIds.size > 0) {
|
|
32619
|
+
const rowsToDelete = new Set(orphanSummaryIds);
|
|
32620
|
+
const survivingPartsReferenceDeletion = parsedParts.some((part) => part.tailStartId !== null && rowsToDelete.has(part.tailStartId));
|
|
32621
|
+
const messageFieldReferencesDeletion = [...rowsToDelete].some((id) => messageTailStartIds.has(id));
|
|
32622
|
+
if (survivingPartsReferenceDeletion || messageFieldReferencesDeletion) {
|
|
32623
|
+
retainedLineages += 1;
|
|
32624
|
+
} else {
|
|
32625
|
+
const rows = db.transaction(() => deleteSummaries(orphanSummaryIds))();
|
|
32626
|
+
removedLineages += 1;
|
|
32627
|
+
removedRows += rows;
|
|
32628
|
+
}
|
|
32629
|
+
}
|
|
32630
|
+
if (removedLineages > 0 || retainedLineages > 0) {
|
|
32631
|
+
log(`[magic-context] compaction-marker: flip-off cleanup for ${sessionId} removed ${removedLineages} lineage(s) (${removedRows} rows), retained ${retainedLineages}`);
|
|
32632
|
+
}
|
|
32633
|
+
return {
|
|
32634
|
+
verified: retainedLineages === 0,
|
|
32635
|
+
removedLineages,
|
|
32636
|
+
removedRows,
|
|
32637
|
+
retainedLineages
|
|
32638
|
+
};
|
|
32639
|
+
}
|
|
32640
|
+
function removeCompactionMarker(state2) {
|
|
31889
32641
|
try {
|
|
31890
32642
|
const db = getWritableOpenCodeDb();
|
|
31891
32643
|
db.transaction(() => {
|
|
31892
|
-
db.prepare("DELETE FROM part WHERE id = ?").run(
|
|
31893
|
-
db.prepare("DELETE FROM message WHERE id = ?").run(
|
|
31894
|
-
db.prepare("DELETE FROM part WHERE id = ?").run(
|
|
32644
|
+
db.prepare("DELETE FROM part WHERE id = ?").run(state2.summaryPartId);
|
|
32645
|
+
db.prepare("DELETE FROM message WHERE id = ?").run(state2.summaryMessageId);
|
|
32646
|
+
db.prepare("DELETE FROM part WHERE id = ?").run(state2.compactionPartId);
|
|
31895
32647
|
})();
|
|
31896
32648
|
return true;
|
|
31897
32649
|
} catch (error51) {
|
|
@@ -31907,9 +32659,9 @@ function dropMarkerSummaryTag(db, sessionId, summaryMessageId) {
|
|
|
31907
32659
|
if (tagNumber !== null)
|
|
31908
32660
|
updateTagStatus(db, sessionId, tagNumber, "dropped");
|
|
31909
32661
|
}
|
|
31910
|
-
function persistMarkerStateAndDropReplacedTag(db, sessionId,
|
|
32662
|
+
function persistMarkerStateAndDropReplacedTag(db, sessionId, state2, replacedSummaryMessageId) {
|
|
31911
32663
|
db.transaction(() => {
|
|
31912
|
-
setPersistedCompactionMarkerState(db, sessionId,
|
|
32664
|
+
setPersistedCompactionMarkerState(db, sessionId, state2);
|
|
31913
32665
|
if (replacedSummaryMessageId !== null) {
|
|
31914
32666
|
dropMarkerSummaryTag(db, sessionId, replacedSummaryMessageId);
|
|
31915
32667
|
}
|
|
@@ -32130,7 +32882,7 @@ function reconcileForkOrphanedCompactionMarkers(db, sessionId) {
|
|
|
32130
32882
|
}
|
|
32131
32883
|
}
|
|
32132
32884
|
function checkCompactionMarkerConsistency(db) {
|
|
32133
|
-
const opencodeDbPath =
|
|
32885
|
+
const opencodeDbPath = join6(getDataDir(), "opencode", "opencode.db");
|
|
32134
32886
|
let opencodeDb;
|
|
32135
32887
|
try {
|
|
32136
32888
|
opencodeDb = new Database(opencodeDbPath, { readonly: true });
|
|
@@ -32146,19 +32898,19 @@ function checkCompactionMarkerConsistency(db) {
|
|
|
32146
32898
|
const checkPart = opencodeDb.prepare("SELECT 1 FROM part WHERE id = ? LIMIT 1");
|
|
32147
32899
|
let reconciledCount = 0;
|
|
32148
32900
|
for (const row of persistedRows) {
|
|
32149
|
-
const
|
|
32150
|
-
if (!
|
|
32901
|
+
const state2 = getPersistedCompactionMarkerState(db, row.session_id);
|
|
32902
|
+
if (!state2)
|
|
32151
32903
|
continue;
|
|
32152
|
-
const boundaryExists = checkMessage.get(
|
|
32153
|
-
const summaryMessageExists = checkMessage.get(
|
|
32154
|
-
const compactionPartExists = checkPart.get(
|
|
32155
|
-
const summaryPartExists = checkPart.get(
|
|
32904
|
+
const boundaryExists = checkMessage.get(state2.boundaryMessageId) != null;
|
|
32905
|
+
const summaryMessageExists = checkMessage.get(state2.summaryMessageId) != null;
|
|
32906
|
+
const compactionPartExists = checkPart.get(state2.compactionPartId) != null;
|
|
32907
|
+
const summaryPartExists = checkPart.get(state2.summaryPartId) != null;
|
|
32156
32908
|
const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
|
|
32157
32909
|
if (allPresent)
|
|
32158
32910
|
continue;
|
|
32159
32911
|
let removedOk = false;
|
|
32160
32912
|
try {
|
|
32161
|
-
removedOk = removeCompactionMarker(
|
|
32913
|
+
removedOk = removeCompactionMarker(state2);
|
|
32162
32914
|
} catch (error51) {
|
|
32163
32915
|
sessionLog(row.session_id, "compaction-marker consistency: partial cleanup of half-written marker failed:", error51);
|
|
32164
32916
|
}
|
|
@@ -33108,8 +33860,8 @@ function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex)
|
|
|
33108
33860
|
}
|
|
33109
33861
|
|
|
33110
33862
|
// src/hooks/magic-context/compartment-runner-historian.ts
|
|
33111
|
-
import { mkdirSync as mkdirSync4, unlinkSync as
|
|
33112
|
-
import { join as
|
|
33863
|
+
import { mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
33864
|
+
import { join as join7 } from "node:path";
|
|
33113
33865
|
|
|
33114
33866
|
// src/features/magic-context/subagent-token-capture.ts
|
|
33115
33867
|
function asNumber(value) {
|
|
@@ -33405,6 +34157,24 @@ function historianResponseDumpDir(directory) {
|
|
|
33405
34157
|
return getProjectMagicContextHistorianDir(directory);
|
|
33406
34158
|
}
|
|
33407
34159
|
var MAX_HISTORIAN_RETRIES = 2;
|
|
34160
|
+
var HISTORIAN_REASONING_PART_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
|
|
34161
|
+
function extractLatestHistorianReasoning(messages) {
|
|
34162
|
+
if (!Array.isArray(messages))
|
|
34163
|
+
return null;
|
|
34164
|
+
const latest = messages.filter((message) => isRecord(message) && isRecord(message.info) && message.info.role === "assistant").sort((left, right) => historianMessageCreatedAt(right) - historianMessageCreatedAt(left))[0];
|
|
34165
|
+
if (!latest || !Array.isArray(latest.parts))
|
|
34166
|
+
return null;
|
|
34167
|
+
return latest.parts.filter(isHistorianReasoningPart).map((part) => part.text).join(`
|
|
34168
|
+
`) || null;
|
|
34169
|
+
}
|
|
34170
|
+
function isHistorianReasoningPart(part) {
|
|
34171
|
+
return isRecord(part) && typeof part.type === "string" && HISTORIAN_REASONING_PART_TYPES.has(part.type) && typeof part.text === "string" && part.text.length > 0;
|
|
34172
|
+
}
|
|
34173
|
+
function historianMessageCreatedAt(message) {
|
|
34174
|
+
if (!isRecord(message.info) || !isRecord(message.info.time))
|
|
34175
|
+
return 0;
|
|
34176
|
+
return typeof message.info.time.created === "number" ? message.info.time.created : 0;
|
|
34177
|
+
}
|
|
33408
34178
|
async function runValidatedHistorianPass(args) {
|
|
33409
34179
|
const firstRun = await runHistorianPrompt({
|
|
33410
34180
|
...args,
|
|
@@ -33470,6 +34240,7 @@ async function runEditorPassOrFallback(args) {
|
|
|
33470
34240
|
sessionLog(args.parentSessionId, "historian two-pass: running editor on draft");
|
|
33471
34241
|
const editorRun = await runHistorianPrompt({
|
|
33472
34242
|
client: args.client,
|
|
34243
|
+
db: args.db,
|
|
33473
34244
|
parentSessionId: args.parentSessionId,
|
|
33474
34245
|
sessionDirectory: args.sessionDirectory,
|
|
33475
34246
|
prompt: buildHistorianEditorPrompt(args.draftXml),
|
|
@@ -33496,6 +34267,7 @@ async function runEditorPassOrFallback(args) {
|
|
|
33496
34267
|
async function runHistorianPrompt(args) {
|
|
33497
34268
|
const {
|
|
33498
34269
|
client,
|
|
34270
|
+
db,
|
|
33499
34271
|
parentSessionId,
|
|
33500
34272
|
sessionDirectory,
|
|
33501
34273
|
prompt,
|
|
@@ -33529,12 +34301,12 @@ async function runHistorianPrompt(args) {
|
|
|
33529
34301
|
};
|
|
33530
34302
|
try {
|
|
33531
34303
|
sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
|
|
33532
|
-
const createResponse = await
|
|
33533
|
-
|
|
33534
|
-
|
|
33535
|
-
|
|
33536
|
-
|
|
33537
|
-
|
|
34304
|
+
const createResponse = await createChildSessionWithFence({
|
|
34305
|
+
client,
|
|
34306
|
+
db,
|
|
34307
|
+
parentSessionId,
|
|
34308
|
+
title: "magic-context-compartment",
|
|
34309
|
+
directory: sessionDirectory
|
|
33538
34310
|
});
|
|
33539
34311
|
const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
33540
34312
|
agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
|
|
@@ -33582,7 +34354,18 @@ async function runHistorianPrompt(args) {
|
|
|
33582
34354
|
preferResponseOnMissingData: true
|
|
33583
34355
|
});
|
|
33584
34356
|
const invocationId = recordInvocation({ status: "completed", messages });
|
|
33585
|
-
const
|
|
34357
|
+
const lengthCapped = hasLengthCappedOutput(messages);
|
|
34358
|
+
const textResult = extractLatestAssistantText(messages);
|
|
34359
|
+
const reasoningResult = textResult ? null : extractLatestHistorianReasoning(messages);
|
|
34360
|
+
if (!textResult && reasoningResult && lengthCapped) {
|
|
34361
|
+
const outputTokens = sumTokensFromChildMessages(messages).output;
|
|
34362
|
+
return {
|
|
34363
|
+
ok: false,
|
|
34364
|
+
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`,
|
|
34365
|
+
invocationId: invocationId ?? undefined
|
|
34366
|
+
};
|
|
34367
|
+
}
|
|
34368
|
+
const result = textResult ?? reasoningResult;
|
|
33586
34369
|
if (!result) {
|
|
33587
34370
|
return {
|
|
33588
34371
|
ok: false,
|
|
@@ -33634,6 +34417,7 @@ async function runFallbackHistorianPass(args) {
|
|
|
33634
34417
|
args.callbacks?.onModelFallback?.(modelId, i + 1, chain.length);
|
|
33635
34418
|
const fallbackRun = await runHistorianPrompt({
|
|
33636
34419
|
client: args.client,
|
|
34420
|
+
db: args.db,
|
|
33637
34421
|
parentSessionId: args.parentSessionId,
|
|
33638
34422
|
sessionDirectory: args.sessionDirectory,
|
|
33639
34423
|
prompt: args.prompt,
|
|
@@ -33695,7 +34479,7 @@ function cleanupHistorianDump(sessionId, dumpPath) {
|
|
|
33695
34479
|
if (!dumpPath)
|
|
33696
34480
|
return;
|
|
33697
34481
|
try {
|
|
33698
|
-
|
|
34482
|
+
unlinkSync3(dumpPath);
|
|
33699
34483
|
} catch (error51) {
|
|
33700
34484
|
sessionLog(sessionId, "compartment agent: failed to remove historian response dump", {
|
|
33701
34485
|
dumpPath,
|
|
@@ -33710,7 +34494,7 @@ function dumpHistorianResponse(sessionId, directory, label, text) {
|
|
|
33710
34494
|
ensureCortexKitArtifactGitignore(directory);
|
|
33711
34495
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
33712
34496
|
const safeLabel = sanitizeDumpName(label);
|
|
33713
|
-
const dumpPath =
|
|
34497
|
+
const dumpPath = join7(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
33714
34498
|
writeFileSync2(dumpPath, text, "utf8");
|
|
33715
34499
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
33716
34500
|
label,
|
|
@@ -33733,26 +34517,7 @@ function sanitizeDumpName(value) {
|
|
|
33733
34517
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
33734
34518
|
|
|
33735
34519
|
// src/features/magic-context/mural/render-trigger.ts
|
|
33736
|
-
import { createHash as
|
|
33737
|
-
|
|
33738
|
-
// src/shared/harness-provider-map.ts
|
|
33739
|
-
var PI_TO_CANONICAL_PROVIDER = {
|
|
33740
|
-
"openai-codex": "openai",
|
|
33741
|
-
"google-antigravity": "google"
|
|
33742
|
-
};
|
|
33743
|
-
function remapProviderPrefix(ref, map2) {
|
|
33744
|
-
if (typeof ref !== "string")
|
|
33745
|
-
return ref;
|
|
33746
|
-
const slash = ref.indexOf("/");
|
|
33747
|
-
if (slash <= 0)
|
|
33748
|
-
return ref;
|
|
33749
|
-
const provider2 = ref.slice(0, slash);
|
|
33750
|
-
const mapped = map2[provider2];
|
|
33751
|
-
return mapped ? `${mapped}${ref.slice(slash)}` : ref;
|
|
33752
|
-
}
|
|
33753
|
-
function piModelRefToCanonical(ref) {
|
|
33754
|
-
return remapProviderPrefix(ref, PI_TO_CANONICAL_PROVIDER);
|
|
33755
|
-
}
|
|
34520
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
33756
34521
|
|
|
33757
34522
|
// src/features/magic-context/mural/mural-selection.ts
|
|
33758
34523
|
var DEFAULT_MURAL_MEMORY_BUDGET = 8000;
|
|
@@ -33955,6 +34720,8 @@ function naturalLineLengths(entries, roomWidth) {
|
|
|
33955
34720
|
let currentCategory = null;
|
|
33956
34721
|
for (let index = 0;index < entries.length; index++) {
|
|
33957
34722
|
const entry = entries[index];
|
|
34723
|
+
if (!entry)
|
|
34724
|
+
continue;
|
|
33958
34725
|
if (entry.category !== currentCategory) {
|
|
33959
34726
|
currentCategory = entry.category;
|
|
33960
34727
|
lengths.push(codepoints(` <${entry.category}> `));
|
|
@@ -33987,6 +34754,8 @@ function planLines(entries, roomWidth) {
|
|
|
33987
34754
|
let currentCategory = null;
|
|
33988
34755
|
for (let index = 0;index < entries.length; index++) {
|
|
33989
34756
|
const entry = entries[index];
|
|
34757
|
+
if (!entry)
|
|
34758
|
+
continue;
|
|
33990
34759
|
if (entry.category !== currentCategory) {
|
|
33991
34760
|
currentCategory = entry.category;
|
|
33992
34761
|
lines.push({
|
|
@@ -34050,8 +34819,11 @@ function renderLayout(entries, plan, roomWidth, columnCount) {
|
|
|
34050
34819
|
const usage = {};
|
|
34051
34820
|
let filledLineCount = 0;
|
|
34052
34821
|
for (const [column, columnPlan] of splitPlan(plan, columnCount).entries()) {
|
|
34822
|
+
const columnGrid = grid[column];
|
|
34823
|
+
if (!columnGrid)
|
|
34824
|
+
continue;
|
|
34053
34825
|
for (const [row, line] of columnPlan.entries()) {
|
|
34054
|
-
|
|
34826
|
+
columnGrid[row] = line.text;
|
|
34055
34827
|
filledLineCount += 1;
|
|
34056
34828
|
usage[line.category] = (usage[line.category] ?? 0) + 1;
|
|
34057
34829
|
const placementLine = row + 1;
|
|
@@ -34162,6 +34934,7 @@ function glyph(character) {
|
|
|
34162
34934
|
}
|
|
34163
34935
|
function drawGlyph(pixels, width, height, x, y, character, color) {
|
|
34164
34936
|
const glyphData = glyph(character);
|
|
34937
|
+
const [red, green, blue] = color;
|
|
34165
34938
|
for (let row = 0;row < MURAL_CELL_HEIGHT; row++) {
|
|
34166
34939
|
const pattern = glyphData.rows[row] ?? 0;
|
|
34167
34940
|
for (let column = 0;column < glyphData.width; column++) {
|
|
@@ -34172,9 +34945,9 @@ function drawGlyph(pixels, width, height, x, y, character, color) {
|
|
|
34172
34945
|
if (px < 0 || py < 0 || px >= width || py >= height)
|
|
34173
34946
|
continue;
|
|
34174
34947
|
const offset = (py * width + px) * 3;
|
|
34175
|
-
pixels[offset] =
|
|
34176
|
-
pixels[offset + 1] =
|
|
34177
|
-
pixels[offset + 2] =
|
|
34948
|
+
pixels[offset] = red;
|
|
34949
|
+
pixels[offset + 1] = green;
|
|
34950
|
+
pixels[offset + 2] = blue;
|
|
34178
34951
|
}
|
|
34179
34952
|
}
|
|
34180
34953
|
}
|
|
@@ -34186,6 +34959,7 @@ function drawText(pixels, width, height, x, y, text, color) {
|
|
|
34186
34959
|
}
|
|
34187
34960
|
}
|
|
34188
34961
|
function fillRect(pixels, canvasWidth, canvasHeight, x, y, width, height, color) {
|
|
34962
|
+
const [red, green, blue] = color;
|
|
34189
34963
|
const left = Math.max(0, x);
|
|
34190
34964
|
const top = Math.max(0, y);
|
|
34191
34965
|
const right = Math.min(canvasWidth, x + width);
|
|
@@ -34193,9 +34967,9 @@ function fillRect(pixels, canvasWidth, canvasHeight, x, y, width, height, color)
|
|
|
34193
34967
|
for (let py = top;py < bottom; py++) {
|
|
34194
34968
|
for (let px = left;px < right; px++) {
|
|
34195
34969
|
const offset = (py * canvasWidth + px) * 3;
|
|
34196
|
-
pixels[offset] =
|
|
34197
|
-
pixels[offset + 1] =
|
|
34198
|
-
pixels[offset + 2] =
|
|
34970
|
+
pixels[offset] = red;
|
|
34971
|
+
pixels[offset + 1] = green;
|
|
34972
|
+
pixels[offset + 2] = blue;
|
|
34199
34973
|
}
|
|
34200
34974
|
}
|
|
34201
34975
|
}
|
|
@@ -34222,6 +34996,9 @@ function renderMural(entries) {
|
|
|
34222
34996
|
});
|
|
34223
34997
|
const fittingCandidates = candidates.filter((candidate) => candidate.layout.droppedIds.length === 0);
|
|
34224
34998
|
const candidatesToCompare = fittingCandidates.length > 0 ? fittingCandidates : candidates;
|
|
34999
|
+
const firstCandidate = candidatesToCompare[0];
|
|
35000
|
+
if (!firstCandidate)
|
|
35001
|
+
throw new Error("mural layout candidate list is empty");
|
|
34225
35002
|
const selected = candidatesToCompare.reduce((best, candidate) => {
|
|
34226
35003
|
if (!best)
|
|
34227
35004
|
return candidate;
|
|
@@ -34232,7 +35009,7 @@ function renderMural(entries) {
|
|
|
34232
35009
|
return candidate.tileArea < best.tileArea ? candidate : best;
|
|
34233
35010
|
}
|
|
34234
35011
|
return candidate.requestedColumnCount < best.requestedColumnCount ? candidate : best;
|
|
34235
|
-
},
|
|
35012
|
+
}, firstCandidate);
|
|
34236
35013
|
const { layout, width, height } = selected;
|
|
34237
35014
|
const pixels = new Uint8Array(width * height * 3).fill(255);
|
|
34238
35015
|
const contentWidth = layout.columnCount === 0 ? 0 : layout.columnCount * roomWidth * MURAL_CELL_WIDTH + MURAL_COLUMN_GAP * (layout.columnCount - 1) * MURAL_CELL_WIDTH;
|
|
@@ -34276,8 +35053,8 @@ function getMuralCoverage(db, projectIdentity) {
|
|
|
34276
35053
|
const cueState = getMuralCueState(db, memories.map((memory) => memory.id));
|
|
34277
35054
|
let cuedMemoryCount = 0;
|
|
34278
35055
|
for (const memory of memories) {
|
|
34279
|
-
const
|
|
34280
|
-
if (
|
|
35056
|
+
const state2 = cueState.get(memory.id);
|
|
35057
|
+
if (state2 && typeof state2.cue === "string" && state2.cue.trim() !== "" && state2.hash !== null && state2.hash === computeCueContentHash(memory.content)) {
|
|
34281
35058
|
cuedMemoryCount += 1;
|
|
34282
35059
|
}
|
|
34283
35060
|
}
|
|
@@ -34291,16 +35068,16 @@ function resolveMural(db, projectIdentity, budgetTokens = DEFAULT_MURAL_MEMORY_B
|
|
|
34291
35068
|
const cueState = getMuralCueState(db, overflow.map((memory) => memory.id));
|
|
34292
35069
|
const entries = [];
|
|
34293
35070
|
for (const memory of overflow) {
|
|
34294
|
-
const
|
|
34295
|
-
if (!
|
|
35071
|
+
const state2 = cueState.get(memory.id);
|
|
35072
|
+
if (!state2 || state2.cue === null || state2.hash === null)
|
|
34296
35073
|
continue;
|
|
34297
|
-
if (
|
|
35074
|
+
if (state2.hash !== computeCueContentHash(memory.content))
|
|
34298
35075
|
continue;
|
|
34299
35076
|
entries.push({
|
|
34300
35077
|
id: memory.id,
|
|
34301
35078
|
category: memory.category,
|
|
34302
35079
|
importance: memory.importance ?? 50,
|
|
34303
|
-
cue:
|
|
35080
|
+
cue: state2.cue
|
|
34304
35081
|
});
|
|
34305
35082
|
}
|
|
34306
35083
|
entries.sort(compareMuralEntries);
|
|
@@ -34366,7 +35143,7 @@ function ensureMuralRendered(db, projectIdentity, budgetTokens = DEFAULT_MURAL_M
|
|
|
34366
35143
|
return { hasMural: false, rerendered: false };
|
|
34367
35144
|
}
|
|
34368
35145
|
const rendered = renderMural(entries);
|
|
34369
|
-
const textHash =
|
|
35146
|
+
const textHash = createHash14("sha256").update(rendered.sha256Input).digest("hex");
|
|
34370
35147
|
const existing = getMural(db, projectIdentity);
|
|
34371
35148
|
if (existing && existing.contentHash === textHash && existing.width === rendered.width && existing.height === rendered.height) {
|
|
34372
35149
|
return {
|
|
@@ -35491,41 +36268,41 @@ function readCurrentM0SnapshotMarkers(args) {
|
|
|
35491
36268
|
});
|
|
35492
36269
|
return fresh.markers;
|
|
35493
36270
|
}
|
|
35494
|
-
function snapshotMarkersFromCachedM0(
|
|
35495
|
-
if (!
|
|
36271
|
+
function snapshotMarkersFromCachedM0(state2) {
|
|
36272
|
+
if (!state2.cachedM0Bytes)
|
|
35496
36273
|
return null;
|
|
35497
|
-
const cachedUpgradeIdentity = decodeCachedM0UpgradeIdentity(
|
|
35498
|
-
if (
|
|
36274
|
+
const cachedUpgradeIdentity = decodeCachedM0UpgradeIdentity(state2.cachedM0UpgradeState);
|
|
36275
|
+
if (state2.cachedM0ProjectMemoryEpoch === null)
|
|
35499
36276
|
return null;
|
|
35500
|
-
if (
|
|
36277
|
+
if (state2.cachedM0ProjectUserProfileVersion === null)
|
|
35501
36278
|
return null;
|
|
35502
|
-
if (
|
|
36279
|
+
if (state2.cachedM0MaxCompartmentSeq === null)
|
|
35503
36280
|
return null;
|
|
35504
|
-
if (
|
|
36281
|
+
if (state2.cachedM0MaxMemoryId === null)
|
|
35505
36282
|
return null;
|
|
35506
|
-
if (
|
|
36283
|
+
if (state2.cachedM0MaxMutationId === null)
|
|
35507
36284
|
return null;
|
|
35508
|
-
if (
|
|
36285
|
+
if (state2.cachedM0MaxMemoryMutationId === null)
|
|
35509
36286
|
return null;
|
|
35510
|
-
if (
|
|
36287
|
+
if (state2.cachedM0SessionFactsVersion === null)
|
|
35511
36288
|
return null;
|
|
35512
36289
|
return {
|
|
35513
|
-
projectMemoryEpoch:
|
|
35514
|
-
workspaceFingerprint:
|
|
35515
|
-
projectUserProfileVersion:
|
|
35516
|
-
maxCompartmentSeq:
|
|
35517
|
-
maxMemoryId:
|
|
35518
|
-
maxMutationId:
|
|
35519
|
-
maxMemoryMutationId:
|
|
35520
|
-
projectDocsHash:
|
|
35521
|
-
materializedAt:
|
|
35522
|
-
sessionFactsVersion:
|
|
36290
|
+
projectMemoryEpoch: state2.cachedM0ProjectMemoryEpoch,
|
|
36291
|
+
workspaceFingerprint: state2.cachedM0WorkspaceFingerprint,
|
|
36292
|
+
projectUserProfileVersion: state2.cachedM0ProjectUserProfileVersion,
|
|
36293
|
+
maxCompartmentSeq: state2.cachedM0MaxCompartmentSeq,
|
|
36294
|
+
maxMemoryId: state2.cachedM0MaxMemoryId,
|
|
36295
|
+
maxMutationId: state2.cachedM0MaxMutationId,
|
|
36296
|
+
maxMemoryMutationId: state2.cachedM0MaxMemoryMutationId,
|
|
36297
|
+
projectDocsHash: state2.cachedM0ProjectDocsHash ?? "",
|
|
36298
|
+
materializedAt: state2.cachedM0MaterializedAt ?? 0,
|
|
36299
|
+
sessionFactsVersion: state2.cachedM0SessionFactsVersion,
|
|
35523
36300
|
upgradeState: cachedUpgradeIdentity.upgradeState,
|
|
35524
36301
|
compartmentRenderEpoch: cachedUpgradeIdentity.compartmentRenderEpoch,
|
|
35525
|
-
systemHash:
|
|
35526
|
-
modelKey:
|
|
35527
|
-
projectIdentity:
|
|
35528
|
-
muralHash:
|
|
36302
|
+
systemHash: state2.cachedM0SystemHash ?? "",
|
|
36303
|
+
modelKey: state2.cachedM0ModelKey ?? "",
|
|
36304
|
+
projectIdentity: state2.cachedM0ProjectIdentity ?? null,
|
|
36305
|
+
muralHash: state2.cachedM0MuralHash ?? null
|
|
35529
36306
|
};
|
|
35530
36307
|
}
|
|
35531
36308
|
function mustMaterialize(args) {
|
|
@@ -35841,26 +36618,26 @@ ${sessionHistory}
|
|
|
35841
36618
|
|
|
35842
36619
|
`).trim();
|
|
35843
36620
|
}
|
|
35844
|
-
function applyMarkersToState(
|
|
35845
|
-
|
|
36621
|
+
function applyMarkersToState(state2, m0Bytes, markers, m1Bytes) {
|
|
36622
|
+
state2.cachedM0Bytes = m0Bytes;
|
|
35846
36623
|
if (m1Bytes)
|
|
35847
|
-
|
|
35848
|
-
|
|
35849
|
-
|
|
35850
|
-
|
|
35851
|
-
|
|
35852
|
-
|
|
35853
|
-
|
|
35854
|
-
|
|
35855
|
-
|
|
35856
|
-
|
|
35857
|
-
|
|
35858
|
-
|
|
35859
|
-
|
|
35860
|
-
|
|
35861
|
-
|
|
35862
|
-
|
|
35863
|
-
|
|
36624
|
+
state2.cachedM1Bytes = m1Bytes;
|
|
36625
|
+
state2.cachedM0ProjectMemoryEpoch = markers.projectMemoryEpoch;
|
|
36626
|
+
state2.cachedM0WorkspaceFingerprint = markers.workspaceFingerprint;
|
|
36627
|
+
state2.cachedM0ProjectUserProfileVersion = markers.projectUserProfileVersion;
|
|
36628
|
+
state2.cachedM0MaxCompartmentSeq = markers.maxCompartmentSeq;
|
|
36629
|
+
state2.cachedM0MaxMemoryId = markers.maxMemoryId;
|
|
36630
|
+
state2.cachedM0MaxMutationId = markers.maxMutationId;
|
|
36631
|
+
state2.cachedM0MaxMemoryMutationId = markers.maxMemoryMutationId;
|
|
36632
|
+
state2.cachedM0ProjectDocsHash = markers.projectDocsHash;
|
|
36633
|
+
state2.cachedM0MaterializedAt = markers.materializedAt;
|
|
36634
|
+
state2.cachedM0SessionFactsVersion = markers.sessionFactsVersion;
|
|
36635
|
+
state2.cachedM0UpgradeState = encodeCachedM0UpgradeIdentity(markers.upgradeState, markers.compartmentRenderEpoch);
|
|
36636
|
+
state2.cachedM0SystemHash = markers.systemHash;
|
|
36637
|
+
state2.cachedM0ModelKey = markers.modelKey;
|
|
36638
|
+
state2.cachedM0ProjectIdentity = markers.projectIdentity;
|
|
36639
|
+
state2.cachedM0MuralHash = markers.muralHash ?? null;
|
|
36640
|
+
state2.snapshotMarkers = markers;
|
|
35864
36641
|
}
|
|
35865
36642
|
function historySliceTokens(m0Text) {
|
|
35866
36643
|
const slice = extractM0Block(m0Text, "session-history");
|
|
@@ -35910,7 +36687,7 @@ function materializeM0(options) {
|
|
|
35910
36687
|
});
|
|
35911
36688
|
docs = readProjectDocsForM0(projectDirectory, options.injectDocs);
|
|
35912
36689
|
snapshotMarkers.projectDocsHash = docs.canonicalHash;
|
|
35913
|
-
compartments = readM0Compartments(options.db, options.sessionId);
|
|
36690
|
+
compartments = options.compactionOff ? [] : readM0Compartments(options.db, options.sessionId);
|
|
35914
36691
|
facts = [];
|
|
35915
36692
|
memories = projectPath ? workspace.isWorkspaced ? getMemoriesByProjects(options.db, workspace.expandedIdentities, ["active", "permanent"], foldMaterializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(options.db, projectPath, ["active", "permanent"], foldMaterializedAt) : [];
|
|
35916
36693
|
userMemories = safeGetActiveUserMemories(options.db);
|
|
@@ -36275,39 +37052,39 @@ function markersFromCachedRow(row) {
|
|
|
36275
37052
|
muralHash: row.cached_m0_mural_hash ?? null
|
|
36276
37053
|
};
|
|
36277
37054
|
}
|
|
36278
|
-
function cachedRowMatchesState(row,
|
|
36279
|
-
return bufferEqualsNullable(row.cached_m0_bytes,
|
|
37055
|
+
function cachedRowMatchesState(row, state2) {
|
|
37056
|
+
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);
|
|
36280
37057
|
}
|
|
36281
|
-
function applyCachedRowToState(
|
|
37058
|
+
function applyCachedRowToState(state2, row) {
|
|
36282
37059
|
const markers = markersFromCachedRow(row);
|
|
36283
37060
|
if (!row.cached_m0_bytes || !row.cached_m1_bytes || !markers) {
|
|
36284
|
-
throw new RenderM1InvalidMarkersError(
|
|
36285
|
-
}
|
|
36286
|
-
|
|
36287
|
-
|
|
36288
|
-
|
|
36289
|
-
|
|
36290
|
-
|
|
36291
|
-
|
|
36292
|
-
|
|
36293
|
-
|
|
36294
|
-
|
|
36295
|
-
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
|
|
36299
|
-
|
|
36300
|
-
|
|
36301
|
-
|
|
36302
|
-
|
|
36303
|
-
|
|
36304
|
-
|
|
36305
|
-
}
|
|
36306
|
-
function replayCachedM1(
|
|
36307
|
-
if (!
|
|
36308
|
-
throw new RenderM1InvalidMarkersError(
|
|
36309
|
-
}
|
|
36310
|
-
return decodeM0Bytes(
|
|
37061
|
+
throw new RenderM1InvalidMarkersError(state2.sessionId);
|
|
37062
|
+
}
|
|
37063
|
+
state2.cachedM0Bytes = toBuffer(row.cached_m0_bytes);
|
|
37064
|
+
state2.cachedM0MuralDataUrl = row.cached_m0_mural_data_url ?? null;
|
|
37065
|
+
state2.cachedM0MuralHash = row.cached_m0_mural_hash ?? null;
|
|
37066
|
+
state2.cachedM1Bytes = toBuffer(row.cached_m1_bytes);
|
|
37067
|
+
state2.cachedM0ProjectMemoryEpoch = markers.projectMemoryEpoch;
|
|
37068
|
+
state2.cachedM0WorkspaceFingerprint = markers.workspaceFingerprint;
|
|
37069
|
+
state2.cachedM0ProjectUserProfileVersion = markers.projectUserProfileVersion;
|
|
37070
|
+
state2.cachedM0MaxCompartmentSeq = markers.maxCompartmentSeq;
|
|
37071
|
+
state2.cachedM0MaxMemoryId = markers.maxMemoryId;
|
|
37072
|
+
state2.cachedM0MaxMutationId = markers.maxMutationId;
|
|
37073
|
+
state2.cachedM0MaxMemoryMutationId = markers.maxMemoryMutationId;
|
|
37074
|
+
state2.cachedM0ProjectDocsHash = markers.projectDocsHash;
|
|
37075
|
+
state2.cachedM0MaterializedAt = markers.materializedAt;
|
|
37076
|
+
state2.cachedM0SessionFactsVersion = markers.sessionFactsVersion;
|
|
37077
|
+
state2.cachedM0UpgradeState = encodeCachedM0UpgradeIdentity(markers.upgradeState, markers.compartmentRenderEpoch);
|
|
37078
|
+
state2.cachedM0SystemHash = markers.systemHash;
|
|
37079
|
+
state2.cachedM0ModelKey = markers.modelKey;
|
|
37080
|
+
state2.cachedM0ProjectIdentity = markers.projectIdentity;
|
|
37081
|
+
state2.snapshotMarkers = markers;
|
|
37082
|
+
}
|
|
37083
|
+
function replayCachedM1(state2) {
|
|
37084
|
+
if (!state2.cachedM1Bytes) {
|
|
37085
|
+
throw new RenderM1InvalidMarkersError(state2.sessionId);
|
|
37086
|
+
}
|
|
37087
|
+
return decodeM0Bytes(state2.cachedM1Bytes) ?? M1_EMPTY_PLACEHOLDER;
|
|
36311
37088
|
}
|
|
36312
37089
|
function softRefreshCachedM1(options) {
|
|
36313
37090
|
options.db.exec("BEGIN IMMEDIATE");
|
|
@@ -36391,7 +37168,7 @@ function renderFreshM0NonPersisted(options) {
|
|
|
36391
37168
|
const docs = readProjectDocsForM0(projectDirectory ?? "", options.injectDocs);
|
|
36392
37169
|
snapshotMarkers.projectDocsHash = docs.canonicalHash;
|
|
36393
37170
|
snapshotMarkers.materializedAt = options.state.cachedM0MaterializedAt ?? 0;
|
|
36394
|
-
const compartments = withCompartmentDates(options.sessionId, readM0Compartments(options.db, options.sessionId), options.temporalAwareness);
|
|
37171
|
+
const compartments = options.compactionOff ? [] : withCompartmentDates(options.sessionId, readM0Compartments(options.db, options.sessionId), options.temporalAwareness);
|
|
36395
37172
|
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) : [];
|
|
36396
37173
|
const userMemories = safeGetActiveUserMemories(options.db);
|
|
36397
37174
|
const memoryBudget = options.memoryInjectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
@@ -36469,7 +37246,7 @@ function injectM0M1(options) {
|
|
|
36469
37246
|
m0Bytes: options.state.cachedM0Bytes,
|
|
36470
37247
|
m1Text: null
|
|
36471
37248
|
};
|
|
36472
|
-
if (options.state.isSubagent)
|
|
37249
|
+
if (options.state.isSubagent && !options.compactionOff)
|
|
36473
37250
|
return skipped;
|
|
36474
37251
|
const decision = mustMaterialize({
|
|
36475
37252
|
db: options.db,
|
|
@@ -36598,14 +37375,14 @@ function onNoteTrigger(db, sessionId, trigger) {
|
|
|
36598
37375
|
sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
|
|
36599
37376
|
}
|
|
36600
37377
|
function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity, noteReadStillVisible) {
|
|
36601
|
-
const
|
|
36602
|
-
if (!
|
|
37378
|
+
const state2 = getPersistedNoteNudge(db, sessionId);
|
|
37379
|
+
if (!state2.triggerPending)
|
|
36603
37380
|
return null;
|
|
36604
|
-
if (!
|
|
37381
|
+
if (!state2.triggerMessageId && currentUserMessageId) {
|
|
36605
37382
|
setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
|
|
36606
|
-
|
|
37383
|
+
state2.triggerMessageId = currentUserMessageId;
|
|
36607
37384
|
}
|
|
36608
|
-
if (
|
|
37385
|
+
if (state2.triggerMessageId && currentUserMessageId && state2.triggerMessageId === currentUserMessageId) {
|
|
36609
37386
|
sessionLog(sessionId, `note-nudge: deferring — current user message ${currentUserMessageId} is same as trigger-time message`);
|
|
36610
37387
|
return null;
|
|
36611
37388
|
}
|
|
@@ -36807,29 +37584,29 @@ function callIdFromPart(part) {
|
|
|
36807
37584
|
const direct = firstStringField(part, ["callID", "callId", "toolCallId", "tool_call_id", "id"]);
|
|
36808
37585
|
if (direct)
|
|
36809
37586
|
return direct;
|
|
36810
|
-
const
|
|
36811
|
-
return
|
|
37587
|
+
const state2 = isRecord(part.state) ? part.state : null;
|
|
37588
|
+
return state2 ? firstStringField(state2, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
|
|
36812
37589
|
}
|
|
36813
37590
|
function toolSignalFromPart(part) {
|
|
36814
37591
|
if (!isRecord(part))
|
|
36815
37592
|
return null;
|
|
36816
37593
|
const type = partType(part);
|
|
36817
|
-
const
|
|
37594
|
+
const state2 = isRecord(part.state) ? part.state : null;
|
|
36818
37595
|
const callId = callIdFromPart(part);
|
|
36819
37596
|
if (!callId && type !== "tool")
|
|
36820
37597
|
return null;
|
|
36821
37598
|
if (type === "tool") {
|
|
36822
|
-
const hasInput =
|
|
36823
|
-
const outputKey =
|
|
37599
|
+
const hasInput = state2 !== null && hasOwn(state2, "input");
|
|
37600
|
+
const outputKey = state2 ? hasOwn(state2, "output") ? "output" : hasOwn(state2, "error") ? "error" : hasOwn(state2, "result") ? "result" : null : null;
|
|
36824
37601
|
const hasOutput = outputKey !== null;
|
|
36825
|
-
const outputValue = outputKey &&
|
|
37602
|
+
const outputValue = outputKey && state2 ? state2[outputKey] : undefined;
|
|
36826
37603
|
const providerExecuted = part.providerExecuted === true;
|
|
36827
37604
|
const openInvocation = !providerExecuted && !hasOutput;
|
|
36828
37605
|
return {
|
|
36829
37606
|
callId,
|
|
36830
37607
|
hasInput: hasInput || openInvocation,
|
|
36831
37608
|
hasOutput,
|
|
36832
|
-
inputText: hasInput &&
|
|
37609
|
+
inputText: hasInput && state2 ? stringValue(state2.input) : "",
|
|
36833
37610
|
outputText: hasOutput ? stringValue(outputValue) : ""
|
|
36834
37611
|
};
|
|
36835
37612
|
}
|
|
@@ -37057,24 +37834,27 @@ function tokenForMessage(message, options) {
|
|
|
37057
37834
|
function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
37058
37835
|
const ordered = [...messages].sort((a, b) => a.ordinal - b.ordinal);
|
|
37059
37836
|
const sliceCount = ordered.length;
|
|
37060
|
-
const
|
|
37061
|
-
const
|
|
37837
|
+
const firstOrdinal = ordered.length > 0 ? ordered[0].ordinal : 1;
|
|
37838
|
+
const terminalOrdinal = ordered.length > 0 ? ordered[ordered.length - 1].ordinal : 0;
|
|
37839
|
+
const rawMessageCount = Math.max(sliceCount, terminalOrdinal, options.absoluteMessageCount ?? sliceCount);
|
|
37840
|
+
const ordinalSpan = terminalOrdinal >= firstOrdinal ? terminalOrdinal - firstOrdinal + 1 : 0;
|
|
37062
37841
|
const tokensByOrdinal = new Map;
|
|
37063
37842
|
const idsByOrdinal = new Map;
|
|
37064
|
-
const prefix = new Array(
|
|
37843
|
+
const prefix = new Array(ordinalSpan + 1).fill(0);
|
|
37065
37844
|
for (const message of ordered) {
|
|
37066
37845
|
const stored = options.storedTotalForMessage?.(message);
|
|
37067
37846
|
const total = stored !== undefined && stored !== null ? stored : tokenForMessage(message, options).total;
|
|
37068
37847
|
tokensByOrdinal.set(message.ordinal, total);
|
|
37069
37848
|
idsByOrdinal.set(message.ordinal, message.id);
|
|
37070
|
-
|
|
37071
|
-
|
|
37849
|
+
const relative = message.ordinal - firstOrdinal + 1;
|
|
37850
|
+
if (relative >= 1 && relative <= ordinalSpan) {
|
|
37851
|
+
prefix[relative] = total;
|
|
37072
37852
|
}
|
|
37073
37853
|
}
|
|
37074
|
-
for (let k = 1;k <=
|
|
37854
|
+
for (let k = 1;k <= ordinalSpan; k += 1) {
|
|
37075
37855
|
prefix[k] += prefix[k - 1];
|
|
37076
37856
|
}
|
|
37077
|
-
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(
|
|
37857
|
+
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(ordinalSpan, ordinal - firstOrdinal));
|
|
37078
37858
|
return {
|
|
37079
37859
|
sessionId,
|
|
37080
37860
|
providerShapeVersion: options.providerShapeVersion,
|
|
@@ -37086,27 +37866,27 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
37086
37866
|
return idsByOrdinal.get(ordinal) ?? null;
|
|
37087
37867
|
},
|
|
37088
37868
|
suffixTokensFromOrdinal(ordinal) {
|
|
37089
|
-
if (ordinal <=
|
|
37090
|
-
return prefix[
|
|
37091
|
-
if (ordinal >
|
|
37869
|
+
if (ordinal <= firstOrdinal)
|
|
37870
|
+
return prefix[ordinalSpan];
|
|
37871
|
+
if (ordinal > terminalOrdinal)
|
|
37092
37872
|
return 0;
|
|
37093
|
-
return prefix[
|
|
37873
|
+
return prefix[ordinalSpan] - prefix[ordinalToIndex(ordinal)];
|
|
37094
37874
|
},
|
|
37095
37875
|
rangeTokens(startInclusive, endExclusive) {
|
|
37096
|
-
const start = Math.max(
|
|
37097
|
-
const end = Math.max(start, Math.min(
|
|
37098
|
-
return prefix[end -
|
|
37876
|
+
const start = Math.max(firstOrdinal, startInclusive);
|
|
37877
|
+
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
37878
|
+
return prefix[end - firstOrdinal] - prefix[start - firstOrdinal];
|
|
37099
37879
|
},
|
|
37100
37880
|
findSuffixStartForTokens(tokens) {
|
|
37101
37881
|
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
37102
|
-
return
|
|
37882
|
+
return terminalOrdinal + 1;
|
|
37103
37883
|
const target = Math.max(0, Math.floor(tokens));
|
|
37104
|
-
const total = prefix[
|
|
37884
|
+
const total = prefix[ordinalSpan];
|
|
37105
37885
|
if (total < target)
|
|
37106
|
-
return
|
|
37886
|
+
return firstOrdinal;
|
|
37107
37887
|
const cut = total - target;
|
|
37108
37888
|
let lo = 0;
|
|
37109
|
-
let hi =
|
|
37889
|
+
let hi = ordinalSpan;
|
|
37110
37890
|
let best = 0;
|
|
37111
37891
|
while (lo <= hi) {
|
|
37112
37892
|
const mid = lo + hi >> 1;
|
|
@@ -37117,29 +37897,31 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
37117
37897
|
hi = mid - 1;
|
|
37118
37898
|
}
|
|
37119
37899
|
}
|
|
37120
|
-
return
|
|
37900
|
+
return firstOrdinal + best;
|
|
37121
37901
|
},
|
|
37122
37902
|
findHeadEndForCap(startInclusive, endExclusive, capTokens) {
|
|
37123
|
-
const start = Math.max(
|
|
37124
|
-
const end = Math.max(start, Math.min(
|
|
37903
|
+
const start = Math.max(firstOrdinal, Math.min(terminalOrdinal + 1, startInclusive));
|
|
37904
|
+
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
37125
37905
|
if (!Number.isFinite(capTokens) || capTokens <= 0)
|
|
37126
37906
|
return start;
|
|
37127
|
-
const
|
|
37128
|
-
const
|
|
37129
|
-
|
|
37130
|
-
let
|
|
37131
|
-
let
|
|
37907
|
+
const startIndex = start - firstOrdinal;
|
|
37908
|
+
const endIndex = end - firstOrdinal;
|
|
37909
|
+
const cut = prefix[startIndex] + Math.floor(capTokens);
|
|
37910
|
+
let lo = startIndex + 1;
|
|
37911
|
+
let hi = endIndex;
|
|
37912
|
+
let bestEndIndex = startIndex;
|
|
37132
37913
|
while (lo <= hi) {
|
|
37133
37914
|
const mid = lo + hi >> 1;
|
|
37134
37915
|
if (prefix[mid] <= cut) {
|
|
37135
|
-
|
|
37916
|
+
bestEndIndex = mid;
|
|
37136
37917
|
lo = mid + 1;
|
|
37137
37918
|
} else {
|
|
37138
37919
|
hi = mid - 1;
|
|
37139
37920
|
}
|
|
37140
37921
|
}
|
|
37922
|
+
let bestEnd = firstOrdinal + bestEndIndex;
|
|
37141
37923
|
if (bestEnd === start && start < end)
|
|
37142
|
-
|
|
37924
|
+
bestEnd = start + 1;
|
|
37143
37925
|
return Math.min(bestEnd, end);
|
|
37144
37926
|
}
|
|
37145
37927
|
};
|
|
@@ -37456,7 +38238,8 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
37456
38238
|
if (ctx.migrationFloorActive)
|
|
37457
38239
|
runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
|
|
37458
38240
|
let protectedTailStart = Math.max(boundary, runtimeFloor);
|
|
37459
|
-
|
|
38241
|
+
const forceMaterializationPercentage = escalationBands(ctx.executeThresholdPercentage).forceMaterializationPercentage;
|
|
38242
|
+
if (!ctx.emergencyTailScale && usagePercentage < forceMaterializationPercentage) {
|
|
37460
38243
|
let lastMeaningfulUserOrdinal = 0;
|
|
37461
38244
|
for (let i = messages.length - 1;i >= 0; i--) {
|
|
37462
38245
|
const message = messages[i];
|
|
@@ -37697,7 +38480,8 @@ function getRawHistoryEligibility(db, sessionId) {
|
|
|
37697
38480
|
function hasRunnableCompartmentWindow(snapshot) {
|
|
37698
38481
|
if (snapshot.offset >= snapshot.protectedTailStart)
|
|
37699
38482
|
return false;
|
|
37700
|
-
|
|
38483
|
+
const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
|
|
38484
|
+
if (snapshot.usagePercentage >= forceMaterializationPercentage || snapshot.emergencyTailScale) {
|
|
37701
38485
|
return snapshot.trueRawEligibleTokens >= deriveMinForceEligibleTokens(snapshot.N) || snapshot.eligibleEndOrdinal > snapshot.offset;
|
|
37702
38486
|
}
|
|
37703
38487
|
return snapshot.eligibleEndOrdinal > snapshot.offset;
|
|
@@ -37760,8 +38544,10 @@ function validateBoundarySnapshot(args) {
|
|
|
37760
38544
|
return { ok: true };
|
|
37761
38545
|
}
|
|
37762
38546
|
function recordHighPressureNoEligibleHead(db, snapshot) {
|
|
37763
|
-
|
|
38547
|
+
const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
|
|
38548
|
+
if (snapshot.usagePercentage < forceMaterializationPercentage && !snapshot.emergencyTailScale) {
|
|
37764
38549
|
return 0;
|
|
38550
|
+
}
|
|
37765
38551
|
return recordProtectedTailNoEligibleHead(db, snapshot.sessionId);
|
|
37766
38552
|
}
|
|
37767
38553
|
|
|
@@ -39792,6 +40578,7 @@ ${chunkText}`,
|
|
|
39792
40578
|
retainDrainReservationForRetryThrottle = true;
|
|
39793
40579
|
const validatedPass = await runValidatedHistorianPass({
|
|
39794
40580
|
client,
|
|
40581
|
+
db,
|
|
39795
40582
|
parentSessionId: sessionId,
|
|
39796
40583
|
sessionDirectory,
|
|
39797
40584
|
prompt,
|
|
@@ -40252,6 +41039,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
|
|
|
40252
41039
|
emitProgress(`Running historian (pass ${passCount + 1})…`);
|
|
40253
41040
|
const validatedPass = await runValidatedHistorianPass({
|
|
40254
41041
|
client,
|
|
41042
|
+
db,
|
|
40255
41043
|
parentSessionId: sessionId,
|
|
40256
41044
|
sessionDirectory,
|
|
40257
41045
|
prompt,
|
|
@@ -40671,6 +41459,7 @@ ${chunk.text}`,
|
|
|
40671
41459
|
Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${chunk.startIndex}-${chunk.endIndex}.`, notifParams());
|
|
40672
41460
|
const validatedPass = await runValidatedHistorianPass({
|
|
40673
41461
|
client,
|
|
41462
|
+
db,
|
|
40674
41463
|
parentSessionId: sessionId,
|
|
40675
41464
|
sessionDirectory,
|
|
40676
41465
|
prompt,
|
|
@@ -40922,7 +41711,7 @@ async function executeContextRecomp(deps, options = {}) {
|
|
|
40922
41711
|
}
|
|
40923
41712
|
|
|
40924
41713
|
// src/hooks/magic-context/lkg-slot.ts
|
|
40925
|
-
import { createHash as
|
|
41714
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
40926
41715
|
var LKG_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
40927
41716
|
var LKG_SINGLE_SLOT_BYTES = 24 * 1024 * 1024;
|
|
40928
41717
|
var LKG_METADATA_BYTES = 256;
|
|
@@ -40933,7 +41722,7 @@ function slotBytes(slot) {
|
|
|
40933
41722
|
return 2 * slot.jsonPrefix.length + digestBytes + LKG_METADATA_BYTES;
|
|
40934
41723
|
}
|
|
40935
41724
|
function lkgContentDigest(message) {
|
|
40936
|
-
const hash2 =
|
|
41725
|
+
const hash2 = createHash15("sha256");
|
|
40937
41726
|
const seen = new WeakSet;
|
|
40938
41727
|
const visit = (value) => {
|
|
40939
41728
|
if (value === null) {
|
|
@@ -41299,4 +42088,4 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
|
|
|
41299
42088
|
}
|
|
41300
42089
|
}
|
|
41301
42090
|
|
|
41302
|
-
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,
|
|
42091
|
+
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, hasMuralCueColumns, 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 };
|