@capybara-hq/kernel 0.0.0-stage → 0.1.0-rc.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/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +20 -2
- package/bootstrap.json +22 -0
- package/dist/admin/admin.controller.d.ts +139 -0
- package/dist/admin/admin.controller.js +238 -0
- package/dist/admin/extensions.controller.d.ts +100 -0
- package/dist/admin/extensions.controller.js +226 -0
- package/dist/admin/setup.controller.d.ts +36 -0
- package/dist/admin/setup.controller.js +106 -0
- package/dist/admin/setup.service.d.ts +47 -0
- package/dist/admin/setup.service.js +90 -0
- package/dist/agents/agents.controller.d.ts +205 -0
- package/dist/agents/agents.controller.js +144 -0
- package/dist/agents/agents.service.d.ts +177 -0
- package/dist/agents/agents.service.js +677 -0
- package/dist/agents/definition.d.ts +198 -0
- package/dist/agents/definition.js +152 -0
- package/dist/agents/models.controller.d.ts +13 -0
- package/dist/agents/models.controller.js +31 -0
- package/dist/agents/templates.controller.d.ts +15 -0
- package/dist/agents/templates.controller.js +37 -0
- package/dist/agents/templates.d.ts +83 -0
- package/dist/agents/templates.js +173 -0
- package/dist/catalog/admin-catalog.controller.d.ts +143 -0
- package/dist/catalog/admin-catalog.controller.js +245 -0
- package/dist/catalog/catalog.service.d.ts +430 -0
- package/dist/catalog/catalog.service.js +882 -0
- package/dist/catalog/claude/agents.d.ts +27 -0
- package/dist/catalog/claude/agents.js +270 -0
- package/dist/catalog/claude/commands.d.ts +50 -0
- package/dist/catalog/claude/commands.js +179 -0
- package/dist/catalog/claude/manifest.d.ts +69 -0
- package/dist/catalog/claude/manifest.js +134 -0
- package/dist/catalog/claude/marketplace.d.ts +35 -0
- package/dist/catalog/claude/marketplace.js +194 -0
- package/dist/catalog/claude/mcp.d.ts +33 -0
- package/dist/catalog/claude/mcp.js +226 -0
- package/dist/catalog/claude/report.d.ts +86 -0
- package/dist/catalog/claude/report.js +121 -0
- package/dist/catalog/claude/translate.d.ts +56 -0
- package/dist/catalog/claude/translate.js +857 -0
- package/dist/catalog/cli.d.ts +46 -0
- package/dist/catalog/cli.js +211 -0
- package/dist/catalog/git-source.d.ts +127 -0
- package/dist/catalog/git-source.js +520 -0
- package/dist/catalog/marketplace-fetch.d.ts +99 -0
- package/dist/catalog/marketplace-fetch.js +203 -0
- package/dist/catalog/marketplaces.controller.d.ts +97 -0
- package/dist/catalog/marketplaces.controller.js +197 -0
- package/dist/catalog/marketplaces.service.d.ts +340 -0
- package/dist/catalog/marketplaces.service.js +1217 -0
- package/dist/catalog/org-catalog.controller.d.ts +128 -0
- package/dist/catalog/org-catalog.controller.js +277 -0
- package/dist/catalog/org-catalog.service.d.ts +246 -0
- package/dist/catalog/org-catalog.service.js +625 -0
- package/dist/catalog/plugin-import.d.ts +436 -0
- package/dist/catalog/plugin-import.js +1069 -0
- package/dist/catalog/plugin-install.d.ts +447 -0
- package/dist/catalog/plugin-install.js +1333 -0
- package/dist/catalog/plugin-package.d.ts +143 -0
- package/dist/catalog/plugin-package.js +382 -0
- package/dist/catalog/plugin-upgrade.d.ts +222 -0
- package/dist/catalog/plugin-upgrade.js +1219 -0
- package/dist/catalog/requests.service.d.ts +162 -0
- package/dist/catalog/requests.service.js +504 -0
- package/dist/catalog/schema-path.d.ts +7 -0
- package/dist/catalog/schema-path.js +63 -0
- package/dist/catalog/upgrade-targets.d.ts +59 -0
- package/dist/catalog/upgrade-targets.js +90 -0
- package/dist/catalog/versions.d.ts +31 -0
- package/dist/catalog/versions.js +44 -0
- package/dist/channels/actions.d.ts +37 -0
- package/dist/channels/actions.js +121 -0
- package/dist/channels/cards.d.ts +44 -0
- package/dist/channels/cards.js +156 -0
- package/dist/channels/connections.controller.d.ts +59 -0
- package/dist/channels/connections.controller.js +115 -0
- package/dist/channels/connections.service.d.ts +187 -0
- package/dist/channels/connections.service.js +534 -0
- package/dist/channels/features.d.ts +12 -0
- package/dist/channels/features.js +22 -0
- package/dist/channels/inbound.d.ts +120 -0
- package/dist/channels/inbound.js +332 -0
- package/dist/channels/outbound.d.ts +140 -0
- package/dist/channels/outbound.js +1125 -0
- package/dist/channels/phrases.d.ts +100 -0
- package/dist/channels/phrases.js +81 -0
- package/dist/channels/supervisor.d.ts +182 -0
- package/dist/channels/supervisor.js +625 -0
- package/dist/channels/threads.d.ts +29 -0
- package/dist/channels/threads.js +104 -0
- package/dist/channels/webhook-url.d.ts +16 -0
- package/dist/channels/webhook-url.js +34 -0
- package/dist/channels/webhook.controller.d.ts +19 -0
- package/dist/channels/webhook.controller.js +72 -0
- package/dist/channels/webhook.d.ts +73 -0
- package/dist/channels/webhook.js +274 -0
- package/dist/config/docs.d.ts +9 -0
- package/dist/config/docs.js +85 -0
- package/dist/config/env.d.ts +6 -0
- package/dist/config/env.js +73 -0
- package/dist/config/fields.d.ts +42 -0
- package/dist/config/fields.js +214 -0
- package/dist/config/file-source.d.ts +3 -0
- package/dist/config/file-source.js +31 -0
- package/dist/config/load.d.ts +20 -0
- package/dist/config/load.js +98 -0
- package/dist/config/nacos-source.d.ts +31 -0
- package/dist/config/nacos-source.js +229 -0
- package/dist/config/schema.d.ts +223 -0
- package/dist/config/schema.js +1151 -0
- package/dist/config/sources.d.ts +18 -0
- package/dist/config/sources.js +59 -0
- package/dist/config/types.d.ts +9 -0
- package/dist/config/types.js +7 -0
- package/dist/conversations/artifacts.d.ts +52 -0
- package/dist/conversations/artifacts.js +137 -0
- package/dist/conversations/attachments.controller.d.ts +28 -0
- package/dist/conversations/attachments.controller.js +96 -0
- package/dist/conversations/attachments.service.d.ts +180 -0
- package/dist/conversations/attachments.service.js +578 -0
- package/dist/conversations/conversations.controller.d.ts +288 -0
- package/dist/conversations/conversations.controller.js +607 -0
- package/dist/conversations/conversations.service.d.ts +321 -0
- package/dist/conversations/conversations.service.js +754 -0
- package/dist/conversations/event-log.d.ts +122 -0
- package/dist/conversations/event-log.js +259 -0
- package/dist/conversations/events.d.ts +856 -0
- package/dist/conversations/events.js +639 -0
- package/dist/conversations/files.d.ts +92 -0
- package/dist/conversations/files.js +213 -0
- package/dist/conversations/outputs-read.d.ts +34 -0
- package/dist/conversations/outputs-read.js +37 -0
- package/dist/conversations/queue.d.ts +169 -0
- package/dist/conversations/queue.js +527 -0
- package/dist/conversations/sandbox-status.d.ts +21 -0
- package/dist/conversations/sandbox-status.js +24 -0
- package/dist/conversations/selection.d.ts +46 -0
- package/dist/conversations/selection.js +75 -0
- package/dist/conversations/titles.d.ts +93 -0
- package/dist/conversations/titles.js +422 -0
- package/dist/ctx/bus.d.ts +16 -0
- package/dist/ctx/bus.js +101 -0
- package/dist/ctx/create-ctx.d.ts +109 -0
- package/dist/ctx/create-ctx.js +592 -0
- package/dist/ctx/kv.d.ts +4 -0
- package/dist/ctx/kv.js +31 -0
- package/dist/ctx/lock.d.ts +4 -0
- package/dist/ctx/lock.js +39 -0
- package/dist/ctx/namespace.d.ts +6 -0
- package/dist/ctx/namespace.js +12 -0
- package/dist/ctx/queue.d.ts +25 -0
- package/dist/ctx/queue.js +150 -0
- package/dist/ctx/secrets.d.ts +45 -0
- package/dist/ctx/secrets.js +175 -0
- package/dist/db/kernel-schema.d.ts +1122 -0
- package/dist/db/kernel-schema.js +5 -0
- package/dist/db/migrator.d.ts +28 -0
- package/dist/db/migrator.js +140 -0
- package/dist/db/pool.d.ts +21 -0
- package/dist/db/pool.js +28 -0
- package/dist/db/sql-statements.d.ts +34 -0
- package/dist/db/sql-statements.js +168 -0
- package/dist/errors.d.ts +210 -0
- package/dist/errors.js +212 -0
- package/dist/evals/budget.d.ts +61 -0
- package/dist/evals/budget.js +181 -0
- package/dist/evals/diff.d.ts +55 -0
- package/dist/evals/diff.js +133 -0
- package/dist/evals/judge.d.ts +53 -0
- package/dist/evals/judge.js +149 -0
- package/dist/evals/plan.d.ts +47 -0
- package/dist/evals/plan.js +77 -0
- package/dist/evals/reconstruct.d.ts +137 -0
- package/dist/evals/reconstruct.js +322 -0
- package/dist/evals/recorded-tools.d.ts +54 -0
- package/dist/evals/recorded-tools.js +167 -0
- package/dist/evals/run-replay.d.ts +86 -0
- package/dist/evals/run-replay.js +502 -0
- package/dist/evals/runs.controller.d.ts +100 -0
- package/dist/evals/runs.controller.js +199 -0
- package/dist/evals/runs.service.d.ts +275 -0
- package/dist/evals/runs.service.js +836 -0
- package/dist/evals/sets.controller.d.ts +61 -0
- package/dist/evals/sets.controller.js +183 -0
- package/dist/evals/sets.service.d.ts +128 -0
- package/dist/evals/sets.service.js +558 -0
- package/dist/evals/snapshot.d.ts +76 -0
- package/dist/evals/snapshot.js +207 -0
- package/dist/evals/step-replay.d.ts +113 -0
- package/dist/evals/step-replay.js +181 -0
- package/dist/evals/trajectory.controller.d.ts +41 -0
- package/dist/evals/trajectory.controller.js +88 -0
- package/dist/evals/trajectory.d.ts +144 -0
- package/dist/evals/trajectory.js +338 -0
- package/dist/evals/trajectory.service.d.ts +108 -0
- package/dist/evals/trajectory.service.js +323 -0
- package/dist/evals/worker.d.ts +101 -0
- package/dist/evals/worker.js +789 -0
- package/dist/extensions/admin-ops.d.ts +105 -0
- package/dist/extensions/admin-ops.js +353 -0
- package/dist/extensions/admin-view.d.ts +79 -0
- package/dist/extensions/admin-view.js +327 -0
- package/dist/extensions/approvals.d.ts +119 -0
- package/dist/extensions/approvals.js +205 -0
- package/dist/extensions/blobs.d.ts +45 -0
- package/dist/extensions/blobs.js +160 -0
- package/dist/extensions/bootstrap.d.ts +149 -0
- package/dist/extensions/bootstrap.js +441 -0
- package/dist/extensions/cli.d.ts +61 -0
- package/dist/extensions/cli.js +165 -0
- package/dist/extensions/config-json-schema.d.ts +9 -0
- package/dist/extensions/config-json-schema.js +73 -0
- package/dist/extensions/deployment-config.d.ts +138 -0
- package/dist/extensions/deployment-config.js +394 -0
- package/dist/extensions/enabled.d.ts +31 -0
- package/dist/extensions/enabled.js +55 -0
- package/dist/extensions/hooks.d.ts +161 -0
- package/dist/extensions/hooks.js +425 -0
- package/dist/extensions/host.d.ts +577 -0
- package/dist/extensions/host.js +2071 -0
- package/dist/extensions/inflight.d.ts +69 -0
- package/dist/extensions/inflight.js +277 -0
- package/dist/extensions/installs.d.ts +166 -0
- package/dist/extensions/installs.js +253 -0
- package/dist/extensions/instance.d.ts +167 -0
- package/dist/extensions/instance.js +329 -0
- package/dist/extensions/ledger.d.ts +30 -0
- package/dist/extensions/ledger.js +50 -0
- package/dist/extensions/lifecycle.d.ts +80 -0
- package/dist/extensions/lifecycle.js +152 -0
- package/dist/extensions/migrate-lock.d.ts +51 -0
- package/dist/extensions/migrate-lock.js +100 -0
- package/dist/extensions/org-config.controller.d.ts +24 -0
- package/dist/extensions/org-config.controller.js +59 -0
- package/dist/extensions/org-config.d.ts +169 -0
- package/dist/extensions/org-config.js +520 -0
- package/dist/extensions/package.d.ts +115 -0
- package/dist/extensions/package.js +246 -0
- package/dist/extensions/permission-set.d.ts +117 -0
- package/dist/extensions/permission-set.js +184 -0
- package/dist/extensions/permission-view.d.ts +194 -0
- package/dist/extensions/permission-view.js +284 -0
- package/dist/extensions/registry.d.ts +24 -0
- package/dist/extensions/registry.js +56 -0
- package/dist/extensions/resolve.d.ts +12 -0
- package/dist/extensions/resolve.js +71 -0
- package/dist/extensions/sources.d.ts +69 -0
- package/dist/extensions/sources.js +120 -0
- package/dist/extensions/sync.d.ts +40 -0
- package/dist/extensions/sync.js +242 -0
- package/dist/files/previewers.controller.d.ts +46 -0
- package/dist/files/previewers.controller.js +105 -0
- package/dist/files/previewers.d.ts +100 -0
- package/dist/files/previewers.js +250 -0
- package/dist/http/api.module.d.ts +114 -0
- package/dist/http/api.module.js +448 -0
- package/dist/http/app.d.ts +77 -0
- package/dist/http/app.js +170 -0
- package/dist/http/error.filter.d.ts +21 -0
- package/dist/http/error.filter.js +85 -0
- package/dist/http/gateway.module.d.ts +15 -0
- package/dist/http/gateway.module.js +13 -0
- package/dist/http/health.controller.d.ts +26 -0
- package/dist/http/health.controller.js +79 -0
- package/dist/http/nest-logger.d.ts +20 -0
- package/dist/http/nest-logger.js +28 -0
- package/dist/http/plugin-dispatch.d.ts +68 -0
- package/dist/http/plugin-dispatch.js +200 -0
- package/dist/http/tokens.d.ts +131 -0
- package/dist/http/tokens.js +131 -0
- package/dist/http/zod.pipe.d.ts +8 -0
- package/dist/http/zod.pipe.js +14 -0
- package/dist/identity/accounts.d.ts +93 -0
- package/dist/identity/accounts.js +367 -0
- package/dist/identity/api-tokens.d.ts +73 -0
- package/dist/identity/api-tokens.js +197 -0
- package/dist/identity/audit.d.ts +23 -0
- package/dist/identity/audit.js +41 -0
- package/dist/identity/auth.controller.d.ts +40 -0
- package/dist/identity/auth.controller.js +130 -0
- package/dist/identity/auth.guard.d.ts +41 -0
- package/dist/identity/auth.guard.js +162 -0
- package/dist/identity/binding.d.ts +47 -0
- package/dist/identity/binding.js +84 -0
- package/dist/identity/cookies.d.ts +16 -0
- package/dist/identity/cookies.js +32 -0
- package/dist/identity/credential-check.d.ts +17 -0
- package/dist/identity/credential-check.js +24 -0
- package/dist/identity/enterprise-admin.d.ts +14 -0
- package/dist/identity/enterprise-admin.js +27 -0
- package/dist/identity/external.d.ts +106 -0
- package/dist/identity/external.js +337 -0
- package/dist/identity/hash.d.ts +2 -0
- package/dist/identity/hash.js +3 -0
- package/dist/identity/login.d.ts +6 -0
- package/dist/identity/login.js +20 -0
- package/dist/identity/me.controller.d.ts +61 -0
- package/dist/identity/me.controller.js +159 -0
- package/dist/identity/passwords.d.ts +6 -0
- package/dist/identity/passwords.js +37 -0
- package/dist/identity/principal.d.ts +56 -0
- package/dist/identity/principal.js +38 -0
- package/dist/identity/rate-limit.d.ts +55 -0
- package/dist/identity/rate-limit.js +121 -0
- package/dist/identity/sessions.d.ts +84 -0
- package/dist/identity/sessions.js +306 -0
- package/dist/identity/settings.d.ts +121 -0
- package/dist/identity/settings.js +248 -0
- package/dist/identity/sso.controller.d.ts +59 -0
- package/dist/identity/sso.controller.js +192 -0
- package/dist/identity/sso.d.ts +83 -0
- package/dist/identity/sso.js +202 -0
- package/dist/inbox/inbox.service.d.ts +43 -0
- package/dist/inbox/inbox.service.js +57 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +65 -0
- package/dist/kernel.d.ts +256 -0
- package/dist/kernel.js +1326 -0
- package/dist/log.d.ts +48 -0
- package/dist/log.js +229 -0
- package/dist/models/prices.d.ts +53 -0
- package/dist/models/prices.js +121 -0
- package/dist/models/purposes.controller.d.ts +19 -0
- package/dist/models/purposes.controller.js +56 -0
- package/dist/models/purposes.d.ts +140 -0
- package/dist/models/purposes.js +267 -0
- package/dist/net/proxy.d.ts +44 -0
- package/dist/net/proxy.js +162 -0
- package/dist/net/public-address.d.ts +60 -0
- package/dist/net/public-address.js +271 -0
- package/dist/net/safe-fetch.d.ts +38 -0
- package/dist/net/safe-fetch.js +140 -0
- package/dist/orgs/directory-sync.controller.d.ts +14 -0
- package/dist/orgs/directory-sync.controller.js +40 -0
- package/dist/orgs/directory-sync.d.ts +166 -0
- package/dist/orgs/directory-sync.js +558 -0
- package/dist/orgs/invitations.controller.d.ts +58 -0
- package/dist/orgs/invitations.controller.js +137 -0
- package/dist/orgs/invitations.service.d.ts +80 -0
- package/dist/orgs/invitations.service.js +217 -0
- package/dist/orgs/members.service.d.ts +63 -0
- package/dist/orgs/members.service.js +234 -0
- package/dist/orgs/offboarding.d.ts +141 -0
- package/dist/orgs/offboarding.js +398 -0
- package/dist/orgs/org-lock.d.ts +32 -0
- package/dist/orgs/org-lock.js +46 -0
- package/dist/orgs/org.guard.d.ts +35 -0
- package/dist/orgs/org.guard.js +95 -0
- package/dist/orgs/orgs.controller.d.ts +78 -0
- package/dist/orgs/orgs.controller.js +192 -0
- package/dist/orgs/orgs.service.d.ts +61 -0
- package/dist/orgs/orgs.service.js +209 -0
- package/dist/orgs/roles.d.ts +13 -0
- package/dist/orgs/roles.js +58 -0
- package/dist/orgs/settings.controller.d.ts +20 -0
- package/dist/orgs/settings.controller.js +53 -0
- package/dist/orgs/settings.service.d.ts +120 -0
- package/dist/orgs/settings.service.js +178 -0
- package/dist/packages/npm.d.ts +50 -0
- package/dist/packages/npm.js +218 -0
- package/dist/packages/provenance.d.ts +47 -0
- package/dist/packages/provenance.js +147 -0
- package/dist/packages/tgz.d.ts +76 -0
- package/dist/packages/tgz.js +225 -0
- package/dist/runtime/abort.d.ts +32 -0
- package/dist/runtime/abort.js +71 -0
- package/dist/runtime/checkpointer.d.ts +32 -0
- package/dist/runtime/checkpointer.js +243 -0
- package/dist/runtime/context/build.d.ts +254 -0
- package/dist/runtime/context/build.js +693 -0
- package/dist/runtime/context/claude-tools.d.ts +11 -0
- package/dist/runtime/context/claude-tools.js +24 -0
- package/dist/runtime/context/compaction.d.ts +26 -0
- package/dist/runtime/context/compaction.js +195 -0
- package/dist/runtime/context/pairing.d.ts +11 -0
- package/dist/runtime/context/pairing.js +82 -0
- package/dist/runtime/context/reminders.d.ts +5 -0
- package/dist/runtime/context/reminders.js +11 -0
- package/dist/runtime/context/skills.d.ts +105 -0
- package/dist/runtime/context/skills.js +283 -0
- package/dist/runtime/context/tokens.d.ts +19 -0
- package/dist/runtime/context/tokens.js +104 -0
- package/dist/runtime/conversation-lock.d.ts +40 -0
- package/dist/runtime/conversation-lock.js +97 -0
- package/dist/runtime/hook-calls.d.ts +84 -0
- package/dist/runtime/hook-calls.js +136 -0
- package/dist/runtime/langgraph-loop.d.ts +52 -0
- package/dist/runtime/langgraph-loop.js +247 -0
- package/dist/runtime/loop.d.ts +93 -0
- package/dist/runtime/loop.js +7 -0
- package/dist/runtime/maintenance.d.ts +80 -0
- package/dist/runtime/maintenance.js +147 -0
- package/dist/runtime/media-cache.d.ts +21 -0
- package/dist/runtime/media-cache.js +51 -0
- package/dist/runtime/model-call.d.ts +176 -0
- package/dist/runtime/model-call.js +1049 -0
- package/dist/runtime/providers.d.ts +86 -0
- package/dist/runtime/providers.js +224 -0
- package/dist/runtime/quota.d.ts +29 -0
- package/dist/runtime/quota.js +70 -0
- package/dist/runtime/run-finish.d.ts +45 -0
- package/dist/runtime/run-finish.js +69 -0
- package/dist/runtime/run-queue.d.ts +121 -0
- package/dist/runtime/run-queue.js +493 -0
- package/dist/runtime/run-worker.d.ts +138 -0
- package/dist/runtime/run-worker.js +805 -0
- package/dist/runtime/stream.controller.d.ts +34 -0
- package/dist/runtime/stream.controller.js +122 -0
- package/dist/runtime/stream.d.ts +63 -0
- package/dist/runtime/stream.js +262 -0
- package/dist/runtime/subagents/child.d.ts +80 -0
- package/dist/runtime/subagents/child.js +225 -0
- package/dist/runtime/subagents/definition.d.ts +103 -0
- package/dist/runtime/subagents/definition.js +392 -0
- package/dist/runtime/subagents/dispatch.d.ts +69 -0
- package/dist/runtime/subagents/dispatch.js +502 -0
- package/dist/runtime/subagents/limits.d.ts +23 -0
- package/dist/runtime/subagents/limits.js +34 -0
- package/dist/runtime/subagents/org-agents.d.ts +50 -0
- package/dist/runtime/subagents/org-agents.js +70 -0
- package/dist/runtime/subagents/preamble.d.ts +7 -0
- package/dist/runtime/subagents/preamble.js +11 -0
- package/dist/runtime/subagents/progress.d.ts +26 -0
- package/dist/runtime/subagents/progress.js +36 -0
- package/dist/runtime/subagents/roster.d.ts +55 -0
- package/dist/runtime/subagents/roster.js +111 -0
- package/dist/runtime/subagents/skill-fork.d.ts +123 -0
- package/dist/runtime/subagents/skill-fork.js +259 -0
- package/dist/runtime/usage-report.d.ts +163 -0
- package/dist/runtime/usage-report.js +550 -0
- package/dist/runtime/waits.d.ts +233 -0
- package/dist/runtime/waits.js +883 -0
- package/dist/sandbox/leases.d.ts +169 -0
- package/dist/sandbox/leases.js +515 -0
- package/dist/sandbox/lifecycle.d.ts +21 -0
- package/dist/sandbox/lifecycle.js +68 -0
- package/dist/sandbox/sandbox.service.d.ts +249 -0
- package/dist/sandbox/sandbox.service.js +908 -0
- package/dist/skills/catalog.d.ts +149 -0
- package/dist/skills/catalog.js +405 -0
- package/dist/skills/changes.controller.d.ts +56 -0
- package/dist/skills/changes.controller.js +136 -0
- package/dist/skills/changes.service.d.ts +175 -0
- package/dist/skills/changes.service.js +805 -0
- package/dist/skills/invocable.controller.d.ts +23 -0
- package/dist/skills/invocable.controller.js +50 -0
- package/dist/skills/invocation-keys.d.ts +23 -0
- package/dist/skills/invocation-keys.js +44 -0
- package/dist/skills/invoke.d.ts +135 -0
- package/dist/skills/invoke.js +286 -0
- package/dist/skills/package.d.ts +87 -0
- package/dist/skills/package.js +227 -0
- package/dist/skills/skills.controller.d.ts +40 -0
- package/dist/skills/skills.controller.js +110 -0
- package/dist/skills/skills.service.d.ts +192 -0
- package/dist/skills/skills.service.js +567 -0
- package/dist/storage/prefix.d.ts +36 -0
- package/dist/storage/prefix.js +104 -0
- package/dist/storage/purge.d.ts +27 -0
- package/dist/storage/purge.js +75 -0
- package/dist/storage/storage.service.d.ts +44 -0
- package/dist/storage/storage.service.js +145 -0
- package/dist/text.d.ts +13 -0
- package/dist/text.js +13 -0
- package/dist/tools/approvals.controller.d.ts +53 -0
- package/dist/tools/approvals.controller.js +128 -0
- package/dist/tools/approvals.service.d.ts +172 -0
- package/dist/tools/approvals.service.js +568 -0
- package/dist/tools/breaker.d.ts +28 -0
- package/dist/tools/breaker.js +72 -0
- package/dist/tools/builtins/agent.d.ts +81 -0
- package/dist/tools/builtins/agent.js +223 -0
- package/dist/tools/builtins/ask-user.d.ts +28 -0
- package/dist/tools/builtins/ask-user.js +139 -0
- package/dist/tools/builtins/read-tool-output.d.ts +18 -0
- package/dist/tools/builtins/read-tool-output.js +108 -0
- package/dist/tools/builtins/sandbox/bash.d.ts +16 -0
- package/dist/tools/builtins/sandbox/bash.js +137 -0
- package/dist/tools/builtins/sandbox/common.d.ts +114 -0
- package/dist/tools/builtins/sandbox/common.js +304 -0
- package/dist/tools/builtins/sandbox/deliver.d.ts +5 -0
- package/dist/tools/builtins/sandbox/deliver.js +180 -0
- package/dist/tools/builtins/sandbox/edit.d.ts +4 -0
- package/dist/tools/builtins/sandbox/edit.js +98 -0
- package/dist/tools/builtins/sandbox/glob-match.d.ts +25 -0
- package/dist/tools/builtins/sandbox/glob-match.js +240 -0
- package/dist/tools/builtins/sandbox/glob.d.ts +4 -0
- package/dist/tools/builtins/sandbox/glob.js +104 -0
- package/dist/tools/builtins/sandbox/grep.d.ts +9 -0
- package/dist/tools/builtins/sandbox/grep.js +133 -0
- package/dist/tools/builtins/sandbox/index.d.ts +12 -0
- package/dist/tools/builtins/sandbox/index.js +27 -0
- package/dist/tools/builtins/sandbox/names.d.ts +7 -0
- package/dist/tools/builtins/sandbox/names.js +20 -0
- package/dist/tools/builtins/sandbox/read.d.ts +8 -0
- package/dist/tools/builtins/sandbox/read.js +121 -0
- package/dist/tools/builtins/sandbox/write.d.ts +4 -0
- package/dist/tools/builtins/sandbox/write.js +88 -0
- package/dist/tools/builtins/skill-load.d.ts +53 -0
- package/dist/tools/builtins/skill-load.js +446 -0
- package/dist/tools/builtins/skill-write.d.ts +59 -0
- package/dist/tools/builtins/skill-write.js +244 -0
- package/dist/tools/builtins/tool-search.d.ts +31 -0
- package/dist/tools/builtins/tool-search.js +171 -0
- package/dist/tools/connector-content.controller.d.ts +34 -0
- package/dist/tools/connector-content.controller.js +78 -0
- package/dist/tools/connector-content.d.ts +104 -0
- package/dist/tools/connector-content.js +267 -0
- package/dist/tools/connectors.controller.d.ts +210 -0
- package/dist/tools/connectors.controller.js +234 -0
- package/dist/tools/connectors.service.d.ts +303 -0
- package/dist/tools/connectors.service.js +1102 -0
- package/dist/tools/credentials.d.ts +139 -0
- package/dist/tools/credentials.js +351 -0
- package/dist/tools/drift.d.ts +141 -0
- package/dist/tools/drift.js +539 -0
- package/dist/tools/elicitation.d.ts +81 -0
- package/dist/tools/elicitation.js +266 -0
- package/dist/tools/endpoint.d.ts +18 -0
- package/dist/tools/endpoint.js +115 -0
- package/dist/tools/info.d.ts +67 -0
- package/dist/tools/info.js +113 -0
- package/dist/tools/naming.d.ts +27 -0
- package/dist/tools/naming.js +47 -0
- package/dist/tools/oauth/discovery.d.ts +65 -0
- package/dist/tools/oauth/discovery.js +307 -0
- package/dist/tools/oauth/flow.d.ts +63 -0
- package/dist/tools/oauth/flow.js +237 -0
- package/dist/tools/oauth/oauth.controller.d.ts +31 -0
- package/dist/tools/oauth/oauth.controller.js +95 -0
- package/dist/tools/oauth/tokens.d.ts +111 -0
- package/dist/tools/oauth/tokens.js +258 -0
- package/dist/tools/outputs.d.ts +61 -0
- package/dist/tools/outputs.js +156 -0
- package/dist/tools/pipeline.d.ts +322 -0
- package/dist/tools/pipeline.js +1236 -0
- package/dist/tools/policy.d.ts +60 -0
- package/dist/tools/policy.js +236 -0
- package/dist/tools/questions.controller.d.ts +24 -0
- package/dist/tools/questions.controller.js +58 -0
- package/dist/tools/questions.service.d.ts +103 -0
- package/dist/tools/questions.service.js +295 -0
- package/dist/tools/redact.d.ts +20 -0
- package/dist/tools/redact.js +60 -0
- package/dist/tools/registry.d.ts +246 -0
- package/dist/tools/registry.js +481 -0
- package/dist/tools/resources.d.ts +16 -0
- package/dist/tools/resources.js +76 -0
- package/dist/tools/specs.d.ts +14 -0
- package/dist/tools/specs.js +31 -0
- package/dist/tools/validate.d.ts +20 -0
- package/dist/tools/validate.js +183 -0
- package/dist/tools/view.d.ts +43 -0
- package/dist/tools/view.js +225 -0
- package/dist/ui/embed-token.d.ts +31 -0
- package/dist/ui/embed-token.js +132 -0
- package/dist/ui/registry.d.ts +58 -0
- package/dist/ui/registry.js +90 -0
- package/dist/ui/ui-stream.d.ts +37 -0
- package/dist/ui/ui-stream.js +162 -0
- package/dist/ui/views.controller.d.ts +81 -0
- package/dist/ui/views.controller.js +261 -0
- package/dist/ui/views.d.ts +155 -0
- package/dist/ui/views.js +306 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +6 -0
- package/package.json +67 -3
- package/src/migrations/0001_init.sql +8 -0
- package/src/migrations/0002_secrets.sql +8 -0
- package/src/migrations/0003_identity.sql +117 -0
- package/src/migrations/0004_plugin_org_config_nullable.sql +3 -0
- package/src/migrations/0005_agents.sql +27 -0
- package/src/migrations/0006_conversations.sql +37 -0
- package/src/migrations/0007_checkpoints.sql +26 -0
- package/src/migrations/0008_runs_trigger_seq.sql +3 -0
- package/src/migrations/0009_runs_active_unique.sql +4 -0
- package/src/migrations/0010_tools.sql +125 -0
- package/src/migrations/0011_approvals_spec.sql +10 -0
- package/src/migrations/0012_run_waits.sql +24 -0
- package/src/migrations/0013_app_tools_source.sql +4 -0
- package/src/migrations/0014_tool_specs.sql +8 -0
- package/src/migrations/0015_tool_drift.sql +20 -0
- package/src/migrations/0016_plugin_permissions.sql +11 -0
- package/src/migrations/0017_identity_sources.sql +6 -0
- package/src/migrations/0018_channels.sql +74 -0
- package/src/migrations/0019_identity_trust.sql +17 -0
- package/src/migrations/0020_channel_card_lookup.sql +10 -0
- package/src/migrations/0021_org_storage_purge.sql +9 -0
- package/src/migrations/0022_attachments.sql +29 -0
- package/src/migrations/0023_sandboxes.sql +37 -0
- package/src/migrations/0024_skills.sql +46 -0
- package/src/migrations/0025_skills_catalog_idx.sql +6 -0
- package/src/migrations/0026_skill_changes.sql +34 -0
- package/src/migrations/0027_model_prices.sql +18 -0
- package/src/migrations/0028_evals.sql +105 -0
- package/src/migrations/0029_model_purposes.sql +18 -0
- package/src/migrations/0030_plugin_installs.sql +73 -0
- package/src/migrations/0031_plugin_snapshot_source.sql +6 -0
- package/src/migrations/0032_plugin_deployment_configs.sql +10 -0
- package/src/migrations/0033_rename_apps_to_connectors.sql +25 -0
- package/src/migrations/0034_directory.sql +49 -0
- package/src/migrations/0035_catalogs.sql +40 -0
- package/src/migrations/0036_app_import_staging.sql +17 -0
- package/src/migrations/0037_directory_installs.sql +92 -0
- package/src/migrations/0038_channel_inbound_mode.sql +6 -0
- package/src/migrations/0039_channel_card_pending.sql +8 -0
- package/src/migrations/0040_usage_report_sweep.sql +10 -0
- package/src/migrations/0041_rename_plugins_to_extensions.sql +38 -0
- package/src/migrations/0042_rename_directory_to_catalog.sql +80 -0
- package/src/migrations/0043_rename_app_installs_to_plugin_installs.sql +20 -0
- package/src/migrations/0044_catalog_one_kind.sql +90 -0
- package/src/migrations/0045_plugin_dependencies.sql +9 -0
- package/src/migrations/0046_git_sources.sql +25 -0
- package/src/migrations/0047_marketplace_entry_heads.sql +7 -0
- package/src/migrations/0048_marketplace_entry_trees.sql +8 -0
- package/src/migrations/0049_plugin_upgrades.sql +37 -0
- package/src/migrations/0050_bootstrap.sql +53 -0
- package/src/migrations/0051_claude_compat.sql +26 -0
- package/src/migrations/0052_conversations_account_agent_idx.sql +5 -0
- package/src/migrations/0053_subagents.sql +73 -0
- package/src/migrations/0054_subagent_wait_expiry.sql +7 -0
- package/src/migrations/0055_subagent_parent_indexes.sql +10 -0
- package/src/migrations/0056_approval_reason.sql +3 -0
- package/src/migrations/0057_conversation_titles.sql +11 -0
- package/src/migrations/0058_conversation_selection.sql +4 -0
- package/src/migrations/0059_conversation_queue.sql +22 -0
- package/src/migrations/0060_file_previewers.sql +10 -0
- package/src/migrations/0061_queue_selection.sql +5 -0
- package/src/migrations/0062_queued_skill.sql +8 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
import { visibleTo } from "../agents/agents.service.js";
|
|
4
|
+
import { ApiError } from "../errors.js";
|
|
5
|
+
import { NO_SELECTION, planSelection, readSelection, sameModel, sameSelection, } from "./selection.js";
|
|
6
|
+
const notFound = () => new ApiError("conversation.not_found", "Conversation not found");
|
|
7
|
+
/**
|
|
8
|
+
* 用户消息的幂等键:客户端的 `clientMessageId` 加前缀,与运行流水线自用的幂等键分开,
|
|
9
|
+
* 避免客户端恰好选中内部键而命中别的事件。
|
|
10
|
+
*/
|
|
11
|
+
export const userMessageIdemKey = (clientMessageId) => `msg:${clientMessageId}`;
|
|
12
|
+
const COLUMNS = [
|
|
13
|
+
"id",
|
|
14
|
+
"org_id",
|
|
15
|
+
"agent_id",
|
|
16
|
+
"account_id",
|
|
17
|
+
"title",
|
|
18
|
+
"title_source",
|
|
19
|
+
"selection",
|
|
20
|
+
"last_seq",
|
|
21
|
+
"created_at",
|
|
22
|
+
"updated_at",
|
|
23
|
+
"parent_conversation_id",
|
|
24
|
+
"subagent_type",
|
|
25
|
+
];
|
|
26
|
+
function toView(r) {
|
|
27
|
+
return {
|
|
28
|
+
id: r.id,
|
|
29
|
+
orgId: r.org_id,
|
|
30
|
+
agentId: r.agent_id,
|
|
31
|
+
accountId: r.account_id,
|
|
32
|
+
title: r.title,
|
|
33
|
+
titleSource: r.title_source,
|
|
34
|
+
selection: r.selection ?? NO_SELECTION,
|
|
35
|
+
lastSeq: Number(r.last_seq),
|
|
36
|
+
createdAt: r.created_at,
|
|
37
|
+
updatedAt: r.updated_at,
|
|
38
|
+
parentConversationId: r.parent_conversation_id,
|
|
39
|
+
subagentType: r.subagent_type,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** The longest conversation title (characters, after trimming). */
|
|
43
|
+
export const CONVERSATION_TITLE_MAX_CHARS = 120;
|
|
44
|
+
/**
|
|
45
|
+
* A sub-agent's conversation is reached through its parent (sub-agents spec §6.1, review m6, the
|
|
46
|
+
* same rule as its trajectory): the parent is a chat conversation of the same owner whose agent is
|
|
47
|
+
* not deleted and still visible to the viewer. Any other kind passes. The child's own `agent_id`
|
|
48
|
+
* (the dispatched agent for an org agent, §5.5) is checked on top by the callers.
|
|
49
|
+
*/
|
|
50
|
+
const parentReachable = (viewerAccountId) => (eb) => eb.or([
|
|
51
|
+
eb("conversations.kind", "<>", "subagent"),
|
|
52
|
+
eb.exists(eb
|
|
53
|
+
.selectFrom("conversations as p")
|
|
54
|
+
.innerJoin("agents", "agents.id", "p.agent_id")
|
|
55
|
+
.select(sql `1`.as("one"))
|
|
56
|
+
.whereRef("p.id", "=", "conversations.parent_conversation_id")
|
|
57
|
+
.whereRef("p.org_id", "=", "conversations.org_id")
|
|
58
|
+
.where("p.account_id", "=", viewerAccountId)
|
|
59
|
+
.where("p.kind", "=", "chat")
|
|
60
|
+
.where("agents.deleted_at", "is", null)
|
|
61
|
+
.where(visibleTo(viewerAccountId))),
|
|
62
|
+
]);
|
|
63
|
+
/** The sums of the `usage` events matching `where` (over `conversation_events e`), one row. */
|
|
64
|
+
const usageSums = (where) => sql `
|
|
65
|
+
SELECT
|
|
66
|
+
coalesce(sum((e.payload->>'inputTokens')::bigint), 0) AS input_tokens,
|
|
67
|
+
coalesce(sum((e.payload->>'outputTokens')::bigint), 0) AS output_tokens,
|
|
68
|
+
coalesce(sum((e.payload->>'cacheReadTokens')::bigint), 0) AS cache_read_tokens,
|
|
69
|
+
coalesce(sum((e.payload->>'cacheWriteTokens')::bigint), 0) AS cache_write_tokens
|
|
70
|
+
FROM capybara.conversation_events e
|
|
71
|
+
WHERE e.type = 'usage' AND ${where}
|
|
72
|
+
`;
|
|
73
|
+
const toUsage = (r) => ({
|
|
74
|
+
inputTokens: Number(r.input_tokens),
|
|
75
|
+
outputTokens: Number(r.output_tokens),
|
|
76
|
+
cacheReadTokens: Number(r.cache_read_tokens),
|
|
77
|
+
cacheWriteTokens: Number(r.cache_write_tokens),
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* 会话:只有所有者可见;每次访问都实时复核 agent 对所有者仍可用(spec 3.6 R5),
|
|
81
|
+
* 不可用时会话也按不存在处理(`conversation.not_found`)。
|
|
82
|
+
*/
|
|
83
|
+
export class ConversationsService {
|
|
84
|
+
db;
|
|
85
|
+
eventLog;
|
|
86
|
+
agents;
|
|
87
|
+
runs;
|
|
88
|
+
log;
|
|
89
|
+
resolveAttachments;
|
|
90
|
+
bindFiles;
|
|
91
|
+
models;
|
|
92
|
+
invoker;
|
|
93
|
+
constructor(db, eventLog, agents, runs, log, resolveAttachments, bindFiles,
|
|
94
|
+
/** Resolves a switched-to model and its declared parameters (UI refresh B). */
|
|
95
|
+
models,
|
|
96
|
+
/** Renders a person's skill invocation (phase D); without it `skill` is refused. */
|
|
97
|
+
invoker) {
|
|
98
|
+
this.db = db;
|
|
99
|
+
this.eventLog = eventLog;
|
|
100
|
+
this.agents = agents;
|
|
101
|
+
this.runs = runs;
|
|
102
|
+
this.log = log;
|
|
103
|
+
this.resolveAttachments = resolveAttachments;
|
|
104
|
+
this.bindFiles = bindFiles;
|
|
105
|
+
this.models = models;
|
|
106
|
+
this.invoker = invoker;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 不阻塞地调用一次 `ensureRun`(打开 SSE、读事件时):它幂等,并顺带清理孤儿运行,
|
|
110
|
+
* 会话不会因为某次运行结束后的投递失败而永远停在「运行中」。失败只记日志。
|
|
111
|
+
*/
|
|
112
|
+
kickRun(conversationId) {
|
|
113
|
+
if (!this.runs)
|
|
114
|
+
return;
|
|
115
|
+
this.runs
|
|
116
|
+
.ensureRun(conversationId)
|
|
117
|
+
.catch((err) => this.log?.warn("ensureRun on conversation open failed", { conversationId, err }));
|
|
118
|
+
}
|
|
119
|
+
/** 新建会话;agent 对该账号不可用 → `agent.not_found`。 */
|
|
120
|
+
async create(orgId, accountId, agentId) {
|
|
121
|
+
await this.agents.get(orgId, agentId, accountId);
|
|
122
|
+
const row = await this.db
|
|
123
|
+
.insertInto("conversations")
|
|
124
|
+
.values({ id: randomUUID(), org_id: orgId, agent_id: agentId, account_id: accountId })
|
|
125
|
+
.returning(COLUMNS)
|
|
126
|
+
.executeTakeFirstOrThrow();
|
|
127
|
+
return toView(row);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates a conversation together with its first message (UI refresh B, create on first send):
|
|
131
|
+
* the conversation row and the `user_message` are written in one transaction, keyed by the
|
|
132
|
+
* message's `clientMessageId` (`create_request_id`), so a retried send returns the same
|
|
133
|
+
* conversation and event with `created: false`. An agent the caller cannot use →
|
|
134
|
+
* `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
|
|
135
|
+
*/
|
|
136
|
+
async createWithMessage(orgId, accountId, agentId, input) {
|
|
137
|
+
const agent = await this.agents.get(orgId, agentId, accountId);
|
|
138
|
+
const prior = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
|
|
139
|
+
if (prior)
|
|
140
|
+
return prior;
|
|
141
|
+
this.checkFilesSupported(input);
|
|
142
|
+
const next = await this.planSwitch(agent.definition, NO_SELECTION, input);
|
|
143
|
+
const extra = await this.resolveExtra(orgId, accountId, input);
|
|
144
|
+
const invoked = await this.prepareSkill(orgId, agentId, undefined, agent.definition, input);
|
|
145
|
+
const id = randomUUID();
|
|
146
|
+
const written = await this.db.transaction().execute(async (t) => {
|
|
147
|
+
const row = await t
|
|
148
|
+
.insertInto("conversations")
|
|
149
|
+
.values({
|
|
150
|
+
id,
|
|
151
|
+
org_id: orgId,
|
|
152
|
+
agent_id: agentId,
|
|
153
|
+
account_id: accountId,
|
|
154
|
+
create_request_id: input.clientMessageId,
|
|
155
|
+
})
|
|
156
|
+
.onConflict((oc) => oc
|
|
157
|
+
.columns(["org_id", "account_id", "create_request_id"])
|
|
158
|
+
.where("create_request_id", "is not", null)
|
|
159
|
+
.doNothing())
|
|
160
|
+
.returning("id")
|
|
161
|
+
.executeTakeFirst();
|
|
162
|
+
// a concurrent retry committed first: answer with its conversation below
|
|
163
|
+
if (!row)
|
|
164
|
+
return undefined;
|
|
165
|
+
const blocks = await this.bindIn(t, orgId, id, accountId, input.files ?? []);
|
|
166
|
+
return this.writeMessage(t, id, accountId, input, next, extra, blocks, invoked);
|
|
167
|
+
});
|
|
168
|
+
if (!written) {
|
|
169
|
+
const raced = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
|
|
170
|
+
if (!raced)
|
|
171
|
+
throw new ApiError("conflict", "The conversation is being created; retry");
|
|
172
|
+
return raced;
|
|
173
|
+
}
|
|
174
|
+
await this.eventLog.notify(id, written.seq);
|
|
175
|
+
await this.runs?.ensureRun(id);
|
|
176
|
+
return { conversation: await this.rowView(id), event: written, created: true };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The conversation (and its first event) an earlier first send with this id created. The id used
|
|
180
|
+
* for another agent's conversation → `conflict` (the caller only checked access to `agentId`, so
|
|
181
|
+
* the other conversation is never returned).
|
|
182
|
+
*/
|
|
183
|
+
async findCreated(orgId, accountId, agentId, clientMessageId) {
|
|
184
|
+
const row = await this.db
|
|
185
|
+
.selectFrom("conversations")
|
|
186
|
+
.select(COLUMNS)
|
|
187
|
+
.where("org_id", "=", orgId)
|
|
188
|
+
.where("account_id", "=", accountId)
|
|
189
|
+
.where("create_request_id", "=", clientMessageId)
|
|
190
|
+
.where("kind", "=", "chat")
|
|
191
|
+
.executeTakeFirst();
|
|
192
|
+
if (!row)
|
|
193
|
+
return null;
|
|
194
|
+
if (row.agent_id !== agentId) {
|
|
195
|
+
throw new ApiError("conflict", "This clientMessageId was already used for another conversation");
|
|
196
|
+
}
|
|
197
|
+
const event = await this.eventLog.findByIdem(row.id, userMessageIdemKey(clientMessageId));
|
|
198
|
+
if (!event)
|
|
199
|
+
return null;
|
|
200
|
+
await this.runs?.ensureRun(row.id);
|
|
201
|
+
return { conversation: toView(row), event, created: false };
|
|
202
|
+
}
|
|
203
|
+
async rowView(id) {
|
|
204
|
+
return toView(await this.db
|
|
205
|
+
.selectFrom("conversations")
|
|
206
|
+
.select(COLUMNS)
|
|
207
|
+
.where("id", "=", id)
|
|
208
|
+
.executeTakeFirstOrThrow());
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Renames a conversation (owner only, as {@link get}): `title_source = 'manual'`, so the title
|
|
212
|
+
* job never overwrites it. Publishes `changed: title`. `title` is already trimmed and checked.
|
|
213
|
+
*/
|
|
214
|
+
async rename(orgId, conversationId, accountId, title) {
|
|
215
|
+
await this.writable(orgId, conversationId, accountId);
|
|
216
|
+
const row = await this.db
|
|
217
|
+
.updateTable("conversations")
|
|
218
|
+
.set({ title, title_source: "manual" })
|
|
219
|
+
.where("id", "=", conversationId)
|
|
220
|
+
.returning(COLUMNS)
|
|
221
|
+
.executeTakeFirstOrThrow();
|
|
222
|
+
await this.eventLog.publishChanged(conversationId, "title");
|
|
223
|
+
return toView(row);
|
|
224
|
+
}
|
|
225
|
+
/** 读会话:非所有者、或 agent 已对所有者不可用 → `conversation.not_found`。 */
|
|
226
|
+
async get(orgId, conversationId, viewerAccountId) {
|
|
227
|
+
const { kind: _kind, ...row } = await this.readable(orgId, conversationId, viewerAccountId);
|
|
228
|
+
return toView(row);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* {@link get} for a change made by a person (a message, rename, queue, model switch): a
|
|
232
|
+
* sub-agent's conversation is read-only (sub-agents spec §6.1) → `conversation.read_only`.
|
|
233
|
+
*/
|
|
234
|
+
async writable(orgId, conversationId, accountId) {
|
|
235
|
+
const { kind, ...row } = await this.readable(orgId, conversationId, accountId);
|
|
236
|
+
if (kind === "subagent") {
|
|
237
|
+
throw new ApiError("conversation.read_only", "A sub-agent's conversation takes no messages");
|
|
238
|
+
}
|
|
239
|
+
return toView(row);
|
|
240
|
+
}
|
|
241
|
+
/** {@link get}'s row, with the conversation's kind. */
|
|
242
|
+
async readable(orgId, conversationId, viewerAccountId) {
|
|
243
|
+
const row = await this.db
|
|
244
|
+
.selectFrom("conversations")
|
|
245
|
+
.select([...COLUMNS, "kind"])
|
|
246
|
+
.where("id", "=", conversationId)
|
|
247
|
+
.where("org_id", "=", orgId)
|
|
248
|
+
.where("account_id", "=", viewerAccountId)
|
|
249
|
+
// Hidden eval replay conversations (kind 'eval') are not reachable here (only the trajectory
|
|
250
|
+
// viewer shows them); a sub-agent's conversation is, for its owner (sub-agents spec §6.1)
|
|
251
|
+
.where("kind", "in", ["chat", "subagent"])
|
|
252
|
+
.where(parentReachable(viewerAccountId))
|
|
253
|
+
.executeTakeFirst();
|
|
254
|
+
if (!row)
|
|
255
|
+
throw notFound();
|
|
256
|
+
try {
|
|
257
|
+
await this.agents.get(orgId, row.agent_id, viewerAccountId);
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
if (e instanceof ApiError && e.code === "agent.not_found")
|
|
261
|
+
throw notFound();
|
|
262
|
+
throw e;
|
|
263
|
+
}
|
|
264
|
+
return row;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 轻量的可见性判定(一条查询,不加载 agent 视图),供长连接(SSE)建立时与定期复核用:
|
|
268
|
+
* 会话属于该组织且归 viewer 所有、viewer 账号仍为 active、agent 未删除且对 viewer 仍可见
|
|
269
|
+
* (`visibleTo` 已含「组织未删除、viewer 仍是成员」)。
|
|
270
|
+
*/
|
|
271
|
+
async isAccessible(orgId, conversationId, viewerAccountId) {
|
|
272
|
+
const row = await this.db
|
|
273
|
+
.selectFrom("conversations")
|
|
274
|
+
.select("conversations.id")
|
|
275
|
+
.where("conversations.id", "=", conversationId)
|
|
276
|
+
.where("conversations.org_id", "=", orgId)
|
|
277
|
+
.where("conversations.account_id", "=", viewerAccountId)
|
|
278
|
+
.where("conversations.kind", "in", ["chat", "subagent"])
|
|
279
|
+
.where(parentReachable(viewerAccountId))
|
|
280
|
+
.where((eb) => eb.and([
|
|
281
|
+
eb.exists(eb
|
|
282
|
+
.selectFrom("accounts")
|
|
283
|
+
.select(sql `1`.as("one"))
|
|
284
|
+
.where("accounts.id", "=", viewerAccountId)
|
|
285
|
+
.where("accounts.status", "=", "active")),
|
|
286
|
+
eb.exists(eb
|
|
287
|
+
.selectFrom("agents")
|
|
288
|
+
.select(sql `1`.as("one"))
|
|
289
|
+
.whereRef("agents.id", "=", "conversations.agent_id")
|
|
290
|
+
.where("agents.deleted_at", "is", null)
|
|
291
|
+
.where(visibleTo(viewerAccountId))),
|
|
292
|
+
]))
|
|
293
|
+
.executeTakeFirst();
|
|
294
|
+
return row !== undefined;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 我的会话,最近更新的在前;只含 agent 仍可用的会话。`after` 为上一页最后一个会话的 id,
|
|
298
|
+
* 从它当前的 (updated_at, id) 之后继续;游标会话已不存在时返回空页。
|
|
299
|
+
*/
|
|
300
|
+
async listMine(orgId, accountId, opts) {
|
|
301
|
+
let q = this.db
|
|
302
|
+
.selectFrom("conversations")
|
|
303
|
+
.select(COLUMNS)
|
|
304
|
+
.where("org_id", "=", orgId)
|
|
305
|
+
.where("account_id", "=", accountId)
|
|
306
|
+
// 评测回放的隐藏对话(kind = 'eval')不在「我的对话」里
|
|
307
|
+
.where("kind", "=", "chat")
|
|
308
|
+
.where((eb) => eb.exists(eb
|
|
309
|
+
.selectFrom("agents")
|
|
310
|
+
.select(sql `1`.as("one"))
|
|
311
|
+
.whereRef("agents.id", "=", "conversations.agent_id")
|
|
312
|
+
.where("agents.deleted_at", "is", null)
|
|
313
|
+
.where(visibleTo(accountId))))
|
|
314
|
+
.orderBy("updated_at", "desc")
|
|
315
|
+
.orderBy("id", "desc")
|
|
316
|
+
.limit(opts.limit);
|
|
317
|
+
if (opts.agentId !== undefined)
|
|
318
|
+
q = q.where("agent_id", "=", opts.agentId);
|
|
319
|
+
if (opts.after !== undefined) {
|
|
320
|
+
const after = opts.after;
|
|
321
|
+
q = q.where((eb) => sql `(${eb.ref("updated_at")}, ${eb.ref("id")}) < (
|
|
322
|
+
SELECT c.updated_at, c.id FROM capybara.conversations c
|
|
323
|
+
WHERE c.id = ${after} AND c.org_id = ${orgId} AND c.account_id = ${accountId}
|
|
324
|
+
)`);
|
|
325
|
+
}
|
|
326
|
+
return (await q.execute()).map(toView);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* My chat conversations grouped by agent (the sidebar, UI refresh spec §5.9), in one statement.
|
|
330
|
+
* Only agents that are not deleted and still visible to me (as {@link listMine}). Groups are
|
|
331
|
+
* ordered by (latestAt DESC, agent id DESC); each holds up to `perGroup` conversations and says
|
|
332
|
+
* whether it has more. `after` is the last group's agent id, re-evaluated against that group's
|
|
333
|
+
* current latestAt; a cursor agent that is no longer listed gives an empty page.
|
|
334
|
+
*/
|
|
335
|
+
async listGroups(orgId, accountId, opts) {
|
|
336
|
+
const visibleAgents = this.db
|
|
337
|
+
.selectFrom("agents")
|
|
338
|
+
.select("agents.id")
|
|
339
|
+
.where("agents.org_id", "=", orgId)
|
|
340
|
+
.where("agents.deleted_at", "is", null)
|
|
341
|
+
.where(visibleTo(accountId));
|
|
342
|
+
const cursor = opts.after === undefined
|
|
343
|
+
? sql ``
|
|
344
|
+
: sql `WHERE (g.latest_at, g.agent_id) < (
|
|
345
|
+
SELECT g2.latest_at, g2.agent_id FROM g g2 WHERE g2.agent_id = ${opts.after}
|
|
346
|
+
)`;
|
|
347
|
+
const { rows } = await sql `
|
|
348
|
+
WITH g AS (
|
|
349
|
+
SELECT c.agent_id, max(c.updated_at) AS latest_at
|
|
350
|
+
FROM capybara.conversations c
|
|
351
|
+
WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
|
|
352
|
+
AND c.agent_id IN ${visibleAgents}
|
|
353
|
+
GROUP BY c.agent_id
|
|
354
|
+
), page AS (
|
|
355
|
+
SELECT g.agent_id, g.latest_at FROM g
|
|
356
|
+
${cursor}
|
|
357
|
+
ORDER BY g.latest_at DESC, g.agent_id DESC
|
|
358
|
+
LIMIT ${opts.limit + 1}
|
|
359
|
+
)
|
|
360
|
+
SELECT page.agent_id, page.latest_at, v.definition ->> 'name' AS agent_name,
|
|
361
|
+
cs.id, cs.org_id, cs.account_id, cs.title, cs.title_source, cs.selection, cs.last_seq,
|
|
362
|
+
cs.created_at, cs.updated_at, cs.parent_conversation_id, cs.subagent_type
|
|
363
|
+
FROM page
|
|
364
|
+
JOIN capybara.agents a ON a.id = page.agent_id
|
|
365
|
+
JOIN capybara.agent_versions v ON v.agent_id = a.id AND v.version = a.current_version
|
|
366
|
+
CROSS JOIN LATERAL (
|
|
367
|
+
SELECT c.id, c.org_id, c.account_id, c.title, c.title_source, c.selection, c.last_seq,
|
|
368
|
+
c.created_at, c.updated_at, c.parent_conversation_id, c.subagent_type
|
|
369
|
+
FROM capybara.conversations c
|
|
370
|
+
WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
|
|
371
|
+
AND c.agent_id = page.agent_id
|
|
372
|
+
ORDER BY c.updated_at DESC, c.id DESC
|
|
373
|
+
LIMIT ${opts.perGroup + 1}
|
|
374
|
+
) cs
|
|
375
|
+
ORDER BY page.latest_at DESC, page.agent_id DESC, cs.updated_at DESC, cs.id DESC
|
|
376
|
+
`.execute(this.db);
|
|
377
|
+
const groups = [];
|
|
378
|
+
let current;
|
|
379
|
+
for (const r of rows) {
|
|
380
|
+
if (current?.agent.id !== r.agent_id) {
|
|
381
|
+
current = {
|
|
382
|
+
agent: { id: r.agent_id, name: r.agent_name ?? "" },
|
|
383
|
+
latestAt: r.latest_at,
|
|
384
|
+
conversations: [],
|
|
385
|
+
more: false,
|
|
386
|
+
};
|
|
387
|
+
groups.push(current);
|
|
388
|
+
}
|
|
389
|
+
if (current.conversations.length < opts.perGroup) {
|
|
390
|
+
current.conversations.push(toView(r));
|
|
391
|
+
}
|
|
392
|
+
else
|
|
393
|
+
current.more = true;
|
|
394
|
+
}
|
|
395
|
+
const page = groups.slice(0, opts.limit);
|
|
396
|
+
const next = groups.length > opts.limit ? (page.at(-1)?.agent.id ?? null) : null;
|
|
397
|
+
return { groups: page, next };
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* The sub-agents of a conversation, newest first (sub-agents spec §14.3); owner only, as
|
|
401
|
+
* {@link isAccessible} (else `conversation.not_found`). `after` is the previous page's last id,
|
|
402
|
+
* re-evaluated against its current position; a cursor that is not a sub-agent of this
|
|
403
|
+
* conversation gives an empty page. A row is listed even when its child is not accessible to the
|
|
404
|
+
* viewer (an org agent they can no longer see): its id is already in the parent's log, and opening
|
|
405
|
+
* the child still goes through {@link isAccessible}.
|
|
406
|
+
*/
|
|
407
|
+
async listSubagents(orgId, conversationId, viewerAccountId, opts) {
|
|
408
|
+
if (!(await this.isAccessible(orgId, conversationId, viewerAccountId)))
|
|
409
|
+
throw notFound();
|
|
410
|
+
const cursor = opts.after === undefined
|
|
411
|
+
? sql ``
|
|
412
|
+
: sql `AND (c.created_at, c.id) < (
|
|
413
|
+
SELECT c2.created_at, c2.id FROM capybara.conversations c2
|
|
414
|
+
WHERE c2.id = ${opts.after} AND c2.parent_conversation_id = ${conversationId}
|
|
415
|
+
)`;
|
|
416
|
+
const { rows } = await sql `
|
|
417
|
+
WITH page AS (
|
|
418
|
+
-- the page of children first, so the per-child work below runs for this page only
|
|
419
|
+
-- (batch 11 m3)
|
|
420
|
+
SELECT c.id, c.created_at, c.subagent_type, c.title
|
|
421
|
+
FROM capybara.conversations c
|
|
422
|
+
WHERE c.org_id = ${orgId} AND c.parent_conversation_id = ${conversationId}
|
|
423
|
+
AND c.kind = 'subagent'
|
|
424
|
+
AND EXISTS (
|
|
425
|
+
SELECT 1 FROM capybara.subagent_dispatches sd WHERE sd.child_conversation_id = c.id
|
|
426
|
+
)
|
|
427
|
+
${cursor}
|
|
428
|
+
ORDER BY c.created_at DESC, c.id DESC
|
|
429
|
+
LIMIT ${opts.limit + 1}
|
|
430
|
+
),
|
|
431
|
+
described AS (
|
|
432
|
+
SELECT DISTINCT ON (e.payload->>'subagentId')
|
|
433
|
+
e.payload->>'subagentId' AS id, e.payload->>'description' AS description
|
|
434
|
+
FROM capybara.conversation_events e
|
|
435
|
+
WHERE e.conversation_id = ${conversationId} AND e.type = 'subagent_dispatched'
|
|
436
|
+
AND e.payload->>'subagentId' IN (SELECT id FROM page)
|
|
437
|
+
ORDER BY e.payload->>'subagentId', e.seq DESC
|
|
438
|
+
)
|
|
439
|
+
SELECT c.id, c.subagent_type, c.title, d.description, r.status,
|
|
440
|
+
r.created_at AS run_created_at, r.started_at, r.finished_at,
|
|
441
|
+
(SELECT count(*) FROM capybara.conversation_events e
|
|
442
|
+
WHERE e.conversation_id = c.id AND e.run_id = r.id AND e.type = 'tool_result'
|
|
443
|
+
) AS tool_calls,
|
|
444
|
+
u.input_tokens, u.output_tokens, u.cache_read_tokens, u.cache_write_tokens,
|
|
445
|
+
ARRAY(SELECT a.id FROM capybara.approvals a
|
|
446
|
+
WHERE a.conversation_id = c.id AND a.status = 'pending' AND a.expires_at > now()
|
|
447
|
+
ORDER BY a.created_at, a.id) AS approval_ids,
|
|
448
|
+
ARRAY(SELECT q.id FROM capybara.questions q
|
|
449
|
+
WHERE q.conversation_id = c.id AND q.status = 'pending' AND q.expires_at > now()
|
|
450
|
+
ORDER BY q.created_at, q.id) AS question_ids
|
|
451
|
+
FROM page c
|
|
452
|
+
CROSS JOIN LATERAL (
|
|
453
|
+
SELECT sd.child_run_id FROM capybara.subagent_dispatches sd
|
|
454
|
+
WHERE sd.child_conversation_id = c.id
|
|
455
|
+
ORDER BY sd.created_at DESC, sd.child_run_id DESC
|
|
456
|
+
LIMIT 1
|
|
457
|
+
) latest
|
|
458
|
+
JOIN capybara.runs r ON r.id = latest.child_run_id
|
|
459
|
+
CROSS JOIN LATERAL (${usageSums(sql `e.conversation_id = c.id`)}) u
|
|
460
|
+
LEFT JOIN described d ON d.id = c.id
|
|
461
|
+
ORDER BY c.created_at DESC, c.id DESC
|
|
462
|
+
`.execute(this.db);
|
|
463
|
+
const subagents = rows.slice(0, opts.limit).map((r) => ({
|
|
464
|
+
id: r.id,
|
|
465
|
+
agentType: r.subagent_type ?? "",
|
|
466
|
+
description: r.description ?? r.title ?? "",
|
|
467
|
+
status: r.status,
|
|
468
|
+
toolCalls: Number(r.tool_calls),
|
|
469
|
+
startedAt: (r.started_at ?? r.run_created_at).toISOString(),
|
|
470
|
+
finishedAt: r.finished_at?.toISOString() ?? null,
|
|
471
|
+
usage: toUsage(r),
|
|
472
|
+
pendingApprovalIds: r.approval_ids,
|
|
473
|
+
pendingQuestionIds: r.question_ids,
|
|
474
|
+
}));
|
|
475
|
+
const next = rows.length > opts.limit ? (subagents.at(-1)?.id ?? null) : null;
|
|
476
|
+
return { subagents, next };
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* A conversation's token usage: its own `usage` events (compaction included) plus those of its
|
|
480
|
+
* sub-agents (sub-agents spec §8.3), whose logs keep their own. Owner only, as {@link get}.
|
|
481
|
+
*/
|
|
482
|
+
async usage(orgId, conversationId, viewerAccountId) {
|
|
483
|
+
if (!(await this.isAccessible(orgId, conversationId, viewerAccountId)))
|
|
484
|
+
throw notFound();
|
|
485
|
+
const { rows } = await sql `${usageSums(sql `e.conversation_id IN (
|
|
486
|
+
SELECT ${conversationId}::text
|
|
487
|
+
UNION ALL
|
|
488
|
+
SELECT c.id FROM capybara.conversations c
|
|
489
|
+
WHERE c.parent_conversation_id = ${conversationId} AND c.kind = 'subagent'
|
|
490
|
+
)`)}`.execute(this.db);
|
|
491
|
+
const row = rows[0];
|
|
492
|
+
return row
|
|
493
|
+
? toUsage(row)
|
|
494
|
+
: { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* The conversation whose sandbox `conversationId` works in: a sub-agent's is its parent's
|
|
498
|
+
* (sub-agents spec §10.1), any other its own. Call only after an access check.
|
|
499
|
+
*/
|
|
500
|
+
async sandboxConversationOf(orgId, conversationId) {
|
|
501
|
+
const row = await this.db
|
|
502
|
+
.selectFrom("conversations")
|
|
503
|
+
.select("parent_conversation_id")
|
|
504
|
+
.where("id", "=", conversationId)
|
|
505
|
+
.where("org_id", "=", orgId)
|
|
506
|
+
.executeTakeFirst();
|
|
507
|
+
return row?.parent_conversation_id ?? conversationId;
|
|
508
|
+
}
|
|
509
|
+
/** 读事件(先做 `get` 的可见性判定);顺带不阻塞地 {@link kickRun}。 */
|
|
510
|
+
async listEvents(orgId, conversationId, viewerAccountId, afterSeq, limit) {
|
|
511
|
+
await this.get(orgId, conversationId, viewerAccountId);
|
|
512
|
+
this.kickRun(conversationId);
|
|
513
|
+
return this.eventLog.readAfter(conversationId, afterSeq, limit);
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* 追加一条用户消息;带 `clientMessageId` 时重复提交返回同一事件。写入后调用 `ensureRun`:
|
|
517
|
+
* 没有进行中的运行时投递一轮;运行中则由当前运行在步骤间隙吸收,或在它结束时再次 `ensureRun`。
|
|
518
|
+
* 投递失败时抛错(消息已写入;客户端带同一 `clientMessageId` 重试会再次触发投递)。
|
|
519
|
+
*
|
|
520
|
+
* `attachments`(`@` 资源引用):内核以发送者的凭据读取资源(打码、限长),作为用户文本之后的额外文本块写入同一条
|
|
521
|
+
* user_message,并在 `payload.attachments` 记录元数据(第 i 个附件对应 `content[i + 1]`)。读取失败整条不写。
|
|
522
|
+
* 带 `clientMessageId` 的重复提交直接返回已写入的事件,不再读取。
|
|
523
|
+
*
|
|
524
|
+
* `files`(上传的附件 id):在写事件的同一事务里校验并绑定到本会话(见 {@link BindFiles}),作为 image /
|
|
525
|
+
* document 引用块追加在最后(`@` 资源块之后);任一附件不可用则整条不写。日志里只有引用,没有字节。
|
|
526
|
+
* `text` 为空时(只发附件,HTTP 层保证此时有 `files` 且没有 `@` 资源)不写文本块。
|
|
527
|
+
*/
|
|
528
|
+
async postUserMessage(orgId, conversationId, accountId, input) {
|
|
529
|
+
const conv = await this.writable(orgId, conversationId, accountId);
|
|
530
|
+
const refs = input.attachments ?? [];
|
|
531
|
+
const files = input.files ?? [];
|
|
532
|
+
this.checkFilesSupported(input);
|
|
533
|
+
// A retry returns the written event (no resource read, no file check, no skill resolution)
|
|
534
|
+
if ((refs.length > 0 || files.length > 0 || input.skill !== undefined) &&
|
|
535
|
+
input.clientMessageId !== undefined) {
|
|
536
|
+
const existing = await this.eventLog.findByIdem(conversationId, userMessageIdemKey(input.clientMessageId));
|
|
537
|
+
if (existing) {
|
|
538
|
+
await this.runs?.ensureRun(conversationId);
|
|
539
|
+
return existing;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const needsAgent = input.model !== undefined || input.effort !== undefined || input.skill !== undefined;
|
|
543
|
+
const agent = needsAgent
|
|
544
|
+
? (await this.agents.get(orgId, conv.agentId, accountId)).definition
|
|
545
|
+
: undefined;
|
|
546
|
+
const next = agent === undefined || (input.model === undefined && input.effort === undefined)
|
|
547
|
+
? null
|
|
548
|
+
: await this.planSwitch(agent, conv.selection, input);
|
|
549
|
+
const extra = await this.resolveExtra(orgId, accountId, input);
|
|
550
|
+
const invoked = agent === undefined
|
|
551
|
+
? undefined
|
|
552
|
+
: await this.prepareSkill(orgId, conv.agentId, conversationId, agent, input);
|
|
553
|
+
let event;
|
|
554
|
+
if (files.length > 0 || next !== null) {
|
|
555
|
+
event = await this.db.transaction().execute(async (t) => {
|
|
556
|
+
const blocks = await this.bindIn(t, orgId, conversationId, accountId, files);
|
|
557
|
+
return this.writeMessage(t, conversationId, accountId, input, next, extra, blocks, invoked);
|
|
558
|
+
});
|
|
559
|
+
await this.eventLog.notify(conversationId, event.seq);
|
|
560
|
+
if (next !== null)
|
|
561
|
+
await this.eventLog.publishChanged(conversationId, "selection");
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
[event] = await this.eventLog.append(conversationId, [
|
|
565
|
+
this.userMessage(accountId, input, extra, [], invoked),
|
|
566
|
+
]);
|
|
567
|
+
}
|
|
568
|
+
await this.runs?.ensureRun(conversationId);
|
|
569
|
+
return event;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* The selection a message switches to, or null when it changes nothing. A model equal to the
|
|
573
|
+
* agent's means "back to the agent's model" (null); a model change without `effort` resets the
|
|
574
|
+
* effort. The target model must resolve (`agent.model_unavailable`) and declare the effort
|
|
575
|
+
* (`validation_failed`). Resolving is the agent editor's check: providers are deployment-scoped
|
|
576
|
+
* and serve chat models only. If per-org provider enablement is added, this check and the run
|
|
577
|
+
* worker's `fallBackIfUnavailable` must follow it.
|
|
578
|
+
*/
|
|
579
|
+
async planSwitch(agent, current, input) {
|
|
580
|
+
const next = planSelection(agent.model, current, input);
|
|
581
|
+
if (next === null)
|
|
582
|
+
return null;
|
|
583
|
+
const { model, effort } = next;
|
|
584
|
+
if (!this.models) {
|
|
585
|
+
throw new ApiError("validation_failed", "Request validation failed", [
|
|
586
|
+
{ path: "model", message: "Model switching is not supported" },
|
|
587
|
+
]);
|
|
588
|
+
}
|
|
589
|
+
const { info } = await this.models.resolve(model ?? agent.model);
|
|
590
|
+
if (effort !== null) {
|
|
591
|
+
const knob = info.parameters.find((p) => p.key === "effort");
|
|
592
|
+
const values = knob?.schema?.enum ?? [];
|
|
593
|
+
if (!values.includes(effort)) {
|
|
594
|
+
throw new ApiError("validation_failed", "Request validation failed", [
|
|
595
|
+
{ path: "effort", message: `The model does not offer effort "${effort}"` },
|
|
596
|
+
]);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return next;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Checks a queued message's model / effort pick by the direct send's rules (final review I1): the
|
|
603
|
+
* target model must resolve and declare the effort, against the selection in effect now. The
|
|
604
|
+
* queue re-plans it against the selection in effect at dispatch.
|
|
605
|
+
*/
|
|
606
|
+
async checkPick(orgId, conv, accountId, pick) {
|
|
607
|
+
if (pick.model === undefined && pick.effort === undefined)
|
|
608
|
+
return;
|
|
609
|
+
const agent = await this.agents.get(orgId, conv.agentId, accountId);
|
|
610
|
+
await this.planSwitch(agent.definition, conv.selection, pick);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Writes the message (preceded by its `model_switched` when it switches) inside `t` and updates
|
|
614
|
+
* `conversations.selection`; returns the `user_message` event. The caller notifies after commit.
|
|
615
|
+
*/
|
|
616
|
+
async writeMessage(t, conversationId, accountId, input, next, extra, blocks, invoked) {
|
|
617
|
+
const events = [];
|
|
618
|
+
if (next !== null) {
|
|
619
|
+
events.push({
|
|
620
|
+
type: "model_switched",
|
|
621
|
+
...(input.clientMessageId !== undefined && {
|
|
622
|
+
idemKey: `switch:${input.clientMessageId}`,
|
|
623
|
+
}),
|
|
624
|
+
payload: { ...next, byAccountId: accountId, reason: "user" },
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
events.push(this.userMessage(accountId, input, extra, blocks, invoked));
|
|
628
|
+
const written = await this.eventLog.append(conversationId, events, t);
|
|
629
|
+
if (next !== null) {
|
|
630
|
+
// the cache follows the log (a retried message's switch may be an old, deduped event that a
|
|
631
|
+
// later switch already replaced), read under the row lock append took
|
|
632
|
+
await t
|
|
633
|
+
.updateTable("conversations")
|
|
634
|
+
.set({ selection: JSON.stringify(await readSelection(t, conversationId)) })
|
|
635
|
+
.where("id", "=", conversationId)
|
|
636
|
+
.execute();
|
|
637
|
+
}
|
|
638
|
+
return written.at(-1);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Renders the message's skill invocation (phase D), or undefined when it has none. Resolved
|
|
642
|
+
* against the agent's current version and, for an existing conversation, its trial drafts.
|
|
643
|
+
*/
|
|
644
|
+
async prepareSkill(orgId, agentId, conversationId, agent, input) {
|
|
645
|
+
if (input.skill === undefined)
|
|
646
|
+
return undefined;
|
|
647
|
+
if (!this.invoker) {
|
|
648
|
+
throw new ApiError("validation_failed", "Request validation failed", [
|
|
649
|
+
{ path: "skill", message: "Skill invocation is not supported" },
|
|
650
|
+
]);
|
|
651
|
+
}
|
|
652
|
+
return this.invoker.prepare({
|
|
653
|
+
orgId,
|
|
654
|
+
agentId,
|
|
655
|
+
...(conversationId !== undefined && { conversationId }),
|
|
656
|
+
agent,
|
|
657
|
+
name: input.skill.name,
|
|
658
|
+
args: input.text,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Renders a queued message's skill invocation now (phase D Task 9), by the direct send's rules,
|
|
663
|
+
* against the agent's current version and the conversation's trial drafts; undefined without
|
|
664
|
+
* `skill`. The queue stores the result and sends it unchanged later.
|
|
665
|
+
*/
|
|
666
|
+
async prepareQueuedSkill(orgId, conv, accountId, input) {
|
|
667
|
+
if (input.skill === undefined)
|
|
668
|
+
return undefined;
|
|
669
|
+
const agent = await this.agents.get(orgId, conv.agentId, accountId);
|
|
670
|
+
return this.prepareSkill(orgId, conv.agentId, conv.id, agent.definition, input);
|
|
671
|
+
}
|
|
672
|
+
/** `files` need the attachments service (shared with the queue). */
|
|
673
|
+
checkFilesSupported(input) {
|
|
674
|
+
if ((input.files?.length ?? 0) > 0 && !this.bindFiles) {
|
|
675
|
+
throw new ApiError("validation_failed", "Request validation failed", [
|
|
676
|
+
{ path: "files", message: "Files are not supported" },
|
|
677
|
+
]);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Reads the `@` resources with the sender's credentials (nothing when there are none). Shared
|
|
682
|
+
* with the queue, which reads them when a message is queued (decision 15).
|
|
683
|
+
*/
|
|
684
|
+
async resolveExtra(orgId, accountId, input) {
|
|
685
|
+
const refs = input.attachments ?? [];
|
|
686
|
+
if (refs.length === 0)
|
|
687
|
+
return { blocks: [], attachments: [] };
|
|
688
|
+
if (!this.resolveAttachments) {
|
|
689
|
+
throw new ApiError("validation_failed", "Request validation failed", [
|
|
690
|
+
{ path: "attachments", message: "Attachments are not supported" },
|
|
691
|
+
]);
|
|
692
|
+
}
|
|
693
|
+
return this.resolveAttachments(orgId, accountId, refs);
|
|
694
|
+
}
|
|
695
|
+
/** Binds uploaded files inside the message's (or queue item's) transaction; none → no blocks. */
|
|
696
|
+
async bindIn(t, orgId, conversationId, accountId, files) {
|
|
697
|
+
if (files.length === 0 || !this.bindFiles)
|
|
698
|
+
return [];
|
|
699
|
+
return this.bindFiles(t, orgId, conversationId, accountId, files);
|
|
700
|
+
}
|
|
701
|
+
userMessage(accountId, input, extra, fileBlocks, invoked) {
|
|
702
|
+
// a skill invocation's rendered text replaces the typed arguments as content[0]
|
|
703
|
+
const text = invoked?.text ?? input.text;
|
|
704
|
+
return {
|
|
705
|
+
type: "user_message",
|
|
706
|
+
...(input.clientMessageId !== undefined && {
|
|
707
|
+
idemKey: userMessageIdemKey(input.clientMessageId),
|
|
708
|
+
}),
|
|
709
|
+
payload: {
|
|
710
|
+
// 只发附件时(text 为空)不写空文本块(供应商不接受空文本);带 `@` 资源时 text 一定非空(content[0])
|
|
711
|
+
content: [
|
|
712
|
+
...(text === "" ? [] : [{ type: "text", text }]),
|
|
713
|
+
...extra.blocks,
|
|
714
|
+
...fileBlocks,
|
|
715
|
+
],
|
|
716
|
+
...(extra.attachments.length > 0 && { attachments: extra.attachments }),
|
|
717
|
+
authorAccountId: accountId,
|
|
718
|
+
...(input.clientMessageId !== undefined && { clientMessageId: input.clientMessageId }),
|
|
719
|
+
...(input.retryOf !== undefined && { retryOf: input.retryOf }),
|
|
720
|
+
...(invoked !== undefined && { skill: invoked.skill }),
|
|
721
|
+
},
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* 中止会话当前的运行(queued / running / waiting):返回 runId;没有 → `run.not_active`。
|
|
726
|
+
* 等人处理的运行没有 worker:直接取消等待并写终态。其余经 Redis 广播,运行所在的任意副本都会收到;
|
|
727
|
+
* 运行已是孤儿(没有任何 worker 在执行)时直接写终态。
|
|
728
|
+
*/
|
|
729
|
+
async abort(orgId, conversationId, accountId) {
|
|
730
|
+
await this.get(orgId, conversationId, accountId);
|
|
731
|
+
const run = await this.db
|
|
732
|
+
.selectFrom("runs")
|
|
733
|
+
.select(["id", "status"])
|
|
734
|
+
.where("conversation_id", "=", conversationId)
|
|
735
|
+
.where("status", "in", ["queued", "running", "waiting"])
|
|
736
|
+
.orderBy("created_at", "desc")
|
|
737
|
+
.executeTakeFirst();
|
|
738
|
+
if (!run || !this.runs)
|
|
739
|
+
throw new ApiError("run.not_active", "No active run to abort");
|
|
740
|
+
// 等待中:直接结束;没成功(刚被续跑、已回到 queued)则按下面的常规路径中止。
|
|
741
|
+
if (run.status === "waiting" &&
|
|
742
|
+
(await this.runs.abortWaiting(conversationId, run.id, accountId))) {
|
|
743
|
+
this.kickRun(conversationId);
|
|
744
|
+
return run.id;
|
|
745
|
+
}
|
|
746
|
+
// 孤儿运行没有 worker 在听中止广播:直接写成 aborted,之后再看有没有等待中的消息要起新一轮。
|
|
747
|
+
if (await this.runs.abortIfOrphaned(conversationId, run.id, accountId)) {
|
|
748
|
+
this.kickRun(conversationId);
|
|
749
|
+
return run.id;
|
|
750
|
+
}
|
|
751
|
+
await this.runs.requestAbort(run.id, accountId);
|
|
752
|
+
return run.id;
|
|
753
|
+
}
|
|
754
|
+
}
|