@loreai/core 0.10.1 → 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/bun/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,
|
|
@@ -419,6 +421,58 @@ var MIGRATIONS = [
|
|
|
419
421
|
to_id TEXT NOT NULL REFERENCES knowledge(id) ON DELETE CASCADE,
|
|
420
422
|
PRIMARY KEY (from_id, to_id)
|
|
421
423
|
);
|
|
424
|
+
`,
|
|
425
|
+
`
|
|
426
|
+
-- Version 11: F3b -- unambiguous chunk terminator in temporal_messages.content.
|
|
427
|
+
--
|
|
428
|
+
-- Pre-F3b, partsToText joined chunks with a newline. Tool-output payloads
|
|
429
|
+
-- can contain newlines too, so the boundary between a tool envelope and a
|
|
430
|
+
-- following plain-text or [reasoning] chunk was structurally ambiguous.
|
|
431
|
+
-- This caused two known limitations in the F3 distill-input truncator:
|
|
432
|
+
-- trailing text could be swallowed into a tool payload, and embedded
|
|
433
|
+
-- literal envelope strings inside a payload (e.g. when reading AGENTS.md)
|
|
434
|
+
-- could fabricate fake boundaries.
|
|
435
|
+
--
|
|
436
|
+
-- F3b switches the chunk separator to newline plus ASCII Unit Separator
|
|
437
|
+
-- (char 31). The Unit Separator is non-word so FTS5's unicode61 tokenizer
|
|
438
|
+
-- ignores it (zero BM25 impact). New rows are written via the post-F3b
|
|
439
|
+
-- partsToText. Existing rows are rewritten in place by the UPDATE below,
|
|
440
|
+
-- which uses pure SQL replace() to inject the Unit Separator after every
|
|
441
|
+
-- legacy chunk-prefix sequence -- the same boundary patterns the legacy
|
|
442
|
+
-- F3 reader was already trying to recover.
|
|
443
|
+
--
|
|
444
|
+
-- Trade-off (acceptable): any embedded legacy chunk-prefix sequence
|
|
445
|
+
-- inside a tool payload becomes a structural boundary post-migration.
|
|
446
|
+
-- This matches what the legacy F3 reader did at read-time anyway, baked
|
|
447
|
+
-- into the row permanently. The migration runs once per machine.
|
|
448
|
+
--
|
|
449
|
+
-- Idempotent: a row that already contains the Unit Separator before a
|
|
450
|
+
-- chunk prefix no longer matches the search literal (the separator
|
|
451
|
+
-- interposes), so re-running the UPDATE is a no-op for migrated rows.
|
|
452
|
+
-- (Important: migrate() in db.ts runs each migration via database.exec()
|
|
453
|
+
-- with no explicit BEGIN/COMMIT around the whole loop. SQLite makes this
|
|
454
|
+
-- single UPDATE statement atomic per-statement, so partial progress on
|
|
455
|
+
-- crash is safe to retry thanks to the idempotency above.)
|
|
456
|
+
--
|
|
457
|
+
-- char(10) = newline, char(31) = Unit Separator. SQLite has no native
|
|
458
|
+
-- regex, but two nested replace() calls on the literal prefixes are
|
|
459
|
+
-- sufficient because both legacy chunk prefixes match at line-start.
|
|
460
|
+
--
|
|
461
|
+
-- Each row UPDATE fires the temporal_fts_update trigger once; because
|
|
462
|
+
-- the Unit Separator is a non-word character, the re-indexed content
|
|
463
|
+
-- tokenizes identically -- net no-op for FTS scoring.
|
|
464
|
+
UPDATE temporal_messages
|
|
465
|
+
SET content = replace(
|
|
466
|
+
replace(
|
|
467
|
+
content,
|
|
468
|
+
char(10) || '[tool:',
|
|
469
|
+
char(10) || char(31) || '[tool:'
|
|
470
|
+
),
|
|
471
|
+
char(10) || '[reasoning] ',
|
|
472
|
+
char(10) || char(31) || '[reasoning] '
|
|
473
|
+
)
|
|
474
|
+
WHERE content LIKE '%' || char(10) || '[tool:%'
|
|
475
|
+
OR content LIKE '%' || char(10) || '[reasoning] %';
|
|
422
476
|
`
|
|
423
477
|
];
|
|
424
478
|
function dataDir() {
|
|
@@ -442,6 +496,7 @@ function db() {
|
|
|
442
496
|
instance = new Database(path);
|
|
443
497
|
instance.exec("PRAGMA journal_mode = WAL");
|
|
444
498
|
instance.exec("PRAGMA foreign_keys = ON");
|
|
499
|
+
instance.exec("PRAGMA busy_timeout = 5000");
|
|
445
500
|
instance.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
446
501
|
migrate(instance);
|
|
447
502
|
return instance;
|
|
@@ -10748,12 +10803,27 @@ EXACT NUMBERS: When two segments report different numbers for what seems like th
|
|
|
10748
10803
|
|
|
10749
10804
|
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.
|
|
10750
10805
|
|
|
10806
|
+
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.
|
|
10807
|
+
|
|
10751
10808
|
Output ONLY an <observations> block with the consolidated observations.`;
|
|
10752
|
-
function recursiveUser(distillations) {
|
|
10809
|
+
function recursiveUser(distillations, previousMeta) {
|
|
10753
10810
|
const entries = distillations.map(
|
|
10754
10811
|
(d, i) => `Segment ${i + 1}:
|
|
10755
10812
|
${d.observations}`
|
|
10756
10813
|
);
|
|
10814
|
+
if (previousMeta) {
|
|
10815
|
+
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.
|
|
10816
|
+
|
|
10817
|
+
<previous-meta-summary>
|
|
10818
|
+
${previousMeta}
|
|
10819
|
+
</previous-meta-summary>
|
|
10820
|
+
|
|
10821
|
+
---
|
|
10822
|
+
|
|
10823
|
+
New observation segments to merge (chronological order):
|
|
10824
|
+
|
|
10825
|
+
${entries.join("\n\n---\n\n")}`;
|
|
10826
|
+
}
|
|
10757
10827
|
return `Observation segments to consolidate (chronological order):
|
|
10758
10828
|
|
|
10759
10829
|
${entries.join("\n\n---\n\n")}`;
|
|
@@ -10908,6 +10978,61 @@ function formatDistillations(distillations) {
|
|
|
10908
10978
|
}
|
|
10909
10979
|
return sections.join("\n\n");
|
|
10910
10980
|
}
|
|
10981
|
+
var COMPACT_SUMMARY_TEMPLATE = `Output exactly this Markdown structure. Keep every section in this order, even when empty (use "(none)").
|
|
10982
|
+
|
|
10983
|
+
---
|
|
10984
|
+
## Goal
|
|
10985
|
+
- [single-sentence task summary]
|
|
10986
|
+
|
|
10987
|
+
## Constraints & Preferences
|
|
10988
|
+
- [user constraints, preferences, specs, or "(none)"]
|
|
10989
|
+
|
|
10990
|
+
## Progress
|
|
10991
|
+
### Done
|
|
10992
|
+
- [completed work or "(none)"]
|
|
10993
|
+
|
|
10994
|
+
### In Progress
|
|
10995
|
+
- [current work or "(none)"]
|
|
10996
|
+
|
|
10997
|
+
### Blocked
|
|
10998
|
+
- [blockers or "(none)"]
|
|
10999
|
+
|
|
11000
|
+
## Key Decisions
|
|
11001
|
+
- [decision and why, or "(none)"]
|
|
11002
|
+
|
|
11003
|
+
## Next Steps
|
|
11004
|
+
- [ordered next actions or "(none)"]
|
|
11005
|
+
|
|
11006
|
+
## Critical Context
|
|
11007
|
+
- [important technical facts, errors, open questions, or "(none)"]
|
|
11008
|
+
|
|
11009
|
+
## Relevant Files
|
|
11010
|
+
- [file or directory path: why it matters, or "(none)"]
|
|
11011
|
+
---
|
|
11012
|
+
|
|
11013
|
+
Rules:
|
|
11014
|
+
- Keep every section, even when empty.
|
|
11015
|
+
- Use terse bullets, not prose paragraphs.
|
|
11016
|
+
- Preserve exact file paths, commands, error strings, and identifiers when known.
|
|
11017
|
+
- Do not mention the summary process or that context was compacted.
|
|
11018
|
+
- End with "I'm ready to continue." on its own line after the closing "---".`;
|
|
11019
|
+
function buildCompactPrompt(input) {
|
|
11020
|
+
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" : "";
|
|
11021
|
+
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.
|
|
11022
|
+
|
|
11023
|
+
<previous-summary>
|
|
11024
|
+
${input.previousSummary}
|
|
11025
|
+
</previous-summary>
|
|
11026
|
+
|
|
11027
|
+
` : "";
|
|
11028
|
+
const knowledgeBlock = input.knowledge ? `
|
|
11029
|
+
${input.knowledge}
|
|
11030
|
+
` : "";
|
|
11031
|
+
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.
|
|
11032
|
+
|
|
11033
|
+
${distillSection}${anchorBlock}${COMPACT_SUMMARY_TEMPLATE}
|
|
11034
|
+
${knowledgeBlock}`;
|
|
11035
|
+
}
|
|
10911
11036
|
function estimateTokens(text4) {
|
|
10912
11037
|
return Math.ceil(text4.length / 3);
|
|
10913
11038
|
}
|
|
@@ -11160,6 +11285,7 @@ function isToolPart(p3) {
|
|
|
11160
11285
|
function estimate(text4) {
|
|
11161
11286
|
return Math.ceil(text4.length / 3);
|
|
11162
11287
|
}
|
|
11288
|
+
var CHUNK_TERMINATOR = "";
|
|
11163
11289
|
function partsToText(parts) {
|
|
11164
11290
|
const chunks = [];
|
|
11165
11291
|
for (const part of parts) {
|
|
@@ -11169,7 +11295,7 @@ function partsToText(parts) {
|
|
|
11169
11295
|
else if (isToolPart(part) && part.state.status === "completed")
|
|
11170
11296
|
chunks.push(`[tool:${part.tool}] ${part.state.output}`);
|
|
11171
11297
|
}
|
|
11172
|
-
return sanitizeSurrogates(chunks.join("\n"));
|
|
11298
|
+
return sanitizeSurrogates(chunks.join("\n" + CHUNK_TERMINATOR));
|
|
11173
11299
|
}
|
|
11174
11300
|
function messageMetadata(info2, parts) {
|
|
11175
11301
|
const meta3 = {};
|
|
@@ -11248,11 +11374,11 @@ function search2(input) {
|
|
|
11248
11374
|
const limit = input.limit ?? 20;
|
|
11249
11375
|
const q = ftsQuery(input.query);
|
|
11250
11376
|
if (q === EMPTY_QUERY) return [];
|
|
11251
|
-
const ftsSQL = input.sessionID ? `SELECT m.* FROM
|
|
11252
|
-
JOIN
|
|
11377
|
+
const ftsSQL = input.sessionID ? `SELECT m.* FROM temporal_fts f
|
|
11378
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11253
11379
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11254
|
-
ORDER BY rank LIMIT ?` : `SELECT m.* FROM
|
|
11255
|
-
JOIN
|
|
11380
|
+
ORDER BY rank LIMIT ?` : `SELECT m.* FROM temporal_fts f
|
|
11381
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11256
11382
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11257
11383
|
ORDER BY rank LIMIT ?`;
|
|
11258
11384
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -11277,11 +11403,11 @@ function searchScored(input) {
|
|
|
11277
11403
|
const limit = input.limit ?? 20;
|
|
11278
11404
|
const q = ftsQuery(input.query);
|
|
11279
11405
|
if (q === EMPTY_QUERY) return [];
|
|
11280
|
-
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM
|
|
11281
|
-
JOIN
|
|
11406
|
+
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM temporal_fts f
|
|
11407
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11282
11408
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11283
|
-
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM
|
|
11284
|
-
JOIN
|
|
11409
|
+
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM temporal_fts f
|
|
11410
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11285
11411
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11286
11412
|
ORDER BY rank LIMIT ?`;
|
|
11287
11413
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -25483,18 +25609,63 @@ var LoreConfig = external_exports.object({
|
|
|
25483
25609
|
providerID: external_exports.string(),
|
|
25484
25610
|
modelID: external_exports.string()
|
|
25485
25611
|
}).optional(),
|
|
25612
|
+
/** Explicit worker model override. When set, all background workers (distillation,
|
|
25613
|
+
* curation, query expansion) use this model instead of the session model or the
|
|
25614
|
+
* auto-selected worker model. Bypasses dynamic worker model selection entirely. */
|
|
25615
|
+
workerModel: external_exports.object({
|
|
25616
|
+
providerID: external_exports.string(),
|
|
25617
|
+
modelID: external_exports.string()
|
|
25618
|
+
}).optional(),
|
|
25486
25619
|
budget: external_exports.object({
|
|
25487
25620
|
distilled: external_exports.number().min(0.05).max(0.5).default(0.25),
|
|
25488
25621
|
raw: external_exports.number().min(0.1).max(0.7).default(0.4),
|
|
25489
25622
|
output: external_exports.number().min(0.1).max(0.5).default(0.25),
|
|
25490
|
-
/** Max fraction of usable context reserved for LTM system-prompt injection. Default: 0.
|
|
25491
|
-
ltm: external_exports.number().min(0.02).max(0.3).default(0.
|
|
25492
|
-
|
|
25623
|
+
/** Max fraction of usable context reserved for LTM system-prompt injection. Default: 0.05 (5%). */
|
|
25624
|
+
ltm: external_exports.number().min(0.02).max(0.3).default(0.05),
|
|
25625
|
+
/** Per-turn cache-read cost target in dollars. Controls when layer 0 (full
|
|
25626
|
+
* passthrough) escalates to layer 1 (compressed). The cap is derived as:
|
|
25627
|
+
* maxLayer0Tokens = max(target / model.cost.cache.read, 40K).
|
|
25628
|
+
* Lower = cheaper but earlier compression. Default: 0.10. Set to 0 to
|
|
25629
|
+
* disable cost-aware capping (use the model's full context). */
|
|
25630
|
+
targetCacheReadCostPerTurn: external_exports.number().min(0).default(0.1),
|
|
25631
|
+
/** Direct override for the layer-0 token cap. When set, bypasses the
|
|
25632
|
+
* cost-aware formula from targetCacheReadCostPerTurn. 0 = disabled
|
|
25633
|
+
* (no cap, use full context). Default: undefined (use cost-aware auto). */
|
|
25634
|
+
maxLayer0Tokens: external_exports.number().min(0).optional()
|
|
25635
|
+
}).default({ distilled: 0.25, raw: 0.4, output: 0.25, ltm: 0.05, targetCacheReadCostPerTurn: 0.1 }),
|
|
25636
|
+
/**
|
|
25637
|
+
* Cold-cache idle-resume handling.
|
|
25638
|
+
*
|
|
25639
|
+
* Anthropic's prompt cache evicts entries after ~5 min (default tier) /
|
|
25640
|
+
* ~1 hour (extended tier). When a session resumes after the eviction window,
|
|
25641
|
+
* Lore's byte-identity caches (distilled prefix, raw window pin, LTM block)
|
|
25642
|
+
* are providing no value because the underlying provider cache is already
|
|
25643
|
+
* cold. On detection, Lore refreshes those caches so the next turn can
|
|
25644
|
+
* produce a better-fitting window without paying a cache cost it would
|
|
25645
|
+
* otherwise be trying to preserve. Reasoning blocks are NOT touched —
|
|
25646
|
+
* Anthropic's April 23 postmortem identified dropping reasoning blocks as
|
|
25647
|
+
* the root cause of forgetfulness/repetition.
|
|
25648
|
+
*
|
|
25649
|
+
* `idleResumeMinutes` is the threshold in minutes. Default 60 — matches
|
|
25650
|
+
* Anthropic's extended-cache eviction window, conservative across providers.
|
|
25651
|
+
* Set to 0 to disable the feature.
|
|
25652
|
+
*/
|
|
25653
|
+
idleResumeMinutes: external_exports.number().min(0).max(24 * 60).default(60),
|
|
25493
25654
|
distillation: external_exports.object({
|
|
25494
|
-
minMessages: external_exports.number().min(3).default(
|
|
25495
|
-
maxSegment: external_exports.number().min(5).default(
|
|
25496
|
-
metaThreshold: external_exports.number().min(3).default(10)
|
|
25497
|
-
|
|
25655
|
+
minMessages: external_exports.number().min(3).default(5),
|
|
25656
|
+
maxSegment: external_exports.number().min(5).default(30),
|
|
25657
|
+
metaThreshold: external_exports.number().min(3).default(10),
|
|
25658
|
+
/** Max chars per tool output when rendering temporal messages for distillation input.
|
|
25659
|
+
* Outputs longer than this are replaced with a compact annotation preserving line
|
|
25660
|
+
* count, error signals, and file paths. Default: 2000 (matches upstream OpenCode's
|
|
25661
|
+
* TOOL_OUTPUT_MAX_CHARS during compaction). Set to 0 to disable. */
|
|
25662
|
+
toolOutputMaxChars: external_exports.number().min(0).default(2e3)
|
|
25663
|
+
}).default({
|
|
25664
|
+
minMessages: 5,
|
|
25665
|
+
maxSegment: 30,
|
|
25666
|
+
metaThreshold: 10,
|
|
25667
|
+
toolOutputMaxChars: 2e3
|
|
25668
|
+
}),
|
|
25498
25669
|
knowledge: external_exports.object({
|
|
25499
25670
|
/** Set to false to disable long-term knowledge storage and system-prompt injection.
|
|
25500
25671
|
* Conversation recall (temporal search, distillation search) and context management
|
|
@@ -25599,6 +25770,7 @@ __export(embedding_exports, {
|
|
|
25599
25770
|
vectorSearch: () => vectorSearch,
|
|
25600
25771
|
vectorSearchDistillations: () => vectorSearchDistillations
|
|
25601
25772
|
});
|
|
25773
|
+
var EMBED_TIMEOUT_MS = 1e4;
|
|
25602
25774
|
var VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
|
|
25603
25775
|
var VoyageProvider = class {
|
|
25604
25776
|
maxBatchSize = 128;
|
|
@@ -25622,7 +25794,8 @@ var VoyageProvider = class {
|
|
|
25622
25794
|
model: this.model,
|
|
25623
25795
|
input_type: inputType,
|
|
25624
25796
|
output_dimension: this.dimensions
|
|
25625
|
-
})
|
|
25797
|
+
}),
|
|
25798
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25626
25799
|
});
|
|
25627
25800
|
if (!res.ok) {
|
|
25628
25801
|
const body = await res.text().catch(() => "");
|
|
@@ -25658,7 +25831,8 @@ var OpenAIProvider = class {
|
|
|
25658
25831
|
"Content-Type": "application/json",
|
|
25659
25832
|
Authorization: `Bearer ${this.apiKey}`
|
|
25660
25833
|
},
|
|
25661
|
-
body: JSON.stringify(body)
|
|
25834
|
+
body: JSON.stringify(body),
|
|
25835
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25662
25836
|
});
|
|
25663
25837
|
if (!res.ok) {
|
|
25664
25838
|
const responseBody = await res.text().catch(() => "");
|
|
@@ -26041,8 +26215,8 @@ function searchScored2(input) {
|
|
|
26041
26215
|
const ftsSQL = `SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26042
26216
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26043
26217
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26044
|
-
FROM
|
|
26045
|
-
JOIN
|
|
26218
|
+
FROM lat_sections_fts f
|
|
26219
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26046
26220
|
WHERE lat_sections_fts MATCH ?
|
|
26047
26221
|
AND s.project_id = ?
|
|
26048
26222
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26068,8 +26242,8 @@ function scoreForSession(projectPath, sessionContext, maxTokens) {
|
|
|
26068
26242
|
`SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26069
26243
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26070
26244
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26071
|
-
FROM
|
|
26072
|
-
JOIN
|
|
26245
|
+
FROM lat_sections_fts f
|
|
26246
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26073
26247
|
WHERE lat_sections_fts MATCH ?
|
|
26074
26248
|
AND s.project_id = ?
|
|
26075
26249
|
ORDER BY rank`
|
|
@@ -26198,10 +26372,10 @@ function scoreEntriesFTS(sessionContext) {
|
|
|
26198
26372
|
try {
|
|
26199
26373
|
const results = db().query(
|
|
26200
26374
|
`SELECT k.id, bm25(knowledge_fts, ?, ?, ?) as rank
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
|
|
26375
|
+
FROM knowledge_fts f
|
|
26376
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26377
|
+
WHERE knowledge_fts MATCH ?
|
|
26378
|
+
AND k.confidence > 0.2`
|
|
26205
26379
|
).all(title, content3, category, q);
|
|
26206
26380
|
if (!results.length) return /* @__PURE__ */ new Map();
|
|
26207
26381
|
const ranks = results.map((r) => r.rank);
|
|
@@ -26335,13 +26509,13 @@ function search3(input) {
|
|
|
26335
26509
|
const q = ftsQuery(input.query);
|
|
26336
26510
|
if (q === EMPTY_QUERY) return [];
|
|
26337
26511
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26338
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26339
|
-
JOIN
|
|
26512
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26513
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26340
26514
|
WHERE knowledge_fts MATCH ?
|
|
26341
26515
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26342
26516
|
AND k.confidence > 0.2
|
|
26343
|
-
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26344
|
-
JOIN
|
|
26517
|
+
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26518
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26345
26519
|
WHERE knowledge_fts MATCH ?
|
|
26346
26520
|
AND k.confidence > 0.2
|
|
26347
26521
|
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?`;
|
|
@@ -26368,13 +26542,13 @@ function searchScored3(input) {
|
|
|
26368
26542
|
if (q === EMPTY_QUERY) return [];
|
|
26369
26543
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26370
26544
|
const { title, content: content3, category } = ftsWeights();
|
|
26371
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26372
|
-
JOIN
|
|
26545
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26546
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26373
26547
|
WHERE knowledge_fts MATCH ?
|
|
26374
26548
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26375
26549
|
AND k.confidence > 0.2
|
|
26376
|
-
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26377
|
-
JOIN
|
|
26550
|
+
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26551
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26378
26552
|
WHERE knowledge_fts MATCH ?
|
|
26379
26553
|
AND k.confidence > 0.2
|
|
26380
26554
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26396,8 +26570,8 @@ function searchScoredOtherProjects(input) {
|
|
|
26396
26570
|
if (q === EMPTY_QUERY) return [];
|
|
26397
26571
|
const excludePid = ensureProject(input.excludeProjectPath);
|
|
26398
26572
|
const { title, content: content3, category } = ftsWeights();
|
|
26399
|
-
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26400
|
-
JOIN
|
|
26573
|
+
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26574
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26401
26575
|
WHERE knowledge_fts MATCH ?
|
|
26402
26576
|
AND k.project_id IS NOT NULL
|
|
26403
26577
|
AND k.project_id != ?
|
|
@@ -26530,8 +26704,8 @@ function check2(projectPath) {
|
|
|
26530
26704
|
try {
|
|
26531
26705
|
const { title, content: content3, category } = config2().search.ftsWeights;
|
|
26532
26706
|
const matches = db().query(
|
|
26533
|
-
`SELECT k.id, k.title FROM
|
|
26534
|
-
JOIN
|
|
26707
|
+
`SELECT k.id, k.title FROM knowledge_fts f
|
|
26708
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26535
26709
|
WHERE knowledge_fts MATCH ?
|
|
26536
26710
|
AND k.id != ?
|
|
26537
26711
|
AND k.confidence > 0.2
|
|
@@ -26565,9 +26739,13 @@ function check2(projectPath) {
|
|
|
26565
26739
|
// src/distillation.ts
|
|
26566
26740
|
var distillation_exports = {};
|
|
26567
26741
|
__export(distillation_exports, {
|
|
26742
|
+
latestMetaObservations: () => latestMetaObservations,
|
|
26568
26743
|
loadForSession: () => loadForSession,
|
|
26744
|
+
messagesToText: () => messagesToText,
|
|
26745
|
+
metaDistill: () => metaDistill,
|
|
26569
26746
|
parseSourceIds: () => parseSourceIds,
|
|
26570
26747
|
run: () => run,
|
|
26748
|
+
truncateToolOutputsInContent: () => truncateToolOutputsInContent,
|
|
26571
26749
|
workerSessionIDs: () => workerSessionIDs
|
|
26572
26750
|
});
|
|
26573
26751
|
|
|
@@ -26592,6 +26770,8 @@ function estimateMessage(msg) {
|
|
|
26592
26770
|
}
|
|
26593
26771
|
var contextLimit = 2e5;
|
|
26594
26772
|
var outputReserved = 32e3;
|
|
26773
|
+
var maxLayer0Tokens = 0;
|
|
26774
|
+
var MIN_LAYER0_FLOOR = 4e4;
|
|
26595
26775
|
var FIRST_TURN_OVERHEAD = 15e3;
|
|
26596
26776
|
var calibratedOverhead = null;
|
|
26597
26777
|
function makeSessionState() {
|
|
@@ -26605,7 +26785,11 @@ function makeSessionState() {
|
|
|
26605
26785
|
forceMinLayer: 0,
|
|
26606
26786
|
lastTransformEstimate: 0,
|
|
26607
26787
|
prefixCache: null,
|
|
26608
|
-
rawWindowCache: null
|
|
26788
|
+
rawWindowCache: null,
|
|
26789
|
+
lastTurnAt: 0,
|
|
26790
|
+
cameOutOfIdle: false,
|
|
26791
|
+
consecutiveHighLayer: 0,
|
|
26792
|
+
lastPrefixHash: ""
|
|
26609
26793
|
};
|
|
26610
26794
|
}
|
|
26611
26795
|
var sessionStates = /* @__PURE__ */ new Map();
|
|
@@ -26618,11 +26802,36 @@ function getSessionState(sessionID) {
|
|
|
26618
26802
|
}
|
|
26619
26803
|
return state;
|
|
26620
26804
|
}
|
|
26805
|
+
function onIdleResume(sessionID, thresholdMs, now = Date.now()) {
|
|
26806
|
+
if (thresholdMs <= 0) return { triggered: false };
|
|
26807
|
+
const state = getSessionState(sessionID);
|
|
26808
|
+
if (state.lastTurnAt === 0) return { triggered: false };
|
|
26809
|
+
const idleMs = now - state.lastTurnAt;
|
|
26810
|
+
if (idleMs < thresholdMs) return { triggered: false };
|
|
26811
|
+
state.prefixCache = null;
|
|
26812
|
+
state.rawWindowCache = null;
|
|
26813
|
+
state.cameOutOfIdle = true;
|
|
26814
|
+
return { triggered: true, idleMs };
|
|
26815
|
+
}
|
|
26816
|
+
function consumeCameOutOfIdle(sessionID) {
|
|
26817
|
+
const state = sessionStates.get(sessionID);
|
|
26818
|
+
if (!state || !state.cameOutOfIdle) return false;
|
|
26819
|
+
state.cameOutOfIdle = false;
|
|
26820
|
+
return true;
|
|
26821
|
+
}
|
|
26621
26822
|
var ltmTokens = 0;
|
|
26622
26823
|
function setModelLimits(limits) {
|
|
26623
26824
|
contextLimit = limits.context || 2e5;
|
|
26624
26825
|
outputReserved = Math.min(limits.output || 32e3, 32e3);
|
|
26625
26826
|
}
|
|
26827
|
+
function setMaxLayer0Tokens(tokens) {
|
|
26828
|
+
maxLayer0Tokens = Math.max(0, Math.floor(tokens));
|
|
26829
|
+
}
|
|
26830
|
+
function computeLayer0Cap(targetCostPerTurn, cacheReadCostPerToken) {
|
|
26831
|
+
if (targetCostPerTurn <= 0 || cacheReadCostPerToken <= 0) return 0;
|
|
26832
|
+
const rawCap = Math.floor(targetCostPerTurn / cacheReadCostPerToken);
|
|
26833
|
+
return Math.max(rawCap, MIN_LAYER0_FLOOR);
|
|
26834
|
+
}
|
|
26626
26835
|
function setLtmTokens(tokens) {
|
|
26627
26836
|
ltmTokens = tokens;
|
|
26628
26837
|
}
|
|
@@ -26667,6 +26876,19 @@ function setForceMinLayer(layer, sessionID) {
|
|
|
26667
26876
|
}
|
|
26668
26877
|
}
|
|
26669
26878
|
}
|
|
26879
|
+
function inspectSessionState(sessionID) {
|
|
26880
|
+
const state = sessionStates.get(sessionID);
|
|
26881
|
+
if (!state) return null;
|
|
26882
|
+
return {
|
|
26883
|
+
hasPrefixCache: state.prefixCache !== null,
|
|
26884
|
+
hasRawWindowCache: state.rawWindowCache !== null,
|
|
26885
|
+
cameOutOfIdle: state.cameOutOfIdle,
|
|
26886
|
+
lastTurnAt: state.lastTurnAt
|
|
26887
|
+
};
|
|
26888
|
+
}
|
|
26889
|
+
function setLastTurnAtForTest(sessionID, ms) {
|
|
26890
|
+
getSessionState(sessionID).lastTurnAt = ms;
|
|
26891
|
+
}
|
|
26670
26892
|
function loadDistillations(projectPath, sessionID) {
|
|
26671
26893
|
const pid = ensureProject(projectPath);
|
|
26672
26894
|
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";
|
|
@@ -26699,12 +26921,17 @@ function cleanParts(parts) {
|
|
|
26699
26921
|
}
|
|
26700
26922
|
return filtered.length > 0 ? filtered : parts;
|
|
26701
26923
|
}
|
|
26924
|
+
var ANNOTATION_PATH_SCAN_LIMIT = 64 * 1024;
|
|
26925
|
+
var PATH_RE = /(?:[\w.-]+\/)+[\w.-]+\.\w{1,5}/g;
|
|
26702
26926
|
function toolStripAnnotation(toolName, output) {
|
|
26703
26927
|
const lines = output.split("\n").length;
|
|
26704
|
-
const chars = output.length;
|
|
26705
26928
|
const hasError = /\b(?:error|fail(?:ed|ure)?|exception|panic|traceback)\b/i.test(output);
|
|
26706
|
-
|
|
26707
|
-
|
|
26929
|
+
let uniquePaths = [];
|
|
26930
|
+
if (output.indexOf("/") !== -1) {
|
|
26931
|
+
const pathScan = output.length > ANNOTATION_PATH_SCAN_LIMIT ? output.slice(0, ANNOTATION_PATH_SCAN_LIMIT) : output;
|
|
26932
|
+
const paths = pathScan.match(PATH_RE);
|
|
26933
|
+
if (paths) uniquePaths = [...new Set(paths)].slice(0, 5);
|
|
26934
|
+
}
|
|
26708
26935
|
let annotation = `[output omitted \u2014 ${toolName}: ${lines} lines`;
|
|
26709
26936
|
if (hasError) annotation += ", contained errors";
|
|
26710
26937
|
if (uniquePaths.length > 0) annotation += `, paths: ${uniquePaths.join(", ")}`;
|
|
@@ -27135,7 +27362,11 @@ function transformInner(input) {
|
|
|
27135
27362
|
expectedInput = messageTokens + overhead + ltmTokens;
|
|
27136
27363
|
}
|
|
27137
27364
|
const layer0Input = calibrated ? expectedInput : expectedInput * UNCALIBRATED_SAFETY;
|
|
27138
|
-
|
|
27365
|
+
let layer0Ceiling = maxLayer0Tokens > 0 ? Math.min(maxInput, maxLayer0Tokens) : maxInput;
|
|
27366
|
+
if (!calibrated && layer0Ceiling < maxInput) {
|
|
27367
|
+
layer0Ceiling = Math.floor(layer0Ceiling * 0.7);
|
|
27368
|
+
}
|
|
27369
|
+
if (effectiveMinLayer === 0 && layer0Input <= layer0Ceiling) {
|
|
27139
27370
|
const messageTokens = calibrated ? expectedInput - (ltmTokens - sessState.lastKnownLtm) : expectedInput - overhead - ltmTokens;
|
|
27140
27371
|
return {
|
|
27141
27372
|
messages: input.messages,
|
|
@@ -27216,14 +27447,31 @@ function transformInner(input) {
|
|
|
27216
27447
|
(sum, m) => sum + estimateMessage(m),
|
|
27217
27448
|
0
|
|
27218
27449
|
);
|
|
27219
|
-
const
|
|
27450
|
+
const tailBudget = Math.max(2e3, Math.min(8e3, Math.floor(usable * 0.25)));
|
|
27451
|
+
const nuclearTurnStart = currentTurnStart(input.messages);
|
|
27452
|
+
const currentTurn = input.messages.slice(nuclearTurnStart).map((m) => ({
|
|
27220
27453
|
info: m.info,
|
|
27221
27454
|
parts: cleanParts(m.parts)
|
|
27222
27455
|
}));
|
|
27223
|
-
const
|
|
27456
|
+
const currentTurnTokens = currentTurn.reduce(
|
|
27224
27457
|
(sum, m) => sum + estimateMessage(m),
|
|
27225
27458
|
0
|
|
27226
27459
|
);
|
|
27460
|
+
const olderMessages = [];
|
|
27461
|
+
let olderTokens = 0;
|
|
27462
|
+
const remaining = Math.max(0, tailBudget - currentTurnTokens);
|
|
27463
|
+
for (let i = nuclearTurnStart - 1; i >= 0 && olderTokens < remaining; i--) {
|
|
27464
|
+
const msg = input.messages[i];
|
|
27465
|
+
const est = estimateMessage(msg);
|
|
27466
|
+
if (olderTokens + est > remaining) break;
|
|
27467
|
+
olderMessages.unshift({
|
|
27468
|
+
info: msg.info,
|
|
27469
|
+
parts: cleanParts(msg.parts)
|
|
27470
|
+
});
|
|
27471
|
+
olderTokens += est;
|
|
27472
|
+
}
|
|
27473
|
+
const nuclearRaw = [...olderMessages, ...currentTurn];
|
|
27474
|
+
const nuclearRawTokens = olderTokens + currentTurnTokens;
|
|
27227
27475
|
return {
|
|
27228
27476
|
messages: [...nuclearPrefix, ...nuclearRaw],
|
|
27229
27477
|
layer: 4,
|
|
@@ -27245,6 +27493,28 @@ function transform2(input) {
|
|
|
27245
27493
|
state.lastTransformEstimate = result.totalTokens;
|
|
27246
27494
|
state.lastLayer = result.layer;
|
|
27247
27495
|
state.lastWindowMessageIDs = new Set(result.messages.map((m) => m.info.id));
|
|
27496
|
+
state.lastTurnAt = Date.now();
|
|
27497
|
+
const prefixIds = result.messages.slice(0, 5).map((m) => m.info.id).join(",");
|
|
27498
|
+
const prefixHash = `${result.layer}:${prefixIds}`;
|
|
27499
|
+
if (state.lastPrefixHash && state.lastPrefixHash !== prefixHash) {
|
|
27500
|
+
info(
|
|
27501
|
+
`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)}`
|
|
27502
|
+
);
|
|
27503
|
+
}
|
|
27504
|
+
state.lastPrefixHash = prefixHash;
|
|
27505
|
+
if (result.layer >= 2) {
|
|
27506
|
+
state.consecutiveHighLayer++;
|
|
27507
|
+
if (state.consecutiveHighLayer === 3) {
|
|
27508
|
+
info(
|
|
27509
|
+
`session ${sid} has been at gradient layer ${result.layer}+ for 3 consecutive turns. Consider running /compact to reset the context window.`
|
|
27510
|
+
);
|
|
27511
|
+
}
|
|
27512
|
+
} else {
|
|
27513
|
+
state.consecutiveHighLayer = 0;
|
|
27514
|
+
}
|
|
27515
|
+
info(
|
|
27516
|
+
`gradient: session=${sid} layer=${result.layer} tokens=${result.totalTokens} (distilled=${result.distilledTokens} raw=${result.rawTokens}) usable=${result.usable} cap=${maxLayer0Tokens || "off"}`
|
|
27517
|
+
);
|
|
27248
27518
|
}
|
|
27249
27519
|
return result;
|
|
27250
27520
|
}
|
|
@@ -27338,8 +27608,39 @@ function formatTime(ms) {
|
|
|
27338
27608
|
const m = d.getMinutes().toString().padStart(2, "0");
|
|
27339
27609
|
return `${h3}:${m}`;
|
|
27340
27610
|
}
|
|
27341
|
-
|
|
27342
|
-
|
|
27611
|
+
var CHUNK_SEPARATOR = "\n" + CHUNK_TERMINATOR;
|
|
27612
|
+
function truncateToolOutputsInContent(content3, maxChars) {
|
|
27613
|
+
if (maxChars <= 0 || content3.length === 0) return content3;
|
|
27614
|
+
if (content3.indexOf(CHUNK_TERMINATOR) === -1) {
|
|
27615
|
+
return truncateSingleChunk(content3, maxChars);
|
|
27616
|
+
}
|
|
27617
|
+
const chunks = content3.split(CHUNK_SEPARATOR);
|
|
27618
|
+
let anyToolChunk = false;
|
|
27619
|
+
for (const c of chunks) {
|
|
27620
|
+
if (c.startsWith("[tool:")) {
|
|
27621
|
+
anyToolChunk = true;
|
|
27622
|
+
break;
|
|
27623
|
+
}
|
|
27624
|
+
}
|
|
27625
|
+
if (!anyToolChunk) return content3;
|
|
27626
|
+
const out = chunks.map((chunk) => truncateSingleChunk(chunk, maxChars));
|
|
27627
|
+
return out.join(CHUNK_SEPARATOR);
|
|
27628
|
+
}
|
|
27629
|
+
function truncateSingleChunk(chunk, maxChars) {
|
|
27630
|
+
if (!chunk.startsWith("[tool:")) return chunk;
|
|
27631
|
+
const closeBracket = chunk.indexOf("] ");
|
|
27632
|
+
if (closeBracket < 0) return chunk;
|
|
27633
|
+
const toolName = chunk.slice(6, closeBracket);
|
|
27634
|
+
const payload = chunk.slice(closeBracket + 2);
|
|
27635
|
+
if (payload.length <= maxChars) return chunk;
|
|
27636
|
+
return `[tool:${toolName}] ${toolStripAnnotation(toolName, payload)}`;
|
|
27637
|
+
}
|
|
27638
|
+
function messagesToText(messages, toolOutputMaxChars) {
|
|
27639
|
+
const cap = toolOutputMaxChars ?? config2().distillation.toolOutputMaxChars;
|
|
27640
|
+
return messages.map((m) => {
|
|
27641
|
+
const body = m.role === "user" ? m.content : truncateToolOutputsInContent(m.content, cap);
|
|
27642
|
+
return `[${m.role}] (${formatTime(m.created_at)}) ${body}`;
|
|
27643
|
+
}).join("\n\n");
|
|
27343
27644
|
}
|
|
27344
27645
|
function parseDistillationResult(text4) {
|
|
27345
27646
|
const match = text4.match(/<observations>([\s\S]*?)<\/observations>/i);
|
|
@@ -27354,6 +27655,19 @@ function latestObservations(projectPath, sessionID) {
|
|
|
27354
27655
|
).get(pid, sessionID);
|
|
27355
27656
|
return row?.observations || void 0;
|
|
27356
27657
|
}
|
|
27658
|
+
function latestMetaObservations(projectPath, sessionID) {
|
|
27659
|
+
return latestMeta(projectPath, sessionID)?.observations;
|
|
27660
|
+
}
|
|
27661
|
+
function latestMeta(projectPath, sessionID) {
|
|
27662
|
+
const pid = ensureProject(projectPath);
|
|
27663
|
+
const row = db().query(
|
|
27664
|
+
`SELECT observations, generation FROM distillations
|
|
27665
|
+
WHERE project_id = ? AND session_id = ? AND generation > 0
|
|
27666
|
+
ORDER BY generation DESC, created_at DESC LIMIT 1`
|
|
27667
|
+
).get(pid, sessionID);
|
|
27668
|
+
if (!row || !row.observations) return void 0;
|
|
27669
|
+
return row;
|
|
27670
|
+
}
|
|
27357
27671
|
function parseSourceIds(raw) {
|
|
27358
27672
|
try {
|
|
27359
27673
|
const parsed = JSON.parse(raw);
|
|
@@ -27363,11 +27677,10 @@ function parseSourceIds(raw) {
|
|
|
27363
27677
|
return [];
|
|
27364
27678
|
}
|
|
27365
27679
|
}
|
|
27366
|
-
function loadForSession(projectPath, sessionID) {
|
|
27680
|
+
function loadForSession(projectPath, sessionID, includeArchived = false) {
|
|
27367
27681
|
const pid = ensureProject(projectPath);
|
|
27368
|
-
const
|
|
27369
|
-
|
|
27370
|
-
).all(pid, sessionID);
|
|
27682
|
+
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";
|
|
27683
|
+
const rows = db().query(sql).all(pid, sessionID);
|
|
27371
27684
|
return rows.map((r) => ({
|
|
27372
27685
|
...r,
|
|
27373
27686
|
source_ids: parseSourceIds(r.source_ids)
|
|
@@ -27532,8 +27845,13 @@ async function distillSegment(input) {
|
|
|
27532
27845
|
}
|
|
27533
27846
|
async function metaDistill(input) {
|
|
27534
27847
|
const existing = loadGen0(input.projectPath, input.sessionID);
|
|
27535
|
-
|
|
27536
|
-
|
|
27848
|
+
const priorMeta = latestMeta(input.projectPath, input.sessionID);
|
|
27849
|
+
if (priorMeta) {
|
|
27850
|
+
if (existing.length === 0) return null;
|
|
27851
|
+
} else {
|
|
27852
|
+
if (existing.length < 3) return null;
|
|
27853
|
+
}
|
|
27854
|
+
const userContent = recursiveUser(existing, priorMeta?.observations);
|
|
27537
27855
|
const model = input.model ?? config2().model;
|
|
27538
27856
|
const responseText = await input.llm.prompt(
|
|
27539
27857
|
RECURSIVE_SYSTEM,
|
|
@@ -27543,19 +27861,30 @@ async function metaDistill(input) {
|
|
|
27543
27861
|
if (!responseText) return null;
|
|
27544
27862
|
const result = parseDistillationResult(responseText);
|
|
27545
27863
|
if (!result) return null;
|
|
27546
|
-
const maxGen = Math.max(
|
|
27864
|
+
const maxGen = Math.max(
|
|
27865
|
+
...existing.map((d) => d.generation),
|
|
27866
|
+
priorMeta?.generation ?? 0
|
|
27867
|
+
);
|
|
27547
27868
|
const allSourceIDs = existing.flatMap((d) => d.source_ids);
|
|
27548
|
-
|
|
27549
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
27554
|
-
|
|
27869
|
+
let metaId;
|
|
27870
|
+
db().exec("BEGIN IMMEDIATE");
|
|
27871
|
+
try {
|
|
27872
|
+
metaId = storeDistillation({
|
|
27873
|
+
projectPath: input.projectPath,
|
|
27874
|
+
sessionID: input.sessionID,
|
|
27875
|
+
observations: result.observations,
|
|
27876
|
+
sourceIDs: allSourceIDs,
|
|
27877
|
+
generation: maxGen + 1
|
|
27878
|
+
});
|
|
27879
|
+
archiveDistillations(existing.map((d) => d.id));
|
|
27880
|
+
db().exec("COMMIT");
|
|
27881
|
+
} catch (e) {
|
|
27882
|
+
db().exec("ROLLBACK");
|
|
27883
|
+
throw e;
|
|
27884
|
+
}
|
|
27555
27885
|
if (isAvailable()) {
|
|
27556
27886
|
embedDistillation(metaId, result.observations);
|
|
27557
27887
|
}
|
|
27558
|
-
archiveDistillations(existing.map((d) => d.id));
|
|
27559
27888
|
return result;
|
|
27560
27889
|
}
|
|
27561
27890
|
|
|
@@ -27714,13 +28043,13 @@ function searchDistillationsScored(input) {
|
|
|
27714
28043
|
const q = ftsQuery(input.query);
|
|
27715
28044
|
if (q === EMPTY_QUERY) return [];
|
|
27716
28045
|
const ftsSQL = input.sessionID ? `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27717
|
-
FROM
|
|
27718
|
-
JOIN
|
|
28046
|
+
FROM distillation_fts f
|
|
28047
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27719
28048
|
WHERE distillation_fts MATCH ?
|
|
27720
28049
|
AND d.project_id = ? AND d.session_id = ?
|
|
27721
28050
|
ORDER BY rank LIMIT ?` : `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27722
|
-
FROM
|
|
27723
|
-
JOIN
|
|
28051
|
+
FROM distillation_fts f
|
|
28052
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27724
28053
|
WHERE distillation_fts MATCH ?
|
|
27725
28054
|
AND d.project_id = ?
|
|
27726
28055
|
ORDER BY rank LIMIT ?`;
|
|
@@ -28163,7 +28492,130 @@ function importFromFile(input) {
|
|
|
28163
28492
|
}
|
|
28164
28493
|
}
|
|
28165
28494
|
}
|
|
28495
|
+
|
|
28496
|
+
// src/worker-model.ts
|
|
28497
|
+
var worker_model_exports = {};
|
|
28498
|
+
__export(worker_model_exports, {
|
|
28499
|
+
WORKER_JUDGE_SYSTEM: () => WORKER_JUDGE_SYSTEM,
|
|
28500
|
+
computeModelFingerprint: () => computeModelFingerprint,
|
|
28501
|
+
getValidatedWorkerModel: () => getValidatedWorkerModel,
|
|
28502
|
+
isValidationStale: () => isValidationStale,
|
|
28503
|
+
parseJudgeScore: () => parseJudgeScore,
|
|
28504
|
+
resolveWorkerModel: () => resolveWorkerModel,
|
|
28505
|
+
selectWorkerCandidates: () => selectWorkerCandidates,
|
|
28506
|
+
storeValidatedWorkerModel: () => storeValidatedWorkerModel,
|
|
28507
|
+
structuralCheck: () => structuralCheck,
|
|
28508
|
+
workerJudgeUser: () => workerJudgeUser
|
|
28509
|
+
});
|
|
28510
|
+
var KV_PREFIX = "lore:worker_model:";
|
|
28511
|
+
function selectWorkerCandidates(sessionModel, providerModels) {
|
|
28512
|
+
const eligible = providerModels.filter(
|
|
28513
|
+
(m) => m.providerID === sessionModel.providerID && m.status === "active" && m.capabilities.input.text
|
|
28514
|
+
);
|
|
28515
|
+
if (eligible.length === 0) return [];
|
|
28516
|
+
const sorted = [...eligible].sort((a, b) => a.cost.input - b.cost.input);
|
|
28517
|
+
const cheapest = sorted[0];
|
|
28518
|
+
const belowSession = sorted.filter((m) => m.cost.input < sessionModel.cost.input).pop();
|
|
28519
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
28520
|
+
candidates.set(cheapest.id, cheapest);
|
|
28521
|
+
if (belowSession && belowSession.id !== cheapest.id) {
|
|
28522
|
+
candidates.set(belowSession.id, belowSession);
|
|
28523
|
+
}
|
|
28524
|
+
if (cheapest.id === sessionModel.id || cheapest.cost.input >= sessionModel.cost.input) {
|
|
28525
|
+
return [cheapest];
|
|
28526
|
+
}
|
|
28527
|
+
return [...candidates.values()];
|
|
28528
|
+
}
|
|
28529
|
+
function computeModelFingerprint(providerID, sessionModelID, activeModelIDs) {
|
|
28530
|
+
const sorted = [...activeModelIDs].sort();
|
|
28531
|
+
return sha256(
|
|
28532
|
+
JSON.stringify({ providerID, sessionModelID, modelIDs: sorted })
|
|
28533
|
+
);
|
|
28534
|
+
}
|
|
28535
|
+
function getValidatedWorkerModel(providerID) {
|
|
28536
|
+
const row = db().query("SELECT value FROM kv_meta WHERE key = ?").get(`${KV_PREFIX}${providerID}`);
|
|
28537
|
+
if (!row) return null;
|
|
28538
|
+
try {
|
|
28539
|
+
return JSON.parse(row.value);
|
|
28540
|
+
} catch {
|
|
28541
|
+
return null;
|
|
28542
|
+
}
|
|
28543
|
+
}
|
|
28544
|
+
function storeValidatedWorkerModel(result) {
|
|
28545
|
+
const key = `${KV_PREFIX}${result.providerID}`;
|
|
28546
|
+
const value = JSON.stringify(result);
|
|
28547
|
+
db().query(
|
|
28548
|
+
"INSERT INTO kv_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?"
|
|
28549
|
+
).run(key, value, value);
|
|
28550
|
+
}
|
|
28551
|
+
function isValidationStale(stored, currentFingerprint) {
|
|
28552
|
+
if (!stored) return true;
|
|
28553
|
+
return stored.fingerprint !== currentFingerprint;
|
|
28554
|
+
}
|
|
28555
|
+
function structuralCheck(candidateObservations, referenceObservations) {
|
|
28556
|
+
if (candidateObservations == null || candidateObservations.length === 0) {
|
|
28557
|
+
return { passed: false, observationCount: 0, tokenCount: 0, reason: candidateObservations === null ? "parse_failed" : "empty" };
|
|
28558
|
+
}
|
|
28559
|
+
const countObs = (text4) => text4.split("\n").filter((l) => l.trim().length > 0).length;
|
|
28560
|
+
const refCount = countObs(referenceObservations);
|
|
28561
|
+
const candCount = countObs(candidateObservations);
|
|
28562
|
+
const candTokens = Math.ceil(candidateObservations.length / 3);
|
|
28563
|
+
if (refCount > 0 && (candCount < refCount * 0.5 || candCount > refCount * 1.5)) {
|
|
28564
|
+
return {
|
|
28565
|
+
passed: false,
|
|
28566
|
+
observationCount: candCount,
|
|
28567
|
+
tokenCount: candTokens,
|
|
28568
|
+
reason: `observation_count_${candCount}_vs_ref_${refCount}`
|
|
28569
|
+
};
|
|
28570
|
+
}
|
|
28571
|
+
const refTokens = Math.ceil(referenceObservations.length / 3);
|
|
28572
|
+
if (candTokens === 0) {
|
|
28573
|
+
return { passed: false, observationCount: candCount, tokenCount: candTokens, reason: "empty" };
|
|
28574
|
+
}
|
|
28575
|
+
if (refTokens > 0 && candTokens > refTokens * 3) {
|
|
28576
|
+
return {
|
|
28577
|
+
passed: false,
|
|
28578
|
+
observationCount: candCount,
|
|
28579
|
+
tokenCount: candTokens,
|
|
28580
|
+
reason: `token_count_${candTokens}_vs_ref_${refTokens}_3x`
|
|
28581
|
+
};
|
|
28582
|
+
}
|
|
28583
|
+
return { passed: true, observationCount: candCount, tokenCount: candTokens };
|
|
28584
|
+
}
|
|
28585
|
+
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.
|
|
28586
|
+
|
|
28587
|
+
Rate the candidate on a scale of 1-5:
|
|
28588
|
+
5 = Captures all key facts and decisions, equivalent to reference
|
|
28589
|
+
4 = Captures most facts, minor omissions
|
|
28590
|
+
3 = Captures the essential facts, some detail loss acceptable
|
|
28591
|
+
2 = Missing important facts or technical details
|
|
28592
|
+
1 = Significantly incomplete or inaccurate
|
|
28593
|
+
|
|
28594
|
+
Respond with ONLY a single digit (1-5).`;
|
|
28595
|
+
function workerJudgeUser(reference, candidate) {
|
|
28596
|
+
return `<reference>
|
|
28597
|
+
${reference}
|
|
28598
|
+
</reference>
|
|
28599
|
+
|
|
28600
|
+
<candidate>
|
|
28601
|
+
${candidate}
|
|
28602
|
+
</candidate>`;
|
|
28603
|
+
}
|
|
28604
|
+
function parseJudgeScore(response) {
|
|
28605
|
+
const match = response.trim().match(/^([1-5])/);
|
|
28606
|
+
if (!match) return null;
|
|
28607
|
+
return parseInt(match[1], 10);
|
|
28608
|
+
}
|
|
28609
|
+
function resolveWorkerModel(providerID, configWorkerModel, configModel) {
|
|
28610
|
+
if (configWorkerModel) return configWorkerModel;
|
|
28611
|
+
const validated = getValidatedWorkerModel(providerID);
|
|
28612
|
+
if (validated) {
|
|
28613
|
+
return { providerID: validated.providerID, modelID: validated.modelID };
|
|
28614
|
+
}
|
|
28615
|
+
return configModel;
|
|
28616
|
+
}
|
|
28166
28617
|
export {
|
|
28618
|
+
COMPACT_SUMMARY_TEMPLATE,
|
|
28167
28619
|
CONSOLIDATION_SYSTEM,
|
|
28168
28620
|
CURATOR_SYSTEM,
|
|
28169
28621
|
DISTILLATION_SYSTEM,
|
|
@@ -28172,10 +28624,14 @@ export {
|
|
|
28172
28624
|
RECALL_PARAM_DESCRIPTIONS,
|
|
28173
28625
|
RECALL_TOOL_DESCRIPTION,
|
|
28174
28626
|
RECURSIVE_SYSTEM,
|
|
28627
|
+
WORKER_JUDGE_SYSTEM,
|
|
28628
|
+
buildCompactPrompt,
|
|
28175
28629
|
calibrate,
|
|
28176
28630
|
close,
|
|
28631
|
+
computeLayer0Cap,
|
|
28177
28632
|
config2 as config,
|
|
28178
28633
|
consolidationUser,
|
|
28634
|
+
consumeCameOutOfIdle,
|
|
28179
28635
|
curator_exports as curator,
|
|
28180
28636
|
curatorUser,
|
|
28181
28637
|
db,
|
|
@@ -28197,6 +28653,7 @@ export {
|
|
|
28197
28653
|
h,
|
|
28198
28654
|
importFromFile,
|
|
28199
28655
|
inline,
|
|
28656
|
+
inspectSessionState,
|
|
28200
28657
|
isFirstRun,
|
|
28201
28658
|
isReasoningPart,
|
|
28202
28659
|
isTextPart,
|
|
@@ -28211,6 +28668,7 @@ export {
|
|
|
28211
28668
|
ltm_exports as ltm,
|
|
28212
28669
|
needsUrgentDistillation,
|
|
28213
28670
|
normalize,
|
|
28671
|
+
onIdleResume,
|
|
28214
28672
|
p,
|
|
28215
28673
|
projectId,
|
|
28216
28674
|
projectName,
|
|
@@ -28222,15 +28680,20 @@ export {
|
|
|
28222
28680
|
saveForceMinLayer,
|
|
28223
28681
|
serialize,
|
|
28224
28682
|
setForceMinLayer,
|
|
28683
|
+
setLastTurnAtForTest,
|
|
28225
28684
|
setLtmTokens,
|
|
28685
|
+
setMaxLayer0Tokens,
|
|
28226
28686
|
setModelLimits,
|
|
28227
28687
|
shouldImport,
|
|
28228
28688
|
strong2 as strong,
|
|
28229
28689
|
t,
|
|
28230
28690
|
temporal_exports as temporal,
|
|
28691
|
+
toolStripAnnotation,
|
|
28231
28692
|
transform2 as transform,
|
|
28232
28693
|
ul,
|
|
28233
28694
|
unescapeMarkdown,
|
|
28695
|
+
workerJudgeUser,
|
|
28696
|
+
worker_model_exports as workerModel,
|
|
28234
28697
|
workerSessionIDs
|
|
28235
28698
|
};
|
|
28236
28699
|
//# sourceMappingURL=index.js.map
|