@mickey-kras/hindsight-memory-router-coding-agents 0.8.10 → 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.
Files changed (59) hide show
  1. package/dist/antigravity-hook.js +70 -24
  2. package/dist/antigravity-statusline.js +60 -14
  3. package/dist/antigravity-stop-hook.js +70 -24
  4. package/dist/claude-hook.js +70 -24
  5. package/dist/claude-sessionstart-hook.js +70 -24
  6. package/dist/claude-stop-hook.js +70 -24
  7. package/dist/cline.js +70 -24
  8. package/dist/codebuddy-hook.js +70 -24
  9. package/dist/codebuddy-sessionstart-hook.js +70 -24
  10. package/dist/codebuddy-stop-hook.js +70 -24
  11. package/dist/codex-hook.js +70 -24
  12. package/dist/codex-sessionstart-hook.js +70 -24
  13. package/dist/codex-stop-hook.js +70 -24
  14. package/dist/copilot-hook.js +70 -24
  15. package/dist/copilot-sessionstart-hook.js +70 -24
  16. package/dist/copilot-stop-hook.js +70 -24
  17. package/dist/cursor-hook.js +70 -24
  18. package/dist/cursor-sessionstart-hook.js +70 -24
  19. package/dist/cursor-stop-hook.js +70 -24
  20. package/dist/dcode-hook.js +70 -24
  21. package/dist/dcode-sessionstart-hook.js +70 -24
  22. package/dist/dcode-stop-hook.js +70 -24
  23. package/dist/deepen.js +70 -68
  24. package/dist/devin-hook.js +70 -24
  25. package/dist/devin-sessionstart-hook.js +70 -24
  26. package/dist/devin-stop-hook.js +70 -24
  27. package/dist/droid-hook.js +70 -24
  28. package/dist/droid-sessionstart-hook.js +70 -24
  29. package/dist/droid-stop-hook.js +70 -24
  30. package/dist/dsh.js +70 -24
  31. package/dist/grok-hook.js +70 -24
  32. package/dist/grok-sessionstart-hook.js +70 -24
  33. package/dist/grok-stop-hook.js +70 -24
  34. package/dist/hindsight-seed.js +60 -14
  35. package/dist/index.js +70 -24
  36. package/dist/installer.js +70 -21
  37. package/dist/kilo.js +70 -24
  38. package/dist/kimi-hook.js +70 -24
  39. package/dist/kimi-sessionstart-hook.js +70 -24
  40. package/dist/kimi-stop-hook.js +70 -24
  41. package/dist/mcp-server.js +79 -24
  42. package/dist/opencode2.js +70 -24
  43. package/dist/pi.js +70 -24
  44. package/dist/prime-agent.js +70 -24
  45. package/dist/qwen-hook.js +70 -24
  46. package/dist/qwen-sessionstart-hook.js +70 -24
  47. package/dist/qwen-stop-hook.js +70 -24
  48. package/dist/status.js +70 -24
  49. package/dist/traecode-hook.js +70 -24
  50. package/dist/traecode-sessionstart-hook.js +70 -24
  51. package/dist/traecode-stop-hook.js +70 -24
  52. package/dist/workbuddy-hook.js +70 -24
  53. package/dist/workbuddy-sessionstart-hook.js +70 -24
  54. package/dist/workbuddy-stop-hook.js +70 -24
  55. package/dist/zcode-hook.js +70 -24
  56. package/dist/zcode-sessionstart-hook.js +70 -24
  57. package/dist/zcode-stop-hook.js +70 -24
  58. package/npm-shrinkwrap.json +194 -663
  59. package/package.json +7 -4
package/dist/cline.js CHANGED
@@ -31,12 +31,10 @@ function requireBank(access, bank, operation) {
31
31
  }
32
32
  function classifyOperation(method, suffix) {
33
33
  if (method === "POST" && ["/memories/recall", "/reflect"].includes(suffix)) return "read";
34
- if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|knowledge-base)(?:\/[^/]+)*$/.test(
35
- suffix
36
- ))
34
+ if (method === "GET" && /^\/(?:config|stats|tags|graph|documents|operations|memories|mental-models|pages)(?:\/[^/]+)*$/.test(suffix))
37
35
  return "read";
38
36
  if (method === "GET" && suffix === "") return "read";
39
- if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|knowledge-base|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
37
+ if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && /^(?:\/(?:config|import|memories|documents|mental-models|pages|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
40
38
  suffix
41
39
  ))
42
40
  return "write";
@@ -441,6 +439,54 @@ var RouterTransport = class {
441
439
  // ../../coding-agents/human-session.ts
442
440
  import { createHash as createHash2 } from "crypto";
443
441
  import { createRequire } from "module";
442
+
443
+ // src/harness/names.ts
444
+ var HARNESS_NAMES = [
445
+ "opencode",
446
+ // opencode v2 (binary `opencode2`) — a ground-up rewrite of the plugin API, loaded from the
447
+ // package's root index.js (src/opencode2.ts). Like opencode it has NO hook binary.
448
+ "opencode2",
449
+ // Kilo CLI is an opencode fork loaded as a persistent plugin (src/kilo.ts), so like opencode it
450
+ // has NO hook binary — deliberately absent from HOOK_BINS below.
451
+ "kilo",
452
+ // Cline CLI loads dist/cline.js through its native plugin manager; file hooks cannot inject.
453
+ "cline-cli",
454
+ // DeepSeek Harness loads dist/dsh.js as a native Cordis plugin (src/dsh.ts). Its Claude Code /
455
+ // Codex hook bridges are optional packages, so there is no hook binary to install either.
456
+ "dsh",
457
+ // pi loads dist/pi.js as an extension (src/pi.ts), and Prime Agent — a fork of pi — loads
458
+ // dist/prime-agent.js the same way (src/prime-agent.ts). Neither has a hook binary.
459
+ "pi",
460
+ "prime-agent",
461
+ "claude-code",
462
+ "cursor-cli",
463
+ "codex",
464
+ "dcode",
465
+ "antigravity-cli",
466
+ "devin-cli",
467
+ "copilot-cli",
468
+ "grok-build",
469
+ "qwen-code",
470
+ // Factory Droid is a per-prompt HOOK host: the installer wires ~/.factory/hooks.json and the
471
+ // stdio MCP registration in ~/.factory/mcp.json (see src/installer.ts).
472
+ "factory-droid",
473
+ // ZCode is a per-prompt HOOK host too, registered in its own CLI config
474
+ // (~/.zcode/cli/config.json) under `hooks.events` — never the user's real Claude Code settings.
475
+ "zcode",
476
+ // TraeCode is a per-prompt HOOK host registered in ~/.trae-cn/hooks.json (see src/installer.ts).
477
+ "traecode",
478
+ "kimi-code",
479
+ // WorkBuddy is a per-prompt HOOK host too: the installer wires ~/.workbuddy/settings.json and
480
+ // the stdio MCP registration in ~/.workbuddy/mcp.json (see src/installer.ts).
481
+ "workbuddy",
482
+ // CodeBuddy Code is the SAME @genie/agent-cli HOOK host, one product config apart (WorkBuddy only
483
+ // renames the home folder): the installer wires ~/.codebuddy/settings.json and the stdio MCP
484
+ // registration in CodeBuddy's priority-chained MCP file (see src/installer.ts). The CLI
485
+ // writes WorkBuddy's transcript format; the CodeBuddy IDE has a reader of its own.
486
+ "codebuddy"
487
+ ];
488
+
489
+ // ../../coding-agents/human-session.ts
444
490
  var HUMAN_TOKEN_PATTERN = /^mrh_[0-9a-f]{32}_[0-9a-f]{64}$/;
445
491
  var HumanLoginRequiredError = class extends AccessDeniedError {
446
492
  constructor() {
@@ -466,14 +512,14 @@ function humanRouterUrl(value) {
466
512
  if (url2.search || url2.hash) throw new HumanLoginRequiredError();
467
513
  return canonical2;
468
514
  }
469
- function secureEntry(routerUrl, credentialRef) {
515
+ function secureEntry(routerUrl, credentialRef, harness) {
470
516
  try {
471
- if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef)) {
517
+ if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef) || !HARNESS_NAMES.includes(harness)) {
472
518
  throw new HumanLoginRequiredError();
473
519
  }
474
520
  const { Entry: NativeEntry } = createRequire(import.meta.url)("@napi-rs/keyring");
475
- const service = `hindsight-memory-router:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
476
- return new NativeEntry(service, credentialRef, { linux: { store: "secret-service" } });
521
+ const service = `hindsight-memory-router:human-v2:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
522
+ return new NativeEntry(service, `${harness}:${credentialRef}`, { linux: { store: "secret-service" } });
477
523
  } catch {
478
524
  throw new HumanLoginRequiredError();
479
525
  }
@@ -491,9 +537,9 @@ function validateHumanSession(value, routerUrl, allowExpired = false) {
491
537
  routerUrl: session.routerUrl
492
538
  };
493
539
  }
494
- function readHumanSession(routerUrl, credentialRef) {
540
+ function readHumanSession(routerUrl, credentialRef, harness) {
495
541
  try {
496
- const password = secureEntry(routerUrl, credentialRef).getPassword();
542
+ const password = secureEntry(routerUrl, credentialRef, harness).getPassword();
497
543
  if (!password || password.length > 4096) throw new HumanLoginRequiredError();
498
544
  return validateHumanSession(JSON.parse(password), routerUrl);
499
545
  } catch {
@@ -505,11 +551,11 @@ function readHumanSession(routerUrl, credentialRef) {
505
551
  function harnessTransport(harness) {
506
552
  return transportForConfig(harness, loadManagedConfig(process.env, harness));
507
553
  }
508
- function transportForConfig(harness, { config: config2, principal }) {
554
+ function transportForConfig(harness, { config: config2, principal, principalId }) {
509
555
  if (principal.human) {
510
556
  const routerUrl = humanRouterUrl(config2.routerUrl);
511
557
  const reference = principal.human.credentialRef;
512
- let session = readHumanSession(routerUrl, reference);
558
+ let session = readHumanSession(routerUrl, reference, principalId);
513
559
  return new RouterTransport({
514
560
  routerUrl,
515
561
  access: principal,
@@ -517,7 +563,7 @@ function transportForConfig(harness, { config: config2, principal }) {
517
563
  tokenPattern: HUMAN_TOKEN_PATTERN,
518
564
  unauthorizedError: () => new HumanLoginRequiredError(),
519
565
  token: () => {
520
- session = readHumanSession(routerUrl, reference);
566
+ session = readHumanSession(routerUrl, reference, principalId);
521
567
  return session.token;
522
568
  }
523
569
  });
@@ -1324,7 +1370,7 @@ var PLUGIN_GOVERNED_BANK_FIELDS = [
1324
1370
  ];
1325
1371
 
1326
1372
  // package.json
1327
- var version = "0.8.10";
1373
+ var version = "0.8.14";
1328
1374
 
1329
1375
  // src/core/util.ts
1330
1376
  import { accessSync, constants } from "fs";
@@ -1857,7 +1903,7 @@ var HindsightClient = class _HindsightClient {
1857
1903
  */
1858
1904
  async tree() {
1859
1905
  if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
1860
- const r = await this.req("GET", this.bankUrl("/knowledge-base/tree"));
1906
+ const r = await this.req("GET", this.bankUrl("/pages/tree"));
1861
1907
  if (await this.pagesUnsupported(r)) throw new KnowledgePagesUnavailableError();
1862
1908
  if (r.status === 404) return [];
1863
1909
  this.knowledgePagesSupported = true;
@@ -1905,7 +1951,7 @@ var HindsightClient = class _HindsightClient {
1905
1951
  if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
1906
1952
  const r = await this.req(
1907
1953
  "GET",
1908
- this.bankUrl(`/knowledge-base/pages/${encodeURIComponent(pageId)}`)
1954
+ this.bankUrl(`/pages/${encodeURIComponent(pageId)}`)
1909
1955
  );
1910
1956
  if (r.status === 404) throw new Error(`knowledge page not found: ${pageId}`);
1911
1957
  return shapePage(await r.json());
@@ -1918,7 +1964,7 @@ var HindsightClient = class _HindsightClient {
1918
1964
  const q = `?q=${encodeURIComponent(query)}&limit=${opts.limit ?? this.pageSearchLimit}`;
1919
1965
  const r = await this.req(
1920
1966
  "GET",
1921
- this.bankUrl(`/knowledge-base/search${q}`),
1967
+ this.bankUrl(`/pages/search${q}`),
1922
1968
  void 0,
1923
1969
  [],
1924
1970
  opts.timeoutMs
@@ -1937,7 +1983,7 @@ var HindsightClient = class _HindsightClient {
1937
1983
  /**
1938
1984
  * Seed the fixed page taxonomy as knowledge-base pages at the tree root, idempotently.
1939
1985
  *
1940
- * Matched by NAME, not id: `/knowledge-base/pages` mints its own `kp-…` id, so a stable
1986
+ * Matched by NAME, not id: `/pages` mints its own `kp-…` id, so a stable
1941
1987
  * client-chosen id isn't available to match on (unlike the old mental-model path, which keyed
1942
1988
  * off a slug). Names are unique per folder server-side, which makes them a sound key.
1943
1989
  *
@@ -1976,7 +2022,7 @@ var HindsightClient = class _HindsightClient {
1976
2022
  trigger: pageTriggerFor(pageTrigger, this.bank, page.name)
1977
2023
  };
1978
2024
  if (!hit) {
1979
- const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), body, [409]);
2025
+ const r = await this.req("POST", this.bankUrl("/pages"), body, [409]);
1980
2026
  if (await this.pagesUnsupported(r)) {
1981
2027
  this.log(
1982
2028
  `[bank] knowledge pages unavailable on ${this.apiUrl}; continuing without pages`
@@ -2003,7 +2049,7 @@ var HindsightClient = class _HindsightClient {
2003
2049
  if (triggerDrift) patch.trigger = pageTriggerPatch(body.trigger);
2004
2050
  const r = await this.req(
2005
2051
  "PATCH",
2006
- this.bankUrl(`/knowledge-base/nodes/${encodeURIComponent(hit.id)}`),
2052
+ this.bankUrl(`/pages/nodes/${encodeURIComponent(hit.id)}`),
2007
2053
  patch
2008
2054
  );
2009
2055
  if (await this.pagesUnsupported(r)) {
@@ -2057,7 +2103,7 @@ var HindsightClient = class _HindsightClient {
2057
2103
  if (!patch.trigger && !patch.source_query) continue;
2058
2104
  const r = await this.req(
2059
2105
  "PATCH",
2060
- this.bankUrl(`/knowledge-base/nodes/${encodeURIComponent(page.id)}`),
2106
+ this.bankUrl(`/pages/nodes/${encodeURIComponent(page.id)}`),
2061
2107
  patch
2062
2108
  );
2063
2109
  if (r.status === 404) continue;
@@ -2086,7 +2132,7 @@ var HindsightClient = class _HindsightClient {
2086
2132
  let pageId = args.relatesToPageId;
2087
2133
  if (!pageId) {
2088
2134
  const folderId = await this.ensureFolder("Initiatives");
2089
- const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), {
2135
+ const r = await this.req("POST", this.bankUrl("/pages"), {
2090
2136
  name: args.title,
2091
2137
  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),
2092
2138
  parent_id: folderId,
@@ -2114,7 +2160,7 @@ var HindsightClient = class _HindsightClient {
2114
2160
  /** Find a root folder by name (case-insensitive) or create it; returns its id. Fail-open to undefined. */
2115
2161
  async ensureFolder(name) {
2116
2162
  try {
2117
- const tree = await (await this.req("GET", this.bankUrl("/knowledge-base/tree"))).json();
2163
+ const tree = await (await this.req("GET", this.bankUrl("/pages/tree"))).json();
2118
2164
  const hit = (tree.roots || []).find(
2119
2165
  (n) => n.kind === "folder" && (n.name || "").toLowerCase() === name.toLowerCase()
2120
2166
  );
@@ -2123,7 +2169,7 @@ var HindsightClient = class _HindsightClient {
2123
2169
  if (error62 instanceof AccessDeniedError) throw error62;
2124
2170
  }
2125
2171
  try {
2126
- const r = await this.req("POST", this.bankUrl("/knowledge-base/folders"), { name });
2172
+ const r = await this.req("POST", this.bankUrl("/pages/folders"), { name });
2127
2173
  return (await r.json()).id;
2128
2174
  } catch (error62) {
2129
2175
  if (error62 instanceof AccessDeniedError) throw error62;
@@ -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|knowledge-base)(?:\/[^/]+)*$/.test(
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|knowledge-base|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
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";
@@ -393,6 +391,54 @@ var RouterTransport = class {
393
391
  // ../../coding-agents/human-session.ts
394
392
  import { createHash as createHash2 } from "crypto";
395
393
  import { createRequire } from "module";
394
+
395
+ // src/harness/names.ts
396
+ var HARNESS_NAMES = [
397
+ "opencode",
398
+ // opencode v2 (binary `opencode2`) — a ground-up rewrite of the plugin API, loaded from the
399
+ // package's root index.js (src/opencode2.ts). Like opencode it has NO hook binary.
400
+ "opencode2",
401
+ // Kilo CLI is an opencode fork loaded as a persistent plugin (src/kilo.ts), so like opencode it
402
+ // has NO hook binary — deliberately absent from HOOK_BINS below.
403
+ "kilo",
404
+ // Cline CLI loads dist/cline.js through its native plugin manager; file hooks cannot inject.
405
+ "cline-cli",
406
+ // DeepSeek Harness loads dist/dsh.js as a native Cordis plugin (src/dsh.ts). Its Claude Code /
407
+ // Codex hook bridges are optional packages, so there is no hook binary to install either.
408
+ "dsh",
409
+ // pi loads dist/pi.js as an extension (src/pi.ts), and Prime Agent — a fork of pi — loads
410
+ // dist/prime-agent.js the same way (src/prime-agent.ts). Neither has a hook binary.
411
+ "pi",
412
+ "prime-agent",
413
+ "claude-code",
414
+ "cursor-cli",
415
+ "codex",
416
+ "dcode",
417
+ "antigravity-cli",
418
+ "devin-cli",
419
+ "copilot-cli",
420
+ "grok-build",
421
+ "qwen-code",
422
+ // Factory Droid is a per-prompt HOOK host: the installer wires ~/.factory/hooks.json and the
423
+ // stdio MCP registration in ~/.factory/mcp.json (see src/installer.ts).
424
+ "factory-droid",
425
+ // ZCode is a per-prompt HOOK host too, registered in its own CLI config
426
+ // (~/.zcode/cli/config.json) under `hooks.events` — never the user's real Claude Code settings.
427
+ "zcode",
428
+ // TraeCode is a per-prompt HOOK host registered in ~/.trae-cn/hooks.json (see src/installer.ts).
429
+ "traecode",
430
+ "kimi-code",
431
+ // WorkBuddy is a per-prompt HOOK host too: the installer wires ~/.workbuddy/settings.json and
432
+ // the stdio MCP registration in ~/.workbuddy/mcp.json (see src/installer.ts).
433
+ "workbuddy",
434
+ // CodeBuddy Code is the SAME @genie/agent-cli HOOK host, one product config apart (WorkBuddy only
435
+ // renames the home folder): the installer wires ~/.codebuddy/settings.json and the stdio MCP
436
+ // registration in CodeBuddy's priority-chained MCP file (see src/installer.ts). The CLI
437
+ // writes WorkBuddy's transcript format; the CodeBuddy IDE has a reader of its own.
438
+ "codebuddy"
439
+ ];
440
+
441
+ // ../../coding-agents/human-session.ts
396
442
  var HUMAN_TOKEN_PATTERN = /^mrh_[0-9a-f]{32}_[0-9a-f]{64}$/;
397
443
  var HumanLoginRequiredError = class extends AccessDeniedError {
398
444
  constructor() {
@@ -418,14 +464,14 @@ function humanRouterUrl(value) {
418
464
  if (url.search || url.hash) throw new HumanLoginRequiredError();
419
465
  return canonical2;
420
466
  }
421
- function secureEntry(routerUrl, credentialRef) {
467
+ function secureEntry(routerUrl, credentialRef, harness) {
422
468
  try {
423
- if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef)) {
469
+ if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef) || !HARNESS_NAMES.includes(harness)) {
424
470
  throw new HumanLoginRequiredError();
425
471
  }
426
472
  const { Entry: NativeEntry } = createRequire(import.meta.url)("@napi-rs/keyring");
427
- const service = `hindsight-memory-router:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
428
- return new NativeEntry(service, credentialRef, { linux: { store: "secret-service" } });
473
+ const service = `hindsight-memory-router:human-v2:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
474
+ return new NativeEntry(service, `${harness}:${credentialRef}`, { linux: { store: "secret-service" } });
429
475
  } catch {
430
476
  throw new HumanLoginRequiredError();
431
477
  }
@@ -443,9 +489,9 @@ function validateHumanSession(value, routerUrl, allowExpired = false) {
443
489
  routerUrl: session.routerUrl
444
490
  };
445
491
  }
446
- function readHumanSession(routerUrl, credentialRef) {
492
+ function readHumanSession(routerUrl, credentialRef, harness) {
447
493
  try {
448
- const password = secureEntry(routerUrl, credentialRef).getPassword();
494
+ const password = secureEntry(routerUrl, credentialRef, harness).getPassword();
449
495
  if (!password || password.length > 4096) throw new HumanLoginRequiredError();
450
496
  return validateHumanSession(JSON.parse(password), routerUrl);
451
497
  } catch {
@@ -457,11 +503,11 @@ function readHumanSession(routerUrl, credentialRef) {
457
503
  function harnessTransport(harness) {
458
504
  return transportForConfig(harness, loadManagedConfig(process.env, harness));
459
505
  }
460
- function transportForConfig(harness, { config: config2, principal }) {
506
+ function transportForConfig(harness, { config: config2, principal, principalId }) {
461
507
  if (principal.human) {
462
508
  const routerUrl = humanRouterUrl(config2.routerUrl);
463
509
  const reference = principal.human.credentialRef;
464
- let session = readHumanSession(routerUrl, reference);
510
+ let session = readHumanSession(routerUrl, reference, principalId);
465
511
  return new RouterTransport({
466
512
  routerUrl,
467
513
  access: principal,
@@ -469,7 +515,7 @@ function transportForConfig(harness, { config: config2, principal }) {
469
515
  tokenPattern: HUMAN_TOKEN_PATTERN,
470
516
  unauthorizedError: () => new HumanLoginRequiredError(),
471
517
  token: () => {
472
- session = readHumanSession(routerUrl, reference);
518
+ session = readHumanSession(routerUrl, reference, principalId);
473
519
  return session.token;
474
520
  }
475
521
  });
@@ -1277,7 +1323,7 @@ var PLUGIN_GOVERNED_BANK_FIELDS = [
1277
1323
  ];
1278
1324
 
1279
1325
  // package.json
1280
- var version = "0.8.10";
1326
+ var version = "0.8.14";
1281
1327
 
1282
1328
  // src/core/util.ts
1283
1329
  import { accessSync, constants } from "fs";
@@ -1791,7 +1837,7 @@ var HindsightClient = class _HindsightClient {
1791
1837
  */
1792
1838
  async tree() {
1793
1839
  if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
1794
- const r = await this.req("GET", this.bankUrl("/knowledge-base/tree"));
1840
+ const r = await this.req("GET", this.bankUrl("/pages/tree"));
1795
1841
  if (await this.pagesUnsupported(r)) throw new KnowledgePagesUnavailableError();
1796
1842
  if (r.status === 404) return [];
1797
1843
  this.knowledgePagesSupported = true;
@@ -1839,7 +1885,7 @@ var HindsightClient = class _HindsightClient {
1839
1885
  if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
1840
1886
  const r = await this.req(
1841
1887
  "GET",
1842
- this.bankUrl(`/knowledge-base/pages/${encodeURIComponent(pageId)}`)
1888
+ this.bankUrl(`/pages/${encodeURIComponent(pageId)}`)
1843
1889
  );
1844
1890
  if (r.status === 404) throw new Error(`knowledge page not found: ${pageId}`);
1845
1891
  return shapePage(await r.json());
@@ -1852,7 +1898,7 @@ var HindsightClient = class _HindsightClient {
1852
1898
  const q = `?q=${encodeURIComponent(query)}&limit=${opts.limit ?? this.pageSearchLimit}`;
1853
1899
  const r = await this.req(
1854
1900
  "GET",
1855
- this.bankUrl(`/knowledge-base/search${q}`),
1901
+ this.bankUrl(`/pages/search${q}`),
1856
1902
  void 0,
1857
1903
  [],
1858
1904
  opts.timeoutMs
@@ -1871,7 +1917,7 @@ var HindsightClient = class _HindsightClient {
1871
1917
  /**
1872
1918
  * Seed the fixed page taxonomy as knowledge-base pages at the tree root, idempotently.
1873
1919
  *
1874
- * Matched by NAME, not id: `/knowledge-base/pages` mints its own `kp-…` id, so a stable
1920
+ * Matched by NAME, not id: `/pages` mints its own `kp-…` id, so a stable
1875
1921
  * client-chosen id isn't available to match on (unlike the old mental-model path, which keyed
1876
1922
  * off a slug). Names are unique per folder server-side, which makes them a sound key.
1877
1923
  *
@@ -1910,7 +1956,7 @@ var HindsightClient = class _HindsightClient {
1910
1956
  trigger: pageTriggerFor(pageTrigger, this.bank, page.name)
1911
1957
  };
1912
1958
  if (!hit) {
1913
- const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), body, [409]);
1959
+ const r = await this.req("POST", this.bankUrl("/pages"), body, [409]);
1914
1960
  if (await this.pagesUnsupported(r)) {
1915
1961
  this.log(
1916
1962
  `[bank] knowledge pages unavailable on ${this.apiUrl}; continuing without pages`
@@ -1937,7 +1983,7 @@ var HindsightClient = class _HindsightClient {
1937
1983
  if (triggerDrift) patch.trigger = pageTriggerPatch(body.trigger);
1938
1984
  const r = await this.req(
1939
1985
  "PATCH",
1940
- this.bankUrl(`/knowledge-base/nodes/${encodeURIComponent(hit.id)}`),
1986
+ this.bankUrl(`/pages/nodes/${encodeURIComponent(hit.id)}`),
1941
1987
  patch
1942
1988
  );
1943
1989
  if (await this.pagesUnsupported(r)) {
@@ -1991,7 +2037,7 @@ var HindsightClient = class _HindsightClient {
1991
2037
  if (!patch.trigger && !patch.source_query) continue;
1992
2038
  const r = await this.req(
1993
2039
  "PATCH",
1994
- this.bankUrl(`/knowledge-base/nodes/${encodeURIComponent(page.id)}`),
2040
+ this.bankUrl(`/pages/nodes/${encodeURIComponent(page.id)}`),
1995
2041
  patch
1996
2042
  );
1997
2043
  if (r.status === 404) continue;
@@ -2020,7 +2066,7 @@ var HindsightClient = class _HindsightClient {
2020
2066
  let pageId = args.relatesToPageId;
2021
2067
  if (!pageId) {
2022
2068
  const folderId = await this.ensureFolder("Initiatives");
2023
- const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), {
2069
+ const r = await this.req("POST", this.bankUrl("/pages"), {
2024
2070
  name: args.title,
2025
2071
  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),
2026
2072
  parent_id: folderId,
@@ -2048,7 +2094,7 @@ var HindsightClient = class _HindsightClient {
2048
2094
  /** Find a root folder by name (case-insensitive) or create it; returns its id. Fail-open to undefined. */
2049
2095
  async ensureFolder(name) {
2050
2096
  try {
2051
- const tree = await (await this.req("GET", this.bankUrl("/knowledge-base/tree"))).json();
2097
+ const tree = await (await this.req("GET", this.bankUrl("/pages/tree"))).json();
2052
2098
  const hit = (tree.roots || []).find(
2053
2099
  (n) => n.kind === "folder" && (n.name || "").toLowerCase() === name.toLowerCase()
2054
2100
  );
@@ -2057,7 +2103,7 @@ var HindsightClient = class _HindsightClient {
2057
2103
  if (error instanceof AccessDeniedError) throw error;
2058
2104
  }
2059
2105
  try {
2060
- const r = await this.req("POST", this.bankUrl("/knowledge-base/folders"), { name });
2106
+ const r = await this.req("POST", this.bankUrl("/pages/folders"), { name });
2061
2107
  return (await r.json()).id;
2062
2108
  } catch (error) {
2063
2109
  if (error instanceof AccessDeniedError) throw error;
@@ -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|knowledge-base)(?:\/[^/]+)*$/.test(
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|knowledge-base|consolidate|consolidation)(?:\/[^/]+)*)?$/.test(
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";
@@ -427,6 +425,54 @@ var RouterTransport = class {
427
425
  // ../../coding-agents/human-session.ts
428
426
  import { createHash as createHash2 } from "crypto";
429
427
  import { createRequire } from "module";
428
+
429
+ // src/harness/names.ts
430
+ var HARNESS_NAMES = [
431
+ "opencode",
432
+ // opencode v2 (binary `opencode2`) — a ground-up rewrite of the plugin API, loaded from the
433
+ // package's root index.js (src/opencode2.ts). Like opencode it has NO hook binary.
434
+ "opencode2",
435
+ // Kilo CLI is an opencode fork loaded as a persistent plugin (src/kilo.ts), so like opencode it
436
+ // has NO hook binary — deliberately absent from HOOK_BINS below.
437
+ "kilo",
438
+ // Cline CLI loads dist/cline.js through its native plugin manager; file hooks cannot inject.
439
+ "cline-cli",
440
+ // DeepSeek Harness loads dist/dsh.js as a native Cordis plugin (src/dsh.ts). Its Claude Code /
441
+ // Codex hook bridges are optional packages, so there is no hook binary to install either.
442
+ "dsh",
443
+ // pi loads dist/pi.js as an extension (src/pi.ts), and Prime Agent — a fork of pi — loads
444
+ // dist/prime-agent.js the same way (src/prime-agent.ts). Neither has a hook binary.
445
+ "pi",
446
+ "prime-agent",
447
+ "claude-code",
448
+ "cursor-cli",
449
+ "codex",
450
+ "dcode",
451
+ "antigravity-cli",
452
+ "devin-cli",
453
+ "copilot-cli",
454
+ "grok-build",
455
+ "qwen-code",
456
+ // Factory Droid is a per-prompt HOOK host: the installer wires ~/.factory/hooks.json and the
457
+ // stdio MCP registration in ~/.factory/mcp.json (see src/installer.ts).
458
+ "factory-droid",
459
+ // ZCode is a per-prompt HOOK host too, registered in its own CLI config
460
+ // (~/.zcode/cli/config.json) under `hooks.events` — never the user's real Claude Code settings.
461
+ "zcode",
462
+ // TraeCode is a per-prompt HOOK host registered in ~/.trae-cn/hooks.json (see src/installer.ts).
463
+ "traecode",
464
+ "kimi-code",
465
+ // WorkBuddy is a per-prompt HOOK host too: the installer wires ~/.workbuddy/settings.json and
466
+ // the stdio MCP registration in ~/.workbuddy/mcp.json (see src/installer.ts).
467
+ "workbuddy",
468
+ // CodeBuddy Code is the SAME @genie/agent-cli HOOK host, one product config apart (WorkBuddy only
469
+ // renames the home folder): the installer wires ~/.codebuddy/settings.json and the stdio MCP
470
+ // registration in CodeBuddy's priority-chained MCP file (see src/installer.ts). The CLI
471
+ // writes WorkBuddy's transcript format; the CodeBuddy IDE has a reader of its own.
472
+ "codebuddy"
473
+ ];
474
+
475
+ // ../../coding-agents/human-session.ts
430
476
  var HUMAN_TOKEN_PATTERN = /^mrh_[0-9a-f]{32}_[0-9a-f]{64}$/;
431
477
  var HumanLoginRequiredError = class extends AccessDeniedError {
432
478
  constructor() {
@@ -452,14 +498,14 @@ function humanRouterUrl(value) {
452
498
  if (url.search || url.hash) throw new HumanLoginRequiredError();
453
499
  return canonical2;
454
500
  }
455
- function secureEntry(routerUrl, credentialRef) {
501
+ function secureEntry(routerUrl, credentialRef, harness) {
456
502
  try {
457
- if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef)) {
503
+ if (!["darwin", "win32", "linux"].includes(process.platform) || !/^[A-Za-z0-9._-]{1,64}$/.test(credentialRef) || !HARNESS_NAMES.includes(harness)) {
458
504
  throw new HumanLoginRequiredError();
459
505
  }
460
506
  const { Entry: NativeEntry } = createRequire(import.meta.url)("@napi-rs/keyring");
461
- const service = `hindsight-memory-router:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
462
- return new NativeEntry(service, credentialRef, { linux: { store: "secret-service" } });
507
+ const service = `hindsight-memory-router:human-v2:${createHash2("sha256").update(humanRouterUrl(routerUrl)).digest("hex")}`;
508
+ return new NativeEntry(service, `${harness}:${credentialRef}`, { linux: { store: "secret-service" } });
463
509
  } catch {
464
510
  throw new HumanLoginRequiredError();
465
511
  }
@@ -477,9 +523,9 @@ function validateHumanSession(value, routerUrl, allowExpired = false) {
477
523
  routerUrl: session.routerUrl
478
524
  };
479
525
  }
480
- function readHumanSession(routerUrl, credentialRef) {
526
+ function readHumanSession(routerUrl, credentialRef, harness) {
481
527
  try {
482
- const password = secureEntry(routerUrl, credentialRef).getPassword();
528
+ const password = secureEntry(routerUrl, credentialRef, harness).getPassword();
483
529
  if (!password || password.length > 4096) throw new HumanLoginRequiredError();
484
530
  return validateHumanSession(JSON.parse(password), routerUrl);
485
531
  } catch {
@@ -491,11 +537,11 @@ function readHumanSession(routerUrl, credentialRef) {
491
537
  function harnessTransport(harness) {
492
538
  return transportForConfig(harness, loadManagedConfig(process.env, harness));
493
539
  }
494
- function transportForConfig(harness, { config: config2, principal }) {
540
+ function transportForConfig(harness, { config: config2, principal, principalId }) {
495
541
  if (principal.human) {
496
542
  const routerUrl = humanRouterUrl(config2.routerUrl);
497
543
  const reference = principal.human.credentialRef;
498
- let session = readHumanSession(routerUrl, reference);
544
+ let session = readHumanSession(routerUrl, reference, principalId);
499
545
  return new RouterTransport({
500
546
  routerUrl,
501
547
  access: principal,
@@ -503,7 +549,7 @@ function transportForConfig(harness, { config: config2, principal }) {
503
549
  tokenPattern: HUMAN_TOKEN_PATTERN,
504
550
  unauthorizedError: () => new HumanLoginRequiredError(),
505
551
  token: () => {
506
- session = readHumanSession(routerUrl, reference);
552
+ session = readHumanSession(routerUrl, reference, principalId);
507
553
  return session.token;
508
554
  }
509
555
  });
@@ -1348,7 +1394,7 @@ var PLUGIN_GOVERNED_BANK_FIELDS = [
1348
1394
  ];
1349
1395
 
1350
1396
  // package.json
1351
- var version = "0.8.10";
1397
+ var version = "0.8.14";
1352
1398
 
1353
1399
  // src/core/util.ts
1354
1400
  import { accessSync, constants } from "fs";
@@ -1881,7 +1927,7 @@ var HindsightClient = class _HindsightClient {
1881
1927
  */
1882
1928
  async tree() {
1883
1929
  if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
1884
- const r = await this.req("GET", this.bankUrl("/knowledge-base/tree"));
1930
+ const r = await this.req("GET", this.bankUrl("/pages/tree"));
1885
1931
  if (await this.pagesUnsupported(r)) throw new KnowledgePagesUnavailableError();
1886
1932
  if (r.status === 404) return [];
1887
1933
  this.knowledgePagesSupported = true;
@@ -1929,7 +1975,7 @@ var HindsightClient = class _HindsightClient {
1929
1975
  if (this.knowledgePagesSupported === false) throw new KnowledgePagesUnavailableError();
1930
1976
  const r = await this.req(
1931
1977
  "GET",
1932
- this.bankUrl(`/knowledge-base/pages/${encodeURIComponent(pageId)}`)
1978
+ this.bankUrl(`/pages/${encodeURIComponent(pageId)}`)
1933
1979
  );
1934
1980
  if (r.status === 404) throw new Error(`knowledge page not found: ${pageId}`);
1935
1981
  return shapePage(await r.json());
@@ -1942,7 +1988,7 @@ var HindsightClient = class _HindsightClient {
1942
1988
  const q = `?q=${encodeURIComponent(query)}&limit=${opts.limit ?? this.pageSearchLimit}`;
1943
1989
  const r = await this.req(
1944
1990
  "GET",
1945
- this.bankUrl(`/knowledge-base/search${q}`),
1991
+ this.bankUrl(`/pages/search${q}`),
1946
1992
  void 0,
1947
1993
  [],
1948
1994
  opts.timeoutMs
@@ -1961,7 +2007,7 @@ var HindsightClient = class _HindsightClient {
1961
2007
  /**
1962
2008
  * Seed the fixed page taxonomy as knowledge-base pages at the tree root, idempotently.
1963
2009
  *
1964
- * Matched by NAME, not id: `/knowledge-base/pages` mints its own `kp-…` id, so a stable
2010
+ * Matched by NAME, not id: `/pages` mints its own `kp-…` id, so a stable
1965
2011
  * client-chosen id isn't available to match on (unlike the old mental-model path, which keyed
1966
2012
  * off a slug). Names are unique per folder server-side, which makes them a sound key.
1967
2013
  *
@@ -2000,7 +2046,7 @@ var HindsightClient = class _HindsightClient {
2000
2046
  trigger: pageTriggerFor(pageTrigger, this.bank, page.name)
2001
2047
  };
2002
2048
  if (!hit) {
2003
- const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), body, [409]);
2049
+ const r = await this.req("POST", this.bankUrl("/pages"), body, [409]);
2004
2050
  if (await this.pagesUnsupported(r)) {
2005
2051
  this.log(
2006
2052
  `[bank] knowledge pages unavailable on ${this.apiUrl}; continuing without pages`
@@ -2027,7 +2073,7 @@ var HindsightClient = class _HindsightClient {
2027
2073
  if (triggerDrift) patch.trigger = pageTriggerPatch(body.trigger);
2028
2074
  const r = await this.req(
2029
2075
  "PATCH",
2030
- this.bankUrl(`/knowledge-base/nodes/${encodeURIComponent(hit.id)}`),
2076
+ this.bankUrl(`/pages/nodes/${encodeURIComponent(hit.id)}`),
2031
2077
  patch
2032
2078
  );
2033
2079
  if (await this.pagesUnsupported(r)) {
@@ -2081,7 +2127,7 @@ var HindsightClient = class _HindsightClient {
2081
2127
  if (!patch.trigger && !patch.source_query) continue;
2082
2128
  const r = await this.req(
2083
2129
  "PATCH",
2084
- this.bankUrl(`/knowledge-base/nodes/${encodeURIComponent(page.id)}`),
2130
+ this.bankUrl(`/pages/nodes/${encodeURIComponent(page.id)}`),
2085
2131
  patch
2086
2132
  );
2087
2133
  if (r.status === 404) continue;
@@ -2110,7 +2156,7 @@ var HindsightClient = class _HindsightClient {
2110
2156
  let pageId = args.relatesToPageId;
2111
2157
  if (!pageId) {
2112
2158
  const folderId = await this.ensureFolder("Initiatives");
2113
- const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), {
2159
+ const r = await this.req("POST", this.bankUrl("/pages"), {
2114
2160
  name: args.title,
2115
2161
  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),
2116
2162
  parent_id: folderId,
@@ -2138,7 +2184,7 @@ var HindsightClient = class _HindsightClient {
2138
2184
  /** Find a root folder by name (case-insensitive) or create it; returns its id. Fail-open to undefined. */
2139
2185
  async ensureFolder(name) {
2140
2186
  try {
2141
- const tree = await (await this.req("GET", this.bankUrl("/knowledge-base/tree"))).json();
2187
+ const tree = await (await this.req("GET", this.bankUrl("/pages/tree"))).json();
2142
2188
  const hit = (tree.roots || []).find(
2143
2189
  (n) => n.kind === "folder" && (n.name || "").toLowerCase() === name.toLowerCase()
2144
2190
  );
@@ -2147,7 +2193,7 @@ var HindsightClient = class _HindsightClient {
2147
2193
  if (error instanceof AccessDeniedError) throw error;
2148
2194
  }
2149
2195
  try {
2150
- const r = await this.req("POST", this.bankUrl("/knowledge-base/folders"), { name });
2196
+ const r = await this.req("POST", this.bankUrl("/pages/folders"), { name });
2151
2197
  return (await r.json()).id;
2152
2198
  } catch (error) {
2153
2199
  if (error instanceof AccessDeniedError) throw error;