@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,54 @@
1
+ /**
2
+ * Thin channel adapter for Harness model selection.
3
+ *
4
+ * Harness owns the default model and the Web Host owns the session RPC. The
5
+ * channel only bridges `/model` to those official surfaces. In headless mode
6
+ * it uses the official Agent-scoped `ModelSelectionRef` required to affect the
7
+ * current Session. There is no per-Agent owner pin, host identity cache,
8
+ * first-turn prepare, or fallback state machine.
9
+ *
10
+ * Host transport: the official `ctx.sessionController`
11
+ * (`@deepseek-ai/dsh-api-session-controller`), which the Web profile mounts as
12
+ * the session business API. Selection goes through
13
+ * `sessionController.selectModel()`; the current-selection read is a
14
+ * best-effort local chain (a Web-side selection that is pending but not yet
15
+ * used by a request is not visible here — the official controller does not
16
+ * expose a host-side current-selection read).
17
+ */
18
+ import type { Context } from '@deepseek-ai/cordis';
19
+ import type { Agent, ModelSelection, ModelSelectionRef } from '@deepseek-ai/dsh-agent';
20
+ import type { SessionController } from '@deepseek-ai/dsh-api-session-controller';
21
+ export type ChannelModelSelectionRef = ModelSelectionRef;
22
+ export type ChannelModelSelectionMode = 'host' | 'local';
23
+ /**
24
+ * Narrow channel view over the official Host session business API — only the
25
+ * model surface this bridge consumes, derived from
26
+ * `@deepseek-ai/dsh-api-session-controller` instead of a local duplicate.
27
+ */
28
+ export type ChannelHostSessionController = Pick<SessionController, 'selectModel'>;
29
+ export declare class ChannelModelSelectionController {
30
+ private readonly rootCtx;
31
+ private readonly refs;
32
+ /**
33
+ * The owner strategy is fixed when the Agent scope is installed. The Host
34
+ * service itself is resolved live so HMR can replace its implementation
35
+ * without changing ownership of an existing Agent.
36
+ */
37
+ private readonly strategies;
38
+ constructor(rootCtx: Context);
39
+ get mode(): ChannelModelSelectionMode;
40
+ /** Install only the headless hook; Web Host owns it when sessionController is present. */
41
+ install(agentCtx: Context): () => void;
42
+ current(agent: Agent): Promise<ModelSelection | undefined>;
43
+ selectionForStep(agent: Agent): Promise<ModelSelection | undefined>;
44
+ select(agent: Agent, selection: ModelSelection): Promise<void>;
45
+ private hostSessionController;
46
+ /**
47
+ * Agents configured through the bridge always have a recorded strategy.
48
+ * Keep the deployment-wide mode as a compatibility fallback for direct
49
+ * controller callers that have not installed the Agent-scoped hook.
50
+ */
51
+ private strategyFor;
52
+ private readLocal;
53
+ }
54
+ //# sourceMappingURL=model-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selection.d.ts","sourceRoot":"","sources":["../src/model-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAEjF,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AACzD,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;AAElF,qBAAa,+BAA+B;IAS9B,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoD;IACzE;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqD;gBAEnD,OAAO,EAAE,OAAO;IAE7C,IAAI,IAAI,IAAI,yBAAyB,CAEpC;IAED,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,IAAI;IAkBhC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI1D,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAQnE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BpE,OAAO,CAAC,qBAAqB;IAI7B;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,SAAS;CAqBlB"}
@@ -0,0 +1,116 @@
1
+ import { installModelSelection } from '@deepseek-ai/dsh-agent';
2
+ import { ReasoningEffortId } from '@deepseek-ai/dsh-llm';
3
+ export class ChannelModelSelectionController {
4
+ rootCtx;
5
+ refs = new WeakMap();
6
+ /**
7
+ * The owner strategy is fixed when the Agent scope is installed. The Host
8
+ * service itself is resolved live so HMR can replace its implementation
9
+ * without changing ownership of an existing Agent.
10
+ */
11
+ strategies = new WeakMap();
12
+ constructor(rootCtx) {
13
+ this.rootCtx = rootCtx;
14
+ }
15
+ get mode() {
16
+ return this.hostSessionController() ? 'host' : 'local';
17
+ }
18
+ /** Install only the headless hook; Web Host owns it when sessionController is present. */
19
+ install(agentCtx) {
20
+ const strategy = this.mode;
21
+ this.strategies.set(agentCtx, strategy);
22
+ if (strategy === 'host') {
23
+ return () => {
24
+ if (this.strategies.get(agentCtx) === strategy)
25
+ this.strategies.delete(agentCtx);
26
+ };
27
+ }
28
+ const ref = { current: undefined, assembled: undefined };
29
+ const dispose = installModelSelection(agentCtx, ref);
30
+ this.refs.set(agentCtx, ref);
31
+ return () => {
32
+ dispose();
33
+ if (this.refs.get(agentCtx) === ref)
34
+ this.refs.delete(agentCtx);
35
+ if (this.strategies.get(agentCtx) === strategy)
36
+ this.strategies.delete(agentCtx);
37
+ };
38
+ }
39
+ async current(agent) {
40
+ return this.readLocal(agent);
41
+ }
42
+ async selectionForStep(agent) {
43
+ if (this.strategyFor(agent) === 'local') {
44
+ const ref = this.refs.get(agent.ctx);
45
+ if (ref?.assembled)
46
+ return ref.assembled;
47
+ }
48
+ return this.current(agent);
49
+ }
50
+ async select(agent, selection) {
51
+ if (this.strategyFor(agent) === 'host') {
52
+ const controller = this.hostSessionController();
53
+ if (!controller) {
54
+ throw new Error('host model selection is unavailable: sessionController is not mounted');
55
+ }
56
+ try {
57
+ await controller.selectModel({
58
+ sessionId: agent.id,
59
+ provider: selection.provider,
60
+ model: selection.model,
61
+ ...(selection.reasoningEffort ? { reasoningEffort: String(selection.reasoningEffort) } : {}),
62
+ });
63
+ }
64
+ catch (error) {
65
+ throw new Error(`model selection was rejected: ${error instanceof Error ? error.message : String(error)}`);
66
+ }
67
+ return;
68
+ }
69
+ const ref = this.refs.get(agent.ctx);
70
+ if (!ref)
71
+ throw new Error(`model selection is not installed for session '${String(agent.id)}'`);
72
+ ref.current = selection;
73
+ try {
74
+ await this.rootCtx.get('agentDefaultModel')?.saveSelection(selection);
75
+ }
76
+ catch {
77
+ // The current-session switch already holds; default persistence is best effort.
78
+ }
79
+ }
80
+ hostSessionController() {
81
+ return this.rootCtx.get('sessionController');
82
+ }
83
+ /**
84
+ * Agents configured through the bridge always have a recorded strategy.
85
+ * Keep the deployment-wide mode as a compatibility fallback for direct
86
+ * controller callers that have not installed the Agent-scoped hook.
87
+ */
88
+ strategyFor(agent) {
89
+ return this.strategies.get(agent.ctx) ?? this.mode;
90
+ }
91
+ readLocal(agent) {
92
+ const picked = this.refs.get(agent.ctx)?.current;
93
+ if (picked)
94
+ return picked;
95
+ const headerConfig = agent.session.requestHeader?.()?.config;
96
+ if (headerConfig?.provider && headerConfig.model) {
97
+ return {
98
+ provider: headerConfig.provider,
99
+ model: headerConfig.model,
100
+ ...(headerConfig.reasoningEffort ? { reasoningEffort: ReasoningEffortId(String(headerConfig.reasoningEffort)) } : {}),
101
+ };
102
+ }
103
+ const { provider, model } = agent.options;
104
+ if (provider && model)
105
+ return { provider, model };
106
+ const defaults = agent.ctx.get('agentDefaultModel')?.currentSelection();
107
+ if (!defaults?.provider || !defaults.model)
108
+ return undefined;
109
+ return {
110
+ provider: defaults.provider,
111
+ model: defaults.model,
112
+ ...(defaults.reasoningEffort ? { reasoningEffort: defaults.reasoningEffort } : {}),
113
+ };
114
+ }
115
+ }
116
+ //# sourceMappingURL=model-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selection.js","sourceRoot":"","sources":["../src/model-selection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Durable Binding Authority.
3
+ *
4
+ * Outbox authorization comes from the DURABLE `SessionBindingStore`, NEVER from
5
+ * the in-memory `AgentManager` cache. `AgentManager.bindingFor()` is only a
6
+ * hint used by the reply pipeline; a send MUST re-resolve through
7
+ * `findBySessionId` so it sees the current durable binding — including after a
8
+ * `/new`, at which point the retired session's durable binding is gone and the
9
+ * send must fail closed (from the binding update onward, session A's
10
+ * outbox has lost authority).
11
+ *
12
+ * `findBySessionId` fails closed with `AmbiguousBindingError`
13
+ * (OUTBOX_AMBIGUOUS_BINDING) when one session id maps to more than one current
14
+ * binding; here it is surfaced as the typed `OutboxError` with the same code.
15
+ */
16
+ import { type SessionBindingStore } from '../binding-store.js';
17
+ import type { SessionBinding } from '../session-router.js';
18
+ /**
19
+ * Resolve the single current durable binding for a session.
20
+ * Never consults an in-memory cache.
21
+ */
22
+ export declare function resolveBindingForSession(sessionId: string, bindingStore: SessionBindingStore): Promise<SessionBinding>;
23
+ //# sourceMappingURL=binding-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-resolver.d.ts","sourceRoot":"","sources":["../../src/outbox/binding-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,mBAAmB,GAChC,OAAO,CAAC,cAAc,CAAC,CAsBzB"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Durable Binding Authority.
3
+ *
4
+ * Outbox authorization comes from the DURABLE `SessionBindingStore`, NEVER from
5
+ * the in-memory `AgentManager` cache. `AgentManager.bindingFor()` is only a
6
+ * hint used by the reply pipeline; a send MUST re-resolve through
7
+ * `findBySessionId` so it sees the current durable binding — including after a
8
+ * `/new`, at which point the retired session's durable binding is gone and the
9
+ * send must fail closed (from the binding update onward, session A's
10
+ * outbox has lost authority).
11
+ *
12
+ * `findBySessionId` fails closed with `AmbiguousBindingError`
13
+ * (OUTBOX_AMBIGUOUS_BINDING) when one session id maps to more than one current
14
+ * binding; here it is surfaced as the typed `OutboxError` with the same code.
15
+ */
16
+ import { AmbiguousBindingError } from '../binding-store.js';
17
+ import { OutboxError } from './types.js';
18
+ /**
19
+ * Resolve the single current durable binding for a session.
20
+ * Never consults an in-memory cache.
21
+ */
22
+ export async function resolveBindingForSession(sessionId, bindingStore) {
23
+ let binding;
24
+ try {
25
+ binding = await bindingStore.findBySessionId(sessionId);
26
+ }
27
+ catch (error) {
28
+ if (error instanceof AmbiguousBindingError) {
29
+ throw new OutboxError('OUTBOX_AMBIGUOUS_BINDING', "session '" + sessionId + "' maps to more than one current channel binding; failing closed", { sessionId, cause: error });
30
+ }
31
+ throw error;
32
+ }
33
+ if (!binding) {
34
+ throw new OutboxError('OUTBOX_NO_BINDING', "no current channel binding for session '" + sessionId + "'", { sessionId });
35
+ }
36
+ return binding;
37
+ }
38
+ //# sourceMappingURL=binding-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-resolver.js","sourceRoot":"","sources":["../../src/outbox/binding-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,qBAAqB,EAA4B,MAAM,qBAAqB,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,SAAiB,EACjB,YAAiC;IAEjC,IAAI,OAAmC,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;YAC3C,MAAM,IAAI,WAAW,CACnB,0BAA0B,EAC1B,WAAW,GAAG,SAAS,GAAG,iEAAiE,EAC3F,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAC5B,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,mBAAmB,EACnB,0CAA0C,GAAG,SAAS,GAAG,GAAG,EAC5D,EAAE,SAAS,EAAE,CACd,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Outbox capability resolution.
3
+ *
4
+ * Proactive (outbound, unsolicited) sends are a distinct capability from the
5
+ * transport flags in `ChannelAdapter.capabilities`. An adapter declares its
6
+ * proactive support via an optional `outboxCapabilities` getter/field (e.g.
7
+ * DingTalk will add it in M7C once its official proactive API is wired). When
8
+ * the adapter exposes none, capability DERIVES from the transport flags.
9
+ *
10
+ * Fail-closed rule (cannot pretend support): `proactiveText` is
11
+ * true for adapters exposing the field; otherwise the conservative default
12
+ * assumes text is available but media is only available when the adapter
13
+ * transports image, file, audio, or video. A capability of `false` means the
14
+ * outbox must refuse the send (fail closed), never fabricate support.
15
+ */
16
+ /**
17
+ * Outbox (proactive-send) capability set.
18
+ *
19
+ * - `proactiveText` — the adapter can proactively send plain text to an
20
+ * arbitrary target (not just a reply).
21
+ * - `proactiveMedia` — the adapter can proactively send an image, file,
22
+ * audio, or video part to an arbitrary target.
23
+ */
24
+ export interface OutboxCapabilities {
25
+ proactiveText: boolean;
26
+ proactiveMedia: boolean;
27
+ }
28
+ /**
29
+ * Optional adapter-declared outbox capabilities. Adapters that complete their
30
+ * proactive path attach this field; when absent the caller derives a default.
31
+ */
32
+ export interface OutboxCapabilitySource {
33
+ readonly outboxCapabilities?: OutboxCapabilities;
34
+ readonly capabilities?: {
35
+ image?: boolean;
36
+ file?: boolean;
37
+ audio?: boolean;
38
+ video?: boolean;
39
+ };
40
+ }
41
+ /**
42
+ * Resolve an adapter's proactive outbox capabilities. Uses an
43
+ * explicitly declared `outboxCapabilities` when present; otherwise derives
44
+ * `proactiveText = true` and `proactiveMedia` from the transport
45
+ * image/file/audio/video flags. Capability `false` always means fail closed.
46
+ */
47
+ export declare function resolveOutboxCapabilities(adapter: OutboxCapabilitySource): OutboxCapabilities;
48
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/outbox/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACjD,QAAQ,CAAC,YAAY,CAAC,EAAE;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,kBAAkB,CAY7F"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Outbox capability resolution.
3
+ *
4
+ * Proactive (outbound, unsolicited) sends are a distinct capability from the
5
+ * transport flags in `ChannelAdapter.capabilities`. An adapter declares its
6
+ * proactive support via an optional `outboxCapabilities` getter/field (e.g.
7
+ * DingTalk will add it in M7C once its official proactive API is wired). When
8
+ * the adapter exposes none, capability DERIVES from the transport flags.
9
+ *
10
+ * Fail-closed rule (cannot pretend support): `proactiveText` is
11
+ * true for adapters exposing the field; otherwise the conservative default
12
+ * assumes text is available but media is only available when the adapter
13
+ * transports image, file, audio, or video. A capability of `false` means the
14
+ * outbox must refuse the send (fail closed), never fabricate support.
15
+ */
16
+ /**
17
+ * Resolve an adapter's proactive outbox capabilities. Uses an
18
+ * explicitly declared `outboxCapabilities` when present; otherwise derives
19
+ * `proactiveText = true` and `proactiveMedia` from the transport
20
+ * image/file/audio/video flags. Capability `false` always means fail closed.
21
+ */
22
+ export function resolveOutboxCapabilities(adapter) {
23
+ if (adapter.outboxCapabilities) {
24
+ return {
25
+ proactiveText: adapter.outboxCapabilities.proactiveText,
26
+ proactiveMedia: adapter.outboxCapabilities.proactiveMedia,
27
+ };
28
+ }
29
+ const caps = adapter.capabilities;
30
+ return {
31
+ proactiveText: true,
32
+ proactiveMedia: Boolean(caps?.image || caps?.file || caps?.audio || caps?.video),
33
+ };
34
+ }
35
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/outbox/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA6BH;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA+B;IACvE,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC/B,OAAO;YACL,aAAa,EAAE,OAAO,CAAC,kBAAkB,CAAC,aAAa;YACvD,cAAc,EAAE,OAAO,CAAC,kBAAkB,CAAC,cAAc;SAC1D,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC;IAClC,OAAO;QACL,aAAa,EAAE,IAAI;QACnB,cAAc,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,CAAC;KACjF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Durable Channel Outbox.
3
+ *
4
+ * Public surface of the outbox module: the request + error types, the durable
5
+ * binding/target/attachment resolvers, the proactive capability resolver, the
6
+ * `ChannelOutboxService`, and the `send_channel_message` tool registration.
7
+ */
8
+ export * from './types.js';
9
+ export * from './target.js';
10
+ export * from './binding-resolver.js';
11
+ export * from './capabilities.js';
12
+ export * from './service.js';
13
+ export * from './tool-send.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/outbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Durable Channel Outbox.
3
+ *
4
+ * Public surface of the outbox module: the request + error types, the durable
5
+ * binding/target/attachment resolvers, the proactive capability resolver, the
6
+ * `ChannelOutboxService`, and the `send_channel_message` tool registration.
7
+ */
8
+ export * from './types.js';
9
+ export * from './target.js';
10
+ export * from './binding-resolver.js';
11
+ export * from './capabilities.js';
12
+ export * from './service.js';
13
+ export * from './tool-send.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/outbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ChannelOutboxService — the durable outbox.
3
+ *
4
+ * One `send` call walks the full outbound path:
5
+ *
6
+ * ```
7
+ * sessionId
8
+ * → resolveBindingForSession (durable authority)
9
+ * → targetFromBinding
10
+ * → build OutboundMessage (text / attachment bytes)
11
+ * → proactive capability gate (fail closed)
12
+ * → adapter.send(target, message)
13
+ * ```
14
+ *
15
+ * The binding authority is the DURABLE store — never an in-memory agent cache.
16
+ * Send errors are NEVER swallowed: they propagate to the tool caller after a
17
+ * diagnostic log.
18
+ */
19
+ import type { ChannelAdapter, ChannelLogger, SendResult } from '@krischoichoi/channel-core';
20
+ import type { SessionBindingStore } from '../binding-store.js';
21
+ import type { ResolvedChannelAttachment } from '../file-provider.js';
22
+ import type { ChannelOutboundRequest } from './types.js';
23
+ export interface ChannelOutboxServiceOptions {
24
+ bindingStore: SessionBindingStore;
25
+ getAdapter(channelId: string): ChannelAdapter | undefined;
26
+ /** Optional extension resolver; attachment sends are disabled without it. */
27
+ attachmentResolver?: ChannelAttachmentResolver;
28
+ logger: ChannelLogger;
29
+ }
30
+ export interface ChannelOutboxSendOptions {
31
+ signal?: AbortSignal;
32
+ }
33
+ export type ChannelAttachmentResolver = (attachmentId: string, sessionId: string) => Promise<ResolvedChannelAttachment>;
34
+ export declare class ChannelOutboxService {
35
+ private readonly bindingStore;
36
+ private readonly getAdapter;
37
+ private readonly attachmentResolver;
38
+ private readonly logger;
39
+ constructor(options: ChannelOutboxServiceOptions);
40
+ /**
41
+ * Send a proactive channel message on behalf of `sessionId`. Resolves the
42
+ * durable binding, builds the outbound message, gates on proactive
43
+ * capability (fail closed), and delivers through the adapter. Returns the
44
+ * adapter's `SendResult`.
45
+ */
46
+ send(sessionId: string, request: ChannelOutboundRequest, options?: ChannelOutboxSendOptions): Promise<SendResult>;
47
+ }
48
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/outbox/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAIb,UAAU,EACX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAMzD,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,mBAAmB,CAAC;IAClC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC1D,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;IAC/C,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,yBAAyB,GAAG,CACtC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,KACd,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAExC,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoD;IAC/E,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAwC;IAC3E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,OAAO,EAAE,2BAA2B;IAOhD;;;;;OAKG;IACG,IAAI,CACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,sBAAsB,EAC/B,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC;CA4EvB"}
@@ -0,0 +1,74 @@
1
+ import { resolveBindingForSession } from './binding-resolver.js';
2
+ import { targetFromBinding } from './target.js';
3
+ import { resolveOutboxCapabilities } from './capabilities.js';
4
+ import { OutboxError } from './types.js';
5
+ export class ChannelOutboxService {
6
+ bindingStore;
7
+ getAdapter;
8
+ attachmentResolver;
9
+ logger;
10
+ constructor(options) {
11
+ this.bindingStore = options.bindingStore;
12
+ this.getAdapter = options.getAdapter;
13
+ this.attachmentResolver = options.attachmentResolver;
14
+ this.logger = options.logger;
15
+ }
16
+ /**
17
+ * Send a proactive channel message on behalf of `sessionId`. Resolves the
18
+ * durable binding, builds the outbound message, gates on proactive
19
+ * capability (fail closed), and delivers through the adapter. Returns the
20
+ * adapter's `SendResult`.
21
+ */
22
+ async send(sessionId, request, options = {}) {
23
+ const signal = options.signal;
24
+ if (signal?.aborted) {
25
+ const err = new Error('channel outbox send aborted for session ' + sessionId);
26
+ err.name = 'AbortError';
27
+ throw err;
28
+ }
29
+ const binding = await resolveBindingForSession(sessionId, this.bindingStore);
30
+ const target = targetFromBinding(binding);
31
+ const adapter = this.getAdapter(binding.channelId);
32
+ if (!adapter) {
33
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', "no channel adapter for channel '" + binding.channelId + "'", { sessionId });
34
+ }
35
+ const caps = resolveOutboxCapabilities(adapter);
36
+ const hasText = !!request.text;
37
+ const hasAttachment = !!request.attachmentId;
38
+ if (!hasText && !hasAttachment) {
39
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', 'a channel outbound send requires text and/or an attachment', { sessionId });
40
+ }
41
+ if (hasText && !caps.proactiveText) {
42
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', "channel '" + binding.channelId + "' cannot proactively send text (proactiveText=false)", { sessionId });
43
+ }
44
+ if (hasAttachment && !caps.proactiveMedia) {
45
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', "channel '" + binding.channelId + "' cannot proactively send media (proactiveMedia=false)", { sessionId });
46
+ }
47
+ const message = { text: request.text };
48
+ if (hasAttachment) {
49
+ if (!this.attachmentResolver) {
50
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', 'outbound attachments require a private asset store that is not configured', { sessionId });
51
+ }
52
+ const asset = await this.attachmentResolver(request.attachmentId, sessionId);
53
+ if (!adapter.capabilities[asset.kind]) {
54
+ throw new OutboxError('OUTBOX_CAPABILITY_UNAVAILABLE', "channel '" + binding.channelId + "' cannot send " + asset.kind
55
+ + ' attachments (' + asset.kind + '=false)', { sessionId });
56
+ }
57
+ message.parts = [resolvedAttachmentPart(asset)];
58
+ }
59
+ this.logger.info("[channel-harness] outbox send session='" + sessionId + "' channel='" + binding.channelId
60
+ + "' text=" + hasText + ' attachment=' + hasAttachment);
61
+ const result = await adapter.send(target, message);
62
+ this.logger.debug("[channel-harness] outbox sent session='" + sessionId + "' delivered=" + result.delivered);
63
+ return result;
64
+ }
65
+ }
66
+ function resolvedAttachmentPart(asset) {
67
+ return {
68
+ type: asset.kind,
69
+ localData: asset.data,
70
+ name: asset.name,
71
+ ...(asset.mimeType ? { mimeType: asset.mimeType } : {}),
72
+ };
73
+ }
74
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/outbox/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Durable outbox target derivation.
3
+ *
4
+ * `targetFromBinding` maps a durable `SessionBinding`'s stable identity onto
5
+ * the `ChannelTarget` the adapter sends to. The binding is the durable
6
+ * authority, so the target comes from the STORE-resolved binding,
7
+ * never from an in-memory agent cache and never from the incoming message.
8
+ */
9
+ import type { ChannelTarget } from '@krischoichoi/channel-core';
10
+ import type { SessionBinding } from '../session-router.js';
11
+ /**
12
+ * Derive the outbound `ChannelTarget` for a durable binding.
13
+ * Spreads `threadId` only when present; `conversationType` is always carried
14
+ * because the binding requires it (v3).
15
+ */
16
+ export declare function targetFromBinding(binding: SessionBinding): ChannelTarget;
17
+ //# sourceMappingURL=target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../src/outbox/target.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAQxE"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Derive the outbound `ChannelTarget` for a durable binding.
3
+ * Spreads `threadId` only when present; `conversationType` is always carried
4
+ * because the binding requires it (v3).
5
+ */
6
+ export function targetFromBinding(binding) {
7
+ return {
8
+ channelId: binding.channelId,
9
+ accountId: binding.accountId,
10
+ conversationId: binding.conversationId,
11
+ conversationType: binding.conversationType,
12
+ ...(binding.threadId ? { threadId: binding.threadId } : {}),
13
+ };
14
+ }
15
+ //# sourceMappingURL=target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.js","sourceRoot":"","sources":["../../src/outbox/target.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAuB;IACvD,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,SAAuC;QAC1D,SAAS,EAAE,OAAO,CAAC,SAAuC;QAC1D,cAAc,EAAE,OAAO,CAAC,cAAiD;QACzE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAqC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ChannelOutboxService } from './service.js';
3
+ export interface RegisterSendChannelMessageToolOptions {
4
+ /** The durable outbox service backing the tool. */
5
+ outbox: ChannelOutboxService;
6
+ }
7
+ /** Rendered confirmation of a successful send. */
8
+ export interface SendChannelMessageResult {
9
+ delivered: boolean;
10
+ messageId?: string;
11
+ }
12
+ /**
13
+ * The tool definition, ready to register on an Agent scope. Parameters are
14
+ * type-level guaranteed to be only `{ text?, attachment_id? }`.
15
+ */
16
+ export declare function registerSendChannelMessageTool(options: RegisterSendChannelMessageToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
17
+ /** Install the tool on an Agent scoped context (Agent-scoped registration). */
18
+ export declare function installSendChannelMessageTool(agentCtx: Context, options: RegisterSendChannelMessageToolOptions): Promise<void>;
19
+ //# sourceMappingURL=tool-send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-send.d.ts","sourceRoot":"","sources":["../../src/outbox/tool-send.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,WAAW,qCAAqC;IACpD,mDAAmD;IACnD,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED,kDAAkD;AAClD,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,qCAAqC,mDA2D/C;AAED,+EAA+E;AAC/E,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,qCAAqC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAKf"}