osborn 0.9.220 → 0.9.222
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/claude-llm.js +33 -2
- package/dist/embedder.js +2 -2
- package/dist/session-store.d.ts +3 -0
- package/dist/session-store.js +43 -1
- package/package.json +1 -1
package/dist/claude-llm.js
CHANGED
|
@@ -86,13 +86,13 @@ async function buildRecallInjection(sessionId, workingDir, prompt) {
|
|
|
86
86
|
return ''; // recall is best-effort — never break the turn
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
// ≤
|
|
89
|
+
// ≤5 direct tool call budget per turn. Reset on every UserPromptSubmit (new user message).
|
|
90
90
|
// Enforced mechanically in PreToolUse — the model CANNOT exceed this regardless of JSONL history.
|
|
91
91
|
// Task/Agent delegations are exempt (delegation is what we WANT). Sub-agent tool calls
|
|
92
92
|
// (agent_type !== null) are exempt (they're inside a delegation). Only the main orchestrator
|
|
93
93
|
// agent's direct tool calls count against the budget.
|
|
94
94
|
let turnToolCallCount = 0;
|
|
95
|
-
const TOOL_CALL_BUDGET =
|
|
95
|
+
const TOOL_CALL_BUDGET = 5;
|
|
96
96
|
/**
|
|
97
97
|
* Strip markdown formatting for TTS (text-to-speech)
|
|
98
98
|
* Removes **bold**, ##headers, ```code```, etc. so TTS doesn't read them literally
|
|
@@ -1704,6 +1704,17 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1704
1704
|
return {};
|
|
1705
1705
|
}],
|
|
1706
1706
|
}],
|
|
1707
|
+
// Self-review gate — block ONCE (stop_hook_active-guarded) so the reviewer
|
|
1708
|
+
// re-checks its own verdict before returning, instead of stopping on a
|
|
1709
|
+
// first-pass judgment.
|
|
1710
|
+
Stop: [{
|
|
1711
|
+
matcher: '.*',
|
|
1712
|
+
hooks: [async (input) => {
|
|
1713
|
+
if (input?.stop_hook_active)
|
|
1714
|
+
return {};
|
|
1715
|
+
return { decision: 'block', reason: 'Before you finalize: re-read your findings against the diff once. Confirm each BLOCKER/MAJOR is real and reproducible (not a style nit or a false positive), and that your verdict matches the severity of what you actually found. Then end with exactly `VERDICT: ACCEPT` or `VERDICT: REJECT`.' };
|
|
1716
|
+
}],
|
|
1717
|
+
}],
|
|
1707
1718
|
},
|
|
1708
1719
|
};
|
|
1709
1720
|
console.log(`[DISPATCH] spawning reviewer for agentId=${agentId.slice(0, 8)}`);
|
|
@@ -1793,6 +1804,16 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1793
1804
|
return {};
|
|
1794
1805
|
}],
|
|
1795
1806
|
}],
|
|
1807
|
+
// Self-review gate — block ONCE (stop_hook_active-guarded) so the tester
|
|
1808
|
+
// re-checks its own conclusion before returning.
|
|
1809
|
+
Stop: [{
|
|
1810
|
+
matcher: '.*',
|
|
1811
|
+
hooks: [async (input) => {
|
|
1812
|
+
if (input?.stop_hook_active)
|
|
1813
|
+
return {};
|
|
1814
|
+
return { decision: 'block', reason: 'Before you finalize: re-check your conclusion once. Did you actually RUN the commands (not assume the outcome)? Does your result match the real output, and did you distinguish a genuine regression from a flaky/environment failure? Then end with exactly `RESULT: PASS` or `RESULT: FAIL` followed by a brief summary.' };
|
|
1815
|
+
}],
|
|
1816
|
+
}],
|
|
1796
1817
|
},
|
|
1797
1818
|
};
|
|
1798
1819
|
console.log(`[DISPATCH] spawning tester for agentId=${agentId.slice(0, 8)}`);
|
|
@@ -1879,6 +1900,16 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1879
1900
|
return {};
|
|
1880
1901
|
}],
|
|
1881
1902
|
}],
|
|
1903
|
+
// Self-review gate — block ONCE (stop_hook_active-guarded) so the gate
|
|
1904
|
+
// re-checks its own PASS/NEEDS-MORE call before returning.
|
|
1905
|
+
Stop: [{
|
|
1906
|
+
matcher: '.*',
|
|
1907
|
+
hooks: [async (input) => {
|
|
1908
|
+
if (input?.stop_hook_active)
|
|
1909
|
+
return {};
|
|
1910
|
+
return { decision: 'block', reason: 'Before you finalize: re-check your gate decision once. Is the research genuinely complete and well-sourced for the original question — not passing thin work, nor failing solid work? Then end with exactly `GATE: PASS` or `GATE: NEEDS-MORE`.' };
|
|
1911
|
+
}],
|
|
1912
|
+
}],
|
|
1882
1913
|
},
|
|
1883
1914
|
};
|
|
1884
1915
|
console.log(`[DISPATCH] spawning research-gate for agentId=${agentId.slice(0, 8)}`);
|
package/dist/embedder.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Model cache honors TRANSFORMERS_CACHE / HF_HOME so it can be baked into the image.
|
|
17
17
|
*/
|
|
18
|
-
import { EMBED_DIM } from './session-store.js';
|
|
19
|
-
const MODEL_ID =
|
|
18
|
+
import { EMBED_DIM, EMBED_MODEL } from './session-store.js';
|
|
19
|
+
const MODEL_ID = EMBED_MODEL;
|
|
20
20
|
let pipelinePromise = null;
|
|
21
21
|
let disabled = false;
|
|
22
22
|
/** Quantize a normalized float32 embedding to int8[dim] (×127, clamped). */
|
package/dist/session-store.d.ts
CHANGED
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
import Database from 'better-sqlite3';
|
|
31
31
|
export declare const STORE_VERSION = 1;
|
|
32
32
|
export declare const EMBED_DIM = 384;
|
|
33
|
+
/** Canonical embed model id — the single source of truth (embedder.ts imports this).
|
|
34
|
+
* Recorded in meta.embed_model so a model swap is detected as a stale store. */
|
|
35
|
+
export declare const EMBED_MODEL: string;
|
|
33
36
|
/** An embedder turns text into int8[EMBED_DIM] vectors (quantized, normalized). */
|
|
34
37
|
export type Embedder = (texts: string[]) => Promise<Int8Array[] | null>;
|
|
35
38
|
export interface RecallHit {
|
package/dist/session-store.js
CHANGED
|
@@ -39,6 +39,9 @@ import { getSessionPaths, getSessionSubAgents, projectPathToSlug } from './sessi
|
|
|
39
39
|
// ============================================================
|
|
40
40
|
export const STORE_VERSION = 1;
|
|
41
41
|
export const EMBED_DIM = 384;
|
|
42
|
+
/** Canonical embed model id — the single source of truth (embedder.ts imports this).
|
|
43
|
+
* Recorded in meta.embed_model so a model swap is detected as a stale store. */
|
|
44
|
+
export const EMBED_MODEL = process.env.OSBORN_EMBED_MODEL || 'Xenova/all-MiniLM-L6-v2';
|
|
42
45
|
// ============================================================
|
|
43
46
|
// PATHS
|
|
44
47
|
// ============================================================
|
|
@@ -64,6 +67,41 @@ export function openStore(dbPath) {
|
|
|
64
67
|
sqliteVec.load(db);
|
|
65
68
|
db.pragma('journal_mode = WAL');
|
|
66
69
|
db.pragma('synchronous = NORMAL');
|
|
70
|
+
// ── Migration gate ──────────────────────────────────────────────
|
|
71
|
+
// The derived tables (content/fts/vec/sources) are a re-derivable cache of the
|
|
72
|
+
// JSONL, which is the source of truth. If this DB was built by a different
|
|
73
|
+
// STORE_VERSION, EMBED_DIM, or embed model, the cache is stale — most fatally the
|
|
74
|
+
// vec table is fixed at its old dimension, so queries embedded at the new dim throw
|
|
75
|
+
// or return garbage. Detect that and drop the derived tables + reset offsets; the
|
|
76
|
+
// next updateSessionStore() rebuilds them fresh from the JSONL at offset 0.
|
|
77
|
+
//
|
|
78
|
+
// A store with NO recorded embed_model (i.e. predating this stamp) is treated as
|
|
79
|
+
// NOT stale — otherwise the first rollout of this code would wipe every existing DB.
|
|
80
|
+
// We only wipe on a real mismatch, never on absence.
|
|
81
|
+
const hasMeta = db
|
|
82
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='meta'`)
|
|
83
|
+
.get();
|
|
84
|
+
if (hasMeta) {
|
|
85
|
+
const readMeta = db.prepare('SELECT value FROM meta WHERE key = ?');
|
|
86
|
+
const prevVersion = readMeta.get('version')?.value ?? null;
|
|
87
|
+
const prevDim = readMeta.get('embed_dim')?.value ?? null;
|
|
88
|
+
const prevModel = readMeta.get('embed_model')?.value ?? null;
|
|
89
|
+
const stale = (prevVersion != null && prevVersion !== String(STORE_VERSION)) ||
|
|
90
|
+
(prevDim != null && prevDim !== String(EMBED_DIM)) ||
|
|
91
|
+
(prevModel != null && prevModel !== EMBED_MODEL);
|
|
92
|
+
if (stale) {
|
|
93
|
+
console.warn(`⚠️ recall store ${dbPath} is stale ` +
|
|
94
|
+
`(version ${prevVersion}→${STORE_VERSION}, dim ${prevDim}→${EMBED_DIM}, ` +
|
|
95
|
+
`model ${prevModel}→${EMBED_MODEL}) — rebuilding derived tables from JSONL`);
|
|
96
|
+
db.exec(`
|
|
97
|
+
DROP TABLE IF EXISTS content;
|
|
98
|
+
DROP TABLE IF EXISTS fts;
|
|
99
|
+
DROP TABLE IF EXISTS vec;
|
|
100
|
+
DROP TABLE IF EXISTS sources;
|
|
101
|
+
DELETE FROM meta WHERE key IN ('version', 'embed_dim', 'embed_model');
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
67
105
|
db.exec(`
|
|
68
106
|
CREATE TABLE IF NOT EXISTS content (
|
|
69
107
|
id INTEGER PRIMARY KEY,
|
|
@@ -94,9 +132,13 @@ export function openStore(dbPath) {
|
|
|
94
132
|
|
|
95
133
|
CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
96
134
|
`);
|
|
97
|
-
|
|
135
|
+
// INSERT OR REPLACE (not IGNORE) so a rebuilt store restamps its identity; the
|
|
136
|
+
// migration gate above cleared these rows on a stale open, so this writes the
|
|
137
|
+
// current version/dim/model that the freshly-created tables were built for.
|
|
138
|
+
const setMeta = db.prepare('INSERT OR REPLACE INTO meta(key, value) VALUES (?, ?)');
|
|
98
139
|
setMeta.run('version', String(STORE_VERSION));
|
|
99
140
|
setMeta.run('embed_dim', String(EMBED_DIM));
|
|
141
|
+
setMeta.run('embed_model', EMBED_MODEL);
|
|
100
142
|
return db;
|
|
101
143
|
}
|
|
102
144
|
// ============================================================
|