@geminixiang/mikan 1.0.0-beta.29 → 1.0.0-beta.30

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 (225) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +10 -7
  3. package/dist/adapters/discord/context.d.ts.map +1 -1
  4. package/dist/adapters/discord/context.js +20 -25
  5. package/dist/adapters/discord/context.js.map +1 -1
  6. package/dist/adapters/github/cloudbuild.d.ts +2 -7
  7. package/dist/adapters/github/cloudbuild.d.ts.map +1 -1
  8. package/dist/adapters/github/cloudbuild.js.map +1 -1
  9. package/dist/adapters/github/context.d.ts.map +1 -1
  10. package/dist/adapters/github/context.js +13 -18
  11. package/dist/adapters/github/context.js.map +1 -1
  12. package/dist/adapters/github/gcp-auth.d.ts +2 -5
  13. package/dist/adapters/github/gcp-auth.d.ts.map +1 -1
  14. package/dist/adapters/github/gcp-auth.js.map +1 -1
  15. package/dist/adapters/github/tools/checks.d.ts +2 -7
  16. package/dist/adapters/github/tools/checks.d.ts.map +1 -1
  17. package/dist/adapters/github/tools/checks.js.map +1 -1
  18. package/dist/adapters/github/tools/issue.d.ts +2 -3
  19. package/dist/adapters/github/tools/issue.d.ts.map +1 -1
  20. package/dist/adapters/github/tools/issue.js.map +1 -1
  21. package/dist/adapters/github/tools/read.d.ts +2 -3
  22. package/dist/adapters/github/tools/read.d.ts.map +1 -1
  23. package/dist/adapters/github/tools/read.js.map +1 -1
  24. package/dist/adapters/github/tools/review-reply.d.ts +2 -4
  25. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -1
  26. package/dist/adapters/github/tools/review-reply.js.map +1 -1
  27. package/dist/adapters/github/tools/sync.d.ts +2 -2
  28. package/dist/adapters/github/tools/sync.d.ts.map +1 -1
  29. package/dist/adapters/github/tools/sync.js.map +1 -1
  30. package/dist/adapters/github/types.d.ts +22 -0
  31. package/dist/adapters/github/types.d.ts.map +1 -1
  32. package/dist/adapters/github/types.js.map +1 -1
  33. package/dist/adapters/progressive-renderer.d.ts +7 -0
  34. package/dist/adapters/progressive-renderer.d.ts.map +1 -0
  35. package/dist/adapters/progressive-renderer.js +383 -0
  36. package/dist/adapters/progressive-renderer.js.map +1 -0
  37. package/dist/adapters/slack/bot.d.ts +1 -0
  38. package/dist/adapters/slack/bot.d.ts.map +1 -1
  39. package/dist/adapters/slack/bot.js +21 -0
  40. package/dist/adapters/slack/bot.js.map +1 -1
  41. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  42. package/dist/adapters/slack/response-lifecycle.js +128 -234
  43. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  44. package/dist/adapters/slack/tool-pack.d.ts +2 -18
  45. package/dist/adapters/slack/tool-pack.d.ts.map +1 -1
  46. package/dist/adapters/slack/tool-pack.js +24 -3
  47. package/dist/adapters/slack/tool-pack.js.map +1 -1
  48. package/dist/adapters/slack/tools/blockkit.d.ts +2 -16
  49. package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -1
  50. package/dist/adapters/slack/tools/blockkit.js +2 -2
  51. package/dist/adapters/slack/tools/blockkit.js.map +1 -1
  52. package/dist/adapters/slack/types.d.ts +35 -0
  53. package/dist/adapters/slack/types.d.ts.map +1 -1
  54. package/dist/adapters/slack/types.js.map +1 -1
  55. package/dist/adapters/telegram/context.d.ts.map +1 -1
  56. package/dist/adapters/telegram/context.js +16 -20
  57. package/dist/adapters/telegram/context.js.map +1 -1
  58. package/dist/adapters/types.d.ts +58 -8
  59. package/dist/adapters/types.d.ts.map +1 -1
  60. package/dist/adapters/types.js.map +1 -1
  61. package/dist/agent.d.ts +5 -34
  62. package/dist/agent.d.ts.map +1 -1
  63. package/dist/agent.js +114 -13
  64. package/dist/agent.js.map +1 -1
  65. package/dist/cli/boot.d.ts +2 -13
  66. package/dist/cli/boot.d.ts.map +1 -1
  67. package/dist/cli/boot.js.map +1 -1
  68. package/dist/cli/ext-git.d.ts +2 -7
  69. package/dist/cli/ext-git.d.ts.map +1 -1
  70. package/dist/cli/ext-git.js +36 -5
  71. package/dist/cli/ext-git.js.map +1 -1
  72. package/dist/cli/types.d.ts +20 -0
  73. package/dist/cli/types.d.ts.map +1 -0
  74. package/dist/cli/types.js +2 -0
  75. package/dist/cli/types.js.map +1 -0
  76. package/dist/commands/manifest.d.ts +2 -44
  77. package/dist/commands/manifest.d.ts.map +1 -1
  78. package/dist/commands/manifest.js.map +1 -1
  79. package/dist/commands/model.d.ts +2 -3
  80. package/dist/commands/model.d.ts.map +1 -1
  81. package/dist/commands/model.js +48 -13
  82. package/dist/commands/model.js.map +1 -1
  83. package/dist/commands/registry.d.ts +2 -2
  84. package/dist/commands/registry.d.ts.map +1 -1
  85. package/dist/commands/registry.js +1 -2
  86. package/dist/commands/registry.js.map +1 -1
  87. package/dist/commands/types.d.ts +54 -0
  88. package/dist/commands/types.d.ts.map +1 -1
  89. package/dist/commands/types.js.map +1 -1
  90. package/dist/env-manifest.d.ts +13 -20
  91. package/dist/env-manifest.d.ts.map +1 -1
  92. package/dist/env-manifest.js +0 -11
  93. package/dist/env-manifest.js.map +1 -1
  94. package/dist/harness/event-format.d.ts +2 -43
  95. package/dist/harness/event-format.d.ts.map +1 -1
  96. package/dist/harness/event-format.js +48 -1
  97. package/dist/harness/event-format.js.map +1 -1
  98. package/dist/harness/extensions/loader.d.ts +2 -70
  99. package/dist/harness/extensions/loader.d.ts.map +1 -1
  100. package/dist/harness/extensions/loader.js.map +1 -1
  101. package/dist/harness/extensions/types.d.ts +67 -2
  102. package/dist/harness/extensions/types.d.ts.map +1 -1
  103. package/dist/harness/extensions/types.js.map +1 -1
  104. package/dist/harness/index.d.ts +14 -7
  105. package/dist/harness/index.d.ts.map +1 -1
  106. package/dist/harness/index.js +4 -4
  107. package/dist/harness/index.js.map +1 -1
  108. package/dist/harness/models.d.ts +2 -4
  109. package/dist/harness/models.d.ts.map +1 -1
  110. package/dist/harness/models.js.map +1 -1
  111. package/dist/harness/runner.d.ts +3 -57
  112. package/dist/harness/runner.d.ts.map +1 -1
  113. package/dist/harness/runner.js.map +1 -1
  114. package/dist/harness/settings.d.ts +2 -20
  115. package/dist/harness/settings.d.ts.map +1 -1
  116. package/dist/harness/settings.js +11 -0
  117. package/dist/harness/settings.js.map +1 -1
  118. package/dist/harness/skills.d.ts +2 -29
  119. package/dist/harness/skills.d.ts.map +1 -1
  120. package/dist/harness/skills.js.map +1 -1
  121. package/dist/harness/subagent-profiles.d.ts +2 -10
  122. package/dist/harness/subagent-profiles.d.ts.map +1 -1
  123. package/dist/harness/subagent-profiles.js +102 -23
  124. package/dist/harness/subagent-profiles.js.map +1 -1
  125. package/dist/harness/subagent-runner.js +17 -6
  126. package/dist/harness/subagent-runner.js.map +1 -1
  127. package/dist/harness/types.d.ts +162 -4
  128. package/dist/harness/types.d.ts.map +1 -1
  129. package/dist/harness/types.js.map +1 -1
  130. package/dist/index.d.ts +32 -7
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +19 -5
  133. package/dist/index.js.map +1 -1
  134. package/dist/main.js +7 -1
  135. package/dist/main.js.map +1 -1
  136. package/dist/packages/admin.d.ts +2 -14
  137. package/dist/packages/admin.d.ts.map +1 -1
  138. package/dist/packages/admin.js.map +1 -1
  139. package/dist/packages/inspect.d.ts +2 -27
  140. package/dist/packages/inspect.d.ts.map +1 -1
  141. package/dist/packages/inspect.js.map +1 -1
  142. package/dist/packages/materialize.d.ts +13 -15
  143. package/dist/packages/materialize.d.ts.map +1 -1
  144. package/dist/packages/materialize.js +120 -10
  145. package/dist/packages/materialize.js.map +1 -1
  146. package/dist/packages/resolve.d.ts +2 -37
  147. package/dist/packages/resolve.d.ts.map +1 -1
  148. package/dist/packages/resolve.js.map +1 -1
  149. package/dist/packages/source.d.ts +1 -1
  150. package/dist/packages/source.js +1 -1
  151. package/dist/packages/source.js.map +1 -1
  152. package/dist/packages/types.d.ts +46 -0
  153. package/dist/packages/types.d.ts.map +1 -1
  154. package/dist/packages/types.js.map +1 -1
  155. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  156. package/dist/runtime/conversation-runtime.js +194 -139
  157. package/dist/runtime/conversation-runtime.js.map +1 -1
  158. package/dist/runtime/session-lifecycle.d.ts +9 -0
  159. package/dist/runtime/session-lifecycle.d.ts.map +1 -1
  160. package/dist/runtime/session-lifecycle.js +90 -5
  161. package/dist/runtime/session-lifecycle.js.map +1 -1
  162. package/dist/runtime/types.d.ts +1 -1
  163. package/dist/runtime/types.d.ts.map +1 -1
  164. package/dist/runtime/types.js.map +1 -1
  165. package/dist/sandbox/gondolin.d.ts +2 -23
  166. package/dist/sandbox/gondolin.d.ts.map +1 -1
  167. package/dist/sandbox/gondolin.js +92 -8
  168. package/dist/sandbox/gondolin.js.map +1 -1
  169. package/dist/sandbox/host.d.ts +1 -0
  170. package/dist/sandbox/host.d.ts.map +1 -1
  171. package/dist/sandbox/host.js +39 -2
  172. package/dist/sandbox/host.js.map +1 -1
  173. package/dist/sandbox/index.d.ts +2 -1
  174. package/dist/sandbox/index.d.ts.map +1 -1
  175. package/dist/sandbox/index.js.map +1 -1
  176. package/dist/sandbox/path-context.js +18 -8
  177. package/dist/sandbox/path-context.js.map +1 -1
  178. package/dist/sandbox/types.d.ts +28 -0
  179. package/dist/sandbox/types.d.ts.map +1 -1
  180. package/dist/sandbox/types.js.map +1 -1
  181. package/dist/sessions/chat-history-sync.d.ts +2 -9
  182. package/dist/sessions/chat-history-sync.d.ts.map +1 -1
  183. package/dist/sessions/chat-history-sync.js +53 -17
  184. package/dist/sessions/chat-history-sync.js.map +1 -1
  185. package/dist/sessions/conversation-log.d.ts +2 -6
  186. package/dist/sessions/conversation-log.d.ts.map +1 -1
  187. package/dist/sessions/conversation-log.js.map +1 -1
  188. package/dist/sessions/types.d.ts +15 -2
  189. package/dist/sessions/types.d.ts.map +1 -1
  190. package/dist/sessions/types.js.map +1 -1
  191. package/dist/settings-mutation.d.ts +2 -17
  192. package/dist/settings-mutation.d.ts.map +1 -1
  193. package/dist/settings-mutation.js +2 -1
  194. package/dist/settings-mutation.js.map +1 -1
  195. package/dist/tools/index.d.ts +2 -5
  196. package/dist/tools/index.d.ts.map +1 -1
  197. package/dist/tools/index.js.map +1 -1
  198. package/dist/tools/types.d.ts +2 -2
  199. package/dist/tools/types.d.ts.map +1 -1
  200. package/dist/tools/types.js.map +1 -1
  201. package/dist/types.d.ts +51 -0
  202. package/dist/types.d.ts.map +1 -1
  203. package/dist/types.js.map +1 -1
  204. package/dist/vault/index.d.ts.map +1 -1
  205. package/dist/vault/index.js +29 -1
  206. package/dist/vault/index.js.map +1 -1
  207. package/dist/web/admin/portal.d.ts +2 -14
  208. package/dist/web/admin/portal.d.ts.map +1 -1
  209. package/dist/web/admin/portal.js.map +1 -1
  210. package/dist/web/admin/types.d.ts +12 -0
  211. package/dist/web/admin/types.d.ts.map +1 -1
  212. package/dist/web/admin/types.js.map +1 -1
  213. package/package.json +19 -9
  214. package/dist/adapters/buffered-responder.d.ts +0 -66
  215. package/dist/adapters/buffered-responder.d.ts.map +0 -1
  216. package/dist/adapters/buffered-responder.js +0 -207
  217. package/dist/adapters/buffered-responder.js.map +0 -1
  218. package/dist/adapters/slack/progressive-render.d.ts +0 -66
  219. package/dist/adapters/slack/progressive-render.d.ts.map +0 -1
  220. package/dist/adapters/slack/progressive-render.js +0 -171
  221. package/dist/adapters/slack/progressive-render.js.map +0 -1
  222. package/dist/adapters/streaming.d.ts +0 -22
  223. package/dist/adapters/streaming.d.ts.map +0 -1
  224. package/dist/adapters/streaming.js +0 -55
  225. package/dist/adapters/streaming.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -9,6 +9,34 @@ any release.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [1.0.0-beta.30]
13
+
14
+ ### Added
15
+
16
+ - Add role-based subagent profiles for software engineering, DevOps, data science, account management, business development, creative production, advertising operations, and bounded general work.
17
+ - Add a generated cross-module hardening report covering module boundaries, coupling, and verification evidence.
18
+
19
+ ### Changed
20
+
21
+ - Stabilize exported module interfaces and route executor-dependent file staging through the active execution transport.
22
+ - Deepen progressive response rendering and keep Slack E2E turns bound to the runner lifecycle.
23
+
24
+ ### Fixed
25
+
26
+ - Prevent extension packages, attachment bridges, vault keys, sandbox paths, and event inputs from escaping their intended boundaries.
27
+ - Close runner construction, cached-runner invalidation, Gondolin projection, and automatic Session Dream settlement races.
28
+ - Validate model identifiers, event timezones, package refreshes, and shared-history watermarks consistently.
29
+ - Keep reset history bounded by reset time and exclude queued future turns from session history.
30
+ - Prevent Slack from posting bare working-indicator updates.
31
+
32
+ ### Security
33
+
34
+ - Harden Git package materialization, extension subpaths, filesystem bridges, and sandbox path validation against traversal and symlink escapes.
35
+
36
+ ### Tests
37
+
38
+ - Add regression coverage for declaration surfaces, session history boundaries, runtime races, event validation, package materialization, and Slack runner integration.
39
+
12
40
  ## [1.0.0-beta.29]
13
41
 
14
42
  ### Added
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![npm version](https://img.shields.io/npm/v/@geminixiang/mikan.svg)](https://www.npmjs.com/package/@geminixiang/mikan)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
 
10
- A multi-platform AI coding agent for Slack, Telegram, and Discord.
10
+ A multi-platform AI coding agent for Slack, Telegram, Discord, and GitHub.
11
11
 
12
12
  > [!WARNING]
13
13
  > **Pre-1.0 status** — the overall framework stabilizes at 1.0.0. Until then, releases may change settings and on-disk data formats without migrations: upgrading between pre-1.0 versions can require resetting or manually adjusting existing state and workspace data.
@@ -26,7 +26,7 @@ mikan keeps the chat record, agent session, and execution runtime separate:
26
26
 
27
27
  ## Features
28
28
 
29
- - **Multi-platform** — Slack, Telegram, Discord adapters
29
+ - **Multi-platform** — Slack, Telegram, Discord, and GitHub adapters
30
30
  - **Concurrent conversations** — Slack threads, Discord replies/threads, and Telegram reply chains run as independent sessions
31
31
  - **Sandbox execution** — host, shared container, per-user managed container, Firecracker (alpha), or Cloudflare bridge (experimental)
32
32
  - **Credential vaults** — `/login` stores credentials under `--state-dir` and injects env into sandbox runs
@@ -105,7 +105,7 @@ The working directory is optional: it defaults to `<state-dir>/workspace` (so `~
105
105
  - **Telegram** — create a bot via [@BotFather](https://t.me/BotFather). The bot responds to private messages, `@mention`, and reply chains in groups.
106
106
  - **Discord** — create an application in the [Discord Developer Portal](https://discord.com/developers/applications), enable **Message Content Intent**, and invite it with message/file permissions.
107
107
 
108
- Slack threads, Discord replies/threads, and Telegram reply chains are mapped to independent session scopes. See [src/content/docs/sessions.md](src/content/docs/sessions.md).
108
+ Slack threads, Discord replies/threads, and Telegram reply chains are mapped to independent session scopes. See [src/content/docs/sessions.mdx](src/content/docs/sessions.mdx).
109
109
 
110
110
  ## Sandbox
111
111
 
@@ -117,7 +117,7 @@ Slack threads, Discord replies/threads, and Telegram reply chains are mapped to
117
117
  | `firecracker:<vm-id>:<path>` | Firecracker microVM (alpha; not recommended) |
118
118
  | `cloudflare:<sandbox-id>` | Cloudflare Worker bridge (experimental; no auto workspace sync) |
119
119
 
120
- For routing, mounts, vault behavior, managed container details, and Firecracker/Cloudflare notes, see [src/content/docs/sandbox.md](src/content/docs/sandbox.md).
120
+ For routing, mounts, vault behavior, managed container details, and Firecracker/Cloudflare notes, see [src/content/docs/sandbox.mdx](src/content/docs/sandbox.mdx).
121
121
 
122
122
  ## Chat commands
123
123
 
@@ -134,7 +134,7 @@ For routing, mounts, vault behavior, managed container details, and Firecracker/
134
134
 
135
135
  ## Configuration
136
136
 
137
- mikan reads global settings from `<state-dir>/settings.json`; per-conversation overrides live at `<working-directory>/<conversationId>/settings.json`.
137
+ mikan reads global settings from `<state-dir>/settings.json`; host-only per-conversation overrides live at `<state-dir>/conversations/<conversationId>/settings.json`. Legacy workspace settings are migrated once, then ignored.
138
138
 
139
139
  ```json
140
140
  {
@@ -153,6 +153,9 @@ See [src/content/docs/configuration.md](src/content/docs/configuration.md) for a
153
153
  ```text
154
154
  <state-dir>/
155
155
  ├── settings.json
156
+ ├── conversations/
157
+ │ └── <conversation-id>/
158
+ │ └── settings.json
156
159
  └── vaults/
157
160
 
158
161
  <working-directory>/
@@ -172,9 +175,9 @@ See [src/content/docs/configuration.md](src/content/docs/configuration.md) for a
172
175
 
173
176
  - [Events](src/content/docs/events.md)
174
177
  - [Skills](src/content/docs/skills.md)
175
- - [Deployment](src/content/docs/deployment.md)
178
+ - [Deployment](src/content/docs/deployment.mdx)
176
179
  - [Development](src/content/docs/development.md)
177
- - [Sandbox](src/content/docs/sandbox.md)
180
+ - [Sandbox](src/content/docs/sandbox.mdx)
178
181
 
179
182
  ## Slack: Download channel history
180
183
 
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIlG,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAWlE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,mBAAmB,GACvB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CAmFA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlG,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAWlE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,mBAAmB,GACvB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CA6EA"}
@@ -1,6 +1,5 @@
1
1
  import { resolveChatSessionKey } from "../../sessions/policy.js";
2
- import { createBufferedResponder, formatMarkdownToolResult } from "../buffered-responder.js";
3
- import { createChatResponseErrorReporter } from "../shared.js";
2
+ import { createProgressiveRenderer, formatMarkdownToolResult } from "../progressive-renderer.js";
4
3
  // Discord hard limit is 2000 chars; 1900 leaves headroom for working indicator.
5
4
  const MAX_LENGTH = 1900;
6
5
  const formatDiscordContinuation = (partNum) => `*(continued ${partNum})*`;
@@ -31,18 +30,6 @@ export function createDiscordAdapters(event, bot) {
31
30
  };
32
31
  // The bot's getMessagingInfo() is the single authority for platform info.
33
32
  const platform = bot.getMessagingInfo();
34
- let currentResponseId = null;
35
- const reportResponseError = createChatResponseErrorReporter(() => ({
36
- platform: "discord",
37
- conversationId,
38
- channelId,
39
- messageId: message.id,
40
- sessionKey: message.sessionKey,
41
- responseMessageId: currentResponseId,
42
- threadTs: threadTargetId,
43
- replyTargetId,
44
- conversationKind: message.conversationKind,
45
- }));
46
33
  function postFirst(text) {
47
34
  if (threadTargetId)
48
35
  return bot.postInThread(channelId, threadTargetId, text);
@@ -50,13 +37,13 @@ export function createDiscordAdapters(event, bot) {
50
37
  return bot.postReply(channelId, replyTargetId, text);
51
38
  return bot.postMessage(channelId, text);
52
39
  }
53
- const { responder } = createBufferedResponder({
40
+ const { responder } = createProgressiveRenderer({
54
41
  label: "Discord",
55
42
  maxLength: MAX_LENGTH,
56
43
  formatContinuation: formatDiscordContinuation,
57
44
  errorPrefix: "*Error:* ",
58
45
  workingIndicator: " ...",
59
- streaming: true,
46
+ supportsDeltas: true,
60
47
  typing: {
61
48
  // Send immediately and repeat every 8s (Discord clears indicator after ~10s)
62
49
  send: () => bot.sendTyping(channelId),
@@ -64,24 +51,32 @@ export function createDiscordAdapters(event, bot) {
64
51
  stopOnSend: true,
65
52
  },
66
53
  formatToolResult: formatMarkdownToolResult,
67
- reportError: reportResponseError,
54
+ responseErrorContext: (responseId) => ({
55
+ platform: "discord",
56
+ conversationId,
57
+ channelId,
58
+ messageId: message.id,
59
+ sessionKey: message.sessionKey,
60
+ responseMessageId: responseId,
61
+ threadTs: threadTargetId,
62
+ replyTargetId,
63
+ conversationKind: message.conversationKind,
64
+ }),
68
65
  post: async (text) => {
69
- currentResponseId = await postFirst(text);
70
- return currentResponseId;
66
+ return postFirst(text);
71
67
  },
72
68
  update: (id, text) => bot.updateMessageRaw(channelId, id, text),
73
- postExtra: (text, responseId) => {
69
+ postExtra: async (text, responseId) => {
74
70
  if (threadTargetId)
75
- return bot.postInThread(channelId, threadTargetId, text);
71
+ return void (await bot.postInThread(channelId, threadTargetId, text));
76
72
  if (replyTargetId)
77
- return bot.postReply(channelId, replyTargetId, text);
73
+ return void (await bot.postReply(channelId, replyTargetId, text));
78
74
  if (responseId !== null)
79
- return bot.postReply(channelId, responseId, text);
80
- return bot.postMessage(channelId, text);
75
+ return void (await bot.postReply(channelId, responseId, text));
76
+ return void (await bot.postMessage(channelId, text));
81
77
  },
82
78
  delete: async (id) => {
83
79
  await bot.deleteMessageRaw(channelId, id);
84
- currentResponseId = null;
85
80
  },
86
81
  logBotResponse: (text, id) => bot.logBotResponse(channelId, text, id),
87
82
  uploadFile: (filePath, title) => bot.uploadFile(channelId, filePath, title),
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationMessage, ConversationResponder, MessagingInfo } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../sessions/policy.js\";\nimport { createBufferedResponder, formatMarkdownToolResult } from \"../buffered-responder.js\";\nimport { createChatResponseErrorReporter } from \"../shared.js\";\nimport type { DiscordMessagingBot, DiscordEvent } from \"./bot.js\";\n\n// Discord hard limit is 2000 chars; 1900 leaves headroom for working indicator.\nconst MAX_LENGTH = 1900;\n\nconst formatDiscordContinuation = (partNum: number): string => `*(continued ${partNum})*`;\n\nfunction isDiscordMessageReference(id: string | undefined): id is string {\n return typeof id === \"string\" && id !== \"\" && !id.startsWith(\"event:\");\n}\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordMessagingBot,\n): {\n message: ConversationMessage;\n responder: ConversationResponder;\n platform: MessagingInfo;\n} {\n const conversationId = event.conversationId;\n const channelId = conversationId;\n const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;\n const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;\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 const platform: MessagingInfo = bot.getMessagingInfo();\n\n let currentResponseId: string | null = null;\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"discord\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: currentResponseId,\n threadTs: threadTargetId,\n replyTargetId,\n conversationKind: message.conversationKind,\n }));\n\n function postFirst(text: string): Promise<string> {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n return bot.postMessage(channelId, text);\n }\n\n const { responder } = createBufferedResponder({\n label: \"Discord\",\n maxLength: MAX_LENGTH,\n formatContinuation: formatDiscordContinuation,\n errorPrefix: \"*Error:* \",\n workingIndicator: \" ...\",\n streaming: true,\n typing: {\n // Send immediately and repeat every 8s (Discord clears indicator after ~10s)\n send: () => bot.sendTyping(channelId),\n intervalMs: 8000,\n stopOnSend: true,\n },\n formatToolResult: formatMarkdownToolResult,\n reportError: reportResponseError,\n post: async (text) => {\n currentResponseId = await postFirst(text);\n return currentResponseId;\n },\n update: (id, text) => bot.updateMessageRaw(channelId, id, text),\n postExtra: (text, responseId) => {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n if (responseId !== null) return bot.postReply(channelId, responseId, text);\n return bot.postMessage(channelId, text);\n },\n delete: async (id) => {\n await bot.deleteMessageRaw(channelId, id);\n currentResponseId = null;\n },\n logBotResponse: (text, id) => bot.logBotResponse(channelId, text, id),\n uploadFile: (filePath, title) => bot.uploadFile(channelId, filePath, title),\n });\n\n return { message, responder, platform };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAGjG,gFAAgF;AAChF,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,yBAAyB,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,eAAe,OAAO,IAAI,CAAC;AAE1F,SAAS,yBAAyB,CAAC,EAAsB;IACvD,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,KAAmB,EACnB,GAAwB;IAMxB,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;IAC5C,MAAM,SAAS,GAAG,cAAc,CAAC;IACjC,MAAM,cAAc,GAAG,yBAAyB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAChG,MAAM,aAAa,GAAG,yBAAyB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAEjF,MAAM,OAAO,GAAwB;QACnC,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,UAAU,EACR,KAAK,CAAC,UAAU;YAChB,qBAAqB,CAAC;gBACpB,cAAc;gBACd,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;gBACxC,SAAS,EAAE,KAAK,CAAC,EAAE;gBACnB,kBAAkB,EAAE,IAAI;gBACxB,QAAQ,EAAE,KAAK,CAAC,SAAS;aAC1B,CAAC;QACJ,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,MAAM,EAAE,KAAK,CAAC,IAAI;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS;KAC1B,CAAC;IAEF,0EAA0E;IAC1E,MAAM,QAAQ,GAAkB,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEvD,SAAS,SAAS,CAAC,IAAY;QAC7B,IAAI,cAAc;YAAE,OAAO,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;QAC7E,IAAI,aAAa;YAAE,OAAO,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QACxE,OAAO,GAAG,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,EAAE,SAAS,EAAE,GAAG,yBAAyB,CAAC;QAC9C,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,UAAU;QACrB,kBAAkB,EAAE,yBAAyB;QAC7C,WAAW,EAAE,WAAW;QACxB,gBAAgB,EAAE,MAAM;QACxB,cAAc,EAAE,IAAI;QACpB,MAAM,EAAE;YACN,6EAA6E;YAC7E,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YACrC,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;SACjB;QACD,gBAAgB,EAAE,wBAAwB;QAC1C,oBAAoB,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACrC,QAAQ,EAAE,SAAS;YACnB,cAAc;YACd,SAAS;YACT,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,iBAAiB,EAAE,UAAU;YAC7B,QAAQ,EAAE,cAAc;YACxB,aAAa;YACb,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;SAC3C,CAAC;QACF,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACnB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC;QAC/D,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE;YACpC,IAAI,cAAc;gBAAE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC;YAC1F,IAAI,aAAa;gBAAE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;YACrF,IAAI,UAAU,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;YACxF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;YACnB,MAAM,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;QACrE,UAAU,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC;KAC5E,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAC1C,CAAC","sourcesContent":["import type { ConversationMessage, ConversationResponder, MessagingInfo } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../sessions/policy.js\";\nimport { createProgressiveRenderer, formatMarkdownToolResult } from \"../progressive-renderer.js\";\nimport type { DiscordMessagingBot, DiscordEvent } from \"./bot.js\";\n\n// Discord hard limit is 2000 chars; 1900 leaves headroom for working indicator.\nconst MAX_LENGTH = 1900;\n\nconst formatDiscordContinuation = (partNum: number): string => `*(continued ${partNum})*`;\n\nfunction isDiscordMessageReference(id: string | undefined): id is string {\n return typeof id === \"string\" && id !== \"\" && !id.startsWith(\"event:\");\n}\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordMessagingBot,\n): {\n message: ConversationMessage;\n responder: ConversationResponder;\n platform: MessagingInfo;\n} {\n const conversationId = event.conversationId;\n const channelId = conversationId;\n const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;\n const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;\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 const platform: MessagingInfo = bot.getMessagingInfo();\n\n function postFirst(text: string): Promise<string> {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n return bot.postMessage(channelId, text);\n }\n\n const { responder } = createProgressiveRenderer({\n label: \"Discord\",\n maxLength: MAX_LENGTH,\n formatContinuation: formatDiscordContinuation,\n errorPrefix: \"*Error:* \",\n workingIndicator: \" ...\",\n supportsDeltas: true,\n typing: {\n // Send immediately and repeat every 8s (Discord clears indicator after ~10s)\n send: () => bot.sendTyping(channelId),\n intervalMs: 8000,\n stopOnSend: true,\n },\n formatToolResult: formatMarkdownToolResult,\n responseErrorContext: (responseId) => ({\n platform: \"discord\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: responseId,\n threadTs: threadTargetId,\n replyTargetId,\n conversationKind: message.conversationKind,\n }),\n post: async (text) => {\n return postFirst(text);\n },\n update: (id, text) => bot.updateMessageRaw(channelId, id, text),\n postExtra: async (text, responseId) => {\n if (threadTargetId) return void (await bot.postInThread(channelId, threadTargetId, text));\n if (replyTargetId) return void (await bot.postReply(channelId, replyTargetId, text));\n if (responseId !== null) return void (await bot.postReply(channelId, responseId, text));\n return void (await bot.postMessage(channelId, text));\n },\n delete: async (id) => {\n await bot.deleteMessageRaw(channelId, id);\n },\n logBotResponse: (text, id) => bot.logBotResponse(channelId, text, id),\n uploadFile: (filePath, title) => bot.uploadFile(channelId, filePath, title),\n });\n\n return { message, responder, platform };\n}\n"]}
@@ -1,15 +1,10 @@
1
- import type { GcpTokenProvider } from "./gcp-auth.js";
1
+ export type { CloudBuildLogOptions } from "./types.js";
2
+ import type { CloudBuildLogOptions } from "./types.js";
2
3
  /** Check runs reported by this app slug are Cloud Build builds. */
3
4
  export declare const CLOUD_BUILD_APP_SLUG = "google-cloud-build";
4
5
  export declare function isCloudBuildId(value: string): boolean;
5
6
  /** GCP project (id or number) from a Cloud Build details_url, or null. */
6
7
  export declare function projectFromDetailsUrl(detailsUrl: string | null | undefined): string | null;
7
- export interface CloudBuildLogOptions {
8
- tokenProvider: GcpTokenProvider;
9
- project: string;
10
- buildId: string;
11
- fetchImpl?: typeof fetch;
12
- }
13
8
  export declare class CloudBuildLogUnavailableError extends Error {
14
9
  readonly consoleUrl?: string | undefined;
15
10
  constructor(message: string, consoleUrl?: string | undefined);
@@ -1 +1 @@
1
- {"version":3,"file":"cloudbuild.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/cloudbuild.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAetD,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAIzD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQ1F;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,gBAAgB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAQD,qBAAa,6BAA8B,SAAQ,KAAK;aAGpC,UAAU,CAAC,EAAE,MAAM;IAFrC,YACE,OAAO,EAAE,MAAM,EACC,UAAU,CAAC,EAAE,MAAM,YAAA,EAIpC;CACF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCvF"}
1
+ {"version":3,"file":"cloudbuild.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/cloudbuild.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAevD,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAIzD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQ1F;AAQD,qBAAa,6BAA8B,SAAQ,KAAK;aAGpC,UAAU,CAAC,EAAE,MAAM;IAFrC,YACE,OAAO,EAAE,MAAM,EACC,UAAU,CAAC,EAAE,MAAM,YAAA,EAIpC;CACF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"cloudbuild.js","sourceRoot":"","sources":["../../../src/adapters/github/cloudbuild.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GcpTokenProvider } from \"./gcp-auth.js\";\n\n/**\n * Host-side Cloud Build log retrieval for `github_checks`. Cloud Build's\n * GitHub App reports check runs whose `external_id` is the build UUID and\n * whose `details_url` carries the GCP project; with ADC on the host (e.g. a\n * WIF external_account file) the build's log object can be read from GCS.\n *\n * Required IAM for the ADC principal: roles/cloudbuild.builds.viewer on the\n * project and roles/storage.objectViewer on the logs bucket.\n */\n\nconst CLOUDBUILD_API = \"https://cloudbuild.googleapis.com/v1\";\nconst GCS_API = \"https://storage.googleapis.com/storage/v1\";\n\n/** Check runs reported by this app slug are Cloud Build builds. */\nexport const CLOUD_BUILD_APP_SLUG = \"google-cloud-build\";\n\nconst BUILD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function isCloudBuildId(value: string): boolean {\n return BUILD_ID_PATTERN.test(value);\n}\n\n/** GCP project (id or number) from a Cloud Build details_url, or null. */\nexport function projectFromDetailsUrl(detailsUrl: string | null | undefined): string | null {\n if (!detailsUrl) return null;\n try {\n const project = new URL(detailsUrl).searchParams.get(\"project\");\n return project || null;\n } catch {\n return null;\n }\n}\n\nexport interface CloudBuildLogOptions {\n tokenProvider: GcpTokenProvider;\n project: string;\n buildId: string;\n fetchImpl?: typeof fetch;\n}\n\ninterface CloudBuildBuild {\n status?: string;\n logsBucket?: string;\n logUrl?: string;\n}\n\nexport class CloudBuildLogUnavailableError extends Error {\n constructor(\n message: string,\n public readonly consoleUrl?: string,\n ) {\n super(message);\n this.name = \"CloudBuildLogUnavailableError\";\n }\n}\n\n/**\n * Fetch one build's log text: builds.get → the `log-<id>.txt` object in the\n * build's logs bucket. Throws CloudBuildLogUnavailableError (with the console\n * url when known) whenever the log cannot be read — access denied, build not\n * found, or CLOUD_LOGGING_ONLY builds that write no GCS object.\n */\nexport async function fetchCloudBuildLog(options: CloudBuildLogOptions): Promise<string> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const token = await options.tokenProvider.getAccessToken();\n const authHeaders = { Authorization: `Bearer ${token}` };\n\n const buildResponse = await fetchImpl(\n `${CLOUDBUILD_API}/projects/${encodeURIComponent(options.project)}/builds/${encodeURIComponent(options.buildId)}`,\n { headers: authHeaders },\n );\n if (!buildResponse.ok) {\n throw new CloudBuildLogUnavailableError(\n `Cloud Build builds.get failed with ${buildResponse.status} — check that the ADC ` +\n `principal has roles/cloudbuild.builds.viewer on project ${options.project}.`,\n );\n }\n const build = (await buildResponse.json()) as CloudBuildBuild;\n\n if (!build.logsBucket) {\n throw new CloudBuildLogUnavailableError(\n \"This build stores logs in Cloud Logging only (no GCS log object); read them in the console.\",\n build.logUrl,\n );\n }\n // logsBucket may be spelled gs://bucket or gs://bucket/some/prefix.\n const bucketPath = build.logsBucket.replace(/^gs:\\/\\//, \"\");\n const [bucket, ...prefixParts] = bucketPath.split(\"/\");\n const prefix = prefixParts.length > 0 ? `${prefixParts.join(\"/\")}/` : \"\";\n const objectName = encodeURIComponent(`${prefix}log-${options.buildId}.txt`);\n\n const logResponse = await fetchImpl(`${GCS_API}/b/${bucket}/o/${objectName}?alt=media`, {\n headers: authHeaders,\n });\n if (!logResponse.ok) {\n throw new CloudBuildLogUnavailableError(\n `Cloud Build log object gs://${bucket}/${prefix}log-${options.buildId}.txt returned ` +\n `${logResponse.status} — the ADC principal may lack roles/storage.objectViewer on the ` +\n `logs bucket, or the build writes logs elsewhere.`,\n build.logUrl,\n );\n }\n return logResponse.text();\n}\n"]}
1
+ {"version":3,"file":"cloudbuild.js","sourceRoot":"","sources":["../../../src/adapters/github/cloudbuild.ts"],"names":[],"mappings":"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","sourcesContent":["export type { CloudBuildLogOptions } from \"./types.js\";\nimport type { CloudBuildLogOptions } from \"./types.js\";\n\n/**\n * Host-side Cloud Build log retrieval for `github_checks`. Cloud Build's\n * GitHub App reports check runs whose `external_id` is the build UUID and\n * whose `details_url` carries the GCP project; with ADC on the host (e.g. a\n * WIF external_account file) the build's log object can be read from GCS.\n *\n * Required IAM for the ADC principal: roles/cloudbuild.builds.viewer on the\n * project and roles/storage.objectViewer on the logs bucket.\n */\n\nconst CLOUDBUILD_API = \"https://cloudbuild.googleapis.com/v1\";\nconst GCS_API = \"https://storage.googleapis.com/storage/v1\";\n\n/** Check runs reported by this app slug are Cloud Build builds. */\nexport const CLOUD_BUILD_APP_SLUG = \"google-cloud-build\";\n\nconst BUILD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function isCloudBuildId(value: string): boolean {\n return BUILD_ID_PATTERN.test(value);\n}\n\n/** GCP project (id or number) from a Cloud Build details_url, or null. */\nexport function projectFromDetailsUrl(detailsUrl: string | null | undefined): string | null {\n if (!detailsUrl) return null;\n try {\n const project = new URL(detailsUrl).searchParams.get(\"project\");\n return project || null;\n } catch {\n return null;\n }\n}\n\ninterface CloudBuildBuild {\n status?: string;\n logsBucket?: string;\n logUrl?: string;\n}\n\nexport class CloudBuildLogUnavailableError extends Error {\n constructor(\n message: string,\n public readonly consoleUrl?: string,\n ) {\n super(message);\n this.name = \"CloudBuildLogUnavailableError\";\n }\n}\n\n/**\n * Fetch one build's log text: builds.get → the `log-<id>.txt` object in the\n * build's logs bucket. Throws CloudBuildLogUnavailableError (with the console\n * url when known) whenever the log cannot be read — access denied, build not\n * found, or CLOUD_LOGGING_ONLY builds that write no GCS object.\n */\nexport async function fetchCloudBuildLog(options: CloudBuildLogOptions): Promise<string> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const token = await options.tokenProvider.getAccessToken();\n const authHeaders = { Authorization: `Bearer ${token}` };\n\n const buildResponse = await fetchImpl(\n `${CLOUDBUILD_API}/projects/${encodeURIComponent(options.project)}/builds/${encodeURIComponent(options.buildId)}`,\n { headers: authHeaders },\n );\n if (!buildResponse.ok) {\n throw new CloudBuildLogUnavailableError(\n `Cloud Build builds.get failed with ${buildResponse.status} — check that the ADC ` +\n `principal has roles/cloudbuild.builds.viewer on project ${options.project}.`,\n );\n }\n const build = (await buildResponse.json()) as CloudBuildBuild;\n\n if (!build.logsBucket) {\n throw new CloudBuildLogUnavailableError(\n \"This build stores logs in Cloud Logging only (no GCS log object); read them in the console.\",\n build.logUrl,\n );\n }\n // logsBucket may be spelled gs://bucket or gs://bucket/some/prefix.\n const bucketPath = build.logsBucket.replace(/^gs:\\/\\//, \"\");\n const [bucket, ...prefixParts] = bucketPath.split(\"/\");\n const prefix = prefixParts.length > 0 ? `${prefixParts.join(\"/\")}/` : \"\";\n const objectName = encodeURIComponent(`${prefix}log-${options.buildId}.txt`);\n\n const logResponse = await fetchImpl(`${GCS_API}/b/${bucket}/o/${objectName}?alt=media`, {\n headers: authHeaders,\n });\n if (!logResponse.ok) {\n throw new CloudBuildLogUnavailableError(\n `Cloud Build log object gs://${bucket}/${prefix}log-${options.buildId}.txt returned ` +\n `${logResponse.status} — the ADC principal may lack roles/storage.objectViewer on the ` +\n `logs bucket, or the build writes logs elsewhere.`,\n build.logUrl,\n );\n }\n return logResponse.text();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIlG,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,kBAAkB,GACtB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CAwGA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlG,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,kBAAkB,GACtB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CAkGA"}
@@ -1,6 +1,5 @@
1
1
  import { resolveChatSessionKey } from "../../sessions/policy.js";
2
- import { createBufferedResponder, formatMarkdownToolResult } from "../buffered-responder.js";
3
- import { createChatResponseErrorReporter } from "../shared.js";
2
+ import { createProgressiveRenderer, formatMarkdownToolResult } from "../progressive-renderer.js";
4
3
  import { formatGithubContinuation } from "./bot.js";
5
4
  import { GITHUB_MAX_COMMENT_LENGTH } from "./client.js";
6
5
  import { parseGithubConversationId } from "./ids.js";
@@ -61,36 +60,32 @@ export function createGithubAdapters(event, bot) {
61
60
  showUsageSummary: false,
62
61
  },
63
62
  };
64
- let currentResponseId = null;
65
- const reportResponseError = createChatResponseErrorReporter(() => ({
66
- platform: "github",
67
- conversationId,
68
- messageId: message.id,
69
- sessionKey: message.sessionKey,
70
- responseMessageId: currentResponseId === null ? null : Number(currentResponseId),
71
- conversationKind: message.conversationKind,
72
- }));
73
63
  // streaming: false — GitHub gets the finished response in one comment.
74
64
  // Streaming would edit the comment on every flush: API churn and "edited"
75
65
  // noise for readers who refresh rather than watch typing. There is also no
76
66
  // typing indicator or working suffix; progress shows through comment edits.
77
- const { responder } = createBufferedResponder({
67
+ const { responder } = createProgressiveRenderer({
78
68
  label: "GitHub",
79
69
  maxLength: GITHUB_MAX_COMMENT_LENGTH,
80
70
  formatContinuation: formatGithubContinuation,
81
71
  errorPrefix: "**Error:** ",
82
- streaming: false,
83
72
  formatToolResult: formatMarkdownToolResult,
84
- reportError: reportResponseError,
73
+ logIntermediateResponses: true,
74
+ responseErrorContext: (responseId) => ({
75
+ platform: "github",
76
+ conversationId,
77
+ messageId: message.id,
78
+ sessionKey: message.sessionKey,
79
+ responseMessageId: responseId === null ? null : Number(responseId),
80
+ conversationKind: message.conversationKind,
81
+ }),
85
82
  post: async (text) => {
86
- currentResponseId = String(await bot.postComment(ref, text));
87
- return currentResponseId;
83
+ return String(await bot.postComment(ref, text));
88
84
  },
89
85
  update: (id, text) => bot.updateMessage(conversationId, id, text),
90
- postExtra: (text) => bot.postComment(ref, text),
86
+ postExtra: async (text) => bot.postComment(ref, text),
91
87
  delete: async (id) => {
92
88
  await bot.deleteComment(ref, Number(id));
93
- currentResponseId = null;
94
89
  },
95
90
  logBotResponse: (text, id) => bot.logBotResponse(conversationId, text, id),
96
91
  // The REST API cannot attach files to comments (uploads are a browser
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationMessage, ConversationResponder, MessagingInfo } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../sessions/policy.js\";\nimport { createBufferedResponder, formatMarkdownToolResult } from \"../buffered-responder.js\";\nimport { createChatResponseErrorReporter } from \"../shared.js\";\nimport { formatGithubContinuation, type GithubMessagingBot } from \"./bot.js\";\nimport { GITHUB_MAX_COMMENT_LENGTH } from \"./client.js\";\nimport { parseGithubConversationId } from \"./ids.js\";\nimport type { GithubEvent } from \"./types.js\";\n\nexport function createGithubAdapters(\n event: GithubEvent,\n bot: GithubMessagingBot,\n): {\n message: ConversationMessage;\n responder: ConversationResponder;\n platform: MessagingInfo;\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 let currentResponseId: string | null = null;\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"github\",\n conversationId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: currentResponseId === null ? null : Number(currentResponseId),\n conversationKind: message.conversationKind,\n }));\n\n // streaming: false — GitHub gets the finished response in one comment.\n // Streaming would edit the comment on every flush: API churn and \"edited\"\n // noise for readers who refresh rather than watch typing. There is also no\n // typing indicator or working suffix; progress shows through comment edits.\n const { responder } = createBufferedResponder({\n label: \"GitHub\",\n maxLength: GITHUB_MAX_COMMENT_LENGTH,\n formatContinuation: formatGithubContinuation,\n errorPrefix: \"**Error:** \",\n streaming: false,\n formatToolResult: formatMarkdownToolResult,\n reportError: reportResponseError,\n post: async (text) => {\n currentResponseId = String(await bot.postComment(ref, text));\n return currentResponseId;\n },\n update: (id, text) => bot.updateMessage(conversationId, id, text),\n postExtra: (text) => bot.postComment(ref, text),\n delete: async (id) => {\n await bot.deleteComment(ref, Number(id));\n currentResponseId = null;\n },\n logBotResponse: (text, id) => bot.logBotResponse(conversationId, text, id),\n // The REST API cannot attach files to comments (uploads are a browser\n // feature); leave a pointer instead of failing the run.\n uploadFallbackNote: (name) =>\n `*(file \\`${name}\\` was produced, but the GitHub adapter cannot attach files to comments)*`,\n react: (emoji) => bot.addReaction(conversationId, event.ts, emoji),\n });\n\n return { message, responder, platform };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationMessage, ConversationResponder, MessagingInfo } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../sessions/policy.js\";\nimport { createProgressiveRenderer, formatMarkdownToolResult } from \"../progressive-renderer.js\";\nimport { formatGithubContinuation, type GithubMessagingBot } from \"./bot.js\";\nimport { GITHUB_MAX_COMMENT_LENGTH } from \"./client.js\";\nimport { parseGithubConversationId } from \"./ids.js\";\nimport type { GithubEvent } from \"./types.js\";\n\nexport function createGithubAdapters(\n event: GithubEvent,\n bot: GithubMessagingBot,\n): {\n message: ConversationMessage;\n responder: ConversationResponder;\n platform: MessagingInfo;\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 // streaming: false — GitHub gets the finished response in one comment.\n // Streaming would edit the comment on every flush: API churn and \"edited\"\n // noise for readers who refresh rather than watch typing. There is also no\n // typing indicator or working suffix; progress shows through comment edits.\n const { responder } = createProgressiveRenderer({\n label: \"GitHub\",\n maxLength: GITHUB_MAX_COMMENT_LENGTH,\n formatContinuation: formatGithubContinuation,\n errorPrefix: \"**Error:** \",\n formatToolResult: formatMarkdownToolResult,\n logIntermediateResponses: true,\n responseErrorContext: (responseId) => ({\n platform: \"github\",\n conversationId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: responseId === null ? null : Number(responseId),\n conversationKind: message.conversationKind,\n }),\n post: async (text) => {\n return String(await bot.postComment(ref, text));\n },\n update: (id, text) => bot.updateMessage(conversationId, id, text),\n postExtra: async (text) => bot.postComment(ref, text),\n delete: async (id) => {\n await bot.deleteComment(ref, Number(id));\n },\n logBotResponse: (text, id) => bot.logBotResponse(conversationId, text, id),\n // The REST API cannot attach files to comments (uploads are a browser\n // feature); leave a pointer instead of failing the run.\n uploadFallbackNote: (name) =>\n `*(file \\`${name}\\` was produced, but the GitHub adapter cannot attach files to comments)*`,\n react: (emoji) => bot.addReaction(conversationId, event.ts, emoji),\n });\n\n return { message, responder, platform };\n}\n"]}
@@ -1,8 +1,5 @@
1
- export interface GcpTokenProviderOptions {
2
- /** Path to the ADC JSON (usually $GOOGLE_APPLICATION_CREDENTIALS). */
3
- credentialsPath: string;
4
- fetchImpl?: typeof fetch;
5
- }
1
+ export type { GcpTokenProviderOptions } from "./types.js";
2
+ import type { GcpTokenProviderOptions } from "./types.js";
6
3
  export declare class GcpAuthError extends Error {
7
4
  constructor(message: string, options?: {
8
5
  cause?: unknown;
@@ -1 +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"}
1
+ {"version":3,"file":"gcp-auth.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/gcp-auth.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA2D1D,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"}
@@ -1 +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"]}
1
+ {"version":3,"file":"gcp-auth.js","sourceRoot":"","sources":["../../../src/adapters/github/gcp-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAIpC;;;;;;;;;;;GAWG;AAEH,MAAM,oBAAoB,GAAG,gDAAgD,CAAC;AAC9E,MAAM,eAAe,GAAG,qCAAqC,CAAC;AAE9D;yEACyE;AACzE,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAmC9C,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,gBAAgB;IAO3B,YAAY,OAAgC;QAJpC,gBAAW,GAA0B,IAAI,CAAC;QAC1C,WAAM,GAAgD,IAAI,CAAC;QAC3D,aAAQ,GAA2B,IAAI,CAAC;QAG9C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC9C,CAAC;IAED,qEAAqE;IACrE,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,uBAAuB,EAAE,CAAC;YAChF,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAC3B,CAAC;QACD,8DAA8D;QAC9D,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YAC9C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC;QAC9C,IAAI,MAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAmB,CAAC;QACrF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,YAAY,CACpB,kCAAkC,IAAI,CAAC,eAAe,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC7G,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,CAAC,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACtF,MAAM,IAAI,YAAY,CACpB,oCAAqC,MAA4B,CAAC,IAAI,eAAe;gBACnF,8DAA8D,CACjE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC3C,IAAI,MAAmD,CAAC;QACxD,QAAQ,WAAW,CAAC,IAAI,EAAE,CAAC;YACzB,KAAK,kBAAkB;gBACrB,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;gBACrD,MAAM;YACR,KAAK,iBAAiB;gBACpB,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;gBACpD,MAAM;YACR,KAAK,iBAAiB;gBACpB,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;gBACpD,MAAM;QACV,CAAC;QACD,IAAI,CAAC,MAAM,GAAG;YACZ,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,gBAAgB,GAAG,IAAI;SACvD,CAAC;QACF,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,QAAQ,CACpB,GAAW,EACX,IAA4B,EAC5B,OAAe;QAEf,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;SAC3C,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACrE,MAAM,IAAI,YAAY,CAAC,GAAG,OAAO,gBAAgB,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;IAC5D,CAAC;IAED,4EAA4E;IACpE,KAAK,CAAC,mBAAmB,CAC/B,WAAuC;QAEvC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC9D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC7B,WAAW,CAAC,SAAS,EACrB;YACE,UAAU,EAAE,iDAAiD;YAC7D,QAAQ,EAAE,WAAW,CAAC,QAAQ;YAC9B,KAAK,EAAE,oBAAoB;YAC3B,oBAAoB,EAAE,+CAA+C;YACrE,aAAa,EAAE,YAAY;YAC3B,kBAAkB,EAAE,WAAW,CAAC,kBAAkB;SACnD,EACD,wBAAwB,CACzB,CAAC;QACF,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAsB,CAAC;QAC5C,MAAM,UAAU,GAAI,GAAG,CAAC,UAAiC,IAAI,IAAI,CAAC;QAClE,IAAI,CAAC,WAAW,CAAC,iCAAiC,EAAE,CAAC;YACnD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC;QAC3D,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,iCAAiC,EAAE;YACnF,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,QAAQ,EAAE;aACpC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,oBAAoB,CAAC,EAAE,CAAC;SACxD,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACrE,MAAM,IAAI,YAAY,CACpB,iDAAiD,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAC9E,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAgD,CAAC;QAC5F,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAC/B,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CACtE,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAC/D,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,WAAuC;QACpE,MAAM,MAAM,GAAG,WAAW,CAAC,iBAAiB,CAAC;QAC7C,IAAI,GAAW,CAAC;QAChB,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;aAAM,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/E,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,YAAY,CACpB,sCAAsC,QAAQ,CAAC,MAAM,8BAA8B,CACpF,CAAC;YACJ,CAAC;YACD,GAAG,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,YAAY,CACpB,6EAA6E;gBAC3E,mCAAmC,CACtC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,wBAAwB,IAAI,OAAO,CAAC;YAChE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA2B,CAAC;YACzD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,YAAY,CAAC,4BAA4B,KAAK,4BAA4B,CAAC,CAAC;YACxF,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,oEAAoE;IAC5D,KAAK,CAAC,kBAAkB,CAC9B,WAAsC;QAEtC,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,IAAI,eAAe,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,SAAS,CACvB,IAAI,CAAC,SAAS,CAAC;YACb,GAAG,EAAE,WAAW,CAAC,YAAY;YAC7B,KAAK,EAAE,oBAAoB;YAC3B,GAAG,EAAE,QAAQ;YACb,GAAG,EAAE,GAAG;YACR,GAAG,EAAE,GAAG,GAAG,IAAI;SAChB,CAAC,CACH,CAAC;QACF,MAAM,SAAS,GAAG,UAAU,CAAC,YAAY,CAAC;aACvC,MAAM,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;aAC9B,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC;aAC7B,QAAQ,CAAC,WAAW,CAAC,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,QAAQ,EACR;YACE,UAAU,EAAE,6CAA6C;YACzD,SAAS,EAAE,GAAG,MAAM,IAAI,OAAO,IAAI,SAAS,EAAE;SAC/C,EACD,iCAAiC,CAClC,CAAC;QACF,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,YAAsB;YAClC,gBAAgB,EAAG,IAAI,CAAC,UAAiC,IAAI,IAAI;SAClE,CAAC;IACJ,CAAC;IAED,4CAA4C;IACpC,KAAK,CAAC,kBAAkB,CAC9B,WAAsC;QAEtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,eAAe,EACf;YACE,UAAU,EAAE,eAAe;YAC3B,SAAS,EAAE,WAAW,CAAC,SAAS;YAChC,aAAa,EAAE,WAAW,CAAC,aAAa;YACxC,aAAa,EAAE,WAAW,CAAC,aAAa;SACzC,EACD,6BAA6B,CAC9B,CAAC;QACF,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,YAAsB;YAClC,gBAAgB,EAAG,IAAI,CAAC,UAAiC,IAAI,IAAI;SAClE,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { readFileSync } from \"fs\";\nimport { createSign } from \"crypto\";\nexport type { GcpTokenProviderOptions } from \"./types.js\";\nimport type { GcpTokenProviderOptions } from \"./types.js\";\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\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"]}
@@ -1,15 +1,11 @@
1
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
2
- import type { GithubCheckSummary } from "../types.js";
2
+ export type { GithubChecksFns } from "../types.js";
3
+ import type { GithubChecksFns } from "../types.js";
3
4
  declare const githubChecksSchema: import("@sinclair/typebox").TObject<{
4
5
  branch: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
5
6
  job_id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
6
7
  build_id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
7
8
  }>;
8
- export interface GithubChecksFns {
9
- getChecks: (branch?: string) => Promise<GithubCheckSummary[]>;
10
- getJobLog: (jobId: number) => Promise<string>;
11
- getBuildLog: (buildId: string) => Promise<string>;
12
- }
13
9
  /**
14
10
  * The `github_checks` tool reads CI check runs (GitHub Actions and other
15
11
  * check-reporting apps) for a branch or the conversation's PR head, and can
@@ -20,5 +16,4 @@ export declare function createGithubChecksTool(): {
20
16
  tool: AgentTool<typeof githubChecksSchema>;
21
17
  setGithubChecksFunction: (fns: GithubChecksFns | null) => void;
22
18
  };
23
- export {};
24
19
  //# sourceMappingURL=checks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../../../src/adapters/github/tools/checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,QAAA,MAAM,kBAAkB;;;;EAsBtB,CAAC;AA+BH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC9D,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,IAAI,EAAE,SAAS,CAAC,OAAO,kBAAkB,CAAC,CAAC;IAC3C,uBAAuB,EAAE,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;CAChE,CA+DA"}
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../../../src/adapters/github/tools/checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAsB,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE,QAAA,MAAM,kBAAkB;;;;EAsBtB,CAAC;AA+BH;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,IAAI,EAAE,SAAS,CAAC,OAAO,kBAAkB,CAAC,CAAC;IAC3C,uBAAuB,EAAE,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;CAChE,CA+DA"}
@@ -1 +1 @@
1
- {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../../src/adapters/github/tools/checks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;IACrC,MAAM,EAAE,IAAI,CAAC,QAAQ,CACnB,IAAI,CAAC,MAAM,CAAC;QACV,WAAW,EACT,uEAAuE;YACvE,mEAAmE;KACtE,CAAC,CACH;IACD,MAAM,EAAE,IAAI,CAAC,QAAQ,CACnB,IAAI,CAAC,MAAM,CAAC;QACV,WAAW,EACT,8EAA8E;YAC9E,6EAA6E;KAChF,CAAC,CACH;IACD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,MAAM,CAAC;QACV,WAAW,EACT,+EAA+E;YAC/E,4EAA4E;KAC/E,CAAC,CACH;CACF,CAAC,CAAC;AAEH,2EAA2E;AAC3E,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC;AAE9F,SAAS,eAAe,CAAC,GAAuB;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;IACtF,MAAM,MAAM,GACV,GAAG,CAAC,UAAU,KAAK,IAAI;QACrB,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC;YACvC,CAAC,CAAC,GAAG;YACL,CAAC,CAAC,GAAG,CAAC,UAAU,KAAK,SAAS;gBAC5B,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,GAAG,CAAC;IACd,2EAA2E;IAC3E,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,MAAM,GACV,GAAG,CAAC,OAAO,KAAK,gBAAgB;QAC9B,CAAC,CAAC,QAAQ,GAAG,CAAC,EAAE,GAAG;QACnB,CAAC,CAAC,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,UAAU;YACvC,CAAC,CAAC,UAAU,GAAG,CAAC,UAAU,GAAG;YAC7B,CAAC,CAAC,iBAAiB,GAAG,CAAC,OAAO,IAAI,SAAS,wBAAwB,CAAC;IAC1E,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC1F,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,KAAK,IAAI,IAAI,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAClF,OAAO,MAAM,IAAI,GAAG,CAAC,aAAa;QAChC,CAAC,CAAC,GAAG,IAAI,SAAS,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE;QAC/D,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB;IAIpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAA6C;QACnF,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,kFAAkF;YAClF,iFAAiF;YACjF,kFAAkF;YAClF,qFAAqF;YACrF,mFAAmF;YACnF,uBAAuB;QACzB,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,0DAA0D;QACvE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;YAC7B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC7D,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvD,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,IAAI,aAAa,EAAE,CAAC;oBACpE,OAAO,EAAE,SAAS;iBACnB,CAAC;YACJ,CAAC;YAED,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC3D,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,IAAI,aAAa,EAAE,CAAC;oBACpE,OAAO,EAAE,SAAS;iBACnB,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,uEAAuE;yBAC9E;qBACF;oBACD,OAAO,EAAE,SAAS;iBACnB,CAAC;YACJ,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;YACnE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAC9B,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,IAAI,IAAI,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAC5E,CAAC;YACF,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,cAAc,SAAS,CAAC,MAAM,eAAe,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,aAAa,OAAO,CAAC,MAAM,UAAU,CAAC;YAC/I,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;qBACzD;iBACF;gBACD,OAAO,EAAE,SAAS;aACnB,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,CAAC;AAClD,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { defineHostFnTool } from \"../../../tools/host-fn-tool.js\";\nimport type { GithubCheckSummary } from \"../types.js\";\n\nconst githubChecksSchema = Type.Object({\n branch: Type.Optional(\n Type.String({\n description:\n \"Branch whose CI checks to read (e.g. a pi/<name> branch you pushed). \" +\n \"Omit in a pull-request conversation to read the PR head's checks.\",\n }),\n ),\n job_id: Type.Optional(\n Type.Number({\n description:\n \"A [job …] id from the summary output (GitHub Actions checks) — returns that \" +\n \"CI job's log (tail) instead of the summary, for diagnosing a failing check.\",\n }),\n ),\n build_id: Type.Optional(\n Type.String({\n description:\n \"A [build …] uuid from the summary output (Cloud Build checks) — returns that \" +\n \"build's log (tail) instead of the summary. Mutually exclusive with job_id.\",\n }),\n ),\n});\n\n/** Conclusions that mean \"this check did not pass and needs attention\". */\nconst FAILING_CONCLUSIONS = new Set([\"failure\", \"timed_out\", \"action_required\", \"cancelled\"]);\n\nfunction formatCheckLine(run: GithubCheckSummary): string {\n const state = run.status === \"completed\" ? (run.conclusion ?? \"unknown\") : run.status;\n const marker =\n run.conclusion === null\n ? \"…\"\n : FAILING_CONCLUSIONS.has(run.conclusion)\n ? \"✗\"\n : run.conclusion === \"success\"\n ? \"✓\"\n : \"−\";\n // GitHub Actions logs fetch via job_id; Cloud Build logs via build_id when\n // the host has GCP credentials. Other external CI keeps logs on its own\n // service, so advertising an id there would only invite doomed fetches.\n const handle =\n run.appSlug === \"github-actions\"\n ? `[job ${run.id}]`\n : run.buildLogAvailable && run.externalId\n ? `[build ${run.externalId}]`\n : `[external CI: ${run.appSlug ?? \"unknown\"} — logs not on GitHub]`;\n const line = `${marker} ${run.name}: ${state} ${handle}${run.url ? ` (${run.url})` : \"\"}`;\n const failed = run.conclusion !== null && FAILING_CONCLUSIONS.has(run.conclusion);\n return failed && run.outputSummary\n ? `${line}\\n ↳ ${run.outputSummary.replace(/\\s*\\n\\s*/g, \" \")}`\n : line;\n}\n\nexport interface GithubChecksFns {\n getChecks: (branch?: string) => Promise<GithubCheckSummary[]>;\n getJobLog: (jobId: number) => Promise<string>;\n getBuildLog: (buildId: string) => Promise<string>;\n}\n\n/**\n * The `github_checks` tool reads CI check runs (GitHub Actions and other\n * check-reporting apps) for a branch or the conversation's PR head, and can\n * fetch one job's log tail so the agent can diagnose failures and iterate.\n * Read-only, host-side; wired per run and only for GitHub conversations.\n */\nexport function createGithubChecksTool(): {\n tool: AgentTool<typeof githubChecksSchema>;\n setGithubChecksFunction: (fns: GithubChecksFns | null) => void;\n} {\n const { tool, setFn } = defineHostFnTool<GithubChecksFns, typeof githubChecksSchema>({\n name: \"github_checks\",\n description:\n \"Read CI status (check runs) for a branch you pushed with github_pr, or for this \" +\n \"conversation's pull request when branch is omitted. Pass job_id (a [job …] id, \" +\n \"GitHub Actions) or build_id (a [build …] uuid, Cloud Build) from the summary to \" +\n \"fetch that run's log. Other external CI checks keep logs on their own service: use \" +\n \"their summary/url, or reproduce the failure locally in ./repo. Only available in \" +\n \"GitHub conversations.\",\n parameters: githubChecksSchema,\n unavailable: \"github_checks is only available in GitHub conversations.\",\n run: async (checksFns, args) => {\n if (args.job_id !== undefined && args.build_id !== undefined) {\n throw new Error(\"Pass either job_id or build_id, not both.\");\n }\n\n if (args.job_id !== undefined) {\n const logText = await checksFns.getJobLog(args.job_id);\n return {\n content: [{ type: \"text\" as const, text: logText || \"(empty log)\" }],\n details: undefined,\n };\n }\n\n if (args.build_id !== undefined) {\n const logText = await checksFns.getBuildLog(args.build_id);\n return {\n content: [{ type: \"text\" as const, text: logText || \"(empty log)\" }],\n details: undefined,\n };\n }\n\n const runs = await checksFns.getChecks(args.branch);\n if (runs.length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"No check runs found — CI may not have started yet; try again shortly.\",\n },\n ],\n details: undefined,\n };\n }\n const completed = runs.filter((run) => run.status === \"completed\");\n const failing = completed.filter(\n (run) => run.conclusion !== null && FAILING_CONCLUSIONS.has(run.conclusion),\n );\n const summary = `${runs.length} check(s): ${completed.length} completed, ${runs.length - completed.length} running, ${failing.length} failing`;\n return {\n content: [\n {\n type: \"text\" as const,\n text: [summary, ...runs.map(formatCheckLine)].join(\"\\n\"),\n },\n ],\n details: undefined,\n };\n },\n });\n\n return { tool, setGithubChecksFunction: setFn };\n}\n"]}
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../../src/adapters/github/tools/checks.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { defineHostFnTool } from \"../../../tools/host-fn-tool.js\";\nexport type { GithubChecksFns } from \"../types.js\";\nimport type { GithubCheckSummary, GithubChecksFns } from \"../types.js\";\n\nconst githubChecksSchema = Type.Object({\n branch: Type.Optional(\n Type.String({\n description:\n \"Branch whose CI checks to read (e.g. a pi/<name> branch you pushed). \" +\n \"Omit in a pull-request conversation to read the PR head's checks.\",\n }),\n ),\n job_id: Type.Optional(\n Type.Number({\n description:\n \"A [job …] id from the summary output (GitHub Actions checks) — returns that \" +\n \"CI job's log (tail) instead of the summary, for diagnosing a failing check.\",\n }),\n ),\n build_id: Type.Optional(\n Type.String({\n description:\n \"A [build …] uuid from the summary output (Cloud Build checks) — returns that \" +\n \"build's log (tail) instead of the summary. Mutually exclusive with job_id.\",\n }),\n ),\n});\n\n/** Conclusions that mean \"this check did not pass and needs attention\". */\nconst FAILING_CONCLUSIONS = new Set([\"failure\", \"timed_out\", \"action_required\", \"cancelled\"]);\n\nfunction formatCheckLine(run: GithubCheckSummary): string {\n const state = run.status === \"completed\" ? (run.conclusion ?? \"unknown\") : run.status;\n const marker =\n run.conclusion === null\n ? \"…\"\n : FAILING_CONCLUSIONS.has(run.conclusion)\n ? \"✗\"\n : run.conclusion === \"success\"\n ? \"✓\"\n : \"−\";\n // GitHub Actions logs fetch via job_id; Cloud Build logs via build_id when\n // the host has GCP credentials. Other external CI keeps logs on its own\n // service, so advertising an id there would only invite doomed fetches.\n const handle =\n run.appSlug === \"github-actions\"\n ? `[job ${run.id}]`\n : run.buildLogAvailable && run.externalId\n ? `[build ${run.externalId}]`\n : `[external CI: ${run.appSlug ?? \"unknown\"} — logs not on GitHub]`;\n const line = `${marker} ${run.name}: ${state} ${handle}${run.url ? ` (${run.url})` : \"\"}`;\n const failed = run.conclusion !== null && FAILING_CONCLUSIONS.has(run.conclusion);\n return failed && run.outputSummary\n ? `${line}\\n ↳ ${run.outputSummary.replace(/\\s*\\n\\s*/g, \" \")}`\n : line;\n}\n\n/**\n * The `github_checks` tool reads CI check runs (GitHub Actions and other\n * check-reporting apps) for a branch or the conversation's PR head, and can\n * fetch one job's log tail so the agent can diagnose failures and iterate.\n * Read-only, host-side; wired per run and only for GitHub conversations.\n */\nexport function createGithubChecksTool(): {\n tool: AgentTool<typeof githubChecksSchema>;\n setGithubChecksFunction: (fns: GithubChecksFns | null) => void;\n} {\n const { tool, setFn } = defineHostFnTool<GithubChecksFns, typeof githubChecksSchema>({\n name: \"github_checks\",\n description:\n \"Read CI status (check runs) for a branch you pushed with github_pr, or for this \" +\n \"conversation's pull request when branch is omitted. Pass job_id (a [job …] id, \" +\n \"GitHub Actions) or build_id (a [build …] uuid, Cloud Build) from the summary to \" +\n \"fetch that run's log. Other external CI checks keep logs on their own service: use \" +\n \"their summary/url, or reproduce the failure locally in ./repo. Only available in \" +\n \"GitHub conversations.\",\n parameters: githubChecksSchema,\n unavailable: \"github_checks is only available in GitHub conversations.\",\n run: async (checksFns, args) => {\n if (args.job_id !== undefined && args.build_id !== undefined) {\n throw new Error(\"Pass either job_id or build_id, not both.\");\n }\n\n if (args.job_id !== undefined) {\n const logText = await checksFns.getJobLog(args.job_id);\n return {\n content: [{ type: \"text\" as const, text: logText || \"(empty log)\" }],\n details: undefined,\n };\n }\n\n if (args.build_id !== undefined) {\n const logText = await checksFns.getBuildLog(args.build_id);\n return {\n content: [{ type: \"text\" as const, text: logText || \"(empty log)\" }],\n details: undefined,\n };\n }\n\n const runs = await checksFns.getChecks(args.branch);\n if (runs.length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"No check runs found — CI may not have started yet; try again shortly.\",\n },\n ],\n details: undefined,\n };\n }\n const completed = runs.filter((run) => run.status === \"completed\");\n const failing = completed.filter(\n (run) => run.conclusion !== null && FAILING_CONCLUSIONS.has(run.conclusion),\n );\n const summary = `${runs.length} check(s): ${completed.length} completed, ${runs.length - completed.length} running, ${failing.length} failing`;\n return {\n content: [\n {\n type: \"text\" as const,\n text: [summary, ...runs.map(formatCheckLine)].join(\"\\n\"),\n },\n ],\n details: undefined,\n };\n },\n });\n\n return { tool, setGithubChecksFunction: setFn };\n}\n"]}