@cortexkit/opencode-magic-context 0.21.2 → 0.21.5
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 +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +0 -3
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/variable.d.ts +9 -7
- package/dist/config/variable.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +3 -1
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
- package/dist/features/magic-context/key-files/identify-key-files.d.ts.map +1 -1
- package/dist/features/magic-context/key-files/project-key-files.d.ts.map +1 -1
- package/dist/features/magic-context/key-files/read-history.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +18 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +17 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-notes.d.ts +6 -2
- package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts +4 -4
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +0 -2
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +2 -2
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +0 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +0 -2
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +0 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.js +214 -75
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/sidebar-snapshot-cache.d.ts.map +1 -1
- package/dist/shared/jsonc-parser.d.ts +1 -0
- package/dist/shared/jsonc-parser.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/package.json +1 -1
- package/src/shared/jsonc-parser.ts +1 -1
- package/src/tui/slots/sidebar-content.tsx +27 -14
package/dist/index.js
CHANGED
|
@@ -14950,7 +14950,6 @@ var init_magic_context = __esm(() => {
|
|
|
14950
14950
|
enabled: exports_external.boolean().default(true),
|
|
14951
14951
|
min_clusters: exports_external.number().min(1).default(3)
|
|
14952
14952
|
}).default({ enabled: true, min_clusters: 3 }),
|
|
14953
|
-
compaction_markers: exports_external.boolean().default(true),
|
|
14954
14953
|
system_prompt_injection: exports_external.object({
|
|
14955
14954
|
enabled: exports_external.boolean().default(true),
|
|
14956
14955
|
skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"])
|
|
@@ -156281,7 +156280,7 @@ function promoteRecompStaging(db, sessionId) {
|
|
|
156281
156280
|
insertFactRows(db, sessionId, staging.facts, now);
|
|
156282
156281
|
db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
|
|
156283
156282
|
db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
|
|
156284
|
-
db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE session_id = ?").run(sessionId);
|
|
156283
|
+
db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
|
|
156285
156284
|
return { compartments: staging.compartments, facts: staging.facts };
|
|
156286
156285
|
})();
|
|
156287
156286
|
}
|
|
@@ -157706,8 +157705,10 @@ function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
|
|
|
157706
157705
|
if (existsSync10(row.projectPath))
|
|
157707
157706
|
continue;
|
|
157708
157707
|
try {
|
|
157709
|
-
db.
|
|
157710
|
-
|
|
157708
|
+
db.transaction(() => {
|
|
157709
|
+
db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
|
|
157710
|
+
db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
|
|
157711
|
+
})();
|
|
157711
157712
|
deletedProjects++;
|
|
157712
157713
|
} catch (error51) {
|
|
157713
157714
|
log(`[key-files] orphan GC failed for ${row.projectPath}:`, error51);
|
|
@@ -158255,6 +158256,12 @@ function getNoteById(db, noteId) {
|
|
|
158255
158256
|
const row = db.prepare("SELECT * FROM notes WHERE id = ?").get(noteId);
|
|
158256
158257
|
return isNoteRow(row) ? toNote(row) : null;
|
|
158257
158258
|
}
|
|
158259
|
+
function noteBelongsToScope(note, scope) {
|
|
158260
|
+
if (note.type === "session") {
|
|
158261
|
+
return note.sessionId === scope.sessionId;
|
|
158262
|
+
}
|
|
158263
|
+
return note.projectPath === scope.projectPath;
|
|
158264
|
+
}
|
|
158258
158265
|
function buildStatusClause(status) {
|
|
158259
158266
|
if (status === undefined) {
|
|
158260
158267
|
return null;
|
|
@@ -158316,9 +158323,9 @@ function getPendingSmartNotes(db, projectPath) {
|
|
|
158316
158323
|
function getReadySmartNotes(db, projectPath) {
|
|
158317
158324
|
return getSmartNotes(db, projectPath, "ready");
|
|
158318
158325
|
}
|
|
158319
|
-
function updateNote(db, noteId, updates) {
|
|
158326
|
+
function updateNote(db, noteId, updates, scope) {
|
|
158320
158327
|
const existing = getNoteById(db, noteId);
|
|
158321
|
-
if (!existing) {
|
|
158328
|
+
if (!existing || !noteBelongsToScope(existing, scope)) {
|
|
158322
158329
|
return null;
|
|
158323
158330
|
}
|
|
158324
158331
|
const now = Date.now();
|
|
@@ -158365,7 +158372,11 @@ function updateNote(db, noteId, updates) {
|
|
|
158365
158372
|
const result = db.prepare(`UPDATE notes SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...params);
|
|
158366
158373
|
return isNoteRow(result) ? toNote(result) : null;
|
|
158367
158374
|
}
|
|
158368
|
-
function dismissNote(db, noteId) {
|
|
158375
|
+
function dismissNote(db, noteId, scope) {
|
|
158376
|
+
const existing = getNoteById(db, noteId);
|
|
158377
|
+
if (!existing || !noteBelongsToScope(existing, scope)) {
|
|
158378
|
+
return false;
|
|
158379
|
+
}
|
|
158369
158380
|
const result = db.prepare("UPDATE notes SET status = 'dismissed', updated_at = ? WHERE id = ? AND status != 'dismissed'").run(Date.now(), noteId);
|
|
158370
158381
|
return result.changes > 0;
|
|
158371
158382
|
}
|
|
@@ -160473,7 +160484,7 @@ function getCurrentVersion(db) {
|
|
|
160473
160484
|
const row = db.prepare("SELECT MAX(version) as version FROM schema_migrations").get();
|
|
160474
160485
|
return row?.version ?? 0;
|
|
160475
160486
|
}
|
|
160476
|
-
function isSiblingMigrationConflict(error51, version2) {
|
|
160487
|
+
function isSiblingMigrationConflict(db, error51, version2) {
|
|
160477
160488
|
if (!(error51 instanceof Error))
|
|
160478
160489
|
return false;
|
|
160479
160490
|
const code = error51.code;
|
|
@@ -160485,7 +160496,8 @@ function isSiblingMigrationConflict(error51, version2) {
|
|
|
160485
160496
|
return false;
|
|
160486
160497
|
if (!msg.toLowerCase().includes("version"))
|
|
160487
160498
|
return false;
|
|
160488
|
-
|
|
160499
|
+
const confirmed = db.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(version2);
|
|
160500
|
+
return confirmed != null;
|
|
160489
160501
|
}
|
|
160490
160502
|
function runMigrations(db) {
|
|
160491
160503
|
ensureMigrationsTable(db);
|
|
@@ -160506,7 +160518,7 @@ function runMigrations(db) {
|
|
|
160506
160518
|
log(`[migrations] applied v${migration.version}: ${migration.description}`);
|
|
160507
160519
|
migrationIndex += 1;
|
|
160508
160520
|
} catch (error51) {
|
|
160509
|
-
if (isSiblingMigrationConflict(error51, migration.version)) {
|
|
160521
|
+
if (isSiblingMigrationConflict(db, error51, migration.version)) {
|
|
160510
160522
|
log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
|
|
160511
160523
|
const reReadVersion = getCurrentVersion(db);
|
|
160512
160524
|
if (reReadVersion <= currentVersion) {
|
|
@@ -160912,6 +160924,16 @@ var init_migrations = __esm(async () => {
|
|
|
160912
160924
|
WHERE auto_search_hint_decisions IS NULL
|
|
160913
160925
|
`);
|
|
160914
160926
|
}
|
|
160927
|
+
},
|
|
160928
|
+
{
|
|
160929
|
+
version: 18,
|
|
160930
|
+
description: "Add pending_pi_compaction_marker_state column for Pi deferred marker drain",
|
|
160931
|
+
up: (db) => {
|
|
160932
|
+
const cols = db.prepare("PRAGMA table_info(session_meta)").all();
|
|
160933
|
+
if (!cols.some((c) => c.name === "pending_pi_compaction_marker_state")) {
|
|
160934
|
+
db.exec("ALTER TABLE session_meta ADD COLUMN pending_pi_compaction_marker_state TEXT");
|
|
160935
|
+
}
|
|
160936
|
+
}
|
|
160915
160937
|
}
|
|
160916
160938
|
];
|
|
160917
160939
|
});
|
|
@@ -161443,6 +161465,11 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
161443
161465
|
-- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
|
|
161444
161466
|
-- != empty-string defensively. Plan v6 section 3.
|
|
161445
161467
|
pending_compaction_marker_state TEXT,
|
|
161468
|
+
-- pending_pi_compaction_marker_state: intentionally NULLABLE without a
|
|
161469
|
+
-- default. Absence of a deferred Pi-native marker is SQL NULL; presence
|
|
161470
|
+
-- is a valid JSON blob written via setPendingPiCompactionMarkerState.
|
|
161471
|
+
-- Excluded from healNullTextColumns.
|
|
161472
|
+
pending_pi_compaction_marker_state TEXT,
|
|
161446
161473
|
-- deferred_execute_state: intentionally NULLABLE without a default.
|
|
161447
161474
|
-- Absence is SQL NULL; presence is a JSON blob written via
|
|
161448
161475
|
-- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
|
|
@@ -161539,6 +161566,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
161539
161566
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
161540
161567
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
161541
161568
|
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
161569
|
+
ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
|
|
161542
161570
|
ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
|
|
161543
161571
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
161544
161572
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -162614,22 +162642,24 @@ function updateTagMessageId(db, sessionId, tagId, messageId) {
|
|
|
162614
162642
|
getUpdateTagMessageIdStatement(db).run(messageId, sessionId, tagId);
|
|
162615
162643
|
}
|
|
162616
162644
|
function deleteTagsByMessageId(db, sessionId, messageId) {
|
|
162617
|
-
|
|
162618
|
-
|
|
162619
|
-
|
|
162620
|
-
|
|
162621
|
-
|
|
162622
|
-
|
|
162623
|
-
|
|
162624
|
-
|
|
162625
|
-
|
|
162626
|
-
|
|
162627
|
-
|
|
162628
|
-
|
|
162629
|
-
|
|
162630
|
-
|
|
162631
|
-
|
|
162632
|
-
|
|
162645
|
+
return db.transaction(() => {
|
|
162646
|
+
const escapedMessageId = escapeLikePattern(messageId);
|
|
162647
|
+
const textPartPattern = `${escapedMessageId}:p%`;
|
|
162648
|
+
const filePartPattern = `${escapedMessageId}:file%`;
|
|
162649
|
+
const messageScopedTags = getTagNumbersByMessageIdStatement(db).all(sessionId, messageId, textPartPattern, filePartPattern).filter(isTagNumberRow).map((row) => row.tag_number);
|
|
162650
|
+
const ownerScopedTagNumbers = getOwnerScopedToolTagNumbers(db, sessionId, messageId);
|
|
162651
|
+
if (messageScopedTags.length === 0 && ownerScopedTagNumbers.length === 0) {
|
|
162652
|
+
return [];
|
|
162653
|
+
}
|
|
162654
|
+
if (messageScopedTags.length > 0) {
|
|
162655
|
+
getDeleteTagsByMessageIdStatement(db).run(sessionId, messageId, textPartPattern, filePartPattern);
|
|
162656
|
+
}
|
|
162657
|
+
if (ownerScopedTagNumbers.length > 0) {
|
|
162658
|
+
deleteToolTagsByOwner(db, sessionId, messageId);
|
|
162659
|
+
}
|
|
162660
|
+
const merged = new Set([...messageScopedTags, ...ownerScopedTagNumbers]);
|
|
162661
|
+
return Array.from(merged).sort((a, b) => a - b);
|
|
162662
|
+
})();
|
|
162633
162663
|
}
|
|
162634
162664
|
function getOwnerScopedToolTagNumbers(db, sessionId, ownerMsgId) {
|
|
162635
162665
|
let stmt = getOwnerScopedToolTagNumbersStatements.get(db);
|
|
@@ -164561,7 +164591,7 @@ async function executePartialRecompInternal(deps, range) {
|
|
|
164561
164591
|
}
|
|
164562
164592
|
deps.onCompartmentStatePublished?.(sessionId);
|
|
164563
164593
|
const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
|
|
164564
|
-
if (
|
|
164594
|
+
if (lastEnd > 0) {
|
|
164565
164595
|
updateCompactionMarkerAfterPublication(db, sessionId, lastEnd, deps.directory);
|
|
164566
164596
|
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
164567
164597
|
if (stalePending) {
|
|
@@ -165778,6 +165808,7 @@ async function runCompartmentAgent(deps) {
|
|
|
165778
165808
|
const existingValidationError = validateStoredCompartments(priorCompartments);
|
|
165779
165809
|
if (existingValidationError) {
|
|
165780
165810
|
sessionLog(sessionId, `historian failure: source=existing-validation reason="${existingValidationError}"`);
|
|
165811
|
+
incrementHistorianFailure(db, sessionId, existingValidationError);
|
|
165781
165812
|
await notifyHistorianIssue(`## Historian alert
|
|
165782
165813
|
|
|
165783
165814
|
Historian skipped this session because existing stored compartments are invalid: ${existingValidationError}
|
|
@@ -165788,15 +165819,20 @@ No new compartments or facts were written. Rebuild or clear the broken compartme
|
|
|
165788
165819
|
const offset = priorCompartments.length > 0 ? priorCompartments[priorCompartments.length - 1].endMessage + 1 : 1;
|
|
165789
165820
|
const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
|
|
165790
165821
|
if (protectedTailStart <= offset) {
|
|
165822
|
+
sessionLog(sessionId, `historian no-op: protectedTailStart=${protectedTailStart} <= offset=${offset} — nothing to compact`);
|
|
165823
|
+
clearEmergencyRecovery(db, sessionId);
|
|
165791
165824
|
return;
|
|
165792
165825
|
}
|
|
165793
165826
|
const chunk = readSessionChunk(sessionId, historianChunkTokens, offset, protectedTailStart);
|
|
165794
165827
|
if (!chunk.text || chunk.messageCount === 0) {
|
|
165828
|
+
sessionLog(sessionId, `historian no-op: chunk empty after filtering (messageCount=${chunk.messageCount}, textLen=${chunk.text?.length ?? 0}) range=${offset}-${protectedTailStart - 1}`);
|
|
165829
|
+
clearEmergencyRecovery(db, sessionId);
|
|
165795
165830
|
return;
|
|
165796
165831
|
}
|
|
165797
165832
|
const chunkCoverageError = validateChunkCoverage(chunk);
|
|
165798
165833
|
if (chunkCoverageError) {
|
|
165799
165834
|
sessionLog(sessionId, `historian failure: source=chunk-coverage reason="${chunkCoverageError}" chunkRange=${chunk.startIndex}-${chunk.endIndex}`);
|
|
165835
|
+
incrementHistorianFailure(db, sessionId, chunkCoverageError);
|
|
165800
165836
|
await notifyHistorianIssue(`## Historian alert
|
|
165801
165837
|
|
|
165802
165838
|
Historian skipped this session because the raw chunk could not be represented safely: ${chunkCoverageError}
|
|
@@ -165858,7 +165894,7 @@ Historian returned compartments that made no forward progress beyond raw message
|
|
|
165858
165894
|
No new compartments or facts were written. Check the historian model/output and try again.`);
|
|
165859
165895
|
return;
|
|
165860
165896
|
}
|
|
165861
|
-
const deferMarkerApplication = deps.preserveInjectionCacheUntilConsumed === true
|
|
165897
|
+
const deferMarkerApplication = deps.preserveInjectionCacheUntilConsumed === true;
|
|
165862
165898
|
const lastCompartmentEnd = lastNewEnd;
|
|
165863
165899
|
const lastNewEndMessageId = newCompartments[newCompartments.length - 1]?.endMessageId;
|
|
165864
165900
|
db.transaction(() => {
|
|
@@ -165883,12 +165919,10 @@ No new compartments or facts were written. Check the historian model/output and
|
|
|
165883
165919
|
promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), validatedPass.facts ?? []);
|
|
165884
165920
|
}
|
|
165885
165921
|
queueDropsForCompartmentalizedMessages(db, sessionId, lastCompartmentEnd);
|
|
165886
|
-
if (
|
|
165887
|
-
|
|
165888
|
-
|
|
165889
|
-
|
|
165890
|
-
updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, sessionDirectory);
|
|
165891
|
-
}
|
|
165922
|
+
if (deferMarkerApplication) {
|
|
165923
|
+
deps.onDeferredMarkerPending?.(sessionId);
|
|
165924
|
+
} else {
|
|
165925
|
+
updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, sessionDirectory);
|
|
165892
165926
|
}
|
|
165893
165927
|
if (deps.historyBudgetTokens && deps.historyBudgetTokens > 0) {
|
|
165894
165928
|
await runCompressionPassIfNeeded({
|
|
@@ -166031,7 +166065,7 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
|
|
|
166031
166065
|
if (lastCompartmentEnd2 > 0) {
|
|
166032
166066
|
queueDropsForCompartmentalizedMessages(db, sessionId, lastCompartmentEnd2);
|
|
166033
166067
|
}
|
|
166034
|
-
if (
|
|
166068
|
+
if (lastCompartmentEnd2 > 0) {
|
|
166035
166069
|
updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
|
|
166036
166070
|
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
166037
166071
|
if (stalePending) {
|
|
@@ -166414,6 +166448,7 @@ import { homedir as homedir2 } from "node:os";
|
|
|
166414
166448
|
import { join } from "node:path";
|
|
166415
166449
|
|
|
166416
166450
|
// src/config/variable.ts
|
|
166451
|
+
init_jsonc_parser();
|
|
166417
166452
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
166418
166453
|
import { homedir } from "node:os";
|
|
166419
166454
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
@@ -166422,6 +166457,21 @@ var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
|
166422
166457
|
function substituteConfigVariables(input) {
|
|
166423
166458
|
const warnings = [];
|
|
166424
166459
|
let text = input.text;
|
|
166460
|
+
if (input.isProjectConfig) {
|
|
166461
|
+
const hasEnvTokens = ENV_PATTERN.test(text);
|
|
166462
|
+
const hasFileTokens = FILE_PATTERN.test(text);
|
|
166463
|
+
ENV_PATTERN.lastIndex = 0;
|
|
166464
|
+
FILE_PATTERN.lastIndex = 0;
|
|
166465
|
+
if (hasEnvTokens || hasFileTokens) {
|
|
166466
|
+
const tokenTypes = [
|
|
166467
|
+
hasEnvTokens ? "{env:}" : undefined,
|
|
166468
|
+
hasFileTokens ? "{file:}" : undefined
|
|
166469
|
+
].filter(Boolean).join(" and ");
|
|
166470
|
+
warnings.push(`Project-level config no longer supports ${tokenTypes} tokens for security reasons; leaving tokens literal. Move secret expansion to user-level config.`);
|
|
166471
|
+
}
|
|
166472
|
+
return { text, warnings };
|
|
166473
|
+
}
|
|
166474
|
+
text = stripJsonComments(text);
|
|
166425
166475
|
text = text.replace(ENV_PATTERN, (_, rawName) => {
|
|
166426
166476
|
const varName = rawName.trim();
|
|
166427
166477
|
const value = varName ? process.env[varName] : undefined;
|
|
@@ -166429,7 +166479,7 @@ function substituteConfigVariables(input) {
|
|
|
166429
166479
|
warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
|
|
166430
166480
|
return "";
|
|
166431
166481
|
}
|
|
166432
|
-
return value;
|
|
166482
|
+
return JSON.stringify(value).slice(1, -1);
|
|
166433
166483
|
});
|
|
166434
166484
|
const fileMatches = Array.from(text.matchAll(FILE_PATTERN));
|
|
166435
166485
|
if (fileMatches.length === 0) {
|
|
@@ -166484,13 +166534,17 @@ function getUserConfigBasePath() {
|
|
|
166484
166534
|
function getProjectConfigBasePath(directory) {
|
|
166485
166535
|
return join(directory, ".opencode", CONFIG_FILE_BASENAME);
|
|
166486
166536
|
}
|
|
166487
|
-
function loadConfigFile(configPath) {
|
|
166537
|
+
function loadConfigFile(configPath, isProjectConfig = false) {
|
|
166488
166538
|
try {
|
|
166489
166539
|
if (!existsSync3(configPath)) {
|
|
166490
166540
|
return null;
|
|
166491
166541
|
}
|
|
166492
166542
|
const rawText = readFileSync3(configPath, "utf-8");
|
|
166493
|
-
const substituted = substituteConfigVariables({
|
|
166543
|
+
const substituted = substituteConfigVariables({
|
|
166544
|
+
text: rawText,
|
|
166545
|
+
configPath,
|
|
166546
|
+
isProjectConfig
|
|
166547
|
+
});
|
|
166494
166548
|
return {
|
|
166495
166549
|
config: parseJsonc(substituted.text),
|
|
166496
166550
|
warnings: substituted.warnings.map((w) => `${configPath}: ${w}`)
|
|
@@ -166518,7 +166572,11 @@ function loadConfigFileDetailed(configPath, source) {
|
|
|
166518
166572
|
};
|
|
166519
166573
|
}
|
|
166520
166574
|
try {
|
|
166521
|
-
const substituted = substituteConfigVariables({
|
|
166575
|
+
const substituted = substituteConfigVariables({
|
|
166576
|
+
text: rawText,
|
|
166577
|
+
configPath,
|
|
166578
|
+
isProjectConfig: source === "project"
|
|
166579
|
+
});
|
|
166522
166580
|
return {
|
|
166523
166581
|
config: parseJsonc(substituted.text),
|
|
166524
166582
|
warnings: substituted.warnings.map((w) => `${configPath}: ${w}`),
|
|
@@ -166690,7 +166748,7 @@ function loadPluginConfig(directory) {
|
|
|
166690
166748
|
const dotOpenCodeDetected = detectConfigFile(getProjectConfigBasePath(directory));
|
|
166691
166749
|
const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
|
|
166692
166750
|
const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
|
|
166693
|
-
const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
|
|
166751
|
+
const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path, true);
|
|
166694
166752
|
const allWarnings = [];
|
|
166695
166753
|
let mergedRaw = {};
|
|
166696
166754
|
if (userLoaded) {
|
|
@@ -168219,6 +168277,43 @@ function normalizeProjectRelativePath(projectPath, filePath) {
|
|
|
168219
168277
|
return null;
|
|
168220
168278
|
return rel;
|
|
168221
168279
|
}
|
|
168280
|
+
function isDocumentationOrMetaFile(rel) {
|
|
168281
|
+
const lower = rel.toLowerCase();
|
|
168282
|
+
if (lower.endsWith(".md") || lower.endsWith(".mdx") || lower.endsWith(".rst"))
|
|
168283
|
+
return true;
|
|
168284
|
+
if (lower.endsWith(".txt"))
|
|
168285
|
+
return true;
|
|
168286
|
+
const base = lower.split("/").pop() ?? "";
|
|
168287
|
+
const baseNoExt = base.replace(/\.[^.]+$/, "");
|
|
168288
|
+
const META_BASENAMES = new Set([
|
|
168289
|
+
"license",
|
|
168290
|
+
"licence",
|
|
168291
|
+
"notice",
|
|
168292
|
+
"copying",
|
|
168293
|
+
"authors",
|
|
168294
|
+
"contributors",
|
|
168295
|
+
"changelog",
|
|
168296
|
+
"changes",
|
|
168297
|
+
"history",
|
|
168298
|
+
"readme",
|
|
168299
|
+
"contributing",
|
|
168300
|
+
"code_of_conduct",
|
|
168301
|
+
"security",
|
|
168302
|
+
"support",
|
|
168303
|
+
"maintainers",
|
|
168304
|
+
"governance",
|
|
168305
|
+
"package-lock",
|
|
168306
|
+
"bun.lock",
|
|
168307
|
+
"bun.lockb",
|
|
168308
|
+
"yarn.lock",
|
|
168309
|
+
"pnpm-lock",
|
|
168310
|
+
"cargo.lock",
|
|
168311
|
+
"uv.lock",
|
|
168312
|
+
"poetry.lock",
|
|
168313
|
+
"gemfile.lock"
|
|
168314
|
+
]);
|
|
168315
|
+
return META_BASENAMES.has(base) || META_BASENAMES.has(baseNoExt);
|
|
168316
|
+
}
|
|
168222
168317
|
function primarySessionIds(db) {
|
|
168223
168318
|
try {
|
|
168224
168319
|
const rows = db.prepare("SELECT session_id AS sessionId FROM session_meta WHERE is_subagent = 0").all();
|
|
@@ -168250,6 +168345,8 @@ function collectKeyFileCandidates(args) {
|
|
|
168250
168345
|
const rel = normalizeProjectRelativePath(args.projectPath, row.file_path);
|
|
168251
168346
|
if (!rel)
|
|
168252
168347
|
continue;
|
|
168348
|
+
if (isDocumentationOrMetaFile(rel))
|
|
168349
|
+
continue;
|
|
168253
168350
|
const timestamp = toMs(row.time_created);
|
|
168254
168351
|
const candidate = byPath.get(rel) ?? {
|
|
168255
168352
|
path: rel,
|
|
@@ -168386,7 +168483,8 @@ Rules:
|
|
|
168386
168483
|
- Total approx_token_estimate must be < ${args.config.token_budget}.
|
|
168387
168484
|
- path must be relative, no .., no absolute paths, no symlinks escaping project root.
|
|
168388
168485
|
- path must be unique case-sensitively and case-insensitively.
|
|
168389
|
-
- content must be plain text; no XML tags inside
|
|
168486
|
+
- content must be plain text; no XML tags inside.
|
|
168487
|
+
- DO NOT pick prose documentation (README.md, CONTRIBUTING.md, CHANGELOG, LICENSE, *.md/*.mdx/*.rst/*.txt) or lockfiles. Key files are project SOURCE the agent needs repeated orientation context on, not reference docs. Project docs are surfaced through a separate injection path.`;
|
|
168390
168488
|
}
|
|
168391
168489
|
function validateLlmOutput(raw, config2, projectPath) {
|
|
168392
168490
|
let obj;
|
|
@@ -169278,6 +169376,13 @@ async function evaluateSmartNotes(args) {
|
|
|
169278
169376
|
For each note below, determine whether its surface condition has been met.
|
|
169279
169377
|
You have access to tools like GitHub CLI (gh), web search, and the local codebase to verify conditions.
|
|
169280
169378
|
|
|
169379
|
+
You DO NOT have access to:
|
|
169380
|
+
- Any conversation between the user and the original agent that wrote the note
|
|
169381
|
+
- The state of any active session, including whether messages have been sent
|
|
169382
|
+
- The current task, mood, or intent of the human user
|
|
169383
|
+
|
|
169384
|
+
If a condition references conversation context the user is having ("When the user mentions X", "When they ask to do Y", "When we revisit Z", "When relevant to current discussion", etc.), it is UNEVALUATABLE — skip it (do not include in results) so the note stays pending. These are misuse cases that should never have been written as smart notes; leaving them pending is the correct outcome, the dreamer's archive-stale task will eventually retire them.
|
|
169385
|
+
|
|
169281
169386
|
## Pending Smart Notes
|
|
169282
169387
|
|
|
169283
169388
|
${noteDescriptions}
|
|
@@ -169286,7 +169391,8 @@ ${noteDescriptions}
|
|
|
169286
169391
|
|
|
169287
169392
|
1. Check each condition using the tools available to you.
|
|
169288
169393
|
2. Be conservative — only mark a condition as met when you have clear evidence.
|
|
169289
|
-
3.
|
|
169394
|
+
3. Skip conditions that depend on session/conversation context you cannot observe — do not invent a "false" verdict for them, just omit them from your response.
|
|
169395
|
+
4. Respond with a JSON array of results:
|
|
169290
169396
|
|
|
169291
169397
|
\`\`\`json
|
|
169292
169398
|
[
|
|
@@ -169294,7 +169400,7 @@ ${noteDescriptions}
|
|
|
169294
169400
|
]
|
|
169295
169401
|
\`\`\`
|
|
169296
169402
|
|
|
169297
|
-
Only include notes whose conditions you could definitively evaluate. Skip notes where you cannot determine the status (they will be re-evaluated next run).`;
|
|
169403
|
+
Only include notes whose conditions you could definitively evaluate against external signals. Skip notes where you cannot determine the status (they will be re-evaluated next run, or eventually archived as stale).`;
|
|
169298
169404
|
const taskStartedAt = Date.now();
|
|
169299
169405
|
let agentSessionId = null;
|
|
169300
169406
|
const abortController = new AbortController;
|
|
@@ -170761,8 +170867,7 @@ var FORCE_MATERIALIZE_PERCENTAGE = 85;
|
|
|
170761
170867
|
function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
|
|
170762
170868
|
return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
|
|
170763
170869
|
}
|
|
170764
|
-
function estimateProjectedPostDropPercentage(db, sessionId, usage, autoDropToolAge, protectedTags, clearReasoningAge, clearedReasoningThroughTag, dropToolStructure = true) {
|
|
170765
|
-
const activeTags = getTagsBySession(db, sessionId).filter((tag) => tag.status === "active");
|
|
170870
|
+
function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, autoDropToolAge, protectedTags, clearReasoningAge, clearedReasoningThroughTag, dropToolStructure = true) {
|
|
170766
170871
|
const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
|
|
170767
170872
|
if (totalActiveBytes === 0)
|
|
170768
170873
|
return null;
|
|
@@ -170849,15 +170954,24 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget) {
|
|
|
170849
170954
|
}
|
|
170850
170955
|
});
|
|
170851
170956
|
}
|
|
170852
|
-
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, autoDropToolAge, protectedTagCount, clearReasoningAge, dropToolStructure = true, commitClusterTrigger) {
|
|
170957
|
+
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, autoDropToolAge, protectedTagCount, clearReasoningAge, dropToolStructure = true, commitClusterTrigger, preloadedActiveTags) {
|
|
170853
170958
|
if (sessionMeta.compartmentInProgress) {
|
|
170959
|
+
sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
|
|
170854
170960
|
return { shouldFire: false };
|
|
170855
170961
|
}
|
|
170856
170962
|
const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget);
|
|
170857
170963
|
if (!tailInfo.hasNewRawHistory) {
|
|
170964
|
+
try {
|
|
170965
|
+
const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
|
|
170966
|
+
const rawMessageCount = getRawSessionMessageCount(sessionId);
|
|
170967
|
+
const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
|
|
170968
|
+
sessionLog(sessionId, `compartment trigger: skipped — no new raw history (usage=${usage.percentage.toFixed(1)}% nextStartOrdinal=${tailInfo.nextStartOrdinal} lastCompartmentEnd=${lastCompartmentEnd} rawMessageCount=${rawMessageCount} protectedTailStart=${protectedTailStart})`);
|
|
170969
|
+
} catch (error51) {
|
|
170970
|
+
sessionLog(sessionId, `compartment trigger: skipped — no new raw history (usage=${usage.percentage.toFixed(1)}% nextStartOrdinal=${tailInfo.nextStartOrdinal} diagnostic-collection-failed: ${error51 instanceof Error ? error51.message : String(error51)})`);
|
|
170971
|
+
}
|
|
170858
170972
|
return { shouldFire: false };
|
|
170859
170973
|
}
|
|
170860
|
-
const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, autoDropToolAge, protectedTagCount, clearReasoningAge, sessionMeta.clearedReasoningThroughTag, dropToolStructure);
|
|
170974
|
+
const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), autoDropToolAge, protectedTagCount, clearReasoningAge, sessionMeta.clearedReasoningThroughTag, dropToolStructure);
|
|
170861
170975
|
const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
|
|
170862
170976
|
if (usage.percentage >= FORCE_COMPARTMENT_PERCENTAGE) {
|
|
170863
170977
|
if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
|
|
@@ -170879,6 +170993,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
170879
170993
|
}
|
|
170880
170994
|
const proactiveTriggerPercentage = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
|
|
170881
170995
|
if (usage.percentage < proactiveTriggerPercentage) {
|
|
170996
|
+
sessionLog(sessionId, `compartment trigger: not firing at ${usage.percentage.toFixed(1)}% — below proactive floor (${proactiveTriggerPercentage}%)`);
|
|
170882
170997
|
return { shouldFire: false };
|
|
170883
170998
|
}
|
|
170884
170999
|
if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
|
|
@@ -171978,6 +172093,8 @@ function stripSystemInjectedMessages(messages, protectedTailStart, providerID) {
|
|
|
171978
172093
|
const msg = messages[i];
|
|
171979
172094
|
if (msg.parts.length === 0)
|
|
171980
172095
|
continue;
|
|
172096
|
+
if (msg.info.role === "user")
|
|
172097
|
+
continue;
|
|
171981
172098
|
if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
|
|
171982
172099
|
continue;
|
|
171983
172100
|
let hasContentPart = false;
|
|
@@ -172435,7 +172552,6 @@ async function runCompartmentPhase(args) {
|
|
|
172435
172552
|
fallbackModelId: args.fallbackModelId,
|
|
172436
172553
|
ensureProjectRegistered: args.ensureProjectRegistered,
|
|
172437
172554
|
getNotificationParams: args.getNotificationParams,
|
|
172438
|
-
experimentalCompactionMarkers: args.experimentalCompactionMarkers,
|
|
172439
172555
|
experimentalUserMemories: args.experimentalUserMemories,
|
|
172440
172556
|
historianTwoPass: args.historianTwoPass,
|
|
172441
172557
|
compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
|
|
@@ -172465,7 +172581,6 @@ async function runCompartmentPhase(args) {
|
|
|
172465
172581
|
fallbackModelId: args.fallbackModelId,
|
|
172466
172582
|
ensureProjectRegistered: args.ensureProjectRegistered,
|
|
172467
172583
|
getNotificationParams: args.getNotificationParams,
|
|
172468
|
-
experimentalCompactionMarkers: args.experimentalCompactionMarkers,
|
|
172469
172584
|
experimentalUserMemories: args.experimentalUserMemories,
|
|
172470
172585
|
historianTwoPass: args.historianTwoPass,
|
|
172471
172586
|
compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
|
|
@@ -173818,7 +173933,7 @@ function stripNestedSystemReminders(text) {
|
|
|
173818
173933
|
return result;
|
|
173819
173934
|
}
|
|
173820
173935
|
function extractUserPromptText(message) {
|
|
173821
|
-
return stripNestedSystemReminders(collectUserPromptParts(message)).replace(
|
|
173936
|
+
return stripNestedSystemReminders(collectUserPromptParts(message)).replace(/<!--[\s\S]*?-->/g, "").replace(/<\/?[a-zA-Z][^<>]*>/g, "").replace(/§\d+§\s*/g, "").replace(/[ \t]+\n/g, `
|
|
173822
173937
|
`).replace(/\n{3,}/g, `
|
|
173823
173938
|
|
|
173824
173939
|
`).trim();
|
|
@@ -174378,12 +174493,6 @@ async function runPostTransformPhase(args) {
|
|
|
174378
174493
|
didMutateFromPendingOperations = true;
|
|
174379
174494
|
}
|
|
174380
174495
|
logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags}`);
|
|
174381
|
-
if (args.watermark > 0) {
|
|
174382
|
-
const t6 = performance.now();
|
|
174383
|
-
truncateErroredTools(args.messages, args.watermark, args.messageTagNumbers);
|
|
174384
|
-
stripProcessedImages(args.messages, args.watermark, args.messageTagNumbers);
|
|
174385
|
-
logTransformTiming(args.sessionId, "watermarkCleanup", t6);
|
|
174386
|
-
}
|
|
174387
174496
|
const t7 = performance.now();
|
|
174388
174497
|
const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
|
|
174389
174498
|
stripClearedReasoning(args.messages);
|
|
@@ -174427,6 +174536,12 @@ async function runPostTransformPhase(args) {
|
|
|
174427
174536
|
deferredMaterializedSuccessfully = true;
|
|
174428
174537
|
heuristicsRanSuccessfully = true;
|
|
174429
174538
|
}
|
|
174539
|
+
if (args.watermark > 0) {
|
|
174540
|
+
const tWatermarkCleanup = performance.now();
|
|
174541
|
+
truncateErroredTools(args.messages, args.watermark, args.messageTagNumbers);
|
|
174542
|
+
stripProcessedImages(args.messages, args.watermark, args.messageTagNumbers);
|
|
174543
|
+
logTransformTiming(args.sessionId, "watermarkCleanup", tWatermarkCleanup);
|
|
174544
|
+
}
|
|
174430
174545
|
if (shouldApplyPendingOps) {
|
|
174431
174546
|
pendingOpsRanSuccessfully = true;
|
|
174432
174547
|
}
|
|
@@ -174927,7 +175042,6 @@ function createTransform(deps) {
|
|
|
174927
175042
|
directory: compartmentDirectory,
|
|
174928
175043
|
fallbackModelId,
|
|
174929
175044
|
getNotificationParams: () => notificationParams,
|
|
174930
|
-
experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
|
|
174931
175045
|
experimentalUserMemories: deps.experimentalUserMemories,
|
|
174932
175046
|
experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
|
|
174933
175047
|
historianTwoPass: deps.historianTwoPass,
|
|
@@ -175111,7 +175225,6 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
|
|
|
175111
175225
|
suppressBackgroundCompressionThisPass: historyBustThisPass,
|
|
175112
175226
|
deferredHistoryRefreshSessions,
|
|
175113
175227
|
skipAwaitForThisPass: skipCompartmentAwaitForThisPass,
|
|
175114
|
-
experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
|
|
175115
175228
|
experimentalUserMemories: deps.experimentalUserMemories,
|
|
175116
175229
|
experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
|
|
175117
175230
|
historianTwoPass: deps.historianTwoPass,
|
|
@@ -175852,7 +175965,8 @@ function applyStickySnapshotCache(sessionId, fresh) {
|
|
|
175852
175965
|
cache.delete(sessionId);
|
|
175853
175966
|
return fresh;
|
|
175854
175967
|
}
|
|
175855
|
-
|
|
175968
|
+
const stateSurvived = fresh.compartmentCount >= cached2.snapshot.compartmentCount && fresh.memoryCount >= cached2.snapshot.memoryCount;
|
|
175969
|
+
if (!hasInFlightEvidence(fresh) && !stateSurvived) {
|
|
175856
175970
|
cache.delete(sessionId);
|
|
175857
175971
|
return fresh;
|
|
175858
175972
|
}
|
|
@@ -176493,12 +176607,10 @@ function createMagicContextHook(deps) {
|
|
|
176493
176607
|
}
|
|
176494
176608
|
const projectPath = resolveProjectIdentity(deps.directory);
|
|
176495
176609
|
registerDreamProjectDirectory(projectPath, deps.directory);
|
|
176496
|
-
|
|
176497
|
-
|
|
176498
|
-
|
|
176499
|
-
|
|
176500
|
-
log("[magic-context] startup compaction-marker consistency check failed:", error51);
|
|
176501
|
-
}
|
|
176610
|
+
try {
|
|
176611
|
+
checkCompactionMarkerConsistency(db);
|
|
176612
|
+
} catch (error51) {
|
|
176613
|
+
log("[magic-context] startup compaction-marker consistency check failed:", error51);
|
|
176502
176614
|
}
|
|
176503
176615
|
let lastScheduleCheckMs = 0;
|
|
176504
176616
|
const getHistorianChunkTokens = () => deriveHistorianChunkTokens(resolveHistorianContextLimit(deps.config.historian?.model));
|
|
@@ -176589,7 +176701,6 @@ function createMagicContextHook(deps) {
|
|
|
176589
176701
|
return model ? `${model.providerID}/${model.modelID}` : undefined;
|
|
176590
176702
|
},
|
|
176591
176703
|
projectPath,
|
|
176592
|
-
experimentalCompactionMarkers: deps.config.compaction_markers,
|
|
176593
176704
|
experimentalUserMemories: deps.config.dreamer?.user_memories?.enabled,
|
|
176594
176705
|
experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true,
|
|
176595
176706
|
historianTwoPass: deps.config.historian?.two_pass === true,
|
|
@@ -176854,7 +176965,6 @@ function createSessionHooks(args) {
|
|
|
176854
176965
|
sidekick: pluginConfig.sidekick,
|
|
176855
176966
|
dreamer: pluginConfig.dreamer,
|
|
176856
176967
|
commit_cluster_trigger: pluginConfig.commit_cluster_trigger,
|
|
176857
|
-
compaction_markers: pluginConfig.compaction_markers,
|
|
176858
176968
|
system_prompt_injection: pluginConfig.system_prompt_injection,
|
|
176859
176969
|
compressor: pluginConfig.compressor,
|
|
176860
176970
|
experimental: pluginConfig.experimental
|
|
@@ -177885,9 +177995,26 @@ Actions:
|
|
|
177885
177995
|
- \`dismiss\`: Dismiss a note by \`note_id\`.
|
|
177886
177996
|
- \`update\`: Update a note by \`note_id\`.
|
|
177887
177997
|
|
|
177888
|
-
**Smart Notes**: When \`surface_condition\` is provided with \`write\`, the note becomes a project-scoped smart note.
|
|
177889
|
-
|
|
177890
|
-
|
|
177998
|
+
**Smart Notes**: When \`surface_condition\` is provided with \`write\`, the note becomes a project-scoped smart note. A separate background process (the dreamer) periodically checks the condition using ONLY external, verifiable signals: GitHub state via \`gh\` CLI, web pages, files on disk, git history, etc. The dreamer cannot read your current conversation, cannot detect when the user says something, and has no memory of context that lives only in this session.
|
|
177999
|
+
|
|
178000
|
+
Write a smart note ONLY when the surface_condition is something an external agent with read-only tools can definitively check:
|
|
178001
|
+
|
|
178002
|
+
✓ GOOD conditions (externally verifiable):
|
|
178003
|
+
- "When PR #42 in cortexkit/magic-context is merged"
|
|
178004
|
+
- "When the file packages/plugin/src/foo.ts contains a function named bar"
|
|
178005
|
+
- "When the latest release tag is >= v0.22.0"
|
|
178006
|
+
- "When the GitHub Actions workflow runs/123 succeeds"
|
|
178007
|
+
|
|
178008
|
+
✗ BAD conditions (require knowing this session's context):
|
|
178009
|
+
- "When the user mentions the worktree system has landed" → dreamer cannot see user messages
|
|
178010
|
+
- "When they ask to re-run the audit fixes" → dreamer cannot see future requests
|
|
178011
|
+
- "When we revisit this code path" → no observable signal
|
|
178012
|
+
- "When relevant to the current discussion" → no observable signal
|
|
178013
|
+
- "After we finish the current refactor" → no externally checkable boundary
|
|
178014
|
+
|
|
178015
|
+
If you want context that surfaces based on what's happening in your session, use a regular note (omit surface_condition) — those show up on natural work boundaries within this session. If you want a reminder tied to your future work without a clean external trigger, just write a regular note describing what to do; you'll see it when you read notes later.
|
|
178016
|
+
|
|
178017
|
+
Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 in cortexkit/magic-context is merged")\`
|
|
177891
178018
|
|
|
177892
178019
|
Historian reads these notes, deduplicates them, and rewrites the remaining useful notes over time.`;
|
|
177893
178020
|
// src/tools/ctx-note/tools.ts
|
|
@@ -177965,7 +178092,7 @@ function createCtxNoteTool(deps) {
|
|
|
177965
178092
|
args: {
|
|
177966
178093
|
action: tool3.schema.enum(["write", "read", "dismiss", "update"]).optional().describe("Operation to perform. Defaults to 'write' when content is provided, otherwise 'read'."),
|
|
177967
178094
|
content: tool3.schema.string().optional().describe("Note text to store when action is 'write'."),
|
|
177968
|
-
surface_condition: tool3.schema.string().optional().describe("
|
|
178095
|
+
surface_condition: tool3.schema.string().optional().describe("Externally verifiable condition for smart notes. A separate background agent (dreamer) checks this using gh CLI, web fetches, file reads, git, etc. — NOT your conversation history. Use only for things like GitHub PR/issue state, release tags, file contents, or workflow runs. DO NOT use for 'when the user mentions X' / 'when we revisit Y' / 'when relevant to current task' — dreamer has no access to session context. For session-relative reminders, omit this and write a regular note."),
|
|
177969
178096
|
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."),
|
|
177970
178097
|
note_id: tool3.schema.number().optional().describe("Note ID (required for 'dismiss' and 'update' actions).")
|
|
177971
178098
|
},
|
|
@@ -178003,8 +178130,14 @@ function createCtxNoteTool(deps) {
|
|
|
178003
178130
|
if (typeof noteId !== "number") {
|
|
178004
178131
|
return "Error: 'note_id' is required when action is 'dismiss'.";
|
|
178005
178132
|
}
|
|
178006
|
-
|
|
178007
|
-
|
|
178133
|
+
if (!projectIdentity) {
|
|
178134
|
+
return "Error: Could not resolve project identity for note dismiss.";
|
|
178135
|
+
}
|
|
178136
|
+
const dismissed = dismissNote(deps.db, noteId, {
|
|
178137
|
+
projectPath: projectIdentity,
|
|
178138
|
+
sessionId
|
|
178139
|
+
});
|
|
178140
|
+
return dismissed ? `Note #${noteId} dismissed.` : `Error: Note #${noteId} not found in your session/project or already dismissed.`;
|
|
178008
178141
|
}
|
|
178009
178142
|
if (action === "update") {
|
|
178010
178143
|
const noteId = args.note_id;
|
|
@@ -178019,9 +178152,15 @@ function createCtxNoteTool(deps) {
|
|
|
178019
178152
|
if (!updates.content && !updates.surfaceCondition) {
|
|
178020
178153
|
return "Error: Provide 'content' and/or 'surface_condition' to update.";
|
|
178021
178154
|
}
|
|
178022
|
-
|
|
178155
|
+
if (!projectIdentity) {
|
|
178156
|
+
return "Error: Could not resolve project identity for note update.";
|
|
178157
|
+
}
|
|
178158
|
+
const updated = updateNote(deps.db, noteId, updates, {
|
|
178159
|
+
projectPath: projectIdentity,
|
|
178160
|
+
sessionId
|
|
178161
|
+
});
|
|
178023
178162
|
if (!updated) {
|
|
178024
|
-
return `Note #${noteId} not found or has no compatible fields to update.`;
|
|
178163
|
+
return `Error: Note #${noteId} not found in your session/project or has no compatible fields to update.`;
|
|
178025
178164
|
}
|
|
178026
178165
|
const parts = [];
|
|
178027
178166
|
if (updates.content)
|