@2kw/ai 6.3.0-dev.116 → 6.3.0-dev.122
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/knowledge.js +5 -0
- package/dist/lib/agent-run.d.ts +2 -1
- package/dist/lib/agent-run.js +11 -43
- package/dist/lib/config.d.ts +3 -3
- package/dist/lib/config.js +5 -19
- package/dist/lib/connect-pause.d.ts +71 -0
- package/dist/lib/connect-pause.js +147 -0
- package/package.json +1 -1
|
@@ -47,6 +47,7 @@ export function makeKnowledgeCommand() {
|
|
|
47
47
|
.option("--parent-chunk-size <n>", "Parent chunk size (hierarchical chunking)", parseInt)
|
|
48
48
|
.option("--reranker-provider <id>", "Reranker provider ID")
|
|
49
49
|
.option("--hybrid-search <bool>", "Enable hybrid search (true/false)")
|
|
50
|
+
.option("--text-search-language <name>", "Text-search language for the lexical leg (default simple)")
|
|
50
51
|
.action(async (opts, command) => {
|
|
51
52
|
await runAction(command, async () => {
|
|
52
53
|
const client = getClient(command);
|
|
@@ -64,6 +65,7 @@ export function makeKnowledgeCommand() {
|
|
|
64
65
|
parentChunkSize: opts.parentChunkSize,
|
|
65
66
|
rerankerProviderId: opts.rerankerProvider,
|
|
66
67
|
hybridSearchEnabled: opts.hybridSearch !== undefined ? opts.hybridSearch === "true" : undefined,
|
|
68
|
+
textSearchLanguage: opts.textSearchLanguage,
|
|
67
69
|
},
|
|
68
70
|
});
|
|
69
71
|
formatDetail(data, command);
|
|
@@ -83,6 +85,7 @@ export function makeKnowledgeCommand() {
|
|
|
83
85
|
.option("--parent-chunk-size <n>", "New parent chunk size", parseInt)
|
|
84
86
|
.option("--reranker-provider <id>", "New reranker provider ID")
|
|
85
87
|
.option("--hybrid-search <bool>", "Enable hybrid search (true/false)")
|
|
88
|
+
.option("--text-search-language <name>", "Change the text-search language (reindexes in the background); omit to leave it untouched")
|
|
86
89
|
.action(async (id, opts, command) => {
|
|
87
90
|
await runAction(command, async () => {
|
|
88
91
|
const client = getClient(command);
|
|
@@ -107,6 +110,8 @@ export function makeKnowledgeCommand() {
|
|
|
107
110
|
body.rerankerProviderId = opts.rerankerProvider;
|
|
108
111
|
if (opts.hybridSearch !== undefined)
|
|
109
112
|
body.hybridSearchEnabled = opts.hybridSearch === "true";
|
|
113
|
+
if (opts.textSearchLanguage)
|
|
114
|
+
body.textSearchLanguage = opts.textSearchLanguage;
|
|
110
115
|
const { data } = await client.PATCH("/v1/knowledge-bases/{id}", {
|
|
111
116
|
params: { path: { id } },
|
|
112
117
|
body: body,
|
package/dist/lib/agent-run.d.ts
CHANGED
|
@@ -28,7 +28,8 @@ export interface PendingToolCall {
|
|
|
28
28
|
/**
|
|
29
29
|
* A connector the run waits for the user to connect, allow or reconnect in chat.2kw.ai: an open
|
|
30
30
|
* `backbone:connector_auth_request` (#807 R12). The caller never answers the connect call; the
|
|
31
|
-
* continuation re-checks access itself.
|
|
31
|
+
* continuation re-checks access itself. Decoded by `connect-pause.ts` (#1086), whose `id` and
|
|
32
|
+
* `callId` the envelope does not publish.
|
|
32
33
|
*/
|
|
33
34
|
export interface PendingConnection {
|
|
34
35
|
serverLabel: string;
|
package/dist/lib/agent-run.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { DEFAULT_CHAT_URL } from "./config.js";
|
|
3
|
+
import { connectCallIds, connectionsPhrase, pendingConnectionsOf } from "./connect-pause.js";
|
|
3
4
|
import { CliUsageError } from "./errors.js";
|
|
4
5
|
export const CONVERSATION_MODES = ["plan", "ask", "auto"];
|
|
5
6
|
/** `--mode` help text, in the words of the embedded chat's mode chip (#655). */
|
|
@@ -92,49 +93,9 @@ export function shellQuote(value) {
|
|
|
92
93
|
return value;
|
|
93
94
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
94
95
|
}
|
|
95
|
-
/** An open connector consent request; a continuation's projection carries another status. */
|
|
96
|
-
function pendingConnectionsOf(output) {
|
|
97
|
-
return output
|
|
98
|
-
.filter((i) => i.type === "backbone:connector_auth_request" && (i.status === undefined || i.status === "in_progress"))
|
|
99
|
-
.map((i) => ({
|
|
100
|
-
serverLabel: String(i.server_label),
|
|
101
|
-
host: String(i.host),
|
|
102
|
-
reason: String(i.reason),
|
|
103
|
-
...(Array.isArray(i.destinations) && i.destinations.length > 0
|
|
104
|
-
? { destinations: i.destinations.map((d) => String(d)) }
|
|
105
|
-
: {}),
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* The call ids a connect pause withholds from the caller: the call each request names, and every
|
|
110
|
-
* `mcp__<label>__connect` call for a pending connector (the model may call it more than once; one
|
|
111
|
-
* request stands for all of them).
|
|
112
|
-
*/
|
|
113
|
-
function connectCallIds(output, connections) {
|
|
114
|
-
const connectNames = new Set(connections.map((c) => `mcp__${c.serverLabel}__connect`));
|
|
115
|
-
const ids = new Set();
|
|
116
|
-
for (const item of output) {
|
|
117
|
-
if (item.type === "backbone:connector_auth_request" && (item.status === undefined || item.status === "in_progress")) {
|
|
118
|
-
ids.add(String(item.call_id));
|
|
119
|
-
}
|
|
120
|
-
else if (item.type === "function_call" && connectNames.has(String(item.name))) {
|
|
121
|
-
ids.add(String(item.call_id));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return ids;
|
|
125
|
-
}
|
|
126
|
-
function connectionPhrase(c) {
|
|
127
|
-
const target = `${c.serverLabel} (${c.host})`;
|
|
128
|
-
if (c.reason === "allow")
|
|
129
|
-
return `allow the agent to use ${target}`;
|
|
130
|
-
if (c.reason === "reconnect")
|
|
131
|
-
return `reconnect ${target}`;
|
|
132
|
-
return `connect ${target}`;
|
|
133
|
-
}
|
|
134
96
|
/** "Connect a (h), allow the agent to use b (h) and reconnect c (h)". */
|
|
135
97
|
export function connectionsSentence(connections) {
|
|
136
|
-
const
|
|
137
|
-
const joined = phrases.length > 1 ? `${phrases.slice(0, -1).join(", ")} and ${phrases.at(-1)}` : phrases[0] ?? "";
|
|
98
|
+
const joined = connectionsPhrase(connections);
|
|
138
99
|
return joined.charAt(0).toUpperCase() + joined.slice(1);
|
|
139
100
|
}
|
|
140
101
|
/** Joins a connect pause's instruction to the command that continues it; {@link printRunText} breaks the line there. */
|
|
@@ -204,8 +165,15 @@ export function buildRunEnvelope(result, agentRef, chatUrl = DEFAULT_CHAT_URL) {
|
|
|
204
165
|
reason: typeof i.reason === "string" && i.reason ? i.reason : null,
|
|
205
166
|
preview: i.preview && typeof i.preview === "object" && !Array.isArray(i.preview) ? i.preview : null,
|
|
206
167
|
}));
|
|
207
|
-
|
|
208
|
-
const
|
|
168
|
+
// The one decoding rule (#1086): only an open request with its id, call id, label and host.
|
|
169
|
+
const connectRequests = pendingConnectionsOf(output);
|
|
170
|
+
const pendingConnections = connectRequests.map(({ serverLabel, host, reason, destinations }) => ({
|
|
171
|
+
serverLabel,
|
|
172
|
+
host,
|
|
173
|
+
reason,
|
|
174
|
+
...(destinations ? { destinations } : {}),
|
|
175
|
+
}));
|
|
176
|
+
const withheldCallIds = new Set([...pendingApprovals.map((a) => a.callId), ...connectCallIds(output, connectRequests)]);
|
|
209
177
|
const toolCalls = [];
|
|
210
178
|
const pendingToolCalls = [];
|
|
211
179
|
for (const item of output) {
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -52,12 +52,12 @@ export declare const DEFAULT_BASE_URL: string;
|
|
|
52
52
|
*/
|
|
53
53
|
export declare const DEFAULT_AUTH_URL: string;
|
|
54
54
|
export declare function defaultAuthUrlFor(baseUrl: string): string;
|
|
55
|
-
|
|
56
|
-
export declare const DEFAULT_CHAT_URL = "https://chat.2kw.ai";
|
|
55
|
+
export { DEFAULT_CHAT_URL } from "./connect-pause.js";
|
|
57
56
|
/**
|
|
58
57
|
* The chat web host that belongs to an API base URL, where a member connects a connector
|
|
59
58
|
* (`<chat>/connectors`). `AI_2KW_CHAT_URL` overrides the mapping, for deployments the map
|
|
60
|
-
* does not know; a trailing slash is dropped so callers can append a path.
|
|
59
|
+
* does not know; a trailing slash is dropped so callers can append a path. The mapping itself
|
|
60
|
+
* is `connect-pause.ts`'s {@link chatOriginFor}, shared with the MCP server and n8n (#1086).
|
|
61
61
|
*/
|
|
62
62
|
export declare function chatUrlFor(baseUrl: string, env?: NodeJS.ProcessEnv): string;
|
|
63
63
|
declare const store: Conf<BackboneConfigStore>;
|
package/dist/lib/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Conf from "conf";
|
|
2
2
|
import { readFileSync, existsSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
+
import { chatOriginFor } from "./connect-pause.js";
|
|
4
5
|
/**
|
|
5
6
|
* Fallback base URL when the user has no context, env var, or local config.
|
|
6
7
|
* Override at runtime by setting AI_2KW_DEFAULT_BASE_URL (or the legacy
|
|
@@ -36,33 +37,18 @@ export function defaultAuthUrlFor(baseUrl) {
|
|
|
36
37
|
return DEFAULT_AUTH_URL;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
-
export const DEFAULT_CHAT_URL = "https://chat.2kw.ai";
|
|
41
|
-
/** Known API-host → chat web host mappings; a connect pause points the user there (#807 R11). */
|
|
42
|
-
const CHAT_URL_BY_API_HOST = {
|
|
43
|
-
"api.2kw.ai": "https://chat.2kw.ai",
|
|
44
|
-
"api-dev.2kw.ai": "https://chat-dev.2kw.ai",
|
|
45
|
-
"backbone.manfred-kunze.dev": "https://chat.2kw.ai",
|
|
46
|
-
"localhost:8080": "http://localhost:3000",
|
|
47
|
-
"127.0.0.1:8080": "http://localhost:3000",
|
|
48
|
-
};
|
|
40
|
+
export { DEFAULT_CHAT_URL } from "./connect-pause.js";
|
|
49
41
|
/**
|
|
50
42
|
* The chat web host that belongs to an API base URL, where a member connects a connector
|
|
51
43
|
* (`<chat>/connectors`). `AI_2KW_CHAT_URL` overrides the mapping, for deployments the map
|
|
52
|
-
* does not know; a trailing slash is dropped so callers can append a path.
|
|
44
|
+
* does not know; a trailing slash is dropped so callers can append a path. The mapping itself
|
|
45
|
+
* is `connect-pause.ts`'s {@link chatOriginFor}, shared with the MCP server and n8n (#1086).
|
|
53
46
|
*/
|
|
54
47
|
export function chatUrlFor(baseUrl, env = process.env) {
|
|
55
48
|
const override = env.AI_2KW_CHAT_URL?.trim();
|
|
56
49
|
if (override)
|
|
57
50
|
return override.replace(/\/+$/, "");
|
|
58
|
-
|
|
59
|
-
const host = new URL(baseUrl).host;
|
|
60
|
-
// hasOwn, as in defaultAuthUrlFor: a host named after an Object.prototype key must not resolve.
|
|
61
|
-
return Object.hasOwn(CHAT_URL_BY_API_HOST, host) ? CHAT_URL_BY_API_HOST[host] : DEFAULT_CHAT_URL;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return DEFAULT_CHAT_URL;
|
|
65
|
-
}
|
|
51
|
+
return chatOriginFor(baseUrl);
|
|
66
52
|
}
|
|
67
53
|
// One-shot flag so we only print the deprecation warning once per process,
|
|
68
54
|
// even if resolveConfig is called multiple times across commands.
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
|
4
|
+
* connect, allow or reconnect in chat.2kw.ai.
|
|
5
|
+
*
|
|
6
|
+
* This file is canonical. `mcp/src/lib/connect-pause.ts` and
|
|
7
|
+
* `n8n/nodes/TwoKw/operations/connect-pause.ts` are byte copies of it under a header: edit it
|
|
8
|
+
* here, then run `npm run sync:connect-pause` in mcp/ and `npm run sync-connect-pause` in n8n/.
|
|
9
|
+
* Its golden fixture, `cli/tests/fixtures/connect-pause-cases.json`, is copied the same way into
|
|
10
|
+
* mcp/, n8n/ and surface/, and every client's decoder test runs every case of it.
|
|
11
|
+
*
|
|
12
|
+
* Plain TypeScript with no imports, no `process`, no Node types and no timers, so n8n's source
|
|
13
|
+
* scanner and its empty `dependencies` accept the copy. The CLI and the MCP server keep their
|
|
14
|
+
* `AI_2KW_CHAT_URL` override in their own code, around {@link chatOriginFor}.
|
|
15
|
+
*/
|
|
16
|
+
/** Fallback chat.2kw.ai origin when the API host is not one we recognise. */
|
|
17
|
+
export declare const DEFAULT_CHAT_URL = "https://chat.2kw.ai";
|
|
18
|
+
/** Known API-host → chat web host mappings; a connect pause points the user there (#807 R11). */
|
|
19
|
+
export declare const CHAT_URL_BY_API_HOST: Readonly<Record<string, string>>;
|
|
20
|
+
/**
|
|
21
|
+
* The chat web host that belongs to an API base URL, where a member connects a connector
|
|
22
|
+
* (`<chat>/connectors`). An unknown host, an unparsable URL or no URL at all is chat.2kw.ai.
|
|
23
|
+
*/
|
|
24
|
+
export declare function chatOriginFor(baseUrl: unknown): string;
|
|
25
|
+
/** What the user is asked to do: connect, allow the agent, or reconnect. */
|
|
26
|
+
export type ConnectReason = "connect" | "allow" | "reconnect";
|
|
27
|
+
export declare const CONNECT_REASONS: readonly ConnectReason[];
|
|
28
|
+
/**
|
|
29
|
+
* One open connect request. `id` (`cauth_<call id>`) and `callId` (the first connect call for
|
|
30
|
+
* this connector) are what a client needs to withhold the connect calls; the CLI and the MCP
|
|
31
|
+
* server drop both from what they publish.
|
|
32
|
+
*/
|
|
33
|
+
export interface PendingConnection {
|
|
34
|
+
id: string;
|
|
35
|
+
callId: string;
|
|
36
|
+
serverLabel: string;
|
|
37
|
+
/** The MCP server's host: what the user is asked to connect to. */
|
|
38
|
+
host: string;
|
|
39
|
+
reason: ConnectReason;
|
|
40
|
+
/** The egress difference an allow is asked again for (#807 R9); absent otherwise, never empty. */
|
|
41
|
+
destinations?: string[];
|
|
42
|
+
}
|
|
43
|
+
/** Enough of a connection to name it in a sentence. */
|
|
44
|
+
export interface ConnectTarget {
|
|
45
|
+
serverLabel: string;
|
|
46
|
+
host: string;
|
|
47
|
+
reason: string;
|
|
48
|
+
}
|
|
49
|
+
/** The synthetic tool a connector's connect pause is raised through (#806). */
|
|
50
|
+
export declare function connectToolName(serverLabel: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* The connectors a response's `output` still waits on: every `in_progress` connect request whose
|
|
53
|
+
* id no other-status projection in the same list resolved, one per id, in order. An item without
|
|
54
|
+
* a non-empty `id`, `call_id`, `server_label` or `host` cannot be acted on and is skipped; a
|
|
55
|
+
* `reason` outside the three reads as `connect`, the action that always applies; only the string
|
|
56
|
+
* entries of `destinations` are kept, and none at all omits the field.
|
|
57
|
+
*/
|
|
58
|
+
export declare function pendingConnectionsOf(output: unknown): PendingConnection[];
|
|
59
|
+
/**
|
|
60
|
+
* The call ids a connect pause withholds from the caller: the call each pending request names;
|
|
61
|
+
* the own `call_id` of every open connect request, even one {@link pendingConnectionsOf} could not
|
|
62
|
+
* describe (a missing `server_label` or `host`), since a connect call is the engine's to answer,
|
|
63
|
+
* never the client's (R7); and every `mcp__<label>__connect` call for a connector in
|
|
64
|
+
* `connections` (the model may call it more than once; one request stands for all of them).
|
|
65
|
+
*/
|
|
66
|
+
export declare function connectCallIds(output: unknown, connections: readonly Pick<PendingConnection, "callId" | "serverLabel">[]): Set<string>;
|
|
67
|
+
/** "connect erp (erp.example.com)", "allow the agent to use …" or "reconnect …". */
|
|
68
|
+
export declare function connectionPhrase(c: ConnectTarget): string;
|
|
69
|
+
/** "connect a (h), allow the agent to use b (h) and reconnect c (h)"; empty for none. */
|
|
70
|
+
export declare function connectionsPhrase(connections: readonly ConnectTarget[]): string;
|
|
71
|
+
//# sourceMappingURL=connect-pause.d.ts.map
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
|
4
|
+
* connect, allow or reconnect in chat.2kw.ai.
|
|
5
|
+
*
|
|
6
|
+
* This file is canonical. `mcp/src/lib/connect-pause.ts` and
|
|
7
|
+
* `n8n/nodes/TwoKw/operations/connect-pause.ts` are byte copies of it under a header: edit it
|
|
8
|
+
* here, then run `npm run sync:connect-pause` in mcp/ and `npm run sync-connect-pause` in n8n/.
|
|
9
|
+
* Its golden fixture, `cli/tests/fixtures/connect-pause-cases.json`, is copied the same way into
|
|
10
|
+
* mcp/, n8n/ and surface/, and every client's decoder test runs every case of it.
|
|
11
|
+
*
|
|
12
|
+
* Plain TypeScript with no imports, no `process`, no Node types and no timers, so n8n's source
|
|
13
|
+
* scanner and its empty `dependencies` accept the copy. The CLI and the MCP server keep their
|
|
14
|
+
* `AI_2KW_CHAT_URL` override in their own code, around {@link chatOriginFor}.
|
|
15
|
+
*/
|
|
16
|
+
/** Fallback chat.2kw.ai origin when the API host is not one we recognise. */
|
|
17
|
+
export const DEFAULT_CHAT_URL = "https://chat.2kw.ai";
|
|
18
|
+
/** Known API-host → chat web host mappings; a connect pause points the user there (#807 R11). */
|
|
19
|
+
export const CHAT_URL_BY_API_HOST = {
|
|
20
|
+
"api.2kw.ai": "https://chat.2kw.ai",
|
|
21
|
+
"api-dev.2kw.ai": "https://chat-dev.2kw.ai",
|
|
22
|
+
"backbone.manfred-kunze.dev": "https://chat.2kw.ai",
|
|
23
|
+
"localhost:8080": "http://localhost:3000",
|
|
24
|
+
"127.0.0.1:8080": "http://localhost:3000",
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The chat web host that belongs to an API base URL, where a member connects a connector
|
|
28
|
+
* (`<chat>/connectors`). An unknown host, an unparsable URL or no URL at all is chat.2kw.ai.
|
|
29
|
+
*/
|
|
30
|
+
export function chatOriginFor(baseUrl) {
|
|
31
|
+
try {
|
|
32
|
+
const host = new URL(String(baseUrl ?? "")).host;
|
|
33
|
+
// hasOwn, not a bare index: a host named after an Object.prototype key must not resolve.
|
|
34
|
+
return Object.hasOwn(CHAT_URL_BY_API_HOST, host) ? CHAT_URL_BY_API_HOST[host] ?? DEFAULT_CHAT_URL : DEFAULT_CHAT_URL;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return DEFAULT_CHAT_URL;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const CONNECT_REASONS = ["connect", "allow", "reconnect"];
|
|
41
|
+
const CONNECT_REQUEST = "backbone:connector_auth_request";
|
|
42
|
+
function itemsOf(output) {
|
|
43
|
+
if (!Array.isArray(output))
|
|
44
|
+
return [];
|
|
45
|
+
return output.filter((item) => typeof item === "object" && item !== null && !Array.isArray(item));
|
|
46
|
+
}
|
|
47
|
+
function text(value) {
|
|
48
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
49
|
+
}
|
|
50
|
+
/** The synthetic tool a connector's connect pause is raised through (#806). */
|
|
51
|
+
export function connectToolName(serverLabel) {
|
|
52
|
+
return `mcp__${serverLabel}__connect`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The connectors a response's `output` still waits on: every `in_progress` connect request whose
|
|
56
|
+
* id no other-status projection in the same list resolved, one per id, in order. An item without
|
|
57
|
+
* a non-empty `id`, `call_id`, `server_label` or `host` cannot be acted on and is skipped; a
|
|
58
|
+
* `reason` outside the three reads as `connect`, the action that always applies; only the string
|
|
59
|
+
* entries of `destinations` are kept, and none at all omits the field.
|
|
60
|
+
*/
|
|
61
|
+
export function pendingConnectionsOf(output) {
|
|
62
|
+
const items = itemsOf(output);
|
|
63
|
+
const resolved = new Set();
|
|
64
|
+
for (const item of items) {
|
|
65
|
+
const id = text(item.id);
|
|
66
|
+
if (item.type === CONNECT_REQUEST && id && item.status !== "in_progress")
|
|
67
|
+
resolved.add(id);
|
|
68
|
+
}
|
|
69
|
+
const pending = new Map();
|
|
70
|
+
for (const item of items) {
|
|
71
|
+
if (item.type !== CONNECT_REQUEST || item.status !== "in_progress")
|
|
72
|
+
continue;
|
|
73
|
+
const id = text(item.id);
|
|
74
|
+
const callId = text(item.call_id);
|
|
75
|
+
const serverLabel = text(item.server_label);
|
|
76
|
+
const host = text(item.host);
|
|
77
|
+
if (!id || !callId || !serverLabel || !host || resolved.has(id) || pending.has(id))
|
|
78
|
+
continue;
|
|
79
|
+
const reason = text(item.reason);
|
|
80
|
+
const destinations = Array.isArray(item.destinations)
|
|
81
|
+
? item.destinations.filter((entry) => typeof entry === "string")
|
|
82
|
+
: [];
|
|
83
|
+
pending.set(id, {
|
|
84
|
+
id,
|
|
85
|
+
callId,
|
|
86
|
+
serverLabel,
|
|
87
|
+
host,
|
|
88
|
+
reason: reason && CONNECT_REASONS.includes(reason) ? reason : "connect",
|
|
89
|
+
...(destinations.length > 0 ? { destinations } : {}),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return [...pending.values()];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The call ids a connect pause withholds from the caller: the call each pending request names;
|
|
96
|
+
* the own `call_id` of every open connect request, even one {@link pendingConnectionsOf} could not
|
|
97
|
+
* describe (a missing `server_label` or `host`), since a connect call is the engine's to answer,
|
|
98
|
+
* never the client's (R7); and every `mcp__<label>__connect` call for a connector in
|
|
99
|
+
* `connections` (the model may call it more than once; one request stands for all of them).
|
|
100
|
+
*/
|
|
101
|
+
export function connectCallIds(output, connections) {
|
|
102
|
+
const names = new Set(connections.map((c) => connectToolName(c.serverLabel)));
|
|
103
|
+
const ids = new Set(connections.map((c) => c.callId));
|
|
104
|
+
const items = itemsOf(output);
|
|
105
|
+
// Every open connect request's own call id is withheld, even one pendingConnectionsOf could
|
|
106
|
+
// not fully describe (a missing server_label or host): the paired function_call must never be
|
|
107
|
+
// left for the caller to fabricate an output for (R7).
|
|
108
|
+
const resolved = new Set();
|
|
109
|
+
for (const item of items) {
|
|
110
|
+
const id = text(item.id);
|
|
111
|
+
if (item.type === CONNECT_REQUEST && id && item.status !== "in_progress")
|
|
112
|
+
resolved.add(id);
|
|
113
|
+
}
|
|
114
|
+
for (const item of items) {
|
|
115
|
+
if (item.type !== CONNECT_REQUEST || item.status !== "in_progress")
|
|
116
|
+
continue;
|
|
117
|
+
const id = text(item.id);
|
|
118
|
+
if (id && resolved.has(id))
|
|
119
|
+
continue;
|
|
120
|
+
const callId = text(item.call_id);
|
|
121
|
+
if (callId)
|
|
122
|
+
ids.add(callId);
|
|
123
|
+
}
|
|
124
|
+
for (const item of items) {
|
|
125
|
+
const callId = text(item.call_id);
|
|
126
|
+
if (item.type === "function_call" && callId && typeof item.name === "string" && names.has(item.name))
|
|
127
|
+
ids.add(callId);
|
|
128
|
+
}
|
|
129
|
+
return ids;
|
|
130
|
+
}
|
|
131
|
+
/** "connect erp (erp.example.com)", "allow the agent to use …" or "reconnect …". */
|
|
132
|
+
export function connectionPhrase(c) {
|
|
133
|
+
const target = `${c.serverLabel} (${c.host})`;
|
|
134
|
+
if (c.reason === "allow")
|
|
135
|
+
return `allow the agent to use ${target}`;
|
|
136
|
+
if (c.reason === "reconnect")
|
|
137
|
+
return `reconnect ${target}`;
|
|
138
|
+
return `connect ${target}`;
|
|
139
|
+
}
|
|
140
|
+
/** "connect a (h), allow the agent to use b (h) and reconnect c (h)"; empty for none. */
|
|
141
|
+
export function connectionsPhrase(connections) {
|
|
142
|
+
const phrases = connections.map(connectionPhrase);
|
|
143
|
+
if (phrases.length < 2)
|
|
144
|
+
return phrases[0] ?? "";
|
|
145
|
+
return `${phrases.slice(0, -1).join(", ")} and ${phrases[phrases.length - 1]}`;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=connect-pause.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2kw/ai",
|
|
3
|
-
"version": "6.3.0-dev.
|
|
3
|
+
"version": "6.3.0-dev.122",
|
|
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",
|