@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,319 @@
1
+ /**
2
+ * codex adapter — launches/resumes the `codex` CLI and scans its rollout
3
+ * store for resumable history. Codex has no hook system (see
4
+ * core/collector/codex-tailer.ts for how its analytics eventSource works
5
+ * instead); this file only covers process launch/resume/doctor/history.
6
+ *
7
+ * Verified against a locally installed `codex-cli 0.134.0`: `codex resume
8
+ * [SESSION_ID] [PROMPT]` (positional UUID or thread name) via `codex resume
9
+ * --help`; the generic `-c key=value` config-override mechanism via `codex
10
+ * --help`; and, via real spawns with `--strict-config`, that both
11
+ * `developer_instructions` and `model_instructions_file` are real,
12
+ * recognized keys (an earlier version of this file used the latter and
13
+ * flagged both as unconfirmed — see buildConfigArgs below for why the
14
+ * adapter now uses `developer_instructions` instead: `model_instructions_file`
15
+ * makes codex's own startup depend on re-reading a file we already have the
16
+ * content of, and an unreadable path there kills the process instantly with
17
+ * no trust prompt, no TUI — precisely the "session has no live pty" a user
18
+ * sees with zero indication why).
19
+ */
20
+ import { execFile } from "node:child_process";
21
+ import { readFileSync } from "node:fs";
22
+ import { open, readdir, stat } from "node:fs/promises";
23
+ import { homedir } from "node:os";
24
+ import { basename, join } from "node:path";
25
+ import { promisify } from "node:util";
26
+ const execFileAsync = promisify(execFile);
27
+ /** Bytes read from the head of a rollout file to find its session_meta line
28
+ * and an early title candidate. Unlike Claude's transcripts (where a summary
29
+ * lands near the end), Codex has no end-of-session summary line, so the
30
+ * earliest user message is the best available title — and it's always near
31
+ * the start of the file. */
32
+ const ROLLOUT_HEAD_BYTES = 65536;
33
+ const MAX_SCAN_DEPTH = 4; // ~/.codex/sessions/YYYY/MM/DD/*.jsonl
34
+ /**
35
+ * Matches Codex's directory-trust confirmation prompt ("Do you trust the
36
+ * contents of this directory? ... 1. Yes, continue 2. No, quit"), after
37
+ * `stripAnsi` — confirmed against a real capture: Codex's TUI positions
38
+ * each *word* with its own cursor-addressing escape sequence instead of
39
+ * emitting literal spaces between them (`trust\x1b[3;16Hthe\x1b[3;20H...`),
40
+ * so even a stripped buffer runs the words together with no separator at
41
+ * all; `\s*` between them tolerates that (and ordinary whitespace, in case
42
+ * a future TUI revision renders differently). See `detectBlockingPrompt`
43
+ * for why this exists at all (Codex's own structured readiness signal can't
44
+ * be relied on standalone, unlike Claude Code's).
45
+ */
46
+ const TRUST_PROMPT_PATTERN = /trust\s*the\s*contents\s*of\s*this\s*directory/i;
47
+ /** Strips ANSI/CSI/OSC control sequences a pty stream is otherwise full of,
48
+ * so text pattern-matching (e.g. `TRUST_PROMPT_PATTERN`) sees the words a
49
+ * human would actually read rather than raw terminal-control bytes. Not
50
+ * exhaustive of every escape sequence a TUI could emit, but covers what
51
+ * Codex's TUI is confirmed (via real captures) to use: CSI (cursor moves,
52
+ * SGR color/style) and OSC (window title) sequences. */
53
+ export function stripAnsi(text) {
54
+ /* eslint-disable no-control-regex -- matching literal ESC (\x1b) / BEL
55
+ * (\x07) bytes is the entire point of an ANSI-sequence stripper; there's
56
+ * no non-control-character way to express "a real escape sequence". */
57
+ return text
58
+ // Lazy (`*?`), not greedy: a greedy `[^\x07]*` doesn't exclude `\x1b\\`
59
+ // (ST) from what it can consume, so it backtracks from the END of the
60
+ // whole string looking for the last reachable terminator instead of the
61
+ // next one — silently swallowing everything (including real prompt
62
+ // text) between an OSC sequence and some unrelated, much-later BEL/ST.
63
+ // Confirmed against a real capture: this exact bug made the trust-
64
+ // prompt regex below never match a full multi-KB scrollback buffer.
65
+ .replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "") // OSC ... BEL or ST
66
+ .replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, "") // CSI sequences
67
+ .replace(/\x1b[()>=][A-Za-z0-9]?/g, ""); // charset/keypad-mode selection
68
+ /* eslint-enable no-control-regex */
69
+ }
70
+ /** Read only the head of a (possibly huge) rollout file and extract its
71
+ * `session_meta` entry. Codex always writes `session_meta` as the first
72
+ * line, but this tolerates a few leading blank/malformed lines defensively. */
73
+ async function readSessionMeta(filePath, maxBytes = ROLLOUT_HEAD_BYTES) {
74
+ let content;
75
+ try {
76
+ const handle = await open(filePath, "r");
77
+ try {
78
+ const { size } = await handle.stat();
79
+ const length = Math.min(size, maxBytes);
80
+ const buffer = Buffer.allocUnsafe(length);
81
+ await handle.read(buffer, 0, length, 0);
82
+ content = buffer.toString("utf8");
83
+ }
84
+ finally {
85
+ await handle.close();
86
+ }
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ for (const line of content.split("\n").slice(0, 20)) {
92
+ const trimmed = line.trim();
93
+ if (!trimmed)
94
+ continue;
95
+ let parsed;
96
+ try {
97
+ parsed = JSON.parse(trimmed);
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ if (parsed.type !== "session_meta")
103
+ continue;
104
+ const payload = parsed.payload;
105
+ const id = typeof payload?.id === "string" ? payload.id : undefined;
106
+ const cwd = typeof payload?.cwd === "string" ? payload.cwd : undefined;
107
+ if (!id || !cwd)
108
+ return null;
109
+ const timestamp = typeof payload?.timestamp === "string" ? Date.parse(payload.timestamp) : NaN;
110
+ return { id, cwd, timestampMs: Number.isNaN(timestamp) ? null : timestamp };
111
+ }
112
+ return null;
113
+ }
114
+ /** First `event_msg`/`user_message` found in the head of the file, truncated
115
+ * for use as a session title. Falls back to `fallback` when none is found
116
+ * within the head window (long system-prompt-only sessions, mid-tail-cut). */
117
+ function extractTitleFromHead(content, fallback) {
118
+ for (const line of content.split("\n")) {
119
+ const trimmed = line.trim();
120
+ if (!trimmed)
121
+ continue;
122
+ let parsed;
123
+ try {
124
+ parsed = JSON.parse(trimmed);
125
+ }
126
+ catch {
127
+ continue;
128
+ }
129
+ if (parsed.type !== "event_msg" || parsed.payload?.type !== "user_message")
130
+ continue;
131
+ const message = parsed.payload.message;
132
+ if (typeof message === "string" && message.trim()) {
133
+ const text = message.trim();
134
+ return text.length > 120 ? `${text.slice(0, 120)}...` : text;
135
+ }
136
+ }
137
+ return fallback;
138
+ }
139
+ /** Recursively collect `.jsonl` files under Codex's date-sharded sessions
140
+ * root (`YYYY/MM/DD/rollout-*.jsonl`). Bounded depth as a safety guard
141
+ * against unexpectedly deep/cyclical directory structures. */
142
+ async function collectRolloutFiles(dir, depth = 0) {
143
+ if (depth > MAX_SCAN_DEPTH)
144
+ return [];
145
+ let entries;
146
+ try {
147
+ entries = await readdir(dir, { withFileTypes: true });
148
+ }
149
+ catch {
150
+ return [];
151
+ }
152
+ const results = [];
153
+ for (const entry of entries) {
154
+ const fullPath = join(dir, entry.name);
155
+ if (entry.isDirectory()) {
156
+ results.push(...(await collectRolloutFiles(fullPath, depth + 1)));
157
+ }
158
+ else if (entry.name.endsWith(".jsonl")) {
159
+ results.push(fullPath);
160
+ }
161
+ }
162
+ return results;
163
+ }
164
+ export class CodexAdapter {
165
+ constructor(options = {}) {
166
+ this.id = "codex";
167
+ this.eventSource = "transcript-tail";
168
+ this.binary = options.binary ?? "codex";
169
+ this.homeDir = options.homeDir ?? homedir();
170
+ }
171
+ async doctor() {
172
+ try {
173
+ const { stdout } = await execFileAsync(this.binary, ["--version"], { timeout: 5000 });
174
+ return [{ name: "codex", ok: true, detail: stdout.trim() || "installed" }];
175
+ }
176
+ catch {
177
+ return [
178
+ {
179
+ name: "codex",
180
+ ok: false,
181
+ detail: `\`${this.binary}\` not found on PATH. Install: https://github.com/openai/codex`,
182
+ },
183
+ ];
184
+ }
185
+ }
186
+ launch(opts) {
187
+ return {
188
+ command: this.binary,
189
+ args: buildConfigArgs(opts),
190
+ // Codex has no analog to Claude's CLAUDECODE nested-agent guard; no env
191
+ // overrides are needed for a fresh launch.
192
+ env: {},
193
+ cwd: opts.cwd,
194
+ };
195
+ }
196
+ resume(agentSessionId, opts) {
197
+ return {
198
+ command: this.binary,
199
+ args: ["resume", agentSessionId, ...buildConfigArgs(opts)],
200
+ env: {},
201
+ cwd: opts.cwd,
202
+ };
203
+ }
204
+ /**
205
+ * See `HarnessAdapter.detectBlockingPrompt`. Codex needs this (Claude Code
206
+ * doesn't) because its rollout file — and therefore the SessionStart-
207
+ * equivalent event `SessionManager` otherwise waits on — isn't written
208
+ * until the *first* turn is actually submitted, confirmed empirically
209
+ * against a real `codex-cli 0.134.0`: an idle, fully-interactive session
210
+ * (trust prompt already accepted, composer visibly ready) produces zero
211
+ * files under `~/.codex/sessions` for as long as nothing is submitted.
212
+ * That real signal therefore can't arrive before the very first
213
+ * injection that needs it — a scrollback check is the only thing that
214
+ * can stand in for it up to that point.
215
+ */
216
+ detectBlockingPrompt(scrollback) {
217
+ return TRUST_PROMPT_PATTERN.test(stripAnsi(scrollback));
218
+ }
219
+ async listPastSessions(cwd) {
220
+ const root = join(this.homeDir, ".codex", "sessions");
221
+ const files = await collectRolloutFiles(root);
222
+ const summaries = [];
223
+ for (const filePath of files) {
224
+ const meta = await readSessionMeta(filePath);
225
+ if (!meta || meta.cwd !== cwd)
226
+ continue;
227
+ const fileStat = await stat(filePath).catch(() => undefined);
228
+ const lastActiveAt = fileStat ? fileStat.mtime.toISOString() : new Date(0).toISOString();
229
+ let title = basename(filePath, ".jsonl");
230
+ try {
231
+ const handle = await open(filePath, "r");
232
+ try {
233
+ const { size } = await handle.stat();
234
+ const length = Math.min(size, ROLLOUT_HEAD_BYTES);
235
+ const buffer = Buffer.allocUnsafe(length);
236
+ await handle.read(buffer, 0, length, 0);
237
+ title = extractTitleFromHead(buffer.toString("utf8"), meta.id);
238
+ }
239
+ finally {
240
+ await handle.close();
241
+ }
242
+ }
243
+ catch {
244
+ // Fall back to the rollout id as the title.
245
+ }
246
+ summaries.push({
247
+ agentSessionId: meta.id,
248
+ harness: "codex",
249
+ cwd,
250
+ title,
251
+ lastActiveAt,
252
+ source: "transcript",
253
+ });
254
+ }
255
+ return summaries.sort((a, b) => (a.lastActiveAt < b.lastActiveAt ? 1 : -1));
256
+ }
257
+ }
258
+ /**
259
+ * Codex has no single-flag equivalent to Claude's `--append-system-prompt` /
260
+ * `--mcp-config` — MCP servers are registered globally via `codex mcp add`
261
+ * (a persistent config.toml mutation, which the harness's "zero config
262
+ * mutation" design deliberately avoids), so `opts.mcpConfigFile` /
263
+ * `opts.settingsFile` are intentionally unused here. System-prompt injection
264
+ * uses the generic `-c key=value` override mechanism instead.
265
+ *
266
+ * Confirmed against a locally installed codex-cli 0.134.0: `-c
267
+ * model_instructions_file=<path>` is a real, recognized key — but if that
268
+ * path is missing/unreadable at codex's own startup (a moment we don't
269
+ * control, in a separate process), codex exits immediately with a config
270
+ * error, no trust prompt, no TUI — which reads to a user as the session
271
+ * dying instantly. Since we already have the prompt's content in hand
272
+ * (we're the ones who generated the file), embedding it inline via
273
+ * `developer_instructions=<value>` instead removes that dependency
274
+ * entirely: nothing for codex to fail to (re)read. `-c` values parse as
275
+ * TOML, and TOML basic strings share JSON's escaping rules for control
276
+ * characters/quotes/backslashes, so `JSON.stringify` produces a valid TOML
277
+ * string literal here — confirmed with a real multiline prompt.
278
+ *
279
+ * If even this read fails (the file we just generated is somehow gone by
280
+ * the time we get here — a narrow race, but still no reason to crash the
281
+ * whole session over an optional prompt), fall back to launching without
282
+ * one rather than passing a broken reference that's guaranteed to kill the
283
+ * process on startup.
284
+ */
285
+ function buildConfigArgs(opts) {
286
+ const args = [
287
+ "-c",
288
+ "check_for_update_on_startup=false",
289
+ // Codex's default approval policy interrupts the session with "Would you
290
+ // like to run the following command?" prompts (even for read-only
291
+ // commands like `ps`). Harness sessions are expected to run without
292
+ // approval interruptions — the analog of Claude Code sessions, which the
293
+ // harness runs in auto mode — so pin codex's non-interactive pairing:
294
+ // never ask for approval, and confine writes to the workspace via the OS
295
+ // sandbox instead of via per-command human review. Both keys and their
296
+ // values confirmed against a locally installed codex-cli 0.134.0 (its
297
+ // config deserializer enumerates `never` / `workspace-write` among the
298
+ // accepted variants when probed with a bogus value). `-c` values parse
299
+ // as TOML, so the string values need the embedded quotes.
300
+ "-c",
301
+ 'approval_policy="never"',
302
+ "-c",
303
+ 'sandbox_mode="workspace-write"',
304
+ ];
305
+ if (opts.systemPromptFile) {
306
+ try {
307
+ const prompt = readFileSync(opts.systemPromptFile, "utf8");
308
+ args.push("-c", `developer_instructions=${JSON.stringify(prompt)}`);
309
+ }
310
+ catch (err) {
311
+ console.error(`[codex adapter] could not read systemPromptFile "${opts.systemPromptFile}" — launching without an injected system prompt: ${err.message}`);
312
+ }
313
+ }
314
+ return args;
315
+ }
316
+ export function createCodexAdapter(options) {
317
+ return new CodexAdapter(options);
318
+ }
319
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/core/adapters/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Assembles the `<div id="canvas-root">` body — the bound workflow's
3
+ * `<section class="canvas-panel">` (header + stats + SVG diagram) and a
4
+ * legend footer — entirely from the classes `core/canvas-template.ts`'s shell
5
+ * already defines. This is the HTML half of the deterministic render;
6
+ * `core/canvas-render.ts` wraps the result through `renderCanvasDocument()`
7
+ * and writes it to the workflow's render file.
8
+ */
9
+ import type { CanvasEdgeKind, CanvasGraph, CanvasNodeKind } from "./canvas-graph.js";
10
+ import type { CanvasEnrichment } from "./canvas-enrichment.js";
11
+ export interface WorkflowPanelMeta {
12
+ title: string;
13
+ badges?: string[];
14
+ }
15
+ /** How enrichment content reaches the panel/footer builders: the validated
16
+ * content itself plus whether its source fingerprint no longer matches the
17
+ * workflow's current sources (kept displayed, marked stale). */
18
+ export interface PanelEnrichment {
19
+ enrichment: CanvasEnrichment;
20
+ stale: boolean;
21
+ }
22
+ /** One workflow's full panel: title/badges header, step/entry stats, SVG
23
+ * diagram — with any enrichment merged in (summary line, node/edge
24
+ * annotations, a "stale" chip when its fingerprint no longer matches). */
25
+ export declare function buildWorkflowPanelHtml(graph: CanvasGraph, meta: WorkflowPanelMeta, panelEnrichment?: PanelEnrichment | null): string;
26
+ /** A degraded panel for a workflow whose graph couldn't be extracted — never
27
+ * a crash, never a silent fallback to the LLM path, just an honest reason
28
+ * styled through the same shell. */
29
+ export declare function buildErrorPanelHtml(title: string, reason: string): string;
30
+ /** Legend footer covering every node/edge kind actually used across every
31
+ * rendered panel (aggregate, not per-panel — matches the template's shape). */
32
+ export declare function buildLegendHtml(nodeKinds: Set<CanvasNodeKind>, edgeKinds: Set<CanvasEdgeKind>): string;
33
+ /** Joins panels + a footer (enrichment notes/cross-workflow tie, legend,
34
+ * note) into the final `#canvas-root` body — the string
35
+ * `renderCanvasDocument()` wraps. */
36
+ export declare function assembleCanvasBody(input: {
37
+ panels: string[];
38
+ legend: string;
39
+ note: string;
40
+ /** Enrichment footer facts (already schema-bounded), rendered as a list. */
41
+ notes?: string[];
42
+ /** Enrichment's "how this ties into the other workflows" line. */
43
+ crossWorkflow?: string;
44
+ }): string;
45
+ //# sourceMappingURL=canvas-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-body.d.ts","sourceRoot":"","sources":["../../src/core/canvas-body.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO/D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;iEAEiE;AACjE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;2EAE2E;AAC3E,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,iBAAiB,EACvB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,GACvC,MAAM,CA4BR;AAED;;qCAEqC;AACrC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAYzE;AAmBD;gFACgF;AAChF,wBAAgB,eAAe,CAAC,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,GAAG,MAAM,CAWtG;AAED;;sCAEsC;AACtC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,MAAM,CAcT"}
@@ -0,0 +1,94 @@
1
+ import { renderGraphSvg } from "./canvas-svg.js";
2
+ function esc(s) {
3
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4
+ }
5
+ /** One workflow's full panel: title/badges header, step/entry stats, SVG
6
+ * diagram — with any enrichment merged in (summary line, node/edge
7
+ * annotations, a "stale" chip when its fingerprint no longer matches). */
8
+ export function buildWorkflowPanelHtml(graph, meta, panelEnrichment) {
9
+ const badges = (meta.badges ?? [])
10
+ .map((b) => `<span class="canvas-badge">${esc(b)}</span>`)
11
+ .join("");
12
+ const warningBadge = graph.warnings.length > 0
13
+ ? `<span class="canvas-badge">${graph.warnings.length} warning${graph.warnings.length === 1 ? "" : "s"}</span>`
14
+ : "";
15
+ const enrichment = panelEnrichment?.enrichment ?? null;
16
+ const staleBadge = panelEnrichment?.stale
17
+ ? `<span class="canvas-badge canvas-badge--stale">stale — Refresh</span>`
18
+ : "";
19
+ const summary = enrichment?.summary ? `\n <p class="canvas-subtitle">${esc(enrichment.summary)}</p>` : "";
20
+ return `<section class="canvas-panel">
21
+ <header class="canvas-header">
22
+ <div class="canvas-title-row">
23
+ <h1 class="canvas-title">${esc(meta.title)}</h1>
24
+ ${badges}${warningBadge}${staleBadge}
25
+ </div>${summary}
26
+ <div class="canvas-stats">
27
+ <div class="canvas-stat"><span class="canvas-stat-value">${graph.nodes.length}</span><span class="canvas-stat-label">steps</span></div>
28
+ <div class="canvas-stat"><span class="canvas-stat-value">${esc(graph.entry)}</span><span class="canvas-stat-label">entry</span></div>
29
+ </div>
30
+ </header>
31
+ <div class="canvas-diagram-panel">
32
+ ${renderGraphSvg(graph, enrichment)}
33
+ </div>
34
+ </section>`;
35
+ }
36
+ /** A degraded panel for a workflow whose graph couldn't be extracted — never
37
+ * a crash, never a silent fallback to the LLM path, just an honest reason
38
+ * styled through the same shell. */
39
+ export function buildErrorPanelHtml(title, reason) {
40
+ return `<section class="canvas-panel">
41
+ <header class="canvas-header">
42
+ <div class="canvas-title-row">
43
+ <h1 class="canvas-title">${esc(title)}</h1>
44
+ <span class="canvas-badge">render failed</span>
45
+ </div>
46
+ </header>
47
+ <div class="canvas-diagram-panel">
48
+ <p class="canvas-empty-note">Could not extract this workflow's step graph: ${esc(reason)}. Ask your agent to fix the issue (see the terminal for details) — this pane updates automatically once it builds cleanly.</p>
49
+ </div>
50
+ </section>`;
51
+ }
52
+ const NODE_KIND_LABEL = {
53
+ entry: "entry / active step",
54
+ step: "step",
55
+ pause: "pause / waits for input",
56
+ "terminal-success": "terminal · success",
57
+ "terminal-warn": "terminal · escalation",
58
+ "launched-workflow": "launches another workflow",
59
+ };
60
+ const NODE_KIND_ORDER = [
61
+ "entry",
62
+ "step",
63
+ "pause",
64
+ "terminal-success",
65
+ "terminal-warn",
66
+ "launched-workflow",
67
+ ];
68
+ /** Legend footer covering every node/edge kind actually used across every
69
+ * rendered panel (aggregate, not per-panel — matches the template's shape). */
70
+ export function buildLegendHtml(nodeKinds, edgeKinds) {
71
+ const items = NODE_KIND_ORDER.filter((k) => nodeKinds.has(k)).map((k) => `<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--${k}"></span>${NODE_KIND_LABEL[k]}</span>`);
72
+ if (edgeKinds.has("cross")) {
73
+ items.push(`<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--cross"></span>cross-workflow signal/handoff</span>`);
74
+ }
75
+ return `<div class="canvas-legend">${items.join("")}</div>`;
76
+ }
77
+ /** Joins panels + a footer (enrichment notes/cross-workflow tie, legend,
78
+ * note) into the final `#canvas-root` body — the string
79
+ * `renderCanvasDocument()` wraps. */
80
+ export function assembleCanvasBody(input) {
81
+ const parts = [...input.panels];
82
+ const notesHtml = input.notes && input.notes.length > 0
83
+ ? `\n <ul class="canvas-notes">${input.notes.map((n) => `<li>${esc(n)}</li>`).join("")}</ul>`
84
+ : "";
85
+ const crossHtml = input.crossWorkflow
86
+ ? `\n <p class="canvas-cross-workflow">${esc(input.crossWorkflow)}</p>`
87
+ : "";
88
+ parts.push(`<footer class="canvas-footer">${notesHtml}${crossHtml}
89
+ ${input.legend}
90
+ <p class="canvas-note">${esc(input.note)}</p>
91
+ </footer>`);
92
+ return parts.join("\n\n");
93
+ }
94
+ //# sourceMappingURL=canvas-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-body.js","sourceRoot":"","sources":["../../src/core/canvas-body.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type ExtractionResult } from "./canvas-graph.js";
2
+ /** `<file count>:<newest mtimeMs>` over the workflow's own sources. */
3
+ export declare function fingerprintWorkflowSources(root: string): Promise<string>;
4
+ export interface CachedExtraction {
5
+ result: ExtractionResult;
6
+ /** True when the result came from cache — no child process ran. */
7
+ cached: boolean;
8
+ /** The source fingerprint the result corresponds to — the same value the
9
+ * enrichment cache stores as `sourceFingerprint`, so freshness checks
10
+ * compare like with like. */
11
+ fingerprint: string;
12
+ }
13
+ /**
14
+ * `extractWorkflowGraph` behind the fingerprint cache. The `extract`
15
+ * parameter exists for tests only (inject a spy to prove hit/miss behavior).
16
+ */
17
+ export declare function extractWorkflowGraphCached(sourceDir: string, extract?: (dir: string) => Promise<ExtractionResult>): Promise<CachedExtraction>;
18
+ /** Drops one workflow's cached extraction — the visualize macro's force
19
+ * refresh, which must re-run the child process even for unchanged sources. */
20
+ export declare function invalidateExtractionCache(sourceDir: string): void;
21
+ /** Test hook — the cache is module-level state shared across a process. */
22
+ export declare function clearExtractionCache(): void;
23
+ //# sourceMappingURL=canvas-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-cache.d.ts","sourceRoot":"","sources":["../../src/core/canvas-cache.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAwB,KAAK,gBAAgB,EAA0B,MAAM,mBAAmB,CAAC;AAGxG,uEAAuE;AACvE,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9E;AASD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC;IAChB;;kCAE8B;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,CAAwB,GACzE,OAAO,CAAC,gBAAgB,CAAC,CAU3B;AAED;+EAC+E;AAC/E,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEjE;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * In-memory extraction cache: binding an unchanged workflow must never pay
3
+ * for a child `check()` process twice. Keyed by workflow directory, guarded
4
+ * by a cheap source fingerprint (file count + newest mtime over the
5
+ * project's own `.ts`/`.tsx` sources — the same file walk the launch grep
6
+ * uses, see core/canvas-interconnections.ts's `listSourceFiles`). Any source
7
+ * edit bumps an mtime, any add/remove changes the count; either invalidates.
8
+ *
9
+ * Only SUCCESSFUL extractions are cached: a failure like "run npm install
10
+ * first" gets fixed without touching any `.ts` file, so a cached failure
11
+ * would never self-invalidate — re-running the (already fast-failing)
12
+ * extraction is the honest choice there.
13
+ *
14
+ * Process-lifetime only; a server restart re-extracts once per workflow.
15
+ */
16
+ import * as fs from "node:fs/promises";
17
+ import * as path from "node:path";
18
+ import { extractWorkflowGraph } from "./canvas-graph.js";
19
+ import { listSourceFiles } from "./canvas-interconnections.js";
20
+ /** `<file count>:<newest mtimeMs>` over the workflow's own sources. */
21
+ export async function fingerprintWorkflowSources(root) {
22
+ const files = await listSourceFiles(root);
23
+ let maxMtimeMs = 0;
24
+ for (const file of files) {
25
+ try {
26
+ const stat = await fs.stat(file);
27
+ if (stat.mtimeMs > maxMtimeMs)
28
+ maxMtimeMs = stat.mtimeMs;
29
+ }
30
+ catch {
31
+ // A file deleted mid-walk still counts toward the file count; the next
32
+ // fingerprint won't include it, which is invalidation working as intended.
33
+ }
34
+ }
35
+ return `${files.length}:${maxMtimeMs}`;
36
+ }
37
+ const cache = new Map();
38
+ /**
39
+ * `extractWorkflowGraph` behind the fingerprint cache. The `extract`
40
+ * parameter exists for tests only (inject a spy to prove hit/miss behavior).
41
+ */
42
+ export async function extractWorkflowGraphCached(sourceDir, extract = extractWorkflowGraph) {
43
+ const key = path.resolve(sourceDir);
44
+ const fingerprint = await fingerprintWorkflowSources(key);
45
+ const hit = cache.get(key);
46
+ if (hit && hit.fingerprint === fingerprint)
47
+ return { result: hit.result, cached: true, fingerprint };
48
+ const result = await extract(key);
49
+ if (result.ok)
50
+ cache.set(key, { fingerprint, result });
51
+ else
52
+ cache.delete(key);
53
+ return { result, cached: false, fingerprint };
54
+ }
55
+ /** Drops one workflow's cached extraction — the visualize macro's force
56
+ * refresh, which must re-run the child process even for unchanged sources. */
57
+ export function invalidateExtractionCache(sourceDir) {
58
+ cache.delete(path.resolve(sourceDir));
59
+ }
60
+ /** Test hook — the cache is module-level state shared across a process. */
61
+ export function clearExtractionCache() {
62
+ cache.clear();
63
+ }
64
+ //# sourceMappingURL=canvas-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-cache.js","sourceRoot":"","sources":["../../src/core/canvas-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAiD,MAAM,mBAAmB,CAAC;AACxG,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,IAAY;IAC3D,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,IAAI,CAAC,OAAO,GAAG,UAAU;gBAAE,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,2EAA2E;QAC7E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;AACzC,CAAC;AAOD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;AAY5C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,SAAiB,EACjB,UAAsD,oBAAoB;IAE1E,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,MAAM,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,GAAG,IAAI,GAAG,CAAC,WAAW,KAAK,WAAW;QAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAErG,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;;QAClD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAChD,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,yBAAyB,CAAC,SAAiB;IACzD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB;IAClC,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}