@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.
- package/README.md +59 -0
- package/dist/billing/index.d.ts +108 -0
- package/dist/billing/index.js +7 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/chunk-45MYQ3GD.js +62 -0
- package/dist/chunk-45MYQ3GD.js.map +1 -0
- package/dist/chunk-4NXVI7PW.js +32 -0
- package/dist/chunk-4NXVI7PW.js.map +1 -0
- package/dist/chunk-7P6VIHI4.js +33 -0
- package/dist/chunk-7P6VIHI4.js.map +1 -0
- package/dist/chunk-C5CREGT2.js +45 -0
- package/dist/chunk-C5CREGT2.js.map +1 -0
- package/dist/chunk-CN75FIPT.js +61 -0
- package/dist/chunk-CN75FIPT.js.map +1 -0
- package/dist/chunk-EDIQ6F55.js +274 -0
- package/dist/chunk-EDIQ6F55.js.map +1 -0
- package/dist/chunk-FS5OUVRB.js +208 -0
- package/dist/chunk-FS5OUVRB.js.map +1 -0
- package/dist/chunk-GMFPCCQZ.js +245 -0
- package/dist/chunk-GMFPCCQZ.js.map +1 -0
- package/dist/chunk-L2TG5DBW.js +74 -0
- package/dist/chunk-L2TG5DBW.js.map +1 -0
- package/dist/chunk-SIDR6BH3.js +57 -0
- package/dist/chunk-SIDR6BH3.js.map +1 -0
- package/dist/chunk-YGUNTIT5.js +48 -0
- package/dist/chunk-YGUNTIT5.js.map +1 -0
- package/dist/crypto/index.d.ts +27 -0
- package/dist/crypto/index.js +13 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/delegation/index.d.ts +50 -0
- package/dist/delegation/index.js +11 -0
- package/dist/delegation/index.js.map +1 -0
- package/dist/eval/index.d.ts +50 -0
- package/dist/eval/index.js +17 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +149 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/index.d.ts +84 -0
- package/dist/integrations/index.js +11 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/redact/index.d.ts +22 -0
- package/dist/redact/index.js +7 -0
- package/dist/redact/index.js.map +1 -0
- package/dist/runtime/index.d.ts +219 -0
- package/dist/runtime/index.js +17 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/stream/index.d.ts +39 -0
- package/dist/stream/index.js +35 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/tangle/index.d.ts +93 -0
- package/dist/tangle/index.js +9 -0
- package/dist/tangle/index.js.map +1 -0
- package/dist/tools/index.d.ts +213 -0
- package/dist/tools/index.js +37 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types-CeWor4bQ.d.ts +120 -0
- package/dist/web/index.d.ts +51 -0
- package/dist/web/index.js +15 -0
- package/dist/web/index.js.map +1 -0
- 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 @@
|
|
|
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":[]}
|