@gavana.ai/cli 0.2.1 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog — @gavana.ai/cli
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### Changed
6
+
7
+ - Image commands and MCP tools now return immediately after the durable Canvas
8
+ targets and Run are queued. Use `--wait` in the CLI or `wait: true` in an MCP
9
+ call when the completed image is required in the same interaction.
10
+ - Distinct local image references upload concurrently while their original
11
+ reference order and duplicate-upload reuse remain stable.
12
+
3
13
  ## 0.2.1
4
14
 
5
15
  ### Fixed
package/README.md CHANGED
@@ -114,6 +114,7 @@ gavana action list
114
114
  gavana action get action:resize
115
115
  gavana action run action:resize --input ./product.png --destination agent-canvas --width 1080 --height 1350
116
116
  gavana image generate --destination agent-canvas --model model:OPAQUE_MODEL_KEY --prompt "A studio product photograph"
117
+ gavana image generate --destination agent-canvas --prompt "A studio product photograph" --wait
117
118
  gavana model list --capability video.generate
118
119
  gavana video generate --model model:OPAQUE_MODEL_KEY --prompt "A slow product turntable" --duration 15 --aspect-ratio 9:16 --download ./turntable.mp4
119
120
  gavana video generate --model model:OPAQUE_MODEL_KEY --prompt "Animate the fabric naturally" --first-frame ./product.png --no-wait
@@ -179,8 +180,13 @@ text-only Recipes.
179
180
 
180
181
  Recipe, image, and Action work returns a shared `run:` handle with typed outputs,
181
182
  duration estimates, observed queue/execution timing, and stable retry guidance.
182
- Default waiting and `run get|wait|cancel` require `job:manage`; a start-only
183
- token can use `--no-wait` with a signed webhook.
183
+ Image commands return as soon as Gavana has created the durable Canvas targets
184
+ and queued the Run, so the caller can continue while generation proceeds. Add
185
+ `--wait` only when the completed image is needed in the same command. `--no-wait`
186
+ remains accepted for existing image scripts. Recipe and Action commands retain
187
+ their existing wait-by-default behavior. Waiting and `run get|wait|cancel`
188
+ require `job:manage`; a start-only token can queue image work directly or use
189
+ `--no-wait` with a signed webhook.
184
190
  The legacy `job:` handle remains an image and Action compatibility alias; it is
185
191
  never used for Recipes. Image and Action `run:` and `job:` handles point to the
186
192
  same temporary record and expire together:
@@ -25,7 +25,7 @@ order: 6
25
25
 
26
26
  ## Paid execution
27
27
 
28
- Generation is allowed only after explicit current-turn user intent. Start one run with one caller-stable idempotency key. Poll the returned Run or Job; do not start another run while waiting. A terminal failure must be reported without automatic retry.
28
+ Generation is allowed only after explicit current-turn user intent. Start one run with one caller-stable idempotency key. Image tools return durable queued progress by default; report that progress immediately and do not automatically call `run_wait`. Call `run_wait` only when the current user explicitly needs the completed asset in this same interaction. Otherwise, a later `run_get` or Canvas read can observe progress and the durable output. Do not start another run while one is pending. A terminal failure must be reported without automatic retry.
29
29
 
30
30
  ## Completion
31
31
 
@@ -15,7 +15,7 @@ Start paid work only when the current user message explicitly asks to run or gen
15
15
  ## Retry boundary
16
16
 
17
17
  - Use one stable idempotency key for one intended paid operation.
18
- - Poll the returned handle with status tools.
18
+ - Return an image Run's durable queued progress immediately. Do not call `run_wait` unless the current user explicitly needs the completed asset in this same interaction; otherwise observe it later with `run_get` or a Canvas read.
19
19
  - Never automatically retry a terminal failure, timeout, disconnect, or ambiguous provider response with a new key.
20
20
  - Ask for new user intent before any new paid attempt.
21
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gavana.ai/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "JSON-first command-line client for the Gavana Canvas API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,10 +4,10 @@
4
4
  // ./canvas-agent-validation.mjs in guide version 1.5.0.
5
5
  import { GAVANA_CANVAS_GUIDE_SOURCES } from "./guide-sources.mjs";
6
6
 
7
- export const GAVANA_CANVAS_GUIDE_VERSION = "1.5.0";
7
+ export const GAVANA_CANVAS_GUIDE_VERSION = "1.5.1";
8
8
  export const GAVANA_CANVAS_GUIDE_INDEX_URI = `gavana://guides/canvas/v${GAVANA_CANVAS_GUIDE_VERSION.split(".")[0]}/index`;
9
9
  export const GAVANA_CANVAS_GUIDE_WORKFLOW_INSTRUCTION =
10
- "For the first canvas task in a session, and before any unfamiliar canvas operation, inspect the relevant guide through MCP resources or call guide_search then guide_get. Before changing an existing canvas, call canvas_get. For spatial, multi-node, or destructive work, call canvas_validate with the proposed operations, apply one revision-safe atomic batch, then call canvas_validate again. Campaign work must read creative-canvas and use a reference-led concept before image output. Before image or video generation, call model_list for the required capability and use its exact model: handle; a bare model name does not select a saved connection. If no matching model is returned, report that the agent account cannot access the connection instead of asking the user to add a key again. When a standalone image fallback uses visual references, forward every exact node:/asset: handle (and each known role) in the image request; never reduce that work to a prompt-only generation. After an image or workflow finishes, read completionReview. Never claim Done while completionReview.doneClaimAllowed is false, delivery is pending, failed, or non-durable, or blocking findings remain. Render campaign compositions for visual inspection. Treat legacy warnings as review items, not permission to rewrite unrelated work.";
10
+ "For the first canvas task in a session, and before any unfamiliar canvas operation, inspect the relevant guide through MCP resources or call guide_search then guide_get. Before changing an existing canvas, call canvas_get. For spatial, multi-node, or destructive work, call canvas_validate with the proposed operations, apply one revision-safe atomic batch, then call canvas_validate again. Campaign work must read creative-canvas and use a reference-led concept before image output. Before image or video generation, call model_list for the required capability and use its exact model: handle; a bare model name does not select a saved connection. If no matching model is returned, report that the agent account cannot access the connection instead of asking the user to add a key again. When a standalone image fallback uses visual references, forward every exact node:/asset: handle (and each known role) in the image request; never reduce that work to a prompt-only generation. Image tools return durable queued progress by default; report it immediately and call run_wait only when the current user explicitly needs the completed asset in the same interaction. A later run_get or Canvas read can observe progress. After an image or workflow finishes, read completionReview. Never claim Done while completionReview.doneClaimAllowed is false, delivery is pending, failed, or non-durable, or blocking findings remain. Render campaign compositions for visual inspection. Treat legacy warnings as review items, not permission to rewrite unrelated work.";
11
11
  export const GAVANA_CANVAS_GUIDE_READ_ONLY_INSTRUCTION =
12
12
  "For the first canvas task in a session, and before any unfamiliar canvas review, inspect the relevant guide through MCP resources or call guide_search then guide_get. Call canvas_get before reasoning about an existing canvas, and use canvas_validate to audit its current graph. Treat validation warnings as review items and never claim to mutate the canvas.";
13
13
 
package/src/commands.mjs CHANGED
@@ -144,9 +144,9 @@ export const GAVANA_CLI_COMMANDS = Object.freeze([
144
144
  "gavana action run action:side-by-side-composite --input node:<id> --input asset:<id> --destination canvas:<id>",
145
145
  ],
146
146
  },
147
- { group: "image", action: "generate", usage: ["gavana image generate --destination agent-canvas --prompt \"...\" [--element element:<id>@v<n>]"] },
148
- { group: "image", action: "edit", usage: ["gavana image edit --destination canvas:<id> --reference path/to/image.png --prompt \"...\""] },
149
- { group: "image", action: "variations", usage: ["gavana image variations --destination new-canvas --canvas-title \"Variations\" --reference path/to/source.png --prompt \"...\""] },
147
+ { group: "image", action: "generate", usage: ["gavana image generate --destination agent-canvas --prompt \"...\" [--element element:<id>@v<n>] [--wait]"] },
148
+ { group: "image", action: "edit", usage: ["gavana image edit --destination canvas:<id> --reference path/to/image.png --prompt \"...\" [--wait]"] },
149
+ { group: "image", action: "variations", usage: ["gavana image variations --destination new-canvas --canvas-title \"Variations\" --reference path/to/source.png --prompt \"...\" [--wait]"] },
150
150
  { group: "video", action: "generate", usage: ["gavana video generate --model model:<id> --prompt \"...\" --duration 15", "gavana video generate --model model:<id> --prompt \"...\" --first-frame path/to/start.png --no-wait", "gavana video generate --model model:<id> --prompt \"...\" --download output.mp4"] },
151
151
  { group: "video", action: "download", usage: ["gavana video download job:<id> --file output.mp4 [--yes]"] },
152
152
  { group: "job", action: "get", usage: ["gavana job get job:<id>"] },
@@ -49,7 +49,7 @@ export const GAVANA_CANVAS_GUIDE_SOURCES = Object.freeze([
49
49
  description: "Prepare media nodes, start only explicit generation, and preserve output lineage.",
50
50
  keywords: Object.freeze(["generated","generation","image","video","output","asset","durable","lineage","placeholder","job"]),
51
51
  order: 6,
52
- markdown: "\n## Before generation\n\n- Read the destination canvas and relevant source nodes.\n- Use exact source `node:` or `asset:` handles.\n- Before image or video generation, call `model_list` for the required capability and pass its exact `model:` handle. A bare model name does not select a saved connection. If no matching model is returned, report that the agent account cannot access that connection; do not ask the user to add a key again.\n- For a standalone image request, pass every visual source in `references`.\n Use `{ \"handle\": \"node:...\", \"role\": \"identity\" }` when its\n responsibility is known; valid roles are `identity`, `construction`,\n `texture`, `fit`, and `style`. Do not flatten multi-reference work\n into prompt prose or omit a source during fallback.\n- Reuse existing Canvas `node:` or `asset:` handles directly. Do not download\n and re-upload a generated Canvas image merely to use it as the next\n generation's reference. State whether a style reference establishes the\n brand-world or typography/layout direction in the prompt.\n- Create an empty image or video target only through supported operations. Do not write media bytes, storage keys, or arbitrary output URLs into metadata.\n- Connect prompts, products, references, Lists, and frame inputs to their target with the correct direction and mode.\n\n## Paid execution\n\nGeneration is allowed only after explicit current-turn user intent. Start one run with one caller-stable idempotency key. Poll the returned Run or Job; do not start another run while waiting. A terminal failure must be reported without automatic retry.\n\n## Completion\n\nDo not claim a generated image is durable until the result returns a target `node:`, durable `asset:`, and the final canvas read shows server-owned media fields. A video Job may return a protected download without materializing a native video node; report exactly what the server returned and do not invent durability.\n\nKeep generated output spatially near its input stage and connected to its source, prompt, List, or workflow. After finalization, run `canvas_validate` and read `completionReview`: it reports overlap, full-frame Section containment, reference lineage, and delivery state. Do not claim Done while it says `doneClaimAllowed: false`, including when delivery is pending, failed, or non-durable. Render the Canvas for visual inspection when the request includes a campaign, poster, banner, or multi-direction composition. Reference provenance is not a user workflow state and never requires a Keep action. Never create another paid provider call automatically.\n",
52
+ markdown: "\n## Before generation\n\n- Read the destination canvas and relevant source nodes.\n- Use exact source `node:` or `asset:` handles.\n- Before image or video generation, call `model_list` for the required capability and pass its exact `model:` handle. A bare model name does not select a saved connection. If no matching model is returned, report that the agent account cannot access that connection; do not ask the user to add a key again.\n- For a standalone image request, pass every visual source in `references`.\n Use `{ \"handle\": \"node:...\", \"role\": \"identity\" }` when its\n responsibility is known; valid roles are `identity`, `construction`,\n `texture`, `fit`, and `style`. Do not flatten multi-reference work\n into prompt prose or omit a source during fallback.\n- Reuse existing Canvas `node:` or `asset:` handles directly. Do not download\n and re-upload a generated Canvas image merely to use it as the next\n generation's reference. State whether a style reference establishes the\n brand-world or typography/layout direction in the prompt.\n- Create an empty image or video target only through supported operations. Do not write media bytes, storage keys, or arbitrary output URLs into metadata.\n- Connect prompts, products, references, Lists, and frame inputs to their target with the correct direction and mode.\n\n## Paid execution\n\nGeneration is allowed only after explicit current-turn user intent. Start one run with one caller-stable idempotency key. Image tools return durable queued progress by default; report that progress immediately and do not automatically call `run_wait`. Call `run_wait` only when the current user explicitly needs the completed asset in this same interaction. Otherwise, a later `run_get` or Canvas read can observe progress and the durable output. Do not start another run while one is pending. A terminal failure must be reported without automatic retry.\n\n## Completion\n\nDo not claim a generated image is durable until the result returns a target `node:`, durable `asset:`, and the final canvas read shows server-owned media fields. A video Job may return a protected download without materializing a native video node; report exactly what the server returned and do not invent durability.\n\nKeep generated output spatially near its input stage and connected to its source, prompt, List, or workflow. After finalization, run `canvas_validate` and read `completionReview`: it reports overlap, full-frame Section containment, reference lineage, and delivery state. Do not claim Done while it says `doneClaimAllowed: false`, including when delivery is pending, failed, or non-durable. Render the Canvas for visual inspection when the request includes a campaign, poster, banner, or multi-direction composition. Reference provenance is not a user workflow state and never requires a Keep action. Never create another paid provider call automatically.\n",
53
53
  }),
54
54
  Object.freeze({
55
55
  id: "existing-canvases",
@@ -65,7 +65,7 @@ export const GAVANA_CANVAS_GUIDE_SOURCES = Object.freeze([
65
65
  description: "Separate preparation from execution and prevent accidental or repeated provider charges.",
66
66
  keywords: Object.freeze(["paid","credits","cost","generate","run","retry","failure","prepare","setup","explicit"]),
67
67
  order: 8,
68
- markdown: "\n## Intent boundary\n\n\"Build\", \"prepare\", \"set up\", \"connect\", \"draft\", and \"make ready\" authorize graph edits only. They do not authorize Recipe, image, video, or Action execution.\n\nStart paid work only when the current user message explicitly asks to run or generate it. Do not infer authorization from an older message, a node label, an unfinished placeholder, or nearby content.\n\n## Retry boundary\n\n- Use one stable idempotency key for one intended paid operation.\n- Poll the returned handle with status tools.\n- Never automatically retry a terminal failure, timeout, disconnect, or ambiguous provider response with a new key.\n- Ask for new user intent before any new paid attempt.\n\nDeterministic Actions may be described as credit-free only when `action_get` confirms that contract. Inspect an Action before running it.\n",
68
+ markdown: "\n## Intent boundary\n\n\"Build\", \"prepare\", \"set up\", \"connect\", \"draft\", and \"make ready\" authorize graph edits only. They do not authorize Recipe, image, video, or Action execution.\n\nStart paid work only when the current user message explicitly asks to run or generate it. Do not infer authorization from an older message, a node label, an unfinished placeholder, or nearby content.\n\n## Retry boundary\n\n- Use one stable idempotency key for one intended paid operation.\n- Return an image Run's durable queued progress immediately. Do not call `run_wait` unless the current user explicitly needs the completed asset in this same interaction; otherwise observe it later with `run_get` or a Canvas read.\n- Never automatically retry a terminal failure, timeout, disconnect, or ambiguous provider response with a new key.\n- Ask for new user intent before any new paid attempt.\n\nDeterministic Actions may be described as credit-free only when `action_get` confirms that contract. Inspect an Action before running it.\n",
69
69
  }),
70
70
  Object.freeze({
71
71
  id: "validation-recovery",
@@ -49,7 +49,7 @@ export function gavanaMcpClientDefinition(clientName, baseUrl, readOnly = false)
49
49
  transport: "stdio",
50
50
  serverName,
51
51
  command: "npx",
52
- args: ["-y", "@gavana.ai/mcp@0.2.0"],
52
+ args: ["-y", "@gavana.ai/mcp@0.2.1"],
53
53
  env: readOnly ? { GAVANA_MCP_READ_ONLY: "true" } : {},
54
54
  credentialSource: "Reads the active Gavana CLI profile or inherited GAVANA_BASE_URL and GAVANA_AGENT_TOKEN environment variables.",
55
55
  };
package/src/runner.mjs CHANGED
@@ -504,11 +504,11 @@ async function executeCommand(client, group, action, args, options, runtime) {
504
504
  }
505
505
 
506
506
  if (group === "image" && (action === "generate" || action === "edit" || action === "variations")) {
507
+ if (options.wait === true && options["no-wait"] === true) throw usageError("--wait and --no-wait cannot be combined.");
507
508
  const prepared = await imageInput(client, action, args, options, runtime);
508
509
  const { __destination, ...input } = prepared;
509
510
  const queued = await client.startImage(action, input);
510
- const shouldWait = options["no-wait"] !== true;
511
- if (!shouldWait) return { ...queued, destination: __destination };
511
+ if (options.wait !== true) return { ...queued, destination: __destination };
512
512
  const result = await client.waitForRun(queued.run || queued.id, {
513
513
  timeoutMs: secondsOption(options.timeout, 15 * 60) * 1000,
514
514
  intervalMs: secondsOption(options.interval, 1.5, true) * 1000,
@@ -1580,23 +1580,17 @@ function ensureUniqueVideoReferences(references) {
1580
1580
  }
1581
1581
 
1582
1582
  async function uploadLocalReferenceInputs(client, references, canvasReference) {
1583
- const handles = [];
1584
1583
  const uploads = new Map();
1585
- for (const reference of references) {
1586
- if (reference.kind === "handle") {
1587
- handles.push(reference.handle);
1588
- continue;
1589
- }
1590
- if (reference.kind === "url") {
1591
- handles.push(reference.url);
1592
- continue;
1593
- }
1594
- const fingerprint = crypto.createHash("sha256").update(reference.bytes).digest("base64url");
1595
- if (!uploads.has(fingerprint)) uploads.set(fingerprint, client.uploadAsset({ ...reference, canvasReference }));
1596
- const uploaded = await uploads.get(fingerprint);
1597
- handles.push(uploaded.asset.handle);
1598
- }
1599
- return handles;
1584
+ return Promise.all(
1585
+ references.map(async (reference) => {
1586
+ if (reference.kind === "handle") return reference.handle;
1587
+ if (reference.kind === "url") return reference.url;
1588
+ const fingerprint = crypto.createHash("sha256").update(reference.bytes).digest("base64url");
1589
+ if (!uploads.has(fingerprint)) uploads.set(fingerprint, client.uploadAsset({ ...reference, canvasReference }));
1590
+ const uploaded = await uploads.get(fingerprint);
1591
+ return uploaded.asset.handle;
1592
+ }),
1593
+ );
1600
1594
  }
1601
1595
 
1602
1596
  async function readMacClipboardImage() {
@@ -2052,7 +2046,8 @@ Global options:
2052
2046
  --webhook-url URL Send one signed callback when a Run finishes
2053
2047
  --webhook-secret-env VAR Read the signing secret from VAR (default: GAVANA_WEBHOOK_SECRET)
2054
2048
  --canvas-title VALUE Title used when --destination is new-canvas
2055
- --no-wait Return immediately after queueing Recipe, image, video, or Action work
2049
+ --wait Wait for an image Run to finish instead of returning after queueing
2050
+ --no-wait Return immediately after queueing Recipe, video, or Action work (legacy image alias)
2056
2051
  --progress Write Run or video Job state transitions to stderr
2057
2052
 
2058
2053
  Video options:
@@ -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
  },
@@ -166,7 +166,7 @@ function imageSchema() {
166
166
  size: z.string().max(80).optional(),
167
167
  quality: z.string().max(80).optional(),
168
168
  count: z.number().int().min(1).max(4).optional(),
169
- 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."),
170
170
  timeoutSeconds: z.number().min(1).max(3_600).default(900),
171
171
  });
172
172
  }
package/src/version.mjs CHANGED
@@ -7,4 +7,4 @@
7
7
  //
8
8
  // scripts/gavana-mcp-tool-contract.test.mjs asserts this equals
9
9
  // packages/cli/package.json, so the two cannot drift.
10
- export const GAVANA_CLI_VERSION = "0.2.1";
10
+ export const GAVANA_CLI_VERSION = "0.2.2";