@flamingo-stack/openframe-frontend-core 0.0.532-snapshot.20260811155109 → 0.0.532

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 (151) hide show
  1. package/dist/chat-protocol/decode.d.ts +9 -1
  2. package/dist/chat-protocol/decode.d.ts.map +1 -1
  3. package/dist/chat-protocol/events.d.ts +0 -35
  4. package/dist/chat-protocol/events.d.ts.map +1 -1
  5. package/dist/chat-protocol/index.cjs +3 -72
  6. package/dist/chat-protocol/index.cjs.map +1 -1
  7. package/dist/chat-protocol/index.d.ts +0 -1
  8. package/dist/chat-protocol/index.d.ts.map +1 -1
  9. package/dist/chat-protocol/index.js +4 -67
  10. package/dist/chat-protocol/index.js.map +1 -1
  11. package/dist/chat-protocol/nats-decoder.d.ts +0 -41
  12. package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
  13. package/dist/{chunk-OLOKQQAP.cjs → chunk-2MBS3YEY.cjs} +5 -5
  14. package/dist/{chunk-OLOKQQAP.cjs.map → chunk-2MBS3YEY.cjs.map} +1 -1
  15. package/dist/{chunk-DFQLUK75.cjs → chunk-2QP67D2L.cjs} +3 -3
  16. package/dist/{chunk-DFQLUK75.cjs.map → chunk-2QP67D2L.cjs.map} +1 -1
  17. package/dist/{chunk-6SMLYZLH.js → chunk-3G7FDE5E.js} +4 -4
  18. package/dist/{chunk-J4BE4FIR.cjs → chunk-4GL2XJ5C.cjs} +4 -4
  19. package/dist/{chunk-J4BE4FIR.cjs.map → chunk-4GL2XJ5C.cjs.map} +1 -1
  20. package/dist/{chunk-LE3BJ5H2.js → chunk-5W3RCZIK.js} +2 -2
  21. package/dist/{chunk-MVGL5NNJ.js → chunk-65NR333L.js} +2 -2
  22. package/dist/{chunk-6W3ULZ4X.js → chunk-7SGBF2I2.js} +6 -6
  23. package/dist/{chunk-6YXN5RHW.js → chunk-ALXLWF75.js} +2 -2
  24. package/dist/{chunk-5U67WTAS.js → chunk-AZBS4PZG.js} +2 -2
  25. package/dist/{chunk-4QKXNWH5.js → chunk-ECR37JHH.js} +2 -2
  26. package/dist/{chunk-ENPGPZIB.cjs → chunk-ETF2QXXU.cjs} +61 -61
  27. package/dist/{chunk-ENPGPZIB.cjs.map → chunk-ETF2QXXU.cjs.map} +1 -1
  28. package/dist/{chunk-UPHN2VRZ.cjs → chunk-GVLA2BPW.cjs} +117 -259
  29. package/dist/chunk-GVLA2BPW.cjs.map +1 -0
  30. package/dist/{chunk-4ZRHVOEB.cjs → chunk-GWFC3NE5.cjs} +31 -31
  31. package/dist/{chunk-4ZRHVOEB.cjs.map → chunk-GWFC3NE5.cjs.map} +1 -1
  32. package/dist/{chunk-GNOK5J34.cjs → chunk-HJDCDOJM.cjs} +37 -37
  33. package/dist/{chunk-GNOK5J34.cjs.map → chunk-HJDCDOJM.cjs.map} +1 -1
  34. package/dist/{chunk-JDW2IL7A.cjs → chunk-HXHA3DCI.cjs} +11 -11
  35. package/dist/{chunk-JDW2IL7A.cjs.map → chunk-HXHA3DCI.cjs.map} +1 -1
  36. package/dist/{chunk-XN4JKIJY.js → chunk-IZOGFZ7N.js} +2 -2
  37. package/dist/{chunk-Z3NOI7X7.cjs → chunk-JZ5XDZYD.cjs} +26 -26
  38. package/dist/{chunk-Z3NOI7X7.cjs.map → chunk-JZ5XDZYD.cjs.map} +1 -1
  39. package/dist/{chunk-YLB6XXO6.js → chunk-KYQZBQYV.js} +2 -2
  40. package/dist/{chunk-RKITTO5V.cjs → chunk-L6EKEYF7.cjs} +14 -14
  41. package/dist/{chunk-RKITTO5V.cjs.map → chunk-L6EKEYF7.cjs.map} +1 -1
  42. package/dist/{chunk-NAV4FRNL.cjs → chunk-LYWSHNK4.cjs} +26 -26
  43. package/dist/{chunk-NAV4FRNL.cjs.map → chunk-LYWSHNK4.cjs.map} +1 -1
  44. package/dist/{chunk-46FKXLNE.js → chunk-OAEJWP7H.js} +2 -2
  45. package/dist/{chunk-2MYDGLJS.cjs → chunk-QEMI34VA.cjs} +87 -87
  46. package/dist/{chunk-2MYDGLJS.cjs.map → chunk-QEMI34VA.cjs.map} +1 -1
  47. package/dist/{chunk-QZ2NAPLF.cjs → chunk-QYNLVIGQ.cjs} +9 -9
  48. package/dist/{chunk-QZ2NAPLF.cjs.map → chunk-QYNLVIGQ.cjs.map} +1 -1
  49. package/dist/{chunk-KMB66NYX.cjs → chunk-TMZS63G2.cjs} +7 -7
  50. package/dist/{chunk-KMB66NYX.cjs.map → chunk-TMZS63G2.cjs.map} +1 -1
  51. package/dist/{chunk-2TUOBFPK.js → chunk-TQ7EPKKO.js} +2 -2
  52. package/dist/{chunk-U555TMPC.js → chunk-UP4RA54U.js} +5 -5
  53. package/dist/{chunk-LIUARTOZ.js → chunk-WINT757E.js} +64 -206
  54. package/dist/chunk-WINT757E.js.map +1 -0
  55. package/dist/{chunk-2XGHEJZK.js → chunk-XBYEPSNZ.js} +4 -4
  56. package/dist/components/case-studies/index.cjs +8 -8
  57. package/dist/components/case-studies/index.js +2 -2
  58. package/dist/components/chat/embeddable-chat.d.ts +0 -13
  59. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  60. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  61. package/dist/components/chat/index.cjs +2 -10
  62. package/dist/components/chat/index.cjs.map +1 -1
  63. package/dist/components/chat/index.js +1 -9
  64. package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -9
  65. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
  66. package/dist/components/chat/types/message.types.d.ts +0 -15
  67. package/dist/components/chat/types/message.types.d.ts.map +1 -1
  68. package/dist/components/chat/types/network.types.d.ts +0 -4
  69. package/dist/components/chat/types/network.types.d.ts.map +1 -1
  70. package/dist/components/chat/types/processing.types.d.ts +1 -7
  71. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  72. package/dist/components/chat/utils/index.d.ts +0 -1
  73. package/dist/components/chat/utils/index.d.ts.map +1 -1
  74. package/dist/components/chat/utils/message-segment-accumulator.d.ts +3 -25
  75. package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
  76. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  77. package/dist/components/contact/index.cjs +3 -3
  78. package/dist/components/contact/index.js +2 -2
  79. package/dist/components/docs/index.cjs +5 -5
  80. package/dist/components/docs/index.js +4 -4
  81. package/dist/components/embeds/index.cjs +3 -3
  82. package/dist/components/embeds/index.js +2 -2
  83. package/dist/components/faq/index.cjs +3 -3
  84. package/dist/components/faq/index.js +2 -2
  85. package/dist/components/features/index.cjs +2 -2
  86. package/dist/components/features/index.js +1 -1
  87. package/dist/components/help-center-pages/index.cjs +22 -22
  88. package/dist/components/help-center-pages/index.js +13 -13
  89. package/dist/components/index.cjs +132 -140
  90. package/dist/components/index.cjs.map +1 -1
  91. package/dist/components/index.js +11 -19
  92. package/dist/components/index.js.map +1 -1
  93. package/dist/components/meeting-scheduler/index.cjs +34 -34
  94. package/dist/components/meeting-scheduler/index.js +3 -3
  95. package/dist/components/navigation/index.cjs +2 -2
  96. package/dist/components/navigation/index.js +1 -1
  97. package/dist/components/onboarding-guides/index.cjs +5 -5
  98. package/dist/components/onboarding-guides/index.js +4 -4
  99. package/dist/components/related-content/index.cjs +3 -3
  100. package/dist/components/related-content/index.js +2 -2
  101. package/dist/components/tickets/index.cjs +6 -6
  102. package/dist/components/tickets/index.js +5 -5
  103. package/dist/components/ui/index.cjs +2 -10
  104. package/dist/components/ui/index.cjs.map +1 -1
  105. package/dist/components/ui/index.js +1 -9
  106. package/dist/index.cjs +2 -10
  107. package/dist/index.cjs.map +1 -1
  108. package/dist/index.js +1 -9
  109. package/dist/utils/index.cjs.map +1 -1
  110. package/dist/utils/index.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -70
  113. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +1 -108
  114. package/src/chat-protocol/decode.ts +153 -7
  115. package/src/chat-protocol/events.ts +0 -40
  116. package/src/chat-protocol/index.ts +0 -3
  117. package/src/chat-protocol/nats-decoder.ts +3 -111
  118. package/src/components/chat/embeddable-chat.tsx +0 -18
  119. package/src/components/chat/hooks/use-sse-chat-adapter.ts +16 -7
  120. package/src/components/chat/stream/chat-stream-reducer.ts +3 -49
  121. package/src/components/chat/types/message.types.ts +0 -15
  122. package/src/components/chat/types/network.types.ts +0 -4
  123. package/src/components/chat/types/processing.types.ts +1 -12
  124. package/src/components/chat/utils/__tests__/process-historical-messages-approvals.test.ts +0 -92
  125. package/src/components/chat/utils/index.ts +0 -9
  126. package/src/components/chat/utils/message-segment-accumulator.ts +32 -62
  127. package/src/components/chat/utils/process-historical-messages.ts +6 -40
  128. package/dist/chat-protocol/confirm-tool.d.ts +0 -50
  129. package/dist/chat-protocol/confirm-tool.d.ts.map +0 -1
  130. package/dist/chat-protocol/leading-frames.d.ts +0 -38
  131. package/dist/chat-protocol/leading-frames.d.ts.map +0 -1
  132. package/dist/chunk-LIUARTOZ.js.map +0 -1
  133. package/dist/chunk-UPHN2VRZ.cjs.map +0 -1
  134. package/dist/components/chat/utils/approval-display.d.ts +0 -49
  135. package/dist/components/chat/utils/approval-display.d.ts.map +0 -1
  136. package/src/chat-protocol/confirm-tool.ts +0 -67
  137. package/src/chat-protocol/leading-frames.ts +0 -175
  138. package/src/components/chat/utils/approval-display.ts +0 -70
  139. /package/dist/{chunk-6SMLYZLH.js.map → chunk-3G7FDE5E.js.map} +0 -0
  140. /package/dist/{chunk-LE3BJ5H2.js.map → chunk-5W3RCZIK.js.map} +0 -0
  141. /package/dist/{chunk-MVGL5NNJ.js.map → chunk-65NR333L.js.map} +0 -0
  142. /package/dist/{chunk-6W3ULZ4X.js.map → chunk-7SGBF2I2.js.map} +0 -0
  143. /package/dist/{chunk-6YXN5RHW.js.map → chunk-ALXLWF75.js.map} +0 -0
  144. /package/dist/{chunk-5U67WTAS.js.map → chunk-AZBS4PZG.js.map} +0 -0
  145. /package/dist/{chunk-4QKXNWH5.js.map → chunk-ECR37JHH.js.map} +0 -0
  146. /package/dist/{chunk-XN4JKIJY.js.map → chunk-IZOGFZ7N.js.map} +0 -0
  147. /package/dist/{chunk-YLB6XXO6.js.map → chunk-KYQZBQYV.js.map} +0 -0
  148. /package/dist/{chunk-46FKXLNE.js.map → chunk-OAEJWP7H.js.map} +0 -0
  149. /package/dist/{chunk-2TUOBFPK.js.map → chunk-TQ7EPKKO.js.map} +0 -0
  150. /package/dist/{chunk-U555TMPC.js.map → chunk-UP4RA54U.js.map} +0 -0
  151. /package/dist/{chunk-2XGHEJZK.js.map → chunk-XBYEPSNZ.js.map} +0 -0
@@ -1,49 +0,0 @@
1
- /**
2
- * WHERE an approval card renders — the one rule, read by every path that turns
3
- * an approval into segments.
4
- *
5
- * There are three such paths and they must agree: the NATS kernel (live chunks),
6
- * the SSE kernel, and the history replay that rebuilds a dialog after a reload.
7
- * When the rule lived only in the live kernel, a Product Guide card rendered
8
- * inline while streaming and then moved into the escalation treatment on the
9
- * next page load — same card, two behaviors, and its buttons pointed at the
10
- * wrong backend once it moved.
11
- *
12
- * The rule itself: a card is gated on approval TIER (`displayApprovalTypes`,
13
- * the agent's own privilege model) UNLESS it came from the Product Guide, whose
14
- * proposals are typed by TOOL and are resolved through the hub's confirm route.
15
- * A hub proposal has no tier to gate on, so tier-gating one means escalating it
16
- * to a human who has no way to act on it.
17
- */
18
- import type { GuideOrigin } from '../../../chat-protocol/events';
19
- import type { MessageSegment } from '../types/message.types';
20
- /** Anything carrying the optional `origin` marker: a stream event, or the
21
- * `data` of a rendered segment. */
22
- interface OriginBearing {
23
- origin?: GuideOrigin | string;
24
- }
25
- /** True when this approval was minted by the Product Guide (the hub) rather
26
- * than by the agent's own tool-approval tiering. */
27
- export declare function isGuideApproval(source: OriginBearing | null | undefined): boolean;
28
- /** The marker to stamp onto a built segment, or undefined for agent approvals.
29
- * Keeps `origin` out of segment data unless it means something. */
30
- export declare function guideApprovalOrigin(source: OriginBearing | null | undefined): GuideOrigin | undefined;
31
- /**
32
- * Does this approval render inline in its turn (true), or take the consumer's
33
- * escalation treatment (false)?
34
- *
35
- * `displayApprovalTypes` undefined means "no tier gate configured" and every
36
- * agent approval displays — the pre-existing history-replay default, preserved
37
- * here so both kernels can share one predicate.
38
- */
39
- export declare function approvalDisplaysInline(source: OriginBearing | null | undefined, approvalType: string, displayApprovalTypes?: readonly string[]): boolean;
40
- /**
41
- * True for a rendered segment that holds a Product Guide card (single or batch).
42
- *
43
- * Consumers use it to keep such a card in the message flow, where the hub's own
44
- * chat puts it, instead of lifting it into their sticky pending-approvals
45
- * footer — and to route its buttons to the hub rather than to the agent.
46
- */
47
- export declare function isGuideApprovalSegment(segment: MessageSegment): boolean;
48
- export {};
49
- //# sourceMappingURL=approval-display.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"approval-display.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/utils/approval-display.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D;oCACoC;AACpC,UAAU,aAAa;IACrB,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;CAC9B;AAED;qDACqD;AACrD,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEjF;AAED;oEACoE;AACpE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACvC,WAAW,GAAG,SAAS,CAEzB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EACxC,YAAY,EAAE,MAAM,EACpB,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,GACvC,OAAO,CAGT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAGvE"}
@@ -1,67 +0,0 @@
1
- /**
2
- * The hub's confirm-tool contract — request shape and error envelope.
3
- *
4
- * Resolving a Product Guide proposal is a POST to `endpoints.approvalToolUrl`
5
- * whose response is an ordinary guide stream (a `decision_resolved` frame, then
6
- * the hub's auto-continuation). Two callers make that call today: the SSE chat
7
- * adapter, and any host replaying the same route into a different transport's
8
- * conversation (the product app resolves guide cards inside a NATS Mingo
9
- * dialog). They MUST send byte-identical bodies — a proposal is single-use, so a
10
- * caller that drifts on a field name does not degrade, it fails the write — and
11
- * they must surface the same server copy, because the hub's errors are written
12
- * for the end user ("This approval expired — ask again to get a fresh one").
13
- *
14
- * Hence both live here rather than at either call site.
15
- *
16
- * Server-safe: no React, no browser APIs.
17
- */
18
-
19
- /** What the user did with the card. */
20
- export type ApprovalToolAction = 'approve' | 'reject'
21
-
22
- /** Client-side shape of a confirm; `buildConfirmToolBody` renames it to the
23
- * wire's own casing so no caller hand-writes `proposal_id`. */
24
- export interface ConfirmToolRequest {
25
- /** The hub-minted proposal id carried by the card. */
26
- proposalId: string
27
- action: ApprovalToolAction
28
- /**
29
- * The hub's conversation id, quoted back verbatim. The hub rejects a confirm
30
- * without it: a proposal only means something inside the conversation that
31
- * minted it.
32
- */
33
- conversationId: string | null | undefined
34
- }
35
-
36
- /** Build the confirm-tool request body. The ONE place that knows the wire
37
- * spells the id `proposal_id` while everything else is camelCase. */
38
- export function buildConfirmToolBody(request: ConfirmToolRequest): Record<string, unknown> {
39
- return {
40
- proposal_id: request.proposalId,
41
- action: request.action,
42
- conversationId: request.conversationId,
43
- }
44
- }
45
-
46
- /**
47
- * Pull the user-facing copy out of a failed chat/confirm response.
48
- *
49
- * The route-base envelope is `{error, code}`; `error` is written for the end
50
- * user, so it beats any status-code copy the client could invent. Returns null
51
- * when the body is not that envelope (HTML error page, empty body, gateway
52
- * response) — the caller then supplies its own generic line.
53
- *
54
- * Consumes the body: call it once, on a response you have already decided is a
55
- * failure.
56
- */
57
- export async function readServerErrorMessage(response: {
58
- json: () => Promise<unknown>
59
- }): Promise<string | null> {
60
- try {
61
- const body = (await response.json()) as { error?: unknown } | null
62
- if (typeof body?.error === 'string' && body.error.length > 0) return body.error
63
- } catch {
64
- /* non-JSON error body — the caller falls through to its generic copy */
65
- }
66
- return null
67
- }
@@ -1,175 +0,0 @@
1
- /**
2
- * Leading-frame → `ChatStreamEvent` table — the ONE mapping both transports read.
3
- *
4
- * The SSE decoder (`./decode.ts`) reaches it while walking the hub's byte
5
- * framing. The NATS decoder (`./nats-decoder.ts`) reaches it for `GUIDE` chunks
6
- * carrying a `payload`: the saas-ai-agent re-streams the Product Guide's hub
7
- * frames VERBATIM into the Mingo chunk stream (`GuideStreamingService`), so the
8
- * bytes a guide answer is made of are the same frames on both transports — only
9
- * the envelope differs. Decoding them twice would drift the moment the hub adds
10
- * a frame kind, which is why this table lives outside either decoder.
11
- *
12
- * The NATS side does NOT take every event this table can produce; see
13
- * `guideFrameEvent` in `./nats-decoder.ts` for which ones cross over and why.
14
- *
15
- * Server-safe: no React, no browser APIs.
16
- */
17
-
18
- import type { ChatStreamEvent } from './events'
19
-
20
- /**
21
- * Escape `<` so markdown renderers that pass HTML through (rehypeRaw)
22
- * don't treat XML-like tokens in Claude's thinking output as elements.
23
- * `<` → `&lt;` preserves the visible character without breaking
24
- * blockquote `>` markers. Per-character, so it distributes over
25
- * concatenation: escape(a + b) === escape(a) + escape(b) — callers may
26
- * apply it per-delta or on the accumulated string interchangeably.
27
- */
28
- export function escapeThinkingTags(text: string): string {
29
- return text.replace(/</g, '&lt;')
30
- }
31
-
32
- /**
33
- * Map one parsed leading frame to normalized events, replicating the
34
- * legacy parser's else-if chain ORDER and its exact truthiness/typeof
35
- * gates. Frames that matched a branch but failed its inner validation
36
- * (e.g. `routing` without a string `routedComplexity`) produce NO event,
37
- * exactly like the legacy no-op.
38
- *
39
- * Every branch pushes AT MOST ONE event — `guideFrameEvent` relies on that
40
- * to hand a single event back to `decodeNatsChunk`'s per-chunk contract.
41
- */
42
- export function mapLeadingFrame(meta: any, out: ChatStreamEvent[]): void {
43
- if (meta.status === 'thinking') {
44
- out.push({ type: 'status', phase: 'thinking' })
45
- } else if (meta.kind === 'thinking-delta' && typeof meta.text === 'string') {
46
- // Wire is ALREADY delta — emit verbatim, append-only contract.
47
- out.push({ type: 'thinking-delta', text: meta.text })
48
- } else if (meta.kind === 'usage' && meta.stage === 'start') {
49
- out.push({
50
- type: 'usage',
51
- stage: 'start',
52
- input_tokens: meta.input_tokens,
53
- cache_read_input_tokens: meta.cache_read_input_tokens,
54
- cache_creation_input_tokens: meta.cache_creation_input_tokens,
55
- })
56
- } else if (meta.kind === 'decision_resolved' && typeof meta.action === 'string') {
57
- const status = meta.action === 'rejected' ? 'rejected' : 'approved'
58
- const toolName = typeof meta.tool_name === 'string' ? meta.tool_name : undefined
59
- const result = meta.result ?? null
60
- const card = meta.card ?? null
61
- out.push({
62
- type: 'approval-resolved',
63
- status,
64
- ok: meta.ok === true,
65
- willAutoContinue: meta.willAutoContinue === true,
66
- ...(toolName ? { toolName } : {}),
67
- ...(result ? { result } : {}),
68
- ...(card?.marker ? { marker: card.marker } : {}),
69
- ...(card?.ref ? { cardRef: card.ref } : {}),
70
- ...(card?.type ? { cardType: card.type } : {}),
71
- ...(typeof meta.receiptText === 'string' ? { receiptText: meta.receiptText } : {}),
72
- requestId: typeof meta.proposalId === 'string' ? meta.proposalId : undefined,
73
- })
74
- } else if (meta.kind === 'approval_batch' && meta.batchId && Array.isArray(meta.proposals)) {
75
- // Server-grouped multi-proposal turn → ONE batch event carrying a
76
- // tool-call row per proposal. `toolExecutionRequestId` is the row's
77
- // PROPOSAL id (each row resolves through its own per-proposal
78
- // confirm); the batch's `requestId` is the stable anchor the shell
79
- // uses for status flips. Field rows ride as the row's expandable
80
- // args so per-proposal detail stays reachable inside the batch.
81
- const toolCalls = (meta.proposals as Array<Record<string, any>>)
82
- .filter((p) => p && typeof p.proposalId === 'string')
83
- .map((p) => {
84
- const rawFields = Array.isArray(p.fields)
85
- ? (p.fields as Array<{ label?: string; value?: string }>).filter(
86
- (f) => f && f.label && f.value,
87
- )
88
- : []
89
- // Prefer human-readable identity labels for the row's
90
- // disambiguator; opaque-id labels ("Task", "Ticket") only as
91
- // the last-resort first field.
92
- const detail =
93
- rawFields.find((f) => /^(title|subject|name)$/i.test(String(f.label))) ??
94
- rawFields[0]
95
- const base =
96
- typeof p.title === 'string' && p.title.length > 0 ? p.title : String(p.toolName ?? 'Tool call')
97
- return {
98
- toolExecutionRequestId: String(p.proposalId),
99
- toolName: String(p.toolName ?? 'tool'),
100
- toolTitle: detail ? `${base} — ${detail.value}` : base,
101
- requiresApproval: true,
102
- toolCallArguments:
103
- rawFields.length > 0
104
- ? Object.fromEntries(rawFields.map((f) => [String(f.label), String(f.value)]))
105
- : null,
106
- }
107
- })
108
- if (toolCalls.length > 0) {
109
- out.push({
110
- type: 'approval-request',
111
- requestId: String(meta.batchId),
112
- approvalType: 'chat',
113
- toolCalls,
114
- status: 'pending',
115
- })
116
- }
117
- } else if (meta.kind === 'approval_request' && meta.proposalId) {
118
- const proposalId = String(meta.proposalId)
119
- const toolName = String(meta.toolName ?? 'tool')
120
- const headline =
121
- typeof meta.title === 'string' && meta.title.length > 0 ? meta.title : toolName
122
- const rawFields = Array.isArray(meta.fields)
123
- ? (meta.fields as Array<{ label?: string; value?: string }>)
124
- : []
125
- const fields: Array<{ label: string; value: string }> = []
126
- for (const f of rawFields) {
127
- if (!f || !f.label || !f.value) continue
128
- fields.push({ label: f.label, value: f.value })
129
- }
130
- out.push({
131
- type: 'approval-request',
132
- requestId: proposalId,
133
- approvalType: toolName,
134
- command: headline,
135
- fields,
136
- status: 'pending',
137
- })
138
- } else if (meta.kind === 'text-leading' && typeof meta.text === 'string') {
139
- out.push({ type: 'text-delta', text: meta.text, leading: true })
140
- } else if (meta.kind === 'tool_error') {
141
- const msg =
142
- typeof meta.message === 'string' && meta.message.length > 0
143
- ? meta.message
144
- : 'Could not complete the requested action right now.'
145
- out.push({ type: 'error', title: msg })
146
- } else if (meta.kind === 'routing') {
147
- if (typeof meta.routedComplexity === 'string') {
148
- out.push({
149
- type: 'metadata',
150
- routing: {
151
- routedComplexity: meta.routedComplexity,
152
- ...(typeof meta.routedModel === 'string' ? { routedModel: meta.routedModel } : {}),
153
- routedThinkingBudget:
154
- typeof meta.routedThinkingBudget === 'number' ? meta.routedThinkingBudget : null,
155
- },
156
- })
157
- }
158
- } else {
159
- // Catch-all metadata-ish frame. Raw values pass through UNVALIDATED
160
- // (possibly undefined) so the consumer can replicate the legacy
161
- // presence/truthiness gates exactly — including the `model`-presence
162
- // trigger whose value is never stored.
163
- out.push({
164
- type: 'metadata',
165
- sources: meta.sources,
166
- refs: meta.refs,
167
- provider: meta.provider,
168
- modelLabel: meta.modelLabel,
169
- modelName: meta.model,
170
- contextWindowMaxTokens: meta.contextWindowMaxTokens,
171
- scrollAnchor: meta.scrollAnchor,
172
- conversationId: meta.conversationId,
173
- })
174
- }
175
- }
@@ -1,70 +0,0 @@
1
- /**
2
- * WHERE an approval card renders — the one rule, read by every path that turns
3
- * an approval into segments.
4
- *
5
- * There are three such paths and they must agree: the NATS kernel (live chunks),
6
- * the SSE kernel, and the history replay that rebuilds a dialog after a reload.
7
- * When the rule lived only in the live kernel, a Product Guide card rendered
8
- * inline while streaming and then moved into the escalation treatment on the
9
- * next page load — same card, two behaviors, and its buttons pointed at the
10
- * wrong backend once it moved.
11
- *
12
- * The rule itself: a card is gated on approval TIER (`displayApprovalTypes`,
13
- * the agent's own privilege model) UNLESS it came from the Product Guide, whose
14
- * proposals are typed by TOOL and are resolved through the hub's confirm route.
15
- * A hub proposal has no tier to gate on, so tier-gating one means escalating it
16
- * to a human who has no way to act on it.
17
- */
18
-
19
- import type { GuideOrigin } from '../../../chat-protocol/events'
20
- import { GUIDE_ORIGIN, isGuideOrigin } from '../../../chat-protocol/events'
21
- import type { MessageSegment } from '../types/message.types'
22
-
23
- /** Anything carrying the optional `origin` marker: a stream event, or the
24
- * `data` of a rendered segment. */
25
- interface OriginBearing {
26
- origin?: GuideOrigin | string
27
- }
28
-
29
- /** True when this approval was minted by the Product Guide (the hub) rather
30
- * than by the agent's own tool-approval tiering. */
31
- export function isGuideApproval(source: OriginBearing | null | undefined): boolean {
32
- return isGuideOrigin(source)
33
- }
34
-
35
- /** The marker to stamp onto a built segment, or undefined for agent approvals.
36
- * Keeps `origin` out of segment data unless it means something. */
37
- export function guideApprovalOrigin(
38
- source: OriginBearing | null | undefined,
39
- ): GuideOrigin | undefined {
40
- return isGuideApproval(source) ? GUIDE_ORIGIN : undefined
41
- }
42
-
43
- /**
44
- * Does this approval render inline in its turn (true), or take the consumer's
45
- * escalation treatment (false)?
46
- *
47
- * `displayApprovalTypes` undefined means "no tier gate configured" and every
48
- * agent approval displays — the pre-existing history-replay default, preserved
49
- * here so both kernels can share one predicate.
50
- */
51
- export function approvalDisplaysInline(
52
- source: OriginBearing | null | undefined,
53
- approvalType: string,
54
- displayApprovalTypes?: readonly string[],
55
- ): boolean {
56
- if (isGuideApproval(source)) return true
57
- return !displayApprovalTypes || displayApprovalTypes.includes(approvalType)
58
- }
59
-
60
- /**
61
- * True for a rendered segment that holds a Product Guide card (single or batch).
62
- *
63
- * Consumers use it to keep such a card in the message flow, where the hub's own
64
- * chat puts it, instead of lifting it into their sticky pending-approvals
65
- * footer — and to route its buttons to the hub rather than to the agent.
66
- */
67
- export function isGuideApprovalSegment(segment: MessageSegment): boolean {
68
- if (segment.type !== 'approval_request' && segment.type !== 'approval_batch') return false
69
- return isGuideApproval(segment.data)
70
- }