@2kw/ai 6.3.0-dev.13 → 6.3.0-dev.135

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 (49) hide show
  1. package/README.md +4 -1
  2. package/dist/agent-config/schema.d.ts +6 -3
  3. package/dist/agent-config/schema.js +17 -4
  4. package/dist/agent-config/template.js +1 -1
  5. package/dist/commands/agent-apply.js +1 -1
  6. package/dist/commands/agent-policy.d.ts +2 -1
  7. package/dist/commands/agent-policy.js +9 -2
  8. package/dist/commands/agent-run.d.ts +5 -0
  9. package/dist/commands/agent-run.js +47 -21
  10. package/dist/commands/agents.js +33 -6
  11. package/dist/commands/ai.js +3 -9
  12. package/dist/commands/auth.js +6 -1
  13. package/dist/commands/billing.js +107 -3
  14. package/dist/commands/config.d.ts +1 -1
  15. package/dist/commands/config.js +16 -2
  16. package/dist/commands/conversations.js +16 -0
  17. package/dist/commands/datasets.js +17 -15
  18. package/dist/commands/experiments.js +36 -26
  19. package/dist/commands/files.js +10 -28
  20. package/dist/commands/installations.js +46 -1
  21. package/dist/commands/knowledge-documents.js +24 -32
  22. package/dist/commands/knowledge.js +5 -0
  23. package/dist/commands/memory.d.ts +10 -0
  24. package/dist/commands/memory.js +132 -0
  25. package/dist/commands/prompts.js +9 -8
  26. package/dist/commands/schemas.js +15 -6
  27. package/dist/commands/skill-versions.js +27 -1
  28. package/dist/commands/skills.js +31 -1
  29. package/dist/commands/tracing.js +12 -17
  30. package/dist/index.js +2 -0
  31. package/dist/lib/agent-decide.d.ts +29 -2
  32. package/dist/lib/agent-decide.js +100 -3
  33. package/dist/lib/agent-run.d.ts +50 -3
  34. package/dist/lib/agent-run.js +166 -16
  35. package/dist/lib/approval-prompt.js +31 -1
  36. package/dist/lib/client.d.ts +8 -0
  37. package/dist/lib/client.js +18 -1
  38. package/dist/lib/config.d.ts +16 -0
  39. package/dist/lib/config.js +26 -1
  40. package/dist/lib/connect-pause.d.ts +71 -0
  41. package/dist/lib/connect-pause.js +147 -0
  42. package/dist/lib/errors.js +3 -1
  43. package/dist/lib/overlay.d.ts +10 -0
  44. package/dist/lib/overlay.js +20 -0
  45. package/dist/lib/skills-apply-preview.d.ts +19 -0
  46. package/dist/lib/skills-apply-preview.js +94 -0
  47. package/dist/lib/tracing-settings.d.ts +26 -0
  48. package/dist/lib/tracing-settings.js +25 -0
  49. package/package.json +1 -1
@@ -58,15 +58,24 @@ export function makeSchemasCommand() {
58
58
  .option("-d, --description <text>", "New description")
59
59
  .action(async (id, opts, command) => {
60
60
  await runAction(command, async () => {
61
+ let name = opts.name;
62
+ let description = opts.description;
63
+ if (name !== undefined && !name.trim())
64
+ throw new Error("--name must not be empty.");
61
65
  const client = getClient(command);
62
- const body = {};
63
- if (opts.name)
64
- body.name = opts.name;
65
- if (opts.description !== undefined)
66
- body.description = opts.description;
66
+ // PUT /v1/schemas/{id} replaces the whole schema, so an omitted field would be stored as null.
67
+ if (name === undefined || description === undefined) {
68
+ const { data: current } = await client.GET("/v1/schemas/{id}", {
69
+ params: { path: { id } },
70
+ });
71
+ if (!current)
72
+ throw new Error(`Schema ${id} could not be read.`);
73
+ name ??= current.name;
74
+ description ??= current.description;
75
+ }
67
76
  const { data } = await client.PUT("/v1/schemas/{id}", {
68
77
  params: { path: { id } },
69
- body: body,
78
+ body: { name, description },
70
79
  });
71
80
  formatDetail(data, command);
72
81
  });
@@ -1,4 +1,4 @@
1
- import { Command } from "commander";
1
+ import { Command, InvalidArgumentError } from "commander";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { getClient, runAction } from "../lib/client.js";
4
4
  import { formatPage, formatDetail } from "../lib/output.js";
@@ -48,6 +48,32 @@ export function makeSkillVersionsCommand() {
48
48
  formatDetail(data, command);
49
49
  });
50
50
  });
51
+ cmd
52
+ .command("restore")
53
+ .description("Restore a version as a new version; latest moves to it (the newest version is reused when it already has the content)")
54
+ .argument("<id>", "Skill ID")
55
+ .argument("<versionNumber>", "Version number to restore", parseVersionNumber)
56
+ .action(async (id, versionNumber, _opts, command) => {
57
+ await runAction(command, async () => {
58
+ const client = getClient(command);
59
+ const { data, response } = await client.POST("/v1/skills/{id}/versions/{versionNumber}/restore", {
60
+ params: { path: { id, versionNumber } },
61
+ });
62
+ // 200, not 201: the newest version already had the content and latest moved to it.
63
+ if (response.status === 200 && data) {
64
+ console.error(`No new version: v${data.versionNumber} already has the content of v${versionNumber}; latest now points at v${data.versionNumber}.`);
65
+ }
66
+ formatDetail(data, command);
67
+ });
68
+ });
51
69
  return cmd;
52
70
  }
71
+ /** A restore names an existing version by number; refuse anything else before calling the API (#783). */
72
+ function parseVersionNumber(value) {
73
+ const n = Number(value);
74
+ if (!/^\d+$/.test(value) || n < 1) {
75
+ throw new InvalidArgumentError("Version number must be a positive integer.");
76
+ }
77
+ return n;
78
+ }
53
79
  //# sourceMappingURL=skill-versions.js.map
@@ -21,6 +21,17 @@ async function checkResponse(res) {
21
21
  timestamp: body.timestamp ?? new Date().toISOString(),
22
22
  });
23
23
  }
24
+ const MIN_ROLES = ["user", "viewer", "member", "admin", "owner"];
25
+ /** A role word as the API's `Role`, or null for `none` (#785). */
26
+ function parseMinRole(word) {
27
+ const lower = word.trim().toLowerCase();
28
+ if (lower === "none")
29
+ return null;
30
+ if (MIN_ROLES.includes(lower)) {
31
+ return lower.toUpperCase();
32
+ }
33
+ throw new Error(`Role must be one of: ${MIN_ROLES.join(", ")}, none`);
34
+ }
24
35
  /** Manage skills, versions and labels (#642). */
25
36
  export function makeSkillsCommand() {
26
37
  const cmd = new Command("skills").description("Manage skills");
@@ -75,6 +86,24 @@ export function makeSkillsCommand() {
75
86
  formatDetail(data, command);
76
87
  });
77
88
  });
89
+ cmd
90
+ .command("min-role")
91
+ .description("Set the minimum organization role that sees this skill in agent runs (admin only); none removes it")
92
+ .argument("<id>", "Skill ID")
93
+ .argument("<role>", "user, viewer, member, admin, owner or none")
94
+ .action(async (id, role, _opts, command) => {
95
+ await runAction(command, async () => {
96
+ const minRole = parseMinRole(role);
97
+ const client = getClient(command);
98
+ const { data } = await client.PUT("/v1/skills/{id}/min-role", {
99
+ params: { path: { id } },
100
+ // The spec publishes minRole without null (springdoc marks no record component
101
+ // nullable); the operation documents null as "remove the minimum", and it is sent as such.
102
+ body: { minRole: minRole },
103
+ });
104
+ formatDetail(data, command);
105
+ });
106
+ });
78
107
  cmd
79
108
  .command("delete")
80
109
  .description("Delete a skill")
@@ -157,7 +186,8 @@ export function makeSkillsCommand() {
157
186
  const { data } = await client.GET("/v1/skills/{id}", { params: { path: { id } } });
158
187
  if (!data?.name)
159
188
  throw new Error("Skill name missing; specify an output path with -o.");
160
- outputPath = `${basename(data.name)}-v${versionNumber}.zip`;
189
+ // #788: ":" (a plugin skill) becomes "." before basename, which on Windows reads "a:" as a drive.
190
+ outputPath = `${basename(data.name.replace(/:/g, "."))}-v${versionNumber}.zip`;
161
191
  }
162
192
  const config = resolveConfig(command);
163
193
  const authHeader = await resolveAuthHeader(config);
@@ -1,10 +1,11 @@
1
1
  import { Command } from "commander";
2
2
  import { getClient, runAction } from "../lib/client.js";
3
3
  import { formatDetail, formatList } from "../lib/output.js";
4
+ import { tracingSettingsBody } from "../lib/tracing-settings.js";
4
5
  export function makeTracingCommand() {
5
6
  const cmd = new Command("tracing").description("Traces and per-org tracing settings (prompt / completion capture)");
6
7
  // ── settings ──────────────────────────────────────────────────
7
- const settings = new Command("settings").description("Manage per-org tracing settings (includePrompts / includeCompletions)");
8
+ const settings = new Command("settings").description("Manage per-org tracing settings (PII capture and trace retention)");
8
9
  settings
9
10
  .command("get")
10
11
  .description("Show current tracing settings")
@@ -20,6 +21,7 @@ export function makeTracingCommand() {
20
21
  .description("Update tracing settings. Both toggles control PII capture — default is false for both.")
21
22
  .option("--include-prompts <bool>", "Retain prompt content on ingested spans (true/false)")
22
23
  .option("--include-completions <bool>", "Retain completion content on ingested spans (true/false)")
24
+ .option("--retention-days <days>", 'Trace retention in days, at most your plan\'s limit, or "plan" to follow the plan. Shortening deletes older traces at the next daily sweep.')
23
25
  .action(async (opts, command) => {
24
26
  await runAction(command, async () => {
25
27
  const client = getClient(command);
@@ -27,17 +29,8 @@ export function makeTracingCommand() {
27
29
  // resetting the other. Otherwise an unset flag defaults to false
28
30
  // and silently disables capture the admin had already enabled.
29
31
  const { data: current } = await client.GET("/v1/tracing/settings");
30
- const body = {
31
- includePrompts: opts.includePrompts !== undefined
32
- ? opts.includePrompts === "true"
33
- : (current?.includePrompts ?? false),
34
- includeCompletions: opts.includeCompletions !== undefined
35
- ? opts.includeCompletions === "true"
36
- : (current?.includeCompletions ?? false),
37
- };
38
- const { data } = await client.PUT("/v1/tracing/settings", {
39
- body,
40
- });
32
+ const body = tracingSettingsBody(current, opts);
33
+ const { data } = await client.PUT("/v1/tracing/settings", { body });
41
34
  formatDetail(data, command);
42
35
  });
43
36
  });
@@ -84,6 +77,7 @@ export function makeTracingCommand() {
84
77
  const sessions = new Command("sessions").description("Session-level trace views (spans grouped by exporter-stamped session id)");
85
78
  const sessionsList = new Command("list").description("List trace sessions");
86
79
  sessionsList.option("--search <query>", "Free-text search across session id and name");
80
+ sessionsList.option("--session-id <sessionId>", "Exact match on the session.id the exporter stamped (slashes allowed)");
87
81
  sessionsList.option("--from <iso>", "Start of time range (ISO-8601)");
88
82
  sessionsList.option("--to <iso>", "End of time range (ISO-8601)");
89
83
  sessionsList.option("--page <n>", "Page index (0-based)", "0");
@@ -95,6 +89,7 @@ export function makeTracingCommand() {
95
89
  params: {
96
90
  query: {
97
91
  search: opts.search,
92
+ sessionId: opts.sessionId,
98
93
  from: opts.from,
99
94
  to: opts.to,
100
95
  page: parseInt(opts.page, 10),
@@ -103,17 +98,17 @@ export function makeTracingCommand() {
103
98
  },
104
99
  });
105
100
  const page = data;
106
- formatList((page?.content ?? []), command, ["sessionId", "name", "startTime", "durationMs", "turnCount", "errorCount", "costUsd"]);
101
+ formatList((page?.content ?? []), command, ["id", "sessionId", "name", "startTime", "durationMs", "turnCount", "errorCount", "costUsd"]);
107
102
  });
108
103
  });
109
104
  sessions.addCommand(sessionsList);
110
105
  const sessionsGet = new Command("get").description("Get all spans of a session by id, sorted chronologically");
111
- sessionsGet.argument("<sessionId>", "Session id");
112
- sessionsGet.action(async (sessionId, _opts, command) => {
106
+ sessionsGet.argument("<id>", "Session id: the id column of `sessions list` (find one by session.id with --session-id)");
107
+ sessionsGet.action(async (id, _opts, command) => {
113
108
  await runAction(command, async () => {
114
109
  const client = getClient(command);
115
- const { data } = await client.GET("/v1/traces/sessions/{sessionId}", {
116
- params: { path: { sessionId } },
110
+ const { data } = await client.GET("/v1/traces/sessions/{id}", {
111
+ params: { path: { id } },
117
112
  });
118
113
  formatDetail(data, command);
119
114
  });
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import { makeContextCommand } from "./commands/context.js";
22
22
  import { makeExperimentsCommand } from "./commands/experiments.js";
23
23
  import { makeTracingCommand } from "./commands/tracing.js";
24
24
  import { makeSettingsCommand } from "./commands/settings.js";
25
+ import { makeMemoryCommand } from "./commands/memory.js";
25
26
  import { makeEvaluatorsCommand } from "./commands/evaluators.js";
26
27
  import { makeScoresCommand } from "./commands/scores.js";
27
28
  import { makeQueuesCommand } from "./commands/queues.js";
@@ -64,6 +65,7 @@ program.addCommand(makeScoresCommand());
64
65
  program.addCommand(makeQueuesCommand());
65
66
  program.addCommand(makeTracingCommand());
66
67
  program.addCommand(makeSettingsCommand());
68
+ program.addCommand(makeMemoryCommand());
67
69
  program.addCommand(makeAgentsCommand());
68
70
  program.addCommand(makeConversationsCommand());
69
71
  program.addCommand(makeKnowledgeCommand());
@@ -1,6 +1,6 @@
1
1
  import type { components } from "../generated/openapi.js";
2
2
  import type { ApiClient } from "./agent-lookup.js";
3
- import type { ResponsesResult } from "./agent-run.js";
3
+ import { type ConversationMode, type ResponsesResult } from "./agent-run.js";
4
4
  export type ApprovalRow = components["schemas"]["ToolApprovalDTO"];
5
5
  export interface DecideOptions {
6
6
  approve?: string[];
@@ -22,20 +22,47 @@ export interface PromptAnswer {
22
22
  reason?: string;
23
23
  remember?: boolean;
24
24
  }
25
+ /** One relayed tool call answered by `--output` or `--fail` (#671 D2, D3). */
26
+ export interface ToolAnswer {
27
+ callId: string;
28
+ /** Sent verbatim as the item's `output` string. */
29
+ output: string;
30
+ failed: boolean;
31
+ }
32
+ export interface ToolAnswerIo {
33
+ readFile: (path: string) => string;
34
+ readStdin: () => string;
35
+ }
36
+ /**
37
+ * `--output` and `--fail` values as `<callId>=<value>`, split on the first `=`. `@<path>` reads a file and
38
+ * `@-` reads stdin, both verbatim. Only syntax is checked here: without stored state the CLI cannot list the
39
+ * calls a response released, so completeness is the server's check (#671 D4). Every check runs before any read.
40
+ */
41
+ export declare function parseToolAnswers(outputs?: string[], fails?: string[], io?: ToolAnswerIo): ToolAnswer[];
25
42
  export declare function fetchPendingApprovals(client: ApiClient, agentId: string, responseId: string): Promise<ApprovalRow[]>;
26
43
  /**
27
44
  * The backend refuses a continuation that leaves any pending approval of the paused
28
45
  * response undecided (400 incomplete_tool_outputs), so the whole set is checked here first.
29
46
  */
30
47
  export declare function planDecisions(pending: ApprovalRow[], opts: DecideOptions, responseId: string): Decision[];
48
+ /**
49
+ * The decisions a `decide` call sends beside its tool answers (#671 D5). A pure relay pause has no pending
50
+ * approval, so the approval flags are optional there: `--approve-all` / `--reject-all` add nothing (P1) and
51
+ * only explicit ids are refused. A mixed pause needs its approvals decided in the same call.
52
+ */
53
+ export declare function planContinuation(pending: ApprovalRow[], opts: DecideOptions, toolAnswers: ToolAnswer[], responseId: string): Decision[];
31
54
  export declare function decisionsFromAnswers(pending: ApprovalRow[], answers: Map<string, PromptAnswer>): Decision[];
32
55
  /** Same item shape as n8n Decide Approval (#660). */
33
56
  export declare function toApprovalItems(decisions: Decision[]): Record<string, string>[];
57
+ /** `failed: true` is Backbone's extension (#480): the tool's span ends ERROR. */
58
+ export declare function toToolOutputItems(answers: ToolAnswer[]): Record<string, string | boolean>[];
34
59
  /**
35
60
  * Continues on `agent/<agentId>[@<label>][#<model>]` with the label and model override the run used: the
36
61
  * server resolves the continuation's tools, instructions and model from this reference, so both must match
37
62
  * the run's or the continuation drops to another version or the agent's default model.
38
63
  * Never the echoed `name@<versionNumber>` (it would 404 as a label).
64
+ * With `mode`, the `backbone:mode` item follows the decisions (#656 D7); without it, nothing is added (D2).
65
+ * Tool answers go first, in flag order (#671 D6): the server replays accepted outputs ahead of other items.
39
66
  */
40
- export declare function continueWithDecisions(client: ApiClient, agentId: string, responseId: string, decisions: Decision[], label?: string, model?: string): Promise<ResponsesResult>;
67
+ export declare function continueWithDecisions(client: ApiClient, agentId: string, responseId: string, decisions: Decision[], label?: string, model?: string, mode?: ConversationMode, toolAnswers?: ToolAnswer[]): Promise<ResponsesResult>;
41
68
  //# sourceMappingURL=agent-decide.d.ts.map
@@ -1,5 +1,69 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { modeItem } from "./agent-run.js";
1
3
  import { CliUsageError } from "./errors.js";
2
4
  import { paginationParams } from "./pagination.js";
5
+ const defaultIo = {
6
+ readFile: (path) => readFileSync(path, "utf-8"),
7
+ // Not readStdinInput: that one trims, and a tool result is sent as read (P5).
8
+ readStdin: () => readFileSync(0, "utf-8"),
9
+ };
10
+ /**
11
+ * `--output` and `--fail` values as `<callId>=<value>`, split on the first `=`. `@<path>` reads a file and
12
+ * `@-` reads stdin, both verbatim. Only syntax is checked here: without stored state the CLI cannot list the
13
+ * calls a response released, so completeness is the server's check (#671 D4). Every check runs before any read.
14
+ */
15
+ export function parseToolAnswers(outputs = [], fails = [], io = defaultIo) {
16
+ const entries = [
17
+ ...outputs.map((raw) => ({ raw, flag: "--output", noun: "value", failed: false })),
18
+ ...fails.map((raw) => ({ raw, flag: "--fail", noun: "message", failed: true })),
19
+ ].map(({ raw, flag, noun, failed }) => {
20
+ const eq = raw.indexOf("=");
21
+ if (eq < 0)
22
+ throw new CliUsageError(`Invalid ${flag} '${raw}': use <callId>=<${noun}>.`);
23
+ const callId = raw.slice(0, eq);
24
+ if (!callId)
25
+ throw new CliUsageError(`Invalid ${flag} '${raw}': the call id is empty.`);
26
+ return { callId, value: raw.slice(eq + 1), failed };
27
+ });
28
+ const seen = new Map();
29
+ for (const e of entries) {
30
+ const before = seen.get(e.callId);
31
+ if (before === undefined) {
32
+ seen.set(e.callId, e.failed);
33
+ }
34
+ else if (before === e.failed) {
35
+ throw new CliUsageError(`Tool call ${e.callId} is answered more than once.`);
36
+ }
37
+ else {
38
+ throw new CliUsageError(`Tool call ${e.callId} is answered by both --output and --fail.`);
39
+ }
40
+ }
41
+ if (entries.filter((e) => e.value === "@-").length > 1) {
42
+ throw new CliUsageError("Only one --output or --fail value can read stdin (@-).");
43
+ }
44
+ return entries.map(({ callId, value, failed }) => ({ callId, output: readAnswerValue(value, io), failed }));
45
+ }
46
+ function readAnswerValue(value, io) {
47
+ if (value === "@-") {
48
+ try {
49
+ return io.readStdin();
50
+ }
51
+ catch (err) {
52
+ // A closed or non-blocking stdin (EAGAIN, EOF) is a usage error like an unreadable file.
53
+ throw new CliUsageError(`stdin: cannot read (${err.code ?? err.message})`);
54
+ }
55
+ }
56
+ if (!value.startsWith("@"))
57
+ return value;
58
+ const path = value.slice(1);
59
+ try {
60
+ return io.readFile(path);
61
+ }
62
+ catch (err) {
63
+ const code = err.code;
64
+ throw new CliUsageError(`${path}: cannot read file (${code ?? err.message})`);
65
+ }
66
+ }
3
67
  const MAX_PAGES = 50;
4
68
  export async function fetchPendingApprovals(client, agentId, responseId) {
5
69
  const rows = [];
@@ -40,7 +104,8 @@ export function planDecisions(pending, opts, responseId) {
40
104
  throw new CliUsageError("Use either --approve-all / --reject-all or explicit --approve / --reject ids, not both.");
41
105
  }
42
106
  if (!usesAll && approve.length === 0 && reject.length === 0) {
43
- throw new CliUsageError("Choose a decision: --approve <ids...>, --reject <ids...>, --approve-all or --reject-all.");
107
+ throw new CliUsageError("Choose a decision: --approve <ids...>, --reject <ids...>, --approve-all or --reject-all " +
108
+ "(and answer relayed tool calls with --output / --fail).");
44
109
  }
45
110
  const choice = new Map();
46
111
  if (usesAll) {
@@ -83,6 +148,26 @@ export function planDecisions(pending, opts, responseId) {
83
148
  };
84
149
  });
85
150
  }
151
+ /**
152
+ * The decisions a `decide` call sends beside its tool answers (#671 D5). A pure relay pause has no pending
153
+ * approval, so the approval flags are optional there: `--approve-all` / `--reject-all` add nothing (P1) and
154
+ * only explicit ids are refused. A mixed pause needs its approvals decided in the same call.
155
+ */
156
+ export function planContinuation(pending, opts, toolAnswers, responseId) {
157
+ if (toolAnswers.length === 0)
158
+ return planDecisions(pending, opts, responseId);
159
+ const ids = [...(opts.approve ?? []), ...(opts.reject ?? [])];
160
+ if (pending.length === 0) {
161
+ if (ids.length)
162
+ throw new CliUsageError(`Not pending on ${responseId}: ${ids.join(", ")}`);
163
+ return [];
164
+ }
165
+ if (!opts.approveAll && !opts.rejectAll && ids.length === 0) {
166
+ throw new CliUsageError(`${responseId} also waits for approvals: every relayed tool call and every pending approval must be answered ` +
167
+ `in one call. Undecided: ${pending.map((r) => r.id).join(", ")} (add --approve-all, --reject-all or the ids).`);
168
+ }
169
+ return planDecisions(pending, opts, responseId);
170
+ }
86
171
  export function decisionsFromAnswers(pending, answers) {
87
172
  return pending.map((r) => {
88
173
  const answer = answers.get(String(r.id));
@@ -108,18 +193,30 @@ export function toApprovalItems(decisions) {
108
193
  ...(d.remember ? { remember: d.remember } : {}),
109
194
  }));
110
195
  }
196
+ /** `failed: true` is Backbone's extension (#480): the tool's span ends ERROR. */
197
+ export function toToolOutputItems(answers) {
198
+ return answers.map((a) => ({
199
+ type: "function_call_output",
200
+ call_id: a.callId,
201
+ output: a.output,
202
+ ...(a.failed ? { failed: true } : {}),
203
+ }));
204
+ }
111
205
  /**
112
206
  * Continues on `agent/<agentId>[@<label>][#<model>]` with the label and model override the run used: the
113
207
  * server resolves the continuation's tools, instructions and model from this reference, so both must match
114
208
  * the run's or the continuation drops to another version or the agent's default model.
115
209
  * Never the echoed `name@<versionNumber>` (it would 404 as a label).
210
+ * With `mode`, the `backbone:mode` item follows the decisions (#656 D7); without it, nothing is added (D2).
211
+ * Tool answers go first, in flag order (#671 D6): the server replays accepted outputs ahead of other items.
116
212
  */
117
- export async function continueWithDecisions(client, agentId, responseId, decisions, label, model) {
213
+ export async function continueWithDecisions(client, agentId, responseId, decisions, label, model, mode, toolAnswers = []) {
214
+ const items = [...toToolOutputItems(toolAnswers), ...toApprovalItems(decisions)];
118
215
  const body = {
119
216
  model: `agent/${agentId}${label ? `@${label}` : ""}${model ? `#${model}` : ""}`,
120
217
  previous_response_id: responseId,
121
218
  stream: false,
122
- input: toApprovalItems(decisions),
219
+ input: mode ? [...items, modeItem(mode)] : items,
123
220
  };
124
221
  // The generated body type does not model backbone: extension items, hence the cast.
125
222
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1,5 +1,8 @@
1
+ import type { components } from "../generated/openapi.js";
1
2
  type AnyRecord = Record<string, any>;
2
3
  export type RunStatus = "completed" | "requires_approval" | "requires_tool_output" | "incomplete";
4
+ /** A built-in SkillsApply approval's preview (#781, spec §6); the generated type, fields all optional. */
5
+ export type SkillsApplyPreview = components["schemas"]["SkillsApplyPreview"];
3
6
  export interface PendingApproval {
4
7
  approvalId: string;
5
8
  callId: string;
@@ -8,6 +11,8 @@ export interface PendingApproval {
8
11
  policyClass: string;
9
12
  /** Why the automatic approver escalated this request to a human (#634); null when it did not. */
10
13
  reason: string | null;
14
+ /** What a built-in SkillsApply change does (#781); null for every other approval. Keyed on presence, not on the tool name (#948). */
15
+ preview: SkillsApplyPreview | null;
11
16
  }
12
17
  export interface ToolCallSummary {
13
18
  tool: string;
@@ -20,10 +25,44 @@ export interface PendingToolCall {
20
25
  callId: string;
21
26
  arguments: unknown;
22
27
  }
28
+ /**
29
+ * A connector the run waits for the user to connect, allow or reconnect in chat.2kw.ai: an open
30
+ * `backbone:connector_auth_request` (#807 R12). The caller never answers the connect call; the
31
+ * continuation re-checks access itself. Decoded by `connect-pause.ts` (#1086), whose `id` and
32
+ * `callId` the envelope does not publish.
33
+ */
34
+ export interface PendingConnection {
35
+ serverLabel: string;
36
+ host: string;
37
+ /** `connect`, `allow` or `reconnect`. */
38
+ reason: string;
39
+ /** The egress difference an allow is re-asked for; absent otherwise. */
40
+ destinations?: string[];
41
+ }
42
+ /** The end user's conversation mode (epic &59); the server matches these three values exactly. */
43
+ export type ConversationMode = "plan" | "ask" | "auto";
44
+ export declare const CONVERSATION_MODES: readonly ConversationMode[];
45
+ /** `--mode` help text, in the words of the embedded chat's mode chip (#655). */
46
+ export declare const MODE_OPTION_HELP: string;
47
+ /**
48
+ * The `--mode` value, checked before any request is sent: anything but the three exact values is a
49
+ * usage error (exit 2). Commander's `.choices()` is not used because it exits 1 through `process.exit`.
50
+ */
51
+ export declare function parseModeOption(raw: string | undefined): ConversationMode | undefined;
52
+ /** The `backbone:mode` input item (S1 D7); always appended last. */
53
+ export declare function modeItem(mode: ConversationMode): {
54
+ type: "backbone:mode";
55
+ mode: ConversationMode;
56
+ };
57
+ /**
58
+ * The mode the response ran under (`conversation_mode`, #656). An absent key (a server before #656)
59
+ * and any value other than the three read as null, which also means "none set".
60
+ */
61
+ export declare function responseMode(result: ResponsesResult | undefined): ConversationMode | null;
23
62
  export interface RunEnvelope {
24
63
  status: RunStatus;
25
- /** Reserved for the server-side conversation mode (#656); always null until then. */
26
- mode: null;
64
+ /** The conversation mode the request ran under (`conversation_mode`); null when none is set. */
65
+ mode: ConversationMode | null;
27
66
  agent: string | null;
28
67
  version: number | null;
29
68
  responseId: string | null;
@@ -32,6 +71,7 @@ export interface RunEnvelope {
32
71
  toolCalls: ToolCallSummary[];
33
72
  pendingApprovals: PendingApproval[];
34
73
  pendingToolCalls: PendingToolCall[];
74
+ pendingConnections: PendingConnection[];
35
75
  incompleteReason: string | null;
36
76
  usage: {
37
77
  inputTokens: number;
@@ -56,6 +96,7 @@ export interface ResponsesResult {
56
96
  incomplete_details?: {
57
97
  reason?: string;
58
98
  } | null;
99
+ conversation_mode?: string | null;
59
100
  }
60
101
  export declare const EXIT_CODES: Record<RunStatus, number>;
61
102
  /** The assistant's text: every `output_text` part of every `message` item, in order. */
@@ -76,7 +117,13 @@ export declare function parseAgentModel(model?: string): {
76
117
  */
77
118
  export declare function stripControl(s: string): string;
78
119
  export declare function shellQuote(value: string): string;
79
- export declare function buildRunEnvelope(result: ResponsesResult | undefined, agentRef?: string): RunEnvelope;
120
+ /** "Connect a (h), allow the agent to use b (h) and reconnect c (h)". */
121
+ export declare function connectionsSentence(connections: PendingConnection[]): string;
122
+ /**
123
+ * @param chatUrl the chat web host of the API the run went to ({@link chatUrlFor}); a connect pause's
124
+ * `next` sends the user to its Connectors page.
125
+ */
126
+ export declare function buildRunEnvelope(result: ResponsesResult | undefined, agentRef?: string, chatUrl?: string): RunEnvelope;
80
127
  /** Human-readable rendering of an envelope on stdout. Every value printed is passed through {@link stripControl}. */
81
128
  export declare function printRunText(env: RunEnvelope): void;
82
129
  export {};