@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
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @krischoichoi/channel-harness
2
+
3
+ Thin Harness-native bridge between channel adapters and DeepSeek Harness
4
+ agents/sessions.
5
+
6
+ This package is the **only** package allowed to import Harness public APIs. It
7
+ turns `ChannelEvent`s emitted by `@krischoichoi/channel-core` adapters into Harness
8
+ sessions, routes them to the right agent, and streams assistant output back to
9
+ the channel reply pipeline.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @krischoichoi/channel-harness
15
+ ```
16
+
17
+ As a Cordis plugin:
18
+
19
+ ```yaml
20
+ - id: channels-harness
21
+ name: '@krischoichoi/channel-harness'
22
+ inject:
23
+ - channels
24
+ - agents
25
+ - agentDefaultModel
26
+ - agentPresets
27
+ - llm
28
+ - commands
29
+ ```
30
+
31
+ Channel images follow the official Harness image pipeline: the bridge hands
32
+ raw inbound image bytes to the Harness Attachment Store
33
+ (`attachments.saveImage` -> `ImageAttachmentRef` -> real `ImageBlock`) and
34
+ never rewrites messages for model compatibility. Whether the current model
35
+ can see images (vision variant vs. deterministic text placeholder) is decided
36
+ by Harness request projection, and the append-only session history keeps the
37
+ original attachment reference.
38
+
39
+ ## What it does
40
+
41
+ | Area | Responsibility |
42
+ | --- | --- |
43
+ | `SessionBinding` / `BindingStore` | Binds platform conversations to Harness session ids (file-backed by default, survives restarts) |
44
+ | `AgentManager` / `AgentRouter` | Resolves/creates the agent for a conversation via `agent.default` plus per-channel/account/conversation overrides |
45
+ | `MessageConverter` | Maps structured `ChannelEvent` messages to Harness message types |
46
+ | `ReplyRouter` / `ReplyContextStore` | Streams `session/event` output back to the adapter (`ReplyHandle`) |
47
+ | `ChannelQuestionPresenter` (interactions/) | Presents `ask_user_question` requests through generic interactive actions. One root-level, **prepended** answerer on the official `user-questions/request` Cordis waterfall serves every profile (the official Remote/Web answerer registers first at boot, and a waterfall is first-claim-wins); a declined presentation delegates to the next answerer (the Web UI) via `next()` |
48
+ | `WorkspaceResolver` | Maps conversations to Harness workspaces (`channel-account` by default) |
49
+ | commands | Registers Agent-scoped slash commands (`/new`, `/stop`, `/help`, `/status`, `/model`, `/version`, `/mirror`, `/bind`) through Harness `CommandRuntime` |
50
+ | outbox | Proactive `send_channel_message` tool support (`OutboxService`) |
51
+
52
+ Model routing remains Harness-owned. A channel Session uses the model resolved
53
+ at create/resume time from its request header, route options, or the shared
54
+ `agentDefaultModel`. `/model` delegates the current-Session switch to the
55
+ official Host RPC (or the official headless hook) and persists the same choice
56
+ as the default for future Sessions. The bridge does not add a second owner or
57
+ run a first-turn model preparation RPC.
58
+
59
+ ## Configuration
60
+
61
+ ```yaml
62
+ - id: channels-harness
63
+ name: '@krischoichoi/channel-harness'
64
+ inject: [channels, agents, agentDefaultModel, agentPresets, llm, commands]
65
+ config:
66
+ agent:
67
+ default:
68
+ preset: main
69
+ provider: deepseek
70
+ model: deepseek-chat
71
+ maxTokens: 4096
72
+ routing:
73
+ mode: global # global | channel | account | conversation
74
+ bindingStore:
75
+ type: file # memory | file
76
+ workspace:
77
+ mode: channel-account # channel-account | host-cwd | disabled
78
+ autoCreate: true
79
+ userQuestions:
80
+ enabled: true
81
+ timeoutMs: 300000
82
+ reply:
83
+ updateIntervalMs: 200
84
+ splitParagraphs: true
85
+ splitCodeBlocks: true
86
+ finalFlush: true
87
+ maxConcurrency: 4
88
+ drainTimeoutMs: 5000
89
+ includeMetadataPrefix: false
90
+ ```
91
+
92
+ Routing priority when `routing.mode` is not `global`:
93
+ `conversation` > `account` > `channel` > `agent.default`.
94
+
95
+ ## Development
96
+
97
+ ```bash
98
+ pnpm --filter @krischoichoi/channel-harness build
99
+ pnpm --filter @krischoichoi/channel-harness typecheck
100
+ pnpm --filter @krischoichoi/channel-harness test
101
+ ```
102
+
103
+ ## Related
104
+
105
+ - [Repository root](../../README.md)
106
+ - [Architecture design](../../docs/architecture.md)
107
+
108
+ ## License
109
+
110
+ [MIT](../../LICENSE)
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Pure Inbound Access Controller.
3
+ *
4
+ * NO I/O, NO imports of storage/adapters/model. This is a deterministic pure
5
+ * function of a resolved policy + inbound identity facts. The bridge resolves
6
+ * the policy, then feeds both into `authorize` and acts ONLY on the returned
7
+ * decision (drop when !authorized or !activated).
8
+ *
9
+ * Algorithm (§19.1 DM / §19.2 Group):
10
+ * - DM: disabled -> DENY; open -> ALLOW; allowlist -> allowFrom includes
11
+ * senderId ? ALLOW : DENY (empty allowFrom == DENY ALL, never open).
12
+ * - Group: groupPolicy disabled -> DENY; allowlist -> groups[conversationId]
13
+ * must exist; open -> defaultGroupRule must exist. The selected rule
14
+ * must be enabled; then the
15
+ * sender gate (allowlist/open); finally the ACTIVATION gate: requireMention
16
+ * without mentionedBot === true is NOT activated (`undefined !== true`, no
17
+ * fail-open).
18
+ *
19
+ * The plan separates the Security Gate (authorized) from the Activation Gate
20
+ * (activated). `reason` is always the single most-specific cause.
21
+ */
22
+ import type { ChannelAccessPolicy } from '@krischoichoi/channel-core';
23
+ import type { InboundAccessDecision } from './decision.js';
24
+ export interface InboundAccessInput {
25
+ conversationType: 'dm' | 'group';
26
+ senderId: string;
27
+ conversationId: string;
28
+ /** Adapter-supplied activation fact; `undefined`/`false` is never "mentioned". */
29
+ mentionedBot?: boolean;
30
+ policy: ChannelAccessPolicy;
31
+ }
32
+ export declare class InboundAccessController {
33
+ authorize(input: InboundAccessInput): InboundAccessDecision;
34
+ private authorizeDm;
35
+ private authorizeGroup;
36
+ }
37
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/access/controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,IAAI,GAAG,OAAO,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,qBAAa,uBAAuB;IAClC,SAAS,CAAC,KAAK,EAAE,kBAAkB,GAAG,qBAAqB;IAO3D,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,cAAc;CAiCvB"}
@@ -0,0 +1,51 @@
1
+ export class InboundAccessController {
2
+ authorize(input) {
3
+ if (input.conversationType === 'dm') {
4
+ return this.authorizeDm(input);
5
+ }
6
+ return this.authorizeGroup(input);
7
+ }
8
+ authorizeDm(input) {
9
+ const { policy, senderId } = input;
10
+ if (policy.dmPolicy === 'disabled') {
11
+ return { authorized: false, activated: false, reason: 'dm_disabled' };
12
+ }
13
+ if (policy.dmPolicy === 'open') {
14
+ return { authorized: true, activated: true, reason: 'allowed' };
15
+ }
16
+ // policy.dmPolicy === 'allowlist' (incl. allowFrom = []).
17
+ if (policy.allowFrom.includes(senderId)) {
18
+ return { authorized: true, activated: true, reason: 'allowed' };
19
+ }
20
+ return { authorized: false, activated: false, reason: 'user_not_allowed' };
21
+ }
22
+ authorizeGroup(input) {
23
+ const { policy, senderId, conversationId, mentionedBot } = input;
24
+ if (policy.groupPolicy === 'disabled') {
25
+ return { authorized: false, activated: false, reason: 'group_disabled' };
26
+ }
27
+ const rule = policy.groupPolicy === 'open'
28
+ ? policy.defaultGroupRule
29
+ : policy.groups[conversationId];
30
+ if (!rule) {
31
+ return { authorized: false, activated: false, reason: 'group_not_allowed' };
32
+ }
33
+ if (rule.enabled !== true) {
34
+ return { authorized: false, activated: false, reason: 'group_disabled' };
35
+ }
36
+ // Sender gate.
37
+ if (rule.senderPolicy === 'allowlist') {
38
+ if (!rule.allowFrom.includes(senderId)) {
39
+ return { authorized: false, activated: false, reason: 'group_user_not_allowed' };
40
+ }
41
+ }
42
+ // senderPolicy === 'open' -> any sender in this NAMED group is authorized.
43
+ // Activation gate (§14): requireMention without a reliable mention is NOT
44
+ // activated (undefined !== true, never fail-open).
45
+ if (rule.requireMention && mentionedBot !== true) {
46
+ return { authorized: true, activated: false, reason: 'mention_required' };
47
+ }
48
+ return { authorized: true, activated: true, reason: 'allowed' };
49
+ }
50
+ }
51
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/access/controller.ts"],"names":[],"mappings":"AAiCA,MAAM,OAAO,uBAAuB;IAClC,SAAS,CAAC,KAAyB;QACjC,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAEO,WAAW,CAAC,KAAyB;QAC3C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;QAEnC,IAAI,MAAM,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;YACnC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QACxE,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC/B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClE,CAAC;QACD,0DAA0D;QAC1D,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClE,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7E,CAAC;IAEO,cAAc,CAAC,KAAyB;QAC9C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;QAEjE,IAAI,MAAM,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;YACtC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAC3E,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,KAAK,MAAM;YACxC,CAAC,CAAC,MAAM,CAAC,gBAAgB;YACzB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAC9E,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC1B,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAC3E,CAAC;QAED,eAAe;QACf,IAAI,IAAI,CAAC,YAAY,KAAK,WAAW,EAAE,CAAC;YACtC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACvC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;YACnF,CAAC;QACH,CAAC;QACD,2EAA2E;QAE3E,0EAA0E;QAC1E,mDAAmD;QACnD,IAAI,IAAI,CAAC,cAAc,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;QAC5E,CAAC;QAED,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClE,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Access decision types.
3
+ *
4
+ * `AccessDecisionReason` enumerates every fail-closed outcome the Harness
5
+ * Access Gate can produce. `InboundAccessDecision` is the pure controller's
6
+ * output: `authorized` = the security gate passed; `activated` = the activation
7
+ * gate passed; `reason` is the singular, most specific cause (see §19).
8
+ *
9
+ * These types are intentionally free of I/O concerns — the controller that
10
+ * produces them never touches storage, adapters or the model.
11
+ */
12
+ export type AccessDecisionReason = 'allowed' | 'missing_policy' | 'invalid_policy' | 'unidentified_sender' | 'invalid_conversation' | 'dm_disabled' | 'user_not_allowed' | 'group_disabled' | 'group_not_allowed' | 'group_user_not_allowed' | 'mention_required';
13
+ export interface InboundAccessDecision {
14
+ authorized: boolean;
15
+ activated: boolean;
16
+ reason: AccessDecisionReason;
17
+ }
18
+ //# sourceMappingURL=decision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../src/access/decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,sBAAsB,GACtB,aAAa,GACb,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,wBAAwB,GACxB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,oBAAoB,CAAC;CAC9B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.js","sourceRoot":"","sources":["../../src/access/decision.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Harness-side Access Policy Resolver.
3
+ *
4
+ * Failure-closed resolution over the shared ChannelStorage. The Harness DOES
5
+ * NOT depend on channel-control: it reads the shared versioned policy KV via
6
+ * `accessPolicyStorageKey` and validates it with
7
+ * the shared `channelAccessPolicySchema` (both live in @krischoichoi/channel-core).
8
+ *
9
+ * Reading rules (§15):
10
+ * - missing JSON -> `missing`
11
+ * - malformed JSON -> `invalid`
12
+ * - unknown version / schema failure -> `invalid`
13
+ *
14
+ * There is no policy caching: every inbound reads once, so Web
15
+ * saves and permission revocations take effect immediately with no invalidation
16
+ * machinery and no stale-ACL window.
17
+ */
18
+ import type { ChannelAccessPolicy, ChannelStorage } from '@krischoichoi/channel-core';
19
+ export type ResolvedAccessPolicy = {
20
+ state: 'present';
21
+ policy: ChannelAccessPolicy;
22
+ } | {
23
+ state: 'missing';
24
+ } | {
25
+ state: 'invalid';
26
+ error: string;
27
+ };
28
+ export interface ChannelAccessPolicyResolver {
29
+ resolve(channelId: string, accountId: string): Promise<ResolvedAccessPolicy>;
30
+ }
31
+ /**
32
+ * Default resolver backed by the shared ChannelStorage (production wiring is
33
+ * `ctx.channels.resources.storage`). Lazily resolves storage via a getter so it
34
+ * stays decoupled from the Cordis context / ChannelService lifecycle.
35
+ */
36
+ export declare class StoredChannelAccessPolicyResolver implements ChannelAccessPolicyResolver {
37
+ private readonly getStorage;
38
+ constructor(getStorage: () => ChannelStorage);
39
+ resolve(channelId: string, accountId: string): Promise<ResolvedAccessPolicy>;
40
+ }
41
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/access/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAGtF,MAAM,MAAM,oBAAoB,GAC5B;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,GACjD;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,WAAW,2BAA2B;IAC1C,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC9E;AAED;;;;GAIG;AACH,qBAAa,iCAAkC,YAAW,2BAA2B;IACvE,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,MAAM,cAAc;IAEvD,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAkBnF"}
@@ -0,0 +1,31 @@
1
+ import { accessPolicyStorageKey, channelAccessPolicySchema } from '@krischoichoi/channel-core';
2
+ /**
3
+ * Default resolver backed by the shared ChannelStorage (production wiring is
4
+ * `ctx.channels.resources.storage`). Lazily resolves storage via a getter so it
5
+ * stays decoupled from the Cordis context / ChannelService lifecycle.
6
+ */
7
+ export class StoredChannelAccessPolicyResolver {
8
+ getStorage;
9
+ constructor(getStorage) {
10
+ this.getStorage = getStorage;
11
+ }
12
+ async resolve(channelId, accountId) {
13
+ const storage = this.getStorage();
14
+ const raw = await storage.get(accessPolicyStorageKey(channelId, accountId));
15
+ if (raw === undefined)
16
+ return { state: 'missing' };
17
+ let parsed;
18
+ try {
19
+ parsed = JSON.parse(raw);
20
+ }
21
+ catch {
22
+ return { state: 'invalid', error: 'malformed policy JSON' };
23
+ }
24
+ const result = channelAccessPolicySchema.safeParse(parsed);
25
+ if (!result.success) {
26
+ return { state: 'invalid', error: `policy schema validation failed: ${result.error.message}` };
27
+ }
28
+ return { state: 'present', policy: result.data };
29
+ }
30
+ }
31
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/access/resolver.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAW/F;;;;GAIG;AACH,MAAM,OAAO,iCAAiC;IACf;IAA7B,YAA6B,UAAgC;QAAhC,eAAU,GAAV,UAAU,CAAsB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,SAAiB;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;QAC5E,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAEnD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;QAC9D,CAAC;QAED,MAAM,MAAM,GAAG,yBAAyB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,oCAAoC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACjG,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IACnD,CAAC;CACF"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * AgentManager — the single place that touches `ctx.agents`.
3
+ *
4
+ * Ownership model (doc H0.4/H0.7):
5
+ * - `ctx.agents.get()` returns a live agent the bridge does NOT own — it is
6
+ * never disposed here.
7
+ * - `ctx.agents.create()` / `ctx.agents.resume()` return an `AgentHandle`;
8
+ * the bridge MUST hold every handle it created/resumed and dispose it on
9
+ * plugin unload. Owned handles live in `owned` and are disposed exactly
10
+ * once by `disposeAll()`.
11
+ *
12
+ * Routing is expressed as an `AgentRouteSpec`, never an `agentId` (the
13
+ * Harness Agent identity is the `SessionId`). `create` and `resume` receive
14
+ * the SAME route so provider/model/maxTokens stay identical on both paths
15
+ * (doc H0.5 route parity).
16
+ *
17
+ * Create-vs-resume is decided by the CALLER (the bridge), not by error-regex
18
+ * fallback. Persistence is an OPTIONAL capability resolved LIVE at the use
19
+ * site: `canResume()` tells the caller whether a sessionPersistence service
20
+ * is currently mounted, `exists()` probes membership via the live service,
21
+ * and `probePersisted()` answers the atomic unavailable/present/missing
22
+ * question in one resolver call (no canResume/exists TOCTOU across a
23
+ * persistence HMR) — corruption / unsupported-format / backend failures
24
+ * propagate loudly rather than being misread as "no persistence".
25
+ *
26
+ * Global concurrency gate: at most `maxConcurrency` `create()`/`resume()` calls
27
+ * are in flight at once across all sessions; `get()` (a live lookup) is never
28
+ * limited.
29
+ */
30
+ import { SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session';
31
+ import type { SessionPersistence } from '@deepseek-ai/dsh-session-persistence';
32
+ import type { Context } from '@deepseek-ai/cordis';
33
+ import type { Agent, AgentOptions, AgentSetup, ModelSelection } from '@deepseek-ai/dsh-agent';
34
+ import type { ChannelLogger } from '@krischoichoi/channel-core';
35
+ import type { AgentRouteSpec } from './agent-router.js';
36
+ import type { SessionBinding } from './session-router.js';
37
+ /** A live agent as seen through the gateway (id + raw Agent + drive surface). */
38
+ export interface GatewayAgent {
39
+ id: string;
40
+ /** The raw Harness Agent — the dsh-scope key used to install Agent-scoped commands. */
41
+ agent: Agent;
42
+ followup(message: unknown): void;
43
+ whenIdle(): Promise<void>;
44
+ }
45
+ /** A gateway-created/resumed agent that the caller must dispose. */
46
+ export interface GatewayAgentHandle extends GatewayAgent {
47
+ dispose(): Promise<void>;
48
+ }
49
+ /**
50
+ * Whether a persisted session exists, probed through the optional persistence
51
+ * service. Implemented via membership/inspection, NEVER by matching an error
52
+ * message against a /persistence|persist/i regex.
53
+ */
54
+ export interface PersistenceProbe {
55
+ exists(sessionId: string): Promise<boolean>;
56
+ }
57
+ /**
58
+ * Atomic result of probing ONE session through the LIVE persistence
59
+ * capability: `unavailable` (no sessionPersistence service mounted right
60
+ * now), `present`, or `missing`. Resolving the capability once per probe
61
+ * closes the canResume/exists TOCTOU window (a persistence HMR between the
62
+ * two lookups would otherwise pair different backends).
63
+ */
64
+ export type PersistedProbeResult = 'unavailable' | 'present' | 'missing';
65
+ /** Persistence was configured for the binding, but is unavailable right now. */
66
+ export declare class PersistenceUnavailableError extends Error {
67
+ readonly sessionId: string;
68
+ readonly bindingKey?: string | undefined;
69
+ constructor(sessionId: string, bindingKey?: string | undefined);
70
+ }
71
+ /**
72
+ * A durable binding references a persisted session that no longer exists
73
+ * (session-not-found, aligned with the official Host's
74
+ * "persisted identity missing -> session-not-found"). With a
75
+ * sessionPersistence service mounted, a missing persisted session behind an
76
+ * existing binding is a binding/session durability inconsistency — NEVER a
77
+ * first create — so resolution fails loudly instead of silently
78
+ * blank-recreating the same session id. Deployments WITHOUT persistence
79
+ * explicitly ephemeral bindings never throw this: they recreate on the recorded id.
80
+ */
81
+ export declare class SessionNotFoundError extends Error {
82
+ readonly sessionId: string;
83
+ readonly bindingKey?: string | undefined;
84
+ constructor(sessionId: string, bindingKey?: string | undefined);
85
+ }
86
+ /** Caller-supplied metadata for a fresh agent create. */
87
+ export interface AgentCreateMeta {
88
+ /** Explicit working directory for the new session's header.cwd. */
89
+ cwd?: string;
90
+ }
91
+ /** Minimal port the bridge needs from the Harness agent runtime. */
92
+ export interface AgentGateway {
93
+ get(sessionId: string): GatewayAgent | undefined;
94
+ create(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup, meta?: AgentCreateMeta): Promise<GatewayAgentHandle>;
95
+ resume(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup): Promise<GatewayAgentHandle>;
96
+ /** Whether a sessionPersistence service is available (enables resume). */
97
+ canResume(): boolean;
98
+ /** Probe whether a persisted session exists. */
99
+ exists(sessionId: string): Promise<boolean>;
100
+ /**
101
+ * Atomic persistence probe (live capability resolved once). Optional on
102
+ * the interface so minimal test doubles can rely on the AgentManager's
103
+ * canResume+exists fallback; the real gateway implements it.
104
+ */
105
+ probePersisted?(sessionId: string): Promise<PersistedProbeResult>;
106
+ }
107
+ /** A resolved agent reference handed to the caller. */
108
+ export interface AgentRef {
109
+ sessionId: string;
110
+ route: AgentRouteSpec;
111
+ /** The raw Harness Agent — the dsh-scope key used to install Agent-scoped commands. */
112
+ agent: Agent;
113
+ followup(message: unknown): void;
114
+ whenIdle(): Promise<void>;
115
+ /**
116
+ * Marks the caller done with the ref. Simple v1 semantics: the ref never
117
+ * disposes the underlying handle itself — the manager owns lifecycle and
118
+ * disposes each handle exactly once during `disposeAll()`.
119
+ */
120
+ release(): void;
121
+ }
122
+ /** Filter out undefined optional agentOptions fields (route parity helper). */
123
+ export declare function optionsFor(route: AgentRouteSpec): AgentOptions | undefined;
124
+ /**
125
+ * Resolve a route's provider/model, falling back to Harness's default model
126
+ * selection when the route leaves the model unset.
127
+ *
128
+ * `{{model}}` (the persona variable that fails with "prompt variable has no
129
+ * value") depends only on `model`, so a route that already pins a model —
130
+ * with or without a provider — is left untouched. Only a missing model is
131
+ * filled from the default (and the default's provider rides along when the
132
+ * route also left provider unset).
133
+ *
134
+ * The default model is read live (per agent creation), so new channel sessions
135
+ * follow the user's Harness-wide default while sessions pinned to an explicit
136
+ * route keep their own provider/model.
137
+ */
138
+ export declare function resolveRoute(route: AgentRouteSpec, defaultSelection: ModelSelection | undefined): AgentRouteSpec;
139
+ /**
140
+ * Probe a persisted session's existence through the persistence service's
141
+ * `list()` membership. A session that is absent returns `false`; any backend
142
+ * failure (corruption, unsupported format, connectivity) propagates loudly.
143
+ *
144
+ * The persistence service is resolved LIVE on every call (a resolver, not a
145
+ * startup snapshot): a sessionPersistence mounted/unmounted/replaced after
146
+ * the bridge started is observed on the next probe — reversible Cordis
147
+ * lifecycle parity, since `ctx.get` only returns currently active providers.
148
+ */
149
+ export declare class PersistenceMembershipProbe implements PersistenceProbe {
150
+ private readonly resolvePersistence;
151
+ constructor(resolvePersistence: () => SessionPersistence | undefined);
152
+ exists(sessionId: string): Promise<boolean>;
153
+ /** Atomic probe: the live capability is resolved exactly once per call. */
154
+ probe(sessionId: string): Promise<PersistedProbeResult>;
155
+ }
156
+ /**
157
+ * Read one durable V3 session through the public read handle. A channel bridge
158
+ * never depends on an implementation-specific persistence reader.
159
+ */
160
+ export declare function resolvePersistedInspection(persistence: SessionPersistence, sessionId: SessionId): Promise<PersistedInspection | undefined>;
161
+ /** Structural subset of the official `SessionInspection` the bridge consumes. */
162
+ export interface PersistedInspection {
163
+ readonly meta: SessionHeader;
164
+ readonly events: readonly SessionEvent[];
165
+ }
166
+ /**
167
+ * Real gateway over `ctx.agents`. This is the only Harness import surface in
168
+ * the bridge (besides the `session/event` feed consumed by ReplyRouter).
169
+ *
170
+ * Persistence is an OPTIONAL capability resolved LIVE at the use site: the
171
+ * caller passes a resolver (`() => ctx.get('sessionPersistence')`), so
172
+ * `canResume()` and the existence probe reflect the service's CURRENT
173
+ * presence — mounting, unmounting, or replacing the service after the bridge
174
+ * started is observed on the next probe (never a startup snapshot).
175
+ */
176
+ export declare class HarnessAgentGateway implements AgentGateway {
177
+ private readonly ctx;
178
+ private readonly probe;
179
+ private readonly resolvePersistence;
180
+ /**
181
+ * All persisted session ids (shape-tolerant, issue #8) — the `/bind`
182
+ * resolution universe. Empty when no persistence is mounted.
183
+ */
184
+ listPersistedSessionIds(): Promise<string[]>;
185
+ /**
186
+ * The persisted Agent preset of one session (issue #6 route parity): the
187
+ * official `agentPresetProjectionDefinition` fold over the durable
188
+ * inspection. Undefined when no persistence / no recorded preset.
189
+ */
190
+ persistedPresetOf(sessionId: string): Promise<string | undefined>;
191
+ constructor(ctx: Context, resolvePersistence?: () => SessionPersistence | undefined);
192
+ get(sessionId: string): GatewayAgent | undefined;
193
+ canResume(): boolean;
194
+ exists(sessionId: string): Promise<boolean>;
195
+ probePersisted(sessionId: string): Promise<PersistedProbeResult>;
196
+ /** Read Harness's live default-model selection (undefined when absent). */
197
+ private defaultSelection;
198
+ /**
199
+ * Resolve and mount the official Agent preset before publication, matching
200
+ * the Harness Host's create/resume composition boundary. Without this join,
201
+ * preset-scoped tools such as `ask_user_question` are invisible to the Agent.
202
+ */
203
+ private composePreset;
204
+ create(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup, meta?: AgentCreateMeta): Promise<GatewayAgentHandle>;
205
+ resume(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup): Promise<GatewayAgentHandle>;
206
+ private wrap;
207
+ }
208
+ export declare class AgentManager {
209
+ private readonly gateway;
210
+ private readonly logger;
211
+ private readonly inFlight;
212
+ /** Handles this manager created/resumed — the ones it must dispose. */
213
+ private readonly owned;
214
+ /** Resolved refs, kept for drain (`whenIdle`) lookups. */
215
+ private readonly refs;
216
+ /** Agents that already received the one-time channel-command setup. */
217
+ private readonly configuredAgents;
218
+ /** sessionId -> binding, the reverse lookup used by the ReplyRouter. */
219
+ private readonly bindings;
220
+ private readonly maxConcurrency;
221
+ private active;
222
+ /** Waiters queued for a free concurrency slot. */
223
+ private readonly waiters;
224
+ private closed;
225
+ constructor(gateway: AgentGateway, logger: ChannelLogger, maxConcurrency?: number);
226
+ /** Whether a sessionPersistence service enables `resume`. */
227
+ canResume(): boolean;
228
+ /** Probe whether a persisted session exists. Backend failures propagate. */
229
+ exists(sessionId: string): Promise<boolean>;
230
+ /**
231
+ * Atomic persistence probe: resolves the LIVE capability once and answers
232
+ * `unavailable` / `present` / `missing` — the bridge's single decision
233
+ * point for recreate-vs-resume-vs-fail, immune to a persistence HMR between
234
+ * `canResume()` and `exists()`.
235
+ */
236
+ probePersisted(sessionId: string): Promise<PersistedProbeResult>;
237
+ /**
238
+ * Create a NEW agent for a session (no prior binding). Calls
239
+ * `gateway.create` directly (under the concurrency slot), owns the handle,
240
+ * and returns the ref. Never resumes — the caller already decided this is a
241
+ * fresh conversation. Single-flight per session id.
242
+ */
243
+ create(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup, meta?: AgentCreateMeta): Promise<AgentRef>;
244
+ /**
245
+ * Resolve an agent for an EXISTING, persisted session: live `get` first; on
246
+ * a miss, `gateway.resume(route)`. A resume failure propagates loudly
247
+ * (throws) — never falls back to create and never sniffs error messages.
248
+ * Single-flight per session id.
249
+ */
250
+ resolve(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup): Promise<AgentRef>;
251
+ /**
252
+ * Borrow the LIVE agent for a session, when one is loaded in this process.
253
+ * Returns `undefined` when there is no live agent — the caller (the Session
254
+ * factory, for channel-session lifecycle decisions) decides what to do next
255
+ * (resume, or a cwd/workspace-aware recreate). Never creates or resumes and
256
+ * never takes ownership (the borrowed agent is never disposed here). Runs
257
+ * the one-time setup against a borrowed agent, exactly once.
258
+ */
259
+ borrowIfLive(sessionId: string, route: AgentRouteSpec, setup?: AgentSetup): Promise<AgentRef | undefined>;
260
+ /** Register the binding associated with a session (reverse reply routing). */
261
+ registerBinding(binding: SessionBinding): void;
262
+ /** Reverse lookup for the ReplyRouter. */
263
+ bindingFor(sessionId: string): SessionBinding | undefined;
264
+ /**
265
+ * Live agent lookup for the /stop fast path (spec §8). Returns the raw
266
+ * Agent when it is currently live in this process — NEVER creates or
267
+ * resumes a session just to answer this probe.
268
+ */
269
+ getLiveAgent(sessionId: string): Agent | undefined;
270
+ /** Resolved ref for drain purposes, if any. */
271
+ refFor(sessionId: string): AgentRef | undefined;
272
+ /** Session ids that currently have an active turn (for drain). */
273
+ activeSessions(): string[];
274
+ /**
275
+ * Dispose every owned handle exactly once and clear all tracking. Agents
276
+ * obtained via `gateway.get()` (not owned) are never disposed.
277
+ */
278
+ disposeAll(): Promise<void>;
279
+ /**
280
+ * Dispose a single owned handle (used to roll back a create whose binding
281
+ * write failed afterward). No-op for handles the manager does not own.
282
+ */
283
+ disposeSession(sessionId: string): Promise<void>;
284
+ /**
285
+ * Retire a session's reference and reverse binding and dispose it ONLY if the
286
+ * manager owns the handle. Borrowed / unknown agents are released
287
+ * from local tracking but NEVER disposed. Retiring never touches persisted
288
+ * history — the old session keeps its durable log.
289
+ */
290
+ retireSession(sessionId: string): Promise<void>;
291
+ /**
292
+ * One-time channel-command setup for a BORROWED live agent. A
293
+ * borrowed agent never went through create/resume, so its setup could not
294
+ * have run at publication; run it here exactly once against the agent's
295
+ * scoped context. Setup failure propagates to the caller. The borrowed
296
+ * agent is never disposed here.
297
+ */
298
+ private ensureBorrowedSetup;
299
+ private doCreate;
300
+ private doResolve;
301
+ private doBorrowIfLive;
302
+ private withSlot;
303
+ private acquireSlot;
304
+ private releaseSlot;
305
+ private makeRef;
306
+ }
307
+ /** A durable session cannot be silently recomposed under a different preset. */
308
+ export declare class AgentPresetConflictError extends Error {
309
+ readonly sessionId: string;
310
+ readonly requested: string;
311
+ readonly persisted: string;
312
+ constructor(sessionId: string, requested: string, persisted: string);
313
+ }
314
+ //# sourceMappingURL=agent-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-manager.d.ts","sourceRoot":"","sources":["../src/agent-manager.ts"],"names":[],"mappings":"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"}