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.
- package/CHANGELOG.md +13 -0
- package/dist/docs/public/tools.mdx +51 -0
- package/dist/skills/v0-ash-onboarding/SKILL.md +308 -0
- package/dist/src/cli/dev/tui/markdown.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +2 -2
- package/dist/src/compiled/experimental-ai-sdk-code-mode/approval-continuation.d.ts +71 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/approval.d.ts +32 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/code-mode-tool.d.ts +16 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/continuation-capability.d.ts +33 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/errors.d.ts +114 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/fetch-policy.d.ts +21 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/host-interrupt.d.ts +25 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/index.d.ts +11 -144
- package/dist/src/compiled/experimental-ai-sdk-code-mode/index.js +10 -73
- package/dist/src/compiled/experimental-ai-sdk-code-mode/interrupt-continuation.d.ts +32 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/options.d.ts +3 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/run-code-mode.d.ts +12 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/guest-sources.d.ts +3 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/manager.d.ts +22 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/max-workers.d.ts +20 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/protocol.d.ts +49 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/runtime/worker-source.d.ts +11 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/serialization.d.ts +5 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/source-cache.d.ts +11 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/telemetry.d.ts +20 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/tool-invocation.d.ts +24 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/tool-prompt.d.ts +3 -0
- package/dist/src/compiled/experimental-ai-sdk-code-mode/types.d.ts +802 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/tool-auth.d.ts +42 -0
- package/dist/src/execution/tool-auth.js +1 -0
- package/dist/src/harness/action-result-helpers.d.ts +17 -0
- package/dist/src/harness/action-result-helpers.js +1 -1
- package/dist/src/harness/code-mode-interrupt-state.d.ts +26 -0
- package/dist/src/harness/code-mode-interrupt-state.js +1 -0
- package/dist/src/harness/code-mode-lifecycle.js +1 -1
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +2 -2
- package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +1 -1
- package/dist/src/packages/ash-scaffold/src/web-template.js +2 -14
- package/dist/src/public/connections/index.js +1 -1
- package/dist/src/public/definitions/tool.d.ts +61 -1
- package/dist/src/public/definitions/tool.js +1 -1
- package/dist/src/public/next/server.js +1 -1
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.d.ts +61 -0
- package/dist/src/runtime/connections/scoped-authorization.js +1 -0
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/types.d.ts +10 -0
- package/package.json +2 -2
- package/dist/src/harness/code-mode-approval.d.ts +0 -22
- package/dist/src/harness/code-mode-approval.js +0 -1
- package/dist/src/harness/code-mode-connection-auth-state.d.ts +0 -15
- package/dist/src/harness/code-mode-connection-auth-state.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{jsonSchema}from"ai";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{
|
|
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
|
|
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{
|
|
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{
|
|
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.
|
|
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.
|
|
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)),
|
|
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
|
-
|
|
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{
|
|
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};
|