@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,31 @@
1
+ // Generated from types/*.ts โ€” do not edit.
2
+ //
3
+ // Regenerate with: npm run generate:go
4
+
5
+ package ahptypes
6
+
7
+ // ProtocolVersion is the current protocol version (SemVer
8
+ // MAJOR.MINOR.PATCH) that this generated source speaks.
9
+ const ProtocolVersion = "0.9.0"
10
+
11
+ // supportedProtocolVersions backs [SupportedProtocolVersions] โ€” held
12
+ // in an unexported slice so callers cannot accidentally mutate the
13
+ // shared backing array.
14
+ var supportedProtocolVersions = []string{
15
+ "0.9.0",
16
+ "0.8.0",
17
+ "0.7.0",
18
+ "0.6.0",
19
+ "0.5.2",
20
+ "0.5.1",
21
+ }
22
+
23
+ // SupportedProtocolVersions returns every protocol version this client
24
+ // is willing to negotiate, ordered most-preferred-first. The first
25
+ // entry always equals [ProtocolVersion]. The returned slice is a fresh
26
+ // copy on every call so callers may mutate it freely.
27
+ func SupportedProtocolVersions() []string {
28
+ out := make([]string, len(supportedProtocolVersions))
29
+ copy(out, supportedProtocolVersions)
30
+ return out
31
+ }
@@ -0,0 +1,3 @@
1
+ module github.com/microsoft/agent-host-protocol/clients/go
2
+
3
+ go 1.22
@@ -0,0 +1,91 @@
1
+ package ahp_test
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "regexp"
8
+ "sort"
9
+ "strconv"
10
+ "strings"
11
+ "testing"
12
+ )
13
+
14
+ // TestEveryUpstreamCaseHasATwin is the Skill's "exact 1:1 case mapping" gate. Every leaf case of
15
+ // the upstream test suite (testdata/upstream-tests.json, produced by running the pinned oracle) must
16
+ // have a twin(twin.Run) or a named skipped twin (twin.Skip) with the same file and title.
17
+ func TestEveryUpstreamCaseHasATwin(t *testing.T) {
18
+ raw, err := os.ReadFile(filepath.Join("testdata", "upstream-tests.json"))
19
+ if err != nil {
20
+ t.Fatal(err)
21
+ }
22
+ var upstream []struct {
23
+ File string `json:"file"`
24
+ Title string `json:"title"`
25
+ }
26
+ if err := json.Unmarshal(raw, &upstream); err != nil {
27
+ t.Fatal(err)
28
+ }
29
+
30
+ call := regexp.MustCompile(`twin\.(Run|Skip)\(\s*t,\s*"((?:[^"\\]|\\.)*)",\s*"((?:[^"\\]|\\.)*)"(?:,\s*"((?:[^"\\]|\\.)*)")?`)
31
+ have := map[string]string{} // "file\x00title" -> "run" | "skip: reason"
32
+ err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
33
+ if err != nil || info.IsDir() || !strings.HasSuffix(path, "_test.go") || strings.Contains(path, "third_party") {
34
+ return err
35
+ }
36
+ src, err := os.ReadFile(path)
37
+ if err != nil {
38
+ return err
39
+ }
40
+ for _, m := range call.FindAllStringSubmatch(string(src), -1) {
41
+ file, _ := strconv.Unquote(`"` + m[2] + `"`)
42
+ title, _ := strconv.Unquote(`"` + m[3] + `"`)
43
+ key := file + "\x00" + title
44
+ if _, dup := have[key]; dup {
45
+ t.Errorf("duplicate twin for %s: %q", file, title)
46
+ }
47
+ if m[1] == "Skip" {
48
+ reason, _ := strconv.Unquote(`"` + m[4] + `"`)
49
+ if strings.TrimSpace(reason) == "" {
50
+ t.Errorf("skipped twin without a reason: %s: %q", file, title)
51
+ }
52
+ have[key] = "skip: " + reason
53
+ } else {
54
+ have[key] = "run"
55
+ }
56
+ }
57
+ return nil
58
+ })
59
+ if err != nil {
60
+ t.Fatal(err)
61
+ }
62
+
63
+ want := map[string]bool{}
64
+ var missing []string
65
+ for _, c := range upstream {
66
+ key := c.File + "\x00" + c.Title
67
+ want[key] = true
68
+ if _, ok := have[key]; !ok {
69
+ missing = append(missing, c.File+": "+c.Title)
70
+ }
71
+ }
72
+ var extra []string
73
+ skipped := 0
74
+ for key, status := range have {
75
+ if !want[key] {
76
+ extra = append(extra, strings.ReplaceAll(key, "\x00", ": "))
77
+ }
78
+ if strings.HasPrefix(status, "skip") {
79
+ skipped++
80
+ }
81
+ }
82
+ sort.Strings(missing)
83
+ sort.Strings(extra)
84
+ t.Logf("upstream cases: %d, twins: %d run + %d skipped, missing: %d", len(upstream), len(have)-skipped, skipped, len(missing))
85
+ if len(missing) > 0 {
86
+ t.Errorf("%d upstream cases have no twin:\n %s", len(missing), strings.Join(missing, "\n "))
87
+ }
88
+ if len(extra) > 0 {
89
+ t.Errorf("%d twins name no upstream case:\n %s", len(extra), strings.Join(extra, "\n "))
90
+ }
91
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@pi-in-go/pigpen-ahp",
3
+ "version": "0.1.0",
4
+ "description": "Go port of pi-ahp: serves the running PiG session to remote Microsoft Agent Host Protocol clients over WebSocket, with reconnect and auth. No listener unless you ask; no remote filesystem or terminal unless you opt in",
5
+ "keywords": [
6
+ "pig-package",
7
+ "ahp",
8
+ "agent-host-protocol",
9
+ "remote",
10
+ "extension",
11
+ "pigpen"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Michael Kinsy",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/MichaelKinsy/pigpen.git",
18
+ "directory": "components/ahp"
19
+ },
20
+ "homepage": "https://github.com/MichaelKinsy/pigpen/tree/main/components/ahp#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/MichaelKinsy/pigpen/issues"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "files": [
28
+ "extensions",
29
+ "proof",
30
+ "README.md",
31
+ "LICENSE",
32
+ "CREDITS.md",
33
+ "provenance.json"
34
+ ],
35
+ "pi": {
36
+ "extensions": [
37
+ "extensions/ahp"
38
+ ]
39
+ }
40
+ }
package/proof/PORT.md ADDED
@@ -0,0 +1,121 @@
1
+ # Port record: ahp
2
+
3
+ (This directory is `proof/`, not the Skill's usual `port/`: the repository's port-layout test and
4
+ `test:port` treat every `port/` as a scenario/golden-trace port recorded with `pigeq`, which needs the
5
+ original to be a Pi extension. pi-ahp is a standalone host, so its proof is the twin gate, the real-pig
6
+ scenarios and the mutation list `proof/mutations.json`.)
7
+
8
+ | Input | Identity |
9
+ |---|---|
10
+ | Original | pi-ahp (`src/`, `test/`), https://github.com/Qusic/pi-ahp, commit `4065e98309b03c1d2d916300856c3c21a9c10674`, MIT (Bang Lee) |
11
+ | Protocol | Microsoft Agent Host Protocol 0.9.0, https://github.com/microsoft/agent-host-protocol, commit `296b25e7b698a4a84a0ee5a28d9573e70048a0bf`, MIT. Types and reducers vendored byte for byte (`extensions/ahp/third_party/agent-host-protocol-go`) |
12
+ | Oracle | pi-ahp under Node 24 with Pi 0.87.1: 420 of its 430 leaf cases pass here; the 10 `resource-watch` "lifetime" cases time out in this environment (they mock `setTimeout`) and are red upstream too |
13
+ | Target | PiG 0.3.0 + Pi 0.87.1 (a pre-release build, `63c6ba456`), go1.27.1, public Go SDK only |
14
+
15
+ ## What kind of port this is
16
+
17
+ pi-ahp is not a Pi extension: it is a standalone Node host that embeds Pi (`createAgentSession`) and
18
+ runs many sessions itself. The Go port is an **extension inside one PiG session**, so the
19
+ protocol host, the mapping from Pi events to AHP actions, the catalogue and the services are ported
20
+ faithfully, and the agent side is an adapter over the Go SDK that serves exactly the running
21
+ session. The equivalence method is therefore three layers, not the scenario harness alone:
22
+
23
+ 1. **Twins.** Every one of the 430 upstream test cases has a Go test named after it
24
+ (`internal/twin`; the gate `extensions/ahp/twins_test.go` fails when a case has neither a test nor
25
+ a named skip). **375 run, 55 are named skips** (below). Upstream inputs and expectations are kept;
26
+ where the Go form had to differ the test says why.
27
+ 2. **Real host.** `extensions/ahp/realpig_test.go` builds the extension with a real `pig`, serves a real PiG
28
+ session backed by a scripted local model (`tools/fakellm`), and drives it with a WebSocket AHP
29
+ client: token and Origin refusal, nothing exposed by default, catalogue, remote turn, local
30
+ prompt mirrored, opt-in filesystem and terminal, resumed session history, remote cancel,
31
+ non-loopback without token, listener off unless asked, session replacement. The same eight
32
+ scenarios pass against a Piglet Binary with the extension fused in (`PIGPEN_AHP_FUSED=1`).
33
+ 3. **Mutation.** `proof/mutations.json` (run with `pigeq mutate --unit-only`) lists mutants of the
34
+ security and reconciliation checks. Each one is killed by a unit test. **No protocol differential
35
+ against pi-ahp under Pi, and no interop run with Microsoft's Go client, has been done.** Both are
36
+ open.
37
+
38
+ ## Mapping
39
+
40
+ | Upstream | Go | Twins |
41
+ |---|---|---|
42
+ | `src/protocol/*`, `core/channels.ts`, `core/uri.ts` | `internal/wire` | handshake, uri, protocol-surface |
43
+ | `core/host.ts`, `sequencer.ts`, `client-workarounds.ts` | `internal/host` (+ `internal/ws`, an RFC 6455 server on `net/http` Hijack) | handshake 14, subscriptions 9, reconnect 9, client-workarounds 14, schema 4, upstream-workarounds 2 |
44
+ | `pi/event-mapper.ts`, `activity.ts`, `user-message.ts`, `message-input.ts`, `session-title.ts` | `internal/mapper` | event-mapper 27, activity 14, message-input 12, mapper-fixtures 25 |
45
+ | `channels/*` | `internal/channels` | session-summary 13 |
46
+ | `pi/chat-driver.ts` | `internal/pi/chatdriver.go` | chat-driver 13 |
47
+ | `pi/session-registry.ts`, `session-catalogue.ts`, `session-hydrator.ts`, `history.ts`, `turn-paging`, `delete-session.ts` | `internal/pi` | session-lifecycle 17, disposal 6, client-actions 6, catalogue 11, hydration 8, hydrated lifecycle 9, fetch-turns 4, truncate 5, delete-session 6, active-turn-reconnect 3, session-storage 1, reconnect-after-restart 1 |
48
+ | `pi/models.ts`, `session-config.ts`, `completions.ts`, `project-trust.ts` | `internal/pi` | models 10, session-config 8, completions 22, project-trust 6 |
49
+ | Pi `SessionManager` (JSONL v3) | `internal/pisession` | used by the above |
50
+ | `pi/resource-*.ts` | `internal/svc` (`resource.go`, `watch.go`, `glob.go`, `watchpolicy.go`) | resource 23, resource-watch 28, policy 9, watch-events 4 |
51
+ | `host/terminal-service.ts` | `internal/svc` (`terminal.go`, `pty*.go`) | terminal-service 14, pty 2 |
52
+ | `pi/image-input.ts` | `internal/pi/imageinput.go` | image-input 2 |
53
+ | `host/pi-host.ts` | `internal/compose` | pi-host 1 |
54
+ | `host/direct-settings.ts` | `internal/settings` | direct-settings 3 |
55
+ | `pi/in-process-backend.ts` | `internal/live` (adapter over the SDK; not a port) | real-pig scenarios |
56
+ | (none) | root package: flags, `/ahp`, listener lifecycle | real-pig scenarios |
57
+
58
+ ## Named skipped twins (55)
59
+
60
+ | Family | Count | Why |
61
+ |---|---|---|
62
+ | `tunnel` | 20 | The dev tunnel (Microsoft `devtunnel` CLI management) is not bundled: owner decision, ACP, AHP and A2A are separate extensions |
63
+ | `pi-replay` | 15 | Replays provider streams through a live embedded Pi agent loop. Covered by the recorded-stream twins (`mapper-fixtures`) and the real-pig scenarios |
64
+ | `changeset`, `changeset-lifecycle`, `changeset-uri` | 15 | The git changeset service (`git-changes.ts`, `changeset-service.ts`) is not ported in this release; the host advertises no changeset channels. Documented gap, candidate for the next release |
65
+ | `image-session` | 1 | Needs Pi's `InProcessPiBackend` and a faux model |
66
+ | `model-discovery` | 2 | Needs an embedded Pi model registry with extension providers; the adapter reads models through the SDK |
67
+ | `live-turn` | 1 | Upstream skips it itself without a real model |
68
+ | `project-trust` | 1 | "applies the decision before pi loads project extensions": the extension cannot gate what PiG loaded |
69
+
70
+ ## Deviations from upstream (deliberate)
71
+
72
+ 1. **One session.** Only the running PiG session has an agent. Other sessions in the catalogue are
73
+ read from their files (browse, history); starting a turn on one fails with a clear error.
74
+ Clients cannot create sessions, and `chat/truncated`, fork and tree navigation are refused: the
75
+ SDK offers `NewSession`, `Fork` and `NavigateTree` to commands only.
76
+ 2. **Session replacement.** Contexts retained from `session_start` belong to their runtime. On
77
+ `new`, `resume`, `fork` and `reload`, PiG emits `session_shutdown` and runs the extension factory
78
+ again, so the listener is stopped. With `--ahp` the new session's `session_start` opens it again
79
+ on the same port. After `/ahp start` it stays closed, and a warning tells the user
80
+ (`TestRealPigSessionReplacement`, review fix).
81
+ 3. **Nothing on by default.** Upstream's standalone process is the listener; the extension opens none
82
+ without `--ahp` or `/ahp start`. Upstream's resource service is unrestricted without roots; here
83
+ the filesystem is off, then confined to the working directory, and only `unrestricted: true`
84
+ lifts that. Terminals and session deletion are opt-in settings. Non-loopback needs a token.
85
+ 4. **Foreign turns.** A prompt typed into the session locally is mirrored into the chat
86
+ (`AdoptForeignTurns`); upstream owns its agent and never sees one.
87
+ 5. **Steering.** PiG has no `queue_update` event; the adapter synthesises it from the delivered user
88
+ message so pending-steering reconciliation works.
89
+ 6. **Newer spec, older client library.** The pinned spec has `chat/isArchivedChanged` and
90
+ `session/mcpServerBackgroundRequested`, which npm 0.9.0 lacks; both are refused and the
91
+ client-actions twin expects that. The spec has no `Conflict` error code; -32011 is kept from npm 0.9.0.
92
+ 7. **Polling watches.** No portable file notification exists in the standard library, so a resource
93
+ watch diffs a snapshot (default every second, excluded directories pruned) where chokidar uses
94
+ native events. Batching, filters, grace and disposal are upstream's.
95
+ 8. **PTY.** A stdlib-only PTY (`/dev/ptmx` + ioctl) on linux and darwin. Windows has none; the terminal
96
+ service refuses to start there. A signalled child reports `128+signal`.
97
+ 9. **Images.** Pi's `convertToPng`/`resizeImage` are not in the SDK: PNG, JPEG, GIF decode with the
98
+ standard library, BMP with a small decoder, resizing is a box filter, WebP passes through unvalidated
99
+ beyond its header.
100
+ 10. **MIME table** is the subset of the `mime` package that matters for source trees.
101
+ 11. **Async side effects.** Registry and driver effects run on goroutines where upstream runs them in
102
+ the same tick; a few twins wait for the backend call before the next step. `host.drain` defers
103
+ re-entrant listener callbacks; the driver serialises events (`evMu`).
104
+ 12. **JS artefacts.** Echoed tool arguments have alphabetical key order (events arrive decoded); UTF-16
105
+ cuts at a lone surrogate yield U+FFFD; `localeCompare` is approximated (`localeLess`); terminal trimming counts runes.
106
+ 13. **Vendored client library.** Its reconnect results marshal without the `type` discriminator, so the
107
+ host has its own structs; its `SnapshotState` cannot decode a terminal snapshot (tests read the raw result).
108
+ 14. **Tests written with the code.** From the third layer on, tests and implementation were committed
109
+ together (failing runs observed before fixes) rather than as a separate red commit.
110
+ 15. **403 for a bad token.** A missing or wrong `token`/`tkn` is refused at the upgrade with 403 Forbidden, as VS Code's
111
+ agent host (the AHP reference host, `src/vs/platform/agentHost/node/webSocketTransport.ts`) answers a bad `tkn`; pi-ahp
112
+ writes 401. The AHP spec leaves endpoint access to the transport, upstream's twin only asserts the refusal, and no
113
+ client tells the two apart (a browser WebSocket reports either as one error); a 401 would also owe a `WWW-Authenticate`
114
+ challenge that a query token has no scheme for. A browser `Origin` that is not allowed is 403 as before.
115
+
116
+ ## Build notes
117
+
118
+ - A fused Piglet Binary ignores `replace` directives of the extension's `go.mod`; a `go.work` beside it
119
+ (`use . ./third_party/agent-host-protocol-go`) is how the vendored module is found.
120
+ - PiG 0.4.0 (Pi 0.99.1): re-check `session_start` retained contexts across session replacement, whether
121
+ `Fork`/`NewSession` become callable from events, any `queue_update` event, and the SDK's image helpers.
@@ -0,0 +1,152 @@
1
+ [
2
+ {
3
+ "name": "ws-token-check-dropped",
4
+ "file": "internal/ws/ws.go",
5
+ "find": "if !ok || subtle.ConstantTimeCompare([]byte(presented), []byte(s.opts.Token)) != 1 {",
6
+ "replace": "if false && (!ok || subtle.ConstantTimeCompare([]byte(presented), []byte(s.opts.Token)) != 1) {"
7
+ },
8
+ {
9
+ "name": "ws-token-any-accepted",
10
+ "file": "internal/ws/ws.go",
11
+ "find": "subtle.ConstantTimeCompare([]byte(presented), []byte(s.opts.Token)) != 1",
12
+ "replace": "subtle.ConstantTimeCompare([]byte(presented), []byte(presented)) != 1"
13
+ },
14
+ {
15
+ "name": "ws-origin-not-enforced",
16
+ "file": "internal/ws/ws.go",
17
+ "find": "origin != \"\" && !s.originAllowed(origin)",
18
+ "replace": "origin != \"\" && false"
19
+ },
20
+ {
21
+ "name": "ws-origin-allowlist-inverted",
22
+ "file": "internal/ws/ws.go",
23
+ "find": "if strings.EqualFold(allowed, origin) {\n\t\t\treturn true",
24
+ "replace": "if !strings.EqualFold(allowed, origin) {\n\t\t\treturn true"
25
+ },
26
+ {
27
+ "name": "paths-prefix-check-loose",
28
+ "file": "internal/svc/paths.go",
29
+ "find": "if candidate == root || strings.HasPrefix(candidate, prefix) {",
30
+ "replace": "if candidate == root || strings.HasPrefix(candidate, strings.TrimSuffix(prefix, string(filepath.Separator))) {"
31
+ },
32
+ {
33
+ "name": "paths-roots-ignored",
34
+ "file": "internal/svc/paths.go",
35
+ "find": "\tif len(p.roots) == 0 {\n\t\treturn path, nil\n\t}",
36
+ "replace": "\tif true {\n\t\treturn path, nil\n\t}"
37
+ },
38
+ {
39
+ "name": "resource-ifmatch-ignored",
40
+ "file": "internal/svc/resource.go",
41
+ "find": "if !exists || *p.IfMatch != current {",
42
+ "replace": "if !exists || (false && *p.IfMatch != current) {"
43
+ },
44
+ {
45
+ "name": "terminal-claim-not-checked",
46
+ "file": "internal/svc/terminal.go",
47
+ "find": "if !ok || claim.ClientId != clientID {\n\t\treturn \"This terminal is claimed by another client\"",
48
+ "replace": "if !ok || (false && claim.ClientId != clientID) {\n\t\treturn \"This terminal is claimed by another client\""
49
+ },
50
+ {
51
+ "name": "terminal-claim-must-match-initialized-client",
52
+ "file": "internal/svc/terminal.go",
53
+ "find": "if clientID == \"\" || claim.ClientId != clientID {",
54
+ "replace": "if clientID == \"\" || (false && claim.ClientId != clientID) {"
55
+ },
56
+ {
57
+ "name": "compose-unrestricted-implied",
58
+ "file": "internal/compose/compose.go",
59
+ "find": "if len(fs.Roots) == 0 && !fs.Unrestricted {",
60
+ "replace": "if false {"
61
+ },
62
+ {
63
+ "name": "compose-roots-dropped",
64
+ "file": "internal/compose/compose.go",
65
+ "find": "\t\tif !fs.Unrestricted {\n\t\t\troots = fs.Roots\n\t\t}",
66
+ "replace": "\t\tif false {\n\t\t\troots = fs.Roots\n\t\t}"
67
+ },
68
+ {
69
+ "name": "driver-foreign-turn-while-cancelling",
70
+ "file": "internal/pi/chatdriver.go",
71
+ "find": "\tif d.quiesced || d.cancellation != nil || d.mapper != nil {\n\t\td.mu.Unlock()\n\t\treturn nil\n\t}",
72
+ "replace": "\tif d.quiesced || d.mapper != nil {\n\t\td.mu.Unlock()\n\t\treturn nil\n\t}"
73
+ },
74
+ {
75
+ "name": "driver-foreign-turn-any-role",
76
+ "file": "internal/pi/chatdriver.go",
77
+ "find": "if message == nil || message[\"role\"] != \"user\" {",
78
+ "replace": "if message == nil {"
79
+ },
80
+ {
81
+ "name": "host-replay-window-off-by-one",
82
+ "file": "internal/host/host.go",
83
+ "find": "return len(h.buf) > 0 && h.buf[0].ServerSeq <= lastSeen+1",
84
+ "replace": "return len(h.buf) > 0 && h.buf[0].ServerSeq <= lastSeen+2"
85
+ },
86
+ {
87
+ "name": "host-replay-always",
88
+ "file": "internal/host/host.go",
89
+ "find": "\tif lastSeen >= h.seq {\n\t\treturn true\n\t}\n\treturn len(h.buf) > 0 && h.buf[0].ServerSeq <= lastSeen+1",
90
+ "replace": "\treturn true"
91
+ },
92
+ {
93
+ "name": "store-update-without-lock",
94
+ "file": "internal/host/store.go",
95
+ "find": "func (s *Store) UpdateChat(uri string, fn func(*ahptypes.ChatState) bool) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()",
96
+ "replace": "func (s *Store) UpdateChat(uri string, fn func(*ahptypes.ChatState) bool) bool {"
97
+ },
98
+ {
99
+ "name": "watch-racy-hash-off",
100
+ "file": "internal/svc/watch.go",
101
+ "find": "if recent || (seen && old.hash != \"\" && old.sig == entry.sig) {",
102
+ "replace": "if false && (recent || (seen && old.hash != \"\" && old.sig == entry.sig)) {"
103
+ },
104
+ {
105
+ "name": "delete-refused-unless-allowed",
106
+ "file": "runtime.go",
107
+ "find": "if !allow {",
108
+ "replace": "if false {"
109
+ },
110
+ {
111
+ "name": "delete-live-file-allowed",
112
+ "file": "runtime.go",
113
+ "find": "filepath.Clean(live) == filepath.Clean(path)",
114
+ "replace": "false && filepath.Clean(live) == filepath.Clean(path)"
115
+ },
116
+ {
117
+ "name": "nonloopback-without-token-allowed",
118
+ "file": "runtime.go",
119
+ "find": "if !isLoopback(host) && token == \"\" {",
120
+ "replace": "if false {"
121
+ },
122
+ {
123
+ "name": "loopback-localhost-only",
124
+ "file": "runtime.go",
125
+ "find": "if host == \"localhost\" {\n\t\treturn true\n\t}",
126
+ "replace": "if host == \"localhost\" {\n\t\treturn false\n\t}"
127
+ },
128
+ {
129
+ "name": "runtime-default-root-widened",
130
+ "file": "runtime.go",
131
+ "find": "roots = []string{cwd}",
132
+ "replace": "roots = []string{filepath.Dir(cwd)}"
133
+ },
134
+ {
135
+ "name": "runtime-terminals-always",
136
+ "file": "runtime.go",
137
+ "find": "if access.Terminals.Enabled {",
138
+ "replace": "if true || access.Terminals.Enabled {"
139
+ },
140
+ {
141
+ "name": "runtime-filesystem-always",
142
+ "file": "runtime.go",
143
+ "find": "if access.Filesystem.Enabled {",
144
+ "replace": "if true || access.Filesystem.Enabled {"
145
+ },
146
+ {
147
+ "name": "runtime-replacement-silent",
148
+ "file": "runtime.go",
149
+ "find": "if !running || restarts || reason == \"quit\" {",
150
+ "replace": "if true || !running || restarts || reason == \"quit\" {"
151
+ }
152
+ ]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bang Lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ # pi-ahp
2
+
3
+ [![CI](https://github.com/Qusic/pi-ahp/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/Qusic/pi-ahp/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/pi-ahp)](https://www.npmjs.com/package/pi-ahp)
5
+ [![pi](https://img.shields.io/npm/dependency-version/pi-ahp/@earendil-works/pi-coding-agent?label=pi)](https://github.com/earendil-works/pi)
6
+ [![AHP](https://img.shields.io/npm/dependency-version/pi-ahp/@microsoft/agent-host-protocol?label=AHP)](https://github.com/microsoft/agent-host-protocol)
7
+ [![License](https://img.shields.io/npm/l/pi-ahp)](LICENSE)
8
+
9
+ `pi-ahp` is an [Agent Host Protocol](https://github.com/microsoft/agent-host-protocol) host for the [pi coding agent](https://github.com/earendil-works/pi).
10
+
11
+ It embeds pi and makes its sessions available to AHP clients over WebSocket. Interoperability is primarily tested with [Agent Host support in Visual Studio Code](https://code.visualstudio.com/docs/agents/concepts/agent-host) and [Agent Console for iPhone and iPad](https://qusic.github.io/agent-console/), but the host is not limited to either client.
12
+
13
+ The project is under active development; expect rough edges and occasional instability. Contributions are welcome.
14
+
15
+ ## Requirements
16
+
17
+ - Node.js 24 or later
18
+ - At least one model provider configured for pi
19
+
20
+ ## Usage
21
+
22
+ ```sh
23
+ npm install --global pi-ahp
24
+ ```
25
+
26
+ `pi-ahp` starts a direct WebSocket listener and prints its connection URL:
27
+
28
+ ```sh
29
+ pi-ahp
30
+ ```
31
+
32
+ On first run, it creates `~/.pi/ahp/settings.json` with a free port and a random token, for example:
33
+
34
+ ```json
35
+ {
36
+ "host": "127.0.0.1",
37
+ "port": 32145,
38
+ "token": "<generated token>"
39
+ }
40
+ ```
41
+
42
+ The token is optional and may be set to `null`. The host and port can also be overridden for one run:
43
+
44
+ ```sh
45
+ pi-ahp --host 127.0.0.1 --port 31546
46
+ ```
47
+
48
+ Enter the printed URL in any AHP client. The direct listener is bring-your-own-network: reach it through Tailscale, SSH port forwarding, a reverse proxy, or any other network you manage. Keep the connection token enabled whenever the listener is reachable beyond localhost.
49
+
50
+ `pi-ahp-tunnel` instead creates or reuses a Microsoft Dev Tunnel for clients that support it, including VS Code and Agent Console. It requires the [`devtunnel` CLI](https://aka.ms/devtunnels/download) and does not use the direct-listener settings:
51
+
52
+ ```sh
53
+ devtunnel user login
54
+ pi-ahp-tunnel
55
+ ```
56
+
57
+ Run either command with `--help` for all available options.
58
+
59
+ ### Visual Studio Code
60
+
61
+ VS Code exposes remote Agent Host setup only in its dedicated Agents window, not in a regular editor window. From a regular window, run **Chat: Open Agents Window**.
62
+
63
+ If the remote-host commands are unavailable, or if you want to use a direct connection without signing in to GitHub, add these settings to VS Code's user `settings.json` before opening the Agents window:
64
+
65
+ ```json
66
+ {
67
+ "chat.remoteAgentHostsEnabled": true,
68
+ "chat.agentHost.allowSignedOutWhenUsable": true
69
+ }
70
+ ```
71
+
72
+ The signed-out option is currently experimental and desktop-only. When prompted, choose **Continue Without Signing In**; you do not need to preconfigure a host manually.
73
+
74
+ - **Direct WebSocket:** run **Agents: Add Remote Agent Host...** and paste the URL printed by `pi-ahp`.
75
+ - **Dev Tunnel:** sign in to the same account in VS Code, then run **Agents: Connect to Remote Agent Host via Dev Tunnel**. Tunnel discovery requires sign-in.
76
+
77
+ ### iPhone and iPad
78
+
79
+ I also make [Agent Console](https://qusic.github.io/agent-console/), a paid native AHP client for iPhone and iPad. Purchasing it helps support continued work on both Agent Console and `pi-ahp`.
80
+
81
+ Agent Console connects with the direct URL printed by `pi-ahp`, or through a tunnel created by `pi-ahp-tunnel` when signed in to the same Dev Tunnels account. pi and its model credentials remain on the host, and Agent Console has no hosted backend. See the [Agent Console setup guide for Pi Coding Agent](https://qusic.github.io/agent-console/docs/pi-coding-agent/) for details.
82
+
83
+ ## AHP support
84
+
85
+ The following is a high-level mapping to the [official AHP specification](https://microsoft.github.io/agent-host-protocol/specification/overview).
86
+
87
+ Currently supported protocol versions: **0.9.0**
88
+
89
+ ๐ŸŸข Supported ยท ๐ŸŸก Planned ยท ๐Ÿ”ด Out of scope
90
+
91
+ | Feature | Status | Notes |
92
+ | --- | :---: | --- |
93
+ | Connection lifecycle and reconnect | ๐ŸŸข | Active work survives a temporary connection loss, but not a host restart |
94
+ | Durable sessions and history | ๐ŸŸข | Completed history for the active conversation branch recovers after restart |
95
+ | Streaming chat and pi tool calls | ๐ŸŸข | One chat per session |
96
+ | Steering, queues, drafts, and truncation | ๐ŸŸข | Drafts and unconsumed queues do not survive a host restart |
97
+ | Models, session setup, and completions | ๐ŸŸข | Model and thinking-level selection, working directory, and `@` file completions |
98
+ | Text attachments | ๐ŸŸข | Client-provided text, local file references, and embedded UTF-8 text |
99
+ | Files and watches | ๐ŸŸข | Host-local `file:` resources only |
100
+ | Interactive terminals | ๐ŸŸข | Client-owned; terminals and scrollback end when the host stops |
101
+ | Images and vision | ๐ŸŸข | Embedded image attachments for vision-capable pi models |
102
+ | Changeset views | ๐ŸŸข | Latest commit and uncommitted changes in Git workspaces |
103
+ | Tool catalogue and client tools | ๐ŸŸก | Show available pi tools and let connected clients contribute tools |
104
+ | pi customizations | ๐ŸŸก | Show and configure loaded extensions, skills, and prompt templates |
105
+ | Terminal command detection | ๐ŸŸก | Group terminal output by command and exit status |
106
+ | Persistent read and archive controls | ๐Ÿ”ด | pi sessions do not store this metadata |
107
+ | Multiple chats or working directories | ๐Ÿ”ด | A pi session has one active branch and cwd; branches are not simultaneous AHP chats |
108
+ | Custom agents | ๐Ÿ”ด | pi sub-agents are extension tools rather than selectable agents |
109
+ | Elicitation | ๐Ÿ”ด | pi extension dialogs are not persisted as turn input |
110
+ | Tool input and result confirmation | ๐Ÿ”ด | pi has no built-in permission policy |
111
+ | Errored-turn resume | ๐Ÿ”ด | pi cannot reopen a finalized turn |
112
+ | MCP lifecycle and Apps | ๐Ÿ”ด | pi has no built-in MCP runtime; extension implementations are opaque |
113
+ | Annotations | ๐Ÿ”ด | There is no corresponding pi session state |
114
+ | Automations and automation runs | ๐Ÿ”ด | pi has no scheduler or automation-run model |
115
+ | AHP protected-resource authentication | ๐Ÿ”ด | pi manages model-provider credentials itself |
116
+ | OTLP telemetry channels | ๐Ÿ”ด | pi does not provide host telemetry as OTLP |
117
+
118
+ ## License
119
+
120
+ [MIT](LICENSE)
@@ -0,0 +1,7 @@
1
+ # Oracle: pi-ahp
2
+
3
+ Unmodified copy of `src/`, `test/` and the package metadata of https://github.com/Qusic/pi-ahp
4
+ (Bang Lee, MIT, see `LICENSE`) at commit `4065e98309b03c1d2d916300856c3c21a9c10674`. It is the
5
+ equivalence oracle: 430 leaf test cases (`../upstream-tests.json`), each with a Go twin or a named
6
+ skipped twin (`../PORT.md`). To run it: install with `pnpm install` in a checkout of upstream, then
7
+ `AHP_SPEC_PATH=<agent-host-protocol checkout at 296b25e7b698a4a84a0ee5a28d9573e70048a0bf> node --test test/<file>.test.ts`.
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "pi-ahp",
3
+ "version": "0.0.0",
4
+ "description": "Agent Host Protocol server for Pi coding agent",
5
+ "keywords": [
6
+ "agent-host-protocol",
7
+ "ahp",
8
+ "agent-host",
9
+ "pi",
10
+ "pi-coding-agent",
11
+ "coding-agent",
12
+ "ai-agent",
13
+ "agent",
14
+ "ai",
15
+ "llm",
16
+ "cli",
17
+ "json-rpc",
18
+ "websocket",
19
+ "vscode"
20
+ ],
21
+ "homepage": "https://github.com/Qusic/pi-ahp#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/Qusic/pi-ahp/issues"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/Qusic/pi-ahp.git"
28
+ },
29
+ "license": "MIT",
30
+ "type": "module",
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "bin": {
35
+ "pi-ahp": "dist/bin/cli.js",
36
+ "pi-ahp-tunnel": "dist/bin/tunnel.js"
37
+ },
38
+ "engines": {
39
+ "node": ">=24.0.0"
40
+ },
41
+ "scripts": {
42
+ "start": "node src/bin/cli.ts",
43
+ "test": "node --test \"test/**/*.test.ts\" && node --test --test-concurrency=1 \"test/**/*.test.serial.ts\"",
44
+ "check": "tsc",
45
+ "lint": "biome check",
46
+ "format": "biome check --write",
47
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
48
+ "release": "semantic-release",
49
+ "prepack": "pnpm run build"
50
+ },
51
+ "dependencies": {
52
+ "@earendil-works/pi-ai": "0.87.1",
53
+ "@earendil-works/pi-coding-agent": "0.87.1",
54
+ "@microsoft/agent-host-protocol": "0.9.0",
55
+ "chokidar": "^5.0.0",
56
+ "mime": "^4.1.0",
57
+ "node-pty": "^1.2.0-beta.15",
58
+ "ws": "^8.21.3"
59
+ },
60
+ "devDependencies": {
61
+ "@biomejs/biome": "^2.5.14",
62
+ "@types/node": "^24.13.6",
63
+ "@types/ws": "^8.18.1",
64
+ "ajv": "^8.20.0",
65
+ "semantic-release": "^25.0.9",
66
+ "typescript": "^7.0.2"
67
+ }
68
+ }