@krischoichoi/channel-harness 0.6.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 (202) hide show
  1. package/README.md +110 -0
  2. package/lib/access/controller.d.ts +37 -0
  3. package/lib/access/controller.d.ts.map +1 -0
  4. package/lib/access/controller.js +51 -0
  5. package/lib/access/controller.js.map +1 -0
  6. package/lib/access/decision.d.ts +18 -0
  7. package/lib/access/decision.d.ts.map +1 -0
  8. package/lib/access/decision.js +2 -0
  9. package/lib/access/decision.js.map +1 -0
  10. package/lib/access/resolver.d.ts +41 -0
  11. package/lib/access/resolver.d.ts.map +1 -0
  12. package/lib/access/resolver.js +31 -0
  13. package/lib/access/resolver.js.map +1 -0
  14. package/lib/agent-manager.d.ts +314 -0
  15. package/lib/agent-manager.d.ts.map +1 -0
  16. package/lib/agent-manager.js +603 -0
  17. package/lib/agent-manager.js.map +1 -0
  18. package/lib/agent-router.d.ts +37 -0
  19. package/lib/agent-router.d.ts.map +1 -0
  20. package/lib/agent-router.js +33 -0
  21. package/lib/agent-router.js.map +1 -0
  22. package/lib/bind-support.d.ts +23 -0
  23. package/lib/bind-support.d.ts.map +1 -0
  24. package/lib/bind-support.js +63 -0
  25. package/lib/bind-support.js.map +1 -0
  26. package/lib/binding-store.d.ts +104 -0
  27. package/lib/binding-store.d.ts.map +1 -0
  28. package/lib/binding-store.js +354 -0
  29. package/lib/binding-store.js.map +1 -0
  30. package/lib/bridge.d.ts +248 -0
  31. package/lib/bridge.d.ts.map +1 -0
  32. package/lib/bridge.js +961 -0
  33. package/lib/bridge.js.map +1 -0
  34. package/lib/channel-label.d.ts +46 -0
  35. package/lib/channel-label.d.ts.map +1 -0
  36. package/lib/channel-label.js +89 -0
  37. package/lib/channel-label.js.map +1 -0
  38. package/lib/channel-session-factory.d.ts +76 -0
  39. package/lib/channel-session-factory.d.ts.map +1 -0
  40. package/lib/channel-session-factory.js +187 -0
  41. package/lib/channel-session-factory.js.map +1 -0
  42. package/lib/commands/bind.d.ts +21 -0
  43. package/lib/commands/bind.d.ts.map +1 -0
  44. package/lib/commands/bind.js +91 -0
  45. package/lib/commands/bind.js.map +1 -0
  46. package/lib/commands/help.d.ts +15 -0
  47. package/lib/commands/help.d.ts.map +1 -0
  48. package/lib/commands/help.js +89 -0
  49. package/lib/commands/help.js.map +1 -0
  50. package/lib/commands/index.d.ts +122 -0
  51. package/lib/commands/index.d.ts.map +1 -0
  52. package/lib/commands/index.js +63 -0
  53. package/lib/commands/index.js.map +1 -0
  54. package/lib/commands/locale.d.ts +9 -0
  55. package/lib/commands/locale.d.ts.map +1 -0
  56. package/lib/commands/locale.js +27 -0
  57. package/lib/commands/locale.js.map +1 -0
  58. package/lib/commands/mirror.d.ts +14 -0
  59. package/lib/commands/mirror.d.ts.map +1 -0
  60. package/lib/commands/mirror.js +44 -0
  61. package/lib/commands/mirror.js.map +1 -0
  62. package/lib/commands/model.d.ts +20 -0
  63. package/lib/commands/model.d.ts.map +1 -0
  64. package/lib/commands/model.js +106 -0
  65. package/lib/commands/model.js.map +1 -0
  66. package/lib/commands/models.d.ts +14 -0
  67. package/lib/commands/models.d.ts.map +1 -0
  68. package/lib/commands/models.js +67 -0
  69. package/lib/commands/models.js.map +1 -0
  70. package/lib/commands/new.d.ts +14 -0
  71. package/lib/commands/new.d.ts.map +1 -0
  72. package/lib/commands/new.js +37 -0
  73. package/lib/commands/new.js.map +1 -0
  74. package/lib/commands/status.d.ts +12 -0
  75. package/lib/commands/status.d.ts.map +1 -0
  76. package/lib/commands/status.js +30 -0
  77. package/lib/commands/status.js.map +1 -0
  78. package/lib/commands/stop.d.ts +16 -0
  79. package/lib/commands/stop.d.ts.map +1 -0
  80. package/lib/commands/stop.js +27 -0
  81. package/lib/commands/stop.js.map +1 -0
  82. package/lib/commands/version.d.ts +25 -0
  83. package/lib/commands/version.d.ts.map +1 -0
  84. package/lib/commands/version.js +57 -0
  85. package/lib/commands/version.js.map +1 -0
  86. package/lib/config.d.ts +119 -0
  87. package/lib/config.d.ts.map +1 -0
  88. package/lib/config.js +60 -0
  89. package/lib/config.js.map +1 -0
  90. package/lib/debug-logger.d.ts +8 -0
  91. package/lib/debug-logger.d.ts.map +1 -0
  92. package/lib/debug-logger.js +32 -0
  93. package/lib/debug-logger.js.map +1 -0
  94. package/lib/dsh-home.d.ts +12 -0
  95. package/lib/dsh-home.d.ts.map +1 -0
  96. package/lib/dsh-home.js +31 -0
  97. package/lib/dsh-home.js.map +1 -0
  98. package/lib/failure-display.d.ts +10 -0
  99. package/lib/failure-display.d.ts.map +1 -0
  100. package/lib/failure-display.js +42 -0
  101. package/lib/failure-display.js.map +1 -0
  102. package/lib/file-provider.d.ts +96 -0
  103. package/lib/file-provider.d.ts.map +1 -0
  104. package/lib/file-provider.js +44 -0
  105. package/lib/file-provider.js.map +1 -0
  106. package/lib/index.d.ts +31 -0
  107. package/lib/index.d.ts.map +1 -0
  108. package/lib/index.js +31 -0
  109. package/lib/index.js.map +1 -0
  110. package/lib/interactions/index.d.ts +13 -0
  111. package/lib/interactions/index.d.ts.map +1 -0
  112. package/lib/interactions/index.js +13 -0
  113. package/lib/interactions/index.js.map +1 -0
  114. package/lib/interactions/question-backend.d.ts +133 -0
  115. package/lib/interactions/question-backend.d.ts.map +1 -0
  116. package/lib/interactions/question-backend.js +37 -0
  117. package/lib/interactions/question-backend.js.map +1 -0
  118. package/lib/interactions/question-presenter.d.ts +68 -0
  119. package/lib/interactions/question-presenter.d.ts.map +1 -0
  120. package/lib/interactions/question-presenter.js +425 -0
  121. package/lib/interactions/question-presenter.js.map +1 -0
  122. package/lib/interactions/question-renderer.d.ts +37 -0
  123. package/lib/interactions/question-renderer.d.ts.map +1 -0
  124. package/lib/interactions/question-renderer.js +79 -0
  125. package/lib/interactions/question-renderer.js.map +1 -0
  126. package/lib/interactions/question-state.d.ts +106 -0
  127. package/lib/interactions/question-state.d.ts.map +1 -0
  128. package/lib/interactions/question-state.js +99 -0
  129. package/lib/interactions/question-state.js.map +1 -0
  130. package/lib/interactions/question-text-answer.d.ts +37 -0
  131. package/lib/interactions/question-text-answer.d.ts.map +1 -0
  132. package/lib/interactions/question-text-answer.js +70 -0
  133. package/lib/interactions/question-text-answer.js.map +1 -0
  134. package/lib/interactions/question-waterfall-backend.d.ts +42 -0
  135. package/lib/interactions/question-waterfall-backend.d.ts.map +1 -0
  136. package/lib/interactions/question-waterfall-backend.js +162 -0
  137. package/lib/interactions/question-waterfall-backend.js.map +1 -0
  138. package/lib/lifecycle.d.ts +36 -0
  139. package/lib/lifecycle.d.ts.map +1 -0
  140. package/lib/lifecycle.js +303 -0
  141. package/lib/lifecycle.js.map +1 -0
  142. package/lib/loggable-error.d.ts +10 -0
  143. package/lib/loggable-error.d.ts.map +1 -0
  144. package/lib/loggable-error.js +32 -0
  145. package/lib/loggable-error.js.map +1 -0
  146. package/lib/message-converter.d.ts +64 -0
  147. package/lib/message-converter.d.ts.map +1 -0
  148. package/lib/message-converter.js +191 -0
  149. package/lib/message-converter.js.map +1 -0
  150. package/lib/model-selection.d.ts +54 -0
  151. package/lib/model-selection.d.ts.map +1 -0
  152. package/lib/model-selection.js +116 -0
  153. package/lib/model-selection.js.map +1 -0
  154. package/lib/outbox/binding-resolver.d.ts +23 -0
  155. package/lib/outbox/binding-resolver.d.ts.map +1 -0
  156. package/lib/outbox/binding-resolver.js +38 -0
  157. package/lib/outbox/binding-resolver.js.map +1 -0
  158. package/lib/outbox/capabilities.d.ts +48 -0
  159. package/lib/outbox/capabilities.d.ts.map +1 -0
  160. package/lib/outbox/capabilities.js +35 -0
  161. package/lib/outbox/capabilities.js.map +1 -0
  162. package/lib/outbox/index.d.ts +14 -0
  163. package/lib/outbox/index.d.ts.map +1 -0
  164. package/lib/outbox/index.js +14 -0
  165. package/lib/outbox/index.js.map +1 -0
  166. package/lib/outbox/service.d.ts +48 -0
  167. package/lib/outbox/service.d.ts.map +1 -0
  168. package/lib/outbox/service.js +74 -0
  169. package/lib/outbox/service.js.map +1 -0
  170. package/lib/outbox/target.d.ts +17 -0
  171. package/lib/outbox/target.d.ts.map +1 -0
  172. package/lib/outbox/target.js +15 -0
  173. package/lib/outbox/target.js.map +1 -0
  174. package/lib/outbox/tool-send.d.ts +19 -0
  175. package/lib/outbox/tool-send.d.ts.map +1 -0
  176. package/lib/outbox/tool-send.js +77 -0
  177. package/lib/outbox/tool-send.js.map +1 -0
  178. package/lib/outbox/types.d.ts +42 -0
  179. package/lib/outbox/types.d.ts.map +1 -0
  180. package/lib/outbox/types.js +30 -0
  181. package/lib/outbox/types.js.map +1 -0
  182. package/lib/plugin.d.ts +27 -0
  183. package/lib/plugin.d.ts.map +1 -0
  184. package/lib/plugin.js +47 -0
  185. package/lib/plugin.js.map +1 -0
  186. package/lib/reply-context-store.d.ts +69 -0
  187. package/lib/reply-context-store.d.ts.map +1 -0
  188. package/lib/reply-context-store.js +75 -0
  189. package/lib/reply-context-store.js.map +1 -0
  190. package/lib/reply-router.d.ts +138 -0
  191. package/lib/reply-router.d.ts.map +1 -0
  192. package/lib/reply-router.js +674 -0
  193. package/lib/reply-router.js.map +1 -0
  194. package/lib/session-router.d.ts +49 -0
  195. package/lib/session-router.d.ts.map +1 -0
  196. package/lib/session-router.js +45 -0
  197. package/lib/session-router.js.map +1 -0
  198. package/lib/workspace-resolver.d.ts +67 -0
  199. package/lib/workspace-resolver.d.ts.map +1 -0
  200. package/lib/workspace-resolver.js +102 -0
  201. package/lib/workspace-resolver.js.map +1 -0
  202. package/package.json +103 -0
@@ -0,0 +1,303 @@
1
+ import { SessionId } from '@deepseek-ai/dsh-session';
2
+ import { createBindingStore } from './binding-store.js';
3
+ import { AgentManager, HarnessAgentGateway, resolvePersistedInspection } from './agent-manager.js';
4
+ import { confirmBindTarget, resolveBindTarget } from './bind-support.js';
5
+ import { AgentRouter } from './agent-router.js';
6
+ import { ReplyRouter } from './reply-router.js';
7
+ import { ChannelHarnessBridge } from './bridge.js';
8
+ import { ChannelOutboxService } from './outbox/service.js';
9
+ import { OutboxError } from './outbox/types.js';
10
+ import { HarnessChannelWorkspaceResolver } from './workspace-resolver.js';
11
+ import { ReplyContextStore } from './reply-context-store.js';
12
+ import { installDebugConsoleExporter } from './debug-logger.js';
13
+ import { StoredChannelAccessPolicyResolver } from './access/resolver.js';
14
+ import { liveAttachmentProvider } from './file-provider.js';
15
+ import { createQuestionInteraction } from './interactions/question-backend.js';
16
+ export function startBridge(ctx, config, resolvePersistence) {
17
+ installDebugConsoleExporter(ctx);
18
+ const logger = ctx.logger('channel-harness');
19
+ const channels = ctx.get('channels');
20
+ const bindingStore = createBindingStore(config.bindingStore);
21
+ const agentGateway = new HarnessAgentGateway(ctx, resolvePersistence);
22
+ const agentManager = new AgentManager(agentGateway, logger, config.maxConcurrency);
23
+ const agentRouter = new AgentRouter(config);
24
+ const workspaceResolver = new HarnessChannelWorkspaceResolver(ctx, config.workspace, logger);
25
+ const getAdapter = (channelId) => channels.get(channelId);
26
+ const replyContexts = new ReplyContextStore();
27
+ // Question answerer composition: the channel composes ONE PREPENDED answerer
28
+ // on the official `user-questions/request` waterfall — headless it is
29
+ // typically the only answerer; in the Web profile it must win over the
30
+ // official Remote/Web answerer (which registers at boot, before this bridge)
31
+ // or every channel-bound ask would be swallowed by the browser. A declined
32
+ // presentation still delegates via `next()`. Everything lives in
33
+ // interactions/question-backend.ts.
34
+ const questionPresenter = config.userQuestions.enabled
35
+ ? createQuestionInteraction({
36
+ ctx,
37
+ getUserQuestions: () => ctx.get('userQuestions'),
38
+ agentManager,
39
+ replyContexts,
40
+ getAdapter,
41
+ logger,
42
+ timeoutMs: config.userQuestions.timeoutMs,
43
+ })
44
+ : undefined;
45
+ questionPresenter?.start();
46
+ // Optional attachment service -> real image path (WX5). Resolved LIVE at
47
+ // every use, exactly like `sessionPersistence` above: the profile entry group
48
+ // creates every row concurrently (`Promise.allSettled`), so a startup
49
+ // snapshot can precede the service's own fiber. A deployment without an
50
+ // attachment backend keeps today's fallback — the hook throws, `imageBlock`
51
+ // catches, and the converter emits the deterministic text placeholder.
52
+ const saveImage = async (input) => {
53
+ const attachments = ctx.get('attachments');
54
+ if (!attachments) {
55
+ throw new Error('the Harness attachments service is not mounted');
56
+ }
57
+ return attachments.saveImage(input);
58
+ };
59
+ // Optional generic-file extension. Harness currently has a native image
60
+ // service but no generic FileBlock/FileAttachment surface, so deployments
61
+ // may provide this separately without coupling document parsers to the
62
+ // bridge. The provider is resolved LIVE per call (see
63
+ // `liveAttachmentProvider`): the bundle's `channels-files` row loads
64
+ // concurrently with this plugin, and deleting that row must stay supported.
65
+ const resolveFileProvider = () => ctx.get('channelFiles');
66
+ const fileProvider = liveAttachmentProvider(resolveFileProvider);
67
+ // Best-effort typing indicator wiring: a typing API failure must NEVER break
68
+ // the inbound/outbound flow, so every call is fire-and-forget with a swallow.
69
+ const startTyping = (sessionId, context) => {
70
+ const binding = agentManager.bindingFor(sessionId);
71
+ if (!binding)
72
+ return;
73
+ const adapter = getAdapter(binding.channelId);
74
+ if (adapter?.startTypingForTarget && context) {
75
+ void adapter.startTypingForTarget(typingTarget(binding, context)).catch(() => { });
76
+ return;
77
+ }
78
+ if (adapter?.startTyping)
79
+ void adapter.startTyping(binding.conversationId).catch(() => { });
80
+ };
81
+ const stopTyping = (sessionId, context) => {
82
+ const binding = agentManager.bindingFor(sessionId);
83
+ if (!binding)
84
+ return;
85
+ const adapter = getAdapter(binding.channelId);
86
+ if (adapter?.stopTypingForTarget && context) {
87
+ void adapter.stopTypingForTarget(typingTarget(binding, context)).catch(() => { });
88
+ return;
89
+ }
90
+ if (adapter?.stopTyping)
91
+ void adapter.stopTyping(binding.conversationId).catch(() => { });
92
+ };
93
+ ctx.on('agent/inbox/claimed', ({ agent, message, turn }) => {
94
+ const context = replyContexts.claim({
95
+ sessionId: String(agent.session.id),
96
+ messageId: String(message.id),
97
+ turn,
98
+ });
99
+ startTyping(String(agent.session.id), context);
100
+ });
101
+ ctx.on('agent/inbox/discarded', ({ message }) => {
102
+ // Resolve the session id BEFORE dropping the pending entry so we can
103
+ // cancel any typing the (never-claimed) inbound would have triggered.
104
+ const messageId = String(message.id);
105
+ const sessionId = replyContexts.pendingSessionId(messageId);
106
+ const context = replyContexts.pendingContext(messageId);
107
+ replyContexts.discard(messageId);
108
+ if (sessionId)
109
+ stopTyping(sessionId, context);
110
+ });
111
+ const replyRouter = new ReplyRouter({
112
+ config: config.reply,
113
+ getAdapter,
114
+ getBinding: (sessionId) => agentManager.bindingFor(sessionId),
115
+ replyContexts,
116
+ logger,
117
+ });
118
+ const stopListening = replyRouter.attach(ctx);
119
+ // Deferred bridge reference: `commandDeps` routes the /new command backed by
120
+ // the bridge back to the bridge's own fresh-session bootstrap. The arrow only
121
+ // calls `bridge.startNewSession` at runtime (when a command actually runs), by
122
+ // which point the bridge is assigned (definite-assignment assertion below).
123
+ // Durable outbox: the proactive send path. The
124
+ // binding authority is the DURABLE store (never AgentManager's hint cache),
125
+ // and the adapter lookup + asset store are the same ones the reply pipeline
126
+ // uses. Optional: absent -> the send_channel_message tool is not installed.
127
+ const outbox = new ChannelOutboxService({
128
+ bindingStore,
129
+ getAdapter,
130
+ // Live again: the resolver runs per send, so a provider mounted after the
131
+ // bridge started (or an HMR replacement) is picked up instead of leaving
132
+ // the outbox permanently without an attachment resolver (issue #7).
133
+ attachmentResolver: async (attachmentId, sessionId) => {
134
+ const provider = resolveFileProvider();
135
+ if (!provider) {
136
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', 'outbound attachments require a private asset store that is not configured', { sessionId });
137
+ }
138
+ return provider.resolveAttachment(attachmentId, sessionId);
139
+ },
140
+ logger,
141
+ });
142
+ let bridge;
143
+ const commandDeps = {
144
+ startNewSession: (agent) => bridge.startNewSession(agent),
145
+ // /mirror (issue #5): the toggle state lives on the durable binding.
146
+ mirror: {
147
+ get: async (agent) => (await bindingStore.findBySessionId(String(agent.id)))?.mirror === true,
148
+ set: async (agent, on) => {
149
+ const binding = await bindingStore.findBySessionId(String(agent.id));
150
+ if (!binding)
151
+ throw new Error(`no session binding for '${String(agent.id)}'`);
152
+ await bindingStore.put({ ...binding, mirror: on, updatedAt: Date.now() });
153
+ },
154
+ },
155
+ // /bind (issue #6): fail-closed rebind support over the durable bindings
156
+ // and the persisted session universe.
157
+ bind: {
158
+ resolve: (agent, query) => resolveBindTarget(agentGateway, bindingStore, agent, query),
159
+ confirm: (agent, sessionId) => confirmBindTarget(agentGateway, bindingStore, agent, sessionId),
160
+ },
161
+ };
162
+ // Fail-closed Access Gate: production ALWAYS resolves the
163
+ // policy from the shared ChannelStorage and logs decisions on the
164
+ // `channel-access` namespace. Reads once per inbound — no policy caching (§16).
165
+ const accessResolver = new StoredChannelAccessPolicyResolver(() => channels.resources.storage);
166
+ const accessLogger = ctx.logger('channel-access');
167
+ bridge = new ChannelHarnessBridge({
168
+ config,
169
+ bindingStore,
170
+ agentManager,
171
+ agentRouter,
172
+ getAdapter,
173
+ replyContexts,
174
+ logger,
175
+ accessResolver,
176
+ accessLogger,
177
+ saveImage,
178
+ fileProvider,
179
+ ctx,
180
+ commandDeps,
181
+ workspaceResolver,
182
+ outbox,
183
+ questionPresenter,
184
+ });
185
+ const stopInbound = channels.on((event) => bridge.handleChannelEvent(event));
186
+ let disposed = false;
187
+ async function dispose() {
188
+ if (disposed)
189
+ return;
190
+ disposed = true;
191
+ // 1. Stop new inbound (adapter events no longer reach the bridge).
192
+ stopInbound();
193
+ // Cancel channel-owned question waits before draining the blocked Agent.
194
+ await questionPresenter?.stop();
195
+ // Release this bridge's Agent-scoped commands before a replacement bridge
196
+ // can borrow the same live agents and install fresh handlers.
197
+ await bridge.disposeCommandSetups();
198
+ // 2. Drain active turns with a bounded wait (the session/event listener is
199
+ // still attached here, but we do NOT depend on it for final-reply
200
+ // correctness).
201
+ await drainActiveTurns(agentManager, replyRouter, logger, config.drainTimeoutMs);
202
+ // 3. RECONCILE replies from the Session durable log (final text delivery
203
+ // does NOT rely on the listener still being attached).
204
+ await reconcileReplies(ctx, resolvePersistence, agentManager, replyRouter, logger);
205
+ // 4. Finalize any replies still marked active whose turn/end never arrived.
206
+ await replyRouter.flushAll();
207
+ // 5. Stop listening to session/event.
208
+ stopListening();
209
+ // 6. Dispose owned agent handles (each exactly once).
210
+ await agentManager.disposeAll();
211
+ // 7. Dispose the reply router (clear timers).
212
+ replyRouter.dispose();
213
+ }
214
+ return {
215
+ dispose,
216
+ handleChannelEvent: (event) => bridge.handleChannelEvent(event),
217
+ };
218
+ }
219
+ function typingTarget(binding, context) {
220
+ return {
221
+ channelId: binding.channelId,
222
+ accountId: binding.accountId,
223
+ conversationId: binding.conversationId,
224
+ threadId: binding.threadId,
225
+ conversationType: context.conversationType,
226
+ replyToMessageId: context.replyToMessageId,
227
+ raw: context.raw,
228
+ runId: context.runId,
229
+ };
230
+ }
231
+ async function drainActiveTurns(agentManager, replyRouter, logger, drainTimeoutMs) {
232
+ const sessionIds = replyRouter.activeSessions();
233
+ if (sessionIds.length === 0)
234
+ return;
235
+ await Promise.all(sessionIds.map(async (sessionId) => {
236
+ const ref = agentManager.refFor(sessionId);
237
+ if (!ref)
238
+ return;
239
+ const timedOut = await withTimeout(ref.whenIdle(), drainTimeoutMs);
240
+ if (timedOut) {
241
+ logger.warn(`[channel-harness] drain timed out for session '${sessionId}'`);
242
+ }
243
+ }));
244
+ }
245
+ /**
246
+ * Reconcile every active session's reply from the durable log. Reads the live
247
+ * Session when it is still in the store; otherwise inspects persistence
248
+ * (resolved LIVE at dispose time — the currently mounted backend, whatever
249
+ * the bridge started with). Skips gracefully when neither is available.
250
+ * Final-reply correctness comes from the Session log, not from the
251
+ * still-attached event listener.
252
+ */
253
+ async function reconcileReplies(ctx, resolvePersistence, agentManager, replyRouter, logger) {
254
+ const sessionIds = agentManager.activeSessions();
255
+ if (sessionIds.length === 0)
256
+ return;
257
+ const sessions = ctx.get('sessions');
258
+ const persistence = resolvePersistence?.();
259
+ for (const sessionId of sessionIds) {
260
+ try {
261
+ const live = sessions?.get(SessionId(sessionId));
262
+ if (live) {
263
+ await replyRouter.reconcileSession({ id: sessionId, events: live.snapshotEvents() });
264
+ continue;
265
+ }
266
+ if (persistence) {
267
+ // V3 persistence inspection uses the public read handle.
268
+ const inspection = await resolvePersistedInspection(persistence, SessionId(sessionId));
269
+ if (inspection) {
270
+ await replyRouter.reconcileSession({ id: sessionId, events: inspection.events });
271
+ }
272
+ }
273
+ // else: no live session and no persistence — skip gracefully.
274
+ }
275
+ catch (error) {
276
+ logger.warn(`[channel-harness] reconcile failed for session '${sessionId}'`, error);
277
+ }
278
+ }
279
+ }
280
+ /** Resolve true when the timeout elapses before the promise settles. */
281
+ function withTimeout(promise, ms) {
282
+ return new Promise((resolve) => {
283
+ let settled = false;
284
+ const timer = setTimeout(() => {
285
+ if (!settled)
286
+ resolve(true);
287
+ }, ms);
288
+ promise.then(() => {
289
+ if (settled)
290
+ return;
291
+ settled = true;
292
+ clearTimeout(timer);
293
+ resolve(false);
294
+ }, () => {
295
+ if (settled)
296
+ return;
297
+ settled = true;
298
+ clearTimeout(timer);
299
+ resolve(false);
300
+ });
301
+ });
302
+ }
303
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** JSON-safe shape for errors passed through logger exporters. */
2
+ export interface LoggableError {
3
+ name: string;
4
+ message: string;
5
+ stack?: string;
6
+ cause?: LoggableError;
7
+ }
8
+ /** Preserve useful Error fields without relying on non-enumerable properties. */
9
+ export declare function toLoggableError(error: unknown): LoggableError;
10
+ //# sourceMappingURL=loggable-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loggable-error.d.ts","sourceRoot":"","sources":["../src/loggable-error.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAE7D"}
@@ -0,0 +1,32 @@
1
+ /** Preserve useful Error fields without relying on non-enumerable properties. */
2
+ export function toLoggableError(error) {
3
+ return normalizeError(error, new Set());
4
+ }
5
+ function normalizeError(error, seen) {
6
+ if (!(error instanceof Error)) {
7
+ return { name: 'NonErrorThrown', message: formatUnknown(error) };
8
+ }
9
+ if (seen.has(error)) {
10
+ return { name: error.name, message: `${error.message} (circular cause)` };
11
+ }
12
+ seen.add(error);
13
+ const result = {
14
+ name: error.name,
15
+ message: error.message,
16
+ stack: error.stack,
17
+ };
18
+ if (error.cause !== undefined)
19
+ result.cause = normalizeError(error.cause, seen);
20
+ return result;
21
+ }
22
+ function formatUnknown(value) {
23
+ if (typeof value === 'string')
24
+ return value;
25
+ try {
26
+ return JSON.stringify(value) ?? String(value);
27
+ }
28
+ catch {
29
+ return String(value);
30
+ }
31
+ }
32
+ //# sourceMappingURL=loggable-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loggable-error.js","sourceRoot":"","sources":["../src/loggable-error.ts"],"names":[],"mappings":"AAQA,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,EAAS,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,cAAc,CAAC,KAAc,EAAE,IAAgB;IACtD,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;IACnE,CAAC;IAED,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,mBAAmB,EAAE,CAAC;IAC5E,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChB,MAAM,MAAM,GAAkB;QAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC;IACF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Inbound conversion: `MessageReceived` -> a Harness `UserMessage`.
3
+ *
4
+ * Content is structured-text by default (red line 6): platform parts without
5
+ * downloadable local bytes are mapped to plain-text blocks — images/files/audio/video
6
+ * become `[type: ...]` placeholders instead of raw platform JSON. Conversation
7
+ * metadata (channel / sender / message id) is folded into a configurable text
8
+ * prefix so the model can still tell where a message came from.
9
+ *
10
+ * WX5 attachment path: when an image part carries `localData` (the adapter
11
+ * already downloaded + decrypted the bytes), and a `saveImage` hook is supplied,
12
+ * the converter registers the bytes with the Harness attachment service and emits
13
+ * a real `ImageBlock` (`{ type: 'image', attachment: ImageAttachmentRef }`) so
14
+ * the model receives a genuine multimodal image instead of the `[image]`
15
+ * placeholder. The hook is optional; without it (or on attachment failure) the
16
+ * converter falls back to the text placeholder so message delivery never breaks.
17
+ */
18
+ import { type ContentBlock, type UserMessage } from '@deepseek-ai/dsh-llm';
19
+ import type { ImageAttachmentRef, SaveImageAttachment } from '@deepseek-ai/dsh-attachment';
20
+ import { type AudioPart, type FilePart, type MessagePart, type MessageReceived, type VideoPart } from '@krischoichoi/channel-core';
21
+ import type { ChannelAttachmentDescriptor } from './file-provider.js';
22
+ /** Type-level alias: the bridge always produces Harness user messages. */
23
+ export type UserMessageLike = UserMessage;
24
+ /**
25
+ * Bridge-provided seam to commit one image to the Harness attachment service.
26
+ * Mirrors `AttachmentStore.saveImage` so the converter stays decoupled from the
27
+ * concrete Cordis service (resolved at the use site and injected). Returns the
28
+ * durable reference used to build the `ImageBlock`.
29
+ */
30
+ export type SaveImageHook = (input: SaveImageAttachment) => Promise<ImageAttachmentRef>;
31
+ /** A binary (file / audio / video) part carrying local bytes. */
32
+ export type StoredBinaryPart = FilePart | AudioPart | VideoPart;
33
+ /**
34
+ * Optional bridge-provided seam that persists a file/audio/video part with
35
+ * `localData` into the private channel asset store and returns a compact
36
+ * model descriptor (plan \u00a750/\u00a754). Mirrors `SaveImageHook`: the
37
+ * converter stays decoupled from the concrete store (injected at the use
38
+ * site). Absent -> the part falls back to its `[file: name]` placeholder;
39
+ * a thrown/undefined result also falls back, never breaking delivery.
40
+ */
41
+ export type FileStoreHook = (part: StoredBinaryPart) => Promise<ChannelAttachmentDescriptor | undefined>;
42
+ export interface MessageConvertOptions {
43
+ /** Whether to prepend the `[channel=.. sender=.. message=..]` prefix. Defaults to false. */
44
+ includeMetadataPrefix?: boolean;
45
+ /** Optional image-commit hook enabling the real attachment path. */
46
+ saveImage?: SaveImageHook;
47
+ /** Optional file/audio/video store hook enabling the private asset path. */
48
+ fileStore?: FileStoreHook;
49
+ }
50
+ /** Convert a channel message-received event into a model-facing user message. */
51
+ export declare function toHarnessUserMessage(event: MessageReceived, options?: MessageConvertOptions): Promise<UserMessageLike>;
52
+ /**
53
+ * Map structured parts to a single text line. Never embeds raw payloads.
54
+ * Image parts with local bytes and a saveImage hook are surfaced via
55
+ * `onImage` instead of being folded into text; they never contribute a text
56
+ * placeholder (unless the attachment path is unavailable, in which case they
57
+ * fall back to the ordinary `[image]` placeholder).
58
+ */
59
+ export declare function partsToText(parts: readonly MessagePart[], options?: {
60
+ saveImage?: SaveImageHook;
61
+ fileStore?: FileStoreHook;
62
+ onImage?: (block: ContentBlock) => void;
63
+ }): Promise<string>;
64
+ //# sourceMappingURL=message-converter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-converter.d.ts","sourceRoot":"","sources":["../src/message-converter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,kBAAkB,EAAkB,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC3G,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,QAAQ,EAEb,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,SAAS,EACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEtE,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAExF,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,gBAAgB,KACnB,OAAO,CAAC,2BAA2B,GAAG,SAAS,CAAC,CAAC;AAEtD,MAAM,WAAW,qBAAqB;IACpC,4FAA4F;IAC5F,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oEAAoE;IACpE,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,iFAAiF;AACjF,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAkB1B;AA0BD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACpC,GACL,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Inbound conversion: `MessageReceived` -> a Harness `UserMessage`.
3
+ *
4
+ * Content is structured-text by default (red line 6): platform parts without
5
+ * downloadable local bytes are mapped to plain-text blocks — images/files/audio/video
6
+ * become `[type: ...]` placeholders instead of raw platform JSON. Conversation
7
+ * metadata (channel / sender / message id) is folded into a configurable text
8
+ * prefix so the model can still tell where a message came from.
9
+ *
10
+ * WX5 attachment path: when an image part carries `localData` (the adapter
11
+ * already downloaded + decrypted the bytes), and a `saveImage` hook is supplied,
12
+ * the converter registers the bytes with the Harness attachment service and emits
13
+ * a real `ImageBlock` (`{ type: 'image', attachment: ImageAttachmentRef }`) so
14
+ * the model receives a genuine multimodal image instead of the `[image]`
15
+ * placeholder. The hook is optional; without it (or on attachment failure) the
16
+ * converter falls back to the text placeholder so message delivery never breaks.
17
+ */
18
+ import { createUserMessage, } from '@deepseek-ai/dsh-llm';
19
+ import { mimeHintFromFilename, normalizeMimeHint, } from '@krischoichoi/channel-core';
20
+ /** Convert a channel message-received event into a model-facing user message. */
21
+ export async function toHarnessUserMessage(event, options = {}) {
22
+ const blocks = [];
23
+ const includePrefix = options.includeMetadataPrefix ?? false;
24
+ if (includePrefix) {
25
+ const prefix = metadataPrefix(event);
26
+ if (prefix)
27
+ blocks.push({ type: 'text', text: prefix });
28
+ }
29
+ blocks.push(...await partsToBlocks(event.message.content, options));
30
+ if (blocks.length === 0)
31
+ blocks.push({ type: 'text', text: '' });
32
+ return createUserMessage({
33
+ content: blocks,
34
+ // Channel inbound messages are authored by the remote human. Marking
35
+ // them as plugin context makes Harness skip user-only behavior such as
36
+ // deterministic and LLM-backed session title generation.
37
+ source: { kind: 'user' },
38
+ });
39
+ }
40
+ /** Convert platform parts to Harness blocks without changing their order. */
41
+ async function partsToBlocks(parts, options) {
42
+ const blocks = [];
43
+ for (const part of parts) {
44
+ if (part.type === 'image') {
45
+ const block = await imageBlock(part, options.saveImage);
46
+ if (block) {
47
+ blocks.push(block);
48
+ continue;
49
+ }
50
+ }
51
+ const text = await partsToText([part], { fileStore: options.fileStore });
52
+ if (text)
53
+ blocks.push({ type: 'text', text });
54
+ }
55
+ return blocks;
56
+ }
57
+ function metadataPrefix(event) {
58
+ return `[channel=${event.channel} sender=${event.sender.id} message=${event.message.id}] `;
59
+ }
60
+ /**
61
+ * Map structured parts to a single text line. Never embeds raw payloads.
62
+ * Image parts with local bytes and a saveImage hook are surfaced via
63
+ * `onImage` instead of being folded into text; they never contribute a text
64
+ * placeholder (unless the attachment path is unavailable, in which case they
65
+ * fall back to the ordinary `[image]` placeholder).
66
+ */
67
+ export async function partsToText(parts, options = {}) {
68
+ const segments = [];
69
+ for (const part of parts) {
70
+ switch (part.type) {
71
+ case 'text':
72
+ segments.push(part.text);
73
+ break;
74
+ case 'image': {
75
+ const block = await imageBlock(part, options.saveImage);
76
+ if (block) {
77
+ options.onImage?.(block);
78
+ }
79
+ else {
80
+ segments.push(part.alt ? `[image: ${part.alt}]` : part.url ? `[image: ${part.url}]` : '[image]');
81
+ }
82
+ break;
83
+ }
84
+ case 'file':
85
+ segments.push((await storedDescriptor(part, options.fileStore)) ??
86
+ (part.name ? `[file: ${part.name}]` : part.url ? `[file: ${part.url}]` : '[file]'));
87
+ break;
88
+ case 'audio':
89
+ segments.push((await storedDescriptor(part, options.fileStore)) ??
90
+ (part.durationMs !== undefined ? `[audio: ${part.durationMs}ms]` : '[audio]'));
91
+ break;
92
+ case 'video':
93
+ segments.push((await storedDescriptor(part, options.fileStore)) ??
94
+ (part.durationMs !== undefined ? `[video: ${part.durationMs}ms]` : '[video]'));
95
+ break;
96
+ case 'location':
97
+ segments.push(`[location: ${part.latitude},${part.longitude}]`);
98
+ break;
99
+ case 'card':
100
+ segments.push(`[card: ${part.kind}]`);
101
+ break;
102
+ case 'unsupported':
103
+ segments.push('[unsupported content]');
104
+ break;
105
+ }
106
+ }
107
+ return segments.join('');
108
+ }
109
+ /**
110
+ * Produce an image content block when the part carries local bytes and a
111
+ * saveImage hook commits them. A media type is inferred from `mimeType`
112
+ * (or the data URI / URL extension) and accepted only when it maps to a
113
+ * supported raster type; otherwise the bytes are committed as-is and the
114
+ * attachment service performs final validation. Any failure returns undefined
115
+ * so the caller falls back to the text placeholder (never breaks delivery).
116
+ */
117
+ async function imageBlock(part, saveImage) {
118
+ if (!saveImage || !part.localData || part.localData.byteLength === 0)
119
+ return undefined;
120
+ const mediaType = inferImageMediaType(part);
121
+ if (!mediaType)
122
+ return undefined;
123
+ try {
124
+ const ref = await saveImage({
125
+ data: part.localData,
126
+ mediaType,
127
+ name: part.alt,
128
+ });
129
+ return { type: 'image', attachment: ref };
130
+ }
131
+ catch {
132
+ return undefined;
133
+ }
134
+ }
135
+ /** Infer a raster media type from a part's mimeType / url / dataUri. */
136
+ function inferImageMediaType(part) {
137
+ const candidate = normalizeMimeHint(part.mimeType)
138
+ ?? normalizeMimeHint(mimeFromDataUri(part.dataUri))
139
+ ?? mimeHintFromFilename(part.url);
140
+ if (!candidate)
141
+ return undefined;
142
+ const normalized = candidate.split(';')[0].trim().toLowerCase();
143
+ switch (normalized) {
144
+ case 'image/png':
145
+ case 'image/jpeg':
146
+ case 'image/jpg':
147
+ return normalized === 'image/jpg' ? 'image/jpeg' : normalized;
148
+ case 'image/webp':
149
+ case 'image/gif':
150
+ return normalized;
151
+ default:
152
+ return undefined;
153
+ }
154
+ }
155
+ function mimeFromDataUri(dataUri) {
156
+ if (!dataUri)
157
+ return undefined;
158
+ const m = /^data:([^;,]+)/.exec(dataUri);
159
+ return m?.[1];
160
+ }
161
+ /**
162
+ * Render a stored file/audio/video descriptor line when the optional
163
+ * `fileStore` hook persists the part's local bytes. Returns `undefined`
164
+ * (fall back to the placeholder) on no local bytes, a missing hook, an
165
+ * unrecognized result, or a hook failure — delivery never breaks.
166
+ */
167
+ async function storedDescriptor(part, fileStore) {
168
+ if (!fileStore || !part.localData || part.localData.byteLength === 0)
169
+ return undefined;
170
+ try {
171
+ const descriptor = await fileStore(part);
172
+ return descriptor ? renderDescriptor(descriptor) : undefined;
173
+ }
174
+ catch {
175
+ return undefined;
176
+ }
177
+ }
178
+ function renderDescriptor(asset) {
179
+ const mime = asset.mimeType ? ' ' + asset.mimeType : '';
180
+ const readable = asset.readable ? ' readable' : '';
181
+ return `[attachment ${asset.attachmentId} ${asset.name}${mime} ${humanSize(asset.bytes)}${readable}]`;
182
+ }
183
+ function humanSize(bytes) {
184
+ if (bytes < 1024)
185
+ return bytes + ' B';
186
+ const kb = bytes / 1024;
187
+ if (kb < 1024)
188
+ return kb.toFixed(1) + ' KB';
189
+ return (kb / 1024).toFixed(1) + ' MB';
190
+ }
191
+ //# sourceMappingURL=message-converter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-converter.js","sourceRoot":"","sources":["../src/message-converter.ts"],"names":[],"mappings":"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"}