@theokit/agents 0.30.2 → 0.31.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/dist/index.js CHANGED
@@ -31,9 +31,12 @@ import {
31
31
  AgentRunnerBuilder,
32
32
  AgentWarningCode,
33
33
  BudgetExceededError,
34
+ CostBudgetExceededError,
34
35
  DEFAULT_KEEP_TOKENS,
35
36
  DEFAULT_MAX_ITERATIONS,
36
37
  DelegationError,
38
+ GuardrailViolationError,
39
+ agent,
37
40
  agentsPlugin,
38
41
  buildModelSelection,
39
42
  compactionStrategyConfigSchema,
@@ -44,11 +47,15 @@ import {
44
47
  compileProjectContext,
45
48
  compileSkills,
46
49
  compileTools,
50
+ contextualTool,
51
+ costGuard,
47
52
  createAgentExecutionContext,
48
53
  createSdkAgentStream,
49
54
  createThinkTagExtractor,
55
+ createToolHooksPlugin,
50
56
  defineAgent,
51
57
  delegate,
58
+ estimateTokens,
52
59
  extractThinkTagStream,
53
60
  generateAgentManifest,
54
61
  generateAgentRoutes,
@@ -63,19 +70,26 @@ import {
63
70
  isToolResult,
64
71
  ladderReflectionStrategy,
65
72
  loopStrategyConfigSchema,
73
+ moderateOutputStream,
66
74
  noopReflectionStrategy,
75
+ outputModeration,
76
+ piiDetector,
67
77
  projectContextMetadataOnlyKnobs,
78
+ promptInjectionDetector,
68
79
  reflectionStrategyConfigSchema,
69
80
  resolveCompactionStrategy,
70
81
  resolveLoopStrategy,
82
+ runInputGuards,
83
+ runOutputGuards,
71
84
  streamAgentResponse,
72
85
  streamAgentUIMessages,
73
86
  tokenBudgetCompactionStrategy,
74
87
  translateSdkEvent,
75
88
  translateToUIMessageStream,
89
+ unicodeNormalizer,
76
90
  validateUniqueRoutes,
77
91
  walkAgentMetadata
78
- } from "./chunk-QJKU2YMC.js";
92
+ } from "./chunk-BWYBOMKR.js";
79
93
  import {
80
94
  Agent,
81
95
  Audit,
@@ -109,9 +123,281 @@ import {
109
123
  getSubAgents,
110
124
  resolveSessionId
111
125
  } from "./chunk-FI6ZG2YP.js";
112
- import "./chunk-7QVYU63E.js";
126
+ import {
127
+ __name
128
+ } from "./chunk-7QVYU63E.js";
129
+
130
+ // src/a2a/agent-card.ts
131
+ function trimTrailingSlash(url) {
132
+ return url.endsWith("/") ? url.slice(0, -1) : url;
133
+ }
134
+ __name(trimTrailingSlash, "trimTrailingSlash");
135
+ function buildAgentCard(entry, options) {
136
+ const base = trimTrailingSlash(options.baseUrl);
137
+ return {
138
+ name: entry.name,
139
+ description: options.description ?? `TheoKit agent "${entry.name}".`,
140
+ url: `${base}${entry.route}`,
141
+ version: "1.0",
142
+ capabilities: {
143
+ streaming: entry.stream,
144
+ pushNotifications: false,
145
+ stateTransitionHistory: false
146
+ },
147
+ defaultInputModes: [
148
+ "text"
149
+ ],
150
+ defaultOutputModes: [
151
+ "text"
152
+ ],
153
+ skills: entry.tools.map((t) => ({
154
+ id: t.name,
155
+ name: t.name,
156
+ description: t.description
157
+ }))
158
+ };
159
+ }
160
+ __name(buildAgentCard, "buildAgentCard");
161
+ function wellKnownCardPath(agentName) {
162
+ return `/.well-known/${agentName}/agent-card.json`;
163
+ }
164
+ __name(wellKnownCardPath, "wellKnownCardPath");
165
+
166
+ // src/a2a/mcp-server-manifest.ts
167
+ var MCP_PROTOCOL_VERSION = "2024-11-05";
168
+ function buildMcpToolDescriptors(entry) {
169
+ return entry.tools.map((t) => ({
170
+ name: t.name,
171
+ description: t.description,
172
+ inputSchema: {
173
+ type: "object",
174
+ properties: {}
175
+ }
176
+ }));
177
+ }
178
+ __name(buildMcpToolDescriptors, "buildMcpToolDescriptors");
179
+ function mcpServerInfo(entry) {
180
+ return {
181
+ name: entry.name,
182
+ version: "1.0",
183
+ protocolVersion: MCP_PROTOCOL_VERSION
184
+ };
185
+ }
186
+ __name(mcpServerInfo, "mcpServerInfo");
187
+
188
+ // src/a2a/a2a-client.ts
189
+ function buildHeaders(config) {
190
+ const headers = {
191
+ "content-type": "application/json",
192
+ ...config.headers
193
+ };
194
+ if (config.auth?.bearer) headers.authorization = `Bearer ${config.auth.bearer}`;
195
+ if (config.auth?.apiKey) headers[config.auth.apiKey.header] = config.auth.apiKey.value;
196
+ return headers;
197
+ }
198
+ __name(buildHeaders, "buildHeaders");
199
+ function createA2ATool(config) {
200
+ const doFetch = config.fetchImpl ?? fetch;
201
+ return {
202
+ name: config.name,
203
+ description: config.description,
204
+ inputSchema: {
205
+ type: "object",
206
+ properties: {
207
+ message: {
208
+ type: "string",
209
+ description: "The message to send to the remote agent."
210
+ }
211
+ },
212
+ required: [
213
+ "message"
214
+ ]
215
+ },
216
+ handler: /* @__PURE__ */ __name(async (input) => {
217
+ const message = typeof input.message === "string" ? input.message : "";
218
+ const res = await doFetch(config.url, {
219
+ method: "POST",
220
+ headers: buildHeaders(config),
221
+ body: JSON.stringify({
222
+ message
223
+ })
224
+ });
225
+ if (!res.ok) {
226
+ throw new Error(`A2A call to "${config.name}" failed: ${res.status} ${res.statusText}`);
227
+ }
228
+ const data = await res.json();
229
+ return data.response ?? data.text ?? "";
230
+ }, "handler")
231
+ };
232
+ }
233
+ __name(createA2ATool, "createA2ATool");
234
+
235
+ // src/conversation-scope.ts
236
+ var SEPARATOR = "/";
237
+ function deriveConversationId(resource, thread) {
238
+ if (!resource) throw new Error("[@theokit/agents] deriveConversationId: resource must be non-empty");
239
+ if (!thread) throw new Error("[@theokit/agents] deriveConversationId: thread must be non-empty");
240
+ return `${encodeURIComponent(resource)}${SEPARATOR}${encodeURIComponent(thread)}`;
241
+ }
242
+ __name(deriveConversationId, "deriveConversationId");
243
+ function parseConversationId(id) {
244
+ const idx = id.indexOf(SEPARATOR);
245
+ if (idx <= 0 || idx >= id.length - 1) return null;
246
+ const resource = decodeURIComponent(id.slice(0, idx));
247
+ const thread = decodeURIComponent(id.slice(idx + 1));
248
+ if (!resource || !thread) return null;
249
+ return {
250
+ resource,
251
+ thread
252
+ };
253
+ }
254
+ __name(parseConversationId, "parseConversationId");
255
+
256
+ // src/skills-resolver.ts
257
+ async function resolveEnabledSkills(selection, ctx) {
258
+ if (selection === void 0) return void 0;
259
+ if (typeof selection !== "function") return [
260
+ ...selection
261
+ ];
262
+ const resolved = await selection(ctx);
263
+ if (!Array.isArray(resolved)) {
264
+ throw new Error("[@theokit/agents] skills resolver must return an array of skill names");
265
+ }
266
+ return [
267
+ ...resolved
268
+ ];
269
+ }
270
+ __name(resolveEnabledSkills, "resolveEnabledSkills");
271
+
272
+ // src/acp/protocol.ts
273
+ function encodeAcpMessage(message) {
274
+ return `${JSON.stringify(message)}
275
+ `;
276
+ }
277
+ __name(encodeAcpMessage, "encodeAcpMessage");
278
+ var AcpMessageDecoder = class {
279
+ static {
280
+ __name(this, "AcpMessageDecoder");
281
+ }
282
+ buffer = "";
283
+ /** Feed a chunk; returns the messages completed by this chunk (possibly empty). */
284
+ push(chunk) {
285
+ this.buffer += chunk;
286
+ const messages = [];
287
+ let newlineIndex = this.buffer.indexOf("\n");
288
+ while (newlineIndex !== -1) {
289
+ const line = this.buffer.slice(0, newlineIndex).trim();
290
+ this.buffer = this.buffer.slice(newlineIndex + 1);
291
+ if (line.length > 0) {
292
+ try {
293
+ messages.push(JSON.parse(line));
294
+ } catch (cause) {
295
+ throw new Error(`[@theokit/agents] ACP decode failed on line: ${line}`, {
296
+ cause
297
+ });
298
+ }
299
+ }
300
+ newlineIndex = this.buffer.indexOf("\n");
301
+ }
302
+ return messages;
303
+ }
304
+ };
305
+
306
+ // src/acp/client.ts
307
+ function isResponse(m) {
308
+ return typeof m.id === "number" && ("result" in m || "error" in m) && !("method" in m);
309
+ }
310
+ __name(isResponse, "isResponse");
311
+ function isServerRequest(m) {
312
+ return typeof m.method === "string" && typeof m.id === "number";
313
+ }
314
+ __name(isServerRequest, "isServerRequest");
315
+ var AcpClient = class {
316
+ static {
317
+ __name(this, "AcpClient");
318
+ }
319
+ transport;
320
+ nextId = 1;
321
+ pending = /* @__PURE__ */ new Map();
322
+ handlers = /* @__PURE__ */ new Map();
323
+ decoder = new AcpMessageDecoder();
324
+ constructor(transport) {
325
+ this.transport = transport;
326
+ transport.subscribe((chunk) => {
327
+ for (const message of this.decoder.push(chunk)) {
328
+ this.dispatch(message);
329
+ }
330
+ });
331
+ }
332
+ /** Send a JSON-RPC request and resolve with its `result` (or reject on `error`). */
333
+ request(method, params) {
334
+ const id = this.nextId++;
335
+ return new Promise((resolve, reject) => {
336
+ this.pending.set(id, {
337
+ resolve,
338
+ reject
339
+ });
340
+ this.transport.send(encodeAcpMessage({
341
+ jsonrpc: "2.0",
342
+ id,
343
+ method,
344
+ params
345
+ }));
346
+ });
347
+ }
348
+ /** Register a handler for a server→client request method (e.g. `session/request_permission`). */
349
+ onRequest(method, handler) {
350
+ this.handlers.set(method, handler);
351
+ }
352
+ dispatch(message) {
353
+ if (isResponse(message)) {
354
+ const entry = this.pending.get(message.id);
355
+ if (!entry) return;
356
+ this.pending.delete(message.id);
357
+ if (message.error) entry.reject(new Error(message.error.message));
358
+ else entry.resolve(message.result);
359
+ return;
360
+ }
361
+ if (isServerRequest(message)) {
362
+ void this.handleServerRequest(message);
363
+ }
364
+ }
365
+ async handleServerRequest(req) {
366
+ const handler = this.handlers.get(req.method);
367
+ if (!handler) {
368
+ this.transport.send(encodeAcpMessage({
369
+ jsonrpc: "2.0",
370
+ id: req.id,
371
+ error: {
372
+ code: -32601,
373
+ message: `No handler: ${req.method}`
374
+ }
375
+ }));
376
+ return;
377
+ }
378
+ try {
379
+ const result = await handler(req.params);
380
+ this.transport.send(encodeAcpMessage({
381
+ jsonrpc: "2.0",
382
+ id: req.id,
383
+ result
384
+ }));
385
+ } catch (err) {
386
+ this.transport.send(encodeAcpMessage({
387
+ jsonrpc: "2.0",
388
+ id: req.id,
389
+ error: {
390
+ code: -32603,
391
+ message: err instanceof Error ? err.message : "handler failed"
392
+ }
393
+ }));
394
+ }
395
+ }
396
+ };
113
397
  export {
114
398
  AGENT_BRAND,
399
+ AcpClient,
400
+ AcpMessageDecoder,
115
401
  Agent,
116
402
  AgentDefinitionError,
117
403
  AgentRunner,
@@ -125,14 +411,17 @@ export {
125
411
  Compaction,
126
412
  ContextWindow,
127
413
  Conversation,
414
+ CostBudgetExceededError,
128
415
  DEFAULT_KEEP_TOKENS,
129
416
  DEFAULT_MAX_ITERATIONS,
130
417
  DelegationError,
131
418
  EditFormat,
132
419
  Gateway,
420
+ GuardrailViolationError,
133
421
  Hook,
134
422
  HumanInTheLoop,
135
423
  MCP,
424
+ MCP_PROTOCOL_VERSION,
136
425
  MainLoop,
137
426
  Memory,
138
427
  Mixin,
@@ -148,8 +437,11 @@ export {
148
437
  Tool,
149
438
  Toolbox,
150
439
  Trace,
440
+ agent,
151
441
  agentsPlugin,
152
442
  applyDecorators,
443
+ buildAgentCard,
444
+ buildMcpToolDescriptors,
153
445
  buildModelSelection,
154
446
  compactionStrategyConfigSchema,
155
447
  compileAgent,
@@ -159,11 +451,18 @@ export {
159
451
  compileProjectContext,
160
452
  compileSkills,
161
453
  compileTools,
454
+ contextualTool,
455
+ costGuard,
456
+ createA2ATool,
162
457
  createAgentExecutionContext,
163
458
  createSdkAgentStream,
164
459
  createThinkTagExtractor,
460
+ createToolHooksPlugin,
165
461
  defineAgent,
166
462
  delegate,
463
+ deriveConversationId,
464
+ encodeAcpMessage,
465
+ estimateTokens,
167
466
  extractThinkTagStream,
168
467
  generateAgentManifest,
169
468
  generateAgentRoutes,
@@ -203,18 +502,29 @@ export {
203
502
  isToolResult,
204
503
  ladderReflectionStrategy,
205
504
  loopStrategyConfigSchema,
505
+ mcpServerInfo,
506
+ moderateOutputStream,
206
507
  noopReflectionStrategy,
508
+ outputModeration,
509
+ parseConversationId,
510
+ piiDetector,
207
511
  projectContextMetadataOnlyKnobs,
512
+ promptInjectionDetector,
208
513
  reflectionStrategyConfigSchema,
209
514
  resolveCompactionStrategy,
515
+ resolveEnabledSkills,
210
516
  resolveLoopStrategy,
211
517
  resolveSessionId,
518
+ runInputGuards,
519
+ runOutputGuards,
212
520
  streamAgentResponse,
213
521
  streamAgentUIMessages,
214
522
  tokenBudgetCompactionStrategy,
215
523
  translateSdkEvent,
216
524
  translateToUIMessageStream,
525
+ unicodeNormalizer,
217
526
  validateUniqueRoutes,
218
- walkAgentMetadata
527
+ walkAgentMetadata,
528
+ wellKnownCardPath
219
529
  };
220
530
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/a2a/agent-card.ts","../src/a2a/mcp-server-manifest.ts","../src/a2a/a2a-client.ts","../src/conversation-scope.ts","../src/skills-resolver.ts","../src/acp/protocol.ts","../src/acp/client.ts"],"sourcesContent":["/**\n * M15 (theokit-ai-first) — A2A agent card generation (ADR-0040 § D2, home/discovery concern).\n *\n * Produces an [A2A](https://github.com/google/A2A)-spec Agent Card from the framework's\n * `AgentManifestEntry`, so other systems can discover a TheoKit agent's capabilities over HTTP at\n * `/.well-known/<name>/agent-card.json`. Pure data transform — no LLM, no runtime (G2).\n */\nimport type { AgentManifestEntry } from '../manifest/agent-manifest.js'\n\n/** A single capability exposed by an A2A agent (maps from a TheoKit tool). */\nexport interface A2ASkill {\n id: string\n name: string\n description: string\n}\n\n/** A2A agent-card capabilities block. */\nexport interface A2ACapabilities {\n streaming: boolean\n pushNotifications: boolean\n stateTransitionHistory: boolean\n}\n\n/** An A2A-spec Agent Card (the subset TheoKit advertises). */\nexport interface AgentCard {\n name: string\n description: string\n url: string\n version: string\n capabilities: A2ACapabilities\n defaultInputModes: string[]\n defaultOutputModes: string[]\n skills: A2ASkill[]\n}\n\nexport interface BuildAgentCardOptions {\n /** Absolute base URL of the deployment (e.g. `https://app.example.com`). A trailing slash is trimmed. */\n baseUrl: string\n /** Human description of the agent. Defaults to a generated sentence when omitted (spec requires non-empty). */\n description?: string\n}\n\n/** Strip a single trailing slash so `${baseUrl}${route}` never doubles the separator. */\nfunction trimTrailingSlash(url: string): string {\n return url.endsWith('/') ? url.slice(0, -1) : url\n}\n\n/** Build an A2A agent card from a manifest entry. */\nexport function buildAgentCard(entry: AgentManifestEntry, options: BuildAgentCardOptions): AgentCard {\n const base = trimTrailingSlash(options.baseUrl)\n return {\n name: entry.name,\n description: options.description ?? `TheoKit agent \"${entry.name}\".`,\n url: `${base}${entry.route}`,\n version: '1.0',\n capabilities: {\n streaming: entry.stream,\n pushNotifications: false,\n stateTransitionHistory: false,\n },\n defaultInputModes: ['text'],\n defaultOutputModes: ['text'],\n skills: entry.tools.map((t) => ({ id: t.name, name: t.name, description: t.description })),\n }\n}\n\n/** The A2A discovery path for an agent: `/.well-known/<name>/agent-card.json`. */\nexport function wellKnownCardPath(agentName: string): string {\n return `/.well-known/${agentName}/agent-card.json`\n}\n","/**\n * M16 (theokit-ai-first) — MCP server manifest generation (ADR-0040 § D2, home concern).\n *\n * Exposes a TheoKit agent's tools to external MCP clients as `tools/list` descriptors, so the app\n * can advertise its agents over its OWN HTTP routes. Pure data transform — no LLM, no runtime, and\n * NO stdio transport (that stays SDK-side per sdk-runtime.md). Serving these over `GET /mcp` and the\n * JSON-RPC envelope are follow-ups (mount path).\n */\nimport type { AgentManifestEntry } from '../manifest/agent-manifest.js'\n\n/** The MCP protocol revision these descriptors target. */\nexport const MCP_PROTOCOL_VERSION = '2024-11-05'\n\n/** A JSON-schema object (the shape MCP expects for a tool's `inputSchema`). */\nexport interface McpJsonSchema {\n type: 'object'\n properties: Record<string, unknown>\n required?: string[]\n}\n\n/** An MCP `tools/list` tool descriptor. */\nexport interface McpToolDescriptor {\n name: string\n description: string\n inputSchema: McpJsonSchema\n}\n\n/** MCP `initialize` server info. */\nexport interface McpServerInfo {\n name: string\n version: string\n protocolVersion: string\n}\n\n/**\n * Map an agent's tools to MCP tool descriptors. The manifest tool carries name + description; the\n * per-tool JSON schema is not retained in the manifest, so a permissive empty-object schema is\n * emitted (MCP clients accept it and pass through arbitrary args). Wiring the real per-tool schema\n * is a follow-up once the manifest carries it.\n */\nexport function buildMcpToolDescriptors(entry: AgentManifestEntry): McpToolDescriptor[] {\n return entry.tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: { type: 'object', properties: {} },\n }))\n}\n\n/** Build the MCP `initialize` server-info block for an agent. */\nexport function mcpServerInfo(entry: AgentManifestEntry): McpServerInfo {\n return { name: entry.name, version: '1.0', protocolVersion: MCP_PROTOCOL_VERSION }\n}\n","/**\n * M15 (theokit-ai-first) — A2A client: call a remote A2A agent as a tool (ADR-0040 § D2).\n *\n * `createA2ATool` returns a `CustomTool` whose handler POSTs the input message to a remote agent's\n * HTTP endpoint and returns its text response — cross-network delegation. Uses `fetch` (Web\n * Standards, G8). The target is a remote AGENT endpoint, not an LLM provider, so the G2 grep guard\n * (`openrouter.ai|api.openai.com|api.anthropic.com`) is unaffected. `fetchImpl` is injectable for tests.\n */\nimport type { CustomTool } from '@theokit/sdk'\n\n/** How to authenticate to the remote agent. */\nexport interface A2AAuth {\n /** Bearer token → `Authorization: Bearer <token>`. */\n bearer?: string\n /** API-key header pair → `<name>: <value>` (e.g. `x-api-key`). */\n apiKey?: { header: string; value: string }\n}\n\nexport interface A2AToolConfig {\n /** Remote agent endpoint URL (POST target). */\n url: string\n /** Tool name the model calls. */\n name: string\n /** Tool description surfaced to the model. */\n description: string\n /** Static headers merged into every request. */\n headers?: Record<string, string>\n /** Auth applied to every request. */\n auth?: A2AAuth\n /** Injected fetch (defaults to the global). Narrowed to the call shape this client uses. */\n fetchImpl?: (url: string, init: RequestInit) => Promise<Response>\n}\n\n/** Build the request headers from static headers + auth. */\nfunction buildHeaders(config: A2AToolConfig): Record<string, string> {\n const headers: Record<string, string> = { 'content-type': 'application/json', ...config.headers }\n if (config.auth?.bearer) headers.authorization = `Bearer ${config.auth.bearer}`\n if (config.auth?.apiKey) headers[config.auth.apiKey.header] = config.auth.apiKey.value\n return headers\n}\n\n/**\n * Create a tool that delegates to a remote A2A agent. The remote is expected to answer a\n * `{ message }` POST with a JSON body carrying a `response` (or `text`) string.\n */\nexport function createA2ATool(config: A2AToolConfig): CustomTool {\n const doFetch = config.fetchImpl ?? fetch\n return {\n name: config.name,\n description: config.description,\n inputSchema: {\n type: 'object',\n properties: { message: { type: 'string', description: 'The message to send to the remote agent.' } },\n required: ['message'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n // The input schema requires `message: string`; narrow defensively (never base-to-string).\n const message = typeof input.message === 'string' ? input.message : ''\n const res = await doFetch(config.url, {\n method: 'POST',\n headers: buildHeaders(config),\n body: JSON.stringify({ message }),\n })\n if (!res.ok) {\n throw new Error(`A2A call to \"${config.name}\" failed: ${res.status} ${res.statusText}`)\n }\n const data = (await res.json()) as { response?: string; text?: string }\n return data.response ?? data.text ?? ''\n },\n }\n}\n","/**\n * M11 (theokit-ai-first) — {resource, thread} conversation scoping (ADR-0040 § D2, home concern).\n *\n * Maps a request's (resource, thread) pair to a deterministic, collision-safe conversation id so\n * multi-tenant apps isolate history without hand-building `user-${id}-thread-${id}` strings. This\n * is the app's request→conversation mapping — NOT the SDK's storage engine (which the derived id is\n * simply handed to). Background compression of long histories stays SDK-side.\n *\n * Encoding: each component is `encodeURIComponent`-escaped and joined with `/`. Since\n * `encodeURIComponent` escapes a raw `/` to `%2F`, the separator is unambiguous — `('a/b','c')` and\n * `('a','b/c')` derive DIFFERENT ids (collision-safe). Reversible via {@link parseConversationId}.\n */\n\nconst SEPARATOR = '/'\n\n/** Derive a deterministic conversation id from a (resource, thread) pair. Fails fast on empty input. */\nexport function deriveConversationId(resource: string, thread: string): string {\n if (!resource) throw new Error('[@theokit/agents] deriveConversationId: resource must be non-empty')\n if (!thread) throw new Error('[@theokit/agents] deriveConversationId: thread must be non-empty')\n return `${encodeURIComponent(resource)}${SEPARATOR}${encodeURIComponent(thread)}`\n}\n\n/**\n * Reverse a derived conversation id back to its (resource, thread). Returns `null` for a value that\n * is not a derived scope id (no separator, or an empty component) — callers treat that as \"opaque id,\n * not scoped\".\n */\nexport function parseConversationId(id: string): { resource: string; thread: string } | null {\n const idx = id.indexOf(SEPARATOR)\n if (idx <= 0 || idx >= id.length - 1) return null\n const resource = decodeURIComponent(id.slice(0, idx))\n const thread = decodeURIComponent(id.slice(idx + 1))\n if (!resource || !thread) return null\n return { resource, thread }\n}\n","/**\n * M13 (theokit-ai-first) — per-request skills resolution (ADR-0040 § D2, home/boundary concern).\n *\n * The static `skills.enabled` filter already works (`compile-skills` maps `include` → the SDK's\n * `enabled`). This adds a PER-REQUEST resolver so multi-tenant apps expose different skill sets to\n * different users. A selection is either a static list or a function of the request context (the M7\n * run-context). Discovery + injection stay in the SDK; this only CHOOSES the enabled set per call.\n */\n\n/** The request context handed to a skills resolver (the M7 run-context — opaque per-request data). */\nexport type SkillsRequestContext = Record<string, unknown>\n\n/**\n * How the enabled skill set is chosen:\n * - `string[]` — a static list (same shape `skills.enabled` accepts today).\n * - a function — resolved per request from the {@link SkillsRequestContext} (sync or async).\n */\nexport type SkillsSelection =\n | readonly string[]\n | ((ctx: SkillsRequestContext) => readonly string[] | Promise<readonly string[]>)\n\n/**\n * Resolve the enabled skill names for a request. Returns `undefined` when no selection is given (the\n * SDK then enables every discovered skill). Fails fast if a resolver returns a non-array.\n */\nexport async function resolveEnabledSkills(\n selection: SkillsSelection | undefined,\n ctx: SkillsRequestContext,\n): Promise<string[] | undefined> {\n if (selection === undefined) return undefined\n if (typeof selection !== 'function') return [...selection]\n const resolved = await selection(ctx)\n if (!Array.isArray(resolved)) {\n throw new Error('[@theokit/agents] skills resolver must return an array of skill names')\n }\n return [...resolved]\n}\n","/**\n * M17 (theokit-ai-first) — ACP (Agent Client Protocol) stdio framing.\n *\n * ACP talks to a coding agent (Claude Code, Amp, Codex) over stdio as newline-delimited JSON. This\n * is the transport-agnostic CORE: encode a message to a line, and decode a byte/char stream that may\n * split a message across chunks. Pure — no subprocess, no Node API. The subprocess spawn lives in the\n * adapter layer (G8) / SDK; `createACPTool` (wrapping this codec + an injected transport) is a\n * follow-up once the adapter ships.\n */\n\n/** Serialize a message to a single newline-terminated JSON line. */\nexport function encodeAcpMessage(message: unknown): string {\n return `${JSON.stringify(message)}\\n`\n}\n\n/**\n * Incremental decoder for newline-delimited JSON. Feed it chunks with {@link push}; it buffers a\n * partial trailing line across calls and returns every WHOLE message parsed so far. Blank lines are\n * skipped. A completed non-JSON line fails fast with a typed error (error-handling.md) — a corrupt\n * frame must never be silently dropped.\n */\nexport class AcpMessageDecoder {\n private buffer = ''\n\n /** Feed a chunk; returns the messages completed by this chunk (possibly empty). */\n push(chunk: string): unknown[] {\n this.buffer += chunk\n const messages: unknown[] = []\n let newlineIndex = this.buffer.indexOf('\\n')\n while (newlineIndex !== -1) {\n const line = this.buffer.slice(0, newlineIndex).trim()\n this.buffer = this.buffer.slice(newlineIndex + 1)\n if (line.length > 0) {\n try {\n messages.push(JSON.parse(line))\n } catch (cause) {\n throw new Error(`[@theokit/agents] ACP decode failed on line: ${line}`, { cause })\n }\n }\n newlineIndex = this.buffer.indexOf('\\n')\n }\n return messages\n }\n}\n","/**\n * M17 (theokit-ai-first) — ACP client: JSON-RPC over the stdio framing.\n *\n * Drives a coding agent (Claude Code, Amp, Codex) over an INJECTED {@link AcpTransport}. The\n * subprocess spawn is a Node API and lives in the adapter layer (G8); this client is transport-\n * agnostic and testable. It correlates responses to requests by `id`, and dispatches server→client\n * requests (e.g. `session/request_permission`) to a registered handler, replying with its decision.\n */\nimport { AcpMessageDecoder, encodeAcpMessage } from './protocol.js'\n\n/** The stdio channel to the coding-agent subprocess (abstracted for testability + G8). */\nexport interface AcpTransport {\n /** Write one already-encoded (newline-terminated) line to the agent's stdin. */\n send(line: string): void\n /** Subscribe to raw lines/chunks from the agent's stdout. */\n subscribe(onData: (chunk: string) => void): void\n}\n\ninterface JsonRpcResponse {\n id: number\n result?: unknown\n error?: { code: number; message: string }\n}\ninterface JsonRpcServerRequest {\n id: number\n method: string\n params?: unknown\n}\n\ninterface Pending {\n resolve: (value: unknown) => void\n reject: (err: Error) => void\n}\n/** Return a value or a Promise — `unknown` already includes `Promise<unknown>`; `await` handles both. */\ntype ServerRequestHandler = (params: unknown) => unknown\n\nfunction isResponse(m: Record<string, unknown>): m is JsonRpcResponse & Record<string, unknown> {\n return typeof m.id === 'number' && (('result' in m) || ('error' in m)) && !('method' in m)\n}\nfunction isServerRequest(m: Record<string, unknown>): m is JsonRpcServerRequest & Record<string, unknown> {\n return typeof m.method === 'string' && typeof m.id === 'number'\n}\n\nexport class AcpClient {\n private nextId = 1\n private readonly pending = new Map<number, Pending>()\n private readonly handlers = new Map<string, ServerRequestHandler>()\n private readonly decoder = new AcpMessageDecoder()\n\n constructor(private readonly transport: AcpTransport) {\n transport.subscribe((chunk) => {\n for (const message of this.decoder.push(chunk)) {\n this.dispatch(message as Record<string, unknown>)\n }\n })\n }\n\n /** Send a JSON-RPC request and resolve with its `result` (or reject on `error`). */\n request(method: string, params: unknown): Promise<unknown> {\n const id = this.nextId++\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject })\n this.transport.send(encodeAcpMessage({ jsonrpc: '2.0', id, method, params }))\n })\n }\n\n /** Register a handler for a server→client request method (e.g. `session/request_permission`). */\n onRequest(method: string, handler: ServerRequestHandler): void {\n this.handlers.set(method, handler)\n }\n\n private dispatch(message: Record<string, unknown>): void {\n if (isResponse(message)) {\n const entry = this.pending.get(message.id)\n if (!entry) return\n this.pending.delete(message.id)\n if (message.error) entry.reject(new Error(message.error.message))\n else entry.resolve(message.result)\n return\n }\n if (isServerRequest(message)) {\n void this.handleServerRequest(message)\n }\n }\n\n private async handleServerRequest(req: JsonRpcServerRequest): Promise<void> {\n const handler = this.handlers.get(req.method)\n if (!handler) {\n this.transport.send(\n encodeAcpMessage({ jsonrpc: '2.0', id: req.id, error: { code: -32601, message: `No handler: ${req.method}` } }),\n )\n return\n }\n try {\n const result = await handler(req.params)\n this.transport.send(encodeAcpMessage({ jsonrpc: '2.0', id: req.id, result }))\n } catch (err) {\n this.transport.send(\n encodeAcpMessage({\n jsonrpc: '2.0',\n id: req.id,\n error: { code: -32603, message: err instanceof Error ? err.message : 'handler failed' },\n }),\n )\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAASA,kBAAkBC,KAAW;AACpC,SAAOA,IAAIC,SAAS,GAAA,IAAOD,IAAIE,MAAM,GAAG,EAAC,IAAKF;AAChD;AAFSD;AAKF,SAASI,eAAeC,OAA2BC,SAA8B;AACtF,QAAMC,OAAOP,kBAAkBM,QAAQE,OAAO;AAC9C,SAAO;IACLC,MAAMJ,MAAMI;IACZC,aAAaJ,QAAQI,eAAe,kBAAkBL,MAAMI,IAAI;IAChER,KAAK,GAAGM,IAAAA,GAAOF,MAAMM,KAAK;IAC1BC,SAAS;IACTC,cAAc;MACZC,WAAWT,MAAMU;MACjBC,mBAAmB;MACnBC,wBAAwB;IAC1B;IACAC,mBAAmB;MAAC;;IACpBC,oBAAoB;MAAC;;IACrBC,QAAQf,MAAMgB,MAAMC,IAAI,CAACC,OAAO;MAAEC,IAAID,EAAEd;MAAMA,MAAMc,EAAEd;MAAMC,aAAaa,EAAEb;IAAY,EAAA;EACzF;AACF;AAhBgBN;AAmBT,SAASqB,kBAAkBC,WAAiB;AACjD,SAAO,gBAAgBA,SAAAA;AACzB;AAFgBD;;;ACxDT,IAAME,uBAAuB;AA6B7B,SAASC,wBAAwBC,OAAyB;AAC/D,SAAOA,MAAMC,MAAMC,IAAI,CAACC,OAAO;IAC7BC,MAAMD,EAAEC;IACRC,aAAaF,EAAEE;IACfC,aAAa;MAAEC,MAAM;MAAUC,YAAY,CAAC;IAAE;EAChD,EAAA;AACF;AANgBT;AAST,SAASU,cAAcT,OAAyB;AACrD,SAAO;IAAEI,MAAMJ,MAAMI;IAAMM,SAAS;IAAOC,iBAAiBb;EAAqB;AACnF;AAFgBW;;;ACfhB,SAASG,aAAaC,QAAqB;AACzC,QAAMC,UAAkC;IAAE,gBAAgB;IAAoB,GAAGD,OAAOC;EAAQ;AAChG,MAAID,OAAOE,MAAMC,OAAQF,SAAQG,gBAAgB,UAAUJ,OAAOE,KAAKC,MAAM;AAC7E,MAAIH,OAAOE,MAAMG,OAAQJ,SAAQD,OAAOE,KAAKG,OAAOC,MAAM,IAAIN,OAAOE,KAAKG,OAAOE;AACjF,SAAON;AACT;AALSF;AAWF,SAASS,cAAcR,QAAqB;AACjD,QAAMS,UAAUT,OAAOU,aAAaC;AACpC,SAAO;IACLC,MAAMZ,OAAOY;IACbC,aAAab,OAAOa;IACpBC,aAAa;MACXC,MAAM;MACNC,YAAY;QAAEC,SAAS;UAAEF,MAAM;UAAUF,aAAa;QAA2C;MAAE;MACnGK,UAAU;QAAC;;IACb;IACAC,SAAS,8BAAOC,UAAAA;AAEd,YAAMH,UAAU,OAAOG,MAAMH,YAAY,WAAWG,MAAMH,UAAU;AACpE,YAAMI,MAAM,MAAMZ,QAAQT,OAAOsB,KAAK;QACpCC,QAAQ;QACRtB,SAASF,aAAaC,MAAAA;QACtBwB,MAAMC,KAAKC,UAAU;UAAET;QAAQ,CAAA;MACjC,CAAA;AACA,UAAI,CAACI,IAAIM,IAAI;AACX,cAAM,IAAIC,MAAM,gBAAgB5B,OAAOY,IAAI,aAAaS,IAAIQ,MAAM,IAAIR,IAAIS,UAAU,EAAE;MACxF;AACA,YAAMC,OAAQ,MAAMV,IAAIW,KAAI;AAC5B,aAAOD,KAAKE,YAAYF,KAAKG,QAAQ;IACvC,GAbS;EAcX;AACF;AAzBgB1B;;;AChChB,IAAM2B,YAAY;AAGX,SAASC,qBAAqBC,UAAkBC,QAAc;AACnE,MAAI,CAACD,SAAU,OAAM,IAAIE,MAAM,oEAAA;AAC/B,MAAI,CAACD,OAAQ,OAAM,IAAIC,MAAM,kEAAA;AAC7B,SAAO,GAAGC,mBAAmBH,QAAAA,CAAAA,GAAYF,SAAAA,GAAYK,mBAAmBF,MAAAA,CAAAA;AAC1E;AAJgBF;AAWT,SAASK,oBAAoBC,IAAU;AAC5C,QAAMC,MAAMD,GAAGE,QAAQT,SAAAA;AACvB,MAAIQ,OAAO,KAAKA,OAAOD,GAAGG,SAAS,EAAG,QAAO;AAC7C,QAAMR,WAAWS,mBAAmBJ,GAAGK,MAAM,GAAGJ,GAAAA,CAAAA;AAChD,QAAML,SAASQ,mBAAmBJ,GAAGK,MAAMJ,MAAM,CAAA,CAAA;AACjD,MAAI,CAACN,YAAY,CAACC,OAAQ,QAAO;AACjC,SAAO;IAAED;IAAUC;EAAO;AAC5B;AAPgBG;;;ACFhB,eAAsBO,qBACpBC,WACAC,KAAyB;AAEzB,MAAID,cAAcE,OAAW,QAAOA;AACpC,MAAI,OAAOF,cAAc,WAAY,QAAO;OAAIA;;AAChD,QAAMG,WAAW,MAAMH,UAAUC,GAAAA;AACjC,MAAI,CAACG,MAAMC,QAAQF,QAAAA,GAAW;AAC5B,UAAM,IAAIG,MAAM,uEAAA;EAClB;AACA,SAAO;OAAIH;;AACb;AAXsBJ;;;ACdf,SAASQ,iBAAiBC,SAAgB;AAC/C,SAAO,GAAGC,KAAKC,UAAUF,OAAAA,CAAAA;;AAC3B;AAFgBD;AAUT,IAAMI,oBAAN,MAAMA;EArBb,OAqBaA;;;EACHC,SAAS;;EAGjBC,KAAKC,OAA0B;AAC7B,SAAKF,UAAUE;AACf,UAAMC,WAAsB,CAAA;AAC5B,QAAIC,eAAe,KAAKJ,OAAOK,QAAQ,IAAA;AACvC,WAAOD,iBAAiB,IAAI;AAC1B,YAAME,OAAO,KAAKN,OAAOO,MAAM,GAAGH,YAAAA,EAAcI,KAAI;AACpD,WAAKR,SAAS,KAAKA,OAAOO,MAAMH,eAAe,CAAA;AAC/C,UAAIE,KAAKG,SAAS,GAAG;AACnB,YAAI;AACFN,mBAASF,KAAKJ,KAAKa,MAAMJ,IAAAA,CAAAA;QAC3B,SAASK,OAAO;AACd,gBAAM,IAAIC,MAAM,gDAAgDN,IAAAA,IAAQ;YAAEK;UAAM,CAAA;QAClF;MACF;AACAP,qBAAe,KAAKJ,OAAOK,QAAQ,IAAA;IACrC;AACA,WAAOF;EACT;AACF;;;ACPA,SAASU,WAAWC,GAA0B;AAC5C,SAAO,OAAOA,EAAEC,OAAO,aAAc,YAAYD,KAAO,WAAWA,MAAO,EAAE,YAAYA;AAC1F;AAFSD;AAGT,SAASG,gBAAgBF,GAA0B;AACjD,SAAO,OAAOA,EAAEG,WAAW,YAAY,OAAOH,EAAEC,OAAO;AACzD;AAFSC;AAIF,IAAME,YAAN,MAAMA;EA3Cb,OA2CaA;;;;EACHC,SAAS;EACAC,UAAU,oBAAIC,IAAAA;EACdC,WAAW,oBAAID,IAAAA;EACfE,UAAU,IAAIC,kBAAAA;EAE/B,YAA6BC,WAAyB;SAAzBA,YAAAA;AAC3BA,cAAUC,UAAU,CAACC,UAAAA;AACnB,iBAAWC,WAAW,KAAKL,QAAQM,KAAKF,KAAAA,GAAQ;AAC9C,aAAKG,SAASF,OAAAA;MAChB;IACF,CAAA;EACF;;EAGAG,QAAQd,QAAgBe,QAAmC;AACzD,UAAMjB,KAAK,KAAKI;AAChB,WAAO,IAAIc,QAAiB,CAACC,SAASC,WAAAA;AACpC,WAAKf,QAAQgB,IAAIrB,IAAI;QAAEmB;QAASC;MAAO,CAAA;AACvC,WAAKV,UAAUY,KAAKC,iBAAiB;QAAEC,SAAS;QAAOxB;QAAIE;QAAQe;MAAO,CAAA,CAAA;IAC5E,CAAA;EACF;;EAGAQ,UAAUvB,QAAgBwB,SAAqC;AAC7D,SAAKnB,SAASc,IAAInB,QAAQwB,OAAAA;EAC5B;EAEQX,SAASF,SAAwC;AACvD,QAAIf,WAAWe,OAAAA,GAAU;AACvB,YAAMc,QAAQ,KAAKtB,QAAQuB,IAAIf,QAAQb,EAAE;AACzC,UAAI,CAAC2B,MAAO;AACZ,WAAKtB,QAAQwB,OAAOhB,QAAQb,EAAE;AAC9B,UAAIa,QAAQiB,MAAOH,OAAMP,OAAO,IAAIW,MAAMlB,QAAQiB,MAAMjB,OAAO,CAAA;UAC1Dc,OAAMR,QAAQN,QAAQmB,MAAM;AACjC;IACF;AACA,QAAI/B,gBAAgBY,OAAAA,GAAU;AAC5B,WAAK,KAAKoB,oBAAoBpB,OAAAA;IAChC;EACF;EAEA,MAAcoB,oBAAoBC,KAA0C;AAC1E,UAAMR,UAAU,KAAKnB,SAASqB,IAAIM,IAAIhC,MAAM;AAC5C,QAAI,CAACwB,SAAS;AACZ,WAAKhB,UAAUY,KACbC,iBAAiB;QAAEC,SAAS;QAAOxB,IAAIkC,IAAIlC;QAAI8B,OAAO;UAAEK,MAAM;UAAQtB,SAAS,eAAeqB,IAAIhC,MAAM;QAAG;MAAE,CAAA,CAAA;AAE/G;IACF;AACA,QAAI;AACF,YAAM8B,SAAS,MAAMN,QAAQQ,IAAIjB,MAAM;AACvC,WAAKP,UAAUY,KAAKC,iBAAiB;QAAEC,SAAS;QAAOxB,IAAIkC,IAAIlC;QAAIgC;MAAO,CAAA,CAAA;IAC5E,SAASI,KAAK;AACZ,WAAK1B,UAAUY,KACbC,iBAAiB;QACfC,SAAS;QACTxB,IAAIkC,IAAIlC;QACR8B,OAAO;UAAEK,MAAM;UAAQtB,SAASuB,eAAeL,QAAQK,IAAIvB,UAAU;QAAiB;MACxF,CAAA,CAAA;IAEJ;EACF;AACF;","names":["trimTrailingSlash","url","endsWith","slice","buildAgentCard","entry","options","base","baseUrl","name","description","route","version","capabilities","streaming","stream","pushNotifications","stateTransitionHistory","defaultInputModes","defaultOutputModes","skills","tools","map","t","id","wellKnownCardPath","agentName","MCP_PROTOCOL_VERSION","buildMcpToolDescriptors","entry","tools","map","t","name","description","inputSchema","type","properties","mcpServerInfo","version","protocolVersion","buildHeaders","config","headers","auth","bearer","authorization","apiKey","header","value","createA2ATool","doFetch","fetchImpl","fetch","name","description","inputSchema","type","properties","message","required","handler","input","res","url","method","body","JSON","stringify","ok","Error","status","statusText","data","json","response","text","SEPARATOR","deriveConversationId","resource","thread","Error","encodeURIComponent","parseConversationId","id","idx","indexOf","length","decodeURIComponent","slice","resolveEnabledSkills","selection","ctx","undefined","resolved","Array","isArray","Error","encodeAcpMessage","message","JSON","stringify","AcpMessageDecoder","buffer","push","chunk","messages","newlineIndex","indexOf","line","slice","trim","length","parse","cause","Error","isResponse","m","id","isServerRequest","method","AcpClient","nextId","pending","Map","handlers","decoder","AcpMessageDecoder","transport","subscribe","chunk","message","push","dispatch","request","params","Promise","resolve","reject","set","send","encodeAcpMessage","jsonrpc","onRequest","handler","entry","get","delete","error","Error","result","handleServerRequest","req","code","err"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theokit/agents",
3
- "version": "0.30.2",
3
+ "version": "0.31.0",
4
4
  "description": "Unified decorator runtime — AI agents as first-class citizens of the TheoKit pipeline. @Agent() compiles to SDK Agent.create(), @Tool() compiles to defineTool().",
5
5
  "type": "module",
6
6
  "sideEffects": false,