@stitchdb/cli 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/cli.js +87 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -761,19 +761,97 @@ async function cmdHook(args) {
|
|
|
761
761
|
content = content.trim();
|
|
762
762
|
if (!content)
|
|
763
763
|
return;
|
|
764
|
+
// For UserPromptSubmit: log the turn AND fetch task-relevant memory in
|
|
765
|
+
// parallel, so the model sees only what's actually relevant to *this*
|
|
766
|
+
// prompt — not 8 random preloaded memories from session start. Task
|
|
767
|
+
// changes self-surface because the recall hits change with the prompt.
|
|
768
|
+
if (eventName === 'UserPromptSubmit') {
|
|
769
|
+
await Promise.all([
|
|
770
|
+
(async () => {
|
|
771
|
+
try {
|
|
772
|
+
const stitch = client(cfg);
|
|
773
|
+
await stitch.thread(threadName).append({ role: role, content });
|
|
774
|
+
}
|
|
775
|
+
catch { /* silent */ }
|
|
776
|
+
})(),
|
|
777
|
+
handleUserPromptInjection(cfg, content),
|
|
778
|
+
]);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
// Stop: log the assistant turn, then maybe-distill.
|
|
764
782
|
try {
|
|
765
783
|
const stitch = client(cfg);
|
|
766
|
-
await stitch.thread(threadName).append({ role, content });
|
|
784
|
+
await stitch.thread(threadName).append({ role: role, content });
|
|
767
785
|
}
|
|
768
|
-
catch {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
786
|
+
catch { /* silent */ }
|
|
787
|
+
maybeAutoDistill(threadName).catch(() => { });
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Per-prompt smart context injection. Runs `recall(prompt)` against the
|
|
791
|
+
* project workspace + the user's `_global` workspace, score-gates the hits,
|
|
792
|
+
* and writes a Claude-Code-flavoured `additionalContext` JSON object to
|
|
793
|
+
* stdout. Time-bounded so a slow proxy doesn't delay the user's prompt.
|
|
794
|
+
*
|
|
795
|
+
* Why per-prompt: a static SessionStart block must guess what'll matter
|
|
796
|
+
* across the whole session. A per-prompt recall sees the actual question
|
|
797
|
+
* and pulls memory tailored to *that* — much higher signal per token.
|
|
798
|
+
* A task switch ("now let's do the dashboard") naturally surfaces the
|
|
799
|
+
* dashboard memories; an unrelated follow-up ("ok run the tests") returns
|
|
800
|
+
* low scores and we inject nothing — no noise.
|
|
801
|
+
*/
|
|
802
|
+
const PROMPT_INJECTION_TIMEOUT_MS = 2000;
|
|
803
|
+
const PROMPT_INJECTION_MIN_SCORE = 0.4;
|
|
804
|
+
async function handleUserPromptInjection(cfg, prompt) {
|
|
805
|
+
if (!prompt || prompt.length < 10)
|
|
806
|
+
return;
|
|
807
|
+
try {
|
|
808
|
+
const baseUrl = cfg.baseUrl || 'https://db.stitchdb.com';
|
|
809
|
+
const stitch = client(cfg);
|
|
810
|
+
const tOut = new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), PROMPT_INJECTION_TIMEOUT_MS));
|
|
811
|
+
// Project recall + global recall in parallel; tolerate either failing.
|
|
812
|
+
const projectHitsP = stitch.recall(prompt, { k: 3 }).catch(() => []);
|
|
813
|
+
const globalHitsP = (async () => {
|
|
814
|
+
try {
|
|
815
|
+
const ws = await stitch.workspaces.list();
|
|
816
|
+
const g = ws.find((w) => w.name === '_global');
|
|
817
|
+
if (!g)
|
|
818
|
+
return [];
|
|
819
|
+
const gc = new Stitch({ apiKey: cfg.apiKey, baseUrl, workspace: g.id });
|
|
820
|
+
return gc.recall(prompt, { k: 2 }).catch(() => []);
|
|
821
|
+
}
|
|
822
|
+
catch {
|
|
823
|
+
return [];
|
|
824
|
+
}
|
|
825
|
+
})();
|
|
826
|
+
const [projectHits, globalHits] = await Promise.race([
|
|
827
|
+
Promise.all([projectHitsP, globalHitsP]),
|
|
828
|
+
tOut,
|
|
829
|
+
]);
|
|
830
|
+
const project = projectHits.filter((h) => h.score >= PROMPT_INJECTION_MIN_SCORE).slice(0, 2);
|
|
831
|
+
const global = globalHits.filter((h) => h.score >= PROMPT_INJECTION_MIN_SCORE).slice(0, 1);
|
|
832
|
+
if (project.length === 0 && global.length === 0)
|
|
833
|
+
return;
|
|
834
|
+
const lines = ['<stitch-recall>'];
|
|
835
|
+
if (global.length > 0) {
|
|
836
|
+
lines.push('User-level rules relevant here:');
|
|
837
|
+
for (const h of global) {
|
|
838
|
+
const txt = String(h.content || '').replace(/\n+/g, ' ').slice(0, 250);
|
|
839
|
+
lines.push(`- [${h.kind}] ${txt}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (project.length > 0) {
|
|
843
|
+
lines.push('Project memory relevant to this prompt:');
|
|
844
|
+
for (const h of project) {
|
|
845
|
+
const txt = String(h.content || '').replace(/\n+/g, ' ').slice(0, 300);
|
|
846
|
+
const src = h.source_thread_id ? ' _(thread receipt available — call thread_recall to dig)_' : '';
|
|
847
|
+
lines.push(`- [${h.kind}] (score ${Number(h.score).toFixed(2)}) ${txt}${src}`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
lines.push('</stitch-recall>');
|
|
851
|
+
const payload = { hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: lines.join('\n') } };
|
|
852
|
+
process.stdout.write(JSON.stringify(payload));
|
|
776
853
|
}
|
|
854
|
+
catch { /* silent — never break a prompt */ }
|
|
777
855
|
}
|
|
778
856
|
/**
|
|
779
857
|
* Derive a thread name for the project at `cwd`. Strategy:
|