eve 0.83.2 → 0.83.3
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 +20 -0
- package/dist/src/chunks/{use-eve-agent-ZY7NQ2VW.js → use-eve-agent-BE2_zd5q.js} +17 -12
- package/dist/src/chunks/{use-eve-agent-DnbG9c7c.js → use-eve-agent-e8a5vcFO.js} +17 -12
- package/dist/src/client/optimistic-message-submissions.d.ts +2 -3
- package/dist/src/client/optimistic-message-submissions.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/execution/cancelled-model-call-batch.js +1 -1
- package/dist/src/execution/catalog/connection-auth.js +1 -1
- package/dist/src/execution/catalog/rank.js +1 -1
- package/dist/src/execution/dynamic-workflow/tool.js +1 -1
- package/dist/src/execution/legacy-session/prepare-step.js +1 -1
- package/dist/src/execution/session/handoff-steps.d.ts +1 -0
- package/dist/src/execution/session/handoff-steps.js +1 -1
- package/dist/src/execution/session/handoff.d.ts +1 -0
- package/dist/src/execution/session/handoff.js +1 -1
- package/dist/src/execution/session/program.js +1 -1
- package/dist/src/execution/session/turn-step.js +1 -1
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/web-fetch/html.js +3 -4
- package/dist/src/harness/compaction/engine.js +2 -2
- package/dist/src/harness/compaction/over-limit-error.d.ts +7 -0
- package/dist/src/harness/compaction/over-limit-error.js +1 -0
- package/dist/src/harness/compaction/prompt.js +1 -1
- package/dist/src/harness/compaction/step.d.ts +9 -0
- package/dist/src/harness/compaction/step.js +1 -1
- package/dist/src/harness/emission.d.ts +8 -3
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/model-call/call.d.ts +6 -2
- package/dist/src/harness/model-call/call.js +1 -1
- package/dist/src/harness/model-call/failure.d.ts +7 -0
- package/dist/src/harness/model-call/failure.js +1 -1
- package/dist/src/harness/model-call/run.js +1 -1
- package/dist/src/harness/model-call/usage.js +1 -1
- package/dist/src/harness/session-machine/current.d.ts +8 -0
- package/dist/src/harness/session-machine/current.js +1 -1
- package/dist/src/internal/application/stamped-package-version.js +1 -1
- package/dist/src/internal/authored-asset-import-plugin.d.ts +8 -7
- package/dist/src/internal/authored-asset-import-plugin.js +1 -1
- package/dist/src/internal/nitro/dev-client-address.d.ts +12 -0
- package/dist/src/internal/nitro/dev-client-address.js +1 -1
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.d.ts +6 -3
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
- package/dist/src/public/channels/auth.d.ts +14 -0
- package/dist/src/public/channels/auth.js +1 -1
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +6 -1
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
- package/dist/src/public/channels/slack/reply-delivery.d.ts +1 -7
- package/dist/src/public/channels/slack/reply-delivery.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +2 -6
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/undelivered-reply.d.ts +30 -0
- package/dist/src/public/channels/undelivered-reply.js +1 -0
- package/dist/src/runtime/connections/mcp-client.d.ts +5 -2
- 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/openapi-operations.d.ts +4 -0
- package/dist/src/runtime/connections/openapi-operations.js +1 -1
- package/dist/src/runtime/connections/openapi-schema.d.ts +19 -14
- package/dist/src/runtime/connections/openapi-schema.js +1 -1
- package/dist/src/runtime/local-dev-capability.d.ts +2 -2
- package/dist/src/runtime/local-dev-capability.js +1 -1
- package/dist/src/runtime/subagents/workflow.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/shared/connection-types.d.ts +5 -0
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tools/provided/ask-question-workflow.js +1 -1
- package/dist/src/tools/provided/sleep-workflow.js +1 -1
- package/dist/src/tools/schema.d.ts +2 -0
- package/dist/src/tools/schema.js +1 -1
- package/dist/src/tools/signature.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/slack.mdx +1 -1
- package/docs/concepts/default-harness.md +1 -1
- package/docs/concepts/sessions-runs-and-streaming.md +1 -1
- package/docs/connections/openapi.mdx +1 -1
- package/docs/guides/auth-and-route-protection.md +4 -0
- package/docs/reference/cli.md +3 -1
- package/docs/reference/typescript-api.md +3 -1
- package/docs/tools/tasks.md +2 -2
- package/package.json +6 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{streamText}from"ai";import{createLogger,logError}from"#internal/logging.js";import{contextStorage}from"#context/container.js";import{AuthKey,HistoryStateKey}from"#context/keys.js";import{createCompactionCompletedEvent,createCompactionRequestedEvent}from"#protocol/message.js";import{isUserModelMessage,validateHarnessModelMessages}from"#harness/messages.js";import{drainMemoryCommit,prepareMemoryCompaction}from"#context/memory-lifecycle.js";import{canonicalizeMemoryRecords,shouldCanonicalizeMemory}from"#shared/memory-state.js";import{activeTurnId}from"#harness/session-machine/view.js";import{
|
|
1
|
+
import{streamText}from"ai";import{createLogger,logError}from"#internal/logging.js";import{contextStorage}from"#context/container.js";import{addStepUsage}from"#harness/session-machine/current.js";import{AuthKey,HistoryStateKey}from"#context/keys.js";import{createCompactionCompletedEvent,createCompactionRequestedEvent}from"#protocol/message.js";import{isUserModelMessage,validateHarnessModelMessages}from"#harness/messages.js";import{drainMemoryCommit,prepareMemoryCompaction}from"#context/memory-lifecycle.js";import{canonicalizeMemoryRecords,shouldCanonicalizeMemory}from"#shared/memory-state.js";import{activeTurnId}from"#harness/session-machine/view.js";import{addUsageOutsideTurns}from"#harness/turn-tag-state.js";import{idle}from"#harness/session-machine/transitions.js";import{stepStartedForResolvers}from"#harness/session-machine/resolver-events.js";import{CompactionOverLimitError}from"#harness/compaction/over-limit-error.js";import{estimateTokens}from"#harness/token-estimate.js";import{requireSessionModelReference}from"#harness/types.js";import{buildGatewayAttributionHeaders,resolveEffectiveRuntimeModel,withGatewayTraceContext}from"#harness/model-call/model.js";import{compactMessages,getInputTokenCount,shouldCompact}from"#harness/compaction/engine.js";import{normalizeModelStreamError}from"#harness/model-call/errors.js";import{runModelCallWithRetries}from"#harness/model-call/retry.js";import{extractGatewayCostUsd,extractTokenUsageDelta,gatewayModelId}from"#harness/model-call/usage.js";import{resolveModelProfile}from"#harness/model-profile.js";import{getRequestEnvelopeTokens}from"#harness/request-envelope.js";import{resolveCallProviderOptions}from"#harness/provider-safety.js";const log=createLogger(`harness.tool-loop`);function assertFixedPartFits(e){let t=e.requestEnvelopeTokens+e.protectedTokens;if(t>=e.threshold)throw new CompactionOverLimitError(`The agent's system prompt, tools, memory and context take ~${Math.round(t)} tokens, over this model's ~${e.threshold}-token compaction limit. Shorten the instructions, remove tools or trim memory.`)}async function compactHistory(e){let{config:t}=e;if(e.session.history.length>0)try{if(e.ctx!==void 0&&t.dispatchDynamicModelEvent!==void 0){let n=e.position();await t.dispatchDynamicModelEvent({ctx:e.ctx,event:stepStartedForResolvers({modelId:e.session.agent.modelReference?.id??`dynamic`,sequence:n.sequence,stepIndex:n.stepIndex,turnId:activeTurnId(n)}),messages:validateHarnessModelMessages(e.projectHistory(e.session.history))})}let n=await resolveEffectiveRuntimeModel({config:t,ctx:e.ctx,session:e.session});e.session=n.session;let r=e.position(),i=await maybeCompact({abortSignal:t.abortSignal,auth:e.ctx?.get(AuthKey)??null,betweenTurns:!0,emissionState:{...r,turnId:activeTurnId(r)},force:!0,historyProjector:t.historyProjector,messages:[...e.session.history],model:n.model,publish:e.publish,requestEnvelopeTokens:getRequestEnvelopeTokens(e.session),resolveModel:t.resolveModel,runtimeIdentity:t.runtimeIdentity,session:e.session,telemetry:e.instrumentation?.telemetry(),traceContextHeaders:e.instrumentation?.gatewayTraceContextHeaders});if(e.session=i.session,i.failure!==void 0)throw i.failure.error}catch(t){logError(log,`manual session compaction failed`,t,{sessionId:e.session.sessionId})}return await e.apply(idle(e.view())),{next:null,session:e.session}}function replaceSessionHistory(e,t){return contextStorage.getStore()?.delete(HistoryStateKey),{...e,history:t,compaction:{recentWindowSize:e.compaction.recentWindowSize,threshold:e.compaction.threshold,thresholdPercent:e.compaction.thresholdPercent}}}async function maybeCompact(e){let{emissionState:t,publish:n}=e,r=e.messages,i=e.session,a=e.promptMessages??r,o=validateHarnessModelMessages(e.historyProjector?.({messages:a,state:i.state})??a),s=e.force===!0||shouldCompact(o,i.compaction,e.requestEnvelopeTokens,getRequestEnvelopeTokens(i)),c=shouldCanonicalizeMemory(r);if(!s&&!c)return{compacted:!1,messages:r,session:i};let l=requireSessionModelReference(i),u=i.agent.compactionModelReference??l,d=u===l?e.model:await e.resolveModel(u),f=resolveModelProfile(d),p=resolveCallProviderOptions({auth:e.auth,profile:f,providerOptions:u.providerOptions,session:i}),m={abortSignal:e.abortSignal,headers:buildGatewayAttributionHeaders(f,e.runtimeIdentity),model:withGatewayTraceContext(d,f,e.traceContextHeaders),providerOptions:p,telemetry:e.telemetry&&{...e.telemetry,functionId:`eve.compaction`}},h=[],summarize=async n=>{let r=await runModelCallWithRetries(()=>streamCompactionSummary({...m,...n}),{sessionId:i.sessionId,turnId:t.turnId},e.abortSignal);if(h.push(r.usage),r.text.trim().length===0)throw Error(`The compaction model returned an empty summary. Finish reason: ${r.finishReason}.`);return r.text};{let a=contextStorage.getStore();a!==void 0&&prepareMemoryCompaction(a,{history:r,state:i.state}),await n(createCompactionRequestedEvent({modelId:gatewayModelId(d)??`unknown`,sequence:t.sequence,sessionId:i.sessionId,stepIndex:t.stepIndex,turnId:t.turnId,usageInputTokens:getInputTokenCount(o,i.compaction,e.requestEnvelopeTokens,getRequestEnvelopeTokens(i))}),o)}let g=canonicalizeMemoryRecords(r),_=validateHarnessModelMessages(e.historyProjector?.({messages:g.ordinary,state:i.state})??g.ordinary),v=e.requestEnvelopeTokens??0,y=Math.max(0,estimateTokens(o)-estimateTokens(_)),b={...i.compaction,threshold:Math.max(1,i.compaction.threshold-v-y),lastKnownInputTokens:i.compaction.lastKnownInputTokens===void 0?void 0:Math.max(0,i.compaction.lastKnownInputTokens-Math.min(getRequestEnvelopeTokens(i)??0,v))},x=[..._],S;try{s&&(assertFixedPartFits({protectedTokens:y,requestEnvelopeTokens:v,threshold:i.compaction.threshold}),x=await compactMessages([..._],b,summarize,e.force===!0,e.requestEnvelopeTokens===void 0?void 0:Math.max(0,getInputTokenCount(o,i.compaction,v,getRequestEnvelopeTokens(i))-v-y)))}catch(e){S={error:e}}for(let n of h)i=e.betweenTurns===!0?addUsageOutsideTurns(i,n):addStepUsage(i,t.turnId,n);if(S!==void 0)return{compacted:!1,failure:S,messages:e.messages,session:i};r=validateHarnessModelMessages([...g.memory,...x]);{let a=contextStorage.getStore();if(a!==void 0&&prepareMemoryCompaction(a,{history:r,state:i.state}),await n(createCompactionCompletedEvent({modelId:gatewayModelId(d)??`unknown`,sequence:t.sequence,sessionId:i.sessionId,stepIndex:t.stepIndex,turnId:t.turnId}),e.historyProjector?.({messages:r,state:i.state})??r),a!==void 0){let t=drainMemoryCommit(a);t!==void 0&&(r=validateHarnessModelMessages(placeRecalledMemory(r,t.recalledMessages,e.betweenTurns===!0)),i={...i,state:t.state})}}return{compacted:!0,messages:r,session:replaceSessionHistory(i,r)}}function compactedTurnInput(e){return e.findLast(e=>isUserModelMessage(e)&&e.kind===`user`)}function placeRecalledMemory(e,t,n){let r=n?void 0:compactedTurnInput(e);if(r===void 0)return[...e,...t];let i=e.indexOf(r);return[...e.slice(0,i),...t,...e.slice(i)]}async function streamCompactionSummary(e){let t=streamText({...e,onError:()=>{}});for await(let e of t.fullStream)if(e.type===`error`)throw normalizeModelStreamError(e.error);let[n,r,i,a]=await Promise.all([t.text,t.finishReason,t.usage,t.providerMetadata]);return{finishReason:r,text:n,usage:extractTokenUsageDelta({costUsd:extractGatewayCostUsd(a),usage:i})}}export{assertFixedPartFits,compactHistory,compactedTurnInput,maybeCompact,replaceSessionHistory};
|
|
@@ -23,11 +23,16 @@ interface EmittedStreamContent {
|
|
|
23
23
|
interface StreamActionEmissionOptions {
|
|
24
24
|
readonly excludedActionToolNames: ReadonlySet<string>;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* as the reply.
|
|
26
|
+
* The session is a child or a schedule, whose caller hears only the turn's
|
|
27
|
+
* real end: text before `eve__task_wait` reports `"tool-calls"`, and
|
|
28
|
+
* channels don't post it as the reply.
|
|
29
29
|
*/
|
|
30
30
|
readonly hidesHeldText?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* A task is working, so the turn is held and a text step can't end it. For a
|
|
33
|
+
* session that `hidesHeldText`, the step reports `"tool-calls"`.
|
|
34
|
+
*/
|
|
35
|
+
readonly tasksWorking?: boolean;
|
|
31
36
|
readonly tools: HarnessToolLookup;
|
|
32
37
|
readonly unsettledActionToolNames?: Map<string, string>;
|
|
33
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createActionInputAppendedEvent,createActionPartialEvent,createActionResultEvent,createActionsRequestedEvent,createMessageAppendedEvent,createMessageCompletedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent}from"#protocol/message.js";import{createRuntimeToolResultFromStepResult,createRuntimeToolResultFromToolError,createToolResultMessagePartFromToolError,toActionResult}from"#harness/action-result-helpers.js";import{normalizeModelStreamError}from"#harness/model-call/errors.js";import{createInvalidToolCallInputError,isInvalidToolCall,resolveProviderToolCallRequest}from"#harness/tool-call-input-errors.js";import{collectActionPresentation,createPresentedRuntimeActionRequestFromToolCall}from"#harness/action-presentation.js";import{projectDeltaPresentation,projectResultPresentation}from"#harness/tool-presentation.js";import{createProviderStreamActionBatch}from"#harness/stream-actions.js";import{createOrderedStreamEmitter}from"#harness/ordered-stream-emitter.js";import{interruptStreamOnFailure}from"#harness/interruptible-stream.js";import{isInlineAuthorizationToolResult}from"#harness/inline-tool-authorization.js";import{normalizeAssistantStepFinishReason}from"#harness/finish-reason.js";async function emitStreamContent(e,t,n,r){let i=createOrderedStreamEmitter(e),a=createProviderStreamActionBatch({emitFn:i.emit,onActionsEmitted:e=>{for(let{request:t,toolName:n}of e)r?.unsettledActionToolNames?.set(t.action.callId,n)},state:t});try{return await consumeStreamContent(i.emit,t,interruptStreamOnFailure(n,i.failureSignal),a,r)}finally{try{await a.cancel()}finally{await i.closeAndDrain()}}}function reportedFinishReason(e,t){return t&&e===`stop`?`tool-calls`:e}async function consumeStreamContent(e,t,n,r,i){let a=``,o=``,s=`stop`,c,l=new Set,u=new Set,d=new Set,f=new Set,p=new Set,m=new Set,h=[],g=[],_=new Map,v=new Map,flushCurrentMessage=async()=>{o.length!==0&&(await e(createMessageCompletedEvent({finishReason
|
|
1
|
+
import{TASK_WAIT_TOOL_NAME}from"#protocol/task-tools.js";import{createActionInputAppendedEvent,createActionPartialEvent,createActionResultEvent,createActionsRequestedEvent,createMessageAppendedEvent,createMessageCompletedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent}from"#protocol/message.js";import{createRuntimeToolResultFromStepResult,createRuntimeToolResultFromToolError,createToolResultMessagePartFromToolError,toActionResult}from"#harness/action-result-helpers.js";import{normalizeModelStreamError}from"#harness/model-call/errors.js";import{createInvalidToolCallInputError,isInvalidToolCall,resolveProviderToolCallRequest}from"#harness/tool-call-input-errors.js";import{collectActionPresentation,createPresentedRuntimeActionRequestFromToolCall}from"#harness/action-presentation.js";import{projectDeltaPresentation,projectResultPresentation}from"#harness/tool-presentation.js";import{createProviderStreamActionBatch}from"#harness/stream-actions.js";import{createOrderedStreamEmitter}from"#harness/ordered-stream-emitter.js";import{interruptStreamOnFailure}from"#harness/interruptible-stream.js";import{isInlineAuthorizationToolResult}from"#harness/inline-tool-authorization.js";import{normalizeAssistantStepFinishReason}from"#harness/finish-reason.js";async function emitStreamContent(e,t,n,r){let i=createOrderedStreamEmitter(e),a=createProviderStreamActionBatch({emitFn:i.emit,onActionsEmitted:e=>{for(let{request:t,toolName:n}of e)r?.unsettledActionToolNames?.set(t.action.callId,n)},state:t});try{return await consumeStreamContent(i.emit,t,interruptStreamOnFailure(n,i.failureSignal),a,r)}finally{try{await a.cancel()}finally{await i.closeAndDrain()}}}function reportedFinishReason(e,t){return t&&e===`stop`?`tool-calls`:e}function textFinishReasonBefore(e,t){return e===TASK_WAIT_TOOL_NAME&&!t?`stop`:`tool-calls`}async function consumeStreamContent(e,t,n,r,i){let a=``,o=``,s=`stop`,c,l=new Set,u=new Set,d=new Set,f=new Set,p=new Set,m=new Set,h=[],g=[],_=new Map,v=new Map,flushCurrentMessage=async(n=`tool-calls`)=>{o.length!==0&&(await e(createMessageCompletedEvent({finishReason:n,message:o,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),o=``)},emitActionInput=async(n,r,a)=>{await e(createActionInputAppendedEvent({callId:n,inputTextDelta:a,sequence:t.sequence,stepIndex:t.stepIndex,toolName:r,turnId:t.turnId})),i?.unsettledActionToolNames?.set(n,r)},emitActionRequest=async(n,r)=>{let{action:a}=n;u.has(a.callId)||(o.trim().length>0&&await flushCurrentMessage(textFinishReasonBefore(r,i?.hidesHeldText===!0)),u.add(a.callId),_.set(a.callId,a.input),await e(createActionsRequestedEvent({actions:[a],presentation:collectActionPresentation([n]),sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),i?.unsettledActionToolNames?.set(a.callId,r))},collectProviderToolCall=async e=>{if(f.has(e.toolCallId)||(f.add(e.toolCallId),u.has(e.toolCallId)))return;u.add(e.toolCallId),o.trim().length>0&&await flushCurrentMessage();let t=resolveProviderToolCallRequest(e,i?.tools??new Map);if(t.toolError!==void 0){m.add(e.toolCallId),await emitActionResult(createRuntimeToolResultFromToolError(t.toolError)),p.add(e.toolCallId),g.push(createToolResultMessagePartFromToolError(t.toolError));return}_.set(t.request.action.callId,t.request.action.input),r.observe(t.request,e.toolName)},emitActionResult=async n=>{if(d.has(n.callId))return;d.add(n.callId);let r=n.isError===!0?void 0:projectResultPresentation(i?.tools.get(n.toolName),n.callId,_.get(n.callId),n.output);await e(createActionResultEvent({presentation:r,result:toActionResult(n,_.get(n.callId)),sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),i?.unsettledActionToolNames?.delete(n.callId)},emitActionPartial=async n=>{let r=projectDeltaPresentation(i?.tools.get(n.toolName),n.callId,_.get(n.callId),n.output);await e(createActionPartialEvent({presentation:r,result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}))},emitToolCall=async e=>{if(isInvalidToolCall(e)){m.add(e.toolCallId);return}if(!(i===void 0||i.excludedActionToolNames.has(e.toolName)))try{await emitActionRequest(createPresentedRuntimeActionRequestFromToolCall({toolCall:e,tools:i.tools}),e.toolName)}catch(t){if(t instanceof TypeError){let n=createInvalidToolCallInputError({error:t,toolCall:e});m.add(e.toolCallId),o.trim().length>0&&await flushCurrentMessage(),await emitActionResult(createRuntimeToolResultFromToolError(n)),p.add(e.toolCallId),g.push(createToolResultMessagePartFromToolError(n));return}throw t}};for await(let d of n)if(c===void 0)switch(d.type){case`reasoning-delta`:await r.flush(),a+=d.text,await e(createReasoningAppendedEvent({reasoningDelta:d.text,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}));break;case`text-delta`:await r.flush(),a.trim().length>0&&(await e(createReasoningCompletedEvent({reasoning:a,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),a=``),o+=d.text,await e(createMessageAppendedEvent({messageDelta:d.text,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}));break;case`tool-input-start`:if(i===void 0||d.providerExecuted===!0||i.tools.get(d.toolName)?.behavior?.handling?.kind===`provider-tool`||i.excludedActionToolNames.has(d.toolName)){v.delete(d.id);break}await r.flush(),o.trim().length>0&&await flushCurrentMessage(textFinishReasonBefore(d.toolName,i.hidesHeldText===!0)),v.set(d.id,{toolName:d.toolName});break;case`tool-input-delta`:{let e=v.get(d.id);if(e===void 0)break;await r.flush(),await emitActionInput(d.id,e.toolName,d.delta);break}case`tool-input-end`:v.delete(d.id);break;case`tool-call`:{let e=d;v.delete(e.toolCallId),l.add(e.toolCallId),e.providerExecuted===!0?await collectProviderToolCall(e):(await r.flush(),await emitToolCall(e));break}case`tool-result`:{let e=d;if(e.preliminary===!0){e.providerExecuted!==!0&&await emitActionPartial(createRuntimeToolResultFromStepResult(e));break}if(e.providerExecuted===!0){await collectProviderToolCall({input:`input`in e?e.input:void 0,toolCallId:e.toolCallId,toolName:e.toolName}),await r.flush(),await emitActionResult(createRuntimeToolResultFromStepResult(e));break}if(l.has(d.toolCallId)){if(isInlineAuthorizationToolResult(e))break;u.has(d.toolCallId)&&(await emitActionResult(createRuntimeToolResultFromStepResult(e)),p.add(d.toolCallId));break}if(await r.flush(),await flushCurrentMessage(),isInlineAuthorizationToolResult(e)){p.add(d.toolCallId),h.push(e);break}await emitActionResult(createRuntimeToolResultFromStepResult(e)),p.add(d.toolCallId);break}case`tool-error`:{let e=d;e.providerExecuted===!0?(await collectProviderToolCall(e),await r.flush(),await emitActionResult(createRuntimeToolResultFromToolError(e))):u.has(e.toolCallId)&&(await emitActionResult(createRuntimeToolResultFromToolError(e)),p.add(e.toolCallId),g.push(createToolResultMessagePartFromToolError(e)));break}case`finish-step`:s=normalizeAssistantStepFinishReason(d.finishReason),await r.flush();break;case`error`:c=normalizeModelStreamError(d.error);break;case`abort`:throw new DOMException(d.reason??`The model stream was aborted.`,`AbortError`)}if(await r.flush(),c!==void 0)throw c;return a.trim().length>0&&await e(createReasoningCompletedEvent({reasoning:a,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),s!==`content-filter`&&o.trim().length>0&&await e(createMessageCompletedEvent({finishReason:reportedFinishReason(s,i?.hidesHeldText===!0&&i.tasksWorking===!0),message:o,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),{emittedActionCallIds:u,handledInlineToolResultCallIds:p,invalidInputToolCallIds:m,inlineAuthorizationResults:h,trailingInlineToolResultParts:g}}export{emitStreamContent};
|
|
@@ -64,8 +64,12 @@ export declare class ModelCaller {
|
|
|
64
64
|
* attempt starts. The replacement re-requests whatever the model still wants to run.
|
|
65
65
|
*/
|
|
66
66
|
private settleRetriedActions;
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* A failed compaction fails the step, whatever recovery the call attempted.
|
|
69
|
+
* One that left the history over the limit ends the session with its message;
|
|
70
|
+
* any other failure throws.
|
|
71
|
+
*/
|
|
72
|
+
failIfCompactionFailed(): Promise<StepResult | undefined>;
|
|
69
73
|
/** Steering interrupted the call: the step ends with what it committed, and runs again. */
|
|
70
74
|
steered(): Promise<StepResult>;
|
|
71
75
|
/** The step's result counted its usage, so an interrupted call's usage no longer applies. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildGatewayAttributionHeaders,withGatewayTraceContext}from"./model.js";import{isEmptyModelResponse,rethrowNoOutputAsEmptyResponse}from"./recovery.js";import{modelInstructions,requestMessages,withTrailingUserNote}from"./request.js";import{answerSkippedToolCalls,appendMissingToolResultMessages,extractToolResultCallIds,withAccumulatedResponseMessages}from"./response.js";import{runModelCallWithRetries}from"./retry.js";import{logToolExecutionError,prepareModelTools}from"./tools.js";import{extractGatewayCostUsd,extractTokenUsageDelta}from"./usage.js";import{ToolLoopAgent,isStepCount}from"ai";import{createLogger,logError}from"#internal/logging.js";import{AuthKey,HistoryStateKey}from"#context/keys.js";import{REPLY_TOOL_NAME}from"#protocol/reply-tool.js";import{createStepCompletedEvent}from"#protocol/message.js";import{activeTurnId}from"#harness/session-machine/view.js";import{addTurnUsage}from"#harness/turn-tag-state.js";import{hydrateSandboxAttachments,moveToolResultFilesToUserMessages}from"#harness/attachment-staging.js";import{workingTaskIds}from"#execution/tasks/model-step.js";import{toEntryStep,toEntryStream,toEntryTelemetry}from"#harness/execute-call.js";import{discardAttempt}from"#harness/session-machine/transitions.js";import{throwIfTurnAborted}from"#harness/turn-cancellation.js";import{summarizeKnownError}from"#harness/semantic-errors/index.js";import{estimateTokens}from"#harness/token-estimate.js";import{requireSessionModelReference}from"#harness/types.js";import{ContentFilteredModelResponseError,EmptyModelResponseError,ModelStreamStalledError}from"#harness/model-call/errors.js";import{resolveModelProfile}from"#harness/model-profile.js";import{estimateRequestEnvelope}from"#harness/request-envelope.js";import{interruptStreamOnFailure}from"#harness/interruptible-stream.js";import{buildToolApproval}from"#harness/tools.js";import{emitStreamContent}from"#harness/emission.js";import{compactPrompt,projectPrompt,withClientContext}from"#harness/step/prompt.js";import{buildStepHooks,emitStepActions,readGatewayGenerationId}from"#harness/step-hooks.js";import{compactedTurnInput,maybeCompact}from"#harness/compaction/step.js";const environment=process.env.NODE_ENV??`unknown`,log=createLogger(`harness.tool-loop`),MODEL_OUTPUT_TIMEOUT_MS=6e5;var ModelCaller=class{profile;attributionHeaders;projectedMessages;turnMessages;request;modelMessages=[];requestEnvelopeTokens=0;interruptedUsage;compactionFailure;attemptIndex=0;step;prompt;input;constructor(e,t,n){this.step=e,this.prompt=t,this.input=n,this.profile=resolveModelProfile(n.model,requireSessionModelReference(e.session).promptCache),this.attributionHeaders=buildGatewayAttributionHeaders(this.profile,e.config.runtimeIdentity),this.projectedMessages=n.projectedMessages,this.turnMessages=n.turnMessages,this.request=this.buildRequest()}async call(e){let t=new Map;return await runModelCallWithRetries(async n=>(n>1&&(await this.settleRetriedActions(t),t=new Map),await this.attempt({...e,suppressStepStartedEmission:n!==1||e.suppressStepStartedEmission},t)),{canRetry:()=>this.compactionFailure===void 0,sessionId:this.step.session.sessionId,turnId:this.step.position().turnId},this.input.generation.signal)}async settleRetriedActions(e){for(let[t,n]of e)await this.step.apply(discardAttempt(this.step.view(),{calls:[{callId:t,toolName:n}]})),e.delete(t)}throwIfCompactionFailed(){if(this.compactionFailure!==void 0)throw this.compactionFailure.error}async steered(){let{step:e}=this;return throwIfTurnAborted(e.config.abortSignal),e.ctx?.set(HistoryStateKey,this.request.historyState),this.interruptedUsage!==void 0&&(e.session=addTurnUsage(e.session,e.position().turnId,this.interruptedUsage)),await e.emit?.(createStepCompletedEvent({...e.position(),finishReason:`other`,usage:this.interruptedUsage})),{next:e.runStep,session:{...e.session,history:[...this.request.history]},steered:!0}}clearInterruptedUsage(){this.interruptedUsage=void 0}buildRequest(){return requestMessages(this.step,{catalog:this.input.catalog,hidesHeldText:this.input.hidesHeldText,messages:this.prompt.messages,pendingApprovalsNote:this.input.pendingApprovalsNote,projectedMessages:this.projectedMessages,turnMessages:this.turnMessages})}instructions(e){return modelInstructions({anthropicCache:this.profile.anthropicCache,extraSystemNote:e,session:this.step.session,systemMessages:this.request.systemMessages})}async prepare(e){let t=await prepareModelTools(this.step,{catalog:this.input.catalog,disabledProviderTools:e.disabledProviderTools,generation:this.input.generation,profile:this.profile});return this.request=this.buildRequest(),this.requestEnvelopeTokens=await estimateRequestEnvelope({history:this.projectedMessages,instructions:this.instructions(e.extraSystemNote),messages:withTrailingUserNote(this.request.nonSystemMessages,e.trailingUserNote),tools:t}),t}async compact(e,t){let{step:n,prompt:r}=this,{config:i}=n,a;try{if(a=await maybeCompact({abortSignal:i.abortSignal,auth:n.ctx?.get(AuthKey)??null,emissionState:n.position(),historyProjector:i.historyProjector,messages:[...r.messages],model:this.input.model,promptMessages:withClientContext(r),publish:n.publish,requestEnvelopeTokens:this.requestEnvelopeTokens,resolveModel:i.resolveModel,runtimeIdentity:i.runtimeIdentity,session:n.session,telemetry:n.instrumentation?.telemetry(),traceContextHeaders:n.instrumentation?.gatewayTraceContextHeaders}),a.failure!==void 0)throw a.failure.error}catch(e){throw this.compactionFailure={error:e},e}if(n.session=a.session,!a.compacted)return t;compactPrompt(n,r,a.messages);let o=compactedTurnInput(r.messages);this.turnMessages=o===void 0?this.input.turnMessages:[...this.input.turnMessages,o];let{compaction:s}=n.session;return n.session={...n.session,compaction:{recentWindowSize:s.recentWindowSize,threshold:s.threshold,thresholdPercent:s.thresholdPercent}},this.projectedMessages=projectPrompt(n,r),await this.prepare(e)}async attempt(e,t){let{step:n}=this,{catalog:r,generation:i,model:a}=this.input,o=await this.compact(e,await this.prepare(e));this.requestEnvelopeTokens=Math.max(0,this.requestEnvelopeTokens-(estimateTokens(this.request.history)-estimateTokens(this.prompt.messages))),i.begin(),this.modelMessages=await hydrateSandboxAttachments(this.request.nonSystemMessages),this.profile.filesOutsideToolResults&&(this.modelMessages=moveToolResultFilesToUserMessages(this.modelMessages));let s=this.instructions(e.extraSystemNote),c=n.instrumentation?.resolveRuntimeContext({emissionState:n.position(),environment,modelInput:{instructions:s,messages:this.modelMessages},session:n.session})??{};e.retryReason&&(c[`eve.retry.reason`]=e.retryReason);let l=withTrailingUserNote(this.modelMessages,e.trailingUserNote),u=n.position(),d=n.instrumentation?.prepareAttempt({isFrameworkTool:e=>n.frameworkToolNames.has(e),attemptIndex:this.attemptIndex++,runtimeContext:c,stepIndex:u.stepIndex,turnId:activeTurnId(u)});this.input.setAttemptScope(d?.scope);let f=buildStepHooks({auth:n.ctx?.get(AuthKey)??null,profile:this.profile,session:n.session,startStep:e.suppressStepStartedEmission===!0?void 0:this.input.startStep}),p={headers:this.attributionHeaders,instructions:s,model:withGatewayTraceContext(a,this.profile,this.step.instrumentation?.gatewayTraceContextHeaders),onLanguageModelCallEnd:e=>{i.interrupted||(this.interruptedUsage=extractTokenUsageDelta({costUsd:extractGatewayCostUsd(e.providerMetadata),usage:e.usage}))},onToolExecutionEnd:e=>logToolExecutionError(e,r.resolve),onError(e){i.interrupted||summarizeKnownError(e.error)?.tags.includes(`config`)!==!0&&logError(log,`tool-loop stream error`,e.error)},onStepEnd:f.onStepEnd,onStepStart:f.onStepStart,prepareStep:f.prepareStep,reasoning:n.session.agent.modelReference?.reasoning??n.session.agent.reasoning,runtimeContext:c,stopWhen:isStepCount(1),telemetry:toEntryTelemetry(d?.telemetry,r.resolve),timeout:{chunkMs:MODEL_OUTPUT_TIMEOUT_MS,firstChunkMs:MODEL_OUTPUT_TIMEOUT_MS},toolApproval:buildToolApproval({abortSignal:i.signal,approvedTools:this.input.approvedTools,resolve:r.resolve}),tools:o},m=new ToolLoopAgent(p);try{let e=await this.stream(m,l,f.stepResult,t);return await d?.complete(),e}catch(e){if(i.interrupted||i.signal.aborted?await d?.complete():await d?.fail(e),i.check(),isAbortError(e)&&!i.signal.aborted)throw new ModelStreamStalledError({cause:e});return rethrowNoOutputAsEmptyResponse(e)}}async stream(e,t,n,r){let{step:i}=this,{catalog:a,generation:o}=this.input,s=new Set([REPLY_TOOL_NAME]),c=await e.stream({abortSignal:o.signal,messages:t}),{emittedActionCallIds:l,handledInlineToolResultCallIds:u,invalidInputToolCallIds:d,inlineAuthorizationResults:f,trailingInlineToolResultParts:p}=await emitStreamContent(i.publish,i.position(),toEntryStream(interruptStreamOnFailure(c.fullStream,o.signal),a.resolve),{excludedActionToolNames:s,hidesHeldText:this.input.hidesHeldText&&workingTaskIds(i.session).length>0,tools:a,unsettledActionToolNames:r});throwIfTurnAborted(i.config.abortSignal),o.check();let[m,h]=toEntryStep(...await Promise.all([n,c.responseMessages]),a.resolve);assertUsableResponse(m,h,{allowEmptyStop:!i.hasDelegatedCaller,hasInlineResults:f.length>0||p.length>0});let g=answerSkippedToolCalls(m,a);await emitStepActions(i.publish,i.position(),g.length===0?m:withAccumulatedResponseMessages({stepResult:m,responseMessages:appendMissingToolResultMessages({append:g,responseMessages:m.response.messages})}),{emittedActionCallIds:l,excludedActionCallIds:d,excludedActionToolNames:s,handledInlineToolResultCallIds:u,tools:a});let _=new Map(m.toolResults.map(e=>[e.toolCallId,e]));for(let e of f)_.set(e.toolCallId,e);return withAccumulatedResponseMessages({invalidInputToolCallIds:d,responseMessages:appendMissingToolResultMessages({append:[...p,...answerSkippedToolCalls(m,a)],responseMessages:h}),stepResult:m,toolResults:[..._.values()]})}};function assertUsableResponse(e,t,n){if(e.finishReason===`content-filter`)throw new ContentFilteredModelResponseError(readGatewayGenerationId(e.providerMetadata));if(!(n.allowEmptyStop&&e.finishReason===`stop`)&&isEmptyModelResponse(e)&&extractToolResultCallIds(t).size===0&&!n.hasInlineResults)throw new EmptyModelResponseError}function isAbortError(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}export{ModelCaller};
|
|
1
|
+
import{reportCompactionFailure}from"./failure.js";import{buildGatewayAttributionHeaders,withGatewayTraceContext}from"./model.js";import{isEmptyModelResponse,rethrowNoOutputAsEmptyResponse}from"./recovery.js";import{modelInstructions,requestMessages,withTrailingUserNote}from"./request.js";import{answerSkippedToolCalls,appendMissingToolResultMessages,extractToolResultCallIds,withAccumulatedResponseMessages}from"./response.js";import{runModelCallWithRetries}from"./retry.js";import{logToolExecutionError,prepareModelTools}from"./tools.js";import{extractGatewayCostUsd,extractTokenUsageDelta}from"./usage.js";import{ToolLoopAgent,isStepCount}from"ai";import{createLogger,logError}from"#internal/logging.js";import{addStepUsage}from"#harness/session-machine/current.js";import{AuthKey,HistoryStateKey}from"#context/keys.js";import{REPLY_TOOL_NAME}from"#protocol/reply-tool.js";import{createStepCompletedEvent}from"#protocol/message.js";import{activeTurnId}from"#harness/session-machine/view.js";import{hydrateSandboxAttachments,moveToolResultFilesToUserMessages}from"#harness/attachment-staging.js";import{workingTaskIds}from"#execution/tasks/model-step.js";import{toEntryStep,toEntryStream,toEntryTelemetry}from"#harness/execute-call.js";import{discardAttempt}from"#harness/session-machine/transitions.js";import{throwIfTurnAborted}from"#harness/turn-cancellation.js";import{summarizeKnownError}from"#harness/semantic-errors/index.js";import{CompactionOverLimitError}from"#harness/compaction/over-limit-error.js";import{estimateTokens}from"#harness/token-estimate.js";import{requireSessionModelReference}from"#harness/types.js";import{ContentFilteredModelResponseError,EmptyModelResponseError,ModelStreamStalledError}from"#harness/model-call/errors.js";import{resolveModelProfile}from"#harness/model-profile.js";import{estimateRequestEnvelope}from"#harness/request-envelope.js";import{interruptStreamOnFailure}from"#harness/interruptible-stream.js";import{buildToolApproval}from"#harness/tools.js";import{emitStreamContent}from"#harness/emission.js";import{compactPrompt,projectPrompt,withClientContext}from"#harness/step/prompt.js";import{buildStepHooks,emitStepActions,readGatewayGenerationId}from"#harness/step-hooks.js";import{compactedTurnInput,maybeCompact}from"#harness/compaction/step.js";const environment=process.env.NODE_ENV??`unknown`,log=createLogger(`harness.tool-loop`),MODEL_OUTPUT_TIMEOUT_MS=6e5;var ModelCaller=class{profile;attributionHeaders;projectedMessages;turnMessages;request;modelMessages=[];requestEnvelopeTokens=0;interruptedUsage;compactionFailure;attemptIndex=0;step;prompt;input;constructor(e,t,n){this.step=e,this.prompt=t,this.input=n,this.profile=resolveModelProfile(n.model,requireSessionModelReference(e.session).promptCache),this.attributionHeaders=buildGatewayAttributionHeaders(this.profile,e.config.runtimeIdentity),this.projectedMessages=n.projectedMessages,this.turnMessages=n.turnMessages,this.request=this.buildRequest()}async call(e){let t=new Map;return await runModelCallWithRetries(async n=>(n>1&&(await this.settleRetriedActions(t),t=new Map),await this.attempt({...e,suppressStepStartedEmission:n!==1||e.suppressStepStartedEmission},t)),{canRetry:()=>this.compactionFailure===void 0,sessionId:this.step.session.sessionId,turnId:this.step.position().turnId},this.input.generation.signal)}async settleRetriedActions(e){for(let[t,n]of e)await this.step.apply(discardAttempt(this.step.view(),{calls:[{callId:t,toolName:n}]})),e.delete(t)}async failIfCompactionFailed(){if(this.compactionFailure===void 0)return;let{error:e}=this.compactionFailure;if(e instanceof CompactionOverLimitError)return reportCompactionFailure(this.step,e);throw e}async steered(){let{step:e}=this;return throwIfTurnAborted(e.config.abortSignal),e.ctx?.set(HistoryStateKey,this.request.historyState),this.interruptedUsage!==void 0&&(e.session=addStepUsage(e.session,e.position().turnId,this.interruptedUsage)),await e.emit?.(createStepCompletedEvent({...e.position(),finishReason:`other`,usage:this.interruptedUsage})),{next:e.runStep,session:{...e.session,history:[...this.request.history]},steered:!0}}clearInterruptedUsage(){this.interruptedUsage=void 0}buildRequest(){return requestMessages(this.step,{catalog:this.input.catalog,hidesHeldText:this.input.hidesHeldText,messages:this.prompt.messages,pendingApprovalsNote:this.input.pendingApprovalsNote,projectedMessages:this.projectedMessages,turnMessages:this.turnMessages})}instructions(e){return modelInstructions({anthropicCache:this.profile.anthropicCache,extraSystemNote:e,session:this.step.session,systemMessages:this.request.systemMessages})}async prepare(e){let t=await prepareModelTools(this.step,{catalog:this.input.catalog,disabledProviderTools:e.disabledProviderTools,generation:this.input.generation,profile:this.profile});return this.request=this.buildRequest(),this.requestEnvelopeTokens=await estimateRequestEnvelope({history:this.projectedMessages,instructions:this.instructions(e.extraSystemNote),messages:withTrailingUserNote(this.request.nonSystemMessages,e.trailingUserNote),tools:t}),t}async compact(e,t){let{step:n,prompt:r}=this,{config:i}=n,a;try{if(a=await maybeCompact({abortSignal:i.abortSignal,auth:n.ctx?.get(AuthKey)??null,emissionState:n.position(),historyProjector:i.historyProjector,messages:[...r.messages],model:this.input.model,promptMessages:withClientContext(r),publish:n.publish,requestEnvelopeTokens:this.requestEnvelopeTokens,resolveModel:i.resolveModel,runtimeIdentity:i.runtimeIdentity,session:n.session,telemetry:n.instrumentation?.telemetry(),traceContextHeaders:n.instrumentation?.gatewayTraceContextHeaders}),n.session=a.session,a.failure!==void 0)throw a.failure.error}catch(e){throw this.compactionFailure={error:e},e}if(!a.compacted)return t;compactPrompt(n,r,a.messages);let o=compactedTurnInput(r.messages);this.turnMessages=o===void 0?this.input.turnMessages:[...this.input.turnMessages,o];let{compaction:s}=n.session;return n.session={...n.session,compaction:{recentWindowSize:s.recentWindowSize,threshold:s.threshold,thresholdPercent:s.thresholdPercent}},this.projectedMessages=projectPrompt(n,r),await this.prepare(e)}async attempt(e,t){let{step:n}=this,{catalog:r,generation:i,model:a}=this.input,o=await this.compact(e,await this.prepare(e));this.requestEnvelopeTokens=Math.max(0,this.requestEnvelopeTokens-(estimateTokens(this.request.history)-estimateTokens(this.prompt.messages))),i.begin(),this.modelMessages=await hydrateSandboxAttachments(this.request.nonSystemMessages),this.profile.filesOutsideToolResults&&(this.modelMessages=moveToolResultFilesToUserMessages(this.modelMessages));let s=this.instructions(e.extraSystemNote),c=n.instrumentation?.resolveRuntimeContext({emissionState:n.position(),environment,modelInput:{instructions:s,messages:this.modelMessages},session:n.session})??{};e.retryReason&&(c[`eve.retry.reason`]=e.retryReason);let l=withTrailingUserNote(this.modelMessages,e.trailingUserNote),u=n.position(),d=n.instrumentation?.prepareAttempt({isFrameworkTool:e=>n.frameworkToolNames.has(e),attemptIndex:this.attemptIndex++,runtimeContext:c,stepIndex:u.stepIndex,turnId:activeTurnId(u)});this.input.setAttemptScope(d?.scope);let f=buildStepHooks({auth:n.ctx?.get(AuthKey)??null,profile:this.profile,session:n.session,startStep:e.suppressStepStartedEmission===!0?void 0:this.input.startStep}),p={headers:this.attributionHeaders,instructions:s,model:withGatewayTraceContext(a,this.profile,this.step.instrumentation?.gatewayTraceContextHeaders),onLanguageModelCallEnd:e=>{i.interrupted||(this.interruptedUsage=extractTokenUsageDelta({costUsd:extractGatewayCostUsd(e.providerMetadata),usage:e.usage}))},onToolExecutionEnd:e=>logToolExecutionError(e,r.resolve),onError(e){i.interrupted||summarizeKnownError(e.error)?.tags.includes(`config`)!==!0&&logError(log,`tool-loop stream error`,e.error)},onStepEnd:f.onStepEnd,onStepStart:f.onStepStart,prepareStep:f.prepareStep,reasoning:n.session.agent.modelReference?.reasoning??n.session.agent.reasoning,runtimeContext:c,stopWhen:isStepCount(1),telemetry:toEntryTelemetry(d?.telemetry,r.resolve),timeout:{chunkMs:MODEL_OUTPUT_TIMEOUT_MS,firstChunkMs:MODEL_OUTPUT_TIMEOUT_MS},toolApproval:buildToolApproval({abortSignal:i.signal,approvedTools:this.input.approvedTools,resolve:r.resolve}),tools:o},m=new ToolLoopAgent(p);try{let e=await this.stream(m,l,f.stepResult,t);return await d?.complete(),e}catch(e){if(i.interrupted||i.signal.aborted?await d?.complete():await d?.fail(e),i.check(),isAbortError(e)&&!i.signal.aborted)throw new ModelStreamStalledError({cause:e});return rethrowNoOutputAsEmptyResponse(e)}}async stream(e,t,n,r){let{step:i}=this,{catalog:a,generation:o}=this.input,s=new Set([REPLY_TOOL_NAME]),c=await e.stream({abortSignal:o.signal,messages:t}),{emittedActionCallIds:l,handledInlineToolResultCallIds:u,invalidInputToolCallIds:d,inlineAuthorizationResults:f,trailingInlineToolResultParts:p}=await emitStreamContent(i.publish,i.position(),toEntryStream(interruptStreamOnFailure(c.fullStream,o.signal),a.resolve),{excludedActionToolNames:s,hidesHeldText:this.input.hidesHeldText,tasksWorking:workingTaskIds(i.session).length>0,tools:a,unsettledActionToolNames:r});throwIfTurnAborted(i.config.abortSignal),o.check();let[m,h]=toEntryStep(...await Promise.all([n,c.responseMessages]),a.resolve);assertUsableResponse(m,h,{allowEmptyStop:!i.hasDelegatedCaller,hasInlineResults:f.length>0||p.length>0});let g=answerSkippedToolCalls(m,a);await emitStepActions(i.publish,i.position(),g.length===0?m:withAccumulatedResponseMessages({stepResult:m,responseMessages:appendMissingToolResultMessages({append:g,responseMessages:m.response.messages})}),{emittedActionCallIds:l,excludedActionCallIds:d,excludedActionToolNames:s,handledInlineToolResultCallIds:u,tools:a});let _=new Map(m.toolResults.map(e=>[e.toolCallId,e]));for(let e of f)_.set(e.toolCallId,e);return withAccumulatedResponseMessages({invalidInputToolCallIds:d,responseMessages:appendMissingToolResultMessages({append:[...p,...answerSkippedToolCalls(m,a)],responseMessages:h}),stepResult:m,toolResults:[..._.values()]})}};function assertUsableResponse(e,t,n){if(e.finishReason===`content-filter`)throw new ContentFilteredModelResponseError(readGatewayGenerationId(e.providerMetadata));if(!(n.allowEmptyStop&&e.finishReason===`stop`)&&isEmptyModelResponse(e)&&extractToolResultCallIds(t).size===0&&!n.hasInlineResults)throw new EmptyModelResponseError}function isAbortError(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}export{ModelCaller};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { JsonObject } from "#shared/json.js";
|
|
2
2
|
import type { SemanticErrorSummary } from "#harness/semantic-errors/rule.js";
|
|
3
3
|
import { type UpstreamRejectionSummary } from "#harness/model-call/errors.js";
|
|
4
|
+
import type { CompactionOverLimitError } from "#harness/compaction/over-limit-error.js";
|
|
4
5
|
import type { Step } from "#harness/step/context.js";
|
|
5
6
|
import type { StepResult } from "#harness/types.js";
|
|
6
7
|
/**
|
|
@@ -50,3 +51,9 @@ export declare function buildModelCallFailureLogFields(input: {
|
|
|
50
51
|
* failure ends the session; any other fails the turn recoverably.
|
|
51
52
|
*/
|
|
52
53
|
export declare function reportModelCallFailure(step: Step, error: unknown): Promise<StepResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Compaction could not bring the history under the limit. The next attempt
|
|
56
|
+
* would fail the same way, so the session ends with the message instead of
|
|
57
|
+
* the step being retried.
|
|
58
|
+
*/
|
|
59
|
+
export declare function reportCompactionFailure(step: Step, error: CompactionOverLimitError): Promise<StepResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createErrorId,createLogger,formatError}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{fail}from"#harness/session-machine/transitions.js";import{summarizeKnownError}from"#harness/semantic-errors/index.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUpstreamRejectionMessage}from"#harness/model-call/errors.js";import{extractWorkflowStreamWriteErrorDetails}from"#harness/workflow-stream-error.js";const log=createLogger(`harness.tool-loop`);function buildModelCallFailureDetails(e){let{catalogSummary:t,error:n,errorId:r,modelCallDetails:i,upstreamRejection:a}=e;if(t!==null){let e={errorId:r,message:t.message,name:t.name,semanticErrorId:t.id,...i};return t.hint!==void 0&&(e.hint=t.hint),e}return a===null?{...formatError(n,r),...i}:{errorId:r,message:toErrorMessage(n),name:a.name,...i}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.recognized?{...t,details:e.modelCallDetails}:{...t,error:e.error}}async function reportModelCallFailure(e,t){if(e.instrumentation?.recordError(t),e.emit===void 0)throw t;let{sessionId:n}=e.session,{turnId:r}=e.position(),i=extractWorkflowStreamWriteErrorDetails(t);if(i!==null){let a=createErrorId();return log.error(`workflow stream write failed — parking session for retry by the user`,{...i,errorId:a,error:t,sessionId:n,turnId:r}),await e.apply(fail(e.view(),{code:`WORKFLOW_STREAM_WRITE_FAILED`,details:{...i,errorId:a},message:toErrorMessage(t)})),{next:null,session:e.session}}let a=createErrorId(),o=summarizeKnownError(t),s=o===null?extractUpstreamRejectionMessage(t):null,c=o?.message??s?.message??toErrorMessage(t),l=o?.hint===void 0?c:`${c} ${o.hint}`,u=extractModelCallErrorDetails(t),d=buildModelCallFailureDetails({catalogSummary:o,error:t,errorId:a,modelCallDetails:u,upstreamRejection:s}),f=buildModelCallFailureLogFields({error:t,errorId:a,modelCallDetails:u,recognized:o!==null||s!==null,sessionId:n,turnId:r});return classifyModelCallError(t)===`terminal`?(o===null?log.error(s?.message??`model call failed terminally`,f):log.error(`${o.name}: ${o.message}`,{errorId:a,hint:o.hint,sessionId:n,turnId:r}),await e.apply(fail(e.view(),{code:`MODEL_CALL_FAILED`,details:d,message:c,terminal:{sessionId:n}})),{next:e.hasDelegatedCaller?{done:!0,isError:!0,output:l}:{done:!0,output:``},session:e.session}):(log.error(s?.message??`model call failed — parking session for retry by the user`,f),await e.apply(fail(e.view(),{code:`MODEL_CALL_FAILED`,details:d,message:c})),e.session={...e.session,outputSchema:void 0},{next:null,session:e.session,settledTurn:{isError:!0,output:l}})}export{buildModelCallFailureDetails,buildModelCallFailureLogFields,reportModelCallFailure};
|
|
1
|
+
import{createErrorId,createLogger,formatError}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{fail}from"#harness/session-machine/transitions.js";import{summarizeKnownError}from"#harness/semantic-errors/index.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUpstreamRejectionMessage}from"#harness/model-call/errors.js";import{extractWorkflowStreamWriteErrorDetails}from"#harness/workflow-stream-error.js";const log=createLogger(`harness.tool-loop`);function buildModelCallFailureDetails(e){let{catalogSummary:t,error:n,errorId:r,modelCallDetails:i,upstreamRejection:a}=e;if(t!==null){let e={errorId:r,message:t.message,name:t.name,semanticErrorId:t.id,...i};return t.hint!==void 0&&(e.hint=t.hint),e}return a===null?{...formatError(n,r),...i}:{errorId:r,message:toErrorMessage(n),name:a.name,...i}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.recognized?{...t,details:e.modelCallDetails}:{...t,error:e.error}}async function reportModelCallFailure(e,t){if(e.instrumentation?.recordError(t),e.emit===void 0)throw t;let{sessionId:n}=e.session,{turnId:r}=e.position(),i=extractWorkflowStreamWriteErrorDetails(t);if(i!==null){let a=createErrorId();return log.error(`workflow stream write failed — parking session for retry by the user`,{...i,errorId:a,error:t,sessionId:n,turnId:r}),await e.apply(fail(e.view(),{code:`WORKFLOW_STREAM_WRITE_FAILED`,details:{...i,errorId:a},message:toErrorMessage(t)})),{next:null,session:e.session}}let a=createErrorId(),o=summarizeKnownError(t),s=o===null?extractUpstreamRejectionMessage(t):null,c=o?.message??s?.message??toErrorMessage(t),l=o?.hint===void 0?c:`${c} ${o.hint}`,u=extractModelCallErrorDetails(t),d=buildModelCallFailureDetails({catalogSummary:o,error:t,errorId:a,modelCallDetails:u,upstreamRejection:s}),f=buildModelCallFailureLogFields({error:t,errorId:a,modelCallDetails:u,recognized:o!==null||s!==null,sessionId:n,turnId:r});return classifyModelCallError(t)===`terminal`?(o===null?log.error(s?.message??`model call failed terminally`,f):log.error(`${o.name}: ${o.message}`,{errorId:a,hint:o.hint,sessionId:n,turnId:r}),await e.apply(fail(e.view(),{code:`MODEL_CALL_FAILED`,details:d,message:c,terminal:{sessionId:n}})),{next:e.hasDelegatedCaller?{done:!0,isError:!0,output:l}:{done:!0,output:``},session:e.session}):(log.error(s?.message??`model call failed — parking session for retry by the user`,f),await e.apply(fail(e.view(),{code:`MODEL_CALL_FAILED`,details:d,message:c})),e.session={...e.session,outputSchema:void 0},{next:null,session:e.session,settledTurn:{isError:!0,output:l}})}async function reportCompactionFailure(e,t){let{sessionId:n}=e.session;return log.error(t.message,{sessionId:n,turnId:e.position().turnId}),await e.apply(fail(e.view(),{code:`COMPACTION_FAILED`,message:t.message,terminal:{sessionId:n}})),{next:e.hasDelegatedCaller?{done:!0,isError:!0,output:t.message}:{done:!0,output:``},session:e.session}}export{buildModelCallFailureDetails,buildModelCallFailureLogFields,reportCompactionFailure,reportModelCallFailure};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveEffectiveRuntimeModel}from"./model.js";import{recoverModelCall}from"./recovery.js";import{endsTurnTools,frameworkToolNames}from"./tools.js";import{recordModelUsage}from"./usage.js";import{ModelCaller}from"./call.js";import{
|
|
1
|
+
import{reportModelCallFailure}from"./failure.js";import{resolveEffectiveRuntimeModel}from"./model.js";import{recoverModelCall}from"./recovery.js";import{endsTurnTools,frameworkToolNames}from"./tools.js";import{recordModelUsage}from"./usage.js";import{ModelCaller}from"./call.js";import{HistoryStateKey}from"#context/keys.js";import{validateHarnessModelMessages}from"#harness/messages.js";import{activeTurnId,nextStepIndex}from"#harness/session-machine/view.js";import{dispatchApprovedWorkflows,enforceBudget,hasApprovedWork,humanInputContext,runApprovedLocalCalls}from"#harness/hitl/index.js";import{startStep}from"#harness/session-machine/transitions.js";import{stepStartedForResolvers}from"#harness/session-machine/resolver-events.js";import{throwIfTurnAborted}from"#harness/turn-cancellation.js";import{requireSessionModelReference}from"#harness/types.js";import{buildStepCatalog,recordOfferedCatalogTools}from"#execution/catalog/step-catalog.js";import{buildPrompt,previewPrompt,projectPrompt,withClientContext}from"#harness/step/prompt.js";import{failBoundaryEvent,failModelSelection}from"#harness/step/context.js";async function runModelStep(e,t){let{generation:n}=t,r=hasApprovedWork(e),i=r?previewPrompt(e,t.turn):await buildPrompt(e,t.turn),a=await selectModel(e,i);if(!(`model`in a))return a.failed;let start=t=>e.apply(startStep(e.view(),{modelId:requireSessionModelReference(e.session).id}),t),o=projectPrompt(e,i);try{await start(o)}catch(t){return failBoundaryEvent(e,t)}if(r&&await dispatchApprovedWorkflows(e,t.approved))return{next:null,session:e.session};let s=await enforceBudget(e,i.messages);if(s!==void 0)return s;if(r){let n=await runApprovedLocalCalls(e,t.approved,t.setAttemptScope);if(n!==void 0)return n;i=await buildPrompt(e,t.turn),o=projectPrompt(e,i)}let c=!e.hasDelegatedCaller&&e.session.outputSchema===void 0,l=buildStepCatalog({agentTools:e.config.tools,ctx:e.ctx,endsTurn:c,session:e.session});recordOfferedCatalogTools(e.ctx,l),e.frameworkToolNames=frameworkToolNames(l);let{approvedTools:u,pendingApprovalsNote:d}=humanInputContext(e,l),f=new ModelCaller(e,i,{approvedTools:u,catalog:l,generation:n,hidesHeldText:t.hidesHeldText,model:a.model,pendingApprovalsNote:d,projectedMessages:o,setAttemptScope:t.setAttemptScope,startStep:start,turnMessages:t.turn.messages}),p;try{p=await f.call({suppressStepStartedEmission:!0})}catch(t){let r=await f.failIfCompactionFailed();if(r!==void 0)return r;if(throwIfTurnAborted(e.config.abortSignal),n.interrupted)return f.steered();let i=await recoverModelCall({call:e=>f.call(e),error:t,sessionId:e.session.sessionId,turnId:e.position().turnId}),a=await f.failIfCompactionFailed();if(a!==void 0)return a;if(throwIfTurnAborted(e.config.abortSignal),n.interrupted)return f.steered();if(!(`result`in i))return reportModelCallFailure(e,i.error);p=i.result}await recordModelUsage(e,{model:a.model,result:p}),f.clearInterruptedUsage();let m;try{n.check(),m=await t.onResponse({catalog:l,durableModelPromptMessageCount:i.clientContext===void 0||i.clientContext.messages.length===0?f.modelMessages.length:void 0,endsTurnTools:endsTurnTools(l,c),promptMessages:f.request.history,requestEnvelopeTokens:f.requestEnvelopeTokens,result:p})}catch(t){if(throwIfTurnAborted(e.config.abortSignal),n.interrupted)return f.steered();throw t}return e.ctx?.set(HistoryStateKey,f.request.historyState),m}async function selectModel(e,t){let{config:n,ctx:r}=e;try{if(r!==void 0&&n.dispatchDynamicModelEvent!==void 0){let i=e.position();await n.dispatchDynamicModelEvent({ctx:r,event:stepStartedForResolvers({modelId:e.session.agent.modelReference?.id??`dynamic`,sequence:i.sequence,stepIndex:nextStepIndex(e.view().projection),turnId:activeTurnId(i)}),messages:validateHarnessModelMessages(e.projectHistory(withClientContext(t)))})}let i=await resolveEffectiveRuntimeModel({config:n,ctx:r,session:e.session});return e.session=i.session,{model:i.model}}catch(t){return{failed:await failModelSelection(e,t)}}}export{runModelStep};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{
|
|
1
|
+
import{addStepUsage}from"#harness/session-machine/current.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{getTurnUsageState}from"#harness/turn-tag-state.js";import{setEveAttributes}from"#runtime/attributes/emit.js";function gatewayModelId(e){try{return formatLanguageModelGatewayId(e)}catch{return}}function extractTokenUsageDelta(e){let t=e.usage;if(t!==void 0||e.costUsd!==void 0)return{cacheReadTokens:t?.inputTokenDetails?.cacheReadTokens,cacheWriteTokens:t?.inputTokenDetails?.cacheWriteTokens,costUsd:e.costUsd,inputTokens:t?.inputTokens,outputTokens:t?.outputTokens}}function extractGatewayCostUsd(e){let t=readGatewayMetadata(e)?.cost;if(typeof t==`number`&&Number.isFinite(t))return t;if(typeof t==`string`){let e=Number(t);return Number.isFinite(e)?e:void 0}}function readGatewayMetadata(e){let t=e?.gateway;return t&&typeof t==`object`&&!Array.isArray(t)?t:void 0}async function recordModelUsage(e,t){e.session=addStepUsage(e.session,e.position().turnId,extractTokenUsageDelta({costUsd:extractGatewayCostUsd(t.result.providerMetadata),usage:t.result.usage}));let n=getTurnUsageState(e.session.state),r={"$eve.model":gatewayModelId(t.model),"$eve.input_tokens":n?.inputTokens,"$eve.output_tokens":n?.outputTokens,"$eve.cache_read_tokens":n?.cacheReadTokens,"$eve.cache_write_tokens":n?.cacheWriteTokens,"$eve.cost_usd":n?.sawCost?n.costUsd:void 0,"$eve.tool_count":e.config.tools.size};await e.config.titleAttributeWrite,await setEveAttributes(r)}export{extractGatewayCostUsd,extractTokenUsageDelta,gatewayModelId,recordModelUsage};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContextReader } from "#context/key.js";
|
|
2
|
+
import { type TokenUsageDelta } from "#harness/turn-tag-state.js";
|
|
2
3
|
import type { HarnessSessionBase, SessionStateMap } from "#harness/types.js";
|
|
3
4
|
import { type SessionProjection } from "#protocol/session-projection.js";
|
|
4
5
|
import type { MessageStreamEvent, UnstampedMessageStreamEvent } from "#protocol/message.js";
|
|
@@ -19,6 +20,13 @@ export declare function recordPublishedEvent(ctx: ContextReader, event: MessageS
|
|
|
19
20
|
export declare function recordAppliedSession(ctx: ContextReader, session: HarnessSessionBase): void;
|
|
20
21
|
/** The session the step's last applied transition produced, if it applied any. */
|
|
21
22
|
export declare function appliedSession<T extends HarnessSessionBase>(ctx: ContextReader): T | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Counts a model call's usage on `session` and on the session the step last applied. The provider
|
|
25
|
+
* already served the call, so a hook that cancels the turn before the step applies again keeps it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function addStepUsage<T extends {
|
|
28
|
+
readonly state?: SessionStateMap;
|
|
29
|
+
}>(session: T, turnId: string, usage: TokenUsageDelta | undefined): T;
|
|
22
30
|
export declare function saveSessionProjection<T extends HarnessSessionBase>(session: T, ctx: ContextReader): T;
|
|
23
31
|
/**
|
|
24
32
|
* The session saves the lifecycle its steps read. Calls and tasks are for the stream's readers,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_PROJECTION_STATE_KEY,storedProjection}from"./view.js";import{foldSession,pruneSessionProjection}from"#protocol/session-projection.js";import{contextStorage}from"#context/container.js";const LIVE_PROJECTIONS_GLOBAL_KEY=Symbol.for(`eve.live-session-projections`),liveProjections=globalThis[LIVE_PROJECTIONS_GLOBAL_KEY]??=new WeakMap;function enterSessionProjection(e,t){liveProjections.set(e,{projection:storedProjection(t)})}function ensureSessionProjection(e,t){liveProjections.has(e)||enterSessionProjection(e,t)}function liveProjection(e=contextStorage.getStore()){return e===void 0?void 0:liveProjections.get(e)?.projection}function currentProjection(e=contextStorage.getStore()){let t=e===void 0?void 0:liveProjections.get(e);if(t===void 0)throw Error(`Session lifecycle requires an initialized projection.`);return t.projection}function projectionFor(e,t){return liveProjections.get(e)?.projection??storedProjection(t)}function recordPublishedEvent(e,t){let n=liveProjections.get(e);if(n===void 0)throw Error(`Session publication requires an initialized projection.`);let r=foldSession(n.projection,t);n.projection=t.type===`session.waiting`?pruneSessionProjection(r):r}const appliedSessions=new WeakMap;function recordAppliedSession(e,t){appliedSessions.set(e,t)}function appliedSession(e){return appliedSessions.get(e)}function saveSessionProjection(e,t){return saveProjection(e,currentProjection(t))}function saveProjection(e,t){let n={...t,calls:{},tasks:{}};return{...e,state:{...e.state,[SESSION_PROJECTION_STATE_KEY]:n}}}function stepProjection(e,t){if(e!==void 0)return ensureSessionProjection(e,t),{read:()=>currentProjection(e),record:t=>recordPublishedEvent(e,t)};let n=storedProjection(t);return{read:()=>n,record(e){let t=foldSession(n,e);n=e.type===`session.waiting`?pruneSessionProjection(t):t}}}export{appliedSession,currentProjection,ensureSessionProjection,enterSessionProjection,liveProjection,projectionFor,recordAppliedSession,recordPublishedEvent,saveProjection,saveSessionProjection,stepProjection};
|
|
1
|
+
import{SESSION_PROJECTION_STATE_KEY,storedProjection}from"./view.js";import{foldSession,pruneSessionProjection}from"#protocol/session-projection.js";import{contextStorage}from"#context/container.js";import{addTurnUsage}from"#harness/turn-tag-state.js";const LIVE_PROJECTIONS_GLOBAL_KEY=Symbol.for(`eve.live-session-projections`),liveProjections=globalThis[LIVE_PROJECTIONS_GLOBAL_KEY]??=new WeakMap;function enterSessionProjection(e,t){liveProjections.set(e,{projection:storedProjection(t)})}function ensureSessionProjection(e,t){liveProjections.has(e)||enterSessionProjection(e,t)}function liveProjection(e=contextStorage.getStore()){return e===void 0?void 0:liveProjections.get(e)?.projection}function currentProjection(e=contextStorage.getStore()){let t=e===void 0?void 0:liveProjections.get(e);if(t===void 0)throw Error(`Session lifecycle requires an initialized projection.`);return t.projection}function projectionFor(e,t){return liveProjections.get(e)?.projection??storedProjection(t)}function recordPublishedEvent(e,t){let n=liveProjections.get(e);if(n===void 0)throw Error(`Session publication requires an initialized projection.`);let r=foldSession(n.projection,t);n.projection=t.type===`session.waiting`?pruneSessionProjection(r):r}const appliedSessions=new WeakMap;function recordAppliedSession(e,t){appliedSessions.set(e,t)}function appliedSession(e){return appliedSessions.get(e)}function addStepUsage(e,t,n){let r=contextStorage.getStore(),i=r===void 0?void 0:appliedSessions.get(r);return r!==void 0&&i!==void 0&&appliedSessions.set(r,addTurnUsage(i,t,n)),addTurnUsage(e,t,n)}function saveSessionProjection(e,t){return saveProjection(e,currentProjection(t))}function saveProjection(e,t){let n={...t,calls:{},tasks:{}};return{...e,state:{...e.state,[SESSION_PROJECTION_STATE_KEY]:n}}}function stepProjection(e,t){if(e!==void 0)return ensureSessionProjection(e,t),{read:()=>currentProjection(e),record:t=>recordPublishedEvent(e,t)};let n=storedProjection(t);return{read:()=>n,record(e){let t=foldSession(n,e);n=e.type===`session.waiting`?pruneSessionProjection(t):t}}}export{addStepUsage,appliedSession,currentProjection,ensureSessionProjection,enterSessionProjection,liveProjection,projectionFor,recordAppliedSession,recordPublishedEvent,saveProjection,saveSessionProjection,stepProjection};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const STAMPED_PACKAGE_VERSION=`0.83.
|
|
1
|
+
const STAMPED_PACKAGE_VERSION=`0.83.3`;function readStampedPackageVersion(){return STAMPED_PACKAGE_VERSION.startsWith(`__`)?void 0:STAMPED_PACKAGE_VERSION}export{readStampedPackageVersion};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Creates the Rollup-compatible plugin that gives authored modules a small
|
|
3
|
-
* asset-module surface for non-code relative imports
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* asset-module surface for non-code relative imports. An asset may sit outside
|
|
4
|
+
* the agent directory, but not outside the authored package or the package
|
|
5
|
+
* that owns the importing module, such as a workspace package bundled from
|
|
6
|
+
* source.
|
|
6
7
|
*/
|
|
7
8
|
export declare function createAuthoredAssetImportPlugin(input: {
|
|
8
9
|
readonly packageRoot: string;
|
|
9
10
|
/**
|
|
10
|
-
* Handles only importers and assets
|
|
11
|
-
*
|
|
12
|
-
* the Nitro server and the workflow driver, so
|
|
13
|
-
* the host bundler.
|
|
11
|
+
* Handles only importers and assets outside `node_modules`: authored source
|
|
12
|
+
* and workspace packages bundled from source. Set it for bundles that also
|
|
13
|
+
* bundle dependencies, such as the Nitro server and the workflow driver, so
|
|
14
|
+
* installed dependency imports stay with the host bundler.
|
|
14
15
|
*/
|
|
15
16
|
readonly authoredSourceOnly?: boolean;
|
|
16
17
|
}): Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFile}from"node:fs/promises";import{realpathSync}from"node:fs";import{dirname,extname,isAbsolute,relative,resolve}from"node:path";import{isNodeModulesPath}from"#internal/authored-package-boundary.js";const AUTHORED_ASSET_CODE_EXTENSIONS=[`.ts`,`.tsx`,`.mts`,`.cts`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`];function createAuthoredAssetImportPlugin(e){let t=toCanonicalPath(e.packageRoot),handles=
|
|
1
|
+
import{readFile}from"node:fs/promises";import{realpathSync}from"node:fs";import{dirname,extname,isAbsolute,relative,resolve}from"node:path";import{findAuthoredPackageRoot,isNodeModulesPath}from"#internal/authored-package-boundary.js";const AUTHORED_ASSET_CODE_EXTENSIONS=[`.ts`,`.tsx`,`.mts`,`.cts`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`];function createAuthoredAssetImportPlugin(e){let t=toCanonicalPath(e.packageRoot),handles=t=>e.authoredSourceOnly!==!0||isAbsolute(t)&&!isNodeModulesPath(t);return{name:`eve-authored-asset-import`,resolveId(e,n){if(!isPotentialAuthoredAssetImport(e)||n===void 0||!handles(n))return;let{path:r,suffix:i}=splitImportSuffix(e),a=toCanonicalPath(isAbsolute(r)?r:resolve(dirname(n),r)),o=findAuthoredPackageRoot(dirname(toCanonicalPath(n)))??t;if(!isPathInsideOrEqual(a,t)&&!isPathInsideOrEqual(a,o))throw Error(`Authored asset import "${e}" from "${n}" resolves outside package root "${o}".`);return`${a}${i}`},async load(e){let{path:t,suffix:n}=splitImportSuffix(e);if(!isPotentialAuthoredAssetImport(e)||!handles(t))return;if(n===`?raw`){let e=await readAssetText(t);return e===void 0?void 0:{code:`export default ${JSON.stringify(e)};`,moduleType:`js`}}let r=await readAssetDataUrl(t);if(r!==void 0)return{code:`export default ${JSON.stringify(r)};`,moduleType:`js`}}}}function isPathInsideOrEqual(e,t){let n=relative(t,resolve(e));return n===``||!n.startsWith(`..`)&&!isAbsolute(n)}function toCanonicalPath(e){let t=resolve(e);try{return realpathSync.native(t)}catch{return t}}async function readAssetText(e){try{return await readFile(e,`utf8`)}catch(e){if(isPathNotFoundError(e))return;throw e}}function isPotentialAuthoredAssetImport(e){if(e.startsWith(`\0`)||e.startsWith(`node:`)||e.startsWith(`data:`)||e.startsWith(`file:`))return!1;let{path:t,suffix:n}=splitImportSuffix(e);if(!t.startsWith(`.`)&&!t.startsWith(`/`)&&!/^[A-Za-z]:[\\/]/.test(t))return!1;if(n===`?raw`)return!0;let r=extname(t);return r.length>0&&r!==`.node`&&!AUTHORED_ASSET_CODE_EXTENSIONS.includes(r)}function splitImportSuffix(e){let t=e.indexOf(`?`),n=e.indexOf(`#`),r=t===-1?n:n===-1?t:Math.min(t,n);return r===-1?{path:e,suffix:``}:{path:e.slice(0,r),suffix:e.slice(r)}}async function readAssetDataUrl(e){try{let t=await readFile(e);return`data:${getAssetMimeType(e)};base64,${t.toString(`base64`)}`}catch(e){if(isPathNotFoundError(e))return;throw e}}function isPathNotFoundError(e){return typeof e==`object`&&!!e&&`code`in e&&(e.code===`ENOENT`||e.code===`ENOTDIR`)}function getAssetMimeType(e){switch(extname(e).toLowerCase()){case`.css`:return`text/css`;case`.csv`:return`text/csv`;case`.gif`:return`image/gif`;case`.html`:return`text/html`;case`.jpg`:case`.jpeg`:return`image/jpeg`;case`.png`:return`image/png`;case`.svg`:return`image/svg+xml`;case`.txt`:return`text/plain`;case`.wasm`:return`application/wasm`;case`.webp`:return`image/webp`;default:return`application/octet-stream`}}export{createAuthoredAssetImportPlugin};
|
|
@@ -15,6 +15,18 @@ export declare function stampDevelopmentClientAddress(headers: Headers | Incomin
|
|
|
15
15
|
* proxied dev topology is the parent's loopback hop.
|
|
16
16
|
*/
|
|
17
17
|
export declare function readTrustedDevelopmentClientAddress(headers: Headers, secret: string | undefined): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Where the dev listener saw a request come from: `"local"` for this machine,
|
|
20
|
+
* `"remote"` for another one, or `undefined` when the request carries no
|
|
21
|
+
* verified stamp, as outside `eve dev`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function readTrustedDevelopmentPeer(headers: Headers): "local" | "remote" | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Whether a socket peer address or URL host name belongs to this machine. With `--host` bound
|
|
26
|
+
* to a LAN address, the developer's own clients connect from that address
|
|
27
|
+
* rather than loopback.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isLocalMachineAddress(address: string): boolean;
|
|
18
30
|
/** Verifies parent-stamped client provenance carried beyond the original request. */
|
|
19
31
|
export declare function isTrustedDevelopmentClientAddress(address: string, signature: string, secret: string | undefined): boolean;
|
|
20
32
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,createHmac,timingSafeEqual}from"node:crypto";const DEVELOPMENT_CLIENT_ADDRESS_HEADER=`x-eve-dev-client-address`,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER=`x-eve-dev-client-address-signature`;function stampDevelopmentClientAddress(e,t,n){deleteHeader(e,DEVELOPMENT_CLIENT_ADDRESS_HEADER),deleteHeader(e,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER),t!==void 0&&t.length!==0&&n!==void 0&&n.length!==0&&(setHeader(e,DEVELOPMENT_CLIENT_ADDRESS_HEADER,t),setHeader(e,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER,signClientAddress(t,n)))}function readTrustedDevelopmentClientAddress(e,t){let n=e.get(DEVELOPMENT_CLIENT_ADDRESS_HEADER),r=e.get(DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER);return n!==null&&r!==null&&isTrustedDevelopmentClientAddress(n,r,t)?n:void 0}function isTrustedDevelopmentClientAddress(e,t,n){return e.length>0&&t.length>0&&n!==void 0&&n.length>0&&timingSafeEqualStrings(t,signClientAddress(e,n))}function timingSafeEqualStrings(e,t){let n=createHash(`sha256`).update(e).digest(),r=createHash(`sha256`).update(t).digest();return timingSafeEqual(n,r)}function signClientAddress(e,t){return createHmac(`sha256`,t).update(e).digest(`base64url`)}function deleteHeader(e,t){if(e instanceof Headers){e.delete(t);return}delete e[t]}function setHeader(e,t,n){if(e instanceof Headers){e.set(t,n);return}e[t]=n}export{DEVELOPMENT_CLIENT_ADDRESS_HEADER,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER,isTrustedDevelopmentClientAddress,readTrustedDevelopmentClientAddress,stampDevelopmentClientAddress,timingSafeEqualStrings};
|
|
1
|
+
import{createHash,createHmac,timingSafeEqual}from"node:crypto";import{isLoopbackHostname}from"#shared/network-address.js";import{networkInterfaces}from"node:os";import{DEVELOPMENT_WORKFLOW_SECRET_ENV}from"#internal/workflow/development-world-protocol.js";const DEVELOPMENT_CLIENT_ADDRESS_HEADER=`x-eve-dev-client-address`,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER=`x-eve-dev-client-address-signature`;function stampDevelopmentClientAddress(e,t,n){deleteHeader(e,DEVELOPMENT_CLIENT_ADDRESS_HEADER),deleteHeader(e,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER),t!==void 0&&t.length!==0&&n!==void 0&&n.length!==0&&(setHeader(e,DEVELOPMENT_CLIENT_ADDRESS_HEADER,t),setHeader(e,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER,signClientAddress(t,n)))}function readTrustedDevelopmentClientAddress(e,t){let n=e.get(DEVELOPMENT_CLIENT_ADDRESS_HEADER),r=e.get(DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER);return n!==null&&r!==null&&isTrustedDevelopmentClientAddress(n,r,t)?n:void 0}function readTrustedDevelopmentPeer(e){let t=readTrustedDevelopmentClientAddress(e,process.env[DEVELOPMENT_WORKFLOW_SECRET_ENV]);if(t!==void 0)return isLocalMachineAddress(t)?`local`:`remote`}function isLocalMachineAddress(e){if(isLoopbackHostname(e))return!0;let t=e.toLowerCase().replace(/^\[(.*)\]$/u,`$1`).replace(/^::ffff:/u,``);return Object.values(networkInterfaces()).some(e=>e?.some(e=>e.address.toLowerCase()===t))}function isTrustedDevelopmentClientAddress(e,t,n){return e.length>0&&t.length>0&&n!==void 0&&n.length>0&&timingSafeEqualStrings(t,signClientAddress(e,n))}function timingSafeEqualStrings(e,t){let n=createHash(`sha256`).update(e).digest(),r=createHash(`sha256`).update(t).digest();return timingSafeEqual(n,r)}function signClientAddress(e,t){return createHmac(`sha256`,t).update(e).digest(`base64url`)}function deleteHeader(e,t){if(e instanceof Headers){e.delete(t);return}delete e[t]}function setHeader(e,t,n){if(e instanceof Headers){e.set(t,n);return}e[t]=n}export{DEVELOPMENT_CLIENT_ADDRESS_HEADER,DEVELOPMENT_CLIENT_ADDRESS_SIGNATURE_HEADER,isLocalMachineAddress,isTrustedDevelopmentClientAddress,readTrustedDevelopmentClientAddress,readTrustedDevelopmentPeer,stampDevelopmentClientAddress,timingSafeEqualStrings};
|
|
@@ -6,9 +6,12 @@ export interface ChannelRequestTrace {
|
|
|
6
6
|
channelKind?: string;
|
|
7
7
|
}): void;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The request span ends when the response body finishes, errors, or is
|
|
11
|
+
* cancelled, not when the handler returns.
|
|
12
|
+
*/
|
|
13
|
+
export declare function traceChannelRequest(input: {
|
|
11
14
|
readonly request: Request;
|
|
12
15
|
readonly routeKey: string;
|
|
13
|
-
}, handler: (operation: ChannelRequestTrace | undefined) => Promise<
|
|
16
|
+
}, handler: (operation: ChannelRequestTrace | undefined) => Promise<Response>): Promise<Response>;
|
|
14
17
|
export declare function traceConnectionEventRequest(request: Request, handler: (span: Span | undefined) => Promise<Response>): Promise<Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SpanKind,SpanStatusCode,context,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{getInstrumentationRuntime}from"#instrumentation/runtime.js";import{markAgentTraceContext}from"#tracing/eve/agent-trace-context.js";import{withErrorContent}from"#compiled/@vercel/agent-runs/otel.js";import{connectionEventDestination}from"#runtime/connections/events/path.js";const headersGetter={get:(e,t)=>e.get(t)??void 0,keys:e=>[...e.keys()]};async function traceChannelRequest(e,t){return traceRequest({...e,spanName:`agent.channel.request`},e=>t(e===void 0?void 0:{reference:e.spanContext(),channel(t){t.channelName!==void 0&&(e.setAttribute(`agent.channel.name`,t.channelName),e.setAttribute(`eve.channel.name`,t.channelName)),t.channelKind!==void 0&&(e.setAttribute(`agent.channel.kind`,t.channelKind),e.setAttribute(`eve.channel.kind`,t.channelKind))}}))}async function traceConnectionEventRequest(e,t){return traceRequest({request:e,routeKey:`POST ${connectionEventDestination(`:connectionName`).replace(`%3AconnectionName`,`:connectionName`)}`,spanName:`agent.connection.event.request`},t)}async function traceRequest(e,t){if(getInstrumentationRuntime()?.otelSettings?.traceChannelRequests!==!0)return t(void 0);let{request:n,routeKey:r}=e,i=propagation.extract(context.active(),n.headers,headersGetter),a=r.indexOf(` `),o;try{o=new URL(n.url)}catch{}let s=trace.getTracer(`eve.channel`).startSpan(e.spanName,{kind:SpanKind.SERVER,attributes:{"operation.name":e.spanName,"resource.name":e.spanName,"http.request.method":n.method,"http.route":a===-1?r:r.slice(a+1),"url.scheme":o?.protocol.replace(/:$/,``),"server.address":o?.hostname}},i);try{let e=await context.with(markAgentTraceContext(withErrorContent(trace.setSpan(i,s),!1)),()=>t(s));return s.setAttribute(`http.response.status_code`,e.status),e.status>=500&&s.setStatus({code:SpanStatusCode.ERROR}),e}catch(e){throw s.setStatus({code:SpanStatusCode.ERROR}),e}
|
|
1
|
+
import{SpanKind,SpanStatusCode,context,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{getInstrumentationRuntime}from"#instrumentation/runtime.js";import{markAgentTraceContext}from"#tracing/eve/agent-trace-context.js";import{withErrorContent}from"#compiled/@vercel/agent-runs/otel.js";import{connectionEventDestination}from"#runtime/connections/events/path.js";const headersGetter={get:(e,t)=>e.get(t)??void 0,keys:e=>[...e.keys()]};async function traceChannelRequest(e,t){return traceRequest({...e,spanName:`agent.channel.request`},e=>t(e===void 0?void 0:{reference:e.spanContext(),channel(t){t.channelName!==void 0&&(e.setAttribute(`agent.channel.name`,t.channelName),e.setAttribute(`eve.channel.name`,t.channelName)),t.channelKind!==void 0&&(e.setAttribute(`agent.channel.kind`,t.channelKind),e.setAttribute(`eve.channel.kind`,t.channelKind))}}))}async function traceConnectionEventRequest(e,t){return traceRequest({request:e,routeKey:`POST ${connectionEventDestination(`:connectionName`).replace(`%3AconnectionName`,`:connectionName`)}`,spanName:`agent.connection.event.request`},t)}async function traceRequest(e,t){if(getInstrumentationRuntime()?.otelSettings?.traceChannelRequests!==!0)return t(void 0);let{request:n,routeKey:r}=e,i=propagation.extract(context.active(),n.headers,headersGetter),a=r.indexOf(` `),o;try{o=new URL(n.url)}catch{}let s=trace.getTracer(`eve.channel`).startSpan(e.spanName,{kind:SpanKind.SERVER,attributes:{"operation.name":e.spanName,"resource.name":e.spanName,"http.request.method":n.method,"http.route":a===-1?r:r.slice(a+1),"url.scheme":o?.protocol.replace(/:$/,``),"server.address":o?.hostname}},i);try{let e=await context.with(markAgentTraceContext(withErrorContent(trace.setSpan(i,s),!1)),()=>t(s));return s.setAttribute(`http.response.status_code`,e.status),e.status>=500&&s.setStatus({code:SpanStatusCode.ERROR}),endWithBody(e,()=>s.end())}catch(e){throw s.setStatus({code:SpanStatusCode.ERROR}),s.end(),e}}function endWithBody(e,t){if(e.body===null)return t(),e;let{readable:n,writable:r}=new TransformStream;return e.body.pipeTo(r).then(t,t),new Response(n,e)}export{traceChannelRequest,traceConnectionEventRequest};
|
|
@@ -347,6 +347,20 @@ export declare function none<TEvent = unknown>(): AuthFn<TEvent>;
|
|
|
347
347
|
* deployment (`eve start`, a Vercel deployment, or any container host) sets
|
|
348
348
|
* neither flag, so `localDev()` authenticates nothing there. Layer a real
|
|
349
349
|
* authenticator on those hosts.
|
|
350
|
+
*
|
|
351
|
+
* On a development server, `localDev()` also refuses requests a browser sent
|
|
352
|
+
* on behalf of another site: a request addressed to a host that is neither
|
|
353
|
+
* loopback nor one of this machine's addresses, or one whose `Origin` is not. Browsers set both themselves and
|
|
354
|
+
* send `Origin` on every cross-origin request that can change state, so a web
|
|
355
|
+
* page cannot use the developer's browser to reach the local agent, including
|
|
356
|
+
* through DNS rebinding. Frontends on any loopback host and port, or on this
|
|
357
|
+
* machine's own address, still authenticate.
|
|
358
|
+
*
|
|
359
|
+
* On `eve dev`, `localDev()` also refuses callers whose connection comes from
|
|
360
|
+
* another machine, such as LAN clients of a server started with
|
|
361
|
+
* `--host 0.0.0.0`. A tunnel or reverse proxy on this machine still forwards
|
|
362
|
+
* its callers as `local-dev`, so expose a development server that way only
|
|
363
|
+
* behind an authenticator that runs before `localDev()`.
|
|
350
364
|
*/
|
|
351
365
|
export declare function localDev(): AuthFn<Request>;
|
|
352
366
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{decodeJwt}from"#compiled/jose/index.js";import{areTokenClaimMatchersSatisfied}from"#channel/auth/token-claims.js";import{isVercelOidcIssuer}from"#shared/vercel-project.js";import{isLoopbackHostname}from"#shared/network-address.js";import{isEveDevEnvironment}from"#internal/application/dev-environment.js";import{ANONYMOUS_SESSION_AUTH_CONTEXT}from"#channel/principal-identity.js";import{resolveVercelOidcCurrentProject}from"#channel/auth/vercel-oidc-project.js";import{authenticateHttpBasicStrategy}from"#channel/auth/http-basic.js";import{authenticateJwtEcdsaStrategy}from"#channel/auth/jwt-ecdsa.js";import{authenticateJwtHmacStrategy}from"#channel/auth/jwt-hmac.js";import{authenticateOidcStrategy}from"#channel/auth/oidc.js";import{createRuntimeSessionAuthContext}from"#channel/auth/types.js";import{createRuntimeIpAllowList,isRuntimeIpAllowed}from"#channel/ip-allow-list.js";const vercelOidcLog=createLogger(`auth.vercel-oidc`);function verifyHttpBasic(e,t){if(e===null)return{ok:!1};let n=authenticateHttpBasicStrategy({authorization:e,strategy:{kind:`http-basic`,password:t.password,username:t.username}});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function verifyJwtHmac(e,t){if(e===null||e.length===0)return{ok:!1};let n={algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-hmac`,secret:t.secret,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},r=await authenticateJwtHmacStrategy({strategy:n,token:e});return r.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}:{ok:!1}}async function verifyJwtEcdsa(e,t){if(e===null||e.length===0)return{ok:!1};let n={algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-ecdsa`,publicKey:t.publicKey,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},r=await authenticateJwtEcdsaStrategy({strategy:n,token:e});return r.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}:{ok:!1}}async function verifyOidc(e,t){let n=await runOidcVerification(e,{...t,acceptCurrentVercelProject:!1,currentVercelProject:void 0});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function runOidcVerification(e,t){if(e===null||e.length===0)return{kind:`not-authenticated`};let n={acceptCurrentVercelProject:t.acceptCurrentVercelProject,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,discoveryUrl:t.discoveryUrl??`${t.issuer.replace(/\/$/,``)}/.well-known/openid-configuration`,issuer:t.issuer,kind:`oidc`,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},r=t.currentVercelProject===void 0?n:{...n,currentVercelProject:t.currentVercelProject};return await authenticateOidcStrategy({strategy:r,token:e})}function extractBearerToken(e){if(e===null)return null;let t=/^Bearer\s+(.+)$/i.exec(e)?.[1]?.trim();return t===void 0||t.length===0?null:t}function createIpAllowList(e){return createRuntimeIpAllowList(e)}function isIpAllowed(e,t){return e!==null&&isRuntimeIpAllowed(e,t)}function createUnauthorizedResponse(e={}){let t=e.status??401,n=e.code??(t===403?`forbidden`:`unauthorized`),r=e.message??(t===403?`Forbidden.`:`Authorization is required for this route.`),i=e.challenges??[],a=new Headers({"cache-control":`no-store`});for(let e of i)a.append(`www-authenticate`,formatChallenge(e));return Response.json({code:n,error:r,ok:!1},{headers:a,status:t})}function formatChallenge(e){if(e.parameters===void 0||Object.keys(e.parameters).length===0)return e.scheme;let t=Object.entries(e.parameters).map(([e,t])=>`${e}="${escapeAuthChallengeParameter(t)}"`).join(`, `);return`${e.scheme} ${t}`}function escapeAuthChallengeParameter(e){return e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`)}function isValidOAuthIdentifierUrl(e){try{let t=new URL(e);return(t.protocol===`https:`||t.protocol===`http:`&&isLoopbackHostname(t.hostname))&&t.username.length===0&&t.password.length===0&&t.search.length===0&&t.hash.length===0}catch{return!1}}function isValidMetadataPath(e){if(!e.startsWith(`/`)||e.startsWith(`//`))return!1;let t=new URL(e,`https://eve.invalid`);return t.origin===`https://eve.invalid`&&t.search.length===0&&t.hash.length===0}var UnauthenticatedError=class extends Error{response;constructor(e={}){super(e.message??`Authorization is required for this route.`),this.name=`UnauthenticatedError`,this.response=createUnauthorizedResponse({...e,status:401})}},ForbiddenError=class extends Error{response;constructor(e={}){super(e.message??`Forbidden.`),this.name=`ForbiddenError`,this.response=createUnauthorizedResponse({...e,status:403})}};const OAUTH_RESOURCE_SYMBOL=Symbol.for(`eve.channels.auth.oauthResource`);function oauthResource(e,t){let n=t.issuer===void 0?t.authorizationServers:[t.issuer];if(n.length===0||n.some(e=>!isValidOAuthIdentifierUrl(e)))throw Error(`oauthResource requires at least one HTTPS authorization server URL (HTTP is allowed only on loopback).`);if(t.resource!==void 0&&!isValidOAuthIdentifierUrl(t.resource))throw Error(`oauthResource resource must be an HTTPS URL without credentials, query, or fragment (HTTP is allowed only on loopback).`);if(t.metadataPath!==void 0&&!isValidMetadataPath(t.metadataPath))throw Error(`oauthResource metadataPath must be an absolute path without a host, query, or fragment.`);let r=Array.isArray(e)?e:[e],composed=async e=>{for(let t of r){let n=await t(e);if(n)return n}return null},i=collectDeclaredChallenges(r),a=withAuthChallenges(composed,i.length>0?i:[{scheme:`Bearer`}]);return Object.defineProperty(a,OAUTH_RESOURCE_SYMBOL,{enumerable:!1,value:t}),a}function readOAuthResourceOptions(e){if(!Array.isArray(e))return e[OAUTH_RESOURCE_SYMBOL]}const AUTH_CHALLENGES_SYMBOL=Symbol.for(`eve.channels.auth.challenges`);function withAuthChallenges(e,t){let wrapped=t=>e(t);return Object.defineProperty(wrapped,AUTH_CHALLENGES_SYMBOL,{value:t}),wrapped}function getDeclaredChallenges(e){return e[AUTH_CHALLENGES_SYMBOL]}function collectDeclaredChallenges(e){let t=new Set,n=[];for(let r of e)for(let e of getDeclaredChallenges(r)??[]){let r=formatChallenge(e);t.has(r)||(t.add(r),n.push(e))}return n}async function routeAuth(e,t){let n=Array.isArray(t)?t:[t];try{for(let t of n){let n=await t(e);if(n)return n}}catch(e){if(typeof e==`object`&&e&&`response`in e&&e.response instanceof Response)return e.response;throw e}let r=collectDeclaredChallenges(n);return createUnauthorizedResponse({challenges:r.length>0?addInvalidBearerTokenError(e,r):[{scheme:`Bearer`}]})}function addInvalidBearerTokenError(e,t){let n=e.headers.get(`authorization`);return n===null||!/^Bearer(?:\s|$)/i.test(n.trim())||!t.some(e=>e.scheme===`Bearer`)?t:t.map(e=>e.scheme!==`Bearer`||e.parameters?.error!==void 0?e:{...e,parameters:{...e.parameters,error:`invalid_token`}})}function placeholderAuth(){return()=>{if(process.env.VERCEL_ENV!==`production`)return null;throw new UnauthenticatedError({code:`eve_production_auth_not_configured`,message:`Production auth is not configured. Replace placeholderAuth() in agent/channels/eve.ts with your app's auth provider.`})}}function none(){return()=>ANONYMOUS_SESSION_AUTH_CONTEXT}function localDev(){return()=>isLocalDevelopmentServer()?LOCAL_DEV_SESSION_AUTH_CONTEXT:null}function isLocalDevelopmentServer(){return process.env.VERCEL&&process.env.VERCEL_ENV===`development`?!0:isEveDevEnvironment()}const LOCAL_DEV_SESSION_AUTH_CONTEXT={attributes:{},authenticator:`local-dev`,principalId:`local-dev`,principalType:`local-dev`};async function verifyVercelOidc(e,t={}){if(e===null||e.length===0)return{ok:!1};let n=decodeUnverifiedJwtClaims(e);if(n===null)return vercelOidcLog.debug(`Rejected token that failed to decode as a JWT.`),{ok:!1};if(!isVercelOidcIssuer(n.issuer))return vercelOidcLog.debug(`Rejected token whose issuer is not a Vercel OIDC issuer.`,{issuer:n.issuer}),{ok:!1};if(n.audiences.length===0)return vercelOidcLog.debug(`Rejected token with no audience claim.`,{issuer:n.issuer}),{ok:!1};if(!n.audiences.some(e=>e.startsWith(`https://vercel.com/`)))return vercelOidcLog.debug(`Rejected token whose audience is not a Vercel audience.`,{audiences:n.audiences,issuer:n.issuer}),{ok:!1};let r=resolveCurrentVercelProject(t),i=await runOidcVerification(e,{acceptCurrentVercelProject:!0,audiences:n.audiences,currentVercelProject:r,issuer:n.issuer,subjects:t.subjects??[]});return i.kind===`authenticated`?(vercelOidcLog.debug(`Accepted Vercel OIDC token.`,{issuer:n.issuer,principalType:i.principal.principalType,subject:i.principal.subject}),{ok:!0,sessionAuth:createRuntimeSessionAuthContext(i.principal)}):(vercelOidcLog.debug(`Rejected Vercel OIDC token after verification.`,{audiences:n.audiences,issuer:n.issuer,reason:i.kind,subjectsConfigured:(t.subjects??[]).length>0,...i.kind===`misconfigured`?{detail:i.message}:{}}),{ok:!1})}function resolveCurrentVercelProject(e){if(e.currentVercelProject!==void 0)return e.currentVercelProject;let t=process.env.VERCEL_PROJECT_ID?.trim();if(t===void 0||t.length===0)return;let n=process.env.VERCEL_TARGET_ENV?.trim()||process.env.VERCEL_ENV?.trim();return n===void 0||n.length===0?{projectId:t}:{environment:n,projectId:t}}function vercelSubject(e){assertVercelSubjectSegment(`teamSlug`,e.teamSlug),assertVercelSubjectSegment(`projectName`,e.projectName);let t=e.environment??`production`;if(t!==`production`&&t!==`preview`&&t!==`development`&&t!==`*`)throw Error(`vercelSubject: invalid environment ${JSON.stringify(t)}; expected "production", "preview", "development", or "*".`);return`owner:${e.teamSlug}:project:${e.projectName}:environment:${t}`}function assertVercelSubjectSegment(e,t){if(t.length===0)throw Error(`vercelSubject: ${e} must be a non-empty string.`);if(t.includes(`*`)||t.includes(`:`))throw Error(`vercelSubject: ${e} ${JSON.stringify(t)} may not contain ${t.includes(`:`)?`':'`:`'*'`}. Hand-write the subject string when wildcards are intentional.`)}function vercelOidc(e={}){let{subjects:t,...n}=e,r={...n,subjects:t?.map(e=>typeof e==`string`?e:e.subject)};return withAuthChallenges(async n=>{let i=extractBearerToken(n.headers.get(`authorization`)),a=e.currentVercelProject??(i!==null&&isLocalDevelopmentVercelOidc()?await resolveVercelOidcCurrentProject(n):void 0),o=await verifyVercelOidc(i,a===void 0?r:{...r,currentVercelProject:a});if(!o.ok)return null;let s=o.sessionAuth;return(s.principalType===`service`||s.principalType===`runtime`)&&t?.some(e=>typeof e!=`string`&&e.allowToolStubs===!0&&areTokenClaimMatchersSatisfied({sub:s.subject},{subjects:[e.subject]}))?{...s,allowToolStubs:!0}:s},[{scheme:`Bearer`}])}function isLocalDevelopmentVercelOidc(){return process.env.VERCEL_ENV===`development`?!0:process.env.VERCEL_ENV===`preview`||process.env.VERCEL_ENV===`production`?!1:isEveDevEnvironment()}function httpBasic(e,t){return withAuthChallenges(t=>{let n=verifyHttpBasic(t.headers.get(`authorization`),e);return n.ok?n.sessionAuth:null},[{parameters:{realm:t?.realm??`eve`,charset:`UTF-8`},scheme:`Basic`}])}function jwtHmac(e){return withAuthChallenges(async t=>{let n=await verifyJwtHmac(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null},[{scheme:`Bearer`}])}function jwtEcdsa(e){return withAuthChallenges(async t=>{let n=await verifyJwtEcdsa(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null},[{scheme:`Bearer`}])}function oidc(e){return withAuthChallenges(async t=>{let n=await verifyOidc(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null},[{scheme:`Bearer`}])}function decodeUnverifiedJwtClaims(e){let t;try{t=decodeJwt(e)}catch{return null}return typeof t.iss!=`string`||t.iss.length===0?null:{audiences:typeof t.aud==`string`?[t.aud]:Array.isArray(t.aud)?t.aud.filter(e=>typeof e==`string`):[],issuer:t.iss}}export{ForbiddenError,UnauthenticatedError,createIpAllowList,createUnauthorizedResponse,escapeAuthChallengeParameter,extractBearerToken,httpBasic,isIpAllowed,jwtEcdsa,jwtHmac,localDev,none,oauthResource,oidc,placeholderAuth,readOAuthResourceOptions,routeAuth,vercelOidc,vercelSubject,verifyHttpBasic,verifyJwtEcdsa,verifyJwtHmac,verifyOidc,verifyVercelOidc,withAuthChallenges};
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{decodeJwt}from"#compiled/jose/index.js";import{areTokenClaimMatchersSatisfied}from"#channel/auth/token-claims.js";import{isVercelOidcIssuer}from"#shared/vercel-project.js";import{isLoopbackHostname}from"#shared/network-address.js";import{isEveDevEnvironment}from"#internal/application/dev-environment.js";import{ANONYMOUS_SESSION_AUTH_CONTEXT}from"#channel/principal-identity.js";import{isLocalMachineAddress,readTrustedDevelopmentPeer}from"#internal/nitro/dev-client-address.js";import{resolveVercelOidcCurrentProject}from"#channel/auth/vercel-oidc-project.js";import{authenticateHttpBasicStrategy}from"#channel/auth/http-basic.js";import{authenticateJwtEcdsaStrategy}from"#channel/auth/jwt-ecdsa.js";import{authenticateJwtHmacStrategy}from"#channel/auth/jwt-hmac.js";import{authenticateOidcStrategy}from"#channel/auth/oidc.js";import{createRuntimeSessionAuthContext}from"#channel/auth/types.js";import{createRuntimeIpAllowList,isRuntimeIpAllowed}from"#channel/ip-allow-list.js";const vercelOidcLog=createLogger(`auth.vercel-oidc`);function verifyHttpBasic(e,t){if(e===null)return{ok:!1};let n=authenticateHttpBasicStrategy({authorization:e,strategy:{kind:`http-basic`,password:t.password,username:t.username}});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function verifyJwtHmac(e,t){if(e===null||e.length===0)return{ok:!1};let n={algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-hmac`,secret:t.secret,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},r=await authenticateJwtHmacStrategy({strategy:n,token:e});return r.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}:{ok:!1}}async function verifyJwtEcdsa(e,t){if(e===null||e.length===0)return{ok:!1};let n={algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-ecdsa`,publicKey:t.publicKey,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},r=await authenticateJwtEcdsaStrategy({strategy:n,token:e});return r.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}:{ok:!1}}async function verifyOidc(e,t){let n=await runOidcVerification(e,{...t,acceptCurrentVercelProject:!1,currentVercelProject:void 0});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function runOidcVerification(e,t){if(e===null||e.length===0)return{kind:`not-authenticated`};let n={acceptCurrentVercelProject:t.acceptCurrentVercelProject,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,discoveryUrl:t.discoveryUrl??`${t.issuer.replace(/\/$/,``)}/.well-known/openid-configuration`,issuer:t.issuer,kind:`oidc`,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},r=t.currentVercelProject===void 0?n:{...n,currentVercelProject:t.currentVercelProject};return await authenticateOidcStrategy({strategy:r,token:e})}function extractBearerToken(e){if(e===null)return null;let t=/^Bearer\s+(.+)$/i.exec(e)?.[1]?.trim();return t===void 0||t.length===0?null:t}function createIpAllowList(e){return createRuntimeIpAllowList(e)}function isIpAllowed(e,t){return e!==null&&isRuntimeIpAllowed(e,t)}function createUnauthorizedResponse(e={}){let t=e.status??401,n=e.code??(t===403?`forbidden`:`unauthorized`),r=e.message??(t===403?`Forbidden.`:`Authorization is required for this route.`),i=e.challenges??[],a=new Headers({"cache-control":`no-store`});for(let e of i)a.append(`www-authenticate`,formatChallenge(e));return Response.json({code:n,error:r,ok:!1},{headers:a,status:t})}function formatChallenge(e){if(e.parameters===void 0||Object.keys(e.parameters).length===0)return e.scheme;let t=Object.entries(e.parameters).map(([e,t])=>`${e}="${escapeAuthChallengeParameter(t)}"`).join(`, `);return`${e.scheme} ${t}`}function escapeAuthChallengeParameter(e){return e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`)}function isValidOAuthIdentifierUrl(e){try{let t=new URL(e);return(t.protocol===`https:`||t.protocol===`http:`&&isLoopbackHostname(t.hostname))&&t.username.length===0&&t.password.length===0&&t.search.length===0&&t.hash.length===0}catch{return!1}}function isValidMetadataPath(e){if(!e.startsWith(`/`)||e.startsWith(`//`))return!1;let t=new URL(e,`https://eve.invalid`);return t.origin===`https://eve.invalid`&&t.search.length===0&&t.hash.length===0}var UnauthenticatedError=class extends Error{response;constructor(e={}){super(e.message??`Authorization is required for this route.`),this.name=`UnauthenticatedError`,this.response=createUnauthorizedResponse({...e,status:401})}},ForbiddenError=class extends Error{response;constructor(e={}){super(e.message??`Forbidden.`),this.name=`ForbiddenError`,this.response=createUnauthorizedResponse({...e,status:403})}};const OAUTH_RESOURCE_SYMBOL=Symbol.for(`eve.channels.auth.oauthResource`);function oauthResource(e,t){let n=t.issuer===void 0?t.authorizationServers:[t.issuer];if(n.length===0||n.some(e=>!isValidOAuthIdentifierUrl(e)))throw Error(`oauthResource requires at least one HTTPS authorization server URL (HTTP is allowed only on loopback).`);if(t.resource!==void 0&&!isValidOAuthIdentifierUrl(t.resource))throw Error(`oauthResource resource must be an HTTPS URL without credentials, query, or fragment (HTTP is allowed only on loopback).`);if(t.metadataPath!==void 0&&!isValidMetadataPath(t.metadataPath))throw Error(`oauthResource metadataPath must be an absolute path without a host, query, or fragment.`);let r=Array.isArray(e)?e:[e],composed=async e=>{for(let t of r){let n=await t(e);if(n)return n}return null},i=collectDeclaredChallenges(r),a=withAuthChallenges(composed,i.length>0?i:[{scheme:`Bearer`}]);return Object.defineProperty(a,OAUTH_RESOURCE_SYMBOL,{enumerable:!1,value:t}),a}function readOAuthResourceOptions(e){if(!Array.isArray(e))return e[OAUTH_RESOURCE_SYMBOL]}const AUTH_CHALLENGES_SYMBOL=Symbol.for(`eve.channels.auth.challenges`);function withAuthChallenges(e,t){let wrapped=t=>e(t);return Object.defineProperty(wrapped,AUTH_CHALLENGES_SYMBOL,{value:t}),wrapped}function getDeclaredChallenges(e){return e[AUTH_CHALLENGES_SYMBOL]}function collectDeclaredChallenges(e){let t=new Set,n=[];for(let r of e)for(let e of getDeclaredChallenges(r)??[]){let r=formatChallenge(e);t.has(r)||(t.add(r),n.push(e))}return n}async function routeAuth(e,t){let n=Array.isArray(t)?t:[t];try{for(let t of n){let n=await t(e);if(n)return n}}catch(e){if(typeof e==`object`&&e&&`response`in e&&e.response instanceof Response)return e.response;throw e}let r=collectDeclaredChallenges(n);return createUnauthorizedResponse({challenges:r.length>0?addInvalidBearerTokenError(e,r):[{scheme:`Bearer`}]})}function addInvalidBearerTokenError(e,t){let n=e.headers.get(`authorization`);return n===null||!/^Bearer(?:\s|$)/i.test(n.trim())||!t.some(e=>e.scheme===`Bearer`)?t:t.map(e=>e.scheme!==`Bearer`||e.parameters?.error!==void 0?e:{...e,parameters:{...e.parameters,error:`invalid_token`}})}function placeholderAuth(){return()=>{if(process.env.VERCEL_ENV!==`production`)return null;throw new UnauthenticatedError({code:`eve_production_auth_not_configured`,message:`Production auth is not configured. Replace placeholderAuth() in agent/channels/eve.ts with your app's auth provider.`})}}function none(){return()=>ANONYMOUS_SESSION_AUTH_CONTEXT}function localDev(){return e=>isLocalDevelopmentServer()&&!isCrossSiteBrowserRequest(e)&&!isFromRemotePeer(e)?LOCAL_DEV_SESSION_AUTH_CONTEXT:null}function isCrossSiteBrowserRequest(e){if(!isLocalMachineAddress(new URL(e.url).hostname))return!0;let t=e.headers.get(`origin`);if(t===null)return!1;let n=URL.parse(t)?.hostname;return n===void 0||!isLocalMachineAddress(n)}function isFromRemotePeer(e){return readTrustedDevelopmentPeer(e.headers)===`remote`}function isLocalDevelopmentServer(){return process.env.VERCEL&&process.env.VERCEL_ENV===`development`?!0:isEveDevEnvironment()}const LOCAL_DEV_SESSION_AUTH_CONTEXT={attributes:{},authenticator:`local-dev`,principalId:`local-dev`,principalType:`local-dev`};async function verifyVercelOidc(e,t={}){if(e===null||e.length===0)return{ok:!1};let n=decodeUnverifiedJwtClaims(e);if(n===null)return vercelOidcLog.debug(`Rejected token that failed to decode as a JWT.`),{ok:!1};if(!isVercelOidcIssuer(n.issuer))return vercelOidcLog.debug(`Rejected token whose issuer is not a Vercel OIDC issuer.`,{issuer:n.issuer}),{ok:!1};if(n.audiences.length===0)return vercelOidcLog.debug(`Rejected token with no audience claim.`,{issuer:n.issuer}),{ok:!1};if(!n.audiences.some(e=>e.startsWith(`https://vercel.com/`)))return vercelOidcLog.debug(`Rejected token whose audience is not a Vercel audience.`,{audiences:n.audiences,issuer:n.issuer}),{ok:!1};let r=resolveCurrentVercelProject(t),i=await runOidcVerification(e,{acceptCurrentVercelProject:!0,audiences:n.audiences,currentVercelProject:r,issuer:n.issuer,subjects:t.subjects??[]});return i.kind===`authenticated`?(vercelOidcLog.debug(`Accepted Vercel OIDC token.`,{issuer:n.issuer,principalType:i.principal.principalType,subject:i.principal.subject}),{ok:!0,sessionAuth:createRuntimeSessionAuthContext(i.principal)}):(vercelOidcLog.debug(`Rejected Vercel OIDC token after verification.`,{audiences:n.audiences,issuer:n.issuer,reason:i.kind,subjectsConfigured:(t.subjects??[]).length>0,...i.kind===`misconfigured`?{detail:i.message}:{}}),{ok:!1})}function resolveCurrentVercelProject(e){if(e.currentVercelProject!==void 0)return e.currentVercelProject;let t=process.env.VERCEL_PROJECT_ID?.trim();if(t===void 0||t.length===0)return;let n=process.env.VERCEL_TARGET_ENV?.trim()||process.env.VERCEL_ENV?.trim();return n===void 0||n.length===0?{projectId:t}:{environment:n,projectId:t}}function vercelSubject(e){assertVercelSubjectSegment(`teamSlug`,e.teamSlug),assertVercelSubjectSegment(`projectName`,e.projectName);let t=e.environment??`production`;if(t!==`production`&&t!==`preview`&&t!==`development`&&t!==`*`)throw Error(`vercelSubject: invalid environment ${JSON.stringify(t)}; expected "production", "preview", "development", or "*".`);return`owner:${e.teamSlug}:project:${e.projectName}:environment:${t}`}function assertVercelSubjectSegment(e,t){if(t.length===0)throw Error(`vercelSubject: ${e} must be a non-empty string.`);if(t.includes(`*`)||t.includes(`:`))throw Error(`vercelSubject: ${e} ${JSON.stringify(t)} may not contain ${t.includes(`:`)?`':'`:`'*'`}. Hand-write the subject string when wildcards are intentional.`)}function vercelOidc(e={}){let{subjects:t,...n}=e,r={...n,subjects:t?.map(e=>typeof e==`string`?e:e.subject)};return withAuthChallenges(async n=>{let i=extractBearerToken(n.headers.get(`authorization`)),a=e.currentVercelProject??(i!==null&&isLocalDevelopmentVercelOidc()?await resolveVercelOidcCurrentProject(n):void 0),o=await verifyVercelOidc(i,a===void 0?r:{...r,currentVercelProject:a});if(!o.ok)return null;let s=o.sessionAuth;return(s.principalType===`service`||s.principalType===`runtime`)&&t?.some(e=>typeof e!=`string`&&e.allowToolStubs===!0&&areTokenClaimMatchersSatisfied({sub:s.subject},{subjects:[e.subject]}))?{...s,allowToolStubs:!0}:s},[{scheme:`Bearer`}])}function isLocalDevelopmentVercelOidc(){return process.env.VERCEL_ENV===`development`?!0:process.env.VERCEL_ENV===`preview`||process.env.VERCEL_ENV===`production`?!1:isEveDevEnvironment()}function httpBasic(e,t){return withAuthChallenges(t=>{let n=verifyHttpBasic(t.headers.get(`authorization`),e);return n.ok?n.sessionAuth:null},[{parameters:{realm:t?.realm??`eve`,charset:`UTF-8`},scheme:`Basic`}])}function jwtHmac(e){return withAuthChallenges(async t=>{let n=await verifyJwtHmac(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null},[{scheme:`Bearer`}])}function jwtEcdsa(e){return withAuthChallenges(async t=>{let n=await verifyJwtEcdsa(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null},[{scheme:`Bearer`}])}function oidc(e){return withAuthChallenges(async t=>{let n=await verifyOidc(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null},[{scheme:`Bearer`}])}function decodeUnverifiedJwtClaims(e){let t;try{t=decodeJwt(e)}catch{return null}return typeof t.iss!=`string`||t.iss.length===0?null:{audiences:typeof t.aud==`string`?[t.aud]:Array.isArray(t.aud)?t.aud.filter(e=>typeof e==`string`):[],issuer:t.iss}}export{ForbiddenError,UnauthenticatedError,createIpAllowList,createUnauthorizedResponse,escapeAuthChallengeParameter,extractBearerToken,httpBasic,isIpAllowed,jwtEcdsa,jwtHmac,localDev,none,oauthResource,oidc,placeholderAuth,readOAuthResourceOptions,routeAuth,vercelOidc,vercelSubject,verifyHttpBasic,verifyJwtEcdsa,verifyJwtHmac,verifyOidc,verifyVercelOidc,withAuthChallenges};
|
|
@@ -7,13 +7,18 @@ import type { InputRequest } from "#shared/input.js";
|
|
|
7
7
|
import { type InputResponse, type StrictInputResponses } from "#shared/input.js";
|
|
8
8
|
import type { ActionEvent, Adapter, ChatConfig, SerializedThread, Thread, WebhookOptions } from "#compiled/chat/index.js";
|
|
9
9
|
import { Chat } from "#compiled/chat/index.js";
|
|
10
|
+
import { type UndeliveredReplyState } from "#public/channels/undelivered-reply.js";
|
|
10
11
|
import { type Channel, type ChannelEvents, type ChannelContinuationOps, type Session } from "#public/definitions/channel.js";
|
|
11
12
|
type ChatSdkAdapters = Record<string, Adapter>;
|
|
12
13
|
/** Durable Chat SDK thread state plus default-handler streaming bookkeeping. */
|
|
13
|
-
export interface ChatSdkChannelState extends Record<string, unknown>, PromptQueueState {
|
|
14
|
+
export interface ChatSdkChannelState extends Record<string, unknown>, PromptQueueState, UndeliveredReplyState {
|
|
14
15
|
thread: SerializedThread | null;
|
|
15
16
|
/** Message id of the in-flight streamed assistant post (edit fallback). */
|
|
16
17
|
anchorMessageId?: string | null;
|
|
18
|
+
/** Text streamed for the anchor so far, including deltas no throttled edit has shown yet. */
|
|
19
|
+
streamText?: string | null;
|
|
20
|
+
/** Text the anchor showed after its last successful post or edit. */
|
|
21
|
+
shownText?: string | null;
|
|
17
22
|
/**
|
|
18
23
|
* Whether `adapter.editMessage` works for this session. Set to `false` once
|
|
19
24
|
* an edit throws {@link isNotImplemented}, which disables streaming edits for
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{actionLabel,visibleActions}from"#shared/action-label.js";import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{parseInputResponses}from"#shared/input.js";import{defaultDeliverResult}from"#channel/adapter.js";import{ContextContainer,contextStorage}from"#context/container.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import{ContextKey}from"#context/key.js";import{DEFAULT_UPLOAD_POLICY}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{assertWithinLimit}from"#internal/attachments/limited-read.js";import{isNotImplemented}from"#public/channels/chat-sdk/notImplemented.js";import{promptQueueEvents}from"#channel/prompt-queue.js";import{INACTIVE_REQUEST_NOTICE,respondOrRetireInactivePrompt}from"#channel/inactive-prompt.js";import{Chat,Message,ThreadImpl}from"#compiled/chat/index.js";import{defaultAuthorizationEvents}from"#public/channels/chat-sdk/authorization.js";import{parseChatSdkFileRef}from"#public/channels/chat-sdk/attachment-refs.js";import{decodeInputAction,renderInputRequests,renderRetiredInputRequest}from"#public/channels/chat-sdk/input-actions.js";import{chatSdkInstrumentation}from"#public/channels/chat-sdk/audience.js";const log=createLogger(`chat-sdk.channel`),
|
|
1
|
+
import{actionLabel,visibleActions}from"#shared/action-label.js";import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{parseInputResponses}from"#shared/input.js";import{defaultDeliverResult}from"#channel/adapter.js";import{ContextContainer,contextStorage}from"#context/container.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import{ContextKey}from"#context/key.js";import{DEFAULT_UPLOAD_POLICY}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{assertWithinLimit}from"#internal/attachments/limited-read.js";import{isNotImplemented}from"#public/channels/chat-sdk/notImplemented.js";import{promptQueueEvents}from"#channel/prompt-queue.js";import{INACTIVE_REQUEST_NOTICE,respondOrRetireInactivePrompt}from"#channel/inactive-prompt.js";import{Chat,Message,ThreadImpl}from"#compiled/chat/index.js";import{defaultAuthorizationEvents}from"#public/channels/chat-sdk/authorization.js";import{parseChatSdkFileRef}from"#public/channels/chat-sdk/attachment-refs.js";import{decodeInputAction,renderInputRequests,renderRetiredInputRequest}from"#public/channels/chat-sdk/input-actions.js";import{reportUndeliveredReply,withUndeliveredReplyNote}from"#public/channels/undelivered-reply.js";import{chatSdkInstrumentation}from"#public/channels/chat-sdk/audience.js";const log=createLogger(`chat-sdk.channel`),SURFACE_NAME=`this chat`,ActiveWebhookKey=new ContextKey(`chat-sdk.active-webhook`);function chatSdkChannel(e){let t=new Chat(e),n=e.inputActionPrefix??`eve_input:`,r=e.streaming??!0,i=e.streamingEditIntervalMs??1e3,a={...defaultEvents(n),...e.events};return t.onAction(async r=>{let i=decodeInputAction(r.actionId,n,r.value);if(!i)return;let{thread:a}=r;if(!a)throw Error(`chatSdkChannel input actions require a thread on the Chat SDK action event.`);let o=e.resolveInputAuth?await e.resolveInputAuth(r):null,s=await respondOrRetireInactivePrompt(()=>bridgeRespond(t,[i],{auth:o,thread:a.toJSON()}),{retirePrompt:e=>retireInputCard(a,r.messageId,e)},log);if(s.outcome===`failed`)throw s.error;s.outcome!==`delivered`&&await Promise.all([s.retirement,a.postEphemeral(r.user,{markdown:INACTIVE_REQUEST_NOTICE},{fallbackToDM:!0}).catch(e=>log.error(`inactive prompt notice failed`,{error:e}))])}),{bot:t,channel:defineChannel({kindHint:`chat-sdk`,turnPolicy:e.turnPolicy,fetchFile:e=>fetchAttachment(t,e),state:initialState(),...chatSdkInstrumentation,context(e){return{bot:t,state:e,streaming:r,streamingEditIntervalMs:i,thread:threadFromState(t,e)}},routes:adapterNames(e.adapters).flatMap(n=>{let r=routeForAdapter(n,e),handler=async(r,i)=>{let a=t.webhooks[n],o=new ContextContainer;return o.setVirtualContext(ActiveWebhookKey,{from:i.from}),contextStorage.run(o,()=>a(r,{...e.webhook,waitUntil(e){i.waitUntil(e)}}))};return[GET(r,handler),POST(r,handler)]}),async receive(e,{from:n}){let r=serializeReceiveTarget(t,e.target);return n(r.id).send(e.message,{auth:e.auth,state:{thread:r}})},deliver(e,t){recordPrincipalUser(t.state,t.session.auth.current,e),recordLatestMessage(t.state,e);let n=resolveInputAnswer(t.state,e);return n===void 0?defaultDeliverResult(withUndeliveredReplyNote(t.state,e,SURFACE_NAME)):{context:e.context,inputResponses:parseInputResponses([n])}},events:a}),send(e,n){return bridgeSend(t,e,n)},respond(e,n){return bridgeRespond(t,e,n)}}}function defaultEvents(e){async function showPrompt(t,n){if(!t.thread)return!1;let r=await t.thread.post(renderInputRequests([n],e));r.id&&(t.state.pendingInputCards={...t.state.pendingInputCards,[r.id]:{requests:[n],resolved:{}}})}async function clearAnsweredCards(t,n){let r=n.thread;if(r)for(let[i,a]of Object.entries(n.state.pendingInputCards??{})){let o=t.resolutions.filter(e=>a.requests.some(t=>t.requestId===e.requestId));if(o.length===0)continue;let s={...a.resolved,...Object.fromEntries(o.map(e=>[e.requestId,e]))},{[i]:c,...l}=n.state.pendingInputCards??{};if(n.state.pendingInputCards=a.requests.every(e=>s[e.requestId]!==void 0)?l:{...l,[i]:{requests:a.requests,resolved:s}},n.state.editSupported!==!1)try{await r.adapter.editMessage(r.id,i,renderInputRequests(a.requests,e,s))}catch(e){if(!isNotImplemented(e)){log.warn(`answered input card edit failed`,{error:e,messageId:i});continue}n.state.editSupported=!1}}}let t=promptQueueEvents(showPrompt);return{...defaultAuthorizationEvents(),async"turn.started"(e,t,n){t.state.pendingToolCallMessage=null,clearStream(t.state),await safeStartTyping(t.thread,`Working...`)},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,r){await safeStartTyping(t.thread,truncate(r));return}let i=visibleActions(e.actions).map(t=>actionLabel(t,e.presentation));i.length!==0&&await safeStartTyping(t.thread,truncate(`${i.join(`, `)}...`))},async"message.appended"(e,t,n){if(!t.thread||!canStream(t))return;let r=((t.state.streamStepIndex===e.stepIndex?t.state.streamText:null)??``)+e.messageDelta;if(!r)return;t.state.streamText=r;let i=t.state.anchorMessageId;if(!i||t.state.streamStepIndex!==e.stepIndex){let n=await t.thread.post({markdown:r});t.state.shownText=r,t.state.anchorMessageId=n.id,t.state.streamStepIndex=e.stepIndex,t.state.lastEditAtMs=Date.now();return}let a=t.state.lastEditAtMs??0;if(!(Date.now()-a<t.streamingEditIntervalMs))try{await editMessage(t.thread,i,r),t.state.shownText=r,t.state.lastEditAtMs=Date.now()}catch(e){if(!isNotImplemented(e))throw e;t.state.editSupported=!1}},...t,async"input.resolved"(e,n,r){await clearAnsweredCards(e,n),await t[`input.resolved`](e,n)},async"message.completed"(e,t,n){if(e.finishReason===`tool-calls`){if(t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null,e.message)try{await finalizeStreamedMessage(t,e.message,!1)}catch(e){if(!isNotImplemented(e))throw e}else clearStream(t.state);return}if(t.state.pendingToolCallMessage=null,!e.message){clearStream(t.state);return}try{await finalizeStreamedMessage(t,e.message,!0)}catch(n){await reportUndeliveredReply({log,name:SURFACE_NAME,post:async e=>t.thread?.post(e),state:t.state},e.turnId,n)}},async"turn.failed"(e,t,n){e.terminal||await postFailure(t.thread,`I hit an error while handling your request`,`Please try again, rephrase, or reach out if it keeps failing.`,e)},async"session.failed"(e,t){await postFailure(t.thread,`This session could not recover from an error`,`Start a new conversation to continue.`,e)}}}async function safeStartTyping(e,t){if(e)try{await e.startTyping(t)}catch(e){if(!isNotImplemented(e))throw e}}async function editMessage(e,t,n){await e.adapter.editMessage(e.id,t,{markdown:n})}async function retireInputCard(e,t,n){try{await e.adapter.editMessage(e.id,t,renderRetiredInputRequest(n))}catch(e){if(!isNotImplemented(e))throw e}}async function finalizeStreamedMessage(e,t,n){let{anchorMessageId:r,shownText:i}=e.state;clearStream(e.state);let a=e.thread;if(a){if(!r){n&&await a.post({markdown:t});return}if(i!==t)try{await editMessage(a,r,t)}catch(t){throw isNotImplemented(t)&&(e.state.editSupported=!1),t}}}function canStream(e){return e.streaming&&e.state.editSupported!==!1}function clearStream(e){e.anchorMessageId=null,e.lastEditAtMs=null,e.streamStepIndex=null,e.streamText=null,e.shownText=null}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function truncate(e,t=80){return e.length<=t?e:`${e.slice(0,t-1)}\u2026`}async function postFailure(e,t,n,r){if(!e)return;let i=formatErrorHint(r),a=extractErrorId(r.details);await e.post([`${t}${i}.`,n,...a?[`Error id: ${a}`]:[]].join(`
|
|
2
2
|
|
|
3
3
|
`))}async function bridgeSend(e,t,n){let r=activeFrom(`send`),{adapterName:i,auth:a,inputAnswer:o,thread:s,...c}=n,l=serializeThread(e,s,i),u={thread:l};return o!==void 0&&(u.inputAnswer=o),r(l.id).send(t,{...c,auth:a??null,state:u})}async function bridgeRespond(e,t,n){let r=activeFrom(`respond`),{adapterName:i,auth:a,thread:o,...s}=n;return r(serializeThread(e,o,i).id).respond(parseInputResponses(t),{...s,auth:a??null})}function recordPrincipalUser(e,t,n){if(t===null)return;let r=(n.state?.thread)?.currentMessage?.author;if(r===void 0||r.isBot===!0||r.isMe)return;let i=e.usersByPrincipal?.[t.principalId];i!==r.userId&&i!==null&&(e.usersByPrincipal={...e.usersByPrincipal,[t.principalId]:i===void 0?r.userId:null})}function recordLatestMessage(e,t){let n=t.state?.thread?.currentMessage;n===void 0||n.author.isMe||(e.latestMessageId=n.id)}function resolveInputAnswer(e,t){let n=t.state?.inputAnswer;if(n===void 0)return;let r=e.pendingInputCards?.[n.messageId],i=r?.requests.find(e=>r.resolved[e.requestId]===void 0&&e.options?.some(e=>e.id===n.optionId)===!0);return i===void 0?void 0:{optionId:n.optionId,requestId:i.requestId}}function activeFrom(e){let t=contextStorage.getStore()?.get(ActiveWebhookKey);if(!t)throw Error(`chatSdkChannel().${e} can only run during a Chat SDK webhook handler for this bridge.`);return t.from}function initialState(){return{pendingAuthMessageIds:{},thread:null}}async function fetchAttachment(e,t){let n=parseChatSdkFileRef(new URL(t));if(n===null)return null;await e.initialize();let r=e.getAdapter(n.adapter).rehydrateAttachment?.(n.attachment);if(r?.fetchData===void 0)throw new EveAttachmentError({adapterKind:`chat-sdk`,kind:`resolver-threw`,message:`the ${n.adapter} adapter can't download it after the webhook returns.`});let i;try{i=await r.fetchData()}catch(e){throw e instanceof EveAttachmentError?e:new EveAttachmentError({adapterKind:`chat-sdk`,cause:e,kind:`resolver-threw`,message:`the ${n.adapter} download failed.`})}let a=Buffer.isBuffer(i)?i:Buffer.from(i);return assertWithinLimit(a,DEFAULT_UPLOAD_POLICY.maxBytes,`chat-sdk`),{bytes:a,mediaType:n.attachment.mimeType}}function threadFromState(e,t){if(!t.thread)return null;try{let n=t.thread;return new ThreadImpl({adapter:e.getAdapter(n.adapterName),channelId:n.channelId,channelVisibility:n.channelVisibility,currentMessage:n.currentMessage?Message.fromJSON(n.currentMessage):void 0,id:n.id,isDM:n.isDM,stateAdapter:e.getState()})}catch(e){return log.warn(`failed to rebuild Chat SDK thread from channel state`,{error:e}),null}}function serializeReceiveTarget(e,t){if(t.thread)return t.thread;if(!t.threadId)throw Error(`chatSdkChannel().receive requires target.thread or target.threadId.`);return serializeThread(e,t.threadId,t.adapterName)}function serializeThread(e,t,n){if(typeof t==`string`){let r=n??inferAdapterName(t),i=e.getAdapter(r);return{_type:`chat:Thread`,adapterName:r,channelId:i.channelIdFromThreadId(t),channelVisibility:i.getChannelVisibility?.(t),id:t,isDM:!1}}return isSerializedThread(t)?t:t.toJSON()}function isSerializedThread(e){return`_type`in e&&e._type===`chat:Thread`}function inferAdapterName(e){let t=e.indexOf(`:`);if(t<=0)throw Error(`chatSdkChannel string thread references require options.adapterName.`);return e.slice(0,t)}function adapterNames(e){return Object.keys(e)}function routeForAdapter(e,t){return t.routes?.[e]||`${(t.route??`/eve/v1`).replace(/\/$/u,``)}/${e}`}export{chatSdkChannel};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { SlackChannelInternalEvents, SlackChannelState, SlackContext } from "#public/channels/slack/slackChannel.js";
|
|
1
|
+
import type { SlackChannelInternalEvents, SlackContext } from "#public/channels/slack/slackChannel.js";
|
|
3
2
|
type MessageCompletedHandler = NonNullable<SlackChannelInternalEvents["message.completed"]>;
|
|
4
3
|
/**
|
|
5
4
|
* A completed reply for {@link postCompletedSlackReply}: Markdown alone, or
|
|
@@ -69,9 +68,4 @@ export declare function deliverCompletedSlackReply(channel: SlackContext, reply:
|
|
|
69
68
|
* Steps that end in tool calls and empty replies pass through.
|
|
70
69
|
*/
|
|
71
70
|
export declare function withFinalReplyDelivery(render: MessageCompletedHandler | undefined): MessageCompletedHandler;
|
|
72
|
-
/**
|
|
73
|
-
* Tells the model, ahead of the next message, that its previous reply never
|
|
74
|
-
* reached Slack, so it does not assume the user read that reply.
|
|
75
|
-
*/
|
|
76
|
-
export declare function withUndeliveredReplyNote<T extends DeliverPayload>(state: SlackChannelState, payload: T): T;
|
|
77
71
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{reportUndeliveredReply}from"#public/channels/undelivered-reply.js";import{SLACK_MARKDOWN_TEXT_MAX_LENGTH,truncateMessageText}from"#public/channels/slack/limits.js";const log=createLogger(`slack.reply`),LONG_RESPONSE_NOTICE=`Here's a snippet with the full response.`,FALLBACK_LOG=`Slack refused the final reply; delivering it with a fallback`,REFUSED_PAYLOAD_ERRORS=new Set([`invalid_blocks`,`invalid_blocks_format`,`msg_blocks_too_long`,`msg_blocks_too_many`,`msg_too_long`]);async function postCompletedSlackReply(e,t){await deliverCompletedSlackReply(e,t,{})}async function deliverCompletedSlackReply(e,t,n){let{blocks:r,markdown:i,text:a}=typeof t==`string`?{markdown:t}:t;if(await threadRootIsGone(e)){log.warn(`Slack thread root was deleted; dropping the final reply`,n);return}let o;if(r!==void 0)try{await e.thread.post({blocks:r,text:a??truncateMessageText(i)});return}catch(e){if(o=refusedPayloadError(e),o===void 0)throw e}if(i.length<=SLACK_MARKDOWN_TEXT_MAX_LENGTH){o!==void 0&&log.warn(FALLBACK_LOG,{fallback:`inline`,slackError:o,...n});try{await e.thread.post(i);return}catch(e){if(o=refusedPayloadError(e),o===void 0)throw e}}o!==void 0&&log.warn(FALLBACK_LOG,{fallback:`snippet`,slackError:o,...n}),await uploadReplySnippet(e,i)}async function threadRootIsGone(e){let{channelId:t,threadTs:n}=e.slack;if(!t||!n||e.state?.postedInThread===!0)return!1;try{let r=await e.slack.request(`chat.getPermalink`,{channel:t,message_ts:n});return r.ok!==!0&&r.error===`message_not_found`}catch{return!1}}async function uploadReplySnippet(e,t){let n={data:new Blob([t],{type:`text/markdown`}),filename:`eve-response.md`,mimeType:`text/markdown`},r=e.slack.threadTs.length>0;if(!r&&(!(await e.thread.post(LONG_RESPONSE_NOTICE)).id||e.slack.threadTs.length===0))throw Error(`Slack did not return a thread timestamp for the long response notice.`);await e.slack.uploadFiles([n],{initialComment:r?LONG_RESPONSE_NOTICE:void 0,snippetType:`markdown`})}function refusedPayloadError(e){let t=e;for(let e=0;e<5&&typeof t==`object`&&t;e++){let e=t,n=e.response?.error??e.data?.error;if(typeof n==`string`&&REFUSED_PAYLOAD_ERRORS.has(n))return n;t=e.cause}}function withFinalReplyDelivery(e){return async(t,n,r)=>{if(t.finishReason===`tool-calls`||!t.message){await e?.(t,n,r);return}try{await e?.(t,n,r)}catch(e){await reportUndeliveredReply({log,name:`Slack`,post:e=>n.thread.post(e),state:n.state},t.turnId,e)}}}export{deliverCompletedSlackReply,postCompletedSlackReply,withFinalReplyDelivery};
|