robrain 2.3.9 → 2.4.2
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/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +42 -27
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export-memory.d.ts.map +1 -1
- package/dist/commands/export-memory.js +7 -4
- package/dist/commands/export-memory.js.map +1 -1
- package/dist/commands/init-project.js +2 -2
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/install.d.ts +11 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +45 -60
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/install.test.d.ts +2 -0
- package/dist/commands/install.test.d.ts.map +1 -0
- package/dist/commands/install.test.js +107 -0
- package/dist/commands/install.test.js.map +1 -0
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +3 -0
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/mcp.test.js +8 -0
- package/dist/commands/mcp.test.js.map +1 -1
- package/dist/commands/outcomes.js +4 -4
- package/dist/commands/outcomes.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +53 -11
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +7 -5
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/up.d.ts +3 -2
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +9 -7
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/up.test.js +14 -2
- package/dist/commands/up.test.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/editor.codex.test.js +35 -0
- package/dist/lib/editor.codex.test.js.map +1 -1
- package/dist/lib/editor.d.ts +14 -0
- package/dist/lib/editor.d.ts.map +1 -1
- package/dist/lib/editor.js +30 -4
- package/dist/lib/editor.js.map +1 -1
- package/dist/lib/mcp-bundle.test.js +2 -2
- package/package.json +2 -2
- package/vendor/sensing-mcp/dist/index.js +234 -2
- package/vendor/sensing-mcp/package.json +1 -1
|
@@ -6935,6 +6935,222 @@ var require_veto_match = __commonJS({
|
|
|
6935
6935
|
}
|
|
6936
6936
|
});
|
|
6937
6937
|
|
|
6938
|
+
// ../shared/dist/trust.js
|
|
6939
|
+
var require_trust = __commonJS({
|
|
6940
|
+
"../shared/dist/trust.js"(exports) {
|
|
6941
|
+
"use strict";
|
|
6942
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6943
|
+
exports.ANOMALOUS_FIELD_LENGTH = exports.QUARANTINE_THRESHOLD = void 0;
|
|
6944
|
+
exports.scoreMemoryTrust = scoreMemoryTrust;
|
|
6945
|
+
exports.QUARANTINE_THRESHOLD = 0.6;
|
|
6946
|
+
exports.ANOMALOUS_FIELD_LENGTH = 8e3;
|
|
6947
|
+
var DAMPEN_DISCUSSED = 0.25;
|
|
6948
|
+
function regexDetector(id, type, weight, dampenable, regex) {
|
|
6949
|
+
return {
|
|
6950
|
+
id,
|
|
6951
|
+
type,
|
|
6952
|
+
weight,
|
|
6953
|
+
dampenable,
|
|
6954
|
+
find(text) {
|
|
6955
|
+
const out = [];
|
|
6956
|
+
regex.lastIndex = 0;
|
|
6957
|
+
for (const m of text.matchAll(regex)) {
|
|
6958
|
+
out.push({ index: m.index ?? 0, match: m[0] });
|
|
6959
|
+
}
|
|
6960
|
+
return out;
|
|
6961
|
+
}
|
|
6962
|
+
};
|
|
6963
|
+
}
|
|
6964
|
+
var ZERO_WIDTH_RE = /[\u200B\u200C\u200D\u2060\uFEFF]/g;
|
|
6965
|
+
var BIDI_RE = /[\u202A-\u202E\u2066-\u2069]/g;
|
|
6966
|
+
var MARKDOWN_LINK_RE = /\[([^\]\n]+)\]\(\s*([^)\s]+)\s*\)/g;
|
|
6967
|
+
var HOST_IN_TEXT_RE = /(?:https?:\/\/)?([a-z0-9-]+(?:\.[a-z0-9-]+)+)/i;
|
|
6968
|
+
function normalizeHost(host) {
|
|
6969
|
+
return host.toLowerCase().replace(/^www\./, "");
|
|
6970
|
+
}
|
|
6971
|
+
function linkTargetHost(url) {
|
|
6972
|
+
const m = url.match(/^https?:\/\/([^/?#]+)/i);
|
|
6973
|
+
return m?.[1] ? normalizeHost(m[1].replace(/:\d+$/, "")) : null;
|
|
6974
|
+
}
|
|
6975
|
+
var DETECTORS = [
|
|
6976
|
+
// ── instruction_injection — imperatives aimed at the agent ──
|
|
6977
|
+
regexDetector("ii_ignore_instructions", "instruction_injection", 0.45, true, /ignore\s+(?:all|previous|prior)\s+(?:instructions|context)/gi),
|
|
6978
|
+
regexDetector("ii_you_must", "instruction_injection", 0.3, true, /you must (?:always|never)\b/gi),
|
|
6979
|
+
regexDetector("ii_hide_from_user", "instruction_injection", 0.45, true, /do not tell the user/gi),
|
|
6980
|
+
regexDetector("ii_without_asking", "instruction_injection", 0.3, true, /without asking(?: the user)?\b/gi),
|
|
6981
|
+
regexDetector("ii_system_prompt", "instruction_injection", 0.25, true, /system prompt/gi),
|
|
6982
|
+
// Role-prefix smuggling — a stored memory has no business containing
|
|
6983
|
+
// chat-transcript role markers at line starts. HARD: quarantines alone.
|
|
6984
|
+
regexDetector("ii_role_smuggling", "instruction_injection", 0.7, false, /(?:^|\n)\s{0,8}(?:system|assistant|user)\s*:/gi),
|
|
6985
|
+
// <system>/<important> pseudo-tags — prompt-boundary forgery. HARD.
|
|
6986
|
+
regexDetector("ii_pseudo_tag", "instruction_injection", 0.7, false, /<\/?\s*(?:system|important)\s*>/gi),
|
|
6987
|
+
// ── tool_coercion — trying to make the agent run things ─────
|
|
6988
|
+
regexDetector("tc_run_command", "tool_coercion", 0.4, true, /run the following command/gi),
|
|
6989
|
+
regexDetector("tc_execute_this", "tool_coercion", 0.35, true, /execute this\b/gi),
|
|
6990
|
+
// curl … | sh pipeline — a live remote-code-execution payload. HARD when
|
|
6991
|
+
// undiscussed; dampened when it's quoted in a rejection ("rejected:
|
|
6992
|
+
// curl | bash installers").
|
|
6993
|
+
regexDetector("tc_curl_pipe_sh", "tool_coercion", 0.7, true, /\bcurl\b[^\n]{0,200}?\|\s*(?:ba|z|da)?sh\b/gi),
|
|
6994
|
+
regexDetector("tc_rm_rf", "tool_coercion", 0.5, true, /\brm\s+-rf\b/g),
|
|
6995
|
+
regexDetector("tc_bashrc", "tool_coercion", 0.5, true, /add[^\n]{0,40}\.(?:bashrc|zshrc|profile)\b/gi),
|
|
6996
|
+
// ── link_smuggling — URI schemes + text/host mismatch ───────
|
|
6997
|
+
regexDetector("ls_javascript_uri", "link_smuggling", 0.7, true, /\bjavascript:\s*\S/gi),
|
|
6998
|
+
regexDetector("ls_data_uri", "link_smuggling", 0.7, true, /\bdata:[a-z]+\/[a-z0-9.+-]+[;,]/gi),
|
|
6999
|
+
{
|
|
7000
|
+
id: "ls_text_host_mismatch",
|
|
7001
|
+
type: "link_smuggling",
|
|
7002
|
+
weight: 0.5,
|
|
7003
|
+
dampenable: true,
|
|
7004
|
+
find(text) {
|
|
7005
|
+
const out = [];
|
|
7006
|
+
MARKDOWN_LINK_RE.lastIndex = 0;
|
|
7007
|
+
for (const m of text.matchAll(MARKDOWN_LINK_RE)) {
|
|
7008
|
+
const [, linkText, target] = m;
|
|
7009
|
+
if (linkText === void 0 || target === void 0)
|
|
7010
|
+
continue;
|
|
7011
|
+
const targetHost = linkTargetHost(target);
|
|
7012
|
+
if (!targetHost)
|
|
7013
|
+
continue;
|
|
7014
|
+
const textHost = linkText.match(HOST_IN_TEXT_RE)?.[1];
|
|
7015
|
+
if (!textHost)
|
|
7016
|
+
continue;
|
|
7017
|
+
const shown = normalizeHost(textHost);
|
|
7018
|
+
if (targetHost === shown || targetHost.endsWith(`.${shown}`) || shown.endsWith(`.${targetHost}`))
|
|
7019
|
+
continue;
|
|
7020
|
+
out.push({ index: m.index ?? 0, match: m[0] });
|
|
7021
|
+
}
|
|
7022
|
+
return out;
|
|
7023
|
+
}
|
|
7024
|
+
},
|
|
7025
|
+
// ── invisible_unicode — HARD, never dampened ────────────────
|
|
7026
|
+
{
|
|
7027
|
+
id: "iu_zero_width",
|
|
7028
|
+
type: "invisible_unicode",
|
|
7029
|
+
weight: 0.9,
|
|
7030
|
+
dampenable: false,
|
|
7031
|
+
find: (text) => invisibleOccurrences(text, ZERO_WIDTH_RE, "zero-width")
|
|
7032
|
+
},
|
|
7033
|
+
{
|
|
7034
|
+
id: "iu_bidi",
|
|
7035
|
+
type: "invisible_unicode",
|
|
7036
|
+
weight: 0.9,
|
|
7037
|
+
dampenable: false,
|
|
7038
|
+
find: (text) => invisibleOccurrences(text, BIDI_RE, "bidi-override")
|
|
7039
|
+
},
|
|
7040
|
+
// ── encoded_blob — opaque payloads have no place in a decision ──
|
|
7041
|
+
{
|
|
7042
|
+
id: "eb_base64",
|
|
7043
|
+
type: "encoded_blob",
|
|
7044
|
+
weight: 0.7,
|
|
7045
|
+
dampenable: false,
|
|
7046
|
+
find(text) {
|
|
7047
|
+
const out = [];
|
|
7048
|
+
for (const m of text.matchAll(/[A-Za-z0-9+/]{120,}={0,2}/g)) {
|
|
7049
|
+
const run = m[0];
|
|
7050
|
+
if (!/\d/.test(run) || !/[a-z]/.test(run) || !/[A-Z]/.test(run))
|
|
7051
|
+
continue;
|
|
7052
|
+
out.push({ index: m.index ?? 0, match: run, evidence: `base64-like run of ${run.length} chars: ${run.slice(0, 40)}\u2026` });
|
|
7053
|
+
}
|
|
7054
|
+
return out;
|
|
7055
|
+
}
|
|
7056
|
+
},
|
|
7057
|
+
{
|
|
7058
|
+
id: "eb_hex",
|
|
7059
|
+
type: "encoded_blob",
|
|
7060
|
+
weight: 0.7,
|
|
7061
|
+
dampenable: false,
|
|
7062
|
+
find(text) {
|
|
7063
|
+
const out = [];
|
|
7064
|
+
for (const m of text.matchAll(/[0-9a-fA-F]{160,}/g)) {
|
|
7065
|
+
if (!/\d/.test(m[0]))
|
|
7066
|
+
continue;
|
|
7067
|
+
out.push({ index: m.index ?? 0, match: m[0], evidence: `hex run of ${m[0].length} chars: ${m[0].slice(0, 40)}\u2026` });
|
|
7068
|
+
}
|
|
7069
|
+
return out;
|
|
7070
|
+
}
|
|
7071
|
+
}
|
|
7072
|
+
];
|
|
7073
|
+
function invisibleOccurrences(text, re, label) {
|
|
7074
|
+
re.lastIndex = 0;
|
|
7075
|
+
const matches = [...text.matchAll(re)];
|
|
7076
|
+
const first = matches[0];
|
|
7077
|
+
if (!first)
|
|
7078
|
+
return [];
|
|
7079
|
+
const codepoints = [...new Set(matches.map((m) => `U+${(m[0].codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}`))];
|
|
7080
|
+
return [{
|
|
7081
|
+
index: first.index ?? 0,
|
|
7082
|
+
match: first[0],
|
|
7083
|
+
evidence: `${matches.length}\xD7 ${label} char${matches.length === 1 ? "" : "s"} (${codepoints.join(", ")}) near "${excerptAround(text, first.index ?? 0)}"`
|
|
7084
|
+
}];
|
|
7085
|
+
}
|
|
7086
|
+
var DISCUSSION_MARKER_RE = /(?:reject(?:ed|ing)?|rule[sd]?\s+out|sanitiz\w*|redact\w*|filter\w*|detect\w*|scan\w*|block\w*|flag\w*|strip\w*|escap\w*|guard\w*|forbid\w*|ban(?:ned|ning)?|avoid\w*|never\s+(?:use|run|allow)|do(?:n't| not)\s+(?:use|run|allow)|disallow\w*|prevent\w*|against|instead\s+of|pattern[s]?|attack[s]?|inject\w*|poison\w*|phish\w*|malicious|risk[sy]?|vulnerab\w*|supply.chain|e\.g\.|such\s+as|example[s]?|like\s+["'`])/i;
|
|
7087
|
+
var QUOTE_CHARS = /* @__PURE__ */ new Set(['"', "'", "`", "\u2018", "\u2019", "\u201C", "\u201D"]);
|
|
7088
|
+
function inDiscussedContext(text, index) {
|
|
7089
|
+
const before = text.slice(Math.max(0, index - 100), index);
|
|
7090
|
+
if (DISCUSSION_MARKER_RE.test(before))
|
|
7091
|
+
return true;
|
|
7092
|
+
const lineStart = text.lastIndexOf("\n", index) + 1;
|
|
7093
|
+
const linePrefix = text.slice(lineStart, index);
|
|
7094
|
+
let quotes = 0;
|
|
7095
|
+
for (const ch of linePrefix)
|
|
7096
|
+
if (QUOTE_CHARS.has(ch))
|
|
7097
|
+
quotes += 1;
|
|
7098
|
+
return quotes % 2 === 1;
|
|
7099
|
+
}
|
|
7100
|
+
var EVIDENCE_MAX = 80;
|
|
7101
|
+
function escapeInvisible(s) {
|
|
7102
|
+
return s.replace(/[\u200B-\u200D\u2060\uFEFF\u202A-\u202E\u2066-\u2069]/g, (ch) => `U+${(ch.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}`);
|
|
7103
|
+
}
|
|
7104
|
+
function excerpt(s) {
|
|
7105
|
+
const oneLine = escapeInvisible(s).replace(/\s+/g, " ").trim();
|
|
7106
|
+
return oneLine.length <= EVIDENCE_MAX ? oneLine : `${oneLine.slice(0, EVIDENCE_MAX - 1)}\u2026`;
|
|
7107
|
+
}
|
|
7108
|
+
function excerptAround(text, index) {
|
|
7109
|
+
return excerpt(text.slice(Math.max(0, index - 20), index + 20));
|
|
7110
|
+
}
|
|
7111
|
+
var MAX_FLAGS = 20;
|
|
7112
|
+
function scoreMemoryTrust(texts) {
|
|
7113
|
+
const hits = /* @__PURE__ */ new Map();
|
|
7114
|
+
for (const text of texts) {
|
|
7115
|
+
if (!text)
|
|
7116
|
+
continue;
|
|
7117
|
+
for (const det of DETECTORS) {
|
|
7118
|
+
for (const occ of det.find(text)) {
|
|
7119
|
+
const dampened = det.dampenable && inDiscussedContext(text, occ.index);
|
|
7120
|
+
const weight = dampened ? det.weight * DAMPEN_DISCUSSED : det.weight;
|
|
7121
|
+
const prev = hits.get(det.id);
|
|
7122
|
+
if (prev && prev.weight >= weight)
|
|
7123
|
+
continue;
|
|
7124
|
+
hits.set(det.id, {
|
|
7125
|
+
type: det.type,
|
|
7126
|
+
weight,
|
|
7127
|
+
evidence: occ.evidence ?? excerpt(occ.match)
|
|
7128
|
+
});
|
|
7129
|
+
}
|
|
7130
|
+
}
|
|
7131
|
+
if (text.length > exports.ANOMALOUS_FIELD_LENGTH) {
|
|
7132
|
+
const prev = hits.get("anomalous_length");
|
|
7133
|
+
if (!prev) {
|
|
7134
|
+
hits.set("anomalous_length", {
|
|
7135
|
+
type: "anomalous_length",
|
|
7136
|
+
weight: 0.3,
|
|
7137
|
+
evidence: `field length ${text.length} > ${exports.ANOMALOUS_FIELD_LENGTH}`
|
|
7138
|
+
});
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
let survival = 1;
|
|
7143
|
+
const flags = [];
|
|
7144
|
+
for (const hit of [...hits.values()].sort((a, b) => b.weight - a.weight)) {
|
|
7145
|
+
survival *= 1 - hit.weight;
|
|
7146
|
+
if (flags.length < MAX_FLAGS)
|
|
7147
|
+
flags.push({ type: hit.type, evidence: hit.evidence });
|
|
7148
|
+
}
|
|
7149
|
+
return { score: Math.round((1 - survival) * 100) / 100, flags };
|
|
7150
|
+
}
|
|
7151
|
+
}
|
|
7152
|
+
});
|
|
7153
|
+
|
|
6938
7154
|
// ../shared/dist/extract-decision.js
|
|
6939
7155
|
var require_extract_decision = __commonJS({
|
|
6940
7156
|
"../shared/dist/extract-decision.js"(exports) {
|
|
@@ -7070,6 +7286,7 @@ var require_dist = __commonJS({
|
|
|
7070
7286
|
__exportStar(require_embeddings(), exports);
|
|
7071
7287
|
__exportStar(require_redact(), exports);
|
|
7072
7288
|
__exportStar(require_veto_match(), exports);
|
|
7289
|
+
__exportStar(require_trust(), exports);
|
|
7073
7290
|
__exportStar(require_extract_decision(), exports);
|
|
7074
7291
|
__exportStar(require_db(), exports);
|
|
7075
7292
|
}
|
|
@@ -22491,6 +22708,9 @@ var init_mcp = __esm({
|
|
|
22491
22708
|
// src/config.ts
|
|
22492
22709
|
import { homedir } from "node:os";
|
|
22493
22710
|
import { join } from "node:path";
|
|
22711
|
+
function isThinMode(env = process.env) {
|
|
22712
|
+
return env.ROBRAIN_MODE?.trim().toLowerCase() === "cloud";
|
|
22713
|
+
}
|
|
22494
22714
|
var import_shared, config2;
|
|
22495
22715
|
var init_config = __esm({
|
|
22496
22716
|
"src/config.ts"() {
|
|
@@ -22699,7 +22919,7 @@ function inferScope(turn, extracted) {
|
|
|
22699
22919
|
return "team";
|
|
22700
22920
|
}
|
|
22701
22921
|
async function classifyTopicShift(turn, signal) {
|
|
22702
|
-
if (config2.topicShiftDisableEmbedding) return null;
|
|
22922
|
+
if (config2.topicShiftDisableEmbedding || isThinMode()) return null;
|
|
22703
22923
|
const sessionId = turn.session_id;
|
|
22704
22924
|
const window = embeddingWindows.get(sessionId) ?? [];
|
|
22705
22925
|
const currentEmbedding = await embed(turn.user_message, signal);
|
|
@@ -23062,7 +23282,7 @@ function resolveSessionId(raw) {
|
|
|
23062
23282
|
function buildServer() {
|
|
23063
23283
|
const server = new McpServer({
|
|
23064
23284
|
name: "sensing-mcp",
|
|
23065
|
-
version: "2.
|
|
23285
|
+
version: "2.4.2"
|
|
23066
23286
|
});
|
|
23067
23287
|
server.tool(
|
|
23068
23288
|
"sensing_start_session",
|
|
@@ -23159,6 +23379,16 @@ function buildServer() {
|
|
|
23159
23379
|
const projectId = session.project_id;
|
|
23160
23380
|
setImmediate(async () => {
|
|
23161
23381
|
try {
|
|
23382
|
+
if (isThinMode()) {
|
|
23383
|
+
const errors = await routeFlushTurns([turn], projectId);
|
|
23384
|
+
if (errors.length === 0) {
|
|
23385
|
+
lastDecisionShipFailure = null;
|
|
23386
|
+
streamBuffer.markClassified(session_id, sequence);
|
|
23387
|
+
} else {
|
|
23388
|
+
lastDecisionShipFailure = `${session_id} seq ${sequence}: ${errors.join("; ")}`;
|
|
23389
|
+
}
|
|
23390
|
+
return;
|
|
23391
|
+
}
|
|
23162
23392
|
const decisionSignal = await classifyDecision(turn, projectId);
|
|
23163
23393
|
if (decisionSignal) {
|
|
23164
23394
|
const outcome = await routeDecisionSignal(decisionSignal, projectId);
|
|
@@ -23259,6 +23489,8 @@ function buildServer() {
|
|
|
23259
23489
|
content: [{
|
|
23260
23490
|
type: "text",
|
|
23261
23491
|
text: JSON.stringify({
|
|
23492
|
+
// Only surfaced in thin mode so full-mode status output stays byte-identical.
|
|
23493
|
+
...isThinMode() ? { mode: "cloud-thin" } : {},
|
|
23262
23494
|
session_found: !!session,
|
|
23263
23495
|
project_id: session?.project_id,
|
|
23264
23496
|
started_at: session?.started_at,
|