@2kw/ai 8.0.2-dev.2 → 8.0.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/dist/commands/agent-run.js +4 -13
- package/dist/commands/responses.js +1 -2
- package/dist/lib/agent-decide.js +5 -4
- package/dist/lib/output.d.ts +1 -2
- package/dist/lib/output.js +1 -1
- package/package.json +1 -1
- package/dist/lib/response-stream.d.ts +0 -14
- package/dist/lib/response-stream.js +0 -120
|
@@ -9,7 +9,6 @@ import { buildRunEnvelope, EXIT_CODES, MODE_OPTION_HELP, modeItem, parseModeOpti
|
|
|
9
9
|
import { continueWithDecisions, decisionsFromAnswers, fetchPendingApprovals, parseToolAnswers, planContinuation, } from "../lib/agent-decide.js";
|
|
10
10
|
import { PromptAbortedError, promptApprovals, readlineAsk } from "../lib/approval-prompt.js";
|
|
11
11
|
import { withModelOverride } from "../lib/agent-models.js";
|
|
12
|
-
import { postResponse } from "../lib/response-stream.js";
|
|
13
12
|
import { readStdinInput } from "./ai.js";
|
|
14
13
|
export function resolveRunInput(arg, inputFile, readStdin = readStdinInput) {
|
|
15
14
|
const text = readRunInput(arg, inputFile, readStdin);
|
|
@@ -147,7 +146,7 @@ export function makeAgentRunCommand() {
|
|
|
147
146
|
const body = {
|
|
148
147
|
model: `agent/${agentRef}`,
|
|
149
148
|
input: mode ? [...turn, modeItem(mode)] : text ?? [],
|
|
150
|
-
stream:
|
|
149
|
+
stream: false,
|
|
151
150
|
};
|
|
152
151
|
if (opts.conversation)
|
|
153
152
|
body.conversation = opts.conversation;
|
|
@@ -155,17 +154,9 @@ export function makeAgentRunCommand() {
|
|
|
155
154
|
body.previous_response_id = opts.continue;
|
|
156
155
|
let data;
|
|
157
156
|
try {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return;
|
|
162
|
-
// ora's render() writes a frame even off a TTY, where it never spins.
|
|
163
|
-
if (spinner.isSpinning)
|
|
164
|
-
spinner.clear();
|
|
165
|
-
console.error(`Conversation: ${started.conversation.id}`);
|
|
166
|
-
if (spinner.isSpinning)
|
|
167
|
-
spinner.render();
|
|
168
|
-
}));
|
|
157
|
+
// The generated body type does not model a bare-string input, hence the cast.
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
159
|
+
({ data } = await withSpinner("Running agent...", () => client.POST("/v1/responses", { body })));
|
|
169
160
|
}
|
|
170
161
|
catch (err) {
|
|
171
162
|
throw await withAgentSuggestions(client, agentRef, err);
|
|
@@ -7,8 +7,7 @@ export function makeResponsesCommand() {
|
|
|
7
7
|
cmd
|
|
8
8
|
.command("get")
|
|
9
9
|
.description("Read a background response as it stands now: queued, in_progress, requires_action, completed, " +
|
|
10
|
-
"incomplete, failed or cancelled. Only background responses can be read by id
|
|
11
|
-
"`agents run` is read with `conversations items list <conversationId>`.")
|
|
10
|
+
"incomplete, failed or cancelled. Only background responses can be read by id.")
|
|
12
11
|
.argument("<responseId>", "Response ID")
|
|
13
12
|
.action(async (responseId, _opts, command) => {
|
|
14
13
|
await runAction(command, async () => {
|
package/dist/lib/agent-decide.js
CHANGED
|
@@ -2,7 +2,6 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { modeItem } from "./agent-run.js";
|
|
3
3
|
import { CliUsageError } from "./errors.js";
|
|
4
4
|
import { paginationParams } from "./pagination.js";
|
|
5
|
-
import { postResponse } from "./response-stream.js";
|
|
6
5
|
const defaultIo = {
|
|
7
6
|
readFile: (path) => readFileSync(path, "utf-8"),
|
|
8
7
|
// Not readStdinInput: that one trims, and a tool result is sent as read (P5).
|
|
@@ -216,10 +215,12 @@ export async function continueWithDecisions(client, agentId, responseId, decisio
|
|
|
216
215
|
const body = {
|
|
217
216
|
model: `agent/${agentId}${label ? `@${label}` : ""}${model ? `#${model}` : ""}`,
|
|
218
217
|
previous_response_id: responseId,
|
|
219
|
-
stream:
|
|
218
|
+
stream: false,
|
|
220
219
|
input: mode ? [...items, modeItem(mode)] : items,
|
|
221
220
|
};
|
|
222
|
-
//
|
|
223
|
-
|
|
221
|
+
// The generated body type does not model backbone: extension items, hence the cast.
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
|
+
const { data } = await client.POST("/v1/responses", { body });
|
|
224
|
+
return data;
|
|
224
225
|
}
|
|
225
226
|
//# sourceMappingURL=agent-decide.js.map
|
package/dist/lib/output.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type Ora } from "ora";
|
|
2
1
|
import type { Command } from "commander";
|
|
3
2
|
type AnyRecord = Record<string, any>;
|
|
4
3
|
/**
|
|
@@ -25,6 +24,6 @@ export declare function formatSuccess(message: string, command: Command): void;
|
|
|
25
24
|
/**
|
|
26
25
|
* Run an async action with a spinner.
|
|
27
26
|
*/
|
|
28
|
-
export declare function withSpinner<T>(text: string, action: (
|
|
27
|
+
export declare function withSpinner<T>(text: string, action: () => Promise<T>): Promise<T>;
|
|
29
28
|
export {};
|
|
30
29
|
//# sourceMappingURL=output.d.ts.map
|
package/dist/lib/output.js
CHANGED
|
@@ -88,7 +88,7 @@ export function formatSuccess(message, command) {
|
|
|
88
88
|
export async function withSpinner(text, action) {
|
|
89
89
|
const spinner = ora(text).start();
|
|
90
90
|
try {
|
|
91
|
-
const result = await action(
|
|
91
|
+
const result = await action();
|
|
92
92
|
spinner.succeed();
|
|
93
93
|
return result;
|
|
94
94
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2kw/ai",
|
|
3
|
-
"version": "8.0.2
|
|
3
|
+
"version": "8.0.2",
|
|
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",
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ApiClient } from "./agent-lookup.js";
|
|
2
|
-
import type { ResponsesResult } from "./agent-run.js";
|
|
3
|
-
/**
|
|
4
|
-
* POSTs an agent turn to `/v1/responses` and returns its terminal response, as `stream: false` would (#1729).
|
|
5
|
-
* The turn is streamed: a proxy in front of the API cuts a request that answers nothing for 100 s (HTTP 524),
|
|
6
|
-
* and a stream's events and keepalives keep it open however long the run takes. A model that cannot stream
|
|
7
|
-
* is refused before any work (`stream_not_supported`), so that turn is sent again without a stream.
|
|
8
|
-
*
|
|
9
|
-
* `onStart` gets the response as `response.created` names it, before the run works. Once the conversation is
|
|
10
|
-
* known, a cut stream or Ctrl+C names it and the command that reads the run's result: a synchronous
|
|
11
|
-
* response cannot be read by id afterwards (`GET /v1/responses/{id}` reads background responses only).
|
|
12
|
-
*/
|
|
13
|
-
export declare function postResponse(client: ApiClient, body: Record<string, unknown>, onStart?: (started: ResponsesResult) => void): Promise<ResponsesResult>;
|
|
14
|
-
//# sourceMappingURL=response-stream.d.ts.map
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { BackboneApiError } from "./errors.js";
|
|
2
|
-
/**
|
|
3
|
-
* POSTs an agent turn to `/v1/responses` and returns its terminal response, as `stream: false` would (#1729).
|
|
4
|
-
* The turn is streamed: a proxy in front of the API cuts a request that answers nothing for 100 s (HTTP 524),
|
|
5
|
-
* and a stream's events and keepalives keep it open however long the run takes. A model that cannot stream
|
|
6
|
-
* is refused before any work (`stream_not_supported`), so that turn is sent again without a stream.
|
|
7
|
-
*
|
|
8
|
-
* `onStart` gets the response as `response.created` names it, before the run works. Once the conversation is
|
|
9
|
-
* known, a cut stream or Ctrl+C names it and the command that reads the run's result: a synchronous
|
|
10
|
-
* response cannot be read by id afterwards (`GET /v1/responses/{id}` reads background responses only).
|
|
11
|
-
*/
|
|
12
|
-
export async function postResponse(client, body, onStart) {
|
|
13
|
-
let response;
|
|
14
|
-
try {
|
|
15
|
-
// The generated body type does not model a bare-string input or backbone: items, hence the cast.
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
-
({ response } = await client.POST("/v1/responses", { body: { ...body, stream: true }, parseAs: "stream" }));
|
|
18
|
-
}
|
|
19
|
-
catch (err) {
|
|
20
|
-
if (!(err instanceof BackboneApiError && err.code === "stream_not_supported"))
|
|
21
|
-
throw err;
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
-
const { data } = await client.POST("/v1/responses", { body: { ...body, stream: false } });
|
|
24
|
-
return data;
|
|
25
|
-
}
|
|
26
|
-
if (!response.body || !response.headers.get("content-type")?.includes("text/event-stream")) {
|
|
27
|
-
return (await response.json());
|
|
28
|
-
}
|
|
29
|
-
return readResponseStream(response.body, onStart);
|
|
30
|
-
}
|
|
31
|
-
/** How to read a run whose stream was lost: the conversation keeps every item the run wrote. */
|
|
32
|
-
function readHint(started) {
|
|
33
|
-
const conversation = started?.conversation?.id;
|
|
34
|
-
return conversation
|
|
35
|
-
? ` The run goes on on the server; read its result with: 2kw conversations items list ${conversation}`
|
|
36
|
-
: "";
|
|
37
|
-
}
|
|
38
|
-
/** Reads Open Responses server-sent events up to the terminal one; comments (keepalives) are skipped. */
|
|
39
|
-
async function readResponseStream(body, onStart) {
|
|
40
|
-
let started;
|
|
41
|
-
let error;
|
|
42
|
-
const failed = () => new BackboneApiError({
|
|
43
|
-
status: 500,
|
|
44
|
-
error: error?.code ?? "run_failed",
|
|
45
|
-
code: error?.code ?? undefined,
|
|
46
|
-
message: error?.message ?? "The response failed.",
|
|
47
|
-
timestamp: new Date().toISOString(),
|
|
48
|
-
});
|
|
49
|
-
const onInterrupt = () => {
|
|
50
|
-
console.error(`Interrupted.${readHint(started)}`);
|
|
51
|
-
process.exit(130);
|
|
52
|
-
};
|
|
53
|
-
const handle = (frame) => {
|
|
54
|
-
const data = frame
|
|
55
|
-
.split(/\r?\n/)
|
|
56
|
-
.filter((line) => line.startsWith("data:"))
|
|
57
|
-
.map((line) => line.slice(5).trimStart())
|
|
58
|
-
.join("\n");
|
|
59
|
-
if (!data || data === "[DONE]")
|
|
60
|
-
return undefined;
|
|
61
|
-
let event;
|
|
62
|
-
try {
|
|
63
|
-
event = JSON.parse(data);
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return undefined; // a garbled frame is skipped: the run goes on, and its terminal event still ends it
|
|
67
|
-
}
|
|
68
|
-
switch (event.type) {
|
|
69
|
-
case "response.created":
|
|
70
|
-
started = event.response;
|
|
71
|
-
if (started?.conversation?.id)
|
|
72
|
-
process.once("SIGINT", onInterrupt);
|
|
73
|
-
onStart?.(event.response);
|
|
74
|
-
return undefined;
|
|
75
|
-
case "error":
|
|
76
|
-
error = event;
|
|
77
|
-
return undefined;
|
|
78
|
-
case "response.completed":
|
|
79
|
-
case "response.incomplete":
|
|
80
|
-
return event.response;
|
|
81
|
-
case "response.failed":
|
|
82
|
-
throw failed();
|
|
83
|
-
default:
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
const decoder = new TextDecoder();
|
|
88
|
-
let buffer = "";
|
|
89
|
-
const reader = body.getReader();
|
|
90
|
-
try {
|
|
91
|
-
for (;;) {
|
|
92
|
-
let chunk;
|
|
93
|
-
try {
|
|
94
|
-
chunk = await reader.read();
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
throw new Error(`The connection closed before the run finished (${err.message}).${readHint(started)}`);
|
|
98
|
-
}
|
|
99
|
-
if (chunk.done)
|
|
100
|
-
break;
|
|
101
|
-
buffer += decoder.decode(chunk.value, { stream: true });
|
|
102
|
-
let end;
|
|
103
|
-
while ((end = /\r?\n\r?\n/.exec(buffer))) {
|
|
104
|
-
const frame = buffer.slice(0, end.index);
|
|
105
|
-
buffer = buffer.slice(end.index + end[0].length);
|
|
106
|
-
const terminal = handle(frame);
|
|
107
|
-
if (terminal)
|
|
108
|
-
return terminal;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
process.removeListener("SIGINT", onInterrupt);
|
|
114
|
-
await reader.cancel().catch(() => { });
|
|
115
|
-
}
|
|
116
|
-
if (error)
|
|
117
|
-
throw failed(); // an error event whose response.failed never arrived
|
|
118
|
-
throw new Error(`The response stream ended before the run finished.${readHint(started)}`);
|
|
119
|
-
}
|
|
120
|
-
//# sourceMappingURL=response-stream.js.map
|