@cortexkit/opencode-magic-context 0.42.5 → 0.42.6
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 +2 -2
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +1 -0
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-jyj54gbe.js → conflict-warning-hook-aktepwpz.js} +3 -3
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/context-authority.d.ts +28 -1
- package/dist/features/magic-context/context-authority.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/open-opencode-db.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +1 -0
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +12 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +1 -0
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/session-project-backfill.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +0 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts +8 -0
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts +12 -0
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +4 -4
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-state-sync.d.ts +3 -0
- package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-transport.d.ts +24 -2
- package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-wire.d.ts +1 -1
- package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
- package/dist/hooks/magic-context/producer-window-guard.d.ts +31 -1
- package/dist/hooks/magic-context/producer-window-guard.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +6 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +8 -0
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +6 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +30 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-refusal-recovery.d.ts +28 -0
- package/dist/hooks/magic-context/rust-refusal-recovery.d.ts.map +1 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +7 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-jkrqhc7y.js → index-a9ssj59b.js} +94 -15
- package/dist/{index-3wbd8sdq.js → index-djp6rwz6.js} +455 -46
- package/dist/{index-p474rw9p.js → index-ngjmj6xx.js} +1 -1
- package/dist/{index-d8mgqezq.js → index-s61hh7gv.js} +50 -17
- package/dist/{index-1ar7b3e9.js → index-znraa0kv.js} +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1485 -360
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/memory-id-translation.d.ts +23 -0
- package/dist/plugin/memory-id-translation.d.ts.map +1 -0
- package/dist/plugin/rust-tool-backends.d.ts +1 -1
- package/dist/plugin/rust-tool-backends.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-k20sfpm2.js → recomp-orchestrator-0xx1364v.js} +4 -4
- package/dist/{send-session-notification-xnypkxpx.js → send-session-notification-jbxknvar.js} +2 -2
- package/dist/shared/auto-update-provenance.d.ts +9 -0
- package/dist/shared/auto-update-provenance.d.ts.map +1 -0
- package/dist/shared/conflict-detector.d.ts +5 -1
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/opencode-db-path.d.ts +31 -2
- package/dist/shared/opencode-db-path.d.ts.map +1 -1
- package/dist/shared/prompt-surface-runtime.d.ts +3 -3
- package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
- package/dist/{storage-meta-session-x3j0q9qy.js → storage-meta-session-z818pn1e.js} +2 -2
- package/dist/tools/ctx-expand/mode.d.ts +31 -0
- package/dist/tools/ctx-expand/mode.d.ts.map +1 -0
- package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-note/constants.d.ts +1 -1
- package/dist/tools/ctx-note/constants.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/dist/tools/ctx-note/types.d.ts +1 -1
- package/dist/tools/ctx-note/types.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
- package/dist/tools/ctx-search/types.d.ts +1 -1
- package/dist/tools/ctx-search/types.d.ts.map +1 -1
- package/dist/tools/light-descriptions.d.ts +2 -2
- package/dist/tools/light-descriptions.d.ts.map +1 -1
- package/dist/v2/hidden-completion.d.ts +69 -13
- package/dist/v2/hidden-completion.d.ts.map +1 -1
- package/dist/v2/hooks/context.d.ts +13 -1
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/hidden-child.d.ts +31 -0
- package/dist/v2/hooks/hidden-child.d.ts.map +1 -0
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +99 -19
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/server.d.ts +19 -0
- package/dist/v2/server.d.ts.map +1 -1
- package/dist/v2/server.js +1781 -253
- package/dist/v2/store-reader.d.ts +11 -5
- package/dist/v2/store-reader.d.ts.map +1 -1
- package/dist/v2/tui/index.d.ts +10 -0
- package/dist/v2/tui/index.d.ts.map +1 -0
- package/dist/v2/tui/types.d.ts +80 -0
- package/dist/v2/tui/types.d.ts.map +1 -0
- package/package.json +5 -4
- package/src/agents/magic-context-prompt.ts +1 -1
- package/src/config/index.ts +31 -1
- package/src/config/protected-tokens.test.ts +40 -0
- package/src/config/schema/magic-context.ts +8 -3
- package/src/shared/auto-update-provenance.ts +42 -0
- package/src/shared/conflict-detector.test.ts +53 -0
- package/src/shared/conflict-detector.ts +88 -19
- package/src/shared/models-dev-cache.test.ts +25 -0
- package/src/shared/opencode-db-path.test.ts +96 -0
- package/src/shared/opencode-db-path.ts +146 -15
- package/src/shared/prompt-surface-a1-golden.md +12 -16
- package/src/shared/prompt-surface-runtime.test.ts +20 -0
- package/src/shared/prompt-surface-runtime.ts +7 -5
- package/src/tui/entry.mjs +11 -1
- package/src/v2/tui/host-contract.test.ts +202 -0
- package/src/v2/tui/index.ts +270 -0
- package/src/v2/tui/types.ts +67 -0
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
resolveModelConfigOrDefault,
|
|
33
33
|
DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
|
|
34
34
|
DEFAULT_HISTORIAN_TIMEOUT_MS,
|
|
35
|
+
PROTECTED_TOKENS_MIN,
|
|
35
36
|
DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
36
37
|
PER_HARNESS_MIGRATION_INVENTORY,
|
|
37
38
|
PER_HARNESS_MODEL_KEYS,
|
|
@@ -72,7 +73,10 @@ import {
|
|
|
72
73
|
prepareAuthority,
|
|
73
74
|
drainAuthority,
|
|
74
75
|
getMemoryMirrorStatus,
|
|
76
|
+
moduleMemoryIdentityForHostId,
|
|
77
|
+
hostMemoryIdentityForModuleId,
|
|
75
78
|
applyMirroredNoteCompileFields,
|
|
79
|
+
applyTargetedMemoryMirrorRow,
|
|
76
80
|
drainMirrorPages,
|
|
77
81
|
pullMemoryMirrorOnce,
|
|
78
82
|
V2_MEMORY_CATEGORIES,
|
|
@@ -207,6 +211,7 @@ import {
|
|
|
207
211
|
primeInMemoryTailRawMessageCache,
|
|
208
212
|
readRawSessionMessageOrdinalPage,
|
|
209
213
|
getRawSessionStoredMessageCount,
|
|
214
|
+
readRawSessionMessageIdOrdinals,
|
|
210
215
|
readRawSessionMessagePartsById,
|
|
211
216
|
readRawSessionMessageOrdinalById,
|
|
212
217
|
readRawSessionMessageById,
|
|
@@ -365,11 +370,13 @@ import {
|
|
|
365
370
|
detectThinkingBindingMismatch,
|
|
366
371
|
isFable51ThinkingBindingModel,
|
|
367
372
|
detectOverflow,
|
|
373
|
+
parseProviderModel,
|
|
368
374
|
modelBodyField,
|
|
369
375
|
toModelEntry,
|
|
370
376
|
getPromptFailureDetail,
|
|
371
377
|
promptSyncWithValidatedOutputRetry,
|
|
372
378
|
normalizeSDKResponse,
|
|
379
|
+
ACTIVE_TOOL_IDS,
|
|
373
380
|
promptSurfaceHashMaterial,
|
|
374
381
|
createPromptSurfaceRuntime,
|
|
375
382
|
createPromptSurfaceGuidanceEpochCache,
|
|
@@ -422,6 +429,7 @@ import {
|
|
|
422
429
|
embedShadowTextForProject,
|
|
423
430
|
enqueueShadowEmbeddingItems,
|
|
424
431
|
registerProjectInObservationMode,
|
|
432
|
+
unregisterProjectShadowEmbedding,
|
|
425
433
|
getProjectEmbeddingSnapshot,
|
|
426
434
|
embedTextForProject,
|
|
427
435
|
embedBatchForProject,
|
|
@@ -547,7 +555,7 @@ import {
|
|
|
547
555
|
setRecompTerminal2,
|
|
548
556
|
runManagedRecomp2,
|
|
549
557
|
runManagedUpgrade2
|
|
550
|
-
} from "./index-
|
|
558
|
+
} from "./index-djp6rwz6.js";
|
|
551
559
|
import {
|
|
552
560
|
stripJsonComments,
|
|
553
561
|
isPrototypePollutionKey,
|
|
@@ -576,6 +584,7 @@ import {
|
|
|
576
584
|
} from "./index-f9gem5cr.js";
|
|
577
585
|
import {
|
|
578
586
|
resolveOpenCodeDbPath,
|
|
587
|
+
assertOpenCodeStoreGeneration,
|
|
579
588
|
openCodeDbPathExists,
|
|
580
589
|
formatOpenCodeDbMissingBanner,
|
|
581
590
|
formatOpenCodeDbMissingStatusLine,
|
|
@@ -592,9 +601,10 @@ import {
|
|
|
592
601
|
observeOpenCodeTurnEvent,
|
|
593
602
|
clearTrackedOpenCodeSession,
|
|
594
603
|
assistantAwaitingTools,
|
|
604
|
+
latestPersistedMessageForRecovery,
|
|
595
605
|
getMessageTimesFromOpenCodeDb,
|
|
596
606
|
findLastAssistantModelFromOpenCodeDb
|
|
597
|
-
} from "./index-
|
|
607
|
+
} from "./index-a9ssj59b.js";
|
|
598
608
|
import {
|
|
599
609
|
getErrorMessage,
|
|
600
610
|
describeError,
|
|
@@ -603,13 +613,13 @@ import {
|
|
|
603
613
|
clearIgnoredMessages2,
|
|
604
614
|
observeIgnoredNotificationEvent2,
|
|
605
615
|
sendStatusNotification2
|
|
606
|
-
} from "./index-
|
|
616
|
+
} from "./index-ngjmj6xx.js";
|
|
607
617
|
import {
|
|
608
618
|
detectConflicts,
|
|
609
619
|
resolveCompactionForBoot,
|
|
610
620
|
sendConflictWarning2,
|
|
611
621
|
cleanupConflictWarnings2
|
|
612
|
-
} from "./index-
|
|
622
|
+
} from "./index-s61hh7gv.js";
|
|
613
623
|
import {
|
|
614
624
|
resolveHistorianModel2,
|
|
615
625
|
resolveOpenCodeAgentOverrides2,
|
|
@@ -636,7 +646,7 @@ import {
|
|
|
636
646
|
retryPendingSessionCleanups2,
|
|
637
647
|
retryPendingRustSessionCleanupsForProject2,
|
|
638
648
|
clearSession2
|
|
639
|
-
} from "./index-
|
|
649
|
+
} from "./index-znraa0kv.js";
|
|
640
650
|
import {
|
|
641
651
|
registerNotificationSink2,
|
|
642
652
|
pushNotification2,
|
|
@@ -10370,6 +10380,9 @@ function redactConfigValue(value) {
|
|
|
10370
10380
|
return typeof value;
|
|
10371
10381
|
}
|
|
10372
10382
|
var warnedProtectedTagsDeprecation = false;
|
|
10383
|
+
function formatProtectedTokensBelowMinWarning(value) {
|
|
10384
|
+
return `protected_tokens is a token floor (minimum ${PROTECTED_TOKENS_MIN}, default derived from the context window); ${value} looks like the old protected_tags count. Remove the key to use the default, or set a token count such as 16000.`;
|
|
10385
|
+
}
|
|
10373
10386
|
function warnProtectedTagsDeprecationOnce() {
|
|
10374
10387
|
if (!warnedProtectedTagsDeprecation) {
|
|
10375
10388
|
warnedProtectedTagsDeprecation = true;
|
|
@@ -10460,6 +10473,11 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
|
|
|
10460
10473
|
delete patched[key];
|
|
10461
10474
|
const defaultVal = defaults[key];
|
|
10462
10475
|
const reason = customMessagesByKey.get(key);
|
|
10476
|
+
const invalidRawValue = rawConfig[key];
|
|
10477
|
+
if (key === "protected_tokens" && typeof invalidRawValue === "number" && invalidRawValue < PROTECTED_TOKENS_MIN) {
|
|
10478
|
+
warnings.push(formatProtectedTokensBelowMinWarning(invalidRawValue));
|
|
10479
|
+
continue;
|
|
10480
|
+
}
|
|
10463
10481
|
warnings.push(`"${key}": invalid value (${redactConfigValue(rawConfig[key])}), using default ${JSON.stringify(defaultVal)}.${reason ? ` ${reason}` : ""}`);
|
|
10464
10482
|
}
|
|
10465
10483
|
const retryMigrated = migrateLegacyAgentEnabledInMemory(migrateDreamerV2(migrateLegacyExperimental(patched, preMigrationWarnings), preMigrationWarnings), preMigrationWarnings);
|
|
@@ -10702,6 +10720,12 @@ function openOpenCodeDb() {
|
|
|
10702
10720
|
}
|
|
10703
10721
|
try {
|
|
10704
10722
|
const db = new Database(dbPath, { readonly: true });
|
|
10723
|
+
try {
|
|
10724
|
+
assertOpenCodeStoreGeneration(db, "v1", dbPath);
|
|
10725
|
+
} catch (error) {
|
|
10726
|
+
db.close();
|
|
10727
|
+
throw error;
|
|
10728
|
+
}
|
|
10705
10729
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
10706
10730
|
clearOpenCodeDbReadFailure();
|
|
10707
10731
|
return db;
|
|
@@ -11192,7 +11216,19 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
11192
11216
|
let identityResolutionsSinceYield = 0;
|
|
11193
11217
|
let afterSessionId = null;
|
|
11194
11218
|
for (;; ) {
|
|
11195
|
-
|
|
11219
|
+
let sourcePage;
|
|
11220
|
+
try {
|
|
11221
|
+
sourcePage = await readPage(afterSessionId, SESSION_PAGE_SIZE);
|
|
11222
|
+
} catch (error) {
|
|
11223
|
+
try {
|
|
11224
|
+
if (!markBackfillRetryPending(db, harness, holderId, now())) {
|
|
11225
|
+
log("[session-projects] backfill lease changed before failure cleanup");
|
|
11226
|
+
}
|
|
11227
|
+
} catch (releaseError) {
|
|
11228
|
+
log(`[session-projects] failed to make backfill lease retryable after discovery failed: ${releaseError}`);
|
|
11229
|
+
}
|
|
11230
|
+
throw error;
|
|
11231
|
+
}
|
|
11196
11232
|
if (sourcePage.length === 0)
|
|
11197
11233
|
break;
|
|
11198
11234
|
afterSessionId = sourcePage.at(-1)?.sessionId ?? afterSessionId;
|
|
@@ -11523,17 +11559,47 @@ async function runDeferredV22Backfill(db, options = {}) {
|
|
|
11523
11559
|
}
|
|
11524
11560
|
|
|
11525
11561
|
// src/hooks/auto-update-checker/index.ts
|
|
11526
|
-
import { existsSync as
|
|
11562
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync5, renameSync as renameSync3, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
11527
11563
|
import { dirname as dirname4, join as join4 } from "node:path";
|
|
11528
11564
|
|
|
11565
|
+
// src/shared/auto-update-provenance.ts
|
|
11566
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
11567
|
+
var AUTO_UPDATE_CHECK_STATE_FILENAME = "last-update-check.json";
|
|
11568
|
+
function readAutoUpdateCheckState(path) {
|
|
11569
|
+
if (!existsSync5(path))
|
|
11570
|
+
return null;
|
|
11571
|
+
try {
|
|
11572
|
+
const value = JSON.parse(readFileSync3(path, "utf-8"));
|
|
11573
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11574
|
+
return null;
|
|
11575
|
+
const raw = value;
|
|
11576
|
+
const state = {};
|
|
11577
|
+
if (typeof raw.lastCheckedMs === "number" && Number.isFinite(raw.lastCheckedMs)) {
|
|
11578
|
+
state.lastCheckedMs = raw.lastCheckedMs;
|
|
11579
|
+
}
|
|
11580
|
+
if (typeof raw.updaterPinnedSpec === "string" && raw.updaterPinnedSpec.length > 0) {
|
|
11581
|
+
state.updaterPinnedSpec = raw.updaterPinnedSpec;
|
|
11582
|
+
}
|
|
11583
|
+
if (typeof raw.updaterPinnedAt === "number" && Number.isFinite(raw.updaterPinnedAt)) {
|
|
11584
|
+
state.updaterPinnedAt = raw.updaterPinnedAt;
|
|
11585
|
+
}
|
|
11586
|
+
return state;
|
|
11587
|
+
} catch {
|
|
11588
|
+
return null;
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
function isUpdaterPinnedSpec(state, configuredSpec) {
|
|
11592
|
+
return state?.updaterPinnedSpec === configuredSpec && typeof state.updaterPinnedAt === "number" && Number.isFinite(state.updaterPinnedAt);
|
|
11593
|
+
}
|
|
11594
|
+
|
|
11529
11595
|
// src/hooks/auto-update-checker/checker.ts
|
|
11530
11596
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
11531
11597
|
import { spawn } from "node:child_process";
|
|
11532
11598
|
import {
|
|
11533
11599
|
accessSync,
|
|
11534
|
-
existsSync as
|
|
11600
|
+
existsSync as existsSync6,
|
|
11535
11601
|
constants as fsConstants,
|
|
11536
|
-
readFileSync as
|
|
11602
|
+
readFileSync as readFileSync4,
|
|
11537
11603
|
renameSync as renameSync2,
|
|
11538
11604
|
statSync as statSync2,
|
|
11539
11605
|
writeFileSync as writeFileSync2
|
|
@@ -11643,9 +11709,9 @@ function getWinningConfig(directory, surface) {
|
|
|
11643
11709
|
let winner = null;
|
|
11644
11710
|
for (const path of getSurfaceConfigPaths(directory, surface)) {
|
|
11645
11711
|
try {
|
|
11646
|
-
if (!
|
|
11712
|
+
if (!existsSync6(path))
|
|
11647
11713
|
continue;
|
|
11648
|
-
const parsed = parseJsonConfig(
|
|
11714
|
+
const parsed = parseJsonConfig(readFileSync4(path, "utf-8"));
|
|
11649
11715
|
const entries = getPluginEntries(parsed);
|
|
11650
11716
|
if (entries.some((entry) => entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))) {
|
|
11651
11717
|
if (winner) {
|
|
@@ -11678,9 +11744,9 @@ function findPackageJsonUp(startPath) {
|
|
|
11678
11744
|
let dir = stat.isDirectory() ? startPath : dirname3(startPath);
|
|
11679
11745
|
for (let i = 0;i < 10; i++) {
|
|
11680
11746
|
const pkgPath = join3(dir, "package.json");
|
|
11681
|
-
if (
|
|
11747
|
+
if (existsSync6(pkgPath)) {
|
|
11682
11748
|
try {
|
|
11683
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
11749
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync4(pkgPath, "utf-8")));
|
|
11684
11750
|
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
11685
11751
|
return pkgPath;
|
|
11686
11752
|
} catch {}
|
|
@@ -11696,9 +11762,9 @@ function findPackageJsonUp(startPath) {
|
|
|
11696
11762
|
function getLocalDevPath(directory) {
|
|
11697
11763
|
for (const configPath of getConfigPaths(directory)) {
|
|
11698
11764
|
try {
|
|
11699
|
-
if (!
|
|
11765
|
+
if (!existsSync6(configPath))
|
|
11700
11766
|
continue;
|
|
11701
|
-
const rawConfig = parseJsonConfig(
|
|
11767
|
+
const rawConfig = parseJsonConfig(readFileSync4(configPath, "utf-8"));
|
|
11702
11768
|
const plugins = getPluginEntries(rawConfig);
|
|
11703
11769
|
for (const entry of plugins) {
|
|
11704
11770
|
if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
|
|
@@ -11708,7 +11774,7 @@ function getLocalDevPath(directory) {
|
|
|
11708
11774
|
const pkgPath = findPackageJsonUp(localPath);
|
|
11709
11775
|
if (!pkgPath)
|
|
11710
11776
|
continue;
|
|
11711
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
11777
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync4(pkgPath, "utf-8")));
|
|
11712
11778
|
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
11713
11779
|
return localPath;
|
|
11714
11780
|
}
|
|
@@ -11725,7 +11791,7 @@ function getLocalDevVersion(directory) {
|
|
|
11725
11791
|
const pkgPath = findPackageJsonUp(localPath);
|
|
11726
11792
|
if (!pkgPath)
|
|
11727
11793
|
return null;
|
|
11728
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
11794
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync4(pkgPath, "utf-8")));
|
|
11729
11795
|
return pkg.success ? pkg.data.version ?? null : null;
|
|
11730
11796
|
} catch {
|
|
11731
11797
|
return null;
|
|
@@ -11782,7 +11848,7 @@ function preflightConfigUpdates(paths, version) {
|
|
|
11782
11848
|
for (const path of paths) {
|
|
11783
11849
|
try {
|
|
11784
11850
|
accessSync(path, fsConstants.W_OK);
|
|
11785
|
-
const original =
|
|
11851
|
+
const original = readFileSync4(path, "utf-8");
|
|
11786
11852
|
const updated = configUpdateContent(original, version);
|
|
11787
11853
|
if (!updated)
|
|
11788
11854
|
return null;
|
|
@@ -11811,7 +11877,7 @@ function restoreConfigs(snapshots) {
|
|
|
11811
11877
|
function configsHaveSpec(paths, spec) {
|
|
11812
11878
|
return paths.every((path) => {
|
|
11813
11879
|
try {
|
|
11814
|
-
const parsed = parseJsonConfig(
|
|
11880
|
+
const parsed = parseJsonConfig(readFileSync4(path, "utf-8"));
|
|
11815
11881
|
return getPluginEntries(parsed).includes(spec);
|
|
11816
11882
|
} catch {
|
|
11817
11883
|
return false;
|
|
@@ -11901,9 +11967,9 @@ function getCachedVersion(_spec) {
|
|
|
11901
11967
|
const candidates = [getCurrentRuntimePackageJsonPath()].filter(isString);
|
|
11902
11968
|
for (const packageJsonPath of candidates) {
|
|
11903
11969
|
try {
|
|
11904
|
-
if (!
|
|
11970
|
+
if (!existsSync6(packageJsonPath))
|
|
11905
11971
|
continue;
|
|
11906
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
11972
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync4(packageJsonPath, "utf-8")));
|
|
11907
11973
|
if (pkg.success && pkg.data.version) {
|
|
11908
11974
|
if (!_spec)
|
|
11909
11975
|
cachedPackageVersion = pkg.data.version;
|
|
@@ -11945,7 +12011,6 @@ async function getLatestVersion(channel = "latest", options = {}) {
|
|
|
11945
12011
|
// src/hooks/auto-update-checker/index.ts
|
|
11946
12012
|
var DEFAULT_CHECK_INTERVAL_MS = 60 * 60 * 1000;
|
|
11947
12013
|
var DEFAULT_INIT_DELAY_MS = 5000;
|
|
11948
|
-
var TIMESTAMP_FILENAME = "last-update-check.json";
|
|
11949
12014
|
var PENDING_UPDATE_FILENAME = "pending-plugin-update.json";
|
|
11950
12015
|
var PENDING_UPDATE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
11951
12016
|
function warn2(message) {
|
|
@@ -11997,34 +12062,50 @@ async function maybeRunCheck(ctx, options) {
|
|
|
11997
12062
|
}
|
|
11998
12063
|
await runStartupCheck(ctx, options);
|
|
11999
12064
|
}
|
|
12065
|
+
function checkStatePath(storageDir) {
|
|
12066
|
+
return join4(storageDir ?? getOpenCodeStorageDir(), AUTO_UPDATE_CHECK_STATE_FILENAME);
|
|
12067
|
+
}
|
|
12068
|
+
function writeCheckState(path, state) {
|
|
12069
|
+
mkdirSync(dirname4(path), { recursive: true });
|
|
12070
|
+
const tmp = `${path}.tmp.${process.pid}`;
|
|
12071
|
+
writeFileSync3(tmp, `${JSON.stringify(state, null, 2)}
|
|
12072
|
+
`, "utf-8");
|
|
12073
|
+
renameSync3(tmp, path);
|
|
12074
|
+
}
|
|
12000
12075
|
function claimCheckSlot(storageDir, intervalMs) {
|
|
12001
12076
|
if (!storageDir)
|
|
12002
12077
|
return true;
|
|
12003
12078
|
try {
|
|
12004
|
-
const file =
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
return false;
|
|
12011
|
-
}
|
|
12012
|
-
} catch {}
|
|
12013
|
-
}
|
|
12014
|
-
mkdirSync(dirname4(file), { recursive: true });
|
|
12015
|
-
const tmp = `${file}.tmp.${process.pid}`;
|
|
12016
|
-
writeFileSync3(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
|
|
12017
|
-
renameSync3(tmp, file);
|
|
12079
|
+
const file = checkStatePath(storageDir);
|
|
12080
|
+
const previous = readAutoUpdateCheckState(file) ?? {};
|
|
12081
|
+
const last = previous.lastCheckedMs ?? 0;
|
|
12082
|
+
if (Date.now() - last < intervalMs)
|
|
12083
|
+
return false;
|
|
12084
|
+
writeCheckState(file, { ...previous, lastCheckedMs: Date.now() });
|
|
12018
12085
|
return true;
|
|
12019
12086
|
} catch (err) {
|
|
12020
12087
|
warn2(`[auto-update-checker] Could not coordinate via timestamp file: ${String(err)}`);
|
|
12021
12088
|
return true;
|
|
12022
12089
|
}
|
|
12023
12090
|
}
|
|
12091
|
+
function recordUpdaterPinnedSpec(storageDir, spec) {
|
|
12092
|
+
try {
|
|
12093
|
+
const path = checkStatePath(storageDir);
|
|
12094
|
+
const previous = readAutoUpdateCheckState(path) ?? {};
|
|
12095
|
+
writeCheckState(path, {
|
|
12096
|
+
...previous,
|
|
12097
|
+
updaterPinnedSpec: spec,
|
|
12098
|
+
updaterPinnedAt: Date.now()
|
|
12099
|
+
});
|
|
12100
|
+
} catch (err) {
|
|
12101
|
+
warn2(`[auto-update-checker] Could not record updater pin provenance: ${String(err)}`);
|
|
12102
|
+
}
|
|
12103
|
+
}
|
|
12024
12104
|
function pendingMarkerPath(storageDir) {
|
|
12025
12105
|
return join4(storageDir ?? getOpenCodeStorageDir(), PENDING_UPDATE_FILENAME);
|
|
12026
12106
|
}
|
|
12027
12107
|
function writePendingUpdateMarker(storageDir, update, version) {
|
|
12108
|
+
recordUpdaterPinnedSpec(storageDir, update.spec);
|
|
12028
12109
|
try {
|
|
12029
12110
|
const dir = storageDir ?? dirname4(pendingMarkerPath(storageDir));
|
|
12030
12111
|
mkdirSync(dir, { recursive: true });
|
|
@@ -12039,10 +12120,10 @@ function writePendingUpdateMarker(storageDir, update, version) {
|
|
|
12039
12120
|
}
|
|
12040
12121
|
function consumePendingUpdateMarker(storageDir, loadedVersion) {
|
|
12041
12122
|
const path = pendingMarkerPath(storageDir);
|
|
12042
|
-
if (!
|
|
12123
|
+
if (!existsSync7(path))
|
|
12043
12124
|
return;
|
|
12044
12125
|
try {
|
|
12045
|
-
const marker = JSON.parse(
|
|
12126
|
+
const marker = JSON.parse(readFileSync5(path, "utf-8"));
|
|
12046
12127
|
const age = Date.now() - (marker.writtenAt ?? 0);
|
|
12047
12128
|
const olderThanLoaded = typeof marker.version === "string" && typeof loadedVersion === "string" && (compareSemverCore(marker.version, loadedVersion) ?? 0) < 0;
|
|
12048
12129
|
if (marker.version === loadedVersion && marker.spec === `${PACKAGE_NAME}@${loadedVersion}` && Array.isArray(marker.configPaths) && marker.configPaths.length === 2) {
|
|
@@ -12114,11 +12195,15 @@ async function runBackgroundUpdateCheck(ctx, options) {
|
|
|
12114
12195
|
return;
|
|
12115
12196
|
}
|
|
12116
12197
|
log(`[auto-update-checker] Update available (${channel}): ${currentVersion} → ${latestVersion}`);
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12198
|
+
const updaterOwnedPin = pluginInfo.isPinned && isUpdaterPinnedSpec(readAutoUpdateCheckState(checkStatePath(options.storageDir)), pluginInfo.entry);
|
|
12199
|
+
if (pluginInfo.isPinned && !updaterOwnedPin) {
|
|
12200
|
+
showToast(ctx, `Magic Context ${latestVersion}`, `v${latestVersion} available. This version was pinned by you; update your OpenCode plugin config to upgrade.`, "info", 8000);
|
|
12201
|
+
log("[auto-update-checker] Version was pinned by you; skipping auto-update");
|
|
12120
12202
|
return;
|
|
12121
12203
|
}
|
|
12204
|
+
if (updaterOwnedPin) {
|
|
12205
|
+
log("[auto-update-checker] Version was pinned by the updater; continuing auto-update");
|
|
12206
|
+
}
|
|
12122
12207
|
if (!options.autoUpdate) {
|
|
12123
12208
|
showToast(ctx, `Magic Context ${latestVersion}`, `v${latestVersion} available. Auto-update is disabled.`, "info", 8000);
|
|
12124
12209
|
log("[auto-update-checker] Auto-update disabled, notification only");
|
|
@@ -12452,9 +12537,11 @@ class SubcModuleTransport {
|
|
|
12452
12537
|
this.wrapupSessions.delete(args.sessionId);
|
|
12453
12538
|
};
|
|
12454
12539
|
const laneDeadlineMs = Date.now() + attemptTimeoutMs;
|
|
12455
|
-
let releaseLane;
|
|
12540
|
+
let releaseLane = () => {};
|
|
12456
12541
|
try {
|
|
12457
|
-
|
|
12542
|
+
if (!args.bypassSessionLane) {
|
|
12543
|
+
releaseLane = await this.acquireCorrectnessLane(args.sessionId, args.signal, laneDeadlineMs);
|
|
12544
|
+
}
|
|
12458
12545
|
} catch (error) {
|
|
12459
12546
|
finishWrapupTracking();
|
|
12460
12547
|
if (args.method === "state_sync" && isDeadlineFailure(error)) {
|
|
@@ -12556,12 +12643,13 @@ class SubcModuleTransport {
|
|
|
12556
12643
|
args.onTimings?.(timings);
|
|
12557
12644
|
}
|
|
12558
12645
|
}
|
|
12559
|
-
async authorityRequest(sessionId, projectRoot, method, body) {
|
|
12646
|
+
async authorityRequest(sessionId, projectRoot, method, body, timeoutMs) {
|
|
12560
12647
|
const response = await this.call({
|
|
12561
12648
|
sessionId,
|
|
12562
12649
|
projectRoot,
|
|
12563
12650
|
method,
|
|
12564
|
-
body: { ...body, method, v: 1 }
|
|
12651
|
+
body: { ...body, method, v: 1 },
|
|
12652
|
+
timeoutMs
|
|
12565
12653
|
});
|
|
12566
12654
|
if (isRecord(response) && isRecord(response.result))
|
|
12567
12655
|
return response.result;
|
|
@@ -12621,6 +12709,20 @@ class SubcModuleTransport {
|
|
|
12621
12709
|
throw new Error("mirror.pull omitted page");
|
|
12622
12710
|
return { page: response.page };
|
|
12623
12711
|
}
|
|
12712
|
+
async mirrorMemory(args) {
|
|
12713
|
+
const { projectRoot, ...body } = args;
|
|
12714
|
+
const response = await this.authorityRequest(`mirror-memory:${args.module_row_id}`, projectRoot ?? this.bindRootForAuthority(), "mirror.memory", body, 1900);
|
|
12715
|
+
return {
|
|
12716
|
+
row: isRecord(response.row) ? response.row : null
|
|
12717
|
+
};
|
|
12718
|
+
}
|
|
12719
|
+
async memoryIdentityAck(args) {
|
|
12720
|
+
const { projectRoot, ...body } = args;
|
|
12721
|
+
const response = await this.authorityRequest(`memory-identity:${args.project}`, projectRoot ?? this.bindRootForAuthority(), "memory.identity.ack", body);
|
|
12722
|
+
return {
|
|
12723
|
+
acknowledged: typeof response.acknowledged === "number" ? response.acknowledged : 0
|
|
12724
|
+
};
|
|
12725
|
+
}
|
|
12624
12726
|
async deleteSession(sessionId, projectRoot) {
|
|
12625
12727
|
await this.call({
|
|
12626
12728
|
sessionId,
|
|
@@ -13631,7 +13733,7 @@ function recordRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
|
13631
13733
|
|
|
13632
13734
|
// src/features/magic-context/dreamer/task-executor.ts
|
|
13633
13735
|
import { createHash as createHash8 } from "node:crypto";
|
|
13634
|
-
import { existsSync as
|
|
13736
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
13635
13737
|
|
|
13636
13738
|
// src/agents/dreamer.ts
|
|
13637
13739
|
var DREAMER_AGENT = "dreamer";
|
|
@@ -17135,7 +17237,7 @@ async function archiveExpiredMemories(args) {
|
|
|
17135
17237
|
}
|
|
17136
17238
|
|
|
17137
17239
|
// src/features/magic-context/dreamer/maintain-docs-protected-enforcement.ts
|
|
17138
|
-
import { existsSync as
|
|
17240
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
17139
17241
|
import { join as join7 } from "node:path";
|
|
17140
17242
|
|
|
17141
17243
|
// src/features/magic-context/dreamer/protected-regions.ts
|
|
@@ -17239,8 +17341,8 @@ function snapshotMaintainDocsFiles(docsDir) {
|
|
|
17239
17341
|
for (const name of MAINTAIN_DOCS_SNAPSHOT_FILES) {
|
|
17240
17342
|
const path = join7(docsDir, name);
|
|
17241
17343
|
try {
|
|
17242
|
-
if (
|
|
17243
|
-
snapshot.set(name,
|
|
17344
|
+
if (existsSync8(path)) {
|
|
17345
|
+
snapshot.set(name, readFileSync6(path, "utf8"));
|
|
17244
17346
|
}
|
|
17245
17347
|
} catch {}
|
|
17246
17348
|
}
|
|
@@ -17250,7 +17352,7 @@ function enforceMaintainDocsProtectedRegions(args) {
|
|
|
17250
17352
|
for (const [fileName, original] of args.snapshot) {
|
|
17251
17353
|
const path = join7(args.docsDir, fileName);
|
|
17252
17354
|
try {
|
|
17253
|
-
const current =
|
|
17355
|
+
const current = readFileSync6(path, "utf8");
|
|
17254
17356
|
const { text, violated } = enforceProtectedRegions(original, current);
|
|
17255
17357
|
if (!violated) {
|
|
17256
17358
|
continue;
|
|
@@ -17267,7 +17369,7 @@ function enforceMaintainDocsProtectedRegions(args) {
|
|
|
17267
17369
|
import { createHash as createHash6 } from "node:crypto";
|
|
17268
17370
|
|
|
17269
17371
|
// src/features/magic-context/dreamer/map-memories-prompt.ts
|
|
17270
|
-
import { existsSync as
|
|
17372
|
+
import { existsSync as existsSync9, statSync as statSync3 } from "node:fs";
|
|
17271
17373
|
import path3 from "node:path";
|
|
17272
17374
|
var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context system. You map project memories to the repository files that back them.
|
|
17273
17375
|
|
|
@@ -17305,7 +17407,7 @@ function extractMemoryCandidatePaths(content, repoDir) {
|
|
|
17305
17407
|
if (!abs.startsWith(`${root}/`))
|
|
17306
17408
|
continue;
|
|
17307
17409
|
try {
|
|
17308
|
-
if (
|
|
17410
|
+
if (existsSync9(abs) && statSync3(abs).isFile())
|
|
17309
17411
|
found.add(rel);
|
|
17310
17412
|
} catch {}
|
|
17311
17413
|
if (found.size >= MAX_SEED_PATHS_PER_MEMORY)
|
|
@@ -20066,8 +20168,8 @@ async function runAgenticTask(config, ctx, helpers) {
|
|
|
20066
20168
|
const lastRunAt = getTaskScheduleState(db, projectIdentity, config.task)?.lastRunAt ?? null;
|
|
20067
20169
|
const maintainDocsSnapshot = task === "maintain-docs" ? snapshotMaintainDocsFiles(docsDir) : undefined;
|
|
20068
20170
|
const existingDocs = task === "maintain-docs" ? {
|
|
20069
|
-
architecture:
|
|
20070
|
-
structure:
|
|
20171
|
+
architecture: existsSync10(`${docsDir}/ARCHITECTURE.md`),
|
|
20172
|
+
structure: existsSync10(`${docsDir}/STRUCTURE.md`)
|
|
20071
20173
|
} : undefined;
|
|
20072
20174
|
const abortController = new AbortController;
|
|
20073
20175
|
let leaseLost = false;
|
|
@@ -20293,11 +20395,11 @@ function planDueTasks(db, projectIdentity, tasks, now) {
|
|
|
20293
20395
|
}
|
|
20294
20396
|
return due;
|
|
20295
20397
|
}
|
|
20296
|
-
function advanceAfterRun(db, projectIdentity, due, finishedAt, status, error, schedulePatch) {
|
|
20398
|
+
function advanceAfterRun(db, projectIdentity, due, finishedAt, status, error, schedulePatch, startedAt) {
|
|
20297
20399
|
writeTaskScheduleState(db, {
|
|
20298
20400
|
projectPath: projectIdentity,
|
|
20299
20401
|
task: due.config.task,
|
|
20300
|
-
lastRunAt: status === "completed" ? finishedAt : readLastRunAt(db, projectIdentity, due.config.task),
|
|
20402
|
+
lastRunAt: status === "completed" ? startedAt ?? finishedAt : readLastRunAt(db, projectIdentity, due.config.task),
|
|
20301
20403
|
nextDueAt: nextDueAtMs(due.config.schedule, finishedAt, due.scheduledAt),
|
|
20302
20404
|
schedule: due.config.schedule,
|
|
20303
20405
|
lastStatus: status,
|
|
@@ -20382,6 +20484,7 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
20382
20484
|
}
|
|
20383
20485
|
}
|
|
20384
20486
|
let outcome;
|
|
20487
|
+
const startedAt = Date.now();
|
|
20385
20488
|
try {
|
|
20386
20489
|
outcome = await executor(due.config, {
|
|
20387
20490
|
db,
|
|
@@ -20395,7 +20498,7 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
20395
20498
|
}
|
|
20396
20499
|
const finishedAt = Date.now();
|
|
20397
20500
|
if (outcome.status === "completed") {
|
|
20398
|
-
advanceAfterRun(db, projectIdentity, due, finishedAt, "completed", null, outcome.schedulePatch);
|
|
20501
|
+
advanceAfterRun(db, projectIdentity, due, finishedAt, "completed", null, outcome.schedulePatch, startedAt);
|
|
20399
20502
|
cb?.onRan?.(due.config.task, outcome.detail, outcome.backlog);
|
|
20400
20503
|
} else if (outcome.transient) {
|
|
20401
20504
|
recordTransientFailure(db, projectIdentity, due, finishedAt, outcome.error ?? null);
|
|
@@ -21523,6 +21626,8 @@ async function ensureProjectRegisteredFromOpenCodeDirectory(directory, db) {
|
|
|
21523
21626
|
registerProjectEmbedding(db, projectIdentity, routing.primary, features, directory);
|
|
21524
21627
|
if (routing.shadow) {
|
|
21525
21628
|
registerProjectShadowEmbedding(db, projectIdentity, routing.shadow, directory);
|
|
21629
|
+
} else {
|
|
21630
|
+
unregisterProjectShadowEmbedding(projectIdentity);
|
|
21526
21631
|
}
|
|
21527
21632
|
}
|
|
21528
21633
|
|
|
@@ -22224,6 +22329,56 @@ function createTagger() {
|
|
|
22224
22329
|
};
|
|
22225
22330
|
}
|
|
22226
22331
|
|
|
22332
|
+
// src/plugin/memory-id-translation.ts
|
|
22333
|
+
function unmappedMemoryIdMessage(id) {
|
|
22334
|
+
return `Error: memory id ${id} has no module mapping yet — it was written seconds ago or the mirror is behind; retry or use the id shown in <project-memory>.`;
|
|
22335
|
+
}
|
|
22336
|
+
function translateHostMemoryIds(db, hostIds) {
|
|
22337
|
+
const moduleIds = [];
|
|
22338
|
+
for (const hostId of hostIds) {
|
|
22339
|
+
const identity = moduleMemoryIdentityForHostId(db, hostId);
|
|
22340
|
+
if (!identity)
|
|
22341
|
+
return { error: unmappedMemoryIdMessage(hostId) };
|
|
22342
|
+
moduleIds.push(identity.moduleRowId);
|
|
22343
|
+
}
|
|
22344
|
+
return { moduleIds };
|
|
22345
|
+
}
|
|
22346
|
+
function moduleMemoryOperation(response, depth = 0) {
|
|
22347
|
+
if (depth > 4 || response === null || typeof response !== "object")
|
|
22348
|
+
return null;
|
|
22349
|
+
if (Array.isArray(response)) {
|
|
22350
|
+
for (const item of response) {
|
|
22351
|
+
const operation = moduleMemoryOperation(item, depth + 1);
|
|
22352
|
+
if (operation)
|
|
22353
|
+
return operation;
|
|
22354
|
+
}
|
|
22355
|
+
return null;
|
|
22356
|
+
}
|
|
22357
|
+
const record = response;
|
|
22358
|
+
if (record.memory_operation !== null && typeof record.memory_operation === "object" && typeof record.memory_operation.action === "string") {
|
|
22359
|
+
return record.memory_operation;
|
|
22360
|
+
}
|
|
22361
|
+
return moduleMemoryOperation(record.result, depth + 1);
|
|
22362
|
+
}
|
|
22363
|
+
function translateModuleMemoryMutationReply(args) {
|
|
22364
|
+
const operation = moduleMemoryOperation(args.response);
|
|
22365
|
+
if (operation?.action === "write" && operation.module_id !== undefined) {
|
|
22366
|
+
const identity = hostMemoryIdentityForModuleId(args.db, args.moduleProject, operation.module_id);
|
|
22367
|
+
const category = operation.category ?? args.requestedCategory;
|
|
22368
|
+
return identity ? `Saved memory [ID: ${identity.contextRowId}] in ${category}.` : `Saved memory in ${category}. Its id will appear in <project-memory> on the next pass.`;
|
|
22369
|
+
}
|
|
22370
|
+
if (operation?.action === "merge" && operation.canonical_module_id !== undefined) {
|
|
22371
|
+
const canonical = hostMemoryIdentityForModuleId(args.db, args.moduleProject, operation.canonical_module_id);
|
|
22372
|
+
const superseded = (operation.superseded_module_ids ?? []).map((moduleId) => hostMemoryIdentityForModuleId(args.db, args.moduleProject, moduleId)?.contextRowId).filter((id) => id !== undefined);
|
|
22373
|
+
const category = operation.category ?? args.requestedCategory;
|
|
22374
|
+
if (canonical) {
|
|
22375
|
+
return `Merged memories [${args.requestedHostIds.join(", ")}] into canonical memory [ID: ${canonical.contextRowId}] in ${category}; superseded [${superseded.join(", ")}].`;
|
|
22376
|
+
}
|
|
22377
|
+
return `Merged memories [${args.requestedHostIds.join(", ")}] into a canonical memory in ${category}. Its id will appear in <project-memory> on the next pass.`;
|
|
22378
|
+
}
|
|
22379
|
+
return null;
|
|
22380
|
+
}
|
|
22381
|
+
|
|
22227
22382
|
// src/plugin/rpc-handlers.ts
|
|
22228
22383
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
22229
22384
|
import { once } from "node:events";
|
|
@@ -27114,11 +27269,35 @@ function toFlatModuleWireBody(payload) {
|
|
|
27114
27269
|
function moduleWireBodyBytes(payload) {
|
|
27115
27270
|
return Buffer.byteLength(JSON.stringify(toFlatModuleWireBody(payload)));
|
|
27116
27271
|
}
|
|
27117
|
-
function buildPagedModuleTransformPayloads(body, pageMaxBytes = MODULE_PAGE_MAX_BYTES) {
|
|
27272
|
+
function buildPagedModuleTransformPayloads(body, pageMaxBytes = MODULE_PAGE_MAX_BYTES, forcePageEnvelope = false) {
|
|
27118
27273
|
const serializedBody = JSON.stringify(body);
|
|
27119
27274
|
const unpagedBytes = Buffer.byteLength(serializedBody);
|
|
27120
|
-
if (unpagedBytes <= pageMaxBytes)
|
|
27121
|
-
|
|
27275
|
+
if (unpagedBytes <= pageMaxBytes) {
|
|
27276
|
+
if (!forcePageEnvelope)
|
|
27277
|
+
return [{ page: body, bytes: unpagedBytes }];
|
|
27278
|
+
const pageContent = Object.fromEntries([
|
|
27279
|
+
"input",
|
|
27280
|
+
"messages",
|
|
27281
|
+
"native_messages",
|
|
27282
|
+
"ts_output",
|
|
27283
|
+
"ts_ck_messages",
|
|
27284
|
+
"normalizations",
|
|
27285
|
+
"tool_input_key_orders"
|
|
27286
|
+
].filter((field) => {
|
|
27287
|
+
const value = body[field];
|
|
27288
|
+
return Array.isArray(value) || value !== null && typeof value === "object" && field === "tool_input_key_orders";
|
|
27289
|
+
}).map((field) => [field, body[field]]));
|
|
27290
|
+
const page = {
|
|
27291
|
+
transform_page_id: crypto2.createHash("sha256").update(serializedBody).digest("hex"),
|
|
27292
|
+
transform_generation: body.shadow_generation ?? 0,
|
|
27293
|
+
transform_page_index: 0,
|
|
27294
|
+
transform_page_total: 1,
|
|
27295
|
+
transform_page_complete: true,
|
|
27296
|
+
transform_page_digest: transformPageDigest(pageContent),
|
|
27297
|
+
...body
|
|
27298
|
+
};
|
|
27299
|
+
return [{ page, bytes: Buffer.byteLength(JSON.stringify(page)) }];
|
|
27300
|
+
}
|
|
27122
27301
|
const arrayFields = [
|
|
27123
27302
|
"input",
|
|
27124
27303
|
"messages",
|
|
@@ -27768,36 +27947,43 @@ function canonicalOrdinalForMessageId(args) {
|
|
|
27768
27947
|
args.state.idOrdinalMemo.set(args.messageId, canonical);
|
|
27769
27948
|
return canonical;
|
|
27770
27949
|
}
|
|
27771
|
-
|
|
27950
|
+
var boundaryDiagnosticObserverForTest = null;
|
|
27951
|
+
function logBoundaryDiagnostic(sessionId, message) {
|
|
27952
|
+
sessionLog(sessionId, message);
|
|
27953
|
+
boundaryDiagnosticObserverForTest?.(message);
|
|
27954
|
+
}
|
|
27955
|
+
function readCompartmentBoundaryResolution(args) {
|
|
27772
27956
|
const startRaw = args.readRawById(args.compartment.startMessageId);
|
|
27773
27957
|
const endRaw = args.readRawById(args.compartment.endMessageId);
|
|
27774
|
-
|
|
27775
|
-
|
|
27776
|
-
|
|
27777
|
-
|
|
27778
|
-
|
|
27779
|
-
|
|
27780
|
-
|
|
27781
|
-
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27958
|
+
return {
|
|
27959
|
+
startRaw,
|
|
27960
|
+
endRaw,
|
|
27961
|
+
startOrdinal: canonicalOrdinalForMessageId({
|
|
27962
|
+
sessionId: args.sessionId,
|
|
27963
|
+
raw: startRaw,
|
|
27964
|
+
messageId: args.compartment.startMessageId,
|
|
27965
|
+
generation: args.state.moduleGeneration,
|
|
27966
|
+
state: args.state
|
|
27967
|
+
}),
|
|
27968
|
+
endOrdinal: canonicalOrdinalForMessageId({
|
|
27969
|
+
sessionId: args.sessionId,
|
|
27970
|
+
raw: endRaw,
|
|
27971
|
+
messageId: args.compartment.endMessageId,
|
|
27972
|
+
generation: args.state.moduleGeneration,
|
|
27973
|
+
state: args.state
|
|
27974
|
+
})
|
|
27975
|
+
};
|
|
27976
|
+
}
|
|
27977
|
+
function serializeCompartment(args) {
|
|
27978
|
+
const startCreatedAt = args.startRaw?.createdAt;
|
|
27979
|
+
const endCreatedAt = args.endRaw?.createdAt;
|
|
27794
27980
|
const dateRange = typeof startCreatedAt === "number" && typeof endCreatedAt === "number" ? { start_date: formatDate(startCreatedAt), end_date: formatDate(endCreatedAt) } : {};
|
|
27795
27981
|
return {
|
|
27796
27982
|
sequence: args.compartment.sequence,
|
|
27797
|
-
start_message: startOrdinal,
|
|
27798
|
-
end_message: endOrdinal,
|
|
27799
|
-
start_message_id: flatBlockIdForRawMessage(args.compartment.startMessageId, startRaw, "start"),
|
|
27800
|
-
end_message_id: flatBlockIdForRawMessage(args.compartment.endMessageId, endRaw, "end"),
|
|
27983
|
+
start_message: args.startOrdinal,
|
|
27984
|
+
end_message: args.endOrdinal,
|
|
27985
|
+
start_message_id: flatBlockIdForRawMessage(args.compartment.startMessageId, args.startRaw, "start"),
|
|
27986
|
+
end_message_id: flatBlockIdForRawMessage(args.compartment.endMessageId, args.endRaw, "end"),
|
|
27801
27987
|
...dateRange,
|
|
27802
27988
|
title: args.compartment.title,
|
|
27803
27989
|
content: args.compartment.content,
|
|
@@ -27811,6 +27997,15 @@ function serializeCompartment(args) {
|
|
|
27811
27997
|
created_at: args.compartment.createdAt
|
|
27812
27998
|
};
|
|
27813
27999
|
}
|
|
28000
|
+
function adjacentCompartmentBoundaryId(args) {
|
|
28001
|
+
const previous = args.direction === "previous";
|
|
28002
|
+
const row = args.db.prepare(`SELECT ${previous ? "end_message_id" : "start_message_id"} AS message_id
|
|
28003
|
+
FROM compartments
|
|
28004
|
+
WHERE session_id = ? AND sequence ${previous ? "<" : ">"} ?
|
|
28005
|
+
ORDER BY sequence ${previous ? "DESC" : "ASC"}
|
|
28006
|
+
LIMIT 1`).get(args.sessionId, args.sequence);
|
|
28007
|
+
return typeof row?.message_id === "string" && row.message_id.length > 0 ? row.message_id : null;
|
|
28008
|
+
}
|
|
27814
28009
|
function seedBoundaryFromSerializedCompartments(compartments) {
|
|
27815
28010
|
const serialized = compartments.filter(isRecord).filter((compartment) => typeof compartment.sequence === "number" && typeof compartment.end_message_id === "string");
|
|
27816
28011
|
serialized.sort((left, right) => left.sequence - right.sequence);
|
|
@@ -28283,21 +28478,102 @@ async function buildModuleStateSyncPayload(args) {
|
|
|
28283
28478
|
const compartments = [];
|
|
28284
28479
|
let serializedCount = 0;
|
|
28285
28480
|
const compartmentsToSerialize = compartmentsChanged ? args.force ? inventory ? readCompartmentsAfterSequence(args.pass.db, args.pass.sessionId, inventory.maxCompartmentSequence) : getCompartments(args.pass.db, args.pass.sessionId) : readCompartmentsAfterSequence(args.pass.db, args.pass.sessionId, acked.compartment_sequence) : [];
|
|
28286
|
-
|
|
28287
|
-
|
|
28288
|
-
|
|
28289
|
-
|
|
28290
|
-
|
|
28481
|
+
const boundaryResolutions = new Map;
|
|
28482
|
+
const resolveBoundary = (compartment) => {
|
|
28483
|
+
const cached = boundaryResolutions.get(compartment.sequence);
|
|
28484
|
+
if (cached)
|
|
28485
|
+
return cached;
|
|
28486
|
+
const resolved = readCompartmentBoundaryResolution({
|
|
28291
28487
|
compartment,
|
|
28292
28488
|
sessionId: args.pass.sessionId,
|
|
28293
28489
|
readRawById,
|
|
28294
28490
|
state: args.state
|
|
28295
28491
|
});
|
|
28296
|
-
|
|
28492
|
+
boundaryResolutions.set(compartment.sequence, resolved);
|
|
28493
|
+
return resolved;
|
|
28494
|
+
};
|
|
28495
|
+
const canonicalOrdinalForAdjacentId = (messageId) => {
|
|
28496
|
+
if (!messageId)
|
|
28497
|
+
return null;
|
|
28498
|
+
const raw = readRawById(messageId);
|
|
28499
|
+
return canonicalOrdinalForMessageId({
|
|
28500
|
+
sessionId: args.pass.sessionId,
|
|
28501
|
+
raw,
|
|
28502
|
+
messageId,
|
|
28503
|
+
generation: args.state.moduleGeneration,
|
|
28504
|
+
state: args.state
|
|
28505
|
+
});
|
|
28506
|
+
};
|
|
28507
|
+
let firstRawOrdinal;
|
|
28508
|
+
for (const [index, compartment] of compartmentsToSerialize.entries()) {
|
|
28509
|
+
args.options?.beforeSerializeCompartment?.();
|
|
28510
|
+
if (args.options?.shouldAbortSeed?.())
|
|
28511
|
+
return "seed_budget";
|
|
28512
|
+
const boundary = resolveBoundary(compartment);
|
|
28513
|
+
if (boundary.startOrdinal === "mismatch" || boundary.endOrdinal === "mismatch") {
|
|
28297
28514
|
return "mismatch";
|
|
28298
|
-
|
|
28299
|
-
|
|
28300
|
-
|
|
28515
|
+
}
|
|
28516
|
+
const startMissing = boundary.startOrdinal === null;
|
|
28517
|
+
const endMissing = boundary.endOrdinal === null;
|
|
28518
|
+
if (startMissing && endMissing) {
|
|
28519
|
+
logBoundaryDiagnostic(args.pass.sessionId, `state-sync compartment skipped session=${args.pass.sessionId} sequence=${compartment.sequence} missing_start_id=${compartment.startMessageId} missing_end_id=${compartment.endMessageId} method=both_boundaries_dangling`);
|
|
28520
|
+
continue;
|
|
28521
|
+
}
|
|
28522
|
+
let startOrdinal = boundary.startOrdinal;
|
|
28523
|
+
if (startOrdinal === null) {
|
|
28524
|
+
const previous = compartmentsToSerialize[index - 1];
|
|
28525
|
+
const previousBoundaryId = previous ? previous.endMessageId : adjacentCompartmentBoundaryId({
|
|
28526
|
+
db: args.pass.db,
|
|
28527
|
+
sessionId: args.pass.sessionId,
|
|
28528
|
+
sequence: compartment.sequence,
|
|
28529
|
+
direction: "previous"
|
|
28530
|
+
});
|
|
28531
|
+
const previousEnd = previous ? resolveBoundary(previous).endOrdinal : canonicalOrdinalForAdjacentId(previousBoundaryId);
|
|
28532
|
+
if (previousEnd === "mismatch")
|
|
28533
|
+
return "mismatch";
|
|
28534
|
+
if (typeof previousEnd === "number") {
|
|
28535
|
+
startOrdinal = previousEnd + 1;
|
|
28536
|
+
logBoundaryDiagnostic(args.pass.sessionId, `state-sync boundary repaired session=${args.pass.sessionId} sequence=${compartment.sequence} side=start missing_id=${compartment.startMessageId} resolved_ordinal=${startOrdinal} method=previous_compartment_end_plus_one`);
|
|
28537
|
+
} else if (previousBoundaryId === null) {
|
|
28538
|
+
if (firstRawOrdinal === undefined) {
|
|
28539
|
+
const ordinals = [
|
|
28540
|
+
...readRawSessionMessageIdOrdinals(args.pass.sessionId).values()
|
|
28541
|
+
].filter((ordinal) => ordinal >= 1).sort((left, right) => left - right);
|
|
28542
|
+
firstRawOrdinal = ordinals[0] ?? null;
|
|
28543
|
+
}
|
|
28544
|
+
startOrdinal = firstRawOrdinal;
|
|
28545
|
+
if (startOrdinal !== null) {
|
|
28546
|
+
logBoundaryDiagnostic(args.pass.sessionId, `state-sync boundary repaired session=${args.pass.sessionId} sequence=${compartment.sequence} side=start missing_id=${compartment.startMessageId} resolved_ordinal=${startOrdinal} method=raw_store_first_ordinal`);
|
|
28547
|
+
}
|
|
28548
|
+
}
|
|
28549
|
+
}
|
|
28550
|
+
let endOrdinal = boundary.endOrdinal;
|
|
28551
|
+
if (endOrdinal === null) {
|
|
28552
|
+
const next = compartmentsToSerialize[index + 1];
|
|
28553
|
+
const nextStart = next ? resolveBoundary(next).startOrdinal : canonicalOrdinalForAdjacentId(adjacentCompartmentBoundaryId({
|
|
28554
|
+
db: args.pass.db,
|
|
28555
|
+
sessionId: args.pass.sessionId,
|
|
28556
|
+
sequence: compartment.sequence,
|
|
28557
|
+
direction: "next"
|
|
28558
|
+
}));
|
|
28559
|
+
if (nextStart === "mismatch")
|
|
28560
|
+
return "mismatch";
|
|
28561
|
+
if (typeof nextStart === "number") {
|
|
28562
|
+
endOrdinal = nextStart - 1;
|
|
28563
|
+
logBoundaryDiagnostic(args.pass.sessionId, `state-sync boundary repaired session=${args.pass.sessionId} sequence=${compartment.sequence} side=end missing_id=${compartment.endMessageId} resolved_ordinal=${endOrdinal} method=next_compartment_start_minus_one`);
|
|
28564
|
+
}
|
|
28565
|
+
}
|
|
28566
|
+
if (startOrdinal === null || endOrdinal === null || startOrdinal > endOrdinal) {
|
|
28567
|
+
logBoundaryDiagnostic(args.pass.sessionId, `state-sync compartment skipped session=${args.pass.sessionId} sequence=${compartment.sequence} missing_start_id=${startMissing ? compartment.startMessageId : "none"} missing_end_id=${endMissing ? compartment.endMessageId : "none"} method=${startOrdinal !== null && endOrdinal !== null ? "invalid_repaired_range" : "no_adjacent_canonical_boundary"}`);
|
|
28568
|
+
continue;
|
|
28569
|
+
}
|
|
28570
|
+
compartments.push(serializeCompartment({
|
|
28571
|
+
compartment,
|
|
28572
|
+
startRaw: boundary.startRaw,
|
|
28573
|
+
endRaw: boundary.endRaw,
|
|
28574
|
+
startOrdinal,
|
|
28575
|
+
endOrdinal
|
|
28576
|
+
}));
|
|
28301
28577
|
serializedCount += 1;
|
|
28302
28578
|
const yieldEvery = Math.max(1, args.options?.yieldEveryCompartments ?? 10);
|
|
28303
28579
|
if (serializedCount % yieldEvery === 0) {
|
|
@@ -29691,8 +29967,8 @@ function registerRpcHandlers(rpcServer, args) {
|
|
|
29691
29967
|
const db = getDb();
|
|
29692
29968
|
if (!db)
|
|
29693
29969
|
return { ok: false, error: "db unavailable" };
|
|
29694
|
-
const { runManagedRecomp } = await import("./recomp-orchestrator-
|
|
29695
|
-
const { sendIgnoredMessage } = await import("./send-session-notification-
|
|
29970
|
+
const { runManagedRecomp } = await import("./recomp-orchestrator-0xx1364v.js");
|
|
29971
|
+
const { sendIgnoredMessage } = await import("./send-session-notification-jbxknvar.js");
|
|
29696
29972
|
log(`[rpc] recomp requested for session ${sessionId}`);
|
|
29697
29973
|
const ctx = await buildManagedCtx(db);
|
|
29698
29974
|
runManagedRecomp(ctx, sessionId).then((message) => {
|
|
@@ -29710,8 +29986,8 @@ function registerRpcHandlers(rpcServer, args) {
|
|
|
29710
29986
|
const db = getDb();
|
|
29711
29987
|
if (!db)
|
|
29712
29988
|
return { ok: false, error: "db unavailable" };
|
|
29713
|
-
const { runManagedUpgrade } = await import("./recomp-orchestrator-
|
|
29714
|
-
const { sendIgnoredMessage } = await import("./send-session-notification-
|
|
29989
|
+
const { runManagedUpgrade } = await import("./recomp-orchestrator-0xx1364v.js");
|
|
29990
|
+
const { sendIgnoredMessage } = await import("./send-session-notification-jbxknvar.js");
|
|
29715
29991
|
log(`[rpc] session-upgrade requested for session ${sessionId}`);
|
|
29716
29992
|
const ctx = await buildManagedCtx(db);
|
|
29717
29993
|
runManagedUpgrade(ctx, sessionId).then((message) => {
|
|
@@ -29727,7 +30003,7 @@ function registerRpcHandlers(rpcServer, args) {
|
|
|
29727
30003
|
if (!db)
|
|
29728
30004
|
return { ok: false, error: "db unavailable" };
|
|
29729
30005
|
try {
|
|
29730
|
-
const { updateSessionMeta } = await import("./storage-meta-session-
|
|
30006
|
+
const { updateSessionMeta } = await import("./storage-meta-session-z818pn1e.js");
|
|
29731
30007
|
updateSessionMeta(db, sessionId, { upgradeRemindedAt: Date.now() });
|
|
29732
30008
|
return { ok: true };
|
|
29733
30009
|
} catch (error) {
|
|
@@ -30243,6 +30519,31 @@ function releaseClaimWithoutDelivery(db, sessionId, claimToken) {
|
|
|
30243
30519
|
sessionLog(sessionId, "channel2 terminal-run claim release failed; lease will heal:", error);
|
|
30244
30520
|
}
|
|
30245
30521
|
}
|
|
30522
|
+
async function deliverSyntheticUserMessage(sessionId, deps) {
|
|
30523
|
+
const promptContext = await resolvePromptContext2(deps.client, sessionId);
|
|
30524
|
+
const body = {
|
|
30525
|
+
noReply: false,
|
|
30526
|
+
parts: [{ type: "text", text: deps.text, synthetic: true }]
|
|
30527
|
+
};
|
|
30528
|
+
if (promptContext?.agent)
|
|
30529
|
+
body.agent = promptContext.agent;
|
|
30530
|
+
if (promptContext?.model) {
|
|
30531
|
+
body.model = {
|
|
30532
|
+
providerID: promptContext.model.providerID,
|
|
30533
|
+
modelID: promptContext.model.modelID
|
|
30534
|
+
};
|
|
30535
|
+
}
|
|
30536
|
+
if (promptContext?.variant)
|
|
30537
|
+
body.variant = promptContext.variant;
|
|
30538
|
+
if (deps.beforeSend && !deps.beforeSend())
|
|
30539
|
+
return false;
|
|
30540
|
+
const session = deps.client.session;
|
|
30541
|
+
if (typeof session?.promptAsync !== "function") {
|
|
30542
|
+
throw new Error("client has no session.promptAsync");
|
|
30543
|
+
}
|
|
30544
|
+
await session.promptAsync({ path: { id: sessionId }, body });
|
|
30545
|
+
return true;
|
|
30546
|
+
}
|
|
30246
30547
|
async function maybeDeliverChannel2(sessionId, deps) {
|
|
30247
30548
|
let state;
|
|
30248
30549
|
try {
|
|
@@ -30281,36 +30582,25 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
30281
30582
|
return false;
|
|
30282
30583
|
}
|
|
30283
30584
|
try {
|
|
30284
|
-
const promptContext = await resolvePromptContext2(client, sessionId);
|
|
30285
30585
|
const reminder = deps.directiveText ?? buildChannel2Reminder(effectiveU, reclaimableToolOutputCount(deps.baseline?.baselineParts ?? []), deps.oldestReclaimableToolTags);
|
|
30286
|
-
const
|
|
30287
|
-
|
|
30288
|
-
|
|
30289
|
-
|
|
30290
|
-
|
|
30291
|
-
|
|
30292
|
-
|
|
30293
|
-
|
|
30294
|
-
|
|
30295
|
-
|
|
30296
|
-
|
|
30297
|
-
|
|
30298
|
-
|
|
30299
|
-
|
|
30300
|
-
|
|
30301
|
-
|
|
30302
|
-
|
|
30303
|
-
}
|
|
30304
|
-
const claim = getChannel2NudgeClaim(deps.db, sessionId);
|
|
30305
|
-
if (claim.state !== "claimed" || claim.claimToken !== claimToken) {
|
|
30306
|
-
sessionLog(sessionId, `channel2 ceiling nudge delivery skipped: claim no longer owned before send (state=${claim.state || "empty"})`);
|
|
30307
|
-
return false;
|
|
30308
|
-
}
|
|
30309
|
-
if (!subagentRunIsActive(deps, sessionId)) {
|
|
30310
|
-
releaseClaimWithoutDelivery(deps.db, sessionId, claimToken);
|
|
30586
|
+
const sent = await deliverSyntheticUserMessage(sessionId, {
|
|
30587
|
+
client,
|
|
30588
|
+
text: reminder,
|
|
30589
|
+
beforeSend: () => {
|
|
30590
|
+
const claim = getChannel2NudgeClaim(deps.db, sessionId);
|
|
30591
|
+
if (claim.state !== "claimed" || claim.claimToken !== claimToken) {
|
|
30592
|
+
sessionLog(sessionId, `channel2 ceiling nudge delivery skipped: claim no longer owned before send (state=${claim.state || "empty"})`);
|
|
30593
|
+
return false;
|
|
30594
|
+
}
|
|
30595
|
+
if (!subagentRunIsActive(deps, sessionId)) {
|
|
30596
|
+
releaseClaimWithoutDelivery(deps.db, sessionId, claimToken);
|
|
30597
|
+
return false;
|
|
30598
|
+
}
|
|
30599
|
+
return true;
|
|
30600
|
+
}
|
|
30601
|
+
});
|
|
30602
|
+
if (!sent)
|
|
30311
30603
|
return false;
|
|
30312
|
-
}
|
|
30313
|
-
await session.promptAsync({ path: { id: sessionId }, body });
|
|
30314
30604
|
} catch (error) {
|
|
30315
30605
|
try {
|
|
30316
30606
|
const restored = casChannel2NudgeClaim(deps.db, sessionId, "pending", claimToken);
|
|
@@ -30346,7 +30636,7 @@ function canConsumeDeferredOnThisPass(args) {
|
|
|
30346
30636
|
return args.schedulerDecision === "execute" || args.contextPercentage >= (args.forceMaterializationPercentage ?? escalationBands(65).forceMaterializationPercentage);
|
|
30347
30637
|
}
|
|
30348
30638
|
function hasReclaimRide(signals) {
|
|
30349
|
-
return signals.hardFold || signals.force || signals.explicitFlush || signals.publishedHistory
|
|
30639
|
+
return signals.hardFold || signals.force || signals.explicitFlush || signals.publishedHistory;
|
|
30350
30640
|
}
|
|
30351
30641
|
|
|
30352
30642
|
// src/hooks/magic-context/caveman-cleanup.ts
|
|
@@ -30468,7 +30758,7 @@ function replayCavemanCompression(sessionId, db, targets, tags) {
|
|
|
30468
30758
|
}
|
|
30469
30759
|
|
|
30470
30760
|
// src/hooks/magic-context/compaction-off-transition.ts
|
|
30471
|
-
import { existsSync as
|
|
30761
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
30472
30762
|
var loggedUnverifiedMarkerCleanupRetry = false;
|
|
30473
30763
|
var COMPACTION_OFF_FLIP_NOTICE = [
|
|
30474
30764
|
"## Magic Context — compaction-off mode is now active",
|
|
@@ -30503,7 +30793,7 @@ function clearCachedM0Baseline(db, sessionId) {
|
|
|
30503
30793
|
return (result.changes ?? 0) > 0;
|
|
30504
30794
|
}
|
|
30505
30795
|
function cleanupOffMarkers(sessionId) {
|
|
30506
|
-
if (!
|
|
30796
|
+
if (!existsSync12(getOpenCodeDbPath())) {
|
|
30507
30797
|
return { verified: true, removedLineages: 0, removedRows: 0, retainedLineages: 0 };
|
|
30508
30798
|
}
|
|
30509
30799
|
return removeMcOwnedCompactionMarkers(sessionId, MARKER_SUMMARY_TEXT);
|
|
@@ -31440,7 +31730,7 @@ function createPassOutcome() {
|
|
|
31440
31730
|
}
|
|
31441
31731
|
|
|
31442
31732
|
// src/hooks/magic-context/rust-mode-transform.ts
|
|
31443
|
-
import { createHash as createHash12 } from "node:crypto";
|
|
31733
|
+
import { createHash as createHash12, randomUUID as randomUUID6 } from "node:crypto";
|
|
31444
31734
|
|
|
31445
31735
|
// src/hooks/magic-context/raw-fallback-context-limit.ts
|
|
31446
31736
|
class RawFallbackContextLimitError extends Error {
|
|
@@ -33956,7 +34246,8 @@ async function runPostTransformPhase(args) {
|
|
|
33956
34246
|
const firstRenderBust = m0M1EnabledForFold && !completeCachedPrefixAvailable;
|
|
33957
34247
|
let foldExecutedThisPass = false;
|
|
33958
34248
|
let publishedM1RefreshedThisPass = false;
|
|
33959
|
-
|
|
34249
|
+
let prefixPreflightFailed = false;
|
|
34250
|
+
const softRefreshOpportunity = args.schedulerDecision === "execute" || deferredMaterialize;
|
|
33960
34251
|
let m0RematerializedThisPass = false;
|
|
33961
34252
|
const m0CoverageBeforeFold = args.sessionMeta.cachedM0Bytes === null ? -1 : args.sessionMeta.cachedM0MaxCompartmentSeq;
|
|
33962
34253
|
let m0MaterializeReason = null;
|
|
@@ -33983,6 +34274,7 @@ async function runPostTransformPhase(args) {
|
|
|
33983
34274
|
compactionOff
|
|
33984
34275
|
});
|
|
33985
34276
|
preparedPrefix = foldResult;
|
|
34277
|
+
prefixPreflightFailed = foldResult.materializationContentionRetryExhausted === true;
|
|
33986
34278
|
foldExecutedThisPass = foldExecutesThisPass(foldDueDecision.value || softRefreshOpportunity, foldResult.m0RematerializedThisPass);
|
|
33987
34279
|
publishedM1RefreshedThisPass = !foldResult.materializationContentionRetryExhausted && previousM1 !== args.sessionMeta.cachedM1Bytes?.toString("utf8");
|
|
33988
34280
|
m0RematerializedThisPass = foldResult.m0RematerializedThisPass;
|
|
@@ -34004,29 +34296,29 @@ async function runPostTransformPhase(args) {
|
|
|
34004
34296
|
}
|
|
34005
34297
|
} catch (error) {
|
|
34006
34298
|
preparedPrefix = cachedPrefixBeforePreflight;
|
|
34299
|
+
prefixPreflightFailed = true;
|
|
34007
34300
|
args.passOutcome?.record("m0-m1-fold-preexecution-degradation");
|
|
34008
34301
|
sessionLog(args.sessionId, "transform: m[0] HARD fold pre-execution failed:", getErrorMessage(error));
|
|
34009
34302
|
}
|
|
34010
34303
|
sessionLog(args.sessionId, `m[0] HARD fold decision: reason=${foldDueDecision.reason ?? "unknown"} executed=${foldExecutedThisPass}`);
|
|
34011
34304
|
}
|
|
34012
|
-
const
|
|
34013
|
-
const shouldReadPendingOps = !compactionOff && (materializationRequested || args.schedulerDecision === "execute" || forceMaterialization || foldExecutedThisPass || firstRenderBust || compartmentRunning);
|
|
34305
|
+
const shouldReadPendingOps = !compactionOff && (materializationRequested || publishedM1RefreshedThisPass || args.schedulerDecision === "execute" || forceMaterialization || foldExecutedThisPass || firstRenderBust || compartmentRunning);
|
|
34014
34306
|
const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
|
|
34015
34307
|
const hasPendingUserOps = pendingOps.length > 0;
|
|
34016
34308
|
const formatPendingOpsDepth = () => {
|
|
34017
34309
|
const depth = getPendingOpsCount(args.db, args.sessionId);
|
|
34018
34310
|
return depth === null ? "not loaded (deferred pass)" : String(depth);
|
|
34019
34311
|
};
|
|
34020
|
-
const shouldApplyPendingOps = !compactionOff && (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization || foldExecutedThisPass || firstRenderBust) && publishedWorkDrainAllowed;
|
|
34021
34312
|
const rideSignals = {
|
|
34022
34313
|
hardFold: foldExecutedThisPass || firstRenderBust,
|
|
34023
|
-
force:
|
|
34024
|
-
explicitFlush: isExplicitFlush,
|
|
34025
|
-
publishedHistory: publishedM1RefreshedThisPass || !m0M1EnabledForFold && (args.pendingCompartmentInjection?.compartmentCount ?? 0) > 0 && (args.historyRebuiltThisPass || args.rebuiltHistoryFromInitialPrepare || args.canConsumeDeferredLate && args.deferredHistoryWasPendingAtPassStart)
|
|
34026
|
-
|
|
34027
|
-
|
|
34028
|
-
|
|
34029
|
-
|
|
34314
|
+
force: emergencyDropEligible && (args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0),
|
|
34315
|
+
explicitFlush: isExplicitFlush || deferredMaterialize && !prefixPreflightFailed,
|
|
34316
|
+
publishedHistory: publishedM1RefreshedThisPass || !m0M1EnabledForFold && (args.pendingCompartmentInjection?.compartmentCount ?? 0) > 0 && (args.historyRebuiltThisPass || args.rebuiltHistoryFromInitialPrepare || args.canConsumeDeferredLate && args.deferredHistoryWasPendingAtPassStart)
|
|
34317
|
+
};
|
|
34318
|
+
const publishedWorkDrainAllowed = !compactionOff && hasReclaimRide(rideSignals);
|
|
34319
|
+
const shouldApplyPendingOps = publishedWorkDrainAllowed;
|
|
34320
|
+
const shouldRunHeuristics = !compactionOff && hasReclaimRide(rideSignals) && (rideSignals.publishedHistory || materializationRequested || forceMaterialization || foldExecutedThisPass || firstRenderBust || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
|
|
34321
|
+
const isCacheBustingPass = publishedWorkDrainAllowed;
|
|
34030
34322
|
if (isCacheBustingPass && args.client && args.ctxReduceAvailability.callable && !hasLoggedCtxReducePermissionDeny(args.sessionId)) {
|
|
34031
34323
|
try {
|
|
34032
34324
|
const denied = await resolveToolPermissionDenied(args.client, args.sessionId, "ctx_reduce", args.activeAgent);
|
|
@@ -34052,13 +34344,8 @@ async function runPostTransformPhase(args) {
|
|
|
34052
34344
|
if (!shouldApplyPendingOps && args.schedulerDeferReason !== undefined && args.schedulerDeferReason !== null) {
|
|
34053
34345
|
sessionLog(args.sessionId, `pending ops WILL NOT APPLY — reason=${args.schedulerDeferReason} pendingOps=${formatPendingOpsDepth()} context=${args.contextUsage.percentage.toFixed(1)}%`);
|
|
34054
34346
|
}
|
|
34055
|
-
if (
|
|
34056
|
-
|
|
34057
|
-
const bypassReason = forceMaterialization ? `emergency >=${args.forceMaterializationPercentage}%` : "m0 hard fold";
|
|
34058
|
-
sessionLog(args.sessionId, `transform: compartment-gate bypass (${bypassReason}) — applying ${pendingOps.length} pending ops while compartment agent runs (${args.contextUsage.percentage.toFixed(1)}%)`);
|
|
34059
|
-
} else {
|
|
34060
|
-
sessionLog(args.sessionId, "transform: deferring pending ops — compartment agent in progress");
|
|
34061
|
-
}
|
|
34347
|
+
if (hasPendingUserOps && !shouldApplyPendingOps) {
|
|
34348
|
+
sessionLog(args.sessionId, `pending ops held — reason=no originating cache-bust opportunity; scheduler=${args.schedulerDecision}, historianRunning=${compartmentRunning}`);
|
|
34062
34349
|
}
|
|
34063
34350
|
let explicitMaterializedSuccessfully = false;
|
|
34064
34351
|
let deferredMaterializedSuccessfully = false;
|
|
@@ -34090,9 +34377,6 @@ async function runPostTransformPhase(args) {
|
|
|
34090
34377
|
const tApply = performance.now();
|
|
34091
34378
|
pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.contextUsage.percentage >= 95 ? newestCtxReduceTagNumbers(getTagsBySession(args.db, args.sessionId)) : args.protectedTagIds, undefined, pendingOps, [], new Set, (reduction) => droppedTokenReductions.push(reduction));
|
|
34092
34379
|
if (pendingOpsDidMutate) {
|
|
34093
|
-
rideSignals.agentDrop = true;
|
|
34094
|
-
isCacheBustingPass = hasReclaimRide(rideSignals);
|
|
34095
|
-
shouldRunHeuristics = isCacheBustingPass;
|
|
34096
34380
|
droppedCount += pendingOps.length;
|
|
34097
34381
|
for (const pendingOp of pendingOps) {
|
|
34098
34382
|
const message = args.targets.get(pendingOp.tagId)?.message;
|
|
@@ -34854,6 +35138,8 @@ var RUST_PARK_RETRY_INTERVAL = 5;
|
|
|
34854
35138
|
var RUST_EMERGENCY_WALL_PCT = 95;
|
|
34855
35139
|
var RUST_PARK_PROBE_PRESSURE_BYPASS_PCT = 90;
|
|
34856
35140
|
var RUST_SEND_TIMEOUT_MS = 15000;
|
|
35141
|
+
var RUST_SILENT_RESEND_AFTER_MS = 1e4;
|
|
35142
|
+
var RUST_HEALTH_PROBE_TIMEOUT_MS = 2000;
|
|
34857
35143
|
var RUST_LKG_FROZEN_HEALTHY_PASS_LIMIT = 8;
|
|
34858
35144
|
var RUST_LKG_FROZEN_RAW_TAIL_GROWTH_LIMIT = 16;
|
|
34859
35145
|
function activeAgentFromMessages(messages) {
|
|
@@ -35763,6 +36049,64 @@ function createRustModeTransform(deps, options) {
|
|
|
35763
36049
|
clearTimeout(timer);
|
|
35764
36050
|
}
|
|
35765
36051
|
};
|
|
36052
|
+
const callTransformWithSilentResend = async (args, attemptTimeoutMs) => {
|
|
36053
|
+
const startedAtMs = Date.now();
|
|
36054
|
+
const deadlineMs = startedAtMs + attemptTimeoutMs;
|
|
36055
|
+
const silentAfterMs = options.silentResendAfterMsForTests ?? RUST_SILENT_RESEND_AFTER_MS;
|
|
36056
|
+
const probeTimeoutMs = options.healthProbeTimeoutMsForTests ?? RUST_HEALTH_PROBE_TIMEOUT_MS;
|
|
36057
|
+
const originalAttemptId = randomUUID6();
|
|
36058
|
+
const originalBody = isRecord4(args.body) ? { ...args.body, attempt_id: originalAttemptId } : args.body;
|
|
36059
|
+
const original = callModule({ ...args, body: originalBody }, attemptTimeoutMs);
|
|
36060
|
+
if (attemptTimeoutMs <= silentAfterMs)
|
|
36061
|
+
return original;
|
|
36062
|
+
let silentTimer;
|
|
36063
|
+
const first = await Promise.race([
|
|
36064
|
+
original.then((response) => ({ kind: "response", response }), (error) => ({ kind: "error", error })),
|
|
36065
|
+
new Promise((resolve) => {
|
|
36066
|
+
silentTimer = setTimeout(() => resolve({ kind: "silent" }), silentAfterMs);
|
|
36067
|
+
})
|
|
36068
|
+
]);
|
|
36069
|
+
if (first.kind !== "silent")
|
|
36070
|
+
clearTimeout(silentTimer);
|
|
36071
|
+
if (first.kind === "response")
|
|
36072
|
+
return first.response;
|
|
36073
|
+
if (first.kind === "error")
|
|
36074
|
+
throw first.error;
|
|
36075
|
+
const probeBudgetMs = Math.min(probeTimeoutMs, Math.max(0, deadlineMs - Date.now()));
|
|
36076
|
+
if (probeBudgetMs <= 0)
|
|
36077
|
+
return original;
|
|
36078
|
+
try {
|
|
36079
|
+
await callModule({
|
|
36080
|
+
sessionId: args.sessionId,
|
|
36081
|
+
projectRoot: args.projectRoot,
|
|
36082
|
+
method: "session.status",
|
|
36083
|
+
body: {
|
|
36084
|
+
method: "session.status",
|
|
36085
|
+
v: 1,
|
|
36086
|
+
session_id: args.sessionId
|
|
36087
|
+
},
|
|
36088
|
+
bypassSessionLane: true
|
|
36089
|
+
}, probeBudgetMs);
|
|
36090
|
+
} catch {
|
|
36091
|
+
return original;
|
|
36092
|
+
}
|
|
36093
|
+
const remainingMs = Math.max(0, deadlineMs - Date.now());
|
|
36094
|
+
if (remainingMs <= 0)
|
|
36095
|
+
return original;
|
|
36096
|
+
const resendAttemptId = randomUUID6();
|
|
36097
|
+
sessionLog(args.sessionId, `rust silent resend original_attempt=${originalAttemptId} resend_attempt=${resendAttemptId} silent_gap_ms=${Date.now() - startedAtMs}`);
|
|
36098
|
+
const resendBody = isRecord4(args.body) ? {
|
|
36099
|
+
...args.body,
|
|
36100
|
+
attempt_id: resendAttemptId,
|
|
36101
|
+
original_attempt_id: originalAttemptId,
|
|
36102
|
+
resend: true
|
|
36103
|
+
} : args.body;
|
|
36104
|
+
return callModule({
|
|
36105
|
+
...args,
|
|
36106
|
+
body: resendBody,
|
|
36107
|
+
bypassSessionLane: true
|
|
36108
|
+
}, remainingMs);
|
|
36109
|
+
};
|
|
35766
36110
|
const markFailure = (sessionId, state, error) => {
|
|
35767
36111
|
state.consecutiveFailures = isNonRetryableStateSyncFailure(error) ? Math.max(RUST_FAILURE_PARK_THRESHOLD, state.consecutiveFailures + 1) : state.consecutiveFailures + 1;
|
|
35768
36112
|
state.failureCount += 1;
|
|
@@ -35979,6 +36323,8 @@ function createRustModeTransform(deps, options) {
|
|
|
35979
36323
|
} catch {}
|
|
35980
36324
|
let appliedAt;
|
|
35981
36325
|
let emergencyFailClosed = false;
|
|
36326
|
+
let providerProvenEmergency = false;
|
|
36327
|
+
let recoveryProjectRoot = options.projectRoot ?? deps.directory ?? "";
|
|
35982
36328
|
const passUsageSnapshot = loadContextUsage(deps.contextUsageMap, deps.db, sessionId);
|
|
35983
36329
|
requestInputTokens = Math.max(0, Math.floor(passUsageSnapshot.inputTokens));
|
|
35984
36330
|
let preflightError;
|
|
@@ -36012,11 +36358,15 @@ function createRustModeTransform(deps, options) {
|
|
|
36012
36358
|
}
|
|
36013
36359
|
const transformGeometry = transformGeometryForWire(resolvedWindowGeometry);
|
|
36014
36360
|
const hasTrustedEmergencyWall = transformGeometry ? transformGeometry.usable_hard > 0 : resolvedContextLimit !== undefined && resolvedContextLimit > 0;
|
|
36015
|
-
|
|
36361
|
+
const hardWallPercentage = hardWallUsagePercentage(passUsageSnapshot, transformGeometry);
|
|
36362
|
+
const providerOverflowProven = isProviderOverflowFailClosedProven(sessionId);
|
|
36363
|
+
emergencyFailClosed = providerOverflowProven || hardWallPercentage >= RUST_EMERGENCY_WALL_PCT && hasTrustedEmergencyWall;
|
|
36016
36364
|
if (overflowState) {
|
|
36017
36365
|
const detectedLimitMatchesModel = overflowState.detectedContextLimitModelKey === null || canonicalModelIdentity(overflowState.detectedContextLimitModelKey) === canonicalModelIdentity(modelKey ?? "");
|
|
36018
36366
|
const hasProviderProof = overflowState.detectedContextLimit > 0 && detectedLimitMatchesModel || isProviderOverflowReconfirmed(sessionId);
|
|
36019
|
-
|
|
36367
|
+
const persistedProviderEmergency = overflowState.needsEmergencyRecovery && overflowState.emergencyRecoveryOrigin === "provider_overflow" && hasProviderProof;
|
|
36368
|
+
emergencyFailClosed ||= persistedProviderEmergency;
|
|
36369
|
+
providerProvenEmergency = hardWallPercentage >= RUST_EMERGENCY_WALL_PCT && (providerOverflowProven || persistedProviderEmergency);
|
|
36020
36370
|
}
|
|
36021
36371
|
const serveRawFallback = (cause) => {
|
|
36022
36372
|
const contextLimit = transformGeometry?.usable_hard ?? resolvedContextLimit ?? (overflowState && overflowState.detectedContextLimit > 0 ? overflowState.detectedContextLimit : undefined);
|
|
@@ -36319,6 +36669,7 @@ function createRustModeTransform(deps, options) {
|
|
|
36319
36669
|
nowMs: Date.now()
|
|
36320
36670
|
};
|
|
36321
36671
|
const projectRoot = options.projectRoot ?? directory;
|
|
36672
|
+
recoveryProjectRoot = projectRoot;
|
|
36322
36673
|
const authoritySeqAdoption = { used: false };
|
|
36323
36674
|
const memorySyncRequested = options.memorySyncRequestedSessions?.delete(sessionId) === true;
|
|
36324
36675
|
const currentStateSyncInputSignature = stateSyncInputSignature({
|
|
@@ -36513,7 +36864,7 @@ function createRustModeTransform(deps, options) {
|
|
|
36513
36864
|
logStage(sessionId, "wireBuild", wireBuildStartedAt, timings, wireDelta ? `mode=tail_delta input=${encodedInput.length}` : "mode=full");
|
|
36514
36865
|
const sendTransformSeries = async (payload, detail = "") => {
|
|
36515
36866
|
const pagingStartedAt = performance.now();
|
|
36516
|
-
const pages = buildPagedModuleTransformPayloads(payload, options.modulePageMaxBytes);
|
|
36867
|
+
const pages = buildPagedModuleTransformPayloads(payload, options.modulePageMaxBytes, true);
|
|
36517
36868
|
timings.paging += performance.now() - pagingStartedAt;
|
|
36518
36869
|
const seedMessageCount = Array.isArray(payload.input) ? payload.input.length : Array.isArray(payload.messages) ? payload.messages.length : 0;
|
|
36519
36870
|
const paged = pages.some((entry) => typeof entry.page.transform_page_id === "string");
|
|
@@ -36524,7 +36875,7 @@ function createRustModeTransform(deps, options) {
|
|
|
36524
36875
|
try {
|
|
36525
36876
|
const attemptClass = paged ? index === pages.length - 1 ? "transform_series_execute" : "transform_page_upload" : undefined;
|
|
36526
36877
|
const attemptTimeoutMs = options.moduleTimeoutMs ?? (attemptClass === "transform_series_execute" ? transformColdStartExecuteTimeoutMs(seedMessageCount) : attemptClass === "transform_page_upload" ? TRANSFORM_PAGE_UPLOAD_TIMEOUT_MS : timeoutMs);
|
|
36527
|
-
|
|
36878
|
+
const callArgs = {
|
|
36528
36879
|
sessionId,
|
|
36529
36880
|
projectRoot,
|
|
36530
36881
|
method: "transform",
|
|
@@ -36535,7 +36886,8 @@ function createRustModeTransform(deps, options) {
|
|
|
36535
36886
|
},
|
|
36536
36887
|
generationSensitive: paged && index > 0,
|
|
36537
36888
|
attemptClass
|
|
36538
|
-
}
|
|
36889
|
+
};
|
|
36890
|
+
moduleResponse = page.transform_page_complete ? await callTransformWithSilentResend(callArgs, attemptTimeoutMs) : await callModule(callArgs, attemptTimeoutMs);
|
|
36539
36891
|
} catch (error) {
|
|
36540
36892
|
if (paged && isTransformPageAttemptMismatch(error)) {
|
|
36541
36893
|
return {
|
|
@@ -37061,6 +37413,21 @@ function createRustModeTransform(deps, options) {
|
|
|
37061
37413
|
sessionLog(sessionId, "mc_rust_emergency_refusal before_lkg");
|
|
37062
37414
|
markFailure(sessionId, state, error);
|
|
37063
37415
|
finishPass(false, false);
|
|
37416
|
+
const refusedUser = newestUserMessage(messages);
|
|
37417
|
+
const refusedUserMessageId = refusedUser ? messageIdOf(refusedUser) : null;
|
|
37418
|
+
if (refusedUserMessageId) {
|
|
37419
|
+
try {
|
|
37420
|
+
options.onEngineReconnectRefusal?.({
|
|
37421
|
+
sessionId,
|
|
37422
|
+
projectRoot: recoveryProjectRoot,
|
|
37423
|
+
refusedUserMessageId,
|
|
37424
|
+
providerProvenEmergency,
|
|
37425
|
+
compactionOff: deps.compactionOff === true
|
|
37426
|
+
});
|
|
37427
|
+
} catch (recoveryError) {
|
|
37428
|
+
sessionLog(sessionId, "rust refusal recovery failed to arm:", recoveryError);
|
|
37429
|
+
}
|
|
37430
|
+
}
|
|
37064
37431
|
throw new EmergencyFailClosedError(ENGINE_RECONNECTING_USER_MESSAGE, {
|
|
37065
37432
|
cause: error
|
|
37066
37433
|
});
|
|
@@ -37557,6 +37924,7 @@ function createTransform(deps) {
|
|
|
37557
37924
|
projectRoot: deps.rustModeProjectRoot,
|
|
37558
37925
|
notifyParked: deps.onRustModeParked,
|
|
37559
37926
|
onProjectPrepared: deps.onRustModeProjectPrepared,
|
|
37927
|
+
onEngineReconnectRefusal: deps.onRustEngineReconnectRefusal,
|
|
37560
37928
|
memorySyncRequestedSessions: deps.rustMemorySyncRequestedSessions,
|
|
37561
37929
|
allowAuthorityProtocolBypassForTests: deps.rustModeAllowAuthorityProtocolBypassForTests
|
|
37562
37930
|
}) : undefined;
|
|
@@ -38975,6 +39343,142 @@ function formatEmbedFailureSummary(embedded, remaining, failure, style = "markdo
|
|
|
38975
39343
|
return `${progress} ${renderEmbeddingFailure(failure?.class ?? "empty_result", style)}`;
|
|
38976
39344
|
}
|
|
38977
39345
|
|
|
39346
|
+
// src/hooks/magic-context/rust-refusal-recovery.ts
|
|
39347
|
+
var RUST_REFUSAL_RECOVERY_PROMPT = "Magic Context's engine reconnected. Continue from where you stopped.";
|
|
39348
|
+
var RUST_REFUSAL_RECOVERY_POLL_MS = 2000;
|
|
39349
|
+
var RUST_REFUSAL_RECOVERY_MAX_MS = 5 * 60000;
|
|
39350
|
+
var RUST_REFUSAL_RECOVERY_PROBE_TIMEOUT_MS = 2000;
|
|
39351
|
+
function errorContainsReconnectRefusal(error) {
|
|
39352
|
+
try {
|
|
39353
|
+
return JSON.stringify(error).includes(ENGINE_RECONNECTING_USER_MESSAGE);
|
|
39354
|
+
} catch {
|
|
39355
|
+
return false;
|
|
39356
|
+
}
|
|
39357
|
+
}
|
|
39358
|
+
function createRustRefusalRecovery(options) {
|
|
39359
|
+
const watchers = new Map;
|
|
39360
|
+
const latestRefusalBySession = new Map;
|
|
39361
|
+
const pollIntervalMs = options.pollIntervalMs ?? RUST_REFUSAL_RECOVERY_POLL_MS;
|
|
39362
|
+
const maxDurationMs = options.maxDurationMs ?? RUST_REFUSAL_RECOVERY_MAX_MS;
|
|
39363
|
+
const probeTimeoutMs = options.probeTimeoutMs ?? RUST_REFUSAL_RECOVERY_PROBE_TIMEOUT_MS;
|
|
39364
|
+
const readLatestMessage = options.readLatestMessage ?? latestPersistedMessageForRecovery;
|
|
39365
|
+
const cancel = (sessionId) => {
|
|
39366
|
+
const watcher = watchers.get(sessionId);
|
|
39367
|
+
if (!watcher)
|
|
39368
|
+
return;
|
|
39369
|
+
clearTimeout(watcher.timer);
|
|
39370
|
+
watchers.delete(sessionId);
|
|
39371
|
+
};
|
|
39372
|
+
const schedule = (sessionId, watcher) => {
|
|
39373
|
+
watcher.timer = setTimeout(() => void poll(sessionId, watcher), pollIntervalMs);
|
|
39374
|
+
};
|
|
39375
|
+
const probe = async (watcher, sessionId) => {
|
|
39376
|
+
const controller = new AbortController;
|
|
39377
|
+
const timeoutError = new Error("rust refusal recovery probe timed out");
|
|
39378
|
+
const timer = setTimeout(() => controller.abort(timeoutError), probeTimeoutMs);
|
|
39379
|
+
try {
|
|
39380
|
+
await Promise.race([
|
|
39381
|
+
options.moduleClient.call({
|
|
39382
|
+
sessionId,
|
|
39383
|
+
projectRoot: watcher.projectRoot,
|
|
39384
|
+
method: "session.status",
|
|
39385
|
+
body: { method: "session.status", v: 1, session_id: sessionId },
|
|
39386
|
+
signal: controller.signal,
|
|
39387
|
+
timeoutMs: probeTimeoutMs,
|
|
39388
|
+
bypassSessionLane: true
|
|
39389
|
+
}),
|
|
39390
|
+
new Promise((_resolve, reject) => {
|
|
39391
|
+
controller.signal.addEventListener("abort", () => reject(controller.signal.reason ?? timeoutError), { once: true });
|
|
39392
|
+
})
|
|
39393
|
+
]);
|
|
39394
|
+
return true;
|
|
39395
|
+
} catch {
|
|
39396
|
+
return false;
|
|
39397
|
+
} finally {
|
|
39398
|
+
clearTimeout(timer);
|
|
39399
|
+
}
|
|
39400
|
+
};
|
|
39401
|
+
const poll = async (sessionId, watcher) => {
|
|
39402
|
+
if (watchers.get(sessionId) !== watcher)
|
|
39403
|
+
return;
|
|
39404
|
+
if (Date.now() - watcher.armedAtMs >= maxDurationMs) {
|
|
39405
|
+
cancel(sessionId);
|
|
39406
|
+
sessionLog(sessionId, "rust refusal recovery expired before the module became healthy");
|
|
39407
|
+
return;
|
|
39408
|
+
}
|
|
39409
|
+
if (!await probe(watcher, sessionId)) {
|
|
39410
|
+
schedule(sessionId, watcher);
|
|
39411
|
+
return;
|
|
39412
|
+
}
|
|
39413
|
+
let latest;
|
|
39414
|
+
try {
|
|
39415
|
+
latest = readLatestMessage(sessionId);
|
|
39416
|
+
} catch (error) {
|
|
39417
|
+
sessionLog(sessionId, "rust refusal recovery could not read persisted history:", error);
|
|
39418
|
+
schedule(sessionId, watcher);
|
|
39419
|
+
return;
|
|
39420
|
+
}
|
|
39421
|
+
const matchingAssistant = latest?.role === "assistant" && (latest.parentID === undefined || latest.parentID === watcher.refusedUserMessageId) ? latest : null;
|
|
39422
|
+
const stillRefusedStep = matchingAssistant !== null && (errorContainsReconnectRefusal(matchingAssistant.error) || matchingAssistant.parentID === watcher.refusedUserMessageId && matchingAssistant.error === undefined && matchingAssistant.completedAt === undefined);
|
|
39423
|
+
if (!stillRefusedStep) {
|
|
39424
|
+
cancel(sessionId);
|
|
39425
|
+
sessionLog(sessionId, "rust refusal recovery skipped because persisted history advanced past the refused step");
|
|
39426
|
+
return;
|
|
39427
|
+
}
|
|
39428
|
+
if (watcher.state !== "pending" || watchers.get(sessionId) !== watcher)
|
|
39429
|
+
return;
|
|
39430
|
+
watcher.state = "claimed";
|
|
39431
|
+
const delivered = await deliverSyntheticUserMessage(sessionId, {
|
|
39432
|
+
client: options.client,
|
|
39433
|
+
text: RUST_REFUSAL_RECOVERY_PROMPT,
|
|
39434
|
+
beforeSend: () => watchers.get(sessionId) === watcher && watcher.state === "claimed"
|
|
39435
|
+
}).catch((error) => {
|
|
39436
|
+
sessionLog(sessionId, "rust refusal recovery synthetic delivery failed:", error);
|
|
39437
|
+
return false;
|
|
39438
|
+
});
|
|
39439
|
+
if (!delivered) {
|
|
39440
|
+
if (watchers.get(sessionId) === watcher) {
|
|
39441
|
+
watcher.state = "pending";
|
|
39442
|
+
schedule(sessionId, watcher);
|
|
39443
|
+
}
|
|
39444
|
+
return;
|
|
39445
|
+
}
|
|
39446
|
+
watchers.delete(sessionId);
|
|
39447
|
+
sessionLog(sessionId, "rust refusal recovery synthetic continue delivered");
|
|
39448
|
+
};
|
|
39449
|
+
return {
|
|
39450
|
+
arm(args) {
|
|
39451
|
+
if (args.compactionOff || args.providerProvenEmergency)
|
|
39452
|
+
return;
|
|
39453
|
+
const refusalKey = `${args.sessionId}:${args.refusedUserMessageId}`;
|
|
39454
|
+
if (latestRefusalBySession.get(args.sessionId) === refusalKey)
|
|
39455
|
+
return;
|
|
39456
|
+
latestRefusalBySession.set(args.sessionId, refusalKey);
|
|
39457
|
+
const current = watchers.get(args.sessionId);
|
|
39458
|
+
if (current)
|
|
39459
|
+
cancel(args.sessionId);
|
|
39460
|
+
const watcher = {
|
|
39461
|
+
refusalKey,
|
|
39462
|
+
refusedUserMessageId: args.refusedUserMessageId,
|
|
39463
|
+
projectRoot: args.projectRoot,
|
|
39464
|
+
armedAtMs: Date.now(),
|
|
39465
|
+
state: "pending"
|
|
39466
|
+
};
|
|
39467
|
+
watchers.set(args.sessionId, watcher);
|
|
39468
|
+
schedule(args.sessionId, watcher);
|
|
39469
|
+
sessionLog(args.sessionId, `rust refusal recovery armed refusal=${refusalKey}`);
|
|
39470
|
+
},
|
|
39471
|
+
cancel,
|
|
39472
|
+
forget(sessionId) {
|
|
39473
|
+
cancel(sessionId);
|
|
39474
|
+
latestRefusalBySession.delete(sessionId);
|
|
39475
|
+
},
|
|
39476
|
+
activeCountForTests() {
|
|
39477
|
+
return watchers.size;
|
|
39478
|
+
}
|
|
39479
|
+
};
|
|
39480
|
+
}
|
|
39481
|
+
|
|
38978
39482
|
// src/hooks/magic-context/text-complete.ts
|
|
38979
39483
|
function createTextCompleteHandler() {
|
|
38980
39484
|
return async (_input, output) => {
|
|
@@ -39375,7 +39879,7 @@ function memoryGuidanceBlock(memoryEnabled) {
|
|
|
39375
39879
|
` : "";
|
|
39376
39880
|
}
|
|
39377
39881
|
var BASE_INTRO = (memoryEnabled) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
|
|
39378
|
-
Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking
|
|
39882
|
+
Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking QUEUES content for release. It stays fully visible to you until it is actually released, which may be the next turn or many turns later. Mark a tool output as soon as you're done with it rather than hoarding the call for the end of the turn. The newest token-mass window stays protected until displaced. Syntax: "3-5", "1,2,9", or "1-5,8,12-15".
|
|
39379
39883
|
Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
|
|
39380
39884
|
${CTX_NOTE_GUIDANCE}
|
|
39381
39885
|
${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
|
|
@@ -40136,6 +40640,8 @@ function createMagicContextHook(deps) {
|
|
|
40136
40640
|
authoritySeed: (args) => transport.authoritySeed(args),
|
|
40137
40641
|
authorityDrain: (args) => transport.authorityDrain(args),
|
|
40138
40642
|
mirrorPull: (args) => transport.mirrorPull(args),
|
|
40643
|
+
mirrorMemory: (args) => transport.mirrorMemory(args),
|
|
40644
|
+
memoryIdentityAck: (args) => transport.memoryIdentityAck(args),
|
|
40139
40645
|
getCompartmentsAfter: async (sessionId, afterSequence) => {
|
|
40140
40646
|
const response = await transport.call({
|
|
40141
40647
|
sessionId,
|
|
@@ -40166,18 +40672,45 @@ function createMagicContextHook(deps) {
|
|
|
40166
40672
|
return client;
|
|
40167
40673
|
})();
|
|
40168
40674
|
const rustModeModuleClient = deps.config.transform_mode === "rust" ? authorityRecoveryModuleClient : undefined;
|
|
40169
|
-
const
|
|
40675
|
+
const rustRefusalRecovery = rustModeModuleClient ? createRustRefusalRecovery({
|
|
40676
|
+
moduleClient: rustModeModuleClient,
|
|
40677
|
+
client: deps.client
|
|
40678
|
+
}) : undefined;
|
|
40679
|
+
const syncModuleDomain = async (domain, pageBudget) => {
|
|
40170
40680
|
if (!rustModeModuleClient?.mirrorPull)
|
|
40171
40681
|
return;
|
|
40172
40682
|
await drainMirrorPages({
|
|
40173
40683
|
db,
|
|
40174
40684
|
module: rustModeModuleClient,
|
|
40175
40685
|
domain,
|
|
40176
|
-
limit: 1000
|
|
40686
|
+
limit: 1000,
|
|
40687
|
+
pageBudget
|
|
40177
40688
|
});
|
|
40178
40689
|
};
|
|
40179
40690
|
const syncModuleNotes = () => syncModuleDomain("notes");
|
|
40180
|
-
const
|
|
40691
|
+
const syncModuleMemoryIdentity = async (moduleProject, moduleRowId, projectRoot) => {
|
|
40692
|
+
if (!rustModeModuleClient?.mirrorMemory)
|
|
40693
|
+
return;
|
|
40694
|
+
const { row } = await rustModeModuleClient.mirrorMemory({
|
|
40695
|
+
module_row_id: moduleRowId,
|
|
40696
|
+
projectRoot
|
|
40697
|
+
});
|
|
40698
|
+
if (!row)
|
|
40699
|
+
return;
|
|
40700
|
+
const identity = applyTargetedMemoryMirrorRow({ db, row });
|
|
40701
|
+
if (!identity || !rustModeModuleClient.memoryIdentityAck)
|
|
40702
|
+
return;
|
|
40703
|
+
await rustModeModuleClient.memoryIdentityAck({
|
|
40704
|
+
project: moduleProject,
|
|
40705
|
+
projectRoot,
|
|
40706
|
+
rows: [
|
|
40707
|
+
{
|
|
40708
|
+
module_row_id: moduleRowId,
|
|
40709
|
+
context_row_id: identity.contextRowId
|
|
40710
|
+
}
|
|
40711
|
+
]
|
|
40712
|
+
});
|
|
40713
|
+
};
|
|
40181
40714
|
const rustToolBackends = deps.config.transform_mode === "rust" && rustModeModuleClient ? {
|
|
40182
40715
|
authorityState: async ({ projectPath, projectRoot, sessionId, domain }) => {
|
|
40183
40716
|
if (!rustModeModuleClient.authorityStatus)
|
|
@@ -40218,7 +40751,7 @@ function createMagicContextHook(deps) {
|
|
|
40218
40751
|
filter,
|
|
40219
40752
|
limit,
|
|
40220
40753
|
offset,
|
|
40221
|
-
|
|
40754
|
+
noteIds
|
|
40222
40755
|
}) => {
|
|
40223
40756
|
const response = await rustModeModuleClient.call({
|
|
40224
40757
|
sessionId,
|
|
@@ -40239,13 +40772,13 @@ function createMagicContextHook(deps) {
|
|
|
40239
40772
|
filter,
|
|
40240
40773
|
limit,
|
|
40241
40774
|
offset,
|
|
40242
|
-
|
|
40775
|
+
note_ids: noteIds
|
|
40243
40776
|
}
|
|
40244
40777
|
}
|
|
40245
40778
|
});
|
|
40246
40779
|
await syncModuleNotes();
|
|
40247
40780
|
if (compileStatus && !moduleNoteResponseIsError(response)) {
|
|
40248
|
-
const moduleRowId = action === "write" ? moduleNoteRowId(response) :
|
|
40781
|
+
const moduleRowId = action === "write" ? moduleNoteRowId(response) : noteIds?.[0] ?? null;
|
|
40249
40782
|
if (moduleRowId === null || !applyMirroredNoteCompileFields({
|
|
40250
40783
|
db,
|
|
40251
40784
|
moduleProject: memoryProject,
|
|
@@ -40274,6 +40807,11 @@ function createMagicContextHook(deps) {
|
|
|
40274
40807
|
reason,
|
|
40275
40808
|
limit
|
|
40276
40809
|
}) => {
|
|
40810
|
+
const hostIds = ids ?? [];
|
|
40811
|
+
const translatedIds = translateHostMemoryIds(db, hostIds);
|
|
40812
|
+
if ("error" in translatedIds)
|
|
40813
|
+
return translatedIds.error;
|
|
40814
|
+
const moduleIds = translatedIds.moduleIds;
|
|
40277
40815
|
const response = await rustModeModuleClient.call({
|
|
40278
40816
|
sessionId,
|
|
40279
40817
|
projectRoot,
|
|
@@ -40285,14 +40823,24 @@ function createMagicContextHook(deps) {
|
|
|
40285
40823
|
action,
|
|
40286
40824
|
content,
|
|
40287
40825
|
category,
|
|
40288
|
-
ids,
|
|
40826
|
+
ids: moduleIds,
|
|
40827
|
+
host_ids: hostIds,
|
|
40828
|
+
memory_id_lane: "host",
|
|
40289
40829
|
reason,
|
|
40290
40830
|
limit,
|
|
40291
40831
|
memory_project: memoryProject
|
|
40292
40832
|
}
|
|
40293
40833
|
}
|
|
40294
40834
|
});
|
|
40295
|
-
|
|
40835
|
+
const operation = moduleMemoryOperation(response);
|
|
40836
|
+
const newModuleRowId = operation?.action === "write" ? operation.module_id : operation?.action === "merge" ? operation.canonical_module_id : undefined;
|
|
40837
|
+
try {
|
|
40838
|
+
if (newModuleRowId !== undefined) {
|
|
40839
|
+
await syncModuleMemoryIdentity(memoryProject, newModuleRowId, projectRoot);
|
|
40840
|
+
}
|
|
40841
|
+
} catch (error) {
|
|
40842
|
+
log("[magic-context] targeted memory mirror sync failed:", error);
|
|
40843
|
+
}
|
|
40296
40844
|
if (!moduleNoteResponseIsError(response) && (action === "write" || action === "update" || action === "merge")) {
|
|
40297
40845
|
(async () => {
|
|
40298
40846
|
await ensureProjectRegisteredFromOpenCodeDirectory(projectRoot, db);
|
|
@@ -40304,7 +40852,13 @@ function createMagicContextHook(deps) {
|
|
|
40304
40852
|
log("[magic-context] mirrored memory embedding failed:", error);
|
|
40305
40853
|
});
|
|
40306
40854
|
}
|
|
40307
|
-
return
|
|
40855
|
+
return translateModuleMemoryMutationReply({
|
|
40856
|
+
db,
|
|
40857
|
+
moduleProject: memoryProject,
|
|
40858
|
+
response,
|
|
40859
|
+
requestedHostIds: hostIds,
|
|
40860
|
+
requestedCategory: category
|
|
40861
|
+
}) ?? response;
|
|
40308
40862
|
},
|
|
40309
40863
|
noteEvaluationAvailable: (evaluationProjectPath) => getModuleNoteEvaluationBridge(evaluationProjectPath) !== undefined,
|
|
40310
40864
|
memorySync: (sessionId) => {
|
|
@@ -40441,7 +40995,8 @@ function createMagicContextHook(deps) {
|
|
|
40441
40995
|
tsAuthorityRecoveryModuleClient: authorityRecoveryModuleClient,
|
|
40442
40996
|
rustMemorySyncRequestedSessions,
|
|
40443
40997
|
onRustModeParked: notifyRustModeParked,
|
|
40444
|
-
onRustModeProjectPrepared: ensureModuleNoteEvaluationBridge
|
|
40998
|
+
onRustModeProjectPrepared: ensureModuleNoteEvaluationBridge,
|
|
40999
|
+
onRustEngineReconnectRefusal: (args) => rustRefusalRecovery?.arm(args)
|
|
40445
41000
|
});
|
|
40446
41001
|
const eventHandler = createEventHandler2({
|
|
40447
41002
|
contextUsageMap,
|
|
@@ -40466,6 +41021,7 @@ function createMagicContextHook(deps) {
|
|
|
40466
41021
|
},
|
|
40467
41022
|
rustSessionCleanup: rustModeModuleClient !== undefined,
|
|
40468
41023
|
onSessionDeleted: async (sessionId) => {
|
|
41024
|
+
rustRefusalRecovery?.forget(sessionId);
|
|
40469
41025
|
dropSlot(sessionId, "session-deleted");
|
|
40470
41026
|
try {
|
|
40471
41027
|
await transform.clearRustSession(sessionId);
|
|
@@ -41108,6 +41664,46 @@ Two recovery modes for finer detail:
|
|
|
41108
41664
|
- ctx_expand(message=138) — returns the FULL untruncated content of the message at that ordinal: every text part, and every tool call's complete input + output, read from stored history. This is the cheap way to get back a tool output you dropped with ctx_reduce — the original is still in storage even though the wire shows [dropped §N§]. If the message was deleted from history (session prune/revert), it says so.`;
|
|
41109
41665
|
var CTX_EXPAND_TOKEN_BUDGET = 15000;
|
|
41110
41666
|
|
|
41667
|
+
// src/tools/ctx-expand/mode.ts
|
|
41668
|
+
function isInt(value) {
|
|
41669
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
41670
|
+
}
|
|
41671
|
+
function minOrdinal(domain) {
|
|
41672
|
+
return domain === "non-negative" ? 0 : 1;
|
|
41673
|
+
}
|
|
41674
|
+
function messageError(domain) {
|
|
41675
|
+
return domain === "non-negative" ? "Error: message must be a non-negative integer." : "Error: message must be a positive integer.";
|
|
41676
|
+
}
|
|
41677
|
+
function rangeError(domain) {
|
|
41678
|
+
return domain === "non-negative" ? "Error: provide either message=<ordinal>, or start and end (non-negative integers, start <= end)." : "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).";
|
|
41679
|
+
}
|
|
41680
|
+
function resolveCtxExpandMode(args, domain) {
|
|
41681
|
+
const min = minOrdinal(domain);
|
|
41682
|
+
const messagePresent = args.message !== undefined && args.message !== null;
|
|
41683
|
+
const message = isInt(args.message) ? args.message : undefined;
|
|
41684
|
+
const start = isInt(args.start) ? args.start : undefined;
|
|
41685
|
+
const end = isInt(args.end) ? args.end : undefined;
|
|
41686
|
+
const messageValid = message !== undefined && message >= min;
|
|
41687
|
+
const rangeValid = start !== undefined && end !== undefined && start >= min && end >= start;
|
|
41688
|
+
const fillerPair = start === 0 && end === 0;
|
|
41689
|
+
const rangeNamed = rangeValid && !fillerPair;
|
|
41690
|
+
if (messageValid && !rangeNamed) {
|
|
41691
|
+
return { kind: "message", message };
|
|
41692
|
+
}
|
|
41693
|
+
if (messagePresent && !messageValid && !rangeNamed) {
|
|
41694
|
+
return { kind: "error", message: messageError(domain) };
|
|
41695
|
+
}
|
|
41696
|
+
if (rangeValid) {
|
|
41697
|
+
return {
|
|
41698
|
+
kind: "range",
|
|
41699
|
+
start,
|
|
41700
|
+
end,
|
|
41701
|
+
verbose: args.verbose === true
|
|
41702
|
+
};
|
|
41703
|
+
}
|
|
41704
|
+
return { kind: "error", message: rangeError(domain) };
|
|
41705
|
+
}
|
|
41706
|
+
|
|
41111
41707
|
// src/tools/ctx-expand/render.ts
|
|
41112
41708
|
function isRecord5(value) {
|
|
41113
41709
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -41301,27 +41897,26 @@ function createCtxExpandTool(deps) {
|
|
|
41301
41897
|
message: "number"
|
|
41302
41898
|
});
|
|
41303
41899
|
const sessionId = toolContext.sessionID;
|
|
41304
|
-
|
|
41305
|
-
|
|
41306
|
-
|
|
41307
|
-
}
|
|
41308
|
-
return renderMessageByOrdinal(sessionId, args.message);
|
|
41900
|
+
const mode = resolveCtxExpandMode(args, "positive");
|
|
41901
|
+
if (mode.kind === "error") {
|
|
41902
|
+
return mode.message;
|
|
41309
41903
|
}
|
|
41310
|
-
if (
|
|
41311
|
-
return
|
|
41904
|
+
if (mode.kind === "message") {
|
|
41905
|
+
return renderMessageByOrdinal(sessionId, mode.message);
|
|
41312
41906
|
}
|
|
41907
|
+
const { start, end, verbose } = mode;
|
|
41313
41908
|
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
41314
|
-
if (lastCompartmentEnd >= 0 &&
|
|
41315
|
-
return `Range ${
|
|
41909
|
+
if (lastCompartmentEnd >= 0 && start > lastCompartmentEnd) {
|
|
41910
|
+
return `Range ${start}-${end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`;
|
|
41316
41911
|
}
|
|
41317
|
-
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(
|
|
41318
|
-
if (
|
|
41319
|
-
const v = renderVerboseRange(sessionId,
|
|
41912
|
+
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(end, lastCompartmentEnd) : end;
|
|
41913
|
+
if (verbose) {
|
|
41914
|
+
const v = renderVerboseRange(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
|
|
41320
41915
|
if (!v.text) {
|
|
41321
|
-
return `No messages found in range ${
|
|
41916
|
+
return `No messages found in range ${start}-${effectiveEnd}. The range may be outside this session's history.`;
|
|
41322
41917
|
}
|
|
41323
41918
|
const out = [
|
|
41324
|
-
`Messages ${
|
|
41919
|
+
`Messages ${start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
|
|
41325
41920
|
"",
|
|
41326
41921
|
v.text
|
|
41327
41922
|
];
|
|
@@ -41331,9 +41926,9 @@ function createCtxExpandTool(deps) {
|
|
|
41331
41926
|
return out.join(`
|
|
41332
41927
|
`);
|
|
41333
41928
|
}
|
|
41334
|
-
const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET,
|
|
41929
|
+
const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, start, effectiveEnd + 1);
|
|
41335
41930
|
if (!chunk.text || chunk.messageCount === 0) {
|
|
41336
|
-
return `No messages found in range ${
|
|
41931
|
+
return `No messages found in range ${start}-${end}. The range may be outside this session's history.`;
|
|
41337
41932
|
}
|
|
41338
41933
|
const lines = [];
|
|
41339
41934
|
lines.push(`Messages ${chunk.startIndex}-${chunk.endIndex} (${chunk.messageCount} messages, ~${chunk.tokenEstimate} tokens):`);
|
|
@@ -41396,7 +41991,7 @@ function isMemoryCategory(value) {
|
|
|
41396
41991
|
return MEMORY_CATEGORIES.has(value);
|
|
41397
41992
|
}
|
|
41398
41993
|
function normalizeLimit2(limit) {
|
|
41399
|
-
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
41994
|
+
if (typeof limit !== "number" || !Number.isFinite(limit) || limit === 0) {
|
|
41400
41995
|
return DEFAULT_SEARCH_LIMIT;
|
|
41401
41996
|
}
|
|
41402
41997
|
return Math.max(1, Math.floor(limit));
|
|
@@ -41680,14 +42275,17 @@ function createCtxMemoryTool(deps) {
|
|
|
41680
42275
|
await deps.ensureProjectRegistered?.(toolContext.directory, deps.db);
|
|
41681
42276
|
const activeCurateCategory = toolContext.agent === DREAMER_AGENT ? getActiveCurateCategory(deps.db, projectPath) : null;
|
|
41682
42277
|
if (activeCurateCategory) {
|
|
42278
|
+
const usesCategory = ["write", "update", "merge", "list"].includes(args.action);
|
|
42279
|
+
const usesIds = ["update", "archive", "merge", "get"].includes(args.action);
|
|
42280
|
+
const usesSuccessor = args.action === "update" || args.action === "archive";
|
|
41683
42281
|
const scopeRefusal = getCurateCategoryScopeRefusal({
|
|
41684
42282
|
scope: activeCurateCategory,
|
|
41685
42283
|
action: args.action,
|
|
41686
|
-
requestedCategory: args.category,
|
|
41687
|
-
ids: [
|
|
42284
|
+
requestedCategory: usesCategory ? args.category : undefined,
|
|
42285
|
+
ids: usesIds ? [
|
|
41688
42286
|
...args.ids ?? [],
|
|
41689
|
-
...Number.isInteger(args.superseded_by) ? [args.superseded_by] : []
|
|
41690
|
-
],
|
|
42287
|
+
...usesSuccessor && Number.isInteger(args.superseded_by) ? [args.superseded_by] : []
|
|
42288
|
+
] : [],
|
|
41691
42289
|
categoryForId: (id) => {
|
|
41692
42290
|
const category = getMemoryById(deps.db, id)?.category;
|
|
41693
42291
|
return category ? curateCategoryForMemoryCategory(category) : null;
|
|
@@ -42088,7 +42686,7 @@ Use a note when something matters LATER but not in the next few steps: "revisit
|
|
|
42088
42686
|
Actions:
|
|
42089
42687
|
- write: save a note (content). Add surface_condition to make it a smart note (below).
|
|
42090
42688
|
- read: list notes, newest first. Default: latest active session notes + ready smart notes; page older ones with limit/offset, or inspect other states with filter.
|
|
42091
|
-
- update: change
|
|
42689
|
+
- update: change one note (note_ids=[N]). dismiss: retire 1–50 notes (note_ids=[...]).
|
|
42092
42690
|
|
|
42093
42691
|
Smart notes: pass surface_condition and the note stays hidden until a background checker confirms the condition — using ONLY externally verifiable signals (GitHub state via gh, files on disk, git history, web pages). It cannot see this conversation, so the condition must be checkable from outside:
|
|
42094
42692
|
✓ "When PR #42 in cortexkit/magic-context is merged"
|
|
@@ -42101,7 +42699,7 @@ Example: ctx_note(action="write", content="Re-run the perf benchmark once the bo
|
|
|
42101
42699
|
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
42102
42700
|
|
|
42103
42701
|
// src/features/magic-context/smart-notes/condition-compiler.ts
|
|
42104
|
-
import { existsSync as
|
|
42702
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
42105
42703
|
import { isAbsolute as isAbsolute4 } from "node:path";
|
|
42106
42704
|
|
|
42107
42705
|
// ../retina-local-fs/src/path-fence.ts
|
|
@@ -42358,7 +42956,7 @@ async function compileSurfaceCondition(surfaceCondition, options) {
|
|
|
42358
42956
|
dataDirectory: options.dataDirectory,
|
|
42359
42957
|
cwd: options.projectPath
|
|
42360
42958
|
});
|
|
42361
|
-
return { path: resolved, exists:
|
|
42959
|
+
return { path: resolved, exists: existsSync13(resolved) };
|
|
42362
42960
|
});
|
|
42363
42961
|
let predicates;
|
|
42364
42962
|
try {
|
|
@@ -42613,7 +43211,7 @@ function formatNoteLine(note) {
|
|
|
42613
43211
|
}
|
|
42614
43212
|
var DISMISS_FOOTER = `
|
|
42615
43213
|
|
|
42616
|
-
To dismiss a stale note: ctx_note(action="dismiss",
|
|
43214
|
+
To dismiss a stale note: ctx_note(action="dismiss", note_ids=[N])`;
|
|
42617
43215
|
var DEFAULT_READ_LIMIT = 25;
|
|
42618
43216
|
function paginateNewestFirst(notes, limit, offset) {
|
|
42619
43217
|
const total = notes.length;
|
|
@@ -42728,8 +43326,7 @@ var ctxNoteArgsShape = {
|
|
|
42728
43326
|
filter: tool3.schema.enum(["all", "active", "pending", "ready", "dismissed"]).optional().describe("Optional read filter. Defaults to active session notes + ready smart notes. Use 'all' to inspect every status or 'pending' to inspect unsurfaced smart notes."),
|
|
42729
43327
|
limit: tool3.schema.number().optional().describe("Max notes per section for read, newest first (default: 25)"),
|
|
42730
43328
|
offset: tool3.schema.number().optional().describe("Skip this many newest notes for read — page older ones (default: 0)"),
|
|
42731
|
-
|
|
42732
|
-
note_ids: tool3.schema.array(tool3.schema.number().int().min(1)).min(1).max(50).optional().describe("One to fifty note ids for 'dismiss' only; do not combine with note_id.")
|
|
43329
|
+
note_ids: tool3.schema.array(tool3.schema.number().int().min(1)).min(1).max(50).optional().describe("Note ids: exactly one for 'update', one to fifty for 'dismiss'. Ignored by 'write' and 'read'.")
|
|
42733
43330
|
};
|
|
42734
43331
|
var ctxNoteArgsSchema = tool3.schema.object(ctxNoteArgsShape).passthrough();
|
|
42735
43332
|
function formatDismissResults(results) {
|
|
@@ -42738,9 +43335,10 @@ function formatDismissResults(results) {
|
|
|
42738
43335
|
${results.map((result) => `- Note #${result.noteId}: ${result.outcome}`).join(`
|
|
42739
43336
|
`)}`;
|
|
42740
43337
|
}
|
|
42741
|
-
function
|
|
42742
|
-
|
|
42743
|
-
|
|
43338
|
+
function parseNoteIds(action, value) {
|
|
43339
|
+
const max = action === "update" ? 1 : 50;
|
|
43340
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > max || value.some((id) => typeof id !== "number" || !Number.isInteger(id) || id <= 0)) {
|
|
43341
|
+
return action === "update" ? "Error: 'note_ids' must contain exactly one positive integer id when action is 'update'." : "Error: 'note_ids' must contain 1 to 50 positive integer ids when action is 'dismiss'.";
|
|
42744
43342
|
}
|
|
42745
43343
|
return value;
|
|
42746
43344
|
}
|
|
@@ -42761,22 +43359,13 @@ function createCtxNoteTool(deps) {
|
|
|
42761
43359
|
},
|
|
42762
43360
|
limit: "number",
|
|
42763
43361
|
offset: "number",
|
|
42764
|
-
note_id: "number",
|
|
42765
43362
|
note_ids: { type: "array", items: "number", maxItems: 50 }
|
|
42766
43363
|
});
|
|
42767
43364
|
const sessionId = toolContext.sessionID;
|
|
42768
43365
|
const action = args.action ?? (args.content?.trim() ? "write" : "read");
|
|
42769
|
-
const
|
|
42770
|
-
|
|
42771
|
-
|
|
42772
|
-
return "Error: 'note_id' and 'note_ids' cannot be used together; provide one or the other.";
|
|
42773
|
-
}
|
|
42774
|
-
if (hasNoteIds && action !== "dismiss") {
|
|
42775
|
-
return "Error: 'note_ids' is only valid when action is 'dismiss'.";
|
|
42776
|
-
}
|
|
42777
|
-
const dismissNoteIds = action === "dismiss" && hasNoteIds ? parseDismissNoteIds(args.note_ids) : undefined;
|
|
42778
|
-
if (typeof dismissNoteIds === "string")
|
|
42779
|
-
return dismissNoteIds;
|
|
43366
|
+
const noteIds = action === "dismiss" || action === "update" ? parseNoteIds(action, args.note_ids) : undefined;
|
|
43367
|
+
if (typeof noteIds === "string")
|
|
43368
|
+
return noteIds;
|
|
42780
43369
|
const wakePlaneActive = action === "write" && Boolean(args.surface_condition?.trim()) && await wakePlaneStatus() === "present";
|
|
42781
43370
|
const surfaceCondition = wakePlaneActive ? undefined : args.surface_condition?.trim();
|
|
42782
43371
|
const projectIdentity = deps.resolveProjectPath?.(toolContext.directory);
|
|
@@ -42825,8 +43414,7 @@ function createCtxNoteTool(deps) {
|
|
|
42825
43414
|
filter: args.filter,
|
|
42826
43415
|
limit: args.limit,
|
|
42827
43416
|
offset: args.offset,
|
|
42828
|
-
|
|
42829
|
-
noteIds: dismissNoteIds
|
|
43417
|
+
noteIds: Array.isArray(noteIds) ? noteIds : undefined
|
|
42830
43418
|
};
|
|
42831
43419
|
try {
|
|
42832
43420
|
const text = moduleNoteText(await rustNote(request), args, action);
|
|
@@ -42895,33 +43483,24 @@ wake plane active — create a scheduled wake instead; stored as a plain note.`;
|
|
|
42895
43483
|
return `Saved session note #${note.id}.`;
|
|
42896
43484
|
}
|
|
42897
43485
|
if (action === "dismiss") {
|
|
42898
|
-
if (dismissNoteIds) {
|
|
42899
|
-
if (!projectIdentity) {
|
|
42900
|
-
return "Error: Could not resolve project identity for note dismiss.";
|
|
42901
|
-
}
|
|
42902
|
-
return formatDismissResults(dismissNotes(deps.db, dismissNoteIds, {
|
|
42903
|
-
projectPath: projectIdentity,
|
|
42904
|
-
sessionId
|
|
42905
|
-
}));
|
|
42906
|
-
}
|
|
42907
|
-
const noteId = args.note_id;
|
|
42908
|
-
if (typeof noteId !== "number") {
|
|
42909
|
-
return "Error: 'note_id' is required when action is 'dismiss'.";
|
|
42910
|
-
}
|
|
42911
43486
|
if (!projectIdentity) {
|
|
42912
43487
|
return "Error: Could not resolve project identity for note dismiss.";
|
|
42913
43488
|
}
|
|
42914
|
-
const
|
|
43489
|
+
const ids = noteIds;
|
|
43490
|
+
if (ids.length === 1) {
|
|
43491
|
+
const dismissed = dismissNote(deps.db, ids[0], {
|
|
43492
|
+
projectPath: projectIdentity,
|
|
43493
|
+
sessionId
|
|
43494
|
+
});
|
|
43495
|
+
return dismissed ? `Note #${ids[0]} dismissed.` : `Error: Note #${ids[0]} not found in your session/project or already dismissed.`;
|
|
43496
|
+
}
|
|
43497
|
+
return formatDismissResults(dismissNotes(deps.db, ids, {
|
|
42915
43498
|
projectPath: projectIdentity,
|
|
42916
43499
|
sessionId
|
|
42917
|
-
});
|
|
42918
|
-
return dismissed ? `Note #${noteId} dismissed.` : `Error: Note #${noteId} not found in your session/project or already dismissed.`;
|
|
43500
|
+
}));
|
|
42919
43501
|
}
|
|
42920
43502
|
if (action === "update") {
|
|
42921
|
-
const noteId =
|
|
42922
|
-
if (typeof noteId !== "number") {
|
|
42923
|
-
return "Error: 'note_id' is required when action is 'update'.";
|
|
42924
|
-
}
|
|
43503
|
+
const noteId = noteIds[0];
|
|
42925
43504
|
const updates = {};
|
|
42926
43505
|
if (args.content?.trim())
|
|
42927
43506
|
updates.content = args.content.trim();
|
|
@@ -42993,7 +43572,7 @@ function createCtxNoteTools(deps) {
|
|
|
42993
43572
|
var CTX_REDUCE_DESCRIPTION = `Mark spent tagged content as discardable to reclaim context space. This is NOT an immediate delete. Use §N§ identifiers visible in the conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
|
|
42994
43573
|
|
|
42995
43574
|
How it works:
|
|
42996
|
-
- Marking QUEUES content for release. It stays fully visible to you until
|
|
43575
|
+
- Marking QUEUES content for release. It stays fully visible to you until it is actually released, which may be the next turn or many turns later. Mark spent outputs as soon as you finish with them; don't hoard the call for the end of the turn.
|
|
42997
43576
|
- The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
|
|
42998
43577
|
- When content is finally released it becomes a short placeholder, and re-running the tool is the only way to get it back. So mark only what you are genuinely DONE with — the test is "have I extracted what I need from this?", not "is it safe / do I have time before it drops?".
|
|
42999
43578
|
|
|
@@ -43238,7 +43817,7 @@ var VALID_SOURCES = new Set([
|
|
|
43238
43817
|
"note"
|
|
43239
43818
|
]);
|
|
43240
43819
|
function normalizeLimit3(limit) {
|
|
43241
|
-
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
43820
|
+
if (typeof limit !== "number" || !Number.isFinite(limit) || limit === 0) {
|
|
43242
43821
|
return DEFAULT_CTX_SEARCH_LIMIT;
|
|
43243
43822
|
}
|
|
43244
43823
|
return Math.max(1, Math.floor(limit));
|
|
@@ -43257,7 +43836,7 @@ function normalizeSources(sources) {
|
|
|
43257
43836
|
}
|
|
43258
43837
|
}
|
|
43259
43838
|
}
|
|
43260
|
-
return result;
|
|
43839
|
+
return sources.length === 0 ? undefined : result;
|
|
43261
43840
|
}
|
|
43262
43841
|
var ctxSearchArgsShape = {
|
|
43263
43842
|
query: tool5.schema.string().optional().describe("Search query. Matches against memory content, Primers, git commit messages, and raw user/assistant message text."),
|
|
@@ -43484,7 +44063,7 @@ import {
|
|
|
43484
44063
|
chmodSync as chmodSync2,
|
|
43485
44064
|
mkdirSync as mkdirSync4,
|
|
43486
44065
|
readdirSync,
|
|
43487
|
-
readFileSync as
|
|
44066
|
+
readFileSync as readFileSync7,
|
|
43488
44067
|
renameSync as renameSync5,
|
|
43489
44068
|
rmSync as rmSync4,
|
|
43490
44069
|
unlinkSync as unlinkSync2,
|
|
@@ -43621,7 +44200,7 @@ class MagicContextRpcServer {
|
|
|
43621
44200
|
for (const entry of readdirSync(this.portDir)) {
|
|
43622
44201
|
if (!entry.startsWith("port-") || !entry.endsWith(".json"))
|
|
43623
44202
|
continue;
|
|
43624
|
-
const record = parseRpcPortFile(
|
|
44203
|
+
const record = parseRpcPortFile(readFileSync7(`${this.portDir}/${entry}`, "utf-8"));
|
|
43625
44204
|
if (!record || record.pid === process.pid || !isPidAlive(record.pid))
|
|
43626
44205
|
continue;
|
|
43627
44206
|
log(`[rpc] another Magic Context RPC server is active for this project (pid ${record.pid}, port ${record.port}); starting separate instance on a new port`);
|
|
@@ -44015,7 +44594,240 @@ ${data.output?.output ?? ""}`
|
|
|
44015
44594
|
return [...content.length ? [make("assistant", content)] : [], ...results];
|
|
44016
44595
|
}
|
|
44017
44596
|
|
|
44597
|
+
// src/v2/hooks/hidden-child.ts
|
|
44598
|
+
var HIDDEN_HISTORIAN_AGENT = "historian";
|
|
44599
|
+
var HIDDEN_DREAMER_AGENT = "dreamer-classifier";
|
|
44600
|
+
async function registerHiddenChildAgents(agent) {
|
|
44601
|
+
await agent.transform((editor) => {
|
|
44602
|
+
for (const id of [HIDDEN_HISTORIAN_AGENT, HIDDEN_DREAMER_AGENT]) {
|
|
44603
|
+
editor.update(id, (config) => {
|
|
44604
|
+
config.system = "Magic Context hidden completion carrier.";
|
|
44605
|
+
config.description = "Internal Magic Context hidden completion carrier.";
|
|
44606
|
+
config.mode = "primary";
|
|
44607
|
+
config.hidden = true;
|
|
44608
|
+
config.request.settings = {};
|
|
44609
|
+
config.request.headers = {};
|
|
44610
|
+
config.request.body = {};
|
|
44611
|
+
config.permissions = [{ action: "*", resource: "*", effect: "deny" }];
|
|
44612
|
+
});
|
|
44613
|
+
}
|
|
44614
|
+
});
|
|
44615
|
+
}
|
|
44616
|
+
function newestUserText(draft) {
|
|
44617
|
+
const message = draft.messages.at(-1);
|
|
44618
|
+
if (!message || message.role !== undefined && message.role !== "user")
|
|
44619
|
+
return;
|
|
44620
|
+
const content = message.content;
|
|
44621
|
+
if (typeof content === "string" && content.length > 0)
|
|
44622
|
+
return content;
|
|
44623
|
+
const parts = Array.isArray(content) ? content : Array.isArray(message.parts) ? message.parts : [];
|
|
44624
|
+
for (const part of parts) {
|
|
44625
|
+
if (!part || typeof part !== "object")
|
|
44626
|
+
continue;
|
|
44627
|
+
const record = part;
|
|
44628
|
+
if (typeof record.text !== "string" || record.text.length === 0)
|
|
44629
|
+
continue;
|
|
44630
|
+
if (record.type === undefined || record.type === "text" || record.type === "input_text") {
|
|
44631
|
+
return record.text;
|
|
44632
|
+
}
|
|
44633
|
+
}
|
|
44634
|
+
return;
|
|
44635
|
+
}
|
|
44636
|
+
function calibratedParts(attempt) {
|
|
44637
|
+
const parts = attempt.request.body.parts;
|
|
44638
|
+
if (!Array.isArray(parts) || parts.length === 0 || parts.some((part) => !part || typeof part !== "object" || part.type !== "text" || typeof part.text !== "string")) {
|
|
44639
|
+
throw new HiddenCompletionRefusal("hidden_prompt_unrecognized", "Hidden completion accepts text-only calibrated prompts", true);
|
|
44640
|
+
}
|
|
44641
|
+
return parts.map((part) => ({ type: "text", text: part.text }));
|
|
44642
|
+
}
|
|
44643
|
+
|
|
44644
|
+
class HiddenChildHook {
|
|
44645
|
+
childIDs = new Set;
|
|
44646
|
+
attempts = new Map;
|
|
44647
|
+
registerChild(sessionID) {
|
|
44648
|
+
this.childIDs.add(sessionID);
|
|
44649
|
+
}
|
|
44650
|
+
registerAttempt(marker, attempt) {
|
|
44651
|
+
this.registerChild(attempt.childSessionId);
|
|
44652
|
+
this.attempts.set(marker, attempt);
|
|
44653
|
+
}
|
|
44654
|
+
releaseAttempt(marker) {
|
|
44655
|
+
this.attempts.delete(marker);
|
|
44656
|
+
}
|
|
44657
|
+
owns(sessionID) {
|
|
44658
|
+
return this.childIDs.has(sessionID);
|
|
44659
|
+
}
|
|
44660
|
+
apply(draft) {
|
|
44661
|
+
if (!this.owns(draft.sessionID))
|
|
44662
|
+
return false;
|
|
44663
|
+
const raw = newestUserText(draft);
|
|
44664
|
+
const stripped = raw === undefined ? undefined : stripWellFormedLeadingTagPrefix(raw);
|
|
44665
|
+
const attempt = (raw !== undefined ? this.attempts.get(raw) : undefined) ?? (stripped && stripped !== raw ? this.attempts.get(stripped) : undefined);
|
|
44666
|
+
if (!attempt || attempt.childSessionId !== draft.sessionID) {
|
|
44667
|
+
throw new HiddenCompletionRefusal("hidden_prompt_unrecognized", "Refusing an unregistered prompt on a Magic Context hidden-run session", true);
|
|
44668
|
+
}
|
|
44669
|
+
if (attempt.request.signal?.aborted) {
|
|
44670
|
+
throw new Error("Hidden completion prompt aborted");
|
|
44671
|
+
}
|
|
44672
|
+
const system = typeof attempt.request.body.system === "string" ? attempt.request.body.system : attempt.identity.system;
|
|
44673
|
+
const temperature = attempt.request.body.temperature;
|
|
44674
|
+
draft.system = [{ type: "text", text: system }];
|
|
44675
|
+
draft.messages = [{ role: "user", content: calibratedParts(attempt) }];
|
|
44676
|
+
draft.options = {
|
|
44677
|
+
maxOutputTokens: 32768,
|
|
44678
|
+
maxTokens: 32768,
|
|
44679
|
+
...typeof temperature === "number" && Number.isFinite(temperature) ? { temperature } : {}
|
|
44680
|
+
};
|
|
44681
|
+
draft.tools = {};
|
|
44682
|
+
attempt.shaped = true;
|
|
44683
|
+
return true;
|
|
44684
|
+
}
|
|
44685
|
+
}
|
|
44686
|
+
|
|
44018
44687
|
// src/v2/hidden-completion.ts
|
|
44688
|
+
var META_PREFIX = "opencode2_hidden_children:";
|
|
44689
|
+
var POLL_INTERVAL_MS = 200;
|
|
44690
|
+
function hiddenChildrenMetaKey(projectIdentity) {
|
|
44691
|
+
return `${META_PREFIX}${projectIdentity}`;
|
|
44692
|
+
}
|
|
44693
|
+
function emptyMeta() {
|
|
44694
|
+
return { version: 1, active: {}, retired_children: [] };
|
|
44695
|
+
}
|
|
44696
|
+
function isModel(value) {
|
|
44697
|
+
if (!value || typeof value !== "object")
|
|
44698
|
+
return false;
|
|
44699
|
+
const model = value;
|
|
44700
|
+
return typeof model.providerID === "string" && typeof model.modelID === "string";
|
|
44701
|
+
}
|
|
44702
|
+
function isRole(value) {
|
|
44703
|
+
return value === "historian" || value === "dreamer";
|
|
44704
|
+
}
|
|
44705
|
+
function isPersistedChild(value) {
|
|
44706
|
+
if (!value || typeof value !== "object")
|
|
44707
|
+
return false;
|
|
44708
|
+
const child = value;
|
|
44709
|
+
return typeof child.id === "string" && isRole(child.role) && typeof child.generation === "string" && typeof child.title === "string" && isModel(child.model) && typeof child.created_at === "number" && typeof child.title_reasserted === "boolean";
|
|
44710
|
+
}
|
|
44711
|
+
function parseMeta(value) {
|
|
44712
|
+
if (value === null)
|
|
44713
|
+
return emptyMeta();
|
|
44714
|
+
let parsed;
|
|
44715
|
+
try {
|
|
44716
|
+
parsed = JSON.parse(value);
|
|
44717
|
+
} catch (error) {
|
|
44718
|
+
throw new Error("Invalid OpenCode 2 hidden-child metadata JSON", { cause: error });
|
|
44719
|
+
}
|
|
44720
|
+
if (!parsed || typeof parsed !== "object") {
|
|
44721
|
+
throw new Error("Invalid OpenCode 2 hidden-child metadata");
|
|
44722
|
+
}
|
|
44723
|
+
const candidate = parsed;
|
|
44724
|
+
if (candidate.version !== 1 || !candidate.active || !candidate.retired_children) {
|
|
44725
|
+
throw new Error("Unsupported OpenCode 2 hidden-child metadata version");
|
|
44726
|
+
}
|
|
44727
|
+
const active = {};
|
|
44728
|
+
for (const role of ["historian", "dreamer"]) {
|
|
44729
|
+
const child = candidate.active[role];
|
|
44730
|
+
if (child !== undefined) {
|
|
44731
|
+
if (!isPersistedChild(child) || child.role !== role) {
|
|
44732
|
+
throw new Error(`Invalid OpenCode 2 ${role} child metadata`);
|
|
44733
|
+
}
|
|
44734
|
+
active[role] = child;
|
|
44735
|
+
}
|
|
44736
|
+
}
|
|
44737
|
+
const retired = candidate.retired_children;
|
|
44738
|
+
if (!Array.isArray(retired) || retired.some((child) => !isPersistedChild(child) || typeof child.retired_at !== "number" || typeof child.reason !== "string")) {
|
|
44739
|
+
throw new Error("Invalid OpenCode 2 retired-child metadata");
|
|
44740
|
+
}
|
|
44741
|
+
return { version: 1, active, retired_children: retired };
|
|
44742
|
+
}
|
|
44743
|
+
|
|
44744
|
+
class HiddenChildStateStore {
|
|
44745
|
+
db;
|
|
44746
|
+
key;
|
|
44747
|
+
constructor(db, projectIdentity) {
|
|
44748
|
+
this.db = db;
|
|
44749
|
+
this.key = hiddenChildrenMetaKey(projectIdentity);
|
|
44750
|
+
}
|
|
44751
|
+
read() {
|
|
44752
|
+
const row = this.db.prepare("SELECT value FROM schema_migrations_meta WHERE key = ?").get(this.key);
|
|
44753
|
+
return parseMeta(row?.value ?? null);
|
|
44754
|
+
}
|
|
44755
|
+
mutate(change) {
|
|
44756
|
+
return this.db.transaction(() => {
|
|
44757
|
+
const state = this.read();
|
|
44758
|
+
const result = change(state);
|
|
44759
|
+
this.db.prepare(`INSERT INTO schema_migrations_meta (key, value) VALUES (?, ?)
|
|
44760
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(this.key, JSON.stringify(state));
|
|
44761
|
+
return result;
|
|
44762
|
+
})();
|
|
44763
|
+
}
|
|
44764
|
+
put(child) {
|
|
44765
|
+
this.mutate((state) => {
|
|
44766
|
+
state.active[child.role] = child;
|
|
44767
|
+
});
|
|
44768
|
+
}
|
|
44769
|
+
updateModel(child, model) {
|
|
44770
|
+
return this.mutate((state) => {
|
|
44771
|
+
const active = state.active[child.role];
|
|
44772
|
+
if (!active || active.id !== child.id)
|
|
44773
|
+
return { ...child, model };
|
|
44774
|
+
active.model = model;
|
|
44775
|
+
return { ...active };
|
|
44776
|
+
});
|
|
44777
|
+
}
|
|
44778
|
+
markTitleReasserted(child) {
|
|
44779
|
+
return this.mutate((state) => {
|
|
44780
|
+
const active = state.active[child.role];
|
|
44781
|
+
if (!active || active.id !== child.id)
|
|
44782
|
+
return { ...child, title_reasserted: true };
|
|
44783
|
+
active.title_reasserted = true;
|
|
44784
|
+
return { ...active };
|
|
44785
|
+
});
|
|
44786
|
+
}
|
|
44787
|
+
retire(child, reason) {
|
|
44788
|
+
this.mutate((state) => {
|
|
44789
|
+
const active = state.active[child.role];
|
|
44790
|
+
if (!active || active.id !== child.id)
|
|
44791
|
+
return;
|
|
44792
|
+
state.retired_children.push({
|
|
44793
|
+
...active,
|
|
44794
|
+
retired_at: Date.now(),
|
|
44795
|
+
reason
|
|
44796
|
+
});
|
|
44797
|
+
delete state.active[child.role];
|
|
44798
|
+
});
|
|
44799
|
+
}
|
|
44800
|
+
}
|
|
44801
|
+
function modelKey(model) {
|
|
44802
|
+
return `${model.providerID}/${model.modelID}`;
|
|
44803
|
+
}
|
|
44804
|
+
function sameModel(left, right) {
|
|
44805
|
+
return modelKey(left) === modelKey(right) && left.variant === right.variant;
|
|
44806
|
+
}
|
|
44807
|
+
function configuredHead(identity) {
|
|
44808
|
+
const candidates = [identity.model, ...identity.configuredModels ?? []];
|
|
44809
|
+
for (const candidate of candidates) {
|
|
44810
|
+
const entry = toModelEntry(candidate);
|
|
44811
|
+
const parsed = entry ? parseProviderModel(entry.model) : null;
|
|
44812
|
+
if (parsed)
|
|
44813
|
+
return { ...parsed, ...entry?.qualifier ? { variant: entry.qualifier } : {} };
|
|
44814
|
+
}
|
|
44815
|
+
return;
|
|
44816
|
+
}
|
|
44817
|
+
function roleFor(identity) {
|
|
44818
|
+
return identity.kind === "dreamer-task" ? "dreamer" : "historian";
|
|
44819
|
+
}
|
|
44820
|
+
function roleTitle(role) {
|
|
44821
|
+
return role === "historian" ? "Magic Context historian" : "Magic Context dreamer";
|
|
44822
|
+
}
|
|
44823
|
+
function roleAgent(role) {
|
|
44824
|
+
return role === "historian" ? HIDDEN_HISTORIAN_AGENT : HIDDEN_DREAMER_AGENT;
|
|
44825
|
+
}
|
|
44826
|
+
function promptText(request) {
|
|
44827
|
+
const parts = request.body.parts;
|
|
44828
|
+
return Array.isArray(parts) ? parts.flatMap((part) => part && typeof part === "object" && typeof part.text === "string" ? [part.text] : []).join(`
|
|
44829
|
+
`) : "";
|
|
44830
|
+
}
|
|
44019
44831
|
function meter(system, prompt, text) {
|
|
44020
44832
|
return {
|
|
44021
44833
|
input: estimateTokens(system) + estimateTokens(prompt),
|
|
@@ -44024,115 +44836,294 @@ function meter(system, prompt, text) {
|
|
|
44024
44836
|
cacheWrite: 0
|
|
44025
44837
|
};
|
|
44026
44838
|
}
|
|
44027
|
-
|
|
44839
|
+
function successfulReusableAssistant(row) {
|
|
44840
|
+
return row !== undefined && typeof row.data.finish === "string" && row.data.error === undefined && row.data.tokens !== undefined;
|
|
44841
|
+
}
|
|
44842
|
+
function assistantText(row) {
|
|
44843
|
+
const text = (row.data.content ?? []).flatMap((part) => part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("");
|
|
44844
|
+
return text.length > 0 ? text : null;
|
|
44845
|
+
}
|
|
44846
|
+
function errorText(value) {
|
|
44847
|
+
if (value instanceof Error)
|
|
44848
|
+
return value.message;
|
|
44849
|
+
if (typeof value === "string")
|
|
44850
|
+
return value;
|
|
44851
|
+
try {
|
|
44852
|
+
return JSON.stringify(value);
|
|
44853
|
+
} catch {
|
|
44854
|
+
return String(value);
|
|
44855
|
+
}
|
|
44856
|
+
}
|
|
44857
|
+
function requestModel(request, current) {
|
|
44858
|
+
const requested = request.body.model;
|
|
44859
|
+
if (requested && typeof requested.providerID === "string" && typeof requested.modelID === "string") {
|
|
44860
|
+
return {
|
|
44861
|
+
providerID: requested.providerID,
|
|
44862
|
+
modelID: requested.modelID,
|
|
44863
|
+
...typeof request.body.variant === "string" ? { variant: request.body.variant } : {}
|
|
44864
|
+
};
|
|
44865
|
+
}
|
|
44866
|
+
return current;
|
|
44867
|
+
}
|
|
44868
|
+
function withReader(openReader, read) {
|
|
44869
|
+
const reader = openReader();
|
|
44870
|
+
try {
|
|
44871
|
+
return read(reader);
|
|
44872
|
+
} finally {
|
|
44873
|
+
reader.close?.();
|
|
44874
|
+
}
|
|
44875
|
+
}
|
|
44876
|
+
async function sleepUntilPoll(signal, deadline) {
|
|
44877
|
+
if (signal?.aborted)
|
|
44878
|
+
throw new Error("Hidden completion prompt aborted");
|
|
44879
|
+
const delay = Math.min(POLL_INTERVAL_MS, Math.max(0, deadline - Date.now()));
|
|
44880
|
+
if (delay <= 0)
|
|
44881
|
+
return;
|
|
44882
|
+
await new Promise((resolve, reject) => {
|
|
44883
|
+
const timer = setTimeout(resolve, delay);
|
|
44884
|
+
const abort = () => {
|
|
44885
|
+
clearTimeout(timer);
|
|
44886
|
+
reject(new Error("Hidden completion prompt aborted"));
|
|
44887
|
+
};
|
|
44888
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
44889
|
+
if (signal?.aborted)
|
|
44890
|
+
abort();
|
|
44891
|
+
else
|
|
44892
|
+
setTimeout(() => {
|
|
44893
|
+
signal?.removeEventListener("abort", abort);
|
|
44894
|
+
}, delay);
|
|
44895
|
+
});
|
|
44896
|
+
}
|
|
44897
|
+
async function awaitAssistantRow(openReader, childID, afterSeq, deadline, signal) {
|
|
44898
|
+
for (;; ) {
|
|
44899
|
+
const row = withReader(openReader, (reader) => reader.latestAssistant(childID));
|
|
44900
|
+
if (row && row.seq > afterSeq) {
|
|
44901
|
+
if (row.data.error !== undefined) {
|
|
44902
|
+
throw new Error(`Hidden completion provider error: ${errorText(row.data.error)}`);
|
|
44903
|
+
}
|
|
44904
|
+
if (typeof row.data.finish === "string")
|
|
44905
|
+
return row;
|
|
44906
|
+
}
|
|
44907
|
+
if (Date.now() >= deadline) {
|
|
44908
|
+
throw new Error("Hidden completion timed out waiting for a persisted assistant row");
|
|
44909
|
+
}
|
|
44910
|
+
await sleepUntilPoll(signal, deadline);
|
|
44911
|
+
}
|
|
44912
|
+
}
|
|
44913
|
+
async function createV2HiddenCompletionExecutor(host, options) {
|
|
44028
44914
|
const runs = new WeakMap;
|
|
44029
|
-
const
|
|
44030
|
-
const
|
|
44031
|
-
const
|
|
44032
|
-
|
|
44033
|
-
|
|
44034
|
-
|
|
44035
|
-
|
|
44036
|
-
}
|
|
44037
|
-
const
|
|
44038
|
-
|
|
44039
|
-
|
|
44040
|
-
|
|
44041
|
-
|
|
44042
|
-
|
|
44043
|
-
|
|
44044
|
-
|
|
44045
|
-
|
|
44046
|
-
|
|
44047
|
-
|
|
44048
|
-
|
|
44049
|
-
|
|
44050
|
-
|
|
44051
|
-
|
|
44052
|
-
|
|
44053
|
-
|
|
44054
|
-
|
|
44055
|
-
|
|
44056
|
-
if (
|
|
44057
|
-
|
|
44915
|
+
const store = new HiddenChildStateStore(options.db, options.projectIdentity);
|
|
44916
|
+
const generation = options.generation ?? "opencode2";
|
|
44917
|
+
const roleTails = new Map;
|
|
44918
|
+
const persisted = store.read();
|
|
44919
|
+
for (const child of [...Object.values(persisted.active), ...persisted.retired_children]) {
|
|
44920
|
+
if (child)
|
|
44921
|
+
options.hook.registerChild(child.id);
|
|
44922
|
+
}
|
|
44923
|
+
const acquireRole = async (role) => {
|
|
44924
|
+
const previous = roleTails.get(role) ?? Promise.resolve();
|
|
44925
|
+
let release;
|
|
44926
|
+
const gate = new Promise((resolve) => {
|
|
44927
|
+
release = resolve;
|
|
44928
|
+
});
|
|
44929
|
+
const tail = previous.then(() => gate);
|
|
44930
|
+
roleTails.set(role, tail);
|
|
44931
|
+
await previous;
|
|
44932
|
+
return () => {
|
|
44933
|
+
release();
|
|
44934
|
+
if (roleTails.get(role) === tail)
|
|
44935
|
+
roleTails.delete(role);
|
|
44936
|
+
};
|
|
44937
|
+
};
|
|
44938
|
+
const resolveHead = async (identity) => {
|
|
44939
|
+
const configured = configuredHead(identity);
|
|
44940
|
+
if (configured)
|
|
44941
|
+
return configured;
|
|
44942
|
+
if (!identity.parentSessionId) {
|
|
44943
|
+
throw new HiddenCompletionRefusal("hidden_model_unsupported", "Hidden completion requires a configured model or an existing parent session model", true);
|
|
44058
44944
|
}
|
|
44059
|
-
const
|
|
44060
|
-
if (!
|
|
44061
|
-
throw new HiddenCompletionRefusal("
|
|
44945
|
+
const parent = await host.get({ sessionID: identity.parentSessionId });
|
|
44946
|
+
if (!parent.model) {
|
|
44947
|
+
throw new HiddenCompletionRefusal("hidden_model_unsupported", "Hidden completion could not resolve the parent session model", true);
|
|
44062
44948
|
}
|
|
44063
|
-
|
|
44064
|
-
|
|
44065
|
-
|
|
44066
|
-
|
|
44949
|
+
return {
|
|
44950
|
+
providerID: parent.model.providerID,
|
|
44951
|
+
modelID: parent.model.id,
|
|
44952
|
+
...parent.model.variant ? { variant: parent.model.variant } : {}
|
|
44953
|
+
};
|
|
44954
|
+
};
|
|
44955
|
+
const switchChildModel = async (run, requested) => {
|
|
44956
|
+
if (sameModel(run.child.model, requested))
|
|
44957
|
+
return;
|
|
44958
|
+
await host.switchModel({
|
|
44959
|
+
sessionID: run.child.id,
|
|
44960
|
+
model: {
|
|
44961
|
+
providerID: requested.providerID,
|
|
44962
|
+
id: requested.modelID,
|
|
44963
|
+
...requested.variant ? { variant: requested.variant } : {}
|
|
44067
44964
|
}
|
|
44068
|
-
|
|
44069
|
-
|
|
44070
|
-
|
|
44071
|
-
|
|
44072
|
-
|
|
44073
|
-
|
|
44074
|
-
|
|
44075
|
-
|
|
44076
|
-
}
|
|
44965
|
+
});
|
|
44966
|
+
run.child = store.updateModel(run.child, requested);
|
|
44967
|
+
};
|
|
44968
|
+
const retire = (run, reason) => {
|
|
44969
|
+
if (run.retired)
|
|
44970
|
+
return;
|
|
44971
|
+
store.retire(run.child, reason);
|
|
44972
|
+
run.retired = true;
|
|
44973
|
+
};
|
|
44974
|
+
const interruptAndRetire = async (run, reason) => {
|
|
44975
|
+
try {
|
|
44976
|
+
await host.interrupt({ sessionID: run.child.id });
|
|
44977
|
+
} finally {
|
|
44978
|
+
retire(run, reason);
|
|
44979
|
+
}
|
|
44980
|
+
};
|
|
44077
44981
|
return {
|
|
44078
44982
|
capabilities: { tools: false, harness: "opencode2" },
|
|
44079
44983
|
async open(identity) {
|
|
44080
|
-
const
|
|
44081
|
-
const
|
|
44082
|
-
|
|
44083
|
-
|
|
44084
|
-
|
|
44085
|
-
|
|
44086
|
-
|
|
44087
|
-
|
|
44984
|
+
const role = roleFor(identity);
|
|
44985
|
+
const releaseRole = await acquireRole(role);
|
|
44986
|
+
let openedChild;
|
|
44987
|
+
try {
|
|
44988
|
+
await options.ensureAgent?.();
|
|
44989
|
+
const head = await resolveHead(identity);
|
|
44990
|
+
let active = store.read().active[role];
|
|
44991
|
+
if (active && active.generation !== generation) {
|
|
44992
|
+
store.retire(active, "host-generation-changed");
|
|
44993
|
+
active = undefined;
|
|
44994
|
+
}
|
|
44995
|
+
if (active) {
|
|
44996
|
+
const activeID = active.id;
|
|
44997
|
+
const latest = withReader(options.openReader, (reader) => reader.latestAssistant(activeID));
|
|
44998
|
+
if (!successfulReusableAssistant(latest)) {
|
|
44999
|
+
store.retire(active, "newest-assistant-not-reusable");
|
|
45000
|
+
active = undefined;
|
|
45001
|
+
}
|
|
45002
|
+
}
|
|
45003
|
+
if (!active) {
|
|
45004
|
+
const title = roleTitle(role);
|
|
45005
|
+
const created = await host.create({
|
|
45006
|
+
title,
|
|
45007
|
+
agent: roleAgent(role),
|
|
45008
|
+
model: {
|
|
45009
|
+
providerID: head.providerID,
|
|
45010
|
+
id: head.modelID,
|
|
45011
|
+
...head.variant ? { variant: head.variant } : {}
|
|
45012
|
+
},
|
|
45013
|
+
location: { directory: identity.directory },
|
|
45014
|
+
metadata: { magic_context: "hidden-run", role }
|
|
45015
|
+
});
|
|
45016
|
+
if (!created.id)
|
|
45017
|
+
throw new Error("OpenCode 2 did not return a child session id");
|
|
45018
|
+
active = {
|
|
45019
|
+
id: created.id,
|
|
45020
|
+
role,
|
|
45021
|
+
generation,
|
|
45022
|
+
title,
|
|
45023
|
+
model: head,
|
|
45024
|
+
created_at: Date.now(),
|
|
45025
|
+
title_reasserted: false
|
|
45026
|
+
};
|
|
45027
|
+
store.put(active);
|
|
45028
|
+
options.hook.registerChild(active.id);
|
|
45029
|
+
}
|
|
45030
|
+
openedChild = active;
|
|
45031
|
+
const handle = { id: active.id, childSessionId: active.id };
|
|
45032
|
+
const run = {
|
|
45033
|
+
identity,
|
|
45034
|
+
role,
|
|
45035
|
+
child: active,
|
|
45036
|
+
releaseRole,
|
|
45037
|
+
failed: false,
|
|
45038
|
+
retired: false
|
|
45039
|
+
};
|
|
45040
|
+
runs.set(handle, run);
|
|
45041
|
+
await switchChildModel(run, head);
|
|
45042
|
+
return handle;
|
|
45043
|
+
} catch (error) {
|
|
45044
|
+
if (openedChild)
|
|
45045
|
+
store.retire(openedChild, "hidden-run-open-failed");
|
|
45046
|
+
releaseRole();
|
|
45047
|
+
throw error;
|
|
45048
|
+
}
|
|
44088
45049
|
},
|
|
44089
45050
|
async attempt(handle, request) {
|
|
44090
45051
|
const run = runs.get(handle);
|
|
44091
45052
|
if (!run)
|
|
44092
45053
|
throw new Error("Unknown hidden completion run");
|
|
44093
|
-
|
|
44094
|
-
|
|
44095
|
-
if (sessionID === undefined)
|
|
44096
|
-
throw new Error("Hidden completion session id missing");
|
|
44097
|
-
const requested = request.body.model ?? actual;
|
|
44098
|
-
if (modelKey(requested) !== modelKey(actual))
|
|
44099
|
-
refuseModel(modelKey(requested), modelKey(actual));
|
|
44100
|
-
if (request.signal?.aborted)
|
|
45054
|
+
if (request.signal?.aborted) {
|
|
45055
|
+
await interruptAndRetire(run, "aborted-before-prompt");
|
|
44101
45056
|
throw new Error("Hidden completion prompt aborted");
|
|
44102
|
-
|
|
44103
|
-
const
|
|
44104
|
-
|
|
45057
|
+
}
|
|
45058
|
+
const requested = requestModel(request, run.child.model);
|
|
45059
|
+
await switchChildModel(run, requested);
|
|
45060
|
+
const baseline = withReader(options.openReader, (reader) => reader.latestSequence(run.child.id));
|
|
45061
|
+
const marker = `mc:hidden:${crypto.randomUUID()}:${crypto.randomUUID()}`;
|
|
45062
|
+
const attempt = {
|
|
45063
|
+
childSessionId: run.child.id,
|
|
45064
|
+
identity: run.identity,
|
|
45065
|
+
request,
|
|
45066
|
+
shaped: false
|
|
45067
|
+
};
|
|
45068
|
+
options.hook.registerAttempt(marker, attempt);
|
|
45069
|
+
const deadline = Date.now() + run.identity.timeoutMs;
|
|
45070
|
+
let abortReject;
|
|
45071
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
45072
|
+
abortReject = reject;
|
|
45073
|
+
});
|
|
45074
|
+
const onAbort = () => {
|
|
45075
|
+
interruptAndRetire(run, "prompt-aborted").finally(() => abortReject(new Error("Hidden completion prompt aborted")));
|
|
45076
|
+
};
|
|
45077
|
+
const deadlineTimer = setTimeout(() => {
|
|
45078
|
+
interruptAndRetire(run, "prompt-timeout").finally(() => abortReject(new Error("Hidden completion prompt timed out")));
|
|
45079
|
+
}, Math.max(0, deadline - Date.now()));
|
|
45080
|
+
request.signal?.addEventListener("abort", onAbort, { once: true });
|
|
44105
45081
|
try {
|
|
44106
|
-
|
|
44107
|
-
|
|
44108
|
-
|
|
44109
|
-
|
|
44110
|
-
|
|
44111
|
-
|
|
44112
|
-
|
|
44113
|
-
|
|
44114
|
-
|
|
44115
|
-
|
|
44116
|
-
|
|
44117
|
-
|
|
44118
|
-
|
|
44119
|
-
|
|
44120
|
-
|
|
45082
|
+
await Promise.race([
|
|
45083
|
+
host.prompt({ sessionID: run.child.id, text: marker }),
|
|
45084
|
+
aborted
|
|
45085
|
+
]);
|
|
45086
|
+
await Promise.race([host.wait({ sessionID: run.child.id }), aborted]);
|
|
45087
|
+
clearTimeout(deadlineTimer);
|
|
45088
|
+
const row = await Promise.race([
|
|
45089
|
+
awaitAssistantRow(options.openReader, run.child.id, baseline, deadline, request.signal),
|
|
45090
|
+
aborted
|
|
45091
|
+
]);
|
|
45092
|
+
if (!attempt.shaped) {
|
|
45093
|
+
throw new HiddenCompletionRefusal("hidden_prompt_unrecognized", "Host did not dispatch the hidden child context hook", true);
|
|
45094
|
+
}
|
|
45095
|
+
if (!run.child.title_reasserted) {
|
|
45096
|
+
await host.update({ sessionID: run.child.id, title: run.child.title });
|
|
45097
|
+
run.child = store.markTitleReasserted(run.child);
|
|
45098
|
+
}
|
|
45099
|
+
const text = assistantText(row);
|
|
44121
45100
|
const system = typeof request.body.system === "string" ? request.body.system : run.identity.system;
|
|
44122
|
-
const
|
|
44123
|
-
usage.input += run.usage?.input ?? 0;
|
|
44124
|
-
usage.output += run.usage?.output ?? 0;
|
|
44125
|
-
run.usage = usage;
|
|
45101
|
+
const tokens = row.data.tokens;
|
|
44126
45102
|
run.completion = {
|
|
44127
|
-
text
|
|
44128
|
-
|
|
44129
|
-
|
|
44130
|
-
|
|
44131
|
-
|
|
45103
|
+
text,
|
|
45104
|
+
reasoning: null,
|
|
45105
|
+
usage: tokens ? {
|
|
45106
|
+
input: tokens.input,
|
|
45107
|
+
output: tokens.output,
|
|
45108
|
+
cacheRead: tokens.cache.read,
|
|
45109
|
+
cacheWrite: tokens.cache.write
|
|
45110
|
+
} : meter(system, promptText(request), text ?? ""),
|
|
45111
|
+
lengthCapped: ["length", "max_tokens"].includes(row.data.finish ?? ""),
|
|
45112
|
+
providerId: row.data.model?.providerID ?? requested.providerID,
|
|
45113
|
+
modelId: row.data.model?.id ?? requested.modelID
|
|
44132
45114
|
};
|
|
45115
|
+
} catch (error) {
|
|
45116
|
+
run.failed = true;
|
|
45117
|
+
if (request.signal?.aborted && !run.retired) {
|
|
45118
|
+
await interruptAndRetire(run, "prompt-aborted");
|
|
45119
|
+
} else if (error instanceof Error && error.message.includes("timed out") && !run.retired) {
|
|
45120
|
+
await interruptAndRetire(run, "prompt-timeout");
|
|
45121
|
+
}
|
|
45122
|
+
throw error;
|
|
44133
45123
|
} finally {
|
|
44134
|
-
|
|
44135
|
-
|
|
45124
|
+
clearTimeout(deadlineTimer);
|
|
45125
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
45126
|
+
options.hook.releaseAttempt(marker);
|
|
44136
45127
|
}
|
|
44137
45128
|
},
|
|
44138
45129
|
async collect(handle) {
|
|
@@ -44141,18 +45132,27 @@ async function createV2HiddenCompletionExecutor(host, currentModel) {
|
|
|
44141
45132
|
throw new Error("Hidden completion has no settled output");
|
|
44142
45133
|
return completion;
|
|
44143
45134
|
},
|
|
44144
|
-
async close() {
|
|
45135
|
+
async close(handle, settlement) {
|
|
45136
|
+
if (!handle)
|
|
45137
|
+
return;
|
|
45138
|
+
const run = runs.get(handle);
|
|
45139
|
+
if (!run)
|
|
45140
|
+
return;
|
|
45141
|
+
try {
|
|
45142
|
+
if (!run.completion && (run.failed || !settlement.promptSettled)) {
|
|
45143
|
+
retire(run, "hidden-run-failed");
|
|
45144
|
+
}
|
|
45145
|
+
} finally {
|
|
45146
|
+
runs.delete(handle);
|
|
45147
|
+
run.releaseRole();
|
|
45148
|
+
}
|
|
45149
|
+
}
|
|
44145
45150
|
};
|
|
44146
45151
|
}
|
|
44147
45152
|
|
|
44148
45153
|
// src/v2/store-reader.ts
|
|
44149
|
-
import { join as join11 } from "node:path";
|
|
44150
|
-
function sourceDatabaseFilename(channel, env = process.env) {
|
|
44151
|
-
return env.OPENCODE_DB ?? (["latest", "dev", "beta", "next", "prod"].includes(channel) || env.OPENCODE_DISABLE_CHANNEL_DB === "1" || env.OPENCODE_DISABLE_CHANNEL_DB === "true" ? "opencode.db" : `opencode-${channel.replace(/[^a-zA-Z0-9._-]/g, "")}.db`);
|
|
44152
|
-
}
|
|
44153
45154
|
function gaDatabasePath(dataHome, channel = "latest", env = process.env) {
|
|
44154
|
-
|
|
44155
|
-
return filename === ":memory:" ? filename : join11(dataHome, "opencode", filename);
|
|
45155
|
+
return resolveOpenCodeDbPath("v2", { dataHome, channel, env }).path;
|
|
44156
45156
|
}
|
|
44157
45157
|
function decode(row) {
|
|
44158
45158
|
const data = JSON.parse(row.data);
|
|
@@ -44169,6 +45169,12 @@ class V2StoreReader {
|
|
|
44169
45169
|
db;
|
|
44170
45170
|
constructor(path) {
|
|
44171
45171
|
this.db = new Database(path, { readonly: true, fileMustExist: true });
|
|
45172
|
+
try {
|
|
45173
|
+
assertOpenCodeStoreGeneration(this.db, "v2", path);
|
|
45174
|
+
} catch (error) {
|
|
45175
|
+
this.db.close();
|
|
45176
|
+
throw error;
|
|
45177
|
+
}
|
|
44172
45178
|
}
|
|
44173
45179
|
close() {
|
|
44174
45180
|
this.db.close();
|
|
@@ -44194,6 +45200,16 @@ class V2StoreReader {
|
|
|
44194
45200
|
idleRows(sessionID, after = -1) {
|
|
44195
45201
|
return this.all(sessionID, after, "idle");
|
|
44196
45202
|
}
|
|
45203
|
+
latestSequence(sessionID) {
|
|
45204
|
+
const row = this.db.prepare("SELECT MAX(seq) AS seq FROM session_message WHERE session_id = ?").get(sessionID);
|
|
45205
|
+
return typeof row?.seq === "number" ? row.seq : -1;
|
|
45206
|
+
}
|
|
45207
|
+
latestAssistant(sessionID) {
|
|
45208
|
+
const row = this.db.prepare(`SELECT id, session_id, type, seq, data FROM session_message
|
|
45209
|
+
WHERE session_id = ? AND type = 'assistant'
|
|
45210
|
+
ORDER BY seq DESC LIMIT 1`).get(sessionID);
|
|
45211
|
+
return row ? decode(row) : undefined;
|
|
45212
|
+
}
|
|
44197
45213
|
window(sessionID) {
|
|
44198
45214
|
return this.db.transaction(() => {
|
|
44199
45215
|
const cut = this.latestCompaction(sessionID);
|
|
@@ -44443,7 +45459,7 @@ function rawMessages(rows, history = rows) {
|
|
|
44443
45459
|
const ordinals = new Map(history.filter(isRawRow).map((row, index) => [row.id, index + 1]));
|
|
44444
45460
|
return rows.filter(isRawRow).map((row) => ({
|
|
44445
45461
|
id: row.id,
|
|
44446
|
-
ordinal: ordinals.get(row.id),
|
|
45462
|
+
ordinal: ordinals.get(row.id) ?? 0,
|
|
44447
45463
|
role: row.type === "assistant" ? "assistant" : "user",
|
|
44448
45464
|
createdAt: row.data.time?.created,
|
|
44449
45465
|
parts: row.type === "assistant" ? (row.data.content ?? []).map((part) => {
|
|
@@ -44477,20 +45493,96 @@ function createHostSeams(context, read, liveModels) {
|
|
|
44477
45493
|
hostRefuse: (_client, sessionID) => interruptBeforeProvider(context.session, sessionID)
|
|
44478
45494
|
};
|
|
44479
45495
|
}
|
|
45496
|
+
function toolResultText(result) {
|
|
45497
|
+
const content = result?.content ?? result?.output;
|
|
45498
|
+
if (typeof content === "string")
|
|
45499
|
+
return content;
|
|
45500
|
+
if (content && typeof content === "object" && !Array.isArray(content)) {
|
|
45501
|
+
const record = content;
|
|
45502
|
+
if (typeof record.text === "string")
|
|
45503
|
+
return record.text;
|
|
45504
|
+
if (typeof record.value === "string")
|
|
45505
|
+
return record.value;
|
|
45506
|
+
return "";
|
|
45507
|
+
}
|
|
45508
|
+
if (!Array.isArray(content))
|
|
45509
|
+
return "";
|
|
45510
|
+
return content.map((part) => {
|
|
45511
|
+
if (typeof part === "string")
|
|
45512
|
+
return part;
|
|
45513
|
+
if (!part || typeof part !== "object")
|
|
45514
|
+
return "";
|
|
45515
|
+
const record = part;
|
|
45516
|
+
if (typeof record.text === "string")
|
|
45517
|
+
return record.text;
|
|
45518
|
+
if (typeof record.value === "string")
|
|
45519
|
+
return record.value;
|
|
45520
|
+
return "";
|
|
45521
|
+
}).filter(Boolean).join(`
|
|
45522
|
+
`);
|
|
45523
|
+
}
|
|
45524
|
+
function catalogModels(listed) {
|
|
45525
|
+
const rows = Array.isArray(listed) ? listed : listed && typeof listed === "object" && Array.isArray(listed.data) ? listed.data : [];
|
|
45526
|
+
return rows.flatMap((row) => {
|
|
45527
|
+
if (!row || typeof row !== "object")
|
|
45528
|
+
return [];
|
|
45529
|
+
const model = row;
|
|
45530
|
+
if (typeof model.id !== "string" || typeof model.providerID !== "string")
|
|
45531
|
+
return [];
|
|
45532
|
+
const contextLimit = model.limit?.context;
|
|
45533
|
+
if (typeof contextLimit !== "number" || !Number.isFinite(contextLimit))
|
|
45534
|
+
return [];
|
|
45535
|
+
return [{ id: model.id, providerID: model.providerID, limit: { context: contextLimit } }];
|
|
45536
|
+
});
|
|
45537
|
+
}
|
|
45538
|
+
function applyV2PromptSurfaceTools(draft, runtime, config) {
|
|
45539
|
+
if (!draft.tools)
|
|
45540
|
+
return;
|
|
45541
|
+
const modelKey = `${draft.model.providerID}/${draft.model.id}`;
|
|
45542
|
+
const registration = runtime.resolveRegistration(config, modelKey);
|
|
45543
|
+
for (const id of ACTIVE_TOOL_IDS) {
|
|
45544
|
+
const tool = draft.tools[id];
|
|
45545
|
+
if (!tool)
|
|
45546
|
+
continue;
|
|
45547
|
+
tool.description = registration.descriptionFor(id, tool.description);
|
|
45548
|
+
}
|
|
45549
|
+
}
|
|
44480
45550
|
async function registerContext(context) {
|
|
44481
45551
|
const directory = context.location.directory;
|
|
44482
45552
|
const config = loadPluginConfigDetailed(directory).config;
|
|
44483
45553
|
if (!config.enabled || !isCompactionEnabled(config))
|
|
44484
45554
|
return;
|
|
45555
|
+
const conflicts = detectConflicts(directory, {
|
|
45556
|
+
compactionEnabled: true,
|
|
45557
|
+
hostGeneration: "v2"
|
|
45558
|
+
});
|
|
45559
|
+
if (conflicts.hasConflict) {
|
|
45560
|
+
console.warn(`[magic-context] v2 setup disabled by conflicting context hooks: ${conflicts.reasons.join("; ")}`);
|
|
45561
|
+
return;
|
|
45562
|
+
}
|
|
44485
45563
|
const folds = new FoldOwner(context.storage);
|
|
44486
45564
|
const limits = new Map;
|
|
44487
45565
|
const queriedModels = new Set;
|
|
44488
45566
|
const liveModels = new Map;
|
|
44489
|
-
const
|
|
44490
|
-
|
|
44491
|
-
|
|
44492
|
-
|
|
44493
|
-
}
|
|
45567
|
+
const promptSurfaceRuntime = createPromptSurfaceRuntime({
|
|
45568
|
+
harness: "opencode2",
|
|
45569
|
+
directory,
|
|
45570
|
+
warn: (message) => console.warn(`[magic-context] config warning: ${message}`)
|
|
45571
|
+
});
|
|
45572
|
+
let db;
|
|
45573
|
+
try {
|
|
45574
|
+
db = openDatabase() ?? undefined;
|
|
45575
|
+
} catch {}
|
|
45576
|
+
const hiddenChildHook = new HiddenChildHook;
|
|
45577
|
+
await registerHiddenChildAgents(context.agent);
|
|
45578
|
+
let hiddenAgentsReady;
|
|
45579
|
+
const hiddenCompletionExecutor = db && isDatabasePersisted(db) ? await createV2HiddenCompletionExecutor(context.session, {
|
|
45580
|
+
db,
|
|
45581
|
+
projectIdentity: resolveProjectIdentity(directory) ?? directory,
|
|
45582
|
+
hook: hiddenChildHook,
|
|
45583
|
+
ensureAgent: () => hiddenAgentsReady ??= context.agent.reload(),
|
|
45584
|
+
openReader: () => new V2StoreReader(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"))
|
|
45585
|
+
}) : undefined;
|
|
44494
45586
|
const dreamTrigger = hiddenCompletionExecutor && config.dreamer && !config.dreamer.disable ? startDreamTrigger(context, {
|
|
44495
45587
|
config: config.dreamer,
|
|
44496
45588
|
executor: hiddenCompletionExecutor,
|
|
@@ -44511,16 +45603,17 @@ async function registerContext(context) {
|
|
|
44511
45603
|
let toolDuties;
|
|
44512
45604
|
await context.tool.hook("execute.before", (draft) => assertExecutableToolInput(draft.input));
|
|
44513
45605
|
await context.tool.hook("execute.after", async (draft) => {
|
|
44514
|
-
if (!db
|
|
45606
|
+
if (!db)
|
|
45607
|
+
return;
|
|
45608
|
+
if (draft.status && draft.status !== "completed")
|
|
44515
45609
|
return;
|
|
44516
45610
|
try {
|
|
44517
45611
|
toolDuties ??= createToolExecuteAfterHook({ db, channel1StateBySession: channel1 });
|
|
44518
|
-
const
|
|
44519
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.filter((part) => part.type === "text").map((part) => part.text).join(`
|
|
44520
|
-
`) : "";
|
|
45612
|
+
const text = toolResultText(draft.result);
|
|
44521
45613
|
const output = { output: text };
|
|
44522
45614
|
await toolDuties({ ...draft, args: draft.input }, output);
|
|
44523
45615
|
if (draft.result && output.output !== text) {
|
|
45616
|
+
const content = draft.result.content;
|
|
44524
45617
|
if (typeof content === "string")
|
|
44525
45618
|
draft.result.content = output.output;
|
|
44526
45619
|
else if (Array.isArray(content) && output.output.startsWith(text))
|
|
@@ -44545,7 +45638,6 @@ async function registerContext(context) {
|
|
|
44545
45638
|
getCount: (sessionID) => read(sessionID).length
|
|
44546
45639
|
});
|
|
44547
45640
|
let transform;
|
|
44548
|
-
let db;
|
|
44549
45641
|
const refuseIfUnsafe = async (draft) => {
|
|
44550
45642
|
let unsafe = false;
|
|
44551
45643
|
try {
|
|
@@ -44559,10 +45651,8 @@ async function registerContext(context) {
|
|
|
44559
45651
|
const tokens = latest?.data.tokens;
|
|
44560
45652
|
const modelKey = `${draft.model.providerID}/${draft.model.id}`;
|
|
44561
45653
|
if (!queriedModels.has(modelKey)) {
|
|
44562
|
-
const catalog = await context.
|
|
44563
|
-
|
|
44564
|
-
});
|
|
44565
|
-
for (const model of catalog.data)
|
|
45654
|
+
const catalog = await Promise.resolve(context.model.list());
|
|
45655
|
+
for (const model of catalogModels(catalog))
|
|
44566
45656
|
limits.set(`${model.providerID}/${model.id}`, model.limit.context);
|
|
44567
45657
|
queriedModels.add(modelKey);
|
|
44568
45658
|
}
|
|
@@ -44582,7 +45672,8 @@ async function registerContext(context) {
|
|
|
44582
45672
|
} finally {
|
|
44583
45673
|
reader.close();
|
|
44584
45674
|
}
|
|
44585
|
-
} catch {
|
|
45675
|
+
} catch (error) {
|
|
45676
|
+
console.warn("[magic-context] v2 refuseIfUnsafe", error);
|
|
44586
45677
|
unsafe = true;
|
|
44587
45678
|
}
|
|
44588
45679
|
if (unsafe)
|
|
@@ -44635,6 +45726,26 @@ async function registerContext(context) {
|
|
|
44635
45726
|
}
|
|
44636
45727
|
});
|
|
44637
45728
|
await context.session.hook("context", async (draft) => {
|
|
45729
|
+
if (hiddenChildHook.apply(draft))
|
|
45730
|
+
return;
|
|
45731
|
+
liveModels.set(draft.sessionID, {
|
|
45732
|
+
providerID: draft.model.providerID,
|
|
45733
|
+
modelID: draft.model.id
|
|
45734
|
+
});
|
|
45735
|
+
variants.set(draft.sessionID, draft.model.variant);
|
|
45736
|
+
agents.set(draft.sessionID, draft.agent);
|
|
45737
|
+
applyV2PromptSurfaceTools(draft, promptSurfaceRuntime, config.prompt_surface);
|
|
45738
|
+
if (context.tool.transform) {
|
|
45739
|
+
const modelKey = `${draft.model.providerID}/${draft.model.id}`;
|
|
45740
|
+
const registration = promptSurfaceRuntime.resolveRegistration(config.prompt_surface, modelKey);
|
|
45741
|
+
await context.tool.transform((editor) => {
|
|
45742
|
+
for (const id of ACTIVE_TOOL_IDS) {
|
|
45743
|
+
editor.update(id, (tool) => {
|
|
45744
|
+
tool.description = registration.descriptionFor(id, tool.description);
|
|
45745
|
+
});
|
|
45746
|
+
}
|
|
45747
|
+
});
|
|
45748
|
+
}
|
|
44638
45749
|
let postFold = false;
|
|
44639
45750
|
try {
|
|
44640
45751
|
if (await refuseIfUnsafe(draft))
|
|
@@ -44751,6 +45862,9 @@ async function registerContext(context) {
|
|
|
44751
45862
|
const mapped = adaptPayload(draft, admitted);
|
|
44752
45863
|
await transform({}, mapped);
|
|
44753
45864
|
mapped.commit();
|
|
45865
|
+
if (db) {
|
|
45866
|
+
await deliverPendingChannel2(context, db, draft.sessionID, channel1.get(draft.sessionID));
|
|
45867
|
+
}
|
|
44754
45868
|
if (checkpoint && submitted !== undefined) {
|
|
44755
45869
|
const head = draft.messages.find((message) => message.id === HEAD_IDS[0]);
|
|
44756
45870
|
const baseline = head?.content.find((part) => part.type === "text")?.text;
|
|
@@ -44790,18 +45904,18 @@ ${baseline}
|
|
|
44790
45904
|
}
|
|
44791
45905
|
|
|
44792
45906
|
// src/v2/hooks/update-check.ts
|
|
44793
|
-
import { existsSync as
|
|
44794
|
-
import { dirname as dirname8, join as
|
|
45907
|
+
import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
|
|
45908
|
+
import { dirname as dirname8, join as join11 } from "node:path";
|
|
44795
45909
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
44796
45910
|
function packageInfo() {
|
|
44797
45911
|
let directory = dirname8(fileURLToPath3(import.meta.url));
|
|
44798
45912
|
for (;; ) {
|
|
44799
45913
|
try {
|
|
44800
|
-
const pkg = JSON.parse(
|
|
45914
|
+
const pkg = JSON.parse(readFileSync8(join11(directory, "package.json"), "utf8"));
|
|
44801
45915
|
if (pkg.name === "@cortexkit/opencode-magic-context" && typeof pkg.version === "string") {
|
|
44802
45916
|
return {
|
|
44803
45917
|
version: pkg.version,
|
|
44804
|
-
development:
|
|
45918
|
+
development: existsSync14(join11(directory, "src/v2/server.ts"))
|
|
44805
45919
|
};
|
|
44806
45920
|
}
|
|
44807
45921
|
} catch {}
|
|
@@ -44848,7 +45962,17 @@ function startUpdateChecks(context, check = (signal) => {
|
|
|
44848
45962
|
}
|
|
44849
45963
|
|
|
44850
45964
|
// src/v2/server.ts
|
|
45965
|
+
function isOpenCode2HostContext(context) {
|
|
45966
|
+
if (typeof context !== "object" || context === null)
|
|
45967
|
+
return false;
|
|
45968
|
+
const session = context.session;
|
|
45969
|
+
return typeof session === "object" && session !== null && typeof session.hook === "function";
|
|
45970
|
+
}
|
|
44851
45971
|
async function setup(context) {
|
|
45972
|
+
if (!isOpenCode2HostContext(context)) {
|
|
45973
|
+
log("v2 setup() called on a host without session.hook; v1 server lane owns this host, v2 lane inert");
|
|
45974
|
+
return async () => {};
|
|
45975
|
+
}
|
|
44852
45976
|
setHarness("opencode2");
|
|
44853
45977
|
const duties = await registerContext(context);
|
|
44854
45978
|
const checks = loadPluginConfigDetailed(context.location.directory).config.auto_update === false ? undefined : startUpdateChecks(context);
|
|
@@ -44872,6 +45996,7 @@ var server = async (ctx) => {
|
|
|
44872
45996
|
let hooksDeadlineReason = null;
|
|
44873
45997
|
let deadlinePhase = null;
|
|
44874
45998
|
emitBootEnteringBreadcrumb(process.pid, ctx.directory, log, flushLogger);
|
|
45999
|
+
setHarness("opencode");
|
|
44875
46000
|
const configStartedAt = performance.now();
|
|
44876
46001
|
beginBootQuietPeriod();
|
|
44877
46002
|
const configMigrationWarnings = migrateMagicContextConfigLocations(ctx.directory, {
|
|
@@ -44911,7 +46036,7 @@ var server = async (ctx) => {
|
|
|
44911
46036
|
if (hasBannerEntries)
|
|
44912
46037
|
setTimeout(async () => {
|
|
44913
46038
|
try {
|
|
44914
|
-
const { sendStatusNotification } = await import("./send-session-notification-
|
|
46039
|
+
const { sendStatusNotification } = await import("./send-session-notification-jbxknvar.js");
|
|
44915
46040
|
const clientWithSessions = ctx.client;
|
|
44916
46041
|
const sessions = await Promise.resolve(clientWithSessions.session?.list?.()).catch(() => null);
|
|
44917
46042
|
const sessionList = Array.isArray(sessions) ? sessions : sessions?.data;
|
|
@@ -44928,7 +46053,7 @@ var server = async (ctx) => {
|
|
|
44928
46053
|
log(`[magic-context] opencode_db_missing path=${openCodeDbResolution.path} source=${openCodeDbResolution.source}`);
|
|
44929
46054
|
setTimeout(async () => {
|
|
44930
46055
|
try {
|
|
44931
|
-
const { sendStatusNotification } = await import("./send-session-notification-
|
|
46056
|
+
const { sendStatusNotification } = await import("./send-session-notification-jbxknvar.js");
|
|
44932
46057
|
const clientWithSessions = ctx.client;
|
|
44933
46058
|
const sessions = await Promise.resolve(clientWithSessions.session?.list?.()).catch(() => null);
|
|
44934
46059
|
const sessionList = Array.isArray(sessions) ? sessions : sessions?.data;
|
|
@@ -45174,12 +46299,12 @@ var server = async (ctx) => {
|
|
|
45174
46299
|
{
|
|
45175
46300
|
const fence = getSchemaFenceRejection();
|
|
45176
46301
|
if (fence) {
|
|
45177
|
-
import("./conflict-warning-hook-
|
|
46302
|
+
import("./conflict-warning-hook-aktepwpz.js").then(({ sendSchemaFenceWarning }) => sendSchemaFenceWarning(ctx.client, ctx.directory, fence));
|
|
45178
46303
|
}
|
|
45179
46304
|
}
|
|
45180
46305
|
const serverUrl = ctx.serverUrl;
|
|
45181
46306
|
const serverUrlStr = serverUrl instanceof URL ? serverUrl.toString().replace(/\/$/, "") : typeof serverUrl === "string" ? serverUrl.replace(/\/$/, "") : undefined;
|
|
45182
|
-
import("./send-session-notification-
|
|
46307
|
+
import("./send-session-notification-jbxknvar.js").then(({ setNotificationServerUrl }) => {
|
|
45183
46308
|
setNotificationServerUrl(serverUrlStr);
|
|
45184
46309
|
});
|
|
45185
46310
|
if (conflictResult?.hasConflict) {
|
|
@@ -45202,7 +46327,7 @@ var server = async (ctx) => {
|
|
|
45202
46327
|
}) => {
|
|
45203
46328
|
if (!shouldShowAnnouncement())
|
|
45204
46329
|
return;
|
|
45205
|
-
return import("./conflict-warning-hook-
|
|
46330
|
+
return import("./conflict-warning-hook-aktepwpz.js").then(({ sendStartupAnnouncement }) => sendStartupAnnouncement(ctx.client, ctx.directory, ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER, markAnnouncementSeen));
|
|
45206
46331
|
}).catch(() => {});
|
|
45207
46332
|
}, 8000);
|
|
45208
46333
|
}
|