@pi-in-go/pigpen-ahp 0.1.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 (304) hide show
  1. package/CREDITS.md +27 -0
  2. package/LICENSE +23 -0
  3. package/README.md +148 -0
  4. package/extensions/ahp/access_options_test.go +116 -0
  5. package/extensions/ahp/extension.go +54 -0
  6. package/extensions/ahp/go.mod +12 -0
  7. package/extensions/ahp/go.sum +2 -0
  8. package/extensions/ahp/go.work +8 -0
  9. package/extensions/ahp/internal/channels/chat.go +110 -0
  10. package/extensions/ahp/internal/channels/root.go +32 -0
  11. package/extensions/ahp/internal/channels/session.go +128 -0
  12. package/extensions/ahp/internal/channels/session_test.go +108 -0
  13. package/extensions/ahp/internal/compose/compose.go +153 -0
  14. package/extensions/ahp/internal/compose/compose_test.go +120 -0
  15. package/extensions/ahp/internal/gaps/gaps_test.go +88 -0
  16. package/extensions/ahp/internal/host/handshake_test.go +206 -0
  17. package/extensions/ahp/internal/host/host.go +1305 -0
  18. package/extensions/ahp/internal/host/reconnect_test.go +197 -0
  19. package/extensions/ahp/internal/host/replaywindow_test.go +31 -0
  20. package/extensions/ahp/internal/host/schema_test.go +79 -0
  21. package/extensions/ahp/internal/host/store.go +291 -0
  22. package/extensions/ahp/internal/host/store_update_test.go +85 -0
  23. package/extensions/ahp/internal/host/subscriptions_test.go +160 -0
  24. package/extensions/ahp/internal/host/surface_test.go +95 -0
  25. package/extensions/ahp/internal/host/workarounds.go +458 -0
  26. package/extensions/ahp/internal/host/workarounds_test.go +96 -0
  27. package/extensions/ahp/internal/live/live.go +328 -0
  28. package/extensions/ahp/internal/live/live_test.go +91 -0
  29. package/extensions/ahp/internal/live/store.go +113 -0
  30. package/extensions/ahp/internal/mapper/activity.go +199 -0
  31. package/extensions/ahp/internal/mapper/activity_test.go +188 -0
  32. package/extensions/ahp/internal/mapper/bench_test.go +38 -0
  33. package/extensions/ahp/internal/mapper/event_mapper_test.go +380 -0
  34. package/extensions/ahp/internal/mapper/fixtures_test.go +387 -0
  35. package/extensions/ahp/internal/mapper/helpers_test.go +251 -0
  36. package/extensions/ahp/internal/mapper/js.go +131 -0
  37. package/extensions/ahp/internal/mapper/mapper.go +600 -0
  38. package/extensions/ahp/internal/mapper/message_input_test.go +196 -0
  39. package/extensions/ahp/internal/mapper/messageinput.go +272 -0
  40. package/extensions/ahp/internal/mapper/schema_helpers_test.go +5 -0
  41. package/extensions/ahp/internal/mapper/testdata/LICENSE-pi-ahp +21 -0
  42. package/extensions/ahp/internal/mapper/testdata/fixtures/abort.json +711 -0
  43. package/extensions/ahp/internal/mapper/testdata/fixtures/bash-long-output.json +1312 -0
  44. package/extensions/ahp/internal/mapper/testdata/fixtures/compaction.json +6285 -0
  45. package/extensions/ahp/internal/mapper/testdata/fixtures/parallel-tools.json +2622 -0
  46. package/extensions/ahp/internal/mapper/testdata/fixtures/plain-text.json +1856 -0
  47. package/extensions/ahp/internal/mapper/testdata/fixtures/single-tool.json +1128 -0
  48. package/extensions/ahp/internal/mapper/testdata/fixtures/steering.json +3926 -0
  49. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-bash.json +2372 -0
  50. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-edit.json +3983 -0
  51. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-error.json +2850 -0
  52. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-find.json +1162 -0
  53. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-grep.json +2783 -0
  54. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-loop.json +1693 -0
  55. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-ls.json +2427 -0
  56. package/extensions/ahp/internal/mapper/testdata/fixtures/tool-write.json +1271 -0
  57. package/extensions/ahp/internal/mapper/title.go +34 -0
  58. package/extensions/ahp/internal/mapper/usermsg.go +87 -0
  59. package/extensions/ahp/internal/pi/activeturn_test.go +296 -0
  60. package/extensions/ahp/internal/pi/backend.go +64 -0
  61. package/extensions/ahp/internal/pi/catalogue.go +370 -0
  62. package/extensions/ahp/internal/pi/catalogue_test.go +271 -0
  63. package/extensions/ahp/internal/pi/chatdriver.go +533 -0
  64. package/extensions/ahp/internal/pi/chatdriver_test.go +416 -0
  65. package/extensions/ahp/internal/pi/clientactions.go +212 -0
  66. package/extensions/ahp/internal/pi/clientactions_test.go +273 -0
  67. package/extensions/ahp/internal/pi/completions.go +230 -0
  68. package/extensions/ahp/internal/pi/completions_test.go +272 -0
  69. package/extensions/ahp/internal/pi/config_test.go +269 -0
  70. package/extensions/ahp/internal/pi/deletesession.go +69 -0
  71. package/extensions/ahp/internal/pi/deletesession_test.go +153 -0
  72. package/extensions/ahp/internal/pi/disposal_test.go +362 -0
  73. package/extensions/ahp/internal/pi/fixture_test.go +308 -0
  74. package/extensions/ahp/internal/pi/foreign_test.go +122 -0
  75. package/extensions/ahp/internal/pi/harness_test.go +83 -0
  76. package/extensions/ahp/internal/pi/history.go +269 -0
  77. package/extensions/ahp/internal/pi/hydrated_test.go +171 -0
  78. package/extensions/ahp/internal/pi/hydratedlifecycle_test.go +196 -0
  79. package/extensions/ahp/internal/pi/hydration_test.go +209 -0
  80. package/extensions/ahp/internal/pi/hydrator.go +212 -0
  81. package/extensions/ahp/internal/pi/imageinput.go +224 -0
  82. package/extensions/ahp/internal/pi/imageinput_test.go +57 -0
  83. package/extensions/ahp/internal/pi/lifecycle_test.go +381 -0
  84. package/extensions/ahp/internal/pi/models.go +212 -0
  85. package/extensions/ahp/internal/pi/models_test.go +128 -0
  86. package/extensions/ahp/internal/pi/paging.go +95 -0
  87. package/extensions/ahp/internal/pi/pagingtruncate_test.go +323 -0
  88. package/extensions/ahp/internal/pi/projecttrust.go +222 -0
  89. package/extensions/ahp/internal/pi/registry.go +924 -0
  90. package/extensions/ahp/internal/pi/restart_test.go +185 -0
  91. package/extensions/ahp/internal/pi/services.go +70 -0
  92. package/extensions/ahp/internal/pi/sessionconfig.go +64 -0
  93. package/extensions/ahp/internal/pi/sessionfiles_test.go +74 -0
  94. package/extensions/ahp/internal/pi/sessionstore.go +83 -0
  95. package/extensions/ahp/internal/pi/summary_test.go +426 -0
  96. package/extensions/ahp/internal/pi/workarounds_wire_test.go +208 -0
  97. package/extensions/ahp/internal/pisession/fromentries_test.go +32 -0
  98. package/extensions/ahp/internal/pisession/json.go +31 -0
  99. package/extensions/ahp/internal/pisession/pisession.go +580 -0
  100. package/extensions/ahp/internal/settings/access.go +63 -0
  101. package/extensions/ahp/internal/settings/access_test.go +52 -0
  102. package/extensions/ahp/internal/settings/settings.go +117 -0
  103. package/extensions/ahp/internal/settings/settings_test.go +104 -0
  104. package/extensions/ahp/internal/svc/etag_other.go +15 -0
  105. package/extensions/ahp/internal/svc/etag_unix.go +34 -0
  106. package/extensions/ahp/internal/svc/glob.go +197 -0
  107. package/extensions/ahp/internal/svc/mime.go +37 -0
  108. package/extensions/ahp/internal/svc/paths.go +108 -0
  109. package/extensions/ahp/internal/svc/pty.go +64 -0
  110. package/extensions/ahp/internal/svc/pty_darwin.go +42 -0
  111. package/extensions/ahp/internal/svc/pty_linux.go +36 -0
  112. package/extensions/ahp/internal/svc/pty_other.go +9 -0
  113. package/extensions/ahp/internal/svc/pty_test.go +114 -0
  114. package/extensions/ahp/internal/svc/pty_unix.go +147 -0
  115. package/extensions/ahp/internal/svc/resource.go +495 -0
  116. package/extensions/ahp/internal/svc/resource_test.go +501 -0
  117. package/extensions/ahp/internal/svc/stat_bsd.go +9 -0
  118. package/extensions/ahp/internal/svc/stat_linux.go +9 -0
  119. package/extensions/ahp/internal/svc/terminal.go +454 -0
  120. package/extensions/ahp/internal/svc/terminal_test.go +512 -0
  121. package/extensions/ahp/internal/svc/watch.go +590 -0
  122. package/extensions/ahp/internal/svc/watch_test.go +627 -0
  123. package/extensions/ahp/internal/svc/watchevents_test.go +452 -0
  124. package/extensions/ahp/internal/svc/watchpolicy.go +81 -0
  125. package/extensions/ahp/internal/svc/watchpolicy_test.go +151 -0
  126. package/extensions/ahp/internal/svc/watchracy_test.go +61 -0
  127. package/extensions/ahp/internal/testkit/schema/LICENSE-agent-host-protocol +21 -0
  128. package/extensions/ahp/internal/testkit/schema/actions.schema.json +9208 -0
  129. package/extensions/ahp/internal/testkit/schema/commands.schema.json +10862 -0
  130. package/extensions/ahp/internal/testkit/schema/errors.schema.json +10928 -0
  131. package/extensions/ahp/internal/testkit/schema/notifications.schema.json +6667 -0
  132. package/extensions/ahp/internal/testkit/schema/state.schema.json +6392 -0
  133. package/extensions/ahp/internal/testkit/schema.go +360 -0
  134. package/extensions/ahp/internal/testkit/testkit.go +367 -0
  135. package/extensions/ahp/internal/twin/twin.go +27 -0
  136. package/extensions/ahp/internal/wire/dispatchable.go +106 -0
  137. package/extensions/ahp/internal/wire/dispatchable_list.go +15 -0
  138. package/extensions/ahp/internal/wire/helpers_test.go +5 -0
  139. package/extensions/ahp/internal/wire/wire.go +218 -0
  140. package/extensions/ahp/internal/wire/wire_test.go +123 -0
  141. package/extensions/ahp/internal/ws/helpers_test.go +7 -0
  142. package/extensions/ahp/internal/ws/vscode_test.go +198 -0
  143. package/extensions/ahp/internal/ws/ws.go +674 -0
  144. package/extensions/ahp/internal/ws/ws_test.go +324 -0
  145. package/extensions/ahp/realpig_test.go +589 -0
  146. package/extensions/ahp/runtime.go +311 -0
  147. package/extensions/ahp/runtime_test.go +110 -0
  148. package/extensions/ahp/testdata/upstream-tests.json +3833 -0
  149. package/extensions/ahp/third_party/agent-host-protocol-go/LICENSE +21 -0
  150. package/extensions/ahp/third_party/agent-host-protocol-go/NOTICE-PIGPEN.md +18 -0
  151. package/extensions/ahp/third_party/agent-host-protocol-go/ahp/client.go +1011 -0
  152. package/extensions/ahp/third_party/agent-host-protocol-go/ahp/error.go +111 -0
  153. package/extensions/ahp/third_party/agent-host-protocol-go/ahp/multi_host_state_mirror.go +239 -0
  154. package/extensions/ahp/third_party/agent-host-protocol-go/ahp/reducers.go +1939 -0
  155. package/extensions/ahp/third_party/agent-host-protocol-go/ahp/transport.go +176 -0
  156. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/actions.generated.go +2447 -0
  157. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/commands.generated.go +1546 -0
  158. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/common.go +205 -0
  159. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/errors.generated.go +65 -0
  160. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/messages.generated.go +138 -0
  161. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/notifications.generated.go +264 -0
  162. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/state.generated.go +6171 -0
  163. package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/version.generated.go +31 -0
  164. package/extensions/ahp/third_party/agent-host-protocol-go/go.mod +3 -0
  165. package/extensions/ahp/twins_test.go +91 -0
  166. package/package.json +40 -0
  167. package/proof/PORT.md +121 -0
  168. package/proof/mutations.json +152 -0
  169. package/proof/oracle/LICENSE +21 -0
  170. package/proof/oracle/README.md +120 -0
  171. package/proof/oracle/UPSTREAM.md +7 -0
  172. package/proof/oracle/package.json +68 -0
  173. package/proof/oracle/src/bin/cli.ts +66 -0
  174. package/proof/oracle/src/bin/tunnel.ts +128 -0
  175. package/proof/oracle/src/channels/chat.ts +134 -0
  176. package/proof/oracle/src/channels/root.ts +39 -0
  177. package/proof/oracle/src/channels/session.ts +118 -0
  178. package/proof/oracle/src/channels/terminal.ts +11 -0
  179. package/proof/oracle/src/core/channels.ts +108 -0
  180. package/proof/oracle/src/core/client-workarounds.ts +358 -0
  181. package/proof/oracle/src/core/connection.ts +41 -0
  182. package/proof/oracle/src/core/host.ts +882 -0
  183. package/proof/oracle/src/core/sequencer.ts +75 -0
  184. package/proof/oracle/src/core/state-store.ts +162 -0
  185. package/proof/oracle/src/core/uri.ts +25 -0
  186. package/proof/oracle/src/host/direct-settings.ts +110 -0
  187. package/proof/oracle/src/host/pi-host.ts +220 -0
  188. package/proof/oracle/src/host/serve.ts +71 -0
  189. package/proof/oracle/src/host/terminal-service.ts +346 -0
  190. package/proof/oracle/src/pi/activity.ts +171 -0
  191. package/proof/oracle/src/pi/changeset-service.ts +646 -0
  192. package/proof/oracle/src/pi/changeset-uri.ts +64 -0
  193. package/proof/oracle/src/pi/chat-driver.ts +529 -0
  194. package/proof/oracle/src/pi/completions.ts +160 -0
  195. package/proof/oracle/src/pi/delete-session.ts +53 -0
  196. package/proof/oracle/src/pi/event-mapper.ts +648 -0
  197. package/proof/oracle/src/pi/git-changes.ts +605 -0
  198. package/proof/oracle/src/pi/history.ts +305 -0
  199. package/proof/oracle/src/pi/image-input.ts +56 -0
  200. package/proof/oracle/src/pi/image-mime.ts +8 -0
  201. package/proof/oracle/src/pi/in-process-backend.ts +155 -0
  202. package/proof/oracle/src/pi/message-input.ts +212 -0
  203. package/proof/oracle/src/pi/models.ts +124 -0
  204. package/proof/oracle/src/pi/project-trust.ts +66 -0
  205. package/proof/oracle/src/pi/provider.ts +2 -0
  206. package/proof/oracle/src/pi/resource-paths.ts +80 -0
  207. package/proof/oracle/src/pi/resource-service.ts +356 -0
  208. package/proof/oracle/src/pi/resource-watch-policy.ts +35 -0
  209. package/proof/oracle/src/pi/resource-watch.ts +357 -0
  210. package/proof/oracle/src/pi/session-catalogue.ts +318 -0
  211. package/proof/oracle/src/pi/session-config.ts +84 -0
  212. package/proof/oracle/src/pi/session-history.ts +75 -0
  213. package/proof/oracle/src/pi/session-hydrator.ts +219 -0
  214. package/proof/oracle/src/pi/session-registry.ts +815 -0
  215. package/proof/oracle/src/pi/session-storage.ts +29 -0
  216. package/proof/oracle/src/pi/session-title.ts +18 -0
  217. package/proof/oracle/src/pi/turn-paging.ts +94 -0
  218. package/proof/oracle/src/pi/user-message.ts +66 -0
  219. package/proof/oracle/src/protocol/errors.ts +44 -0
  220. package/proof/oracle/src/protocol/jsonrpc.ts +89 -0
  221. package/proof/oracle/src/protocol/version.ts +39 -0
  222. package/proof/oracle/src/transport/websocket.ts +136 -0
  223. package/proof/oracle/src/tunnel/devtunnel.ts +293 -0
  224. package/proof/oracle/src/tunnel/discovery.ts +37 -0
  225. package/proof/oracle/test/active-turn-reconnect.test.ts +274 -0
  226. package/proof/oracle/test/activity.test.ts +171 -0
  227. package/proof/oracle/test/changeset-lifecycle.test.serial.ts +303 -0
  228. package/proof/oracle/test/changeset-uri.test.ts +37 -0
  229. package/proof/oracle/test/changeset.test.serial.ts +487 -0
  230. package/proof/oracle/test/chat-driver.test.ts +691 -0
  231. package/proof/oracle/test/client-actions.test.ts +409 -0
  232. package/proof/oracle/test/client-workarounds.test.ts +320 -0
  233. package/proof/oracle/test/completions.test.ts +323 -0
  234. package/proof/oracle/test/delete-session.test.ts +142 -0
  235. package/proof/oracle/test/direct-settings.test.ts +81 -0
  236. package/proof/oracle/test/event-mapper.test.ts +619 -0
  237. package/proof/oracle/test/fetch-turns.test.ts +207 -0
  238. package/proof/oracle/test/fixtures/abort.json +711 -0
  239. package/proof/oracle/test/fixtures/bash-long-output.json +1312 -0
  240. package/proof/oracle/test/fixtures/compaction.json +6285 -0
  241. package/proof/oracle/test/fixtures/parallel-tools.json +2622 -0
  242. package/proof/oracle/test/fixtures/plain-text.json +1856 -0
  243. package/proof/oracle/test/fixtures/single-tool.json +1128 -0
  244. package/proof/oracle/test/fixtures/steering.json +3926 -0
  245. package/proof/oracle/test/fixtures/tool-bash.json +2372 -0
  246. package/proof/oracle/test/fixtures/tool-edit.json +3983 -0
  247. package/proof/oracle/test/fixtures/tool-error.json +2850 -0
  248. package/proof/oracle/test/fixtures/tool-find.json +1162 -0
  249. package/proof/oracle/test/fixtures/tool-grep.json +2783 -0
  250. package/proof/oracle/test/fixtures/tool-loop.json +1693 -0
  251. package/proof/oracle/test/fixtures/tool-ls.json +2427 -0
  252. package/proof/oracle/test/fixtures/tool-write.json +1271 -0
  253. package/proof/oracle/test/handshake.test.ts +275 -0
  254. package/proof/oracle/test/harness.ts +153 -0
  255. package/proof/oracle/test/hydrated-session-lifecycle.test.ts +211 -0
  256. package/proof/oracle/test/image-input.test.ts +28 -0
  257. package/proof/oracle/test/image-session.test.ts +69 -0
  258. package/proof/oracle/test/live-turn.test.ts +304 -0
  259. package/proof/oracle/test/mapper-fixtures.test.ts +293 -0
  260. package/proof/oracle/test/message-input.test.ts +181 -0
  261. package/proof/oracle/test/model-discovery.test.ts +170 -0
  262. package/proof/oracle/test/models.test.ts +109 -0
  263. package/proof/oracle/test/pi-host.test.ts +91 -0
  264. package/proof/oracle/test/pi-replay.test.ts +141 -0
  265. package/proof/oracle/test/project-trust.test.ts +125 -0
  266. package/proof/oracle/test/protocol-surface.test.ts +121 -0
  267. package/proof/oracle/test/pty.test.ts +100 -0
  268. package/proof/oracle/test/reconnect.test.ts +389 -0
  269. package/proof/oracle/test/resource-watch-policy.test.ts +78 -0
  270. package/proof/oracle/test/resource-watch.test.serial.ts +536 -0
  271. package/proof/oracle/test/resource.test.ts +499 -0
  272. package/proof/oracle/test/schema.test.ts +88 -0
  273. package/proof/oracle/test/session-catalogue.test.ts +314 -0
  274. package/proof/oracle/test/session-config.test.ts +224 -0
  275. package/proof/oracle/test/session-disposal.test.ts +364 -0
  276. package/proof/oracle/test/session-hydration.test.ts +194 -0
  277. package/proof/oracle/test/session-lifecycle.test.ts +416 -0
  278. package/proof/oracle/test/session-storage.test.serial.ts +32 -0
  279. package/proof/oracle/test/session-summary.test.ts +418 -0
  280. package/proof/oracle/test/subscriptions.test.ts +198 -0
  281. package/proof/oracle/test/support/assertions.ts +32 -0
  282. package/proof/oracle/test/support/async.ts +26 -0
  283. package/proof/oracle/test/support/hydrated-session.ts +205 -0
  284. package/proof/oracle/test/support/images.ts +8 -0
  285. package/proof/oracle/test/support/recorded-fixtures.ts +60 -0
  286. package/proof/oracle/test/support/recorded-scenarios.ts +145 -0
  287. package/proof/oracle/test/support/replay.ts +194 -0
  288. package/proof/oracle/test/support/schema.ts +221 -0
  289. package/proof/oracle/test/support/session-files.ts +12 -0
  290. package/proof/oracle/test/support/session-storage.ts +22 -0
  291. package/proof/oracle/test/support/upstream.ts +25 -0
  292. package/proof/oracle/test/support/watch-events.ts +108 -0
  293. package/proof/oracle/test/terminal-service.test.ts +495 -0
  294. package/proof/oracle/test/truncate.test.ts +230 -0
  295. package/proof/oracle/test/tunnel.test.ts +258 -0
  296. package/proof/oracle/test/upstream-workarounds.test.ts +37 -0
  297. package/proof/oracle/test/uri.test.ts +24 -0
  298. package/proof/oracle/test/watch-events.test.ts +118 -0
  299. package/proof/oracle/tsconfig.json +32 -0
  300. package/proof/proof-piglet/piglet.yaml +18 -0
  301. package/proof/tools/fakellm/go.mod +3 -0
  302. package/proof/tools/fakellm/main.go +109 -0
  303. package/proof/tools/gen-dispatchable.py +30 -0
  304. package/provenance.json +28 -0
@@ -0,0 +1,111 @@
1
+ // Package ahp is the async SDK for the Agent Host Protocol (AHP).
2
+ //
3
+ // The [Client] drives a pluggable [Transport] over a JSON-RPC channel
4
+ // and fans incoming actions out to per-URI subscriptions and an
5
+ // optional top-level event stream. Reducers (see [ApplyActionToRoot],
6
+ // [ApplyActionToSession], [ApplyActionToTerminal],
7
+ // [ApplyActionToChangeset]) translate every [ahptypes.StateAction]
8
+ // into mutations on the matching state tree.
9
+ //
10
+ // Multi-host consumers should reach for the
11
+ // [github.com/microsoft/agent-host-protocol/clients/go/ahp/hosts]
12
+ // sub-package, which adds a registry, reconnect supervisor, and
13
+ // generation-checked handles on top of the single-host [Client].
14
+ //
15
+ // # Quickstart
16
+ //
17
+ // transport, err := ahpws.Connect(ctx, "ws://localhost:12345")
18
+ // if err != nil { /* ... */ }
19
+ //
20
+ // client, err := ahp.Connect(ctx, transport, ahp.DefaultConfig())
21
+ // if err != nil { /* ... */ }
22
+ // defer client.Shutdown(ctx)
23
+ //
24
+ // if _, err := client.Initialize(ctx, "my-client",
25
+ // ahptypes.SupportedProtocolVersions(), nil); err != nil { /* ... */ }
26
+ //
27
+ // _, sub, err := client.Subscribe(ctx, "ahp-session:/s1")
28
+ // if err != nil { /* ... */ }
29
+ //
30
+ // for evt := range sub.Events() {
31
+ // // handle evt ...
32
+ // }
33
+ package ahp
34
+
35
+ import (
36
+ "context"
37
+ "encoding/json"
38
+ "errors"
39
+ "fmt"
40
+ )
41
+
42
+ // ─── Error types ─────────────────────────────────────────────────────────
43
+
44
+ // ErrClosed is returned by transports when the underlying connection
45
+ // has been cleanly closed by the remote peer.
46
+ var ErrClosed = errors.New("ahp: transport closed")
47
+
48
+ // ErrShutdown is returned by [Client] methods when the client (or its
49
+ // background driver) has been shut down.
50
+ var ErrShutdown = errors.New("ahp: client shut down")
51
+
52
+ // ErrSequenceGap is returned when an action envelope arrives out of
53
+ // sequence and the client cannot reconcile without a new snapshot. The
54
+ // caller should resubscribe to recover.
55
+ var ErrSequenceGap = errors.New("ahp: sequence gap detected; resubscribe required")
56
+
57
+ // TransportError wraps any error produced by an underlying [Transport]
58
+ // implementation so that callers can distinguish transport faults from
59
+ // protocol-level RPC errors via [errors.As].
60
+ type TransportError struct {
61
+ // Kind classifies the failure: "closed", "io", or "protocol".
62
+ Kind string
63
+ // Err is the underlying error, if any.
64
+ Err error
65
+ }
66
+
67
+ // Error implements the standard error interface.
68
+ func (e *TransportError) Error() string {
69
+ if e.Err != nil {
70
+ return fmt.Sprintf("ahp: transport %s: %v", e.Kind, e.Err)
71
+ }
72
+ return fmt.Sprintf("ahp: transport %s", e.Kind)
73
+ }
74
+
75
+ // Unwrap exposes the underlying cause for [errors.Is] / [errors.As].
76
+ func (e *TransportError) Unwrap() error { return e.Err }
77
+
78
+ // RPCError wraps a JSON-RPC error response so the caller can branch on
79
+ // it via [errors.As].
80
+ type RPCError struct {
81
+ Code int32
82
+ Message string
83
+ Data json.RawMessage
84
+ }
85
+
86
+ // Error implements the standard error interface.
87
+ func (e *RPCError) Error() string {
88
+ return fmt.Sprintf("ahp: rpc error %d: %s", e.Code, e.Message)
89
+ }
90
+
91
+ // UnknownSubscriptionError is returned by [Client.Unsubscribe] when
92
+ // the URI is not tracked by this client.
93
+ type UnknownSubscriptionError struct {
94
+ URI string
95
+ }
96
+
97
+ // Error implements the standard error interface.
98
+ func (e *UnknownSubscriptionError) Error() string {
99
+ return fmt.Sprintf("ahp: no such subscription: %s", e.URI)
100
+ }
101
+
102
+ // ─── Context helpers ────────────────────────────────────────────────────
103
+
104
+ // ctxErr converts a context cancellation into ErrShutdown when the
105
+ // context expired while waiting on the client's background driver.
106
+ func ctxErr(ctx context.Context) error {
107
+ if err := ctx.Err(); err != nil {
108
+ return err
109
+ }
110
+ return ErrShutdown
111
+ }
@@ -0,0 +1,239 @@
1
+ package ahp
2
+
3
+ import (
4
+ "sync"
5
+
6
+ "github.com/microsoft/agent-host-protocol/clients/go/ahptypes"
7
+ )
8
+
9
+ // HostedResourceKey identifies a resource on a particular host.
10
+ // Used by [MultiHostStateMirror] as the lookup key.
11
+ type HostedResourceKey struct {
12
+ HostID string
13
+ URI ahptypes.URI
14
+ }
15
+
16
+ // MultiHostStateMirror is a thread-safe map of (host, URI) → state
17
+ // snapshot. It deliberately exposes only the bare minimum needed to
18
+ // drive a UI that observes multiple hosts simultaneously: write
19
+ // snapshots in, read them back, drop them when the host or resource
20
+ // disappears.
21
+ //
22
+ // The mirror has no opinion about how snapshots are kept in sync with
23
+ // the server — that's the consumer's job, typically by feeding action
24
+ // envelopes from a [HostSubscriptionEvent] stream through the matching
25
+ // [ApplyActionToRoot] / [ApplyActionToSession] / [ApplyActionToChat] /
26
+ // [ApplyActionToTerminal] reducer and re-storing the result.
27
+ type MultiHostStateMirror struct {
28
+ mu sync.RWMutex
29
+ roots map[string]ahptypes.RootState
30
+ session map[HostedResourceKey]ahptypes.SessionState
31
+ chat map[HostedResourceKey]ahptypes.ChatState
32
+ term map[HostedResourceKey]ahptypes.TerminalState
33
+ changes map[HostedResourceKey]ahptypes.ChangesetState
34
+ automationCat map[string]ahptypes.AutomationState
35
+ automation map[HostedResourceKey]ahptypes.AutomationEntry
36
+ automationRun map[HostedResourceKey]ahptypes.AutomationRunState
37
+ }
38
+
39
+ // NewMultiHostStateMirror returns an empty mirror.
40
+ func NewMultiHostStateMirror() *MultiHostStateMirror {
41
+ return &MultiHostStateMirror{
42
+ roots: make(map[string]ahptypes.RootState),
43
+ session: make(map[HostedResourceKey]ahptypes.SessionState),
44
+ chat: make(map[HostedResourceKey]ahptypes.ChatState),
45
+ term: make(map[HostedResourceKey]ahptypes.TerminalState),
46
+ changes: make(map[HostedResourceKey]ahptypes.ChangesetState),
47
+ automationCat: make(map[string]ahptypes.AutomationState),
48
+ automation: make(map[HostedResourceKey]ahptypes.AutomationEntry),
49
+ automationRun: make(map[HostedResourceKey]ahptypes.AutomationRunState),
50
+ }
51
+ }
52
+
53
+ // PutRoot stores host's root snapshot.
54
+ func (m *MultiHostStateMirror) PutRoot(hostID string, root ahptypes.RootState) {
55
+ m.mu.Lock()
56
+ defer m.mu.Unlock()
57
+ m.roots[hostID] = root
58
+ }
59
+
60
+ // Root returns the root snapshot for hostID, or (zero, false) if
61
+ // none is recorded.
62
+ func (m *MultiHostStateMirror) Root(hostID string) (ahptypes.RootState, bool) {
63
+ m.mu.RLock()
64
+ defer m.mu.RUnlock()
65
+ v, ok := m.roots[hostID]
66
+ return v, ok
67
+ }
68
+
69
+ // PutSession stores a session snapshot under (hostID, uri).
70
+ func (m *MultiHostStateMirror) PutSession(hostID string, uri ahptypes.URI, s ahptypes.SessionState) {
71
+ m.mu.Lock()
72
+ defer m.mu.Unlock()
73
+ m.session[HostedResourceKey{hostID, uri}] = s
74
+ }
75
+
76
+ // Session returns the session snapshot at (hostID, uri), or
77
+ // (zero, false) if none is recorded.
78
+ func (m *MultiHostStateMirror) Session(hostID string, uri ahptypes.URI) (ahptypes.SessionState, bool) {
79
+ m.mu.RLock()
80
+ defer m.mu.RUnlock()
81
+ v, ok := m.session[HostedResourceKey{hostID, uri}]
82
+ return v, ok
83
+ }
84
+
85
+ // PutChat stores a chat snapshot under (hostID, uri).
86
+ func (m *MultiHostStateMirror) PutChat(hostID string, uri ahptypes.URI, c ahptypes.ChatState) {
87
+ m.mu.Lock()
88
+ defer m.mu.Unlock()
89
+ m.chat[HostedResourceKey{hostID, uri}] = c
90
+ }
91
+
92
+ // Chat returns the chat snapshot at (hostID, uri), or
93
+ // (zero, false) if none is recorded.
94
+ func (m *MultiHostStateMirror) Chat(hostID string, uri ahptypes.URI) (ahptypes.ChatState, bool) {
95
+ m.mu.RLock()
96
+ defer m.mu.RUnlock()
97
+ v, ok := m.chat[HostedResourceKey{hostID, uri}]
98
+ return v, ok
99
+ }
100
+
101
+ // PutTerminal stores a terminal snapshot under (hostID, uri).
102
+ func (m *MultiHostStateMirror) PutTerminal(hostID string, uri ahptypes.URI, t ahptypes.TerminalState) {
103
+ m.mu.Lock()
104
+ defer m.mu.Unlock()
105
+ m.term[HostedResourceKey{hostID, uri}] = t
106
+ }
107
+
108
+ // Terminal returns the terminal snapshot at (hostID, uri), or
109
+ // (zero, false) if none is recorded.
110
+ func (m *MultiHostStateMirror) Terminal(hostID string, uri ahptypes.URI) (ahptypes.TerminalState, bool) {
111
+ m.mu.RLock()
112
+ defer m.mu.RUnlock()
113
+ v, ok := m.term[HostedResourceKey{hostID, uri}]
114
+ return v, ok
115
+ }
116
+
117
+ // PutChangeset stores a changeset snapshot under (hostID, uri).
118
+ func (m *MultiHostStateMirror) PutChangeset(hostID string, uri ahptypes.URI, c ahptypes.ChangesetState) {
119
+ m.mu.Lock()
120
+ defer m.mu.Unlock()
121
+ m.changes[HostedResourceKey{hostID, uri}] = c
122
+ }
123
+
124
+ // Changeset returns the changeset snapshot at (hostID, uri), or
125
+ // (zero, false) if none is recorded.
126
+ func (m *MultiHostStateMirror) Changeset(hostID string, uri ahptypes.URI) (ahptypes.ChangesetState, bool) {
127
+ m.mu.RLock()
128
+ defer m.mu.RUnlock()
129
+ v, ok := m.changes[HostedResourceKey{hostID, uri}]
130
+ return v, ok
131
+ }
132
+
133
+ // PutAutomationCatalog stores a host's automation catalogue snapshot and
134
+ // refreshes the per-automation lookup.
135
+ func (m *MultiHostStateMirror) PutAutomationCatalog(hostID string, catalog ahptypes.AutomationState) {
136
+ m.mu.Lock()
137
+ defer m.mu.Unlock()
138
+ m.automationCat[hostID] = catalog
139
+ for key := range m.automation {
140
+ if key.HostID == hostID {
141
+ delete(m.automation, key)
142
+ }
143
+ }
144
+ for _, automation := range catalog.Entries {
145
+ m.automation[HostedResourceKey{hostID, automation.Resource}] = automation
146
+ }
147
+ }
148
+
149
+ // AutomationCatalog returns the automation catalogue for hostID.
150
+ func (m *MultiHostStateMirror) AutomationCatalog(hostID string) (ahptypes.AutomationState, bool) {
151
+ m.mu.RLock()
152
+ defer m.mu.RUnlock()
153
+ v, ok := m.automationCat[hostID]
154
+ return v, ok
155
+ }
156
+
157
+ // Automation returns the automation snapshot at (hostID, uri).
158
+ func (m *MultiHostStateMirror) Automation(hostID string, uri ahptypes.URI) (ahptypes.AutomationEntry, bool) {
159
+ m.mu.RLock()
160
+ defer m.mu.RUnlock()
161
+ v, ok := m.automation[HostedResourceKey{hostID, uri}]
162
+ return v, ok
163
+ }
164
+
165
+ // PutAutomationRun stores an automation-run snapshot under (hostID, uri).
166
+ func (m *MultiHostStateMirror) PutAutomationRun(hostID string, uri ahptypes.URI, run ahptypes.AutomationRunState) {
167
+ m.mu.Lock()
168
+ defer m.mu.Unlock()
169
+ m.automationRun[HostedResourceKey{hostID, uri}] = run
170
+ }
171
+
172
+ // AutomationRun returns the automation-run snapshot at (hostID, uri).
173
+ func (m *MultiHostStateMirror) AutomationRun(hostID string, uri ahptypes.URI) (ahptypes.AutomationRunState, bool) {
174
+ m.mu.RLock()
175
+ defer m.mu.RUnlock()
176
+ v, ok := m.automationRun[HostedResourceKey{hostID, uri}]
177
+ return v, ok
178
+ }
179
+
180
+ // DropHost removes every snapshot belonging to hostID. Use when a
181
+ // host is removed from the multi-host registry.
182
+ func (m *MultiHostStateMirror) DropHost(hostID string) {
183
+ m.mu.Lock()
184
+ defer m.mu.Unlock()
185
+ delete(m.roots, hostID)
186
+ delete(m.automationCat, hostID)
187
+ for k := range m.session {
188
+ if k.HostID == hostID {
189
+ delete(m.session, k)
190
+ }
191
+ }
192
+ for k := range m.chat {
193
+ if k.HostID == hostID {
194
+ delete(m.chat, k)
195
+ }
196
+ }
197
+ for k := range m.term {
198
+ if k.HostID == hostID {
199
+ delete(m.term, k)
200
+ }
201
+ }
202
+ for k := range m.changes {
203
+ if k.HostID == hostID {
204
+ delete(m.changes, k)
205
+ }
206
+ }
207
+ for k := range m.automation {
208
+ if k.HostID == hostID {
209
+ delete(m.automation, k)
210
+ }
211
+ }
212
+ for k := range m.automationRun {
213
+ if k.HostID == hostID {
214
+ delete(m.automationRun, k)
215
+ }
216
+ }
217
+ }
218
+
219
+ // DropResource removes the snapshot at (hostID, uri) across every
220
+ // resource kind. No-op if no snapshot exists.
221
+ func (m *MultiHostStateMirror) DropResource(hostID string, uri ahptypes.URI) {
222
+ m.mu.Lock()
223
+ defer m.mu.Unlock()
224
+ k := HostedResourceKey{hostID, uri}
225
+ if uri == "ahp-automations://" {
226
+ delete(m.automationCat, hostID)
227
+ for key := range m.automation {
228
+ if key.HostID == hostID {
229
+ delete(m.automation, key)
230
+ }
231
+ }
232
+ }
233
+ delete(m.session, k)
234
+ delete(m.chat, k)
235
+ delete(m.term, k)
236
+ delete(m.changes, k)
237
+ delete(m.automation, k)
238
+ delete(m.automationRun, k)
239
+ }