eve 0.31.2 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/src/chunks/{use-eve-agent-CF5D6jSV.js → use-eve-agent-CX87G1vU.js} +30 -5
- package/dist/src/chunks/{use-eve-agent-DbYZLG0h.js → use-eve-agent-DwyRde1N.js} +30 -5
- package/dist/src/cli/commands/integration-connect.d.ts +1 -0
- package/dist/src/cli/commands/integration-connect.js +1 -1
- package/dist/src/cli/commands/integration-setup.d.ts +4 -0
- package/dist/src/cli/commands/integration-setup.js +1 -1
- package/dist/src/cli/commands/register-integration-commands.js +1 -1
- package/dist/src/cli/commands/register-registry-commands.js +1 -1
- package/dist/src/cli/commands/registry-declared-setups.d.ts +23 -0
- package/dist/src/cli/commands/registry-declared-setups.js +1 -0
- package/dist/src/cli/commands/registry-metadata.d.ts +69 -0
- package/dist/src/cli/commands/registry-metadata.js +1 -1
- package/dist/src/cli/commands/registry-package.d.ts +3 -3
- package/dist/src/cli/commands/registry-package.js +1 -1
- package/dist/src/cli/commands/registry-presentation.d.ts +20 -0
- package/dist/src/cli/commands/registry-presentation.js +7 -0
- package/dist/src/cli/commands/registry-search-presentation.d.ts +16 -0
- package/dist/src/cli/commands/registry-search-presentation.js +6 -0
- package/dist/src/cli/commands/registry-setup-command.d.ts +5 -2
- package/dist/src/cli/commands/registry-setup-command.js +1 -2
- package/dist/src/cli/commands/registry.d.ts +16 -7
- package/dist/src/cli/commands/registry.js +1 -6
- package/dist/src/cli/commands/setup-answers.d.ts +2 -0
- package/dist/src/cli/commands/setup-answers.js +1 -0
- package/dist/src/cli/commands/setup-headless.d.ts +79 -0
- package/dist/src/cli/commands/setup-headless.js +1 -0
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.js +3 -3
- package/dist/src/cli/dev/tui/setup-flow.d.ts +8 -1
- package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
- package/dist/src/cli/dev/tui/traces/trace-conversation.d.ts +1 -1
- package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
- package/dist/src/client/url.d.ts +5 -0
- package/dist/src/client/url.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +8 -8
- package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +33 -13
- package/dist/src/compiled/@ai-sdk/anthropic/index.js +4 -2
- package/dist/src/compiled/@ai-sdk/google/index.d.ts +50 -3
- package/dist/src/compiled/@ai-sdk/google/index.js +7 -7
- package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +33 -3
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/openai/index.d.ts +140 -9
- package/dist/src/compiled/@ai-sdk/openai/index.js +9 -6
- package/dist/src/compiled/@ai-sdk/otel/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/provider/index.d.ts +300 -6
- package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +70 -20
- package/dist/src/compiled/@opentelemetry/api/index.d.ts +2 -1
- package/dist/src/compiled/@photon-ai/chat-adapter-imessage/index.js +1 -1
- package/dist/src/compiled/@vercel/otel/index.d.ts +6 -0
- package/dist/src/compiled/@vercel/sandbox/index.js +1 -1
- package/dist/src/compiled/_chunks/node/undici-CUIybhQ2.js +95 -0
- package/dist/src/compiled/_chunks/workflow/dist-eUxFublw.js +77 -0
- package/dist/src/compiled/shadcn-registry/index.js +309 -215
- package/dist/src/compiler/extension-compatibility.d.ts +10 -10
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/context/build-callback-context.js +1 -1
- package/dist/src/execution/authorization-callback-match.d.ts +14 -0
- package/dist/src/execution/authorization-callback-match.js +1 -0
- package/dist/src/execution/next-driver-action.d.ts +1 -0
- package/dist/src/execution/parked-delivery-wait.d.ts +16 -1
- package/dist/src/execution/parked-delivery-wait.js +1 -1
- package/dist/src/execution/sandbox/abort-bound-session.d.ts +1 -1
- package/dist/src/execution/sandbox/bindings/docker.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.d.ts +1 -0
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/ensure.js +1 -1
- package/dist/src/execution/session-command-inbox.d.ts +26 -1
- package/dist/src/execution/session-command-inbox.js +1 -1
- package/dist/src/execution/turn-execution-cursor.d.ts +1 -0
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +3 -4
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/authorization.d.ts +24 -11
- package/dist/src/harness/authorization.js +1 -1
- package/dist/src/harness/input-extraction.js +1 -1
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/provider-tools.d.ts +1 -1
- package/dist/src/harness/provider-tools.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/protocol/routes.d.ts +8 -6
- package/dist/src/protocol/routes.js +1 -1
- package/dist/src/public/channels/linear/linearChannel.d.ts +2 -0
- package/dist/src/public/channels/linear/linearChannel.js +1 -1
- package/dist/src/public/channels/linear/verify.js +1 -1
- package/dist/src/public/channels/slack/attachments.js +1 -1
- package/dist/src/public/channels/slack/defaults.js +1 -1
- package/dist/src/public/channels/slack/hitl.d.ts +14 -1
- package/dist/src/public/channels/slack/hitl.js +2 -2
- package/dist/src/public/connections/index.d.ts +1 -0
- package/dist/src/public/definitions/callback-context.d.ts +2 -2
- package/dist/src/public/definitions/connections/mcp.d.ts +9 -0
- package/dist/src/public/definitions/connections/openapi.d.ts +9 -0
- package/dist/src/public/definitions/connections/tool-call.d.ts +21 -0
- package/dist/src/public/definitions/connections/tool-call.js +1 -0
- package/dist/src/public/definitions/sandbox.d.ts +1 -1
- package/dist/src/public/definitions/tool.d.ts +9 -9
- package/dist/src/public/sandbox/index.d.ts +1 -1
- package/dist/src/public/tools/web-search.d.ts +2 -2
- package/dist/src/runtime/connections/callback-route.d.ts +4 -5
- package/dist/src/runtime/connections/callback-route.js +1 -1
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/openapi-client.js +1 -1
- package/dist/src/runtime/connections/provided-arguments.d.ts +9 -0
- package/dist/src/runtime/connections/provided-arguments.js +1 -0
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -2
- package/dist/src/runtime/framework-tools/todo.js +1 -1
- package/dist/src/runtime/input/types.d.ts +1 -4
- package/dist/src/runtime/resolve-connection.js +1 -1
- package/dist/src/runtime/types.d.ts +2 -0
- package/dist/src/sandbox/state.d.ts +1 -0
- package/dist/src/setup/ask-editable.d.ts +22 -0
- package/dist/src/setup/ask-editable.js +1 -0
- package/dist/src/setup/ask-signals.d.ts +23 -0
- package/dist/src/setup/ask-signals.js +1 -0
- package/dist/src/setup/ask.d.ts +24 -25
- package/dist/src/setup/ask.js +1 -1
- package/dist/src/setup/boxes/deploy-project.js +1 -1
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/connection-connector.js +3 -2
- package/dist/src/setup/flows/registry-session.d.ts +33 -0
- package/dist/src/setup/flows/registry-session.js +1 -0
- package/dist/src/setup/flows/registry.d.ts +16 -0
- package/dist/src/setup/flows/registry.js +1 -1
- package/dist/src/setup/index.js +1 -1
- package/dist/src/setup/integrations/discord/setup.d.ts +19 -7
- package/dist/src/setup/integrations/discord/setup.js +4 -3
- package/dist/src/setup/integrations/github/setup.d.ts +36 -7
- package/dist/src/setup/integrations/github/setup.js +2 -2
- package/dist/src/setup/integrations/linear/setup.d.ts +21 -9
- package/dist/src/setup/integrations/linear/setup.js +2 -2
- package/dist/src/setup/integrations/photon/management.d.ts +8 -1
- package/dist/src/setup/integrations/photon/management.js +1 -1
- package/dist/src/setup/integrations/photon/setup-flow.d.ts +28 -24
- package/dist/src/setup/integrations/photon/setup-flow.js +3 -3
- package/dist/src/setup/integrations/photon/setup.d.ts +1 -3
- package/dist/src/setup/integrations/photon/setup.js +1 -1
- package/dist/src/setup/integrations/registry.d.ts +1 -1
- package/dist/src/setup/integrations/registry.js +1 -1
- package/dist/src/setup/integrations/runner.d.ts +14 -11
- package/dist/src/setup/integrations/runner.js +1 -1
- package/dist/src/setup/integrations/shared/prerequisite.d.ts +17 -0
- package/dist/src/setup/integrations/shared/prerequisite.js +1 -0
- package/dist/src/setup/integrations/shared/ui.d.ts +22 -15
- package/dist/src/setup/integrations/shared/ui.js +2 -2
- package/dist/src/setup/integrations/shared/vercel-project.d.ts +11 -0
- package/dist/src/setup/integrations/shared/vercel-project.js +1 -0
- package/dist/src/setup/integrations/slack/setup.d.ts +25 -10
- package/dist/src/setup/integrations/slack/setup.js +1 -1
- package/dist/src/setup/integrations/types.d.ts +43 -13
- package/dist/src/setup/integrations/types.js +1 -1
- package/dist/src/setup/integrations/web/setup.d.ts +22 -3
- package/dist/src/setup/integrations/web/setup.js +1 -1
- package/dist/src/setup/prompter.d.ts +8 -0
- package/dist/src/setup/question-options.d.ts +15 -0
- package/dist/src/setup/question-options.js +1 -0
- package/dist/src/setup/registry-setup-client.d.ts +3 -4
- package/dist/src/setup/registry-setup-client.js +2 -2
- package/dist/src/setup/registry-setup-completion.d.ts +3 -0
- package/dist/src/setup/registry-setup-completion.js +1 -0
- package/dist/src/setup/registry-setup-protocol.d.ts +22 -3
- package/dist/src/setup/registry-setup-protocol.js +1 -1
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +55 -3
- package/dist/src/setup/scaffold/update/channels.js +1 -1
- package/dist/src/setup/setup-question-wire.d.ts +48 -0
- package/dist/src/setup/setup-question-wire.js +1 -0
- package/dist/src/setup/slackbot.d.ts +5 -1
- package/dist/src/setup/slackbot.js +1 -1
- package/dist/src/setup/state.d.ts +2 -2
- package/dist/src/shared/sandbox-backend.d.ts +6 -0
- package/dist/src/shared/sandbox-session.d.ts +15 -0
- package/dist/src/shared/tool-definition.d.ts +2 -2
- package/dist/src/shared/tool-schema.d.ts +5 -4
- package/dist/src/shared/tool-schema.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tracing/agent-otel-provider.d.ts +5 -38
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/dist/src/tracing/agent-span-id-generator.d.ts +15 -0
- package/dist/src/tracing/agent-span-id-generator.js +1 -0
- package/dist/src/tracing/agent-trace-context-store.d.ts +1 -1
- package/dist/src/tracing/agent-trace-context-store.js +1 -1
- package/dist/src/tracing/agent-trace-state.d.ts +41 -0
- package/dist/src/tracing/agent-trace-state.js +1 -0
- package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/channels/chat-sdk.mdx +1 -1
- package/docs/channels/github.mdx +1 -1
- package/docs/channels/linear.mdx +4 -2
- package/docs/channels/overview.mdx +2 -2
- package/docs/channels/slack.mdx +154 -40
- package/docs/concepts/default-harness.md +3 -3
- package/docs/concepts/sessions-runs-and-streaming.md +1 -1
- package/docs/connections/mcp.mdx +35 -1
- package/docs/connections/openapi.mdx +24 -0
- package/docs/guides/dev-tui.md +58 -142
- package/docs/guides/dynamic-workflows.md +2 -0
- package/docs/guides/frontend/overview.mdx +4 -2
- package/docs/guides/hooks.md +25 -3
- package/docs/guides/session-context.md +18 -0
- package/docs/install-integrations.mdx +2 -0
- package/docs/reference/cli.md +5 -3
- package/docs/reference/typescript-api.md +1 -1
- package/docs/sandbox.mdx +25 -5
- package/docs/skills.mdx +12 -1
- package/docs/subagents.mdx +1 -1
- package/docs/tools/human-in-the-loop.md +1 -1
- package/docs/tutorial/ship-it.mdx +1 -1
- package/package.json +10 -10
- package/dist/src/compiled/_chunks/node/undici-DWL_MYmT.js +0 -95
- package/dist/src/compiled/_chunks/workflow/dist-CZxNEvEs.js +0 -79
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ROOT_CONTEXT,SpanStatusCode,context,trace}from"#compiled/@opentelemetry/api/index.js";import{contentAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute}from"#tracing/agent-otel-content.js";const SESSION_WINDOW_TURN_LIMIT=200;
|
|
2
|
-
`));t!==void 0&&n.span.setAttribute(`ai.response.reasoning`,t);let r=textContentAttribute(e.source.content.filter(e=>e.type===`text`).map(e=>e.text).join(``));r!==void 0&&n.span.setAttribute(`ai.response.text`,r);let a=e.source.content.filter(e=>e.type===`tool-call`).map(e=>({input:e.input,toolName:e.toolName}));if(a.length>0){let e=contentAttribute(a,!1);e!==void 0&&n.span.setAttribute(`ai.response.tool_calls`,e)}let o=e.source.content.filter(e=>e.type===`tool-result`||e.type===`tool-error`).map(e=>e.type===`tool-result`?{input:e.input,output:e.output,toolName:e.toolName}:{error:errorText(e.error),input:e.input,toolName:e.toolName});if(o.length>0){let e=toolResultsContentAttribute(o);e!==void 0&&n.span.setAttribute(`ai.response.tool_results`,e)}}}n.span.end()}},beforeToolCall=n=>{let a=u.get(n.scope);if(a===void 0)return;let o=e.tracer.startSpan(`agent.action`,{attributes:{"agent.action.call_id":n.source.toolCall.toolCallId,"agent.action.kind":`tool`,"agent.action.name":n.source.toolCall.toolName,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":n.scope.rootSessionId??n.scope.sessionId,"agent.session.id":n.scope.sessionId,"agent.step.attempt":n.scope.attemptIndex,"agent.step.index":n.scope.stepIndex,"agent.turn.id":n.scope.turnId}},a.step.context),s=trace.setSpan(a.step.context,o),c=e.tracer.startSpan(`ai.toolCall`,{attributes:{"gen_ai.operation.name":`execute_tool`,"gen_ai.tool.call.id":n.source.toolCall.toolCallId,"gen_ai.tool.name":n.source.toolCall.toolName}},s);if(t){let e=contentAttribute(n.source.toolCall.input,!1);e!==void 0&&c.setAttribute(`gen_ai.tool.call.arguments`,e)}let l={context:trace.setSpan(s,c),span:o,toolSpan:c};return getExecutionContexts(n.scope).tools.set(n.id,l.context),l},afterToolCall=(e,n)=>{if(l.get(e.scope)?.tools.delete(e.id),isToolSpanState(n)){if(e.type===`tool.call.failed`)recordError(n.toolSpan,e.error),recordError(n.span,e.error);else if(e.source.toolOutput.type!==`tool-result`)recordError(n.toolSpan,e.source.toolOutput.error),recordError(n.span,e.source.toolOutput.error);else if(t){let t=contentAttribute(e.source.toolOutput.output,!1);t!==void 0&&n.toolSpan.setAttribute(`gen_ai.tool.call.result`,t)}n.toolSpan.end(),n.span.end()}},openSessionWindow=t=>{let n=e.tracer.startSpan(`agent.session`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":t.agentName,"agent.root.session.id":t.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":t.index,...t.previousTraceId===void 0?{}:{"agent.session.window.previous.trace.id":t.previousTraceId}},root:!0});return n.addEvent(t.index===0?`session.started`:`session.window.opened`),n.end(),n.spanContext()},ensureSessionContext=async t=>{let n=await e.stateStore.getSession(t.sessionId);return n===void 0&&(n={agentName:t.agentName,channelKind:t.channelKind,context:t.parentTraceContext===void 0?openSessionWindow({agentName:t.agentName,index:0,rootSessionId:t.rootSessionId,sessionId:t.sessionId}):adoptedSpanContext(t.parentTraceContext),rootSessionId:t.rootSessionId,turnsInWindow:0,window:0},await e.stateStore.setSession(t.sessionId,n)),n},advanceSessionWindow=(e,t)=>{if(t.turnsInWindow<200)return t;let n=t.window+1;return{...t,context:openSessionWindow({agentName:t.agentName,index:n,previousTraceId:t.context.traceId,rootSessionId:t.rootSessionId,sessionId:e}),turnsInWindow:0,window:n}};return{hook:{events:{"attempt.completed":onAttemptTerminal,"attempt.failed":onAttemptTerminal,"attempt.metadata":e=>{let t=u.get(e.scope);if(t===void 0)return;let n=readGatewayCost(e.providerMetadata);if(n!==void 0)for(let[e,r]of Object.entries(n))t.step.span.setAttribute(e,r)},"attempt.started":onAttemptStarted,"model.call":{after:afterModelCall,before:beforeModelCall},"session.completed":onSessionTransition,"session.failed":onSessionTransition,"session.started":onSessionStarted,"session.waiting":onSessionTransition,"tool.call":{after:afterToolCall,before:beforeToolCall},"turn.cancelled":onTurnTerminal,"turn.completed":onTurnTerminal,"turn.failed":onTurnTerminal,"turn.started":onTurnStarted}},runInContext(e,t){let r=l.get(e.scope),i=e.type===`model.call`?r?.models.get(e.id):r?.tools.get(e.id);return i===void 0?t():context.with(i,t)}};function getExecutionContexts(e){let t=l.get(e);return t===void 0&&(t={models:new Map,tools:new Map},l.set(e,t)),t}}function
|
|
1
|
+
import{ROOT_CONTEXT,SpanStatusCode,context,trace}from"#compiled/@opentelemetry/api/index.js";import{contentAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute}from"#tracing/agent-otel-content.js";const SESSION_WINDOW_TURN_LIMIT=200;function createAgentOtelInstrumentation(e){let t=e.captureContent??!0,l=new WeakMap,u=new WeakMap,onSessionStarted=async e=>{await ensureSessionContext(e)},onTurnStarted=async t=>{let n=advanceSessionWindow(t.sessionId,await ensureSessionContext({agentName:void 0,channelKind:void 0,parentTraceContext:t.parentTraceContext,rootSessionId:t.rootSessionId,sessionId:t.sessionId,type:`session.started`})),r={isRemote:!1,spanId:e.idGenerator.allocateSpanId(),traceFlags:n.context.traceFlags,traceId:n.context.traceId};await e.stateStore.setSession(t.sessionId,{...n,turnsInWindow:n.turnsInWindow+1}),await e.stateStore.setTurn(t.sessionId,t.turnId,{context:r,lineage:t.parentLineage,parentSpanId:n.context.spanId,rootSessionId:t.rootSessionId,sequence:t.sequence,startTimeMs:Date.now()})},onAttemptStarted=async t=>{let n=await e.stateStore.getTurn(t.scope.sessionId,t.scope.turnId);if(n===void 0)return;let i=contextFromSpanContext(n.context),a=e.tracer.startSpan(`agent.step`,{attributes:{"agent.session.id":t.scope.sessionId,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":t.scope.rootSessionId??t.scope.sessionId,"agent.step.attempt":t.scope.attemptIndex,"agent.step.index":t.scope.stepIndex,"agent.turn.id":t.scope.turnId,"agent.name":t.scope.functionId}},i);a.addEvent(`step.started`);let o=trace.setSpan(i,a),s=t.operation.operationId,c=e.tracer.startSpan(s,{attributes:{"gen_ai.operation.name":s,"gen_ai.provider.name":t.operation.provider,"gen_ai.request.model":t.operation.modelId}},o);u.set(t.scope,{operation:{context:trace.setSpan(o,c),name:s,span:c},step:{context:o,span:a}})},onAttemptTerminal=e=>{l.delete(e.scope);let t=u.get(e.scope);t!==void 0&&(t.step.span.addEvent(e.type===`attempt.completed`?`step.completed`:`step.failed`),e.type===`attempt.failed`&&(recordError(t.operation.span,e.error),recordError(t.step.span,e.error)),t.operation.span.end(),t.step.span.end(),u.delete(e.scope))},onTurnTerminal=async t=>{let n=await e.stateStore.getTurn(t.sessionId,t.turnId);n!==void 0&&await e.stateStore.setTurn(t.sessionId,t.turnId,{...n,terminal:{error:t.error,type:t.type}})},onSessionTransition=async t=>{if(t.turnId!==void 0){let n=await e.stateStore.getTurn(t.sessionId,t.turnId);if(n!==void 0){let r=await e.stateStore.getSession(t.sessionId),i=e.idGenerator.withSpanId(n.context.spanId,()=>e.tracer.startSpan(`agent.turn`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":r?.agentName,...parentLineageAttributes(n.lineage),"agent.root.session.id":n.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":r?.window,"agent.turn.id":t.turnId,"agent.turn.sequence":n.sequence},startTime:n.startTimeMs},contextFromSpanContext({isRemote:!1,spanId:n.parentSpanId,traceFlags:n.context.traceFlags,traceId:n.context.traceId})));i.addEvent(`turn.started`,void 0,n.startTimeMs),n.terminal!==void 0&&(i.addEvent(n.terminal.type),n.terminal.type===`turn.failed`&&recordError(i,n.terminal.error)),i.addEvent(t.type),i.end(),await e.stateStore.deleteTurn(t.sessionId,t.turnId)}}(t.type===`session.completed`||t.type===`session.failed`)&&await e.stateStore.deleteSession(t.sessionId)},beforeModelCall=n=>{let i=u.get(n.scope);if(i===void 0)return;i.step.span.setAttribute(`agent.model.id`,n.source.modelId),i.step.span.setAttribute(`agent.model.provider`,n.source.provider);let s=e.tracer.startSpan(modelSpanName(i.operation.name),{attributes:{"gen_ai.operation.name":i.operation.name,"gen_ai.provider.name":n.source.provider,"gen_ai.request.model":n.source.modelId}},i.operation.context);if(t){let e=messagesContentAttribute(n.source.messages);e!==void 0&&s.setAttribute(`ai.prompt.messages`,e);let t=systemPromptAttribute(n.source.instructions);t!==void 0&&s.setAttribute(`ai.prompt.system`,t)}let c={context:trace.setSpan(i.operation.context,s),span:s};return getExecutionContexts(n.scope).models.set(n.id,c.context),c},afterModelCall=(e,n)=>{if(l.get(e.scope)?.models.delete(e.id),isSpanState(n)){if(e.type===`model.call.failed`)recordError(n.span,e.error);else{setUsage(n.span,e.source.usage);let r=u.get(e.scope);if(r!==void 0&&setUsage(r.step.span,e.source.usage),t){n.span.setAttribute(`ai.response.finish_reason`,e.source.finishReason);let t=textContentAttribute(e.source.content.filter(e=>e.type===`reasoning`).map(e=>e.text).filter(e=>e.trim().length>0).join(`
|
|
2
|
+
`));t!==void 0&&n.span.setAttribute(`ai.response.reasoning`,t);let r=textContentAttribute(e.source.content.filter(e=>e.type===`text`).map(e=>e.text).join(``));r!==void 0&&n.span.setAttribute(`ai.response.text`,r);let a=e.source.content.filter(e=>e.type===`tool-call`).map(e=>({input:e.input,toolName:e.toolName}));if(a.length>0){let e=contentAttribute(a,!1);e!==void 0&&n.span.setAttribute(`ai.response.tool_calls`,e)}let o=e.source.content.filter(e=>e.type===`tool-result`||e.type===`tool-error`).map(e=>e.type===`tool-result`?{input:e.input,output:e.output,toolName:e.toolName}:{error:errorText(e.error),input:e.input,toolName:e.toolName});if(o.length>0){let e=toolResultsContentAttribute(o);e!==void 0&&n.span.setAttribute(`ai.response.tool_results`,e)}}}n.span.end()}},beforeToolCall=n=>{let a=u.get(n.scope);if(a===void 0)return;let o=e.tracer.startSpan(`agent.action`,{attributes:{"agent.action.call_id":n.source.toolCall.toolCallId,"agent.action.kind":`tool`,"agent.action.name":n.source.toolCall.toolName,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":n.scope.rootSessionId??n.scope.sessionId,"agent.session.id":n.scope.sessionId,"agent.step.attempt":n.scope.attemptIndex,"agent.step.index":n.scope.stepIndex,"agent.turn.id":n.scope.turnId}},a.step.context),s=trace.setSpan(a.step.context,o),c=e.tracer.startSpan(`ai.toolCall`,{attributes:{"gen_ai.operation.name":`execute_tool`,"gen_ai.tool.call.id":n.source.toolCall.toolCallId,"gen_ai.tool.name":n.source.toolCall.toolName}},s);if(t){let e=contentAttribute(n.source.toolCall.input,!1);e!==void 0&&c.setAttribute(`gen_ai.tool.call.arguments`,e)}let l={context:trace.setSpan(s,c),span:o,toolSpan:c};return getExecutionContexts(n.scope).tools.set(n.id,l.context),l},afterToolCall=(e,n)=>{if(l.get(e.scope)?.tools.delete(e.id),isToolSpanState(n)){if(e.type===`tool.call.failed`)recordError(n.toolSpan,e.error),recordError(n.span,e.error);else if(e.source.toolOutput.type!==`tool-result`)recordError(n.toolSpan,e.source.toolOutput.error),recordError(n.span,e.source.toolOutput.error);else if(t){let t=contentAttribute(e.source.toolOutput.output,!1);t!==void 0&&n.toolSpan.setAttribute(`gen_ai.tool.call.result`,t)}n.toolSpan.end(),n.span.end()}},openSessionWindow=t=>{let n=e.tracer.startSpan(`agent.session`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":t.agentName,"agent.root.session.id":t.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":t.index,...t.previousTraceId===void 0?{}:{"agent.session.window.previous.trace.id":t.previousTraceId}},root:!0});return n.addEvent(t.index===0?`session.started`:`session.window.opened`),n.end(),n.spanContext()},ensureSessionContext=async t=>{let n=await e.stateStore.getSession(t.sessionId);return n===void 0&&(n={agentName:t.agentName,channelKind:t.channelKind,context:t.parentTraceContext===void 0?openSessionWindow({agentName:t.agentName,index:0,rootSessionId:t.rootSessionId,sessionId:t.sessionId}):adoptedSpanContext(t.parentTraceContext),rootSessionId:t.rootSessionId,turnsInWindow:0,window:0},await e.stateStore.setSession(t.sessionId,n)),n},advanceSessionWindow=(e,t)=>{if(t.turnsInWindow<200)return t;let n=t.window+1;return{...t,context:openSessionWindow({agentName:t.agentName,index:n,previousTraceId:t.context.traceId,rootSessionId:t.rootSessionId,sessionId:e}),turnsInWindow:0,window:n}};return{hook:{events:{"attempt.completed":onAttemptTerminal,"attempt.failed":onAttemptTerminal,"attempt.metadata":e=>{let t=u.get(e.scope);if(t===void 0)return;let n=readGatewayCost(e.providerMetadata);if(n!==void 0)for(let[e,r]of Object.entries(n))t.step.span.setAttribute(e,r)},"attempt.started":onAttemptStarted,"model.call":{after:afterModelCall,before:beforeModelCall},"session.completed":onSessionTransition,"session.failed":onSessionTransition,"session.started":onSessionStarted,"session.waiting":onSessionTransition,"tool.call":{after:afterToolCall,before:beforeToolCall},"turn.cancelled":onTurnTerminal,"turn.completed":onTurnTerminal,"turn.failed":onTurnTerminal,"turn.started":onTurnStarted}},runInContext(e,t){let r=l.get(e.scope),i=e.type===`model.call`?r?.models.get(e.id):r?.tools.get(e.id);return i===void 0?t():context.with(i,t)}};function getExecutionContexts(e){let t=l.get(e);return t===void 0&&(t={models:new Map,tools:new Map},l.set(e,t)),t}}function parentLineageAttributes(e){if(e===void 0)return{};let t={"agent.parent.call_id":e.callId,"agent.parent.session.id":e.sessionId,"agent.parent.turn.id":e.turnId};return e.subagentName!==void 0&&(t[`agent.subagent.name`]=e.subagentName),t}function adoptedSpanContext(e){return{isRemote:!1,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function contextFromSpanContext(t){return trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext(t))}function readGatewayCost(e){let t=e.gateway;if(!isRecord(t))return;let n={},r=readUsd(t.cost);r!==void 0&&(n[`gen_ai.usage.cost`]=r);let i=readUsd(t.gatewayCost);i!==void 0&&(n[`gen_ai.usage.gateway_cost`]=i);let a=readUsd(t.inputInferenceCost);a!==void 0&&(n[`gen_ai.usage.input_cost`]=a);let o=readUsd(t.outputInferenceCost);return o!==void 0&&(n[`gen_ai.usage.output_cost`]=o),typeof t.generationId==`string`&&t.generationId.length>0&&(n[`gen_ai.generation.id`]=t.generationId),Object.keys(n).length===0?void 0:n}function readUsd(e){if(typeof e!=`string`)return;let t=Number(e);return Number.isFinite(t)?t:void 0}function isSpanState(e){return typeof e==`object`&&!!e&&`context`in e&&`span`in e}function isToolSpanState(e){return isSpanState(e)&&`toolSpan`in e}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function modelSpanName(e){return e===`ai.generateText`?`ai.generateText.doGenerate`:`ai.streamText.doStream`}function setUsage(e,t){t.inputTokens!==void 0&&e.setAttribute(`agent.usage.input_tokens`,t.inputTokens),t.outputTokens!==void 0&&e.setAttribute(`agent.usage.output_tokens`,t.outputTokens);let n=t.inputTokenDetails;n?.cacheReadTokens!==void 0&&e.setAttribute(`gen_ai.usage.cache_read.input_tokens`,n.cacheReadTokens),n?.cacheWriteTokens!==void 0&&e.setAttribute(`gen_ai.usage.cache_creation.input_tokens`,n.cacheWriteTokens)}function errorText(e){return e instanceof Error?e.message:e}function recordError(e,n){n instanceof Error?(e.recordException(n),e.setStatus({code:SpanStatusCode.ERROR,message:n.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{SESSION_WINDOW_TURN_LIMIT,createAgentOtelInstrumentation};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Id generator shared by `registerOTel` and the agent OTel provider. A span
|
|
3
|
+
* whose lifetime crosses durable worker boundaries (`agent.turn`) is emitted
|
|
4
|
+
* at its terminal; priming the next span id lets that span carry the
|
|
5
|
+
* pre-allocated id its descendants already parented to.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AgentSpanIdGenerator {
|
|
8
|
+
#private;
|
|
9
|
+
/** Reserves a span id for a span emitted later via {@link withSpanId}. */
|
|
10
|
+
allocateSpanId(): string;
|
|
11
|
+
generateSpanId(): string;
|
|
12
|
+
generateTraceId(): string;
|
|
13
|
+
/** Runs `startSpan` so the next span started carries `spanId`. */
|
|
14
|
+
withSpanId<T>(spanId: string, startSpan: () => T): T;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var AgentSpanIdGenerator=class{#e;allocateSpanId(){return randomHexId(16)}generateSpanId(){let e=this.#e;return e===void 0?randomHexId(16):(this.#e=void 0,e)}generateTraceId(){return randomHexId(32)}withSpanId(e,t){this.#e=e;try{return t()}finally{this.#e=void 0}}};function randomHexId(e){let t;do{t=``;for(let n=0;n<e;n+=1)t+=`0123456789abcdef`[Math.trunc(Math.random()*16)]}while(!/[1-9a-f]/u.test(t));return t}export{AgentSpanIdGenerator};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
-
import type { AgentSessionTraceState, AgentTraceStateStore, AgentTurnTraceState } from "#tracing/agent-
|
|
2
|
+
import type { AgentSessionTraceState, AgentTraceStateStore, AgentTurnTraceState } from "#tracing/agent-trace-state.js";
|
|
3
3
|
/** Keeps only framework trace state from an interrupted step's context changes. */
|
|
4
4
|
export declare function preserveSerializedAgentTraceState(original: Record<string, unknown>, interrupted: Record<string, unknown>): Record<string, unknown>;
|
|
5
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contextStorage,loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readSessionTraceContext(e,t){let n=e[AgentTraceContextKey.name];if(n!==void 0)return deserializeState(n).sessions[t]?.context}var ContextAgentTraceStateStore=class{deleteSession(e){updateState(t=>{let n={...t.sessions};return delete n[e],{...t,sessions:n}})}deleteTurn(e,t){updateState(n=>{let r={...n.turns};return delete r[turnKey(e,t)],{...n,turns:r}})}getSession(t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[t]}getTurn(t,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(t,n)]}setSession(e,t){updateState(n=>({...n,sessions:{...n.sessions,[e]:t}}))}setTurn(e,t,n){updateState(r=>({...r,turns:{...r.turns,[turnKey(e,t)]:n}}))}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??{sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{sessions:Object.fromEntries(Object.entries(e.sessions).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context)}])),turns:Object.fromEntries(Object.entries(e.turns).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context),terminal:t.terminal===void 0?void 0:{error:serializeError(t.terminal.error),type:t.terminal.type}}]))}}function deserializeState(e){return isRecord(e)?{sessions:deserializeRecord(e.sessions,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{agentName:typeof e.agentName==`string`?e.agentName:void 0,channelKind:typeof e.channelKind==`string`?e.channelKind:void 0,context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,turnsInWindow:typeof e.turnsInWindow==`number`?e.turnsInWindow:0,window:typeof e.window==`number`?e.window:0}}),turns:deserializeRecord(e.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,terminal:deserializeTerminal(e.terminal)}})}:{sessions:{},turns:{}}}function deserializeRecord(e,t){if(!isRecord(e))return{};let n={};for(let[r,i]of Object.entries(e)){let e=t(i);e!==void 0&&(n[r]=e)}return n}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return{error:deserializeError(e.error),type:t}}function serializeSpanContext(e){return{isRemote:e.isRemote,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function serializeError(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:void 0}function deserializeError(e){if(!isRecord(e)||typeof e.message!=`string`)return;let t=Error(e.message);return typeof e.name==`string`&&(t.name=e.name),typeof e.stack==`string`&&(t.stack=e.stack),t}function isTurnTerminalType(e){return e===`turn.cancelled`||e===`turn.completed`||e===`turn.failed`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readSessionTraceContext};
|
|
1
|
+
import{contextStorage,loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readSessionTraceContext(e,t){let n=e[AgentTraceContextKey.name];if(n!==void 0)return deserializeState(n).sessions[t]?.context}var ContextAgentTraceStateStore=class{deleteSession(e){updateState(t=>{let n={...t.sessions};return delete n[e],{...t,sessions:n}})}deleteTurn(e,t){updateState(n=>{let r={...n.turns};return delete r[turnKey(e,t)],{...n,turns:r}})}getSession(t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[t]}getTurn(t,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(t,n)]}setSession(e,t){updateState(n=>({...n,sessions:{...n.sessions,[e]:t}}))}setTurn(e,t,n){updateState(r=>({...r,turns:{...r.turns,[turnKey(e,t)]:n}}))}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??{sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{sessions:Object.fromEntries(Object.entries(e.sessions).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context)}])),turns:Object.fromEntries(Object.entries(e.turns).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context),terminal:t.terminal===void 0?void 0:{error:serializeError(t.terminal.error),type:t.terminal.type}}]))}}function deserializeState(e){return isRecord(e)?{sessions:deserializeRecord(e.sessions,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{agentName:typeof e.agentName==`string`?e.agentName:void 0,channelKind:typeof e.channelKind==`string`?e.channelKind:void 0,context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,turnsInWindow:typeof e.turnsInWindow==`number`?e.turnsInWindow:0,window:typeof e.window==`number`?e.window:0}}),turns:deserializeRecord(e.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context))&&!(typeof e.parentSpanId!=`string`||typeof e.startTimeMs!=`number`))return{context:e.context,lineage:deserializeLineage(e.lineage),parentSpanId:e.parentSpanId,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,startTimeMs:e.startTimeMs,terminal:deserializeTerminal(e.terminal)}})}:{sessions:{},turns:{}}}function deserializeRecord(e,t){if(!isRecord(e))return{};let n={};for(let[r,i]of Object.entries(e)){let e=t(i);e!==void 0&&(n[r]=e)}return n}function deserializeLineage(e){if(!(!isRecord(e)||typeof e.callId!=`string`||typeof e.sessionId!=`string`||typeof e.turnId!=`string`))return{callId:e.callId,sessionId:e.sessionId,subagentName:typeof e.subagentName==`string`?e.subagentName:void 0,turnId:e.turnId}}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return{error:deserializeError(e.error),type:t}}function serializeSpanContext(e){return{isRemote:e.isRemote,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function serializeError(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:void 0}function deserializeError(e){if(!isRecord(e)||typeof e.message!=`string`)return;let t=Error(e.message);return typeof e.name==`string`&&(t.name=e.name),typeof e.stack==`string`&&(t.stack=e.stack),t}function isTurnTerminalType(e){return e===`turn.cancelled`||e===`turn.completed`||e===`turn.failed`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readSessionTraceContext};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { InstrumentationParentLineage, InstrumentationTurnTerminalEvent } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
export interface AgentSessionTraceState {
|
|
4
|
+
readonly agentName?: string;
|
|
5
|
+
readonly channelKind?: string;
|
|
6
|
+
readonly context: SpanContext;
|
|
7
|
+
readonly rootSessionId: string;
|
|
8
|
+
readonly turnsInWindow: number;
|
|
9
|
+
readonly window: number;
|
|
10
|
+
}
|
|
11
|
+
export interface AgentTurnTraceState {
|
|
12
|
+
readonly context: SpanContext;
|
|
13
|
+
readonly lineage?: InstrumentationParentLineage;
|
|
14
|
+
readonly parentSpanId: string;
|
|
15
|
+
readonly rootSessionId: string;
|
|
16
|
+
readonly sequence: number;
|
|
17
|
+
readonly startTimeMs: number;
|
|
18
|
+
readonly terminal?: {
|
|
19
|
+
readonly error?: unknown;
|
|
20
|
+
readonly type: InstrumentationTurnTerminalEvent["type"];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Provider-owned serializable storage for durable agent trace state. */
|
|
24
|
+
export interface AgentTraceStateStore {
|
|
25
|
+
deleteSession(sessionId: string): void | PromiseLike<void>;
|
|
26
|
+
deleteTurn(sessionId: string, turnId: string): void | PromiseLike<void>;
|
|
27
|
+
getSession(sessionId: string): AgentSessionTraceState | undefined | PromiseLike<AgentSessionTraceState | undefined>;
|
|
28
|
+
getTurn(sessionId: string, turnId: string): AgentTurnTraceState | undefined | PromiseLike<AgentTurnTraceState | undefined>;
|
|
29
|
+
setSession(sessionId: string, state: AgentSessionTraceState): void | PromiseLike<void>;
|
|
30
|
+
setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void | PromiseLike<void>;
|
|
31
|
+
}
|
|
32
|
+
/** In-memory trace state used by tests and non-durable runtimes. */
|
|
33
|
+
export declare class InMemoryAgentTraceStateStore implements AgentTraceStateStore {
|
|
34
|
+
#private;
|
|
35
|
+
deleteSession(sessionId: string): void;
|
|
36
|
+
deleteTurn(sessionId: string, turnId: string): void;
|
|
37
|
+
getSession(sessionId: string): AgentSessionTraceState | undefined;
|
|
38
|
+
getTurn(sessionId: string, turnId: string): AgentTurnTraceState | undefined;
|
|
39
|
+
setSession(sessionId: string, state: AgentSessionTraceState): void;
|
|
40
|
+
setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var InMemoryAgentTraceStateStore=class{#e=new Map;#t=new Map;deleteSession(e){this.#e.delete(e)}deleteTurn(e,t){this.#t.delete(turnKey(e,t))}getSession(e){return this.#e.get(e)}getTurn(e,t){return this.#t.get(turnKey(e,t))}setSession(e,t){this.#e.set(e,t)}setTurn(e,t,n){this.#t.set(turnKey(e,t),n)}};function turnKey(e,t){return`${e}:${t}`}export{InMemoryAgentTraceStateStore};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ContextAgentTraceStateStore}from"#tracing/agent-trace-context-store.js";import{getInstrumentationRuntime,registerInstrumentationRuntime}from"#harness/instrumentation-runtime.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{registerOTel}from"#compiled/@vercel/otel/index.js";import{createAgentOtelInstrumentation}from"#tracing/agent-otel-provider.js";import{AgentTraceSpanProcessor}from"#tracing/agent-trace-span-processor.js";import{createInstrumentationHooks}from"#harness/instrumentation-lifecycle.js";import{requestLocalTraceStorePrune,resolveLocalTraceRetentionSettings}from"#tracing/local-trace-retention.js";import{LocalTraceSpanProcessor}from"#tracing/local-trace-span-processor.js";function installLocalInstrumentationRuntime(t){let n=getInstrumentationRuntime();if(n!==void 0)return n;let r=resolveLocalTraceRetentionSettings(),i=new AgentTraceSpanProcessor(r.enabled?[new LocalTraceSpanProcessor(t.appRoot)]:[]);registerOTel({autoDetectResources:!1,instrumentations:[],propagators:[`none`],serviceName:t.serviceName,spanProcessors:[i]});let
|
|
1
|
+
import{ContextAgentTraceStateStore}from"#tracing/agent-trace-context-store.js";import{getInstrumentationRuntime,registerInstrumentationRuntime}from"#harness/instrumentation-runtime.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{registerOTel}from"#compiled/@vercel/otel/index.js";import{createAgentOtelInstrumentation}from"#tracing/agent-otel-provider.js";import{AgentSpanIdGenerator}from"#tracing/agent-span-id-generator.js";import{AgentTraceSpanProcessor}from"#tracing/agent-trace-span-processor.js";import{createInstrumentationHooks}from"#harness/instrumentation-lifecycle.js";import{requestLocalTraceStorePrune,resolveLocalTraceRetentionSettings}from"#tracing/local-trace-retention.js";import{LocalTraceSpanProcessor}from"#tracing/local-trace-span-processor.js";function installLocalInstrumentationRuntime(t){let n=getInstrumentationRuntime();if(n!==void 0)return n;let r=resolveLocalTraceRetentionSettings(),i=new AgentTraceSpanProcessor(r.enabled?[new LocalTraceSpanProcessor(t.appRoot)]:[]),a=new AgentSpanIdGenerator;registerOTel({autoDetectResources:!1,idGenerator:a,instrumentations:[],propagators:[`none`],serviceName:t.serviceName,spanProcessors:[i]});let o=trace.getTracer(`eve.registration`).startSpan(`eve.otel.registration`),s=trace.setSpan(context.active(),o),c=context.with(s,()=>trace.getActiveSpan()===o);if(o.end(),!i.isAttached()||!c)throw Error(`eve could not register OpenTelemetry because another runtime already exists.`);let l=createAgentOtelInstrumentation({captureContent:process.env.EVE_TRACES_CONTENT!==`off`,frameworkVersion:t.frameworkVersion,idGenerator:a,stateStore:new ContextAgentTraceStateStore,tracer:trace.getTracer(`eve.agent`,t.frameworkVersion)}),u={events:{"session.completed":releaseSessionTrace,"session.failed":releaseSessionTrace}};return requestPrune(),registerInstrumentationRuntime({forceFlush:()=>i.forceFlush(),hooks:createInstrumentationHooks([l.hook,u]),runInContext:l.runInContext});async function releaseSessionTrace(e){await i.forceFlush(),i.releaseSession(e.sessionId)&&requestPrune()}function requestPrune(){r.enabled&&requestLocalTraceStorePrune({activeTraceIds:i.activeTraceIds(),appRoot:t.appRoot,maxAgeMs:r.maxAgeMs,maxTotalBytes:r.maxTotalBytes,retainCount:r.retainCount})}}export{installLocalInstrumentationRuntime};
|
package/dist/src/vue/index.js
CHANGED
|
@@ -5,7 +5,7 @@ description: "Bridge any Vercel Chat SDK adapter — Slack, Discord, Telegram, W
|
|
|
5
5
|
|
|
6
6
|
The Chat SDK channel connects your agent to any [Vercel Chat SDK](https://chat-sdk.dev) adapter. You pick an adapter (`@chat-adapter/slack`, `@resend/chat-sdk-adapter`, and so on), register handlers for the messages you care about, and call `send` to hand each turn to eve. Use it to reach a surface eve does not ship a first-class channel for, or when you want to manage credentials and state with the Chat SDK's own primitives rather than [Vercel Connect](../guides/auth-and-route-protection). See [Channels](./overview) for the contract this builds on.
|
|
7
7
|
|
|
8
|
-
You supply an adapter and a state store: the adapter owns provider auth, webhook verification, and delivery, while eve owns session dispatch, streaming, typing, and human-in-the-loop. First-class channels such as [Slack](./slack) manage credentials through Vercel Connect
|
|
8
|
+
You supply an adapter and a state store: the adapter owns provider auth, webhook verification, and delivery, while eve owns session dispatch, streaming, typing, and human-in-the-loop. First-class channels such as [Slack](./slack) use their own channel APIs and can manage credentials through Vercel Connect or environment variables you configure directly.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
package/docs/channels/github.mdx
CHANGED
|
@@ -14,7 +14,7 @@ Run the registry setup from the agent directory:
|
|
|
14
14
|
eve add channel/github
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
The flow checks that the Vercel CLI is authenticated, creates or links a Vercel project when needed, provisions an app-scoped GitHub Connect client, and
|
|
17
|
+
The flow checks that the Vercel CLI is authenticated, creates or links a Vercel project when needed, provisions an app-scoped GitHub Connect client, and registers `/eve/v1/github` as a trigger destination. It then installs `@vercel/connect` and writes `agent/channels/github.ts` with the connector UID.
|
|
18
18
|
|
|
19
19
|
Vercel Connect creates the GitHub App, receives and verifies its webhooks, and forwards them to the deployed agent. After deploying, open the GitHub App in the Connect dashboard and install it in the organization or account where you want to use it. Add the generated invocation token (for example, `@my-agent`) to a new issue, pull request, or review comment to start a conversation. GitHub may not autocomplete the token or render it as a linked mention.
|
|
20
20
|
|
package/docs/channels/linear.mdx
CHANGED
|
@@ -16,7 +16,7 @@ eve add linear
|
|
|
16
16
|
|
|
17
17
|
Select **Linear Channel** in the component checklist. **Linear MCP** is also selected by default if you want the agent to search and update Linear through MCP. Run `eve add channel/linear-agent` instead to install only the channel directly.
|
|
18
18
|
|
|
19
|
-
The flow checks that the Vercel CLI is authenticated, creates or links a Vercel project when needed, reuses a compatible existing connector when available or provisions an app-scoped Linear Connect client, and
|
|
19
|
+
The flow checks that the Vercel CLI is authenticated, creates or links a Vercel project when needed, reuses a compatible existing connector when available or provisions an app-scoped Linear Connect client, and registers `/eve/v1/linear` as a trigger destination. It then installs `@vercel/connect` and writes `agent/channels/linear.ts` with the connector UID.
|
|
20
20
|
|
|
21
21
|
Vercel Connect creates the Linear app with the `app:assignable` and `app:mentionable` scopes required for Agent Sessions, receives and verifies `AgentSessionEvent` webhooks, and forwards them to the deployed agent. After deploying, open the Linear app in the Connect dashboard and install it in the workspace where you want to delegate work. Then delegate an issue or mention the agent in a Linear Agent Session.
|
|
22
22
|
|
|
@@ -48,6 +48,8 @@ export default linearChannel({
|
|
|
48
48
|
});
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
Direct webhook verification accepts timestamps within 60 seconds by default, as Linear recommends. Set `maxSkewMs` to a larger number of milliseconds only when you intentionally accept delayed retries. Use the narrowest window that fits your retry policy.
|
|
52
|
+
|
|
51
53
|
```bash
|
|
52
54
|
LINEAR_AGENT_ACCESS_TOKEN=lin_api_... # posts Agent Activities and creates proactive sessions
|
|
53
55
|
LINEAR_WEBHOOK_SECRET=... # verifies Linear-Signature
|
|
@@ -63,7 +65,7 @@ https://<deployment>/eve/v1/linear
|
|
|
63
65
|
|
|
64
66
|
For Linear's agent surface, configure the OAuth authorize URL with `actor=app` and grant the app scopes that let it appear as an agent in Linear, including `app:assignable` and `app:mentionable`. Subscribe to the `AgentSessionEvent` webhook category so Linear sends `created` events when the agent is delegated or mentioned and `prompted` events when the user continues the session.
|
|
65
67
|
|
|
66
|
-
Linear sends webhook signatures in `Linear-Signature`; eve verifies the HMAC over the raw body and rejects stale `webhookTimestamp` values. If a trusted gateway verifies Linear before the request reaches eve, pass `credentials.webhookVerifier` instead of a webhook secret.
|
|
68
|
+
Linear sends webhook signatures in `Linear-Signature`; eve verifies the HMAC over the raw body and rejects stale `webhookTimestamp` values. If a trusted gateway verifies Linear before the request reaches eve, pass `credentials.webhookVerifier` instead of a webhook secret. Your custom verifier must enforce its timestamp policy because `maxSkewMs` does not apply.
|
|
67
69
|
|
|
68
70
|
## How the channel handles messages
|
|
69
71
|
|
|
@@ -40,9 +40,9 @@ When eve doesn't ship a channel for your surface, build one with `defineChannel`
|
|
|
40
40
|
|
|
41
41
|
## Relationship to the Chat SDK
|
|
42
42
|
|
|
43
|
-
eve uses the Chat SDK's
|
|
43
|
+
eve uses the [Chat SDK's card-builder components](https://chat-sdk.dev/docs/api/cards) (Cards, Buttons, Actions, etc.) for composing rich Slack messages. When you build a card with the [Slack channel](./slack), the underlying primitives come from the Chat SDK and get converted to Slack Block Kit at post time.
|
|
44
44
|
|
|
45
|
-
eve
|
|
45
|
+
eve's first-class channels use eve-owned runtimes for webhook handling, verification, event parsing, and thread management. The optional [Chat SDK channel](./chat-sdk) is the exception: it accepts a Chat SDK adapter and exposes its `Chat` and `Thread` primitives. Use `slackChannel` for eve's first-class Slack integration, `defineChannel(...)` for an eve-native custom channel, or `chatSdkChannel` when you intentionally want a Chat SDK adapter and runtime.
|
|
46
46
|
|
|
47
47
|
## Which channel?
|
|
48
48
|
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Slack"
|
|
3
|
-
description: "Reach your agent from Slack app mentions and DMs
|
|
3
|
+
description: "Reach your agent from Slack app mentions and DMs with Vercel Connect-managed credentials, threaded replies, and interactive buttons."
|
|
4
4
|
type: integration
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Slack channel puts your agent inside a workspace. It answers `@mentions` and DMs, replies in threads, shows typing indicators, and turns human-in-the-loop (HITL) prompts into buttons.
|
|
7
|
+
The Slack channel puts your agent inside a workspace. It answers `@mentions` and DMs, replies in threads, shows typing indicators, and turns human-in-the-loop (HITL) prompts into buttons. [Vercel Connect](https://vercel.com/kb/guide/vercel-connect) is the recommended setup: it manages the Slack bot token, verifies inbound requests, supports token rotation and multiple workspace installations, and forwards events to your agent without copying Slack secrets into your project environment. If you cannot use Vercel Connect, you can provide the bot token and signing secret through environment variables. See [Channels](./overview) for the contract this builds on.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Add the channel
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Run the guided setup from your agent project:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
15
|
-
vercel connect create slack --triggers
|
|
16
|
-
vercel connect detach <uid> --yes
|
|
17
|
-
vercel connect attach <uid> --triggers --trigger-path /eve/v1/slack --yes
|
|
14
|
+
eve add channel/slack
|
|
18
15
|
```
|
|
19
16
|
|
|
20
|
-
The
|
|
17
|
+
The command recommends Vercel Connect, then scaffolds `agent/channels/slack.ts` and installs any required dependency. Choose the environment-variable option only when you need to manage the Slack bot token and signing secret yourself.
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
### Recommended: use Vercel Connect
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
Vercel Connect setup requires an authenticated Vercel CLI and a linked Vercel project. The guided flow creates or reuses a Slack connector, waits for you to install it in a workspace, and registers `/eve/v1/slack` as a trigger destination before writing the channel file.
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
npm install @vercel/connect
|
|
28
|
-
```
|
|
23
|
+
The generated channel resolves credentials at runtime:
|
|
29
24
|
|
|
30
25
|
```ts title="agent/channels/slack.ts"
|
|
31
26
|
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
@@ -38,17 +33,93 @@ export default slackChannel({
|
|
|
38
33
|
|
|
39
34
|
`connectSlackCredentials` returns `{ botToken, webhookVerifier }`, keeping token rotation, multi-workspace tenancy, and request verification inside Connect rather than your code.
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
### Manage Slack credentials yourself
|
|
37
|
+
|
|
38
|
+
Use this fallback when the agent does not run on Vercel or Vercel Connect is unavailable. In the setup command, **Use portable credentials** is the environment-variable option; eve adds `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` to `.env.example`.
|
|
39
|
+
|
|
40
|
+
This option does not create or install the Slack app. Configure it in [Slack app settings](https://api.slack.com/apps):
|
|
41
|
+
|
|
42
|
+
1. Add the `app_mentions:read` and `chat:write` bot scopes. Add `im:history` for DMs, `files:read` for inbound attachments, and `files:write` when the agent uploads files.
|
|
43
|
+
2. Install or reinstall the app, copy its Bot User OAuth Token to `SLACK_BOT_TOKEN`, and copy the signing secret from **Basic Information** to `SLACK_SIGNING_SECRET`.
|
|
44
|
+
3. Set both values in `.env.local` for local development or in the target runtime's environment, then start or deploy the agent at a public URL.
|
|
45
|
+
4. Under **Event Subscriptions**, set the Request URL to `https://your-agent.example/eve/v1/slack`. Subscribe to `app_mention`, plus `message.im` for DMs.
|
|
46
|
+
5. Under **Interactivity & Shortcuts**, use the same Request URL when the agent uses buttons or HITL prompts.
|
|
47
|
+
|
|
48
|
+
Add the matching message events and history scopes if you also want unmentioned thread replies, as described in [Continue conversations without repeated mentions](#continue-conversations-without-repeated-mentions).
|
|
49
|
+
|
|
50
|
+
## Deploy
|
|
51
|
+
|
|
52
|
+
Deploy the production agent after the trigger destination and channel file are ready:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
eve deploy
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`eve deploy` deploys the linked Vercel project to production. The guided flow registers the trigger destination against the production branch, which is the Vercel CLI default. Manage trigger destinations separately in the Connect dashboard when you need a different branch or environment.
|
|
59
|
+
|
|
60
|
+
## Test a preview branch with Vercel Connect
|
|
61
|
+
|
|
62
|
+
Use a separate connector when you want preview Slack traffic isolated from production, then register the preview branch as a trigger destination:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install -g vercel@latest
|
|
66
|
+
vercel connect create slack --name your-agent-preview --triggers
|
|
67
|
+
vercel connect attach slack/your-agent-preview \
|
|
68
|
+
--environment preview \
|
|
69
|
+
--triggers \
|
|
70
|
+
--trigger-branch your-preview-branch \
|
|
71
|
+
--trigger-path /eve/v1/slack \
|
|
72
|
+
--yes
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Run these commands from the linked project directory. `--environment preview` makes the connector credentials available to Preview deployments. `--trigger-branch` selects the branch that receives forwarded Slack webhooks.
|
|
76
|
+
|
|
77
|
+
Select the matching connector at runtime instead of leaving the generated production UID hard-coded:
|
|
78
|
+
|
|
79
|
+
```ts title="agent/channels/slack.ts"
|
|
80
|
+
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
81
|
+
import { slackChannel } from "eve/channels/slack";
|
|
82
|
+
|
|
83
|
+
const connectorUid =
|
|
84
|
+
process.env.VERCEL_ENV === "preview" ? "slack/your-agent-preview" : "slack/my-agent";
|
|
85
|
+
|
|
86
|
+
export default slackChannel({
|
|
87
|
+
credentials: connectSlackCredentials(connectorUid),
|
|
88
|
+
});
|
|
89
|
+
```
|
|
42
90
|
|
|
43
|
-
|
|
91
|
+
Creating a connector with triggers also registers a default production destination. Remove that destination from the Connect dashboard before treating the preview connector as isolated. The guided production setup does not restrict credential environments, so also set the production connector's project access to **Production** and the preview connector's access to **Preview** in the dashboard. `vercel connect detach` removes project token access, not trigger destinations. Vercel Connect supports up to three trigger destinations per connector; see the [Vercel Connect guide](https://vercel.com/kb/guide/vercel-connect) for current limits and environment guidance.
|
|
44
92
|
|
|
45
|
-
|
|
93
|
+
Slack must be able to reach the preview route without an interactive authentication challenge. For a Connect trigger, exempt the branch domain with a [Deployment Protection Exception](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/deployment-protection-exceptions) when that feature is available, or use an unprotected preview domain. For a Slack app that uses environment-variable credentials, append `?x-vercel-protection-bypass=your-generated-secret` to its event and interaction request URLs. Enabling [Protection Bypass for Automation](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation) does not bypass a request unless the sender presents that secret.
|
|
94
|
+
|
|
95
|
+
## Set up Vercel Connect manually
|
|
96
|
+
|
|
97
|
+
To set up the connector outside the guided flow, create it, then register eve's Slack route with the linked project:
|
|
46
98
|
|
|
47
99
|
```bash
|
|
48
|
-
|
|
100
|
+
npm install -g vercel@latest
|
|
101
|
+
vercel connect create slack --name your-agent --triggers
|
|
102
|
+
vercel connect attach slack/your-agent \
|
|
103
|
+
--environment production \
|
|
104
|
+
--triggers \
|
|
105
|
+
--trigger-path /eve/v1/slack \
|
|
106
|
+
--yes
|
|
49
107
|
```
|
|
50
108
|
|
|
51
|
-
`
|
|
109
|
+
`--triggers` enables Slack Event Subscriptions. The attach command registers eve's route in addition to the default destination created with the connector. Remove the default destination separately in the [Connect dashboard](https://vercel.com/d?to=/%5Bteam%5D/~/connect&title=Go+to+Connect). For an existing connector, skip `create` and attach the intended route directly.
|
|
110
|
+
|
|
111
|
+
## Troubleshoot Slack delivery
|
|
112
|
+
|
|
113
|
+
Check the delivery path in order so you can identify where a Slack event stops:
|
|
114
|
+
|
|
115
|
+
| Symptom | Check | Next action |
|
|
116
|
+
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| `eve add channel/slack` stops before scaffolding the channel | Read the setup error for the failed Vercel login, project link, workspace install, connector lookup, or trigger registration step. | Resolve that step, then rerun `eve add channel/slack`. |
|
|
118
|
+
| Setup says it could not remove an existing trigger destination | Check the connector's destinations in the Connect dashboard. `vercel connect detach` removes project token access, not trigger destinations. | Edit or remove the stale destination in the dashboard, then register `/eve/v1/slack` with `vercel connect attach`. |
|
|
119
|
+
| Mentions never reach the deployment | Confirm that the connector trigger destination uses the intended project and branch, enables triggers, and points to `/eve/v1/slack`. | Edit or remove stale destinations in the Connect dashboard, then register the intended destination with `vercel connect attach`. |
|
|
120
|
+
| Mentions work but DMs or thread replies do not | Check the Slack app's Event Subscriptions and OAuth scopes. DMs require `message.im` and `im:history`; channel replies require the matching message event and history scope. | Add the missing events or scopes, then reinstall the Slack app if Slack requires it. |
|
|
121
|
+
| A preview request receives an authentication page or protection error | Check whether the branch domain is exempt or the direct Slack request URL includes `x-vercel-protection-bypass`. Slack cannot complete an interactive Vercel Authentication challenge. | Add a Deployment Protection Exception for the branch domain, or add the automation bypass query parameter to a Slack app that uses environment-variable credentials. |
|
|
122
|
+
| The webhook reaches the deployment but the agent does not reply | Open the project's Vercel runtime logs, then inspect the session in **Agent Runs** when your team has access. | Use the first missing or failed stage to narrow the issue to channel dispatch, the agent run, or outbound Slack delivery. |
|
|
52
123
|
|
|
53
124
|
## How the channel handles inbound events
|
|
54
125
|
|
|
@@ -71,6 +142,43 @@ Only the first available handler runs. A message hook returning `null` drops the
|
|
|
71
142
|
|
|
72
143
|
`onInteraction(action, ctx)` separately handles `block_actions` callbacks not consumed by HITL. eve attaches the triggering Slack user id to the same model message as its text, preserving speaker attribution without profile lookups.
|
|
73
144
|
|
|
145
|
+
#### Restrict who can invoke the agent
|
|
146
|
+
|
|
147
|
+
A valid Slack signature proves that Slack sent the event. It does not decide which channel or person your agent should trust. Fail closed in the inbound handler before returning auth, especially when the app is installed in a shared Slack Connect channel:
|
|
148
|
+
|
|
149
|
+
```ts title="agent/channels/slack.ts"
|
|
150
|
+
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
151
|
+
import { defaultSlackAuth, slackChannel, type SlackMessage } from "eve/channels/slack";
|
|
152
|
+
|
|
153
|
+
const allowedChannels = new Set(["C01234567"]);
|
|
154
|
+
const allowedUsers = new Set(["U01234567"]);
|
|
155
|
+
|
|
156
|
+
function isAllowedUser(message: SlackMessage) {
|
|
157
|
+
const userId = message.author?.userId;
|
|
158
|
+
return Boolean(userId && allowedUsers.has(userId));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default slackChannel({
|
|
162
|
+
credentials: connectSlackCredentials("slack/my-agent"),
|
|
163
|
+
onAppMention(ctx, message) {
|
|
164
|
+
if (!isAllowedUser(message) || !allowedChannels.has(message.channelId)) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return { auth: defaultSlackAuth(message, ctx) };
|
|
168
|
+
},
|
|
169
|
+
onDirectMessage(ctx, message) {
|
|
170
|
+
if (!isAllowedUser(message)) return null;
|
|
171
|
+
return { auth: defaultSlackAuth(message, ctx) };
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Override every enabled inbound handler; leaving `onDirectMessage` out would retain eve's default DM behavior. Slack Connect can deliver events from external users, so authorize the explicit user and current channel IDs your policy expects. If access depends on organization membership, resolve it from Slack's shared-channel and user data instead of inferring it from the request signature.
|
|
177
|
+
|
|
178
|
+
Keep authorization checks inside sensitive tools too. The inbound allowlist controls who can start a turn; tool-level checks control what that authenticated Slack principal may read or change.
|
|
179
|
+
|
|
180
|
+
The allowlist above gates message-started turns. Built-in HITL buttons are handled before `onInteraction`, and the person who clicks supplies the Slack auth for the resumed session. Anyone who can interact with the Slack message can answer it. Put sensitive prompts in channels limited to the intended approvers, and re-check `ctx.session.auth.current` inside a sensitive tool before performing its side effect.
|
|
181
|
+
|
|
74
182
|
#### Continue conversations without repeated mentions
|
|
75
183
|
|
|
76
184
|
Use `onMessage` to handle explicit mentions and continue replies in threads with an active eve session:
|
|
@@ -106,6 +214,28 @@ async onMessage(ctx, message) {
|
|
|
106
214
|
|
|
107
215
|
Like `threadContext`, this helper calls `conversations.replies` and requires the matching Slack history scope. It observes at most the first 50 messages of the thread, and a failed fetch is logged and swallowed, so the returned list may be empty or stale — treat an unexpected empty list as "don't route" rather than "no participants".
|
|
108
216
|
|
|
217
|
+
#### Load prior thread messages
|
|
218
|
+
|
|
219
|
+
You get the triggering mention by default, but not the earlier replies in the thread. Enable `threadContext` to fetch and inject them with every message attributed by stable Slack user id. Use `since: "last-agent-reply"` so repeated mentions inject only what is new:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { slackChannel } from "eve/channels/slack";
|
|
223
|
+
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
224
|
+
|
|
225
|
+
export default slackChannel({
|
|
226
|
+
credentials: connectSlackCredentials("slack/my-agent"),
|
|
227
|
+
threadContext: { since: "last-agent-reply" },
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`since` sets the boundary for what each mention injects and accepts three values:
|
|
232
|
+
|
|
233
|
+
- `"thread-root"` (the default): every prior message in the thread, on every mention. `threadContext: {}` behaves the same.
|
|
234
|
+
- `"last-agent-reply"`: only messages after this installed agent's last reply, keeping repeated mentions incremental. Replies from other Slack bots do not move the boundary.
|
|
235
|
+
- A predicate `(message: SlackThreadMessage) => boolean`: only messages after the last one it matches, such as "since the last message that mentioned a particular user".
|
|
236
|
+
|
|
237
|
+
`threadContext` requires the matching Slack history scope. Thread helpers reuse messages already loaded within the same inbound handler, and overlapping refreshes share one `conversations.replies` request. 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.
|
|
238
|
+
|
|
109
239
|
#### Cancel an active turn
|
|
110
240
|
|
|
111
241
|
Message hooks (`onMessage`, `onAppMention`, and `onDirectMessage`) receive thread-bound session operations directly on `ctx`. Call `ctx.cancel({ turnId? })` before returning `{ auth }` to stop the current turn and queue the new Slack message as replacement input, producing a debounce-like experience:
|
|
@@ -211,26 +341,6 @@ const session = await ctx.resolveSession({ target });
|
|
|
211
341
|
|
|
212
342
|
`onEvent` covers JSON `event_callback` deliveries only. URL verification, slash commands, and interactive payloads do not reach it. Add every desired event and required OAuth scope to the Slack app's Event Subscriptions configuration; eve can only handle events Slack sends.
|
|
213
343
|
|
|
214
|
-
You get the triggering mention by default, but not the earlier replies in the thread. Enable `threadContext` to fetch and inject them with every message attributed by stable Slack user id. Use `since: "last-agent-reply"` so repeated mentions inject only what is new:
|
|
215
|
-
|
|
216
|
-
```ts
|
|
217
|
-
import { slackChannel } from "eve/channels/slack";
|
|
218
|
-
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
219
|
-
|
|
220
|
-
export default slackChannel({
|
|
221
|
-
credentials: connectSlackCredentials("slack/my-agent"),
|
|
222
|
-
threadContext: { since: "last-agent-reply" },
|
|
223
|
-
});
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
`since` sets the boundary for what each mention injects and accepts three values:
|
|
227
|
-
|
|
228
|
-
- `"thread-root"` (the default): every prior message in the thread, on every mention. `threadContext: {}` behaves the same.
|
|
229
|
-
- `"last-agent-reply"`: only messages after this installed agent's last reply, keeping repeated mentions incremental. Replies from other Slack bots do not move the boundary.
|
|
230
|
-
- A predicate `(message: SlackThreadMessage) => boolean`: only messages after the last one it matches, such as "since the last message that mentioned a particular user".
|
|
231
|
-
|
|
232
|
-
`threadContext` requires the matching Slack history scope. Thread helpers reuse messages already loaded within the same inbound handler, and overlapping refreshes share one `conversations.replies` request. 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.
|
|
233
|
-
|
|
234
344
|
### Slack API calls outside a handler
|
|
235
345
|
|
|
236
346
|
Inside webhook-side handlers (`onAppMention`, `onEvent`, `onInteraction`, `events`),
|
|
@@ -240,7 +350,7 @@ messages, for example, has no inbound Slack request. For that, call the
|
|
|
240
350
|
same primitive the handle uses directly:
|
|
241
351
|
|
|
242
352
|
```ts
|
|
243
|
-
import { callSlackApi
|
|
353
|
+
import { callSlackApi } from "eve/channels/slack";
|
|
244
354
|
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
245
355
|
|
|
246
356
|
const { botToken } = connectSlackCredentials("slack/my-agent");
|
|
@@ -309,6 +419,10 @@ Start a session without an inbound message through `ctx.to(slack, target).send(m
|
|
|
309
419
|
|
|
310
420
|
Inbound files behind authenticated Slack URLs are staged with `fetchFile`. See [File uploads](./custom#file-uploads) for the `fetchFile` contract.
|
|
311
421
|
|
|
422
|
+
Private file downloads require the Slack bot token's `files:read` scope. Add the scope and reinstall the Slack app before sending attachments.
|
|
423
|
+
|
|
424
|
+
Staged files live in the session sandbox under `/workspace/attachments`. Session history keeps a reference to that path, not a second copy of the Slack file. If the backend can no longer reattach the original sandbox, eve cannot restore the attachment bytes from history; later turns receive a missing-file result. Store files outside the sandbox when the agent must retain them independently of sandbox availability.
|
|
425
|
+
|
|
312
426
|
## What to read next
|
|
313
427
|
|
|
314
428
|
- [Channels overview](./overview): the channel contract and every built-in channel
|
|
@@ -60,7 +60,7 @@ Notes:
|
|
|
60
60
|
- **`agent`** is available only in the root session. Its child uses the root's instructions, tools, connections, and sandbox, but starts with fresh conversation history and fresh [state](../guides/state). The child receives neither `agent` nor `Workflow`; declared subagents do not receive the built-in `agent` either. See [Subagents](../subagents).
|
|
61
61
|
- **`load_skill`** only pulls instructions into context. It adds no new execution surface, because behavior still comes from the tools the agent already has.
|
|
62
62
|
- **`connection_search`** surfaces a connection's tools by their qualified name (e.g. `linear__list_issues`), which the model can then call directly. It's registered only when the agent has connections.
|
|
63
|
-
- **`web_search`** has no local executor; the provider runs it. AI Gateway models use
|
|
63
|
+
- **`web_search`** has no local executor; the provider runs it. AI Gateway models use Exa by default. To use Parallel instead, export `webSearch({ provider: "parallel" })` from `agent/tools/web_search.ts`. Direct provider models continue to use their native search implementation. To supply your own implementation, override it with `defineTool()`.
|
|
64
64
|
|
|
65
65
|
Review these built-in tools before production use. Disable, wrap, restrict, or require approval for any tool that can access the filesystem, network, shell, or sensitive data.
|
|
66
66
|
|
|
@@ -88,10 +88,10 @@ Provider-managed web search has a dedicated configuration helper instead of an e
|
|
|
88
88
|
```ts title="agent/tools/web_search.ts"
|
|
89
89
|
import { webSearch } from "eve/tools";
|
|
90
90
|
|
|
91
|
-
export default webSearch({ provider: "
|
|
91
|
+
export default webSearch({ provider: "parallel" });
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
Set `provider` to `"
|
|
94
|
+
Set `provider` to `"exa"` or `"parallel"`. Without this file, AI Gateway models use Exa.
|
|
95
95
|
|
|
96
96
|
## Disable a default
|
|
97
97
|
|
|
@@ -150,7 +150,7 @@ curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId> \
|
|
|
150
150
|
|
|
151
151
|
The follow-up reuses the same durable session: same history, same state.
|
|
152
152
|
|
|
153
|
-
If the session is waiting on a human-in-the-loop approval, a matching text reply such as `approve` or `
|
|
153
|
+
If the session is waiting on a human-in-the-loop approval, a matching text reply such as `approve` or `cancel` answers the approval. Other follow-up text is held until the approval is answered, so an unrelated message does not implicitly deny the pending tool call.
|
|
154
154
|
|
|
155
155
|
If the session is waiting on `ask_question`, a follow-up message clears that pending request before the model continues. An exact option match or permitted freeform response answers the question; any other message marks the question unanswered and starts the follow-up turn.
|
|
156
156
|
|
package/docs/connections/mcp.mdx
CHANGED
|
@@ -89,6 +89,34 @@ export default defineMcpClientConnection({
|
|
|
89
89
|
|
|
90
90
|
`auth` and `headers` can also be functions that receive the active session context. Use that when credentials, tenants, or routing depend on the caller.
|
|
91
91
|
|
|
92
|
+
## Application-provided tool arguments
|
|
93
|
+
|
|
94
|
+
Some MCP servers require arguments that belong to the application rather than the model. For example, UCP servers expect the agent profile in `arguments.meta` on every tool call. Configure those values with `toolCall.providedArguments`:
|
|
95
|
+
|
|
96
|
+
```ts title="agent/connections/storefront.ts"
|
|
97
|
+
import { defineMcpClientConnection } from "eve/connections";
|
|
98
|
+
|
|
99
|
+
const profileUrl = "https://agent.example.com/.well-known/ucp";
|
|
100
|
+
|
|
101
|
+
export default defineMcpClientConnection({
|
|
102
|
+
url: "https://store.example.com/api/ucp/mcp",
|
|
103
|
+
description: "Storefront catalog, carts, checkouts, and orders.",
|
|
104
|
+
toolCall: {
|
|
105
|
+
providedArguments: {
|
|
106
|
+
meta: ({ session }) => ({
|
|
107
|
+
"ucp-agent": {
|
|
108
|
+
profile: `${profileUrl}?session=${encodeURIComponent(session.id)}`,
|
|
109
|
+
},
|
|
110
|
+
}),
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Values may be JSON values, promises, or callbacks. Callbacks receive the active session context plus the bare remote `toolName`.
|
|
117
|
+
|
|
118
|
+
eve treats configured keys as application-owned: it removes them from every remote tool's model-facing input schema and adds their resolved values immediately before execution. They apply to every tool call on the connection and replace any conflicting model value. Approval policies continue to receive only the model-authored input.
|
|
119
|
+
|
|
92
120
|
## No auth
|
|
93
121
|
|
|
94
122
|
Omit `auth` and `headers` only for intentionally public or local-only servers:
|
|
@@ -186,6 +214,12 @@ Return `"user-approval"` (or `true`) to pause for a person and `"not-applicable"
|
|
|
186
214
|
|
|
187
215
|
[Human-in-the-loop](/docs/human-in-the-loop#approvals) covers the full set of statuses, how `approvedTools` and `session.auth` factor in, and how a gated call pauses and resumes durably.
|
|
188
216
|
|
|
217
|
+
## Control result size
|
|
218
|
+
|
|
219
|
+
eve sends an MCP tool's returned content to the model as the tool result. MCP connections do not expose a per-result transform equivalent to an authored tool's [`toModelOutput`](/docs/tools#shape-what-the-model-sees-with-tomodeloutput).
|
|
220
|
+
|
|
221
|
+
When a remote tool returns more data than the model needs, narrow the result at the MCP server. Prefer a purpose-built search or summary tool, or return a stable handle that another call can use to fetch a smaller slice. If you do not control the server and an equivalent upstream API is available, replace only that operation with an authored tool that stores the full payload outside model context and projects the needed fields through `toModelOutput`. Block the original MCP operation through [`tools.block`](#tool-filters) so the model does not see duplicate tools.
|
|
222
|
+
|
|
189
223
|
## Troubleshooting
|
|
190
224
|
|
|
191
225
|
| Symptom | Check |
|
|
@@ -193,7 +227,7 @@ Return `"user-approval"` (or `true`) to pause for a person and `"not-applicable"
|
|
|
193
227
|
| `principal_required` | A user-scoped `connect("...")` ran without an authenticated user. Return `principalType: "user"` from route auth, or use app-scoped auth. |
|
|
194
228
|
| The model does not find the remote tool | Improve the connection `description`, then check `tools.allow` / `tools.block`. |
|
|
195
229
|
| OAuth works locally but fails after deploy | Attach the Connect connector to the deployed Vercel project and verify the UID in `connect("...")`. |
|
|
196
|
-
| The server rejects requests | Confirm the MCP URL, transport support, auth scheme, and
|
|
230
|
+
| The server rejects requests | Confirm the MCP URL, transport support, auth scheme, required headers, and application-provided arguments. |
|
|
197
231
|
|
|
198
232
|
## What to read next
|
|
199
233
|
|