eve 0.22.0 → 0.22.1
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 +9 -0
- package/dist/src/channel/types.d.ts +18 -1
- package/dist/src/execution/reconcile-session-continuation-token.d.ts +4 -0
- package/dist/src/execution/reconcile-session-continuation-token.js +1 -0
- package/dist/src/execution/sandbox/bindings/vercel-credentials.js +1 -1
- package/dist/src/execution/sandbox/ensure.js +1 -1
- package/dist/src/execution/subagent-adapter.js +1 -1
- package/dist/src/execution/subagent-event-proxy-step.d.ts +23 -0
- package/dist/src/execution/subagent-event-proxy-step.js +1 -0
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +1 -22
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/public/channels/slack/hitl.d.ts +3 -2
- package/dist/src/public/channels/slack/hitl.js +3 -3
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/index.js +1 -1
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/limits.d.ts +18 -0
- package/dist/src/public/channels/slack/limits.js +1 -1
- package/dist/src/runtime/sandbox/keys.d.ts +4 -0
- package/dist/src/runtime/sandbox/keys.js +1 -1
- package/dist/src/runtime/sandbox/template-plan.d.ts +0 -2
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/shared/vercel-project.d.ts +25 -0
- package/dist/src/shared/vercel-project.js +1 -0
- package/docs/channels/slack.mdx +27 -0
- package/docs/connections/overview.mdx +2 -0
- package/docs/sandbox.mdx +3 -1
- package/docs/subagents.mdx +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# eve
|
|
2
2
|
|
|
3
|
+
## 0.22.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9c63a4e: Export `callSlackApi` and `resolveSlackBotToken` from `eve/channels/slack`. Code running outside a webhook-side handler — schedules resolving reactions or reading history, for example — has no `ctx.slack` handle; these were the internal primitives behind `slack.request`, already public-shaped and documented, and are now importable so apps stop hand-rolling `fetch` against the Slack Web API.
|
|
8
|
+
- 210f097: Session sandboxes are now keyed per durable session instead of per deployment, so redeploying no longer discards a session's `/workspace` state. A session gets a fresh sandbox only when the sandbox definition itself changes (authored sandbox source, workspace seed content, or `revalidationKey`), and `onSession` runs again on the replacement sandbox.
|
|
9
|
+
- a3efd4b: Render Slack HITL button prompts as card blocks, move approval tool input into collapsible containers, and keep answered-card updates scoped to the answered request so sibling batched approval buttons remain clickable.
|
|
10
|
+
- 3c6abbf: Surface authorization prompts and completion updates from local subagents on the parent channel, including through nested delegation chains, while keeping the authorization callback scoped to the child session.
|
|
11
|
+
|
|
3
12
|
## 0.22.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
|
@@ -129,10 +129,27 @@ export interface SubagentInputRequestHookPayload {
|
|
|
129
129
|
readonly kind: "subagent-input-request";
|
|
130
130
|
readonly subagentName: string;
|
|
131
131
|
}
|
|
132
|
+
/** Authorization lifecycle event forwarded from a delegated child. */
|
|
133
|
+
export type SubagentAuthorizationEvent = Extract<HandleMessageStreamEvent, {
|
|
134
|
+
type: "authorization.required" | "authorization.completed";
|
|
135
|
+
}>;
|
|
136
|
+
/**
|
|
137
|
+
* Proxy payload sent from a child subagent while it waits for authorization.
|
|
138
|
+
*
|
|
139
|
+
* Runtime-internal. The parent re-emits the unchanged event through its own
|
|
140
|
+
* channel; the authorization callback continues to target the child directly.
|
|
141
|
+
*/
|
|
142
|
+
export interface SubagentAuthorizationEventHookPayload {
|
|
143
|
+
readonly callId: string;
|
|
144
|
+
readonly childSessionId: string;
|
|
145
|
+
readonly event: SubagentAuthorizationEvent;
|
|
146
|
+
readonly kind: "subagent-authorization-event";
|
|
147
|
+
readonly subagentName: string;
|
|
148
|
+
}
|
|
132
149
|
/**
|
|
133
150
|
* Serializable payload sent through the workflow `resumeHook`.
|
|
134
151
|
*/
|
|
135
|
-
export type HookPayload = DeliverHookPayload | RuntimeActionResultHookPayload | SubagentInputRequestHookPayload;
|
|
152
|
+
export type HookPayload = DeliverHookPayload | RuntimeActionResultHookPayload | SubagentAuthorizationEventHookPayload | SubagentInputRequestHookPayload;
|
|
136
153
|
/**
|
|
137
154
|
* Terminal callback metadata attached to a session at creation.
|
|
138
155
|
*
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ContextAccessor } from "#context/key.js";
|
|
2
|
+
import type { HarnessSession } from "#harness/types.js";
|
|
3
|
+
/** Re-stamps a session after a channel handler changes its continuation token. */
|
|
4
|
+
export declare function reconcileSessionContinuationToken(ctx: ContextAccessor, session: HarnessSession): HarnessSession;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ContinuationTokenKey}from"#context/keys.js";function reconcileSessionContinuationToken(e,t){let n=e.get(ContinuationTokenKey);return n===void 0||n===t.continuationToken?t:{...t,continuationToken:n}}export{reconcileSessionContinuationToken};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getVercelOidcToken}from"#compiled/@vercel/oidc/index.js";import{withEveSandboxUserAgent}from"#execution/sandbox/bindings/vercel-user-agent.js";function getVercelSandboxFetch(e){let
|
|
1
|
+
import{getVercelOidcToken}from"#compiled/@vercel/oidc/index.js";import{decodeVercelOidcTokenClaims}from"#shared/vercel-project.js";import{withEveSandboxUserAgent}from"#execution/sandbox/bindings/vercel-user-agent.js";function getVercelSandboxFetch(e){let t=e.fetch;return withEveSandboxUserAgent(t??globalThis.fetch)}async function getVercelSandboxCredentials(t){let n=readNonEmptyString(t,`teamId`)??readNonEmptyEnvironmentVariable(`VERCEL_TEAM_ID`)??readNonEmptyEnvironmentVariable(`VERCEL_ORG_ID`),r=readNonEmptyString(t,`projectId`)??readNonEmptyEnvironmentVariable(`VERCEL_PROJECT_ID`),i=readNonEmptyString(t,`token`)??readNonEmptyEnvironmentVariable(`VERCEL_OIDC_TOKEN`)??readNonEmptyEnvironmentVariable(`VERCEL_TOKEN`);return i&&n&&r?{projectId:r,teamId:n,token:i}:getVercelSandboxCredentialsFromOidcToken(await getVercelOidcToken({project:r,team:n}))}function readNonEmptyString(e,t){let n=e[t];return typeof n==`string`&&n.trim().length>0?n.trim():void 0}function readNonEmptyEnvironmentVariable(e){let t=process.env[e];return typeof t==`string`&&t.trim().length>0?t.trim():void 0}function getVercelSandboxCredentialsFromOidcToken(e){let n=decodeVercelOidcTokenClaims(e);if(n.ownerId===void 0||n.projectId===void 0)throw Error(`Invalid Vercel OIDC token: missing owner_id or project_id.`);return{projectId:n.projectId,teamId:n.ownerId,token:e}}export{getVercelSandboxCredentials,getVercelSandboxFetch};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{waitForDevelopmentSandboxPrewarm}from"#execution/sandbox/development-prewarm.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{trackActiveSandboxHandle}from"#execution/sandbox/active-handles.js";import{isEveDevEnvironment}from"#internal/application/optional-package-install.js";import{markDevelopmentSandboxBackendInitialized}from"#execution/sandbox/development-run.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{getRuntimeCompiledArtifactsSandboxAppRoot}from"#runtime/compiled-artifacts-source.js";import{createRuntimeSandboxKeys}from"#runtime/sandbox/keys.js";import{createRuntimeSandboxTemplatePlan}from"#runtime/sandbox/template-plan.js";import{prewarmAppSandboxes}from"#execution/sandbox/prewarm.js";import{waitForSandboxTemplatePrewarmLock}from"#execution/sandbox/template-prewarm-lock.js";async function ensureSandboxAccess(r){let a=r.state?.initialized??!1,s=r.state?.session??null,c=getRuntimeCompiledArtifactsSandboxAppRoot(r.compiledArtifactsSource)??process.cwd(),l=r.registry.sandbox,u;function getHandle(){return u===void 0&&(u=createHandle().catch(e=>{throw u=void 0,e})),u}async function createHandle(){if(l===null)return null;let o=l.definition,u=o.backend,d=createRuntimeSandboxTemplatePlan({definition:o,workspaceResourceRoot:l.workspaceResourceRoot}),f=await createRuntimeSandboxKeys({backendName:u.name,compiledArtifactsSource:r.compiledArtifactsSource,nodeId:r.nodeId,sessionId:r.sessionId,sourceId:o.sourceId,templatePlan:d});f.templateKey!==null&&(await waitForDevelopmentSandboxPrewarm({appRoot:c,compiledArtifactsSource:r.compiledArtifactsSource,log:e=>logDevelopmentSandbox(`eve: sandbox template "${formatNodeLabel(r.nodeId)}" (${u.name}): ${e}`)}),await waitForSandboxTemplatePrewarmLock({appRoot:c,backendName:u.name,log:e=>logDevelopmentSandbox(`eve: sandbox template "${formatNodeLabel(r.nodeId)}" (${u.name}): ${e}`),templateKey:f.templateKey}));let p=
|
|
1
|
+
import{waitForDevelopmentSandboxPrewarm}from"#execution/sandbox/development-prewarm.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{trackActiveSandboxHandle}from"#execution/sandbox/active-handles.js";import{isEveDevEnvironment}from"#internal/application/optional-package-install.js";import{markDevelopmentSandboxBackendInitialized}from"#execution/sandbox/development-run.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{getRuntimeCompiledArtifactsSandboxAppRoot}from"#runtime/compiled-artifacts-source.js";import{createRuntimeSandboxKeys}from"#runtime/sandbox/keys.js";import{createRuntimeSandboxTemplatePlan}from"#runtime/sandbox/template-plan.js";import{prewarmAppSandboxes}from"#execution/sandbox/prewarm.js";import{waitForSandboxTemplatePrewarmLock}from"#execution/sandbox/template-prewarm-lock.js";async function ensureSandboxAccess(r){let a=r.state?.initialized??!1,s=r.state?.session??null,c=getRuntimeCompiledArtifactsSandboxAppRoot(r.compiledArtifactsSource)??process.cwd(),l=r.registry.sandbox,u;function getHandle(){return u===void 0&&(u=createHandle().catch(e=>{throw u=void 0,e})),u}async function createHandle(){if(l===null)return null;let o=l.definition,u=o.backend,d=createRuntimeSandboxTemplatePlan({definition:o,workspaceResourceRoot:l.workspaceResourceRoot}),f=await createRuntimeSandboxKeys({backendName:u.name,compiledArtifactsSource:r.compiledArtifactsSource,nodeId:r.nodeId,sessionId:r.sessionId,sourceId:o.sourceId,templatePlan:d});f.templateKey!==null&&(await waitForDevelopmentSandboxPrewarm({appRoot:c,compiledArtifactsSource:r.compiledArtifactsSource,log:e=>logDevelopmentSandbox(`eve: sandbox template "${formatNodeLabel(r.nodeId)}" (${u.name}): ${e}`)}),await waitForSandboxTemplatePrewarmLock({appRoot:c,backendName:u.name,log:e=>logDevelopmentSandbox(`eve: sandbox template "${formatNodeLabel(r.nodeId)}" (${u.name}): ${e}`),templateKey:f.templateKey}));let p=s!==null&&s.backendName===u.name&&s.sessionKey===f.sessionKey?s:null;p===null&&(a=!1);let m={existingMetadata:p?.metadata,runtimeContext:{appRoot:c},sessionKey:f.sessionKey,tags:r.tags,templateKey:f.templateKey},h=await withDevelopmentSandboxProgress(`eve: opening sandbox session "${formatNodeLabel(r.nodeId)}" on backend "${u.name}"...`,`eve: opening sandbox session "${formatNodeLabel(r.nodeId)}" on backend "${u.name}"`,async()=>await createBackendHandleWithPrewarmRetry({appRoot:c,backend:u,compiledArtifactsSource:r.compiledArtifactsSource,createInput:m}));return markDevelopmentSandboxBackendInitialized(u.name),trackActiveSandboxHandle({backendName:u.name,handle:h,sessionKey:f.sessionKey}),a||=(await runOnSession(async()=>{await o.onSession?.({ctx:buildCallbackContext(),use:h.useSessionFn})}),!0),h}async function runOnSession(e){if(r.runOnSession!==void 0){await r.runOnSession(e);return}await e()}return{async captureState(){if(u!==void 0){let e=await u;e!==null&&(s=await e.captureState())}return{initialized:a,session:s}},async get(){return(await getHandle())?.session??null}}}async function createBackendHandleWithPrewarmRetry(e){try{return await e.backend.create(e.createInput)}catch(t){if(e.createInput.templateKey===null||e.compiledArtifactsSource.kind!==`disk`||!SandboxTemplateNotProvisionedError.is(t))throw t;return await prewarmAppSandboxes({appRoot:e.appRoot,compiledArtifactsSource:e.compiledArtifactsSource,log:e=>logDevelopmentSandbox(e)}),await waitForSandboxTemplatePrewarmLock({appRoot:e.appRoot,backendName:e.backend.name,log:e=>logDevelopmentSandbox(`eve: ${e}`),templateKey:e.createInput.templateKey}),logDevelopmentSandbox(`eve: sandbox template is ready; retrying sandbox creation...`),await e.backend.create(e.createInput)}}function logDevelopmentSandbox(e){isEveDevEnvironment()&&console.log(e)}async function withDevelopmentSandboxProgress(e,t,n){if(logDevelopmentSandbox(e),!isEveDevEnvironment())return await n();let i=Date.now(),a=setInterval(()=>{logDevelopmentSandbox(`${t} (${Math.round((Date.now()-i)/1e3)}s elapsed)...`)},5e3);a.unref?.();try{return await n()}finally{clearInterval(a)}}function formatNodeLabel(e){return e===`__root__`?`root`:e}export{ensureSandboxAccess};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createErrorId,createLogger}from"#internal/logging.js";import{ContinuationTokenKey,SessionIdKey}from"#context/keys.js";import{resumeHook}from"#internal/workflow/runtime.js";const log=createLogger(`execution.subagent-adapter`),SUBAGENT_ADAPTER_KIND=`subagent`;function isSubagentAdapterState(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.callId==`string`&&t.callId.length>0&&typeof t.parentContinuationToken==`string`&&t.parentContinuationToken.length>0&&typeof t.parentSessionId==`string`&&typeof t.subagentName==`string`&&t.subagentName.length>0}const SUBAGENT_ADAPTER={kind:SUBAGENT_ADAPTER_KIND,async"input.requested"(e,t){let i=t.state;isSubagentAdapterState(i)&&await forwardSubagentInputRequestStep({hookPayload:{callId:i.callId,childContinuationToken:t.ctx.require(ContinuationTokenKey),childSessionId:t.ctx.require(SessionIdKey),event:{requests:e.requests,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},kind:`subagent-input-request`,subagentName:i.subagentName},parentContinuationToken:i.parentContinuationToken})}};async function forwardSubagentInputRequestStep(t){"use step";try{await resumeHook(t.parentContinuationToken,t.hookPayload)}catch(n){let r=createErrorId();throw log.warn(`failed to forward proxied HITL batch to parent`,{callId:t.hookPayload.callId,childContinuationToken:t.hookPayload.childContinuationToken,childSessionId:t.hookPayload.childSessionId,errorId:r,parentContinuationToken:t.parentContinuationToken,subagentName:t.hookPayload.subagentName,error:n}),n}}export{SUBAGENT_ADAPTER,SUBAGENT_ADAPTER_KIND,isSubagentAdapterState};
|
|
1
|
+
import{createErrorId,createLogger}from"#internal/logging.js";import{ContinuationTokenKey,SessionIdKey}from"#context/keys.js";import{resumeHook}from"#internal/workflow/runtime.js";const log=createLogger(`execution.subagent-adapter`),SUBAGENT_ADAPTER_KIND=`subagent`;function isSubagentAdapterState(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.callId==`string`&&t.callId.length>0&&typeof t.parentContinuationToken==`string`&&t.parentContinuationToken.length>0&&typeof t.parentSessionId==`string`&&typeof t.subagentName==`string`&&t.subagentName.length>0}const SUBAGENT_ADAPTER={kind:SUBAGENT_ADAPTER_KIND,async"authorization.required"(e,t){await forwardSubagentAuthorizationEvent({data:e,type:`authorization.required`},t)},async"authorization.completed"(e,t){await forwardSubagentAuthorizationEvent({data:e,type:`authorization.completed`},t)},async"input.requested"(e,t){let i=t.state;isSubagentAdapterState(i)&&await forwardSubagentInputRequestStep({hookPayload:{callId:i.callId,childContinuationToken:t.ctx.require(ContinuationTokenKey),childSessionId:t.ctx.require(SessionIdKey),event:{requests:e.requests,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},kind:`subagent-input-request`,subagentName:i.subagentName},parentContinuationToken:i.parentContinuationToken})}};async function forwardSubagentAuthorizationEvent(e,t){let n=t.state;isSubagentAdapterState(n)&&await forwardSubagentAuthorizationEventStep({hookPayload:{callId:n.callId,childSessionId:t.ctx.require(SessionIdKey),event:e,kind:`subagent-authorization-event`,subagentName:n.subagentName},parentContinuationToken:n.parentContinuationToken})}async function forwardSubagentAuthorizationEventStep(t){"use step";try{await resumeHook(t.parentContinuationToken,t.hookPayload)}catch(n){let r=createErrorId();throw log.warn(`failed to forward subagent authorization event to parent`,{callId:t.hookPayload.callId,childSessionId:t.hookPayload.childSessionId,errorId:r,eventType:t.hookPayload.event.type,parentContinuationToken:t.parentContinuationToken,subagentName:t.hookPayload.subagentName,error:n}),n}}async function forwardSubagentInputRequestStep(t){"use step";try{await resumeHook(t.parentContinuationToken,t.hookPayload)}catch(n){let r=createErrorId();throw log.warn(`failed to forward proxied HITL batch to parent`,{callId:t.hookPayload.callId,childContinuationToken:t.hookPayload.childContinuationToken,childSessionId:t.hookPayload.childSessionId,errorId:r,parentContinuationToken:t.parentContinuationToken,subagentName:t.hookPayload.subagentName,error:n}),n}}export{SUBAGENT_ADAPTER,SUBAGENT_ADAPTER_KIND,isSubagentAdapterState};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SubagentAuthorizationEventHookPayload, SubagentInputRequestHookPayload } from "#channel/types.js";
|
|
2
|
+
import type { ContextContainer } from "#context/container.js";
|
|
3
|
+
import { type DurableSession, type DurableSessionState } from "#execution/durable-session-store.js";
|
|
4
|
+
type SubagentEventHookPayload = SubagentAuthorizationEventHookPayload | SubagentInputRequestHookPayload;
|
|
5
|
+
interface ProxySubagentEventResult {
|
|
6
|
+
readonly serializedContext: Record<string, unknown>;
|
|
7
|
+
readonly sessionState: DurableSessionState;
|
|
8
|
+
}
|
|
9
|
+
/** Proxies one child event through its parent channel across a durable step boundary. */
|
|
10
|
+
export declare function runProxySubagentEventStep(input: {
|
|
11
|
+
readonly hookPayload: SubagentEventHookPayload;
|
|
12
|
+
readonly parentWritable: WritableStream<Uint8Array>;
|
|
13
|
+
readonly serializedContext: Record<string, unknown>;
|
|
14
|
+
readonly sessionState: DurableSessionState;
|
|
15
|
+
}): Promise<ProxySubagentEventResult>;
|
|
16
|
+
/** Applies one proxied child event to an already-hydrated parent context. */
|
|
17
|
+
export declare function emitProxiedSubagentEvent(input: {
|
|
18
|
+
readonly ctx: ContextContainer;
|
|
19
|
+
readonly durableSession: DurableSession;
|
|
20
|
+
readonly hookPayload: SubagentEventHookPayload;
|
|
21
|
+
readonly parentWritable: WritableStream<Uint8Array>;
|
|
22
|
+
}): Promise<ProxySubagentEventResult>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{callAdapterEventHandler}from"#channel/adapter.js";import{ModeKey}from"#context/keys.js";import{encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{createDurableSessionState,readDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession}from"#execution/session.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{upsertProxyInputRequests}from"#harness/proxy-input-requests.js";import{setChannelContext}from"#execution/channel-context.js";import{withContextScope}from"#context/run-step.js";import{reconcileSessionContinuationToken}from"#execution/reconcile-session-continuation-token.js";import{emitProxiedInputRequest}from"#execution/subagent-hitl-proxy.js";async function runProxySubagentEventStep(e){"use step";let t=await readDurableSession(e.sessionState);return emitProxiedSubagentEvent({ctx:await deserializeContext(e.serializedContext),durableSession:t,hookPayload:e.hookPayload,parentWritable:e.parentWritable})}async function emitProxiedSubagentEvent(i){let{ctx:a}=i,o=a.require(ChannelKey),s=a.require(BundleKey),c=hydrateDurableSession({compactionOverrides:{thresholdPercent:s.resolvedAgent.config.compaction?.thresholdPercent},durable:i.durableSession,turnAgent:s.turnAgent}),l=buildAdapterContext(o,a),u=i.parentWritable.getWriter(),d,f;try{let emit=async t=>{let r=await callAdapterEventHandler(o,t,l);await u.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(r)))},r=await withContextScope(a,c,async e=>{if(i.hookPayload.kind===`subagent-authorization-event`)return await emit(i.hookPayload.event),{result:void 0,session:e};let n=await emitProxiedInputRequest({emit,hookPayload:i.hookPayload,mode:a.require(ModeKey),session:e});return{result:n.entries,session:n.session}});d=r.result,f=r.session}finally{u.releaseLock()}setChannelContext(a,{...o,state:{...l.state}}),d!==void 0&&i.hookPayload.kind===`subagent-input-request`&&(f=upsertProxyInputRequests({entries:d,forChildContinuationToken:i.hookPayload.childContinuationToken,session:f}));let p=reconcileSessionContinuationToken(a,f);return{serializedContext:serializeContext(a),sessionState:createDurableSessionState({session:p})}}export{emitProxiedSubagentEvent,runProxySubagentEventStep};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveRuntimeActionResultsForKeys}from"#harness/runtime-actions.js";import{dispatchRuntimeActionsStep}from"#execution/dispatch-runtime-actions-step.js";import{resolveWorkflowCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{
|
|
1
|
+
import{resolveRuntimeActionResultsForKeys}from"#harness/runtime-actions.js";import{dispatchRuntimeActionsStep}from"#execution/dispatch-runtime-actions-step.js";import{resolveWorkflowCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{turnStep}from"#execution/workflow-steps.js";import{createHook,getWorkflowMetadata}from"#compiled/@workflow/core/index.js";import{claimHookOwnership,closeHookIterator,disposeHook,isHookConflictError}from"#execution/hook-ownership.js";import{normalizeSerializableError}from"#execution/workflow-errors.js";import{sendTurnControlStep}from"#execution/turn-control-protocol.js";import{dispatchWorkflowRuntimeActionsStep}from"#execution/dispatch-workflow-runtime-actions-step.js";import{migrateTurnWorkflowInput}from"#execution/durable-session-migrations/turn-workflow.js";import{routeDeliverToChildren}from"#execution/route-child-delivery.js";import{runProxySubagentEventStep}from"#execution/subagent-event-proxy-step.js";import{TurnExecutionCursor}from"#execution/turn-execution-cursor.js";const TASK_MODE_WAIT_ERROR_MESSAGE="Task mode cannot wait for follow-up input (`next: null`).";async function turnWorkflow(e){"use workflow";let t=migrateTurnWorkflowInput(e);return t.driverCapabilities?.turnInbox===!0?runTurnOwnedWorkflow(t):runLegacyTurnWorkflow(t)}async function runTurnOwnedWorkflow(e){let s=createHook({token:`${e.completionToken}:inbox`}),c=s[Symbol.asyncIterator](),l=new TurnExecutionCursor({controlToken:e.completionToken,parentWritable:e.stepInput.parentWritable,serializedContext:e.stepInput.serializedContext,sessionState:e.stepInput.sessionState}),u=0,nextDeliveryRequestId=()=>`${s.token}:delivery:${String(u++)}`,d=[],f=e.stepInput.input,p=!1;try{try{await claimHookOwnership(s),p=!0}catch(e){if(isHookConflictError(e))return;throw e}for(;;){let i=await turnStep(l.createStepInput(f));if(i.action===`done`){await l.finish(i,{kind:`done`,output:i.output??``,isError:i.isError,usage:i.usage},d);return}let o=i.action===`dispatch-workflow-runtime-actions`||i.action===`park`?i.pendingRuntimeActionKeys:void 0;if(o!==void 0){await l.adopt(i);let e=await(i.action===`dispatch-workflow-runtime-actions`?dispatchWorkflowRuntimeActionsStep:dispatchRuntimeActionsStep)({callbackBaseUrl:resolveWorkflowCallbackBaseUrl(getWorkflowMetadata().url),parentContinuationToken:s.token,parentWritable:l.parentWritable,serializedContext:l.serializedContext,sessionState:l.sessionState});await l.adopt(e),f={kind:`runtime-action-result`,results:await waitForRuntimeActionResults({bufferedDeliveries:d,cursor:l,inboxToken:s.token,initialResults:e.results,iterator:c,nextDeliveryRequestId,pendingActionKeys:o})};continue}if(i.action===`park`){if(!(i.hasPendingAuthorization||i.hasPendingInputBatch&&e.capabilities?.requestInput===!0||e.mode===`conversation`))throw Error(TASK_MODE_WAIT_ERROR_MESSAGE);await l.finish(i,{authorizationNames:i.authorizationNames,kind:`park`},d);return}await l.adopt(i),f=void 0}}catch(e){throw await l.send({error:normalizeSerializableError(e),kind:`turn-error`}),e}finally{await closeHookIterator(c),p&&await disposeHook(s)}}async function waitForRuntimeActionResults(t){let n,r=[...t.initialResults];for(;;){let i=resolveRuntimeActionResultsForKeys({pendingKeys:t.pendingActionKeys,results:r});if(i!==void 0)return n!==void 0&&await t.cursor.send({kind:`turn-delivery-cancelled`,requestId:n}),i;t.cursor.sessionState.hasProxyInputRequests&&n===void 0&&(n=t.nextDeliveryRequestId(),await t.cursor.send({continuationToken:t.cursor.sessionState.continuationToken,inboxToken:t.inboxToken,kind:`turn-delivery-request`,requestId:n}));let a=await t.iterator.next();if(a.done)throw Error(`Turn inbox closed before runtime actions completed.`);let o=a.value;if(o.kind===`runtime-action-result`){r.push(...o.results);continue}if(o.kind===`subagent-input-request`||o.kind===`subagent-authorization-event`){let e=await runProxySubagentEventStep({hookPayload:o,parentWritable:t.cursor.parentWritable,serializedContext:t.cursor.serializedContext,sessionState:t.cursor.sessionState});await t.cursor.adopt(e);continue}if(o.kind===`driver-delivery`&&o.requestId===n){await t.cursor.send({kind:`turn-delivery-accepted`,requestId:o.requestId}),n=void 0;let e=await routeDeliverToChildren({auth:o.delivery.auth,parentWritable:t.cursor.parentWritable,payloads:o.delivery.payloads,sessionState:t.cursor.sessionState});e!==void 0&&t.bufferedDeliveries.push({...o.delivery,payloads:[e]})}}}async function runLegacyTurnWorkflow(e){let t=e.stepInput;try{for(;;){let n=await turnStep(t);if(n.action===`done`){await sendTurnControlStep({controlToken:e.completionToken,payload:{action:{kind:`done`,output:n.output??``,isError:n.isError,serializedContext:n.serializedContext,sessionState:n.sessionState,usage:n.usage},kind:`turn-result`}});return}if(n.action===`dispatch-workflow-runtime-actions`){await sendTurnControlStep({controlToken:e.completionToken,payload:{action:{kind:`dispatch-workflow-runtime-actions`,pendingActionKeys:n.pendingRuntimeActionKeys,serializedContext:n.serializedContext,sessionState:n.sessionState},kind:`turn-result`}});return}if(n.action===`park`){let t=n.pendingRuntimeActionKeys;if(!(t!==void 0||n.hasPendingAuthorization||n.hasPendingInputBatch&&e.capabilities?.requestInput===!0||e.mode===`conversation`))throw Error(TASK_MODE_WAIT_ERROR_MESSAGE);let r=t===void 0?{kind:`park`,serializedContext:n.serializedContext,sessionState:n.sessionState,authorizationNames:n.authorizationNames}:{kind:`dispatch-runtime-actions`,pendingActionKeys:t,serializedContext:n.serializedContext,sessionState:n.sessionState};await sendTurnControlStep({controlToken:e.completionToken,payload:{action:r,kind:`turn-result`}});return}t={input:void 0,parentWritable:t.parentWritable,serializedContext:n.serializedContext,sessionState:n.sessionState}}}catch(t){throw await sendTurnControlStep({controlToken:e.completionToken,payload:{error:normalizeSerializableError(t),kind:`turn-error`}}),t}}export{turnWorkflow};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { DeliverPayload, SessionAuthContext
|
|
2
|
-
import { deserializeContext } from "#context/serialize.js";
|
|
1
|
+
import type { DeliverPayload, SessionAuthContext } from "#channel/types.js";
|
|
3
2
|
import type { HarnessSession, StepInput } from "#harness/types.js";
|
|
4
3
|
import type { TokenUsage } from "#shared/token-usage.js";
|
|
5
4
|
import type { JsonObject } from "#shared/json.js";
|
|
@@ -41,12 +40,6 @@ export type { TurnStepInput };
|
|
|
41
40
|
* Runs one atomic harness step inside a durable `"use step"` boundary.
|
|
42
41
|
*/
|
|
43
42
|
export declare function turnStep(rawInput: TurnStepInput): Promise<DurableStepResult>;
|
|
44
|
-
/**
|
|
45
|
-
* Re-stamps `session.continuationToken` from `ContinuationTokenKey`
|
|
46
|
-
* after channels call `setContinuationToken(...)`. Idempotent when the
|
|
47
|
-
* token is unchanged.
|
|
48
|
-
*/
|
|
49
|
-
export declare function reconcileSessionContinuationToken(ctx: Awaited<ReturnType<typeof deserializeContext>>, session: HarnessSession): HarnessSession;
|
|
50
43
|
/**
|
|
51
44
|
* Resolves the single output schema in effect for this turn, decoupling schema
|
|
52
45
|
* enforcement from {@link RunMode}: downstream the harness reads
|
|
@@ -71,20 +64,6 @@ export declare function emitTerminalSessionFailureStep(input: {
|
|
|
71
64
|
readonly parentWritable: WritableStream<Uint8Array>;
|
|
72
65
|
readonly serializedContext: Record<string, unknown>;
|
|
73
66
|
}): Promise<void>;
|
|
74
|
-
export interface ProxyInputRequestResult {
|
|
75
|
-
readonly serializedContext: Record<string, unknown>;
|
|
76
|
-
readonly sessionState: DurableSessionState;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Emits a proxied `input.requested` event through the parent's adapter
|
|
80
|
-
* and records the routing entries on the parent session.
|
|
81
|
-
*/
|
|
82
|
-
export declare function runProxyInputRequestStep(input: {
|
|
83
|
-
readonly hookPayload: SubagentInputRequestHookPayload;
|
|
84
|
-
readonly parentWritable: WritableStream<Uint8Array>;
|
|
85
|
-
readonly serializedContext: Record<string, unknown>;
|
|
86
|
-
readonly sessionState: DurableSessionState;
|
|
87
|
-
}): Promise<ProxyInputRequestResult>;
|
|
88
67
|
export interface RoutedDeliverResult {
|
|
89
68
|
/** `undefined` when the entire payload was routed to descendants. */
|
|
90
69
|
readonly remainder: DeliverPayload | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,formatError}from"#internal/logging.js";import{callAdapterEventHandler,defaultDeliverResult}from"#channel/adapter.js";import{AuthKey,CapabilitiesKey,ContinuationTokenKey,ModeKey}from"#context/keys.js";import{createAuthorizationCompletedEvent,createSessionFailedEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getHarnessEmissionState}from"#harness/emission.js";import{createDurableSessionState,readDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession,refreshSessionFromTurnAgent}from"#execution/session.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{resumeHook}from"#internal/workflow/runtime.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{getPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{createWorkflowRuntime,startWorkflowPreferLatest,turnWorkflowReference}from"#execution/workflow-runtime.js";import{getPendingWorkflowInterrupt}from"#harness/workflow-interrupt-state.js";import{getRuntimeActionKeysFromWorkflowInterrupt,isWorkflowRuntimeActionInterrupt}from"#harness/workflow-runtime-action-state.js";import{upsertProxyInputRequests}from"#harness/proxy-input-requests.js";import{dispatchDynamicModelEvent}from"#context/dynamic-model-lifecycle.js";import{resolveWorkflowCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{setChannelContext}from"#execution/channel-context.js";import{CallbackBaseUrlKey,PendingAuthorizationResultKey,clearPendingAuthorization,getPendingAuthorization}from"#harness/authorization.js";import{createTurnWorkflowInput}from"#execution/durable-session-migrations/turn-workflow.js";import{coalesceTurnInputs}from"#harness/messages.js";import{buildTurnAttributes,readRootSessionId}from"#execution/eve-workflow-attributes.js";import{normalizeEveAttributes}from"#runtime/attributes/normalize.js";import{dispatchStreamEventHooks}from"#context/hook-lifecycle.js";import{dispatchDynamicInstructionEvent}from"#context/dynamic-instruction-lifecycle.js";import{dispatchDynamicSkillEvent}from"#context/dynamic-skill-lifecycle.js";import{dispatchDynamicToolEvent}from"#context/dynamic-tool-lifecycle.js";import{runStep,withContextScope}from"#context/run-step.js";import{hasPendingInputBatch}from"#harness/input-requests.js";import{getTurnUsageState,toUsage}from"#harness/turn-tag-state.js";import{getRuntimeActionRequestKey}from"#runtime/actions/keys.js";import{createExecutionNodeStep}from"#execution/node-step.js";import{emitProxiedInputRequest,routeDeliverPayload}from"#execution/subagent-hitl-proxy.js";import{recordSubagentUsageSpans}from"#execution/subagent-usage-span.js";import{resolveSessionSkillRoot}from"#execution/workflow-skill-root.js";async function turnStep(e){"use step";let t=e,o=await readDurableSession(t.sessionState),l=await deserializeContext(t.serializedContext),f=l.require(ChannelKey),p=l.require(BundleKey);try{let{getWorkflowMetadata:e}=await import(`#compiled/@workflow/core/index.js`),t=e();typeof t.url==`string`&&l.set(CallbackBaseUrlKey,resolveWorkflowCallbackBaseUrl(t.url))}catch{}let m=getPendingAuthorization(o.state),h;if(m&&t.input?.kind===`deliver`){let e=[],n=[],r=[];for(let i of t.input.payloads){let t=i.authorizationCallback;if(t){let r=m.challenges.find(e=>e.name===t.connectionName);r&&(e.push({name:r.name,resume:r.resume,callback:t.callback,hookUrl:r.hookUrl}),n.push({name:r.name,authorization:r.challenge}))}else r.push(i)}e.length>0&&(l.set(PendingAuthorizationResultKey,e),o={...o,state:clearPendingAuthorization(o.state,e.map(e=>e.name))},h=n,t=r.length>0?{...t,input:{...t.input,payloads:r}}:{...t,input:void 0})}t.input?.kind===`deliver`&&t.input.auth!==void 0&&l.set(AuthKey,t.input.auth??null);let g=hydrateDurableSession({compactionOverrides:{thresholdPercent:p.resolvedAgent.config.compaction?.thresholdPercent},durable:o,turnAgent:p.turnAgent}),_=buildAdapterContext(f,l),v;if(t.input?.kind===`deliver`){let e=[];for(let n of t.input.payloads){let t=f.deliver?await f.deliver(n,_):defaultDeliverResult(n);t!=null&&e.push(t)}v=e.length===0?void 0:e.reduce(coalesceTurnInputs)}else t.input?.kind===`runtime-action-result`&&(recordSubagentUsageSpans(t.input.results),v={runtimeActionResults:t.input.results});if(t.input?.kind===`deliver`&&setChannelContext(l,{...f,state:{..._.state}}),t.input?.kind===`deliver`&&v===void 0){let e=reconcileSessionContinuationToken(l,g),n=serializeContext(l),r=e===g?t.sessionState:createDurableSessionState({session:e});return{action:`park`,...derivePendingState(e),serializedContext:n,sessionState:r}}let y=t.parentWritable.getWriter(),b=p.hookRegistry,x=p.resolvedAgent.dynamicInstructionsResolvers??[],S=p.resolvedAgent.dynamicSkillResolvers??[],C=p.resolvedAgent.dynamicToolResolvers??[],emit=async e=>{let t=await callAdapterEventHandler(f,e,_);return setChannelContext(l,{...f,state:{..._.state}}),await y.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t))),t},handleEvent=async(e,t)=>{let n=await emit(e);await dispatchStreamEventHooks({ctx:l,registry:b,event:n}),n.type!==`step.started`&&await dispatchDynamicModelEvent({ctx:l,dynamicModel:p.turnAgent.dynamicModel,event:n,fallback:p.turnAgent.model,messages:t??[],scope:{moduleMap:p.moduleMap,nodeId:p.nodeId}}),await dispatchDynamicToolEvent({ctx:l,resolvers:C,event:n,messages:t??[]}),await dispatchDynamicSkillEvent({ctx:l,resolvers:S,event:n,messages:t??[]}),await dispatchDynamicInstructionEvent({ctx:l,resolvers:x,event:n,messages:t??[]})},w=l.require(ModeKey),T=await runStep(l,g,async e=>{let n=resolveEffectiveOutputSchema({agentOutputSchema:p.turnAgent.outputSchema,input:v,mode:w,session:e});if(h){let e=getHarnessEmissionState(n.state);for(let{name:t,authorization:n}of h)await handleEvent(createAuthorizationCompletedEvent({authorization:n,name:t,outcome:`authorized`,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId}))}let r=l.get(CapabilitiesKey);return(async(e,n)=>{let i=await resolveSessionSkillRoot({ctx:l,turnAgent:p.turnAgent}),a=refreshSessionFromTurnAgent({compactionOverrides:{thresholdPercent:p.resolvedAgent.config.compaction?.thresholdPercent},session:e,skillRoot:i,turnAgent:p.turnAgent});return createExecutionNodeStep({abortSignal:t.abortSignal,capabilities:r,createRuntime:createWorkflowRuntime,handleEvent,mode:w,modelResolutionScope:{moduleMap:p.moduleMap,nodeId:p.nodeId},node:p.graph.root,workflowMaxSubagents:a.workflowMaxSubagents})(a,n)})(n,v)}),E=reconcileSessionContinuationToken(l,T.session),D=serializeContext(l);T={...T,session:E};let O=createDurableSessionState({session:T.session});if(T.next!==null&&typeof T.next==`object`&&`done`in T.next){await y.close();let e=getTurnUsageState(T.session.state)?.session;return{action:`done`,output:T.next.output,isError:T.next.isError,serializedContext:D,sessionState:O,usage:e===void 0?void 0:toUsage(e)}}if(T.next===null){y.releaseLock();let e=getPendingWorkflowInterrupt(T.session.state);return e!==void 0&&isWorkflowRuntimeActionInterrupt(e.interrupt)?{action:`dispatch-workflow-runtime-actions`,pendingRuntimeActionKeys:getRuntimeActionKeysFromWorkflowInterrupt(e.interrupt),serializedContext:D,sessionState:O}:{action:`park`,...derivePendingState(T.session),serializedContext:D,sessionState:O}}return y.releaseLock(),{action:`continue`,serializedContext:D,sessionState:O}}function derivePendingState(e){let t=getPendingRuntimeActionBatch(e.state),n=getPendingAuthorization(e.state),r={authorizationNames:n?.challenges.map(e=>e.name),hasPendingAuthorization:n!==void 0,hasPendingInputBatch:hasPendingInputBatch(e.state)};return t===void 0?r:{...r,pendingRuntimeActionKeys:t.actions.map(e=>getRuntimeActionRequestKey(e))}}function reconcileSessionContinuationToken(e,t){let n=e.get(ContinuationTokenKey);return n===void 0||n===t.continuationToken?t:{...t,continuationToken:n}}function resolveEffectiveOutputSchema(e){let{agentOutputSchema:t,input:n,mode:r,session:i}=e;return n?.outputSchema===void 0?r===`task`&&i.outputSchema===void 0&&t!==void 0?{...i,outputSchema:t}:i:{...i,outputSchema:n.outputSchema}}const log=createLogger(`execution.workflow-entry`);async function emitTerminalSessionFailureStep(e){"use step";let r=formatError(e.error),i=typeof r.name==`string`?r.name:`WORKFLOW_EXECUTION_FAILED`,a=typeof r.message==`string`?r.message:String(e.error),o=e.serializedContext[`eve.sessionId`]??``;log.error(`workflow loop threw — emitting terminal session.failed`,{sessionId:o,errorId:typeof r.errorId==`string`?r.errorId:void 0,code:i,message:a,detail:typeof r.detail==`string`?r.detail:void 0});let s=createSessionFailedEvent({code:i,details:r,message:a,sessionId:o});try{let t=await deserializeContext(e.serializedContext),r=t.get(ChannelKey);r!==void 0&&await callAdapterEventHandler(r,s,buildAdapterContext(r,t))}catch(e){log.error(`adapter failed to handle terminal session.failed event`,{errorId:typeof r.errorId==`string`?r.errorId:void 0,sessionId:o,error:e})}try{let t=e.parentWritable.getWriter();try{await t.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(s)))}finally{t.releaseLock()}}catch(e){log.error(`failed to write terminal session.failed event to durable stream`,{errorId:typeof r.errorId==`string`?r.errorId:void 0,sessionId:o,error:e})}}async function runProxyInputRequestStep(e){"use step";let t=await readDurableSession(e.sessionState),r=await deserializeContext(e.serializedContext),i=r.require(ChannelKey),a=buildAdapterContext(i,r),o=r.require(ModeKey),c=r.require(BundleKey),l=hydrateDurableSession({compactionOverrides:{thresholdPercent:c.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:c.turnAgent}),u=e.parentWritable.getWriter(),d;try{let emit=async e=>{let t=await callAdapterEventHandler(i,e,a);await u.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t)))};d=await withContextScope(r,l,async t=>{let n=await emitProxiedInputRequest({emit,hookPayload:e.hookPayload,mode:o,session:t});return{result:n.entries,session:n.session}})}finally{u.releaseLock()}return setChannelContext(r,{...i,state:{...a.state}}),{serializedContext:serializeContext(r),sessionState:createDurableSessionState({session:reconcileSessionContinuationToken(r,upsertProxyInputRequests({entries:d.result,forChildContinuationToken:e.hookPayload.childContinuationToken,session:d.session}))})}}async function routeProxiedDeliverStep(e){"use step";let t=await readDurableSession(e.sessionState),n=routeDeliverPayload({payload:e.payload,state:t.state});for(let t of n.forChildren)await resumeHook(t.childContinuationToken,{auth:e.auth,kind:`deliver`,payloads:[t.payload]});return{remainder:n.forSelf}}async function dispatchTurnStep(e){"use step";return{runId:(await startWorkflowPreferLatest(turnWorkflowReference,[createTurnWorkflowInput(e)],{allowReservedAttributes:!0,attributes:normalizeEveAttributes(buildTurnAttributes({parentSessionId:e.sessionState.sessionId,requestId:e.delivery.kind===`deliver`?e.delivery.requestId:void 0,rootSessionId:readRootSessionId(e.serializedContext)??e.sessionState.sessionId}))})).runId}}export{dispatchTurnStep,emitTerminalSessionFailureStep,reconcileSessionContinuationToken,resolveEffectiveOutputSchema,routeProxiedDeliverStep,runProxyInputRequestStep,turnStep};
|
|
1
|
+
import{createLogger,formatError}from"#internal/logging.js";import{callAdapterEventHandler,defaultDeliverResult}from"#channel/adapter.js";import{AuthKey,CapabilitiesKey,ModeKey}from"#context/keys.js";import{createAuthorizationCompletedEvent,createSessionFailedEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getHarnessEmissionState}from"#harness/emission.js";import{createDurableSessionState,readDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession,refreshSessionFromTurnAgent}from"#execution/session.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{resumeHook}from"#internal/workflow/runtime.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{getPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{createWorkflowRuntime,startWorkflowPreferLatest,turnWorkflowReference}from"#execution/workflow-runtime.js";import{getPendingWorkflowInterrupt}from"#harness/workflow-interrupt-state.js";import{getRuntimeActionKeysFromWorkflowInterrupt,isWorkflowRuntimeActionInterrupt}from"#harness/workflow-runtime-action-state.js";import{dispatchDynamicModelEvent}from"#context/dynamic-model-lifecycle.js";import{resolveWorkflowCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{setChannelContext}from"#execution/channel-context.js";import{runStep}from"#context/run-step.js";import{reconcileSessionContinuationToken}from"#execution/reconcile-session-continuation-token.js";import{routeDeliverPayload}from"#execution/subagent-hitl-proxy.js";import{CallbackBaseUrlKey,PendingAuthorizationResultKey,clearPendingAuthorization,getPendingAuthorization}from"#harness/authorization.js";import{createTurnWorkflowInput}from"#execution/durable-session-migrations/turn-workflow.js";import{coalesceTurnInputs}from"#harness/messages.js";import{buildTurnAttributes,readRootSessionId}from"#execution/eve-workflow-attributes.js";import{normalizeEveAttributes}from"#runtime/attributes/normalize.js";import{dispatchStreamEventHooks}from"#context/hook-lifecycle.js";import{dispatchDynamicInstructionEvent}from"#context/dynamic-instruction-lifecycle.js";import{dispatchDynamicSkillEvent}from"#context/dynamic-skill-lifecycle.js";import{dispatchDynamicToolEvent}from"#context/dynamic-tool-lifecycle.js";import{hasPendingInputBatch}from"#harness/input-requests.js";import{getTurnUsageState,toUsage}from"#harness/turn-tag-state.js";import{getRuntimeActionRequestKey}from"#runtime/actions/keys.js";import{createExecutionNodeStep}from"#execution/node-step.js";import{recordSubagentUsageSpans}from"#execution/subagent-usage-span.js";import{resolveSessionSkillRoot}from"#execution/workflow-skill-root.js";async function turnStep(e){"use step";let t=e,c=await readDurableSession(t.sessionState),l=await deserializeContext(t.serializedContext),u=l.require(ChannelKey),d=l.require(BundleKey);try{let{getWorkflowMetadata:e}=await import(`#compiled/@workflow/core/index.js`),t=e();typeof t.url==`string`&&l.set(CallbackBaseUrlKey,resolveWorkflowCallbackBaseUrl(t.url))}catch{}let f=getPendingAuthorization(c.state),p;if(f&&t.input?.kind===`deliver`){let e=[],n=[],r=[];for(let i of t.input.payloads){let t=i.authorizationCallback;if(t){let r=f.challenges.find(e=>e.name===t.connectionName);r&&(e.push({name:r.name,resume:r.resume,callback:t.callback,hookUrl:r.hookUrl}),n.push({name:r.name,authorization:r.challenge}))}else r.push(i)}e.length>0&&(l.set(PendingAuthorizationResultKey,e),c={...c,state:clearPendingAuthorization(c.state,e.map(e=>e.name))},p=n,t=r.length>0?{...t,input:{...t.input,payloads:r}}:{...t,input:void 0})}t.input?.kind===`deliver`&&t.input.auth!==void 0&&l.set(AuthKey,t.input.auth??null);let m=hydrateDurableSession({compactionOverrides:{thresholdPercent:d.resolvedAgent.config.compaction?.thresholdPercent},durable:c,turnAgent:d.turnAgent}),h=buildAdapterContext(u,l),g;if(t.input?.kind===`deliver`){let e=[];for(let n of t.input.payloads){let t=u.deliver?await u.deliver(n,h):defaultDeliverResult(n);t!=null&&e.push(t)}g=e.length===0?void 0:e.reduce(coalesceTurnInputs)}else t.input?.kind===`runtime-action-result`&&(recordSubagentUsageSpans(t.input.results),g={runtimeActionResults:t.input.results});if(t.input?.kind===`deliver`&&setChannelContext(l,{...u,state:{...h.state}}),t.input?.kind===`deliver`&&g===void 0){let e=reconcileSessionContinuationToken(l,m),n=serializeContext(l),r=e===m?t.sessionState:createDurableSessionState({session:e});return{action:`park`,...derivePendingState(e),serializedContext:n,sessionState:r}}let _=t.parentWritable.getWriter(),v=d.hookRegistry,y=d.resolvedAgent.dynamicInstructionsResolvers??[],b=d.resolvedAgent.dynamicSkillResolvers??[],x=d.resolvedAgent.dynamicToolResolvers??[],emit=async e=>{let t=await callAdapterEventHandler(u,e,h);return setChannelContext(l,{...u,state:{...h.state}}),await _.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t))),t},handleEvent=async(e,t)=>{let n=await emit(e);await dispatchStreamEventHooks({ctx:l,registry:v,event:n}),n.type!==`step.started`&&await dispatchDynamicModelEvent({ctx:l,dynamicModel:d.turnAgent.dynamicModel,event:n,fallback:d.turnAgent.model,messages:t??[],scope:{moduleMap:d.moduleMap,nodeId:d.nodeId}}),await dispatchDynamicToolEvent({ctx:l,resolvers:x,event:n,messages:t??[]}),await dispatchDynamicSkillEvent({ctx:l,resolvers:b,event:n,messages:t??[]}),await dispatchDynamicInstructionEvent({ctx:l,resolvers:y,event:n,messages:t??[]})},S=l.require(ModeKey),C=await runStep(l,m,async e=>{let n=resolveEffectiveOutputSchema({agentOutputSchema:d.turnAgent.outputSchema,input:g,mode:S,session:e});if(p){let e=getHarnessEmissionState(n.state);for(let{name:t,authorization:n}of p)await handleEvent(createAuthorizationCompletedEvent({authorization:n,name:t,outcome:`authorized`,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId}))}let r=l.get(CapabilitiesKey);return(async(e,n)=>{let i=await resolveSessionSkillRoot({ctx:l,turnAgent:d.turnAgent}),a=refreshSessionFromTurnAgent({compactionOverrides:{thresholdPercent:d.resolvedAgent.config.compaction?.thresholdPercent},session:e,skillRoot:i,turnAgent:d.turnAgent});return createExecutionNodeStep({abortSignal:t.abortSignal,capabilities:r,createRuntime:createWorkflowRuntime,handleEvent,mode:S,modelResolutionScope:{moduleMap:d.moduleMap,nodeId:d.nodeId},node:d.graph.root,workflowMaxSubagents:a.workflowMaxSubagents})(a,n)})(n,g)}),w=reconcileSessionContinuationToken(l,C.session),T=serializeContext(l);C={...C,session:w};let E=createDurableSessionState({session:C.session});if(C.next!==null&&typeof C.next==`object`&&`done`in C.next){await _.close();let e=getTurnUsageState(C.session.state)?.session;return{action:`done`,output:C.next.output,isError:C.next.isError,serializedContext:T,sessionState:E,usage:e===void 0?void 0:toUsage(e)}}if(C.next===null){_.releaseLock();let e=getPendingWorkflowInterrupt(C.session.state);return e!==void 0&&isWorkflowRuntimeActionInterrupt(e.interrupt)?{action:`dispatch-workflow-runtime-actions`,pendingRuntimeActionKeys:getRuntimeActionKeysFromWorkflowInterrupt(e.interrupt),serializedContext:T,sessionState:E}:{action:`park`,...derivePendingState(C.session),serializedContext:T,sessionState:E}}return _.releaseLock(),{action:`continue`,serializedContext:T,sessionState:E}}function derivePendingState(e){let t=getPendingRuntimeActionBatch(e.state),n=getPendingAuthorization(e.state),r={authorizationNames:n?.challenges.map(e=>e.name),hasPendingAuthorization:n!==void 0,hasPendingInputBatch:hasPendingInputBatch(e.state)};return t===void 0?r:{...r,pendingRuntimeActionKeys:t.actions.map(e=>getRuntimeActionRequestKey(e))}}function resolveEffectiveOutputSchema(e){let{agentOutputSchema:t,input:n,mode:r,session:i}=e;return n?.outputSchema===void 0?r===`task`&&i.outputSchema===void 0&&t!==void 0?{...i,outputSchema:t}:i:{...i,outputSchema:n.outputSchema}}const log=createLogger(`execution.workflow-entry`);async function emitTerminalSessionFailureStep(e){"use step";let r=formatError(e.error),i=typeof r.name==`string`?r.name:`WORKFLOW_EXECUTION_FAILED`,a=typeof r.message==`string`?r.message:String(e.error),o=e.serializedContext[`eve.sessionId`]??``;log.error(`workflow loop threw — emitting terminal session.failed`,{sessionId:o,errorId:typeof r.errorId==`string`?r.errorId:void 0,code:i,message:a,detail:typeof r.detail==`string`?r.detail:void 0});let s=createSessionFailedEvent({code:i,details:r,message:a,sessionId:o});try{let t=await deserializeContext(e.serializedContext),r=t.get(ChannelKey);r!==void 0&&await callAdapterEventHandler(r,s,buildAdapterContext(r,t))}catch(e){log.error(`adapter failed to handle terminal session.failed event`,{errorId:typeof r.errorId==`string`?r.errorId:void 0,sessionId:o,error:e})}try{let t=e.parentWritable.getWriter();try{await t.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(s)))}finally{t.releaseLock()}}catch(e){log.error(`failed to write terminal session.failed event to durable stream`,{errorId:typeof r.errorId==`string`?r.errorId:void 0,sessionId:o,error:e})}}async function routeProxiedDeliverStep(e){"use step";let t=await readDurableSession(e.sessionState),n=routeDeliverPayload({payload:e.payload,state:t.state});for(let t of n.forChildren)await resumeHook(t.childContinuationToken,{auth:e.auth,kind:`deliver`,payloads:[t.payload]});return{remainder:n.forSelf}}async function dispatchTurnStep(e){"use step";return{runId:(await startWorkflowPreferLatest(turnWorkflowReference,[createTurnWorkflowInput(e)],{allowReservedAttributes:!0,attributes:normalizeEveAttributes(buildTurnAttributes({parentSessionId:e.sessionState.sessionId,requestId:e.delivery.kind===`deliver`?e.delivery.requestId:void 0,rootSessionId:readRootSessionId(e.serializedContext)??e.sessionState.sessionId}))})).runId}}export{dispatchTurnStep,emitTerminalSessionFailureStep,resolveEffectiveOutputSchema,routeProxiedDeliverStep,turnStep};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{existsSync,readFileSync,realpathSync}from"node:fs";import{basename,dirname,join}from"node:path";import{EVE_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const BUNDLED_FALLBACK_PACKAGE_VERSION=`0.22.
|
|
1
|
+
import{createRequire}from"node:module";import{existsSync,readFileSync,realpathSync}from"node:fs";import{basename,dirname,join}from"node:path";import{EVE_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const BUNDLED_FALLBACK_PACKAGE_VERSION=`0.22.1`,WORKFLOW_MODULE_ALIASES={"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`};function resolveFallbackPackageVersion(){return BUNDLED_FALLBACK_PACKAGE_VERSION.startsWith(`__`)?`0.0.0`:BUNDLED_FALLBACK_PACKAGE_VERSION}const FALLBACK_PACKAGE_INFO={name:EVE_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 n=e;for(;;){if(existsSync(join(n,`package.json`))&&!isBuildOutputPackageRoot(n))return n;let r=dirname(n);if(r===n)throw Error(`Failed to resolve package root from "${e}".`);n=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 resolvePackageDependencyPath(e){return require.resolve(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 r=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(r?.name===t)return r}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),EVE_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),EVE_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function readWorkflowVersionFromManifest(e){let t=e;for(let e of[t.devDependencies,t.dependencies,t.peerDependencies]){let t=e?.[`@workflow/core`];if(typeof t==`string`&&t.trim().length>0)return t}}function resolveExpectedWorkflowVersion(){let e=tryResolvePackageRoot();if(e!==void 0)try{return readWorkflowVersionFromManifest(JSON.parse(readFileSync(join(e,`package.json`),`utf8`)))}catch{}try{return readWorkflowVersionFromManifest(JSON.parse(readFileSync(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),`utf8`)))}catch{return}}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/api`||e===`workflow/runtime`)return resolvePackageSourceFilePath(`src/internal/workflow/runtime.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{resolveExpectedWorkflowVersion,resolveInstalledPackageInfo,resolvePackageDependencyPath,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
|
|
@@ -80,8 +80,9 @@ export declare function isHitlAction(actionId: string): boolean;
|
|
|
80
80
|
* visible.
|
|
81
81
|
* - `display === "select"` with more options → `static_select`
|
|
82
82
|
* dropdown so the picker stays scrollable.
|
|
83
|
-
* - Anything else with options →
|
|
84
|
-
* choices (approve / deny /
|
|
83
|
+
* - Anything else with options → Slack `card` blocks with action
|
|
84
|
+
* buttons. Best for visually distinct choices (approve / deny /
|
|
85
|
+
* cancel).
|
|
85
86
|
* - No options (or `allowFreeform: true`) → a single "Type your answer"
|
|
86
87
|
* button that opens a Slack modal with a plain_text_input. The modal
|
|
87
88
|
* submission comes back as a `view_submission` webhook the channel
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{SLACK_SECTION_TEXT_MAX_LENGTH,truncateModalTitle,truncatePlainText,truncateSectionText}from"#public/channels/slack/limits.js";const HITL_ACTION_PREFIX=`eve_input:`,HITL_FREEFORM_ACTION_PREFIX=`eve_input_freeform:`,HITL_FREEFORM_MODAL_CALLBACK_ID=`eve_input_freeform_submit`,HITL_FREEFORM_MODAL_BLOCK_ID=`eve_freeform_block`,HITL_FREEFORM_MODAL_ACTION_ID=`eve_freeform_text`,BUTTON_ACTION_ID_RE=/^(?<requestId>.+):button:\d+$/u;function deriveHitlResponse(e){if(!e.actionId.startsWith(`eve_input:`))return null;let t=e.actionId.slice(10);if(e.selectedOptionValue!==void 0)return t?{optionId:e.selectedOptionValue,requestId:t}:null;if(e.value!==void 0){let n=BUTTON_ACTION_ID_RE.exec(t)?.groups?.requestId;return n?{optionId:e.value,requestId:n}:null}return null}function isHitlAction(e){return e.startsWith(HITL_ACTION_PREFIX)}function renderInputRequestBlocks(e){let t={text:{text:truncateSectionText(e.prompt),type:`mrkdwn`},type:`section`},n=renderInputRequestDetailBlocks(e),
|
|
1
|
+
import{SLACK_SECTION_TEXT_MAX_LENGTH,truncateCardBodyText,truncateModalTitle,truncatePlainText,truncateSectionText}from"#public/channels/slack/limits.js";const HITL_ACTION_PREFIX=`eve_input:`,HITL_FREEFORM_ACTION_PREFIX=`eve_input_freeform:`,HITL_FREEFORM_MODAL_CALLBACK_ID=`eve_input_freeform_submit`,HITL_FREEFORM_MODAL_BLOCK_ID=`eve_freeform_block`,HITL_FREEFORM_MODAL_ACTION_ID=`eve_freeform_text`,BUTTON_ACTION_ID_RE=/^(?<requestId>.+):button:\d+$/u,TOOL_INPUT_SUFFIX="\n```";function deriveHitlResponse(e){if(!e.actionId.startsWith(`eve_input:`))return null;let t=e.actionId.slice(10);if(e.selectedOptionValue!==void 0)return t?{optionId:e.selectedOptionValue,requestId:t}:null;if(e.value!==void 0){let n=BUTTON_ACTION_ID_RE.exec(t)?.groups?.requestId;return n?{optionId:e.value,requestId:n}:null}return null}function isHitlAction(e){return e.startsWith(HITL_ACTION_PREFIX)}function renderInputRequestBlocks(e){let t={text:{text:truncateSectionText(e.prompt),type:`mrkdwn`},type:`section`},n=renderInputRequestDetailBlocks(e),r=`${HITL_ACTION_PREFIX}${e.requestId}`,a=e.options,o=e.allowFreeform===!0||!a||a.length===0;if(a&&a.length>0&&e.display===`select`){let e=a.length<=6?{type:`radio_buttons`,action_id:r,options:a.map(buildOption)}:{type:`static_select`,action_id:r,options:a.map(buildOption),placeholder:{type:`plain_text`,text:`Choose an option`}};return[t,...n,{type:`actions`,elements:[e]}]}if(a&&a.length>0){let t=renderInputRequestCardBlock(e,r),n=renderToolInputContainerBlock(e);return n===void 0?[t]:[t,n]}return o?[t,...n,{type:`actions`,elements:[{type:`button`,action_id:`${HITL_FREEFORM_ACTION_PREFIX}${e.requestId}`,text:{type:`plain_text`,text:`Type your answer`},style:`primary`,value:e.requestId}]}]:[t]}function formatInputRequestFallbackText(e){let t=formatToolInputDetails(e);return t===void 0?e.prompt:`${e.prompt}\n${t}`}function buildFreeformModalView(e){let t=e.prompt?truncateModalTitle(e.prompt):`Your answer`,r=e.prompt?[{type:`section`,text:{type:`mrkdwn`,text:truncateSectionText(e.prompt)}}]:[];return{type:`modal`,callback_id:HITL_FREEFORM_MODAL_CALLBACK_ID,private_metadata:JSON.stringify(e.metadata),title:{type:`plain_text`,text:t},submit:{type:`plain_text`,text:`Submit`},close:{type:`plain_text`,text:`Cancel`},blocks:[...r,{type:`input`,block_id:HITL_FREEFORM_MODAL_BLOCK_ID,element:{type:`plain_text_input`,action_id:HITL_FREEFORM_MODAL_ACTION_ID,multiline:!0,placeholder:{type:`plain_text`,text:`Type your answer here...`}},label:{type:`plain_text`,text:`Answer`}}]}}function isFreeformAction(e){return e.startsWith(HITL_FREEFORM_ACTION_PREFIX)}function freeformRequestIdFromActionId(e){if(!isFreeformAction(e))return;let t=e.slice(19);return t.length>0?t:void 0}function buildCardButton(e,t,n){let i={type:`button`,text:{type:`plain_text`,text:truncatePlainText(e.label),emoji:!1},action_id:`${t}:button:${n}`,value:e.id};return(e.style===`primary`||e.style===`danger`)&&(i.style=e.style),i}function buildOption(e){let t={text:{text:truncatePlainText(e.label),type:`plain_text`},value:e.id},n=truncatePlainText(e.description);return n&&n.length>0&&(t.description={text:n,type:`plain_text`}),t}function renderInputRequestCardBlock(e,n){return{type:`card`,body:{type:`mrkdwn`,text:truncateCardBodyText(`*${e.prompt}*`),verbatim:!1},actions:cardButtonOptions(e).map((e,t)=>buildCardButton(e,n,t))}}function cardButtonOptions(e){let t=e.options??[];if(!isApprovalRequest(e))return t.map(toCardButtonOption);let n=t.find(e=>e.id===`approve`),r=t.find(e=>e.id===`deny`);return!n||!r?t.map(toCardButtonOption):[{id:r.id,label:`Deny`},{id:n.id,label:`Allow`,style:`primary`}]}function toCardButtonOption(e){let t={id:e.id,label:e.label};return e.style===`primary`||e.style===`danger`?{...t,style:e.style}:t}function buildAnsweredBlocks(t){let n=[];for(let e of t.promptBlocks)e!=null&&n.push(e);let r=truncateWithEllipsis(t.answerLabel,SLACK_SECTION_TEXT_MAX_LENGTH-20-1);return n.push({type:`section`,text:{type:`mrkdwn`,text:`:white_check_mark: *${r}*`}}),t.userId&&t.userId.length>0&&n.push({type:`context`,elements:[{type:`mrkdwn`,text:`Answered by <@${t.userId}>`}]}),n}function renderInputRequestDetailBlocks(e){let t=formatToolInputDetails(e);return t===void 0?[]:[{type:`section`,text:{type:`mrkdwn`,text:t}}]}function renderToolInputContainerBlock(e){let t=formatToolInputContainerText(e);if(t!==void 0)return{type:`container`,title:{type:`plain_text`,text:`Tool input`},is_collapsible:!0,default_collapsed:!1,child_blocks:[{type:`section`,text:{type:`mrkdwn`,text:t}}]}}function formatToolInputContainerText(t){if(!isApprovalRequest(t))return;let n=JSON.stringify(t.action.input,null,2);return n===`{}`?void 0:`\`\`\`
|
|
2
|
+
${truncateWithEllipsis(n,SLACK_SECTION_TEXT_MAX_LENGTH-4-4)}${TOOL_INPUT_SUFFIX}`}function formatToolInputDetails(t){if(!isApprovalRequest(t))return;let n=JSON.stringify(t.action.input,null,2);return n===`{}`?void 0:`*Tool input*
|
|
2
3
|
\`\`\`
|
|
3
|
-
${truncateWithEllipsis(n,SLACK_SECTION_TEXT_MAX_LENGTH-17-4)}
|
|
4
|
-
\`\`\``}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function isApprovalRequest(e){return e.display===`confirmation`&&e.options?.length===2&&e.options[0]?.id===`approve`&&e.options[1]?.id===`deny`}export{HITL_ACTION_PREFIX,HITL_FREEFORM_ACTION_PREFIX,HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,formatInputRequestFallbackText,freeformRequestIdFromActionId,isFreeformAction,isHitlAction,renderInputRequestBlocks};
|
|
4
|
+
${truncateWithEllipsis(n,SLACK_SECTION_TEXT_MAX_LENGTH-17-4)}${TOOL_INPUT_SUFFIX}`}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function isApprovalRequest(e){return e.display===`confirmation`&&e.options?.length===2&&e.options[0]?.id===`approve`&&e.options[1]?.id===`deny`}export{HITL_ACTION_PREFIX,HITL_FREEFORM_ACTION_PREFIX,HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,formatInputRequestFallbackText,freeformRequestIdFromActionId,isFreeformAction,isHitlAction,renderInputRequestBlocks};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export type { ModelMessage } from "ai";
|
|
8
8
|
export { slackChannel, type SlackApiResponse, type SlackAuthorizationEventContext, type SlackAuthorizationRequiredHandler, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackHandle, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInstrumentationMetadata, type SlackInitialMessage, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveTarget, type SlackThread, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
|
|
9
9
|
export type { SlackAttachment, SlackAuthor, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
|
|
10
|
-
export { slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
|
|
10
|
+
export { callSlackApi, resolveSlackBotToken, slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
|
|
11
11
|
export { defaultSlackAuth } from "#public/channels/slack/defaults.js";
|
|
12
12
|
export { loadThreadContextMessages, type LoadThreadContextMessagesOptions, type ThreadContextSince, } from "#public/channels/slack/thread.js";
|
|
13
13
|
export { cardToBlocks, cardToFallbackText, type BlockKitBlock, } from "#public/channels/slack/blocks.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Actions,Button,Card,CardLink,CardText,Divider,ExternalSelect,Field,Fields,Image,LinkButton,Modal,RadioSelect,Section,Select,SelectOption,Table,TextInput,cardChildToFallbackText,isCardElement}from"#compiled/chat/index.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{slackContinuationToken}from"#public/channels/slack/api.js";import{slackChannel}from"#public/channels/slack/slackChannel.js";import{defaultSlackAuth}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";export{Actions,Button,Card,CardLink,CardText,Divider,ExternalSelect,Field,Fields,Image,LinkButton,Modal,RadioSelect,Section,Select,SelectOption,Table,TextInput,cardChildToFallbackText,cardToBlocks,cardToFallbackText,defaultSlackAuth,isCardElement,loadThreadContextMessages,slackChannel,slackContinuationToken};
|
|
1
|
+
import{Actions,Button,Card,CardLink,CardText,Divider,ExternalSelect,Field,Fields,Image,LinkButton,Modal,RadioSelect,Section,Select,SelectOption,Table,TextInput,cardChildToFallbackText,isCardElement}from"#compiled/chat/index.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{callSlackApi,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{slackChannel}from"#public/channels/slack/slackChannel.js";import{defaultSlackAuth}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";export{Actions,Button,Card,CardLink,CardText,Divider,ExternalSelect,Field,Fields,Image,LinkButton,Modal,RadioSelect,Section,Select,SelectOption,Table,TextInput,callSlackApi,cardChildToFallbackText,cardToBlocks,cardToFallbackText,defaultSlackAuth,isCardElement,loadThreadContextMessages,resolveSlackBotToken,slackChannel,slackContinuationToken};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}async function handleInteractionPost(e,n
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}function buildAnsweredHitlMessageBlocks(e){let t=findActionBlockIndex(e.messageBlocks,e.actionId);if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],r=answeredBlocksFromActionBlock({answerLabel:e.answerLabel,block:n,userId:e.userId})??buildAnsweredBlocks({promptBlocks:promptBlocksFromActionBlock(n),answerLabel:e.answerLabel,userId:e.userId});return[...e.messageBlocks.slice(0,t),...r,...e.messageBlocks.slice(t+1)]}function findActionBlockIndex(e,t){return e.findIndex(e=>blockContainsActionId(e,t))}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function isObjectRecord(e){return typeof e==`object`&&!!e}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let r=e.userId.length>0?` by <@${e.userId}>`:``,i=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-r.length;return truncateCardSubtext(`:white_check_mark: *${truncateWithEllipsis(e.answerLabel,i)}*${r}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}async function handleInteractionPost(e,t,n){let i=new Response(`ok`,{status:200}),s;try{s=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(s.kind===`view_submission`)return handleViewSubmission(s,t,n);if(s.kind!==`block_actions`)return i;let c=parseBlockActionsPayload(s);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:s.raw,interaction:c,freeformAction:l,deps:n}),i;let u=slackContinuationToken(c.channelId,c.threadTs),d=c.actions.map(deriveHitlResponse).filter(e=>e!==null);if(d.length>0){let e=c.actions[0]?.user;if(!e)return i;t.waitUntil(t.send({inputResponses:d},{auth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:e.id,userName:e.username??e.name}),continuationToken:u,state:{channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId??null,triggeringUserId:e.id}}).catch(e=>{log.error(`HITL interaction delivery failed`,{error:e})})),t.waitUntil(updateAnsweredHitlCard(c,n).catch(e=>{log.error(`HITL answered-card update failed`,{error:e})}))}let p=n.config.onInteraction;if(p){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let{thread:i,slack:a}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId}),o={thread:i,slack:a};for(let n of e)t.waitUntil(Promise.resolve(p(n,o)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=freeformRequestIdFromActionId(e.freeformAction.actionId)??e.freeformAction.value;if(!n){log.warn(`freeform button click missing requestId`);return}let r=e.freeformAction.messageTs;if(!r){log.warn(`freeform button click missing messageTs`);return}let o=buildFreeformModalView({metadata:{continuationToken:slackContinuationToken(e.interaction.channelId,e.interaction.threadTs),channelId:e.interaction.channelId,threadTs:e.interaction.threadTs,messageTs:r,requestId:n},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:o})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let i;try{i=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!i.continuationToken||!i.requestId||!i.messageTs||!i.channelId||!i.threadTs)return r;let a=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(a.length===0)return r;let u=e.user,d=e.userId,f=u?.teamId??e.teamId??null;return t.waitUntil(t.send({inputResponses:[{requestId:i.requestId,text:a}]},{auth:buildSlackAuthContext({channelId:i.channelId,teamId:f,threadTs:i.threadTs,userId:d,userName:u?.username??u?.name}),continuationToken:i.continuationToken,state:{channelId:i.channelId,threadTs:i.threadTs,teamId:f,triggeringUserId:d}}).catch(e=>{log.error(`freeform answer delivery failed`,{error:e})})),t.waitUntil(updateAnsweredFreeformCard({channelId:i.channelId,messageTs:i.messageTs,answerLabel:a,userId:d??void 0,deps:n}).catch(e=>{log.error(`freeform answered-card update failed`,{error:e})})),r}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n||!n.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;if(!r)return;let a=buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),o=await resolveSlackBotToken(t.config.credentials?.botToken),s=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:n.messageTs,blocks:a,text:`Answered: ${r}`})});if(!s.ok)throw Error(`Slack chat.update returned HTTP ${s.status}`)}async function updateAnsweredFreeformCard(e){let t=buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId}),n=await resolveSlackBotToken(e.deps.config.credentials?.botToken),r=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${n}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:t,text:`Answered: ${e.answerLabel}`})});if(!r.ok)throw Error(`Slack chat.update returned HTTP ${r.status}`)}export{handleInteractionPost,parseBlockActionsPayload};
|
|
@@ -25,6 +25,14 @@ export declare const SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH = 75;
|
|
|
25
25
|
* longer fails the whole post with `invalid_blocks`.
|
|
26
26
|
*/
|
|
27
27
|
export declare const SLACK_SECTION_TEXT_MAX_LENGTH = 3000;
|
|
28
|
+
/**
|
|
29
|
+
* Block Kit `card` blocks cap `body.text` at 200 chars.
|
|
30
|
+
*/
|
|
31
|
+
export declare const SLACK_CARD_BODY_TEXT_MAX_LENGTH = 200;
|
|
32
|
+
/**
|
|
33
|
+
* Block Kit `card` blocks cap `subtext.text` at 200 chars.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SLACK_CARD_SUBTEXT_MAX_LENGTH = 200;
|
|
28
36
|
/**
|
|
29
37
|
* Top-level `text` field on `chat.postMessage` is capped at 40000 chars.
|
|
30
38
|
*/
|
|
@@ -58,6 +66,16 @@ export declare function truncatePlainText(value: string | undefined): string | u
|
|
|
58
66
|
* trailing ellipsis.
|
|
59
67
|
*/
|
|
60
68
|
export declare function truncateSectionText(value: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Caps a card block's `body.text` at the Slack limit with a trailing
|
|
71
|
+
* ellipsis.
|
|
72
|
+
*/
|
|
73
|
+
export declare function truncateCardBodyText(value: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* Caps a card block's `subtext.text` at the Slack limit with a trailing
|
|
76
|
+
* ellipsis.
|
|
77
|
+
*/
|
|
78
|
+
export declare function truncateCardSubtext(value: string): string;
|
|
61
79
|
/**
|
|
62
80
|
* Caps a `chat.postMessage` `text` field at the Slack limit with a
|
|
63
81
|
* trailing ellipsis.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const SLACK_TYPING_STATUS_MAX_LENGTH=50,SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH=75,SLACK_SECTION_TEXT_MAX_LENGTH=3e3,SLACK_MESSAGE_TEXT_MAX_LENGTH=4e4,SLACK_MAX_BLOCKS_PER_MESSAGE=50,SLACK_MODAL_TITLE_MAX_LENGTH=24;function truncateTypingStatus(e){return truncateWithEllipsis(stripTypingStatusMarkdown(e).trim().replace(/\s+/gu,` `),50)}function truncatePlainText(e){if(e!==void 0)return truncateWithEllipsis(e,75)}function truncateSectionText(e){return truncateWithEllipsis(e,SLACK_SECTION_TEXT_MAX_LENGTH)}function truncateMessageText(e){return truncateWithEllipsis(e,SLACK_MESSAGE_TEXT_MAX_LENGTH)}function truncateModalTitle(e){return truncateWithEllipsis(e,24)}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function stripTypingStatusMarkdown(e){return e.replace(/\[([^\]]+)\]\([^)]+\)/gu,`$1`).replace(/`([^`]+)`/gu,`$1`).replace(/~~([^~]+)~~/gu,`$1`).replace(/(\*\*|__)([^*_]+)\1/gu,`$2`).replace(/(^|[^\p{L}\p{N}])([*_])([^*_]+)\2(?=$|[^\p{L}\p{N}])/gu,`$1$3`)}export{SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH,SLACK_MAX_BLOCKS_PER_MESSAGE,SLACK_MESSAGE_TEXT_MAX_LENGTH,SLACK_MODAL_TITLE_MAX_LENGTH,SLACK_SECTION_TEXT_MAX_LENGTH,SLACK_TYPING_STATUS_MAX_LENGTH,truncateMessageText,truncateModalTitle,truncatePlainText,truncateSectionText,truncateTypingStatus};
|
|
1
|
+
const SLACK_TYPING_STATUS_MAX_LENGTH=50,SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH=75,SLACK_SECTION_TEXT_MAX_LENGTH=3e3,SLACK_CARD_BODY_TEXT_MAX_LENGTH=200,SLACK_CARD_SUBTEXT_MAX_LENGTH=200,SLACK_MESSAGE_TEXT_MAX_LENGTH=4e4,SLACK_MAX_BLOCKS_PER_MESSAGE=50,SLACK_MODAL_TITLE_MAX_LENGTH=24;function truncateTypingStatus(e){return truncateWithEllipsis(stripTypingStatusMarkdown(e).trim().replace(/\s+/gu,` `),50)}function truncatePlainText(e){if(e!==void 0)return truncateWithEllipsis(e,75)}function truncateSectionText(e){return truncateWithEllipsis(e,SLACK_SECTION_TEXT_MAX_LENGTH)}function truncateCardBodyText(e){return truncateWithEllipsis(e,200)}function truncateCardSubtext(e){return truncateWithEllipsis(e,200)}function truncateMessageText(e){return truncateWithEllipsis(e,SLACK_MESSAGE_TEXT_MAX_LENGTH)}function truncateModalTitle(e){return truncateWithEllipsis(e,24)}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function stripTypingStatusMarkdown(e){return e.replace(/\[([^\]]+)\]\([^)]+\)/gu,`$1`).replace(/`([^`]+)`/gu,`$1`).replace(/~~([^~]+)~~/gu,`$1`).replace(/(\*\*|__)([^*_]+)\1/gu,`$2`).replace(/(^|[^\p{L}\p{N}])([*_])([^*_]+)\2(?=$|[^\p{L}\p{N}])/gu,`$1$3`)}export{SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH,SLACK_CARD_BODY_TEXT_MAX_LENGTH,SLACK_CARD_SUBTEXT_MAX_LENGTH,SLACK_MAX_BLOCKS_PER_MESSAGE,SLACK_MESSAGE_TEXT_MAX_LENGTH,SLACK_MODAL_TITLE_MAX_LENGTH,SLACK_SECTION_TEXT_MAX_LENGTH,SLACK_TYPING_STATUS_MAX_LENGTH,truncateCardBodyText,truncateCardSubtext,truncateMessageText,truncateModalTitle,truncatePlainText,truncateSectionText,truncateTypingStatus};
|
|
@@ -14,6 +14,10 @@ interface CreateRuntimeSandboxKeysInput {
|
|
|
14
14
|
/**
|
|
15
15
|
* Creates the stable runtime template and session keys for one sandbox
|
|
16
16
|
* definition under the current artifact source and backend.
|
|
17
|
+
*
|
|
18
|
+
* Both keys derive from one {@link RuntimeSandboxKeyParts} value, so the
|
|
19
|
+
* coupling holds by construction: the session key rotates exactly when
|
|
20
|
+
* the template content rotates.
|
|
17
21
|
*/
|
|
18
22
|
export declare function createRuntimeSandboxKeys(input: CreateRuntimeSandboxKeysInput): Promise<{
|
|
19
23
|
readonly sessionKey: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{realpath}from"node:fs/promises";import{createHash}from"node:crypto";import{getRuntimeCompiledArtifactsCacheKey,getRuntimeCompiledArtifactsSandboxAppRoot}from"#runtime/compiled-artifacts-source.js";import{loadCompileMetadata}from"#runtime/loaders/compile-metadata.js";async function createRuntimeSandboxKeys(e){return{sessionKey:
|
|
1
|
+
import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{realpath}from"node:fs/promises";import{createHash}from"node:crypto";import{resolveVercelProjectIdFromEnvironment}from"#shared/vercel-project.js";import{getRuntimeCompiledArtifactsCacheKey,getRuntimeCompiledArtifactsSandboxAppRoot}from"#runtime/compiled-artifacts-source.js";import{loadCompileMetadata}from"#runtime/loaders/compile-metadata.js";async function createRuntimeSandboxKeys(e){let t=await deriveRuntimeSandboxKeyParts(e);return{sessionKey:buildRuntimeSandboxSessionKey(e,t),templateKey:buildRuntimeSandboxTemplateKey(e,t)}}async function createRuntimeSandboxTemplateKey(e){return buildRuntimeSandboxTemplateKey(e,await deriveRuntimeSandboxKeyParts(e))}async function deriveRuntimeSandboxKeyParts(e){let t=await loadCompileMetadataForKeys(e.compiledArtifactsSource);return{metadata:t,scope:await resolveRuntimeSandboxScope(e),versionHash:e.templatePlan.kind===`none`?null:resolveRuntimeSandboxVersionHash({compiledArtifactsSource:e.compiledArtifactsSource,metadata:t,nodeId:e.nodeId,sourceId:e.sourceId,templatePlan:e.templatePlan})}}function buildRuntimeSandboxTemplateKey(e,t){if(t.versionHash===null)return null;let n=createStableHash(`${resolvePackageVersionForTemplateKey(t.metadata)}:7:${t.versionHash}`).slice(0,20);return sanitizeRuntimeSandboxKey(`eve-sbx-tpl-${e.backendName}-${t.scope}-${n}`)}function buildRuntimeSandboxSessionKey(e,t){let n=createStableHash(`7:${t.versionHash??`none`}`).slice(0,12),r=sanitizeRuntimeSandboxKey(e.nodeId);return sanitizeRuntimeSandboxKey(`eve-sbx-ses-${e.backendName}-${t.scope}-${n}-${e.sessionId}-${r}`)}function resolvePackageVersionForTemplateKey(t){return t?.generator.version??resolveInstalledPackageInfo().version}async function loadCompileMetadataForKeys(e){try{return await loadCompileMetadata({compiledArtifactsSource:e})}catch{return null}}async function resolveRuntimeSandboxScope(e){if(e.backendName===`vercel`){let e=resolveVercelProjectIdFromEnvironment();if(e!==void 0)return createStableHash(`vercel-project:${e}`).slice(0,16)}let n=getRuntimeCompiledArtifactsSandboxAppRoot(e.compiledArtifactsSource);return n===void 0?createStableHash(getRuntimeCompiledArtifactsCacheKey(e.compiledArtifactsSource)).slice(0,16):createStableHash(await realpath(n)).slice(0,16)}function resolveRuntimeSandboxVersionHash(e){let t=e.templatePlan.contentHash??resolveSourceGraphHash(e.metadata,e.compiledArtifactsSource);return e.templatePlan.kind===`bootstrap`?createStableHash(`bootstrap:${e.templatePlan.revalidationKey??``}:${e.templatePlan.sourceHash}:${t}:${e.nodeId}:${e.sourceId}`):createStableHash(`workspace-content:${t}:${e.nodeId}:${e.sourceId}`)}function resolveSourceGraphHash(e,t){return e?.discovery.sourceGraphHash??getRuntimeCompiledArtifactsCacheKey(t)}function createStableHash(e){return createHash(`sha256`).update(e).digest(`hex`)}function sanitizeRuntimeSandboxKey(e){return e.replaceAll(/[^a-zA-Z0-9._-]+/g,`-`).slice(0,120)}export{createRuntimeSandboxKeys,createRuntimeSandboxTemplateKey};
|
|
@@ -14,8 +14,6 @@ export type RuntimeSandboxTemplatePlan = {
|
|
|
14
14
|
readonly kind: "bootstrap";
|
|
15
15
|
readonly revalidationKey?: string;
|
|
16
16
|
readonly sourceHash: string;
|
|
17
|
-
} | {
|
|
18
|
-
readonly kind: "source-graph";
|
|
19
17
|
};
|
|
20
18
|
/**
|
|
21
19
|
* Chooses the template strategy for one resolved sandbox definition.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.22.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.22.1`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claims eve reads from a Vercel OIDC token payload.
|
|
3
|
+
*
|
|
4
|
+
* Decoded locally without signature verification: callers use the values
|
|
5
|
+
* as stable identifiers (sandbox key derivation, credential routing),
|
|
6
|
+
* never to make authentication decisions.
|
|
7
|
+
*/
|
|
8
|
+
export interface VercelOidcTokenClaims {
|
|
9
|
+
readonly ownerId: string | undefined;
|
|
10
|
+
readonly projectId: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Decodes the payload claims of a Vercel OIDC token. Values that are not
|
|
14
|
+
* decodable JWTs, or that carry no recognized claims, decode to empty
|
|
15
|
+
* claims — callers only ever check individual fields.
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeVercelOidcTokenClaims(token: string): VercelOidcTokenClaims;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the Vercel project id visible to this process: the
|
|
20
|
+
* `VERCEL_PROJECT_ID` env var when exposed, otherwise the `project_id`
|
|
21
|
+
* claim of `VERCEL_OIDC_TOKEN`. Both sources exist at build time and at
|
|
22
|
+
* deployed runtime and name the same project, so identifiers derived
|
|
23
|
+
* from this value agree across the two phases.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveVercelProjectIdFromEnvironment(): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const EMPTY_CLAIMS={ownerId:void 0,projectId:void 0};function decodeVercelOidcTokenClaims(t){let n=t.split(`.`)[1];if(n===void 0||n.length===0)return EMPTY_CLAIMS;let r;try{r=JSON.parse(Buffer.from(n,`base64url`).toString(`utf8`))}catch{return EMPTY_CLAIMS}if(typeof r!=`object`||!r)return EMPTY_CLAIMS;let i=r;return{ownerId:typeof i.owner_id==`string`?i.owner_id:void 0,projectId:typeof i.project_id==`string`?i.project_id:void 0}}function resolveVercelProjectIdFromEnvironment(){let e=process.env.VERCEL_PROJECT_ID?.trim();if(e!==void 0&&e.length>0)return e;let t=process.env.VERCEL_OIDC_TOKEN?.trim();if(!(t===void 0||t.length===0))return decodeVercelOidcTokenClaims(t).projectId}export{decodeVercelOidcTokenClaims,resolveVercelProjectIdFromEnvironment};
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -70,6 +70,33 @@ export default slackChannel({
|
|
|
70
70
|
|
|
71
71
|
`threadContext` performs one `conversations.replies` request for each triggering thread reply and requires the matching Slack history scope. Omit it when the agent should see only direct mentions. `loadThreadContextMessages` remains available when you need custom filtering or non-model processing of the raw thread messages.
|
|
72
72
|
|
|
73
|
+
### Slack API calls outside a handler
|
|
74
|
+
|
|
75
|
+
Inside webhook-side handlers (`onAppMention`, `onInteraction`, `events`),
|
|
76
|
+
`ctx.slack.request(operation, body)` is the raw-API escape hatch. Outside
|
|
77
|
+
those contexts there is no handle — a schedule resolving reactions on old
|
|
78
|
+
messages, for example, has no inbound Slack request. For that, call the
|
|
79
|
+
same primitive the handle uses directly:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { callSlackApi, resolveSlackBotToken } from "eve/channels/slack";
|
|
83
|
+
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
84
|
+
|
|
85
|
+
const { botToken } = connectSlackCredentials("slack/my-agent");
|
|
86
|
+
const response = await callSlackApi({
|
|
87
|
+
botToken,
|
|
88
|
+
operation: "reactions.get",
|
|
89
|
+
body: { channel: "C0123456789", timestamp: "1712345678.000100", full: true },
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) throw new Error(String(response.error));
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`callSlackApi` resolves function-form tokens (secret managers, Connect
|
|
95
|
+
rotation) at call time and form-encodes the body — the only safe default,
|
|
96
|
+
since Slack's JSON support is partial (`conversations.replies` rejects
|
|
97
|
+
JSON). `resolveSlackBotToken` materializes a `SlackBotToken` to a string
|
|
98
|
+
when you need the bearer token itself.
|
|
99
|
+
|
|
73
100
|
### Delivery
|
|
74
101
|
|
|
75
102
|
The default handlers reply in-thread and show progress. Typing indicators post automatically: `Thinking…` on inbound, `Working…` on `turn.started`, a truncated reasoning snippet on `reasoning.appended`, and tool status on `actions.requested`. Reasoning snippets build progressively: extensions of at least four characters appear immediately, while smaller streamed deltas use the five-second refresh interval to avoid one Slack request per token. Override `events["reasoning.appended"]` if you prefer generic wording. Override `onAppMention` or the `events` handlers to customize.
|
|
@@ -138,6 +138,8 @@ export default defineMcpClientConnection({
|
|
|
138
138
|
|
|
139
139
|
`"linear/myagent"` is the UID you chose when registering the Connect client. `connect("linear/myagent")` is shorthand for a user-scoped interactive OAuth connection: eve resolves a token for the active user before each tool call, emits `authorization.required` when that user has not authorized yet, and resumes the parked turn after the callback completes.
|
|
140
140
|
|
|
141
|
+
When a local subagent needs interactive authorization, eve surfaces the authorization lifecycle on the root session's channel, including through nested subagent chains. The challenge still points to the child session's callback, so completing it resumes the child directly while the parent continues waiting for its result.
|
|
142
|
+
|
|
141
143
|
That means the channel that creates or continues the session must authenticate a real user. For a web app, configure `agent/channels/eve.ts` so your app session maps to `principalType: "user"`; for platform channels, use the built-in channel auth that maps the sender to a user principal. If no authenticated user is attached to the session, the first user-scoped connection call fails with `reason: "principal_required"`.
|
|
142
144
|
|
|
143
145
|
If the remote service should act as the agent itself instead of the end-user, make the Connect connection app-scoped:
|
package/docs/sandbox.mdx
CHANGED
|
@@ -164,7 +164,7 @@ You can also write your own backend. A `SandboxBackend` is an adapter object wit
|
|
|
164
164
|
There are two hooks, scoped differently:
|
|
165
165
|
|
|
166
166
|
- **`bootstrap({ use })`** is template-scoped and runs once when the template is built. Put reusable setup here that every later session inherits, such as cloning a baseline repo, installing dependencies, or seeding files. Call `use()` to get a `SandboxSession`. Only template filesystem state and supported backend metadata carry into later sessions; config like network policy does not. If external inputs affect what bootstrap produces, set `revalidationKey: () => string` so eve knows when to rebuild the template (authored sandbox source and seed contents are already tracked for you).
|
|
167
|
-
- **`onSession({ use, ctx })`** is durable-session-scoped and runs once per session. Put per-session setup here, including network policy, resources, timeout, per-user credentials, and one-time markers. Because it runs inside the active runtime context, it can read `ctx.session` and derive the current principal without baking credentials into the template. Call `use(opts?)` to get a `SandboxSession`; `opts` flow to the backend's update path after create.
|
|
167
|
+
- **`onSession({ use, ctx })`** is durable-session-scoped and runs once per session (and again if a sandbox definition change replaces the session's sandbox). Put per-session setup here, including network policy, resources, timeout, per-user credentials, and one-time markers. Because it runs inside the active runtime context, it can read `ctx.session` and derive the current principal without baking credentials into the template. Call `use(opts?)` to get a `SandboxSession`; `opts` flow to the backend's update path after create.
|
|
168
168
|
|
|
169
169
|
If you require a network policy or other configuration for every session, configure it on the backend factory or in `onSession`; do not rely on bootstrap-only configuration.
|
|
170
170
|
|
|
@@ -185,6 +185,8 @@ export default defineSandbox({
|
|
|
185
185
|
|
|
186
186
|
Sessions are persistent, and how the underlying runtime idles out depends on the backend. On the Vercel backend, the VM times out after a period of inactivity (default 30 minutes); eve preserves the filesystem and resumes the sandbox on the next message as if nothing happened, even days later. The Docker backend keeps a long-lived container per durable session and persists `/workspace` across turns without that timeout, and the just-bash backend stores its virtual filesystem under `.eve/sandbox-cache/`. In every case, `/workspace` survives between turns for the same session.
|
|
187
187
|
|
|
188
|
+
Session sandboxes are keyed per durable session, not per deployment, so redeploying your app does not discard them. A session gets a replacement sandbox only when the sandbox definition itself changes — the authored sandbox source, workspace seed content, or `revalidationKey` — in which case the next turn starts from the rebuilt template and `onSession` runs again.
|
|
189
|
+
|
|
188
190
|
When the eve server stops, no sandbox compute outlives it. `eve dev` stops the sandboxes it started when the dev server closes, and a self-hosted production server stops every open sandbox on shutdown (`SIGTERM`/`SIGINT`). Session state persists across the stop — the next server start reattaches each durable session from its stopped container, VM, or snapshot. Custom `SandboxBackend` adapters participate through the handle's `shutdown()` method: stop the underlying compute, keeping the session reattachable from persisted state where the backend supports it.
|
|
189
191
|
|
|
190
192
|
## Network policy
|
package/docs/subagents.mdx
CHANGED
|
@@ -109,7 +109,7 @@ Because the name lives in the same runtime tool namespace as authored tools, a s
|
|
|
109
109
|
|
|
110
110
|
Do not rely on subagent delegation by itself as an approval boundary. Put sensitive tools behind `approval`, connection approval, route/session authorization, or other controls wherever those tools can be called.
|
|
111
111
|
|
|
112
|
-
Each delegated subagent spins up its own child session and stream. The parent stream carries
|
|
112
|
+
Each delegated subagent spins up its own child session and stream. The parent stream carries the control-plane events `subagent.called` and `subagent.completed`, plus interactive `input.requested`, `authorization.required`, and `authorization.completed` events proxied from descendants so the root channel can prompt the user. To follow the child's other progress, read `subagent.called.data.childSessionId` and subscribe at `GET /eve/v1/session/:childSessionId/stream`.
|
|
113
113
|
|
|
114
114
|
## When to split
|
|
115
115
|
|