eve 0.46.1 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/src/channel/schedule.d.ts +1 -0
- package/dist/src/channel/schedule.js +1 -1
- package/dist/src/cli/dev/run-interactive-ui.d.ts +19 -0
- package/dist/src/cli/dev/run-interactive-ui.js +1 -0
- package/dist/src/cli/dev/tui/agent-header.js +1 -1
- package/dist/src/cli/dev/tui/remote-auth-command.js +1 -1
- package/dist/src/cli/dev/tui/remote-connection.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +6 -0
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/terminal-renderer.d.ts +6 -0
- package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
- package/dist/src/cli/dev/tui/tui.d.ts +16 -0
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/cli/run.js +3 -3
- package/dist/src/compiled/.vendor-stamp.json +2 -1
- package/dist/src/compiled/@vercel/sandbox/index.js +9 -15
- package/dist/src/compiled/@vercel/sandbox-delete/LICENSE +201 -0
- package/dist/src/compiled/@vercel/sandbox-delete/_async-retry.d.ts +8 -0
- package/dist/src/compiled/@vercel/sandbox-delete/_workflow-serde.d.ts +5 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/api-client.d.ts +1730 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/api-error.d.ts +29 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/base-client.d.ts +39 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/file-writer.d.ts +66 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/index.d.ts +2 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/validators.d.ts +907 -0
- package/dist/src/compiled/@vercel/sandbox-delete/api-client/with-retry.d.ts +10 -0
- package/dist/src/compiled/@vercel/sandbox-delete/auth/error.d.ts +15 -0
- package/dist/src/compiled/@vercel/sandbox-delete/auth/file.d.ts +18 -0
- package/dist/src/compiled/@vercel/sandbox-delete/auth/index.d.ts +6 -0
- package/dist/src/compiled/@vercel/sandbox-delete/auth/oauth.d.ts +111 -0
- package/dist/src/compiled/@vercel/sandbox-delete/auth/poll-for-token.d.ts +28 -0
- package/dist/src/compiled/@vercel/sandbox-delete/auth/project.d.ts +40 -0
- package/dist/src/compiled/@vercel/sandbox-delete/command.d.ts +292 -0
- package/dist/src/compiled/@vercel/sandbox-delete/constants.d.ts +15 -0
- package/dist/src/compiled/@vercel/sandbox-delete/execution-context.d.ts +116 -0
- package/dist/src/compiled/@vercel/sandbox-delete/filesystem.d.ts +258 -0
- package/dist/src/compiled/@vercel/sandbox-delete/index.d.ts +13 -0
- package/dist/src/compiled/@vercel/sandbox-delete/index.js +9 -0
- package/dist/src/compiled/@vercel/sandbox-delete/network-policy.d.ts +169 -0
- package/dist/src/compiled/@vercel/sandbox-delete/proxy.d.ts +55 -0
- package/dist/src/compiled/@vercel/sandbox-delete/sandbox-user.d.ts +211 -0
- package/dist/src/compiled/@vercel/sandbox-delete/sandbox.d.ts +1258 -0
- package/dist/src/compiled/@vercel/sandbox-delete/session.d.ts +435 -0
- package/dist/src/compiled/@vercel/sandbox-delete/snapshot.d.ts +238 -0
- package/dist/src/compiled/@vercel/sandbox-delete/utils/get-credentials.d.ts +21 -0
- package/dist/src/compiled/@vercel/sandbox-delete/utils/paginator.d.ts +16 -0
- package/dist/src/compiled/@vercel/sandbox-delete/utils/resolveSignal.d.ts +15 -0
- package/dist/src/compiled/@vercel/sandbox-delete/utils/sandbox-snapshot.d.ts +11 -0
- package/dist/src/compiled/@vercel/sandbox-delete/utils/types.d.ts +11 -0
- package/dist/src/compiled/_chunks/node/auth-DpzFWvTn.js +2 -0
- package/dist/src/compiled/_chunks/node/auth-QgeX8nBM.js +2 -0
- package/dist/src/compiled/_chunks/node/decode_jwt-Col8JOnf.js +7 -0
- package/dist/src/compiled/_chunks/node/version-DH6yRy-M.js +1 -0
- package/dist/src/compiled/_chunks/node/xdg-app-paths-Bys8q02u.js +1 -0
- package/dist/src/compiler/extension-compatibility.d.ts +12 -12
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/model-catalog.js +1 -1
- package/dist/src/context/build-callback-context.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +2 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/context/memory-tools.js +1 -1
- package/dist/src/context/providers/sandbox.js +1 -1
- package/dist/src/evals/context.js +1 -1
- package/dist/src/evals/session-content.d.ts +5 -0
- package/dist/src/evals/session-content.js +3 -0
- package/dist/src/evals/session.d.ts +1 -0
- package/dist/src/evals/session.js +1 -1
- package/dist/src/evals/types.d.ts +5 -0
- package/dist/src/execution/dispatch-runtime-actions-shared.js +1 -1
- package/dist/src/execution/eve-workflow-attributes.d.ts +3 -0
- package/dist/src/execution/eve-workflow-attributes.js +1 -1
- package/dist/src/execution/runtime-context.js +1 -1
- package/dist/src/execution/sandbox/bindings/docker-base-setup.d.ts +4 -4
- package/dist/src/execution/sandbox/bindings/docker-base-setup.js +1 -1
- package/dist/src/execution/sandbox/bindings/docker-session.d.ts +1 -1
- package/dist/src/execution/sandbox/bindings/docker-session.js +1 -1
- package/dist/src/execution/sandbox/bindings/docker.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-base-runtime.d.ts +4 -0
- package/dist/src/execution/sandbox/bindings/vercel-base-runtime.js +2 -2
- package/dist/src/execution/sandbox/bindings/vercel-lifecycle.d.ts +8 -0
- package/dist/src/execution/sandbox/bindings/vercel-lifecycle.js +1 -0
- package/dist/src/execution/sandbox/bindings/vercel-sdk-types.d.ts +3 -0
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/ensure.d.ts +2 -0
- package/dist/src/execution/sandbox/ensure.js +1 -1
- package/dist/src/execution/sandbox/require-sandbox.js +1 -1
- package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
- package/dist/src/execution/tools/connection-search.js +2 -2
- package/dist/src/execution/tools/file-state.js +1 -1
- package/dist/src/execution/tools/load-skill.js +1 -1
- package/dist/src/execution/tools/subagent/local.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/approval-delivery-coordinator.js +1 -1
- package/dist/src/harness/attachment-staging.js +1 -1
- package/dist/src/harness/authorization.js +1 -1
- package/dist/src/harness/current-messages.d.ts +15 -0
- package/dist/src/harness/current-messages.js +1 -0
- package/dist/src/harness/instrumentation/runtime-context.js +1 -1
- package/dist/src/harness/instrumentation/state.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/harness/tools.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/model-catalog.js +1 -1
- package/dist/src/internal/nitro/host/start-development-server.js +1 -1
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
- package/dist/src/public/definitions/sandbox-backend.d.ts +1 -1
- package/dist/src/public/sandbox/index.d.ts +1 -1
- package/dist/src/runtime/agent/resolve-model.js +1 -1
- package/dist/src/runtime/connections/principal.js +1 -1
- package/dist/src/sandbox/state.d.ts +2 -0
- package/dist/src/services/dev-client.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
- package/dist/src/setup/scaffold/create/web-template.js +4 -2
- package/dist/src/shared/sandbox-backend.d.ts +9 -0
- package/dist/src/shared/sandbox-definition.d.ts +5 -4
- package/dist/src/shared/sandbox-session.d.ts +4 -1
- package/dist/src/tasks/delivery-policy.d.ts +12 -0
- package/dist/src/tasks/delivery-policy.js +1 -0
- package/dist/src/tracing/agent-trace-context-store.js +1 -1
- package/docs/evals/cases.mdx +11 -2
- package/docs/evals/judge.mdx +15 -0
- package/docs/evals/overview.mdx +2 -2
- package/docs/guides/instrumentation.md +1 -0
- package/docs/guides/session-context.md +2 -2
- package/docs/sandbox.mdx +25 -2
- package/docs/schedules.mdx +2 -0
- package/docs/tools/overview.mdx +2 -0
- package/package.json +2 -1
- package/dist/src/compiled/_chunks/node/auth-BaNbsImr.js +0 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SandboxSession } from "#public/definitions/sandbox.js";
|
|
2
2
|
import type { SandboxBackendSessionState } from "#public/definitions/sandbox-backend.js";
|
|
3
|
+
import type { SandboxDeleteOptions } from "#public/definitions/sandbox-backend.js";
|
|
3
4
|
/**
|
|
4
5
|
* Serializable sandbox reconnect record stored on the harness session.
|
|
5
6
|
* Alias for {@link SandboxBackendSessionState} kept at this layer so
|
|
@@ -28,6 +29,7 @@ export interface SandboxState {
|
|
|
28
29
|
*/
|
|
29
30
|
export interface SandboxAccess {
|
|
30
31
|
captureState(): Promise<SandboxState>;
|
|
32
|
+
delete?(options?: SandboxDeleteOptions): Promise<void>;
|
|
31
33
|
get(): Promise<SandboxSession | null>;
|
|
32
34
|
stop(): Promise<void>;
|
|
33
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts}from"#services/dev-client/runtime-artifacts.js";import{isLocalDevelopmentServerUrl}from"#services/dev-client/local-host.js";var LocalDevelopmentRuntimeArtifactRefresher=class{#e;#t;#n;constructor(e){this.#e=isLocalDevelopmentServerUrl(e.serverUrl),this.#t=e.serverUrl}clear(){this.#n=void 0}async refresh(e){shouldRefreshRuntimeArtifactsForTurn(e)&&await this.#r({...e,rebuild:!0})}async refreshIdle(e){await this.#r({...e,rebuild:!1})}async refreshAfterSourceChange(e){await this.#r({...e,force:!0,rebuild:!0})}async#r(t){if(!this.#e)return;let n=(t.rebuild?await rebuildDevelopmentRuntimeArtifacts({force:t.force,serverUrl:this.#t}):void 0)??await readDevelopmentRuntimeArtifactsRevision({serverUrl:this.#t});if(n===void 0)return;let r=this.#n;r!==void 0&&r!==n&&await t.onRuntimeArtifactsChanged?.({previousRevision:r,revision:n}),this.#n=n}};function shouldRefreshRuntimeArtifactsForTurn(e){return e.message!==void 0&&(e.inputResponses?.length??0)===0}function createDevelopmentRuntimeArtifactRefresher(e){return new LocalDevelopmentRuntimeArtifactRefresher(e)}export{createDevelopmentRuntimeArtifactRefresher};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.47.1`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e,t){return{"agent/agent.ts":renderAgentTemplate(e,t),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,n){let r=parseChatGptModelSelection(e);return r===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(r)}),\n${reasoningTemplateLine(n)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,n){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(n.model)!==void 0?renderAgentTemplate(n.model,n.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,n.appName).replaceAll(`__EVE_INIT_MODEL__`,n.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(n.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(n.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(n.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,n.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,n.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,n.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,n.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,n.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,n.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -17,7 +17,7 @@ export declare const WEB_APP_TEMPLATE_FILES: {
|
|
|
17
17
|
readonly "components/ai-elements/question.tsx": '"use client";\n\nimport type {\n ChangeEvent,\n ComponentProps,\n FormEvent,\n HTMLAttributes,\n KeyboardEvent,\n MouseEvent,\n ReactNode,\n} from "react";\n\nimport { Button } from "@/components/ui/button";\nimport { Textarea } from "@/components/ui/textarea";\nimport { cn } from "@/lib/utils";\nimport { createContext, useCallback, useContext, useMemo, useState } from "react";\n\nexport interface QuestionValue {\n selectedValues: readonly string[];\n text: string;\n}\n\nexport interface QuestionResponse {\n selectedValues: readonly string[];\n text?: string;\n}\n\ntype SelectionMode = "multiple" | "single";\n\ninterface QuestionContextValue {\n disabled: boolean;\n selectedValues: readonly string[];\n selectionMode: SelectionMode;\n setText: (text: string) => void;\n text: string;\n toggleValue: (value: string) => void;\n}\n\nconst QuestionContext = createContext<QuestionContextValue | null>(null);\n\nconst useQuestion = () => {\n const context = useContext(QuestionContext);\n\n if (!context) {\n throw new Error("Question components must be used within Question");\n }\n\n return context;\n};\n\nexport type QuestionProps = Omit<ComponentProps<"form">, "defaultValue" | "onSubmit" | "value"> & {\n defaultValue?: QuestionValue;\n disabled?: boolean;\n onSubmit?: (\n response: QuestionResponse,\n event: FormEvent<HTMLFormElement>,\n ) => void | Promise<void>;\n onValueChange?: (value: QuestionValue) => void;\n selectionMode?: SelectionMode;\n value?: QuestionValue;\n};\n\nconst EMPTY_VALUE: QuestionValue = { selectedValues: [], text: "" };\n\nconst getSelectedValues = (\n currentValues: readonly string[],\n optionValue: string,\n selectionMode: SelectionMode,\n): readonly string[] => {\n const isSelected = currentValues.includes(optionValue);\n\n if (selectionMode === "single") {\n return isSelected ? [] : [optionValue];\n }\n\n if (isSelected) {\n return currentValues.filter((item) => item !== optionValue);\n }\n\n return [...currentValues, optionValue];\n};\n\nexport const Question = ({\n children,\n className,\n defaultValue = EMPTY_VALUE,\n disabled = false,\n onSubmit,\n onValueChange,\n selectionMode = "single",\n value: controlledValue,\n ...props\n}: QuestionProps) => {\n const [internalValue, setInternalValue] = useState(defaultValue);\n const value = controlledValue ?? internalValue;\n\n const setValue = useCallback(\n (nextValue: QuestionValue) => {\n if (controlledValue === undefined) {\n setInternalValue(nextValue);\n }\n onValueChange?.(nextValue);\n },\n [controlledValue, onValueChange],\n );\n\n const setText = useCallback(\n (text: string) => {\n setValue({ ...value, text });\n },\n [setValue, value],\n );\n\n const toggleValue = useCallback(\n (optionValue: string) => {\n const selectedValues = getSelectedValues(value.selectedValues, optionValue, selectionMode);\n setValue({ ...value, selectedValues });\n },\n [selectionMode, setValue, value],\n );\n\n const contextValue = useMemo(\n () => ({\n disabled,\n selectedValues: value.selectedValues,\n selectionMode,\n setText,\n text: value.text,\n toggleValue,\n }),\n [disabled, selectionMode, setText, toggleValue, value],\n );\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (disabled) {\n return;\n }\n\n const text = value.text.trim();\n if (value.selectedValues.length === 0 && text.length === 0) {\n return;\n }\n\n await onSubmit?.(\n {\n selectedValues: value.selectedValues,\n text: text.length > 0 ? text : undefined,\n },\n event,\n );\n },\n [disabled, onSubmit, value],\n );\n\n return (\n <QuestionContext.Provider value={contextValue}>\n <form\n className={cn("space-y-3 rounded-xl border bg-card p-4", className)}\n onSubmit={handleSubmit}\n {...props}\n >\n {children}\n </form>\n </QuestionContext.Provider>\n );\n};\n\nexport type QuestionPromptProps = HTMLAttributes<HTMLParagraphElement>;\n\nexport const QuestionPrompt = ({ className, ...props }: QuestionPromptProps) => (\n <p className={cn("font-medium text-sm leading-snug", className)} {...props} />\n);\n\nexport type QuestionDescriptionProps = HTMLAttributes<HTMLParagraphElement>;\n\nexport const QuestionDescription = ({ className, ...props }: QuestionDescriptionProps) => (\n <p className={cn("text-muted-foreground text-sm", className)} {...props} />\n);\n\nexport type QuestionOptionsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const QuestionOptions = ({ className, ...props }: QuestionOptionsProps) => {\n const { selectionMode } = useQuestion();\n\n return (\n <div\n className={cn("flex flex-wrap gap-1.5", className)}\n role={selectionMode === "single" ? "radiogroup" : "group"}\n {...props}\n />\n );\n};\n\nexport type QuestionOptionProps = Omit<ComponentProps<typeof Button>, "value"> & {\n value: string;\n};\n\nexport const QuestionOption = ({\n children,\n className,\n disabled,\n onClick,\n value,\n variant,\n ...props\n}: QuestionOptionProps) => {\n const question = useQuestion();\n const isSelected = question.selectedValues.includes(value);\n const role = question.selectionMode === "single" ? "radio" : "checkbox";\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n question.toggleValue(value);\n onClick?.(event);\n },\n [onClick, question, value],\n );\n\n return (\n <Button\n aria-checked={isSelected}\n // Selection only changes colors: the border is always present so the\n // layout never shifts when an option is picked.\n className={cn(\n "group/option h-auto whitespace-normal border border-input font-normal shadow-none transition-colors",\n isSelected\n ? "border-foreground/20 bg-accent text-accent-foreground disabled:opacity-100"\n : "text-muted-foreground hover:bg-accent/50 hover:text-foreground",\n className,\n )}\n data-state={isSelected ? "checked" : "unchecked"}\n disabled={question.disabled || disabled}\n onClick={handleClick}\n role={role}\n type="button"\n variant={variant ?? "ghost"}\n {...props}\n >\n {children ?? value}\n </Button>\n );\n};\n\nexport type QuestionInputProps = Omit<ComponentProps<typeof Textarea>, "defaultValue" | "value">;\n\nexport const QuestionInput = ({\n className,\n disabled,\n onChange,\n onKeyDown,\n ...props\n}: QuestionInputProps) => {\n const question = useQuestion();\n const handleChange = useCallback(\n (event: ChangeEvent<HTMLTextAreaElement>) => {\n question.setText(event.currentTarget.value);\n onChange?.(event);\n },\n [onChange, question],\n );\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLTextAreaElement>) => {\n onKeyDown?.(event);\n if (\n event.defaultPrevented ||\n event.key !== "Enter" ||\n event.shiftKey ||\n event.nativeEvent.isComposing\n ) {\n return;\n }\n\n event.preventDefault();\n event.currentTarget.form?.requestSubmit();\n },\n [onKeyDown],\n );\n\n return (\n <Textarea\n className={cn(\n "min-h-16 resize-none rounded-lg text-sm shadow-none focus-visible:border-foreground!",\n className,\n )}\n disabled={question.disabled || disabled}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n value={question.text}\n {...props}\n />\n );\n};\n\nexport type QuestionActionsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const QuestionActions = ({ className, ...props }: QuestionActionsProps) => (\n <div className={cn("flex items-center justify-end gap-2", className)} {...props} />\n);\n\nexport type QuestionSubmitProps = ComponentProps<typeof Button> & {\n children?: ReactNode;\n};\n\nexport const QuestionSubmit = ({\n children = "Submit",\n className,\n disabled,\n size = "sm",\n ...props\n}: QuestionSubmitProps) => {\n const question = useQuestion();\n const hasResponse = question.selectedValues.length > 0 || question.text.trim().length > 0;\n\n return (\n <Button\n className={cn("text-sm! shadow-none", className)}\n disabled={question.disabled || disabled || !hasResponse}\n size={size}\n type="submit"\n {...props}\n >\n {children}\n </Button>\n );\n};\n';
|
|
18
18
|
readonly "components/ai-elements/reasoning.tsx": '"use client";\n\nimport { useControllableState } from "@radix-ui/react-use-controllable-state";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";\nimport { cn } from "@/lib/utils";\nimport { cjk } from "@streamdown/cjk";\nimport { code } from "@streamdown/code";\nimport { math } from "@streamdown/math";\nimport { mermaid } from "@streamdown/mermaid";\nimport { BrainIcon, ChevronDownIcon } from "lucide-react";\nimport type { ComponentProps, ReactNode } from "react";\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from "react";\nimport { Streamdown } from "streamdown";\n\nimport { Shimmer } from "./shimmer";\n\ninterface ReasoningContextValue {\n isStreaming: boolean;\n isOpen: boolean;\n setIsOpen: (open: boolean) => void;\n duration: number | undefined;\n}\n\nconst ReasoningContext = createContext<ReasoningContextValue | null>(null);\n\nexport const useReasoning = () => {\n const context = useContext(ReasoningContext);\n if (!context) {\n throw new Error("Reasoning components must be used within Reasoning");\n }\n return context;\n};\n\nexport type ReasoningProps = ComponentProps<typeof Collapsible> & {\n isStreaming?: boolean;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n duration?: number;\n};\n\nconst MS_IN_S = 1000;\n\nexport const Reasoning = memo(\n ({\n className,\n isStreaming = false,\n open,\n defaultOpen,\n onOpenChange,\n duration: durationProp,\n children,\n ...props\n }: ReasoningProps) => {\n const resolvedDefaultOpen = defaultOpen ?? isStreaming;\n // Track if defaultOpen was explicitly set to false (to prevent auto-open)\n const isExplicitlyClosed = defaultOpen === false;\n\n const [isOpen, setIsOpen] = useControllableState<boolean>({\n defaultProp: resolvedDefaultOpen,\n onChange: onOpenChange,\n prop: open,\n });\n const [duration, setDuration] = useControllableState<number | undefined>({\n defaultProp: undefined,\n prop: durationProp,\n });\n\n const hasEverStreamedRef = useRef(isStreaming);\n const [hasAutoClosed, setHasAutoClosed] = useState(false);\n const startTimeRef = useRef<number | null>(null);\n\n // Track when streaming starts and compute duration\n useEffect(() => {\n if (isStreaming) {\n hasEverStreamedRef.current = true;\n if (startTimeRef.current === null) {\n startTimeRef.current = Date.now();\n }\n } else if (startTimeRef.current !== null) {\n setDuration(Math.ceil((Date.now() - startTimeRef.current) / MS_IN_S));\n startTimeRef.current = null;\n }\n }, [isStreaming, setDuration]);\n\n // Auto-open when streaming starts (unless explicitly closed)\n useEffect(() => {\n if (isStreaming && !isOpen && !isExplicitlyClosed) {\n setIsOpen(true);\n }\n }, [isStreaming, isOpen, setIsOpen, isExplicitlyClosed]);\n\n // Auto-close when streaming ends (once only, and only if it ever streamed)\n useEffect(() => {\n if (hasEverStreamedRef.current && !isStreaming && isOpen && !hasAutoClosed) {\n setIsOpen(false);\n setHasAutoClosed(true);\n }\n }, [isStreaming, isOpen, setIsOpen, hasAutoClosed]);\n\n const handleOpenChange = useCallback(\n (newOpen: boolean) => {\n setIsOpen(newOpen);\n },\n [setIsOpen],\n );\n\n const contextValue = useMemo(\n () => ({ duration, isOpen, isStreaming, setIsOpen }),\n [duration, isOpen, isStreaming, setIsOpen],\n );\n\n return (\n <ReasoningContext.Provider value={contextValue}>\n <Collapsible\n className={cn("not-prose mb-4 w-full", className)}\n onOpenChange={handleOpenChange}\n open={isOpen}\n {...props}\n >\n {children}\n </Collapsible>\n </ReasoningContext.Provider>\n );\n },\n);\n\nexport type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {\n getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode;\n};\n\nconst defaultGetThinkingMessage = (isStreaming: boolean, duration?: number) => {\n if (isStreaming || duration === 0) {\n return <Shimmer duration={1}>Thinking...</Shimmer>;\n }\n if (duration === undefined) {\n return <p>Thought for a few seconds</p>;\n }\n return <p>Thought for {duration} seconds</p>;\n};\n\nexport const ReasoningTrigger = memo(\n ({\n className,\n children,\n getThinkingMessage = defaultGetThinkingMessage,\n ...props\n }: ReasoningTriggerProps) => {\n const { isStreaming, isOpen, duration } = useReasoning();\n\n return (\n <CollapsibleTrigger\n className={cn(\n "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",\n className,\n )}\n {...props}\n >\n {children ?? (\n <>\n <BrainIcon className="size-4" />\n {getThinkingMessage(isStreaming, duration)}\n <ChevronDownIcon\n className={cn("size-4 transition-transform", isOpen ? "rotate-180" : "rotate-0")}\n />\n </>\n )}\n </CollapsibleTrigger>\n );\n },\n);\n\nexport type ReasoningContentProps = ComponentProps<typeof CollapsibleContent> & {\n children: string;\n};\n\nconst streamdownPlugins = { cjk, code, math, mermaid };\n\nexport const ReasoningContent = memo(({ className, children, ...props }: ReasoningContentProps) => (\n <CollapsibleContent\n className={cn(\n "mt-4 text-sm",\n "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",\n className,\n )}\n {...props}\n >\n <Streamdown plugins={streamdownPlugins}>{children}</Streamdown>\n </CollapsibleContent>\n));\n\nReasoning.displayName = "Reasoning";\nReasoningTrigger.displayName = "ReasoningTrigger";\nReasoningContent.displayName = "ReasoningContent";\n';
|
|
19
19
|
readonly "components/ai-elements/shimmer.tsx": '"use client";\n\nimport { cn } from "@/lib/utils";\nimport type { MotionProps } from "motion/react";\nimport { motion } from "motion/react";\nimport type { CSSProperties, ElementType, JSX } from "react";\nimport { memo, useMemo } from "react";\n\ntype MotionHTMLProps = MotionProps & Record<string, unknown>;\n\n// Cache motion components at module level to avoid creating during render\nconst motionComponentCache = new Map<\n keyof JSX.IntrinsicElements,\n React.ComponentType<MotionHTMLProps>\n>();\n\nconst getMotionComponent = (element: keyof JSX.IntrinsicElements) => {\n let component = motionComponentCache.get(element);\n if (!component) {\n component = motion.create(element);\n motionComponentCache.set(element, component);\n }\n return component;\n};\n\nexport interface TextShimmerProps {\n children: string;\n as?: ElementType;\n className?: string;\n duration?: number;\n spread?: number;\n}\n\nconst ShimmerComponent = ({\n children,\n as: Component = "p",\n className,\n duration = 2,\n spread = 2,\n}: TextShimmerProps) => {\n const MotionComponent = getMotionComponent(Component as keyof JSX.IntrinsicElements);\n\n const dynamicSpread = useMemo(() => (children?.length ?? 0) * spread, [children, spread]);\n\n return (\n <MotionComponent\n animate={{ backgroundPosition: "0% center" }}\n className={cn(\n "relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",\n "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",\n className,\n )}\n initial={{ backgroundPosition: "100% center" }}\n style={\n {\n "--spread": `${dynamicSpread}px`,\n backgroundImage:\n "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))",\n } as CSSProperties\n }\n transition={{\n duration,\n ease: "linear",\n repeat: Number.POSITIVE_INFINITY,\n }}\n >\n {children}\n </MotionComponent>\n );\n};\n\nexport const Shimmer = memo(ShimmerComponent);\n';
|
|
20
|
-
readonly "components/ai-elements/tool.tsx": '"use client";\n\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";\nimport { cn } from "@/lib/utils";\nimport type { DynamicToolUIPart, ToolUIPart } from "ai";\nimport { ChevronRightIcon, TerminalIcon, WrenchIcon } from "lucide-react";\nimport type { ComponentProps, ReactNode } from "react";\nimport { isValidElement } from "react";\n\nimport { CodeBlock } from "./code-block";\n\nconst compactCodeBlockClassName =\n "rounded-none border-0 bg-transparent [&_pre]:!bg-transparent [&_pre]:px-3 [&_pre]:pt-2 [&_pre]:pb-3 [&_pre]:text-xs [&_code]:text-xs";\n\nexport type ToolProps = ComponentProps<typeof Collapsible>;\n\nexport const Tool = ({ className, ...props }: ToolProps) => (\n <Collapsible className={cn("group not-prose w-full", className)} {...props} />\n);\n\nexport type ToolPart = ToolUIPart | DynamicToolUIPart;\n\nexport type ToolHeaderProps = {\n title?: string;\n className?: string;\n} & (\n | { type: ToolUIPart["type"]; state: ToolUIPart["state"]; toolName?: never }\n | {\n type: DynamicToolUIPart["type"];\n state: DynamicToolUIPart["state"];\n toolName: string;\n }\n);\n\nconst statusLabels: Record<ToolPart["state"], string> = {\n "approval-requested": "Awaiting Approval",\n "approval-responded": "Responded",\n "input-available": "Running",\n "input-streaming": "Pending",\n "output-available": "Completed",\n "output-denied": "Denied",\n "output-error": "Error",\n};\n\nexport const getStatusIndicator = (status: ToolPart["state"]) =>\n status === "output-available" ? null : (\n <span className={cn("text-sm", status === "output-error" && "text-destructive")}>\n {statusLabels[status]}\n </span>\n );\n\nexport const ToolHeader = ({\n className,\n title,\n type,\n state,\n toolName,\n ...props\n}: ToolHeaderProps) => {\n const derivedName = type === "dynamic-tool" ? toolName : type.split("-").slice(1).join("-");\n const displayName = title ?? derivedName;\n\n return (\n <CollapsibleTrigger\n className={cn(\n "flex w-full items-center gap-2 py-0.5 text-left text-muted-foreground transition-colors hover:text-foreground",\n className,\n )}\n {...props}\n >\n {displayName === "bash" ? (\n <TerminalIcon className="size-4 shrink-0" />\n ) : (\n <WrenchIcon className="size-4 shrink-0" />\n )}\n <span className="text-sm">{displayName}</span>\n {getStatusIndicator(state)}\n <ChevronRightIcon className="size-3.5 shrink-0 transition-transform group-data-[state=open]:rotate-90" />\n </CollapsibleTrigger>\n );\n};\n\nexport type ToolContentProps = ComponentProps<typeof CollapsibleContent>;\n\nexport const ToolContent = ({ className, ...props }: ToolContentProps) => (\n <CollapsibleContent\n className={cn(\n "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 py-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",\n className,\n )}\n {...props}\n />\n);\n\nexport type BashToolContentProps = ComponentProps<"div"> & {\n input: ToolPart["input"];\n output: ToolPart["output"];\n errorText: ToolPart["errorText"];\n};\n\nexport const BashToolContent = ({\n className,\n input,\n output,\n errorText,\n ...props\n}: BashToolContentProps) => {\n const command = getRecordValue(input, "command");\n const stdout = getRecordValue(output, "stdout") ?? (typeof output === "string" ? output : "");\n const stderr = getRecordValue(output, "stderr") ?? errorText ?? "";\n const exitCode = getRecordValue(output, "exitCode");\n const hasResult = Boolean(stdout || stderr || (typeof exitCode === "number" && exitCode !== 0));\n\n return (\n <div className={cn("space-y-2", className)} {...props}>\n <pre className="overflow-x-auto whitespace-pre-wrap rounded-md bg-muted/50 p-3 font-mono text-xs leading-relaxed">\n <code>\n <span className="text-muted-foreground">$ </span>\n {command ?? "…"}\n </code>\n </pre>\n {hasResult ? (\n <pre className="overflow-x-auto whitespace-pre-wrap rounded-md bg-muted/50 p-3 font-mono text-xs leading-relaxed">\n <code>\n <span className="mb-2 block font-sans text-[10px] text-muted-foreground uppercase tracking-wide">\n Output\n </span>\n {stdout ? <span className="block">{stdout.trimEnd()}</span> : null}\n {stderr ? <span className="block text-destructive">{stderr.trimEnd()}</span
|
|
20
|
+
readonly "components/ai-elements/tool.tsx": '"use client";\n\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";\nimport { cn } from "@/lib/utils";\nimport type { DynamicToolUIPart, ToolUIPart } from "ai";\nimport { ChevronRightIcon, TerminalIcon, WrenchIcon } from "lucide-react";\nimport type { ComponentProps, ReactNode } from "react";\nimport { isValidElement } from "react";\n\nimport { CodeBlock } from "./code-block";\n\nconst compactCodeBlockClassName =\n "rounded-none border-0 bg-transparent [&_pre]:!bg-transparent [&_pre]:px-3 [&_pre]:pt-2 [&_pre]:pb-3 [&_pre]:text-xs [&_code]:text-xs";\n\nexport type ToolProps = ComponentProps<typeof Collapsible>;\n\nexport const Tool = ({ className, ...props }: ToolProps) => (\n <Collapsible className={cn("group not-prose w-full", className)} {...props} />\n);\n\nexport type ToolPart = ToolUIPart | DynamicToolUIPart;\n\nexport type ToolHeaderProps = {\n title?: string;\n className?: string;\n} & (\n | { type: ToolUIPart["type"]; state: ToolUIPart["state"]; toolName?: never }\n | {\n type: DynamicToolUIPart["type"];\n state: DynamicToolUIPart["state"];\n toolName: string;\n }\n);\n\nconst statusLabels: Record<ToolPart["state"], string> = {\n "approval-requested": "Awaiting Approval",\n "approval-responded": "Responded",\n "input-available": "Running",\n "input-streaming": "Pending",\n "output-available": "Completed",\n "output-denied": "Denied",\n "output-error": "Error",\n};\n\nexport const getStatusIndicator = (status: ToolPart["state"]) =>\n status === "output-available" ? null : (\n <span className={cn("text-sm", status === "output-error" && "text-destructive")}>\n {statusLabels[status]}\n </span>\n );\n\nexport const ToolHeader = ({\n className,\n title,\n type,\n state,\n toolName,\n ...props\n}: ToolHeaderProps) => {\n const derivedName = type === "dynamic-tool" ? toolName : type.split("-").slice(1).join("-");\n const displayName = title ?? derivedName;\n\n return (\n <CollapsibleTrigger\n className={cn(\n "flex w-full items-center gap-2 py-0.5 text-left text-muted-foreground transition-colors hover:text-foreground",\n className,\n )}\n {...props}\n >\n {displayName === "bash" ? (\n <TerminalIcon className="size-4 shrink-0" />\n ) : (\n <WrenchIcon className="size-4 shrink-0" />\n )}\n <span className="text-sm">{displayName}</span>\n {getStatusIndicator(state)}\n <ChevronRightIcon className="size-3.5 shrink-0 transition-transform group-data-[state=open]:rotate-90" />\n </CollapsibleTrigger>\n );\n};\n\nexport type ToolContentProps = ComponentProps<typeof CollapsibleContent>;\n\nexport const ToolContent = ({ className, ...props }: ToolContentProps) => (\n <CollapsibleContent\n className={cn(\n "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 py-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",\n className,\n )}\n {...props}\n />\n);\n\nexport type BashToolContentProps = ComponentProps<"div"> & {\n input: ToolPart["input"];\n output: ToolPart["output"];\n errorText: ToolPart["errorText"];\n};\n\nexport const BashToolContent = ({\n className,\n input,\n output,\n errorText,\n ...props\n}: BashToolContentProps) => {\n const command = getRecordValue(input, "command");\n const stdout = getRecordValue(output, "stdout") ?? (typeof output === "string" ? output : "");\n const stderr = getRecordValue(output, "stderr") ?? errorText ?? "";\n const exitCode = getRecordValue(output, "exitCode");\n const hasResult = Boolean(stdout || stderr || (typeof exitCode === "number" && exitCode !== 0));\n\n return (\n <div className={cn("space-y-2", className)} {...props}>\n <pre className="overflow-x-auto whitespace-pre-wrap rounded-md bg-muted/50 p-3 font-mono text-xs leading-relaxed">\n <code>\n <span className="text-muted-foreground">$ </span>\n {command ?? "…"}\n </code>\n </pre>\n {hasResult ? (\n <pre className="overflow-x-auto whitespace-pre-wrap rounded-md bg-muted/50 p-3 font-mono text-xs leading-relaxed">\n <code>\n <span className="mb-2 block font-sans text-[10px] text-muted-foreground uppercase tracking-wide">\n Output\n </span>\n {stdout ? <span className="block">{String(stdout).trimEnd()}</span> : null}\n {stderr ? (\n <span className="block text-destructive">{String(stderr).trimEnd()}</span>\n ) : null}\n {typeof exitCode === "number" && exitCode !== 0 ? (\n <span className="block text-muted-foreground">Exited with code {exitCode}</span>\n ) : null}\n </code>\n </pre>\n ) : null}\n </div>\n );\n};\n\nconst getRecordValue = (value: unknown, key: string): string | number | undefined => {\n if (typeof value !== "object" || value === null || !(key in value)) {\n return undefined;\n }\n\n const property = value[key as keyof typeof value];\n return typeof property === "string" || typeof property === "number" ? property : undefined;\n};\n\nexport type ToolInputProps = ComponentProps<"div"> & {\n input: ToolPart["input"];\n};\n\nexport const ToolInput = ({ className, input, ...props }: ToolInputProps) => (\n <div className={cn("overflow-hidden rounded-md bg-muted/50", className)} {...props}>\n <span className="block px-3 pt-3 font-sans text-[10px] text-muted-foreground uppercase tracking-wide">\n Parameters\n </span>\n <div>\n <CodeBlock\n className={compactCodeBlockClassName}\n code={JSON.stringify(input, null, 2)}\n language="json"\n />\n </div>\n </div>\n);\n\nexport type ToolOutputProps = ComponentProps<"div"> & {\n output: ToolPart["output"];\n errorText: ToolPart["errorText"];\n};\n\nexport const ToolOutput = ({ className, output, errorText, ...props }: ToolOutputProps) => {\n if (!(output || errorText)) {\n return null;\n }\n\n let Output = <div>{output as ReactNode}</div>;\n\n if (typeof output === "object" && !isValidElement(output)) {\n Output = (\n <CodeBlock\n className={compactCodeBlockClassName}\n code={JSON.stringify(output, null, 2)}\n language="json"\n />\n );\n } else if (typeof output === "string") {\n Output = <CodeBlock className={compactCodeBlockClassName} code={output} language="json" />;\n }\n\n return (\n <div\n className={cn(\n "overflow-x-auto rounded-md text-xs [&_table]:w-full",\n errorText ? "bg-destructive/10 text-destructive" : "bg-muted/50 text-foreground",\n className,\n )}\n {...props}\n >\n <span className="block px-3 pt-3 font-sans text-[10px] text-muted-foreground uppercase tracking-wide">\n {errorText ? "Error" : "Result"}\n </span>\n {errorText && <div className="px-3 pt-2 pb-3">{errorText}</div>}\n {Output}\n </div>\n );\n};\n';
|
|
21
21
|
readonly "components/ui/badge.tsx": 'import * as React from "react";\nimport { cva, type VariantProps } from "class-variance-authority";\nimport { Slot } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nconst badgeVariants = cva(\n "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",\n {\n variants: {\n variant: {\n default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",\n secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",\n destructive:\n "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",\n outline:\n "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",\n ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",\n link: "text-primary underline-offset-4 [a&]:hover:underline",\n },\n },\n defaultVariants: {\n variant: "default",\n },\n },\n);\n\nfunction Badge({\n className,\n variant = "default",\n asChild = false,\n ...props\n}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot.Root : "span";\n\n return (\n <Comp\n data-slot="badge"\n data-variant={variant}\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n );\n}\n\nexport { Badge, badgeVariants };\n';
|
|
22
22
|
readonly "components/ui/button-group.tsx": 'import { cva, type VariantProps } from "class-variance-authority";\nimport { Slot } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\nimport { Separator } from "@/components/ui/separator";\n\nconst buttonGroupVariants = cva(\n "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*=\'w-\'])]:w-fit [&>input]:flex-1",\n {\n variants: {\n orientation: {\n horizontal:\n "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",\n vertical:\n "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",\n },\n },\n defaultVariants: {\n orientation: "horizontal",\n },\n },\n);\n\nfunction ButtonGroup({\n className,\n orientation,\n ...props\n}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {\n return (\n <div\n role="group"\n data-slot="button-group"\n data-orientation={orientation}\n className={cn(buttonGroupVariants({ orientation }), className)}\n {...props}\n />\n );\n}\n\nfunction ButtonGroupText({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<"div"> & {\n asChild?: boolean;\n}) {\n const Comp = asChild ? Slot.Root : "div";\n\n return (\n <Comp\n className={cn(\n "flex items-center gap-2 rounded-md border bg-muted px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction ButtonGroupSeparator({\n className,\n orientation = "vertical",\n ...props\n}: React.ComponentProps<typeof Separator>) {\n return (\n <Separator\n data-slot="button-group-separator"\n orientation={orientation}\n className={cn(\n "relative m-0! self-stretch bg-input data-[orientation=vertical]:h-auto",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };\n';
|
|
23
23
|
readonly "components/ui/button.tsx": 'import * as React from "react";\nimport { cva, type VariantProps } from "class-variance-authority";\nimport { Slot } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nconst buttonVariants = cva(\n "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4",\n {\n variants: {\n variant: {\n default: "bg-primary text-primary-foreground hover:bg-primary/90",\n destructive:\n "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",\n outline:\n "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",\n secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",\n ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",\n link: "text-primary underline-offset-4 hover:underline",\n },\n size: {\n default: "h-9 px-4 py-2 has-[>svg]:px-3",\n xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*=\'size-\'])]:size-3",\n sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",\n lg: "h-10 rounded-md px-6 has-[>svg]:px-4",\n icon: "size-9",\n "icon-xs": "size-6 rounded-md [&_svg:not([class*=\'size-\'])]:size-3",\n "icon-sm": "size-8",\n "icon-lg": "size-10",\n },\n },\n defaultVariants: {\n variant: "default",\n size: "default",\n },\n },\n);\n\nfunction Button({\n className,\n variant = "default",\n size = "default",\n asChild = false,\n ...props\n}: React.ComponentProps<"button"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : "button";\n\n return (\n <Comp\n data-slot="button"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n';
|
|
@@ -4319,8 +4319,10 @@ export const BashToolContent = ({
|
|
|
4319
4319
|
<span className="mb-2 block font-sans text-[10px] text-muted-foreground uppercase tracking-wide">
|
|
4320
4320
|
Output
|
|
4321
4321
|
</span>
|
|
4322
|
-
{stdout ? <span className="block">{stdout.trimEnd()}</span> : null}
|
|
4323
|
-
{stderr ?
|
|
4322
|
+
{stdout ? <span className="block">{String(stdout).trimEnd()}</span> : null}
|
|
4323
|
+
{stderr ? (
|
|
4324
|
+
<span className="block text-destructive">{String(stderr).trimEnd()}</span>
|
|
4325
|
+
) : null}
|
|
4324
4326
|
{typeof exitCode === "number" && exitCode !== 0 ? (
|
|
4325
4327
|
<span className="block text-muted-foreground">Exited with code {exitCode}</span>
|
|
4326
4328
|
) : null}
|
|
@@ -11,6 +11,11 @@ export interface SandboxBackendHandle<SO = Record<string, never>> {
|
|
|
11
11
|
readonly session: SandboxSession;
|
|
12
12
|
readonly useSessionFn: SandboxSessionUseFn<SO>;
|
|
13
13
|
captureState(): Promise<SandboxBackendSessionState>;
|
|
14
|
+
/**
|
|
15
|
+
* Permanently deletes this sandbox and its disposable backend state.
|
|
16
|
+
* Reusable or shared template state must not be deleted.
|
|
17
|
+
*/
|
|
18
|
+
delete(options?: SandboxDeleteOptions): Promise<void>;
|
|
14
19
|
/**
|
|
15
20
|
* Stops the underlying compute at an authored runtime boundary while
|
|
16
21
|
* preserving any backend state needed to reopen the durable session.
|
|
@@ -25,6 +30,10 @@ export interface SandboxBackendHandle<SO = Record<string, never>> {
|
|
|
25
30
|
*/
|
|
26
31
|
shutdown(): Promise<void>;
|
|
27
32
|
}
|
|
33
|
+
/** Options for permanently deleting one sandbox. */
|
|
34
|
+
export interface SandboxDeleteOptions {
|
|
35
|
+
readonly abortSignal?: AbortSignal;
|
|
36
|
+
}
|
|
28
37
|
/**
|
|
29
38
|
* Serializable per-sandbox reconnect record stored on the harness session.
|
|
30
39
|
*
|
|
@@ -26,12 +26,13 @@ export interface SandboxBootstrapContext<O = Record<string, never>> {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Argument passed to a sandbox `onSession` hook, invoked once per live
|
|
29
|
-
* session. `ctx`
|
|
30
|
-
*
|
|
31
|
-
* `options` (typed by the backend's session-use
|
|
29
|
+
* session. `ctx` contains the active session metadata; sandbox access during
|
|
30
|
+
* initialization goes through `use(options?)`, which opens the live session
|
|
31
|
+
* and applies backend-specific `options` (typed by the backend's session-use
|
|
32
|
+
* options `SO`).
|
|
32
33
|
*/
|
|
33
34
|
export interface SandboxSessionContext<O = Record<string, never>> {
|
|
34
|
-
readonly ctx: SessionContext
|
|
35
|
+
readonly ctx: Pick<SessionContext, "session">;
|
|
35
36
|
readonly use: SandboxSessionUseFn<O>;
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Experimental_SandboxSession as AiSdkSandbox } from "ai";
|
|
2
|
+
import type { SandboxDeleteOptions } from "#shared/sandbox-backend.js";
|
|
2
3
|
import type { SandboxNetworkPolicy } from "#shared/sandbox-network-policy.js";
|
|
3
4
|
/**
|
|
4
5
|
* Options for running one command in a sandbox. Shape mirrors the AI
|
|
@@ -124,9 +125,11 @@ export interface SandboxSession extends Pick<AiSdkSandbox, "run" | "spawn" | "re
|
|
|
124
125
|
* `ctx.getSandbox()`.
|
|
125
126
|
*
|
|
126
127
|
* Unlike the I/O-only session used during sandbox initialization, this handle
|
|
127
|
-
*
|
|
128
|
+
* exposes provider-backed lifecycle operations.
|
|
128
129
|
*/
|
|
129
130
|
export interface RuntimeSandboxSession extends SandboxSession {
|
|
131
|
+
/** Permanently deletes this sandbox and its disposable backend state. */
|
|
132
|
+
delete(options?: SandboxDeleteOptions): Promise<void>;
|
|
130
133
|
/**
|
|
131
134
|
* Stops the backing sandbox compute while preserving the durable session.
|
|
132
135
|
* A later runtime callback reopens the session through its configured
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface DeliveryPolicy {
|
|
2
|
+
readonly allowsEmptyDelivery: boolean;
|
|
3
|
+
readonly instruction?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Resolves one policy for both model prompting and empty-response recovery. */
|
|
6
|
+
export declare function resolveDeliveryPolicy(input: {
|
|
7
|
+
readonly hasOutputSchema: boolean;
|
|
8
|
+
readonly isChild: boolean;
|
|
9
|
+
readonly isFirstTurn: boolean;
|
|
10
|
+
readonly hasScheduleProvenance: boolean;
|
|
11
|
+
readonly taskDeliveryPhase: "none" | "initiating" | "pending" | "settled" | undefined;
|
|
12
|
+
}): DeliveryPolicy;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{TASK_DELIVERY_INITIATING_INSTRUCTION,TASK_DELIVERY_PENDING_INSTRUCTION,TASK_DELIVERY_SETTLED_INSTRUCTION}from"#tasks/delivery-context.js";import{CONDITIONAL_DELIVERY_INSTRUCTION}from"#shared/empty-delivery.js";const POLICIES={conditional:{allowsEmptyDelivery:!0,instruction:CONDITIONAL_DELIVERY_INSTRUCTION},initiating:{allowsEmptyDelivery:!1,instruction:TASK_DELIVERY_INITIATING_INSTRUCTION},normal:{allowsEmptyDelivery:!1},pending:{allowsEmptyDelivery:!0,instruction:TASK_DELIVERY_PENDING_INSTRUCTION},settled:{allowsEmptyDelivery:!1,instruction:TASK_DELIVERY_SETTLED_INSTRUCTION}};function resolveDeliveryPolicy(e){return e.hasOutputSchema||e.isChild?POLICIES.normal:e.taskDeliveryPhase===`pending`?POLICIES.pending:e.taskDeliveryPhase===`settled`?POLICIES.settled:e.isFirstTurn&&e.hasScheduleProvenance?POLICIES.conditional:e.taskDeliveryPhase===`initiating`?POLICIES.initiating:POLICIES.normal}export{resolveDeliveryPolicy};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeChannelAudience}from"#shared/channel-audience.js";import{
|
|
1
|
+
import{normalizeChannelAudience}from"#shared/channel-audience.js";import{contextStorage,loadContext}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readSessionTraceContext(e,t){let n=e[AgentTraceContextKey.name];if(n===void 0)return;let r=deserializeState(n).sessions[t]?.context;return r===void 0?void 0:withTraceDecision(e,r)}function readActionTraceContext(e,t,n,r){let i=e[AgentTraceContextKey.name];if(i===void 0)return;let a=Object.values(deserializeState(i).actions).find(e=>e.sessionId===t&&e.turnId===n&&e.callId===r);if(a!==void 0)return withTraceDecision(e,{isRemote:!1,spanId:a.spanId,traceFlags:a.parent.traceFlags,traceId:a.parent.traceId})}function withTraceDecision(e,t){let n=e[SessionTraceSeedKey.name];return n?.decision===void 0?t:{...t,decision:n.decision}}var ContextAgentTraceStateStore=class{deleteAction(e){updateState(t=>{let n={...t.actions};return delete n[e],{...t,actions:n}})}deleteActions(e,t){updateState(n=>{let r={...n.actions};for(let[n,i]of Object.entries(r))i.sessionId===e&&(t===void 0||i.turnId===t)&&delete r[n];return{...n,actions:r}})}deleteSession(e){updateState(t=>{let n={...t.sessions};return delete n[e],{...t,sessions:n}})}deleteTurn(e,t){updateState(n=>{let r={...n.turns};return delete r[turnKey(e,t)],{...n,turns:r}})}findAction(e,n){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.actions??{}).find(t=>t.sessionId===e&&t.callId===n)}getAction(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.actions[e]}getSession(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[e]}getTurn(e,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(e,n)]}setAction(e,t){updateState(n=>({...n,actions:{...n.actions,[e]:t}}))}setSession(e,t){updateState(n=>({...n,sessions:{...n.sessions,[e]:t}}))}setTurn(e,t,n){updateState(r=>({...r,turns:{...r.turns,[turnKey(e,t)]:n}}))}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??{actions:{},sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{actions:e.actions,sessions:Object.fromEntries(Object.entries(e.sessions).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context)}])),turns:Object.fromEntries(Object.entries(e.turns).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context),terminal:t.terminal===void 0?void 0:t.terminal.type===`turn.failed`?{error:serializeError(t.terminal.error),type:t.terminal.type}:{type:t.terminal.type}}]))}}function deserializeState(t){return isRecord(t)?{actions:deserializeRecord(t.actions,deserializeAction),sessions:deserializeRecord(t.sessions,t=>{if(!(!isRecord(t)||!isSpanContext(t.context)))return{agentName:typeof t.agentName==`string`?t.agentName:void 0,channelAudience:normalizeChannelAudience(t.channelAudience),channelKind:typeof t.channelKind==`string`?t.channelKind:void 0,context:t.context,rootSessionId:typeof t.rootSessionId==`string`?t.rootSessionId:``}}),turns:deserializeRecord(t.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context))&&!(typeof e.parentSpanId!=`string`||typeof e.startTimeMs!=`number`))return{context:e.context,parentIsRemote:typeof e.parentIsRemote==`boolean`?e.parentIsRemote:void 0,parentSpanId:e.parentSpanId,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,startTimeMs:e.startTimeMs,subagentName:typeof e.subagentName==`string`?e.subagentName:void 0,terminal:deserializeTerminal(e.terminal)}})}:{actions:{},sessions:{},turns:{}}}function deserializeAction(t){if(!(!isRecord(t)||typeof t.attemptIndex!=`number`||typeof t.callId!=`string`||!isActionKind(t.kind)||typeof t.name!=`string`||!isSpanContext(t.parent)||typeof t.rootSessionId!=`string`||typeof t.sessionId!=`string`||typeof t.spanId!=`string`||typeof t.startTimeMs!=`number`||typeof t.stepIndex!=`number`||typeof t.turnId!=`string`))return{attemptIndex:t.attemptIndex,callId:t.callId,channelAudience:normalizeChannelAudience(t.channelAudience),inputAttribute:typeof t.inputAttribute==`string`?t.inputAttribute:void 0,kind:t.kind,name:t.name,parent:t.parent,rootSessionId:t.rootSessionId,sessionId:t.sessionId,spanId:t.spanId,startTimeMs:t.startTimeMs,stepIndex:t.stepIndex,turnId:t.turnId}}function isActionKind(e){return e===`load-skill`||e===`remote-agent-call`||e===`subagent-call`||e===`tool-call`}function deserializeRecord(e,t){if(!isRecord(e))return{};let n={};for(let[r,i]of Object.entries(e)){let e=t(i);e!==void 0&&(n[r]=e)}return n}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return t===`turn.failed`?{error:deserializeError(e.error),type:t}:{type:t}}function serializeSpanContext(e){return{isRemote:e.isRemote,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function serializeError(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:void 0}function deserializeError(e){if(!isRecord(e)||typeof e.message!=`string`)return;let t=Error(e.message);return typeof e.name==`string`&&(t.name=e.name),typeof e.stack==`string`&&(t.stack=e.stack),t}function isTurnTerminalType(e){return e===`turn.cancelled`||e===`turn.completed`||e===`turn.failed`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readActionTraceContext,readSessionTraceContext};
|
package/docs/evals/cases.mdx
CHANGED
|
@@ -88,11 +88,17 @@ export default defineEval({
|
|
|
88
88
|
async test(t) {
|
|
89
89
|
const first = await t.send("My favorite word is marigold.");
|
|
90
90
|
|
|
91
|
-
const second = await t.send("
|
|
91
|
+
const second = await t.send("What is my favorite word?");
|
|
92
92
|
await t.require(second.sessionId, equals(first.sessionId));
|
|
93
93
|
|
|
94
94
|
t.succeeded();
|
|
95
|
-
second.messageIncludes("
|
|
95
|
+
second.messageIncludes("marigold");
|
|
96
|
+
|
|
97
|
+
t.judge.autoevals
|
|
98
|
+
.closedQA("The assistant remembers the user's favorite word across turns", {
|
|
99
|
+
on: t.transcript,
|
|
100
|
+
})
|
|
101
|
+
.atLeast(0.8);
|
|
96
102
|
},
|
|
97
103
|
});
|
|
98
104
|
```
|
|
@@ -109,6 +115,9 @@ export default defineEval({
|
|
|
109
115
|
- `t.respond(responses, options?)` answers specific pending input requests and sends them as the next turn.
|
|
110
116
|
- `t.respondAll(optionId)` answers every pending input request with the same option and sends the responses as the next turn.
|
|
111
117
|
- `t.reply` is the last assistant message (or `null`); `t.sessionId` is the current session id; `t.events` is the full typed event stream captured so far.
|
|
118
|
+
- `t.transcript` formats the primary session's observed user and assistant messages in turn order. Pass it as a judge's `on` value to grade the complete conversation. An independent session returned by `t.newSession()` exposes its own `session.transcript`.
|
|
119
|
+
|
|
120
|
+
The transcript uses `User:` and `Assistant:` labels separated by blank lines. It excludes reasoning, tool calls, tool results, and messages from other sessions. It updates after each turn settles, so read it after `await t.send(...)`, `await t.respond(...)`, or `await live.result()`.
|
|
112
121
|
|
|
113
122
|
Each `send` (and `respond`/`respondAll`) resolves to an immutable turn with `.message`, `.data`, `.events`, `.inputRequests`, `.toolCalls`, `.sessionId`, `.status`, and `.expectOk()`. Use `.sessionId` to relate turns or attach follow-up work to the session that produced a specific turn. `expectOk()` throws only when the turn ended failed; a session left open for a next message is the normal end state of a successful turn.
|
|
114
123
|
|
package/docs/evals/judge.mdx
CHANGED
|
@@ -48,6 +48,21 @@ const draft = await t.send("Draft the welcome email.");
|
|
|
48
48
|
t.judge.autoevals.closedQA("professional tone", { on: draft.message }).atLeast(0.6);
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
For a multi-turn eval, pass `t.transcript` to grade the primary session's complete observed conversation instead of only its final reply:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
await t.send("My favorite word is marigold. Remember it.");
|
|
55
|
+
await t.send("What is my favorite word?");
|
|
56
|
+
|
|
57
|
+
t.judge.autoevals
|
|
58
|
+
.closedQA("The assistant remembers the user's favorite word across turns", {
|
|
59
|
+
on: t.transcript,
|
|
60
|
+
})
|
|
61
|
+
.atLeast(0.8);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`t.transcript` contains the session's user and assistant messages in turn order. It excludes reasoning, tool calls, and tool results. See [Multi-turn evals](./cases#multi-turn-evals) for the transcript format and independent sessions.
|
|
65
|
+
|
|
51
66
|
## Soft scoring and thresholds
|
|
52
67
|
|
|
53
68
|
Judge assertions are soft, so the threshold rides on the assertion handle. There is no separate thresholds map:
|
package/docs/evals/overview.mdx
CHANGED
|
@@ -100,7 +100,7 @@ model: mockModel({
|
|
|
100
100
|
|
|
101
101
|
`t` is both the driver and the assertion surface. There are no separate `input`, `run`, `checks`, or `scores` fields. You write ordinary control flow, sending turns and asserting inline.
|
|
102
102
|
|
|
103
|
-
- **Drive** the agent: `t.send(...)`, `t.start(...)`, `t.cancel()`, `t.respond(...)`, `t.respondAll(...)`, `t.sendFile(...)`, `t.requireInputRequest(...)`, `t.newSession()`. Live turns returned by `start()` can wait for typed mid-turn events before cancellation or settlement. Read what came back with `t.reply` (the last assistant message), `t.sessionId`, and `t.events`. See [Cases](./cases).
|
|
103
|
+
- **Drive** the agent: `t.send(...)`, `t.start(...)`, `t.cancel()`, `t.respond(...)`, `t.respondAll(...)`, `t.sendFile(...)`, `t.requireInputRequest(...)`, `t.newSession()`. Live turns returned by `start()` can wait for typed mid-turn events before cancellation or settlement. Read what came back with `t.reply` (the last assistant message), `t.transcript` (the primary session's user and assistant messages), `t.sessionId`, and `t.events`. See [Cases](./cases).
|
|
104
104
|
- **Assert** with three surfaces, covered next.
|
|
105
105
|
|
|
106
106
|
## Three assertion surfaces
|
|
@@ -109,7 +109,7 @@ Each surface matches a genuinely different kind of judgment:
|
|
|
109
109
|
|
|
110
110
|
- **Scoped methods** read the final whole run on `t`, snapshot one independent session when invoked there, or inspect one immutable `EveEvalTurn`. See [Assertions](./assertions).
|
|
111
111
|
- **`t.check(value, assertion)`** grades an explicit value with a deterministic builder from `eve/evals/expect`, such as `t.check(t.reply, includes("sunny"))`. Grade `t.reply`, an intermediate draft, parsed JSON, or anything else. See [Assertions](./assertions).
|
|
112
|
-
- **`t.judge.autoevals.*`** is the LLM-as-judge surface, like `t.judge.autoevals.closedQA("cites a source")`. It grades `t.reply` by default
|
|
112
|
+
- **`t.judge.autoevals.*`** is the LLM-as-judge surface, like `t.judge.autoevals.closedQA("cites a source")`. It grades `t.reply` by default; pass `{ on: t.transcript }` to grade a multi-turn conversation. The judge uses the configured judge model, never the agent under test. See [Judge](./judge).
|
|
113
113
|
|
|
114
114
|
## Gate vs soft
|
|
115
115
|
|
|
@@ -163,6 +163,7 @@ Structural tags describe each run's place in the tree:
|
|
|
163
163
|
- `$eve.root`: session id of the root session in the chain (group a whole tree with `$eve.root=<id>`)
|
|
164
164
|
- `$eve.subagent`: compiled graph node id (subagent runs only)
|
|
165
165
|
- `$eve.trigger`: the channel kind that started the run
|
|
166
|
+
- `$eve.schedule`: the authored schedule that created the session, including sessions started through a target channel
|
|
166
167
|
- `$eve.title`: truncated title derived from the first user message
|
|
167
168
|
- `$eve.trace_id`: trace id of the sampled agent trace containing the run, written on session, subagent, and turn rows so a dashboard run can be joined to its OpenTelemetry trace. Present only when the trace is sampled; absence means no exported OTEL trace exists.
|
|
168
169
|
|
|
@@ -59,7 +59,7 @@ const sandbox = await ctx.getSandbox();
|
|
|
59
59
|
const result = await sandbox.run({ command: "npm test" });
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
The accessor is asynchronous because eve may need to bind or restore the sandbox. A subagent sees its own sandbox, not its parent's. The returned
|
|
62
|
+
The accessor is asynchronous because eve may need to bind or restore the sandbox. A subagent sees its own sandbox, not its parent's. The returned handle also exposes `stop()` and `delete()`; see [Sandbox lifecycle](../sandbox#lifecycle) for their behavior.
|
|
63
63
|
|
|
64
64
|
## `ctx.getSkill(identifier)`
|
|
65
65
|
|
|
@@ -82,7 +82,7 @@ Runtime context is available:
|
|
|
82
82
|
|
|
83
83
|
- inside `defineTool(...).execute(input, ctx)`;
|
|
84
84
|
- inside connection `auth` and `headers` resolvers;
|
|
85
|
-
- inside channel and hook callbacks that receive `ctx`;
|
|
85
|
+
- inside channel and agent hook callbacks that receive the full runtime `ctx`;
|
|
86
86
|
- after asynchronous boundaries within the same authored execution chain.
|
|
87
87
|
|
|
88
88
|
Runtime context is not available during top-level module evaluation, build scripts, or discovery. Declare reusable definitions and state handles at module scope, but call their context-dependent methods only from an eve-managed callback.
|
package/docs/sandbox.mdx
CHANGED
|
@@ -202,14 +202,14 @@ export default defineSandbox({
|
|
|
202
202
|
|
|
203
203
|
Custom commands are registered on live and reopened sessions, not during template prewarming. They run trusted host application code and create an explicit bridge from the sandbox to that code. Adapters can use this extension point to expose application APIs such as a Capable App's CLI; the outer eve `bash` tool does not automatically provide per-command Capable approvals.
|
|
204
204
|
|
|
205
|
-
You can also write your own backend. A `SandboxBackend` is an adapter object with a `name`, a `create`, and an optional `prewarm`. It can point at your own container runner, VM pool, internal sandbox service, or another isolation layer, as long as it returns the `SandboxSession` operations eve needs. Handles returned by `create` implement `
|
|
205
|
+
You can also write your own backend. A `SandboxBackend` is an adapter object with a `name`, a `create`, and an optional `prewarm`. It can point at your own container runner, VM pool, internal sandbox service, or another isolation layer, as long as it returns the `SandboxSession` operations eve needs. Handles returned by `create` implement `delete()`, `stop()`, and `shutdown()`. See the `SandboxBackend*` types on `eve/sandbox`.
|
|
206
206
|
|
|
207
207
|
## Lifecycle
|
|
208
208
|
|
|
209
209
|
There are two hooks, scoped differently:
|
|
210
210
|
|
|
211
211
|
- **`bootstrap({ use })`** is template-scoped and runs once when the template is built. Put reusable setup here that every later session inherits, such as cloning a baseline repo, installing dependencies, or seeding files. Call `use()` to get a `SandboxSession`. Only template filesystem state and supported backend metadata carry into later sessions; config like network policy does not. If external inputs affect what bootstrap produces, set `revalidationKey: () => string` so eve knows when to rebuild the template (authored sandbox source and seed contents are already tracked for you).
|
|
212
|
-
- **`onSession({ use, ctx })`** is durable-session-scoped and runs once per session (and again if a sandbox definition change replaces the session's sandbox). Put per-session setup here, including network policy, resources, timeout, per-user credentials, and one-time markers.
|
|
212
|
+
- **`onSession({ use, ctx })`** is durable-session-scoped and runs once per session (and again if a sandbox definition change replaces the session's sandbox). Put per-session setup here, including network policy, resources, timeout, per-user credentials, and one-time markers. `ctx` contains only the active session metadata, so it can identify the current principal without exposing runtime lifecycle operations while the sandbox is initializing. Call `use(opts?)` to get a `SandboxSession`; `opts` flow to the backend's update path after create.
|
|
213
213
|
|
|
214
214
|
If you require a network policy or other configuration for every session, configure it on the backend factory or in `onSession`; do not rely on bootstrap-only configuration.
|
|
215
215
|
|
|
@@ -248,6 +248,29 @@ stop-specific state is needed. Lifecycle `use()` calls return the I/O-only
|
|
|
248
248
|
`SandboxSession` because bootstrap and session initialization do not own runtime
|
|
249
249
|
teardown.
|
|
250
250
|
|
|
251
|
+
### Delete a sandbox
|
|
252
|
+
|
|
253
|
+
Permanently delete the current session sandbox from an authored runtime callback:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const sandbox = await ctx.getSandbox();
|
|
257
|
+
await sandbox.delete();
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
eve stops compute first, deletes the physical sandbox and its disposable backend state, and clears the saved reconnect state. It preserves reusable template state that contains `bootstrap` and seeded workspace files.
|
|
261
|
+
|
|
262
|
+
The durable eve session remains active. The next call to `ctx.getSandbox()` provisions a fresh workspace from the current sandbox definition and runs `onSession` again. Files and other workspace changes from the deleted sandbox are not restored.
|
|
263
|
+
|
|
264
|
+
Backend behavior differs:
|
|
265
|
+
|
|
266
|
+
- **Vercel Sandbox**: stops the persistent sandbox, deletes its record, and asks Vercel to delete snapshots that no other sandbox uses. Snapshot cleanup runs asynchronously.
|
|
267
|
+
- **microsandbox**: stops and removes the session VM and its persisted state snapshot
|
|
268
|
+
- **Docker and just-bash**: stop compute and discard their session runtime state
|
|
269
|
+
|
|
270
|
+
Only the session that owns a shared sandbox can delete it. Deleting the owner's sandbox affects every parent or child currently using it. If the backend rejects deletion, eve preserves the current reconnect state so you can retry.
|
|
271
|
+
|
|
272
|
+
Custom backend handles implement the same boundary in `delete()`: remove the session runtime and disposable persisted state without deleting reusable templates.
|
|
273
|
+
|
|
251
274
|
Session sandboxes are keyed per durable session, not per deployment, so redeploying your app does not by itself discard them. A definition change to the authored sandbox source, workspace seed content, or `revalidationKey` replaces the sandbox on the next turn and runs `onSession` again.
|
|
252
275
|
|
|
253
276
|
Reattachment still depends on the backend retaining its physical sandbox state. If a persisted Vercel sandbox is no longer available, eve creates a replacement, using the current template when one is configured. Files and other changes made after the original sandbox was created are not restored automatically. Because the durable session still has the same sandbox key, `onSession` does not run again for this replacement. Persist important artifacts outside the sandbox, and do not rely on `onSession` as the only place that applies security-critical configuration.
|
package/docs/schedules.mdx
CHANGED
|
@@ -84,6 +84,8 @@ The agent does not have to deliver a message on every run. When a prompt makes d
|
|
|
84
84
|
- `waitUntil(promise)`: extends the cron task's lifetime so the parked session and any in-flight fetches settle before the task ends. Wrap the `send` call in it.
|
|
85
85
|
- `appAuth`: the app principal (`{ authenticator: "app", principalId: "eve:app", principalType: "runtime" }`). Pass it as `to(...).send(..., { auth: appAuth })` for work the agent does on its own behalf.
|
|
86
86
|
|
|
87
|
+
The `auth` option controls which principal the session runs as. A handler may instead supply a user principal when scheduled work needs that user's grants; a session created by that dispatch still retains schedule provenance and conditional delivery behavior.
|
|
88
|
+
|
|
87
89
|
A handler-form session runs on the same durable runtime engine as any other session, so it can park (durably suspend), for instance when the channel handoff is waiting for a Slack reply. Only markdown task mode is barred from waiting.
|
|
88
90
|
|
|
89
91
|
## Session continuity
|
package/docs/tools/overview.mdx
CHANGED
|
@@ -72,6 +72,8 @@ framework-owned and is not yet a stable authored-tool API.
|
|
|
72
72
|
|
|
73
73
|
After the initiating turn accepts background tasks, eve supplies runtime-authored state for that
|
|
74
74
|
cohort and instructs the model to acknowledge that the work started without waiting for results.
|
|
75
|
+
[Schedule](/docs/schedules)-initiated turns are the exception: no user prompted them, so their
|
|
76
|
+
launches keep conditional delivery and send no acknowledgement.
|
|
75
77
|
Later task-triggered parent turns receive fresh state for the same cohort. The model is instructed
|
|
76
78
|
to keep related intermediate results silent while any task remains pending. Once every related
|
|
77
79
|
task is terminal, the state includes their outputs so the model can combine the useful results.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
|
|
6
6
|
"keywords": [
|
|
@@ -408,6 +408,7 @@
|
|
|
408
408
|
"@vercel/oidc": "3.8.0",
|
|
409
409
|
"@vercel/otel": "2.1.3",
|
|
410
410
|
"@vercel/sandbox": "2.8.0",
|
|
411
|
+
"@vercel/sandbox-delete": "npm:@vercel/sandbox@3.2.0",
|
|
411
412
|
"@vercel/sandbox-drives": "npm:@vercel/sandbox@2.10.0-beta.0",
|
|
412
413
|
"@vercel/sdk": "1.28.8",
|
|
413
414
|
"@workflow/core": "5.0.0-beta.43",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{i as e,o as t,t as n}from"./rolldown-runtime-DaDcL9Zw.js";import{Et as r,Rn as i,Tn as a,Zn as o,_r as s,dn as c,wc as l,wn as u,yt as d}from"./schemas-DtQwuPdM.js";import{n as f}from"./compat-Dm2n85Mx.js";import{t as p}from"./version-BYHw-SDY.js";import*as m from"node:path";import h from"node:path";import{homedir as g}from"node:os";import _ from"node:fs";import*as ee from"node:fs/promises";import v from"os";import{setTimeout as y}from"node:timers/promises";const b=i().transform((e,t)=>{try{return JSON.parse(e)}catch(e){return t.addIssue({code:f.custom,message:`Invalid JSON: ${e.message}`}),l}});var x=n(((t,n)=>{let r=e(`os`),i=e(`path`),a=/^win/i.test(process.platform);function o(e){return i.normalize(i.join(e,`.`))}let s=()=>{let{env:e}=process,t={};return t.home=()=>o(r.homedir?r.homedir():e.HOME),t.temp=()=>o(r.tmpdir?r.tmpdir():e.TMPDIR||e.TEMP||e.TMP),t},c=()=>{let{env:e}=process,t={};return t.home=()=>o(r.homedir?r.homedir():e.USERPROFILE||i.join(e.HOMEDRIVE,e.HOMEPATH)||e.HOME),t.temp=()=>o(r.tmpdir?r.tmpdir():e.TEMP||e.TMP||i.join(e.LOCALAPPDATA||e.SystemRoot||e.windir,`Temp`)),t};n.exports=new class e{constructor(){let t=function(){return new e};this._fn=t;let n=a?c():s();return Object.keys(n).forEach(e=>{this._fn[e]=n[e]}),this._fn}}})),S=n(((t,n)=>{let r=e(`path`),i=x(),a=()=>{let e={};return e.cache=()=>process.env.XDG_CACHE_HOME||r.join(i.home()||i.temp(),`.cache`),e.config=()=>process.env.XDG_CONFIG_HOME||r.join(i.home()||i.temp(),`.config`),e.data=()=>process.env.XDG_DATA_HOME||r.join(i.home()||i.temp(),`.local`,`share`),e.runtime=()=>process.env.XDG_RUNTIME_DIR||void 0,e.state=()=>process.env.XDG_STATE_HOME||r.join(i.home()||i.temp(),`.local`,`state`),e},o=()=>{let e={};return e.cache=()=>process.env.XDG_CACHE_HOME||r.join(r.join(i.home()||i.temp(),`Library`),`Caches`),e.config=()=>process.env.XDG_CONFIG_HOME||r.join(r.join(i.home()||i.temp(),`Library`),`Preferences`),e.data=()=>process.env.XDG_DATA_HOME||r.join(r.join(i.home()||i.temp(),`Library`),`Application Support`),e.runtime=()=>process.env.XDG_RUNTIME_DIR||void 0,e.state=()=>process.env.XDG_STATE_HOME||r.join(r.join(i.home()||i.temp(),`Library`),`State`),e},s=()=>{let e={};return e.cache=()=>{let e=process.env.LOCALAPPDATA||r.join(i.home()||i.temp(),`AppData`,`Local`);return process.env.XDG_CACHE_HOME||r.join(e,`xdg.cache`)},e.config=()=>{let e=process.env.APPDATA||r.join(i.home()||i.temp(),`AppData`,`Roaming`);return process.env.XDG_CONFIG_HOME||r.join(e,`xdg.config`)},e.data=()=>{let e=process.env.APPDATA||r.join(i.home()||i.temp(),`AppData`,`Roaming`);return process.env.XDG_DATA_HOME||r.join(e,`xdg.data`)},e.runtime=()=>process.env.XDG_RUNTIME_DIR||void 0,e.state=()=>{let e=process.env.LOCALAPPDATA||r.join(i.home()||i.temp(),`AppData`,`Local`);return process.env.XDG_STATE_HOME||r.join(e,`xdg.state`)},e},c=()=>{let e=function(){return c()},t={};return t=/^darwin$/i.test(process.platform)?o():/^win/i.test(process.platform)?s():a(),t.configDirs=()=>{let e=[];return e.push(t.config()),process.env.XDG_CONFIG_DIRS&&e.push(...process.env.XDG_CONFIG_DIRS.split(r.delimiter)),e},t.dataDirs=()=>{let e=[];return e.push(t.data()),process.env.XDG_DATA_DIRS&&e.push(...process.env.XDG_DATA_DIRS.split(r.delimiter)),e},Object.keys(t).forEach(n=>{e[n]=t[n]}),e};n.exports=c()})),C=t(n(((t,n)=>{let r=e(`path`),i=e(`os`),a=S(),o=/^win/i.test(process.platform);function s(e,t){if(e||={},typeof e!=`object`&&(e={isolated:e}),e.isolated=e.isolated===void 0||e.isolated===null?t:e.isolated,typeof e.isolated!=`boolean`)throw TypeError(`Expected boolean for "isolated" argument, got ${typeof e.isolated}`);return e}let c=(e,t)=>{let n={};return n.cache=(n={isolated:null})=>(n=s(n,t),r.join(a.cache(),n.isolated?e:``)),n.config=(n={isolated:null})=>(n=s(n,t),r.join(a.config(),n.isolated?e:``)),n.data=(n={isolated:null})=>(n=s(n,t),r.join(a.data(),n.isolated?e:``)),n.runtime=(n={isolated:null})=>(n=s(n,t),a.runtime()?r.join(a.runtime(),n.isolated?e:``):void 0),n.state=(n={isolated:null})=>(n=s(n,t),r.join(a.state(),n.isolated?e:``)),n.configDirs=(n={isolated:null})=>(n=s(n,t),a.configDirs().map(t=>r.join(t,n.isolated?e:``))),n.dataDirs=(n={isolated:null})=>(n=s(n,t),a.dataDirs().map(t=>r.join(t,n.isolated?e:``))),n},l=(e,t)=>{let{env:n}=process,o=i.homedir(),c=i.tmpdir(),l=n.APPDATA||r.join(o||c,`AppData`,`Roaming`),u=n.LOCALAPPDATA||r.join(o||c,`AppData`,`Local`),d={};return d.cache=(i={isolated:null})=>(i=s(i,t),!i.isolated||n.XDG_CACHE_HOME?r.join(a.cache(),i.isolated?e:``):r.join(u,i.isolated?e:``,`Cache`)),d.config=(i={isolated:null})=>(i=s(i,t),!i.isolated||n.XDG_CONFIG_HOME?r.join(a.config(),i.isolated?e:``):r.join(l,i.isolated?e:``,`Config`)),d.data=(i={isolated:null})=>(i=s(i,t),!i.isolated||n.XDG_DATA_HOME?r.join(a.data(),i.isolated?e:``):r.join(l,i.isolated?e:``,`Data`)),d.runtime=(n={isolated:null})=>(n=s(n,t),a.runtime()?r.join(a.runtime(),n.isolated?e:``):void 0),d.state=(i={isolated:null})=>(i=s(i,t),!i.isolated||n.XDG_STATE_HOME?r.join(a.state(),i.isolated?e:``):r.join(u,i.isolated?e:``,`State`)),d.configDirs=(i={isolated:null})=>{i=s(i,t);let a=[d.config(i)];return n.XDG_CONFIG_DIRS&&a.push(...n.XDG_CONFIG_DIRS.split(r.delimiter).map(t=>r.join(t,i.isolated?e:``))),a},d.dataDirs=(i={isolated:null})=>{i=s(i,t);let a=[d.data(i)];return n.XDG_DATA_DIRS&&a.push(...n.XDG_DATA_DIRS.split(r.delimiter).map(t=>r.join(t,i.isolated?e:``))),a},d};n.exports=new class t{constructor(n={name:null,suffix:null,isolated:!0}){let i=function(e={name:null,suffix:null,isolated:!0}){return new t(e)};this._fn=i,n||={},typeof n!=`object`&&(n={name:n});let a=n.name||``;if(typeof a!=`string`)throw TypeError(`Expected string for "name" argument, got ${typeof a}`);let s=n.suffix||``;if(typeof s!=`string`)throw TypeError(`Expected string for "suffix" argument, got ${typeof s}`);let u=n.isolated===void 0||n.isolated===null||n.isolated;if(typeof u!=`boolean`)throw TypeError(`Expected boolean for "isolated" argument, got ${typeof u}`);a||=r.parse(process.pkg?process.execPath:e.main?e.main.filename:process.argv[0]).name,s&&(a+=s),this._fn.$name=()=>a,this._fn.$isolated=()=>u;let d=o?l(a,u):c(a,u);return Object.keys(d).forEach(e=>{this._fn[e]=d[e]}),this._fn}}}))(),1);const w=u().transform(e=>new Date(e*1e3)),T=a({token:i().min(1).optional(),refreshToken:i().min(1).optional(),expiresAt:w.optional()}),E=b.pipe(T),te=e=>{try{return _.lstatSync(e).isDirectory()}catch{return!1}},D=()=>{if(process.env.VERCEL_AUTH_CONFIG_DIR)return process.env.VERCEL_AUTH_CONFIG_DIR;let e=(0,C.default)(`com.vercel.cli`).dataDirs();return[...e,h.join(g(),`.now`),...(0,C.default)(`now`).dataDirs()].find(e=>te(e))||e[0]},ne=()=>{try{let e=h.join(D(),`auth.json`);return E.parse(_.readFileSync(e,`utf8`))}catch{return null}};function O(e){let t=h.join(D(),`auth.json`);_.mkdirSync(h.dirname(t),{recursive:!0});let n={token:e.token,expiresAt:e.expiresAt&&Math.round(e.expiresAt.getTime()/1e3),refreshToken:e.refreshToken};_.writeFileSync(t,JSON.stringify(n)+`
|
|
2
|
-
`)}const k=`${v.hostname()} @ vercel/sandbox/${p} node-${process.version} ${v.platform()} (${v.arch()})`,A=new URL(`https://vercel.com`),j=`cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp`,M=a({issuer:i().url(),device_authorization_endpoint:i().url(),token_endpoint:i().url(),revocation_endpoint:i().url(),jwks_uri:i().url(),introspection_endpoint:i().url()});let N;const P=a({device_code:i(),user_code:i(),verification_uri:i().url(),verification_uri_complete:i().url(),expires_in:u(),interval:u()}),F=a({active:c(!0),client_id:i(),session_id:i()}).or(a({active:c(!1)}));async function I(){if(N)return N;let e=await fetch(new URL(`.well-known/openid-configuration`,A),{headers:{"Content-Type":`application/json`,"user-agent":k}});return N=M.parse(await e.json()),N}async function L(){let e=await I();return{async deviceAuthorizationRequest(){let t=await(await fetch(e.device_authorization_endpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`,"user-agent":k},body:new URLSearchParams({client_id:j,scope:`openid offline_access`})})).json(),n=P.safeParse(t);if(!n.success)throw new V(`Failed to parse device authorization response from the Vercel authorization server.`,t);return{device_code:n.data.device_code,user_code:n.data.user_code,verification_uri:n.data.verification_uri,verification_uri_complete:n.data.verification_uri_complete,expiresAt:Date.now()+n.data.expires_in*1e3,interval:n.data.interval}},async deviceAccessTokenRequest(t){try{return[null,await fetch(e.token_endpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`,"user-agent":k},body:new URLSearchParams({client_id:j,grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:t}),signal:AbortSignal.timeout(10*1e3)})]}catch(e){return e instanceof Error?[e]:[Error(`An unknown error occurred. See the logs for details.`,{cause:e})]}},async processTokenResponse(e){let t=await e.json(),n=R.safeParse(t);return n.success?[null,n.data]:[new V(`Failed to parse token response from the Vercel authorization server.`,t)]},async revokeToken(t){let n=await fetch(e.revocation_endpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`,"user-agent":k},body:new URLSearchParams({token:t,client_id:j})});if(!n.ok)return new V(`Revocation request failed`,await n.json())},async refreshToken(t){let n=await fetch(e.token_endpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`,"user-agent":k},body:new URLSearchParams({client_id:j,grant_type:`refresh_token`,refresh_token:t})}),[r,i]=await this.processTokenResponse(n);if(r)throw r;return i},async introspectToken(t){let n=await(await fetch(e.introspection_endpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`,"user-agent":k},body:new URLSearchParams({token:t})})).json(),r=F.safeParse(n);if(!r.success)throw new V(`Failed to parse introspection response from the Vercel authorization server.`,n);return r.data}}}const R=a({access_token:i(),token_type:c(`Bearer`),expires_in:u(),refresh_token:i().optional(),scope:i().optional()}),z=a({error:d([`invalid_request`,`invalid_client`,`invalid_grant`,`unauthorized_client`,`unsupported_grant_type`,`invalid_scope`,`server_error`,`authorization_pending`,`slow_down`,`access_denied`,`expired_token`,`unsupported_token_type`]),error_description:i().optional(),error_uri:i().optional()});function B(e){try{return z.parse(e)}catch(e){return e instanceof s?TypeError(`Invalid OAuth error response: ${e.message}`):TypeError(`Failed to parse OAuth error response`)}}var V=class extends Error{constructor(e,t){super(e),this.name=`OAuthError`;let n=B(t);if(n instanceof TypeError){this.cause=Error(`Unexpected response from the Vercel authorization server.`),this.code=`server_error`;return}let r=n.error;n.error_description&&(r+=`: ${n.error_description}`),n.error_uri&&(r+=` (${n.error_uri})`),this.cause=Error(r),this.code=n.error}};function H(e){return e instanceof V}async function*U({request:e,oauth:t}){let n=new AbortController;try{let r=e.interval*1e3;for(;Date.now()<e.expiresAt;){let[i,a]=await t.deviceAccessTokenRequest(e.device_code);if(i){if(i.message.includes(`timeout`)){r*=2,yield{_tag:`Timeout`,newInterval:r},await y(r,{signal:n.signal});continue}yield{_tag:`Error`,error:i};return}yield{_tag:`Response`,response:a.clone()};let[o,s]=await t.processTokenResponse(a);if(H(o)){let{code:e}=o;switch(e){case`authorization_pending`:await y(r,{signal:n.signal});continue;case`slow_down`:r+=5*1e3,yield{_tag:`SlowDown`,newInterval:r},await y(r,{signal:n.signal});continue;default:yield{_tag:`Error`,error:o.cause};return}}if(o){yield{_tag:`Error`,error:o};return}O({token:s.access_token,expiresAt:new Date(Date.now()+s.expires_in*1e3),refreshToken:s.refresh_token});return}yield{_tag:`Error`,error:Error(`Timed out waiting for authentication. Please try again.`)};return}finally{n.abort()}}var W=class extends Error{constructor(e){super(`HTTP ${e.statusCode}: ${e.responseText}`),this.name=`NotOk`,this.response=e}};async function G(e){let t=await fetch(`https://vercel.com/api${e.endpoint}`,{method:e.method,body:e.body,headers:{Authorization:`Bearer ${e.token}`,"Content-Type":`application/json`}});if(!t.ok){let e=await t.text();try{let{error:t}=JSON.parse(e);e=`${t.code.toUpperCase()}: ${t.message}`}catch{}throw new W({responseText:e,statusCode:t.status})}return await t.json()}const K=b.pipe(a({projectId:i(),orgId:i()}));async function q(e){let t=m.join(e,`.vercel`,`project.json`),n;try{n=await ee.readFile(t,`utf-8`)}catch{return null}let r=K.safeParse(n);return r.success?{projectId:r.data.projectId,teamId:r.data.orgId}:null}const J=a({user:a({defaultTeamId:i().nullable(),username:i()})}),Y=a({id:i(),slug:i(),updatedAt:u().optional(),membership:a({role:i()}),billing:a({plan:i()})}),X=a({teams:r(o()).transform(e=>e.flatMap(e=>{let t=Y.safeParse(e);return t.success?[t.data]:[]})),pagination:a({count:u(),next:u().nullable()})}),Z=`vercel-sandbox-default-project`;function Q(e){return e instanceof W&&(e.response.statusCode===402||e.response.statusCode===403)}async function re(e){let t=await q(e.cwd??process.cwd());if(t){let n=await ie(e.token,t.teamId,t.projectId);return{...t,created:!1,...n}}if(e.teamId)return $(e.token,e.teamId);let{defaultTeamId:n,username:r}=(await G({token:e.token,endpoint:`/v2/user`}).then(J.parse)).user;if(n)try{let t=await $(e.token,n);try{let r=await G({token:e.token,endpoint:`/v2/teams/${encodeURIComponent(n)}`}).then(a({slug:i()}).parse);return{...t,teamSlug:r.slug}}catch{return t}}catch(e){if(!Q(e))throw e}let o=null;do{let t=o===null?`/v2/teams?limit=20`:`/v2/teams?limit=20&until=${o}`,i=await G({token:e.token,endpoint:t}).then(X.parse);o=i.pagination.next;let a=i.teams.filter(e=>e.membership.role===`OWNER`&&e.billing.plan===`hobby`);if(a.length===0)continue;let s=a.find(e=>e.slug===r)??a.sort((e,t)=>(t.updatedAt??0)-(e.updatedAt??0))[0];if(s&&s.id!==n)try{return{...await $(e.token,s.id),teamSlug:s.slug}}catch(e){if(!Q(e))throw e}}while(o!==null);try{return{...await $(e.token,r),teamSlug:r}}catch(e){if(!Q(e))throw e}throw new W({statusCode:403,responseText:`Authenticated as "${r}" but none of the available teams allow sandbox creation.`})}async function $(e,t){let n=t.startsWith(`team_`)?`teamId=${encodeURIComponent(t)}`:`slug=${encodeURIComponent(t)}`,r=!1;try{await G({token:e,endpoint:`/v2/projects/vercel-sandbox-default-project?${n}`})}catch(t){if(!(t instanceof W)||t.response.statusCode!==404)throw t;await G({token:e,endpoint:`/v11/projects?${n}`,method:`POST`,body:JSON.stringify({name:Z})}),r=!0}return{projectId:Z,teamId:t,created:r}}async function ie(e,t,n){try{let r=t.startsWith(`team_`)?`teamId=${encodeURIComponent(t)}`:`slug=${encodeURIComponent(t)}`,[o,s]=await Promise.all([G({token:e,endpoint:`/v2/teams/${encodeURIComponent(t)}`}).then(a({slug:i()}).parse),G({token:e,endpoint:`/v2/projects/${encodeURIComponent(n)}?${r}`}).then(a({name:i()}).parse)]);return{teamSlug:o.slug,projectSlug:s.name}}catch{return{}}}export{W as NotOk,L as OAuth,ne as getAuth,re as inferScope,H as isOAuthError,U as pollForToken,O as updateAuthConfig};
|