eve 0.71.2 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +22 -6
- package/dist/src/acp/adapter.js +1 -1
- package/dist/src/ai/decide.d.ts +7 -0
- package/dist/src/ai/decide.js +1 -0
- package/dist/src/channel/conversation-context.d.ts +4 -1
- package/dist/src/channel/invoke-tool.d.ts +41 -0
- package/dist/src/channel/invoke-tool.js +1 -0
- package/dist/src/channel/routes.d.ts +3 -0
- package/dist/src/chunks/{use-eve-agent-CFCaYnrI.js → use-eve-agent-C1GYGCtb.js} +698 -388
- package/dist/src/chunks/{use-eve-agent-DcDhXNv1.js → use-eve-agent-hZJ7CB8t.js} +698 -388
- package/dist/src/cli/commands/integration-setup.js +1 -1
- package/dist/src/cli/commands/registry.js +1 -1
- package/dist/src/cli/commands/trace-detail.d.ts +1 -1
- package/dist/src/cli/commands/trace-detail.js +1 -1
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/setup-flow.d.ts +1 -0
- package/dist/src/cli/dev/tui/setup-panel.d.ts +1 -0
- package/dist/src/cli/dev/tui/setup-panel.js +2 -1
- package/dist/src/cli/dev/tui/traces/trace-conversation.d.ts +2 -2
- package/dist/src/cli/dev/tui/traces/trace-conversation.js +2 -2
- package/dist/src/cli/dev/tui/transcript-parts.d.ts +2 -1
- package/dist/src/cli/dev/tui/transcript-parts.js +1 -1
- package/dist/src/cli/dev/tui/transcript.js +1 -1
- package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
- package/dist/src/client/conversation-projection.d.ts +4 -0
- package/dist/src/client/conversation-projection.js +1 -0
- package/dist/src/client/conversation-reducer.js +1 -1
- package/dist/src/client/conversation-state.d.ts +5 -26
- package/dist/src/client/conversation-state.js +1 -1
- package/dist/src/client/index.d.ts +1 -0
- package/dist/src/client/index.js +1 -1
- package/dist/src/client/message-action-parts.d.ts +1 -11
- package/dist/src/client/message-action-parts.js +1 -1
- package/dist/src/client/message-reducer.d.ts +3 -1
- package/dist/src/client/message-reducer.js +1 -1
- package/dist/src/client/session-utils.js +1 -1
- package/dist/src/client/session.js +1 -1
- package/dist/src/client/tool-call-state.d.ts +23 -0
- package/dist/src/client/tool-call-state.js +1 -0
- package/dist/src/client/tool-part-state.d.ts +7 -0
- package/dist/src/client/tool-part-state.js +1 -0
- package/dist/src/compiled/.vendor-stamp.json +10 -10
- package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +1453 -1358
- package/dist/src/compiled/@ai-sdk/anthropic/index.js +1 -2
- package/dist/src/compiled/@ai-sdk/code-mode/index.d.ts +172 -163
- package/dist/src/compiled/@ai-sdk/code-mode/index.js +15 -20
- package/dist/src/compiled/@ai-sdk/google/index.d.ts +772 -733
- package/dist/src/compiled/@ai-sdk/google/index.js +6 -8
- package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +887 -790
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/openai/index.d.ts +1722 -1691
- package/dist/src/compiled/@ai-sdk/openai/index.js +3 -14
- package/dist/src/compiled/@ai-sdk/otel/index.d.ts +216 -159
- package/dist/src/compiled/@ai-sdk/otel/index.js +3 -3
- package/dist/src/compiled/@ai-sdk/provider/index.d.ts +7542 -7271
- package/dist/src/compiled/@ai-sdk/provider/index.js +1 -3
- package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +1799 -1600
- package/dist/src/compiled/@eve/catalog/index.js +1 -1
- package/dist/src/compiled/_chunks/workflow/dist-DXLdlucM.js +15 -0
- package/dist/src/compiled/_chunks/workflow/experimental-decision-BrfWf6-n.js +3 -0
- package/dist/src/compiler/extension-compatibility.d.ts +36 -20
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/context/build-dynamic-tools.js +1 -1
- package/dist/src/context/caller-scope.d.ts +7 -0
- package/dist/src/context/caller-scope.js +1 -0
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/dynamic-tool-metadata.d.ts +1 -0
- package/dist/src/context/providers/session.js +1 -1
- package/dist/src/evals/define-eval.d.ts +1 -1
- package/dist/src/evals/judge.d.ts +1 -1
- package/dist/src/evals/judge.js +2 -2
- package/dist/src/evals/types.d.ts +8 -8
- package/dist/src/execution/cancel-descendant-turns-step.d.ts +1 -0
- package/dist/src/execution/cancel-descendant-turns-step.js +1 -1
- package/dist/src/execution/cancelled-model-call-batch.js +1 -1
- package/dist/src/execution/coordination-dispatch-shared.d.ts +2 -1
- package/dist/src/execution/coordination-dispatch-shared.js +1 -1
- package/dist/src/execution/coordination-dispatch-step.js +1 -1
- package/dist/src/execution/durable-session-read.d.ts +5 -0
- package/dist/src/execution/durable-session-read.js +1 -0
- package/dist/src/execution/durable-session-store.d.ts +4 -10
- package/dist/src/execution/durable-session-store.js +1 -1
- package/dist/src/execution/dynamic-connections.d.ts +3 -3
- package/dist/src/execution/dynamic-connections.js +1 -1
- package/dist/src/execution/dynamic-workflow/tool.js +1 -1
- package/dist/src/execution/invoke-tool.d.ts +35 -0
- package/dist/src/execution/invoke-tool.js +1 -0
- package/dist/src/execution/legacy-session/interrupt-step.js +1 -1
- package/dist/src/execution/legacy-session/snapshot.d.ts +3 -0
- package/dist/src/execution/legacy-session/snapshot.js +1 -1
- package/dist/src/execution/model-call-batching.js +1 -1
- package/dist/src/execution/node-step.d.ts +2 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/proxied-deliver-step.js +1 -1
- package/dist/src/execution/publish-session-events.d.ts +8 -0
- package/dist/src/execution/publish-session-events.js +1 -1
- package/dist/src/execution/sandbox/active-handles.d.ts +6 -0
- package/dist/src/execution/sandbox/active-handles.js +1 -1
- package/dist/src/execution/sandbox/ensure.js +1 -1
- package/dist/src/execution/session/active-turn.js +1 -1
- package/dist/src/execution/session/answer-caller.d.ts +15 -0
- package/dist/src/execution/session/answer-caller.js +1 -0
- package/dist/src/execution/session/checkpoint-migrations.js +1 -1
- package/dist/src/execution/session/finalization.js +1 -1
- package/dist/src/execution/session/handoff-steps.d.ts +3 -1
- package/dist/src/execution/session/handoff-steps.js +1 -1
- package/dist/src/execution/session/handoff.d.ts +1 -1
- package/dist/src/execution/session/handoff.js +1 -1
- package/dist/src/execution/session/pending-turn-state.d.ts +8 -3
- package/dist/src/execution/session/pending-turn-state.js +1 -1
- package/dist/src/execution/session/task-tool-tracing-step.d.ts +5 -0
- package/dist/src/execution/session/task-tool-tracing-step.js +1 -0
- package/dist/src/execution/session/turn-step-result.js +1 -1
- package/dist/src/execution/session/turn-step.js +1 -1
- package/dist/src/execution/session/turn-waiting-step.js +1 -1
- package/dist/src/execution/session/turn.d.ts +1 -0
- package/dist/src/execution/session/turn.js +1 -1
- package/dist/src/execution/settle-cancelled-turn-step.d.ts +4 -6
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/tasks/start.d.ts +3 -3
- package/dist/src/execution/tasks/start.js +1 -1
- package/dist/src/execution/tasks/steps.d.ts +1 -1
- package/dist/src/execution/tasks/steps.js +1 -1
- package/dist/src/execution/terminal-session-completion-step.d.ts +7 -0
- package/dist/src/execution/terminal-session-completion-step.js +1 -1
- package/dist/src/execution/terminal-session-failure-step.js +1 -1
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/tools/connection-tools.js +1 -1
- package/dist/src/execution/tools/workflow/authorization-completion.js +1 -1
- package/dist/src/execution/tools/workflow/harness-definition.js +1 -1
- package/dist/src/execution/tools/workflow/step-execution.js +1 -1
- package/dist/src/execution/tools/workflow/step.js +1 -1
- package/dist/src/execution/tools/workflow/withdraw-step.d.ts +7 -9
- package/dist/src/execution/tools/workflow/withdraw-step.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-trace-context.d.ts +2 -2
- package/dist/src/harness/authorization.d.ts +4 -2
- package/dist/src/harness/authorization.js +1 -1
- package/dist/src/harness/{compaction.js → compaction/engine.js} +1 -1
- package/dist/src/harness/compaction/step.d.ts +46 -0
- package/dist/src/harness/compaction/step.js +1 -0
- package/dist/src/harness/coordination.d.ts +32 -64
- package/dist/src/harness/coordination.js +1 -1
- package/dist/src/harness/current-messages.d.ts +1 -3
- package/dist/src/harness/current-messages.js +1 -1
- package/dist/src/harness/emission.d.ts +4 -52
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/execute-tool.d.ts +1 -0
- package/dist/src/harness/final-output.d.ts +5 -0
- package/dist/src/harness/final-output.js +1 -1
- package/dist/src/harness/hitl/approvals.d.ts +115 -0
- package/dist/src/harness/hitl/approvals.js +1 -0
- package/dist/src/harness/hitl/approved-call-callers.d.ts +14 -0
- package/dist/src/harness/hitl/approved-call-callers.js +1 -0
- package/dist/src/harness/hitl/approved-calls.d.ts +37 -0
- package/dist/src/harness/hitl/approved-calls.js +1 -0
- package/dist/src/harness/{session-limit-continuation.d.ts → hitl/budget-request.d.ts} +3 -1
- package/dist/src/harness/hitl/budget-request.js +1 -0
- package/dist/src/harness/hitl/budget.d.ts +21 -0
- package/dist/src/harness/hitl/budget.js +1 -0
- package/dist/src/harness/{approval-candidates.d.ts → hitl/candidates.d.ts} +13 -18
- package/dist/src/harness/hitl/candidates.js +1 -0
- package/dist/src/harness/hitl/coordinator.js +1 -0
- package/dist/src/harness/hitl/delivery.d.ts +37 -0
- package/dist/src/harness/hitl/delivery.js +1 -0
- package/dist/src/harness/hitl/index.d.ts +49 -0
- package/dist/src/harness/hitl/index.js +1 -0
- package/dist/src/harness/hitl/intake.d.ts +67 -0
- package/dist/src/harness/hitl/intake.js +1 -0
- package/dist/src/harness/{stale-input-responses.d.ts → hitl/stale-responses.d.ts} +5 -3
- package/dist/src/harness/hitl/stale-responses.js +3 -0
- package/dist/src/harness/inline-tool-authorization.d.ts +4 -0
- package/dist/src/harness/inline-tool-authorization.js +1 -1
- package/dist/src/harness/input-extraction.d.ts +1 -10
- package/dist/src/harness/input-extraction.js +1 -1
- package/dist/src/harness/input-request-resolution.d.ts +1 -5
- package/dist/src/harness/input-request-resolution.js +1 -1
- package/dist/src/harness/messages.js +1 -1
- package/dist/src/harness/model-call/call.d.ts +78 -0
- package/dist/src/harness/model-call/call.js +1 -0
- package/dist/src/harness/{model-call-error.d.ts → model-call/errors.d.ts} +1 -1
- package/dist/src/harness/model-call/failure.d.ts +52 -0
- package/dist/src/harness/model-call/failure.js +1 -0
- package/dist/src/harness/model-call/model.d.ts +18 -0
- package/dist/src/harness/model-call/model.js +1 -0
- package/dist/src/harness/model-call/recovery.d.ts +52 -0
- package/dist/src/harness/model-call/recovery.js +1 -0
- package/dist/src/harness/model-call/request.d.ts +30 -0
- package/dist/src/harness/model-call/request.js +3 -0
- package/dist/src/harness/model-call/response.d.ts +39 -0
- package/dist/src/harness/model-call/response.js +1 -0
- package/dist/src/harness/model-call/retry.d.ts +11 -0
- package/dist/src/harness/model-call/retry.js +1 -0
- package/dist/src/harness/model-call/run.d.ts +38 -0
- package/dist/src/harness/model-call/run.js +1 -0
- package/dist/src/harness/model-call/tools.d.ts +48 -0
- package/dist/src/harness/model-call/tools.js +1 -0
- package/dist/src/harness/model-call/usage.d.ts +23 -0
- package/dist/src/harness/model-call/usage.js +1 -0
- package/dist/src/harness/nested-actions.d.ts +2 -2
- package/dist/src/harness/provider-tools.d.ts +7 -3
- package/dist/src/harness/provider-tools.js +1 -1
- package/dist/src/harness/proxy-input-requests.d.ts +4 -12
- package/dist/src/harness/proxy-input-requests.js +1 -1
- package/dist/src/harness/session-machine/commit.d.ts +28 -0
- package/dist/src/harness/session-machine/commit.js +1 -0
- package/dist/src/harness/session-machine/current.d.ts +29 -0
- package/dist/src/harness/session-machine/current.js +1 -0
- package/dist/src/harness/session-machine/events.d.ts +6 -0
- package/dist/src/harness/session-machine/events.js +1 -0
- package/dist/src/harness/session-machine/resolver-events.d.ts +12 -0
- package/dist/src/harness/session-machine/resolver-events.js +1 -0
- package/dist/src/harness/session-machine/state.d.ts +47 -0
- package/dist/src/harness/session-machine/state.js +1 -0
- package/dist/src/harness/session-machine/transitions.d.ts +188 -0
- package/dist/src/harness/session-machine/transitions.js +1 -0
- package/dist/src/harness/session-machine/view.d.ts +58 -0
- package/dist/src/harness/session-machine/view.js +1 -0
- package/dist/src/harness/step/after-model.d.ts +7 -0
- package/dist/src/harness/step/after-model.js +1 -0
- package/dist/src/harness/step/context.d.ts +59 -0
- package/dist/src/harness/step/context.js +1 -0
- package/dist/src/harness/step/intake.d.ts +34 -0
- package/dist/src/harness/step/intake.js +1 -0
- package/dist/src/harness/step/prompt.d.ts +37 -0
- package/dist/src/harness/step/prompt.js +1 -0
- package/dist/src/harness/step-hooks.d.ts +8 -13
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/tool-call-io.d.ts +25 -0
- package/dist/src/harness/tool-call-io.js +1 -0
- package/dist/src/harness/tool-loop.d.ts +7 -22
- package/dist/src/harness/tool-loop.js +1 -3
- package/dist/src/harness/tool-model-output.d.ts +1 -2
- package/dist/src/harness/tools.d.ts +14 -0
- package/dist/src/harness/tools.js +1 -1
- package/dist/src/harness/turn-tag-state.d.ts +1 -1
- package/dist/src/harness/types.d.ts +6 -0
- package/dist/src/instrumentation/ai-sdk-hook-bridge.d.ts +1 -1
- package/dist/src/instrumentation/ai-sdk-hook-bridge.js +1 -1
- package/dist/src/instrumentation/content.js +1 -1
- package/dist/src/instrumentation/dispatch.js +1 -1
- package/dist/src/instrumentation/lifecycle.d.ts +28 -52
- package/dist/src/instrumentation/native-events.d.ts +2 -1
- package/dist/src/instrumentation/native-events.js +1 -1
- package/dist/src/instrumentation/provider.d.ts +1 -1
- package/dist/src/instrumentation/runtime-context.d.ts +2 -3
- package/dist/src/instrumentation/runtime-context.js +1 -1
- package/dist/src/instrumentation/runtime.d.ts +11 -1
- package/dist/src/instrumentation/runtime.js +1 -1
- package/dist/src/instrumentation/state.d.ts +6 -1
- package/dist/src/instrumentation/state.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/attachments/limited-read.d.ts +16 -0
- package/dist/src/internal/attachments/limited-read.js +1 -0
- package/dist/src/internal/authored-definition/schema-backed.d.ts +2 -1
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/gateway.d.ts +2 -0
- package/dist/src/internal/gateway.js +1 -1
- package/dist/src/internal/mcp/published-tools.d.ts +13 -0
- package/dist/src/internal/mcp/published-tools.js +1 -0
- package/dist/src/internal/mcp/request-trace-context.d.ts +12 -0
- package/dist/src/internal/mcp/request-trace-context.js +1 -0
- package/dist/src/internal/mcp/streamable-http-server.d.ts +7 -3
- package/dist/src/internal/mcp/streamable-http-server.js +1 -1
- package/dist/src/internal/model-auth/transport.d.ts +3 -3
- package/dist/src/internal/model-auth/transport.js +1 -1
- package/dist/src/internal/nitro/host/instrumentation-entry.js +2 -2
- package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
- package/dist/src/internal/nitro/routes/route-invoke-tool.d.ts +24 -0
- package/dist/src/internal/nitro/routes/route-invoke-tool.js +1 -0
- package/dist/src/models/auto.d.ts +5 -5
- package/dist/src/models/auto.js +2 -2
- package/dist/src/protocol/message.d.ts +7 -0
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/protocol/session-projection.d.ts +159 -0
- package/dist/src/protocol/session-projection.js +1 -0
- package/dist/src/public/ai/index.d.ts +1 -1
- package/dist/src/public/ai/index.js +1 -1
- package/dist/src/public/channels/chat-sdk/attachment-refs.d.ts +15 -0
- package/dist/src/public/channels/chat-sdk/attachment-refs.js +1 -0
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
- package/dist/src/public/channels/chat-sdk/messageToUserContent.d.ts +11 -5
- package/dist/src/public/channels/chat-sdk/messageToUserContent.js +1 -1
- package/dist/src/public/channels/discord/attachments.d.ts +15 -0
- package/dist/src/public/channels/discord/attachments.js +1 -0
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/discord/inbound.d.ts +13 -0
- package/dist/src/public/channels/discord/inbound.js +1 -1
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/linear/inbound-uploads.d.ts +21 -0
- package/dist/src/public/channels/linear/inbound-uploads.js +1 -0
- package/dist/src/public/channels/linear/linearChannel.js +1 -1
- package/dist/src/public/channels/linq/inboundContent.d.ts +7 -1
- package/dist/src/public/channels/linq/inboundContent.js +1 -1
- package/dist/src/public/channels/linq/linqChannel.js +1 -1
- package/dist/src/public/channels/mcp.d.ts +17 -2
- package/dist/src/public/channels/mcp.js +1 -1
- package/dist/src/public/channels/photon/attachments.d.ts +21 -0
- package/dist/src/public/channels/photon/attachments.js +1 -0
- package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -1
- package/dist/src/public/channels/slack/attachments.d.ts +3 -1
- package/dist/src/public/channels/slack/attachments.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/attachments.d.ts +4 -4
- package/dist/src/public/channels/teams/attachments.js +1 -1
- package/dist/src/public/channels/telegram/attachments.js +1 -1
- package/dist/src/public/channels/twilio/api.d.ts +7 -0
- package/dist/src/public/channels/twilio/api.js +1 -1
- package/dist/src/public/channels/twilio/inbound.d.ts +7 -0
- package/dist/src/public/channels/twilio/inbound.js +2 -2
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/channel.d.ts +1 -0
- package/dist/src/public/definitions/connections/mcp.d.ts +1 -1
- package/dist/src/public/definitions/connections/openapi.d.ts +1 -1
- package/dist/src/public/models/openai/chatgpt/model.js +1 -1
- package/dist/src/react/index.d.ts +1 -0
- package/dist/src/react/index.js +1 -1
- package/dist/src/runtime/agent/mock-model-adapter.js +2 -2
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/setup/boxes/resolve-provisioning.d.ts +2 -0
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/cli/prompt-ui.d.ts +3 -0
- package/dist/src/setup/cli/prompt-ui.js +3 -1
- package/dist/src/setup/cli/select-component.d.ts +1 -0
- package/dist/src/setup/cli/select-component.js +1 -1
- package/dist/src/setup/flows/ensure-vercel-project.d.ts +2 -0
- package/dist/src/setup/flows/ensure-vercel-project.js +1 -1
- package/dist/src/setup/integrations/discord/api.js +1 -1
- package/dist/src/setup/integrations/web/auth-options.d.ts +4 -0
- package/dist/src/setup/integrations/web/auth-options.js +1 -0
- package/dist/src/setup/integrations/web/auth-scaffold.d.ts +7 -0
- package/dist/src/setup/integrations/web/auth-scaffold.js +1 -0
- package/dist/src/setup/integrations/web/provision-auth.d.ts +3 -0
- package/dist/src/setup/integrations/web/provision-auth.js +1 -0
- package/dist/src/setup/integrations/web/setup.d.ts +11 -6
- package/dist/src/setup/integrations/web/setup.js +3 -3
- package/dist/src/setup/prompter.d.ts +2 -0
- package/dist/src/setup/prompter.js +1 -1
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +2 -2
- package/dist/src/setup/scaffold/create/web-template.d.ts +3 -3
- package/dist/src/setup/scaffold/create/web-template.js +26 -8
- package/dist/src/setup/scaffold/update/web-options.js +1 -1
- package/dist/src/setup/vercel-project-api.d.ts +9 -0
- package/dist/src/setup/vercel-project-api.js +1 -1
- package/dist/src/setup/vercel-project.d.ts +4 -2
- package/dist/src/setup/vercel-project.js +0 -0
- package/dist/src/shared/web-search.d.ts +1 -1
- package/dist/src/subagents/adapter.js +1 -1
- package/dist/src/subagents/event-proxy-step.js +1 -1
- package/dist/src/subagents/hitl-proxy.d.ts +4 -28
- package/dist/src/subagents/hitl-proxy.js +1 -1
- package/dist/src/svelte/index.d.ts +2 -0
- package/dist/src/svelte/index.js +39 -2
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tools/approval/policies.d.ts +5 -5
- package/dist/src/tools/approval/policies.js +1 -1
- package/dist/src/tools/framework/agent.js +1 -1
- package/dist/src/tools/provided/agent-router.js +1 -1
- package/dist/src/tools/provided/ask-question-workflow.js +1 -1
- package/dist/src/tools/provided/ask-question.js +2 -2
- package/dist/src/tools/provided/bash.js +1 -1
- package/dist/src/tools/provided/framework-tool.d.ts +3 -0
- package/dist/src/tools/provided/framework-tool.js +1 -0
- package/dist/src/tools/provided/glob.js +2 -2
- package/dist/src/tools/provided/grep.js +2 -2
- package/dist/src/tools/provided/load-skill.js +1 -1
- package/dist/src/tools/provided/no-reply.js +1 -1
- package/dist/src/tools/provided/read-file.js +2 -2
- package/dist/src/tools/provided/sleep-workflow.js +1 -1
- package/dist/src/tools/provided/sleep.js +1 -1
- package/dist/src/tools/provided/task-cancel.js +1 -1
- package/dist/src/tools/provided/task-wait.js +1 -1
- package/dist/src/tools/provided/web-fetch.js +2 -2
- package/dist/src/tools/provided/web-search.js +1 -1
- package/dist/src/tools/provided/workflow.js +1 -1
- package/dist/src/tools/provided/write-file.js +2 -2
- package/dist/src/tracing/agent-approval-instrumentation.d.ts +2 -2
- package/dist/src/tracing/agent-approval-instrumentation.js +1 -1
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/dist/src/tracing/agent-otel-usage.d.ts +1 -1
- package/dist/src/tracing/agent-otel-usage.js +1 -1
- package/dist/src/tracing/agent-span-contract.d.ts +8 -1
- package/dist/src/tracing/agent-span-contract.js +1 -1
- package/dist/src/tracing/agent-tool-instrumentation.d.ts +16 -5
- package/dist/src/tracing/agent-tool-instrumentation.js +1 -1
- package/dist/src/tracing/agent-trace-context-codec.js +1 -1
- package/dist/src/tracing/agent-trace-span-processor.js +1 -1
- package/dist/src/tracing/agent-trace-state.d.ts +8 -3
- package/dist/src/tracing/install-instrumentation-runtime.js +1 -1
- package/dist/src/tracing/invoke-tool-span.d.ts +61 -0
- package/dist/src/tracing/invoke-tool-span.js +1 -0
- package/dist/src/tracing/local-trace-analysis.js +1 -1
- package/dist/src/vue/index.d.ts +2 -0
- package/dist/src/vue/index.js +39 -2
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/agent-config.md +2 -2
- package/docs/channels/chat-sdk.mdx +3 -1
- package/docs/channels/custom.mdx +3 -0
- package/docs/channels/discord.mdx +5 -4
- package/docs/channels/eve.mdx +65 -0
- package/docs/channels/linear.mdx +1 -1
- package/docs/channels/linq.mdx +2 -0
- package/docs/channels/mcp.mdx +39 -1
- package/docs/channels/photon.mdx +5 -0
- package/docs/channels/teams.mdx +4 -7
- package/docs/channels/twilio.mdx +1 -1
- package/docs/concepts/built-in-tools.md +12 -0
- package/docs/concepts/sessions-runs-and-streaming.md +3 -3
- package/docs/connections/overview.mdx +1 -1
- package/docs/dynamic-configuration/meta.json +1 -1
- package/docs/evals/judge.mdx +16 -16
- package/docs/evals/overview.mdx +1 -1
- package/docs/guides/{evaluate.md → decide.md} +35 -35
- package/docs/guides/dynamic-capabilities.md +1 -1
- package/docs/guides/frontend/overview.mdx +3 -1
- package/docs/guides/hooks.md +1 -1
- package/docs/guides/instrumentation/instrumentation.mdx +23 -22
- package/docs/guides/instrumentation/otel.mdx +13 -2
- package/docs/reference/cli.md +4 -2
- package/docs/reference/typescript-api.md +4 -2
- package/docs/tools/human-in-the-loop.md +8 -4
- package/docs/tools/workflows.mdx +4 -4
- package/package.json +13 -11
- package/dist/src/ai/evaluate.d.ts +0 -7
- package/dist/src/ai/evaluate.js +0 -1
- package/dist/src/client/message-task-parts.d.ts +0 -8
- package/dist/src/client/message-task-parts.js +0 -1
- package/dist/src/compiled/_chunks/workflow/dist-C3ZE62yI.js +0 -25
- package/dist/src/compiled/_chunks/workflow/experimental-evaluation-Dm32egEG.js +0 -5
- package/dist/src/harness/active-turn-id.d.ts +0 -11
- package/dist/src/harness/active-turn-id.js +0 -1
- package/dist/src/harness/approval-candidates.js +0 -1
- package/dist/src/harness/approval-delivery-coordinator.js +0 -1
- package/dist/src/harness/cancelled-turn-emission.d.ts +0 -12
- package/dist/src/harness/cancelled-turn-emission.js +0 -1
- package/dist/src/harness/emission-state.d.ts +0 -26
- package/dist/src/harness/emission-state.js +0 -1
- package/dist/src/harness/held-requests.d.ts +0 -18
- package/dist/src/harness/held-requests.js +0 -1
- package/dist/src/harness/hitl/approval-input-requests.d.ts +0 -27
- package/dist/src/harness/hitl/approval-input-requests.js +0 -1
- package/dist/src/harness/hitl/pending-input-resolution.d.ts +0 -54
- package/dist/src/harness/hitl/pending-input-resolution.js +0 -1
- package/dist/src/harness/hitl/session-limit-input-requests.d.ts +0 -12
- package/dist/src/harness/hitl/session-limit-input-requests.js +0 -1
- package/dist/src/harness/input-requests.d.ts +0 -31
- package/dist/src/harness/input-requests.js +0 -1
- package/dist/src/harness/pending-input-batches.d.ts +0 -81
- package/dist/src/harness/pending-input-batches.js +0 -1
- package/dist/src/harness/session-limit-continuation.js +0 -1
- package/dist/src/harness/session-limit-enforcement.d.ts +0 -45
- package/dist/src/harness/session-limit-enforcement.js +0 -1
- package/dist/src/harness/stale-input-responses.js +0 -3
- package/dist/src/harness/workflow-dispatch.d.ts +0 -30
- package/dist/src/harness/workflow-dispatch.js +0 -1
- package/dist/src/public/channels/linear/inbound-images.d.ts +0 -19
- package/dist/src/public/channels/linear/inbound-images.js +0 -1
- package/dist/src/tracing/agent-action-instrumentation.d.ts +0 -25
- package/dist/src/tracing/agent-action-instrumentation.js +0 -1
- /package/dist/src/harness/{compaction.d.ts → compaction/engine.d.ts} +0 -0
- /package/dist/src/harness/{compaction-prompt.d.ts → compaction/prompt.d.ts} +0 -0
- /package/dist/src/harness/{compaction-prompt.js → compaction/prompt.js} +0 -0
- /package/dist/src/harness/{approval-delivery-coordinator.d.ts → hitl/coordinator.d.ts} +0 -0
- /package/dist/src/harness/{model-call-error.js → model-call/errors.js} +0 -0
|
@@ -2,4 +2,6 @@ import{UNICODE_ROW_GLYPHS,renderCursorRow,renderOptionRow,renderOptionRowContinu
|
|
|
2
2
|
`);return`${t.green(`│`)}\n${t.green(`●`)} ${n}\n`}function formatPromptCancellation(e,t){let n=e.replace(/\n/g,`
|
|
3
3
|
`);return`${t.red(`│`)}\n${t.red(`●`)} ${t.red(n)}\n`}function formatRailLine(e,t,n){if(e===``)return`${t.green(`│`)}\n`;let r=`${t.green(`│`)} `;return`${e.split(`
|
|
4
4
|
`).map(e=>e===``?t.green(`│`):wrapTextWithPrefix(n,e,r)).join(`
|
|
5
|
-
`)}\n`}function renderMultiselectPrompt(e){let t=railFor(e.state,e.colors),n=formatPromptHeader(e.state,e.message,{colors:e.colors,leadingRail:e.leadingRail}),r=new Set(e.selectedValues),i=e.options.some(e=>!e.disabled)?``:`\n${t}\n${t} ${e.colors.dim(`(no channels available to add)`)}`;switch(e.state){case`submit`:{let t=r.size===0?e.colors.dim(`(none selected)`):e.options.filter(e=>r.has(e.value)).map(e=>e.label).join(`, `);return formatPromptSubmission(e.state,e.message,t,{colors:e.colors,leadingRail:e.leadingRail})}case`cancel`:return`${n}${t} ${e.colors.strikethrough(e.colors.dim(`cancelled`))}\n${t}`;case`error`:{let r=renderMultiselectRows({...e,rail:t});return`${n.trim()}\n${t} ${r}${i}
|
|
5
|
+
`)}\n`}function renderMultiselectPrompt(e){let t=railFor(e.state,e.colors),n=formatPromptHeader(e.state,e.message,{colors:e.colors,leadingRail:e.leadingRail}),r=new Set(e.selectedValues),i=e.options.some(e=>!e.disabled)?``:`\n${t}\n${t} ${e.colors.dim(`(no channels available to add)`)}`;switch(e.state){case`submit`:{let t=r.size===0?e.colors.dim(`(none selected)`):e.options.filter(e=>r.has(e.value)).map(e=>e.label).join(`, `);return formatPromptSubmission(e.state,e.message,t,{colors:e.colors,leadingRail:e.leadingRail})}case`cancel`:return`${n}${t} ${e.colors.strikethrough(e.colors.dim(`cancelled`))}\n${t}`;case`error`:{let r=renderMultiselectRows({...e,rail:t});return`${n.trim()}\n${t} ${r}${i}${selectHelpText(e.helpText,t,e.colors)}\n${cornerFor(e.state,e.colors)} ${e.colors.red(e.error??``)}\n`}case`initial`:case`active`:{let r=renderMultiselectRows({...e,rail:t}),a=cornerWithNote(cornerFor(e.state,e.colors),e.footerNote);return`${n}${t} ${r}${i}${selectHelpText(e.helpText,t,e.colors)}\n${a}\n`}}}function selectHelpText(e,t,n){return e?`\n${t}\n${e.split(`
|
|
6
|
+
`).map(e=>wrapTextWithPrefix(process.stdout,n.dim(e),`${t} `)).join(`
|
|
7
|
+
`)}\n${t}`:``}function cornerWithNote(e,t){return t?`${e} ${t}`:e}function renderSelectPrompt(e){let t=railFor(e.state,e.colors),n=formatPromptHeader(e.state,e.message,{colors:e.colors,leadingRail:e.leadingRail}),r=e.options[e.cursor];switch(e.state){case`submit`:{let t=r?r.label:``;return formatPromptSubmission(e.state,e.message,t,{colors:e.colors,leadingRail:e.leadingRail})}case`cancel`:return`${n}${t} ${r?e.colors.strikethrough(e.colors.dim(r.label)):``}\n${t}`;case`initial`:case`active`:case`error`:{let r=labelColumnWidth(e.options),i=e.options.map((n,i)=>{let a=i===e.cursor;return`${optionRow(n,{colors:e.colors,isCursor:a,isChecked:!1,placeholder:!1,hintPadding:r-n.label.length})}${descriptionLine(n,a,t,e.colors)}`}).join(`\n${t} `),a=cornerWithNote(cornerFor(e.state,e.colors),e.footerNote);return`${n}${t} ${i}${selectHelpText(e.helpText,t,e.colors)}\n${a}\n`}}}function labelColumnWidth(e){return e.reduce((e,t)=>Math.max(e,t.label.length),0)}function optionRow(e,t){return renderOptionRow({colors:t.colors,glyphs:UNICODE_ROW_GLYPHS,label:e.label,hint:e.hint,focusHint:e.focusHint,accent:e.accent,isCursor:t.isCursor,state:resolveOptionRowState(e,t.isChecked),placeholder:t.placeholder,hintPadding:t.hintPadding})}function descriptionLine(e,t,n,r){return t&&e.description&&!e.disabled?`\n${n} ${renderOptionRowContinuation(r.dim(e.description))}`:``}function renderSubmitRow(e,t,n=`Submit`){let r=t.bold(n),i=e?`${UNICODE_ROW_GLYPHS.selectedPointer} ${r}`:` ${t.dim(r)}`;return`${renderCursorRow(i,e,t)} ${t.green(UNICODE_ROW_GLYPHS.success)}`}function renderMultiselectRows(e){let t=new Set(e.selectedValues),n=labelColumnWidth(e.options),r=e.options.map((r,i)=>{let a=i===e.cursor;return`${optionRow(r,{colors:e.colors,isCursor:a,isChecked:t.has(r.value),placeholder:!0,hintPadding:n-r.label.length})}${descriptionLine(r,a,e.rail,e.colors)}`}).join(`\n${e.rail} `),i=renderSubmitRow(e.cursor===e.options.length,e.colors,e.submitLabel);return`${r}\n${e.rail}\n${e.rail} ${i}`}function searchableHelpLine(e,t,n){let r=(n?[[t.cyan(`type`),`to filter`]]:[[t.cyan(`type`),`to filter`],[t.cyan(`enter`),`to select`]]).map(([e,n])=>`${e}${t.dim(` ${n}`)}`).join(t.dim(` · `));return`\n${e} ${t.dim(`(`)}${r}${t.dim(`)`)}`}function renderSearchableSelect(e){let{colors:t}=e,n=railFor(e.state,t),r=formatPromptHeader(e.state,e.message,{colors:t,leadingRail:e.leadingRail});if(e.state===`submit`)return formatPromptSubmission(e.state,e.message,e.submitDisplay,{colors:t,leadingRail:e.leadingRail});if(e.state===`cancel`)return`${r}${n} ${t.strikethrough(t.dim(e.filter))}${e.filter.trim()?`\n${n}`:``}`;let i=new Set(e.selectedValues),a=0;for(;e.options[a]?.featured;)a+=1;let o=e.viewSize??8,s=e.filter===``&&a>0?Math.min(a,o):o,c=e.multiple&&e.cursor>=e.options.length,l=!c&&e.cursor>=e.options.length?0:e.cursor,u=t.inverse(` `);e.filter.length>0?u=e.filter+t.inverse(` `):e.placeholder&&(u=t.dim(e.placeholder));let d=Math.max(0,Math.min(l-Math.floor(s/2),Math.max(0,e.options.length-s))),f=Math.min(d+s,e.options.length),p=e.options.slice(d,f),m=labelColumnWidth(p),h=p.length===0?t.dim(`(no matches)`):p.map((r,a)=>{let o=!c&&a+d===l;return`${optionRow(r,{colors:t,isCursor:o,isChecked:e.multiple&&i.has(r.value),placeholder:!1,hintPadding:m-r.label.length})}${descriptionLine(r,o,n,t)}`}).join(`\n${n} `),g=e.multiple?`\n${n}\n${n} ${renderSubmitRow(c,t,e.submitLabel)}`:``,_=e.options.length>p.length?`\n${n} ${t.dim(`↑↓ ${e.options.length} options, showing ${d+1}–${f}`)}`:``,v=searchableHelpLine(n,t,e.multiple),y=`${n} ${t.dim(` `)} ${u}\n${n} ${h}${g}${_}${selectHelpText(e.helpText,n,t)}${v}`;return e.state===`error`?`${r.trim()}\n${y}\n${cornerFor(e.state,t)} ${t.red(e.error??``)}\n`:`${r}${y}\n${cornerWithNote(cornerFor(e.state,t),e.footerNote)}\n`}export{CORNER,RAIL,bulletFor,cornerFor,formatPromptCancellation,formatPromptHeader,formatPromptOpener,formatPromptOutro,formatPromptSubmission,formatRailLine,railFor,renderMultiselectPrompt,renderSearchableSelect,renderSelectPrompt};
|
|
@@ -73,6 +73,7 @@ export declare class SelectComponent extends Prompt<string | string[]> {
|
|
|
73
73
|
*/
|
|
74
74
|
export declare function runSelectComponent<T extends PromptValue>(input: {
|
|
75
75
|
message: string;
|
|
76
|
+
helpText?: string;
|
|
76
77
|
options: readonly PromptOption<T>[];
|
|
77
78
|
multiple: boolean;
|
|
78
79
|
search: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{renderMultiselectPrompt,renderSearchableSelect,renderSelectPrompt}from"./prompt-ui.js";import{createSelectOptionCodec}from"./select-option-codec.js";import{filterOptions,initialSelectState,orderedSelection,reduceSelect,searchActionQuery,selectValueAtCursor,submitRowIndex}from"./select-state.js";import pc from"#compiled/picocolors/index.js";import{Prompt,isCancel}from"#compiled/@clack/core/index.js";function toPromptState(e){return e}var SelectComponent=class extends Prompt{options;multiple;search;searchAction;required;filter=``;optionCursor=0;selectedSet=new Set;constructor(e){super({render:e.render,validate:()=>this.submitError()},!1),this.options=e.options,this.multiple=e.multiple,this.search=e.search,this.searchAction=e.searchAction,this.required=e.required,this.filter=e.initial.filter,this.optionCursor=e.initial.cursor,this.selectedSet=e.initial.selected,this.refreshValue(),this.on(`key`,(e,t)=>{if(this.multiple&&t?.name===`space`){this.apply({type:`toggle`});return}if(this.search){if(t?.name===`backspace`||e===``||e===`\b`){this.apply({type:`backspace`});return}e!==void 0&&e.length===1&&e>=` `&&e!==``&&this.apply({type:`char`,char:e})}}),this.on(`cursor`,e=>{e===`up`||e===`left`?this.apply({type:`up`}):(e===`down`||e===`right`)&&this.apply({type:`down`})})}visibleOptions(){return filterOptions(this.options,this.filter,this.searchAction)}onSubmitRow(){return this.multiple&&this.optionCursor===submitRowIndex(this.visibleOptions())}submitLabel(){if(this.required)return`Submit`;let e=new Set(this.options.filter(e=>e.locked).map(e=>e.value));return[...this.selectedSet].some(t=>!e.has(t))?`Submit`:`Skip`}_shouldSubmit(){return this.multiple?this.onSubmitRow()?!0:(this.apply({type:`toggle`}),!1):this.visibleOptions()[this.optionCursor]?.completed!==!0}selectedValues(){if(this.multiple)return orderedSelection(this.options,this.selectedSet);let e=selectValueAtCursor(this.visibleOptions(),this.optionCursor);return e===void 0?[]:[e]}submitDisplay(){if(this.multiple){let e=this.options.filter(e=>this.selectedSet.has(e.value)).map(e=>e.label);return e.length>0?e.join(`, `):pc.dim(`(none selected)`)}let e=selectValueAtCursor(this.visibleOptions(),this.optionCursor);return e===void 0?``:this.labelForValue(e)}labelForValue(e){let t=searchActionQuery(e);return t!==void 0&&this.searchAction!==void 0?this.searchAction.label(t):this.options.find(t=>t.value===e)?.label??e}submitError(){return this.multiple?this.required&&this.selectedSet.size===0?`Select at least one option, then press enter.`:void 0:selectValueAtCursor(this.visibleOptions(),this.optionCursor)===void 0?`Type to match an option, then press enter.`:void 0}apply(e){let t=reduceSelect({filter:this.filter,cursor:this.optionCursor,selected:this.selectedSet},e,{options:this.options,searchAction:this.searchAction,submitRow:this.multiple});this.filter=t.filter,this.optionCursor=t.cursor,this.selectedSet=t.selected,this.refreshValue()}refreshValue(){this.value=this.multiple?orderedSelection(this.options,this.selectedSet):selectValueAtCursor(this.visibleOptions(),this.optionCursor)}};function renderSelectComponent(e,t,n,r){let i=toPromptState(e.state);return e.search?renderSearchableSelect({colors:pc,state:i,leadingRail:n,message:t.message,multiple:e.multiple,filter:e.filter,placeholder:t.placeholder,options:e.visibleOptions(),cursor:e.optionCursor,selectedValues:e.selectedValues(),submitDisplay:e.submitDisplay(),footerNote:r,error:e.error,submitLabel:e.submitLabel()}):e.multiple?renderMultiselectPrompt({colors:pc,cursor:e.optionCursor,error:e.error,footerNote:r,leadingRail:n,message:t.message,options:e.options,selectedValues:e.selectedValues(),state:i,submitLabel:e.submitLabel()}):renderSelectPrompt({colors:pc,cursor:e.optionCursor,footerNote:r,leadingRail:n,message:t.message,options:e.options,state:i})}async function runSelectComponent(e){let t=createSelectOptionCodec(e.options),n=initialSelectState({options:t.options,defaultValue:e.defaultValue===void 0?void 0:t.encode(e.defaultValue),initialValues:e.initialValues?.map(e=>t.encode(e)),submitRow:e.multiple}),r,i,a=new SelectComponent({options:t.options,multiple:e.multiple,search:e.search,searchAction:e.searchAction===void 0?void 0:{label:e.searchAction.label},required:e.required,initial:n,render(){return r?renderSelectComponent(r,{message:e.message,placeholder:e.placeholder},e.leadingRail,i?.note()):``}});r=a,i=e.attachGuard?.(a);let o=await a.prompt();if(isCancel(o))return o;if(o===void 0)throw Error(`Select prompt returned no value.`);if(Array.isArray(o))return o.map(e=>t.decode(e));let s=searchActionQuery(o);return s!==void 0&&e.searchAction!==void 0?e.searchAction.value(s):t.decode(o)}export{SelectComponent,runSelectComponent};
|
|
1
|
+
import{renderMultiselectPrompt,renderSearchableSelect,renderSelectPrompt}from"./prompt-ui.js";import{createSelectOptionCodec}from"./select-option-codec.js";import{filterOptions,initialSelectState,orderedSelection,reduceSelect,searchActionQuery,selectValueAtCursor,submitRowIndex}from"./select-state.js";import pc from"#compiled/picocolors/index.js";import{Prompt,isCancel}from"#compiled/@clack/core/index.js";function toPromptState(e){return e}var SelectComponent=class extends Prompt{options;multiple;search;searchAction;required;filter=``;optionCursor=0;selectedSet=new Set;constructor(e){super({render:e.render,validate:()=>this.submitError()},!1),this.options=e.options,this.multiple=e.multiple,this.search=e.search,this.searchAction=e.searchAction,this.required=e.required,this.filter=e.initial.filter,this.optionCursor=e.initial.cursor,this.selectedSet=e.initial.selected,this.refreshValue(),this.on(`key`,(e,t)=>{if(this.multiple&&t?.name===`space`){this.apply({type:`toggle`});return}if(this.search){if(t?.name===`backspace`||e===``||e===`\b`){this.apply({type:`backspace`});return}e!==void 0&&e.length===1&&e>=` `&&e!==``&&this.apply({type:`char`,char:e})}}),this.on(`cursor`,e=>{e===`up`||e===`left`?this.apply({type:`up`}):(e===`down`||e===`right`)&&this.apply({type:`down`})})}visibleOptions(){return filterOptions(this.options,this.filter,this.searchAction)}onSubmitRow(){return this.multiple&&this.optionCursor===submitRowIndex(this.visibleOptions())}submitLabel(){if(this.required)return`Submit`;let e=new Set(this.options.filter(e=>e.locked).map(e=>e.value));return[...this.selectedSet].some(t=>!e.has(t))?`Submit`:`Skip`}_shouldSubmit(){return this.multiple?this.onSubmitRow()?!0:(this.apply({type:`toggle`}),!1):this.visibleOptions()[this.optionCursor]?.completed!==!0}selectedValues(){if(this.multiple)return orderedSelection(this.options,this.selectedSet);let e=selectValueAtCursor(this.visibleOptions(),this.optionCursor);return e===void 0?[]:[e]}submitDisplay(){if(this.multiple){let e=this.options.filter(e=>this.selectedSet.has(e.value)).map(e=>e.label);return e.length>0?e.join(`, `):pc.dim(`(none selected)`)}let e=selectValueAtCursor(this.visibleOptions(),this.optionCursor);return e===void 0?``:this.labelForValue(e)}labelForValue(e){let t=searchActionQuery(e);return t!==void 0&&this.searchAction!==void 0?this.searchAction.label(t):this.options.find(t=>t.value===e)?.label??e}submitError(){return this.multiple?this.required&&this.selectedSet.size===0?`Select at least one option, then press enter.`:void 0:selectValueAtCursor(this.visibleOptions(),this.optionCursor)===void 0?`Type to match an option, then press enter.`:void 0}apply(e){let t=reduceSelect({filter:this.filter,cursor:this.optionCursor,selected:this.selectedSet},e,{options:this.options,searchAction:this.searchAction,submitRow:this.multiple});this.filter=t.filter,this.optionCursor=t.cursor,this.selectedSet=t.selected,this.refreshValue()}refreshValue(){this.value=this.multiple?orderedSelection(this.options,this.selectedSet):selectValueAtCursor(this.visibleOptions(),this.optionCursor)}};function renderSelectComponent(e,t,n,r){let i=toPromptState(e.state);return e.search?renderSearchableSelect({colors:pc,state:i,leadingRail:n,message:t.message,helpText:t.helpText,multiple:e.multiple,filter:e.filter,placeholder:t.placeholder,options:e.visibleOptions(),cursor:e.optionCursor,selectedValues:e.selectedValues(),submitDisplay:e.submitDisplay(),footerNote:r,error:e.error,submitLabel:e.submitLabel()}):e.multiple?renderMultiselectPrompt({colors:pc,cursor:e.optionCursor,error:e.error,footerNote:r,leadingRail:n,message:t.message,helpText:t.helpText,options:e.options,selectedValues:e.selectedValues(),state:i,submitLabel:e.submitLabel()}):renderSelectPrompt({colors:pc,cursor:e.optionCursor,footerNote:r,leadingRail:n,message:t.message,helpText:t.helpText,options:e.options,state:i})}async function runSelectComponent(e){let t=createSelectOptionCodec(e.options),n=initialSelectState({options:t.options,defaultValue:e.defaultValue===void 0?void 0:t.encode(e.defaultValue),initialValues:e.initialValues?.map(e=>t.encode(e)),submitRow:e.multiple}),r,i,a=new SelectComponent({options:t.options,multiple:e.multiple,search:e.search,searchAction:e.searchAction===void 0?void 0:{label:e.searchAction.label},required:e.required,initial:n,render(){return r?renderSelectComponent(r,{message:e.message,placeholder:e.placeholder,helpText:e.helpText},e.leadingRail,i?.note()):``}});r=a,i=e.attachGuard?.(a);let o=await a.prompt();if(isCancel(o))return o;if(o===void 0)throw Error(`Select prompt returned no value.`);if(Array.isArray(o))return o.map(e=>t.decode(e));let s=searchActionQuery(o);return s!==void 0&&e.searchAction!==void 0?e.searchAction.value(s):t.decode(o)}export{SelectComponent,runSelectComponent};
|
|
@@ -3,6 +3,7 @@ import { type ResolveProvisioningDeps } from "../boxes/resolve-provisioning.js";
|
|
|
3
3
|
import type { Prompter } from "../prompter.js";
|
|
4
4
|
import { readProjectLink, type VercelProjectReference } from "../project-resolution.js";
|
|
5
5
|
import { requireAuth } from "../vercel-project.js";
|
|
6
|
+
import { type VercelTeamRequirement } from "../vercel-project-api.js";
|
|
6
7
|
import { runLoginFlow } from "./login.js";
|
|
7
8
|
export interface EnsureVercelProjectDeps {
|
|
8
9
|
resolveProvisioning?: ResolveProvisioningDeps;
|
|
@@ -17,5 +18,6 @@ export declare function ensureVercelProject(input: {
|
|
|
17
18
|
prompter: Prompter;
|
|
18
19
|
signal?: AbortSignal;
|
|
19
20
|
teamSelectMessage?: (currentTeam: string) => string;
|
|
21
|
+
teamRequirement?: VercelTeamRequirement;
|
|
20
22
|
deps?: Partial<EnsureVercelProjectDeps>;
|
|
21
23
|
}): Promise<VercelProjectReference>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{interactiveAsker,withAnswers}from"../ask.js";import{readProjectLink}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{requireAuth}from"../vercel-project.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{WizardCancelledError}from"../step.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";import{runLoginFlow}from"./login.js";async function ensureVercelProject(e){let t=e.deps?.readProjectLink??readProjectLink,n=await(e.deps?.runLoginFlow??runLoginFlow)({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal,allowLogin:!1});if(n.kind===`cancelled`)throw new WizardCancelledError;n.kind!==`already`&&n.kind!==`logged-in`&&await(e.deps?.requireAuth??requireAuth)(e.appRoot,e.prompter,{signal:e.signal});let r=await t(e.appRoot);if(r!==void 0)return r
|
|
1
|
+
import{interactiveAsker,withAnswers}from"../ask.js";import{readProjectLink}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{checkTeamRequirement}from"../vercel-project-api.js";import{requireAuth}from"../vercel-project.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{WizardCancelledError}from"../step.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";import{runLoginFlow}from"./login.js";async function ensureVercelProject(e){let t=e.deps?.readProjectLink??readProjectLink,n=await(e.deps?.runLoginFlow??runLoginFlow)({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal,allowLogin:!1});if(n.kind===`cancelled`)throw new WizardCancelledError;n.kind!==`already`&&n.kind!==`logged-in`&&await(e.deps?.requireAuth??requireAuth)(e.appRoot,e.prompter,{signal:e.signal});let r=await t(e.appRoot);if(r!==void 0){if(e.teamRequirement){let t=await checkTeamRequirement(e.appRoot,r.orgId,e.teamRequirement,{signal:e.signal});if(t)throw Error(`Cannot configure the linked Vercel team. ${t} Run \`eve link\` to choose another team, or ask its owner to finish setup.`)}return r}let i=inProjectSetupState(e.appRoot,{kind:`unresolved`}),a=[resolveProvisioning({asker:withAnswers({deploy:`vercel`})(interactiveAsker(e.prompter)),prompter:e.prompter,targetDirectory:e.appRoot,mode:{headless:!1},adoptExistingLink:!1,projectSelection:`create-or-link`,teamSelectMessage:e.teamSelectMessage,teamRequirement:e.teamRequirement,deps:e.deps?.resolveProvisioning}),linkVercelProject({prompter:e.prompter,deps:e.deps?.linkProject})];if((await runInteractive(a,i,prompterSink(e.prompter),{snapshot:snapshotSetupState,signal:e.signal})).kind===`cancelled`)throw e.signal?.throwIfAborted(),new WizardCancelledError;let o=await t(e.appRoot);if(o===void 0)throw Error(`Vercel project linking failed.`);return o}export{ensureVercelProject};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z}from"#compiled/zod/index.js";const DiscordApplicationSchema=z.object({id:z.string(),name:z.string(),verify_key:z.string()});async function discordRequest(e,t,n,r){return r(`https://discord.com/api/v10${t}`,{...n,headers:{authorization:`Bot ${e}`,"content-type":`application/json`,...n.headers}})}async function resolveDiscordApplication(e,t=fetch){let n=await discordRequest(e,`/oauth2/applications/@me`,{method:`GET`},t);if(!n.ok)throw Error(`Discord rejected the bot token (HTTP ${n.status}).`);let r=DiscordApplicationSchema.safeParse(await n.json());if(!r.success)throw Error(`Discord returned incomplete application details.`);return{id:r.data.id,name:r.data.name,publicKey:r.data.verify_key}}async function registerDiscordCommand(e,t,n,r=fetch){let i=await discordRequest(t,`/applications/${encodeURIComponent(e)}/commands`,{method:`POST`,body:JSON.stringify({name:n.name,description:n.description,type:1,options:[{name:`message`,description:`What should the agent do?`,type:3,required:!0}]})},r);if(!i.ok)throw Error(`Discord command registration failed (HTTP ${i.status}).`)}async function configureDiscordInteractionsEndpoint(e,t,n=fetch){let r=await discordRequest(e,`/applications/@me`,{method:`PATCH`,body:JSON.stringify({interactions_endpoint_url:`https://connect.vercel.com/trigger/${encodeURIComponent(t)}`})},n);if(!r.ok)throw Error(`Discord rejected the interactions endpoint (HTTP ${r.status}).`)}export{configureDiscordInteractionsEndpoint,registerDiscordCommand,resolveDiscordApplication};
|
|
1
|
+
import{z}from"#compiled/zod/index.js";const DiscordApplicationSchema=z.object({id:z.string(),name:z.string(),verify_key:z.string()});async function discordRequest(e,t,n,r){return r(`https://discord.com/api/v10${t}`,{...n,headers:{authorization:`Bot ${e}`,"content-type":`application/json`,...n.headers}})}async function resolveDiscordApplication(e,t=fetch){let n=await discordRequest(e,`/oauth2/applications/@me`,{method:`GET`},t);if(!n.ok)throw Error(`Discord rejected the bot token (HTTP ${n.status}).`);let r=DiscordApplicationSchema.safeParse(await n.json());if(!r.success)throw Error(`Discord returned incomplete application details.`);return{id:r.data.id,name:r.data.name,publicKey:r.data.verify_key}}async function registerDiscordCommand(e,t,n,r=fetch){let i=await discordRequest(t,`/applications/${encodeURIComponent(e)}/commands`,{method:`POST`,body:JSON.stringify({name:n.name,description:n.description,type:1,options:[{name:`message`,description:`What should the agent do?`,type:3,required:!0},{name:`file`,description:`A file for the agent to read`,type:11,required:!1}]})},r);if(!i.ok)throw Error(`Discord command registration failed (HTTP ${i.status}).`)}async function configureDiscordInteractionsEndpoint(e,t,n=fetch){let r=await discordRequest(e,`/applications/@me`,{method:`PATCH`,body:JSON.stringify({interactions_endpoint_url:`https://connect.vercel.com/trigger/${encodeURIComponent(t)}`})},n);if(!r.ok)throw Error(`Discord rejected the interactions endpoint (HTTP ${r.status}).`)}export{configureDiscordInteractionsEndpoint,registerDiscordCommand,resolveDiscordApplication};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VercelTeamRequirement } from "#setup/vercel-project-api.js";
|
|
2
|
+
export declare const WEB_CHAT_TEAM_REQUIREMENT: VercelTeamRequirement;
|
|
3
|
+
export type WebAuthenticationChoice = "vercel" | "custom";
|
|
4
|
+
export declare const WEB_AUTHENTICATION_QUESTION: import("#setup/ask.js").Question<WebAuthenticationChoice>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{select}from"#setup/ask.js";const WEB_CHAT_TEAM_REQUIREMENT={permissions:{oauth2Application:[`create`,`update`],projectEnvVars:[`create`],projectEnvVarsProduction:[`create`]},disabledReason:`Web Chat sign-in requires app and environment permissions; ask a team owner.`},WEB_AUTHENTICATION_QUESTION=select({key:`web-authentication`,message:`How should people sign in to Web Chat?`,options:[{id:`vercel`,label:`Sign in with Vercel`,hint:`Create a Vercel App for members of the linked project's team.`,value:`vercel`},{id:`custom`,label:`Configure authentication myself`,hint:`Keep the current channel auth; the default rejects production browser requests.`,value:`custom`}],recommended:`vercel`,required:!0});export{WEB_AUTHENTICATION_QUESTION,WEB_CHAT_TEAM_REQUIREMENT};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Checks authored files before provisioning, then returns the local scaffold operation. */
|
|
2
|
+
export declare function prepareWebAuthScaffold(input: {
|
|
3
|
+
environmentRoot: string;
|
|
4
|
+
agentAppRoot: string;
|
|
5
|
+
webRoot?: string;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}): Promise<() => Promise<void>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile}from"node:fs/promises";import{basename,dirname,join,relative,sep}from"node:path";import{writeTextFile}from"#setup/scaffold/files.js";import{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_CHANNEL_TEMPLATES}from"#setup/scaffold/create/web-template.js";import{appendEnv}from"#setup/append-env.js";import{resolveWebPackageVersions}from"#setup/scaffold/update/web-options.js";async function readOptional(e){try{return await readFile(e,`utf8`)}catch(e){if(e.code===`ENOENT`)return;throw e}}async function prepareWebAuthScaffold(e){let t=e.webRoot??join(e.environmentRoot,`apps`,`web`),n=join(e.agentAppRoot,`agent`,`channels`,`eve.ts`),r=relative(dirname(n),join(t,`lib`,`auth.js`)).split(sep).join(`/`),i=WEB_CHANNEL_TEMPLATES[`sign-in-with-vercel`].replace(`"@/lib/auth"`,JSON.stringify(r.startsWith(`.`)?r:`./${r}`)),a=[...Object.entries(WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES).filter(([e])=>e!==`app/layout.tsx`).map(([n,r])=>({path:join(t,n),source:r.replaceAll(`__EVE_INIT_APP_NAME__`,()=>JSON.stringify(basename(e.agentAppRoot)).slice(1,-1)),previous:WEB_APP_TEMPLATE_FILES[n]})),{path:n,source:i,previous:WEB_CHANNEL_TEMPLATES.default}];for(let t of a){let n=await readOptional(t.path);if(!e.force&&n!==void 0&&n!==t.source&&n!==t.previous)throw Error(`Could not add Sign in with Vercel because ${t.path} contains authored code. Preserve your changes and integrate auth manually, or retry setup with --overwrite.`)}let o=join(e.environmentRoot,`package.json`);return async()=>{let t=JSON.parse(await readFile(o,`utf8`));t.dependencies={...t.dependencies,"better-auth":t.dependencies?.[`better-auth`]??resolveWebPackageVersions(void 0,`sign-in-with-vercel`).betterAuthPackageVersion};for(let e of a)await writeTextFile(e.path,e.source,{force:!0});await writeTextFile(o,`${JSON.stringify(t,null,2)}\n`,{force:!0}),await appendEnv(join(e.environmentRoot,`.env.example`),{VERCEL_APP_CLIENT_ID:``,VERCEL_APP_CLIENT_SECRET:``,BETTER_AUTH_SECRET:``})}}export{prepareWebAuthScaffold};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { VercelProjectReference } from "#setup/project-resolution.js";
|
|
2
|
+
/** Provisions team-only browser sign-in without rotating existing project credentials. */
|
|
3
|
+
export declare function provisionWebChatAuth(project: VercelProjectReference, signal?: AbortSignal): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomBytes}from"node:crypto";import{z}from"#compiled/zod/index.js";import{readVercelCliToken}from"#internal/model-auth/vercel-cli.js";const APP_CONFLICT_RECOVERY="Rename the Vercel project in its settings, then retry `eve add channel/web --skip-install`.",CALLBACK_PATH=`/api/auth/callback/vercel`,TARGETS=[`production`,`preview`],KEYS=[`VERCEL_APP_CLIENT_ID`,`VERCEL_APP_CLIENT_SECRET`,`BETTER_AUTH_SECRET`],AppSchema=z.object({clientId:z.string().min(1),teamId:z.string(),signInFrom:z.string().optional(),scopes:z.array(z.string()),grantTypes:z.record(z.string(),z.boolean()).optional(),clientAuthenticationMethods:z.record(z.string(),z.boolean()).optional(),projectRedirectUris:z.array(z.object({projectId:z.string(),path:z.string()})).optional(),clientSecrets:z.array(z.object({lastFourChars:z.string()})).optional()}),EnvSchema=z.object({id:z.string(),key:z.string(),value:z.string().optional(),target:z.union([z.string(),z.array(z.string())]).optional(),gitBranch:z.string().nullable().optional(),customEnvironmentIds:z.array(z.string()).optional()});var AppApiError=class extends Error{status;code;constructor(e,t){super(e===401||e===403?"Vercel denied Web Chat auth setup. Run `vercel login` and check that you can manage apps and environment variables in this team, or ask a team owner to run setup.":t===`app_limit_reached`?`This team has reached its Vercel App limit. Remove an unused app in team settings and retry.`:t===`app_name_taken`||t===`app_slug_taken`?`A Vercel App with this name or slug already exists. ${APP_CONFLICT_RECOVERY}`:"Vercel could not configure Web Chat authentication. Retry `eve add channel/web --skip-install`."),this.status=e,this.code=t}};function assertMatchingApp(e,t,n){if(e.teamId!==t.orgId||e.signInFrom!==`owning-team`||!e.grantTypes?.authorization_code||!e.clientAuthenticationMethods?.client_secret_post||![`openid`,`email`,`profile`].every(t=>e.scopes.includes(t))||!e.projectRedirectUris?.some(e=>e.projectId===t.projectId&&e.path===CALLBACK_PATH))throw Error(`The existing Vercel App does not match this project's team-only Web Chat settings. ${n?`Check VERCEL_APP_CLIENT_ID and the app in team settings before retrying.`:APP_CONFLICT_RECOVERY}`)}async function provisionWebChatAuth(e,t){let n=await readVercelCliToken();if(!n)throw Error("Run `vercel login` before setting up Sign in with Vercel.");let r=new URLSearchParams({teamId:e.orgId}),request=async(e,i=`GET`,a)=>{let o={method:i,headers:{authorization:`Bearer ${n}`,"content-type":`application/json`},redirect:`error`,signal:t?AbortSignal.any([t,AbortSignal.timeout(15e3)]):AbortSignal.timeout(15e3)};a!==void 0&&(o.body=JSON.stringify(a));let s=await fetch(`https://api.vercel.com${e}?${r}`,o).catch(()=>{throw t?.throwIfAborted(),new AppApiError(0)});if(!s.ok){let e=z.object({error:z.object({code:z.string().optional()})}).safeParse(await s.json().catch(()=>void 0));throw new AppApiError(s.status,e.success?e.data.error.code:void 0)}if(s.status!==204)return s.json().catch(()=>{throw new AppApiError(s.status)})},getApp=async e=>{try{return z.object({app:AppSchema}).parse(await request(`/oauth-apps/${encodeURIComponent(e)}`)).app}catch(e){if(e instanceof AppApiError&&e.code===`invalid_client`)return;throw e}},i=`/v9/projects/${encodeURIComponent(e.projectId)}`,a=z.object({id:z.string(),accountId:z.string(),name:z.string()}).parse(await request(i));if(a.id!==e.projectId||a.accountId!==e.orgId)throw Error(`The linked Vercel project is not available in the selected team.`);let o=z.object({envs:z.array(EnvSchema)}).parse(await request(`${i}/env`)).envs.filter(e=>!e.gitBranch&&!e.customEnvironmentIds?.length),forTarget=(e,t)=>o.find(n=>n.key===e&&(typeof n.target==`string`?[n.target]:n.target??[]).includes(t)),s=new Set(TARGETS.map(e=>forTarget(KEYS[0],e)?.value).filter(Boolean));if(s.size>1||TARGETS.some(e=>(forTarget(KEYS[1],e)||forTarget(KEYS[0],e))&&!forTarget(KEYS[0],e)?.value))throw Error(`Web Chat has conflicting or incomplete Vercel App credentials. Check production and preview environment variables before retrying.`);let c=e.projectId.replace(/^prj_/,``).toLowerCase(),l=`${a.name.slice(0,100)}-web-chat-${c}`.toLowerCase().replace(/[^a-z0-9-]/g,`-`),u=[...s][0],d=await getApp(u??l);if(!d&&u)throw Error(`The configured Vercel App is unavailable. Check VERCEL_APP_CLIENT_ID before retrying.`);if(!d)try{d=AppSchema.parse(await request(`/oauth-apps`,`POST`,{name:`${a.name.slice(0,100)} Web Chat ${c}`,slug:l,projectRedirectUris:[{projectId:e.projectId,path:CALLBACK_PATH}],scopes:[`openid`,`email`,`profile`],grantTypes:{authorization_code:!0},clientAuthenticationMethods:{client_secret_post:!0},signInFrom:`owning-team`}))}catch(e){if(!(e instanceof AppApiError)||(t?.throwIfAborted(),d=await getApp(l).catch(()=>{throw t?.throwIfAborted(),e}),!d))throw e}assertMatchingApp(d,e,u!==void 0);let missingTargets=e=>TARGETS.filter(t=>!forTarget(e,t));if(KEYS.every(e=>missingTargets(e).length===0))return;let f=missingTargets(KEYS[1]);if(f.length&&(d.clientSecrets?.length??0)>=2)throw Error(`This Vercel App has reached its client secret limit. Remove an unused secret in team settings and retry.`);let p=f.length?z.object({clientSecret:z.string().min(4)}).parse(await request(`/v1/apps/${encodeURIComponent(d.clientId)}/secrets`,`POST`,{})).clientSecret:void 0,m={VERCEL_APP_CLIENT_ID:d.clientId,VERCEL_APP_CLIENT_SECRET:p,BETTER_AUTH_SECRET:randomBytes(32).toString(`base64url`)},rollback=async e=>{let t=await Promise.allSettled(e.map(e=>request(`${i}/env/${encodeURIComponent(e.id)}`,`DELETE`))),n=t.some((t,n)=>t.status===`rejected`&&e[n]?.key===KEYS[1]);if(p&&!n&&t.push(...await Promise.allSettled([request(`/oauth-apps/${encodeURIComponent(d.clientId)}/secret/${encodeURIComponent(p.slice(-4))}`,`DELETE`)])),t.some(e=>e.status===`rejected`))throw Error(`Could not save Web Chat auth environment variables. Cleanup was incomplete. Check the project's environment variables and Vercel App client secrets in team settings before retrying.`)},h=z.object({created:z.array(EnvSchema),failed:z.array(z.unknown())}).parse(await request(`/v10/projects/${encodeURIComponent(e.projectId)}/env`,`POST`,KEYS.filter(e=>missingTargets(e).length).map(e=>({key:e,value:m[e],target:missingTargets(e),type:e===KEYS[0]?`plain`:`sensitive`,visibility:e===KEYS[0]?`config`:`secret`}))).catch(async e=>{throw e instanceof AppApiError&&e.status>=400&&e.status<500&&e.status!==408&&e.status!==499&&await rollback([]),e}));if(h.failed.length!==0)throw await rollback(h.created),Error("Could not save Web Chat auth environment variables. Retry `eve add channel/web --skip-install`.")}export{provisionWebChatAuth};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { resolveEveProjectContext } from "#internal/project-context.js";
|
|
2
|
+
import type { RegistrySetupCompletion } from "#setup/registry-setup-protocol.js";
|
|
3
|
+
import type { VercelProjectReference } from "#setup/project-resolution.js";
|
|
4
|
+
import { installScaffoldDependencies } from "../shared/scaffold.js";
|
|
5
|
+
import { prepareWebAuthScaffold } from "./auth-scaffold.js";
|
|
6
|
+
import { provisionWebChatAuth } from "./provision-auth.js";
|
|
2
7
|
import { detectPackageManager, type PackageManagerKind } from "#setup/package-manager.js";
|
|
3
8
|
import { pathExists, writeTextFile } from "#setup/scaffold/files.js";
|
|
4
9
|
import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
|
|
@@ -8,17 +13,17 @@ export interface WebSetupDeps {
|
|
|
8
13
|
readTextFile(path: string): Promise<string>;
|
|
9
14
|
resolveEveProjectContext: typeof resolveEveProjectContext;
|
|
10
15
|
writeTextFile: typeof writeTextFile;
|
|
16
|
+
prepareWebAuthScaffold: typeof prepareWebAuthScaffold;
|
|
17
|
+
provisionWebChatAuth: typeof provisionWebChatAuth;
|
|
18
|
+
installScaffoldDependencies: typeof installScaffoldDependencies;
|
|
11
19
|
}
|
|
12
20
|
interface WebSetupPlan {
|
|
13
21
|
hosting: "next" | "vercel";
|
|
14
22
|
packageManager: PackageManagerKind;
|
|
23
|
+
authProject?: VercelProjectReference;
|
|
24
|
+
rootWebChat?: boolean;
|
|
15
25
|
}
|
|
16
26
|
export declare function prepareWebSetup(context: SetupPrepareContext, deps?: WebSetupDeps): Promise<WebSetupPlan>;
|
|
17
|
-
export declare function applyWebSetup(plan: WebSetupPlan, context: SetupApplyContext, deps?: WebSetupDeps): Promise<
|
|
18
|
-
facts: {
|
|
19
|
-
label: string;
|
|
20
|
-
value: string;
|
|
21
|
-
}[];
|
|
22
|
-
}>;
|
|
27
|
+
export declare function applyWebSetup(plan: WebSetupPlan, context: SetupApplyContext, deps?: WebSetupDeps): Promise<RegistrySetupCompletion>;
|
|
23
28
|
export declare const WEB_SETUP: import("../types.js").SetupIntegration;
|
|
24
29
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{defineSetupIntegration}from"../types.js";import{readFile}from"node:fs/promises";import{join}from"node:path";import{resolveEveProjectContext}from"#internal/project-context.js";import{detectPackageManager}from"#setup/package-manager.js";import{pathExists,writeTextFile}from"#setup/scaffold/files.js";import{select}from"#setup/ask.js";import{WEB_CHANNEL_TEMPLATES}from"#setup/scaffold/create/web-template.js";const NEXT_HOSTED_CONFIG=`import type { NextConfig } from "next";
|
|
1
|
+
import{defineSetupIntegration}from"../types.js";import{installScaffoldDependencies}from"../shared/scaffold.js";import{prepareWebAuthScaffold}from"./auth-scaffold.js";import{WEB_AUTHENTICATION_QUESTION}from"./auth-options.js";import{provisionWebChatAuth}from"./provision-auth.js";import{readFile}from"node:fs/promises";import{join}from"node:path";import{resolveEveProjectContext}from"#internal/project-context.js";import{detectPackageManager}from"#setup/package-manager.js";import{pathExists,writeTextFile}from"#setup/scaffold/files.js";import{select}from"#setup/ask.js";import{WEB_CHANNEL_TEMPLATES}from"#setup/scaffold/create/web-template.js";const NEXT_HOSTED_CONFIG=`import type { NextConfig } from "next";
|
|
2
2
|
import { withEve } from "eve/next";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
@@ -25,10 +25,10 @@ export default await withEve({
|
|
|
25
25
|
{ src: "^(.*)$", destination: { type: "service", service: "web" } },
|
|
26
26
|
],
|
|
27
27
|
});
|
|
28
|
-
`,defaultDeps={detectPackageManager,pathExists,readTextFile:e=>readFile(e,`utf8`),resolveEveProjectContext,writeTextFile};async function prepareWebSetup(e,t=defaultDeps){let n=await t.resolveEveProjectContext(e.appRoot);if(n.kind===`workspace`)throw Error(`Web Chat setup requires a selected workspace agent.`);
|
|
28
|
+
`,defaultDeps={detectPackageManager,pathExists,readTextFile:e=>readFile(e,`utf8`),resolveEveProjectContext,writeTextFile,prepareWebAuthScaffold,provisionWebChatAuth,installScaffoldDependencies};async function prepareWebSetup(e,t=defaultDeps){let n=await t.resolveEveProjectContext(e.appRoot);if(n.kind===`workspace`)throw Error(`Web Chat setup requires a selected workspace agent.`);let r=await t.pathExists(join(n.environmentRoot,`app`,`eve-agent.ts`))&&!await t.pathExists(join(n.environmentRoot,`apps`,`web`,`app`,`eve-agent.ts`)),i=r?`next`:await e.asker.ask(select({key:`web-hosting`,message:`How should Web Chat and your agents be deployed?`,options:[{id:`vercel`,label:`Vercel services`,hint:`(Recommended) Web Chat and agents deploy as separate services.`,value:`vercel`},{id:`next`,label:`Next.js`,hint:`One Next.js app serves Web Chat and routes agent requests.`,value:`next`}],recommended:`vercel`,required:!0})),a=await e.asker.ask(WEB_AUTHENTICATION_QUESTION)===`vercel`?await e.resolveVercelProject(`Web Chat sign-in`):void 0,o={hosting:i,packageManager:(await t.detectPackageManager(n.environmentRoot)).kind};return a!==void 0&&(o.authProject=a),r&&(o.rootWebChat=!0),o}function runScriptCommand(e,t){switch(e){case`npm`:return`npm run ${t}`;case`pnpm`:return`pnpm ${t}`;case`yarn`:return`yarn ${t}`;case`bun`:return`bun run ${t}`}}async function configurePeerServiceScripts(e,t){let n=join(e,`package.json`),r=JSON.parse(await t.readTextFile(n)),i={...r.scripts};i.dev??=`eve dev`,i[`dev:eve`]??=`eve dev`,i[`dev:all`]??=`vercel dev --local`,await t.writeTextFile(n,`${JSON.stringify({...r,scripts:i},null,2)}\n`,{force:!0})}async function assertInstallerOwned(e,t){try{let n=await readFile(e,`utf8`);if(t.includes(n))return}catch(e){if(e.code===`ENOENT`)return;throw e}throw Error(`Could not configure Web Chat because ${e} contains authored configuration. Preserve it and compose the eve integration manually.`)}async function applyWebSetup(e,t,n=defaultDeps){let r=await n.resolveEveProjectContext(t.appRoot);if(r.kind===`workspace`)throw Error(`Web Chat setup requires a selected workspace agent.`);let i=r.kind===`workspace-member`?r.member.appRoot:r.appRoot,a=join(i,`agent`,`channels`,`eve.ts`),o=e.rootWebChat?r.environmentRoot:join(r.environmentRoot,`apps`,`web`),s=e.authProject===void 0?void 0:await n.prepareWebAuthScaffold({environmentRoot:r.environmentRoot,agentAppRoot:i,webRoot:o,force:t.force});s===void 0&&(t.force||!await n.pathExists(a))&&await n.writeTextFile(a,WEB_CHANNEL_TEMPLATES.default,{force:t.force});let c=r.kind===`workspace-member`?r.member.name:void 0;await n.writeTextFile(join(o,`app`,`eve-agent.ts`),`/** Named workspace agent selected by the Web Chat installer. */\nexport const WEB_CHAT_AGENT: string | undefined = ${c===void 0?`undefined`:JSON.stringify(c)};\n`,{force:!0});let l=join(o,`next.config.ts`),u=`import type { NextConfig } from "next";
|
|
29
29
|
import { withEve } from "eve/next";
|
|
30
30
|
|
|
31
31
|
const nextConfig: NextConfig = {};
|
|
32
32
|
|
|
33
33
|
export default withEve(nextConfig);
|
|
34
|
-
|
|
34
|
+
`;await assertInstallerOwned(l,[u,NEXT_HOSTED_CONFIG,PEER_SERVICE_NEXT_CONFIG]);let d;if(e.hosting===`vercel`){let e=join(r.environmentRoot,`vercel.ts`),t=join(r.environmentRoot,`vercel.json`);if(await assertInstallerOwned(e,[PEER_SERVICE_VERCEL_CONFIG,PEER_SERVICE_VERCEL_CONFIG.replace(/ web: \{[^}]+\},/,` web: { framework: "nextjs", root: "apps/web" },`)]),await n.pathExists(t))throw Error(`Could not configure Vercel services because ${t} already exists. Preserve it and compose eve/vercel manually.`);await n.writeTextFile(l,PEER_SERVICE_NEXT_CONFIG,{force:!0}),await n.writeTextFile(e,PEER_SERVICE_VERCEL_CONFIG,{force:!0}),await configurePeerServiceScripts(r.environmentRoot,n),d=`dev:all`}else await n.writeTextFile(l,e.rootWebChat?u:NEXT_HOSTED_CONFIG,{force:!0}),d=e.rootWebChat?`dev`:`dev:web`;e.authProject!==void 0&&s!==void 0&&(await n.provisionWebChatAuth(e.authProject,t.signal),t.signal?.throwIfAborted(),await s(),await n.installScaffoldDependencies({changed:!0,log:t.presenter.log,projectPath:r.environmentRoot,signal:t.signal}),t.presenter.log.success(`Configured Sign in with Vercel for this project's team`),t.presenter.nextSteps(["Local setup is complete. Run `eve deploy` to publish these changes. Production and preview credentials are configured.",`Local development continues to use localDev() without signing in.`])),t.presenter.log.success(`Configured channel: web`);let f={facts:[{label:``,value:`Start locally with \`${runScriptCommand(e.packageManager,d)}\`.`}]};return e.authProject!==void 0&&(f.deploymentRequired=!0),f}const WEB_SETUP=defineSetupIntegration({kind:`web`,label:`Web Chat`,hint:`Browser-based chat interface`,prepare:prepareWebSetup,apply:applyWebSetup});export{WEB_SETUP,applyWebSetup,prepareWebSetup};
|
|
@@ -93,6 +93,8 @@ export interface SelectCommonOptions<T extends PrompterValue> {
|
|
|
93
93
|
message: string;
|
|
94
94
|
/** Inert context rendered beneath the question heading and above its options. */
|
|
95
95
|
description?: string;
|
|
96
|
+
/** Shared guidance rendered below the options and above keyboard hints. */
|
|
97
|
+
helpText?: string;
|
|
96
98
|
/** Labeled facts rendered beneath the description and above the options. */
|
|
97
99
|
metadata?: readonly SelectMetadata[];
|
|
98
100
|
options: SelectOption<T>[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{WizardCancelledError}from"./step.js";import{initialQuitGuardState,quitHintNote,reduceQuitGuard}from"./quit-guard.js";import pc from"#compiled/picocolors/index.js";import{cornerFor,createRailLog,formatPromptCancellation,formatPromptHeader,formatPromptOpener,formatPromptOutro,formatPromptSubmission,formatRailLine,railFor,runSelectComponent}from"#setup/cli/index.js";import{TextPrompt,isCancel,settings}from"#compiled/@clack/core/index.js";var PlannerNavigationError=class extends Error{direction;values;constructor(e,t){super(`Planner requested ${e} navigation.`),this.name=`PlannerNavigationError`,this.direction=e,this.values=t}};function guardCancel(e){if(isCancel(e))throw process.stdout.write(formatPromptCancellation(`Setup cancelled.`,pc)),new WizardCancelledError;return e}let escapeCancelDisabled=!1;function disableEscapeCancel(){escapeCancelDisabled||=(settings.aliases.delete(`escape`),!0)}function attachQuitGuard(e){let t=initialQuitGuardState;return e.on(`key`,(n,r)=>{let{state:i,action:a}=reduceQuitGuard(t,r?.name===`escape`?{type:`escape`}:{type:`other-key`});t=i,a===`quit`&&(e.state=`cancel`)}),{note:()=>quitHintNote(t,pc)}}function toPromptState(e){return e}function railForState(e){return railFor(toPromptState(e),pc)}function cornerForState(e){return cornerFor(toPromptState(e),pc)}function header(e,t,n){return formatPromptHeader(toPromptState(e),t,{colors:pc,leadingRail:n===0?`white`:`green`})}const REDACTED_DISPLAY=`••••••••`;function textPrompt(e,t){let n,r=new TextPrompt({validate:t.validate?e=>t.validate?.(e??``):void 0,placeholder:t.placeholder,defaultValue:t.defaultValue,initialValue:t.defaultValue,render(){let r=header(this.state,t.message,e),i=t.placeholder?pc.inverse(t.placeholder[0])+pc.dim(t.placeholder.slice(1)):pc.inverse(pc.hidden(`_`)),a=this.value?`•`.repeat(this.value.length):i,o=i;switch(t.mask?o=a:this.value&&(o=this.userInputWithCursor),this.state){case`error`:return`${r.trim()}\n${railForState(this.state)} ${o}\n${cornerForState(this.state)} ${pc.red(this.error)}\n`;case`submit`:{let n=this.value||t.placeholder||``;return t.mask&&(n=this.value?REDACTED_DISPLAY:t.placeholder??``),formatPromptSubmission(this.state,t.message,n,{colors:pc,leadingRail:e===0?`white`:`green`})}case`cancel`:return`${r}${railForState(this.state)} ${pc.strikethrough(pc.dim(t.mask&&this.value?REDACTED_DISPLAY:this.value??``))}${this.value?.trim()?`\n${railForState(this.state)}`:``}`;default:return`${r}${railForState(this.state)} ${o}\n${cornerForState(this.state)}${cornerNote(n?.note())}\n`}}});return n=attachQuitGuard(r),r.prompt()}function cornerNote(e){return e?` ${e}`:``}function createPrompter(){disableEscapeCancel();let e=0,t=createRailLog({colors:pc,output:process.stdout});function printNotices(e){for(let n of e??[])n.tone===`success`?t.success(n.text):n.tone===`warning`?t.warning(n.text):n.tone===`error`?t.error(n.text):t.message(n.text)}return{async text(n){t.settle(),printNotices(n.notices);let r=guardCancel(await textPrompt(e,n));return e+=1,r??``},async password(n){t.settle();let r=guardCancel(await textPrompt(e,{message:n.message,mask:!0,validate:n.validate}));return e+=1,r??``},async select(n){t.settle(),printNotices(n.notices),n.description!==void 0&&process.stdout.write(formatRailLine(pc.dim(n.description),pc,process.stdout));for(let{label:e,value:t}of n.metadata??[])process.stdout.write(formatRailLine(`${pc.dim(`${e}:`)} ${t}`,pc,process.stdout));let r=guardCancel(await runSelectComponent({message:n.message,options:n.options,multiple:n.multiple===!0,search:n.search??!1,searchAction:n.multiple===!0?void 0:n.searchAction,required:n.required??!1,placeholder:n.placeholder,defaultValue:n.multiple===!0?void 0:n.initialValue,initialValues:n.multiple===!0?n.initialValues:void 0,leadingRail:e===0?`white`:`green`,attachGuard:e=>attachQuitGuard(e)}));return e+=1,r},async acknowledge(e){t.settle(),process.stdout.write(formatRailLine(pc.bold(e.message),pc,process.stdout));for(let t of e.lines??[])process.stdout.write(formatRailLine(pc.dim(t),pc,process.stdout))},note(e,n,r){t.settle();let i=r?.tone===`success`?pc.green:pc.red;n&&process.stdout.write(formatRailLine(i(pc.bold(n)),pc,process.stdout)),process.stdout.write(formatRailLine(i(e),pc,process.stdout))},intro(e,n=`Production-grade agent framework.`){t.settle(),process.stdout.write(formatPromptOpener(e,n,pc))},outro(e){t.settle(),process.stdout.write(formatPromptOutro(e,pc))},log:t}}export{PlannerNavigationError,createPrompter};
|
|
1
|
+
import{WizardCancelledError}from"./step.js";import{initialQuitGuardState,quitHintNote,reduceQuitGuard}from"./quit-guard.js";import pc from"#compiled/picocolors/index.js";import{cornerFor,createRailLog,formatPromptCancellation,formatPromptHeader,formatPromptOpener,formatPromptOutro,formatPromptSubmission,formatRailLine,railFor,runSelectComponent}from"#setup/cli/index.js";import{TextPrompt,isCancel,settings}from"#compiled/@clack/core/index.js";var PlannerNavigationError=class extends Error{direction;values;constructor(e,t){super(`Planner requested ${e} navigation.`),this.name=`PlannerNavigationError`,this.direction=e,this.values=t}};function guardCancel(e){if(isCancel(e))throw process.stdout.write(formatPromptCancellation(`Setup cancelled.`,pc)),new WizardCancelledError;return e}let escapeCancelDisabled=!1;function disableEscapeCancel(){escapeCancelDisabled||=(settings.aliases.delete(`escape`),!0)}function attachQuitGuard(e){let t=initialQuitGuardState;return e.on(`key`,(n,r)=>{let{state:i,action:a}=reduceQuitGuard(t,r?.name===`escape`?{type:`escape`}:{type:`other-key`});t=i,a===`quit`&&(e.state=`cancel`)}),{note:()=>quitHintNote(t,pc)}}function toPromptState(e){return e}function railForState(e){return railFor(toPromptState(e),pc)}function cornerForState(e){return cornerFor(toPromptState(e),pc)}function header(e,t,n){return formatPromptHeader(toPromptState(e),t,{colors:pc,leadingRail:n===0?`white`:`green`})}const REDACTED_DISPLAY=`••••••••`;function textPrompt(e,t){let n,r=new TextPrompt({validate:t.validate?e=>t.validate?.(e??``):void 0,placeholder:t.placeholder,defaultValue:t.defaultValue,initialValue:t.defaultValue,render(){let r=header(this.state,t.message,e),i=t.placeholder?pc.inverse(t.placeholder[0])+pc.dim(t.placeholder.slice(1)):pc.inverse(pc.hidden(`_`)),a=this.value?`•`.repeat(this.value.length):i,o=i;switch(t.mask?o=a:this.value&&(o=this.userInputWithCursor),this.state){case`error`:return`${r.trim()}\n${railForState(this.state)} ${o}\n${cornerForState(this.state)} ${pc.red(this.error)}\n`;case`submit`:{let n=this.value||t.placeholder||``;return t.mask&&(n=this.value?REDACTED_DISPLAY:t.placeholder??``),formatPromptSubmission(this.state,t.message,n,{colors:pc,leadingRail:e===0?`white`:`green`})}case`cancel`:return`${r}${railForState(this.state)} ${pc.strikethrough(pc.dim(t.mask&&this.value?REDACTED_DISPLAY:this.value??``))}${this.value?.trim()?`\n${railForState(this.state)}`:``}`;default:return`${r}${railForState(this.state)} ${o}\n${cornerForState(this.state)}${cornerNote(n?.note())}\n`}}});return n=attachQuitGuard(r),r.prompt()}function cornerNote(e){return e?` ${e}`:``}function createPrompter(){disableEscapeCancel();let e=0,t=createRailLog({colors:pc,output:process.stdout});function printNotices(e){for(let n of e??[])n.tone===`success`?t.success(n.text):n.tone===`warning`?t.warning(n.text):n.tone===`error`?t.error(n.text):t.message(n.text)}return{async text(n){t.settle(),printNotices(n.notices);let r=guardCancel(await textPrompt(e,n));return e+=1,r??``},async password(n){t.settle();let r=guardCancel(await textPrompt(e,{message:n.message,mask:!0,validate:n.validate}));return e+=1,r??``},async select(n){t.settle(),printNotices(n.notices),n.description!==void 0&&process.stdout.write(formatRailLine(pc.dim(n.description),pc,process.stdout));for(let{label:e,value:t}of n.metadata??[])process.stdout.write(formatRailLine(`${pc.dim(`${e}:`)} ${t}`,pc,process.stdout));let r=guardCancel(await runSelectComponent({message:n.message,helpText:n.helpText,options:n.options,multiple:n.multiple===!0,search:n.search??!1,searchAction:n.multiple===!0?void 0:n.searchAction,required:n.required??!1,placeholder:n.placeholder,defaultValue:n.multiple===!0?void 0:n.initialValue,initialValues:n.multiple===!0?n.initialValues:void 0,leadingRail:e===0?`white`:`green`,attachGuard:e=>attachQuitGuard(e)}));return e+=1,r},async acknowledge(e){t.settle(),process.stdout.write(formatRailLine(pc.bold(e.message),pc,process.stdout));for(let t of e.lines??[])process.stdout.write(formatRailLine(pc.dim(t),pc,process.stdout))},note(e,n,r){t.settle();let i=r?.tone===`success`?pc.green:pc.red;n&&process.stdout.write(formatRailLine(i(pc.bold(n)),pc,process.stdout)),process.stdout.write(formatRailLine(i(e),pc,process.stdout))},intro(e,n=`Production-grade agent framework.`){t.settle(),process.stdout.write(formatPromptOpener(e,n,pc))},outro(e){t.settle(),process.stdout.write(formatPromptOutro(e,pc))},log:t}}export{PlannerNavigationError,createPrompter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"../update/package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{readFile}from"node:fs/promises";import{join}from"node:path";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(e){let t=e.packageManager??`pnpm`,n=join(e.projectRoot,`package.json`);if(!await pathExists(n))throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let r;try{r=JSON.parse(await readFile(n,`utf8`))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Cannot add an eve agent because "${n}" is not valid JSON. No files were changed. Fix the file, then retry eve init. ${t}`)}let i=agentTemplateFiles(e.model,e.reasoning),a=[];for(let t of Object.keys(i))await pathExists(join(e.projectRoot,t))&&a.push(t);if(a.length===0&&await pathExists(join(e.projectRoot,`agent`))&&a.push(`agent/`),a.length>0)throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it already has: ${a.join(`, `)}.`);let o=resolveEvePackageContract(e.evePackage),s=resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.
|
|
1
|
+
import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"../update/package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{readFile}from"node:fs/promises";import{join}from"node:path";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(e){let t=e.packageManager??`pnpm`,n=join(e.projectRoot,`package.json`);if(!await pathExists(n))throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let r;try{r=JSON.parse(await readFile(n,`utf8`))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Cannot add an eve agent because "${n}" is not valid JSON. No files were changed. Fix the file, then retry eve init. ${t}`)}let i=agentTemplateFiles(e.model,e.reasoning),a=[];for(let t of Object.keys(i))await pathExists(join(e.projectRoot,t))&&a.push(t);if(a.length===0&&await pathExists(join(e.projectRoot,`agent`))&&a.push(`agent/`),a.length>0)throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it already has: ${a.join(`, `)}.`);let o=resolveEvePackageContract(e.evePackage),s=resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.128`),c=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`),l=resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),u=[];for(let[t,n]of Object.entries(i)){let r=join(e.projectRoot,t);await writeTextFile(r,n),u.push(r)}let d={"@vercel/connect":c,ai:s,eve:formatEveDependencySpecifier(o.version),zod:l},f={};for(let[e,t]of Object.entries(d))hasDeclaredDependency(r,e)||(f[e]=t);let p={};Object.keys(f).length>0&&(p.dependencies=f),isPackageManagerWorkspaceMember(t,e.projectRoot)||(p.nodeEngineRequirement=o.nodeEngine);let m=await patchPackageJson(n,p),h=await patchWorkspaceRootPackageJson(t,e.projectRoot,{nodeEngineRequirement:o.nodeEngine}),g=h.nodeEngineOverride??m.nodeEngineOverride,_=await applyPackageManagerWorkspaceConfiguration({packageManager:t,projectRoot:e.projectRoot});return{filesWritten:u,dependenciesAdded:Object.keys(f).sort(),nodeEngineOverride:g,configurationFilesChanged:[...m.changed?[n]:[],...h.changed&&h.path!==void 0?[h.path]:[],..._.filesWritten]}}export{addAgentToProject};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{DEFAULT_CONNECT_PACKAGE_VERSION,resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_CHANNEL_TEMPLATES}from"./web-template.js";import{AGENT_INSTRUCTIONS_TEMPLATE}from"./instructions-template.js";import{SCAFFOLDED_AGENT_PATHS}from"./agent-paths.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{DEFAULT_CONNECT_PACKAGE_VERSION,resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_CHANNEL_TEMPLATES}from"./web-template.js";import{AGENT_INSTRUCTIONS_TEMPLATE}from"./instructions-template.js";import{SCAFFOLDED_AGENT_PATHS}from"./agent-paths.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.128`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.72.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e,t){return{[SCAFFOLDED_AGENT_PATHS.config]:renderAgentTemplate(e,t),[SCAFFOLDED_AGENT_PATHS.channel]:WEB_CHANNEL_TEMPLATES.default,[SCAFFOLDED_AGENT_PATHS.instructions]:AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,t){let n=parseChatGptModelSelection(e);return n===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(n)}),\n${reasoningTemplateLine(t)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,t){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(t.model)!==void 0?renderAgentTemplate(t.model,t.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -114,4 +114,4 @@ __EVE_INIT_REASONING__ modelOptions: {
|
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
116
|
});
|
|
117
|
-
`:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(e.includeRootOnlyPackageJsonFields)}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let n=await readdir(e),r=blockingCreateInPlaceEntries(n);if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let t=resolve(e.targetDirectory??process.cwd(),e.projectName),n=e.projectName===`.`,r=e.overwriteExisting??!1,i=e.byokProvider??!1,a=e.packageManager??`pnpm`,o=resolveEvePackageContract(e.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c=resolve(e.workspaceProbeDirectory??t),l=isPackageManagerWorkspaceMember(a,c);if(n)await assertCanCreateInPlace(t,r);else if(await pathExists(t))throw Error(`Cannot create project because "${t}" already exists.`);let u={appName:basename(t),model:e.model,reasoning:e.reasoning,eveVersion:o.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.
|
|
117
|
+
`:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(e.includeRootOnlyPackageJsonFields)}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let n=await readdir(e),r=blockingCreateInPlaceEntries(n);if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let t=resolve(e.targetDirectory??process.cwd(),e.projectName),n=e.projectName===`.`,r=e.overwriteExisting??!1,i=e.byokProvider??!1,a=e.packageManager??`pnpm`,o=resolveEvePackageContract(e.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c=resolve(e.workspaceProbeDirectory??t),l=isPackageManagerWorkspaceMember(a,c);if(n)await assertCanCreateInPlace(t,r);else if(await pathExists(t))throw Error(`Cannot create project because "${t}" already exists.`);let u={appName:basename(t),model:e.model,reasoning:e.reasoning,eveVersion:o.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.128`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,e.typescriptPackageVersion??`7.0.2`),nodeTypesVersion:s,nodeEngine:s};await mkdir(t,{recursive:!0});for(let[a,o]of Object.entries(templateFiles({byokProvider:i,includeRootOnlyPackageJsonFields:!l}))){let i=`${t}/${a}`,s=await pathExists(i);await writeTextFile(i,renderTemplate(o,u),{force:n&&r}),s&&await e.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:a,projectRoot:t,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(a,c,{nodeEngineRequirement:o.nodeEngine,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),t}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const WEB_APP_TEMPLATE_FILES: {
|
|
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 // Answers are accepted while work runs; the server\'s settlement closes each request.\n const canRespond = (requestId: string) =>\n !isResuming && agent.data.inputs[requestId]?.status === "open";\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={canRespond}\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';
|
|
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: (requestId: string) => 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: (requestId: string) => 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(inputRequest.requestId)}\n inputRequest={inputRequest}\n inputResponse={part.toolMetadata?.eve?.inputResponse}\n onInputResponses={onInputResponses}\n />\n );\n }\n\n return (\n <>\n <Tool>\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\n errorText={part.errorText}\n input={part.input}\n output={part.output}\n />\n ) : (\n <>\n <ToolInput input={part.input} />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </>\n )}\n </ToolContent>\n </Tool>\n {/* Kept outside the collapsed card so a pending approval is always visible. */}\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n </>\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: (requestId: string) => 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 // An approval can settle before its batch resolves, without an input response.\n const settledApproval =\n part.approval?.approved === undefined\n ? undefined\n : part.approval.approved\n ? "Approved"\n : "Denied";\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 || settledApproval ? (\n <p className="font-medium text-sm">\n Responded:{" "}\n {selectedOption?.label ??\n inputResponse?.text ??\n inputResponse?.optionId ??\n settledApproval}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond(inputRequest.requestId)}\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 part.attemptId === undefined\n ? `authorization:${part.turnId}:${part.stepIndex}:${part.name}`\n : `authorization:${part.attemptId}`;\n case "dynamic-tool":\n return part.toolCallId;\n case "reasoning":\n case "text":\n return `${part.type}:${part.id ?? index}`;\n default:\n return `${part.type}:${index}`;\n }\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 // Answers are accepted while work runs; the server\'s settlement closes each request.\n const canRespond = (requestId: string) =>\n !isResuming && agent.data.inputs[requestId]?.status === "open";\n // A tool call can ask several questions at once, and its message part holds only the latest.\n const questionsFor = (callId: string) =>\n Object.values(agent.data.inputs).filter(\n (input) => input.request.kind === "question" && input.request.action.callId === callId,\n );\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={canRespond}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n questionsFor={questionsFor}\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';
|
|
3
|
+
readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n ConversationInput,\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 questionsFor,\n}: {\n readonly canRespond: (requestId: string) => boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly questionsFor: (callId: string) => readonly ConversationInput[];\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 questionsFor={questionsFor}\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 questionsFor,\n showCaret,\n}: {\n readonly canRespond: (requestId: string) => boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly questionsFor: (callId: string) => readonly ConversationInput[];\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 questions = questionsFor(part.toolCallId);\n if (questions.length > 0) {\n return (\n <div className="space-y-4">\n {questions.map(({ request, response }) => (\n <QuestionRequest\n canRespond={canRespond(request.requestId)}\n inputRequest={request}\n inputResponse={response}\n key={request.requestId}\n onInputResponses={onInputResponses}\n />\n ))}\n </div>\n );\n }\n\n return (\n <>\n <Tool>\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\n errorText={part.errorText}\n input={part.input}\n output={part.output}\n />\n ) : (\n <>\n <ToolInput input={part.input} />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </>\n )}\n </ToolContent>\n </Tool>\n {/* Kept outside the collapsed card so a pending approval is always visible. */}\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n </>\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: (requestId: string) => 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 // An approval can settle before its batch resolves, without an input response.\n const settledApproval =\n part.approval?.approved === undefined\n ? undefined\n : part.approval.approved\n ? "Approved"\n : "Denied";\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 || settledApproval ? (\n <p className="font-medium text-sm">\n Responded:{" "}\n {selectedOption?.label ??\n inputResponse?.text ??\n inputResponse?.optionId ??\n settledApproval}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond(inputRequest.requestId)}\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 part.attemptId === undefined\n ? `authorization:${part.turnId}:${part.stepIndex}:${part.name}`\n : `authorization:${part.attemptId}`;\n case "dynamic-tool":\n return part.toolCallId;\n case "reasoning":\n case "text":\n return `${part.type}:${part.id ?? index}`;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
|
|
4
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
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';
|
|
@@ -55,7 +55,7 @@ export declare const WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES: {
|
|
|
55
55
|
readonly "app/s/[sessionId]/page.tsx": 'import { AuthenticatedAgentChat } from "@/app/_components/authenticated-agent-chat";\n\nexport default async function SessionPage({\n params,\n}: {\n readonly params: Promise<{ readonly sessionId: string }>;\n}) {\n const { sessionId } = await params;\n return <AuthenticatedAgentChat sessionId={sessionId} />;\n}\n';
|
|
56
56
|
readonly "app/s/page.tsx": 'import { AuthenticatedAgentChat } from "@/app/_components/authenticated-agent-chat";\n\nexport default function NewSessionPage() {\n return <AuthenticatedAgentChat sessionless />;\n}\n';
|
|
57
57
|
readonly "lib/auth-client.ts": '"use client";\n\nimport { createAuthClient } from "better-auth/react";\n\nexport const authClient = createAuthClient();\n';
|
|
58
|
-
readonly "lib/auth.ts": 'import { betterAuth } from "better-auth";\n\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst DEVELOPMENT_ALLOWED_HOSTS = ["localhost:*", "127.0.0.1:*"];\n\nfunction getAllowedHosts(): string[] {\n if (process.env.NODE_ENV === "development") {\n return DEVELOPMENT_ALLOWED_HOSTS;\n }\n const deploymentHosts = [\n process.env.VERCEL_URL,\n process.env.VERCEL_BRANCH_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n ].filter((host): host is string => Boolean(host));\n if (deploymentHosts.length === 0) {\n throw new Error("No trusted deployment hosts are configured");\n }\n return Array.from(new Set(deploymentHosts));\n}\n\nfunction requireEnvironmentVariable(name: string): string {\n const value = process.env[name];\n if (value) return value;\n if (process.env.NODE_ENV === "development") return `development-${name}`;\n throw new Error(`Missing required environment variable: ${name}`);\n}\n\nexport const auth = betterAuth({\n baseURL: {\n allowedHosts: getAllowedHosts(),\n protocol: process.env.NODE_ENV === "development" ? "auto" : "https",\n },\n secret: requireEnvironmentVariable("BETTER_AUTH_SECRET"),\n session: {\n expiresIn: SESSION_MAX_AGE_SECONDS,\n disableSessionRefresh: true,\n cookieCache: {\n enabled: true,\n maxAge: SESSION_MAX_AGE_SECONDS,\n refreshCache: false,\n strategy: "jwe",\n },\n },\n socialProviders: {\n vercel: {\n clientId: requireEnvironmentVariable("VERCEL_APP_CLIENT_ID"),\n clientSecret: requireEnvironmentVariable("VERCEL_APP_CLIENT_SECRET"),\n },\n },\n});\n';
|
|
58
|
+
readonly "lib/auth.ts": 'import { betterAuth } from "better-auth";\n\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst DEVELOPMENT_ALLOWED_HOSTS = ["localhost:*", "127.0.0.1:*"];\n\nfunction getAllowedHosts(): string[] {\n if (process.env.NODE_ENV === "development") {\n return DEVELOPMENT_ALLOWED_HOSTS;\n }\n const deploymentHosts = [\n process.env.VERCEL_URL,\n process.env.VERCEL_BRANCH_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n ].filter((host): host is string => Boolean(host));\n if (deploymentHosts.length === 0) {\n throw new Error("No trusted deployment hosts are configured");\n }\n return Array.from(new Set(deploymentHosts));\n}\n\nfunction requireEnvironmentVariable(name: string): string {\n const value = process.env[name];\n if (value) return value;\n if (process.env.NODE_ENV === "development") return `development-${name}`;\n throw new Error(`Missing required environment variable: ${name}`);\n}\n\nexport const auth = betterAuth({\n baseURL: {\n allowedHosts: getAllowedHosts(),\n protocol: process.env.NODE_ENV === "development" ? "auto" : "https",\n },\n secret: requireEnvironmentVariable("BETTER_AUTH_SECRET"),\n session: {\n expiresIn: SESSION_MAX_AGE_SECONDS,\n disableSessionRefresh: true,\n cookieCache: {\n enabled: true,\n maxAge: SESSION_MAX_AGE_SECONDS,\n refreshCache: false,\n strategy: "jwe",\n },\n },\n socialProviders: {\n vercel: {\n clientId: requireEnvironmentVariable("VERCEL_APP_CLIENT_ID"),\n clientSecret: requireEnvironmentVariable("VERCEL_APP_CLIENT_SECRET"),\n scope: ["openid", "email", "profile"],\n },\n },\n});\n';
|
|
59
59
|
};
|
|
60
60
|
export declare const WEB_APP_TEMPLATE_PACKAGE_JSON: {
|
|
61
61
|
readonly scripts: {
|
|
@@ -78,6 +78,11 @@ export function AgentChat({
|
|
|
78
78
|
// Answers are accepted while work runs; the server's settlement closes each request.
|
|
79
79
|
const canRespond = (requestId: string) =>
|
|
80
80
|
!isResuming && agent.data.inputs[requestId]?.status === "open";
|
|
81
|
+
// A tool call can ask several questions at once, and its message part holds only the latest.
|
|
82
|
+
const questionsFor = (callId: string) =>
|
|
83
|
+
Object.values(agent.data.inputs).filter(
|
|
84
|
+
(input) => input.request.kind === "question" && input.request.action.callId === callId,
|
|
85
|
+
);
|
|
81
86
|
|
|
82
87
|
const requestCancellation = () => {
|
|
83
88
|
setCancellationError(undefined);
|
|
@@ -161,6 +166,7 @@ export function AgentChat({
|
|
|
161
166
|
}
|
|
162
167
|
key={message.id}
|
|
163
168
|
message={message}
|
|
169
|
+
questionsFor={questionsFor}
|
|
164
170
|
onInputResponses={(inputResponses) => {
|
|
165
171
|
setCancellationError(undefined);
|
|
166
172
|
return agent.respond(inputResponses);
|
|
@@ -309,6 +315,7 @@ function getLatestTurnFailure(
|
|
|
309
315
|
`,"app/_components/agent-message.tsx":`"use client";
|
|
310
316
|
|
|
311
317
|
import type {
|
|
318
|
+
ConversationInput,
|
|
312
319
|
EveAuthorizationPart,
|
|
313
320
|
EveDynamicToolPart,
|
|
314
321
|
EveMessage,
|
|
@@ -362,11 +369,13 @@ export function AgentMessage({
|
|
|
362
369
|
isStreaming,
|
|
363
370
|
message,
|
|
364
371
|
onInputResponses,
|
|
372
|
+
questionsFor,
|
|
365
373
|
}: {
|
|
366
374
|
readonly canRespond: (requestId: string) => boolean;
|
|
367
375
|
readonly isStreaming: boolean;
|
|
368
376
|
readonly message: EveMessage;
|
|
369
377
|
readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;
|
|
378
|
+
readonly questionsFor: (callId: string) => readonly ConversationInput[];
|
|
370
379
|
}) {
|
|
371
380
|
const lastTextIndex = message.parts.reduce(
|
|
372
381
|
(last, part, index) => (part.type === "text" ? index : last),
|
|
@@ -389,6 +398,7 @@ export function AgentMessage({
|
|
|
389
398
|
key={partKey(part, index)}
|
|
390
399
|
onInputResponses={onInputResponses}
|
|
391
400
|
part={part}
|
|
401
|
+
questionsFor={questionsFor}
|
|
392
402
|
showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}
|
|
393
403
|
/>
|
|
394
404
|
),
|
|
@@ -402,11 +412,13 @@ function AgentMessagePart({
|
|
|
402
412
|
canRespond,
|
|
403
413
|
onInputResponses,
|
|
404
414
|
part,
|
|
415
|
+
questionsFor,
|
|
405
416
|
showCaret,
|
|
406
417
|
}: {
|
|
407
418
|
readonly canRespond: (requestId: string) => boolean;
|
|
408
419
|
readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;
|
|
409
420
|
readonly part: EveMessagePart;
|
|
421
|
+
readonly questionsFor: (callId: string) => readonly ConversationInput[];
|
|
410
422
|
readonly showCaret: boolean;
|
|
411
423
|
}) {
|
|
412
424
|
switch (part.type) {
|
|
@@ -430,15 +442,20 @@ function AgentMessagePart({
|
|
|
430
442
|
case "authorization":
|
|
431
443
|
return <AuthorizationPrompt part={part} />;
|
|
432
444
|
case "dynamic-tool": {
|
|
433
|
-
const
|
|
434
|
-
if (
|
|
445
|
+
const questions = questionsFor(part.toolCallId);
|
|
446
|
+
if (questions.length > 0) {
|
|
435
447
|
return (
|
|
436
|
-
<
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
448
|
+
<div className="space-y-4">
|
|
449
|
+
{questions.map(({ request, response }) => (
|
|
450
|
+
<QuestionRequest
|
|
451
|
+
canRespond={canRespond(request.requestId)}
|
|
452
|
+
inputRequest={request}
|
|
453
|
+
inputResponse={response}
|
|
454
|
+
key={request.requestId}
|
|
455
|
+
onInputResponses={onInputResponses}
|
|
456
|
+
/>
|
|
457
|
+
))}
|
|
458
|
+
</div>
|
|
442
459
|
);
|
|
443
460
|
}
|
|
444
461
|
|
|
@@ -6122,6 +6139,7 @@ export const auth = betterAuth({
|
|
|
6122
6139
|
vercel: {
|
|
6123
6140
|
clientId: requireEnvironmentVariable("VERCEL_APP_CLIENT_ID"),
|
|
6124
6141
|
clientSecret: requireEnvironmentVariable("VERCEL_APP_CLIENT_SECRET"),
|
|
6142
|
+
scope: ["openid", "email", "profile"],
|
|
6125
6143
|
},
|
|
6126
6144
|
},
|
|
6127
6145
|
});
|