@bolt-foundry/gambit-core 0.8.3 → 0.8.5-rc.3

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 (230) hide show
  1. package/README.md +32 -2
  2. package/{script/deps/jsr.io/@std/collections/1.1.4 → esm/deps/jsr.io/@std/collections/1.1.5}/deep_merge.d.ts +2 -2
  3. package/esm/deps/jsr.io/@std/collections/{1.1.4 → 1.1.5}/deep_merge.d.ts.map +1 -1
  4. package/esm/deps/jsr.io/@std/collections/{1.1.4 → 1.1.5}/deep_merge.js +29 -19
  5. package/esm/deps/jsr.io/@std/toml/1.0.11/_parser.js +1 -1
  6. package/esm/mod.d.ts +9 -1
  7. package/esm/mod.d.ts.map +1 -1
  8. package/esm/mod.js +4 -0
  9. package/esm/schemas/graders/contexts/conversation.d.ts +22 -0
  10. package/esm/schemas/graders/contexts/conversation.d.ts.map +1 -0
  11. package/esm/schemas/graders/contexts/conversation.js +17 -0
  12. package/esm/schemas/graders/contexts/conversation.zod.d.ts +3 -0
  13. package/esm/schemas/graders/contexts/conversation.zod.d.ts.map +1 -0
  14. package/esm/schemas/graders/contexts/conversation.zod.js +2 -0
  15. package/esm/schemas/graders/contexts/conversation_tools.d.ts +31 -0
  16. package/esm/schemas/graders/contexts/conversation_tools.d.ts.map +1 -0
  17. package/esm/schemas/graders/contexts/conversation_tools.js +25 -0
  18. package/esm/schemas/graders/contexts/conversation_tools.zod.d.ts +3 -0
  19. package/esm/schemas/graders/contexts/conversation_tools.zod.d.ts.map +1 -0
  20. package/esm/schemas/graders/contexts/conversation_tools.zod.js +2 -0
  21. package/esm/schemas/graders/contexts/tools.d.ts +4 -0
  22. package/esm/schemas/graders/contexts/tools.d.ts.map +1 -0
  23. package/esm/schemas/graders/contexts/tools.js +3 -0
  24. package/esm/schemas/graders/contexts/tools.zod.d.ts +3 -0
  25. package/esm/schemas/graders/contexts/tools.zod.d.ts.map +1 -0
  26. package/esm/schemas/graders/contexts/tools.zod.js +2 -0
  27. package/esm/schemas/graders/contexts/turn.d.ts +10 -0
  28. package/esm/schemas/graders/contexts/turn.d.ts.map +1 -0
  29. package/esm/schemas/graders/contexts/turn.js +8 -0
  30. package/esm/schemas/graders/contexts/turn.zod.d.ts +3 -0
  31. package/esm/schemas/graders/contexts/turn.zod.d.ts.map +1 -0
  32. package/esm/schemas/graders/contexts/turn.zod.js +2 -0
  33. package/esm/schemas/graders/contexts/turn_tools.d.ts +32 -0
  34. package/esm/schemas/graders/contexts/turn_tools.d.ts.map +1 -0
  35. package/esm/schemas/graders/contexts/turn_tools.js +28 -0
  36. package/esm/schemas/graders/contexts/turn_tools.zod.d.ts +3 -0
  37. package/esm/schemas/graders/contexts/turn_tools.zod.d.ts.map +1 -0
  38. package/esm/schemas/graders/contexts/turn_tools.zod.js +2 -0
  39. package/esm/schemas/graders/grader_output.d.ts +10 -0
  40. package/esm/schemas/graders/grader_output.d.ts.map +1 -0
  41. package/esm/schemas/graders/grader_output.js +8 -0
  42. package/esm/schemas/graders/grader_output.zod.d.ts +3 -0
  43. package/esm/schemas/graders/grader_output.zod.d.ts.map +1 -0
  44. package/esm/schemas/graders/grader_output.zod.js +2 -0
  45. package/esm/schemas/graders/respond.d.ts +12 -0
  46. package/esm/schemas/graders/respond.d.ts.map +1 -0
  47. package/esm/schemas/graders/respond.js +10 -0
  48. package/esm/schemas/graders/respond.zod.d.ts +3 -0
  49. package/esm/schemas/graders/respond.zod.d.ts.map +1 -0
  50. package/esm/schemas/graders/respond.zod.js +2 -0
  51. package/esm/schemas/scenarios/plain_chat_input_optional.d.ts +5 -0
  52. package/esm/schemas/scenarios/plain_chat_input_optional.d.ts.map +1 -0
  53. package/esm/schemas/scenarios/plain_chat_input_optional.js +5 -0
  54. package/esm/schemas/scenarios/plain_chat_input_optional.zod.d.ts +3 -0
  55. package/esm/schemas/scenarios/plain_chat_input_optional.zod.d.ts.map +1 -0
  56. package/esm/schemas/scenarios/plain_chat_input_optional.zod.js +2 -0
  57. package/esm/schemas/scenarios/plain_chat_output.d.ts +5 -0
  58. package/esm/schemas/scenarios/plain_chat_output.d.ts.map +1 -0
  59. package/esm/schemas/scenarios/plain_chat_output.js +4 -0
  60. package/esm/schemas/scenarios/plain_chat_output.zod.d.ts +3 -0
  61. package/esm/schemas/scenarios/plain_chat_output.zod.d.ts.map +1 -0
  62. package/esm/schemas/scenarios/plain_chat_output.zod.js +2 -0
  63. package/esm/src/builtins.d.ts +2 -0
  64. package/esm/src/builtins.d.ts.map +1 -1
  65. package/esm/src/builtins.js +45 -1
  66. package/esm/src/loader.d.ts.map +1 -1
  67. package/esm/src/loader.js +58 -11
  68. package/esm/src/markdown.d.ts.map +1 -1
  69. package/esm/src/markdown.js +167 -41
  70. package/esm/src/permissions.d.ts +143 -0
  71. package/esm/src/permissions.d.ts.map +1 -0
  72. package/esm/src/permissions.js +406 -0
  73. package/esm/src/runtime.d.ts +27 -2
  74. package/esm/src/runtime.d.ts.map +1 -1
  75. package/esm/src/runtime.js +2755 -63
  76. package/esm/src/runtime_exec_host.d.ts +6 -0
  77. package/esm/src/runtime_exec_host.d.ts.map +1 -0
  78. package/esm/src/runtime_exec_host.js +17 -0
  79. package/esm/src/runtime_exec_host_contract.d.ts +23 -0
  80. package/esm/src/runtime_exec_host_contract.d.ts.map +1 -0
  81. package/esm/src/runtime_exec_host_contract.js +14 -0
  82. package/esm/src/runtime_exec_host_deno.d.ts +3 -0
  83. package/esm/src/runtime_exec_host_deno.d.ts.map +1 -0
  84. package/esm/src/runtime_exec_host_deno.js +35 -0
  85. package/esm/src/runtime_exec_host_unsupported.d.ts +3 -0
  86. package/esm/src/runtime_exec_host_unsupported.d.ts.map +1 -0
  87. package/esm/src/runtime_exec_host_unsupported.js +8 -0
  88. package/esm/src/runtime_worker_host.d.ts +6 -0
  89. package/esm/src/runtime_worker_host.d.ts.map +1 -0
  90. package/esm/src/runtime_worker_host.js +17 -0
  91. package/esm/src/runtime_worker_host_contract.d.ts +33 -0
  92. package/esm/src/runtime_worker_host_contract.d.ts.map +1 -0
  93. package/esm/src/runtime_worker_host_contract.js +14 -0
  94. package/esm/src/runtime_worker_host_deno.d.ts +3 -0
  95. package/esm/src/runtime_worker_host_deno.d.ts.map +1 -0
  96. package/esm/src/runtime_worker_host_deno.js +26 -0
  97. package/esm/src/runtime_worker_host_unsupported.d.ts +3 -0
  98. package/esm/src/runtime_worker_host_unsupported.d.ts.map +1 -0
  99. package/esm/src/runtime_worker_host_unsupported.js +8 -0
  100. package/esm/src/state.d.ts +1 -0
  101. package/esm/src/state.d.ts.map +1 -1
  102. package/esm/src/types.d.ts +235 -3
  103. package/esm/src/types.d.ts.map +1 -1
  104. package/package.json +73 -1
  105. package/schemas/graders/contexts/conversation.ts +32 -9
  106. package/schemas/graders/contexts/conversation.zod.ts +1 -0
  107. package/schemas/graders/contexts/conversation_tools.ts +63 -0
  108. package/schemas/graders/contexts/conversation_tools.zod.ts +1 -0
  109. package/schemas/graders/contexts/tools.ts +5 -0
  110. package/schemas/graders/contexts/tools.zod.ts +1 -0
  111. package/schemas/graders/contexts/turn.ts +8 -1
  112. package/schemas/graders/contexts/turn.zod.ts +1 -0
  113. package/schemas/graders/contexts/turn_tools.ts +63 -0
  114. package/schemas/graders/contexts/turn_tools.zod.ts +1 -0
  115. package/schemas/graders/grader_output.ts +9 -1
  116. package/schemas/graders/grader_output.zod.ts +1 -0
  117. package/schemas/graders/respond.ts +13 -3
  118. package/schemas/graders/respond.zod.ts +1 -0
  119. package/schemas/scenarios/plain_chat_input_optional.ts +6 -0
  120. package/schemas/scenarios/plain_chat_input_optional.zod.ts +1 -0
  121. package/schemas/scenarios/plain_chat_output.ts +5 -0
  122. package/schemas/scenarios/plain_chat_output.zod.ts +1 -0
  123. package/{esm/deps/jsr.io/@std/collections/1.1.4 → script/deps/jsr.io/@std/collections/1.1.5}/deep_merge.d.ts +2 -2
  124. package/script/deps/jsr.io/@std/collections/{1.1.4 → 1.1.5}/deep_merge.d.ts.map +1 -1
  125. package/script/deps/jsr.io/@std/collections/{1.1.4 → 1.1.5}/deep_merge.js +29 -19
  126. package/script/deps/jsr.io/@std/toml/1.0.11/_parser.js +1 -1
  127. package/script/mod.d.ts +9 -1
  128. package/script/mod.d.ts.map +1 -1
  129. package/script/mod.js +12 -1
  130. package/script/schemas/graders/contexts/conversation.d.ts +22 -0
  131. package/script/schemas/graders/contexts/conversation.d.ts.map +1 -0
  132. package/script/schemas/graders/contexts/conversation.js +20 -0
  133. package/script/schemas/graders/contexts/conversation.zod.d.ts +3 -0
  134. package/script/schemas/graders/contexts/conversation.zod.d.ts.map +1 -0
  135. package/script/schemas/graders/contexts/conversation.zod.js +9 -0
  136. package/script/schemas/graders/contexts/conversation_tools.d.ts +31 -0
  137. package/script/schemas/graders/contexts/conversation_tools.d.ts.map +1 -0
  138. package/script/schemas/graders/contexts/conversation_tools.js +28 -0
  139. package/script/schemas/graders/contexts/conversation_tools.zod.d.ts +3 -0
  140. package/script/schemas/graders/contexts/conversation_tools.zod.d.ts.map +1 -0
  141. package/script/schemas/graders/contexts/conversation_tools.zod.js +9 -0
  142. package/script/schemas/graders/contexts/tools.d.ts +4 -0
  143. package/script/schemas/graders/contexts/tools.d.ts.map +1 -0
  144. package/script/schemas/graders/contexts/tools.js +12 -0
  145. package/script/schemas/graders/contexts/tools.zod.d.ts +3 -0
  146. package/script/schemas/graders/contexts/tools.zod.d.ts.map +1 -0
  147. package/script/schemas/graders/contexts/tools.zod.js +9 -0
  148. package/script/schemas/graders/contexts/turn.d.ts +10 -0
  149. package/script/schemas/graders/contexts/turn.d.ts.map +1 -0
  150. package/script/schemas/graders/contexts/turn.js +10 -0
  151. package/script/schemas/graders/contexts/turn.zod.d.ts +3 -0
  152. package/script/schemas/graders/contexts/turn.zod.d.ts.map +1 -0
  153. package/script/schemas/graders/contexts/turn.zod.js +9 -0
  154. package/script/schemas/graders/contexts/turn_tools.d.ts +32 -0
  155. package/script/schemas/graders/contexts/turn_tools.d.ts.map +1 -0
  156. package/script/schemas/graders/contexts/turn_tools.js +31 -0
  157. package/script/schemas/graders/contexts/turn_tools.zod.d.ts +3 -0
  158. package/script/schemas/graders/contexts/turn_tools.zod.d.ts.map +1 -0
  159. package/script/schemas/graders/contexts/turn_tools.zod.js +9 -0
  160. package/script/schemas/graders/grader_output.d.ts +10 -0
  161. package/script/schemas/graders/grader_output.d.ts.map +1 -0
  162. package/script/schemas/graders/grader_output.js +10 -0
  163. package/script/schemas/graders/grader_output.zod.d.ts +3 -0
  164. package/script/schemas/graders/grader_output.zod.d.ts.map +1 -0
  165. package/script/schemas/graders/grader_output.zod.js +9 -0
  166. package/script/schemas/graders/respond.d.ts +12 -0
  167. package/script/schemas/graders/respond.d.ts.map +1 -0
  168. package/script/schemas/graders/respond.js +12 -0
  169. package/script/schemas/graders/respond.zod.d.ts +3 -0
  170. package/script/schemas/graders/respond.zod.d.ts.map +1 -0
  171. package/script/schemas/graders/respond.zod.js +9 -0
  172. package/script/schemas/scenarios/plain_chat_input_optional.d.ts +5 -0
  173. package/script/schemas/scenarios/plain_chat_input_optional.d.ts.map +1 -0
  174. package/script/schemas/scenarios/plain_chat_input_optional.js +7 -0
  175. package/script/schemas/scenarios/plain_chat_input_optional.zod.d.ts +3 -0
  176. package/script/schemas/scenarios/plain_chat_input_optional.zod.d.ts.map +1 -0
  177. package/script/schemas/scenarios/plain_chat_input_optional.zod.js +9 -0
  178. package/script/schemas/scenarios/plain_chat_output.d.ts +5 -0
  179. package/script/schemas/scenarios/plain_chat_output.d.ts.map +1 -0
  180. package/script/schemas/scenarios/plain_chat_output.js +6 -0
  181. package/script/schemas/scenarios/plain_chat_output.zod.d.ts +3 -0
  182. package/script/schemas/scenarios/plain_chat_output.zod.d.ts.map +1 -0
  183. package/script/schemas/scenarios/plain_chat_output.zod.js +9 -0
  184. package/script/src/builtins.d.ts +2 -0
  185. package/script/src/builtins.d.ts.map +1 -1
  186. package/script/src/builtins.js +47 -1
  187. package/script/src/loader.d.ts.map +1 -1
  188. package/script/src/loader.js +57 -10
  189. package/script/src/markdown.d.ts.map +1 -1
  190. package/script/src/markdown.js +167 -41
  191. package/script/src/permissions.d.ts +143 -0
  192. package/script/src/permissions.d.ts.map +1 -0
  193. package/script/src/permissions.js +453 -0
  194. package/script/src/runtime.d.ts +27 -2
  195. package/script/src/runtime.d.ts.map +1 -1
  196. package/script/src/runtime.js +2758 -63
  197. package/script/src/runtime_exec_host.d.ts +6 -0
  198. package/script/src/runtime_exec_host.d.ts.map +1 -0
  199. package/script/src/runtime_exec_host.js +56 -0
  200. package/script/src/runtime_exec_host_contract.d.ts +23 -0
  201. package/script/src/runtime_exec_host_contract.d.ts.map +1 -0
  202. package/script/src/runtime_exec_host_contract.js +18 -0
  203. package/script/src/runtime_exec_host_deno.d.ts +3 -0
  204. package/script/src/runtime_exec_host_deno.d.ts.map +1 -0
  205. package/script/src/runtime_exec_host_deno.js +71 -0
  206. package/script/src/runtime_exec_host_unsupported.d.ts +3 -0
  207. package/script/src/runtime_exec_host_unsupported.d.ts.map +1 -0
  208. package/script/src/runtime_exec_host_unsupported.js +11 -0
  209. package/script/src/runtime_worker_host.d.ts +6 -0
  210. package/script/src/runtime_worker_host.d.ts.map +1 -0
  211. package/script/src/runtime_worker_host.js +56 -0
  212. package/script/src/runtime_worker_host_contract.d.ts +33 -0
  213. package/script/src/runtime_worker_host_contract.d.ts.map +1 -0
  214. package/script/src/runtime_worker_host_contract.js +18 -0
  215. package/script/src/runtime_worker_host_deno.d.ts +3 -0
  216. package/script/src/runtime_worker_host_deno.d.ts.map +1 -0
  217. package/script/src/runtime_worker_host_deno.js +62 -0
  218. package/script/src/runtime_worker_host_unsupported.d.ts +3 -0
  219. package/script/src/runtime_worker_host_unsupported.d.ts.map +1 -0
  220. package/script/src/runtime_worker_host_unsupported.js +11 -0
  221. package/script/src/state.d.ts +1 -0
  222. package/script/src/state.d.ts.map +1 -1
  223. package/script/src/types.d.ts +235 -3
  224. package/script/src/types.d.ts.map +1 -1
  225. package/esm/deps/jsr.io/@std/collections/1.1.4/_utils.d.ts +0 -6
  226. package/esm/deps/jsr.io/@std/collections/1.1.4/_utils.d.ts.map +0 -1
  227. package/esm/deps/jsr.io/@std/collections/1.1.4/_utils.js +0 -18
  228. package/script/deps/jsr.io/@std/collections/1.1.4/_utils.d.ts +0 -6
  229. package/script/deps/jsr.io/@std/collections/1.1.4/_utils.d.ts.map +0 -1
  230. package/script/deps/jsr.io/@std/collections/1.1.4/_utils.js +0 -21
@@ -42,6 +42,7 @@ const path = __importStar(require("../deps/jsr.io/@std/path/1.1.4/mod.js"));
42
42
  const constants_js_1 = require("./constants.js");
43
43
  const definitions_js_1 = require("./definitions.js");
44
44
  const loader_js_1 = require("./loader.js");
45
+ const permissions_js_1 = require("./permissions.js");
45
46
  const schema_js_1 = require("./schema.js");
46
47
  const builtins_js_1 = require("./builtins.js");
47
48
  const logger = console;
@@ -88,6 +89,23 @@ function toFileUrl(p) {
88
89
  const abs = path.resolve(p);
89
90
  return path.toFileUrl(abs).href;
90
91
  }
92
+ function startsWithFrontMatterDelimiter(raw) {
93
+ const normalized = raw.startsWith("\uFEFF") ? raw.slice(1) : raw;
94
+ const trimmed = normalized.trimStart();
95
+ return /^(\+\+\+|---)\s*(\r?\n|$)/.test(trimmed);
96
+ }
97
+ function parseFrontMatterOrRaw(raw, resolvedPath) {
98
+ try {
99
+ return (0, any_js_1.extract)(raw);
100
+ }
101
+ catch (err) {
102
+ if (!startsWithFrontMatterDelimiter(raw)) {
103
+ return { attrs: {}, body: raw };
104
+ }
105
+ const message = err instanceof Error ? err.message : String(err);
106
+ throw new Error(`Failed to parse front matter in ${resolvedPath}: ${message}`);
107
+ }
108
+ }
91
109
  async function maybeLoadSchema(schemaPath, basePath) {
92
110
  if (!schemaPath || typeof schemaPath !== "string")
93
111
  return undefined;
@@ -97,7 +115,7 @@ async function maybeLoadSchema(schemaPath, basePath) {
97
115
  const mod = await Promise.resolve(`${toFileUrl(resolved)}`).then(s => __importStar(require(s)));
98
116
  return mod.default;
99
117
  }
100
- function normalizeDeckRefs(refs, basePath) {
118
+ function normalizeDeckRefs(refs, basePath, opts) {
101
119
  if (!Array.isArray(refs))
102
120
  return [];
103
121
  return refs
@@ -108,14 +126,32 @@ function normalizeDeckRefs(refs, basePath) {
108
126
  if (!p) {
109
127
  throw new Error("Deck reference must include a path");
110
128
  }
129
+ if (opts?.requirePrompt && !p.endsWith("PROMPT.md")) {
130
+ throw new Error(`Deck reference must point to PROMPT.md (${basePath})`);
131
+ }
111
132
  const normalized = { ...rec };
112
- normalized.path = path.resolve(path.dirname(basePath), p);
133
+ normalized.path = p.startsWith("gambit://")
134
+ ? p
135
+ : path.resolve(path.dirname(basePath), p);
113
136
  if (typeof rec.description !== "string")
114
137
  delete normalized.description;
115
138
  if (typeof rec.label !== "string")
116
139
  delete normalized.label;
117
140
  if (typeof rec.id !== "string")
118
141
  delete normalized.id;
142
+ if (rec.permissions !== undefined) {
143
+ const parsed = (0, permissions_js_1.normalizePermissionDeclaration)(rec.permissions, path.dirname(basePath));
144
+ if (parsed)
145
+ normalized.permissions = parsed;
146
+ }
147
+ if (opts?.requireDescription) {
148
+ const desc = typeof rec.description === "string"
149
+ ? rec.description.trim()
150
+ : "";
151
+ if (!desc) {
152
+ throw new Error(`Action deck must include a description (${basePath})`);
153
+ }
154
+ }
119
155
  return normalized;
120
156
  });
121
157
  }
@@ -132,14 +168,92 @@ function mergeDeckRefs(...lists) {
132
168
  }
133
169
  return Array.from(merged.values());
134
170
  }
135
- function normalizeActionDecks(entries, basePath) {
136
- return normalizeDeckRefs(entries, basePath).map((entry) => {
137
- const name = "name" in entry ? String(entry.name ?? "").trim() : "";
171
+ async function normalizeActionDecks(entries, basePath, opts) {
172
+ if (!Array.isArray(entries))
173
+ return [];
174
+ const out = [];
175
+ for (const rawEntry of entries) {
176
+ if (!rawEntry || typeof rawEntry !== "object")
177
+ continue;
178
+ const rec = rawEntry;
179
+ const name = String(rec.name ?? "").trim();
138
180
  if (!name) {
139
181
  throw new Error(`Action deck must include a name (${basePath})`);
140
182
  }
141
- return { ...entry, name };
142
- });
183
+ const desc = typeof rec.description === "string"
184
+ ? rec.description.trim()
185
+ : "";
186
+ if (opts?.requireDescription && !desc) {
187
+ throw new Error(`Action deck must include a description (${basePath})`);
188
+ }
189
+ const rawPath = typeof rec.path === "string" ? rec.path.trim() : "";
190
+ const rawExecute = typeof rec.execute === "string"
191
+ ? rec.execute.trim()
192
+ : "";
193
+ const hasPath = rawPath.length > 0;
194
+ const hasExecute = rawExecute.length > 0;
195
+ if (hasPath === hasExecute) {
196
+ throw new Error(`Action deck must include exactly one of path or execute (${basePath})`);
197
+ }
198
+ if (hasPath && opts?.requirePrompt && !rawPath.endsWith("PROMPT.md")) {
199
+ throw new Error(`Deck reference must point to PROMPT.md (${basePath})`);
200
+ }
201
+ const actionContextSchema = await maybeLoadSchema(rec.contextSchema, basePath);
202
+ const actionResponseSchema = await maybeLoadSchema(rec.responseSchema, basePath);
203
+ if (hasExecute && (!actionContextSchema || !actionResponseSchema)) {
204
+ throw new Error(`Action execute target must include contextSchema and responseSchema (${basePath})`);
205
+ }
206
+ const selectedTarget = hasPath ? rawPath : rawExecute;
207
+ const normalizedPath = selectedTarget.startsWith("gambit://")
208
+ ? selectedTarget
209
+ : path.resolve(path.dirname(basePath), selectedTarget);
210
+ const normalized = {
211
+ name,
212
+ path: normalizedPath,
213
+ description: desc || undefined,
214
+ label: typeof rec.label === "string" ? rec.label : undefined,
215
+ id: typeof rec.id === "string" ? rec.id : undefined,
216
+ execute: hasExecute ? normalizedPath : undefined,
217
+ contextSchema: actionContextSchema,
218
+ responseSchema: actionResponseSchema,
219
+ };
220
+ if (rec.permissions !== undefined) {
221
+ const parsed = (0, permissions_js_1.normalizePermissionDeclaration)(rec.permissions, path.dirname(basePath));
222
+ if (parsed)
223
+ normalized.permissions = parsed;
224
+ }
225
+ out.push(normalized);
226
+ }
227
+ return out;
228
+ }
229
+ async function normalizeExternalTools(refs, basePath) {
230
+ if (!Array.isArray(refs))
231
+ return [];
232
+ const out = [];
233
+ for (const entry of refs) {
234
+ if (!entry || typeof entry !== "object")
235
+ continue;
236
+ const rec = entry;
237
+ const name = String(rec.name ?? "").trim();
238
+ if (!name) {
239
+ throw new Error(`External tool must include a name (${basePath})`);
240
+ }
241
+ if (name.startsWith(constants_js_1.RESERVED_TOOL_PREFIX)) {
242
+ throw new Error(`External tool name ${name} is reserved (prefix ${constants_js_1.RESERVED_TOOL_PREFIX})`);
243
+ }
244
+ if (!constants_js_1.TOOL_NAME_PATTERN.test(name) || name.length > constants_js_1.MAX_TOOL_NAME_LENGTH) {
245
+ throw new Error(`External tool name ${name} must match ${constants_js_1.TOOL_NAME_PATTERN} and be <= ${constants_js_1.MAX_TOOL_NAME_LENGTH} characters`);
246
+ }
247
+ const inputSchema = await maybeLoadSchema(rec.inputSchema, basePath);
248
+ out.push({
249
+ name,
250
+ description: typeof rec.description === "string"
251
+ ? rec.description
252
+ : undefined,
253
+ inputSchema,
254
+ });
255
+ }
256
+ return out;
143
257
  }
144
258
  async function expandEmbedsInBody(args) {
145
259
  const { body, resolvedPath, stack } = args;
@@ -155,17 +269,17 @@ async function expandEmbedsInBody(args) {
155
269
  const target = match[1];
156
270
  out += body.slice(lastIndex, matchIndex);
157
271
  if (target === RESPOND_MARKER) {
158
- warnLegacyMarker("respond", "gambit://cards/respond.card.md");
272
+ warnLegacyMarker("respond", "gambit://snippets/respond.md");
159
273
  respond = true;
160
274
  out += RESPOND_TEXT;
161
275
  }
162
276
  else if (target === INIT_MARKER) {
163
- warnLegacyMarker("init", "gambit://cards/context.card.md");
277
+ warnLegacyMarker("init", "gambit://snippets/context.md");
164
278
  initHint = true;
165
279
  out += INIT_TEXT;
166
280
  }
167
281
  else if (target === END_MARKER) {
168
- warnLegacyMarker("end", "gambit://cards/end.card.md");
282
+ warnLegacyMarker("end", "gambit://snippets/end.md");
169
283
  endHint = true;
170
284
  out += END_TEXT;
171
285
  }
@@ -189,17 +303,7 @@ async function loadMarkdownCard(filePath, parentPath, stack = []) {
189
303
  }
190
304
  const nextStack = [...stack, resolved];
191
305
  const raw = await dntShim.Deno.readTextFile(resolved);
192
- let attrs;
193
- let body;
194
- try {
195
- const parsed = (0, any_js_1.extract)(raw);
196
- attrs = parsed.attrs;
197
- body = parsed.body;
198
- }
199
- catch (err) {
200
- const message = err instanceof Error ? err.message : String(err);
201
- throw new Error(`Failed to parse front matter in ${resolved}: ${message}`);
202
- }
306
+ const { attrs, body } = parseFrontMatterOrRaw(raw, resolved);
203
307
  const candidate = attrs;
204
308
  if ((0, definitions_js_1.isCardDefinition)(candidate)) {
205
309
  // treat attrs as ts-shaped card
@@ -209,7 +313,7 @@ async function loadMarkdownCard(filePath, parentPath, stack = []) {
209
313
  }
210
314
  const hasNewActionField = attrs.actionDecks;
211
315
  const legacyActions = attrs.actions;
212
- const actionDecks = normalizeActionDecks(hasNewActionField ?? legacyActions, resolved);
316
+ const actionDecks = await normalizeActionDecks(hasNewActionField ?? legacyActions, resolved);
213
317
  if (!hasNewActionField && legacyActions) {
214
318
  logger.warn(`[gambit] card at ${resolved} uses deprecated "actions"; rename to "actionDecks"`);
215
319
  }
@@ -243,6 +347,7 @@ async function loadMarkdownCard(filePath, parentPath, stack = []) {
243
347
  const embeddedCards = replaced.embeds;
244
348
  const respondFlag = Boolean(attrs.respond);
245
349
  const allowEndFlag = Boolean(attrs.allowEnd);
350
+ const permissions = (0, permissions_js_1.normalizePermissionDeclaration)(attrs.permissions, path.dirname(resolved));
246
351
  return {
247
352
  kind: "gambit.card",
248
353
  path: resolved,
@@ -253,6 +358,7 @@ async function loadMarkdownCard(filePath, parentPath, stack = []) {
253
358
  testDecks: normalizeDeckRefs(attrs.testDecks, resolved),
254
359
  graderDecks: normalizeDeckRefs(attrs.graderDecks, resolved),
255
360
  cards: embeddedCards,
361
+ permissions,
256
362
  contextFragment,
257
363
  responseFragment,
258
364
  inputFragment: contextFragment,
@@ -265,26 +371,35 @@ async function loadMarkdownDeck(filePath, parentPath) {
265
371
  ? path.resolve(path.dirname(parentPath), filePath)
266
372
  : path.resolve(filePath);
267
373
  const raw = await dntShim.Deno.readTextFile(resolved);
268
- let attrs;
269
- let body;
270
- try {
271
- const parsed = (0, any_js_1.extract)(raw);
272
- attrs = parsed.attrs;
273
- body = parsed.body;
274
- }
275
- catch (err) {
276
- const message = err instanceof Error ? err.message : String(err);
277
- throw new Error(`Failed to parse front matter in ${resolved}: ${message}`);
278
- }
374
+ const { attrs, body } = parseFrontMatterOrRaw(raw, resolved);
279
375
  const deckAttrs = attrs;
280
376
  const deckMeta = (deckAttrs.deck ?? deckAttrs);
377
+ if (deckMeta.mcpServers !== undefined) {
378
+ throw new Error(`Deck-level [[mcpServers]] is unsupported in this phase (${resolved})`);
379
+ }
380
+ if (deckMeta.execute !== undefined) {
381
+ throw new Error(`Top-level execute in PROMPT.md is unsupported (${resolved})`);
382
+ }
281
383
  const hasNewActionDecks = deckMeta.actionDecks;
282
- const legacyDeckActions = deckMeta.actions;
283
- const actionDecks = normalizeActionDecks(hasNewActionDecks ?? legacyDeckActions, resolved);
284
- if (!hasNewActionDecks && legacyDeckActions) {
285
- logger.warn(`[gambit] deck at ${resolved} uses deprecated "actions"; rename to "actionDecks"`);
384
+ const canonicalActions = deckMeta.actions;
385
+ const actionDecks = await normalizeActionDecks(canonicalActions, resolved, {
386
+ requirePrompt: true,
387
+ requireDescription: true,
388
+ });
389
+ const legacyActionDecks = await normalizeActionDecks(hasNewActionDecks, resolved);
390
+ if (hasNewActionDecks) {
391
+ logger.warn(`[gambit] deck at ${resolved} uses deprecated "actionDecks"; use "[[actions]]" instead.`);
286
392
  }
287
- actionDecks.forEach((a) => {
393
+ if (deckMeta.testDecks) {
394
+ logger.warn(`[gambit] deck at ${resolved} uses deprecated "testDecks"; use "[[scenarios]]" instead.`);
395
+ }
396
+ if (deckMeta.graderDecks) {
397
+ logger.warn(`[gambit] deck at ${resolved} uses deprecated "graderDecks"; use "[[graders]]" instead.`);
398
+ }
399
+ const scenarioDecks = normalizeDeckRefs(deckMeta.scenarios, resolved, { requirePrompt: true });
400
+ const graderDecks = normalizeDeckRefs(deckMeta.graders, resolved, { requirePrompt: true });
401
+ const allActionDecks = [...actionDecks, ...legacyActionDecks];
402
+ allActionDecks.forEach((a) => {
288
403
  if (a.name.startsWith(constants_js_1.RESERVED_TOOL_PREFIX) &&
289
404
  !constants_js_1.BUILTIN_TOOL_NAME_SET.has(a.name)) {
290
405
  throw new Error(`Action name ${a.name} is reserved`);
@@ -322,7 +437,7 @@ async function loadMarkdownDeck(filePath, parentPath) {
322
437
  mergedActions[action.name] = action;
323
438
  }
324
439
  }
325
- for (const action of actionDecks) {
440
+ for (const action of allActionDecks) {
326
441
  mergedActions[action.name] = action;
327
442
  }
328
443
  let mergedContextSchema = contextSchema;
@@ -365,10 +480,18 @@ async function loadMarkdownDeck(filePath, parentPath) {
365
480
  }
366
481
  : undefined;
367
482
  const mergedActionDecks = Object.values(mergedActions);
483
+ const tools = await normalizeExternalTools(deckMeta.tools, resolved);
484
+ const actionNameSet = new Set(mergedActionDecks.map((action) => action.name));
485
+ for (const tool of tools) {
486
+ if (actionNameSet.has(tool.name)) {
487
+ logger.warn(`[gambit] tool ${tool.name} is shadowed by an action in ${resolved}`);
488
+ }
489
+ }
368
490
  const rootTestDecks = normalizeDeckRefs(deckMeta.testDecks, resolved);
369
491
  const rootGraderDecks = normalizeDeckRefs(deckMeta.graderDecks, resolved);
370
492
  const embeddedTestDecks = allCards.flatMap((card) => card.testDecks ?? []);
371
493
  const embeddedGraderDecks = allCards.flatMap((card) => card.graderDecks ?? []);
494
+ const permissions = (0, permissions_js_1.normalizePermissionDeclaration)(deckMeta.permissions, path.dirname(resolved));
372
495
  return {
373
496
  kind: "gambit.deck",
374
497
  path: resolved,
@@ -376,8 +499,9 @@ async function loadMarkdownDeck(filePath, parentPath) {
376
499
  allowEnd,
377
500
  actionDecks: mergedActionDecks,
378
501
  actions: mergedActionDecks,
379
- testDecks: mergeDeckRefs(rootTestDecks, embeddedTestDecks),
380
- graderDecks: mergeDeckRefs(rootGraderDecks, embeddedGraderDecks),
502
+ tools,
503
+ testDecks: mergeDeckRefs(scenarioDecks, rootTestDecks, embeddedTestDecks),
504
+ graderDecks: mergeDeckRefs(graderDecks, rootGraderDecks, embeddedGraderDecks),
381
505
  cards: allCards,
382
506
  label: deckMeta.label,
383
507
  startMode: deckMeta.startMode,
@@ -387,11 +511,13 @@ async function loadMarkdownDeck(filePath, parentPath) {
387
511
  responseSchema: mergedResponseSchema,
388
512
  inputSchema: mergedInputSchema,
389
513
  outputSchema: mergedOutputSchema,
514
+ executor: undefined,
390
515
  handlers,
391
516
  respond: Boolean(deckMeta.respond) ||
392
517
  replaced.respond ||
393
518
  allCards.some((c) => c.respond),
394
519
  inlineEmbeds: true,
520
+ permissions,
395
521
  };
396
522
  }
397
523
  function isMarkdownFile(filePath) {
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Deno-native permission kinds supported by Gambit's permission contract.
3
+ */
4
+ export declare const PERMISSION_KINDS: readonly ["read", "write", "run", "net", "env"];
5
+ export type PermissionKind = (typeof PERMISSION_KINDS)[number];
6
+ export type PathPermissionInput = boolean | Array<string>;
7
+ export type RunPermissionInput = boolean | Array<string> | {
8
+ paths?: Array<string>;
9
+ commands?: Array<string>;
10
+ };
11
+ export type PermissionDeclarationInput = Partial<{
12
+ read: PathPermissionInput;
13
+ write: PathPermissionInput;
14
+ run: RunPermissionInput;
15
+ net: PathPermissionInput;
16
+ env: PathPermissionInput;
17
+ }>;
18
+ export type SerializedRunPermission = false | true | {
19
+ paths: Array<string>;
20
+ commands: Array<string>;
21
+ };
22
+ export type SerializedPermissionSet = {
23
+ read: false | true | Array<string>;
24
+ write: false | true | Array<string>;
25
+ run: SerializedRunPermission;
26
+ net: false | true | Array<string>;
27
+ env: false | true | Array<string>;
28
+ };
29
+ export type PermissionDeclaration = SerializedPermissionSet;
30
+ type NormalizedScope = {
31
+ all: boolean;
32
+ values: Set<string>;
33
+ };
34
+ type NormalizedRunScope = {
35
+ all: boolean;
36
+ paths: Set<string>;
37
+ commands: Set<string>;
38
+ };
39
+ export type NormalizedPermissionSet = {
40
+ baseDir: string;
41
+ read: NormalizedScope;
42
+ write: NormalizedScope;
43
+ run: NormalizedRunScope;
44
+ net: NormalizedScope;
45
+ env: NormalizedScope;
46
+ };
47
+ export type PermissionLayerName = "parent" | "workspace" | "declaration" | "reference" | "session" | "host";
48
+ export type PermissionLayerTrace = {
49
+ name: PermissionLayerName;
50
+ baseDir: string;
51
+ requested: SerializedPermissionSet;
52
+ effective: SerializedPermissionSet;
53
+ };
54
+ export type PermissionTrace = {
55
+ baseDir: string;
56
+ effective: SerializedPermissionSet;
57
+ layers: Array<PermissionLayerTrace>;
58
+ };
59
+ export declare function cloneNormalizedPermissions(input: NormalizedPermissionSet): NormalizedPermissionSet;
60
+ /**
61
+ * Returns an allow-all permission set anchored to `baseDir`.
62
+ */
63
+ export declare function allowAllPermissions(baseDir: string): NormalizedPermissionSet;
64
+ /**
65
+ * Normalizes a permission declaration to a serializable, deterministic shape.
66
+ *
67
+ * Relative path grants are resolved against `baseDir`.
68
+ */
69
+ export declare function normalizePermissionDeclaration(input: PermissionDeclarationInput | undefined, baseDir: string): PermissionDeclaration | undefined;
70
+ /**
71
+ * Normalizes a declaration to the internal set form used during intersection.
72
+ */
73
+ export declare function normalizePermissionDeclarationToSet(input: PermissionDeclarationInput | undefined, baseDir: string): NormalizedPermissionSet | undefined;
74
+ /**
75
+ * Serializes an internal normalized permission set for traces/persistence.
76
+ */
77
+ export declare function serializePermissions(set: NormalizedPermissionSet): SerializedPermissionSet;
78
+ /**
79
+ * Computes the monotonic intersection between two permission sets.
80
+ *
81
+ * `baseDir` controls how relative checks (`canReadPath`/etc) are evaluated for
82
+ * the returned set.
83
+ */
84
+ export declare function intersectPermissions(parent: NormalizedPermissionSet, next: NormalizedPermissionSet, baseDir: string): NormalizedPermissionSet;
85
+ /**
86
+ * Resolves effective permissions and emits a layer-by-layer permission trace.
87
+ *
88
+ * Layer precedence:
89
+ * 1. `parent` (or host allow-all for roots)
90
+ * 2. `workspace` (root only)
91
+ * 3. `declaration` (deck/card declaration)
92
+ * 4. `reference` (parent reference override)
93
+ * 5. `session` (root only)
94
+ */
95
+ export declare function resolveEffectivePermissions(args: {
96
+ baseDir: string;
97
+ parent?: NormalizedPermissionSet;
98
+ workspace?: {
99
+ baseDir: string;
100
+ permissions: PermissionDeclarationInput;
101
+ };
102
+ declaration?: {
103
+ baseDir: string;
104
+ permissions: PermissionDeclarationInput;
105
+ };
106
+ reference?: {
107
+ baseDir: string;
108
+ permissions: PermissionDeclarationInput;
109
+ };
110
+ session?: {
111
+ baseDir: string;
112
+ permissions: PermissionDeclarationInput;
113
+ };
114
+ }): {
115
+ effective: NormalizedPermissionSet;
116
+ trace: PermissionTrace;
117
+ };
118
+ /**
119
+ * Returns whether `targetPath` is readable under `set`.
120
+ *
121
+ * Relative paths are resolved against `set.baseDir`.
122
+ */
123
+ export declare function canReadPath(set: NormalizedPermissionSet, targetPath: string): boolean;
124
+ /**
125
+ * Returns whether `targetPath` is writable under `set`.
126
+ *
127
+ * Relative paths are resolved against `set.baseDir`.
128
+ */
129
+ export declare function canWritePath(set: NormalizedPermissionSet, targetPath: string): boolean;
130
+ /**
131
+ * Returns whether `targetPath` is executable via run-path grants.
132
+ *
133
+ * Relative paths are resolved against `set.baseDir`.
134
+ */
135
+ export declare function canRunPath(set: NormalizedPermissionSet, targetPath: string): boolean;
136
+ /**
137
+ * Returns whether `commandName` is executable via run-command grants.
138
+ *
139
+ * This check intentionally does not apply basename/path fallback semantics.
140
+ */
141
+ export declare function canRunCommand(set: NormalizedPermissionSet, commandName: string): boolean;
142
+ export {};
143
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/src/permissions.ts"],"names":[],"mappings":"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"}