@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,531 @@
1
+ import { intersectTools, withQueryPairing } from "../providers/basic/ash/dist/index.mjs";
2
+ import { resolveBudget } from "../agent-runners/budget.mjs";
3
+ import { attachScratchMount, createScratchProvider } from "../agent-runners/scratchpad-afs.mjs";
4
+ import { attachMemoryMount, buildMemoryIndexOptsForAfs, buildMemoryStorageForAfs, createMemoryProvider, extractManifestDomain, resolveMemoryDomain } from "../agent-runners/memory-afs.mjs";
5
+ import { MESSAGING_TOOLS_GLOB, MESSAGING_TOOLS_ROOT } from "./agent-messages-tools.mjs";
6
+ import { projectSubAgent } from "@aigne/afs";
7
+
8
+ //#region src/program/scoped-afs.ts
9
+ /**
10
+ * Chain-depth hard cap. Mirrors `HARD_CAP` in
11
+ * `providers/basic/ash/src/spawn-depth.ts`; kept in sync manually because
12
+ * importing across package boundaries for a constant isn't worth the
13
+ * dependency. Any change there must be reflected here.
14
+ *
15
+ * Semantics match `spawn-depth.ts`: a child whose post-increment depth
16
+ * would be **>= HARD_CAP** is rejected with `CHAIN_DEPTH_EXCEEDED`. With
17
+ * root.chainDepth = 0 this means the deepest reachable child is depth 9;
18
+ * attempting to spawn depth 10 throws.
19
+ */
20
+ const CHAIN_DEPTH_HARD_CAP = 10;
21
+ /** Default ops applied when a manifest tool entry omits `ops`. */
22
+ const DEFAULT_TOOL_OPS = [
23
+ "read",
24
+ "list",
25
+ "exec",
26
+ "query"
27
+ ];
28
+ /**
29
+ * Built-in messaging tool path — every scope surfaces
30
+ * `/proc/messaging/tools/*`
31
+ * so the LLM can call `agent_post_message` / `agent_read_messages` without
32
+ * each manifest having to declare them. See B32 in
33
+ * `providers/basic/ash/src/agent-run.ts`.
34
+ */
35
+ const MESSAGING_TOOL_PATH = MESSAGING_TOOLS_GLOB;
36
+ /**
37
+ * Budget mount — per-scope `/.meta/budget/{limit,remaining,consumed}` state
38
+ * files driven by the exec middleware in `attachBudgetMount`. Path contract:
39
+ *
40
+ * - `read("/.meta/budget/limit")` → configured ceiling (tokens)
41
+ * - `read("/.meta/budget/remaining")` → `max(0, limit - consumed)`
42
+ * - `read("/.meta/budget/consumed")` → tokens decremented so far
43
+ * - `write("/.meta/budget/*", ...)` → always `PERMISSION_DENIED`; only
44
+ * the middleware may mutate state (via the internal `BudgetState` ref).
45
+ * - `list("/.meta/budget")` → three-entry listing (above keys).
46
+ *
47
+ * The exec middleware wraps the underlying `afs.exec`:
48
+ * 1. Pre-check `consumed < limit`; otherwise throw `BUDGET_EXHAUSTED`.
49
+ * 2. Call underlying exec.
50
+ * 3. Extract `tokens_used` from the result and serialize the increment
51
+ * against the rootSession mutex so concurrent sub-runs don't race.
52
+ *
53
+ * Child scopes created by `projectForChild` inherit via a new `BudgetState`
54
+ * whose `parent` points back to the caller's state. Each scope's middleware
55
+ * only decrements its *own* `consumed` — propagation to ancestors happens
56
+ * naturally because `child.exec` delegates to `parent.exec` through the
57
+ * prototype chain, so every ancestor's middleware runs once per call and
58
+ * the rootSession mutex keeps the sequence of increments linear.
59
+ */
60
+ const BUDGET_MOUNT_ROOT = "/.meta/budget";
61
+ const BUDGET_KEY_LIMIT = "limit";
62
+ const BUDGET_KEY_REMAINING = "remaining";
63
+ const BUDGET_KEY_CONSUMED = "consumed";
64
+ const BUDGET_KEYS = [
65
+ BUDGET_KEY_LIMIT,
66
+ BUDGET_KEY_REMAINING,
67
+ BUDGET_KEY_CONSUMED
68
+ ];
69
+ /**
70
+ * Process-local mutex map keyed on `rootSession`. Every decrement for a
71
+ * given scope tree serializes against the same promise chain, so two
72
+ * concurrent sub-runs sharing a rootSession cannot double-count or see a
73
+ * mid-update view of `consumed`. Phase 2 will swap the Map for an AFS KV
74
+ * provider so the guarantee holds across processes; the API shape stays.
75
+ */
76
+ const rootBudgetLocks = /* @__PURE__ */ new Map();
77
+ async function withRootBudgetLock(rootSession, fn) {
78
+ const stored = (rootBudgetLocks.get(rootSession) ?? Promise.resolve()).then(fn, fn);
79
+ rootBudgetLocks.set(rootSession, stored);
80
+ try {
81
+ return await stored;
82
+ } finally {
83
+ if (rootBudgetLocks.get(rootSession) === stored) rootBudgetLocks.delete(rootSession);
84
+ }
85
+ }
86
+ function createBudgetState(init) {
87
+ if (!init.rootSession || typeof init.rootSession !== "string") throw new Error(`BUDGET_INVALID: rootSession must be a non-empty string`);
88
+ if (typeof init.limit !== "number" || Number.isNaN(init.limit) || init.limit < 0) throw new Error(`BUDGET_INVALID: limit must be a non-negative number`);
89
+ return {
90
+ rootSession: init.rootSession,
91
+ limit: Math.floor(init.limit),
92
+ consumed: 0
93
+ };
94
+ }
95
+ function budgetRemaining(state) {
96
+ return Math.max(0, state.limit - state.consumed);
97
+ }
98
+ function isBudgetPath(path) {
99
+ return path === BUDGET_MOUNT_ROOT || path.startsWith(`${BUDGET_MOUNT_ROOT}/`);
100
+ }
101
+ function parseBudgetKey(path) {
102
+ if (!path.startsWith(`${BUDGET_MOUNT_ROOT}/`)) return null;
103
+ const key = path.slice(14);
104
+ return BUDGET_KEYS.includes(key) ? key : null;
105
+ }
106
+ function budgetValue(state, key) {
107
+ if (key === BUDGET_KEY_LIMIT) return state.limit;
108
+ if (key === BUDGET_KEY_CONSUMED) return state.consumed;
109
+ return budgetRemaining(state);
110
+ }
111
+ /**
112
+ * Extract `tokens_used` from an exec result. Supports both legacy
113
+ * `data.total_tokens` (the agent-run envelope) and `data._meta.tokens_used`
114
+ * (team/orchestrator aggregate meta). Anything else → 0.
115
+ */
116
+ function extractTokensUsed(result) {
117
+ const data = result?.data;
118
+ if (!data || typeof data !== "object") return 0;
119
+ const candidates = [data.total_tokens, data._meta?.tokens_used];
120
+ for (const value of candidates) if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
121
+ return 0;
122
+ }
123
+ /**
124
+ * Overlay the budget mount on an AFS root:
125
+ * - `/.meta/budget/{limit,remaining,consumed}` → live reads
126
+ * - `/.meta/budget/*` writes → `PERMISSION_DENIED`
127
+ * - `exec(...)` → pre-check `BUDGET_EXHAUSTED`, then decrement by
128
+ * `tokens_used` under the rootSession mutex.
129
+ *
130
+ * The overlay is non-enumerable (matches the scratch/memory pattern) and
131
+ * transparent for non-budget paths, which fall through via the prototype
132
+ * chain. Every projected child attaches its own overlay backed by its own
133
+ * `BudgetState`, so sibling scopes never see each other's `consumed`.
134
+ */
135
+ function attachBudgetMount(afs, state) {
136
+ const child = Object.create(afs);
137
+ Object.defineProperty(child, "read", {
138
+ value: async (path, options) => {
139
+ const key = parseBudgetKey(path);
140
+ if (key) return {
141
+ success: true,
142
+ data: {
143
+ content: String(budgetValue(state, key)),
144
+ path
145
+ }
146
+ };
147
+ return afs.read.call(afs, path, options);
148
+ },
149
+ enumerable: false,
150
+ writable: false,
151
+ configurable: false
152
+ });
153
+ Object.defineProperty(child, "write", {
154
+ value: async (path, payload, options) => {
155
+ if (isBudgetPath(path)) {
156
+ const err = /* @__PURE__ */ new Error(`PERMISSION_DENIED: ${path} is managed by the budget exec middleware and cannot be written directly`);
157
+ err.code = "PERMISSION_DENIED";
158
+ throw err;
159
+ }
160
+ if (typeof afs.write !== "function") throw new Error(`BUDGET_NO_UNDERLYING_WRITE: afs.write not available for ${path}`);
161
+ return afs.write.call(afs, path, payload, options);
162
+ },
163
+ enumerable: false,
164
+ writable: false,
165
+ configurable: false
166
+ });
167
+ Object.defineProperty(child, "list", {
168
+ value: async (path, options) => {
169
+ if (path === BUDGET_MOUNT_ROOT || path === `${BUDGET_MOUNT_ROOT}/`) return {
170
+ success: true,
171
+ data: { list: BUDGET_KEYS.map((name) => ({
172
+ path: `${BUDGET_MOUNT_ROOT}/${name}`,
173
+ name,
174
+ type: "file"
175
+ })) }
176
+ };
177
+ return afs.list.call(afs, path, options);
178
+ },
179
+ enumerable: false,
180
+ writable: false,
181
+ configurable: false
182
+ });
183
+ Object.defineProperty(child, "exec", {
184
+ value: async (path, args, options) => {
185
+ if (state.consumed >= state.limit) {
186
+ const err = /* @__PURE__ */ new Error(`BUDGET_EXHAUSTED: scope budget exhausted (path=${path}, limit=${state.limit}, consumed=${state.consumed})`);
187
+ err.code = "BUDGET_EXHAUSTED";
188
+ throw err;
189
+ }
190
+ const result = await afs.exec.call(afs, path, args, options);
191
+ const used = extractTokensUsed(result);
192
+ if (used > 0) await withRootBudgetLock(state.rootSession, () => {
193
+ state.consumed += used;
194
+ });
195
+ return result;
196
+ },
197
+ enumerable: false,
198
+ writable: false,
199
+ configurable: false
200
+ });
201
+ return child;
202
+ }
203
+ /**
204
+ * Resolve a child scope's budget. The child's effective limit is the
205
+ * result of `resolveBudget({manifest, parentRemaining})`; consumed starts
206
+ * at 0. Ancestor updates are handled by the middleware chain itself
207
+ * (each scope's middleware runs once per call via the prototype chain),
208
+ * so the child's `BudgetState` is independent from the parent's.
209
+ *
210
+ * Returns `undefined` when the parent has no active budget — zero-overhead
211
+ * path for legacy callers that never opt in.
212
+ *
213
+ * Inheritance rules (P5 deferred nit documented here for clarity):
214
+ *
215
+ * - Parent has budget, child manifest declares one → `resolveBudget`
216
+ * clamps `min(childBudget.totalTokens, parentRemaining)`.
217
+ * - Parent has budget, child manifest omits budget → `resolveBudget` uses
218
+ * `DEFAULT_TOTAL_TOKENS` (80k) then clamps to `parentRemaining`. Net
219
+ * effect: an undeclared child never exceeds the parent's remaining
220
+ * window, which is the conservative default.
221
+ * - Parent has no budget → child is unbudgeted too; the whole
222
+ * `/.meta/budget/*` overlay is skipped (zero-overhead legacy path).
223
+ *
224
+ * The clamp is only a ceiling. Actual spend is still accounted per call
225
+ * via the exec middleware (`afs.exec` → decrement under `rootSession`
226
+ * mutex), so siblings can't double-spend the parent's remaining window.
227
+ */
228
+ function deriveChildBudgetState(parent, childBudget) {
229
+ if (!parent) return void 0;
230
+ const resolved = resolveBudget({
231
+ manifest: childBudget,
232
+ parentRemaining: budgetRemaining(parent)
233
+ });
234
+ return createBudgetState({
235
+ rootSession: parent.rootSession,
236
+ limit: resolved.totalTokens
237
+ });
238
+ }
239
+ function assertSession(session) {
240
+ if (session === void 0) return;
241
+ if (typeof session !== "string" || session.length === 0) throw new Error(`SCOPE_INVALID_INPUT: session must be a non-empty string`);
242
+ if (session.includes("/") || session.includes("\\")) throw new Error(`SCOPE_INVALID_INPUT: session must not contain path separators`);
243
+ if (session.includes("\0")) throw new Error(`SCOPE_INVALID_INPUT: session must not contain null bytes`);
244
+ }
245
+ function assertMemberPath(memberPath) {
246
+ if (typeof memberPath !== "string" || memberPath.length === 0) throw new Error(`SCOPE_INVALID_MEMBER: memberPath must be a non-empty string`);
247
+ if (memberPath.includes("\0")) throw new Error(`SCOPE_INVALID_MEMBER: memberPath must not contain null bytes`);
248
+ if (memberPath.split("/").some((s) => s === "..")) throw new Error(`SCOPE_INVALID_MEMBER: memberPath must not traverse parent directories`);
249
+ }
250
+ function assertChildManifest(manifest) {
251
+ if (!manifest || typeof manifest !== "object") throw new Error(`SCOPE_INVALID_INPUT: childManifest is required`);
252
+ }
253
+ /**
254
+ * Normalize a manifest's `tools` field into `ToolEntry[]`, rejecting shapes
255
+ * that would let a dispatcher smuggle traversal or typo past intersection:
256
+ * - non-string / empty path → TOOL_PATH_INVALID
257
+ * - path containing `..` or `\0` → TOOL_PATH_INVALID
258
+ * - ops must be a non-empty string[] when provided
259
+ *
260
+ * The Phase 1 contract: one call site (projectForChild) owns normalization;
261
+ * older runner-local normalize helpers delegate here.
262
+ */
263
+ function normalizeManifestTools(manifest) {
264
+ const raw = manifest.tools ?? [];
265
+ const result = [];
266
+ raw.forEach((t, idx) => {
267
+ const path = typeof t === "string" ? t : t?.path;
268
+ if (typeof path !== "string" || path.length === 0) throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].path must be a non-empty string`);
269
+ if (path.includes("\0")) throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].path must not contain null bytes`);
270
+ if (path.includes("..")) throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].path must not contain ".."`);
271
+ if (typeof t === "string") {
272
+ result.push({
273
+ path,
274
+ ops: [...DEFAULT_TOOL_OPS]
275
+ });
276
+ return;
277
+ }
278
+ const ops = t.ops ?? [...DEFAULT_TOOL_OPS];
279
+ if (!Array.isArray(ops) || ops.length === 0 || ops.some((op) => typeof op !== "string")) throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].ops must be a non-empty string[]`);
280
+ const entry = {
281
+ path,
282
+ ops: withQueryPairing(ops)
283
+ };
284
+ if (t.maxDepth != null) entry.maxDepth = t.maxDepth;
285
+ result.push(entry);
286
+ });
287
+ return result;
288
+ }
289
+ /**
290
+ * Phase 5 canonical session accessor — runners must read the effective
291
+ * session id from `ctx.afs.scope.session` rather than from the legacy
292
+ * `context.session` field. This helper keeps back-compat while the four
293
+ * runners are swapped: if the AFS already carries a scope, return
294
+ * `scope.session`; otherwise fall back to the deprecated `fallback`
295
+ * argument. P6 removes the fallback path once `RunnerContext.session` is
296
+ * deleted.
297
+ */
298
+ function runnerSession(afs, fallback) {
299
+ return afs.scope?.session ?? fallback;
300
+ }
301
+ /**
302
+ * Phase 5 canonical parent-session accessor. Mirrors `runnerSession` but
303
+ * surfaces the parent's session id so runners can tag sub-runs without
304
+ * consulting the legacy `context.parentSession` field. Returns undefined
305
+ * for root scopes (no parent) — callers must treat undefined as "root".
306
+ */
307
+ function runnerParentSession(afs, fallback) {
308
+ return afs.scope?.parentSession ?? fallback;
309
+ }
310
+ /**
311
+ * Shared session-resolution ladder for every runner. Reads session and
312
+ * parentSession from scope first (legacy context fields only as fallback
313
+ * until P6) and, when both are set, derives the child session id.
314
+ *
315
+ * Collapses the ai / ash / team / orchestrator entry blocks into one
316
+ * place — any adjustment to the derivation rule now has a single owner.
317
+ */
318
+ function resolveChildSession(afs, ctx, deriveChildSession) {
319
+ const parentSession = runnerParentSession(afs, ctx.parentSession);
320
+ let session = runnerSession(afs, ctx.session);
321
+ if (parentSession && session) session = deriveChildSession(parentSession);
322
+ return {
323
+ session,
324
+ parentSession
325
+ };
326
+ }
327
+ /**
328
+ * Lift a raw `AFSRoot` into a root-level `AgentScope`. Root scopes are
329
+ * unbounded by construction — `scope.allowedTools` is the caller's own
330
+ * manifest tools (or fully permissive when omitted), and `chainDepth` is 0.
331
+ *
332
+ * Top-level runners (agent-run action handler, test harnesses) use this to
333
+ * bootstrap the scope chain; downstream runners never construct a root
334
+ * scope themselves. Omit `manifestTools` (or pass `[]`) to keep the root
335
+ * fully permissive — useful for tests that don't declare tools.
336
+ */
337
+ function rootAgentScope(afs, opts) {
338
+ assertSession(opts.session);
339
+ const allowedTools = opts.manifestTools && opts.manifestTools.length > 0 ? opts.manifestTools : [{
340
+ path: "/**",
341
+ ops: [
342
+ "read",
343
+ "list",
344
+ "exec",
345
+ "write",
346
+ "search",
347
+ "query"
348
+ ]
349
+ }];
350
+ let withBudget = afs;
351
+ let budgetState;
352
+ if (opts.budget) {
353
+ const resolved = resolveBudget({ manifest: opts.budget });
354
+ budgetState = createBudgetState({
355
+ rootSession: opts.session,
356
+ limit: resolved.totalTokens
357
+ });
358
+ withBudget = attachBudgetMount(afs, budgetState);
359
+ }
360
+ return attachScope(withBudget, {
361
+ session: opts.session,
362
+ rootSession: opts.session,
363
+ chainDepth: 0,
364
+ allowedPaths: allowedTools.map((t) => t.path),
365
+ allowedTools,
366
+ budget: budgetState,
367
+ memoryDomain: opts.memoryDomain
368
+ });
369
+ }
370
+ /**
371
+ * Top-level runner bootstrap — if the caller's AFS is already a scope, use
372
+ * it verbatim; otherwise synthesize a root scope and attach a fresh
373
+ * `/scratch/*` mount. Team and orchestrator runners call this so hook
374
+ * events and `state.yml` writes land in the scope-local scratch rather
375
+ * than the legacy `/modules/memory/runs/` tree.
376
+ *
377
+ * Pass `manifestTools` + `session` to drive the synthetic root scope's
378
+ * descriptor; the caller's own manifest is typically the source, and
379
+ * `scratchRetention` follows `manifest.scratch` (run → session default).
380
+ */
381
+ function ensureScopeWithMounts(afs, opts) {
382
+ if (afs.scope) return afs;
383
+ return attachScratchMount(rootAgentScope(afs, {
384
+ session: opts.session,
385
+ manifestTools: opts.manifestTools,
386
+ budget: opts.budget,
387
+ memoryDomain: extractManifestDomain(opts.memory)
388
+ }), opts.scratchProvider ?? createScratchProvider({
389
+ session: sanitizeSessionForScratch(opts.session),
390
+ retention: opts.scratchRetention ?? "session"
391
+ }));
392
+ }
393
+ /**
394
+ * Resolve the `AgentScope` a parent runner projects children from.
395
+ *
396
+ * Phase 6 — if the caller already handed us a scoped AFS (via
397
+ * `projectForChild` / `ensureScopeWithMounts`), reuse it: its `allowedTools`
398
+ * is already the post-intersection view. Otherwise synthesize a root scope
399
+ * from the caller's `manifest.tools`, normalized via `normalizeManifestTools`
400
+ * for a single source of truth. Callers that want a tighter inherited
401
+ * set must hand over a scoped AFS built via `projectForChild` or
402
+ * `ensureScopeWithMounts`.
403
+ */
404
+ function resolveParentAgentScope(opts) {
405
+ if (opts.afs.scope) return opts.afs;
406
+ const manifestTools = normalizeManifestTools(opts.manifest);
407
+ return rootAgentScope(opts.afs, {
408
+ session: opts.session ?? "root",
409
+ manifestTools: manifestTools.length > 0 ? manifestTools : void 0
410
+ });
411
+ }
412
+ function attachScope(afs, scope) {
413
+ const child = Object.create(afs);
414
+ Object.defineProperty(child, "scope", {
415
+ value: scope,
416
+ enumerable: true,
417
+ writable: false,
418
+ configurable: false
419
+ });
420
+ return child;
421
+ }
422
+ /**
423
+ * Narrow down `parent` for a child agent:
424
+ * - path projection via existing `projectSubAgent` (rewrite + readonly)
425
+ * - tool ACL intersection via existing `intersectTools` (paths + ops)
426
+ * - scope descriptor: session / rootSession / chainDepth+1 / allowedPaths
427
+ *
428
+ * If the parent has a non-empty `scope.allowedTools` and the child declares
429
+ * a non-empty `manifest.tools` that doesn't overlap (intersection empty),
430
+ * we throw `TOOL_INTERSECTION_EMPTY` — an empty intersection means the
431
+ * dispatcher has asked for an unreachable child scope, which is a hard
432
+ * error rather than a silently-permissive default.
433
+ *
434
+ * See design.md §5 for the full six-step mount composition; P2–P5 add
435
+ * scratch / memory / budget / session arms to the same function.
436
+ */
437
+ function projectForChild(parent, options) {
438
+ assertChildManifest(options.childManifest);
439
+ assertMemberPath(options.memberPath);
440
+ assertSession(options.session);
441
+ if (!parent || typeof parent !== "object" || !parent.scope) throw new Error(`SCOPE_INVALID_INPUT: parent scope is required`);
442
+ const nextDepth = parent.scope.chainDepth + 1;
443
+ if (nextDepth >= CHAIN_DEPTH_HARD_CAP) throw new Error(`CHAIN_DEPTH_EXCEEDED: chainDepth ${nextDepth} reaches hard cap ${CHAIN_DEPTH_HARD_CAP}`);
444
+ const childDeclaredTools = normalizeManifestTools(options.childManifest);
445
+ const parentAllowed = parent.scope.allowedTools;
446
+ const intersected = childDeclaredTools.length === 0 ? [] : intersectTools([...parentAllowed], childDeclaredTools);
447
+ if (childDeclaredTools.length > 0 && intersected.length === 0) throw new Error(`TOOL_INTERSECTION_EMPTY: child manifest declares tools that don't intersect parent scope`);
448
+ const withMessaging = ensureMessagingTool(intersected);
449
+ const projectedAfs = projectSubAgent(parent, {
450
+ memberPath: options.memberPath,
451
+ mode: "readonly"
452
+ });
453
+ const childSession = options.session ?? deriveDefaultChildSession(parent.scope.session, options.memberPath);
454
+ const withScratch = attachScratchMount(projectedAfs, createScratchProvider({
455
+ session: sanitizeSessionForScratch(childSession),
456
+ retention: options.childManifest.scratch ? "session" : "run"
457
+ }));
458
+ const memoryDomain = isMemoryEnabled(options.childManifest.memory) ? resolveMemoryDomain({
459
+ manifestMemory: options.childManifest.memory,
460
+ parentDomain: parent.scope.memoryDomain,
461
+ memberPath: options.memberPath
462
+ }) : void 0;
463
+ const inheritsParentMemoryMount = memoryDomain !== void 0 && memoryDomain === parent.scope.memoryDomain;
464
+ const withMemory = memoryDomain && !inheritsParentMemoryMount ? attachMemoryMount(withScratch, createMemoryProvider({
465
+ domain: memoryDomain,
466
+ storage: buildMemoryStorageForAfs(parent),
467
+ ...buildMemoryIndexOptsForAfs(parent)
468
+ })) : withScratch;
469
+ const finalTools = memoryDomain ? ensureMemoryTool(withMessaging) : withMessaging;
470
+ const childBudget = deriveChildBudgetState(parent.scope.budget, options.childManifest.budget);
471
+ return attachScope(childBudget ? attachBudgetMount(withMemory, childBudget) : withMemory, {
472
+ session: childSession,
473
+ parentSession: parent.scope.session,
474
+ rootSession: parent.scope.rootSession,
475
+ chainDepth: nextDepth,
476
+ allowedPaths: finalTools.map((t) => t.path),
477
+ allowedTools: finalTools,
478
+ memoryDomain,
479
+ budget: childBudget
480
+ });
481
+ }
482
+ /** `memory: true` | `memory: { … }` both count; `false`/`undefined` do not. */
483
+ function isMemoryEnabled(value) {
484
+ return value === true || typeof value === "object" && value !== null;
485
+ }
486
+ const MEMORY_TOOL_PATH = "/memory/**";
487
+ function ensureMemoryTool(tools) {
488
+ if (tools.some((t) => t.path === MEMORY_TOOL_PATH || t.path === "/memory")) return tools;
489
+ return [...tools, {
490
+ path: MEMORY_TOOL_PATH,
491
+ ops: [
492
+ "read",
493
+ "list",
494
+ "exec"
495
+ ]
496
+ }];
497
+ }
498
+ /**
499
+ * The scope descriptor's `session` may come from callers that tolerate path
500
+ * segments (e.g. `.../planner-0`). The scratch provider rejects separators
501
+ * and dots, so we collapse the caller-supplied id down to a bare token
502
+ * before handing it over.
503
+ */
504
+ function sanitizeSessionForScratch(session) {
505
+ const trimmed = (session ?? "").replace(/\/+$/, "");
506
+ const cleaned = trimmed.slice(trimmed.lastIndexOf("/") + 1).replace(/\.\./g, "").replace(/\0/g, "");
507
+ return cleaned.length > 0 ? cleaned : "scope";
508
+ }
509
+ function ensureMessagingTool(tools) {
510
+ if (tools.some((t) => t.path === MESSAGING_TOOL_PATH || t.path === MESSAGING_TOOLS_ROOT)) return tools;
511
+ return [...tools, {
512
+ path: MESSAGING_TOOL_PATH,
513
+ ops: ["exec"]
514
+ }];
515
+ }
516
+ function deriveDefaultChildSession(parentSession, memberPath) {
517
+ return `${parentSession}.${memberPath.split("/").filter(Boolean).pop() ?? "child"}.${Math.random().toString(36).slice(2, 8)}`;
518
+ }
519
+ /**
520
+ * Phase 2 target — serialize a scope for cross-process hand-off (MCP
521
+ * sandbox worker, code-agent runner, …). Always throws in Phase 0/1 so
522
+ * no caller is tempted to persist a partial descriptor before the sandbox
523
+ * boundary exists.
524
+ */
525
+ function toJSON(_scope) {
526
+ throw new Error("AGENT_SCOPE_TOJSON_PHASE_2: scope serialization lands with the MCP sandbox (Phase 2)");
527
+ }
528
+
529
+ //#endregion
530
+ export { CHAIN_DEPTH_HARD_CAP, ensureScopeWithMounts, normalizeManifestTools, projectForChild, resolveChildSession, resolveParentAgentScope, rootAgentScope, runnerSession, toJSON };
531
+ //# sourceMappingURL=scoped-afs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-afs.mjs","names":[],"sources":["../../src/program/scoped-afs.ts"],"sourcesContent":["/**\n * AgentScope — the per-agent AFS view that replaces the earlier grab-bag\n * of ad-hoc fields on `RunnerContext`. It carries tool whitelist, memory\n * domain, budget, spawn-chain depth, and session identity in one place.\n *\n * Phase 0 defined the contract; Phase 1 arms the **tools** projection:\n * `projectForChild` computes the child's `scope.allowedPaths` as the\n * intersection of `parent.scope.allowedTools` and `childManifest.tools`,\n * and also carries the full `ToolEntry[]` (with ops/maxDepth) in\n * `scope.allowedTools` so runners surface a correctly narrowed tool\n * catalog to the LLM directly from the scope. The memory / scratch /\n * budget / session mount arms land in P2–P5.\n *\n * Naming note: this file intentionally does NOT reuse the existing\n * `ScopedAFS` class in `@aigne/afs/blocklet` (that class is the path-prefix\n * proxy for blocklet-local AFS views). `AgentScope` is a richer structure\n * that layers scratch / memory / budget / tool-projection on top of a\n * base scope; P1+ phases compose them.\n *\n * HARD CONSTRAINT — `projectForChild` options are closed at three fields:\n * { memberPath, childManifest, session? }\n * `tools`, `budget`, `scratch`, `memory`, `budgetRemaining` etc. are NEVER\n * accepted as options. The implementation must derive them from\n * `childManifest` + parent scope state (including `afs.read(\"/.meta/budget/remaining\")`\n * in Phase 4). A dispatcher that hands `tools: [\"**\"]` or a wider budget\n * must be rejected in code review — the options type shape is closed.\n */\n\nimport type { AFSModule, AFSRoot, AgentManifest } from \"@aigne/afs\";\nimport { projectSubAgent } from \"@aigne/afs\";\nimport { intersectTools, type ToolEntry, withQueryPairing } from \"@aigne/afs-ash\";\nimport { type BudgetConfig, resolveBudget } from \"../agent-runners/budget.js\";\nimport {\n attachMemoryMount,\n buildMemoryIndexOptsForAfs,\n buildMemoryStorageForAfs,\n createMemoryProvider,\n extractManifestDomain,\n resolveMemoryDomain,\n} from \"../agent-runners/memory-afs.js\";\nimport {\n attachScratchMount,\n createScratchProvider,\n type ScratchProvider,\n type ScratchRetention,\n} from \"../agent-runners/scratchpad-afs.js\";\nimport { MESSAGING_TOOLS_GLOB, MESSAGING_TOOLS_ROOT } from \"./agent-messages-tools.js\";\n\n/**\n * Chain-depth hard cap. Mirrors `HARD_CAP` in\n * `providers/basic/ash/src/spawn-depth.ts`; kept in sync manually because\n * importing across package boundaries for a constant isn't worth the\n * dependency. Any change there must be reflected here.\n *\n * Semantics match `spawn-depth.ts`: a child whose post-increment depth\n * would be **>= HARD_CAP** is rejected with `CHAIN_DEPTH_EXCEEDED`. With\n * root.chainDepth = 0 this means the deepest reachable child is depth 9;\n * attempting to spawn depth 10 throws.\n */\nexport const CHAIN_DEPTH_HARD_CAP = 10;\n\n/** Default ops applied when a manifest tool entry omits `ops`. */\n// `query` pairs with `list` (read-class collection view, design §3.1b).\nconst DEFAULT_TOOL_OPS: readonly string[] = [\"read\", \"list\", \"exec\", \"query\"];\n\n/**\n * Built-in messaging tool path — every scope surfaces\n * `/proc/messaging/tools/*`\n * so the LLM can call `agent_post_message` / `agent_read_messages` without\n * each manifest having to declare them. See B32 in\n * `providers/basic/ash/src/agent-run.ts`.\n */\nconst MESSAGING_TOOL_PATH = MESSAGING_TOOLS_GLOB;\n\n/**\n * Budget mount — per-scope `/.meta/budget/{limit,remaining,consumed}` state\n * files driven by the exec middleware in `attachBudgetMount`. Path contract:\n *\n * - `read(\"/.meta/budget/limit\")` → configured ceiling (tokens)\n * - `read(\"/.meta/budget/remaining\")` → `max(0, limit - consumed)`\n * - `read(\"/.meta/budget/consumed\")` → tokens decremented so far\n * - `write(\"/.meta/budget/*\", ...)` → always `PERMISSION_DENIED`; only\n * the middleware may mutate state (via the internal `BudgetState` ref).\n * - `list(\"/.meta/budget\")` → three-entry listing (above keys).\n *\n * The exec middleware wraps the underlying `afs.exec`:\n * 1. Pre-check `consumed < limit`; otherwise throw `BUDGET_EXHAUSTED`.\n * 2. Call underlying exec.\n * 3. Extract `tokens_used` from the result and serialize the increment\n * against the rootSession mutex so concurrent sub-runs don't race.\n *\n * Child scopes created by `projectForChild` inherit via a new `BudgetState`\n * whose `parent` points back to the caller's state. Each scope's middleware\n * only decrements its *own* `consumed` — propagation to ancestors happens\n * naturally because `child.exec` delegates to `parent.exec` through the\n * prototype chain, so every ancestor's middleware runs once per call and\n * the rootSession mutex keeps the sequence of increments linear.\n */\nconst BUDGET_MOUNT_ROOT = \"/.meta/budget\";\nconst BUDGET_KEY_LIMIT = \"limit\";\nconst BUDGET_KEY_REMAINING = \"remaining\";\nconst BUDGET_KEY_CONSUMED = \"consumed\";\nconst BUDGET_KEYS = [BUDGET_KEY_LIMIT, BUDGET_KEY_REMAINING, BUDGET_KEY_CONSUMED] as const;\ntype BudgetKey = (typeof BUDGET_KEYS)[number];\n\n/**\n * Process-local mutex map keyed on `rootSession`. Every decrement for a\n * given scope tree serializes against the same promise chain, so two\n * concurrent sub-runs sharing a rootSession cannot double-count or see a\n * mid-update view of `consumed`. Phase 2 will swap the Map for an AFS KV\n * provider so the guarantee holds across processes; the API shape stays.\n */\nconst rootBudgetLocks = new Map<string, Promise<unknown>>();\n\nasync function withRootBudgetLock<T>(rootSession: string, fn: () => Promise<T> | T): Promise<T> {\n const prev = rootBudgetLocks.get(rootSession) ?? Promise.resolve();\n // `prev.then(fn, fn)` chains regardless of whether the previous decrement\n // settled cleanly — so one failing fn doesn't poison the rest of the\n // chain (subsequent decrements still run). `stored` is the exact promise\n // identity we put into the map; the `finally` below uses it to detect\n // whether a later caller has already queued behind us.\n const stored: Promise<unknown> = prev.then(fn, fn);\n rootBudgetLocks.set(rootSession, stored);\n try {\n return (await stored) as T;\n } finally {\n if (rootBudgetLocks.get(rootSession) === stored) {\n rootBudgetLocks.delete(rootSession);\n }\n }\n}\n\n/**\n * Mutable budget accounting backing a single scope's `/.meta/budget/*`\n * mount and its exec middleware. `rootSession` is the mutex key shared by\n * every scope in one spawn chain; ancestor aggregation happens naturally\n * because `child.exec` delegates to `parent.exec` through the prototype\n * chain, so each ancestor's middleware runs once per call and applies its\n * own increment — no parent pointer needed.\n */\nexport interface BudgetState {\n readonly rootSession: string;\n readonly limit: number;\n consumed: number;\n}\n\nexport interface BudgetStateInit {\n readonly rootSession: string;\n readonly limit: number;\n}\n\nexport function createBudgetState(init: BudgetStateInit): BudgetState {\n if (!init.rootSession || typeof init.rootSession !== \"string\") {\n throw new Error(`BUDGET_INVALID: rootSession must be a non-empty string`);\n }\n if (typeof init.limit !== \"number\" || Number.isNaN(init.limit) || init.limit < 0) {\n throw new Error(`BUDGET_INVALID: limit must be a non-negative number`);\n }\n return {\n rootSession: init.rootSession,\n limit: Math.floor(init.limit),\n consumed: 0,\n };\n}\n\nfunction budgetRemaining(state: BudgetState): number {\n return Math.max(0, state.limit - state.consumed);\n}\n\nfunction isBudgetPath(path: string): boolean {\n return path === BUDGET_MOUNT_ROOT || path.startsWith(`${BUDGET_MOUNT_ROOT}/`);\n}\n\nfunction parseBudgetKey(path: string): BudgetKey | null {\n if (!path.startsWith(`${BUDGET_MOUNT_ROOT}/`)) return null;\n const key = path.slice(BUDGET_MOUNT_ROOT.length + 1);\n return (BUDGET_KEYS as readonly string[]).includes(key) ? (key as BudgetKey) : null;\n}\n\nfunction budgetValue(state: BudgetState, key: BudgetKey): number {\n if (key === BUDGET_KEY_LIMIT) return state.limit;\n if (key === BUDGET_KEY_CONSUMED) return state.consumed;\n return budgetRemaining(state);\n}\n\n/**\n * Extract `tokens_used` from an exec result. Supports both legacy\n * `data.total_tokens` (the agent-run envelope) and `data._meta.tokens_used`\n * (team/orchestrator aggregate meta). Anything else → 0.\n */\nfunction extractTokensUsed(result: unknown): number {\n const data = (result as { data?: unknown } | undefined)?.data;\n if (!data || typeof data !== \"object\") return 0;\n const candidates = [\n (data as { total_tokens?: unknown }).total_tokens,\n (data as { _meta?: { tokens_used?: unknown } })._meta?.tokens_used,\n ];\n for (const value of candidates) {\n if (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\n return Math.floor(value);\n }\n }\n return 0;\n}\n\n/**\n * Overlay the budget mount on an AFS root:\n * - `/.meta/budget/{limit,remaining,consumed}` → live reads\n * - `/.meta/budget/*` writes → `PERMISSION_DENIED`\n * - `exec(...)` → pre-check `BUDGET_EXHAUSTED`, then decrement by\n * `tokens_used` under the rootSession mutex.\n *\n * The overlay is non-enumerable (matches the scratch/memory pattern) and\n * transparent for non-budget paths, which fall through via the prototype\n * chain. Every projected child attaches its own overlay backed by its own\n * `BudgetState`, so sibling scopes never see each other's `consumed`.\n */\nexport function attachBudgetMount<T extends AFSRoot>(afs: T, state: BudgetState): T {\n const child = Object.create(afs) as T;\n\n Object.defineProperty(child, \"read\", {\n value: async (path: string, options?: unknown) => {\n const key = parseBudgetKey(path);\n if (key) {\n return {\n success: true,\n data: { content: String(budgetValue(state, key)), path },\n } as never;\n }\n return (afs.read as never as (p: string, o?: unknown) => unknown).call(afs, path, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"write\", {\n value: async (path: string, payload: unknown, options?: unknown) => {\n if (isBudgetPath(path)) {\n const err = new Error(\n `PERMISSION_DENIED: ${path} is managed by the budget exec middleware and cannot be written directly`,\n );\n (err as { code?: string }).code = \"PERMISSION_DENIED\";\n throw err;\n }\n if (typeof afs.write !== \"function\") {\n throw new Error(`BUDGET_NO_UNDERLYING_WRITE: afs.write not available for ${path}`);\n }\n return (afs.write as never as (p: string, v: unknown, o?: unknown) => unknown).call(\n afs,\n path,\n payload,\n options,\n );\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"list\", {\n value: async (path: string, options?: unknown) => {\n if (path === BUDGET_MOUNT_ROOT || path === `${BUDGET_MOUNT_ROOT}/`) {\n return {\n success: true,\n data: {\n list: BUDGET_KEYS.map((name) => ({\n path: `${BUDGET_MOUNT_ROOT}/${name}`,\n name,\n type: \"file\",\n })),\n },\n } as never;\n }\n return (afs.list as never as (p: string, o?: unknown) => unknown).call(afs, path, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"exec\", {\n value: async (path: string, args: unknown, options: unknown) => {\n if (state.consumed >= state.limit) {\n const err = new Error(\n `BUDGET_EXHAUSTED: scope budget exhausted (path=${path}, limit=${state.limit}, consumed=${state.consumed})`,\n );\n (err as { code?: string }).code = \"BUDGET_EXHAUSTED\";\n throw err;\n }\n const result = await (\n afs.exec as never as (p: string, a: unknown, o: unknown) => Promise<unknown>\n ).call(afs, path, args, options);\n const used = extractTokensUsed(result);\n if (used > 0) {\n await withRootBudgetLock(state.rootSession, () => {\n state.consumed += used;\n });\n }\n return result as never;\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n return child;\n}\n\n/**\n * Resolve a child scope's budget. The child's effective limit is the\n * result of `resolveBudget({manifest, parentRemaining})`; consumed starts\n * at 0. Ancestor updates are handled by the middleware chain itself\n * (each scope's middleware runs once per call via the prototype chain),\n * so the child's `BudgetState` is independent from the parent's.\n *\n * Returns `undefined` when the parent has no active budget — zero-overhead\n * path for legacy callers that never opt in.\n *\n * Inheritance rules (P5 deferred nit documented here for clarity):\n *\n * - Parent has budget, child manifest declares one → `resolveBudget`\n * clamps `min(childBudget.totalTokens, parentRemaining)`.\n * - Parent has budget, child manifest omits budget → `resolveBudget` uses\n * `DEFAULT_TOTAL_TOKENS` (80k) then clamps to `parentRemaining`. Net\n * effect: an undeclared child never exceeds the parent's remaining\n * window, which is the conservative default.\n * - Parent has no budget → child is unbudgeted too; the whole\n * `/.meta/budget/*` overlay is skipped (zero-overhead legacy path).\n *\n * The clamp is only a ceiling. Actual spend is still accounted per call\n * via the exec middleware (`afs.exec` → decrement under `rootSession`\n * mutex), so siblings can't double-spend the parent's remaining window.\n */\nfunction deriveChildBudgetState(\n parent: BudgetState | undefined,\n childBudget: BudgetConfig | undefined,\n): BudgetState | undefined {\n if (!parent) return undefined;\n const resolved = resolveBudget({\n manifest: childBudget,\n parentRemaining: budgetRemaining(parent),\n });\n return createBudgetState({\n rootSession: parent.rootSession,\n limit: resolved.totalTokens,\n });\n}\n\n/**\n * Per-scope descriptor threaded through every AFS call a runner makes.\n *\n * - `session` — this scope's session id (not the underlying WS session)\n * - `parentSession` — the session that spawned us (used for event aggregation)\n * - `rootSession` — the session at the root of the spawn chain. All children\n * of the same root share this value; P4's budget exec middleware uses it\n * as the mutex key so concurrent sub-runs serialize correctly against the\n * same budget file. Root scope: rootSession === session.\n * - `chainDepth` — distance from the root scope (root = 0). Enforced against\n * `CHAIN_DEPTH_HARD_CAP` on every `projectForChild` call.\n * - `allowedPaths` — paths visible inside this scope. Populated from the\n * intersection of the parent's `allowedTools` and the child manifest's\n * declared tools (P1).\n * - `allowedTools` — intersected `ToolEntry[]` (paths + ops + maxDepth +\n * exclude_actions). Carried alongside `allowedPaths` because runners\n * need the ops metadata to build the LLM tool catalog; the `.actions/`\n * mount in later phases will let consumers list tools directly from the\n * scope and this field will become a cache.\n */\nexport interface AgentScopeDescriptorFields {\n readonly session: string;\n readonly parentSession?: string;\n readonly rootSession: string;\n readonly chainDepth: number;\n readonly allowedPaths: readonly string[];\n readonly allowedTools: readonly ToolEntry[];\n /**\n * Tenant string that the scope's `/memory/` mount was instantiated with.\n * Absent on scopes where `manifest.memory` is falsy — child scopes inherit\n * this value when they declare `memory: true` without an explicit domain.\n * See `docs/architecture/agent-memory.md` §3.3 for the precedence ladder.\n */\n readonly memoryDomain?: string;\n /**\n * Live budget state backing this scope's `/.meta/budget/*` mount. Absent\n * on scopes that never opted into budget tracking (legacy callers that\n * synthesize a scope without declaring a limit). When present, the exec\n * middleware reads/writes this object under the rootSession mutex;\n * children created via `projectForChild` inherit a new state whose\n * `parent` pointer links here so decrements walk up the chain.\n */\n readonly budget?: BudgetState;\n}\n\n/**\n * A scoped AFS view handed to an agent runner. Same shape as `AFSRoot`\n * (list/read/write/exec/…) with an extra `scope` descriptor.\n *\n * Runners read session / chain / permissions from `scope` rather than\n * from their own arguments. The five sibling phases (P1 tools, P2 scratch,\n * P3 memory, P4 budget, P5 session) wire mounts into the scope so the\n * runner never needs provider-specific fields on its context.\n */\nexport interface AgentScope extends AFSRoot {\n readonly scope: AgentScopeDescriptorFields;\n}\n\n/**\n * Options accepted by `projectForChild`.\n *\n * **Closed at 3 fields** — permission-widening fields (tools / budget /\n * scratch / memory / budgetRemaining / …) are intentionally absent. The\n * implementation must derive those from `childManifest` and the parent\n * scope. Extending this type to smuggle in a wider budget or tool set\n * defeats the whole point of scope-based projection.\n */\nexport interface ProjectForChildOptions {\n /** Required — where the child agent lives in the parent's mount tree (e.g. \"members/reviewer\"). */\n readonly memberPath: string;\n /** Required — scratch/memory/tools/budget are all derived from this. */\n readonly childManifest: AgentManifest;\n /**\n * Optional — if omitted, the runtime generates\n * `${parent.scope.session}.${memberName}.${counter}`. Must not contain\n * path separators, null bytes, or be empty.\n */\n readonly session?: string;\n}\n\n/**\n * Phase 2 sandbox serialization target. When the runtime needs to hand a\n * scope to an out-of-process worker (code agent, sandboxed MCP server, …),\n * it serializes to this descriptor and the far side rebuilds an AgentScope\n * backed by an MCP bridge.\n *\n * Phase 0 only defines the shape — `toJSON()` throws until Phase 2 lands.\n */\nexport interface AgentScopeDescriptor {\n readonly kind: \"agent-scope\";\n readonly session: string;\n readonly parentSession?: string;\n readonly rootSession: string;\n readonly chainDepth: number;\n readonly allowedPaths: readonly string[];\n readonly mountDescriptors: ReadonlyArray<{\n readonly path: string;\n readonly providerDescriptor: unknown;\n }>;\n}\n\nfunction assertSession(session: string | undefined): void {\n if (session === undefined) return;\n if (typeof session !== \"string\" || session.length === 0) {\n throw new Error(`SCOPE_INVALID_INPUT: session must be a non-empty string`);\n }\n if (session.includes(\"/\") || session.includes(\"\\\\\")) {\n throw new Error(`SCOPE_INVALID_INPUT: session must not contain path separators`);\n }\n if (session.includes(\"\\0\")) {\n throw new Error(`SCOPE_INVALID_INPUT: session must not contain null bytes`);\n }\n}\n\nfunction assertMemberPath(memberPath: unknown): asserts memberPath is string {\n if (typeof memberPath !== \"string\" || memberPath.length === 0) {\n throw new Error(`SCOPE_INVALID_MEMBER: memberPath must be a non-empty string`);\n }\n if (memberPath.includes(\"\\0\")) {\n throw new Error(`SCOPE_INVALID_MEMBER: memberPath must not contain null bytes`);\n }\n const segments = memberPath.split(\"/\");\n if (segments.some((s) => s === \"..\")) {\n throw new Error(`SCOPE_INVALID_MEMBER: memberPath must not traverse parent directories`);\n }\n}\n\nfunction assertChildManifest(manifest: unknown): asserts manifest is AgentManifest {\n if (!manifest || typeof manifest !== \"object\") {\n throw new Error(`SCOPE_INVALID_INPUT: childManifest is required`);\n }\n}\n\n/**\n * Normalize a manifest's `tools` field into `ToolEntry[]`, rejecting shapes\n * that would let a dispatcher smuggle traversal or typo past intersection:\n * - non-string / empty path → TOOL_PATH_INVALID\n * - path containing `..` or `\\0` → TOOL_PATH_INVALID\n * - ops must be a non-empty string[] when provided\n *\n * The Phase 1 contract: one call site (projectForChild) owns normalization;\n * older runner-local normalize helpers delegate here.\n */\nexport function normalizeManifestTools(manifest: { tools?: AgentManifest[\"tools\"] }): ToolEntry[] {\n const raw = manifest.tools ?? [];\n const result: ToolEntry[] = [];\n raw.forEach((t, idx) => {\n const path = typeof t === \"string\" ? t : t?.path;\n if (typeof path !== \"string\" || path.length === 0) {\n throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].path must be a non-empty string`);\n }\n if (path.includes(\"\\0\")) {\n throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].path must not contain null bytes`);\n }\n if (path.includes(\"..\")) {\n throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].path must not contain \"..\"`);\n }\n if (typeof t === \"string\") {\n result.push({ path, ops: [...DEFAULT_TOOL_OPS] });\n return;\n }\n const ops = t.ops ?? [...DEFAULT_TOOL_OPS];\n if (!Array.isArray(ops) || ops.length === 0 || ops.some((op) => typeof op !== \"string\")) {\n throw new Error(`TOOL_PATH_INVALID: manifest.tools[${idx}].ops must be a non-empty string[]`);\n }\n // list grants pair with query (read-class collection view).\n const entry: ToolEntry = { path, ops: withQueryPairing(ops) };\n if (t.maxDepth != null) entry.maxDepth = t.maxDepth;\n result.push(entry);\n });\n return result;\n}\n\n/**\n * Phase 5 canonical session accessor — runners must read the effective\n * session id from `ctx.afs.scope.session` rather than from the legacy\n * `context.session` field. This helper keeps back-compat while the four\n * runners are swapped: if the AFS already carries a scope, return\n * `scope.session`; otherwise fall back to the deprecated `fallback`\n * argument. P6 removes the fallback path once `RunnerContext.session` is\n * deleted.\n */\nexport function runnerSession(afs: AFSRoot, fallback?: string): string | undefined {\n const scope = (afs as AgentScope).scope;\n return scope?.session ?? fallback;\n}\n\n/**\n * Phase 5 canonical parent-session accessor. Mirrors `runnerSession` but\n * surfaces the parent's session id so runners can tag sub-runs without\n * consulting the legacy `context.parentSession` field. Returns undefined\n * for root scopes (no parent) — callers must treat undefined as \"root\".\n */\nexport function runnerParentSession(afs: AFSRoot, fallback?: string): string | undefined {\n const scope = (afs as AgentScope).scope;\n return scope?.parentSession ?? fallback;\n}\n\n/**\n * Shared session-resolution ladder for every runner. Reads session and\n * parentSession from scope first (legacy context fields only as fallback\n * until P6) and, when both are set, derives the child session id.\n *\n * Collapses the ai / ash / team / orchestrator entry blocks into one\n * place — any adjustment to the derivation rule now has a single owner.\n */\nexport function resolveChildSession(\n afs: AFSRoot,\n ctx: { session?: string; parentSession?: string },\n deriveChildSession: (parent: string) => string,\n): { session: string | undefined; parentSession: string | undefined } {\n const parentSession = runnerParentSession(afs, ctx.parentSession);\n let session = runnerSession(afs, ctx.session);\n if (parentSession && session) session = deriveChildSession(parentSession);\n return { session, parentSession };\n}\n\n/**\n * Lift a raw `AFSRoot` into a root-level `AgentScope`. Root scopes are\n * unbounded by construction — `scope.allowedTools` is the caller's own\n * manifest tools (or fully permissive when omitted), and `chainDepth` is 0.\n *\n * Top-level runners (agent-run action handler, test harnesses) use this to\n * bootstrap the scope chain; downstream runners never construct a root\n * scope themselves. Omit `manifestTools` (or pass `[]`) to keep the root\n * fully permissive — useful for tests that don't declare tools.\n */\nexport function rootAgentScope(\n afs: AFSRoot,\n opts: {\n session: string;\n manifestTools?: ToolEntry[];\n /**\n * Optional manifest budget config for the root scope. When set, the\n * returned scope carries a `/.meta/budget/*` mount and the exec\n * middleware; children projected from it inherit clamped budgets.\n * Omitted → scope is unbudgeted (legacy behaviour).\n */\n budget?: BudgetConfig;\n /**\n * Optional memory domain to stamp into the root scope descriptor.\n * Callers that pre-attached a `/memory/*` mount (e.g. team-runner when\n * the team manifest declares memory at the top level) pass the domain\n * so downstream `scope.memoryDomain` reads resolve authoritatively.\n * Omitted → scope carries no memory domain (legacy behaviour).\n */\n memoryDomain?: string;\n },\n): AgentScope {\n assertSession(opts.session);\n const allowedTools: ToolEntry[] =\n opts.manifestTools && opts.manifestTools.length > 0\n ? opts.manifestTools\n : [{ path: \"/**\", ops: [\"read\", \"list\", \"exec\", \"write\", \"search\", \"query\"] }];\n let withBudget: AFSRoot = afs;\n let budgetState: BudgetState | undefined;\n if (opts.budget) {\n const resolved = resolveBudget({ manifest: opts.budget });\n budgetState = createBudgetState({\n rootSession: opts.session,\n limit: resolved.totalTokens,\n });\n withBudget = attachBudgetMount(afs, budgetState);\n }\n return attachScope(withBudget, {\n session: opts.session,\n rootSession: opts.session,\n chainDepth: 0,\n allowedPaths: allowedTools.map((t) => t.path),\n allowedTools,\n budget: budgetState,\n memoryDomain: opts.memoryDomain,\n });\n}\n\n/**\n * Top-level runner bootstrap — if the caller's AFS is already a scope, use\n * it verbatim; otherwise synthesize a root scope and attach a fresh\n * `/scratch/*` mount. Team and orchestrator runners call this so hook\n * events and `state.yml` writes land in the scope-local scratch rather\n * than the legacy `/modules/memory/runs/` tree.\n *\n * Pass `manifestTools` + `session` to drive the synthetic root scope's\n * descriptor; the caller's own manifest is typically the source, and\n * `scratchRetention` follows `manifest.scratch` (run → session default).\n */\nexport function ensureScopeWithMounts(\n afs: AFSRoot,\n opts: {\n session: string;\n manifestTools?: ToolEntry[];\n scratchRetention?: ScratchRetention;\n scratchProvider?: ScratchProvider;\n /**\n * Manifest budget for the synthetic root scope. Forwarded to\n * `rootAgentScope` so the bootstrap scope carries the budget mount\n * and children projected from it inherit the clamp. Optional — when\n * unset, the scope stays unbudgeted (legacy behaviour).\n */\n budget?: BudgetConfig;\n /**\n * Top-level team's `manifest.memory`. When the team declares memory,\n * the synthetic root scope stamps `memoryDomain` so downstream G2\n * handoff read-through resolves authoritatively without re-reading\n * the manifest. Does NOT attach a memory mount — the caller is\n * expected to have one already (tests use `attachMemoryMount`;\n * production wires via the per-scope scaffolding).\n */\n memory?: unknown;\n },\n): AgentScope {\n const existing = (afs as AgentScope).scope;\n if (existing) return afs as AgentScope;\n const root = rootAgentScope(afs, {\n session: opts.session,\n manifestTools: opts.manifestTools,\n budget: opts.budget,\n memoryDomain: extractManifestDomain(opts.memory),\n });\n const provider =\n opts.scratchProvider ??\n createScratchProvider({\n session: sanitizeSessionForScratch(opts.session),\n retention: opts.scratchRetention ?? \"session\",\n });\n return attachScratchMount(root, provider);\n}\n\n/**\n * Resolve the `AgentScope` a parent runner projects children from.\n *\n * Phase 6 — if the caller already handed us a scoped AFS (via\n * `projectForChild` / `ensureScopeWithMounts`), reuse it: its `allowedTools`\n * is already the post-intersection view. Otherwise synthesize a root scope\n * from the caller's `manifest.tools`, normalized via `normalizeManifestTools`\n * for a single source of truth. Callers that want a tighter inherited\n * set must hand over a scoped AFS built via `projectForChild` or\n * `ensureScopeWithMounts`.\n */\nexport function resolveParentAgentScope(opts: {\n afs: AFSRoot;\n manifest: AgentManifest;\n session?: string;\n}): AgentScope {\n const existing = (opts.afs as AgentScope).scope;\n if (existing) return opts.afs as AgentScope;\n const manifestTools = normalizeManifestTools(opts.manifest);\n return rootAgentScope(opts.afs, {\n session: opts.session ?? \"root\",\n manifestTools: manifestTools.length > 0 ? manifestTools : undefined,\n });\n}\n\nfunction attachScope(afs: AFSRoot, scope: AgentScopeDescriptorFields): AgentScope {\n const child = Object.create(afs as object) as AgentScope;\n Object.defineProperty(child, \"scope\", {\n value: scope,\n enumerable: true,\n writable: false,\n configurable: false,\n });\n return child;\n}\n\n/**\n * Narrow down `parent` for a child agent:\n * - path projection via existing `projectSubAgent` (rewrite + readonly)\n * - tool ACL intersection via existing `intersectTools` (paths + ops)\n * - scope descriptor: session / rootSession / chainDepth+1 / allowedPaths\n *\n * If the parent has a non-empty `scope.allowedTools` and the child declares\n * a non-empty `manifest.tools` that doesn't overlap (intersection empty),\n * we throw `TOOL_INTERSECTION_EMPTY` — an empty intersection means the\n * dispatcher has asked for an unreachable child scope, which is a hard\n * error rather than a silently-permissive default.\n *\n * See design.md §5 for the full six-step mount composition; P2–P5 add\n * scratch / memory / budget / session arms to the same function.\n */\nexport function projectForChild(parent: AgentScope, options: ProjectForChildOptions): AgentScope {\n assertChildManifest(options.childManifest);\n assertMemberPath(options.memberPath);\n assertSession(options.session);\n\n if (!parent || typeof parent !== \"object\" || !parent.scope) {\n throw new Error(`SCOPE_INVALID_INPUT: parent scope is required`);\n }\n\n const nextDepth = parent.scope.chainDepth + 1;\n if (nextDepth >= CHAIN_DEPTH_HARD_CAP) {\n throw new Error(\n `CHAIN_DEPTH_EXCEEDED: chainDepth ${nextDepth} reaches hard cap ${CHAIN_DEPTH_HARD_CAP}`,\n );\n }\n\n const childDeclaredTools = normalizeManifestTools(options.childManifest);\n const parentAllowed = parent.scope.allowedTools;\n const intersected =\n childDeclaredTools.length === 0 ? [] : intersectTools([...parentAllowed], childDeclaredTools);\n\n if (childDeclaredTools.length > 0 && intersected.length === 0) {\n throw new Error(\n `TOOL_INTERSECTION_EMPTY: child manifest declares tools that don't intersect parent scope`,\n );\n }\n\n // Built-in `/proc/messaging/tools/*` is appended after intersection so that\n // even a child with an empty or fully-disjoint manifest.tools still has a\n // working `agent_post_message` / `agent_read_messages`. Parent cannot\n // forbid this channel — B32 semantics.\n const withMessaging = ensureMessagingTool(intersected);\n\n const projectedAfs = projectSubAgent(parent as unknown as AFSModule, {\n memberPath: options.memberPath,\n mode: \"readonly\",\n }) as unknown as AFSRoot;\n\n const childSession =\n options.session ?? deriveDefaultChildSession(parent.scope.session, options.memberPath);\n\n // Every child scope carries its own `/scratch/*` mount. Sub-agent writes to\n // `state.yml` / `hook-channel.jsonl` land in this in-memory store —\n // independent of the parent and of any sibling scope, so concurrent sub-runs\n // never stomp each other. Retention follows the child manifest's `scratch`\n // flag: declared-scratch agents default to `session` retention so the\n // scratch outlives a single run; plain sub-agents use `run` retention.\n const scratchProvider = createScratchProvider({\n session: sanitizeSessionForScratch(childSession),\n retention: options.childManifest.scratch ? \"session\" : \"run\",\n });\n const withScratch = attachScratchMount(projectedAfs, scratchProvider);\n\n // Memory arm — when the child manifest declares `memory: true` (or an\n // object override), mount a per-scope AFSMemory at `/memory/`. The\n // provider is instantiated with a fixed `defaultDomain`, so runtime arg\n // tampering can't read another tenant's entries. Child scopes inherit\n // the parent's domain by default; explicit `memory.domain` wins. Tools\n // automatically pick up `/memory/**` in the LLM catalog when memory is on.\n const memoryDomain = isMemoryEnabled(options.childManifest.memory)\n ? resolveMemoryDomain({\n manifestMemory: options.childManifest.memory,\n parentDomain: parent.scope.memoryDomain,\n memberPath: options.memberPath,\n })\n : undefined;\n // When the child inherits the parent's domain, reuse the parent's existing\n // `/memory/` mount via the prototype chain instead of layering a fresh\n // provider with its own storage. A new `createMemoryProvider` would\n // instantiate a fresh `InMemoryStorage`, so archive writes from the child\n // would never be visible to siblings, the team, or cross-run G2 recall.\n // Only attach a new mount when the child explicitly overrides the domain.\n const inheritsParentMemoryMount =\n memoryDomain !== undefined && memoryDomain === parent.scope.memoryDomain;\n const withMemory =\n memoryDomain && !inheritsParentMemoryMount\n ? attachMemoryMount(\n withScratch,\n createMemoryProvider({\n domain: memoryDomain,\n // Propagate DID Space storage so child scopes (different domain)\n // also persist to the caller's /user/memory space, and the same\n // per-caller index root so recall hits /user/index (not the global).\n storage: buildMemoryStorageForAfs(parent),\n ...buildMemoryIndexOptsForAfs(parent),\n }),\n )\n : withScratch;\n const finalTools = memoryDomain ? ensureMemoryTool(withMessaging) : withMessaging;\n\n // Budget arm — inherit when the parent has a live budget; clamp via\n // `resolveBudget`; wire a fresh `/.meta/budget/*` mount + middleware for\n // the child. Parents without a budget leave the child unbudgeted so\n // legacy scopes keep working.\n const childBudget = deriveChildBudgetState(parent.scope.budget, options.childManifest.budget);\n const withBudget = childBudget ? attachBudgetMount(withMemory, childBudget) : withMemory;\n\n return attachScope(withBudget, {\n session: childSession,\n parentSession: parent.scope.session,\n rootSession: parent.scope.rootSession,\n chainDepth: nextDepth,\n allowedPaths: finalTools.map((t) => t.path),\n allowedTools: finalTools,\n memoryDomain,\n budget: childBudget,\n });\n}\n\n/** `memory: true` | `memory: { … }` both count; `false`/`undefined` do not. */\nfunction isMemoryEnabled(value: unknown): boolean {\n return value === true || (typeof value === \"object\" && value !== null);\n}\n\nconst MEMORY_TOOL_PATH = \"/memory/**\";\n\nfunction ensureMemoryTool(tools: ToolEntry[]): ToolEntry[] {\n if (tools.some((t) => t.path === MEMORY_TOOL_PATH || t.path === \"/memory\")) {\n return tools;\n }\n return [...tools, { path: MEMORY_TOOL_PATH, ops: [\"read\", \"list\", \"exec\"] }];\n}\n\n/**\n * The scope descriptor's `session` may come from callers that tolerate path\n * segments (e.g. `.../planner-0`). The scratch provider rejects separators\n * and dots, so we collapse the caller-supplied id down to a bare token\n * before handing it over.\n */\nfunction sanitizeSessionForScratch(session: string): string {\n const trimmed = (session ?? \"\").replace(/\\/+$/, \"\");\n const tail = trimmed.slice(trimmed.lastIndexOf(\"/\") + 1);\n const cleaned = tail.replace(/\\.\\./g, \"\").replace(/\\0/g, \"\");\n return cleaned.length > 0 ? cleaned : \"scope\";\n}\n\nfunction ensureMessagingTool(tools: ToolEntry[]): ToolEntry[] {\n if (tools.some((t) => t.path === MESSAGING_TOOL_PATH || t.path === MESSAGING_TOOLS_ROOT)) {\n return tools;\n }\n return [...tools, { path: MESSAGING_TOOL_PATH, ops: [\"exec\"] }];\n}\n\nfunction deriveDefaultChildSession(parentSession: string, memberPath: string): string {\n const memberName = memberPath.split(\"/\").filter(Boolean).pop() ?? \"child\";\n // Stable-ish id: session.member.<random>. The numeric counter mentioned in\n // design.md becomes a fresh crypto segment here — good enough for dev/test;\n // P5 will tie this to real session hierarchy.\n const suffix = Math.random().toString(36).slice(2, 8);\n return `${parentSession}.${memberName}.${suffix}`;\n}\n\n/**\n * Phase 2 target — serialize a scope for cross-process hand-off (MCP\n * sandbox worker, code-agent runner, …). Always throws in Phase 0/1 so\n * no caller is tempted to persist a partial descriptor before the sandbox\n * boundary exists.\n */\nexport function toJSON(_scope: AgentScope): AgentScopeDescriptor {\n throw new Error(\n \"AGENT_SCOPE_TOJSON_PHASE_2: scope serialization lands with the MCP sandbox (Phase 2)\",\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ let _aigne_afs_agent = require("@aigne/afs-agent");
@@ -0,0 +1,2 @@
1
+ import { checkSpawnDepth, deriveChildSession } from "@aigne/afs-agent";
2
+ export { checkSpawnDepth, deriveChildSession };
@@ -0,0 +1,2 @@
1
+ import { checkSpawnDepth, deriveChildSession } from "@aigne/afs-agent";
2
+ export { checkSpawnDepth, deriveChildSession };
@@ -0,0 +1,3 @@
1
+ import { checkSpawnDepth, deriveChildSession } from "@aigne/afs-agent";
2
+
3
+ export { checkSpawnDepth, deriveChildSession };