@acpus/runtime 0.7.1 → 0.9.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/admission/input.d.ts +5 -0
  3. package/dist/admission/input.d.ts.map +1 -0
  4. package/dist/admission/input.js +17 -0
  5. package/dist/admission/input.js.map +1 -0
  6. package/dist/artifacts/rewrite.d.ts +7 -0
  7. package/dist/artifacts/rewrite.d.ts.map +1 -0
  8. package/dist/artifacts/rewrite.js +28 -0
  9. package/dist/artifacts/rewrite.js.map +1 -0
  10. package/dist/control/agent-overrides.d.ts +4 -0
  11. package/dist/control/agent-overrides.d.ts.map +1 -0
  12. package/dist/control/agent-overrides.js +40 -0
  13. package/dist/control/agent-overrides.js.map +1 -0
  14. package/dist/daemon/loop.d.ts +16 -0
  15. package/dist/daemon/loop.d.ts.map +1 -0
  16. package/dist/daemon/loop.js +191 -0
  17. package/dist/daemon/loop.js.map +1 -0
  18. package/dist/daemon/sessions.d.ts +23 -0
  19. package/dist/daemon/sessions.d.ts.map +1 -0
  20. package/dist/daemon/sessions.js +190 -0
  21. package/dist/daemon/sessions.js.map +1 -0
  22. package/dist/daemon/socket.d.ts +82 -0
  23. package/dist/daemon/socket.d.ts.map +1 -0
  24. package/dist/daemon/socket.js +341 -0
  25. package/dist/daemon/socket.js.map +1 -0
  26. package/dist/daemon/tick.d.ts +9 -0
  27. package/dist/daemon/tick.d.ts.map +1 -0
  28. package/dist/daemon/tick.js +18 -0
  29. package/dist/daemon/tick.js.map +1 -0
  30. package/dist/evaluation/admissible.d.ts +2 -0
  31. package/dist/evaluation/admissible.d.ts.map +1 -0
  32. package/dist/evaluation/admissible.js +50 -0
  33. package/dist/evaluation/admissible.js.map +1 -0
  34. package/dist/evaluation/evaluator.d.ts +14 -0
  35. package/dist/evaluation/evaluator.d.ts.map +1 -0
  36. package/dist/evaluation/evaluator.js +34 -0
  37. package/dist/evaluation/evaluator.js.map +1 -0
  38. package/dist/evaluation/loop-limit.d.ts +4 -0
  39. package/dist/evaluation/loop-limit.d.ts.map +1 -0
  40. package/dist/evaluation/loop-limit.js +9 -0
  41. package/dist/evaluation/loop-limit.js.map +1 -0
  42. package/dist/evaluation/schema.d.ts +4 -0
  43. package/dist/evaluation/schema.d.ts.map +1 -0
  44. package/dist/evaluation/schema.js +92 -0
  45. package/dist/evaluation/schema.js.map +1 -0
  46. package/dist/execution/advance.d.ts +18 -0
  47. package/dist/execution/advance.d.ts.map +1 -0
  48. package/dist/execution/advance.js +57 -0
  49. package/dist/execution/advance.js.map +1 -0
  50. package/dist/execution/agent-node.d.ts +23 -0
  51. package/dist/execution/agent-node.d.ts.map +1 -0
  52. package/dist/execution/agent-node.js +512 -0
  53. package/dist/execution/agent-node.js.map +1 -0
  54. package/dist/execution/duration.d.ts +2 -0
  55. package/dist/execution/duration.d.ts.map +1 -0
  56. package/dist/execution/duration.js +15 -0
  57. package/dist/execution/duration.js.map +1 -0
  58. package/dist/execution/ir.d.ts +3 -0
  59. package/dist/execution/ir.d.ts.map +1 -0
  60. package/dist/execution/ir.js +46 -0
  61. package/dist/execution/ir.js.map +1 -0
  62. package/dist/execution/scheduler.d.ts +59 -0
  63. package/dist/execution/scheduler.d.ts.map +1 -0
  64. package/dist/execution/scheduler.js +306 -0
  65. package/dist/execution/scheduler.js.map +1 -0
  66. package/dist/execution/task-executor.d.ts +14 -0
  67. package/dist/execution/task-executor.d.ts.map +1 -0
  68. package/dist/execution/task-executor.js +186 -0
  69. package/dist/execution/task-executor.js.map +1 -0
  70. package/dist/hooks/config.d.ts +32 -0
  71. package/dist/hooks/config.d.ts.map +1 -0
  72. package/dist/hooks/config.js +112 -0
  73. package/dist/hooks/config.js.map +1 -0
  74. package/dist/hooks/context.d.ts +50 -0
  75. package/dist/hooks/context.d.ts.map +1 -0
  76. package/dist/hooks/context.js +116 -0
  77. package/dist/hooks/context.js.map +1 -0
  78. package/dist/hooks/events.d.ts +22 -0
  79. package/dist/hooks/events.d.ts.map +1 -0
  80. package/dist/hooks/events.js +41 -0
  81. package/dist/hooks/events.js.map +1 -0
  82. package/dist/hooks/journal.d.ts +21 -19
  83. package/dist/hooks/journal.d.ts.map +1 -1
  84. package/dist/hooks/journal.js +1 -48
  85. package/dist/hooks/journal.js.map +1 -1
  86. package/dist/hooks/loader.d.ts +33 -39
  87. package/dist/hooks/loader.d.ts.map +1 -1
  88. package/dist/hooks/loader.js +79 -95
  89. package/dist/hooks/loader.js.map +1 -1
  90. package/dist/hooks/runner.d.ts +14 -41
  91. package/dist/hooks/runner.d.ts.map +1 -1
  92. package/dist/hooks/runner.js +177 -145
  93. package/dist/hooks/runner.js.map +1 -1
  94. package/dist/index.d.ts +13 -28
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +13 -33
  97. package/dist/index.js.map +1 -1
  98. package/dist/runs/advance-runtime.d.ts +50 -0
  99. package/dist/runs/advance-runtime.d.ts.map +1 -0
  100. package/dist/runs/advance-runtime.js +82 -0
  101. package/dist/runs/advance-runtime.js.map +1 -0
  102. package/dist/runs/use-cases.d.ts +111 -0
  103. package/dist/runs/use-cases.d.ts.map +1 -0
  104. package/dist/runs/use-cases.js +539 -0
  105. package/dist/runs/use-cases.js.map +1 -0
  106. package/dist/scheduler/advance.d.ts +65 -0
  107. package/dist/scheduler/advance.d.ts.map +1 -0
  108. package/dist/scheduler/advance.js +435 -0
  109. package/dist/scheduler/advance.js.map +1 -0
  110. package/dist/scheduler/control.d.ts +49 -0
  111. package/dist/scheduler/control.d.ts.map +1 -0
  112. package/dist/scheduler/control.js +212 -0
  113. package/dist/scheduler/control.js.map +1 -0
  114. package/dist/scheduler/event-codec.d.ts +4 -0
  115. package/dist/scheduler/event-codec.d.ts.map +1 -0
  116. package/dist/scheduler/event-codec.js +57 -0
  117. package/dist/scheduler/event-codec.js.map +1 -0
  118. package/dist/scheduler/events.d.ts +193 -0
  119. package/dist/scheduler/events.d.ts.map +1 -0
  120. package/dist/scheduler/events.js +13 -0
  121. package/dist/scheduler/events.js.map +1 -0
  122. package/dist/scheduler/fork-seed.d.ts +38 -0
  123. package/dist/scheduler/fork-seed.d.ts.map +1 -0
  124. package/dist/scheduler/fork-seed.js +590 -0
  125. package/dist/scheduler/fork-seed.js.map +1 -0
  126. package/dist/scheduler/identity.d.ts +8 -0
  127. package/dist/scheduler/identity.d.ts.map +1 -0
  128. package/dist/scheduler/identity.js +49 -0
  129. package/dist/scheduler/identity.js.map +1 -0
  130. package/dist/scheduler/ir-walk.d.ts +3 -0
  131. package/dist/scheduler/ir-walk.d.ts.map +1 -0
  132. package/dist/scheduler/ir-walk.js +25 -0
  133. package/dist/scheduler/ir-walk.js.map +1 -0
  134. package/dist/scheduler/limiter.d.ts +7 -0
  135. package/dist/scheduler/limiter.d.ts.map +1 -0
  136. package/dist/scheduler/limiter.js +10 -0
  137. package/dist/scheduler/limiter.js.map +1 -0
  138. package/dist/scheduler/materialize.d.ts +7 -0
  139. package/dist/scheduler/materialize.d.ts.map +1 -0
  140. package/dist/scheduler/materialize.js +756 -0
  141. package/dist/scheduler/materialize.js.map +1 -0
  142. package/dist/scheduler/membership.d.ts +12 -0
  143. package/dist/scheduler/membership.d.ts.map +1 -0
  144. package/dist/scheduler/membership.js +74 -0
  145. package/dist/scheduler/membership.js.map +1 -0
  146. package/dist/scheduler/node-executor.d.ts +15 -0
  147. package/dist/scheduler/node-executor.d.ts.map +1 -0
  148. package/dist/scheduler/node-executor.js +69 -0
  149. package/dist/scheduler/node-executor.js.map +1 -0
  150. package/dist/scheduler/runtime-runner.d.ts +44 -0
  151. package/dist/scheduler/runtime-runner.d.ts.map +1 -0
  152. package/dist/scheduler/runtime-runner.js +155 -0
  153. package/dist/scheduler/runtime-runner.js.map +1 -0
  154. package/dist/scheduler/scope.d.ts +6 -0
  155. package/dist/scheduler/scope.d.ts.map +1 -0
  156. package/dist/scheduler/scope.js +56 -0
  157. package/dist/scheduler/scope.js.map +1 -0
  158. package/dist/scheduler/store-port.d.ts +211 -0
  159. package/dist/scheduler/store-port.d.ts.map +1 -0
  160. package/dist/scheduler/store-port.js +28 -0
  161. package/dist/scheduler/store-port.js.map +1 -0
  162. package/dist/scheduler/transitions.d.ts +89 -0
  163. package/dist/scheduler/transitions.d.ts.map +1 -0
  164. package/dist/scheduler/transitions.js +967 -0
  165. package/dist/scheduler/transitions.js.map +1 -0
  166. package/dist/scheduler/types.d.ts +146 -0
  167. package/dist/scheduler/types.d.ts.map +1 -0
  168. package/dist/{executors → scheduler}/types.js.map +1 -1
  169. package/dist/store/store.d.ts +390 -0
  170. package/dist/store/store.d.ts.map +1 -0
  171. package/dist/store/store.js +3053 -0
  172. package/dist/store/store.js.map +1 -0
  173. package/dist/visualization/overlay.d.ts +86 -0
  174. package/dist/visualization/overlay.d.ts.map +1 -0
  175. package/dist/visualization/overlay.js +161 -0
  176. package/dist/visualization/overlay.js.map +1 -0
  177. package/package.json +19 -37
  178. package/README.md +0 -7
  179. package/dist/agent-telemetry.d.ts +0 -56
  180. package/dist/agent-telemetry.d.ts.map +0 -1
  181. package/dist/agent-telemetry.js +0 -304
  182. package/dist/agent-telemetry.js.map +0 -1
  183. package/dist/artifacts.d.ts +0 -45
  184. package/dist/artifacts.d.ts.map +0 -1
  185. package/dist/artifacts.js +0 -163
  186. package/dist/artifacts.js.map +0 -1
  187. package/dist/attempt-artifacts.d.ts +0 -35
  188. package/dist/attempt-artifacts.d.ts.map +0 -1
  189. package/dist/attempt-artifacts.js +0 -90
  190. package/dist/attempt-artifacts.js.map +0 -1
  191. package/dist/client.d.ts +0 -82
  192. package/dist/client.d.ts.map +0 -1
  193. package/dist/client.js +0 -200
  194. package/dist/client.js.map +0 -1
  195. package/dist/evaluator.d.ts +0 -36
  196. package/dist/evaluator.d.ts.map +0 -1
  197. package/dist/evaluator.js +0 -101
  198. package/dist/evaluator.js.map +0 -1
  199. package/dist/executors/agent.d.ts +0 -101
  200. package/dist/executors/agent.d.ts.map +0 -1
  201. package/dist/executors/agent.js +0 -508
  202. package/dist/executors/agent.js.map +0 -1
  203. package/dist/executors/mock-program.d.ts +0 -26
  204. package/dist/executors/mock-program.d.ts.map +0 -1
  205. package/dist/executors/mock-program.js +0 -100
  206. package/dist/executors/mock-program.js.map +0 -1
  207. package/dist/executors/output-preview.d.ts +0 -2
  208. package/dist/executors/output-preview.d.ts.map +0 -1
  209. package/dist/executors/output-preview.js +0 -20
  210. package/dist/executors/output-preview.js.map +0 -1
  211. package/dist/executors/program.d.ts +0 -30
  212. package/dist/executors/program.d.ts.map +0 -1
  213. package/dist/executors/program.js +0 -230
  214. package/dist/executors/program.js.map +0 -1
  215. package/dist/executors/types.d.ts +0 -32
  216. package/dist/executors/types.d.ts.map +0 -1
  217. package/dist/fork.d.ts +0 -74
  218. package/dist/fork.d.ts.map +0 -1
  219. package/dist/fork.js +0 -278
  220. package/dist/fork.js.map +0 -1
  221. package/dist/hooks/payload.d.ts +0 -17
  222. package/dist/hooks/payload.d.ts.map +0 -1
  223. package/dist/hooks/payload.js +0 -45
  224. package/dist/hooks/payload.js.map +0 -1
  225. package/dist/interpreter.d.ts +0 -168
  226. package/dist/interpreter.d.ts.map +0 -1
  227. package/dist/interpreter.js +0 -1747
  228. package/dist/interpreter.js.map +0 -1
  229. package/dist/keys.d.ts +0 -77
  230. package/dist/keys.d.ts.map +0 -1
  231. package/dist/keys.js +0 -375
  232. package/dist/keys.js.map +0 -1
  233. package/dist/run-control.d.ts +0 -45
  234. package/dist/run-control.d.ts.map +0 -1
  235. package/dist/run-control.js +0 -255
  236. package/dist/run-control.js.map +0 -1
  237. package/dist/run-id.d.ts +0 -3
  238. package/dist/run-id.d.ts.map +0 -1
  239. package/dist/run-id.js +0 -10
  240. package/dist/run-id.js.map +0 -1
  241. package/dist/state-machine.d.ts +0 -31
  242. package/dist/state-machine.d.ts.map +0 -1
  243. package/dist/state-machine.js +0 -102
  244. package/dist/state-machine.js.map +0 -1
  245. package/dist/store.d.ts +0 -92
  246. package/dist/store.d.ts.map +0 -1
  247. package/dist/store.js +0 -420
  248. package/dist/store.js.map +0 -1
  249. package/dist/supervisor-app.d.ts +0 -27
  250. package/dist/supervisor-app.d.ts.map +0 -1
  251. package/dist/supervisor-app.js +0 -694
  252. package/dist/supervisor-app.js.map +0 -1
  253. package/dist/supervisor-discovery.d.ts +0 -18
  254. package/dist/supervisor-discovery.d.ts.map +0 -1
  255. package/dist/supervisor-discovery.js +0 -180
  256. package/dist/supervisor-discovery.js.map +0 -1
  257. package/dist/supervisor-entry.d.ts +0 -11
  258. package/dist/supervisor-entry.d.ts.map +0 -1
  259. package/dist/supervisor-entry.js +0 -50
  260. package/dist/supervisor-entry.js.map +0 -1
  261. package/dist/supervisor-lock.d.ts +0 -26
  262. package/dist/supervisor-lock.d.ts.map +0 -1
  263. package/dist/supervisor-lock.js +0 -49
  264. package/dist/supervisor-lock.js.map +0 -1
  265. package/dist/supervisor-runner.d.ts +0 -11
  266. package/dist/supervisor-runner.d.ts.map +0 -1
  267. package/dist/supervisor-runner.js +0 -128
  268. package/dist/supervisor-runner.js.map +0 -1
  269. package/dist/types.d.ts +0 -375
  270. package/dist/types.d.ts.map +0 -1
  271. package/dist/types.js +0 -7
  272. package/dist/types.js.map +0 -1
  273. package/dist/validate-input.d.ts +0 -27
  274. package/dist/validate-input.d.ts.map +0 -1
  275. package/dist/validate-input.js +0 -56
  276. package/dist/validate-input.js.map +0 -1
  277. package/dist/validate-signal.d.ts +0 -14
  278. package/dist/validate-signal.d.ts.map +0 -1
  279. package/dist/validate-signal.js +0 -31
  280. package/dist/validate-signal.js.map +0 -1
  281. package/dist/workflow-context.d.ts +0 -4
  282. package/dist/workflow-context.d.ts.map +0 -1
  283. package/dist/workflow-context.js +0 -11
  284. package/dist/workflow-context.js.map +0 -1
  285. package/dist/workflow-outputs.d.ts +0 -9
  286. package/dist/workflow-outputs.d.ts.map +0 -1
  287. package/dist/workflow-outputs.js +0 -60
  288. package/dist/workflow-outputs.js.map +0 -1
  289. /package/dist/{executors → scheduler}/types.js +0 -0
@@ -1,110 +1,94 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { parse as parseYaml } from "yaml";
6
- import { EVENT_NAMES, INJECTOR_NAMES, validateHookConfigShape } from "@acpus/core";
7
- /** Absolute path to the global hooks file (~/.acpus/hooks.yaml). */
8
- export function globalHookConfigPath() {
9
- return join(homedir(), ".acpus", "hooks.yaml");
4
+ import { join } from "node:path";
5
+ import { err, ok } from "neverthrow";
6
+ import { hookEvents, validateHooksFile } from "./config.js";
7
+ export function formatHookLoadError(error) {
8
+ if (error.type === "invalid-config") {
9
+ return `Invalid hooks config at ${error.path}: ${error.errors.map(item => `${item.path}: ${item.message}`).join("; ")}`;
10
+ }
11
+ return `Invalid hooks config at ${error.path}: ${error.message}`;
12
+ }
13
+ export function projectHooksPath(workspaceDir) {
14
+ return join(workspaceDir, ".acpus", "hooks.json");
15
+ }
16
+ export function globalHooksPath(homeDir = homedir()) {
17
+ return join(homeDir, ".acpus", "hooks.json");
10
18
  }
11
- /** Absolute path to the project hooks file (<workspace>/.acpus/hooks.yaml). */
12
- export function projectHookConfigPath(workspace) {
13
- return join(resolve(workspace), ".acpus", "hooks.yaml");
19
+ export async function loadHooksConfig(workspaceDir, options = {}) {
20
+ const scoped = await loadHooksConfigScopes(workspaceDir, options);
21
+ return scoped.map(scopes => scopes.flatMap(scope => scope.hooks));
14
22
  }
15
- /** True when the config declares no injectors and no events. */
16
- export function isEmptyHookConfig(config) {
17
- const injectorCount = INJECTOR_NAMES.reduce((n, k) => n + (config.injectors?.[k]?.length ?? 0), 0);
18
- const eventCount = EVENT_NAMES.reduce((n, k) => n + (config.events?.[k]?.length ?? 0), 0);
19
- return injectorCount === 0 && eventCount === 0;
23
+ export async function loadHooksConfigScopes(workspaceDir, options = {}) {
24
+ const project = await loadHooksConfigScope("project", projectHooksPath(workspaceDir));
25
+ if (project.isErr())
26
+ return err(project.error);
27
+ const global = await loadHooksConfigScope("global", globalHooksPath(options.homeDir));
28
+ if (global.isErr())
29
+ return err(global.error);
30
+ return ok([project.value, global.value]);
20
31
  }
21
- /**
22
- * Loads and merges hook configuration from the global and project layers.
23
- *
24
- * A missing layer is treated as empty (no error). Merge concatenates handler
25
- * arrays per key with global handlers ordered before project handlers.
26
- */
27
- export class HookConfigLoader {
28
- workspace;
29
- constructor(workspace = process.cwd()) {
30
- this.workspace = workspace;
32
+ export async function loadHooksConfigScope(source, path) {
33
+ return loadScope(source, path);
34
+ }
35
+ export function flattenHooksFile(file, source, sourcePath) {
36
+ const hooks = [];
37
+ for (const event of hookEvents) {
38
+ const entries = file[event] ?? [];
39
+ for (const [definitionIndex, entry] of entries.entries()) {
40
+ const definitionHash = hashDefinition(source, sourcePath, event, definitionIndex, entry);
41
+ hooks.push({
42
+ ...entry,
43
+ event,
44
+ source,
45
+ sourcePath,
46
+ definitionIndex,
47
+ definitionHash,
48
+ effectiveId: entry.id ?? `${source}:${event}:${definitionIndex}:${definitionHash.slice(0, 12)}`,
49
+ });
50
+ }
31
51
  }
32
- /** Read a single layer; absent or empty files yield an empty config. */
33
- loadLayer(path) {
34
- if (!existsSync(path))
35
- return { path, config: {}, exists: false };
36
- const raw = readFileSync(path, "utf8").trim();
37
- if (raw.length === 0)
38
- return { path, config: {}, exists: true };
39
- const parsed = parseYaml(raw);
40
- validateHookConfig(parsed, path);
41
- return { path, config: parsed, exists: true };
52
+ return hooks;
53
+ }
54
+ async function loadScope(source, path) {
55
+ let bytes;
56
+ try {
57
+ bytes = await readFile(path, "utf8");
42
58
  }
43
- /** Load and merge both layers, returning the merged config plus source paths. */
44
- load() {
45
- const globalLayer = this.loadLayer(globalHookConfigPath());
46
- const projectLayer = this.loadLayer(projectHookConfigPath(this.workspace));
47
- const merged = mergeHookConfigs(globalLayer.config, projectLayer.config);
48
- return { merged, globalLayer, projectLayer };
59
+ catch (error) {
60
+ if (isNotFound(error))
61
+ return ok({ source, path, hooks: [] });
62
+ return err({ type: "read-failed", source, path, message: error instanceof Error ? error.message : String(error) });
49
63
  }
50
- /**
51
- * Freeze the merged configuration into a snapshot. Returns `undefined` when
52
- * both layers are absent/empty so the runtime can skip all hook machinery.
53
- */
54
- freeze() {
55
- const { merged, globalLayer, projectLayer } = this.load();
56
- if (isEmptyHookConfig(merged))
57
- return undefined;
58
- return {
59
- hash: hashHookConfig(merged),
60
- globalConfigPath: globalLayer.exists ? globalLayer.path : undefined,
61
- projectConfigPath: projectLayer.exists ? projectLayer.path : undefined,
62
- mergedConfig: merged
63
- };
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(bytes);
64
67
  }
65
- }
66
- /** Validate the minimal command-only hook config shape used by the runtime. */
67
- function validateHookConfig(config, path) {
68
- const [issue] = validateHookConfigShape(config);
69
- if (!issue)
70
- return;
71
- throw new Error(`${path}: ${issue.path ? `${issue.path} ` : ""}${issue.message}`);
72
- }
73
- /** Concatenate handler arrays per key; global handlers precede project handlers. */
74
- export function mergeHookConfigs(global, project) {
75
- const injectors = {};
76
- for (const key of INJECTOR_NAMES) {
77
- const combined = [...(global.injectors?.[key] ?? []), ...(project.injectors?.[key] ?? [])];
78
- if (combined.length > 0)
79
- injectors[key] = combined;
80
- }
81
- const events = {};
82
- for (const key of EVENT_NAMES) {
83
- const combined = [...(global.events?.[key] ?? []), ...(project.events?.[key] ?? [])];
84
- if (combined.length > 0)
85
- events[key] = combined;
68
+ catch (error) {
69
+ return err({ type: "invalid-json", source, path, message: error instanceof Error ? error.message : String(error) });
86
70
  }
87
- const result = {};
88
- if (Object.keys(injectors).length > 0)
89
- result.injectors = injectors;
90
- if (Object.keys(events).length > 0)
91
- result.events = events;
92
- return result;
71
+ const validated = validateHooksFile(parsed);
72
+ if (validated.isErr())
73
+ return err({ type: "invalid-config", source, path, errors: validated.error });
74
+ return ok({ source, path, hooks: flattenHooksFile(validated.value, source, path) });
75
+ }
76
+ function hashDefinition(source, sourcePath, event, definitionIndex, config) {
77
+ return createHash("sha256")
78
+ .update(stableJson({ source, sourcePath, event, definitionIndex, config }))
79
+ .digest("hex");
93
80
  }
94
- /** Stable "sha256:..." hash over the canonicalized merged configuration. */
95
- export function hashHookConfig(config) {
96
- return `sha256:${createHash("sha256").update(canonicalJson(config)).digest("hex")}`;
81
+ function stableJson(value) {
82
+ return JSON.stringify(sortValue(value));
97
83
  }
98
- /** Deterministic JSON with object keys sorted recursively. */
99
- function canonicalJson(value) {
84
+ function sortValue(value) {
100
85
  if (Array.isArray(value))
101
- return `[${value.map(canonicalJson).join(",")}]`;
102
- if (value && typeof value === "object") {
103
- const entries = Object.entries(value)
104
- .filter(([, v]) => v !== undefined)
105
- .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
106
- return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
107
- }
108
- return JSON.stringify(value ?? null);
86
+ return value.map(sortValue);
87
+ if (!value || typeof value !== "object")
88
+ return value;
89
+ return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, sortValue(item)]));
90
+ }
91
+ function isNotFound(error) {
92
+ return !!error && typeof error === "object" && "code" in error && error.code === "ENOENT";
109
93
  }
110
94
  //# sourceMappingURL=loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/hooks/loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/hooks/loader.ts"],"names":[],"mappings":"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"}
@@ -1,42 +1,15 @@
1
- import { type EventName, type HookConfig, type InjectorHookHandler, type HookPayload, type InjectorName, type InjectorResult } from "@acpus/core";
2
- /**
3
- * Thrown when an injector handler fails under the `on_failure: "fail"` policy.
4
- * The interpreter maps this to a node failure with `failureKind: "hook_failure"`.
5
- */
6
- export declare class HookFailureError extends Error {
7
- constructor(message: string);
8
- }
9
- /**
10
- * Executes hook command handlers. Injectors run sequentially and may fail the
11
- * node; events never affect outcome. The runner holds the frozen, merged
12
- * configuration for a single Run.
13
- */
14
- export declare class HookRunner {
15
- private readonly config;
16
- constructor(config: HookConfig);
17
- hasInjector(name: InjectorName): boolean;
18
- hasEvent(name: EventName): boolean;
19
- injectorHandlers(name: InjectorName): InjectorHookHandler[];
20
- /**
21
- * Run all handlers for an injector sequentially, merging their results.
22
- * `prependPrompt` is concatenated in order for beforeAgentExec; `env` maps
23
- * merge with later handlers overriding earlier ones for beforeProgramExec.
24
- * A failing handler under `fail` throws
25
- * HookFailureError; under `skip` it logs a warning and injects nothing.
26
- *
27
- * `onHandler` is called once per handler with the resolved per-handler result
28
- * (for journaling).
29
- */
30
- runInjector(name: InjectorName, payload: HookPayload, onHandler?: (handlerIndex: number, result: InjectorResult, durationMs: number) => void): Promise<InjectorResult>;
31
- /**
32
- * Fire all handlers for an event. Event handlers are async by default;
33
- * `sync: true` handlers are awaited. Event handlers never throw and never
34
- * affect Run/Node outcome — failures are logged as warnings.
35
- */
36
- emitEvent(name: EventName, payload: HookPayload): Promise<void>;
37
- private runHandler;
38
- private runCommand;
39
- /** Parse handler stdout as an InjectorResult. Empty output → empty result. */
40
- private parseInjectorStdout;
41
- }
1
+ import type { HookEvent, LoadedHookConfig } from "./config.js";
2
+ import type { HookContext } from "./context.js";
3
+ import type { HookJournalEntry } from "./journal.js";
4
+ export type HookRunner = {
5
+ trigger(event: HookEvent, context: HookContext): void;
6
+ drain(): Promise<void>;
7
+ activeCount(): number;
8
+ };
9
+ export type HookJournalWriter = {
10
+ writeHookJournal(entry: HookJournalEntry): void;
11
+ };
12
+ export declare function createHookRunner(hooks: readonly LoadedHookConfig[], journal: HookJournalWriter, options?: {
13
+ now?: () => Date;
14
+ }): HookRunner;
42
15
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/hooks/runner.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,UAAU,EAEf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,aAAa,CAAC;AAKrB;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAWD;;;;GAIG;AACH,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE,UAAU,EAAI;IAEnD,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAEvC;IAED,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEjC;IAED,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,mBAAmB,EAAE,CAE1D;IAED;;;;;;;;;OASG;IACG,WAAW,CACf,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,WAAW,EACpB,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GACrF,OAAO,CAAC,cAAc,CAAC,CA6CzB;IAED;;;;OAIG;IACG,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAepE;YAGa,UAAU;YAUV,UAAU;IA8BxB,8EAA8E;IAC9E,OAAO,CAAC,mBAAmB;CAU5B"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/hooks/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAa,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,WAAW,IAAI,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACjD,CAAC;AAKF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,gBAAgB,EAAE,EAClC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,GACjC,UAAU,CA0BZ"}
@@ -1,161 +1,193 @@
1
- import { execa } from "execa";
2
- import { parseDurationMs } from "@acpus/core";
3
- const DEFAULT_INJECTOR_TIMEOUT_MS = 5_000;
4
- const DEFAULT_EVENT_TIMEOUT_MS = 30_000;
5
- /**
6
- * Thrown when an injector handler fails under the `on_failure: "fail"` policy.
7
- * The interpreter maps this to a node failure with `failureKind: "hook_failure"`.
8
- */
9
- export class HookFailureError extends Error {
10
- constructor(message) {
11
- super(message);
12
- this.name = "HookFailureError";
13
- }
14
- }
15
- /**
16
- * Executes hook command handlers. Injectors run sequentially and may fail the
17
- * node; events never affect outcome. The runner holds the frozen, merged
18
- * configuration for a single Run.
19
- */
20
- export class HookRunner {
21
- config;
22
- constructor(config) {
23
- this.config = config;
24
- }
25
- hasInjector(name) {
26
- return (this.config.injectors?.[name]?.length ?? 0) > 0;
27
- }
28
- hasEvent(name) {
29
- return (this.config.events?.[name]?.length ?? 0) > 0;
30
- }
31
- injectorHandlers(name) {
32
- return this.config.injectors?.[name] ?? [];
33
- }
34
- /**
35
- * Run all handlers for an injector sequentially, merging their results.
36
- * `prependPrompt` is concatenated in order for beforeAgentExec; `env` maps
37
- * merge with later handlers overriding earlier ones for beforeProgramExec.
38
- * A failing handler under `fail` throws
39
- * HookFailureError; under `skip` it logs a warning and injects nothing.
40
- *
41
- * `onHandler` is called once per handler with the resolved per-handler result
42
- * (for journaling).
43
- */
44
- async runInjector(name, payload, onHandler) {
45
- const handlers = this.injectorHandlers(name);
46
- const contexts = [];
47
- let env;
48
- for (let i = 0; i < handlers.length; i++) {
49
- const handler = handlers[i];
50
- const started = Date.now();
51
- const outcome = await this.runHandler(handler, payload, DEFAULT_INJECTOR_TIMEOUT_MS, true);
52
- const durationMs = Date.now() - started;
53
- if (!outcome.ok) {
54
- const policy = handler.on_failure ?? "fail";
55
- if (policy === "fail") {
56
- throw new HookFailureError(`Injector '${name}' handler #${i} failed: ${outcome.error ?? "unknown error"}`);
57
- }
58
- console.warn(`Injector '${name}' handler #${i} failed (skipped): ${outcome.error ?? "unknown error"}`);
59
- onHandler?.(i, {}, durationMs);
1
+ import { spawn } from "node:child_process";
2
+ import { parseDurationMs } from "../execution/duration.js";
3
+ const defaultTimeout = "30s";
4
+ const outputLimit = 4 * 1024;
5
+ export function createHookRunner(hooks, journal, options = {}) {
6
+ const now = options.now ?? (() => new Date());
7
+ const active = new Set();
8
+ let nextTriggerOrder = 1;
9
+ function trigger(event, context) {
10
+ for (const hook of hooks) {
11
+ if (hook.event !== event || !matches(hook.match, context))
60
12
  continue;
61
- }
62
- const result = outcome.result ?? {};
63
- if (name === "beforeAgentExec") {
64
- const agentResult = result;
65
- if (typeof agentResult.prependPrompt === "string" && agentResult.prependPrompt.length > 0) {
66
- contexts.push(agentResult.prependPrompt);
67
- }
68
- }
69
- if (name === "beforeProgramExec") {
70
- const programResult = result;
71
- if (programResult.env && Object.keys(programResult.env).length > 0) {
72
- env = { ...(env ?? {}), ...programResult.env };
73
- }
74
- }
75
- onHandler?.(i, result, durationMs);
13
+ const startedAt = now();
14
+ const triggerOrder = nextTriggerOrder++;
15
+ const running = spawnHook(hook, context, startedAt, triggerOrder, journal)
16
+ .catch(() => undefined)
17
+ .finally(() => active.delete(running));
18
+ active.add(running);
76
19
  }
77
- if (name === "beforeAgentExec") {
78
- const merged = {};
79
- if (contexts.length > 0)
80
- merged.prependPrompt = contexts.join("\n");
81
- return merged;
82
- }
83
- const merged = {};
84
- if (env)
85
- merged.env = env;
86
- return merged;
87
20
  }
88
- /**
89
- * Fire all handlers for an event. Event handlers are async by default;
90
- * `sync: true` handlers are awaited. Event handlers never throw and never
91
- * affect Run/Node outcome — failures are logged as warnings.
92
- */
93
- async emitEvent(name, payload) {
94
- const handlers = this.config.events?.[name] ?? [];
95
- for (let i = 0; i < handlers.length; i++) {
96
- const handler = handlers[i];
97
- const run = this.runHandler(handler, payload, DEFAULT_EVENT_TIMEOUT_MS, false)
98
- .then((outcome) => {
99
- if (!outcome.ok) {
100
- console.warn(`Event '${name}' handler #${i} failed (ignored): ${outcome.error ?? "unknown error"}`);
101
- }
102
- })
103
- .catch((error) => {
104
- console.warn(`Event '${name}' handler #${i} threw (ignored): ${error instanceof Error ? error.message : String(error)}`);
105
- });
106
- if (handler.sync)
107
- await run;
21
+ return {
22
+ trigger,
23
+ async drain() {
24
+ await Promise.all([...active]);
25
+ },
26
+ activeCount() {
27
+ return active.size;
28
+ },
29
+ };
30
+ }
31
+ function matches(match, context) {
32
+ if (!match)
33
+ return true;
34
+ return matchesField(match.workflow, context.run.workflowName)
35
+ && matchesField(match.nodeId, context.node?.id)
36
+ && matchesField(match.nodeKey, context.node?.key)
37
+ && matchesField(match.kind, context.node?.kind);
38
+ }
39
+ function matchesField(regex, value) {
40
+ return regex === undefined || (value !== undefined && new RegExp(regex).test(value));
41
+ }
42
+ async function spawnHook(hook, context, startedAt, triggerOrder, journal) {
43
+ const timeoutMs = parseDurationMs(hook.timeout ?? defaultTimeout);
44
+ const startedMs = startedAt.getTime();
45
+ const result = await runShellCommand(hook.command, context, timeoutMs);
46
+ writeJournal(journal, journalEntry(hook, context, triggerOrder, {
47
+ ...result,
48
+ durationMs: Math.max(0, Date.now() - startedMs),
49
+ triggeredAt: startedAt.toISOString(),
50
+ }));
51
+ }
52
+ function runShellCommand(command, context, timeoutMs) {
53
+ return new Promise(resolve => {
54
+ const child = spawn(command, { shell: true, cwd: context.run.workspaceDir, detached: process.platform !== "win32" });
55
+ const stdout = new OutputCollector(outputLimit);
56
+ const stderr = new OutputCollector(outputLimit);
57
+ let timedOut = false;
58
+ let settled = false;
59
+ let killTimer;
60
+ const timeout = setTimeout(() => {
61
+ timedOut = true;
62
+ killProcessTree(child.pid, "SIGTERM");
63
+ killTimer = setTimeout(() => killProcessTree(child.pid, "SIGKILL"), 2_000);
64
+ }, timeoutMs);
65
+ child.stdout?.on("data", chunk => stdout.append(Buffer.from(chunk)));
66
+ child.stderr?.on("data", chunk => stderr.append(Buffer.from(chunk)));
67
+ child.stdin?.on("error", () => {
68
+ // Hooks are non-interfering; a command that exits before reading stdin
69
+ // should be recorded from process close/error, not crash the daemon.
70
+ });
71
+ child.on("error", error => finish({
72
+ status: "failed",
73
+ stdout: stdout.toString(),
74
+ stderr: stderr.toString(),
75
+ error: error.message,
76
+ }));
77
+ child.on("close", code => finish({
78
+ status: timedOut ? "timed_out" : code === 0 ? "completed" : "failed",
79
+ ...(timedOut || code === null ? {} : { exitCode: code }),
80
+ stdout: stdout.toString(),
81
+ stderr: stderr.toString(),
82
+ ...(timedOut ? { error: "timeout" } : code === 0 ? {} : { error: `exit_code_${code ?? "null"}` }),
83
+ }));
84
+ child.stdin?.end(JSON.stringify(context));
85
+ function finish(result) {
86
+ if (settled)
87
+ return;
88
+ settled = true;
89
+ clearTimeout(timeout);
90
+ if (killTimer)
91
+ clearTimeout(killTimer);
92
+ resolve(result);
108
93
  }
94
+ });
95
+ }
96
+ function journalEntry(hook, context, triggerOrder, result) {
97
+ return {
98
+ runId: context.run.id,
99
+ eventSequence: context.eventSequence,
100
+ triggerOrder,
101
+ event: context.event,
102
+ source: hook.source,
103
+ sourcePath: hook.sourcePath,
104
+ handlerId: hook.id ?? hook.effectiveId,
105
+ definitionHash: hook.definitionHash,
106
+ ...(context.node?.key === undefined ? {} : { nodeKey: context.node.key }),
107
+ status: result.status,
108
+ ...(result.exitCode === undefined ? {} : { exitCode: result.exitCode }),
109
+ ...(result.stdout === undefined ? {} : { stdout: result.stdout }),
110
+ ...(result.stderr === undefined ? {} : { stderr: result.stderr }),
111
+ ...(result.durationMs === undefined ? {} : { durationMs: result.durationMs }),
112
+ ...(result.error === undefined ? {} : { error: result.error }),
113
+ triggeredAt: result.triggeredAt,
114
+ };
115
+ }
116
+ function writeJournal(journal, entry) {
117
+ try {
118
+ journal.writeHookJournal(entry);
109
119
  }
110
- /** Dispatch a single command handler. `parseResult` only for injectors. */
111
- async runHandler(handler, payload, defaultTimeoutMs, parseResult) {
112
- const timeoutMs = handler.timeout ? parseDurationMs(handler.timeout) || defaultTimeoutMs : defaultTimeoutMs;
113
- return this.runCommand(handler, payload, timeoutMs, parseResult);
120
+ catch {
121
+ // Hooks are non-interfering; journal failures must not affect workflow execution.
114
122
  }
115
- async runCommand(handler, payload, timeoutMs, parseResult) {
116
- try {
117
- const result = await execa(handler.command, {
118
- shell: true,
119
- cwd: handler.cwd ?? process.cwd(),
120
- timeout: timeoutMs > 0 ? timeoutMs : undefined,
121
- reject: false,
122
- input: JSON.stringify(payload),
123
- env: handler.env
124
- ? { ...Object.fromEntries(Object.entries(process.env).filter(([, v]) => v !== undefined)), ...handler.env }
125
- : undefined
126
- });
127
- if (result.timedOut) {
128
- return { ok: false, error: `timed out after ${timeoutMs}ms` };
129
- }
130
- if ((result.exitCode ?? 0) !== 0) {
131
- return { ok: false, error: `exit code ${result.exitCode}: ${stderrTail(result.stderr)}` };
132
- }
133
- if (!parseResult)
134
- return { ok: true };
135
- return this.parseInjectorStdout(result.stdout ?? "");
136
- }
137
- catch (error) {
138
- return { ok: false, error: error instanceof Error ? error.message : String(error) };
123
+ }
124
+ function killProcessTree(pid, signal) {
125
+ if (pid === undefined)
126
+ return;
127
+ try {
128
+ if (process.platform === "win32") {
129
+ spawn("taskkill", ["/pid", String(pid), "/T", signal === "SIGKILL" ? "/F" : ""].filter(Boolean), { stdio: "ignore" }).unref();
130
+ return;
139
131
  }
132
+ process.kill(-pid, signal);
140
133
  }
141
- /** Parse handler stdout as an InjectorResult. Empty output → empty result. */
142
- parseInjectorStdout(raw) {
143
- const trimmed = raw.trim();
144
- if (trimmed.length === 0)
145
- return { ok: true, result: {} };
134
+ catch {
146
135
  try {
147
- const parsed = JSON.parse(trimmed);
148
- return { ok: true, result: parsed ?? {} };
136
+ process.kill(pid, signal);
149
137
  }
150
138
  catch {
151
- return { ok: false, error: "stdout is not valid JSON" };
139
+ // Best effort; hook timeout is non-interfering.
140
+ }
141
+ }
142
+ }
143
+ class OutputCollector {
144
+ limit;
145
+ full = Buffer.alloc(0);
146
+ head = Buffer.alloc(0);
147
+ tail = Buffer.alloc(0);
148
+ total = 0;
149
+ truncated = false;
150
+ constructor(limit) {
151
+ this.limit = limit;
152
+ }
153
+ append(chunk) {
154
+ const nextTotal = this.total + chunk.length;
155
+ if (!this.truncated && nextTotal <= this.limit * 2) {
156
+ this.full = Buffer.concat([this.full, chunk]);
157
+ this.total = nextTotal;
158
+ return;
159
+ }
160
+ if (!this.truncated) {
161
+ this.head = this.full.length >= this.limit
162
+ ? this.full.subarray(0, this.limit)
163
+ : Buffer.concat([this.full, chunk.subarray(0, this.limit - this.full.length)]);
164
+ this.tail = lastBytes(this.full.subarray(Math.max(0, this.full.length - this.limit)), chunk, this.limit);
165
+ this.full = Buffer.alloc(0);
166
+ this.truncated = true;
167
+ this.total = nextTotal;
168
+ return;
152
169
  }
170
+ this.total += chunk.length;
171
+ this.tail = lastBytes(this.tail, chunk, this.limit);
153
172
  }
173
+ toString() {
174
+ return boundedUtf8(this.truncated ? Buffer.concat([this.head, this.tail]) : this.full, this.limit * 2);
175
+ }
176
+ }
177
+ function lastBytes(previousTail, chunk, limit) {
178
+ if (chunk.length >= limit)
179
+ return chunk.subarray(chunk.length - limit);
180
+ const combined = Buffer.concat([previousTail, chunk]);
181
+ return combined.length <= limit ? combined : combined.subarray(combined.length - limit);
154
182
  }
155
- /** Last few lines of stderr for a concise failure summary. */
156
- function stderrTail(stderr) {
157
- if (!stderr)
158
- return "";
159
- return stderr.replace(/\s+$/u, "").split(/\r?\n/u).slice(-5).join("\n");
183
+ function boundedUtf8(buffer, maxBytes) {
184
+ let end = Math.min(buffer.length, maxBytes);
185
+ while (end >= 0) {
186
+ const value = buffer.subarray(0, end).toString("utf8");
187
+ if (Buffer.byteLength(value, "utf8") <= maxBytes)
188
+ return value;
189
+ end -= 1;
190
+ }
191
+ return "";
160
192
  }
161
193
  //# sourceMappingURL=runner.js.map