experimental-ash 0.58.0 → 0.59.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/CHANGELOG.md +21 -0
- package/dist/docs/public/advanced/execution-model-and-security.md +319 -0
- package/dist/docs/public/advanced/meta.json +1 -0
- package/dist/docs/public/tools.mdx +51 -0
- package/dist/skills/v0-ash-onboarding/SKILL.md +308 -0
- package/dist/src/cli/dev/tui/markdown.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +2 -2
- package/dist/src/compiled/experimental-ai-sdk-code-mode/approval-continuation.d.ts +71 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/approval.d.ts +32 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/code-mode-tool.d.ts +16 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/continuation-capability.d.ts +33 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/errors.d.ts +114 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/fetch-policy.d.ts +21 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/host-interrupt.d.ts +25 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/index.d.ts +11 -144
- package/dist/src/compiled/experimental-ai-sdk-code-mode/index.js +10 -73
- package/dist/src/compiled/experimental-ai-sdk-code-mode/interrupt-continuation.d.ts +32 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/options.d.ts +3 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/run-code-mode.d.ts +12 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/guest-sources.d.ts +3 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/manager.d.ts +22 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/max-workers.d.ts +20 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/protocol.d.ts +49 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/worker-source.d.ts +11 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/serialization.d.ts +5 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/source-cache.d.ts +11 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/telemetry.d.ts +20 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/tool-invocation.d.ts +24 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/tool-prompt.d.ts +3 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/types.d.ts +802 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/tool-auth.d.ts +42 -0
- package/dist/src/execution/tool-auth.js +1 -0
- package/dist/src/harness/action-result-helpers.d.ts +17 -0
- package/dist/src/harness/action-result-helpers.js +1 -1
- package/dist/src/harness/code-mode-interrupt-state.d.ts +26 -0
- package/dist/src/harness/code-mode-interrupt-state.js +1 -0
- package/dist/src/harness/code-mode-lifecycle.js +1 -1
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/node-esm-compat-banner.d.ts +11 -1
- package/dist/src/internal/node-esm-compat-banner.js +3 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +2 -2
- package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +1 -1
- package/dist/src/packages/ash-scaffold/src/web-template.js +2 -14
- package/dist/src/public/channels/slack/inbound.d.ts +2 -0
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/slack/utils.d.ts +1 -0
- package/dist/src/public/channels/slack/utils.js +1 -0
- package/dist/src/public/connections/index.js +1 -1
- package/dist/src/public/definitions/tool.d.ts +61 -1
- package/dist/src/public/definitions/tool.js +1 -1
- package/dist/src/public/next/server.js +1 -1
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.d.ts +61 -0
- package/dist/src/runtime/connections/scoped-authorization.js +1 -0
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/types.d.ts +10 -0
- package/package.json +2 -2
- package/dist/src/harness/code-mode-approval.d.ts +0 -22
- package/dist/src/harness/code-mode-approval.js +0 -1
- package/dist/src/harness/code-mode-connection-auth-state.d.ts +0 -15
- package/dist/src/harness/code-mode-connection-auth-state.js +0 -1
|
@@ -3,6 +3,7 @@ import type { ModelMessage } from "ai";
|
|
|
3
3
|
import type { PublicToolDefinition, ToolModelOutput } from "#shared/tool-definition.js";
|
|
4
4
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
5
5
|
import type { JsonObject } from "#shared/json.js";
|
|
6
|
+
import type { AuthorizationDefinition, NonInteractiveAuthorizationDefinition, TokenResult } from "#runtime/connections/types.js";
|
|
6
7
|
import { type DynamicEvents, type DynamicSentinel } from "#shared/dynamic-tool-definition.js";
|
|
7
8
|
/**
|
|
8
9
|
* Domain input passed to a tool's {@link ToolDefinition.onCompact} hook.
|
|
@@ -40,11 +41,51 @@ export interface NeedsApprovalContext<TInput = Record<string, unknown>> {
|
|
|
40
41
|
readonly toolName: string;
|
|
41
42
|
}
|
|
42
43
|
export type { ToolModelOutput } from "#shared/tool-definition.js";
|
|
44
|
+
/**
|
|
45
|
+
* Authorization strategy declared on a {@link ToolDefinition} via the
|
|
46
|
+
* `auth` field.
|
|
47
|
+
*
|
|
48
|
+
* Accepts the same shapes as a connection's `auth`:
|
|
49
|
+
* - a `getToken`-only object (static API keys, pre-provisioned JWTs);
|
|
50
|
+
* `principalType` may be omitted and defaults to `"app"`.
|
|
51
|
+
* - a full interactive OAuth definition (e.g. `connect("okta")` from
|
|
52
|
+
* `@vercel/connect/ash`, or {@link defineInteractiveAuthorization}).
|
|
53
|
+
*/
|
|
54
|
+
export type ToolAuthDefinition = (Omit<NonInteractiveAuthorizationDefinition, "principalType"> & {
|
|
55
|
+
readonly principalType?: NonInteractiveAuthorizationDefinition["principalType"];
|
|
56
|
+
}) | AuthorizationDefinition;
|
|
43
57
|
/**
|
|
44
58
|
* Authored tool context. Passed as the last argument to
|
|
45
59
|
* {@link ToolDefinition.execute}.
|
|
60
|
+
*
|
|
61
|
+
* Extends {@link SessionContext} with token accessors for tools that
|
|
62
|
+
* declare an `auth` strategy. {@link getToken} and {@link requireAuth}
|
|
63
|
+
* only do useful work when the tool declares `auth`; calling them on a
|
|
64
|
+
* tool without `auth` throws.
|
|
46
65
|
*/
|
|
47
|
-
export type ToolContext = SessionContext
|
|
66
|
+
export type ToolContext = SessionContext & {
|
|
67
|
+
/**
|
|
68
|
+
* Resolves the bearer token for this tool's declared `auth`,
|
|
69
|
+
* consulting the per-step token cache before invoking the authored
|
|
70
|
+
* `getToken`. For interactive strategies a cache miss throws
|
|
71
|
+
* `ConnectionAuthorizationRequiredError`; the runtime catches it,
|
|
72
|
+
* suspends the turn on a framework-owned callback URL, shows a
|
|
73
|
+
* "Sign in" affordance, and re-runs the tool after the OAuth
|
|
74
|
+
* callback completes.
|
|
75
|
+
*
|
|
76
|
+
* Throws when the tool does not declare an `auth` strategy.
|
|
77
|
+
*/
|
|
78
|
+
getToken(): Promise<TokenResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Explicitly signals that the caller must complete this tool's
|
|
81
|
+
* authorization flow before it can proceed. Throws
|
|
82
|
+
* `ConnectionAuthorizationRequiredError`, which the runtime converts
|
|
83
|
+
* into a consent prompt (for interactive strategies) and re-runs the
|
|
84
|
+
* tool after sign-in. Use it to gate a tool on authorization without
|
|
85
|
+
* resolving a token first.
|
|
86
|
+
*/
|
|
87
|
+
requireAuth(): never;
|
|
88
|
+
};
|
|
48
89
|
/**
|
|
49
90
|
* Public tool definition authored in `agent/tools/*.ts`.
|
|
50
91
|
*
|
|
@@ -57,6 +98,21 @@ export type ToolContext = SessionContext;
|
|
|
57
98
|
*/
|
|
58
99
|
export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefinition<TInput, TOutput> & {
|
|
59
100
|
execute(input: TInput, ctx: ToolContext): Promise<TOutput> | TOutput;
|
|
101
|
+
/**
|
|
102
|
+
* Optional authorization strategy for this tool. When set, the tool's
|
|
103
|
+
* execute context gains a working {@link ToolContext.getToken} /
|
|
104
|
+
* {@link ToolContext.requireAuth}, and a thrown
|
|
105
|
+
* `ConnectionAuthorizationRequiredError` (implicit from `getToken()`
|
|
106
|
+
* or explicit via `requireAuth()`) drives the framework's interactive
|
|
107
|
+
* consent flow — the same machinery used by MCP connections, scoped
|
|
108
|
+
* to this tool's name.
|
|
109
|
+
*
|
|
110
|
+
* Use `connect("...")` from `@vercel/connect/ash` for Vercel
|
|
111
|
+
* Connect-backed OAuth, {@link defineInteractiveAuthorization} for a
|
|
112
|
+
* custom interactive flow, or a plain `{ getToken }` object for
|
|
113
|
+
* static/pre-provisioned credentials.
|
|
114
|
+
*/
|
|
115
|
+
auth?: ToolAuthDefinition;
|
|
60
116
|
/**
|
|
61
117
|
* Optional per-tool approval gate. When set, the function's return value
|
|
62
118
|
* determines whether user approval is required before executing this tool.
|
|
@@ -106,6 +162,7 @@ export declare function defineTool<TInputSchema extends StandardJSONSchemaV1<unk
|
|
|
106
162
|
needsApproval?: ToolDefinition<StandardJSONSchemaV1.InferOutput<TInputSchema>, unknown>["needsApproval"];
|
|
107
163
|
toModelOutput?: ToolDefinition<unknown, StandardJSONSchemaV1.InferOutput<TOutputSchema>>["toModelOutput"];
|
|
108
164
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
165
|
+
auth?: ToolAuthDefinition;
|
|
109
166
|
}): ToolDefinition<StandardJSONSchemaV1.InferOutput<TInputSchema>, StandardJSONSchemaV1.InferOutput<TOutputSchema>>;
|
|
110
167
|
export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown, unknown>, TOutput>(definition: {
|
|
111
168
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -115,6 +172,7 @@ export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown,
|
|
|
115
172
|
needsApproval?: ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, unknown>["needsApproval"];
|
|
116
173
|
toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
|
|
117
174
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
175
|
+
auth?: ToolAuthDefinition;
|
|
118
176
|
}): ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, TOutput>;
|
|
119
177
|
export declare function defineTool<TOutputSchema extends StandardJSONSchemaV1<unknown, unknown>>(definition: {
|
|
120
178
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -124,6 +182,7 @@ export declare function defineTool<TOutputSchema extends StandardJSONSchemaV1<un
|
|
|
124
182
|
needsApproval?: ToolDefinition<Record<string, unknown>, unknown>["needsApproval"];
|
|
125
183
|
toModelOutput?: ToolDefinition<unknown, StandardJSONSchemaV1.InferOutput<TOutputSchema>>["toModelOutput"];
|
|
126
184
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
185
|
+
auth?: ToolAuthDefinition;
|
|
127
186
|
}): ToolDefinition<Record<string, unknown>, StandardJSONSchemaV1.InferOutput<TOutputSchema>>;
|
|
128
187
|
export declare function defineTool<TOutput>(definition: {
|
|
129
188
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -133,6 +192,7 @@ export declare function defineTool<TOutput>(definition: {
|
|
|
133
192
|
needsApproval?: ToolDefinition<Record<string, unknown>, unknown>["needsApproval"];
|
|
134
193
|
toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
|
|
135
194
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
195
|
+
auth?: ToolAuthDefinition;
|
|
136
196
|
}): ToolDefinition<Record<string, unknown>, TOutput>;
|
|
137
197
|
export declare function defineTool<TInput = unknown, TOutput = unknown>(definition: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
138
198
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{stampDefinitionKey}from"#public/tool-result-narrowing.js";function defineTool(e){return Object.assign(e,{[TOOL_BRAND]:!0}),stampDefinitionKey(e,`tool:${e.description}`),e}function defineDynamic(t){let n={kind:DYNAMIC_SENTINEL_KIND,events:t.events};return stampDefinitionKey(n,`dynamic:${Object.keys(t.events).join(`,`)}`),n}const DISABLED_TOOL_SENTINEL_KIND=`ash:disabled-tool`;function disableTool(){return{kind:DISABLED_TOOL_SENTINEL_KIND}}function isDisabledToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_TOOL_SENTINEL_KIND}export{defineDynamic,defineTool,disableTool,isDisabledToolSentinel};
|
|
1
|
+
import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{normalizeAuthorizationSpec}from"#runtime/connections/validate-authorization.js";import{stampDefinitionKey}from"#public/tool-result-narrowing.js";function defineTool(e){return e.auth!==void 0&&(e.auth=normalizeAuthorizationSpec(e.auth,`defineTool:`)),Object.assign(e,{[TOOL_BRAND]:!0}),stampDefinitionKey(e,`tool:${e.description}`),e}function defineDynamic(t){let n={kind:DYNAMIC_SENTINEL_KIND,events:t.events};return stampDefinitionKey(n,`dynamic:${Object.keys(t.events).join(`,`)}`),n}const DISABLED_TOOL_SENTINEL_KIND=`ash:disabled-tool`;function disableTool(){return{kind:DISABLED_TOOL_SENTINEL_KIND}}function isDisabledToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_TOOL_SENTINEL_KIND}export{defineDynamic,defineTool,disableTool,isDisabledToolSentinel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ASH_ROUTE_PREFIX}from"#protocol/routes.js";import{join}from"node:path";import{mkdir,open,readFile,rm,stat,writeFile}from"node:fs/promises";import{spawn}from"node:child_process";import{existsSync}from"node:fs";import{resolvePackageRoot}from"#internal/application/package.js";const ASH_BASE_URL_ENV=`ASH_BASE_URL`,DEFAULT_SERVER_READY_TIMEOUT_MS=3e4,DEV_SERVER_REGISTRY_TIMEOUT_MS=3e4,
|
|
1
|
+
import{ASH_ROUTE_PREFIX}from"#protocol/routes.js";import{join}from"node:path";import{mkdir,open,readFile,rm,stat,writeFile}from"node:fs/promises";import{spawn}from"node:child_process";import{existsSync}from"node:fs";import{resolvePackageRoot}from"#internal/application/package.js";const ASH_BASE_URL_ENV=`ASH_BASE_URL`,DEFAULT_SERVER_READY_TIMEOUT_MS=3e4,DEV_SERVER_REGISTRY_TIMEOUT_MS=3e4,SERVER_URL_CANDIDATE_PATTERN=/https?:\/\/[^\s"'<>]+/g,globalStateSymbol=Symbol.for(`experimental-ash.next.state`);function getGlobalState(){let e=globalThis;return e[globalStateSymbol]??={servers:new Map},e[globalStateSymbol]}function joinRoutePrefix(e,t){return`${e.replace(/\/+$/,``)}/${t.replace(/^\/+/,``)}`}function normalizeOrigin(e){return new URL(e).origin}function readAshBaseUrlEnvironment(){let e=process.env[ASH_BASE_URL_ENV];if(!(e===void 0||e.trim().length===0))return normalizeOrigin(e)}function isNodeErrorWithCode(e,t){return e instanceof Error&&`code`in e&&e.code===t}function delay(e){return new Promise(t=>setTimeout(t,e))}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function resolveAshCacheDirectory(e){return join(e,`.ash`)}function resolveAshDevServerRegistryPath(e){return join(resolveAshCacheDirectory(e),`next-dev-server.json`)}function resolveAshDevServerLockPath(e){return join(resolveAshCacheDirectory(e),`next-dev-server.lock`)}function normalizeDevServerRegistry(e){if(isRecord(e)&&!(typeof e.appRoot!=`string`||typeof e.origin!=`string`||typeof e.updatedAt!=`string`)&&!(e.pid!==null&&typeof e.pid!=`number`))try{return{appRoot:e.appRoot,origin:normalizeOrigin(e.origin),pid:e.pid,updatedAt:e.updatedAt}}catch{return}}async function isAshServerHealthy(t){let n=new AbortController,r=setTimeout(()=>{n.abort()},1e3);try{return(await fetch(joinRoutePrefix(t,`${ASH_ROUTE_PREFIX}/health`),{signal:n.signal})).ok}catch{return!1}finally{clearTimeout(r)}}async function readUsableAshDevServerRegistry(e){try{let t=normalizeDevServerRegistry(JSON.parse(await readFile(resolveAshDevServerRegistryPath(e),`utf8`)));return t===void 0||t.appRoot!==e||!await isAshServerHealthy(t.origin)?void 0:(process.env[ASH_BASE_URL_ENV]=t.origin,t.origin)}catch(e){if(isNodeErrorWithCode(e,`ENOENT`))return;throw e}}async function writeAshDevServerRegistry(e,t){await mkdir(resolveAshCacheDirectory(e),{recursive:!0}),await writeFile(resolveAshDevServerRegistryPath(e),`${JSON.stringify({appRoot:e,origin:t.origin,pid:t.process?.pid??null,updatedAt:new Date().toISOString()},null,2)}\n`)}async function removeStaleAshDevServerLock(e){try{let t=await stat(e);Date.now()-t.mtimeMs>3e4&&await rm(e,{force:!0})}catch(e){if(!isNodeErrorWithCode(e,`ENOENT`))throw e}}async function acquireAshDevServerLock(e){let t=resolveAshCacheDirectory(e),i=resolveAshDevServerLockPath(e),a=Date.now()+DEV_SERVER_REGISTRY_TIMEOUT_MS;for(await mkdir(t,{recursive:!0});;)try{let e=await open(i,`wx`);return await e.writeFile(`${String(process.pid)}\n`),await e.close(),async()=>{await rm(i,{force:!0})}}catch(t){if(!isNodeErrorWithCode(t,`EEXIST`))throw t;if(await readUsableAshDevServerRegistry(e)!==void 0)return async()=>{};if(await removeStaleAshDevServerLock(i),Date.now()>a)throw Error(`Timed out after ${DEV_SERVER_REGISTRY_TIMEOUT_MS}ms waiting for another Next.js process to start Ash.`);await delay(100)}}function createAshBinaryPath(){return join(resolvePackageRoot(),`bin`,`ash.js`)}function isLoopbackHostname(e){return e===`localhost`||e===`::1`||e===`[::1]`||/^127(?:\.\d{1,3}){3}$/.test(e)}function parseLocalServerOrigin(e){let t=URL.parse(e);if(!(t===null||t.protocol!==`http:`&&t.protocol!==`https:`||!isLoopbackHostname(t.hostname)||t.port.length===0))return t.origin}function findLocalServerOrigin(e){for(let t of e.matchAll(SERVER_URL_CANDIDATE_PATTERN)){let e=t[0],n=parseLocalServerOrigin(e);if(n!==void 0)return n}}function startServerProcess(e){return new Promise((t,n)=>{let r=spawn(e.command,e.args,{cwd:e.cwd,env:{...process.env,...e.env},stdio:[`ignore`,`pipe`,`pipe`]}),i=setTimeout(()=>{r.kill(),n(Error(`Timed out after ${e.timeoutMs??DEFAULT_SERVER_READY_TIMEOUT_MS}ms waiting for Ash to print its server URL.`))},e.timeoutMs??DEFAULT_SERVER_READY_TIMEOUT_MS),cleanup=()=>{clearTimeout(i),r.off(`error`,handleError),r.off(`exit`,handleEarlyExit)},handleError=e=>{cleanup(),n(e)},handleEarlyExit=(e,t)=>{cleanup(),n(Error(`Ash server process exited before printing its server URL (code ${String(e)}, signal ${String(t)}).`))},handleOutput=e=>{let n=findLocalServerOrigin(e.toString(`utf8`));n!==void 0&&(cleanup(),t({origin:n,process:r}))};r.once(`error`,handleError),r.once(`exit`,handleEarlyExit),r.stdout.on(`data`,e=>{process.stdout.write(e),handleOutput(e)}),r.stderr.on(`data`,e=>{process.stderr.write(e),handleOutput(e)})})}function installProcessShutdown(e){let t=e.process;if(t===void 0)return e;let close=()=>{t.killed||t.kill()};return process.once(`beforeExit`,close),process.once(`exit`,close),e}function startAshDevServer(e){return startServerProcess({args:[createAshBinaryPath(),`dev`,`--no-repl`,`--port`,`0`],command:process.execPath,cwd:e}).then(e=>(process.env[ASH_BASE_URL_ENV]=e.origin,installProcessShutdown(e)))}function startAshProductionServer(e){let n=new URL(e.origin),r=n.port,i=join(e.appRoot,`.output`,`server`,`index.mjs`);if(existsSync(i))return startServerProcess({args:[i],command:process.execPath,cwd:e.appRoot,env:{HOST:n.hostname,NITRO_HOST:n.hostname,NITRO_PORT:r,PORT:r}}).then(installProcessShutdown)}async function resolveSharedAshDevServer(e){let t=await readUsableAshDevServerRegistry(e);if(t!==void 0)return{origin:t};let n=await acquireAshDevServerLock(e);try{let t=await readUsableAshDevServerRegistry(e);if(t!==void 0)return{origin:t};let n=await startAshDevServer(e);return await writeAshDevServerRegistry(e,n),n}finally{await n()}}async function resolveAshDestinationPrefix(e){let t=getGlobalState();if(process.env.NODE_ENV===`production`){if(e.phase===`phase-production-build`)return e.productionDestinationPrefix;let n=`production:${e.appRoot}`,r=t.servers.get(n);return r===void 0&&(r=process.env.VERCEL||e.productionServerOrigin===void 0?void 0:startAshProductionServer({appRoot:e.appRoot,origin:e.productionServerOrigin}),r!==void 0&&(r=r.catch(e=>{throw t.servers.delete(n),e}),t.servers.set(n,r))),r===void 0?e.productionDestinationPrefix:(await r).origin}let n=readAshBaseUrlEnvironment();if(n!==void 0)return n;if(process.env.NODE_ENV!==`development`)return e.productionDestinationPrefix;let r=`dev:${e.appRoot}`,i=t.servers.get(r);return i===void 0&&(i=resolveSharedAshDevServer(e.appRoot).catch(e=>{throw t.servers.delete(r),e}),t.servers.set(r,i)),(await i).origin}export{resolveAshDestinationPrefix};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isObject}from"#shared/guards.js";import{contextStorage}from"#context/container.js";import{ConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{evictCachedToken
|
|
1
|
+
import{isObject}from"#shared/guards.js";import{contextStorage}from"#context/container.js";import{ConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{resolveScopedToken}from"#runtime/connections/scoped-authorization.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{evictCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";var McpConnectionClient=class{#e;#t;#n;#r;#i;constructor(e){this.#i=e}async connect(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#a();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#a(){let e=await resolveHeaders(this.#i),t=this.#i.url;try{return await createMCPClient({transport:{type:`http`,url:t,headers:e}})}catch(n){if(!isMcpHttpFallbackRetryableError(n))throw n;return await createMCPClient({transport:{type:`sse`,url:t,headers:e}})}}async getToolMetadata(){return(await this.#o()).metadata}async getTools(){return(await this.#o()).tools}async executeTool(e,t){try{let{tools:n}=await this.#o(),r=n[e];if(r?.execute===void 0)throw Error(`Tool "${e}" not found in connection "${this.#i.connectionName}".`);return await r.execute(t,{})}catch(e){return await this.#l(e)}}async#o(){if(this.#r!==void 0)return this.#r;if(this.#n!==void 0)return this.#n;this.#n=this.#s();try{return this.#r=await this.#n,this.#r}catch(e){throw this.#n=void 0,e}}async#s(){try{return await this.#c()}catch(e){return await this.#l(e)}}async#c(){let e=await this.connect(),t=await e.listTools(),n=this.#i.tools,r=n===void 0?t.tools:t.tools.filter(e=>passesToolFilter(e.name,n)),i=e.toolsFromDefinitions({tools:r});return{metadata:r.map(e=>({annotations:e.annotations,description:e.description??``,inputSchema:e.inputSchema??{},name:e.name,outputSchema:`outputSchema`in e&&e.outputSchema!==void 0?e.outputSchema:void 0})),tools:i}}async close(){this.#t!==void 0&&(await this.#t.close(),this.#t=void 0),this.#e=void 0,this.#n=void 0,this.#r=void 0}async#l(e){throw isMcpAuthRequiredError(e)?(this.#u(),await this.close(),new ConnectionAuthorizationRequiredError(this.#i.connectionName,{message:`Connection "${this.#i.connectionName}" requires authorization (the server rejected the token).`})):e}#u(){let e=this.#i.authorization;if(e===void 0)return;let n=contextStorage.getStore();if(n!==void 0)try{let t=resolveConnectionPrincipal(this.#i.connectionName,e,n);evictCachedToken(n,this.#i.connectionName,principalKey(t))}catch{}}};function isMcpAuthRequiredError(e){return readHttpStatus(e)===401}function isMcpHttpFallbackRetryableError(e){let t=readHttpStatus(e);return t===400||t===404||t===405}function readHttpStatus(t){for(let n of walkErrorChain(t)){if(!isObject(n))continue;let t=readStatusField(n);if(t!==void 0)return t;let r=n.response;if(isObject(r)){let e=readStatusField(r);if(e!==void 0)return e}if(typeof n.message==`string`){let e=/\bHTTP\s+(\d{3})\b/u.exec(n.message);if(e?.[1]!==void 0)return Number(e[1])}}}function readStatusField(e){if(typeof e.status==`number`)return e.status;if(typeof e.statusCode==`number`)return e.statusCode}function*walkErrorChain(t){let n=t,r=new Set;for(;n!=null&&!r.has(n);){if(r.add(n),yield n,!isObject(n)||!(`cause`in n))return;n=n.cause}}function passesToolFilter(e,t){return t===void 0?!0:`allow`in t?t.allow.includes(e):!t.block.includes(e)}async function resolveHeaders(e){let t={};if(e.authorization!==void 0&&(t.Authorization=`Bearer ${(await resolveToken(e)).token}`),e.headers!==void 0){let n=await resolveHeadersDefinition(e.headers);for(let[r,i]of Object.entries(n)){if(e.authorization!==void 0&&r.toLowerCase()===`authorization`)throw Error(`Connection "${e.connectionName}" headers must not include an "Authorization" key when "authorization" is also provided.`);t[r]=i}}return t}async function resolveToken(e){if(e.authorization===void 0)throw Error(`Connection "${e.connectionName}" does not define authorization.`);return await resolveScopedToken({authorization:e.authorization,connection:{url:e.url},scope:e.connectionName})}async function resolveHeadersDefinition(e){if(typeof e==`function`)return await e();let t={},n=Object.entries(e);for(let[e,r]of n)t[e]=await resolveHeaderValue(r);return t}async function resolveHeaderValue(e){return typeof e==`function`?await e():await e}export{McpConnectionClient,isMcpAuthRequiredError,passesToolFilter,resolveHeaders};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope-parameterized authorization flow shared by MCP connections and
|
|
3
|
+
* authored tools that declare `auth`.
|
|
4
|
+
*
|
|
5
|
+
* A *scope* is the stable identifier the per-step token cache and the
|
|
6
|
+
* framework-owned callback URL are keyed by — a connection name for an
|
|
7
|
+
* MCP connection, a tool name for tool-hosted auth. Everything else
|
|
8
|
+
* (principal resolution, the park/resume webhook dance, the loop guard)
|
|
9
|
+
* is identical across both, so it lives here once instead of being
|
|
10
|
+
* duplicated per caller.
|
|
11
|
+
*/
|
|
12
|
+
import { type AuthorizationSignal } from "#harness/authorization.js";
|
|
13
|
+
import { type AuthorizationDefinition, type ConnectionAuthorizationContext, type TokenResult } from "#runtime/connections/types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Everything the scoped authorization helpers need to drive one
|
|
16
|
+
* authorization strategy: the cache/callback {@link scope}, the
|
|
17
|
+
* authored {@link AuthorizationDefinition}, and the per-scope
|
|
18
|
+
* {@link ConnectionAuthorizationContext} handed to every callback.
|
|
19
|
+
*/
|
|
20
|
+
export interface ScopedAuthorization {
|
|
21
|
+
readonly scope: string;
|
|
22
|
+
readonly authorization: Readonly<AuthorizationDefinition>;
|
|
23
|
+
readonly connection: ConnectionAuthorizationContext;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolves a bearer token for one scope, consulting the per-step token
|
|
27
|
+
* cache before invoking the authored `getToken`.
|
|
28
|
+
*
|
|
29
|
+
* The cache is keyed by `(scope, principalKey(principal))` so concurrent
|
|
30
|
+
* users on one session never alias onto each other's bearer. Outside a
|
|
31
|
+
* runtime scope the cache is unavailable, but `getToken` still runs with
|
|
32
|
+
* a framework-resolved principal so ad-hoc `"app"`-scoped use keeps
|
|
33
|
+
* working; `"user"`-scoped strategies without a context fail fast inside
|
|
34
|
+
* {@link resolveConnectionPrincipal}.
|
|
35
|
+
*
|
|
36
|
+
* `getToken` may throw {@link ConnectionAuthorizationRequiredError};
|
|
37
|
+
* callers catch it and drive {@link startScopedAuthorization}.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveScopedToken(input: ScopedAuthorization): Promise<TokenResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Completes an authorization whose callback arrived this turn, caching
|
|
42
|
+
* the freshly minted token under the scope.
|
|
43
|
+
*
|
|
44
|
+
* Returns `true` when a token was minted. Callers use the boolean as a
|
|
45
|
+
* loop guard: a scope authorized this turn that still reports `Required`
|
|
46
|
+
* on the immediately following call has a token the server itself
|
|
47
|
+
* rejected, so it must fail terminally rather than re-challenge forever.
|
|
48
|
+
*
|
|
49
|
+
* No-op (returns `false`) when the strategy is not interactive or no
|
|
50
|
+
* callback arrived for the scope.
|
|
51
|
+
*/
|
|
52
|
+
export declare function completeScopedAuthorization(input: ScopedAuthorization): Promise<boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* Starts an interactive authorization for one scope and returns the
|
|
55
|
+
* {@link AuthorizationSignal} a tool returns to park the turn.
|
|
56
|
+
*
|
|
57
|
+
* Returns `undefined` when the strategy is not interactive or no
|
|
58
|
+
* callback URL can be minted (for example outside a deployment), so
|
|
59
|
+
* callers can fall through to rethrowing the original `Required` error.
|
|
60
|
+
*/
|
|
61
|
+
export declare function startScopedAuthorization(input: ScopedAuthorization): Promise<AuthorizationSignal | undefined>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{contextStorage,loadContext}from"#context/container.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{readCachedToken,writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";async function resolveScopedToken(t){let{scope:n,authorization:r,connection:i}=t,a=contextStorage.getStore(),l=resolveConnectionPrincipal(n,r,a);if(a===void 0)return await r.getToken({connection:i,principal:l});let u=principalKey(l),d=readCachedToken(a,n,u);if(d!==void 0)return d;let f=await r.getToken({connection:i,principal:l});return writeCachedToken(a,n,u,f),f}async function completeScopedAuthorization(e){let{scope:r,authorization:i,connection:o}=e;if(!supportsInteractiveAuthorization(i))return!1;let l=getAuthorizationResult(r);if(l===void 0)return!1;let u=i,d=loadContext(),f=resolveConnectionPrincipal(r,u,d),p=await u.completeAuthorization({callbackUrl:l.hookUrl,connection:o,principal:f,request:l.callback,state:l.state});return writeCachedToken(d,r,principalKey(f),p),!0}async function startScopedAuthorization(e){let{scope:t,authorization:n,connection:o}=e;if(!supportsInteractiveAuthorization(n))return;let s=getHookUrl(t);if(s===void 0)return;let c=n,l=resolveConnectionPrincipal(t,c),{challenge:u,state:d}=await c.startAuthorization({callbackUrl:s,connection:o,principal:l});return requestAuthorization([{challenge:u,hookUrl:s,name:t,state:d}])}export{completeScopedAuthorization,resolveScopedToken,startScopedAuthorization};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";const logger=createLogger(`framework.connection-search-dynamic`),CONNECTION_SEARCH_OUTPUT_SCHEMA={items:{additionalProperties:!1,properties:{connection:{type:`string`},description:{type:`string`},error:{type:`string`},inputSchema:{type:`object`},needsAuthorization:{type:`boolean`},outputSchema:{type:`object`},qualifiedName:{type:`string`},tool:{type:`string`}},required:[`connection`,`description`],type:`object`},type:`array`},ConnectionSearchResultsKey=new ContextKey(`ash.connectionSearchResults`);function qualifiedConnectionToolName(e,t){return`${e}__${t}`}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n?.authorization&&supportsInteractiveAuthorization(n.authorization))return n.authorization}async function completePendingAuthorizations(e){let n=loadContext(),r=new Set;for(let t of e.getConnections()){let i=getAuthorizationResult(t.connectionName);if(!i)continue;let a=resolveInteractiveAuth(e,t.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(t.connectionName,a),c=await a.completeAuthorization({callbackUrl:i.hookUrl,connection:{url:t.url??``},principal:o,request:i.callback,state:i.state});writeCachedToken(n,t.connectionName,principalKey(o),c),r.add(t.connectionName)}return r}async function executeConnectionSearch(e){let n=loadContext(),i=n.get(ConnectionRegistryKey);if(i===void 0)return[];let s=await completePendingAuthorizations(i),l=e.limit??10,u=tokenize(e.keywords),d=[],p=[],h=e.connection!==void 0&&e.connection!==``?i.getConnections().filter(t=>t.connectionName===e.connection):i.getConnections(),g=[];for(let e of h){let t;try{t=await i.getClient(e.connectionName).getToolMetadata()}catch(t){if(isConnectionAuthorizationRequiredError(t)){if(s.has(e.connectionName)){logger.warn(`connection still unauthorized after authorization`,{connection:e.connectionName}),p.push({connection:e.connectionName,description:e.description,error:`Authorization for "${e.connectionName}" did not take effect; the token was rejected after sign-in.`});continue}let t=resolveInteractiveAuth(i,e.connectionName);if(t){let n=getHookUrl(e.connectionName);if(n){let r=resolveConnectionPrincipal(e.connectionName,t);try{let{challenge:i,state:a}=await t.startAuthorization({callbackUrl:n,connection:{url:e.url??``},principal:r});g.push({name:e.connectionName,challenge:i,hookUrl:n,state:a})}catch(t){logger.warn(`startAuthorization failed`,{connection:e.connectionName,error:t instanceof Error?t:Error(String(t))})}}}p.push({connection:e.connectionName,description:e.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(t)){logger.warn(`connection authorization failed`,{connection:e.connectionName,reason:t.reason,retryable:t.retryable,error:t}),p.push({connection:e.connectionName,description:e.description,error:`Authorization failed for ${e.connectionName}: ${t.message}`});continue}let n=t instanceof Error?t.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:e.connectionName,error:t instanceof Error?t:Error(n)}),p.push({connection:e.connectionName,description:e.description,error:`Failed to load tools for "${e.connectionName}": ${n}`});continue}for(let n of t){let t=scoreMatch(u,n);t>0&&d.push({item:{connection:e.connectionName,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,qualifiedName:`connection__${qualifiedConnectionToolName(e.connectionName,n.name)}`,tool:n.name},score:t})}}if(g.length>0)return requestAuthorization(g);d.sort((e,t)=>t.score-e.score);let _=d.slice(0,l).map(e=>e.item);if(_.length>0){let e=[..._,...p],t=n.get(ConnectionSearchResultsKey)??[],r=new Map(t.map(e=>[e.qualifiedName,e]));for(let e of _)e.qualifiedName&&r.set(e.qualifiedName,e);return n.set(ConnectionSearchResultsKey,[...r.values()]),e}return h.map(e=>p.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}function extractDiscoveredTools(e){let t=new Map;for(let n of e){if(n.role!==`tool`)continue;let e=n.content;for(let n of e){if(n.type!==`tool-result`||n.toolName!==`connection__search`)continue;let e=n.output;if(e==null)continue;let r=typeof e==`object`&&`type`in e&&`value`in e?e.value:e;if(Array.isArray(r))for(let e of r)e.tool&&e.qualifiedName&&t.set(e.qualifiedName,e)}}return[...t.values()]}function createConnectionSearchEvents(){return{"step.started":async(e,n)=>{let a=loadContext().get(ConnectionRegistryKey);if(!a||a.getConnections().length===0)return null;let f=a.getConnections().map(e=>e.connectionName),h=extractDiscoveredTools(n.messages),g=loadContext().get(ConnectionSearchResultsKey)??[],_=new Map;for(let e of g)e.qualifiedName&&_.set(e.qualifiedName,e);for(let e of h)e.qualifiedName&&_.set(e.qualifiedName,e);let v=[..._.values()],y={};y.search={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. \`connection__linear__list_issues\`) in your next response. Available connections: ${f.join(`, `)}.`,inputSchema:{type:`object`,additionalProperties:!1,properties:{keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`},connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`}},required:[`keywords`]},async execute(e){return executeConnectionSearch(e)},outputSchema:CONNECTION_SEARCH_OUTPUT_SCHEMA};for(let e of v){let n=e.connection,f=e.tool,p=a.getConnectionApproval(n);y[qualifiedConnectionToolName(n,f)]={description:e.description,inputSchema:e.inputSchema??{type:`object`},needsApproval:p,outputSchema:e.outputSchema,async execute(e){let a=loadContext().get(ConnectionRegistryKey),p=a.getConnections().find(e=>e.connectionName===n),m=p?.authorization&&supportsInteractiveAuthorization(p.authorization)?p.authorization:void 0,h=!1;if(m){let e=getAuthorizationResult(n);if(e){h=!0;let r=loadContext(),i=resolveConnectionPrincipal(n,m),a=await m.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:p?.url??``},principal:i,request:e.callback,state:e.state});writeCachedToken(r,n,principalKey(i),a)}}try{return await a.getClient(n).executeTool(f,e)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!m)throw e;if(h)throw new ConnectionAuthorizationFailedError(n,{retryable:!1,reason:`token_rejected_after_authorization`,message:`Connection "${n}" rejected the token immediately after authorization.`});let t=getHookUrl(n);if(!t)throw e;let r=resolveConnectionPrincipal(n,m),{challenge:a,state:s}=await m.startAuthorization({callbackUrl:t,connection:{url:p?.url??``},principal:r});return requestAuthorization([{name:n,challenge:a,hookUrl:t,state:s}])}}}}return y}}}function createConnectionSearchResolver(){let e=createConnectionSearchEvents();return{slug:`connection`,eventNames:Object.keys(e),events:e,sourceId:`ash:connection-search-dynamic`,sourceKind:`module`,logicalPath:`ash:framework/connection-search-dynamic`}}export{createConnectionSearchEvents,createConnectionSearchResolver,extractDiscoveredTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{registerDefinitionSource,stampDefinitionKey}from"#public/tool-result-narrowing.js";async function resolveToolDefinition(
|
|
1
|
+
import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeAuthorizationSpec}from"#runtime/connections/validate-authorization.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{registerDefinitionSource,stampDefinitionKey}from"#public/tool-result-narrowing.js";async function resolveToolDefinition(r,a,o){try{let n=expectObjectRecord(await loadResolvedModuleExport({definition:r,kindLabel:`tool`,moduleMap:a,nodeId:o}),describe(r,`to return an object`)),i={kind:`tool`,logicalPath:r.logicalPath,name:r.name},s=`tool-source:${r.sourceId}`;stampDefinitionKey(n,s),registerDefinitionSource(s,i),registerDefinitionSource(`tool:${n.description}`,i);let c=expectFunction(n.execute,describe(r,`to provide an execute function`));return{description:r.description,execute:c,exportName:r.exportName,inputSchema:r.inputSchema,logicalPath:r.logicalPath,name:r.name,outputSchema:r.outputSchema,sourceId:r.sourceId,sourceKind:`module`,...extractOptionalHooks(n,r)}}catch(e){throw e instanceof ResolveAgentError?e:new ResolveAgentError(`Failed to attach the tool execute function from "${r.logicalPath}": ${toErrorMessage(e)}`,{logicalPath:r.logicalPath,sourceId:r.sourceId})}}function extractOptionalHooks(t,n){let i={};return t.onCompact!==void 0&&(i.onCompact=expectFunction(t.onCompact,describe(n,`to provide an onCompact function`))),t.needsApproval!==void 0&&(i.needsApproval=expectFunction(t.needsApproval,describe(n,`to provide a needsApproval function`))),t.toModelOutput!==void 0&&(i.toModelOutput=expectFunction(t.toModelOutput,describe(n,`to provide a toModelOutput function`))),t.inputSchema!==void 0&&isFlexibleSchema(t.inputSchema)&&(i.inputStandardSchema=t.inputSchema),t.outputSchema!==void 0&&isFlexibleSchema(t.outputSchema)&&(i.outputStandardSchema=t.outputSchema),t.auth!==void 0&&(i.auth=normalizeAuthorizationSpec(t.auth,`${describe(n,`to provide a valid auth object`)}:`)),i}function describe(e,t){return`Expected the tool export "${e.exportName??`default`}" from "${e.logicalPath}" ${t}.`}function isFlexibleSchema(e){return typeof e==`object`&&!!e&&`~standard`in e&&typeof e[`~standard`]==`object`}export{resolveToolDefinition};
|
|
@@ -146,6 +146,16 @@ export type ResolvedToolDefinition = Readonly<Optional<InternalToolDefinitionWit
|
|
|
146
146
|
* handlers and the stream. See {@link ToolModelOutput}.
|
|
147
147
|
*/
|
|
148
148
|
readonly toModelOutput?: (output: unknown) => ToolModelOutput | Promise<ToolModelOutput>;
|
|
149
|
+
/**
|
|
150
|
+
* Optional authorization strategy reattached from the authored
|
|
151
|
+
* module at resolve time. Carries live `getToken` /
|
|
152
|
+
* `startAuthorization` / `completeAuthorization` callbacks, so it
|
|
153
|
+
* cannot survive compilation and must be read off the resolved
|
|
154
|
+
* module like {@link execute}. When present, the execution layer
|
|
155
|
+
* builds token accessors onto the tool context and drives the
|
|
156
|
+
* interactive consent flow scoped to this tool's {@link name}.
|
|
157
|
+
*/
|
|
158
|
+
readonly auth?: AuthorizationDefinition;
|
|
149
159
|
};
|
|
150
160
|
/**
|
|
151
161
|
* Runtime-owned authored hook definition resolved from a compiled module
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "experimental-ash",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-framework",
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
"chat": "4.29.0",
|
|
228
228
|
"chokidar": "5.0.0",
|
|
229
229
|
"commander": "14.0.3",
|
|
230
|
-
"experimental-ai-sdk-code-mode": "1.0.
|
|
230
|
+
"experimental-ai-sdk-code-mode": "1.0.14",
|
|
231
231
|
"gray-matter": "4.0.3",
|
|
232
232
|
"jose": "6.2.3",
|
|
233
233
|
"jsonc-parser": "3.3.1",
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ModelMessage } from "ai";
|
|
2
|
-
import type { HarnessSession, SessionStateMap } from "#harness/types.js";
|
|
3
|
-
import type { CodeModeApprovalInterrupt } from "#shared/code-mode.js";
|
|
4
|
-
export interface PendingCodeModeApproval {
|
|
5
|
-
readonly interrupt: CodeModeApprovalInterrupt;
|
|
6
|
-
readonly responseMessages: readonly ModelMessage[];
|
|
7
|
-
}
|
|
8
|
-
export declare function getPendingCodeModeApproval(state: SessionStateMap | undefined): PendingCodeModeApproval | undefined;
|
|
9
|
-
export declare function setPendingCodeModeApproval(input: {
|
|
10
|
-
readonly interrupt: CodeModeApprovalInterrupt;
|
|
11
|
-
readonly responseMessages: readonly ModelMessage[];
|
|
12
|
-
readonly session: HarnessSession;
|
|
13
|
-
}): HarnessSession;
|
|
14
|
-
export declare function clearPendingCodeModeApproval(session: HarnessSession): HarnessSession;
|
|
15
|
-
export declare function replaceCodeModeApprovalInterruptResult(messages: readonly ModelMessage[], interrupt: CodeModeApprovalInterrupt, finalOutput: unknown): ModelMessage[];
|
|
16
|
-
export declare function splitCodeModeOuterResponseMessages(input: {
|
|
17
|
-
readonly messages: readonly ModelMessage[];
|
|
18
|
-
readonly outerToolCallId: string;
|
|
19
|
-
}): {
|
|
20
|
-
readonly promptMessages: readonly ModelMessage[];
|
|
21
|
-
readonly responseMessages: readonly ModelMessage[];
|
|
22
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const PENDING_CODE_MODE_APPROVAL_KEY=`ash.harness.pendingCodeModeApproval`;var CodeModeProtocolError=class extends Error{details;constructor(e,t){super(e),this.details=t,this.name=`CodeModeProtocolError`}};function getPendingCodeModeApproval(t){let n=t?.[PENDING_CODE_MODE_APPROVAL_KEY];if(isRecord(n)&&!(!isCodeModeApprovalInterrupt(n.interrupt)||!Array.isArray(n.responseMessages)))return{interrupt:n.interrupt,responseMessages:n.responseMessages}}function setPendingCodeModeApproval(t){return{...t.session,state:{...t.session.state,[PENDING_CODE_MODE_APPROVAL_KEY]:{interrupt:t.interrupt,responseMessages:t.responseMessages}}}}function clearPendingCodeModeApproval(t){if(t.state?.[PENDING_CODE_MODE_APPROVAL_KEY]===void 0)return t;let n={...t.state};return delete n[PENDING_CODE_MODE_APPROVAL_KEY],{...t,state:Object.keys(n).length>0?n:void 0}}function replaceCodeModeApprovalInterruptResult(e,t,n){let r=0,i=toModelToolOutput(n),a=e.map(e=>{let n=e;if(!Array.isArray(n.content))return e;let a=!1,o=n.content.map(e=>{if(!isRecord(e)||e.type!==`tool-result`||e.toolCallId!==t.continuation.outerToolCallId)return e;if(!toolResultOutputContainsApprovalInterrupt(e.output,t.approvalId))throw new CodeModeProtocolError(`Outer code_mode tool result ${t.continuation.outerToolCallId} does not contain pending approval ${t.approvalId}.`,{approvalId:t.approvalId,outerToolCallId:t.continuation.outerToolCallId});return r++,a=!0,{...e,output:i}});return a?{...e,content:o}:e});if(r!==1)throw new CodeModeProtocolError(`Expected one outer code_mode approval result replacement for ${t.approvalId}, found ${r}.`,{approvalId:t.approvalId,outerToolCallId:t.continuation.outerToolCallId,replacements:r});return a}function splitCodeModeOuterResponseMessages(e){let t=e.messages.findIndex(t=>messageContainsToolResult(t,e.outerToolCallId));if(t<0)return{promptMessages:e.messages,responseMessages:[]};let n=t-1,r=n>=0&&messageContainsToolCall(e.messages[n],e.outerToolCallId)?n:t;return{promptMessages:e.messages.slice(0,r),responseMessages:e.messages.slice(r)}}function messageContainsToolCall(e,t){return e?.role!==`assistant`||!Array.isArray(e.content)?!1:e.content.some(e=>typeof e==`object`&&!!e&&`type`in e&&e.type===`tool-call`&&`toolCallId`in e&&e.toolCallId===t)}function messageContainsToolResult(e,t){return e?.role!==`tool`||!Array.isArray(e.content)?!1:e.content.some(e=>e.type===`tool-result`&&`toolCallId`in e&&e.toolCallId===t)}function toolResultOutputContainsApprovalInterrupt(e,t){return readApprovalInterruptValue(e)?.approvalId===t}function readApprovalInterruptValue(e){if(isCodeModeApprovalInterrupt(e))return e;if(isRecord(e)&&(e.type===`json`||e.type===`text`)&&Object.hasOwn(e,`value`))return readApprovalInterruptValue(e.value)}function toModelToolOutput(e){return typeof e==`string`?{type:`text`,value:e}:{type:`json`,value:e===void 0?null:e}}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function isCodeModeApprovalInterrupt(e){return isRecord(e)&&e.type===`code-mode-approval-required`&&typeof e.approvalId==`string`&&isRecord(e.continuation)&&typeof e.continuation.outerToolCallId==`string`}export{clearPendingCodeModeApproval,getPendingCodeModeApproval,replaceCodeModeApprovalInterruptResult,setPendingCodeModeApproval,splitCodeModeOuterResponseMessages};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ModelMessage } from "ai";
|
|
2
|
-
import type { CodeModeConnectionAuthPayload } from "#runtime/framework-tools/code-mode-connection-auth.js";
|
|
3
|
-
import type { HarnessSession, SessionStateMap } from "#harness/types.js";
|
|
4
|
-
import type { CodeModeInterrupt } from "#shared/code-mode.js";
|
|
5
|
-
export interface PendingCodeModeConnectionAuth {
|
|
6
|
-
readonly interrupt: CodeModeInterrupt<CodeModeConnectionAuthPayload>;
|
|
7
|
-
readonly responseMessages: readonly ModelMessage[];
|
|
8
|
-
}
|
|
9
|
-
export declare function getPendingCodeModeConnectionAuth(state: SessionStateMap | undefined): PendingCodeModeConnectionAuth | undefined;
|
|
10
|
-
export declare function setPendingCodeModeConnectionAuth(input: {
|
|
11
|
-
readonly interrupt: CodeModeInterrupt<CodeModeConnectionAuthPayload>;
|
|
12
|
-
readonly responseMessages: readonly ModelMessage[];
|
|
13
|
-
readonly session: HarnessSession;
|
|
14
|
-
}): HarnessSession;
|
|
15
|
-
export declare function clearPendingCodeModeConnectionAuth(session: HarnessSession): HarnessSession;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";const PENDING_KEY=`ash.harness.pendingCodeModeConnectionAuth`;function getPendingCodeModeConnectionAuth(t){let n=t?.[PENDING_KEY];if(isRecord(n)&&!(!isCodeModeConnectionAuthInterrupt(n.interrupt)||!Array.isArray(n.responseMessages)))return{interrupt:n.interrupt,responseMessages:n.responseMessages}}function setPendingCodeModeConnectionAuth(e){return{...e.session,state:{...e.session.state,[PENDING_KEY]:{interrupt:e.interrupt,responseMessages:e.responseMessages}}}}function clearPendingCodeModeConnectionAuth(e){if(e.state?.[PENDING_KEY]===void 0)return e;let t={...e.state};return delete t[PENDING_KEY],{...e,state:Object.keys(t).length>0?t:void 0}}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{clearPendingCodeModeConnectionAuth,getPendingCodeModeConnectionAuth,setPendingCodeModeConnectionAuth};
|