@mickey-kras/hindsight-memory-router-coding-agents 0.8.9 → 0.8.14
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/antigravity-hook.js +70 -24
- package/dist/antigravity-statusline.js +60 -14
- package/dist/antigravity-stop-hook.js +70 -24
- package/dist/claude-hook.js +70 -24
- package/dist/claude-sessionstart-hook.js +70 -24
- package/dist/claude-stop-hook.js +70 -24
- package/dist/cline.js +70 -24
- package/dist/codebuddy-hook.js +70 -24
- package/dist/codebuddy-sessionstart-hook.js +70 -24
- package/dist/codebuddy-stop-hook.js +70 -24
- package/dist/codex-hook.js +70 -24
- package/dist/codex-sessionstart-hook.js +70 -24
- package/dist/codex-stop-hook.js +70 -24
- package/dist/copilot-hook.js +70 -24
- package/dist/copilot-sessionstart-hook.js +70 -24
- package/dist/copilot-stop-hook.js +70 -24
- package/dist/cursor-hook.js +70 -24
- package/dist/cursor-sessionstart-hook.js +70 -24
- package/dist/cursor-stop-hook.js +70 -24
- package/dist/dcode-hook.js +70 -24
- package/dist/dcode-sessionstart-hook.js +70 -24
- package/dist/dcode-stop-hook.js +70 -24
- package/dist/deepen.js +70 -68
- package/dist/devin-hook.js +70 -24
- package/dist/devin-sessionstart-hook.js +70 -24
- package/dist/devin-stop-hook.js +70 -24
- package/dist/droid-hook.js +70 -24
- package/dist/droid-sessionstart-hook.js +70 -24
- package/dist/droid-stop-hook.js +70 -24
- package/dist/dsh.js +70 -24
- package/dist/grok-hook.js +70 -24
- package/dist/grok-sessionstart-hook.js +70 -24
- package/dist/grok-stop-hook.js +70 -24
- package/dist/hindsight-seed.js +60 -14
- package/dist/index.js +70 -24
- package/dist/installer.js +70 -21
- package/dist/kilo.js +70 -24
- package/dist/kimi-hook.js +70 -24
- package/dist/kimi-sessionstart-hook.js +70 -24
- package/dist/kimi-stop-hook.js +70 -24
- package/dist/mcp-server.js +79 -24
- package/dist/opencode2.js +70 -24
- package/dist/pi.js +70 -24
- package/dist/prime-agent.js +70 -24
- package/dist/qwen-hook.js +70 -24
- package/dist/qwen-sessionstart-hook.js +70 -24
- package/dist/qwen-stop-hook.js +70 -24
- package/dist/status.js +70 -24
- package/dist/traecode-hook.js +70 -24
- package/dist/traecode-sessionstart-hook.js +70 -24
- package/dist/traecode-stop-hook.js +70 -24
- package/dist/workbuddy-hook.js +70 -24
- package/dist/workbuddy-sessionstart-hook.js +70 -24
- package/dist/workbuddy-stop-hook.js +70 -24
- package/dist/zcode-hook.js +70 -24
- package/dist/zcode-sessionstart-hook.js +70 -24
- package/dist/zcode-stop-hook.js +70 -24
- package/npm-shrinkwrap.json +257 -711
- package/package.json +8 -5
package/dist/grok-stop-hook.js
CHANGED
|
@@ -36,12 +36,10 @@ function requireBank(access, bank, operation) {
|
|
|
36
36
|
}
|
|
37
37
|
function classifyOperation(method, suffix) {
|
|
38
38
|
if (method === "POST" && ["/memories/recall", "/reflect"].includes(suffix)) return "read";
|
|
39
|
-
if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|
|
|
40
|
-
suffix
|
|
41
|
-
))
|
|
39
|
+
if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|pages)(?:\/[^/]+)*$/.test(suffix))
|
|
42
40
|
return "read";
|
|
43
41
|
if (method === "GET" && suffix === "") return "read";
|
|
44
|
-
if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|
|
|
42
|
+
if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|pages|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
|
|
45
43
|
suffix
|
|
46
44
|
))
|
|
47
45
|
return "write";
|
|
@@ -379,6 +377,54 @@ var RouterTransport = class {
|
|
|
379
377
|
// ../../coding-agents/human-session.ts
|
|
380
378
|
import { createHash as createHash2 } from "crypto";
|
|
381
379
|
import { createRequire } from "module";
|
|
380
|
+
|
|
381
|
+
// src/harness/names.ts
|
|
382
|
+
var HARNESS_NAMES = [
|
|
383
|
+
"opencode",
|
|
384
|
+
// opencode v2 (binary `opencode2`) — a ground-up rewrite of the plugin API, loaded from the
|
|
385
|
+
// package's root index.js (src/opencode2.ts). Like opencode it has NO hook binary.
|
|
386
|
+
"opencode2",
|
|
387
|
+
// Kilo CLI is an opencode fork loaded as a persistent plugin (src/kilo.ts), so like opencode it
|
|
388
|
+
// has NO hook binary — deliberately absent from HOOK_BINS below.
|
|
389
|
+
"kilo",
|
|
390
|
+
// Cline CLI loads dist/cline.js through its native plugin manager; file hooks cannot inject.
|
|
391
|
+
"cline-cli",
|
|
392
|
+
// DeepSeek Harness loads dist/dsh.js as a native Cordis plugin (src/dsh.ts). Its Claude Code /
|
|
393
|
+
// Codex hook bridges are optional packages, so there is no hook binary to install either.
|
|
394
|
+
"dsh",
|
|
395
|
+
// pi loads dist/pi.js as an extension (src/pi.ts), and Prime Agent — a fork of pi — loads
|
|
396
|
+
// dist/prime-agent.js the same way (src/prime-agent.ts). Neither has a hook binary.
|
|
397
|
+
"pi",
|
|
398
|
+
"prime-agent",
|
|
399
|
+
"claude-code",
|
|
400
|
+
"cursor-cli",
|
|
401
|
+
"codex",
|
|
402
|
+
"dcode",
|
|
403
|
+
"antigravity-cli",
|
|
404
|
+
"devin-cli",
|
|
405
|
+
"copilot-cli",
|
|
406
|
+
"grok-build",
|
|
407
|
+
"qwen-code",
|
|
408
|
+
// Factory Droid is a per-prompt HOOK host: the installer wires ~/.factory/hooks.json and the
|
|
409
|
+
// stdio MCP registration in ~/.factory/mcp.json (see src/installer.ts).
|
|
410
|
+
"factory-droid",
|
|
411
|
+
// ZCode is a per-prompt HOOK host too, registered in its own CLI config
|
|
412
|
+
// (~/.zcode/cli/config.json) under `hooks.events` — never the user's real Claude Code settings.
|
|
413
|
+
"zcode",
|
|
414
|
+
// TraeCode is a per-prompt HOOK host registered in ~/.trae-cn/hooks.json (see src/installer.ts).
|
|
415
|
+
"traecode",
|
|
416
|
+
"kimi-code",
|
|
417
|
+
// WorkBuddy is a per-prompt HOOK host too: the installer wires ~/.workbuddy/settings.json and
|
|
418
|
+
// the stdio MCP registration in ~/.workbuddy/mcp.json (see src/installer.ts).
|
|
419
|
+
"workbuddy",
|
|
420
|
+
// CodeBuddy Code is the SAME @genie/agent-cli HOOK host, one product config apart (WorkBuddy only
|
|
421
|
+
// renames the home folder): the installer wires ~/.codebuddy/settings.json and the stdio MCP
|
|
422
|
+
// registration in CodeBuddy's priority-chained MCP file (see src/installer.ts). The CLI
|
|
423
|
+
// writes WorkBuddy's transcript format; the CodeBuddy IDE has a reader of its own.
|
|
424
|
+
"codebuddy"
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
// ../../coding-agents/human-session.ts
|
|
382
428
|
var HUMAN_TOKEN_PATTERN = /^mrh_[0-9a-f]{32}_[0-9a-f]{64}$/;
|
|
383
429
|
var HumanLoginRequiredError = class extends AccessDeniedError {
|
|
384
430
|
constructor() {
|
|
@@ -404,14 +450,14 @@ function humanRouterUrl(value) {
|
|
|
404
450
|
if (url.search || url.hash) throw new HumanLoginRequiredError();
|
|
405
451
|
return canonical2;
|
|
406
452
|
}
|
|
407
|
-
function secureEntry(routerUrl, credentialRef) {
|
|
453
|
+
function secureEntry(routerUrl, credentialRef, harness) {
|
|
408
454
|
try {
|
|
409
|
-
if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef)) {
|
|
455
|
+
if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef) || !HARNESS_NAMES.includes(harness)) {
|
|
410
456
|
throw new HumanLoginRequiredError();
|
|
411
457
|
}
|
|
412
458
|
const { Entry: NativeEntry } = createRequire(import.meta.url)("@napi-rs/keyring");
|
|
413
|
-
const service = `hindsight-memory-router:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
|
|
414
|
-
return new NativeEntry(service, credentialRef
|
|
459
|
+
const service = `hindsight-memory-router:human-v2:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
|
|
460
|
+
return new NativeEntry(service, `${harness}:${credentialRef}`, { linux: { store: "secret-service" } });
|
|
415
461
|
} catch {
|
|
416
462
|
throw new HumanLoginRequiredError();
|
|
417
463
|
}
|
|
@@ -429,9 +475,9 @@ function validateHumanSession(value, routerUrl, allowExpired = false) {
|
|
|
429
475
|
routerUrl: session.routerUrl
|
|
430
476
|
};
|
|
431
477
|
}
|
|
432
|
-
function readHumanSession(routerUrl, credentialRef) {
|
|
478
|
+
function readHumanSession(routerUrl, credentialRef, harness) {
|
|
433
479
|
try {
|
|
434
|
-
const password = secureEntry(routerUrl, credentialRef).getPassword();
|
|
480
|
+
const password = secureEntry(routerUrl, credentialRef, harness).getPassword();
|
|
435
481
|
if (!password || password.length > 4096) throw new HumanLoginRequiredError();
|
|
436
482
|
return validateHumanSession(JSON.parse(password), routerUrl);
|
|
437
483
|
} catch {
|
|
@@ -443,11 +489,11 @@ function readHumanSession(routerUrl, credentialRef) {
|
|
|
443
489
|
function harnessTransport(harness) {
|
|
444
490
|
return transportForConfig(harness, loadManagedConfig(process.env, harness));
|
|
445
491
|
}
|
|
446
|
-
function transportForConfig(harness, { config: config2, principal }) {
|
|
492
|
+
function transportForConfig(harness, { config: config2, principal, principalId }) {
|
|
447
493
|
if (principal.human) {
|
|
448
494
|
const routerUrl = humanRouterUrl(config2.routerUrl);
|
|
449
495
|
const reference = principal.human.credentialRef;
|
|
450
|
-
let session = readHumanSession(routerUrl, reference);
|
|
496
|
+
let session = readHumanSession(routerUrl, reference, principalId);
|
|
451
497
|
return new RouterTransport({
|
|
452
498
|
routerUrl,
|
|
453
499
|
access: principal,
|
|
@@ -455,7 +501,7 @@ function transportForConfig(harness, { config: config2, principal }) {
|
|
|
455
501
|
tokenPattern: HUMAN_TOKEN_PATTERN,
|
|
456
502
|
unauthorizedError: () => new HumanLoginRequiredError(),
|
|
457
503
|
token: () => {
|
|
458
|
-
session = readHumanSession(routerUrl, reference);
|
|
504
|
+
session = readHumanSession(routerUrl, reference, principalId);
|
|
459
505
|
return session.token;
|
|
460
506
|
}
|
|
461
507
|
});
|
|
@@ -1214,7 +1260,7 @@ var PLUGIN_GOVERNED_BANK_FIELDS = [
|
|
|
1214
1260
|
];
|
|
1215
1261
|
|
|
1216
1262
|
// package.json
|
|
1217
|
-
var version = "0.8.
|
|
1263
|
+
var version = "0.8.14";
|
|
1218
1264
|
|
|
1219
1265
|
// src/core/util.ts
|
|
1220
1266
|
import { accessSync, constants } from "fs";
|
|
@@ -1728,7 +1774,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1728
1774
|
*/
|
|
1729
1775
|
async tree() {
|
|
1730
1776
|
if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
|
|
1731
|
-
const r = await this.req("GET", this.bankUrl("/
|
|
1777
|
+
const r = await this.req("GET", this.bankUrl("/pages/tree"));
|
|
1732
1778
|
if (await this.pagesUnsupported(r)) throw new KnowledgePagesUnavailableError();
|
|
1733
1779
|
if (r.status === 404) return [];
|
|
1734
1780
|
this.knowledgePagesSupported = true;
|
|
@@ -1776,7 +1822,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1776
1822
|
if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
|
|
1777
1823
|
const r = await this.req(
|
|
1778
1824
|
"GET",
|
|
1779
|
-
this.bankUrl(`/
|
|
1825
|
+
this.bankUrl(`/pages/${encodeURIComponent(pageId)}`)
|
|
1780
1826
|
);
|
|
1781
1827
|
if (r.status === 404) throw new Error(`knowledge page not found: ${pageId}`);
|
|
1782
1828
|
return shapePage(await r.json());
|
|
@@ -1789,7 +1835,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1789
1835
|
const q = `?q=${encodeURIComponent(query)}&limit=${opts.limit ?? this.pageSearchLimit}`;
|
|
1790
1836
|
const r = await this.req(
|
|
1791
1837
|
"GET",
|
|
1792
|
-
this.bankUrl(`/
|
|
1838
|
+
this.bankUrl(`/pages/search${q}`),
|
|
1793
1839
|
void 0,
|
|
1794
1840
|
[],
|
|
1795
1841
|
opts.timeoutMs
|
|
@@ -1808,7 +1854,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1808
1854
|
/**
|
|
1809
1855
|
* Seed the fixed page taxonomy as knowledge-base pages at the tree root, idempotently.
|
|
1810
1856
|
*
|
|
1811
|
-
* Matched by NAME, not id: `/
|
|
1857
|
+
* Matched by NAME, not id: `/pages` mints its own `kp-…` id, so a stable
|
|
1812
1858
|
* client-chosen id isn't available to match on (unlike the old mental-model path, which keyed
|
|
1813
1859
|
* off a slug). Names are unique per folder server-side, which makes them a sound key.
|
|
1814
1860
|
*
|
|
@@ -1847,7 +1893,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1847
1893
|
trigger: pageTriggerFor(pageTrigger, this.bank, page.name)
|
|
1848
1894
|
};
|
|
1849
1895
|
if (!hit) {
|
|
1850
|
-
const r = await this.req("POST", this.bankUrl("/
|
|
1896
|
+
const r = await this.req("POST", this.bankUrl("/pages"), body, [409]);
|
|
1851
1897
|
if (await this.pagesUnsupported(r)) {
|
|
1852
1898
|
this.log(
|
|
1853
1899
|
`[bank] knowledge pages unavailable on ${this.apiUrl}; continuing without pages`
|
|
@@ -1874,7 +1920,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1874
1920
|
if (triggerDrift) patch.trigger = pageTriggerPatch(body.trigger);
|
|
1875
1921
|
const r = await this.req(
|
|
1876
1922
|
"PATCH",
|
|
1877
|
-
this.bankUrl(`/
|
|
1923
|
+
this.bankUrl(`/pages/nodes/${encodeURIComponent(hit.id)}`),
|
|
1878
1924
|
patch
|
|
1879
1925
|
);
|
|
1880
1926
|
if (await this.pagesUnsupported(r)) {
|
|
@@ -1928,7 +1974,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1928
1974
|
if (!patch.trigger && !patch.source_query) continue;
|
|
1929
1975
|
const r = await this.req(
|
|
1930
1976
|
"PATCH",
|
|
1931
|
-
this.bankUrl(`/
|
|
1977
|
+
this.bankUrl(`/pages/nodes/${encodeURIComponent(page.id)}`),
|
|
1932
1978
|
patch
|
|
1933
1979
|
);
|
|
1934
1980
|
if (r.status === 404) continue;
|
|
@@ -1957,7 +2003,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1957
2003
|
let pageId = args.relatesToPageId;
|
|
1958
2004
|
if (!pageId) {
|
|
1959
2005
|
const folderId = await this.ensureFolder("Initiatives");
|
|
1960
|
-
const r = await this.req("POST", this.bankUrl("/
|
|
2006
|
+
const r = await this.req("POST", this.bankUrl("/pages"), {
|
|
1961
2007
|
name: args.title,
|
|
1962
2008
|
source_query: `Summarize the "${args.title}" initiative: what is being built or changed and why, and its current state \u2014 drawn from the project's memory.` + pageScopeRule(this.project),
|
|
1963
2009
|
parent_id: folderId,
|
|
@@ -1985,7 +2031,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1985
2031
|
/** Find a root folder by name (case-insensitive) or create it; returns its id. Fail-open to undefined. */
|
|
1986
2032
|
async ensureFolder(name) {
|
|
1987
2033
|
try {
|
|
1988
|
-
const tree = await (await this.req("GET", this.bankUrl("/
|
|
2034
|
+
const tree = await (await this.req("GET", this.bankUrl("/pages/tree"))).json();
|
|
1989
2035
|
const hit = (tree.roots || []).find(
|
|
1990
2036
|
(n) => n.kind === "folder" && (n.name || "").toLowerCase() === name.toLowerCase()
|
|
1991
2037
|
);
|
|
@@ -1994,7 +2040,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1994
2040
|
if (error instanceof AccessDeniedError) throw error;
|
|
1995
2041
|
}
|
|
1996
2042
|
try {
|
|
1997
|
-
const r = await this.req("POST", this.bankUrl("/
|
|
2043
|
+
const r = await this.req("POST", this.bankUrl("/pages/folders"), { name });
|
|
1998
2044
|
return (await r.json()).id;
|
|
1999
2045
|
} catch (error) {
|
|
2000
2046
|
if (error instanceof AccessDeniedError) throw error;
|
package/dist/hindsight-seed.js
CHANGED
|
@@ -27,12 +27,10 @@ function requireBank(access, bank, operation) {
|
|
|
27
27
|
}
|
|
28
28
|
function classifyOperation(method, suffix) {
|
|
29
29
|
if (method === "POST" && ["/memories/recall", "/reflect"].includes(suffix)) return "read";
|
|
30
|
-
if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|
|
|
31
|
-
suffix
|
|
32
|
-
))
|
|
30
|
+
if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|pages)(?:\/[^/]+)*$/.test(suffix))
|
|
33
31
|
return "read";
|
|
34
32
|
if (method === "GET" && suffix === "") return "read";
|
|
35
|
-
if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|
|
|
33
|
+
if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|pages|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
|
|
36
34
|
suffix
|
|
37
35
|
))
|
|
38
36
|
return "write";
|
|
@@ -354,6 +352,54 @@ var RouterTransport = class {
|
|
|
354
352
|
// ../../coding-agents/human-session.ts
|
|
355
353
|
import { createHash as createHash2 } from "crypto";
|
|
356
354
|
import { createRequire } from "module";
|
|
355
|
+
|
|
356
|
+
// src/harness/names.ts
|
|
357
|
+
var HARNESS_NAMES = [
|
|
358
|
+
"opencode",
|
|
359
|
+
// opencode v2 (binary `opencode2`) — a ground-up rewrite of the plugin API, loaded from the
|
|
360
|
+
// package's root index.js (src/opencode2.ts). Like opencode it has NO hook binary.
|
|
361
|
+
"opencode2",
|
|
362
|
+
// Kilo CLI is an opencode fork loaded as a persistent plugin (src/kilo.ts), so like opencode it
|
|
363
|
+
// has NO hook binary — deliberately absent from HOOK_BINS below.
|
|
364
|
+
"kilo",
|
|
365
|
+
// Cline CLI loads dist/cline.js through its native plugin manager; file hooks cannot inject.
|
|
366
|
+
"cline-cli",
|
|
367
|
+
// DeepSeek Harness loads dist/dsh.js as a native Cordis plugin (src/dsh.ts). Its Claude Code /
|
|
368
|
+
// Codex hook bridges are optional packages, so there is no hook binary to install either.
|
|
369
|
+
"dsh",
|
|
370
|
+
// pi loads dist/pi.js as an extension (src/pi.ts), and Prime Agent — a fork of pi — loads
|
|
371
|
+
// dist/prime-agent.js the same way (src/prime-agent.ts). Neither has a hook binary.
|
|
372
|
+
"pi",
|
|
373
|
+
"prime-agent",
|
|
374
|
+
"claude-code",
|
|
375
|
+
"cursor-cli",
|
|
376
|
+
"codex",
|
|
377
|
+
"dcode",
|
|
378
|
+
"antigravity-cli",
|
|
379
|
+
"devin-cli",
|
|
380
|
+
"copilot-cli",
|
|
381
|
+
"grok-build",
|
|
382
|
+
"qwen-code",
|
|
383
|
+
// Factory Droid is a per-prompt HOOK host: the installer wires ~/.factory/hooks.json and the
|
|
384
|
+
// stdio MCP registration in ~/.factory/mcp.json (see src/installer.ts).
|
|
385
|
+
"factory-droid",
|
|
386
|
+
// ZCode is a per-prompt HOOK host too, registered in its own CLI config
|
|
387
|
+
// (~/.zcode/cli/config.json) under `hooks.events` — never the user's real Claude Code settings.
|
|
388
|
+
"zcode",
|
|
389
|
+
// TraeCode is a per-prompt HOOK host registered in ~/.trae-cn/hooks.json (see src/installer.ts).
|
|
390
|
+
"traecode",
|
|
391
|
+
"kimi-code",
|
|
392
|
+
// WorkBuddy is a per-prompt HOOK host too: the installer wires ~/.workbuddy/settings.json and
|
|
393
|
+
// the stdio MCP registration in ~/.workbuddy/mcp.json (see src/installer.ts).
|
|
394
|
+
"workbuddy",
|
|
395
|
+
// CodeBuddy Code is the SAME @genie/agent-cli HOOK host, one product config apart (WorkBuddy only
|
|
396
|
+
// renames the home folder): the installer wires ~/.codebuddy/settings.json and the stdio MCP
|
|
397
|
+
// registration in CodeBuddy's priority-chained MCP file (see src/installer.ts). The CLI
|
|
398
|
+
// writes WorkBuddy's transcript format; the CodeBuddy IDE has a reader of its own.
|
|
399
|
+
"codebuddy"
|
|
400
|
+
];
|
|
401
|
+
|
|
402
|
+
// ../../coding-agents/human-session.ts
|
|
357
403
|
var HUMAN_TOKEN_PATTERN = /^mrh_[0-9a-f]{32}_[0-9a-f]{64}$/;
|
|
358
404
|
var HumanLoginRequiredError = class extends AccessDeniedError {
|
|
359
405
|
constructor() {
|
|
@@ -379,14 +425,14 @@ function humanRouterUrl(value) {
|
|
|
379
425
|
if (url.search || url.hash) throw new HumanLoginRequiredError();
|
|
380
426
|
return canonical;
|
|
381
427
|
}
|
|
382
|
-
function secureEntry(routerUrl, credentialRef) {
|
|
428
|
+
function secureEntry(routerUrl, credentialRef, harness) {
|
|
383
429
|
try {
|
|
384
|
-
if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef)) {
|
|
430
|
+
if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef) || !HARNESS_NAMES.includes(harness)) {
|
|
385
431
|
throw new HumanLoginRequiredError();
|
|
386
432
|
}
|
|
387
433
|
const { Entry: NativeEntry } = createRequire(import.meta.url)("@napi-rs/keyring");
|
|
388
|
-
const service = `hindsight-memory-router:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
|
|
389
|
-
return new NativeEntry(service, credentialRef
|
|
434
|
+
const service = `hindsight-memory-router:human-v2:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
|
|
435
|
+
return new NativeEntry(service, `${harness}:${credentialRef}`, { linux: { store: "secret-service" } });
|
|
390
436
|
} catch {
|
|
391
437
|
throw new HumanLoginRequiredError();
|
|
392
438
|
}
|
|
@@ -404,9 +450,9 @@ function validateHumanSession(value, routerUrl, allowExpired = false) {
|
|
|
404
450
|
routerUrl: session.routerUrl
|
|
405
451
|
};
|
|
406
452
|
}
|
|
407
|
-
function readHumanSession(routerUrl, credentialRef) {
|
|
453
|
+
function readHumanSession(routerUrl, credentialRef, harness) {
|
|
408
454
|
try {
|
|
409
|
-
const password = secureEntry(routerUrl, credentialRef).getPassword();
|
|
455
|
+
const password = secureEntry(routerUrl, credentialRef, harness).getPassword();
|
|
410
456
|
if (!password || password.length > 4096) throw new HumanLoginRequiredError();
|
|
411
457
|
return validateHumanSession(JSON.parse(password), routerUrl);
|
|
412
458
|
} catch {
|
|
@@ -415,11 +461,11 @@ function readHumanSession(routerUrl, credentialRef) {
|
|
|
415
461
|
}
|
|
416
462
|
|
|
417
463
|
// ../../coding-agents/runtime.ts
|
|
418
|
-
function transportForConfig(harness, { config, principal }) {
|
|
464
|
+
function transportForConfig(harness, { config, principal, principalId }) {
|
|
419
465
|
if (principal.human) {
|
|
420
466
|
const routerUrl = humanRouterUrl(config.routerUrl);
|
|
421
467
|
const reference = principal.human.credentialRef;
|
|
422
|
-
let session = readHumanSession(routerUrl, reference);
|
|
468
|
+
let session = readHumanSession(routerUrl, reference, principalId);
|
|
423
469
|
return new RouterTransport({
|
|
424
470
|
routerUrl,
|
|
425
471
|
access: principal,
|
|
@@ -427,7 +473,7 @@ function transportForConfig(harness, { config, principal }) {
|
|
|
427
473
|
tokenPattern: HUMAN_TOKEN_PATTERN,
|
|
428
474
|
unauthorizedError: () => new HumanLoginRequiredError(),
|
|
429
475
|
token: () => {
|
|
430
|
-
session = readHumanSession(routerUrl, reference);
|
|
476
|
+
session = readHumanSession(routerUrl, reference, principalId);
|
|
431
477
|
return session.token;
|
|
432
478
|
}
|
|
433
479
|
});
|
|
@@ -761,7 +807,7 @@ var PLUGIN_GOVERNED_BANK_FIELDS = [
|
|
|
761
807
|
];
|
|
762
808
|
|
|
763
809
|
// package.json
|
|
764
|
-
var version = "0.8.
|
|
810
|
+
var version = "0.8.14";
|
|
765
811
|
|
|
766
812
|
// src/core/util.ts
|
|
767
813
|
import { accessSync, constants } from "fs";
|
package/dist/index.js
CHANGED
|
@@ -34,12 +34,10 @@ function requireBank(access, bank, operation) {
|
|
|
34
34
|
}
|
|
35
35
|
function classifyOperation(method, suffix) {
|
|
36
36
|
if (method === "POST" && ["/memories/recall", "/reflect"].includes(suffix)) return "read";
|
|
37
|
-
if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|
|
|
38
|
-
suffix
|
|
39
|
-
))
|
|
37
|
+
if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|pages)(?:\/[^/]+)*$/.test(suffix))
|
|
40
38
|
return "read";
|
|
41
39
|
if (method === "GET" && suffix === "") return "read";
|
|
42
|
-
if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|
|
|
40
|
+
if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|pages|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
|
|
43
41
|
suffix
|
|
44
42
|
))
|
|
45
43
|
return "write";
|
|
@@ -451,6 +449,54 @@ var RouterTransport = class {
|
|
|
451
449
|
// ../../coding-agents/human-session.ts
|
|
452
450
|
import { createHash as createHash2 } from "crypto";
|
|
453
451
|
import { createRequire } from "module";
|
|
452
|
+
|
|
453
|
+
// src/harness/names.ts
|
|
454
|
+
var HARNESS_NAMES = [
|
|
455
|
+
"opencode",
|
|
456
|
+
// opencode v2 (binary `opencode2`) — a ground-up rewrite of the plugin API, loaded from the
|
|
457
|
+
// package's root index.js (src/opencode2.ts). Like opencode it has NO hook binary.
|
|
458
|
+
"opencode2",
|
|
459
|
+
// Kilo CLI is an opencode fork loaded as a persistent plugin (src/kilo.ts), so like opencode it
|
|
460
|
+
// has NO hook binary — deliberately absent from HOOK_BINS below.
|
|
461
|
+
"kilo",
|
|
462
|
+
// Cline CLI loads dist/cline.js through its native plugin manager; file hooks cannot inject.
|
|
463
|
+
"cline-cli",
|
|
464
|
+
// DeepSeek Harness loads dist/dsh.js as a native Cordis plugin (src/dsh.ts). Its Claude Code /
|
|
465
|
+
// Codex hook bridges are optional packages, so there is no hook binary to install either.
|
|
466
|
+
"dsh",
|
|
467
|
+
// pi loads dist/pi.js as an extension (src/pi.ts), and Prime Agent — a fork of pi — loads
|
|
468
|
+
// dist/prime-agent.js the same way (src/prime-agent.ts). Neither has a hook binary.
|
|
469
|
+
"pi",
|
|
470
|
+
"prime-agent",
|
|
471
|
+
"claude-code",
|
|
472
|
+
"cursor-cli",
|
|
473
|
+
"codex",
|
|
474
|
+
"dcode",
|
|
475
|
+
"antigravity-cli",
|
|
476
|
+
"devin-cli",
|
|
477
|
+
"copilot-cli",
|
|
478
|
+
"grok-build",
|
|
479
|
+
"qwen-code",
|
|
480
|
+
// Factory Droid is a per-prompt HOOK host: the installer wires ~/.factory/hooks.json and the
|
|
481
|
+
// stdio MCP registration in ~/.factory/mcp.json (see src/installer.ts).
|
|
482
|
+
"factory-droid",
|
|
483
|
+
// ZCode is a per-prompt HOOK host too, registered in its own CLI config
|
|
484
|
+
// (~/.zcode/cli/config.json) under `hooks.events` — never the user's real Claude Code settings.
|
|
485
|
+
"zcode",
|
|
486
|
+
// TraeCode is a per-prompt HOOK host registered in ~/.trae-cn/hooks.json (see src/installer.ts).
|
|
487
|
+
"traecode",
|
|
488
|
+
"kimi-code",
|
|
489
|
+
// WorkBuddy is a per-prompt HOOK host too: the installer wires ~/.workbuddy/settings.json and
|
|
490
|
+
// the stdio MCP registration in ~/.workbuddy/mcp.json (see src/installer.ts).
|
|
491
|
+
"workbuddy",
|
|
492
|
+
// CodeBuddy Code is the SAME @genie/agent-cli HOOK host, one product config apart (WorkBuddy only
|
|
493
|
+
// renames the home folder): the installer wires ~/.codebuddy/settings.json and the stdio MCP
|
|
494
|
+
// registration in CodeBuddy's priority-chained MCP file (see src/installer.ts). The CLI
|
|
495
|
+
// writes WorkBuddy's transcript format; the CodeBuddy IDE has a reader of its own.
|
|
496
|
+
"codebuddy"
|
|
497
|
+
];
|
|
498
|
+
|
|
499
|
+
// ../../coding-agents/human-session.ts
|
|
454
500
|
var HUMAN_TOKEN_PATTERN = /^mrh_[0-9a-f]{32}_[0-9a-f]{64}$/;
|
|
455
501
|
var HumanLoginRequiredError = class extends AccessDeniedError {
|
|
456
502
|
constructor() {
|
|
@@ -476,14 +522,14 @@ function humanRouterUrl(value) {
|
|
|
476
522
|
if (url2.search || url2.hash) throw new HumanLoginRequiredError();
|
|
477
523
|
return canonical2;
|
|
478
524
|
}
|
|
479
|
-
function secureEntry(routerUrl, credentialRef) {
|
|
525
|
+
function secureEntry(routerUrl, credentialRef, harness) {
|
|
480
526
|
try {
|
|
481
|
-
if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef)) {
|
|
527
|
+
if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef) || !HARNESS_NAMES.includes(harness)) {
|
|
482
528
|
throw new HumanLoginRequiredError();
|
|
483
529
|
}
|
|
484
530
|
const { Entry: NativeEntry } = createRequire(import.meta.url)("@napi-rs/keyring");
|
|
485
|
-
const service = `hindsight-memory-router:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
|
|
486
|
-
return new NativeEntry(service, credentialRef
|
|
531
|
+
const service = `hindsight-memory-router:human-v2:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
|
|
532
|
+
return new NativeEntry(service, `${harness}:${credentialRef}`, { linux: { store: "secret-service" } });
|
|
487
533
|
} catch {
|
|
488
534
|
throw new HumanLoginRequiredError();
|
|
489
535
|
}
|
|
@@ -501,9 +547,9 @@ function validateHumanSession(value, routerUrl, allowExpired = false) {
|
|
|
501
547
|
routerUrl: session.routerUrl
|
|
502
548
|
};
|
|
503
549
|
}
|
|
504
|
-
function readHumanSession(routerUrl, credentialRef) {
|
|
550
|
+
function readHumanSession(routerUrl, credentialRef, harness) {
|
|
505
551
|
try {
|
|
506
|
-
const password = secureEntry(routerUrl, credentialRef).getPassword();
|
|
552
|
+
const password = secureEntry(routerUrl, credentialRef, harness).getPassword();
|
|
507
553
|
if (!password || password.length > 4096) throw new HumanLoginRequiredError();
|
|
508
554
|
return validateHumanSession(JSON.parse(password), routerUrl);
|
|
509
555
|
} catch {
|
|
@@ -515,11 +561,11 @@ function readHumanSession(routerUrl, credentialRef) {
|
|
|
515
561
|
function harnessTransport(harness) {
|
|
516
562
|
return transportForConfig(harness, loadManagedConfig(process.env, harness));
|
|
517
563
|
}
|
|
518
|
-
function transportForConfig(harness, { config: config2, principal }) {
|
|
564
|
+
function transportForConfig(harness, { config: config2, principal, principalId }) {
|
|
519
565
|
if (principal.human) {
|
|
520
566
|
const routerUrl = humanRouterUrl(config2.routerUrl);
|
|
521
567
|
const reference = principal.human.credentialRef;
|
|
522
|
-
let session = readHumanSession(routerUrl, reference);
|
|
568
|
+
let session = readHumanSession(routerUrl, reference, principalId);
|
|
523
569
|
return new RouterTransport({
|
|
524
570
|
routerUrl,
|
|
525
571
|
access: principal,
|
|
@@ -527,7 +573,7 @@ function transportForConfig(harness, { config: config2, principal }) {
|
|
|
527
573
|
tokenPattern: HUMAN_TOKEN_PATTERN,
|
|
528
574
|
unauthorizedError: () => new HumanLoginRequiredError(),
|
|
529
575
|
token: () => {
|
|
530
|
-
session = readHumanSession(routerUrl, reference);
|
|
576
|
+
session = readHumanSession(routerUrl, reference, principalId);
|
|
531
577
|
return session.token;
|
|
532
578
|
}
|
|
533
579
|
});
|
|
@@ -1334,7 +1380,7 @@ var PLUGIN_GOVERNED_BANK_FIELDS = [
|
|
|
1334
1380
|
];
|
|
1335
1381
|
|
|
1336
1382
|
// package.json
|
|
1337
|
-
var version = "0.8.
|
|
1383
|
+
var version = "0.8.14";
|
|
1338
1384
|
|
|
1339
1385
|
// src/core/util.ts
|
|
1340
1386
|
import { accessSync, constants } from "fs";
|
|
@@ -1867,7 +1913,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1867
1913
|
*/
|
|
1868
1914
|
async tree() {
|
|
1869
1915
|
if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
|
|
1870
|
-
const r = await this.req("GET", this.bankUrl("/
|
|
1916
|
+
const r = await this.req("GET", this.bankUrl("/pages/tree"));
|
|
1871
1917
|
if (await this.pagesUnsupported(r)) throw new KnowledgePagesUnavailableError();
|
|
1872
1918
|
if (r.status === 404) return [];
|
|
1873
1919
|
this.knowledgePagesSupported = true;
|
|
@@ -1915,7 +1961,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1915
1961
|
if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
|
|
1916
1962
|
const r = await this.req(
|
|
1917
1963
|
"GET",
|
|
1918
|
-
this.bankUrl(`/
|
|
1964
|
+
this.bankUrl(`/pages/${encodeURIComponent(pageId)}`)
|
|
1919
1965
|
);
|
|
1920
1966
|
if (r.status === 404) throw new Error(`knowledge page not found: ${pageId}`);
|
|
1921
1967
|
return shapePage(await r.json());
|
|
@@ -1928,7 +1974,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1928
1974
|
const q = `?q=${encodeURIComponent(query)}&limit=${opts.limit ?? this.pageSearchLimit}`;
|
|
1929
1975
|
const r = await this.req(
|
|
1930
1976
|
"GET",
|
|
1931
|
-
this.bankUrl(`/
|
|
1977
|
+
this.bankUrl(`/pages/search${q}`),
|
|
1932
1978
|
void 0,
|
|
1933
1979
|
[],
|
|
1934
1980
|
opts.timeoutMs
|
|
@@ -1947,7 +1993,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1947
1993
|
/**
|
|
1948
1994
|
* Seed the fixed page taxonomy as knowledge-base pages at the tree root, idempotently.
|
|
1949
1995
|
*
|
|
1950
|
-
* Matched by NAME, not id: `/
|
|
1996
|
+
* Matched by NAME, not id: `/pages` mints its own `kp-…` id, so a stable
|
|
1951
1997
|
* client-chosen id isn't available to match on (unlike the old mental-model path, which keyed
|
|
1952
1998
|
* off a slug). Names are unique per folder server-side, which makes them a sound key.
|
|
1953
1999
|
*
|
|
@@ -1986,7 +2032,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
1986
2032
|
trigger: pageTriggerFor(pageTrigger, this.bank, page.name)
|
|
1987
2033
|
};
|
|
1988
2034
|
if (!hit) {
|
|
1989
|
-
const r = await this.req("POST", this.bankUrl("/
|
|
2035
|
+
const r = await this.req("POST", this.bankUrl("/pages"), body, [409]);
|
|
1990
2036
|
if (await this.pagesUnsupported(r)) {
|
|
1991
2037
|
this.log(
|
|
1992
2038
|
`[bank] knowledge pages unavailable on ${this.apiUrl}; continuing without pages`
|
|
@@ -2013,7 +2059,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
2013
2059
|
if (triggerDrift) patch.trigger = pageTriggerPatch(body.trigger);
|
|
2014
2060
|
const r = await this.req(
|
|
2015
2061
|
"PATCH",
|
|
2016
|
-
this.bankUrl(`/
|
|
2062
|
+
this.bankUrl(`/pages/nodes/${encodeURIComponent(hit.id)}`),
|
|
2017
2063
|
patch
|
|
2018
2064
|
);
|
|
2019
2065
|
if (await this.pagesUnsupported(r)) {
|
|
@@ -2067,7 +2113,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
2067
2113
|
if (!patch.trigger && !patch.source_query) continue;
|
|
2068
2114
|
const r = await this.req(
|
|
2069
2115
|
"PATCH",
|
|
2070
|
-
this.bankUrl(`/
|
|
2116
|
+
this.bankUrl(`/pages/nodes/${encodeURIComponent(page.id)}`),
|
|
2071
2117
|
patch
|
|
2072
2118
|
);
|
|
2073
2119
|
if (r.status === 404) continue;
|
|
@@ -2096,7 +2142,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
2096
2142
|
let pageId = args.relatesToPageId;
|
|
2097
2143
|
if (!pageId) {
|
|
2098
2144
|
const folderId = await this.ensureFolder("Initiatives");
|
|
2099
|
-
const r = await this.req("POST", this.bankUrl("/
|
|
2145
|
+
const r = await this.req("POST", this.bankUrl("/pages"), {
|
|
2100
2146
|
name: args.title,
|
|
2101
2147
|
source_query: `Summarize the "${args.title}" initiative: what is being built or changed and why, and its current state \u2014 drawn from the project's memory.` + pageScopeRule(this.project),
|
|
2102
2148
|
parent_id: folderId,
|
|
@@ -2124,7 +2170,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
2124
2170
|
/** Find a root folder by name (case-insensitive) or create it; returns its id. Fail-open to undefined. */
|
|
2125
2171
|
async ensureFolder(name) {
|
|
2126
2172
|
try {
|
|
2127
|
-
const tree = await (await this.req("GET", this.bankUrl("/
|
|
2173
|
+
const tree = await (await this.req("GET", this.bankUrl("/pages/tree"))).json();
|
|
2128
2174
|
const hit = (tree.roots || []).find(
|
|
2129
2175
|
(n) => n.kind === "folder" && (n.name || "").toLowerCase() === name.toLowerCase()
|
|
2130
2176
|
);
|
|
@@ -2133,7 +2179,7 @@ var HindsightClient = class _HindsightClient {
|
|
|
2133
2179
|
if (error62 instanceof AccessDeniedError) throw error62;
|
|
2134
2180
|
}
|
|
2135
2181
|
try {
|
|
2136
|
-
const r = await this.req("POST", this.bankUrl("/
|
|
2182
|
+
const r = await this.req("POST", this.bankUrl("/pages/folders"), { name });
|
|
2137
2183
|
return (await r.json()).id;
|
|
2138
2184
|
} catch (error62) {
|
|
2139
2185
|
if (error62 instanceof AccessDeniedError) throw error62;
|