@action-llama/action-llama 0.3.0 → 0.4.1

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 (212) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -102
  3. package/dist/agents/container-entry.js +123 -17
  4. package/dist/agents/container-entry.js.map +1 -1
  5. package/dist/agents/container-runner.d.ts +10 -4
  6. package/dist/agents/container-runner.d.ts.map +1 -1
  7. package/dist/agents/container-runner.js +83 -105
  8. package/dist/agents/container-runner.js.map +1 -1
  9. package/dist/agents/prompt.d.ts +2 -0
  10. package/dist/agents/prompt.d.ts.map +1 -1
  11. package/dist/agents/prompt.js +12 -4
  12. package/dist/agents/prompt.js.map +1 -1
  13. package/dist/agents/runner.d.ts +10 -1
  14. package/dist/agents/runner.d.ts.map +1 -1
  15. package/dist/agents/runner.js +60 -8
  16. package/dist/agents/runner.js.map +1 -1
  17. package/dist/cli/commands/{setup.d.ts → cloud-setup.d.ts} +1 -1
  18. package/dist/cli/commands/cloud-setup.d.ts.map +1 -0
  19. package/dist/cli/commands/cloud-setup.js +565 -0
  20. package/dist/cli/commands/cloud-setup.js.map +1 -0
  21. package/dist/cli/commands/cloud-teardown.d.ts +6 -0
  22. package/dist/cli/commands/cloud-teardown.d.ts.map +1 -0
  23. package/dist/cli/commands/cloud-teardown.js +152 -0
  24. package/dist/cli/commands/cloud-teardown.js.map +1 -0
  25. package/dist/cli/commands/console.d.ts.map +1 -1
  26. package/dist/cli/commands/console.js +108 -8
  27. package/dist/cli/commands/console.js.map +1 -1
  28. package/dist/cli/commands/creds.d.ts +2 -0
  29. package/dist/cli/commands/creds.d.ts.map +1 -0
  30. package/dist/cli/commands/creds.js +62 -0
  31. package/dist/cli/commands/creds.js.map +1 -0
  32. package/dist/cli/commands/doctor.d.ts +7 -0
  33. package/dist/cli/commands/doctor.d.ts.map +1 -0
  34. package/dist/cli/commands/doctor.js +405 -0
  35. package/dist/cli/commands/doctor.js.map +1 -0
  36. package/dist/cli/commands/logs.d.ts +1 -0
  37. package/dist/cli/commands/logs.d.ts.map +1 -1
  38. package/dist/cli/commands/logs.js +67 -0
  39. package/dist/cli/commands/logs.js.map +1 -1
  40. package/dist/cli/commands/new.d.ts.map +1 -1
  41. package/dist/cli/commands/new.js +9 -2
  42. package/dist/cli/commands/new.js.map +1 -1
  43. package/dist/cli/commands/run.d.ts +6 -0
  44. package/dist/cli/commands/run.d.ts.map +1 -0
  45. package/dist/cli/commands/run.js +121 -0
  46. package/dist/cli/commands/run.js.map +1 -0
  47. package/dist/cli/commands/start.d.ts +3 -1
  48. package/dist/cli/commands/start.d.ts.map +1 -1
  49. package/dist/cli/commands/start.js +52 -16
  50. package/dist/cli/commands/start.js.map +1 -1
  51. package/dist/cli/commands/status.d.ts +1 -0
  52. package/dist/cli/commands/status.d.ts.map +1 -1
  53. package/dist/cli/commands/status.js +39 -2
  54. package/dist/cli/commands/status.js.map +1 -1
  55. package/dist/cli/main.js +58 -16
  56. package/dist/cli/main.js.map +1 -1
  57. package/dist/credentials/builtins/aws.d.ts +4 -0
  58. package/dist/credentials/builtins/aws.d.ts.map +1 -0
  59. package/dist/credentials/builtins/aws.js +33 -0
  60. package/dist/credentials/builtins/aws.js.map +1 -0
  61. package/dist/credentials/builtins/bugsnag-token.d.ts +4 -0
  62. package/dist/credentials/builtins/bugsnag-token.d.ts.map +1 -0
  63. package/dist/credentials/builtins/bugsnag-token.js +18 -0
  64. package/dist/credentials/builtins/bugsnag-token.js.map +1 -0
  65. package/dist/credentials/builtins/github-webhook-secret.d.ts.map +1 -1
  66. package/dist/credentials/builtins/github-webhook-secret.js +2 -1
  67. package/dist/credentials/builtins/github-webhook-secret.js.map +1 -1
  68. package/dist/credentials/builtins/index.d.ts.map +1 -1
  69. package/dist/credentials/builtins/index.js +10 -0
  70. package/dist/credentials/builtins/index.js.map +1 -1
  71. package/dist/credentials/builtins/netlify-token.d.ts +4 -0
  72. package/dist/credentials/builtins/netlify-token.d.ts.map +1 -0
  73. package/dist/credentials/builtins/netlify-token.js +18 -0
  74. package/dist/credentials/builtins/netlify-token.js.map +1 -0
  75. package/dist/credentials/builtins/openai-key.d.ts +4 -0
  76. package/dist/credentials/builtins/openai-key.d.ts.map +1 -0
  77. package/dist/credentials/builtins/openai-key.js +38 -0
  78. package/dist/credentials/builtins/openai-key.js.map +1 -0
  79. package/dist/credentials/builtins/x-twitter-api.d.ts +4 -0
  80. package/dist/credentials/builtins/x-twitter-api.d.ts.map +1 -0
  81. package/dist/credentials/builtins/x-twitter-api.js +28 -0
  82. package/dist/credentials/builtins/x-twitter-api.js.map +1 -0
  83. package/dist/credentials/prompter.d.ts.map +1 -1
  84. package/dist/credentials/prompter.js +8 -5
  85. package/dist/credentials/prompter.js.map +1 -1
  86. package/dist/docker/cloud-run-runtime.d.ts +61 -0
  87. package/dist/docker/cloud-run-runtime.d.ts.map +1 -0
  88. package/dist/docker/cloud-run-runtime.js +510 -0
  89. package/dist/docker/cloud-run-runtime.js.map +1 -0
  90. package/dist/docker/ecs-runtime.d.ts +73 -0
  91. package/dist/docker/ecs-runtime.d.ts.map +1 -0
  92. package/dist/docker/ecs-runtime.js +596 -0
  93. package/dist/docker/ecs-runtime.js.map +1 -0
  94. package/dist/docker/image.d.ts.map +1 -1
  95. package/dist/docker/image.js +3 -2
  96. package/dist/docker/image.js.map +1 -1
  97. package/dist/docker/local-runtime.d.ts +19 -0
  98. package/dist/docker/local-runtime.d.ts.map +1 -0
  99. package/dist/docker/local-runtime.js +209 -0
  100. package/dist/docker/local-runtime.js.map +1 -0
  101. package/dist/docker/network.d.ts +1 -1
  102. package/dist/docker/network.d.ts.map +1 -1
  103. package/dist/docker/network.js +2 -1
  104. package/dist/docker/network.js.map +1 -1
  105. package/dist/docker/runtime.d.ts +90 -0
  106. package/dist/docker/runtime.d.ts.map +1 -0
  107. package/dist/docker/runtime.js +2 -0
  108. package/dist/docker/runtime.js.map +1 -0
  109. package/dist/gateway/index.d.ts +8 -2
  110. package/dist/gateway/index.d.ts.map +1 -1
  111. package/dist/gateway/index.js +16 -8
  112. package/dist/gateway/index.js.map +1 -1
  113. package/dist/gateway/routes/credentials.d.ts +5 -0
  114. package/dist/gateway/routes/credentials.d.ts.map +1 -0
  115. package/dist/gateway/routes/credentials.js +17 -0
  116. package/dist/gateway/routes/credentials.js.map +1 -0
  117. package/dist/gateway/routes/logs.d.ts +5 -0
  118. package/dist/gateway/routes/logs.d.ts.map +1 -0
  119. package/dist/gateway/routes/logs.js +31 -0
  120. package/dist/gateway/routes/logs.js.map +1 -0
  121. package/dist/gateway/routes/shutdown.d.ts +2 -1
  122. package/dist/gateway/routes/shutdown.d.ts.map +1 -1
  123. package/dist/gateway/routes/shutdown.js +7 -16
  124. package/dist/gateway/routes/shutdown.js.map +1 -1
  125. package/dist/gateway/routes/webhooks.d.ts +2 -1
  126. package/dist/gateway/routes/webhooks.d.ts.map +1 -1
  127. package/dist/gateway/routes/webhooks.js +11 -4
  128. package/dist/gateway/routes/webhooks.js.map +1 -1
  129. package/dist/gateway/types.d.ts +6 -0
  130. package/dist/gateway/types.d.ts.map +1 -0
  131. package/dist/gateway/types.js +2 -0
  132. package/dist/gateway/types.js.map +1 -0
  133. package/dist/scheduler/index.d.ts +3 -2
  134. package/dist/scheduler/index.d.ts.map +1 -1
  135. package/dist/scheduler/index.js +286 -67
  136. package/dist/scheduler/index.js.map +1 -1
  137. package/dist/setup/scaffold.d.ts.map +1 -1
  138. package/dist/setup/scaffold.js +110 -50
  139. package/dist/setup/scaffold.js.map +1 -1
  140. package/dist/setup/validators.d.ts +14 -0
  141. package/dist/setup/validators.d.ts.map +1 -1
  142. package/dist/setup/validators.js +53 -0
  143. package/dist/setup/validators.js.map +1 -1
  144. package/dist/shared/asm-backend.d.ts +25 -0
  145. package/dist/shared/asm-backend.d.ts.map +1 -0
  146. package/dist/shared/asm-backend.js +107 -0
  147. package/dist/shared/asm-backend.js.map +1 -0
  148. package/dist/shared/aws-constants.d.ts +55 -0
  149. package/dist/shared/aws-constants.d.ts.map +1 -0
  150. package/dist/shared/aws-constants.js +55 -0
  151. package/dist/shared/aws-constants.js.map +1 -0
  152. package/dist/shared/config.d.ts +25 -5
  153. package/dist/shared/config.d.ts.map +1 -1
  154. package/dist/shared/config.js +15 -22
  155. package/dist/shared/config.js.map +1 -1
  156. package/dist/shared/credential-backend.d.ts +28 -0
  157. package/dist/shared/credential-backend.d.ts.map +1 -0
  158. package/dist/shared/credential-backend.js +2 -0
  159. package/dist/shared/credential-backend.js.map +1 -0
  160. package/dist/shared/credentials.d.ts +42 -4
  161. package/dist/shared/credentials.d.ts.map +1 -1
  162. package/dist/shared/credentials.js +83 -6
  163. package/dist/shared/credentials.js.map +1 -1
  164. package/dist/shared/filesystem-backend.d.ts +18 -0
  165. package/dist/shared/filesystem-backend.d.ts.map +1 -0
  166. package/dist/shared/filesystem-backend.js +86 -0
  167. package/dist/shared/filesystem-backend.js.map +1 -0
  168. package/dist/shared/gsm-backend.d.ts +35 -0
  169. package/dist/shared/gsm-backend.d.ts.map +1 -0
  170. package/dist/shared/gsm-backend.js +208 -0
  171. package/dist/shared/gsm-backend.js.map +1 -0
  172. package/dist/shared/remote.d.ts +11 -0
  173. package/dist/shared/remote.d.ts.map +1 -0
  174. package/dist/shared/remote.js +29 -0
  175. package/dist/shared/remote.js.map +1 -0
  176. package/dist/tui/App.d.ts.map +1 -1
  177. package/dist/tui/App.js +13 -4
  178. package/dist/tui/App.js.map +1 -1
  179. package/dist/tui/plain-logger.d.ts +5 -0
  180. package/dist/tui/plain-logger.d.ts.map +1 -0
  181. package/dist/tui/plain-logger.js +80 -0
  182. package/dist/tui/plain-logger.js.map +1 -0
  183. package/dist/tui/status-tracker.d.ts +3 -2
  184. package/dist/tui/status-tracker.d.ts.map +1 -1
  185. package/dist/tui/status-tracker.js.map +1 -1
  186. package/dist/webhooks/definitions/github.js +1 -1
  187. package/dist/webhooks/definitions/github.js.map +1 -1
  188. package/dist/webhooks/definitions/sentry.js +1 -1
  189. package/dist/webhooks/definitions/sentry.js.map +1 -1
  190. package/dist/webhooks/providers/github.d.ts +1 -1
  191. package/dist/webhooks/providers/github.d.ts.map +1 -1
  192. package/dist/webhooks/providers/github.js +13 -9
  193. package/dist/webhooks/providers/github.js.map +1 -1
  194. package/dist/webhooks/providers/sentry.d.ts +1 -1
  195. package/dist/webhooks/providers/sentry.d.ts.map +1 -1
  196. package/dist/webhooks/providers/sentry.js +12 -9
  197. package/dist/webhooks/providers/sentry.js.map +1 -1
  198. package/dist/webhooks/registry.d.ts +1 -1
  199. package/dist/webhooks/registry.d.ts.map +1 -1
  200. package/dist/webhooks/registry.js +20 -13
  201. package/dist/webhooks/registry.js.map +1 -1
  202. package/dist/webhooks/types.d.ts +16 -6
  203. package/dist/webhooks/types.d.ts.map +1 -1
  204. package/docker/Dockerfile +4 -1
  205. package/package.json +10 -1
  206. package/dist/cli/commands/setup.d.ts.map +0 -1
  207. package/dist/cli/commands/setup.js +0 -50
  208. package/dist/cli/commands/setup.js.map +0 -1
  209. package/dist/docker/container.d.ts +0 -19
  210. package/dist/docker/container.d.ts.map +0 -1
  211. package/dist/docker/container.js +0 -73
  212. package/dist/docker/container.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import type { Router } from "../router.js";
2
2
  import type { WebhookRegistry } from "../../webhooks/registry.js";
3
3
  import type { Logger } from "../../shared/logger.js";
4
- export declare function registerWebhookRoutes(router: Router, registry: WebhookRegistry, webhookSecrets: Record<string, string | undefined>, logger: Logger): void;
4
+ import type { StatusTracker } from "../../tui/status-tracker.js";
5
+ export declare function registerWebhookRoutes(router: Router, registry: WebhookRegistry, webhookSecrets: Record<string, Record<string, string>>, logger: Logger, statusTracker?: StatusTracker): void;
5
6
  //# sourceMappingURL=webhooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/gateway/routes/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAClD,MAAM,EAAE,MAAM,GACb,IAAI,CA+DN"}
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/gateway/routes/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACtD,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,aAAa,GAC5B,IAAI,CAsEN"}
@@ -1,11 +1,12 @@
1
1
  import { readBody, sendJson, sendError } from "../router.js";
2
- export function registerWebhookRoutes(router, registry, webhookSecrets, logger) {
2
+ export function registerWebhookRoutes(router, registry, webhookSecrets, logger, statusTracker) {
3
3
  router.post("/webhooks/:source", async (req, res, params) => {
4
4
  const source = params.source;
5
5
  logger.debug({ source }, "webhook request received");
6
6
  const provider = registry.getProvider(source);
7
7
  if (!provider) {
8
8
  logger.warn({ source }, "webhook rejected: unknown source");
9
+ statusTracker?.addLogLine("webhook", `Rejected: unknown source "${source}"`);
9
10
  sendError(res, 404, `unknown webhook source: ${source}`);
10
11
  return;
11
12
  }
@@ -15,6 +16,7 @@ export function registerWebhookRoutes(router, registry, webhookSecrets, logger)
15
16
  }
16
17
  catch (err) {
17
18
  logger.error({ err, source }, "webhook body read failed");
19
+ statusTracker?.addLogLine("webhook", `Failed to read body from ${source}: ${err.message}`);
18
20
  sendError(res, 400, "failed to read request body");
19
21
  return;
20
22
  }
@@ -31,15 +33,20 @@ export function registerWebhookRoutes(router, registry, webhookSecrets, logger)
31
33
  delivery: headers["x-github-delivery"],
32
34
  hasSignature: !!headers["x-hub-signature-256"],
33
35
  }, "webhook headers");
34
- const secret = webhookSecrets[source];
35
- const result = registry.dispatch(source, headers, rawBody, secret);
36
+ const secrets = webhookSecrets[source];
37
+ const result = registry.dispatch(source, headers, rawBody, secrets);
36
38
  if (!result.ok) {
37
39
  const status = result.errors?.includes("signature validation failed") ? 401 : 400;
40
+ const errorMsg = result.errors?.[0] || "dispatch failed";
38
41
  logger.warn({ source, status, errors: result.errors }, "webhook dispatch failed");
39
- sendError(res, status, result.errors?.[0] || "dispatch failed");
42
+ statusTracker?.addLogLine("webhook", `${source}: ${errorMsg}`);
43
+ sendError(res, status, errorMsg);
40
44
  return;
41
45
  }
42
46
  logger.info({ source, matched: result.matched, skipped: result.skipped }, "webhook dispatched");
47
+ if (result.matched > 0) {
48
+ statusTracker?.addLogLine("webhook", `${source}: dispatched to ${result.matched} agent${result.matched !== 1 ? "s" : ""}`);
49
+ }
43
50
  sendJson(res, 200, {
44
51
  ok: true,
45
52
  matched: result.matched,
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../../src/gateway/routes/webhooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../../src/gateway/routes/webhooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface ContainerRegistration {
2
+ containerName: string;
3
+ credentials?: Record<string, Record<string, Record<string, string>>>;
4
+ onLogLine?: (line: string) => void;
5
+ }
6
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gateway/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACrE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/gateway/types.ts"],"names":[],"mappings":""}
@@ -1,13 +1,14 @@
1
1
  import { Cron } from "croner";
2
2
  import type { GlobalConfig } from "../shared/config.js";
3
+ import { type RunOutcome } from "../agents/runner.js";
3
4
  import type { StatusTracker } from "../tui/status-tracker.js";
4
5
  import { WebhookRegistry } from "../webhooks/registry.js";
5
6
  import type { GatewayServer } from "../gateway/index.js";
6
7
  interface RunnerLike {
7
8
  isRunning: boolean;
8
- run(prompt: string): Promise<void>;
9
+ run(prompt: string): Promise<RunOutcome>;
9
10
  }
10
- export declare function startScheduler(projectPath: string, globalConfigOverride?: GlobalConfig, statusTracker?: StatusTracker): Promise<{
11
+ export declare function startScheduler(projectPath: string, globalConfigOverride?: GlobalConfig, statusTracker?: StatusTracker, cloudMode?: boolean): Promise<{
11
12
  cronJobs: Cron<undefined>[];
12
13
  runners: Record<string, RunnerLike>;
13
14
  gateway: GatewayServer | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scheduler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAKrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,UAAU,UAAU;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,aAAa;;;;;;;GAyQ3H"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scheduler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAIrE,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD,UAAU,UAAU;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1C;AAmHD,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,OAAO;;;;;;;GAqZhJ"}
@@ -1,15 +1,106 @@
1
1
  import { Cron } from "croner";
2
2
  import { mkdirSync } from "fs";
3
3
  import { loadGlobalConfig, loadAgentConfig, discoverAgents, validateAgentConfig } from "../shared/config.js";
4
- import { requireCredentialRef, parseCredentialRef, loadCredentialField } from "../shared/credentials.js";
4
+ import { backendRequireCredentialRef, backendLoadField, backendListInstances } from "../shared/credentials.js";
5
5
  import { createLogger, createFileOnlyLogger } from "../shared/logger.js";
6
6
  import { agentDir } from "../shared/paths.js";
7
7
  import { AgentRunner } from "../agents/runner.js";
8
- import { buildScheduledPrompt, buildWebhookPrompt } from "../agents/prompt.js";
8
+ import { buildScheduledPrompt, buildWebhookPrompt, buildTriggeredPrompt } from "../agents/prompt.js";
9
9
  import { WebhookRegistry } from "../webhooks/registry.js";
10
10
  import { GitHubWebhookProvider } from "../webhooks/providers/github.js";
11
11
  import { SentryWebhookProvider } from "../webhooks/providers/sentry.js";
12
- export async function startScheduler(projectPath, globalConfigOverride, statusTracker) {
12
+ import { AWS_CONSTANTS } from "../shared/aws-constants.js";
13
+ // Provider type → credential type for loading secrets
14
+ const PROVIDER_TO_CREDENTIAL = {
15
+ github: "github_webhook_secret",
16
+ sentry: "sentry_client_secret",
17
+ };
18
+ function buildFilterFromTrigger(trigger) {
19
+ const providerType = trigger.type;
20
+ if (providerType === "github") {
21
+ const f = {};
22
+ if (trigger.events)
23
+ f.events = trigger.events;
24
+ if (trigger.actions)
25
+ f.actions = trigger.actions;
26
+ if (trigger.repos)
27
+ f.repos = trigger.repos;
28
+ if (trigger.labels)
29
+ f.labels = trigger.labels;
30
+ if (trigger.assignee)
31
+ f.assignee = trigger.assignee;
32
+ if (trigger.author)
33
+ f.author = trigger.author;
34
+ if (trigger.branches)
35
+ f.branches = trigger.branches;
36
+ return Object.keys(f).length > 0 ? f : undefined;
37
+ }
38
+ if (providerType === "sentry") {
39
+ const f = {};
40
+ if (trigger.resources)
41
+ f.resources = trigger.resources;
42
+ return Object.keys(f).length > 0 ? f : undefined;
43
+ }
44
+ return undefined;
45
+ }
46
+ const DEFAULT_MAX_RERUNS = 10;
47
+ const DEFAULT_MAX_TRIGGER_DEPTH = 3;
48
+ function dispatchTriggers(triggers, sourceAgent, depth, ctx) {
49
+ for (const { agent, context } of triggers) {
50
+ if (agent === sourceAgent) {
51
+ ctx.logger.warn({ source: sourceAgent }, "agent cannot trigger itself, skipping");
52
+ continue;
53
+ }
54
+ if (depth >= ctx.maxTriggerDepth) {
55
+ ctx.logger.warn({ source: sourceAgent, target: agent, depth, maxTriggerDepth: ctx.maxTriggerDepth }, "trigger depth limit reached, skipping");
56
+ continue;
57
+ }
58
+ const targetConfig = ctx.agentConfigs.find((a) => a.name === agent);
59
+ if (!targetConfig) {
60
+ ctx.logger.warn({ source: sourceAgent, target: agent }, "trigger target agent not found, skipping");
61
+ continue;
62
+ }
63
+ const runner = ctx.runners[agent];
64
+ if (runner.isRunning) {
65
+ ctx.logger.warn({ source: sourceAgent, target: agent }, "trigger target agent is busy, skipping");
66
+ continue;
67
+ }
68
+ ctx.logger.info({ source: sourceAgent, target: agent, depth }, "agent trigger firing");
69
+ const prompt = buildTriggeredPrompt(targetConfig, sourceAgent, context);
70
+ runTriggered(runner, targetConfig, prompt, sourceAgent, depth + 1, ctx).catch((err) => {
71
+ ctx.logger.error({ err, target: agent }, "triggered run failed");
72
+ });
73
+ }
74
+ }
75
+ async function runTriggered(runner, agentConfig, prompt, sourceAgent, depth, ctx) {
76
+ const { result, triggers } = await runner.run(prompt);
77
+ if (triggers.length > 0) {
78
+ dispatchTriggers(triggers, agentConfig.name, depth, ctx);
79
+ }
80
+ // No reruns for triggered runs — they respond to a specific event
81
+ if (result === "completed") {
82
+ ctx.logger.info(`${agentConfig.name} triggered run completed`);
83
+ }
84
+ }
85
+ async function runWithReruns(runner, agentConfig, depth, ctx) {
86
+ let { result, triggers } = await runner.run(buildScheduledPrompt(agentConfig));
87
+ if (triggers.length > 0) {
88
+ dispatchTriggers(triggers, agentConfig.name, depth, ctx);
89
+ }
90
+ let reruns = 0;
91
+ while (result === "completed" && reruns < ctx.maxReruns) {
92
+ reruns++;
93
+ ctx.logger.info({ rerun: reruns, maxReruns: ctx.maxReruns }, `${agentConfig.name} did work, re-running immediately`);
94
+ ({ result, triggers } = await runner.run(buildScheduledPrompt(agentConfig)));
95
+ if (triggers.length > 0) {
96
+ dispatchTriggers(triggers, agentConfig.name, depth, ctx);
97
+ }
98
+ }
99
+ if (result === "completed" && reruns >= ctx.maxReruns) {
100
+ ctx.logger.warn({ maxReruns: ctx.maxReruns }, `${agentConfig.name} hit max reruns limit`);
101
+ }
102
+ }
103
+ export async function startScheduler(projectPath, globalConfigOverride, statusTracker, cloudMode) {
13
104
  const mkLogger = statusTracker ? createFileOnlyLogger : createLogger;
14
105
  const logger = mkLogger(projectPath, "scheduler");
15
106
  logger.info("Starting scheduler...");
@@ -27,17 +118,19 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
27
118
  // Validate credentials exist for each agent
28
119
  const allCredentials = new Set(agentConfigs.flatMap((a) => a.credentials));
29
120
  for (const credRef of allCredentials) {
30
- requireCredentialRef(credRef);
121
+ await backendRequireCredentialRef(credRef);
31
122
  }
123
+ const maxReruns = globalConfig.maxReruns ?? DEFAULT_MAX_RERUNS;
124
+ const maxTriggerDepth = globalConfig.maxTriggerDepth ?? DEFAULT_MAX_TRIGGER_DEPTH;
32
125
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
33
- const dockerEnabled = globalConfig.docker?.enabled === true;
34
- const anyWebhooks = agentConfigs.some((a) => a.webhooks?.filters?.length);
126
+ const dockerEnabled = globalConfig.local?.enabled === true;
127
+ const anyWebhooks = agentConfigs.some((a) => a.webhooks?.length);
35
128
  // Validate pi_auth is not used with Docker (containers can't access host auth storage)
36
129
  if (dockerEnabled) {
37
130
  for (const config of agentConfigs) {
38
131
  if (config.model.authType === "pi_auth") {
39
132
  throw new Error(`Agent "${config.name}" uses pi_auth which is not supported in Docker mode. ` +
40
- `Either switch to api_key/oauth_token (run 'al setup') or use --dangerous-no-docker.`);
133
+ `Either switch to api_key/oauth_token (run 'al doctor') or use --no-docker.`);
41
134
  }
42
135
  }
43
136
  }
@@ -49,64 +142,176 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
49
142
  // Register providers
50
143
  webhookRegistry.registerProvider(new GitHubWebhookProvider());
51
144
  webhookRegistry.registerProvider(new SentryWebhookProvider());
52
- // Load GitHub webhook secret from credentials (if configured)
53
- const githubSecretRef = globalConfig.webhooks?.secretCredentials?.github
54
- || "github_webhook_secret:default";
55
- const { type: ghType, instance: ghInst } = parseCredentialRef(githubSecretRef);
56
- const githubSecret = loadCredentialField(ghType, ghInst, "secret");
57
- if (githubSecret) {
58
- webhookSecrets.github = githubSecret;
59
- }
60
- // Load Sentry webhook secret from credentials (if configured)
61
- const sentrySecretRef = globalConfig.webhooks?.secretCredentials?.sentry
62
- || "sentry_client_secret:default";
63
- const { type: sType, instance: sInst } = parseCredentialRef(sentrySecretRef);
64
- const sentrySecret = loadCredentialField(sType, sInst, "secret");
65
- if (sentrySecret) {
66
- webhookSecrets.sentry = sentrySecret;
145
+ // Load all GitHub webhook secrets (instanceName secret value)
146
+ const ghInstances = await backendListInstances("github_webhook_secret");
147
+ const ghSecrets = {};
148
+ for (const inst of ghInstances) {
149
+ const secret = await backendLoadField("github_webhook_secret", inst, "secret");
150
+ if (secret)
151
+ ghSecrets[inst] = secret;
152
+ }
153
+ if (Object.keys(ghSecrets).length > 0) {
154
+ webhookSecrets.github = ghSecrets;
155
+ logger.info({ count: Object.keys(ghSecrets).length }, "loaded GitHub webhook secrets");
156
+ }
157
+ // Load all Sentry webhook secrets (instanceName secret value)
158
+ const sentryInstances = await backendListInstances("sentry_client_secret");
159
+ const sentrySecrets = {};
160
+ for (const inst of sentryInstances) {
161
+ const secret = await backendLoadField("sentry_client_secret", inst, "secret");
162
+ if (secret)
163
+ sentrySecrets[inst] = secret;
164
+ }
165
+ if (Object.keys(sentrySecrets).length > 0) {
166
+ webhookSecrets.sentry = sentrySecrets;
167
+ logger.info({ count: Object.keys(sentrySecrets).length }, "loaded Sentry webhook secrets");
67
168
  }
68
169
  }
69
170
  let gateway;
70
- let baseImage = "al-agent:latest";
171
+ let runtime;
172
+ let baseImage = AWS_CONSTANTS.DEFAULT_IMAGE;
71
173
  const agentImages = {};
174
+ // Determine runtime type from cloud mode or local
175
+ const useCloudRuntime = cloudMode && globalConfig.cloud;
176
+ const runtimeType = useCloudRuntime ? globalConfig.cloud.provider : "local";
177
+ // Register agents early so the TUI shows them during image builds
178
+ for (const agentConfig of agentConfigs) {
179
+ statusTracker?.registerAgent(agentConfig.name);
180
+ }
72
181
  if (dockerEnabled) {
73
- logger.info("Docker mode enabled — initializing docker infrastructure");
74
- // 1. Verify Docker is available and running
75
- const { execFileSync } = await import("child_process");
76
- try {
77
- execFileSync("docker", ["info"], { stdio: "pipe", timeout: 10000 });
78
- }
79
- catch {
80
- throw new Error("Docker is not running. Start Docker Desktop (or the Docker daemon) and try again, " +
81
- "or use --dangerous-no-docker to run without container isolation.");
82
- }
83
- // 2. Ensure Docker network exists
84
- logger.info("Ensuring Docker network...");
85
- const { ensureNetwork } = await import("../docker/network.js");
86
- ensureNetwork();
87
- // 3. Ensure base Docker image is built (may take a while on first run)
88
- baseImage = globalConfig.docker?.image || "al-agent:latest";
89
- logger.info({ image: baseImage }, "Ensuring base Docker image (this may take a few minutes on first run)...");
90
- const { ensureImage, ensureAgentImage } = await import("../docker/image.js");
91
- ensureImage(baseImage);
92
- // 4. Build per-agent images for agents with a custom Dockerfile
182
+ logger.info({ runtime: runtimeType }, "Docker mode enabled — initializing infrastructure");
183
+ // 1. Create the container runtime
184
+ if (useCloudRuntime && globalConfig.cloud.provider === "cloud-run") {
185
+ const { CloudRunJobRuntime } = await import("../docker/cloud-run-runtime.js");
186
+ const { gcpProject, region, artifactRegistry, serviceAccount, secretPrefix } = globalConfig.cloud;
187
+ if (!gcpProject || !region || !artifactRegistry || !serviceAccount) {
188
+ throw new Error("Cloud Run runtime requires cloud.gcpProject, cloud.region, " +
189
+ "cloud.artifactRegistry, and cloud.serviceAccount in config.toml");
190
+ }
191
+ runtime = new CloudRunJobRuntime({ gcpProject, region, artifactRegistry, serviceAccount, secretPrefix });
192
+ logger.info({ gcpProject, region }, "Using Cloud Run Jobs runtime");
193
+ }
194
+ else if (useCloudRuntime && globalConfig.cloud.provider === "ecs") {
195
+ const { ECSFargateRuntime } = await import("../docker/ecs-runtime.js");
196
+ const cc = globalConfig.cloud;
197
+ if (!cc.awsRegion || !cc.ecsCluster || !cc.ecrRepository || !cc.executionRoleArn || !cc.taskRoleArn || !cc.subnets?.length) {
198
+ throw new Error("ECS runtime requires cloud.awsRegion, cloud.ecsCluster, cloud.ecrRepository, " +
199
+ "cloud.executionRoleArn, cloud.taskRoleArn, and cloud.subnets in config.toml");
200
+ }
201
+ runtime = new ECSFargateRuntime({
202
+ awsRegion: cc.awsRegion,
203
+ ecsCluster: cc.ecsCluster,
204
+ ecrRepository: cc.ecrRepository,
205
+ executionRoleArn: cc.executionRoleArn,
206
+ taskRoleArn: cc.taskRoleArn,
207
+ subnets: cc.subnets,
208
+ securityGroups: cc.securityGroups,
209
+ secretPrefix: cc.awsSecretPrefix,
210
+ });
211
+ logger.info({ region: cc.awsRegion, cluster: cc.ecsCluster }, "Using ECS Fargate runtime");
212
+ }
213
+ else {
214
+ // Local runtime needs Docker running
215
+ const { execFileSync } = await import("child_process");
216
+ try {
217
+ execFileSync("docker", ["info"], { stdio: "pipe", timeout: 10000 });
218
+ }
219
+ catch {
220
+ throw new Error("Docker is not running. Start Docker Desktop (or the Docker daemon) and try again, " +
221
+ "or use --no-docker to run without container isolation.");
222
+ }
223
+ const { LocalDockerRuntime } = await import("../docker/local-runtime.js");
224
+ runtime = new LocalDockerRuntime();
225
+ // Local-only: ensure Docker network
226
+ logger.info("Ensuring Docker network...");
227
+ const { ensureNetwork } = await import("../docker/network.js");
228
+ ensureNetwork();
229
+ }
230
+ // 2. Build base image via the runtime
231
+ const { resolve: resolvePath, dirname } = await import("path");
232
+ const { fileURLToPath } = await import("url");
233
+ const packageRoot = resolvePath(dirname(fileURLToPath(import.meta.url)), "..", "..");
234
+ baseImage = globalConfig.local?.image || AWS_CONSTANTS.DEFAULT_IMAGE;
235
+ logger.info({ image: baseImage }, "Building base image (this may take a few minutes on first run)...");
236
+ // Show all agents as "building" during the base image build
237
+ const setBuildProgress = (message) => {
238
+ for (const ac of agentConfigs) {
239
+ statusTracker?.setAgentStatusText(ac.name, message);
240
+ }
241
+ };
242
+ for (const ac of agentConfigs) {
243
+ statusTracker?.setAgentState(ac.name, "building");
244
+ }
245
+ if (runtimeType === "local") {
246
+ // Local: only build if image doesn't exist yet
247
+ const { imageExists } = await import("../docker/image.js");
248
+ if (!imageExists(baseImage)) {
249
+ setBuildProgress("Building base image");
250
+ await runtime.buildImage({
251
+ tag: baseImage, dockerfile: "docker/Dockerfile", contextDir: packageRoot,
252
+ onProgress: setBuildProgress,
253
+ });
254
+ }
255
+ }
256
+ else {
257
+ // Cloud: always build (Cloud Build handles caching)
258
+ baseImage = await runtime.buildImage({
259
+ tag: baseImage, dockerfile: "docker/Dockerfile", contextDir: packageRoot,
260
+ onProgress: setBuildProgress,
261
+ });
262
+ }
263
+ // 3. Build per-agent images for agents with a custom Dockerfile
264
+ const { existsSync } = await import("fs");
93
265
  for (const agentConfig of agentConfigs) {
94
- const image = ensureAgentImage(agentConfig.name, projectPath, baseImage);
266
+ const agentDockerfile = resolvePath(projectPath, agentConfig.name, "Dockerfile");
267
+ if (!existsSync(agentDockerfile)) {
268
+ agentImages[agentConfig.name] = baseImage;
269
+ continue;
270
+ }
271
+ statusTracker?.setAgentState(agentConfig.name, "building");
272
+ statusTracker?.setAgentStatusText(agentConfig.name, "Building custom image");
273
+ const agentImageTag = AWS_CONSTANTS.agentImage(agentConfig.name);
274
+ const image = await runtime.buildImage({
275
+ tag: agentImageTag,
276
+ dockerfile: agentDockerfile,
277
+ contextDir: packageRoot,
278
+ baseImage,
279
+ onProgress: (msg) => statusTracker?.setAgentStatusText(agentConfig.name, msg),
280
+ });
95
281
  agentImages[agentConfig.name] = image;
96
- if (image !== baseImage) {
97
- logger.info({ agent: agentConfig.name, image }, "Built custom agent image");
282
+ logger.info({ agent: agentConfig.name, image }, "Built custom agent image");
283
+ }
284
+ // 4. Push images to remote registry if needed (no-op for local, tags+pushes for cloud)
285
+ if (runtimeType !== "local") {
286
+ for (const agentConfig of agentConfigs) {
287
+ const currentImage = agentImages[agentConfig.name] || baseImage;
288
+ if (!currentImage.includes("/")) {
289
+ // Looks like a local tag — needs pushing
290
+ statusTracker?.setAgentStatusText(agentConfig.name, "Pushing image to registry");
291
+ const remoteImage = await runtime.pushImage(currentImage);
292
+ agentImages[agentConfig.name] = remoteImage;
293
+ logger.info({ agent: agentConfig.name, image: remoteImage }, "Pushed image to registry");
294
+ }
98
295
  }
99
296
  }
297
+ // Reset all agents back to idle after builds complete
298
+ for (const ac of agentConfigs) {
299
+ statusTracker?.setAgentState(ac.name, "idle");
300
+ }
100
301
  logger.info("Docker infrastructure ready");
101
- // 4. Start gateway (with webhook registry if configured)
102
- const { startGateway } = await import("../gateway/index.js");
103
- const gatewayPort = globalConfig.gateway?.port || 8080;
104
- gateway = await startGateway({
105
- port: gatewayPort,
106
- logger: mkLogger(projectPath, "gateway"),
107
- webhookRegistry,
108
- webhookSecrets,
109
- });
302
+ // 6. Start gateway if the runtime needs it or webhooks are configured
303
+ if (runtime.needsGateway || anyWebhooks) {
304
+ const { startGateway } = await import("../gateway/index.js");
305
+ const gatewayPort = globalConfig.gateway?.port || 8080;
306
+ gateway = await startGateway({
307
+ port: gatewayPort,
308
+ logger: mkLogger(projectPath, "gateway"),
309
+ killContainer: (name) => runtime.kill(name),
310
+ webhookRegistry,
311
+ webhookSecrets,
312
+ statusTracker,
313
+ });
314
+ }
110
315
  }
111
316
  else if (anyWebhooks) {
112
317
  // Start gateway even without docker when webhooks are configured
@@ -118,35 +323,45 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
118
323
  logger: mkLogger(projectPath, "gateway"),
119
324
  webhookRegistry,
120
325
  webhookSecrets,
326
+ statusTracker,
121
327
  });
122
328
  }
123
329
  // Create runners for each agent
124
330
  const runners = {};
125
- if (dockerEnabled && gateway) {
331
+ if (dockerEnabled && runtime) {
126
332
  const { ContainerAgentRunner } = await import("../agents/container-runner.js");
127
333
  const gatewayPort = globalConfig.gateway?.port || 8080;
128
334
  const gatewayUrl = `http://host.docker.internal:${gatewayPort}`;
335
+ // Gateway callbacks — no-ops if gateway isn't running (remote runtimes)
336
+ const registerContainer = gateway
337
+ ? gateway.registerContainer
338
+ : (_secret, _reg) => { };
339
+ const unregisterContainer = gateway
340
+ ? gateway.unregisterContainer
341
+ : (_secret) => { };
129
342
  for (const agentConfig of agentConfigs) {
130
- statusTracker?.registerAgent(agentConfig.name);
131
- runners[agentConfig.name] = new ContainerAgentRunner(globalConfig, agentConfig, mkLogger(projectPath, agentConfig.name), gateway.registerContainer, gatewayUrl, projectPath, agentImages[agentConfig.name] || baseImage, statusTracker);
343
+ runners[agentConfig.name] = new ContainerAgentRunner(runtime, globalConfig, agentConfig, mkLogger(projectPath, agentConfig.name), registerContainer, unregisterContainer, gatewayUrl, projectPath, agentImages[agentConfig.name] || baseImage, statusTracker);
132
344
  }
133
345
  }
134
346
  else {
135
347
  for (const agentConfig of agentConfigs) {
136
348
  mkdirSync(agentDir(projectPath, agentConfig.name), { recursive: true });
137
- statusTracker?.registerAgent(agentConfig.name);
138
349
  runners[agentConfig.name] = new AgentRunner(agentConfig, mkLogger(projectPath, agentConfig.name), projectPath, statusTracker);
139
350
  }
140
351
  }
352
+ const schedulerCtx = { runners, agentConfigs, maxReruns, maxTriggerDepth, logger };
141
353
  // Set up webhook bindings
142
354
  if (webhookRegistry) {
143
355
  for (const agentConfig of agentConfigs) {
144
- if (!agentConfig.webhooks?.filters?.length)
356
+ if (!agentConfig.webhooks?.length)
145
357
  continue;
146
358
  const runner = runners[agentConfig.name];
147
- for (const filter of agentConfig.webhooks.filters) {
359
+ for (const trigger of agentConfig.webhooks) {
360
+ const filter = buildFilterFromTrigger(trigger);
148
361
  webhookRegistry.addBinding({
149
362
  agentName: agentConfig.name,
363
+ source: trigger.source,
364
+ type: trigger.type,
150
365
  filter,
151
366
  trigger: (context) => {
152
367
  if (runner.isRunning) {
@@ -155,7 +370,11 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
155
370
  }
156
371
  logger.info({ agent: agentConfig.name, event: context.event, action: context.action }, "webhook triggering agent");
157
372
  const prompt = buildWebhookPrompt(agentConfig, context);
158
- runner.run(prompt).catch((err) => {
373
+ runner.run(prompt).then(({ triggers }) => {
374
+ if (triggers.length > 0) {
375
+ dispatchTriggers(triggers, agentConfig.name, 0, schedulerCtx);
376
+ }
377
+ }).catch((err) => {
159
378
  logger.error({ err }, `${agentConfig.name} webhook run failed`);
160
379
  });
161
380
  },
@@ -175,7 +394,7 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
175
394
  return;
176
395
  }
177
396
  logger.info(`Triggering ${agentConfig.name} (scheduled)`);
178
- await runner.run(buildScheduledPrompt(agentConfig));
397
+ await runWithReruns(runner, agentConfig, 0, schedulerCtx);
179
398
  });
180
399
  cronJobs.push(job);
181
400
  const nextRun = job.nextRun();
@@ -187,9 +406,9 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
187
406
  const webhookUrls = [];
188
407
  if (anyWebhooks && gateway) {
189
408
  const gatewayPort = globalConfig.gateway?.port || 8080;
190
- const sources = new Set(agentConfigs.flatMap((a) => a.webhooks?.filters?.map((f) => f.source) || []));
191
- for (const source of sources) {
192
- webhookUrls.push(`http://localhost:${gatewayPort}/webhooks/${source}`);
409
+ const providerTypes = new Set(agentConfigs.flatMap((a) => a.webhooks?.map((t) => t.type) || []));
410
+ for (const pt of providerTypes) {
411
+ webhookUrls.push(`http://localhost:${gatewayPort}/webhooks/${pt}`);
193
412
  }
194
413
  }
195
414
  logger.info(`Scheduler running with ${cronJobs.length} scheduled jobs`);
@@ -199,7 +418,7 @@ export async function startScheduler(projectPath, globalConfigOverride, statusTr
199
418
  continue;
200
419
  const runner = runners[agentConfig.name];
201
420
  logger.info(`Initial run for ${agentConfig.name}`);
202
- runner.run(buildScheduledPrompt(agentConfig)).catch((err) => {
421
+ runWithReruns(runner, agentConfig, 0, schedulerCtx).catch((err) => {
203
422
  logger.error({ err }, `Initial ${agentConfig.name} run failed`);
204
423
  });
205
424
  }