@tangle-network/agent-app 0.12.0 → 0.13.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.
Files changed (40) hide show
  1. package/dist/DesignCanvas-3JEEIT6Y.js +10 -0
  2. package/dist/DesignCanvas-3JEEIT6Y.js.map +1 -0
  3. package/dist/DesignCanvasEditor-37LPJIIR.js +9 -0
  4. package/dist/DesignCanvasEditor-37LPJIIR.js.map +1 -0
  5. package/dist/chunk-2Q73HGDI.js +1743 -0
  6. package/dist/chunk-2Q73HGDI.js.map +1 -0
  7. package/dist/{chunk-3WAJWYKD.js → chunk-6UOE5CTA.js} +9 -92
  8. package/dist/{chunk-3WAJWYKD.js.map → chunk-6UOE5CTA.js.map} +1 -1
  9. package/dist/chunk-7QCIYDGC.js +1119 -0
  10. package/dist/chunk-7QCIYDGC.js.map +1 -0
  11. package/dist/chunk-A76ZHWNF.js +194 -0
  12. package/dist/chunk-A76ZHWNF.js.map +1 -0
  13. package/dist/{chunk-CF5DZELC.js → chunk-ABGSFUJQ.js} +2 -2
  14. package/dist/chunk-F5KTWRO7.js +2276 -0
  15. package/dist/chunk-F5KTWRO7.js.map +1 -0
  16. package/dist/chunk-JZAJE3JL.js +990 -0
  17. package/dist/chunk-JZAJE3JL.js.map +1 -0
  18. package/dist/design-canvas/drizzle.d.ts +569 -0
  19. package/dist/design-canvas/drizzle.js +183 -0
  20. package/dist/design-canvas/drizzle.js.map +1 -0
  21. package/dist/design-canvas/index.d.ts +261 -0
  22. package/dist/design-canvas/index.js +96 -0
  23. package/dist/design-canvas/index.js.map +1 -0
  24. package/dist/design-canvas-react/index.d.ts +916 -0
  25. package/dist/design-canvas-react/index.js +423 -0
  26. package/dist/design-canvas-react/index.js.map +1 -0
  27. package/dist/export-presets-Dl5Aa5xj.d.ts +284 -0
  28. package/dist/index.d.ts +5 -0
  29. package/dist/index.js +99 -3
  30. package/dist/mcp-rpc-DLw_r9PQ.d.ts +55 -0
  31. package/dist/model-BHLN208Z.d.ts +183 -0
  32. package/dist/sequences/index.d.ts +4 -0
  33. package/dist/sequences/index.js +2 -2
  34. package/dist/store-CUStmtdH.d.ts +64 -0
  35. package/dist/tools/index.d.ts +1 -0
  36. package/dist/tools/index.js +6 -2
  37. package/package.json +28 -3
  38. package/dist/chunk-IJZJWKUK.js +0 -77
  39. package/dist/chunk-IJZJWKUK.js.map +0 -1
  40. /package/dist/{chunk-CF5DZELC.js.map → chunk-ABGSFUJQ.js.map} +0 -0
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Design-canvas scene model — the product-agnostic document behind the visual
3
+ * asset editor. A document is an ordered list of pages; a page is an ordered
4
+ * list of elements (index order IS z-order, bottom→top); every element is a
5
+ * typed node with a closed attribute vocabulary. The whole document
6
+ * serializes as one JSON value and persists atomically with a revision
7
+ * counter (see ./store) — every canvas action is a durable operation against
8
+ * this schema, which is what makes the canvas automatable: agents and
9
+ * external data sources mutate the same document the editor renders.
10
+ *
11
+ * Units are CSS pixels throughout; `settings.dpi` carries the print
12
+ * conversion factor for bleed/trim-aware exports. Angles are degrees.
13
+ * Nothing here touches Konva, React, or a database.
14
+ */
15
+ declare const SCENE_SCHEMA_VERSION = 1;
16
+ interface SceneDocument {
17
+ schemaVersion: typeof SCENE_SCHEMA_VERSION;
18
+ title: string;
19
+ pages: ScenePage[];
20
+ settings: SceneSettings;
21
+ metadata: Record<string, unknown>;
22
+ }
23
+ interface SceneSettings {
24
+ /** Print conversion factor for mm↔px math at export; 96 = CSS default. */
25
+ dpi: number;
26
+ }
27
+ /** Per-side bleed extents in px, drawn OUTSIDE the page bounds. Trim = the
28
+ * page rect itself; exports may include or exclude the bleed area. */
29
+ interface PageBleed {
30
+ top: number;
31
+ right: number;
32
+ bottom: number;
33
+ left: number;
34
+ }
35
+ /** Saved ruler guides, in page coordinates. */
36
+ interface PageGuides {
37
+ vertical: number[];
38
+ horizontal: number[];
39
+ }
40
+ interface ScenePage {
41
+ id: string;
42
+ name: string;
43
+ width: number;
44
+ height: number;
45
+ /** Page background fill (color string); elements paint over it. */
46
+ background: string;
47
+ bleed: PageBleed | null;
48
+ guides: PageGuides;
49
+ elements: SceneElement[];
50
+ }
51
+ /** Attributes every element carries. `x`/`y` are the element's top-left in
52
+ * page coordinates (for `group`, children are relative to the group origin).
53
+ * `slot` names a template binding point — `apply_data` targets it. */
54
+ interface SceneElementBase {
55
+ id: string;
56
+ name: string;
57
+ x: number;
58
+ y: number;
59
+ /** Degrees, clockwise, about the element's top-left origin (Konva default). */
60
+ rotation: number;
61
+ /** 0..1 */
62
+ opacity: number;
63
+ locked: boolean;
64
+ visible: boolean;
65
+ slot?: string;
66
+ }
67
+ interface RectElement extends SceneElementBase {
68
+ kind: 'rect';
69
+ width: number;
70
+ height: number;
71
+ fill: string;
72
+ stroke?: string;
73
+ strokeWidth?: number;
74
+ cornerRadius?: number;
75
+ }
76
+ interface EllipseElement extends SceneElementBase {
77
+ kind: 'ellipse';
78
+ width: number;
79
+ height: number;
80
+ fill: string;
81
+ stroke?: string;
82
+ strokeWidth?: number;
83
+ }
84
+ interface LineElement extends SceneElementBase {
85
+ kind: 'line';
86
+ /** Flat [x0, y0, x1, y1, ...] relative to (x, y); ≥ 2 points. */
87
+ points: number[];
88
+ stroke: string;
89
+ strokeWidth: number;
90
+ dash?: number[];
91
+ }
92
+ interface TextElement extends SceneElementBase {
93
+ kind: 'text';
94
+ text: string;
95
+ /** Wrap width; height derives from content. */
96
+ width: number;
97
+ fontFamily: string;
98
+ fontSize: number;
99
+ fontStyle: 'normal' | 'bold' | 'italic' | 'bold italic';
100
+ fill: string;
101
+ align: 'left' | 'center' | 'right';
102
+ lineHeight: number;
103
+ letterSpacing: number;
104
+ }
105
+ interface ImageElement extends SceneElementBase {
106
+ kind: 'image';
107
+ width: number;
108
+ height: number;
109
+ /** http(s) or rooted /api/ path — same boundary rule as sequences media. */
110
+ src: string;
111
+ /** How the source maps into the frame; 'fill' stretches, 'cover' crops. */
112
+ fit: 'fill' | 'cover' | 'contain';
113
+ }
114
+ /** Video placed on a canvas renders and exports as its poster frame — motion
115
+ * belongs to the sequences surface; this keeps video assets placeable in
116
+ * static layouts (e.g. a thumbnail mock) without a playback engine. */
117
+ interface VideoElement extends SceneElementBase {
118
+ kind: 'video';
119
+ width: number;
120
+ height: number;
121
+ src: string;
122
+ posterSrc?: string;
123
+ }
124
+ interface GroupElement extends SceneElementBase {
125
+ kind: 'group';
126
+ children: SceneElement[];
127
+ }
128
+ type SceneElement = RectElement | EllipseElement | LineElement | TextElement | ImageElement | VideoElement | GroupElement;
129
+ type SceneElementKind = SceneElement['kind'];
130
+ declare const SCENE_ELEMENT_KINDS: readonly SceneElementKind[];
131
+ interface Bounds {
132
+ x: number;
133
+ y: number;
134
+ width: number;
135
+ height: number;
136
+ }
137
+ /** Unrotated local extent of an element (line/group derive from content). */
138
+ declare function elementExtent(element: SceneElement): {
139
+ width: number;
140
+ height: number;
141
+ };
142
+ declare function estimateTextHeight(element: Pick<TextElement, 'text' | 'fontSize' | 'lineHeight'>): number;
143
+ /** Axis-aligned bounding box in the parent's coordinate space, accounting for
144
+ * rotation about the element's top-left origin. */
145
+ declare function elementAabb(element: SceneElement): Bounds;
146
+ declare function boundsIntersect(a: Bounds, b: Bounds): boolean;
147
+ declare function requirePage(document: SceneDocument, pageId: string): ScenePage;
148
+ /** Depth-first search across a page including group children. Returns the
149
+ * element and the array that owns it (page.elements or a group's children),
150
+ * so callers can splice in place. */
151
+ declare function findElement(page: ScenePage, elementId: string): {
152
+ element: SceneElement;
153
+ owner: SceneElement[];
154
+ index: number;
155
+ } | null;
156
+ declare function requireElement(page: ScenePage, elementId: string): {
157
+ element: SceneElement;
158
+ owner: SceneElement[];
159
+ index: number;
160
+ };
161
+ /** All slot names declared across the document — the template's fillable
162
+ * surface. Duplicate slot names are a validation error (see ./validate). */
163
+ declare function collectSlots(document: SceneDocument): Map<string, {
164
+ pageId: string;
165
+ elementId: string;
166
+ kind: SceneElementKind;
167
+ }>;
168
+ interface NewPageOptions {
169
+ name?: string;
170
+ width?: number;
171
+ height?: number;
172
+ background?: string;
173
+ }
174
+ declare function createEmptyDocument(title: string, page?: NewPageOptions): SceneDocument;
175
+ declare function createPage(options: NewPageOptions, id: string): ScenePage;
176
+ declare function assertPositiveFinite(value: number, label: string): void;
177
+ declare function assertFinite(value: number, label: string): void;
178
+ declare function assertColor(value: string, label: string): void;
179
+ /** Media boundary rule shared with sequences: remote http(s) or a rooted
180
+ * /api/ path — never sandbox-local files or data: blobs. */
181
+ declare function assertSceneMediaSrc(value: string, label: string): void;
182
+
183
+ export { type Bounds as B, type EllipseElement as E, type GroupElement as G, type ImageElement as I, type LineElement as L, type NewPageOptions as N, type PageBleed as P, type RectElement as R, SCENE_ELEMENT_KINDS as S, type TextElement as T, type VideoElement as V, type PageGuides as a, SCENE_SCHEMA_VERSION as b, type SceneDocument as c, type SceneElement as d, type SceneElementBase as e, type SceneElementKind as f, type ScenePage as g, type SceneSettings as h, assertColor as i, assertFinite as j, assertPositiveFinite as k, assertSceneMediaSrc as l, boundsIntersect as m, collectSlots as n, createEmptyDocument as o, createPage as p, elementAabb as q, elementExtent as r, estimateTextHeight as s, findElement as t, requireElement as u, requirePage as v };
@@ -276,6 +276,10 @@ declare function findSequenceMcpTool(name: string): SequenceMcpToolDefinition |
276
276
  * execution failures (argument shape, validation, store throws) become
277
277
  * `isError` tool results carrying the thrown message verbatim so the model can
278
278
  * read WHY and retry; only protocol-level misuse becomes a JSON-RPC error.
279
+ *
280
+ * The envelope (JSON-RPC framing, initialize/ping/tools/list/tools/call,
281
+ * notification 202, -32601) lives in {@link createMcpToolHandler}; this module
282
+ * is a thin adapter wiring the sequences tool list + playhead env.
279
283
  */
280
284
 
281
285
  /** Newest first. The handler echoes the client's requested version when
@@ -41,7 +41,7 @@ import {
41
41
  validateSetClipText,
42
42
  validateSplitClip,
43
43
  validateTrimClip
44
- } from "../chunk-3WAJWYKD.js";
44
+ } from "../chunk-6UOE5CTA.js";
45
45
  import {
46
46
  MIN_SEQUENCE_CLIP_FRAMES,
47
47
  assertClipFitsSequence,
@@ -55,7 +55,7 @@ import {
55
55
  snapshotFrame,
56
56
  trackIntervals
57
57
  } from "../chunk-ZYBWGSAZ.js";
58
- import "../chunk-IJZJWKUK.js";
58
+ import "../chunk-A76ZHWNF.js";
59
59
  export {
60
60
  DEFAULT_SEQUENCES_MCP_DESCRIPTION,
61
61
  MAX_CAPTION_BATCH,
@@ -0,0 +1,64 @@
1
+ import { c as SceneDocument } from './model-BHLN208Z.js';
2
+
3
+ /**
4
+ * Storage contract for design-canvas documents. Unlike sequences (row per
5
+ * clip), a scene document persists as ONE JSON value with a monotonic
6
+ * revision counter — saves are atomic and optimistic: a save carrying a
7
+ * stale `expectedRev` throws, the caller refetches and replays. That keeps
8
+ * concurrent editors (human + agent in the same document) from silently
9
+ * clobbering each other without needing row-level merge machinery.
10
+ *
11
+ * The product constructs one store per (workspace, document, actor) request
12
+ * scope — RBAC runs BEFORE construction; the store never re-checks identity.
13
+ * Every method throws on failure; the MCP dispatcher converts throws into
14
+ * structured tool errors.
15
+ */
16
+
17
+ interface SceneDocumentRecord {
18
+ document: SceneDocument;
19
+ /** Monotonic revision; increments on every successful save. */
20
+ rev: number;
21
+ }
22
+ interface SceneDecision {
23
+ id: string;
24
+ kind: 'human_edit' | 'agent_edit' | 'agent_proposal' | 'export' | 'note';
25
+ instruction: string;
26
+ reasoningSummary: string | null;
27
+ metadata: Record<string, unknown>;
28
+ createdAt: Date;
29
+ }
30
+ interface NewSceneDecision {
31
+ kind: SceneDecision['kind'];
32
+ instruction: string;
33
+ reasoningSummary?: string | null;
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+ type SceneExportFormat = 'png' | 'jpeg' | 'json';
37
+ interface SceneExportRecord {
38
+ id: string;
39
+ format: SceneExportFormat;
40
+ status: 'queued' | 'processing' | 'completed' | 'failed';
41
+ resultUrl: string | null;
42
+ metadata: Record<string, unknown>;
43
+ createdAt: Date;
44
+ }
45
+ interface SceneStore {
46
+ /** Current document + revision. */
47
+ getDocument(): Promise<SceneDocumentRecord>;
48
+ /** Atomic full-document save. Throws when `expectedRev` is stale — the
49
+ * caller must refetch, reapply, and retry; never merge silently. */
50
+ saveDocument(document: SceneDocument, expectedRev: number): Promise<SceneDocumentRecord>;
51
+ recordDecision(input: NewSceneDecision): Promise<SceneDecision>;
52
+ createExport(format: SceneExportFormat, metadata?: Record<string, unknown>): Promise<SceneExportRecord>;
53
+ listDecisions(limit?: number): Promise<SceneDecision[]>;
54
+ listExports(limit?: number): Promise<SceneExportRecord[]>;
55
+ }
56
+ /** Per-request scope a product binds at store construction; decision and
57
+ * export rows attribute to the acting user — never trusted from tool args. */
58
+ interface SceneStoreScope {
59
+ workspaceId: string;
60
+ documentId: string;
61
+ userId: string;
62
+ }
63
+
64
+ export type { NewSceneDecision as N, SceneDecision as S, SceneDocumentRecord as a, SceneExportFormat as b, SceneExportRecord as c, SceneStore as d, SceneStoreScope as e };
@@ -2,6 +2,7 @@ import { b as AppToolName, f as ToolHeaderNames } from '../mcp-CIupfjxV.js';
2
2
  export { A as APP_TOOL_NAMES, a as AppToolMcpServer, c as AuthenticateOptions, B as BuildHttpMcpServerOptions, d as BuildMcpServerOptions, D as DEFAULT_APP_TOOL_PATHS, e as DEFAULT_HEADER_NAMES, O as OpenAIFunctionTool, T as ToolAuthResult, g as authenticateToolRequest, h as buildAppToolMcpServer, i as buildAppToolOpenAITools, j as buildHttpMcpServer, k as isAppToolName, r as readToolArgs } from '../mcp-CIupfjxV.js';
3
3
  import { c as AppToolHandlers, f as AppToolTaxonomy, b as AppToolContext, e as AppToolProducedEvent, d as AppToolOutcome } from '../types-By4B3K37.js';
4
4
  export { A as AddCitationArgs, a as AddCitationResult, R as RenderUiArgs, g as RenderUiResult, S as ScheduleFollowupArgs, h as ScheduleFollowupResult, i as SubmitProposalArgs, j as SubmitProposalResult } from '../types-By4B3K37.js';
5
+ export { C as CreateMcpToolHandlerOptions, M as MCP_PROTOCOL_VERSIONS, a as McpProtocolVersion, b as McpServerInfo, c as McpToolDefinition, d as createMcpToolHandler } from '../mcp-rpc-DLw_r9PQ.js';
5
6
 
6
7
  /** A correctable bad-input error a tool handler throws; the HTTP layer maps it
7
8
  * to a 4xx with the code, the runtime layer to a failed tool_result. So the
@@ -4,15 +4,17 @@ import {
4
4
  handleAppToolRequest,
5
5
  verifyCapabilityToken,
6
6
  verifyExpiringCapabilityToken
7
- } from "../chunk-CF5DZELC.js";
7
+ } from "../chunk-ABGSFUJQ.js";
8
8
  import {
9
9
  DEFAULT_APP_TOOL_PATHS,
10
10
  DEFAULT_HEADER_NAMES,
11
+ MCP_PROTOCOL_VERSIONS,
11
12
  authenticateToolRequest,
12
13
  buildAppToolMcpServer,
13
14
  buildHttpMcpServer,
15
+ createMcpToolHandler,
14
16
  readToolArgs
15
- } from "../chunk-IJZJWKUK.js";
17
+ } from "../chunk-A76ZHWNF.js";
16
18
  import {
17
19
  APP_TOOL_NAMES,
18
20
  ToolInputError,
@@ -26,6 +28,7 @@ export {
26
28
  APP_TOOL_NAMES,
27
29
  DEFAULT_APP_TOOL_PATHS,
28
30
  DEFAULT_HEADER_NAMES,
31
+ MCP_PROTOCOL_VERSIONS,
29
32
  ToolInputError,
30
33
  authenticateToolRequest,
31
34
  buildAppToolMcpServer,
@@ -34,6 +37,7 @@ export {
34
37
  createAppToolRuntimeExecutor,
35
38
  createCapabilityToken,
36
39
  createExpiringCapabilityToken,
40
+ createMcpToolHandler,
37
41
  dispatchAppTool,
38
42
  handleAppToolRequest,
39
43
  isAppToolName,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "packageManager": "pnpm@10.33.4",
5
- "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto composed over the Tangle agent substrate through typed seams.",
5
+ "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent\u2192app tool side channel, integration-hub client, per-workspace billing, and crypto \u2014 composed over the Tangle agent substrate through typed seams.",
6
6
  "keywords": [
7
7
  "tangle",
8
8
  "ai-agent",
@@ -166,6 +166,21 @@
166
166
  "types": "./dist/sequences-react/index.d.ts",
167
167
  "import": "./dist/sequences-react/index.js",
168
168
  "default": "./dist/sequences-react/index.js"
169
+ },
170
+ "./design-canvas": {
171
+ "types": "./dist/design-canvas/index.d.ts",
172
+ "import": "./dist/design-canvas/index.js",
173
+ "default": "./dist/design-canvas/index.js"
174
+ },
175
+ "./design-canvas/drizzle": {
176
+ "types": "./dist/design-canvas/drizzle.d.ts",
177
+ "import": "./dist/design-canvas/drizzle.js",
178
+ "default": "./dist/design-canvas/drizzle.js"
179
+ },
180
+ "./design-canvas-react": {
181
+ "types": "./dist/design-canvas-react/index.d.ts",
182
+ "import": "./dist/design-canvas-react/index.js",
183
+ "default": "./dist/design-canvas-react/index.js"
169
184
  }
170
185
  },
171
186
  "scripts": {
@@ -189,8 +204,10 @@
189
204
  "better-sqlite3": "^12.10.0",
190
205
  "drizzle-orm": "^0.45.2",
191
206
  "jsdom": "^29.1.1",
207
+ "konva": "^10.3.0",
192
208
  "react": "^19.0.0",
193
209
  "react-dom": "^19.2.7",
210
+ "react-konva": "^19.2.5",
194
211
  "tsup": "^8.0.0",
195
212
  "typescript": "^5.7.0",
196
213
  "vitest": "^3.0.0"
@@ -202,7 +219,9 @@
202
219
  "@tangle-network/agent-knowledge": ">=1.5.0",
203
220
  "@tangle-network/agent-runtime": ">=0.21.0",
204
221
  "drizzle-orm": ">=0.36",
205
- "react": ">=18"
222
+ "react": ">=18",
223
+ "konva": ">=9",
224
+ "react-konva": ">=18"
206
225
  },
207
226
  "peerDependenciesMeta": {
208
227
  "@huggingface/transformers": {
@@ -219,6 +238,12 @@
219
238
  },
220
239
  "react": {
221
240
  "optional": true
241
+ },
242
+ "konva": {
243
+ "optional": true
244
+ },
245
+ "react-konva": {
246
+ "optional": true
222
247
  }
223
248
  },
224
249
  "dependencies": {
@@ -1,77 +0,0 @@
1
- // src/tools/auth.ts
2
- var DEFAULT_HEADER_NAMES = {
3
- userId: "X-Agent-App-User-Id",
4
- workspaceId: "X-Agent-App-Workspace-Id",
5
- threadId: "X-Agent-App-Thread-Id"
6
- };
7
- async function authenticateToolRequest(request, opts) {
8
- const h = opts.headerNames ?? DEFAULT_HEADER_NAMES;
9
- const userId = request.headers.get(h.userId)?.trim();
10
- const workspaceId = request.headers.get(h.workspaceId)?.trim();
11
- const threadId = request.headers.get(h.threadId)?.trim() || null;
12
- const bearer = request.headers.get("authorization")?.match(/^Bearer\s+(.+)$/i)?.[1];
13
- if (!userId || !bearer) {
14
- return { ok: false, response: Response.json({ error: "Missing capability credentials" }, { status: 401 }) };
15
- }
16
- if (!await opts.verifyToken(userId, bearer)) {
17
- return { ok: false, response: Response.json({ error: "Invalid capability token" }, { status: 401 }) };
18
- }
19
- if (!workspaceId) {
20
- return { ok: false, response: Response.json({ error: "Missing workspace context" }, { status: 400 }) };
21
- }
22
- return { ok: true, ctx: { userId, workspaceId, threadId } };
23
- }
24
- async function readToolArgs(request) {
25
- let body;
26
- try {
27
- body = await request.json();
28
- } catch {
29
- return null;
30
- }
31
- return body.args ?? body.arguments ?? body;
32
- }
33
-
34
- // src/tools/mcp.ts
35
- var DEFAULT_APP_TOOL_PATHS = {
36
- submit_proposal: "/api/tools/propose",
37
- schedule_followup: "/api/tools/followup",
38
- render_ui: "/api/tools/render-ui",
39
- add_citation: "/api/tools/citation"
40
- };
41
- function buildHttpMcpServer(opts) {
42
- const base = opts.baseUrl.replace(/\/+$/, "");
43
- const h = opts.headerNames ?? DEFAULT_HEADER_NAMES;
44
- return {
45
- transport: "http",
46
- url: `${base}${opts.path}`,
47
- headers: {
48
- Authorization: `Bearer ${opts.token}`,
49
- [h.userId]: opts.ctx.userId,
50
- ...opts.ctx.workspaceId ? { [h.workspaceId]: opts.ctx.workspaceId } : {},
51
- ...opts.ctx.threadId ? { [h.threadId]: opts.ctx.threadId } : {},
52
- "Content-Type": "application/json"
53
- },
54
- enabled: true,
55
- metadata: { description: opts.description }
56
- };
57
- }
58
- function buildAppToolMcpServer(opts) {
59
- return buildHttpMcpServer({
60
- path: opts.paths?.[opts.tool] ?? DEFAULT_APP_TOOL_PATHS[opts.tool],
61
- baseUrl: opts.baseUrl,
62
- token: opts.token,
63
- ctx: opts.ctx,
64
- description: opts.description,
65
- headerNames: opts.headerNames
66
- });
67
- }
68
-
69
- export {
70
- DEFAULT_HEADER_NAMES,
71
- authenticateToolRequest,
72
- readToolArgs,
73
- DEFAULT_APP_TOOL_PATHS,
74
- buildHttpMcpServer,
75
- buildAppToolMcpServer
76
- };
77
- //# sourceMappingURL=chunk-IJZJWKUK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/tools/auth.ts","../src/tools/mcp.ts"],"sourcesContent":["import type { AppToolContext } from './types'\n\n/**\n * Header names carrying the server-set per-turn context + the capability token.\n * Defaults are product-neutral (`X-Agent-App-*`); a product that already ships\n * a header convention (e.g. `X-Acme-User-Id`) passes its own.\n */\nexport interface ToolHeaderNames {\n userId: string\n workspaceId: string\n threadId: string\n}\n\nexport const DEFAULT_HEADER_NAMES: ToolHeaderNames = {\n userId: 'X-Agent-App-User-Id',\n workspaceId: 'X-Agent-App-Workspace-Id',\n threadId: 'X-Agent-App-Thread-Id',\n}\n\nexport interface AuthenticateOptions {\n /** Verify the bearer capability token belongs to `userId`. The product's\n * HMAC/JWT impl — the seam that keeps token crypto out of this package. */\n verifyToken: (userId: string, bearer: string) => Promise<boolean>\n headerNames?: ToolHeaderNames\n}\n\nexport type ToolAuthResult =\n | { ok: true; ctx: AppToolContext }\n | { ok: false; response: Response }\n\n/**\n * Recover + verify the trusted context for a tool request. The user comes from\n * a server-set header and the bearer token MUST verify against THAT user; the\n * workspace comes from a header too — never from tool args — so the model can\n * neither forge identity nor target another workspace. Fail-closed: any missing\n * credential or a token minted for another user yields a 401/400 Response.\n */\nexport async function authenticateToolRequest(request: Request, opts: AuthenticateOptions): Promise<ToolAuthResult> {\n const h = opts.headerNames ?? DEFAULT_HEADER_NAMES\n const userId = request.headers.get(h.userId)?.trim()\n const workspaceId = request.headers.get(h.workspaceId)?.trim()\n const threadId = request.headers.get(h.threadId)?.trim() || null\n const bearer = request.headers.get('authorization')?.match(/^Bearer\\s+(.+)$/i)?.[1]\n\n if (!userId || !bearer) {\n return { ok: false, response: Response.json({ error: 'Missing capability credentials' }, { status: 401 }) }\n }\n if (!(await opts.verifyToken(userId, bearer))) {\n return { ok: false, response: Response.json({ error: 'Invalid capability token' }, { status: 401 }) }\n }\n if (!workspaceId) {\n return { ok: false, response: Response.json({ error: 'Missing workspace context' }, { status: 400 }) }\n }\n return { ok: true, ctx: { userId, workspaceId, threadId } }\n}\n\n/** Read a tool's argument object from the request body, tolerant of MCP host\n * aliases (`args` / `arguments`) or a bare body. Returns null on non-JSON. */\nexport async function readToolArgs<T>(request: Request): Promise<T | null> {\n let body: { args?: T; arguments?: T }\n try {\n body = (await request.json()) as typeof body\n } catch {\n return null\n }\n return (body.args ?? body.arguments ?? (body as T)) as T\n}\n","import type { AppToolContext } from './types'\nimport type { AppToolName } from './openai'\nimport type { ToolHeaderNames } from './auth'\nimport { DEFAULT_HEADER_NAMES } from './auth'\n\n/** Default route path each app tool is served at. A product mounts its routes\n * at these paths (or supplies its own via {@link BuildMcpServerOptions.paths}). */\nexport const DEFAULT_APP_TOOL_PATHS: Record<AppToolName, string> = {\n submit_proposal: '/api/tools/propose',\n schedule_followup: '/api/tools/followup',\n render_ui: '/api/tools/render-ui',\n add_citation: '/api/tools/citation',\n}\n\n/** The portable MCP server entry the sandbox SDK accepts (transport + url +\n * headers). Matches `AgentProfileMcpServer` structurally without importing the\n * sandbox SDK — products spread it into their profile's `mcp` map. */\nexport interface AppToolMcpServer {\n transport: 'http'\n url: string\n headers: Record<string, string>\n enabled: true\n metadata: { description: string }\n}\n\nexport interface BuildHttpMcpServerOptions {\n /** Route path on the app the sandbox POSTs to (e.g. `/api/tools/propose`). */\n path: string\n /** App base URL the sandbox reaches back to (no trailing slash required). */\n baseUrl: string\n /** Per-user capability token, baked into the Authorization header. */\n token: string\n ctx: AppToolContext\n /** Tool description the model sees. */\n description: string\n headerNames?: ToolHeaderNames\n}\n\n/**\n * Build ONE HTTP MCP server entry — the generic agent→app bridge. The\n * capability token + the user/workspace/thread ids ride in server-set headers\n * (never tool args), so the model can't forge identity or target another\n * workspace. Workspace/thread headers are omitted when their `ctx` value is\n * empty/null (e.g. an integration-invoke bridge that's user-scoped only). Used\n * directly for non-app-tool bridges (integration_invoke) and via\n * {@link buildAppToolMcpServer} for the four app tools.\n */\nexport function buildHttpMcpServer(opts: BuildHttpMcpServerOptions): AppToolMcpServer {\n const base = opts.baseUrl.replace(/\\/+$/, '')\n const h = opts.headerNames ?? DEFAULT_HEADER_NAMES\n return {\n transport: 'http',\n url: `${base}${opts.path}`,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n [h.userId]: opts.ctx.userId,\n ...(opts.ctx.workspaceId ? { [h.workspaceId]: opts.ctx.workspaceId } : {}),\n ...(opts.ctx.threadId ? { [h.threadId]: opts.ctx.threadId } : {}),\n 'Content-Type': 'application/json',\n },\n enabled: true,\n metadata: { description: opts.description },\n }\n}\n\nexport interface BuildMcpServerOptions {\n tool: AppToolName\n baseUrl: string\n token: string\n ctx: AppToolContext\n description: string\n headerNames?: ToolHeaderNames\n paths?: Partial<Record<AppToolName, string>>\n}\n\n/** Build one of the four app-tool MCP servers — a thin wrapper over\n * {@link buildHttpMcpServer} that maps the tool name to its route path. */\nexport function buildAppToolMcpServer(opts: BuildMcpServerOptions): AppToolMcpServer {\n return buildHttpMcpServer({\n path: opts.paths?.[opts.tool] ?? DEFAULT_APP_TOOL_PATHS[opts.tool],\n baseUrl: opts.baseUrl,\n token: opts.token,\n ctx: opts.ctx,\n description: opts.description,\n headerNames: opts.headerNames,\n })\n}\n"],"mappings":";AAaO,IAAM,uBAAwC;AAAA,EACnD,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,UAAU;AACZ;AAoBA,eAAsB,wBAAwB,SAAkB,MAAoD;AAClH,QAAM,IAAI,KAAK,eAAe;AAC9B,QAAM,SAAS,QAAQ,QAAQ,IAAI,EAAE,MAAM,GAAG,KAAK;AACnD,QAAM,cAAc,QAAQ,QAAQ,IAAI,EAAE,WAAW,GAAG,KAAK;AAC7D,QAAM,WAAW,QAAQ,QAAQ,IAAI,EAAE,QAAQ,GAAG,KAAK,KAAK;AAC5D,QAAM,SAAS,QAAQ,QAAQ,IAAI,eAAe,GAAG,MAAM,kBAAkB,IAAI,CAAC;AAElF,MAAI,CAAC,UAAU,CAAC,QAAQ;AACtB,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,KAAK,EAAE,OAAO,iCAAiC,GAAG,EAAE,QAAQ,IAAI,CAAC,EAAE;AAAA,EAC5G;AACA,MAAI,CAAE,MAAM,KAAK,YAAY,QAAQ,MAAM,GAAI;AAC7C,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC,EAAE;AAAA,EACtG;AACA,MAAI,CAAC,aAAa;AAChB,WAAO,EAAE,IAAI,OAAO,UAAU,SAAS,KAAK,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC,EAAE;AAAA,EACvG;AACA,SAAO,EAAE,IAAI,MAAM,KAAK,EAAE,QAAQ,aAAa,SAAS,EAAE;AAC5D;AAIA,eAAsB,aAAgB,SAAqC;AACzE,MAAI;AACJ,MAAI;AACF,WAAQ,MAAM,QAAQ,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAQ,KAAK,QAAQ,KAAK,aAAc;AAC1C;;;AC3DO,IAAM,yBAAsD;AAAA,EACjE,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,cAAc;AAChB;AAmCO,SAAS,mBAAmB,MAAmD;AACpF,QAAM,OAAO,KAAK,QAAQ,QAAQ,QAAQ,EAAE;AAC5C,QAAM,IAAI,KAAK,eAAe;AAC9B,SAAO;AAAA,IACL,WAAW;AAAA,IACX,KAAK,GAAG,IAAI,GAAG,KAAK,IAAI;AAAA,IACxB,SAAS;AAAA,MACP,eAAe,UAAU,KAAK,KAAK;AAAA,MACnC,CAAC,EAAE,MAAM,GAAG,KAAK,IAAI;AAAA,MACrB,GAAI,KAAK,IAAI,cAAc,EAAE,CAAC,EAAE,WAAW,GAAG,KAAK,IAAI,YAAY,IAAI,CAAC;AAAA,MACxE,GAAI,KAAK,IAAI,WAAW,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,IAAI,SAAS,IAAI,CAAC;AAAA,MAC/D,gBAAgB;AAAA,IAClB;AAAA,IACA,SAAS;AAAA,IACT,UAAU,EAAE,aAAa,KAAK,YAAY;AAAA,EAC5C;AACF;AAcO,SAAS,sBAAsB,MAA+C;AACnF,SAAO,mBAAmB;AAAA,IACxB,MAAM,KAAK,QAAQ,KAAK,IAAI,KAAK,uBAAuB,KAAK,IAAI;AAAA,IACjE,SAAS,KAAK;AAAA,IACd,OAAO,KAAK;AAAA,IACZ,KAAK,KAAK;AAAA,IACV,aAAa,KAAK;AAAA,IAClB,aAAa,KAAK;AAAA,EACpB,CAAC;AACH;","names":[]}