@gavana.ai/cli 0.2.0 → 0.2.2

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 (42) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +42 -2
  3. package/guides/creative-canvas.md +52 -0
  4. package/guides/generated-assets.md +7 -2
  5. package/guides/paid-action-safety.md +1 -1
  6. package/guides/sections-layout.md +3 -3
  7. package/guides/validation-recovery.md +9 -2
  8. package/package.json +1 -1
  9. package/src/canvas-agent-guide.mjs +3 -3
  10. package/src/canvas-agent-validation.mjs +30 -15
  11. package/src/capabilities.mjs +3 -1
  12. package/src/client.mjs +252 -0
  13. package/src/commands.mjs +25 -3
  14. package/src/config.mjs +50 -17
  15. package/src/guide-sources.mjs +12 -4
  16. package/src/mcp-targets.mjs +72 -0
  17. package/src/runner.mjs +205 -58
  18. package/src/tools/campaign_plan.mjs +2 -2
  19. package/src/tools/campaign_review.mjs +2 -2
  20. package/src/tools/campaign_start.mjs +2 -2
  21. package/src/tools/definitions.mjs +34 -0
  22. package/src/tools/element_archive.mjs +12 -0
  23. package/src/tools/element_collection_create.mjs +11 -0
  24. package/src/tools/element_collection_delete.mjs +12 -0
  25. package/src/tools/element_collection_list.mjs +13 -0
  26. package/src/tools/element_collection_update.mjs +12 -0
  27. package/src/tools/element_create.mjs +11 -0
  28. package/src/tools/element_get.mjs +12 -0
  29. package/src/tools/element_history.mjs +13 -0
  30. package/src/tools/element_list.mjs +13 -0
  31. package/src/tools/element_restore.mjs +12 -0
  32. package/src/tools/element_update.mjs +21 -0
  33. package/src/tools/element_update_collections.mjs +12 -0
  34. package/src/tools/image_tool.mjs +3 -3
  35. package/src/tools/registry.mjs +202 -0
  36. package/src/tools/schemas.mjs +63 -1
  37. package/src/tools/work_continue.mjs +42 -0
  38. package/src/tools/work_execute.mjs +12 -0
  39. package/src/tools/work_get.mjs +12 -0
  40. package/src/tools/work_prepare.mjs +21 -0
  41. package/src/tools/work_refresh.mjs +12 -0
  42. package/src/version.mjs +5 -7
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { elementCollectionReference } from "./schemas.mjs";
3
+
4
+ export function defineElementCollectionUpdate(client) {
5
+ return {
6
+ title: "Rename an Element collection",
7
+ description: "Rename a user-owned Element collection.",
8
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
9
+ inputSchema: z.object({ collectionId: elementCollectionReference, name: z.string().min(1).max(120) }),
10
+ handler: ({ collectionId, name }) => client.updateElementCollection(collectionId, { name }),
11
+ };
12
+ }
@@ -0,0 +1,11 @@
1
+ import { elementInput } from "./schemas.mjs";
2
+
3
+ export function defineElementCreate(client) {
4
+ return {
5
+ title: "Create a Gavana Element",
6
+ description: "Create a reusable visual Element from 1-8 existing image assets, guidelines, or both.",
7
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
8
+ inputSchema: elementInput,
9
+ handler: (input) => client.createElement(input),
10
+ };
11
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { pinnedElementReference } from "./schemas.mjs";
3
+
4
+ export function defineElementGet(client) {
5
+ return {
6
+ title: "Read a Gavana Element",
7
+ description: "Read one exact immutable Element revision. The user must provide an element:<id>@v<n> handle returned by list or history.",
8
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
9
+ inputSchema: z.object({ elementId: pinnedElementReference }),
10
+ handler: ({ elementId }) => client.getElement(elementId),
11
+ };
12
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { listOptions } from "./helpers.mjs";
3
+ import { listCursor, listLimit, mutableElementReference } from "./schemas.mjs";
4
+
5
+ export function defineElementHistory(client) {
6
+ return {
7
+ title: "Read Element version history",
8
+ description: "List immutable revisions for one Element, newest first.",
9
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
10
+ inputSchema: z.object({ elementId: mutableElementReference, limit: listLimit(100), cursor: listCursor }),
11
+ handler: ({ elementId, limit, cursor }) => client.listElementHistory(elementId, listOptions(limit, cursor)),
12
+ };
13
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { listOptions } from "./helpers.mjs";
3
+ import { listCursor, listLimit } from "./schemas.mjs";
4
+
5
+ export function defineElementList(client) {
6
+ return {
7
+ title: "List Gavana Elements",
8
+ description: "Search active or archived reusable visual Elements. Each result includes its current version-pinned handle.",
9
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
10
+ inputSchema: z.object({ query: z.string().max(240).optional(), state: z.enum(["active", "archived"]).optional(), limit: listLimit(100), cursor: listCursor }),
11
+ handler: ({ query, state, limit, cursor }) => client.listElements({ ...(query ? { query } : {}), ...(state ? { state } : {}) }, listOptions(limit, cursor)),
12
+ };
13
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { mutableElementReference } from "./schemas.mjs";
3
+
4
+ export function defineElementRestore(client) {
5
+ return {
6
+ title: "Restore a Gavana Element",
7
+ description: "Restore an archived Element when all of its source assets remain available.",
8
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
9
+ inputSchema: z.object({ elementId: mutableElementReference }),
10
+ handler: ({ elementId }) => client.restoreElement(elementId),
11
+ };
12
+ }
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ import { elementType, mutableElementReference, assetReference } from "./schemas.mjs";
3
+
4
+ export function defineElementUpdate(client) {
5
+ return {
6
+ title: "Update a Gavana Element",
7
+ description: "Create a new immutable revision for the current Element. Historical version handles stay unchanged.",
8
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
9
+ inputSchema: z
10
+ .object({
11
+ elementId: mutableElementReference,
12
+ name: z.string().min(1).max(160),
13
+ type: elementType,
14
+ sourceAssetIds: z.array(assetReference).max(8).default([]),
15
+ guidelines: z.string().max(2_000).optional(),
16
+ })
17
+ .strict()
18
+ .refine((value) => value.sourceAssetIds.length > 0 || Boolean(value.guidelines?.trim()), "Add at least one source asset or guideline."),
19
+ handler: ({ elementId, ...input }) => client.updateElement(elementId, input),
20
+ };
21
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { elementCollectionReference, mutableElementReference } from "./schemas.mjs";
3
+
4
+ export function defineElementUpdateCollections(client) {
5
+ return {
6
+ title: "Organize an Element in collections",
7
+ description: "Replace this Element's collection memberships without changing its visual revision.",
8
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
9
+ inputSchema: z.object({ elementId: mutableElementReference, collectionIds: z.array(elementCollectionReference).max(24) }),
10
+ handler: ({ elementId, collectionIds }) => client.updateElementCollections(elementId, collectionIds),
11
+ };
12
+ }
@@ -11,9 +11,9 @@ export function imageToolDefinition(operation, client) {
11
11
  title: `${operation === "generate" ? "Generate" : operation === "edit" ? "Edit" : "Vary"} canvas images`,
12
12
  description:
13
13
  operation === "generate"
14
- ? "Queue image generation into existing target image nodes. For an exact product, logo, face, garment, or other visual identity, do not rely on prompt-only generation: use asset_upload for a user-provided local image (or an existing node:/asset: handle), then call image_edit with references."
14
+ ? "Queue image generation into existing target image nodes. Campaign work needs a visible reference-led concept before output; reuse existing Canvas node:/asset: handles for product identity, brand-world, and typography/layout references. For an exact product, logo, face, garment, or other visual identity, do not rely on prompt-only generation: use a user-provided local image (or an existing node:/asset: handle), then call image_edit with references."
15
15
  : operation === "edit"
16
- ? "Queue an image edit using stable node: or asset: references. Use this for exact product or visual identity work; call asset_upload first when the user supplied a local image."
16
+ ? "Queue an image edit using stable node: or asset: references. Reuse a Canvas image by its existing handle rather than re-uploading it. Use this for exact product or visual identity work; call asset_upload first only when the user supplied a local image."
17
17
  : "Queue variations using an existing node: or asset: source.",
18
18
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
19
19
  inputSchema: imageSchema(),
@@ -48,7 +48,7 @@ export function imageToolDefinition(operation, client) {
48
48
  idempotencyKey: input.idempotencyKey,
49
49
  });
50
50
  const destinationResult = { requested: destination, canvasId: prepared.canvasId, targetNodeIds: prepared.targetNodeIds };
51
- if (input.wait === false) return { ...queued, destination: destinationResult };
51
+ if (input.wait !== true) return { ...queued, destination: destinationResult };
52
52
  const result = await client.waitForRun(queued.run || queued.id, withProgress({ timeoutMs: (input.timeoutSeconds || 900) * 1000 }, extra));
53
53
  return { ...result, destination: destinationResult };
54
54
  },
@@ -18,6 +18,7 @@
18
18
  * @property {("local"|"hosted")[]} surfaces Which servers advertise it.
19
19
  * @property {string} [hostedAlias] Deprecated verb-object name the hosted server still accepts.
20
20
  * @property {string[]} [scopes] OAuth scopes the hosted surface requires.
21
+ * @property {Record<string, string[]>} [conditionalScopes] Additional hosted OAuth scopes required when the named input field is present.
21
22
  * @property {boolean} [embedImage] Hosted surface inlines an image into the result.
22
23
  * @property {boolean} [legacyCampaign] Deprecated campaign tool, hidden unless explicitly enabled.
23
24
  * @property {boolean} [readOnlySurface] Advertised on the read-only surface.
@@ -278,6 +279,144 @@ export const GAVANA_TOOL_REGISTRY = Object.freeze([
278
279
  hermesDefault: false,
279
280
  annotations: {"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},
280
281
  },
282
+ {
283
+ name: "element_archive",
284
+ toolset: "elements",
285
+ surfaces: ["hosted","local"],
286
+ scopes: ["element:read","element:write"],
287
+ hostedOrder: 17,
288
+ hostedReadOnly: false,
289
+ hostedToolset: "elements",
290
+ localOrder: 48,
291
+ hermesDefault: false,
292
+ annotations: {"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},
293
+ },
294
+ {
295
+ name: "element_collection_create",
296
+ toolset: "elements",
297
+ surfaces: ["hosted","local"],
298
+ scopes: ["element:read","element:write"],
299
+ hostedOrder: 18,
300
+ hostedReadOnly: false,
301
+ hostedToolset: "elements",
302
+ localOrder: 50,
303
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},
304
+ },
305
+ {
306
+ name: "element_collection_delete",
307
+ toolset: "elements",
308
+ surfaces: ["hosted","local"],
309
+ scopes: ["element:read","element:write"],
310
+ hostedOrder: 19,
311
+ hostedReadOnly: false,
312
+ hostedToolset: "elements",
313
+ localOrder: 53,
314
+ hermesDefault: false,
315
+ annotations: {"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},
316
+ },
317
+ {
318
+ name: "element_collection_list",
319
+ toolset: "elements",
320
+ surfaces: ["hosted","local"],
321
+ scopes: ["element:read"],
322
+ readOnlySurface: true,
323
+ hostedOrder: 20,
324
+ hostedReadOnly: true,
325
+ hostedToolset: "elements",
326
+ localOrder: 49,
327
+ annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
328
+ },
329
+ {
330
+ name: "element_collection_update",
331
+ toolset: "elements",
332
+ surfaces: ["hosted","local"],
333
+ scopes: ["element:read","element:write"],
334
+ hostedOrder: 21,
335
+ hostedReadOnly: false,
336
+ hostedToolset: "elements",
337
+ localOrder: 52,
338
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
339
+ },
340
+ {
341
+ name: "element_create",
342
+ toolset: "elements",
343
+ surfaces: ["hosted","local"],
344
+ scopes: ["element:read","element:write"],
345
+ hostedOrder: 22,
346
+ hostedReadOnly: false,
347
+ hostedToolset: "elements",
348
+ localOrder: 45,
349
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},
350
+ },
351
+ {
352
+ name: "element_get",
353
+ toolset: "elements",
354
+ surfaces: ["hosted","local"],
355
+ scopes: ["element:read"],
356
+ readOnlySurface: true,
357
+ hostedOrder: 23,
358
+ hostedReadOnly: true,
359
+ hostedToolset: "elements",
360
+ localOrder: 43,
361
+ annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
362
+ },
363
+ {
364
+ name: "element_history",
365
+ toolset: "elements",
366
+ surfaces: ["hosted","local"],
367
+ scopes: ["element:read"],
368
+ readOnlySurface: true,
369
+ hostedOrder: 24,
370
+ hostedReadOnly: true,
371
+ hostedToolset: "elements",
372
+ localOrder: 44,
373
+ annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
374
+ },
375
+ {
376
+ name: "element_list",
377
+ toolset: "elements",
378
+ surfaces: ["hosted","local"],
379
+ scopes: ["element:read"],
380
+ readOnlySurface: true,
381
+ hostedOrder: 25,
382
+ hostedReadOnly: true,
383
+ hostedToolset: "elements",
384
+ localOrder: 42,
385
+ annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
386
+ },
387
+ {
388
+ name: "element_restore",
389
+ toolset: "elements",
390
+ surfaces: ["hosted","local"],
391
+ scopes: ["element:read","element:write"],
392
+ hostedOrder: 26,
393
+ hostedReadOnly: false,
394
+ hostedToolset: "elements",
395
+ localOrder: 47,
396
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
397
+ },
398
+ {
399
+ name: "element_update",
400
+ toolset: "elements",
401
+ surfaces: ["hosted","local"],
402
+ scopes: ["element:read","element:write"],
403
+ hostedOrder: 27,
404
+ hostedReadOnly: false,
405
+ hostedToolset: "elements",
406
+ localOrder: 46,
407
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},
408
+ },
409
+ {
410
+ name: "element_update_collections",
411
+ toolset: "elements",
412
+ surfaces: ["hosted","local"],
413
+ scopes: ["element:read","element:write"],
414
+ hostedOrder: 28,
415
+ hostedReadOnly: false,
416
+ hostedToolset: "elements",
417
+ localOrder: 51,
418
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
419
+ },
281
420
  {
282
421
  name: "guide_get",
283
422
  toolset: "canvas",
@@ -315,6 +454,7 @@ export const GAVANA_TOOL_REGISTRY = Object.freeze([
315
454
  surfaces: ["hosted","local"],
316
455
  hostedAlias: "generate_image_in_canvas",
317
456
  scopes: ["canvas:read","canvas:write","asset:read","image:generate","job:manage"],
457
+ conditionalScopes: { elements: ["element:read"] },
318
458
  hostedOrder: 13,
319
459
  paid: true,
320
460
  hostedReadOnly: false,
@@ -517,6 +657,68 @@ export const GAVANA_TOOL_REGISTRY = Object.freeze([
517
657
  hostedToolset: "models",
518
658
  annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
519
659
  },
660
+ {
661
+ name: "work_continue",
662
+ toolset: "canvas",
663
+ surfaces: ["hosted","local"],
664
+ scopes: ["canvas:read","canvas:write"],
665
+ hostedOrder: 31,
666
+ hostedReadOnly: false,
667
+ hostedToolset: "canvas",
668
+ localOrder: 56,
669
+ hermesDefault: false,
670
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
671
+ },
672
+ {
673
+ name: "work_execute",
674
+ toolset: "canvas",
675
+ surfaces: ["hosted","local"],
676
+ scopes: ["canvas:read","canvas:write","asset:read","image:generate"],
677
+ hostedOrder: 32,
678
+ paid: true,
679
+ hostedReadOnly: false,
680
+ hostedToolset: "canvas",
681
+ localOrder: 57,
682
+ hermesDefault: false,
683
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true},
684
+ },
685
+ {
686
+ name: "work_get",
687
+ toolset: "canvas",
688
+ surfaces: ["hosted","local"],
689
+ scopes: ["canvas:read"],
690
+ readOnlySurface: true,
691
+ hostedOrder: 30,
692
+ hostedReadOnly: true,
693
+ hostedToolset: "canvas",
694
+ localOrder: 55,
695
+ hermesDefault: false,
696
+ annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
697
+ },
698
+ {
699
+ name: "work_prepare",
700
+ toolset: "canvas",
701
+ surfaces: ["hosted","local"],
702
+ scopes: ["canvas:read","canvas:write","asset:read"],
703
+ hostedOrder: 29,
704
+ hostedReadOnly: false,
705
+ hostedToolset: "canvas",
706
+ localOrder: 54,
707
+ hermesDefault: false,
708
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},
709
+ },
710
+ {
711
+ name: "work_refresh",
712
+ toolset: "canvas",
713
+ surfaces: ["hosted","local"],
714
+ scopes: ["canvas:read","canvas:write","image:generate"],
715
+ hostedOrder: 33,
716
+ hostedReadOnly: false,
717
+ hostedToolset: "canvas",
718
+ localOrder: 58,
719
+ hermesDefault: false,
720
+ annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true},
721
+ },
520
722
  ]);
521
723
 
522
724
  /** Canonical name for a tool name or deprecated hosted alias. */
@@ -14,6 +14,29 @@ const assetReference = z
14
14
  .string()
15
15
  .regex(/^asset:(?:[A-Za-z0-9_-]{1,180}:)?[A-Za-z0-9_-]{1,180}$/)
16
16
  .describe("Stable asset:<id> or shared asset:<ownerUid>:<id> handle.");
17
+ const pinnedElementReference = z
18
+ .string()
19
+ .regex(/^element:[A-Za-z0-9_-]{1,180}@v[1-9][0-9]*$/)
20
+ .describe("Exact immutable Element revision handle element:<id>@v<n>.");
21
+ const mutableElementReference = z
22
+ .string()
23
+ .regex(/^element:[A-Za-z0-9_-]{1,180}$/)
24
+ .describe("Current Element handle element:<id>. Mutations never target a historical revision.");
25
+ const elementCollectionReference = z
26
+ .string()
27
+ .regex(/^element-collection:[A-Za-z0-9_-]{1,180}$/)
28
+ .describe("Stable Element collection handle element-collection:<id>.");
29
+ const elementType = z.enum(["character", "product/object", "environment", "style", "material/texture", "lighting"]);
30
+ const elementInput = z
31
+ .object({
32
+ name: z.string().min(1).max(160),
33
+ type: elementType,
34
+ sourceAssetIds: z.array(assetReference).max(8).default([]),
35
+ guidelines: z.string().max(2_000).optional(),
36
+ collectionIds: z.array(elementCollectionReference).max(24).optional(),
37
+ })
38
+ .strict()
39
+ .refine((value) => value.sourceAssetIds.length > 0 || Boolean(value.guidelines?.trim()), "Add at least one source asset or guideline.");
17
40
  const imageReferenceInput = z
18
41
  .union([
19
42
  z.union([nodeReference, assetReference]),
@@ -66,6 +89,18 @@ const runReference = z
66
89
  .string()
67
90
  .regex(/^run:[A-Za-z0-9_-]{1,180}$/)
68
91
  .describe("Opaque run:<id> handle. Image and Action Runs are temporary; Recipe Runs currently persist.");
92
+ const workReference = z
93
+ .string()
94
+ .regex(/^work:[A-Za-z0-9_-]{1,180}$/)
95
+ .describe("Stable work:<id> handle returned by work_prepare.");
96
+ const workReferenceHandle = z.string().regex(/^(?:node|asset):(?:[A-Za-z0-9_-]{1,180}:)?[A-Za-z0-9_-]{1,180}$/);
97
+ const workReferenceInput = z
98
+ .union([
99
+ workReferenceHandle,
100
+ z.object({ handle: workReferenceHandle, role: z.enum(["identity", "style", "product"]).optional() }).strict(),
101
+ ])
102
+ .describe("Durable node: or asset: reference, optionally with an identity, style, or product role.");
103
+ const workIdempotencyKey = z.string().min(8).max(200).describe("Required caller-stable retry key. Reuse it only for the exact same work action.");
69
104
  const campaignProductReference = z
70
105
  .string()
71
106
  .max(400)
@@ -84,6 +119,18 @@ const approvedOutputReferences = z
84
119
  const campaignIdempotencyKey = z.string().min(8).max(200).describe("Required caller-stable retry key. Reuse it only for the exact same campaign action.");
85
120
  const recipeForkIdempotencyKey = z.string().min(8).max(200).describe("Required caller-stable retry key. Reuse it only for the exact same Recipe fork.");
86
121
  const campaignAspectRatio = z.enum(["1:1", "4:5", "3:4", "16:9", "9:16"]);
122
+ const elementGenerationInput = z
123
+ .union([
124
+ pinnedElementReference,
125
+ z
126
+ .object({
127
+ handle: pinnedElementReference,
128
+ role: z.enum(["identity", "construction", "texture", "fit", "style"]).optional(),
129
+ influence: z.number().min(0).max(1).optional(),
130
+ })
131
+ .strict(),
132
+ ])
133
+ .describe("A version-pinned Element, optionally with the visual role and influence to apply.");
87
134
  const MAX_LOCAL_REFERENCE_IMAGE_BYTES = 50 * 1024 * 1024;
88
135
  const revisionFields = {
89
136
  baseRevision: z.string().min(1).optional().describe("Revision returned by canvas_get. Omit to read the latest revision immediately before the write."),
@@ -107,13 +154,19 @@ function imageSchema() {
107
154
  .array(imageReferenceInput)
108
155
  .max(16)
109
156
  .optional(),
157
+ elements: z
158
+ .array(elementGenerationInput)
159
+ .max(8)
160
+ .refine((items) => new Set(items.map((item) => (typeof item === "string" ? item : item.handle))).size === items.length, "Element handles must be unique.")
161
+ .optional()
162
+ .describe("Up to eight version-pinned Elements. Element source images share the 16-image reference limit."),
110
163
  source: z.union([nodeReference, assetReference]).optional(),
111
164
  connectionId: z.string().max(180).optional(),
112
165
  model: z.union([modelReference, rawModelId]).optional(),
113
166
  size: z.string().max(80).optional(),
114
167
  quality: z.string().max(80).optional(),
115
168
  count: z.number().int().min(1).max(4).optional(),
116
- wait: z.boolean().default(true),
169
+ wait: z.boolean().default(false).describe("Return after queueing by default. Set true to wait for the completed Run."),
117
170
  timeoutSeconds: z.number().min(1).max(3_600).default(900),
118
171
  });
119
172
  }
@@ -140,6 +193,10 @@ export {
140
193
  actionReference,
141
194
  approvedOutputReferences,
142
195
  assetReference,
196
+ elementCollectionReference,
197
+ elementGenerationInput,
198
+ elementInput,
199
+ elementType,
143
200
  campaignAspectRatio,
144
201
  campaignIdempotencyKey,
145
202
  campaignProductReference,
@@ -154,7 +211,9 @@ export {
154
211
  listCursor,
155
212
  listLimit,
156
213
  modelReference,
214
+ mutableElementReference,
157
215
  nodeReference,
216
+ pinnedElementReference,
158
217
  rawModelId,
159
218
  recipeForkIdempotencyKey,
160
219
  recipeReference,
@@ -162,4 +221,7 @@ export {
162
221
  runReference,
163
222
  validateImageInput,
164
223
  videoImageReference,
224
+ workIdempotencyKey,
225
+ workReference,
226
+ workReferenceInput,
165
227
  };
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ import { workIdempotencyKey, workReference } from "./schemas.mjs";
3
+
4
+ const workDecision = z
5
+ .object({
6
+ workId: workReference,
7
+ action: z.enum(["answer", "select_direction", "adjust", "acknowledge_canvas"]).optional(),
8
+ rebase: z.literal(true).optional(),
9
+ answer: z.string().min(1).max(2_000).optional(),
10
+ directionId: z.string().min(1).max(400).optional(),
11
+ adjustment: z.string().min(1).max(2_000).optional(),
12
+ idempotencyKey: workIdempotencyKey,
13
+ })
14
+ .strict()
15
+ .superRefine((value, context) => {
16
+ if (value.rebase === true && value.action && value.action !== "acknowledge_canvas") context.addIssue({ code: z.ZodIssueCode.custom, path: ["action"], message: "rebase may only be used with acknowledge_canvas." });
17
+ if (value.rebase === true || value.action === "acknowledge_canvas") {
18
+ for (const field of ["answer", "directionId", "adjustment"]) {
19
+ if (value[field] !== undefined) context.addIssue({ code: z.ZodIssueCode.custom, path: [field], message: `${field} is not allowed when acknowledging Canvas changes.` });
20
+ }
21
+ return;
22
+ }
23
+ if (!value.action) {
24
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ["action"], message: "action is required unless rebase is true." });
25
+ return;
26
+ }
27
+ const required = value.action === "answer" ? "answer" : value.action === "select_direction" ? "directionId" : "adjustment";
28
+ if (!value[required]) context.addIssue({ code: z.ZodIssueCode.custom, path: [required], message: `${required} is required for ${value.action}.` });
29
+ for (const field of ["answer", "directionId", "adjustment"]) {
30
+ if (field !== required && value[field] !== undefined) context.addIssue({ code: z.ZodIssueCode.custom, path: [field], message: `${field} is not allowed for ${value.action}.` });
31
+ }
32
+ });
33
+
34
+ export function defineWorkContinue(client) {
35
+ return {
36
+ title: "Continue a Gavana campaign work",
37
+ description: "Answer one critical question, select one direction, adjust the brief, or acknowledge a changed Canvas with rebase. This updates planning only and never starts paid generation.",
38
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
39
+ inputSchema: workDecision,
40
+ handler: ({ workId, ...input }) => client.continueWork(workId, input),
41
+ };
42
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { workIdempotencyKey, workReference } from "./schemas.mjs";
3
+
4
+ export function defineWorkExecute(client) {
5
+ return {
6
+ title: "Execute one selected Gavana campaign direction",
7
+ description: "Start the selected work only after explicit confirmation. This may incur provider cost and returns immediately with durable work progress; do not retry automatically.",
8
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
9
+ inputSchema: z.object({ workId: workReference, confirm: z.literal(true), idempotencyKey: workIdempotencyKey }).strict(),
10
+ handler: ({ workId, ...input }) => client.executeWork(workId, input),
11
+ };
12
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { workReference } from "./schemas.mjs";
3
+
4
+ export function defineWorkGet(client) {
5
+ return {
6
+ title: "Read a Gavana work",
7
+ description: "Read a durable snapshot of the brief, directions, selection, delivery status, and Canvas synchronization state. This never polls providers or writes Work/Canvas.",
8
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
9
+ inputSchema: z.object({ workId: workReference }).strict(),
10
+ handler: ({ workId }) => client.getWork(workId),
11
+ };
12
+ }
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ import { workIdempotencyKey, workReferenceInput } from "./schemas.mjs";
3
+
4
+ const canvasId = z.string().regex(/^canvas:[A-Za-z0-9_-]{1,180}(?::[A-Za-z0-9_-]{1,180})?$/).describe("Optional existing Canvas for the Work projection.");
5
+
6
+ export function defineWorkPrepare(client) {
7
+ return {
8
+ title: "Prepare a chat-first Gavana campaign",
9
+ description: "Create a social-campaign brief, exactly three creative directions, and a recommendation without starting paid generation. Non-social-only campaigns must use another workflow. Stop for a user choice after this call.",
10
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
11
+ inputSchema: z
12
+ .object({
13
+ request: z.string().min(1).max(8_000).describe("The user's campaign request."),
14
+ references: z.array(workReferenceInput).max(12).optional(),
15
+ canvasId: canvasId.optional(),
16
+ idempotencyKey: workIdempotencyKey,
17
+ })
18
+ .strict(),
19
+ handler: (input) => client.prepareWork(input),
20
+ };
21
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { workReference } from "./schemas.mjs";
3
+
4
+ export function defineWorkRefresh(client) {
5
+ return {
6
+ title: "Refresh a Gavana work",
7
+ description: "Poll the selected provider job and reconcile progress into durable Work and Canvas state. This is owner-only and may write status or finalized output metadata.",
8
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
9
+ inputSchema: z.object({ workId: workReference }).strict(),
10
+ handler: ({ workId }) => client.refreshWork(workId),
11
+ };
12
+ }
package/src/version.mjs CHANGED
@@ -1,12 +1,10 @@
1
1
  // The single source of the CLI package version.
2
2
  //
3
- // Its own module because three things need it and two of them must not drag in
4
- // the tool registry: client.mjs stamps every request's User-Agent, the stdio MCP
5
- // server advertises it as its server version, and capabilities.mjs re-exports it
6
- // for `gavana version`. Reading it from a literal is what let the User-Agent
7
- // report 0.1.0 for the whole life of 0.1.1, and let the MCP server advertise
8
- // 0.1.0 while packaged as 0.1.1.
3
+ // Its own module because the client stamps every request's User-Agent while the
4
+ // CLI reports and re-exports the same version without dragging in the tool
5
+ // registry. Reading it from a literal is what previously let published package
6
+ // metadata and the runtime version drift apart.
9
7
  //
10
8
  // scripts/gavana-mcp-tool-contract.test.mjs asserts this equals
11
9
  // packages/cli/package.json, so the two cannot drift.
12
- export const GAVANA_CLI_VERSION = "0.2.0";
10
+ export const GAVANA_CLI_VERSION = "0.2.2";