@cotal-ai/cli 0.6.0 → 0.8.0

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 (135) hide show
  1. package/dist/commands/channels.d.ts +4 -2
  2. package/dist/commands/channels.d.ts.map +1 -1
  3. package/dist/commands/channels.js +17 -18
  4. package/dist/commands/channels.js.map +1 -1
  5. package/dist/commands/console.d.ts.map +1 -1
  6. package/dist/commands/console.js +13 -29
  7. package/dist/commands/console.js.map +1 -1
  8. package/dist/commands/down-manifest.d.ts +6 -0
  9. package/dist/commands/down-manifest.d.ts.map +1 -0
  10. package/dist/commands/down-manifest.js +282 -0
  11. package/dist/commands/down-manifest.js.map +1 -0
  12. package/dist/commands/down.d.ts +5 -3
  13. package/dist/commands/down.d.ts.map +1 -1
  14. package/dist/commands/down.js +30 -3
  15. package/dist/commands/down.js.map +1 -1
  16. package/dist/commands/history.d.ts.map +1 -1
  17. package/dist/commands/history.js +6 -13
  18. package/dist/commands/history.js.map +1 -1
  19. package/dist/commands/join.d.ts.map +1 -1
  20. package/dist/commands/join.js +20 -7
  21. package/dist/commands/join.js.map +1 -1
  22. package/dist/commands/meshes.d.ts +5 -0
  23. package/dist/commands/meshes.d.ts.map +1 -0
  24. package/dist/commands/meshes.js +25 -0
  25. package/dist/commands/meshes.js.map +1 -0
  26. package/dist/commands/mint.d.ts.map +1 -1
  27. package/dist/commands/mint.js +2 -1
  28. package/dist/commands/mint.js.map +1 -1
  29. package/dist/commands/setup.d.ts.map +1 -1
  30. package/dist/commands/setup.js +66 -16
  31. package/dist/commands/setup.js.map +1 -1
  32. package/dist/commands/spawn-manifest.d.ts +10 -0
  33. package/dist/commands/spawn-manifest.d.ts.map +1 -0
  34. package/dist/commands/spawn-manifest.js +197 -0
  35. package/dist/commands/spawn-manifest.js.map +1 -0
  36. package/dist/commands/spawn.d.ts +4 -2
  37. package/dist/commands/spawn.d.ts.map +1 -1
  38. package/dist/commands/spawn.js +84 -25
  39. package/dist/commands/spawn.js.map +1 -1
  40. package/dist/commands/topology.d.ts +10 -0
  41. package/dist/commands/topology.d.ts.map +1 -0
  42. package/dist/commands/topology.js +46 -0
  43. package/dist/commands/topology.js.map +1 -0
  44. package/dist/commands/up.d.ts +4 -0
  45. package/dist/commands/up.d.ts.map +1 -1
  46. package/dist/commands/up.js +223 -6
  47. package/dist/commands/up.js.map +1 -1
  48. package/dist/commands/use.d.ts +7 -0
  49. package/dist/commands/use.d.ts.map +1 -0
  50. package/dist/commands/use.js +27 -0
  51. package/dist/commands/use.js.map +1 -0
  52. package/dist/commands/web.d.ts.map +1 -1
  53. package/dist/commands/web.js +60 -28
  54. package/dist/commands/web.js.map +1 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +27 -2
  57. package/dist/index.js.map +1 -1
  58. package/dist/lib/connect.d.ts +74 -0
  59. package/dist/lib/connect.d.ts.map +1 -0
  60. package/dist/lib/connect.js +91 -0
  61. package/dist/lib/connect.js.map +1 -0
  62. package/dist/lib/delivery-proc.d.ts.map +1 -1
  63. package/dist/lib/delivery-proc.js +2 -1
  64. package/dist/lib/delivery-proc.js.map +1 -1
  65. package/dist/lib/manager-proc.d.ts +4 -0
  66. package/dist/lib/manager-proc.d.ts.map +1 -1
  67. package/dist/lib/manager-proc.js +17 -0
  68. package/dist/lib/manager-proc.js.map +1 -1
  69. package/dist/lib/manifest/apply.d.ts +29 -0
  70. package/dist/lib/manifest/apply.d.ts.map +1 -0
  71. package/dist/lib/manifest/apply.js +138 -0
  72. package/dist/lib/manifest/apply.js.map +1 -0
  73. package/dist/lib/manifest/errors.d.ts +21 -0
  74. package/dist/lib/manifest/errors.d.ts.map +1 -0
  75. package/dist/lib/manifest/errors.js +19 -0
  76. package/dist/lib/manifest/errors.js.map +1 -0
  77. package/dist/lib/manifest/index.d.ts +13 -0
  78. package/dist/lib/manifest/index.d.ts.map +1 -0
  79. package/dist/lib/manifest/index.js +21 -0
  80. package/dist/lib/manifest/index.js.map +1 -0
  81. package/dist/lib/manifest/ledger.d.ts +81 -0
  82. package/dist/lib/manifest/ledger.d.ts.map +1 -0
  83. package/dist/lib/manifest/ledger.js +213 -0
  84. package/dist/lib/manifest/ledger.js.map +1 -0
  85. package/dist/lib/manifest/live.d.ts +25 -0
  86. package/dist/lib/manifest/live.d.ts.map +1 -0
  87. package/dist/lib/manifest/live.js +61 -0
  88. package/dist/lib/manifest/live.js.map +1 -0
  89. package/dist/lib/manifest/model.d.ts +71 -0
  90. package/dist/lib/manifest/model.d.ts.map +1 -0
  91. package/dist/lib/manifest/model.js +2 -0
  92. package/dist/lib/manifest/model.js.map +1 -0
  93. package/dist/lib/manifest/preflight.d.ts +12 -0
  94. package/dist/lib/manifest/preflight.d.ts.map +1 -0
  95. package/dist/lib/manifest/preflight.js +43 -0
  96. package/dist/lib/manifest/preflight.js.map +1 -0
  97. package/dist/lib/manifest/prepare.d.ts +57 -0
  98. package/dist/lib/manifest/prepare.d.ts.map +1 -0
  99. package/dist/lib/manifest/prepare.js +95 -0
  100. package/dist/lib/manifest/prepare.js.map +1 -0
  101. package/dist/lib/manifest/render.d.ts +41 -0
  102. package/dist/lib/manifest/render.d.ts.map +1 -0
  103. package/dist/lib/manifest/render.js +177 -0
  104. package/dist/lib/manifest/render.js.map +1 -0
  105. package/dist/lib/manifest/resolve.d.ts +5 -0
  106. package/dist/lib/manifest/resolve.d.ts.map +1 -0
  107. package/dist/lib/manifest/resolve.js +185 -0
  108. package/dist/lib/manifest/resolve.js.map +1 -0
  109. package/dist/lib/manifest/schema.d.ts +103 -0
  110. package/dist/lib/manifest/schema.d.ts.map +1 -0
  111. package/dist/lib/manifest/schema.js +77 -0
  112. package/dist/lib/manifest/schema.js.map +1 -0
  113. package/dist/lib/manifest/spawn-plan.d.ts +87 -0
  114. package/dist/lib/manifest/spawn-plan.d.ts.map +1 -0
  115. package/dist/lib/manifest/spawn-plan.js +75 -0
  116. package/dist/lib/manifest/spawn-plan.js.map +1 -0
  117. package/dist/lib/meshes.d.ts +2 -0
  118. package/dist/lib/meshes.d.ts.map +1 -0
  119. package/dist/lib/meshes.js +6 -0
  120. package/dist/lib/meshes.js.map +1 -0
  121. package/dist/lib/onboard.js +6 -6
  122. package/dist/lib/onboard.js.map +1 -1
  123. package/dist/lib/paths.js +1 -1
  124. package/dist/lib/paths.js.map +1 -1
  125. package/dist/lib/status.d.ts.map +1 -1
  126. package/dist/lib/status.js +2 -1
  127. package/dist/lib/status.js.map +1 -1
  128. package/dist/lib/transient.d.ts +7 -6
  129. package/dist/lib/transient.d.ts.map +1 -1
  130. package/dist/lib/transient.js +6 -25
  131. package/dist/lib/transient.js.map +1 -1
  132. package/dist/web/graph.html +207 -0
  133. package/dist/web/graph.js +508 -0
  134. package/dist/web/index.html +6 -0
  135. package/package.json +6 -2
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Live-mesh helpers for `cotal spawn -f` — the network half: a transient, invisible probe endpoint
3
+ * for reading the roster + membership feed, and the admin-tier control calls that drive the running
4
+ * manager's `launch` op. (Channel-registry reads use `readChannelRegistry`, which connects itself.)
5
+ */
6
+ import { CotalEndpoint, CONTROL_ADMIN } from "@cotal-ai/core";
7
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
8
+ /** Connect a transient, presence-invisible endpoint (it watches the roster but doesn't register
9
+ * itself) used to read live state + drive control. The caller stops it. */
10
+ export async function connectProbe(conn) {
11
+ const ep = new CotalEndpoint({
12
+ space: conn.space,
13
+ servers: conn.server,
14
+ creds: conn.creds,
15
+ channels: [],
16
+ consume: false,
17
+ registerPresence: false, // an invisible probe — don't add ourselves to the roster we read
18
+ watchPresence: true,
19
+ card: { name: "spawn-f", kind: "endpoint" },
20
+ });
21
+ ep.on("error", () => { }); // a presence/control hiccup must never crash the deploy
22
+ await ep.start();
23
+ return ep;
24
+ }
25
+ /** Let the presence KV replay settle (roster count steady across two polls, ≤1s), then snapshot the
26
+ * live peers — mirrors the dedup probe in `cotal spawn`. */
27
+ export async function settleRoster(ep) {
28
+ let prev = -1;
29
+ for (let i = 0; i < 10; i++) {
30
+ await sleep(100);
31
+ const n = ep.getRoster().length;
32
+ if (n === prev)
33
+ break;
34
+ prev = n;
35
+ }
36
+ return ep.getRoster();
37
+ }
38
+ /** Poll the manager's control plane until it answers `ps` — it may have just been started detached,
39
+ * so it needs a moment to connect + come up. Returns false on timeout. */
40
+ export async function waitManagerReady(ep, timeoutMs = 20_000) {
41
+ const deadline = Date.now() + timeoutMs;
42
+ while (Date.now() < deadline) {
43
+ try {
44
+ const r = await ep.requestControl(CONTROL_ADMIN, { op: "ps" });
45
+ if (r.ok)
46
+ return true;
47
+ }
48
+ catch {
49
+ /* manager not answering yet */
50
+ }
51
+ await sleep(500);
52
+ }
53
+ return false;
54
+ }
55
+ /** Ask the running manager to launch one resolved agent from the run spec, on the ADMIN tier (the
56
+ * `launch` op is operator-only — a spawn-capable agent must not reach it). The manager derives
57
+ * `.cotal/run/<runId>.json` itself; we pass the runId, never a path. */
58
+ export async function launchAgent(ep, runId, name) {
59
+ return ep.requestControl(CONTROL_ADMIN, { op: "launch", args: { runId, name } });
60
+ }
61
+ //# sourceMappingURL=live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live.js","sourceRoot":"","sources":["../../../src/lib/manifest/live.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAoC,MAAM,gBAAgB,CAAC;AAQhG,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAEnF;4EAC4E;AAC5E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc;IAC/C,MAAM,EAAE,GAAG,IAAI,aAAa,CAAC;QAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,MAAM;QACpB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,KAAK;QACd,gBAAgB,EAAE,KAAK,EAAE,iEAAiE;QAC1F,aAAa,EAAE,IAAI;QACnB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE;KAC5C,CAAC,CAAC;IACH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,wDAAwD;IAClF,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IACjB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;6DAC6D;AAC7D,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAiB;IAClD,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC;QAChC,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM;QACtB,IAAI,GAAG,CAAC,CAAC;IACX,CAAC;IACD,OAAO,EAAE,CAAC,SAAS,EAAE,CAAC;AACxB,CAAC;AAED;2EAC2E;AAC3E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAiB,EAAE,SAAS,GAAG,MAAM;IAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,cAAc,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,+BAA+B;QACjC,CAAC;QACD,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;yEAEyE;AACzE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAiB,EAAE,KAAa,EAAE,IAAY;IAC9E,OAAO,EAAE,CAAC,cAAc,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The resolved manifest model — the typed output of the pure pipeline (parse → schema →
3
+ * normalize/invert → semantic), with channel-centric membership inverted into per-agent ACLs.
4
+ * Preflight (persona reads + the `include` merge) and the plan/apply stages consume this.
5
+ */
6
+ import type { ChannelDefaults, DeliveryClass } from "@cotal-ai/core";
7
+ export type PersonaPermissions = "reject" | "include";
8
+ /** A channel after normalization: `allowSubscribe` defaulted to `subscribe`, dedup'd. The
9
+ * registry-card fields (description/instructions/replay…) seed the channel registry verbatim. */
10
+ export interface ResolvedChannel {
11
+ name: string;
12
+ description?: string;
13
+ instructions?: string;
14
+ /** Active read set (boot subscription). */
15
+ subscribe: string[];
16
+ /** Read ACL — may read/join. Defaults to {@link subscribe}; always a superset of it. */
17
+ allowSubscribe: string[];
18
+ /** Post ACL (default-deny: empty ⇒ nobody posts). */
19
+ allowPublish: string[];
20
+ replay?: boolean;
21
+ replayWindow?: string;
22
+ deliveryClass?: DeliveryClass;
23
+ }
24
+ /** Per-agent ACLs inverted from the channel membership lists — the manifest-declared access only
25
+ * (a persona's own grants, under `include`, are merged on top in preflight). 1:1 with AgentDef. */
26
+ export interface AgentPolicy {
27
+ subscribe: string[];
28
+ allowSubscribe: string[];
29
+ allowPublish: string[];
30
+ }
31
+ /** A resolved agent: its identity/behavior source (a persona file or fully inline) plus the
32
+ * manifest-declared policy. Behavior fields here are the manifest's overrides; the persona
33
+ * default is filled in during preflight (which reads the file). */
34
+ export interface ResolvedAgent {
35
+ /** The `agents:` key — also the requested spawn name in v1. */
36
+ name: string;
37
+ /** Connector type to spawn with (agent-entry `agent:` ?? top-level `agent:`). */
38
+ agentType: string;
39
+ /** Resolved persona path (absolute), or undefined for an inline agent. */
40
+ persona?: string;
41
+ /** Manifest override of the persona's model (or the inline model). */
42
+ model?: string;
43
+ role?: string;
44
+ /** Manifest card blurb (override / inline). */
45
+ description?: string;
46
+ /** Manifest persona body (REPLACES the file body when set; the sole body for inline). */
47
+ instructions?: string;
48
+ /** Manifest capabilities — win over the persona's when present. */
49
+ capabilities?: string[];
50
+ /** Effective policy for THIS agent: per-agent override ?? top-level ?? "reject". */
51
+ personaPermissions: PersonaPermissions;
52
+ /** ACLs inverted from the channels this agent appears in (pre persona-merge). */
53
+ policy: AgentPolicy;
54
+ }
55
+ /** The fully resolved, validated manifest — channel-centric on disk, per-agent here. */
56
+ export interface ResolvedManifest {
57
+ space: string;
58
+ broker?: {
59
+ servers?: string;
60
+ host?: string;
61
+ auth?: boolean;
62
+ };
63
+ runtime?: "pty" | "tmux" | "cmux";
64
+ personaPermissions: PersonaPermissions;
65
+ defaults?: ChannelDefaults;
66
+ agents: ResolvedAgent[];
67
+ channels: ResolvedChannel[];
68
+ /** Absolute path the manifest was loaded from — anchors relative persona refs + error output. */
69
+ sourcePath: string;
70
+ }
71
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/lib/manifest/model.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAErE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtD;kGACkG;AAClG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,wFAAwF;IACxF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,qDAAqD;IACrD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;oGACoG;AACpG,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;oEAEoE;AACpE,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oFAAoF;IACpF,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,iFAAiF;IACjF,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,wFAAwF;AACxF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7D,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,iGAAiG;IACjG,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/lib/manifest/model.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { ResolvedManifest } from "./model.js";
2
+ import { type AgentWarning, type PreparedAgent } from "./prepare.js";
3
+ export interface PreparedManifest {
4
+ manifest: ResolvedManifest;
5
+ agents: PreparedAgent[];
6
+ /** Non-fatal diagnostics to render in dry-run / topology view. */
7
+ warnings: AgentWarning[];
8
+ }
9
+ /** Read personas + merge. Behavior defaults always come from the file (under both policies); only
10
+ * the persona's *permissions* are gated by `personaPermissions`. */
11
+ export declare function preparePersonas(manifest: ResolvedManifest): PreparedManifest;
12
+ //# sourceMappingURL=preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../../src/lib/manifest/preflight.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAgB,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGnF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,kEAAkE;IAClE,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;qEACqE;AACrE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CA4B5E"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Preflight — the I/O half of resolution: read each agent's persona file and merge it with the
3
+ * manifest (via the pure {@link prepareAgent}). Returns the launch-ready {@link PreparedManifest}
4
+ * (agents + diagnostics) or throws {@link ManifestError} with every persona problem located.
5
+ *
6
+ * Live checks that need the network (broker reachability for `up -f` vs `spawn -f`) or the connector
7
+ * registry stay in the command wiring; this is the fail-fast, file-level half.
8
+ */
9
+ import { loadAgentFile } from "@cotal-ai/core";
10
+ import { prepareAgent } from "./prepare.js";
11
+ import { ManifestError } from "./errors.js";
12
+ /** Read personas + merge. Behavior defaults always come from the file (under both policies); only
13
+ * the persona's *permissions* are gated by `personaPermissions`. */
14
+ export function preparePersonas(manifest) {
15
+ const declared = new Set(manifest.channels.map((c) => c.name));
16
+ const issues = [];
17
+ const warnings = [];
18
+ const agents = [];
19
+ for (const a of manifest.agents) {
20
+ let persona;
21
+ if (a.persona) {
22
+ try {
23
+ persona = loadAgentFile(a.persona);
24
+ }
25
+ catch (e) {
26
+ const code = e.code;
27
+ issues.push({
28
+ message: code === "ENOENT" ? `persona file not found: ${a.persona}` : e.message,
29
+ path: ["agents", a.name],
30
+ });
31
+ continue;
32
+ }
33
+ }
34
+ const r = prepareAgent(a, persona, declared);
35
+ issues.push(...r.issues);
36
+ warnings.push(...r.warnings);
37
+ agents.push(r.prepared);
38
+ }
39
+ if (issues.length)
40
+ throw new ManifestError(manifest.sourcePath, issues);
41
+ return { manifest, agents, warnings };
42
+ }
43
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../../src/lib/manifest/preflight.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAyC,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,aAAa,EAAsB,MAAM,aAAa,CAAC;AAShE;qEACqE;AACrE,MAAM,UAAU,eAAe,CAAC,QAA0B;IACxD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,OAA6B,CAAC;QAClC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,IAAI,CAAC;gBACH,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,GAAI,CAA2B,CAAC,IAAI,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAE,CAAW,CAAC,OAAO;oBAC1F,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC;iBACzB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;QACH,CAAC;QACD,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,MAAM,CAAC,MAAM;QAAE,MAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Prepare stage — resolve each agent's effective launch identity + ACLs by merging its persona
3
+ * (read from disk) with the manifest. Pure: it takes already-loaded {@link AgentDef}s so it stays
4
+ * unit-testable; the fs/live I/O lives in {@link ./preflight.js}.
5
+ *
6
+ * Behavior fields (model/role/description/body) are persona-default + manifest-override. Access is
7
+ * governed by {@link PersonaPermissions}: under `reject` the manifest is the sole source; under
8
+ * `include` a persona's own grants are inherited **only for channels the manifest does not declare**
9
+ * (one authority per channel), concrete-only (wildcards rejected), and surfaced loudly.
10
+ */
11
+ import { type AgentDef } from "@cotal-ai/core";
12
+ import type { AgentPolicy, ResolvedAgent } from "./model.js";
13
+ import type { ManifestIssue } from "./errors.js";
14
+ /** Persona grants that fall outside the manifest's channels (unmanaged credential scopes) + the
15
+ * capabilities inherited from the persona — the data the loud dry-run section renders. */
16
+ export interface InheritedScopes {
17
+ subscribe: string[];
18
+ allowSubscribe: string[];
19
+ allowPublish: string[];
20
+ capabilities: string[];
21
+ }
22
+ /** A non-fatal diagnostic to surface in dry-run / topology view. `loud` = call it out prominently
23
+ * (e.g. an agent with capabilities but no channel access). */
24
+ export interface AgentWarning {
25
+ agent: string;
26
+ message: string;
27
+ loud?: boolean;
28
+ }
29
+ /** The fully resolved launch form for one agent — what the spawn path needs. */
30
+ export interface PreparedAgent {
31
+ name: string;
32
+ agentType: string;
33
+ /** Persona file path (or undefined for an inline agent). */
34
+ persona?: string;
35
+ /** Effective values (manifest override ?? persona default). */
36
+ model?: string;
37
+ role?: string;
38
+ description?: string;
39
+ /** Effective persona body (manifest `instructions` REPLACES the file body; sole body for inline). */
40
+ body?: string;
41
+ /** Effective merged capabilities. */
42
+ capabilities: string[];
43
+ capabilitySource: "manifest" | "persona" | "none";
44
+ /** Effective merged per-channel ACLs (manifest + persona-undeclared under `include`). */
45
+ policy: AgentPolicy;
46
+ /** Persona grants outside manifest channels + inherited caps (empty under `reject`/inline). */
47
+ inherited: InheritedScopes;
48
+ }
49
+ export interface PreparedResult {
50
+ prepared: PreparedAgent;
51
+ issues: ManifestIssue[];
52
+ warnings: AgentWarning[];
53
+ }
54
+ /** Merge one resolved agent with its (optional) loaded persona, given the set of channel names the
55
+ * manifest declares. Returns the launch form plus any errors/warnings. */
56
+ export declare function prepareAgent(agent: ResolvedAgent, persona: AgentDef | undefined, declared: Set<string>): PreparedResult;
57
+ //# sourceMappingURL=prepare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare.d.ts","sourceRoot":"","sources":["../../../src/lib/manifest/prepare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;2FAC2F;AAC3F,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;+DAC+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,yFAAyF;IACzF,MAAM,EAAE,WAAW,CAAC;IACpB,+FAA+F;IAC/F,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;2EAC2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,GAAG,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,cAAc,CAqFvH"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Prepare stage — resolve each agent's effective launch identity + ACLs by merging its persona
3
+ * (read from disk) with the manifest. Pure: it takes already-loaded {@link AgentDef}s so it stays
4
+ * unit-testable; the fs/live I/O lives in {@link ./preflight.js}.
5
+ *
6
+ * Behavior fields (model/role/description/body) are persona-default + manifest-override. Access is
7
+ * governed by {@link PersonaPermissions}: under `reject` the manifest is the sole source; under
8
+ * `include` a persona's own grants are inherited **only for channels the manifest does not declare**
9
+ * (one authority per channel), concrete-only (wildcards rejected), and surfaced loudly.
10
+ */
11
+ import { isConcreteChannel } from "@cotal-ai/core";
12
+ /** Merge one resolved agent with its (optional) loaded persona, given the set of channel names the
13
+ * manifest declares. Returns the launch form plus any errors/warnings. */
14
+ export function prepareAgent(agent, persona, declared) {
15
+ const issues = [];
16
+ const warnings = [];
17
+ const at = ["agents", agent.name];
18
+ // Behavior: persona default, manifest override wins. Inline `instructions` REPLACE the body.
19
+ const model = agent.model ?? persona?.model;
20
+ const role = agent.role ?? persona?.role;
21
+ const description = agent.description ?? persona?.description;
22
+ const body = agent.instructions ?? persona?.persona;
23
+ // Capabilities: manifest wins; else inherited from the persona only under `include`.
24
+ let capabilities = [];
25
+ let capabilitySource = "none";
26
+ if (agent.capabilities?.length) {
27
+ capabilities = [...agent.capabilities];
28
+ capabilitySource = "manifest";
29
+ }
30
+ else if (agent.personaPermissions === "include" && persona?.capabilities?.length) {
31
+ capabilities = [...persona.capabilities];
32
+ capabilitySource = "persona";
33
+ }
34
+ // Access. Start from the manifest-inverted policy (all its channels are declared by construction).
35
+ const policy = {
36
+ subscribe: [...agent.policy.subscribe],
37
+ allowSubscribe: [...agent.policy.allowSubscribe],
38
+ allowPublish: [...agent.policy.allowPublish],
39
+ };
40
+ const inherited = { subscribe: [], allowSubscribe: [], allowPublish: [], capabilities: [] };
41
+ if (agent.personaPermissions === "include" && persona) {
42
+ const personaAllowSub = persona.allowSubscribe ?? persona.subscribe ?? [];
43
+ // Reject persona WILDCARD grants in v1 (they'd re-introduce wildcards via the persona file and
44
+ // break the per-channel partition).
45
+ for (const [field, list] of [["subscribe", persona.subscribe], ["allowSubscribe", personaAllowSub], ["allowPublish", persona.allowPublish]])
46
+ for (const ch of list ?? [])
47
+ if (!isConcreteChannel(ch))
48
+ issues.push({ message: `persona ${field} "${ch}" is a wildcard — not supported in v1 (declare concrete channels)`, path: at });
49
+ // Persona grants apply ONLY to channels the manifest does not declare (manifest owns its own).
50
+ const undeclared = (list) => (list ?? []).filter((c) => isConcreteChannel(c) && !declared.has(c));
51
+ inherited.subscribe = undeclared(persona.subscribe);
52
+ inherited.allowSubscribe = undeclared(personaAllowSub);
53
+ inherited.allowPublish = undeclared(persona.allowPublish);
54
+ inherited.capabilities = capabilitySource === "persona" ? capabilities : [];
55
+ policy.subscribe = dedupe([...policy.subscribe, ...inherited.subscribe]);
56
+ policy.allowSubscribe = dedupe([...policy.allowSubscribe, ...inherited.allowSubscribe]);
57
+ policy.allowPublish = dedupe([...policy.allowPublish, ...inherited.allowPublish]);
58
+ }
59
+ // Defensive: the merged read set must stay within the merged read ACL.
60
+ const missing = policy.subscribe.filter((c) => !policy.allowSubscribe.includes(c));
61
+ if (missing.length)
62
+ issues.push({ message: `merged subscribe [${missing.join(", ")}] not within allowSubscribe`, path: at });
63
+ // Warn on an agent the manifest declares but never grants channel access — likely a typo, but
64
+ // valid (a DM/control-only peer). Loud when it nonetheless carries capabilities.
65
+ const noAccess = !policy.subscribe.length && !policy.allowSubscribe.length && !policy.allowPublish.length;
66
+ if (noAccess)
67
+ warnings.push({
68
+ agent: agent.name,
69
+ loud: capabilities.length > 0,
70
+ message: capabilities.length
71
+ ? `declared with capabilities [${capabilities.join(", ")}] but NO channel access (DM/control-only — a powerful non-channel grant)`
72
+ : `declared but has no channel access from the manifest (DM/control-only unless a persona under \`include\` grants scopes)`,
73
+ });
74
+ return {
75
+ prepared: {
76
+ name: agent.name,
77
+ agentType: agent.agentType,
78
+ persona: agent.persona,
79
+ model,
80
+ role,
81
+ description,
82
+ body,
83
+ capabilities,
84
+ capabilitySource,
85
+ policy,
86
+ inherited,
87
+ },
88
+ issues,
89
+ warnings,
90
+ };
91
+ }
92
+ function dedupe(xs) {
93
+ return [...new Set(xs)];
94
+ }
95
+ //# sourceMappingURL=prepare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare.js","sourceRoot":"","sources":["../../../src/lib/manifest/prepare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { PreparedManifest } from "./preflight.js";
2
+ import type { AgentWarning } from "./prepare.js";
3
+ import type { AgentPlan, ChannelPlan, UnmanagedReport } from "./spawn-plan.js";
4
+ /** The full `topology view`: header, channel access, agent access, persona-inherited scopes, warnings. */
5
+ export declare function renderTopology(p: PreparedManifest): string;
6
+ /** The `cotal up -f --dry-run` plan: a fresh mesh creates everything, so the grouping is simply
7
+ * "will create" — broker + channels + agents — followed by the full access view. Mutates nothing. */
8
+ export declare function renderUpPlan(p: PreparedManifest, server: string): string;
9
+ /** The loud "persona grants outside manifest channels" section — unmanaged credential scopes that
10
+ * an old persona ref drags in under `personaPermissions: include`. Returns "" when there are none. */
11
+ export declare function renderInherited(p: PreparedManifest): string;
12
+ /** Render the non-fatal warnings (empty-ACL agents, loud when they carry capabilities). */
13
+ export declare function renderWarnings(warnings: AgentWarning[]): string;
14
+ /** The `cotal spawn -f` plan / `--dry-run`: deploy onto a RUNNING mesh. Groups channels and agents
15
+ * by disposition (create / exists-unmanaged / owned · will-create / already-owned / stale), then the
16
+ * SECURITY block + persona-inherited access. Creation-only — an existing unmanaged card is shown
17
+ * desired-vs-live, never patched. */
18
+ export declare function renderSpawnPlan(p: PreparedManifest, channels: ChannelPlan, agents: AgentPlan, unmanaged: UnmanagedReport, ctx: {
19
+ server: string;
20
+ runId: string;
21
+ dryRun: boolean;
22
+ }): string;
23
+ /** The SECURITY block: unmanaged actors observed with read access to a manifest-declared channel —
24
+ * an isolation conflict on the shared mesh — phrased as an explicit LOWER BOUND (presence + the
25
+ * broker membership feed; live-only core subscriptions aren't observable when the feed is absent).
26
+ * Returns "" only when there's nothing to say AND the feed was readable. */
27
+ export declare function renderUnmanaged(u: UnmanagedReport): string;
28
+ /** Post-apply summary for `cotal spawn -f`: what was created/launched, what was left untouched, the
29
+ * SECURITY block, and the exact ownership-scoped teardown command + ledger path. */
30
+ export declare function renderSpawnSummary(ctx: {
31
+ space: string;
32
+ server: string;
33
+ runId: string;
34
+ ledgerPath: string;
35
+ manifestPath: string;
36
+ created: string[];
37
+ launched: string[];
38
+ existsUnmanaged: string[];
39
+ unmanaged: UnmanagedReport;
40
+ }): string;
41
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/lib/manifest/render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAU/E,0GAA0G;AAC1G,wBAAgB,cAAc,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAwC1D;AAED;sGACsG;AACtG,wBAAgB,YAAY,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAWxE;AAED;uGACuG;AACvG,wBAAgB,eAAe,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAqB3D;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAG/D;AAED;;;sCAGsC;AACtC,wBAAgB,eAAe,CAC7B,CAAC,EAAE,gBAAgB,EACnB,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,eAAe,EAC1B,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACtD,MAAM,CA+BR;AAED;;;6EAG6E;AAC7E,wBAAgB,eAAe,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAmB1D;AAED;qFACqF;AACrF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,eAAe,CAAC;CAC5B,GAAG,MAAM,CAWT"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Human-readable rendering of a prepared manifest — the `cotal topology view` output and the shared
3
+ * pieces of `--dry-run`. The native verbs get humane labels here so the field names aren't the only
4
+ * explanation (UX): `subscribe (auto-listens at boot)` etc. No mutation, no live state.
5
+ */
6
+ import { c } from "../../ui.js";
7
+ const LABEL = {
8
+ subscribe: "subscribe (auto-listens at boot)",
9
+ allowSubscribe: "allowSubscribe (may read/join)",
10
+ allowPublish: "allowPublish (may post)",
11
+ };
12
+ const list = (xs) => (xs.length ? xs.join(", ") : c.dim("(none)"));
13
+ /** The full `topology view`: header, channel access, agent access, persona-inherited scopes, warnings. */
14
+ export function renderTopology(p) {
15
+ const m = p.manifest;
16
+ const out = [];
17
+ const broker = m.broker?.servers ?? "fresh local broker";
18
+ out.push(c.bold(`Mesh "${m.space}"`) +
19
+ c.dim(` (broker: ${broker} · runtime ${m.runtime ?? "pty"} · personaPermissions: ${m.personaPermissions})`));
20
+ // Channels — who subscribes / may read / may post (agent names).
21
+ out.push("", c.bold(`Channels (${m.channels.length})`));
22
+ for (const ch of m.channels) {
23
+ out.push(` ${c.cyan("#" + ch.name)}${ch.description ? c.dim(" " + ch.description) : ""}`);
24
+ out.push(` ${c.dim(LABEL.subscribe + ":")} ${list(ch.subscribe)}`);
25
+ out.push(` ${c.dim(LABEL.allowSubscribe + ":")} ${list(ch.allowSubscribe)}`);
26
+ out.push(` ${c.dim(LABEL.allowPublish + ":")} ${list(ch.allowPublish)}`);
27
+ if (ch.instructions)
28
+ out.push(` ${c.dim("instructions: " + ch.instructions)}`);
29
+ }
30
+ // Agents — effective merged access (manifest + any persona-inherited under `include`).
31
+ out.push("", c.bold(`Agents (${p.agents.length})`));
32
+ for (const a of p.agents) {
33
+ const src = a.persona ? `persona ${a.persona}` : "inline";
34
+ const meta = [a.agentType, a.model ? `model ${a.model}` : undefined, a.role ? `role ${a.role}` : undefined]
35
+ .filter(Boolean)
36
+ .join(" · ");
37
+ out.push(` ${c.bold(a.name)} ${c.dim(meta + " · " + src)}`);
38
+ out.push(` ${c.dim(LABEL.subscribe + ":")} ${list(a.policy.subscribe)}`);
39
+ out.push(` ${c.dim(LABEL.allowSubscribe + ":")} ${list(a.policy.allowSubscribe)}`);
40
+ out.push(` ${c.dim(LABEL.allowPublish + ":")} ${list(a.policy.allowPublish)}`);
41
+ if (a.capabilities.length)
42
+ out.push(` ${c.dim("capabilities:")} ${a.capabilities.join(", ")}${a.capabilitySource === "persona" ? c.dim(" (persona-inherited)") : ""}`);
43
+ }
44
+ const inherited = renderInherited(p);
45
+ if (inherited)
46
+ out.push("", inherited);
47
+ if (p.warnings.length)
48
+ out.push("", renderWarnings(p.warnings));
49
+ return out.join("\n");
50
+ }
51
+ /** The `cotal up -f --dry-run` plan: a fresh mesh creates everything, so the grouping is simply
52
+ * "will create" — broker + channels + agents — followed by the full access view. Mutates nothing. */
53
+ export function renderUpPlan(p, server) {
54
+ const m = p.manifest;
55
+ const head = [
56
+ c.bold("Plan — cotal up -f (fresh mesh)"),
57
+ c.bold("Will create:"),
58
+ ` ${c.green("+")} broker + space ${c.cyan(`"${m.space}"`)} at ${server}`,
59
+ ` ${c.green("+")} ${m.channels.length} channel(s): ${m.channels.map((ch) => c.cyan("#" + ch.name)).join(", ")}`,
60
+ ` ${c.green("+")} ${p.agents.length} agent(s): ${p.agents.map((a) => a.name).join(", ")}`,
61
+ "",
62
+ ].join("\n");
63
+ return `${head}${renderTopology(p)}\n\n${c.dim("Dry run — nothing was changed. Re-run without --dry-run to apply.")}`;
64
+ }
65
+ /** The loud "persona grants outside manifest channels" section — unmanaged credential scopes that
66
+ * an old persona ref drags in under `personaPermissions: include`. Returns "" when there are none. */
67
+ export function renderInherited(p) {
68
+ const rows = [];
69
+ for (const a of p.agents) {
70
+ const i = a.inherited;
71
+ const hasAcl = i.subscribe.length || i.allowSubscribe.length || i.allowPublish.length;
72
+ if (!hasAcl && !i.capabilities.length)
73
+ continue;
74
+ // Capabilities first — they are NOT channel-scoped (spawn/tool power), so they're easiest to miss
75
+ // and most security-significant (security review, round-8).
76
+ if (i.capabilities.length)
77
+ rows.push(` ${c.yellow("‼")} ${c.bold(a.name)} capabilities: ${i.capabilities.join(", ")} ${c.dim(`(persona ${a.persona} — not channel-scoped)`)}`);
78
+ if (hasAcl) {
79
+ const parts = [
80
+ i.subscribe.length ? `subscribe ${i.subscribe.join(",")}` : "",
81
+ i.allowSubscribe.length ? `read ${i.allowSubscribe.join(",")}` : "",
82
+ i.allowPublish.length ? `post ${i.allowPublish.join(",")}` : "",
83
+ ].filter(Boolean);
84
+ rows.push(` ${c.bold(a.name)} → ${parts.join(" · ")} ${c.dim(`(persona ${a.persona} · unmanaged by manifest, no card)`)}`);
85
+ }
86
+ }
87
+ if (!rows.length)
88
+ return "";
89
+ return [c.yellow(c.bold("⚠ Persona-inherited access + capabilities outside manifest channels")), ...rows].join("\n");
90
+ }
91
+ /** Render the non-fatal warnings (empty-ACL agents, loud when they carry capabilities). */
92
+ export function renderWarnings(warnings) {
93
+ const rows = warnings.map((w) => ` ${w.loud ? c.yellow("‼") : c.dim("•")} ${c.bold(w.agent)}: ${w.message}`);
94
+ return [c.yellow(c.bold(`⚠ Warnings (${warnings.length})`)), ...rows].join("\n");
95
+ }
96
+ /** The `cotal spawn -f` plan / `--dry-run`: deploy onto a RUNNING mesh. Groups channels and agents
97
+ * by disposition (create / exists-unmanaged / owned · will-create / already-owned / stale), then the
98
+ * SECURITY block + persona-inherited access. Creation-only — an existing unmanaged card is shown
99
+ * desired-vs-live, never patched. */
100
+ export function renderSpawnPlan(p, channels, agents, unmanaged, ctx) {
101
+ const out = [c.bold(`Plan — cotal spawn -f (deploy onto running mesh ${ctx.server})`)];
102
+ out.push("", c.bold("Channels:"));
103
+ for (const ch of channels.create)
104
+ out.push(` ${c.green("+")} create ${c.cyan("#" + ch.name)} ${c.dim("(seed + own)")}`);
105
+ for (const { channel, live } of channels.existsUnmanaged) {
106
+ out.push(` ${c.yellow("~")} ${c.cyan("#" + channel.name)} ${c.yellow("exists — unmanaged")} ${c.dim("(card left untouched)")}`);
107
+ if ((channel.description ?? "") !== (live.description ?? ""))
108
+ out.push(` ${c.dim(`desired: ${channel.description ?? "(none)"} · live: ${live.description ?? "(none)"}`)}`);
109
+ if ((channel.instructions ?? "") !== (live.instructions ?? ""))
110
+ out.push(` ${c.dim(`desired instructions differ from live — not applied (use a future --patch flag)`)}`);
111
+ }
112
+ for (const ch of channels.owned)
113
+ out.push(` ${c.dim("=")} ${c.cyan("#" + ch.name)} ${c.dim("(already owned by this run)")}`);
114
+ if (!channels.create.length && !channels.existsUnmanaged.length && !channels.owned.length)
115
+ out.push(` ${c.dim("(none)")}`);
116
+ out.push("", c.bold("Agents:"));
117
+ for (const e of agents.willCreate)
118
+ out.push(` ${c.green("+")} ${c.bold(e.agent.name)} ${c.dim(`${e.agent.agentType} — will launch`)}`);
119
+ for (const e of agents.alreadyOwned)
120
+ out.push(` ${c.dim("=")} ${c.bold(e.agent.name)} ${c.dim(`(already running as ${e.prior?.name} — no-op)`)}`);
121
+ for (const e of agents.stale)
122
+ out.push(` ${c.yellow("!")} ${c.bold(e.agent.name)} ${c.yellow("stale — restart required")} ` +
123
+ c.dim(`(${e.prior?.name}: hash ${e.prior?.hash.slice(0, 8)} → ${e.hash.slice(0, 8)}${e.running ? "" : ", not running"})`));
124
+ if (!agents.entries.length)
125
+ out.push(` ${c.dim("(none)")}`);
126
+ const sec = renderUnmanaged(unmanaged);
127
+ if (sec)
128
+ out.push("", sec);
129
+ const inherited = renderInherited(p);
130
+ if (inherited)
131
+ out.push("", inherited);
132
+ if (ctx.dryRun)
133
+ out.push("", c.dim(`Dry run — nothing was changed. Run ${ctx.runId} not written. Re-run without --dry-run to apply.`));
134
+ return out.join("\n");
135
+ }
136
+ /** The SECURITY block: unmanaged actors observed with read access to a manifest-declared channel —
137
+ * an isolation conflict on the shared mesh — phrased as an explicit LOWER BOUND (presence + the
138
+ * broker membership feed; live-only core subscriptions aren't observable when the feed is absent).
139
+ * Returns "" only when there's nothing to say AND the feed was readable. */
140
+ export function renderUnmanaged(u) {
141
+ const rows = [];
142
+ for (const ce of u.perChannel) {
143
+ const who = ce.actors.map((a) => `${a.name ?? a.id.slice(0, 8)} (${a.via})`).join(", ");
144
+ rows.push(` ${c.red("‼")} ${c.cyan("#" + ce.channel)}: unmanaged ${who}`);
145
+ }
146
+ const caveat = u.feedAvailable
147
+ ? c.dim(` detected via presence + membership feed (asOf ${new Date(u.asOf).toISOString()}); live-only core subscriptions are a lower bound`)
148
+ : c.dim(" membership feed unavailable — detection is PRESENCE-ONLY (a lower bound; channel membership/live subscriptions not observable)");
149
+ // Show the block when there are conflicts, or when the feed was unavailable (so an empty result is
150
+ // never mistaken for "provably isolated").
151
+ if (!rows.length && u.feedAvailable) {
152
+ return u.presentUnowned.length
153
+ ? c.dim(`note: ${u.presentUnowned.length} unmanaged peer(s) present on the mesh; none on a declared channel (${caveat.trim()})`)
154
+ : "";
155
+ }
156
+ const head = c.red(c.bold("⚠ SECURITY — unmanaged actors with access to declared channels"));
157
+ const tail = u.presentUnowned.length ? [c.dim(` (${u.presentUnowned.length} unmanaged peer(s) present on the mesh in total)`)] : [];
158
+ return [head, ...rows, caveat, ...tail].join("\n");
159
+ }
160
+ /** Post-apply summary for `cotal spawn -f`: what was created/launched, what was left untouched, the
161
+ * SECURITY block, and the exact ownership-scoped teardown command + ledger path. */
162
+ export function renderSpawnSummary(ctx) {
163
+ const out = [c.green(`✓ deployed onto "${ctx.space}" (${ctx.server})`)];
164
+ if (ctx.created.length)
165
+ out.push(` ${c.green("+")} created ${ctx.created.length} channel(s): ${ctx.created.map((n) => c.cyan("#" + n)).join(", ")}`);
166
+ if (ctx.launched.length)
167
+ out.push(` ${c.green("+")} launched ${ctx.launched.length} agent(s): ${ctx.launched.join(", ")}`);
168
+ if (ctx.existsUnmanaged.length)
169
+ out.push(` ${c.yellow("~")} left ${ctx.existsUnmanaged.length} existing channel(s) untouched: ${ctx.existsUnmanaged.map((n) => c.cyan("#" + n)).join(", ")}`);
170
+ const sec = renderUnmanaged(ctx.unmanaged);
171
+ if (sec)
172
+ out.push("", sec);
173
+ out.push("", c.dim(`Run ${ctx.runId} · ledger ${ctx.ledgerPath}`));
174
+ out.push(c.dim(`Tear down ONLY this deploy: `) + `cotal down -f ${ctx.manifestPath} --run ${ctx.runId}`);
175
+ return out.join("\n");
176
+ }
177
+ //# sourceMappingURL=render.js.map