experimental-ash 0.58.1 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/docs/public/tools.mdx +51 -0
  3. package/dist/skills/v0-ash-onboarding/SKILL.md +308 -0
  4. package/dist/src/cli/dev/tui/markdown.js +1 -1
  5. package/dist/src/compiled/.vendor-stamp.json +2 -2
  6. package/dist/src/compiled/experimental-ai-sdk-code-mode/approval-continuation.d.ts +71 -0
  7. package/dist/src/compiled/experimental-ai-sdk-code-mode/approval.d.ts +32 -0
  8. package/dist/src/compiled/experimental-ai-sdk-code-mode/code-mode-tool.d.ts +16 -0
  9. package/dist/src/compiled/experimental-ai-sdk-code-mode/continuation-capability.d.ts +33 -0
  10. package/dist/src/compiled/experimental-ai-sdk-code-mode/errors.d.ts +114 -0
  11. package/dist/src/compiled/experimental-ai-sdk-code-mode/fetch-policy.d.ts +21 -0
  12. package/dist/src/compiled/experimental-ai-sdk-code-mode/host-interrupt.d.ts +25 -0
  13. package/dist/src/compiled/experimental-ai-sdk-code-mode/index.d.ts +11 -144
  14. package/dist/src/compiled/experimental-ai-sdk-code-mode/index.js +10 -73
  15. package/dist/src/compiled/experimental-ai-sdk-code-mode/interrupt-continuation.d.ts +32 -0
  16. package/dist/src/compiled/experimental-ai-sdk-code-mode/options.d.ts +3 -0
  17. package/dist/src/compiled/experimental-ai-sdk-code-mode/run-code-mode.d.ts +12 -0
  18. package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/guest-sources.d.ts +3 -0
  19. package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/manager.d.ts +22 -0
  20. package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/max-workers.d.ts +20 -0
  21. package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/protocol.d.ts +49 -0
  22. package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/worker-source.d.ts +11 -0
  23. package/dist/src/compiled/experimental-ai-sdk-code-mode/serialization.d.ts +5 -0
  24. package/dist/src/compiled/experimental-ai-sdk-code-mode/source-cache.d.ts +11 -0
  25. package/dist/src/compiled/experimental-ai-sdk-code-mode/telemetry.d.ts +20 -0
  26. package/dist/src/compiled/experimental-ai-sdk-code-mode/tool-invocation.d.ts +24 -0
  27. package/dist/src/compiled/experimental-ai-sdk-code-mode/tool-prompt.d.ts +3 -0
  28. package/dist/src/compiled/experimental-ai-sdk-code-mode/types.d.ts +802 -0
  29. package/dist/src/execution/node-step.js +1 -1
  30. package/dist/src/execution/tool-auth.d.ts +42 -0
  31. package/dist/src/execution/tool-auth.js +1 -0
  32. package/dist/src/harness/action-result-helpers.d.ts +17 -0
  33. package/dist/src/harness/action-result-helpers.js +1 -1
  34. package/dist/src/harness/code-mode-interrupt-state.d.ts +26 -0
  35. package/dist/src/harness/code-mode-interrupt-state.js +1 -0
  36. package/dist/src/harness/code-mode-lifecycle.js +1 -1
  37. package/dist/src/harness/code-mode.js +1 -1
  38. package/dist/src/harness/tool-loop.js +1 -1
  39. package/dist/src/internal/application/package.js +1 -1
  40. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  41. package/dist/src/packages/ash-scaffold/src/channels.js +2 -2
  42. package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +1 -1
  43. package/dist/src/packages/ash-scaffold/src/web-template.js +2 -14
  44. package/dist/src/public/connections/index.js +1 -1
  45. package/dist/src/public/definitions/tool.d.ts +61 -1
  46. package/dist/src/public/definitions/tool.js +1 -1
  47. package/dist/src/public/next/server.js +1 -1
  48. package/dist/src/runtime/connections/mcp-client.js +1 -1
  49. package/dist/src/runtime/connections/scoped-authorization.d.ts +61 -0
  50. package/dist/src/runtime/connections/scoped-authorization.js +1 -0
  51. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  52. package/dist/src/runtime/resolve-tool.js +1 -1
  53. package/dist/src/runtime/types.d.ts +10 -0
  54. package/package.json +2 -2
  55. package/dist/src/harness/code-mode-approval.d.ts +0 -22
  56. package/dist/src/harness/code-mode-approval.js +0 -1
  57. package/dist/src/harness/code-mode-connection-auth-state.d.ts +0 -15
  58. package/dist/src/harness/code-mode-connection-auth-state.js +0 -1
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{jsonSchema}from"ai";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveCodeModeEnabled}from"#shared/code-mode.js";import{resolveRuntimeModelReference}from"#runtime/agent/resolve-model.js";import{findRegisteredRuntimeTool}from"#runtime/tools/registry.js";import{SUBAGENT_TOOL_INPUT_SCHEMA}from"#runtime/subagents/registry.js";import{createToolCompactionHandler}from"#execution/tool-compaction.js";const log=createLogger(`execution.node-step`);function createExecutionNodeStep(e){let t=createRuntimeModelResolver(e.compiledArtifactsSource),n=createNodeHarnessTools({node:e.node}),r=createToolCompactionHandler(collectResolvedTools(e.node));return createToolLoopHarness({capabilities:e.capabilities,codeMode:resolveCodeModeEnabled(e.node.agent.config?.experimental?.codeMode),handleEvent:e.handleEvent,mode:e.mode,onCompaction:r,resolveModel:t,runtimeIdentity:buildRuntimeIdentity(e.node),tools:n})}function buildRuntimeIdentity(e){let t=resolveInstalledPackageInfo(),r={agentId:e.turnAgent.id,agentName:e.agent.config?.name,ashVersion:t.version,modelId:e.turnAgent.model.id},i=process.env.VERCEL_GIT_COMMIT_SHA?.trim(),a=process.env.VERCEL_GIT_COMMIT_REF?.trim(),o=process.env.VERCEL_DEPLOYMENT_CREATED_AT?.trim();return i||a||o?{...r,build:{deployedAt:o||void 0,gitBranch:a||void 0,gitSha:i||void 0}}:r}function createRuntimeModelResolver(e){return t=>resolveRuntimeModelReference(t,{compiledArtifactsSource:e})}function collectResolvedTools(e){return[...e.toolRegistry.toolsByName.values()].map(e=>e.definition)}function createNodeHarnessTools(e){let n=new Map;for(let t of e.node.turnAgent.tools){let r=resolveHarnessToolDefinition({node:e.node,tool:t});r!==null&&n.set(t.name,r)}return n.has(`agent`)||n.set(`agent`,{description:`Run a copy of this agent as a subagent with a focused task. The child agent has the same tools and instructions but a fresh conversation history.`,inputSchema:jsonSchema(SUBAGENT_TOOL_INPUT_SCHEMA),name:`agent`,runtimeAction:{kind:`subagent-call`,nodeId:e.node.nodeId,subagentName:`agent`}}),n}function resolveHarnessToolDefinition(e){if(e.tool.kind===`subagent`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,outputSchema:e.tool.outputSchema===void 0?void 0:jsonSchema(e.tool.outputSchema),runtimeAction:{kind:`subagent-call`,nodeId:e.tool.nodeId,subagentName:e.tool.name}};if(e.tool.kind===`remote`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,outputSchema:e.tool.outputSchema===void 0?void 0:jsonSchema(e.tool.outputSchema),runtimeAction:{kind:`remote-agent-call`,nodeId:e.tool.nodeId,remoteAgentName:e.tool.name,subagentName:e.tool.name}};let n=findRegisteredRuntimeTool(e.node.toolRegistry,e.tool.name);if(n===null)return log.warn(`declared tool is not registered — omitting from toolset`,{toolName:e.tool.name,nodeId:e.node.nodeId}),null;let i=n.definition,a=i.sourceId.startsWith(`ash:`),o=i.execute;return{approvalKey:i.approvalKey,description:i.description,execute:o===void 0?void 0:a?o:e=>o(e,buildCallbackContext()),inputSchema:i.inputStandardSchema??jsonSchema(i.inputSchema??{}),name:i.name,needsApproval:i.needsApproval,outputSchema:i.outputStandardSchema??maybeJsonSchema(i.outputSchema),toModelOutput:i.toModelOutput}}function maybeJsonSchema(e){return e===void 0?void 0:jsonSchema(e)}export{createExecutionNodeStep,createNodeHarnessTools};
1
+ import{createLogger}from"#internal/logging.js";import{jsonSchema}from"ai";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveCodeModeEnabled}from"#shared/code-mode.js";import{resolveRuntimeModelReference}from"#runtime/agent/resolve-model.js";import{findRegisteredRuntimeTool}from"#runtime/tools/registry.js";import{SUBAGENT_TOOL_INPUT_SCHEMA}from"#runtime/subagents/registry.js";import{createToolCompactionHandler}from"#execution/tool-compaction.js";import{buildUnauthorizedToolContext,createAuthorizedToolExecute}from"#execution/tool-auth.js";const log=createLogger(`execution.node-step`);function createExecutionNodeStep(e){let t=createRuntimeModelResolver(e.compiledArtifactsSource),n=createNodeHarnessTools({node:e.node}),a=createToolCompactionHandler(collectResolvedTools(e.node));return createToolLoopHarness({capabilities:e.capabilities,codeMode:resolveCodeModeEnabled(e.node.agent.config?.experimental?.codeMode),handleEvent:e.handleEvent,mode:e.mode,onCompaction:a,resolveModel:t,runtimeIdentity:buildRuntimeIdentity(e.node),tools:n})}function buildRuntimeIdentity(e){let t=resolveInstalledPackageInfo(),r={agentId:e.turnAgent.id,agentName:e.agent.config?.name,ashVersion:t.version,modelId:e.turnAgent.model.id},i=process.env.VERCEL_GIT_COMMIT_SHA?.trim(),a=process.env.VERCEL_GIT_COMMIT_REF?.trim(),o=process.env.VERCEL_DEPLOYMENT_CREATED_AT?.trim();return i||a||o?{...r,build:{deployedAt:o||void 0,gitBranch:a||void 0,gitSha:i||void 0}}:r}function createRuntimeModelResolver(e){return t=>resolveRuntimeModelReference(t,{compiledArtifactsSource:e})}function collectResolvedTools(e){return[...e.toolRegistry.toolsByName.values()].map(e=>e.definition)}function createNodeHarnessTools(e){let n=new Map;for(let t of e.node.turnAgent.tools){let r=resolveHarnessToolDefinition({node:e.node,tool:t});r!==null&&n.set(t.name,r)}return n.has(`agent`)||n.set(`agent`,{description:`Run a copy of this agent as a subagent with a focused task. The child agent has the same tools and instructions but a fresh conversation history.`,inputSchema:jsonSchema(SUBAGENT_TOOL_INPUT_SCHEMA),name:`agent`,runtimeAction:{kind:`subagent-call`,nodeId:e.node.nodeId,subagentName:`agent`}}),n}function resolveHarnessToolDefinition(e){if(e.tool.kind===`subagent`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,outputSchema:e.tool.outputSchema===void 0?void 0:jsonSchema(e.tool.outputSchema),runtimeAction:{kind:`subagent-call`,nodeId:e.tool.nodeId,subagentName:e.tool.name}};if(e.tool.kind===`remote`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,outputSchema:e.tool.outputSchema===void 0?void 0:jsonSchema(e.tool.outputSchema),runtimeAction:{kind:`remote-agent-call`,nodeId:e.tool.nodeId,remoteAgentName:e.tool.name,subagentName:e.tool.name}};let n=findRegisteredRuntimeTool(e.node.toolRegistry,e.tool.name);if(n===null)return log.warn(`declared tool is not registered — omitting from toolset`,{toolName:e.tool.name,nodeId:e.node.nodeId}),null;let r=n.definition,i=r.sourceId.startsWith(`ash:`),a=r.execute;return{approvalKey:r.approvalKey,description:r.description,execute:resolveAuthoredExecute({auth:r.auth,isFrameworkTool:i,rawExecute:a,scope:r.name}),inputSchema:r.inputStandardSchema??jsonSchema(r.inputSchema??{}),name:r.name,needsApproval:r.needsApproval,outputSchema:r.outputStandardSchema??maybeJsonSchema(r.outputSchema),toModelOutput:r.toModelOutput}}function resolveAuthoredExecute(e){let{auth:t,isFrameworkTool:n,rawExecute:r,scope:i}=e;if(r===void 0)return;if(n)return r;let a=r;return t===void 0?e=>a(e,buildUnauthorizedToolContext(i)):createAuthorizedToolExecute({auth:t,execute:a,scope:i})}function maybeJsonSchema(e){return e===void 0?void 0:jsonSchema(e)}export{createExecutionNodeStep,createNodeHarnessTools};
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Tool-hosted authorization wiring for authored tools that declare
3
+ * `auth` on {@link defineTool}.
4
+ *
5
+ * Mirrors the connection authorization flow (see
6
+ * `runtime/framework-tools/connection-search-dynamic.ts`) but scopes the
7
+ * per-step token cache and framework-owned callback URL by the tool's
8
+ * path-derived name instead of a connection name. All the shared
9
+ * machinery — principal resolution, cache reads/writes, the park/resume
10
+ * webhook dance, and the loop guard — lives in
11
+ * `runtime/connections/scoped-authorization.ts`; this module is the thin
12
+ * execution-layer adapter that wraps one tool's `execute`.
13
+ */
14
+ import type { ToolContext } from "#public/definitions/tool.js";
15
+ import type { AuthorizationDefinition } from "#runtime/connections/types.js";
16
+ /**
17
+ * Wraps one authored tool's `execute` with the tool-hosted
18
+ * authorization flow.
19
+ *
20
+ * Each invocation:
21
+ * 1. Completes an authorization whose OAuth callback arrived this turn,
22
+ * caching the freshly minted token (the loop-guard flag).
23
+ * 2. Runs the authored `execute` with a {@link ToolContext} whose
24
+ * `getToken()` / `requireAuth()` are bound to this tool's scope.
25
+ * 3. On a thrown `ConnectionAuthorizationRequiredError` — implicit from
26
+ * `getToken()` or explicit via `requireAuth()` — either fails
27
+ * terminally (token rejected immediately after sign-in) or starts the
28
+ * interactive flow and returns an `AuthorizationSignal` to park the
29
+ * turn. Non-interactive strategies (no callback URL) rethrow the
30
+ * original error so the model sees a normal failure.
31
+ */
32
+ export declare function createAuthorizedToolExecute(input: {
33
+ readonly scope: string;
34
+ readonly auth: AuthorizationDefinition;
35
+ readonly execute: (toolInput: unknown, ctx: unknown) => unknown;
36
+ }): (toolInput: unknown) => Promise<unknown>;
37
+ /**
38
+ * Builds the {@link ToolContext} for an authored tool that does **not**
39
+ * declare `auth`. The token accessors are present (the type promises
40
+ * them) but throw, since there is no strategy to resolve a token from.
41
+ */
42
+ export declare function buildUnauthorizedToolContext(scope: string): ToolContext;
@@ -0,0 +1 @@
1
+ import{buildCallbackContext}from"#context/build-callback-context.js";import{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{completeScopedAuthorization,resolveScopedToken,startScopedAuthorization}from"#runtime/connections/scoped-authorization.js";function createAuthorizedToolExecute(e){let{scope:n,auth:i,execute:a}=e,o={authorization:i,connection:{url:``},scope:n};return async e=>{let i=await completeScopedAuthorization(o);try{return await a(e,buildToolContext(o))}catch(e){if(!isConnectionAuthorizationRequiredError(e))throw e;if(i)throw new ConnectionAuthorizationFailedError(n,{message:`Tool "${n}" rejected the token immediately after authorization.`,reason:`token_rejected_after_authorization`,retryable:!1});let r=await startScopedAuthorization(o);if(r===void 0)throw e;return r}}}function buildUnauthorizedToolContext(t){return{...buildCallbackContext(),getToken(){throw noAuthError(t)},requireAuth(){throw noAuthError(t)}}}function buildToolContext(t){return{...buildCallbackContext(),getToken(){return resolveScopedToken(t)},requireAuth(){throw new ConnectionAuthorizationRequiredError(t.scope)}}}function noAuthError(e){return Error(`Tool "${e}" called ctx.getToken()/ctx.requireAuth() but does not declare an "auth" strategy. Add \`auth\` to the tool definition (e.g. \`connect("...")\` from "@vercel/connect/ash").`)}export{buildUnauthorizedToolContext,createAuthorizedToolExecute};
@@ -6,6 +6,23 @@ type ToolResponsePart = Extract<ModelMessage, {
6
6
  type ToolResultPart = Extract<ToolResponsePart, {
7
7
  type: "tool-result";
8
8
  }>;
9
+ /**
10
+ * Builds a `RuntimeToolResultActionResult` from a raw tool output value.
11
+ *
12
+ * This is the single coercion point for `action.result` projection. Both
13
+ * native tool execution (via {@link createRuntimeToolResultFromStepResult} /
14
+ * {@link createRuntimeToolResultFromMessagePart}) and code-mode nested tool
15
+ * calls funnel through here, so the raw-output-vs-`toModelOutput` decision —
16
+ * always raw — is decided once. The output is passed through structurally
17
+ * because it is already JSON-serialized (the AI SDK tool result, or the
18
+ * code-mode worker bridge).
19
+ */
20
+ export declare function createRuntimeToolResultFromValue(input: {
21
+ readonly callId: string;
22
+ readonly toolName: string;
23
+ readonly output: unknown;
24
+ readonly isError?: boolean;
25
+ }): RuntimeToolResultActionResult;
9
26
  /**
10
27
  * Builds a `RuntimeToolResultActionResult` from one AI SDK
11
28
  * {@link TypedToolResult}. Used for tool results captured on the AI SDK
@@ -1 +1 @@
1
- function toJsonValue(e){return e===null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:e instanceof Error?e.message:typeof e==`object`?e:String(e)}function createRuntimeToolResultFromStepResult(e){return{callId:e.toolCallId,kind:`tool-result`,output:toJsonValue(e.output),toolName:e.toolName}}function createRuntimeToolResultFromMessagePart(e){let t={callId:e.toolCallId,kind:`tool-result`,output:toolResultOutputToJsonValue(e.output),toolName:e.toolName};return isToolResultError(e.output)?{...t,isError:!0}:t}function toolResultOutputToJsonValue(e){switch(e.type){case`text`:case`error-text`:return e.value;case`json`:case`error-json`:return toJsonValue(e.value);case`execution-denied`:return{code:`TOOL_EXECUTION_DENIED`,message:e.reason??`Tool execution was denied.`};case`content`:return toJsonValue(e.value)}}function isToolResultError(e){return e.type===`error-json`||e.type===`error-text`||e.type===`execution-denied`}export{createRuntimeToolResultFromMessagePart,createRuntimeToolResultFromStepResult};
1
+ function toJsonValue(e){return e===null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:e instanceof Error?e.message:typeof e==`object`?e:String(e)}function createRuntimeToolResultFromValue(e){let t={callId:e.callId,kind:`tool-result`,output:toJsonValue(e.output),toolName:e.toolName};return e.isError===!0?{...t,isError:!0}:t}function createRuntimeToolResultFromStepResult(e){return createRuntimeToolResultFromValue({callId:e.toolCallId,output:e.output,toolName:e.toolName})}function createRuntimeToolResultFromMessagePart(e){return createRuntimeToolResultFromValue({callId:e.toolCallId,output:toolResultOutputToJsonValue(e.output),toolName:e.toolName,isError:isToolResultError(e.output)})}function toolResultOutputToJsonValue(e){switch(e.type){case`text`:case`error-text`:return e.value;case`json`:case`error-json`:return toJsonValue(e.value);case`execution-denied`:return{code:`TOOL_EXECUTION_DENIED`,message:e.reason??`Tool execution was denied.`};case`content`:return toJsonValue(e.value)}}function isToolResultError(e){return e.type===`error-json`||e.type===`error-text`||e.type===`execution-denied`}export{createRuntimeToolResultFromMessagePart,createRuntimeToolResultFromStepResult,createRuntimeToolResultFromValue};
@@ -0,0 +1,26 @@
1
+ import type { ModelMessage } from "ai";
2
+ import type { HarnessSession, SessionStateMap } from "#harness/types.js";
3
+ import type { CodeModeInterrupt } from "#shared/code-mode.js";
4
+ /**
5
+ * One code-mode invocation parked on a host interrupt (nested-tool approval,
6
+ * connection auth, or any future durable interrupt kind), plus the assistant
7
+ * response messages that produced the outer `code_mode` tool result.
8
+ *
9
+ * Every interrupt kind rides this single pending slot; the kind-specific
10
+ * behavior lives in {@link parkOnCodeModeInterrupt} /
11
+ * {@link continuePendingCodeModeInterrupt}, keyed by `interrupt.payload.kind`.
12
+ */
13
+ export interface PendingCodeModeInterrupt {
14
+ readonly interrupt: CodeModeInterrupt;
15
+ readonly responseMessages: readonly ModelMessage[];
16
+ }
17
+ /** Returns the pending code-mode interrupt stored on the session, if any. */
18
+ export declare function getPendingCodeModeInterrupt(state: SessionStateMap | undefined): PendingCodeModeInterrupt | undefined;
19
+ /** Stores one pending code-mode interrupt on the session. */
20
+ export declare function setPendingCodeModeInterrupt(input: {
21
+ readonly interrupt: CodeModeInterrupt;
22
+ readonly responseMessages: readonly ModelMessage[];
23
+ readonly session: HarnessSession;
24
+ }): HarnessSession;
25
+ /** Clears any pending code-mode interrupt from the session. */
26
+ export declare function clearPendingCodeModeInterrupt(session: HarnessSession): HarnessSession;
@@ -0,0 +1 @@
1
+ const PENDING_KEY=`ash.harness.pendingCodeModeInterrupt`;function getPendingCodeModeInterrupt(t){let n=t?.[PENDING_KEY];if(isRecord(n)&&!(!isCodeModeInterruptShape(n.interrupt)||!Array.isArray(n.responseMessages)))return{interrupt:n.interrupt,responseMessages:n.responseMessages}}function setPendingCodeModeInterrupt(t){return{...t.session,state:{...t.session.state,[PENDING_KEY]:{interrupt:t.interrupt,responseMessages:t.responseMessages}}}}function clearPendingCodeModeInterrupt(t){if(t.state?.[PENDING_KEY]===void 0)return t;let n={...t.state};return delete n[PENDING_KEY],{...t,state:Object.keys(n).length>0?n:void 0}}function isCodeModeInterruptShape(e){return isRecord(e)&&e.type===`code-mode-interrupt`&&typeof e.interruptId==`string`&&typeof e.outerToolCallId==`string`&&isRecord(e.payload)&&typeof e.payload.kind==`string`&&isRecord(e.continuation)&&typeof e.continuation.outerToolCallId==`string`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{clearPendingCodeModeInterrupt,getPendingCodeModeInterrupt,setPendingCodeModeInterrupt};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{createActionResultEvent,createActionsRequestedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{parseJsonValue}from"#shared/json.js";import{contextStorage}from"#context/container.js";import{createRuntimeActionRequestFromToolCall}from"#harness/runtime-actions.js";const log=createLogger(`harness.code-mode-lifecycle`);function createCodeModeLifecycle(e){let r=contextStorage.getStore(),emit=t=>r===void 0?e.emit(t):contextStorage.run(r,()=>e.emit(t));return{onHookError(e,t){log.warn(`code-mode lifecycle hook failed`,{error:e,hook:t.hook})},async onNestedToolCall(t){e.skipReplayed===!0&&t.replayed||await emit(createActionsRequestedEvent({actions:[createRuntimeActionRequestFromToolCall({toolCall:{input:t.input,toolCallId:t.toolCallId,toolName:t.toolName,type:`tool-call`},tools:e.tools})],sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}))},async onNestedToolResult(n){if(e.skipReplayed===!0&&n.replayed||n.status===`interrupted`||n.status===`approval-required`)return;let r={callId:n.toolCallId,kind:`tool-result`,output:n.status===`rejected`?toActionJsonValue(toErrorMessage(n.error)):toActionJsonValue(n.output),toolName:n.toolName};n.status===`rejected`&&Object.assign(r,{isError:!0}),await emit(createActionResultEvent({result:r,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}))}}}function toActionJsonValue(e){try{return parseJsonValue(e)}catch{return toErrorMessage(e)}}export{createCodeModeLifecycle};
1
+ import{createLogger}from"#internal/logging.js";import{createActionResultEvent,createActionsRequestedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{createRuntimeActionRequestFromToolCall}from"#harness/runtime-actions.js";import{createRuntimeToolResultFromValue}from"#harness/action-result-helpers.js";const log=createLogger(`harness.code-mode-lifecycle`);function createCodeModeLifecycle(e){return{onHookError(e,t){log.warn(`code-mode lifecycle hook failed`,{error:e,hook:t.hook})},async onNestedToolCall(t){if(e.skipReplayed===!0&&t.replayed)return;let r={input:t.input,toolCallId:t.toolCallId,toolName:t.toolName,type:`tool-call`};await e.emit(createActionsRequestedEvent({actions:[createRuntimeActionRequestFromToolCall({toolCall:r,tools:e.tools})],sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}))},async onNestedToolResult(n){if(e.skipReplayed===!0&&n.replayed||n.status===`interrupted`)return;let i=createRuntimeToolResultFromValue({callId:n.toolCallId,output:n.status===`rejected`?toErrorMessage(n.error):n.output,toolName:n.toolName,isError:n.status===`rejected`});await e.emit(createActionResultEvent({result:i,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}))}}}export{createCodeModeLifecycle};
@@ -1 +1 @@
1
- import"ai";import{contextStorage}from"#context/container.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{isAuthorizationSignal}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,markCodeModeToolExecutionOptions,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{buildToolSet}from"#harness/tools.js";function createAshCodeModeOptions(e={}){let t={approval:{mode:`interrupt`}};return e.lifecycle!==void 0&&(t.lifecycle=e.lifecycle),t}async function applyCodeModeToToolSet(e){let r={},i={};for(let[t,n]of Object.entries(e.tools)){if(isDirectTool(n,e.harnessTools.get(t))){i[t]=n;continue}r[t]=wrapHostToolForCodeMode(n)}if(Object.keys(r).length>0){let{createCodeModeTool:a}=await loadCodeModeModule();i[CODE_MODE_TOOL_NAME]=a(r,createAshCodeModeOptions({lifecycle:e.lifecycle}))}return{hostTools:r,modelTools:i}}async function buildCodeModeHostTools(t){let n=buildToolSet({approvedTools:t.approvedTools,capabilities:t.capabilities,tools:t.tools}),r=contextStorage.getStore();if(r!==void 0){let e=buildDynamicTools(r);for(let t of e)n[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute,outputSchema:t.outputSchema}}return(await applyCodeModeToToolSet({harnessTools:t.tools,tools:n})).hostTools}function isDirectTool(e,t){return e.execute===void 0||t?.runtimeAction!==void 0}function wrapHostToolForCodeMode(t){let o=t.execute;if(o===void 0)return t;let s=t.needsApproval,c=contextStorage.getStore(),invoke=async(e,t)=>{let s=await resolveExecuteOutput(o(e,markCodeModeToolExecutionOptions(t)));if(isAuthorizationSignal(s)){let{requestCodeModeInterrupt:t}=await loadCodeModeModule(),r=s.challenges[0]?.name;r&&t({args:toCodeModeConnectionAuthArgs(e),challenges:s.challenges,connectionName:r,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:``})}return s},l=typeof s==`function`?(...t)=>c===void 0?s(...t):contextStorage.run(c,()=>s(...t)):s,u={...t,execute:(t,n)=>c===void 0?invoke(t,n):contextStorage.run(c,invoke,t,n)};return l!==void 0&&(u.needsApproval=l),u}async function resolveExecuteOutput(e){if(isAsyncIterable(e)){let t;for await(let n of e)t=n;return t}return await e}function isAsyncIterable(e){return typeof e==`object`&&!!e&&Symbol.asyncIterator in e&&typeof e[Symbol.asyncIterator]==`function`}export{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions};
1
+ import"ai";import{contextStorage}from"#context/container.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{isAuthorizationSignal}from"#harness/authorization.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,markCodeModeToolExecutionOptions,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildToolSet}from"#harness/tools.js";function createAshCodeModeOptions(e={}){let t={approval:{mode:`interrupt`}};return e.lifecycle!==void 0&&(t.lifecycle=e.lifecycle),t}async function applyCodeModeToToolSet(e){let r={},i={};for(let[t,n]of Object.entries(e.tools)){if(isDirectTool(n,e.harnessTools.get(t))){i[t]=n;continue}r[t]=wrapHostToolForCodeMode(n)}if(Object.keys(r).length>0){let{createCodeModeTool:a}=await loadCodeModeModule();i[CODE_MODE_TOOL_NAME]=a(r,createAshCodeModeOptions({lifecycle:e.lifecycle}))}return{hostTools:r,modelTools:i}}async function buildCodeModeHostTools(t){let n=buildToolSet({approvedTools:t.approvedTools,capabilities:t.capabilities,tools:t.tools}),r=contextStorage.getStore();if(r!==void 0){let e=buildDynamicTools(r);for(let t of e)n[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute,outputSchema:t.outputSchema}}return(await applyCodeModeToToolSet({harnessTools:t.tools,tools:n})).hostTools}function isDirectTool(e,t){return e.execute===void 0||t?.runtimeAction!==void 0}function wrapHostToolForCodeMode(e){let t=e.execute;if(t===void 0)return e;let invoke=async(e,i)=>{let o=await resolveExecuteOutput(t(e,markCodeModeToolExecutionOptions(i)));if(isAuthorizationSignal(o)){let{requestCodeModeInterrupt:t}=await loadCodeModeModule(),r=o.challenges[0]?.name;r&&t({args:toCodeModeConnectionAuthArgs(e),challenges:o.challenges,connectionName:r,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:``})}return o};return{...e,execute:(e,t)=>invoke(e,t)}}async function resolveExecuteOutput(e){if(isAsyncIterable(e)){let t;for await(let n of e)t=n;return t}return await e}function isAsyncIterable(e){return typeof e==`object`&&!!e&&Symbol.asyncIterator in e&&typeof e[Symbol.asyncIterator]==`function`}export{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions};
@@ -1 +1 @@
1
- import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createResultCompletedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{ToolLoopAgent,isStepCount}from"ai";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{setAshAttributes}from"#runtime/attributes/emit.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{buildDynamicInstructionMessages}from"#context/dynamic-instruction-lifecycle.js";import{PendingSkillAnnouncementKey}from"#context/dynamic-skill-lifecycle.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,applySystemCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{clearPendingCodeModeApproval,getPendingCodeModeApproval,replaceCodeModeApprovalInterruptResult,setPendingCodeModeApproval}from"#harness/code-mode-approval.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{accumulateTurnUsage,getTurnUsageState,setTurnUsageState}from"#harness/turn-tag-state.js";import{buildTelemetryRuntimeContext}from"#harness/instrumentation-runtime-context.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{clearPendingCodeModeConnectionAuth,getPendingCodeModeConnectionAuth,setPendingCodeModeConnectionAuth}from"#harness/code-mode-connection-auth-state.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{extractWorkflowStreamWriteErrorDetails}from"#harness/workflow-stream-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";import{FINAL_OUTPUT_TOOL_NAME,buildFinalOutputTool}from"#runtime/framework-tools/final-output.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t,n){if(e===void 0)return;let r={};for(let e of Object.keys(n??{}))r[e]=!0;return{functionId:e.functionId??t,includeRuntimeContext:r,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`ash.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let o=a===void 0?void 0:trace.getTracer(`ash`),s=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(o&&hasStepInput(t)){let t=a?.functionId??s,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=o.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(o,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(o,c,l){let p=o;l&&(p=setTurnTraceState(p,l.spanContext()));let _=getHarnessEmissionState(p.state),y=consumeDeferredStepInput({input:c,session:p});p=y.session;let b=await resolvePendingRuntimeActions({emit:n,session:p,stepInput:y.input});if(b.outcome===`unresolved`)return{next:null,session:b.session};p=b.session;let T=resolvePendingInput({history:b.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:p,stepInput:y.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};n&&hasStepInput(c)&&(_=await emitTurnPreamble(n,c??{},_,t.runtimeIdentity),p=setHarnessEmissionState(p,_),l&&l.setAttribute(`ash.turn.id`,_.turnId)),p=T.session;let k=T.messages;if(y.input?.context!==void 0)for(let e of y.input.context)k.push({content:e,role:`user`});if(y.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(y.input.message);k.push({content:e,role:`user`})}let A=await t.resolveModel(p.agent.modelReference),j=detectPromptCachePath(A),M=j.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,N=buildGatewayAttributionHeaders(A,t.runtimeIdentity);({messages:k,session:p}=await maybeCompact({emit:n,emissionState:_,headers:N,messages:k,model:A,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:p,telemetry:enrichTelemetry(a,s)??void 0}));let P=getApprovedTools(p),F=contextStorage.getStore(),I=await hydrateSandboxAttachments(k),L=[],R=[];for(let e of I)e.role===`system`?L.push(e):R.push(e);if(F!==void 0){L.push(...buildDynamicInstructionMessages(F));let e=F.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&L.push({role:`system`,content:e})}let z=R,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=p.agent.system?[{role:`system`,content:p.agent.system}]:[],r=L.length>0||t.length>0?[...t,...n,...L]:void 0,i=r!==void 0&&M?applySystemCacheBreakpoint(r,M):r??p.agent.system??void 0;return{instructions:i,telemetryRuntimeContext:buildTelemetryRuntimeContext({ashVersion,authored:a,emissionState:_,environment,modelInput:{instructions:i,messages:z},session:p})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:o}=e.preparedInput??prepareModelCallInput(e.extraSystemNote),c=t.codeMode===!0,l=await buildToolSetWithProviderTools({approvedTools:P,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:p.agent.modelReference,tools:t.tools});if(F!==void 0){let e=buildDynamicTools(F);for(let t of e)l[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute,outputSchema:t.outputSchema}}p.outputSchema!==void 0&&(l[FINAL_OUTPUT_TOOL_NAME]=buildFinalOutputTool(p.outputSchema));let u=c?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:_,tools:t.tools}),tools:l})).modelTools:l,m=M?applyLastToolCacheBreakpoint(u,M):u,h=resolveGatewayPinForStep({cachePath:j,modelReference:p.agent.modelReference,tools:m}),g=buildStepHooks({cachePath:j,emit:n,emissionState:_,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:h,marker:M,session:p}),v=new ToolLoopAgent({headers:N,instructions:i,model:A,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:g.onStepFinish,prepareStep:g.prepareStep,runtimeContext:o,stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,s,o),tools:m});return runModelCallWithRetries(async()=>{if(n){let e=await v.stream({messages:z}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,_,e.fullStream),a=await g.stepResult;return await emitStepActions(n,_,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME,FINAL_OUTPUT_TOOL_NAME]),inlineActionResultCallIds:r,tools:t.tools}),i.length>0?{content:a.content,finishReason:a.finishReason,response:{...a.response,messages:[{role:`tool`,content:[...i]},...a.response.messages]},text:a.text,toolCalls:a.toolCalls,toolResults:a.toolResults,usage:a.usage}:a}return await v.generate({messages:z}),await g.stepResult},{sessionId:p.sessionId,turnId:_.turnId})},B=prepareModelCallInput();n&&await emitStepStarted(n,_,k);let V=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:k,runStep,session:p});if(V!==null)return V;let H=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:k,runStep,session:p});if(H!==null)return H;let U;try{U=await runOneModelCall({preparedInput:B,suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:p.sessionId,turnId:_.turnId});if(r.outcome===`recovered`)U=r.result;else{let t=r.error;if(l&&recordErrorOnSpan(l,t),!n)throw t;let a=extractWorkflowStreamWriteErrorDetails(t);if(a!==null){let r=createErrorId();return log.error(`workflow stream write failed — parking session for retry by the user`,{...a,errorId:r,error:t,sessionId:p.sessionId,turnId:_.turnId}),_=await emitRecoverableFailedTurn(n,_,{code:`WORKFLOW_STREAM_WRITE_FAILED`,details:{...a,errorId:r},message:toErrorMessage(t)}),{next:null,session:setHarnessEmissionState(p,_)}}let o=classifyModelCallError(t),s=createErrorId(),c=o===`terminal`?summarizeKnownModelCallConfigError(t):null,d=c===null?summarizeKnownModelCallRequestError(t):null,f=c?.message??d?.message??toErrorMessage(t),m=extractModelCallErrorDetails(t),h=buildModelCallFailureDetails({configSummary:c,error:t,errorId:s,modelCallDetails:m,requestSummary:d}),g=buildModelCallFailureLogFields({error:t,errorId:s,modelCallDetails:m,requestSummary:d,sessionId:p.sessionId,turnId:_.turnId});return o===`terminal`?(c===null?log.error(d?.message??`model call failed terminally`,g):log.error(`${c.name}: ${c.message}`,{errorId:s,sessionId:p.sessionId,turnId:_.turnId}),await emitFailedStep(n,_,{code:`MODEL_CALL_FAILED`,details:h,message:f,sessionId:p.sessionId}),{next:{done:!0,output:``},session:p}):(log.error(d?.message??`model call failed — parking session for retry by the user`,g),_=await emitRecoverableFailedTurn(n,_,{code:`MODEL_CALL_FAILED`,details:h,message:f}),{next:null,session:setHarnessEmissionState(p,_)})}}let W=accumulateTurnUsage({previous:getTurnUsageState(p.state),turnId:_.turnId,usage:U.usage??{}});p=setTurnUsageState(p,W);let G;try{G=formatLanguageModelGatewayId(A)}catch{G=void 0}return await setAshAttributes({"$ash.model":G,"$ash.input_tokens":W.inputTokens,"$ash.output_tokens":W.outputTokens,"$ash.cache_read_tokens":W.cacheReadTokens,"$ash.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:_,promptMessages:k,result:U,runStep,session:p})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`failed`,error:e.error};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`failed`,error:e.error};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});try{return{outcome:`recovered`,result:await e.runOneModelCall({disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n),suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:o}=e,{emissionState:s,session:l}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...l,compaction:createNextCompactionConfig(l.compaction,r,i)};if(t.codeMode===!0){let{getCodeModeInterrupt:e,isCodeModeApprovalInterrupt:a}=await loadCodeModeModule(),o=e(i);if(o!==void 0){if(isCodeModeConnectionAuthInterrupt(o))return parkOnCodeModeConnectionAuth({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u});if(a(o))return parkOnCodeModeApproval({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u})}}let p=extractToolApprovalInputRequests({content:i.content??[]}),m=new Set(p.map(e=>e.action.callId)),h=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:m}),g=[...p,...h],v=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(v.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:v,event:{sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId},responseMessages:u,session:{...f,history:[...r]}}),s)};if(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId})),t.mode===`conversation`&&(s=await emitTurnEpilogue(n,s,t.mode),e=setHarnessEmissionState(e,s))),{next:null,session:e}}let x=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(x&&isAuthorizationSignal(x.output)){let{challenges:e}=x.output;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId}));return{next:null,session:setHarnessEmissionState({...f,history:[...r],state:setPendingAuthorization(f.state,{challenges:e})},s)}}let S=pruneToolResults(r),C=S!==r,w=f.compaction;C&&w.lastKnownInputTokens!==void 0&&(w={recentWindowSize:w.recentWindowSize,threshold:w.threshold});let E=[...S,...u],D={...f,compaction:w,history:E},O=!(D.outputSchema!==void 0&&extractFinalOutput(i)!==void 0)&&(u.at(-1)?.role===`tool`||hasDeferredStepInput(D));return!O&&D.outputSchema===void 0&&d===null&&log.warn(`model completed terminal step without visible assistant text`,{finishReason:i.finishReason,mode:t.mode,responseMessageCount:u.length,responseMessageRoles:u.map(e=>e.role),sequence:s.sequence,sessionId:l.sessionId,stepIndex:s.stepIndex,turnId:s.turnId}),O?(n&&(s=advanceStep(s),D=setHarnessEmissionState(D,s)),{next:o,session:D}):t.mode===`task`?finishTaskTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D,stepOutput:d}):finishConversationTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D})}const OUTPUT_SCHEMA_NOT_FULFILLED={code:`OUTPUT_SCHEMA_NOT_FULFILLED`,message:`The agent could not produce a result matching the requested schema.`};function extractFinalOutput(e){return(e.toolCalls??[]).find(e=>e.toolName===FINAL_OUTPUT_TOOL_NAME)?.input}function persistStructuredAssistantTurn(e,t,n){return{...e,history:[...t,{content:JSON.stringify(n),role:`assistant`}],outputSchema:void 0}}async function emitStructuredResult(e,t,n,r){return await e(createResultCompletedEvent({result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),emitTurnEpilogue(e,t,r)}async function finishTaskTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i,stepOutput:a}=e,{emissionState:o,session:s}=e;if(i===void 0)return t&&(o=await emitTurnEpilogue(t,o,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:a??``},session:s};let c=extractFinalOutput(r);return c===void 0?(t&&await emitFailedStep(t,o,{...OUTPUT_SCHEMA_NOT_FULFILLED,sessionId:s.sessionId}),{next:{done:!0,isError:!0,output:OUTPUT_SCHEMA_NOT_FULFILLED.message},session:s}):(s=persistStructuredAssistantTurn(s,n,c),t&&(o=await emitStructuredResult(t,o,c,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:c},session:s})}async function finishConversationTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i}=e,{emissionState:a,session:o}=e;if(i===void 0)return t&&(a=await emitTurnEpilogue(t,a,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o};let s=extractFinalOutput(r);return s===void 0?(t&&(a=await emitRecoverableFailedTurn(t,a,OUTPUT_SCHEMA_NOT_FULFILLED),o=setHarnessEmissionState(o,a)),{next:null,session:o}):(o=persistStructuredAssistantTurn(o,n,s),t&&(a=await emitStructuredResult(t,a,s,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o})}async function continuePendingCodeModeApproval(e){let t=getPendingCodeModeApproval(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,getCodeModeApprovalResponse:i,isCodeModeApprovalInterrupt:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=i([...e.messages],t.interrupt);if(s===void 0)return{next:null,session:e.session};let c=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools}),l;try{l=await n({approvalResponse:s,interrupt:t.interrupt,options:createAshCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),tools:c})}catch(e){logError(log,`code-mode approval continuation failed`,e),l={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let d=o(l),f=d.status===`interrupted`?d.interrupt:d.output,p=replaceCodeModeApprovalInterruptResult([...e.session.history,...t.responseMessages],t.interrupt,f),m=clearPendingCodeModeApproval({...e.session,history:p});if(d.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeConnectionAuth({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}if(a(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeApproval({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:m}}async function parkOnCodeModeConnectionAuth(e){let t=[...e.interrupt.payload.challenges??[]];if(e.emit)for(let n of t)await e.emit(createAuthorizationRequiredEvent({authorization:n.challenge,name:n.name,description:n.challenge.instructions??`Authorization required for ${n.name}`,webhookUrl:n.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeConnectionAuth({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages],state:setPendingAuthorization(e.baseSession.state,{challenges:t})}})}}async function continuePendingCodeModeConnectionAuth(e){let t=getPendingCodeModeConnectionAuth(e.session.state);if(t===void 0)return null;let{continueCodeModeInterrupt:n,isCodeModeApprovalInterrupt:i,replaceCodeModeInterruptResult:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools}),c=e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools}),l;try{l=await n({interrupt:t.interrupt,resolution:{status:`authorized`},tools:s,options:createAshCodeModeOptions({lifecycle:c})})}catch(e){logError(log,`code-mode interrupt continuation failed`,e),l={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let d=o(l),f=d.status===`interrupted`?d.interrupt:d.output,p=a([...e.session.history,...t.responseMessages],t.interrupt.pendingContinuation,f),m=clearPendingCodeModeConnectionAuth({...e.session,history:p});if(d.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeConnectionAuth({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}if(i(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeApproval({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:m}}async function parkOnCodeModeApproval(e){let{toCodeModeApprovalMessages:t}=await loadCodeModeModule(),n=t(e.interrupt),r=extractToolApprovalInputRequests({content:extractAssistantContent(n)}),i=setPendingInputBatch({requests:r,responseMessages:n,session:setPendingCodeModeApproval({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages]}})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:r,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);i=setHarnessEmissionState(i,t)}return{next:null,session:i}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction){let t=await e.onCompaction(i);i=t.session;for(let e of t.messages)r.push(e)}return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}export{createToolLoopHarness};
1
+ import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createResultCompletedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{ToolLoopAgent,isStepCount}from"ai";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{setAshAttributes}from"#runtime/attributes/emit.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{buildDynamicInstructionMessages}from"#context/dynamic-instruction-lifecycle.js";import{PendingSkillAnnouncementKey}from"#context/dynamic-skill-lifecycle.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,applySystemCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{clearPendingCodeModeInterrupt,getPendingCodeModeInterrupt,setPendingCodeModeInterrupt}from"#harness/code-mode-interrupt-state.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{accumulateTurnUsage,getTurnUsageState,setTurnUsageState}from"#harness/turn-tag-state.js";import{buildTelemetryRuntimeContext}from"#harness/instrumentation-runtime-context.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{extractWorkflowStreamWriteErrorDetails}from"#harness/workflow-stream-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";import{FINAL_OUTPUT_TOOL_NAME,buildFinalOutputTool}from"#runtime/framework-tools/final-output.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t,n){if(e===void 0)return;let r={};for(let e of Object.keys(n??{}))r[e]=!0;return{functionId:e.functionId??t,includeRuntimeContext:r,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`ash.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let o=a===void 0?void 0:trace.getTracer(`ash`),s=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(o&&hasStepInput(t)){let t=a?.functionId??s,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=o.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(o,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(o,c,l){let p=o;l&&(p=setTurnTraceState(p,l.spanContext()));let _=getHarnessEmissionState(p.state),y=consumeDeferredStepInput({input:c,session:p});p=y.session;let b=await resolvePendingRuntimeActions({emit:n,session:p,stepInput:y.input});if(b.outcome===`unresolved`)return{next:null,session:b.session};p=b.session;let T=resolvePendingInput({history:b.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:p,stepInput:y.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};n&&hasStepInput(c)&&(_=await emitTurnPreamble(n,c??{},_,t.runtimeIdentity),p=setHarnessEmissionState(p,_),l&&l.setAttribute(`ash.turn.id`,_.turnId)),p=T.session;let k=T.messages;if(y.input?.context!==void 0)for(let e of y.input.context)k.push({content:e,role:`user`});if(y.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(y.input.message);k.push({content:e,role:`user`})}let A=await t.resolveModel(p.agent.modelReference),j=detectPromptCachePath(A),M=j.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,N=buildGatewayAttributionHeaders(A,t.runtimeIdentity);({messages:k,session:p}=await maybeCompact({emit:n,emissionState:_,headers:N,messages:k,model:A,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:p,telemetry:enrichTelemetry(a,s)??void 0}));let P=getApprovedTools(p),F=contextStorage.getStore(),I=await hydrateSandboxAttachments(k),L=[],R=[];for(let e of I)e.role===`system`?L.push(e):R.push(e);if(F!==void 0){L.push(...buildDynamicInstructionMessages(F));let e=F.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&L.push({role:`system`,content:e})}let z=R,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=p.agent.system?[{role:`system`,content:p.agent.system}]:[],r=L.length>0||t.length>0?[...t,...n,...L]:void 0,i=r!==void 0&&M?applySystemCacheBreakpoint(r,M):r??p.agent.system??void 0;return{instructions:i,telemetryRuntimeContext:buildTelemetryRuntimeContext({ashVersion,authored:a,emissionState:_,environment,modelInput:{instructions:i,messages:z},session:p})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:o}=e.preparedInput??prepareModelCallInput(e.extraSystemNote),c=t.codeMode===!0,l=await buildToolSetWithProviderTools({approvedTools:P,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:p.agent.modelReference,tools:t.tools});if(F!==void 0){let e=buildDynamicTools(F);for(let t of e)l[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute,outputSchema:t.outputSchema}}p.outputSchema!==void 0&&(l[FINAL_OUTPUT_TOOL_NAME]=buildFinalOutputTool(p.outputSchema));let u=c?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:_,tools:t.tools}),tools:l})).modelTools:l,m=M?applyLastToolCacheBreakpoint(u,M):u,h=resolveGatewayPinForStep({cachePath:j,modelReference:p.agent.modelReference,tools:m}),g=buildStepHooks({cachePath:j,emit:n,emissionState:_,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:h,marker:M,session:p}),v=new ToolLoopAgent({headers:N,instructions:i,model:A,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:g.onStepFinish,prepareStep:g.prepareStep,runtimeContext:o,stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,s,o),tools:m});return runModelCallWithRetries(async()=>{if(n){let e=await v.stream({messages:z}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,_,e.fullStream),a=await g.stepResult;return await emitStepActions(n,_,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME,FINAL_OUTPUT_TOOL_NAME]),inlineActionResultCallIds:r,tools:t.tools}),i.length>0?{content:a.content,finishReason:a.finishReason,response:{...a.response,messages:[{role:`tool`,content:[...i]},...a.response.messages]},text:a.text,toolCalls:a.toolCalls,toolResults:a.toolResults,usage:a.usage}:a}return await v.generate({messages:z}),await g.stepResult},{sessionId:p.sessionId,turnId:_.turnId})},B=prepareModelCallInput();n&&await emitStepStarted(n,_,k);let V=await continuePendingCodeModeInterrupt({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:k,runStep,session:p});if(V!==null)return V;let H;try{H=await runOneModelCall({preparedInput:B,suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:p.sessionId,turnId:_.turnId});if(r.outcome===`recovered`)H=r.result;else{let t=r.error;if(l&&recordErrorOnSpan(l,t),!n)throw t;let a=extractWorkflowStreamWriteErrorDetails(t);if(a!==null){let r=createErrorId();return log.error(`workflow stream write failed — parking session for retry by the user`,{...a,errorId:r,error:t,sessionId:p.sessionId,turnId:_.turnId}),_=await emitRecoverableFailedTurn(n,_,{code:`WORKFLOW_STREAM_WRITE_FAILED`,details:{...a,errorId:r},message:toErrorMessage(t)}),{next:null,session:setHarnessEmissionState(p,_)}}let o=classifyModelCallError(t),s=createErrorId(),c=o===`terminal`?summarizeKnownModelCallConfigError(t):null,d=c===null?summarizeKnownModelCallRequestError(t):null,f=c?.message??d?.message??toErrorMessage(t),m=extractModelCallErrorDetails(t),h=buildModelCallFailureDetails({configSummary:c,error:t,errorId:s,modelCallDetails:m,requestSummary:d}),g=buildModelCallFailureLogFields({error:t,errorId:s,modelCallDetails:m,requestSummary:d,sessionId:p.sessionId,turnId:_.turnId});return o===`terminal`?(c===null?log.error(d?.message??`model call failed terminally`,g):log.error(`${c.name}: ${c.message}`,{errorId:s,sessionId:p.sessionId,turnId:_.turnId}),await emitFailedStep(n,_,{code:`MODEL_CALL_FAILED`,details:h,message:f,sessionId:p.sessionId}),{next:{done:!0,output:``},session:p}):(log.error(d?.message??`model call failed — parking session for retry by the user`,g),_=await emitRecoverableFailedTurn(n,_,{code:`MODEL_CALL_FAILED`,details:h,message:f}),{next:null,session:setHarnessEmissionState(p,_)})}}let U=accumulateTurnUsage({previous:getTurnUsageState(p.state),turnId:_.turnId,usage:H.usage??{}});p=setTurnUsageState(p,U);let W;try{W=formatLanguageModelGatewayId(A)}catch{W=void 0}return await setAshAttributes({"$ash.model":W,"$ash.input_tokens":U.inputTokens,"$ash.output_tokens":U.outputTokens,"$ash.cache_read_tokens":U.cacheReadTokens,"$ash.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:_,promptMessages:k,result:H,runStep,session:p})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`failed`,error:e.error};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`failed`,error:e.error};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});try{return{outcome:`recovered`,result:await e.runOneModelCall({disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n),suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:o}=e,{emissionState:s,session:l}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...l,compaction:createNextCompactionConfig(l.compaction,r,i)};if(t.codeMode===!0){let{getCodeModeInterrupt:e}=await loadCodeModeModule(),a=e(i);if(a!==void 0)return parkOnCodeModeInterrupt({baseSession:f,config:t,emit:n,emissionState:s,interrupt:a,promptMessages:r,responseMessages:u})}let p=extractToolApprovalInputRequests({content:i.content??[]}),m=new Set(p.map(e=>e.action.callId)),h=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:m}),g=[...p,...h],v=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(v.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:v,event:{sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId},responseMessages:u,session:{...f,history:[...r]}}),s)};if(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId})),t.mode===`conversation`&&(s=await emitTurnEpilogue(n,s,t.mode),e=setHarnessEmissionState(e,s))),{next:null,session:e}}let x=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(x&&isAuthorizationSignal(x.output)){let{challenges:e}=x.output;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId}));return{next:null,session:setHarnessEmissionState({...f,history:[...r],state:setPendingAuthorization(f.state,{challenges:e})},s)}}let S=pruneToolResults(r),C=S!==r,w=f.compaction;C&&w.lastKnownInputTokens!==void 0&&(w={recentWindowSize:w.recentWindowSize,threshold:w.threshold});let E=[...S,...u],D={...f,compaction:w,history:E},O=!(D.outputSchema!==void 0&&extractFinalOutput(i)!==void 0)&&(u.at(-1)?.role===`tool`||hasDeferredStepInput(D));return!O&&D.outputSchema===void 0&&d===null&&log.warn(`model completed terminal step without visible assistant text`,{finishReason:i.finishReason,mode:t.mode,responseMessageCount:u.length,responseMessageRoles:u.map(e=>e.role),sequence:s.sequence,sessionId:l.sessionId,stepIndex:s.stepIndex,turnId:s.turnId}),O?(n&&(s=advanceStep(s),D=setHarnessEmissionState(D,s)),{next:o,session:D}):t.mode===`task`?finishTaskTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D,stepOutput:d}):finishConversationTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D})}const OUTPUT_SCHEMA_NOT_FULFILLED={code:`OUTPUT_SCHEMA_NOT_FULFILLED`,message:`The agent could not produce a result matching the requested schema.`};function extractFinalOutput(e){return(e.toolCalls??[]).find(e=>e.toolName===FINAL_OUTPUT_TOOL_NAME)?.input}function persistStructuredAssistantTurn(e,t,n){return{...e,history:[...t,{content:JSON.stringify(n),role:`assistant`}],outputSchema:void 0}}async function emitStructuredResult(e,t,n,r){return await e(createResultCompletedEvent({result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),emitTurnEpilogue(e,t,r)}async function finishTaskTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i,stepOutput:a}=e,{emissionState:o,session:s}=e;if(i===void 0)return t&&(o=await emitTurnEpilogue(t,o,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:a??``},session:s};let c=extractFinalOutput(r);return c===void 0?(t&&await emitFailedStep(t,o,{...OUTPUT_SCHEMA_NOT_FULFILLED,sessionId:s.sessionId}),{next:{done:!0,isError:!0,output:OUTPUT_SCHEMA_NOT_FULFILLED.message},session:s}):(s=persistStructuredAssistantTurn(s,n,c),t&&(o=await emitStructuredResult(t,o,c,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:c},session:s})}async function finishConversationTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i}=e,{emissionState:a,session:o}=e;if(i===void 0)return t&&(a=await emitTurnEpilogue(t,a,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o};let s=extractFinalOutput(r);return s===void 0?(t&&(a=await emitRecoverableFailedTurn(t,a,OUTPUT_SCHEMA_NOT_FULFILLED),o=setHarnessEmissionState(o,a)),{next:null,session:o}):(o=persistStructuredAssistantTurn(o,n,s),t&&(a=await emitStructuredResult(t,a,s,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o})}async function continuePendingCodeModeInterrupt(e){let t=getPendingCodeModeInterrupt(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,continueCodeModeInterrupt:i,getCodeModeApprovalResponse:a,isCodeModeApprovalInterrupt:o,replaceCodeModeInterruptResult:s,unwrapCodeModeResult:c}=await loadCodeModeModule(),l=t.interrupt,d=o(l)?a([...e.messages],l):void 0;if(o(l)&&d===void 0)return{next:null,session:e.session};let f=createAshCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),p;try{let t=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools});if(o(l)&&d!==void 0)p=await n({approvalResponse:d,interrupt:l,options:f,tools:t});else if(isCodeModeConnectionAuthInterrupt(l))p=await i({interrupt:l,resolution:{status:`authorized`},tools:t,options:f});else throw Error(`Unsupported code-mode interrupt kind "${l.payload.kind}".`)}catch(e){logError(log,`code-mode interrupt continuation failed`,e),p={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let m=c(p),h=m.status===`interrupted`?m.interrupt:m.output,g=s([...e.session.history,...t.responseMessages],l,h),_=clearPendingCodeModeInterrupt({...e.session,history:g});if(m.status===`interrupted`){let t=e.session.history.length,n=g.slice(0,t),r=g.slice(t);return _={..._,history:n},parkOnCodeModeInterrupt({baseSession:_,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:m.interrupt,promptMessages:n,responseMessages:r})}return{next:e.runStep,session:_}}async function parkOnCodeModeInterrupt(e){let{isCodeModeApprovalInterrupt:t,toCodeModeApprovalMessages:n}=await loadCodeModeModule(),r=e.interrupt,i={...e.baseSession,history:[...e.promptMessages]};if(isCodeModeConnectionAuthInterrupt(r)){let t=[...r.payload.challenges??[]];if(e.emit)for(let n of t)await e.emit(createAuthorizationRequiredEvent({authorization:n.challenge,name:n.name,description:n.challenge.instructions??`Authorization required for ${n.name}`,webhookUrl:n.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:{...i,state:setPendingAuthorization(i.state,{challenges:t})}})}}if(t(r)){let t=n(r),a=extractToolApprovalInputRequests({content:extractAssistantContent(t)}),o=setPendingInputBatch({requests:a,responseMessages:t,session:setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:i})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:a,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);o=setHarnessEmissionState(o,t)}return{next:null,session:o}}throw Error(`Unsupported code-mode interrupt kind "${r.payload.kind}".`)}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction){let t=await e.onCompaction(i);i=t.session;for(let e of t.messages)r.push(e)}return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}export{createToolLoopHarness};
@@ -1 +1 @@
1
- import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.58.1`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
1
+ import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.59.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
@@ -1 +1 @@
1
- import{expectFunction,expectObjectRecord,expectOnlyKnownKeys,expectString}from"#internal/authored-module.js";import{isDynamicSentinel}from"#shared/dynamic-tool-definition.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{isDisabledToolSentinel}from"#public/definitions/tool.js";function normalizeToolDefinition(e,t){if(isDynamicSentinel(e))return{kind:`dynamic-tool`,eventNames:Object.keys(e.events)};if(isDisabledToolSentinel(e))return{kind:`disabled`};let n=expectObjectRecord(e,t);expectOnlyKnownKeys(n,[`description`,`execute`,`inputSchema`,`needsApproval`,`onCompact`,`outputSchema`,`toModelOutput`],t);let r=n.inputSchema===void 0?null:normalizeJsonSchemaDefinition(n.inputSchema),i=n.outputSchema===void 0?void 0:normalizeJsonSchemaDefinition(n.outputSchema,`output`),a={description:expectString(n.description,t),execute:expectFunction(n.execute,t),inputSchema:r};return i!==void 0&&(a.outputSchema=i),n.onCompact!==void 0&&expectFunction(n.onCompact,t),n.needsApproval!==void 0&&expectFunction(n.needsApproval,t),n.toModelOutput!==void 0&&expectFunction(n.toModelOutput,t),{kind:`tool`,definition:a}}export{normalizeToolDefinition};
1
+ import{expectFunction,expectObjectRecord,expectOnlyKnownKeys,expectString}from"#internal/authored-module.js";import{isDynamicSentinel}from"#shared/dynamic-tool-definition.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{isDisabledToolSentinel}from"#public/definitions/tool.js";function normalizeToolDefinition(e,t){if(isDynamicSentinel(e))return{kind:`dynamic-tool`,eventNames:Object.keys(e.events)};if(isDisabledToolSentinel(e))return{kind:`disabled`};let n=expectObjectRecord(e,t);expectOnlyKnownKeys(n,[`auth`,`description`,`execute`,`inputSchema`,`needsApproval`,`onCompact`,`outputSchema`,`toModelOutput`],t);let r=n.inputSchema===void 0?null:normalizeJsonSchemaDefinition(n.inputSchema),i=n.outputSchema===void 0?void 0:normalizeJsonSchemaDefinition(n.outputSchema,`output`),a={description:expectString(n.description,t),execute:expectFunction(n.execute,t),inputSchema:r};return i!==void 0&&(a.outputSchema=i),n.onCompact!==void 0&&expectFunction(n.onCompact,t),n.needsApproval!==void 0&&expectFunction(n.needsApproval,t),n.toModelOutput!==void 0&&expectFunction(n.toModelOutput,t),n.auth!==void 0&&expectFunction(expectObjectRecord(n.auth,t).getToken,t),{kind:`tool`,definition:a}}export{normalizeToolDefinition};
@@ -1,7 +1,7 @@
1
- import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.58.1`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
1
+ import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH),WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.59.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
2
2
  import { slackChannel } from "experimental-ash/channels/slack";
3
3
 
4
4
  export default slackChannel({
5
5
  credentials: connectSlackCredentials("slack/${e}"),
6
6
  });
7
- `}function renderWebAppTemplate(e,t,n){let r=n?``:`, { configureVercelJson: false }`;return e.replaceAll(`__ASH_INIT_APP_NAME__`,t).replaceAll(`__ASH_INIT_WITH_ASH_OPTIONS__`,r)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,ash:n?.ash??WEB_DEFAULT_VERCEL_SERVICES.ash}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}function assertStampedVersion(e,t){if(t.startsWith(`__`))throw Error(`Channel scaffold received unstamped version token (${e}=${t}). Build @vercel/ash-scaffold before using its dist entrypoint.`)}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),s=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await hasPackageDependency(t,`next`))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let c=await patchWebPackageJson(t,resolveWebPackageVersions(e.webPackageVersions)),l=[],u=[],d=[],f=basename(resolve(e.projectRoot)),p=e.configureVercelServices??!0;if(p){let t=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(t)===`written`?l.push(t):d.push(t)}let m=join(e.projectRoot,PNPM_WORKSPACE_PATH);await ensurePnpmWorkspacePolicy(m)===`written`?l.push(m):d.push(m);for(let[t,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/ash.ts`&&!e.force&&await pathExists(a)){d.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,f,p),{force:!0}),l.push(a),o&&u.push(a)}let h={kind:`web`,action:s?`overwritten`:`created`,filesWritten:l,filesSkipped:d,packageJsonUpdated:c};return u.length>0&&(h.filesOverwritten=u),h}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=e.connectPackageVersion??`0.1.1`;assertStampedVersion(`connectPackageVersion`,a);let o=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,a),s=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(s),{force:e.force});let c={kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:o,slackConnectorSlug:s};return i&&(c.filesOverwritten=[t]),c}async function listAuthoredChannels(n){let r=join(n,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let n of i){if(n.isFile()){let t=getSupportedModuleBaseName(n.name);t!==null&&a.push(t);continue}if(n.isDirectory())try{(await readdir(join(r,n.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(n.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,listAuthoredChannels,normalizeSlackConnectorSlug};
7
+ `}function renderWebAppTemplate(e,t,n){let r=n?``:`, { configureVercelJson: false }`;return e.replaceAll(`__ASH_INIT_APP_NAME__`,t).replaceAll(`__ASH_INIT_WITH_ASH_OPTIONS__`,r)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,ash:n?.ash??WEB_DEFAULT_VERCEL_SERVICES.ash}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let r of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,r);await pathExists(i)&&t.push(i)}return t}function assertStampedVersion(e,t){if(t.startsWith(`__`))throw Error(`Channel scaffold received unstamped version token (${e}=${t}). Build @vercel/ash-scaffold before using its dist entrypoint.`)}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),s=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await hasPackageDependency(t,`next`))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let c=await patchWebPackageJson(t,resolveWebPackageVersions(e.webPackageVersions)),l=[],u=[],d=[],f=[],p=basename(resolve(e.projectRoot)),m=e.configureVercelServices??!0;if(m){let t=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(t)===`written`?l.push(t):f.push(t)}let h=join(e.projectRoot,PNPM_WORKSPACE_PATH);await ensurePnpmWorkspacePolicy(h)===`written`?l.push(h):f.push(h);for(let[t,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/ash.ts`&&!e.force&&await pathExists(a)){f.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,p,m),{force:!0}),l.push(a),o&&u.push(a)}d.push(...await findCompetingNextConfigFiles(e.projectRoot));let g={kind:`web`,action:s?`overwritten`:`created`,filesWritten:l,filesSkipped:f,packageJsonUpdated:c};return u.length>0&&(g.filesOverwritten=u),d.length>0&&(g.competingNextConfigFiles=d),g}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=e.connectPackageVersion??`0.1.1`;assertStampedVersion(`connectPackageVersion`,a);let o=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,a),s=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(s),{force:e.force});let c={kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:o,slackConnectorSlug:s};return i&&(c.filesOverwritten=[t]),c}async function listAuthoredChannels(n){let r=join(n,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let n of i){if(n.isFile()){let t=getSupportedModuleBaseName(n.name);t!==null&&a.push(t);continue}if(n.isDirectory())try{(await readdir(join(r,n.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(n.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,listAuthoredChannels,normalizeSlackConnectorSlug};
@@ -1,2 +1,2 @@
1
- import{deriveSlackConnectorSlug,ensureChannel}from"../channels.js";import{HumanActionRequiredError}from"../human-action.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{runVercel}from"../primitives/run-vercel.js";import{detectDeployment}from"../primitives/detect-deployment.js";import{deployToVercel}from"./deploy-to-vercel.js";import"../cli/channel-setup-prompter.js";import{runPnpmInstall}from"../primitives/run-pnpm.js";import{isProjectResolved,mergeProjectResolution,projectResolutionFromDeployResult,projectResolutionFromDeployment}from"./project-resolution.js";import{reconcileSlackUid,setupSlackbot}from"./setup-slackbot.js";const CHANNEL_OPTIONS=[{value:`web`,label:`Web Chat`},{value:`slack`,label:`Slack`,hint:`creates slackbot and deploys to Vercel`}];function selectableChannels(e){return CHANNEL_OPTIONS.map(t=>{let n=e?.[t.value];return n===void 0?t:{...t,disabled:!0,disabledReason:n}})}function cloneProjectResolution(e){switch(e.kind){case`unresolved`:return{kind:`unresolved`};case`linked`:return{kind:`linked`,projectId:e.projectId};case`deployed`:return{kind:`deployed`,projectId:e.projectId,productionUrl:e.productionUrl}}}function cloneAddToAgentState(e){return{channels:[...e.channels],webScaffolded:e.webScaffolded,slackScaffolded:e.slackScaffolded,deploymentDependenciesInstalled:e.deploymentDependenciesInstalled,project:cloneProjectResolution(e.project),deploymentPending:e.deploymentPending,slackbotCreated:e.slackbotCreated,slackbotAttached:e.slackbotAttached,slackConnectorUid:e.slackConnectorUid,slackWorkspaceUrl:e.slackWorkspaceUrl,slackWorkspaceName:e.slackWorkspaceName}}function createAddToAgentState(e={state:`unlinked`}){return{channels:[],webScaffolded:!1,slackScaffolded:!1,deploymentDependenciesInstalled:!1,project:projectResolutionFromDeployment(e),deploymentPending:!1,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackWorkspaceUrl:void 0,slackWorkspaceName:void 0}}function recordScaffoldedChannel(e,t){e.channels.includes(t)||(e.channels.push(t),e.deploymentPending=!0)}function warnOverwrittenFiles(e,t){for(let n of t??[])e.log.warning(`Overwrote ${n}`)}async function promptCreateSlackbot(e){return await e.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`,hint:`connects Slack, then deploys`},{value:`no`,label:`No`}]})===`yes`}async function installDeploymentDependencies(e,t,n){if(!n.deploymentDependenciesInstalled){if(e.log.message(`Installing project dependencies before deployment (pnpm install)...`),!await runPnpmInstall(t,{onOutput:createPromptCommandOutput(e.log)}))throw Error(`Dependency installation failed. Deployment did not start.`);n.deploymentDependenciesInstalled=!0}}async function linkProjectForSlackbot(e){if(isProjectResolved(e.state.project))return;let t;if(e.linkProjectForSlackbot!==void 0)t=await e.linkProjectForSlackbot();else if(e.headless)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Slack needs this directory linked to a Vercel project.`});else{if(e.prompter.log.message(`Linking this directory to a Vercel project...`),!await runVercel([`link`],{cwd:e.projectPath}))throw Error(`Vercel project linking failed. Slackbot creation did not start.`);t=projectResolutionFromDeployment(await detectDeployment(e.projectPath))}if(e.state.project=mergeProjectResolution(e.state.project,t),!isProjectResolved(e.state.project))throw Error(`Vercel project linking failed. Slackbot creation did not start.`)}async function scaffoldSlackChannel(e,n){let{prompter:r,projectPath:i,state:a}=e;if(!a.slackScaffolded){r.log.message(`Scaffolding Slack channel files...`);let o=await ensureChannel({projectRoot:i,kind:`slack`,slackConnectorSlug:n,force:e.force});warnOverwrittenFiles(r,o.filesOverwritten),o.action===`created`||o.action===`overwritten`?r.log.success(`Scaffolded channel: slack`):r.log.info(`Channel "slack" already exists. Skipping file creation.`),a.slackScaffolded=!0}recordScaffoldedChannel(a,`slack`)}async function linkProjectForDeployment(e){let{prompter:t,projectPath:o,state:s}=e;if(isProjectResolved(s.project))return;let c=createPromptCommandOutput(t.log);if(e.headless)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Deployment needs this directory linked to a Vercel project.`});if(t.log.message(`Linking this directory to a Vercel project before deployment...`),!await runVercel([`link`],{cwd:o,onOutput:c}))throw Error(`Vercel project linking failed. Deployment did not start.`);let l=await detectDeployment(o);if(s.project=mergeProjectResolution(s.project,projectResolutionFromDeployment(l)),!isProjectResolved(s.project))throw Error(`Vercel project linking failed. Deployment did not start.`)}async function deployProject(e){let t=cloneAddToAgentState(e.state),n={...e,state:t},{prompter:r,projectPath:i}=e;if(!t.deploymentPending)return t;await linkProjectForDeployment(n),await installDeploymentDependencies(r,i,t);let a=await deployToVercel(r,i,{presetDeploy:!0});if(t.project=projectResolutionFromDeployResult(t.project,a),!a.deployed)throw Error(`Deployment failed after channel setup.`);return t.deploymentPending=!1,t}async function reconcilePreparedSlackChannel(e,t,n,r){let i=n.slackConnectorUid;if(i===void 0)throw Error(`Slack connector UID was not resolved. Slack deployment did not start.`);if(!await reconcileSlackUid(e,t,{kind:`attached`,created:!0,attached:!0,connectorUid:i},r))throw Error(`Slack connector UID update is required before deployment.`)}function recordAttachedSlackbot(e,t){e.slackbotCreated=!0,e.slackbotAttached=!0,e.slackConnectorUid=t.connectorUid,e.slackWorkspaceUrl=t.workspaceUrl,e.slackWorkspaceName=t.workspaceName}async function runAddToAgentOnce(n,r){let{prompter:i,projectPath:a,projectName:o,state:s}=n;if(n.headless&&r.includes(`slack`))throw Error(`Slack setup is interactive. Run the guided create flow with -i, or add Slack from the onboarding steps after headless scaffolding.`);if(r.includes(`web`))if(s.webScaffolded)recordScaffoldedChannel(s,`web`);else{i.log.message(`Scaffolding Web Chat channel files...`);let e=await ensureChannel({projectRoot:a,kind:`web`,force:n.force,webPackageVersions:n.webPackageVersions,configureVercelServices:n.configureVercelServices});warnOverwrittenFiles(i,e.filesOverwritten),e.action===`created`||e.action===`overwritten`?(i.log.success(`Scaffolded channel: web`),s.webScaffolded=!0,recordScaffoldedChannel(s,`web`)):i.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`)}if(r.includes(`slack`)){let t=await deriveSlackConnectorSlug(a,o);if(s.slackbotCreated){if(!s.slackbotAttached)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);s.deploymentPending&&(await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`))}else if(n.presetCreateSlackbot??await promptCreateSlackbot(i)){await linkProjectForSlackbot(n);let e=await setupSlackbot(i,a,{presetCreate:!0,slackbotName:t});if(!e.created)throw Error(`Slackbot creation failed.`);if(e.kind!==`attached`)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);recordAttachedSlackbot(s,e),await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`)}else i.log.info(`Slack channel was not added because Slackbot setup was skipped.`)}}async function runAddToAgent(e){let t=cloneAddToAgentState(e.state),n={...e,state:t};for(;;){let r=e.presetChannels??await e.prompter.multiselect({message:`Add to agent`,options:selectableChannels(e.disabledChannelReasons),required:!1});await e.validateSelectedChannels?.(r);try{return await runAddToAgentOnce(n,r),t}catch(t){if(t instanceof Error&&(t.name===`WizardCancelledError`||t.name===`ChannelAddCancelledError`))throw t;let n=t instanceof Error?t.message:String(t),r=n.split(`
1
+ import{deriveSlackConnectorSlug,ensureChannel}from"../channels.js";import{HumanActionRequiredError}from"../human-action.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{runVercel}from"../primitives/run-vercel.js";import{detectDeployment}from"../primitives/detect-deployment.js";import{deployToVercel}from"./deploy-to-vercel.js";import"../cli/channel-setup-prompter.js";import{runPnpmInstall}from"../primitives/run-pnpm.js";import{isProjectResolved,mergeProjectResolution,projectResolutionFromDeployResult,projectResolutionFromDeployment}from"./project-resolution.js";import{reconcileSlackUid,setupSlackbot}from"./setup-slackbot.js";const CHANNEL_OPTIONS=[{value:`web`,label:`Web Chat`},{value:`slack`,label:`Slack`,hint:`creates slackbot and deploys to Vercel`}];function selectableChannels(e){return CHANNEL_OPTIONS.map(t=>{let n=e?.[t.value];return n===void 0?t:{...t,disabled:!0,disabledReason:n}})}function cloneProjectResolution(e){switch(e.kind){case`unresolved`:return{kind:`unresolved`};case`linked`:return{kind:`linked`,projectId:e.projectId};case`deployed`:return{kind:`deployed`,projectId:e.projectId,productionUrl:e.productionUrl}}}function cloneAddToAgentState(e){return{channels:[...e.channels],webScaffolded:e.webScaffolded,slackScaffolded:e.slackScaffolded,deploymentDependenciesInstalled:e.deploymentDependenciesInstalled,project:cloneProjectResolution(e.project),deploymentPending:e.deploymentPending,slackbotCreated:e.slackbotCreated,slackbotAttached:e.slackbotAttached,slackConnectorUid:e.slackConnectorUid,slackWorkspaceUrl:e.slackWorkspaceUrl,slackWorkspaceName:e.slackWorkspaceName}}function createAddToAgentState(e={state:`unlinked`}){return{channels:[],webScaffolded:!1,slackScaffolded:!1,deploymentDependenciesInstalled:!1,project:projectResolutionFromDeployment(e),deploymentPending:!1,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackWorkspaceUrl:void 0,slackWorkspaceName:void 0}}function recordScaffoldedChannel(e,t){e.channels.includes(t)||(e.channels.push(t),e.deploymentPending=!0)}function warnOverwrittenFiles(e,t){for(let n of t??[])e.log.warning(`Overwrote ${n}`)}function warnCompetingNextConfigFiles(e,t){for(let n of t??[])e.log.warning(`Found competing Next.js config at ${n}; merge any needed settings into next.config.ts and remove it before starting the preview, or Next.js may ignore the generated Ash rewrite.`)}async function promptCreateSlackbot(e){return await e.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`,hint:`connects Slack, then deploys`},{value:`no`,label:`No`}]})===`yes`}async function installDeploymentDependencies(e,t,n){if(!n.deploymentDependenciesInstalled){if(e.log.message(`Installing project dependencies before deployment (pnpm install)...`),!await runPnpmInstall(t,{onOutput:createPromptCommandOutput(e.log)}))throw Error(`Dependency installation failed. Deployment did not start.`);n.deploymentDependenciesInstalled=!0}}async function linkProjectForSlackbot(e){if(isProjectResolved(e.state.project))return;let t;if(e.linkProjectForSlackbot!==void 0)t=await e.linkProjectForSlackbot();else if(e.headless)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Slack needs this directory linked to a Vercel project.`});else{if(e.prompter.log.message(`Linking this directory to a Vercel project...`),!await runVercel([`link`],{cwd:e.projectPath}))throw Error(`Vercel project linking failed. Slackbot creation did not start.`);t=projectResolutionFromDeployment(await detectDeployment(e.projectPath))}if(e.state.project=mergeProjectResolution(e.state.project,t),!isProjectResolved(e.state.project))throw Error(`Vercel project linking failed. Slackbot creation did not start.`)}async function scaffoldSlackChannel(e,n){let{prompter:r,projectPath:i,state:a}=e;if(!a.slackScaffolded){r.log.message(`Scaffolding Slack channel files...`);let o=await ensureChannel({projectRoot:i,kind:`slack`,slackConnectorSlug:n,force:e.force});warnOverwrittenFiles(r,o.filesOverwritten),o.action===`created`||o.action===`overwritten`?r.log.success(`Scaffolded channel: slack`):r.log.info(`Channel "slack" already exists. Skipping file creation.`),a.slackScaffolded=!0}recordScaffoldedChannel(a,`slack`)}async function linkProjectForDeployment(e){let{prompter:t,projectPath:o,state:s}=e;if(isProjectResolved(s.project))return;let c=createPromptCommandOutput(t.log);if(e.headless)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Deployment needs this directory linked to a Vercel project.`});if(t.log.message(`Linking this directory to a Vercel project before deployment...`),!await runVercel([`link`],{cwd:o,onOutput:c}))throw Error(`Vercel project linking failed. Deployment did not start.`);let l=await detectDeployment(o);if(s.project=mergeProjectResolution(s.project,projectResolutionFromDeployment(l)),!isProjectResolved(s.project))throw Error(`Vercel project linking failed. Deployment did not start.`)}async function deployProject(e){let t=cloneAddToAgentState(e.state),n={...e,state:t},{prompter:r,projectPath:i}=e;if(!t.deploymentPending)return t;await linkProjectForDeployment(n),await installDeploymentDependencies(r,i,t);let a=await deployToVercel(r,i,{presetDeploy:!0});if(t.project=projectResolutionFromDeployResult(t.project,a),!a.deployed)throw Error(`Deployment failed after channel setup.`);return t.deploymentPending=!1,t}async function reconcilePreparedSlackChannel(e,t,n,r){let i=n.slackConnectorUid;if(i===void 0)throw Error(`Slack connector UID was not resolved. Slack deployment did not start.`);if(!await reconcileSlackUid(e,t,{kind:`attached`,created:!0,attached:!0,connectorUid:i},r))throw Error(`Slack connector UID update is required before deployment.`)}function recordAttachedSlackbot(e,t){e.slackbotCreated=!0,e.slackbotAttached=!0,e.slackConnectorUid=t.connectorUid,e.slackWorkspaceUrl=t.workspaceUrl,e.slackWorkspaceName=t.workspaceName}async function runAddToAgentOnce(n,r){let{prompter:i,projectPath:a,projectName:o,state:s}=n;if(n.headless&&r.includes(`slack`))throw Error(`Slack setup is interactive. Run the guided create flow with -i, or add Slack from the onboarding steps after headless scaffolding.`);if(r.includes(`web`))if(s.webScaffolded)recordScaffoldedChannel(s,`web`);else{i.log.message(`Scaffolding Web Chat channel files...`);let e=await ensureChannel({projectRoot:a,kind:`web`,force:n.force,webPackageVersions:n.webPackageVersions,configureVercelServices:n.configureVercelServices});warnOverwrittenFiles(i,e.filesOverwritten),warnCompetingNextConfigFiles(i,`competingNextConfigFiles`in e?e.competingNextConfigFiles:void 0),e.action===`created`||e.action===`overwritten`?(i.log.success(`Scaffolded channel: web`),s.webScaffolded=!0,recordScaffoldedChannel(s,`web`)):i.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`)}if(r.includes(`slack`)){let t=await deriveSlackConnectorSlug(a,o);if(s.slackbotCreated){if(!s.slackbotAttached)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);s.deploymentPending&&(await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`))}else if(n.presetCreateSlackbot??await promptCreateSlackbot(i)){await linkProjectForSlackbot(n);let e=await setupSlackbot(i,a,{presetCreate:!0,slackbotName:t});if(!e.created)throw Error(`Slackbot creation failed.`);if(e.kind!==`attached`)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);recordAttachedSlackbot(s,e),await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`)}else i.log.info(`Slack channel was not added because Slackbot setup was skipped.`)}}async function runAddToAgent(e){let t=cloneAddToAgentState(e.state),n={...e,state:t};for(;;){let r=e.presetChannels??await e.prompter.multiselect({message:`Add to agent`,options:selectableChannels(e.disabledChannelReasons),required:!1});await e.validateSelectedChannels?.(r);try{return await runAddToAgentOnce(n,r),t}catch(t){if(t instanceof Error&&(t.name===`WizardCancelledError`||t.name===`ChannelAddCancelledError`))throw t;let n=t instanceof Error?t.message:String(t),r=n.split(`
2
2
  `)[0]?.trim()??n;if(e.prompter.log.error(r),e.presetChannels!==void 0)throw t}}}export{cloneAddToAgentState,createAddToAgentState,deployProject,runAddToAgent};
@@ -28,7 +28,6 @@ export default ashChannel({
28
28
 
29
29
  import { useAshAgent } from "experimental-ash/react";
30
30
  import { AlertCircleIcon } from "lucide-react";
31
- import { useState } from "react";
32
31
  import {
33
32
  Conversation,
34
33
  ConversationContent,
@@ -49,31 +48,20 @@ type AgentStatus = ReturnType<typeof useAshAgent>["status"];
49
48
 
50
49
  export function AgentChat() {
51
50
  const agent = useAshAgent();
52
- const [inputText, setInputText] = useState("");
53
51
  const isBusy = agent.status === "submitted" || agent.status === "streaming";
54
52
  const isEmpty = agent.data.messages.length === 0;
55
- const hasInputText = inputText.trim().length > 0;
56
53
 
57
54
  const handleSubmit = async (message: PromptInputMessage) => {
58
55
  const text = message.text.trim();
59
56
  if (!text || isBusy) return;
60
57
 
61
- setInputText("");
62
58
  await agent.sendMessage(text);
63
59
  };
64
60
 
65
61
  const composer = (
66
62
  <PromptInput onSubmit={handleSubmit}>
67
- <PromptInputTextarea
68
- onChange={(event) => setInputText(event.currentTarget.value)}
69
- placeholder="Send a message…"
70
- value={inputText}
71
- />
72
- <PromptInputSubmit
73
- disabled={!isBusy && !hasInputText}
74
- onStop={agent.stop}
75
- status={agent.status}
76
- />
63
+ <PromptInputTextarea placeholder="Send a message…" />
64
+ <PromptInputSubmit onStop={agent.stop} status={agent.status} />
77
65
  </PromptInput>
78
66
  );
79
67
 
@@ -1 +1 @@
1
- import{defineInteractiveAuthorization}from"#runtime/connections/types.js";import{defineMcpClientConnection}from"#public/definitions/connections/mcp.js";import{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";export{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,defineInteractiveAuthorization,defineMcpClientConnection,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError};
1
+ import{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{defineInteractiveAuthorization}from"#runtime/connections/types.js";import{defineMcpClientConnection}from"#public/definitions/connections/mcp.js";export{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,defineInteractiveAuthorization,defineMcpClientConnection,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError};
@@ -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};