@2kw/ai 7.4.0-dev.13 → 7.4.0-dev.15

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.
@@ -571,7 +571,7 @@ export declare const agentSchema: {
571
571
  };
572
572
  readonly additionalProperties: {
573
573
  readonly type: "string";
574
- readonly enum: readonly ["read", "write", "destructive", "approve", "auto", "deny"];
574
+ readonly enum: readonly ["read", "write", "destructive", "approve", "deny"];
575
575
  };
576
576
  };
577
577
  };
@@ -105,8 +105,8 @@ export const agentSchema = {
105
105
  properties: {
106
106
  readOnlyHint: { type: "boolean" },
107
107
  destructiveHint: { type: "boolean" },
108
- // Optional. With destructiveHint: false it declares the tool harmless, so a write under
109
- // `auto` may reach the approver (#938).
108
+ // Optional. With destructiveHint: false it declares the tool harmless, so a write that needs
109
+ // approval may be decided by the automatic approver in the Auto mode (#938, #1645).
110
110
  openWorldHint: { type: "boolean" },
111
111
  },
112
112
  },
@@ -290,17 +290,17 @@ export const agentSchema = {
290
290
  type: "object",
291
291
  additionalProperties: false,
292
292
  properties: {
293
- read: oneOfLowercase("allow", "auto", "approve", "block", "deny"),
294
- write: oneOfLowercase("auto", "approve", "block", "deny"),
295
- destructive: oneOfLowercase("block", "deny"),
293
+ read: oneOfLowercase("allow", "approve", "block", "deny"),
294
+ write: oneOfLowercase("approve", "block", "deny"),
295
+ destructive: oneOfLowercase("approve", "block", "deny"),
296
296
  },
297
297
  },
298
298
  tools: {
299
- // Value is a policy class or an action. Actions (approve, auto, deny) are exact lowercase;
299
+ // Value is a policy class or an action. Actions (approve, deny) are exact lowercase;
300
300
  // classes (read, write, destructive) are case-insensitive and lowercased by the validator.
301
301
  type: "object",
302
302
  propertyNames: { pattern: "\\S" },
303
- additionalProperties: { type: "string", enum: ["read", "write", "destructive", "approve", "auto", "deny"] },
303
+ additionalProperties: { type: "string", enum: ["read", "write", "destructive", "approve", "deny"] },
304
304
  },
305
305
  },
306
306
  },
@@ -24,7 +24,8 @@ export function makeAgentPolicyCommand() {
24
24
  .option("--tool <name>", "Answer for one tool instead of every configured tool")
25
25
  .option("--installation <id>", "Resolve the module tool catalog of this installation")
26
26
  .option("--mode <mode>", "Answer as a conversation in this mode would be gated: plan (every call that is not read-only is refused), " +
27
- "ask (a person decides where the policy would ask the judge), auto (the policy as written)")
27
+ "ask (a person decides every call that needs approval), auto (the automatic approver decides reads and " +
28
+ "writes the server marks safe). Without it, the agent's default mode")
28
29
  .action(async (agentRef, opts, command) => {
29
30
  await runAction(command, async () => {
30
31
  const mode = parseModeOption(opts.mode);
@@ -49,14 +50,14 @@ export function makeAgentPolicyCommand() {
49
50
  // model, so they belong above the table rather than in a footnote.
50
51
  const auto = data?.autoApproval;
51
52
  const parts = [
52
- `auto-approval ${auto?.enabled ? "on" : "off (every auto pauses for a human)"}`,
53
- `judge ${auto?.judgeConfigured ? "configured" : "not configured (every candidate escalates)"}`,
53
+ `auto-approval ${auto?.enabled ? "on" : "off (every call the approver would decide pauses for a human)"}`,
54
+ `approver ${auto?.judgeConfigured ? "configured" : "not configured (every candidate escalates)"}`,
54
55
  ];
55
56
  if (auto?.instructionsExceedJudgeBound) {
56
- parts.push("instructions longer than the judge reads (every candidate escalates)");
57
+ parts.push("instructions longer than the approver reads (every candidate escalates)");
57
58
  }
58
- if (mode)
59
- parts.push(`mode ${mode}`);
59
+ if (data?.effectiveMode)
60
+ parts.push(`mode ${data.effectiveMode} (${data.effectiveModeSource})`);
60
61
  if (data?.catalogResolvedAt)
61
62
  parts.push(`catalog resolved ${data.catalogResolvedAt}`);
62
63
  console.log(chalk.dim(parts.join(", ")));
@@ -5,8 +5,9 @@ import { CliUsageError } from "./errors.js";
5
5
  import { previewIsIncomplete, previewLines } from "./skills-apply-preview.js";
6
6
  export const CONVERSATION_MODES = ["plan", "ask", "auto"];
7
7
  /** `--mode` help text, in the words of the embedded chat's mode chip (#655). */
8
- export const MODE_OPTION_HELP = "Conversation mode: plan (read-only), ask (no automatic approver; calls that need approval wait for you), " +
9
- "auto (the operator's policy as written). Without it, the conversation keeps its mode";
8
+ export const MODE_OPTION_HELP = "Conversation mode: plan (read-only), ask (calls that need approval wait for you), " +
9
+ "auto (the automatic approver decides reads and writes the server marks safe; the rest wait for you). " +
10
+ "Without it, the conversation keeps its mode, else the agent's default; a mode the agent does not offer is refused";
10
11
  function isConversationMode(value) {
11
12
  return typeof value === "string" && CONVERSATION_MODES.includes(value);
12
13
  }
@@ -102,6 +102,7 @@ const CODE_HINTS = {
102
102
  "Check pendingToolCalls and pendingApprovals in the run envelope.",
103
103
  unknown_tool_output: "That call id was not released by this response. Use the responseId of the latest envelope.",
104
104
  conversation_agent_mismatch: "This conversation belongs to another agent. Start a new conversation or run the agent that owns it.",
105
+ conversation_mode_not_offered: "The agent does not offer that mode. Use one of the modes the message lists (plan is always offered), or leave --mode out for the agent's default.",
105
106
  provider_quota_exhausted: "The model provider account is out of credits or at its spend limit. Add credits or raise the limit with the provider; retrying will not help.",
106
107
  };
107
108
  /** The most specific hint for an API error: gateway code, then known messages, then the status table. */
@@ -117,6 +118,9 @@ export function hintFor(err) {
117
118
  }
118
119
  if (err.code && Object.hasOwn(CODE_HINTS, err.code))
119
120
  return CODE_HINTS[err.code];
121
+ // The REST layer (the explain view) spells the same refusal as an errorCode.
122
+ if (err.errorCode === "CONVERSATION_MODE_NOT_OFFERED")
123
+ return CODE_HINTS.conversation_mode_not_offered;
120
124
  // The gateway answers `agent_not_published` (code), the REST layer AGENT_NOT_PUBLISHED (errorCode).
121
125
  if (err.code === "agent_not_published" || err.errorCode === "AGENT_NOT_PUBLISHED") {
122
126
  return NOT_PUBLISHED_HINT;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2kw/ai",
3
- "version": "7.4.0-dev.13",
3
+ "version": "7.4.0-dev.15",
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",