@sapiom/harness 0.0.0 → 0.1.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 (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -43
  3. package/dist/cli/auth.d.ts +26 -0
  4. package/dist/cli/auth.d.ts.map +1 -0
  5. package/dist/cli/auth.js +39 -0
  6. package/dist/cli/auth.js.map +1 -0
  7. package/dist/cli/bin.d.ts +3 -0
  8. package/dist/cli/bin.d.ts.map +1 -0
  9. package/dist/cli/bin.js +163 -0
  10. package/dist/cli/bin.js.map +1 -0
  11. package/dist/cli/consent.d.ts +15 -0
  12. package/dist/cli/consent.d.ts.map +1 -0
  13. package/dist/cli/consent.js +81 -0
  14. package/dist/cli/consent.js.map +1 -0
  15. package/dist/cli/doctor.d.ts +27 -0
  16. package/dist/cli/doctor.d.ts.map +1 -0
  17. package/dist/cli/doctor.js +74 -0
  18. package/dist/cli/doctor.js.map +1 -0
  19. package/dist/cli/machine-id.d.ts +19 -0
  20. package/dist/cli/machine-id.d.ts.map +1 -0
  21. package/dist/cli/machine-id.js +75 -0
  22. package/dist/cli/machine-id.js.map +1 -0
  23. package/dist/cli/paths.d.ts +3 -0
  24. package/dist/cli/paths.d.ts.map +1 -0
  25. package/dist/cli/paths.js +11 -0
  26. package/dist/cli/paths.js.map +1 -0
  27. package/dist/cli/settings.d.ts +26 -0
  28. package/dist/cli/settings.d.ts.map +1 -0
  29. package/dist/cli/settings.js +123 -0
  30. package/dist/cli/settings.js.map +1 -0
  31. package/dist/core/adapters/claude-code.d.ts +41 -0
  32. package/dist/core/adapters/claude-code.d.ts.map +1 -0
  33. package/dist/core/adapters/claude-code.js +238 -0
  34. package/dist/core/adapters/claude-code.js.map +1 -0
  35. package/dist/core/adapters/codex.d.ts +59 -0
  36. package/dist/core/adapters/codex.d.ts.map +1 -0
  37. package/dist/core/adapters/codex.js +319 -0
  38. package/dist/core/adapters/codex.js.map +1 -0
  39. package/dist/core/canvas-body.d.ts +45 -0
  40. package/dist/core/canvas-body.d.ts.map +1 -0
  41. package/dist/core/canvas-body.js +94 -0
  42. package/dist/core/canvas-body.js.map +1 -0
  43. package/dist/core/canvas-cache.d.ts +23 -0
  44. package/dist/core/canvas-cache.d.ts.map +1 -0
  45. package/dist/core/canvas-cache.js +64 -0
  46. package/dist/core/canvas-cache.js.map +1 -0
  47. package/dist/core/canvas-enrich.d.ts +106 -0
  48. package/dist/core/canvas-enrich.d.ts.map +1 -0
  49. package/dist/core/canvas-enrich.js +178 -0
  50. package/dist/core/canvas-enrich.js.map +1 -0
  51. package/dist/core/canvas-enrichment.d.ts +144 -0
  52. package/dist/core/canvas-enrichment.d.ts.map +1 -0
  53. package/dist/core/canvas-enrichment.js +192 -0
  54. package/dist/core/canvas-enrichment.js.map +1 -0
  55. package/dist/core/canvas-graph.d.ts +71 -0
  56. package/dist/core/canvas-graph.d.ts.map +1 -0
  57. package/dist/core/canvas-graph.js +111 -0
  58. package/dist/core/canvas-graph.js.map +1 -0
  59. package/dist/core/canvas-index-classify.d.ts +41 -0
  60. package/dist/core/canvas-index-classify.d.ts.map +1 -0
  61. package/dist/core/canvas-index-classify.js +63 -0
  62. package/dist/core/canvas-index-classify.js.map +1 -0
  63. package/dist/core/canvas-interconnections.d.ts +24 -0
  64. package/dist/core/canvas-interconnections.d.ts.map +1 -0
  65. package/dist/core/canvas-interconnections.js +96 -0
  66. package/dist/core/canvas-interconnections.js.map +1 -0
  67. package/dist/core/canvas-manifest-check.d.ts +15 -0
  68. package/dist/core/canvas-manifest-check.d.ts.map +1 -0
  69. package/dist/core/canvas-manifest-check.js +90 -0
  70. package/dist/core/canvas-manifest-check.js.map +1 -0
  71. package/dist/core/canvas-render.d.ts +73 -0
  72. package/dist/core/canvas-render.d.ts.map +1 -0
  73. package/dist/core/canvas-render.js +145 -0
  74. package/dist/core/canvas-render.js.map +1 -0
  75. package/dist/core/canvas-svg.d.ts +50 -0
  76. package/dist/core/canvas-svg.d.ts.map +1 -0
  77. package/dist/core/canvas-svg.js +240 -0
  78. package/dist/core/canvas-svg.js.map +1 -0
  79. package/dist/core/canvas-template.d.ts +43 -0
  80. package/dist/core/canvas-template.d.ts.map +1 -0
  81. package/dist/core/canvas-template.js +365 -0
  82. package/dist/core/canvas-template.js.map +1 -0
  83. package/dist/core/canvas-watcher.d.ts +25 -0
  84. package/dist/core/canvas-watcher.d.ts.map +1 -0
  85. package/dist/core/canvas-watcher.js +144 -0
  86. package/dist/core/canvas-watcher.js.map +1 -0
  87. package/dist/core/collector/batcher.d.ts +57 -0
  88. package/dist/core/collector/batcher.d.ts.map +1 -0
  89. package/dist/core/collector/batcher.js +143 -0
  90. package/dist/core/collector/batcher.js.map +1 -0
  91. package/dist/core/collector/codex-tailer.d.ts +85 -0
  92. package/dist/core/collector/codex-tailer.d.ts.map +1 -0
  93. package/dist/core/collector/codex-tailer.js +270 -0
  94. package/dist/core/collector/codex-tailer.js.map +1 -0
  95. package/dist/core/collector/normalizer.d.ts +33 -0
  96. package/dist/core/collector/normalizer.d.ts.map +1 -0
  97. package/dist/core/collector/normalizer.js +94 -0
  98. package/dist/core/collector/normalizer.js.map +1 -0
  99. package/dist/core/collector/seq.d.ts +14 -0
  100. package/dist/core/collector/seq.d.ts.map +1 -0
  101. package/dist/core/collector/seq.js +20 -0
  102. package/dist/core/collector/seq.js.map +1 -0
  103. package/dist/core/collector/store.d.ts +16 -0
  104. package/dist/core/collector/store.d.ts.map +1 -0
  105. package/dist/core/collector/store.js +30 -0
  106. package/dist/core/collector/store.js.map +1 -0
  107. package/dist/core/collector/transcript.d.ts +38 -0
  108. package/dist/core/collector/transcript.d.ts.map +1 -0
  109. package/dist/core/collector/transcript.js +110 -0
  110. package/dist/core/collector/transcript.js.map +1 -0
  111. package/dist/core/event-bus.d.ts +9 -0
  112. package/dist/core/event-bus.d.ts.map +1 -0
  113. package/dist/core/event-bus.js +25 -0
  114. package/dist/core/event-bus.js.map +1 -0
  115. package/dist/core/example-seed.d.ts +34 -0
  116. package/dist/core/example-seed.d.ts.map +1 -0
  117. package/dist/core/example-seed.js +277 -0
  118. package/dist/core/example-seed.js.map +1 -0
  119. package/dist/core/inject/claude-settings.d.ts +34 -0
  120. package/dist/core/inject/claude-settings.d.ts.map +1 -0
  121. package/dist/core/inject/claude-settings.js +152 -0
  122. package/dist/core/inject/claude-settings.js.map +1 -0
  123. package/dist/core/inject/mcp-config.d.ts +26 -0
  124. package/dist/core/inject/mcp-config.d.ts.map +1 -0
  125. package/dist/core/inject/mcp-config.js +39 -0
  126. package/dist/core/inject/mcp-config.js.map +1 -0
  127. package/dist/core/inject/retention.d.ts +66 -0
  128. package/dist/core/inject/retention.d.ts.map +1 -0
  129. package/dist/core/inject/retention.js +119 -0
  130. package/dist/core/inject/retention.js.map +1 -0
  131. package/dist/core/inject/system-prompt.d.ts +11 -0
  132. package/dist/core/inject/system-prompt.d.ts.map +1 -0
  133. package/dist/core/inject/system-prompt.js +22 -0
  134. package/dist/core/inject/system-prompt.js.map +1 -0
  135. package/dist/core/macro-runner.d.ts +36 -0
  136. package/dist/core/macro-runner.d.ts.map +1 -0
  137. package/dist/core/macro-runner.js +66 -0
  138. package/dist/core/macro-runner.js.map +1 -0
  139. package/dist/core/macros.d.ts +10 -0
  140. package/dist/core/macros.d.ts.map +1 -0
  141. package/dist/core/macros.js +59 -0
  142. package/dist/core/macros.js.map +1 -0
  143. package/dist/core/path-safety.d.ts +33 -0
  144. package/dist/core/path-safety.d.ts.map +1 -0
  145. package/dist/core/path-safety.js +63 -0
  146. package/dist/core/path-safety.js.map +1 -0
  147. package/dist/core/paths.d.ts +27 -0
  148. package/dist/core/paths.d.ts.map +1 -0
  149. package/dist/core/paths.js +43 -0
  150. package/dist/core/paths.js.map +1 -0
  151. package/dist/core/port-detector.d.ts +69 -0
  152. package/dist/core/port-detector.d.ts.map +1 -0
  153. package/dist/core/port-detector.js +121 -0
  154. package/dist/core/port-detector.js.map +1 -0
  155. package/dist/core/session-manager.d.ts +275 -0
  156. package/dist/core/session-manager.d.ts.map +1 -0
  157. package/dist/core/session-manager.js +729 -0
  158. package/dist/core/session-manager.js.map +1 -0
  159. package/dist/core/task-manager.d.ts +138 -0
  160. package/dist/core/task-manager.d.ts.map +1 -0
  161. package/dist/core/task-manager.js +286 -0
  162. package/dist/core/task-manager.js.map +1 -0
  163. package/dist/core/task-stream.d.ts +32 -0
  164. package/dist/core/task-stream.d.ts.map +1 -0
  165. package/dist/core/task-stream.js +87 -0
  166. package/dist/core/task-stream.js.map +1 -0
  167. package/dist/core/workflow-registry.d.ts +36 -0
  168. package/dist/core/workflow-registry.d.ts.map +1 -0
  169. package/dist/core/workflow-registry.js +220 -0
  170. package/dist/core/workflow-registry.js.map +1 -0
  171. package/dist/core/workspace-context.d.ts +43 -0
  172. package/dist/core/workspace-context.d.ts.map +1 -0
  173. package/dist/core/workspace-context.js +108 -0
  174. package/dist/core/workspace-context.js.map +1 -0
  175. package/dist/core/workspace-watcher.d.ts +26 -0
  176. package/dist/core/workspace-watcher.d.ts.map +1 -0
  177. package/dist/core/workspace-watcher.js +174 -0
  178. package/dist/core/workspace-watcher.js.map +1 -0
  179. package/dist/index.d.ts +6 -0
  180. package/dist/index.d.ts.map +1 -0
  181. package/dist/index.js +6 -0
  182. package/dist/index.js.map +1 -0
  183. package/dist/profiles/canvas-guidelines.d.ts +20 -0
  184. package/dist/profiles/canvas-guidelines.d.ts.map +1 -0
  185. package/dist/profiles/canvas-guidelines.js +48 -0
  186. package/dist/profiles/canvas-guidelines.js.map +1 -0
  187. package/dist/profiles/default.d.ts +10 -0
  188. package/dist/profiles/default.d.ts.map +1 -0
  189. package/dist/profiles/default.js +70 -0
  190. package/dist/profiles/default.js.map +1 -0
  191. package/dist/server/auth.d.ts +9 -0
  192. package/dist/server/auth.d.ts.map +1 -0
  193. package/dist/server/auth.js +28 -0
  194. package/dist/server/auth.js.map +1 -0
  195. package/dist/server/canvas-render.d.ts +18 -0
  196. package/dist/server/canvas-render.d.ts.map +1 -0
  197. package/dist/server/canvas-render.js +24 -0
  198. package/dist/server/canvas-render.js.map +1 -0
  199. package/dist/server/canvas.d.ts +12 -0
  200. package/dist/server/canvas.d.ts.map +1 -0
  201. package/dist/server/canvas.js +174 -0
  202. package/dist/server/canvas.js.map +1 -0
  203. package/dist/server/events-ws.d.ts +11 -0
  204. package/dist/server/events-ws.d.ts.map +1 -0
  205. package/dist/server/events-ws.js +24 -0
  206. package/dist/server/events-ws.js.map +1 -0
  207. package/dist/server/fs.d.ts +4 -0
  208. package/dist/server/fs.d.ts.map +1 -0
  209. package/dist/server/fs.js +88 -0
  210. package/dist/server/fs.js.map +1 -0
  211. package/dist/server/index.d.ts +83 -0
  212. package/dist/server/index.d.ts.map +1 -0
  213. package/dist/server/index.js +0 -0
  214. package/dist/server/index.js.map +1 -0
  215. package/dist/server/ingest.d.ts +74 -0
  216. package/dist/server/ingest.d.ts.map +1 -0
  217. package/dist/server/ingest.js +74 -0
  218. package/dist/server/ingest.js.map +1 -0
  219. package/dist/server/macros.d.ts +32 -0
  220. package/dist/server/macros.d.ts.map +1 -0
  221. package/dist/server/macros.js +76 -0
  222. package/dist/server/macros.js.map +1 -0
  223. package/dist/server/rest.d.ts +75 -0
  224. package/dist/server/rest.d.ts.map +1 -0
  225. package/dist/server/rest.js +235 -0
  226. package/dist/server/rest.js.map +1 -0
  227. package/dist/server/static.d.ts +8 -0
  228. package/dist/server/static.d.ts.map +1 -0
  229. package/dist/server/static.js +36 -0
  230. package/dist/server/static.js.map +1 -0
  231. package/dist/server/terminal-ws.d.ts +9 -0
  232. package/dist/server/terminal-ws.d.ts.map +1 -0
  233. package/dist/server/terminal-ws.js +59 -0
  234. package/dist/server/terminal-ws.js.map +1 -0
  235. package/dist/server/ws-router.d.ts +16 -0
  236. package/dist/server/ws-router.d.ts.map +1 -0
  237. package/dist/server/ws-router.js +26 -0
  238. package/dist/server/ws-router.js.map +1 -0
  239. package/dist/shared/types.d.ts +485 -0
  240. package/dist/shared/types.d.ts.map +1 -0
  241. package/dist/shared/types.js +80 -0
  242. package/dist/shared/types.js.map +1 -0
  243. package/dist/web/assets/index-B9R4TzH7.css +32 -0
  244. package/dist/web/assets/index-Dh97QAgq.js +192 -0
  245. package/dist/web/index.html +27 -0
  246. package/package.json +84 -7
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The deterministic render pipeline: given a session bound to a workflow,
3
+ * extracts that workflow's graph (core/canvas-cache.ts — cached, so an
4
+ * unchanged workflow never pays for a second child process), builds the SVG +
5
+ * panel markup (core/canvas-svg.ts, core/canvas-body.ts), wraps it through
6
+ * the shared document shell (core/canvas-template.ts's
7
+ * `renderCanvasDocument`), and writes it to the workflow's own render file,
8
+ * `<cwd>/.sapiom/canvas/renders/<slug>.html`. Zero LLM involvement —
9
+ * extraction failure degrades to an honest error panel, never a crash.
10
+ *
11
+ * Renders are per-WORKFLOW files (not a shared index.html) so switching the
12
+ * binding never rewrites anything another binding depends on — the server
13
+ * (src/server/canvas.ts) resolves the session's current binding at request
14
+ * time and serves the matching render. `index.html` remains the
15
+ * agent-authored/custom canvas and is never touched here. An unbound session
16
+ * renders nothing at all (no extraction, no write): the server serves the
17
+ * existing empty-state/custom canvas for it.
18
+ *
19
+ * The write alone is enough to hot-reload an open canvas pane —
20
+ * CanvasWatcherManager already watches the whole session cwd and treats any
21
+ * change under `.sapiom/canvas/` as a reload signal, regardless of who wrote
22
+ * it.
23
+ */
24
+ import { createHash } from "node:crypto";
25
+ import * as fs from "node:fs/promises";
26
+ import * as path from "node:path";
27
+ import { CANVAS_CACHE_DIR, CANVAS_RENDERS_DIR } from "../shared/types.js";
28
+ import { renderCanvasDocument } from "./canvas-template.js";
29
+ import { extractWorkflowGraphCached } from "./canvas-cache.js";
30
+ import { readEnrichmentCacheFile } from "./canvas-enrichment.js";
31
+ import { usedKinds } from "./canvas-svg.js";
32
+ import { assembleCanvasBody, buildErrorPanelHtml, buildLegendHtml, buildWorkflowPanelHtml, } from "./canvas-body.js";
33
+ /**
34
+ * Stable, filesystem-safe render-file name for a workflow: its directory
35
+ * basename (readable in `ls`) plus a short path hash (two same-named
36
+ * workflows at different paths can never collide). Shared by the writer here
37
+ * and the server's request-time resolution — must stay deterministic.
38
+ */
39
+ export function slugForWorkflowPath(workflowPath) {
40
+ const base = path
41
+ .basename(workflowPath)
42
+ .toLowerCase()
43
+ .replace(/[^a-z0-9]+/g, "-")
44
+ .replace(/^-+|-+$/g, "") || "workflow";
45
+ const hash = createHash("sha256").update(path.resolve(workflowPath)).digest("hex").slice(0, 8);
46
+ return `${base}-${hash}`;
47
+ }
48
+ /** Absolute path of `workflowPath`'s render file under `cwd`'s canvas dir. */
49
+ export function renderFileFor(cwd, workflowPath) {
50
+ return path.join(cwd, CANVAS_RENDERS_DIR, `${slugForWorkflowPath(workflowPath)}.html`);
51
+ }
52
+ /** Absolute path of `workflowPath`'s enrichment cache file under `cwd`'s
53
+ * canvas dir — same slug scheme as the render file. */
54
+ export function enrichmentCacheFileFor(cwd, workflowPath) {
55
+ return path.join(cwd, CANVAS_CACHE_DIR, `${slugForWorkflowPath(workflowPath)}.json`);
56
+ }
57
+ function badgesFor(workflow) {
58
+ return [workflow.definitionId != null ? "deployed" : "local only"];
59
+ }
60
+ function buildSingleBody(workflow, graph, reason, panelEnrichment) {
61
+ if (!graph) {
62
+ return assembleCanvasBody({
63
+ panels: [buildErrorPanelHtml(workflow.name, reason ?? "unknown extraction failure")],
64
+ legend: "",
65
+ note: "Static preview — this workflow has a build error; regenerate once it's fixed.",
66
+ });
67
+ }
68
+ const used = usedKinds(graph);
69
+ return assembleCanvasBody({
70
+ panels: [buildWorkflowPanelHtml(graph, { title: workflow.name, badges: badgesFor(workflow) }, panelEnrichment)],
71
+ legend: buildLegendHtml(used.nodeKinds, used.edgeKinds),
72
+ note: "Static preview — re-renders automatically when the workflow changes.",
73
+ ...(panelEnrichment?.enrichment.notes ? { notes: panelEnrichment.enrichment.notes } : {}),
74
+ ...(panelEnrichment?.enrichment.crossWorkflow
75
+ ? { crossWorkflow: panelEnrichment.enrichment.crossWorkflow }
76
+ : {}),
77
+ });
78
+ }
79
+ /**
80
+ * Renders `session`'s bound workflow to its per-workflow render file. Never
81
+ * throws — every failure mode (extraction, filesystem) is captured in the
82
+ * returned outcome instead. Unbound sessions are a cheap no-op: no
83
+ * extraction, no write (`mode: "empty"`).
84
+ */
85
+ export async function renderCanvasForSession(session, workflows, options = {}) {
86
+ const bound = session.boundWorkflowPath ? workflows.find((w) => w.path === session.boundWorkflowPath) : undefined;
87
+ if (!bound) {
88
+ return { mode: "empty", extractionFailed: [] };
89
+ }
90
+ return renderWorkflowRenderFile(session.cwd, bound, options);
91
+ }
92
+ /**
93
+ * Renders ONE workflow to its render file under `cwd`, merging in any cached
94
+ * enrichment (fresh or stale — stale gets the chip; the base structure is
95
+ * always freshly extracted either way). This is the write path shared by
96
+ * bind/create/macro renders (via `renderCanvasForSession`) and by the
97
+ * enrichment task's own completion re-render (core/canvas-enrich.ts), which
98
+ * targets a workflow directly — the session may have switched bindings while
99
+ * the task ran, and the render file is per-workflow anyway.
100
+ */
101
+ export async function renderWorkflowRenderFile(cwd, bound, options = {}) {
102
+ const { result, cached, fingerprint } = await extractWorkflowGraphCached(bound.path);
103
+ const failed = !result.ok;
104
+ // The enrichment cache only ever decorates a successful extraction — an
105
+ // error panel with AI annotations for steps it can't show would be noise.
106
+ let panelEnrichment = null;
107
+ if (result.ok) {
108
+ const entry = await readEnrichmentCacheFile(enrichmentCacheFileFor(cwd, bound.path));
109
+ if (entry) {
110
+ panelEnrichment = { enrichment: entry.enrichment, stale: entry.sourceFingerprint !== fingerprint };
111
+ }
112
+ }
113
+ const body = buildSingleBody(bound, result.ok ? result.graph : null, result.ok ? null : result.reason, panelEnrichment);
114
+ const renderPath = renderFileFor(cwd, bound.path);
115
+ const outcome = {
116
+ mode: "single",
117
+ workflowPath: bound.path,
118
+ renderPath,
119
+ extractionFailed: failed ? [bound.name] : [],
120
+ cachedExtraction: cached,
121
+ ...(panelEnrichment ? { enrichmentApplied: true, enrichmentStale: panelEnrichment.stale } : {}),
122
+ };
123
+ // An unprompted render that failed to extract must not destroy an existing
124
+ // (possibly good) diagram for this workflow — e.g. a project that merely
125
+ // hasn't run `npm install` yet after a fresh clone.
126
+ if (options.preserveExistingOnFailure && failed) {
127
+ const exists = await fs
128
+ .access(renderPath)
129
+ .then(() => true)
130
+ .catch(() => false);
131
+ if (exists) {
132
+ outcome.preservedExisting = true;
133
+ return outcome;
134
+ }
135
+ }
136
+ try {
137
+ await fs.mkdir(path.dirname(renderPath), { recursive: true });
138
+ await fs.writeFile(renderPath, renderCanvasDocument(body), "utf8");
139
+ }
140
+ catch (err) {
141
+ outcome.writeError = err instanceof Error ? err.message : String(err);
142
+ }
143
+ return outcome;
144
+ }
145
+ //# sourceMappingURL=canvas-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-render.js","sourceRoot":"","sources":["../../src/core/canvas-render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Server-side SVG layout for a `CanvasGraph` (see core/canvas-graph.ts),
3
+ * emitting markup against the classes `core/canvas-template.ts`'s CSS shell
4
+ * already defines (node--entry/step/pause/terminal-success/terminal-warn/
5
+ * launched-workflow, canvas-edge/--success/--warn/--cross/--launch). No DOM,
6
+ * no client-side script — the
7
+ * whole diagram is a static string built once, at render time.
8
+ *
9
+ * Layout: longest-path-from-roots layering (a Kahn's-algorithm variant),
10
+ * robust to reconverging branches (diamonds) and defensively bounded against
11
+ * a cycle (a malformed/adversarial manifest should degrade, not hang). Ported
12
+ * from the layout math in an earlier client-side renderer (see git history of
13
+ * canvas-template.ts, commit "canvas kit — LLM writes data, not HTML") — the
14
+ * geometry is unchanged, only where it runs (server, not browser).
15
+ */
16
+ import type { CanvasEdge, CanvasEdgeKind, CanvasGraph, CanvasNode, CanvasNodeKind } from "./canvas-graph.js";
17
+ import type { CanvasEnrichment, CanvasLayoutHints } from "./canvas-enrichment.js";
18
+ export declare const NODE_W = 176;
19
+ export declare const NODE_H = 64;
20
+ export interface GraphLayout {
21
+ pos: Record<string, {
22
+ x: number;
23
+ y: number;
24
+ }>;
25
+ width: number;
26
+ height: number;
27
+ }
28
+ /** Assigns each node the longest-path layer reachable from a root (an
29
+ * in-degree-0 node). Nodes never reached by a root (only possible on a
30
+ * disconnected/cyclic fragment) fall to the layer just past the deepest
31
+ * known one, so they still render instead of vanishing. */
32
+ export declare function computeLayers(nodes: readonly CanvasNode[], edges: readonly CanvasEdge[]): Map<string, number>;
33
+ /** Computes (x, y) for every node and the overall canvas size. Nodes in the
34
+ * same layer are centered as a row; layers stack top to bottom. An
35
+ * enrichment's `laneOrder` hint reorders nodes WITHIN their computed layer
36
+ * only — layer assignment itself stays purely structural. */
37
+ export declare function layoutGraph(graph: CanvasGraph, laneOrder?: CanvasLayoutHints["laneOrder"]): GraphLayout;
38
+ /** Renders one graph's `<svg>` diagram, merging in any enrichment content
39
+ * (node sublabels/descriptions, edge labels, group bands, lane order) — all
40
+ * of it pre-bounded plain strings (core/canvas-enrichment.ts), escaped here
41
+ * like every other text. Assumes the shared `<defs>` (glow filter + arrow
42
+ * markers) are already present once at the document level — see
43
+ * `renderCanvasDocument`'s `SVG_DEFS` — so this never emits its own. */
44
+ export declare function renderGraphSvg(graph: CanvasGraph, enrichment?: CanvasEnrichment | null): string;
45
+ /** Every node/edge kind actually used, for the shared legend footer. */
46
+ export declare function usedKinds(graph: CanvasGraph): {
47
+ nodeKinds: Set<CanvasNodeKind>;
48
+ edgeKinds: Set<CanvasEdgeKind>;
49
+ };
50
+ //# sourceMappingURL=canvas-svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-svg.d.ts","sourceRoot":"","sources":["../../src/core/canvas-svg.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC7G,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAElF,eAAO,MAAM,MAAM,MAAM,CAAC;AAC1B,eAAO,MAAM,MAAM,KAAK,CAAC;AAOzB,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;4DAG4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqC7G;AAED;;;8DAG8D;AAC9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,iBAAiB,CAAC,WAAW,CAAC,GAAG,WAAW,CAyBvG;AAkFD;;;;;yEAKyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CA4E/F;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG;IAAE,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,CAKhH"}
@@ -0,0 +1,240 @@
1
+ export const NODE_W = 176;
2
+ export const NODE_H = 64;
3
+ const LAYER_GAP = 72;
4
+ const COL_GAP = 32;
5
+ const MARGIN = 40;
6
+ /** How far a group band extends past its member nodes' boxes. */
7
+ const GROUP_PAD = 10;
8
+ /** Assigns each node the longest-path layer reachable from a root (an
9
+ * in-degree-0 node). Nodes never reached by a root (only possible on a
10
+ * disconnected/cyclic fragment) fall to the layer just past the deepest
11
+ * known one, so they still render instead of vanishing. */
12
+ export function computeLayers(nodes, edges) {
13
+ const ids = nodes.map((n) => n.id);
14
+ const idSet = new Set(ids);
15
+ const indeg = new Map(ids.map((id) => [id, 0]));
16
+ const adj = new Map(ids.map((id) => [id, []]));
17
+ for (const e of edges) {
18
+ if (!idSet.has(e.from) || !idSet.has(e.to))
19
+ continue;
20
+ adj.get(e.from).push(e.to);
21
+ indeg.set(e.to, (indeg.get(e.to) ?? 0) + 1);
22
+ }
23
+ const layer = new Map();
24
+ let queue = ids.filter((id) => indeg.get(id) === 0);
25
+ const seen = new Set(queue);
26
+ for (const id of queue)
27
+ layer.set(id, 0);
28
+ const maxIterations = ids.length * 2 + 2;
29
+ let iterations = 0;
30
+ while (queue.length > 0 && iterations++ < maxIterations) {
31
+ const next = [];
32
+ for (const id of queue) {
33
+ for (const child of adj.get(id) ?? []) {
34
+ const candidate = (layer.get(id) ?? 0) + 1;
35
+ if (!layer.has(child) || candidate > layer.get(child))
36
+ layer.set(child, candidate);
37
+ if (!seen.has(child)) {
38
+ seen.add(child);
39
+ next.push(child);
40
+ }
41
+ }
42
+ }
43
+ queue = next;
44
+ }
45
+ let maxLayer = 0;
46
+ for (const l of layer.values())
47
+ if (l > maxLayer)
48
+ maxLayer = l;
49
+ for (const id of ids)
50
+ if (!layer.has(id))
51
+ layer.set(id, maxLayer + 1);
52
+ return layer;
53
+ }
54
+ /** Computes (x, y) for every node and the overall canvas size. Nodes in the
55
+ * same layer are centered as a row; layers stack top to bottom. An
56
+ * enrichment's `laneOrder` hint reorders nodes WITHIN their computed layer
57
+ * only — layer assignment itself stays purely structural. */
58
+ export function layoutGraph(graph, laneOrder) {
59
+ const layer = computeLayers(graph.nodes, graph.edges);
60
+ const byLayer = new Map();
61
+ for (const n of graph.nodes) {
62
+ const l = layer.get(n.id) ?? 0;
63
+ const arr = byLayer.get(l) ?? [];
64
+ arr.push(n.id);
65
+ byLayer.set(l, arr);
66
+ }
67
+ applyLaneOrder(byLayer, graph, laneOrder);
68
+ const layers = [...byLayer.keys()].sort((a, b) => a - b);
69
+ const maxCols = layers.reduce((m, l) => Math.max(m, byLayer.get(l).length), 1);
70
+ const width = MARGIN * 2 + maxCols * NODE_W + (maxCols - 1) * COL_GAP;
71
+ const pos = {};
72
+ for (const l of layers) {
73
+ const idsInLayer = byLayer.get(l);
74
+ const rowWidth = idsInLayer.length * NODE_W + (idsInLayer.length - 1) * COL_GAP;
75
+ const startX = (width - rowWidth) / 2;
76
+ idsInLayer.forEach((id, i) => {
77
+ pos[id] = { x: startX + i * (NODE_W + COL_GAP), y: MARGIN + l * (NODE_H + LAYER_GAP) };
78
+ });
79
+ }
80
+ const height = MARGIN * 2 + (layers.length - 1) * (NODE_H + LAYER_GAP) + NODE_H;
81
+ return { pos, width, height: Math.max(height, MARGIN * 2 + NODE_H) };
82
+ }
83
+ /**
84
+ * Reorders each hinted layer's nodes in place. A hint entry is applied only
85
+ * when every node id it lists exists in the graph (the schema's contract —
86
+ * a stale enrichment must degrade to "no hint", never to a broken layout);
87
+ * listed ids that live in a DIFFERENT layer are simply not in this layer's
88
+ * row and are ignored, since laneOrder can never move a node between layers.
89
+ */
90
+ function applyLaneOrder(byLayer, graph, laneOrder) {
91
+ if (!laneOrder)
92
+ return;
93
+ const nodeIds = new Set(graph.nodes.map((n) => n.id));
94
+ for (const [layerKey, order] of Object.entries(laneOrder)) {
95
+ const layerIndex = Number(layerKey);
96
+ const row = byLayer.get(layerIndex);
97
+ if (!row || !order.every((id) => nodeIds.has(id)))
98
+ continue;
99
+ const inRow = new Set(row);
100
+ const ordered = order.filter((id) => inRow.has(id));
101
+ const rest = row.filter((id) => !ordered.includes(id));
102
+ byLayer.set(layerIndex, [...ordered, ...rest]);
103
+ }
104
+ }
105
+ function esc(s) {
106
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
107
+ }
108
+ function edgeColorClass(nodesById, edge) {
109
+ const target = nodesById.get(edge.to);
110
+ if (target?.kind === "terminal-success")
111
+ return "--success";
112
+ if (target?.kind === "terminal-warn")
113
+ return "--warn";
114
+ return "";
115
+ }
116
+ function arrowMarker(colorSuffix) {
117
+ if (colorSuffix === "--success")
118
+ return "url(#canvas-arrow-success)";
119
+ if (colorSuffix === "--warn")
120
+ return "url(#canvas-arrow-warn)";
121
+ return "url(#canvas-arrow)";
122
+ }
123
+ function edgePath(kind, x1, y1, x2, y2) {
124
+ if (kind === "branching") {
125
+ const midY = (y1 + y2) / 2;
126
+ return `M${x1},${y1} C${x1},${midY} ${x2},${midY} ${x2},${y2}`;
127
+ }
128
+ return `M${x1},${y1} L${x2},${y2}`;
129
+ }
130
+ /**
131
+ * Group-hint background bands: one subtle rect per group whose member nodes
132
+ * ALL exist, sized to their bounding box, labeled at its top-left. Rendered
133
+ * before edges/nodes so it sits behind everything.
134
+ */
135
+ function groupBandMarkup(groups, graph, layout) {
136
+ if (!groups || groups.length === 0)
137
+ return "";
138
+ const nodeIds = new Set(graph.nodes.map((n) => n.id));
139
+ return groups
140
+ .map((group) => {
141
+ if (!group.nodeIds.every((id) => nodeIds.has(id)))
142
+ return "";
143
+ const positions = group.nodeIds.map((id) => layout.pos[id]).filter(Boolean);
144
+ if (positions.length === 0)
145
+ return "";
146
+ const minX = Math.min(...positions.map((p) => p.x)) - GROUP_PAD;
147
+ const minY = Math.min(...positions.map((p) => p.y)) - GROUP_PAD;
148
+ const maxX = Math.max(...positions.map((p) => p.x + NODE_W)) + GROUP_PAD;
149
+ const maxY = Math.max(...positions.map((p) => p.y + NODE_H)) + GROUP_PAD;
150
+ return (`<rect class="canvas-group-band" x="${minX}" y="${minY}" width="${maxX - minX}" height="${maxY - minY}" rx="18" />` +
151
+ `<text class="canvas-group-label" x="${minX + 8}" y="${minY - 4}">${esc(group.label)}</text>`);
152
+ })
153
+ .filter(Boolean)
154
+ .join("\n");
155
+ }
156
+ /** Renders one graph's `<svg>` diagram, merging in any enrichment content
157
+ * (node sublabels/descriptions, edge labels, group bands, lane order) — all
158
+ * of it pre-bounded plain strings (core/canvas-enrichment.ts), escaped here
159
+ * like every other text. Assumes the shared `<defs>` (glow filter + arrow
160
+ * markers) are already present once at the document level — see
161
+ * `renderCanvasDocument`'s `SVG_DEFS` — so this never emits its own. */
162
+ export function renderGraphSvg(graph, enrichment) {
163
+ const layout = layoutGraph(graph, enrichment?.layoutHints?.laneOrder);
164
+ const nodesById = new Map(graph.nodes.map((n) => [n.id, n]));
165
+ const bandMarkup = groupBandMarkup(enrichment?.layoutHints?.groups, graph, layout);
166
+ const edgeMarkup = graph.edges
167
+ .map((edge) => {
168
+ const from = layout.pos[edge.from];
169
+ const to = layout.pos[edge.to];
170
+ if (!from || !to)
171
+ return "";
172
+ const x1 = from.x + NODE_W / 2;
173
+ const y1 = from.y + NODE_H;
174
+ const x2 = to.x + NODE_W / 2;
175
+ const y2 = to.y;
176
+ // Cross (signal/handoff) and launch edges are always dashed-neutral,
177
+ // never colored by their destination.
178
+ const dashedClass = edge.kind === "cross" ? "canvas-edge--cross" : edge.kind === "launch" ? "canvas-edge--launch" : null;
179
+ const colorSuffix = dashedClass ? "" : edgeColorClass(nodesById, edge);
180
+ const classes = ["canvas-edge", dashedClass ?? (colorSuffix && `canvas-edge${colorSuffix}`)]
181
+ .filter(Boolean)
182
+ .join(" ");
183
+ const d = edgePath(edge.kind, x1, y1, x2, y2);
184
+ const marker = dashedClass ? "url(#canvas-arrow)" : arrowMarker(colorSuffix);
185
+ const path = `<path class="${classes}" d="${d}" marker-end="${marker}" />`;
186
+ // An enriched label (an intent/condition name the AI read out of the
187
+ // step body) wins over the structural default (e.g. "launch()").
188
+ const labelText = enrichment?.edgeLabels?.[`${edge.from}->${edge.to}`] ?? edge.label;
189
+ if (!labelText)
190
+ return path;
191
+ const dx = x2 - x1;
192
+ const anchor = dx > 4 ? "start" : dx < -4 ? "end" : "middle";
193
+ const labelX = x1 + (dx > 0 ? 10 : dx < 0 ? -10 : 0);
194
+ const label = `<text class="canvas-edge-label" x="${labelX}" y="${y1 + 20}" text-anchor="${anchor}">${esc(labelText)}</text>`;
195
+ return path + label;
196
+ })
197
+ .join("\n");
198
+ const nodeMarkup = graph.nodes
199
+ .map((node) => {
200
+ const p = layout.pos[node.id];
201
+ if (!p)
202
+ return "";
203
+ const details = enrichment?.nodeDetails?.[node.id];
204
+ const sublabel = details?.sublabel ?? node.sublabel;
205
+ const titleY = sublabel ? 26 : NODE_H / 2;
206
+ const sub = sublabel
207
+ ? `<text class="canvas-node-sub" x="${NODE_W / 2}" y="44">${esc(sublabel)}</text>`
208
+ : "";
209
+ // SVG <title> = native hover tooltip; the only enrichment slot with
210
+ // room for a full sentence.
211
+ const description = details?.description ? `<title>${esc(details.description)}</title>` : "";
212
+ return (`<g class="canvas-node node--${node.kind}" filter="url(#canvas-glow)" transform="translate(${p.x},${p.y})">` +
213
+ description +
214
+ `<rect class="canvas-node-rect" width="${NODE_W}" height="${NODE_H}" rx="14" />` +
215
+ `<text class="canvas-node-title" x="${NODE_W / 2}" y="${titleY}">${esc(node.label)}</text>` +
216
+ sub +
217
+ `</g>`);
218
+ })
219
+ .join("\n");
220
+ // Explicit width/height pin the diagram to its NATURAL (1×) size. Without
221
+ // them, the CSS `width:100%` used to stretch a single-column workflow's
222
+ // tiny viewBox to fill the pane, ballooning every node and its font (~4×).
223
+ // The CSS now only centers the svg (`margin: 0 auto`); a graph wider than
224
+ // the pane keeps full size and scrolls (`.canvas-diagram-panel`'s
225
+ // overflow-x) rather than shrinking to an illegible size.
226
+ return (`<svg viewBox="0 0 ${layout.width} ${layout.height}" width="${layout.width}" height="${layout.height}" class="canvas-graph-svg">\n` +
227
+ (bandMarkup ? bandMarkup + "\n" : "") +
228
+ edgeMarkup +
229
+ "\n" +
230
+ nodeMarkup +
231
+ `\n</svg>`);
232
+ }
233
+ /** Every node/edge kind actually used, for the shared legend footer. */
234
+ export function usedKinds(graph) {
235
+ return {
236
+ nodeKinds: new Set(graph.nodes.map((n) => n.kind)),
237
+ edgeKinds: new Set(graph.edges.map((e) => e.kind)),
238
+ };
239
+ }
240
+ //# sourceMappingURL=canvas-svg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-svg.js","sourceRoot":"","sources":["../../src/core/canvas-svg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Canvas kit: a prewritten template — CSS (light + dark, following the
3
+ * app's own theme) and a documented set of markup building blocks — that an
4
+ * agent clones and fills in with real content, instead of hand-writing a
5
+ * canvas from scratch every time. This is deliberately NOT a JSON-data +
6
+ * JS-renderer scheme: the agent authors real HTML/SVG using the template's
7
+ * classes, so it keeps full expressive freedom over the graph itself while
8
+ * the CSS (and therefore the visual language: colors, glow, edge styling,
9
+ * legend markers) is locked and can't drift.
10
+ *
11
+ * `renderCanvasDocument(bodyHtml)` is the single shared shell (CSS + theme
12
+ * switch) — `TEMPLATE_HTML` (the pristine, empty-state document) and
13
+ * `scripts/seed-example.mjs` (prefilled with real content) both wrap their
14
+ * body through it, so the kit and the seed can't drift from each other.
15
+ */
16
+ /** Lives alongside index.html, in the same CANVAS_DIR — a pristine copy of
17
+ * the template, written once and never touched again, so "clone the
18
+ * template" always has a clean, un-filled-in source to clone from. */
19
+ export declare const CANVAS_TEMPLATE_FILE = ".sapiom/canvas/_template.html";
20
+ /**
21
+ * Wraps `bodyHtml` in the shared canvas document shell: doctype, the theme
22
+ * switch script, and every CSS class an agent's markup can use. This is the
23
+ * single source both the pristine template and `scripts/seed-example.mjs`'s
24
+ * prefilled instance render through, so they can't drift from each other.
25
+ */
26
+ export declare function renderCanvasDocument(bodyHtml: string): string;
27
+ /** The exact document written to both `_template.html` and the initial
28
+ * `index.html` — a friendly empty state plus the patterns reference. */
29
+ export declare const TEMPLATE_HTML: string;
30
+ /**
31
+ * Backfill-only: ensures both `<cwd>/.sapiom/canvas/_template.html` (a
32
+ * pristine copy, so "clone the template" always has a clean source — see
33
+ * the visualize macro's prompt) and `<cwd>/.sapiom/canvas/index.html` (the
34
+ * live canvas, seeded with the same empty-state content) exist. Never
35
+ * clobbers either file if something's already there — an earlier session,
36
+ * or the agent's own edits. Called from SessionManager's create()/resume()
37
+ * (see its `ensureCanvasTemplate` option) so the canvas pane never opens to
38
+ * a completely empty iframe. Best-effort, like the sibling
39
+ * `workspace-context.ts` writer: a session's cwd could be unwritable, and
40
+ * that must never fail session creation itself.
41
+ */
42
+ export declare function ensureCanvasTemplate(cwd: string): Promise<void>;
43
+ //# sourceMappingURL=canvas-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-template.d.ts","sourceRoot":"","sources":["../../src/core/canvas-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH;;uEAEuE;AACvE,eAAO,MAAM,oBAAoB,kCAAiC,CAAC;AAiOnE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAsB7D;AAgCD;yEACyE;AACzE,eAAO,MAAM,aAAa,QAAsC,CAAC;AAiBjE;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrE"}