agentic-assistant 0.4.0 → 0.5.1
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/CHANGELOG.md +32 -0
- package/README.md +19 -2
- package/SKILL.md +2 -1
- package/dist/{chat-CXbhePC5.d.ts → chat-CGITyrxg.d.ts} +1 -1
- package/dist/{chunk-FQD4OK2R.js → chunk-2RRM6OLC.js} +1 -1
- package/dist/{chunk-MESH6X6N.js → chunk-5BZENFYL.js} +58 -12
- package/dist/chunk-E7TFMM2Z.js +167 -0
- package/dist/{chunk-C44QT474.js → chunk-JNZPVHNN.js} +1 -1
- package/dist/chunk-QTIEDY3V.js +196 -0
- package/dist/{chunk-YMEM57LX.js → chunk-TOQIHYDH.js} +12 -18
- package/dist/cli.d.ts +24 -3
- package/dist/cli.js +65 -10
- package/dist/client.d.ts +2 -2
- package/dist/client.js +4 -191
- package/dist/demo.js +14 -9
- package/dist/index.d.ts +14 -6
- package/dist/index.js +5 -4
- package/dist/{loop-D4qWbRu6.d.ts → loop-B6aowbyz.d.ts} +1 -1
- package/dist/mcp-server-7R5WCSID.js +13 -0
- package/dist/react.css +18 -15
- package/dist/react.d.ts +1 -1
- package/dist/worker.d.ts +8 -2
- package/dist/worker.js +2 -2
- package/llms.txt +2 -2
- package/package.json +7 -2
- package/dist/chunk-KNUEYFX2.js +0 -82
- package/dist/mcp-server-LXGWKEHG.js +0 -12
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
hasScope,
|
|
5
5
|
parseScope,
|
|
6
6
|
runAssistant
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JNZPVHNN.js";
|
|
8
8
|
|
|
9
9
|
// src/api.ts
|
|
10
10
|
import { PRINCIPAL_KINDS } from "agentic-identity";
|
|
@@ -14,8 +14,8 @@ var MAX_MESSAGE_CHARS = 1e4;
|
|
|
14
14
|
var MAX_REASON_CHARS = 500;
|
|
15
15
|
var DEFAULT_MAX_CONCURRENT_RUNS = 4;
|
|
16
16
|
var APPROVER_SCOPE = "assistant:admin";
|
|
17
|
-
async function callerOf(request, auth, origins) {
|
|
18
|
-
const found = await requirePrincipal(request, auth, { origins });
|
|
17
|
+
async function callerOf(request, auth, origins, scope) {
|
|
18
|
+
const found = await requirePrincipal(request, auth, { origins, scope });
|
|
19
19
|
if (found instanceof Response) return found.status === 401 ? fail(401, "sign in with a valid key", { "www-authenticate": "Bearer" }) : found;
|
|
20
20
|
return toCaller(found) ?? fail(401, "sign in with a valid key", { "www-authenticate": "Bearer" });
|
|
21
21
|
}
|
|
@@ -205,7 +205,7 @@ function createAssistantApi(options) {
|
|
|
205
205
|
const allowed = route === "chat" || verb !== void 0 ? "POST" : "GET";
|
|
206
206
|
if (request.method !== allowed) return fail(405, "method not allowed", { allow: allowed });
|
|
207
207
|
if (route !== "approvals" && id !== void 0) return fail(404, "not found");
|
|
208
|
-
const caller = await callerOf(request, options.auth, options.origins);
|
|
208
|
+
const caller = await callerOf(request, options.auth, options.origins, options.routeScopes?.[route]);
|
|
209
209
|
if (caller instanceof Response) return caller;
|
|
210
210
|
if (route === "tools") {
|
|
211
211
|
const tools = options.registry.list().filter((tool) => hasScope(caller.scopes, tool.scope)).map(({ name, description, scope, risk, inputSchema }) => ({ name, description, scope, risk, inputSchema }));
|
|
@@ -248,6 +248,7 @@ function createAssistantApi(options) {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
// src/static-auth.ts
|
|
251
|
+
import { createMemoryStore, createStoreAuth } from "agentic-identity";
|
|
251
252
|
var MIN_TOKEN_BYTES = 32;
|
|
252
253
|
var encoder = new TextEncoder();
|
|
253
254
|
async function digest(token) {
|
|
@@ -255,7 +256,8 @@ async function digest(token) {
|
|
|
255
256
|
return [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
256
257
|
}
|
|
257
258
|
async function createStaticTokenAuth(tokens) {
|
|
258
|
-
const
|
|
259
|
+
const store = createMemoryStore();
|
|
260
|
+
const ids = /* @__PURE__ */ new Set();
|
|
259
261
|
for (const [token, entry] of Object.entries(tokens)) {
|
|
260
262
|
if (encoder.encode(token).length < MIN_TOKEN_BYTES) throw new Error(`tokens must hold at least ${MIN_TOKEN_BYTES} bytes`);
|
|
261
263
|
if (/\s/.test(token)) throw new Error("a token may not hold white space");
|
|
@@ -267,20 +269,12 @@ async function createStaticTokenAuth(tokens) {
|
|
|
267
269
|
throw new Error("each token needs an id and a list of scopes");
|
|
268
270
|
}
|
|
269
271
|
for (const scope of entry.scopes) parseScope(scope);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
scopes: [...entry.scopes]
|
|
275
|
-
});
|
|
272
|
+
if (ids.has(entry.id)) throw new Error("each token needs its own id");
|
|
273
|
+
ids.add(entry.id);
|
|
274
|
+
await store.put("principals", entry.id, { id: entry.id, kind: "human", scopes: [...entry.scopes] });
|
|
275
|
+
await store.put("credentials", await digest(token), { principalId: entry.id, kind: "api_key" });
|
|
276
276
|
}
|
|
277
|
-
return
|
|
278
|
-
async authenticate(request) {
|
|
279
|
-
const match = /^Bearer ([^\s]+)$/.exec(request.headers.get("authorization") ?? "");
|
|
280
|
-
const context = match ? byDigest.get(await digest(match[1])) : void 0;
|
|
281
|
-
return context ? { ...context, chain: context.chain.map((step) => ({ ...step })), scopes: [...context.scopes] } : null;
|
|
282
|
-
}
|
|
283
|
-
};
|
|
277
|
+
return createStoreAuth(store);
|
|
284
278
|
}
|
|
285
279
|
|
|
286
280
|
// src/worker.ts
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as ChatConfig } from './chat-
|
|
3
|
-
import './loop-
|
|
2
|
+
import { C as ChatConfig } from './chat-CGITyrxg.js';
|
|
3
|
+
import './loop-B6aowbyz.js';
|
|
4
4
|
import './types-DGH8FS56.js';
|
|
5
5
|
|
|
6
|
+
interface RemoteConfig {
|
|
7
|
+
api: string;
|
|
8
|
+
apiTokenEnv: string;
|
|
9
|
+
}
|
|
10
|
+
type RemoteCommand = {
|
|
11
|
+
name: "tools";
|
|
12
|
+
} | {
|
|
13
|
+
name: "approvals";
|
|
14
|
+
} | {
|
|
15
|
+
name: "approve";
|
|
16
|
+
id: string;
|
|
17
|
+
digest: string;
|
|
18
|
+
} | {
|
|
19
|
+
name: "deny";
|
|
20
|
+
id: string;
|
|
21
|
+
reason?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
6
24
|
declare class UsageError extends Error {
|
|
7
25
|
constructor(message: string);
|
|
8
26
|
}
|
|
9
27
|
interface Parsed {
|
|
10
|
-
command: "chat" | "mcp" | "help" | "version";
|
|
28
|
+
command: "chat" | "mcp" | "help" | "version" | "remote";
|
|
11
29
|
config?: ChatConfig;
|
|
30
|
+
remote?: RemoteConfig;
|
|
31
|
+
allowApprove?: boolean;
|
|
32
|
+
remoteCommand?: RemoteCommand;
|
|
12
33
|
message?: string;
|
|
13
34
|
json: boolean;
|
|
14
35
|
}
|
package/dist/cli.js
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ConfigError,
|
|
4
4
|
buildSetup,
|
|
5
|
+
clientFor,
|
|
6
|
+
describeFailure,
|
|
5
7
|
readTextFile,
|
|
6
|
-
runChat
|
|
7
|
-
|
|
8
|
+
runChat,
|
|
9
|
+
runRemote
|
|
10
|
+
} from "./chunk-5BZENFYL.js";
|
|
8
11
|
import "./chunk-HYT732TN.js";
|
|
9
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-2RRM6OLC.js";
|
|
10
13
|
import {
|
|
11
14
|
TOOL_RISKS,
|
|
12
15
|
VERSION,
|
|
13
16
|
printable,
|
|
14
17
|
provenanceLabel
|
|
15
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-JNZPVHNN.js";
|
|
19
|
+
import "./chunk-QTIEDY3V.js";
|
|
16
20
|
|
|
17
21
|
// src/cli.ts
|
|
18
22
|
import { realpathSync } from "fs";
|
|
@@ -30,9 +34,22 @@ An AI assistant that acts inside your apps with the user's own permissions.
|
|
|
30
34
|
|
|
31
35
|
Usage:
|
|
32
36
|
agentic-assistant chat [flags] [message] Send a message. Read stdin when there is no message.
|
|
33
|
-
agentic-assistant mcp [flags] Start an MCP server with
|
|
37
|
+
agentic-assistant mcp [flags] Start an MCP server with the tool assistant_chat. With --api it adds three more.
|
|
38
|
+
agentic-assistant tools [flags] List the tools of a running assistant API, with scope and risk.
|
|
39
|
+
agentic-assistant approvals [flags] List the calls that wait for a person.
|
|
40
|
+
agentic-assistant approve <id> --digest <d> [flags] Approve the exact call. Copy the digest from "approvals".
|
|
41
|
+
agentic-assistant deny <id> [--reason <text>] [flags] Deny the call. A denial is final.
|
|
42
|
+
|
|
43
|
+
Flags for tools, approvals, approve, and deny:
|
|
44
|
+
--api <url> The assistant API origin, like https://assistant.example.com. https, or localhost. Required.
|
|
45
|
+
--api-token-env <NAME> Read the bearer token from this environment variable. Required. Tokens never go in arguments.
|
|
46
|
+
--digest <digest> approve only: the digest of the pending call.
|
|
47
|
+
--reason <text> deny only: why.
|
|
48
|
+
--json Print the result as JSON on stdout.
|
|
34
49
|
|
|
35
50
|
Flags for chat and mcp:
|
|
51
|
+
--api <url> --api-token-env <NAME> mcp only: also serve assistant_tools, assistant_approvals, assistant_deny.
|
|
52
|
+
--allow-approve mcp only, with --api: also serve assistant_approve. Use an agent token (it needs assistant:admin).
|
|
36
53
|
--tools <target> An https MCP URL, or a command that starts an MCP server on stdio. Required.
|
|
37
54
|
--tools-header-env <h=E> Send header h with the value of environment variable E (http only). Repeat it.
|
|
38
55
|
--scope <scope> A scope the assistant may hold, like notes:read. Repeat it. Required.
|
|
@@ -82,6 +99,11 @@ var OPTIONS = {
|
|
|
82
99
|
"max-tokens": { type: "string" },
|
|
83
100
|
"max-cost": { type: "string" },
|
|
84
101
|
"max-tool-calls": { type: "string" },
|
|
102
|
+
api: { type: "string" },
|
|
103
|
+
"api-token-env": { type: "string" },
|
|
104
|
+
"allow-approve": { type: "boolean" },
|
|
105
|
+
digest: { type: "string" },
|
|
106
|
+
reason: { type: "string" },
|
|
85
107
|
json: { type: "boolean" },
|
|
86
108
|
message: { type: "string" },
|
|
87
109
|
help: { type: "boolean", short: "h" },
|
|
@@ -106,8 +128,27 @@ function parseCli(argv) {
|
|
|
106
128
|
if (values.version) return { command: "version", json: false };
|
|
107
129
|
const [command, ...rest] = positionals;
|
|
108
130
|
if (values.help || command === void 0) return { command: "help", json: false };
|
|
109
|
-
|
|
110
|
-
if (!
|
|
131
|
+
const remoteNames = ["tools", "approvals", "approve", "deny"];
|
|
132
|
+
if (command !== "chat" && command !== "mcp" && !remoteNames.includes(command)) throw new UsageError(`unknown command "${command}"`);
|
|
133
|
+
if (values.api === "" || values["api-token-env"] === "") throw new UsageError("--api needs a URL, and --api-token-env a variable name");
|
|
134
|
+
if (values.api === void 0 !== (values["api-token-env"] === void 0)) throw new UsageError("--api and --api-token-env go together");
|
|
135
|
+
const remote = values.api !== void 0 && values["api-token-env"] !== void 0 ? { api: values.api, apiTokenEnv: values["api-token-env"] } : void 0;
|
|
136
|
+
if (command !== "mcp" && command !== "chat" && remote === void 0) throw new UsageError("--api and --api-token-env are required");
|
|
137
|
+
if (command === "chat" && remote !== void 0) throw new UsageError("--api works with mcp and the approval commands, not chat");
|
|
138
|
+
const wantsId = command === "approve" || command === "deny";
|
|
139
|
+
if (remoteNames.includes(command) && rest.length !== (wantsId ? 1 : 0)) throw new UsageError(wantsId ? `${command} needs one approval id` : `${command} takes no arguments`);
|
|
140
|
+
if (values["allow-approve"] && (command !== "mcp" || remote === void 0)) throw new UsageError("--allow-approve works with mcp and --api only");
|
|
141
|
+
if (command !== "approve" && values.digest !== void 0) throw new UsageError("--digest works with approve only");
|
|
142
|
+
if (command !== "deny" && values.reason !== void 0) throw new UsageError("--reason works with deny only");
|
|
143
|
+
if (remoteNames.includes(command)) {
|
|
144
|
+
const chatOnly = Object.keys(values).filter((key) => !["api", "api-token-env", "digest", "reason", "json", "help", "version"].includes(key));
|
|
145
|
+
if (chatOnly.length > 0) throw new UsageError(`--${chatOnly[0]} does not work with ${command}`);
|
|
146
|
+
const id = rest[0];
|
|
147
|
+
if (command === "approve" && !values.digest) throw new UsageError("approve needs --digest <digest>");
|
|
148
|
+
const remoteCommand = command === "approve" ? { name: "approve", id, digest: values.digest } : command === "deny" ? { name: "deny", id, ...values.reason !== void 0 ? { reason: values.reason } : {} } : { name: command };
|
|
149
|
+
return { command: "remote", remote, remoteCommand, json: values.json ?? false };
|
|
150
|
+
}
|
|
151
|
+
if (!values.tools) throw new UsageError(`--tools is required for ${command}: an https MCP URL, or a command that starts an MCP server on stdio`);
|
|
111
152
|
if (!values["tool-scope"]) throw new UsageError("--tool-scope is required");
|
|
112
153
|
const risk = values["tool-risk"] ?? "risky";
|
|
113
154
|
if (!TOOL_RISKS.includes(risk)) throw new UsageError(`--tool-risk must be one of ${TOOL_RISKS.join(", ")}`);
|
|
@@ -159,7 +200,7 @@ function parseCli(argv) {
|
|
|
159
200
|
preApproved,
|
|
160
201
|
approve
|
|
161
202
|
};
|
|
162
|
-
return { command, config, message: values.message ?? (rest.length > 0 ? rest.join(" ") : void 0), json: values.json ?? false };
|
|
203
|
+
return { command, config, remote, allowApprove: values["allow-approve"] ?? false, message: values.message ?? (rest.length > 0 ? rest.join(" ") : void 0), json: values.json ?? false };
|
|
163
204
|
}
|
|
164
205
|
function defaultIo() {
|
|
165
206
|
return {
|
|
@@ -264,16 +305,30 @@ async function runCli(argv, io = defaultIo()) {
|
|
|
264
305
|
`);
|
|
265
306
|
return 0;
|
|
266
307
|
}
|
|
308
|
+
if (parsed.command === "remote") {
|
|
309
|
+
try {
|
|
310
|
+
const client = clientFor(parsed.remote, io.env);
|
|
311
|
+
const { data, text } = await runRemote(client, parsed.remoteCommand);
|
|
312
|
+
io.out(parsed.json ? `${JSON.stringify(data)}
|
|
313
|
+
` : `${text}
|
|
314
|
+
`);
|
|
315
|
+
return 0;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
io.err(error instanceof ConfigError ? `${error.name}: ${oneLine(error.message, 500)}` : describeFailure(error));
|
|
318
|
+
return error instanceof ConfigError ? 2 : 1;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
267
321
|
const config = parsed.config;
|
|
268
322
|
const chatIo = { env: io.env, readFile: io.readFile, ...config.approve === "ask" && io.isTTY ? { ask: io.ask } : {} };
|
|
269
323
|
let setup;
|
|
270
324
|
try {
|
|
325
|
+
const remote = parsed.command === "mcp" && parsed.remote !== void 0 ? clientFor(parsed.remote, io.env) : void 0;
|
|
271
326
|
setup = await buildSetup(config, chatIo);
|
|
272
327
|
for (const skip of setup.skipped.slice(0, MAX_SKIPPED_LINES)) io.err(`skipped tool ${oneLine(skip.name, 100)}: ${oneLine(skip.reason, 200)}`);
|
|
273
328
|
if (setup.skipped.length > MAX_SKIPPED_LINES) io.err(`skipped ${setup.skipped.length - MAX_SKIPPED_LINES} more tools`);
|
|
274
329
|
if (parsed.command === "mcp") {
|
|
275
|
-
const { startAssistantMcp } = await import("./mcp-server-
|
|
276
|
-
await startAssistantMcp(setup);
|
|
330
|
+
const { startAssistantMcp } = await import("./mcp-server-7R5WCSID.js");
|
|
331
|
+
await startAssistantMcp(setup, remote, { approve: parsed.allowApprove ?? false });
|
|
277
332
|
return 0;
|
|
278
333
|
}
|
|
279
334
|
const text = parsed.message ?? (io.isTTY ? void 0 : (await io.readStdin()).trim());
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { h as AssistantEvent, e as ApprovalRequest, b as Actor } from './loop-B6aowbyz.js';
|
|
2
|
+
export { j as DeniedReason, k as DoneReason, p as RunTotals } from './loop-B6aowbyz.js';
|
|
3
3
|
import './types-DGH8FS56.js';
|
|
4
4
|
|
|
5
5
|
/** The error code of a failed call. The client derives it from the HTTP status and the error body. */
|
package/dist/client.js
CHANGED
|
@@ -1,194 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
403: "FORBIDDEN",
|
|
6
|
-
404: "NOT_FOUND",
|
|
7
|
-
405: "METHOD_NOT_ALLOWED",
|
|
8
|
-
409: "CONFLICT",
|
|
9
|
-
429: "RATE_LIMITED"
|
|
10
|
-
};
|
|
11
|
-
var AssistantApiError = class extends Error {
|
|
12
|
-
code;
|
|
13
|
-
status;
|
|
14
|
-
/** Seconds to wait before a new try, from `retry-after` of a 429 reply. */
|
|
15
|
-
retryAfter;
|
|
16
|
-
constructor(code, status, message, retryAfter) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.name = "AssistantApiError";
|
|
19
|
-
this.code = code;
|
|
20
|
-
this.status = status;
|
|
21
|
-
this.retryAfter = retryAfter;
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
25
|
-
var isString = (v) => typeof v === "string";
|
|
26
|
-
var RISKS = ["safe", "risky", "irreversible"];
|
|
27
|
-
var isTool = (v) => isObject(v) && isString(v.name) && isString(v.description) && isString(v.scope) && RISKS.includes(v.risk) && "inputSchema" in v;
|
|
28
|
-
var isPending = (v) => isObject(v) && isString(v.id) && isString(v.tool) && RISKS.includes(v.risk) && isString(v.digest) && isObject(v.input) && isObject(v.proposedBy) && isString(v.requestedBy) && typeof v.canDecide === "boolean";
|
|
29
|
-
var SHAPES = {
|
|
30
|
-
health: (v) => isObject(v) && typeof v.ok === "boolean" && isString(v.pack) && isString(v.version),
|
|
31
|
-
tools: (v) => isObject(v) && Array.isArray(v.tools) && v.tools.every(isTool),
|
|
32
|
-
approvals: (v) => isObject(v) && Array.isArray(v.approvals) && v.approvals.every(isPending),
|
|
33
|
-
decision: (v) => isObject(v) && isString(v.requestId) && typeof v.approved === "boolean" && isString(v.decidedBy) && typeof v.machineMade === "boolean"
|
|
34
|
-
};
|
|
35
|
-
var MAX_EVENT_CHARS = 4194304;
|
|
36
|
-
var EVENT_TYPES = ["run_start", "turn_start", "text_delta", "tool_call", "approval_request", "approval_decision", "tool_result", "turn_end", "done", "error"];
|
|
37
|
-
var isEvent = (v) => {
|
|
38
|
-
if (!isObject(v) || typeof v.type !== "string" || !EVENT_TYPES.includes(v.type)) return false;
|
|
39
|
-
switch (v.type) {
|
|
40
|
-
case "done":
|
|
41
|
-
return isString(v.reason) && isObject(v.totals);
|
|
42
|
-
case "error":
|
|
43
|
-
return isString(v.error);
|
|
44
|
-
case "text_delta":
|
|
45
|
-
return isString(v.text);
|
|
46
|
-
case "tool_result":
|
|
47
|
-
return isString(v.id) && isString(v.name) && isString(v.content) && typeof v.isError === "boolean";
|
|
48
|
-
case "tool_call":
|
|
49
|
-
return isString(v.id) && isString(v.name);
|
|
50
|
-
case "approval_request":
|
|
51
|
-
return isObject(v.request) && isString(v.request.id) && isString(v.request.digest);
|
|
52
|
-
case "approval_decision":
|
|
53
|
-
return isString(v.requestId) && typeof v.approved === "boolean";
|
|
54
|
-
default:
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
function createClient(options) {
|
|
59
|
-
if (typeof options.baseUrl !== "string" || options.baseUrl === "") throw new TypeError("createClient needs a baseUrl");
|
|
60
|
-
if (typeof options.token !== "string" || options.token === "") throw new TypeError("createClient needs a token");
|
|
61
|
-
let origin;
|
|
62
|
-
try {
|
|
63
|
-
origin = new URL(options.baseUrl);
|
|
64
|
-
} catch {
|
|
65
|
-
throw new TypeError("baseUrl must be an absolute URL");
|
|
66
|
-
}
|
|
67
|
-
const local = ["localhost", "127.0.0.1", "[::1]"].includes(origin.hostname);
|
|
68
|
-
if (origin.protocol !== "https:" && !(origin.protocol === "http:" && local)) {
|
|
69
|
-
throw new TypeError("baseUrl must be an https URL, or an http URL on localhost, 127.0.0.1, or [::1]");
|
|
70
|
-
}
|
|
71
|
-
if (origin.search !== "" || origin.hash !== "" || origin.username !== "" || origin.password !== "") {
|
|
72
|
-
throw new TypeError("baseUrl must have no query, hash, or credentials");
|
|
73
|
-
}
|
|
74
|
-
const prefix = origin.pathname.replace(/\/+$/, "");
|
|
75
|
-
const root = `${origin.origin}${prefix}/api/v1`;
|
|
76
|
-
const token = options.token;
|
|
77
|
-
const send = (input, init) => {
|
|
78
|
-
const run = options.fetch ?? globalThis.fetch;
|
|
79
|
-
return run(input, init);
|
|
80
|
-
};
|
|
81
|
-
const seg = (value) => {
|
|
82
|
-
if (typeof value !== "string" || /^(?:\.|%2e){0,2}$/i.test(value)) throw new TypeError("an id must be a non-empty string that is not . or ..");
|
|
83
|
-
return encodeURIComponent(value);
|
|
84
|
-
};
|
|
85
|
-
async function request(url, method, body, callOptions, authed = true) {
|
|
86
|
-
const headers = authed ? { authorization: `Bearer ${token}` } : {};
|
|
87
|
-
if (body !== void 0) headers["content-type"] = "application/json";
|
|
88
|
-
return send(url, {
|
|
89
|
-
method,
|
|
90
|
-
headers,
|
|
91
|
-
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
92
|
-
signal: callOptions?.signal,
|
|
93
|
-
// Never follow a redirect: it would send the token to another host.
|
|
94
|
-
redirect: "manual"
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
async function failure(response) {
|
|
98
|
-
let parsed;
|
|
99
|
-
try {
|
|
100
|
-
parsed = JSON.parse(await response.text());
|
|
101
|
-
} catch {
|
|
102
|
-
parsed = void 0;
|
|
103
|
-
}
|
|
104
|
-
const error = isObject(parsed) ? parsed.error : void 0;
|
|
105
|
-
const message = isObject(parsed) && typeof parsed.message === "string" ? parsed.message : typeof error === "string" ? error : `HTTP ${response.status}`;
|
|
106
|
-
let code = STATUS_CODES[response.status] ?? (response.status >= 500 ? "INTERNAL" : "UNKNOWN");
|
|
107
|
-
if (response.status === 503 && error === "not_configured") code = "NOT_CONFIGURED";
|
|
108
|
-
if (typeof error !== "string") code = response.status >= 500 ? "INTERNAL" : "UNKNOWN";
|
|
109
|
-
const after = response.headers.get("retry-after")?.trim() ?? "";
|
|
110
|
-
return new AssistantApiError(code, response.status, message, /^\d{1,9}$/.test(after) ? Number(after) : void 0);
|
|
111
|
-
}
|
|
112
|
-
async function call(url, method, body, callOptions, shape, authed = true) {
|
|
113
|
-
const response = await request(url, method, body, callOptions, authed);
|
|
114
|
-
if (!response.ok) throw await failure(response);
|
|
115
|
-
let parsed;
|
|
116
|
-
try {
|
|
117
|
-
parsed = JSON.parse(await response.text());
|
|
118
|
-
} catch {
|
|
119
|
-
parsed = void 0;
|
|
120
|
-
}
|
|
121
|
-
if (!SHAPES[shape](parsed)) throw new AssistantApiError("UNKNOWN", response.status, `the reply is not a ${shape}`);
|
|
122
|
-
return parsed;
|
|
123
|
-
}
|
|
124
|
-
async function* chat(message, callOptions) {
|
|
125
|
-
const response = await request(`${root}/chat`, "POST", { message }, callOptions);
|
|
126
|
-
if (!response.ok) throw await failure(response);
|
|
127
|
-
if (!(response.headers.get("content-type") ?? "").startsWith("text/event-stream") || response.body === null) {
|
|
128
|
-
await response.body?.cancel();
|
|
129
|
-
throw new AssistantApiError("UNKNOWN", response.status, "the reply is not an event stream");
|
|
130
|
-
}
|
|
131
|
-
const reader = response.body.getReader();
|
|
132
|
-
const decoder = new TextDecoder();
|
|
133
|
-
let buffer = "";
|
|
134
|
-
let ended = false;
|
|
135
|
-
function* drain(final) {
|
|
136
|
-
const text = final ? buffer : buffer.replace(/\r$/, "");
|
|
137
|
-
const normal = text.replace(/\r\n?/g, "\n");
|
|
138
|
-
const rest = buffer.slice(text.length);
|
|
139
|
-
let from = 0;
|
|
140
|
-
for (let end = normal.indexOf("\n\n", from); end >= 0; end = normal.indexOf("\n\n", from)) {
|
|
141
|
-
const event = parseBlock(normal.slice(from, end), response.status);
|
|
142
|
-
from = end + 2;
|
|
143
|
-
if (event === void 0) continue;
|
|
144
|
-
if (ended) throw new AssistantApiError("UNKNOWN", response.status, "an event came after the end of the stream");
|
|
145
|
-
ended = event.type === "done" || event.type === "error";
|
|
146
|
-
yield event;
|
|
147
|
-
}
|
|
148
|
-
buffer = normal.slice(from) + rest;
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
for (; ; ) {
|
|
152
|
-
const chunk = await reader.read();
|
|
153
|
-
if (chunk.done) break;
|
|
154
|
-
buffer += decoder.decode(chunk.value, { stream: true });
|
|
155
|
-
yield* drain(false);
|
|
156
|
-
if (buffer.length > MAX_EVENT_CHARS) throw new AssistantApiError("UNKNOWN", response.status, "an event is too long");
|
|
157
|
-
}
|
|
158
|
-
buffer += decoder.decode();
|
|
159
|
-
yield* drain(true);
|
|
160
|
-
if (!ended || buffer.trim() !== "") throw new AssistantApiError("UNKNOWN", response.status, "the stream ended before a done event");
|
|
161
|
-
} finally {
|
|
162
|
-
await reader.cancel().catch(() => void 0);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function parseBlock(block, status) {
|
|
166
|
-
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).replace(/^ /, "")).join("\n");
|
|
167
|
-
if (data === "") return void 0;
|
|
168
|
-
let parsed;
|
|
169
|
-
try {
|
|
170
|
-
parsed = JSON.parse(data);
|
|
171
|
-
} catch {
|
|
172
|
-
parsed = void 0;
|
|
173
|
-
}
|
|
174
|
-
if (!isEvent(parsed)) throw new AssistantApiError("UNKNOWN", status, "an event is not valid");
|
|
175
|
-
return parsed;
|
|
176
|
-
}
|
|
177
|
-
const decided = (id, decision) => {
|
|
178
|
-
if (decision.requestId !== id) throw new AssistantApiError("UNKNOWN", 200, "the reply is for another approval");
|
|
179
|
-
return decision;
|
|
180
|
-
};
|
|
181
|
-
const methods = {
|
|
182
|
-
// /health is outside /api/v1 and needs no token, so the client sends none.
|
|
183
|
-
health: async (o) => call(`${origin.origin}${prefix}/health`, "GET", void 0, o, "health", false),
|
|
184
|
-
tools: async (o) => (await call(`${root}/tools`, "GET", void 0, o, "tools")).tools,
|
|
185
|
-
chat,
|
|
186
|
-
approvals: async (o) => (await call(`${root}/approvals`, "GET", void 0, o, "approvals")).approvals,
|
|
187
|
-
approve: async (id, digest, o) => decided(id, await call(`${root}/approvals/${seg(id)}/approve`, "POST", { digest }, o, "decision")),
|
|
188
|
-
deny: async (id, reason, o) => decided(id, await call(`${root}/approvals/${seg(id)}/deny`, "POST", reason === void 0 ? {} : { reason }, o, "decision"))
|
|
189
|
-
};
|
|
190
|
-
return methods;
|
|
191
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AssistantApiError,
|
|
3
|
+
createClient
|
|
4
|
+
} from "./chunk-QTIEDY3V.js";
|
|
192
5
|
export {
|
|
193
6
|
AssistantApiError,
|
|
194
7
|
createClient
|
package/dist/demo.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createStaticTokenAuth,
|
|
3
3
|
createWorker
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TOQIHYDH.js";
|
|
5
5
|
import {
|
|
6
6
|
AnthropicProvider
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2RRM6OLC.js";
|
|
8
8
|
import {
|
|
9
9
|
ToolRegistry,
|
|
10
|
-
VERSION
|
|
11
|
-
|
|
10
|
+
VERSION,
|
|
11
|
+
hasScope
|
|
12
|
+
} from "./chunk-JNZPVHNN.js";
|
|
12
13
|
|
|
13
14
|
// src/demo-page.ts
|
|
14
15
|
var DEMO_CSP = "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
|
|
@@ -19,13 +20,13 @@ var DEMO_PAGE = `<!doctype html>
|
|
|
19
20
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
20
21
|
<title>agentic-assistant demo</title>
|
|
21
22
|
<style>
|
|
22
|
-
body{font:16px/1.5 system-ui,sans-serif;max-width:42rem;margin:2rem auto;padding:0 1rem;color
|
|
23
|
+
body{font:16px/1.5 system-ui,sans-serif;max-width:42rem;margin:2rem auto;padding:0 1rem;color:var(--ad-color-text,CanvasText);background:var(--ad-color-surface,Canvas)}
|
|
23
24
|
label{display:block;margin-top:1rem;font-weight:600}
|
|
24
25
|
input,textarea,button{font:inherit;padding:.5rem;width:100%;box-sizing:border-box}
|
|
25
26
|
button{width:auto;min-height:2.5rem;margin-top:1rem;cursor:pointer}
|
|
26
|
-
:focus-visible{outline:3px solid
|
|
27
|
-
#out{white-space:pre-wrap;border:1px solid
|
|
28
|
-
.badge{display:inline-block;border:1px solid
|
|
27
|
+
:focus-visible{outline:3px solid var(--ad-color-focus,LinkText);outline-offset:2px}
|
|
28
|
+
#out{white-space:pre-wrap;border:1px solid var(--ad-color-border-strong,CanvasText);padding:1rem;margin-top:1rem;min-height:4rem}
|
|
29
|
+
.badge{display:inline-block;border:1px solid var(--ad-color-text,CanvasText);padding:0 .5rem;font-size:.875rem}
|
|
29
30
|
</style>
|
|
30
31
|
</head>
|
|
31
32
|
<body>
|
|
@@ -103,6 +104,7 @@ var demoRegistry = () => new ToolRegistry().register({
|
|
|
103
104
|
risk: "safe",
|
|
104
105
|
handler: () => ({ now: (/* @__PURE__ */ new Date()).toISOString() })
|
|
105
106
|
});
|
|
107
|
+
var DEMO_SCOPE = "demo:read";
|
|
106
108
|
var DEMO_SYSTEM = "You are a demo assistant. You may use the demo_time tool. Answer in short, plain text.";
|
|
107
109
|
function modelOptions(env) {
|
|
108
110
|
if (!env.MODEL_KEY) return void 0;
|
|
@@ -117,6 +119,8 @@ async function buildDemo(env) {
|
|
|
117
119
|
const auth = await createStaticTokenAuth(tokens);
|
|
118
120
|
const worker = createWorker({
|
|
119
121
|
auth,
|
|
122
|
+
// A token without `demo:read` signs in but may use no route.
|
|
123
|
+
routeScopes: { tools: DEMO_SCOPE, chat: DEMO_SCOPE, approvals: DEMO_SCOPE },
|
|
120
124
|
registry: demoRegistry(),
|
|
121
125
|
system: DEMO_SYSTEM,
|
|
122
126
|
limits: { maxTurns: 4, maxTotalTokens: 2e4, maxOutputTokensPerTurn: 1024 },
|
|
@@ -148,7 +152,8 @@ var demo_default = {
|
|
|
148
152
|
return notConfigured("this demo is not set up");
|
|
149
153
|
}
|
|
150
154
|
if (request.method === "POST" && url.pathname.replace(/\/$/, "") === "/api/v1/chat" && modelOptions(env) === void 0) {
|
|
151
|
-
|
|
155
|
+
const who = await demo.auth.authenticate(request.clone());
|
|
156
|
+
if (who !== null && hasScope(who.scopes, DEMO_SCOPE)) return notConfigured("this demo has no model key or model endpoint set");
|
|
152
157
|
}
|
|
153
158
|
return demo.worker.fetch(request, env);
|
|
154
159
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export {
|
|
2
|
-
import { S as Setup } from './chat-
|
|
3
|
-
export { C as ChatConfig, a as ChatIo, b as ConfigError, c as ConnectMcpOptions, M as McpToolInfo, d as McpTools, e as McpTransportConfig, f as buildSetup, g as connectMcpTools, p as parseTarget, r as runChat } from './chat-
|
|
1
|
+
export { A as ACTIONS, a as Action, b as Actor, c as ApprovalDecision, d as ApprovalGate, e as ApprovalRequest, f as Approver, g as ApproverKind, h as AssistantEvent, i as AssistantPrincipal, D as DATA_NOTICE, j as DeniedReason, k as DoneReason, P as PrincipalError, l as PrincipalInput, m as Provenance, n as ProvenanceInput, Q as QueueApprovalGate, R as RunLimits, o as RunOptions, p as RunTotals, S as Scope, T as TOOL_RISKS, q as TRAINED_ALGORITHMIC_MEDIA, r as ToolContext, s as ToolDefinition, t as ToolRegistry, u as ToolRisk, v as actorOf, w as assertPrincipal, x as canonicalJson, y as createPrincipal, z as createProvenance, B as defineTool, C as digestCall, E as hasScope, F as parseScope, G as provenanceLabel, H as provenanceMarker, I as runAssistant, J as scopeCovers } from './loop-B6aowbyz.js';
|
|
2
|
+
import { S as Setup } from './chat-CGITyrxg.js';
|
|
3
|
+
export { C as ChatConfig, a as ChatIo, b as ConfigError, c as ConnectMcpOptions, M as McpToolInfo, d as McpTools, e as McpTransportConfig, f as buildSetup, g as connectMcpTools, p as parseTarget, r as runChat } from './chat-CGITyrxg.js';
|
|
4
4
|
import Anthropic from '@anthropic-ai/sdk';
|
|
5
5
|
import { M as ModelProvider, P as Pricing, a as ModelRequest, b as ModelEvent } from './types-DGH8FS56.js';
|
|
6
6
|
export { A as AnsweredBy, C as ContentBlock, J as JsonSchema, c as Message, d as ProviderError, S as StopReason, T as ToolSpec, U as Usage } from './types-DGH8FS56.js';
|
|
7
7
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
+
import { AssistantClient } from './client.js';
|
|
8
9
|
|
|
9
10
|
declare const VERSION: string;
|
|
10
11
|
|
|
@@ -81,9 +82,16 @@ declare class FallbackProvider implements ModelProvider {
|
|
|
81
82
|
stream(request: ModelRequest): AsyncGenerator<ModelEvent>;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
85
|
+
/**
|
|
86
|
+
* An MCP server with the tool `assistant_chat`. Each call starts a new conversation. With a `remote` client it
|
|
87
|
+
* also serves `assistant_tools`, `assistant_approvals`, and `assistant_deny`. `assistant_approve` needs `allow.approve`.
|
|
88
|
+
*/
|
|
89
|
+
declare function createAssistantMcpServer(setup: Setup, shutdown?: AbortSignal, remote?: AssistantClient, allow?: {
|
|
90
|
+
approve?: boolean;
|
|
91
|
+
}): Server;
|
|
86
92
|
/** Serves on stdio. Resolves when the client closes its input, or the transport closes. */
|
|
87
|
-
declare function startAssistantMcp(setup: Setup
|
|
93
|
+
declare function startAssistantMcp(setup: Setup, remote?: AssistantClient, allow?: {
|
|
94
|
+
approve?: boolean;
|
|
95
|
+
}): Promise<void>;
|
|
88
96
|
|
|
89
97
|
export { AnthropicProvider, type AnthropicProviderOptions, DEFAULT_ANTHROPIC_MODEL, FallbackProvider, ModelEvent, ModelProvider, ModelRequest, type OpenAICompatibleOptions, OpenAICompatibleProvider, PRICING_CHECKED_ON, Pricing, Setup, VERSION, createAssistantMcpServer, startAssistantMcp };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createAssistantMcpServer,
|
|
3
3
|
startAssistantMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-E7TFMM2Z.js";
|
|
5
5
|
import {
|
|
6
6
|
ConfigError,
|
|
7
7
|
FallbackProvider,
|
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
connectMcpTools,
|
|
11
11
|
parseTarget,
|
|
12
12
|
runChat
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-5BZENFYL.js";
|
|
14
14
|
import "./chunk-HYT732TN.js";
|
|
15
15
|
import {
|
|
16
16
|
AnthropicProvider,
|
|
17
17
|
DEFAULT_ANTHROPIC_MODEL,
|
|
18
18
|
PRICING_CHECKED_ON
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2RRM6OLC.js";
|
|
20
20
|
import {
|
|
21
21
|
ACTIONS,
|
|
22
22
|
DATA_NOTICE,
|
|
@@ -40,7 +40,8 @@ import {
|
|
|
40
40
|
provenanceMarker,
|
|
41
41
|
runAssistant,
|
|
42
42
|
scopeCovers
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-JNZPVHNN.js";
|
|
44
|
+
import "./chunk-QTIEDY3V.js";
|
|
44
45
|
export {
|
|
45
46
|
ACTIONS,
|
|
46
47
|
AnthropicProvider,
|
|
@@ -287,4 +287,4 @@ declare const DATA_NOTICE: string;
|
|
|
287
287
|
*/
|
|
288
288
|
declare function runAssistant(options: RunOptions): AsyncGenerator<AssistantEvent>;
|
|
289
289
|
|
|
290
|
-
export {
|
|
290
|
+
export { ACTIONS as A, defineTool as B, digestCall as C, DATA_NOTICE as D, hasScope as E, parseScope as F, provenanceLabel as G, provenanceMarker as H, runAssistant as I, scopeCovers as J, PrincipalError as P, QueueApprovalGate as Q, type RunLimits as R, type Scope as S, TOOL_RISKS as T, type Action as a, type Actor as b, type ApprovalDecision as c, type ApprovalGate as d, type ApprovalRequest as e, type Approver as f, type ApproverKind as g, type AssistantEvent as h, type AssistantPrincipal as i, type DeniedReason as j, type DoneReason as k, type PrincipalInput as l, type Provenance as m, type ProvenanceInput as n, type RunOptions as o, type RunTotals as p, TRAINED_ALGORITHMIC_MEDIA as q, type ToolContext as r, type ToolDefinition as s, ToolRegistry as t, type ToolRisk as u, actorOf as v, assertPrincipal as w, canonicalJson as x, createPrincipal as y, createProvenance as z };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAssistantMcpServer,
|
|
3
|
+
startAssistantMcp
|
|
4
|
+
} from "./chunk-E7TFMM2Z.js";
|
|
5
|
+
import "./chunk-5BZENFYL.js";
|
|
6
|
+
import "./chunk-HYT732TN.js";
|
|
7
|
+
import "./chunk-2RRM6OLC.js";
|
|
8
|
+
import "./chunk-JNZPVHNN.js";
|
|
9
|
+
import "./chunk-QTIEDY3V.js";
|
|
10
|
+
export {
|
|
11
|
+
createAssistantMcpServer,
|
|
12
|
+
startAssistantMcp
|
|
13
|
+
};
|