@nocobase/app-plugin-agents 2.0.0-beta.0 → 2.0.0-beta.10
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 +199 -0
- package/README.md +26 -4
- package/dist/client/chat/api.d.ts +6 -1
- package/dist/client/chat/api.d.ts.map +1 -1
- package/dist/client/chat/api.js +18 -0
- package/dist/client/chat/api.js.map +1 -1
- package/dist/client/chat/coalesced-invalidation.d.ts +3 -8
- package/dist/client/chat/coalesced-invalidation.d.ts.map +1 -1
- package/dist/client/chat/coalesced-invalidation.js +16 -21
- package/dist/client/chat/coalesced-invalidation.js.map +1 -1
- package/dist/client/chat/controller.d.ts +30 -3
- package/dist/client/chat/controller.d.ts.map +1 -1
- package/dist/client/chat/controller.js +477 -78
- package/dist/client/chat/controller.js.map +1 -1
- package/dist/client/chat/notices.d.ts +12 -0
- package/dist/client/chat/notices.d.ts.map +1 -0
- package/dist/client/chat/notices.js +15 -0
- package/dist/client/chat/notices.js.map +1 -0
- package/dist/client/chat/provider.d.ts.map +1 -1
- package/dist/client/chat/provider.js +7 -4
- package/dist/client/chat/provider.js.map +1 -1
- package/dist/client/chat/submissions.d.ts +16 -0
- package/dist/client/chat/submissions.d.ts.map +1 -1
- package/dist/client/chat/submissions.js +22 -0
- package/dist/client/chat/submissions.js.map +1 -1
- package/dist/client/chat/use-chat.d.ts +4 -2
- package/dist/client/chat/use-chat.d.ts.map +1 -1
- package/dist/client/chat/use-chat.js +83 -51
- package/dist/client/chat/use-chat.js.map +1 -1
- package/dist/client/chat.d.ts +1 -0
- package/dist/client/chat.d.ts.map +1 -1
- package/dist/client/chat.js +1 -0
- package/dist/client/chat.js.map +1 -1
- package/dist/client/components/page-container.js +1 -1
- package/dist/client/components/page-container.js.map +1 -1
- package/dist/client/components/route-child-page.d.ts +1 -1
- package/dist/client/components/route-child-page.d.ts.map +1 -1
- package/dist/client/components/route-child-page.js +18 -2
- package/dist/client/components/route-child-page.js.map +1 -1
- package/dist/client/components/route-overlay.d.ts +4 -1
- package/dist/client/components/route-overlay.d.ts.map +1 -1
- package/dist/client/components/route-overlay.js +32 -3
- package/dist/client/components/route-overlay.js.map +1 -1
- package/dist/client/components/ui/alert-dialog.d.ts +3 -1
- package/dist/client/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/client/components/ui/alert-dialog.js +2 -2
- package/dist/client/components/ui/alert-dialog.js.map +1 -1
- package/dist/client/components/unsaved-changes.d.ts.map +1 -1
- package/dist/client/components/unsaved-changes.js +17 -4
- package/dist/client/components/unsaved-changes.js.map +1 -1
- package/dist/client/components/use-route-overlay.d.ts +2 -9
- package/dist/client/components/use-route-overlay.d.ts.map +1 -1
- package/dist/client/components/use-route-overlay.js +5 -3
- package/dist/client/components/use-route-overlay.js.map +1 -1
- package/dist/client/hooks/use-realtime-topic.d.ts +1 -1
- package/dist/client/hooks/use-realtime-topic.d.ts.map +1 -1
- package/dist/client/hooks/use-realtime-topic.js +8 -4
- package/dist/client/hooks/use-realtime-topic.js.map +1 -1
- package/dist/client/kit.d.ts.map +1 -1
- package/dist/client/kit.js +30 -5
- package/dist/client/kit.js.map +1 -1
- package/dist/client/locales/de-DE.d.ts.map +1 -1
- package/dist/client/locales/de-DE.js +137 -109
- package/dist/client/locales/de-DE.js.map +1 -1
- package/dist/client/locales/en-US.d.ts +2 -0
- package/dist/client/locales/en-US.d.ts.map +1 -1
- package/dist/client/locales/en-US.js +4 -2
- package/dist/client/locales/en-US.js.map +1 -1
- package/dist/client/locales/es-ES.d.ts.map +1 -1
- package/dist/client/locales/es-ES.js +214 -186
- package/dist/client/locales/es-ES.js.map +1 -1
- package/dist/client/locales/fr-FR.d.ts.map +1 -1
- package/dist/client/locales/fr-FR.js +146 -118
- package/dist/client/locales/fr-FR.js.map +1 -1
- package/dist/client/locales/id-ID.d.ts.map +1 -1
- package/dist/client/locales/id-ID.js +135 -107
- package/dist/client/locales/id-ID.js.map +1 -1
- package/dist/client/locales/index.d.ts.map +1 -1
- package/dist/client/locales/index.js +2 -0
- package/dist/client/locales/index.js.map +1 -1
- package/dist/client/locales/ja-JP.d.ts.map +1 -1
- package/dist/client/locales/ja-JP.js +218 -190
- package/dist/client/locales/ja-JP.js.map +1 -1
- package/dist/client/locales/ko-KR.d.ts +5 -0
- package/dist/client/locales/ko-KR.d.ts.map +1 -0
- package/dist/client/locales/ko-KR.js +1311 -0
- package/dist/client/locales/ko-KR.js.map +1 -0
- package/dist/client/locales/pt-BR.d.ts.map +1 -1
- package/dist/client/locales/pt-BR.js +208 -180
- package/dist/client/locales/pt-BR.js.map +1 -1
- package/dist/client/locales/ru-RU.d.ts +5 -0
- package/dist/client/locales/ru-RU.d.ts.map +1 -0
- package/dist/client/locales/ru-RU.js +1341 -0
- package/dist/client/locales/ru-RU.js.map +1 -0
- package/dist/client/locales/runWait.en-US.d.ts +2 -0
- package/dist/client/locales/runWait.en-US.d.ts.map +1 -1
- package/dist/client/locales/runWait.en-US.js +2 -0
- package/dist/client/locales/runWait.en-US.js.map +1 -1
- package/dist/client/locales/runWait.zh-CN.d.ts.map +1 -1
- package/dist/client/locales/runWait.zh-CN.js +2 -0
- package/dist/client/locales/runWait.zh-CN.js.map +1 -1
- package/dist/client/locales/runtimes.en-US.d.ts +1 -0
- package/dist/client/locales/runtimes.en-US.d.ts.map +1 -1
- package/dist/client/locales/runtimes.en-US.js +3 -2
- package/dist/client/locales/runtimes.en-US.js.map +1 -1
- package/dist/client/locales/runtimes.zh-CN.d.ts.map +1 -1
- package/dist/client/locales/runtimes.zh-CN.js +3 -2
- package/dist/client/locales/runtimes.zh-CN.js.map +1 -1
- package/dist/client/locales/vi-VN.d.ts.map +1 -1
- package/dist/client/locales/vi-VN.js +174 -146
- package/dist/client/locales/vi-VN.js.map +1 -1
- package/dist/client/locales/zh-CN.d.ts.map +1 -1
- package/dist/client/locales/zh-CN.js +4 -2
- package/dist/client/locales/zh-CN.js.map +1 -1
- package/dist/client/locales/zh-TW.d.ts.map +1 -1
- package/dist/client/locales/zh-TW.js +151 -123
- package/dist/client/locales/zh-TW.js.map +1 -1
- package/dist/client/pages/agents/agent-dialog.js +4 -1
- package/dist/client/pages/agents/agent-dialog.js.map +1 -1
- package/dist/client/pages/runtimes/quota.d.ts +3 -2
- package/dist/client/pages/runtimes/quota.d.ts.map +1 -1
- package/dist/client/pages/runtimes/quota.js +8 -3
- package/dist/client/pages/runtimes/quota.js.map +1 -1
- package/dist/client/pages/runtimes/runner-runs.d.ts.map +1 -1
- package/dist/client/pages/runtimes/runner-runs.js +2 -8
- package/dist/client/pages/runtimes/runner-runs.js.map +1 -1
- package/dist/client/pages/runtimes/runner-tools.d.ts.map +1 -1
- package/dist/client/pages/runtimes/runner-tools.js +7 -16
- package/dist/client/pages/runtimes/runner-tools.js.map +1 -1
- package/dist/client/pages/runtimes/workspace-diagnostics.js +3 -3
- package/dist/client/pages/runtimes/workspace-diagnostics.js.map +1 -1
- package/dist/client/pages/usage/date-range-picker.d.ts.map +1 -1
- package/dist/client/pages/usage/date-range-picker.js +3 -1
- package/dist/client/pages/usage/date-range-picker.js.map +1 -1
- package/dist/client/routes.d.ts.map +1 -1
- package/dist/client/routes.js +7 -0
- package/dist/client/routes.js.map +1 -1
- package/dist/client/runs/format-wait.d.ts.map +1 -1
- package/dist/client/runs/format-wait.js +2 -0
- package/dist/client/runs/format-wait.js.map +1 -1
- package/dist/database/migrations/.manifest.json +16 -0
- package/dist/database/migrations/202611070001_ag_run_runner_pin.d.ts +4 -0
- package/dist/database/migrations/202611070001_ag_run_runner_pin.d.ts.map +1 -0
- package/dist/database/migrations/202611070001_ag_run_runner_pin.js +17 -0
- package/dist/database/migrations/202611070001_ag_run_runner_pin.js.map +1 -0
- package/dist/database/migrations/202611080001_ag_conversation_shares.d.ts +4 -0
- package/dist/database/migrations/202611080001_ag_conversation_shares.d.ts.map +1 -0
- package/dist/database/migrations/202611080001_ag_conversation_shares.js +25 -0
- package/dist/database/migrations/202611080001_ag_conversation_shares.js.map +1 -0
- package/dist/database/migrations/202611080002_ag_run_failure_params.d.ts +4 -0
- package/dist/database/migrations/202611080002_ag_run_failure_params.d.ts.map +1 -0
- package/dist/database/migrations/202611080002_ag_run_failure_params.js +17 -0
- package/dist/database/migrations/202611080002_ag_run_failure_params.js.map +1 -0
- package/dist/database/migrations/202611090001_ag_runner_preference.d.ts +9 -0
- package/dist/database/migrations/202611090001_ag_runner_preference.d.ts.map +1 -0
- package/dist/database/migrations/202611090001_ag_runner_preference.js +28 -0
- package/dist/database/migrations/202611090001_ag_runner_preference.js.map +1 -0
- package/dist/package.json +1 -1
- package/dist/server/composition.d.ts +7 -1
- package/dist/server/composition.d.ts.map +1 -1
- package/dist/server/composition.js +14 -2
- package/dist/server/composition.js.map +1 -1
- package/dist/server/core/agents/agent.service.d.ts +2 -1
- package/dist/server/core/agents/agent.service.d.ts.map +1 -1
- package/dist/server/core/agents/agent.service.js.map +1 -1
- package/dist/server/core/agents/agent.store.d.ts +2 -2
- package/dist/server/core/agents/agent.store.d.ts.map +1 -1
- package/dist/server/core/agents/agent.store.js +8 -2
- package/dist/server/core/agents/agent.store.js.map +1 -1
- package/dist/server/core/brief/brief.d.ts +10 -5
- package/dist/server/core/brief/brief.d.ts.map +1 -1
- package/dist/server/core/brief/brief.js +36 -13
- package/dist/server/core/brief/brief.js.map +1 -1
- package/dist/server/core/brief/index.d.ts +1 -1
- package/dist/server/core/brief/index.d.ts.map +1 -1
- package/dist/server/core/brief/index.js +1 -1
- package/dist/server/core/brief/index.js.map +1 -1
- package/dist/server/core/conversations/attachments.d.ts +2 -12
- package/dist/server/core/conversations/attachments.d.ts.map +1 -1
- package/dist/server/core/conversations/attachments.js +56 -14
- package/dist/server/core/conversations/attachments.js.map +1 -1
- package/dist/server/core/conversations/chat-settings.d.ts +1 -1
- package/dist/server/core/conversations/chat-settings.d.ts.map +1 -1
- package/dist/server/core/conversations/chat-settings.js +6 -0
- package/dist/server/core/conversations/chat-settings.js.map +1 -1
- package/dist/server/core/conversations/conversation.service.d.ts +3 -1
- package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
- package/dist/server/core/conversations/conversation.service.js +212 -21
- package/dist/server/core/conversations/conversation.service.js.map +1 -1
- package/dist/server/core/conversations/conversation.store.d.ts +2 -0
- package/dist/server/core/conversations/conversation.store.d.ts.map +1 -1
- package/dist/server/core/conversations/conversation.store.js.map +1 -1
- package/dist/server/core/conversations/index.d.ts +1 -0
- package/dist/server/core/conversations/index.d.ts.map +1 -1
- package/dist/server/core/conversations/links.d.ts +3 -0
- package/dist/server/core/conversations/links.d.ts.map +1 -1
- package/dist/server/core/conversations/links.js +43 -5
- package/dist/server/core/conversations/links.js.map +1 -1
- package/dist/server/core/conversations/ports.d.ts +8 -2
- package/dist/server/core/conversations/ports.d.ts.map +1 -1
- package/dist/server/core/conversations/shares.d.ts +38 -0
- package/dist/server/core/conversations/shares.d.ts.map +1 -0
- package/dist/server/core/conversations/shares.js +189 -0
- package/dist/server/core/conversations/shares.js.map +1 -0
- package/dist/server/core/runs/claim.d.ts +22 -3
- package/dist/server/core/runs/claim.d.ts.map +1 -1
- package/dist/server/core/runs/claim.js +260 -42
- package/dist/server/core/runs/claim.js.map +1 -1
- package/dist/server/core/runs/extensions.d.ts +30 -2
- package/dist/server/core/runs/extensions.d.ts.map +1 -1
- package/dist/server/core/runs/extensions.js +18 -1
- package/dist/server/core/runs/extensions.js.map +1 -1
- package/dist/server/core/runs/git-credentials.d.ts.map +1 -1
- package/dist/server/core/runs/git-credentials.js +24 -2
- package/dist/server/core/runs/git-credentials.js.map +1 -1
- package/dist/server/core/runs/identity.d.ts.map +1 -1
- package/dist/server/core/runs/identity.js +9 -0
- package/dist/server/core/runs/identity.js.map +1 -1
- package/dist/server/core/runs/index.d.ts +3 -2
- package/dist/server/core/runs/index.d.ts.map +1 -1
- package/dist/server/core/runs/index.js +2 -1
- package/dist/server/core/runs/index.js.map +1 -1
- package/dist/server/core/runs/lifecycle.d.ts.map +1 -1
- package/dist/server/core/runs/lifecycle.js +14 -0
- package/dist/server/core/runs/lifecycle.js.map +1 -1
- package/dist/server/core/runs/npm-credentials.d.ts +35 -0
- package/dist/server/core/runs/npm-credentials.d.ts.map +1 -0
- package/dist/server/core/runs/npm-credentials.js +83 -0
- package/dist/server/core/runs/npm-credentials.js.map +1 -0
- package/dist/server/core/runs/ports.d.ts +19 -2
- package/dist/server/core/runs/ports.d.ts.map +1 -1
- package/dist/server/core/runs/ports.js.map +1 -1
- package/dist/server/core/runs/preview.d.ts +1 -4
- package/dist/server/core/runs/preview.d.ts.map +1 -1
- package/dist/server/core/runs/preview.js +10 -5
- package/dist/server/core/runs/preview.js.map +1 -1
- package/dist/server/core/runs/quota-wait.d.ts +7 -0
- package/dist/server/core/runs/quota-wait.d.ts.map +1 -0
- package/dist/server/core/runs/quota-wait.js +56 -0
- package/dist/server/core/runs/quota-wait.js.map +1 -0
- package/dist/server/core/runs/run-data-purge.d.ts.map +1 -1
- package/dist/server/core/runs/run-data-purge.js +1 -0
- package/dist/server/core/runs/run-data-purge.js.map +1 -1
- package/dist/server/core/runs/run.service.d.ts +29 -5
- package/dist/server/core/runs/run.service.d.ts.map +1 -1
- package/dist/server/core/runs/run.service.js +101 -10
- package/dist/server/core/runs/run.service.js.map +1 -1
- package/dist/server/core/runs/run.store.d.ts +24 -0
- package/dist/server/core/runs/run.store.d.ts.map +1 -1
- package/dist/server/core/runs/run.store.js +44 -0
- package/dist/server/core/runs/run.store.js.map +1 -1
- package/dist/server/core/runs/sweeper.d.ts.map +1 -1
- package/dist/server/core/runs/sweeper.js +2 -0
- package/dist/server/core/runs/sweeper.js.map +1 -1
- package/dist/server/core/runs/transitions.d.ts +1 -0
- package/dist/server/core/runs/transitions.d.ts.map +1 -1
- package/dist/server/core/runs/transitions.js +15 -1
- package/dist/server/core/runs/transitions.js.map +1 -1
- package/dist/server/core/runs/workload.d.ts +2 -2
- package/dist/server/core/runs/workload.d.ts.map +1 -1
- package/dist/server/core/runs/workload.js +62 -9
- package/dist/server/core/runs/workload.js.map +1 -1
- package/dist/server/kernel/clock.d.ts +2 -0
- package/dist/server/kernel/clock.d.ts.map +1 -1
- package/dist/server/kernel/clock.js +14 -0
- package/dist/server/kernel/clock.js.map +1 -1
- package/dist/server/kernel/events.d.ts +6 -1
- package/dist/server/kernel/events.d.ts.map +1 -1
- package/dist/server/kernel/events.js.map +1 -1
- package/dist/server/locales/de-DE.js +3 -3
- package/dist/server/locales/de-DE.js.map +1 -1
- package/dist/server/locales/es-ES.js +6 -6
- package/dist/server/locales/es-ES.js.map +1 -1
- package/dist/server/locales/fr-FR.js +4 -4
- package/dist/server/locales/fr-FR.js.map +1 -1
- package/dist/server/locales/id-ID.js +5 -5
- package/dist/server/locales/id-ID.js.map +1 -1
- package/dist/server/locales/index.d.ts.map +1 -1
- package/dist/server/locales/index.js +2 -0
- package/dist/server/locales/index.js.map +1 -1
- package/dist/server/locales/ja-JP.d.ts.map +1 -1
- package/dist/server/locales/ja-JP.js +6 -6
- package/dist/server/locales/ja-JP.js.map +1 -1
- package/dist/server/locales/ko-KR.d.ts +5 -0
- package/dist/server/locales/ko-KR.d.ts.map +1 -0
- package/dist/server/locales/ko-KR.js +59 -0
- package/dist/server/locales/ko-KR.js.map +1 -0
- package/dist/server/locales/pt-BR.js +6 -6
- package/dist/server/locales/pt-BR.js.map +1 -1
- package/dist/server/locales/ru-RU.d.ts +5 -0
- package/dist/server/locales/ru-RU.d.ts.map +1 -0
- package/dist/server/locales/ru-RU.js +59 -0
- package/dist/server/locales/ru-RU.js.map +1 -0
- package/dist/server/locales/vi-VN.js +6 -6
- package/dist/server/locales/vi-VN.js.map +1 -1
- package/dist/server/locales/zh-TW.d.ts.map +1 -1
- package/dist/server/locales/zh-TW.js +6 -6
- package/dist/server/locales/zh-TW.js.map +1 -1
- package/dist/server/online/agent/persistence/store.d.ts.map +1 -1
- package/dist/server/online/agent/persistence/store.js +3 -0
- package/dist/server/online/agent/persistence/store.js.map +1 -1
- package/dist/server/online/agent/runner/backend.d.ts.map +1 -1
- package/dist/server/online/agent/runner/backend.js +17 -8
- package/dist/server/online/agent/runner/backend.js.map +1 -1
- package/dist/server/online/agent/runner/projection.d.ts +1 -1
- package/dist/server/online/agent/runner/projection.d.ts.map +1 -1
- package/dist/server/online/agent/runner/projection.js +8 -1
- package/dist/server/online/agent/runner/projection.js.map +1 -1
- package/dist/server/providers/agents.d.ts.map +1 -1
- package/dist/server/providers/agents.js +3 -0
- package/dist/server/providers/agents.js.map +1 -1
- package/dist/server/routes/admin.d.ts +1 -0
- package/dist/server/routes/admin.d.ts.map +1 -1
- package/dist/server/routes/admin.js +32 -2
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/chat.d.ts.map +1 -1
- package/dist/server/routes/chat.js +99 -16
- package/dist/server/routes/chat.js.map +1 -1
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +8 -2
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/run.js +1 -1
- package/dist/server/routes/run.js.map +1 -1
- package/dist/server/routes/runners/admin.d.ts.map +1 -1
- package/dist/server/routes/runners/admin.js +3 -3
- package/dist/server/routes/runners/admin.js.map +1 -1
- package/dist/server/routes/runners/install.d.ts.map +1 -1
- package/dist/server/routes/runners/install.js +5 -0
- package/dist/server/routes/runners/install.js.map +1 -1
- package/dist/server/routes/runners/runner.d.ts.map +1 -1
- package/dist/server/routes/runners/runner.js +13 -22
- package/dist/server/routes/runners/runner.js.map +1 -1
- package/dist/server/routes/runners/runs.d.ts +1 -1
- package/dist/server/routes/runners/runs.d.ts.map +1 -1
- package/dist/server/routes/runners/runs.js +20 -1
- package/dist/server/routes/runners/runs.js.map +1 -1
- package/dist/server/routes/schemas.d.ts +5 -1
- package/dist/server/routes/schemas.d.ts.map +1 -1
- package/dist/server/routes/schemas.js +33 -8
- package/dist/server/routes/schemas.js.map +1 -1
- package/dist/server/runners/runner.service.d.ts.map +1 -1
- package/dist/server/runners/runner.service.js +17 -5
- package/dist/server/runners/runner.service.js.map +1 -1
- package/dist/server/tokens.d.ts +1 -1
- package/dist/server/tokens.d.ts.map +1 -1
- package/dist/server/tokens.js.map +1 -1
- package/dist/shared/briefs.d.ts +2 -6
- package/dist/shared/briefs.d.ts.map +1 -1
- package/dist/shared/conversations.d.ts +38 -1
- package/dist/shared/conversations.d.ts.map +1 -1
- package/dist/shared/conversations.js +10 -0
- package/dist/shared/conversations.js.map +1 -1
- package/dist/shared/runs.d.ts +11 -3
- package/dist/shared/runs.d.ts.map +1 -1
- package/dist/shared/runs.js +2 -0
- package/dist/shared/runs.js.map +1 -1
- package/dist/shared/ui-message.d.ts +2 -2
- package/dist/shared/ui-message.d.ts.map +1 -1
- package/dist/shared/ui-message.js +1 -1
- package/dist/shared/ui-message.js.map +1 -1
- package/docs/conversation-sharing.md +55 -0
- package/package.json +16 -16
- package/skills/nocobase-app-plugin-agents/references/extension-points.md +2 -0
- package/skills/nocobase-app-plugin-agents/references/runners.md +1 -1
- package/skills/nocobase-app-plugin-agents/references/verification-and-troubleshooting.md +2 -1
|
@@ -19,6 +19,17 @@ import { ACTIVE_JOB, jobsRepo } from '../../jobs/job.store.js';
|
|
|
19
19
|
* agent's entries whose tool the runner runs and has room for, so an agent listing two tools runs with the second while
|
|
20
20
|
* the first is full; a run none of whose tools has room is passed over for the runs behind it.
|
|
21
21
|
*
|
|
22
|
+
* Runners share a queue in proportion to each tool's limit, not by who asks first: before it locks anything, a claim
|
|
23
|
+
* waits until the run has been queued for the share of the tool's limit the runner already runs, times one second
|
|
24
|
+
* (`claimPace`). The runners' route hands out one run per request and the runner asks again at once, so of several
|
|
25
|
+
* runners woken for the same runs, the least busy with each tool takes the next one. A request that ends while it
|
|
26
|
+
* waits takes nothing.
|
|
27
|
+
*
|
|
28
|
+
* A run whose actor prefers one kind of runner (`runnerPreference`, `personal` or `team`, noted when it was queued)
|
|
29
|
+
* makes the other kind wait one second more (`preferenceDelay`), so an idle runner of the preferred kind takes it
|
|
30
|
+
* first, and one of the other kind takes it about a second later when none can. Work that only one runner may take
|
|
31
|
+
* (pinned, reserved, continuing where its session is, or for shared runners only) adds no wait. Nothing else is read.
|
|
32
|
+
*
|
|
22
33
|
* Variables are checked and opened in the claim's own transaction, on its connection: which ones the assembled payload
|
|
23
34
|
* gets, whether any is for shared runners only, and their values, so what the runner receives is what was checked. The
|
|
24
35
|
* names taken from the runner go in `workspace.passthrough`, without values.
|
|
@@ -45,10 +56,10 @@ import { ACTIVE_JOB, jobsRepo } from '../../jobs/job.store.js';
|
|
|
45
56
|
*/
|
|
46
57
|
import { GLOBAL_SKILL_TARGET } from '../../../shared/skills.js';
|
|
47
58
|
import { ANNOUNCED_FAILURES, isProtocolSupported, policyAllowsAgent, policyAllowsRepo, policyAllowsSubject, TIMINGS, } from '@nocobase/agent-protocol';
|
|
48
|
-
import { chatEffortCapabilities, runnerLowOnDisk, runsEntry, toolLimit, supportsWorkspaceKey, } from '../../../shared/runners.js';
|
|
59
|
+
import { chatEffortCapabilities, quotaForAgent, runnerLowOnDisk, runsEntry, toolLimit, supportsWorkspaceKey, } from '../../../shared/runners.js';
|
|
49
60
|
import { queueStart } from './selection.js';
|
|
50
61
|
import { createHash } from 'node:crypto';
|
|
51
|
-
import { later } from '../../kernel/clock.js';
|
|
62
|
+
import { later, pause } from '../../kernel/clock.js';
|
|
52
63
|
import { notFound } from '../../kernel/errors.js';
|
|
53
64
|
import { runSecretsKey } from '../../kernel/redaction.js';
|
|
54
65
|
import { cleanList, covers, jsonObject } from '../../kernel/values.js';
|
|
@@ -57,7 +68,7 @@ import { joinBrief, renderBrief } from '../brief/index.js';
|
|
|
57
68
|
import { cliPackageFor } from './cli-package.js';
|
|
58
69
|
import { executionHistory } from './execution.js';
|
|
59
70
|
import { describeVariables, variableRefs, } from '../variables/index.js';
|
|
60
|
-
import { mountsFor, prepareExtensions, repoAccessFor, responsibleOf, sessionKeyOf, withSections, } from './extensions.js';
|
|
71
|
+
import { mountsFor, prepareExtensions, repoAccessFor, RepoAccessError, responsibleOf, sessionKeyOf, withSections, } from './extensions.js';
|
|
61
72
|
import { MAX_CLAIM_FAILURES, toolPolicyFor } from './policy.js';
|
|
62
73
|
import { mintRunToken } from './run-tokens.js';
|
|
63
74
|
import { ACTIVE, countActive, eventsRepo, findRunRecord, markDelivered, pendingInputs, runsOfKey, runsRepo, inputsRepo, sessionsRepo, toInput, toExecutions, toRun, } from './run.store.js';
|
|
@@ -71,6 +82,11 @@ import { consumeReset, saveBrief } from './workspace.store.js';
|
|
|
71
82
|
const CANDIDATES = 50;
|
|
72
83
|
/** The initial page plus at most two further pages per claim request, shared by all free slots. */
|
|
73
84
|
const CANDIDATE_PAGES = 3;
|
|
85
|
+
/**
|
|
86
|
+
* What a runner waits for a run when it already runs its tool at the limit, and what one of the kind of runner the
|
|
87
|
+
* run's actor does not prefer waits on top (`preferenceDelay`).
|
|
88
|
+
*/
|
|
89
|
+
const PACE_MS = 1000;
|
|
74
90
|
export function createClaimScan() {
|
|
75
91
|
return { remainingPages: CANDIDATE_PAGES };
|
|
76
92
|
}
|
|
@@ -109,6 +125,43 @@ function toolRoom(tools) {
|
|
|
109
125
|
},
|
|
110
126
|
};
|
|
111
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* How long `runner` waits before it takes `run` with `tool`, at `now`: the share of the tool's limit it already runs
|
|
130
|
+
* (`toolLimit`), of the busier of what this application counts (`byTool`) and what the runner last reported
|
|
131
|
+
* (`Runner.toolLoad`), times one second, counted from when the run entered the queue (`queueStart`). Several runners
|
|
132
|
+
* woken for one run take it in order of how busy each is with its tool, whichever asked first and however many runs
|
|
133
|
+
* each took before; an idle runner, and work queued longer than its share, wait nothing.
|
|
134
|
+
*/
|
|
135
|
+
export function claimPace(runner, tool, byTool, run, now,
|
|
136
|
+
/** Added to the share and counted from the same moment (`preferenceDelay`). */
|
|
137
|
+
extraMs = 0) {
|
|
138
|
+
const limit = toolLimit(runner, tool);
|
|
139
|
+
if (limit <= 0)
|
|
140
|
+
return 0;
|
|
141
|
+
const load = runner.toolLoad?.[tool];
|
|
142
|
+
const used = Math.max(byTool[tool] ?? 0, load ? load.slots - load.free : 0);
|
|
143
|
+
const share = (Math.min(used, limit) / limit) * PACE_MS + extraMs;
|
|
144
|
+
return Math.min(share, Math.max(0, queueStart(run) + share - now));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* What `runner` waits on top of its share (`claimPace`) for `run`, from the kind of runner its actor prefers: one
|
|
148
|
+
* second when the run prefers the other kind, nothing otherwise. Nothing either when only this runner may take it
|
|
149
|
+
* (`bound`: pinned, reserved or continuing where its session is), or when it prefers personal runners but has
|
|
150
|
+
* variables for shared runners only, which no personal runner may receive.
|
|
151
|
+
*/
|
|
152
|
+
export function preferenceDelay(runner, run, bound) {
|
|
153
|
+
const preferred = run.runnerPreference;
|
|
154
|
+
if (preferred !== 'personal' && preferred !== 'team')
|
|
155
|
+
return 0;
|
|
156
|
+
if (bound || run.runnerPinId)
|
|
157
|
+
return 0;
|
|
158
|
+
if (preferred === (runner.trust === 'team' ? 'team' : 'personal'))
|
|
159
|
+
return 0;
|
|
160
|
+
if (preferred === 'personal' &&
|
|
161
|
+
variableRefs(run.teamOnlyVariables).length > 0)
|
|
162
|
+
return 0;
|
|
163
|
+
return PACE_MS;
|
|
164
|
+
}
|
|
112
165
|
/**
|
|
113
166
|
* The entry `runner` takes a run of `agent` with: the first whose tool it has enabled, installed and signed in, with
|
|
114
167
|
* the entry's model among those it reports (`runsEntry`). Null when it has none of them.
|
|
@@ -146,9 +199,24 @@ export function fitsActor(runner, agent, actorUserId, requires, now) {
|
|
|
146
199
|
return false;
|
|
147
200
|
return runner.trust === 'team' || runner.ownerUserId === actorUserId;
|
|
148
201
|
}
|
|
202
|
+
/** A strict continuation keeps the tool/model that owns its original session. */
|
|
203
|
+
function quotaAgent(run, agent) {
|
|
204
|
+
if (!run.runnerPinId || run.freshSession)
|
|
205
|
+
return agent;
|
|
206
|
+
const last = toExecutions(run.executionHistory).at(-1);
|
|
207
|
+
const tool = last?.tool ?? run.tool;
|
|
208
|
+
const model = last ? last.model : run.model;
|
|
209
|
+
return tool
|
|
210
|
+
? {
|
|
211
|
+
...agent,
|
|
212
|
+
modelEntries: agent.modelEntries.filter((entry) => entry.tool === tool && entry.model === model),
|
|
213
|
+
}
|
|
214
|
+
: agent;
|
|
215
|
+
}
|
|
149
216
|
/** Whether `runner` may run `run` of `agent`, before any lock is taken and before its payload is known. */
|
|
150
217
|
export function fits(runner, agent, run, now) {
|
|
151
|
-
return
|
|
218
|
+
return ((!run.runnerPinId || run.runnerPinId === runner.id) &&
|
|
219
|
+
fitsActor(runner, quotaAgent(run, agent), run.actorUserId, toRun(run).requires, now));
|
|
152
220
|
}
|
|
153
221
|
/** The runner features a payload needs. */
|
|
154
222
|
export function payloadRequires(dirs, skills, variables, workspaceKey = false) {
|
|
@@ -185,7 +253,7 @@ export function skillTargets(_agent, assembly) {
|
|
|
185
253
|
...assembly.dirs.flatMap((dir) => dir.scopeId ? [{ scope: 'workdir', scopeId: dir.scopeId }] : []),
|
|
186
254
|
];
|
|
187
255
|
}
|
|
188
|
-
function fingerprint(agent, entry, layers, skills) {
|
|
256
|
+
function fingerprint(agent, entry, layers, skills, readOnly) {
|
|
189
257
|
return createHash('sha256')
|
|
190
258
|
.update(JSON.stringify([
|
|
191
259
|
entry.tool,
|
|
@@ -194,12 +262,16 @@ function fingerprint(agent, entry, layers, skills) {
|
|
|
194
262
|
layers.system,
|
|
195
263
|
layers.agent,
|
|
196
264
|
skills.map((skill) => [skill.slug, skill.hash]),
|
|
265
|
+
// Keep existing writable fingerprints unchanged across upgrades.
|
|
266
|
+
...(readOnly ? [{ readOnly: true }] : []),
|
|
197
267
|
]))
|
|
198
268
|
.digest('hex');
|
|
199
269
|
}
|
|
200
270
|
/** The brief for `assembly`, with the skills a run would get. */
|
|
201
271
|
export function briefOf(agent, assembly, skills, cli, appName) {
|
|
202
|
-
|
|
272
|
+
// Custom system prompts (such as conversations) keep their own layout.
|
|
273
|
+
const stablePrefix = assembly.briefLayout === 'stable-prefix' && assembly.system === undefined;
|
|
274
|
+
const input = {
|
|
203
275
|
agentName: agent.name,
|
|
204
276
|
subject: { noun: assembly.subject.noun, key: assembly.subject.key },
|
|
205
277
|
task: assembly.task,
|
|
@@ -210,14 +282,20 @@ export function briefOf(agent, assembly, skills, cli, appName) {
|
|
|
210
282
|
cli,
|
|
211
283
|
appName,
|
|
212
284
|
...(assembly.guidance ? { guidance: assembly.guidance } : {}),
|
|
213
|
-
}
|
|
285
|
+
};
|
|
286
|
+
const rendered = renderBrief(input);
|
|
214
287
|
const layers = assembly.system === undefined
|
|
215
288
|
? rendered
|
|
216
289
|
: { ...rendered, system: assembly.system };
|
|
217
290
|
const turn = assembly.turn.previousSummary
|
|
218
291
|
? `${assembly.turn.prompt}\n\nWhat earlier runs on this already did:\n\n${assembly.turn.previousSummary}`
|
|
219
292
|
: assembly.turn.prompt;
|
|
220
|
-
|
|
293
|
+
// Keep the logical layers (and their session fingerprint) unchanged. Only the outgoing text is reordered,
|
|
294
|
+
// so an ordinary follow-up can still resume the session created by an independent review.
|
|
295
|
+
const prompt = stablePrefix
|
|
296
|
+
? joinBrief(renderBrief({ ...input, stablePrefix: true }), true)
|
|
297
|
+
: joinBrief(layers);
|
|
298
|
+
return { layers, turn, prompt };
|
|
221
299
|
}
|
|
222
300
|
/** Whether `runner` asks for repository credentials on demand rather than taking them with the claim. */
|
|
223
301
|
export function asksCredentialsOnDemand(runner) {
|
|
@@ -330,8 +408,16 @@ export function createClaimService(deps) {
|
|
|
330
408
|
const installed = connected.filter((runner) => runner.tools.some((tool) => tool.kind === entry.tool));
|
|
331
409
|
const usable = installed.filter((runner) => runsEntry(runner, entry, clock.now().getTime()));
|
|
332
410
|
const online = usable.filter((runner) => runner.trust === 'team' || runner.ownerUserId === run.actorUserId);
|
|
411
|
+
const quota = usable.length
|
|
412
|
+
? []
|
|
413
|
+
: quotaForAgent(installed, single, run.actorUserId, now.getTime());
|
|
333
414
|
const compatible = [];
|
|
334
|
-
const loads =
|
|
415
|
+
const loads = quota.map((runner) => ({
|
|
416
|
+
name: runner.runnerName,
|
|
417
|
+
ownerUserId: installed.find((item) => item.id === runner.runnerId)
|
|
418
|
+
.ownerUserId,
|
|
419
|
+
reason: 'toolQuota',
|
|
420
|
+
}));
|
|
335
421
|
let unknown = false;
|
|
336
422
|
let incompatible = false;
|
|
337
423
|
let ready = false;
|
|
@@ -429,11 +515,13 @@ export function createClaimService(deps) {
|
|
|
429
515
|
: 'restricted'
|
|
430
516
|
: usable.length
|
|
431
517
|
? 'noSharedRunner'
|
|
432
|
-
:
|
|
433
|
-
? '
|
|
434
|
-
:
|
|
435
|
-
? '
|
|
436
|
-
:
|
|
518
|
+
: quota.length
|
|
519
|
+
? 'toolQuota'
|
|
520
|
+
: installed.length
|
|
521
|
+
? 'toolUnavailable'
|
|
522
|
+
: connected.length
|
|
523
|
+
? 'toolNotInstalled'
|
|
524
|
+
: 'offline';
|
|
437
525
|
if (!ready &&
|
|
438
526
|
compatible.length > 0 &&
|
|
439
527
|
loads.every((runner) => runner.reason === 'updatePending' || runner.reason === 'pruning'))
|
|
@@ -454,6 +542,13 @@ export function createClaimService(deps) {
|
|
|
454
542
|
tool: entry.tool,
|
|
455
543
|
model: entry.model,
|
|
456
544
|
reason,
|
|
545
|
+
...(reason === 'toolQuota'
|
|
546
|
+
? {
|
|
547
|
+
until: quota
|
|
548
|
+
.flatMap((runner) => runner.resetsAt ? [runner.resetsAt] : [])
|
|
549
|
+
.sort()[0] ?? null,
|
|
550
|
+
}
|
|
551
|
+
: {}),
|
|
457
552
|
compatibleCount: compatible.length,
|
|
458
553
|
blockers: [
|
|
459
554
|
...(unknown ? ['unknown'] : []),
|
|
@@ -667,6 +762,7 @@ export function createClaimService(deps) {
|
|
|
667
762
|
const token = await mintRunToken(conn, deps, run, runner.id);
|
|
668
763
|
const git = await repoAccessFor(conn, deps.repoAccess, {
|
|
669
764
|
claim,
|
|
765
|
+
readOnly: assembly.readOnly === true,
|
|
670
766
|
repos: dirs.filter((dir) => dir.kind === 'repo'),
|
|
671
767
|
onDemand: asksCredentialsOnDemand(runner),
|
|
672
768
|
tools: wrapsHostTools(runner),
|
|
@@ -683,16 +779,20 @@ export function createClaimService(deps) {
|
|
|
683
779
|
});
|
|
684
780
|
const render = async () => briefOf(agent, await withSections(conn, deps.sections, effectiveClaim, assembly, prepared), skills, deps.cli.name, deps.app.name);
|
|
685
781
|
let brief = await render();
|
|
686
|
-
const briefFingerprint = fingerprint(agent, entry, brief.layers, skills);
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
!
|
|
691
|
-
|
|
782
|
+
const briefFingerprint = fingerprint(agent, entry, brief.layers, skills, assembly.readOnly === true);
|
|
783
|
+
const strict = Boolean(run.runnerPinId && !run.freshSession);
|
|
784
|
+
const resume = strict
|
|
785
|
+
? Boolean(run.sessionId)
|
|
786
|
+
: !run.freshSession &&
|
|
787
|
+
binding.sessionPolicy?.(effectiveClaim) !== 'fresh' &&
|
|
788
|
+
!clean &&
|
|
789
|
+
session &&
|
|
790
|
+
!session.poisoned &&
|
|
791
|
+
session.fingerprint === briefFingerprint;
|
|
692
792
|
// A kept session that cannot be resumed: the new one is told what came before, as a first one is. The history
|
|
693
793
|
// is outside the fingerprint, so it does not change.
|
|
694
794
|
let recoveryContext;
|
|
695
|
-
if (session) {
|
|
795
|
+
if (session && !strict) {
|
|
696
796
|
const fresh = await binding.context.assemble(conn, { ...claim, freshSession: true }, prepared.get('subject'));
|
|
697
797
|
if (resume) {
|
|
698
798
|
// Should the kept session be gone on the runner, the new one hears what came before from this.
|
|
@@ -726,6 +826,11 @@ export function createClaimService(deps) {
|
|
|
726
826
|
'The previous attempt was interrupted by a runner upgrade. Continue from the current state.',
|
|
727
827
|
]
|
|
728
828
|
: []),
|
|
829
|
+
...(strict
|
|
830
|
+
? [
|
|
831
|
+
'Quota recovery: continue the interrupted step from the existing session, progress and files.',
|
|
832
|
+
]
|
|
833
|
+
: []),
|
|
729
834
|
...resumeNotes,
|
|
730
835
|
...(sessionNote ? [sessionNote] : []),
|
|
731
836
|
].join('\n\n');
|
|
@@ -738,6 +843,7 @@ export function createClaimService(deps) {
|
|
|
738
843
|
createdAt: nowText,
|
|
739
844
|
});
|
|
740
845
|
const payload = {
|
|
846
|
+
...(strict ? { session: { strict: true } } : {}),
|
|
741
847
|
...(run.subjectKind === 'conversation' &&
|
|
742
848
|
runner.features.includes('outputBlocks')
|
|
743
849
|
? { outputBlocks: true }
|
|
@@ -768,8 +874,14 @@ export function createClaimService(deps) {
|
|
|
768
874
|
prompt: {
|
|
769
875
|
system: brief.prompt,
|
|
770
876
|
turn,
|
|
771
|
-
session: resume ? 'resume' : 'fresh',
|
|
772
|
-
...(
|
|
877
|
+
session: strict || resume ? 'resume' : 'fresh',
|
|
878
|
+
...(strict
|
|
879
|
+
? run.sessionId
|
|
880
|
+
? { resumeSessionId: run.sessionId }
|
|
881
|
+
: {}
|
|
882
|
+
: resume && session
|
|
883
|
+
? { resumeSessionId: session.sessionId }
|
|
884
|
+
: {}),
|
|
773
885
|
...(resume && recoveryContext ? { recoveryContext } : {}),
|
|
774
886
|
},
|
|
775
887
|
inputs,
|
|
@@ -1033,7 +1145,11 @@ export function createClaimService(deps) {
|
|
|
1033
1145
|
const pinned = await pin.runnerOf(conn, toRun(run));
|
|
1034
1146
|
return pinned === null || pinned === runner.id;
|
|
1035
1147
|
}
|
|
1036
|
-
async function attempt(runner, agent, candidate, reported
|
|
1148
|
+
async function attempt(runner, agent, candidate, reported, pass,
|
|
1149
|
+
/** Only this runner may take it: no preference wait (`preferenceDelay`). */
|
|
1150
|
+
bound) {
|
|
1151
|
+
if (candidate.runnerPinId && candidate.runnerPinId !== runner.id)
|
|
1152
|
+
return { kind: 'skipped' };
|
|
1037
1153
|
const conn = tx.read();
|
|
1038
1154
|
// Rules that do not depend on who the run acts as come first, and settle the rest of the pass: an agent at its
|
|
1039
1155
|
// limit is checked once per poll, not once per queued run.
|
|
@@ -1061,6 +1177,17 @@ export function createClaimService(deps) {
|
|
|
1061
1177
|
const consent = await commentConsentFor(conn, runner, candidate);
|
|
1062
1178
|
if (!consent.allowed)
|
|
1063
1179
|
return { kind: 'skipped' };
|
|
1180
|
+
// Wait this runner's turn for the run, after every read-only rule and before any lock or write. The transaction
|
|
1181
|
+
// checks again whether the run is still queued and which tool has room.
|
|
1182
|
+
const paced = pickOpenEntry(runner, agent, (tool) => (pass.byTool[tool] ?? 0) < toolLimit(runner, tool) &&
|
|
1183
|
+
reported.has(tool), clock.now().getTime());
|
|
1184
|
+
if (!paced)
|
|
1185
|
+
return { kind: 'skipped' };
|
|
1186
|
+
const wait = claimPace(runner, paced.tool, pass.byTool, candidate, clock.now().getTime(), preferenceDelay(runner, candidate, bound));
|
|
1187
|
+
if (wait > 0)
|
|
1188
|
+
await (deps.pause ?? pause)(wait, pass.signal);
|
|
1189
|
+
if (pass.signal?.aborted)
|
|
1190
|
+
return { kind: 'aborted' };
|
|
1064
1191
|
// Read once, before the transaction, and handed to every repository access hook of this claim. A failed read
|
|
1065
1192
|
// leaves the run queued for the next poll rather than letting a provider pick an identity without it.
|
|
1066
1193
|
let responsibleUserId = null;
|
|
@@ -1136,6 +1263,8 @@ export function createClaimService(deps) {
|
|
|
1136
1263
|
status: 'dispatched',
|
|
1137
1264
|
failureReason: null,
|
|
1138
1265
|
failureDetail: null,
|
|
1266
|
+
failureParams: null,
|
|
1267
|
+
claimFailures: 0,
|
|
1139
1268
|
runnerId: runner.id,
|
|
1140
1269
|
// Brief providers observe this reservation inside the claim transaction.
|
|
1141
1270
|
// Replace a previous attempt's tool before computing the roster.
|
|
@@ -1223,7 +1352,16 @@ export function createClaimService(deps) {
|
|
|
1223
1352
|
: null,
|
|
1224
1353
|
},
|
|
1225
1354
|
});
|
|
1226
|
-
|
|
1355
|
+
if (current.runnerPinId && !current.freshSession)
|
|
1356
|
+
await deps.subjects
|
|
1357
|
+
.get(run.subjectKind)
|
|
1358
|
+
?.sink?.onQuotaResumed?.(unit, toRun(run));
|
|
1359
|
+
unit.emit({
|
|
1360
|
+
type: 'run.changed',
|
|
1361
|
+
...(current.runnerPinId ? { quotaWait: false } : {}),
|
|
1362
|
+
runId: run.id,
|
|
1363
|
+
status: 'dispatched',
|
|
1364
|
+
});
|
|
1227
1365
|
if (variableRefs(candidate.teamOnlyVariables).length > 0)
|
|
1228
1366
|
unit.emit({
|
|
1229
1367
|
type: 'notice.cleared',
|
|
@@ -1266,28 +1404,98 @@ export function createClaimService(deps) {
|
|
|
1266
1404
|
}
|
|
1267
1405
|
}
|
|
1268
1406
|
}
|
|
1269
|
-
/**
|
|
1270
|
-
async function recordFailure(
|
|
1271
|
-
deps.onClaimFailure?.(
|
|
1407
|
+
/** Repository failures have their own budget; ordinary assembly failures keep the existing three-try limit. */
|
|
1408
|
+
async function recordFailure(candidate, error) {
|
|
1409
|
+
deps.onClaimFailure?.(candidate.id, error.cause);
|
|
1272
1410
|
await tx.run(async (unit) => {
|
|
1273
|
-
const run = await findRunRecord(unit.conn,
|
|
1274
|
-
if (!run ||
|
|
1411
|
+
const run = await findRunRecord(unit.conn, candidate.id);
|
|
1412
|
+
if (!run ||
|
|
1413
|
+
run.status !== 'queued' ||
|
|
1414
|
+
run.revision !== candidate.revision ||
|
|
1415
|
+
Number(run.attempt) !== Number(candidate.attempt) ||
|
|
1416
|
+
run.actorUserId !== candidate.actorUserId ||
|
|
1417
|
+
!(await queuedIdentityCurrent(unit.conn, deps, run)))
|
|
1418
|
+
return;
|
|
1419
|
+
const previous = jsonObject(run.failureParams);
|
|
1420
|
+
const cause = error.cause;
|
|
1421
|
+
if (cause instanceof RepoAccessError) {
|
|
1422
|
+
const now = clock.now().getTime();
|
|
1423
|
+
const repoFailures = Number(previous.repoFailures ?? 0) + 1;
|
|
1424
|
+
const first = typeof previous.repoFirstFailureAt === 'string'
|
|
1425
|
+
? Date.parse(previous.repoFirstFailureAt)
|
|
1426
|
+
: now;
|
|
1427
|
+
const retryAt = typeof cause.params.retryAt === 'string'
|
|
1428
|
+
? Date.parse(cause.params.retryAt)
|
|
1429
|
+
: NaN;
|
|
1430
|
+
const delays = [1, 2, 4, 8, 15];
|
|
1431
|
+
const next = Math.max(now + (delays[repoFailures - 1] ?? 15) * 60_000, Number.isFinite(retryAt) ? retryAt : 0);
|
|
1432
|
+
const params = {
|
|
1433
|
+
...cause.params,
|
|
1434
|
+
repoFailures,
|
|
1435
|
+
repoFirstFailureAt: new Date(first).toISOString(),
|
|
1436
|
+
};
|
|
1437
|
+
const reason = cause.kind === 'denied'
|
|
1438
|
+
? 'repoAccessDenied'
|
|
1439
|
+
: 'repoAccessUnavailable';
|
|
1440
|
+
if (cause.kind === 'denied' ||
|
|
1441
|
+
repoFailures >= 6 ||
|
|
1442
|
+
next > first + 60 * 60_000) {
|
|
1443
|
+
await finishRun(unit, deps, run, {
|
|
1444
|
+
status: 'failed',
|
|
1445
|
+
reason,
|
|
1446
|
+
detail: cause.message,
|
|
1447
|
+
failureParams: params,
|
|
1448
|
+
}, true);
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
const availableAt = new Date(next).toISOString();
|
|
1452
|
+
const changed = await runsRepo(unit.conn).updateMany({
|
|
1453
|
+
filter: {
|
|
1454
|
+
id: run.id,
|
|
1455
|
+
status: 'queued',
|
|
1456
|
+
revision: run.revision,
|
|
1457
|
+
attempt: Number(run.attempt),
|
|
1458
|
+
actorUserId: run.actorUserId,
|
|
1459
|
+
},
|
|
1460
|
+
values: {
|
|
1461
|
+
availableAt,
|
|
1462
|
+
failureReason: reason,
|
|
1463
|
+
failureDetail: cause.message.slice(0, 10_000),
|
|
1464
|
+
failureParams: params,
|
|
1465
|
+
updatedAt: clock.now().toISOString(),
|
|
1466
|
+
},
|
|
1467
|
+
});
|
|
1468
|
+
if (changed.updatedCount > 0) {
|
|
1469
|
+
unit.emit({ type: 'run.changed', runId: run.id, status: 'queued' });
|
|
1470
|
+
unit.emit({ type: 'run.requeued', runId: run.id, availableAt });
|
|
1471
|
+
}
|
|
1275
1472
|
return;
|
|
1473
|
+
}
|
|
1276
1474
|
const failures = Number(run.claimFailures) + 1;
|
|
1475
|
+
// Preserve only the independent repository retry budget across an unrelated preparation failure.
|
|
1476
|
+
const budget = previous.repoFailures
|
|
1477
|
+
? {
|
|
1478
|
+
repoFailures: previous.repoFailures,
|
|
1479
|
+
repoFirstFailureAt: previous.repoFirstFailureAt,
|
|
1480
|
+
repoAccess: previous.repoAccess ?? previous,
|
|
1481
|
+
}
|
|
1482
|
+
: null;
|
|
1277
1483
|
if (failures >= MAX_CLAIM_FAILURES) {
|
|
1278
1484
|
await finishRun(unit, deps, run, {
|
|
1279
1485
|
status: 'failed',
|
|
1280
1486
|
reason: 'setupFailed',
|
|
1281
1487
|
detail: error.message,
|
|
1282
|
-
|
|
1488
|
+
failureParams: null,
|
|
1489
|
+
}, true);
|
|
1283
1490
|
return;
|
|
1284
1491
|
}
|
|
1285
1492
|
await runsRepo(unit.conn).updateMany({
|
|
1286
|
-
filter: { id:
|
|
1493
|
+
filter: { id: run.id, status: 'queued', revision: run.revision },
|
|
1287
1494
|
values: {
|
|
1288
1495
|
claimFailures: failures,
|
|
1289
1496
|
failureReason: 'setupFailed',
|
|
1290
1497
|
failureDetail: error.message.slice(0, 10_000),
|
|
1498
|
+
failureParams: budget,
|
|
1291
1499
|
},
|
|
1292
1500
|
});
|
|
1293
1501
|
});
|
|
@@ -1511,8 +1719,8 @@ export function createClaimService(deps) {
|
|
|
1511
1719
|
}
|
|
1512
1720
|
return true;
|
|
1513
1721
|
}
|
|
1514
|
-
/** False stops the whole request when a transactional capacity check finds the runner full. */
|
|
1515
|
-
async function claimPage(runner, seen, reported, queued, full) {
|
|
1722
|
+
/** False stops the whole request when a transactional capacity check finds the runner full, or the request ended. */
|
|
1723
|
+
async function claimPage(runner, seen, reported, queued, full, pass) {
|
|
1516
1724
|
const conn = tx.read();
|
|
1517
1725
|
const affinity = await affinityBatch(queued, runner);
|
|
1518
1726
|
const agents = await findAgents(conn, queued
|
|
@@ -1524,10 +1732,13 @@ export function createClaimService(deps) {
|
|
|
1524
1732
|
? deps.runners.all(conn)
|
|
1525
1733
|
: Promise.resolve([runner]));
|
|
1526
1734
|
for (const candidate of queued) {
|
|
1527
|
-
if (seen.has(candidate.id) ||
|
|
1735
|
+
if (seen.has(candidate.id) ||
|
|
1736
|
+
full.has(candidate.agentId) ||
|
|
1737
|
+
(candidate.runnerPinId && candidate.runnerPinId !== runner.id))
|
|
1528
1738
|
continue;
|
|
1529
1739
|
seen.add(candidate.id);
|
|
1530
|
-
const
|
|
1740
|
+
const configured = agents.get(candidate.agentId);
|
|
1741
|
+
const agent = configured ? quotaAgent(candidate, configured) : undefined;
|
|
1531
1742
|
if (!agent ||
|
|
1532
1743
|
(!deps.subjects.get(candidate.subjectKind)?.actsAs &&
|
|
1533
1744
|
!fits(runner, agent, candidate, clock.now().getTime())))
|
|
@@ -1537,7 +1748,8 @@ export function createClaimService(deps) {
|
|
|
1537
1748
|
continue;
|
|
1538
1749
|
// Work bound to another runner waits for it; work bound here takes no other preference.
|
|
1539
1750
|
const pin = deps.subjects.get(candidate.subjectKind)?.runnerPin;
|
|
1540
|
-
const pinned =
|
|
1751
|
+
const pinned = candidate.runnerPinId ??
|
|
1752
|
+
(pin ? await pin.runnerOf(conn, toRun(candidate)) : null);
|
|
1541
1753
|
if (pinned !== null && pinned !== runner.id)
|
|
1542
1754
|
continue;
|
|
1543
1755
|
const preferred = affinity.preferred.get(candidate.id);
|
|
@@ -1551,9 +1763,13 @@ export function createClaimService(deps) {
|
|
|
1551
1763
|
if (!pickOpenEntry(runner, agent, (tool) => reported.has(tool), clock.now().getTime()))
|
|
1552
1764
|
continue;
|
|
1553
1765
|
try {
|
|
1554
|
-
const result = await attempt(runner, agent, candidate, reported
|
|
1766
|
+
const result = await attempt(runner, agent, candidate, reported, pass, reserved === runner.id ||
|
|
1767
|
+
pinned !== null ||
|
|
1768
|
+
preferred?.id === runner.id);
|
|
1555
1769
|
if (result.kind === 'claimed')
|
|
1556
1770
|
return result.payload;
|
|
1771
|
+
if (result.kind === 'aborted')
|
|
1772
|
+
return false;
|
|
1557
1773
|
if (result.kind === 'full') {
|
|
1558
1774
|
if (result.scope === 'runner')
|
|
1559
1775
|
return false;
|
|
@@ -1568,7 +1784,7 @@ export function createClaimService(deps) {
|
|
|
1568
1784
|
continue;
|
|
1569
1785
|
if (!(error instanceof AssemblyError))
|
|
1570
1786
|
throw error;
|
|
1571
|
-
await recordFailure(candidate
|
|
1787
|
+
await recordFailure(candidate, error);
|
|
1572
1788
|
}
|
|
1573
1789
|
}
|
|
1574
1790
|
return null;
|
|
@@ -1615,7 +1831,7 @@ export function createClaimService(deps) {
|
|
|
1615
1831
|
const agent = await findAgent(conn, run.agentId);
|
|
1616
1832
|
return agent ? selectionFacts(conn, toRun(run), agent, false) : null;
|
|
1617
1833
|
},
|
|
1618
|
-
async claim(runner, free, tools, scan = createClaimScan()) {
|
|
1834
|
+
async claim(runner, free, tools, scan = createClaimScan(), signal) {
|
|
1619
1835
|
if (scan.remainingPages <= 0 ||
|
|
1620
1836
|
runner.status !== 'online' ||
|
|
1621
1837
|
runner.draining ||
|
|
@@ -1628,8 +1844,10 @@ export function createClaimService(deps) {
|
|
|
1628
1844
|
const pages = candidates(runner, scan);
|
|
1629
1845
|
let queued = [];
|
|
1630
1846
|
while (payloads.length < limit) {
|
|
1631
|
-
|
|
1847
|
+
const held = await deps.slotsHeld(tx.read(), runner.id);
|
|
1848
|
+
if (held.runs + held.jobs >= runner.slots)
|
|
1632
1849
|
break;
|
|
1850
|
+
const pass = { byTool: held.byTool ?? {}, signal };
|
|
1633
1851
|
const full = new Set();
|
|
1634
1852
|
let payload;
|
|
1635
1853
|
do {
|
|
@@ -1639,7 +1857,7 @@ export function createClaimService(deps) {
|
|
|
1639
1857
|
return payloads;
|
|
1640
1858
|
queued = page.value;
|
|
1641
1859
|
}
|
|
1642
|
-
payload = await claimPage(runner, seen, reported, queued, full);
|
|
1860
|
+
payload = await claimPage(runner, seen, reported, queued, full, pass);
|
|
1643
1861
|
if (payload === false)
|
|
1644
1862
|
return payloads;
|
|
1645
1863
|
if (!payload)
|