@loreai/core 0.10.2 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun/config.d.ts +8 -0
- package/dist/bun/config.d.ts.map +1 -1
- package/dist/bun/db.d.ts.map +1 -1
- package/dist/bun/distillation.d.ts +74 -2
- package/dist/bun/distillation.d.ts.map +1 -1
- package/dist/bun/embedding.d.ts.map +1 -1
- package/dist/bun/gradient.d.ts +72 -0
- package/dist/bun/gradient.d.ts.map +1 -1
- package/dist/bun/index.d.ts +4 -2
- package/dist/bun/index.d.ts.map +1 -1
- package/dist/bun/index.js +554 -76
- package/dist/bun/index.js.map +4 -4
- package/dist/bun/prompt.d.ts +8 -2
- package/dist/bun/prompt.d.ts.map +1 -1
- package/dist/bun/temporal.d.ts +31 -0
- package/dist/bun/temporal.d.ts.map +1 -1
- package/dist/bun/types.d.ts +9 -0
- package/dist/bun/types.d.ts.map +1 -1
- package/dist/bun/worker-model.d.ts +90 -0
- package/dist/bun/worker-model.d.ts.map +1 -0
- package/dist/node/config.d.ts +8 -0
- package/dist/node/config.d.ts.map +1 -1
- package/dist/node/db.d.ts.map +1 -1
- package/dist/node/distillation.d.ts +74 -2
- package/dist/node/distillation.d.ts.map +1 -1
- package/dist/node/embedding.d.ts.map +1 -1
- package/dist/node/gradient.d.ts +72 -0
- package/dist/node/gradient.d.ts.map +1 -1
- package/dist/node/index.d.ts +4 -2
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +554 -76
- package/dist/node/index.js.map +4 -4
- package/dist/node/prompt.d.ts +8 -2
- package/dist/node/prompt.d.ts.map +1 -1
- package/dist/node/temporal.d.ts +31 -0
- package/dist/node/temporal.d.ts.map +1 -1
- package/dist/node/types.d.ts +9 -0
- package/dist/node/types.d.ts.map +1 -1
- package/dist/node/worker-model.d.ts +90 -0
- package/dist/node/worker-model.d.ts.map +1 -0
- package/dist/types/config.d.ts +8 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/db.d.ts.map +1 -1
- package/dist/types/distillation.d.ts +74 -2
- package/dist/types/distillation.d.ts.map +1 -1
- package/dist/types/embedding.d.ts.map +1 -1
- package/dist/types/gradient.d.ts +72 -0
- package/dist/types/gradient.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/prompt.d.ts +8 -2
- package/dist/types/prompt.d.ts.map +1 -1
- package/dist/types/temporal.d.ts +31 -0
- package/dist/types/temporal.d.ts.map +1 -1
- package/dist/types/types.d.ts +9 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/worker-model.d.ts +90 -0
- package/dist/types/worker-model.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/config.ts +53 -6
- package/src/db.ts +68 -6
- package/src/distillation.ts +225 -28
- package/src/embedding.ts +7 -0
- package/src/gradient.ts +305 -17
- package/src/index.ts +16 -0
- package/src/lat-reader.ts +4 -4
- package/src/ltm.ts +17 -17
- package/src/prompt.ts +101 -0
- package/src/recall.ts +4 -4
- package/src/temporal.ts +41 -10
- package/src/types.ts +9 -0
- package/src/worker-model.ts +264 -0
package/dist/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() {
|
|
@@ -439,11 +493,13 @@ function db() {
|
|
|
439
493
|
mkdirSync(dir, { recursive: true });
|
|
440
494
|
path = join(dir, "lore.db");
|
|
441
495
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
496
|
+
const database = new Database(path);
|
|
497
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
498
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
499
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
500
|
+
database.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
501
|
+
migrate(database);
|
|
502
|
+
instance = database;
|
|
447
503
|
return instance;
|
|
448
504
|
}
|
|
449
505
|
var VACUUM_MIGRATION_INDEX = 2;
|
|
@@ -10748,12 +10804,27 @@ EXACT NUMBERS: When two segments report different numbers for what seems like th
|
|
|
10748
10804
|
|
|
10749
10805
|
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
10806
|
|
|
10807
|
+
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.
|
|
10808
|
+
|
|
10751
10809
|
Output ONLY an <observations> block with the consolidated observations.`;
|
|
10752
|
-
function recursiveUser(distillations) {
|
|
10810
|
+
function recursiveUser(distillations, previousMeta) {
|
|
10753
10811
|
const entries = distillations.map(
|
|
10754
10812
|
(d, i) => `Segment ${i + 1}:
|
|
10755
10813
|
${d.observations}`
|
|
10756
10814
|
);
|
|
10815
|
+
if (previousMeta) {
|
|
10816
|
+
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.
|
|
10817
|
+
|
|
10818
|
+
<previous-meta-summary>
|
|
10819
|
+
${previousMeta}
|
|
10820
|
+
</previous-meta-summary>
|
|
10821
|
+
|
|
10822
|
+
---
|
|
10823
|
+
|
|
10824
|
+
New observation segments to merge (chronological order):
|
|
10825
|
+
|
|
10826
|
+
${entries.join("\n\n---\n\n")}`;
|
|
10827
|
+
}
|
|
10757
10828
|
return `Observation segments to consolidate (chronological order):
|
|
10758
10829
|
|
|
10759
10830
|
${entries.join("\n\n---\n\n")}`;
|
|
@@ -10908,6 +10979,61 @@ function formatDistillations(distillations) {
|
|
|
10908
10979
|
}
|
|
10909
10980
|
return sections.join("\n\n");
|
|
10910
10981
|
}
|
|
10982
|
+
var COMPACT_SUMMARY_TEMPLATE = `Output exactly this Markdown structure. Keep every section in this order, even when empty (use "(none)").
|
|
10983
|
+
|
|
10984
|
+
---
|
|
10985
|
+
## Goal
|
|
10986
|
+
- [single-sentence task summary]
|
|
10987
|
+
|
|
10988
|
+
## Constraints & Preferences
|
|
10989
|
+
- [user constraints, preferences, specs, or "(none)"]
|
|
10990
|
+
|
|
10991
|
+
## Progress
|
|
10992
|
+
### Done
|
|
10993
|
+
- [completed work or "(none)"]
|
|
10994
|
+
|
|
10995
|
+
### In Progress
|
|
10996
|
+
- [current work or "(none)"]
|
|
10997
|
+
|
|
10998
|
+
### Blocked
|
|
10999
|
+
- [blockers or "(none)"]
|
|
11000
|
+
|
|
11001
|
+
## Key Decisions
|
|
11002
|
+
- [decision and why, or "(none)"]
|
|
11003
|
+
|
|
11004
|
+
## Next Steps
|
|
11005
|
+
- [ordered next actions or "(none)"]
|
|
11006
|
+
|
|
11007
|
+
## Critical Context
|
|
11008
|
+
- [important technical facts, errors, open questions, or "(none)"]
|
|
11009
|
+
|
|
11010
|
+
## Relevant Files
|
|
11011
|
+
- [file or directory path: why it matters, or "(none)"]
|
|
11012
|
+
---
|
|
11013
|
+
|
|
11014
|
+
Rules:
|
|
11015
|
+
- Keep every section, even when empty.
|
|
11016
|
+
- Use terse bullets, not prose paragraphs.
|
|
11017
|
+
- Preserve exact file paths, commands, error strings, and identifiers when known.
|
|
11018
|
+
- Do not mention the summary process or that context was compacted.
|
|
11019
|
+
- End with "I'm ready to continue." on its own line after the closing "---".`;
|
|
11020
|
+
function buildCompactPrompt(input) {
|
|
11021
|
+
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" : "";
|
|
11022
|
+
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.
|
|
11023
|
+
|
|
11024
|
+
<previous-summary>
|
|
11025
|
+
${input.previousSummary}
|
|
11026
|
+
</previous-summary>
|
|
11027
|
+
|
|
11028
|
+
` : "";
|
|
11029
|
+
const knowledgeBlock = input.knowledge ? `
|
|
11030
|
+
${input.knowledge}
|
|
11031
|
+
` : "";
|
|
11032
|
+
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.
|
|
11033
|
+
|
|
11034
|
+
${distillSection}${anchorBlock}${COMPACT_SUMMARY_TEMPLATE}
|
|
11035
|
+
${knowledgeBlock}`;
|
|
11036
|
+
}
|
|
10911
11037
|
function estimateTokens(text4) {
|
|
10912
11038
|
return Math.ceil(text4.length / 3);
|
|
10913
11039
|
}
|
|
@@ -11160,6 +11286,7 @@ function isToolPart(p3) {
|
|
|
11160
11286
|
function estimate(text4) {
|
|
11161
11287
|
return Math.ceil(text4.length / 3);
|
|
11162
11288
|
}
|
|
11289
|
+
var CHUNK_TERMINATOR = "";
|
|
11163
11290
|
function partsToText(parts) {
|
|
11164
11291
|
const chunks = [];
|
|
11165
11292
|
for (const part of parts) {
|
|
@@ -11169,7 +11296,7 @@ function partsToText(parts) {
|
|
|
11169
11296
|
else if (isToolPart(part) && part.state.status === "completed")
|
|
11170
11297
|
chunks.push(`[tool:${part.tool}] ${part.state.output}`);
|
|
11171
11298
|
}
|
|
11172
|
-
return sanitizeSurrogates(chunks.join("\n"));
|
|
11299
|
+
return sanitizeSurrogates(chunks.join("\n" + CHUNK_TERMINATOR));
|
|
11173
11300
|
}
|
|
11174
11301
|
function messageMetadata(info2, parts) {
|
|
11175
11302
|
const meta3 = {};
|
|
@@ -11248,11 +11375,11 @@ function search2(input) {
|
|
|
11248
11375
|
const limit = input.limit ?? 20;
|
|
11249
11376
|
const q = ftsQuery(input.query);
|
|
11250
11377
|
if (q === EMPTY_QUERY) return [];
|
|
11251
|
-
const ftsSQL = input.sessionID ? `SELECT m.* FROM
|
|
11252
|
-
JOIN
|
|
11378
|
+
const ftsSQL = input.sessionID ? `SELECT m.* FROM temporal_fts f
|
|
11379
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11253
11380
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11254
|
-
ORDER BY rank LIMIT ?` : `SELECT m.* FROM
|
|
11255
|
-
JOIN
|
|
11381
|
+
ORDER BY rank LIMIT ?` : `SELECT m.* FROM temporal_fts f
|
|
11382
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11256
11383
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11257
11384
|
ORDER BY rank LIMIT ?`;
|
|
11258
11385
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -11277,11 +11404,11 @@ function searchScored(input) {
|
|
|
11277
11404
|
const limit = input.limit ?? 20;
|
|
11278
11405
|
const q = ftsQuery(input.query);
|
|
11279
11406
|
if (q === EMPTY_QUERY) return [];
|
|
11280
|
-
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM
|
|
11281
|
-
JOIN
|
|
11407
|
+
const ftsSQL = input.sessionID ? `SELECT m.*, rank FROM temporal_fts f
|
|
11408
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11282
11409
|
WHERE f.content MATCH ? AND m.project_id = ? AND m.session_id = ?
|
|
11283
|
-
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM
|
|
11284
|
-
JOIN
|
|
11410
|
+
ORDER BY rank LIMIT ?` : `SELECT m.*, rank FROM temporal_fts f
|
|
11411
|
+
CROSS JOIN temporal_messages m ON m.rowid = f.rowid
|
|
11285
11412
|
WHERE f.content MATCH ? AND m.project_id = ?
|
|
11286
11413
|
ORDER BY rank LIMIT ?`;
|
|
11287
11414
|
const params = input.sessionID ? [q, pid, input.sessionID, limit] : [q, pid, limit];
|
|
@@ -25483,18 +25610,63 @@ var LoreConfig = external_exports.object({
|
|
|
25483
25610
|
providerID: external_exports.string(),
|
|
25484
25611
|
modelID: external_exports.string()
|
|
25485
25612
|
}).optional(),
|
|
25613
|
+
/** Explicit worker model override. When set, all background workers (distillation,
|
|
25614
|
+
* curation, query expansion) use this model instead of the session model or the
|
|
25615
|
+
* auto-selected worker model. Bypasses dynamic worker model selection entirely. */
|
|
25616
|
+
workerModel: external_exports.object({
|
|
25617
|
+
providerID: external_exports.string(),
|
|
25618
|
+
modelID: external_exports.string()
|
|
25619
|
+
}).optional(),
|
|
25486
25620
|
budget: external_exports.object({
|
|
25487
25621
|
distilled: external_exports.number().min(0.05).max(0.5).default(0.25),
|
|
25488
25622
|
raw: external_exports.number().min(0.1).max(0.7).default(0.4),
|
|
25489
25623
|
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
|
-
|
|
25624
|
+
/** Max fraction of usable context reserved for LTM system-prompt injection. Default: 0.05 (5%). */
|
|
25625
|
+
ltm: external_exports.number().min(0.02).max(0.3).default(0.05),
|
|
25626
|
+
/** Per-turn cache-read cost target in dollars. Controls when layer 0 (full
|
|
25627
|
+
* passthrough) escalates to layer 1 (compressed). The cap is derived as:
|
|
25628
|
+
* maxLayer0Tokens = max(target / model.cost.cache.read, 40K).
|
|
25629
|
+
* Lower = cheaper but earlier compression. Default: 0.10. Set to 0 to
|
|
25630
|
+
* disable cost-aware capping (use the model's full context). */
|
|
25631
|
+
targetCacheReadCostPerTurn: external_exports.number().min(0).default(0.1),
|
|
25632
|
+
/** Direct override for the layer-0 token cap. When set, bypasses the
|
|
25633
|
+
* cost-aware formula from targetCacheReadCostPerTurn. 0 = disabled
|
|
25634
|
+
* (no cap, use full context). Default: undefined (use cost-aware auto). */
|
|
25635
|
+
maxLayer0Tokens: external_exports.number().min(0).optional()
|
|
25636
|
+
}).default({ distilled: 0.25, raw: 0.4, output: 0.25, ltm: 0.05, targetCacheReadCostPerTurn: 0.1 }),
|
|
25637
|
+
/**
|
|
25638
|
+
* Cold-cache idle-resume handling.
|
|
25639
|
+
*
|
|
25640
|
+
* Anthropic's prompt cache evicts entries after ~5 min (default tier) /
|
|
25641
|
+
* ~1 hour (extended tier). When a session resumes after the eviction window,
|
|
25642
|
+
* Lore's byte-identity caches (distilled prefix, raw window pin, LTM block)
|
|
25643
|
+
* are providing no value because the underlying provider cache is already
|
|
25644
|
+
* cold. On detection, Lore refreshes those caches so the next turn can
|
|
25645
|
+
* produce a better-fitting window without paying a cache cost it would
|
|
25646
|
+
* otherwise be trying to preserve. Reasoning blocks are NOT touched —
|
|
25647
|
+
* Anthropic's April 23 postmortem identified dropping reasoning blocks as
|
|
25648
|
+
* the root cause of forgetfulness/repetition.
|
|
25649
|
+
*
|
|
25650
|
+
* `idleResumeMinutes` is the threshold in minutes. Default 60 — matches
|
|
25651
|
+
* Anthropic's extended-cache eviction window, conservative across providers.
|
|
25652
|
+
* Set to 0 to disable the feature.
|
|
25653
|
+
*/
|
|
25654
|
+
idleResumeMinutes: external_exports.number().min(0).max(24 * 60).default(60),
|
|
25493
25655
|
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
|
-
|
|
25656
|
+
minMessages: external_exports.number().min(3).default(5),
|
|
25657
|
+
maxSegment: external_exports.number().min(5).default(30),
|
|
25658
|
+
metaThreshold: external_exports.number().min(3).default(10),
|
|
25659
|
+
/** Max chars per tool output when rendering temporal messages for distillation input.
|
|
25660
|
+
* Outputs longer than this are replaced with a compact annotation preserving line
|
|
25661
|
+
* count, error signals, and file paths. Default: 2000 (matches upstream OpenCode's
|
|
25662
|
+
* TOOL_OUTPUT_MAX_CHARS during compaction). Set to 0 to disable. */
|
|
25663
|
+
toolOutputMaxChars: external_exports.number().min(0).default(2e3)
|
|
25664
|
+
}).default({
|
|
25665
|
+
minMessages: 5,
|
|
25666
|
+
maxSegment: 30,
|
|
25667
|
+
metaThreshold: 10,
|
|
25668
|
+
toolOutputMaxChars: 2e3
|
|
25669
|
+
}),
|
|
25498
25670
|
knowledge: external_exports.object({
|
|
25499
25671
|
/** Set to false to disable long-term knowledge storage and system-prompt injection.
|
|
25500
25672
|
* Conversation recall (temporal search, distillation search) and context management
|
|
@@ -25599,6 +25771,7 @@ __export(embedding_exports, {
|
|
|
25599
25771
|
vectorSearch: () => vectorSearch,
|
|
25600
25772
|
vectorSearchDistillations: () => vectorSearchDistillations
|
|
25601
25773
|
});
|
|
25774
|
+
var EMBED_TIMEOUT_MS = 1e4;
|
|
25602
25775
|
var VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
|
|
25603
25776
|
var VoyageProvider = class {
|
|
25604
25777
|
maxBatchSize = 128;
|
|
@@ -25622,7 +25795,8 @@ var VoyageProvider = class {
|
|
|
25622
25795
|
model: this.model,
|
|
25623
25796
|
input_type: inputType,
|
|
25624
25797
|
output_dimension: this.dimensions
|
|
25625
|
-
})
|
|
25798
|
+
}),
|
|
25799
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25626
25800
|
});
|
|
25627
25801
|
if (!res.ok) {
|
|
25628
25802
|
const body = await res.text().catch(() => "");
|
|
@@ -25658,7 +25832,8 @@ var OpenAIProvider = class {
|
|
|
25658
25832
|
"Content-Type": "application/json",
|
|
25659
25833
|
Authorization: `Bearer ${this.apiKey}`
|
|
25660
25834
|
},
|
|
25661
|
-
body: JSON.stringify(body)
|
|
25835
|
+
body: JSON.stringify(body),
|
|
25836
|
+
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS)
|
|
25662
25837
|
});
|
|
25663
25838
|
if (!res.ok) {
|
|
25664
25839
|
const responseBody = await res.text().catch(() => "");
|
|
@@ -26041,8 +26216,8 @@ function searchScored2(input) {
|
|
|
26041
26216
|
const ftsSQL = `SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26042
26217
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26043
26218
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26044
|
-
FROM
|
|
26045
|
-
JOIN
|
|
26219
|
+
FROM lat_sections_fts f
|
|
26220
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26046
26221
|
WHERE lat_sections_fts MATCH ?
|
|
26047
26222
|
AND s.project_id = ?
|
|
26048
26223
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26068,8 +26243,8 @@ function scoreForSession(projectPath, sessionContext, maxTokens) {
|
|
|
26068
26243
|
`SELECT s.id, s.project_id, s.file, s.heading, s.depth, s.content,
|
|
26069
26244
|
s.content_hash, s.first_paragraph, s.updated_at,
|
|
26070
26245
|
bm25(lat_sections_fts, 6.0, 2.0) as rank
|
|
26071
|
-
FROM
|
|
26072
|
-
JOIN
|
|
26246
|
+
FROM lat_sections_fts f
|
|
26247
|
+
CROSS JOIN lat_sections s ON s.rowid = f.rowid
|
|
26073
26248
|
WHERE lat_sections_fts MATCH ?
|
|
26074
26249
|
AND s.project_id = ?
|
|
26075
26250
|
ORDER BY rank`
|
|
@@ -26198,10 +26373,10 @@ function scoreEntriesFTS(sessionContext) {
|
|
|
26198
26373
|
try {
|
|
26199
26374
|
const results = db().query(
|
|
26200
26375
|
`SELECT k.id, bm25(knowledge_fts, ?, ?, ?) as rank
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
|
|
26376
|
+
FROM knowledge_fts f
|
|
26377
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26378
|
+
WHERE knowledge_fts MATCH ?
|
|
26379
|
+
AND k.confidence > 0.2`
|
|
26205
26380
|
).all(title, content3, category, q);
|
|
26206
26381
|
if (!results.length) return /* @__PURE__ */ new Map();
|
|
26207
26382
|
const ranks = results.map((r) => r.rank);
|
|
@@ -26335,13 +26510,13 @@ function search3(input) {
|
|
|
26335
26510
|
const q = ftsQuery(input.query);
|
|
26336
26511
|
if (q === EMPTY_QUERY) return [];
|
|
26337
26512
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26338
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26339
|
-
JOIN
|
|
26513
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26514
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26340
26515
|
WHERE knowledge_fts MATCH ?
|
|
26341
26516
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26342
26517
|
AND k.confidence > 0.2
|
|
26343
|
-
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM
|
|
26344
|
-
JOIN
|
|
26518
|
+
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K} FROM knowledge_fts f
|
|
26519
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26345
26520
|
WHERE knowledge_fts MATCH ?
|
|
26346
26521
|
AND k.confidence > 0.2
|
|
26347
26522
|
ORDER BY bm25(knowledge_fts, ?, ?, ?) LIMIT ?`;
|
|
@@ -26368,13 +26543,13 @@ function searchScored3(input) {
|
|
|
26368
26543
|
if (q === EMPTY_QUERY) return [];
|
|
26369
26544
|
const pid = input.projectPath ? ensureProject(input.projectPath) : null;
|
|
26370
26545
|
const { title, content: content3, category } = ftsWeights();
|
|
26371
|
-
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26372
|
-
JOIN
|
|
26546
|
+
const ftsSQL = pid ? `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26547
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26373
26548
|
WHERE knowledge_fts MATCH ?
|
|
26374
26549
|
AND (k.project_id = ? OR k.project_id IS NULL OR k.cross_project = 1)
|
|
26375
26550
|
AND k.confidence > 0.2
|
|
26376
|
-
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26377
|
-
JOIN
|
|
26551
|
+
ORDER BY rank LIMIT ?` : `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26552
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26378
26553
|
WHERE knowledge_fts MATCH ?
|
|
26379
26554
|
AND k.confidence > 0.2
|
|
26380
26555
|
ORDER BY rank LIMIT ?`;
|
|
@@ -26396,8 +26571,8 @@ function searchScoredOtherProjects(input) {
|
|
|
26396
26571
|
if (q === EMPTY_QUERY) return [];
|
|
26397
26572
|
const excludePid = ensureProject(input.excludeProjectPath);
|
|
26398
26573
|
const { title, content: content3, category } = ftsWeights();
|
|
26399
|
-
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM
|
|
26400
|
-
JOIN
|
|
26574
|
+
const ftsSQL = `SELECT ${KNOWLEDGE_COLS_K}, bm25(knowledge_fts, ?, ?, ?) as rank FROM knowledge_fts f
|
|
26575
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26401
26576
|
WHERE knowledge_fts MATCH ?
|
|
26402
26577
|
AND k.project_id IS NOT NULL
|
|
26403
26578
|
AND k.project_id != ?
|
|
@@ -26530,8 +26705,8 @@ function check2(projectPath) {
|
|
|
26530
26705
|
try {
|
|
26531
26706
|
const { title, content: content3, category } = config2().search.ftsWeights;
|
|
26532
26707
|
const matches = db().query(
|
|
26533
|
-
`SELECT k.id, k.title FROM
|
|
26534
|
-
JOIN
|
|
26708
|
+
`SELECT k.id, k.title FROM knowledge_fts f
|
|
26709
|
+
CROSS JOIN knowledge k ON k.rowid = f.rowid
|
|
26535
26710
|
WHERE knowledge_fts MATCH ?
|
|
26536
26711
|
AND k.id != ?
|
|
26537
26712
|
AND k.confidence > 0.2
|
|
@@ -26565,9 +26740,13 @@ function check2(projectPath) {
|
|
|
26565
26740
|
// src/distillation.ts
|
|
26566
26741
|
var distillation_exports = {};
|
|
26567
26742
|
__export(distillation_exports, {
|
|
26743
|
+
latestMetaObservations: () => latestMetaObservations,
|
|
26568
26744
|
loadForSession: () => loadForSession,
|
|
26745
|
+
messagesToText: () => messagesToText,
|
|
26746
|
+
metaDistill: () => metaDistill,
|
|
26569
26747
|
parseSourceIds: () => parseSourceIds,
|
|
26570
26748
|
run: () => run,
|
|
26749
|
+
truncateToolOutputsInContent: () => truncateToolOutputsInContent,
|
|
26571
26750
|
workerSessionIDs: () => workerSessionIDs
|
|
26572
26751
|
});
|
|
26573
26752
|
|
|
@@ -26592,6 +26771,8 @@ function estimateMessage(msg) {
|
|
|
26592
26771
|
}
|
|
26593
26772
|
var contextLimit = 2e5;
|
|
26594
26773
|
var outputReserved = 32e3;
|
|
26774
|
+
var maxLayer0Tokens = 0;
|
|
26775
|
+
var MIN_LAYER0_FLOOR = 4e4;
|
|
26595
26776
|
var FIRST_TURN_OVERHEAD = 15e3;
|
|
26596
26777
|
var calibratedOverhead = null;
|
|
26597
26778
|
function makeSessionState() {
|
|
@@ -26605,7 +26786,11 @@ function makeSessionState() {
|
|
|
26605
26786
|
forceMinLayer: 0,
|
|
26606
26787
|
lastTransformEstimate: 0,
|
|
26607
26788
|
prefixCache: null,
|
|
26608
|
-
rawWindowCache: null
|
|
26789
|
+
rawWindowCache: null,
|
|
26790
|
+
lastTurnAt: 0,
|
|
26791
|
+
cameOutOfIdle: false,
|
|
26792
|
+
consecutiveHighLayer: 0,
|
|
26793
|
+
lastPrefixHash: ""
|
|
26609
26794
|
};
|
|
26610
26795
|
}
|
|
26611
26796
|
var sessionStates = /* @__PURE__ */ new Map();
|
|
@@ -26618,11 +26803,36 @@ function getSessionState(sessionID) {
|
|
|
26618
26803
|
}
|
|
26619
26804
|
return state;
|
|
26620
26805
|
}
|
|
26806
|
+
function onIdleResume(sessionID, thresholdMs, now = Date.now()) {
|
|
26807
|
+
if (thresholdMs <= 0) return { triggered: false };
|
|
26808
|
+
const state = getSessionState(sessionID);
|
|
26809
|
+
if (state.lastTurnAt === 0) return { triggered: false };
|
|
26810
|
+
const idleMs = now - state.lastTurnAt;
|
|
26811
|
+
if (idleMs < thresholdMs) return { triggered: false };
|
|
26812
|
+
state.prefixCache = null;
|
|
26813
|
+
state.rawWindowCache = null;
|
|
26814
|
+
state.cameOutOfIdle = true;
|
|
26815
|
+
return { triggered: true, idleMs };
|
|
26816
|
+
}
|
|
26817
|
+
function consumeCameOutOfIdle(sessionID) {
|
|
26818
|
+
const state = sessionStates.get(sessionID);
|
|
26819
|
+
if (!state || !state.cameOutOfIdle) return false;
|
|
26820
|
+
state.cameOutOfIdle = false;
|
|
26821
|
+
return true;
|
|
26822
|
+
}
|
|
26621
26823
|
var ltmTokens = 0;
|
|
26622
26824
|
function setModelLimits(limits) {
|
|
26623
26825
|
contextLimit = limits.context || 2e5;
|
|
26624
26826
|
outputReserved = Math.min(limits.output || 32e3, 32e3);
|
|
26625
26827
|
}
|
|
26828
|
+
function setMaxLayer0Tokens(tokens) {
|
|
26829
|
+
maxLayer0Tokens = Math.max(0, Math.floor(tokens));
|
|
26830
|
+
}
|
|
26831
|
+
function computeLayer0Cap(targetCostPerTurn, cacheReadCostPerToken) {
|
|
26832
|
+
if (targetCostPerTurn <= 0 || cacheReadCostPerToken <= 0) return 0;
|
|
26833
|
+
const rawCap = Math.floor(targetCostPerTurn / cacheReadCostPerToken);
|
|
26834
|
+
return Math.max(rawCap, MIN_LAYER0_FLOOR);
|
|
26835
|
+
}
|
|
26626
26836
|
function setLtmTokens(tokens) {
|
|
26627
26837
|
ltmTokens = tokens;
|
|
26628
26838
|
}
|
|
@@ -26667,6 +26877,19 @@ function setForceMinLayer(layer, sessionID) {
|
|
|
26667
26877
|
}
|
|
26668
26878
|
}
|
|
26669
26879
|
}
|
|
26880
|
+
function inspectSessionState(sessionID) {
|
|
26881
|
+
const state = sessionStates.get(sessionID);
|
|
26882
|
+
if (!state) return null;
|
|
26883
|
+
return {
|
|
26884
|
+
hasPrefixCache: state.prefixCache !== null,
|
|
26885
|
+
hasRawWindowCache: state.rawWindowCache !== null,
|
|
26886
|
+
cameOutOfIdle: state.cameOutOfIdle,
|
|
26887
|
+
lastTurnAt: state.lastTurnAt
|
|
26888
|
+
};
|
|
26889
|
+
}
|
|
26890
|
+
function setLastTurnAtForTest(sessionID, ms) {
|
|
26891
|
+
getSessionState(sessionID).lastTurnAt = ms;
|
|
26892
|
+
}
|
|
26670
26893
|
function loadDistillations(projectPath, sessionID) {
|
|
26671
26894
|
const pid = ensureProject(projectPath);
|
|
26672
26895
|
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 +26922,17 @@ function cleanParts(parts) {
|
|
|
26699
26922
|
}
|
|
26700
26923
|
return filtered.length > 0 ? filtered : parts;
|
|
26701
26924
|
}
|
|
26925
|
+
var ANNOTATION_PATH_SCAN_LIMIT = 64 * 1024;
|
|
26926
|
+
var PATH_RE = /(?:[\w.-]+\/)+[\w.-]+\.\w{1,5}/g;
|
|
26702
26927
|
function toolStripAnnotation(toolName, output) {
|
|
26703
26928
|
const lines = output.split("\n").length;
|
|
26704
|
-
const chars = output.length;
|
|
26705
26929
|
const hasError = /\b(?:error|fail(?:ed|ure)?|exception|panic|traceback)\b/i.test(output);
|
|
26706
|
-
|
|
26707
|
-
|
|
26930
|
+
let uniquePaths = [];
|
|
26931
|
+
if (output.indexOf("/") !== -1) {
|
|
26932
|
+
const pathScan = output.length > ANNOTATION_PATH_SCAN_LIMIT ? output.slice(0, ANNOTATION_PATH_SCAN_LIMIT) : output;
|
|
26933
|
+
const paths = pathScan.match(PATH_RE);
|
|
26934
|
+
if (paths) uniquePaths = [...new Set(paths)].slice(0, 5);
|
|
26935
|
+
}
|
|
26708
26936
|
let annotation = `[output omitted \u2014 ${toolName}: ${lines} lines`;
|
|
26709
26937
|
if (hasError) annotation += ", contained errors";
|
|
26710
26938
|
if (uniquePaths.length > 0) annotation += `, paths: ${uniquePaths.join(", ")}`;
|
|
@@ -27135,7 +27363,11 @@ function transformInner(input) {
|
|
|
27135
27363
|
expectedInput = messageTokens + overhead + ltmTokens;
|
|
27136
27364
|
}
|
|
27137
27365
|
const layer0Input = calibrated ? expectedInput : expectedInput * UNCALIBRATED_SAFETY;
|
|
27138
|
-
|
|
27366
|
+
let layer0Ceiling = maxLayer0Tokens > 0 ? Math.min(maxInput, maxLayer0Tokens) : maxInput;
|
|
27367
|
+
if (!calibrated && layer0Ceiling < maxInput) {
|
|
27368
|
+
layer0Ceiling = Math.floor(layer0Ceiling * 0.7);
|
|
27369
|
+
}
|
|
27370
|
+
if (effectiveMinLayer === 0 && layer0Input <= layer0Ceiling) {
|
|
27139
27371
|
const messageTokens = calibrated ? expectedInput - (ltmTokens - sessState.lastKnownLtm) : expectedInput - overhead - ltmTokens;
|
|
27140
27372
|
return {
|
|
27141
27373
|
messages: input.messages,
|
|
@@ -27216,14 +27448,31 @@ function transformInner(input) {
|
|
|
27216
27448
|
(sum, m) => sum + estimateMessage(m),
|
|
27217
27449
|
0
|
|
27218
27450
|
);
|
|
27219
|
-
const
|
|
27451
|
+
const tailBudget = Math.max(2e3, Math.min(8e3, Math.floor(usable * 0.25)));
|
|
27452
|
+
const nuclearTurnStart = currentTurnStart(input.messages);
|
|
27453
|
+
const currentTurn = input.messages.slice(nuclearTurnStart).map((m) => ({
|
|
27220
27454
|
info: m.info,
|
|
27221
27455
|
parts: cleanParts(m.parts)
|
|
27222
27456
|
}));
|
|
27223
|
-
const
|
|
27457
|
+
const currentTurnTokens = currentTurn.reduce(
|
|
27224
27458
|
(sum, m) => sum + estimateMessage(m),
|
|
27225
27459
|
0
|
|
27226
27460
|
);
|
|
27461
|
+
const olderMessages = [];
|
|
27462
|
+
let olderTokens = 0;
|
|
27463
|
+
const remaining = Math.max(0, tailBudget - currentTurnTokens);
|
|
27464
|
+
for (let i = nuclearTurnStart - 1; i >= 0 && olderTokens < remaining; i--) {
|
|
27465
|
+
const msg = input.messages[i];
|
|
27466
|
+
const est = estimateMessage(msg);
|
|
27467
|
+
if (olderTokens + est > remaining) break;
|
|
27468
|
+
olderMessages.unshift({
|
|
27469
|
+
info: msg.info,
|
|
27470
|
+
parts: cleanParts(msg.parts)
|
|
27471
|
+
});
|
|
27472
|
+
olderTokens += est;
|
|
27473
|
+
}
|
|
27474
|
+
const nuclearRaw = [...olderMessages, ...currentTurn];
|
|
27475
|
+
const nuclearRawTokens = olderTokens + currentTurnTokens;
|
|
27227
27476
|
return {
|
|
27228
27477
|
messages: [...nuclearPrefix, ...nuclearRaw],
|
|
27229
27478
|
layer: 4,
|
|
@@ -27245,19 +27494,55 @@ function transform2(input) {
|
|
|
27245
27494
|
state.lastTransformEstimate = result.totalTokens;
|
|
27246
27495
|
state.lastLayer = result.layer;
|
|
27247
27496
|
state.lastWindowMessageIDs = new Set(result.messages.map((m) => m.info.id));
|
|
27497
|
+
state.lastTurnAt = Date.now();
|
|
27498
|
+
const prefixIds = result.messages.slice(0, 5).map((m) => m.info.id).join(",");
|
|
27499
|
+
const prefixHash = `${result.layer}:${prefixIds}`;
|
|
27500
|
+
if (state.lastPrefixHash && state.lastPrefixHash !== prefixHash) {
|
|
27501
|
+
info(
|
|
27502
|
+
`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)}`
|
|
27503
|
+
);
|
|
27504
|
+
}
|
|
27505
|
+
state.lastPrefixHash = prefixHash;
|
|
27506
|
+
if (result.layer >= 2) {
|
|
27507
|
+
state.consecutiveHighLayer++;
|
|
27508
|
+
if (state.consecutiveHighLayer === 3) {
|
|
27509
|
+
info(
|
|
27510
|
+
`session ${sid} has been at gradient layer ${result.layer}+ for 3 consecutive turns. Consider running /compact to reset the context window.`
|
|
27511
|
+
);
|
|
27512
|
+
}
|
|
27513
|
+
} else {
|
|
27514
|
+
state.consecutiveHighLayer = 0;
|
|
27515
|
+
}
|
|
27516
|
+
info(
|
|
27517
|
+
`gradient: session=${sid} layer=${result.layer} tokens=${result.totalTokens} (distilled=${result.distilledTokens} raw=${result.rawTokens}) usable=${result.usable} cap=${maxLayer0Tokens || "off"}`
|
|
27518
|
+
);
|
|
27248
27519
|
}
|
|
27249
27520
|
return result;
|
|
27250
27521
|
}
|
|
27251
27522
|
function currentTurnStart(messages) {
|
|
27252
|
-
|
|
27523
|
+
if (messages.length === 0) return 0;
|
|
27524
|
+
let boundary = messages.length;
|
|
27253
27525
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
27254
27526
|
if (messages[i].info.role === "user") {
|
|
27255
|
-
|
|
27527
|
+
boundary = i;
|
|
27256
27528
|
break;
|
|
27257
27529
|
}
|
|
27258
27530
|
}
|
|
27259
|
-
if (
|
|
27260
|
-
|
|
27531
|
+
if (boundary === messages.length) return 0;
|
|
27532
|
+
for (let i = boundary - 1; i >= 0; i--) {
|
|
27533
|
+
const msg = messages[i];
|
|
27534
|
+
const hasToolParts = msg.parts.some(isToolPart);
|
|
27535
|
+
if (hasToolParts) {
|
|
27536
|
+
boundary = i;
|
|
27537
|
+
continue;
|
|
27538
|
+
}
|
|
27539
|
+
if (msg.info.role === "user") {
|
|
27540
|
+
boundary = i;
|
|
27541
|
+
continue;
|
|
27542
|
+
}
|
|
27543
|
+
break;
|
|
27544
|
+
}
|
|
27545
|
+
return boundary;
|
|
27261
27546
|
}
|
|
27262
27547
|
function tryFit(input) {
|
|
27263
27548
|
if (input.prefixTokens > input.distilledBudget && input.prefix.length > 0)
|
|
@@ -27338,8 +27623,39 @@ function formatTime(ms) {
|
|
|
27338
27623
|
const m = d.getMinutes().toString().padStart(2, "0");
|
|
27339
27624
|
return `${h3}:${m}`;
|
|
27340
27625
|
}
|
|
27341
|
-
|
|
27342
|
-
|
|
27626
|
+
var CHUNK_SEPARATOR = "\n" + CHUNK_TERMINATOR;
|
|
27627
|
+
function truncateToolOutputsInContent(content3, maxChars) {
|
|
27628
|
+
if (maxChars <= 0 || content3.length === 0) return content3;
|
|
27629
|
+
if (content3.indexOf(CHUNK_TERMINATOR) === -1) {
|
|
27630
|
+
return truncateSingleChunk(content3, maxChars);
|
|
27631
|
+
}
|
|
27632
|
+
const chunks = content3.split(CHUNK_SEPARATOR);
|
|
27633
|
+
let anyToolChunk = false;
|
|
27634
|
+
for (const c of chunks) {
|
|
27635
|
+
if (c.startsWith("[tool:")) {
|
|
27636
|
+
anyToolChunk = true;
|
|
27637
|
+
break;
|
|
27638
|
+
}
|
|
27639
|
+
}
|
|
27640
|
+
if (!anyToolChunk) return content3;
|
|
27641
|
+
const out = chunks.map((chunk) => truncateSingleChunk(chunk, maxChars));
|
|
27642
|
+
return out.join(CHUNK_SEPARATOR);
|
|
27643
|
+
}
|
|
27644
|
+
function truncateSingleChunk(chunk, maxChars) {
|
|
27645
|
+
if (!chunk.startsWith("[tool:")) return chunk;
|
|
27646
|
+
const closeBracket = chunk.indexOf("] ");
|
|
27647
|
+
if (closeBracket < 0) return chunk;
|
|
27648
|
+
const toolName = chunk.slice(6, closeBracket);
|
|
27649
|
+
const payload = chunk.slice(closeBracket + 2);
|
|
27650
|
+
if (payload.length <= maxChars) return chunk;
|
|
27651
|
+
return `[tool:${toolName}] ${toolStripAnnotation(toolName, payload)}`;
|
|
27652
|
+
}
|
|
27653
|
+
function messagesToText(messages, toolOutputMaxChars) {
|
|
27654
|
+
const cap = toolOutputMaxChars ?? config2().distillation.toolOutputMaxChars;
|
|
27655
|
+
return messages.map((m) => {
|
|
27656
|
+
const body = m.role === "user" ? m.content : truncateToolOutputsInContent(m.content, cap);
|
|
27657
|
+
return `[${m.role}] (${formatTime(m.created_at)}) ${body}`;
|
|
27658
|
+
}).join("\n\n");
|
|
27343
27659
|
}
|
|
27344
27660
|
function parseDistillationResult(text4) {
|
|
27345
27661
|
const match = text4.match(/<observations>([\s\S]*?)<\/observations>/i);
|
|
@@ -27354,6 +27670,19 @@ function latestObservations(projectPath, sessionID) {
|
|
|
27354
27670
|
).get(pid, sessionID);
|
|
27355
27671
|
return row?.observations || void 0;
|
|
27356
27672
|
}
|
|
27673
|
+
function latestMetaObservations(projectPath, sessionID) {
|
|
27674
|
+
return latestMeta(projectPath, sessionID)?.observations;
|
|
27675
|
+
}
|
|
27676
|
+
function latestMeta(projectPath, sessionID) {
|
|
27677
|
+
const pid = ensureProject(projectPath);
|
|
27678
|
+
const row = db().query(
|
|
27679
|
+
`SELECT observations, generation FROM distillations
|
|
27680
|
+
WHERE project_id = ? AND session_id = ? AND generation > 0
|
|
27681
|
+
ORDER BY generation DESC, created_at DESC LIMIT 1`
|
|
27682
|
+
).get(pid, sessionID);
|
|
27683
|
+
if (!row || !row.observations) return void 0;
|
|
27684
|
+
return row;
|
|
27685
|
+
}
|
|
27357
27686
|
function parseSourceIds(raw) {
|
|
27358
27687
|
try {
|
|
27359
27688
|
const parsed = JSON.parse(raw);
|
|
@@ -27363,11 +27692,10 @@ function parseSourceIds(raw) {
|
|
|
27363
27692
|
return [];
|
|
27364
27693
|
}
|
|
27365
27694
|
}
|
|
27366
|
-
function loadForSession(projectPath, sessionID) {
|
|
27695
|
+
function loadForSession(projectPath, sessionID, includeArchived = false) {
|
|
27367
27696
|
const pid = ensureProject(projectPath);
|
|
27368
|
-
const
|
|
27369
|
-
|
|
27370
|
-
).all(pid, sessionID);
|
|
27697
|
+
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";
|
|
27698
|
+
const rows = db().query(sql).all(pid, sessionID);
|
|
27371
27699
|
return rows.map((r) => ({
|
|
27372
27700
|
...r,
|
|
27373
27701
|
source_ids: parseSourceIds(r.source_ids)
|
|
@@ -27532,8 +27860,13 @@ async function distillSegment(input) {
|
|
|
27532
27860
|
}
|
|
27533
27861
|
async function metaDistill(input) {
|
|
27534
27862
|
const existing = loadGen0(input.projectPath, input.sessionID);
|
|
27535
|
-
|
|
27536
|
-
|
|
27863
|
+
const priorMeta = latestMeta(input.projectPath, input.sessionID);
|
|
27864
|
+
if (priorMeta) {
|
|
27865
|
+
if (existing.length === 0) return null;
|
|
27866
|
+
} else {
|
|
27867
|
+
if (existing.length < 3) return null;
|
|
27868
|
+
}
|
|
27869
|
+
const userContent = recursiveUser(existing, priorMeta?.observations);
|
|
27537
27870
|
const model = input.model ?? config2().model;
|
|
27538
27871
|
const responseText = await input.llm.prompt(
|
|
27539
27872
|
RECURSIVE_SYSTEM,
|
|
@@ -27543,19 +27876,30 @@ async function metaDistill(input) {
|
|
|
27543
27876
|
if (!responseText) return null;
|
|
27544
27877
|
const result = parseDistillationResult(responseText);
|
|
27545
27878
|
if (!result) return null;
|
|
27546
|
-
const maxGen = Math.max(
|
|
27879
|
+
const maxGen = Math.max(
|
|
27880
|
+
...existing.map((d) => d.generation),
|
|
27881
|
+
priorMeta?.generation ?? 0
|
|
27882
|
+
);
|
|
27547
27883
|
const allSourceIDs = existing.flatMap((d) => d.source_ids);
|
|
27548
|
-
|
|
27549
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
27554
|
-
|
|
27884
|
+
let metaId;
|
|
27885
|
+
db().exec("BEGIN IMMEDIATE");
|
|
27886
|
+
try {
|
|
27887
|
+
metaId = storeDistillation({
|
|
27888
|
+
projectPath: input.projectPath,
|
|
27889
|
+
sessionID: input.sessionID,
|
|
27890
|
+
observations: result.observations,
|
|
27891
|
+
sourceIDs: allSourceIDs,
|
|
27892
|
+
generation: maxGen + 1
|
|
27893
|
+
});
|
|
27894
|
+
archiveDistillations(existing.map((d) => d.id));
|
|
27895
|
+
db().exec("COMMIT");
|
|
27896
|
+
} catch (e) {
|
|
27897
|
+
db().exec("ROLLBACK");
|
|
27898
|
+
throw e;
|
|
27899
|
+
}
|
|
27555
27900
|
if (isAvailable()) {
|
|
27556
27901
|
embedDistillation(metaId, result.observations);
|
|
27557
27902
|
}
|
|
27558
|
-
archiveDistillations(existing.map((d) => d.id));
|
|
27559
27903
|
return result;
|
|
27560
27904
|
}
|
|
27561
27905
|
|
|
@@ -27714,13 +28058,13 @@ function searchDistillationsScored(input) {
|
|
|
27714
28058
|
const q = ftsQuery(input.query);
|
|
27715
28059
|
if (q === EMPTY_QUERY) return [];
|
|
27716
28060
|
const ftsSQL = input.sessionID ? `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27717
|
-
FROM
|
|
27718
|
-
JOIN
|
|
28061
|
+
FROM distillation_fts f
|
|
28062
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27719
28063
|
WHERE distillation_fts MATCH ?
|
|
27720
28064
|
AND d.project_id = ? AND d.session_id = ?
|
|
27721
28065
|
ORDER BY rank LIMIT ?` : `SELECT d.id, d.observations, d.generation, d.created_at, d.session_id, rank
|
|
27722
|
-
FROM
|
|
27723
|
-
JOIN
|
|
28066
|
+
FROM distillation_fts f
|
|
28067
|
+
CROSS JOIN distillations d ON d.rowid = f.rowid
|
|
27724
28068
|
WHERE distillation_fts MATCH ?
|
|
27725
28069
|
AND d.project_id = ?
|
|
27726
28070
|
ORDER BY rank LIMIT ?`;
|
|
@@ -28163,7 +28507,130 @@ function importFromFile(input) {
|
|
|
28163
28507
|
}
|
|
28164
28508
|
}
|
|
28165
28509
|
}
|
|
28510
|
+
|
|
28511
|
+
// src/worker-model.ts
|
|
28512
|
+
var worker_model_exports = {};
|
|
28513
|
+
__export(worker_model_exports, {
|
|
28514
|
+
WORKER_JUDGE_SYSTEM: () => WORKER_JUDGE_SYSTEM,
|
|
28515
|
+
computeModelFingerprint: () => computeModelFingerprint,
|
|
28516
|
+
getValidatedWorkerModel: () => getValidatedWorkerModel,
|
|
28517
|
+
isValidationStale: () => isValidationStale,
|
|
28518
|
+
parseJudgeScore: () => parseJudgeScore,
|
|
28519
|
+
resolveWorkerModel: () => resolveWorkerModel,
|
|
28520
|
+
selectWorkerCandidates: () => selectWorkerCandidates,
|
|
28521
|
+
storeValidatedWorkerModel: () => storeValidatedWorkerModel,
|
|
28522
|
+
structuralCheck: () => structuralCheck,
|
|
28523
|
+
workerJudgeUser: () => workerJudgeUser
|
|
28524
|
+
});
|
|
28525
|
+
var KV_PREFIX = "lore:worker_model:";
|
|
28526
|
+
function selectWorkerCandidates(sessionModel, providerModels) {
|
|
28527
|
+
const eligible = providerModels.filter(
|
|
28528
|
+
(m) => m.providerID === sessionModel.providerID && m.status === "active" && m.capabilities.input.text
|
|
28529
|
+
);
|
|
28530
|
+
if (eligible.length === 0) return [];
|
|
28531
|
+
const sorted = [...eligible].sort((a, b) => a.cost.input - b.cost.input);
|
|
28532
|
+
const cheapest = sorted[0];
|
|
28533
|
+
const belowSession = sorted.filter((m) => m.cost.input < sessionModel.cost.input).pop();
|
|
28534
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
28535
|
+
candidates.set(cheapest.id, cheapest);
|
|
28536
|
+
if (belowSession && belowSession.id !== cheapest.id) {
|
|
28537
|
+
candidates.set(belowSession.id, belowSession);
|
|
28538
|
+
}
|
|
28539
|
+
if (cheapest.id === sessionModel.id || cheapest.cost.input >= sessionModel.cost.input) {
|
|
28540
|
+
return [cheapest];
|
|
28541
|
+
}
|
|
28542
|
+
return [...candidates.values()];
|
|
28543
|
+
}
|
|
28544
|
+
function computeModelFingerprint(providerID, sessionModelID, activeModelIDs) {
|
|
28545
|
+
const sorted = [...activeModelIDs].sort();
|
|
28546
|
+
return sha256(
|
|
28547
|
+
JSON.stringify({ providerID, sessionModelID, modelIDs: sorted })
|
|
28548
|
+
);
|
|
28549
|
+
}
|
|
28550
|
+
function getValidatedWorkerModel(providerID) {
|
|
28551
|
+
const row = db().query("SELECT value FROM kv_meta WHERE key = ?").get(`${KV_PREFIX}${providerID}`);
|
|
28552
|
+
if (!row) return null;
|
|
28553
|
+
try {
|
|
28554
|
+
return JSON.parse(row.value);
|
|
28555
|
+
} catch {
|
|
28556
|
+
return null;
|
|
28557
|
+
}
|
|
28558
|
+
}
|
|
28559
|
+
function storeValidatedWorkerModel(result) {
|
|
28560
|
+
const key = `${KV_PREFIX}${result.providerID}`;
|
|
28561
|
+
const value = JSON.stringify(result);
|
|
28562
|
+
db().query(
|
|
28563
|
+
"INSERT INTO kv_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?"
|
|
28564
|
+
).run(key, value, value);
|
|
28565
|
+
}
|
|
28566
|
+
function isValidationStale(stored, currentFingerprint) {
|
|
28567
|
+
if (!stored) return true;
|
|
28568
|
+
return stored.fingerprint !== currentFingerprint;
|
|
28569
|
+
}
|
|
28570
|
+
function structuralCheck(candidateObservations, referenceObservations) {
|
|
28571
|
+
if (candidateObservations == null || candidateObservations.length === 0) {
|
|
28572
|
+
return { passed: false, observationCount: 0, tokenCount: 0, reason: candidateObservations === null ? "parse_failed" : "empty" };
|
|
28573
|
+
}
|
|
28574
|
+
const countObs = (text4) => text4.split("\n").filter((l) => l.trim().length > 0).length;
|
|
28575
|
+
const refCount = countObs(referenceObservations);
|
|
28576
|
+
const candCount = countObs(candidateObservations);
|
|
28577
|
+
const candTokens = Math.ceil(candidateObservations.length / 3);
|
|
28578
|
+
if (refCount > 0 && (candCount < refCount * 0.5 || candCount > refCount * 1.5)) {
|
|
28579
|
+
return {
|
|
28580
|
+
passed: false,
|
|
28581
|
+
observationCount: candCount,
|
|
28582
|
+
tokenCount: candTokens,
|
|
28583
|
+
reason: `observation_count_${candCount}_vs_ref_${refCount}`
|
|
28584
|
+
};
|
|
28585
|
+
}
|
|
28586
|
+
const refTokens = Math.ceil(referenceObservations.length / 3);
|
|
28587
|
+
if (candTokens === 0) {
|
|
28588
|
+
return { passed: false, observationCount: candCount, tokenCount: candTokens, reason: "empty" };
|
|
28589
|
+
}
|
|
28590
|
+
if (refTokens > 0 && candTokens > refTokens * 3) {
|
|
28591
|
+
return {
|
|
28592
|
+
passed: false,
|
|
28593
|
+
observationCount: candCount,
|
|
28594
|
+
tokenCount: candTokens,
|
|
28595
|
+
reason: `token_count_${candTokens}_vs_ref_${refTokens}_3x`
|
|
28596
|
+
};
|
|
28597
|
+
}
|
|
28598
|
+
return { passed: true, observationCount: candCount, tokenCount: candTokens };
|
|
28599
|
+
}
|
|
28600
|
+
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.
|
|
28601
|
+
|
|
28602
|
+
Rate the candidate on a scale of 1-5:
|
|
28603
|
+
5 = Captures all key facts and decisions, equivalent to reference
|
|
28604
|
+
4 = Captures most facts, minor omissions
|
|
28605
|
+
3 = Captures the essential facts, some detail loss acceptable
|
|
28606
|
+
2 = Missing important facts or technical details
|
|
28607
|
+
1 = Significantly incomplete or inaccurate
|
|
28608
|
+
|
|
28609
|
+
Respond with ONLY a single digit (1-5).`;
|
|
28610
|
+
function workerJudgeUser(reference, candidate) {
|
|
28611
|
+
return `<reference>
|
|
28612
|
+
${reference}
|
|
28613
|
+
</reference>
|
|
28614
|
+
|
|
28615
|
+
<candidate>
|
|
28616
|
+
${candidate}
|
|
28617
|
+
</candidate>`;
|
|
28618
|
+
}
|
|
28619
|
+
function parseJudgeScore(response) {
|
|
28620
|
+
const match = response.trim().match(/^([1-5])/);
|
|
28621
|
+
if (!match) return null;
|
|
28622
|
+
return parseInt(match[1], 10);
|
|
28623
|
+
}
|
|
28624
|
+
function resolveWorkerModel(providerID, configWorkerModel, configModel) {
|
|
28625
|
+
if (configWorkerModel) return configWorkerModel;
|
|
28626
|
+
const validated = getValidatedWorkerModel(providerID);
|
|
28627
|
+
if (validated) {
|
|
28628
|
+
return { providerID: validated.providerID, modelID: validated.modelID };
|
|
28629
|
+
}
|
|
28630
|
+
return configModel;
|
|
28631
|
+
}
|
|
28166
28632
|
export {
|
|
28633
|
+
COMPACT_SUMMARY_TEMPLATE,
|
|
28167
28634
|
CONSOLIDATION_SYSTEM,
|
|
28168
28635
|
CURATOR_SYSTEM,
|
|
28169
28636
|
DISTILLATION_SYSTEM,
|
|
@@ -28172,10 +28639,14 @@ export {
|
|
|
28172
28639
|
RECALL_PARAM_DESCRIPTIONS,
|
|
28173
28640
|
RECALL_TOOL_DESCRIPTION,
|
|
28174
28641
|
RECURSIVE_SYSTEM,
|
|
28642
|
+
WORKER_JUDGE_SYSTEM,
|
|
28643
|
+
buildCompactPrompt,
|
|
28175
28644
|
calibrate,
|
|
28176
28645
|
close,
|
|
28646
|
+
computeLayer0Cap,
|
|
28177
28647
|
config2 as config,
|
|
28178
28648
|
consolidationUser,
|
|
28649
|
+
consumeCameOutOfIdle,
|
|
28179
28650
|
curator_exports as curator,
|
|
28180
28651
|
curatorUser,
|
|
28181
28652
|
db,
|
|
@@ -28197,6 +28668,7 @@ export {
|
|
|
28197
28668
|
h,
|
|
28198
28669
|
importFromFile,
|
|
28199
28670
|
inline,
|
|
28671
|
+
inspectSessionState,
|
|
28200
28672
|
isFirstRun,
|
|
28201
28673
|
isReasoningPart,
|
|
28202
28674
|
isTextPart,
|
|
@@ -28211,6 +28683,7 @@ export {
|
|
|
28211
28683
|
ltm_exports as ltm,
|
|
28212
28684
|
needsUrgentDistillation,
|
|
28213
28685
|
normalize,
|
|
28686
|
+
onIdleResume,
|
|
28214
28687
|
p,
|
|
28215
28688
|
projectId,
|
|
28216
28689
|
projectName,
|
|
@@ -28222,15 +28695,20 @@ export {
|
|
|
28222
28695
|
saveForceMinLayer,
|
|
28223
28696
|
serialize,
|
|
28224
28697
|
setForceMinLayer,
|
|
28698
|
+
setLastTurnAtForTest,
|
|
28225
28699
|
setLtmTokens,
|
|
28700
|
+
setMaxLayer0Tokens,
|
|
28226
28701
|
setModelLimits,
|
|
28227
28702
|
shouldImport,
|
|
28228
28703
|
strong2 as strong,
|
|
28229
28704
|
t,
|
|
28230
28705
|
temporal_exports as temporal,
|
|
28706
|
+
toolStripAnnotation,
|
|
28231
28707
|
transform2 as transform,
|
|
28232
28708
|
ul,
|
|
28233
28709
|
unescapeMarkdown,
|
|
28710
|
+
workerJudgeUser,
|
|
28711
|
+
worker_model_exports as workerModel,
|
|
28234
28712
|
workerSessionIDs
|
|
28235
28713
|
};
|
|
28236
28714
|
//# sourceMappingURL=index.js.map
|