@bevel-software/platform-core-backend 0.28.1 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-guide/access-control.md +15 -0
- package/agent-guide/skills.md +8 -0
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +4 -2
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/compose-embed.d.ts +35 -0
- package/dist/core/compose-embed.d.ts.map +1 -0
- package/dist/core/compose-embed.js +14 -0
- package/dist/core/compose-embed.js.map +1 -0
- package/dist/core/core-ports.d.ts +10 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +7 -1
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +1 -1
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +25 -14
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +11 -6
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +13 -6
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +36 -14
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +145 -43
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/auth/index.d.ts +15 -0
- package/dist/modules/auth/index.d.ts.map +1 -0
- package/dist/modules/auth/index.js +20 -0
- package/dist/modules/auth/index.js.map +1 -0
- package/dist/modules/embed/embed.interface.d.ts +22 -0
- package/dist/modules/embed/embed.interface.d.ts.map +1 -1
- package/dist/modules/embed/embed.service.d.ts +28 -14
- package/dist/modules/embed/embed.service.d.ts.map +1 -1
- package/dist/modules/embed/embed.service.js +38 -17
- package/dist/modules/embed/embed.service.js.map +1 -1
- package/dist/modules/embed/embed.tools.d.ts.map +1 -1
- package/dist/modules/embed/embed.tools.js +10 -2
- package/dist/modules/embed/embed.tools.js.map +1 -1
- package/dist/modules/kb-fs/index.d.ts +7 -0
- package/dist/modules/kb-fs/index.d.ts.map +1 -0
- package/dist/modules/kb-fs/index.js +2 -0
- package/dist/modules/kb-fs/index.js.map +1 -0
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.js +43 -8
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +8 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +115 -11
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-helpers/default-branch-write.d.ts +69 -0
- package/dist/modules/tool-helpers/default-branch-write.d.ts.map +1 -0
- package/dist/modules/tool-helpers/default-branch-write.js +63 -0
- package/dist/modules/tool-helpers/default-branch-write.js.map +1 -0
- package/dist/modules/tool-helpers/index.d.ts +1 -0
- package/dist/modules/tool-helpers/index.d.ts.map +1 -1
- package/dist/modules/tool-helpers/index.js +1 -0
- package/dist/modules/tool-helpers/index.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +27 -5
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +70 -18
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +13 -0
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +24 -0
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/index.d.ts +8 -0
- package/dist/modules/workflow/index.d.ts.map +1 -0
- package/dist/modules/workflow/index.js +8 -0
- package/dist/modules/workflow/index.js.map +1 -0
- package/dist/modules/workflow/pending-commits.service.d.ts +2 -17
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +4 -24
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +20 -1
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +5 -3
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/index.d.ts +10 -0
- package/dist/modules/workspace/index.d.ts.map +1 -0
- package/dist/modules/workspace/index.js +10 -0
- package/dist/modules/workspace/index.js.map +1 -0
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +106 -20
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/workspace-id.d.ts +17 -0
- package/dist/shared/workspace-id.d.ts.map +1 -1
- package/dist/shared/workspace-id.js +24 -0
- package/dist/shared/workspace-id.js.map +1 -1
- package/mcp-app/page.html +142 -8
- package/package.json +3 -3
- package/src/core/__tests__/compose-embed.test.ts +65 -0
- package/src/core/__tests__/lifecycle.test.ts +29 -26
- package/src/core/catalog-revision.ts +4 -2
- package/src/core/compose-embed.ts +50 -0
- package/src/core/core-ports.ts +10 -0
- package/src/core/create-core-server.ts +7 -1
- package/src/core/create-core-services.ts +27 -21
- package/src/core/lifecycle.ts +24 -12
- package/src/modules/access/__tests__/access-control.atref-cache.test.ts +303 -25
- package/src/modules/access/access-control.service.ts +162 -46
- package/src/modules/auth/index.ts +29 -0
- package/src/modules/embed/__tests__/embed.service.test.ts +76 -3
- package/src/modules/embed/__tests__/embed.tools.test.ts +29 -0
- package/src/modules/embed/embed.interface.ts +24 -0
- package/src/modules/embed/embed.service.ts +44 -19
- package/src/modules/embed/embed.tools.ts +10 -2
- package/src/modules/kb-fs/index.ts +6 -0
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +73 -6
- package/src/modules/plugins/join-request-jobs.service.ts +45 -8
- package/src/modules/skills/__tests__/branch-skills.tools.test.ts +1 -1
- package/src/modules/skills/skills.contract.ts +4 -0
- package/src/modules/skills/skills.tools.ts +120 -10
- package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +581 -0
- package/src/modules/tool-helpers/default-branch-write.ts +90 -0
- package/src/modules/tool-helpers/index.ts +5 -0
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +36 -2
- package/src/modules/workflow/__tests__/fake-file-lock-db.ts +13 -2
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +168 -1
- package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +102 -6
- package/src/modules/workflow/file-lock.service.ts +79 -18
- package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +42 -0
- package/src/modules/workflow/git/git.service.ts +23 -0
- package/src/modules/workflow/index.ts +7 -0
- package/src/modules/workflow/pending-commits.service.ts +4 -23
- package/src/modules/workflow/workflow.routes.ts +24 -1
- package/src/modules/workflow/workflow.service.ts +3 -2
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +21 -11
- package/src/modules/workspace/index.ts +14 -0
- package/src/modules/workspace/workspace.tools.ts +120 -20
- package/src/shared/workspace-id.ts +24 -0
|
@@ -5,6 +5,7 @@ import { ToolError } from '../tool-helpers/tool.contract.js';
|
|
|
5
5
|
import { toolDef } from '../tool-helpers/tool-def.js';
|
|
6
6
|
import { bodyEnvelope, declareRouteTool } from '../tool-helpers/route-tool-schemas.js';
|
|
7
7
|
import { EXTERNAL_KB_MANUAL_NAME } from '../tool-manuals/tool-manuals.contract.js';
|
|
8
|
+
import { CAN_WRITE_CLAUSE, defaultBranchWriteVerdicts } from '../tool-helpers/index.js';
|
|
8
9
|
/**
|
|
9
10
|
* The optional `branch` both skill tools declare. Optional on purpose, unlike
|
|
10
11
|
* the required `branch` of the KB file tools: a skill read is answered by the
|
|
@@ -64,7 +65,54 @@ const GET_SKILL_INPUTS = {
|
|
|
64
65
|
*/
|
|
65
66
|
export function registerSkillsTools(registry, router, toolAuth, toolHandler, skillService,
|
|
66
67
|
/** When given, a loaded skill carries `warnings` for `allowed-tools` entries that name no visible tool. */
|
|
67
|
-
allowedTools
|
|
68
|
+
allowedTools,
|
|
69
|
+
/**
|
|
70
|
+
* Where `canWrite` is judged: the access layer and the default branch's
|
|
71
|
+
* workspace. Without it every verdict is `false` — the safe answer, which
|
|
72
|
+
* sends the agent to a change request rather than a direct write.
|
|
73
|
+
*/
|
|
74
|
+
writeVerdicts) {
|
|
75
|
+
/**
|
|
76
|
+
* Whether the caller may commit each repo-relative path directly on the
|
|
77
|
+
* default branch — one batch per call, whichever branch was read (see
|
|
78
|
+
* `defaultBranchWriteVerdicts`).
|
|
79
|
+
*/
|
|
80
|
+
const verdictsFor = (ctx, paths, at) => writeVerdicts
|
|
81
|
+
? defaultBranchWriteVerdicts(writeVerdicts, ctx, paths, { ref: at })
|
|
82
|
+
: Promise.resolve(new Map(paths.map((p) => [p, false])));
|
|
83
|
+
/**
|
|
84
|
+
* Every repo-relative file `get_skill`'s `canWrite` covers: the skill's
|
|
85
|
+
* folder as COMMITTED on the default branch (`HEAD` of its clone), which is
|
|
86
|
+
* what a direct write is judged against — never the working tree, where a
|
|
87
|
+
* bundled file deleted but not yet committed would drop out of the verdict
|
|
88
|
+
* while `HEAD` still forbids changing it. A skill is committed there when
|
|
89
|
+
* its `SKILL.md` is — a folder of that name holding other tracked files is
|
|
90
|
+
* not the skill, and its files are not the skill's. A skill not committed
|
|
91
|
+
* there exists only on the branch read, and is judged by its files there,
|
|
92
|
+
* where they would land. The files are listed at ONE commit — the one
|
|
93
|
+
* `HEAD` names when asked, answered beside them — and the verdict is judged
|
|
94
|
+
* at that same commit, so a commit landing between the two cannot pair one
|
|
95
|
+
* snapshot's files with another's rules. Null — judged `false` — when
|
|
96
|
+
* `HEAD` names no commit or the listing fails (fail closed), and without
|
|
97
|
+
* one when the verdict cannot be anything but `false`.
|
|
98
|
+
*/
|
|
99
|
+
const skillFilesOnDefault = async (ctx, loaded) => {
|
|
100
|
+
if (!writeVerdicts?.committedFiles || ctx.scope !== 'write')
|
|
101
|
+
return null;
|
|
102
|
+
const workspaceId = writeVerdicts.defaultWorkspaceId();
|
|
103
|
+
let at;
|
|
104
|
+
let committed;
|
|
105
|
+
try {
|
|
106
|
+
at = await writeVerdicts.committedFiles.commitAtRef(workspaceId, 'HEAD');
|
|
107
|
+
if (!at)
|
|
108
|
+
return null;
|
|
109
|
+
committed = await writeVerdicts.committedFiles.listFilesAtRef(workspaceId, at, loaded.path);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return { paths: committed.includes(skillMdOf(loaded)) ? committed : [skillMdOf(loaded), ...loaded.files], at };
|
|
115
|
+
};
|
|
68
116
|
registry.registerExternalTool((ctx) => buildListSkillsDef(skillService, ctx.userEmail));
|
|
69
117
|
registry.registerInternalTool((ctx) => buildListSkillsDef(skillService, ctx.userEmail));
|
|
70
118
|
registry.registerExternalTool((ctx) => buildGetSkillDef(skillService, ctx.userEmail));
|
|
@@ -74,9 +122,14 @@ allowedTools) {
|
|
|
74
122
|
// call that lands before the first listing must be checked against them too.
|
|
75
123
|
declareRouteTool('list_skills', LIST_SKILLS_INPUTS);
|
|
76
124
|
declareRouteTool('get_skill', GET_SKILL_INPUTS);
|
|
77
|
-
router.post('/agent/tools/list_skills', toolAuth, toolHandler(async (args, ctx) =>
|
|
78
|
-
skills
|
|
79
|
-
|
|
125
|
+
router.post('/agent/tools/list_skills', toolAuth, toolHandler(async (args, ctx) => {
|
|
126
|
+
const skills = await skillService.listSkills(ctx.user.email, { branch: branchArg(args) });
|
|
127
|
+
// Judged on each skill's SKILL.md, by the default branch's rules — a
|
|
128
|
+
// skill only on a draft is judged where it would land. Each verdict goes
|
|
129
|
+
// onto a COPY: the summaries may be the catalog shared across users.
|
|
130
|
+
const verdicts = await verdictsFor(ctx, skills.map(skillMdOf));
|
|
131
|
+
return { skills: skills.map((s) => ({ ...s, canWrite: verdicts.get(skillMdOf(s)) === true })) };
|
|
132
|
+
}));
|
|
80
133
|
router.post('/agent/tools/get_skill', toolAuth, toolHandler(async (args, ctx) => {
|
|
81
134
|
const name = typeof args.name === 'string' ? args.name : '';
|
|
82
135
|
const file = typeof args.file === 'string' ? args.file : undefined;
|
|
@@ -88,16 +141,38 @@ allowedTools) {
|
|
|
88
141
|
// skill's history, a `branch` is a draft nobody has released — asked for
|
|
89
142
|
// together, neither answer is the one the caller meant, and guessing
|
|
90
143
|
// would serve instructions under a label that does not describe them.
|
|
91
|
-
|
|
144
|
+
const versioned = version !== undefined && version.trim().length > 0;
|
|
145
|
+
if (branch !== undefined && versioned) {
|
|
92
146
|
throw new ToolError('get_skill takes `branch` or `version`, not both: `branch` loads the skill as that draft has it ' +
|
|
93
147
|
'now, `version` loads a version the released skill declared. Pass one of them.', 400);
|
|
94
148
|
}
|
|
95
149
|
const result = await skillService.getSkill(ctx.user.email, name, file, { version, branch });
|
|
96
|
-
if (!
|
|
150
|
+
if (!result.ok)
|
|
97
151
|
return result;
|
|
98
|
-
|
|
152
|
+
if (result.kind === 'file') {
|
|
153
|
+
const verdicts = await verdictsFor(ctx, [result.file.path]);
|
|
154
|
+
return { ...result, file: { ...result.file, canWrite: verdicts.get(result.file.path) === true } };
|
|
155
|
+
}
|
|
156
|
+
// The whole skill: true only when every one of its files may be written —
|
|
157
|
+
// the files the skill has COMMITTED on the default branch, so a draft, an
|
|
158
|
+
// older version, or a deletion not yet committed that drops a
|
|
159
|
+
// write-denied bundled file does not turn the verdict true. A skill only
|
|
160
|
+
// on the draft is judged by its draft files, where they would land.
|
|
161
|
+
const committed = await skillFilesOnDefault(ctx, result.skill);
|
|
162
|
+
const verdicts = committed === null ? new Map() : await verdictsFor(ctx, committed.paths, committed.at);
|
|
163
|
+
const skill = {
|
|
164
|
+
...result.skill,
|
|
165
|
+
canWrite: committed !== null && committed.paths.every((p) => verdicts.get(p) === true),
|
|
166
|
+
};
|
|
167
|
+
if (!allowedTools)
|
|
168
|
+
return { ...result, skill };
|
|
169
|
+
return { ...result, skill, warnings: await allowedTools.check(ctx.user.email, result.skill.allowedTools) };
|
|
99
170
|
}));
|
|
100
171
|
}
|
|
172
|
+
/** The repo-relative `SKILL.md` of a skill, which `list_skills` judges it by. */
|
|
173
|
+
function skillMdOf(skill) {
|
|
174
|
+
return `${skill.path}/SKILL.md`;
|
|
175
|
+
}
|
|
101
176
|
/**
|
|
102
177
|
* The `branch` a tool call named, or undefined when it named none (the
|
|
103
178
|
* released catalog). A non-string or blank value is NOT a branch: it is taken
|
|
@@ -166,7 +241,9 @@ const LIST_SKILLS_DESCRIPTION = 'List the available skills (reusable specialist
|
|
|
166
241
|
'Discover what skills exist before specialist work, then `get_skill` to load one. ' +
|
|
167
242
|
'Pass `branch` to list the skills as they are on a draft branch instead of the released set — ' +
|
|
168
243
|
'what you need to try a skill you just wrote there; each skill that differs from the released ' +
|
|
169
|
-
'one comes back with `unmerged: true` and that branch, meaning nobody has approved it. '
|
|
244
|
+
'one comes back with `unmerged: true` and that branch, meaning nobody has approved it. ' +
|
|
245
|
+
'Each skill carries `canWrite`: whether you may change its SKILL.md directly on the default branch. ' +
|
|
246
|
+
`${CAN_WRITE_CLAUSE} `;
|
|
170
247
|
async function buildListSkillsDef(skillService, userEmail) {
|
|
171
248
|
return toolDef({
|
|
172
249
|
name: 'list_skills',
|
|
@@ -197,6 +274,13 @@ async function buildListSkillsDef(skillService, userEmail) {
|
|
|
197
274
|
'(it exists only on that branch, or was changed there): nobody has approved what it says.',
|
|
198
275
|
},
|
|
199
276
|
branch: { type: 'string', description: 'With `unmerged`: the branch the skill was read from.' },
|
|
277
|
+
canWrite: {
|
|
278
|
+
type: 'boolean',
|
|
279
|
+
description: 'Whether you may commit a change to its SKILL.md directly on the default branch, under that ' +
|
|
280
|
+
'branch\'s access rules — the same answer whichever branch you listed. `false`: use a branch ' +
|
|
281
|
+
'and a change request; `file_stat` with `explainAccess` says why — and, when you can manage that ' +
|
|
282
|
+
'path\'s access, who can approve.',
|
|
283
|
+
},
|
|
200
284
|
},
|
|
201
285
|
},
|
|
202
286
|
},
|
|
@@ -208,7 +292,9 @@ async function buildListSkillsDef(skillService, userEmail) {
|
|
|
208
292
|
const GET_SKILL_DESCRIPTION = 'Load a skill by name: returns its full instructions (SKILL.md body) to follow, plus the skill ' +
|
|
209
293
|
'folder path and the list of bundled files. Pass `file` to fetch a bundled file’s content ' +
|
|
210
294
|
'(e.g. a script) instead of the body. Loads the latest copy unless `version` names an earlier ' +
|
|
211
|
-
'one the skill declared, or `branch` names a draft to load it from. '
|
|
295
|
+
'one the skill declared, or `branch` names a draft to load it from. ' +
|
|
296
|
+
'The skill (or `file`) carries `canWrite`: whether you may change all of it directly on the default branch. ' +
|
|
297
|
+
`${CAN_WRITE_CLAUSE} `;
|
|
212
298
|
async function buildGetSkillDef(skillService, userEmail) {
|
|
213
299
|
return toolDef({
|
|
214
300
|
name: 'get_skill',
|
|
@@ -225,12 +311,30 @@ async function buildGetSkillDef(skillService, userEmail) {
|
|
|
225
311
|
description: 'The loaded skill: name, description, body, files, …. Read from a `branch` that changed it, ' +
|
|
226
312
|
'it also carries `unmerged: true` and that branch, and its `body` BEGINS with one line ' +
|
|
227
313
|
'saying the skill comes from that unmerged branch and is not approved — treat it as a ' +
|
|
228
|
-
'proposal you are testing, not as approved instructions.'
|
|
314
|
+
'proposal you are testing, not as approved instructions. `canWrite` is true only when you may ' +
|
|
315
|
+
'commit a change to EVERY file of the skill (SKILL.md and each bundled file) directly on the ' +
|
|
316
|
+
'default branch, whichever branch you read.',
|
|
317
|
+
properties: {
|
|
318
|
+
canWrite: {
|
|
319
|
+
type: 'boolean',
|
|
320
|
+
description: 'Whether you may commit a change to every file of the skill (SKILL.md and each bundled file) ' +
|
|
321
|
+
'directly on the default branch, under that branch\'s access rules — the same answer whichever ' +
|
|
322
|
+
'branch you read.',
|
|
323
|
+
},
|
|
324
|
+
},
|
|
229
325
|
},
|
|
230
326
|
file: {
|
|
231
327
|
type: 'object',
|
|
232
328
|
description: 'A bundled file: name, file, path, content — plus `unmerged` and `branch` when it came off ' +
|
|
233
|
-
'a branch that changed the skill (the note stays beside the content, never inside it)
|
|
329
|
+
'a branch that changed the skill (the note stays beside the content, never inside it) — and ' +
|
|
330
|
+
'`canWrite` for that file on the default branch.',
|
|
331
|
+
properties: {
|
|
332
|
+
canWrite: {
|
|
333
|
+
type: 'boolean',
|
|
334
|
+
description: 'Whether you may commit a change to this file directly on the default branch, under that ' +
|
|
335
|
+
'branch\'s access rules — the same answer whichever branch you read.',
|
|
336
|
+
},
|
|
337
|
+
},
|
|
234
338
|
},
|
|
235
339
|
warnings: {
|
|
236
340
|
type: 'array',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.tools.js","sourceRoot":"","sources":["../../../src/modules/skills/skills.tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,SAAS,EAAoB,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAGvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"skills.tools.js","sourceRoot":"","sources":["../../../src/modules/skills/skills.tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,SAAS,EAAoB,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAGvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAA+B,MAAM,0BAA0B,CAAC;AAErH;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG;IAC1B,IAAI,EAAE,QAAiB;IACvB,WAAW,EACT,8FAA8F;QAC9F,0FAA0F;QAC1F,sFAAsF;CACzF,CAAC;AAEF,gGAAgG;AAChG,MAAM,kBAAkB,GAAG;IACzB,IAAI,EAAE,QAAiB;IACvB,UAAU,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE;IAC3C,oBAAoB,EAAE,KAAK;CAC5B,CAAC;AAEF,yFAAyF;AACzF,MAAM,gBAAgB,GAAG;IACvB,IAAI,EAAE,QAAiB;IACvB,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,2CAA2C,EAAE;QACzG,IAAI,EAAE;YACJ,IAAI,EAAE,QAAiB;YACvB,WAAW,EACT,2FAA2F;gBAC3F,wCAAwC;SAC3C;QACD,OAAO,EAAE;YACP,IAAI,EAAE,QAAiB;YACvB,WAAW,EACT,0FAA0F;gBAC1F,6GAA6G;gBAC7G,kGAAkG;gBAClG,uFAAuF;gBACvF,uDAAuD;SAC1D;QACD,MAAM,EAAE,mBAAmB;KAC5B;IACD,QAAQ,EAAE,CAAC,MAAM,CAAC;IAClB,oBAAoB,EAAE,KAAK;CAC5B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAuB,EACvB,MAAc,EACd,QAAwB,EACxB,WAA+B,EAC/B,YAA2B;AAC3B,2GAA2G;AAC3G,YAAmC;AACnC;;;;GAIG;AACH,aAAsC;IAEtC;;;;OAIG;IACH,MAAM,WAAW,GAAG,CAAC,GAAgB,EAAE,KAAe,EAAE,EAAW,EAAiC,EAAE,CACpG,aAAa;QACX,CAAC,CAAC,0BAA0B,CAAC,aAAa,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;QACpE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7D;;;;;;;;;;;;;;;OAeG;IACH,MAAM,mBAAmB,GAAG,KAAK,EAC/B,GAAgB,EAChB,MAAyC,EACQ,EAAE;QACnD,IAAI,CAAC,aAAa,EAAE,cAAc,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QACzE,MAAM,WAAW,GAAG,aAAa,CAAC,kBAAkB,EAAE,CAAC;QACvD,IAAI,EAAiB,CAAC;QACtB,IAAI,SAAmB,CAAC;QACxB,IAAI,CAAC;YACH,EAAE,GAAG,MAAM,aAAa,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACzE,IAAI,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YACrB,SAAS,GAAG,MAAM,aAAa,CAAC,cAAc,CAAC,cAAc,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9F,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;IACjH,CAAC,CAAC;IAEF,QAAQ,CAAC,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,CAAC,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACxF,QAAQ,CAAC,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,CAAC,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACxF,QAAQ,CAAC,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACtF,QAAQ,CAAC,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACtF,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC7E,gBAAgB,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;IACpD,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;IAEhD,MAAM,CAAC,IAAI,CACT,0BAA0B,EAC1B,QAAQ,EACR,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,GAAgB,EAAE,EAAE;QAC3C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1F,qEAAqE;QACrE,yEAAyE;QACzE,qEAAqE;QACrE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IAClG,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,QAAQ,EACR,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,GAAgB,EAAE,EAAE;QAC3C,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;QAC5C,qEAAqE;QACrE,yEAAyE;QACzE,qEAAqE;QACrE,sEAAsE;QACtE,MAAM,SAAS,GAAG,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QACrE,IAAI,MAAM,KAAK,SAAS,IAAI,SAAS,EAAE,CAAC;YACtC,MAAM,IAAI,SAAS,CACjB,iGAAiG;gBAC/F,+EAA+E,EACjF,GAAG,CACJ,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5F,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,MAAM,CAAC;QAC9B,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5D,OAAO,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACpG,CAAC;QACD,0EAA0E;QAC1E,0EAA0E;QAC1E,8DAA8D;QAC9D,yEAAyE;QACzE,oEAAoE;QACpE,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/D,MAAM,QAAQ,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,EAAmB,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QACzH,MAAM,KAAK,GAAG;YACZ,GAAG,MAAM,CAAC,KAAK;YACf,QAAQ,EAAE,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;SACvF,CAAC;QACF,IAAI,CAAC,YAAY;YAAE,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC;QAC/C,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;IAC7G,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,SAAS,SAAS,CAAC,KAAuB;IACxC,OAAO,GAAG,KAAK,CAAC,IAAI,WAAW,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,IAA6B;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACrF,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,mBAAmB,CAAC,YAA2B,EAAE,SAA6B,EAAE,MAAc;IAC3G,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,oCAAoC,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CACrC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,GAAG,KAAK,+BAA+B,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5F,MAAM,IAAI,GAAG,8BAA8B,CAAC;IAC5C,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,QAAQ,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,IAAI,QAAQ,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC/C,0EAA0E;IAC1E,wEAAwE;IACxE,2EAA2E;IAC3E,iDAAiD;IACjD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAChD,IAAI,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM;YAAE,MAAM;QAC5D,MAAM,IAAI,KAAK,CAAC;QAChB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;YACvB,CAAC,CAAC,uDAAuD;YACzD,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,0DAA0D,CAAC;IAChF,CAAC;IACD,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,IAAY,EAAE,MAAkB,EAAE,KAAa;IACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,uBAAuB,IAAI,IAAI,EAAE,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7F,OAAO,oBAAoB,GAAG,IAAI,GAAG,oBAAoB,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;AACtF,CAAC;AAED,MAAM,uBAAuB,GAC3B,mGAAmG;IACnG,gGAAgG;IAChG,kDAAkD;IAClD,mFAAmF;IACnF,+FAA+F;IAC/F,+FAA+F;IAC/F,wFAAwF;IACxF,qGAAqG;IACrG,GAAG,gBAAgB,GAAG,CAAC;AAEzB,KAAK,UAAU,kBAAkB,CAAC,YAA2B,EAAE,SAAkB;IAC/E,OAAO,OAAO,CAAC;QACb,IAAI,EAAE,aAAa;QACnB,WAAW,EACT,uBAAuB;YACvB,CAAC,MAAM,mBAAmB,CAAC,YAAY,EAAE,SAAS,EAAE,gBAAgB,CAAC,aAAa,EAAE,kBAAkB,EAAE,uBAAuB,CAAC,CAAC,CAAC;QACpI,IAAI,EAAE,8BAA8B;QACpC,MAAM,EAAE,kBAAkB;QAC1B,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,IAAI,EAAE,OAAO;oBACb,WAAW,EAAE,mBAAmB;oBAChC,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,UAAU,EAAE;4BACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;4BACxB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;4BAC/B,OAAO,EAAE;gCACP,IAAI,EAAE,QAAQ;gCACd,WAAW,EACT,0FAA0F;oCAC1F,0EAA0E;6BAC7E;4BACD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;4BACxB,QAAQ,EAAE;gCACR,IAAI,EAAE,SAAS;gCACf,WAAW,EACT,wFAAwF;oCACxF,0FAA0F;6BAC7F;4BACD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sDAAsD,EAAE;4BAC/F,QAAQ,EAAE;gCACR,IAAI,EAAE,SAAS;gCACf,WAAW,EACT,6FAA6F;oCAC7F,8FAA8F;oCAC9F,kGAAkG;oCAClG,kCAAkC;6BACrC;yBACF;qBACF;iBACF;aACF;SACF;QACD,IAAI,EAAE,CAAC,QAAQ,CAAC;KACjB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,qBAAqB,GACzB,gGAAgG;IAChG,2FAA2F;IAC3F,+FAA+F;IAC/F,qEAAqE;IACrE,6GAA6G;IAC7G,GAAG,gBAAgB,GAAG,CAAC;AAEzB,KAAK,UAAU,gBAAgB,CAAC,YAA2B,EAAE,SAAkB;IAC7E,OAAO,OAAO,CAAC;QACb,IAAI,EAAE,WAAW;QACjB,WAAW,EACT,qBAAqB;YACrB,CAAC,MAAM,mBAAmB,CAAC,YAAY,EAAE,SAAS,EAAE,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,EAAE,qBAAqB,CAAC,CAAC,CAAC;QAC9H,IAAI,EAAE,4BAA4B;QAClC,MAAM,EAAE,gBAAgB;QACxB,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,4FAA4F;YACzG,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,6FAA6F;wBAC7F,wFAAwF;wBACxF,uFAAuF;wBACvF,+FAA+F;wBAC/F,8FAA8F;wBAC9F,4CAA4C;oBAC9C,UAAU,EAAE;wBACV,QAAQ,EAAE;4BACR,IAAI,EAAE,SAAS;4BACf,WAAW,EACT,8FAA8F;gCAC9F,gGAAgG;gCAChG,kBAAkB;yBACrB;qBACF;iBACF;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,4FAA4F;wBAC5F,6FAA6F;wBAC7F,iDAAiD;oBACnD,UAAU,EAAE;wBACV,QAAQ,EAAE;4BACR,IAAI,EAAE,SAAS;4BACf,WAAW,EACT,0FAA0F;gCAC1F,qEAAqE;yBACxE;qBACF;iBACF;gBACD,QAAQ,EAAE;oBACR,IAAI,EAAE,OAAO;oBACb,WAAW,EACT,kGAAkG;wBAClG,qEAAqE;oBACvE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC1B;gBACD,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,4FAA4F;iBAC1G;gBACD,QAAQ,EAAE;oBACR,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,WAAW,EAAE,+EAA+E;iBAC7F;aACF;SACF;QACD,IAAI,EAAE,CAAC,QAAQ,CAAC;KACjB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
2
|
+
import type { ToolContext } from './tool.contract.js';
|
|
3
|
+
/**
|
|
4
|
+
* What the read tools need to say whether a change can land directly: the
|
|
5
|
+
* access layer and the default branch's workspace. A function for the
|
|
6
|
+
* workspace, not a value, because first-run setup may name the default branch
|
|
7
|
+
* after the tools are mounted.
|
|
8
|
+
*/
|
|
9
|
+
export interface DefaultBranchWriteDeps {
|
|
10
|
+
accessControl: Pick<IAccessControl, 'canWriteBatchAtRef'>;
|
|
11
|
+
defaultWorkspaceId: () => string;
|
|
12
|
+
/**
|
|
13
|
+
* The files under a folder as COMMITTED at a ref of a workspace, repo-root
|
|
14
|
+
* relative — what `get_skill`'s whole-skill verdict covers: the skill's
|
|
15
|
+
* files as the default branch's `HEAD` has them, not as its working tree
|
|
16
|
+
* has them, so a bundled file deleted on disk but not yet committed is
|
|
17
|
+
* still judged. Absent, `get_skill` answers `false` for the whole skill.
|
|
18
|
+
*/
|
|
19
|
+
committedFiles?: {
|
|
20
|
+
/** The commit `ref` names right now, or null when it names none (an unborn HEAD). */
|
|
21
|
+
commitAtRef(workspaceId: string, ref: string): Promise<string | null>;
|
|
22
|
+
listFilesAtRef(workspaceId: string, ref: string, folder: string): Promise<string[]>;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The `canWrite` verdicts `list_skills`, `get_skill`, `list_files` and
|
|
27
|
+
* `read_file` report: whether the caller may commit a change to each
|
|
28
|
+
* repo-relative path DIRECTLY on the default branch, under the access rules
|
|
29
|
+
* COMMITTED there. `false` means "make the change on a branch and open a
|
|
30
|
+
* change request", never "you cannot write at all" — on a draft, anyone who
|
|
31
|
+
* may read a path may write it, so "writable on the branch read" would say
|
|
32
|
+
* yes exactly where a direct write is later refused.
|
|
33
|
+
*
|
|
34
|
+
* Always judged against the default branch, whichever branch the call read:
|
|
35
|
+
* that is where a direct change lands. And judged at that clone's `HEAD`, the
|
|
36
|
+
* way the direct write itself is gated (`assertCanWriteAtRef` in the git
|
|
37
|
+
* service), not from the rules as the working tree has them: a grant written
|
|
38
|
+
* to disk but not yet committed — a queued save, an edit in flight — would
|
|
39
|
+
* otherwise say `true` for a write that is then refused. ONE batched check
|
|
40
|
+
* per call.
|
|
41
|
+
*
|
|
42
|
+
* Fails closed: a read-only credential gets `false` for every path without
|
|
43
|
+
* the access layer being asked; a check that throws, or a path the answer
|
|
44
|
+
* leaves out, is `false`. Read the result with `=== true`. The one answer
|
|
45
|
+
* that is not closed is the gate's own: `null` — no rules at `HEAD` yet, a
|
|
46
|
+
* knowledge base being set up — is where the direct write is allowed
|
|
47
|
+
* (bootstrap, `assertCanWriteAtRef`), so every path is `true` there; a
|
|
48
|
+
* `false` would send the agent to a change request the gate does not ask
|
|
49
|
+
* for.
|
|
50
|
+
*
|
|
51
|
+
* Per caller and never cached here: a verdict belongs to one caller's answer,
|
|
52
|
+
* not to anything shared across users.
|
|
53
|
+
*/
|
|
54
|
+
export declare function defaultBranchWriteVerdicts(deps: DefaultBranchWriteDeps, ctx: Pick<ToolContext, 'scope' | 'user'>, paths: readonly string[],
|
|
55
|
+
/**
|
|
56
|
+
* The commit to judge at, when the caller has already pinned one — a
|
|
57
|
+
* caller that read a file set at a commit judges it at THAT commit, so a
|
|
58
|
+
* commit landing in between cannot pair one snapshot's files with
|
|
59
|
+
* another's rules. Default: `HEAD` of the default branch's clone.
|
|
60
|
+
*/
|
|
61
|
+
opts?: {
|
|
62
|
+
ref?: string;
|
|
63
|
+
}): Promise<Map<string, boolean>>;
|
|
64
|
+
/**
|
|
65
|
+
* The clause each of the four tools carries in its description. Said the
|
|
66
|
+
* same way everywhere so an agent reading any one of them learns the rule.
|
|
67
|
+
*/
|
|
68
|
+
export declare const CAN_WRITE_CLAUSE = "`canWrite: false` means use a branch and a change request, never a direct write; a read-only connection can do neither.";
|
|
69
|
+
//# sourceMappingURL=default-branch-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-branch-write.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/default-branch-write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,oBAAoB,CAAC,CAAC;IAC1D,kBAAkB,EAAE,MAAM,MAAM,CAAC;IACjC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE;QACf,qFAAqF;QACrF,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QACtE,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;KACrF,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,sBAAsB,EAC5B,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,CAAC,EACxC,KAAK,EAAE,SAAS,MAAM,EAAE;AACxB;;;;;GAKG;AACH,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAgB/B;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,4HAC8F,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `canWrite` verdicts `list_skills`, `get_skill`, `list_files` and
|
|
3
|
+
* `read_file` report: whether the caller may commit a change to each
|
|
4
|
+
* repo-relative path DIRECTLY on the default branch, under the access rules
|
|
5
|
+
* COMMITTED there. `false` means "make the change on a branch and open a
|
|
6
|
+
* change request", never "you cannot write at all" — on a draft, anyone who
|
|
7
|
+
* may read a path may write it, so "writable on the branch read" would say
|
|
8
|
+
* yes exactly where a direct write is later refused.
|
|
9
|
+
*
|
|
10
|
+
* Always judged against the default branch, whichever branch the call read:
|
|
11
|
+
* that is where a direct change lands. And judged at that clone's `HEAD`, the
|
|
12
|
+
* way the direct write itself is gated (`assertCanWriteAtRef` in the git
|
|
13
|
+
* service), not from the rules as the working tree has them: a grant written
|
|
14
|
+
* to disk but not yet committed — a queued save, an edit in flight — would
|
|
15
|
+
* otherwise say `true` for a write that is then refused. ONE batched check
|
|
16
|
+
* per call.
|
|
17
|
+
*
|
|
18
|
+
* Fails closed: a read-only credential gets `false` for every path without
|
|
19
|
+
* the access layer being asked; a check that throws, or a path the answer
|
|
20
|
+
* leaves out, is `false`. Read the result with `=== true`. The one answer
|
|
21
|
+
* that is not closed is the gate's own: `null` — no rules at `HEAD` yet, a
|
|
22
|
+
* knowledge base being set up — is where the direct write is allowed
|
|
23
|
+
* (bootstrap, `assertCanWriteAtRef`), so every path is `true` there; a
|
|
24
|
+
* `false` would send the agent to a change request the gate does not ask
|
|
25
|
+
* for.
|
|
26
|
+
*
|
|
27
|
+
* Per caller and never cached here: a verdict belongs to one caller's answer,
|
|
28
|
+
* not to anything shared across users.
|
|
29
|
+
*/
|
|
30
|
+
export async function defaultBranchWriteVerdicts(deps, ctx, paths,
|
|
31
|
+
/**
|
|
32
|
+
* The commit to judge at, when the caller has already pinned one — a
|
|
33
|
+
* caller that read a file set at a commit judges it at THAT commit, so a
|
|
34
|
+
* commit landing in between cannot pair one snapshot's files with
|
|
35
|
+
* another's rules. Default: `HEAD` of the default branch's clone.
|
|
36
|
+
*/
|
|
37
|
+
opts = {}) {
|
|
38
|
+
const verdicts = new Map(paths.map((p) => [p, false]));
|
|
39
|
+
if (ctx.scope !== 'write' || paths.length === 0)
|
|
40
|
+
return verdicts;
|
|
41
|
+
try {
|
|
42
|
+
const answer = await deps.accessControl.canWriteBatchAtRef(deps.defaultWorkspaceId(), opts.ref ?? 'HEAD', ctx.user.email, [
|
|
43
|
+
...new Set(paths),
|
|
44
|
+
]);
|
|
45
|
+
// Bootstrap: no rules at HEAD, and the gate lets the write through.
|
|
46
|
+
if (!answer)
|
|
47
|
+
return new Map(paths.map((p) => [p, true]));
|
|
48
|
+
for (const p of paths)
|
|
49
|
+
verdicts.set(p, answer.get(p) === true);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// A verdict that cannot be computed is `false`: the agent then proposes
|
|
53
|
+
// the change, which can land; a wrong `true` would offer a direct write
|
|
54
|
+
// that is refused.
|
|
55
|
+
}
|
|
56
|
+
return verdicts;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The clause each of the four tools carries in its description. Said the
|
|
60
|
+
* same way everywhere so an agent reading any one of them learns the rule.
|
|
61
|
+
*/
|
|
62
|
+
export const CAN_WRITE_CLAUSE = '`canWrite: false` means use a branch and a change request, never a direct write; a read-only connection can do neither.';
|
|
63
|
+
//# sourceMappingURL=default-branch-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-branch-write.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/default-branch-write.ts"],"names":[],"mappings":"AA0BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAA4B,EAC5B,GAAwC,EACxC,KAAwB;AACxB;;;;;GAKG;AACH,OAAyB,EAAE;IAE3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAkB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,CAAC,GAAG,IAAI,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE;YACxH,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC;SAClB,CAAC,CAAC;QACH,oEAAoE;QACpE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACzD,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,wEAAwE;QACxE,mBAAmB;IACrB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,yHAAyH,CAAC"}
|
|
@@ -43,4 +43,5 @@ export { createToolValidator, type ValidateToken, type ToolValidatorDeps } from
|
|
|
43
43
|
export { toolDef, BRANCH_INPUT, DEFAULTED_BRANCH_INPUT, type ToolDefSpec, type BranchDeclaration, } from './tool-def.js';
|
|
44
44
|
export { ToolError, hasHttpStatus, type ToolContext, type ToolHandler } from './tool.contract.js';
|
|
45
45
|
export type { JsonSchema, UtcpTool } from '../tool-registry/tool.contract.js';
|
|
46
|
+
export { CAN_WRITE_CLAUSE, defaultBranchWriteVerdicts, type DefaultBranchWriteDeps, } from './default-branch-write.js';
|
|
46
47
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EACL,OAAO,EACP,YAAY,EACZ,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClG,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EACL,OAAO,EACP,YAAY,EACZ,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClG,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,2BAA2B,CAAC"}
|
|
@@ -42,4 +42,5 @@
|
|
|
42
42
|
export { createToolValidator } from './validate-token.js';
|
|
43
43
|
export { toolDef, BRANCH_INPUT, DEFAULTED_BRANCH_INPUT, } from './tool-def.js';
|
|
44
44
|
export { ToolError, hasHttpStatus } from './tool.contract.js';
|
|
45
|
+
export { CAN_WRITE_CLAUSE, defaultBranchWriteVerdicts, } from './default-branch-write.js';
|
|
45
46
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,mBAAmB,EAA8C,MAAM,qBAAqB,CAAC;AACtG,OAAO,EACL,OAAO,EACP,YAAY,EACZ,sBAAsB,GAGvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAsC,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,mBAAmB,EAA8C,MAAM,qBAAqB,CAAC;AACtG,OAAO,EACL,OAAO,EACP,YAAY,EACZ,sBAAsB,GAGvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAsC,MAAM,oBAAoB,CAAC;AAElG,OAAO,EACL,gBAAgB,EAChB,0BAA0B,GAE3B,MAAM,2BAA2B,CAAC"}
|
|
@@ -23,6 +23,28 @@
|
|
|
23
23
|
* never be keyed on a path no file verb would accept. See
|
|
24
24
|
* `canonicalFileIdentity`.
|
|
25
25
|
*
|
|
26
|
+
* Workspace identity works the same way. The lock routes pass the `:id`
|
|
27
|
+
* path param, which Express URL-decodes, so a slashed branch's workspace
|
|
28
|
+
* arrives as `alice/feature`; the deletion gate and the git status probe ask
|
|
29
|
+
* with the encoded `alice%2Ffeature`. Every method keys rows on
|
|
30
|
+
* `canonicalWorkspaceId`, so both spellings are one workspace and a held file
|
|
31
|
+
* is seen whichever spelling asks.
|
|
32
|
+
*
|
|
33
|
+
* Unlike raw path spellings (below), rows written under the decoded workspace id before this
|
|
34
|
+
* landed are still honoured until they go: every read, heartbeat and release
|
|
35
|
+
* also matches that spelling, and an acquire is refused while a live row
|
|
36
|
+
* under it holds the same file. Otherwise a lock held across the upgrade
|
|
37
|
+
* would be invisible for up to its TTL — to the deletion gate and to a second
|
|
38
|
+
* editor alike. Nothing new is ever written under it, so once the last such
|
|
39
|
+
* row has expired or been released the extra spelling matches nothing.
|
|
40
|
+
*
|
|
41
|
+
* This covers rows LEFT by the previous version, not rows it is still
|
|
42
|
+
* writing: a previous-version process never looks at a canonical row, so no
|
|
43
|
+
* check here could make the two contend while both run. They never do — one
|
|
44
|
+
* server process owns the workspaces directory (see `branchLifecycle` in the
|
|
45
|
+
* workflow service; this service's deletion gate is in-process for the same
|
|
46
|
+
* reason), and an upgrade stops the old process before the new one starts.
|
|
47
|
+
*
|
|
26
48
|
* There is no transition handling for rows written under a raw spelling
|
|
27
49
|
* before this landed: such a row is now unreachable by name and expires on
|
|
28
50
|
* its own TTL. For one deploy an in-flight edit's lock can linger up to the
|
|
@@ -81,7 +103,7 @@ export declare class FileLockService {
|
|
|
81
103
|
* mode, so a stale coordination row can't launder a later edit acquire
|
|
82
104
|
* (or vice versa).
|
|
83
105
|
*/
|
|
84
|
-
acquire(
|
|
106
|
+
acquire(rawWorkspaceId: string, branch: string, rawPath: string, user: AuthUser, opts?: {
|
|
85
107
|
coordination?: boolean;
|
|
86
108
|
}): Promise<AcquireLockResult>;
|
|
87
109
|
/** `acquire` past the deletion gate. */
|
|
@@ -92,19 +114,19 @@ export declare class FileLockService {
|
|
|
92
114
|
* which will either succeed (taking over a stale lock) or surface the
|
|
93
115
|
* current holder.
|
|
94
116
|
*/
|
|
95
|
-
heartbeat(
|
|
117
|
+
heartbeat(rawWorkspaceId: string, branch: string, rawPath: string, user: AuthUser): Promise<FileLock>;
|
|
96
118
|
/**
|
|
97
119
|
* Idempotent release. Deletes the lock row when the caller holds it;
|
|
98
120
|
* silently no-ops otherwise (the lock may have expired and been taken
|
|
99
121
|
* over by someone else — that's not an error from the caller's POV).
|
|
100
122
|
*/
|
|
101
|
-
release(
|
|
123
|
+
release(rawWorkspaceId: string, branch: string, rawPath: string, user: AuthUser): Promise<void>;
|
|
102
124
|
/**
|
|
103
125
|
* Read the current lock for `(workspace, branch, path)`. Returns null when
|
|
104
126
|
* no one holds it, OR when the existing row is expired — expired locks
|
|
105
127
|
* are purged here lazily.
|
|
106
128
|
*/
|
|
107
|
-
get(
|
|
129
|
+
get(rawWorkspaceId: string, branch: string, rawPath: string): Promise<FileLock | null>;
|
|
108
130
|
/**
|
|
109
131
|
* Drop every lock held on `branch`, whoever holds it and whichever
|
|
110
132
|
* workspace it sits in, and answer how many rows went.
|
|
@@ -137,6 +159,6 @@ export declare class FileLockService {
|
|
|
137
159
|
* the "missed lock-release commit" the loud warning is for. Expired rows
|
|
138
160
|
* don't count (their holder is gone; they explain nothing).
|
|
139
161
|
*/
|
|
140
|
-
hasAnyActive(
|
|
162
|
+
hasAnyActive(rawWorkspaceId: string): Promise<boolean>;
|
|
141
163
|
}
|
|
142
164
|
//# sourceMappingURL=file-lock.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock.service.d.ts","sourceRoot":"","sources":["../../../src/modules/workflow/file-lock.service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"file-lock.service.d.ts","sourceRoot":"","sources":["../../../src/modules/workflow/file-lock.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAwC7F,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEzC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IAErD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IAEtE;;;;;OAKG;IACG,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAY5E;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,OAAO,CACX,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,QAAQ,EACd,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GAChC,OAAO,CAAC,iBAAiB,CAAC;IAsB7B,wCAAwC;YAC1B,gBAAgB;IAmH9B;;;;;OAKG;IACG,SAAS,CACb,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,QAAQ,CAAC;IAsCpB;;;;OAIG;IACG,OAAO,CACX,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC;IAehB;;;;OAIG;IACG,GAAG,CACP,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA0C3B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzD;;;;;;;OAOG;IACG,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAS7D"}
|