chat-recall 0.0.1 → 0.4.8

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.
Files changed (94) hide show
  1. package/LICENSE +93 -0
  2. package/dist/auto-update.d.ts +52 -0
  3. package/dist/auto-update.d.ts.map +1 -0
  4. package/dist/auto-update.js +157 -0
  5. package/dist/auto-update.js.map +1 -0
  6. package/dist/cli.d.ts +6 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +26102 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/client-events.d.ts +11 -0
  11. package/dist/client-events.d.ts.map +1 -0
  12. package/dist/client-events.js +77 -0
  13. package/dist/client-events.js.map +1 -0
  14. package/dist/collector-migrate.d.ts +27 -0
  15. package/dist/collector-migrate.d.ts.map +1 -0
  16. package/dist/collector-migrate.js +60 -0
  17. package/dist/collector-migrate.js.map +1 -0
  18. package/dist/device-auth.d.ts +37 -0
  19. package/dist/device-auth.d.ts.map +1 -0
  20. package/dist/device-auth.js +161 -0
  21. package/dist/device-auth.js.map +1 -0
  22. package/dist/escalate.d.ts +99 -0
  23. package/dist/escalate.d.ts.map +1 -0
  24. package/dist/escalate.js +219 -0
  25. package/dist/escalate.js.map +1 -0
  26. package/dist/http.d.ts +14 -0
  27. package/dist/http.d.ts.map +1 -0
  28. package/dist/http.js +49 -0
  29. package/dist/http.js.map +1 -0
  30. package/dist/install-skills.d.ts +51 -0
  31. package/dist/install-skills.d.ts.map +1 -0
  32. package/dist/install-skills.js +223 -0
  33. package/dist/install-skills.js.map +1 -0
  34. package/dist/intent-drain.d.ts +25 -0
  35. package/dist/intent-drain.d.ts.map +1 -0
  36. package/dist/intent-drain.js +189 -0
  37. package/dist/intent-drain.js.map +1 -0
  38. package/dist/keyring.d.ts +28 -0
  39. package/dist/keyring.d.ts.map +1 -0
  40. package/dist/keyring.js +163 -0
  41. package/dist/keyring.js.map +1 -0
  42. package/dist/mcp.d.ts +8 -0
  43. package/dist/mcp.d.ts.map +1 -0
  44. package/dist/mcp.js +23665 -0
  45. package/dist/mcp.js.map +1 -0
  46. package/dist/project-tasks.d.ts +54 -0
  47. package/dist/project-tasks.d.ts.map +1 -0
  48. package/dist/project-tasks.js +204 -0
  49. package/dist/project-tasks.js.map +1 -0
  50. package/dist/repair.d.ts +73 -0
  51. package/dist/repair.d.ts.map +1 -0
  52. package/dist/repair.js +267 -0
  53. package/dist/repair.js.map +1 -0
  54. package/dist/self-restart.d.ts +91 -0
  55. package/dist/self-restart.d.ts.map +1 -0
  56. package/dist/self-restart.js +137 -0
  57. package/dist/self-restart.js.map +1 -0
  58. package/dist/service-installer.d.ts +32 -0
  59. package/dist/service-installer.d.ts.map +1 -0
  60. package/dist/service-installer.js +231 -0
  61. package/dist/service-installer.js.map +1 -0
  62. package/dist/sync-client.d.ts +234 -0
  63. package/dist/sync-client.d.ts.map +1 -0
  64. package/dist/sync-client.js +1985 -0
  65. package/dist/sync-client.js.map +1 -0
  66. package/dist/sync-ledger.d.ts +131 -0
  67. package/dist/sync-ledger.d.ts.map +1 -0
  68. package/dist/sync-ledger.js +366 -0
  69. package/dist/sync-ledger.js.map +1 -0
  70. package/dist/update-notice.d.ts +16 -0
  71. package/dist/update-notice.d.ts.map +1 -0
  72. package/dist/update-notice.js +107 -0
  73. package/dist/update-notice.js.map +1 -0
  74. package/dist/verify-deep.d.ts +79 -0
  75. package/dist/verify-deep.d.ts.map +1 -0
  76. package/dist/verify-deep.js +97 -0
  77. package/dist/verify-deep.js.map +1 -0
  78. package/dist/verify-repair.d.ts +21 -0
  79. package/dist/verify-repair.d.ts.map +1 -0
  80. package/dist/verify-repair.js +58 -0
  81. package/dist/verify-repair.js.map +1 -0
  82. package/dist/watch.js +21550 -0
  83. package/hooks/chat_recall_escalate_hook.sh +51 -0
  84. package/hooks/chat_recall_resume_hook.sh +69 -0
  85. package/hooks/chat_recall_save_hook.sh +254 -0
  86. package/hooks/chat_recall_wakeup_hook.sh +39 -0
  87. package/package.json +38 -4
  88. package/skills/chat-recall/SKILL.md +76 -0
  89. package/skills/chat-recall-changes/SKILL.md +50 -0
  90. package/skills/chat-recall-find/SKILL.md +64 -0
  91. package/skills/chat-recall-memory/SKILL.md +55 -0
  92. package/skills/chat-recall-project/SKILL.md +75 -0
  93. package/skills/chat-recall-resume/SKILL.md +62 -0
  94. package/README.md +0 -7
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Device Authorization Grant (RFC 8628) for `chat-recall login` — two flavors,
3
+ * picked by the target server's /api/capabilities `authProvider`:
4
+ *
5
+ * better-auth (betterAuthDeviceLogin) — the flow runs against the SERVER
6
+ * ITSELF (/api/auth/device/*). The token it returns is a
7
+ * better-auth session token; the server accepts it as a Bearer
8
+ * at /api/me, /api/teams* via its bearer plugin.
9
+ * keycloak (deviceLogin, legacy) — the flow runs against the realm the
10
+ * server advertises as `oidcIssuer`. Returns a realm access
11
+ * token, verified server-side via JWKS.
12
+ *
13
+ * Either way the CLI can't host a redirect URI, so: mint a user_code, tell the
14
+ * human to approve it in a browser, poll the token endpoint until they do.
15
+ * There is NO baked-in issuer or server: everything is discovered from the
16
+ * server being logged into. Self-host with AUTH_PROVIDER=none skips this flow
17
+ * entirely (token / no-auth login).
18
+ *
19
+ * The Keycloak chat-recall-web client enforces PKCE (S256), so that flavor
20
+ * carries a code_challenge on init and the matching code_verifier on the poll.
21
+ */
22
+ import { createHash, randomBytes } from 'node:crypto';
23
+ const b64url = (b) => b.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
24
+ /** Optional env fallback for the OIDC issuer. No hardcoded default — the value
25
+ * comes from the target server's /api/capabilities, `--issuer`, or this var. */
26
+ export const DEFAULT_ISSUER = process.env.CHAT_RECALL_OIDC_ISSUER || '';
27
+ export const DEFAULT_CLIENT_ID = 'chat-recall-web';
28
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
29
+ /**
30
+ * Run the full device flow. `onPrompt` receives the human-facing instructions
31
+ * (verification URL + code) so the caller controls presentation.
32
+ */
33
+ export async function deviceLogin(opts, onPrompt) {
34
+ const issuer = (opts.issuer || DEFAULT_ISSUER).replace(/\/+$/, '');
35
+ if (!issuer) {
36
+ throw new Error('No OIDC issuer configured for SSO login. Pass --issuer <url>, set '
37
+ + 'CHAT_RECALL_OIDC_ISSUER, or log in with a token instead: '
38
+ + '`chat-recall login <server-url> --token <token>` '
39
+ + '(self-host with AUTH_PROVIDER=none needs no token at all).');
40
+ }
41
+ const clientId = opts.clientId || DEFAULT_CLIENT_ID;
42
+ const scope = opts.scope || 'openid email profile';
43
+ // PKCE: the client requires S256. Verifier is held until the token poll.
44
+ const codeVerifier = b64url(randomBytes(32));
45
+ const codeChallenge = b64url(createHash('sha256').update(codeVerifier).digest());
46
+ const startRes = await fetch(`${issuer}/protocol/openid-connect/auth/device`, {
47
+ method: 'POST',
48
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
49
+ body: new URLSearchParams({ client_id: clientId, scope, code_challenge: codeChallenge, code_challenge_method: 'S256' }),
50
+ });
51
+ if (!startRes.ok) {
52
+ throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => '')}`);
53
+ }
54
+ const start = (await startRes.json());
55
+ onPrompt({
56
+ url: start.verification_uri_complete || start.verification_uri,
57
+ userCode: start.user_code,
58
+ verificationUri: start.verification_uri,
59
+ });
60
+ const tokenUrl = `${issuer}/protocol/openid-connect/token`;
61
+ const deadline = Date.now() + start.expires_in * 1000;
62
+ let interval = (start.interval ?? 5) * 1000;
63
+ while (Date.now() < deadline) {
64
+ await sleep(interval);
65
+ const res = await fetch(tokenUrl, {
66
+ method: 'POST',
67
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
68
+ body: new URLSearchParams({
69
+ // RFC 8628 §3.4 — the exact URN matters: 'grant-type' is part of it.
70
+ // The truncated 'urn:ietf:params:oauth:device_code' made Keycloak
71
+ // reject every poll with unsupported_grant_type (found by the first
72
+ // real new-user onboarding test, 2026-07-03).
73
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
74
+ device_code: start.device_code,
75
+ client_id: clientId,
76
+ code_verifier: codeVerifier,
77
+ }),
78
+ });
79
+ const body = (await res.json().catch(() => ({})));
80
+ if (res.ok && typeof body.access_token === 'string') {
81
+ return {
82
+ accessToken: body.access_token,
83
+ refreshToken: typeof body.refresh_token === 'string' ? body.refresh_token : undefined,
84
+ expiresIn: typeof body.expires_in === 'number' ? body.expires_in : 0,
85
+ };
86
+ }
87
+ // RFC 8628 §3.5: keep polling on these, back off on slow_down, fail otherwise.
88
+ const err = body.error;
89
+ if (err === 'authorization_pending')
90
+ continue;
91
+ if (err === 'slow_down') {
92
+ interval += 5000;
93
+ continue;
94
+ }
95
+ if (err === 'expired_token')
96
+ break;
97
+ throw new Error(`device-auth failed: ${String(err || `HTTP ${res.status}`)}`);
98
+ }
99
+ throw new Error('device-auth timed out — the code expired before approval.');
100
+ }
101
+ /** Client id the CLI presents to the server's embedded provider. A public
102
+ * identifier (not a secret) — the server pins it via validateClient. */
103
+ export const BETTER_AUTH_CLI_CLIENT_ID = 'chat-recall-cli';
104
+ /**
105
+ * Device flow against the server's EMBEDDED better-auth provider
106
+ * (AUTH_PROVIDER=better-auth): POST /api/auth/device/code, send the human to
107
+ * the server's own /device page, poll /api/auth/device/token. Same RFC 8628
108
+ * poll semantics as the Keycloak flavor; JSON bodies instead of form-encoded.
109
+ */
110
+ export async function betterAuthDeviceLogin(serverUrl, onPrompt) {
111
+ const base = serverUrl.replace(/\/+$/, '');
112
+ const startRes = await fetch(`${base}/api/auth/device/code`, {
113
+ method: 'POST',
114
+ headers: { 'content-type': 'application/json' },
115
+ body: JSON.stringify({ client_id: BETTER_AUTH_CLI_CLIENT_ID }),
116
+ });
117
+ if (!startRes.ok) {
118
+ throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => '')}`);
119
+ }
120
+ const start = (await startRes.json());
121
+ onPrompt({
122
+ url: start.verification_uri_complete || start.verification_uri,
123
+ userCode: start.user_code,
124
+ verificationUri: start.verification_uri,
125
+ });
126
+ const deadline = Date.now() + start.expires_in * 1000;
127
+ let interval = (start.interval ?? 5) * 1000;
128
+ while (Date.now() < deadline) {
129
+ await sleep(interval);
130
+ const res = await fetch(`${base}/api/auth/device/token`, {
131
+ method: 'POST',
132
+ headers: { 'content-type': 'application/json' },
133
+ body: JSON.stringify({
134
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
135
+ device_code: start.device_code,
136
+ client_id: BETTER_AUTH_CLI_CLIENT_ID,
137
+ }),
138
+ });
139
+ const body = (await res.json().catch(() => ({})));
140
+ if (res.ok && typeof body.access_token === 'string') {
141
+ return {
142
+ accessToken: body.access_token,
143
+ expiresIn: typeof body.expires_in === 'number' ? body.expires_in : 0,
144
+ };
145
+ }
146
+ // RFC 8628 §3.5 — same contract as the Keycloak flavor.
147
+ const err = body.error;
148
+ if (err === 'authorization_pending')
149
+ continue;
150
+ if (err === 'slow_down') {
151
+ interval += 5000;
152
+ continue;
153
+ }
154
+ if (err === 'expired_token' || err === 'access_denied') {
155
+ throw new Error(err === 'access_denied' ? 'device-auth denied in the browser.' : 'device-auth timed out — the code expired before approval.');
156
+ }
157
+ throw new Error(`device-auth failed: ${String(err || `HTTP ${res.status}`)}`);
158
+ }
159
+ throw new Error('device-auth timed out — the code expired before approval.');
160
+ }
161
+ //# sourceMappingURL=device-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-auth.js","sourceRoot":"","sources":["../src/device-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * `chat-recall escalate` — the WRITE half of the Context Engineering loop.
3
+ *
4
+ * The read half already exists: `chat-recall memory wake-up` (SessionStart
5
+ * hook) injects prior knowledge into a new session. This module closes the
6
+ * loop at the other end: at SessionEnd it extracts a finished session's
7
+ * learnings and asserts them into the temporal knowledge graph, so the NEXT
8
+ * wake-up already knows them. Three kinds of learning, three predicates:
9
+ *
10
+ * - decisions the agent announced → <project> → decided → <text>
11
+ * - corrections the user gave → <project> → user_corrected → <text>
12
+ * - how the session ended → <project> → session_outcome → <status: reason>
13
+ *
14
+ * Sources: decisions + outcome come from the server's own outcome analyzer
15
+ * (`GET /api/conversations/:id/outcome` — the same data recall_summary shows);
16
+ * corrections are extracted client-side from the synced transcript
17
+ * (`GET /api/conversations/:id?limit=0`) with the marker heuristics below.
18
+ * Writes go through `POST /api/kg/add` — the same endpoint recall_kg_add and
19
+ * recall_decision_record use — with `supersede: false` (learnings are
20
+ * multi-valued: a project accumulates many decisions) and `origin: 'asserted'`
21
+ * stamped server-side.
22
+ *
23
+ * Hook safety: this runs from a SessionEnd hook, so every "cannot proceed"
24
+ * condition (not logged in, server down, session not synced yet) is a dim
25
+ * note + a clean return — the CLI command exits 0. A hook must never break
26
+ * session end.
27
+ */
28
+ export interface ConvoMessage {
29
+ line?: number;
30
+ role: string;
31
+ content: string;
32
+ }
33
+ export interface SessionOutcome {
34
+ status?: string;
35
+ reason?: string;
36
+ decisions?: Array<{
37
+ text: string;
38
+ }>;
39
+ }
40
+ export interface Learning {
41
+ kind: 'decision' | 'correction' | 'outcome';
42
+ subject: string;
43
+ predicate: string;
44
+ object: string;
45
+ confidence: number;
46
+ }
47
+ /** Minimal server access the runner needs — cli.ts passes its own helpers. */
48
+ export interface EscalateDeps {
49
+ get<T>(path: string): Promise<T>;
50
+ getSoft<T>(path: string): Promise<{
51
+ status: number;
52
+ data: T | null;
53
+ message?: string;
54
+ }>;
55
+ post<T>(path: string, body: unknown): Promise<T>;
56
+ /** Informational output (goes to stdout via the CLI's own styling). */
57
+ log(line: string): void;
58
+ }
59
+ export interface EscalateOptions {
60
+ /** Explicit session id; wins over `latest`. */
61
+ sessionId?: string;
62
+ /** Resolve the most recent synced session of the cwd's project. */
63
+ latest?: boolean;
64
+ /** KG entity name for the facts. Default: basename of the session's project path, then of cwd. */
65
+ project?: string;
66
+ /** Extract and print without writing. */
67
+ dryRun?: boolean;
68
+ cwd: string;
69
+ }
70
+ /** Collapse whitespace and cut at a word boundary. */
71
+ export declare function clip(text: string, max?: number): string;
72
+ /**
73
+ * Corrections the user gave, in transcript order. The FIRST user message is
74
+ * skipped (the task statement cannot correct anything), noise carriers are
75
+ * skipped, and the result is deduplicated and capped.
76
+ */
77
+ export declare function extractCorrections(messages: ConvoMessage[], cap?: number): string[];
78
+ /**
79
+ * Assemble the full learning set for one session. Confidence 0.8 marks these
80
+ * as auto-extracted — below a deliberate recall_decision_record (1.0), above
81
+ * the KG's auto-mined guesses (0.5).
82
+ */
83
+ export declare function extractLearnings(input: {
84
+ project: string;
85
+ messages: ConvoMessage[];
86
+ outcome: SessionOutcome | null;
87
+ }): Learning[];
88
+ export interface EscalateResult {
89
+ sessionId: string;
90
+ written: number;
91
+ skippedExisting: number;
92
+ dryRun: boolean;
93
+ }
94
+ /**
95
+ * Resolve → extract → dedup → write. Returns null when there is nothing to do
96
+ * (not synced, no learnings) — the caller exits 0 either way.
97
+ */
98
+ export declare function runEscalate(deps: EscalateDeps, opts: EscalateOptions): Promise<EscalateResult | null>;
99
+ //# sourceMappingURL=escalate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalate.d.ts","sourceRoot":"","sources":["../src/escalate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,uEAAuE;IACvE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kGAAkG;IAClG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAyCD,sDAAsD;AACtD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAMpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,GAAG,SAAI,GAAG,MAAM,EAAE,CAsB9E;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;CAChC,GAAG,QAAQ,EAAE,CAqBb;AAgBD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAoF3G"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * `chat-recall escalate` — the WRITE half of the Context Engineering loop.
3
+ *
4
+ * The read half already exists: `chat-recall memory wake-up` (SessionStart
5
+ * hook) injects prior knowledge into a new session. This module closes the
6
+ * loop at the other end: at SessionEnd it extracts a finished session's
7
+ * learnings and asserts them into the temporal knowledge graph, so the NEXT
8
+ * wake-up already knows them. Three kinds of learning, three predicates:
9
+ *
10
+ * - decisions the agent announced → <project> → decided → <text>
11
+ * - corrections the user gave → <project> → user_corrected → <text>
12
+ * - how the session ended → <project> → session_outcome → <status: reason>
13
+ *
14
+ * Sources: decisions + outcome come from the server's own outcome analyzer
15
+ * (`GET /api/conversations/:id/outcome` — the same data recall_summary shows);
16
+ * corrections are extracted client-side from the synced transcript
17
+ * (`GET /api/conversations/:id?limit=0`) with the marker heuristics below.
18
+ * Writes go through `POST /api/kg/add` — the same endpoint recall_kg_add and
19
+ * recall_decision_record use — with `supersede: false` (learnings are
20
+ * multi-valued: a project accumulates many decisions) and `origin: 'asserted'`
21
+ * stamped server-side.
22
+ *
23
+ * Hook safety: this runs from a SessionEnd hook, so every "cannot proceed"
24
+ * condition (not logged in, server down, session not synced yet) is a dim
25
+ * note + a clean return — the CLI command exits 0. A hook must never break
26
+ * session end.
27
+ */
28
+ import { basename } from 'node:path';
29
+ // ── Extraction heuristics ────────────────────────────────────────────────────
30
+ /**
31
+ * A user message counts as a correction when it opens with a rejection /
32
+ * redirection marker, or contains an explicit "you got it wrong" phrase.
33
+ * Anchored-at-start patterns keep false positives low ("no" mid-sentence is
34
+ * usually not a correction; "No, use the pooler" is).
35
+ */
36
+ const CORRECTION_STARTS = [
37
+ /^no+\b[,.! ]/i,
38
+ /^(nope|wrong|incorrect|not quite|not right)\b/i,
39
+ /^that'?s (wrong|not right|not it|not what)/i,
40
+ /^(don'?t|do not|stop|undo|revert)\b/i,
41
+ /^(actually|instead)[, ]/i,
42
+ /^(why did you|you didn'?t|you did not|you forgot|you missed|you broke)\b/i,
43
+ ];
44
+ const CORRECTION_CONTAINS = [
45
+ /\bthat'?s not what i (asked|meant|wanted|said)\b/i,
46
+ /\byou (misunderstood|misread|ignored my|shouldn'?t have)\b/i,
47
+ /\bi (said|asked for|told you)\b.*\bnot\b/i,
48
+ ];
49
+ /**
50
+ * Transcript noise that must never be mined for corrections: slash-command
51
+ * carriers, caveat banners, interrupt markers, and giant pastes.
52
+ */
53
+ function isNoiseUserMessage(content) {
54
+ const c = content.trimStart();
55
+ return (c.length === 0 ||
56
+ c.length > 4000 ||
57
+ c.startsWith('Caveat:') ||
58
+ c.startsWith('[Request interrupted') ||
59
+ c.includes('<command-name>') ||
60
+ c.includes('<local-command') ||
61
+ c.startsWith('<system-reminder>'));
62
+ }
63
+ /** Collapse whitespace and cut at a word boundary. */
64
+ export function clip(text, max = 200) {
65
+ const flat = text.replace(/\s+/g, ' ').trim();
66
+ if (flat.length <= max)
67
+ return flat;
68
+ const cut = flat.slice(0, max);
69
+ const lastSpace = cut.lastIndexOf(' ');
70
+ return (lastSpace > max * 0.6 ? cut.slice(0, lastSpace) : cut) + '…';
71
+ }
72
+ /**
73
+ * Corrections the user gave, in transcript order. The FIRST user message is
74
+ * skipped (the task statement cannot correct anything), noise carriers are
75
+ * skipped, and the result is deduplicated and capped.
76
+ */
77
+ export function extractCorrections(messages, cap = 5) {
78
+ const out = [];
79
+ const seen = new Set();
80
+ let userTurns = 0;
81
+ for (const msg of messages) {
82
+ if (msg.role !== 'user')
83
+ continue;
84
+ userTurns++;
85
+ if (userTurns === 1)
86
+ continue; // the initial prompt is the task, not feedback
87
+ if (isNoiseUserMessage(msg.content))
88
+ continue;
89
+ const text = msg.content.trim();
90
+ const hit = CORRECTION_STARTS.some((re) => re.test(text)) ||
91
+ CORRECTION_CONTAINS.some((re) => re.test(text));
92
+ if (!hit)
93
+ continue;
94
+ const clipped = clip(text);
95
+ const key = clipped.toLowerCase();
96
+ if (seen.has(key))
97
+ continue;
98
+ seen.add(key);
99
+ out.push(clipped);
100
+ if (out.length >= cap)
101
+ break;
102
+ }
103
+ return out;
104
+ }
105
+ /** Outcome statuses worth remembering. Transient/unknown states are not learnings. */
106
+ const TERMINAL_STATUSES = new Set(['shipped', 'interrupted', 'abandoned']);
107
+ /**
108
+ * Assemble the full learning set for one session. Confidence 0.8 marks these
109
+ * as auto-extracted — below a deliberate recall_decision_record (1.0), above
110
+ * the KG's auto-mined guesses (0.5).
111
+ */
112
+ export function extractLearnings(input) {
113
+ const learnings = [];
114
+ const subject = input.project;
115
+ for (const d of (input.outcome?.decisions ?? []).slice(0, 8)) {
116
+ const text = clip(d.text);
117
+ if (!text)
118
+ continue;
119
+ learnings.push({ kind: 'decision', subject, predicate: 'decided', object: text, confidence: 0.8 });
120
+ }
121
+ for (const c of extractCorrections(input.messages)) {
122
+ learnings.push({ kind: 'correction', subject, predicate: 'user_corrected', object: c, confidence: 0.8 });
123
+ }
124
+ const status = input.outcome?.status;
125
+ if (status && TERMINAL_STATUSES.has(status)) {
126
+ const reason = input.outcome?.reason ? `: ${clip(input.outcome.reason, 140)}` : '';
127
+ learnings.push({ kind: 'outcome', subject, predicate: 'session_outcome', object: `${status}${reason}`, confidence: 0.8 });
128
+ }
129
+ return learnings;
130
+ }
131
+ /**
132
+ * Resolve → extract → dedup → write. Returns null when there is nothing to do
133
+ * (not synced, no learnings) — the caller exits 0 either way.
134
+ */
135
+ export async function runEscalate(deps, opts) {
136
+ // 1. Resolve the session id (+ the project entity name).
137
+ let sessionId = opts.sessionId;
138
+ let projectName = opts.project;
139
+ if (!sessionId && opts.latest) {
140
+ // The recent endpoint filters by LOGICAL project id (git:… / ws:…), the
141
+ // same resolution every other cwd-scoped command uses.
142
+ const { resolveProjectId } = await import('@chat-recall/engine/core/project-resolver.js');
143
+ const resolved = resolveProjectId(opts.cwd);
144
+ const pid = resolved && resolved.source !== 'ignored' ? resolved.id : '';
145
+ const qs = new URLSearchParams({ limit: '1' });
146
+ if (pid)
147
+ qs.set('project', pid);
148
+ const recent = await deps.get(`/api/conversations/recent?${qs.toString()}`);
149
+ const row = recent.sessions?.[0];
150
+ if (!row) {
151
+ deps.log(`escalate: no synced session found for this project${pid ? ` (${pid})` : ''} — nothing to do.`);
152
+ return null;
153
+ }
154
+ sessionId = row.sessionId;
155
+ if (!projectName && row.projectPath)
156
+ projectName = basename(row.projectPath);
157
+ }
158
+ if (!sessionId)
159
+ throw new Error('pass a session id or --latest');
160
+ if (!projectName)
161
+ projectName = basename(opts.cwd);
162
+ // 2. Fetch the transcript + the server-side outcome analysis.
163
+ const convo = await deps.getSoft(`/api/conversations/${encodeURIComponent(sessionId)}?limit=0`);
164
+ if (!convo.data || convo.data.messages.length === 0) {
165
+ deps.log(`escalate: session ${sessionId} not synced yet${convo.message ? ` (${convo.message})` : ''} — nothing to do.`);
166
+ return null;
167
+ }
168
+ const outcomeSoft = await deps.getSoft(`/api/conversations/${encodeURIComponent(sessionId)}/outcome`);
169
+ const outcome = outcomeSoft.data;
170
+ // 3. Extract.
171
+ const learnings = extractLearnings({ project: projectName, messages: convo.data.messages, outcome });
172
+ if (learnings.length === 0) {
173
+ deps.log(`escalate: no learnings found in session ${sessionId.slice(0, 8)}… — nothing to escalate.`);
174
+ return null;
175
+ }
176
+ // 4. Dedup against facts already in the graph, so a re-run (or a hook firing
177
+ // twice) does not double-write. The timeline lookup is best-effort: if it
178
+ // fails we still write (supersede:false tolerates rare duplicates).
179
+ const existing = new Set();
180
+ try {
181
+ const tl = await deps.get(`/api/kg/timeline?entity=${encodeURIComponent(projectName)}&limit=500`);
182
+ for (const e of tl.entries ?? []) {
183
+ if (e.current)
184
+ existing.add(`${e.predicate}${e.object}`.toLowerCase());
185
+ }
186
+ }
187
+ catch { /* best-effort */ }
188
+ const fresh = learnings.filter((l) => !existing.has(`${l.predicate}${l.object}`.toLowerCase()));
189
+ const skippedExisting = learnings.length - fresh.length;
190
+ // 5. Print (and in dry-run: stop here).
191
+ for (const l of fresh) {
192
+ deps.log(` [${l.kind}] ${l.subject} → ${l.predicate} → ${l.object}`);
193
+ }
194
+ if (skippedExisting > 0)
195
+ deps.log(` (${skippedExisting} already in the knowledge graph — skipped)`);
196
+ if (opts.dryRun) {
197
+ deps.log(`escalate (dry-run): ${fresh.length} learning(s) extracted from ${sessionId.slice(0, 8)}… — nothing written.`);
198
+ return { sessionId, written: 0, skippedExisting, dryRun: true };
199
+ }
200
+ // 6. Write each fresh learning as an asserted, session-attributed triple.
201
+ let written = 0;
202
+ for (const l of fresh) {
203
+ await deps.post('/api/kg/add', {
204
+ subject: l.subject,
205
+ predicate: l.predicate,
206
+ object: l.object,
207
+ confidence: l.confidence,
208
+ source_session: sessionId,
209
+ // Learnings are multi-valued: a project accumulates decisions and
210
+ // corrections. Without this, each write would supersede the previous
211
+ // fact with the same (subject, predicate).
212
+ supersede: false,
213
+ });
214
+ written++;
215
+ }
216
+ deps.log(`escalate: wrote ${written} learning(s) from session ${sessionId.slice(0, 8)}… to the knowledge graph.`);
217
+ return { sessionId, written, skippedExisting, dryRun: false };
218
+ }
219
+ //# sourceMappingURL=escalate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalate.js","sourceRoot":"","sources":["../src/escalate.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** This CLI's own version — the one the server compares against. */
2
+ export declare function cliVersion(): string;
3
+ /**
4
+ * Identity headers on every request this CLI makes to its server.
5
+ *
6
+ * The server stamps them onto the device row (see the auth middleware's device
7
+ * heartbeat), which is what makes "this machine last synced 6 days ago and runs
8
+ * 0.3.1" answerable at all. Before this the server knew a device token existed
9
+ * and nothing else — so a machine on a CLI too old to self-update stayed stale
10
+ * indefinitely and no UI could say so. Two short, non-identifying values.
11
+ */
12
+ export declare function clientHeaders(): Record<string, string>;
13
+ export declare function fetchWithTimeout(url: string, init?: RequestInit, ms?: number): Promise<Response>;
14
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAwBA,oEAAoE;AACpE,wBAAgB,UAAU,IAAI,MAAM,CAAwB;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKtD;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,EACtB,EAAE,SAAS,GACV,OAAO,CAAC,QAAQ,CAAC,CAMnB"}
package/dist/http.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * fetch() with a hard timeout.
3
+ *
4
+ * Node's global `fetch` (undici) has NO caller-facing default timeout — a
5
+ * connected-but-silent peer (half-open TCP, a hung reverse proxy, a wedged
6
+ * server) makes the promise hang for undici's internal ~300s, or effectively
7
+ * forever from the caller's view. The collector's upload path already guards
8
+ * against this with an AbortController; every OTHER request the daemon makes
9
+ * (capabilities handshake, tenant-config pulls, intent drain, code-index push,
10
+ * auto-update checks) must too — otherwise ONE stalled endpoint wedges the whole
11
+ * serialized sync tick. This is the single helper they all use.
12
+ */
13
+ import { readFileSync } from 'node:fs';
14
+ let CLI_VERSION = '0.0.0';
15
+ try {
16
+ CLI_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version || CLI_VERSION;
17
+ }
18
+ catch {
19
+ try {
20
+ if (typeof __CLI_VERSION__ === 'string')
21
+ CLI_VERSION = __CLI_VERSION__;
22
+ }
23
+ catch { /* keep default */ }
24
+ }
25
+ /** This CLI's own version — the one the server compares against. */
26
+ export function cliVersion() { return CLI_VERSION; }
27
+ /**
28
+ * Identity headers on every request this CLI makes to its server.
29
+ *
30
+ * The server stamps them onto the device row (see the auth middleware's device
31
+ * heartbeat), which is what makes "this machine last synced 6 days ago and runs
32
+ * 0.3.1" answerable at all. Before this the server knew a device token existed
33
+ * and nothing else — so a machine on a CLI too old to self-update stayed stale
34
+ * indefinitely and no UI could say so. Two short, non-identifying values.
35
+ */
36
+ export function clientHeaders() {
37
+ return {
38
+ 'x-chat-recall-cli': CLI_VERSION,
39
+ 'x-chat-recall-os': `${process.platform}-${process.arch}`,
40
+ };
41
+ }
42
+ export function fetchWithTimeout(url, init = {}, ms = 15_000) {
43
+ // Headers first so an explicit caller value always wins.
44
+ const headers = new Headers(clientHeaders());
45
+ new Headers(init.headers ?? {}).forEach((v, k) => headers.set(k, v));
46
+ // Respect an explicit caller signal if given; otherwise arm a timeout.
47
+ return fetch(url, { ...init, headers, signal: init.signal ?? AbortSignal.timeout(ms) });
48
+ }
49
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAKvC,IAAI,WAAW,GAAG,OAAO,CAAC;AAC1B,IAAI,CAAC;IACH,WAAW,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAA0B,CAAC,OAAO,IAAI,WAAW,CAAC;AAChJ,CAAC;AAAC,MAAM,CAAC;IACP,IAAI,CAAC;QAAC,IAAI,OAAO,eAAe,KAAK,QAAQ;YAAE,WAAW,GAAG,eAAe,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;AAC9G,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,KAAa,OAAO,WAAW,CAAC,CAAC,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,mBAAmB,EAAE,WAAW;QAChC,kBAAkB,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,OAAoB,EAAE,EACtB,EAAE,GAAG,MAAM;IAEX,yDAAyD;IACzD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,uEAAuE;IACvE,OAAO,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,51 @@
1
+ /** Bundled skills source dir: <package-root>/skills (dist/install-skills.js → ..). */
2
+ export declare function skillsSourceDir(): string;
3
+ /** The skill directory names we ship (any dir under skills/ containing SKILL.md). */
4
+ export declare function bundledSkillNames(): string[];
5
+ export interface SkillTarget {
6
+ id: string;
7
+ label: string;
8
+ dir: string;
9
+ available: boolean;
10
+ }
11
+ /** Per-tool skills dir. agy keeps its own (~/.gemini/antigravity-cli/skills) AND
12
+ * we also cover ~/.agents/skills (tool-neutral) so any future consumer sees them. */
13
+ export declare function skillTargets(): SkillTarget[];
14
+ export interface InstallResult {
15
+ version: string;
16
+ perTarget: Array<{
17
+ id: string;
18
+ label: string;
19
+ dir: string;
20
+ installed: string[];
21
+ skippedUserOwned: string[];
22
+ available: boolean;
23
+ }>;
24
+ }
25
+ /** Install (or update) the bundled skills into every target tool.
26
+ * @param onlyAvailable install only to tools detected on this machine (default true). */
27
+ export declare function installSkills(opts?: {
28
+ onlyAvailable?: boolean;
29
+ }): InstallResult;
30
+ export interface UninstallResult {
31
+ perTarget: Array<{
32
+ id: string;
33
+ label: string;
34
+ removed: string[];
35
+ }>;
36
+ }
37
+ /** Remove only chat-recall-managed skills (identified by the marker). */
38
+ export declare function uninstallSkills(): UninstallResult;
39
+ /** True if any AVAILABLE tool is missing a bundled skill or has an out-of-date
40
+ * version. The MCP boot uses this to refresh only when needed (cheap: stat a
41
+ * marker per skill), so it doesn't churn the disk on every tool launch. */
42
+ export declare function skillsNeedRefresh(): boolean;
43
+ /** For `doctor`: which tools have the current skill version installed. */
44
+ export declare function skillStatus(): Array<{
45
+ id: string;
46
+ label: string;
47
+ available: boolean;
48
+ installed: number;
49
+ version: string | null;
50
+ }>;
51
+ //# sourceMappingURL=install-skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skills.d.ts","sourceRoot":"","sources":["../src/install-skills.ts"],"names":[],"mappings":"AA6BA,sFAAsF;AACtF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAQD,qFAAqF;AACrF,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAO5C;AAED,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;CAAE;AA+C5F;sFACsF;AACtF,wBAAgB,YAAY,IAAI,WAAW,EAAE,CAa5C;AAOD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACnI;AAED;0FAC0F;AAC1F,wBAAgB,aAAa,CAAC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,aAAa,CAwBnF;AAED,MAAM,WAAW,eAAe;IAAG,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAAE;AAExG,yEAAyE;AACzE,wBAAgB,eAAe,IAAI,eAAe,CAkBjD;AAED;;4EAE4E;AAC5E,wBAAgB,iBAAiB,IAAI,OAAO,CAa3C;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,IAAI,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAcjI"}