@loreai/core 0.10.2 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun/config.d.ts +8 -0
- package/dist/bun/config.d.ts.map +1 -1
- package/dist/bun/db.d.ts.map +1 -1
- package/dist/bun/distillation.d.ts +74 -2
- package/dist/bun/distillation.d.ts.map +1 -1
- package/dist/bun/embedding.d.ts.map +1 -1
- package/dist/bun/gradient.d.ts +72 -0
- package/dist/bun/gradient.d.ts.map +1 -1
- package/dist/bun/index.d.ts +4 -2
- package/dist/bun/index.d.ts.map +1 -1
- package/dist/bun/index.js +554 -76
- package/dist/bun/index.js.map +4 -4
- package/dist/bun/prompt.d.ts +8 -2
- package/dist/bun/prompt.d.ts.map +1 -1
- package/dist/bun/temporal.d.ts +31 -0
- package/dist/bun/temporal.d.ts.map +1 -1
- package/dist/bun/types.d.ts +9 -0
- package/dist/bun/types.d.ts.map +1 -1
- package/dist/bun/worker-model.d.ts +90 -0
- package/dist/bun/worker-model.d.ts.map +1 -0
- package/dist/node/config.d.ts +8 -0
- package/dist/node/config.d.ts.map +1 -1
- package/dist/node/db.d.ts.map +1 -1
- package/dist/node/distillation.d.ts +74 -2
- package/dist/node/distillation.d.ts.map +1 -1
- package/dist/node/embedding.d.ts.map +1 -1
- package/dist/node/gradient.d.ts +72 -0
- package/dist/node/gradient.d.ts.map +1 -1
- package/dist/node/index.d.ts +4 -2
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +554 -76
- package/dist/node/index.js.map +4 -4
- package/dist/node/prompt.d.ts +8 -2
- package/dist/node/prompt.d.ts.map +1 -1
- package/dist/node/temporal.d.ts +31 -0
- package/dist/node/temporal.d.ts.map +1 -1
- package/dist/node/types.d.ts +9 -0
- package/dist/node/types.d.ts.map +1 -1
- package/dist/node/worker-model.d.ts +90 -0
- package/dist/node/worker-model.d.ts.map +1 -0
- package/dist/types/config.d.ts +8 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/db.d.ts.map +1 -1
- package/dist/types/distillation.d.ts +74 -2
- package/dist/types/distillation.d.ts.map +1 -1
- package/dist/types/embedding.d.ts.map +1 -1
- package/dist/types/gradient.d.ts +72 -0
- package/dist/types/gradient.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/prompt.d.ts +8 -2
- package/dist/types/prompt.d.ts.map +1 -1
- package/dist/types/temporal.d.ts +31 -0
- package/dist/types/temporal.d.ts.map +1 -1
- package/dist/types/types.d.ts +9 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/worker-model.d.ts +90 -0
- package/dist/types/worker-model.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/config.ts +53 -6
- package/src/db.ts +68 -6
- package/src/distillation.ts +225 -28
- package/src/embedding.ts +7 -0
- package/src/gradient.ts +305 -17
- package/src/index.ts +16 -0
- package/src/lat-reader.ts +4 -4
- package/src/ltm.ts +17 -17
- package/src/prompt.ts +101 -0
- package/src/recall.ts +4 -4
- package/src/temporal.ts +41 -10
- package/src/types.ts +9 -0
- package/src/worker-model.ts +264 -0
package/dist/node/index.js
CHANGED
|
@@ -122,9 +122,11 @@ var require_extend = __commonJS({
|
|
|
122
122
|
// src/temporal.ts
|
|
123
123
|
var temporal_exports = {};
|
|
124
124
|
__export(temporal_exports, {
|
|
125
|
+
CHUNK_TERMINATOR: () => CHUNK_TERMINATOR,
|
|
125
126
|
bySession: () => bySession,
|
|
126
127
|
count: () => count,
|
|
127
128
|
markDistilled: () => markDistilled,
|
|
129
|
+
partsToText: () => partsToText,
|
|
128
130
|
prune: () => prune,
|
|
129
131
|
search: () => search2,
|
|
130
132
|
searchScored: () => searchScored,
|
|
@@ -436,6 +438,58 @@ var MIGRATIONS = [
|
|
|
436
438
|
to_id TEXT NOT NULL REFERENCES knowledge(id) ON DELETE CASCADE,
|
|
437
439
|
PRIMARY KEY (from_id, to_id)
|
|
438
440
|
);
|
|
441
|
+
`,
|
|
442
|
+
`
|
|
443
|
+
-- Version 11: F3b -- unambiguous chunk terminator in temporal_messages.content.
|
|
444
|
+
--
|
|
445
|
+
-- Pre-F3b, partsToText joined chunks with a newline. Tool-output payloads
|
|
446
|
+
-- can contain newlines too, so the boundary between a tool envelope and a
|
|
447
|
+
-- following plain-text or [reasoning] chunk was structurally ambiguous.
|
|
448
|
+
-- This caused two known limitations in the F3 distill-input truncator:
|
|
449
|
+
-- trailing text could be swallowed into a tool payload, and embedded
|
|
450
|
+
-- literal envelope strings inside a payload (e.g. when reading AGENTS.md)
|
|
451
|
+
-- could fabricate fake boundaries.
|
|
452
|
+
--
|
|
453
|
+
-- F3b switches the chunk separator to newline plus ASCII Unit Separator
|
|
454
|
+
-- (char 31). The Unit Separator is non-word so FTS5's unicode61 tokenizer
|
|
455
|
+
-- ignores it (zero BM25 impact). New rows are written via the post-F3b
|
|
456
|
+
-- partsToText. Existing rows are rewritten in place by the UPDATE below,
|
|
457
|
+
-- which uses pure SQL replace() to inject the Unit Separator after every
|
|
458
|
+
-- legacy chunk-prefix sequence -- the same boundary patterns the legacy
|
|
459
|
+
-- F3 reader was already trying to recover.
|
|
460
|
+
--
|
|
461
|
+
-- Trade-off (acceptable): any embedded legacy chunk-prefix sequence
|
|
462
|
+
-- inside a tool payload becomes a structural boundary post-migration.
|
|
463
|
+
-- This matches what the legacy F3 reader did at read-time anyway, baked
|
|
464
|
+
-- into the row permanently. The migration runs once per machine.
|
|
465
|
+
--
|
|
466
|
+
-- Idempotent: a row that already contains the Unit Separator before a
|
|
467
|
+
-- chunk prefix no longer matches the search literal (the separator
|
|
468
|
+
-- interposes), so re-running the UPDATE is a no-op for migrated rows.
|
|
469
|
+
-- (Important: migrate() in db.ts runs each migration via database.exec()
|
|
470
|
+
-- with no explicit BEGIN/COMMIT around the whole loop. SQLite makes this
|
|
471
|
+
-- single UPDATE statement atomic per-statement, so partial progress on
|
|
472
|
+
-- crash is safe to retry thanks to the idempotency above.)
|
|
473
|
+
--
|
|
474
|
+
-- char(10) = newline, char(31) = Unit Separator. SQLite has no native
|
|
475
|
+
-- regex, but two nested replace() calls on the literal prefixes are
|
|
476
|
+
-- sufficient because both legacy chunk prefixes match at line-start.
|
|
477
|
+
--
|
|
478
|
+
-- Each row UPDATE fires the temporal_fts_update trigger once; because
|
|
479
|
+
-- the Unit Separator is a non-word character, the re-indexed content
|
|
480
|
+
-- tokenizes identically -- net no-op for FTS scoring.
|
|
481
|
+
UPDATE temporal_messages
|
|
482
|
+
SET content = replace(
|
|
483
|
+
replace(
|
|
484
|
+
content,
|
|
485
|
+
char(10) || '[tool:',
|
|
486
|
+
char(10) || char(31) || '[tool:'
|
|
487
|
+
),
|
|
488
|
+
char(10) || '[reasoning] ',
|
|
489
|
+
char(10) || char(31) || '[reasoning] '
|
|
490
|
+
)
|
|
491
|
+
WHERE content LIKE '%' || char(10) || '[tool:%'
|
|
492
|
+
OR content LIKE '%' || char(10) || '[reasoning] %';
|
|
439
493
|
`
|
|
440
494
|
];
|
|
441
495
|
function dataDir() {
|
|
@@ -456,11 +510,13 @@ function db() {
|
|
|
456
510
|
mkdirSync(dir, { recursive: true });
|
|
457
511
|
path = join(dir, "lore.db");
|
|
458
512
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
513
|
+
const database = new Database(path);
|
|
514
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
515
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
516
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
517
|
+
database.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
518
|
+
migrate(database);
|
|
519
|
+
instance = database;
|
|
464
520
|
return instance;
|
|
465
521
|
}
|
|
466
522
|
var VACUUM_MIGRATION_INDEX = 2;
|
|
@@ -10765,12 +10821,27 @@ EXACT NUMBERS: When two segments report different numbers for what seems like th
|
|
|
10765
10821
|
|
|
10766
10822
|
EARLY-SESSION CONTENT: Bug fixes, code changes, and decisions from the start of a session are just as important as later work. Never drop them just because the segment is short or old. If the first segment contains a specific bug fix with file paths and root cause, it MUST survive into the reflection.
|
|
10767
10823
|
|
|
10824
|
+
ANCHORED UPDATES: If the prompt includes a <previous-meta-summary> block, treat it as the current consolidated state. Update it using the NEW observation segments \u2014 preserve still-true details, remove stale details, and merge in new facts. Keep the same section headings. Do NOT re-derive unchanged sections verbatim unless the new segments contradict them.
|
|
10825
|
+
|
|
10768
10826
|
Output ONLY an <observations> block with the consolidated observations.`;
|
|
10769
|
-
function recursiveUser(distillations) {
|
|
10827
|
+
function recursiveUser(distillations, previousMeta) {
|
|
10770
10828
|
const entries = distillations.map(
|
|
10771
10829
|
(d, i) => `Segment ${i + 1}:
|
|
10772
10830
|
${d.observations}`
|
|
10773
10831
|
);
|
|
10832
|
+
if (previousMeta) {
|
|
10833
|
+
return `Update the anchored meta-summary below using the NEW observation segments. Preserve still-true details, remove stale details, and merge in new facts. Keep the same section headings.
|
|
10834
|
+
|
|
10835
|
+
<previous-meta-summary>
|
|
10836
|
+
${previousMeta}
|
|
10837
|
+
</previous-meta-summary>
|
|
10838
|
+
|
|
10839
|
+
---
|
|
10840
|
+
|
|
10841
|
+
New observation segments to merge (chronological order):
|
|
10842
|
+
|
|
10843
|
+
${entries.join("\n\n---\n\n")}`;
|
|
10844
|
+
}
|
|
10774
10845
|
return `Observation segments to consolidate (chronological order):
|
|
10775
10846
|
|
|
10776
10847
|
${entries.join("\n\n---\n\n")}`;
|
|
@@ -10925,6 +10996,61 @@ function formatDistillations(distillations) {
|
|
|
10925
10996
|
}
|
|
10926
10997
|
return sections.join("\n\n");
|
|
10927
10998
|
}
|
|
10999
|
+
var COMPACT_SUMMARY_TEMPLATE = `Output exactly this Markdown structure. Keep every section in this order, even when empty (use "(none)").
|
|
11000
|
+
|
|
11001
|
+
---
|
|
11002
|
+
## Goal
|
|
11003
|
+
- [single-sentence task summary]
|
|
11004
|
+
|
|
11005
|
+
## Constraints & Preferences
|
|
11006
|
+
- [user constraints, preferences, specs, or "(none)"]
|
|
11007
|
+
|
|
11008
|
+
## Progress
|
|
11009
|
+
### Done
|
|
11010
|
+
- [completed work or "(none)"]
|
|
11011
|
+
|
|
11012
|
+
### In Progress
|
|
11013
|
+
- [current work or "(none)"]
|
|
11014
|
+
|
|
11015
|
+
### Blocked
|
|
11016
|
+
- [blockers or "(none)"]
|
|
11017
|
+
|
|
11018
|
+
## Key Decisions
|
|
11019
|
+
- [decision and why, or "(none)"]
|
|
11020
|
+
|
|
11021
|
+
## Next Steps
|
|
11022
|
+
- [ordered next actions or "(none)"]
|
|
11023
|
+
|
|
11024
|
+
## Critical Context
|
|
11025
|
+
- [important technical facts, errors, open questions, or "(none)"]
|
|
11026
|
+
|
|
11027
|
+
## Relevant Files
|
|
11028
|
+
- [file or directory path: why it matters, or "(none)"]
|
|
11029
|
+
---
|
|
11030
|
+
|
|
11031
|
+
Rules:
|
|
11032
|
+
- Keep every section, even when empty.
|
|
11033
|
+
- Use terse bullets, not prose paragraphs.
|
|
11034
|
+
- Preserve exact file paths, commands, error strings, and identifiers when known.
|
|
11035
|
+
- Do not mention the summary process or that context was compacted.
|
|
11036
|
+
- End with "I'm ready to continue." on its own line after the closing "---".`;
|
|
11037
|
+
function buildCompactPrompt(input) {
|
|
11038
|
+
const distillSection = input.hasDistillations ? "Lore has pre-computed chunked summaries of the session history (injected above as context). Use them as the authoritative source \u2014 do NOT re-read raw conversation messages that conflict with them.\n\n" : "";
|
|
11039
|
+
const anchorBlock = input.previousSummary ? `A prior compacted summary exists for this session. Update it using the conversation history above: preserve still-true details, remove stale details, and merge in new facts. Keep every section in place.
|
|
11040
|
+
|
|
11041
|
+
<previous-summary>
|
|
11042
|
+
${input.previousSummary}
|
|
11043
|
+
</previous-summary>
|
|
11044
|
+
|
|
11045
|
+
` : "";
|
|
11046
|
+
const knowledgeBlock = input.knowledge ? `
|
|
11047
|
+
${input.knowledge}
|
|
11048
|
+
` : "";
|
|
11049
|
+
return `You are producing a compacted session summary for an AI coding agent. This summary will be the ONLY context available in the next part of the conversation.
|
|
11050
|
+
|
|
11051
|
+
${distillSection}${anchorBlock}${COMPACT_SUMMARY_TEMPLATE}
|
|
11052
|
+
${knowledgeBlock}`;
|
|
11053
|
+
}
|
|
10928
11054
|
function estimateTokens(text4) {
|
|
10929
11055
|
return Math.ceil(text4.length / 3);
|
|
10930
11056
|
}
|
|
@@ -11177,6 +11303,7 @@ function isToolPart(p3) {
|
|
|
11177
11303
|
function estimate(text4) {
|
|
11178
11304
|
return Math.ceil(text4.length / 3);
|
|
11179
11305
|
}
|
|
11306
|
+
var CHUNK_TERMINATOR = "";
|
|
11180
11307
|
function partsToText(parts) {
|
|
11181
11308
|
const chunks = [];
|
|
11182
11309
|
for (const part of parts) {
|
|
@@ -11186,7 +11313,7 @@ function partsToText(parts) {
|
|
|
11186
11313
|
else if (isToolPart(part) && part.state.status === "completed")
|
|
11187
11314
|
chunks.push(`[tool:${part.tool}] ${part.state.output}`);
|
|
11188
11315
|
}
|
|
11189
|
-
return sanitizeSurrogates(chunks.join("\n"));
|
|
11316
|
+
return sanitizeSurrogates(chunks.join("\n" + CHUNK_TERMINATOR));
|
|
11190
11317
|
}
|
|
11191
11318
|
function messageMetadata(info2, parts) {
|
|
11192
11319
|
const meta3 = {};
|
|
@@ -11265,11 +11392,11 @@ function search2(input) {
|
|
|
11265
11392
|
const limit = input.limit ?? 20;
|
|
11266
11393
|
const q = ftsQuery(input.query);
|
|
11267
11394
|
if (q === EMPTY_QUERY) return [];
|
|
11268
|
-
const ftsSQL = input.sessionID ? `SELECT m.* FROM
|
|
11269
|
-
JOIN
|
|
11395
|
+
const ftsSQL = input.sessionID ? `SELECT m.* FROM temporal_fts f
|
|
11396
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11270
11397
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11271
|
-
ORDER BY rank LIMIT ?` : `SELECT m.* FROM
|
|
11272
|
-
JOIN
|
|
11398
|
+
ORDER BY rank LIMIT ?` : `SELECT m.* FROM temporal_fts f
|
|
11399
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11273
11400
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11274
11401
|
ORDER BY rank LIMIT ?`;
|
|
11275
11402
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -11294,11 +11421,11 @@ function searchScored(input) {
|
|
|
11294
11421
|
const limit = input.limit ?? 20;
|
|
11295
11422
|
const q = ftsQuery(input.query);
|
|
11296
11423
|
if (q === EMPTY_QUERY) return [];
|
|
11297
|
-
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM
|
|
11298
|
-
JOIN
|
|
11424
|
+
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM temporal_fts f
|
|
11425
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11299
11426
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11300
|
-
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM
|
|
11301
|
-
JOIN
|
|
11427
|
+
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM temporal_fts f
|
|
11428
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11302
11429
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11303
11430
|
ORDER BY rank LIMIT ?`;
|
|
11304
11431
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -25500,18 +25627,63 @@ var LoreConfig = external_exports.object({
|
|
|
25500
25627
|
providerID: external_exports.string(),
|
|
25501
25628
|
modelID: external_exports.string()
|
|
25502
25629
|
}).optional(),
|
|
25630
|
+
/** Explicit worker model override. When set, all background workers (distillation,
|
|
25631
|
+
* curation, query expansion) use this model instead of the session model or the
|
|
25632
|
+
* auto-selected worker model. Bypasses dynamic worker model selection entirely. */
|
|
25633
|
+
workerModel: external_exports.object({
|
|
25634
|
+
providerID: external_exports.string(),
|
|
25635
|
+
modelID: external_exports.string()
|
|
25636
|
+
}).optional(),
|
|
25503
25637
|
budget: external_exports.object({
|
|
25504
25638
|
distilled: external_exports.number().min(0.05).max(0.5).default(0.25),
|
|
25505
25639
|
raw: external_exports.number().min(0.1).max(0.7).default(0.4),
|
|
25506
25640
|
output: external_exports.number().min(0.1).max(0.5).default(0.25),
|
|
25507
|
-
/** Max fraction of usable context reserved for LTM system-prompt injection. Default: 0.
|
|
25508
|
-
ltm: external_exports.number().min(0.02).max(0.3).default(0.
|
|
25509
|
-
|
|
25641
|
+
/** Max fraction of usable context reserved for LTM system-prompt injection. Default: 0.05 (5%). */
|
|
25642
|
+
ltm: external_exports.number().min(0.02).max(0.3).default(0.05),
|
|
25643
|
+
/** Per-turn cache-read cost target in dollars. Controls when layer 0 (full
|
|
25644
|
+
* passthrough) escalates to layer 1 (compressed). The cap is derived as:
|
|
25645
|
+
* maxLayer0Tokens = max(target / model.cost.cache.read, 40K).
|
|
25646
|
+
* Lower = cheaper but earlier compression. Default: 0.10. Set to 0 to
|
|
25647
|
+
* disable cost-aware capping (use the model's full context). */
|
|
25648
|
+
targetCacheReadCostPerTurn: external_exports.number().min(0).default(0.1),
|
|
25649
|
+
/** Direct override for the layer-0 token cap. When set, bypasses the
|
|
25650
|
+
* cost-aware formula from targetCacheReadCostPerTurn. 0 = disabled
|
|
25651
|
+
* (no cap, use full context). Default: undefined (use cost-aware auto). */
|
|
25652
|
+
maxLayer0Tokens: external_exports.number().min(0).optional()
|
|
25653
|
+
}).default({ distilled: 0.25, raw: 0.4, output: 0.25, ltm: 0.05, targetCacheReadCostPerTurn: 0.1 }),
|
|
25654
|
+
/**
|
|
25655
|
+
* Cold-cache idle-resume handling.
|
|
25656
|
+
*
|
|
25657
|
+
* Anthropic's prompt cache evicts entries after ~5 min (default tier) /
|
|
25658
|
+
* ~1 hour (extended tier). When a session resumes after the eviction window,
|
|
25659
|
+
* Lore's byte-identity caches (distilled prefix, raw window pin, LTM block)
|
|
25660
|
+
* are providing no value because the underlying provider cache is already
|
|
25661
|
+
* cold. On detection, Lore refreshes those caches so the next turn can
|
|
25662
|
+
* produce a better-fitting window without paying a cache cost it would
|
|
25663
|
+
* otherwise be trying to preserve. Reasoning blocks are NOT touched —
|
|
25664
|
+
* Anthropic's April 23 postmortem identified dropping reasoning blocks as
|
|
25665
|
+
* the root cause of forgetfulness/repetition.
|
|
25666
|
+
*
|
|
25667
|
+
* `idleResumeMinutes` is the threshold in minutes. Default 60 — matches
|
|
25668
|
+
* Anthropic's extended-cache eviction window, conservative across providers.
|
|
25669
|
+
* Set to 0 to disable the feature.
|
|
25670
|
+
*/
|
|
25671
|
+
idleResumeMinutes: external_exports.number().min(0).max(24 * 60).default(60),
|
|
25510
25672
|
distillation: external_exports.object({
|
|
25511
|
-
minMessages: external_exports.number().min(3).default(
|
|
25512
|
-
maxSegment: external_exports.number().min(5).default(
|
|
25513
|
-
metaThreshold: external_exports.number().min(3).default(10)
|
|
25514
|
-
|
|
25673
|
+
minMessages: external_exports.number().min(3).default(5),
|
|
25674
|
+
maxSegment: external_exports.number().min(5).default(30),
|
|
25675
|
+
metaThreshold: external_exports.number().min(3).default(10),
|
|
25676
|
+
/** Max chars per tool output when rendering temporal messages for distillation input.
|
|
25677
|
+
* Outputs longer than this are replaced with a compact annotation preserving line
|
|
25678
|
+
* count, error signals, and file paths. Default: 2000 (matches upstream OpenCode's
|
|
25679
|
+
* TOOL_OUTPUT_MAX_CHARS during compaction). Set to 0 to disable. */
|
|
25680
|
+
toolOutputMaxChars: external_exports.number().min(0).default(2e3)
|
|
25681
|
+
}).default({
|
|
25682
|
+
minMessages: 5,
|
|
25683
|
+
maxSegment: 30,
|
|
25684
|
+
metaThreshold: 10,
|
|
25685
|
+
toolOutputMaxChars: 2e3
|
|
25686
|
+
}),
|
|
25515
25687
|
knowledge: external_exports.object({
|
|
25516
25688
|
/** Set to false to disable long-term knowledge storage and system-prompt injection.
|
|
25517
25689
|
* Conversation recall (temporal search, distillation search) and context management
|
|
@@ -25616,6 +25788,7 @@ __export(embedding_exports, {
|
|
|
25616
25788
|
vectorSearch: () => vectorSearch,
|
|
25617
25789
|
vectorSearchDistillations: () => vectorSearchDistillations
|
|
25618
25790
|
});
|
|
25791
|
+
var EMBED_TIMEOUT_MS = 1e4;
|
|
25619
25792
|
var VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
|
|
25620
25793
|
var VoyageProvider = class {
|
|
25621
25794
|
maxBatchSize = 128;
|
|
@@ -25639,7 +25812,8 @@ var VoyageProvider = class {
|
|
|
25639
25812
|
model: this.model,
|
|
25640
25813
|
input_type: inputType,
|
|
25641
25814
|
output_dimension: this.dimensions
|
|
25642
|
-
})
|
|
25815
|
+
}),
|
|
25816
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25643
25817
|
});
|
|
25644
25818
|
if (!res.ok) {
|
|
25645
25819
|
const body = await res.text().catch(() => "");
|
|
@@ -25675,7 +25849,8 @@ var OpenAIProvider = class {
|
|
|
25675
25849
|
"Content-Type": "application/json",
|
|
25676
25850
|
Authorization: `Bearer ${this.apiKey}`
|
|
25677
25851
|
},
|
|
25678
|
-
body: JSON.stringify(body)
|
|
25852
|
+
body: JSON.stringify(body),
|
|
25853
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25679
25854
|
});
|
|
25680
25855
|
if (!res.ok) {
|
|
25681
25856
|
const responseBody = await res.text().catch(() => "");
|
|
@@ -26058,8 +26233,8 @@ function searchScored2(input) {
|
|
|
26058
26233
|
const ftsSQL = `SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26059
26234
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26060
26235
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26061
|
-
FROM
|
|
26062
|
-
JOIN
|
|
26236
|
+
FROM lat_sections_fts f
|
|
26237
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26063
26238
|
WHERE lat_sections_fts MATCH ?
|
|
26064
26239
|
AND s.project_id = ?
|
|
26065
26240
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26085,8 +26260,8 @@ function scoreForSession(projectPath, sessionContext, maxTokens) {
|
|
|
26085
26260
|
`SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26086
26261
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26087
26262
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26088
|
-
FROM
|
|
26089
|
-
JOIN
|
|
26263
|
+
FROM lat_sections_fts f
|
|
26264
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26090
26265
|
WHERE lat_sections_fts MATCH ?
|
|
26091
26266
|
AND s.project_id = ?
|
|
26092
26267
|
ORDER BY rank`
|
|
@@ -26215,10 +26390,10 @@ function scoreEntriesFTS(sessionContext) {
|
|
|
26215
26390
|
try {
|
|
26216
26391
|
const results = db().query(
|
|
26217
26392
|
`SELECT k.id, bm25(knowledge_fts, ?, ?, ?) as rank
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26393
|
+
FROM knowledge_fts f
|
|
26394
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26395
|
+
WHERE knowledge_fts MATCH ?
|
|
26396
|
+
AND k.confidence > 0.2`
|
|
26222
26397
|
).all(title, content3, category, q);
|
|
26223
26398
|
if (!results.length) return /* @__PURE__ */ new Map();
|
|
26224
26399
|
const ranks = results.map((r) => r.rank);
|
|
@@ -26352,13 +26527,13 @@ function search3(input) {
|
|
|
26352
26527
|
const q = ftsQuery(input.query);
|
|
26353
26528
|
if (q === EMPTY_QUERY) return [];
|
|
26354
26529
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26355
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26356
|
-
JOIN
|
|
26530
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26531
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26357
26532
|
WHERE knowledge_fts MATCH ?
|
|
26358
26533
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26359
26534
|
AND k.confidence > 0.2
|
|
26360
|
-
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26361
|
-
JOIN
|
|
26535
|
+
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26536
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26362
26537
|
WHERE knowledge_fts MATCH ?
|
|
26363
26538
|
AND k.confidence > 0.2
|
|
26364
26539
|
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?`;
|
|
@@ -26385,13 +26560,13 @@ function searchScored3(input) {
|
|
|
26385
26560
|
if (q === EMPTY_QUERY) return [];
|
|
26386
26561
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26387
26562
|
const { title, content: content3, category } = ftsWeights();
|
|
26388
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26389
|
-
JOIN
|
|
26563
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26564
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26390
26565
|
WHERE knowledge_fts MATCH ?
|
|
26391
26566
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26392
26567
|
AND k.confidence > 0.2
|
|
26393
|
-
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26394
|
-
JOIN
|
|
26568
|
+
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26569
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26395
26570
|
WHERE knowledge_fts MATCH ?
|
|
26396
26571
|
AND k.confidence > 0.2
|
|
26397
26572
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26413,8 +26588,8 @@ function searchScoredOtherProjects(input) {
|
|
|
26413
26588
|
if (q === EMPTY_QUERY) return [];
|
|
26414
26589
|
const excludePid = ensureProject(input.excludeProjectPath);
|
|
26415
26590
|
const { title, content: content3, category } = ftsWeights();
|
|
26416
|
-
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26417
|
-
JOIN
|
|
26591
|
+
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26592
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26418
26593
|
WHERE knowledge_fts MATCH ?
|
|
26419
26594
|
AND k.project_id IS NOT NULL
|
|
26420
26595
|
AND k.project_id != ?
|
|
@@ -26547,8 +26722,8 @@ function check2(projectPath) {
|
|
|
26547
26722
|
try {
|
|
26548
26723
|
const { title, content: content3, category } = config2().search.ftsWeights;
|
|
26549
26724
|
const matches = db().query(
|
|
26550
|
-
`SELECT k.id, k.title FROM
|
|
26551
|
-
JOIN
|
|
26725
|
+
`SELECT k.id, k.title FROM knowledge_fts f
|
|
26726
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26552
26727
|
WHERE knowledge_fts MATCH ?
|
|
26553
26728
|
AND k.id != ?
|
|
26554
26729
|
AND k.confidence > 0.2
|
|
@@ -26582,9 +26757,13 @@ function check2(projectPath) {
|
|
|
26582
26757
|
// src/distillation.ts
|
|
26583
26758
|
var distillation_exports = {};
|
|
26584
26759
|
__export(distillation_exports, {
|
|
26760
|
+
latestMetaObservations: () => latestMetaObservations,
|
|
26585
26761
|
loadForSession: () => loadForSession,
|
|
26762
|
+
messagesToText: () => messagesToText,
|
|
26763
|
+
metaDistill: () => metaDistill,
|
|
26586
26764
|
parseSourceIds: () => parseSourceIds,
|
|
26587
26765
|
run: () => run,
|
|
26766
|
+
truncateToolOutputsInContent: () => truncateToolOutputsInContent,
|
|
26588
26767
|
workerSessionIDs: () => workerSessionIDs
|
|
26589
26768
|
});
|
|
26590
26769
|
|
|
@@ -26609,6 +26788,8 @@ function estimateMessage(msg) {
|
|
|
26609
26788
|
}
|
|
26610
26789
|
var contextLimit = 2e5;
|
|
26611
26790
|
var outputReserved = 32e3;
|
|
26791
|
+
var maxLayer0Tokens = 0;
|
|
26792
|
+
var MIN_LAYER0_FLOOR = 4e4;
|
|
26612
26793
|
var FIRST_TURN_OVERHEAD = 15e3;
|
|
26613
26794
|
var calibratedOverhead = null;
|
|
26614
26795
|
function makeSessionState() {
|
|
@@ -26622,7 +26803,11 @@ function makeSessionState() {
|
|
|
26622
26803
|
forceMinLayer: 0,
|
|
26623
26804
|
lastTransformEstimate: 0,
|
|
26624
26805
|
prefixCache: null,
|
|
26625
|
-
rawWindowCache: null
|
|
26806
|
+
rawWindowCache: null,
|
|
26807
|
+
lastTurnAt: 0,
|
|
26808
|
+
cameOutOfIdle: false,
|
|
26809
|
+
consecutiveHighLayer: 0,
|
|
26810
|
+
lastPrefixHash: ""
|
|
26626
26811
|
};
|
|
26627
26812
|
}
|
|
26628
26813
|
var sessionStates = /* @__PURE__ */ new Map();
|
|
@@ -26635,11 +26820,36 @@ function getSessionState(sessionID) {
|
|
|
26635
26820
|
}
|
|
26636
26821
|
return state;
|
|
26637
26822
|
}
|
|
26823
|
+
function onIdleResume(sessionID, thresholdMs, now = Date.now()) {
|
|
26824
|
+
if (thresholdMs <= 0) return { triggered: false };
|
|
26825
|
+
const state = getSessionState(sessionID);
|
|
26826
|
+
if (state.lastTurnAt === 0) return { triggered: false };
|
|
26827
|
+
const idleMs = now - state.lastTurnAt;
|
|
26828
|
+
if (idleMs < thresholdMs) return { triggered: false };
|
|
26829
|
+
state.prefixCache = null;
|
|
26830
|
+
state.rawWindowCache = null;
|
|
26831
|
+
state.cameOutOfIdle = true;
|
|
26832
|
+
return { triggered: true, idleMs };
|
|
26833
|
+
}
|
|
26834
|
+
function consumeCameOutOfIdle(sessionID) {
|
|
26835
|
+
const state = sessionStates.get(sessionID);
|
|
26836
|
+
if (!state || !state.cameOutOfIdle) return false;
|
|
26837
|
+
state.cameOutOfIdle = false;
|
|
26838
|
+
return true;
|
|
26839
|
+
}
|
|
26638
26840
|
var ltmTokens = 0;
|
|
26639
26841
|
function setModelLimits(limits) {
|
|
26640
26842
|
contextLimit = limits.context || 2e5;
|
|
26641
26843
|
outputReserved = Math.min(limits.output || 32e3, 32e3);
|
|
26642
26844
|
}
|
|
26845
|
+
function setMaxLayer0Tokens(tokens) {
|
|
26846
|
+
maxLayer0Tokens = Math.max(0, Math.floor(tokens));
|
|
26847
|
+
}
|
|
26848
|
+
function computeLayer0Cap(targetCostPerTurn, cacheReadCostPerToken) {
|
|
26849
|
+
if (targetCostPerTurn <= 0 || cacheReadCostPerToken <= 0) return 0;
|
|
26850
|
+
const rawCap = Math.floor(targetCostPerTurn / cacheReadCostPerToken);
|
|
26851
|
+
return Math.max(rawCap, MIN_LAYER0_FLOOR);
|
|
26852
|
+
}
|
|
26643
26853
|
function setLtmTokens(tokens) {
|
|
26644
26854
|
ltmTokens = tokens;
|
|
26645
26855
|
}
|
|
@@ -26684,6 +26894,19 @@ function setForceMinLayer(layer, sessionID) {
|
|
|
26684
26894
|
}
|
|
26685
26895
|
}
|
|
26686
26896
|
}
|
|
26897
|
+
function inspectSessionState(sessionID) {
|
|
26898
|
+
const state = sessionStates.get(sessionID);
|
|
26899
|
+
if (!state) return null;
|
|
26900
|
+
return {
|
|
26901
|
+
hasPrefixCache: state.prefixCache !== null,
|
|
26902
|
+
hasRawWindowCache: state.rawWindowCache !== null,
|
|
26903
|
+
cameOutOfIdle: state.cameOutOfIdle,
|
|
26904
|
+
lastTurnAt: state.lastTurnAt
|
|
26905
|
+
};
|
|
26906
|
+
}
|
|
26907
|
+
function setLastTurnAtForTest(sessionID, ms) {
|
|
26908
|
+
getSessionState(sessionID).lastTurnAt = ms;
|
|
26909
|
+
}
|
|
26687
26910
|
function loadDistillations(projectPath, sessionID) {
|
|
26688
26911
|
const pid = ensureProject(projectPath);
|
|
26689
26912
|
const query = sessionID ? "SELECT id, observations, generation, token_count, created_at, session_id FROM distillations WHERE project_id = ? AND session_id = ? AND archived = 0 ORDER BY created_at ASC" : "SELECT id, observations, generation, token_count, created_at, session_id FROM distillations WHERE project_id = ? AND archived = 0 ORDER BY created_at ASC";
|
|
@@ -26716,12 +26939,17 @@ function cleanParts(parts) {
|
|
|
26716
26939
|
}
|
|
26717
26940
|
return filtered.length > 0 ? filtered : parts;
|
|
26718
26941
|
}
|
|
26942
|
+
var ANNOTATION_PATH_SCAN_LIMIT = 64 * 1024;
|
|
26943
|
+
var PATH_RE = /(?:[\w.-]+\/)+[\w.-]+\.\w{1,5}/g;
|
|
26719
26944
|
function toolStripAnnotation(toolName, output) {
|
|
26720
26945
|
const lines = output.split("\n").length;
|
|
26721
|
-
const chars = output.length;
|
|
26722
26946
|
const hasError = /\b(?:error|fail(?:ed|ure)?|exception|panic|traceback)\b/i.test(output);
|
|
26723
|
-
|
|
26724
|
-
|
|
26947
|
+
let uniquePaths = [];
|
|
26948
|
+
if (output.indexOf("/") !== -1) {
|
|
26949
|
+
const pathScan = output.length > ANNOTATION_PATH_SCAN_LIMIT ? output.slice(0, ANNOTATION_PATH_SCAN_LIMIT) : output;
|
|
26950
|
+
const paths = pathScan.match(PATH_RE);
|
|
26951
|
+
if (paths) uniquePaths = [...new Set(paths)].slice(0, 5);
|
|
26952
|
+
}
|
|
26725
26953
|
let annotation = `[output omitted \u2014 ${toolName}: ${lines} lines`;
|
|
26726
26954
|
if (hasError) annotation += ", contained errors";
|
|
26727
26955
|
if (uniquePaths.length > 0) annotation += `, paths: ${uniquePaths.join(", ")}`;
|
|
@@ -27152,7 +27380,11 @@ function transformInner(input) {
|
|
|
27152
27380
|
expectedInput = messageTokens + overhead + ltmTokens;
|
|
27153
27381
|
}
|
|
27154
27382
|
const layer0Input = calibrated ? expectedInput : expectedInput * UNCALIBRATED_SAFETY;
|
|
27155
|
-
|
|
27383
|
+
let layer0Ceiling = maxLayer0Tokens > 0 ? Math.min(maxInput, maxLayer0Tokens) : maxInput;
|
|
27384
|
+
if (!calibrated && layer0Ceiling < maxInput) {
|
|
27385
|
+
layer0Ceiling = Math.floor(layer0Ceiling * 0.7);
|
|
27386
|
+
}
|
|
27387
|
+
if (effectiveMinLayer === 0 && layer0Input <= layer0Ceiling) {
|
|
27156
27388
|
const messageTokens = calibrated ? expectedInput - (ltmTokens - sessState.lastKnownLtm) : expectedInput - overhead - ltmTokens;
|
|
27157
27389
|
return {
|
|
27158
27390
|
messages: input.messages,
|
|
@@ -27233,14 +27465,31 @@ function transformInner(input) {
|
|
|
27233
27465
|
(sum, m) => sum + estimateMessage(m),
|
|
27234
27466
|
0
|
|
27235
27467
|
);
|
|
27236
|
-
const
|
|
27468
|
+
const tailBudget = Math.max(2e3, Math.min(8e3, Math.floor(usable * 0.25)));
|
|
27469
|
+
const nuclearTurnStart = currentTurnStart(input.messages);
|
|
27470
|
+
const currentTurn = input.messages.slice(nuclearTurnStart).map((m) => ({
|
|
27237
27471
|
info: m.info,
|
|
27238
27472
|
parts: cleanParts(m.parts)
|
|
27239
27473
|
}));
|
|
27240
|
-
const
|
|
27474
|
+
const currentTurnTokens = currentTurn.reduce(
|
|
27241
27475
|
(sum, m) => sum + estimateMessage(m),
|
|
27242
27476
|
0
|
|
27243
27477
|
);
|
|
27478
|
+
const olderMessages = [];
|
|
27479
|
+
let olderTokens = 0;
|
|
27480
|
+
const remaining = Math.max(0, tailBudget - currentTurnTokens);
|
|
27481
|
+
for (let i = nuclearTurnStart - 1; i >= 0 && olderTokens < remaining; i--) {
|
|
27482
|
+
const msg = input.messages[i];
|
|
27483
|
+
const est = estimateMessage(msg);
|
|
27484
|
+
if (olderTokens + est > remaining) break;
|
|
27485
|
+
olderMessages.unshift({
|
|
27486
|
+
info: msg.info,
|
|
27487
|
+
parts: cleanParts(msg.parts)
|
|
27488
|
+
});
|
|
27489
|
+
olderTokens += est;
|
|
27490
|
+
}
|
|
27491
|
+
const nuclearRaw = [...olderMessages, ...currentTurn];
|
|
27492
|
+
const nuclearRawTokens = olderTokens + currentTurnTokens;
|
|
27244
27493
|
return {
|
|
27245
27494
|
messages: [...nuclearPrefix, ...nuclearRaw],
|
|
27246
27495
|
layer: 4,
|
|
@@ -27262,19 +27511,55 @@ function transform2(input) {
|
|
|
27262
27511
|
state.lastTransformEstimate = result.totalTokens;
|
|
27263
27512
|
state.lastLayer = result.layer;
|
|
27264
27513
|
state.lastWindowMessageIDs = new Set(result.messages.map((m) => m.info.id));
|
|
27514
|
+
state.lastTurnAt = Date.now();
|
|
27515
|
+
const prefixIds = result.messages.slice(0, 5).map((m) => m.info.id).join(",");
|
|
27516
|
+
const prefixHash = `${result.layer}:${prefixIds}`;
|
|
27517
|
+
if (state.lastPrefixHash && state.lastPrefixHash !== prefixHash) {
|
|
27518
|
+
info(
|
|
27519
|
+
`cache-bust detected: session=${sid} layer=${state.lastLayer}\u2192${result.layer} msgs=${state.lastTransformedCount}\u2192${result.messages.length} prefix=${state.lastPrefixHash.slice(0, 30)}\u2192${prefixHash.slice(0, 30)}`
|
|
27520
|
+
);
|
|
27521
|
+
}
|
|
27522
|
+
state.lastPrefixHash = prefixHash;
|
|
27523
|
+
if (result.layer >= 2) {
|
|
27524
|
+
state.consecutiveHighLayer++;
|
|
27525
|
+
if (state.consecutiveHighLayer === 3) {
|
|
27526
|
+
info(
|
|
27527
|
+
`session ${sid} has been at gradient layer ${result.layer}+ for 3 consecutive turns. Consider running /compact to reset the context window.`
|
|
27528
|
+
);
|
|
27529
|
+
}
|
|
27530
|
+
} else {
|
|
27531
|
+
state.consecutiveHighLayer = 0;
|
|
27532
|
+
}
|
|
27533
|
+
info(
|
|
27534
|
+
`gradient: session=${sid} layer=${result.layer} tokens=${result.totalTokens} (distilled=${result.distilledTokens} raw=${result.rawTokens}) usable=${result.usable} cap=${maxLayer0Tokens || "off"}`
|
|
27535
|
+
);
|
|
27265
27536
|
}
|
|
27266
27537
|
return result;
|
|
27267
27538
|
}
|
|
27268
27539
|
function currentTurnStart(messages) {
|
|
27269
|
-
|
|
27540
|
+
if (messages.length === 0) return 0;
|
|
27541
|
+
let boundary = messages.length;
|
|
27270
27542
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
27271
27543
|
if (messages[i].info.role === "user") {
|
|
27272
|
-
|
|
27544
|
+
boundary = i;
|
|
27273
27545
|
break;
|
|
27274
27546
|
}
|
|
27275
27547
|
}
|
|
27276
|
-
if (
|
|
27277
|
-
|
|
27548
|
+
if (boundary === messages.length) return 0;
|
|
27549
|
+
for (let i = boundary - 1; i >= 0; i--) {
|
|
27550
|
+
const msg = messages[i];
|
|
27551
|
+
const hasToolParts = msg.parts.some(isToolPart);
|
|
27552
|
+
if (hasToolParts) {
|
|
27553
|
+
boundary = i;
|
|
27554
|
+
continue;
|
|
27555
|
+
}
|
|
27556
|
+
if (msg.info.role === "user") {
|
|
27557
|
+
boundary = i;
|
|
27558
|
+
continue;
|
|
27559
|
+
}
|
|
27560
|
+
break;
|
|
27561
|
+
}
|
|
27562
|
+
return boundary;
|
|
27278
27563
|
}
|
|
27279
27564
|
function tryFit(input) {
|
|
27280
27565
|
if (input.prefixTokens > input.distilledBudget && input.prefix.length > 0)
|
|
@@ -27355,8 +27640,39 @@ function formatTime(ms) {
|
|
|
27355
27640
|
const m = d.getMinutes().toString().padStart(2, "0");
|
|
27356
27641
|
return `${h3}:${m}`;
|
|
27357
27642
|
}
|
|
27358
|
-
|
|
27359
|
-
|
|
27643
|
+
var CHUNK_SEPARATOR = "\n" + CHUNK_TERMINATOR;
|
|
27644
|
+
function truncateToolOutputsInContent(content3, maxChars) {
|
|
27645
|
+
if (maxChars <= 0 || content3.length === 0) return content3;
|
|
27646
|
+
if (content3.indexOf(CHUNK_TERMINATOR) === -1) {
|
|
27647
|
+
return truncateSingleChunk(content3, maxChars);
|
|
27648
|
+
}
|
|
27649
|
+
const chunks = content3.split(CHUNK_SEPARATOR);
|
|
27650
|
+
let anyToolChunk = false;
|
|
27651
|
+
for (const c of chunks) {
|
|
27652
|
+
if (c.startsWith("[tool:")) {
|
|
27653
|
+
anyToolChunk = true;
|
|
27654
|
+
break;
|
|
27655
|
+
}
|
|
27656
|
+
}
|
|
27657
|
+
if (!anyToolChunk) return content3;
|
|
27658
|
+
const out = chunks.map((chunk) => truncateSingleChunk(chunk, maxChars));
|
|
27659
|
+
return out.join(CHUNK_SEPARATOR);
|
|
27660
|
+
}
|
|
27661
|
+
function truncateSingleChunk(chunk, maxChars) {
|
|
27662
|
+
if (!chunk.startsWith("[tool:")) return chunk;
|
|
27663
|
+
const closeBracket = chunk.indexOf("] ");
|
|
27664
|
+
if (closeBracket < 0) return chunk;
|
|
27665
|
+
const toolName = chunk.slice(6, closeBracket);
|
|
27666
|
+
const payload = chunk.slice(closeBracket + 2);
|
|
27667
|
+
if (payload.length <= maxChars) return chunk;
|
|
27668
|
+
return `[tool:${toolName}] ${toolStripAnnotation(toolName, payload)}`;
|
|
27669
|
+
}
|
|
27670
|
+
function messagesToText(messages, toolOutputMaxChars) {
|
|
27671
|
+
const cap = toolOutputMaxChars ?? config2().distillation.toolOutputMaxChars;
|
|
27672
|
+
return messages.map((m) => {
|
|
27673
|
+
const body = m.role === "user" ? m.content : truncateToolOutputsInContent(m.content, cap);
|
|
27674
|
+
return `[${m.role}] (${formatTime(m.created_at)}) ${body}`;
|
|
27675
|
+
}).join("\n\n");
|
|
27360
27676
|
}
|
|
27361
27677
|
function parseDistillationResult(text4) {
|
|
27362
27678
|
const match = text4.match(/<observations>([\s\S]*?)<\/observations>/i);
|
|
@@ -27371,6 +27687,19 @@ function latestObservations(projectPath, sessionID) {
|
|
|
27371
27687
|
).get(pid, sessionID);
|
|
27372
27688
|
return row?.observations || void 0;
|
|
27373
27689
|
}
|
|
27690
|
+
function latestMetaObservations(projectPath, sessionID) {
|
|
27691
|
+
return latestMeta(projectPath, sessionID)?.observations;
|
|
27692
|
+
}
|
|
27693
|
+
function latestMeta(projectPath, sessionID) {
|
|
27694
|
+
const pid = ensureProject(projectPath);
|
|
27695
|
+
const row = db().query(
|
|
27696
|
+
`SELECT observations, generation FROM distillations
|
|
27697
|
+
WHERE project_id = ? AND session_id = ? AND generation > 0
|
|
27698
|
+
ORDER BY generation DESC, created_at DESC LIMIT 1`
|
|
27699
|
+
).get(pid, sessionID);
|
|
27700
|
+
if (!row || !row.observations) return void 0;
|
|
27701
|
+
return row;
|
|
27702
|
+
}
|
|
27374
27703
|
function parseSourceIds(raw) {
|
|
27375
27704
|
try {
|
|
27376
27705
|
const parsed = JSON.parse(raw);
|
|
@@ -27380,11 +27709,10 @@ function parseSourceIds(raw) {
|
|
|
27380
27709
|
return [];
|
|
27381
27710
|
}
|
|
27382
27711
|
}
|
|
27383
|
-
function loadForSession(projectPath, sessionID) {
|
|
27712
|
+
function loadForSession(projectPath, sessionID, includeArchived = false) {
|
|
27384
27713
|
const pid = ensureProject(projectPath);
|
|
27385
|
-
const
|
|
27386
|
-
|
|
27387
|
-
).all(pid, sessionID);
|
|
27714
|
+
const sql = includeArchived ? "SELECT id, project_id, session_id, observations, source_ids, generation, token_count, created_at FROM distillations WHERE project_id = ? AND session_id = ? ORDER BY created_at ASC" : "SELECT id, project_id, session_id, observations, source_ids, generation, token_count, created_at FROM distillations WHERE project_id = ? AND session_id = ? AND archived = 0 ORDER BY created_at ASC";
|
|
27715
|
+
const rows = db().query(sql).all(pid, sessionID);
|
|
27388
27716
|
return rows.map((r) => ({
|
|
27389
27717
|
...r,
|
|
27390
27718
|
source_ids: parseSourceIds(r.source_ids)
|
|
@@ -27549,8 +27877,13 @@ async function distillSegment(input) {
|
|
|
27549
27877
|
}
|
|
27550
27878
|
async function metaDistill(input) {
|
|
27551
27879
|
const existing = loadGen0(input.projectPath, input.sessionID);
|
|
27552
|
-
|
|
27553
|
-
|
|
27880
|
+
const priorMeta = latestMeta(input.projectPath, input.sessionID);
|
|
27881
|
+
if (priorMeta) {
|
|
27882
|
+
if (existing.length === 0) return null;
|
|
27883
|
+
} else {
|
|
27884
|
+
if (existing.length < 3) return null;
|
|
27885
|
+
}
|
|
27886
|
+
const userContent = recursiveUser(existing, priorMeta?.observations);
|
|
27554
27887
|
const model = input.model ?? config2().model;
|
|
27555
27888
|
const responseText = await input.llm.prompt(
|
|
27556
27889
|
RECURSIVE_SYSTEM,
|
|
@@ -27560,19 +27893,30 @@ async function metaDistill(input) {
|
|
|
27560
27893
|
if (!responseText) return null;
|
|
27561
27894
|
const result = parseDistillationResult(responseText);
|
|
27562
27895
|
if (!result) return null;
|
|
27563
|
-
const maxGen = Math.max(
|
|
27896
|
+
const maxGen = Math.max(
|
|
27897
|
+
...existing.map((d) => d.generation),
|
|
27898
|
+
priorMeta?.generation ?? 0
|
|
27899
|
+
);
|
|
27564
27900
|
const allSourceIDs = existing.flatMap((d) => d.source_ids);
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27901
|
+
let metaId;
|
|
27902
|
+
db().exec("BEGIN IMMEDIATE");
|
|
27903
|
+
try {
|
|
27904
|
+
metaId = storeDistillation({
|
|
27905
|
+
projectPath: input.projectPath,
|
|
27906
|
+
sessionID: input.sessionID,
|
|
27907
|
+
observations: result.observations,
|
|
27908
|
+
sourceIDs: allSourceIDs,
|
|
27909
|
+
generation: maxGen + 1
|
|
27910
|
+
});
|
|
27911
|
+
archiveDistillations(existing.map((d) => d.id));
|
|
27912
|
+
db().exec("COMMIT");
|
|
27913
|
+
} catch (e) {
|
|
27914
|
+
db().exec("ROLLBACK");
|
|
27915
|
+
throw e;
|
|
27916
|
+
}
|
|
27572
27917
|
if (isAvailable()) {
|
|
27573
27918
|
embedDistillation(metaId, result.observations);
|
|
27574
27919
|
}
|
|
27575
|
-
archiveDistillations(existing.map((d) => d.id));
|
|
27576
27920
|
return result;
|
|
27577
27921
|
}
|
|
27578
27922
|
|
|
@@ -27731,13 +28075,13 @@ function searchDistillationsScored(input) {
|
|
|
27731
28075
|
const q = ftsQuery(input.query);
|
|
27732
28076
|
if (q === EMPTY_QUERY) return [];
|
|
27733
28077
|
const ftsSQL = input.sessionID ? `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27734
|
-
FROM
|
|
27735
|
-
JOIN
|
|
28078
|
+
FROM distillation_fts f
|
|
28079
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27736
28080
|
WHERE distillation_fts MATCH ?
|
|
27737
28081
|
AND d.project_id = ? AND d.session_id = ?
|
|
27738
28082
|
ORDER BY rank LIMIT ?` : `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27739
|
-
FROM
|
|
27740
|
-
JOIN
|
|
28083
|
+
FROM distillation_fts f
|
|
28084
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27741
28085
|
WHERE distillation_fts MATCH ?
|
|
27742
28086
|
AND d.project_id = ?
|
|
27743
28087
|
ORDER BY rank LIMIT ?`;
|
|
@@ -28180,7 +28524,130 @@ function importFromFile(input) {
|
|
|
28180
28524
|
}
|
|
28181
28525
|
}
|
|
28182
28526
|
}
|
|
28527
|
+
|
|
28528
|
+
// src/worker-model.ts
|
|
28529
|
+
var worker_model_exports = {};
|
|
28530
|
+
__export(worker_model_exports, {
|
|
28531
|
+
WORKER_JUDGE_SYSTEM: () => WORKER_JUDGE_SYSTEM,
|
|
28532
|
+
computeModelFingerprint: () => computeModelFingerprint,
|
|
28533
|
+
getValidatedWorkerModel: () => getValidatedWorkerModel,
|
|
28534
|
+
isValidationStale: () => isValidationStale,
|
|
28535
|
+
parseJudgeScore: () => parseJudgeScore,
|
|
28536
|
+
resolveWorkerModel: () => resolveWorkerModel,
|
|
28537
|
+
selectWorkerCandidates: () => selectWorkerCandidates,
|
|
28538
|
+
storeValidatedWorkerModel: () => storeValidatedWorkerModel,
|
|
28539
|
+
structuralCheck: () => structuralCheck,
|
|
28540
|
+
workerJudgeUser: () => workerJudgeUser
|
|
28541
|
+
});
|
|
28542
|
+
var KV_PREFIX = "lore:worker_model:";
|
|
28543
|
+
function selectWorkerCandidates(sessionModel, providerModels) {
|
|
28544
|
+
const eligible = providerModels.filter(
|
|
28545
|
+
(m) => m.providerID === sessionModel.providerID && m.status === "active" && m.capabilities.input.text
|
|
28546
|
+
);
|
|
28547
|
+
if (eligible.length === 0) return [];
|
|
28548
|
+
const sorted = [...eligible].sort((a, b) => a.cost.input - b.cost.input);
|
|
28549
|
+
const cheapest = sorted[0];
|
|
28550
|
+
const belowSession = sorted.filter((m) => m.cost.input < sessionModel.cost.input).pop();
|
|
28551
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
28552
|
+
candidates.set(cheapest.id, cheapest);
|
|
28553
|
+
if (belowSession && belowSession.id !== cheapest.id) {
|
|
28554
|
+
candidates.set(belowSession.id, belowSession);
|
|
28555
|
+
}
|
|
28556
|
+
if (cheapest.id === sessionModel.id || cheapest.cost.input >= sessionModel.cost.input) {
|
|
28557
|
+
return [cheapest];
|
|
28558
|
+
}
|
|
28559
|
+
return [...candidates.values()];
|
|
28560
|
+
}
|
|
28561
|
+
function computeModelFingerprint(providerID, sessionModelID, activeModelIDs) {
|
|
28562
|
+
const sorted = [...activeModelIDs].sort();
|
|
28563
|
+
return sha256(
|
|
28564
|
+
JSON.stringify({ providerID, sessionModelID, modelIDs: sorted })
|
|
28565
|
+
);
|
|
28566
|
+
}
|
|
28567
|
+
function getValidatedWorkerModel(providerID) {
|
|
28568
|
+
const row = db().query("SELECT value FROM kv_meta WHERE key = ?").get(`${KV_PREFIX}${providerID}`);
|
|
28569
|
+
if (!row) return null;
|
|
28570
|
+
try {
|
|
28571
|
+
return JSON.parse(row.value);
|
|
28572
|
+
} catch {
|
|
28573
|
+
return null;
|
|
28574
|
+
}
|
|
28575
|
+
}
|
|
28576
|
+
function storeValidatedWorkerModel(result) {
|
|
28577
|
+
const key = `${KV_PREFIX}${result.providerID}`;
|
|
28578
|
+
const value = JSON.stringify(result);
|
|
28579
|
+
db().query(
|
|
28580
|
+
"INSERT INTO kv_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?"
|
|
28581
|
+
).run(key, value, value);
|
|
28582
|
+
}
|
|
28583
|
+
function isValidationStale(stored, currentFingerprint) {
|
|
28584
|
+
if (!stored) return true;
|
|
28585
|
+
return stored.fingerprint !== currentFingerprint;
|
|
28586
|
+
}
|
|
28587
|
+
function structuralCheck(candidateObservations, referenceObservations) {
|
|
28588
|
+
if (candidateObservations == null || candidateObservations.length === 0) {
|
|
28589
|
+
return { passed: false, observationCount: 0, tokenCount: 0, reason: candidateObservations === null ? "parse_failed" : "empty" };
|
|
28590
|
+
}
|
|
28591
|
+
const countObs = (text4) => text4.split("\n").filter((l) => l.trim().length > 0).length;
|
|
28592
|
+
const refCount = countObs(referenceObservations);
|
|
28593
|
+
const candCount = countObs(candidateObservations);
|
|
28594
|
+
const candTokens = Math.ceil(candidateObservations.length / 3);
|
|
28595
|
+
if (refCount > 0 && (candCount < refCount * 0.5 || candCount > refCount * 1.5)) {
|
|
28596
|
+
return {
|
|
28597
|
+
passed: false,
|
|
28598
|
+
observationCount: candCount,
|
|
28599
|
+
tokenCount: candTokens,
|
|
28600
|
+
reason: `observation_count_${candCount}_vs_ref_${refCount}`
|
|
28601
|
+
};
|
|
28602
|
+
}
|
|
28603
|
+
const refTokens = Math.ceil(referenceObservations.length / 3);
|
|
28604
|
+
if (candTokens === 0) {
|
|
28605
|
+
return { passed: false, observationCount: candCount, tokenCount: candTokens, reason: "empty" };
|
|
28606
|
+
}
|
|
28607
|
+
if (refTokens > 0 && candTokens > refTokens * 3) {
|
|
28608
|
+
return {
|
|
28609
|
+
passed: false,
|
|
28610
|
+
observationCount: candCount,
|
|
28611
|
+
tokenCount: candTokens,
|
|
28612
|
+
reason: `token_count_${candTokens}_vs_ref_${refTokens}_3x`
|
|
28613
|
+
};
|
|
28614
|
+
}
|
|
28615
|
+
return { passed: true, observationCount: candCount, tokenCount: candTokens };
|
|
28616
|
+
}
|
|
28617
|
+
var WORKER_JUDGE_SYSTEM = `You are evaluating distillation quality. You will be given a REFERENCE distillation (produced by a capable model) and a CANDIDATE distillation (produced by a cheaper model) of the same conversation segment.
|
|
28618
|
+
|
|
28619
|
+
Rate the candidate on a scale of 1-5:
|
|
28620
|
+
5 = Captures all key facts and decisions, equivalent to reference
|
|
28621
|
+
4 = Captures most facts, minor omissions
|
|
28622
|
+
3 = Captures the essential facts, some detail loss acceptable
|
|
28623
|
+
2 = Missing important facts or technical details
|
|
28624
|
+
1 = Significantly incomplete or inaccurate
|
|
28625
|
+
|
|
28626
|
+
Respond with ONLY a single digit (1-5).`;
|
|
28627
|
+
function workerJudgeUser(reference, candidate) {
|
|
28628
|
+
return `<reference>
|
|
28629
|
+
${reference}
|
|
28630
|
+
</reference>
|
|
28631
|
+
|
|
28632
|
+
<candidate>
|
|
28633
|
+
${candidate}
|
|
28634
|
+
</candidate>`;
|
|
28635
|
+
}
|
|
28636
|
+
function parseJudgeScore(response) {
|
|
28637
|
+
const match = response.trim().match(/^([1-5])/);
|
|
28638
|
+
if (!match) return null;
|
|
28639
|
+
return parseInt(match[1], 10);
|
|
28640
|
+
}
|
|
28641
|
+
function resolveWorkerModel(providerID, configWorkerModel, configModel) {
|
|
28642
|
+
if (configWorkerModel) return configWorkerModel;
|
|
28643
|
+
const validated = getValidatedWorkerModel(providerID);
|
|
28644
|
+
if (validated) {
|
|
28645
|
+
return { providerID: validated.providerID, modelID: validated.modelID };
|
|
28646
|
+
}
|
|
28647
|
+
return configModel;
|
|
28648
|
+
}
|
|
28183
28649
|
export {
|
|
28650
|
+
COMPACT_SUMMARY_TEMPLATE,
|
|
28184
28651
|
CONSOLIDATION_SYSTEM,
|
|
28185
28652
|
CURATOR_SYSTEM,
|
|
28186
28653
|
DISTILLATION_SYSTEM,
|
|
@@ -28189,10 +28656,14 @@ export {
|
|
|
28189
28656
|
RECALL_PARAM_DESCRIPTIONS,
|
|
28190
28657
|
RECALL_TOOL_DESCRIPTION,
|
|
28191
28658
|
RECURSIVE_SYSTEM,
|
|
28659
|
+
WORKER_JUDGE_SYSTEM,
|
|
28660
|
+
buildCompactPrompt,
|
|
28192
28661
|
calibrate,
|
|
28193
28662
|
close,
|
|
28663
|
+
computeLayer0Cap,
|
|
28194
28664
|
config2 as config,
|
|
28195
28665
|
consolidationUser,
|
|
28666
|
+
consumeCameOutOfIdle,
|
|
28196
28667
|
curator_exports as curator,
|
|
28197
28668
|
curatorUser,
|
|
28198
28669
|
db,
|
|
@@ -28214,6 +28685,7 @@ export {
|
|
|
28214
28685
|
h,
|
|
28215
28686
|
importFromFile,
|
|
28216
28687
|
inline,
|
|
28688
|
+
inspectSessionState,
|
|
28217
28689
|
isFirstRun,
|
|
28218
28690
|
isReasoningPart,
|
|
28219
28691
|
isTextPart,
|
|
@@ -28228,6 +28700,7 @@ export {
|
|
|
28228
28700
|
ltm_exports as ltm,
|
|
28229
28701
|
needsUrgentDistillation,
|
|
28230
28702
|
normalize,
|
|
28703
|
+
onIdleResume,
|
|
28231
28704
|
p,
|
|
28232
28705
|
projectId,
|
|
28233
28706
|
projectName,
|
|
@@ -28239,15 +28712,20 @@ export {
|
|
|
28239
28712
|
saveForceMinLayer,
|
|
28240
28713
|
serialize,
|
|
28241
28714
|
setForceMinLayer,
|
|
28715
|
+
setLastTurnAtForTest,
|
|
28242
28716
|
setLtmTokens,
|
|
28717
|
+
setMaxLayer0Tokens,
|
|
28243
28718
|
setModelLimits,
|
|
28244
28719
|
shouldImport,
|
|
28245
28720
|
strong2 as strong,
|
|
28246
28721
|
t,
|
|
28247
28722
|
temporal_exports as temporal,
|
|
28723
|
+
toolStripAnnotation,
|
|
28248
28724
|
transform2 as transform,
|
|
28249
28725
|
ul,
|
|
28250
28726
|
unescapeMarkdown,
|
|
28727
|
+
workerJudgeUser,
|
|
28728
|
+
worker_model_exports as workerModel,
|
|
28251
28729
|
workerSessionIDs
|
|
28252
28730
|
};
|
|
28253
28731
|
//# sourceMappingURL=index.js.map
|