@geminixiang/mikan 1.0.0-beta.2 → 1.0.0-beta.20

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 (412) hide show
  1. package/CHANGELOG.md +354 -0
  2. package/README.md +42 -16
  3. package/dist/adapter.d.ts +1 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/adapters/discord/bot.d.ts +1 -0
  7. package/dist/adapters/discord/bot.d.ts.map +1 -1
  8. package/dist/adapters/discord/bot.js +36 -67
  9. package/dist/adapters/discord/bot.js.map +1 -1
  10. package/dist/adapters/discord/context.d.ts.map +1 -1
  11. package/dist/adapters/discord/context.js +17 -42
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/github/bot.d.ts +148 -0
  14. package/dist/adapters/github/bot.d.ts.map +1 -0
  15. package/dist/adapters/github/bot.js +1031 -0
  16. package/dist/adapters/github/bot.js.map +1 -0
  17. package/dist/adapters/github/client.d.ts +118 -0
  18. package/dist/adapters/github/client.d.ts.map +1 -0
  19. package/dist/adapters/github/client.js +309 -0
  20. package/dist/adapters/github/client.js.map +1 -0
  21. package/dist/adapters/github/cloudbuild.d.ts +24 -0
  22. package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
  23. package/dist/adapters/github/cloudbuild.js +71 -0
  24. package/dist/adapters/github/cloudbuild.js.map +1 -0
  25. package/dist/adapters/github/context.d.ts +9 -0
  26. package/dist/adapters/github/context.d.ts.map +1 -0
  27. package/dist/adapters/github/context.js +175 -0
  28. package/dist/adapters/github/context.js.map +1 -0
  29. package/dist/adapters/github/gcp-auth.d.ts +31 -0
  30. package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
  31. package/dist/adapters/github/gcp-auth.js +196 -0
  32. package/dist/adapters/github/gcp-auth.js.map +1 -0
  33. package/dist/adapters/github/ids.d.ts +34 -0
  34. package/dist/adapters/github/ids.d.ts.map +1 -0
  35. package/dist/adapters/github/ids.js +53 -0
  36. package/dist/adapters/github/ids.js.map +1 -0
  37. package/dist/adapters/github/repo.d.ts +30 -0
  38. package/dist/adapters/github/repo.d.ts.map +1 -0
  39. package/dist/adapters/github/repo.js +136 -0
  40. package/dist/adapters/github/repo.js.map +1 -0
  41. package/dist/adapters/github/tool-pack.d.ts +14 -0
  42. package/dist/adapters/github/tool-pack.d.ts.map +1 -0
  43. package/dist/adapters/github/tool-pack.js +56 -0
  44. package/dist/adapters/github/tool-pack.js.map +1 -0
  45. package/dist/adapters/github/tools/checks.d.ts +24 -0
  46. package/dist/adapters/github/tools/checks.d.ts.map +1 -0
  47. package/dist/adapters/github/tools/checks.js +116 -0
  48. package/dist/adapters/github/tools/checks.js.map +1 -0
  49. package/dist/adapters/github/tools/issue.d.ts +22 -0
  50. package/dist/adapters/github/tools/issue.d.ts.map +1 -0
  51. package/dist/adapters/github/tools/issue.js +59 -0
  52. package/dist/adapters/github/tools/issue.js.map +1 -0
  53. package/dist/adapters/github/tools/pr.d.ts +22 -0
  54. package/dist/adapters/github/tools/pr.d.ts.map +1 -0
  55. package/dist/adapters/github/tools/pr.js +59 -0
  56. package/dist/adapters/github/tools/pr.js.map +1 -0
  57. package/dist/adapters/github/tools/read.d.ts +22 -0
  58. package/dist/adapters/github/tools/read.d.ts.map +1 -0
  59. package/dist/adapters/github/tools/read.js +152 -0
  60. package/dist/adapters/github/tools/read.js.map +1 -0
  61. package/dist/adapters/github/tools/review-reply.d.ts +19 -0
  62. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
  63. package/dist/adapters/github/tools/review-reply.js +49 -0
  64. package/dist/adapters/github/tools/review-reply.js.map +1 -0
  65. package/dist/adapters/github/tools/sync.d.ts +17 -0
  66. package/dist/adapters/github/tools/sync.d.ts.map +1 -0
  67. package/dist/adapters/github/tools/sync.js +45 -0
  68. package/dist/adapters/github/tools/sync.js.map +1 -0
  69. package/dist/adapters/github/types.d.ts +350 -0
  70. package/dist/adapters/github/types.d.ts.map +1 -0
  71. package/dist/adapters/github/types.js +2 -0
  72. package/dist/adapters/github/types.js.map +1 -0
  73. package/dist/adapters/intake.d.ts +16 -5
  74. package/dist/adapters/intake.d.ts.map +1 -1
  75. package/dist/adapters/intake.js +79 -22
  76. package/dist/adapters/intake.js.map +1 -1
  77. package/dist/adapters/slack/bot.d.ts +8 -6
  78. package/dist/adapters/slack/bot.d.ts.map +1 -1
  79. package/dist/adapters/slack/bot.js +81 -133
  80. package/dist/adapters/slack/bot.js.map +1 -1
  81. package/dist/adapters/slack/context.d.ts.map +1 -1
  82. package/dist/adapters/slack/context.js +3 -14
  83. package/dist/adapters/slack/context.js.map +1 -1
  84. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  85. package/dist/adapters/slack/response-lifecycle.js +14 -17
  86. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  87. package/dist/adapters/slack/session.d.ts.map +1 -1
  88. package/dist/adapters/slack/session.js +5 -8
  89. package/dist/adapters/slack/session.js.map +1 -1
  90. package/dist/adapters/streaming.d.ts +4 -0
  91. package/dist/adapters/streaming.d.ts.map +1 -1
  92. package/dist/adapters/streaming.js +11 -0
  93. package/dist/adapters/streaming.js.map +1 -1
  94. package/dist/adapters/telegram/bot.d.ts +1 -2
  95. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  96. package/dist/adapters/telegram/bot.js +27 -62
  97. package/dist/adapters/telegram/bot.js.map +1 -1
  98. package/dist/adapters/telegram/context.d.ts.map +1 -1
  99. package/dist/adapters/telegram/context.js +8 -27
  100. package/dist/adapters/telegram/context.js.map +1 -1
  101. package/dist/adapters/types.d.ts +28 -2
  102. package/dist/adapters/types.d.ts.map +1 -1
  103. package/dist/adapters/types.js.map +1 -1
  104. package/dist/agent.d.ts +38 -14
  105. package/dist/agent.d.ts.map +1 -1
  106. package/dist/agent.js +358 -224
  107. package/dist/agent.js.map +1 -1
  108. package/dist/cli/download.d.ts.map +1 -0
  109. package/dist/cli/download.js.map +1 -0
  110. package/dist/cli/ext-git.d.ts +22 -0
  111. package/dist/cli/ext-git.d.ts.map +1 -0
  112. package/dist/cli/ext-git.js +76 -0
  113. package/dist/cli/ext-git.js.map +1 -0
  114. package/dist/cli/ext.d.ts +2 -0
  115. package/dist/cli/ext.d.ts.map +1 -0
  116. package/dist/cli/ext.js +217 -0
  117. package/dist/cli/ext.js.map +1 -0
  118. package/dist/commands/admin.d.ts.map +1 -1
  119. package/dist/commands/admin.js +2 -1
  120. package/dist/commands/admin.js.map +1 -1
  121. package/dist/commands/auto-reply.d.ts.map +1 -1
  122. package/dist/commands/auto-reply.js +2 -6
  123. package/dist/commands/auto-reply.js.map +1 -1
  124. package/dist/commands/extensions.d.ts.map +1 -1
  125. package/dist/commands/extensions.js +16 -6
  126. package/dist/commands/extensions.js.map +1 -1
  127. package/dist/commands/login.d.ts +2 -1
  128. package/dist/commands/login.d.ts.map +1 -1
  129. package/dist/commands/login.js +30 -1
  130. package/dist/commands/login.js.map +1 -1
  131. package/dist/commands/manifest.d.ts +72 -0
  132. package/dist/commands/manifest.d.ts.map +1 -0
  133. package/dist/commands/manifest.js +121 -0
  134. package/dist/commands/manifest.js.map +1 -0
  135. package/dist/commands/model.d.ts.map +1 -1
  136. package/dist/commands/model.js +3 -1
  137. package/dist/commands/model.js.map +1 -1
  138. package/dist/commands/new.d.ts.map +1 -1
  139. package/dist/commands/new.js +2 -1
  140. package/dist/commands/new.js.map +1 -1
  141. package/dist/commands/sandbox.d.ts.map +1 -1
  142. package/dist/commands/sandbox.js +16 -12
  143. package/dist/commands/sandbox.js.map +1 -1
  144. package/dist/commands/text.d.ts +2 -0
  145. package/dist/commands/text.d.ts.map +1 -0
  146. package/dist/commands/text.js +16 -0
  147. package/dist/commands/text.js.map +1 -0
  148. package/dist/commands/types.d.ts +14 -1
  149. package/dist/commands/types.d.ts.map +1 -1
  150. package/dist/commands/types.js.map +1 -1
  151. package/dist/config.d.ts +1 -1
  152. package/dist/config.d.ts.map +1 -1
  153. package/dist/config.js +90 -54
  154. package/dist/config.js.map +1 -1
  155. package/dist/events.d.ts +10 -0
  156. package/dist/events.d.ts.map +1 -1
  157. package/dist/events.js +14 -5
  158. package/dist/events.js.map +1 -1
  159. package/dist/execution-resolver.d.ts +3 -2
  160. package/dist/execution-resolver.d.ts.map +1 -1
  161. package/dist/execution-resolver.js +28 -12
  162. package/dist/execution-resolver.js.map +1 -1
  163. package/dist/harness/extensions/loader.d.ts +39 -9
  164. package/dist/harness/extensions/loader.d.ts.map +1 -1
  165. package/dist/harness/extensions/loader.js +258 -54
  166. package/dist/harness/extensions/loader.js.map +1 -1
  167. package/dist/harness/extensions/registry.d.ts +48 -1
  168. package/dist/harness/extensions/registry.d.ts.map +1 -1
  169. package/dist/harness/extensions/registry.js +146 -0
  170. package/dist/harness/extensions/registry.js.map +1 -1
  171. package/dist/harness/extensions/types.d.ts +161 -14
  172. package/dist/harness/extensions/types.d.ts.map +1 -1
  173. package/dist/harness/extensions/types.js.map +1 -1
  174. package/dist/harness/index.d.ts +5 -5
  175. package/dist/harness/index.d.ts.map +1 -1
  176. package/dist/harness/index.js +2 -2
  177. package/dist/harness/index.js.map +1 -1
  178. package/dist/harness/models.d.ts +5 -0
  179. package/dist/harness/models.d.ts.map +1 -1
  180. package/dist/harness/models.js +10 -0
  181. package/dist/harness/models.js.map +1 -1
  182. package/dist/harness/runner.d.ts +21 -2
  183. package/dist/harness/runner.d.ts.map +1 -1
  184. package/dist/harness/runner.js +80 -22
  185. package/dist/harness/runner.js.map +1 -1
  186. package/dist/harness/session-store.d.ts +4 -1
  187. package/dist/harness/session-store.d.ts.map +1 -1
  188. package/dist/harness/session-store.js +11 -2
  189. package/dist/harness/session-store.js.map +1 -1
  190. package/dist/harness/subagent-runner.d.ts +17 -0
  191. package/dist/harness/subagent-runner.d.ts.map +1 -0
  192. package/dist/harness/subagent-runner.js +307 -0
  193. package/dist/harness/subagent-runner.js.map +1 -0
  194. package/dist/harness/types.d.ts +53 -0
  195. package/dist/harness/types.d.ts.map +1 -1
  196. package/dist/harness/types.js.map +1 -1
  197. package/dist/index.d.ts +1 -1
  198. package/dist/index.d.ts.map +1 -1
  199. package/dist/index.js.map +1 -1
  200. package/dist/main.js +232 -29
  201. package/dist/main.js.map +1 -1
  202. package/dist/provisioner.d.ts.map +1 -1
  203. package/dist/provisioner.js +2 -5
  204. package/dist/provisioner.js.map +1 -1
  205. package/dist/runtime/conversation-runtime.d.ts +1 -1
  206. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  207. package/dist/runtime/conversation-runtime.js +264 -41
  208. package/dist/runtime/conversation-runtime.js.map +1 -1
  209. package/dist/runtime/types.d.ts +32 -5
  210. package/dist/runtime/types.d.ts.map +1 -1
  211. package/dist/runtime/types.js.map +1 -1
  212. package/dist/sandbox/cloudflare.d.ts +2 -0
  213. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  214. package/dist/sandbox/cloudflare.js +7 -0
  215. package/dist/sandbox/cloudflare.js.map +1 -1
  216. package/dist/sandbox/container.d.ts +3 -0
  217. package/dist/sandbox/container.d.ts.map +1 -1
  218. package/dist/sandbox/container.js +8 -2
  219. package/dist/sandbox/container.js.map +1 -1
  220. package/dist/sandbox/firecracker.d.ts +2 -0
  221. package/dist/sandbox/firecracker.d.ts.map +1 -1
  222. package/dist/sandbox/firecracker.js +7 -1
  223. package/dist/sandbox/firecracker.js.map +1 -1
  224. package/dist/sandbox/gondolin-fleet.d.ts +104 -0
  225. package/dist/sandbox/gondolin-fleet.d.ts.map +1 -0
  226. package/dist/sandbox/gondolin-fleet.js +330 -0
  227. package/dist/sandbox/gondolin-fleet.js.map +1 -0
  228. package/dist/sandbox/gondolin-gateway.d.ts +85 -0
  229. package/dist/sandbox/gondolin-gateway.d.ts.map +1 -0
  230. package/dist/sandbox/gondolin-gateway.js +400 -0
  231. package/dist/sandbox/gondolin-gateway.js.map +1 -0
  232. package/dist/sandbox/gondolin-inventory.d.ts +89 -0
  233. package/dist/sandbox/gondolin-inventory.d.ts.map +1 -0
  234. package/dist/sandbox/gondolin-inventory.js +267 -0
  235. package/dist/sandbox/gondolin-inventory.js.map +1 -0
  236. package/dist/sandbox/gondolin-join.d.ts +62 -0
  237. package/dist/sandbox/gondolin-join.d.ts.map +1 -0
  238. package/dist/sandbox/gondolin-join.js +261 -0
  239. package/dist/sandbox/gondolin-join.js.map +1 -0
  240. package/dist/sandbox/gondolin-nfs-advisory.d.ts +15 -0
  241. package/dist/sandbox/gondolin-nfs-advisory.d.ts.map +1 -0
  242. package/dist/sandbox/gondolin-nfs-advisory.js +60 -0
  243. package/dist/sandbox/gondolin-nfs-advisory.js.map +1 -0
  244. package/dist/sandbox/gondolin-placement.d.ts +32 -0
  245. package/dist/sandbox/gondolin-placement.d.ts.map +1 -0
  246. package/dist/sandbox/gondolin-placement.js +83 -0
  247. package/dist/sandbox/gondolin-placement.js.map +1 -0
  248. package/dist/sandbox/gondolin-remote.d.ts +73 -0
  249. package/dist/sandbox/gondolin-remote.d.ts.map +1 -0
  250. package/dist/sandbox/gondolin-remote.js +352 -0
  251. package/dist/sandbox/gondolin-remote.js.map +1 -0
  252. package/dist/sandbox/gondolin-worker-client.d.ts +139 -0
  253. package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -0
  254. package/dist/sandbox/gondolin-worker-client.js +325 -0
  255. package/dist/sandbox/gondolin-worker-client.js.map +1 -0
  256. package/dist/sandbox/gondolin-worker-main.d.ts +2 -0
  257. package/dist/sandbox/gondolin-worker-main.d.ts.map +1 -0
  258. package/dist/sandbox/gondolin-worker-main.js +22 -0
  259. package/dist/sandbox/gondolin-worker-main.js.map +1 -0
  260. package/dist/sandbox/gondolin-worker.d.ts +55 -0
  261. package/dist/sandbox/gondolin-worker.d.ts.map +1 -0
  262. package/dist/sandbox/gondolin-worker.js +201 -0
  263. package/dist/sandbox/gondolin-worker.js.map +1 -0
  264. package/dist/sandbox/gondolin.d.ts +52 -0
  265. package/dist/sandbox/gondolin.d.ts.map +1 -0
  266. package/dist/sandbox/gondolin.js +425 -0
  267. package/dist/sandbox/gondolin.js.map +1 -0
  268. package/dist/sandbox/host.d.ts +2 -0
  269. package/dist/sandbox/host.d.ts.map +1 -1
  270. package/dist/sandbox/host.js +11 -0
  271. package/dist/sandbox/host.js.map +1 -1
  272. package/dist/sandbox/identity.d.ts +21 -0
  273. package/dist/sandbox/identity.d.ts.map +1 -0
  274. package/dist/sandbox/identity.js +36 -0
  275. package/dist/sandbox/identity.js.map +1 -0
  276. package/dist/sandbox/index.d.ts +4 -3
  277. package/dist/sandbox/index.d.ts.map +1 -1
  278. package/dist/sandbox/index.js +5 -3
  279. package/dist/sandbox/index.js.map +1 -1
  280. package/dist/sandbox/types.d.ts +26 -1
  281. package/dist/sandbox/types.d.ts.map +1 -1
  282. package/dist/sandbox/types.js.map +1 -1
  283. package/dist/sandbox/utils.d.ts +11 -0
  284. package/dist/sandbox/utils.d.ts.map +1 -1
  285. package/dist/sandbox/utils.js +35 -0
  286. package/dist/sandbox/utils.js.map +1 -1
  287. package/dist/sessions/agent-memory-file-manager.d.ts.map +1 -1
  288. package/dist/sessions/agent-memory-file-manager.js +16 -30
  289. package/dist/sessions/agent-memory-file-manager.js.map +1 -1
  290. package/dist/sessions/history-line.d.ts +20 -0
  291. package/dist/sessions/history-line.d.ts.map +1 -0
  292. package/dist/sessions/history-line.js +24 -0
  293. package/dist/sessions/history-line.js.map +1 -0
  294. package/dist/sessions/policy.d.ts.map +1 -1
  295. package/dist/sessions/policy.js +5 -3
  296. package/dist/sessions/policy.js.map +1 -1
  297. package/dist/sessions/session-key.d.ts +31 -0
  298. package/dist/sessions/session-key.d.ts.map +1 -0
  299. package/dist/sessions/session-key.js +44 -0
  300. package/dist/sessions/session-key.js.map +1 -0
  301. package/dist/sessions/store.d.ts.map +1 -1
  302. package/dist/sessions/store.js +2 -2
  303. package/dist/sessions/store.js.map +1 -1
  304. package/dist/tools/bash.d.ts +3 -1
  305. package/dist/tools/bash.d.ts.map +1 -1
  306. package/dist/tools/bash.js +24 -17
  307. package/dist/tools/bash.js.map +1 -1
  308. package/dist/tools/edit.d.ts.map +1 -1
  309. package/dist/tools/edit.js +9 -12
  310. package/dist/tools/edit.js.map +1 -1
  311. package/dist/tools/event.d.ts +1 -1
  312. package/dist/tools/event.d.ts.map +1 -1
  313. package/dist/tools/event.js +33 -4
  314. package/dist/tools/event.js.map +1 -1
  315. package/dist/tools/generate-image.d.ts +17 -0
  316. package/dist/tools/generate-image.d.ts.map +1 -0
  317. package/dist/tools/generate-image.js +63 -0
  318. package/dist/tools/generate-image.js.map +1 -0
  319. package/dist/tools/index.d.ts +16 -2
  320. package/dist/tools/index.d.ts.map +1 -1
  321. package/dist/tools/index.js +27 -3
  322. package/dist/tools/index.js.map +1 -1
  323. package/dist/tools/react.d.ts +15 -0
  324. package/dist/tools/react.d.ts.map +1 -0
  325. package/dist/tools/react.js +40 -0
  326. package/dist/tools/react.js.map +1 -0
  327. package/dist/tools/sandbox.d.ts +2 -2
  328. package/dist/tools/sandbox.d.ts.map +1 -1
  329. package/dist/tools/sandbox.js +8 -7
  330. package/dist/tools/sandbox.js.map +1 -1
  331. package/dist/tools/subagent.d.ts +69 -0
  332. package/dist/tools/subagent.d.ts.map +1 -0
  333. package/dist/tools/subagent.js +286 -0
  334. package/dist/tools/subagent.js.map +1 -0
  335. package/dist/tools/types.d.ts +34 -1
  336. package/dist/tools/types.d.ts.map +1 -1
  337. package/dist/tools/types.js.map +1 -1
  338. package/dist/tools/write.d.ts.map +1 -1
  339. package/dist/tools/write.js +1 -8
  340. package/dist/tools/write.js.map +1 -1
  341. package/dist/trigger.d.ts.map +1 -1
  342. package/dist/trigger.js +1 -2
  343. package/dist/trigger.js.map +1 -1
  344. package/dist/types.d.ts +139 -7
  345. package/dist/types.d.ts.map +1 -1
  346. package/dist/types.js.map +1 -1
  347. package/dist/vault/disabled.d.ts +9 -0
  348. package/dist/vault/disabled.d.ts.map +1 -0
  349. package/dist/vault/disabled.js +23 -0
  350. package/dist/vault/disabled.js.map +1 -0
  351. package/dist/vault/index.d.ts.map +1 -1
  352. package/dist/vault/index.js +3 -24
  353. package/dist/vault/index.js.map +1 -1
  354. package/dist/vault/policy.d.ts +20 -0
  355. package/dist/vault/policy.d.ts.map +1 -0
  356. package/dist/vault/policy.js +20 -0
  357. package/dist/vault/policy.js.map +1 -0
  358. package/dist/vault/routing.d.ts +1 -0
  359. package/dist/vault/routing.d.ts.map +1 -1
  360. package/dist/vault/routing.js +3 -10
  361. package/dist/vault/routing.js.map +1 -1
  362. package/dist/web/admin/portal.d.ts +27 -0
  363. package/dist/web/admin/portal.d.ts.map +1 -1
  364. package/dist/web/admin/portal.js +91 -130
  365. package/dist/web/admin/portal.js.map +1 -1
  366. package/dist/web/admin/types.d.ts +3 -0
  367. package/dist/web/admin/types.d.ts.map +1 -1
  368. package/dist/web/admin/types.js.map +1 -1
  369. package/dist/web/login/oauth.d.ts +2 -3
  370. package/dist/web/login/oauth.d.ts.map +1 -1
  371. package/dist/web/login/oauth.js +0 -31
  372. package/dist/web/login/oauth.js.map +1 -1
  373. package/dist/web/login/portal.js +1 -1
  374. package/dist/web/login/portal.js.map +1 -1
  375. package/dist/web/login/types.d.ts +0 -11
  376. package/dist/web/login/types.d.ts.map +1 -1
  377. package/dist/web/login/types.js.map +1 -1
  378. package/dist/web/portal-shell.d.ts +4 -0
  379. package/dist/web/portal-shell.d.ts.map +1 -0
  380. package/dist/{portal-shell.js → web/portal-shell.js} +2 -2
  381. package/dist/web/portal-shell.js.map +1 -0
  382. package/dist/web/server.d.ts.map +1 -1
  383. package/dist/web/server.js +7 -0
  384. package/dist/web/server.js.map +1 -1
  385. package/dist/web/session-view/command.d.ts.map +1 -1
  386. package/dist/web/session-view/command.js +4 -1
  387. package/dist/web/session-view/command.js.map +1 -1
  388. package/dist/web/session-view/portal.d.ts.map +1 -1
  389. package/dist/web/session-view/portal.js +4 -3
  390. package/dist/web/session-view/portal.js.map +1 -1
  391. package/dist/web/session-view/service.d.ts.map +1 -1
  392. package/dist/web/session-view/service.js +2 -1
  393. package/dist/web/session-view/service.js.map +1 -1
  394. package/dist/web/session-view/types.d.ts +2 -1
  395. package/dist/web/session-view/types.d.ts.map +1 -1
  396. package/dist/web/session-view/types.js.map +1 -1
  397. package/package.json +7 -4
  398. package/dist/download.d.ts.map +0 -1
  399. package/dist/download.js.map +0 -1
  400. package/dist/model-registry.d.ts +0 -4
  401. package/dist/model-registry.d.ts.map +0 -1
  402. package/dist/model-registry.js +0 -7
  403. package/dist/model-registry.js.map +0 -1
  404. package/dist/portal-shell.d.ts +0 -4
  405. package/dist/portal-shell.d.ts.map +0 -1
  406. package/dist/portal-shell.js.map +0 -1
  407. package/dist/runtime/agent-run-controller.d.ts +0 -31
  408. package/dist/runtime/agent-run-controller.d.ts.map +0 -1
  409. package/dist/runtime/agent-run-controller.js +0 -195
  410. package/dist/runtime/agent-run-controller.js.map +0 -1
  411. /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
  412. /package/dist/{download.js → cli/download.js} +0 -0
@@ -0,0 +1,175 @@
1
+ import * as log from "../../log.js";
2
+ import { resolveChatSessionKey } from "../../sessions/policy.js";
3
+ import { createChatResponseErrorReporter, formatToolArgs, splitText, } from "../shared.js";
4
+ import { OrderedResponseOperations } from "../streaming.js";
5
+ import { formatGithubContinuation, GITHUB_MAX_COMMENT_LENGTH, } from "./bot.js";
6
+ import { parseGithubConversationId } from "./ids.js";
7
+ function formatToolResult(result) {
8
+ const argsFormatted = formatToolArgs(result.args);
9
+ const duration = (result.durationMs / 1000).toFixed(1);
10
+ let text = `**${result.isError ? "Error" : "Done"} ${result.toolName}**`;
11
+ if (result.label)
12
+ text += `: ${result.label}`;
13
+ text += ` (${duration}s)\n`;
14
+ if (argsFormatted)
15
+ text += `\`\`\`\n${argsFormatted}\n\`\`\`\n`;
16
+ text += `**Result:**\n\`\`\`\n${result.result}\n\`\`\``;
17
+ return text;
18
+ }
19
+ export function createGithubAdapters(event, bot) {
20
+ let commentId = null;
21
+ let accumulatedText = "";
22
+ const responseOperations = new OrderedResponseOperations();
23
+ const conversationId = event.conversationId;
24
+ const ref = parseGithubConversationId(conversationId);
25
+ const message = {
26
+ id: event.ts,
27
+ sessionKey: event.sessionKey ??
28
+ resolveChatSessionKey({
29
+ conversationId,
30
+ conversationKind: event.conversationKind,
31
+ messageId: event.ts,
32
+ persistentTopLevel: true,
33
+ threadTs: event.thread_ts,
34
+ }),
35
+ conversationKind: event.conversationKind,
36
+ userId: event.user,
37
+ userName: event.userName,
38
+ text: event.text,
39
+ attachments: event.attachments,
40
+ threadTs: event.thread_ts,
41
+ };
42
+ // The bot's getMessagingInfo() is the single authority for platform info;
43
+ // the conversation-scoped context below is an explicit append, not a fork.
44
+ const baseInfo = bot.getMessagingInfo();
45
+ const platform = {
46
+ ...baseInfo,
47
+ formattingGuide: `${baseInfo.formattingGuide}\n\n` +
48
+ `## Conversation context\n` +
49
+ `This conversation IS GitHub issue/PR ${ref.owner}/${ref.repo}#${ref.number}: the ` +
50
+ `first message in the history is its title and body, and the following messages are ` +
51
+ `its comments. When the user says "this issue", they mean #${ref.number}. Messages ` +
52
+ `tagged [PR review comment rc-<id> …] are inline review threads on a diff line: ` +
53
+ `answer those with the github_review_reply tool (comment_id = that id) so the reply ` +
54
+ `lands in-thread — your normal response posts as a plain PR comment.\n\n` +
55
+ `## Repository & pull requests\n` +
56
+ `The repository is cloned at ./repo — a snapshot from this conversation's first ` +
57
+ `trigger; run github_sync when it may be stale to pull the latest PR head or base ` +
58
+ `branch. If this conversation is a pull request, its head branch is checked out ` +
59
+ `under its real name (run git branch --show-current in ./repo to see it; fork PRs ` +
60
+ `fall back to pr-${ref.number}). You have no git credentials, so git fetch/push ` +
61
+ `fail by design.\n` +
62
+ `To ship code changes: commit inside ./repo (the git author is preconfigured) on a ` +
63
+ `pi/<name> branch, then call the github_pr tool to push it. When this conversation's ` +
64
+ `checked-out PR head branch is already named pi/<name> — e.g. a PR you opened ` +
65
+ `earlier — commit directly on it and pass that branch to github_pr: the push ` +
66
+ `updates THIS pull request instead of opening a new one. Any other branch opens a ` +
67
+ `new pull request (draft: true for a draft); calling github_pr again with the same ` +
68
+ `branch pushes new commits to its existing PR. Use github_checks to read CI results ` +
69
+ `for your branch (or this PR) — pass a failing check's job id to read its log — and ` +
70
+ `iterate until they pass. You cannot push the default branch or ` +
71
+ `merge — humans review and merge every PR.\n` +
72
+ `github_read looks up PR/issue metadata this clone cannot show (diff stats, changed ` +
73
+ `files, review state, other issues in this repo); github_issue manages labels, ` +
74
+ `assignees, and close/reopen for triage.`,
75
+ diagnostics: {
76
+ showUsageSummary: false,
77
+ },
78
+ };
79
+ const reportResponseError = createChatResponseErrorReporter(() => ({
80
+ platform: "github",
81
+ conversationId,
82
+ messageId: message.id,
83
+ sessionKey: message.sessionKey,
84
+ responseMessageId: commentId,
85
+ conversationKind: message.conversationKind,
86
+ }));
87
+ async function postOrUpdateResponse(displayText) {
88
+ if (commentId !== null) {
89
+ await bot.updateMessage(conversationId, String(commentId), displayText);
90
+ }
91
+ else {
92
+ commentId = await bot.postComment(ref, displayText);
93
+ }
94
+ }
95
+ async function postSplitResponse(text) {
96
+ const [displayText, ...extraParts] = splitText(text, GITHUB_MAX_COMMENT_LENGTH, formatGithubContinuation);
97
+ await postOrUpdateResponse(displayText);
98
+ for (const part of extraParts) {
99
+ await bot.postComment(ref, part);
100
+ }
101
+ }
102
+ function queueGithubResponse(label, operation, work, extra) {
103
+ return responseOperations.run(work, (err) => {
104
+ log.logWarning(`GitHub ${label} error`, err instanceof Error ? err.message : String(err));
105
+ reportResponseError(err, operation, extra());
106
+ });
107
+ }
108
+ // No appendResponseDelta/finishResponse: GitHub gets the finished response
109
+ // in one comment. Streaming would edit the comment on every flush — API
110
+ // churn and "edited" noise for readers who refresh rather than watch typing.
111
+ const responder = {
112
+ respond: async (text) => {
113
+ await queueGithubResponse("respond", "respond", async () => {
114
+ accumulatedText = accumulatedText ? `${accumulatedText}\n${text}` : text;
115
+ await postSplitResponse(accumulatedText);
116
+ if (commentId !== null) {
117
+ bot.logBotResponse(conversationId, text, String(commentId));
118
+ }
119
+ }, () => ({
120
+ phase: commentId ? "update" : "initial_post",
121
+ textLength: text.length,
122
+ accumulatedLength: accumulatedText.length,
123
+ }));
124
+ },
125
+ replaceResponse: async (text) => {
126
+ await queueGithubResponse("replaceResponse", "replace_response", async () => {
127
+ accumulatedText = text;
128
+ await postSplitResponse(accumulatedText);
129
+ }, () => ({
130
+ textLength: text.length,
131
+ hadExistingResponse: Boolean(commentId),
132
+ }));
133
+ },
134
+ respondDiagnostic: async (text, options) => {
135
+ await queueGithubResponse("respondDiagnostic", "respond_diagnostic", async () => {
136
+ const prefix = options?.style === "error" ? "**Error:** " : "";
137
+ for (const part of splitText(`${prefix}${text}`, GITHUB_MAX_COMMENT_LENGTH, formatGithubContinuation)) {
138
+ await bot.postComment(ref, part);
139
+ }
140
+ }, () => ({ textLength: text.length, style: options?.style }));
141
+ },
142
+ respondToolResult: async (result) => {
143
+ await responder.respondDiagnostic(formatToolResult(result));
144
+ },
145
+ // GitHub has no typing indicator; progress shows through comment edits.
146
+ setTyping: async () => { },
147
+ setWorking: async () => { },
148
+ uploadFile: async (filePath, title) => {
149
+ // The REST API cannot attach files to comments (uploads are a browser
150
+ // feature); leave a pointer instead of failing the run.
151
+ const name = title ?? filePath;
152
+ await queueGithubResponse("uploadFile", "respond_diagnostic", async () => {
153
+ await bot.postComment(ref, `*(file \`${name}\` was produced, but the GitHub adapter cannot attach files to comments)*`);
154
+ }, () => ({ filePath }));
155
+ },
156
+ react: async (emoji) => {
157
+ await bot.addReaction(conversationId, event.ts, emoji);
158
+ },
159
+ deleteResponse: async () => {
160
+ await responseOperations.run(async () => {
161
+ if (commentId !== null) {
162
+ try {
163
+ await bot.deleteComment(ref, commentId);
164
+ }
165
+ catch {
166
+ // Ignore errors
167
+ }
168
+ commentId = null;
169
+ }
170
+ });
171
+ },
172
+ };
173
+ return { message, responder, platform };
174
+ }
175
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ChatToolResult,\n ConversationMessage,\n ConversationResponder,\n MessagingInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { resolveChatSessionKey } from \"../../sessions/policy.js\";\nimport {\n createChatResponseErrorReporter,\n formatToolArgs,\n splitText,\n type ChatResponseErrorOperation,\n} from \"../shared.js\";\nimport { OrderedResponseOperations } from \"../streaming.js\";\nimport {\n formatGithubContinuation,\n GITHUB_MAX_COMMENT_LENGTH,\n type GithubMessagingBot,\n} from \"./bot.js\";\nimport { parseGithubConversationId } from \"./ids.js\";\nimport type { GithubEvent } from \"./types.js\";\n\nfunction formatToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `**${result.isError ? \"Error\" : \"Done\"} ${result.toolName}**`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `**Result:**\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createGithubAdapters(\n event: GithubEvent,\n bot: GithubMessagingBot,\n): {\n message: ConversationMessage;\n responder: ConversationResponder;\n platform: MessagingInfo;\n} {\n let commentId: number | null = null;\n let accumulatedText = \"\";\n const responseOperations = new OrderedResponseOperations();\n\n const conversationId = event.conversationId;\n const ref = parseGithubConversationId(conversationId);\n\n const message: ConversationMessage = {\n id: event.ts,\n sessionKey:\n event.sessionKey ??\n resolveChatSessionKey({\n conversationId,\n conversationKind: event.conversationKind,\n messageId: event.ts,\n persistentTopLevel: true,\n threadTs: event.thread_ts,\n }),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n // The bot's getMessagingInfo() is the single authority for platform info;\n // the conversation-scoped context below is an explicit append, not a fork.\n const baseInfo = bot.getMessagingInfo();\n const platform: MessagingInfo = {\n ...baseInfo,\n formattingGuide:\n `${baseInfo.formattingGuide}\\n\\n` +\n `## Conversation context\\n` +\n `This conversation IS GitHub issue/PR ${ref.owner}/${ref.repo}#${ref.number}: the ` +\n `first message in the history is its title and body, and the following messages are ` +\n `its comments. When the user says \"this issue\", they mean #${ref.number}. Messages ` +\n `tagged [PR review comment rc-<id> …] are inline review threads on a diff line: ` +\n `answer those with the github_review_reply tool (comment_id = that id) so the reply ` +\n `lands in-thread — your normal response posts as a plain PR comment.\\n\\n` +\n `## Repository & pull requests\\n` +\n `The repository is cloned at ./repo — a snapshot from this conversation's first ` +\n `trigger; run github_sync when it may be stale to pull the latest PR head or base ` +\n `branch. If this conversation is a pull request, its head branch is checked out ` +\n `under its real name (run git branch --show-current in ./repo to see it; fork PRs ` +\n `fall back to pr-${ref.number}). You have no git credentials, so git fetch/push ` +\n `fail by design.\\n` +\n `To ship code changes: commit inside ./repo (the git author is preconfigured) on a ` +\n `pi/<name> branch, then call the github_pr tool to push it. When this conversation's ` +\n `checked-out PR head branch is already named pi/<name> — e.g. a PR you opened ` +\n `earlier — commit directly on it and pass that branch to github_pr: the push ` +\n `updates THIS pull request instead of opening a new one. Any other branch opens a ` +\n `new pull request (draft: true for a draft); calling github_pr again with the same ` +\n `branch pushes new commits to its existing PR. Use github_checks to read CI results ` +\n `for your branch (or this PR) — pass a failing check's job id to read its log — and ` +\n `iterate until they pass. You cannot push the default branch or ` +\n `merge — humans review and merge every PR.\\n` +\n `github_read looks up PR/issue metadata this clone cannot show (diff stats, changed ` +\n `files, review state, other issues in this repo); github_issue manages labels, ` +\n `assignees, and close/reopen for triage.`,\n diagnostics: {\n showUsageSummary: false,\n },\n };\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"github\",\n conversationId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: commentId,\n conversationKind: message.conversationKind,\n }));\n\n async function postOrUpdateResponse(displayText: string): Promise<void> {\n if (commentId !== null) {\n await bot.updateMessage(conversationId, String(commentId), displayText);\n } else {\n commentId = await bot.postComment(ref, displayText);\n }\n }\n\n async function postSplitResponse(text: string): Promise<void> {\n const [displayText, ...extraParts] = splitText(\n text,\n GITHUB_MAX_COMMENT_LENGTH,\n formatGithubContinuation,\n );\n await postOrUpdateResponse(displayText);\n for (const part of extraParts) {\n await bot.postComment(ref, part);\n }\n }\n\n function queueGithubResponse(\n label: string,\n operation: ChatResponseErrorOperation,\n work: () => Promise<void>,\n extra: () => Record<string, unknown>,\n ): Promise<void> {\n return responseOperations.run(work, (err) => {\n log.logWarning(`GitHub ${label} error`, err instanceof Error ? err.message : String(err));\n reportResponseError(err, operation, extra());\n });\n }\n\n // No appendResponseDelta/finishResponse: GitHub gets the finished response\n // in one comment. Streaming would edit the comment on every flush — API\n // churn and \"edited\" noise for readers who refresh rather than watch typing.\n const responder: ConversationResponder = {\n respond: async (text: string) => {\n await queueGithubResponse(\n \"respond\",\n \"respond\",\n async () => {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n await postSplitResponse(accumulatedText);\n if (commentId !== null) {\n bot.logBotResponse(conversationId, text, String(commentId));\n }\n },\n () => ({\n phase: commentId ? \"update\" : \"initial_post\",\n textLength: text.length,\n accumulatedLength: accumulatedText.length,\n }),\n );\n },\n\n replaceResponse: async (text: string) => {\n await queueGithubResponse(\n \"replaceResponse\",\n \"replace_response\",\n async () => {\n accumulatedText = text;\n await postSplitResponse(accumulatedText);\n },\n () => ({\n textLength: text.length,\n hadExistingResponse: Boolean(commentId),\n }),\n );\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n await queueGithubResponse(\n \"respondDiagnostic\",\n \"respond_diagnostic\",\n async () => {\n const prefix = options?.style === \"error\" ? \"**Error:** \" : \"\";\n for (const part of splitText(\n `${prefix}${text}`,\n GITHUB_MAX_COMMENT_LENGTH,\n formatGithubContinuation,\n )) {\n await bot.postComment(ref, part);\n }\n },\n () => ({ textLength: text.length, style: options?.style }),\n );\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responder.respondDiagnostic(formatToolResult(result));\n },\n\n // GitHub has no typing indicator; progress shows through comment edits.\n setTyping: async () => {},\n\n setWorking: async () => {},\n\n uploadFile: async (filePath: string, title?: string) => {\n // The REST API cannot attach files to comments (uploads are a browser\n // feature); leave a pointer instead of failing the run.\n const name = title ?? filePath;\n await queueGithubResponse(\n \"uploadFile\",\n \"respond_diagnostic\",\n async () => {\n await bot.postComment(\n ref,\n `*(file \\`${name}\\` was produced, but the GitHub adapter cannot attach files to comments)*`,\n );\n },\n () => ({ filePath }),\n );\n },\n\n react: async (emoji: string) => {\n await bot.addReaction(conversationId, event.ts, emoji);\n },\n\n deleteResponse: async () => {\n await responseOperations.run(async () => {\n if (commentId !== null) {\n try {\n await bot.deleteComment(ref, commentId);\n } catch {\n // Ignore errors\n }\n commentId = null;\n }\n });\n },\n };\n\n return { message, responder, platform };\n}\n"]}
@@ -0,0 +1,31 @@
1
+ export interface GcpTokenProviderOptions {
2
+ /** Path to the ADC JSON (usually $GOOGLE_APPLICATION_CREDENTIALS). */
3
+ credentialsPath: string;
4
+ fetchImpl?: typeof fetch;
5
+ }
6
+ export declare class GcpAuthError extends Error {
7
+ constructor(message: string, options?: {
8
+ cause?: unknown;
9
+ });
10
+ }
11
+ export declare class GcpTokenProvider {
12
+ private readonly credentialsPath;
13
+ private readonly fetchImpl;
14
+ private credentials;
15
+ private cached;
16
+ private inFlight;
17
+ constructor(options: GcpTokenProviderOptions);
18
+ /** A valid cloud-platform access token, cached until near expiry. */
19
+ getAccessToken(): Promise<string>;
20
+ private loadCredentials;
21
+ private mintToken;
22
+ private postForm;
23
+ /** WIF: read the subject token, exchange at STS, optionally impersonate. */
24
+ private mintExternalAccount;
25
+ private readSubjectToken;
26
+ /** Service-account key: self-signed RS256 JWT → assertion grant. */
27
+ private mintServiceAccount;
28
+ /** gcloud user ADC: refresh-token grant. */
29
+ private mintAuthorizedUser;
30
+ }
31
+ //# sourceMappingURL=gcp-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp-auth.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/gcp-auth.ts"],"names":[],"mappings":"AAwDA,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAMD,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACF;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,MAAM,CAAqD;IACnE,OAAO,CAAC,QAAQ,CAAgC;IAEhD,YAAY,OAAO,EAAE,uBAAuB,EAG3C;IAED,qEAAqE;IAC/D,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAStC;IAED,OAAO,CAAC,eAAe;YAqBT,SAAS;YAqBT,QAAQ;IAiBtB,4EAA4E;YAC9D,mBAAmB;YA4CnB,gBAAgB;IA+B9B,oEAAoE;YACtD,kBAAkB;IAiChC,4CAA4C;YAC9B,kBAAkB;CAkBjC"}
@@ -0,0 +1,196 @@
1
+ import { readFileSync } from "fs";
2
+ import { createSign } from "crypto";
3
+ /**
4
+ * Minimal Google Application Default Credentials provider — enough to mint
5
+ * cloud-platform access tokens on the host without pulling in
6
+ * google-auth-library (this repo hand-rolls its GitHub App JWTs the same way).
7
+ *
8
+ * Supported `GOOGLE_APPLICATION_CREDENTIALS` JSON shapes:
9
+ * - `external_account` (Workload Identity Federation) with a file or url
10
+ * credential source: STS token exchange, plus optional service-account
11
+ * impersonation. AWS/executable sources are rejected with a clear error.
12
+ * - `service_account` key: self-signed JWT assertion grant.
13
+ * - `authorized_user` (gcloud user ADC): refresh-token grant. Local dev.
14
+ */
15
+ const CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
16
+ const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
17
+ /** Refresh cached tokens this long before expiry — STS/impersonation tokens
18
+ * live ~1h; minting per request would hammer STS in a CI-debug loop. */
19
+ const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
20
+ function base64Url(data) {
21
+ return Buffer.from(data).toString("base64url");
22
+ }
23
+ export class GcpAuthError extends Error {
24
+ constructor(message, options) {
25
+ super(message, options);
26
+ this.name = "GcpAuthError";
27
+ }
28
+ }
29
+ export class GcpTokenProvider {
30
+ constructor(options) {
31
+ this.credentials = null;
32
+ this.cached = null;
33
+ this.inFlight = null;
34
+ this.credentialsPath = options.credentialsPath;
35
+ this.fetchImpl = options.fetchImpl ?? fetch;
36
+ }
37
+ /** A valid cloud-platform access token, cached until near expiry. */
38
+ async getAccessToken() {
39
+ if (this.cached && Date.now() < this.cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {
40
+ return this.cached.token;
41
+ }
42
+ // Single-flight: concurrent tool calls must not each hit STS.
43
+ this.inFlight ??= this.mintToken().finally(() => {
44
+ this.inFlight = null;
45
+ });
46
+ return this.inFlight;
47
+ }
48
+ loadCredentials() {
49
+ if (this.credentials)
50
+ return this.credentials;
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(readFileSync(this.credentialsPath, "utf-8"));
54
+ }
55
+ catch (err) {
56
+ throw new GcpAuthError(`Cannot read GCP credentials at ${this.credentialsPath}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
57
+ }
58
+ if (!["external_account", "service_account", "authorized_user"].includes(parsed.type)) {
59
+ throw new GcpAuthError(`Unsupported GCP credential type '${parsed.type}' — expected ` +
60
+ `external_account (WIF), service_account, or authorized_user.`);
61
+ }
62
+ this.credentials = parsed;
63
+ return parsed;
64
+ }
65
+ async mintToken() {
66
+ const credentials = this.loadCredentials();
67
+ let minted;
68
+ switch (credentials.type) {
69
+ case "external_account":
70
+ minted = await this.mintExternalAccount(credentials);
71
+ break;
72
+ case "service_account":
73
+ minted = await this.mintServiceAccount(credentials);
74
+ break;
75
+ case "authorized_user":
76
+ minted = await this.mintAuthorizedUser(credentials);
77
+ break;
78
+ }
79
+ this.cached = {
80
+ token: minted.token,
81
+ expiresAt: Date.now() + minted.expiresInSeconds * 1000,
82
+ };
83
+ return minted.token;
84
+ }
85
+ async postForm(url, form, context) {
86
+ const response = await this.fetchImpl(url, {
87
+ method: "POST",
88
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
89
+ body: new URLSearchParams(form).toString(),
90
+ });
91
+ if (!response.ok) {
92
+ const detail = (await response.text().catch(() => "")).slice(0, 300);
93
+ throw new GcpAuthError(`${context} failed with ${response.status}: ${detail}`);
94
+ }
95
+ return (await response.json());
96
+ }
97
+ /** WIF: read the subject token, exchange at STS, optionally impersonate. */
98
+ async mintExternalAccount(credentials) {
99
+ const subjectToken = await this.readSubjectToken(credentials);
100
+ const sts = await this.postForm(credentials.token_url, {
101
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
102
+ audience: credentials.audience,
103
+ scope: CLOUD_PLATFORM_SCOPE,
104
+ requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
105
+ subject_token: subjectToken,
106
+ subject_token_type: credentials.subject_token_type,
107
+ }, "GCP STS token exchange");
108
+ const stsToken = sts.access_token;
109
+ const stsExpires = sts.expires_in ?? 3600;
110
+ if (!credentials.service_account_impersonation_url) {
111
+ return { token: stsToken, expiresInSeconds: stsExpires };
112
+ }
113
+ const response = await this.fetchImpl(credentials.service_account_impersonation_url, {
114
+ method: "POST",
115
+ headers: {
116
+ "Content-Type": "application/json",
117
+ Authorization: `Bearer ${stsToken}`,
118
+ },
119
+ body: JSON.stringify({ scope: [CLOUD_PLATFORM_SCOPE] }),
120
+ });
121
+ if (!response.ok) {
122
+ const detail = (await response.text().catch(() => "")).slice(0, 300);
123
+ throw new GcpAuthError(`GCP service-account impersonation failed with ${response.status}: ${detail}`);
124
+ }
125
+ const impersonated = (await response.json());
126
+ const expiresInSeconds = Math.max(0, Math.floor((Date.parse(impersonated.expireTime) - Date.now()) / 1000));
127
+ return { token: impersonated.accessToken, expiresInSeconds };
128
+ }
129
+ async readSubjectToken(credentials) {
130
+ const source = credentials.credential_source;
131
+ let raw;
132
+ if (source.file) {
133
+ raw = readFileSync(source.file, "utf-8").trim();
134
+ }
135
+ else if (source.url) {
136
+ const response = await this.fetchImpl(source.url, { headers: source.headers });
137
+ if (!response.ok) {
138
+ throw new GcpAuthError(`WIF credential_source url returned ${response.status} — cannot read subject token`);
139
+ }
140
+ raw = (await response.text()).trim();
141
+ }
142
+ else {
143
+ throw new GcpAuthError("Unsupported WIF credential_source: only file and url sources are supported " +
144
+ "(aws/executable sources are not).");
145
+ }
146
+ if (source.format?.type === "json") {
147
+ const field = source.format.subject_token_field_name ?? "token";
148
+ const parsed = JSON.parse(raw);
149
+ const token = parsed[field];
150
+ if (!token) {
151
+ throw new GcpAuthError(`WIF subject token field '${field}' missing from JSON source`);
152
+ }
153
+ return token;
154
+ }
155
+ return raw;
156
+ }
157
+ /** Service-account key: self-signed RS256 JWT → assertion grant. */
158
+ async mintServiceAccount(credentials) {
159
+ const tokenUrl = credentials.token_uri ?? OAUTH_TOKEN_URL;
160
+ const now = Math.floor(Date.now() / 1000);
161
+ const header = base64Url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
162
+ const payload = base64Url(JSON.stringify({
163
+ iss: credentials.client_email,
164
+ scope: CLOUD_PLATFORM_SCOPE,
165
+ aud: tokenUrl,
166
+ iat: now,
167
+ exp: now + 3600,
168
+ }));
169
+ const signature = createSign("RSA-SHA256")
170
+ .update(`${header}.${payload}`)
171
+ .sign(credentials.private_key)
172
+ .toString("base64url");
173
+ const data = await this.postForm(tokenUrl, {
174
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
175
+ assertion: `${header}.${payload}.${signature}`,
176
+ }, "GCP service-account token grant");
177
+ return {
178
+ token: data.access_token,
179
+ expiresInSeconds: data.expires_in ?? 3600,
180
+ };
181
+ }
182
+ /** gcloud user ADC: refresh-token grant. */
183
+ async mintAuthorizedUser(credentials) {
184
+ const data = await this.postForm(OAUTH_TOKEN_URL, {
185
+ grant_type: "refresh_token",
186
+ client_id: credentials.client_id,
187
+ client_secret: credentials.client_secret,
188
+ refresh_token: credentials.refresh_token,
189
+ }, "GCP user-credential refresh");
190
+ return {
191
+ token: data.access_token,
192
+ expiresInSeconds: data.expires_in ?? 3600,
193
+ };
194
+ }
195
+ }
196
+ //# sourceMappingURL=gcp-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp-auth.js","sourceRoot":"","sources":["../../../src/adapters/github/gcp-auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"fs\";\nimport { createSign } from \"crypto\";\n\n/**\n * Minimal Google Application Default Credentials provider — enough to mint\n * cloud-platform access tokens on the host without pulling in\n * google-auth-library (this repo hand-rolls its GitHub App JWTs the same way).\n *\n * Supported `GOOGLE_APPLICATION_CREDENTIALS` JSON shapes:\n * - `external_account` (Workload Identity Federation) with a file or url\n * credential source: STS token exchange, plus optional service-account\n * impersonation. AWS/executable sources are rejected with a clear error.\n * - `service_account` key: self-signed JWT assertion grant.\n * - `authorized_user` (gcloud user ADC): refresh-token grant. Local dev.\n */\n\nconst CLOUD_PLATFORM_SCOPE = \"https://www.googleapis.com/auth/cloud-platform\";\nconst OAUTH_TOKEN_URL = \"https://oauth2.googleapis.com/token\";\n\n/** Refresh cached tokens this long before expiry — STS/impersonation tokens\n * live ~1h; minting per request would hammer STS in a CI-debug loop. */\nconst TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\n\ninterface ExternalAccountCredentials {\n type: \"external_account\";\n audience: string;\n subject_token_type: string;\n token_url: string;\n service_account_impersonation_url?: string;\n credential_source: {\n file?: string;\n url?: string;\n headers?: Record<string, string>;\n format?: { type?: string; subject_token_field_name?: string };\n };\n}\n\ninterface ServiceAccountCredentials {\n type: \"service_account\";\n client_email: string;\n private_key: string;\n token_uri?: string;\n}\n\ninterface AuthorizedUserCredentials {\n type: \"authorized_user\";\n client_id: string;\n client_secret: string;\n refresh_token: string;\n}\n\ntype AdcCredentials =\n | ExternalAccountCredentials\n | ServiceAccountCredentials\n | AuthorizedUserCredentials;\n\nexport interface GcpTokenProviderOptions {\n /** Path to the ADC JSON (usually $GOOGLE_APPLICATION_CREDENTIALS). */\n credentialsPath: string;\n fetchImpl?: typeof fetch;\n}\n\nfunction base64Url(data: string): string {\n return Buffer.from(data).toString(\"base64url\");\n}\n\nexport class GcpAuthError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"GcpAuthError\";\n }\n}\n\nexport class GcpTokenProvider {\n private readonly credentialsPath: string;\n private readonly fetchImpl: typeof fetch;\n private credentials: AdcCredentials | null = null;\n private cached: { token: string; expiresAt: number } | null = null;\n private inFlight: Promise<string> | null = null;\n\n constructor(options: GcpTokenProviderOptions) {\n this.credentialsPath = options.credentialsPath;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n /** A valid cloud-platform access token, cached until near expiry. */\n async getAccessToken(): Promise<string> {\n if (this.cached && Date.now() < this.cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {\n return this.cached.token;\n }\n // Single-flight: concurrent tool calls must not each hit STS.\n this.inFlight ??= this.mintToken().finally(() => {\n this.inFlight = null;\n });\n return this.inFlight;\n }\n\n private loadCredentials(): AdcCredentials {\n if (this.credentials) return this.credentials;\n let parsed: AdcCredentials;\n try {\n parsed = JSON.parse(readFileSync(this.credentialsPath, \"utf-8\")) as AdcCredentials;\n } catch (err) {\n throw new GcpAuthError(\n `Cannot read GCP credentials at ${this.credentialsPath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n if (![\"external_account\", \"service_account\", \"authorized_user\"].includes(parsed.type)) {\n throw new GcpAuthError(\n `Unsupported GCP credential type '${(parsed as { type?: string }).type}' — expected ` +\n `external_account (WIF), service_account, or authorized_user.`,\n );\n }\n this.credentials = parsed;\n return parsed;\n }\n\n private async mintToken(): Promise<string> {\n const credentials = this.loadCredentials();\n let minted: { token: string; expiresInSeconds: number };\n switch (credentials.type) {\n case \"external_account\":\n minted = await this.mintExternalAccount(credentials);\n break;\n case \"service_account\":\n minted = await this.mintServiceAccount(credentials);\n break;\n case \"authorized_user\":\n minted = await this.mintAuthorizedUser(credentials);\n break;\n }\n this.cached = {\n token: minted.token,\n expiresAt: Date.now() + minted.expiresInSeconds * 1000,\n };\n return minted.token;\n }\n\n private async postForm(\n url: string,\n form: Record<string, string>,\n context: string,\n ): Promise<Record<string, unknown>> {\n const response = await this.fetchImpl(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(form).toString(),\n });\n if (!response.ok) {\n const detail = (await response.text().catch(() => \"\")).slice(0, 300);\n throw new GcpAuthError(`${context} failed with ${response.status}: ${detail}`);\n }\n return (await response.json()) as Record<string, unknown>;\n }\n\n /** WIF: read the subject token, exchange at STS, optionally impersonate. */\n private async mintExternalAccount(\n credentials: ExternalAccountCredentials,\n ): Promise<{ token: string; expiresInSeconds: number }> {\n const subjectToken = await this.readSubjectToken(credentials);\n const sts = await this.postForm(\n credentials.token_url,\n {\n grant_type: \"urn:ietf:params:oauth:grant-type:token-exchange\",\n audience: credentials.audience,\n scope: CLOUD_PLATFORM_SCOPE,\n requested_token_type: \"urn:ietf:params:oauth:token-type:access_token\",\n subject_token: subjectToken,\n subject_token_type: credentials.subject_token_type,\n },\n \"GCP STS token exchange\",\n );\n const stsToken = sts.access_token as string;\n const stsExpires = (sts.expires_in as number | undefined) ?? 3600;\n if (!credentials.service_account_impersonation_url) {\n return { token: stsToken, expiresInSeconds: stsExpires };\n }\n\n const response = await this.fetchImpl(credentials.service_account_impersonation_url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${stsToken}`,\n },\n body: JSON.stringify({ scope: [CLOUD_PLATFORM_SCOPE] }),\n });\n if (!response.ok) {\n const detail = (await response.text().catch(() => \"\")).slice(0, 300);\n throw new GcpAuthError(\n `GCP service-account impersonation failed with ${response.status}: ${detail}`,\n );\n }\n const impersonated = (await response.json()) as { accessToken: string; expireTime: string };\n const expiresInSeconds = Math.max(\n 0,\n Math.floor((Date.parse(impersonated.expireTime) - Date.now()) / 1000),\n );\n return { token: impersonated.accessToken, expiresInSeconds };\n }\n\n private async readSubjectToken(credentials: ExternalAccountCredentials): Promise<string> {\n const source = credentials.credential_source;\n let raw: string;\n if (source.file) {\n raw = readFileSync(source.file, \"utf-8\").trim();\n } else if (source.url) {\n const response = await this.fetchImpl(source.url, { headers: source.headers });\n if (!response.ok) {\n throw new GcpAuthError(\n `WIF credential_source url returned ${response.status} — cannot read subject token`,\n );\n }\n raw = (await response.text()).trim();\n } else {\n throw new GcpAuthError(\n \"Unsupported WIF credential_source: only file and url sources are supported \" +\n \"(aws/executable sources are not).\",\n );\n }\n if (source.format?.type === \"json\") {\n const field = source.format.subject_token_field_name ?? \"token\";\n const parsed = JSON.parse(raw) as Record<string, string>;\n const token = parsed[field];\n if (!token) {\n throw new GcpAuthError(`WIF subject token field '${field}' missing from JSON source`);\n }\n return token;\n }\n return raw;\n }\n\n /** Service-account key: self-signed RS256 JWT → assertion grant. */\n private async mintServiceAccount(\n credentials: ServiceAccountCredentials,\n ): Promise<{ token: string; expiresInSeconds: number }> {\n const tokenUrl = credentials.token_uri ?? OAUTH_TOKEN_URL;\n const now = Math.floor(Date.now() / 1000);\n const header = base64Url(JSON.stringify({ alg: \"RS256\", typ: \"JWT\" }));\n const payload = base64Url(\n JSON.stringify({\n iss: credentials.client_email,\n scope: CLOUD_PLATFORM_SCOPE,\n aud: tokenUrl,\n iat: now,\n exp: now + 3600,\n }),\n );\n const signature = createSign(\"RSA-SHA256\")\n .update(`${header}.${payload}`)\n .sign(credentials.private_key)\n .toString(\"base64url\");\n const data = await this.postForm(\n tokenUrl,\n {\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n assertion: `${header}.${payload}.${signature}`,\n },\n \"GCP service-account token grant\",\n );\n return {\n token: data.access_token as string,\n expiresInSeconds: (data.expires_in as number | undefined) ?? 3600,\n };\n }\n\n /** gcloud user ADC: refresh-token grant. */\n private async mintAuthorizedUser(\n credentials: AuthorizedUserCredentials,\n ): Promise<{ token: string; expiresInSeconds: number }> {\n const data = await this.postForm(\n OAUTH_TOKEN_URL,\n {\n grant_type: \"refresh_token\",\n client_id: credentials.client_id,\n client_secret: credentials.client_secret,\n refresh_token: credentials.refresh_token,\n },\n \"GCP user-credential refresh\",\n );\n return {\n token: data.access_token as string,\n expiresInSeconds: (data.expires_in as number | undefined) ?? 3600,\n };\n }\n}\n"]}
@@ -0,0 +1,34 @@
1
+ import type { GithubConversationRef } from "./types.js";
2
+ /**
3
+ * Conversation id for one issue/PR: `GH_<owner>_<repo>_<number>`.
4
+ *
5
+ * DESIGN.md originally proposed `gh:<owner>/<repo>#<number>`, but conversation
6
+ * ids are used verbatim as a single path segment (LAYOUT.md § Casing) and the
7
+ * conversation dir is bind-mounted with docker's `-v source:target` syntax in
8
+ * image mode — so the id can contain neither `/` nor `:`. `_` is the
9
+ * separator because it is the only inert character that also parses back
10
+ * unambiguously under GitHub's name grammar: owners allow `-` (so `-` would
11
+ * make the owner/repo boundary ambiguous) but never `_`, repos may contain
12
+ * `_` but the trailing number is pure digits — so the first `_` and the last
13
+ * `_` before the digits are always the real boundaries.
14
+ *
15
+ * Owner and repo are lowercased: GitHub names are case-insensitive (and this
16
+ * id has two spelling sources — the GITHUB_REPOS env var and API payloads),
17
+ * so per LAYOUT.md § Casing this is a mikan-derived slug, not a verbatim
18
+ * platform id, and derived slugs are lowercased to keep one issue from
19
+ * mapping to two conversation dirs on case-sensitive filesystems.
20
+ */
21
+ export declare function buildGithubConversationId(ref: GithubConversationRef): string;
22
+ export declare function parseGithubConversationId(conversationId: string): GithubConversationRef;
23
+ /** Message ts used for the issue/PR body itself (comments use their comment id). */
24
+ export declare const GITHUB_ISSUE_BODY_TS = "issue";
25
+ /**
26
+ * Message ts for an inline PR review comment: `rc-<id>`. Review-comment ids
27
+ * live in a different id space than issue-comment ids, and several endpoints
28
+ * (reactions, edits) differ by family — the prefix makes routing a review id
29
+ * to an issue-comment endpoint structurally impossible.
30
+ */
31
+ export declare function githubReviewCommentTs(commentId: number): string;
32
+ /** The review-comment id inside an `rc-<id>` ts, or null for other ts kinds. */
33
+ export declare function parseReviewCommentTs(ts: string): number | null;
34
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAE5E;AAID,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB,CAYvF;AAED,oFAAoF;AACpF,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Conversation id for one issue/PR: `GH_<owner>_<repo>_<number>`.
3
+ *
4
+ * DESIGN.md originally proposed `gh:<owner>/<repo>#<number>`, but conversation
5
+ * ids are used verbatim as a single path segment (LAYOUT.md § Casing) and the
6
+ * conversation dir is bind-mounted with docker's `-v source:target` syntax in
7
+ * image mode — so the id can contain neither `/` nor `:`. `_` is the
8
+ * separator because it is the only inert character that also parses back
9
+ * unambiguously under GitHub's name grammar: owners allow `-` (so `-` would
10
+ * make the owner/repo boundary ambiguous) but never `_`, repos may contain
11
+ * `_` but the trailing number is pure digits — so the first `_` and the last
12
+ * `_` before the digits are always the real boundaries.
13
+ *
14
+ * Owner and repo are lowercased: GitHub names are case-insensitive (and this
15
+ * id has two spelling sources — the GITHUB_REPOS env var and API payloads),
16
+ * so per LAYOUT.md § Casing this is a mikan-derived slug, not a verbatim
17
+ * platform id, and derived slugs are lowercased to keep one issue from
18
+ * mapping to two conversation dirs on case-sensitive filesystems.
19
+ */
20
+ export function buildGithubConversationId(ref) {
21
+ return `GH_${ref.owner.toLowerCase()}_${ref.repo.toLowerCase()}_${ref.number}`;
22
+ }
23
+ const CONVERSATION_ID_PATTERN = /^GH_([A-Za-z0-9-]+)_(.+)_(\d+)$/;
24
+ export function parseGithubConversationId(conversationId) {
25
+ // The greedy repo group splits at the last `_` followed by digits-to-end,
26
+ // which is always the number boundary (repos may contain `_` themselves).
27
+ const match = CONVERSATION_ID_PATTERN.exec(conversationId);
28
+ if (!match) {
29
+ throw new Error(`Not a GitHub conversation id: ${conversationId}`);
30
+ }
31
+ return {
32
+ owner: match[1].toLowerCase(),
33
+ repo: match[2].toLowerCase(),
34
+ number: Number(match[3]),
35
+ };
36
+ }
37
+ /** Message ts used for the issue/PR body itself (comments use their comment id). */
38
+ export const GITHUB_ISSUE_BODY_TS = "issue";
39
+ /**
40
+ * Message ts for an inline PR review comment: `rc-<id>`. Review-comment ids
41
+ * live in a different id space than issue-comment ids, and several endpoints
42
+ * (reactions, edits) differ by family — the prefix makes routing a review id
43
+ * to an issue-comment endpoint structurally impossible.
44
+ */
45
+ export function githubReviewCommentTs(commentId) {
46
+ return `rc-${commentId}`;
47
+ }
48
+ /** The review-comment id inside an `rc-<id>` ts, or null for other ts kinds. */
49
+ export function parseReviewCommentTs(ts) {
50
+ const match = /^rc-(\d+)$/.exec(ts);
51
+ return match ? Number(match[1]) : null;
52
+ }
53
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.js","sourceRoot":"","sources":["../../../src/adapters/github/ids.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAA0B;IAClE,OAAO,MAAM,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;AACjF,CAAC;AAED,MAAM,uBAAuB,GAAG,iCAAiC,CAAC;AAElE,MAAM,UAAU,yBAAyB,CAAC,cAAsB;IAC9D,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,cAAc,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QAC5B,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,OAAO,MAAM,SAAS,EAAE,CAAC;AAC3B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC","sourcesContent":["import type { GithubConversationRef } from \"./types.js\";\n\n/**\n * Conversation id for one issue/PR: `GH_<owner>_<repo>_<number>`.\n *\n * DESIGN.md originally proposed `gh:<owner>/<repo>#<number>`, but conversation\n * ids are used verbatim as a single path segment (LAYOUT.md § Casing) and the\n * conversation dir is bind-mounted with docker's `-v source:target` syntax in\n * image mode — so the id can contain neither `/` nor `:`. `_` is the\n * separator because it is the only inert character that also parses back\n * unambiguously under GitHub's name grammar: owners allow `-` (so `-` would\n * make the owner/repo boundary ambiguous) but never `_`, repos may contain\n * `_` but the trailing number is pure digits — so the first `_` and the last\n * `_` before the digits are always the real boundaries.\n *\n * Owner and repo are lowercased: GitHub names are case-insensitive (and this\n * id has two spelling sources — the GITHUB_REPOS env var and API payloads),\n * so per LAYOUT.md § Casing this is a mikan-derived slug, not a verbatim\n * platform id, and derived slugs are lowercased to keep one issue from\n * mapping to two conversation dirs on case-sensitive filesystems.\n */\nexport function buildGithubConversationId(ref: GithubConversationRef): string {\n return `GH_${ref.owner.toLowerCase()}_${ref.repo.toLowerCase()}_${ref.number}`;\n}\n\nconst CONVERSATION_ID_PATTERN = /^GH_([A-Za-z0-9-]+)_(.+)_(\\d+)$/;\n\nexport function parseGithubConversationId(conversationId: string): GithubConversationRef {\n // The greedy repo group splits at the last `_` followed by digits-to-end,\n // which is always the number boundary (repos may contain `_` themselves).\n const match = CONVERSATION_ID_PATTERN.exec(conversationId);\n if (!match) {\n throw new Error(`Not a GitHub conversation id: ${conversationId}`);\n }\n return {\n owner: match[1].toLowerCase(),\n repo: match[2].toLowerCase(),\n number: Number(match[3]),\n };\n}\n\n/** Message ts used for the issue/PR body itself (comments use their comment id). */\nexport const GITHUB_ISSUE_BODY_TS = \"issue\";\n\n/**\n * Message ts for an inline PR review comment: `rc-<id>`. Review-comment ids\n * live in a different id space than issue-comment ids, and several endpoints\n * (reactions, edits) differ by family — the prefix makes routing a review id\n * to an issue-comment endpoint structurally impossible.\n */\nexport function githubReviewCommentTs(commentId: number): string {\n return `rc-${commentId}`;\n}\n\n/** The review-comment id inside an `rc-<id>` ts, or null for other ts kinds. */\nexport function parseReviewCommentTs(ts: string): number | null {\n const match = /^rc-(\\d+)$/.exec(ts);\n return match ? Number(match[1]) : null;\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import type { CloneRepoOptions, PushBranchOptions, SyncRepoOptions, SyncRepoResult } from "./types.js";
2
+ /**
3
+ * Branches the bot may push: `pi/<name>`. Everything else — including the
4
+ * repo's default branch by construction — is refused, so a driven agent can
5
+ * only ever produce reviewable branches, never touch mainline directly.
6
+ */
7
+ export declare const GITHUB_PUSH_BRANCH_PATTERN: RegExp;
8
+ /**
9
+ * Shallow-clone a repo into a conversation dir and preconfigure the bot's
10
+ * commit identity. For PR conversations, additionally fetch the PR head and
11
+ * check it out so the agent starts on the code under review — under its real
12
+ * branch name when the caller resolved one, which is what lets the agent push
13
+ * fixes back to the PR itself.
14
+ */
15
+ export declare function cloneRepo(options: CloneRepoOptions): Promise<void>;
16
+ /**
17
+ * Refresh a conversation clone from origin. Always fetches; only moves the
18
+ * checkout when that provably cannot lose agent work: the target branch is
19
+ * the one checked out, the tree is clean, and it has no commits of its own
20
+ * (`checkout -B` then handles fast-forward and force-pushed heads alike).
21
+ * Anything else stays fetch-only — FETCH_HEAD is left for the agent, which
22
+ * has full credential-less git inside the sandbox, to merge or rebase itself.
23
+ */
24
+ export declare function syncRepo(options: SyncRepoOptions): Promise<SyncRepoResult>;
25
+ /**
26
+ * Push one local branch to origin. Non-force by construction (plain refspec),
27
+ * and only `pi/*` branches are accepted.
28
+ */
29
+ export declare function pushBranch(options: PushBranchOptions): Promise<void>;
30
+ //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/repo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAMpB;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,QAAwC,CAAC;AA0ChF;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBxE;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA8ChF;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa1E"}