@tangle-network/agent-app 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.
Files changed (61) hide show
  1. package/README.md +59 -0
  2. package/dist/billing/index.d.ts +108 -0
  3. package/dist/billing/index.js +7 -0
  4. package/dist/billing/index.js.map +1 -0
  5. package/dist/chunk-45MYQ3GD.js +62 -0
  6. package/dist/chunk-45MYQ3GD.js.map +1 -0
  7. package/dist/chunk-4NXVI7PW.js +32 -0
  8. package/dist/chunk-4NXVI7PW.js.map +1 -0
  9. package/dist/chunk-7P6VIHI4.js +33 -0
  10. package/dist/chunk-7P6VIHI4.js.map +1 -0
  11. package/dist/chunk-C5CREGT2.js +45 -0
  12. package/dist/chunk-C5CREGT2.js.map +1 -0
  13. package/dist/chunk-CN75FIPT.js +61 -0
  14. package/dist/chunk-CN75FIPT.js.map +1 -0
  15. package/dist/chunk-EDIQ6F55.js +274 -0
  16. package/dist/chunk-EDIQ6F55.js.map +1 -0
  17. package/dist/chunk-FS5OUVRB.js +208 -0
  18. package/dist/chunk-FS5OUVRB.js.map +1 -0
  19. package/dist/chunk-GMFPCCQZ.js +245 -0
  20. package/dist/chunk-GMFPCCQZ.js.map +1 -0
  21. package/dist/chunk-L2TG5DBW.js +74 -0
  22. package/dist/chunk-L2TG5DBW.js.map +1 -0
  23. package/dist/chunk-SIDR6BH3.js +57 -0
  24. package/dist/chunk-SIDR6BH3.js.map +1 -0
  25. package/dist/chunk-YGUNTIT5.js +48 -0
  26. package/dist/chunk-YGUNTIT5.js.map +1 -0
  27. package/dist/crypto/index.d.ts +27 -0
  28. package/dist/crypto/index.js +13 -0
  29. package/dist/crypto/index.js.map +1 -0
  30. package/dist/delegation/index.d.ts +50 -0
  31. package/dist/delegation/index.js +11 -0
  32. package/dist/delegation/index.js.map +1 -0
  33. package/dist/eval/index.d.ts +50 -0
  34. package/dist/eval/index.js +17 -0
  35. package/dist/eval/index.js.map +1 -0
  36. package/dist/index.d.ts +13 -0
  37. package/dist/index.js +149 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/integrations/index.d.ts +84 -0
  40. package/dist/integrations/index.js +11 -0
  41. package/dist/integrations/index.js.map +1 -0
  42. package/dist/redact/index.d.ts +22 -0
  43. package/dist/redact/index.js +7 -0
  44. package/dist/redact/index.js.map +1 -0
  45. package/dist/runtime/index.d.ts +219 -0
  46. package/dist/runtime/index.js +17 -0
  47. package/dist/runtime/index.js.map +1 -0
  48. package/dist/stream/index.d.ts +39 -0
  49. package/dist/stream/index.js +35 -0
  50. package/dist/stream/index.js.map +1 -0
  51. package/dist/tangle/index.d.ts +93 -0
  52. package/dist/tangle/index.js +9 -0
  53. package/dist/tangle/index.js.map +1 -0
  54. package/dist/tools/index.d.ts +213 -0
  55. package/dist/tools/index.js +37 -0
  56. package/dist/tools/index.js.map +1 -0
  57. package/dist/types-CeWor4bQ.d.ts +120 -0
  58. package/dist/web/index.d.ts +51 -0
  59. package/dist/web/index.js +15 -0
  60. package/dist/web/index.js.map +1 -0
  61. package/package.json +101 -0
@@ -0,0 +1,35 @@
1
+ import {
2
+ asRecord,
3
+ asString,
4
+ buildUserTextParts,
5
+ encodeEvent,
6
+ finalizeAssistantParts,
7
+ getPartKey,
8
+ mergePersistedPart,
9
+ messageHasTurnId,
10
+ normalizeClientTurnId,
11
+ normalizePersistedPart,
12
+ normalizeTime,
13
+ normalizeToolEvent,
14
+ resolveChatTurn,
15
+ resolveToolId,
16
+ resolveToolName
17
+ } from "../chunk-GMFPCCQZ.js";
18
+ export {
19
+ asRecord,
20
+ asString,
21
+ buildUserTextParts,
22
+ encodeEvent,
23
+ finalizeAssistantParts,
24
+ getPartKey,
25
+ mergePersistedPart,
26
+ messageHasTurnId,
27
+ normalizeClientTurnId,
28
+ normalizePersistedPart,
29
+ normalizeTime,
30
+ normalizeToolEvent,
31
+ resolveChatTurn,
32
+ resolveToolId,
33
+ resolveToolName
34
+ };
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Tangle login + the developer self-service app-registration → broker-token
3
+ * flow, for apps built on agent-app.
4
+ *
5
+ * The platform (agent-dev-container integration hub) lets a developer register
6
+ * their client app and obtain a `sk-tan-broker-` bearer to call `/v1/hub/exec`
7
+ * on a user's connected integrations — WITHOUT being a hard-coded "trusted app".
8
+ * The wire client (`TangleAppsClient` — registerApp / exchangeAuthCode /
9
+ * mintBrokerToken) lives in `@tangle-network/agent-integrations`; this module is
10
+ * the app-shell layer on top, and is intentionally **structural**: it depends on
11
+ * the minter CONTRACT, not the concrete client, so it installs without the
12
+ * agent-integrations publish and is trivially testable. A consumer constructs
13
+ * the real client and passes it in.
14
+ *
15
+ * 1. {@link buildConsentUrl} — send the user through the ONE-TIME consent
16
+ * (their Tangle session authorizes the app for a connection + scopes).
17
+ * 2. On the callback, the consumer's client `exchangeAuthCode`s the `agc_`
18
+ * code into the first broker token + a durable grant.
19
+ * 3. {@link createBrokerTokenProvider} — the runtime path: a cached provider
20
+ * that re-mints a fresh single-use broker token per `/v1/hub/exec` from the
21
+ * durable grant using only the app credentials (no user session). Caches
22
+ * until just before expiry so a burst of hub calls shares one mint.
23
+ */
24
+ /** A single-use hub bearer minted from a durable grant — mirrors
25
+ * `@tangle-network/agent-integrations`'s `BrokerToken`. */
26
+ interface BrokerToken {
27
+ /** The `sk-tan-broker-…` bearer for a single `/v1/hub/exec` call. */
28
+ accessToken: string;
29
+ /** Seconds until expiry. */
30
+ expiresIn: number;
31
+ scope: string;
32
+ connectionId?: string;
33
+ }
34
+ /** The one method the provider needs — `TangleAppsClient` satisfies it
35
+ * structurally, so `createBrokerTokenProvider({ client: tangleAppsClient, … })`
36
+ * type-checks without importing the concrete class. */
37
+ interface BrokerTokenMinter {
38
+ mintBrokerToken(input: {
39
+ clientId: string;
40
+ clientSecret: string;
41
+ grantId: string;
42
+ ttlSeconds?: number;
43
+ }): Promise<BrokerToken>;
44
+ }
45
+ interface ConsentUrlInput {
46
+ /** Platform base URL (e.g. https://id.tangle.tools). */
47
+ endpoint: string;
48
+ clientId: string;
49
+ /** Must match one of the app's registered redirect URIs. */
50
+ redirectUri: string;
51
+ /** Scopes the app is requesting for this connection (e.g. ['gmail.read']). */
52
+ scopes: string[];
53
+ /** Opaque CSRF/state value the callback echoes back — verify it on return. */
54
+ state: string;
55
+ /** Optionally pre-select a specific connection to authorize. */
56
+ connectionId?: string;
57
+ }
58
+ /**
59
+ * Build the URL to send the user to for the one-time app-consent. The user's
60
+ * Tangle session (not the app's credentials) authorizes it; on approval the
61
+ * platform redirects to `redirectUri?code=agc_…&state=…`.
62
+ */
63
+ declare function buildConsentUrl(input: ConsentUrlInput): string;
64
+ interface BrokerTokenProviderOptions {
65
+ client: BrokerTokenMinter;
66
+ clientId: string;
67
+ clientSecret: string;
68
+ /** The durable grant id from the consent exchange. */
69
+ grantId: string;
70
+ /** Requested token TTL (seconds). */
71
+ ttlSeconds?: number;
72
+ /** Re-mint this many ms BEFORE expiry so an in-flight call never uses a
73
+ * just-expired token. Default 30s. */
74
+ refreshSkewMs?: number;
75
+ /** Injectable clock (ms). Default `Date.now`. */
76
+ now?: () => number;
77
+ }
78
+ interface BrokerTokenProvider {
79
+ /** A valid `sk-tan-broker-` bearer, minting/refreshing as needed. */
80
+ getToken(): Promise<string>;
81
+ /** Force the next `getToken` to re-mint (e.g. after a 401 from the hub). */
82
+ invalidate(): void;
83
+ }
84
+ /**
85
+ * Cache + auto-refresh a broker token for one grant. A burst of hub calls
86
+ * shares a single mint; the token is re-minted once it's within `refreshSkewMs`
87
+ * of expiry, or on demand via {@link BrokerTokenProvider.invalidate}.
88
+ * Concurrent `getToken` calls during a mint share the same in-flight promise
89
+ * (no thundering herd).
90
+ */
91
+ declare function createBrokerTokenProvider(opts: BrokerTokenProviderOptions): BrokerTokenProvider;
92
+
93
+ export { type BrokerToken, type BrokerTokenMinter, type BrokerTokenProvider, type BrokerTokenProviderOptions, type ConsentUrlInput, buildConsentUrl, createBrokerTokenProvider };
@@ -0,0 +1,9 @@
1
+ import {
2
+ buildConsentUrl,
3
+ createBrokerTokenProvider
4
+ } from "../chunk-YGUNTIT5.js";
5
+ export {
6
+ buildConsentUrl,
7
+ createBrokerTokenProvider
8
+ };
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,213 @@
1
+ import { b as AppToolContext, f as AppToolTaxonomy, c as AppToolHandlers, e as AppToolProducedEvent, d as AppToolOutcome } from '../types-CeWor4bQ.js';
2
+ 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-CeWor4bQ.js';
3
+
4
+ /** A correctable bad-input error a tool handler throws; the HTTP layer maps it
5
+ * to a 4xx with the code, the runtime layer to a failed tool_result. So the
6
+ * agent learns the call failed and can correct, instead of a silent success. */
7
+ declare class ToolInputError extends Error {
8
+ code: string;
9
+ status: number;
10
+ constructor(code: string, message: string, status?: number);
11
+ }
12
+
13
+ /**
14
+ * Per-user capability token — the sandbox→app auth primitive behind the
15
+ * `verifyToken` seam in {@link authenticateToolRequest}.
16
+ *
17
+ * An app-agent runs inside the sandbox and reaches the host app back over HTTP
18
+ * (the app tools, the integration-invoke bridge). The route must act AS the
19
+ * connecting user without trusting any model-supplied identity, so the turn
20
+ * mints a short HMAC token bound to the user id and bakes it into the per-turn
21
+ * MCP server header; the route verifies it to recover the user.
22
+ *
23
+ * `HMAC-SHA256(secret, "user:<userId>")`, base64url, with an app-chosen prefix.
24
+ * The token encodes no scopes — the hub's policy engine authorizes per action.
25
+ * Fail-closed: with no secret, no token is minted (the caller MUST omit the MCP
26
+ * server rather than fake an authorized call). WebCrypto only — runs on
27
+ * Workers, Node, and the browser with no Node `crypto` dependency.
28
+ */
29
+ interface CapabilityTokenOptions {
30
+ /** Shared HMAC secret. When absent, mint returns undefined / verify returns false. */
31
+ secret?: string;
32
+ /** Token prefix (namespaces the credential; lets verify reject foreign tokens
33
+ * cheaply). Default `cap_`. */
34
+ prefix?: string;
35
+ }
36
+ /** Mint a capability token for `userId`, or `undefined` when no secret is
37
+ * configured (fail-closed — the caller omits the MCP server rather than fake it). */
38
+ declare function createCapabilityToken(userId: string, opts: CapabilityTokenOptions): Promise<string | undefined>;
39
+ /** Verify a capability token against `userId`. Returns false (never throws) for
40
+ * an unconfigured secret, a wrong prefix, a malformed token, or a mismatch. */
41
+ declare function verifyCapabilityToken(userId: string, token: string, opts: CapabilityTokenOptions): Promise<boolean>;
42
+
43
+ /**
44
+ * Header names carrying the server-set per-turn context + the capability token.
45
+ * Defaults are product-neutral (`X-Agent-App-*`); a product that already ships
46
+ * a header convention (e.g. `X-Insurance-User-Id`) passes its own.
47
+ */
48
+ interface ToolHeaderNames {
49
+ userId: string;
50
+ workspaceId: string;
51
+ threadId: string;
52
+ }
53
+ declare const DEFAULT_HEADER_NAMES: ToolHeaderNames;
54
+ interface AuthenticateOptions {
55
+ /** Verify the bearer capability token belongs to `userId`. The product's
56
+ * HMAC/JWT impl — the seam that keeps token crypto out of this package. */
57
+ verifyToken: (userId: string, bearer: string) => Promise<boolean>;
58
+ headerNames?: ToolHeaderNames;
59
+ }
60
+ type ToolAuthResult = {
61
+ ok: true;
62
+ ctx: AppToolContext;
63
+ } | {
64
+ ok: false;
65
+ response: Response;
66
+ };
67
+ /**
68
+ * Recover + verify the trusted context for a tool request. The user comes from
69
+ * a server-set header and the bearer token MUST verify against THAT user; the
70
+ * workspace comes from a header too — never from tool args — so the model can
71
+ * neither forge identity nor target another workspace. Fail-closed: any missing
72
+ * credential or a token minted for another user yields a 401/400 Response.
73
+ */
74
+ declare function authenticateToolRequest(request: Request, opts: AuthenticateOptions): Promise<ToolAuthResult>;
75
+ /** Read a tool's argument object from the request body, tolerant of MCP host
76
+ * aliases (`args` / `arguments`) or a bare body. Returns null on non-JSON. */
77
+ declare function readToolArgs<T>(request: Request): Promise<T | null>;
78
+
79
+ /** The four canonical app-tool names. Stable identifiers the model calls in
80
+ * both the sandbox (MCP server name) and runtime (function-tool name) paths. */
81
+ declare const APP_TOOL_NAMES: readonly ["submit_proposal", "schedule_followup", "render_ui", "add_citation"];
82
+ type AppToolName = (typeof APP_TOOL_NAMES)[number];
83
+ declare function isAppToolName(name: string): name is AppToolName;
84
+ /** A minimal OpenAI Chat Completions function-tool shape — structurally
85
+ * compatible with `@tangle-network/agent-runtime`'s `OpenAIChatTool` without
86
+ * importing it (keeps this package runtime-free). */
87
+ interface OpenAIFunctionTool {
88
+ type: 'function';
89
+ function: {
90
+ name: string;
91
+ description: string;
92
+ parameters: Record<string, unknown>;
93
+ };
94
+ }
95
+ /**
96
+ * Build the four app tools in OpenAI function-tool shape. `submit_proposal`'s
97
+ * `type` enum is the product's {@link AppToolTaxonomy.proposalTypes}; the other
98
+ * three are fixed. Pass the result to the agent-runtime backend's `tools`.
99
+ */
100
+ declare function buildAppToolOpenAITools(taxonomy: AppToolTaxonomy): OpenAIFunctionTool[];
101
+
102
+ interface DispatchOptions {
103
+ handlers: AppToolHandlers;
104
+ taxonomy: AppToolTaxonomy;
105
+ /** Called at the real side-effect site for proposals (proposal_created) and
106
+ * generated views (artifact) so a consumer's completion oracle credits
107
+ * persisted state. Omit when produced state isn't tracked. */
108
+ onProduced?: (event: AppToolProducedEvent) => void;
109
+ }
110
+ /**
111
+ * The ONE place an app-tool call is validated, dispatched to the product's
112
+ * handler, and turned into an {@link AppToolOutcome} + produced events. Shared
113
+ * by the HTTP route layer and the agent-runtime executor so both paths apply
114
+ * identical validation and identical side effects. A {@link ToolInputError}
115
+ * (bad input the agent can correct) and any other throw both become
116
+ * `{ ok: false }` — a tool call never silently "succeeds" without its effect.
117
+ */
118
+ declare function dispatchAppTool(toolName: string, rawArgs: Record<string, unknown>, ctx: AppToolContext, opts: DispatchOptions): Promise<AppToolOutcome>;
119
+ /** HTTP status for a failed outcome — the handler's `ToolInputError.status`
120
+ * when present, else 400 for a validation reject. */
121
+ declare function outcomeStatus(outcome: Extract<AppToolOutcome, {
122
+ ok: false;
123
+ }>): number;
124
+
125
+ /** Executes an app-tool call the model emits on the agent-runtime chat path.
126
+ * Plug into `runChatThroughRuntime({ appToolExecutor })` (or any loop that
127
+ * dispatches function tool_calls). */
128
+ type AppToolRuntimeExecutor = (call: {
129
+ toolName: string;
130
+ args: Record<string, unknown>;
131
+ }) => Promise<AppToolOutcome>;
132
+ interface RuntimeExecutorOptions extends DispatchOptions {
133
+ /** The trusted per-turn context — supplied directly (not from headers), since
134
+ * the runtime path has no HTTP request. */
135
+ ctx: AppToolContext;
136
+ }
137
+ /**
138
+ * Build the runtime executor for one turn. The agent-runtime backend must also
139
+ * advertise the tools (`buildAppToolOpenAITools(taxonomy)` on the backend's
140
+ * `tools`) for the model to call them; this executor fulfils each call against
141
+ * the product's handlers and emits produced events via `opts.onProduced`.
142
+ */
143
+ declare function createAppToolRuntimeExecutor(opts: RuntimeExecutorOptions): AppToolRuntimeExecutor;
144
+
145
+ interface HandleToolRequestOptions extends DispatchOptions {
146
+ /** Which app tool this route serves. */
147
+ tool: AppToolName;
148
+ /** Verify the bearer capability token belongs to the header user. */
149
+ verifyToken: (userId: string, bearer: string) => Promise<boolean>;
150
+ headerNames?: ToolHeaderNames;
151
+ /** Optional success-message builder for a friendlier tool result. */
152
+ message?: (result: unknown) => string;
153
+ }
154
+ /**
155
+ * Handle one app-tool HTTP request end to end — the sandbox MCP path. The
156
+ * agent's per-turn HTTP MCP server POSTs here; this authenticates (header user
157
+ * + capability token), reads the args (MCP-alias tolerant), dispatches to the
158
+ * product handler, and returns a JSON Response. A product's route file becomes
159
+ * a one-liner: `export const action = ({ request }) => handleAppToolRequest(request, cfg)`.
160
+ */
161
+ declare function handleAppToolRequest(request: Request, opts: HandleToolRequestOptions): Promise<Response>;
162
+
163
+ /** Default route path each app tool is served at. A product mounts its routes
164
+ * at these paths (or supplies its own via {@link BuildMcpServerOptions.paths}). */
165
+ declare const DEFAULT_APP_TOOL_PATHS: Record<AppToolName, string>;
166
+ /** The portable MCP server entry the sandbox SDK accepts (transport + url +
167
+ * headers). Matches `AgentProfileMcpServer` structurally without importing the
168
+ * sandbox SDK — products spread it into their profile's `mcp` map. */
169
+ interface AppToolMcpServer {
170
+ transport: 'http';
171
+ url: string;
172
+ headers: Record<string, string>;
173
+ enabled: true;
174
+ metadata: {
175
+ description: string;
176
+ };
177
+ }
178
+ interface BuildHttpMcpServerOptions {
179
+ /** Route path on the app the sandbox POSTs to (e.g. `/api/tools/propose`). */
180
+ path: string;
181
+ /** App base URL the sandbox reaches back to (no trailing slash required). */
182
+ baseUrl: string;
183
+ /** Per-user capability token, baked into the Authorization header. */
184
+ token: string;
185
+ ctx: AppToolContext;
186
+ /** Tool description the model sees. */
187
+ description: string;
188
+ headerNames?: ToolHeaderNames;
189
+ }
190
+ /**
191
+ * Build ONE HTTP MCP server entry — the generic agent→app bridge. The
192
+ * capability token + the user/workspace/thread ids ride in server-set headers
193
+ * (never tool args), so the model can't forge identity or target another
194
+ * workspace. Workspace/thread headers are omitted when their `ctx` value is
195
+ * empty/null (e.g. an integration-invoke bridge that's user-scoped only). Used
196
+ * directly for non-app-tool bridges (integration_invoke) and via
197
+ * {@link buildAppToolMcpServer} for the four app tools.
198
+ */
199
+ declare function buildHttpMcpServer(opts: BuildHttpMcpServerOptions): AppToolMcpServer;
200
+ interface BuildMcpServerOptions {
201
+ tool: AppToolName;
202
+ baseUrl: string;
203
+ token: string;
204
+ ctx: AppToolContext;
205
+ description: string;
206
+ headerNames?: ToolHeaderNames;
207
+ paths?: Partial<Record<AppToolName, string>>;
208
+ }
209
+ /** Build one of the four app-tool MCP servers — a thin wrapper over
210
+ * {@link buildHttpMcpServer} that maps the tool name to its route path. */
211
+ declare function buildAppToolMcpServer(opts: BuildMcpServerOptions): AppToolMcpServer;
212
+
213
+ export { APP_TOOL_NAMES, AppToolContext, AppToolHandlers, type AppToolMcpServer, type AppToolName, AppToolOutcome, AppToolProducedEvent, type AppToolRuntimeExecutor, AppToolTaxonomy, type AuthenticateOptions, type BuildHttpMcpServerOptions, type BuildMcpServerOptions, type CapabilityTokenOptions, DEFAULT_APP_TOOL_PATHS, DEFAULT_HEADER_NAMES, type DispatchOptions, type HandleToolRequestOptions, type OpenAIFunctionTool, type RuntimeExecutorOptions, type ToolAuthResult, type ToolHeaderNames, ToolInputError, authenticateToolRequest, buildAppToolMcpServer, buildAppToolOpenAITools, buildHttpMcpServer, createAppToolRuntimeExecutor, createCapabilityToken, dispatchAppTool, handleAppToolRequest, isAppToolName, outcomeStatus, readToolArgs, verifyCapabilityToken };
@@ -0,0 +1,37 @@
1
+ import {
2
+ APP_TOOL_NAMES,
3
+ DEFAULT_APP_TOOL_PATHS,
4
+ DEFAULT_HEADER_NAMES,
5
+ ToolInputError,
6
+ authenticateToolRequest,
7
+ buildAppToolMcpServer,
8
+ buildAppToolOpenAITools,
9
+ buildHttpMcpServer,
10
+ createAppToolRuntimeExecutor,
11
+ createCapabilityToken,
12
+ dispatchAppTool,
13
+ handleAppToolRequest,
14
+ isAppToolName,
15
+ outcomeStatus,
16
+ readToolArgs,
17
+ verifyCapabilityToken
18
+ } from "../chunk-EDIQ6F55.js";
19
+ export {
20
+ APP_TOOL_NAMES,
21
+ DEFAULT_APP_TOOL_PATHS,
22
+ DEFAULT_HEADER_NAMES,
23
+ ToolInputError,
24
+ authenticateToolRequest,
25
+ buildAppToolMcpServer,
26
+ buildAppToolOpenAITools,
27
+ buildHttpMcpServer,
28
+ createAppToolRuntimeExecutor,
29
+ createCapabilityToken,
30
+ dispatchAppTool,
31
+ handleAppToolRequest,
32
+ isAppToolName,
33
+ outcomeStatus,
34
+ readToolArgs,
35
+ verifyCapabilityToken
36
+ };
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * The structured agent→app tool side channel, domain-seamed.
3
+ *
4
+ * Every Tangle agent product needs the same thing: a way for the in-sandbox
5
+ * agent to reach the host app with a STRUCTURED, validated tool call (not a
6
+ * fenced `:::` block scraped from prose). Four canonical tools cover it:
7
+ *
8
+ * - `submit_proposal` — route a regulated/state-changing action to a human
9
+ * approval queue (the human-in-the-loop gate).
10
+ * - `schedule_followup` — register a dated cadence step (executes immediately).
11
+ * - `render_ui` — persist a generated view as an artifact (immediate).
12
+ * - `add_citation` — anchor a grounding reference (immediate).
13
+ *
14
+ * The shapes, validation, OpenAI tool definitions, MCP-server wiring, HTTP
15
+ * route handling, and runtime-loop executor are GENERIC and live here. The
16
+ * persistence (which DB, which tables) and the product's proposal taxonomy are
17
+ * the DOMAIN SEAM: each product supplies {@link AppToolHandlers} +
18
+ * {@link AppToolTaxonomy}. This package imports no product code and no agent
19
+ * runtime — it depends only on the Web `Request`/`Response` types.
20
+ */
21
+ /** Server-set, trusted per-turn context. Recovered from request headers (HTTP
22
+ * path) or supplied directly (runtime path) — never from model tool args, so
23
+ * the model cannot forge identity or target another workspace. */
24
+ interface AppToolContext {
25
+ userId: string;
26
+ workspaceId: string;
27
+ threadId: string | null;
28
+ }
29
+ /** The product's proposal taxonomy — the only domain-specific vocabulary the
30
+ * generic layer needs (to validate `submit_proposal.type` and label the
31
+ * regulated subset). */
32
+ interface AppToolTaxonomy {
33
+ /** Every accepted proposal type. */
34
+ proposalTypes: readonly string[];
35
+ /** The subset that cannot execute without a named certified human (regulated
36
+ * steps). Used only to label the tool result; enforcement is the product's
37
+ * approval executor. */
38
+ regulatedTypes: readonly string[];
39
+ }
40
+ interface SubmitProposalArgs {
41
+ type: string;
42
+ title: string;
43
+ description?: string | null;
44
+ }
45
+ interface SubmitProposalResult {
46
+ proposalId: string;
47
+ /** True when an identical (workspace, title) proposal already existed. */
48
+ deduped: boolean;
49
+ }
50
+ interface ScheduleFollowupArgs {
51
+ title: string;
52
+ dueDate: string;
53
+ priority?: string;
54
+ }
55
+ interface ScheduleFollowupResult {
56
+ id: string;
57
+ dueDate: string;
58
+ deduped: boolean;
59
+ }
60
+ interface RenderUiArgs {
61
+ title: string;
62
+ schema: unknown;
63
+ }
64
+ interface RenderUiResult {
65
+ /** The persisted artifact path. */
66
+ path: string;
67
+ /** The exact persisted body — surfaced as the `artifact` produced event so a
68
+ * consumer's completion oracle sees the real content. */
69
+ content: string;
70
+ }
71
+ interface AddCitationArgs {
72
+ path: string;
73
+ quote: string;
74
+ label?: string;
75
+ }
76
+ interface AddCitationResult {
77
+ citationId: string;
78
+ path: string;
79
+ }
80
+ /**
81
+ * The domain seam. Each product implements these against its own D1/KV/vault.
82
+ * A handler MAY throw {@link ToolInputError} for correctable bad input (mapped
83
+ * to a 4xx / failed tool_result); any other throw is an internal error.
84
+ * `submitProposal` is the only handler whose result feeds the approval queue;
85
+ * the others execute immediately.
86
+ */
87
+ interface AppToolHandlers {
88
+ submitProposal(args: SubmitProposalArgs, ctx: AppToolContext): Promise<SubmitProposalResult>;
89
+ scheduleFollowup(args: ScheduleFollowupArgs, ctx: AppToolContext): Promise<ScheduleFollowupResult>;
90
+ renderUi(args: RenderUiArgs, ctx: AppToolContext): Promise<RenderUiResult>;
91
+ addCitation(args: AddCitationArgs, ctx: AppToolContext): Promise<AddCitationResult>;
92
+ }
93
+ /** Produced-state events the runtime executor emits at the real side-effect
94
+ * site, so a consumer's eval/completion oracle credits a persisted proposal or
95
+ * artifact. Deliberately substrate-free (no RuntimeStreamEvent import); the
96
+ * consumer maps these onto its own telemetry shape. */
97
+ type AppToolProducedEvent = {
98
+ type: 'proposal_created';
99
+ proposalId: string;
100
+ title: string;
101
+ status: 'pending';
102
+ } | {
103
+ type: 'artifact';
104
+ path: string;
105
+ content: string;
106
+ };
107
+ /** Outcome of one tool dispatch — structurally compatible with the integration
108
+ * tool-outcome union the agent-runtime chat loop already folds into a
109
+ * tool_result. */
110
+ type AppToolOutcome = {
111
+ ok: true;
112
+ result: unknown;
113
+ } | {
114
+ ok: false;
115
+ code: string;
116
+ message: string;
117
+ status?: number;
118
+ };
119
+
120
+ export type { AddCitationArgs as A, RenderUiArgs as R, ScheduleFollowupArgs as S, AddCitationResult as a, AppToolContext as b, AppToolHandlers as c, AppToolOutcome as d, AppToolProducedEvent as e, AppToolTaxonomy as f, RenderUiResult as g, ScheduleFollowupResult as h, SubmitProposalArgs as i, SubmitProposalResult as j };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Web-boundary utilities every agent app's routes hand-roll: JSON body parsing
3
+ * + narrowing, request-context extraction (real client IP behind Cloudflare),
4
+ * a KV-backed sliding-window rate limiter, and security response headers. Pure
5
+ * mechanism — no DB, no domain. The KV is a structural interface so this needs
6
+ * no `@cloudflare/workers-types` dependency.
7
+ */
8
+ type JsonObject = Record<string, unknown>;
9
+ /** Parse + object-narrow a Request body. `[body, null]` on success, `[null,
10
+ * errorResponse]` on a non-object body (callers `if (err) return err`). */
11
+ declare function parseJsonObjectBody(request: Request): Promise<[JsonObject, null] | [null, Response]>;
12
+ /** Narrow one required string field, 400 if missing/empty. */
13
+ declare function requireString(body: JsonObject, field: string): string | Response;
14
+ interface RequestContext {
15
+ ipAddress: string;
16
+ userAgent: string;
17
+ timestamp: string;
18
+ requestId: string;
19
+ }
20
+ /** Extract request context for audit trails. Uses `CF-Connecting-IP` for the
21
+ * real client IP behind Cloudflare. */
22
+ declare function extractRequestContext(request: Request): RequestContext;
23
+ /** Minimal KV contract (Cloudflare `KVNamespace` satisfies it structurally). */
24
+ interface KvLike {
25
+ get(key: string): Promise<string | null>;
26
+ put(key: string, value: string, options?: {
27
+ expirationTtl?: number;
28
+ }): Promise<void>;
29
+ }
30
+ interface RateLimitResult {
31
+ allowed: boolean;
32
+ remaining: number;
33
+ resetAt: number;
34
+ }
35
+ /** KV-backed sliding-window rate limit. Stores recent timestamps per key,
36
+ * prunes the window, allows until `limit` is hit. */
37
+ declare function checkRateLimit(kv: KvLike, key: string, limit: number, windowSeconds: number): Promise<RateLimitResult>;
38
+ interface SecurityHeaderOptions {
39
+ /** Product disclaimer (e.g. "AI-powered tool. Not legal advice."). Omitted if absent. */
40
+ disclaimer?: string;
41
+ /** Data-retention label (e.g. "7-years"). Omitted if absent. */
42
+ retention?: string;
43
+ /** Extra headers to set. */
44
+ extra?: Record<string, string>;
45
+ }
46
+ /** Set standard security headers on a response (HSTS, nosniff, frame-options,
47
+ * referrer-policy, XSS) + optional product disclaimer/retention. The security
48
+ * set is generic; the disclaimer/retention are the product's. */
49
+ declare function addSecurityHeaders(response: Response, opts?: SecurityHeaderOptions): Response;
50
+
51
+ export { type JsonObject, type KvLike, type RateLimitResult, type RequestContext, type SecurityHeaderOptions, addSecurityHeaders, checkRateLimit, extractRequestContext, parseJsonObjectBody, requireString };
@@ -0,0 +1,15 @@
1
+ import {
2
+ addSecurityHeaders,
3
+ checkRateLimit,
4
+ extractRequestContext,
5
+ parseJsonObjectBody,
6
+ requireString
7
+ } from "../chunk-CN75FIPT.js";
8
+ export {
9
+ addSecurityHeaders,
10
+ checkRateLimit,
11
+ extractRequestContext,
12
+ parseJsonObjectBody,
13
+ requireString
14
+ };
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}