@cachly-dev/mcp-server 0.10.128 → 0.10.130
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/README.md +2 -2
- package/dist/index.js +1 -7
- package/dist/packages/telegram-notify/client.js +2 -75
- package/dist/src/ambient-cli.js +7 -253
- package/dist/src/ambient-deps.js +1 -55
- package/dist/src/ambient-hooks.js +5 -278
- package/dist/src/ambient-ledger.js +6 -95
- package/dist/src/ambient-memory.js +1 -67
- package/dist/src/ambient-recall.js +1 -221
- package/dist/src/aussetzer.js +1 -82
- package/dist/src/auth.js +9 -140
- package/dist/src/autor-abzeichen.js +1 -56
- package/dist/src/autor.js +1 -136
- package/dist/src/bauabdruck.js +1 -93
- package/dist/src/bedeutung.js +2 -323
- package/dist/src/bench/cachly-bench.js +4 -249
- package/dist/src/bench/external-corpus.js +4 -135
- package/dist/src/bench/fixtures.js +1 -225
- package/dist/src/brain-export.js +16 -119
- package/dist/src/brain-watch-hook.js +8 -86
- package/dist/src/cachly-url.js +1 -21
- package/dist/src/ckg.js +1 -108
- package/dist/src/cls-hook.js +8 -94
- package/dist/src/confidence.js +1 -50
- package/dist/src/credentials.js +1 -85
- package/dist/src/deutsch.js +1 -223
- package/dist/src/doctor.js +2 -248
- package/dist/src/editor.js +1 -63
- package/dist/src/eingaenge-nachruesten.js +1 -290
- package/dist/src/eingaenge.js +2 -228
- package/dist/src/embeddings.js +13 -223
- package/dist/src/feldreparatur.js +3 -0
- package/dist/src/first-contact.js +4 -108
- package/dist/src/first-recall-proof.js +4 -41
- package/dist/src/funnel-milestones.js +2 -48
- package/dist/src/geliefert.js +1 -114
- package/dist/src/handlers/advanced.js +38 -537
- package/dist/src/handlers/brain.js +69 -4181
- package/dist/src/handlers/cache.js +59 -638
- package/dist/src/handlers/context.js +17 -182
- package/dist/src/handlers/fedbrain.js +40 -1436
- package/dist/src/handlers/instances.js +16 -201
- package/dist/src/handlers/roadmap.js +13 -175
- package/dist/src/handlers/share.js +19 -530
- package/dist/src/handlers/syndicate.js +38 -904
- package/dist/src/handlers/team.js +89 -1850
- package/dist/src/handlers/viz.js +3 -148
- package/dist/src/index.js +321 -4281
- package/dist/src/interferenz.js +1 -0
- package/dist/src/kostprobe.js +7 -125
- package/dist/src/lesson-preview.js +2 -133
- package/dist/src/namespace.js +1 -128
- package/dist/src/notifier.js +1 -6
- package/dist/src/rangfolge-stellschrauben.js +1 -0
- package/dist/src/rangfolge.js +1 -214
- package/dist/src/raumkorrektur.js +1 -197
- package/dist/src/recall-tiefe.js +1 -42
- package/dist/src/rerank.js +1 -154
- package/dist/src/resonanz.js +1 -0
- package/dist/src/search.js +1 -2104
- package/dist/src/seltenheitsbestand.js +1 -92
- package/dist/src/speicherlage.js +1 -84
- package/dist/src/spuren.js +1 -150
- package/dist/src/starter-corpus.js +1 -165
- package/dist/src/telemetry-types.js +0 -13
- package/dist/src/tools.js +10 -2465
- package/dist/src/toolspecs.js +1 -121
- package/dist/src/upgrade-nudge.js +1 -29
- package/dist/src/utils.js +1 -89
- package/dist/src/versuch.js +1 -250
- package/dist/src/vorspann.js +1 -146
- package/dist/src/wartung/vektoren-verdichten.js +1 -200
- package/dist/src/werkzeug-nutzung.js +1 -72
- package/dist/src/wertbeitrag.js +1 -73
- package/dist/src/wirkungsspur.js +1 -0
- package/glama.json +1 -1
- package/package.json +8 -7
- package/server.json +3 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js.map +0 -1
- package/dist/packages/telegram-notify/client.d.ts +0 -19
- package/dist/packages/telegram-notify/client.js.map +0 -1
- package/dist/src/ambient-cli.d.ts +0 -101
- package/dist/src/ambient-cli.js.map +0 -1
- package/dist/src/ambient-deps.d.ts +0 -26
- package/dist/src/ambient-deps.js.map +0 -1
- package/dist/src/ambient-hooks.d.ts +0 -137
- package/dist/src/ambient-hooks.js.map +0 -1
- package/dist/src/ambient-ledger.d.ts +0 -19
- package/dist/src/ambient-ledger.js.map +0 -1
- package/dist/src/ambient-memory.d.ts +0 -12
- package/dist/src/ambient-memory.js.map +0 -1
- package/dist/src/ambient-recall.d.ts +0 -127
- package/dist/src/ambient-recall.js.map +0 -1
- package/dist/src/aussetzer.d.ts +0 -67
- package/dist/src/aussetzer.js.map +0 -1
- package/dist/src/auth.d.ts +0 -43
- package/dist/src/auth.js.map +0 -1
- package/dist/src/autor-abzeichen.d.ts +0 -41
- package/dist/src/autor-abzeichen.js.map +0 -1
- package/dist/src/autor.d.ts +0 -101
- package/dist/src/autor.js.map +0 -1
- package/dist/src/bauabdruck-daten.generiert.json +0 -5
- package/dist/src/bauabdruck.d.ts +0 -38
- package/dist/src/bauabdruck.js.map +0 -1
- package/dist/src/bedeutung.d.ts +0 -168
- package/dist/src/bedeutung.js.map +0 -1
- package/dist/src/bench/cachly-bench.d.ts +0 -61
- package/dist/src/bench/cachly-bench.js.map +0 -1
- package/dist/src/bench/external-corpus.d.ts +0 -44
- package/dist/src/bench/external-corpus.js.map +0 -1
- package/dist/src/bench/fixtures.d.ts +0 -37
- package/dist/src/bench/fixtures.js.map +0 -1
- package/dist/src/brain-export.d.ts +0 -64
- package/dist/src/brain-export.js.map +0 -1
- package/dist/src/brain-watch-hook.d.ts +0 -21
- package/dist/src/brain-watch-hook.js.map +0 -1
- package/dist/src/cachly-url.d.ts +0 -68
- package/dist/src/cachly-url.js.map +0 -1
- package/dist/src/ckg.d.ts +0 -54
- package/dist/src/ckg.js.map +0 -1
- package/dist/src/cls-hook.d.ts +0 -15
- package/dist/src/cls-hook.js.map +0 -1
- package/dist/src/confidence.d.ts +0 -19
- package/dist/src/confidence.js.map +0 -1
- package/dist/src/credentials.d.ts +0 -37
- package/dist/src/credentials.js.map +0 -1
- package/dist/src/deutsch.d.ts +0 -98
- package/dist/src/deutsch.js.map +0 -1
- package/dist/src/doctor.d.ts +0 -47
- package/dist/src/doctor.js.map +0 -1
- package/dist/src/editor.d.ts +0 -9
- package/dist/src/editor.js.map +0 -1
- package/dist/src/eingaenge-nachruesten.d.ts +0 -41
- package/dist/src/eingaenge-nachruesten.js.map +0 -1
- package/dist/src/eingaenge.d.ts +0 -135
- package/dist/src/eingaenge.js.map +0 -1
- package/dist/src/embeddings.d.ts +0 -48
- package/dist/src/embeddings.js.map +0 -1
- package/dist/src/first-contact.d.ts +0 -56
- package/dist/src/first-contact.js.map +0 -1
- package/dist/src/first-recall-proof.d.ts +0 -19
- package/dist/src/first-recall-proof.js.map +0 -1
- package/dist/src/funnel-milestones.d.ts +0 -5
- package/dist/src/funnel-milestones.js.map +0 -1
- package/dist/src/geliefert.d.ts +0 -63
- package/dist/src/geliefert.js.map +0 -1
- package/dist/src/handlers/advanced.d.ts +0 -6
- package/dist/src/handlers/advanced.js.map +0 -1
- package/dist/src/handlers/brain.d.ts +0 -41
- package/dist/src/handlers/brain.js.map +0 -1
- package/dist/src/handlers/cache.d.ts +0 -6
- package/dist/src/handlers/cache.js.map +0 -1
- package/dist/src/handlers/context.d.ts +0 -6
- package/dist/src/handlers/context.js.map +0 -1
- package/dist/src/handlers/fedbrain.d.ts +0 -18
- package/dist/src/handlers/fedbrain.js.map +0 -1
- package/dist/src/handlers/instances.d.ts +0 -33
- package/dist/src/handlers/instances.js.map +0 -1
- package/dist/src/handlers/roadmap.d.ts +0 -6
- package/dist/src/handlers/roadmap.js.map +0 -1
- package/dist/src/handlers/share.d.ts +0 -8
- package/dist/src/handlers/share.js.map +0 -1
- package/dist/src/handlers/syndicate.d.ts +0 -6
- package/dist/src/handlers/syndicate.js.map +0 -1
- package/dist/src/handlers/team.d.ts +0 -45
- package/dist/src/handlers/team.js.map +0 -1
- package/dist/src/handlers/viz.d.ts +0 -6
- package/dist/src/handlers/viz.js.map +0 -1
- package/dist/src/index.d.ts +0 -93
- package/dist/src/index.js.map +0 -1
- package/dist/src/kostprobe.d.ts +0 -25
- package/dist/src/kostprobe.js.map +0 -1
- package/dist/src/lesson-preview.d.ts +0 -81
- package/dist/src/lesson-preview.js.map +0 -1
- package/dist/src/namespace.d.ts +0 -20
- package/dist/src/namespace.js.map +0 -1
- package/dist/src/notifier.d.ts +0 -5
- package/dist/src/notifier.js.map +0 -1
- package/dist/src/rangfolge.d.ts +0 -169
- package/dist/src/rangfolge.js.map +0 -1
- package/dist/src/raumkorrektur.d.ts +0 -100
- package/dist/src/raumkorrektur.js.map +0 -1
- package/dist/src/recall-tiefe.d.ts +0 -48
- package/dist/src/recall-tiefe.js.map +0 -1
- package/dist/src/rerank.d.ts +0 -48
- package/dist/src/rerank.js.map +0 -1
- package/dist/src/search.d.ts +0 -153
- package/dist/src/search.js.map +0 -1
- package/dist/src/seltenheitsbestand.d.ts +0 -53
- package/dist/src/seltenheitsbestand.js.map +0 -1
- package/dist/src/speicherlage.d.ts +0 -51
- package/dist/src/speicherlage.js.map +0 -1
- package/dist/src/spuren.d.ts +0 -97
- package/dist/src/spuren.js.map +0 -1
- package/dist/src/starter-corpus.d.ts +0 -28
- package/dist/src/starter-corpus.js.map +0 -1
- package/dist/src/telemetry-types.d.ts +0 -87
- package/dist/src/telemetry-types.js.map +0 -1
- package/dist/src/tools.d.ts +0 -2933
- package/dist/src/tools.js.map +0 -1
- package/dist/src/toolspecs.d.ts +0 -43
- package/dist/src/toolspecs.js.map +0 -1
- package/dist/src/upgrade-nudge.d.ts +0 -15
- package/dist/src/upgrade-nudge.js.map +0 -1
- package/dist/src/utils.d.ts +0 -34
- package/dist/src/utils.js.map +0 -1
- package/dist/src/versuch.d.ts +0 -179
- package/dist/src/versuch.js.map +0 -1
- package/dist/src/vorspann.d.ts +0 -73
- package/dist/src/vorspann.js.map +0 -1
- package/dist/src/wartung/vektoren-verdichten.d.ts +0 -50
- package/dist/src/wartung/vektoren-verdichten.js.map +0 -1
- package/dist/src/werkzeug-nutzung.d.ts +0 -62
- package/dist/src/werkzeug-nutzung.js.map +0 -1
- package/dist/src/wertbeitrag.d.ts +0 -57
- package/dist/src/wertbeitrag.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<img src="https://img.shields.io/badge/GDPR-EU%20servers-green" alt="GDPR: EU servers" />
|
|
25
25
|
</a>
|
|
26
26
|
|
|
27
|
-
<img src="https://img.shields.io/badge/
|
|
27
|
+
<img src="https://img.shields.io/badge/123_MCP_tools-violet" alt="123 MCP tools" />
|
|
28
28
|
|
|
29
29
|
<img src="https://img.shields.io/badge/License-Apache--2.0-yellow" alt="License: Apache-2.0" />
|
|
30
30
|
</p>
|
|
@@ -301,7 +301,7 @@ MCP tool — bulk-ingests past outcomes the same way `brain_from_git` ingests co
|
|
|
301
301
|
|
|
302
302
|
---
|
|
303
303
|
|
|
304
|
-
## MCP Tools (
|
|
304
|
+
## MCP Tools (123 total)
|
|
305
305
|
|
|
306
306
|
The full tool catalog is generated from `sdk/mcp/src/tools.ts`. Cross-surface
|
|
307
307
|
coverage is tracked in [`../../docs/generated/surface-parity.md`](../../docs/generated/surface-parity.md),
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// automatisch und starten mit `node dist/index.js`. tsc (rootDir ".") legt den
|
|
3
|
-
// echten Einstieg aber unter dist/src/index.js ab. Diese Datei kompiliert nach
|
|
4
|
-
// dist/index.js und startet dort denselben Server — der Import genügt, weil
|
|
5
|
-
// src/index.ts auf Modul-Ebene startet.
|
|
6
|
-
import './src/index.js';
|
|
7
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import"./src/index.js";
|
|
@@ -1,75 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Used by cachly-mcp (TypeScript) and TCO backend (Python via client.py).
|
|
5
|
-
* Single source of truth for: emoji map, message format, env var convention.
|
|
6
|
-
*
|
|
7
|
-
* Interface: notify(app, event, payload)
|
|
8
|
-
*
|
|
9
|
-
* Env vars:
|
|
10
|
-
* TELEGRAM_BOT_TOKEN — bot token from @BotFather
|
|
11
|
-
* {APP_UPPER}_TELEGRAM_CHAT_ID — app-specific channel (e.g. CACHLY_TELEGRAM_CHAT_ID)
|
|
12
|
-
* TELEGRAM_CHAT_ID — fallback for all apps
|
|
13
|
-
*
|
|
14
|
-
* No-op when unconfigured. Never throws.
|
|
15
|
-
*
|
|
16
|
-
* Note: emoji map is kept in sync with emojis.json (Python reads the JSON file at runtime).
|
|
17
|
-
*/
|
|
18
|
-
const EMOJI = {
|
|
19
|
-
tool_call: '🔧',
|
|
20
|
-
tool_error: '🔴',
|
|
21
|
-
cache_hit: '⚡',
|
|
22
|
-
cache_miss: '💭',
|
|
23
|
-
auth_ok: '🔑',
|
|
24
|
-
auth_fail: '🚫',
|
|
25
|
-
startup: '🚀',
|
|
26
|
-
waitlist_signup: '📬',
|
|
27
|
-
parse_file: '📄',
|
|
28
|
-
parse_text: '📝',
|
|
29
|
-
parse_url: '🔗',
|
|
30
|
-
trip_created: '🗺️',
|
|
31
|
-
trip_deleted: '🗑️',
|
|
32
|
-
error: '🔴',
|
|
33
|
-
stripe_payment: '💳',
|
|
34
|
-
stripe_checkout: '🛒',
|
|
35
|
-
new_user: '🆕',
|
|
36
|
-
pdf_export: '📑',
|
|
37
|
-
trip_search: '🔍',
|
|
38
|
-
default: '🔔',
|
|
39
|
-
};
|
|
40
|
-
function getToken() {
|
|
41
|
-
return process.env.TELEGRAM_BOT_TOKEN ?? '';
|
|
42
|
-
}
|
|
43
|
-
function getChatId(app) {
|
|
44
|
-
const key = `${app.toUpperCase()}_TELEGRAM_CHAT_ID`;
|
|
45
|
-
return process.env[key] ?? process.env.TELEGRAM_CHAT_ID ?? '';
|
|
46
|
-
}
|
|
47
|
-
export function formatMessage(app, event, payload) {
|
|
48
|
-
const icon = EMOJI[event] ?? EMOJI['default'];
|
|
49
|
-
const lines = [`${icon} *${app.toUpperCase()}* · \`${event}\``];
|
|
50
|
-
for (const [k, v] of Object.entries(payload)) {
|
|
51
|
-
if (v !== null && v !== undefined && v !== '') {
|
|
52
|
-
lines.push(` ${k}: \`${String(v)}\``);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return lines.join('\n');
|
|
56
|
-
}
|
|
57
|
-
export async function notify(app, event, payload = {}) {
|
|
58
|
-
const token = getToken();
|
|
59
|
-
const chatId = getChatId(app);
|
|
60
|
-
if (!token || !chatId)
|
|
61
|
-
return;
|
|
62
|
-
try {
|
|
63
|
-
const text = formatMessage(app, event, payload);
|
|
64
|
-
await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: { 'Content-Type': 'application/json' },
|
|
67
|
-
body: JSON.stringify({ chat_id: chatId, text, parse_mode: 'Markdown' }),
|
|
68
|
-
signal: AbortSignal.timeout(5000),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
// never throws
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
//# sourceMappingURL=client.js.map
|
|
1
|
+
const i={tool_call:"\u{1F527}",tool_error:"\u{1F534}",cache_hit:"\u26A1",cache_miss:"\u{1F4AD}",auth_ok:"\u{1F511}",auth_fail:"\u{1F6AB}",startup:"\u{1F680}",waitlist_signup:"\u{1F4EC}",parse_file:"\u{1F4C4}",parse_text:"\u{1F4DD}",parse_url:"\u{1F517}",trip_created:"\u{1F5FA}\uFE0F",trip_deleted:"\u{1F5D1}\uFE0F",error:"\u{1F534}",stripe_payment:"\u{1F4B3}",stripe_checkout:"\u{1F6D2}",new_user:"\u{1F195}",pdf_export:"\u{1F4D1}",trip_search:"\u{1F50D}",default:"\u{1F514}"};function c(){return process.env.TELEGRAM_BOT_TOKEN??""}function p(t){const e=`${t.toUpperCase()}_TELEGRAM_CHAT_ID`;return process.env[e]??process.env.TELEGRAM_CHAT_ID??""}function _(t,e,r){const n=[`${i[e]??i.default} *${t.toUpperCase()}* \xB7 \`${e}\``];for(const[a,o]of Object.entries(r))o!=null&&o!==""&&n.push(` ${a}: \`${String(o)}\``);return n.join(`
|
|
2
|
+
`)}async function u(t,e,r={}){const s=c(),n=p(t);if(!(!s||!n))try{const a=_(t,e,r);await fetch(`https://api.telegram.org/bot${s}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:n,text:a,parse_mode:"Markdown"}),signal:AbortSignal.timeout(5e3)})}catch{}}export{_ as formatMessage,u as notify};
|
package/dist/src/ambient-cli.js
CHANGED
|
@@ -1,253 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
// Design constraints (roadmap §6.3):
|
|
10
|
-
// • Pure + dependency-injected (recall is passed in) so it is exhaustively
|
|
11
|
-
// unit-tested without any network.
|
|
12
|
-
// • Graceful: EVERY failure path returns '' (no output) — the caller exits 0 so
|
|
13
|
-
// a crashing hook can never block or corrupt the agent's turn.
|
|
14
|
-
// • Self-limited: recall runs under a hard timeout budget; a slow brain never
|
|
15
|
-
// stalls the user's prompt.
|
|
16
|
-
import { decideRecall, commitInjection, emptyMemory, isTrivialPrompt, selectInjectable, estimateTokens, } from './ambient-recall.js';
|
|
17
|
-
/** Parse the hook payload from stdin. Never throws — returns null on any problem. */
|
|
18
|
-
export function parseHookPayload(raw) {
|
|
19
|
-
const trimmed = (raw ?? '').trim();
|
|
20
|
-
if (!trimmed)
|
|
21
|
-
return null;
|
|
22
|
-
try {
|
|
23
|
-
const p = JSON.parse(trimmed);
|
|
24
|
-
if (p && typeof p === 'object')
|
|
25
|
-
return p;
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// PreToolUse fires for every tool; only file-mutating tools carry a wrong-path
|
|
33
|
-
// risk worth a recall (roadmap §6.1: file-open briefing on Edit/Write).
|
|
34
|
-
const FILE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
|
|
35
|
-
/**
|
|
36
|
-
* The query to recall on for a payload, or null when recall should be skipped
|
|
37
|
-
* before any brain call:
|
|
38
|
-
* • UserPromptSubmit → the prompt, unless it is trivial (§6.3 guardrail 2).
|
|
39
|
-
* • SessionStart → a fixed briefing query (there is no user prompt yet);
|
|
40
|
-
* skipped on 'compact'/'clear' resumes where a mid-session briefing is noise.
|
|
41
|
-
* • PreToolUse → a file-scoped query for Edit/Write-class tools only.
|
|
42
|
-
* • Stop → never recalls (it learns instead — see stopObservation).
|
|
43
|
-
*/
|
|
44
|
-
export function recallQueryFor(payload) {
|
|
45
|
-
const event = payload.hook_event_name ?? 'UserPromptSubmit';
|
|
46
|
-
if (event === 'SessionStart') {
|
|
47
|
-
if (payload.source === 'compact' || payload.source === 'clear')
|
|
48
|
-
return null;
|
|
49
|
-
return 'session start: recent lessons, active pitfalls, and known failure modes for this project';
|
|
50
|
-
}
|
|
51
|
-
if (event === 'PreToolUse') {
|
|
52
|
-
if (payload.tool_name && !FILE_TOOLS.has(payload.tool_name))
|
|
53
|
-
return null;
|
|
54
|
-
const filePath = payload.tool_input?.file_path;
|
|
55
|
-
if (typeof filePath !== 'string' || !filePath.trim())
|
|
56
|
-
return null;
|
|
57
|
-
return `known pitfalls, past bugs and lessons for file ${filePath.trim()}`;
|
|
58
|
-
}
|
|
59
|
-
if (event === 'Stop')
|
|
60
|
-
return null;
|
|
61
|
-
const prompt = (payload.prompt ?? '').trim();
|
|
62
|
-
if (!prompt || isTrivialPrompt(prompt))
|
|
63
|
-
return null;
|
|
64
|
-
return prompt;
|
|
65
|
-
}
|
|
66
|
-
// Conservative fix-signal: only turns whose final message clearly reports a
|
|
67
|
-
// resolved problem become auto-lessons. Anything looser floods the brain with
|
|
68
|
-
// junk on every turn-end — the exact noise §6.4 warns about.
|
|
69
|
-
const FIX_SIGNAL_RE = /\b(fixed|resolved|behoben|gefixt|root cause|the bug was|der fehler (lag|war)|ursache (war|gefunden)|now passes|tests? (are|is|sind) (green|grün)|deployed successfully)\b/i;
|
|
70
|
-
/**
|
|
71
|
-
* Derive an auto-learn observation from a Stop payload, or null when the turn
|
|
72
|
-
* carries no clear fix signal. Pure — the caller feeds it to auto_learn_session.
|
|
73
|
-
*/
|
|
74
|
-
export function stopObservation(payload) {
|
|
75
|
-
const msg = (payload.last_assistant_message ?? '').trim();
|
|
76
|
-
if (msg.length < 80)
|
|
77
|
-
return null; // too short to describe a real fix
|
|
78
|
-
if (!FIX_SIGNAL_RE.test(msg))
|
|
79
|
-
return null;
|
|
80
|
-
const firstLine = msg.split('\n').find((l) => l.trim().length > 0)?.trim() ?? msg;
|
|
81
|
-
return {
|
|
82
|
-
action: firstLine.slice(0, 200),
|
|
83
|
-
outcome: 'success',
|
|
84
|
-
details: msg.slice(0, 500),
|
|
85
|
-
severity: 'minor',
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
/** Truncate text to fit an estimated token budget (~4 chars/token), on a word edge. */
|
|
89
|
-
export function truncateToTokens(text, maxTokens) {
|
|
90
|
-
const t = text.trim();
|
|
91
|
-
if (estimateTokens(t) <= maxTokens)
|
|
92
|
-
return t;
|
|
93
|
-
const maxChars = Math.max(0, maxTokens * 4);
|
|
94
|
-
const cut = t.slice(0, maxChars);
|
|
95
|
-
const lastSpace = cut.lastIndexOf(' ');
|
|
96
|
-
return (lastSpace > maxChars * 0.6 ? cut.slice(0, lastSpace) : cut).trimEnd() + '…';
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Tiny footer that closes the net-token loop (§6.2): the agent self-reports
|
|
100
|
-
* prevented tokens via `ambient-credit` when a recalled lesson changed its path.
|
|
101
|
-
* Kept to one short line — its own token cost is counted as injected.
|
|
102
|
-
*/
|
|
103
|
-
export const CREDIT_FOOTER = '(If this memory just saved you from a wrong path, record it: `npx @cachly-dev/mcp-server@latest ambient-credit <tokens-saved>`)';
|
|
104
|
-
/**
|
|
105
|
-
* Der Beleg: WANN es gelernt wurde und WORAN.
|
|
106
|
-
*
|
|
107
|
-
* Ohne Beleg ist proaktive Erinnerung eine Behauptung — und Behauptungen einer
|
|
108
|
-
* Maschine prueft niemand nach, man glaubt sie oder ignoriert sie. Mit Datum
|
|
109
|
-
* und Datei wird daraus eine nachpruefbare Aussage: "am 24.07., in
|
|
110
|
-
* infra/backup.sh — ging damals schief". Genau das macht den Unterschied
|
|
111
|
-
* zwischen einem Hinweis, dem man folgt, und einem, den man wegklickt.
|
|
112
|
-
*
|
|
113
|
-
* Bewusst kurz gehalten (eine Zeile, hoechstens zwei Dateien): der Beleg wird
|
|
114
|
-
* bei JEDER Erinnerung mitgezahlt und darf das Token-Budget nicht sprengen.
|
|
115
|
-
*/
|
|
116
|
-
export function formatProvenance(l) {
|
|
117
|
-
const teile = [];
|
|
118
|
-
if (l.learnedAt) {
|
|
119
|
-
const d = new Date(l.learnedAt);
|
|
120
|
-
if (!Number.isNaN(d.getTime())) {
|
|
121
|
-
teile.push(`am ${String(d.getDate()).padStart(2, "0")}.${String(d.getMonth() + 1).padStart(2, "0")}.`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (l.files?.length) {
|
|
125
|
-
const f = l.files.slice(0, 2).join(", ");
|
|
126
|
-
teile.push(`in ${f}${l.files.length > 2 ? ` (+${l.files.length - 2})` : ""}`);
|
|
127
|
-
}
|
|
128
|
-
if (l.outcome === "failure")
|
|
129
|
-
teile.push("ging damals schief");
|
|
130
|
-
else if (l.outcome === "success")
|
|
131
|
-
teile.push("so ging es damals gut aus");
|
|
132
|
-
return teile.length ? ` ↳ ${teile.join(" · ")}` : "";
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Render the gated lessons into a context block for the agent.
|
|
136
|
-
* • A single already-formatted briefing (multi-line summary — what smart_recall
|
|
137
|
-
* returns) is injected verbatim; it carries its own headings.
|
|
138
|
-
* • Multiple short lessons are rendered as a titled bullet list.
|
|
139
|
-
* `withCreditFooter` appends the self-report line (per-prompt events only —
|
|
140
|
-
* a SessionStart briefing is ambience, not a decision-changing recall).
|
|
141
|
-
*/
|
|
142
|
-
export function formatContextBlock(lessons, withCreditFooter = false) {
|
|
143
|
-
if (lessons.length === 0)
|
|
144
|
-
return '';
|
|
145
|
-
const body = lessons.length === 1 && lessons[0].summary.includes('\n')
|
|
146
|
-
? lessons[0].summary.trim()
|
|
147
|
-
: `🧠 Relevant memory from your cachly brain (auto-recalled):\n` +
|
|
148
|
-
lessons
|
|
149
|
-
.map((l) => {
|
|
150
|
-
const beleg = formatProvenance(l);
|
|
151
|
-
return `- ${l.summary.trim()}` + (beleg ? '\n' + beleg : '');
|
|
152
|
-
})
|
|
153
|
-
.join('\n');
|
|
154
|
-
return withCreditFooter ? `${body}\n${CREDIT_FOOTER}` : body;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Build the JSON Claude Code expects from a hook. `additionalContext` is spliced
|
|
158
|
-
* into the model's context for the turn. Empty context → empty string (no output),
|
|
159
|
-
* which Claude Code treats as "hook contributed nothing".
|
|
160
|
-
*/
|
|
161
|
-
export function buildHookOutput(event, additionalContext) {
|
|
162
|
-
if (!additionalContext)
|
|
163
|
-
return '';
|
|
164
|
-
return JSON.stringify({
|
|
165
|
-
hookSpecificOutput: {
|
|
166
|
-
hookEventName: event,
|
|
167
|
-
additionalContext,
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
/** Resolve a promise to a fallback if it does not settle within `ms`. */
|
|
172
|
-
async function withTimeout(p, ms, fallback) {
|
|
173
|
-
let timer;
|
|
174
|
-
const timeout = new Promise((res) => {
|
|
175
|
-
timer = setTimeout(() => res(fallback), ms);
|
|
176
|
-
});
|
|
177
|
-
try {
|
|
178
|
-
return await Promise.race([p, timeout]);
|
|
179
|
-
}
|
|
180
|
-
finally {
|
|
181
|
-
if (timer)
|
|
182
|
-
clearTimeout(timer);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* The whole ambient-recall CLI flow, as a pure function of (stdin, deps).
|
|
187
|
-
* Returns the string to print on stdout — either the hookSpecificOutput JSON or
|
|
188
|
-
* '' (nothing to inject). Never throws.
|
|
189
|
-
*/
|
|
190
|
-
export async function runAmbient(raw, deps) {
|
|
191
|
-
const payload = parseHookPayload(raw);
|
|
192
|
-
if (!payload)
|
|
193
|
-
return '';
|
|
194
|
-
const event = payload.hook_event_name ?? 'UserPromptSubmit';
|
|
195
|
-
const query = recallQueryFor(payload);
|
|
196
|
-
if (query === null)
|
|
197
|
-
return ''; // trivial / non-recallable event → skip before any brain call
|
|
198
|
-
// Auto-backoff (§6.3 guardrail 3): when the recent net balance is red, stop
|
|
199
|
-
// paying — checked after trivial-skip but before any brain call.
|
|
200
|
-
try {
|
|
201
|
-
if (deps.backoff && (await deps.backoff()))
|
|
202
|
-
return '';
|
|
203
|
-
}
|
|
204
|
-
catch {
|
|
205
|
-
// a broken backoff probe must not disable recall
|
|
206
|
-
}
|
|
207
|
-
let candidates = [];
|
|
208
|
-
try {
|
|
209
|
-
candidates = await withTimeout(deps.recall(query, event), deps.timeoutMs ?? 3000, []);
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
return ''; // recall failed → inject nothing, never block the turn
|
|
213
|
-
}
|
|
214
|
-
if (!Array.isArray(candidates) || candidates.length === 0)
|
|
215
|
-
return '';
|
|
216
|
-
// SessionStart/PreToolUse have no user prompt, so their trivial-skip is
|
|
217
|
-
// meaningless; pass the recall query itself so the gate's non-trivial branch runs.
|
|
218
|
-
const gateInput = event === 'UserPromptSubmit' ? (payload.prompt ?? query) : query;
|
|
219
|
-
// Antizipation braucht Timing, nicht nur Relevanz: dieselbe Lektion in jedem
|
|
220
|
-
// zweiten Turn ist kein Hinweis mehr, sondern Laerm. `decideRecall` bringt
|
|
221
|
-
// Dedupe, Ruhe-Budget und einen risikoabhaengigen Ausloesemoment mit; ohne
|
|
222
|
-
// Gedaechtnis-Deps bleibt das Verhalten exakt wie bisher (reiner Gate).
|
|
223
|
-
const memory = deps.loadMemory?.() ?? emptyMemory();
|
|
224
|
-
const decision = deps.loadMemory
|
|
225
|
-
? decideRecall(gateInput, candidates, memory, deps.gate)
|
|
226
|
-
: {
|
|
227
|
-
...selectInjectable(gateInput, candidates, deps.gate),
|
|
228
|
-
risk: 'normal',
|
|
229
|
-
suppressedDuplicates: [],
|
|
230
|
-
};
|
|
231
|
-
if (deps.saveMemory) {
|
|
232
|
-
try {
|
|
233
|
-
deps.saveMemory(commitInjection(memory, decision));
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
// Gedaechtnis ist Komfort, kein Muss — ein Schreibfehler darf den Turn nie stoppen
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (!decision.inject)
|
|
240
|
-
return '';
|
|
241
|
-
// Credit footer only where a recall can change a decision mid-flight.
|
|
242
|
-
const withFooter = event === 'UserPromptSubmit' || event === 'PreToolUse';
|
|
243
|
-
const context = formatContextBlock(decision.selected, withFooter);
|
|
244
|
-
const injectedTokens = estimateTokens(context); // includes the footer's own cost
|
|
245
|
-
try {
|
|
246
|
-
deps.onInject?.(injectedTokens, event);
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
// ledger is telemetry — never block the injection over it
|
|
250
|
-
}
|
|
251
|
-
return buildHookOutput(event, context);
|
|
252
|
-
}
|
|
253
|
-
//# sourceMappingURL=ambient-cli.js.map
|
|
1
|
+
import{decideRecall as p,commitInjection as h,emptyMemory as g,isTrivialPrompt as y,selectInjectable as d,estimateTokens as l}from"./ambient-recall.js";function b(e){const t=(e??"").trim();if(!t)return null;try{const r=JSON.parse(t);return r&&typeof r=="object"?r:null}catch{return null}}const v=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function S(e){const t=e.hook_event_name??"UserPromptSubmit";if(t==="SessionStart")return e.source==="compact"||e.source==="clear"?null:"session start: recent lessons, active pitfalls, and known failure modes for this project";if(t==="PreToolUse"){if(e.tool_name&&!v.has(e.tool_name))return null;const n=e.tool_input?.file_path;return typeof n!="string"||!n.trim()?null:`known pitfalls, past bugs and lessons for file ${n.trim()}`}if(t==="Stop")return null;const r=(e.prompt??"").trim();return!r||y(r)?null:r}const k=/\b(fixed|resolved|behoben|gefixt|root cause|the bug was|der fehler (lag|war)|ursache (war|gefunden)|now passes|tests? (are|is|sind) (green|grün)|deployed successfully)\b/i;function I(e){const t=(e.last_assistant_message??"").trim();return t.length<80||!k.test(t)?null:{action:(t.split(`
|
|
2
|
+
`).find(n=>n.trim().length>0)?.trim()??t).slice(0,200),outcome:"success",details:t.slice(0,500),severity:"minor"}}function O(e,t){const r=e.trim();if(l(r)<=t)return r;const n=Math.max(0,t*4),o=r.slice(0,n),i=o.lastIndexOf(" ");return(i>n*.6?o.slice(0,i):o).trimEnd()+"\u2026"}const w="(If this memory just saved you from a wrong path, record it: `npx @cachly-dev/mcp-server@latest ambient-credit <tokens-saved>`)";function x(e){const t=[];if(e.learnedAt){const r=new Date(e.learnedAt);Number.isNaN(r.getTime())||t.push(`am ${String(r.getDate()).padStart(2,"0")}.${String(r.getMonth()+1).padStart(2,"0")}.`)}if(e.files?.length){const r=e.files.slice(0,2).join(", ");t.push(`in ${r}${e.files.length>2?` (+${e.files.length-2})`:""}`)}return e.outcome==="failure"?t.push("ging damals schief"):e.outcome==="success"&&t.push("so ging es damals gut aus"),t.length?` \u21B3 ${t.join(" \xB7 ")}`:""}function _(e,t=!1){if(e.length===0)return"";const r=e.length===1&&e[0].summary.includes(`
|
|
3
|
+
`)?e[0].summary.trim():`\u{1F9E0} Relevant memory from your cachly brain (auto-recalled):
|
|
4
|
+
`+e.map(n=>{const o=x(n);return`- ${n.summary.trim()}`+(o?`
|
|
5
|
+
`+o:"")}).join(`
|
|
6
|
+
`);return t?`${r}
|
|
7
|
+
${w}`:r}function T(e,t){return t?JSON.stringify({hookSpecificOutput:{hookEventName:e,additionalContext:t}}):""}async function P(e,t,r){let n;const o=new Promise(i=>{n=setTimeout(()=>i(r),t)});try{return await Promise.race([e,o])}finally{n&&clearTimeout(n)}}async function $(e,t){const r=b(e);if(!r)return"";const n=r.hook_event_name??"UserPromptSubmit",o=S(r);if(o===null)return"";try{if(t.backoff&&await t.backoff())return""}catch{}let i=[];try{i=await P(t.recall(o,n),t.timeoutMs??3e3,[])}catch{return""}if(!Array.isArray(i)||i.length===0)return"";const c=n==="UserPromptSubmit"?r.prompt??o:o,a=t.loadMemory?.()??g(),s=t.loadMemory?p(c,i,a,t.gate):{...d(c,i,t.gate),risk:"normal",suppressedDuplicates:[]};if(t.saveMemory)try{t.saveMemory(h(a,s))}catch{}if(!s.inject)return"";const m=n==="UserPromptSubmit"||n==="PreToolUse",u=_(s.selected,m),f=l(u);try{t.onInject?.(f,n)}catch{}return T(n,u)}export{w as CREDIT_FOOTER,T as buildHookOutput,_ as formatContextBlock,x as formatProvenance,b as parseHookPayload,S as recallQueryFor,$ as runAmbient,I as stopObservation,O as truncateToTokens};
|
package/dist/src/ambient-deps.js
CHANGED
|
@@ -1,55 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// ambient-cli.ts (`runAmbient`) is dependency-injected on purpose so it can be
|
|
4
|
-
// unit-tested without a network. This module is the OTHER half of that seam:
|
|
5
|
-
// the one place that turns a raw `smart_recall` string reply into the
|
|
6
|
-
// `AmbientDeps.recall` candidates the gate understands — miss-detection,
|
|
7
|
-
// truncation and content-based IDs (PR #241 regression: the id must come from
|
|
8
|
-
// the summary, never a constant like 'ambient', or "same lesson twice" and
|
|
9
|
-
// "different lesson" become indistinguishable in `RecallMemory`).
|
|
10
|
-
//
|
|
11
|
-
// Previously this mapping lived inline at the `ambient-recall` CLI call site
|
|
12
|
-
// in index.ts. Moved here so the exact production wiring — not a hand-copied
|
|
13
|
-
// approximation of it — is what the seam test (SDK-003) exercises across
|
|
14
|
-
// multiple turns.
|
|
15
|
-
import { truncateToTokens } from './ambient-cli.js';
|
|
16
|
-
import { candidateIdFor } from './ambient-recall.js';
|
|
17
|
-
/** A brain reply that carries no usable lesson — same heuristic as smart_recall's own callers. */
|
|
18
|
-
const NO_LESSONS_RE = /No lessons found|no lessons|No matches found/i;
|
|
19
|
-
/** Below this length a `smart_recall` reply is boilerplate, not a real lesson. */
|
|
20
|
-
const MIN_LESSON_CHARS = 80;
|
|
21
|
-
/** Hard cap on how much of one recalled lesson may be injected (§6.2 budget). */
|
|
22
|
-
const MAX_LESSON_TOKENS = 500;
|
|
23
|
-
/**
|
|
24
|
-
* Builds the ONE production `AmbientDeps` used by the `ambient-recall` CLI
|
|
25
|
-
* entrypoint. This is the exact mapping that used to be inlined at the call
|
|
26
|
-
* site: a miss-detection heuristic, a token-truncated summary, and a
|
|
27
|
-
* content-derived candidate id — wrapped around whatever `smartRecall`
|
|
28
|
-
* implementation the caller passes in (production: `handleTool('smart_recall', ...)`;
|
|
29
|
-
* tests: a stub).
|
|
30
|
-
*/
|
|
31
|
-
export function buildAmbientDeps(opts) {
|
|
32
|
-
const { instanceId, smartRecall, loadMemory, saveMemory, backoff, onInject, timeoutMs } = opts;
|
|
33
|
-
return {
|
|
34
|
-
timeoutMs: timeoutMs ?? 3000, // §6.3 guardrail 4: never stall the prompt on a slow brain
|
|
35
|
-
// Ambient injection budget: one relevance-ranked briefing, hard-capped.
|
|
36
|
-
// smart_recall already returns its hits best-first, so the capped head is
|
|
37
|
-
// the high-signal part. Per-lesson structured candidates are a future slice.
|
|
38
|
-
gate: { topK: 1, maxTokens: MAX_LESSON_TOKENS },
|
|
39
|
-
loadMemory,
|
|
40
|
-
saveMemory,
|
|
41
|
-
backoff,
|
|
42
|
-
onInject,
|
|
43
|
-
recall: async (query) => {
|
|
44
|
-
if (!instanceId)
|
|
45
|
-
return [];
|
|
46
|
-
const text = String((await smartRecall(query)) ?? '');
|
|
47
|
-
const miss = text.length < MIN_LESSON_CHARS || NO_LESSONS_RE.test(text);
|
|
48
|
-
if (miss)
|
|
49
|
-
return [];
|
|
50
|
-
const summary = truncateToTokens(text, MAX_LESSON_TOKENS);
|
|
51
|
-
return [{ id: candidateIdFor(summary), summary, confidence: 0.9, score: 0.9 }];
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
//# sourceMappingURL=ambient-deps.js.map
|
|
1
|
+
import{truncateToTokens as f}from"./ambient-cli.js";import{candidateIdFor as S}from"./ambient-recall.js";const l=/No lessons found|no lessons|No matches found/i,N=80,n=500;function E(e){const{instanceId:s,smartRecall:r,loadMemory:c,saveMemory:i,backoff:a,onInject:m,timeoutMs:u}=e;return{timeoutMs:u??3e3,gate:{topK:1,maxTokens:n},loadMemory:c,saveMemory:i,backoff:a,onInject:m,recall:async d=>{if(!s)return[];const t=String(await r(d)??"");if(t.length<N||l.test(t))return[];const o=f(t,n);return[{id:S(o),summary:o,confidence:.9,score:.9}]}}}export{E as buildAmbientDeps};
|