agentic-assistant 0.1.0 → 0.3.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.
@@ -1,4 +1,4 @@
1
- import { J as JsonSchema, U as Usage, c as Message, M as ModelProvider } from './types-C47SyHH5.js';
1
+ import { J as JsonSchema, U as Usage, A as AnsweredBy, c as Message, M as ModelProvider } from './types-DGH8FS56.js';
2
2
 
3
3
  declare const ACTIONS: readonly ["read", "comment", "write", "admin"];
4
4
  type Action = (typeof ACTIONS)[number];
@@ -65,11 +65,14 @@ interface ApprovalRequest {
65
65
  digest: string;
66
66
  actor: Actor;
67
67
  }
68
+ /** Who gave an approval. The gate says it. `human` means a signed-in person, `host` means a rule the host set up, `agent` means another agent. */
69
+ type ApproverKind = "human" | "agent" | "host";
68
70
  type ApprovalDecision = {
69
71
  requestId: string;
70
72
  approved: true;
71
73
  digest: string;
72
- decidedBy: string;
74
+ decidedBy: string; /** Leave it out when you do not know. The record then says "unknown". */
75
+ approverKind?: ApproverKind;
73
76
  } | {
74
77
  requestId: string;
75
78
  approved: false;
@@ -100,11 +103,57 @@ declare class QueueApprovalGate implements ApprovalGate {
100
103
  private readonly waiting;
101
104
  pending(): ApprovalRequest[];
102
105
  request(request: ApprovalRequest, signal: AbortSignal): Promise<ApprovalDecision>;
103
- approve(id: string, decidedBy: string): void;
106
+ approve(id: string, decidedBy: string, approverKind?: ApproverKind): void;
104
107
  reject(id: string, decidedBy?: string, reason?: string): void;
105
108
  private take;
106
109
  }
107
110
 
111
+ /** Says that a machine made an output, and which one. Attach it to every result and every edit. */
112
+ interface Provenance {
113
+ generator: "ai";
114
+ /** The model that answered. After a fallback, this is the fallback model. */
115
+ model: string;
116
+ provider: string;
117
+ /** ISO 8601 time, in UTC. */
118
+ createdAt: string;
119
+ /** The id of the assistant principal that acted. */
120
+ actor: string;
121
+ /** The person the assistant acts for. */
122
+ onBehalfOf: string;
123
+ /** The tool that ran. Set on a tool result. */
124
+ tool?: string;
125
+ /** The risk of that tool. A host may mark only `risky` and `irreversible` results as edits. */
126
+ risk?: "safe" | "risky" | "irreversible";
127
+ /** Who approved the action, and what kind of approver it was. Check `kind` before you say a person approved. */
128
+ approvedBy?: Approver[];
129
+ }
130
+ interface Approver {
131
+ id: string;
132
+ /** `unknown` when the approval gate did not say. */
133
+ kind: ApproverKind | "unknown";
134
+ }
135
+ interface ProvenanceInput {
136
+ model: string;
137
+ provider: string;
138
+ /** Epoch milliseconds. */
139
+ createdAt: number;
140
+ actor: string;
141
+ onBehalfOf: string;
142
+ tool?: string;
143
+ risk?: Provenance["risk"];
144
+ approvedBy?: readonly Approver[];
145
+ }
146
+ /** The IPTC digital source type for content made by a model. */
147
+ declare const TRAINED_ALGORITHMIC_MEDIA = "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia";
148
+ declare function createProvenance(input: ProvenanceInput): Provenance;
149
+ /**
150
+ * A JSON-LD marker for a document or a file. Store it next to the content the assistant made.
151
+ * It uses schema.org terms and the IPTC `trainedAlgorithmicMedia` source type.
152
+ */
153
+ declare function provenanceMarker(provenance: Provenance): Record<string, unknown>;
154
+ /** A short text for a person to read, such as "AI-generated by model-1 (acme), approved by ana". */
155
+ declare function provenanceLabel(provenance: Provenance): string;
156
+
108
157
  interface ToolContext {
109
158
  principal: AssistantPrincipal;
110
159
  toolUseId: string;
@@ -126,6 +175,11 @@ declare function defineTool<Input>(tool: ToolDefinition<Input>): ToolDefinition;
126
175
  declare class ToolRegistry {
127
176
  private readonly tools;
128
177
  private readonly ajv;
178
+ /**
179
+ * Ajv builds validators with `new Function`. Workers refuse that inside a request, though not while a module loads.
180
+ * So try Ajv first, and use the small checker when this host refuses code generation. A bad schema still throws.
181
+ */
182
+ private compile;
129
183
  register(tool: ToolDefinition): this;
130
184
  registerAll(tools: readonly ToolDefinition[]): this;
131
185
  get(name: string): ToolDefinition | undefined;
@@ -191,7 +245,8 @@ type AssistantEvent = {
191
245
  } | {
192
246
  type: "approval_decision";
193
247
  requestId: string;
194
- approved: boolean;
248
+ approved: boolean; /** True only when the approver said no. A timeout or a gate failure is not a decline. */
249
+ declined: boolean;
195
250
  decidedBy?: string;
196
251
  reason?: string;
197
252
  } | {
@@ -206,18 +261,22 @@ type AssistantEvent = {
206
261
  /** The raw failure text. It stays in the event and never goes to the model. */
207
262
  detail?: string;
208
263
  actor: Actor;
264
+ /** Marks the result as machine-made. Set when the tool ran. */
265
+ provenance?: Provenance;
209
266
  } | {
210
267
  type: "turn_end";
211
268
  turn: number;
212
269
  usage: Usage;
213
- totals: RunTotals;
270
+ totals: RunTotals; /** The model that answered this turn. */
271
+ answeredBy: AnsweredBy;
214
272
  } | {
215
273
  type: "done";
216
274
  reason: DoneReason;
217
275
  totals: RunTotals;
218
276
  actor: Actor;
219
277
  messages: Message[];
220
- error?: string;
278
+ error?: string; /** Marks the answer as machine-made. Set once a model has answered. */
279
+ provenance?: Provenance;
221
280
  };
222
281
  /** Sent with every request. Tool output is data, never an instruction. */
223
282
  declare const DATA_NOTICE: string;
@@ -228,4 +287,4 @@ declare const DATA_NOTICE: string;
228
287
  */
229
288
  declare function runAssistant(options: RunOptions): AsyncGenerator<AssistantEvent>;
230
289
 
231
- export { ACTIONS as A, DATA_NOTICE as D, PrincipalError as P, QueueApprovalGate as Q, type RunLimits as R, type Scope as S, type ToolRisk as T, type ToolDefinition as a, type Action as b, type Actor as c, type ApprovalDecision as d, type ApprovalGate as e, type ApprovalRequest as f, type AssistantEvent as g, type AssistantPrincipal as h, type DeniedReason as i, type DoneReason as j, type PrincipalInput as k, type RunOptions as l, type RunTotals as m, TOOL_RISKS as n, type ToolContext as o, ToolRegistry as p, actorOf as q, assertPrincipal as r, canonicalJson as s, createPrincipal as t, defineTool as u, digestCall as v, hasScope as w, parseScope as x, runAssistant as y, scopeCovers as z };
290
+ export { type AssistantEvent as A, defineTool as B, digestCall as C, type DeniedReason 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 RunTotals as R, type Scope as S, TOOL_RISKS as T, type ApprovalRequest as a, type Actor as b, type DoneReason as c, ACTIONS as d, type Action as e, type ApprovalDecision as f, type ApprovalGate as g, type Approver as h, type ApproverKind as i, type AssistantPrincipal as j, DATA_NOTICE as k, type PrincipalInput as l, type Provenance as m, type ProvenanceInput as n, type RunLimits as o, type RunOptions 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,12 @@
1
+ import {
2
+ createAssistantMcpServer,
3
+ startAssistantMcp
4
+ } from "./chunk-T5WVSGYQ.js";
5
+ import "./chunk-3X6KU636.js";
6
+ import "./chunk-HYT732TN.js";
7
+ import "./chunk-DXTIEMOT.js";
8
+ import "./chunk-3LKTJJ75.js";
9
+ export {
10
+ createAssistantMcpServer,
11
+ startAssistantMcp
12
+ };
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { g as AssistantEvent, T as ToolRisk } from './loop-CDbbDwSs.js';
3
- import { c as Message } from './types-C47SyHH5.js';
2
+ import { A as AssistantEvent, u as ToolRisk } from './loop-D4qWbRu6.js';
3
+ import { c as Message } from './types-DGH8FS56.js';
4
4
 
5
5
  type PanelItem = {
6
6
  kind: "user";
package/dist/react.js CHANGED
@@ -41,6 +41,7 @@ function useAssistant(options) {
41
41
  const sent = [...history.current, userMessage];
42
42
  let streamingId = null;
43
43
  let finished = false;
44
+ let failed = false;
44
45
  const toolItems = /* @__PURE__ */ new Map();
45
46
  let lastToolItem = null;
46
47
  try {
@@ -81,7 +82,7 @@ function useAssistant(options) {
81
82
  event.requestId,
82
83
  (item) => item.kind === "approval" ? {
83
84
  ...item,
84
- status: event.approved ? "approved" : event.reason === "the person declined" ? "declined" : "unavailable",
85
+ status: event.approved ? "approved" : event.declined ? "declined" : "unavailable",
85
86
  busy: false,
86
87
  decidedBy: event.decidedBy
87
88
  } : item
@@ -109,11 +110,16 @@ function useAssistant(options) {
109
110
  }
110
111
  }
111
112
  } catch {
112
- add({ kind: "notice", id: nextId(), text: RUN_FAILED });
113
+ failed = true;
113
114
  } finally {
114
- if (!finished) history.current = sent;
115
+ if (!finished) {
116
+ history.current = sent;
117
+ if (!failed) failed = true;
118
+ }
119
+ if (failed) add({ kind: "notice", id: nextId(), text: RUN_FAILED });
115
120
  setItems(
116
121
  (current) => current.map((item) => {
122
+ if (item.kind === "assistant" && item.streaming) return { ...item, streaming: false };
117
123
  if (item.kind === "approval" && item.status === "pending") return { ...item, status: "expired", busy: false };
118
124
  if (item.kind === "tool" && (item.status === "running" || item.status === "waiting")) return { ...item, status: "not_run" };
119
125
  return item;
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { M as ModelProvider, P as Pricing, a as ModelRequest, S as StopReason, U as Usage, b as ModelEvent } from './types-C47SyHH5.js';
1
+ import { M as ModelProvider, P as Pricing, a as ModelRequest, S as StopReason, U as Usage, b as ModelEvent } from './types-DGH8FS56.js';
2
2
 
3
3
  interface ScriptedTurn {
4
4
  text?: string;
@@ -19,7 +19,7 @@ declare class ScriptedProvider implements ModelProvider {
19
19
  private readonly script;
20
20
  readonly pricing?: Pricing | undefined;
21
21
  readonly name = "scripted";
22
- readonly model = "scripted-1";
22
+ readonly model: string;
23
23
  readonly requests: ModelRequest[];
24
24
  private next;
25
25
  constructor(script: readonly ScriptedTurn[], pricing?: Pricing | undefined);
package/dist/testing.js CHANGED
@@ -1,35 +1,6 @@
1
- // src/testing.ts
2
- var ScriptedProvider = class {
3
- constructor(script, pricing) {
4
- this.script = script;
5
- this.pricing = pricing;
6
- }
7
- script;
8
- pricing;
9
- name = "scripted";
10
- model = "scripted-1";
11
- requests = [];
12
- next = 0;
13
- async *stream(request) {
14
- this.requests.push({ ...request, messages: structuredClone(request.messages) });
15
- const turn = this.script[this.next++];
16
- if (turn === void 0) throw new Error(`scripted provider ran out of turns at request ${this.next}`);
17
- const content = [];
18
- if (turn.text !== void 0) {
19
- for (const word of turn.text.split(/(?<= )/)) yield { type: "text_delta", text: word };
20
- content.push({ type: "text", text: turn.text });
21
- }
22
- for (const [index, call] of (turn.toolCalls ?? []).entries()) {
23
- content.push({ type: "tool_use", id: call.id ?? `call_${this.next}_${index}`, name: call.name, input: call.input });
24
- }
25
- yield {
26
- type: "message",
27
- content,
28
- stopReason: turn.stopReason ?? (turn.toolCalls?.length ? "tool_use" : "end_turn"),
29
- usage: turn.usage ?? { inputTokens: 10, outputTokens: 5 }
30
- };
31
- }
32
- };
1
+ import {
2
+ ScriptedProvider
3
+ } from "./chunk-HYT732TN.js";
33
4
  export {
34
5
  ScriptedProvider
35
6
  };
@@ -41,6 +41,15 @@ interface ModelRequest {
41
41
  maxOutputTokens: number;
42
42
  signal?: AbortSignal;
43
43
  }
44
+ /** Which model gave an answer. A provider that picks between models sets it on the final message. */
45
+ interface AnsweredBy {
46
+ provider: string;
47
+ model: string;
48
+ /** True when a fallback model answered. */
49
+ fallback: boolean;
50
+ /** Why the first model did not answer. Set only when `fallback` is true. */
51
+ fallbackFor?: "refusal" | "error";
52
+ }
44
53
  type ModelEvent = {
45
54
  type: "text_delta";
46
55
  text: string;
@@ -49,6 +58,7 @@ type ModelEvent = {
49
58
  content: ContentBlock[];
50
59
  stopReason: StopReason;
51
60
  usage: Usage;
61
+ answeredBy?: AnsweredBy;
52
62
  };
53
63
  /** Prices in US dollars per million tokens. */
54
64
  interface Pricing {
@@ -63,10 +73,12 @@ interface ModelProvider {
63
73
  readonly name: string;
64
74
  readonly model: string;
65
75
  readonly pricing?: Pricing;
76
+ /** Most requests one `stream` call may send to a model. Default 1. The loop counts the input of each in its caps. */
77
+ readonly maxAsks?: number;
66
78
  stream(request: ModelRequest): AsyncIterable<ModelEvent>;
67
79
  }
68
80
  declare class ProviderError extends Error {
69
81
  constructor(message: string);
70
82
  }
71
83
 
72
- export { type ContentBlock as C, type JsonSchema as J, type ModelProvider as M, type Pricing as P, type StopReason as S, type ToolSpec as T, type Usage as U, type ModelRequest as a, type ModelEvent as b, type Message as c, ProviderError as d };
84
+ export { type AnsweredBy as A, type ContentBlock as C, type JsonSchema as J, type ModelProvider as M, type Pricing as P, type StopReason as S, type ToolSpec as T, type Usage as U, type ModelRequest as a, type ModelEvent as b, type Message as c, ProviderError as d };
package/dist/worker.d.ts CHANGED
@@ -1,8 +1,49 @@
1
+ import { Auth } from 'agentic-identity';
2
+ import { t as ToolRegistry, o as RunLimits } from './loop-D4qWbRu6.js';
3
+ import { M as ModelProvider } from './types-DGH8FS56.js';
4
+
5
+ interface AssistantApiOptions {
6
+ /** Reads the credential on a request. Use an `agentic-identity` Auth, such as `createStoreAuth`. */
7
+ auth: Auth;
8
+ /** Makes the model for one chat turn. Called once per request, so a provider may keep state. */
9
+ provider(): ModelProvider;
10
+ /** The tools the assistant may use. Each one has a scope and a risk. */
11
+ registry: ToolRegistry;
12
+ system?: string;
13
+ limits?: RunLimits;
14
+ /** Most chat turns one principal may run at once. Default 4. */
15
+ maxConcurrentRuns?: number;
16
+ }
17
+ /** An agent needs this scope before it may decide on a tool call. A person needs no extra scope. */
18
+ declare const APPROVER_SCOPE = "assistant:admin";
19
+ /**
20
+ * The hosted API, under `/api/v1`: `GET /tools`, `POST /chat` (a stream of server-sent events),
21
+ * `GET /approvals`, and `POST /approvals/:id/approve` or `/deny`. Every route needs a principal.
22
+ *
23
+ * A pending approval lives in the memory of this object. Run chat and approval on one instance.
24
+ */
25
+ declare function createAssistantApi(options: AssistantApiOptions): {
26
+ fetch(request: Request): Promise<Response>;
27
+ };
28
+
29
+ /** What one bearer token stands for. The caller is always a person: `kind: "human"`. */
30
+ interface StaticTokenEntry {
31
+ id: string;
32
+ scopes: string[];
33
+ }
34
+ /**
35
+ * An `agentic-identity` Auth that reads `Authorization: Bearer <token>` against a fixed map.
36
+ * It keeps only SHA-256 digests. It throws on a token shorter than 32 bytes or an entry of the wrong shape.
37
+ * An empty map is allowed: every request is then a 401.
38
+ */
39
+ declare function createStaticTokenAuth(tokens: Record<string, StaticTokenEntry>): Promise<Auth>;
40
+
1
41
  interface WorkerEnv {
2
42
  PACK_NAME?: string;
3
43
  }
4
- declare const _default: {
5
- fetch(request: Request, env: WorkerEnv): Promise<Response>;
44
+ /** A Worker that serves `/health` and the `/api/v1` routes. Build the options in your own entry file. */
45
+ declare function createWorker(options: AssistantApiOptions): {
46
+ fetch(request: Request, env?: WorkerEnv): Promise<Response>;
6
47
  };
7
48
 
8
- export { type WorkerEnv, _default as default };
49
+ export { APPROVER_SCOPE, type AssistantApiOptions, type StaticTokenEntry, type WorkerEnv, createAssistantApi, createStaticTokenAuth, createWorker };
package/dist/worker.js CHANGED
@@ -1,17 +1,13 @@
1
1
  import {
2
- VERSION
3
- } from "./chunk-4QE7E5UQ.js";
4
-
5
- // src/worker.ts
6
- var worker_default = {
7
- async fetch(request, env) {
8
- const url = new URL(request.url);
9
- if (request.method === "GET" && url.pathname === "/health") {
10
- return Response.json({ ok: true, pack: env.PACK_NAME ?? "agentic-assistant", version: VERSION });
11
- }
12
- return Response.json({ error: "not found" }, { status: 404 });
13
- }
14
- };
2
+ APPROVER_SCOPE,
3
+ createAssistantApi,
4
+ createStaticTokenAuth,
5
+ createWorker
6
+ } from "./chunk-GLU222OQ.js";
7
+ import "./chunk-3LKTJJ75.js";
15
8
  export {
16
- worker_default as default
9
+ APPROVER_SCOPE,
10
+ createAssistantApi,
11
+ createStaticTokenAuth,
12
+ createWorker
17
13
  };
package/llms.txt CHANGED
@@ -5,6 +5,10 @@ An AI assistant that acts inside your apps with the user's own permissions.
5
5
  Keywords: assistant,ai,agent,tools,mcp,sdk,side-panel,approvals,eu,model-port.
6
6
 
7
7
  - SDK: import { runAssistant, ToolRegistry, createPrincipal } from "agentic-assistant".
8
+ - CLI: agentic-assistant chat --tools <mcp-url> --scope <s> --tool-scope <s> "message". MCP server: agentic-assistant mcp.
9
+ - HTTP API: import { createWorker } from "agentic-assistant/worker". Routes under /api/v1: GET /tools, POST /chat (SSE), GET /approvals, POST /approvals/:id/approve|deny. Sign-in with agentic-identity principals. Chat responses carry x-ai-generated: true. Demo: https://agentic-assistant-demo.hello-fd1.workers.dev (needs a bearer key; no model key set, so chat answers 503 not_configured).
10
+ - HTTP client: import { createClient, AssistantApiError } from "agentic-assistant/client"; createClient({ baseUrl, token, fetch? }) gives health, tools, chat (async generator of events), approvals, approve(id, digest), deny(id, reason?). Typed errors with code and status.
11
+ - AI marking: events carry `provenance`; use provenanceLabel and provenanceMarker (JSON-LD, IPTC trainedAlgorithmicMedia). Also in HTTP, CLI --json and MCP results.
8
12
  - Panel: import { AssistantPanel } from "agentic-assistant/react"; import "agentic-assistant/react.css".
9
13
  - Test provider: import { ScriptedProvider } from "agentic-assistant/testing".
10
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-assistant",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "An AI assistant that acts inside your apps with the user's own permissions.",
5
5
  "keywords": [
6
6
  "assistant",
@@ -23,12 +23,13 @@
23
23
  "type": "module",
24
24
  "private": false,
25
25
  "engines": {
26
- "node": ">=18"
26
+ "node": ">=22"
27
27
  },
28
28
  "files": [
29
29
  "dist",
30
30
  "llms.txt",
31
- "SKILL.md"
31
+ "SKILL.md",
32
+ "CHANGELOG.md"
32
33
  ],
33
34
  "publishConfig": {
34
35
  "access": "public"
@@ -40,6 +41,14 @@
40
41
  "types": "./dist/index.d.ts",
41
42
  "import": "./dist/index.js"
42
43
  },
44
+ "./worker": {
45
+ "types": "./dist/worker.d.ts",
46
+ "import": "./dist/worker.js"
47
+ },
48
+ "./client": {
49
+ "types": "./dist/client.d.ts",
50
+ "import": "./dist/client.js"
51
+ },
43
52
  "./testing": {
44
53
  "types": "./dist/testing.d.ts",
45
54
  "import": "./dist/testing.js"
@@ -58,11 +67,14 @@
58
67
  "typecheck": "tsc --noEmit",
59
68
  "test": "vitest run",
60
69
  "clean": "rm -rf dist",
70
+ "deploy:demo": "npm run build && WRANGLER_SEND_METRICS=false wrangler deploy --config wrangler.demo.jsonc",
71
+ "prepack": "npm run build",
61
72
  "ci:local": "npm run build && npm run typecheck && npm test"
62
73
  },
63
74
  "dependencies": {
64
75
  "@anthropic-ai/sdk": "^1.3.0",
65
76
  "@modelcontextprotocol/sdk": "^1.32.2",
77
+ "agentic-identity": "^0.4.0",
66
78
  "ajv": "^8.20.0"
67
79
  },
68
80
  "devDependencies": {
@@ -77,7 +89,8 @@
77
89
  "react-dom": "^19.3.1",
78
90
  "tsup": "^8.0.0",
79
91
  "typescript": "^5.6.0",
80
- "vitest": "^3.0.0"
92
+ "vitest": "^3.0.0",
93
+ "wrangler": "^4.155.0"
81
94
  },
82
95
  "peerDependencies": {
83
96
  "react": ">=18"
@@ -1,7 +0,0 @@
1
- // src/version.ts
2
- import { createRequire } from "module";
3
- var VERSION = true ? "0.1.0" : readPackageVersion();
4
-
5
- export {
6
- VERSION
7
- };