@2kw/ai 6.3.0-dev.14 → 6.3.0-dev.142
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/README.md +4 -1
- package/dist/agent-config/schema.d.ts +6 -3
- package/dist/agent-config/schema.js +19 -4
- package/dist/agent-config/template.js +1 -1
- package/dist/commands/agent-apply.js +1 -1
- package/dist/commands/agent-policy.d.ts +2 -1
- package/dist/commands/agent-policy.js +9 -2
- package/dist/commands/agent-run.d.ts +5 -0
- package/dist/commands/agent-run.js +47 -21
- package/dist/commands/agents.js +18 -5
- package/dist/commands/ai.js +3 -9
- package/dist/commands/auth.js +6 -1
- package/dist/commands/billing.js +107 -3
- package/dist/commands/config.d.ts +1 -1
- package/dist/commands/config.js +16 -2
- package/dist/commands/conversations.js +16 -0
- package/dist/commands/datasets.js +17 -15
- package/dist/commands/experiments.js +36 -26
- package/dist/commands/files.js +10 -28
- package/dist/commands/installations.js +46 -1
- package/dist/commands/knowledge-documents.js +24 -32
- package/dist/commands/knowledge.js +5 -0
- package/dist/commands/memory.d.ts +10 -0
- package/dist/commands/memory.js +132 -0
- package/dist/commands/prompts.js +9 -8
- package/dist/commands/schemas.js +15 -6
- package/dist/commands/skill-versions.js +27 -1
- package/dist/commands/skills.js +31 -1
- package/dist/commands/tracing.js +12 -17
- package/dist/index.js +2 -0
- package/dist/lib/agent-decide.d.ts +29 -2
- package/dist/lib/agent-decide.js +100 -3
- package/dist/lib/agent-run.d.ts +50 -3
- package/dist/lib/agent-run.js +168 -16
- package/dist/lib/approval-prompt.js +31 -1
- package/dist/lib/client.d.ts +8 -0
- package/dist/lib/client.js +18 -1
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.js +26 -1
- package/dist/lib/connect-pause.d.ts +71 -0
- package/dist/lib/connect-pause.js +147 -0
- package/dist/lib/errors.js +3 -1
- package/dist/lib/overlay.d.ts +10 -0
- package/dist/lib/overlay.js +20 -0
- package/dist/lib/skills-apply-preview.d.ts +19 -0
- package/dist/lib/skills-apply-preview.js +94 -0
- package/dist/lib/tracing-settings.d.ts +26 -0
- package/dist/lib/tracing-settings.js +25 -0
- package/package.json +1 -1
|
@@ -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
|
package/dist/commands/skills.js
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/dist/commands/tracing.js
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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("<
|
|
112
|
-
sessionsGet.action(async (
|
|
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/{
|
|
116
|
-
params: { path: {
|
|
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
|
|
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
|
package/dist/lib/agent-decide.js
CHANGED
|
@@ -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:
|
|
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
|
package/dist/lib/agent-run.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
|
|
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 {};
|