borgmcp 2.0.8 → 2.0.10

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 (61) hide show
  1. package/README.md +2 -2
  2. package/dist/agent-runtime.d.ts +2 -0
  3. package/dist/agent-runtime.d.ts.map +1 -1
  4. package/dist/agent-runtime.js +5 -1
  5. package/dist/agent-runtime.js.map +1 -1
  6. package/dist/assimilate-cmd.d.ts +2 -0
  7. package/dist/assimilate-cmd.d.ts.map +1 -1
  8. package/dist/assimilate-cmd.js +2 -0
  9. package/dist/assimilate-cmd.js.map +1 -1
  10. package/dist/assimilate-deps.d.ts.map +1 -1
  11. package/dist/assimilate-deps.js +6 -0
  12. package/dist/assimilate-deps.js.map +1 -1
  13. package/dist/index.d.ts +13 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +38 -23
  16. package/dist/index.js.map +1 -1
  17. package/dist/regen-format.d.ts.map +1 -1
  18. package/dist/regen-format.js +6 -4
  19. package/dist/regen-format.js.map +1 -1
  20. package/dist/regen.js +2 -0
  21. package/dist/regen.js.map +1 -1
  22. package/dist/remote-client.d.ts +24 -9
  23. package/dist/remote-client.d.ts.map +1 -1
  24. package/dist/remote-client.js +329 -49
  25. package/dist/remote-client.js.map +1 -1
  26. package/dist/roster-render.d.ts +11 -0
  27. package/dist/roster-render.d.ts.map +1 -1
  28. package/dist/roster-render.js +41 -8
  29. package/dist/roster-render.js.map +1 -1
  30. package/dist/runtime-metadata.d.ts +13 -0
  31. package/dist/runtime-metadata.d.ts.map +1 -0
  32. package/dist/runtime-metadata.js +52 -0
  33. package/dist/runtime-metadata.js.map +1 -0
  34. package/dist/server-handshake.d.ts +2 -1
  35. package/dist/server-handshake.d.ts.map +1 -1
  36. package/dist/server-handshake.js +3 -0
  37. package/dist/server-handshake.js.map +1 -1
  38. package/dist/sync-roles-render.d.ts +2 -0
  39. package/dist/sync-roles-render.d.ts.map +1 -1
  40. package/dist/sync-roles-render.js +26 -7
  41. package/dist/sync-roles-render.js.map +1 -1
  42. package/dist/working-repo.d.ts +5 -7
  43. package/dist/working-repo.d.ts.map +1 -1
  44. package/dist/working-repo.js +30 -40
  45. package/dist/working-repo.js.map +1 -1
  46. package/docs/EXTRACTION_PROVENANCE.md +8 -7
  47. package/docs/LOCAL_SERVER.md +1 -1
  48. package/docs/RELEASING.md +19 -1
  49. package/package.json +2 -2
  50. package/src/agent-runtime.ts +8 -1
  51. package/src/assimilate-cmd.ts +3 -1
  52. package/src/assimilate-deps.ts +10 -4
  53. package/src/index.ts +58 -27
  54. package/src/regen-format.ts +12 -5
  55. package/src/regen.ts +2 -0
  56. package/src/remote-client.ts +382 -43
  57. package/src/roster-render.ts +58 -8
  58. package/src/runtime-metadata.ts +67 -0
  59. package/src/server-handshake.ts +7 -2
  60. package/src/sync-roles-render.ts +24 -7
  61. package/src/working-repo.ts +30 -41
@@ -9,11 +9,30 @@
9
9
  *
10
10
  * The shape mirrors the worker's `NonClobberSyncResult`.
11
11
  */
12
+ const BIDI_CONTROL_RE = /\p{Bidi_Control}/u;
13
+ /** Escape cube-controlled text before it reaches Markdown or a terminal. */
14
+ export function escapeSyncDisplay(value) {
15
+ return [...value].map((char) => {
16
+ const code = char.codePointAt(0);
17
+ if (code === 0x0a)
18
+ return '⏎';
19
+ if (code < 0x20 || (code >= 0x7f && code <= 0x9f))
20
+ return `\\u{${code.toString(16)}}`;
21
+ if (BIDI_CONTROL_RE.test(char) || code === 0x2028 || code === 0x2029) {
22
+ return `\\u{${code.toString(16)}}`;
23
+ }
24
+ if (char === '`')
25
+ return '\\u{60}';
26
+ if ('\\*_[]()<>&#|~'.includes(char))
27
+ return `\\${char}`;
28
+ return char;
29
+ }).join('');
30
+ }
12
31
  /** Truncate long fragment bodies for at-a-glance diffs. */
13
32
  function trunc(s, n = 200) {
14
33
  if (s == null)
15
34
  return '(absent)';
16
- const flat = s.replace(/\n/g, '⏎');
35
+ const flat = escapeSyncDisplay(s);
17
36
  return flat.length > n ? flat.slice(0, n) + '…' : flat;
18
37
  }
19
38
  /**
@@ -45,7 +64,7 @@ export function renderSyncRolesResult(result, templateName) {
45
64
  const mode = result.dryRun
46
65
  ? '**DRY RUN** (review conflicts below; re-run with `apply: true` + a `decisions` map to commit)'
47
66
  : '**APPLIED**';
48
- const lines = [`## borg_sync-roles — ${mode}`, `Template: ${templateName}`, ''];
67
+ const lines = [`## borg_sync-roles — ${mode}`, `Template: ${escapeSyncDisplay(templateName)}`, ''];
49
68
  // Gather all fragments across roles + taxonomy for tallying.
50
69
  const allFragments = [
51
70
  ...result.roles.flatMap((r) => r.fragments),
@@ -74,7 +93,7 @@ export function renderSyncRolesResult(result, templateName) {
74
93
  : applied
75
94
  ? '✓ accepted — template version applied'
76
95
  : '↩ kept your version';
77
- lines.push(`- **${f.label}** \`${f.key}\` ${status}`);
96
+ lines.push(`- **${escapeSyncDisplay(f.label)}** \`${escapeSyncDisplay(f.key)}\` ${status}`);
78
97
  lines.push(` - cube (current): "${trunc(f.cubeValue)}"`);
79
98
  lines.push(` - template (new): "${trunc(f.templateValue)}"`);
80
99
  }
@@ -87,7 +106,7 @@ export function renderSyncRolesResult(result, templateName) {
87
106
  lines.push('These keys in your `decisions` map did not correspond to any classified conflict this run ' +
88
107
  '(typo or stale key) — their intended accept had NO effect. Check the exact keys against the conflicts above:');
89
108
  for (const k of unmatched) {
90
- lines.push(`- \`${k}\``);
109
+ lines.push(`- \`${escapeSyncDisplay(k)}\``);
91
110
  }
92
111
  lines.push('');
93
112
  }
@@ -96,17 +115,17 @@ export function renderSyncRolesResult(result, templateName) {
96
115
  lines.push(`### Additions (safe — auto-applied, zero clobber risk)`);
97
116
  for (const r of newRoles) {
98
117
  const note = result.dryRun ? '(new role — would be created)' : '✓ created';
99
- lines.push(`- new role **${r.name}** ${note}`);
118
+ lines.push(`- new role **${escapeSyncDisplay(r.name)}** ${note}`);
100
119
  }
101
120
  for (const f of adds) {
102
121
  const note = result.dryRun ? '(would be added)' : '✓ added';
103
- lines.push(`- **${f.label}** \`${f.key}\` ${note}`);
122
+ lines.push(`- **${escapeSyncDisplay(f.label)}** \`${escapeSyncDisplay(f.key)}\` ${note}`);
104
123
  }
105
124
  lines.push('');
106
125
  }
107
126
  // ── Custom roles (never touched) ──
108
127
  if (customRoles.length > 0) {
109
- lines.push(`### Custom roles (untouched): ${customRoles.map((r) => r.name).join(', ')}`);
128
+ lines.push(`### Custom roles (untouched): ${customRoles.map((r) => escapeSyncDisplay(r.name)).join(', ')}`);
110
129
  lines.push('');
111
130
  }
112
131
  // ── Clean no-op ──
@@ -1 +1 @@
1
- {"version":3,"file":"sync-roles-render.js","sourceRoot":"","sources":["../src/sync-roles-render.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-roles-render.js","sourceRoot":"","sources":["../src/sync-roles-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAyB5C,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC;QAClC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,GAAG,CAAC;QAC9B,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;YAAE,OAAO,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC;QACtF,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrE,OAAO,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC;QACrC,CAAC;QACD,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QACnC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,IAAI,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,2DAA2D;AAC3D,SAAS,KAAK,CAAC,CAAgB,EAAE,CAAC,GAAG,GAAG;IACtC,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,UAAU,CAAC;IACjC,MAAM,IAAI,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAA4B,EAC5B,YAAoB;IAEpB,oEAAoE;IACpE,sDAAsD;IACtD,yEAAyE;IACzE,qEAAqE;IACrE,sEAAsE;IACtE,0EAA0E;IAC1E,wDAAwD;IACxD,MAAM,WAAW,GAAG,MAA4C,CAAC;IACjE,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS,IAAI,SAAS,IAAI,WAAW,EAAE,CAAC;QAChE,OAAO;YACL,uDAAuD;YACvD,EAAE;YACF,+JAA+J;YAC/J,EAAE;YACF,2FAA2F;SAC5F,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM;QACxB,CAAC,CAAC,+FAA+F;QACjG,CAAC,CAAC,aAAa,CAAC;IAClB,MAAM,KAAK,GAAa,CAAC,wBAAwB,IAAI,EAAE,EAAE,aAAa,iBAAiB,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAE7G,6DAA6D;IAC7D,MAAM,YAAY,GAAmB;QACnC,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3C,GAAG,MAAM,CAAC,QAAQ;KACnB,CAAC;IACF,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC;IAE9E,2DAA2D;IAC3D,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CACR,SAAS,SAAS,CAAC,MAAM,0EAA0E,CACpG,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,KAAK,CAAC,IAAI,CACR,2HAA2H;gBACzH,+GAA+G;gBAC/G,0GAA0G,CAC7G,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;QAC7F,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM;gBAC1B,CAAC,CAAC,2BAA2B;gBAC7B,CAAC,CAAC,OAAO;oBACP,CAAC,CAAC,uCAAuC;oBACzC,CAAC,CAAC,qBAAqB,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;YAC7F,KAAK,CAAC,IAAI,CAAC,wBAAwB,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC1D,KAAK,CAAC,IAAI,CAAC,wBAAwB,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAChE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,2EAA2E;IAC3E,MAAM,SAAS,GAAG,MAAM,CAAC,kBAAkB,IAAI,EAAE,CAAC;IAClD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CACR,SAAS,SAAS,CAAC,MAAM,uDAAuD,CACjF,CAAC;QACF,KAAK,CAAC,IAAI,CACR,4FAA4F;YAC1F,8GAA8G,CACjH,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,uDAAuD;IACvD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;QACrE,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,WAAW,CAAC;YAC1E,KAAK,CAAC,IAAI,CAAC,gBAAgB,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,qCAAqC;IACrC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CACP,iCAAiC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjG,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,oBAAoB;IACpB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzE,KAAK,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;AACpC,CAAC"}
@@ -7,8 +7,9 @@
7
7
  */
8
8
  export interface WorkingRepo {
9
9
  name: string | null;
10
- /** Canonical host/path identity, never a raw Git remote URL. */
10
+ /** Canonical public HTTPS identity, never a raw Git remote URL. */
11
11
  origin: string | null;
12
+ state?: 'known' | 'unknown' | 'unavailable' | 'rejected';
12
13
  }
13
14
  export interface WorkingRepoDeps {
14
15
  runGit?: (cwd: string, args: string[]) => {
@@ -17,13 +18,10 @@ export interface WorkingRepoDeps {
17
18
  };
18
19
  }
19
20
  /**
20
- * Convert a Git remote to a non-secret `host/org/repo` identity.
21
- *
22
- * URL userinfo, query strings, fragments, scheme, and SCP-style user prefixes
23
- * are deliberately discarded. Inputs that cannot identify a host and path are
24
- * treated as unreportable rather than forwarded verbatim.
21
+ * Convert a Git remote to the shared canonical public repository identity.
22
+ * Hostile or credential-bearing inputs are rejected rather than sanitized.
25
23
  */
26
- export declare function canonicalizeWorkingRepoIdentity(origin: string): string | null;
24
+ export declare function canonicalizeWorkingRepoIdentity(origin: string): WorkingRepo | null;
27
25
  /**
28
26
  * Return a reportable identity for the caller's current directory.
29
27
  *
@@ -1 +1 @@
1
- {"version":3,"file":"working-repo.d.ts","sourceRoot":"","sources":["../src/working-repo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gEAAgE;IAChE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,KACX;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACxD;AAkBD;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,MAAsB,EAC3B,IAAI,GAAE,eAAoB,GACzB,WAAW,CAeb"}
1
+ {"version":3,"file":"working-repo.d.ts","sourceRoot":"","sources":["../src/working-repo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,mEAAmE;IACnE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,KACX;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACxD;AAYD;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAWlF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,MAAsB,EAC3B,IAAI,GAAE,eAAoB,GACzB,WAAW,CA0Bb"}
@@ -6,6 +6,7 @@
6
6
  * included in lifecycle metadata.
7
7
  */
8
8
  import { spawnSync } from 'node:child_process';
9
+ import { canonicalizeRepositoryIdentity } from 'borgmcp-shared/runtime-metadata';
9
10
  function defaultRunGit(cwd, args) {
10
11
  const result = spawnSync('git', args, { cwd, encoding: 'utf-8' });
11
12
  return { status: result.status, stdout: result.stdout };
@@ -14,45 +15,20 @@ function trimmed(value) {
14
15
  const normalized = value?.trim();
15
16
  return normalized ? normalized : null;
16
17
  }
17
- function nameFromIdentity(identity) {
18
- const lastPathSegment = identity.replace(/\/$/, '').split('/').pop();
19
- const name = lastPathSegment?.replace(/\.git$/i, '').trim();
20
- return name || null;
21
- }
22
18
  /**
23
- * Convert a Git remote to a non-secret `host/org/repo` identity.
24
- *
25
- * URL userinfo, query strings, fragments, scheme, and SCP-style user prefixes
26
- * are deliberately discarded. Inputs that cannot identify a host and path are
27
- * treated as unreportable rather than forwarded verbatim.
19
+ * Convert a Git remote to the shared canonical public repository identity.
20
+ * Hostile or credential-bearing inputs are rejected rather than sanitized.
28
21
  */
29
22
  export function canonicalizeWorkingRepoIdentity(origin) {
30
- const raw = origin.trim();
31
- if (!raw)
32
- return null;
33
- // Remote clients send this canonical form on subsequent lifecycle calls.
34
- const canonical = raw.match(/^([A-Za-z0-9.-]+)\/([^?#\s]+)$/);
35
- if (canonical) {
36
- const host = canonical[1].toLowerCase();
37
- const path = canonical[2].replace(/^\/+|\/+$/g, '').replace(/\.git$/i, '');
38
- return host && path ? `${host}/${path}` : null;
39
- }
40
23
  try {
41
- const url = new URL(origin);
42
- if (!['http:', 'https:', 'ssh:', 'git:'].includes(url.protocol))
43
- return null;
44
- const path = url.pathname.replace(/^\/+|\/+$/g, '').replace(/\.git$/i, '');
45
- return url.hostname && path ? `${url.hostname.toLowerCase()}/${path}` : null;
24
+ const canonical = canonicalizeRepositoryIdentity(origin.trim());
25
+ return {
26
+ name: canonical.working_repo_name,
27
+ origin: canonical.working_repo_origin,
28
+ state: 'known',
29
+ };
46
30
  }
47
31
  catch {
48
- // SCP-style SSH remote: discard its optional user prefix and URL-like
49
- // query/fragment suffix before accepting only host + repository path.
50
- const match = raw.match(/^(?:[^@\s/:]+@)?([A-Za-z0-9.-]+):\/?([^?#\s]+)(?:[?#].*)?$/);
51
- if (match) {
52
- const host = match[1].toLowerCase();
53
- const path = match[2].replace(/^\/+|\/+$/g, '').replace(/\.git$/i, '');
54
- return host && path ? `${host}/${path}` : null;
55
- }
56
32
  return null;
57
33
  }
58
34
  }
@@ -65,17 +41,31 @@ export function canonicalizeWorkingRepoIdentity(origin) {
65
41
  */
66
42
  export function resolveWorkingRepo(cwd = process.cwd(), deps = {}) {
67
43
  const runGit = deps.runGit ?? defaultRunGit;
68
- const rootResult = runGit(cwd, ['rev-parse', '--show-toplevel']);
44
+ let rootResult;
45
+ try {
46
+ rootResult = runGit(cwd, ['rev-parse', '--show-toplevel']);
47
+ }
48
+ catch {
49
+ return { name: null, origin: null, state: 'unavailable' };
50
+ }
69
51
  const root = rootResult.status === 0 ? trimmed(rootResult.stdout) : null;
70
52
  if (!root) {
71
- return { name: null, origin: null };
53
+ return { name: null, origin: null, state: 'unknown' };
54
+ }
55
+ let originResult;
56
+ try {
57
+ originResult = runGit(cwd, ['config', '--get', 'remote.origin.url']);
58
+ }
59
+ catch {
60
+ return { name: null, origin: null, state: 'unavailable' };
72
61
  }
73
- const originResult = runGit(cwd, ['config', '--get', 'remote.origin.url']);
74
62
  const originRaw = originResult.status === 0 ? trimmed(originResult.stdout) : null;
75
- const origin = originRaw ? canonicalizeWorkingRepoIdentity(originRaw) : null;
76
- return {
77
- name: origin ? nameFromIdentity(origin) : null,
78
- origin,
63
+ if (!originRaw)
64
+ return { name: null, origin: null, state: 'unknown' };
65
+ return canonicalizeWorkingRepoIdentity(originRaw) ?? {
66
+ name: null,
67
+ origin: null,
68
+ state: 'rejected',
79
69
  };
80
70
  }
81
71
  //# sourceMappingURL=working-repo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"working-repo.js","sourceRoot":"","sources":["../src/working-repo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"working-repo.js","sourceRoot":"","sources":["../src/working-repo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AAejF,SAAS,aAAa,CAAC,GAAW,EAAE,IAAc;IAChD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAClE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,OAAO,CAAC,KAAgC;IAC/C,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACjC,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,+BAA+B,CAAC,MAAc;IAC5D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,8BAA8B,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChE,OAAO;YACL,IAAI,EAAE,SAAS,CAAC,iBAAiB;YACjC,MAAM,EAAE,SAAS,CAAC,mBAAmB;YACrC,KAAK,EAAE,OAAO;SACf,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,OAAO,CAAC,GAAG,EAAE,EAC3B,OAAwB,EAAE;IAE1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC;IAC5C,IAAI,UAAU,CAAC;IACf,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAC5D,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACxD,CAAC;IAED,IAAI,YAAY,CAAC;IACjB,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAC5D,CAAC;IACD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACtE,OAAO,+BAA+B,CAAC,SAAS,CAAC,IAAI;QACnD,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,UAAU;KAClB,CAAC;AACJ,CAAC"}
@@ -8,7 +8,7 @@ The extraction copied the monorepo's `client/src/` production boundary and top-l
8
8
 
9
9
  ## Deliberate Transformations
10
10
 
11
- - Replaced the monorepo dependency on `borgmcp-shared` with the exact audited registry release `borgmcp-shared@0.5.0` and a fresh standalone lockfile.
11
+ - Replaced the monorepo dependency on `borgmcp-shared` with the exact audited registry release `borgmcp-shared@0.6.1` and a fresh standalone lockfile.
12
12
  - Replaced local template, role-section, drone-address, and log high-water-mark implementations with `borgmcp-shared` exports.
13
13
  - Removed monorepo-only website anti-drift tests and re-anchored remaining filesystem tests to this repository.
14
14
  - Removed consumer lifecycle hooks, parent-directory deployment scripts, minification, and private integration-environment configuration.
@@ -21,8 +21,9 @@ The extraction copied the monorepo's `client/src/` production boundary and top-l
21
21
  lightweight release tag failed before packaging. The reviewed `2.0.1`
22
22
  recovery, `2.0.2`, `2.0.3`, `2.0.4`, `2.0.5`, and `2.0.6` successors were
23
23
  published and registry-verified. The immutable `2.0.7` workflow failed before
24
- package creation or npm publication, so the next candidate identity is `2.0.8`.
25
- Extraction and versioning do not authorize publication.
24
+ package creation or npm publication. The `2.0.8` and `2.0.9` successors were
25
+ published and registry-verified, so the next candidate identity is `2.0.10`. Extraction and
26
+ versioning do not authorize publication.
26
27
 
27
28
  ## Review Holds
28
29
 
@@ -37,9 +38,9 @@ artifact reaches no hosted authority.
37
38
  Local enrollment now uses the reviewed client-generated credential/retry
38
39
  contract, with a pre-request `PENDING` record in the local 0600-permission seat
39
40
  store, exact-tuple ambiguous retry, and verified activation. The contract now
40
- resolves to the audited registry `borgmcp-shared@0.5.0`. The matching
41
- `borgmcp-server@0.1.8` release is published and registry-verified. Client
42
- `borgmcp@2.0.6` is published and registry-verified. The immutable `v2.0.7`
41
+ resolves to the audited registry `borgmcp-shared@0.6.1`. The matching
42
+ `borgmcp-server@0.1.17` release is published and registry-verified. Client
43
+ `borgmcp@2.0.9` is published and registry-verified. The immutable `v2.0.7`
43
44
  attempt failed before publication and remains preserved. Publication of the next
44
- candidate remains gated by reviewed `v2.0.8` source, a fresh annotated tag, and
45
+ candidate remains gated by reviewed `v2.0.10` source, a fresh annotated tag, and
45
46
  exact registry integrity and signature verification.
@@ -139,7 +139,7 @@ The default discovery endpoint is `https://127.0.0.1:7091`. Explicit `--host` va
139
139
 
140
140
  ## Release status
141
141
 
142
- This self-hosted path consumes the published `borgmcp-shared@0.5.0` v3 registry
142
+ This self-hosted path consumes the published `borgmcp-shared@0.6.1` v3 registry
143
143
  release. The matching server owner-enrollment, cube-create, attach, restart, log,
144
144
  and SSE implementation must also pass the full process-level local dogfood gate.
145
145
  Until that gate opens the self-hosted path remains preview-only, and the client
package/docs/RELEASING.md CHANGED
@@ -76,6 +76,24 @@ rerun that tag, version, or workflow. The next candidate uses the unused
76
76
  `v2.0.8` identity from a fresh reviewed protected-main commit and requires the
77
77
  complete release gate again.
78
78
 
79
+ The annotated `v2.0.8` tag object
80
+ `7b5a4929534abfa97a65e278df230adfdb842d8f` peels to protected-main commit
81
+ `8bc796d8fc4e307dca593138fb080984662c7d62`. Workflow run `30012098601`, attempt 1,
82
+ successfully published that exact source as `borgmcp@2.0.8`; the registry records integrity
83
+ `sha512-az1IKG4VNwAF/8PKEVK88gdRoFFBqgtl6ObXX+CcJadavNqkwJ9UFeP8LOJ2js911mIiXEm8c4xDf6MnO0GOng==`.
84
+ Never move, replace, reuse, or rerun that tag or workflow. Its successor used
85
+ the fresh `v2.0.9` identity from a reviewed protected-main commit and passed the
86
+ complete release gate.
87
+
88
+ The annotated `v2.0.9` tag object
89
+ `3a88bb4f46143789803a4e57f52b94d762f1ca9b` peels to protected-main commit
90
+ `18084fc486a041f3438f584a97d218c01a5e0399`. Workflow run `30047299013`, attempt 1,
91
+ successfully published that exact source as `borgmcp@2.0.9`; the registry records integrity
92
+ `sha512-lf0TZ8ZcpHv/Nt3LkY/IGxkUFkg3weavF+rLv6xLImDDhLIaZly1y8jUdo3UuheQVhrLnLcxoz37Myr4mPx9lg==`.
93
+ Never move, replace, reuse, or rerun that tag or workflow. The next candidate
94
+ uses the unused `v2.0.10` identity from a fresh reviewed protected-main commit
95
+ and requires the complete release gate again.
96
+
79
97
  ## Release Prerequisites
80
98
 
81
99
  The standalone client was extracted from private-monorepo commit
@@ -85,7 +103,7 @@ Before creating the release tag, independently verify all of these conditions:
85
103
  - the extraction review confirms no private backend secrets, deployment
86
104
  configuration, customer data, local state, or duplicated shared contracts
87
105
  entered the public package;
88
- - the exact audited registry dependency `borgmcp-shared@0.5.0` remains locked to
106
+ - the exact audited registry dependency `borgmcp-shared@0.6.1` remains locked to
89
107
  its canonical tarball and integrity;
90
108
  - the client and matching server pass the complete local dogfood gate;
91
109
  - the selected stable client version is unused and the exact release commit is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "Coordinate AI coding agents in shared cubes. Works with Claude Code, Codex, and OpenCode.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -68,7 +68,7 @@
68
68
  },
69
69
  "dependencies": {
70
70
  "@modelcontextprotocol/sdk": "^1.0.4",
71
- "borgmcp-shared": "0.5.0",
71
+ "borgmcp-shared": "0.6.1",
72
72
  "chalk": "^5.3.0",
73
73
  "prompts": "^2.4.2",
74
74
  "which": "^4.0.0"
@@ -24,10 +24,17 @@ function isAgentKind(value: string | undefined): value is AgentKind {
24
24
  * already-installed clients.
25
25
  */
26
26
  export function resolveSessionAgentKind(env: NodeJS.ProcessEnv = process.env): AgentKind {
27
+ return resolveReportableSessionAgentKind(env) ?? 'claude';
28
+ }
29
+
30
+ /** Resolve only positively identified CLI state for advisory server reporting. */
31
+ export function resolveReportableSessionAgentKind(
32
+ env: NodeJS.ProcessEnv = process.env
33
+ ): AgentKind | null {
27
34
  if (isAgentKind(env[BORG_AGENT_KIND_ENV])) return env[BORG_AGENT_KIND_ENV];
28
35
  if (env[BORG_OPENCODE_ENV] === '1') return 'opencode';
29
36
  if (env[BORG_CODEX_REMOTE_WAKE_ENV] === '1') return 'codex';
30
- return 'claude';
37
+ return null;
31
38
  }
32
39
 
33
40
  /**
@@ -49,6 +49,7 @@ import type { ExpectedBinding, FinalizeServerSeatOutcome, PersistedLocalSeat } f
49
49
  import type { SeatBinding, BindPendingSeatOutcome } from './seats.js';
50
50
  import { createHash } from 'node:crypto';
51
51
  import { buildOpenCodeLaunchArgs, type LaunchApprovalDecision } from './cli-tool-approval.js';
52
+ import { resolveWorkingRepo, type WorkingRepo } from './working-repo.js';
52
53
 
53
54
  const PRIVATE_STATE_UNAVAILABLE_COPY = [
54
55
  'Borg could not safely prepare its private local state.',
@@ -259,7 +260,7 @@ export interface AssimilateDeps {
259
260
  assimilate: (
260
261
  apiUrl: string,
261
262
  token: string,
262
- params: { cube_id: string; role_id: string; hostname?: string | null; prior_drone_id?: string; remint_invalid_prior?: boolean; model?: string | null; agent_kind?: 'claude' | 'codex' | 'opencode' | null; session_operation?: ServerSessionOperation; session_expected?: ExpectedBinding; revalidate_at_prepare?: boolean },
263
+ params: { cube_id: string; role_id: string; hostname?: string | null; prior_drone_id?: string; remint_invalid_prior?: boolean; model?: string | null; agent_kind?: 'claude' | 'codex' | 'opencode' | null; working_repo?: WorkingRepo; session_operation?: ServerSessionOperation; session_expected?: ExpectedBinding; revalidate_at_prepare?: boolean },
263
264
  serverTrustIdentity?: string,
264
265
  ) => Promise<AssimilateResult>;
265
266
 
@@ -1183,6 +1184,7 @@ export async function runAssimilate(
1183
1184
  hostname: deps.getHostname(),
1184
1185
  agent_kind: cli,
1185
1186
  model: effectiveModel,
1187
+ working_repo: resolveWorkingRepo(projectRoot),
1186
1188
  ...(reattachPriorId ? { prior_drone_id: reattachPriorId } : {}),
1187
1189
  ...(remintInvalidPrior ? { remint_invalid_prior: true } : {}),
1188
1190
  session_operation: sessionOperation,
@@ -16,6 +16,7 @@ import { createInterface } from 'node:readline/promises';
16
16
  import prompts from 'prompts';
17
17
  import { readinessProbeEnv } from './readiness-probe.js';
18
18
  import { resolveMcpBinaryPath } from './self-path.js';
19
+ import { buildRuntimeMetadataReport } from './runtime-metadata.js';
19
20
 
20
21
  import type { AssimilateDeps } from './assimilate-cmd.js';
21
22
  import {
@@ -322,10 +323,15 @@ export function buildDefaultAssimilateDeps(): AssimilateDeps {
322
323
  cubeId: params.cube_id,
323
324
  roleId: params.role_id,
324
325
  operation,
325
- ...(params.prior_drone_id === undefined
326
- ? {}
327
- : { priorDroneId: params.prior_drone_id }),
328
- },
326
+ ...(params.prior_drone_id === undefined
327
+ ? {}
328
+ : { priorDroneId: params.prior_drone_id }),
329
+ runtimeMetadata: buildRuntimeMetadataReport({
330
+ agentKind: params.agent_kind,
331
+ reportedModel: params.model,
332
+ workingRepo: params.working_repo,
333
+ }),
334
+ },
329
335
  pending.credential,
330
336
  { fetchImpl: trust.fetchImpl },
331
337
  );
package/src/index.ts CHANGED
@@ -43,11 +43,14 @@ import {
43
43
  patchTaxonomyClass,
44
44
  deleteRole,
45
45
  getCube,
46
+ getCubeForManagement,
47
+ resolveLocalManageAuthority,
46
48
  listRoles,
47
49
  syncRoles,
48
50
  applyTemplate,
49
51
  whoami,
50
52
  roleRationale,
53
+ type LocalManageAuthority,
51
54
  } from './remote-client.js';
52
55
  import {
53
56
  getTemplate,
@@ -90,7 +93,11 @@ import {
90
93
  formatWakePathPrefix,
91
94
  shouldShowWakePathWarning,
92
95
  } from './stream-status.js';
93
- import { formatRoleAgentLabel, formatWorkingRepoLabel, renderRoster } from './roster-render.js';
96
+ import {
97
+ RUNTIME_METADATA_ADVISORY,
98
+ renderRoster,
99
+ renderRuntimeMetadataLines,
100
+ } from './roster-render.js';
94
101
  import { resolveWorkingRepo } from './working-repo.js';
95
102
  import {
96
103
  DroneEvictedError,
@@ -107,6 +114,7 @@ import { initConsolePrefix, consolePrefix } from './console-prefix.js';
107
114
  import {
108
115
  resolveSessionAgentKind,
109
116
  } from './codex-app-wake.js';
117
+ import { resolveReportableSessionAgentKind } from './agent-runtime.js';
110
118
  import {
111
119
  connectOpenCodeDrone,
112
120
  injectOpenCodeEntry,
@@ -142,9 +150,38 @@ import {
142
150
  */
143
151
  async function applyTemplateToCube(
144
152
  cubeId: string,
145
- template: Template
153
+ template: Template,
154
+ authority?: LocalManageAuthority,
146
155
  ): Promise<{ created: number; updated: number }> {
147
- return await applyTemplate(cubeId, template.name);
156
+ return await applyTemplate(cubeId, template.name, authority);
157
+ }
158
+
159
+ export async function runApplyTemplateTool(
160
+ cubeId: string,
161
+ template: Template,
162
+ authority: LocalManageAuthority,
163
+ deps: {
164
+ applyTemplate?: typeof applyTemplate;
165
+ getCubeForManagement?: typeof getCubeForManagement;
166
+ updateCube?: typeof updateCube;
167
+ } = {},
168
+ ): Promise<{ summary: { created: number; updated: number }; cubeDirectiveNote: string }> {
169
+ const apply = deps.applyTemplate ?? applyTemplate;
170
+ const read = deps.getCubeForManagement ?? getCubeForManagement;
171
+ const update = deps.updateCube ?? updateCube;
172
+ const summary = await apply(cubeId, template.name, authority);
173
+ const cubeForRules = await read(cubeId, {
174
+ operation: `read template target ${JSON.stringify(template.name)}`,
175
+ cubeName: cubeId === authority.active.cubeId ? authority.active.name : cubeId,
176
+ noMutation: 'No template fragments were changed.',
177
+ }, authority.active, authority.connection);
178
+ const newCubeDirective = resolveCubeDirectiveForApply(cubeForRules.cube_directive, template);
179
+ if (newCubeDirective === null) return { summary, cubeDirectiveNote: '' };
180
+ await update(cubeId, { cube_directive: newCubeDirective }, authority.active, authority.connection);
181
+ return {
182
+ summary,
183
+ cubeDirectiveNote: ' Template cube directive applied (cube directive was empty).',
184
+ };
148
185
  }
149
186
 
150
187
  /**
@@ -336,6 +373,7 @@ export async function main() {
336
373
  const result = await regen(active.sessionToken, active.apiUrl, {
337
374
  since,
338
375
  reportedModel,
376
+ agentKind: resolveReportableSessionAgentKind(),
339
377
  workingRepo: resolveWorkingRepo(),
340
378
  serverTrustIdentity: active.serverTrustIdentity,
341
379
  });
@@ -412,6 +450,7 @@ export async function main() {
412
450
  // — an evicted/revoked seat FAILS server-side and is surfaced).
413
451
  try {
414
452
  const result = await regen(active!.sessionToken, active!.apiUrl, {
453
+ agentKind: resolveReportableSessionAgentKind(),
415
454
  workingRepo: resolveWorkingRepo(),
416
455
  serverTrustIdentity: active!.serverTrustIdentity,
417
456
  });
@@ -1052,21 +1091,18 @@ export async function main() {
1052
1091
  return { content: [{ type: 'text', text: 'No drones in this cube yet.' }] };
1053
1092
  }
1054
1093
  const rolesById = new Map(roles.map((r: any) => [r.id, r]));
1055
- const lines = drones.map((d: any) => {
1056
- const r = rolesById.get(d.role_id) as any;
1057
- const roleLabel = formatRoleAgentLabel(r?.name ?? '?', d.agent_kind);
1058
- const wakeClass =
1094
+ const lines = drones.map((d: any) => {
1095
+ const r = rolesById.get(d.role_id) as any;
1096
+ const wakeClass =
1059
1097
  d.wake_path_alert_class && d.wake_path_alert_class !== 'independent'
1060
1098
  ? ` — wake-path-class: ${d.wake_path_alert_class}`
1061
1099
  : '';
1062
- const reportedModel = d.reported_model
1063
- ? `Reported model: ${d.reported_model}`
1064
- : ' — Reported model: not reported';
1065
- const workingRepo = formatWorkingRepoLabel(d);
1066
- const workingRepoText = workingRepo ? ` — ${workingRepo}` : '';
1067
- return `- **${d.label}** (id: ${d.id}) role: ${roleLabel} (${d.role_id}) — last seen ${d.last_seen}${wakeClass}${reportedModel}${workingRepoText}`;
1068
- });
1069
- return { content: [{ type: 'text', text: `Drones in cube ${cubeId} (${drones.length}):\n\n${lines.join('\n')}` }] };
1100
+ return [
1101
+ `- **${d.label}** (id: ${d.id}) — Role: ${r?.name ?? '?'} (${d.role_id})last seen ${d.last_seen}${wakeClass}`,
1102
+ ...renderRuntimeMetadataLines(d),
1103
+ ].join('\n');
1104
+ });
1105
+ return { content: [{ type: 'text', text: `Drones in cube ${cubeId} (${drones.length}):\n\n_${RUNTIME_METADATA_ADVISORY}_\n\n${lines.join('\n')}` }] };
1070
1106
  }
1071
1107
 
1072
1108
  case 'borg_list-roles': {
@@ -1119,18 +1155,13 @@ export async function main() {
1119
1155
  if (!template) {
1120
1156
  throw new Error(`Unknown template "${templateName}". Available: ${listTemplateNames().join(', ')}`);
1121
1157
  }
1122
- const summary = await applyTemplateToCube(cubeId, template);
1123
-
1124
- // Sprint 14: optionally write template's cube_directive to the
1125
- // cube. No-clobber discipline only fills empty directives,
1126
- // never overwrites operator-customized text.
1127
- let cubeDirectiveNote = '';
1128
- const cubeForRules = await getCube(cubeId);
1129
- const newCubeDirective = resolveCubeDirectiveForApply(cubeForRules.cube_directive, template);
1130
- if (newCubeDirective !== null) {
1131
- await updateCube(cubeId, { cube_directive: newCubeDirective });
1132
- cubeDirectiveNote = ' Template cube directive applied (cube directive was empty).';
1133
- }
1158
+ const active = await requireActiveCube();
1159
+ const authority = await resolveLocalManageAuthority(active, {
1160
+ operation: `apply template ${JSON.stringify(templateName)}`,
1161
+ cubeName: cubeId === active.cubeId ? active.name : cubeId,
1162
+ noMutation: 'No template fragments were changed.',
1163
+ });
1164
+ const { summary, cubeDirectiveNote } = await runApplyTemplateTool(cubeId, template, authority);
1134
1165
 
1135
1166
  return { content: [{ type: 'text', text: `Applied template **${templateName}** to cube ${cubeId} — ${summary.created} role(s) created, ${summary.updated} updated.${cubeDirectiveNote}` }] };
1136
1167
  }
@@ -12,7 +12,10 @@ import {
12
12
  } from 'borgmcp-shared/templates';
13
13
  import { parseRoleSections } from 'borgmcp-shared/role-section';
14
14
  import { formatDroneAddressToken } from 'borgmcp-shared/drone-address';
15
- import { formatRoleAgentLabel } from './roster-render.js';
15
+ import {
16
+ RUNTIME_METADATA_ADVISORY,
17
+ renderRuntimeMetadataLines,
18
+ } from './roster-render.js';
16
19
  import { shellEscape } from './shell-escape.js';
17
20
  import { resolveInboxMonitorPath } from './self-path.js';
18
21
 
@@ -488,10 +491,12 @@ export function formatRegenMarkdown(
488
491
 
489
492
  const droneOverview =
490
493
  result.drones
491
- .map((d: any) => {
492
- const role = result.roles.find((r: any) => r.id === d.role_id);
493
- const roleLabel = formatRoleAgentLabel(role?.name ?? '?', d.agent_kind);
494
- return `- **${d.label}** (${roleLabel}) — last seen ${humanAgo(new Date(d.last_seen))}`;
494
+ .map((d: any) => {
495
+ const role = result.roles.find((r: any) => r.id === d.role_id);
496
+ return [
497
+ `- **${d.label}** (Role: ${role?.name ?? '?'}) — last seen ${humanAgo(new Date(d.last_seen))}`,
498
+ ...renderRuntimeMetadataLines(d),
499
+ ].join('\n');
495
500
  })
496
501
  .join('\n') || '_(no drones connected)_';
497
502
 
@@ -611,6 +616,8 @@ export function formatRegenMarkdown(
611
616
  roleOverview,
612
617
  '',
613
618
  `## Connected drones`,
619
+ `_${RUNTIME_METADATA_ADVISORY}_`,
620
+ '',
614
621
  droneOverview,
615
622
  '',
616
623
  `## Cube log`,
package/src/regen.ts CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  type AgentKind,
27
27
  } from './regen-format.js';
28
28
  import { resolveSessionAgentKind } from './codex-app-wake.js';
29
+ import { resolveReportableSessionAgentKind } from './agent-runtime.js';
29
30
  import { handleVersionFlag } from './version.js';
30
31
  import { gateAllowsActivation } from './launch-gate.js';
31
32
  import { resolveWorkingRepo } from './working-repo.js';
@@ -85,6 +86,7 @@ async function main(): Promise<void> {
85
86
  let result: Awaited<ReturnType<typeof regen>> | null = null;
86
87
  try {
87
88
  result = await regen(active.sessionToken, active.apiUrl, {
89
+ agentKind: resolveReportableSessionAgentKind(),
88
90
  workingRepo: resolveWorkingRepo(),
89
91
  serverTrustIdentity: active.serverTrustIdentity,
90
92
  });