@huynle/hub 0.8.7 → 0.9.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 (181) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +69 -12
  3. package/package.json +5 -1
  4. package/packages/claude-code/package.json +1 -1
  5. package/packages/cli/dist/hub-cli.js +278 -35
  6. package/packages/cli/package.json +1 -1
  7. package/packages/cli/src/cli.ts +23 -10
  8. package/packages/cli/src/http.ts +1 -0
  9. package/packages/cli/src/launches.ts +5 -1
  10. package/packages/cli/src/mcp.ts +28 -8
  11. package/packages/cli/src/messaging.ts +14 -0
  12. package/packages/cli/src/team-intake-mcp.ts +26 -2
  13. package/packages/codex/package.json +1 -1
  14. package/packages/node/package.json +1 -1
  15. package/packages/preset/package.json +1 -1
  16. package/packages/protocol/package.json +1 -1
  17. package/packages/protocol/schema/app-delivery/v1.schema.json +677 -0
  18. package/packages/protocol/src/artifacts.ts +43 -0
  19. package/packages/protocol/src/credentials.ts +38 -0
  20. package/packages/protocol/src/index.ts +56 -5
  21. package/packages/protocol/src/launch.ts +7 -2
  22. package/packages/protocol/src/markdown.ts +753 -0
  23. package/packages/protocol/src/media-allowlist.ts +240 -0
  24. package/packages/protocol/src/media.ts +120 -0
  25. package/packages/protocol/src/preview-bundle.ts +221 -0
  26. package/packages/protocol/src/voice-recording.ts +8 -0
  27. package/packages/runtime-openshell/package.json +1 -1
  28. package/packages/runtime-sdk/package.json +1 -1
  29. package/packages/sdk/package.json +1 -1
  30. package/packages/sdk/src/client.ts +17 -3
  31. package/packages/sdk/src/errors.ts +1 -0
  32. package/packages/sdk/src/team-intake.ts +8 -0
  33. package/packages/sdk/src/types.ts +16 -2
  34. package/packages/spoke-sdk/package.json +1 -1
  35. package/src/central/agent/delivery.ts +35 -6
  36. package/src/central/agent/errors.ts +2 -0
  37. package/src/central/agent/events.ts +11 -2
  38. package/src/central/agent/launch-engine.ts +65 -4
  39. package/src/central/agent/launch-routes.ts +8 -0
  40. package/src/central/agent/launch-spec.ts +18 -1
  41. package/src/central/agent/launch-store.ts +2 -2
  42. package/src/central/agent/message-routes.ts +49 -11
  43. package/src/central/agent/messages.ts +17 -4
  44. package/src/central/agent/not-after.ts +28 -0
  45. package/src/central/agent/placement.ts +36 -4
  46. package/src/central/agent/routes.ts +3 -1
  47. package/src/central/agent/webhook-routes.ts +21 -9
  48. package/src/central/agent/webhooks.ts +27 -9
  49. package/src/central/artifacts/host.ts +3 -1
  50. package/src/central/artifacts/maintenance.ts +2 -1
  51. package/src/central/artifacts/preview-bundle.ts +81 -0
  52. package/src/central/artifacts/previews.ts +94 -0
  53. package/src/central/artifacts/routes.ts +12 -1
  54. package/src/central/artifacts/store.ts +198 -8
  55. package/src/central/config.ts +34 -0
  56. package/src/central/fetch-budget.ts +59 -0
  57. package/src/central/fetcher.ts +372 -0
  58. package/src/central/http.ts +277 -22
  59. package/src/central/media-remote.ts +248 -0
  60. package/src/central/media-timeline.ts +317 -0
  61. package/src/central/media-unfurl.ts +119 -0
  62. package/src/central/profiles/content.ts +3 -35
  63. package/src/central/recording/routes.ts +2 -2
  64. package/src/central/recording/schema.ts +3 -1
  65. package/src/central/registry.ts +449 -21
  66. package/src/central/spoke-ws.ts +17 -3
  67. package/src/central/store.ts +21 -0
  68. package/src/central/teams/intake-routes.ts +2 -1
  69. package/src/central/teams/intake.ts +200 -15
  70. package/src/central/unfurl-parse.ts +313 -0
  71. package/src/index.ts +68 -8
  72. package/src/speakable.ts +33 -1
  73. package/src/spoke/conversation.ts +7 -4
  74. package/src/spoke/deliver.ts +19 -0
  75. package/src/spoke/media-bytes.ts +171 -0
  76. package/src/spoke/media-policy.ts +394 -0
  77. package/src/spoke/media.ts +189 -0
  78. package/src/spoke/own-server.ts +30 -0
  79. package/src/spoke/preview-bundle.ts +538 -0
  80. package/src/spoke/spoke.ts +387 -17
  81. package/src/spoke/timeline.ts +59 -5
  82. package/src/spoke/voice-front.ts +335 -0
  83. package/web/dist/assets/24BXRY42-Zfr7jm_p.js +2 -0
  84. package/web/dist/assets/4FHPYW2L-C6Z67098.js +2 -0
  85. package/web/dist/assets/4LWW3ZNH-CUiYeygH.js +1 -0
  86. package/web/dist/assets/5THPTBWS-Ce_hS451.js +1 -0
  87. package/web/dist/assets/5XYWFMKW-CW-BCKiK.js +4 -0
  88. package/web/dist/assets/64PMEAB3-BjpCDGU9.js +8 -0
  89. package/web/dist/assets/6BEWHPQE-BpNa96Ze.js +2 -0
  90. package/web/dist/assets/6PIGVMFT-YXn9Hgnb.js +1 -0
  91. package/web/dist/assets/75JW6PN6-BFpDBuGN.js +2 -0
  92. package/web/dist/assets/7WLXJQOR-D6QVYUu9.js +1 -0
  93. package/web/dist/assets/7XWI72E6-BMuIspEy.js +2 -0
  94. package/web/dist/assets/A4UD2L5Z-BQzcD9dU.js +8 -0
  95. package/web/dist/assets/ArtifactPage-4GoBqSpU.js +1 -0
  96. package/web/dist/assets/ArtifactPane-Dx_0iWwH.js +1 -0
  97. package/web/dist/assets/ArtifactWorkbench-CuHgARMk.js +6 -0
  98. package/web/dist/assets/BGW36YJW-CJMhya8S.js +4 -0
  99. package/web/dist/assets/CGIZI67J-DYDSLME8.js +2 -0
  100. package/web/dist/assets/DKTLEL5E-DciMwFJ1.js +1 -0
  101. package/web/dist/assets/DNNCRC2E-BIeKf5t-.js +5 -0
  102. package/web/dist/assets/Diagram-CZM_R7MG.js +1 -0
  103. package/web/dist/assets/Diagram-vdpmp3xu.css +1 -0
  104. package/web/dist/assets/E67EBMDD--AucIw59.js +5 -0
  105. package/web/dist/assets/EFYWJBJK-CVywe_D5.js +1 -0
  106. package/web/dist/assets/EXE5EH6O-DR1IHCTr.js +4 -0
  107. package/web/dist/assets/FTXIJQGB-BZVuiBn1.js +1 -0
  108. package/web/dist/assets/G3MDBVQH-BZmnMkFw.js +1 -0
  109. package/web/dist/assets/G6GPDDNC-DEz8F449.js +2 -0
  110. package/web/dist/assets/G76NGGFM-CrB2V34K.js +1 -0
  111. package/web/dist/assets/GH6G3TJC-D3RTYEH4.js +2 -0
  112. package/web/dist/assets/HPNUA7RO-B19NeNOJ.js +1 -0
  113. package/web/dist/assets/HQRGC5D7-Dmpzq6Rn.js +4 -0
  114. package/web/dist/assets/IX6Z2TVR-DphRHnek.js +5 -0
  115. package/web/dist/assets/J77NQRCJ-B7AGcxvw.js +3 -0
  116. package/web/dist/assets/JAD36DTZ-B9-dbsS0.js +5 -0
  117. package/web/dist/assets/LF3NJQAX-koL91UZ9.js +2 -0
  118. package/web/dist/assets/LRZCOFJZ-Cdjasfuf.js +6 -0
  119. package/web/dist/assets/Library-TwvrwqSk.js +2 -0
  120. package/web/dist/assets/Lightbox-C8KNKwOA.css +1 -0
  121. package/web/dist/assets/Lightbox-Du56MXGX.js +1 -0
  122. package/web/dist/assets/MediaImage-Ds_JUx3a.js +4 -0
  123. package/web/dist/assets/N5NF2FGY-OuapYTfr.js +2 -0
  124. package/web/dist/assets/N7OWWQHY-BNLd-Q_U.js +2 -0
  125. package/web/dist/assets/NT5JY5WP-1lJM-aun.js +1 -0
  126. package/web/dist/assets/OT7W3RNY-jDL3drzX.js +4 -0
  127. package/web/dist/assets/P67P66DB-CE-opRb8.js +3 -0
  128. package/web/dist/assets/PFLLLM7K-DDJFgzW3.js +2 -0
  129. package/web/dist/assets/PUAMC4MJ-CtBm-WuU.js +12 -0
  130. package/web/dist/assets/PVFCHRLY-CG5JXv60.js +2 -0
  131. package/web/dist/assets/ProfilesDestination-rKNDpLjR.js +1 -0
  132. package/web/dist/assets/QQW2P742-BbcEkpa4.js +1 -0
  133. package/web/dist/assets/RVARI57O-CgA1WoRv.js +1 -0
  134. package/web/dist/assets/S63OEDPG-CKlhpvl2.js +3 -0
  135. package/web/dist/assets/SG5TVYLW-BBtiwXPL.js +2 -0
  136. package/web/dist/assets/TeamsPage-DtJylLrt.js +1 -0
  137. package/web/dist/assets/UGRSUGA2-urZ_hx83.js +5 -0
  138. package/web/dist/assets/V44WA6U7-DYfX3OlM.js +1 -0
  139. package/web/dist/assets/V67O4WWV-xYxJ00QW.js +1 -0
  140. package/web/dist/assets/VBUCM3YD-JHGjuJC_.js +1 -0
  141. package/web/dist/assets/VINGRCPA-DZuJa3SH.js +7 -0
  142. package/web/dist/assets/WL6DNKX4-BHqripdA.js +2 -0
  143. package/web/dist/assets/WQ63CJYP-D6G5frDY.js +2 -0
  144. package/web/dist/assets/Watchtower-Bmgdyfjb.js +2 -0
  145. package/web/dist/assets/XOYAOB3S-L-KeA1EF.js +13 -0
  146. package/web/dist/assets/Y5LQLG3M-dr2-erb8.js +2 -0
  147. package/web/dist/assets/YIK6JTN6--1t-Ud8k.js +2 -0
  148. package/web/dist/assets/artifacts-DU9uRgs1.js +1 -0
  149. package/web/dist/assets/index-C3ci5UtY.css +1 -0
  150. package/web/dist/assets/index-D-xG9nrc.js +36 -0
  151. package/web/dist/assets/lazy-f3CuUNJZ.js +43 -0
  152. package/web/dist/assets/live-7q72Lyj2.js +5 -0
  153. package/web/dist/assets/mermaid.worker-WDVRAh32.js +1 -0
  154. package/web/dist/assets/pane-layout-0RrLHGDr.js +1 -0
  155. package/web/dist/assets/preload-helper-BLdHbfdN.js +1 -0
  156. package/web/dist/assets/{renderer2d-CYag1CnR.js → renderer2d-hfZxM6Xl.js} +1 -1
  157. package/web/dist/assets/{renderer3d-BRB6rgDJ.js → renderer3d-Km9vipTM.js} +1 -1
  158. package/web/dist/assets/{safe-area-fOy0AOuG.js → safe-area-BAbgBiFk.js} +1 -1
  159. package/web/dist/assets/{silero-worker-DhlIHyD_.js → silero-worker-e802lB5T.js} +1 -1
  160. package/web/dist/assets/smart-turn-v3.2-cpu-WpeLRbPA.onnx +0 -0
  161. package/web/dist/assets/smart-turn-worker-DaVJPdvJ.js +11 -0
  162. package/web/dist/assets/source-BWlvaODa.js +1 -0
  163. package/web/dist/assets/toast-Cuu2K0hx.js +1 -0
  164. package/web/dist/assets/ui-CFB4wQfF.js +3 -0
  165. package/web/dist/index.html +8 -6
  166. package/web/src/voice/turn/LICENSE +28 -0
  167. package/web/dist/assets/ArtifactPage-Dq7VW-bU.js +0 -1
  168. package/web/dist/assets/ArtifactPane-DKwbMTX9.js +0 -1
  169. package/web/dist/assets/ArtifactWorkbench-vrgIfncX.js +0 -6
  170. package/web/dist/assets/Library-D6-Go6Cu.js +0 -2
  171. package/web/dist/assets/ProfilesDestination-BOML2Dgf.js +0 -1
  172. package/web/dist/assets/TeamsPage-CNBTE8gB.js +0 -1
  173. package/web/dist/assets/Watchtower-Dor1Ubjy.js +0 -2
  174. package/web/dist/assets/hub-CrbItiwP.js +0 -13
  175. package/web/dist/assets/index-82Ce0IAf.css +0 -1
  176. package/web/dist/assets/index-D9SP3_0z.js +0 -22
  177. package/web/dist/assets/live-BNJgy0vg.js +0 -5
  178. package/web/dist/assets/pane-layout-EdR1kRrG.js +0 -1
  179. package/web/dist/assets/preload-helper-DqrJTq0t.js +0 -1
  180. package/web/dist/assets/source-Wbq35qGp.js +0 -1
  181. package/web/dist/assets/ui-j5FFk90B.js +0 -3
@@ -1,11 +1,22 @@
1
- import { DELIVER_ID_MAX, MESSAGE_KINDS, PROMPT_IMAGE_MIMES, redactEmbeddedMedia, summarizeToolInput, type MessageKind, type PromptImage, type TimelineMessageItem } from "../protocol"
1
+ import { DELIVER_ID_MAX, MEDIA_LIMITS, mediaPathRef, MESSAGE_KINDS, PROMPT_IMAGE_MIMES, redactEmbeddedMedia, summarizeToolInput, type MessageKind, type PromptImage, type TimelineMessageItem } from "../protocol"
2
2
  import { PROMPT_SOURCE } from "./conversation"
3
+ import { base64Bytes, base64Dimensions, mediaMime, mimeOfPath, parseDataURI } from "./media-bytes"
4
+ import { markdownImageSources, sourcePath } from "./media-policy"
3
5
 
4
- /** An attachment of a user message: its position in the message's files (for timelineFile), type and name. */
6
+ /**
7
+ * Media of a row: a user message's attachment (its position in the message's files, for timelineFile), a tool
8
+ * row's file content item (its position in the tool's content) or an image path in an assistant text row (its
9
+ * position among them; `name` is the path as written). `ref` names it for timelineMedia; `bytes`, `width` and
10
+ * `height` when the spoke knows them without reading the file (embedded bytes and their header).
11
+ */
5
12
  export interface TimelineFile {
6
13
  readonly index: number
14
+ readonly ref: string
7
15
  readonly mime: string
8
16
  readonly name?: string
17
+ readonly bytes?: number
18
+ readonly width?: number
19
+ readonly height?: number
9
20
  }
10
21
 
11
22
  export interface TimelineItem {
@@ -22,6 +33,8 @@ export interface TimelineItem {
22
33
  readonly at?: number
23
34
  readonly completed?: number
24
35
  readonly files?: readonly TimelineFile[]
36
+ /** (S4 M2) Tool rows: their image file items; assistant text rows: local image paths in the text (≤ MEDIA_LIMITS.perRow). */
37
+ readonly media?: readonly TimelineFile[]
25
38
  /**
26
39
  * Ordering position: rows sort by it (never by `at`, whose clocks differ between parts). It follows the order
27
40
  * of the session's messages and of the parts within a message, and depends only on the message's own creation
@@ -108,7 +121,8 @@ export function timelineItems(sessionID: string, messages: readonly unknown[]):
108
121
  const base = { key: `${message.id}:${partID}`, messageID: message.id, partID, sessionID, ...(at === undefined ? {} : { at }), pos: pos(index) }
109
122
  if (part.type === "text" && typeof part.text === "string") {
110
123
  const ordinal = texts++
111
- if (part.text) out.push({ ...base, kind: "assistant", text: part.text, ordinal })
124
+ const media = textMedia(part.text)
125
+ if (part.text) out.push({ ...base, kind: "assistant", text: part.text, ordinal, ...(media.length ? { media } : {}) })
112
126
  }
113
127
  if (part.type === "reasoning") out.push({ ...base, kind: "reasoning", text: String(part.text ?? ""), state: part.time?.completed === undefined ? "running" : "done", ...(typeof part.time?.created === "number" ? { at: part.time.created } : {}), ...(typeof part.time?.completed === "number" ? { completed: part.time.completed } : {}) })
114
128
  if (part.type === "tool") {
@@ -117,6 +131,7 @@ export function timelineItems(sessionID: string, messages: readonly unknown[]):
117
131
  const spawn = SUBAGENT_TOOLS.has(name) ? subagentOf(part.state) : {}
118
132
  // (CON-09b) The same short summary live activity shows (the command, the path, the hub verb).
119
133
  const summary = summarizeToolInput(part.state?.input)
134
+ const media = toolMedia(part.state, index)
120
135
  out.push({
121
136
  ...base,
122
137
  kind: "tool",
@@ -125,6 +140,7 @@ export function timelineItems(sessionID: string, messages: readonly unknown[]):
125
140
  state: status === "error" ? "failed" : status === "completed" ? "done" : "running",
126
141
  ...(typeof part.id === "string" && part.id ? { callID: part.id } : {}),
127
142
  ...spawn,
143
+ ...(media.length ? { media } : {}),
128
144
  ...(typeof part.time?.created === "number" ? { at: part.time.created } : {}),
129
145
  ...(typeof part.time?.completed === "number" ? { completed: part.time.completed } : {}),
130
146
  })
@@ -214,7 +230,7 @@ const ESCAPED = /^(\s*)\\(\\*(?:\[hub\]|-{3,}|↩))/i
214
230
  * The body of a hub envelope (packages/sdk/src/envelope.ts renderEnvelope): the lines between its boundaries,
215
231
  * unescaped. Text without boundaries (a reminder) shows its first line without the "[hub]" tag.
216
232
  */
217
- function messageBody(text: string, id: string): string {
233
+ export function messageBody(text: string, id: string): string {
218
234
  const lines = text.split("\n")
219
235
  const boundary = `----- ${id} -----`
220
236
  const start = lines.indexOf(boundary)
@@ -314,16 +330,54 @@ export class TimelineCache {
314
330
  }
315
331
  }
316
332
 
333
+ const NAME_MAX = 200
334
+ const nameOf = (value: unknown) => (typeof value === "string" && value ? { name: value.slice(0, NAME_MAX) } : {})
335
+
336
+ /** The size and header dimensions of embedded base64 bytes (a `data:` URI or bare base64), when there are any. */
337
+ function embedded(data: unknown, mime: string): { bytes?: number; width?: number; height?: number } {
338
+ if (typeof data !== "string" || !data) return {}
339
+ const base64 = parseDataURI(data)?.base64 ?? data
340
+ return { bytes: base64Bytes(base64), ...base64Dimensions(base64, mime) }
341
+ }
342
+
317
343
  /** Attachment metadata (never the bytes) of a user message's files. */
318
344
  function userFiles(raw: unknown): TimelineFile[] {
319
345
  if (!Array.isArray(raw)) return []
320
346
  return raw.flatMap((file, index) => {
321
347
  const f = record(file)
322
348
  if (typeof f.mime !== "string") return []
323
- return [{ index, mime: f.mime, ...(typeof f.name === "string" && f.name ? { name: f.name.slice(0, 200) } : {}) }]
349
+ return [{ index, ref: `f${index}`, mime: f.mime, ...nameOf(f.name), ...embedded(f.data, f.mime) }]
324
350
  })
325
351
  }
326
352
 
353
+ /** A tool's image file content items (OpenCode ToolFileContent: `uri` a data: or media: URI), at most perRow. */
354
+ function toolMedia(rawState: unknown, part: number): TimelineFile[] {
355
+ const content = record(rawState).content
356
+ if (!Array.isArray(content)) return []
357
+ const out: TimelineFile[] = []
358
+ content.forEach((raw: unknown, index: number) => {
359
+ const item = record(raw)
360
+ const mime = mediaMime(typeof item.mime === "string" ? item.mime : undefined)
361
+ if (item.type !== "file" || !mime || out.length >= MEDIA_LIMITS.perRow) return
362
+ const bytes = typeof item.uri === "string" && item.uri.startsWith("data:") ? item.uri : item.data
363
+ out.push({ index, ref: `t${part}.${index}`, mime, ...nameOf(item.name), ...embedded(bytes, mime) })
364
+ })
365
+ return out
366
+ }
367
+
368
+ /** Local image paths of an assistant text (markdown images with an image extension; never remote or `~`), at most perRow. */
369
+ function textMedia(text: string): TimelineFile[] {
370
+ if (!text.includes("![")) return []
371
+ const out: TimelineFile[] = []
372
+ for (const src of markdownImageSources(text)) {
373
+ const path = sourcePath(src, "/")
374
+ const mime = path ? mimeOfPath(path) : undefined
375
+ if (!mime || out.length >= MEDIA_LIMITS.perRow) continue
376
+ out.push({ index: out.length, ref: mediaPathRef(src), mime, ...nameOf(src) })
377
+ }
378
+ return out
379
+ }
380
+
327
381
  /** Largest attachment served (base64 characters), so the answer fits in one frame. */
328
382
  export const TIMELINE_FILE_MAX_BASE64 = 900_000
329
383
 
@@ -0,0 +1,335 @@
1
+ // The voice front (P1, session mode; docs/plans/2026-11-06-voice-front-p1-contract.md, "Spoke"): a hidden session on
2
+ // the fast voice model that talks with the user and relays to the main session, whose agent and model stay untouched.
3
+ // This module holds the pure parts: the hidden agent (an allow-list of the two relay tools, like the Concierge's),
4
+ // its echo-first prompt, the relay's delivery choice, and VoiceFronts, which maps main ↔ front, keeps what the front
5
+ // knows about main (status, latest reply, pending asks, queued relays) for its per-turn context, and wakes the front
6
+ // when main's turn ends (merged over a short window; held while the front runs). Needs-you items (questions,
7
+ // permissions) never wake it: the hub announces them itself, so they are only listed in the front's context.
8
+ import { summarizeToolInput } from "../protocol"
9
+ import { redactCredentials } from "../../packages/protocol/src/credentials.ts"
10
+ import { SPEECH_RULES } from "./conversation"
11
+ import type { AgentEditorLike } from "./conversation"
12
+
13
+ export { SPEECH_RULES }
14
+
15
+ export const VOICE_FRONT_AGENT = "voice-front"
16
+ export const FRONT_SEND_TOOL = "voice_main_send"
17
+ export const FRONT_STOP_TOOL = "voice_main_stop"
18
+ /** The only tools the voice-front agent has. */
19
+ export const FRONT_TOOLS = [FRONT_SEND_TOOL, FRONT_STOP_TOOL] as const
20
+ /** Main's latest assistant text in the front's context and wakes, at most. */
21
+ export const MAIN_TEXT_MAX = 1500
22
+ /** Updates within this window become one wake. */
23
+ export const FRONT_MERGE_MS = 1500
24
+
25
+ export const VOICE_FRONT_NAME = "Voice front"
26
+ export const VOICE_FRONT_DESCRIPTION = "Hub voice front: talks with the user and relays to the session behind it. Hub-managed; not for direct use."
27
+
28
+ export const FRONT_SEND_DESCRIPTION =
29
+ 'Relay the user\'s request to the main session (the real agent doing the work), as the user\'s own words made clear. delivery: "auto" (default: starts a turn when main is idle, steers its running turn when busy), "steer" (join main\'s running turn: a correction or related follow-up), or "queue" (an unrelated request while main is busy: it runs after the current turn).'
30
+ export const FRONT_STOP_DESCRIPTION = "Stop the main session's current work (its turn and every worker below it), e.g. when the user says stop, cancel or never mind."
31
+
32
+ export const VOICE_FRONT_PROMPT = `You are the voice front of an OpenCode session. The user talks to you hands-free; everything they send is a live speech-to-text transcript and everything you write is read aloud. Behind you is the main session: the user's real agent, with shell, edits and subagents. You never do the work yourself. You understand the user, say what you understood, and relay it to main with ${FRONT_SEND_TOOL}, or stop main with ${FRONT_STOP_TOOL}. Each turn you get a live note about main (its status, latest reply, open questions, workers and queued requests).
33
+
34
+ ${SPEECH_RULES}
35
+
36
+ Echo first:
37
+ - Every reply starts with one plain sentence that says what you understood and what you are doing, for example "Got it, I'm asking the session to fix the failing login test." Only then call a tool. Never call a tool before that sentence.
38
+ - Never ask for confirmation before relaying: the echo is the confirmation. The user corrects you if you got it wrong.
39
+ - Keep replies to one or two short sentences of plain speech: no markdown, lists, code, file paths or URLs.
40
+
41
+ Relaying:
42
+ - Pass main the user's request as a clear instruction in their words, with what you inferred from the transcript filled in; don't add work they didn't ask for.
43
+ - While main is busy: a correction or a related follow-up goes in as steering (delivery "auto" or "steer"). An unrelated new request goes in with delivery "queue", and your echo says it is queued for after the current work.
44
+ - "no, wait", "actually", "scratch that": if it changes the request, relay the correction with delivery "steer"; if the user wants it dropped, call ${FRONT_STOP_TOOL}. Then a new echo confirms the change.
45
+ - Stop, cancel, never mind: call ${FRONT_STOP_TOOL} and confirm in a few words.
46
+ - Answer from the note about main when the user asks how it is going or what main said; don't relay a status question.
47
+ - A turn that is only a backchannel ("yeah", "mm-hm", "okay") needs no relay: reply with at most a few words or nothing.
48
+
49
+ Updates:
50
+ - Messages wrapped in <system-update> come from the hub, not the user: main's turn ended. Don't relay them to main.
51
+ - For a finished turn, say in one or two sentences what main did or found, from its final reply.
52
+ - Remember: the hub already announces questions, permissions and failures; don't repeat them. Help only if the user asks about them. When main's turn failed, don't say again that it failed: add at most one sentence explaining why, if that helps.`
53
+
54
+ export const VOICE_FRONT_RULES: ReadonlyArray<{ readonly action: string; readonly resource: string; readonly effect: "allow" | "deny" }> = [
55
+ { action: "*", resource: "*", effect: "deny" },
56
+ ...FRONT_TOOLS.map((action) => ({ action, resource: "*", effect: "allow" as const })),
57
+ ]
58
+
59
+ /** Adds (or refreshes) the hub's hidden "voice-front" agent; a "voice-front" agent the user defined is left alone. */
60
+ export function defineVoiceFrontAgent(editor: AgentEditorLike): void {
61
+ const existing = editor.get(VOICE_FRONT_AGENT)
62
+ if (existing && existing.description !== VOICE_FRONT_DESCRIPTION) return
63
+ editor.update(VOICE_FRONT_AGENT, (agent) => {
64
+ agent.name = VOICE_FRONT_NAME
65
+ agent.description = VOICE_FRONT_DESCRIPTION
66
+ agent.mode = "primary"
67
+ agent.hidden = true
68
+ agent.system = VOICE_FRONT_PROMPT
69
+ agent.permissions = VOICE_FRONT_RULES.map((r) => ({ ...r }))
70
+ })
71
+ }
72
+
73
+ /** Every front's session title starts with this. */
74
+ export const FRONT_TITLE_PREFIX = "Voice: "
75
+
76
+ /** The front's session title. */
77
+ export function frontTitle(mainTitle: string | undefined): string {
78
+ const title = mainTitle?.trim()
79
+ return `${FRONT_TITLE_PREFIX}${title || "session"}`
80
+ }
81
+
82
+ /**
83
+ * How a relay goes into main: "queue" (after the running turn) only when asked for while main is busy; otherwise
84
+ * steered (idle: it starts a turn; busy: it joins the running turn).
85
+ */
86
+ export function relayDelivery(requested: unknown, busy: boolean): "steer" | "queue" {
87
+ return busy && requested === "queue" ? "queue" : "steer"
88
+ }
89
+
90
+ const record = (v: unknown): Record<string, any> => (typeof v === "object" && v !== null ? (v as Record<string, any>) : {})
91
+ const str = (v: unknown) => (typeof v === "string" && v ? v : undefined)
92
+ const flat = (text: string) => text.replace(/\s+/g, " ").trim()
93
+ export const trimText = (text: string, max = MAIN_TEXT_MAX) => {
94
+ const t = text.trim()
95
+ return t.length > max ? `${t.slice(0, max - 1)}…` : t
96
+ }
97
+ const oneLine = (text: string, max = 200) => trimText(flat(text), max)
98
+
99
+ type Update = { readonly text: string }
100
+
101
+ interface Main {
102
+ readonly front: string
103
+ /** The latest tool main ran in its current turn. */
104
+ activity?: string
105
+ tools: Map<string, string>
106
+ /** Main's latest assistant text. */
107
+ lastText?: string
108
+ /** Pending asks in main's tree: `per <id>` / `frm <id>` → description. */
109
+ asks: Map<string, string>
110
+ /** Relays admitted into main's inbox and not yet delivered: inboxID → text. */
111
+ queued: Map<string, string>
112
+ /** Updates waiting for the next wake. */
113
+ pending: Update[]
114
+ /** Done updates held while the front ran: folded into its next context, or woken after its turn. */
115
+ held: Update[]
116
+ timer?: ReturnType<typeof setTimeout>
117
+ /** The front stopped main: the interruption is not reported back to it. */
118
+ stopping: boolean
119
+ }
120
+
121
+ export interface VoiceFrontsOptions {
122
+ readonly mergeMs?: number
123
+ /** Sends a wake to the front (a hub-sourced, steered prompt). */
124
+ readonly send: (front: string, text: string) => void
125
+ /** The front runs a turn right now. */
126
+ readonly frontBusy: (front: string) => boolean
127
+ }
128
+
129
+ const RUN_END = new Set(["session.execution.succeeded", "session.execution.failed", "session.execution.interrupted"])
130
+
131
+ export class VoiceFronts {
132
+ private readonly mains = new Map<string, Main>()
133
+ private readonly fronts = new Map<string, string>()
134
+ private readonly mergeMs: number
135
+
136
+ constructor(private readonly options: VoiceFrontsOptions) {
137
+ this.mergeMs = options.mergeMs ?? FRONT_MERGE_MS
138
+ }
139
+
140
+ link(main: string, front: string, seed: { lastText?: string } = {}): void {
141
+ this.unlink(main)
142
+ this.mains.set(main, {
143
+ front,
144
+ tools: new Map(),
145
+ ...(seed.lastText?.trim() ? { lastText: trimText(seed.lastText) } : {}),
146
+ asks: new Map(),
147
+ queued: new Map(),
148
+ pending: [],
149
+ held: [],
150
+ stopping: false,
151
+ })
152
+ this.fronts.set(front, main)
153
+ }
154
+
155
+ /** Forgets a main's front (its pending wakes are dropped); returns the front. */
156
+ unlink(main: string): string | undefined {
157
+ const entry = this.mains.get(main)
158
+ if (!entry) return undefined
159
+ clearTimeout(entry.timer)
160
+ this.mains.delete(main)
161
+ this.fronts.delete(entry.front)
162
+ return entry.front
163
+ }
164
+
165
+ frontOf(main: string): string | undefined {
166
+ return this.mains.get(main)?.front
167
+ }
168
+
169
+ mainOf(front: string): string | undefined {
170
+ return this.fronts.get(front)
171
+ }
172
+
173
+ isFront(id: string): boolean {
174
+ return this.fronts.has(id)
175
+ }
176
+
177
+ mainIDs(): string[] {
178
+ return [...this.mains.keys()]
179
+ }
180
+
181
+ /** A relay admitted into main's inbox (shown as queued until OpenCode delivers it). */
182
+ queued(main: string, inboxID: string, text: string): void {
183
+ this.mains.get(main)?.queued.set(inboxID, oneLine(text))
184
+ }
185
+
186
+ /** The front is stopping main: the coming interruption is its own doing, not news. */
187
+ stopRequested(main: string): void {
188
+ const entry = this.mains.get(main)
189
+ if (entry) entry.stopping = true
190
+ }
191
+
192
+ observe(event: { type: string; data?: unknown }, rootOf: (id: string) => string): void {
193
+ const data = record(event.data)
194
+ const sessionID = str(data.sessionID) ?? str(record(data.form).sessionID)
195
+ if (!sessionID) return
196
+ const frontMain = this.fronts.get(sessionID)
197
+ if (frontMain) {
198
+ // The front's turn ended: what was held for it goes out now (merged).
199
+ const entry = this.mains.get(frontMain)!
200
+ if (RUN_END.has(event.type) && entry.held.length) {
201
+ entry.pending.unshift(...entry.held.splice(0))
202
+ this.schedule(frontMain, entry, Date.now() + this.mergeMs)
203
+ }
204
+ return
205
+ }
206
+ const main = this.mains.has(sessionID) ? sessionID : this.mains.has(rootOf(sessionID)) ? rootOf(sessionID) : undefined
207
+ if (!main) return
208
+ const entry = this.mains.get(main)!
209
+ switch (event.type) {
210
+ case "permission.asked": {
211
+ const id = str(data.id)
212
+ if (!id) return
213
+ const resources = Array.isArray(data.resources) ? data.resources.filter((r: unknown) => typeof r === "string").join(", ") : ""
214
+ const what = oneLine([str(data.action), resources].filter(Boolean).join(": ") || "a tool")
215
+ entry.asks.set(`per ${id}`, `${sessionID === main ? "" : "a worker's "}permission: ${what}`)
216
+ return
217
+ }
218
+ case "form.created": {
219
+ const form = record(data.form)
220
+ const id = str(form.id)
221
+ if (!id) return
222
+ const title = oneLine(str(form.title) ?? "a question")
223
+ entry.asks.set(`frm ${id}`, `${sessionID === main ? "" : "a worker's "}question: ${title}`)
224
+ return
225
+ }
226
+ case "permission.replied":
227
+ entry.asks.delete(`per ${str(data.requestID)}`)
228
+ return
229
+ case "form.replied":
230
+ case "form.cancelled":
231
+ entry.asks.delete(`frm ${str(data.id) ?? str(record(data.form).id)}`)
232
+ return
233
+ }
234
+ if (sessionID !== main) return
235
+ switch (event.type) {
236
+ case "session.execution.started":
237
+ entry.activity = undefined
238
+ entry.tools.clear()
239
+ return
240
+ case "session.tool.input.started": {
241
+ const id = str(data.id)
242
+ const name = str(data.name)
243
+ if (!id || !name) return
244
+ entry.tools.set(id, name)
245
+ if (entry.tools.size > 20) entry.tools.delete(entry.tools.keys().next().value!)
246
+ entry.activity = name
247
+ return
248
+ }
249
+ case "session.tool.called": {
250
+ const name = entry.tools.get(str(data.id) ?? "") ?? str(data.name) ?? "tool"
251
+ const summary = str(data.summary) ?? summarizeToolInput(data.input)
252
+ entry.activity = oneLine(summary ? `${name} · ${summary}` : name)
253
+ return
254
+ }
255
+ case "session.text.ended":
256
+ if (str(data.text)?.trim()) entry.lastText = trimText(data.text)
257
+ return
258
+ case "session.inbox.delivered":
259
+ entry.queued.delete(str(data.inboxID) ?? "")
260
+ return
261
+ case "session.execution.succeeded":
262
+ case "session.execution.failed":
263
+ case "session.execution.interrupted": {
264
+ entry.activity = undefined
265
+ const reply = entry.lastText ? ` Its final reply: ${entry.lastText}` : ""
266
+ if (event.type === "session.execution.interrupted") {
267
+ if (entry.stopping) {
268
+ entry.stopping = false
269
+ return
270
+ }
271
+ return this.add(main, entry, { text: `Main's turn was interrupted.${reply}` })
272
+ }
273
+ entry.stopping = false
274
+ if (event.type === "session.execution.failed") {
275
+ // Scrubbed: the front may say why main failed, and never reads a credential aloud.
276
+ const message = oneLine(redactCredentials(str(record(data.error).message) ?? "unknown error").text)
277
+ return this.add(main, entry, { text: `Main's turn failed (already announced to the user; don't say it failed again): ${message}.${reply}` })
278
+ }
279
+ return this.add(main, entry, { text: `Main finished its turn.${reply}` })
280
+ }
281
+ }
282
+ }
283
+
284
+ /**
285
+ * The front's per-turn note about main; undefined for a session that is no front. Updates held while the front
286
+ * ran are folded in (and not sent again).
287
+ */
288
+ context(front: string, live: { title?: string; busy: boolean; workers?: string }): string | undefined {
289
+ const main = this.fronts.get(front)
290
+ if (!main) return undefined
291
+ const entry = this.mains.get(main)!
292
+ const held = entry.held.splice(0)
293
+ const lines = [
294
+ `Main session${live.title ? ` "${live.title}"` : ""} (the session you relay to), live now:`,
295
+ `Status: ${live.busy ? `busy${entry.activity ? `, last: ${entry.activity}` : ", working"}` : "idle"}`,
296
+ `Main's latest reply: ${entry.lastText ?? "(none yet)"}`,
297
+ ]
298
+ if (entry.asks.size) lines.push("Waiting for the user (already announced by the hub; mention only if asked):", ...[...entry.asks.values()].map((a) => `- ${a}`))
299
+ if (live.workers) lines.push(`Main's workers:\n${live.workers}`)
300
+ if (entry.queued.size) lines.push("Your relays still waiting in main's queue:", ...[...entry.queued.values()].map((q) => `- ${q}`))
301
+ if (held.length) lines.push("Updates since your last reply (mention them briefly if relevant):", ...held.map((u) => `- ${u.text}`))
302
+ return lines.join("\n")
303
+ }
304
+
305
+ stop(): void {
306
+ for (const entry of this.mains.values()) clearTimeout(entry.timer)
307
+ }
308
+
309
+ /** Queues a turn-end update; updates within the merge window go out as one wake. */
310
+ private add(main: string, entry: Main, update: Update): void {
311
+ entry.pending.push(update)
312
+ this.schedule(main, entry, Date.now() + this.mergeMs)
313
+ }
314
+
315
+ private schedule(main: string, entry: Main, at: number): void {
316
+ if (entry.timer) return
317
+ entry.timer = setTimeout(() => {
318
+ entry.timer = undefined
319
+ if (this.mains.get(main) === entry) this.flush(entry)
320
+ }, Math.max(0, at - Date.now()))
321
+ }
322
+
323
+ private flush(entry: Main): void {
324
+ clearTimeout(entry.timer)
325
+ entry.timer = undefined
326
+ if (!entry.pending.length) return
327
+ // While the front runs: held (folded into its next turn), never a separate interruption.
328
+ if (this.options.frontBusy(entry.front)) {
329
+ entry.held.push(...entry.pending.splice(0))
330
+ return
331
+ }
332
+ const updates = [...entry.held.splice(0), ...entry.pending.splice(0)]
333
+ this.options.send(entry.front, `<system-update>\n${updates.map((u) => u.text).join("\n")}\n</system-update>`)
334
+ }
335
+ }
@@ -0,0 +1,2 @@
1
+ import{i as e,n as t,t as n}from"./7XWI72E6-BMuIspEy.js";import{a as r,d as i,f as a}from"./BGW36YJW-CJMhya8S.js";import{n as o,t as s}from"./G6GPDDNC-DEz8F449.js";import{t as c}from"./DKTLEL5E-DciMwFJ1.js";import{C as l,E as u,F as d,f,h as p,s as m,t as h}from"./lazy-f3CuUNJZ.js";function g(e){let t=/* @__PURE__ */ new Set;for(let n of e)for(let e of n.people)t.add(e);return[...t].sort()}var _=class{type=`journey`;title;accTitle;accDescr;sections=[];tasks=[];currentSection=``;setDiagramTitle(e){this.title=e}setAccTitle(e){this.accTitle=e.replace(/^\s+/g,``)}setAccDescription(e){this.accDescr=e.replace(/\n\s+/g,`
2
+ `)}addSection(e){this.currentSection=e,this.sections.push(e)}addTask(e,t){let n=t.substring(1).split(`:`),r=n.length===1?[]:n[1].split(`,`).map(e=>e.trim());this.tasks.push({task:e,score:Number(n[0]),people:r,section:this.currentSection})}getActors(){return g(this.tasks)}},v=[`$end`,`EOF`,`INVALID`,`NEWLINE`,`journey`,`title`,`acc_title`,`acc_title_value`,`acc_descr`,`acc_descr_value`,`acc_descr_multiline_value`,`section`,`taskName`,`taskData`,`:`];function y(e,t,n){let r=e.length,i=t;for(;i<r;i++){let t=e.charCodeAt(i);if(t===35||t===10||t===59||n&&t===58)break}return i}function b(r){let i=r.length,a=[],o=[],s=[],c={types:a,texts:o,starts:s},l=0,u=(e,t)=>{a.push(e),o.push(r.slice(l,t)),s.push(l),l=t};for(;l=t(r,l),!(l>=i);){let t=r.charCodeAt(l),a=l+1;if(t===10){for(;r.charCodeAt(a)===10;)a++;u(3,a)}else if(n(r,l,`journey`)&&!h(r.charCodeAt(l+7)))u(4,l+7);else{if(n(r,l,`title`)){for(a=l+5;r.charCodeAt(a)!==10&&d(r.charCodeAt(a));)a++;if(a===i||r.charCodeAt(a)===10){l=a;continue}let e=r.charCodeAt(l+5);if(e!==13&&d(e)&&(a=y(r,l+6,!1),a>l+6)){u(5,a);continue}}a=e(r,l,c,6),a===l?n(r,l,`section`)&&d(r.charCodeAt(l+7))&&(a=y(r,l+8,!0),a>l+8)?u(11,a):t===58?(a=y(r,l+1,!1),u(a>l+1?13:14,a>l+1?a:l+1)):t===59?u(2,l+1):u(12,y(r,l+1,!0)):l=a}}return(s.length===0||s[s.length-1]<i)&&u(1,i),u(0,i),c}var x=`'EOF', 'SPACE', 'NEWLINE', 'title', 'acc_title', 'acc_descr', 'acc_descr_multiline_value', 'section', 'taskName'`;function S(e,t){let{types:n,texts:r,starts:i}=b(e),a=(t,r)=>{let a=n[t]===0?`1`:v[n[t]];throw l(`journey`,e,i[t],`Expecting ${r}, got '${a}'`)};n[0]!==4&&a(0,`'journey'`);for(let e=1;;e++){let i=r[e];switch(n[e]){case 3:break;case 5:t.setDiagramTitle(i.substring(6));break;case 6:t.setAccTitle(r[++e].trim());break;case 8:t.setAccDescription(r[++e].trim());break;case 10:t.setAccDescription(i.trim());break;case 11:t.addSection(i.substring(8));break;case 12:n[++e]!==13&&a(e,`'taskData'`),t.addTask(i,r[e]);break;case 1:return;default:a(e,x)}}}var C=10,w=8,T=124,E=200,D=12,ee=6,O=24,k=26,A=11,j=5,te=13,ne=20,M=`<circle cx="-3.6" cy="-2.8" r="1.3"/><circle cx="3.6" cy="-2.8" r="1.3"/>`,N={happy:`M-5,2A6,6 0 0 0 5,2`,neutral:`M-4.5,4.5H4.5`,sad:`M-5,6.5A6,6 0 0 1 5,6.5`};function re(e,t,n){return`<g class="pele-face pele-face-${n}" transform="translate(${u(e)},${u(t)})"><circle r="${A}" fill="var(--_bg)" stroke="var(--_l)"/><g fill="var(--_l)">${M}</g><path d="${N[n]}" fill="none" stroke="var(--_l)" stroke-linecap="round"/></g>`}function P(e,t,n){return c(n,r=>F(e,t,n,r))}function F(e,t,n,c){let l=n.fontSize??16,d=Math.round(l*.875),h=Math.round(l*.75),_=n.measurer??m(n.fontFamily,n.fontFamilyMono),v=n.padding??8;if(c)return ie(e,n);let y=e.tasks,b=y.length,x=g(y).filter(e=>e!==``),S=/* @__PURE__ */ new Map;x.forEach((e,t)=>S.set(e,t));let M=T,N=y.map(e=>o(e.task,_,d,M)),P=0;for(let e of N)P=Math.max(P,e.width);P>M&&(M=Math.min(Math.ceil(P),E),N=y.map(e=>s(e.task,_,d,M)));let F=M+2*C,I=F+D,L=b>0?b*I-D:0,R=Math.round(d*1.5),ae=Math.max(L,240),z=[],B=0,V=0,H=0;for(let e of x){let t=s(e,_,d,ae-2*j-6),n=2*j+6+t.width;H>0&&H+n>ae&&(H=0,V+=4);let r=H>0?z[z.length-1].y:V,i=Math.max(t.height,R);z.push({name:e,label:t,x:H,y:r,h:i}),H+=n,B=Math.max(B,H),V=Math.max(V,r+i),H+=ne}let U=b>0?O+L:0,W=s(e.title,_,l,Math.max(U,320),1),oe=W.height>0?W.height+16:0,G=Math.max(U,W.width,b>0?O+B:0),K=v+(G-U)/2+O,q=v+oe,se=[],J=0;for(let e=0;e<b;){let t=e+1;for(;t<b&&y[t].section===y[e].section;)t++;let n;y[e].section!==``&&(n=s(y[e].section,_,d,(t-e)*I-D-2*C,1),J=Math.max(J,Math.max(n.height,R)+2*ee)),se.push({start:e,count:t-e,label:n}),e=t}let Y=R;for(let e of N)Y=Math.max(Y,e.height);let ce=x.length>0?2*j+6:0,X=Y+ce+2*w,Z=q+(J>0?J+ee:0),le=Z+X+A+16,ue=le+4*k,de=K+L,Q=``,$=q;if(b>0){let e=``,t=``;for(let n=5;n>=1;n--){let r=le+(5-n)*k;e+=`M${u(K-6)},${u(r)}H${u(de)}`,t+=`<text x="${u(K-12)}" y="${u(r+h*.35)}">${n}</text>`}Q+=`<path class="pele-grid" d="${e}" fill="none" stroke="var(--_a)"/><g class="pele-scale" font-size="${h}" text-anchor="end" fill="var(--_m)">${t}</g>`;let n=``,i=``,o=``,s=[];y.forEach((e,t)=>{let c=K+t*I,l=c+F/2,m=e.score,h=le+(5-(Number.isNaN(m)?3:Math.min(5,Math.max(1,m))))*k;n+=`${t===0?`M`:`L`}${u(l)},${u(h)}`,i+=`M${u(l)},${u(Z+X)}V${u(h-A)}`,o+=re(l,h,m>3?`happy`:m<3?`sad`:`neutral`);let g=``,_=e.people.filter(e=>e!==``),v=Math.min(te,(F-2*C-2*j)/Math.max(_.length-1,1));_.forEach((e,t)=>{g+=`<circle class="pele-actor" cx="${u(l+(t-(_.length-1)/2)*v)}" cy="${u(Z+X-w-j)}" r="${j}" fill="${r(S.get(e)??0)}" stroke="var(--_s)"><title>${f(e)}</title></circle>`}),s.push(`<g class="pele-node pele-task" data-id="${f(e.task.trim())}"><rect x="${u(c)}" y="${u(Z)}" width="${u(F)}" height="${u(X)}" fill="var(--_s)" stroke="var(--_b)"${a}/>`+p(N[t],l,Z+w+Y/2,` font-size="${d}"`)+g+`</g>`)}),Q+=`<path class="pele-stems" d="${i}" fill="none" stroke="var(--_b)" stroke-dasharray="2 3"/>`+(b>1?`<path class="pele-edge" d="${n}" fill="none" stroke="var(--_l)" stroke-linejoin="round"/>`:``)+o;for(let e of se){let t=s.slice(e.start,e.start+e.count).join(``);if(!e.label){Q+=t;continue}let n=K+e.start*I,r=e.count*I-D;Q+=`<g class="pele-cluster pele-section" data-id="${f(y[e.start].section.trim())}"><rect x="${u(n)}" y="${u(q)}" width="${u(r)}" height="${u(J)}" fill="var(--_a)"${a}/>`+p(e.label,n+r/2,q+J/2,` font-size="${d}" font-weight="var(--_hw)"`)+t+`</g>`}if($=ue+A,z.length>0){let e=K+(L-B)/2,t=$+16,n=``;for(let i of z){let a=t+i.y+i.h/2;n+=`<g class="pele-legend-item" data-id="${f(i.name)}"><circle cx="${u(e+i.x+j)}" cy="${u(a)}" r="${j}" fill="${r(S.get(i.name)??0)}"/>`+p(i.label,e+i.x+2*j+6+i.label.width/2,a,``)+`</g>`}Q+=`<g class="pele-legend" font-size="${d}">${n}</g>`,$=t+V}}let fe=Math.ceil(G+2*v),pe=Math.ceil(Math.max($,v)+v),me=p(W,v+G/2,v+W.height/2,` class="pele-title" font-weight="var(--_tw)"`)+Q;return{svg:i(`journey`,fe,pe,l,n,e,me),width:fe,height:pe,links:[]}}function ie(e,t){let n=t.fontSize??16,o=Math.round(n*.875),c=t.measurer??m(t.fontFamily,t.fontFamilyMono),l=t.padding??8,d=Math.max(140,(t.maxWidth??320)-2*l),f=s(e.title,c,n,d,1),h=g(e.tasks).filter(e=>e!==``),_=new Map(h.map((e,t)=>[e,t])),v=l+(f.height?f.height+16:0),y,b=``,x=f.width;e.tasks.forEach((t,i)=>{if(t.section!==y){y=t.section;let e=s(y,c,n,d-16,1);e.height&&(b+=p(e,l+8+e.width/2,v+e.height/2,` class="pele-cluster-label" font-weight="var(--_hw)"`),x=Math.max(x,e.width+16),v+=e.height+8)}let f=s(t.task,c,o,d-72),m=t.people.filter(e=>e!==``).map(e=>({name:e,label:s(e,c,o,d-44)})),h=Math.max(f.height,42),g=2*w+h;for(let e of m)g+=Math.max(16,e.label.height)+4;let S=Math.max(d,f.width+72);b+=`<g class="pele-node pele-journey-step" data-id="${i}"><rect x="${u(l)}" y="${u(v)}" width="${u(S)}" height="${u(g)}" fill="var(--_s)" stroke="var(--_b)"${a}/>`,b+=p(f,l+C+f.width/2,v+w+f.height/2,` class="pele-label"`);let T=l+S-28;b+=re(T,v+w+A,t.score>3?`happy`:t.score<3?`sad`:`neutral`),Number.isNaN(t.score)||(b+=`<text class="pele-journey-score" x="${u(T)}" y="${u(v+w+2*A+o)}" font-size="${o}" text-anchor="middle">${u(Math.min(5,Math.max(1,t.score)))}</text>`);let E=v+w+h+4;for(let e of m){let t=Math.max(16,e.label.height);b+=`<circle class="pele-actor" cx="${u(l+16)}" cy="${u(E+t/2)}" r="${j}" fill="${r(_.get(e.name))}"/>`+p(e.label,l+28+e.label.width/2,E+t/2,` class="pele-journey-actor" font-size="${o}"`),E+=t+4}b+=`</g>`,v+=g,i+1<e.tasks.length&&(b+=`<path class="pele-edge" d="M${u(l+S/2)},${u(v)}v16" fill="none" stroke="var(--_l)"/>`),v+=16,x=Math.max(x,S)});let S=Math.ceil(x+2*l),T=Math.ceil(Math.max(l+f.height,v-(e.tasks.length?16:0))+l),E=p(f,S/2,l+f.height/2,` class="pele-title" font-weight="var(--_tw)"`)+b;return{svg:i(`journey`,S,T,n,t,e,E),width:S,height:T,links:[]}}var I={type:`journey`,parse(e,t,n){let r=new _;return r.title=n,S(e,r),r},render:P};export{I as journey};
@@ -0,0 +1,2 @@
1
+ import{a as e,d as t,i as n,n as r,r as i,s as a,t as o}from"./BGW36YJW-CJMhya8S.js";import{i as s,r as c}from"./DKTLEL5E-DciMwFJ1.js";import{t as l}from"./5THPTBWS-Ce_hS451.js";import{_ as u,g as d,h as f,n as p,r as m,s as h,t as g,u as _}from"./EXE5EH6O-DR1IHCTr.js";import{D as v,E as y,d as b,f as x,h as S,i as C,r as w,s as T}from"./lazy-f3CuUNJZ.js";function E(e){let t=[],n=[];for(let r of e){let e=r.type===`Leaf`,i={name:r.name,children:e?void 0:[]};for(i.classSelector=r.classSelector,r.cssCompiledStyles&&(i.cssCompiledStyles=r.cssCompiledStyles),e&&r.value!==void 0&&(i.value=r.value);n.length>0&&n[n.length-1].level>=r.level;)n.pop();n.length===0?t.push(i):n[n.length-1].node.children.push(i),e||n.push({node:i,level:r.level})}return t}function D(e,t){let n=/* @__PURE__ */ new Map;for(let t of e.TreemapRows){if(t.$type!==`ClassDefStatement`)continue;let e=n.get(t.className);e||n.set(t.className,e=[]);let r=(t.styleText??``).replace(/\\,/g,`§§§`).replace(/,/g,`;`).replace(/§§§/g,`,`);for(let t of r.split(`;`))e.push(t)}let r=[];for(let t of e.TreemapRows){if(t.$type!==`TreemapRow`)continue;let e=t.item,i=e.classSelector?n.get(e.classSelector):void 0;r.push({level:t.indent??0,name:e.name,type:e.$type,value:e.$type===`Leaf`?e.value:void 0,classSelector:e.classSelector,cssCompiledStyles:i&&i.length>0?i:void 0})}return{type:`treemap`,title:e.title||t,accTitle:e.accTitle?e.accTitle.replace(/^\s+/g,``):void 0,accDescr:e.accDescr?e.accDescr.replace(/\n\s+/g,`
2
+ `):void 0,roots:E(r),classes:n}}var O=[h,g,f(String.raw`\w`),{name:`TREEMAP_KEYWORD`,pattern:/treemap-beta|treemap/y},{name:`CLASS_DEF`,pattern:/classDef\s+([a-zA-Z_][a-zA-Z0-9_]+)(?:\s+([^;\r\n]*))?(?:;)?/y},{name:`STYLE_SEPARATOR`,pattern:/:::/y},{name:`SEPARATOR`,pattern:/:/y},{name:`COMMA`,pattern:/,/y},{name:`INDENTATION`,pattern:/[ \t]{1,}/y},{name:`WS`,pattern:/[ \t]+/y,hidden:!0},{name:`ML_COMMENT`,pattern:/\%\%[^\n]*/y,hidden:!0},{name:`NL`,pattern:/\r?\n/y,hidden:!0},{name:`ID2`,pattern:/[a-zA-Z_][a-zA-Z0-9_]*/y},{name:`NUMBER2`,pattern:/[0-9_\.\,]+/y},{name:`STRING2`,pattern:/"[^"]*"|'[^']*'/y}],k=/classDef\s+([A-Z_a-z]\w+)(?:\s+([^\n\r;]*))?;?/;function A(e){let t=new d(m(e,O,`treemap`),O,`treemap`),n=t.kinds,r={$type:`Treemap`,TreemapRows:[]},i=()=>t.accept(5)?t.expect(12):void 0;for(t.expect(3);t.kind!==-1;){let e=t.kind;if(e===0){r.accDescr=_(t.take());continue}if(e===1){r.accTitle=p(t.take());continue}if(e===2){r.title=u(t.take());continue}e!==8&&e!==14&&e!==4&&t.fail(`a node, a class definition, or a title`);let a=e===8?t.take().length:void 0;if(t.kind===4){let e=k.exec(t.take()),n={$type:`ClassDefStatement`,className:e[1]};e[2]&&(n.styleText=e[2]),a!==void 0&&(n.indent=a),r.TreemapRows.push(n);continue}let o=t.expect(14).slice(1,-1),s=n[t.i]===8?t.i+1:t.i,c;n[s]===6||n[s]===7?(t.i=s+1,t.accept(8),c={$type:`Leaf`,name:o,value:parseFloat(t.expect(13).replace(/,/g,``))}):c={$type:`Section`,name:o};let l=i();l!==void 0&&(c.classSelector=l);let d={$type:`TreemapRow`,item:c};a!==void 0&&(d.indent=a),r.TreemapRows.push(d)}return r}var j=/^(?:.?[<>=^])?([-+( ])?(\$)?0?\d*(,)?(?:\.(\d+))?(~)?([a-zA-Z%])?$/;function M(e){let t=``;for(let n=e.length;n>0;n-=3)t=e.slice(Math.max(0,n-3),n)+(t?`,`+t:``);return t}function N(e){let t=j.exec(e===`$0,0`?`$,`:e)??j.exec(`,`),n=t[1]===`+`,r=t[3]!==void 0,i=t[4]===void 0?void 0:Math.min(Number(t[4]),20),a=t[6]??``;return e=>{let o=Math.abs(e),s,c=``;if(a===`f`?s=o.toFixed(i??6):a===`%`?(s=(o*100).toFixed(i??6),c=`%`):s=a===`d`?Math.round(o).toFixed(0):a===`e`?o.toExponential(i??6):String(Number(o.toPrecision(Math.max(1,i??12)))),t[5]&&s.includes(`.`)&&!s.includes(`e`)&&(s=s.replace(/\.?0+$/,``)),r&&!s.includes(`e`)){let e=s.indexOf(`.`);s=e===-1?M(s):M(s.slice(0,e))+s.slice(e)}return(e<0?`-`:n?`+`:``)+(t[2]??``)+s+c}}function P(e,t,n){return{node:e,parent:t,series:n,value:e.children===void 0&&Number.isFinite(e.value)&&e.value>0?e.value:0,children:[],headed:!1,header:0,x:0,y:0,w:0,h:0}}var F=(e,t,n)=>e>=t?Math.min(e,n):t;function I(e,t,n,r,i,a,o){let s=t,c=0;for(;c<e.length;){let t=i*a/s,l=Math.min(i,a),u=e[c].value*t,d=u,f=Math.max(l*l*d/(u*u),u*u/(l*l*d)),p=c+1;for(;p<e.length;){let n=e[p].value*t,r=u+n,i=Math.max(l*l*d/(r*r),r*r/(l*l*n));if(i>f)break;f=i,u=r,p++}let m=u/l,h=0;for(let u=c;u<p;u++){let c=e[u],d=u===p-1?l-h:c.value*t/m,f=i>=a?n:n+h,g=i>=a?r+h:r,_=i>=a?m:d,v=i>=a?d:m,y=Math.round(f+_-o/2)>Math.round(f+o/2)?o/2:0,b=Math.round(g+v-o/2)>Math.round(g+o/2)?o/2:0;c.x=Math.round(f+y),c.y=Math.round(g+b),c.w=Math.round(f+_-y)-c.x,c.h=Math.round(g+v-b)-c.y,h+=d,s-=c.value}i>=a?(n+=m,i-=m):(r+=m,a-=m),c=p}}function L(e,t,n,r){let i=e.map((e,t)=>P(e,void 0,t)),a=i.slice();for(let e=0;e<a.length;e++){let t=a[e];for(let e of t.node.children??[]){let n=P(e,t,t.series);t.children.push(n),a.push(n)}}for(let e=a.length-1;e>=0;e--){let t=a[e];t.parent&&(t.parent.value+=t.value)}let{gap:o,header:s,short:c,side:l}=r,u=[],d=(e,t,n,r,i)=>{let a=e.filter(e=>e.value>0).sort((e,t)=>t.value-e.value),s=0;for(let e of a)s+=e.value;if(a.length!==0&&Number.isFinite(s)){r=Math.max(0,r),i=Math.max(0,i),r>0&&i>0&&I(a,s,t-o/2,n-o/2,r+o,i+o,o);for(let e of a){let a=F(e.x,t,t+r),o=F(e.y,n,n+i);e.w=F(e.x+e.w,a,t+r)-a,e.h=F(e.y+e.h,o,n+i)-o,e.x=a,e.y=o,u.push(e)}}};d(i,0,0,t,n);for(let e=0;e<u.length;e++){let t=u[e];if(t.children.length===0)continue;let n=t.h-s-l>=12?s:c;t.headed=t.w>Math.max(24,2*l)&&t.h-n-l>=8,t.header=t.headed?n:0,t.headed?d(t.children,t.x+l,t.y+n,t.w-2*l,t.h-n-l):d(t.children,t.x,t.y,t.w,t.h)}return u}var R=28,z=18,B=6,V=1.25,H=e=>b(v(e).trim()).trim();function U(e){let t=0,n=e.slice();for(;n.length>0;){let e=n.pop();e.children===void 0?Number.isFinite(e.value)&&e.value>0&&t++:n.push(...e.children)}return t}function W(e,t){let n=Math.min(t.w,t.h)/4;return n<4?a(e).replace(`rx:var(--_r)`,`rx:min(var(--_r),${y(n)}px)`):a(e)}function G(e,t,n,r,i){let a=e.treemap?.[t];return typeof a==`number`&&a>=r?Math.min(a,i):n}function K(a,u,d){let f=d.fontSize??16,p=Math.round(f*.875),m=Math.round(f*.75),h=Math.round(f*.5625),g=d.measurer??T(d.fontFamily,d.fontFamilyMono),_=u.treemap??{},E=d.padding??G(u,`diagramPadding`,8,0,2e3),D=Math.round(s(d,G(u,`nodeWidth`,64,1,400)*10,200,2*E)),O=Math.round(G(u,`nodeHeight`,40,1,400)*10),k=_.showValues!==!1,A=N(typeof _.valueFormat==`string`?_.valueFormat:`,`),j=Math.min(1,Math.max(0,Math.log10(D*O/Math.max(1,U(a.roots))/800))),M=L(a.roots,D,O,{gap:G(u,`padding`,Math.round(2+2*j),0,200),header:R,short:z,side:Math.round(3+3*j)}),P=n(a.title,!1,g,f,c(d),1),F=P.height>0?P.height+8:0,I=E+Math.max(0,(P.width-D)/2),K=E+F,q=``,J=``;for(let t of M){let n=t.node,a=i(n.cssCompiledStyles??[]),s=e(t.series),c=I+t.x,u=K+t.y,d=v(n.name).trim(),_=k?A(t.value):``,S=`data-id="${x(n.name)}">`,T=H(n.name),E=T===``?[]:[T];for(let e=t.parent;e!==void 0&&!e.headed;e=e.parent){let t=H(e.node.name);t!==``&&E.unshift(t)}let D=E.join(` › `),O=e=>D===``||e===d&&D===T?``:`<title>${D}</title>`,j=`<rect x="${y(c)}" y="${y(u)}" width="${y(t.w)}" height="${y(t.h)}" fill="${s}"`,M=a.fontSize,N=+!!a.bold|(a.italic?2:0),P=o(a.shape)?w(a):C(t.series);if(n.children!==void 0){let e=``,i;if(t.headed){let n=t.header<R,r=M??(n?m:p),o=M??m,s=t.w-2*B-4,f=_?g.width(_,o,N):0,h=!n&&f>0&&s-f-12>=3*r,v=r*V<t.header?l(d,h?s-f-12:s,r,r,g,N|1):void 0,x=u+t.header/2+1;i=v?.text,v&&(e+=`<text x="${y(c+B+2)}" y="${y(x+r*.35)}" font-weight="var(--_hw)"${n?` font-size="${r}"`:``}${P}${a.text}>${b(v.text)}</text>`),h&&o*V<=R&&(e+=`<text class="pele-value" x="${y(c+t.w-B-2)}" y="${y(x+o*.35)}" font-size="${m}" text-anchor="end"${P||` fill="var(--_m)"`}${a.text}>${b(_)}</text>`)}let o=/[";]fill:/.test(a.shape)?``:` fill-opacity="0.12"`,f=t.headed?`${j}${W(`${o} stroke="${s}"${a.shape}`,t)}/>`:`<rect x="${y(c)}" y="${y(u)}" width="${y(t.w)}" height="${y(t.h)}" fill="none"/>`;q+=`<g class="pele-node pele-section${r(n.classSelector??``)}" ${S}${O(i)}${f}${e}</g>`;continue}let F=``,L=t.w-8,z=M??h,U=M===void 0?Math.min(f,Math.floor((t.h-4)/V)):M*V<=t.h-4?M:0,G=U>=z?l(d,L,U,z,g,N):void 0;if(G){let e=y(c+t.w/2),n=u+t.h/2,r=M??Math.max(h,Math.round(G.size*.8)),i=(G.size+r)*V;if(_&&i<=t.h-4&&g.width(_,r,N)<=L){let t=n-i/2+G.size*V/2,o=n+i/2-r*V/2;F=`<text x="${e}" y="${y(t+G.size*.35)}" font-size="${y(G.size)}"${P}${a.text}>${b(G.text)}</text><text class="pele-value" x="${e}" y="${y(o+r*.35)}" font-size="${y(r)}"${P}${a.text}>${b(_)}</text>`}else F=`<text x="${e}" y="${y(n+G.size*.35)}" font-size="${y(G.size)}"${P}${a.text}>${b(G.text)}</text>`}J+=`<g class="pele-node pele-leaf${r(n.classSelector??``)}" ${S}${O(G?.text)}${j}${W(a.shape,t)}/>${F}</g>`}let Y=Math.ceil(Math.max(D,P.width)+2*E),X=Math.ceil(O+F+2*E),Z=S(P,Y/2,E+P.height/2,` class="pele-title" font-weight="var(--_tw)"`)+(q?`<g class="pele-sections" font-size="${p}">${q}</g>`:``)+(J?`<g class="pele-leaves" text-anchor="middle" fill="var(--_bg)">${J}</g>`:``);return{svg:t(`treemap`,Y,X,f,d,a,Z),width:Y,height:X,links:[]}}var q={type:`treemap`,parse:(e,t,n)=>D(A(e),n),render:K};export{q as treemap};
@@ -0,0 +1 @@
1
+ import{E as e,h as t}from"./lazy-f3CuUNJZ.js";function n(e,t){for(let n=0;n<t.length;n+=3){let r=Math.round(t[n+1]),i=e.get(r);i?i.push(t[n]):e.set(r,[t[n]])}}function r(e,t,n,r){if(t===0)return e.x;let i=e.y-e.h/2,a=e.x-e.w/2,o=t/2,s=[a+r-6+o,e.x,a+e.w-r+6-o],c=n.get(Math.round(i));if(c===void 0)return s[0];let l=s[0],u=-1;for(let t of s){let n=1/0;for(let r of c)r>a&&r<a+e.w&&(n=Math.min(n,Math.abs(r-t)-o));if(n>=6)return t;n>u+.5&&(u=n,l=t)}return l}function i(e,t,n,r){let i=t>0?r.get(Math.round(e.y-e.h/2)):void 0;if(i===void 0)return!1;let a=e.x-e.w/2;for(let r of i)if(r>a&&r<a+e.w&&Math.abs(r-n)<t/2+4)return!0;return!1}function a(n,r,i,a,o,s,c,l){let u=1;for(let e=s[c].parent;e>=0&&u<8;e=s[e].parent)u++;let d=`color-mix(in srgb,var(--_a) ${e(100*(1-.5**u))}%,var(--_bg))`;return`<g class="pele-cluster-title" data-id="${o}">`+t(n,r,i,` fill="none" stroke="${d}" stroke-width="5" stroke-linejoin="round"`,l)+t(n,r,i,a,l)+`</g>`}export{i,r as n,n as r,a as t};
@@ -0,0 +1 @@
1
+ function e(e,t,n,r,i,a=0){if(e===``||t<=0)return;let o=i.width(e,n,a);if(o<=t)return{text:e,size:n,width:o};let s=Math.max(r,Math.floor(n*t/o));for(;s>=r;){if(o=i.width(e,s,a),o<=t)return{text:e,size:s,width:o};s--}let c=Array.from(e),l=0,u=c.length-1;for(;l<u;){let e=l+u+1>>1;i.width(c.slice(0,e).join(``).trimEnd()+`…`,r,a)<=t?l=e:u=e-1}if(l===0)return;let d=c.slice(0,l).join(``).trimEnd()+`…`;return o=i.width(d,r,a),o<=t?{text:d,size:r,width:o}:void 0}export{e as t};
@@ -0,0 +1,4 @@
1
+ import{d as e,i as t,n,o as r,p as i,r as a,s as o,u as s}from"./BGW36YJW-CJMhya8S.js";import{a as c,t as l}from"./DKTLEL5E-DciMwFJ1.js";import{d as u,l as d}from"./EFYWJBJK-CVywe_D5.js";import{a as f,i as p,r as m}from"./VBUCM3YD-JHGjuJC_.js";import{t as h}from"./NT5JY5WP-1lJM-aun.js";import{C as g,E as _,F as v,P as y,d as b,f as x,h as S,m as C,o as w,r as ee,s as te,t as T,u as E,w as D}from"./lazy-f3CuUNJZ.js";var O={REQUIREMENT:`Requirement`,FUNCTIONAL_REQUIREMENT:`Functional Requirement`,INTERFACE_REQUIREMENT:`Interface Requirement`,PERFORMANCE_REQUIREMENT:`Performance Requirement`,PHYSICAL_REQUIREMENT:`Physical Requirement`,DESIGN_CONSTRAINT:`Design Constraint`},k={LOW_RISK:`Low`,MED_RISK:`Medium`,HIGH_RISK:`High`},A={VERIFY_ANALYSIS:`Analysis`,VERIFY_DEMONSTRATION:`Demonstration`,VERIFY_INSPECTION:`Inspection`,VERIFY_TEST:`Test`},j={CONTAINS:`contains`,COPIES:`copies`,DERIVES:`derives`,SATISFIES:`satisfies`,VERIFIES:`verifies`,REFINES:`refines`,TRACES:`traces`},M=1e6,N=class{type=`requirement`;direction=`TB`;requirements=/* @__PURE__ */ new Map;elements=/* @__PURE__ */ new Map;relations=[];classes=/* @__PURE__ */ new Map;title;accTitle;accDescr;newId=``;newText=``;newRisk=``;newVerifyMethod=``;newElementType=``;newDocRef=``;styleCopies=0;copy(e,t){if(this.styleCopies+=t.length,this.styleCopies>M)throw new E(`Requirement diagram applies too many style declarations.`,`limit`,{type:`requirement`});for(let n of t)e.push(n)}setDirection(e){this.direction=e}addRequirement(e,t){let n=this.requirements.get(e);return n===void 0&&(n={name:e,type:t,requirementId:this.newId,text:this.newText,risk:this.newRisk,verifyMethod:this.newVerifyMethod,cssStyles:[],classes:[`default`]},this.requirements.set(e,n)),this.newId=this.newText=this.newRisk=this.newVerifyMethod=``,n}setNewReqId(e){this.newId=e}setNewReqText(e){this.newText=e}setNewReqRisk(e){this.newRisk=e}setNewReqVerifyMethod(e){this.newVerifyMethod=e}addElement(e){let t=this.elements.get(e);return t===void 0&&(t={name:e,type:this.newElementType,docRef:this.newDocRef,cssStyles:[],classes:[`default`]},this.elements.set(e,t)),this.newElementType=this.newDocRef=``,t}setNewElementType(e){this.newElementType=e}setNewElementDocRef(e){this.newDocRef=e}addRelationship(e,t,n){this.relations.push({type:e,src:t,dst:n})}node(e){return this.requirements.get(e)??this.elements.get(e)}setCssStyle(e,t){for(let n of e){let e=this.node(n);if(!t||!e)return;for(let n of t)this.copy(e.cssStyles,n.includes(`,`)?n.split(`,`):[n])}}setClass(e,t){for(let n of e){let e=this.node(n);if(e)for(let n of t){e.classes.push(n);let t=this.classes.get(n)?.styles;t&&this.copy(e.cssStyles,t)}}}defineClass(e,t){for(let n of e){let e=this.classes.get(n);e===void 0&&(e={id:n,styles:[],textStyles:[]},this.classes.set(n,e));for(let n of t)n.includes(`color`)&&e.textStyles.push(n.replace(`fill`,`bgFill`)),e.styles.push(n);let r=t.flatMap(e=>e.split(`,`));for(let e of this.requirements.values())e.classes.includes(n)&&this.copy(e.cssStyles,r);for(let e of this.elements.values())e.classes.includes(n)&&this.copy(e.cssStyles,r)}}setAccTitle(e){this.accTitle=e.replace(/^\s+/g,``)}setAccDescription(e){this.accDescr=e.replace(/\n\s+/g,`
2
+ `)}},P=/* @__PURE__ */ `$end.INVALID.title.acc_title.acc_title_value.acc_descr.acc_descr_value.acc_descr_multiline_value.direction_tb.direction_bt.direction_rl.direction_lr.NEWLINE.EOF.RD.STRUCT_START.STRUCT_STOP.STYLE_SEPARATOR.COLONSEP.ID.TEXT.RISK.VERIFYMTHD.REQUIREMENT.FUNCTIONAL_REQUIREMENT.INTERFACE_REQUIREMENT.PERFORMANCE_REQUIREMENT.PHYSICAL_REQUIREMENT.DESIGN_CONSTRAINT.LOW_RISK.MED_RISK.HIGH_RISK.VERIFY_ANALYSIS.VERIFY_DEMONSTRATION.VERIFY_INSPECTION.VERIFY_TEST.ELEMENT.CONTAINS.COPIES.DERIVES.SATISFIES.VERIFIES.REFINES.TRACES.TYPE.DOCREF.STYLE.CLASSDEF.CLASS.ALPHA.COLON.SEMICOLON.PERCENT.MINUS.BRKT.END_ARROW_L.END_ARROW_R.LINE.qString.unqString.COMMA`.split(`.`),F=/* @__PURE__ */ new Map([[`requirementdiagram`,14],[`id`,19],[`text`,20],[`risk`,21],[`verifymethod`,22],[`requirement`,23],[`functionalrequirement`,24],[`interfacerequirement`,25],[`performancerequirement`,26],[`physicalrequirement`,27],[`designconstraint`,28],[`low`,29],[`medium`,30],[`high`,31],[`analysis`,32],[`demonstration`,33],[`inspection`,34],[`test`,35],[`element`,36],[`contains`,37],[`copies`,38],[`derives`,39],[`satisfies`,40],[`verifies`,41],[`refines`,42],[`traces`,43],[`type`,44],[`docref`,45],[`style`,46],[`classdef`,47],[`class`,48]]),I=/accTitle\s*:\s*/iy,L=/accDescr\s*:\s*/iy,R=/accDescr\s*\{\s*/iy,z=/direction\s+(?:(TB)|(BT)|(RL)|LR)/gi,B=/[\n\r\u2028\u2029]/g,V=22,H=/* @__PURE__ */ new Uint8Array(128);for(let e of`:,\r
3
+ {<>-=`)H[e.charCodeAt(0)]=1;function ne(e){let t=e.length,n=[],r=[],i=[],a=[],o=0,s=0,c=[[],[],[],[]],l=[[],[],[],[]],u=[0,0,0,0],d=!1,f=-1;z.lastIndex=0;for(let t=z.exec(e);t!==null;t=z.exec(e)){let e=t[1]===void 0?t[2]===void 0?t[3]===void 0?3:2:1:0;c[e].push(t.index),l[e].push(z.lastIndex),d=!0}let p=(t,a,o)=>{n.push(t),r.push(e.slice(a,o)),i.push(a),s=o},m=e=>{a.push(o),o=e},h=()=>{a.length>0&&(o=a.pop())},g=(t,n)=>(t.lastIndex=n,t.test(e)?t.lastIndex:-1),_=n=>{let r=e.indexOf(`
4
+ `,n);return r===-1?t:r},b=()=>{if(f<s){B.lastIndex=s,f=B.test(e)?B.lastIndex-1:t;for(let e=0;e<4;e++){let t=c[e],n=u[e];for(;n<t.length&&t[n]<=f;)n++;u[e]=n}}for(let e=0;e<4;e++){let t=u[e]-1;if(t>=0&&c[e][t]>=s)return p(8+e,s,_(l[e][t])),!0}return!1},x=a=>{let o=a|32;if(o===116&&e.slice(s+1,s+5).toLowerCase()===`itle`&&v(e.charCodeAt(s+5))){let n=s+6;for(;n<t;){let t=e.charCodeAt(n);if(t===35||t===10||t===59)break;n++}if(n>s+6)return p(2,s,n),!0}if(o===97&&(e.charCodeAt(s+1)|32)==99&&(e.charCodeAt(s+2)|32)==99){let e;if((e=g(I,s))>=0)return m(3),p(3,s,e),!0;if((e=g(L,s))>=0)return m(4),p(5,s,e),!0;if((e=g(R,s))>=0)return m(5),s=e,!0}if(d&&!y(a)&&b())return!0;if(a===10||a===13&&e.charCodeAt(s+1)===10){let n=s;for(;n<t;){let t=e.charCodeAt(n);if(t===10)n++;else if(t===13&&e.charCodeAt(n+1)===10)n+=2;else break}return p(12,s,n),!0}if(v(a)){for(s++;s<t&&v(e.charCodeAt(s));)s++;return!0}if(a===35||a===37)return s=_(s),!0;if(T(a)){let a=s+1;for(;a<t&&T(e.charCodeAt(a));)a++;if(a-s<=V){let t=F.get(e.slice(s,a).toLowerCase());if(t!==void 0)return(t===46||t===47||t===48)&&m(2),p(t,s,a),!0}for(;a<t;){let t=e.charCodeAt(a);if(t<128&&H[t]===1)break;a++}return n.push(59),r.push(e.slice(s,a).trim()),i.push(s),s=a,!0}switch(a){case 123:return p(15,s,s+1),!0;case 125:return p(16,s,s+1),!0;case 58:return e.charCodeAt(s+1)===58&&e.charCodeAt(s+2)===58?p(17,s,s+3):p(18,s,s+1),!0;case 60:return e.charCodeAt(s+1)===45&&(p(55,s,s+2),!0);case 45:return e.charCodeAt(s+1)===62?p(56,s,s+2):p(57,s,s+1),!0;case 34:return m(1),s++,!0;case 44:return p(60,s,s+1),!0}return!1},S=n=>{if(T(n)){let n=s+1;for(;n<t&&T(e.charCodeAt(n));)n++;return p(49,s,n),!0}switch(n){case 58:return p(50,s,s+1),!0;case 59:return p(51,s,s+1),!0;case 37:return p(52,s,s+1),!0;case 45:return p(53,s,s+1),!0;case 35:return p(54,s,s+1),!0;case 32:return s++,!0;case 34:return m(1),s++,!0;case 10:return h(),s++,!0;case 44:return p(60,s,s+1),!0}return!1};for(;s<t;){let a=e.charCodeAt(s),c=!0;switch(o){case 0:c=x(a);break;case 2:c=S(a);break;case 1:if(a===34)h(),s++;else{let n=e.indexOf(`"`,s);p(58,s,n===-1?t:n)}break;case 3:h(),p(4,s,_(s));break;case 4:h(),p(6,s,_(s));break;case 5:if(a===125)h(),s++;else{let n=e.indexOf(`}`,s);p(7,s,n===-1?t:n)}}if(!c)return n.push(1),r.push(``),i.push(s),{types:n,texts:r,starts:i}}let C=o===0?13:o===1?58:o===3?4:o===4?6:o===5?7:-1;return C!==-1&&p(C,t,t),p(0,t,t),{types:n,texts:r,starts:i}}var re=[O.REQUIREMENT,O.FUNCTIONAL_REQUIREMENT,O.INTERFACE_REQUIREMENT,O.PERFORMANCE_REQUIREMENT,O.PHYSICAL_REQUIREMENT,O.DESIGN_CONSTRAINT,k.LOW_RISK,k.MED_RISK,k.HIGH_RISK,A.VERIFY_ANALYSIS,A.VERIFY_DEMONSTRATION,A.VERIFY_INSPECTION,A.VERIFY_TEST,``,j.CONTAINS,j.COPIES,j.DERIVES,j.SATISFIES,j.VERIFIES,j.REFINES,j.TRACES];function U(e){return e===49||e===50||e===54||e===53||e===51}function ie(e,t){let{types:n,texts:r,starts:i}=ne(e),a=0,o=[],s=[],c=(t,r=t,...o)=>{let s=n[a];if(s===1)throw g(`requirement`,e,i[a],``,!0);for(let e=r;e>=t;e--)o.unshift(e);let c=o.map(e=>`'${P[e]}'`).join(`, `);throw g(`requirement`,e,i[a],`Expecting ${c}, got '${P[s]}'`)},l=e=>(n[a]!==e&&c(e),r[a++]),u=(e,t)=>{let r=n[a];return(r<e||r>t)&&c(e,t),a++,re[r-23]},d=()=>{let e=n[a];return e!==59&&e!==58&&c(58,59),r[a++]},f=()=>{let e=n[a];if(e===3)a++,t.setAccTitle(l(4).trim());else if(e===5)a++,t.setAccDescription(l(6).trim());else if(e===7)t.setAccDescription(r[a++].trim());else return!1;return!0},p=()=>{let e=[];for(;;){let t=n[a];if(t!==49&&t!==59&&t!==58&&c(58,59,49),e.push(r[a++]),n[a]!==60)return e;a++}},m=()=>{let e=[];for(;;){U(n[a])||c(49,51,53,54);let t=r[a++];for(;U(n[a]);)t+=r[a++];if(e.push(t),n[a]!==60)return e;a++}},h=()=>{let e;return n[a]===17?(a++,e=p(),l(15)):n[a]===15?a++:c(15,15,17),l(12),e},_=e=>{for(o.length=0,s.length=0;;){let t=n[a];if(t===16){a++;break}t===12?a++:((e?t<19||t>22:t!==44&&t!==45)&&(e&&c(19,22,12,16),c(44,45,12,16)),a++,l(18),s.push(t===21?u(29,31):t===22?u(32,35):d()),o.push(t),l(12))}for(let e=o.length-1;e>=0;e--){let n=s[e];switch(o[e]){case 19:t.setNewReqId(n);break;case 20:t.setNewReqText(n);break;case 21:t.setNewReqRisk(n);break;case 22:t.setNewReqVerifyMethod(n);break;case 44:t.setNewElementType(n);break;default:t.setNewElementDocRef(n)}}};for(;f();)n[a]===12&&a++;for(n[a]!==14&&c(14,14,3,5,7),a++,l(12);;){let e=n[a];if(e===13)return;if(e===12)a++;else{if(f())continue;if(e>=8&&e<=11)a++,t.setDirection(e===8?`TB`:e===9?`BT`:e===10?`RL`:`LR`);else if(e>=23&&e<=28){let e=u(23,28),n=d(),r=h();_(!0),t.addRequirement(n,e),r&&t.setClass([n],r)}else if(e===36){a++;let e=d(),n=h();_(!1),t.addElement(e),n&&t.setClass([e],n)}else if(e===46){a++;let e=p();t.setCssStyle(e,m())}else if(e===47){a++;let e=p();t.defineClass(e,m())}else if(e===48){a++;let e=p();t.setClass(e,p())}else if(e===59||e===58){let e=r[a++],i=n[a];if(i===17)a++,t.setClass([e],p());else if(i===55||i===57){a++;let n=u(37,43);l(i===57?56:57);let r=d();i===57?t.addRelationship(n,e,r):t.addRelationship(n,r,e)}else c(17,17,55,57)}else c(12,13,3,5,7,8,9,10,11,23,24,25,26,27,28,36,47,48,46,59,58)}}}function ae(e){let t=[];for(let n of e.requirements.values())t.push({id:n.name,requirement:n,element:void 0,cssStyles:n.cssStyles,cssClasses:n.classes.join(` `)});for(let n of e.elements.values())t.push({id:n.name,requirement:void 0,element:n,cssStyles:n.cssStyles,cssClasses:n.classes.join(` `)});let n=t=>e.requirements.has(t)||e.elements.has(t)?t:void 0;return{nodes:t,edges:e.relations.map((e,t)=>({id:`${e.src}-${e.dst}-${t}`,start:n(e.src),end:n(e.dst),type:e.type,contains:e.type===j.CONTAINS})),direction:e.direction}}var oe=40,se=16,ce=56,W=14,G=10,K=2,q=12,le=120,ue=280,de=240,J=26,Y=6,X=9,Z=4.5,fe=28,pe=14;function Q(e,t,n){if(t===0||e.lines.length===0)return e;let r=0,i=[],a=e.lines.map(a=>{let o=0,s=a.map(r=>{let i=r.style|t,a=r.icon!==void 0||r.math?r.width:n.width(r.text,e.size,i);return o+=a,{...r,style:i,width:a}});return i.push(o),o>r&&(r=o),s});return{...e,lines:a,widths:i,width:r}}function $(e,t,n,a){let o=C(e),c=``,l=``;for(let u=0;u<e.lines.length;u++){let d=e.lines[u],f=n+r(e,u);if(i(d)){l+=S(s(e,u),t+e.widths[u]/2,f,a);continue}let p=f+e.size*.35,m=!0;for(let e of d)e.icon!==void 0||e.text===``||(c+=`<tspan${m?` x="${_(t)}" y="${_(p)}"`:``}${w(e.style&~o)}>${b(e.text)}</tspan>`,m=!1)}return(c===``?``:`<text${D(a,o)} text-anchor="start" xml:space="preserve">${c}</text>`)+l}function me(e,t,n,r){let i=Y,a=e-n*i,o=t-r*i;return`<circle class="pele-marker" cx="${_(a)}" cy="${_(o)}" r="${i}"/><path class="pele-marker" d="M${_(a-n*i)},${_(o-r*i)}L${_(a+n*i)},${_(o+r*i)}M${_(a+r*i)},${_(o-n*i)}L${_(a-r*i)},${_(o+n*i)}"/>`}function he(e,t,n,r){let i=e-n*X,a=t-r*X;return`<path class="pele-marker" d="M${_(i-r*Z)},${_(a+n*Z)}L${_(e)},${_(t)}L${_(i+r*Z)},${_(a-n*Z)}" stroke-linejoin="round"/>`}function ge(e,t,n){let r=/* @__PURE__ */ new Map;for(let n of t){let t=n.route;if(!(n.src===n.dst||t.length<6))for(let i of[0,t.length-3]){let a=i===0?n.src:n.dst,o=e[a],s=t[i]-o.x,c=t[i+1]-o.y,l=i===0?3:i-3,u;if(Math.abs(Math.abs(c)-o.h/2)<.5)u=c<0?0:2;else if(Math.abs(Math.abs(s)-o.w/2)<.5)u=s<0?3:1;else continue;let d=a*4+u,f={route:t,at:i,toward:t[u%2==0?l:l+1]},p=r.get(d);p?p.push(f):r.set(d,[f])}}for(let[t,i]of r){if(i.length<2)continue;let r=t>>2,a=t%2==1,o=(a?n[r].h:n[r].w)-2*pe,s=Math.max(0,Math.min(fe,o/(i.length-1)));i.sort((e,t)=>e.toward-t.toward);for(let t=0;t<i.length;t++){let n=(t-(i.length-1)/2)*s;a?i[t].route[i[t].at+1]=e[r].y+n:i[t].route[i[t].at]=e[r].x+n}}}function _e(e,t,n){return c(e.direction)?l(n,r=>ve(e,t,n,r)):ve(e,t,n,!1)}function ve(r,i,s,c){let l=ae(r),g=s.fontSize??16,v=Math.round(g*.875),y=s.measurer??te(s.fontFamily,s.fontFamilyMono),b=s.padding??8,C=/* @__PURE__ */ new Map,w=[],T=[];for(let e of l.nodes){let n=a(e.cssStyles),r=+!!n.bold|(n.italic?2:0),i=n.fontSize??g,o=n.fontSize??v,s=e.requirement,c=e.element,l=Q(t(`«${s?s.type:`Element`}»`,!1,y,o,4e3),r,y),u=Q(t(e.id,!0,y,i,ue),r|1,y),d=s?[[`ID`,s.requirementId],[`Text`,s.text],[`Risk`,s.risk],[`Verification`,s.verifyMethod]]:[[`Type`,c.type],[`Doc Ref`,c.docRef]],p=[],m=0,h=0,_=0;for(let[e,n]of d){if(!n)continue;let i=Q(t(e,!1,y,o,4e3),r,y),a=Q(t(n,!0,y,o,de),r,y),s=Math.max(i.height,a.height);p.push({key:i,value:a,height:s}),m=Math.max(m,i.width),h=Math.max(h,a.width),_+=s+K}p.length>0&&(_+=2*G-K);let b=2*G+l.height+u.height,x=Math.max(le,Math.max(l.width,u.width)+2*W,p.length>0?m+q+h+2*W:0);C.has(e.id)||C.set(e.id,w.length);let S=f(x,b+_);S.seq=w.length,w.push({node:e,style:n,kind:l,name:u,rows:p,keyWidth:m,headHeight:b,w:x,h:b+_}),T.push(S)}let E=c?`TB`:p(l.direction),D=E===`LR`||E===`RL`,O=[],k=[],A=[];for(let e of l.edges){let n=e.start===void 0?void 0:C.get(e.start),r=e.end===void 0?void 0:C.get(e.end);if(n===void 0||r===void 0)continue;let i=t(`«${e.type}»`,!1,y,v,4e3);n===r&&(D?T[n].h+=2*(J+i.height+8):T[n].w+=2*(J+i.width+12)),A.push(e),k.push(i),O.push({src:n,dst:r,minlen:1,labelW:i.width+12,labelH:i.height+4,route:[],labelX:0,labelY:0})}let j=m(T,O,E,{nodeSep:oe,edgeSep:se,rankSep:ce,portSep:20});ge(T,O,w);let M=h(r.title,y,g,4e3),N=M.height>0?M.height+12:0,P=Math.max(j.width,M.width),F=b+(P-j.width)/2,I=b+N,L=Math.ceil(P+2*b),R=Math.ceil(j.height+N+2*b),z=``;for(let e=0;e<w.length;e++){let t=w[e],{w:r,h:i,style:a}=t,s=ee(a),c=s||` fill="var(--_m)"`,l=-r/2,u=-i/2,d=`<rect x="${_(l)}" y="${_(u)}" width="${_(r)}" height="${_(i)}" fill="var(--_s)" stroke="var(--_b)"${o(a.shape)}/>`+S(t.kind,0,u+G+t.kind.height/2,` class="pele-stereotype" font-size="${t.kind.size}"${c}${a.text}`)+S(t.name,0,u+G+t.kind.height+t.name.height/2,` class="pele-label" font-size="${t.name.size}" font-weight="var(--_hw)"${s}${a.text}`);if(t.rows.length>0){let e=u+t.headHeight;d+=`<path class="pele-divider" d="M${_(l)},${_(e)}H${_(-l)}" stroke="var(--_b)"${a.line}/>`;let n=e+G,r=``;for(let e of t.rows)r+=$(e.key,l+W,n,` class="pele-field"${c}${a.text}`)+$(e.value,l+W+t.keyWidth+q,n,` class="pele-value"${s}${a.text}`),n+=e.height+K;d+=`<g class="pele-fields" font-size="${t.rows[0].key.size}">${r}</g>`}z+=`<g class="pele-node ${t.node.requirement?`pele-requirement`:`pele-element`}${n(t.node.cssClasses.replace(/^default\s?/,``))}" data-id="${x(t.node.id)}" transform="translate(${_(T[e].x+F)},${_(T[e].y+I)})">${d}</g>`}let B=``,V=``,H=/* @__PURE__ */ new Map;for(let e=0;e<O.length;e++){let t=O[e],n=A[e],r=k[e],i=n.contains?2*Y:0,a,o=t.labelX+F,s=t.labelY+I;if(t.src===t.dst){let e=w[t.src],n=T[t.src].x+F,r=T[t.src].y+I,c=H.get(t.src)??0;H.set(t.src,c+1);let l=(D?e.w:e.h)/2,u=Math.min(l-4,12+c*6),d=J+c*8,f=(t,i)=>D?`${_(n+i)},${_(r+e.h/2+t)}`:`${_(n+e.w/2+t)},${_(r+i)}`,p=d/.75;a={d:`M${f(i,-u)}C${f(p,-u)} ${f(p,u)} ${f(0,u)}`,sx:D?n-u:n+e.w/2,sy:D?r+e.h/2:r-u,sdx:D?0:-1,sdy:D?-1:0,ex:D?n+u:n+e.w/2,ey:D?r+e.h/2:r+u,edx:D?0:-1,edy:D?-1:0},D?(o=n,s=r+e.h/2+d+4+t.labelH/2):(o=n+e.w/2+d+4+t.labelW/2,s=r)}else{let e=t.route.slice();for(let t=0;t<e.length;t+=3)e[t]+=F,e[t+1]+=I;a=u(e,void 0,i,0)}B+=`<g class="pele-edge pele-relationship-${n.contains?`contains`:`arrow`}" data-id="${x(n.id)}"><path d="${a.d}"${n.contains?``:` stroke-dasharray="5 4"`}/>`+(n.contains?me(a.sx,a.sy,a.sdx,a.sdy):he(a.ex,a.ey,a.edx,a.edy))+`</g>`,V+=d(n.id,r,o,s,``)}return{svg:e(`requirement`,L,R,g,s,r,S(M,L/2,b+M.height/2,` class="pele-title" font-weight="var(--_tw)"`)+(B?`<g class="pele-edges" fill="none" stroke="var(--_l)" stroke-linecap="round">${B}</g>`:``)+(V?`<g class="pele-edge-labels" font-size="${v}" fill="var(--_m)">${V}</g>`:``)+`<g class="pele-nodes">${z}</g>`),width:L,height:R,links:[]}}var ye={type:`requirement`,math:!0,parse(e,t,n){let r=new N;return n&&(r.title=n),ie(e,r),r},render:_e};export{ye as requirement};