@2kw/ai 8.1.2 → 8.2.0-dev.10

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.
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { extname } from "node:path";
3
3
  import { parse, YAMLParseError } from "yaml";
4
4
  import { CliUsageError } from "../lib/errors.js";
5
- import { validateAgentConfig } from "./validate.js";
5
+ import { agentConfigWarnings, validateAgentConfig } from "./validate.js";
6
6
  const PLACEHOLDER = /\$\$\{|\$\{([A-Z_][A-Z0-9_]*)\}/g;
7
7
  /** Variable names whose value is a secret wherever it is used. */
8
8
  const SECRET_NAME = /(SECRET|TOKEN|PASSWORD|PASSWD|PASS|API_?KEY|KEY|PRIVATE|CREDENTIAL|AUTH)/;
@@ -132,6 +132,8 @@ export function loadAgentConfig(path, env, readStdin = () => readFileSync(0, "ut
132
132
  if (issues.length) {
133
133
  throw new CliUsageError(`${source} is invalid:\n${issues.map((i) => ` ${i.pointer}: ${i.message}`).join("\n")}`);
134
134
  }
135
+ for (const warning of agentConfigWarnings(value))
136
+ console.error(`warning: ${source}: ${warning}`);
135
137
  return { config: value, secrets };
136
138
  }
137
139
  //# sourceMappingURL=file.js.map
@@ -10,6 +10,14 @@ export declare const AGENT_SCHEMA_ID = "https://docs.2kw.ai/schemas/agent.v1.jso
10
10
  * The backend refuses the same map on save (NonAuthorableTools, #1263); its test fails if they differ.
11
11
  */
12
12
  export declare const NON_AUTHORABLE_TOOL_TYPES: Record<string, string>;
13
+ /**
14
+ * The three tools `backbone.canvas` grants (#1698). Listing one is refused on the backend too
15
+ * (CanvasToolEntryValidator, 422); kept apart from NON_AUTHORABLE_TOOL_TYPES, which the backend's
16
+ * NonAuthorableToolsTest holds equal to the backend list.
17
+ */
18
+ export declare const CANVAS_MEMBER_TYPES: Record<string, string>;
19
+ /** Types Canvas replaced: still accepted, with a warning (#1698). */
20
+ export declare const RETIRED_TOOL_TYPES: string[];
13
21
  export declare const agentSchema: {
14
22
  readonly $schema: "https://json-schema.org/draft/2020-12/schema";
15
23
  readonly $id: "https://docs.2kw.ai/schemas/agent.v1.json";
@@ -309,6 +317,19 @@ export declare const agentSchema: {
309
317
  };
310
318
  };
311
319
  };
320
+ readonly canvasTool: {
321
+ readonly type: "object";
322
+ readonly additionalProperties: false;
323
+ readonly required: readonly ["type"];
324
+ readonly properties: {
325
+ readonly type: {
326
+ readonly const: "backbone.canvas";
327
+ };
328
+ readonly requiresConfirmation: {
329
+ readonly type: "boolean";
330
+ };
331
+ };
332
+ };
312
333
  readonly showTableTool: {
313
334
  readonly type: "object";
314
335
  readonly additionalProperties: false;
@@ -527,6 +548,18 @@ export declare const agentSchema: {
527
548
  readonly then: {
528
549
  readonly $ref: "#/$defs/bashTool";
529
550
  };
551
+ }, {
552
+ readonly if: {
553
+ required: string[];
554
+ properties: {
555
+ type: {
556
+ const: string;
557
+ };
558
+ };
559
+ };
560
+ readonly then: {
561
+ readonly $ref: "#/$defs/canvasTool";
562
+ };
530
563
  }, {
531
564
  readonly if: {
532
565
  required: string[];
@@ -579,6 +612,22 @@ export declare const agentSchema: {
579
612
  };
580
613
  };
581
614
  };
615
+ }, {
616
+ readonly if: {
617
+ readonly required: readonly ["type"];
618
+ readonly properties: {
619
+ readonly type: {
620
+ readonly enum: string[];
621
+ };
622
+ };
623
+ };
624
+ readonly then: {
625
+ readonly properties: {
626
+ readonly type: {
627
+ readonly not: {};
628
+ };
629
+ };
630
+ };
582
631
  }, {
583
632
  readonly if: {
584
633
  readonly properties: {
@@ -35,6 +35,18 @@ export const NON_AUTHORABLE_TOOL_TYPES = {
35
35
  // #1244: injected wherever backbone.bash is advertised; an authored copy is dropped.
36
36
  "backbone.view_image": "it is added at run time wherever the sandboxed shell is",
37
37
  };
38
+ /**
39
+ * The three tools `backbone.canvas` grants (#1698). Listing one is refused on the backend too
40
+ * (CanvasToolEntryValidator, 422); kept apart from NON_AUTHORABLE_TOOL_TYPES, which the backend's
41
+ * NonAuthorableToolsTest holds equal to the backend list.
42
+ */
43
+ export const CANVAS_MEMBER_TYPES = {
44
+ "backbone.canvas_write": "it is granted by backbone.canvas",
45
+ "backbone.canvas_edit": "it is granted by backbone.canvas",
46
+ "backbone.canvas_read": "it is granted by backbone.canvas",
47
+ };
48
+ /** Types Canvas replaced: still accepted, with a warning (#1698). */
49
+ export const RETIRED_TOOL_TYPES = ["backbone.show_table", "backbone.show_chart"];
38
50
  const BUILTIN_TYPES = [
39
51
  "file_search",
40
52
  "web_search",
@@ -44,6 +56,7 @@ const BUILTIN_TYPES = [
44
56
  "backbone.todo",
45
57
  "backbone.skill",
46
58
  "backbone.bash",
59
+ "backbone.canvas",
47
60
  "backbone.show_table",
48
61
  "backbone.show_chart",
49
62
  "function",
@@ -220,7 +233,14 @@ export const agentSchema = {
220
233
  required: ["type"],
221
234
  properties: { type: { const: "backbone.todo" }, requiresConfirmation: { type: "boolean" } },
222
235
  },
223
- // #1419: no options; requiresConfirmation as todoTool, since an author may tighten a read tool.
236
+ // #1698: one grant for tables, charts and documents; no options. requiresConfirmation as todoTool.
237
+ canvasTool: {
238
+ type: "object",
239
+ additionalProperties: false,
240
+ required: ["type"],
241
+ properties: { type: { const: "backbone.canvas" }, requiresConfirmation: { type: "boolean" } },
242
+ },
243
+ // #1419, retired by Canvas (#1698) but still valid: no options; requiresConfirmation as todoTool.
224
244
  showTableTool: {
225
245
  type: "object",
226
246
  additionalProperties: false,
@@ -292,6 +312,7 @@ export const agentSchema = {
292
312
  { if: typeIs("backbone.todo"), then: { $ref: "#/$defs/todoTool" } },
293
313
  { if: typeIs("backbone.skill"), then: { $ref: "#/$defs/skillTool" } },
294
314
  { if: typeIs("backbone.bash"), then: { $ref: "#/$defs/bashTool" } },
315
+ { if: typeIs("backbone.canvas"), then: { $ref: "#/$defs/canvasTool" } },
295
316
  { if: typeIs("backbone.show_table"), then: { $ref: "#/$defs/showTableTool" } },
296
317
  { if: typeIs("backbone.show_chart"), then: { $ref: "#/$defs/showChartTool" } },
297
318
  { if: typeIs("function"), then: { $ref: "#/$defs/functionTool" } },
@@ -301,7 +322,15 @@ export const agentSchema = {
301
322
  then: { properties: { type: { not: {} } } },
302
323
  },
303
324
  {
304
- if: { properties: { type: { enum: [...BUILTIN_TYPES, ...Object.keys(NON_AUTHORABLE_TOOL_TYPES)] } }, required: ["type"] },
325
+ // validateAgentConfig reports these with the reason from CANVAS_MEMBER_TYPES.
326
+ if: { required: ["type"], properties: { type: { enum: Object.keys(CANVAS_MEMBER_TYPES) } } },
327
+ then: { properties: { type: { not: {} } } },
328
+ },
329
+ {
330
+ if: {
331
+ properties: { type: { enum: [...BUILTIN_TYPES, ...Object.keys(NON_AUTHORABLE_TOOL_TYPES), ...Object.keys(CANVAS_MEMBER_TYPES)] } },
332
+ required: ["type"],
333
+ },
305
334
  else: { $ref: "#/$defs/namedTool" },
306
335
  },
307
336
  ],
@@ -20,9 +20,10 @@ model: provider/model-name
20
20
  # - provider/other-model
21
21
  instructions: |
22
22
  Describe the agent's job, its inputs, and what a good answer looks like.
23
- tools: []
24
- # Uncomment the tools this agent may use and delete the empty list above.
25
- # tools:
23
+ # Canvas lets the agent show tables, charts and documents beside the conversation.
24
+ tools:
25
+ - type: backbone.canvas
26
+ # Uncomment more tools this agent may use.
26
27
  # - type: file_search
27
28
  # config:
28
29
  # knowledgeBaseIds: [kb_id]
@@ -41,8 +42,6 @@ tools: []
41
42
  # skills: [skill-name@latest]
42
43
  # - type: backbone.bash
43
44
  # approval: sandbox
44
- # - type: backbone.show_table
45
- # - type: backbone.show_chart
46
45
  # - type: function
47
46
  # name: lookup_order
48
47
  # description: Look up an order by number
@@ -26,4 +26,9 @@ export interface AgentConfig {
26
26
  */
27
27
  export declare function normalizeForValidation(doc: unknown): unknown;
28
28
  export declare function validateAgentConfig(raw: unknown): ConfigIssue[];
29
+ /**
30
+ * Warnings for a valid config: one per retired tool type it lists, however often (#1698). The
31
+ * backend still accepts these types, so they never fail validation.
32
+ */
33
+ export declare function agentConfigWarnings(raw: unknown): string[];
29
34
  //# sourceMappingURL=validate.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { Ajv2020 } from "ajv/dist/2020.js";
2
- import { agentSchema, NON_AUTHORABLE_TOOL_TYPES } from "./schema.js";
2
+ import { agentSchema, CANVAS_MEMBER_TYPES, NON_AUTHORABLE_TOOL_TYPES, RETIRED_TOOL_TYPES } from "./schema.js";
3
3
  const ajv = new Ajv2020({ allErrors: true, strict: false });
4
4
  const validate = ajv.compile(agentSchema);
5
5
  /** Top-level keys whose explicit `null` the backend reads as absent. */
@@ -135,6 +135,9 @@ export function validateAgentConfig(raw) {
135
135
  if (typeof type === "string" && Object.hasOwn(NON_AUTHORABLE_TOOL_TYPES, type)) {
136
136
  issues.push({ pointer: `/tools/${i}/type`, message: `type "${type}" cannot be authored: ${NON_AUTHORABLE_TOOL_TYPES[type]}` });
137
137
  }
138
+ if (typeof type === "string" && Object.hasOwn(CANVAS_MEMBER_TYPES, type)) {
139
+ issues.push({ pointer: `/tools/${i}/type`, message: `type "${type}" cannot be authored: ${CANVAS_MEMBER_TYPES[type]}` });
140
+ }
138
141
  // The backend refuses a second envelope (WebToolEntryValidator).
139
142
  if (type === "web_search") {
140
143
  if (webSearchSeen)
@@ -163,4 +166,12 @@ export function validateAgentConfig(raw) {
163
166
  const unique = new Map(issues.map((i) => [JSON.stringify([i.pointer, i.message]), i]));
164
167
  return [...unique.values()];
165
168
  }
169
+ /**
170
+ * Warnings for a valid config: one per retired tool type it lists, however often (#1698). The
171
+ * backend still accepts these types, so they never fail validation.
172
+ */
173
+ export function agentConfigWarnings(raw) {
174
+ const tools = isPlainObject(raw) && Array.isArray(raw.tools) ? raw.tools : [];
175
+ return RETIRED_TOOL_TYPES.filter((type) => tools.some((t) => isPlainObject(t) && t.type === type)).map((type) => `${type} is no longer used; use backbone.canvas`);
176
+ }
166
177
  //# sourceMappingURL=validate.js.map
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The connect pause, decoded one way for every client (#1086, spec §4, D4): an open
3
- * `backbone:connector_auth_request` (#807 R12) is a connector the run waits for the user to
2
+ * The connect pause, decoded one way for every client: an open
3
+ * `backbone:connector_auth_request` is a connector the run waits for the user to
4
4
  * connect, allow or reconnect in chat.2kw.ai.
5
5
  *
6
6
  * This file is canonical. `mcp/src/lib/connect-pause.ts` and
@@ -8,7 +8,7 @@
8
8
  * here, then run `npm run sync:connect-pause` in mcp/ and `npm run sync-connect-pause` in n8n/.
9
9
  * Its golden fixture, `cli/tests/fixtures/connect-pause-cases.json`, is copied the same way into
10
10
  * mcp/, n8n/ and surface/, and every client's decoder test runs every case of it. The input pause
11
- * (#1320, connectors spec §12) lives here too, with its own fixture `input-pause-cases.json`.
11
+ * lives here too, with its own fixture `input-pause-cases.json`.
12
12
  *
13
13
  * Plain TypeScript with no imports, no `process`, no Node types and no timers, so n8n's source
14
14
  * scanner and its empty `dependencies` accept the copy. The CLI and the MCP server keep their
@@ -16,7 +16,7 @@
16
16
  */
17
17
  /** Fallback chat.2kw.ai origin when the API host is not one we recognise. */
18
18
  export declare const DEFAULT_CHAT_URL = "https://chat.2kw.ai";
19
- /** Known API-host → chat web host mappings; a connect pause points the user there (#807 R11). */
19
+ /** Known API-host → chat web host mappings; a connect pause points the user there. */
20
20
  export declare const CHAT_URL_BY_API_HOST: Readonly<Record<string, string>>;
21
21
  /**
22
22
  * The chat web host that belongs to an API base URL, where a member connects a connector
@@ -38,7 +38,7 @@ export interface PendingConnection {
38
38
  /** The MCP server's host: what the user is asked to connect to. */
39
39
  host: string;
40
40
  reason: ConnectReason;
41
- /** The egress difference an allow is asked again for (#807 R9); absent otherwise, never empty. */
41
+ /** The egress difference an allow is asked again for; absent otherwise, never empty. */
42
42
  destinations?: string[];
43
43
  }
44
44
  /** Enough of a connection to name it in a sentence. */
@@ -47,7 +47,7 @@ export interface ConnectTarget {
47
47
  host: string;
48
48
  reason: string;
49
49
  }
50
- /** The synthetic tool a connector's connect pause is raised through (#806). */
50
+ /** The synthetic tool a connector's connect pause is raised through. */
51
51
  export declare function connectToolName(serverLabel: string): string;
52
52
  /**
53
53
  * The connectors a response's `output` still waits on: every `in_progress` connect request whose
@@ -75,7 +75,7 @@ export declare function connectionsPhrase(connections: readonly ConnectTarget[])
75
75
  */
76
76
  export type InputMode = "form" | "url" | "mixed" | "unknown";
77
77
  /**
78
- * One open `backbone:input_request` (#1320, connectors spec §12): a connector's question that only
78
+ * One open `backbone:input_request`: a connector's question that only
79
79
  * the requester can answer, and only in chat. `id` is the item's `inreq_…` id. Its message, schema
80
80
  * and URL are never read: no assistant may see them (D7).
81
81
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The connect pause, decoded one way for every client (#1086, spec §4, D4): an open
3
- * `backbone:connector_auth_request` (#807 R12) is a connector the run waits for the user to
2
+ * The connect pause, decoded one way for every client: an open
3
+ * `backbone:connector_auth_request` is a connector the run waits for the user to
4
4
  * connect, allow or reconnect in chat.2kw.ai.
5
5
  *
6
6
  * This file is canonical. `mcp/src/lib/connect-pause.ts` and
@@ -8,7 +8,7 @@
8
8
  * here, then run `npm run sync:connect-pause` in mcp/ and `npm run sync-connect-pause` in n8n/.
9
9
  * Its golden fixture, `cli/tests/fixtures/connect-pause-cases.json`, is copied the same way into
10
10
  * mcp/, n8n/ and surface/, and every client's decoder test runs every case of it. The input pause
11
- * (#1320, connectors spec §12) lives here too, with its own fixture `input-pause-cases.json`.
11
+ * lives here too, with its own fixture `input-pause-cases.json`.
12
12
  *
13
13
  * Plain TypeScript with no imports, no `process`, no Node types and no timers, so n8n's source
14
14
  * scanner and its empty `dependencies` accept the copy. The CLI and the MCP server keep their
@@ -16,7 +16,7 @@
16
16
  */
17
17
  /** Fallback chat.2kw.ai origin when the API host is not one we recognise. */
18
18
  export const DEFAULT_CHAT_URL = "https://chat.2kw.ai";
19
- /** Known API-host → chat web host mappings; a connect pause points the user there (#807 R11). */
19
+ /** Known API-host → chat web host mappings; a connect pause points the user there. */
20
20
  export const CHAT_URL_BY_API_HOST = {
21
21
  "api.2kw.ai": "https://chat.2kw.ai",
22
22
  "api-dev.2kw.ai": "https://chat-dev.2kw.ai",
@@ -48,7 +48,7 @@ function itemsOf(output) {
48
48
  function text(value) {
49
49
  return typeof value === "string" && value.length > 0 ? value : undefined;
50
50
  }
51
- /** The synthetic tool a connector's connect pause is raised through (#806). */
51
+ /** The synthetic tool a connector's connect pause is raised through. */
52
52
  export function connectToolName(serverLabel) {
53
53
  return `mcp__${serverLabel}__connect`;
54
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2kw/ai",
3
- "version": "8.1.2",
3
+ "version": "8.2.0-dev.10",
4
4
  "description": "CLI for 2kw.ai — schema-driven document extraction, an OpenAI-compatible EU LLM gateway, transcription, prompts, datasets, and experiments from your terminal or agentic workflows. Ships as 2kw, backbone, and bb.",
5
5
  "keywords": [
6
6
  "cli",