agentic-assistant 0.3.0 → 0.4.0

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ Added
6
+
7
+ - `origins` option on `createAssistantApi` and `createWorker`: origins that may send a write signed in with a session cookie.
8
+
9
+ Changed
10
+
11
+ - The HTTP API reads the credential with `requirePrincipal` from `agentic-identity/http` and checks the kind with `PRINCIPAL_KINDS`. The `agentic-identity` dependency is now `^0.6.0`.
12
+ - A bearer token must hold only `A-Za-z0-9_-` and at most 512 characters, or the call is a 401. `createStaticTokenAuth` refuses other tokens when it is built.
13
+ - An Auth that throws gives a 503, not a 401. A cookie write from an origin not in `origins` gives a 403.
14
+
3
15
  ## 0.3.0
4
16
 
5
17
  Added
package/README.md CHANGED
@@ -174,9 +174,9 @@ export default {
174
174
  };
175
175
  ```
176
176
 
177
- To deploy the demo Worker yourself, set the secrets `ASSISTANT_TOKENS` (a JSON map of a bearer token of 32 bytes or more to `{"id":"ana","scopes":["demo:read"]}`) and `MODEL_KEY`, set the var `MODEL_URL` (or `MODEL_USE_DEFAULT_ENDPOINT=1` to call the US-hosted `api.anthropic.com`), then run `npm run deploy:demo`. Without `MODEL_KEY`, chat answers 503. `agentic-assistant/worker` also exports `createStaticTokenAuth`, an `agentic-identity` `Auth` that reads bearer tokens from a fixed map.
177
+ To deploy the demo Worker yourself, set the secrets `ASSISTANT_TOKENS` (a JSON map of a bearer token of 32 to 512 characters from `A-Za-z0-9_-` to `{"id":"ana","scopes":["demo:read"]}`) and `MODEL_KEY`, set the var `MODEL_URL` (or `MODEL_USE_DEFAULT_ENDPOINT=1` to call the US-hosted `api.anthropic.com`), then run `npm run deploy:demo`. Without `MODEL_KEY`, chat answers 503. `agentic-assistant/worker` also exports `createStaticTokenAuth`, an `agentic-identity` `Auth` that reads bearer tokens from a fixed map.
178
178
 
179
- Every route is under `/api/v1` and needs `Authorization: Bearer <key>`. The key resolves to an `agentic-identity` principal. A person, an agent, and a token that acts for a user all work. A missing, bad, or odd credential is a 401. Every chat response has the header `x-ai-generated: true`.
179
+ Every route is under `/api/v1` and needs `Authorization: Bearer <key>`. The key resolves to an `agentic-identity` principal. A person, an agent, and a token that acts for a user all work. A missing, bad, or odd credential is a 401. An Auth that fails gives a 503. A write signed in with a session cookie needs an `Origin` listed in the `origins` option, or it is a 403. Every chat response has the header `x-ai-generated: true`.
180
180
 
181
181
  | Route | What it does |
182
182
  |---|---|
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import { createRequire } from "module";
3
- var VERSION = true ? "0.3.0" : readPackageVersion();
3
+ var VERSION = true ? "0.4.0" : readPackageVersion();
4
4
 
5
5
  // src/approvals.ts
6
6
  import { createHash, randomUUID } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ProviderError
3
- } from "./chunk-3LKTJJ75.js";
3
+ } from "./chunk-C44QT474.js";
4
4
 
5
5
  // src/providers/anthropic.ts
6
6
  import Anthropic from "@anthropic-ai/sdk";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  runChat
3
- } from "./chunk-3X6KU636.js";
3
+ } from "./chunk-MESH6X6N.js";
4
4
  import {
5
5
  VERSION,
6
6
  provenanceLabel,
7
7
  provenanceMarker
8
- } from "./chunk-3LKTJJ75.js";
8
+ } from "./chunk-C44QT474.js";
9
9
 
10
10
  // src/mcp-server.ts
11
11
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -5,7 +5,7 @@ import {
5
5
  AnthropicProvider,
6
6
  checkBaseUrl,
7
7
  isLoopback
8
- } from "./chunk-DXTIEMOT.js";
8
+ } from "./chunk-FQD4OK2R.js";
9
9
  import {
10
10
  PrincipalError,
11
11
  ProviderError,
@@ -17,7 +17,7 @@ import {
17
17
  digestCall,
18
18
  printable,
19
19
  runAssistant
20
- } from "./chunk-3LKTJJ75.js";
20
+ } from "./chunk-C44QT474.js";
21
21
 
22
22
  // src/sources/mcp.ts
23
23
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -4,22 +4,29 @@ import {
4
4
  hasScope,
5
5
  parseScope,
6
6
  runAssistant
7
- } from "./chunk-3LKTJJ75.js";
7
+ } from "./chunk-C44QT474.js";
8
8
 
9
9
  // src/api.ts
10
+ import { PRINCIPAL_KINDS } from "agentic-identity";
11
+ import { requirePrincipal } from "agentic-identity/http";
10
12
  var MAX_BODY_BYTES = 65536;
11
13
  var MAX_MESSAGE_CHARS = 1e4;
12
14
  var MAX_REASON_CHARS = 500;
13
15
  var DEFAULT_MAX_CONCURRENT_RUNS = 4;
14
- var KINDS = ["human", "agent_of_human", "agent_of_agent", "agent", "user_token"];
15
16
  var APPROVER_SCOPE = "assistant:admin";
17
+ async function callerOf(request, auth, origins) {
18
+ const found = await requirePrincipal(request, auth, { origins });
19
+ if (found instanceof Response) return found.status === 401 ? fail(401, "sign in with a valid key", { "www-authenticate": "Bearer" }) : found;
20
+ return toCaller(found) ?? fail(401, "sign in with a valid key", { "www-authenticate": "Bearer" });
21
+ }
16
22
  var json = (body, status = 200, headers = {}) => Response.json(body, { status, headers: { "cache-control": "no-store", ...headers } });
17
23
  var fail = (status, error, headers = {}) => json({ error }, status, headers);
18
24
  function toCaller(context) {
19
25
  if (typeof context !== "object" || context === null) return void 0;
20
26
  const { principalId, kind, chain, scopes } = context;
21
- if (typeof principalId !== "string" || principalId === "" || !KINDS.includes(kind)) return void 0;
22
- if (!Array.isArray(chain) || chain.length === 0 || chain[chain.length - 1]?.id !== principalId) return void 0;
27
+ if (typeof principalId !== "string" || principalId === "" || !Array.isArray(chain) || chain.length === 0) return void 0;
28
+ if (!PRINCIPAL_KINDS.includes(kind)) return void 0;
29
+ if (chain[chain.length - 1]?.id !== principalId) return void 0;
23
30
  if (chain[chain.length - 1]?.kind !== kind || kind === "human" && chain.length !== 1) return void 0;
24
31
  const ids = chain.map((step) => step?.id);
25
32
  if (ids.some((id) => typeof id !== "string" || id === "")) return void 0;
@@ -132,8 +139,8 @@ function createAssistantApi(options) {
132
139
  scopes: caller.scopes,
133
140
  // Each check asks the Auth again. A key revoked during a run, or one with other scopes, stops it.
134
141
  isRevoked: async () => {
135
- const now = toCaller(await options.auth.authenticate(request));
136
- return now?.id !== caller.id || [...now.scopes].sort().join(" ") !== [...caller.scopes].sort().join(" ");
142
+ const now = await callerOf(request, options.auth, options.origins);
143
+ return now instanceof Response || now.id !== caller.id || [...now.scopes].sort().join(" ") !== [...caller.scopes].sort().join(" ");
137
144
  }
138
145
  }),
139
146
  approvals: gateFor(caller),
@@ -198,13 +205,8 @@ function createAssistantApi(options) {
198
205
  const allowed = route === "chat" || verb !== void 0 ? "POST" : "GET";
199
206
  if (request.method !== allowed) return fail(405, "method not allowed", { allow: allowed });
200
207
  if (route !== "approvals" && id !== void 0) return fail(404, "not found");
201
- let caller;
202
- try {
203
- caller = toCaller(await options.auth.authenticate(request));
204
- } catch {
205
- caller = void 0;
206
- }
207
- if (caller === void 0) return fail(401, "sign in with a valid key", { "www-authenticate": "Bearer" });
208
+ const caller = await callerOf(request, options.auth, options.origins);
209
+ if (caller instanceof Response) return caller;
208
210
  if (route === "tools") {
209
211
  const tools = options.registry.list().filter((tool) => hasScope(caller.scopes, tool.scope)).map(({ name, description, scope, risk, inputSchema }) => ({ name, description, scope, risk, inputSchema }));
210
212
  return json({ tools });
@@ -257,6 +259,7 @@ async function createStaticTokenAuth(tokens) {
257
259
  for (const [token, entry] of Object.entries(tokens)) {
258
260
  if (encoder.encode(token).length < MIN_TOKEN_BYTES) throw new Error(`tokens must hold at least ${MIN_TOKEN_BYTES} bytes`);
259
261
  if (/\s/.test(token)) throw new Error("a token may not hold white space");
262
+ if (!/^[A-Za-z0-9_-]{1,512}$/.test(token)) throw new Error("a token may hold only A-Z a-z 0-9 _ - and at most 512 characters");
260
263
  if (typeof entry !== "object" || entry === null || Object.keys(entry).some((key) => key !== "id" && key !== "scopes")) {
261
264
  throw new Error("a token entry has only an id and scopes: every caller is a person");
262
265
  }
package/dist/cli.js CHANGED
@@ -4,15 +4,15 @@ import {
4
4
  buildSetup,
5
5
  readTextFile,
6
6
  runChat
7
- } from "./chunk-3X6KU636.js";
7
+ } from "./chunk-MESH6X6N.js";
8
8
  import "./chunk-HYT732TN.js";
9
- import "./chunk-DXTIEMOT.js";
9
+ import "./chunk-FQD4OK2R.js";
10
10
  import {
11
11
  TOOL_RISKS,
12
12
  VERSION,
13
13
  printable,
14
14
  provenanceLabel
15
- } from "./chunk-3LKTJJ75.js";
15
+ } from "./chunk-C44QT474.js";
16
16
 
17
17
  // src/cli.ts
18
18
  import { realpathSync } from "fs";
@@ -272,7 +272,7 @@ async function runCli(argv, io = defaultIo()) {
272
272
  for (const skip of setup.skipped.slice(0, MAX_SKIPPED_LINES)) io.err(`skipped tool ${oneLine(skip.name, 100)}: ${oneLine(skip.reason, 200)}`);
273
273
  if (setup.skipped.length > MAX_SKIPPED_LINES) io.err(`skipped ${setup.skipped.length - MAX_SKIPPED_LINES} more tools`);
274
274
  if (parsed.command === "mcp") {
275
- const { startAssistantMcp } = await import("./mcp-server-ZPCPYMCT.js");
275
+ const { startAssistantMcp } = await import("./mcp-server-LXGWKEHG.js");
276
276
  await startAssistantMcp(setup);
277
277
  return 0;
278
278
  }
package/dist/demo.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  createStaticTokenAuth,
3
3
  createWorker
4
- } from "./chunk-GLU222OQ.js";
4
+ } from "./chunk-YMEM57LX.js";
5
5
  import {
6
6
  AnthropicProvider
7
- } from "./chunk-DXTIEMOT.js";
7
+ } from "./chunk-FQD4OK2R.js";
8
8
  import {
9
9
  ToolRegistry,
10
10
  VERSION
11
- } from "./chunk-3LKTJJ75.js";
11
+ } from "./chunk-C44QT474.js";
12
12
 
13
13
  // src/demo-page.ts
14
14
  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'";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createAssistantMcpServer,
3
3
  startAssistantMcp
4
- } from "./chunk-T5WVSGYQ.js";
4
+ } from "./chunk-KNUEYFX2.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-3X6KU636.js";
13
+ } from "./chunk-MESH6X6N.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-DXTIEMOT.js";
19
+ } from "./chunk-FQD4OK2R.js";
20
20
  import {
21
21
  ACTIONS,
22
22
  DATA_NOTICE,
@@ -40,7 +40,7 @@ import {
40
40
  provenanceMarker,
41
41
  runAssistant,
42
42
  scopeCovers
43
- } from "./chunk-3LKTJJ75.js";
43
+ } from "./chunk-C44QT474.js";
44
44
  export {
45
45
  ACTIONS,
46
46
  AnthropicProvider,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createAssistantMcpServer,
3
3
  startAssistantMcp
4
- } from "./chunk-T5WVSGYQ.js";
5
- import "./chunk-3X6KU636.js";
4
+ } from "./chunk-KNUEYFX2.js";
5
+ import "./chunk-MESH6X6N.js";
6
6
  import "./chunk-HYT732TN.js";
7
- import "./chunk-DXTIEMOT.js";
8
- import "./chunk-3LKTJJ75.js";
7
+ import "./chunk-FQD4OK2R.js";
8
+ import "./chunk-C44QT474.js";
9
9
  export {
10
10
  createAssistantMcpServer,
11
11
  startAssistantMcp
package/dist/worker.d.ts CHANGED
@@ -11,6 +11,8 @@ interface AssistantApiOptions {
11
11
  registry: ToolRegistry;
12
12
  system?: string;
13
13
  limits?: RunLimits;
14
+ /** Origins (such as `https://app.example.org`) that may send a write signed in with a session cookie. A write with a cookie from any other origin is a 403. A Bearer key needs no origin. */
15
+ origins?: readonly string[];
14
16
  /** Most chat turns one principal may run at once. Default 4. */
15
17
  maxConcurrentRuns?: number;
16
18
  }
package/dist/worker.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  createAssistantApi,
4
4
  createStaticTokenAuth,
5
5
  createWorker
6
- } from "./chunk-GLU222OQ.js";
7
- import "./chunk-3LKTJJ75.js";
6
+ } from "./chunk-YMEM57LX.js";
7
+ import "./chunk-C44QT474.js";
8
8
  export {
9
9
  APPROVER_SCOPE,
10
10
  createAssistantApi,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-assistant",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "An AI assistant that acts inside your apps with the user's own permissions.",
5
5
  "keywords": [
6
6
  "assistant",
@@ -74,7 +74,7 @@
74
74
  "dependencies": {
75
75
  "@anthropic-ai/sdk": "^1.3.0",
76
76
  "@modelcontextprotocol/sdk": "^1.32.2",
77
- "agentic-identity": "^0.4.0",
77
+ "agentic-identity": "^0.6.0",
78
78
  "ajv": "^8.20.0"
79
79
  },
80
80
  "devDependencies": {