Skip to content

Commit b5bd053

Browse files
authored
Merge pull request #5498 from nodetool-ai/claude/m1-sub-agents-51bghx
2 parents b13ca2d + 003b99d commit b5bd053

71 files changed

Lines changed: 3038 additions & 63 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

electron/src/__tests__/verifyBackendBundle.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function writeValidBundle(dir: string): void {
6868
);
6969
writeShippedSandboxPacks(dir);
7070
writeShippedSystemSkills(dir);
71+
writeShippedFonts(dir);
7172
fs.mkdirSync(path.join(dir, "js-sandbox-worker"), { recursive: true });
7273
fs.writeFileSync(
7374
path.join(dir, "js-sandbox-worker", "worker-entry.js"),
@@ -98,6 +99,16 @@ const SANDBOX_PACKS_DIR = path.join(
9899
"sandbox-packs"
99100
);
100101

102+
const FONTS_DIR = path.join(
103+
__dirname,
104+
"..",
105+
"..",
106+
"..",
107+
"packages",
108+
"timeline",
109+
"fonts"
110+
);
111+
101112
const SYSTEM_SKILLS_DIR = path.join(
102113
__dirname,
103114
"..",
@@ -156,6 +167,23 @@ function writeShippedSystemSkills(dir: string): void {
156167
}
157168
}
158169

170+
/**
171+
* Stage every bundled font file the repo ships.
172+
*
173+
* Same reason as the packs and skills above: the script cross-checks the
174+
* staged set against `packages/timeline/fonts/` inside a checkout, so a
175+
* fixture staging none of them is an incomplete bundle. The bytes are a stub —
176+
* what the check reads is presence, and no face is parsed here.
177+
*/
178+
function writeShippedFonts(dir: string): void {
179+
const fontDir = path.join(dir, "fonts");
180+
fs.mkdirSync(fontDir, { recursive: true });
181+
for (const entry of fs.readdirSync(FONTS_DIR)) {
182+
if (!/\.(ttf|otf|txt)$/i.test(entry)) continue;
183+
fs.writeFileSync(path.join(fontDir, entry), "x");
184+
}
185+
}
186+
159187
function writeStagedPackage(
160188
dir: string,
161189
name: string,
@@ -383,6 +411,37 @@ describe("verify-backend-bundle", () => {
383411
expect(output).toContain(`system skills staged: ${staged}`);
384412
});
385413

414+
// C6, and the check T17 was required to observe failing: the packaged
415+
// backend registers these faces before it draws a title, so an unstaged one
416+
// silently renders every clip in that family in a host font.
417+
it("fails when a bundled font file is not staged", () => {
418+
const fonts = fs.readdirSync(path.join(tempDir, "fonts")).sort();
419+
expect(fonts).toContain("BebasNeue-Regular.ttf");
420+
fs.rmSync(path.join(tempDir, "fonts", "BebasNeue-Regular.ttf"));
421+
const { status, output } = runVerify(tempDir);
422+
expect(status).toBe(1);
423+
expect(output).toContain("Bundled font file(s) not staged under fonts/");
424+
expect(output).toContain("BebasNeue-Regular.ttf");
425+
});
426+
427+
// A face without its licence is a licensing failure, not a cosmetic one
428+
// (C7), so the OFL files are checked with the faces rather than beside them.
429+
it("fails when a font licence is not staged", () => {
430+
fs.rmSync(path.join(tempDir, "fonts", "OFL-Inter.txt"));
431+
const { status, output } = runVerify(tempDir);
432+
expect(status).toBe(1);
433+
expect(output).toContain("OFL-Inter.txt");
434+
});
435+
436+
it("reports the bundled font files it found", () => {
437+
const { status, output } = runVerify(tempDir);
438+
expect(status).toBe(0);
439+
// A count, so the check cannot pass by having matched nothing.
440+
const staged = fs.readdirSync(path.join(tempDir, "fonts")).length;
441+
expect(staged).toBeGreaterThan(0);
442+
expect(output).toContain(`${staged} bundled font file(s) staged`);
443+
});
444+
386445
it("fails when server.mjs itself is missing", () => {
387446
fs.rmSync(path.join(tempDir, "server.mjs"));
388447
const { status, output } = runVerify(tempDir);

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/agents/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"turndown": "^7.2.2"
7474
},
7575
"devDependencies": {
76+
"@nodetool-ai/video-nodes": "*",
7677
"@stryker-mutator/core": "^9.6.1",
7778
"@stryker-mutator/vitest-runner": "^9.6.1",
7879
"@types/chrome-remote-interface": "^0.33.0",

packages/agents/src/capabilities/timelines.specs.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,72 @@ export const editTimelineSpec: CapabilitySpec = {
353353
}
354354
};
355355

356+
export const SET_TIMELINE_DOCUMENT_SCHEMA: JsonSchema = {
357+
type: "object",
358+
properties: {
359+
timeline_id: {
360+
type: "string",
361+
description: "Timeline sequence id (from list_timelines)."
362+
},
363+
document: {
364+
type: "object",
365+
description:
366+
"The whole document to store: {tracks, clips, markers, transcript?, " +
367+
"scriptEnabled?}. It replaces the stored one field for field, so " +
368+
"anything you leave out is dropped — read the current document with " +
369+
"get_timeline and send it back changed, rather than sending only the " +
370+
"part you edited. `markers` may be omitted and defaults to an empty " +
371+
"list."
372+
},
373+
fps: {
374+
type: "number",
375+
description:
376+
"Frame rate to store with the sequence. Omit to keep the current one."
377+
},
378+
width: {
379+
type: "number",
380+
description: "Render width. Omit to keep the current one."
381+
},
382+
height: {
383+
type: "number",
384+
description: "Render height. Omit to keep the current one."
385+
},
386+
expected_updated_at: {
387+
type: "string",
388+
description:
389+
"The `updated_at` the document was read at. When it no longer " +
390+
"matches the stored row the write is refused as a conflict instead " +
391+
"of overwriting whoever changed it in between."
392+
},
393+
snapshot_name: {
394+
type: "string",
395+
description:
396+
"Label for the snapshot taken before the write, e.g. 'before the " +
397+
"title pass'."
398+
}
399+
},
400+
required: ["timeline_id", "document"]
401+
};
402+
403+
export const setTimelineDocumentSpec: CapabilitySpec = {
404+
name: "set_timeline_document",
405+
description:
406+
"Write a whole timeline document at once — every track, clip, marker and " +
407+
"animation in one call, instead of a script of edit_timeline ops. Reach " +
408+
"for it when you are authoring a cut from scratch or restructuring one " +
409+
"wholesale; edit_timeline stays the better tool for a few targeted " +
410+
"changes to a cut that already exists. The document is validated before " +
411+
"anything is written: errors refuse the write and come back as issues, " +
412+
"so a document that would not render never reaches the sequence. The " +
413+
"state it replaces is snapshotted as a manual version first, so the " +
414+
"write is undoable with restore_timeline_version, and the validation of " +
415+
"what actually landed is returned with the result.",
416+
inputSchema: SET_TIMELINE_DOCUMENT_SCHEMA,
417+
category: "write",
418+
userMessage: (params) =>
419+
`Writing the document of timeline ${String(params["timeline_id"])}`
420+
};
421+
356422
export const validateTimelineSpec: CapabilitySpec = {
357423
name: "validate_timeline",
358424
description:
@@ -473,6 +539,7 @@ export const timelinesSpecs: readonly CapabilitySpec[] = [
473539
deleteTimelineVersionSpec,
474540
editTimelineSpec,
475541
validateTimelineSpec,
542+
setTimelineDocumentSpec,
476543
previewTimelineFrameSpec,
477544
deleteTimelineSpec
478545
];

0 commit comments

Comments
 (0)