eve 0.66.0 → 0.66.2
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 +25 -0
- package/dist/src/channel/forwarded-principal.d.ts +26 -6
- package/dist/src/channel/forwarded-principal.js +1 -1
- package/dist/src/chunks/{use-eve-agent-BUeSjwIk.js → use-eve-agent-Di1u2zKu.js} +24 -4
- package/dist/src/chunks/{use-eve-agent-ydPlURM4.js → use-eve-agent-PbSMyJ6e.js} +24 -4
- package/dist/src/cli/commands/deploy.d.ts +3 -1
- package/dist/src/cli/commands/deploy.js +1 -1
- package/dist/src/cli/commands/extension-build.js +1 -1
- package/dist/src/cli/commands/integration-connect.js +1 -1
- package/dist/src/cli/commands/register-project-commands.js +1 -1
- package/dist/src/cli/commands/registry-project.d.ts +2 -1
- package/dist/src/cli/commands/registry-project.js +2 -2
- package/dist/src/cli/commands/registry.js +1 -1
- package/dist/src/cli/commands/trace-detail.d.ts +5 -25
- package/dist/src/cli/commands/trace-detail.js +1 -1
- package/dist/src/cli/commands/trace.js +2 -2
- package/dist/src/cli/commands/vercel-non-interactive.d.ts +7 -0
- package/dist/src/cli/commands/vercel-non-interactive.js +1 -1
- package/dist/src/cli/dev/run-interactive-ui.js +1 -1
- package/dist/src/cli/dev/tui/argument-typeahead.d.ts +1 -1
- package/dist/src/cli/dev/tui/argument-typeahead.js +1 -1
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
- package/dist/src/cli/dev/tui/registry-result-message.js +1 -1
- package/dist/src/cli/dev/tui/remote-auth.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +2 -2
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +3 -1
- package/dist/src/cli/dev/tui/setup-commands.js +1 -1
- package/dist/src/cli/dev/tui/setup-panel.d.ts +14 -0
- package/dist/src/cli/dev/tui/setup-panel.js +4 -4
- package/dist/src/cli/dev/tui/subagent-pump.d.ts +2 -10
- package/dist/src/cli/dev/tui/subagent-pump.js +1 -1
- package/dist/src/cli/dev/tui/terminal-renderer.d.ts +2 -2
- package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
- package/dist/src/cli/dev/tui/traces/trace-conversation.js +1 -1
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/client/client-error.js +1 -1
- package/dist/src/client/open-stream.d.ts +3 -2
- package/dist/src/client/open-stream.js +1 -1
- package/dist/src/client/session.d.ts +16 -1
- package/dist/src/client/session.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/discover/filesystem.d.ts +2 -0
- package/dist/src/discover/filesystem.js +1 -1
- package/dist/src/discover/grammar.d.ts +4 -2
- package/dist/src/discover/grammar.js +1 -1
- package/dist/src/discover/sandbox.js +1 -1
- package/dist/src/evals/session.d.ts +3 -2
- package/dist/src/evals/session.js +1 -1
- package/dist/src/evals/types.d.ts +8 -2
- package/dist/src/eve-channel/index.js +1 -1
- package/dist/src/eve-channel/types.d.ts +12 -4
- package/dist/src/execution/cancel-indexed-session-tasks-step.js +1 -1
- package/dist/src/execution/dynamic-workflow/tool.js +1 -1
- package/dist/src/execution/proxied-deliver-step.js +1 -1
- package/dist/src/execution/route-child-delivery.js +2 -2
- package/dist/src/execution/session/admission.js +1 -1
- package/dist/src/execution/session/finalization.js +1 -1
- package/dist/src/execution/session/handoff-steps.js +1 -1
- package/dist/src/execution/session/input-queue.d.ts +12 -10
- package/dist/src/execution/session/input-queue.js +1 -1
- package/dist/src/execution/session/next-input.js +1 -1
- package/dist/src/execution/session/program.js +1 -1
- package/dist/src/execution/session/turn-step-result.d.ts +3 -1
- package/dist/src/execution/session/turn-step-result.js +1 -1
- package/dist/src/execution/session/turn-step-types.d.ts +7 -2
- package/dist/src/execution/session/turn-step.js +1 -1
- package/dist/src/execution/session/turn.js +1 -1
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/tasks/parent/control-shared.js +1 -1
- package/dist/src/execution/tasks/parent/dispatch.js +1 -1
- package/dist/src/execution/tasks/parent/hitl-proxy-steps.d.ts +1 -0
- package/dist/src/execution/tasks/parent/hitl-proxy-steps.js +1 -1
- package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/tools/ask-question-workflow.js +1 -1
- package/dist/src/execution/tools/sleep-workflow.js +1 -1
- package/dist/src/execution/tools/subagent/accept-event-step.js +1 -1
- package/dist/src/execution/tools/subagent/invoke-preparation.js +1 -1
- package/dist/src/execution/tools/subagent/invoke-step.js +1 -1
- package/dist/src/execution/tools/subagent/task-cancel.d.ts +1 -1
- package/dist/src/execution/tools/subagent/task-cancel.js +1 -1
- package/dist/src/harness/session-limit-enforcement.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/harness/turn-tag-state.d.ts +2 -2
- package/dist/src/harness/workflow-tool-runs.d.ts +33 -5
- package/dist/src/harness/workflow-tool-runs.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/instrumentation-layout.js +1 -1
- package/dist/src/internal/model-auth/vercel-cli.d.ts +2 -0
- package/dist/src/internal/model-auth/vercel-cli.js +1 -1
- package/dist/src/internal/nitro/host/build-extension.js +1 -1
- package/dist/src/internal/nitro/host/extension-build-lock.d.ts +9 -0
- package/dist/src/internal/nitro/host/extension-build-lock.js +1 -0
- package/dist/src/internal/nitro/host/extension-capability-requirements.d.ts +1 -0
- package/dist/src/internal/nitro/host/extension-capability-requirements.js +1 -1
- package/dist/src/internal/nitro/host/extension-declarations.js +1 -1
- package/dist/src/internal/nitro/host/extension-distribution.js +1 -1
- package/dist/src/internal/nitro/host/extension-state-usage.d.ts +2 -2
- package/dist/src/internal/nitro/host/extension-state-usage.js +1 -1
- package/dist/src/internal/nitro/host/prepare-application-host.d.ts +5 -4
- package/dist/src/internal/nitro/host/prepare-application-host.js +1 -1
- package/dist/src/internal/nitro/host/types.d.ts +2 -2
- package/dist/src/internal/nitro/host/workspace-extensions.d.ts +31 -0
- package/dist/src/internal/nitro/host/workspace-extensions.js +1 -0
- package/dist/src/public/next/index.d.ts +10 -7
- package/dist/src/public/next/index.js +1 -1
- package/dist/src/public/next/server.d.ts +3 -0
- package/dist/src/public/next/server.js +2 -2
- package/dist/src/self-modification/extension/local-only.d.ts +5 -0
- package/dist/src/self-modification/extension/local-only.js +1 -0
- package/dist/src/self-modification/extension/subagents/agent/instructions.js +6 -6
- package/dist/src/self-modification/extension/subagents/agent/skills/trace_analysis.d.ts +10 -0
- package/dist/src/self-modification/extension/subagents/agent/skills/trace_analysis.js +19 -0
- package/dist/src/self-modification/extension/subagents/agent/tools/inspect_trace.d.ts +128 -0
- package/dist/src/self-modification/extension/subagents/agent/tools/inspect_trace.js +1 -0
- package/dist/src/self-modification/extension/subagents/agent/tools/inspect_trace_spans.d.ts +80 -0
- package/dist/src/self-modification/extension/subagents/agent/tools/inspect_trace_spans.js +1 -0
- package/dist/src/self-modification/extension/subagents/agent/tools/registry_add.js +1 -1
- package/dist/src/self-modification/extension/subagents/agent/tools/search_traces.d.ts +53 -0
- package/dist/src/self-modification/extension/subagents/agent/tools/search_traces.js +1 -0
- package/dist/src/self-modification/extension/trace-inspection.d.ts +15 -0
- package/dist/src/self-modification/extension/trace-inspection.js +1 -0
- package/dist/src/self-modification/sandbox.js +1 -1
- package/dist/src/setup/ask.d.ts +2 -0
- package/dist/src/setup/ask.js +1 -1
- package/dist/src/setup/boxes/link-project.d.ts +2 -0
- package/dist/src/setup/boxes/link-project.js +1 -1
- package/dist/src/setup/cli/option-row.d.ts +3 -1
- package/dist/src/setup/cli/option-row.js +1 -1
- package/dist/src/setup/flows/deploy.d.ts +1 -0
- package/dist/src/setup/flows/deploy.js +1 -1
- package/dist/src/setup/flows/install-vercel-cli.js +1 -1
- package/dist/src/setup/flows/login.js +1 -1
- package/dist/src/setup/flows/model-login-connection.js +1 -1
- package/dist/src/setup/flows/model-login.d.ts +4 -0
- package/dist/src/setup/flows/model-login.js +1 -1
- package/dist/src/setup/flows/vercel-model-login.js +1 -1
- package/dist/src/setup/integrations/runner.js +1 -1
- package/dist/src/setup/integrations/web/setup.d.ts +15 -10
- package/dist/src/setup/integrations/web/setup.js +30 -1
- package/dist/src/setup/scaffold/create/project.js +2 -2
- package/dist/src/setup/scaffold/create/web-template.d.ts +7 -3
- package/dist/src/setup/scaffold/create/web-template.js +39 -20
- package/dist/src/setup/scaffold/update/channels.js +2 -2
- package/dist/src/setup/slackbot.js +1 -1
- package/dist/src/setup/vercel-project.d.ts +2 -0
- package/dist/src/setup/vercel-project.js +0 -0
- package/dist/src/setup/vercel-trace-sampling.d.ts +3 -0
- package/dist/src/setup/vercel-trace-sampling.js +1 -0
- package/dist/src/subagents/parent-notification.d.ts +1 -1
- package/dist/src/subagents/remote-dispatch.d.ts +1 -0
- package/dist/src/subagents/remote-dispatch.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tasks/delivery-context.js +2 -2
- package/dist/src/tasks/notification.d.ts +6 -0
- package/dist/src/tasks/notification.js +1 -1
- package/dist/src/tracing/agent-trace-context-store.js +1 -1
- package/dist/src/tracing/local-trace-analysis.d.ts +51 -0
- package/dist/src/tracing/local-trace-analysis.js +1 -0
- package/dist/src/tracing/local-trace-discovery-index.d.ts +15 -0
- package/dist/src/tracing/local-trace-discovery-index.js +1 -0
- package/dist/src/tracing/local-trace-operations.d.ts +13 -0
- package/dist/src/tracing/local-trace-operations.js +1 -0
- package/dist/src/tracing/local-trace-query.d.ts +17 -0
- package/dist/src/tracing/local-trace-query.js +1 -0
- package/dist/src/tracing/local-trace-span-processor.js +1 -1
- package/dist/src/tracing/local-trace-summary.d.ts +31 -0
- package/dist/src/tracing/local-trace-summary.js +1 -0
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/concepts/project-structure.mdx +2 -4
- package/docs/concepts/sessions-runs-and-streaming.md +2 -2
- package/docs/evals/cases.mdx +1 -0
- package/docs/extensions.md +3 -1
- package/docs/guides/auth-and-route-protection.md +33 -1
- package/docs/guides/client/streaming.mdx +26 -0
- package/docs/guides/deployment/vercel.mdx +5 -5
- package/docs/guides/frontend/nextjs.mdx +20 -7
- package/docs/guides/frontend/overview.mdx +1 -1
- package/docs/guides/instrumentation/agent-runs.mdx +4 -4
- package/docs/guides/remote-agents.md +3 -1
- package/docs/install-integrations.mdx +1 -1
- package/docs/reference/agent-files.md +16 -0
- package/docs/reference/cli.md +1 -1
- package/docs/subagents/index.mdx +8 -2
- package/package.json +1 -1
- package/dist/src/internal/nitro/host/dev-workspace-extensions.d.ts +0 -18
- package/dist/src/internal/nitro/host/dev-workspace-extensions.js +0 -1
- package/dist/src/tasks/session-task-cohorts.d.ts +0 -7
- package/dist/src/tasks/session-task-cohorts.js +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const WEB_APP_TEMPLATE_FILES: {
|
|
2
|
-
readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
|
|
3
|
-
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon, BrainIcon, PlusIcon, SquareIcon } from "lucide-react";\nimport { useState } from "react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n ConversationTopFade,\n} from "@/components/ai-elements/conversation";\nimport { Message, MessageContent } from "@/components/ai-elements/message";\nimport {\n PromptInput,\n PromptInputButton,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n usePromptInputAttachments,\n} from "@/components/ai-elements/prompt-input";\nimport { Shimmer } from "@/components/ai-elements/shimmer";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\nexport function AgentChat({\n sessionId,\n sessionless = false,\n}: {\n readonly sessionId?: string;\n readonly sessionless?: boolean;\n}) {\n const [cancellationError, setCancellationError] = useState<string>();\n const [hasInputText, setHasInputText] = useState(false);\n const agent = useEveAgent({\n initialSession:\n sessionId === undefined\n ? undefined\n : {\n sessionId,\n streamIndex: 0,\n },\n resume: sessionId !== undefined,\n onSessionChange(session) {\n if (sessionId === undefined && session !== undefined) {\n // Next patches window.history to navigate, which would detach the active stream.\n History.prototype.replaceState.call(\n window.history,\n window.history.state,\n "",\n `/s/${encodeURIComponent(session.sessionId)}`,\n );\n }\n },\n });\n\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isResuming = agent.status === "resuming";\n const isEmpty = agent.data.messages.length === 0;\n const lastMessage = agent.data.messages.at(-1);\n const isPendingAssistantShell =\n lastMessage?.role === "assistant" &&\n lastMessage.parts.every((part) => part.type === "step-start");\n const showPendingThinking =\n isBusy &&\n (agent.status === "submitted" || lastMessage?.role !== "assistant" || isPendingAssistantShell);\n const turnFailure = isBusy || isResuming ? undefined : getLatestTurnFailure(agent.events);\n const errorMessage = cancellationError ?? agent.error?.message ?? turnFailure;\n const hasConversationContent = sessionless || !isEmpty || errorMessage !== undefined;\n const showConversationLayout = isResuming || hasConversationContent;\n const activeSessionId = sessionId ?? agent.session?.sessionId;\n\n const requestCancellation = () => {\n setCancellationError(undefined);\n void agent.cancel().catch((error: unknown) => {\n setCancellationError(toErrorMessage(error));\n });\n };\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if ((text.length === 0 && message.files.length === 0) || isResuming) return;\n\n setHasInputText(false);\n setCancellationError(undefined);\n const options = isBusy ? { turnPolicy: "steer" as const } : undefined;\n\n if (message.files.length === 0) {\n await agent.send(text, options);\n return;\n }\n\n const parts: UserContent = [];\n if (text.length > 0) {\n parts.push({ text, type: "text" });\n }\n for (const file of message.files) {\n parts.push({\n data: file.url,\n filename: file.filename,\n mediaType: file.mediaType,\n type: "file",\n });\n }\n\n await agent.send(parts, options);\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea\n disabled={isResuming}\n onChange={(event) => setHasInputText(event.currentTarget.value.trim().length > 0)}\n placeholder="Send a message…"\n />\n <ComposerAction\n hasInputText={hasInputText}\n isBusy={isBusy}\n isResuming={isResuming}\n onCancel={requestCancellation}\n />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {showConversationLayout ? (\n <ChatHeader canStartNewChat={activeSessionId !== undefined} />\n ) : null}\n\n {showConversationLayout ? (\n <Conversation\n className="min-h-0 flex-1"\n initial={sessionId === undefined ? undefined : false}\n resize={activeSessionId === undefined ? "smooth" : "instant"}\n scrollRestorationKey={\n isEmpty || activeSessionId === undefined\n ? undefined\n : `eve:web-chat-scroll:${activeSessionId}`\n }\n >\n <ConversationTopFade className="top-14" />\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 pt-20 pb-36 sm:px-6">\n {agent.data.messages.map((message, index) =>\n showPendingThinking &&\n isPendingAssistantShell &&\n message.id === lastMessage.id ? null : (\n <AgentMessage\n canRespond={!isBusy && !isResuming}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => {\n setCancellationError(undefined);\n return agent.respond(inputResponses);\n }}\n />\n ),\n )}\n {showPendingThinking ? <PendingThinking /> : null}\n {errorMessage ? <ErrorMessage message={errorMessage} /> : null}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n ) : null}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n showConversationLayout\n ? "fixed bottom-0 left-1/2 z-20 max-w-3xl -translate-x-1/2 bg-gradient-to-t from-background via-background to-transparent pt-4 pb-6"\n : "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]",\n )}\n >\n {showConversationLayout ? null : (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n )}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction ComposerAction({\n hasInputText,\n isBusy,\n isResuming,\n onCancel,\n}: {\n readonly hasInputText: boolean;\n readonly isBusy: boolean;\n readonly isResuming: boolean;\n readonly onCancel: () => void;\n}) {\n const attachments = usePromptInputAttachments();\n const canSubmit = hasInputText || attachments.files.length > 0;\n\n if (!isBusy || canSubmit) {\n return <PromptInputSubmit disabled={isResuming} />;\n }\n\n return (\n <PromptInputButton\n aria-label="Stop"\n className="absolute right-2.5 bottom-2.5"\n onClick={onCancel}\n variant="outline"\n >\n <SquareIcon className="size-3 fill-current" />\n </PromptInputButton>\n );\n}\n\nfunction ErrorMessage({ message }: { readonly message: string }) {\n return (\n <Message className="max-w-full" from="assistant">\n <MessageContent>\n <div\n className="flex w-full items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm"\n role="alert"\n >\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{message}</p>\n </div>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction ChatHeader({ canStartNewChat }: { readonly canStartNewChat: boolean }) {\n return (\n <header className="pointer-events-none fixed top-0 right-0 left-0 z-20 h-14">\n <div className="relative mx-auto flex h-full w-full max-w-3xl items-center justify-center bg-background px-24">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n {canStartNewChat ? (\n <Button\n aria-label="Start a new chat"\n className="pointer-events-auto fixed top-3 right-6 pr-4"\n onClick={() => window.location.assign("/s")}\n size="sm"\n type="button"\n variant="ghost"\n >\n <PlusIcon className="size-4" />\n <span className="hidden font-normal text-sm sm:inline">New chat</span>\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n\nfunction PendingThinking() {\n return (\n <Message aria-live="polite" from="assistant">\n <MessageContent>\n <div className="mb-4 flex w-full items-center gap-2 text-muted-foreground text-sm">\n <BrainIcon className="size-4" />\n <Shimmer duration={1}>Thinking</Shimmer>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction getLatestTurnFailure(\n events: ReturnType<typeof useEveAgent>["events"],\n): string | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n\n if (event.type === "turn.failed") {\n return event.data.code === "MODEL_CALL_FAILED"\n ? "The model is temporarily unavailable. Please try again."\n : event.data.message;\n }\n\n if (event.type === "turn.completed" || event.type === "turn.cancelled") {\n return undefined;\n }\n\n if (event.type === "message.received") {\n return undefined;\n }\n }\n\n return undefined;\n}\n';
|
|
2
|
+
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon, BrainIcon, PlusIcon, SquareIcon } from "lucide-react";\nimport { useState } from "react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n ConversationTopFade,\n} from "@/components/ai-elements/conversation";\nimport { Message, MessageContent } from "@/components/ai-elements/message";\nimport {\n PromptInput,\n PromptInputButton,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n usePromptInputAttachments,\n} from "@/components/ai-elements/prompt-input";\nimport { Shimmer } from "@/components/ai-elements/shimmer";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\nimport { WEB_CHAT_AGENT } from "@/app/eve-agent";\n\nconst DEFAULT_AGENT_NAME = "__EVE_INIT_APP_NAME__";\nconst AGENT_NAME = WEB_CHAT_AGENT ?? DEFAULT_AGENT_NAME;\n\nexport function AgentChat({\n sessionId,\n sessionless = false,\n}: {\n readonly sessionId?: string;\n readonly sessionless?: boolean;\n}) {\n const [cancellationError, setCancellationError] = useState<string>();\n const [hasInputText, setHasInputText] = useState(false);\n const agent = useEveAgent({\n agent: WEB_CHAT_AGENT,\n initialSession:\n sessionId === undefined\n ? undefined\n : {\n sessionId,\n streamIndex: 0,\n },\n resume: sessionId !== undefined,\n onSessionChange(session) {\n if (sessionId === undefined && session !== undefined) {\n // Next patches window.history to navigate, which would detach the active stream.\n History.prototype.replaceState.call(\n window.history,\n window.history.state,\n "",\n `/s/${encodeURIComponent(session.sessionId)}`,\n );\n }\n },\n });\n\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isResuming = agent.status === "resuming";\n const isEmpty = agent.data.messages.length === 0;\n const lastMessage = agent.data.messages.at(-1);\n const isPendingAssistantShell =\n lastMessage?.role === "assistant" &&\n lastMessage.parts.every((part) => part.type === "step-start");\n const showPendingThinking =\n isBusy &&\n (agent.status === "submitted" || lastMessage?.role !== "assistant" || isPendingAssistantShell);\n const turnFailure = isBusy || isResuming ? undefined : getLatestTurnFailure(agent.events);\n const errorMessage = cancellationError ?? agent.error?.message ?? turnFailure;\n const hasConversationContent = sessionless || !isEmpty || errorMessage !== undefined;\n const showConversationLayout = isResuming || hasConversationContent;\n const activeSessionId = sessionId ?? agent.session?.sessionId;\n\n const requestCancellation = () => {\n setCancellationError(undefined);\n void agent.cancel().catch((error: unknown) => {\n setCancellationError(toErrorMessage(error));\n });\n };\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if ((text.length === 0 && message.files.length === 0) || isResuming) return;\n\n setHasInputText(false);\n setCancellationError(undefined);\n const options = isBusy ? { turnPolicy: "steer" as const } : undefined;\n\n if (message.files.length === 0) {\n await agent.send(text, options);\n return;\n }\n\n const parts: UserContent = [];\n if (text.length > 0) {\n parts.push({ text, type: "text" });\n }\n for (const file of message.files) {\n parts.push({\n data: file.url,\n filename: file.filename,\n mediaType: file.mediaType,\n type: "file",\n });\n }\n\n await agent.send(parts, options);\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea\n disabled={isResuming}\n onChange={(event) => setHasInputText(event.currentTarget.value.trim().length > 0)}\n placeholder="Send a message…"\n />\n <ComposerAction\n hasInputText={hasInputText}\n isBusy={isBusy}\n isResuming={isResuming}\n onCancel={requestCancellation}\n />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {showConversationLayout ? (\n <ChatHeader canStartNewChat={activeSessionId !== undefined} />\n ) : null}\n\n {showConversationLayout ? (\n <Conversation\n className="min-h-0 flex-1"\n initial={sessionId === undefined ? undefined : false}\n resize={activeSessionId === undefined ? "smooth" : "instant"}\n scrollRestorationKey={\n isEmpty || activeSessionId === undefined\n ? undefined\n : `eve:web-chat-scroll:${activeSessionId}`\n }\n >\n <ConversationTopFade className="top-14" />\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 pt-20 pb-36 sm:px-6">\n {agent.data.messages.map((message, index) =>\n showPendingThinking &&\n isPendingAssistantShell &&\n message.id === lastMessage.id ? null : (\n <AgentMessage\n canRespond={!isBusy && !isResuming}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => {\n setCancellationError(undefined);\n return agent.respond(inputResponses);\n }}\n />\n ),\n )}\n {showPendingThinking ? <PendingThinking /> : null}\n {errorMessage ? <ErrorMessage message={errorMessage} /> : null}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n ) : null}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n showConversationLayout\n ? "fixed bottom-0 left-1/2 z-20 max-w-3xl -translate-x-1/2 bg-gradient-to-t from-background via-background to-transparent pt-4 pb-6"\n : "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]",\n )}\n >\n {showConversationLayout ? null : (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n )}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction ComposerAction({\n hasInputText,\n isBusy,\n isResuming,\n onCancel,\n}: {\n readonly hasInputText: boolean;\n readonly isBusy: boolean;\n readonly isResuming: boolean;\n readonly onCancel: () => void;\n}) {\n const attachments = usePromptInputAttachments();\n const canSubmit = hasInputText || attachments.files.length > 0;\n\n if (!isBusy || canSubmit) {\n return <PromptInputSubmit disabled={isResuming} />;\n }\n\n return (\n <PromptInputButton\n aria-label="Stop"\n className="absolute right-2.5 bottom-2.5"\n onClick={onCancel}\n variant="outline"\n >\n <SquareIcon className="size-3 fill-current" />\n </PromptInputButton>\n );\n}\n\nfunction ErrorMessage({ message }: { readonly message: string }) {\n return (\n <Message className="max-w-full" from="assistant">\n <MessageContent>\n <div\n className="flex w-full items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm"\n role="alert"\n >\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{message}</p>\n </div>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction ChatHeader({ canStartNewChat }: { readonly canStartNewChat: boolean }) {\n return (\n <header className="pointer-events-none fixed top-0 right-0 left-0 z-20 h-14">\n <div className="relative mx-auto flex h-full w-full max-w-3xl items-center justify-center bg-background px-24">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n {canStartNewChat ? (\n <Button\n aria-label="Start a new chat"\n className="pointer-events-auto fixed top-3 right-6 pr-4"\n onClick={() => window.location.assign("/s")}\n size="sm"\n type="button"\n variant="ghost"\n >\n <PlusIcon className="size-4" />\n <span className="hidden font-normal text-sm sm:inline">New chat</span>\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n\nfunction PendingThinking() {\n return (\n <Message aria-live="polite" from="assistant">\n <MessageContent>\n <div className="mb-4 flex w-full items-center gap-2 text-muted-foreground text-sm">\n <BrainIcon className="size-4" />\n <Shimmer duration={1}>Thinking</Shimmer>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction getLatestTurnFailure(\n events: ReturnType<typeof useEveAgent>["events"],\n): string | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n\n if (event.type === "turn.failed") {\n return event.data.code === "MODEL_CALL_FAILED"\n ? "The model is temporarily unavailable. Please try again."\n : event.data.message;\n }\n\n if (event.type === "turn.completed" || event.type === "turn.cancelled") {\n return undefined;\n }\n\n if (event.type === "message.received") {\n return undefined;\n }\n }\n\n return undefined;\n}\n';
|
|
4
3
|
readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n EveAuthorizationPart,\n EveDynamicToolPart,\n EveMessage,\n EveMessageInputRequest,\n EveMessagePart,\n} from "eve/react";\nimport { useState } from "react";\nimport {\n ArrowRightIcon,\n CheckCircleIcon,\n CheckIcon,\n ExternalLinkIcon,\n FileIcon,\n ImageIcon,\n KeyRoundIcon,\n XCircleIcon,\n} from "lucide-react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport {\n Question,\n QuestionInput,\n QuestionOption,\n QuestionOptions,\n QuestionPrompt,\n type QuestionResponse,\n QuestionSubmit,\n type QuestionValue,\n} from "@/components/ai-elements/question";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n BashToolContent,\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\ntype EveFilePart = Extract<EveMessagePart, { type: "file" }>;\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n const hasAssistantText =\n message.role === "assistant" &&\n message.parts.some((part) => part.type === "text" && part.text.length > 0);\n\n return (\n <Message\n data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n from={message.role}\n >\n <MessageContent>\n {message.parts.map((part, index) =>\n hasAssistantText && part.type === "reasoning" ? null : (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ),\n )}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n showCaret,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly showCaret: boolean;\n}) {\n switch (part.type) {\n case "step-start":\n return null;\n case "text":\n return (\n <MessageResponse caret="block" isAnimating={showCaret}>\n {part.text}\n </MessageResponse>\n );\n case "reasoning":\n return (\n <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n <ReasoningTrigger />\n <ReasoningContent>{part.text}</ReasoningContent>\n </Reasoning>\n );\n case "file":\n return <AttachmentPart part={part} />;\n case "authorization":\n return <AuthorizationPrompt part={part} />;\n case "dynamic-tool": {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (inputRequest?.kind === "question") {\n return (\n <QuestionRequest\n canRespond={canRespond}\n inputRequest={inputRequest}\n inputResponse={part.toolMetadata?.eve?.inputResponse}\n onInputResponses={onInputResponses}\n />\n );\n }\n\n return (\n <Tool\n defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n >\n <ToolHeader\n state={part.state}\n title={part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n {part.toolName === "bash" ? (\n <BashToolContent errorText={part.errorText} input={part.input} output={part.output} />\n ) : (\n <ToolInput input={part.input} />\n )}\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n {part.toolName === "bash" ? null : (\n <ToolOutput errorText={part.errorText} output={part.output} />\n )}\n </ToolContent>\n </Tool>\n );\n }\n }\n}\n\nfunction QuestionRequest({\n canRespond,\n inputRequest,\n inputResponse,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly inputRequest: EveMessageInputRequest;\n readonly inputResponse?: AgentInputResponse;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const hasOptions = (inputRequest.options?.length ?? 0) > 0;\n const acceptsFreeform = inputRequest.allowFreeform === true || !hasOptions;\n const [questionValue, setQuestionValue] = useState<QuestionValue>({\n selectedValues: inputResponse?.optionId ? [inputResponse.optionId] : [],\n text: inputResponse?.text ?? "",\n });\n\n const submitOption = (optionId: string) => {\n setQuestionValue((value) => ({ ...value, selectedValues: [optionId] }));\n return onInputResponses([\n {\n optionId,\n requestId: inputRequest.requestId,\n },\n ]);\n };\n\n const submitResponse = ({ selectedValues, text }: QuestionResponse) =>\n onInputResponses([\n {\n optionId: selectedValues[0],\n requestId: inputRequest.requestId,\n text,\n },\n ]);\n\n return (\n <Question\n disabled={!canRespond || inputResponse !== undefined}\n onSubmit={submitResponse}\n onValueChange={setQuestionValue}\n value={questionValue}\n >\n <QuestionPrompt>{inputRequest.prompt}</QuestionPrompt>\n {hasOptions ? (\n <QuestionOptions className="flex-col items-stretch" aria-label={inputRequest.prompt}>\n {inputRequest.options?.map((option, index) => (\n <QuestionOption\n className="justify-start px-3 py-2 text-left"\n key={option.id}\n onClick={() => void submitOption(option.id)}\n value={option.id}\n >\n <span className="min-w-0 flex-1">\n <span className="block text-foreground text-sm leading-tight">{option.label}</span>\n {option.description ? (\n <span className="block text-sm text-muted-foreground leading-tight">\n {option.description}\n </span>\n ) : null}\n </span>\n {inputResponse === undefined ? (\n <span aria-hidden="true" className="relative size-6 shrink-0">\n <span className="absolute inset-0 flex items-center justify-center rounded-full bg-foreground/8 text-xs text-muted-foreground transition-opacity group-hover/option:opacity-0 group-focus-visible/option:opacity-0">\n {index + 1}\n </span>\n <ArrowRightIcon className="absolute top-1/2 left-1/2 size-4 -translate-x-1/2 -translate-y-1/2 text-muted-foreground opacity-0 transition-[color,opacity] group-hover/option:text-foreground group-hover/option:opacity-100 group-focus-visible/option:opacity-100" />\n </span>\n ) : (\n <CheckIcon className="size-4 shrink-0 opacity-0 transition-opacity group-data-[state=checked]/option:opacity-100" />\n )}\n </QuestionOption>\n ))}\n </QuestionOptions>\n ) : null}\n {acceptsFreeform ? (\n <div className="relative">\n <QuestionInput\n aria-label="Answer"\n className={inputResponse === undefined ? "pr-12 pb-12" : undefined}\n placeholder="Type your answer…"\n />\n {inputResponse === undefined && questionValue.text.trim().length > 0 ? (\n <QuestionSubmit\n aria-label="Answer"\n className="absolute right-2 bottom-2"\n size="icon-sm"\n >\n <ArrowRightIcon />\n </QuestionSubmit>\n ) : null}\n </div>\n ) : null}\n </Question>\n );\n}\n\nfunction AttachmentPart({ part }: { readonly part: EveFilePart }) {\n const label = part.filename ?? "Attachment";\n const detail = [part.mediaType, formatBytes(part.size)].filter(Boolean).join(" - ");\n const isImage = part.mediaType.startsWith("image/") && part.url !== undefined;\n const Icon = isImage ? ImageIcon : FileIcon;\n const body = (\n <span className="flex max-w-sm items-center gap-3 rounded-md border bg-background/60 p-2 text-sm">\n {isImage ? (\n <img alt={label} className="size-12 shrink-0 rounded-sm object-cover" src={part.url} />\n ) : (\n <span className="flex size-10 shrink-0 items-center justify-center rounded-sm bg-muted text-muted-foreground">\n <Icon className="size-4" />\n </span>\n )}\n <span className="min-w-0 flex-1">\n <span className="block truncate font-medium">{label}</span>\n {detail ? <span className="block truncate text-muted-foreground">{detail}</span> : null}\n </span>\n {part.url ? <ExternalLinkIcon className="size-4 shrink-0 text-muted-foreground" /> : null}\n </span>\n );\n\n return part.url ? (\n <a href={part.url} rel="noreferrer" target="_blank">\n {body}\n </a>\n ) : (\n body\n );\n}\n\nfunction AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {\n const isAuthorized = part.state === "completed" && part.outcome === "authorized";\n const isCompleted = part.state === "completed";\n const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;\n const instructions = part.authorization?.instructions;\n const shouldShowInstructions = instructions !== undefined && instructions !== part.description;\n\n return (\n <div\n className={cn(\n "space-y-3 rounded-md border p-3",\n isAuthorized\n ? "border-emerald-500/30 bg-emerald-500/5"\n : isCompleted\n ? "border-destructive/30 bg-destructive/5"\n : "border-blue-500/30 bg-blue-500/5",\n )}\n >\n <div className="flex items-start gap-3">\n <span\n className={cn(\n "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",\n isAuthorized\n ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"\n : isCompleted\n ? "bg-destructive/10 text-destructive"\n : "bg-blue-500/10 text-blue-700 dark:text-blue-300",\n )}\n >\n <Icon className="size-4" />\n </span>\n <div className="min-w-0 flex-1 space-y-2">\n <p className="font-medium text-sm">{authorizationTitle(part)}</p>\n <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>\n {shouldShowInstructions ? (\n <p className="text-muted-foreground text-sm">{instructions}</p>\n ) : null}\n {part.state === "required" && part.authorization?.userCode ? (\n <div className="flex flex-wrap items-center gap-2 text-sm">\n <span className="text-muted-foreground">Code</span>\n <code className="rounded-md bg-background px-2 py-1 font-mono">\n {part.authorization.userCode}\n </code>\n </div>\n ) : null}\n {part.state === "required" && part.authorization?.url ? (\n <Button asChild size="sm">\n <a href={part.authorization.url} rel="noreferrer" target="_blank">\n <ExternalLinkIcon className="size-4" />\n Sign in with {part.displayName}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n\nfunction authorizationTitle(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return `Connect ${part.displayName}`;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected`;\n }\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}`;\n}\n\nfunction authorizationDescription(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return part.description;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected.`;\n }\n const tail = part.reason !== undefined ? ` (${part.reason})` : "";\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}${tail}.`;\n}\n\nfunction formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {\n switch (outcome) {\n case "authorized":\n return "authorized";\n case "declined":\n return "declined";\n case "failed":\n return "failed";\n case "timed-out":\n return "timed out";\n }\n}\n\nfunction formatBytes(size: number | undefined): string | undefined {\n if (size === undefined) {\n return undefined;\n }\n if (size < 1024) {\n return `${size} B`;\n }\n if (size < 1024 * 1024) {\n return `${(size / 1024).toFixed(1)} KB`;\n }\n return `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveDynamicToolPart;\n}) {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (!inputRequest) {\n return null;\n }\n\n const inputResponse = part.toolMetadata?.eve?.inputResponse;\n const selectedOption = inputRequest.options?.find(\n (option) => option.id === inputResponse?.optionId,\n );\n\n return (\n <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n {inputResponse ? (\n <p className="font-medium text-sm">\n Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond}\n key={option.id}\n onClick={() => {\n void onInputResponses([\n {\n optionId: option.id,\n requestId: inputRequest.requestId,\n },\n ]);\n }}\n size="sm"\n type="button"\n variant={option.style === "danger" ? "destructive" : "default"}\n >\n {option.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n switch (part.type) {\n case "authorization":\n return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;\n case "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
|
|
5
4
|
readonly "app/apple-icon.tsx": 'import { ImageResponse } from "next/og";\n\nexport const size = {\n width: 180,\n height: 180,\n};\n\nexport const contentType = "image/png";\n\nexport default function AppleIcon() {\n return new ImageResponse(\n <svg fill="none" viewBox="0 0 102 102" xmlns="http://www.w3.org/2000/svg">\n <path d="M0 0h102v102H0z" fill="#000" />\n <path\n d="M49.28 66.94 75.03 34.96h-6.89L47.91 60.11l-5.49 6.83h6.86ZM0 34.96h42.4v5.11H0zm0 13.32h27.66v5.11H0zm0 13.54h27.66v5.11H0zm69.63-26.86H102v5.11H69.63zm4.71 13.32H102v5.11H74.34zm0 13.54H102v5.11H74.34z"\n fill="#fff"\n />\n </svg>,\n size,\n );\n}\n';
|
|
5
|
+
readonly "app/eve-agent.ts": '/** Named workspace agent selected by the Web Chat installer. */\nexport const WEB_CHAT_AGENT: string | undefined = undefined;\n';
|
|
6
6
|
readonly "app/globals.css": '@import "tailwindcss";\n@source "../node_modules/streamdown/dist/*.js";\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;\n --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;\n}\n\n:root {\n color-scheme: light;\n /* Soft neutral page with white elevated surfaces so cards/composer pop. */\n --background: oklch(0.971 0 0);\n --foreground: oklch(0.16 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.16 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.16 0 0);\n --primary: oklch(0.19 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.94 0 0);\n --secondary-foreground: oklch(0.19 0 0);\n --muted: oklch(0.94 0 0);\n --muted-foreground: oklch(0.6 0 0);\n --accent: oklch(0.94 0 0);\n --accent-foreground: oklch(0.19 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.916 0 0);\n --input: oklch(0.916 0 0);\n --ring: oklch(0.708 0 0);\n --radius: 0.625rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n }\n}\n\n* {\n border-color: var(--border);\n}\n\nhtml {\n height: 100%;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nbody {\n min-height: 100%;\n margin: 0;\n background: var(--background);\n font-family: var(--font-sans);\n}\n\nbutton,\ninput,\ntextarea {\n font: inherit;\n}\n';
|
|
7
7
|
readonly "app/icon.svg": '<svg width="102" height="102" viewBox="0 0 102 102" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path fill="#000" d="M0 0h102v102H0z" />\n <path\n fill="#fff"\n d="M49.28 66.94 75.03 34.96h-6.89L47.91 60.11l-5.49 6.83h6.86ZM0 34.96h42.4v5.11H0zm0 13.32h27.66v5.11H0zm0 13.54h27.66v5.11H0zm69.63-26.86H102v5.11H69.63zm4.71 13.32H102v5.11H74.34zm0 13.54H102v5.11H74.34z"\n />\n</svg>\n';
|
|
8
8
|
readonly "app/layout.tsx": 'import type { Metadata } from "next";\nimport { Geist, Geist_Mono } from "next/font/google";\nimport type { ReactNode } from "react";\nimport { TooltipProvider } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport "./globals.css";\n\nconst sans = Geist({\n variable: "--font-sans",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nconst mono = Geist_Mono({\n variable: "--font-mono",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nexport const metadata: Metadata = {\n title: "__EVE_INIT_APP_NAME__",\n description: "A Next.js starter for eve agents with AI Elements.",\n};\n\nexport default function RootLayout({ children }: { readonly children: ReactNode }) {\n return (\n <html className={cn(sans.variable, mono.variable)} lang="en">\n <body>\n <TooltipProvider>{children}</TooltipProvider>\n </body>\n </html>\n );\n}\n';
|
|
@@ -39,10 +39,14 @@ export declare const WEB_APP_TEMPLATE_FILES: {
|
|
|
39
39
|
readonly "next-env.d.ts": '/// <reference types="next" />\n/// <reference types="next/image-types/global" />\nimport "./.next/types/routes.d.ts";\nimport "./.next/types/root-params.d.ts";\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.\n';
|
|
40
40
|
readonly "next.config.ts": 'import type { NextConfig } from "next";\nimport { withEve } from "eve/next";\n\nconst nextConfig: NextConfig = {};\n\nexport default withEve(nextConfig__EVE_INIT_WITH_EVE_OPTIONS__);\n';
|
|
41
41
|
readonly "postcss.config.mjs": 'const config = {\n plugins: {\n "@tailwindcss/postcss": {},\n },\n};\n\nexport default config;\n';
|
|
42
|
+
readonly "proxy.ts": 'import type { NextRequest } from "next/server";\nimport { NextResponse } from "next/server";\n\nexport function proxy(request: NextRequest) {\n const routerHost = process.env.VERCEL_URL;\n const forwardedHost = request.headers.get("x-forwarded-host");\n if (process.env.__VERCEL_DEV_RUNNING === "1" && routerHost && forwardedHost !== routerHost) {\n const target = request.nextUrl.clone();\n target.protocol = "http:";\n target.host = routerHost;\n return NextResponse.redirect(target);\n }\n return NextResponse.next();\n}\n';
|
|
42
43
|
readonly "tsconfig.json": '{\n "$schema": "https://json.schemastore.org/tsconfig",\n "compilerOptions": {\n "target": "ES2017",\n "lib": ["dom", "dom.iterable", "esnext"],\n "allowJs": true,\n "skipLibCheck": true,\n "strict": true,\n "noEmit": true,\n "esModuleInterop": true,\n "module": "esnext",\n "moduleResolution": "Bundler",\n "resolveJsonModule": true,\n "isolatedModules": true,\n "jsx": "react-jsx",\n "incremental": true,\n "plugins": [\n {\n "name": "next"\n }\n ],\n "paths": {\n "@/*": ["./*"]\n }\n },\n "include": [\n "next-env.d.ts",\n "**/*.ts",\n "**/*.tsx",\n ".next/types/**/*.ts",\n ".next/dev/types/**/*.ts"\n ],\n "exclude": ["node_modules"]\n}\n';
|
|
43
44
|
};
|
|
45
|
+
export declare const WEB_CHANNEL_TEMPLATES: {
|
|
46
|
+
readonly default: 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
|
|
47
|
+
readonly "sign-in-with-vercel": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, type AuthFn, vercelOidc } from "eve/channels/auth";\nimport { auth } from "@/lib/auth";\n\nconst betterAuthSession: AuthFn<Request> = async (request) => {\n const session = await auth.api.getSession({ headers: request.headers });\n if (!session) return null;\n\n const attributes: Record<string, string> = {\n email: session.user.email,\n name: session.user.name,\n };\n if (session.user.image) {\n attributes.picture = session.user.image;\n }\n\n return {\n attributes,\n authenticator: "better-auth:vercel",\n principalId: session.user.id,\n principalType: "user",\n };\n};\n\nexport default eveChannel({\n auth: [betterAuthSession, vercelOidc(), localDev()],\n});\n';
|
|
48
|
+
};
|
|
44
49
|
export declare const WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES: {
|
|
45
|
-
readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, type AuthFn, vercelOidc } from "eve/channels/auth";\nimport { auth } from "@/lib/auth";\n\nconst betterAuthSession: AuthFn<Request> = async (request) => {\n const session = await auth.api.getSession({ headers: request.headers });\n if (!session) return null;\n\n const attributes: Record<string, string> = {\n email: session.user.email,\n name: session.user.name,\n };\n if (session.user.image) {\n attributes.picture = session.user.image;\n }\n\n return {\n attributes,\n authenticator: "better-auth:vercel",\n principalId: session.user.id,\n principalType: "user",\n };\n};\n\nexport default eveChannel({\n auth: [betterAuthSession, vercelOidc(), localDev()],\n});\n';
|
|
46
50
|
readonly "app/_components/authenticated-agent-chat.tsx": 'import { headers } from "next/headers";\nimport { auth } from "@/lib/auth";\nimport { AgentChat } from "./agent-chat";\nimport { AccountControl, SignIn } from "./web-chat-auth";\n\nexport async function AuthenticatedAgentChat({\n sessionId,\n sessionless,\n}: {\n readonly sessionId?: string;\n readonly sessionless?: boolean;\n}) {\n if (process.env.NODE_ENV === "development") {\n return <AgentChat sessionId={sessionId} sessionless={sessionless} />;\n }\n\n const session = await auth.api.getSession({ headers: await headers() });\n if (!session) return <SignIn />;\n\n return (\n <>\n <AgentChat sessionId={sessionId} sessionless={sessionless} />\n <AccountControl\n email={session.user.email}\n image={session.user.image}\n name={session.user.name}\n />\n </>\n );\n}\n';
|
|
47
51
|
readonly "app/_components/web-chat-auth.tsx": '"use client";\n\nimport { LogOutIcon } from "lucide-react";\nimport { useState } from "react";\nimport { Button } from "@/components/ui/button";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from "@/components/ui/dropdown-menu";\nimport { authClient } from "@/lib/auth-client";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\nexport function SignIn() {\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string>();\n\n async function signIn() {\n setPending(true);\n setError(undefined);\n try {\n const result = await authClient.signIn.social({\n callbackURL: "/",\n provider: "vercel",\n });\n if (!result.error) return;\n setPending(false);\n setError("Sign-in failed. Try again.");\n } catch {\n setPending(false);\n setError("Sign-in failed. Try again.");\n }\n }\n\n return (\n <main className="flex min-h-dvh items-center justify-center bg-background px-8 text-foreground">\n <div className="flex w-full max-w-[22rem] flex-col gap-5">\n <div className="text-foreground opacity-[0.08] dark:opacity-[0.12]">\n <EveWordmark className="h-auto w-[4.875rem]" />\n </div>\n <section aria-label="Sign in" className="flex flex-col gap-2">\n <h1 className="max-w-full break-words font-medium text-sm leading-6">{AGENT_NAME}</h1>\n <p className="flex flex-wrap items-center gap-2 text-muted-foreground text-sm leading-6">\n <span className="inline-flex items-center gap-2 text-emerald-600 dark:text-emerald-400">\n <span aria-hidden="true" className="size-1.5 rounded-full bg-current" />\n Ready\n </span>\n <span aria-hidden="true" className="text-border">\n /\n </span>\n <span>Sign in to start a session</span>\n </p>\n <Button className="mt-3 w-full gap-2 text-sm" disabled={pending} onClick={signIn}>\n <svg aria-hidden="true" className="size-3 fill-current" viewBox="0 0 24 20">\n <path d="M12 0 24 20H0L12 0Z" />\n </svg>\n <span className="leading-5">{pending ? "Redirecting…" : "Continue with Vercel"}</span>\n </Button>\n {error ? (\n <p className="text-destructive text-sm" role="alert">\n {error}\n </p>\n ) : null}\n </section>\n </div>\n </main>\n );\n}\n\nfunction EveWordmark({ className }: { readonly className?: string }) {\n return (\n <svg\n aria-hidden="true"\n className={className}\n fill="none"\n viewBox="0 0 169 53"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n d="M169 8.47h-51.39L81.73 53H70.36L113 0H169zM169 44.51v8.47h-45.87V44.5zM45.87 52.98H0V44.5h45.87zM38.66 30.55H0v-8.47h38.66z"\n fill="currentColor"\n />\n <path d="M169 30.55h-38.66v-8.47H169zM75.52 8.47H0V0h75.52z" fill="currentColor" />\n </svg>\n );\n}\n\nexport function AccountControl({\n email,\n image,\n name,\n}: {\n readonly email: string;\n readonly image?: string | null;\n readonly name: string;\n}) {\n const [imageFailed, setImageFailed] = useState(false);\n const [pending, setPending] = useState(false);\n const initials = getInitials(name, email);\n\n async function signOut() {\n setPending(true);\n try {\n await authClient.signOut({\n fetchOptions: {\n onError: () => setPending(false),\n onSuccess: () => window.location.assign("/"),\n },\n });\n } catch {\n setPending(false);\n }\n }\n\n return (\n <div className="fixed top-3 left-4 z-30 flex h-8 items-center">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n aria-label={`Open account menu for ${name}`}\n className="relative size-7 cursor-pointer overflow-hidden rounded-full p-0"\n size="icon-sm"\n variant="ghost"\n >\n {image && !imageFailed ? (\n <img\n alt=""\n className="size-full object-cover"\n onError={() => setImageFailed(true)}\n src={image}\n />\n ) : (\n <span aria-hidden="true" className="font-medium text-xs">\n {initials}\n </span>\n )}\n <span\n aria-hidden="true"\n className="pointer-events-none absolute inset-0 rounded-full border border-black/20 dark:border-white/25"\n />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align="start" className="w-64">\n <div className="min-w-0 px-2 py-1.5 text-sm">\n <span className="block truncate font-medium leading-5" title={name}>\n {name}\n </span>\n <span className="block truncate text-muted-foreground leading-5" title={email}>\n {email}\n </span>\n </div>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className="cursor-pointer justify-between"\n disabled={pending}\n onSelect={signOut}\n >\n {pending ? "Logging out…" : "Log out"}\n <LogOutIcon aria-hidden="true" />\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n\nfunction getInitials(name: string, email: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length >= 2) {\n return `${parts[0]?.[0] ?? ""}${parts.at(-1)?.[0] ?? ""}`.toUpperCase();\n }\n return (parts[0]?.[0] ?? email[0] ?? "?").toUpperCase();\n}\n';
|
|
48
52
|
readonly "app/api/auth/[...all]/route.ts": 'import { toNextJsHandler } from "better-auth/next-js";\nimport { auth } from "@/lib/auth";\n\nexport const { GET, POST } = toNextJsHandler(auth);\n';
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
const WEB_APP_TEMPLATE_FILES={"
|
|
2
|
-
import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";
|
|
3
|
-
|
|
4
|
-
export default eveChannel({
|
|
5
|
-
auth: [
|
|
6
|
-
// Lets the eve TUI and your Vercel deployments reach the deployed agent.
|
|
7
|
-
vercelOidc(),
|
|
8
|
-
// Open on localhost for \`eve dev\` and the REPL; ignored in production.
|
|
9
|
-
localDev(),
|
|
10
|
-
// This placeholder will not allow browser requests in production.
|
|
11
|
-
// Replace it with your app's auth provider, like Auth.js or Clerk,
|
|
12
|
-
// or use none() for a public demo.
|
|
13
|
-
placeholderAuth(),
|
|
14
|
-
],
|
|
15
|
-
});
|
|
16
|
-
`,"app/_components/agent-chat.tsx":`"use client";
|
|
1
|
+
const WEB_APP_TEMPLATE_FILES={"app/_components/agent-chat.tsx":`"use client";
|
|
17
2
|
|
|
18
3
|
import type { UserContent } from "ai";
|
|
19
4
|
import { useEveAgent } from "eve/react";
|
|
@@ -38,8 +23,10 @@ import { Shimmer } from "@/components/ai-elements/shimmer";
|
|
|
38
23
|
import { Button } from "@/components/ui/button";
|
|
39
24
|
import { cn } from "@/lib/utils";
|
|
40
25
|
import { AgentMessage } from "./agent-message";
|
|
26
|
+
import { WEB_CHAT_AGENT } from "@/app/eve-agent";
|
|
41
27
|
|
|
42
|
-
const
|
|
28
|
+
const DEFAULT_AGENT_NAME = "__EVE_INIT_APP_NAME__";
|
|
29
|
+
const AGENT_NAME = WEB_CHAT_AGENT ?? DEFAULT_AGENT_NAME;
|
|
43
30
|
|
|
44
31
|
export function AgentChat({
|
|
45
32
|
sessionId,
|
|
@@ -51,6 +38,7 @@ export function AgentChat({
|
|
|
51
38
|
const [cancellationError, setCancellationError] = useState<string>();
|
|
52
39
|
const [hasInputText, setHasInputText] = useState(false);
|
|
53
40
|
const agent = useEveAgent({
|
|
41
|
+
agent: WEB_CHAT_AGENT,
|
|
54
42
|
initialSession:
|
|
55
43
|
sessionId === undefined
|
|
56
44
|
? undefined
|
|
@@ -801,6 +789,8 @@ export default function AppleIcon() {
|
|
|
801
789
|
size,
|
|
802
790
|
);
|
|
803
791
|
}
|
|
792
|
+
`,"app/eve-agent.ts":`/** Named workspace agent selected by the Web Chat installer. */
|
|
793
|
+
export const WEB_CHAT_AGENT: string | undefined = undefined;
|
|
804
794
|
`,"app/globals.css":`@import "tailwindcss";
|
|
805
795
|
@source "../node_modules/streamdown/dist/*.js";
|
|
806
796
|
|
|
@@ -5698,6 +5688,20 @@ export default withEve(nextConfig__EVE_INIT_WITH_EVE_OPTIONS__);
|
|
|
5698
5688
|
};
|
|
5699
5689
|
|
|
5700
5690
|
export default config;
|
|
5691
|
+
`,"proxy.ts":`import type { NextRequest } from "next/server";
|
|
5692
|
+
import { NextResponse } from "next/server";
|
|
5693
|
+
|
|
5694
|
+
export function proxy(request: NextRequest) {
|
|
5695
|
+
const routerHost = process.env.VERCEL_URL;
|
|
5696
|
+
const forwardedHost = request.headers.get("x-forwarded-host");
|
|
5697
|
+
if (process.env.__VERCEL_DEV_RUNNING === "1" && routerHost && forwardedHost !== routerHost) {
|
|
5698
|
+
const target = request.nextUrl.clone();
|
|
5699
|
+
target.protocol = "http:";
|
|
5700
|
+
target.host = routerHost;
|
|
5701
|
+
return NextResponse.redirect(target);
|
|
5702
|
+
}
|
|
5703
|
+
return NextResponse.next();
|
|
5704
|
+
}
|
|
5701
5705
|
`,"tsconfig.json":`{
|
|
5702
5706
|
"$schema": "https://json.schemastore.org/tsconfig",
|
|
5703
5707
|
"compilerOptions": {
|
|
@@ -5732,7 +5736,22 @@ export default config;
|
|
|
5732
5736
|
],
|
|
5733
5737
|
"exclude": ["node_modules"]
|
|
5734
5738
|
}
|
|
5735
|
-
`},
|
|
5739
|
+
`},WEB_CHANNEL_TEMPLATES={default:`import { eveChannel } from "eve/channels/eve";
|
|
5740
|
+
import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";
|
|
5741
|
+
|
|
5742
|
+
export default eveChannel({
|
|
5743
|
+
auth: [
|
|
5744
|
+
// Lets the eve TUI and your Vercel deployments reach the deployed agent.
|
|
5745
|
+
vercelOidc(),
|
|
5746
|
+
// Open on localhost for \`eve dev\` and the REPL; ignored in production.
|
|
5747
|
+
localDev(),
|
|
5748
|
+
// This placeholder will not allow browser requests in production.
|
|
5749
|
+
// Replace it with your app's auth provider, like Auth.js or Clerk,
|
|
5750
|
+
// or use none() for a public demo.
|
|
5751
|
+
placeholderAuth(),
|
|
5752
|
+
],
|
|
5753
|
+
});
|
|
5754
|
+
`,"sign-in-with-vercel":`import { eveChannel } from "eve/channels/eve";
|
|
5736
5755
|
import { localDev, type AuthFn, vercelOidc } from "eve/channels/auth";
|
|
5737
5756
|
import { auth } from "@/lib/auth";
|
|
5738
5757
|
|
|
@@ -5759,7 +5778,7 @@ const betterAuthSession: AuthFn<Request> = async (request) => {
|
|
|
5759
5778
|
export default eveChannel({
|
|
5760
5779
|
auth: [betterAuthSession, vercelOidc(), localDev()],
|
|
5761
5780
|
});
|
|
5762
|
-
|
|
5781
|
+
`},WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES={"app/_components/authenticated-agent-chat.tsx":`import { headers } from "next/headers";
|
|
5763
5782
|
import { auth } from "@/lib/auth";
|
|
5764
5783
|
import { AgentChat } from "./agent-chat";
|
|
5765
5784
|
import { AccountControl, SignIn } from "./web-chat-auth";
|
|
@@ -6080,4 +6099,4 @@ export const auth = betterAuth({
|
|
|
6080
6099
|
},
|
|
6081
6100
|
},
|
|
6082
6101
|
});
|
|
6083
|
-
`},WEB_APP_TEMPLATE_PACKAGE_JSON={scripts:{build:`next build`,"build:eve":`eve build`,dev:`next dev`,"dev:eve":`eve dev`,start:`next start`,"start:eve":`eve start`,typecheck:`tsc --noEmit -p tsconfig.json`},dependencies:{"@radix-ui/react-use-controllable-state":`1.2.2`,"@shikijs/core":`3.23.0`,"@shikijs/engine-javascript":`3.23.0`,"@shikijs/engine-oniguruma":`3.23.0`,"@streamdown/cjk":`1.0.3`,"@streamdown/code":`1.1.1`,"@streamdown/math":`1.0.2`,"@streamdown/mermaid":`1.0.2`,"@tailwindcss/postcss":`4.3.0`,"class-variance-authority":`0.7.1`,clsx:`2.1.1`,cmdk:`1.1.1`,"lucide-react":`1.16.0`,motion:`12.40.0`,nanoid:`5.1.11`,next:`16.3.0-preview.6`,"radix-ui":`1.4.3`,react:`19.2.6`,"react-dom":`19.2.6`,shiki:`3.23.0`,streamdown:`2.5.0`,"tailwind-merge":`3.6.0`,tailwindcss:`4.3.0`,"use-stick-to-bottom":`1.1.4`,zod:`4.5.4`},devDependencies:{"@types/node":`26`,"@types/react":`19.2.15`,"@types/react-dom":`19.2.3`,typescript:`6.0.3`}};export{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON};
|
|
6102
|
+
`},WEB_APP_TEMPLATE_PACKAGE_JSON={scripts:{build:`next build`,"build:eve":`eve build`,dev:`next dev`,"dev:eve":`eve dev`,start:`next start`,"start:eve":`eve start`,typecheck:`tsc --noEmit -p tsconfig.json`},dependencies:{"@radix-ui/react-use-controllable-state":`1.2.2`,"@shikijs/core":`3.23.0`,"@shikijs/engine-javascript":`3.23.0`,"@shikijs/engine-oniguruma":`3.23.0`,"@streamdown/cjk":`1.0.3`,"@streamdown/code":`1.1.1`,"@streamdown/math":`1.0.2`,"@streamdown/mermaid":`1.0.2`,"@tailwindcss/postcss":`4.3.0`,"class-variance-authority":`0.7.1`,clsx:`2.1.1`,cmdk:`1.1.1`,"lucide-react":`1.16.0`,motion:`12.40.0`,nanoid:`5.1.11`,next:`16.3.0-preview.6`,"radix-ui":`1.4.3`,react:`19.2.6`,"react-dom":`19.2.6`,shiki:`3.23.0`,streamdown:`2.5.0`,"tailwind-merge":`3.6.0`,tailwindcss:`4.3.0`,"use-stick-to-bottom":`1.1.4`,zod:`4.5.4`},devDependencies:{"@types/node":`26`,"@types/react":`19.2.15`,"@types/react-dom":`19.2.3`,typescript:`6.0.3`}};export{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON,WEB_CHANNEL_TEMPLATES};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{appendEnv}from"../../append-env.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"../create/web-template.js";import{resolveEvePackageContract}from"../create/project.js";import{resolveWebPackageVersions}from"./web-options.js";import{readFile,readdir,unlink,writeFile}from"node:fs/promises";import{basename,join,resolve}from"node:path";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH);function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readPackageJsonObject(e){if(!await pathExists(e))return;let t=JSON.parse(await readFile(e,`utf8`));return isJsonObject(t)?t:void 0}async function readDependencyVersion(e,t){let n=await readPackageJsonObject(e);if(n===void 0||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){let n=await readPackageJsonObject(e);return n!==void 0&&packageJsonHasDependency(n,t)}const VERCEL_HOST_FRAMEWORK_PRESETS={"@sveltejs/kit":`sveltekit`,[NEXT_PACKAGE_NAME]:`nextjs`,nuxt:`nuxtjs`,nuxt3:`nuxtjs`,"nuxt-edge":`nuxtjs`,"nuxt-nightly":`nuxtjs`};async function resolveVercelHostFrameworkPreset(e){let t=await readPackageJsonObject(join(e,`package.json`));if(t!==void 0){for(let[e,n]of Object.entries(VERCEL_HOST_FRAMEWORK_PRESETS))if(packageJsonHasDependency(t,e))return n}}async function hasVercelHostFramework(e){return await resolveVercelHostFrameworkPreset(e)!==void 0}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}async function ensurePackageDependency(e,t,n){return!await pathExists(e)||await readDependencyVersion(e,t)===n?[]:(await patchPackageJson(e,{dependencies:{[t]:n}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t,n,r,i,a=!0,o){let s=join(e,`package.json`);if(!await pathExists(s))return{mutations:[]};let c=resolveEvePackageContract(r.evePackage),l=pinnedNodeEngineMajor(c.nodeEngine),u={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,r.aiPackageVersion),eve:formatEveDependencySpecifier(c.version),next:resolveVersionToken(`nextPackageVersion`,r.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,r.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,r.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,r.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,r.zodPackageVersion),...o===`sign-in-with-vercel`?{"better-auth":resolveVersionToken(`betterAuthPackageVersion`,r.betterAuthPackageVersion)}:{}},d={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":l,"@types/react":resolveVersionToken(`typesReactPackageVersion`,r.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,r.typesReactDomPackageVersion),typescript:`6.0.3`},f=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,p=isPackageManagerWorkspaceMember(t,n),m={scripts:f};a&&(m.dependencies=u,m.devDependencies=d),p||(m.nodeEngineRequirement=c.nodeEngine);let h=await patchPackageJson(s,m),g=(await patchWorkspaceRootPackageJson(t,n,{nodeEngineRequirement:c.nodeEngine,onWorkspaceRootMutation:i})).nodeEngineOverride??h.nodeEngineOverride;return{mutations:[{path:s,dependencies:a?Object.keys(u):[],devDependencies:a?Object.keys(d):[],scripts:Object.keys(f)}],nodeEngineOverride:g}}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackConnectTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
1
|
+
import{appendEnv}from"../../append-env.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON,WEB_CHANNEL_TEMPLATES}from"../create/web-template.js";import{resolveEvePackageContract}from"../create/project.js";import{resolveWebPackageVersions}from"./web-options.js";import{readFile,readdir,unlink,writeFile}from"node:fs/promises";import{basename,join,resolve}from"node:path";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_CHANNEL_PATH=`agent/channels/eve.ts`,WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH);function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readPackageJsonObject(e){if(!await pathExists(e))return;let t=JSON.parse(await readFile(e,`utf8`));return isJsonObject(t)?t:void 0}async function readDependencyVersion(e,t){let n=await readPackageJsonObject(e);if(n===void 0||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){let n=await readPackageJsonObject(e);return n!==void 0&&packageJsonHasDependency(n,t)}const VERCEL_HOST_FRAMEWORK_PRESETS={"@sveltejs/kit":`sveltekit`,[NEXT_PACKAGE_NAME]:`nextjs`,nuxt:`nuxtjs`,nuxt3:`nuxtjs`,"nuxt-edge":`nuxtjs`,"nuxt-nightly":`nuxtjs`};async function resolveVercelHostFrameworkPreset(e){if(await pathExists(join(e,`vercel.ts`)))return`services`;let t=await readPackageJsonObject(join(e,`package.json`));if(t!==void 0){for(let[e,n]of Object.entries(VERCEL_HOST_FRAMEWORK_PRESETS))if(packageJsonHasDependency(t,e))return n}}async function hasVercelHostFramework(e){return await resolveVercelHostFrameworkPreset(e)!==void 0}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}async function ensurePackageDependency(e,t,n){return!await pathExists(e)||await readDependencyVersion(e,t)===n?[]:(await patchPackageJson(e,{dependencies:{[t]:n}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t,n,r,i,a=!0,o){let s=join(e,`package.json`);if(!await pathExists(s))return{mutations:[]};let c=resolveEvePackageContract(r.evePackage),l=pinnedNodeEngineMajor(c.nodeEngine),u={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,r.aiPackageVersion),eve:formatEveDependencySpecifier(c.version),next:resolveVersionToken(`nextPackageVersion`,r.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,r.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,r.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,r.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,r.zodPackageVersion),...o===`sign-in-with-vercel`?{"better-auth":resolveVersionToken(`betterAuthPackageVersion`,r.betterAuthPackageVersion)}:{}},d={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":l,"@types/react":resolveVersionToken(`typesReactPackageVersion`,r.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,r.typesReactDomPackageVersion),typescript:`6.0.3`},f=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,p=isPackageManagerWorkspaceMember(t,n),m={scripts:f};a&&(m.dependencies=u,m.devDependencies=d),p||(m.nodeEngineRequirement=c.nodeEngine);let h=await patchPackageJson(s,m),g=(await patchWorkspaceRootPackageJson(t,n,{nodeEngineRequirement:c.nodeEngine,onWorkspaceRootMutation:i})).nodeEngineOverride??h.nodeEngineOverride;return{mutations:[{path:s,dependencies:a?Object.keys(u):[],devDependencies:a?Object.keys(d):[],scripts:Object.keys(f)}],nodeEngineOverride:g}}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackConnectTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
2
2
|
import { slackChannel } from "eve/channels/slack";
|
|
3
3
|
|
|
4
4
|
export default slackChannel({
|
|
5
5
|
credentials: connectSlackCredentials(${JSON.stringify(e)}),
|
|
6
6
|
});
|
|
7
|
-
`}const SLACK_ENV_EXAMPLE_VALUES={SLACK_BOT_TOKEN:``,SLACK_SIGNING_SECRET:``};function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}async function ensureWebVercelJson(e){return await pathExists(e)?`skipped`:(await writeTextFile(e,`${JSON.stringify({$schema:`https://openapi.vercel.sh/vercel.json`},null,2)}\n`,{force:!0}),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let n of WEB_COMPETING_NEXT_CONFIG_PATHS){let r=join(e,n);await pathExists(r)&&t.push(r)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),n=join(e.projectRoot,`app/page.tsx`),r=await pathExists(n);if(!e.force&&!e.skipDependencyMutation&&await isNextJsProject(e.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let i=resolveWebPackageVersions(e.webPackageVersions,e.webAuthentication),a=e.packageManager??`pnpm`,o=resolve(e.workspaceProbeDirectory??e.projectRoot),s=await patchWebPackageJson(e.projectRoot,a,o,i,e.onWorkspaceRootMutation,!e.skipDependencyMutation,e.webAuthentication),c=[],l=[],u=[],d=[],f=basename(resolve(e.projectRoot));if(e.configureVercelServices??!0){let t=join(e.projectRoot,`vercel.json`);await ensureWebVercelJson(t)===`written`?c.push(t):d.push(t)}let p=await applyPackageManagerWorkspaceConfiguration({packageManager:a,projectRoot:e.projectRoot,workspaceProbeRoot:o,onWorkspaceRootMutation:e.onWorkspaceRootMutation});if(c.push(...p.filesWritten),d.push(...p.filesSkipped),!e.skipDependencyMutation){let t={...WEB_APP_TEMPLATE_FILES,...e.webAuthentication===`sign-in-with-vercel`?WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES:{}};for(let[
|
|
7
|
+
`}const SLACK_ENV_EXAMPLE_VALUES={SLACK_BOT_TOKEN:``,SLACK_SIGNING_SECRET:``};function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}async function ensureWebVercelJson(e){return await pathExists(e)?`skipped`:(await writeTextFile(e,`${JSON.stringify({$schema:`https://openapi.vercel.sh/vercel.json`},null,2)}\n`,{force:!0}),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let n of WEB_COMPETING_NEXT_CONFIG_PATHS){let r=join(e,n);await pathExists(r)&&t.push(r)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),n=join(e.projectRoot,`app/page.tsx`),r=await pathExists(n);if(!e.force&&!e.skipDependencyMutation&&await isNextJsProject(e.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let i=resolveWebPackageVersions(e.webPackageVersions,e.webAuthentication),a=e.packageManager??`pnpm`,o=resolve(e.workspaceProbeDirectory??e.projectRoot),s=await patchWebPackageJson(e.projectRoot,a,o,i,e.onWorkspaceRootMutation,!e.skipDependencyMutation,e.webAuthentication),c=[],l=[],u=[],d=[],f=basename(resolve(e.projectRoot));if(e.configureVercelServices??!0){let t=join(e.projectRoot,`vercel.json`);await ensureWebVercelJson(t)===`written`?c.push(t):d.push(t)}let p=await applyPackageManagerWorkspaceConfiguration({packageManager:a,projectRoot:e.projectRoot,workspaceProbeRoot:o,onWorkspaceRootMutation:e.onWorkspaceRootMutation});if(c.push(...p.filesWritten),d.push(...p.filesSkipped),!e.skipDependencyMutation){let t=WEB_CHANNEL_TEMPLATES[e.webAuthentication??`default`],n={...WEB_APP_TEMPLATE_FILES,...e.webAuthentication===`sign-in-with-vercel`?WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES:{},[WEB_CHANNEL_PATH]:t};for(let[t,r]of Object.entries(n)){let n=join(e.projectRoot,t);if(t===WEB_CHANNEL_PATH&&!e.force&&await pathExists(n)){d.push(n);continue}let i=await pathExists(n);await writeTextFile(n,renderWebAppTemplate(r,f),{force:!0}),c.push(n),i&&l.push(n)}}u.push(...await findCompetingNextConfigFiles(e.projectRoot));let m=[...new Set(c)],h=[...new Set(d)].filter(e=>!m.includes(e)),g={kind:`web`,action:r?`overwritten`:`created`,filesWritten:m,filesSkipped:h,packageJsonUpdated:s.mutations};return l.length>0&&(g.filesOverwritten=l),u.length>0&&(g.competingNextConfigFiles=u),s.nodeEngineOverride!==void 0&&(g.nodeEngineOverride=s.nodeEngineOverride),g}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),n=await pathExists(t);if(!e.force&&n)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let r=e.slackCredentials??`vercel-connect`,i=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot),a,o=[],s=[t],c=[],l;if(r===`vercel-connect`){if(!e.skipDependencyMutation){let t=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`);o=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,t)}a=buildSlackConnectTemplate(e.slackConnectorUid??`slack/${i}`)}else{a=`import { slackChannel } from "eve/channels/slack";
|
|
8
8
|
|
|
9
9
|
export default slackChannel();
|
|
10
10
|
`;let t=join(e.environmentRoot??e.projectRoot,`.env.example`);l={path:t,...await pathExists(t)?{content:await readFile(t,`utf8`)}:{}},(await appendEnv(t,SLACK_ENV_EXAMPLE_VALUES)).written.length>0?s.push(t):c.push(t)}try{await writeTextFile(t,a,{force:e.force})}catch(e){throw l!==void 0&&(l.content===void 0?await unlink(l.path):await writeFile(l.path,l.content,`utf8`)),e}let u={kind:`slack`,action:n?`overwritten`:`created`,filesWritten:s,filesSkipped:c,packageJsonUpdated:o,slackConnectorSlug:i};return n&&(u.filesOverwritten=[t]),u}async function listAuthoredChannels(e){let t=join(e,`channels`),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug,resolveVercelHostFrameworkPreset};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,attachSlackConnector,cleanupCreatedAttempt,fetchSlackWorkspace,findSlackConnector,listSlackConnectors}from"./slack-connect-lifecycle.js";import{createSlackConnector}from"./slack-connect-create.js";import{join}from"node:path";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#setup/scaffold/index.js";import{
|
|
1
|
+
import{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,attachSlackConnector,cleanupCreatedAttempt,fetchSlackWorkspace,findSlackConnector,listSlackConnectors}from"./slack-connect-lifecycle.js";import{createSlackConnector}from"./slack-connect-create.js";import{join}from"node:path";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#setup/scaffold/index.js";import{readProjectLink}from"#setup/project-resolution.js";import{performance}from"node:perf_hooks";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{setTimeout}from"node:timers/promises";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{updateSlackChannelConnectorUid}from"#setup/scaffold/update/update-slack-channel.js";const defaultDeps={captureVercel,runVercel,runVercelCaptureStdout},realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollSlackWorkspace(e,t,n,r,i,a){let o=e.delay??realDelay,s=e.now??monotonicNow,c=s()+3e5;for(;;){a?.throwIfAborted();let l=c-s();if(l<=0)return{state:`timed-out`};let u=await fetchSlackWorkspace({deps:e,projectRoot:t,connectorId:n,orgId:r,onOutput:i,timeoutMs:Math.min(CONNECT_LOOKUP_TIMEOUT_MS,l),signal:a});if(a?.throwIfAborted(),u.state!==`pending`)return u;let d=c-s();if(d<=0)return{state:`timed-out`};await o(Math.min(3e3,d),a)}}function isAbortFromSignal(e,t){return t?.aborted===!0&&(e===t.reason||e instanceof Error&&e.name===`AbortError`)}function cleanupFailureResult(e){return{state:`cleanup-failed`,connectorUids:e.connectorUids}}async function runAttempt(e){let{log:t,deps:n,projectRoot:r,orgId:i,onOutput:a,signal:o,phase:s}=e,c,l;if(e.source.state===`existing`)c=e.source.ref;else{let u=await createSlackConnector({deps:n,projectRoot:r,orgId:i,slug:e.slug,onOutput:a,signal:o,phase:s,onCreated:e.onCreated,waitForWorkspace:async(e,t)=>{let o=await pollSlackWorkspace(n,r,e.id,i,a,t);return o.state===`connected`?o.workspace:void 0}});if(u.state===`failed`)return{state:`create-failed`};if(u.state===`unresolved`)return t.warning(`Vercel did not return an exact Slack connector UID for this request, so eve cannot attach or remove it safely.`),{state:`unresolved`};c=u.ref,u.via===`workspace`&&(l=u.workspace)}if(e.source.state===`existing`&&l===void 0){let e=await s(`Waiting for the Slack workspace connection...`,()=>pollSlackWorkspace(n,r,c.id,i,a,o));if(e.state===`timed-out`)return{state:`timed-out`,ref:c};if(e.state===`failed`)return{state:`failed`,ref:c,message:e.message};l=e.workspace}let u=await s(`Configuring Slack event delivery for this agent...`,()=>attachSlackConnector(n,r,c,a,o));return o?.throwIfAborted(),u.state===`attached`?l===void 0?{state:`attached`,ref:c}:{state:`attached`,ref:c,workspace:l}:{state:u.state,ref:c}}async function raceAttemptAgainstChoice(e){let t=new AbortController,n=e.outerSignal?AbortSignal.any([e.outerSignal,t.signal]):t.signal,r=e.run(n);try{let i=await Promise.race([r.then(e=>({via:`work`,outcome:e})),e.prompt.choice.then(e=>({via:`choice`,choice:e}))]);if(i.via===`work`)return i;t.abort();let a;try{a=await r}catch(e){if(!isAbortFromSignal(e,n))throw e}return{via:`choice`,choice:i.choice,settled:a}}catch(e){t.abort();try{await r}catch{}throw e}finally{e.prompt.close()}}async function inspectSlackbotConnectors(e,t,n,r,i=defaultDeps){let a=await(i.readProjectLink??readProjectLink)(t),o=await findSlackConnector(i,t,a?.projectId,`slack/${n}`,createPromptCommandOutput(e),r);if(o.state===`failed`)throw Error(o.message);return o}async function provisionSlackbot(e,t,n,r=defaultDeps,i={}){i.signal?.throwIfAborted();let a=createPromptCommandOutput(e),o={log:e,deps:r,projectRoot:t,onOutput:a},s=await(r.readProjectLink??readProjectLink)(t),c=s?.projectId,l=s?.orgId,u=`slack/${n}`,d=await withPhase(e,`Checking for an existing Slackbot...`,()=>findSlackConnector(r,t,c,u,a,i.signal));if(i.signal?.throwIfAborted(),d.state===`failed`)return e.warning(`Could not inspect existing Slack connectors, so eve did not create another one. ${d.message}`),{state:`connector-lookup-failed`};if(c===void 0&&d.connectorUids.size>0)return e.warning("Could not verify which Slack connectors belong to this Vercel project, so eve did not create another one. Restore `.vercel/project.json`, then try again."),{state:`connector-lookup-failed`};let finishOutcome=async(t,n,r)=>{let i=n.state===`new`;switch(t.state){case`create-failed`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`unresolved`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`detach-failed`:return e.warning(`Could not remove the connector's existing trigger destination. Run \`vercel connect detach ${t.ref.uid} --yes\`, then \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\`.`),{state:`detach-failed`,connectorUid:t.ref.uid};case`attach-failed`:return e.warning(`Could not register this project as a trigger destination. Run \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\` to enable event delivery.`),{state:`attach-failed`,connectorUid:t.ref.uid};case`attached`:return t.workspace===void 0?{state:`attached`,connectorUid:t.ref.uid}:{state:`attached`,connectorUid:t.ref.uid,chatUrl:t.workspace.workspaceUrl,workspaceName:t.workspace.workspaceName};case`failed`:return e.warning(`Could not verify the Slack workspace connection. ${t.message}`),{state:`installation-check-failed`,connectorUid:t.ref.uid};case`timed-out`:if(e.warning(`The Slackbot did not connect to a Slack workspace in time.`),i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`not-installed`}}},cleanupNewAttempt=async(e,t)=>cleanupCreatedAttempt(o,{expectedUid:u,baselineConnectorUids:e.baselineConnectorUids,createdRef:t});function attemptInput(i,o){return{log:e,deps:r,projectRoot:t,orgId:l,slug:n,source:i,onOutput:a,onCreated:o}}async function runExistingConnector(t){let cleanupCurrentAttempt=async()=>({state:`clean`}),notInstalled=()=>(e.warning(`The existing Slack connector \`${t.ref.uid}\` is not connected to a Slack workspace. eve did not remove it because this run did not create it. If its original browser request is still open, complete it; otherwise run \`vercel connect remove ${t.ref.uid} --disconnect-all --yes\` before trying again.`),{state:`existing-not-installed`,connectorUid:t.ref.uid}),finishExistingOutcome=async e=>e.state===`timed-out`?notInstalled():finishOutcome(e,t,cleanupCurrentAttempt);if(i.awaitChoice!==void 0){let e=await raceAttemptAgainstChoice({prompt:i.awaitChoice({status:`Waiting for the existing Slack workspace connection...`,context:`Complete the original setup in the browser`,actions:[{value:`cancel`,label:`Stop waiting`}]}),outerSignal:i.signal,run:e=>runAttempt({...attemptInput(t,()=>{}),signal:e,phase:(e,t)=>t()})});return e.via===`work`?finishExistingOutcome(e.outcome):e.settled?.state===`attached`?finishExistingOutcome(e.settled):notInstalled()}return finishExistingOutcome(await runAttempt({...attemptInput(t,()=>{}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)}))}async function runUncontrolledAttempt(t){let n,cleanupCurrentAttempt=()=>cleanupNewAttempt(t,n),r=runAttempt({...attemptInput(t,e=>{n=e}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)});try{return await finishOutcome(await r,t,cleanupCurrentAttempt)}catch(e){throw i.signal?.aborted===!0&&await cleanupCurrentAttempt(),e}}async function runInteractiveAttempt(n,o){let s,cleanupCurrentAttempt=()=>cleanupNewAttempt(n,s),c=o({status:`Waiting for Slack setup to finish...`,context:`Complete setup in the browser, then wait while eve verifies the connection`,actions:[{value:`retry`,label:`Did your browser not open? Try again`},{value:`cancel`,label:`Cancel`}]}),l;try{l=await raceAttemptAgainstChoice({prompt:c,outerSignal:i.signal,run:t=>runAttempt({...attemptInput(n,e=>{s=e}),signal:t,phase:e.spinner===void 0?(e,t)=>t():(t,n)=>withPhase(e,t,n)})})}catch(e){throw await cleanupCurrentAttempt(),e}if(l.via===`work`)return{state:`finished`,result:await finishOutcome(l.outcome,n,cleanupCurrentAttempt)};if(l.settled?.state===`attached`)return{state:`finished`,result:await finishOutcome(l.settled,n,cleanupCurrentAttempt)};let u=await cleanupCurrentAttempt();if(u.state===`failed`)return{state:`finished`,result:cleanupFailureResult(u)};if(l.choice===`retry`){let n=await withPhase(e,`Checking existing Slack connectors...`,()=>listSlackConnectors(r,t,a,i.signal));return i.signal?.throwIfAborted(),n.state===`failed`?(e.warning(`Could not inspect existing Slack connectors, so eve did not create another one. ${n.message}`),{state:`finished`,result:{state:`connector-lookup-failed`}}):{state:`retry`,source:{state:`new`,baselineConnectorUids:new Set(n.connectors.map(e=>e.uid))}}}return{state:`finished`,result:{state:`cancelled`}}}if(d.state===`found`){let e=i.selectConnector===void 0?d.preferred??d.connectors[0]:await i.selectConnector(d.connectors,d.preferred);if(i.signal?.throwIfAborted(),e!==`create`)return runExistingConnector({state:`existing`,ref:e})}let f={state:`new`,baselineConnectorUids:d.connectorUids};if(i.awaitChoice===void 0)return runUncontrolledAttempt(f);for(;;){let e=await runInteractiveAttempt(f,i.awaitChoice);if(e.state===`finished`)return e.result;f=e.source}}async function reconcileSlackUid(e,t,n,r){if(n.state!==`attached`||n.connectorUid===r)return!0;let i=join(t,`agent/channels/slack.ts`),{patched:a}=await updateSlackChannelConnectorUid(i,n.connectorUid);return a?!0:(e.warning(`Could not patch agent/channels/slack.ts automatically. Update \`connectSlackCredentials("...")\` to \`"${n.connectorUid}"\` and run \`vercel deploy --prod\`.`),!1)}export{inspectSlackbotConnectors,parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector,provisionSlackbot,reconcileSlackUid};
|
|
@@ -20,6 +20,8 @@ export interface PickTeamOptions extends VercelProjectOperationOptions {
|
|
|
20
20
|
selectMessage?: (currentTeam: string) => string;
|
|
21
21
|
}
|
|
22
22
|
export interface LinkProjectOperationOptions extends CreatedProjectFrameworkOptions {
|
|
23
|
+
/** Configure 100% trace sampling when creating a Vercel project. */
|
|
24
|
+
traceSampling?: boolean;
|
|
23
25
|
}
|
|
24
26
|
/** Effects used to ensure an interactive Vercel project link. */
|
|
25
27
|
export interface EnsureLinkedVercelProjectDeps {
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readVercelCliToken}from"#internal/model-auth/vercel-cli.js";const AGENT_PROJECT_TRACING_SAMPLING=[{type:`head_sampling`,rate:1}];async function configureTraceSampling(e,t,n){try{let t=await readVercelCliToken();if(t===void 0)throw Error(`Vercel CLI credentials are unavailable.`);let r=new URLSearchParams({projectId:e.projectId,teamId:e.orgId});if(!(await fetch(`https://api.vercel.com/v1/drains/tracing/config?${r.toString()}`,{body:JSON.stringify({enabled:!0,sampling:AGENT_PROJECT_TRACING_SAMPLING}),headers:{authorization:`Bearer ${t}`,"content-type":`application/json`},method:`PUT`,redirect:`error`,signal:n?AbortSignal.any([n,AbortSignal.timeout(15e3)]):AbortSignal.timeout(15e3)})).ok)throw Error(`Vercel rejected the trace sampling configuration.`)}catch{n?.throwIfAborted(),t.log.warning(`The Vercel project was created, but trace sampling could not be configured. Set it to 100% for all environments in the Vercel project settings.`)}}export{configureTraceSampling};
|
|
@@ -23,7 +23,7 @@ export declare function notifyDelegatedParentStep(input: {
|
|
|
23
23
|
export interface SettledTurnNotification {
|
|
24
24
|
readonly output: unknown;
|
|
25
25
|
readonly isError?: boolean;
|
|
26
|
-
/** Usage
|
|
26
|
+
/** Usage accumulated since the previous caller settlement, including yielded turns. */
|
|
27
27
|
readonly usage?: TokenUsage;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
@@ -81,6 +81,7 @@ export declare function cancelRemoteAgentTurn(input: {
|
|
|
81
81
|
readonly remote: Pick<ResolvedRuntimeRemoteAgentNode, "auth" | "headers" | "name" | "url">;
|
|
82
82
|
readonly sessionId: string;
|
|
83
83
|
readonly taskId?: string;
|
|
84
|
+
readonly tasks?: boolean;
|
|
84
85
|
readonly turnId?: string;
|
|
85
86
|
}): Promise<CancelTurnResult>;
|
|
86
87
|
/** Retires one exact remote child session through eve's authenticated reset route. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z}from"#compiled/zod/index.js";import{createEveCallbackRoutePath,createEveSessionCancelRoutePath,createEveSessionResetRoutePath,createEveSessionRoutePath}from"#protocol/routes.js";import{decisionToTraceContentCeiling}from"#shared/forwarded-trace-policy.js";import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{CancelTurnResponseSchema}from"#protocol/cancel-turn.js";import{ResetResponseSchema}from"#protocol/reset-session.js";import{formatTraceparent,writeAgentDispatchTracestate}from"#protocol/traceparent.js";import{writeForwardedAudienceBaggage,writeForwardedParentSessionBaggage}from"#protocol/baggage.js";import{writeConversationBaggage}from"#tracing/conversation-context.js";import{createWorkflowCallbackUrl}from"#execution/workflow-callback-url.js";import{formatSubagentInput,normalizeRequestedOutputSchema}from"#subagents/invocation.js";import{readTaskIdFromInboxToken}from"#tasks/task-inbox-token.js";import{AgentHandleError}from"#protocol/agent-handle-error.js";import{createRemoteAgentRouteUrl}from"#subagents/remote-route-url.js";const CreateSessionResponseSchema=z.object({ok:z.literal(!0),sessionId:z.string().min(1),status:z.literal(`accepted`)});var RemoteAgentCancelRequestError=class extends Error{retryable;constructor(e,t){super(e),this.name=`RemoteAgentCancelRequestError`,this.retryable=t.retryable}};async function startRemoteAgentSession(e){let t=e.parent?.continuationToken??e.session.continuationToken;if(!t)throw Error(`Cannot dispatch remote agent without a parent continuation token.`);if(!e.callbackBaseUrl)throw Error(`Cannot dispatch remote agent without a callback base URL.`);let n=buildForwardedPrincipalField(e),r={capabilities:{},callback:{callId:e.action.callId,subagentName:e.action.remoteAgentName,taskId:e.taskId??readTaskIdFromInboxToken(t),token:t,url:createWorkflowCallbackUrl(e.callbackBaseUrl,createEveCallbackRoutePath(t))},message:formatRemoteAgentCallInputMessage({action:e.action,remote:e.remote}),mode:`conversation`,outputSchema:normalizeRequestedOutputSchema(e.action.input.outputSchema)??e.remote.outputSchema};e.activityObserver!==void 0&&(r.activityObserver=e.activityObserver),n!==void 0&&(r.forwardedPrincipal=n),e.operationId!==void 0&&(r.operationId=e.operationId);let i=await resolveRemoteAgentRequestHeaders(e.remote),a=formatTraceparent(e.parent?.traceContext);a!==void 0&&setHeader(i,`traceparent`,a),setHeader(i,`tracestate`,writeAgentDispatchTracestate(readHeader(i,`tracestate`),e.parent?.traceContext));let o=writeForwardedAudienceBaggage(readHeader(i,`baggage`),buildForwardedTraceAssertion({forwardedPrincipal:n,originAudience:e.originAudience,parentTraceContext:e.parent?.traceContext,traceparent:a})),s=writeConversationBaggage(o,e.parent?.conversationId);setHeader(i,`baggage`,writeForwardedParentSessionBaggage(s,e.parent?.lineage));let c=await fetch(createRemoteAgentSessionUrl(e.remote),{body:JSON.stringify(r),headers:{"content-type":`application/json`,...i},method:`POST`});if(!c.ok)throw Error(`Remote agent "${e.action.remoteAgentName}" create-session request failed with HTTP ${c.status}.`);let l;try{l=await c.json()}catch{throw Error(`Remote agent "${e.action.remoteAgentName}" create-session response was not valid JSON.`)}let u=CreateSessionResponseSchema.safeParse(l);if(!u.success)throw Error(`Remote agent "${e.action.remoteAgentName}" create-session response was invalid.`);return{sessionId:u.data.sessionId}}function buildForwardedTraceAssertion(e){let t=decisionToTraceContentCeiling(e.parentTraceContext?.decision);if(e.forwardedPrincipal!==void 0&&e.parentTraceContext!==void 0&&(e.parentTraceContext.traceFlags&1)==1&&e.traceparent!==void 0&&t!==void 0)return{ceiling:t,originAudience:e.originAudience??`unknown`}}async function continueRemoteAgentSession(e){let t=buildForwardedPrincipalField(e),n={activityObserver:e.activityObserver,callback:e.callback,message:e.message,outputSchema:e.outputSchema};t!==void 0&&(n.forwardedPrincipal=t);let r=await fetch(createRemoteAgentContinueUrl(e.remote,e.sessionId),{body:JSON.stringify(n),headers:{"content-type":`application/json`,...await resolveRemoteAgentRequestHeaders(e.remote)},method:`POST`});if(!r.ok){let n=await readRemoteAgentErrorCode(r),i=r.status===404||n===AgentHandleError.SessionNotResumable.code,a=r.status===400&&t!==void 0?` The receiver may support forwarded principals only on session creation; upgrade it before retrying.`:``;throw new RemoteAgentContinueRequestError(`Remote agent "${e.remote.name}" continue-session request failed${i?` permanently`:``} with HTTP ${r.status}.${a}`,{deliveryAmbiguous:isAmbiguousRemoteContinueStatus(r.status),retryable:!i})}}var RemoteAgentContinueRequestError=class extends Error{deliveryAmbiguous;retryable;constructor(e,t){super(e),this.name=`RemoteAgentContinueRequestError`,this.deliveryAmbiguous=t.deliveryAmbiguous,this.retryable=t.retryable}};function isRetryableRemoteAgentContinueError(e){return!(e instanceof RemoteAgentContinueRequestError)||e.retryable}function isAmbiguousRemoteAgentContinueError(e){return!(e instanceof RemoteAgentContinueRequestError)||e.deliveryAmbiguous}function isAmbiguousRemoteContinueStatus(e){return e===408||e===425||e>=500}async function readRemoteAgentErrorCode(e){let t;try{t=await e.json()}catch{return}if(typeof t!=`object`||!t)return;let n=Reflect.get(t,`code`);return typeof n==`string`?n:void 0}function buildForwardedPrincipalField(e){if(e.remote.forwardPrincipal!==!0||e.auth===null||e.auth===void 0)return;let t={current:e.auth};return e.initiatorAuth!==null&&e.initiatorAuth!==void 0&&(t.initiator=e.initiatorAuth),t}async function cancelRemoteAgentTurn(e){let t=e.headers??await resolveRemoteAgentRequestHeaders(e.remote),n=await fetch(createRemoteAgentCancelTurnUrl(e.remote,e.sessionId),{body:e.turnId===void 0&&e.taskId===void 0?void 0:JSON.stringify({taskId:e.taskId,turnId:e.turnId}),headers:t,method:`POST`});if(!n.ok)throw new RemoteAgentCancelRequestError(`Remote agent "${e.remote.name}" cancel-turn request failed with HTTP ${n.status}.`,{retryable:isRetryableRemoteCancelStatus(n.status)});let r;try{r=await n.json()}catch{throw new RemoteAgentCancelRequestError(`Remote agent "${e.remote.name}" cancel-turn response was not valid JSON.`,{retryable:!1})}let i=CancelTurnResponseSchema.safeParse(r);if(!i.success||i.data.status===`accepted`&&i.data.sessionId!==e.sessionId)throw new RemoteAgentCancelRequestError(`Remote agent "${e.remote.name}" cancel-turn response was invalid.`,{retryable:!1});return i.data.status===`accepted`?{sessionId:i.data.sessionId,status:`accepted`}:{status:`no_active_turn`}}function readHeader(e,t){let n=Object.keys(e).find(e=>e.toLowerCase()===t);return n===void 0?void 0:e[n]}function setHeader(e,t,n){for(let n of Object.keys(e))n.toLowerCase()===t&&delete e[n];n!==void 0&&(e[t]=n)}async function resetRemoteAgentSession(e){let t=e.headers??await resolveRemoteAgentRequestHeaders(e.remote),n=await fetch(createRemoteAgentRouteUrl(e.remote.url,createEveSessionResetRoutePath(e.sessionId)),{body:JSON.stringify({reason:`Parent session ended`}),headers:{"content-type":`application/json`,...t},method:`POST`});if(!n.ok)throw Error(`Remote agent "${e.remote.name}" reset-session request failed with HTTP ${n.status}.`);let r=ResetResponseSchema.safeParse(await n.json());if(!r.success||r.data.status===`reset`&&r.data.previousSessionId!==e.sessionId)throw Error(`Remote agent "${e.remote.name}" reset-session response was invalid.`);return r.data}function isRetryableRemoteAgentCancelError(e){return!(e instanceof RemoteAgentCancelRequestError)||e.retryable}function resolveRemoteAgentForAction(e){let t=e.registry.get(e.nodeId)?.definition;if(e.dynamicRemoteAgent!==void 0){if(t===void 0)throw Error(`Missing remote agent "${e.remoteAgentName}" in runtime registry.`);let n=resolveDynamicRemoteAgentCredentials(e.dynamicRemoteAgent),r=e.dynamicRemoteAgent,i={description:r.description,kind:`remote`,logicalPath:t.logicalPath,name:e.remoteAgentName,nodeId:e.nodeId,outputSchema:r.outputSchema,path:r.path,sourceId:t.sourceId,sourceKind:`module`,url:r.url};return r.forwardPrincipal!==void 0&&(i.forwardPrincipal=r.forwardPrincipal),n.auth!==void 0&&(i.auth=n.auth),n.headers!==void 0&&(i.headers=n.headers),i}if(t?.kind!==`remote`)throw Error(`Missing remote agent "${e.remoteAgentName}" in runtime registry.`);return t}async function resolveRemoteAgentStreamHeaders(e){if(e.resolverId===void 0)return{};let t=new Set([e.bundle.graph.root,...e.bundle.graph.nodesByNodeId.values()]);for(let n of t){let t=n.subagentRegistry.subagentsByNodeId.get(e.resolverId)?.definition;if(t!==void 0){if(t.kind!==`remote`||t.name!==e.name||t.url!==e.url)throw Error(`Remote child stream resolver does not match the authored remote agent.`);return await resolveRemoteAgentRequestHeaders(t)}}return await resolveRemoteAgentRequestHeaders(resolveDynamicRemoteAgentCredentials({credentialsStepId:e.resolverId,description:``,path:``,url:e.url}))}function resolveDynamicRemoteAgentCredentials(e){if(e.credentialsStepId===void 0)return{};let t=getStepRegistry().get(e.credentialsStepId);if(t===void 0)throw Error(`Dynamic remote subagent credentials function "${e.credentialsStepId}" is not registered.`);let n=expectObjectRecord(t(),`Dynamic remote subagent credentials are invalid.`),r={};return n.auth!==void 0&&(r.auth=expectFunction(n.auth,`Dynamic remote subagent auth is invalid.`)),n.headers!==void 0&&(r.headers=resolveDynamicRemoteAgentHeaders(n.headers)),r}function resolveDynamicRemoteAgentHeaders(e){if(typeof e==`function`)return e;let t=expectObjectRecord(e,`Dynamic remote subagent headers are invalid.`);for(let e of Object.values(t))if(typeof e!=`string`)throw Error(`Dynamic remote subagent headers are invalid.`);return t}function getStepRegistry(){let e=Symbol.for(`@workflow/core//registeredSteps`),t=globalThis,n=t[e];return n===void 0&&(n=new Map,t[e]=n),n}function createRemoteAgentSessionUrl(e){return createRemoteAgentRouteUrl(e.url,e.path)}function createRemoteAgentCancelTurnUrl(e,t){return createRemoteAgentRouteUrl(e.url,createEveSessionCancelRoutePath(t))}function createRemoteAgentContinueUrl(e,t){return createRemoteAgentRouteUrl(e.url,createEveSessionRoutePath(t))}function isRetryableRemoteCancelStatus(e){return e===408||e===425||e===429||e>=500}async function resolveRemoteAgentRequestHeaders(e){let t={};return e.headers!==void 0&&Object.assign(t,typeof e.headers==`function`?await e.headers():e.headers),e.auth!==void 0&&Object.assign(t,(await e.auth()).headers),t}function formatRemoteAgentCallInputMessage(e){let t=typeof e.action.input.message==`string`?e.action.input.message:``;return formatSubagentInput({description:e.remote.description,message:t,name:e.action.remoteAgentName,type:`remote`}).message}export{RemoteAgentContinueRequestError,cancelRemoteAgentTurn,continueRemoteAgentSession,isAmbiguousRemoteAgentContinueError,isRetryableRemoteAgentCancelError,isRetryableRemoteAgentContinueError,resetRemoteAgentSession,resolveRemoteAgentForAction,resolveRemoteAgentStreamHeaders,startRemoteAgentSession};
|
|
1
|
+
import{z}from"#compiled/zod/index.js";import{createEveCallbackRoutePath,createEveSessionCancelRoutePath,createEveSessionResetRoutePath,createEveSessionRoutePath}from"#protocol/routes.js";import{decisionToTraceContentCeiling}from"#shared/forwarded-trace-policy.js";import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{CancelTurnResponseSchema}from"#protocol/cancel-turn.js";import{ResetResponseSchema}from"#protocol/reset-session.js";import{formatTraceparent,writeAgentDispatchTracestate}from"#protocol/traceparent.js";import{writeForwardedAudienceBaggage,writeForwardedParentSessionBaggage}from"#protocol/baggage.js";import{writeConversationBaggage}from"#tracing/conversation-context.js";import{createWorkflowCallbackUrl}from"#execution/workflow-callback-url.js";import{formatSubagentInput,normalizeRequestedOutputSchema}from"#subagents/invocation.js";import{readTaskIdFromInboxToken}from"#tasks/task-inbox-token.js";import{AgentHandleError}from"#protocol/agent-handle-error.js";import{createRemoteAgentRouteUrl}from"#subagents/remote-route-url.js";const CreateSessionResponseSchema=z.object({ok:z.literal(!0),sessionId:z.string().min(1),status:z.literal(`accepted`)});var RemoteAgentCancelRequestError=class extends Error{retryable;constructor(e,t){super(e),this.name=`RemoteAgentCancelRequestError`,this.retryable=t.retryable}};async function startRemoteAgentSession(e){let t=e.parent?.continuationToken??e.session.continuationToken;if(!t)throw Error(`Cannot dispatch remote agent without a parent continuation token.`);if(!e.callbackBaseUrl)throw Error(`Cannot dispatch remote agent without a callback base URL.`);let n=buildForwardedPrincipalField(e),r={capabilities:{},callback:{callId:e.action.callId,subagentName:e.action.remoteAgentName,taskId:e.taskId??readTaskIdFromInboxToken(t),token:t,url:createWorkflowCallbackUrl(e.callbackBaseUrl,createEveCallbackRoutePath(t))},message:formatRemoteAgentCallInputMessage({action:e.action,remote:e.remote}),mode:`conversation`,outputSchema:normalizeRequestedOutputSchema(e.action.input.outputSchema)??e.remote.outputSchema};e.activityObserver!==void 0&&(r.activityObserver=e.activityObserver),n!==void 0&&(r.forwardedPrincipal=n),e.operationId!==void 0&&(r.operationId=e.operationId);let i=await resolveRemoteAgentRequestHeaders(e.remote),a=formatTraceparent(e.parent?.traceContext);a!==void 0&&setHeader(i,`traceparent`,a),setHeader(i,`tracestate`,writeAgentDispatchTracestate(readHeader(i,`tracestate`),e.parent?.traceContext));let o=writeForwardedAudienceBaggage(readHeader(i,`baggage`),buildForwardedTraceAssertion({forwardedPrincipal:n,originAudience:e.originAudience,parentTraceContext:e.parent?.traceContext,traceparent:a})),s=writeConversationBaggage(o,e.parent?.conversationId);setHeader(i,`baggage`,writeForwardedParentSessionBaggage(s,e.parent?.lineage));let c=await fetch(createRemoteAgentSessionUrl(e.remote),{body:JSON.stringify(r),headers:{"content-type":`application/json`,...i},method:`POST`});if(!c.ok)throw Error(`Remote agent "${e.action.remoteAgentName}" create-session request failed with HTTP ${c.status}.`);let l;try{l=await c.json()}catch{throw Error(`Remote agent "${e.action.remoteAgentName}" create-session response was not valid JSON.`)}let u=CreateSessionResponseSchema.safeParse(l);if(!u.success)throw Error(`Remote agent "${e.action.remoteAgentName}" create-session response was invalid.`);return{sessionId:u.data.sessionId}}function buildForwardedTraceAssertion(e){let t=decisionToTraceContentCeiling(e.parentTraceContext?.decision);if(e.forwardedPrincipal!==void 0&&e.parentTraceContext!==void 0&&(e.parentTraceContext.traceFlags&1)==1&&e.traceparent!==void 0&&t!==void 0)return{ceiling:t,originAudience:e.originAudience??`unknown`}}async function continueRemoteAgentSession(e){let t=buildForwardedPrincipalField(e),n={activityObserver:e.activityObserver,callback:e.callback,message:e.message,outputSchema:e.outputSchema};t!==void 0&&(n.forwardedPrincipal=t);let r=await fetch(createRemoteAgentContinueUrl(e.remote,e.sessionId),{body:JSON.stringify(n),headers:{"content-type":`application/json`,...await resolveRemoteAgentRequestHeaders(e.remote)},method:`POST`});if(!r.ok){let n=await readRemoteAgentErrorCode(r),i=r.status===404||n===AgentHandleError.SessionNotResumable.code,a=r.status===400&&t!==void 0?` The receiver may support forwarded principals only on session creation; upgrade it before retrying.`:``;throw new RemoteAgentContinueRequestError(`Remote agent "${e.remote.name}" continue-session request failed${i?` permanently`:``} with HTTP ${r.status}.${a}`,{deliveryAmbiguous:isAmbiguousRemoteContinueStatus(r.status),retryable:!i})}}var RemoteAgentContinueRequestError=class extends Error{deliveryAmbiguous;retryable;constructor(e,t){super(e),this.name=`RemoteAgentContinueRequestError`,this.deliveryAmbiguous=t.deliveryAmbiguous,this.retryable=t.retryable}};function isRetryableRemoteAgentContinueError(e){return!(e instanceof RemoteAgentContinueRequestError)||e.retryable}function isAmbiguousRemoteAgentContinueError(e){return!(e instanceof RemoteAgentContinueRequestError)||e.deliveryAmbiguous}function isAmbiguousRemoteContinueStatus(e){return e===408||e===425||e>=500}async function readRemoteAgentErrorCode(e){let t;try{t=await e.json()}catch{return}if(typeof t!=`object`||!t)return;let n=Reflect.get(t,`code`);return typeof n==`string`?n:void 0}function buildForwardedPrincipalField(e){if(e.remote.forwardPrincipal!==!0||e.auth===null||e.auth===void 0)return;let t={current:e.auth};return e.initiatorAuth!==null&&e.initiatorAuth!==void 0&&(t.initiator=e.initiatorAuth),t}async function cancelRemoteAgentTurn(e){let t=e.headers??await resolveRemoteAgentRequestHeaders(e.remote),n=await fetch(createRemoteAgentCancelTurnUrl(e.remote,e.sessionId),{body:e.turnId===void 0&&e.taskId===void 0&&e.tasks===void 0?void 0:JSON.stringify({taskId:e.taskId,tasks:e.tasks,turnId:e.turnId}),headers:t,method:`POST`});if(!n.ok)throw new RemoteAgentCancelRequestError(`Remote agent "${e.remote.name}" cancel-turn request failed with HTTP ${n.status}.`,{retryable:isRetryableRemoteCancelStatus(n.status)});let r;try{r=await n.json()}catch{throw new RemoteAgentCancelRequestError(`Remote agent "${e.remote.name}" cancel-turn response was not valid JSON.`,{retryable:!1})}let i=CancelTurnResponseSchema.safeParse(r);if(!i.success||i.data.status===`accepted`&&i.data.sessionId!==e.sessionId)throw new RemoteAgentCancelRequestError(`Remote agent "${e.remote.name}" cancel-turn response was invalid.`,{retryable:!1});return i.data.status===`accepted`?{sessionId:i.data.sessionId,status:`accepted`}:{status:`no_active_turn`}}function readHeader(e,t){let n=Object.keys(e).find(e=>e.toLowerCase()===t);return n===void 0?void 0:e[n]}function setHeader(e,t,n){for(let n of Object.keys(e))n.toLowerCase()===t&&delete e[n];n!==void 0&&(e[t]=n)}async function resetRemoteAgentSession(e){let t=e.headers??await resolveRemoteAgentRequestHeaders(e.remote),n=await fetch(createRemoteAgentRouteUrl(e.remote.url,createEveSessionResetRoutePath(e.sessionId)),{body:JSON.stringify({reason:`Parent session ended`}),headers:{"content-type":`application/json`,...t},method:`POST`});if(!n.ok)throw Error(`Remote agent "${e.remote.name}" reset-session request failed with HTTP ${n.status}.`);let r=ResetResponseSchema.safeParse(await n.json());if(!r.success||r.data.status===`reset`&&r.data.previousSessionId!==e.sessionId)throw Error(`Remote agent "${e.remote.name}" reset-session response was invalid.`);return r.data}function isRetryableRemoteAgentCancelError(e){return!(e instanceof RemoteAgentCancelRequestError)||e.retryable}function resolveRemoteAgentForAction(e){let t=e.registry.get(e.nodeId)?.definition;if(e.dynamicRemoteAgent!==void 0){if(t===void 0)throw Error(`Missing remote agent "${e.remoteAgentName}" in runtime registry.`);let n=resolveDynamicRemoteAgentCredentials(e.dynamicRemoteAgent),r=e.dynamicRemoteAgent,i={description:r.description,kind:`remote`,logicalPath:t.logicalPath,name:e.remoteAgentName,nodeId:e.nodeId,outputSchema:r.outputSchema,path:r.path,sourceId:t.sourceId,sourceKind:`module`,url:r.url};return r.forwardPrincipal!==void 0&&(i.forwardPrincipal=r.forwardPrincipal),n.auth!==void 0&&(i.auth=n.auth),n.headers!==void 0&&(i.headers=n.headers),i}if(t?.kind!==`remote`)throw Error(`Missing remote agent "${e.remoteAgentName}" in runtime registry.`);return t}async function resolveRemoteAgentStreamHeaders(e){if(e.resolverId===void 0)return{};let t=new Set([e.bundle.graph.root,...e.bundle.graph.nodesByNodeId.values()]);for(let n of t){let t=n.subagentRegistry.subagentsByNodeId.get(e.resolverId)?.definition;if(t!==void 0){if(t.kind!==`remote`||t.name!==e.name||t.url!==e.url)throw Error(`Remote child stream resolver does not match the authored remote agent.`);return await resolveRemoteAgentRequestHeaders(t)}}return await resolveRemoteAgentRequestHeaders(resolveDynamicRemoteAgentCredentials({credentialsStepId:e.resolverId,description:``,path:``,url:e.url}))}function resolveDynamicRemoteAgentCredentials(e){if(e.credentialsStepId===void 0)return{};let t=getStepRegistry().get(e.credentialsStepId);if(t===void 0)throw Error(`Dynamic remote subagent credentials function "${e.credentialsStepId}" is not registered.`);let n=expectObjectRecord(t(),`Dynamic remote subagent credentials are invalid.`),r={};return n.auth!==void 0&&(r.auth=expectFunction(n.auth,`Dynamic remote subagent auth is invalid.`)),n.headers!==void 0&&(r.headers=resolveDynamicRemoteAgentHeaders(n.headers)),r}function resolveDynamicRemoteAgentHeaders(e){if(typeof e==`function`)return e;let t=expectObjectRecord(e,`Dynamic remote subagent headers are invalid.`);for(let e of Object.values(t))if(typeof e!=`string`)throw Error(`Dynamic remote subagent headers are invalid.`);return t}function getStepRegistry(){let e=Symbol.for(`@workflow/core//registeredSteps`),t=globalThis,n=t[e];return n===void 0&&(n=new Map,t[e]=n),n}function createRemoteAgentSessionUrl(e){return createRemoteAgentRouteUrl(e.url,e.path)}function createRemoteAgentCancelTurnUrl(e,t){return createRemoteAgentRouteUrl(e.url,createEveSessionCancelRoutePath(t))}function createRemoteAgentContinueUrl(e,t){return createRemoteAgentRouteUrl(e.url,createEveSessionRoutePath(t))}function isRetryableRemoteCancelStatus(e){return e===408||e===425||e===429||e>=500}async function resolveRemoteAgentRequestHeaders(e){let t={};return e.headers!==void 0&&Object.assign(t,typeof e.headers==`function`?await e.headers():e.headers),e.auth!==void 0&&Object.assign(t,(await e.auth()).headers),t}function formatRemoteAgentCallInputMessage(e){let t=typeof e.action.input.message==`string`?e.action.input.message:``;return formatSubagentInput({description:e.remote.description,message:t,name:e.action.remoteAgentName,type:`remote`}).message}export{RemoteAgentContinueRequestError,cancelRemoteAgentTurn,continueRemoteAgentSession,isAmbiguousRemoteAgentContinueError,isRetryableRemoteAgentCancelError,isRetryableRemoteAgentContinueError,resetRemoteAgentSession,resolveRemoteAgentForAction,resolveRemoteAgentStreamHeaders,startRemoteAgentSession};
|
package/dist/src/svelte/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{markFrameworkStepInput}from"#harness/messages.js";import{
|
|
1
|
+
import{markFrameworkStepInput}from"#harness/messages.js";import{getBackgroundTasks}from"#harness/workflow-tool-runs.js";import{taskIdOfDelivery}from"#tasks/notification.js";import{EMPTY_DELIVERY_SENTINEL}from"#shared/empty-delivery.js";const TASK_DELIVERY_CONTEXT_LABEL=`[Task state]`,TASK_DELIVERY_INITIATING_INSTRUCTION=`Background task reporting: launch acknowledgement
|
|
2
2
|
The latest ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists background tasks accepted so far from the current turn. They continue independently after this turn.
|
|
3
3
|
|
|
4
|
-
Continue carrying out the user's request, including starting any remaining background work. When no further tool calls are needed in this turn, send one brief user-facing acknowledgement that the background work has started. Do not wait for results or report results that are not available yet. End the turn after the acknowledgement.`,TASK_DELIVERY_SETTLED_INSTRUCTION=`Background task reporting\nFor this background-task update, the accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists the settled tasks in this cohort and their available terminal outputs. Report their useful results together in one user-facing response without repeating results already reported. Do not reply with ${EMPTY_DELIVERY_SENTINEL}.`,TASK_DELIVERY_AUTO_INSTRUCTION=`Background task reporting\nFor this background-task update, the accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists the whole cohort, including pending tasks and every available terminal output. A received result has not necessarily been reported to the user. Report new results only when they are useful independently of unfinished work. When that work settles, combine its results with any previously withheld results. If there is nothing new and useful to report, reply with exactly ${EMPTY_DELIVERY_SENTINEL}. Do not repeat results already reported or send an acknowledgement just to say you are waiting.`;function getBackgroundTaskDelivery(e){if(typeof e!=`object`||!e)return;let t=e;return t.kind===`deliver`&&typeof t.taskDeliveryId==`string`?e:void 0}function markBackgroundTaskStepInput(e){return e.message===void 0?e:markFrameworkStepInput(e,`execution.background_task`)}function resolveTaskDeliveryContext(e){let t=e.taskDeliveryIds[0];if(t===void 0)return;let n=
|
|
4
|
+
Continue carrying out the user's request, including starting any remaining background work. When no further tool calls are needed in this turn, send one brief user-facing acknowledgement that the background work has started. Do not wait for results or report results that are not available yet. End the turn after the acknowledgement.`,TASK_DELIVERY_SETTLED_INSTRUCTION=`Background task reporting\nFor this background-task update, the accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists the settled tasks in this cohort and their available terminal outputs. Report their useful results together in one user-facing response without repeating results already reported. Do not reply with ${EMPTY_DELIVERY_SENTINEL}.`,TASK_DELIVERY_AUTO_INSTRUCTION=`Background task reporting\nFor this background-task update, the accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists the whole cohort, including pending tasks and every available terminal output. A received result has not necessarily been reported to the user. Report new results only when they are useful independently of unfinished work. When that work settles, combine its results with any previously withheld results. If there is nothing new and useful to report, reply with exactly ${EMPTY_DELIVERY_SENTINEL}. Do not repeat results already reported or send an acknowledgement just to say you are waiting.`;function getBackgroundTaskDelivery(e){if(typeof e!=`object`||!e)return;let t=e;return t.kind===`deliver`&&typeof t.taskDeliveryId==`string`?e:void 0}function markBackgroundTaskStepInput(e){return e.message===void 0?e:markFrameworkStepInput(e,`execution.background_task`)}function resolveTaskDeliveryContext(e){let t=e.taskDeliveryIds[0];if(t===void 0)return;let n=getBackgroundTasks(e.state),r=n.get(taskIdOfDelivery(t));if(r!==void 0)return{...projectTaskCohort(n.query({cohortId:r.cohortId}),e.taskDeliveryPolicy===`auto`),rootTurnId:r.turnId}}function resolveInitiatingTaskContext(e){let t=getBackgroundTasks(e.state);if(t.query({state:`working`,turnId:e.turnId}).length!==0)return{...projectTaskCohort(t.query({turnId:e.turnId}),!1),phase:`initiating`}}function projectTaskCohort(e,t){let n=e.every(e=>e.status!==`working`),r=e.map(e=>({name:e.metadata.name,output:n||t?e.lastOutput:void 0,status:e.status===`working`?`pending`:e.status,taskId:e.taskId}));return{context:`${TASK_DELIVERY_CONTEXT_LABEL}\n${JSON.stringify({tasks:r})}`,phase:n?`settled`:`pending`}}export{TASK_DELIVERY_AUTO_INSTRUCTION,TASK_DELIVERY_CONTEXT_LABEL,TASK_DELIVERY_INITIATING_INSTRUCTION,TASK_DELIVERY_SETTLED_INSTRUCTION,getBackgroundTaskDelivery,markBackgroundTaskStepInput,resolveInitiatingTaskContext,resolveTaskDeliveryContext};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import type { DeliverHookPayload } from "#channel/types.js";
|
|
2
|
+
import type { SessionStateMap } from "#harness/types.js";
|
|
1
3
|
import type { JsonValue } from "#shared/json.js";
|
|
2
4
|
import type { TaskView } from "#tasks/types.js";
|
|
3
5
|
export declare function formatTaskNotification(view: TaskView): string;
|
|
4
6
|
export declare function formatTaskOutput(output: JsonValue): string;
|
|
7
|
+
/** Task delivery ids are `<taskId>` or `<taskId>:<event>`; derived task ids never contain `:`. */
|
|
8
|
+
export declare function taskIdOfDelivery(deliveryId: string): string;
|
|
9
|
+
/** The parent already recorded this task's outcome, so the delivery cannot notify the model again. */
|
|
10
|
+
export declare function hasRecordedTaskOutcome(delivery: DeliverHookPayload, state: SessionStateMap | undefined): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function formatTaskNotification(e){let t=`Background task ${e.taskId} (${e.metadata.name})`;return e.status===`input_required`?`${t} needs input.`:e.status===`completed`?`${t} is completed.\n\nResult:\n${formatTaskOutput(e.lastOutput.data)}`:e.status===`failed`?`${t} failed.\n\nError:\n${formatTaskOutput(e.lastOutput.data)}`:`${t} is cancelled.`}function formatTaskOutput(e){return typeof e==`string`?e:JSON.stringify(e)??`null`}export{formatTaskNotification,formatTaskOutput};
|
|
1
|
+
import{getBackgroundTasks}from"#harness/workflow-tool-runs.js";function formatTaskNotification(e){let t=`Background task ${e.taskId} (${e.metadata.name})`;return e.status===`input_required`?`${t} needs input.`:e.status===`completed`?`${t} is completed.\n\nResult:\n${formatTaskOutput(e.lastOutput.data)}`:e.status===`failed`?`${t} failed.\n\nError:\n${formatTaskOutput(e.lastOutput.data)}`:`${t} is cancelled.`}function formatTaskOutput(e){return typeof e==`string`?e:JSON.stringify(e)??`null`}function taskIdOfDelivery(e){let t=e.indexOf(`:`);return t<0?e:e.slice(0,t)}function hasRecordedTaskOutcome(e,t){let n=e.taskDeliveryId;if(n===void 0)return!1;let r=getBackgroundTasks(t).get(taskIdOfDelivery(n));return r!==void 0&&r.status!==`working`}export{formatTaskNotification,formatTaskOutput,hasRecordedTaskOutcome,taskIdOfDelivery};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{decisionToTraceContentCeiling,resolveForwardedTraceSeed}from"#shared/forwarded-trace-policy.js";import{contextStorage,loadContext}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{ContextKey}from"#context/key.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{decisionToTraceContentCeiling,resolveForwardedTraceSeed}from"#shared/forwarded-trace-policy.js";import{contextStorage,loadContext}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{ContextKey}from"#context/key.js";import{getBackgroundTasks,getBlockingWorkflowToolRuns}from"#harness/workflow-tool-runs.js";import{deriveTaskId}from"#tasks/task-id.js";import{actionIdempotencyKey}from"#instrumentation/lifecycle.js";import{AGENT_TRACE_CONTEXT_KEY,deserializeAgentTraceContextState,emptyAgentTraceContextState,serializeAgentTraceContextState}from"#tracing/agent-trace-context-codec.js";const AgentTraceContextKey=new ContextKey(AGENT_TRACE_CONTEXT_KEY,{codec:{deserialize:deserializeAgentTraceContextState,serialize:serializeAgentTraceContextState}});function pruneAgentTraceState(e,t,n){try{pruneTraceOwnership(e,t,n)}catch(e){createLogger(`tracing.retention`).warn(`could not reconcile trace ownership; preserving trace state`,{error:e})}}function pruneTraceOwnership(e,t,n){let r=e.get(AgentTraceContextKey);if(r===void 0)return;let i=new Set(getBlockingWorkflowToolRuns(n).map(e=>e.callId)),a=new Set(getBackgroundTasks(n).query({state:`working`}).map(e=>e.taskId)),o=Object.fromEntries(Object.entries(r.actionAnchors).filter(([e,n])=>n.sessionId!==t||r.actions[e]!==void 0||i.has(n.callId)||a.has(deriveTaskId({callId:n.callId,parentSessionId:t,parentTurnId:n.turnId})))),s=new Set(Object.values(o).filter(e=>e.sessionId===t).map(e=>e.callId)),c=Object.fromEntries(Object.entries(r.invocations).filter(([,e])=>e.sessionId!==t||e.terminal!==void 0||s.has(e.parentActionCallId)));e.set(AgentTraceContextKey,{...r,actionAnchors:o,invocations:c})}function readCurrentSessionTraceDecision(e){let t=contextStorage.getStore();return t===void 0?void 0:readSessionTraceDecision(t,e)}function readSessionTraceDecision(e,t){return e.get(AgentTraceContextKey)?.sessions[t]?.decision}function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readTurnTraceContext(e,t,n){let r=e[AgentTraceContextKey.name];if(r===void 0)return;let i=deserializeAgentTraceContextState(r),a=i.turns[turnKey(t,n)];return a===void 0?void 0:withTraceDecision(e,a.context,i.sessions[t]?.decision)}function readActionTraceContext(e,t,n,r){let i=e[AgentTraceContextKey.name];if(i===void 0)return;let a=deserializeAgentTraceContextState(i),o=actionIdempotencyKey(t,n,r),s=a.invocations[o]??a.actions[o]??a.actionAnchors[o];if(s!==void 0)return withTraceDecision(e,{isRemote:!1,spanId:s.spanId,traceFlags:s.parent.traceFlags,traceId:s.parent.traceId},a.sessions[s.sessionId]?.decision)}function readTaskActionTrace(e,t,n){let r=deserializeAgentTraceContextState(e[AgentTraceContextKey.name]);return Object.values(r.actionAnchors).find(e=>e.sessionId===t&&deriveTaskId({callId:e.callId,parentSessionId:t,parentTurnId:e.turnId})===n)}function recordNestedAgentInvocation(e){let t=e.serializedContext[AgentTraceContextKey.name];if(t===void 0)return e.serializedContext;let n=deserializeAgentTraceContextState(t),r=actionIdempotencyKey(e.sessionId,e.turnId,e.outerCallId),i=n.actions[r]??n.actionAnchors[r];if(i===void 0)return e.serializedContext;let a=actionIdempotencyKey(e.sessionId,e.turnId,e.callId),promote=e=>e.isWorkflowTool===!0&&e.kind===`tool-call`?{...e,workflowName:e.name}:e,o=n.actions[r]===void 0?n.actions:{...n.actions,[r]:promote(n.actions[r])},s=n.actionAnchors[r]===void 0?n.actionAnchors:{...n.actionAnchors,[r]:promote(n.actionAnchors[r])};if(n.invocations[a]!==void 0)return{...e.serializedContext,[AgentTraceContextKey.name]:serializeAgentTraceContextState({...n,actionAnchors:s,actions:o})};let c={attemptIndex:i.attemptIndex,callId:e.callId,channelAudience:i.channelAudience,kind:e.kind,name:e.name,parent:{spanId:i.spanId,traceFlags:i.parent.traceFlags,traceId:i.parent.traceId},parentActionCallId:e.outerCallId,recordOutputs:e.recordOutputs===!0,rootSessionId:i.rootSessionId,sessionId:i.sessionId,spanId:e.spanId,startTimeMs:e.startTimeMs,stepIndex:i.stepIndex,turnId:i.turnId};return{...e.serializedContext,[AgentTraceContextKey.name]:serializeAgentTraceContextState({...n,actionAnchors:s,actions:o,invocations:{...n.invocations,[a]:c}})}}function recordActionInvocationKind(e){let t=e.serializedContext[AgentTraceContextKey.name];if(t===void 0)return e.serializedContext;let n=deserializeAgentTraceContextState(t),update=t=>t.sessionId===e.sessionId&&t.turnId===e.turnId&&t.callId===e.callId?{...t,isWorkflowTool:void 0,kind:e.kind,workflowName:void 0}:t;return{...e.serializedContext,[AgentTraceContextKey.name]:serializeAgentTraceContextState({...n,actionAnchors:Object.fromEntries(Object.entries(n.actionAnchors).map(([e,t])=>[e,update(t)])),actions:Object.fromEntries(Object.entries(n.actions).map(([e,t])=>[e,update(t)]))})}}function withTraceDecision(e,t,n){let r=e[SessionTraceSeedKey.name],i=resolveForwardedTraceSeed({decision:n??r?.decision,forwardedTracePolicy:r?.forwardedTracePolicy,traceFlags:t.traceFlags}),a=i.decision,o=i.forwardedTracePolicy,s=decisionToTraceContentCeiling(a),c={...t,traceFlags:i.traceFlags};if(o===void 0)return a===void 0?c:{...c,decision:a};let l=s===void 0?o:{...o,ceiling:s};return a===void 0?{...c,forwardedTracePolicy:l}:{...c,decision:a,forwardedTracePolicy:l}}var ContextAgentTraceStateStore=class{deleteAction(e){updateState(t=>{let n={...t.actions};return delete n[e],{...t,actions:n}})}deleteActionAnchors(e){updateState(t=>({...t,actionAnchors:Object.fromEntries(Object.entries(t.actionAnchors).filter(([,t])=>t.sessionId!==e))}))}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}})}deleteInvocation(e){updateState(t=>{let n={...t.invocations};return delete n[e],{...t,invocations:n}})}deleteInvocations(e,t){updateState(n=>({...n,invocations:Object.fromEntries(Object.entries(n.invocations).filter(([,n])=>n.sessionId!==e||t!==void 0&&n.turnId!==t))}))}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,t){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.actions??{}).find(n=>n.sessionId===e&&n.callId===t)}findActionAnchor(e,t,n){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.actionAnchors??{}).find(r=>r.sessionId===e&&r.turnId===t&&r.callId===n)}findInvocations(e,t,n){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.invocations??{}).filter(r=>(e===void 0||r.sessionId===e)&&(t===void 0||r.turnId===t)&&(n===void 0||r.parentActionCallId===n))}getAction(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.actions[e]}getSession(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[e]}getTurn(e,t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(e,t)]}setAction(e,t){updateState(n=>({...n,actions:{...n.actions,[e]:t}}))}setActionAnchor(e,t){updateState(n=>({...n,actionAnchors:{...n.actionAnchors,[e]:t}}))}setInvocation(e,t){updateState(n=>({...n,invocations:{...n.invocations,[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}}))}updateTurn(e,t,n){updateState(r=>{let i=turnKey(e,t),a=r.turns[i];return a===void 0?r:{...r,turns:{...r.turns,[i]:n(a)}}})}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??emptyAgentTraceContextState()))}function turnKey(e,t){return`${e}\0${t}`}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,pruneAgentTraceState,readActionTraceContext,readCurrentSessionTraceDecision,readSessionTraceDecision,readTaskActionTrace,readTurnTraceContext,recordActionInvocationKind,recordNestedAgentInvocation};
|