codex-channel-bgos 0.2.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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -0
  3. package/dist/adapter.d.ts +96 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +677 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/agent-hints.d.ts +20 -0
  8. package/dist/agent-hints.d.ts.map +1 -0
  9. package/dist/agent-hints.js +92 -0
  10. package/dist/agent-hints.js.map +1 -0
  11. package/dist/agent-identity.d.ts +36 -0
  12. package/dist/agent-identity.d.ts.map +1 -0
  13. package/dist/agent-identity.js +42 -0
  14. package/dist/agent-identity.js.map +1 -0
  15. package/dist/attachment-bridge.d.ts +64 -0
  16. package/dist/attachment-bridge.d.ts.map +1 -0
  17. package/dist/attachment-bridge.js +216 -0
  18. package/dist/attachment-bridge.js.map +1 -0
  19. package/dist/auth-mode.d.ts +34 -0
  20. package/dist/auth-mode.d.ts.map +1 -0
  21. package/dist/auth-mode.js +48 -0
  22. package/dist/auth-mode.js.map +1 -0
  23. package/dist/bgos-api.d.ts +191 -0
  24. package/dist/bgos-api.d.ts.map +1 -0
  25. package/dist/bgos-api.js +225 -0
  26. package/dist/bgos-api.js.map +1 -0
  27. package/dist/bgos-ws.d.ts +94 -0
  28. package/dist/bgos-ws.d.ts.map +1 -0
  29. package/dist/bgos-ws.js +310 -0
  30. package/dist/bgos-ws.js.map +1 -0
  31. package/dist/capabilities.d.ts +28 -0
  32. package/dist/capabilities.d.ts.map +1 -0
  33. package/dist/capabilities.js +44 -0
  34. package/dist/capabilities.js.map +1 -0
  35. package/dist/catalog-sync.d.ts +26 -0
  36. package/dist/catalog-sync.d.ts.map +1 -0
  37. package/dist/catalog-sync.js +24 -0
  38. package/dist/catalog-sync.js.map +1 -0
  39. package/dist/cli.d.ts +3 -0
  40. package/dist/cli.d.ts.map +1 -0
  41. package/dist/cli.js +152 -0
  42. package/dist/cli.js.map +1 -0
  43. package/dist/codex-host.d.ts +44 -0
  44. package/dist/codex-host.d.ts.map +1 -0
  45. package/dist/codex-host.js +142 -0
  46. package/dist/codex-host.js.map +1 -0
  47. package/dist/commands-sync.d.ts +24 -0
  48. package/dist/commands-sync.d.ts.map +1 -0
  49. package/dist/commands-sync.js +43 -0
  50. package/dist/commands-sync.js.map +1 -0
  51. package/dist/config.d.ts +4 -0
  52. package/dist/config.d.ts.map +1 -0
  53. package/dist/config.js +36 -0
  54. package/dist/config.js.map +1 -0
  55. package/dist/default-commands.d.ts +27 -0
  56. package/dist/default-commands.d.ts.map +1 -0
  57. package/dist/default-commands.js +64 -0
  58. package/dist/default-commands.js.map +1 -0
  59. package/dist/event-mapper.d.ts +43 -0
  60. package/dist/event-mapper.d.ts.map +1 -0
  61. package/dist/event-mapper.js +127 -0
  62. package/dist/event-mapper.js.map +1 -0
  63. package/dist/heartbeat.d.ts +75 -0
  64. package/dist/heartbeat.d.ts.map +1 -0
  65. package/dist/heartbeat.js +186 -0
  66. package/dist/heartbeat.js.map +1 -0
  67. package/dist/inbound-handler.d.ts +167 -0
  68. package/dist/inbound-handler.d.ts.map +1 -0
  69. package/dist/inbound-handler.js +282 -0
  70. package/dist/inbound-handler.js.map +1 -0
  71. package/dist/inbound-input.d.ts +17 -0
  72. package/dist/inbound-input.d.ts.map +1 -0
  73. package/dist/inbound-input.js +25 -0
  74. package/dist/inbound-input.js.map +1 -0
  75. package/dist/index.d.ts +25 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +18 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/install-service.d.ts +8 -0
  80. package/dist/install-service.d.ts.map +1 -0
  81. package/dist/install-service.js +66 -0
  82. package/dist/install-service.js.map +1 -0
  83. package/dist/last-id-store.d.ts +33 -0
  84. package/dist/last-id-store.d.ts.map +1 -0
  85. package/dist/last-id-store.js +101 -0
  86. package/dist/last-id-store.js.map +1 -0
  87. package/dist/load-config.d.ts +30 -0
  88. package/dist/load-config.d.ts.map +1 -0
  89. package/dist/load-config.js +86 -0
  90. package/dist/load-config.js.map +1 -0
  91. package/dist/media-classify.d.ts +37 -0
  92. package/dist/media-classify.d.ts.map +1 -0
  93. package/dist/media-classify.js +108 -0
  94. package/dist/media-classify.js.map +1 -0
  95. package/dist/media-guard.d.ts +13 -0
  96. package/dist/media-guard.d.ts.map +1 -0
  97. package/dist/media-guard.js +159 -0
  98. package/dist/media-guard.js.map +1 -0
  99. package/dist/outbound-retry.d.ts +24 -0
  100. package/dist/outbound-retry.d.ts.map +1 -0
  101. package/dist/outbound-retry.js +75 -0
  102. package/dist/outbound-retry.js.map +1 -0
  103. package/dist/outbound.d.ts +223 -0
  104. package/dist/outbound.d.ts.map +1 -0
  105. package/dist/outbound.js +366 -0
  106. package/dist/outbound.js.map +1 -0
  107. package/dist/outbox.d.ts +16 -0
  108. package/dist/outbox.d.ts.map +1 -0
  109. package/dist/outbox.js +88 -0
  110. package/dist/outbox.js.map +1 -0
  111. package/dist/pair-cli.d.ts +44 -0
  112. package/dist/pair-cli.d.ts.map +1 -0
  113. package/dist/pair-cli.js +182 -0
  114. package/dist/pair-cli.js.map +1 -0
  115. package/dist/pending-unknown-store.d.ts +26 -0
  116. package/dist/pending-unknown-store.d.ts.map +1 -0
  117. package/dist/pending-unknown-store.js +135 -0
  118. package/dist/pending-unknown-store.js.map +1 -0
  119. package/dist/processed-ids.d.ts +36 -0
  120. package/dist/processed-ids.d.ts.map +1 -0
  121. package/dist/processed-ids.js +55 -0
  122. package/dist/processed-ids.js.map +1 -0
  123. package/dist/reply-markers.d.ts +42 -0
  124. package/dist/reply-markers.d.ts.map +1 -0
  125. package/dist/reply-markers.js +124 -0
  126. package/dist/reply-markers.js.map +1 -0
  127. package/dist/setup/supervisor.d.ts +16 -0
  128. package/dist/setup/supervisor.d.ts.map +1 -0
  129. package/dist/setup/supervisor.js +61 -0
  130. package/dist/setup/supervisor.js.map +1 -0
  131. package/dist/thread-map.d.ts +12 -0
  132. package/dist/thread-map.d.ts.map +1 -0
  133. package/dist/thread-map.js +72 -0
  134. package/dist/thread-map.js.map +1 -0
  135. package/dist/tool-progress.d.ts +103 -0
  136. package/dist/tool-progress.d.ts.map +1 -0
  137. package/dist/tool-progress.js +216 -0
  138. package/dist/tool-progress.js.map +1 -0
  139. package/dist/types.d.ts +244 -0
  140. package/dist/types.d.ts.map +1 -0
  141. package/dist/types.js +16 -0
  142. package/dist/types.js.map +1 -0
  143. package/dist/version.d.ts +2 -0
  144. package/dist/version.d.ts.map +1 -0
  145. package/dist/version.js +28 -0
  146. package/dist/version.js.map +1 -0
  147. package/dist/voice-rpc.d.ts +37 -0
  148. package/dist/voice-rpc.d.ts.map +1 -0
  149. package/dist/voice-rpc.js +44 -0
  150. package/dist/voice-rpc.js.map +1 -0
  151. package/package.json +47 -0
@@ -0,0 +1,223 @@
1
+ import type { BgosApi } from "./bgos-api.js";
2
+ import type { ApprovalMeta, FromAgentInput, MessageOption } from "./types.js";
3
+ export declare class BgosOutbound {
4
+ /** Public read-only handle on the underlying REST client; consumers
5
+ * occasionally need it (e.g. inbound-handler routes uploadFile through
6
+ * publishMediaPath). */
7
+ readonly api: BgosApi;
8
+ private typingEmitter;
9
+ private onLastError;
10
+ private onOutbound;
11
+ /** Single-flight guard for replaySpool: concurrent callers (the 60s spool
12
+ * timer, onReconnect, recover) await the same in-flight run instead of each
13
+ * loading + re-sending the same spooled entries (double-send). */
14
+ private spoolInFlight;
15
+ private sleepFn;
16
+ constructor(api: BgosApi);
17
+ /** Wire the WS typing emitter (contract C4). Without it, sendTyping no-ops. */
18
+ setTypingEmitter(fn: (p: {
19
+ chatId: number;
20
+ assistantId: number;
21
+ }) => void): void;
22
+ /** Report an outbound failure to the heartbeat lastError channel. */
23
+ setLastErrorReporter(fn: (code: string, message: string) => void): void;
24
+ /** Called on every successful send (drives heartbeat lastOutboundAt). */
25
+ setOutboundReporter(fn: () => void): void;
26
+ /** Test seam: override the retry sleep so specs don't wait real seconds. */
27
+ setSleepFn(fn: (ms: number) => Promise<void>): void;
28
+ private rawSend;
29
+ /**
30
+ * Pick the reply endpoint + apply the outbound retry policy (contract C3).
31
+ *
32
+ * Default `messages` (POST /api/v1/messages) is the historical path used for
33
+ * every normal reply, proactive send, and `/board` fan-out. `send-message`
34
+ * (POST /api/v1/send-message) is used ONLY for a2a peer replies.
35
+ *
36
+ * Retry (backoff 1s/5s/25s) applies ONLY to the provably-undelivered
37
+ * network-error class + 429 (see classifyOutboundError), because the backend
38
+ * insert is not idempotent. Ambiguous failures (timeouts, 5xx) are NOT
39
+ * retried and reject to the caller. After 3 failed retries of the safe class
40
+ * the payload is spooled to the outbox for later replay.
41
+ */
42
+ private deliver;
43
+ /**
44
+ * Replay any spooled outbound payloads (contract C3). Called by the adapter
45
+ * on WS reconnect and every 60s while non-empty. Entries older than 24h are
46
+ * dropped on load; safe-class failures are re-kept, ambiguous failures on
47
+ * replay are dropped to avoid a non-idempotent duplicate.
48
+ *
49
+ * Single-flight: concurrent callers (the 60s spool timer racing an
50
+ * onReconnect racing a recover, or WS flapping spawning overlapping
51
+ * onReconnects) coalesce onto one in-flight run. Two overlapping runs would
52
+ * otherwise both load the SAME entries and each re-send them, and the backend
53
+ * insert is NOT idempotent, so that produces duplicate agent messages.
54
+ */
55
+ replaySpool(): Promise<void>;
56
+ private runReplaySpool;
57
+ sendText(params: {
58
+ assistantId: number;
59
+ chatId: number;
60
+ text: string;
61
+ fromAgent?: FromAgentInput;
62
+ /** Anchor this reply to an earlier message - BGOS renders a slim
63
+ * Telegram-style quoted header inside the bubble (tap → jump to
64
+ * source) and persists a frozen text/sender snapshot. Use when
65
+ * answering a stale question, following up on a past commitment,
66
+ * or surfacing a cron-triggered nudge tied to an older message.
67
+ * Same-chat constraint enforced server-side (400 otherwise). See
68
+ * bgos-agent-capabilities.md §9. */
69
+ replyToId?: number;
70
+ /** Route via `/send-message` instead of `/messages`. Set only for a2a
71
+ * peer replies so the backend's peer-reply bridge resolves the
72
+ * initiator's `wait_for_reply`. Defaults to `/messages` (unchanged). */
73
+ replyVia?: "messages" | "send-message";
74
+ }): Promise<{
75
+ id: number;
76
+ }>;
77
+ /**
78
+ * Send a text message stamped with an inline-agent identity.
79
+ *
80
+ * Each call produces a distinct bubble in the BGOS UI labeled with the
81
+ * supplied name / color / avatar - used by Codex's `/board` so the
82
+ * Research, Finance, Strategy, etc. agents each render as their own
83
+ * sender even though they share one bound assistant.
84
+ *
85
+ * The backend's hybrid resolver tries `peerId` (registry id) →
86
+ * `assistantId` (cross-assistant peer) → `externalId` (string lookup),
87
+ * falling back to inline `{name,color,avatarUrl}` when no peer is found.
88
+ * For Codex, pass `name` + `color` (and optionally `avatarUrl`) - the
89
+ * inline path is what we need.
90
+ */
91
+ sendAsAgent(params: {
92
+ assistantId: number;
93
+ chatId: number;
94
+ text: string;
95
+ agent: FromAgentInput;
96
+ options?: MessageOption[];
97
+ }): Promise<{
98
+ id: number;
99
+ }>;
100
+ sendButtons(params: {
101
+ assistantId: number;
102
+ chatId: number;
103
+ text: string;
104
+ options: MessageOption[];
105
+ /** See sendText.replyToId. */
106
+ replyToId?: number;
107
+ /** See sendText.replyVia. */
108
+ replyVia?: "messages" | "send-message";
109
+ }): Promise<{
110
+ id: number;
111
+ }>;
112
+ /**
113
+ * Render an approval request bubble.
114
+ *
115
+ * Callback layout: `ea:<decision>:<reqId>` matching Telegram parity
116
+ * (Hermes uses the same prefix). Decisions: `once|session|always|deny`.
117
+ *
118
+ * Default options cover the standard 4-button bubble. Pass `options`
119
+ * explicitly to override (e.g. for a smaller agreement flow).
120
+ */
121
+ sendApprovalRequest(params: {
122
+ assistantId: number;
123
+ chatId: number;
124
+ text: string;
125
+ meta: ApprovalMeta;
126
+ options?: MessageOption[];
127
+ /** See sendText.replyToId - anchor an approval bubble to the user's
128
+ * earlier request the agent is now asking permission to act on. */
129
+ replyToId?: number;
130
+ /** See sendText.replyVia. */
131
+ replyVia?: "messages" | "send-message";
132
+ }): Promise<{
133
+ id: number;
134
+ }>;
135
+ /**
136
+ * Send an `ask_user_input` prompt.
137
+ *
138
+ * `modal=true` (BGOS default for `ask_user_input`) renders as a
139
+ * pop-over sheet. `modal=false` renders as inline buttons that don't
140
+ * intrude. Use modal only when the user just messaged (~2 min) - see
141
+ * agent hints + the canonical capability doc.
142
+ *
143
+ * Today the backend's `messageType: 'ask_user_input'` carries `options`
144
+ * directly. If the canonical capability doc later splits askId/askOrder
145
+ * out, this method gains those args - for now we keep the surface
146
+ * minimal.
147
+ */
148
+ sendAskUserInput(params: {
149
+ assistantId: number;
150
+ chatId: number;
151
+ prompt: string;
152
+ options?: MessageOption[];
153
+ modal?: boolean;
154
+ }): Promise<{
155
+ id: number;
156
+ }>;
157
+ /** Send a file (image, video, document, audio) via the attachment-bridge. */
158
+ sendFile(params: {
159
+ assistantId: number;
160
+ chatId: number;
161
+ filePath: string;
162
+ caption?: string;
163
+ fileName?: string;
164
+ mimeType?: string;
165
+ /** See sendText.replyToId. */
166
+ replyToId?: number;
167
+ /** See sendText.replyVia. */
168
+ replyVia?: "messages" | "send-message";
169
+ }): Promise<{
170
+ id: number;
171
+ }>;
172
+ /** Image - enforces image/* MIME if provided; otherwise lets MIME
173
+ * inference handle it. */
174
+ sendImage(params: {
175
+ assistantId: number;
176
+ chatId: number;
177
+ filePath: string;
178
+ caption?: string;
179
+ fileName?: string;
180
+ mimeType?: string;
181
+ /** See sendText.replyToId. */
182
+ replyToId?: number;
183
+ /** See sendText.replyVia. */
184
+ replyVia?: "messages" | "send-message";
185
+ }): Promise<{
186
+ id: number;
187
+ }>;
188
+ /** Video - enforces video/* MIME if provided. */
189
+ sendVideo(params: {
190
+ assistantId: number;
191
+ chatId: number;
192
+ filePath: string;
193
+ caption?: string;
194
+ fileName?: string;
195
+ mimeType?: string;
196
+ /** See sendText.replyToId. */
197
+ replyToId?: number;
198
+ /** See sendText.replyVia. */
199
+ replyVia?: "messages" | "send-message";
200
+ }): Promise<{
201
+ id: number;
202
+ }>;
203
+ sendAgentError(params: {
204
+ assistantId: number;
205
+ chatId: number;
206
+ reason: string;
207
+ }): Promise<{
208
+ id: number;
209
+ }>;
210
+ /**
211
+ * Send a "typing..." indicator (contract C4). Emits the client-side WS
212
+ * `typing` event via the wired emitter (throttled 1/3s per chat inside
213
+ * BgosWs). Best-effort: no emitter wired (or an emit failure) resolves
214
+ * silently so a reply path is never broken over a typing indicator.
215
+ */
216
+ sendTyping(params: {
217
+ assistantId: number;
218
+ chatId: number;
219
+ }): Promise<void>;
220
+ }
221
+ /** Backwards-compatible alias retained for the public API surface. */
222
+ export { BgosOutbound as BGOSOutbound };
223
+ //# sourceMappingURL=outbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.d.ts","sourceRoot":"","sources":["../src/outbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAa7C,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EAEd,MAAM,YAAY,CAAC;AA8BpB,qBAAa,YAAY;IACvB;;6BAEyB;IACzB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IAEtB,OAAO,CAAC,aAAa,CAEL;IAChB,OAAO,CAAC,WAAW,CAA0D;IAC7E,OAAO,CAAC,UAAU,CAA6B;IAC/C;;uEAEmE;IACnE,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,OAAO,CACyB;gBAE5B,GAAG,EAAE,OAAO;IAIxB,+EAA+E;IAC/E,gBAAgB,CACd,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvD,IAAI;IAIP,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIvE,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,4EAA4E;IAC5E,UAAU,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAInD,OAAO,CAAC,OAAO;IASf;;;;;;;;;;;;OAYG;YACW,OAAO;IAiCrB;;;;;;;;;;;OAWG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;YAQd,cAAc;IAyB5B,QAAQ,CAAC,MAAM,EAAE;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,cAAc,CAAC;QAC3B;;;;;;6CAMqC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB;;iFAEyE;QACzE,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACxC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAc3B;;;;;;;;;;;;;OAaG;IACH,WAAW,CAAC,MAAM,EAAE;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,cAAc,CAAC;QACtB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;KAC3B,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAsB3B,WAAW,CAAC,MAAM,EAAE;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,aAAa,EAAE,CAAC;QACzB,8BAA8B;QAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,6BAA6B;QAC7B,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACxC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IA0B3B;;;;;;;;OAQG;IACH,mBAAmB,CAAC,MAAM,EAAE;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,YAAY,CAAC;QACnB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;QAC1B;4EACoE;QACpE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,6BAA6B;QAC7B,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACxC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAqC3B;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,MAAM,EAAE;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;QAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IA6B3B,6EAA6E;IACvE,QAAQ,CAAC,MAAM,EAAE;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,8BAA8B;QAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,6BAA6B;QAC7B,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACxC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAiB3B;+BAC2B;IAC3B,SAAS,CAAC,MAAM,EAAE;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,8BAA8B;QAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,6BAA6B;QAC7B,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACxC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAS3B,iDAAiD;IACjD,SAAS,CAAC,MAAM,EAAE;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,8BAA8B;QAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,6BAA6B;QAC7B,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACxC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAa3B,cAAc,CAAC,MAAM,EAAE;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAW3B;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,IAAI,CAAC;CAQlB;AAED,sEAAsE;AACtE,OAAO,EAAE,YAAY,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,366 @@
1
+ import { publishMediaPath } from "./attachment-bridge.js";
2
+ import { sanitizeFromAgent } from "./agent-identity.js";
3
+ import { classifyOutboundError, OUTBOUND_BACKOFFS_MS, } from "./outbound-retry.js";
4
+ import { appendOutbox, loadOutbox, rewriteOutbox, } from "./outbox.js";
5
+ /**
6
+ * High-level outbound adapter for the Codex channel.
7
+ *
8
+ * Wraps `BgosApi.postMessage` with the modality-specific helpers
9
+ * `BGOSAdapter` + the fork's reply-handle expose:
10
+ * - `sendText` plain assistant reply
11
+ * - `sendButtons` text + ≤6 inline option chips
12
+ * - `sendApprovalRequest` approval bubble (`ea:<decision>:<reqId>`)
13
+ * - `sendAskUserInput` blocking pop-under (modal or inline)
14
+ * - `sendFile/Image/Video` attach a local file via attachment-bridge
15
+ * - `sendAgentError` styled error bubble used on dispatch failure
16
+ * - `sendTyping` best-effort indicator (no-op on backend today)
17
+ *
18
+ * Validation is light - the backend does the authoritative checks. We
19
+ * enforce limits client-side only when the failure mode is silent (e.g.
20
+ * options>6 truncates, not 400; message-type mismatches sometimes
21
+ * succeed but render wrong). Limits we enforce:
22
+ * - inline buttons: max 6 options (matches PR #62 backend limit + the
23
+ * `inline_buttons_shipped` memory).
24
+ *
25
+ * `BgosOutbound` is the canonical export used everywhere; `BGOSOutbound`
26
+ * is kept as an alias for the brief moment in PR #82 where the scaffold
27
+ * named it that way (so existing imports don't break).
28
+ */
29
+ /** Backend rejects inline messages with >6 options. */
30
+ const INLINE_OPTION_LIMIT = 6;
31
+ export class BgosOutbound {
32
+ /** Public read-only handle on the underlying REST client; consumers
33
+ * occasionally need it (e.g. inbound-handler routes uploadFile through
34
+ * publishMediaPath). */
35
+ api;
36
+ typingEmitter = null;
37
+ onLastError = null;
38
+ onOutbound = null;
39
+ /** Single-flight guard for replaySpool: concurrent callers (the 60s spool
40
+ * timer, onReconnect, recover) await the same in-flight run instead of each
41
+ * loading + re-sending the same spooled entries (double-send). */
42
+ spoolInFlight = null;
43
+ sleepFn = (ms) => new Promise((r) => setTimeout(r, ms));
44
+ constructor(api) {
45
+ this.api = api;
46
+ }
47
+ /** Wire the WS typing emitter (contract C4). Without it, sendTyping no-ops. */
48
+ setTypingEmitter(fn) {
49
+ this.typingEmitter = fn;
50
+ }
51
+ /** Report an outbound failure to the heartbeat lastError channel. */
52
+ setLastErrorReporter(fn) {
53
+ this.onLastError = fn;
54
+ }
55
+ /** Called on every successful send (drives heartbeat lastOutboundAt). */
56
+ setOutboundReporter(fn) {
57
+ this.onOutbound = fn;
58
+ }
59
+ /** Test seam: override the retry sleep so specs don't wait real seconds. */
60
+ setSleepFn(fn) {
61
+ this.sleepFn = fn;
62
+ }
63
+ rawSend(payload, replyVia) {
64
+ return replyVia === "send-message"
65
+ ? this.api.sendMessage(payload)
66
+ : this.api.postMessage(payload);
67
+ }
68
+ /**
69
+ * Pick the reply endpoint + apply the outbound retry policy (contract C3).
70
+ *
71
+ * Default `messages` (POST /api/v1/messages) is the historical path used for
72
+ * every normal reply, proactive send, and `/board` fan-out. `send-message`
73
+ * (POST /api/v1/send-message) is used ONLY for a2a peer replies.
74
+ *
75
+ * Retry (backoff 1s/5s/25s) applies ONLY to the provably-undelivered
76
+ * network-error class + 429 (see classifyOutboundError), because the backend
77
+ * insert is not idempotent. Ambiguous failures (timeouts, 5xx) are NOT
78
+ * retried and reject to the caller. After 3 failed retries of the safe class
79
+ * the payload is spooled to the outbox for later replay.
80
+ */
81
+ async deliver(payload, replyVia) {
82
+ let attempt = 0;
83
+ for (;;) {
84
+ try {
85
+ const r = await this.rawSend(payload, replyVia);
86
+ this.onOutbound?.();
87
+ return r;
88
+ }
89
+ catch (err) {
90
+ const cls = classifyOutboundError(err);
91
+ if (cls.retriable && attempt < OUTBOUND_BACKOFFS_MS.length) {
92
+ const wait = cls.retryAfterMs ?? OUTBOUND_BACKOFFS_MS[attempt];
93
+ attempt += 1;
94
+ await this.sleepFn(wait);
95
+ continue;
96
+ }
97
+ const message = err instanceof Error ? err.message : String(err);
98
+ if (cls.retriable) {
99
+ // Exhausted the safe class: spool for later replay.
100
+ appendOutbox({
101
+ ts: Date.now(),
102
+ payload,
103
+ ...(replyVia ? { replyVia } : {}),
104
+ });
105
+ }
106
+ this.onLastError?.("outbound_failed", message);
107
+ throw err;
108
+ }
109
+ }
110
+ }
111
+ /**
112
+ * Replay any spooled outbound payloads (contract C3). Called by the adapter
113
+ * on WS reconnect and every 60s while non-empty. Entries older than 24h are
114
+ * dropped on load; safe-class failures are re-kept, ambiguous failures on
115
+ * replay are dropped to avoid a non-idempotent duplicate.
116
+ *
117
+ * Single-flight: concurrent callers (the 60s spool timer racing an
118
+ * onReconnect racing a recover, or WS flapping spawning overlapping
119
+ * onReconnects) coalesce onto one in-flight run. Two overlapping runs would
120
+ * otherwise both load the SAME entries and each re-send them, and the backend
121
+ * insert is NOT idempotent, so that produces duplicate agent messages.
122
+ */
123
+ replaySpool() {
124
+ if (this.spoolInFlight)
125
+ return this.spoolInFlight;
126
+ this.spoolInFlight = this.runReplaySpool().finally(() => {
127
+ this.spoolInFlight = null;
128
+ });
129
+ return this.spoolInFlight;
130
+ }
131
+ async runReplaySpool() {
132
+ const entries = loadOutbox();
133
+ if (entries.length === 0)
134
+ return;
135
+ // Claim the entries by clearing the spool up-front (atomic tmp + rename).
136
+ // At-most-once for the non-idempotent insert: a crash mid-replay cannot
137
+ // double-send on restart, and a concurrent send failure re-spools below.
138
+ // Failed safe-class entries are re-APPENDED (not rewritten) so a
139
+ // concurrent deliver() spool during replay is preserved.
140
+ rewriteOutbox([]);
141
+ for (const entry of entries) {
142
+ try {
143
+ await this.rawSend(entry.payload, entry.replyVia);
144
+ this.onOutbound?.();
145
+ }
146
+ catch (err) {
147
+ const cls = classifyOutboundError(err);
148
+ if (cls.retriable)
149
+ appendOutbox(entry);
150
+ // Ambiguous replay failures are dropped: the write may have applied.
151
+ }
152
+ }
153
+ }
154
+ // -------------------------------------------------------------------
155
+ // Text + buttons
156
+ // -------------------------------------------------------------------
157
+ sendText(params) {
158
+ const fromAgent = sanitizeFromAgent(params.fromAgent);
159
+ const payload = {
160
+ assistantId: params.assistantId,
161
+ chatId: params.chatId,
162
+ sender: "assistant",
163
+ text: params.text,
164
+ messageType: "standard",
165
+ ...(fromAgent ? { fromAgent } : {}),
166
+ ...(params.replyToId !== undefined && { replyToId: params.replyToId }),
167
+ };
168
+ return this.deliver(payload, params.replyVia);
169
+ }
170
+ /**
171
+ * Send a text message stamped with an inline-agent identity.
172
+ *
173
+ * Each call produces a distinct bubble in the BGOS UI labeled with the
174
+ * supplied name / color / avatar - used by Codex's `/board` so the
175
+ * Research, Finance, Strategy, etc. agents each render as their own
176
+ * sender even though they share one bound assistant.
177
+ *
178
+ * The backend's hybrid resolver tries `peerId` (registry id) →
179
+ * `assistantId` (cross-assistant peer) → `externalId` (string lookup),
180
+ * falling back to inline `{name,color,avatarUrl}` when no peer is found.
181
+ * For Codex, pass `name` + `color` (and optionally `avatarUrl`) - the
182
+ * inline path is what we need.
183
+ */
184
+ sendAsAgent(params) {
185
+ if (params.options !== undefined &&
186
+ params.options.length > INLINE_OPTION_LIMIT) {
187
+ throw new Error(`sendAsAgent: ${params.options.length} options exceeds inline limit (${INLINE_OPTION_LIMIT})`);
188
+ }
189
+ const fromAgent = sanitizeFromAgent(params.agent);
190
+ const payload = {
191
+ assistantId: params.assistantId,
192
+ chatId: params.chatId,
193
+ sender: "assistant",
194
+ text: params.text,
195
+ messageType: "standard",
196
+ ...(fromAgent ? { fromAgent } : {}),
197
+ ...(params.options ? { options: params.options } : {}),
198
+ };
199
+ return this.api.postMessage(payload);
200
+ }
201
+ sendButtons(params) {
202
+ if (params.options.length > INLINE_OPTION_LIMIT) {
203
+ // Throw rather than truncate - the agent should be told it sent
204
+ // too many options so it can re-emit. (Hermes truncates with a
205
+ // warning because the agent gets the buttons via a regex in its
206
+ // text; we have a typed call here so the contract is firmer.)
207
+ throw new Error(`sendButtons: ${params.options.length} options exceeds inline limit (${INLINE_OPTION_LIMIT})`);
208
+ }
209
+ const payload = {
210
+ assistantId: params.assistantId,
211
+ chatId: params.chatId,
212
+ sender: "assistant",
213
+ text: params.text,
214
+ options: params.options,
215
+ messageType: "standard",
216
+ ...(params.replyToId !== undefined && { replyToId: params.replyToId }),
217
+ };
218
+ return this.deliver(payload, params.replyVia);
219
+ }
220
+ // -------------------------------------------------------------------
221
+ // Approval bubbles
222
+ // -------------------------------------------------------------------
223
+ /**
224
+ * Render an approval request bubble.
225
+ *
226
+ * Callback layout: `ea:<decision>:<reqId>` matching Telegram parity
227
+ * (Hermes uses the same prefix). Decisions: `once|session|always|deny`.
228
+ *
229
+ * Default options cover the standard 4-button bubble. Pass `options`
230
+ * explicitly to override (e.g. for a smaller agreement flow).
231
+ */
232
+ sendApprovalRequest(params) {
233
+ const reqId = params.meta.request_id;
234
+ const defaults = [
235
+ {
236
+ text: "Allow once",
237
+ callbackData: `ea:once:${reqId}`,
238
+ style: "success",
239
+ },
240
+ {
241
+ text: "Allow for session",
242
+ callbackData: `ea:session:${reqId}`,
243
+ style: "success",
244
+ },
245
+ {
246
+ text: "Always allow",
247
+ callbackData: `ea:always:${reqId}`,
248
+ style: "default",
249
+ },
250
+ { text: "Deny", callbackData: `ea:deny:${reqId}`, style: "danger" },
251
+ ];
252
+ const payload = {
253
+ assistantId: params.assistantId,
254
+ chatId: params.chatId,
255
+ sender: "assistant",
256
+ text: params.text,
257
+ options: params.options ?? defaults,
258
+ messageType: "approval_request",
259
+ approvalMeta: params.meta,
260
+ ...(params.replyToId !== undefined && { replyToId: params.replyToId }),
261
+ };
262
+ return this.deliver(payload, params.replyVia);
263
+ }
264
+ // -------------------------------------------------------------------
265
+ // ask_user_input pop-under (modal or inline)
266
+ // -------------------------------------------------------------------
267
+ /**
268
+ * Send an `ask_user_input` prompt.
269
+ *
270
+ * `modal=true` (BGOS default for `ask_user_input`) renders as a
271
+ * pop-over sheet. `modal=false` renders as inline buttons that don't
272
+ * intrude. Use modal only when the user just messaged (~2 min) - see
273
+ * agent hints + the canonical capability doc.
274
+ *
275
+ * Today the backend's `messageType: 'ask_user_input'` carries `options`
276
+ * directly. If the canonical capability doc later splits askId/askOrder
277
+ * out, this method gains those args - for now we keep the surface
278
+ * minimal.
279
+ */
280
+ sendAskUserInput(params) {
281
+ if (params.options &&
282
+ params.options.length > INLINE_OPTION_LIMIT &&
283
+ params.modal === false) {
284
+ throw new Error(`sendAskUserInput (inline): ${params.options.length} options exceeds inline limit (${INLINE_OPTION_LIMIT})`);
285
+ }
286
+ const payload = {
287
+ assistantId: params.assistantId,
288
+ chatId: params.chatId,
289
+ sender: "assistant",
290
+ text: params.prompt,
291
+ // The backend's messageType union doesn't include "ask_user_input"
292
+ // in the OutboundMessagePayload type today - but the wire format
293
+ // accepts it (the canonical capability doc documents it). Cast to
294
+ // sidestep the union, mirroring how Hermes handles this.
295
+ messageType: "ask_user_input",
296
+ options: params.options,
297
+ };
298
+ return this.api.postMessage(payload);
299
+ }
300
+ // -------------------------------------------------------------------
301
+ // Files / media
302
+ // -------------------------------------------------------------------
303
+ /** Send a file (image, video, document, audio) via the attachment-bridge. */
304
+ async sendFile(params) {
305
+ const fileRef = await publishMediaPath(this.api, params.filePath, {
306
+ fileName: params.fileName,
307
+ mimeType: params.mimeType,
308
+ });
309
+ const payload = {
310
+ assistantId: params.assistantId,
311
+ chatId: params.chatId,
312
+ sender: "assistant",
313
+ text: params.caption ?? "",
314
+ messageType: "standard",
315
+ files: [fileRef],
316
+ ...(params.replyToId !== undefined && { replyToId: params.replyToId }),
317
+ };
318
+ return this.deliver(payload, params.replyVia);
319
+ }
320
+ /** Image - enforces image/* MIME if provided; otherwise lets MIME
321
+ * inference handle it. */
322
+ sendImage(params) {
323
+ if (params.mimeType && !params.mimeType.startsWith("image/")) {
324
+ throw new Error(`sendImage: mimeType ${params.mimeType} is not image/*`);
325
+ }
326
+ return this.sendFile(params);
327
+ }
328
+ /** Video - enforces video/* MIME if provided. */
329
+ sendVideo(params) {
330
+ if (params.mimeType && !params.mimeType.startsWith("video/")) {
331
+ throw new Error(`sendVideo: mimeType ${params.mimeType} is not video/*`);
332
+ }
333
+ return this.sendFile(params);
334
+ }
335
+ // -------------------------------------------------------------------
336
+ // Errors + typing
337
+ // -------------------------------------------------------------------
338
+ sendAgentError(params) {
339
+ const payload = {
340
+ assistantId: params.assistantId,
341
+ chatId: params.chatId,
342
+ sender: "assistant",
343
+ text: `⚠ Agent error: ${params.reason}`,
344
+ messageType: "agent_error",
345
+ };
346
+ return this.api.postMessage(payload);
347
+ }
348
+ /**
349
+ * Send a "typing..." indicator (contract C4). Emits the client-side WS
350
+ * `typing` event via the wired emitter (throttled 1/3s per chat inside
351
+ * BgosWs). Best-effort: no emitter wired (or an emit failure) resolves
352
+ * silently so a reply path is never broken over a typing indicator.
353
+ */
354
+ async sendTyping(params) {
355
+ try {
356
+ this.typingEmitter?.(params);
357
+ }
358
+ catch {
359
+ /* best-effort */
360
+ }
361
+ return;
362
+ }
363
+ }
364
+ /** Backwards-compatible alias retained for the public API surface. */
365
+ export { BgosOutbound as BGOSOutbound };
366
+ //# sourceMappingURL=outbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.js","sourceRoot":"","sources":["../src/outbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { OutboundMessagePayload } from "./types.js";
2
+ export interface OutboxEntry {
3
+ /** Epoch ms when the entry was spooled. */
4
+ ts: number;
5
+ payload: OutboundMessagePayload;
6
+ replyVia?: "messages" | "send-message";
7
+ }
8
+ /** Read the spool, dropping entries older than 24h. Never throws. */
9
+ export declare function loadOutbox(): OutboxEntry[];
10
+ /** Append one entry, then compact to the newest MAX_ENTRIES. Never throws. */
11
+ export declare function appendOutbox(entry: OutboxEntry): void;
12
+ /** Atomically replace the spool with `entries` (tmp + rename). Never throws. */
13
+ export declare function rewriteOutbox(entries: OutboxEntry[]): void;
14
+ /** Empty the spool. Never throws. */
15
+ export declare function clearOutbox(): void;
16
+ //# sourceMappingURL=outbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,sBAAsB,CAAC;IAChC,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;CACxC;AAUD,qEAAqE;AACrE,wBAAgB,UAAU,IAAI,WAAW,EAAE,CA2B1C;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAarD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,CAW1D;AAED,qCAAqC;AACrC,wBAAgB,WAAW,IAAI,IAAI,CAElC"}