robrain 2.4.10 → 2.5.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/commands/capture.d.ts +96 -0
- package/dist/commands/capture.d.ts.map +1 -0
- package/dist/commands/capture.js +356 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/capture.test.d.ts +2 -0
- package/dist/commands/capture.test.d.ts.map +1 -0
- package/dist/commands/capture.test.js +102 -0
- package/dist/commands/capture.test.js.map +1 -0
- package/dist/commands/check.js +2 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/explain.js +2 -2
- package/dist/commands/explain.js.map +1 -1
- package/dist/commands/export-memory.js +2 -2
- package/dist/commands/export-memory.js.map +1 -1
- package/dist/commands/init-project.js +4 -4
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/inject.d.ts +10 -0
- package/dist/commands/inject.d.ts.map +1 -1
- package/dist/commands/inject.js +52 -6
- package/dist/commands/inject.js.map +1 -1
- package/dist/commands/inject.test.d.ts +2 -0
- package/dist/commands/inject.test.d.ts.map +1 -0
- package/dist/commands/inject.test.js +170 -0
- package/dist/commands/inject.test.js.map +1 -0
- package/dist/commands/install.js +5 -5
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/outcomes.js +4 -4
- package/dist/commands/outcomes.js.map +1 -1
- package/dist/commands/projects.d.ts +5 -0
- package/dist/commands/projects.d.ts.map +1 -1
- package/dist/commands/projects.js +2 -2
- package/dist/commands/projects.js.map +1 -1
- package/dist/commands/review.js +7 -7
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +13 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/index.js +41 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +45 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.d.ts +2 -0
- package/dist/lib/config.test.d.ts.map +1 -0
- package/dist/lib/config.test.js +75 -0
- package/dist/lib/config.test.js.map +1 -0
- package/dist/lib/spinner.d.ts +27 -0
- package/dist/lib/spinner.d.ts.map +1 -0
- package/dist/lib/spinner.js +41 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/spinner.test.d.ts +2 -0
- package/dist/lib/spinner.test.d.ts.map +1 -0
- package/dist/lib/spinner.test.js +39 -0
- package/dist/lib/spinner.test.js.map +1 -0
- package/package.json +2 -2
- package/vendor/sensing-mcp/dist/index.js +347 -84
- package/vendor/sensing-mcp/package.json +1 -1
- package/vendor/synthesis/dist/index.js +274 -7
- package/vendor/synthesis/package.json +1 -1
|
@@ -7190,16 +7190,179 @@ var require_trust = __commonJS({
|
|
|
7190
7190
|
}
|
|
7191
7191
|
});
|
|
7192
7192
|
|
|
7193
|
+
// ../shared/dist/capture-settings.js
|
|
7194
|
+
var require_capture_settings = __commonJS({
|
|
7195
|
+
"../shared/dist/capture-settings.js"(exports) {
|
|
7196
|
+
"use strict";
|
|
7197
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7198
|
+
exports.DEFAULT_CAPTURE_SETTINGS = exports.DEFAULT_CAPTURE_KIND = exports.CAPTURE_MODES = exports.CAPTURE_KIND_DESCRIPTION_MAX_CHARS = exports.CAPTURE_KIND_NAME_MAX_CHARS = exports.CAPTURE_KINDS_MAX = exports.CAPTURE_INSTRUCTIONS_MAX_CHARS = void 0;
|
|
7199
|
+
exports.isDefaultCaptureSettings = isDefaultCaptureSettings;
|
|
7200
|
+
exports.normalizeKindName = normalizeKindName;
|
|
7201
|
+
exports.applyCaptureSettingsPatch = applyCaptureSettingsPatch;
|
|
7202
|
+
exports.coerceCaptureSettings = coerceCaptureSettings2;
|
|
7203
|
+
exports.subjectTokens = subjectTokens;
|
|
7204
|
+
exports.subjectsMatch = subjectsMatch;
|
|
7205
|
+
exports.effectiveConfidenceMin = effectiveConfidenceMin2;
|
|
7206
|
+
exports.allowedKindNames = allowedKindNames;
|
|
7207
|
+
exports.CAPTURE_INSTRUCTIONS_MAX_CHARS = 2e3;
|
|
7208
|
+
exports.CAPTURE_KINDS_MAX = 8;
|
|
7209
|
+
exports.CAPTURE_KIND_NAME_MAX_CHARS = 32;
|
|
7210
|
+
exports.CAPTURE_KIND_DESCRIPTION_MAX_CHARS = 300;
|
|
7211
|
+
exports.CAPTURE_MODES = ["strict", "broad"];
|
|
7212
|
+
exports.DEFAULT_CAPTURE_KIND = "decision";
|
|
7213
|
+
exports.DEFAULT_CAPTURE_SETTINGS = {
|
|
7214
|
+
instructions: null,
|
|
7215
|
+
mode: "strict",
|
|
7216
|
+
confidence_min: null,
|
|
7217
|
+
kinds: []
|
|
7218
|
+
};
|
|
7219
|
+
function isDefaultCaptureSettings(s) {
|
|
7220
|
+
return s.instructions === null && s.mode === "strict" && s.confidence_min === null && s.kinds.length === 0;
|
|
7221
|
+
}
|
|
7222
|
+
function normalizeKindName(name) {
|
|
7223
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7224
|
+
}
|
|
7225
|
+
function applyCaptureSettingsPatch(current, patch) {
|
|
7226
|
+
const errors = [];
|
|
7227
|
+
const next = { ...current, kinds: [...current.kinds] };
|
|
7228
|
+
if (patch.instructions !== void 0) {
|
|
7229
|
+
if (patch.instructions === null || patch.instructions.trim() === "") {
|
|
7230
|
+
next.instructions = null;
|
|
7231
|
+
} else if (typeof patch.instructions !== "string") {
|
|
7232
|
+
errors.push("instructions must be text");
|
|
7233
|
+
} else if (patch.instructions.length > exports.CAPTURE_INSTRUCTIONS_MAX_CHARS) {
|
|
7234
|
+
errors.push(`instructions must be at most ${exports.CAPTURE_INSTRUCTIONS_MAX_CHARS} characters (got ${patch.instructions.length})`);
|
|
7235
|
+
} else {
|
|
7236
|
+
next.instructions = patch.instructions.trim();
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
if (patch.mode !== void 0) {
|
|
7240
|
+
const mode = String(patch.mode).trim().toLowerCase();
|
|
7241
|
+
if (exports.CAPTURE_MODES.includes(mode)) {
|
|
7242
|
+
next.mode = mode;
|
|
7243
|
+
} else {
|
|
7244
|
+
errors.push(`mode must be one of: ${exports.CAPTURE_MODES.join(", ")}`);
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
if (patch.confidence_min !== void 0) {
|
|
7248
|
+
if (patch.confidence_min === null) {
|
|
7249
|
+
next.confidence_min = null;
|
|
7250
|
+
} else if (typeof patch.confidence_min !== "number" || !Number.isFinite(patch.confidence_min)) {
|
|
7251
|
+
errors.push("confidence_min must be a number between 0 and 1");
|
|
7252
|
+
} else if (patch.confidence_min < 0 || patch.confidence_min > 1) {
|
|
7253
|
+
errors.push("confidence_min must be between 0 and 1");
|
|
7254
|
+
} else {
|
|
7255
|
+
next.confidence_min = Math.round(patch.confidence_min * 100) / 100;
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
if (patch.kinds !== void 0) {
|
|
7259
|
+
if (patch.kinds === null) {
|
|
7260
|
+
next.kinds = [];
|
|
7261
|
+
} else if (!Array.isArray(patch.kinds)) {
|
|
7262
|
+
errors.push("kinds must be a list");
|
|
7263
|
+
} else if (patch.kinds.length > exports.CAPTURE_KINDS_MAX) {
|
|
7264
|
+
errors.push(`at most ${exports.CAPTURE_KINDS_MAX} kinds`);
|
|
7265
|
+
} else {
|
|
7266
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7267
|
+
const kinds = [];
|
|
7268
|
+
for (const raw of patch.kinds) {
|
|
7269
|
+
const name = typeof raw?.name === "string" ? normalizeKindName(raw.name) : "";
|
|
7270
|
+
const description = typeof raw?.description === "string" ? raw.description.trim() : "";
|
|
7271
|
+
if (!name) {
|
|
7272
|
+
errors.push("each kind needs a name (letters, digits, - or _)");
|
|
7273
|
+
continue;
|
|
7274
|
+
}
|
|
7275
|
+
if (name.length > exports.CAPTURE_KIND_NAME_MAX_CHARS) {
|
|
7276
|
+
errors.push(`kind name "${name}" is longer than ${exports.CAPTURE_KIND_NAME_MAX_CHARS} characters`);
|
|
7277
|
+
continue;
|
|
7278
|
+
}
|
|
7279
|
+
if (name === exports.DEFAULT_CAPTURE_KIND) {
|
|
7280
|
+
errors.push(`"${exports.DEFAULT_CAPTURE_KIND}" is the built-in kind and cannot be redefined`);
|
|
7281
|
+
continue;
|
|
7282
|
+
}
|
|
7283
|
+
if (!description) {
|
|
7284
|
+
errors.push(`kind "${name}" needs a description so the model knows what to store`);
|
|
7285
|
+
continue;
|
|
7286
|
+
}
|
|
7287
|
+
if (description.length > exports.CAPTURE_KIND_DESCRIPTION_MAX_CHARS) {
|
|
7288
|
+
errors.push(`description for kind "${name}" is longer than ${exports.CAPTURE_KIND_DESCRIPTION_MAX_CHARS} characters`);
|
|
7289
|
+
continue;
|
|
7290
|
+
}
|
|
7291
|
+
if (seen.has(name)) {
|
|
7292
|
+
errors.push(`kind "${name}" is listed twice`);
|
|
7293
|
+
continue;
|
|
7294
|
+
}
|
|
7295
|
+
seen.add(name);
|
|
7296
|
+
kinds.push({ name, description });
|
|
7297
|
+
}
|
|
7298
|
+
next.kinds = kinds;
|
|
7299
|
+
}
|
|
7300
|
+
}
|
|
7301
|
+
return errors.length ? { ok: false, errors } : { ok: true, settings: next };
|
|
7302
|
+
}
|
|
7303
|
+
function coerceCaptureSettings2(raw) {
|
|
7304
|
+
if (!raw || typeof raw !== "object")
|
|
7305
|
+
return { ...exports.DEFAULT_CAPTURE_SETTINGS };
|
|
7306
|
+
const r = raw;
|
|
7307
|
+
const result = applyCaptureSettingsPatch(exports.DEFAULT_CAPTURE_SETTINGS, {
|
|
7308
|
+
instructions: typeof r.instructions === "string" ? r.instructions : null,
|
|
7309
|
+
mode: typeof r.mode === "string" ? r.mode : "strict",
|
|
7310
|
+
confidence_min: typeof r.confidence_min === "number" ? r.confidence_min : null,
|
|
7311
|
+
kinds: Array.isArray(r.kinds) ? r.kinds : []
|
|
7312
|
+
});
|
|
7313
|
+
if (result.ok)
|
|
7314
|
+
return result.settings;
|
|
7315
|
+
const safe = { ...exports.DEFAULT_CAPTURE_SETTINGS };
|
|
7316
|
+
const instructions = applyCaptureSettingsPatch(safe, { instructions: typeof r.instructions === "string" ? r.instructions : null });
|
|
7317
|
+
if (instructions.ok)
|
|
7318
|
+
safe.instructions = instructions.settings.instructions;
|
|
7319
|
+
const mode = applyCaptureSettingsPatch(safe, { mode: typeof r.mode === "string" ? r.mode : "strict" });
|
|
7320
|
+
if (mode.ok)
|
|
7321
|
+
safe.mode = mode.settings.mode;
|
|
7322
|
+
const conf = applyCaptureSettingsPatch(safe, { confidence_min: typeof r.confidence_min === "number" ? r.confidence_min : null });
|
|
7323
|
+
if (conf.ok)
|
|
7324
|
+
safe.confidence_min = conf.settings.confidence_min;
|
|
7325
|
+
const kinds = applyCaptureSettingsPatch(safe, { kinds: Array.isArray(r.kinds) ? r.kinds : [] });
|
|
7326
|
+
if (kinds.ok)
|
|
7327
|
+
safe.kinds = kinds.settings.kinds;
|
|
7328
|
+
return safe;
|
|
7329
|
+
}
|
|
7330
|
+
var SUBJECT_STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "of", "for", "on", "in", "at", "to", "and", "this", "that", "our", "my", "its", "last", "latest", "current"]);
|
|
7331
|
+
function subjectTokens(subject) {
|
|
7332
|
+
return subject.toLowerCase().replace(/[^a-z0-9\s_-]+/g, " ").split(/[\s_-]+/).filter((t) => t.length > 1 && !SUBJECT_STOPWORDS.has(t));
|
|
7333
|
+
}
|
|
7334
|
+
function subjectsMatch(a, b) {
|
|
7335
|
+
const ta = new Set(subjectTokens(a));
|
|
7336
|
+
const tb = new Set(subjectTokens(b));
|
|
7337
|
+
if (ta.size === 0 || tb.size === 0)
|
|
7338
|
+
return false;
|
|
7339
|
+
const [small, large] = ta.size <= tb.size ? [ta, tb] : [tb, ta];
|
|
7340
|
+
for (const t of small)
|
|
7341
|
+
if (!large.has(t))
|
|
7342
|
+
return false;
|
|
7343
|
+
return true;
|
|
7344
|
+
}
|
|
7345
|
+
function effectiveConfidenceMin2(settings, stock) {
|
|
7346
|
+
return settings?.confidence_min ?? stock;
|
|
7347
|
+
}
|
|
7348
|
+
function allowedKindNames(settings) {
|
|
7349
|
+
return [exports.DEFAULT_CAPTURE_KIND, ...(settings?.kinds ?? []).map((k) => k.name)];
|
|
7350
|
+
}
|
|
7351
|
+
}
|
|
7352
|
+
});
|
|
7353
|
+
|
|
7193
7354
|
// ../shared/dist/extract-decision.js
|
|
7194
7355
|
var require_extract_decision = __commonJS({
|
|
7195
7356
|
"../shared/dist/extract-decision.js"(exports) {
|
|
7196
7357
|
"use strict";
|
|
7197
7358
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7198
7359
|
exports.LlmKeyMissingError = exports.SELF_HOSTED_USER_RULE_OVERRIDE = exports.DECISION_EXTRACTION_SYSTEM_PROMPT = void 0;
|
|
7360
|
+
exports.buildCaptureRules = buildCaptureRules;
|
|
7199
7361
|
exports.stripMarkdownJsonFence = stripMarkdownJsonFence;
|
|
7200
7362
|
exports.parseExtractedDecision = parseExtractedDecision;
|
|
7201
7363
|
exports.extractDecisionLlm = extractDecisionLlm2;
|
|
7202
7364
|
var llm_js_1 = require_llm();
|
|
7365
|
+
var capture_settings_js_1 = require_capture_settings();
|
|
7203
7366
|
exports.DECISION_EXTRACTION_SYSTEM_PROMPT = `You extract technical decisions from software development conversations.
|
|
7204
7367
|
|
|
7205
7368
|
A decision includes ANY of the following (not only formal deliberation):
|
|
@@ -7225,6 +7388,23 @@ Fields:
|
|
|
7225
7388
|
Output ONLY valid JSON. If no decision: {"decision": null, "rationale": null, "rejected": [], "confidence": 0}.
|
|
7226
7389
|
Never add explanation outside the JSON.
|
|
7227
7390
|
Schema: {"decision": string|null, "rationale": string|null, "rejected": [{"option": string, "reason": string}], "confidence": number}`;
|
|
7391
|
+
function buildCaptureRules(capture) {
|
|
7392
|
+
if (!capture)
|
|
7393
|
+
return "";
|
|
7394
|
+
const sections = [];
|
|
7395
|
+
if (capture.kinds.length > 0) {
|
|
7396
|
+
const kindLines = capture.kinds.map((k) => `- "${k.name}": ${k.description}`).join("\n");
|
|
7397
|
+
sections.push(`Besides decisions, this project also stores these kinds of memory:
|
|
7398
|
+
${kindLines}
|
|
7399
|
+
The rules above about what is NOT a decision apply only to kind "${capture_settings_js_1.DEFAULT_CAPTURE_KIND}". A turn that contains an entry of one of the listed kinds must be returned even though it contains no decision and nothing durable was committed: set "kind" to that kind's name, put the entry in "entry" as one short factual sentence (what happened, when, with what result), set "subject" to a short stable label for the thing it is about (max 6 words, the same words each time so a newer entry about the same subject replaces the older one), set "confidence" to how sure you are that the turn contains such an entry, and leave "decision" null. When the turn contains a decision, return it as usual with "kind": "${capture_settings_js_1.DEFAULT_CAPTURE_KIND}", "entry": null and "subject": null. Never invent a kind that is not listed.
|
|
7400
|
+
Schema: {"decision": string|null, "rationale": string|null, "rejected": [{"option": string, "reason": string}], "confidence": number, "kind": string, "entry": string|null, "subject": string|null}`);
|
|
7401
|
+
}
|
|
7402
|
+
if (capture.instructions) {
|
|
7403
|
+
sections.push(`Project instructions for what to store (they refine the rules above; the output must stay valid JSON in the schema given):
|
|
7404
|
+
${capture.instructions}`);
|
|
7405
|
+
}
|
|
7406
|
+
return sections.join("\n\n");
|
|
7407
|
+
}
|
|
7228
7408
|
exports.SELF_HOSTED_USER_RULE_OVERRIDE = `User-stated rule wins:
|
|
7229
7409
|
- A durable rule is a standing instruction for future work in this repo or team (e.g. "from now on", "always", "never", "we no longer"). An instruction scoped to the current task, file, or step (e.g. "don't use that file", "stop logging here", "don't add tests for this one") is not durable; apply the rules above to it.
|
|
7230
7410
|
- If the user states a durable rule in this turn, extract that rule even when the assistant refuses it, cites an older rule, or says it will not follow the new one. In that case do not return null, and do not extract the older rule the assistant restates.`;
|
|
@@ -7239,24 +7419,28 @@ Schema: {"decision": string|null, "rationale": string|null, "rejected": [{"optio
|
|
|
7239
7419
|
function stripMarkdownJsonFence(raw) {
|
|
7240
7420
|
return raw.trim().replace(/^```[a-z]*\s*/i, "").replace(/\s*```\s*$/, "").trim();
|
|
7241
7421
|
}
|
|
7242
|
-
function parseExtractedDecision(rawText) {
|
|
7422
|
+
function parseExtractedDecision(rawText, allowedKinds = [capture_settings_js_1.DEFAULT_CAPTURE_KIND]) {
|
|
7243
7423
|
const raw = JSON.parse(stripMarkdownJsonFence(rawText));
|
|
7244
7424
|
const rejected = Array.isArray(raw.rejected) ? raw.rejected.filter((x) => x !== null && typeof x === "object" && typeof x.option === "string" && typeof x.reason === "string") : [];
|
|
7245
7425
|
const confidence = typeof raw.confidence === "number" && Number.isFinite(raw.confidence) ? raw.confidence : 0;
|
|
7426
|
+
const requestedKind = typeof raw.kind === "string" ? (0, capture_settings_js_1.normalizeKindName)(raw.kind) : capture_settings_js_1.DEFAULT_CAPTURE_KIND;
|
|
7427
|
+
const kind = allowedKinds.includes(requestedKind) ? requestedKind : capture_settings_js_1.DEFAULT_CAPTURE_KIND;
|
|
7428
|
+
const subject = kind !== capture_settings_js_1.DEFAULT_CAPTURE_KIND && typeof raw.subject === "string" && raw.subject.trim() ? raw.subject.trim().slice(0, 120) : null;
|
|
7429
|
+
const entry = kind !== capture_settings_js_1.DEFAULT_CAPTURE_KIND && typeof raw.entry === "string" && raw.entry.trim() ? raw.entry.trim() : null;
|
|
7246
7430
|
return {
|
|
7247
|
-
decision: typeof raw.decision === "string" ? raw.decision : null,
|
|
7431
|
+
decision: entry ?? (typeof raw.decision === "string" ? raw.decision : null),
|
|
7248
7432
|
rationale: typeof raw.rationale === "string" ? raw.rationale : null,
|
|
7249
7433
|
rejected,
|
|
7250
|
-
confidence
|
|
7434
|
+
confidence,
|
|
7435
|
+
kind,
|
|
7436
|
+
subject
|
|
7251
7437
|
};
|
|
7252
7438
|
}
|
|
7253
7439
|
async function extractDecisionLlm2(userMessage, claudeReply, cfg) {
|
|
7254
7440
|
const userPrompt = `Session turn:
|
|
7255
7441
|
User: ${userMessage}
|
|
7256
7442
|
Claude: ${claudeReply}`;
|
|
7257
|
-
const systemPrompt = cfg.extraSystemRules
|
|
7258
|
-
|
|
7259
|
-
${cfg.extraSystemRules}` : exports.DECISION_EXTRACTION_SYSTEM_PROMPT;
|
|
7443
|
+
const systemPrompt = [exports.DECISION_EXTRACTION_SYSTEM_PROMPT, cfg.extraSystemRules, buildCaptureRules(cfg.capture)].filter((section) => Boolean(section)).join("\n\n");
|
|
7260
7444
|
let rawText;
|
|
7261
7445
|
if (cfg.provider === "openai") {
|
|
7262
7446
|
const baseUrl = cfg.openaiBaseUrl ?? (0, llm_js_1.resolveOpenAiBaseUrl)();
|
|
@@ -7288,7 +7472,88 @@ ${cfg.extraSystemRules}` : exports.DECISION_EXTRACTION_SYSTEM_PROMPT;
|
|
|
7288
7472
|
temperature: 0
|
|
7289
7473
|
});
|
|
7290
7474
|
}
|
|
7291
|
-
return parseExtractedDecision(rawText);
|
|
7475
|
+
return parseExtractedDecision(rawText, (0, capture_settings_js_1.allowedKindNames)(cfg.capture));
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
7478
|
+
});
|
|
7479
|
+
|
|
7480
|
+
// ../shared/dist/capture-gate.js
|
|
7481
|
+
var require_capture_gate = __commonJS({
|
|
7482
|
+
"../shared/dist/capture-gate.js"(exports) {
|
|
7483
|
+
"use strict";
|
|
7484
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7485
|
+
exports.BROAD_MODE_MIN_USER_CHARS = exports.DECISION_KEYWORDS = void 0;
|
|
7486
|
+
exports.captureGate = captureGate2;
|
|
7487
|
+
exports.DECISION_KEYWORDS = [
|
|
7488
|
+
"decided",
|
|
7489
|
+
"decision",
|
|
7490
|
+
"going with",
|
|
7491
|
+
"we'll use",
|
|
7492
|
+
"let's use",
|
|
7493
|
+
"instead of",
|
|
7494
|
+
"switched to",
|
|
7495
|
+
"switching to",
|
|
7496
|
+
"rejected",
|
|
7497
|
+
"won't use",
|
|
7498
|
+
"we tried",
|
|
7499
|
+
"too slow",
|
|
7500
|
+
"doesn't work",
|
|
7501
|
+
"went with",
|
|
7502
|
+
"chosen",
|
|
7503
|
+
"settled on",
|
|
7504
|
+
"ruling out",
|
|
7505
|
+
"dropping",
|
|
7506
|
+
"replacing",
|
|
7507
|
+
// Casual commitments / conventions (often no spelled-out alternatives)
|
|
7508
|
+
"standardize",
|
|
7509
|
+
"standardise",
|
|
7510
|
+
"standardizing",
|
|
7511
|
+
"moving to ",
|
|
7512
|
+
"migrate to",
|
|
7513
|
+
"migrate from",
|
|
7514
|
+
"defaults to",
|
|
7515
|
+
"default to",
|
|
7516
|
+
"stick with",
|
|
7517
|
+
"locking in",
|
|
7518
|
+
"lock in"
|
|
7519
|
+
];
|
|
7520
|
+
var AFFIRMATION_PATTERNS = [
|
|
7521
|
+
/\b(yes|yep|yeah|yup|sure)\b/i,
|
|
7522
|
+
/\b(agreed|agree|approved?)\b/i,
|
|
7523
|
+
/\blet['’]s\s+(do|go|use|try|ship|stick|adopt|switch|move|migrate)\b/i,
|
|
7524
|
+
/\b(go|going)\s+with\b/i,
|
|
7525
|
+
/\bsounds\s+good\b/i,
|
|
7526
|
+
/\bdo\s+it\b/i,
|
|
7527
|
+
/\bship\s+it\b/i
|
|
7528
|
+
];
|
|
7529
|
+
var IMPLICIT_ACTION_PATTERNS = [
|
|
7530
|
+
/import .+ from ['"][^'"]+['"]/,
|
|
7531
|
+
/require\(['"][^'"]+['"]\)/,
|
|
7532
|
+
/npm install|pnpm add|yarn add/
|
|
7533
|
+
];
|
|
7534
|
+
var IMPLICIT_USER_COMMITMENT_PATTERNS = [
|
|
7535
|
+
/\blet['’]s\s+(standardize|standardise|use|go with|stick with|adopt)\b/i,
|
|
7536
|
+
/\b(from now on|going forward)\b/i,
|
|
7537
|
+
/\b(always|never)\s+(use|write|add|call|prefer|put|name|run|import|suggest|commit|push|merge|install|mock)\b/i,
|
|
7538
|
+
/\b(don['’]t|do not|stop|no longer)\s+(use|using|write|writing|add|adding|call|calling|suggest|suggesting|import|importing)\b/i
|
|
7539
|
+
];
|
|
7540
|
+
exports.BROAD_MODE_MIN_USER_CHARS = 24;
|
|
7541
|
+
function captureGate2(turn, settings) {
|
|
7542
|
+
const userText = turn.user_message.toLowerCase();
|
|
7543
|
+
const assistantText = turn.claude_reply.toLowerCase();
|
|
7544
|
+
const userAffirmed = AFFIRMATION_PATTERNS.some((p) => p.test(turn.user_message));
|
|
7545
|
+
const keywordInUser = exports.DECISION_KEYWORDS.some((kw) => userText.includes(kw));
|
|
7546
|
+
const keywordInAssistant = !keywordInUser && exports.DECISION_KEYWORDS.some((kw) => assistantText.includes(kw));
|
|
7547
|
+
if (keywordInUser || keywordInAssistant && userAffirmed) {
|
|
7548
|
+
return { reachesModel: true, reason: "keyword" };
|
|
7549
|
+
}
|
|
7550
|
+
const implicitHit = IMPLICIT_ACTION_PATTERNS.some((p) => p.test(`${turn.user_message} ${turn.claude_reply}`)) || IMPLICIT_USER_COMMITMENT_PATTERNS.some((p) => p.test(turn.user_message));
|
|
7551
|
+
if (implicitHit)
|
|
7552
|
+
return { reachesModel: true, reason: "implicit" };
|
|
7553
|
+
if (settings?.mode === "broad" && turn.user_message.trim().length >= exports.BROAD_MODE_MIN_USER_CHARS) {
|
|
7554
|
+
return { reachesModel: true, reason: "broad" };
|
|
7555
|
+
}
|
|
7556
|
+
return { reachesModel: false, reason: "none" };
|
|
7292
7557
|
}
|
|
7293
7558
|
}
|
|
7294
7559
|
});
|
|
@@ -7336,6 +7601,8 @@ var require_dist = __commonJS({
|
|
|
7336
7601
|
__exportStar(require_veto_match(), exports);
|
|
7337
7602
|
__exportStar(require_trust(), exports);
|
|
7338
7603
|
__exportStar(require_extract_decision(), exports);
|
|
7604
|
+
__exportStar(require_capture_settings(), exports);
|
|
7605
|
+
__exportStar(require_capture_gate(), exports);
|
|
7339
7606
|
__exportStar(require_db(), exports);
|
|
7340
7607
|
}
|
|
7341
7608
|
});
|
|
@@ -22798,6 +23065,9 @@ var init_config = __esm({
|
|
|
22798
23065
|
perceptionApiUrl: (0, import_shared.normalizeLoopbackUrl)(process.env.PERCEPTION_API_URL ?? "http://127.0.0.1:3001"),
|
|
22799
23066
|
perceptionApiKey: process.env.PERCEPTION_API_KEY ?? "",
|
|
22800
23067
|
// ── Classifier thresholds (optional — tune after instrumenting) ─
|
|
23068
|
+
// DECISION_CONFIDENCE_MIN is this machine's stock value; a project can
|
|
23069
|
+
// override it for everyone with `robrain capture set --min-confidence`,
|
|
23070
|
+
// which wins when set (same for Perception's own re-extraction).
|
|
22801
23071
|
decisionConfidenceMin: Number(process.env.DECISION_CONFIDENCE_MIN ?? 0.6),
|
|
22802
23072
|
decisionConfidenceHigh: Number(process.env.DECISION_CONFIDENCE_HIGH ?? 0.9),
|
|
22803
23073
|
topicShiftThreshold: Number(process.env.TOPIC_SHIFT_THRESHOLD ?? 0.35),
|
|
@@ -22895,23 +23165,65 @@ var init_embeddings = __esm({
|
|
|
22895
23165
|
}
|
|
22896
23166
|
});
|
|
22897
23167
|
|
|
23168
|
+
// src/capture-settings.ts
|
|
23169
|
+
async function fetchCaptureSettings(projectId) {
|
|
23170
|
+
if (!config2.perceptionApiUrl) return { ...import_shared4.DEFAULT_CAPTURE_SETTINGS };
|
|
23171
|
+
try {
|
|
23172
|
+
const res = await fetch(
|
|
23173
|
+
`${config2.perceptionApiUrl}/projects/${encodeURIComponent(projectId)}/capture`,
|
|
23174
|
+
{
|
|
23175
|
+
headers: { Authorization: `Bearer ${config2.perceptionApiKey}` },
|
|
23176
|
+
signal: AbortSignal.timeout(config2.fetchTimeoutMs)
|
|
23177
|
+
}
|
|
23178
|
+
);
|
|
23179
|
+
if (!res.ok) {
|
|
23180
|
+
if (res.status !== 404) {
|
|
23181
|
+
console.error(`[Sensing] capture settings fetch failed for ${projectId}: HTTP ${res.status}`);
|
|
23182
|
+
}
|
|
23183
|
+
return { ...import_shared4.DEFAULT_CAPTURE_SETTINGS };
|
|
23184
|
+
}
|
|
23185
|
+
return (0, import_shared4.coerceCaptureSettings)(await res.json());
|
|
23186
|
+
} catch (err) {
|
|
23187
|
+
console.error("[Sensing] capture settings fetch error:", err);
|
|
23188
|
+
return { ...import_shared4.DEFAULT_CAPTURE_SETTINGS };
|
|
23189
|
+
}
|
|
23190
|
+
}
|
|
23191
|
+
async function getCaptureSettings(projectId) {
|
|
23192
|
+
const hit = cache.get(projectId);
|
|
23193
|
+
if (hit && Date.now() - hit.fetchedAt < CACHE_TTL_MS) return hit.settings;
|
|
23194
|
+
const pending = inFlight.get(projectId);
|
|
23195
|
+
if (pending) return pending;
|
|
23196
|
+
const request = fetchCaptureSettings(projectId).then((settings) => {
|
|
23197
|
+
cache.set(projectId, { settings, fetchedAt: Date.now() });
|
|
23198
|
+
return settings;
|
|
23199
|
+
}).finally(() => inFlight.delete(projectId));
|
|
23200
|
+
inFlight.set(projectId, request);
|
|
23201
|
+
return request;
|
|
23202
|
+
}
|
|
23203
|
+
var import_shared4, CACHE_TTL_MS, cache, inFlight;
|
|
23204
|
+
var init_capture_settings = __esm({
|
|
23205
|
+
"src/capture-settings.ts"() {
|
|
23206
|
+
"use strict";
|
|
23207
|
+
import_shared4 = __toESM(require_dist(), 1);
|
|
23208
|
+
init_config();
|
|
23209
|
+
CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
23210
|
+
cache = /* @__PURE__ */ new Map();
|
|
23211
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
23212
|
+
}
|
|
23213
|
+
});
|
|
23214
|
+
|
|
22898
23215
|
// src/classifiers/index.ts
|
|
22899
23216
|
function getLastClassifierFailure() {
|
|
22900
23217
|
return lastClassifierFailure;
|
|
22901
23218
|
}
|
|
22902
23219
|
async function classifyDecision(turn, projectId) {
|
|
22903
|
-
const
|
|
22904
|
-
const
|
|
22905
|
-
|
|
22906
|
-
const keywordInUser = DECISION_KEYWORDS.some((kw) => userText.includes(kw));
|
|
22907
|
-
const keywordInAssistant = !keywordInUser && DECISION_KEYWORDS.some((kw) => assistantText.includes(kw));
|
|
22908
|
-
const keywordHit = keywordInUser || keywordInAssistant && userAffirmed;
|
|
22909
|
-
const implicitHit = !keywordHit && (IMPLICIT_ACTION_PATTERNS.some((p) => p.test(`${turn.user_message} ${turn.claude_reply}`)) || IMPLICIT_USER_COMMITMENT_PATTERNS.some((p) => p.test(turn.user_message)));
|
|
22910
|
-
if (!keywordHit && !implicitHit) {
|
|
23220
|
+
const capture = await getCaptureSettings(projectId);
|
|
23221
|
+
const gate = (0, import_shared5.captureGate)(turn, capture);
|
|
23222
|
+
if (!gate.reachesModel) {
|
|
22911
23223
|
return null;
|
|
22912
23224
|
}
|
|
22913
|
-
const extracted = await extractDecision(turn);
|
|
22914
|
-
if (!extracted.decision || extracted.confidence < config2.decisionConfidenceMin) {
|
|
23225
|
+
const extracted = await extractDecision(turn, capture);
|
|
23226
|
+
if (!extracted.decision || extracted.confidence < (0, import_shared5.effectiveConfidenceMin)(capture, config2.decisionConfidenceMin)) {
|
|
22915
23227
|
return null;
|
|
22916
23228
|
}
|
|
22917
23229
|
const scope = inferScope(turn, extracted);
|
|
@@ -22924,22 +23236,23 @@ async function classifyDecision(turn, projectId) {
|
|
|
22924
23236
|
extracted
|
|
22925
23237
|
};
|
|
22926
23238
|
}
|
|
22927
|
-
async function extractDecision(turn) {
|
|
23239
|
+
async function extractDecision(turn, capture) {
|
|
22928
23240
|
const empty = () => ({ decision: null, rationale: null, rejected: [], confidence: 0 });
|
|
22929
23241
|
try {
|
|
22930
|
-
const extracted = await (0,
|
|
23242
|
+
const extracted = await (0, import_shared5.extractDecisionLlm)(turn.user_message, turn.claude_reply, {
|
|
22931
23243
|
provider: config2.llmProvider,
|
|
22932
23244
|
anthropicApiKey: config2.anthropicApiKey,
|
|
22933
23245
|
anthropicModel: config2.anthropicModel,
|
|
22934
23246
|
openaiApiKey: config2.openaiApiKey,
|
|
22935
23247
|
openaiModel: config2.openaiLlmModel,
|
|
22936
23248
|
openaiBaseUrl: config2.openaiBaseUrl,
|
|
22937
|
-
extraSystemRules:
|
|
23249
|
+
extraSystemRules: import_shared5.SELF_HOSTED_USER_RULE_OVERRIDE,
|
|
23250
|
+
capture
|
|
22938
23251
|
});
|
|
22939
23252
|
lastClassifierFailure = null;
|
|
22940
23253
|
return extracted;
|
|
22941
23254
|
} catch (err) {
|
|
22942
|
-
if (err instanceof
|
|
23255
|
+
if (err instanceof import_shared5.LlmKeyMissingError) {
|
|
22943
23256
|
console.error(`[Sensing] ${err.message} \u2014 skipping decision extraction`);
|
|
22944
23257
|
return empty();
|
|
22945
23258
|
}
|
|
@@ -23014,67 +23327,15 @@ function clearSessionEmbeddings(sessionId) {
|
|
|
23014
23327
|
embeddingWindows.delete(sessionId);
|
|
23015
23328
|
recentFilesPerSession.delete(sessionId);
|
|
23016
23329
|
}
|
|
23017
|
-
var
|
|
23330
|
+
var import_shared5, lastClassifierFailure, embeddingWindows, recentFilesPerSession;
|
|
23018
23331
|
var init_classifiers = __esm({
|
|
23019
23332
|
"src/classifiers/index.ts"() {
|
|
23020
23333
|
"use strict";
|
|
23021
|
-
|
|
23334
|
+
import_shared5 = __toESM(require_dist(), 1);
|
|
23022
23335
|
init_config();
|
|
23023
23336
|
init_embeddings();
|
|
23337
|
+
init_capture_settings();
|
|
23024
23338
|
lastClassifierFailure = null;
|
|
23025
|
-
DECISION_KEYWORDS = [
|
|
23026
|
-
"decided",
|
|
23027
|
-
"decision",
|
|
23028
|
-
"going with",
|
|
23029
|
-
"we'll use",
|
|
23030
|
-
"let's use",
|
|
23031
|
-
"instead of",
|
|
23032
|
-
"switched to",
|
|
23033
|
-
"switching to",
|
|
23034
|
-
"rejected",
|
|
23035
|
-
"won't use",
|
|
23036
|
-
"we tried",
|
|
23037
|
-
"too slow",
|
|
23038
|
-
"doesn't work",
|
|
23039
|
-
"went with",
|
|
23040
|
-
"chosen",
|
|
23041
|
-
"settled on",
|
|
23042
|
-
"ruling out",
|
|
23043
|
-
"dropping",
|
|
23044
|
-
"replacing",
|
|
23045
|
-
// Casual commitments / conventions (often no spelled-out alternatives)
|
|
23046
|
-
"standardize",
|
|
23047
|
-
"standardise",
|
|
23048
|
-
"standardizing",
|
|
23049
|
-
"moving to ",
|
|
23050
|
-
"migrate to",
|
|
23051
|
-
"migrate from",
|
|
23052
|
-
"defaults to",
|
|
23053
|
-
"default to",
|
|
23054
|
-
"stick with",
|
|
23055
|
-
"locking in",
|
|
23056
|
-
"lock in"
|
|
23057
|
-
];
|
|
23058
|
-
AFFIRMATION_PATTERNS = [
|
|
23059
|
-
/\b(yes|yep|yeah|yup|sure)\b/i,
|
|
23060
|
-
/\b(agreed|agree|approved?)\b/i,
|
|
23061
|
-
/\blet['’]s\s+(do|go|use|try|ship|stick|adopt|switch|move|migrate)\b/i,
|
|
23062
|
-
/\b(go|going)\s+with\b/i,
|
|
23063
|
-
/\bsounds\s+good\b/i,
|
|
23064
|
-
/\bdo\s+it\b/i,
|
|
23065
|
-
/\bship\s+it\b/i
|
|
23066
|
-
];
|
|
23067
|
-
IMPLICIT_ACTION_PATTERNS = [
|
|
23068
|
-
/import .+ from ['"][^'"]+['"]/,
|
|
23069
|
-
/require\(['"][^'"]+['"]\)/,
|
|
23070
|
-
/npm install|pnpm add|yarn add/
|
|
23071
|
-
];
|
|
23072
|
-
IMPLICIT_USER_COMMITMENT_PATTERNS = [
|
|
23073
|
-
/\blet['’]s\s+(standardize|standardise|use|go with|stick with|adopt)\b/i,
|
|
23074
|
-
/\b(from now on|going forward)\b/i,
|
|
23075
|
-
/\b(always|never)\s+(use|write|add|call|prefer|put|name|run|import|suggest|commit|push|merge|install|mock)\b/i,
|
|
23076
|
-
/\b(don['’]t|do not|stop|no longer)\s+(use|using|write|writing|add|adding|call|calling|suggest|suggesting|import|importing)\b/i
|
|
23077
|
-
];
|
|
23078
23339
|
embeddingWindows = /* @__PURE__ */ new Map();
|
|
23079
23340
|
recentFilesPerSession = /* @__PURE__ */ new Map();
|
|
23080
23341
|
}
|
|
@@ -23364,7 +23625,7 @@ function resolveSessionId(raw) {
|
|
|
23364
23625
|
function buildServer() {
|
|
23365
23626
|
const server = new McpServer({
|
|
23366
23627
|
name: "sensing-mcp",
|
|
23367
|
-
version: "2.
|
|
23628
|
+
version: "2.5.0"
|
|
23368
23629
|
});
|
|
23369
23630
|
server.tool(
|
|
23370
23631
|
"sensing_start_session",
|
|
@@ -23384,6 +23645,7 @@ function buildServer() {
|
|
|
23384
23645
|
const sessionId = resolveSessionId(session_id);
|
|
23385
23646
|
sessionRegistry.register(sessionId, project_id);
|
|
23386
23647
|
const summaryResult = await fetchAlwaysOnSummary(project_id);
|
|
23648
|
+
if (!isThinMode()) void getCaptureSettings(project_id);
|
|
23387
23649
|
console.error(`[Sensing] Session started: ${sessionId} (${project_id})`);
|
|
23388
23650
|
return {
|
|
23389
23651
|
content: [{
|
|
@@ -23431,9 +23693,9 @@ function buildServer() {
|
|
|
23431
23693
|
console.error(`[Sensing] Unknown session adopted: ${session_id} (${session.project_id})`);
|
|
23432
23694
|
}
|
|
23433
23695
|
}
|
|
23434
|
-
const redactedUser = (0,
|
|
23435
|
-
const redactedReply = (0,
|
|
23436
|
-
const secretsRedacted = (0,
|
|
23696
|
+
const redactedUser = (0, import_shared6.redactSecrets)(user_message);
|
|
23697
|
+
const redactedReply = (0, import_shared6.redactSecrets)(claude_reply);
|
|
23698
|
+
const secretsRedacted = (0, import_shared6.totalRedactions)(redactedUser.redactions) + (0, import_shared6.totalRedactions)(redactedReply.redactions);
|
|
23437
23699
|
if (secretsRedacted > 0) {
|
|
23438
23700
|
console.error(`[Sensing] Redacted ${secretsRedacted} secret(s) from turn ${sequence} before buffering`);
|
|
23439
23701
|
}
|
|
@@ -23650,17 +23912,18 @@ async function fetchAlwaysOnSummary(projectId) {
|
|
|
23650
23912
|
return { text: `Project: ${projectId}` };
|
|
23651
23913
|
}
|
|
23652
23914
|
}
|
|
23653
|
-
var
|
|
23915
|
+
var import_shared6, sessionRegistry, lastDecisionShipFailure, pendingConflictNotices;
|
|
23654
23916
|
var init_server3 = __esm({
|
|
23655
23917
|
"src/server.ts"() {
|
|
23656
23918
|
"use strict";
|
|
23657
23919
|
init_mcp();
|
|
23658
23920
|
init_zod();
|
|
23659
|
-
|
|
23921
|
+
import_shared6 = __toESM(require_dist(), 1);
|
|
23660
23922
|
init_buffer();
|
|
23661
23923
|
init_classifiers();
|
|
23662
23924
|
init_router();
|
|
23663
23925
|
init_config();
|
|
23926
|
+
init_capture_settings();
|
|
23664
23927
|
init_pending_notices();
|
|
23665
23928
|
init_session_registry();
|
|
23666
23929
|
sessionRegistry = new SessionRegistry(config2.sessionRegistryPath);
|
|
@@ -23763,8 +24026,8 @@ var StdioServerTransport = class {
|
|
|
23763
24026
|
};
|
|
23764
24027
|
|
|
23765
24028
|
// src/index.ts
|
|
23766
|
-
var
|
|
23767
|
-
(0,
|
|
24029
|
+
var import_shared7 = __toESM(require_dist(), 1);
|
|
24030
|
+
(0, import_shared7.loadEnv)();
|
|
23768
24031
|
process.on("unhandledRejection", (reason) => {
|
|
23769
24032
|
console.error("[Sensing] Unhandled rejection (process kept alive):", reason);
|
|
23770
24033
|
});
|