switchroom 0.14.75 → 0.14.77
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/agent-scheduler/index.js +1 -0
- package/dist/auth-broker/index.js +23 -1
- package/dist/cli/notion-write-pretool.mjs +1 -0
- package/dist/cli/switchroom.js +98 -20
- package/dist/host-control/main.js +1 -0
- package/dist/vault/approvals/kernel-server.js +1 -0
- package/dist/vault/broker/server.js +1 -0
- package/package.json +1 -1
- package/profiles/default/workspace/CLAUDE.md.hbs +11 -13
- package/telegram-plugin/dist/gateway/gateway.js +6 -5
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +10 -1
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +7 -1
|
@@ -11211,6 +11211,7 @@ var profileFields = {
|
|
|
11211
11211
|
memory: exports_external.object({
|
|
11212
11212
|
collection: exports_external.string().optional(),
|
|
11213
11213
|
auto_recall: exports_external.boolean().optional(),
|
|
11214
|
+
file: exports_external.boolean().optional(),
|
|
11214
11215
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11215
11216
|
recall: exports_external.object({
|
|
11216
11217
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
@@ -11211,6 +11211,7 @@ var profileFields = {
|
|
|
11211
11211
|
memory: exports_external.object({
|
|
11212
11212
|
collection: exports_external.string().optional(),
|
|
11213
11213
|
auto_recall: exports_external.boolean().optional(),
|
|
11214
|
+
file: exports_external.boolean().optional(),
|
|
11214
11215
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11215
11216
|
recall: exports_external.object({
|
|
11216
11217
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
@@ -13891,8 +13892,29 @@ class AuthBroker {
|
|
|
13891
13892
|
return override;
|
|
13892
13893
|
return auth.active ?? null;
|
|
13893
13894
|
}
|
|
13894
|
-
|
|
13895
|
+
servingAccount(identity2) {
|
|
13895
13896
|
const account = this.callerAccount(identity2);
|
|
13897
|
+
if (identity2.kind !== "consumer")
|
|
13898
|
+
return account;
|
|
13899
|
+
return this.consumerAccountWithFailover(account);
|
|
13900
|
+
}
|
|
13901
|
+
isAccountExhausted(account) {
|
|
13902
|
+
const q = this.quota[account];
|
|
13903
|
+
return q !== undefined && q.exhausted_until > this.now();
|
|
13904
|
+
}
|
|
13905
|
+
consumerAccountWithFailover(pinned) {
|
|
13906
|
+
if (!pinned || !this.isAccountExhausted(pinned))
|
|
13907
|
+
return pinned;
|
|
13908
|
+
for (const cand of this.config.auth?.fallback_order ?? []) {
|
|
13909
|
+
if (cand === pinned || this.isAccountExhausted(cand))
|
|
13910
|
+
continue;
|
|
13911
|
+
if (readAccountCredentials(cand, this.home))
|
|
13912
|
+
return cand;
|
|
13913
|
+
}
|
|
13914
|
+
return pinned;
|
|
13915
|
+
}
|
|
13916
|
+
async opGetCredentials(socket, id, identity2) {
|
|
13917
|
+
const account = this.servingAccount(identity2);
|
|
13896
13918
|
if (!account) {
|
|
13897
13919
|
this.audit({ op: "get-credentials", identity: identity2, ok: false, error: "no-active-account" });
|
|
13898
13920
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", "no active account configured"));
|
|
@@ -11959,6 +11959,7 @@ var profileFields = {
|
|
|
11959
11959
|
memory: exports_external.object({
|
|
11960
11960
|
collection: exports_external.string().optional(),
|
|
11961
11961
|
auto_recall: exports_external.boolean().optional(),
|
|
11962
|
+
file: exports_external.boolean().optional(),
|
|
11962
11963
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11963
11964
|
recall: exports_external.object({
|
|
11964
11965
|
max_memories: exports_external.number().int().min(0).optional(),
|
package/dist/cli/switchroom.js
CHANGED
|
@@ -13775,6 +13775,7 @@ var init_schema = __esm(() => {
|
|
|
13775
13775
|
memory: exports_external.object({
|
|
13776
13776
|
collection: exports_external.string().optional(),
|
|
13777
13777
|
auto_recall: exports_external.boolean().optional(),
|
|
13778
|
+
file: exports_external.boolean().optional(),
|
|
13778
13779
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
13779
13780
|
recall: exports_external.object({
|
|
13780
13781
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
@@ -20875,7 +20876,7 @@ function getHindsightSettingsEntry(agentName, config) {
|
|
|
20875
20876
|
}
|
|
20876
20877
|
const collection = getCollectionForAgent(agentName, config);
|
|
20877
20878
|
const mcpConfig = generateHindsightMcpConfig(collection, memoryConfig);
|
|
20878
|
-
return { key: "hindsight", value: mcpConfig };
|
|
20879
|
+
return { key: "hindsight", value: { ...mcpConfig, alwaysLoad: true } };
|
|
20879
20880
|
}
|
|
20880
20881
|
function getPlaywrightMcpSettingsEntry() {
|
|
20881
20882
|
return {
|
|
@@ -28973,7 +28974,7 @@ function classifyExtractionLogs(logs) {
|
|
|
28973
28974
|
name: "hindsight extraction",
|
|
28974
28975
|
status: "fail",
|
|
28975
28976
|
detail: "fact-extraction LLM calls are failing" + (quotaHint ? " (429 / weekly-limit detected)" : "") + " \u2014 retains are accepted but extract 0 facts, so nothing becomes recallable",
|
|
28976
|
-
fix: "Usually hindsight's `auth.consumers[hindsight].account` is quota-" + "exhausted or its OAuth broke. Repoint it to an account with quota in " + "switchroom.yaml, then `docker restart switchroom-auth-broker` and " + "`docker restart switchroom-hindsight` (single-file config mount needs " + "the broker restart to re-read). Confirm with a `claude
|
|
28977
|
+
fix: "Usually hindsight's `auth.consumers[hindsight].account` is quota-" + "exhausted or its OAuth broke. Repoint it to an account with quota in " + "switchroom.yaml, then `docker restart switchroom-auth-broker` and " + "`docker restart switchroom-hindsight` (single-file config mount needs " + "the broker restart to re-read). Confirm with a headless `claude` run " + "inside the container."
|
|
28977
28978
|
};
|
|
28978
28979
|
}
|
|
28979
28980
|
if (zeroFacts >= 3 && okFacts === 0) {
|
|
@@ -49680,8 +49681,8 @@ var {
|
|
|
49680
49681
|
} = import__.default;
|
|
49681
49682
|
|
|
49682
49683
|
// src/build-info.ts
|
|
49683
|
-
var VERSION = "0.14.
|
|
49684
|
-
var COMMIT_SHA = "
|
|
49684
|
+
var VERSION = "0.14.77";
|
|
49685
|
+
var COMMIT_SHA = "2473dbbf";
|
|
49685
49686
|
|
|
49686
49687
|
// src/cli/agent.ts
|
|
49687
49688
|
init_source();
|
|
@@ -50533,16 +50534,16 @@ Every turn that answers a user message ends with a user-visible
|
|
|
50533
50534
|
sees; your terminal output never reaches them.`;
|
|
50534
50535
|
var MEMORY_GUIDANCE = `## Memory \u2014 proactive, conversational
|
|
50535
50536
|
|
|
50536
|
-
You have Hindsight tools: \`mcp__hindsight__sync_retain\`, \`
|
|
50537
|
+
You have Hindsight tools: \`mcp__hindsight__sync_retain\`, \`mcp__hindsight__delete_document\`, \`mcp__hindsight__recall\`, \`mcp__hindsight__reflect\`. Use them without being asked.
|
|
50537
50538
|
|
|
50538
50539
|
### Retain proactively
|
|
50539
50540
|
When the user shares a fact, preference, decision, or plan worth keeping across sessions, call \`sync_retain\` in the same turn. Briefly acknowledge in your reply ("got it, April 2nd anniversary"). Don't narrate the tool call. Skip small talk and transient tool output, the auto-retain hook handles conversation-level signal.
|
|
50540
50541
|
|
|
50541
50542
|
### Correct proactively
|
|
50542
|
-
When the user corrects you or contradicts a prior memory, call \`
|
|
50543
|
+
When the user corrects you or contradicts a prior memory, call \`delete_document\` on the wrong entry, then \`sync_retain\` the correction. Acknowledge the correction in one line ("noted, Alice not Bob").
|
|
50543
50544
|
|
|
50544
50545
|
### Forget proactively
|
|
50545
|
-
When the user asks you to forget something ("forget that", "delete X", "drop what I said about Y"), call \`
|
|
50546
|
+
When the user asks you to forget something ("forget that", "delete X", "drop what I said about Y"), call \`delete_document\` for matching entries and confirm what was removed.
|
|
50546
50547
|
|
|
50547
50548
|
### Inspect proactively
|
|
50548
50549
|
When the user asks "what do you know about X / me", "what do you remember about Y", or any memory audit, use \`reflect\` to synthesize an answer across the bank. Return it as honest prose, not a raw dump. If the bank has little on the topic, say so.
|
|
@@ -50599,7 +50600,18 @@ name from that list.
|
|
|
50599
50600
|
Only call the \`vault_request_access\` MCP tool \u2014 which pings the
|
|
50600
50601
|
operator for a Telegram approval \u2014 for a key you've **confirmed via
|
|
50601
50602
|
\`vault list\` you don't already have.** Requesting access to a guessed
|
|
50602
|
-
or already-held key wastes the operator's tap and fails
|
|
50603
|
+
or already-held key wastes the operator's tap and fails.
|
|
50604
|
+
|
|
50605
|
+
**Never put a secret's value \u2014 or a \`vault:<key>\` placeholder \u2014 in a
|
|
50606
|
+
tool call.** Tool arguments are sent to the underlying API verbatim;
|
|
50607
|
+
nothing resolves a \`vault:\` reference for them. A real secret never
|
|
50608
|
+
needs to appear in a tool call \u2014 the launcher injects it into the
|
|
50609
|
+
tool's environment for you. A value you genuinely must pass literally
|
|
50610
|
+
(an account or customer ID) is a public identifier, not a secret, and
|
|
50611
|
+
belongs in plain config, not the vault. If a tool call is **blocked for
|
|
50612
|
+
containing a vaulted secret**, report that exact block message to the
|
|
50613
|
+
operator and stop \u2014 don't theorise about auth or tokens; it just means
|
|
50614
|
+
a stored value reached a tool argument.`;
|
|
50603
50615
|
function renderFleetInvariants() {
|
|
50604
50616
|
return [
|
|
50605
50617
|
"<!--",
|
|
@@ -51110,6 +51122,11 @@ function buildHumanizerEnvVars(agentDir, agent) {
|
|
|
51110
51122
|
const resolved = expanded.startsWith("/") ? expanded : resolve11(agentDir, expanded);
|
|
51111
51123
|
return { HUMANIZER_VOICE_FILE: resolved };
|
|
51112
51124
|
}
|
|
51125
|
+
function buildToolSearchEnvVars() {
|
|
51126
|
+
if (process.env.SWITCHROOM_DISABLE_TOOL_SEARCH === "1")
|
|
51127
|
+
return {};
|
|
51128
|
+
return { ENABLE_TOOL_SEARCH: process.env.SWITCHROOM_TOOL_SEARCH_MODE ?? "auto" };
|
|
51129
|
+
}
|
|
51113
51130
|
var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
51114
51131
|
"permissions",
|
|
51115
51132
|
"mcpServers",
|
|
@@ -51439,6 +51456,7 @@ function buildWorkspaceContext(args) {
|
|
|
51439
51456
|
fallbackModelQ: agentConfig.fallback_model ? shellSingleQuote(agentConfig.fallback_model) : undefined,
|
|
51440
51457
|
userEnvQuoted: (() => {
|
|
51441
51458
|
const combined = {
|
|
51459
|
+
...buildToolSearchEnvVars(),
|
|
51442
51460
|
...channelsToEnv(agentConfig),
|
|
51443
51461
|
...agentConfig.env ?? {},
|
|
51444
51462
|
...buildRepoEnvVars(name, agentDir, agentConfig),
|
|
@@ -51749,7 +51767,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
51749
51767
|
TELEGRAM_STATE_DIR: telegramStateDir,
|
|
51750
51768
|
SWITCHROOM_CONFIG: resolvedConfigPath,
|
|
51751
51769
|
SWITCHROOM_CLI_PATH: switchroomCliPath
|
|
51752
|
-
}
|
|
51770
|
+
},
|
|
51771
|
+
alwaysLoad: true
|
|
51753
51772
|
},
|
|
51754
51773
|
"agent-config": {
|
|
51755
51774
|
command: switchroomCliPath,
|
|
@@ -51757,7 +51776,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
51757
51776
|
env: {
|
|
51758
51777
|
SWITCHROOM_AGENT_NAME: name,
|
|
51759
51778
|
SWITCHROOM_CONFIG: resolvedConfigPath
|
|
51760
|
-
}
|
|
51779
|
+
},
|
|
51780
|
+
alwaysLoad: true
|
|
51761
51781
|
}
|
|
51762
51782
|
};
|
|
51763
51783
|
if (agentConfig.admin === true) {
|
|
@@ -52502,6 +52522,7 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
|
|
|
52502
52522
|
fallbackModelQ: agentConfig.fallback_model ? shellSingleQuote(agentConfig.fallback_model) : undefined,
|
|
52503
52523
|
userEnvQuoted: (() => {
|
|
52504
52524
|
const combined = {
|
|
52525
|
+
...buildToolSearchEnvVars(),
|
|
52505
52526
|
...channelsToEnv(agentConfig),
|
|
52506
52527
|
...agentConfig.env ?? {},
|
|
52507
52528
|
...buildRepoEnvVars(name, agentDir, agentConfig)
|
|
@@ -52784,7 +52805,8 @@ ${body}
|
|
|
52784
52805
|
TELEGRAM_STATE_DIR: telegramStateDir,
|
|
52785
52806
|
SWITCHROOM_CONFIG: resolvedConfigPath,
|
|
52786
52807
|
SWITCHROOM_CLI_PATH: switchroomCliPath
|
|
52787
|
-
}
|
|
52808
|
+
},
|
|
52809
|
+
alwaysLoad: true
|
|
52788
52810
|
},
|
|
52789
52811
|
"agent-config": {
|
|
52790
52812
|
command: switchroomCliPath,
|
|
@@ -52792,7 +52814,8 @@ ${body}
|
|
|
52792
52814
|
env: {
|
|
52793
52815
|
SWITCHROOM_AGENT_NAME: name,
|
|
52794
52816
|
SWITCHROOM_CONFIG: resolvedConfigPath
|
|
52795
|
-
}
|
|
52817
|
+
},
|
|
52818
|
+
alwaysLoad: true
|
|
52796
52819
|
}
|
|
52797
52820
|
};
|
|
52798
52821
|
if (agentConfig.admin === true) {
|
|
@@ -64068,6 +64091,15 @@ import { existsSync as existsSync37 } from "node:fs";
|
|
|
64068
64091
|
|
|
64069
64092
|
// src/vault/doctor.ts
|
|
64070
64093
|
var SENSITIVE_KEY_RE = /oauth(?![a-zA-Z])|token(?![a-zA-Z])|secret(?![a-zA-Z])|api[-_]?key(?![a-zA-Z])|password(?![a-zA-Z])/i;
|
|
64094
|
+
function looksLikeIdentifierValue(value) {
|
|
64095
|
+
const v = value.trim();
|
|
64096
|
+
if (v.length < 8 || v.length > 24)
|
|
64097
|
+
return false;
|
|
64098
|
+
if (!/^[0-9][0-9 -]*[0-9]$/.test(v))
|
|
64099
|
+
return false;
|
|
64100
|
+
const digitCount = v.replace(/[^0-9]/g, "").length;
|
|
64101
|
+
return digitCount >= 8;
|
|
64102
|
+
}
|
|
64071
64103
|
function analyseVaultHealth(input) {
|
|
64072
64104
|
const diagnostics = [];
|
|
64073
64105
|
if (input.brokerConfigured && input.brokerRunning === false) {
|
|
@@ -64123,6 +64155,23 @@ ${keyList}`,
|
|
|
64123
64155
|
fix: "Consider `switchroom vault set <key> --allow <agent>` to restrict access"
|
|
64124
64156
|
});
|
|
64125
64157
|
}
|
|
64158
|
+
const identifierKeys = [];
|
|
64159
|
+
for (const [keyName, entry] of Object.entries(input.vaultKeys)) {
|
|
64160
|
+
if (entry.looksLikeIdentifier)
|
|
64161
|
+
identifierKeys.push(keyName);
|
|
64162
|
+
}
|
|
64163
|
+
if (identifierKeys.length > 0) {
|
|
64164
|
+
const keyList = identifierKeys.map((k) => ` ${k}`).join(`
|
|
64165
|
+
`);
|
|
64166
|
+
diagnostics.push({
|
|
64167
|
+
level: "warn",
|
|
64168
|
+
check: "identifier-stored-as-secret",
|
|
64169
|
+
message: `${identifierKeys.length} vault key(s) hold an identifier-like value (digits only):
|
|
64170
|
+
${keyList}
|
|
64171
|
+
` + `If an agent must pass one of these in a tool call, the secret-guard hook will block it \u2014 it cannot tell a public ID from a secret.`,
|
|
64172
|
+
fix: "If the value is a public identifier (account/customer ID), remove it from the vault and pass it as plain config (e.g. hardcode it in the MCP launcher). If it is genuinely secret, ignore this."
|
|
64173
|
+
});
|
|
64174
|
+
}
|
|
64126
64175
|
const unreferencedKeys = [];
|
|
64127
64176
|
for (const keyName of Object.keys(input.vaultKeys)) {
|
|
64128
64177
|
if (!referencedKeys.has(keyName)) {
|
|
@@ -64214,7 +64263,8 @@ function registerVaultDoctorCommand(vault, program3) {
|
|
|
64214
64263
|
vaultKeys = {};
|
|
64215
64264
|
for (const [name, entry] of Object.entries(entries)) {
|
|
64216
64265
|
vaultKeys[name] = {
|
|
64217
|
-
scope: "scope" in entry && entry.scope ? entry.scope : undefined
|
|
64266
|
+
scope: "scope" in entry && entry.scope ? entry.scope : undefined,
|
|
64267
|
+
looksLikeIdentifier: entry.kind === "string" ? looksLikeIdentifierValue(entry.value) : false
|
|
64218
64268
|
};
|
|
64219
64269
|
}
|
|
64220
64270
|
} catch (err) {
|
|
@@ -77206,7 +77256,18 @@ function formatBytes(bytes) {
|
|
|
77206
77256
|
return `${bytes.toLocaleString()} bytes`;
|
|
77207
77257
|
}
|
|
77208
77258
|
function estimateTokens(bytes) {
|
|
77209
|
-
return Math.round(bytes /
|
|
77259
|
+
return Math.round(bytes / 3.7);
|
|
77260
|
+
}
|
|
77261
|
+
function readMcpServerNames(agentDir) {
|
|
77262
|
+
const mcpPath = join64(agentDir, ".mcp.json");
|
|
77263
|
+
if (!existsSync64(mcpPath))
|
|
77264
|
+
return [];
|
|
77265
|
+
try {
|
|
77266
|
+
const parsed = JSON.parse(readFileSync56(mcpPath, "utf-8"));
|
|
77267
|
+
return Object.keys(parsed.mcpServers ?? {});
|
|
77268
|
+
} catch {
|
|
77269
|
+
return null;
|
|
77270
|
+
}
|
|
77210
77271
|
}
|
|
77211
77272
|
function sha256(content) {
|
|
77212
77273
|
return createHash13("sha256").update(content).digest("hex").slice(0, 16);
|
|
@@ -77424,14 +77485,31 @@ function registerDebugCommand(program3) {
|
|
|
77424
77485
|
const soulMdBytes = soulMdContent.length;
|
|
77425
77486
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
77426
77487
|
const userBytes = userMessage?.text.length ?? 0;
|
|
77427
|
-
const
|
|
77428
|
-
|
|
77488
|
+
const fleetDir = join64(agentsDir, "..", "fleet");
|
|
77489
|
+
const fleetInvPath = join64(fleetDir, "switchroom-invariants.md");
|
|
77490
|
+
const fleetClaudePath = join64(fleetDir, "CLAUDE.md");
|
|
77491
|
+
const fleetInvBytes = existsSync64(fleetInvPath) ? readFileSync56(fleetInvPath, "utf-8").length : 0;
|
|
77492
|
+
const fleetClaudeBytes = existsSync64(fleetClaudePath) ? readFileSync56(fleetClaudePath, "utf-8").length : 0;
|
|
77493
|
+
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
77494
|
+
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
77495
|
+
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
77429
77496
|
console.log(`Per-session: ${formatBytes(perSessionBytes).padEnd(20)} (cache-warm until next session)`);
|
|
77430
|
-
console.log(`CLAUDE.md:
|
|
77431
|
-
console.log(`
|
|
77432
|
-
console.log(`Per-turn: ${formatBytes(perTurnBytes).padEnd(20)} (never cached)`);
|
|
77497
|
+
console.log(`CLAUDE.md (cwd): ${formatBytes(claudeMdBytes).padEnd(20)} (cache-hot)`);
|
|
77498
|
+
console.log(`Fleet invariants: ${formatBytes(fleetBytes).padEnd(20)} (--add-dir, cache-hot)`);
|
|
77499
|
+
console.log(`Per-turn: ${formatBytes(perTurnBytes).padEnd(20)} (never cached \u2014 the real per-turn $ cost)`);
|
|
77433
77500
|
console.log(`User message: ${formatBytes(userBytes).padEnd(20)}`);
|
|
77434
|
-
console.log(`
|
|
77501
|
+
console.log(`Authored text: ${formatBytes(totalBytes).padEnd(20)} (~${estimateTokens(totalBytes).toLocaleString()} tokens est.)`);
|
|
77502
|
+
const mcpServers = readMcpServerNames(agentDir);
|
|
77503
|
+
const mcpCount = mcpServers?.length ?? null;
|
|
77504
|
+
const mcpEstK = mcpCount != null ? mcpCount * 3 : null;
|
|
77505
|
+
const mcpLabel = mcpCount != null ? `${mcpCount} servers` : "(unreadable \u2014 agent-private .mcp.json)";
|
|
77506
|
+
const mcpEstLabel = mcpEstK != null ? `~${mcpEstK.toLocaleString()}k tok` : "~30k tok (audit est.)";
|
|
77507
|
+
console.log(`MCP tool surface: ${mcpLabel.padEnd(20)} (NOT counted above; ~3k tok/server \u2248 ${mcpEstLabel} \u2014 deferred under tool search)`);
|
|
77508
|
+
if (mcpServers && mcpServers.length > 0) {
|
|
77509
|
+
console.log(` [${mcpServers.join(", ")}]`);
|
|
77510
|
+
}
|
|
77511
|
+
const floorMcp = mcpEstK != null ? `~${mcpEstK.toLocaleString()}k` : "~30k";
|
|
77512
|
+
console.log(`Per-turn FLOOR: ${`~${estimateTokens(totalBytes).toLocaleString()} + ${floorMcp} MCP + ~13k CLI-base`.padEnd(20)} tokens est. (before the user msg, recall, or tool results)`);
|
|
77435
77513
|
const stableCacheInput = stableResult.concatenated + progressGuidance + baseSystemPromptAppend;
|
|
77436
77514
|
const stableHash = sha256(stableCacheInput);
|
|
77437
77515
|
console.log(`Cache stable hash: sha256:${stableHash}`);
|
|
@@ -13946,6 +13946,7 @@ var profileFields = {
|
|
|
13946
13946
|
memory: exports_external.object({
|
|
13947
13947
|
collection: exports_external.string().optional(),
|
|
13948
13948
|
auto_recall: exports_external.boolean().optional(),
|
|
13949
|
+
file: exports_external.boolean().optional(),
|
|
13949
13950
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
13950
13951
|
recall: exports_external.object({
|
|
13951
13952
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
@@ -11532,6 +11532,7 @@ var init_schema = __esm(() => {
|
|
|
11532
11532
|
memory: exports_external.object({
|
|
11533
11533
|
collection: exports_external.string().optional(),
|
|
11534
11534
|
auto_recall: exports_external.boolean().optional(),
|
|
11535
|
+
file: exports_external.boolean().optional(),
|
|
11535
11536
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11536
11537
|
recall: exports_external.object({
|
|
11537
11538
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
@@ -11532,6 +11532,7 @@ var init_schema = __esm(() => {
|
|
|
11532
11532
|
memory: exports_external.object({
|
|
11533
11533
|
collection: exports_external.string().optional(),
|
|
11534
11534
|
auto_recall: exports_external.boolean().optional(),
|
|
11535
|
+
file: exports_external.boolean().optional(),
|
|
11535
11536
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11536
11537
|
recall: exports_external.object({
|
|
11537
11538
|
max_memories: exports_external.number().int().min(0).optional(),
|
package/package.json
CHANGED
|
@@ -11,23 +11,21 @@ Before answering anything non-trivial:
|
|
|
11
11
|
|
|
12
12
|
1. Confirm who you are (see `IDENTITY.md` if present).
|
|
13
13
|
2. Confirm who you're talking to (see `USER.md` if present).
|
|
14
|
-
3.
|
|
15
|
-
`
|
|
16
|
-
|
|
14
|
+
3. Recall what matters — your memory is **Hindsight**: call
|
|
15
|
+
`mcp__hindsight__recall` when you need prior context, decisions, or who
|
|
16
|
+
someone is. (Full memory protocol is in your `CLAUDE.md`.)
|
|
17
17
|
4. If tools are configured, `TOOLS.md` captures setup-specific notes
|
|
18
18
|
(host paths, credentials, endpoints) that save you discovery work.
|
|
19
19
|
|
|
20
20
|
Don't ask permission to read these files. They exist so you can use them.
|
|
21
21
|
|
|
22
|
-
## Memory
|
|
22
|
+
## Memory
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
write it down. Future-you is fresh every session. Files are your
|
|
30
|
-
continuity.
|
|
24
|
+
Memory is **Hindsight**, not files — `recall` to read, `sync_retain` to keep a
|
|
25
|
+
fact across sessions, `delete_document` to forget. Claude Code's built-in
|
|
26
|
+
file-based auto-memory is disabled for this agent; do not maintain a `MEMORY.md`
|
|
27
|
+
index or a `memory/` daily-notes directory. Your `CLAUDE.md` has the full
|
|
28
|
+
protocol.
|
|
31
29
|
|
|
32
30
|
## Safety defaults
|
|
33
31
|
|
|
@@ -55,8 +53,8 @@ Failed edits burn your own context and the user's patience. Verify first.
|
|
|
55
53
|
tool exists.
|
|
56
54
|
- Long-running commands: use the background flag and poll, rather than
|
|
57
55
|
blocking.
|
|
58
|
-
- `
|
|
59
|
-
|
|
56
|
+
- `mcp__hindsight__recall` is your interface to past context and decisions;
|
|
57
|
+
recall before asking the user something you may already know.
|
|
60
58
|
|
|
61
59
|
## Working on code repositories
|
|
62
60
|
|
|
@@ -24035,6 +24035,7 @@ var init_schema = __esm(() => {
|
|
|
24035
24035
|
memory: exports_external.object({
|
|
24036
24036
|
collection: exports_external.string().optional(),
|
|
24037
24037
|
auto_recall: exports_external.boolean().optional(),
|
|
24038
|
+
file: exports_external.boolean().optional(),
|
|
24038
24039
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
24039
24040
|
recall: exports_external.object({
|
|
24040
24041
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
@@ -52820,11 +52821,11 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
52820
52821
|
}
|
|
52821
52822
|
|
|
52822
52823
|
// ../src/build-info.ts
|
|
52823
|
-
var VERSION = "0.14.
|
|
52824
|
-
var COMMIT_SHA = "
|
|
52825
|
-
var COMMIT_DATE = "2026-06-
|
|
52826
|
-
var LATEST_PR =
|
|
52827
|
-
var COMMITS_AHEAD_OF_TAG =
|
|
52824
|
+
var VERSION = "0.14.77";
|
|
52825
|
+
var COMMIT_SHA = "2473dbbf";
|
|
52826
|
+
var COMMIT_DATE = "2026-06-07T00:02:11+10:00";
|
|
52827
|
+
var LATEST_PR = null;
|
|
52828
|
+
var COMMITS_AHEAD_OF_TAG = 4;
|
|
52828
52829
|
|
|
52829
52830
|
// gateway/boot-version.ts
|
|
52830
52831
|
function formatRelativeAgo(iso) {
|
|
@@ -194,10 +194,19 @@ async function main() {
|
|
|
194
194
|
}
|
|
195
195
|
const hit = scanToolInput(toolInput, vaultValues)
|
|
196
196
|
if (hit) {
|
|
197
|
+
// The reason is read by the model. It must be CORRECT and actionable:
|
|
198
|
+
// earlier versions said "reference it as vault:<key> instead", which
|
|
199
|
+
// suggested a `vault:` resolver that does not exist for tool arguments
|
|
200
|
+
// — sending agents (and operators) chasing a phantom mechanism. State
|
|
201
|
+
// the two real resolutions instead, and do NOT imply a resolver.
|
|
197
202
|
process.stdout.write(
|
|
198
203
|
JSON.stringify({
|
|
199
204
|
decision: 'block',
|
|
200
|
-
reason:
|
|
205
|
+
reason:
|
|
206
|
+
`Blocked: this tool input contains the value of vault secret '${hit.key}'. ` +
|
|
207
|
+
`Secrets must never appear in a tool call. ` +
|
|
208
|
+
`If '${hit.key}' is a real secret, do not pass it as an argument — the launcher injects it into the tool's environment, so you never type it. ` +
|
|
209
|
+
`If '${hit.key}' is actually a public identifier (e.g. an account or customer ID that must appear in the call), it should not be in the vault — ask the operator to move it to plain config.`,
|
|
201
210
|
}),
|
|
202
211
|
)
|
|
203
212
|
process.exit(0)
|
|
@@ -132,7 +132,13 @@ describe('secret-guard-pretool.mjs (broker-direct)', () => {
|
|
|
132
132
|
})
|
|
133
133
|
expect(r.status).toBe(0)
|
|
134
134
|
expect(r.stdout).toContain('"decision":"block"')
|
|
135
|
-
|
|
135
|
+
// The reason must name the offending key and explain the real fix.
|
|
136
|
+
expect(r.stdout).toContain('gh-token')
|
|
137
|
+
expect(r.stdout).toContain('Secrets must never appear in a tool call')
|
|
138
|
+
// It must NOT resurrect the old, misleading "reference it as vault:<key>"
|
|
139
|
+
// suggestion — there is no vault: resolver for tool arguments, and that
|
|
140
|
+
// advice sent agents down a dead end (see the hook's block-reason note).
|
|
141
|
+
expect(r.stdout).not.toContain('reference it as vault:')
|
|
136
142
|
})
|
|
137
143
|
|
|
138
144
|
it('allows when tool_input contains no vaulted value', async () => {
|