maestro-core 0.1.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/README.md +55 -0
- package/dist/adapters/ai-sdk.d.ts +60 -0
- package/dist/adapters/ai-sdk.d.ts.map +1 -0
- package/dist/adapters/ai-sdk.js +86 -0
- package/dist/adapters/ai-sdk.js.map +1 -0
- package/dist/adapters/mcp-server.d.ts +37 -0
- package/dist/adapters/mcp-server.d.ts.map +1 -0
- package/dist/adapters/mcp-server.js +99 -0
- package/dist/adapters/mcp-server.js.map +1 -0
- package/dist/cache-control.d.ts +78 -0
- package/dist/cache-control.d.ts.map +1 -0
- package/dist/cache-control.js +57 -0
- package/dist/cache-control.js.map +1 -0
- package/dist/context.d.ts +62 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/envelope.d.ts +36 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +9 -0
- package/dist/envelope.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/ports/audit-store.d.ts +33 -0
- package/dist/ports/audit-store.d.ts.map +1 -0
- package/dist/ports/audit-store.js +2 -0
- package/dist/ports/audit-store.js.map +1 -0
- package/dist/ports/clock.d.ts +22 -0
- package/dist/ports/clock.d.ts.map +1 -0
- package/dist/ports/clock.js +16 -0
- package/dist/ports/clock.js.map +1 -0
- package/dist/ports/index.d.ts +9 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +4 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/ports/key-provider.d.ts +18 -0
- package/dist/ports/key-provider.d.ts.map +1 -0
- package/dist/ports/key-provider.js +2 -0
- package/dist/ports/key-provider.js.map +1 -0
- package/dist/ports/logger.d.ts +25 -0
- package/dist/ports/logger.d.ts.map +1 -0
- package/dist/ports/logger.js +22 -0
- package/dist/ports/logger.js.map +1 -0
- package/dist/ports/memory-store.d.ts +32 -0
- package/dist/ports/memory-store.d.ts.map +1 -0
- package/dist/ports/memory-store.js +2 -0
- package/dist/ports/memory-store.js.map +1 -0
- package/dist/ports/quota-store.d.ts +63 -0
- package/dist/ports/quota-store.d.ts.map +1 -0
- package/dist/ports/quota-store.js +2 -0
- package/dist/ports/quota-store.js.map +1 -0
- package/dist/ports/telemetry-sink.d.ts +71 -0
- package/dist/ports/telemetry-sink.d.ts.map +1 -0
- package/dist/ports/telemetry-sink.js +11 -0
- package/dist/ports/telemetry-sink.js.map +1 -0
- package/dist/ports/turn-store.d.ts +62 -0
- package/dist/ports/turn-store.d.ts.map +1 -0
- package/dist/ports/turn-store.js +2 -0
- package/dist/ports/turn-store.js.map +1 -0
- package/dist/safe-tool.d.ts +38 -0
- package/dist/safe-tool.d.ts.map +1 -0
- package/dist/safe-tool.js +16 -0
- package/dist/safe-tool.js.map +1 -0
- package/dist/tool.d.ts +86 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +11 -0
- package/dist/tool.js.map +1 -0
- package/package.json +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# maestro-core
|
|
2
|
+
|
|
3
|
+
Runtime kernel for the Maestro agent platform. In-process tool-calling runtime with port-based governance — model-agnostic, transport-agnostic, framework-agnostic kernel that powers user-support chat and tool calling for SaaS products.
|
|
4
|
+
|
|
5
|
+
`0.1.0` ships:
|
|
6
|
+
|
|
7
|
+
- `ToolEnvelope<T>` — uniform success/failure shape every tool returns
|
|
8
|
+
- `defineAgentTool<TInput, TOutput, TCtx>` — tool definition factory with generic context extension
|
|
9
|
+
- `BaseToolContext` — extensible per-request context
|
|
10
|
+
- 8 port interfaces: `TurnStore`, `AuditStore`, `MemoryStore`, `QuotaStore`, `ModelKeyProvider`, `TelemetrySink`, `Clock`, `Logger`
|
|
11
|
+
- `applyCacheBreakpoints` — Anthropic ephemeral prompt-cache helper
|
|
12
|
+
- `captureToolException` — observability hook for tool execute exceptions
|
|
13
|
+
- AI SDK adapter (`maestro-core/adapters/ai-sdk`) — wraps registry into `ToolSet` with audit + cache breakpoint
|
|
14
|
+
- MCP server adapter (`maestro-core/adapters/mcp-server`) — registers the same registry on an MCP server
|
|
15
|
+
|
|
16
|
+
`runChatTurn` (the full streaming orchestrator) lands in `0.2.0`.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add maestro-core zod
|
|
22
|
+
# optional, for the AI SDK adapter:
|
|
23
|
+
pnpm add ai
|
|
24
|
+
# optional, for the MCP server adapter:
|
|
25
|
+
pnpm add @modelcontextprotocol/sdk
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { ok, err, defineAgentTool, type BaseToolContext } from 'maestro-core'
|
|
32
|
+
import { z } from 'zod'
|
|
33
|
+
|
|
34
|
+
type MyCtx = BaseToolContext & { role: 'admin' | 'guest' }
|
|
35
|
+
|
|
36
|
+
export const lookupTool = defineAgentTool<z.ZodObject<{ id: z.ZodNumber }>, { name: string }, MyCtx>({
|
|
37
|
+
name: 'lookup',
|
|
38
|
+
description: 'Look up a record by id. Admin only.',
|
|
39
|
+
transports: ['chat'],
|
|
40
|
+
inputSchema: z.object({ id: z.number() }),
|
|
41
|
+
isAvailable: (ctx) => ctx.role === 'admin',
|
|
42
|
+
execute: async (input, ctx) => {
|
|
43
|
+
if (input.id === 0) return err('NOT_FOUND', 'no such record')
|
|
44
|
+
return ok({ name: `record-${input.id}` })
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Design
|
|
50
|
+
|
|
51
|
+
See [DESIGN.md](https://github.com/costasoftware/maestro/blob/main/DESIGN.md) for the architecture, port interfaces, and migration roadmap.
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
Apache-2.0
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ToolSet } from 'ai';
|
|
2
|
+
import { type CacheableBlock, type CachedMessages } from '../cache-control.js';
|
|
3
|
+
import type { BaseToolContext } from '../context.js';
|
|
4
|
+
import type { AuditStore } from '../ports/audit-store.js';
|
|
5
|
+
import type { Clock } from '../ports/clock.js';
|
|
6
|
+
import { type ToolExceptionHandler } from '../safe-tool.js';
|
|
7
|
+
import type { AnyAgentToolDefinition } from '../tool.js';
|
|
8
|
+
/**
|
|
9
|
+
* Translate a registry of `AgentToolDefinition`s into a Vercel AI SDK
|
|
10
|
+
* `ToolSet` ready to pass to `streamText` / `generateText`.
|
|
11
|
+
*
|
|
12
|
+
* Filtering happens BEFORE this call: the host runs each tool's
|
|
13
|
+
* `isAvailable(ctx)` + `def.transports.includes(ctx.transport)` to
|
|
14
|
+
* decide what to advertise; only the eligible subset reaches here.
|
|
15
|
+
* Keeping eligibility outside the adapter means hosts can layer their
|
|
16
|
+
* own gates (OAuth scopes, feature flags) on top without forking.
|
|
17
|
+
*
|
|
18
|
+
* Each tool's `execute` is wrapped in try/catch:
|
|
19
|
+
* 1. On success: writes an audit row (if `audit` port provided).
|
|
20
|
+
* 2. On envelope-error (`ok: false`): same audit row, just with the
|
|
21
|
+
* error code/message.
|
|
22
|
+
* 3. On thrown exception: audit row tagged `tool_exception`, calls
|
|
23
|
+
* `onError` for observability, THEN rethrows so the AI SDK marks
|
|
24
|
+
* the tool result as `error` and the model sees it.
|
|
25
|
+
*
|
|
26
|
+
* The last tool in iteration order receives the Anthropic ephemeral
|
|
27
|
+
* cacheControl marker (`applyCacheBreakpoints`). Cross-tenant cache
|
|
28
|
+
* reuse depends on the tool registry bytes being identical across
|
|
29
|
+
* tenants — keep tool descriptions tenant-invariant.
|
|
30
|
+
*/
|
|
31
|
+
export interface BuildAiSdkToolsArgs<TCtx extends BaseToolContext> {
|
|
32
|
+
/** Registry already filtered for the active surface + actor + isAvailable. */
|
|
33
|
+
registry: readonly AnyAgentToolDefinition<TCtx>[];
|
|
34
|
+
/** Request context — passed to every `execute` call. */
|
|
35
|
+
ctx: TCtx;
|
|
36
|
+
/** Optional audit port. Calls are fire-and-forget. */
|
|
37
|
+
audit?: AuditStore;
|
|
38
|
+
/** Optional observability hook for thrown exceptions. */
|
|
39
|
+
onError?: ToolExceptionHandler;
|
|
40
|
+
/** Optional clock override (testing). */
|
|
41
|
+
clock?: Clock;
|
|
42
|
+
}
|
|
43
|
+
export declare function buildAiSdkTools<TCtx extends BaseToolContext>(args: BuildAiSdkToolsArgs<TCtx>): ToolSet;
|
|
44
|
+
/**
|
|
45
|
+
* Convenience wrapper: builds the ToolSet AND applies the
|
|
46
|
+
* static-vs-dynamic system-prompt cache breakpoint in one call.
|
|
47
|
+
*
|
|
48
|
+
* Hosts that prefer to manage cache placement themselves (e.g. an
|
|
49
|
+
* adapter combining a multi-segment system prompt with a third-party
|
|
50
|
+
* RAG corpus) should call `buildAiSdkTools` + `applyCacheBreakpoints`
|
|
51
|
+
* separately.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildCachedAiSdkSetup<TCtx extends BaseToolContext>(args: {
|
|
54
|
+
build: BuildAiSdkToolsArgs<TCtx>;
|
|
55
|
+
cache: Omit<CacheableBlock<ToolSet>, 'static'> & {
|
|
56
|
+
static: Omit<CacheableBlock<ToolSet>['static'], 'tools'>;
|
|
57
|
+
};
|
|
58
|
+
}): CachedMessages<ToolSet>;
|
|
59
|
+
export { type ToolSet } from 'ai';
|
|
60
|
+
//# sourceMappingURL=ai-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-sdk.d.ts","sourceRoot":"","sources":["../../src/adapters/ai-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,IAAI,CAAA;AAEvC,OAAO,EAAyB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACrG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,EAAwB,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,mBAAmB,CAAC,IAAI,SAAS,eAAe;IAC7D,8EAA8E;IAC9E,QAAQ,EAAE,SAAS,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAA;IACjD,wDAAwD;IACxD,GAAG,EAAE,IAAI,CAAA;IACT,sDAAsD;IACtD,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,yDAAyD;IACzD,OAAO,CAAC,EAAE,oBAAoB,CAAA;IAC9B,yCAAyC;IACzC,KAAK,CAAC,EAAE,KAAK,CAAA;CAChB;AAED,wBAAgB,eAAe,CAAC,IAAI,SAAS,eAAe,EACxD,IAAI,EAAE,mBAAmB,CAAC,IAAI,CAAC,GAChC,OAAO,CAoET;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,SAAS,eAAe,EAAE,IAAI,EAAE;IACtE,KAAK,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAA;IAChC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,GAAG;QAC7C,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAA;KAC3D,CAAA;CACJ,GAAG,cAAc,CAAC,OAAO,CAAC,CAM1B;AAED,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,IAAI,CAAA"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { tool } from 'ai';
|
|
2
|
+
import { applyCacheBreakpoints } from '../cache-control.js';
|
|
3
|
+
import { SystemClock } from '../ports/clock.js';
|
|
4
|
+
import { captureToolException } from '../safe-tool.js';
|
|
5
|
+
export function buildAiSdkTools(args) {
|
|
6
|
+
const clock = args.clock ?? new SystemClock();
|
|
7
|
+
const rawTools = {};
|
|
8
|
+
for (const def of args.registry) {
|
|
9
|
+
rawTools[def.name] = tool({
|
|
10
|
+
description: def.description,
|
|
11
|
+
inputSchema: def.inputSchema,
|
|
12
|
+
execute: async (input) => {
|
|
13
|
+
const startedAt = clock.now();
|
|
14
|
+
try {
|
|
15
|
+
const envelope = await def.execute(input, args.ctx);
|
|
16
|
+
if (args.audit) {
|
|
17
|
+
void args.audit.recordToolCall({
|
|
18
|
+
toolName: def.name,
|
|
19
|
+
transport: args.ctx.transport,
|
|
20
|
+
actor: args.ctx.actor,
|
|
21
|
+
tenantId: args.ctx.tenantId,
|
|
22
|
+
principalId: args.ctx.principal?.id ?? null,
|
|
23
|
+
requestId: args.ctx.requestId ?? null,
|
|
24
|
+
input,
|
|
25
|
+
output: envelope.ok
|
|
26
|
+
? { ok: true }
|
|
27
|
+
: {
|
|
28
|
+
ok: false,
|
|
29
|
+
code: envelope.error.code,
|
|
30
|
+
message: envelope.error.message,
|
|
31
|
+
},
|
|
32
|
+
durationMs: clock.now().getTime() - startedAt.getTime(),
|
|
33
|
+
createdAt: startedAt,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return envelope;
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
40
|
+
if (args.audit) {
|
|
41
|
+
void args.audit.recordToolCall({
|
|
42
|
+
toolName: def.name,
|
|
43
|
+
transport: args.ctx.transport,
|
|
44
|
+
actor: args.ctx.actor,
|
|
45
|
+
tenantId: args.ctx.tenantId,
|
|
46
|
+
principalId: args.ctx.principal?.id ?? null,
|
|
47
|
+
requestId: args.ctx.requestId ?? null,
|
|
48
|
+
input,
|
|
49
|
+
output: { ok: false, code: 'tool_exception', message },
|
|
50
|
+
durationMs: clock.now().getTime() - startedAt.getTime(),
|
|
51
|
+
createdAt: startedAt,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
captureToolException(e, {
|
|
55
|
+
toolName: def.name,
|
|
56
|
+
transport: args.ctx.transport,
|
|
57
|
+
actor: args.ctx.actor,
|
|
58
|
+
tenantId: args.ctx.tenantId,
|
|
59
|
+
principalId: args.ctx.principal?.id ?? null,
|
|
60
|
+
requestId: args.ctx.requestId ?? null,
|
|
61
|
+
}, args.onError);
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return rawTools;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Convenience wrapper: builds the ToolSet AND applies the
|
|
71
|
+
* static-vs-dynamic system-prompt cache breakpoint in one call.
|
|
72
|
+
*
|
|
73
|
+
* Hosts that prefer to manage cache placement themselves (e.g. an
|
|
74
|
+
* adapter combining a multi-segment system prompt with a third-party
|
|
75
|
+
* RAG corpus) should call `buildAiSdkTools` + `applyCacheBreakpoints`
|
|
76
|
+
* separately.
|
|
77
|
+
*/
|
|
78
|
+
export function buildCachedAiSdkSetup(args) {
|
|
79
|
+
const tools = buildAiSdkTools(args.build);
|
|
80
|
+
return applyCacheBreakpoints({
|
|
81
|
+
static: { ...args.cache.static, tools },
|
|
82
|
+
dynamic: args.cache.dynamic,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export {} from 'ai';
|
|
86
|
+
//# sourceMappingURL=ai-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-sdk.js","sourceRoot":"","sources":["../../src/adapters/ai-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,IAAI,CAAA;AAEvC,OAAO,EAAE,qBAAqB,EAA4C,MAAM,qBAAqB,CAAA;AAIrG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,oBAAoB,EAA6B,MAAM,iBAAiB,CAAA;AAuCjF,MAAM,UAAU,eAAe,CAC3B,IAA+B;IAE/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,WAAW,EAAE,CAAA;IAC7C,MAAM,QAAQ,GAAY,EAAE,CAAA;IAE5B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YACtB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE;gBAC9B,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,EAAE,CAAA;gBAC7B,IAAI,CAAC;oBACD,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,KAAc,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;oBAC5D,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBACb,KAAK,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;4BAC3B,QAAQ,EAAE,GAAG,CAAC,IAAI;4BAClB,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS;4BAC7B,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK;4BACrB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ;4BAC3B,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,IAAI;4BAC3C,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI;4BACrC,KAAK;4BACL,MAAM,EAAE,QAAQ,CAAC,EAAE;gCACf,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE;gCACd,CAAC,CAAC;oCACI,EAAE,EAAE,KAAK;oCACT,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI;oCACzB,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO;iCAClC;4BACP,UAAU,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE;4BACvD,SAAS,EAAE,SAAS;yBACvB,CAAC,CAAA;oBACN,CAAC;oBACD,OAAO,QAAQ,CAAA;gBACnB,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACT,MAAM,OAAO,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;oBAC1D,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBACb,KAAK,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;4BAC3B,QAAQ,EAAE,GAAG,CAAC,IAAI;4BAClB,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS;4BAC7B,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK;4BACrB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ;4BAC3B,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,IAAI;4BAC3C,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI;4BACrC,KAAK;4BACL,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE;4BACtD,UAAU,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE;4BACvD,SAAS,EAAE,SAAS;yBACvB,CAAC,CAAA;oBACN,CAAC;oBACD,oBAAoB,CAChB,CAAC,EACD;wBACI,QAAQ,EAAE,GAAG,CAAC,IAAI;wBAClB,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS;wBAC7B,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK;wBACrB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ;wBAC3B,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,IAAI;wBAC3C,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI;qBACxC,EACD,IAAI,CAAC,OAAO,CACf,CAAA;oBACD,MAAM,CAAC,CAAA;gBACX,CAAC;YACL,CAAC;SACJ,CAAC,CAAA;IACN,CAAC;IAED,OAAO,QAAQ,CAAA;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAA+B,IAKnE;IACG,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzC,OAAO,qBAAqB,CAAC;QACzB,MAAM,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE;QACvC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO;KAC9B,CAAC,CAAA;AACN,CAAC;AAED,OAAO,EAAgB,MAAM,IAAI,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { BaseToolContext } from '../context.js';
|
|
3
|
+
import type { AuditStore } from '../ports/audit-store.js';
|
|
4
|
+
import { type Clock } from '../ports/clock.js';
|
|
5
|
+
import type { AnyAgentToolDefinition } from '../tool.js';
|
|
6
|
+
/**
|
|
7
|
+
* Register a registry of `AgentToolDefinition`s on an MCP server
|
|
8
|
+
* instance. The host owns the `McpServer` lifecycle (per-request in
|
|
9
|
+
* stateless HTTP mode, long-lived in stdio mode) — this adapter only
|
|
10
|
+
* fills the tool list.
|
|
11
|
+
*
|
|
12
|
+
* Filtering (surface, actor scope, OAuth scopes, isAvailable) happens
|
|
13
|
+
* BEFORE this call. Same pattern as `buildAiSdkTools`.
|
|
14
|
+
*
|
|
15
|
+
* On execute:
|
|
16
|
+
* - Success or envelope-error → JSON-stringified envelope returned
|
|
17
|
+
* as `text` content. `isError` mirrors `envelope.ok === false`.
|
|
18
|
+
* - Thrown exception → audit + the host's `onError` hook are
|
|
19
|
+
* called, then a JSON `{ ok: false, error }` payload is returned
|
|
20
|
+
* with `isError: true`. The throw is NOT propagated (MCP SDK
|
|
21
|
+
* expects the handler to resolve with an error result, not
|
|
22
|
+
* throw — different contract from AI SDK).
|
|
23
|
+
*/
|
|
24
|
+
export interface RegisterMcpToolsArgs<TCtx extends BaseToolContext> {
|
|
25
|
+
server: McpServer;
|
|
26
|
+
registry: readonly AnyAgentToolDefinition<TCtx>[];
|
|
27
|
+
ctx: TCtx;
|
|
28
|
+
audit?: AuditStore;
|
|
29
|
+
clock?: Clock;
|
|
30
|
+
/**
|
|
31
|
+
* Observability hook for thrown exceptions (Sentry, OTel, etc.).
|
|
32
|
+
* Receives the raw error and a tag bag.
|
|
33
|
+
*/
|
|
34
|
+
onError?: (error: unknown, tags: Record<string, unknown>) => void;
|
|
35
|
+
}
|
|
36
|
+
export declare function registerMcpTools<TCtx extends BaseToolContext>(args: RegisterMcpToolsArgs<TCtx>): void;
|
|
37
|
+
//# sourceMappingURL=mcp-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/adapters/mcp-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB,CAAC,IAAI,SAAS,eAAe;IAC9D,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,SAAS,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAA;IACjD,GAAG,EAAE,IAAI,CAAA;IACT,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,eAAe,EACzD,IAAI,EAAE,oBAAoB,CAAC,IAAI,CAAC,GACjC,IAAI,CAgFN"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { SystemClock } from '../ports/clock.js';
|
|
2
|
+
export function registerMcpTools(args) {
|
|
3
|
+
const clock = args.clock ?? new SystemClock();
|
|
4
|
+
for (const def of args.registry) {
|
|
5
|
+
args.server.registerTool(def.name, {
|
|
6
|
+
description: def.description,
|
|
7
|
+
inputSchema: zodToRawShape(def.inputSchema),
|
|
8
|
+
}, async (input) => {
|
|
9
|
+
const startedAt = clock.now();
|
|
10
|
+
try {
|
|
11
|
+
const envelope = await def.execute(input, args.ctx);
|
|
12
|
+
if (args.audit) {
|
|
13
|
+
void args.audit.recordToolCall({
|
|
14
|
+
toolName: def.name,
|
|
15
|
+
transport: args.ctx.transport,
|
|
16
|
+
actor: args.ctx.actor,
|
|
17
|
+
tenantId: args.ctx.tenantId,
|
|
18
|
+
principalId: args.ctx.principal?.id ?? null,
|
|
19
|
+
requestId: args.ctx.requestId ?? null,
|
|
20
|
+
input,
|
|
21
|
+
output: envelope.ok
|
|
22
|
+
? { ok: true }
|
|
23
|
+
: {
|
|
24
|
+
ok: false,
|
|
25
|
+
code: envelope.error.code,
|
|
26
|
+
message: envelope.error.message,
|
|
27
|
+
},
|
|
28
|
+
durationMs: clock.now().getTime() - startedAt.getTime(),
|
|
29
|
+
createdAt: startedAt,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{ type: 'text', text: JSON.stringify(envelope, null, 2) },
|
|
35
|
+
],
|
|
36
|
+
isError: !envelope.ok,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
41
|
+
if (args.audit) {
|
|
42
|
+
void args.audit.recordToolCall({
|
|
43
|
+
toolName: def.name,
|
|
44
|
+
transport: args.ctx.transport,
|
|
45
|
+
actor: args.ctx.actor,
|
|
46
|
+
tenantId: args.ctx.tenantId,
|
|
47
|
+
principalId: args.ctx.principal?.id ?? null,
|
|
48
|
+
requestId: args.ctx.requestId ?? null,
|
|
49
|
+
input,
|
|
50
|
+
output: { ok: false, code: 'tool_exception', message },
|
|
51
|
+
durationMs: clock.now().getTime() - startedAt.getTime(),
|
|
52
|
+
createdAt: startedAt,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (args.onError) {
|
|
56
|
+
try {
|
|
57
|
+
args.onError(e, {
|
|
58
|
+
toolName: def.name,
|
|
59
|
+
transport: args.ctx.transport,
|
|
60
|
+
actor: args.ctx.actor,
|
|
61
|
+
tenantId: args.ctx.tenantId,
|
|
62
|
+
principalId: args.ctx.principal?.id ?? null,
|
|
63
|
+
requestId: args.ctx.requestId ?? null,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Observability must never crash the tool result.
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{ type: 'text', text: JSON.stringify({ ok: false, error: message }) },
|
|
73
|
+
],
|
|
74
|
+
isError: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The MCP SDK's `registerTool` wants the input schema as a raw Zod
|
|
82
|
+
* shape (`{ key: z.foo() }`), not the wrapped `z.object`. We crack
|
|
83
|
+
* open the internal `_def.shape()` getter to extract it.
|
|
84
|
+
*
|
|
85
|
+
* Unknown / non-object schemas fall through to an empty shape — the
|
|
86
|
+
* MCP server will accept any input for tools that don't declare one,
|
|
87
|
+
* matching the behaviour of barbeiro's original adapter.
|
|
88
|
+
*/
|
|
89
|
+
function zodToRawShape(schema) {
|
|
90
|
+
const s = schema;
|
|
91
|
+
if (s && typeof s._def?.shape === 'function') {
|
|
92
|
+
return s._def.shape();
|
|
93
|
+
}
|
|
94
|
+
if (s && typeof s.shape === 'object' && s.shape !== null) {
|
|
95
|
+
return s.shape;
|
|
96
|
+
}
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../src/adapters/mcp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic prompt-cache breakpoint helper.
|
|
3
|
+
*
|
|
4
|
+
* The model provider supports `cacheControl: { type: 'ephemeral' }`
|
|
5
|
+
* markers — content BEFORE the breakpoint is hashed into the cache key,
|
|
6
|
+
* content AFTER is rendered as a separate uncached segment. 5-minute TTL.
|
|
7
|
+
* Cold call pays cache-write (≈1.25× input rate); hot call pays
|
|
8
|
+
* cache-read (≈10% input rate).
|
|
9
|
+
*
|
|
10
|
+
* The TS-typed boundary between `static` and `dynamic` is the discipline.
|
|
11
|
+
* A developer physically can't put `business.name` into the cached block
|
|
12
|
+
* because the `static` slot only accepts the documented stable fields.
|
|
13
|
+
* Cross-tenant cache reuse is deliberate: tenant-invariant static block
|
|
14
|
+
* means many tenants share the same cache entry; per-tenant data lives
|
|
15
|
+
* in the dynamic segment that the cache key ignores.
|
|
16
|
+
*
|
|
17
|
+
* This helper deals only with the system-prompt + tools shape. The
|
|
18
|
+
* caller passes the result straight to `streamText` / `generateText`
|
|
19
|
+
* (AI SDK) as `system: [...]` and `tools: {...}`.
|
|
20
|
+
*
|
|
21
|
+
* The tools map is kept opaque (`Record<string, unknown>`) so this
|
|
22
|
+
* module has no `ai` dependency; the AI-SDK adapter feeds a properly
|
|
23
|
+
* typed `ToolSet` in, and TypeScript flows the type through.
|
|
24
|
+
*/
|
|
25
|
+
export interface CacheableBlock<TTools extends Record<string, unknown>> {
|
|
26
|
+
/**
|
|
27
|
+
* Tenant-stable content — hashed for the cache key. MUST NOT
|
|
28
|
+
* contain user/business names or any per-tenant interpolated
|
|
29
|
+
* strings. Integer ids inside tool parameter descriptions are
|
|
30
|
+
* fine; rendered-into-prose strings are not.
|
|
31
|
+
*/
|
|
32
|
+
static: {
|
|
33
|
+
/** Fixed introduction / persona block (surface-level instructions). */
|
|
34
|
+
intro: string;
|
|
35
|
+
/** Help corpus or business catalog static reference block. */
|
|
36
|
+
corpus: string;
|
|
37
|
+
/** Tool registry for this surface. The last tool receives the cacheControl marker. */
|
|
38
|
+
tools: TTools;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Tenant-specific content. Rendered as a separate uncached system
|
|
42
|
+
* segment so it never influences the cache key.
|
|
43
|
+
*/
|
|
44
|
+
dynamic: {
|
|
45
|
+
/** Tenant id as an integer-safe shape — never interpolated into cached strings. */
|
|
46
|
+
tenant: {
|
|
47
|
+
id: string;
|
|
48
|
+
timezone: string;
|
|
49
|
+
};
|
|
50
|
+
principal?: {
|
|
51
|
+
id: string;
|
|
52
|
+
};
|
|
53
|
+
/** ISO timestamp for "now" context — varies per turn; must NOT be cached. */
|
|
54
|
+
nowIso: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface CachedMessages<TTools extends Record<string, unknown>> {
|
|
58
|
+
/**
|
|
59
|
+
* Two-element array:
|
|
60
|
+
* [0] static system message — carries the Anthropic ephemeral cacheControl marker.
|
|
61
|
+
* [1] dynamic system message — no marker; rendered after the breakpoint.
|
|
62
|
+
*/
|
|
63
|
+
system: Array<{
|
|
64
|
+
role: 'system';
|
|
65
|
+
content: string;
|
|
66
|
+
providerOptions?: {
|
|
67
|
+
anthropic?: {
|
|
68
|
+
cacheControl?: {
|
|
69
|
+
type: 'ephemeral';
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}>;
|
|
74
|
+
/** Tool registry — the last tool carries the cacheControl marker. */
|
|
75
|
+
tools: TTools;
|
|
76
|
+
}
|
|
77
|
+
export declare function applyCacheBreakpoints<TTools extends Record<string, unknown>>(input: CacheableBlock<TTools>): CachedMessages<TTools>;
|
|
78
|
+
//# sourceMappingURL=cache-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-control.d.ts","sourceRoot":"","sources":["../src/cache-control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE;;;;;OAKG;IACH,MAAM,EAAE;QACJ,uEAAuE;QACvE,KAAK,EAAE,MAAM,CAAA;QACb,8DAA8D;QAC9D,MAAM,EAAE,MAAM,CAAA;QACd,sFAAsF;QACtF,KAAK,EAAE,MAAM,CAAA;KAChB,CAAA;IACD;;;OAGG;IACH,OAAO,EAAE;QACL,mFAAmF;QACnF,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;QACxC,SAAS,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;QAC1B,6EAA6E;QAC7E,MAAM,EAAE,MAAM,CAAA;KACjB,CAAA;CACJ;AAED,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE;;;;OAIG;IACH,MAAM,EAAE,KAAK,CAAC;QACV,IAAI,EAAE,QAAQ,CAAA;QACd,OAAO,EAAE,MAAM,CAAA;QACf,eAAe,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE;gBAAE,YAAY,CAAC,EAAE;oBAAE,IAAI,EAAE,WAAW,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAA;KAC7E,CAAC,CAAA;IACF,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,GAC9B,cAAc,CAAC,MAAM,CAAC,CA4DxB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function applyCacheBreakpoints(input) {
|
|
2
|
+
const { static: st, dynamic: dyn } = input;
|
|
3
|
+
// ── Static system message (cached) ──────────────────────────────────
|
|
4
|
+
const staticContent = st.corpus ? `${st.intro}\n\n${st.corpus}` : st.intro;
|
|
5
|
+
const staticMessage = {
|
|
6
|
+
role: 'system',
|
|
7
|
+
content: staticContent,
|
|
8
|
+
providerOptions: {
|
|
9
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
// ── Dynamic system message (uncached) ───────────────────────────────
|
|
13
|
+
const dynamicLines = [
|
|
14
|
+
`Tenant context: tenant_id=${dyn.tenant.id}, timezone=${dyn.tenant.timezone}`,
|
|
15
|
+
];
|
|
16
|
+
if (dyn.principal?.id !== undefined) {
|
|
17
|
+
dynamicLines.push(`Principal context: principal_id=${dyn.principal.id}`);
|
|
18
|
+
}
|
|
19
|
+
dynamicLines.push(`Current time (ISO): ${dyn.nowIso}`);
|
|
20
|
+
const dynamicMessage = {
|
|
21
|
+
role: 'system',
|
|
22
|
+
content: dynamicLines.join('\n'),
|
|
23
|
+
};
|
|
24
|
+
// ── Tools (cached) ──────────────────────────────────────────────────
|
|
25
|
+
// Clone the registry to avoid mutating the caller's object. Apply the
|
|
26
|
+
// cacheControl marker to the last tool in iteration order. Strip any
|
|
27
|
+
// pre-existing providerOptions on non-last tools so a previously
|
|
28
|
+
// marked registry coming back through here doesn't double-mark.
|
|
29
|
+
const toolKeys = Object.keys(st.tools);
|
|
30
|
+
const markedTools = {};
|
|
31
|
+
for (let i = 0; i < toolKeys.length; i++) {
|
|
32
|
+
const key = toolKeys[i];
|
|
33
|
+
if (key === undefined)
|
|
34
|
+
continue;
|
|
35
|
+
const original = st.tools[key];
|
|
36
|
+
if (original === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
const isLast = i === toolKeys.length - 1;
|
|
39
|
+
if (isLast) {
|
|
40
|
+
markedTools[key] = {
|
|
41
|
+
...original,
|
|
42
|
+
providerOptions: {
|
|
43
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const { providerOptions: _drop, ...rest } = original;
|
|
49
|
+
markedTools[key] = rest;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
system: [staticMessage, dynamicMessage],
|
|
54
|
+
tools: markedTools,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=cache-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-control.js","sourceRoot":"","sources":["../src/cache-control.ts"],"names":[],"mappings":"AAmEA,MAAM,UAAU,qBAAqB,CACjC,KAA6B;IAE7B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;IAE1C,uEAAuE;IACvE,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAA;IAC1E,MAAM,aAAa,GAAG;QAClB,IAAI,EAAE,QAAiB;QACvB,OAAO,EAAE,aAAa;QACtB,eAAe,EAAE;YACb,SAAS,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,WAAoB,EAAE,EAAE;SAC9D;KACJ,CAAA;IAED,uEAAuE;IACvE,MAAM,YAAY,GAAa;QAC3B,6BAA6B,GAAG,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE;KAChF,CAAA;IACD,IAAI,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,SAAS,EAAE,CAAC;QAClC,YAAY,CAAC,IAAI,CAAC,mCAAmC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAA;IAC5E,CAAC;IACD,YAAY,CAAC,IAAI,CAAC,uBAAuB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;IACtD,MAAM,cAAc,GAAG;QACnB,IAAI,EAAE,QAAiB;QACvB,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;KACnC,CAAA;IAED,uEAAuE;IACvE,sEAAsE;IACtE,qEAAqE;IACrE,iEAAiE;IACjE,gEAAgE;IAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IACtC,MAAM,WAAW,GAAG,EAA6B,CAAA;IAEjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC/B,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,QAAQ,KAAK,SAAS;YAAE,SAAQ;QACpC,MAAM,MAAM,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;QAExC,IAAI,MAAM,EAAE,CAAC;YACT,WAAW,CAAC,GAAG,CAAC,GAAG;gBACf,GAAI,QAAmB;gBACvB,eAAe,EAAE;oBACb,SAAS,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,WAAoB,EAAE,EAAE;iBAC9D;aACJ,CAAA;QACL,CAAC;aAAM,CAAC;YACJ,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,QAEjB,CAAA;YAC3B,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;QAC3B,CAAC;IACL,CAAC;IAED,OAAO;QACH,MAAM,EAAE,CAAC,aAAa,EAAE,cAAc,CAAC;QACvC,KAAK,EAAE,WAAqB;KAC/B,CAAA;AACL,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-request context handed to every tool. Built by the host product
|
|
3
|
+
* at the top of the chat-turn handler (route, worker, MCP request) from
|
|
4
|
+
* the authenticated principal + the surface invoking the tool.
|
|
5
|
+
*
|
|
6
|
+
* The base shape is intentionally minimal — everything that is universal
|
|
7
|
+
* to any tool-calling agent system. Hosts extend via intersection at the
|
|
8
|
+
* `defineAgentTool<TInput, TOutput, TCtx>` call site:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* type BarbeiroCtx = BaseToolContext & {
|
|
12
|
+
* businessSlug?: string
|
|
13
|
+
* guestPhone?: string
|
|
14
|
+
* role: HelpRole | null
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* defineAgentTool<Input, Output, BarbeiroCtx>({ ... })
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Per-tool generic param (not TS module augmentation) is the chosen
|
|
21
|
+
* extension mechanism — keeps the augmentation scoped to one tool
|
|
22
|
+
* instead of polluting a shared global interface, so siblings in a
|
|
23
|
+
* future monorepo can't trip over each other's context shapes.
|
|
24
|
+
*/
|
|
25
|
+
export interface BaseToolContext {
|
|
26
|
+
/**
|
|
27
|
+
* Opaque tenant scope. The host product owns the meaning
|
|
28
|
+
* (`businessId.toString()`, workspace UUID, org slug, etc.) — kernel
|
|
29
|
+
* never parses it. Used for audit attribution, quota keys, memory
|
|
30
|
+
* scoping, and telemetry tagging.
|
|
31
|
+
*/
|
|
32
|
+
tenantId: string;
|
|
33
|
+
/**
|
|
34
|
+
* Authenticated principal, or null for anonymous transports
|
|
35
|
+
* (public widget, voice). `id` is opaque; `kind` is a host-defined
|
|
36
|
+
* string that disambiguates principal populations
|
|
37
|
+
* (`'user'`, `'guest'`, `'service-account'`, `'mcp-client'`, ...).
|
|
38
|
+
*/
|
|
39
|
+
principal: {
|
|
40
|
+
id: string;
|
|
41
|
+
kind: string;
|
|
42
|
+
} | null;
|
|
43
|
+
/**
|
|
44
|
+
* Who authorised the call. Logged for governance + abuse detection.
|
|
45
|
+
* String-typed; the host owns the enum.
|
|
46
|
+
*/
|
|
47
|
+
actor: string;
|
|
48
|
+
/**
|
|
49
|
+
* Which surface invoked the call (`'chat'`, `'guest-chat'`,
|
|
50
|
+
* `'whatsapp'`, `'mcp'`, ...). Adapters filter the registry by
|
|
51
|
+
* `def.transports.includes(transport)`. String-typed; the host owns
|
|
52
|
+
* the vocabulary.
|
|
53
|
+
*/
|
|
54
|
+
transport: string;
|
|
55
|
+
/** IETF BCP-47 locale tag, e.g. `'pt-BR'`. */
|
|
56
|
+
locale: string;
|
|
57
|
+
/** IANA timezone, e.g. `'America/Sao_Paulo'`. */
|
|
58
|
+
timezone: string;
|
|
59
|
+
/** Trace id for cross-system correlation (matches HTTP `x-request-id`). */
|
|
60
|
+
requestId: string;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,eAAe;IAC5B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAA;IAEhB;;;;;OAKG;IACH,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAE9C;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IAEb;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAA;IAEd,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAA;IAEhB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAA;CACpB"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uniform return shape for every Maestro tool. Wrapping success and failure
|
|
3
|
+
* in the same envelope lets the model recover gracefully ("sorry, that slot
|
|
4
|
+
* was just taken") instead of crashing the whole turn, and lets every adapter
|
|
5
|
+
* (AI SDK, MCP, transports added later) render the result with the same code
|
|
6
|
+
* path.
|
|
7
|
+
*/
|
|
8
|
+
export interface ToolMeta {
|
|
9
|
+
/**
|
|
10
|
+
* Set when the host UI already renders a rich card for this tool result.
|
|
11
|
+
* Chat transports with a UI surface read this and tell the model to skip
|
|
12
|
+
* restating the data — preventing the duplicate "card + paragraph saying
|
|
13
|
+
* the same thing" bubble. Advisory only on transports without UI cards
|
|
14
|
+
* (whatsapp, mcp, voice) — those still get the full LLM restatement.
|
|
15
|
+
*/
|
|
16
|
+
uiRendered?: string;
|
|
17
|
+
}
|
|
18
|
+
export type ToolEnvelope<T> = {
|
|
19
|
+
ok: true;
|
|
20
|
+
data: T;
|
|
21
|
+
meta?: ToolMeta;
|
|
22
|
+
} | {
|
|
23
|
+
ok: false;
|
|
24
|
+
error: {
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export declare const ok: <T>(data: T, meta?: ToolMeta) => ToolEnvelope<T>;
|
|
30
|
+
export declare const err: (code: string, message: string) => ToolEnvelope<never>;
|
|
31
|
+
export declare function isOk<T>(envelope: ToolEnvelope<T>): envelope is {
|
|
32
|
+
ok: true;
|
|
33
|
+
data: T;
|
|
34
|
+
meta?: ToolMeta;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAA;AAE7D,eAAO,MAAM,EAAE,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,QAAQ,KAAG,YAAY,CAAC,CAAC,CACP,CAAA;AAExD,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,YAAY,CAAC,KAAK,CAGpE,CAAA;AAEF,wBAAgB,IAAI,CAAC,CAAC,EAClB,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GAC1B,QAAQ,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,CAEpD"}
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const ok = (data, meta) => meta ? { ok: true, data, meta } : { ok: true, data };
|
|
2
|
+
export const err = (code, message) => ({
|
|
3
|
+
ok: false,
|
|
4
|
+
error: { code, message },
|
|
5
|
+
});
|
|
6
|
+
export function isOk(envelope) {
|
|
7
|
+
return envelope.ok === true;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,EAAE,GAAG,CAAI,IAAO,EAAE,IAAe,EAAmB,EAAE,CAC/D,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAExD,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,OAAe,EAAuB,EAAE,CAAC,CAAC;IACxE,EAAE,EAAE,KAAK;IACT,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;CAC3B,CAAC,CAAA;AAEF,MAAM,UAAU,IAAI,CAChB,QAAyB;IAEzB,OAAO,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAA;AAC/B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { BaseToolContext } from './context.js';
|
|
2
|
+
export { err, isOk, ok, type ToolEnvelope, type ToolMeta } from './envelope.js';
|
|
3
|
+
export { type AgentToolDefinition, type AnyAgentToolDefinition, defineAgentTool, type ToolCostBand, type ToolKind, } from './tool.js';
|
|
4
|
+
export { applyCacheBreakpoints, type CacheableBlock, type CachedMessages, } from './cache-control.js';
|
|
5
|
+
export { captureToolException, type ToolExceptionHandler, type ToolExceptionTags, } from './safe-tool.js';
|
|
6
|
+
export * from './ports/index.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|