@adonis-agora/agent 0.61.0 → 0.62.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 +62 -0
- package/README.md +12 -7
- package/dist/configure.d.ts +7 -5
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +7 -5
- package/dist/configure.js.map +1 -1
- package/dist/providers/a2a_provider.d.ts +14 -0
- package/dist/providers/a2a_provider.d.ts.map +1 -0
- package/dist/providers/a2a_provider.js +136 -0
- package/dist/providers/a2a_provider.js.map +1 -0
- package/dist/providers/agent_provider.d.ts.map +1 -1
- package/dist/providers/agent_provider.js +80 -13
- package/dist/providers/agent_provider.js.map +1 -1
- package/dist/providers/mcp_provider.d.ts +3 -0
- package/dist/providers/mcp_provider.d.ts.map +1 -1
- package/dist/providers/mcp_provider.js +147 -62
- package/dist/providers/mcp_provider.js.map +1 -1
- package/dist/src/a2a/auth.d.ts +67 -0
- package/dist/src/a2a/auth.d.ts.map +1 -0
- package/dist/src/a2a/auth.js +47 -0
- package/dist/src/a2a/auth.js.map +1 -0
- package/dist/src/a2a/define_config.d.ts +74 -0
- package/dist/src/a2a/define_config.d.ts.map +1 -0
- package/dist/src/a2a/define_config.js +5 -0
- package/dist/src/a2a/define_config.js.map +1 -0
- package/dist/src/a2a/gate.d.ts +23 -0
- package/dist/src/a2a/gate.d.ts.map +1 -0
- package/dist/src/a2a/gate.js +56 -0
- package/dist/src/a2a/gate.js.map +1 -0
- package/dist/src/a2a/handler.d.ts +65 -0
- package/dist/src/a2a/handler.d.ts.map +1 -0
- package/dist/src/a2a/handler.js +322 -0
- package/dist/src/a2a/handler.js.map +1 -0
- package/dist/src/a2a/index.d.ts +15 -0
- package/dist/src/a2a/index.d.ts.map +1 -0
- package/dist/src/a2a/index.js +9 -0
- package/dist/src/a2a/index.js.map +1 -0
- package/dist/src/a2a/permission-tool.d.ts +17 -0
- package/dist/src/a2a/permission-tool.d.ts.map +1 -0
- package/dist/src/a2a/permission-tool.js +70 -0
- package/dist/src/a2a/permission-tool.js.map +1 -0
- package/dist/src/a2a/protocol.d.ts +100 -0
- package/dist/src/a2a/protocol.d.ts.map +1 -0
- package/dist/src/a2a/protocol.js +106 -0
- package/dist/src/a2a/protocol.js.map +1 -0
- package/dist/src/a2a/runtime.d.ts +6 -0
- package/dist/src/a2a/runtime.d.ts.map +1 -0
- package/dist/src/a2a/runtime.js +13 -0
- package/dist/src/a2a/runtime.js.map +1 -0
- package/dist/src/a2a/server_middleware.d.ts +13 -0
- package/dist/src/a2a/server_middleware.d.ts.map +1 -0
- package/dist/src/a2a/server_middleware.js +18 -0
- package/dist/src/a2a/server_middleware.js.map +1 -0
- package/dist/src/a2a/store.d.ts +79 -0
- package/dist/src/a2a/store.d.ts.map +1 -0
- package/dist/src/a2a/store.js +151 -0
- package/dist/src/a2a/store.js.map +1 -0
- package/dist/src/a2a/turn.d.ts +80 -0
- package/dist/src/a2a/turn.d.ts.map +1 -0
- package/dist/src/a2a/turn.js +211 -0
- package/dist/src/a2a/turn.js.map +1 -0
- package/dist/src/action-proposal-executor.d.ts +3 -0
- package/dist/src/action-proposal-executor.d.ts.map +1 -1
- package/dist/src/action-proposal-executor.js +10 -0
- package/dist/src/action-proposal-executor.js.map +1 -1
- package/dist/src/action-proposal-service.d.ts +6 -2
- package/dist/src/action-proposal-service.d.ts.map +1 -1
- package/dist/src/action-proposal-service.js +20 -16
- package/dist/src/action-proposal-service.js.map +1 -1
- package/dist/src/action-proposal-text.d.ts +42 -3
- package/dist/src/action-proposal-text.d.ts.map +1 -1
- package/dist/src/action-proposal-text.js +80 -12
- package/dist/src/action-proposal-text.js.map +1 -1
- package/dist/src/action-proposal-worker.d.ts +2 -1
- package/dist/src/action-proposal-worker.d.ts.map +1 -1
- package/dist/src/action-proposal-worker.js +9 -2
- package/dist/src/action-proposal-worker.js.map +1 -1
- package/dist/src/ag-ui/adapter.d.ts.map +1 -1
- package/dist/src/ag-ui/adapter.js +83 -11
- package/dist/src/ag-ui/adapter.js.map +1 -1
- package/dist/src/ag-ui/core/encoder.d.ts +105 -0
- package/dist/src/ag-ui/core/encoder.d.ts.map +1 -0
- package/dist/src/ag-ui/core/encoder.js +523 -0
- package/dist/src/ag-ui/core/encoder.js.map +1 -0
- package/dist/src/ag-ui/core/index.d.ts +12 -0
- package/dist/src/ag-ui/core/index.d.ts.map +1 -0
- package/dist/src/ag-ui/core/index.js +12 -0
- package/dist/src/ag-ui/core/index.js.map +1 -0
- package/dist/src/ag-ui/core/input.d.ts +74 -0
- package/dist/src/ag-ui/core/input.d.ts.map +1 -0
- package/dist/src/ag-ui/core/input.js +228 -0
- package/dist/src/ag-ui/core/input.js.map +1 -0
- package/dist/src/ag-ui/core/interrupt-id.d.ts +31 -0
- package/dist/src/ag-ui/core/interrupt-id.d.ts.map +1 -0
- package/dist/src/ag-ui/core/interrupt-id.js +48 -0
- package/dist/src/ag-ui/core/interrupt-id.js.map +1 -0
- package/dist/src/ag-ui/{proposal-decision.d.ts → core/proposal-decision.d.ts} +1 -1
- package/dist/src/ag-ui/core/proposal-decision.d.ts.map +1 -0
- package/dist/src/ag-ui/{proposal-decision.js → core/proposal-decision.js} +3 -6
- package/dist/src/ag-ui/core/proposal-decision.js.map +1 -0
- package/dist/src/ag-ui/core/stream.d.ts +27 -0
- package/dist/src/ag-ui/core/stream.d.ts.map +1 -0
- package/dist/src/ag-ui/core/stream.js +68 -0
- package/dist/src/ag-ui/core/stream.js.map +1 -0
- package/dist/src/ag-ui/core/types.d.ts +249 -0
- package/dist/src/ag-ui/core/types.d.ts.map +1 -0
- package/dist/src/ag-ui/core/types.js +35 -0
- package/dist/src/ag-ui/core/types.js.map +1 -0
- package/dist/src/ag-ui/encoder.d.ts +3 -4
- package/dist/src/ag-ui/encoder.d.ts.map +1 -1
- package/dist/src/ag-ui/encoder.js +10 -11
- package/dist/src/ag-ui/encoder.js.map +1 -1
- package/dist/src/ag-ui/frames.d.ts +4 -5
- package/dist/src/ag-ui/frames.d.ts.map +1 -1
- package/dist/src/ag-ui/frames.js +8 -4
- package/dist/src/ag-ui/frames.js.map +1 -1
- package/dist/src/ag-ui/index.d.ts +3 -5
- package/dist/src/ag-ui/index.d.ts.map +1 -1
- package/dist/src/ag-ui/index.js +3 -5
- package/dist/src/ag-ui/index.js.map +1 -1
- package/dist/src/ag-ui/stream.d.ts +3 -4
- package/dist/src/ag-ui/stream.d.ts.map +1 -1
- package/dist/src/ag-ui/stream.js +4 -5
- package/dist/src/ag-ui/stream.js.map +1 -1
- package/dist/src/agent-deps-factory.d.ts +3 -0
- package/dist/src/agent-deps-factory.d.ts.map +1 -1
- package/dist/src/agent-deps-factory.js +3 -0
- package/dist/src/agent-deps-factory.js.map +1 -1
- package/dist/src/agent-deps.d.ts +3 -0
- package/dist/src/agent-deps.d.ts.map +1 -1
- package/dist/src/agent-deps.js.map +1 -1
- package/dist/src/agent-loop.d.ts +4 -3
- package/dist/src/agent-loop.d.ts.map +1 -1
- package/dist/src/agent-loop.js +50 -25
- package/dist/src/agent-loop.js.map +1 -1
- package/dist/src/agent-service.d.ts +11 -2
- package/dist/src/agent-service.d.ts.map +1 -1
- package/dist/src/agent-service.js +23 -11
- package/dist/src/agent-service.js.map +1 -1
- package/dist/src/ai-sdk/ai-sdk-model.js +4 -1
- package/dist/src/ai-sdk/ai-sdk-model.js.map +1 -1
- package/dist/src/ai-tool-ref.d.ts +4 -0
- package/dist/src/ai-tool-ref.d.ts.map +1 -1
- package/dist/src/ai-tool-ref.js +10 -14
- package/dist/src/ai-tool-ref.js.map +1 -1
- package/dist/src/chat-queue-service.d.ts.map +1 -1
- package/dist/src/chat-queue-service.js +2 -0
- package/dist/src/chat-queue-service.js.map +1 -1
- package/dist/src/define_config.d.ts +61 -12
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/engine.d.ts +77 -0
- package/dist/src/engine.d.ts.map +1 -0
- package/dist/src/engine.js +27 -0
- package/dist/src/engine.js.map +1 -0
- package/dist/src/genui/builtins.d.ts +73 -3
- package/dist/src/genui/builtins.d.ts.map +1 -1
- package/dist/src/genui/builtins.js +298 -3
- package/dist/src/genui/builtins.js.map +1 -1
- package/dist/src/genui/capabilities.d.ts +24 -0
- package/dist/src/genui/capabilities.d.ts.map +1 -0
- package/dist/src/genui/capabilities.js +81 -0
- package/dist/src/genui/capabilities.js.map +1 -0
- package/dist/src/genui/catalog.d.ts +77 -0
- package/dist/src/genui/catalog.d.ts.map +1 -0
- package/dist/src/genui/catalog.js +88 -0
- package/dist/src/genui/catalog.js.map +1 -0
- package/dist/src/genui/factory.d.ts +7 -14
- package/dist/src/genui/factory.d.ts.map +1 -1
- package/dist/src/genui/factory.js.map +1 -1
- package/dist/src/genui/index.d.ts +4 -12
- package/dist/src/genui/index.d.ts.map +1 -1
- package/dist/src/genui/index.js +5 -18
- package/dist/src/genui/index.js.map +1 -1
- package/dist/src/genui/public.d.ts +9 -0
- package/dist/src/genui/public.d.ts.map +1 -0
- package/dist/src/genui/public.js +9 -0
- package/dist/src/genui/public.js.map +1 -0
- package/dist/src/genui/registry.d.ts +65 -0
- package/dist/src/genui/registry.d.ts.map +1 -0
- package/dist/src/genui/registry.js +151 -0
- package/dist/src/genui/registry.js.map +1 -0
- package/dist/src/genui/schema.d.ts +71 -0
- package/dist/src/genui/schema.d.ts.map +1 -0
- package/dist/src/genui/schema.js +304 -0
- package/dist/src/genui/schema.js.map +1 -0
- package/dist/src/genui/text.d.ts +35 -0
- package/dist/src/genui/text.d.ts.map +1 -0
- package/dist/src/genui/text.js +156 -0
- package/dist/src/genui/text.js.map +1 -0
- package/dist/src/genui/tools.d.ts +91 -0
- package/dist/src/genui/tools.d.ts.map +1 -0
- package/dist/src/genui/tools.js +292 -0
- package/dist/src/genui/tools.js.map +1 -0
- package/dist/src/genui/tree.d.ts +56 -0
- package/dist/src/genui/tree.d.ts.map +1 -0
- package/dist/src/genui/tree.js +188 -0
- package/dist/src/genui/tree.js.map +1 -0
- package/dist/src/index.d.ts +4 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/auth.d.ts +17 -1
- package/dist/src/mcp/auth.d.ts.map +1 -1
- package/dist/src/mcp/auth.js +14 -3
- package/dist/src/mcp/auth.js.map +1 -1
- package/dist/src/mcp/define_config.d.ts +81 -2
- package/dist/src/mcp/define_config.d.ts.map +1 -1
- package/dist/src/mcp/define_config.js.map +1 -1
- package/dist/src/mcp/index.d.ts +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/oauth-resource.d.ts +9 -0
- package/dist/src/mcp/oauth-resource.d.ts.map +1 -0
- package/dist/src/mcp/oauth-resource.js +16 -0
- package/dist/src/mcp/oauth-resource.js.map +1 -0
- package/dist/src/mcp/server.d.ts +5 -0
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +48 -16
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/negotiated-tool-ui.d.ts.map +1 -1
- package/dist/src/negotiated-tool-ui.js +12 -8
- package/dist/src/negotiated-tool-ui.js.map +1 -1
- package/dist/src/opencode/client.d.ts +202 -0
- package/dist/src/opencode/client.d.ts.map +1 -0
- package/dist/src/opencode/client.js +2 -0
- package/dist/src/opencode/client.js.map +1 -0
- package/dist/src/opencode/durable/index.d.ts +33 -0
- package/dist/src/opencode/durable/index.d.ts.map +1 -0
- package/dist/src/opencode/durable/index.js +38 -0
- package/dist/src/opencode/durable/index.js.map +1 -0
- package/dist/src/opencode/durable/runner.d.ts +36 -0
- package/dist/src/opencode/durable/runner.d.ts.map +1 -0
- package/dist/src/opencode/durable/runner.js +88 -0
- package/dist/src/opencode/durable/runner.js.map +1 -0
- package/dist/src/opencode/durable/workflow.d.ts +40 -0
- package/dist/src/opencode/durable/workflow.d.ts.map +1 -0
- package/dist/src/opencode/durable/workflow.js +106 -0
- package/dist/src/opencode/durable/workflow.js.map +1 -0
- package/dist/src/opencode/engine.d.ts +48 -0
- package/dist/src/opencode/engine.d.ts.map +1 -0
- package/dist/src/opencode/engine.js +98 -0
- package/dist/src/opencode/engine.js.map +1 -0
- package/dist/src/opencode/event-hub.d.ts +23 -0
- package/dist/src/opencode/event-hub.d.ts.map +1 -0
- package/dist/src/opencode/event-hub.js +99 -0
- package/dist/src/opencode/event-hub.js.map +1 -0
- package/dist/src/opencode/forms.d.ts +19 -0
- package/dist/src/opencode/forms.d.ts.map +1 -0
- package/dist/src/opencode/forms.js +82 -0
- package/dist/src/opencode/forms.js.map +1 -0
- package/dist/src/opencode/host.d.ts +179 -0
- package/dist/src/opencode/host.d.ts.map +1 -0
- package/dist/src/opencode/host.js +34 -0
- package/dist/src/opencode/host.js.map +1 -0
- package/dist/src/opencode/index.d.ts +15 -0
- package/dist/src/opencode/index.d.ts.map +1 -0
- package/dist/src/opencode/index.js +9 -0
- package/dist/src/opencode/index.js.map +1 -0
- package/dist/src/opencode/log.d.ts +9 -0
- package/dist/src/opencode/log.d.ts.map +1 -0
- package/dist/src/opencode/log.js +15 -0
- package/dist/src/opencode/log.js.map +1 -0
- package/dist/src/opencode/mcp.d.ts +60 -0
- package/dist/src/opencode/mcp.d.ts.map +1 -0
- package/dist/src/opencode/mcp.js +258 -0
- package/dist/src/opencode/mcp.js.map +1 -0
- package/dist/src/opencode/runner.d.ts +40 -0
- package/dist/src/opencode/runner.d.ts.map +1 -0
- package/dist/src/opencode/runner.js +125 -0
- package/dist/src/opencode/runner.js.map +1 -0
- package/dist/src/opencode/turn.d.ts +167 -0
- package/dist/src/opencode/turn.d.ts.map +1 -0
- package/dist/src/opencode/turn.js +761 -0
- package/dist/src/opencode/turn.js.map +1 -0
- package/dist/src/opencode/turns.d.ts +245 -0
- package/dist/src/opencode/turns.d.ts.map +1 -0
- package/dist/src/opencode/turns.js +867 -0
- package/dist/src/opencode/turns.js.map +1 -0
- package/dist/src/personas.d.ts.map +1 -1
- package/dist/src/personas.js +4 -1
- package/dist/src/personas.js.map +1 -1
- package/dist/src/react/ag-ui.d.ts +4 -2
- package/dist/src/react/ag-ui.d.ts.map +1 -1
- package/dist/src/react/ag-ui.js +2 -2
- package/dist/src/react/ag-ui.js.map +1 -1
- package/dist/src/react/client.d.ts +1 -1
- package/dist/src/react/client.d.ts.map +1 -1
- package/dist/src/react/client.js +1 -1
- package/dist/src/react/client.js.map +1 -1
- package/dist/src/react/core/ag-ui-backend.d.ts +85 -0
- package/dist/src/react/core/ag-ui-backend.d.ts.map +1 -0
- package/dist/src/react/core/ag-ui-backend.js +409 -0
- package/dist/src/react/core/ag-ui-backend.js.map +1 -0
- package/dist/src/react/core/agent-chat-transport.d.ts +187 -0
- package/dist/src/react/core/agent-chat-transport.d.ts.map +1 -0
- package/dist/src/react/core/agent-chat-transport.js +796 -0
- package/dist/src/react/core/agent-chat-transport.js.map +1 -0
- package/dist/src/react/core/approvals/countdown.d.ts +22 -0
- package/dist/src/react/core/approvals/countdown.d.ts.map +1 -0
- package/dist/src/react/core/approvals/countdown.js +39 -0
- package/dist/src/react/core/approvals/countdown.js.map +1 -0
- package/dist/src/react/core/approvals/proposals.d.ts +15 -0
- package/dist/src/react/core/approvals/proposals.d.ts.map +1 -0
- package/dist/src/react/core/approvals/proposals.js +82 -0
- package/dist/src/react/core/approvals/proposals.js.map +1 -0
- package/dist/src/react/core/approvals/use-action-proposals.d.ts +10 -0
- package/dist/src/react/core/approvals/use-action-proposals.d.ts.map +1 -0
- package/dist/src/react/core/approvals/use-action-proposals.js +86 -0
- package/dist/src/react/core/approvals/use-action-proposals.js.map +1 -0
- package/dist/src/react/core/attachments/files.d.ts +39 -0
- package/dist/src/react/core/attachments/files.d.ts.map +1 -0
- package/dist/src/react/core/attachments/files.js +89 -0
- package/dist/src/react/core/attachments/files.js.map +1 -0
- package/dist/src/react/core/attachments/use-attachments.d.ts +106 -0
- package/dist/src/react/core/attachments/use-attachments.d.ts.map +1 -0
- package/dist/src/react/core/attachments/use-attachments.js +217 -0
- package/dist/src/react/core/attachments/use-attachments.js.map +1 -0
- package/dist/src/react/core/backend.d.ts +234 -0
- package/dist/src/react/core/backend.d.ts.map +1 -0
- package/dist/src/react/core/backend.js +21 -0
- package/dist/src/react/core/backend.js.map +1 -0
- package/dist/src/react/core/background-runs.d.ts +32 -0
- package/dist/src/react/core/background-runs.d.ts.map +1 -0
- package/dist/src/react/core/background-runs.js +47 -0
- package/dist/src/react/core/background-runs.js.map +1 -0
- package/dist/src/react/core/catalog/use-agents.d.ts +31 -0
- package/dist/src/react/core/catalog/use-agents.d.ts.map +1 -0
- package/dist/src/react/core/catalog/use-agents.js +28 -0
- package/dist/src/react/core/catalog/use-agents.js.map +1 -0
- package/dist/src/react/core/catalog/use-models.d.ts +41 -0
- package/dist/src/react/core/catalog/use-models.d.ts.map +1 -0
- package/dist/src/react/core/catalog/use-models.js +35 -0
- package/dist/src/react/core/catalog/use-models.js.map +1 -0
- package/dist/src/react/core/catalog/use-resource.d.ts +12 -0
- package/dist/src/react/core/catalog/use-resource.d.ts.map +1 -0
- package/dist/src/react/core/catalog/use-resource.js +41 -0
- package/dist/src/react/core/catalog/use-resource.js.map +1 -0
- package/dist/src/react/core/client-wire.d.ts +14 -0
- package/dist/src/react/core/client-wire.d.ts.map +1 -0
- package/dist/src/react/core/client-wire.js +2 -0
- package/dist/src/react/core/client-wire.js.map +1 -0
- package/dist/src/react/core/client.d.ts +222 -0
- package/dist/src/react/core/client.d.ts.map +1 -0
- package/dist/src/react/core/client.js +435 -0
- package/dist/src/react/core/client.js.map +1 -0
- package/dist/src/react/core/components/ambient-ui.d.ts +22 -0
- package/dist/src/react/core/components/ambient-ui.d.ts.map +1 -0
- package/dist/src/react/core/components/ambient-ui.js +30 -0
- package/dist/src/react/core/components/ambient-ui.js.map +1 -0
- package/dist/src/react/core/components/chat-input.d.ts +44 -0
- package/dist/src/react/core/components/chat-input.d.ts.map +1 -0
- package/dist/src/react/core/components/chat-input.js +69 -0
- package/dist/src/react/core/components/chat-input.js.map +1 -0
- package/dist/src/react/core/components/index.d.ts +7 -0
- package/dist/src/react/core/components/index.d.ts.map +1 -0
- package/dist/src/react/core/components/index.js +7 -0
- package/dist/src/react/core/components/index.js.map +1 -0
- package/dist/src/react/core/components/message-item.d.ts +101 -0
- package/dist/src/react/core/components/message-item.d.ts.map +1 -0
- package/dist/src/react/core/components/message-item.js +118 -0
- package/dist/src/react/core/components/message-item.js.map +1 -0
- package/dist/src/react/core/components/message-list.d.ts +85 -0
- package/dist/src/react/core/components/message-list.d.ts.map +1 -0
- package/dist/src/react/core/components/message-list.js +45 -0
- package/dist/src/react/core/components/message-list.js.map +1 -0
- package/dist/src/react/core/components/speech-languages.d.ts +22 -0
- package/dist/src/react/core/components/speech-languages.d.ts.map +1 -0
- package/dist/src/react/core/components/speech-languages.js +38 -0
- package/dist/src/react/core/components/speech-languages.js.map +1 -0
- package/dist/src/react/core/components/use-speech-recognition.d.ts +19 -0
- package/dist/src/react/core/components/use-speech-recognition.d.ts.map +1 -0
- package/dist/src/react/core/components/use-speech-recognition.js +92 -0
- package/dist/src/react/core/components/use-speech-recognition.js.map +1 -0
- package/dist/src/react/core/composer/index.d.ts +4 -0
- package/dist/src/react/core/composer/index.d.ts.map +1 -0
- package/dist/src/react/core/composer/index.js +4 -0
- package/dist/src/react/core/composer/index.js.map +1 -0
- package/dist/src/react/core/composer/model.d.ts +83 -0
- package/dist/src/react/core/composer/model.d.ts.map +1 -0
- package/dist/src/react/core/composer/model.js +53 -0
- package/dist/src/react/core/composer/model.js.map +1 -0
- package/dist/src/react/core/composer/skills-source.d.ts +29 -0
- package/dist/src/react/core/composer/skills-source.d.ts.map +1 -0
- package/dist/src/react/core/composer/skills-source.js +61 -0
- package/dist/src/react/core/composer/skills-source.js.map +1 -0
- package/dist/src/react/core/composer/use-composer-autocomplete.d.ts +78 -0
- package/dist/src/react/core/composer/use-composer-autocomplete.d.ts.map +1 -0
- package/dist/src/react/core/composer/use-composer-autocomplete.js +244 -0
- package/dist/src/react/core/composer/use-composer-autocomplete.js.map +1 -0
- package/dist/src/react/core/config/use-agent-config.d.ts +21 -0
- package/dist/src/react/core/config/use-agent-config.d.ts.map +1 -0
- package/dist/src/react/core/config/use-agent-config.js +57 -0
- package/dist/src/react/core/config/use-agent-config.js.map +1 -0
- package/dist/src/react/core/elicitation/answers.d.ts +22 -0
- package/dist/src/react/core/elicitation/answers.d.ts.map +1 -0
- package/dist/src/react/core/elicitation/answers.js +65 -0
- package/dist/src/react/core/elicitation/answers.js.map +1 -0
- package/dist/src/react/core/feedback/use-message-feedback.d.ts +32 -0
- package/dist/src/react/core/feedback/use-message-feedback.d.ts.map +1 -0
- package/dist/src/react/core/feedback/use-message-feedback.js +106 -0
- package/dist/src/react/core/feedback/use-message-feedback.js.map +1 -0
- package/dist/src/react/core/genui/generative-ui.d.ts +76 -0
- package/dist/src/react/core/genui/generative-ui.d.ts.map +1 -0
- package/dist/src/react/core/genui/generative-ui.js +210 -0
- package/dist/src/react/core/genui/generative-ui.js.map +1 -0
- package/dist/src/react/core/genui/index.d.ts +5 -0
- package/dist/src/react/core/genui/index.d.ts.map +1 -0
- package/dist/src/react/core/genui/index.js +5 -0
- package/dist/src/react/core/genui/index.js.map +1 -0
- package/dist/src/react/core/genui/json-render.d.ts +54 -0
- package/dist/src/react/core/genui/json-render.d.ts.map +1 -0
- package/dist/src/react/core/genui/json-render.js +64 -0
- package/dist/src/react/core/genui/json-render.js.map +1 -0
- package/dist/src/react/core/genui/playwright.d.ts +12 -0
- package/dist/src/react/core/genui/playwright.d.ts.map +1 -0
- package/dist/src/react/core/genui/playwright.js +141 -0
- package/dist/src/react/core/genui/playwright.js.map +1 -0
- package/dist/src/react/core/genui/react-registry.d.ts +22 -0
- package/dist/src/react/core/genui/react-registry.d.ts.map +1 -0
- package/dist/src/react/core/genui/react-registry.js +72 -0
- package/dist/src/react/core/genui/react-registry.js.map +1 -0
- package/dist/src/react/core/genui/server.d.ts +32 -0
- package/dist/src/react/core/genui/server.d.ts.map +1 -0
- package/dist/src/react/core/genui/server.js +66 -0
- package/dist/src/react/core/genui/server.js.map +1 -0
- package/dist/src/react/core/genui/types.d.ts +98 -0
- package/dist/src/react/core/genui/types.d.ts.map +1 -0
- package/dist/src/react/core/genui/types.js +3 -0
- package/dist/src/react/core/genui/types.js.map +1 -0
- package/dist/src/react/core/genui/use-generative-ui.d.ts +42 -0
- package/dist/src/react/core/genui/use-generative-ui.d.ts.map +1 -0
- package/dist/src/react/core/genui/use-generative-ui.js +186 -0
- package/dist/src/react/core/genui/use-generative-ui.js.map +1 -0
- package/dist/src/react/core/http-error.d.ts +33 -0
- package/dist/src/react/core/http-error.d.ts.map +1 -0
- package/dist/src/react/core/http-error.js +47 -0
- package/dist/src/react/core/http-error.js.map +1 -0
- package/dist/src/react/core/index.d.ts +35 -0
- package/dist/src/react/core/index.d.ts.map +1 -0
- package/dist/src/react/core/index.js +34 -0
- package/dist/src/react/core/index.js.map +1 -0
- package/dist/src/react/core/markdown/agent-markdown.d.ts +39 -0
- package/dist/src/react/core/markdown/agent-markdown.d.ts.map +1 -0
- package/dist/src/react/core/markdown/agent-markdown.js +22 -0
- package/dist/src/react/core/markdown/agent-markdown.js.map +1 -0
- package/dist/src/react/core/markdown/index.d.ts +2 -0
- package/dist/src/react/core/markdown/index.d.ts.map +1 -0
- package/dist/src/react/core/markdown/index.js +2 -0
- package/dist/src/react/core/markdown/index.js.map +1 -0
- package/dist/src/react/core/media/index.d.ts +64 -0
- package/dist/src/react/core/media/index.d.ts.map +1 -0
- package/dist/src/react/core/media/index.js +135 -0
- package/dist/src/react/core/media/index.js.map +1 -0
- package/dist/src/react/core/media/tus.d.ts +13 -0
- package/dist/src/react/core/media/tus.d.ts.map +1 -0
- package/dist/src/react/core/media/tus.js +59 -0
- package/dist/src/react/core/media/tus.js.map +1 -0
- package/dist/src/react/core/presentation/phrasing.d.ts +25 -0
- package/dist/src/react/core/presentation/phrasing.d.ts.map +1 -0
- package/dist/src/react/core/presentation/phrasing.js +58 -0
- package/dist/src/react/core/presentation/phrasing.js.map +1 -0
- package/dist/src/react/core/presentation/result-view.d.ts +43 -0
- package/dist/src/react/core/presentation/result-view.d.ts.map +1 -0
- package/dist/src/react/core/presentation/result-view.js +90 -0
- package/dist/src/react/core/presentation/result-view.js.map +1 -0
- package/dist/src/react/core/presentation/tool-activity.d.ts +106 -0
- package/dist/src/react/core/presentation/tool-activity.d.ts.map +1 -0
- package/dist/src/react/core/presentation/tool-activity.js +165 -0
- package/dist/src/react/core/presentation/tool-activity.js.map +1 -0
- package/dist/src/react/core/presentation/use-tool-catalog.d.ts +39 -0
- package/dist/src/react/core/presentation/use-tool-catalog.d.ts.map +1 -0
- package/dist/src/react/core/presentation/use-tool-catalog.js +77 -0
- package/dist/src/react/core/presentation/use-tool-catalog.js.map +1 -0
- package/dist/src/react/core/provider.d.ts +68 -0
- package/dist/src/react/core/provider.d.ts.map +1 -0
- package/dist/src/react/core/provider.js +61 -0
- package/dist/src/react/core/provider.js.map +1 -0
- package/dist/src/react/core/queue/model.d.ts +73 -0
- package/dist/src/react/core/queue/model.d.ts.map +1 -0
- package/dist/src/react/core/queue/model.js +2 -0
- package/dist/src/react/core/queue/model.js.map +1 -0
- package/dist/src/react/core/quota/use-quota.d.ts +35 -0
- package/dist/src/react/core/quota/use-quota.d.ts.map +1 -0
- package/dist/src/react/core/quota/use-quota.js +47 -0
- package/dist/src/react/core/quota/use-quota.js.map +1 -0
- package/dist/src/react/core/reasoning/timing.d.ts +38 -0
- package/dist/src/react/core/reasoning/timing.d.ts.map +1 -0
- package/dist/src/react/core/reasoning/timing.js +73 -0
- package/dist/src/react/core/reasoning/timing.js.map +1 -0
- package/dist/src/react/core/run-errors.d.ts +37 -0
- package/dist/src/react/core/run-errors.d.ts.map +1 -0
- package/dist/src/react/core/run-errors.js +37 -0
- package/dist/src/react/core/run-errors.js.map +1 -0
- package/dist/src/react/core/stored-message-to-ui-message.d.ts +4 -0
- package/dist/src/react/core/stored-message-to-ui-message.d.ts.map +1 -0
- package/dist/src/react/core/stored-message-to-ui-message.js +216 -0
- package/dist/src/react/core/stored-message-to-ui-message.js.map +1 -0
- package/dist/src/react/core/stored-thread-to-ui-messages.d.ts +82 -0
- package/dist/src/react/core/stored-thread-to-ui-messages.d.ts.map +1 -0
- package/dist/src/react/core/stored-thread-to-ui-messages.js +116 -0
- package/dist/src/react/core/stored-thread-to-ui-messages.js.map +1 -0
- package/dist/src/react/core/threads/threads-events.d.ts +22 -0
- package/dist/src/react/core/threads/threads-events.d.ts.map +1 -0
- package/dist/src/react/core/threads/threads-events.js +21 -0
- package/dist/src/react/core/threads/threads-events.js.map +1 -0
- package/dist/src/react/core/threads/use-threads.d.ts +31 -0
- package/dist/src/react/core/threads/use-threads.d.ts.map +1 -0
- package/dist/src/react/core/threads/use-threads.js +81 -0
- package/dist/src/react/core/threads/use-threads.js.map +1 -0
- package/dist/src/react/core/transcript/index.d.ts +4 -0
- package/dist/src/react/core/transcript/index.d.ts.map +1 -0
- package/dist/src/react/core/transcript/index.js +4 -0
- package/dist/src/react/core/transcript/index.js.map +1 -0
- package/dist/src/react/core/transcript/model.d.ts +399 -0
- package/dist/src/react/core/transcript/model.d.ts.map +1 -0
- package/dist/src/react/core/transcript/model.js +725 -0
- package/dist/src/react/core/transcript/model.js.map +1 -0
- package/dist/src/react/core/transcript/use-chat-transcript.d.ts +254 -0
- package/dist/src/react/core/transcript/use-chat-transcript.d.ts.map +1 -0
- package/dist/src/react/core/transcript/use-chat-transcript.js +572 -0
- package/dist/src/react/core/transcript/use-chat-transcript.js.map +1 -0
- package/dist/src/react/core/transcript/use-stick-to-bottom.d.ts +33 -0
- package/dist/src/react/core/transcript/use-stick-to-bottom.d.ts.map +1 -0
- package/dist/src/react/core/transcript/use-stick-to-bottom.js +48 -0
- package/dist/src/react/core/transcript/use-stick-to-bottom.js.map +1 -0
- package/dist/src/react/core/use-agent-chat.d.ts +341 -0
- package/dist/src/react/core/use-agent-chat.d.ts.map +1 -0
- package/dist/src/react/core/use-agent-chat.js +1118 -0
- package/dist/src/react/core/use-agent-chat.js.map +1 -0
- package/dist/src/react/genui/index.d.ts +1 -7
- package/dist/src/react/genui/index.d.ts.map +1 -1
- package/dist/src/react/genui/index.js +1 -7
- package/dist/src/react/genui/index.js.map +1 -1
- package/dist/src/react/genui/json-render.d.ts +1 -5
- package/dist/src/react/genui/json-render.d.ts.map +1 -1
- package/dist/src/react/genui/json-render.js +1 -5
- package/dist/src/react/genui/json-render.js.map +1 -1
- package/dist/src/react/genui/server.d.ts +2 -0
- package/dist/src/react/genui/server.d.ts.map +1 -0
- package/dist/src/react/genui/server.js +2 -0
- package/dist/src/react/genui/server.js.map +1 -0
- package/dist/src/react/genui/server_playwright.d.ts +2 -0
- package/dist/src/react/genui/server_playwright.d.ts.map +1 -0
- package/dist/src/react/genui/server_playwright.js +2 -0
- package/dist/src/react/genui/server_playwright.js.map +1 -0
- package/dist/src/react/index.d.ts +5 -7
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/react/index.js +5 -7
- package/dist/src/react/index.js.map +1 -1
- package/dist/src/react/markdown.d.ts +1 -6
- package/dist/src/react/markdown.d.ts.map +1 -1
- package/dist/src/react/markdown.js +1 -6
- package/dist/src/react/markdown.js.map +1 -1
- package/dist/src/react/media.d.ts +1 -17
- package/dist/src/react/media.d.ts.map +1 -1
- package/dist/src/react/media.js +1 -17
- package/dist/src/react/media.js.map +1 -1
- package/dist/src/refusal.d.ts +3 -0
- package/dist/src/refusal.d.ts.map +1 -0
- package/dist/src/refusal.js +3 -0
- package/dist/src/refusal.js.map +1 -0
- package/dist/src/spi/agent-runner.d.ts +6 -0
- package/dist/src/spi/agent-runner.d.ts.map +1 -1
- package/dist/src/spi/chat-queue.d.ts +4 -1
- package/dist/src/spi/chat-queue.d.ts.map +1 -1
- package/dist/src/spi/chat-queue.js.map +1 -1
- package/dist/src/spi/protocol-adapter.d.ts +3 -3
- package/dist/src/spi/roles-policy.d.ts +7 -0
- package/dist/src/spi/roles-policy.d.ts.map +1 -1
- package/dist/src/spi/tool.d.ts +18 -3
- package/dist/src/spi/tool.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +2 -0
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +3 -0
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/testing/chat-queue-store-contract.d.ts.map +1 -1
- package/dist/src/testing/chat-queue-store-contract.js +4 -0
- package/dist/src/testing/chat-queue-store-contract.js.map +1 -1
- package/dist/src/testing/in-memory-store.d.ts.map +1 -1
- package/dist/src/testing/in-memory-store.js +1 -0
- package/dist/src/testing/in-memory-store.js.map +1 -1
- package/dist/src/tool-discovery.d.ts +1 -1
- package/dist/src/tool-discovery.d.ts.map +1 -1
- package/dist/src/tool-discovery.js +5 -1
- package/dist/src/tool-discovery.js.map +1 -1
- package/dist/src/tool-registry.d.ts +3 -0
- package/dist/src/tool-registry.d.ts.map +1 -1
- package/dist/src/tool-registry.js +15 -3
- package/dist/src/tool-registry.js.map +1 -1
- package/dist/src/tool-result-presentation.d.ts +12 -0
- package/dist/src/tool-result-presentation.d.ts.map +1 -0
- package/dist/src/tool-result-presentation.js +44 -0
- package/dist/src/tool-result-presentation.js.map +1 -0
- package/dist/src/types.d.ts +20 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/ui-capabilities.d.ts +1 -8
- package/dist/src/ui-capabilities.d.ts.map +1 -1
- package/dist/src/ui-capabilities.js +3 -24
- package/dist/src/ui-capabilities.js.map +1 -1
- package/package.json +123 -22
- package/skills/agent-governance/SKILL.md +12 -4
- package/skills/agent-personas-agents/SKILL.md +2 -1
- package/skills/agent-setup/SKILL.md +36 -13
- package/skills/agent-testing/SKILL.md +22 -0
- package/skills/agent-tools/SKILL.md +54 -9
- package/dist/src/ag-ui/proposal-decision.d.ts.map +0 -1
- package/dist/src/ag-ui/proposal-decision.js.map +0 -1
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { streamErrorFrame } from '../agent-loop.js';
|
|
3
|
+
import { isThreadTurn } from '../chat-queue-service.js';
|
|
4
|
+
import { RUN_ENDED_BEFORE_TOOL_CALL } from '../dangling-tool-calls.js';
|
|
5
|
+
import { buildMemoryBlock, offerMemories } from '../memory.js';
|
|
6
|
+
import { offerSkills } from '../skills.js';
|
|
7
|
+
import { DefaultApprovalPolicy } from '../spi/approval-policy.js';
|
|
8
|
+
import { isChatQueueStore, releaseThreadRun } from '../spi/chat-queue.js';
|
|
9
|
+
import { OpenCodeEventHub } from './event-hub.js';
|
|
10
|
+
import { errorText, openCodeLog } from './log.js';
|
|
11
|
+
import { OpenCodeTurn } from './turn.js';
|
|
12
|
+
const DEFAULT_TURN_TIMEOUT_MS = 30 * 60_000;
|
|
13
|
+
const DEFAULT_TOOLS_TTL_MS = 7 * 24 * 60 * 60_000;
|
|
14
|
+
/** Tool kinds the LOOP serves (delegation, `ask`, `skill`, `remember`): never served over MCP. */
|
|
15
|
+
const LOOP_SERVED_KINDS = new Set(['agent', 'ask', 'skill', 'memory']);
|
|
16
|
+
/**
|
|
17
|
+
* The steps of an OpenCode turn — `begin` (thread, session, instructions), `prompt`, `observe`
|
|
18
|
+
* (until a person is asked something or the execution ends), `reply`, `settle` — so an in-memory
|
|
19
|
+
* runner can run them in a row and a durable one can checkpoint each. A step may run in a process
|
|
20
|
+
* that did not run the one before (a durable run resumed elsewhere): the live turn is rebuilt from
|
|
21
|
+
* the session, catching up on what OpenCode asked while nobody listened.
|
|
22
|
+
*/
|
|
23
|
+
export class OpenCodeTurns {
|
|
24
|
+
events = new OpenCodeEventHub();
|
|
25
|
+
live = new Map();
|
|
26
|
+
/** Actions approved per run, and the approvals the MCP endpoint already spent. */
|
|
27
|
+
grants = new Map();
|
|
28
|
+
spent = new Set();
|
|
29
|
+
/** When the tools endpoint was last registered for a location (`server|boot|directory`). */
|
|
30
|
+
toolsIssued = new Map();
|
|
31
|
+
host;
|
|
32
|
+
sessions;
|
|
33
|
+
settings;
|
|
34
|
+
factory;
|
|
35
|
+
store;
|
|
36
|
+
sink;
|
|
37
|
+
registry;
|
|
38
|
+
queue;
|
|
39
|
+
mintToolsToken;
|
|
40
|
+
/** Starts the next queued message of the thread; set by the runner (it owns `start`). */
|
|
41
|
+
startNext;
|
|
42
|
+
constructor(deps) {
|
|
43
|
+
this.host = deps.host;
|
|
44
|
+
this.sessions = deps.sessions;
|
|
45
|
+
this.settings = deps.settings;
|
|
46
|
+
this.factory = deps.factory;
|
|
47
|
+
this.store = deps.store;
|
|
48
|
+
this.sink = deps.sink;
|
|
49
|
+
this.registry = deps.registry;
|
|
50
|
+
this.queue = deps.queue;
|
|
51
|
+
this.mintToolsToken = deps.mintToolsToken;
|
|
52
|
+
}
|
|
53
|
+
/** The tools server's name in OpenCode. */
|
|
54
|
+
get toolsServer() {
|
|
55
|
+
return this.settings.tools?.server ?? 'agora';
|
|
56
|
+
}
|
|
57
|
+
/** The agent's deps (prompt, approval policy, allow-list, skills, memory) for a turn. */
|
|
58
|
+
depsFor(input) {
|
|
59
|
+
return this.factory.forAgent(input.agentName);
|
|
60
|
+
}
|
|
61
|
+
/** The tools a turn may reach: the agent's allow-list, narrowed by its persona's. */
|
|
62
|
+
allowedTools(input) {
|
|
63
|
+
const deps = this.depsFor(input);
|
|
64
|
+
const persona = personaOf(deps, input.persona);
|
|
65
|
+
const agent = deps.toolAllowList;
|
|
66
|
+
const own = persona?.allowedTools;
|
|
67
|
+
if (agent === undefined)
|
|
68
|
+
return own;
|
|
69
|
+
if (own === undefined)
|
|
70
|
+
return agent;
|
|
71
|
+
return agent.filter((name) => own.includes(name));
|
|
72
|
+
}
|
|
73
|
+
/** Whether the memory provider writes, so the endpoint serves `remember`. */
|
|
74
|
+
memoryWritable(input) {
|
|
75
|
+
return (this.settings.tools !== undefined && this.depsFor(input).memory?.provider.write !== undefined);
|
|
76
|
+
}
|
|
77
|
+
/** The runs this process is following now (an updater drains on it). */
|
|
78
|
+
liveRuns() {
|
|
79
|
+
return [...this.live.keys()];
|
|
80
|
+
}
|
|
81
|
+
/** Stop following every run, and close the event streams. */
|
|
82
|
+
close() {
|
|
83
|
+
for (const live of this.live.values())
|
|
84
|
+
live.stop();
|
|
85
|
+
this.live.clear();
|
|
86
|
+
this.events.close();
|
|
87
|
+
}
|
|
88
|
+
// ---- tools over MCP --------------------------------------------------------------------------
|
|
89
|
+
/**
|
|
90
|
+
* The turn a tool call over the engine's MCP endpoint belongs to, or `undefined` when it belongs
|
|
91
|
+
* to none — which the endpoint refuses: it exists only to serve turns. OpenCode names its session in
|
|
92
|
+
* every call's `_meta` (`ai.opencode/sessionID`); the call is that session's running turn's, and
|
|
93
|
+
* only when the endpoint's caller is the person that turn runs for, on the server the token was
|
|
94
|
+
* issued for. A session this process is not following is found through OpenCode (`session.get` →
|
|
95
|
+
* the thread it was created for → the thread's running turn); its components still reach the
|
|
96
|
+
* stream, as a message of their own.
|
|
97
|
+
*/
|
|
98
|
+
async callContext(call) {
|
|
99
|
+
const sessionId = sessionOfMeta(call.meta);
|
|
100
|
+
if (sessionId === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
const scope = (runId) => `${runId}:${call.requestId || randomUUID()}`;
|
|
103
|
+
for (const [runId, live] of this.live) {
|
|
104
|
+
if (live.handle.sessionId !== sessionId)
|
|
105
|
+
continue;
|
|
106
|
+
if (live.input.actor.id !== call.actor.id || live.handle.serverKey !== call.serverKey) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
input: live.input,
|
|
111
|
+
runId,
|
|
112
|
+
ctx: {
|
|
113
|
+
threadId: live.input.threadId,
|
|
114
|
+
runId,
|
|
115
|
+
...(live.input.agentName !== undefined ? { agentName: live.input.agentName } : {}),
|
|
116
|
+
emitUi: uiEmitter(scope(runId), async (component) => {
|
|
117
|
+
await live.turn.pushUi(component);
|
|
118
|
+
await this.uiPushed(live.input, runId, component);
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const threadId = await this.threadOfSession(call.actor, sessionId);
|
|
124
|
+
if (threadId === undefined)
|
|
125
|
+
return undefined;
|
|
126
|
+
if ((await this.store.getThreadActorRef(threadId)) !== call.actor.id)
|
|
127
|
+
return undefined;
|
|
128
|
+
const known = await this.sessions.get(threadId);
|
|
129
|
+
if (known === null || known.sessionId !== sessionId || known.serverKey !== call.serverKey) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const runId = await this.activeRun(threadId);
|
|
133
|
+
if (runId === null)
|
|
134
|
+
return undefined;
|
|
135
|
+
// Followed elsewhere: what this call knows of the run.
|
|
136
|
+
const input = {
|
|
137
|
+
threadId,
|
|
138
|
+
actor: call.actor,
|
|
139
|
+
userText: '',
|
|
140
|
+
...(known.agentName !== undefined ? { agentName: known.agentName } : {}),
|
|
141
|
+
...(known.persona !== undefined ? { persona: known.persona } : {}),
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
input,
|
|
145
|
+
runId,
|
|
146
|
+
ctx: {
|
|
147
|
+
threadId,
|
|
148
|
+
runId,
|
|
149
|
+
...(known.agentName !== undefined ? { agentName: known.agentName } : {}),
|
|
150
|
+
emitUi: uiEmitter(scope(runId), async (component) => {
|
|
151
|
+
const writer = await this.sink.open(runId);
|
|
152
|
+
await writer.write({ t: 'event', event: { kind: 'ui', ...component } });
|
|
153
|
+
await this.store.appendMessage({
|
|
154
|
+
threadId,
|
|
155
|
+
role: 'assistant',
|
|
156
|
+
content: '',
|
|
157
|
+
runId,
|
|
158
|
+
ui: [component],
|
|
159
|
+
});
|
|
160
|
+
await this.uiPushed(input, runId, component);
|
|
161
|
+
}),
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Spend one approval of `tool` in `runId` — what the MCP endpoint asks before it runs an `action`.
|
|
167
|
+
* OpenCode asks the person (or the policy answers) before it calls an action tool; the endpoint
|
|
168
|
+
* runs it only against an approval this engine saw granted, so a caller that skipped OpenCode's
|
|
169
|
+
* permission rules (anything that got hold of the token) cannot run an action unapproved. The
|
|
170
|
+
* approval is the run's own: live in this process, else read off the run's persisted calls.
|
|
171
|
+
*/
|
|
172
|
+
async spendApproval(runId, threadId, tool) {
|
|
173
|
+
const names = new Set([tool, `${this.toolsServer}.${tool}`, `${this.toolsServer}_${tool}`]);
|
|
174
|
+
const local = (this.grants.get(runId) ?? []).find((grant) => names.has(grant.action) && !this.spent.has(grant.id));
|
|
175
|
+
if (local !== undefined) {
|
|
176
|
+
this.spent.add(local.id);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const thread = await this.store.getThread(threadId);
|
|
180
|
+
for (const message of thread?.messages ?? []) {
|
|
181
|
+
if (message.runId !== runId)
|
|
182
|
+
continue;
|
|
183
|
+
for (const call of message.toolCalls ?? []) {
|
|
184
|
+
if (!names.has(call.name) || this.spent.has(call.id))
|
|
185
|
+
continue;
|
|
186
|
+
const result = message.toolResults?.find((r) => r.id === call.id);
|
|
187
|
+
const approved = result?.output?.approved === true;
|
|
188
|
+
if (approved) {
|
|
189
|
+
this.spent.add(call.id);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Push a component into the run an OpenCode session is serving, for the host's own trusted
|
|
198
|
+
* callers (no caller check — the MCP endpoint checks its own). `false` when no run is live on
|
|
199
|
+
* that session in this process.
|
|
200
|
+
*/
|
|
201
|
+
async pushToSession(sessionId, component) {
|
|
202
|
+
for (const [runId, live] of this.live) {
|
|
203
|
+
if (live.handle.sessionId !== sessionId)
|
|
204
|
+
continue;
|
|
205
|
+
await live.turn.pushUi(component);
|
|
206
|
+
await this.uiPushed(live.input, runId, component);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
async uiPushed(input, runId, component) {
|
|
212
|
+
if (this.host.onUi === undefined)
|
|
213
|
+
return;
|
|
214
|
+
try {
|
|
215
|
+
await this.host.onUi({ input, runId, component });
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
openCodeLog.warn(`onUi failed: ${errorText(error, 'error')}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async threadOfSession(actor, sessionId) {
|
|
222
|
+
try {
|
|
223
|
+
const server = await this.host.server(actor);
|
|
224
|
+
const info = await server.client.session.get?.({ sessionID: sessionId });
|
|
225
|
+
const threadId = info?.metadata?.threadId;
|
|
226
|
+
return typeof threadId === 'string' ? threadId : undefined;
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async activeRun(threadId) {
|
|
233
|
+
if (isChatQueueStore(this.store))
|
|
234
|
+
return this.store.activeRunForThread(threadId);
|
|
235
|
+
return (await this.store.getThread(threadId))?.activeRunId ?? null;
|
|
236
|
+
}
|
|
237
|
+
// ---- begin -----------------------------------------------------------------------------------
|
|
238
|
+
/**
|
|
239
|
+
* Open the run's row, persist the user message (or rewind the thread for a regenerate), find or
|
|
240
|
+
* create the thread's session, and refresh what the session is told this turn.
|
|
241
|
+
*/
|
|
242
|
+
async begin(runId, input, durable = false) {
|
|
243
|
+
// A durable `begin` re-run after a crash finds its row already there.
|
|
244
|
+
if ((await this.store.getRunActorRef(runId)) === null) {
|
|
245
|
+
await this.store.recordRunStart({
|
|
246
|
+
runId,
|
|
247
|
+
threadId: input.threadId,
|
|
248
|
+
actor: input.actor,
|
|
249
|
+
durable,
|
|
250
|
+
...(input.agentName !== undefined ? { agentName: input.agentName } : {}),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const earlier = await this.prepareThread(runId, input);
|
|
254
|
+
const server = await this.host.server(input.actor);
|
|
255
|
+
const known = await this.sessions.get(input.threadId);
|
|
256
|
+
const reusable = known !== null &&
|
|
257
|
+
known.serverKey === server.key &&
|
|
258
|
+
known.bootId === server.bootId &&
|
|
259
|
+
((await this.host.reuse?.({ input, runId, session: known })) ?? true);
|
|
260
|
+
let handle;
|
|
261
|
+
let created = false;
|
|
262
|
+
if (known !== null && reusable) {
|
|
263
|
+
handle = {
|
|
264
|
+
sessionId: known.sessionId,
|
|
265
|
+
serverKey: known.serverKey,
|
|
266
|
+
...(known.bootId !== undefined ? { bootId: known.bootId } : {}),
|
|
267
|
+
...(known.directory !== undefined ? { directory: known.directory } : {}),
|
|
268
|
+
};
|
|
269
|
+
if (input.regenerate === true)
|
|
270
|
+
await this.revertLastExchange(server.client, handle.sessionId);
|
|
271
|
+
await this.addTools(input, server, handle.directory, false);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
handle = await this.createSession(runId, input, server, earlier);
|
|
275
|
+
created = true;
|
|
276
|
+
}
|
|
277
|
+
const persona = typeof input.persona === 'string' ? input.persona : input.persona?.id;
|
|
278
|
+
if (created || known?.agentName !== input.agentName || known?.persona !== persona) {
|
|
279
|
+
await this.sessions.set(input.threadId, {
|
|
280
|
+
...handle,
|
|
281
|
+
...(input.agentName !== undefined ? { agentName: input.agentName } : {}),
|
|
282
|
+
...(persona !== undefined ? { persona } : {}),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
await this.host.beforePrompt?.({
|
|
286
|
+
input,
|
|
287
|
+
runId,
|
|
288
|
+
sessionId: handle.sessionId,
|
|
289
|
+
client: server.client,
|
|
290
|
+
created,
|
|
291
|
+
});
|
|
292
|
+
await this.refreshInstructions(runId, input, server.client, handle.sessionId);
|
|
293
|
+
return handle;
|
|
294
|
+
}
|
|
295
|
+
async createSession(runId, input, server, earlier, note) {
|
|
296
|
+
const { client } = server;
|
|
297
|
+
const context = { input, runId };
|
|
298
|
+
const spec = await this.host.session(context);
|
|
299
|
+
const directory = spec.location?.directory;
|
|
300
|
+
const skills = await this.writeSkills(input, client, directory);
|
|
301
|
+
const sessionId = (await client.session.create({
|
|
302
|
+
...spec,
|
|
303
|
+
permissions: [
|
|
304
|
+
...(spec.permissions ?? []),
|
|
305
|
+
...this.toolRules(input),
|
|
306
|
+
...skills.map((name) => ({
|
|
307
|
+
action: 'skill',
|
|
308
|
+
resource: name,
|
|
309
|
+
effect: 'allow',
|
|
310
|
+
})),
|
|
311
|
+
],
|
|
312
|
+
metadata: { ...(spec.metadata ?? {}), threadId: input.threadId },
|
|
313
|
+
})).id;
|
|
314
|
+
const handle = {
|
|
315
|
+
sessionId,
|
|
316
|
+
serverKey: server.key,
|
|
317
|
+
...(server.bootId !== undefined ? { bootId: server.bootId } : {}),
|
|
318
|
+
...(directory !== undefined ? { directory } : {}),
|
|
319
|
+
};
|
|
320
|
+
await this.sessions.set(input.threadId, handle);
|
|
321
|
+
await this.addTools(input, server, directory, true);
|
|
322
|
+
await this.host.prepare?.({ ...context, sessionId, client });
|
|
323
|
+
const transcript = transcriptOf(earlier, this.settings.historyMessages ?? 20);
|
|
324
|
+
if (transcript || note) {
|
|
325
|
+
await client.session.instructions.entry.put({
|
|
326
|
+
sessionID: sessionId,
|
|
327
|
+
key: 'agora.history',
|
|
328
|
+
value: [
|
|
329
|
+
transcript
|
|
330
|
+
? `The conversation so far (continue it; do not repeat it):\n${transcript}`
|
|
331
|
+
: '',
|
|
332
|
+
note ?? '',
|
|
333
|
+
]
|
|
334
|
+
.filter(Boolean)
|
|
335
|
+
.join('\n\n'),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return handle;
|
|
339
|
+
}
|
|
340
|
+
/** The user message, as the loop persists it (or, on a regenerate, the thread rewound to it). */
|
|
341
|
+
async prepareThread(runId, input) {
|
|
342
|
+
const thread = await this.store.getThread(input.threadId);
|
|
343
|
+
const messages = thread?.messages ?? [];
|
|
344
|
+
if (input.regenerate === true) {
|
|
345
|
+
let lastUser = -1;
|
|
346
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
347
|
+
if (messages[i]?.role === 'user') {
|
|
348
|
+
lastUser = i;
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const firstDropped = messages[lastUser + 1];
|
|
353
|
+
if (firstDropped !== undefined) {
|
|
354
|
+
await this.store.truncateFrom(input.threadId, firstDropped.id);
|
|
355
|
+
}
|
|
356
|
+
return messages.slice(0, Math.max(lastUser, 0));
|
|
357
|
+
}
|
|
358
|
+
await this.store.appendMessage({
|
|
359
|
+
threadId: input.threadId,
|
|
360
|
+
role: 'user',
|
|
361
|
+
content: input.userText,
|
|
362
|
+
runId,
|
|
363
|
+
...(typeof input.persona === 'string' ? { persona: input.persona } : {}),
|
|
364
|
+
...(input.attachments !== undefined ? { attachments: input.attachments } : {}),
|
|
365
|
+
});
|
|
366
|
+
if (thread !== null && (thread.title === '' || thread.title === 'New chat')) {
|
|
367
|
+
await this.store.setTitle(input.threadId, titleOf(input.userText));
|
|
368
|
+
}
|
|
369
|
+
return messages;
|
|
370
|
+
}
|
|
371
|
+
/** Rewind the session to before its last user message, so the regenerated answer replaces it. */
|
|
372
|
+
async revertLastExchange(client, sessionId) {
|
|
373
|
+
if (client.message === undefined || client.session.revert === undefined)
|
|
374
|
+
return;
|
|
375
|
+
const page = await client.message.list({ sessionID: sessionId, order: 'desc', limit: 50 });
|
|
376
|
+
const last = page.data.find((m) => m.type === 'user');
|
|
377
|
+
if (last === undefined)
|
|
378
|
+
return;
|
|
379
|
+
await client.session.revert.stage({ sessionID: sessionId, messageID: last.id, files: false });
|
|
380
|
+
await client.session.revert.commit({ sessionID: sessionId });
|
|
381
|
+
}
|
|
382
|
+
/** The agent's prompt, the memory block and the host's entries — refreshed every turn. */
|
|
383
|
+
async refreshInstructions(runId, input, client, sessionId) {
|
|
384
|
+
const memory = await this.memoryBlock(input);
|
|
385
|
+
const entries = {
|
|
386
|
+
[this.settings.systemKey ?? 'agora.system']: await this.systemPrompt(input),
|
|
387
|
+
...(memory !== undefined ? { 'agora.memory': memory } : {}),
|
|
388
|
+
...((await this.host.instructions?.({ input, runId, sessionId })) ?? {}),
|
|
389
|
+
};
|
|
390
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
391
|
+
await client.session.instructions.entry.put({ sessionID: sessionId, key, value });
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The agent's prompt as the loop resolves it: a persona's prompt stands in for the base (a
|
|
396
|
+
* builder is handed the base as `basePrompt`), else the agent's `systemPrompt`.
|
|
397
|
+
*/
|
|
398
|
+
async systemPrompt(input) {
|
|
399
|
+
const deps = this.depsFor(input);
|
|
400
|
+
const persona = personaOf(deps, input.persona);
|
|
401
|
+
const ctx = {
|
|
402
|
+
actor: input.actor,
|
|
403
|
+
...(persona !== undefined ? { persona } : {}),
|
|
404
|
+
...(input.pageContext !== undefined ? { pageContext: input.pageContext } : {}),
|
|
405
|
+
...(input.uiCapabilities !== undefined ? { uiCapabilities: input.uiCapabilities } : {}),
|
|
406
|
+
};
|
|
407
|
+
const resolve = async (prompt, basePrompt) => typeof prompt === 'function' ? prompt({ ...ctx, basePrompt }) : prompt;
|
|
408
|
+
const own = persona?.systemPrompt;
|
|
409
|
+
if (own === undefined)
|
|
410
|
+
return resolve(deps.systemPrompt, '');
|
|
411
|
+
const base = typeof own === 'function' ? await resolve(deps.systemPrompt, '') : '';
|
|
412
|
+
return resolve(own, base);
|
|
413
|
+
}
|
|
414
|
+
scopeContext(input) {
|
|
415
|
+
return {
|
|
416
|
+
actor: input.actor,
|
|
417
|
+
threadId: input.threadId,
|
|
418
|
+
...(input.agentName !== undefined ? { agentName: input.agentName } : {}),
|
|
419
|
+
...(input.pageContext !== undefined ? { pageContext: input.pageContext } : {}),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/** What is on file about the actor (`memory` in the config); writable when `remember` is served. */
|
|
423
|
+
async memoryBlock(input) {
|
|
424
|
+
const memory = this.depsFor(input).memory;
|
|
425
|
+
if (memory === undefined)
|
|
426
|
+
return undefined;
|
|
427
|
+
const digest = await offerMemories({
|
|
428
|
+
config: memory,
|
|
429
|
+
ctx: this.scopeContext(input),
|
|
430
|
+
query: input.userText,
|
|
431
|
+
});
|
|
432
|
+
if (digest.entries.length === 0)
|
|
433
|
+
return undefined;
|
|
434
|
+
return buildMemoryBlock({
|
|
435
|
+
entries: digest.entries,
|
|
436
|
+
writable: this.memoryWritable(input),
|
|
437
|
+
partial: digest.omitted > 0 || digest.recalled === true,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* The agent's skills (`skills` in the config) as `SKILL.md` files under the session's directory,
|
|
442
|
+
* where OpenCode's own `skill` tool finds them. Returns the names written.
|
|
443
|
+
*/
|
|
444
|
+
async writeSkills(input, client, directory) {
|
|
445
|
+
const skills = this.depsFor(input).skills;
|
|
446
|
+
if (skills === undefined || client.file === undefined || directory === undefined)
|
|
447
|
+
return [];
|
|
448
|
+
const ctx = this.scopeContext(input);
|
|
449
|
+
const offer = await offerSkills(skills, ctx);
|
|
450
|
+
const dir = this.settings.skillsDir ?? '.opencode/skills';
|
|
451
|
+
const written = [];
|
|
452
|
+
for (const entry of offer.entries) {
|
|
453
|
+
const body = await skills.provider.load({ name: entry.name, scope: entry.scope, ctx });
|
|
454
|
+
if (body === null)
|
|
455
|
+
continue;
|
|
456
|
+
const file = `---\nname: ${entry.name}\ndescription: ${JSON.stringify(entry.description)}\n---\n\n${body}\n`;
|
|
457
|
+
await client.file.write({
|
|
458
|
+
location: { directory },
|
|
459
|
+
path: `${dir}/${entry.name}/SKILL.md`,
|
|
460
|
+
payload: new TextEncoder().encode(file),
|
|
461
|
+
});
|
|
462
|
+
written.push(entry.name);
|
|
463
|
+
}
|
|
464
|
+
return written;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Permission rules for the app's tools: the MCP server allowed as a whole (OpenCode offers a
|
|
468
|
+
* server's tools only when the server itself is allowed), then each `action` tool asked — the last
|
|
469
|
+
* matching rule wins, so an action still lands on an approval card. Tools outside the agent's
|
|
470
|
+
* allow-list, and the kinds only the loop serves, are denied.
|
|
471
|
+
*/
|
|
472
|
+
toolRules(input) {
|
|
473
|
+
if (this.settings.tools === undefined)
|
|
474
|
+
return [];
|
|
475
|
+
const server = this.toolsServer;
|
|
476
|
+
const allow = this.allowedTools(input);
|
|
477
|
+
const both = (name) => [`${server}.${name}`, `${server}_${name}`];
|
|
478
|
+
const rules = [
|
|
479
|
+
{ action: `${server}*`, resource: '*', effect: 'allow' },
|
|
480
|
+
];
|
|
481
|
+
for (const spec of this.registry.allSpecs()) {
|
|
482
|
+
const offered = (allow === undefined || allow.includes(spec.name)) && !LOOP_SERVED_KINDS.has(spec.kind);
|
|
483
|
+
if (offered && spec.kind === 'read')
|
|
484
|
+
continue;
|
|
485
|
+
const effect = offered && spec.kind === 'action' ? 'ask' : 'deny';
|
|
486
|
+
for (const action of both(spec.name))
|
|
487
|
+
rules.push({ action, resource: '*', effect });
|
|
488
|
+
}
|
|
489
|
+
return rules;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Register the tools endpoint at the session's location, with a bearer token for the turn's
|
|
493
|
+
* actor. On a new session always; on a kept one when this process has not registered it yet or
|
|
494
|
+
* its token is half-way through its life.
|
|
495
|
+
*/
|
|
496
|
+
async addTools(input, server, directory, fresh) {
|
|
497
|
+
const tools = this.settings.tools;
|
|
498
|
+
if (tools === undefined || this.mintToolsToken === undefined)
|
|
499
|
+
return;
|
|
500
|
+
const { client } = server;
|
|
501
|
+
if (client.mcp === undefined) {
|
|
502
|
+
openCodeLog.warn('`tools` is set but the OpenCode client cannot add MCP servers (`mcp.add`)');
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const key = `${server.key}|${server.bootId ?? ''}|${directory ?? ''}|${input.actor.id}`;
|
|
506
|
+
const issued = this.toolsIssued.get(key);
|
|
507
|
+
const ttl = tools.ttlMs ?? DEFAULT_TOOLS_TTL_MS;
|
|
508
|
+
if (!fresh && issued !== undefined && Date.now() - issued < ttl / 2)
|
|
509
|
+
return;
|
|
510
|
+
await client.mcp.add({
|
|
511
|
+
server: this.toolsServer,
|
|
512
|
+
...(directory !== undefined ? { location: { directory } } : {}),
|
|
513
|
+
config: {
|
|
514
|
+
type: 'remote',
|
|
515
|
+
url: tools.url,
|
|
516
|
+
headers: { Authorization: `Bearer ${this.mintToolsToken(input.actor, server.key)}` },
|
|
517
|
+
oauth: false,
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
this.toolsIssued.set(key, Date.now());
|
|
521
|
+
}
|
|
522
|
+
// ---- prompt / observe / reply ----------------------------------------------------------------
|
|
523
|
+
/** Start listening to the session (so nothing it says is missed), then send the user message. */
|
|
524
|
+
async prompt(runId, input, handle) {
|
|
525
|
+
const live = await this.ensureLive(runId, input, handle, false);
|
|
526
|
+
try {
|
|
527
|
+
const prompt = (await this.host.promptFor?.({
|
|
528
|
+
input,
|
|
529
|
+
runId,
|
|
530
|
+
sessionId: handle.sessionId,
|
|
531
|
+
})) ?? {
|
|
532
|
+
text: input.userText,
|
|
533
|
+
...(input.attachments?.length && this.host.files
|
|
534
|
+
? { files: await this.host.files({ input, runId }) }
|
|
535
|
+
: {}),
|
|
536
|
+
};
|
|
537
|
+
await live.client.session.prompt({
|
|
538
|
+
sessionID: handle.sessionId,
|
|
539
|
+
text: prompt.text,
|
|
540
|
+
...(prompt.files?.length ? { files: prompt.files } : {}),
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
live.turn.fail(`OpenCode refused the message: ${errorText(error, 'unknown error')}`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Wait for the turn's next milestone. In a process that was not following the turn (a resumed
|
|
549
|
+
* durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
|
|
550
|
+
* `session.wait` is the safety net for a terminal event that was missed.
|
|
551
|
+
*/
|
|
552
|
+
async observe(runId, input, handle) {
|
|
553
|
+
const live = await this.ensureLive(runId, input, handle, true);
|
|
554
|
+
const { turn, client } = live;
|
|
555
|
+
if (turn.hasMilestone())
|
|
556
|
+
return this.reached(runId, input, handle, await turn.next());
|
|
557
|
+
// Set once this observation has its milestone: the safety nets below only act while it waits.
|
|
558
|
+
let done = false;
|
|
559
|
+
const next = turn.next().then((milestone) => {
|
|
560
|
+
done = true;
|
|
561
|
+
return milestone;
|
|
562
|
+
});
|
|
563
|
+
const timeoutMs = this.settings.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
|
|
564
|
+
const timer = setTimeout(() => {
|
|
565
|
+
if (!done)
|
|
566
|
+
turn.fail('Timed out waiting for OpenCode.');
|
|
567
|
+
}, timeoutMs);
|
|
568
|
+
timer.unref?.();
|
|
569
|
+
if (client.session.wait !== undefined) {
|
|
570
|
+
void client.session
|
|
571
|
+
.wait({ sessionID: handle.sessionId })
|
|
572
|
+
.then(() => new Promise((resolve) => setTimeout(resolve, 2_000)))
|
|
573
|
+
.then(async () => {
|
|
574
|
+
if (done || turn.finished)
|
|
575
|
+
return;
|
|
576
|
+
await turn.catchUp().catch(() => undefined);
|
|
577
|
+
// Let a milestone the catch-up reached settle this observation first.
|
|
578
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
579
|
+
// Idle with nothing open: the execution ended while its last events were lost.
|
|
580
|
+
if (!done && !turn.finished) {
|
|
581
|
+
turn.handle({
|
|
582
|
+
type: 'session.execution.succeeded',
|
|
583
|
+
data: { sessionID: handle.sessionId },
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
})
|
|
587
|
+
.catch(() => undefined);
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
return await this.reached(runId, input, handle, await next);
|
|
591
|
+
}
|
|
592
|
+
finally {
|
|
593
|
+
clearTimeout(timer);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Hand a person's answer back to OpenCode. When the server restarted while the turn waited, the
|
|
598
|
+
* request is gone with it: a new session is opened, told what the person decided, and prompted
|
|
599
|
+
* to go on.
|
|
600
|
+
*/
|
|
601
|
+
async reply(runId, input, handle, ask, reply) {
|
|
602
|
+
const server = await this.host.server(input.actor);
|
|
603
|
+
if (server.key !== handle.serverKey || server.bootId !== handle.bootId) {
|
|
604
|
+
return this.continueAfterRestart(runId, input, server, ask, reply);
|
|
605
|
+
}
|
|
606
|
+
const live = await this.ensureLive(runId, input, handle, false);
|
|
607
|
+
await live.turn.decide(ask, reply);
|
|
608
|
+
return handle;
|
|
609
|
+
}
|
|
610
|
+
async continueAfterRestart(runId, input, server, ask, reply) {
|
|
611
|
+
const approved = 'approved' in reply && reply.approved === true;
|
|
612
|
+
const note = ask.kind === 'approval'
|
|
613
|
+
? approved
|
|
614
|
+
? `The user approved the action ${ask.action} you asked permission for. Continue the task.`
|
|
615
|
+
: `The user did not approve the action ${ask.action}. Continue without it and explain.`
|
|
616
|
+
: `The user answered your question: ${JSON.stringify('answers' in reply ? reply.answers : {})}. Continue.`;
|
|
617
|
+
const thread = await this.store.getThread(input.threadId);
|
|
618
|
+
const handle = await this.createSession(runId, input, server, thread?.messages ?? [], note);
|
|
619
|
+
await this.refreshInstructions(runId, input, server.client, handle.sessionId);
|
|
620
|
+
this.drop(runId);
|
|
621
|
+
const live = await this.ensureLive(runId, input, handle, false);
|
|
622
|
+
// The old request's card settles as decided; OpenCode is told in the note.
|
|
623
|
+
await live.turn.decide(ask, reply, { tellOpenCode: false });
|
|
624
|
+
await live.client.session.prompt({ sessionID: handle.sessionId, text: note });
|
|
625
|
+
return handle;
|
|
626
|
+
}
|
|
627
|
+
/** Interrupt the thread's session (cancel). */
|
|
628
|
+
async interrupt(input) {
|
|
629
|
+
const known = await this.sessions.get(input.threadId);
|
|
630
|
+
if (known === null)
|
|
631
|
+
return;
|
|
632
|
+
const server = await this.host.server(input.actor);
|
|
633
|
+
if (server.key !== known.serverKey || server.bootId !== known.bootId)
|
|
634
|
+
return;
|
|
635
|
+
await server.client.session.interrupt({ sessionID: known.sessionId });
|
|
636
|
+
}
|
|
637
|
+
async ensureLive(runId, input, handle, catchUp) {
|
|
638
|
+
const current = this.live.get(runId);
|
|
639
|
+
if (current !== undefined && current.handle.sessionId === handle.sessionId)
|
|
640
|
+
return current;
|
|
641
|
+
current?.stop();
|
|
642
|
+
const server = await this.host.server(input.actor);
|
|
643
|
+
const deps = this.depsFor(input);
|
|
644
|
+
const policy = deps.approvalPolicy ?? new DefaultApprovalPolicy();
|
|
645
|
+
const writer = await this.sink.open(runId);
|
|
646
|
+
const turn = new OpenCodeTurn({
|
|
647
|
+
runId,
|
|
648
|
+
input,
|
|
649
|
+
client: server.client,
|
|
650
|
+
sessionId: handle.sessionId,
|
|
651
|
+
writer,
|
|
652
|
+
store: this.store,
|
|
653
|
+
approvalFor: async (action) => policy.requirementFor({ name: action, kind: 'action' }, input.actor, {
|
|
654
|
+
threadId: input.threadId,
|
|
655
|
+
runId,
|
|
656
|
+
...(input.agentName !== undefined ? { agentName: input.agentName } : {}),
|
|
657
|
+
}),
|
|
658
|
+
modelLabel: input.model ?? 'opencode',
|
|
659
|
+
onGranted: (action, id) => {
|
|
660
|
+
const grants = this.grants.get(runId) ?? [];
|
|
661
|
+
grants.push({ action, id });
|
|
662
|
+
this.grants.set(runId, grants);
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
const stop = await this.events.listen(server.key, server.client, handle.sessionId, (event) => turn.handle(event));
|
|
666
|
+
const live = { turn, input, handle, client: server.client, stop };
|
|
667
|
+
this.live.set(runId, live);
|
|
668
|
+
if (catchUp && current === undefined)
|
|
669
|
+
await turn.catchUp().catch(() => undefined);
|
|
670
|
+
return live;
|
|
671
|
+
}
|
|
672
|
+
/** Stop following a run in this process. */
|
|
673
|
+
drop(runId) {
|
|
674
|
+
this.live.get(runId)?.stop();
|
|
675
|
+
this.live.delete(runId);
|
|
676
|
+
}
|
|
677
|
+
// ---- settle ----------------------------------------------------------------------------------
|
|
678
|
+
/** A person was asked something: tell the host (cards in other channels, wake-ups). */
|
|
679
|
+
async reached(runId, input, handle, milestone) {
|
|
680
|
+
if (milestone.kind === 'ask' && this.host.onAsk !== undefined) {
|
|
681
|
+
try {
|
|
682
|
+
await this.host.onAsk({ input, runId, sessionId: handle.sessionId, ask: milestone.ask });
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
openCodeLog.warn(`onAsk failed: ${errorText(error, 'error')}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return milestone;
|
|
689
|
+
}
|
|
690
|
+
/** What the run produced, as the host's settle hooks see it. */
|
|
691
|
+
async runResult(runId, input, outcome, durationMs) {
|
|
692
|
+
const thread = await this.store.getThread(input.threadId);
|
|
693
|
+
const messages = (thread?.messages ?? []).filter((m) => m.role === 'assistant' && m.runId === runId);
|
|
694
|
+
const usage = { inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
695
|
+
for (const m of messages) {
|
|
696
|
+
usage.inputTokens += m.usage?.inputTokens ?? 0;
|
|
697
|
+
usage.outputTokens += m.usage?.outputTokens ?? 0;
|
|
698
|
+
usage.costUsd += m.usage?.costUsd ?? 0;
|
|
699
|
+
}
|
|
700
|
+
return {
|
|
701
|
+
runId,
|
|
702
|
+
input,
|
|
703
|
+
outcome,
|
|
704
|
+
text: messages
|
|
705
|
+
.map((m) => m.content)
|
|
706
|
+
.filter(Boolean)
|
|
707
|
+
.join('\n\n'),
|
|
708
|
+
messages,
|
|
709
|
+
usage,
|
|
710
|
+
durationMs,
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* The host's last word on the answer before the stream ends: components to append (a guardrail
|
|
715
|
+
* notice), or, for a failed run, the error the person reads instead of OpenCode's.
|
|
716
|
+
*/
|
|
717
|
+
async amend(writer, result) {
|
|
718
|
+
if (this.host.beforeSettle === undefined)
|
|
719
|
+
return {};
|
|
720
|
+
let amendment = {};
|
|
721
|
+
try {
|
|
722
|
+
amendment = (await this.host.beforeSettle(result)) ?? {};
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
openCodeLog.warn(`beforeSettle failed: ${errorText(error, 'error')}`);
|
|
726
|
+
}
|
|
727
|
+
const ui = amendment.ui ?? [];
|
|
728
|
+
const last = result.messages.at(-1);
|
|
729
|
+
if (ui.length > 0) {
|
|
730
|
+
for (const component of ui) {
|
|
731
|
+
await writer.write({ t: 'event', event: { kind: 'ui', ...component } });
|
|
732
|
+
}
|
|
733
|
+
if (last !== undefined)
|
|
734
|
+
await this.store.setMessageUi?.(last.id, [...(last.ui ?? []), ...ui]);
|
|
735
|
+
}
|
|
736
|
+
return amendment;
|
|
737
|
+
}
|
|
738
|
+
/** Delivery, telemetry, spend: after the run settled. Errors are the host's, never the run's. */
|
|
739
|
+
async settled(result) {
|
|
740
|
+
try {
|
|
741
|
+
await this.host.onSettled?.(result);
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
openCodeLog.warn(`onSettled failed: ${errorText(error, 'error')}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
forget(runId) {
|
|
748
|
+
this.drop(runId);
|
|
749
|
+
for (const grant of this.grants.get(runId) ?? [])
|
|
750
|
+
this.spent.delete(grant.id);
|
|
751
|
+
this.grants.delete(runId);
|
|
752
|
+
}
|
|
753
|
+
/** End the run's stream and bookkeeping for how its execution ended. */
|
|
754
|
+
async settle(runId, input, outcome, durationMs) {
|
|
755
|
+
if (outcome.status === 'failed') {
|
|
756
|
+
await this.settleFailed(runId, input, outcome.error, durationMs);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
const totals = this.live.get(runId)?.turn.totals();
|
|
760
|
+
this.forget(runId);
|
|
761
|
+
const writer = await this.sink.open(runId);
|
|
762
|
+
const result = await this.runResult(runId, input, outcome, durationMs);
|
|
763
|
+
await this.amend(writer, result);
|
|
764
|
+
if (outcome.status === 'interrupted') {
|
|
765
|
+
await this.store.recordRunEnd({ runId, status: 'cancelled' });
|
|
766
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId, RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
|
|
767
|
+
await this.handoff(writer, input, runId, 'cancelled');
|
|
768
|
+
await releaseThreadRun(this.store, input.threadId, runId);
|
|
769
|
+
await writer.write({ t: 'event', event: { kind: 'cancelled' } });
|
|
770
|
+
await writer.end();
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
await this.store.recordRunEnd({
|
|
774
|
+
runId,
|
|
775
|
+
status: 'completed',
|
|
776
|
+
finishedAt: Date.now(),
|
|
777
|
+
stepCount: totals?.steps ?? 0,
|
|
778
|
+
inputTokens: totals?.inputTokens ?? result.usage.inputTokens,
|
|
779
|
+
outputTokens: totals?.outputTokens ?? result.usage.outputTokens,
|
|
780
|
+
...(totals?.costUsd !== undefined ? { costUsd: totals.costUsd } : {}),
|
|
781
|
+
});
|
|
782
|
+
await this.handoff(writer, input, runId, 'completed');
|
|
783
|
+
await releaseThreadRun(this.store, input.threadId, runId);
|
|
784
|
+
await writer.end();
|
|
785
|
+
}
|
|
786
|
+
await this.settled(result);
|
|
787
|
+
}
|
|
788
|
+
async settleFailed(runId, input, error, durationMs = 0) {
|
|
789
|
+
this.forget(runId);
|
|
790
|
+
const writer = await this.sink.open(runId);
|
|
791
|
+
const result = await this.runResult(runId, input, { status: 'failed', error }, durationMs);
|
|
792
|
+
const worded = (await this.amend(writer, result)).error;
|
|
793
|
+
await this.store.recordRunEnd({ runId, status: 'failed', error });
|
|
794
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId, RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
|
|
795
|
+
await this.handoff(writer, input, runId, 'failed', error);
|
|
796
|
+
await releaseThreadRun(this.store, input.threadId, runId);
|
|
797
|
+
// The host's wording is for the person and shown as is; OpenCode's own error follows the
|
|
798
|
+
// library's rule (shown outside production, logged everywhere).
|
|
799
|
+
await writer.write(worded !== undefined
|
|
800
|
+
? { t: 'error', code: 'run_failed', message: worded }
|
|
801
|
+
: streamErrorFrame(new Error(error), runId));
|
|
802
|
+
await writer.end();
|
|
803
|
+
await this.settled({ ...result, outcome: { status: 'failed', error: worded ?? error } });
|
|
804
|
+
}
|
|
805
|
+
/** As `InlineAgentRunner`: hand the thread to its next queued message, and say so on the stream. */
|
|
806
|
+
async handoff(writer, input, runId, outcome, error) {
|
|
807
|
+
const queue = this.queue;
|
|
808
|
+
const startNext = this.startNext;
|
|
809
|
+
if (queue === undefined || !queue.supported || startNext === undefined)
|
|
810
|
+
return;
|
|
811
|
+
if (!isThreadTurn(input))
|
|
812
|
+
return;
|
|
813
|
+
try {
|
|
814
|
+
const frame = await queue.handoff({ threadId: input.threadId, runId, outcome, ...(error !== undefined ? { error } : {}) }, (next, nextRunId) => startNext(next, nextRunId));
|
|
815
|
+
if (frame !== undefined)
|
|
816
|
+
await writer.write({ t: 'event', event: frame });
|
|
817
|
+
}
|
|
818
|
+
catch (failure) {
|
|
819
|
+
openCodeLog.error(`could not advance the queue of thread ${input.threadId} after run ${runId}: ${errorText(failure, 'error')}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
/** The persona a turn names (an id, or the whole definition a direct caller handed over). */
|
|
824
|
+
function personaOf(deps, persona) {
|
|
825
|
+
if (persona === undefined)
|
|
826
|
+
return undefined;
|
|
827
|
+
return typeof persona === 'string' ? deps.personas.get(persona) : persona;
|
|
828
|
+
}
|
|
829
|
+
function titleOf(text) {
|
|
830
|
+
const trimmed = text.trim().replace(/\s+/g, ' ');
|
|
831
|
+
return trimmed.length > 60 ? `${trimmed.slice(0, 57)}...` : trimmed || 'New chat';
|
|
832
|
+
}
|
|
833
|
+
function transcriptOf(messages, limit) {
|
|
834
|
+
return messages
|
|
835
|
+
.filter((m) => (m.role === 'user' || m.role === 'assistant') && m.content.trim())
|
|
836
|
+
.slice(-limit)
|
|
837
|
+
.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content.slice(0, 2000)}`)
|
|
838
|
+
.join('\n');
|
|
839
|
+
}
|
|
840
|
+
/** The `_meta` key OpenCode names its session with on every MCP call. */
|
|
841
|
+
export const SESSION_META_KEY = 'ai.opencode/sessionID';
|
|
842
|
+
function sessionOfMeta(meta) {
|
|
843
|
+
const value = meta?.[SESSION_META_KEY];
|
|
844
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
845
|
+
}
|
|
846
|
+
/** `ctx.emitUi` over `push`, with the library's id and snapshot rules. */
|
|
847
|
+
function uiEmitter(scope, push) {
|
|
848
|
+
let next = 0;
|
|
849
|
+
return async (component, props, options = {}) => {
|
|
850
|
+
if (typeof component !== 'string' || component.length === 0) {
|
|
851
|
+
throw new Error('emitUi: component must be a non-empty string');
|
|
852
|
+
}
|
|
853
|
+
if (typeof props !== 'object' || props === null || Array.isArray(props)) {
|
|
854
|
+
throw new Error('emitUi: props must be a JSON object');
|
|
855
|
+
}
|
|
856
|
+
const id = options.id ?? `${scope}:ui:${next++}`;
|
|
857
|
+
await push({
|
|
858
|
+
id,
|
|
859
|
+
component,
|
|
860
|
+
props: JSON.parse(JSON.stringify(props)),
|
|
861
|
+
...(options.version !== undefined ? { version: options.version } : {}),
|
|
862
|
+
...(options.fallbackText !== undefined ? { fallbackText: options.fallbackText } : {}),
|
|
863
|
+
});
|
|
864
|
+
return { id };
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
//# sourceMappingURL=turns.js.map
|