@loreai/core 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +530 -67
- 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 +530 -67
- 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 +57 -1
- package/src/distillation.ts +225 -28
- package/src/embedding.ts +7 -0
- package/src/gradient.ts +262 -8
- 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() {
|
|
@@ -459,6 +513,7 @@ function db() {
|
|
|
459
513
|
instance = new Database(path);
|
|
460
514
|
instance.exec("PRAGMA journal_mode = WAL");
|
|
461
515
|
instance.exec("PRAGMA foreign_keys = ON");
|
|
516
|
+
instance.exec("PRAGMA busy_timeout = 5000");
|
|
462
517
|
instance.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
463
518
|
migrate(instance);
|
|
464
519
|
return instance;
|
|
@@ -10765,12 +10820,27 @@ EXACT NUMBERS: When two segments report different numbers for what seems like th
|
|
|
10765
10820
|
|
|
10766
10821
|
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
10822
|
|
|
10823
|
+
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.
|
|
10824
|
+
|
|
10768
10825
|
Output ONLY an <observations> block with the consolidated observations.`;
|
|
10769
|
-
function recursiveUser(distillations) {
|
|
10826
|
+
function recursiveUser(distillations, previousMeta) {
|
|
10770
10827
|
const entries = distillations.map(
|
|
10771
10828
|
(d, i) => `Segment ${i + 1}:
|
|
10772
10829
|
${d.observations}`
|
|
10773
10830
|
);
|
|
10831
|
+
if (previousMeta) {
|
|
10832
|
+
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.
|
|
10833
|
+
|
|
10834
|
+
<previous-meta-summary>
|
|
10835
|
+
${previousMeta}
|
|
10836
|
+
</previous-meta-summary>
|
|
10837
|
+
|
|
10838
|
+
---
|
|
10839
|
+
|
|
10840
|
+
New observation segments to merge (chronological order):
|
|
10841
|
+
|
|
10842
|
+
${entries.join("\n\n---\n\n")}`;
|
|
10843
|
+
}
|
|
10774
10844
|
return `Observation segments to consolidate (chronological order):
|
|
10775
10845
|
|
|
10776
10846
|
${entries.join("\n\n---\n\n")}`;
|
|
@@ -10925,6 +10995,61 @@ function formatDistillations(distillations) {
|
|
|
10925
10995
|
}
|
|
10926
10996
|
return sections.join("\n\n");
|
|
10927
10997
|
}
|
|
10998
|
+
var COMPACT_SUMMARY_TEMPLATE = `Output exactly this Markdown structure. Keep every section in this order, even when empty (use "(none)").
|
|
10999
|
+
|
|
11000
|
+
---
|
|
11001
|
+
## Goal
|
|
11002
|
+
- [single-sentence task summary]
|
|
11003
|
+
|
|
11004
|
+
## Constraints & Preferences
|
|
11005
|
+
- [user constraints, preferences, specs, or "(none)"]
|
|
11006
|
+
|
|
11007
|
+
## Progress
|
|
11008
|
+
### Done
|
|
11009
|
+
- [completed work or "(none)"]
|
|
11010
|
+
|
|
11011
|
+
### In Progress
|
|
11012
|
+
- [current work or "(none)"]
|
|
11013
|
+
|
|
11014
|
+
### Blocked
|
|
11015
|
+
- [blockers or "(none)"]
|
|
11016
|
+
|
|
11017
|
+
## Key Decisions
|
|
11018
|
+
- [decision and why, or "(none)"]
|
|
11019
|
+
|
|
11020
|
+
## Next Steps
|
|
11021
|
+
- [ordered next actions or "(none)"]
|
|
11022
|
+
|
|
11023
|
+
## Critical Context
|
|
11024
|
+
- [important technical facts, errors, open questions, or "(none)"]
|
|
11025
|
+
|
|
11026
|
+
## Relevant Files
|
|
11027
|
+
- [file or directory path: why it matters, or "(none)"]
|
|
11028
|
+
---
|
|
11029
|
+
|
|
11030
|
+
Rules:
|
|
11031
|
+
- Keep every section, even when empty.
|
|
11032
|
+
- Use terse bullets, not prose paragraphs.
|
|
11033
|
+
- Preserve exact file paths, commands, error strings, and identifiers when known.
|
|
11034
|
+
- Do not mention the summary process or that context was compacted.
|
|
11035
|
+
- End with "I'm ready to continue." on its own line after the closing "---".`;
|
|
11036
|
+
function buildCompactPrompt(input) {
|
|
11037
|
+
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" : "";
|
|
11038
|
+
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.
|
|
11039
|
+
|
|
11040
|
+
<previous-summary>
|
|
11041
|
+
${input.previousSummary}
|
|
11042
|
+
</previous-summary>
|
|
11043
|
+
|
|
11044
|
+
` : "";
|
|
11045
|
+
const knowledgeBlock = input.knowledge ? `
|
|
11046
|
+
${input.knowledge}
|
|
11047
|
+
` : "";
|
|
11048
|
+
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.
|
|
11049
|
+
|
|
11050
|
+
${distillSection}${anchorBlock}${COMPACT_SUMMARY_TEMPLATE}
|
|
11051
|
+
${knowledgeBlock}`;
|
|
11052
|
+
}
|
|
10928
11053
|
function estimateTokens(text4) {
|
|
10929
11054
|
return Math.ceil(text4.length / 3);
|
|
10930
11055
|
}
|
|
@@ -11177,6 +11302,7 @@ function isToolPart(p3) {
|
|
|
11177
11302
|
function estimate(text4) {
|
|
11178
11303
|
return Math.ceil(text4.length / 3);
|
|
11179
11304
|
}
|
|
11305
|
+
var CHUNK_TERMINATOR = "";
|
|
11180
11306
|
function partsToText(parts) {
|
|
11181
11307
|
const chunks = [];
|
|
11182
11308
|
for (const part of parts) {
|
|
@@ -11186,7 +11312,7 @@ function partsToText(parts) {
|
|
|
11186
11312
|
else if (isToolPart(part) && part.state.status === "completed")
|
|
11187
11313
|
chunks.push(`[tool:${part.tool}] ${part.state.output}`);
|
|
11188
11314
|
}
|
|
11189
|
-
return sanitizeSurrogates(chunks.join("\n"));
|
|
11315
|
+
return sanitizeSurrogates(chunks.join("\n" + CHUNK_TERMINATOR));
|
|
11190
11316
|
}
|
|
11191
11317
|
function messageMetadata(info2, parts) {
|
|
11192
11318
|
const meta3 = {};
|
|
@@ -11265,11 +11391,11 @@ function search2(input) {
|
|
|
11265
11391
|
const limit = input.limit ?? 20;
|
|
11266
11392
|
const q = ftsQuery(input.query);
|
|
11267
11393
|
if (q === EMPTY_QUERY) return [];
|
|
11268
|
-
const ftsSQL = input.sessionID ? `SELECT m.* FROM
|
|
11269
|
-
JOIN
|
|
11394
|
+
const ftsSQL = input.sessionID ? `SELECT m.* FROM temporal_fts f
|
|
11395
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11270
11396
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11271
|
-
ORDER BY rank LIMIT ?` : `SELECT m.* FROM
|
|
11272
|
-
JOIN
|
|
11397
|
+
ORDER BY rank LIMIT ?` : `SELECT m.* FROM temporal_fts f
|
|
11398
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11273
11399
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11274
11400
|
ORDER BY rank LIMIT ?`;
|
|
11275
11401
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -11294,11 +11420,11 @@ function searchScored(input) {
|
|
|
11294
11420
|
const limit = input.limit ?? 20;
|
|
11295
11421
|
const q = ftsQuery(input.query);
|
|
11296
11422
|
if (q === EMPTY_QUERY) return [];
|
|
11297
|
-
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM
|
|
11298
|
-
JOIN
|
|
11423
|
+
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM temporal_fts f
|
|
11424
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11299
11425
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11300
|
-
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM
|
|
11301
|
-
JOIN
|
|
11426
|
+
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM temporal_fts f
|
|
11427
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11302
11428
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11303
11429
|
ORDER BY rank LIMIT ?`;
|
|
11304
11430
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -25500,18 +25626,63 @@ var LoreConfig = external_exports.object({
|
|
|
25500
25626
|
providerID: external_exports.string(),
|
|
25501
25627
|
modelID: external_exports.string()
|
|
25502
25628
|
}).optional(),
|
|
25629
|
+
/** Explicit worker model override. When set, all background workers (distillation,
|
|
25630
|
+
* curation, query expansion) use this model instead of the session model or the
|
|
25631
|
+
* auto-selected worker model. Bypasses dynamic worker model selection entirely. */
|
|
25632
|
+
workerModel: external_exports.object({
|
|
25633
|
+
providerID: external_exports.string(),
|
|
25634
|
+
modelID: external_exports.string()
|
|
25635
|
+
}).optional(),
|
|
25503
25636
|
budget: external_exports.object({
|
|
25504
25637
|
distilled: external_exports.number().min(0.05).max(0.5).default(0.25),
|
|
25505
25638
|
raw: external_exports.number().min(0.1).max(0.7).default(0.4),
|
|
25506
25639
|
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
|
-
|
|
25640
|
+
/** Max fraction of usable context reserved for LTM system-prompt injection. Default: 0.05 (5%). */
|
|
25641
|
+
ltm: external_exports.number().min(0.02).max(0.3).default(0.05),
|
|
25642
|
+
/** Per-turn cache-read cost target in dollars. Controls when layer 0 (full
|
|
25643
|
+
* passthrough) escalates to layer 1 (compressed). The cap is derived as:
|
|
25644
|
+
* maxLayer0Tokens = max(target / model.cost.cache.read, 40K).
|
|
25645
|
+
* Lower = cheaper but earlier compression. Default: 0.10. Set to 0 to
|
|
25646
|
+
* disable cost-aware capping (use the model's full context). */
|
|
25647
|
+
targetCacheReadCostPerTurn: external_exports.number().min(0).default(0.1),
|
|
25648
|
+
/** Direct override for the layer-0 token cap. When set, bypasses the
|
|
25649
|
+
* cost-aware formula from targetCacheReadCostPerTurn. 0 = disabled
|
|
25650
|
+
* (no cap, use full context). Default: undefined (use cost-aware auto). */
|
|
25651
|
+
maxLayer0Tokens: external_exports.number().min(0).optional()
|
|
25652
|
+
}).default({ distilled: 0.25, raw: 0.4, output: 0.25, ltm: 0.05, targetCacheReadCostPerTurn: 0.1 }),
|
|
25653
|
+
/**
|
|
25654
|
+
* Cold-cache idle-resume handling.
|
|
25655
|
+
*
|
|
25656
|
+
* Anthropic's prompt cache evicts entries after ~5 min (default tier) /
|
|
25657
|
+
* ~1 hour (extended tier). When a session resumes after the eviction window,
|
|
25658
|
+
* Lore's byte-identity caches (distilled prefix, raw window pin, LTM block)
|
|
25659
|
+
* are providing no value because the underlying provider cache is already
|
|
25660
|
+
* cold. On detection, Lore refreshes those caches so the next turn can
|
|
25661
|
+
* produce a better-fitting window without paying a cache cost it would
|
|
25662
|
+
* otherwise be trying to preserve. Reasoning blocks are NOT touched —
|
|
25663
|
+
* Anthropic's April 23 postmortem identified dropping reasoning blocks as
|
|
25664
|
+
* the root cause of forgetfulness/repetition.
|
|
25665
|
+
*
|
|
25666
|
+
* `idleResumeMinutes` is the threshold in minutes. Default 60 — matches
|
|
25667
|
+
* Anthropic's extended-cache eviction window, conservative across providers.
|
|
25668
|
+
* Set to 0 to disable the feature.
|
|
25669
|
+
*/
|
|
25670
|
+
idleResumeMinutes: external_exports.number().min(0).max(24 * 60).default(60),
|
|
25510
25671
|
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
|
-
|
|
25672
|
+
minMessages: external_exports.number().min(3).default(5),
|
|
25673
|
+
maxSegment: external_exports.number().min(5).default(30),
|
|
25674
|
+
metaThreshold: external_exports.number().min(3).default(10),
|
|
25675
|
+
/** Max chars per tool output when rendering temporal messages for distillation input.
|
|
25676
|
+
* Outputs longer than this are replaced with a compact annotation preserving line
|
|
25677
|
+
* count, error signals, and file paths. Default: 2000 (matches upstream OpenCode's
|
|
25678
|
+
* TOOL_OUTPUT_MAX_CHARS during compaction). Set to 0 to disable. */
|
|
25679
|
+
toolOutputMaxChars: external_exports.number().min(0).default(2e3)
|
|
25680
|
+
}).default({
|
|
25681
|
+
minMessages: 5,
|
|
25682
|
+
maxSegment: 30,
|
|
25683
|
+
metaThreshold: 10,
|
|
25684
|
+
toolOutputMaxChars: 2e3
|
|
25685
|
+
}),
|
|
25515
25686
|
knowledge: external_exports.object({
|
|
25516
25687
|
/** Set to false to disable long-term knowledge storage and system-prompt injection.
|
|
25517
25688
|
* Conversation recall (temporal search, distillation search) and context management
|
|
@@ -25616,6 +25787,7 @@ __export(embedding_exports, {
|
|
|
25616
25787
|
vectorSearch: () => vectorSearch,
|
|
25617
25788
|
vectorSearchDistillations: () => vectorSearchDistillations
|
|
25618
25789
|
});
|
|
25790
|
+
var EMBED_TIMEOUT_MS = 1e4;
|
|
25619
25791
|
var VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
|
|
25620
25792
|
var VoyageProvider = class {
|
|
25621
25793
|
maxBatchSize = 128;
|
|
@@ -25639,7 +25811,8 @@ var VoyageProvider = class {
|
|
|
25639
25811
|
model: this.model,
|
|
25640
25812
|
input_type: inputType,
|
|
25641
25813
|
output_dimension: this.dimensions
|
|
25642
|
-
})
|
|
25814
|
+
}),
|
|
25815
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25643
25816
|
});
|
|
25644
25817
|
if (!res.ok) {
|
|
25645
25818
|
const body = await res.text().catch(() => "");
|
|
@@ -25675,7 +25848,8 @@ var OpenAIProvider = class {
|
|
|
25675
25848
|
"Content-Type": "application/json",
|
|
25676
25849
|
Authorization: `Bearer ${this.apiKey}`
|
|
25677
25850
|
},
|
|
25678
|
-
body: JSON.stringify(body)
|
|
25851
|
+
body: JSON.stringify(body),
|
|
25852
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25679
25853
|
});
|
|
25680
25854
|
if (!res.ok) {
|
|
25681
25855
|
const responseBody = await res.text().catch(() => "");
|
|
@@ -26058,8 +26232,8 @@ function searchScored2(input) {
|
|
|
26058
26232
|
const ftsSQL = `SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26059
26233
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26060
26234
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26061
|
-
FROM
|
|
26062
|
-
JOIN
|
|
26235
|
+
FROM lat_sections_fts f
|
|
26236
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26063
26237
|
WHERE lat_sections_fts MATCH ?
|
|
26064
26238
|
AND s.project_id = ?
|
|
26065
26239
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26085,8 +26259,8 @@ function scoreForSession(projectPath, sessionContext, maxTokens) {
|
|
|
26085
26259
|
`SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26086
26260
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26087
26261
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26088
|
-
FROM
|
|
26089
|
-
JOIN
|
|
26262
|
+
FROM lat_sections_fts f
|
|
26263
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26090
26264
|
WHERE lat_sections_fts MATCH ?
|
|
26091
26265
|
AND s.project_id = ?
|
|
26092
26266
|
ORDER BY rank`
|
|
@@ -26215,10 +26389,10 @@ function scoreEntriesFTS(sessionContext) {
|
|
|
26215
26389
|
try {
|
|
26216
26390
|
const results = db().query(
|
|
26217
26391
|
`SELECT k.id, bm25(knowledge_fts, ?, ?, ?) as rank
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26392
|
+
FROM knowledge_fts f
|
|
26393
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26394
|
+
WHERE knowledge_fts MATCH ?
|
|
26395
|
+
AND k.confidence > 0.2`
|
|
26222
26396
|
).all(title, content3, category, q);
|
|
26223
26397
|
if (!results.length) return /* @__PURE__ */ new Map();
|
|
26224
26398
|
const ranks = results.map((r) => r.rank);
|
|
@@ -26352,13 +26526,13 @@ function search3(input) {
|
|
|
26352
26526
|
const q = ftsQuery(input.query);
|
|
26353
26527
|
if (q === EMPTY_QUERY) return [];
|
|
26354
26528
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26355
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26356
|
-
JOIN
|
|
26529
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26530
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26357
26531
|
WHERE knowledge_fts MATCH ?
|
|
26358
26532
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26359
26533
|
AND k.confidence > 0.2
|
|
26360
|
-
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26361
|
-
JOIN
|
|
26534
|
+
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26535
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26362
26536
|
WHERE knowledge_fts MATCH ?
|
|
26363
26537
|
AND k.confidence > 0.2
|
|
26364
26538
|
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?`;
|
|
@@ -26385,13 +26559,13 @@ function searchScored3(input) {
|
|
|
26385
26559
|
if (q === EMPTY_QUERY) return [];
|
|
26386
26560
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26387
26561
|
const { title, content: content3, category } = ftsWeights();
|
|
26388
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26389
|
-
JOIN
|
|
26562
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26563
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26390
26564
|
WHERE knowledge_fts MATCH ?
|
|
26391
26565
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26392
26566
|
AND k.confidence > 0.2
|
|
26393
|
-
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26394
|
-
JOIN
|
|
26567
|
+
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26568
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26395
26569
|
WHERE knowledge_fts MATCH ?
|
|
26396
26570
|
AND k.confidence > 0.2
|
|
26397
26571
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26413,8 +26587,8 @@ function searchScoredOtherProjects(input) {
|
|
|
26413
26587
|
if (q === EMPTY_QUERY) return [];
|
|
26414
26588
|
const excludePid = ensureProject(input.excludeProjectPath);
|
|
26415
26589
|
const { title, content: content3, category } = ftsWeights();
|
|
26416
|
-
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26417
|
-
JOIN
|
|
26590
|
+
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26591
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26418
26592
|
WHERE knowledge_fts MATCH ?
|
|
26419
26593
|
AND k.project_id IS NOT NULL
|
|
26420
26594
|
AND k.project_id != ?
|
|
@@ -26547,8 +26721,8 @@ function check2(projectPath) {
|
|
|
26547
26721
|
try {
|
|
26548
26722
|
const { title, content: content3, category } = config2().search.ftsWeights;
|
|
26549
26723
|
const matches = db().query(
|
|
26550
|
-
`SELECT k.id, k.title FROM
|
|
26551
|
-
JOIN
|
|
26724
|
+
`SELECT k.id, k.title FROM knowledge_fts f
|
|
26725
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26552
26726
|
WHERE knowledge_fts MATCH ?
|
|
26553
26727
|
AND k.id != ?
|
|
26554
26728
|
AND k.confidence > 0.2
|
|
@@ -26582,9 +26756,13 @@ function check2(projectPath) {
|
|
|
26582
26756
|
// src/distillation.ts
|
|
26583
26757
|
var distillation_exports = {};
|
|
26584
26758
|
__export(distillation_exports, {
|
|
26759
|
+
latestMetaObservations: () => latestMetaObservations,
|
|
26585
26760
|
loadForSession: () => loadForSession,
|
|
26761
|
+
messagesToText: () => messagesToText,
|
|
26762
|
+
metaDistill: () => metaDistill,
|
|
26586
26763
|
parseSourceIds: () => parseSourceIds,
|
|
26587
26764
|
run: () => run,
|
|
26765
|
+
truncateToolOutputsInContent: () => truncateToolOutputsInContent,
|
|
26588
26766
|
workerSessionIDs: () => workerSessionIDs
|
|
26589
26767
|
});
|
|
26590
26768
|
|
|
@@ -26609,6 +26787,8 @@ function estimateMessage(msg) {
|
|
|
26609
26787
|
}
|
|
26610
26788
|
var contextLimit = 2e5;
|
|
26611
26789
|
var outputReserved = 32e3;
|
|
26790
|
+
var maxLayer0Tokens = 0;
|
|
26791
|
+
var MIN_LAYER0_FLOOR = 4e4;
|
|
26612
26792
|
var FIRST_TURN_OVERHEAD = 15e3;
|
|
26613
26793
|
var calibratedOverhead = null;
|
|
26614
26794
|
function makeSessionState() {
|
|
@@ -26622,7 +26802,11 @@ function makeSessionState() {
|
|
|
26622
26802
|
forceMinLayer: 0,
|
|
26623
26803
|
lastTransformEstimate: 0,
|
|
26624
26804
|
prefixCache: null,
|
|
26625
|
-
rawWindowCache: null
|
|
26805
|
+
rawWindowCache: null,
|
|
26806
|
+
lastTurnAt: 0,
|
|
26807
|
+
cameOutOfIdle: false,
|
|
26808
|
+
consecutiveHighLayer: 0,
|
|
26809
|
+
lastPrefixHash: ""
|
|
26626
26810
|
};
|
|
26627
26811
|
}
|
|
26628
26812
|
var sessionStates = /* @__PURE__ */ new Map();
|
|
@@ -26635,11 +26819,36 @@ function getSessionState(sessionID) {
|
|
|
26635
26819
|
}
|
|
26636
26820
|
return state;
|
|
26637
26821
|
}
|
|
26822
|
+
function onIdleResume(sessionID, thresholdMs, now = Date.now()) {
|
|
26823
|
+
if (thresholdMs <= 0) return { triggered: false };
|
|
26824
|
+
const state = getSessionState(sessionID);
|
|
26825
|
+
if (state.lastTurnAt === 0) return { triggered: false };
|
|
26826
|
+
const idleMs = now - state.lastTurnAt;
|
|
26827
|
+
if (idleMs < thresholdMs) return { triggered: false };
|
|
26828
|
+
state.prefixCache = null;
|
|
26829
|
+
state.rawWindowCache = null;
|
|
26830
|
+
state.cameOutOfIdle = true;
|
|
26831
|
+
return { triggered: true, idleMs };
|
|
26832
|
+
}
|
|
26833
|
+
function consumeCameOutOfIdle(sessionID) {
|
|
26834
|
+
const state = sessionStates.get(sessionID);
|
|
26835
|
+
if (!state || !state.cameOutOfIdle) return false;
|
|
26836
|
+
state.cameOutOfIdle = false;
|
|
26837
|
+
return true;
|
|
26838
|
+
}
|
|
26638
26839
|
var ltmTokens = 0;
|
|
26639
26840
|
function setModelLimits(limits) {
|
|
26640
26841
|
contextLimit = limits.context || 2e5;
|
|
26641
26842
|
outputReserved = Math.min(limits.output || 32e3, 32e3);
|
|
26642
26843
|
}
|
|
26844
|
+
function setMaxLayer0Tokens(tokens) {
|
|
26845
|
+
maxLayer0Tokens = Math.max(0, Math.floor(tokens));
|
|
26846
|
+
}
|
|
26847
|
+
function computeLayer0Cap(targetCostPerTurn, cacheReadCostPerToken) {
|
|
26848
|
+
if (targetCostPerTurn <= 0 || cacheReadCostPerToken <= 0) return 0;
|
|
26849
|
+
const rawCap = Math.floor(targetCostPerTurn / cacheReadCostPerToken);
|
|
26850
|
+
return Math.max(rawCap, MIN_LAYER0_FLOOR);
|
|
26851
|
+
}
|
|
26643
26852
|
function setLtmTokens(tokens) {
|
|
26644
26853
|
ltmTokens = tokens;
|
|
26645
26854
|
}
|
|
@@ -26684,6 +26893,19 @@ function setForceMinLayer(layer, sessionID) {
|
|
|
26684
26893
|
}
|
|
26685
26894
|
}
|
|
26686
26895
|
}
|
|
26896
|
+
function inspectSessionState(sessionID) {
|
|
26897
|
+
const state = sessionStates.get(sessionID);
|
|
26898
|
+
if (!state) return null;
|
|
26899
|
+
return {
|
|
26900
|
+
hasPrefixCache: state.prefixCache !== null,
|
|
26901
|
+
hasRawWindowCache: state.rawWindowCache !== null,
|
|
26902
|
+
cameOutOfIdle: state.cameOutOfIdle,
|
|
26903
|
+
lastTurnAt: state.lastTurnAt
|
|
26904
|
+
};
|
|
26905
|
+
}
|
|
26906
|
+
function setLastTurnAtForTest(sessionID, ms) {
|
|
26907
|
+
getSessionState(sessionID).lastTurnAt = ms;
|
|
26908
|
+
}
|
|
26687
26909
|
function loadDistillations(projectPath, sessionID) {
|
|
26688
26910
|
const pid = ensureProject(projectPath);
|
|
26689
26911
|
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 +26938,17 @@ function cleanParts(parts) {
|
|
|
26716
26938
|
}
|
|
26717
26939
|
return filtered.length > 0 ? filtered : parts;
|
|
26718
26940
|
}
|
|
26941
|
+
var ANNOTATION_PATH_SCAN_LIMIT = 64 * 1024;
|
|
26942
|
+
var PATH_RE = /(?:[\w.-]+\/)+[\w.-]+\.\w{1,5}/g;
|
|
26719
26943
|
function toolStripAnnotation(toolName, output) {
|
|
26720
26944
|
const lines = output.split("\n").length;
|
|
26721
|
-
const chars = output.length;
|
|
26722
26945
|
const hasError = /\b(?:error|fail(?:ed|ure)?|exception|panic|traceback)\b/i.test(output);
|
|
26723
|
-
|
|
26724
|
-
|
|
26946
|
+
let uniquePaths = [];
|
|
26947
|
+
if (output.indexOf("/") !== -1) {
|
|
26948
|
+
const pathScan = output.length > ANNOTATION_PATH_SCAN_LIMIT ? output.slice(0, ANNOTATION_PATH_SCAN_LIMIT) : output;
|
|
26949
|
+
const paths = pathScan.match(PATH_RE);
|
|
26950
|
+
if (paths) uniquePaths = [...new Set(paths)].slice(0, 5);
|
|
26951
|
+
}
|
|
26725
26952
|
let annotation = `[output omitted \u2014 ${toolName}: ${lines} lines`;
|
|
26726
26953
|
if (hasError) annotation += ", contained errors";
|
|
26727
26954
|
if (uniquePaths.length > 0) annotation += `, paths: ${uniquePaths.join(", ")}`;
|
|
@@ -27152,7 +27379,11 @@ function transformInner(input) {
|
|
|
27152
27379
|
expectedInput = messageTokens + overhead + ltmTokens;
|
|
27153
27380
|
}
|
|
27154
27381
|
const layer0Input = calibrated ? expectedInput : expectedInput * UNCALIBRATED_SAFETY;
|
|
27155
|
-
|
|
27382
|
+
let layer0Ceiling = maxLayer0Tokens > 0 ? Math.min(maxInput, maxLayer0Tokens) : maxInput;
|
|
27383
|
+
if (!calibrated && layer0Ceiling < maxInput) {
|
|
27384
|
+
layer0Ceiling = Math.floor(layer0Ceiling * 0.7);
|
|
27385
|
+
}
|
|
27386
|
+
if (effectiveMinLayer === 0 && layer0Input <= layer0Ceiling) {
|
|
27156
27387
|
const messageTokens = calibrated ? expectedInput - (ltmTokens - sessState.lastKnownLtm) : expectedInput - overhead - ltmTokens;
|
|
27157
27388
|
return {
|
|
27158
27389
|
messages: input.messages,
|
|
@@ -27233,14 +27464,31 @@ function transformInner(input) {
|
|
|
27233
27464
|
(sum, m) => sum + estimateMessage(m),
|
|
27234
27465
|
0
|
|
27235
27466
|
);
|
|
27236
|
-
const
|
|
27467
|
+
const tailBudget = Math.max(2e3, Math.min(8e3, Math.floor(usable * 0.25)));
|
|
27468
|
+
const nuclearTurnStart = currentTurnStart(input.messages);
|
|
27469
|
+
const currentTurn = input.messages.slice(nuclearTurnStart).map((m) => ({
|
|
27237
27470
|
info: m.info,
|
|
27238
27471
|
parts: cleanParts(m.parts)
|
|
27239
27472
|
}));
|
|
27240
|
-
const
|
|
27473
|
+
const currentTurnTokens = currentTurn.reduce(
|
|
27241
27474
|
(sum, m) => sum + estimateMessage(m),
|
|
27242
27475
|
0
|
|
27243
27476
|
);
|
|
27477
|
+
const olderMessages = [];
|
|
27478
|
+
let olderTokens = 0;
|
|
27479
|
+
const remaining = Math.max(0, tailBudget - currentTurnTokens);
|
|
27480
|
+
for (let i = nuclearTurnStart - 1; i >= 0 && olderTokens < remaining; i--) {
|
|
27481
|
+
const msg = input.messages[i];
|
|
27482
|
+
const est = estimateMessage(msg);
|
|
27483
|
+
if (olderTokens + est > remaining) break;
|
|
27484
|
+
olderMessages.unshift({
|
|
27485
|
+
info: msg.info,
|
|
27486
|
+
parts: cleanParts(msg.parts)
|
|
27487
|
+
});
|
|
27488
|
+
olderTokens += est;
|
|
27489
|
+
}
|
|
27490
|
+
const nuclearRaw = [...olderMessages, ...currentTurn];
|
|
27491
|
+
const nuclearRawTokens = olderTokens + currentTurnTokens;
|
|
27244
27492
|
return {
|
|
27245
27493
|
messages: [...nuclearPrefix, ...nuclearRaw],
|
|
27246
27494
|
layer: 4,
|
|
@@ -27262,6 +27510,28 @@ function transform2(input) {
|
|
|
27262
27510
|
state.lastTransformEstimate = result.totalTokens;
|
|
27263
27511
|
state.lastLayer = result.layer;
|
|
27264
27512
|
state.lastWindowMessageIDs = new Set(result.messages.map((m) => m.info.id));
|
|
27513
|
+
state.lastTurnAt = Date.now();
|
|
27514
|
+
const prefixIds = result.messages.slice(0, 5).map((m) => m.info.id).join(",");
|
|
27515
|
+
const prefixHash = `${result.layer}:${prefixIds}`;
|
|
27516
|
+
if (state.lastPrefixHash && state.lastPrefixHash !== prefixHash) {
|
|
27517
|
+
info(
|
|
27518
|
+
`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)}`
|
|
27519
|
+
);
|
|
27520
|
+
}
|
|
27521
|
+
state.lastPrefixHash = prefixHash;
|
|
27522
|
+
if (result.layer >= 2) {
|
|
27523
|
+
state.consecutiveHighLayer++;
|
|
27524
|
+
if (state.consecutiveHighLayer === 3) {
|
|
27525
|
+
info(
|
|
27526
|
+
`session ${sid} has been at gradient layer ${result.layer}+ for 3 consecutive turns. Consider running /compact to reset the context window.`
|
|
27527
|
+
);
|
|
27528
|
+
}
|
|
27529
|
+
} else {
|
|
27530
|
+
state.consecutiveHighLayer = 0;
|
|
27531
|
+
}
|
|
27532
|
+
info(
|
|
27533
|
+
`gradient: session=${sid} layer=${result.layer} tokens=${result.totalTokens} (distilled=${result.distilledTokens} raw=${result.rawTokens}) usable=${result.usable} cap=${maxLayer0Tokens || "off"}`
|
|
27534
|
+
);
|
|
27265
27535
|
}
|
|
27266
27536
|
return result;
|
|
27267
27537
|
}
|
|
@@ -27355,8 +27625,39 @@ function formatTime(ms) {
|
|
|
27355
27625
|
const m = d.getMinutes().toString().padStart(2, "0");
|
|
27356
27626
|
return `${h3}:${m}`;
|
|
27357
27627
|
}
|
|
27358
|
-
|
|
27359
|
-
|
|
27628
|
+
var CHUNK_SEPARATOR = "\n" + CHUNK_TERMINATOR;
|
|
27629
|
+
function truncateToolOutputsInContent(content3, maxChars) {
|
|
27630
|
+
if (maxChars <= 0 || content3.length === 0) return content3;
|
|
27631
|
+
if (content3.indexOf(CHUNK_TERMINATOR) === -1) {
|
|
27632
|
+
return truncateSingleChunk(content3, maxChars);
|
|
27633
|
+
}
|
|
27634
|
+
const chunks = content3.split(CHUNK_SEPARATOR);
|
|
27635
|
+
let anyToolChunk = false;
|
|
27636
|
+
for (const c of chunks) {
|
|
27637
|
+
if (c.startsWith("[tool:")) {
|
|
27638
|
+
anyToolChunk = true;
|
|
27639
|
+
break;
|
|
27640
|
+
}
|
|
27641
|
+
}
|
|
27642
|
+
if (!anyToolChunk) return content3;
|
|
27643
|
+
const out = chunks.map((chunk) => truncateSingleChunk(chunk, maxChars));
|
|
27644
|
+
return out.join(CHUNK_SEPARATOR);
|
|
27645
|
+
}
|
|
27646
|
+
function truncateSingleChunk(chunk, maxChars) {
|
|
27647
|
+
if (!chunk.startsWith("[tool:")) return chunk;
|
|
27648
|
+
const closeBracket = chunk.indexOf("] ");
|
|
27649
|
+
if (closeBracket < 0) return chunk;
|
|
27650
|
+
const toolName = chunk.slice(6, closeBracket);
|
|
27651
|
+
const payload = chunk.slice(closeBracket + 2);
|
|
27652
|
+
if (payload.length <= maxChars) return chunk;
|
|
27653
|
+
return `[tool:${toolName}] ${toolStripAnnotation(toolName, payload)}`;
|
|
27654
|
+
}
|
|
27655
|
+
function messagesToText(messages, toolOutputMaxChars) {
|
|
27656
|
+
const cap = toolOutputMaxChars ?? config2().distillation.toolOutputMaxChars;
|
|
27657
|
+
return messages.map((m) => {
|
|
27658
|
+
const body = m.role === "user" ? m.content : truncateToolOutputsInContent(m.content, cap);
|
|
27659
|
+
return `[${m.role}] (${formatTime(m.created_at)}) ${body}`;
|
|
27660
|
+
}).join("\n\n");
|
|
27360
27661
|
}
|
|
27361
27662
|
function parseDistillationResult(text4) {
|
|
27362
27663
|
const match = text4.match(/<observations>([\s\S]*?)<\/observations>/i);
|
|
@@ -27371,6 +27672,19 @@ function latestObservations(projectPath, sessionID) {
|
|
|
27371
27672
|
).get(pid, sessionID);
|
|
27372
27673
|
return row?.observations || void 0;
|
|
27373
27674
|
}
|
|
27675
|
+
function latestMetaObservations(projectPath, sessionID) {
|
|
27676
|
+
return latestMeta(projectPath, sessionID)?.observations;
|
|
27677
|
+
}
|
|
27678
|
+
function latestMeta(projectPath, sessionID) {
|
|
27679
|
+
const pid = ensureProject(projectPath);
|
|
27680
|
+
const row = db().query(
|
|
27681
|
+
`SELECT observations, generation FROM distillations
|
|
27682
|
+
WHERE project_id = ? AND session_id = ? AND generation > 0
|
|
27683
|
+
ORDER BY generation DESC, created_at DESC LIMIT 1`
|
|
27684
|
+
).get(pid, sessionID);
|
|
27685
|
+
if (!row || !row.observations) return void 0;
|
|
27686
|
+
return row;
|
|
27687
|
+
}
|
|
27374
27688
|
function parseSourceIds(raw) {
|
|
27375
27689
|
try {
|
|
27376
27690
|
const parsed = JSON.parse(raw);
|
|
@@ -27380,11 +27694,10 @@ function parseSourceIds(raw) {
|
|
|
27380
27694
|
return [];
|
|
27381
27695
|
}
|
|
27382
27696
|
}
|
|
27383
|
-
function loadForSession(projectPath, sessionID) {
|
|
27697
|
+
function loadForSession(projectPath, sessionID, includeArchived = false) {
|
|
27384
27698
|
const pid = ensureProject(projectPath);
|
|
27385
|
-
const
|
|
27386
|
-
|
|
27387
|
-
).all(pid, sessionID);
|
|
27699
|
+
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";
|
|
27700
|
+
const rows = db().query(sql).all(pid, sessionID);
|
|
27388
27701
|
return rows.map((r) => ({
|
|
27389
27702
|
...r,
|
|
27390
27703
|
source_ids: parseSourceIds(r.source_ids)
|
|
@@ -27549,8 +27862,13 @@ async function distillSegment(input) {
|
|
|
27549
27862
|
}
|
|
27550
27863
|
async function metaDistill(input) {
|
|
27551
27864
|
const existing = loadGen0(input.projectPath, input.sessionID);
|
|
27552
|
-
|
|
27553
|
-
|
|
27865
|
+
const priorMeta = latestMeta(input.projectPath, input.sessionID);
|
|
27866
|
+
if (priorMeta) {
|
|
27867
|
+
if (existing.length === 0) return null;
|
|
27868
|
+
} else {
|
|
27869
|
+
if (existing.length < 3) return null;
|
|
27870
|
+
}
|
|
27871
|
+
const userContent = recursiveUser(existing, priorMeta?.observations);
|
|
27554
27872
|
const model = input.model ?? config2().model;
|
|
27555
27873
|
const responseText = await input.llm.prompt(
|
|
27556
27874
|
RECURSIVE_SYSTEM,
|
|
@@ -27560,19 +27878,30 @@ async function metaDistill(input) {
|
|
|
27560
27878
|
if (!responseText) return null;
|
|
27561
27879
|
const result = parseDistillationResult(responseText);
|
|
27562
27880
|
if (!result) return null;
|
|
27563
|
-
const maxGen = Math.max(
|
|
27881
|
+
const maxGen = Math.max(
|
|
27882
|
+
...existing.map((d) => d.generation),
|
|
27883
|
+
priorMeta?.generation ?? 0
|
|
27884
|
+
);
|
|
27564
27885
|
const allSourceIDs = existing.flatMap((d) => d.source_ids);
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27886
|
+
let metaId;
|
|
27887
|
+
db().exec("BEGIN IMMEDIATE");
|
|
27888
|
+
try {
|
|
27889
|
+
metaId = storeDistillation({
|
|
27890
|
+
projectPath: input.projectPath,
|
|
27891
|
+
sessionID: input.sessionID,
|
|
27892
|
+
observations: result.observations,
|
|
27893
|
+
sourceIDs: allSourceIDs,
|
|
27894
|
+
generation: maxGen + 1
|
|
27895
|
+
});
|
|
27896
|
+
archiveDistillations(existing.map((d) => d.id));
|
|
27897
|
+
db().exec("COMMIT");
|
|
27898
|
+
} catch (e) {
|
|
27899
|
+
db().exec("ROLLBACK");
|
|
27900
|
+
throw e;
|
|
27901
|
+
}
|
|
27572
27902
|
if (isAvailable()) {
|
|
27573
27903
|
embedDistillation(metaId, result.observations);
|
|
27574
27904
|
}
|
|
27575
|
-
archiveDistillations(existing.map((d) => d.id));
|
|
27576
27905
|
return result;
|
|
27577
27906
|
}
|
|
27578
27907
|
|
|
@@ -27731,13 +28060,13 @@ function searchDistillationsScored(input) {
|
|
|
27731
28060
|
const q = ftsQuery(input.query);
|
|
27732
28061
|
if (q === EMPTY_QUERY) return [];
|
|
27733
28062
|
const ftsSQL = input.sessionID ? `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27734
|
-
FROM
|
|
27735
|
-
JOIN
|
|
28063
|
+
FROM distillation_fts f
|
|
28064
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27736
28065
|
WHERE distillation_fts MATCH ?
|
|
27737
28066
|
AND d.project_id = ? AND d.session_id = ?
|
|
27738
28067
|
ORDER BY rank LIMIT ?` : `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27739
|
-
FROM
|
|
27740
|
-
JOIN
|
|
28068
|
+
FROM distillation_fts f
|
|
28069
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27741
28070
|
WHERE distillation_fts MATCH ?
|
|
27742
28071
|
AND d.project_id = ?
|
|
27743
28072
|
ORDER BY rank LIMIT ?`;
|
|
@@ -28180,7 +28509,130 @@ function importFromFile(input) {
|
|
|
28180
28509
|
}
|
|
28181
28510
|
}
|
|
28182
28511
|
}
|
|
28512
|
+
|
|
28513
|
+
// src/worker-model.ts
|
|
28514
|
+
var worker_model_exports = {};
|
|
28515
|
+
__export(worker_model_exports, {
|
|
28516
|
+
WORKER_JUDGE_SYSTEM: () => WORKER_JUDGE_SYSTEM,
|
|
28517
|
+
computeModelFingerprint: () => computeModelFingerprint,
|
|
28518
|
+
getValidatedWorkerModel: () => getValidatedWorkerModel,
|
|
28519
|
+
isValidationStale: () => isValidationStale,
|
|
28520
|
+
parseJudgeScore: () => parseJudgeScore,
|
|
28521
|
+
resolveWorkerModel: () => resolveWorkerModel,
|
|
28522
|
+
selectWorkerCandidates: () => selectWorkerCandidates,
|
|
28523
|
+
storeValidatedWorkerModel: () => storeValidatedWorkerModel,
|
|
28524
|
+
structuralCheck: () => structuralCheck,
|
|
28525
|
+
workerJudgeUser: () => workerJudgeUser
|
|
28526
|
+
});
|
|
28527
|
+
var KV_PREFIX = "lore:worker_model:";
|
|
28528
|
+
function selectWorkerCandidates(sessionModel, providerModels) {
|
|
28529
|
+
const eligible = providerModels.filter(
|
|
28530
|
+
(m) => m.providerID === sessionModel.providerID && m.status === "active" && m.capabilities.input.text
|
|
28531
|
+
);
|
|
28532
|
+
if (eligible.length === 0) return [];
|
|
28533
|
+
const sorted = [...eligible].sort((a, b) => a.cost.input - b.cost.input);
|
|
28534
|
+
const cheapest = sorted[0];
|
|
28535
|
+
const belowSession = sorted.filter((m) => m.cost.input < sessionModel.cost.input).pop();
|
|
28536
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
28537
|
+
candidates.set(cheapest.id, cheapest);
|
|
28538
|
+
if (belowSession && belowSession.id !== cheapest.id) {
|
|
28539
|
+
candidates.set(belowSession.id, belowSession);
|
|
28540
|
+
}
|
|
28541
|
+
if (cheapest.id === sessionModel.id || cheapest.cost.input >= sessionModel.cost.input) {
|
|
28542
|
+
return [cheapest];
|
|
28543
|
+
}
|
|
28544
|
+
return [...candidates.values()];
|
|
28545
|
+
}
|
|
28546
|
+
function computeModelFingerprint(providerID, sessionModelID, activeModelIDs) {
|
|
28547
|
+
const sorted = [...activeModelIDs].sort();
|
|
28548
|
+
return sha256(
|
|
28549
|
+
JSON.stringify({ providerID, sessionModelID, modelIDs: sorted })
|
|
28550
|
+
);
|
|
28551
|
+
}
|
|
28552
|
+
function getValidatedWorkerModel(providerID) {
|
|
28553
|
+
const row = db().query("SELECT value FROM kv_meta WHERE key = ?").get(`${KV_PREFIX}${providerID}`);
|
|
28554
|
+
if (!row) return null;
|
|
28555
|
+
try {
|
|
28556
|
+
return JSON.parse(row.value);
|
|
28557
|
+
} catch {
|
|
28558
|
+
return null;
|
|
28559
|
+
}
|
|
28560
|
+
}
|
|
28561
|
+
function storeValidatedWorkerModel(result) {
|
|
28562
|
+
const key = `${KV_PREFIX}${result.providerID}`;
|
|
28563
|
+
const value = JSON.stringify(result);
|
|
28564
|
+
db().query(
|
|
28565
|
+
"INSERT INTO kv_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?"
|
|
28566
|
+
).run(key, value, value);
|
|
28567
|
+
}
|
|
28568
|
+
function isValidationStale(stored, currentFingerprint) {
|
|
28569
|
+
if (!stored) return true;
|
|
28570
|
+
return stored.fingerprint !== currentFingerprint;
|
|
28571
|
+
}
|
|
28572
|
+
function structuralCheck(candidateObservations, referenceObservations) {
|
|
28573
|
+
if (candidateObservations == null || candidateObservations.length === 0) {
|
|
28574
|
+
return { passed: false, observationCount: 0, tokenCount: 0, reason: candidateObservations === null ? "parse_failed" : "empty" };
|
|
28575
|
+
}
|
|
28576
|
+
const countObs = (text4) => text4.split("\n").filter((l) => l.trim().length > 0).length;
|
|
28577
|
+
const refCount = countObs(referenceObservations);
|
|
28578
|
+
const candCount = countObs(candidateObservations);
|
|
28579
|
+
const candTokens = Math.ceil(candidateObservations.length / 3);
|
|
28580
|
+
if (refCount > 0 && (candCount < refCount * 0.5 || candCount > refCount * 1.5)) {
|
|
28581
|
+
return {
|
|
28582
|
+
passed: false,
|
|
28583
|
+
observationCount: candCount,
|
|
28584
|
+
tokenCount: candTokens,
|
|
28585
|
+
reason: `observation_count_${candCount}_vs_ref_${refCount}`
|
|
28586
|
+
};
|
|
28587
|
+
}
|
|
28588
|
+
const refTokens = Math.ceil(referenceObservations.length / 3);
|
|
28589
|
+
if (candTokens === 0) {
|
|
28590
|
+
return { passed: false, observationCount: candCount, tokenCount: candTokens, reason: "empty" };
|
|
28591
|
+
}
|
|
28592
|
+
if (refTokens > 0 && candTokens > refTokens * 3) {
|
|
28593
|
+
return {
|
|
28594
|
+
passed: false,
|
|
28595
|
+
observationCount: candCount,
|
|
28596
|
+
tokenCount: candTokens,
|
|
28597
|
+
reason: `token_count_${candTokens}_vs_ref_${refTokens}_3x`
|
|
28598
|
+
};
|
|
28599
|
+
}
|
|
28600
|
+
return { passed: true, observationCount: candCount, tokenCount: candTokens };
|
|
28601
|
+
}
|
|
28602
|
+
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.
|
|
28603
|
+
|
|
28604
|
+
Rate the candidate on a scale of 1-5:
|
|
28605
|
+
5 = Captures all key facts and decisions, equivalent to reference
|
|
28606
|
+
4 = Captures most facts, minor omissions
|
|
28607
|
+
3 = Captures the essential facts, some detail loss acceptable
|
|
28608
|
+
2 = Missing important facts or technical details
|
|
28609
|
+
1 = Significantly incomplete or inaccurate
|
|
28610
|
+
|
|
28611
|
+
Respond with ONLY a single digit (1-5).`;
|
|
28612
|
+
function workerJudgeUser(reference, candidate) {
|
|
28613
|
+
return `<reference>
|
|
28614
|
+
${reference}
|
|
28615
|
+
</reference>
|
|
28616
|
+
|
|
28617
|
+
<candidate>
|
|
28618
|
+
${candidate}
|
|
28619
|
+
</candidate>`;
|
|
28620
|
+
}
|
|
28621
|
+
function parseJudgeScore(response) {
|
|
28622
|
+
const match = response.trim().match(/^([1-5])/);
|
|
28623
|
+
if (!match) return null;
|
|
28624
|
+
return parseInt(match[1], 10);
|
|
28625
|
+
}
|
|
28626
|
+
function resolveWorkerModel(providerID, configWorkerModel, configModel) {
|
|
28627
|
+
if (configWorkerModel) return configWorkerModel;
|
|
28628
|
+
const validated = getValidatedWorkerModel(providerID);
|
|
28629
|
+
if (validated) {
|
|
28630
|
+
return { providerID: validated.providerID, modelID: validated.modelID };
|
|
28631
|
+
}
|
|
28632
|
+
return configModel;
|
|
28633
|
+
}
|
|
28183
28634
|
export {
|
|
28635
|
+
COMPACT_SUMMARY_TEMPLATE,
|
|
28184
28636
|
CONSOLIDATION_SYSTEM,
|
|
28185
28637
|
CURATOR_SYSTEM,
|
|
28186
28638
|
DISTILLATION_SYSTEM,
|
|
@@ -28189,10 +28641,14 @@ export {
|
|
|
28189
28641
|
RECALL_PARAM_DESCRIPTIONS,
|
|
28190
28642
|
RECALL_TOOL_DESCRIPTION,
|
|
28191
28643
|
RECURSIVE_SYSTEM,
|
|
28644
|
+
WORKER_JUDGE_SYSTEM,
|
|
28645
|
+
buildCompactPrompt,
|
|
28192
28646
|
calibrate,
|
|
28193
28647
|
close,
|
|
28648
|
+
computeLayer0Cap,
|
|
28194
28649
|
config2 as config,
|
|
28195
28650
|
consolidationUser,
|
|
28651
|
+
consumeCameOutOfIdle,
|
|
28196
28652
|
curator_exports as curator,
|
|
28197
28653
|
curatorUser,
|
|
28198
28654
|
db,
|
|
@@ -28214,6 +28670,7 @@ export {
|
|
|
28214
28670
|
h,
|
|
28215
28671
|
importFromFile,
|
|
28216
28672
|
inline,
|
|
28673
|
+
inspectSessionState,
|
|
28217
28674
|
isFirstRun,
|
|
28218
28675
|
isReasoningPart,
|
|
28219
28676
|
isTextPart,
|
|
@@ -28228,6 +28685,7 @@ export {
|
|
|
28228
28685
|
ltm_exports as ltm,
|
|
28229
28686
|
needsUrgentDistillation,
|
|
28230
28687
|
normalize,
|
|
28688
|
+
onIdleResume,
|
|
28231
28689
|
p,
|
|
28232
28690
|
projectId,
|
|
28233
28691
|
projectName,
|
|
@@ -28239,15 +28697,20 @@ export {
|
|
|
28239
28697
|
saveForceMinLayer,
|
|
28240
28698
|
serialize,
|
|
28241
28699
|
setForceMinLayer,
|
|
28700
|
+
setLastTurnAtForTest,
|
|
28242
28701
|
setLtmTokens,
|
|
28702
|
+
setMaxLayer0Tokens,
|
|
28243
28703
|
setModelLimits,
|
|
28244
28704
|
shouldImport,
|
|
28245
28705
|
strong2 as strong,
|
|
28246
28706
|
t,
|
|
28247
28707
|
temporal_exports as temporal,
|
|
28708
|
+
toolStripAnnotation,
|
|
28248
28709
|
transform2 as transform,
|
|
28249
28710
|
ul,
|
|
28250
28711
|
unescapeMarkdown,
|
|
28712
|
+
workerJudgeUser,
|
|
28713
|
+
worker_model_exports as workerModel,
|
|
28251
28714
|
workerSessionIDs
|
|
28252
28715
|
};
|
|
28253
28716
|
//# sourceMappingURL=index.js.map
|