context-mode 1.0.99 → 1.0.100
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/pi-extension.js +1 -1
- package/build/search/auto-memory.d.ts +29 -0
- package/build/search/auto-memory.js +121 -0
- package/build/search/unified.d.ts +41 -0
- package/build/search/unified.js +89 -0
- package/build/server.js +69 -21
- package/build/session/analytics.js +1 -1
- package/build/session/db.d.ts +17 -0
- package/build/session/db.js +28 -0
- package/build/session/extract.d.ts +4 -0
- package/build/session/extract.js +232 -1
- package/build/session/snapshot.js +31 -0
- package/build/store.js +67 -4
- package/build/types.d.ts +1 -0
- package/cli.bundle.mjs +254 -119
- package/configs/claude-code/CLAUDE.md +21 -1
- package/configs/codex/AGENTS.md +22 -1
- package/configs/cursor/context-mode.mdc +18 -1
- package/configs/gemini-cli/GEMINI.md +22 -1
- package/configs/jetbrains-copilot/copilot-instructions.md +22 -1
- package/configs/kilo/AGENTS.md +19 -2
- package/configs/kiro/KIRO.md +18 -1
- package/configs/openclaw/AGENTS.md +22 -2
- package/configs/opencode/AGENTS.md +18 -1
- package/configs/pi/AGENTS.md +18 -1
- package/configs/qwen-code/QWEN.md +38 -18
- package/configs/vscode-copilot/copilot-instructions.md +22 -1
- package/hooks/auto-injection.mjs +76 -0
- package/hooks/codex/userpromptsubmit.mjs +1 -1
- package/hooks/core/mcp-ready.mjs +7 -1
- package/hooks/posttooluse.mjs +50 -1
- package/hooks/precompact.mjs +9 -0
- package/hooks/pretooluse.mjs +27 -0
- package/hooks/routing-block.mjs +7 -1
- package/hooks/session-db.bundle.mjs +19 -13
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-snapshot.bundle.mjs +18 -17
- package/hooks/sessionstart.mjs +17 -0
- package/hooks/userpromptsubmit.mjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +227 -92
package/build/session/extract.js
CHANGED
|
@@ -335,9 +335,36 @@ function extractSkill(input) {
|
|
|
335
335
|
type: "skill",
|
|
336
336
|
category: "skill",
|
|
337
337
|
data: safeString(skillName),
|
|
338
|
-
priority:
|
|
338
|
+
priority: 2,
|
|
339
339
|
}];
|
|
340
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Category 16: constraint
|
|
343
|
+
* Constraints discovered through error events — tool failures reveal
|
|
344
|
+
* platform/environment limitations worth remembering.
|
|
345
|
+
*/
|
|
346
|
+
function extractConstraint(input) {
|
|
347
|
+
// Only fire on error events — constraints are discovered through failures
|
|
348
|
+
if (!input.tool_response?.includes("Error") && !input.tool_output?.isError)
|
|
349
|
+
return [];
|
|
350
|
+
const response = String(input.tool_response || "");
|
|
351
|
+
const patterns = [/not supported/i, /cannot/i, /does not support/i, /FAIL/i, /refused/i, /permission denied/i, /incompatible/i];
|
|
352
|
+
for (const pattern of patterns) {
|
|
353
|
+
const match = response.match(pattern);
|
|
354
|
+
if (match) {
|
|
355
|
+
// Extract context around the match
|
|
356
|
+
const idx = response.toLowerCase().indexOf(match[0].toLowerCase());
|
|
357
|
+
const context = response.slice(Math.max(0, idx - 50), Math.min(response.length, idx + 200)).trim();
|
|
358
|
+
return [{
|
|
359
|
+
type: "constraint_discovered",
|
|
360
|
+
category: "constraint",
|
|
361
|
+
data: safeString(context),
|
|
362
|
+
priority: 2,
|
|
363
|
+
}];
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
341
368
|
/**
|
|
342
369
|
* Category 9: subagent
|
|
343
370
|
* Agent tool calls — tracks both launch and completion.
|
|
@@ -410,6 +437,67 @@ function extractDecision(input) {
|
|
|
410
437
|
priority: 2,
|
|
411
438
|
}];
|
|
412
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Category 22: agent-finding
|
|
442
|
+
* When the Agent tool completes (subagent returns), capture a structured
|
|
443
|
+
* summary of its findings (first 500 chars of tool_response).
|
|
444
|
+
*/
|
|
445
|
+
function extractAgentFinding(input) {
|
|
446
|
+
if (input.tool_name !== "Agent")
|
|
447
|
+
return [];
|
|
448
|
+
if (!input.tool_response || input.tool_response.length === 0)
|
|
449
|
+
return [];
|
|
450
|
+
const summary = input.tool_response.length > 500
|
|
451
|
+
? input.tool_response.slice(0, 500)
|
|
452
|
+
: input.tool_response;
|
|
453
|
+
return [{
|
|
454
|
+
type: "agent_finding",
|
|
455
|
+
category: "agent-finding",
|
|
456
|
+
data: safeString(summary),
|
|
457
|
+
priority: 2,
|
|
458
|
+
}];
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Category 24: external-ref
|
|
462
|
+
* Scan tool_input and tool_response for external URLs, GitHub issues, and PRs.
|
|
463
|
+
* Deduplicates found refs and skips internal URLs (localhost, 127.0.0.1).
|
|
464
|
+
*/
|
|
465
|
+
function extractExternalRef(input) {
|
|
466
|
+
const haystack = [
|
|
467
|
+
safeStringAny(input.tool_input),
|
|
468
|
+
safeString(input.tool_response),
|
|
469
|
+
].join(" ");
|
|
470
|
+
if (haystack.length === 0)
|
|
471
|
+
return [];
|
|
472
|
+
const refs = new Set();
|
|
473
|
+
// URLs — skip localhost / 127.0.0.1
|
|
474
|
+
const urlMatches = haystack.match(/https?:\/\/[^\s)]+/g);
|
|
475
|
+
if (urlMatches) {
|
|
476
|
+
for (let url of urlMatches) {
|
|
477
|
+
// Strip trailing punctuation that gets captured from JSON/prose
|
|
478
|
+
url = url.replace(/["'})\],;.]+$/, "");
|
|
479
|
+
if (!/localhost|127\.0\.0\.1/i.test(url)) {
|
|
480
|
+
refs.add(url);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// Full GitHub issue/PR URLs are already captured above.
|
|
485
|
+
// Shorthand GitHub issue refs: #123 (only bare, not inside a URL)
|
|
486
|
+
const issueMatches = haystack.match(/(?<!\w)#(\d+)/g);
|
|
487
|
+
if (issueMatches) {
|
|
488
|
+
for (const m of issueMatches) {
|
|
489
|
+
refs.add(m);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (refs.size === 0)
|
|
493
|
+
return [];
|
|
494
|
+
return [{
|
|
495
|
+
type: "external_ref",
|
|
496
|
+
category: "external-ref",
|
|
497
|
+
data: safeString(Array.from(refs).join(", ")),
|
|
498
|
+
priority: 3,
|
|
499
|
+
}];
|
|
500
|
+
}
|
|
413
501
|
/**
|
|
414
502
|
* Category 8: env (worktree)
|
|
415
503
|
* EnterWorktree tool — tracks worktree creation.
|
|
@@ -490,6 +578,52 @@ function extractIntent(message) {
|
|
|
490
578
|
priority: 4,
|
|
491
579
|
}];
|
|
492
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* Category 25: blocked-on
|
|
583
|
+
* Detect when work is blocked on something, or when a blocker is resolved.
|
|
584
|
+
*/
|
|
585
|
+
const BLOCKER_PATTERNS = [
|
|
586
|
+
/\bblocked on\b/i,
|
|
587
|
+
/\bwaiting for\b/i,
|
|
588
|
+
/\bneed\s+\S+\s+before\b/i,
|
|
589
|
+
/\bcan'?t proceed until\b/i,
|
|
590
|
+
/\bdepends on\b/i,
|
|
591
|
+
/\bblocked\b/i,
|
|
592
|
+
// Turkish patterns
|
|
593
|
+
/\bbekliyor\b/i,
|
|
594
|
+
/\bbekliyorum\b/i,
|
|
595
|
+
];
|
|
596
|
+
const BLOCKER_RESOLVED_PATTERNS = [
|
|
597
|
+
/\bunblocked\b/i,
|
|
598
|
+
/\bresolved\b/i,
|
|
599
|
+
/\bgot the\s+\S+/i,
|
|
600
|
+
/\bis ready now\b/i,
|
|
601
|
+
/\bcan proceed\b/i,
|
|
602
|
+
];
|
|
603
|
+
function extractBlocker(message) {
|
|
604
|
+
const events = [];
|
|
605
|
+
// Check resolution first — if both match, resolution takes priority
|
|
606
|
+
const isResolved = BLOCKER_RESOLVED_PATTERNS.some(p => p.test(message));
|
|
607
|
+
if (isResolved) {
|
|
608
|
+
events.push({
|
|
609
|
+
type: "blocker_resolved",
|
|
610
|
+
category: "blocked-on",
|
|
611
|
+
data: safeString(message),
|
|
612
|
+
priority: 2,
|
|
613
|
+
});
|
|
614
|
+
return events;
|
|
615
|
+
}
|
|
616
|
+
const isBlocked = BLOCKER_PATTERNS.some(p => p.test(message));
|
|
617
|
+
if (isBlocked) {
|
|
618
|
+
events.push({
|
|
619
|
+
type: "blocker",
|
|
620
|
+
category: "blocked-on",
|
|
621
|
+
data: safeString(message),
|
|
622
|
+
priority: 2,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
return events;
|
|
626
|
+
}
|
|
493
627
|
/**
|
|
494
628
|
* Category 12: data
|
|
495
629
|
* Large user-pasted data references (message > 1KB).
|
|
@@ -504,6 +638,96 @@ function extractData(message) {
|
|
|
504
638
|
priority: 4,
|
|
505
639
|
}];
|
|
506
640
|
}
|
|
641
|
+
// ── Cross-event stateful extractors ───────────────────────────────────────
|
|
642
|
+
/**
|
|
643
|
+
* Category 23: error-resolution
|
|
644
|
+
* Detects when an error is followed by a successful fix (cross-event state).
|
|
645
|
+
*/
|
|
646
|
+
let lastError = null;
|
|
647
|
+
function extractErrorResolution(input) {
|
|
648
|
+
const { tool_name, tool_response, tool_output } = input;
|
|
649
|
+
const response = String(tool_response ?? "");
|
|
650
|
+
const isErrorFlag = tool_output?.isError === true;
|
|
651
|
+
const isBashError = tool_name === "Bash" &&
|
|
652
|
+
/exit code [1-9]|error:|Error:|FAIL|failed/i.test(response);
|
|
653
|
+
// If this call is an error, store it and return
|
|
654
|
+
if (isBashError || isErrorFlag) {
|
|
655
|
+
lastError = { tool: tool_name, error: response.slice(0, 200), callsSince: 0 };
|
|
656
|
+
return [];
|
|
657
|
+
}
|
|
658
|
+
// No pending error → nothing to resolve
|
|
659
|
+
if (!lastError)
|
|
660
|
+
return [];
|
|
661
|
+
// Increment staleness counter
|
|
662
|
+
lastError.callsSince++;
|
|
663
|
+
// Timeout: clear after 10 calls without resolution
|
|
664
|
+
if (lastError.callsSince > 10) {
|
|
665
|
+
lastError = null;
|
|
666
|
+
return [];
|
|
667
|
+
}
|
|
668
|
+
// Check if this is a resolution: same tool, or Edit/Write after a Read error
|
|
669
|
+
const sameTool = tool_name === lastError.tool;
|
|
670
|
+
const editAfterReadError = lastError.tool === "Read" && (tool_name === "Edit" || tool_name === "Write");
|
|
671
|
+
if (sameTool || editAfterReadError) {
|
|
672
|
+
const event = {
|
|
673
|
+
type: "error_resolved",
|
|
674
|
+
category: "error-resolution",
|
|
675
|
+
data: safeString(`Error in ${lastError.tool}: ${lastError.error} → Fixed`),
|
|
676
|
+
priority: 2,
|
|
677
|
+
};
|
|
678
|
+
lastError = null;
|
|
679
|
+
return [event];
|
|
680
|
+
}
|
|
681
|
+
return [];
|
|
682
|
+
}
|
|
683
|
+
/** Reset error-resolution state (for testing). */
|
|
684
|
+
export function resetErrorResolutionState() {
|
|
685
|
+
lastError = null;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Category 26: iteration-loop
|
|
689
|
+
* Detects when the same tool is called repeatedly with similar input (stuck loop).
|
|
690
|
+
*/
|
|
691
|
+
const callHistory = [];
|
|
692
|
+
function simpleHash(str) {
|
|
693
|
+
return `${str.length}:${str.slice(0, 20)}`;
|
|
694
|
+
}
|
|
695
|
+
function extractIterationLoop(input) {
|
|
696
|
+
const { tool_name, tool_input } = input;
|
|
697
|
+
const inputHash = simpleHash(JSON.stringify(tool_input).slice(0, 200));
|
|
698
|
+
callHistory.push({ tool: tool_name, inputHash });
|
|
699
|
+
// Keep history bounded
|
|
700
|
+
if (callHistory.length > 50) {
|
|
701
|
+
callHistory.splice(0, callHistory.length - 50);
|
|
702
|
+
}
|
|
703
|
+
// Check last N entries for repeated pattern (minimum 3)
|
|
704
|
+
if (callHistory.length < 3)
|
|
705
|
+
return [];
|
|
706
|
+
let count = 0;
|
|
707
|
+
for (let i = callHistory.length - 1; i >= 0; i--) {
|
|
708
|
+
if (callHistory[i].tool === tool_name && callHistory[i].inputHash === inputHash) {
|
|
709
|
+
count++;
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (count >= 3) {
|
|
716
|
+
// Reset the matching tail to avoid duplicate emissions
|
|
717
|
+
callHistory.splice(callHistory.length - count);
|
|
718
|
+
return [{
|
|
719
|
+
type: "retry_detected",
|
|
720
|
+
category: "iteration-loop",
|
|
721
|
+
data: safeString(`${tool_name} called ${count} times with similar input`),
|
|
722
|
+
priority: 2,
|
|
723
|
+
}];
|
|
724
|
+
}
|
|
725
|
+
return [];
|
|
726
|
+
}
|
|
727
|
+
/** Reset iteration-loop state (for testing). */
|
|
728
|
+
export function resetIterationLoopState() {
|
|
729
|
+
callHistory.length = 0;
|
|
730
|
+
}
|
|
507
731
|
// ── Public API ─────────────────────────────────────────────────────────────
|
|
508
732
|
/**
|
|
509
733
|
* Extract session events from a PostToolUse hook input.
|
|
@@ -528,7 +752,13 @@ export function extractEvents(input) {
|
|
|
528
752
|
events.push(...extractSubagent(input));
|
|
529
753
|
events.push(...extractMcp(input));
|
|
530
754
|
events.push(...extractDecision(input));
|
|
755
|
+
events.push(...extractConstraint(input));
|
|
531
756
|
events.push(...extractWorktree(input));
|
|
757
|
+
events.push(...extractAgentFinding(input));
|
|
758
|
+
events.push(...extractExternalRef(input));
|
|
759
|
+
// Cross-event stateful extractors
|
|
760
|
+
events.push(...extractErrorResolution(input));
|
|
761
|
+
events.push(...extractIterationLoop(input));
|
|
532
762
|
return events;
|
|
533
763
|
}
|
|
534
764
|
catch {
|
|
@@ -548,6 +778,7 @@ export function extractUserEvents(message) {
|
|
|
548
778
|
events.push(...extractUserDecision(message));
|
|
549
779
|
events.push(...extractRole(message));
|
|
550
780
|
events.push(...extractIntent(message));
|
|
781
|
+
events.push(...extractBlocker(message));
|
|
551
782
|
events.push(...extractData(message));
|
|
552
783
|
return events;
|
|
553
784
|
}
|
|
@@ -312,6 +312,30 @@ function buildSkillsSection(skillEvents, searchTool) {
|
|
|
312
312
|
];
|
|
313
313
|
return lines.join("\n");
|
|
314
314
|
}
|
|
315
|
+
function buildRolesSection(roleEvents, searchTool) {
|
|
316
|
+
if (roleEvents.length === 0)
|
|
317
|
+
return "";
|
|
318
|
+
const seen = new Set();
|
|
319
|
+
const summaryLines = [];
|
|
320
|
+
const queryTerms = [];
|
|
321
|
+
for (const ev of roleEvents) {
|
|
322
|
+
if (seen.has(ev.data))
|
|
323
|
+
continue;
|
|
324
|
+
seen.add(ev.data);
|
|
325
|
+
summaryLines.push(` ${escapeXML(ev.data)}`);
|
|
326
|
+
queryTerms.push(ev.data);
|
|
327
|
+
}
|
|
328
|
+
if (summaryLines.length === 0)
|
|
329
|
+
return "";
|
|
330
|
+
const queries = buildQueries(queryTerms);
|
|
331
|
+
const lines = [
|
|
332
|
+
` <roles count="${summaryLines.length}">`,
|
|
333
|
+
...summaryLines,
|
|
334
|
+
toolCall(searchTool, queries),
|
|
335
|
+
` </roles>`,
|
|
336
|
+
];
|
|
337
|
+
return lines.join("\n");
|
|
338
|
+
}
|
|
315
339
|
function buildIntentSection(intentEvents) {
|
|
316
340
|
if (intentEvents.length === 0)
|
|
317
341
|
return "";
|
|
@@ -344,6 +368,7 @@ export function buildResumeSnapshot(events, opts) {
|
|
|
344
368
|
const subagentEvents = [];
|
|
345
369
|
const intentEvents = [];
|
|
346
370
|
const skillEvents = [];
|
|
371
|
+
const roleEvents = [];
|
|
347
372
|
for (const ev of events) {
|
|
348
373
|
switch (ev.category) {
|
|
349
374
|
case "file":
|
|
@@ -379,6 +404,9 @@ export function buildResumeSnapshot(events, opts) {
|
|
|
379
404
|
case "skill":
|
|
380
405
|
skillEvents.push(ev);
|
|
381
406
|
break;
|
|
407
|
+
case "role":
|
|
408
|
+
roleEvents.push(ev);
|
|
409
|
+
break;
|
|
382
410
|
}
|
|
383
411
|
}
|
|
384
412
|
// ── Build all sections ──
|
|
@@ -417,6 +445,9 @@ export function buildResumeSnapshot(events, opts) {
|
|
|
417
445
|
const skills = buildSkillsSection(skillEvents, searchTool);
|
|
418
446
|
if (skills)
|
|
419
447
|
sections.push(skills);
|
|
448
|
+
const roles = buildRolesSection(roleEvents, searchTool);
|
|
449
|
+
if (roles)
|
|
450
|
+
sections.push(roles);
|
|
420
451
|
const intent = buildIntentSection(intentEvents);
|
|
421
452
|
if (intent)
|
|
422
453
|
sections.push(intent);
|
package/build/store.js
CHANGED
|
@@ -402,6 +402,10 @@ export class ContentStore {
|
|
|
402
402
|
content,
|
|
403
403
|
source_id UNINDEXED,
|
|
404
404
|
content_type UNINDEXED,
|
|
405
|
+
source_category UNINDEXED,
|
|
406
|
+
session_id UNINDEXED,
|
|
407
|
+
event_id UNINDEXED,
|
|
408
|
+
timestamp UNINDEXED,
|
|
405
409
|
tokenize='porter unicode61'
|
|
406
410
|
);
|
|
407
411
|
|
|
@@ -410,6 +414,10 @@ export class ContentStore {
|
|
|
410
414
|
content,
|
|
411
415
|
source_id UNINDEXED,
|
|
412
416
|
content_type UNINDEXED,
|
|
417
|
+
source_category UNINDEXED,
|
|
418
|
+
session_id UNINDEXED,
|
|
419
|
+
event_id UNINDEXED,
|
|
420
|
+
timestamp UNINDEXED,
|
|
413
421
|
tokenize='trigram'
|
|
414
422
|
);
|
|
415
423
|
|
|
@@ -419,6 +427,47 @@ export class ContentStore {
|
|
|
419
427
|
|
|
420
428
|
CREATE INDEX IF NOT EXISTS idx_sources_label ON sources(label);
|
|
421
429
|
`);
|
|
430
|
+
// FTS5 schema migration: old schema (4 cols) → new schema (8 cols).
|
|
431
|
+
// FTS5 virtual tables do not support ALTER TABLE ADD COLUMN, so we must
|
|
432
|
+
// DROP + re-CREATE. Detection: check for sentinel column `source_category`
|
|
433
|
+
// via pragma_table_xinfo. Three states:
|
|
434
|
+
// 1. No table → CREATE above handled it (fresh DB)
|
|
435
|
+
// 2. Old schema (4 cols) → DROP + CREATE new
|
|
436
|
+
// 3. New schema (8 cols) → do nothing
|
|
437
|
+
try {
|
|
438
|
+
const cols = this.#db.prepare("SELECT name FROM pragma_table_xinfo('chunks')").all();
|
|
439
|
+
const colNames = new Set(cols.map(c => c.name));
|
|
440
|
+
if (cols.length > 0 && !colNames.has("source_category")) {
|
|
441
|
+
// Old schema detected — drop both FTS5 tables and re-create with new columns
|
|
442
|
+
this.#db.exec("DROP TABLE IF EXISTS chunks");
|
|
443
|
+
this.#db.exec("DROP TABLE IF EXISTS chunks_trigram");
|
|
444
|
+
this.#db.exec(`
|
|
445
|
+
CREATE VIRTUAL TABLE chunks USING fts5(
|
|
446
|
+
title,
|
|
447
|
+
content,
|
|
448
|
+
source_id UNINDEXED,
|
|
449
|
+
content_type UNINDEXED,
|
|
450
|
+
source_category UNINDEXED,
|
|
451
|
+
session_id UNINDEXED,
|
|
452
|
+
event_id UNINDEXED,
|
|
453
|
+
timestamp UNINDEXED,
|
|
454
|
+
tokenize='porter unicode61'
|
|
455
|
+
);
|
|
456
|
+
CREATE VIRTUAL TABLE chunks_trigram USING fts5(
|
|
457
|
+
title,
|
|
458
|
+
content,
|
|
459
|
+
source_id UNINDEXED,
|
|
460
|
+
content_type UNINDEXED,
|
|
461
|
+
source_category UNINDEXED,
|
|
462
|
+
session_id UNINDEXED,
|
|
463
|
+
event_id UNINDEXED,
|
|
464
|
+
timestamp UNINDEXED,
|
|
465
|
+
tokenize='trigram'
|
|
466
|
+
);
|
|
467
|
+
`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch { /* pragma_table_xinfo may fail if table doesn't exist yet — safe to ignore */ }
|
|
422
471
|
// Stale detection columns — safe for existing DBs (ALTER is O(1) in SQLite)
|
|
423
472
|
try {
|
|
424
473
|
this.#db.exec("ALTER TABLE sources ADD COLUMN file_path TEXT");
|
|
@@ -433,8 +482,8 @@ export class ContentStore {
|
|
|
433
482
|
// Write path
|
|
434
483
|
this.#stmtInsertSourceEmpty = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count, file_path, content_hash) VALUES (?, 0, 0, ?, ?)");
|
|
435
484
|
this.#stmtInsertSource = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count, file_path, content_hash) VALUES (?, ?, ?, ?, ?)");
|
|
436
|
-
this.#stmtInsertChunk = this.#db.prepare("INSERT INTO chunks (title, content, source_id, content_type) VALUES (?, ?, ?, ?)");
|
|
437
|
-
this.#stmtInsertChunkTrigram = this.#db.prepare("INSERT INTO chunks_trigram (title, content, source_id, content_type) VALUES (?, ?, ?, ?)");
|
|
485
|
+
this.#stmtInsertChunk = this.#db.prepare("INSERT INTO chunks (title, content, source_id, content_type, source_category, session_id, event_id, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
486
|
+
this.#stmtInsertChunkTrigram = this.#db.prepare("INSERT INTO chunks_trigram (title, content, source_id, content_type, source_category, session_id, event_id, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
438
487
|
this.#stmtInsertVocab = this.#db.prepare("INSERT OR IGNORE INTO vocabulary (word) VALUES (?)");
|
|
439
488
|
// Dedup path: delete previous source with same label before re-indexing
|
|
440
489
|
// Prevents stale outputs from accumulating in iterative workflows (build-fix-build)
|
|
@@ -447,6 +496,7 @@ export class ContentStore {
|
|
|
447
496
|
chunks.title,
|
|
448
497
|
chunks.content,
|
|
449
498
|
chunks.content_type,
|
|
499
|
+
chunks.timestamp,
|
|
450
500
|
sources.label,
|
|
451
501
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
452
502
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -461,6 +511,7 @@ export class ContentStore {
|
|
|
461
511
|
chunks.title,
|
|
462
512
|
chunks.content,
|
|
463
513
|
chunks.content_type,
|
|
514
|
+
chunks.timestamp,
|
|
464
515
|
sources.label,
|
|
465
516
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
466
517
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -475,6 +526,7 @@ export class ContentStore {
|
|
|
475
526
|
chunks.title,
|
|
476
527
|
chunks.content,
|
|
477
528
|
chunks.content_type,
|
|
529
|
+
chunks.timestamp,
|
|
478
530
|
sources.label,
|
|
479
531
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
480
532
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -489,6 +541,7 @@ export class ContentStore {
|
|
|
489
541
|
chunks_trigram.title,
|
|
490
542
|
chunks_trigram.content,
|
|
491
543
|
chunks_trigram.content_type,
|
|
544
|
+
chunks_trigram.timestamp,
|
|
492
545
|
sources.label,
|
|
493
546
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
494
547
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -503,6 +556,7 @@ export class ContentStore {
|
|
|
503
556
|
chunks_trigram.title,
|
|
504
557
|
chunks_trigram.content,
|
|
505
558
|
chunks_trigram.content_type,
|
|
559
|
+
chunks_trigram.timestamp,
|
|
506
560
|
sources.label,
|
|
507
561
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
508
562
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -517,6 +571,7 @@ export class ContentStore {
|
|
|
517
571
|
chunks_trigram.title,
|
|
518
572
|
chunks_trigram.content,
|
|
519
573
|
chunks_trigram.content_type,
|
|
574
|
+
chunks_trigram.timestamp,
|
|
520
575
|
sources.label,
|
|
521
576
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
522
577
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -532,6 +587,7 @@ export class ContentStore {
|
|
|
532
587
|
chunks.title,
|
|
533
588
|
chunks.content,
|
|
534
589
|
chunks.content_type,
|
|
590
|
+
chunks.timestamp,
|
|
535
591
|
sources.label,
|
|
536
592
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
537
593
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -546,6 +602,7 @@ export class ContentStore {
|
|
|
546
602
|
chunks.title,
|
|
547
603
|
chunks.content,
|
|
548
604
|
chunks.content_type,
|
|
605
|
+
chunks.timestamp,
|
|
549
606
|
sources.label,
|
|
550
607
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
551
608
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -560,6 +617,7 @@ export class ContentStore {
|
|
|
560
617
|
chunks.title,
|
|
561
618
|
chunks.content,
|
|
562
619
|
chunks.content_type,
|
|
620
|
+
chunks.timestamp,
|
|
563
621
|
sources.label,
|
|
564
622
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
565
623
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -574,6 +632,7 @@ export class ContentStore {
|
|
|
574
632
|
chunks_trigram.title,
|
|
575
633
|
chunks_trigram.content,
|
|
576
634
|
chunks_trigram.content_type,
|
|
635
|
+
chunks_trigram.timestamp,
|
|
577
636
|
sources.label,
|
|
578
637
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
579
638
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -588,6 +647,7 @@ export class ContentStore {
|
|
|
588
647
|
chunks_trigram.title,
|
|
589
648
|
chunks_trigram.content,
|
|
590
649
|
chunks_trigram.content_type,
|
|
650
|
+
chunks_trigram.timestamp,
|
|
591
651
|
sources.label,
|
|
592
652
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
593
653
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -602,6 +662,7 @@ export class ContentStore {
|
|
|
602
662
|
chunks_trigram.title,
|
|
603
663
|
chunks_trigram.content,
|
|
604
664
|
chunks_trigram.content_type,
|
|
665
|
+
chunks_trigram.timestamp,
|
|
605
666
|
sources.label,
|
|
606
667
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
607
668
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -714,10 +775,11 @@ export class ContentStore {
|
|
|
714
775
|
}
|
|
715
776
|
const info = this.#stmtInsertSource.run(label, chunks.length, codeChunks, filePath ?? null, contentHash ?? null);
|
|
716
777
|
const sourceId = Number(info.lastInsertRowid);
|
|
778
|
+
const now = new Date().toISOString();
|
|
717
779
|
for (const chunk of chunks) {
|
|
718
780
|
const ct = chunk.hasCode ? "code" : "prose";
|
|
719
|
-
this.#stmtInsertChunk.run(chunk.title, chunk.content, sourceId, ct);
|
|
720
|
-
this.#stmtInsertChunkTrigram.run(chunk.title, chunk.content, sourceId, ct);
|
|
781
|
+
this.#stmtInsertChunk.run(chunk.title, chunk.content, sourceId, ct, null, null, null, now);
|
|
782
|
+
this.#stmtInsertChunkTrigram.run(chunk.title, chunk.content, sourceId, ct, null, null, null, now);
|
|
721
783
|
}
|
|
722
784
|
return sourceId;
|
|
723
785
|
});
|
|
@@ -748,6 +810,7 @@ export class ContentStore {
|
|
|
748
810
|
rank: r.rank,
|
|
749
811
|
contentType: r.content_type,
|
|
750
812
|
highlighted: r.highlighted,
|
|
813
|
+
timestamp: r.timestamp ?? undefined,
|
|
751
814
|
}));
|
|
752
815
|
}
|
|
753
816
|
#sourceFilterParam(source, sourceMatchMode) {
|
package/build/types.d.ts
CHANGED