@aigne/agent-runtime 1.12.0-beta.5

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 (234) hide show
  1. package/LICENSE.md +26 -0
  2. package/README.md +79 -0
  3. package/dist/agent-runners/ai-runner.cjs +133 -0
  4. package/dist/agent-runners/ai-runner.d.cts +33 -0
  5. package/dist/agent-runners/ai-runner.d.cts.map +1 -0
  6. package/dist/agent-runners/ai-runner.d.mts +33 -0
  7. package/dist/agent-runners/ai-runner.d.mts.map +1 -0
  8. package/dist/agent-runners/ai-runner.mjs +134 -0
  9. package/dist/agent-runners/ai-runner.mjs.map +1 -0
  10. package/dist/agent-runners/ash-runner.cjs +272 -0
  11. package/dist/agent-runners/ash-runner.d.cts +30 -0
  12. package/dist/agent-runners/ash-runner.d.cts.map +1 -0
  13. package/dist/agent-runners/ash-runner.d.mts +30 -0
  14. package/dist/agent-runners/ash-runner.d.mts.map +1 -0
  15. package/dist/agent-runners/ash-runner.mjs +273 -0
  16. package/dist/agent-runners/ash-runner.mjs.map +1 -0
  17. package/dist/agent-runners/budget.cjs +71 -0
  18. package/dist/agent-runners/budget.d.cts +40 -0
  19. package/dist/agent-runners/budget.d.cts.map +1 -0
  20. package/dist/agent-runners/budget.d.mts +40 -0
  21. package/dist/agent-runners/budget.d.mts.map +1 -0
  22. package/dist/agent-runners/budget.mjs +67 -0
  23. package/dist/agent-runners/budget.mjs.map +1 -0
  24. package/dist/agent-runners/code-runner.cjs +61 -0
  25. package/dist/agent-runners/code-runner.mjs +61 -0
  26. package/dist/agent-runners/code-runner.mjs.map +1 -0
  27. package/dist/agent-runners/env-whitelist.cjs +36 -0
  28. package/dist/agent-runners/env-whitelist.d.cts +5 -0
  29. package/dist/agent-runners/env-whitelist.d.cts.map +1 -0
  30. package/dist/agent-runners/env-whitelist.d.mts +5 -0
  31. package/dist/agent-runners/env-whitelist.d.mts.map +1 -0
  32. package/dist/agent-runners/env-whitelist.mjs +36 -0
  33. package/dist/agent-runners/env-whitelist.mjs.map +1 -0
  34. package/dist/agent-runners/guide-rails.cjs +47 -0
  35. package/dist/agent-runners/guide-rails.d.cts +16 -0
  36. package/dist/agent-runners/guide-rails.d.cts.map +1 -0
  37. package/dist/agent-runners/guide-rails.d.mts +16 -0
  38. package/dist/agent-runners/guide-rails.d.mts.map +1 -0
  39. package/dist/agent-runners/guide-rails.mjs +48 -0
  40. package/dist/agent-runners/guide-rails.mjs.map +1 -0
  41. package/dist/agent-runners/hook-channel.cjs +55 -0
  42. package/dist/agent-runners/hook-channel.d.cts +38 -0
  43. package/dist/agent-runners/hook-channel.d.cts.map +1 -0
  44. package/dist/agent-runners/hook-channel.d.mts +38 -0
  45. package/dist/agent-runners/hook-channel.d.mts.map +1 -0
  46. package/dist/agent-runners/hook-channel.mjs +55 -0
  47. package/dist/agent-runners/hook-channel.mjs.map +1 -0
  48. package/dist/agent-runners/hooks.cjs +107 -0
  49. package/dist/agent-runners/hooks.d.cts +41 -0
  50. package/dist/agent-runners/hooks.d.cts.map +1 -0
  51. package/dist/agent-runners/hooks.d.mts +41 -0
  52. package/dist/agent-runners/hooks.d.mts.map +1 -0
  53. package/dist/agent-runners/hooks.mjs +107 -0
  54. package/dist/agent-runners/hooks.mjs.map +1 -0
  55. package/dist/agent-runners/memory-afs.cjs +342 -0
  56. package/dist/agent-runners/memory-afs.d.cts +221 -0
  57. package/dist/agent-runners/memory-afs.d.cts.map +1 -0
  58. package/dist/agent-runners/memory-afs.d.mts +221 -0
  59. package/dist/agent-runners/memory-afs.d.mts.map +1 -0
  60. package/dist/agent-runners/memory-afs.mjs +326 -0
  61. package/dist/agent-runners/memory-afs.mjs.map +1 -0
  62. package/dist/agent-runners/memory-archive.cjs +263 -0
  63. package/dist/agent-runners/memory-archive.d.cts +47 -0
  64. package/dist/agent-runners/memory-archive.d.cts.map +1 -0
  65. package/dist/agent-runners/memory-archive.d.mts +47 -0
  66. package/dist/agent-runners/memory-archive.d.mts.map +1 -0
  67. package/dist/agent-runners/memory-archive.mjs +257 -0
  68. package/dist/agent-runners/memory-archive.mjs.map +1 -0
  69. package/dist/agent-runners/orchestrator-runner.cjs +302 -0
  70. package/dist/agent-runners/orchestrator-runner.d.cts +23 -0
  71. package/dist/agent-runners/orchestrator-runner.d.cts.map +1 -0
  72. package/dist/agent-runners/orchestrator-runner.d.mts +23 -0
  73. package/dist/agent-runners/orchestrator-runner.d.mts.map +1 -0
  74. package/dist/agent-runners/orchestrator-runner.mjs +303 -0
  75. package/dist/agent-runners/orchestrator-runner.mjs.map +1 -0
  76. package/dist/agent-runners/orchestrator-state.cjs +68 -0
  77. package/dist/agent-runners/orchestrator-state.d.cts +23 -0
  78. package/dist/agent-runners/orchestrator-state.d.cts.map +1 -0
  79. package/dist/agent-runners/orchestrator-state.d.mts +23 -0
  80. package/dist/agent-runners/orchestrator-state.d.mts.map +1 -0
  81. package/dist/agent-runners/orchestrator-state.mjs +65 -0
  82. package/dist/agent-runners/orchestrator-state.mjs.map +1 -0
  83. package/dist/agent-runners/retry.cjs +30 -0
  84. package/dist/agent-runners/retry.d.cts +13 -0
  85. package/dist/agent-runners/retry.d.cts.map +1 -0
  86. package/dist/agent-runners/retry.d.mts +13 -0
  87. package/dist/agent-runners/retry.d.mts.map +1 -0
  88. package/dist/agent-runners/retry.mjs +30 -0
  89. package/dist/agent-runners/retry.mjs.map +1 -0
  90. package/dist/agent-runners/scratchpad-afs.cjs +276 -0
  91. package/dist/agent-runners/scratchpad-afs.d.cts +103 -0
  92. package/dist/agent-runners/scratchpad-afs.d.cts.map +1 -0
  93. package/dist/agent-runners/scratchpad-afs.d.mts +103 -0
  94. package/dist/agent-runners/scratchpad-afs.d.mts.map +1 -0
  95. package/dist/agent-runners/scratchpad-afs.mjs +271 -0
  96. package/dist/agent-runners/scratchpad-afs.mjs.map +1 -0
  97. package/dist/agent-runners/stream-router.cjs +72 -0
  98. package/dist/agent-runners/stream-router.d.cts +22 -0
  99. package/dist/agent-runners/stream-router.d.cts.map +1 -0
  100. package/dist/agent-runners/stream-router.d.mts +22 -0
  101. package/dist/agent-runners/stream-router.d.mts.map +1 -0
  102. package/dist/agent-runners/stream-router.mjs +72 -0
  103. package/dist/agent-runners/stream-router.mjs.map +1 -0
  104. package/dist/agent-runners/system-prompt.cjs +35 -0
  105. package/dist/agent-runners/system-prompt.d.cts +17 -0
  106. package/dist/agent-runners/system-prompt.d.cts.map +1 -0
  107. package/dist/agent-runners/system-prompt.d.mts +17 -0
  108. package/dist/agent-runners/system-prompt.d.mts.map +1 -0
  109. package/dist/agent-runners/system-prompt.mjs +34 -0
  110. package/dist/agent-runners/system-prompt.mjs.map +1 -0
  111. package/dist/agent-runners/team-modes/aggregates.cjs +36 -0
  112. package/dist/agent-runners/team-modes/aggregates.mjs +35 -0
  113. package/dist/agent-runners/team-modes/aggregates.mjs.map +1 -0
  114. package/dist/agent-runners/team-modes/iterate.cjs +46 -0
  115. package/dist/agent-runners/team-modes/iterate.mjs +47 -0
  116. package/dist/agent-runners/team-modes/iterate.mjs.map +1 -0
  117. package/dist/agent-runners/team-modes/parallel.cjs +59 -0
  118. package/dist/agent-runners/team-modes/parallel.mjs +60 -0
  119. package/dist/agent-runners/team-modes/parallel.mjs.map +1 -0
  120. package/dist/agent-runners/team-modes/reflection.cjs +155 -0
  121. package/dist/agent-runners/team-modes/reflection.mjs +156 -0
  122. package/dist/agent-runners/team-modes/reflection.mjs.map +1 -0
  123. package/dist/agent-runners/team-modes/sequential.cjs +63 -0
  124. package/dist/agent-runners/team-modes/sequential.mjs +64 -0
  125. package/dist/agent-runners/team-modes/sequential.mjs.map +1 -0
  126. package/dist/agent-runners/team-modes/utils.cjs +39 -0
  127. package/dist/agent-runners/team-modes/utils.mjs +36 -0
  128. package/dist/agent-runners/team-modes/utils.mjs.map +1 -0
  129. package/dist/agent-runners/team-runner.cjs +277 -0
  130. package/dist/agent-runners/team-runner.d.cts +34 -0
  131. package/dist/agent-runners/team-runner.d.cts.map +1 -0
  132. package/dist/agent-runners/team-runner.d.mts +34 -0
  133. package/dist/agent-runners/team-runner.d.mts.map +1 -0
  134. package/dist/agent-runners/team-runner.mjs +278 -0
  135. package/dist/agent-runners/team-runner.mjs.map +1 -0
  136. package/dist/agent-runners/template-context.cjs +32 -0
  137. package/dist/agent-runners/template-context.d.cts +43 -0
  138. package/dist/agent-runners/template-context.d.cts.map +1 -0
  139. package/dist/agent-runners/template-context.d.mts +43 -0
  140. package/dist/agent-runners/template-context.d.mts.map +1 -0
  141. package/dist/agent-runners/template-context.mjs +33 -0
  142. package/dist/agent-runners/template-context.mjs.map +1 -0
  143. package/dist/index.cjs +123 -0
  144. package/dist/index.d.cts +29 -0
  145. package/dist/index.d.mts +29 -0
  146. package/dist/index.mjs +29 -0
  147. package/dist/program/agent-action.cjs +496 -0
  148. package/dist/program/agent-action.d.cts +42 -0
  149. package/dist/program/agent-action.d.cts.map +1 -0
  150. package/dist/program/agent-action.d.mts +42 -0
  151. package/dist/program/agent-action.d.mts.map +1 -0
  152. package/dist/program/agent-action.mjs +497 -0
  153. package/dist/program/agent-action.mjs.map +1 -0
  154. package/dist/program/agent-messages-tools.cjs +137 -0
  155. package/dist/program/agent-messages-tools.d.cts +59 -0
  156. package/dist/program/agent-messages-tools.d.cts.map +1 -0
  157. package/dist/program/agent-messages-tools.d.mts +59 -0
  158. package/dist/program/agent-messages-tools.d.mts.map +1 -0
  159. package/dist/program/agent-messages-tools.mjs +130 -0
  160. package/dist/program/agent-messages-tools.mjs.map +1 -0
  161. package/dist/program/agent-runner-registry.cjs +314 -0
  162. package/dist/program/agent-runner-registry.d.cts +52 -0
  163. package/dist/program/agent-runner-registry.d.cts.map +1 -0
  164. package/dist/program/agent-runner-registry.d.mts +52 -0
  165. package/dist/program/agent-runner-registry.d.mts.map +1 -0
  166. package/dist/program/agent-runner-registry.mjs +313 -0
  167. package/dist/program/agent-runner-registry.mjs.map +1 -0
  168. package/dist/program/agent-subscribe.cjs +72 -0
  169. package/dist/program/agent-subscribe.d.cts +18 -0
  170. package/dist/program/agent-subscribe.d.cts.map +1 -0
  171. package/dist/program/agent-subscribe.d.mts +18 -0
  172. package/dist/program/agent-subscribe.d.mts.map +1 -0
  173. package/dist/program/agent-subscribe.mjs +73 -0
  174. package/dist/program/agent-subscribe.mjs.map +1 -0
  175. package/dist/program/agent-tools.cjs +135 -0
  176. package/dist/program/agent-tools.d.cts +35 -0
  177. package/dist/program/agent-tools.d.cts.map +1 -0
  178. package/dist/program/agent-tools.d.mts +35 -0
  179. package/dist/program/agent-tools.d.mts.map +1 -0
  180. package/dist/program/agent-tools.mjs +136 -0
  181. package/dist/program/agent-tools.mjs.map +1 -0
  182. package/dist/program/manifest-nesting.cjs +57 -0
  183. package/dist/program/manifest-nesting.d.cts +13 -0
  184. package/dist/program/manifest-nesting.d.cts.map +1 -0
  185. package/dist/program/manifest-nesting.d.mts +13 -0
  186. package/dist/program/manifest-nesting.d.mts.map +1 -0
  187. package/dist/program/manifest-nesting.mjs +57 -0
  188. package/dist/program/manifest-nesting.mjs.map +1 -0
  189. package/dist/program/result-contract.cjs +55 -0
  190. package/dist/program/result-contract.d.cts +23 -0
  191. package/dist/program/result-contract.d.cts.map +1 -0
  192. package/dist/program/result-contract.d.mts +23 -0
  193. package/dist/program/result-contract.d.mts.map +1 -0
  194. package/dist/program/result-contract.mjs +51 -0
  195. package/dist/program/result-contract.mjs.map +1 -0
  196. package/dist/program/runner-error.cjs +71 -0
  197. package/dist/program/runner-error.d.cts +25 -0
  198. package/dist/program/runner-error.d.cts.map +1 -0
  199. package/dist/program/runner-error.d.mts +25 -0
  200. package/dist/program/runner-error.d.mts.map +1 -0
  201. package/dist/program/runner-error.mjs +72 -0
  202. package/dist/program/runner-error.mjs.map +1 -0
  203. package/dist/program/scoped-afs.cjs +538 -0
  204. package/dist/program/scoped-afs.d.cts +245 -0
  205. package/dist/program/scoped-afs.d.cts.map +1 -0
  206. package/dist/program/scoped-afs.d.mts +245 -0
  207. package/dist/program/scoped-afs.d.mts.map +1 -0
  208. package/dist/program/scoped-afs.mjs +531 -0
  209. package/dist/program/scoped-afs.mjs.map +1 -0
  210. package/dist/program/session-hierarchy.cjs +1 -0
  211. package/dist/program/session-hierarchy.d.cts +2 -0
  212. package/dist/program/session-hierarchy.d.mts +2 -0
  213. package/dist/program/session-hierarchy.mjs +3 -0
  214. package/dist/program/tool-discovery/ash-frontmatter.cjs +83 -0
  215. package/dist/program/tool-discovery/ash-frontmatter.d.cts +21 -0
  216. package/dist/program/tool-discovery/ash-frontmatter.d.cts.map +1 -0
  217. package/dist/program/tool-discovery/ash-frontmatter.d.mts +21 -0
  218. package/dist/program/tool-discovery/ash-frontmatter.d.mts.map +1 -0
  219. package/dist/program/tool-discovery/ash-frontmatter.mjs +84 -0
  220. package/dist/program/tool-discovery/ash-frontmatter.mjs.map +1 -0
  221. package/dist/program/tool-discovery/dot-path-resolver.cjs +60 -0
  222. package/dist/program/tool-discovery/dot-path-resolver.mjs +61 -0
  223. package/dist/program/tool-discovery/dot-path-resolver.mjs.map +1 -0
  224. package/dist/program/tool-discovery/skill-gate.cjs +36 -0
  225. package/dist/program/tool-discovery/skill-gate.d.cts +10 -0
  226. package/dist/program/tool-discovery/skill-gate.d.cts.map +1 -0
  227. package/dist/program/tool-discovery/skill-gate.d.mts +10 -0
  228. package/dist/program/tool-discovery/skill-gate.d.mts.map +1 -0
  229. package/dist/program/tool-discovery/skill-gate.mjs +35 -0
  230. package/dist/program/tool-discovery/skill-gate.mjs.map +1 -0
  231. package/dist/providers/basic/ash/dist/index.cjs +1809 -0
  232. package/dist/providers/basic/ash/dist/index.mjs +1807 -0
  233. package/dist/providers/basic/ash/dist/index.mjs.map +1 -0
  234. package/package.json +67 -0
@@ -0,0 +1,1809 @@
1
+ let ufo = require("ufo");
2
+ let _aigne_afs = require("@aigne/afs");
3
+ let _aigne_afs_provider = require("@aigne/afs/provider");
4
+ let _aigne_ash = require("@aigne/ash");
5
+ let croner = require("croner");
6
+ let zod = require("zod");
7
+
8
+ //#region ../../providers/basic/ash/dist/index.mjs
9
+ function __decorate(decorators, target, key, desc) {
10
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
11
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
12
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
13
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
14
+ }
15
+ /**
16
+ * Read-class pairing: a grant of `list` on a data root implies `query` (the
17
+ * collection-query standard action) — both are readPaths-class observation
18
+ * privileges, and the agent default tooling rule says standard observation
19
+ * comes with the data root, not per-tool opt-in. Idempotent.
20
+ */
21
+ function withQueryPairing(ops) {
22
+ if (ops.includes("list") && !ops.includes("query")) return [...ops, "query"];
23
+ return ops;
24
+ }
25
+ /**
26
+ * Match an actual path against a declaration pattern.
27
+ *
28
+ * Supports:
29
+ * - Exact: "/a/b" matches "/a/b"
30
+ * - Single-level `*`: "/a/*" matches "/a/b" but NOT "/a/b/c"
31
+ * - Multi-level `**`: "/a/**" matches "/a/b/c" up to `maxDepth` levels from `**` position
32
+ *
33
+ * @param maxDepth Maximum levels `**` can consume. Defaults to 0 (no expansion).
34
+ */
35
+ function matchPath(actual, pattern, maxDepth = 0) {
36
+ if (actual === pattern) return true;
37
+ const patternParts = pattern.split("/");
38
+ const actualParts = actual.split("/");
39
+ const dstarIdx = patternParts.indexOf("**");
40
+ if (dstarIdx === -1) return matchSingleLevel(actualParts, patternParts);
41
+ const prefix = patternParts.slice(0, dstarIdx);
42
+ const suffix = patternParts.slice(dstarIdx + 1);
43
+ if (actualParts.length < prefix.length + suffix.length) return false;
44
+ for (let i = 0; i < prefix.length; i++) {
45
+ if (prefix[i] === "*") continue;
46
+ if (prefix[i] !== actualParts[i]) return false;
47
+ }
48
+ const suffixStart = actualParts.length - suffix.length;
49
+ if (suffixStart < prefix.length) return false;
50
+ for (let i = 0; i < suffix.length; i++) {
51
+ if (suffix[i] === "*") continue;
52
+ if (suffix[i] !== actualParts[suffixStart + i]) return false;
53
+ }
54
+ const dstarLevels = actualParts.length - prefix.length - suffix.length;
55
+ return dstarLevels >= 0 && dstarLevels <= maxDepth;
56
+ }
57
+ /** Single-level * matching (no **). */
58
+ function matchSingleLevel(actualParts, patternParts) {
59
+ if (!patternParts.some((p) => p === "*")) return false;
60
+ if (actualParts.length === patternParts.length) {
61
+ for (let i = 0; i < patternParts.length; i++) {
62
+ if (patternParts[i] === "*") continue;
63
+ if (patternParts[i] !== actualParts[i]) return false;
64
+ }
65
+ return true;
66
+ }
67
+ if (actualParts.length < patternParts.length) {
68
+ for (let i = actualParts.length; i < patternParts.length; i++) if (patternParts[i] !== "*") return false;
69
+ for (let i = 0; i < actualParts.length; i++) if (patternParts[i] !== "*" && patternParts[i] !== actualParts[i]) return false;
70
+ return true;
71
+ }
72
+ return false;
73
+ }
74
+ /**
75
+ * Extract the action name from an exec path.
76
+ * E.g., "/modules/github/issues/42/.actions/close-issue" → "close-issue"
77
+ */
78
+ function extractActionName(path) {
79
+ const idx = path.lastIndexOf("/.actions/");
80
+ if (idx === -1) return void 0;
81
+ return path.slice(idx + 10).split("/")[0];
82
+ }
83
+ /**
84
+ * Validate a tool_call's path and operation against the tools declaration.
85
+ */
86
+ function validateToolCall(path, op, tools) {
87
+ if (hasUnsafePathSyntax(path)) return {
88
+ allowed: false,
89
+ reason: `Unsafe path syntax in '${path}'`
90
+ };
91
+ let basePath;
92
+ if (op === "exec") {
93
+ const idx = path.indexOf("/.actions/");
94
+ if (idx !== -1) basePath = path.slice(0, idx);
95
+ }
96
+ let pathMatched = false;
97
+ for (const entry of tools) {
98
+ const md = entry.maxDepth ?? 0;
99
+ if (!matchPath(path, entry.path, md) && !(basePath && matchPath(basePath, entry.path, md))) continue;
100
+ pathMatched = true;
101
+ if (!entry.ops.includes(op)) continue;
102
+ if (op === "exec" && entry.exclude_actions?.length) {
103
+ const actionName = extractActionName(path);
104
+ if (actionName && entry.exclude_actions.includes(actionName)) return {
105
+ allowed: false,
106
+ reason: `Action '${actionName}' is excluded on '${entry.path}'`
107
+ };
108
+ }
109
+ return { allowed: true };
110
+ }
111
+ if (pathMatched) return {
112
+ allowed: false,
113
+ reason: `Operation '${op}' not allowed on '${path}'`
114
+ };
115
+ return {
116
+ allowed: false,
117
+ reason: `Path '${path}' not in tools declaration`
118
+ };
119
+ }
120
+ function hasUnsafePathSyntax(path) {
121
+ if (path.includes("..") || path.includes("{") || path.includes("\\") || path.includes("\0")) return true;
122
+ try {
123
+ const decoded = decodeURIComponent(path);
124
+ return decoded !== path && hasUnsafePathSyntax(decoded);
125
+ } catch {
126
+ return true;
127
+ }
128
+ }
129
+ function isChildPath(childPath, parentPath, parentMaxDepth) {
130
+ if (childPath === parentPath) return true;
131
+ const childHasGlob = childPath.includes("**") || childPath.includes("*");
132
+ const parentHasGlob = parentPath.includes("**") || parentPath.includes("*");
133
+ if (!parentHasGlob && !childHasGlob) return childPath === parentPath;
134
+ if (parentHasGlob && !childHasGlob) return matchPath(childPath, parentPath, parentMaxDepth);
135
+ if (parentHasGlob && childHasGlob) {
136
+ const parentBase = parentPath.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
137
+ const childBase = childPath.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
138
+ return childBase === parentBase || childBase.startsWith(`${parentBase}/`);
139
+ }
140
+ return false;
141
+ }
142
+ /**
143
+ * Intersect two ToolEntry[] arrays. Returns the effective tool list for a
144
+ * child agent: only entries that both parent and child declare, with ops
145
+ * narrowed to the intersection and maxDepth capped by the parent.
146
+ *
147
+ * Violations are silently absent (no errors). This is a pure function.
148
+ */
149
+ function intersectTools(parent, child) {
150
+ if (parent.length === 0 || child.length === 0) return [];
151
+ const result = [];
152
+ for (const c of child) {
153
+ if (hasUnsafePathSyntax(c.path)) continue;
154
+ let bestParent = null;
155
+ for (const p of parent) {
156
+ if (hasUnsafePathSyntax(p.path)) continue;
157
+ if (!isChildPath(c.path, p.path, p.maxDepth ?? 0)) continue;
158
+ bestParent = p;
159
+ break;
160
+ }
161
+ if (!bestParent) continue;
162
+ const ops = c.ops.filter((op) => bestParent.ops.includes(op));
163
+ if (ops.length === 0) continue;
164
+ const entry = {
165
+ path: c.path,
166
+ ops
167
+ };
168
+ if (c.path.includes("**")) {
169
+ const parentDepth = bestParent.maxDepth ?? 0;
170
+ const childDepth = c.maxDepth ?? 0;
171
+ entry.maxDepth = Math.min(parentDepth, childDepth);
172
+ } else if (c.maxDepth != null) entry.maxDepth = bestParent.maxDepth != null ? Math.min(bestParent.maxDepth, c.maxDepth) : c.maxDepth;
173
+ const excludes = /* @__PURE__ */ new Set();
174
+ if (bestParent.exclude_actions) for (const a of bestParent.exclude_actions) excludes.add(a);
175
+ if (c.exclude_actions) for (const a of c.exclude_actions) excludes.add(a);
176
+ if (excludes.size > 0) entry.exclude_actions = [...excludes];
177
+ result.push(entry);
178
+ }
179
+ return result;
180
+ }
181
+ /**
182
+ * Validate an ash action path against the manifest's tools whitelist.
183
+ * Used by the ash runner to enforce that compiled ash scripts only call
184
+ * paths permitted by the agent manifest.
185
+ *
186
+ * Returns { allowed: true } or { allowed: false, code, path, reason }.
187
+ */
188
+ function validateActionPath(actionPath, tools) {
189
+ if (hasUnsafePathSyntax(actionPath)) return {
190
+ allowed: false,
191
+ code: "PATH_TRAVERSAL",
192
+ path: actionPath,
193
+ reason: `Unsafe path syntax detected in '${actionPath}'`
194
+ };
195
+ if (tools.length === 0) return {
196
+ allowed: false,
197
+ code: "TOOL_WHITELIST_VIOLATION",
198
+ path: actionPath,
199
+ reason: `No tools declared — action '${actionPath}' is not permitted`
200
+ };
201
+ const result = validateToolCall(actionPath, "exec", tools);
202
+ if (!result.allowed) return {
203
+ allowed: false,
204
+ code: "TOOL_WHITELIST_VIOLATION",
205
+ path: actionPath,
206
+ reason: result.reason
207
+ };
208
+ return { allowed: true };
209
+ }
210
+ const log = (0, _aigne_afs.makeNsLog)("provider:ash");
211
+ /** Lazily resolved docs directory path */
212
+ let _docsDir;
213
+ function getDocsDir() {
214
+ if (!_docsDir) {
215
+ const platform = (0, _aigne_afs.getPlatform)();
216
+ const moduleDir = platform.path.dirname(new URL(require("url").pathToFileURL(__filename).href).pathname);
217
+ _docsDir = platform.path.join(moduleDir, "..", "docs");
218
+ }
219
+ return _docsDir;
220
+ }
221
+ async function readDoc(relativePath) {
222
+ const platform = (0, _aigne_afs.getPlatform)();
223
+ return platform.fs.readTextFile(platform.path.join(getDocsDir(), relativePath));
224
+ }
225
+ /**
226
+ * Dedup deprecation warning for the legacy `/ash/.actions/agent-run`
227
+ * alias — log once per process, not on every call.
228
+ */
229
+ let ashAgentRunDeprecationLogged = false;
230
+ function sanitizeErrorPath(msg) {
231
+ return msg.replace(/'\/[^']*'/g, "'[path]'").replace(/"\/[^"]*"/g, "\"[path]\"");
232
+ }
233
+ function normalizeAbsoluteAfsPath(path) {
234
+ if (!path.startsWith("/")) return path;
235
+ const segments = [];
236
+ for (const segment of path.split("/")) {
237
+ if (!segment || segment === ".") continue;
238
+ if (segment === "..") segments.pop();
239
+ else segments.push(segment);
240
+ }
241
+ return `/${segments.join("/")}`;
242
+ }
243
+ async function writeFileAtomic(path, data) {
244
+ const platform = (0, _aigne_afs.getPlatform)();
245
+ const fs = platform.fs;
246
+ if (!fs.rename) {
247
+ await fs.writeFile(path, data);
248
+ return;
249
+ }
250
+ const dir = platform.path.dirname(path);
251
+ const base = platform.path.basename(path);
252
+ const suffix = platform.crypto?.randomUUID() ?? `${Date.now()}-${Math.random()}`;
253
+ const tmpPath = platform.path.join(dir, `.${base}.${suffix}.tmp`);
254
+ await fs.writeFile(tmpPath, data);
255
+ try {
256
+ await fs.rename(tmpPath, path);
257
+ } catch (error) {
258
+ await fs.rm(tmpPath).catch(() => {});
259
+ throw error;
260
+ }
261
+ }
262
+ /**
263
+ * Normalize an entry's content into a flat record for ASH pipeline access.
264
+ *
265
+ * Object content is spread so scripts can reference fields directly
266
+ * (`${state}`, `${attributes.temperature}`, etc.). String / null / array
267
+ * content is returned as-is under `content` — providers that need structured
268
+ * field access are responsible for returning objects from their read path
269
+ * (e.g. JSON, markdown, registry providers all do this).
270
+ */
271
+ function normalizeContent(content) {
272
+ if (content == null) return {};
273
+ if (typeof content === "object" && !Array.isArray(content)) return {
274
+ content,
275
+ ...content
276
+ };
277
+ return { content };
278
+ }
279
+ function normalizeAfsEntry(entry, fallbackPath) {
280
+ const path = entry.path ?? fallbackPath;
281
+ return {
282
+ path,
283
+ name: path.split("/").filter(Boolean).pop(),
284
+ ...entry.meta,
285
+ ...normalizeContent(entry.content)
286
+ };
287
+ }
288
+ function querySearchNeedle(query) {
289
+ if (!query || query.op !== "contains") return void 0;
290
+ if (query.field !== "content" && query.field !== "_content" && query.field !== "summary") return void 0;
291
+ if (Array.isArray(query.value)) return void 0;
292
+ const needle = String(query.value);
293
+ return needle === "" ? void 0 : needle;
294
+ }
295
+ async function readAfsRecords(afs, localWorld, path, query) {
296
+ const local = await localWorld.read(path, query);
297
+ if (local.length > 0) return local;
298
+ const searchNeedle = querySearchNeedle(query);
299
+ if (searchNeedle && afs.search) try {
300
+ const searchResult = await afs.search(path, searchNeedle, { limit: 1e3 });
301
+ if (searchResult.data.length > 0) return searchResult.data.map((entry) => normalizeAfsEntry(entry, path));
302
+ } catch {}
303
+ if (afs.list) try {
304
+ const listResult = await afs.list(path, query ? { query } : {});
305
+ if (listResult.data.length > 0) {
306
+ const needsEnrich = listResult.data.some((entry) => entry.content == null);
307
+ let entries = listResult.data;
308
+ if (needsEnrich && afs.read) {
309
+ const afsRead = afs.read.bind(afs);
310
+ entries = await Promise.all(listResult.data.map(async (entry) => {
311
+ if (entry.content != null) return entry;
312
+ try {
313
+ const result = await afsRead(entry.path, {});
314
+ return result.data ? {
315
+ ...entry,
316
+ content: result.data.content
317
+ } : entry;
318
+ } catch {
319
+ return entry;
320
+ }
321
+ }));
322
+ }
323
+ return entries.map((entry) => normalizeAfsEntry(entry, path));
324
+ }
325
+ } catch {}
326
+ if (afs.read) try {
327
+ const readResult = await afs.read(path, {});
328
+ if (readResult.data) return [normalizeAfsEntry(readResult.data, path)];
329
+ } catch {}
330
+ return [];
331
+ }
332
+ /**
333
+ * AFS ASH Provider
334
+ *
335
+ * Bridges ASH pipeline DSL to AFS. ASH scripts are stored in-memory and
336
+ * executed against AFS via a world bridge that translates ASH's
337
+ * read/write/publish to AFS read/write operations.
338
+ *
339
+ * Structure:
340
+ * - `/` - root directory
341
+ * - `/scripts` - stored .ash scripts
342
+ * - `/scripts/{name}.ash` - individual script files (executable)
343
+ * - `/.actions/run` - execute inline ASH code
344
+ * - `/.actions/validate` - validate ASH source (compile + type-check)
345
+ */
346
+ var AFSAsh = class AFSAsh extends _aigne_afs_provider.AFSBaseProvider {
347
+ static manifest() {
348
+ return {
349
+ name: "ash",
350
+ description: "ASH pipeline DSL for deterministic data pipelines.\n- Store, validate, and execute ASH scripts\n- Commands: find, where, map, save, publish, tee, fanout, output, input, count, group-by, action, route, lookup\n- Read /.meta/.ash-reference for full syntax",
351
+ uriTemplate: "ash://",
352
+ category: "compute",
353
+ agentMdPath: new URL("../docs/agent.md", require("url").pathToFileURL(__filename).href).pathname,
354
+ schema: zod.z.object({
355
+ name: zod.z.string().optional(),
356
+ description: zod.z.string().optional(),
357
+ allowRootActions: zod.z.array(zod.z.enum([
358
+ "agent-run",
359
+ "run",
360
+ "mount",
361
+ "unmount",
362
+ "read",
363
+ "list",
364
+ "write",
365
+ "delete"
366
+ ])).optional()
367
+ }),
368
+ tags: [
369
+ "ash",
370
+ "pipeline",
371
+ "etl",
372
+ "dsl"
373
+ ],
374
+ capabilityTags: [
375
+ "read-write",
376
+ "search",
377
+ "auth:none",
378
+ "local"
379
+ ],
380
+ security: {
381
+ riskLevel: "system",
382
+ resourceAccess: ["process-spawn"],
383
+ dataSensitivity: ["code"],
384
+ notes: ["Executes ASH pipeline scripts that can read/write data across mounted providers"]
385
+ },
386
+ capabilities: { crossProvider: { afsAccess: true } }
387
+ };
388
+ }
389
+ static treeSchema() {
390
+ return {
391
+ operations: [
392
+ "list",
393
+ "read",
394
+ "write",
395
+ "delete",
396
+ "exec",
397
+ "search",
398
+ "stat",
399
+ "explain"
400
+ ],
401
+ tree: {
402
+ "/": {
403
+ kind: "ash:root",
404
+ operations: [
405
+ "list",
406
+ "read",
407
+ "exec"
408
+ ],
409
+ actions: ["run", "validate"]
410
+ },
411
+ "/scripts": {
412
+ kind: "ash:directory",
413
+ operations: ["list", "read"]
414
+ },
415
+ "/scripts/{name}.ash": {
416
+ kind: "ash:script",
417
+ operations: [
418
+ "read",
419
+ "write",
420
+ "delete",
421
+ "exec"
422
+ ],
423
+ actions: ["exec"]
424
+ }
425
+ },
426
+ auth: { type: "none" },
427
+ bestFor: [
428
+ "data pipelines",
429
+ "ETL scripts",
430
+ "deterministic DSL"
431
+ ],
432
+ notFor: ["general-purpose code"]
433
+ };
434
+ }
435
+ static async load({ config } = {}) {
436
+ return new AFSAsh(config);
437
+ }
438
+ name;
439
+ description;
440
+ accessMode = "readwrite";
441
+ scripts = /* @__PURE__ */ new Map();
442
+ writeQueues = /* @__PURE__ */ new Map();
443
+ afsRoot;
444
+ scriptsDir;
445
+ dataDir;
446
+ allowRootActions;
447
+ triggerSubs = /* @__PURE__ */ new Map();
448
+ cronJobs = /* @__PURE__ */ new Map();
449
+ /** Cron registry for alarm-driven execution (CF Worker DOs where setTimeout is unreliable). */
450
+ cronRegistry = /* @__PURE__ */ new Map();
451
+ externalScripts = /* @__PURE__ */ new Map();
452
+ externalScriptSubs = [];
453
+ /** Pending trigger execution promises — for testing/awaiting async triggers. */
454
+ pendingTriggers = /* @__PURE__ */ new Set();
455
+ /** Per-script AFS override for program-scoped triggers (scriptName → program AFS). */
456
+ triggerAfsOverrides = /* @__PURE__ */ new Map();
457
+ /** Script trigger keys owned by each program trigger registration. */
458
+ programTriggerKeys = /* @__PURE__ */ new Map();
459
+ /** Namespace prefixes registered via registerProgramTriggers (e.g. "/blocklets/evan"). */
460
+ programNamespaces = /* @__PURE__ */ new Set();
461
+ /** Runtime AFSes promoted to program-AFS. When events arrive on these,
462
+ * the onMount-installed `initTriggers` subscription skips to avoid
463
+ * duplicate registration — `registerProgramTriggers` owns them. */
464
+ programAfses = /* @__PURE__ */ new WeakSet();
465
+ dirsReady = false;
466
+ constructor(options = {}) {
467
+ super();
468
+ this.name = options.name ?? "ash";
469
+ this.description = options.description ?? "ASH pipeline DSL for deterministic data pipelines";
470
+ this.scriptsDir = options.scriptsDir;
471
+ this.dataDir = options.dataDir;
472
+ const rawAllow = options.allowRootActions;
473
+ const allowList = Array.isArray(rawAllow) ? rawAllow : typeof rawAllow === "string" ? [rawAllow] : ["agent-run"];
474
+ const validActions = new Set([
475
+ "agent-run",
476
+ "run",
477
+ "mount",
478
+ "unmount",
479
+ "read",
480
+ "list",
481
+ "write",
482
+ "delete"
483
+ ]);
484
+ const normalized = allowList.filter((a) => validActions.has(a));
485
+ this.allowRootActions = new Set(normalized.length > 0 ? normalized : ["agent-run"]);
486
+ }
487
+ async ready() {
488
+ if (this.dirsReady) return;
489
+ const fs = (0, _aigne_afs.getPlatform)().fs;
490
+ if (this.scriptsDir) await fs.mkdir(this.scriptsDir, { recursive: true });
491
+ if (this.dataDir) await fs.mkdir(this.dataDir, { recursive: true });
492
+ this.dirsReady = true;
493
+ }
494
+ async ensureDirs() {
495
+ if (!this.dirsReady) await this.ready();
496
+ }
497
+ async enqueueWrite(key, fn) {
498
+ const previous = this.writeQueues.get(key) ?? Promise.resolve();
499
+ let release;
500
+ const current = new Promise((resolve) => {
501
+ release = resolve;
502
+ });
503
+ this.writeQueues.set(key, previous.then(() => current, () => current));
504
+ await previous.catch(() => {});
505
+ try {
506
+ return await fn();
507
+ } finally {
508
+ release();
509
+ if (this.writeQueues.get(key) === current) this.writeQueues.delete(key);
510
+ }
511
+ }
512
+ async getScript(name) {
513
+ if (this.scriptsDir) {
514
+ const platform = (0, _aigne_afs.getPlatform)();
515
+ const filePath = platform.path.join(this.scriptsDir, `${name}.ash`);
516
+ try {
517
+ return {
518
+ source: await platform.fs.readTextFile(filePath),
519
+ createdAt: /* @__PURE__ */ new Date(),
520
+ updatedAt: /* @__PURE__ */ new Date()
521
+ };
522
+ } catch {
523
+ return;
524
+ }
525
+ }
526
+ return this.scripts.get(name);
527
+ }
528
+ async setScript(name, script) {
529
+ if (this.scriptsDir) {
530
+ await this.ensureDirs();
531
+ await writeFileAtomic((0, _aigne_afs.getPlatform)().path.join(this.scriptsDir, `${name}.ash`), script.source);
532
+ }
533
+ this.scripts.set(name, script);
534
+ }
535
+ async hasScript(name) {
536
+ if (this.scriptsDir) try {
537
+ const platform = (0, _aigne_afs.getPlatform)();
538
+ await platform.fs.stat(platform.path.join(this.scriptsDir, `${name}.ash`));
539
+ return true;
540
+ } catch {
541
+ return false;
542
+ }
543
+ return this.scripts.has(name);
544
+ }
545
+ async removeScript(name) {
546
+ if (this.scriptsDir) try {
547
+ const platform = (0, _aigne_afs.getPlatform)();
548
+ await platform.fs.rm(platform.path.join(this.scriptsDir, `${name}.ash`));
549
+ } catch {}
550
+ this.scripts.delete(name);
551
+ }
552
+ async getAllScriptNames() {
553
+ if (this.scriptsDir) try {
554
+ return (await (0, _aigne_afs.getPlatform)().fs.readdir(this.scriptsDir)).filter((f) => f.endsWith(".ash")).map((f) => f.replace(/\.ash$/, ""));
555
+ } catch {
556
+ return [];
557
+ }
558
+ return Array.from(this.scripts.keys());
559
+ }
560
+ async getScriptCount() {
561
+ return (await this.getAllScriptNames()).length;
562
+ }
563
+ /** Sanitize an ASH path to a flat filename: /data/clean → data%2Fclean.json */
564
+ dataFileName(path) {
565
+ return `${path.replace(/^\/+/, "").replace(/\//g, "%2F")}.json`;
566
+ }
567
+ /** Persist save output to dataDir */
568
+ async persistData(path, data) {
569
+ if (!this.dataDir) return;
570
+ await this.enqueueWrite(`data:${path}`, async () => {
571
+ await this.ensureDirs();
572
+ await writeFileAtomic((0, _aigne_afs.getPlatform)().path.join(this.dataDir, this.dataFileName(path)), JSON.stringify(data));
573
+ });
574
+ }
575
+ async updateScript(name, update) {
576
+ return this.enqueueWrite(`script:${name}`, async () => {
577
+ const next = await update(await this.getScript(name));
578
+ if (next) await this.setScript(name, next);
579
+ else await this.removeScript(name);
580
+ return next;
581
+ });
582
+ }
583
+ /** Load all persisted data files into a dataStore */
584
+ async loadPersistedData() {
585
+ const store = {};
586
+ if (!this.dataDir) return store;
587
+ const platform = (0, _aigne_afs.getPlatform)();
588
+ try {
589
+ const files = await platform.fs.readdir(this.dataDir);
590
+ for (const file of files) {
591
+ if (!file.endsWith(".json")) continue;
592
+ const key = `/${file.replace(/\.json$/, "").replace(/%2F/gi, "/")}`;
593
+ try {
594
+ store[key] = JSON.parse(await platform.fs.readTextFile(platform.path.join(this.dataDir, file)));
595
+ } catch {}
596
+ }
597
+ } catch {}
598
+ return store;
599
+ }
600
+ onMount(afs) {
601
+ this.destroy();
602
+ this.afsRoot = afs;
603
+ this.initTriggers().catch(() => {});
604
+ if (afs.subscribe) this.externalScriptSubs = [afs.subscribe({ type: _aigne_afs.SCRIPT_EVENT_TYPES.REGISTERED }, (evt) => {
605
+ if (evt.data?.runtime !== "ash") return;
606
+ if (this.isFromProgramNamespace(evt.path)) return;
607
+ if (this.programAfses.has(afs)) return;
608
+ this.trackPendingTrigger(this.registerExternalScript(evt.path).catch(() => {}));
609
+ }), afs.subscribe({ type: _aigne_afs.SCRIPT_EVENT_TYPES.UNREGISTERED }, (evt) => {
610
+ if (evt.data?.runtime !== "ash") return;
611
+ if (this.isFromProgramNamespace(evt.path)) return;
612
+ if (this.programAfses.has(afs)) return;
613
+ this.unregisterExternalScript(evt.path);
614
+ })];
615
+ }
616
+ /** Check if a global event path belongs to a registered program namespace. */
617
+ isFromProgramNamespace(path) {
618
+ if (path.startsWith("/blocklets/")) return true;
619
+ for (const ns of this.programNamespaces) if (path.startsWith(`${ns}/`)) return true;
620
+ return false;
621
+ }
622
+ async registerExternalScript(path) {
623
+ const afs = this.afsRoot;
624
+ if (!afs?.read) return;
625
+ for (const ns of this.programNamespaces) if (this.triggerAfsOverrides.has(`${ns}:${path}`)) return;
626
+ for (const key of this.triggerSubs.keys()) if (key.endsWith(`:${path}`)) return;
627
+ try {
628
+ const file = await afs.read(path, {});
629
+ const source = String(file.data?.content ?? "");
630
+ if (!source.trim()) return;
631
+ this.externalScripts.set(path, source);
632
+ this.registerTriggers(path, source);
633
+ } catch {}
634
+ }
635
+ unregisterExternalScript(path) {
636
+ this.externalScripts.delete(path);
637
+ this.unregisterTriggers(path);
638
+ }
639
+ /** Wait for all pending trigger executions to complete. Useful in tests. */
640
+ async flushTriggers() {
641
+ while (this.pendingTriggers.size > 0) await Promise.allSettled([...this.pendingTriggers]);
642
+ }
643
+ trackPendingTrigger(promise) {
644
+ this.pendingTriggers.add(promise);
645
+ promise.finally(() => {
646
+ this.pendingTriggers.delete(promise);
647
+ });
648
+ }
649
+ /** Stop all cron jobs and unsubscribe all event triggers. Call on provider teardown. */
650
+ destroy() {
651
+ const allNames = new Set([...this.triggerSubs.keys(), ...this.cronJobs.keys()]);
652
+ for (const name of allNames) this.unregisterTriggers(name);
653
+ for (const unsub of this.externalScriptSubs) unsub();
654
+ this.externalScriptSubs = [];
655
+ this.externalScripts.clear();
656
+ this.triggerAfsOverrides.clear();
657
+ this.programTriggerKeys.clear();
658
+ this.programNamespaces.clear();
659
+ }
660
+ async initTriggers() {
661
+ await this.ensureDirs();
662
+ for (const name of await this.getAllScriptNames()) {
663
+ const script = await this.getScript(name);
664
+ if (script) this.registerTriggers(name, script.source);
665
+ }
666
+ }
667
+ /**
668
+ * Register triggers for all .ash scripts in a program's /program/scripts/ directory.
669
+ * Uses the program's runtime AFS for both event subscription and action execution,
670
+ * ensuring triggered jobs operate in the correct program namespace (e.g., accessing
671
+ * the program's own telegram/slack providers instead of the host's).
672
+ */
673
+ /**
674
+ * Register triggers for all .ash scripts in a program's script directories.
675
+ * @param runtimeAFS - The program's runtime AFS instance
676
+ * @param namespace - Optional namespace prefix to avoid key collisions when
677
+ * multiple blocklets share the same ASH provider instance.
678
+ * e.g. "/blocklets/telegram-assistant" → keys become
679
+ * "/blocklets/telegram-assistant:/program/scripts/foo.ash"
680
+ */
681
+ async registerProgramTriggers(runtimeAFS, namespace) {
682
+ if (!runtimeAFS.list || !runtimeAFS.read) return;
683
+ if (namespace) this.programNamespaces.add(namespace);
684
+ this.programAfses.add(runtimeAFS);
685
+ const keyPrefix = namespace ? `${namespace}:` : "";
686
+ const programKey = namespace ?? "__default__";
687
+ const subKey = `${keyPrefix}__program__`;
688
+ this.unregisterProgramTriggers(programKey);
689
+ this.unregisterProgramSubscription(subKey);
690
+ let registeredCount = 0;
691
+ for (const dir of [
692
+ "/packages/scripts",
693
+ "/instance/scripts",
694
+ "/instance"
695
+ ]) try {
696
+ const scriptsList = await runtimeAFS.list(dir, {});
697
+ for (const entry of scriptsList.data) {
698
+ if (!entry.path.endsWith(".ash")) continue;
699
+ const scriptPath = entry.path.startsWith("/") ? entry.path : `${dir}/${entry.path}`;
700
+ try {
701
+ const readResult = await runtimeAFS.read(scriptPath, {});
702
+ const source = String(readResult.data?.content ?? "");
703
+ if (!source.trim()) continue;
704
+ const result = (0, _aigne_ash.compileSource)(source);
705
+ if (!result.program) continue;
706
+ if (!result.program.units.some((u) => u.kind === "job" && u.trigger)) continue;
707
+ if (this.triggerSubs.has(scriptPath)) {
708
+ this.unregisterTriggers(scriptPath);
709
+ this.externalScripts.delete(scriptPath);
710
+ }
711
+ this.registerProgramScriptTrigger(programKey, keyPrefix + scriptPath, source, runtimeAFS);
712
+ registeredCount++;
713
+ } catch {}
714
+ }
715
+ } catch {}
716
+ log.debug(`[ASH] registerProgramTriggers ns=${namespace ?? "(none)"} registered=${registeredCount}`);
717
+ if (runtimeAFS.subscribe) {
718
+ const unsub = runtimeAFS.subscribe({ type: _aigne_afs.SCRIPT_EVENT_TYPES.REGISTERED }, (evt) => {
719
+ if (evt.data?.runtime !== "ash") return;
720
+ const p = (async () => {
721
+ try {
722
+ const readResult = await runtimeAFS.read(evt.path, {});
723
+ const source = String(readResult.data?.content ?? "");
724
+ if (!source.trim()) return;
725
+ this.registerProgramScriptTrigger(programKey, keyPrefix + evt.path, source, runtimeAFS);
726
+ } catch {}
727
+ })();
728
+ this.trackPendingTrigger(p);
729
+ });
730
+ this.triggerSubs.set(subKey, [unsub]);
731
+ }
732
+ }
733
+ registerProgramScriptTrigger(programKey, scriptName, source, runtimeAFS) {
734
+ this.registerTriggers(scriptName, source, runtimeAFS);
735
+ const keys = this.programTriggerKeys.get(programKey) ?? /* @__PURE__ */ new Set();
736
+ keys.add(scriptName);
737
+ this.programTriggerKeys.set(programKey, keys);
738
+ }
739
+ unregisterProgramTriggers(programKey) {
740
+ const keys = this.programTriggerKeys.get(programKey);
741
+ if (!keys) return;
742
+ for (const scriptName of keys) this.unregisterTriggers(scriptName);
743
+ this.programTriggerKeys.delete(programKey);
744
+ }
745
+ unregisterProgramSubscription(subKey) {
746
+ const existing = this.triggerSubs.get(subKey);
747
+ if (!existing) return;
748
+ for (const unsub of existing) unsub();
749
+ this.triggerSubs.delete(subKey);
750
+ }
751
+ /**
752
+ * Register EventBus subscriptions and cron schedules for all triggers in a script.
753
+ * Replaces any previous subscriptions/schedules for the same script name.
754
+ *
755
+ * When `afsOverride` is provided (program-scoped triggers), events are subscribed
756
+ * on the program AFS and the override is stored for use by `executeTriggerJob`.
757
+ * When re-registered without an explicit override (e.g., via script:registered),
758
+ * any existing program-scoped override is preserved.
759
+ */
760
+ registerTriggers(scriptName, source, afsOverride) {
761
+ const effectiveOverride = afsOverride ?? this.triggerAfsOverrides.get(scriptName);
762
+ this.unregisterTriggers(scriptName);
763
+ if (effectiveOverride) this.triggerAfsOverrides.set(scriptName, effectiveOverride);
764
+ const result = (0, _aigne_ash.compileSource)(source);
765
+ if (!result.program) return;
766
+ const afs = effectiveOverride ?? this.afsRoot;
767
+ const unsubs = [];
768
+ const crons = [];
769
+ for (const unit of result.program.units) {
770
+ if (unit.kind !== "job" || !unit.trigger) continue;
771
+ const jobName = unit.name;
772
+ const trigger = unit.trigger;
773
+ if (trigger.kind === "event") {
774
+ if (!afs?.subscribe) continue;
775
+ const filter = {
776
+ path: trigger.path,
777
+ type: trigger.event ? `*:${trigger.event}` : void 0
778
+ };
779
+ const unsub = afs.subscribe(filter, (evt) => {
780
+ const p = this.executeTriggerJob(scriptName, jobName, evt);
781
+ this.trackPendingTrigger(p);
782
+ });
783
+ unsubs.push(unsub);
784
+ } else if (trigger.kind === "cron") try {
785
+ new croner.CronPattern(trigger.expression);
786
+ const cronKey = `${scriptName}:${jobName}`;
787
+ this.cronRegistry.set(cronKey, {
788
+ expression: trigger.expression,
789
+ scriptName,
790
+ jobName,
791
+ lastFiredMinute: 0
792
+ });
793
+ const cron = new croner.Cron(trigger.expression, { protect: true }, () => {
794
+ const reg = this.cronRegistry.get(cronKey);
795
+ if (reg) reg.lastFiredMinute = Math.floor(Date.now() / 6e4);
796
+ const syntheticEvent = {
797
+ type: "cron",
798
+ path: `/cron/${scriptName}/${jobName}`,
799
+ source: "ash-cron",
800
+ timestamp: Date.now()
801
+ };
802
+ const p = this.executeTriggerJob(scriptName, jobName, syntheticEvent);
803
+ this.trackPendingTrigger(p);
804
+ });
805
+ crons.push(cron);
806
+ } catch (err) {
807
+ log.error(`[ASH cron] Invalid cron expression "${trigger.expression}" in ${scriptName}/${jobName}:`, err);
808
+ }
809
+ }
810
+ if (unsubs.length > 0) this.triggerSubs.set(scriptName, unsubs);
811
+ if (crons.length > 0) this.cronJobs.set(scriptName, crons);
812
+ }
813
+ /** Unsubscribe all event triggers and stop all cron jobs for a given script. */
814
+ unregisterTriggers(scriptName) {
815
+ const unsubs = this.triggerSubs.get(scriptName);
816
+ if (unsubs) {
817
+ for (const unsub of unsubs) unsub();
818
+ this.triggerSubs.delete(scriptName);
819
+ }
820
+ const crons = this.cronJobs.get(scriptName);
821
+ if (crons) {
822
+ for (const cron of crons) cron.stop();
823
+ this.cronJobs.delete(scriptName);
824
+ }
825
+ for (const key of this.cronRegistry.keys()) if (key.startsWith(`${scriptName}:`)) this.cronRegistry.delete(key);
826
+ this.triggerAfsOverrides.delete(scriptName);
827
+ }
828
+ /**
829
+ * Tick all registered cron triggers — called by external alarm (e.g., DO alarm).
830
+ * Checks each cron expression against the current minute and fires matching jobs.
831
+ * Uses lastFiredMinute to prevent double-firing (croner timer may also fire).
832
+ */
833
+ tickCronTriggers() {
834
+ const now = Date.now();
835
+ const currentMinute = Math.floor(now / 6e4);
836
+ for (const [, entry] of this.cronRegistry) {
837
+ if (entry.lastFiredMinute >= currentMinute) continue;
838
+ try {
839
+ if (!new croner.Cron(entry.expression).match(new Date(now))) continue;
840
+ } catch {
841
+ continue;
842
+ }
843
+ entry.lastFiredMinute = currentMinute;
844
+ const syntheticEvent = {
845
+ type: "cron",
846
+ path: `/cron/${entry.scriptName}/${entry.jobName}`,
847
+ source: "ash-cron-tick",
848
+ timestamp: now
849
+ };
850
+ const p = this.executeTriggerJob(entry.scriptName, entry.jobName, syntheticEvent);
851
+ this.trackPendingTrigger(p);
852
+ }
853
+ }
854
+ /**
855
+ * Execute a single triggered job reactively in response to an AFS event.
856
+ * Re-compiles the script, builds a bridged world context, executes the
857
+ * target job with event data as the initial stream, and writes results
858
+ * back to AFS.
859
+ */
860
+ async executeTriggerJob(scriptName, jobName, event) {
861
+ try {
862
+ let source;
863
+ const script = await this.getScript(scriptName);
864
+ if (script) source = script.source;
865
+ else if (this.externalScripts.has(scriptName)) source = this.externalScripts.get(scriptName);
866
+ else {
867
+ const readAFS = this.triggerAfsOverrides.get(scriptName) ?? this.afsRoot;
868
+ const colonIdx = scriptName.indexOf(":");
869
+ const readPath = colonIdx >= 0 ? scriptName.slice(colonIdx + 1) : scriptName;
870
+ if (readAFS?.read) try {
871
+ const file = await readAFS.read(readPath, {});
872
+ source = String(file.data?.content ?? "");
873
+ } catch {}
874
+ }
875
+ if (!source) return;
876
+ const result = (0, _aigne_ash.compileSource)(source);
877
+ if (!result.program) return;
878
+ const job = result.program.units.find((u) => u.kind === "job" && u.name === jobName);
879
+ if (!job || job.kind !== "job") return;
880
+ const afsOverride = this.triggerAfsOverrides.get(scriptName);
881
+ const effectiveAFS = afsOverride ?? this.afsRoot;
882
+ const { world, written } = await this.createAsyncWorld(afsOverride);
883
+ const ctx = {
884
+ world: effectiveAFS ? {
885
+ read: (path, query) => readAfsRecords(effectiveAFS, world, path, query),
886
+ write: (path, data) => {
887
+ world.write(path, data);
888
+ },
889
+ publish: (topic, data) => {
890
+ world.publish(topic, data);
891
+ },
892
+ exec: world.exec && afsOverride ? async (path, input, params) => {
893
+ const augmentedInput = input.map((item) => typeof item === "object" && item !== null ? {
894
+ ...item,
895
+ _scope_afs: afsOverride
896
+ } : item);
897
+ return world.exec(path, augmentedInput, params);
898
+ } : world.exec
899
+ } : world,
900
+ caps: new Set(["*"]),
901
+ logger: { log() {} },
902
+ output: { output() {} }
903
+ };
904
+ const initialStream = [{ ...event }];
905
+ const jobResult = await job.execute(ctx, initialStream);
906
+ if (jobResult.status !== "ok") log.error(`[ASH trigger] ${scriptName}/${jobName} status=${jobResult.status}`, jobResult.errors);
907
+ if (effectiveAFS?.write) for (const [path, data] of Object.entries(written)) if (path.endsWith(".log")) {
908
+ const content = `${data.map((item) => JSON.stringify(item)).join("\n")}\n`;
909
+ await effectiveAFS.write(path, { content }, { mode: "append" });
910
+ } else await effectiveAFS.write(path, { content: JSON.stringify(data) });
911
+ } catch (err) {
912
+ log.error(`[ASH trigger] ${scriptName}/${jobName} threw:`, err);
913
+ }
914
+ }
915
+ async listRoot(_ctx) {
916
+ return { data: [{
917
+ id: "/scripts",
918
+ path: "/scripts",
919
+ summary: "Stored ASH scripts",
920
+ meta: {
921
+ kind: "ash:directory",
922
+ type: "directory",
923
+ childrenCount: await this.getScriptCount()
924
+ }
925
+ }, {
926
+ id: "/cookbook",
927
+ path: "/cookbook",
928
+ summary: "ASH best practices and quick reference",
929
+ meta: {
930
+ kind: "ash:directory",
931
+ type: "directory",
932
+ childrenCount: 2
933
+ }
934
+ }] };
935
+ }
936
+ async listScripts(_ctx) {
937
+ const entries = [];
938
+ for (const name of await this.getAllScriptNames()) {
939
+ const script = await this.getScript(name);
940
+ if (!script) continue;
941
+ entries.push(this.buildScriptEntry(name, script));
942
+ }
943
+ return { data: entries };
944
+ }
945
+ async listScript(ctx) {
946
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
947
+ if (!await this.hasScript(name)) throw new _aigne_afs.AFSNotFoundError(ctx.path);
948
+ return { data: [] };
949
+ }
950
+ async listRootActions(_ctx) {
951
+ return { data: [{
952
+ id: "run",
953
+ path: "/.actions/run",
954
+ summary: "Execute inline ASH code",
955
+ meta: {
956
+ kind: "afs:executable",
957
+ kinds: ["afs:executable", "afs:node"],
958
+ name: "run",
959
+ description: "Execute inline ASH pipeline code",
960
+ inputSchema: {
961
+ type: "object",
962
+ properties: {
963
+ source: {
964
+ type: "string",
965
+ description: "ASH pipeline source code. Read /.meta/.ash-reference for syntax."
966
+ },
967
+ caps: {
968
+ type: "array",
969
+ items: { type: "string" },
970
+ description: "Capability paths. Required for write-back to AFS (default: no write-back without caps)"
971
+ }
972
+ },
973
+ required: ["source"]
974
+ }
975
+ }
976
+ }, {
977
+ id: "validate",
978
+ path: "/.actions/validate",
979
+ summary: "Validate ASH source code",
980
+ meta: {
981
+ kind: "afs:executable",
982
+ kinds: ["afs:executable", "afs:node"],
983
+ name: "validate",
984
+ description: "Compile and type-check ASH source, return diagnostics",
985
+ inputSchema: {
986
+ type: "object",
987
+ properties: { source: {
988
+ type: "string",
989
+ description: "ASH pipeline source code to validate. Read /.meta/.ash-reference for syntax."
990
+ } },
991
+ required: ["source"]
992
+ }
993
+ }
994
+ }] };
995
+ }
996
+ async listScriptActions(ctx) {
997
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
998
+ const script = await this.getScript(name);
999
+ if (!script) throw new _aigne_afs.AFSNotFoundError(ctx.path);
1000
+ const inputSchema = this.buildScriptExecInputSchema(script.source);
1001
+ return { data: [{
1002
+ id: "exec",
1003
+ path: (0, ufo.joinURL)((0, ufo.joinURL)("/scripts", ctx.params.scriptName, ".actions"), "exec"),
1004
+ summary: `Execute script ${name}`,
1005
+ meta: {
1006
+ kind: "afs:executable",
1007
+ kinds: ["afs:executable", "afs:node"],
1008
+ name: "exec",
1009
+ description: `Execute the ${name} ASH script`,
1010
+ inputSchema
1011
+ }
1012
+ }] };
1013
+ }
1014
+ async readRoot(_ctx) {
1015
+ return {
1016
+ id: "/",
1017
+ path: "/",
1018
+ meta: {
1019
+ type: "directory",
1020
+ childrenCount: 2
1021
+ },
1022
+ actions: [{
1023
+ name: "run",
1024
+ description: "Execute inline ASH code"
1025
+ }, {
1026
+ name: "validate",
1027
+ description: "Validate ASH source code"
1028
+ }]
1029
+ };
1030
+ }
1031
+ async readScriptsDir(_ctx) {
1032
+ return {
1033
+ id: "/scripts",
1034
+ path: "/scripts",
1035
+ summary: "Stored ASH scripts",
1036
+ meta: {
1037
+ kind: "ash:directory",
1038
+ type: "directory",
1039
+ childrenCount: await this.getScriptCount()
1040
+ }
1041
+ };
1042
+ }
1043
+ async readScript(ctx) {
1044
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1045
+ const script = await this.getScript(name);
1046
+ if (!script) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Script not found: ${name}`);
1047
+ return this.buildScriptEntry(name, script);
1048
+ }
1049
+ static COOKBOOK_FILES = ["patterns.md", "quickref.md"];
1050
+ async listCookbook(_ctx) {
1051
+ return { data: AFSAsh.COOKBOOK_FILES.map((file) => ({
1052
+ id: (0, ufo.joinURL)("/cookbook", file),
1053
+ path: (0, ufo.joinURL)("/cookbook", file),
1054
+ summary: file === "patterns.md" ? "agent-run patterns and pitfalls" : "Quick reference card",
1055
+ meta: { kind: "ash:documentation" }
1056
+ })) };
1057
+ }
1058
+ async readCookbookDir(_ctx) {
1059
+ return {
1060
+ id: "/cookbook",
1061
+ path: "/cookbook",
1062
+ meta: {
1063
+ kind: "ash:directory",
1064
+ type: "directory",
1065
+ childrenCount: AFSAsh.COOKBOOK_FILES.length
1066
+ }
1067
+ };
1068
+ }
1069
+ async listCookbookFile(_ctx) {
1070
+ return { data: [] };
1071
+ }
1072
+ async readCookbook(ctx) {
1073
+ const file = ctx.params.file;
1074
+ if (!AFSAsh.COOKBOOK_FILES.includes(file)) throw new _aigne_afs.AFSNotFoundError(ctx.path);
1075
+ return {
1076
+ id: (0, ufo.joinURL)("/cookbook", file),
1077
+ path: (0, ufo.joinURL)("/cookbook", file),
1078
+ content: await readDoc((0, ufo.joinURL)("cookbook", file)),
1079
+ meta: { kind: "ash:documentation" }
1080
+ };
1081
+ }
1082
+ async readAgentMd(_ctx) {
1083
+ return {
1084
+ id: "/.meta/agent.md",
1085
+ path: "/.meta/agent.md",
1086
+ content: await readDoc("agent.md"),
1087
+ meta: {
1088
+ kind: "ash:documentation",
1089
+ description: "ASH agent usage guide"
1090
+ }
1091
+ };
1092
+ }
1093
+ async readAshReference(_ctx) {
1094
+ return {
1095
+ id: "/.meta/.ash-reference",
1096
+ path: "/.meta/.ash-reference",
1097
+ content: _aigne_ash.ASH_REFERENCE,
1098
+ meta: {
1099
+ kind: "ash:reference",
1100
+ description: "ASH language reference for writing valid ASH pipelines"
1101
+ }
1102
+ };
1103
+ }
1104
+ async readCapabilities(_ctx) {
1105
+ return {
1106
+ id: "/.meta/.capabilities",
1107
+ path: "/.meta/.capabilities",
1108
+ content: {
1109
+ schemaVersion: 1,
1110
+ provider: this.name,
1111
+ description: `${this.description}. Read /.meta/.ash-reference for ASH syntax.`,
1112
+ tools: [],
1113
+ operations: this.getOperationsDeclaration(),
1114
+ actions: [{
1115
+ description: "Root-level ASH actions",
1116
+ catalog: [{
1117
+ name: "run",
1118
+ description: "Execute inline ASH code"
1119
+ }, {
1120
+ name: "validate",
1121
+ description: "Validate ASH source code"
1122
+ }],
1123
+ discovery: { pathTemplate: "/.actions" }
1124
+ }, {
1125
+ kind: "ash:script",
1126
+ description: "Script-level actions",
1127
+ catalog: [{
1128
+ name: "exec",
1129
+ description: "Execute the script"
1130
+ }],
1131
+ discovery: { pathTemplate: "/scripts/:scriptName/.actions" }
1132
+ }],
1133
+ resources: { caps: [
1134
+ {
1135
+ op: "read",
1136
+ path: "/*",
1137
+ description: "Read scripts and results"
1138
+ },
1139
+ {
1140
+ op: "write",
1141
+ path: "/scripts/*",
1142
+ description: "Write ASH script files"
1143
+ },
1144
+ {
1145
+ op: "exec",
1146
+ path: "/.actions/*",
1147
+ description: "Execute root actions (run, validate)"
1148
+ },
1149
+ {
1150
+ op: "exec",
1151
+ path: "/scripts/*/.actions/*",
1152
+ description: "Execute a script"
1153
+ }
1154
+ ] }
1155
+ },
1156
+ meta: {
1157
+ kind: "afs:capabilities",
1158
+ description: "ASH provider capabilities manifest",
1159
+ operations: [
1160
+ "list",
1161
+ "read",
1162
+ "write",
1163
+ "delete",
1164
+ "exec",
1165
+ "stat",
1166
+ "explain",
1167
+ "search"
1168
+ ]
1169
+ }
1170
+ };
1171
+ }
1172
+ async readRootMeta(_ctx) {
1173
+ return {
1174
+ id: "/.meta",
1175
+ path: "/.meta",
1176
+ content: {
1177
+ type: "directory",
1178
+ childrenCount: 2
1179
+ },
1180
+ meta: {
1181
+ type: "directory",
1182
+ childrenCount: 2
1183
+ }
1184
+ };
1185
+ }
1186
+ async readCookbookMeta(_ctx) {
1187
+ return {
1188
+ id: "/cookbook/.meta",
1189
+ path: "/cookbook/.meta",
1190
+ content: {
1191
+ kind: "ash:directory",
1192
+ type: "directory",
1193
+ childrenCount: AFSAsh.COOKBOOK_FILES.length
1194
+ },
1195
+ meta: {
1196
+ kind: "ash:directory",
1197
+ type: "directory",
1198
+ childrenCount: AFSAsh.COOKBOOK_FILES.length
1199
+ }
1200
+ };
1201
+ }
1202
+ async readCookbookFileMeta(ctx) {
1203
+ return {
1204
+ id: (0, ufo.joinURL)("/cookbook", ctx.params.file, ".meta"),
1205
+ path: (0, ufo.joinURL)("/cookbook", ctx.params.file, ".meta"),
1206
+ content: { kind: "ash:documentation" },
1207
+ meta: { kind: "ash:documentation" }
1208
+ };
1209
+ }
1210
+ async readScriptsMeta(_ctx) {
1211
+ return {
1212
+ id: "/scripts/.meta",
1213
+ path: "/scripts/.meta",
1214
+ content: {
1215
+ type: "directory",
1216
+ childrenCount: await this.getScriptCount()
1217
+ },
1218
+ meta: {
1219
+ type: "directory",
1220
+ childrenCount: await this.getScriptCount()
1221
+ }
1222
+ };
1223
+ }
1224
+ async readScriptMeta(ctx) {
1225
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1226
+ const script = await this.getScript(name);
1227
+ if (!script) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Script not found: ${name}`);
1228
+ const entry = this.buildScriptEntry(name, script);
1229
+ return {
1230
+ id: (0, ufo.joinURL)("/scripts", ctx.params.scriptName, ".meta"),
1231
+ path: (0, ufo.joinURL)("/scripts", ctx.params.scriptName, ".meta"),
1232
+ content: entry.meta ?? {},
1233
+ meta: entry.meta ?? {}
1234
+ };
1235
+ }
1236
+ async writeScript(ctx, entry) {
1237
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1238
+ const now = /* @__PURE__ */ new Date();
1239
+ const rawContent = typeof entry === "string" ? entry : entry.content;
1240
+ let existed = false;
1241
+ const stored = await this.updateScript(name, async (existing) => {
1242
+ existed = existing !== void 0;
1243
+ if (rawContent === void 0 && existing) return {
1244
+ source: existing.source,
1245
+ createdAt: existing.createdAt,
1246
+ updatedAt: now
1247
+ };
1248
+ return {
1249
+ source: typeof rawContent === "string" ? rawContent : JSON.stringify(rawContent) ?? "",
1250
+ createdAt: existing?.createdAt ?? now,
1251
+ updatedAt: now
1252
+ };
1253
+ });
1254
+ if (!stored) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Script not found: ${name}`);
1255
+ this.registerTriggers(name, stored.source);
1256
+ return {
1257
+ data: this.buildScriptEntry(name, stored),
1258
+ message: existed ? "Script updated" : "Script created"
1259
+ };
1260
+ }
1261
+ async deleteScript(ctx) {
1262
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1263
+ let existed = false;
1264
+ await this.updateScript(name, async (existing) => {
1265
+ existed = existing !== void 0 || await this.hasScript(name);
1266
+ });
1267
+ if (!existed) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Script not found: ${name}`);
1268
+ this.unregisterTriggers(name);
1269
+ return { message: `Script '${name}' deleted` };
1270
+ }
1271
+ async deleteCatchAll(ctx) {
1272
+ throw new _aigne_afs.AFSNotFoundError(ctx.path);
1273
+ }
1274
+ async execScript(ctx, args) {
1275
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1276
+ const script = await this.getScript(name);
1277
+ if (!script) return {
1278
+ success: false,
1279
+ data: { error: `Script not found: ${name}` }
1280
+ };
1281
+ return this.runAsh(script.source, args);
1282
+ }
1283
+ async execScriptDirect(ctx, args) {
1284
+ return this.execScript(ctx, args);
1285
+ }
1286
+ async execRootAction(ctx, args) {
1287
+ const actionName = ctx.params.action;
1288
+ if (actionName === "run") {
1289
+ const source = args.source;
1290
+ if (typeof source !== "string") throw new Error("action 'run' requires 'source' parameter as string");
1291
+ const jobName = args.job;
1292
+ if (jobName) {
1293
+ const runtimeAFS$1 = args._scope_afs;
1294
+ return this.runJob(source, jobName, args, { afsOverride: runtimeAFS$1 });
1295
+ }
1296
+ const hasCaps = Array.isArray(args.caps) && args.caps.length > 0;
1297
+ const runtimeAFS = args._scope_afs;
1298
+ const returnWrittenData = args._return_written_data === true;
1299
+ const skipWritePrefix = typeof args._skip_write_prefix === "string" ? args._skip_write_prefix : void 0;
1300
+ const cleanArgs = { ...args };
1301
+ delete cleanArgs._scope_afs;
1302
+ delete cleanArgs._return_written_data;
1303
+ delete cleanArgs._skip_write_prefix;
1304
+ return this.runAsh(source, cleanArgs, {
1305
+ allowWriteBack: hasCaps,
1306
+ afsOverride: runtimeAFS,
1307
+ returnWrittenData,
1308
+ skipWritePrefix
1309
+ });
1310
+ }
1311
+ if (actionName === "validate") {
1312
+ const source = args.source;
1313
+ if (typeof source !== "string") throw new Error("action 'validate' requires 'source' parameter as string");
1314
+ const result = (0, _aigne_ash.compileSource)(source);
1315
+ const errors = result.diagnostics.filter((d) => d.severity !== "warning");
1316
+ const warnings = result.diagnostics.filter((d) => d.severity === "warning");
1317
+ return {
1318
+ success: true,
1319
+ data: {
1320
+ valid: errors.length === 0,
1321
+ errors,
1322
+ warnings,
1323
+ jobCount: result.program?.jobs.length ?? 0,
1324
+ sourceHash: result.sourceHash
1325
+ }
1326
+ };
1327
+ }
1328
+ if (actionName === "agent-run") {
1329
+ if (!ashAgentRunDeprecationLogged) {
1330
+ log.warn("[ash] DEPRECATED: /ash/.actions/agent-run — use /dev/agent/.actions/run instead. This alias will be removed in a future version.");
1331
+ ashAgentRunDeprecationLogged = true;
1332
+ }
1333
+ const afs = args._scope_afs ?? this.afsRoot;
1334
+ if (!afs?.exec) return {
1335
+ success: false,
1336
+ error: {
1337
+ code: "AGENT_RUN_ALIAS_NO_AFS",
1338
+ message: "AFS root not available"
1339
+ }
1340
+ };
1341
+ return afs.exec("/dev/agent/.actions/run", args, {});
1342
+ }
1343
+ if (actionName === "register-triggers") {
1344
+ const runtimeAFS = args._scope_afs;
1345
+ const namespace = typeof args.namespace === "string" ? args.namespace : void 0;
1346
+ if (runtimeAFS) await this.registerProgramTriggers(runtimeAFS, namespace);
1347
+ return {
1348
+ success: true,
1349
+ data: { registered: !!runtimeAFS }
1350
+ };
1351
+ }
1352
+ if (actionName === "tick-crons") {
1353
+ this.tickCronTriggers();
1354
+ return {
1355
+ success: true,
1356
+ data: { cronCount: this.cronRegistry.size }
1357
+ };
1358
+ }
1359
+ throw new Error(`Unknown action: ${actionName}`);
1360
+ }
1361
+ async statRoot(_ctx) {
1362
+ return { data: {
1363
+ id: "/",
1364
+ path: "/",
1365
+ meta: {
1366
+ type: "directory",
1367
+ childrenCount: 1,
1368
+ scriptCount: await this.getScriptCount()
1369
+ }
1370
+ } };
1371
+ }
1372
+ async statScriptsDir(_ctx) {
1373
+ return { data: {
1374
+ id: "/scripts",
1375
+ path: "/scripts",
1376
+ meta: {
1377
+ type: "directory",
1378
+ childrenCount: await this.getScriptCount()
1379
+ }
1380
+ } };
1381
+ }
1382
+ async statScript(ctx) {
1383
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1384
+ const script = await this.getScript(name);
1385
+ if (!script) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Script not found: ${name}`);
1386
+ return { data: {
1387
+ id: (0, ufo.joinURL)("/scripts", `${name}.ash`),
1388
+ path: (0, ufo.joinURL)("/scripts", `${name}.ash`),
1389
+ createdAt: script.createdAt,
1390
+ updatedAt: script.updatedAt,
1391
+ meta: {
1392
+ type: "file",
1393
+ kind: "ash:script",
1394
+ scriptName: name,
1395
+ size: new TextEncoder().encode(script.source).length
1396
+ }
1397
+ } };
1398
+ }
1399
+ async explainRoot(_ctx) {
1400
+ return {
1401
+ format: "markdown",
1402
+ content: await readDoc("agent.md")
1403
+ };
1404
+ }
1405
+ async explainScript(ctx) {
1406
+ const name = ctx.params.scriptName.replace(/\.ash$/, "");
1407
+ const script = await this.getScript(name);
1408
+ if (!script) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Script not found: ${name}`);
1409
+ const result = (0, _aigne_ash.compileSource)(script.source);
1410
+ const lines = [];
1411
+ lines.push(`# Script: ${name}.ash`);
1412
+ lines.push("");
1413
+ lines.push("## Source");
1414
+ lines.push("");
1415
+ lines.push("```ash");
1416
+ lines.push(script.source);
1417
+ lines.push("```");
1418
+ lines.push("");
1419
+ lines.push("## Analysis");
1420
+ lines.push("");
1421
+ lines.push(`- **Jobs**: ${result.program?.jobs.length ?? 0}`);
1422
+ lines.push(`- **Diagnostics**: ${result.diagnostics.length}`);
1423
+ lines.push(`- **Size**: ${new TextEncoder().encode(script.source).length} bytes`);
1424
+ lines.push("");
1425
+ return {
1426
+ format: "markdown",
1427
+ content: lines.join("\n")
1428
+ };
1429
+ }
1430
+ async searchScripts(_ctx, query) {
1431
+ const results = [];
1432
+ const lowerQuery = query.toLowerCase();
1433
+ for (const name of await this.getAllScriptNames()) {
1434
+ const script = await this.getScript(name);
1435
+ if (!script) continue;
1436
+ if (name.toLowerCase().includes(lowerQuery) || script.source.toLowerCase().includes(lowerQuery)) results.push(this.buildScriptEntry(name, script));
1437
+ }
1438
+ return { data: results };
1439
+ }
1440
+ buildScriptEntry(name, script) {
1441
+ const inputSchema = this.buildScriptExecInputSchema(script.source);
1442
+ return {
1443
+ id: (0, ufo.joinURL)("/scripts", `${name}.ash`),
1444
+ path: (0, ufo.joinURL)("/scripts", `${name}.ash`),
1445
+ content: script.source,
1446
+ createdAt: script.createdAt,
1447
+ updatedAt: script.updatedAt,
1448
+ meta: {
1449
+ type: "file",
1450
+ kind: "ash:script",
1451
+ kinds: [
1452
+ "ash:script",
1453
+ "afs:executable",
1454
+ "afs:node"
1455
+ ],
1456
+ scriptName: name,
1457
+ size: new TextEncoder().encode(script.source).length
1458
+ },
1459
+ actions: [{
1460
+ name: "exec",
1461
+ description: `Execute script ${name}`,
1462
+ inputSchema
1463
+ }]
1464
+ };
1465
+ }
1466
+ buildScriptExecInputSchema(source) {
1467
+ const properties = { caps: {
1468
+ type: "array",
1469
+ items: { type: "string" },
1470
+ description: "Capability paths. Required for write-back to AFS (default: no write-back without caps)"
1471
+ } };
1472
+ const result = (0, _aigne_ash.compileSource)(source);
1473
+ if (result.program) for (const [key, value] of result.program.params) properties[key] = {
1474
+ type: typeof value === "number" ? "number" : "string",
1475
+ description: `Override ASH param '${key}' at execution time`
1476
+ };
1477
+ return {
1478
+ type: "object",
1479
+ properties
1480
+ };
1481
+ }
1482
+ /**
1483
+ * Create an async-capable world that bridges ASH to AFS.
1484
+ * Since ASH's WorldInterface is synchronous but AFS is async,
1485
+ * we collect operations and replay them, or use a pre-loaded data approach.
1486
+ */
1487
+ async createAsyncWorld(afsOverride) {
1488
+ const dataStore = { ...await this.loadPersistedData() };
1489
+ const written = {};
1490
+ const published = {};
1491
+ const writeCounts = {};
1492
+ const afsRoot = afsOverride ?? this.afsRoot;
1493
+ const selfPrefix = `/${this.name}/`;
1494
+ const allowRootActions = this.allowRootActions;
1495
+ const pendingPersist = [];
1496
+ const persistData = (path, data) => {
1497
+ pendingPersist.push(this.persistData(path, data).catch(() => {}));
1498
+ };
1499
+ return {
1500
+ world: {
1501
+ read(path) {
1502
+ return dataStore[normalizeAbsoluteAfsPath(path)] ?? [];
1503
+ },
1504
+ write(path, data) {
1505
+ const normalizedPath = normalizeAbsoluteAfsPath(path);
1506
+ if (normalizedPath.startsWith(selfPrefix) || normalizedPath.startsWith("/scripts") || normalizedPath.startsWith("/.actions") || normalizedPath.startsWith("/.meta")) throw new Error(`Write blocked: '${path}' is a protected ASH provider path`);
1507
+ writeCounts[normalizedPath] = (writeCounts[normalizedPath] ?? 0) + 1;
1508
+ if (written[normalizedPath]) written[normalizedPath].push(...data);
1509
+ else written[normalizedPath] = [...data];
1510
+ dataStore[normalizedPath] = written[normalizedPath];
1511
+ persistData(normalizedPath, data);
1512
+ },
1513
+ publish(topic, data) {
1514
+ published[topic] = data;
1515
+ },
1516
+ async exec(path, input, params) {
1517
+ const normalizedPath = normalizeAbsoluteAfsPath(path);
1518
+ const rootActionMatch = /^\/\.actions\/([^/]+)$/.exec(normalizedPath) ?? (/* @__PURE__ */ new RegExp(`^${selfPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.actions\\/([^/]+)$`)).exec(normalizedPath);
1519
+ if (!(rootActionMatch !== null && allowRootActions.has(rootActionMatch[1])) && (normalizedPath.startsWith(selfPrefix) || normalizedPath.startsWith("/.actions") || normalizedPath.startsWith("/scripts"))) throw new Error(`Exec blocked: self-referencing call to '${path}' would cause recursion`);
1520
+ if (afsRoot?.exec) {
1521
+ const baseParams = params ?? {};
1522
+ const records = input.filter((r) => typeof r === "object" && r !== null);
1523
+ const mergeContext = (inputArgs, outputData) => {
1524
+ const items = Array.isArray(outputData) ? outputData : [outputData];
1525
+ const { _scope_afs: _, ...context } = inputArgs;
1526
+ return items.map((d) => typeof d === "object" && d !== null ? {
1527
+ ...context,
1528
+ ...d
1529
+ } : d);
1530
+ };
1531
+ const execAndCheck = async (mergedArgs) => {
1532
+ const result = await afsRoot.exec(normalizedPath, mergedArgs, {});
1533
+ if (!result.success) {
1534
+ const errMsg = typeof result.error === "object" && result.error !== null ? result.error.message ?? JSON.stringify(result.error) : typeof result.data?.error === "string" ? result.data.error : `Action failed: ${normalizedPath}`;
1535
+ throw new Error(errMsg);
1536
+ }
1537
+ return mergeContext(mergedArgs, result.data);
1538
+ };
1539
+ if (records.length <= 1) {
1540
+ const mergedArgs = {
1541
+ ...records[0] ?? {},
1542
+ ...baseParams
1543
+ };
1544
+ if (afsOverride) mergedArgs._scope_afs = afsOverride;
1545
+ return execAndCheck(mergedArgs);
1546
+ }
1547
+ const results = [];
1548
+ for (const record of records) {
1549
+ const mergedArgs = {
1550
+ ...record,
1551
+ ...baseParams
1552
+ };
1553
+ if (afsOverride) mergedArgs._scope_afs = afsOverride;
1554
+ results.push(...await execAndCheck(mergedArgs));
1555
+ }
1556
+ return results;
1557
+ }
1558
+ return input;
1559
+ },
1560
+ async input(prompt) {
1561
+ if (afsRoot?.exec) try {
1562
+ const result = await afsRoot.exec("/ui/.actions/prompt", {
1563
+ message: prompt,
1564
+ type: "text"
1565
+ }, {});
1566
+ return String(result.data?.response ?? "");
1567
+ } catch (err) {
1568
+ if (err instanceof _aigne_afs.AFSNotFoundError) return "";
1569
+ throw err;
1570
+ }
1571
+ return "";
1572
+ }
1573
+ },
1574
+ written,
1575
+ published,
1576
+ writeCounts,
1577
+ preload(path, data) {
1578
+ dataStore[path] = data;
1579
+ },
1580
+ async flushPersist() {
1581
+ await Promise.all(pendingPersist);
1582
+ }
1583
+ };
1584
+ }
1585
+ /**
1586
+ * Execute a single named job from an ASH source.
1587
+ * Used by ProgramManager to run trigger jobs with event data.
1588
+ */
1589
+ async runJob(source, jobName, args, options) {
1590
+ const result = (0, _aigne_ash.compileSource)(this.applyParamOverridesToSource(source, args));
1591
+ if (!result.program) return {
1592
+ success: false,
1593
+ data: {
1594
+ error: `Compilation failed: ${result.diagnostics.map((d) => d.message).join("; ") || "unknown error"}`,
1595
+ diagnostics: result.diagnostics
1596
+ }
1597
+ };
1598
+ const job = result.program.units.find((u) => u.kind === "job" && u.name === jobName);
1599
+ if (!job || job.kind !== "job") return {
1600
+ success: false,
1601
+ data: { error: `Job "${jobName}" not found in source` }
1602
+ };
1603
+ const afsOverride = options?.afsOverride;
1604
+ const { world, written } = await this.createAsyncWorld(afsOverride);
1605
+ const effectiveAFS = afsOverride ?? this.afsRoot;
1606
+ const bridgedWorld = effectiveAFS ? {
1607
+ read: (path, query) => readAfsRecords(effectiveAFS, world, path, query),
1608
+ write: (path, data) => {
1609
+ world.write(path, data);
1610
+ },
1611
+ publish: (topic, data) => {
1612
+ world.publish(topic, data);
1613
+ },
1614
+ exec: world.exec
1615
+ } : world;
1616
+ let outputResult;
1617
+ const ctx = {
1618
+ world: bridgedWorld,
1619
+ caps: new Set(["*"]),
1620
+ logger: { log() {} },
1621
+ output: { output(event$1) {
1622
+ outputResult = event$1.content;
1623
+ } }
1624
+ };
1625
+ const event = args.event;
1626
+ const initialStream = event ? [{ ...event }] : [];
1627
+ const jobResult = await job.execute(ctx, initialStream);
1628
+ const afs = effectiveAFS;
1629
+ if (afs?.write) for (const [path, data] of Object.entries(written)) if (path.endsWith(".log")) {
1630
+ const content = `${data.map((item) => JSON.stringify(item)).join("\n")}\n`;
1631
+ await afs.write(path, { content }, { mode: "append" });
1632
+ } else await afs.write(path, { content: JSON.stringify(data) });
1633
+ if (jobResult.status !== "ok") return {
1634
+ success: false,
1635
+ data: {
1636
+ error: `Job "${jobName}" failed`,
1637
+ status: jobResult.status,
1638
+ errors: jobResult.errors
1639
+ }
1640
+ };
1641
+ return {
1642
+ success: true,
1643
+ data: {
1644
+ job: jobName,
1645
+ recordCount: jobResult.recordCount,
1646
+ ...outputResult !== void 0 ? { result: outputResult } : {}
1647
+ }
1648
+ };
1649
+ }
1650
+ async runAsh(source, args, options) {
1651
+ const result = (0, _aigne_ash.compileSource)(this.applyParamOverridesToSource(source, args));
1652
+ if (result.diagnostics.filter((d) => d.severity !== "warning").length > 0 || !result.program) {
1653
+ const sanitizedDiags = result.diagnostics.map((d) => ({
1654
+ ...d,
1655
+ message: sanitizeErrorPath(d.message)
1656
+ }));
1657
+ return {
1658
+ success: false,
1659
+ data: {
1660
+ error: `Compilation failed: ${sanitizedDiags.filter((d) => d.severity !== "warning").map((d) => d.message).join("; ") || "unknown error"}`,
1661
+ diagnostics: sanitizedDiags,
1662
+ sourceHash: result.sourceHash
1663
+ }
1664
+ };
1665
+ }
1666
+ const expectedHash = args.expectedHash;
1667
+ if (typeof expectedHash === "string" && expectedHash !== result.sourceHash) return {
1668
+ success: false,
1669
+ data: {
1670
+ error: "Source hash mismatch — code was modified between validate and run (TOCTOU)",
1671
+ expectedHash,
1672
+ actualHash: result.sourceHash
1673
+ }
1674
+ };
1675
+ const capsArg = args.caps;
1676
+ const caps = Array.isArray(capsArg) && capsArg.every((c) => typeof c === "string") ? new Set(capsArg) : new Set(["*"]);
1677
+ const afsOverride = options?.afsOverride;
1678
+ const { world, written, published, writeCounts, flushPersist } = await this.createAsyncWorld(afsOverride);
1679
+ const effectiveAFS = afsOverride ?? this.afsRoot;
1680
+ const bridgedWorld = effectiveAFS ? {
1681
+ read: (path, query) => readAfsRecords(effectiveAFS, world, path, query),
1682
+ write: (path, data) => {
1683
+ world.write(path, data);
1684
+ },
1685
+ publish: (topic, data) => {
1686
+ world.publish(topic, data);
1687
+ },
1688
+ exec: world.exec
1689
+ } : world;
1690
+ const logs = [];
1691
+ const logger = { log(stage, action, detail) {
1692
+ logs.push({
1693
+ stage,
1694
+ action,
1695
+ detail
1696
+ });
1697
+ } };
1698
+ const outputs = [];
1699
+ const ctx = {
1700
+ world: bridgedWorld,
1701
+ caps,
1702
+ logger,
1703
+ output: { output(event) {
1704
+ outputs.push(event.content);
1705
+ } }
1706
+ };
1707
+ const jobResults = [];
1708
+ for (const unit of result.program.units) if (unit.kind === "job") {
1709
+ if (result.program.routeTargets?.has(unit.name)) continue;
1710
+ if (unit.trigger) continue;
1711
+ jobResults.push(await unit.execute(ctx));
1712
+ } else await unit.execute(ctx);
1713
+ const sanitizedJobs = jobResults.map(({ stages, ...rest }) => rest);
1714
+ const allOk = jobResults.every((r) => r.status === "ok");
1715
+ const allErrors = jobResults.flatMap((r) => r.errors).map(sanitizeErrorPath);
1716
+ const afs = afsOverride ?? this.afsRoot;
1717
+ const skipPrefix = options?.skipWritePrefix;
1718
+ if (afs?.write && options?.allowWriteBack !== false) {
1719
+ for (const [path, data] of Object.entries(written)) {
1720
+ if (skipPrefix && path.startsWith(skipPrefix)) continue;
1721
+ try {
1722
+ if (path.endsWith(".log")) {
1723
+ const content = `${data.map((item) => JSON.stringify(item)).join("\n")}\n`;
1724
+ await afs.write(path, { content }, { mode: "append" });
1725
+ } else await afs.write(path, { content: JSON.stringify(data) });
1726
+ } catch {
1727
+ allErrors.push("Failed to write to AFS");
1728
+ }
1729
+ }
1730
+ for (const [topic, data] of Object.entries(published)) try {
1731
+ await afs.write(topic, { content: JSON.stringify(data) });
1732
+ } catch {
1733
+ allErrors.push("Failed to publish to AFS");
1734
+ }
1735
+ }
1736
+ const overwrittenPaths = Object.entries(writeCounts).filter(([, count]) => count > 1).map(([path, count]) => sanitizeErrorPath(`Path '${path}' was written ${count} times — only last write preserved`));
1737
+ if (overwrittenPaths.length > 0) allErrors.push(...overwrittenPaths);
1738
+ await flushPersist();
1739
+ return {
1740
+ success: allOk && allErrors.length === 0 && overwrittenPaths.length === 0,
1741
+ data: {
1742
+ jobs: sanitizedJobs,
1743
+ written: Object.keys(written),
1744
+ published: Object.keys(published),
1745
+ outputs,
1746
+ errors: allErrors.length > 0 ? allErrors : void 0,
1747
+ warnings: result.diagnostics.filter((d) => d.severity === "warning").map((d) => ({
1748
+ ...d,
1749
+ message: sanitizeErrorPath(d.message)
1750
+ })),
1751
+ sourceHash: result.sourceHash,
1752
+ ...options?.returnWrittenData ? { writtenData: written } : {}
1753
+ }
1754
+ };
1755
+ }
1756
+ applyParamOverridesToSource(source, args) {
1757
+ const lines = source.split("\n");
1758
+ let changed = false;
1759
+ const replaced = lines.map((line) => {
1760
+ const match = /^(\s*param\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*)(.+)$/.exec(line);
1761
+ if (!match) return line;
1762
+ const [, prefix, name] = match;
1763
+ if (!name) return line;
1764
+ const value = args[name];
1765
+ if (typeof value !== "string" && typeof value !== "number") return line;
1766
+ changed = true;
1767
+ return `${prefix}${typeof value === "string" ? JSON.stringify(value) : String(value)}`;
1768
+ });
1769
+ return changed ? replaced.join("\n") : source;
1770
+ }
1771
+ };
1772
+ __decorate([(0, _aigne_afs_provider.List)("/")], AFSAsh.prototype, "listRoot", null);
1773
+ __decorate([(0, _aigne_afs_provider.List)("/scripts")], AFSAsh.prototype, "listScripts", null);
1774
+ __decorate([(0, _aigne_afs_provider.List)("/scripts/:scriptName")], AFSAsh.prototype, "listScript", null);
1775
+ __decorate([(0, _aigne_afs_provider.Actions)("/")], AFSAsh.prototype, "listRootActions", null);
1776
+ __decorate([(0, _aigne_afs_provider.Actions)("/scripts/:scriptName")], AFSAsh.prototype, "listScriptActions", null);
1777
+ __decorate([(0, _aigne_afs_provider.Read)("/")], AFSAsh.prototype, "readRoot", null);
1778
+ __decorate([(0, _aigne_afs_provider.Read)("/scripts")], AFSAsh.prototype, "readScriptsDir", null);
1779
+ __decorate([(0, _aigne_afs_provider.Read)("/scripts/:scriptName")], AFSAsh.prototype, "readScript", null);
1780
+ __decorate([(0, _aigne_afs_provider.List)("/cookbook")], AFSAsh.prototype, "listCookbook", null);
1781
+ __decorate([(0, _aigne_afs_provider.Read)("/cookbook")], AFSAsh.prototype, "readCookbookDir", null);
1782
+ __decorate([(0, _aigne_afs_provider.List)("/cookbook/:file")], AFSAsh.prototype, "listCookbookFile", null);
1783
+ __decorate([(0, _aigne_afs_provider.Read)("/cookbook/:file")], AFSAsh.prototype, "readCookbook", null);
1784
+ __decorate([(0, _aigne_afs_provider.Read)("/.meta/agent.md")], AFSAsh.prototype, "readAgentMd", null);
1785
+ __decorate([(0, _aigne_afs_provider.Read)("/.meta/.ash-reference")], AFSAsh.prototype, "readAshReference", null);
1786
+ __decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], AFSAsh.prototype, "readCapabilities", null);
1787
+ __decorate([(0, _aigne_afs_provider.Meta)("/")], AFSAsh.prototype, "readRootMeta", null);
1788
+ __decorate([(0, _aigne_afs_provider.Meta)("/cookbook")], AFSAsh.prototype, "readCookbookMeta", null);
1789
+ __decorate([(0, _aigne_afs_provider.Meta)("/cookbook/:file")], AFSAsh.prototype, "readCookbookFileMeta", null);
1790
+ __decorate([(0, _aigne_afs_provider.Meta)("/scripts")], AFSAsh.prototype, "readScriptsMeta", null);
1791
+ __decorate([(0, _aigne_afs_provider.Meta)("/scripts/:scriptName")], AFSAsh.prototype, "readScriptMeta", null);
1792
+ __decorate([(0, _aigne_afs_provider.Write)("/scripts/:scriptName")], AFSAsh.prototype, "writeScript", null);
1793
+ __decorate([(0, _aigne_afs_provider.Delete)("/scripts/:scriptName")], AFSAsh.prototype, "deleteScript", null);
1794
+ __decorate([(0, _aigne_afs_provider.Delete)("/:path*")], AFSAsh.prototype, "deleteCatchAll", null);
1795
+ __decorate([_aigne_afs_provider.Actions.Exec("/scripts/:scriptName", "exec", void 0, { effect: "write" })], AFSAsh.prototype, "execScript", null);
1796
+ __decorate([(0, _aigne_afs_provider.Exec)("/scripts/:scriptName")], AFSAsh.prototype, "execScriptDirect", null);
1797
+ __decorate([_aigne_afs_provider.Actions.Exec("/", void 0, void 0, { effect: "write" })], AFSAsh.prototype, "execRootAction", null);
1798
+ __decorate([(0, _aigne_afs_provider.Stat)("/")], AFSAsh.prototype, "statRoot", null);
1799
+ __decorate([(0, _aigne_afs_provider.Stat)("/scripts")], AFSAsh.prototype, "statScriptsDir", null);
1800
+ __decorate([(0, _aigne_afs_provider.Stat)("/scripts/:scriptName")], AFSAsh.prototype, "statScript", null);
1801
+ __decorate([(0, _aigne_afs_provider.Explain)("/")], AFSAsh.prototype, "explainRoot", null);
1802
+ __decorate([(0, _aigne_afs_provider.Explain)("/scripts/:scriptName")], AFSAsh.prototype, "explainScript", null);
1803
+ __decorate([(0, _aigne_afs_provider.Search)("/:path*")], AFSAsh.prototype, "searchScripts", null);
1804
+
1805
+ //#endregion
1806
+ exports.intersectTools = intersectTools;
1807
+ exports.validateActionPath = validateActionPath;
1808
+ exports.validateToolCall = validateToolCall;
1809
+ exports.withQueryPairing = withQueryPairing;