@elizaos/agent 2.0.0-alpha.144 → 2.0.0-alpha.151
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/apps/app-lifeops/src/actions/inbox-digest.d.ts +2 -0
- package/apps/app-lifeops/src/actions/inbox-digest.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox-digest.js +1 -0
- package/apps/app-lifeops/src/actions/inbox-respond.d.ts +2 -0
- package/apps/app-lifeops/src/actions/inbox-respond.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox-respond.js +1 -0
- package/apps/app-lifeops/src/actions/inbox-triage.d.ts +2 -0
- package/apps/app-lifeops/src/actions/inbox-triage.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox-triage.js +1 -0
- package/apps/app-lifeops/src/actions/inbox.d.ts +3 -0
- package/apps/app-lifeops/src/actions/inbox.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox.js +856 -0
- package/apps/app-lifeops/src/actions/update-owner-profile.d.ts +3 -0
- package/apps/app-lifeops/src/actions/update-owner-profile.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/update-owner-profile.js +131 -0
- package/apps/app-lifeops/src/inbox/channel-deep-links.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/config.d.ts.map +1 -0
- package/{packages/agent → apps/app-lifeops}/src/inbox/config.js +1 -1
- package/apps/app-lifeops/src/inbox/message-fetcher.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/reflection.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/repository.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/triage-classifier.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/types.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/index.d.ts +25 -0
- package/apps/app-lifeops/src/lifeops/index.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/index.js +24 -0
- package/apps/app-lifeops/src/lifeops/screen-context.d.ts +52 -0
- package/apps/app-lifeops/src/lifeops/screen-context.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/screen-context.js +332 -0
- package/apps/app-lifeops/src/plugin.d.ts +3 -0
- package/apps/app-lifeops/src/plugin.d.ts.map +1 -1
- package/apps/app-lifeops/src/plugin.js +16 -3
- package/apps/app-lifeops/src/providers/inbox-triage.d.ts +3 -0
- package/apps/app-lifeops/src/providers/inbox-triage.d.ts.map +1 -0
- package/apps/app-lifeops/src/providers/inbox-triage.js +89 -0
- package/package.json +6 -4
- package/packages/agent/src/actions/calendar.d.ts +1 -18
- package/packages/agent/src/actions/calendar.d.ts.map +1 -1
- package/packages/agent/src/actions/calendar.js +1 -3143
- package/packages/agent/src/actions/check-balance.d.ts +17 -0
- package/packages/agent/src/actions/check-balance.d.ts.map +1 -0
- package/packages/agent/src/actions/check-balance.js +167 -0
- package/packages/agent/src/actions/connector-resolver.d.ts +75 -0
- package/packages/agent/src/actions/connector-resolver.d.ts.map +1 -0
- package/packages/agent/src/actions/connector-resolver.js +245 -0
- package/packages/agent/src/actions/context-signal-lexicon.d.ts +1 -1
- package/packages/agent/src/actions/context-signal-lexicon.d.ts.map +1 -1
- package/packages/agent/src/actions/context-signal-lexicon.js +6 -0
- package/packages/agent/src/actions/eject-plugin.d.ts +3 -0
- package/packages/agent/src/actions/eject-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/eject-plugin.js +48 -0
- package/packages/agent/src/actions/execute-trade.d.ts +17 -0
- package/packages/agent/src/actions/execute-trade.d.ts.map +1 -0
- package/packages/agent/src/actions/execute-trade.js +299 -0
- package/packages/agent/src/actions/get-self-status.d.ts +13 -0
- package/packages/agent/src/actions/get-self-status.d.ts.map +1 -0
- package/packages/agent/src/actions/get-self-status.js +66 -0
- package/packages/agent/src/actions/gmail.d.ts +1 -32
- package/packages/agent/src/actions/gmail.d.ts.map +1 -1
- package/packages/agent/src/actions/gmail.js +1 -1734
- package/packages/agent/src/actions/inbox-digest.d.ts +1 -1
- package/packages/agent/src/actions/inbox-digest.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox-digest.js +1 -1
- package/packages/agent/src/actions/inbox-respond.d.ts +1 -1
- package/packages/agent/src/actions/inbox-respond.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox-respond.js +1 -1
- package/packages/agent/src/actions/inbox-triage.d.ts +1 -1
- package/packages/agent/src/actions/inbox-triage.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox-triage.js +1 -1
- package/packages/agent/src/actions/inbox.d.ts +1 -2
- package/packages/agent/src/actions/inbox.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox.js +1 -856
- package/packages/agent/src/actions/index.d.ts +13 -0
- package/packages/agent/src/actions/index.d.ts.map +1 -1
- package/packages/agent/src/actions/index.js +13 -0
- package/packages/agent/src/actions/install-plugin.d.ts +3 -0
- package/packages/agent/src/actions/install-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/install-plugin.js +65 -0
- package/packages/agent/src/actions/life-goal-extractor.d.ts +1 -68
- package/packages/agent/src/actions/life-goal-extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life-goal-extractor.js +1 -354
- package/packages/agent/src/actions/life-param-extractor.d.ts +1 -77
- package/packages/agent/src/actions/life-param-extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life-param-extractor.js +1 -423
- package/packages/agent/src/actions/life-recent-context.d.ts +1 -8
- package/packages/agent/src/actions/life-recent-context.d.ts.map +1 -1
- package/packages/agent/src/actions/life-recent-context.js +1 -84
- package/packages/agent/src/actions/life-update-extractor.d.ts +1 -26
- package/packages/agent/src/actions/life-update-extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life-update-extractor.js +1 -195
- package/packages/agent/src/actions/life.d.ts +1 -8
- package/packages/agent/src/actions/life.d.ts.map +1 -1
- package/packages/agent/src/actions/life.extractor.d.ts +1 -17
- package/packages/agent/src/actions/life.extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life.extractor.js +1 -264
- package/packages/agent/src/actions/life.js +1 -3379
- package/packages/agent/src/actions/lifeops-extraction-config.d.ts +1 -15
- package/packages/agent/src/actions/lifeops-extraction-config.d.ts.map +1 -1
- package/packages/agent/src/actions/lifeops-extraction-config.js +1 -25
- package/packages/agent/src/actions/lifeops-google-helpers.d.ts +1 -61
- package/packages/agent/src/actions/lifeops-google-helpers.d.ts.map +1 -1
- package/packages/agent/src/actions/lifeops-google-helpers.js +1 -607
- package/packages/agent/src/actions/list-ejected.d.ts +3 -0
- package/packages/agent/src/actions/list-ejected.d.ts.map +1 -0
- package/packages/agent/src/actions/list-ejected.js +35 -0
- package/packages/agent/src/actions/log-level.d.ts +3 -0
- package/packages/agent/src/actions/log-level.d.ts.map +1 -0
- package/packages/agent/src/actions/log-level.js +125 -0
- package/packages/agent/src/actions/manage-tasks.d.ts.map +1 -1
- package/packages/agent/src/actions/manage-tasks.js +51 -15
- package/packages/agent/src/actions/media.d.ts +21 -0
- package/packages/agent/src/actions/media.d.ts.map +1 -0
- package/packages/agent/src/actions/media.js +384 -0
- package/packages/agent/src/actions/read-messages.d.ts +14 -0
- package/packages/agent/src/actions/read-messages.d.ts.map +1 -0
- package/packages/agent/src/actions/read-messages.js +228 -0
- package/packages/agent/src/actions/reinject-plugin.d.ts +3 -0
- package/packages/agent/src/actions/reinject-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/reinject-plugin.js +47 -0
- package/packages/agent/src/actions/send-message.d.ts +0 -7
- package/packages/agent/src/actions/send-message.d.ts.map +1 -1
- package/packages/agent/src/actions/send-message.js +170 -49
- package/packages/agent/src/actions/sync-plugin.d.ts +3 -0
- package/packages/agent/src/actions/sync-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/sync-plugin.js +47 -0
- package/packages/agent/src/actions/timezone-normalization.d.ts +1 -2
- package/packages/agent/src/actions/timezone-normalization.d.ts.map +1 -1
- package/packages/agent/src/actions/timezone-normalization.js +1 -107
- package/packages/agent/src/actions/transfer-token.d.ts +17 -0
- package/packages/agent/src/actions/transfer-token.d.ts.map +1 -0
- package/packages/agent/src/actions/transfer-token.js +470 -0
- package/packages/agent/src/actions/update-owner-profile.d.ts +1 -2
- package/packages/agent/src/actions/update-owner-profile.d.ts.map +1 -1
- package/packages/agent/src/actions/update-owner-profile.js +1 -131
- package/packages/agent/src/actions/wallet-action-shared.d.ts +15 -0
- package/packages/agent/src/actions/wallet-action-shared.d.ts.map +1 -0
- package/packages/agent/src/actions/wallet-action-shared.js +24 -0
- package/packages/agent/src/api/agent-admin-routes.d.ts.map +1 -1
- package/packages/agent/src/api/agent-admin-routes.js +1 -1
- package/packages/agent/src/api/binance-skill-helpers.d.ts.map +1 -1
- package/packages/agent/src/api/binance-skill-helpers.js +8 -3
- package/packages/agent/src/api/chat-routes.d.ts.map +1 -1
- package/packages/agent/src/api/chat-routes.js +20 -5
- package/packages/agent/src/api/coding-agents-auth-sanitize.d.ts +1 -22
- package/packages/agent/src/api/coding-agents-auth-sanitize.d.ts.map +1 -1
- package/packages/agent/src/api/coding-agents-auth-sanitize.js +1 -39
- package/packages/agent/src/api/coding-agents-preflight-normalize.d.ts +1 -28
- package/packages/agent/src/api/coding-agents-preflight-normalize.d.ts.map +1 -1
- package/packages/agent/src/api/coding-agents-preflight-normalize.js +1 -45
- package/packages/agent/src/api/coordinator-types.d.ts +1 -46
- package/packages/agent/src/api/coordinator-types.d.ts.map +1 -1
- package/packages/agent/src/api/coordinator-types.js +1 -1
- package/packages/agent/src/api/coordinator-wiring.d.ts +1 -45
- package/packages/agent/src/api/coordinator-wiring.d.ts.map +1 -1
- package/packages/agent/src/api/coordinator-wiring.js +1 -108
- package/packages/agent/src/api/index.d.ts +1 -1
- package/packages/agent/src/api/index.d.ts.map +1 -1
- package/packages/agent/src/api/index.js +1 -1
- package/packages/agent/src/api/lifeops-browser-packaging.d.ts +1 -15
- package/packages/agent/src/api/lifeops-browser-packaging.d.ts.map +1 -1
- package/packages/agent/src/api/lifeops-browser-packaging.js +1 -305
- package/packages/agent/src/api/lifeops-routes.d.ts +1 -19
- package/packages/agent/src/api/lifeops-routes.d.ts.map +1 -1
- package/packages/agent/src/api/lifeops-routes.js +1 -1173
- package/packages/agent/src/api/server.d.ts.map +1 -1
- package/packages/agent/src/api/server.js +6 -6
- package/packages/agent/src/api/task-agent-message-routing.d.ts +1 -9
- package/packages/agent/src/api/task-agent-message-routing.d.ts.map +1 -1
- package/packages/agent/src/api/task-agent-message-routing.js +1 -62
- package/packages/agent/src/api/website-blocker-routes.d.ts +1 -6
- package/packages/agent/src/api/website-blocker-routes.d.ts.map +1 -1
- package/packages/agent/src/api/website-blocker-routes.js +1 -174
- package/packages/agent/src/config/types.agent-defaults.d.ts +1 -1
- package/packages/agent/src/config/types.agent-defaults.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-eval-client.d.ts +1 -38
- package/packages/agent/src/evals/coordinator-eval-client.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-eval-client.js +1 -138
- package/packages/agent/src/evals/coordinator-live-runner.d.ts +1 -56
- package/packages/agent/src/evals/coordinator-live-runner.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-live-runner.js +1 -546
- package/packages/agent/src/evals/coordinator-preflight.d.ts +1 -31
- package/packages/agent/src/evals/coordinator-preflight.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-preflight.js +1 -296
- package/packages/agent/src/evals/coordinator-scenarios.d.ts +1 -23
- package/packages/agent/src/evals/coordinator-scenarios.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-scenarios.js +1 -1141
- package/packages/agent/src/lifeops/app-state.d.ts +1 -10
- package/packages/agent/src/lifeops/app-state.d.ts.map +1 -1
- package/packages/agent/src/lifeops/app-state.js +1 -32
- package/packages/agent/src/lifeops/apple-reminders.d.ts +1 -57
- package/packages/agent/src/lifeops/apple-reminders.d.ts.map +1 -1
- package/packages/agent/src/lifeops/apple-reminders.js +1 -325
- package/packages/agent/src/lifeops/defaults.d.ts +1 -23
- package/packages/agent/src/lifeops/defaults.d.ts.map +1 -1
- package/packages/agent/src/lifeops/defaults.js +1 -205
- package/packages/agent/src/lifeops/engine.d.ts +1 -7
- package/packages/agent/src/lifeops/engine.d.ts.map +1 -1
- package/packages/agent/src/lifeops/engine.js +1 -389
- package/packages/agent/src/lifeops/goal-grounding.d.ts +1 -53
- package/packages/agent/src/lifeops/goal-grounding.d.ts.map +1 -1
- package/packages/agent/src/lifeops/goal-grounding.js +1 -147
- package/packages/agent/src/lifeops/goal-semantic-evaluator.d.ts +1 -11
- package/packages/agent/src/lifeops/goal-semantic-evaluator.d.ts.map +1 -1
- package/packages/agent/src/lifeops/goal-semantic-evaluator.js +1 -154
- package/packages/agent/src/lifeops/google-api-error.d.ts +1 -6
- package/packages/agent/src/lifeops/google-api-error.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-api-error.js +1 -35
- package/packages/agent/src/lifeops/google-calendar.d.ts +1 -52
- package/packages/agent/src/lifeops/google-calendar.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-calendar.js +1 -268
- package/packages/agent/src/lifeops/google-connector-gateway.d.ts +1 -18
- package/packages/agent/src/lifeops/google-connector-gateway.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-connector-gateway.js +1 -65
- package/packages/agent/src/lifeops/google-fetch.d.ts +1 -10
- package/packages/agent/src/lifeops/google-fetch.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-fetch.js +1 -85
- package/packages/agent/src/lifeops/google-gmail.d.ts +1 -53
- package/packages/agent/src/lifeops/google-gmail.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-gmail.js +1 -471
- package/packages/agent/src/lifeops/google-managed-client.d.ts +1 -126
- package/packages/agent/src/lifeops/google-managed-client.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-managed-client.js +1 -294
- package/packages/agent/src/lifeops/google-oauth.d.ts +1 -60
- package/packages/agent/src/lifeops/google-oauth.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-oauth.js +1 -494
- package/packages/agent/src/lifeops/google-scopes.d.ts +1 -12
- package/packages/agent/src/lifeops/google-scopes.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-scopes.js +1 -96
- package/packages/agent/src/lifeops/index.d.ts +1 -2
- package/packages/agent/src/lifeops/index.d.ts.map +1 -1
- package/packages/agent/src/lifeops/index.js +1 -2
- package/packages/agent/src/lifeops/owner-profile.d.ts +1 -14
- package/packages/agent/src/lifeops/owner-profile.d.ts.map +1 -1
- package/packages/agent/src/lifeops/owner-profile.js +1 -194
- package/packages/agent/src/lifeops/repository.d.ts +1 -208
- package/packages/agent/src/lifeops/repository.d.ts.map +1 -1
- package/packages/agent/src/lifeops/repository.js +1 -3187
- package/packages/agent/src/lifeops/runtime.d.ts +1 -13
- package/packages/agent/src/lifeops/runtime.d.ts.map +1 -1
- package/packages/agent/src/lifeops/runtime.js +1 -120
- package/packages/agent/src/lifeops/screen-context.d.ts +1 -51
- package/packages/agent/src/lifeops/screen-context.d.ts.map +1 -1
- package/packages/agent/src/lifeops/screen-context.js +1 -332
- package/packages/agent/src/lifeops/seed-routines.d.ts +1 -19
- package/packages/agent/src/lifeops/seed-routines.d.ts.map +1 -1
- package/packages/agent/src/lifeops/seed-routines.js +1 -111
- package/packages/agent/src/lifeops/service.d.ts +1 -274
- package/packages/agent/src/lifeops/service.d.ts.map +1 -1
- package/packages/agent/src/lifeops/service.js +1 -9260
- package/packages/agent/src/lifeops/sql.d.ts +1 -30
- package/packages/agent/src/lifeops/sql.d.ts.map +1 -1
- package/packages/agent/src/lifeops/sql.js +1 -247
- package/packages/agent/src/lifeops/time.d.ts +1 -16
- package/packages/agent/src/lifeops/time.d.ts.map +1 -1
- package/packages/agent/src/lifeops/time.js +1 -132
- package/packages/agent/src/lifeops/twilio.d.ts +1 -24
- package/packages/agent/src/lifeops/twilio.d.ts.map +1 -1
- package/packages/agent/src/lifeops/twilio.js +1 -157
- package/packages/agent/src/lifeops/x-poster.d.ts +1 -18
- package/packages/agent/src/lifeops/x-poster.d.ts.map +1 -1
- package/packages/agent/src/lifeops/x-poster.js +1 -148
- package/packages/agent/src/providers/inbox-triage.d.ts +1 -2
- package/packages/agent/src/providers/inbox-triage.d.ts.map +1 -1
- package/packages/agent/src/providers/inbox-triage.js +1 -89
- package/packages/agent/src/providers/index.d.ts +4 -1
- package/packages/agent/src/providers/index.d.ts.map +1 -1
- package/packages/agent/src/providers/index.js +4 -1
- package/packages/agent/src/providers/lifeops.d.ts +1 -2
- package/packages/agent/src/providers/lifeops.d.ts.map +1 -1
- package/packages/agent/src/providers/lifeops.js +1 -157
- package/packages/agent/src/providers/local-models.d.ts +118 -0
- package/packages/agent/src/providers/local-models.d.ts.map +1 -0
- package/packages/agent/src/providers/local-models.js +427 -0
- package/packages/agent/src/providers/media-provider.d.ts +192 -0
- package/packages/agent/src/providers/media-provider.d.ts.map +1 -0
- package/packages/agent/src/providers/media-provider.js +1088 -0
- package/packages/agent/src/providers/self-status.d.ts +4 -0
- package/packages/agent/src/providers/self-status.d.ts.map +1 -0
- package/packages/agent/src/providers/self-status.js +12 -0
- package/packages/agent/src/providers/tasks.d.ts.map +1 -1
- package/packages/agent/src/providers/tasks.js +7 -7
- package/packages/agent/src/runtime/core-plugins.js +1 -1
- package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -1
- package/packages/agent/src/runtime/eliza-plugin.js +1 -7
- package/packages/agent/src/runtime/eliza.js +2 -2
- package/packages/agent/src/runtime/plugin-collector.js +3 -3
- package/packages/agent/src/runtime/plugin-lifecycle.d.ts.map +1 -1
- package/packages/agent/src/runtime/plugin-lifecycle.js +3 -13
- package/packages/agent/src/runtime/trajectory-internals.d.ts.map +1 -1
- package/packages/agent/src/runtime/trajectory-internals.js +1 -3
- package/packages/agent/src/services/built-in-app-routes/hyperscape.d.ts.map +1 -1
- package/packages/agent/src/services/coding-task-executor.d.ts +3 -3
- package/packages/agent/src/services/coding-task-executor.js +3 -3
- package/packages/shared/src/awareness/index.d.ts +2 -0
- package/packages/shared/src/awareness/index.d.ts.map +1 -0
- package/packages/shared/src/awareness/index.js +1 -0
- package/packages/shared/src/awareness/registry.d.ts +27 -0
- package/packages/shared/src/awareness/registry.d.ts.map +1 -0
- package/packages/shared/src/awareness/registry.js +161 -0
- package/packages/shared/src/i18n/generated/validation-keyword-data.d.ts +24 -0
- package/packages/shared/src/i18n/generated/validation-keyword-data.d.ts.map +1 -1
- package/packages/shared/src/i18n/generated/validation-keyword-data.js +24 -0
- package/packages/shared/src/runtime-env.d.ts.map +1 -1
- package/packages/shared/src/runtime-env.js +5 -1
- package/packages/typescript/src/generated/action-docs.d.ts +135 -0
- package/packages/typescript/src/generated/action-docs.d.ts.map +1 -1
- package/packages/typescript/src/generated/action-docs.js +237 -0
- package/packages/typescript/src/i18n/generated/validation-keyword-data.d.ts +24 -0
- package/packages/typescript/src/i18n/generated/validation-keyword-data.d.ts.map +1 -1
- package/packages/typescript/src/i18n/generated/validation-keyword-data.js +24 -0
- package/packages/typescript/src/index.node.d.ts +2 -2
- package/packages/typescript/src/index.node.d.ts.map +1 -1
- package/packages/typescript/src/index.node.js +4 -3
- package/packages/typescript/src/plugin-lifecycle.d.ts.map +1 -1
- package/packages/typescript/src/plugin-lifecycle.js +42 -3
- package/packages/typescript/src/services/message.d.ts.map +1 -1
- package/packages/typescript/src/services/message.js +32 -0
- package/apps/app-training/src/core/cli.d.ts +0 -11
- package/apps/app-training/src/core/cli.d.ts.map +0 -1
- package/apps/app-training/src/core/cli.js +0 -302
- package/apps/app-training/src/core/context-audit.d.ts +0 -51
- package/apps/app-training/src/core/context-audit.d.ts.map +0 -1
- package/apps/app-training/src/core/context-audit.js +0 -141
- package/apps/app-training/src/core/context-catalog.d.ts +0 -47
- package/apps/app-training/src/core/context-catalog.d.ts.map +0 -1
- package/apps/app-training/src/core/context-catalog.js +0 -259
- package/apps/app-training/src/core/context-types.d.ts +0 -3
- package/apps/app-training/src/core/context-types.d.ts.map +0 -1
- package/apps/app-training/src/core/context-types.js +0 -11
- package/apps/app-training/src/core/dataset-generator.d.ts +0 -135
- package/apps/app-training/src/core/dataset-generator.d.ts.map +0 -1
- package/apps/app-training/src/core/dataset-generator.js +0 -703
- package/apps/app-training/src/core/replay-validator.d.ts +0 -96
- package/apps/app-training/src/core/replay-validator.d.ts.map +0 -1
- package/apps/app-training/src/core/replay-validator.js +0 -265
- package/apps/app-training/src/core/roleplay-executor.d.ts +0 -123
- package/apps/app-training/src/core/roleplay-executor.d.ts.map +0 -1
- package/apps/app-training/src/core/roleplay-executor.js +0 -645
- package/apps/app-training/src/core/roleplay-trajectories.d.ts +0 -54
- package/apps/app-training/src/core/roleplay-trajectories.d.ts.map +0 -1
- package/apps/app-training/src/core/roleplay-trajectories.js +0 -73
- package/apps/app-training/src/core/scenario-blueprints.d.ts +0 -62
- package/apps/app-training/src/core/scenario-blueprints.d.ts.map +0 -1
- package/apps/app-training/src/core/scenario-blueprints.js +0 -790
- package/apps/app-training/src/core/trajectory-task-datasets.d.ts +0 -38
- package/apps/app-training/src/core/trajectory-task-datasets.d.ts.map +0 -1
- package/apps/app-training/src/core/trajectory-task-datasets.js +0 -281
- package/apps/app-training/src/core/vertex-tuning.d.ts +0 -139
- package/apps/app-training/src/core/vertex-tuning.d.ts.map +0 -1
- package/apps/app-training/src/core/vertex-tuning.js +0 -234
- package/packages/agent/src/inbox/channel-deep-links.d.ts.map +0 -1
- package/packages/agent/src/inbox/config.d.ts.map +0 -1
- package/packages/agent/src/inbox/message-fetcher.d.ts.map +0 -1
- package/packages/agent/src/inbox/reflection.d.ts.map +0 -1
- package/packages/agent/src/inbox/repository.d.ts.map +0 -1
- package/packages/agent/src/inbox/triage-classifier.d.ts.map +0 -1
- package/packages/agent/src/inbox/types.d.ts.map +0 -1
- package/packages/agent/src/training/cli.d.ts +0 -2
- package/packages/agent/src/training/cli.d.ts.map +0 -1
- package/packages/agent/src/training/cli.js +0 -2
- package/packages/agent/src/training/context-audit.d.ts +0 -2
- package/packages/agent/src/training/context-audit.d.ts.map +0 -1
- package/packages/agent/src/training/context-audit.js +0 -2
- package/packages/agent/src/training/context-catalog.d.ts +0 -2
- package/packages/agent/src/training/context-catalog.d.ts.map +0 -1
- package/packages/agent/src/training/context-catalog.js +0 -2
- package/packages/agent/src/training/context-types.d.ts +0 -2
- package/packages/agent/src/training/context-types.d.ts.map +0 -1
- package/packages/agent/src/training/context-types.js +0 -2
- package/packages/agent/src/training/dataset-generator.d.ts +0 -2
- package/packages/agent/src/training/dataset-generator.d.ts.map +0 -1
- package/packages/agent/src/training/dataset-generator.js +0 -2
- package/packages/agent/src/training/replay-validator.d.ts +0 -2
- package/packages/agent/src/training/replay-validator.d.ts.map +0 -1
- package/packages/agent/src/training/replay-validator.js +0 -2
- package/packages/agent/src/training/roleplay-executor.d.ts +0 -2
- package/packages/agent/src/training/roleplay-executor.d.ts.map +0 -1
- package/packages/agent/src/training/roleplay-executor.js +0 -2
- package/packages/agent/src/training/roleplay-trajectories.d.ts +0 -2
- package/packages/agent/src/training/roleplay-trajectories.d.ts.map +0 -1
- package/packages/agent/src/training/roleplay-trajectories.js +0 -2
- package/packages/agent/src/training/scenario-blueprints.d.ts +0 -2
- package/packages/agent/src/training/scenario-blueprints.d.ts.map +0 -1
- package/packages/agent/src/training/scenario-blueprints.js +0 -2
- package/packages/agent/src/training/trajectory-task-datasets.d.ts +0 -2
- package/packages/agent/src/training/trajectory-task-datasets.d.ts.map +0 -1
- package/packages/agent/src/training/trajectory-task-datasets.js +0 -2
- package/packages/agent/src/training/vertex-tuning.d.ts +0 -2
- package/packages/agent/src/training/vertex-tuning.d.ts.map +0 -1
- package/packages/agent/src/training/vertex-tuning.js +0 -2
- package/packages/typescript/src/features/orchestrator/actions/coding-task-handlers.d.ts +0 -41
- package/packages/typescript/src/features/orchestrator/actions/coding-task-handlers.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/coding-task-handlers.js +0 -443
- package/packages/typescript/src/features/orchestrator/actions/coding-task-helpers.d.ts +0 -34
- package/packages/typescript/src/features/orchestrator/actions/coding-task-helpers.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/coding-task-helpers.js +0 -171
- package/packages/typescript/src/features/orchestrator/actions/eval-metadata.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/eval-metadata.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/eval-metadata.js +0 -55
- package/packages/typescript/src/features/orchestrator/actions/finalize-workspace.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/finalize-workspace.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/finalize-workspace.js +0 -214
- package/packages/typescript/src/features/orchestrator/actions/list-agents.d.ts +0 -13
- package/packages/typescript/src/features/orchestrator/actions/list-agents.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/list-agents.js +0 -174
- package/packages/typescript/src/features/orchestrator/actions/manage-issues.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/manage-issues.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/manage-issues.js +0 -428
- package/packages/typescript/src/features/orchestrator/actions/provision-workspace.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/provision-workspace.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/provision-workspace.js +0 -189
- package/packages/typescript/src/features/orchestrator/actions/send-to-agent.d.ts +0 -12
- package/packages/typescript/src/features/orchestrator/actions/send-to-agent.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/send-to-agent.js +0 -265
- package/packages/typescript/src/features/orchestrator/actions/spawn-agent.d.ts +0 -12
- package/packages/typescript/src/features/orchestrator/actions/spawn-agent.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/spawn-agent.js +0 -356
- package/packages/typescript/src/features/orchestrator/actions/start-coding-task.d.ts +0 -22
- package/packages/typescript/src/features/orchestrator/actions/start-coding-task.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/start-coding-task.js +0 -270
- package/packages/typescript/src/features/orchestrator/actions/stop-agent.d.ts +0 -12
- package/packages/typescript/src/features/orchestrator/actions/stop-agent.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/stop-agent.js +0 -192
- package/packages/typescript/src/features/orchestrator/actions/task-control.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/actions/task-control.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-control.js +0 -217
- package/packages/typescript/src/features/orchestrator/actions/task-history.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/actions/task-history.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-history.js +0 -323
- package/packages/typescript/src/features/orchestrator/actions/task-share.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/actions/task-share.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-share.js +0 -168
- package/packages/typescript/src/features/orchestrator/actions/task-thread-target.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/task-thread-target.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-thread-target.js +0 -68
- package/packages/typescript/src/features/orchestrator/api/agent-routes.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/api/agent-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/agent-routes.js +0 -654
- package/packages/typescript/src/features/orchestrator/api/coordinator-routes.d.ts +0 -22
- package/packages/typescript/src/features/orchestrator/api/coordinator-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/coordinator-routes.js +0 -403
- package/packages/typescript/src/features/orchestrator/api/hook-routes.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/api/hook-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/hook-routes.js +0 -164
- package/packages/typescript/src/features/orchestrator/api/issue-routes.d.ts +0 -17
- package/packages/typescript/src/features/orchestrator/api/issue-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/issue-routes.js +0 -132
- package/packages/typescript/src/features/orchestrator/api/routes.d.ts +0 -37
- package/packages/typescript/src/features/orchestrator/api/routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/routes.js +0 -96
- package/packages/typescript/src/features/orchestrator/api/workspace-routes.d.ts +0 -17
- package/packages/typescript/src/features/orchestrator/api/workspace-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/workspace-routes.js +0 -149
- package/packages/typescript/src/features/orchestrator/base-plugin.d.ts +0 -19
- package/packages/typescript/src/features/orchestrator/base-plugin.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/base-plugin.js +0 -75
- package/packages/typescript/src/features/orchestrator/claude-jsonl-completion-watcher.d.ts +0 -101
- package/packages/typescript/src/features/orchestrator/claude-jsonl-completion-watcher.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/claude-jsonl-completion-watcher.js +0 -310
- package/packages/typescript/src/features/orchestrator/index.d.ts +0 -33
- package/packages/typescript/src/features/orchestrator/index.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/index.js +0 -30
- package/packages/typescript/src/features/orchestrator/patch-agent-orchestrator-plugin.d.ts +0 -15
- package/packages/typescript/src/features/orchestrator/patch-agent-orchestrator-plugin.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/patch-agent-orchestrator-plugin.js +0 -1449
- package/packages/typescript/src/features/orchestrator/providers/action-examples.d.ts +0 -14
- package/packages/typescript/src/features/orchestrator/providers/action-examples.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/providers/action-examples.js +0 -151
- package/packages/typescript/src/features/orchestrator/providers/active-workspace-context.d.ts +0 -13
- package/packages/typescript/src/features/orchestrator/providers/active-workspace-context.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/providers/active-workspace-context.js +0 -142
- package/packages/typescript/src/features/orchestrator/services/agent-credentials.d.ts +0 -6
- package/packages/typescript/src/features/orchestrator/services/agent-credentials.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/agent-credentials.js +0 -91
- package/packages/typescript/src/features/orchestrator/services/agent-metrics.d.ts +0 -30
- package/packages/typescript/src/features/orchestrator/services/agent-metrics.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/agent-metrics.js +0 -54
- package/packages/typescript/src/features/orchestrator/services/agent-selection.d.ts +0 -53
- package/packages/typescript/src/features/orchestrator/services/agent-selection.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/agent-selection.js +0 -70
- package/packages/typescript/src/features/orchestrator/services/ansi-utils.d.ts +0 -61
- package/packages/typescript/src/features/orchestrator/services/ansi-utils.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/ansi-utils.js +0 -252
- package/packages/typescript/src/features/orchestrator/services/config-env.d.ts +0 -13
- package/packages/typescript/src/features/orchestrator/services/config-env.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/config-env.js +0 -37
- package/packages/typescript/src/features/orchestrator/services/coordinator-event-normalizer.d.ts +0 -50
- package/packages/typescript/src/features/orchestrator/services/coordinator-event-normalizer.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/coordinator-event-normalizer.js +0 -184
- package/packages/typescript/src/features/orchestrator/services/debug-capture.d.ts +0 -38
- package/packages/typescript/src/features/orchestrator/services/debug-capture.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/debug-capture.js +0 -113
- package/packages/typescript/src/features/orchestrator/services/pty-auto-response.d.ts +0 -30
- package/packages/typescript/src/features/orchestrator/services/pty-auto-response.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-auto-response.js +0 -146
- package/packages/typescript/src/features/orchestrator/services/pty-init.d.ts +0 -54
- package/packages/typescript/src/features/orchestrator/services/pty-init.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-init.js +0 -315
- package/packages/typescript/src/features/orchestrator/services/pty-service.d.ts +0 -175
- package/packages/typescript/src/features/orchestrator/services/pty-service.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-service.js +0 -1469
- package/packages/typescript/src/features/orchestrator/services/pty-session-io.d.ts +0 -49
- package/packages/typescript/src/features/orchestrator/services/pty-session-io.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-session-io.js +0 -180
- package/packages/typescript/src/features/orchestrator/services/pty-spawn.d.ts +0 -53
- package/packages/typescript/src/features/orchestrator/services/pty-spawn.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-spawn.js +0 -280
- package/packages/typescript/src/features/orchestrator/services/pty-types.d.ts +0 -80
- package/packages/typescript/src/features/orchestrator/services/pty-types.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-types.js +0 -51
- package/packages/typescript/src/features/orchestrator/services/repo-input.d.ts +0 -16
- package/packages/typescript/src/features/orchestrator/services/repo-input.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/repo-input.js +0 -88
- package/packages/typescript/src/features/orchestrator/services/stall-classifier.d.ts +0 -69
- package/packages/typescript/src/features/orchestrator/services/stall-classifier.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/stall-classifier.js +0 -446
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator-prompts.d.ts +0 -97
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator-prompts.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator-prompts.js +0 -342
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator.d.ts +0 -421
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator.js +0 -2356
- package/packages/typescript/src/features/orchestrator/services/swarm-decision-loop.d.ts +0 -52
- package/packages/typescript/src/features/orchestrator/services/swarm-decision-loop.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-decision-loop.js +0 -1538
- package/packages/typescript/src/features/orchestrator/services/swarm-event-triage.d.ts +0 -49
- package/packages/typescript/src/features/orchestrator/services/swarm-event-triage.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-event-triage.js +0 -171
- package/packages/typescript/src/features/orchestrator/services/swarm-history.d.ts +0 -27
- package/packages/typescript/src/features/orchestrator/services/swarm-history.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-history.js +0 -148
- package/packages/typescript/src/features/orchestrator/services/swarm-idle-watchdog.d.ts +0 -22
- package/packages/typescript/src/features/orchestrator/services/swarm-idle-watchdog.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-idle-watchdog.js +0 -265
- package/packages/typescript/src/features/orchestrator/services/task-acceptance.d.ts +0 -8
- package/packages/typescript/src/features/orchestrator/services/task-acceptance.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-acceptance.js +0 -114
- package/packages/typescript/src/features/orchestrator/services/task-agent-auth.d.ts +0 -68
- package/packages/typescript/src/features/orchestrator/services/task-agent-auth.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-agent-auth.js +0 -559
- package/packages/typescript/src/features/orchestrator/services/task-agent-frameworks.d.ts +0 -82
- package/packages/typescript/src/features/orchestrator/services/task-agent-frameworks.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-agent-frameworks.js +0 -738
- package/packages/typescript/src/features/orchestrator/services/task-kind.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/services/task-kind.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-kind.js +0 -40
- package/packages/typescript/src/features/orchestrator/services/task-policy.d.ts +0 -17
- package/packages/typescript/src/features/orchestrator/services/task-policy.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-policy.js +0 -226
- package/packages/typescript/src/features/orchestrator/services/task-registry.d.ts +0 -550
- package/packages/typescript/src/features/orchestrator/services/task-registry.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-registry.js +0 -2182
- package/packages/typescript/src/features/orchestrator/services/task-share.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/services/task-share.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-share.js +0 -159
- package/packages/typescript/src/features/orchestrator/services/task-validation.d.ts +0 -69
- package/packages/typescript/src/features/orchestrator/services/task-validation.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-validation.js +0 -587
- package/packages/typescript/src/features/orchestrator/services/task-verifier-runner.d.ts +0 -5
- package/packages/typescript/src/features/orchestrator/services/task-verifier-runner.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-verifier-runner.js +0 -372
- package/packages/typescript/src/features/orchestrator/services/trajectory-context.d.ts +0 -73
- package/packages/typescript/src/features/orchestrator/services/trajectory-context.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/trajectory-context.js +0 -64
- package/packages/typescript/src/features/orchestrator/services/trajectory-feedback.d.ts +0 -53
- package/packages/typescript/src/features/orchestrator/services/trajectory-feedback.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/trajectory-feedback.js +0 -260
- package/packages/typescript/src/features/orchestrator/services/workspace-git-ops.d.ts +0 -28
- package/packages/typescript/src/features/orchestrator/services/workspace-git-ops.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-git-ops.js +0 -105
- package/packages/typescript/src/features/orchestrator/services/workspace-github.d.ts +0 -58
- package/packages/typescript/src/features/orchestrator/services/workspace-github.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-github.js +0 -139
- package/packages/typescript/src/features/orchestrator/services/workspace-lifecycle.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/services/workspace-lifecycle.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-lifecycle.js +0 -86
- package/packages/typescript/src/features/orchestrator/services/workspace-service.d.ts +0 -118
- package/packages/typescript/src/features/orchestrator/services/workspace-service.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-service.js +0 -533
- package/packages/typescript/src/features/orchestrator/services/workspace-types.d.ts +0 -81
- package/packages/typescript/src/features/orchestrator/services/workspace-types.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-types.js +0 -8
- package/packages/typescript/src/features/orchestrator/task-progress-streamer.d.ts +0 -38
- package/packages/typescript/src/features/orchestrator/task-progress-streamer.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/task-progress-streamer.js +0 -293
- /package/{packages/agent → apps/app-lifeops}/src/inbox/channel-deep-links.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/channel-deep-links.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/config.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/message-fetcher.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/message-fetcher.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/reflection.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/reflection.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/repository.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/repository.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/triage-classifier.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/triage-classifier.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/types.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/types.js +0 -0
|
@@ -1,2356 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Swarm Coordinator — Event Bridge & Autonomous Coordination Loop
|
|
3
|
-
*
|
|
4
|
-
* Bridges PTY session events to:
|
|
5
|
-
* 1. SSE clients (frontend dashboard) for real-time status
|
|
6
|
-
* 2. LLM coordination decisions for unhandled blocking prompts
|
|
7
|
-
*
|
|
8
|
-
* The coordinator subscribes to PTYService session events and:
|
|
9
|
-
* - Skips events already handled by auto-response rules (autoResponded=true)
|
|
10
|
-
* - Routes unhandled blocking prompts through supervision levels:
|
|
11
|
-
* - autonomous: LLM decides immediately
|
|
12
|
-
* - confirm: queued for human approval
|
|
13
|
-
* - notify: broadcast only (no action)
|
|
14
|
-
*
|
|
15
|
-
* Heavy logic is extracted into:
|
|
16
|
-
* - swarm-decision-loop.ts (blocked, turn-complete, LLM decisions)
|
|
17
|
-
* - swarm-idle-watchdog.ts (idle session scanning)
|
|
18
|
-
*
|
|
19
|
-
* @module services/swarm-coordinator
|
|
20
|
-
*/
|
|
21
|
-
import { logger } from "@elizaos/core";
|
|
22
|
-
import { buildAgentCredentials } from "./agent-credentials.js";
|
|
23
|
-
import { cleanForFailoverContext, extractDevServerUrl } from "./ansi-utils.js";
|
|
24
|
-
import { normalizeCoordinatorEvent, } from "./coordinator-event-normalizer.js";
|
|
25
|
-
import { normalizeAgentType } from "./pty-types.js";
|
|
26
|
-
import { checkAllTasksComplete, clearDeferredTurnCompleteTimers, executeDecision as execDecision, handleBlocked, handleTurnComplete, } from "./swarm-decision-loop.js";
|
|
27
|
-
import { SwarmHistory } from "./swarm-history.js";
|
|
28
|
-
import { scanIdleSessions } from "./swarm-idle-watchdog.js";
|
|
29
|
-
import { deriveTaskAcceptanceCriteria } from "./task-acceptance.js";
|
|
30
|
-
import { isUsageExhaustedTaskAgentError, markTaskAgentFrameworkHealthy, markTaskAgentFrameworkUnavailable, } from "./task-agent-frameworks.js";
|
|
31
|
-
import { inferTaskThreadKind } from "./task-kind.js";
|
|
32
|
-
import { TaskRegistry, } from "./task-registry.js";
|
|
33
|
-
// ─── Constants ───
|
|
34
|
-
/** Time to buffer events for unregistered sessions (ms). */
|
|
35
|
-
/** Exponential backoff delays for unregistered session buffer retries. */
|
|
36
|
-
const UNREGISTERED_RETRY_DELAYS = [2000, 4000, 8000, 16000];
|
|
37
|
-
/** Absolute maximum wait time before discarding unregistered events. */
|
|
38
|
-
const UNREGISTERED_MAX_TOTAL_MS = 30_000;
|
|
39
|
-
/** Coalesce rapid turn-complete events within this window (ms). */
|
|
40
|
-
const TURN_COMPLETE_COALESCE_MS = 500;
|
|
41
|
-
/** How often the idle watchdog scans for idle sessions (ms). */
|
|
42
|
-
const IDLE_SCAN_INTERVAL_MS = 60 * 1000; // 1 minute
|
|
43
|
-
/** How long to wait before auto-resuming a paused coordinator (ms). */
|
|
44
|
-
const PAUSE_TIMEOUT_MS = 30_000;
|
|
45
|
-
/** Max events to buffer before WS bridge is wired. */
|
|
46
|
-
const MAX_PRE_BRIDGE_BUFFER = 100;
|
|
47
|
-
/** Grace window where a late task_complete can recover a recently-stopped task. */
|
|
48
|
-
const STOPPED_RECOVERY_WINDOW_MS = 90_000;
|
|
49
|
-
const FAILOVER_OUTPUT_MAX_CHARS = 4_000;
|
|
50
|
-
const MAX_AUTOMATIC_ERROR_RECOVERIES = 2;
|
|
51
|
-
const ALTERNATE_FRAMEWORK_ERROR_RE = /\b(auth|login|credential|401|403|unauthorized|forbidden|token|api key|not found|enoent|missing executable|command not found)\b/i;
|
|
52
|
-
function inferProviderSource(framework) {
|
|
53
|
-
if (framework.subscriptionReady) {
|
|
54
|
-
return "subscription";
|
|
55
|
-
}
|
|
56
|
-
if (framework.id === "pi") {
|
|
57
|
-
return framework.installed ? "local-cli" : null;
|
|
58
|
-
}
|
|
59
|
-
return framework.authReady ? "credentials" : null;
|
|
60
|
-
}
|
|
61
|
-
// ─── Service ───
|
|
62
|
-
export class SwarmCoordinator {
|
|
63
|
-
static serviceType = "SWARM_COORDINATOR";
|
|
64
|
-
runtime;
|
|
65
|
-
taskRegistry;
|
|
66
|
-
ptyService = null;
|
|
67
|
-
unsubscribeEvents = null;
|
|
68
|
-
/** Per-session task context. */
|
|
69
|
-
tasks = new Map();
|
|
70
|
-
/** SSE clients receiving live events. */
|
|
71
|
-
sseClients = new Set();
|
|
72
|
-
/** Supervision level (default: autonomous). */
|
|
73
|
-
supervisionLevel = "autonomous";
|
|
74
|
-
/** Pending confirmations for "confirm" mode. */
|
|
75
|
-
pendingDecisions = new Map();
|
|
76
|
-
/** In-flight decision lock — prevents parallel LLM calls for same session. */
|
|
77
|
-
inFlightDecisions = new Set();
|
|
78
|
-
/** Buffered task_complete events that arrived while an in-flight decision was running. */
|
|
79
|
-
pendingTurnComplete = new Map();
|
|
80
|
-
/** Fingerprint of the last blocked prompt per session — for re-render dedup. */
|
|
81
|
-
lastBlockedPromptFingerprint = new Map();
|
|
82
|
-
/** Buffered blocked events that arrived while an in-flight decision was running. */
|
|
83
|
-
pendingBlocked = new Map();
|
|
84
|
-
/** Callback to send chat messages to the user's conversation UI. */
|
|
85
|
-
chatCallback = null;
|
|
86
|
-
/** Callback to relay coordinator events to WebSocket clients. */
|
|
87
|
-
wsBroadcast = null;
|
|
88
|
-
/** Callback to route coordinator events through Milaidy's full pipeline. */
|
|
89
|
-
agentDecisionCb = null;
|
|
90
|
-
/** Callback fired when all swarm tasks complete — for synthesis. */
|
|
91
|
-
swarmCompleteCb = null;
|
|
92
|
-
/** Buffer for events arriving before task registration. */
|
|
93
|
-
unregisteredBuffer = new Map();
|
|
94
|
-
/** Idle watchdog timer handle. */
|
|
95
|
-
idleWatchdogTimer = null;
|
|
96
|
-
/** Last-seen output snapshot per session — used by idle watchdog to detect data flow. */
|
|
97
|
-
lastSeenOutput = new Map();
|
|
98
|
-
/** Timestamp of last tool_running chat notification per session — for throttling. */
|
|
99
|
-
lastToolNotification = new Map();
|
|
100
|
-
/** Whether LLM decisions are paused (user sent a chat message). */
|
|
101
|
-
_paused = false;
|
|
102
|
-
/** Significant decisions shared across the swarm (Layer 2). */
|
|
103
|
-
sharedDecisions = [];
|
|
104
|
-
/** Shared context brief generated during swarm planning phase. */
|
|
105
|
-
_swarmContext = "";
|
|
106
|
-
/** @see SwarmCoordinatorContext.swarmCompleteNotified */
|
|
107
|
-
swarmCompleteNotified = false;
|
|
108
|
-
/** Buffered events during pause — replayed on resume. */
|
|
109
|
-
pauseBuffer = [];
|
|
110
|
-
/** Buffered broadcasts waiting for wsBroadcast to be wired. */
|
|
111
|
-
preBridgeBroadcastBuffer = [];
|
|
112
|
-
/** Auto-resume timeout handle. */
|
|
113
|
-
pauseTimeout = null;
|
|
114
|
-
/** Coordinator startup timestamp — ignore events from sessions created before this. */
|
|
115
|
-
startedAt = Date.now();
|
|
116
|
-
/** Active retry timers for unregistered session buffers. */
|
|
117
|
-
unregisteredRetryTimers = new Map();
|
|
118
|
-
/** Turn-complete coalescing timers — debounces rapid events per session. */
|
|
119
|
-
turnCompleteCoalesceTimers = new Map();
|
|
120
|
-
/** Persistent swarm history — JSONL log that survives restarts. */
|
|
121
|
-
history = new SwarmHistory();
|
|
122
|
-
constructor(runtime) {
|
|
123
|
-
this.runtime = runtime;
|
|
124
|
-
this.taskRegistry = new TaskRegistry(runtime);
|
|
125
|
-
}
|
|
126
|
-
// ─── Chat Callback ───
|
|
127
|
-
/** Inject a callback (from server.ts) to route messages to the user's chat UI. */
|
|
128
|
-
/** Track whether we've already wired the scratch decision callback. */
|
|
129
|
-
scratchDecisionWired = false;
|
|
130
|
-
setChatCallback(cb) {
|
|
131
|
-
this.chatCallback = cb;
|
|
132
|
-
this.log("Chat callback wired");
|
|
133
|
-
// Try wiring scratch decision callback now, retry lazily if service not ready
|
|
134
|
-
this.wireScratchDecisionCallback();
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Wire the scratch workspace save prompt callback.
|
|
138
|
-
* Called eagerly from setChatCallback and lazily from handleSessionEvent
|
|
139
|
-
* in case the workspace service wasn't ready at chat-callback time.
|
|
140
|
-
*/
|
|
141
|
-
wireScratchDecisionCallback() {
|
|
142
|
-
if (this.scratchDecisionWired || !this.chatCallback)
|
|
143
|
-
return;
|
|
144
|
-
const wsService = this.runtime.getService("CODING_WORKSPACE_SERVICE");
|
|
145
|
-
if (wsService?.setScratchDecisionCallback) {
|
|
146
|
-
const chatCb = this.chatCallback;
|
|
147
|
-
wsService.setScratchDecisionCallback(async (record) => {
|
|
148
|
-
const ttlNote = record.expiresAt
|
|
149
|
-
? (() => {
|
|
150
|
-
const remainMs = record.expiresAt - Date.now();
|
|
151
|
-
const hours = Math.round(remainMs / (60 * 60 * 1000));
|
|
152
|
-
return hours >= 1
|
|
153
|
-
? `It will be automatically cleaned up in ~${hours} hour${hours === 1 ? "" : "s"}.`
|
|
154
|
-
: `It will be automatically cleaned up shortly.`;
|
|
155
|
-
})()
|
|
156
|
-
: "It will be automatically cleaned up after the configured retention period.";
|
|
157
|
-
await chatCb(`Task "${record.label}" finished. Code is at \`${record.path}\`.\n` +
|
|
158
|
-
`${ttlNote} To keep it, say "keep the workspace" or manage it in Settings -> Task Agents.`, "task-agent");
|
|
159
|
-
});
|
|
160
|
-
this.scratchDecisionWired = true;
|
|
161
|
-
this.log("Scratch decision callback wired");
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/** Inject a callback (from server.ts) to relay events to WebSocket clients. */
|
|
165
|
-
setWsBroadcast(cb) {
|
|
166
|
-
this.wsBroadcast = cb;
|
|
167
|
-
// Replay any events that were broadcast before the bridge was wired
|
|
168
|
-
if (this.preBridgeBroadcastBuffer.length > 0) {
|
|
169
|
-
this.log(`WS broadcast callback wired — replaying ${this.preBridgeBroadcastBuffer.length} buffered event(s)`);
|
|
170
|
-
for (const event of this.preBridgeBroadcastBuffer) {
|
|
171
|
-
cb(event);
|
|
172
|
-
}
|
|
173
|
-
this.preBridgeBroadcastBuffer.length = 0;
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
this.log("WS broadcast callback wired");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
/** Inject a callback fired when all swarm tasks reach terminal state. */
|
|
180
|
-
setSwarmCompleteCallback(cb) {
|
|
181
|
-
this.swarmCompleteCb = cb;
|
|
182
|
-
this.log("Swarm complete callback wired");
|
|
183
|
-
}
|
|
184
|
-
/** Return the swarm complete callback (if wired). */
|
|
185
|
-
getSwarmCompleteCallback() {
|
|
186
|
-
return this.swarmCompleteCb;
|
|
187
|
-
}
|
|
188
|
-
/** Set the shared context brief for this swarm. */
|
|
189
|
-
setSwarmContext(context) {
|
|
190
|
-
this._swarmContext = context;
|
|
191
|
-
this.log(`Swarm context set (${context.length} chars)`);
|
|
192
|
-
}
|
|
193
|
-
/** Return the swarm planning context (if set). */
|
|
194
|
-
getSwarmContext() {
|
|
195
|
-
return this._swarmContext;
|
|
196
|
-
}
|
|
197
|
-
/** Inject a callback (from server.ts) to route events through Milaidy's pipeline. */
|
|
198
|
-
setAgentDecisionCallback(cb) {
|
|
199
|
-
this.agentDecisionCb = cb;
|
|
200
|
-
this.log("Agent decision callback wired — events will route through Milaidy");
|
|
201
|
-
}
|
|
202
|
-
/** Return the agent decision callback (if wired). */
|
|
203
|
-
getAgentDecisionCallback() {
|
|
204
|
-
return this.agentDecisionCb;
|
|
205
|
-
}
|
|
206
|
-
/** Null-safe wrapper — sends a message to the user's conversation if callback is set. */
|
|
207
|
-
sendChatMessage(text, source) {
|
|
208
|
-
if (!this.chatCallback)
|
|
209
|
-
return;
|
|
210
|
-
this.chatCallback(text, source).catch((err) => {
|
|
211
|
-
this.log(`Failed to send chat message: ${err}`);
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
// ─── Lifecycle ───
|
|
215
|
-
/**
|
|
216
|
-
* Initialize the coordinator by subscribing to PTY session events.
|
|
217
|
-
* Called from plugin init after services are ready.
|
|
218
|
-
*/
|
|
219
|
-
async start(ptyService) {
|
|
220
|
-
await this.taskRegistry.ensureSchema();
|
|
221
|
-
await this.taskRegistry.recoverInterruptedTasks();
|
|
222
|
-
await this.rehydratePendingDecisions();
|
|
223
|
-
this.ptyService = ptyService;
|
|
224
|
-
this.unsubscribeEvents = ptyService.onNormalizedSessionEvent((normalized) => {
|
|
225
|
-
this.handleNormalizedSessionEvent(normalized).catch((err) => {
|
|
226
|
-
this.log(`Error handling event: ${err}`);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
// Start idle watchdog
|
|
230
|
-
this.idleWatchdogTimer = setInterval(() => {
|
|
231
|
-
scanIdleSessions(this).catch((err) => {
|
|
232
|
-
this.log(`Idle watchdog error: ${err}`);
|
|
233
|
-
});
|
|
234
|
-
}, IDLE_SCAN_INTERVAL_MS);
|
|
235
|
-
this.log("SwarmCoordinator started");
|
|
236
|
-
}
|
|
237
|
-
restorePendingTaskContext(record) {
|
|
238
|
-
const raw = record.taskContext;
|
|
239
|
-
const status = (() => {
|
|
240
|
-
switch (typeof raw.status === "string" ? raw.status : "") {
|
|
241
|
-
case "active":
|
|
242
|
-
case "blocked":
|
|
243
|
-
case "tool_running":
|
|
244
|
-
case "completed":
|
|
245
|
-
case "error":
|
|
246
|
-
case "stopped":
|
|
247
|
-
return raw.status;
|
|
248
|
-
default:
|
|
249
|
-
return "blocked";
|
|
250
|
-
}
|
|
251
|
-
})();
|
|
252
|
-
return {
|
|
253
|
-
threadId: typeof raw.threadId === "string" && raw.threadId.trim().length > 0
|
|
254
|
-
? raw.threadId
|
|
255
|
-
: record.threadId,
|
|
256
|
-
...(typeof raw.taskNodeId === "string" && raw.taskNodeId.trim().length > 0
|
|
257
|
-
? { taskNodeId: raw.taskNodeId }
|
|
258
|
-
: {}),
|
|
259
|
-
sessionId: record.sessionId,
|
|
260
|
-
agentType: typeof raw.agentType === "string" && raw.agentType.trim().length > 0
|
|
261
|
-
? raw.agentType
|
|
262
|
-
: "claude",
|
|
263
|
-
label: typeof raw.label === "string" && raw.label.trim().length > 0
|
|
264
|
-
? raw.label
|
|
265
|
-
: `agent-${record.sessionId.slice(-8)}`,
|
|
266
|
-
originalTask: typeof raw.originalTask === "string"
|
|
267
|
-
? raw.originalTask
|
|
268
|
-
: record.promptText,
|
|
269
|
-
workdir: typeof raw.workdir === "string" ? raw.workdir : "",
|
|
270
|
-
...(typeof raw.repo === "string" && raw.repo.trim().length > 0
|
|
271
|
-
? { repo: raw.repo }
|
|
272
|
-
: {}),
|
|
273
|
-
status,
|
|
274
|
-
decisions: Array.isArray(raw.decisions)
|
|
275
|
-
? raw.decisions.filter((entry) => Boolean(entry && typeof entry === "object"))
|
|
276
|
-
: [],
|
|
277
|
-
autoResolvedCount: typeof raw.autoResolvedCount === "number" ? raw.autoResolvedCount : 0,
|
|
278
|
-
registeredAt: typeof raw.registeredAt === "number"
|
|
279
|
-
? raw.registeredAt
|
|
280
|
-
: record.createdAt,
|
|
281
|
-
lastActivityAt: typeof raw.lastActivityAt === "number"
|
|
282
|
-
? raw.lastActivityAt
|
|
283
|
-
: record.createdAt,
|
|
284
|
-
idleCheckCount: typeof raw.idleCheckCount === "number" ? raw.idleCheckCount : 0,
|
|
285
|
-
taskDelivered: raw.taskDelivered === true,
|
|
286
|
-
...(typeof raw.completionSummary === "string"
|
|
287
|
-
? { completionSummary: raw.completionSummary }
|
|
288
|
-
: {}),
|
|
289
|
-
lastSeenDecisionIndex: typeof raw.lastSeenDecisionIndex === "number"
|
|
290
|
-
? raw.lastSeenDecisionIndex
|
|
291
|
-
: 0,
|
|
292
|
-
...(typeof raw.lastInputSentAt === "number"
|
|
293
|
-
? { lastInputSentAt: raw.lastInputSentAt }
|
|
294
|
-
: {}),
|
|
295
|
-
...(typeof raw.stoppedAt === "number"
|
|
296
|
-
? { stoppedAt: raw.stoppedAt }
|
|
297
|
-
: {}),
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
restorePendingLlmDecision(record) {
|
|
301
|
-
const raw = record.llmDecision;
|
|
302
|
-
const action = typeof raw.action === "string" &&
|
|
303
|
-
["respond", "escalate", "ignore", "complete"].includes(raw.action)
|
|
304
|
-
? raw.action
|
|
305
|
-
: "escalate";
|
|
306
|
-
return {
|
|
307
|
-
action,
|
|
308
|
-
...(typeof raw.response === "string" ? { response: raw.response } : {}),
|
|
309
|
-
...(raw.useKeys === true ? { useKeys: true } : {}),
|
|
310
|
-
...(Array.isArray(raw.keys)
|
|
311
|
-
? {
|
|
312
|
-
keys: raw.keys.filter((entry) => typeof entry === "string"),
|
|
313
|
-
}
|
|
314
|
-
: {}),
|
|
315
|
-
reasoning: typeof raw.reasoning === "string" && raw.reasoning.trim().length > 0
|
|
316
|
-
? raw.reasoning
|
|
317
|
-
: "Recovered pending confirmation from persisted coordinator state.",
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
async rehydratePendingDecisions() {
|
|
321
|
-
const records = await this.taskRegistry.listPendingDecisions();
|
|
322
|
-
for (const record of records) {
|
|
323
|
-
const taskContext = this.restorePendingTaskContext(record);
|
|
324
|
-
this.tasks.set(record.sessionId, taskContext);
|
|
325
|
-
this.pendingDecisions.set(record.sessionId, {
|
|
326
|
-
sessionId: record.sessionId,
|
|
327
|
-
promptText: record.promptText,
|
|
328
|
-
recentOutput: record.recentOutput,
|
|
329
|
-
llmDecision: this.restorePendingLlmDecision(record),
|
|
330
|
-
taskContext,
|
|
331
|
-
createdAt: record.createdAt,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
async stop() {
|
|
336
|
-
const persistOnShutdown = Array.from(this.tasks.values())
|
|
337
|
-
.filter((task) => task.status === "active" ||
|
|
338
|
-
task.status === "blocked" ||
|
|
339
|
-
task.status === "tool_running")
|
|
340
|
-
.map(async (task) => {
|
|
341
|
-
task.status = "stopped";
|
|
342
|
-
task.stoppedAt = Date.now();
|
|
343
|
-
await this.taskRegistry.updateSession(task.sessionId, {
|
|
344
|
-
status: "interrupted",
|
|
345
|
-
lastActivityAt: task.lastActivityAt,
|
|
346
|
-
idleCheckCount: task.idleCheckCount,
|
|
347
|
-
taskDelivered: task.taskDelivered,
|
|
348
|
-
autoResolvedCount: task.autoResolvedCount,
|
|
349
|
-
decisionCount: task.decisions.length,
|
|
350
|
-
completionSummary: task.completionSummary ?? null,
|
|
351
|
-
lastSeenDecisionIndex: task.lastSeenDecisionIndex,
|
|
352
|
-
lastInputSentAt: task.lastInputSentAt,
|
|
353
|
-
stoppedAt: task.stoppedAt,
|
|
354
|
-
});
|
|
355
|
-
await this.taskRegistry.appendEvent({
|
|
356
|
-
threadId: task.threadId,
|
|
357
|
-
sessionId: task.sessionId,
|
|
358
|
-
eventType: "session_interrupted",
|
|
359
|
-
summary: "Session interrupted during coordinator shutdown",
|
|
360
|
-
data: { reason: "coordinator_shutdown" },
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
await Promise.allSettled(persistOnShutdown);
|
|
364
|
-
if (this.idleWatchdogTimer) {
|
|
365
|
-
clearInterval(this.idleWatchdogTimer);
|
|
366
|
-
this.idleWatchdogTimer = null;
|
|
367
|
-
}
|
|
368
|
-
if (this.unsubscribeEvents) {
|
|
369
|
-
this.unsubscribeEvents();
|
|
370
|
-
this.unsubscribeEvents = null;
|
|
371
|
-
}
|
|
372
|
-
// Close all SSE connections
|
|
373
|
-
for (const client of this.sseClients) {
|
|
374
|
-
if (!client.writableEnded) {
|
|
375
|
-
client.end();
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
this.sseClients.clear();
|
|
379
|
-
this.tasks.clear();
|
|
380
|
-
this.pendingDecisions.clear();
|
|
381
|
-
this.inFlightDecisions.clear();
|
|
382
|
-
this.pendingTurnComplete.clear();
|
|
383
|
-
clearDeferredTurnCompleteTimers();
|
|
384
|
-
this.lastBlockedPromptFingerprint.clear();
|
|
385
|
-
this.pendingBlocked.clear();
|
|
386
|
-
this.unregisteredBuffer.clear();
|
|
387
|
-
for (const timer of this.unregisteredRetryTimers.values()) {
|
|
388
|
-
clearTimeout(timer);
|
|
389
|
-
}
|
|
390
|
-
this.unregisteredRetryTimers.clear();
|
|
391
|
-
for (const timer of this.turnCompleteCoalesceTimers.values()) {
|
|
392
|
-
clearTimeout(timer);
|
|
393
|
-
}
|
|
394
|
-
this.turnCompleteCoalesceTimers.clear();
|
|
395
|
-
this.lastSeenOutput.clear();
|
|
396
|
-
this.lastToolNotification.clear();
|
|
397
|
-
this.agentDecisionCb = null;
|
|
398
|
-
this.sharedDecisions.length = 0;
|
|
399
|
-
this._swarmContext = "";
|
|
400
|
-
this.swarmCompleteNotified = false;
|
|
401
|
-
// Clear pause state
|
|
402
|
-
this._paused = false;
|
|
403
|
-
if (this.pauseTimeout) {
|
|
404
|
-
clearTimeout(this.pauseTimeout);
|
|
405
|
-
this.pauseTimeout = null;
|
|
406
|
-
}
|
|
407
|
-
this.pauseBuffer = [];
|
|
408
|
-
this.preBridgeBroadcastBuffer.length = 0;
|
|
409
|
-
this.log("SwarmCoordinator stopped");
|
|
410
|
-
}
|
|
411
|
-
// ─── Pause / Resume ───
|
|
412
|
-
/** Whether the coordinator is currently paused. */
|
|
413
|
-
get isPaused() {
|
|
414
|
-
return this._paused;
|
|
415
|
-
}
|
|
416
|
-
/** Pause LLM-based decisions. Auto-responses and broadcasts continue. */
|
|
417
|
-
pause() {
|
|
418
|
-
if (this._paused)
|
|
419
|
-
return;
|
|
420
|
-
this._paused = true;
|
|
421
|
-
this.log("Coordinator paused — buffering LLM decisions until user message is processed");
|
|
422
|
-
this.broadcast({
|
|
423
|
-
type: "coordinator_paused",
|
|
424
|
-
sessionId: "",
|
|
425
|
-
timestamp: Date.now(),
|
|
426
|
-
data: {},
|
|
427
|
-
});
|
|
428
|
-
// Safety: auto-resume after timeout
|
|
429
|
-
this.pauseTimeout = setTimeout(() => {
|
|
430
|
-
if (this._paused) {
|
|
431
|
-
this.log("Coordinator auto-resuming after timeout");
|
|
432
|
-
this.resume();
|
|
433
|
-
}
|
|
434
|
-
}, PAUSE_TIMEOUT_MS);
|
|
435
|
-
}
|
|
436
|
-
/** Resume LLM-based decisions and replay buffered events. */
|
|
437
|
-
resume() {
|
|
438
|
-
if (!this._paused)
|
|
439
|
-
return;
|
|
440
|
-
this._paused = false;
|
|
441
|
-
if (this.pauseTimeout) {
|
|
442
|
-
clearTimeout(this.pauseTimeout);
|
|
443
|
-
this.pauseTimeout = null;
|
|
444
|
-
}
|
|
445
|
-
this.log(`Coordinator resumed — replaying ${this.pauseBuffer.length} buffered events`);
|
|
446
|
-
this.broadcast({
|
|
447
|
-
type: "coordinator_resumed",
|
|
448
|
-
sessionId: "",
|
|
449
|
-
timestamp: Date.now(),
|
|
450
|
-
data: {},
|
|
451
|
-
});
|
|
452
|
-
// Replay buffered events
|
|
453
|
-
const buffered = [...this.pauseBuffer];
|
|
454
|
-
this.pauseBuffer = [];
|
|
455
|
-
for (const entry of buffered) {
|
|
456
|
-
this.handleNormalizedSessionEvent(entry).catch((err) => {
|
|
457
|
-
this.log(`Error replaying buffered event: ${err}`);
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
// ─── Task Registration ───
|
|
462
|
-
async registerTask(sessionId, context) {
|
|
463
|
-
const threadId = context.threadId?.trim() || sessionId;
|
|
464
|
-
// Reset swarm state when the first task of a new swarm is registered.
|
|
465
|
-
// Check for terminal-only tasks (all previous tasks in completed/stopped/error)
|
|
466
|
-
// rather than empty map, so reuse without stop() works.
|
|
467
|
-
const allPreviousTerminal = this.tasks.size === 0 ||
|
|
468
|
-
Array.from(this.tasks.values()).every((t) => t.status === "completed" ||
|
|
469
|
-
t.status === "stopped" ||
|
|
470
|
-
t.status === "error");
|
|
471
|
-
if (allPreviousTerminal) {
|
|
472
|
-
this.swarmCompleteNotified = false;
|
|
473
|
-
// Clear stale tasks and shared context from previous swarm
|
|
474
|
-
if (this.tasks.size > 0) {
|
|
475
|
-
this.tasks.clear();
|
|
476
|
-
this.sharedDecisions.length = 0;
|
|
477
|
-
this._swarmContext = "";
|
|
478
|
-
this.log("Cleared stale swarm state for new swarm");
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
const taskNodeId = context.taskNodeId?.trim() || `node-${sessionId}`;
|
|
482
|
-
this.tasks.set(sessionId, {
|
|
483
|
-
threadId,
|
|
484
|
-
taskNodeId,
|
|
485
|
-
sessionId,
|
|
486
|
-
agentType: context.agentType,
|
|
487
|
-
label: context.label,
|
|
488
|
-
originalTask: context.originalTask,
|
|
489
|
-
workdir: context.workdir,
|
|
490
|
-
repo: context.repo,
|
|
491
|
-
status: "active",
|
|
492
|
-
decisions: [],
|
|
493
|
-
autoResolvedCount: 0,
|
|
494
|
-
registeredAt: Date.now(),
|
|
495
|
-
lastActivityAt: Date.now(),
|
|
496
|
-
idleCheckCount: 0,
|
|
497
|
-
taskDelivered: false,
|
|
498
|
-
lastSeenDecisionIndex: 0,
|
|
499
|
-
});
|
|
500
|
-
// Persist last used repo so it survives task cleanup
|
|
501
|
-
if (context.repo) {
|
|
502
|
-
this._lastUsedRepo = context.repo;
|
|
503
|
-
}
|
|
504
|
-
// Log to persistent history (fire-and-forget)
|
|
505
|
-
this.history
|
|
506
|
-
.append({
|
|
507
|
-
timestamp: Date.now(),
|
|
508
|
-
type: "task_registered",
|
|
509
|
-
sessionId,
|
|
510
|
-
label: context.label,
|
|
511
|
-
agentType: context.agentType,
|
|
512
|
-
repo: context.repo,
|
|
513
|
-
workdir: context.workdir,
|
|
514
|
-
originalTask: context.originalTask,
|
|
515
|
-
})
|
|
516
|
-
.catch((err) => {
|
|
517
|
-
this.log(`Failed to append task registration history for ${sessionId}: ${err}`);
|
|
518
|
-
});
|
|
519
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
520
|
-
const persistPromise = taskCtx
|
|
521
|
-
? (async () => {
|
|
522
|
-
const existingThread = await this.taskRegistry.getThreadRecord(threadId);
|
|
523
|
-
if (!existingThread) {
|
|
524
|
-
await this.createTaskThread({
|
|
525
|
-
id: threadId,
|
|
526
|
-
title: context.label,
|
|
527
|
-
originalRequest: context.originalTask,
|
|
528
|
-
metadata: {
|
|
529
|
-
repo: context.repo ?? null,
|
|
530
|
-
source: "register-task-fallback",
|
|
531
|
-
},
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
const existingNode = await this.taskRegistry.getTaskNode(taskNodeId);
|
|
535
|
-
if (!existingNode) {
|
|
536
|
-
await this.taskRegistry.createTaskNode({
|
|
537
|
-
id: taskNodeId,
|
|
538
|
-
threadId: taskCtx.threadId,
|
|
539
|
-
kind: "execution",
|
|
540
|
-
status: "running",
|
|
541
|
-
title: context.label,
|
|
542
|
-
instructions: context.originalTask,
|
|
543
|
-
requiredCapabilities: [context.agentType],
|
|
544
|
-
assignedSessionId: sessionId,
|
|
545
|
-
assignedLabel: context.label,
|
|
546
|
-
agentType: context.agentType,
|
|
547
|
-
workdir: context.workdir,
|
|
548
|
-
repo: context.repo,
|
|
549
|
-
createdFrom: context.taskNodeId
|
|
550
|
-
? "register-task-existing-node"
|
|
551
|
-
: "register-task-fallback",
|
|
552
|
-
metadata: {
|
|
553
|
-
providerSource: context.providerSource ?? null,
|
|
554
|
-
},
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
else if (existingNode.threadId !== taskCtx.threadId) {
|
|
558
|
-
throw new Error(`Task node ${taskNodeId} belongs to ${existingNode.threadId}, expected ${taskCtx.threadId}`);
|
|
559
|
-
}
|
|
560
|
-
await Promise.all([
|
|
561
|
-
this.taskRegistry.registerSession({
|
|
562
|
-
threadId: taskCtx.threadId,
|
|
563
|
-
sessionId,
|
|
564
|
-
framework: context.agentType,
|
|
565
|
-
providerSource: context.providerSource,
|
|
566
|
-
label: context.label,
|
|
567
|
-
originalTask: context.originalTask,
|
|
568
|
-
workdir: context.workdir,
|
|
569
|
-
repo: context.repo,
|
|
570
|
-
status: "active",
|
|
571
|
-
decisionCount: 0,
|
|
572
|
-
autoResolvedCount: 0,
|
|
573
|
-
registeredAt: taskCtx.registeredAt,
|
|
574
|
-
lastActivityAt: taskCtx.lastActivityAt,
|
|
575
|
-
idleCheckCount: taskCtx.idleCheckCount,
|
|
576
|
-
taskDelivered: false,
|
|
577
|
-
lastSeenDecisionIndex: 0,
|
|
578
|
-
metadata: {
|
|
579
|
-
...(context.metadata ?? {}),
|
|
580
|
-
taskNodeId,
|
|
581
|
-
},
|
|
582
|
-
}),
|
|
583
|
-
this.taskRegistry.updateTaskNode(taskNodeId, {
|
|
584
|
-
status: "running",
|
|
585
|
-
title: context.label,
|
|
586
|
-
instructions: context.originalTask,
|
|
587
|
-
assignedSessionId: sessionId,
|
|
588
|
-
assignedLabel: context.label,
|
|
589
|
-
agentType: context.agentType,
|
|
590
|
-
workdir: context.workdir,
|
|
591
|
-
repo: context.repo,
|
|
592
|
-
metadata: {
|
|
593
|
-
providerSource: context.providerSource ?? null,
|
|
594
|
-
},
|
|
595
|
-
}),
|
|
596
|
-
this.taskRegistry.createTaskClaim({
|
|
597
|
-
threadId,
|
|
598
|
-
nodeId: taskNodeId,
|
|
599
|
-
sessionId,
|
|
600
|
-
claimType: "execution",
|
|
601
|
-
status: "active",
|
|
602
|
-
metadata: {
|
|
603
|
-
label: context.label,
|
|
604
|
-
},
|
|
605
|
-
}),
|
|
606
|
-
this.taskRegistry.appendEvent({
|
|
607
|
-
threadId,
|
|
608
|
-
sessionId,
|
|
609
|
-
eventType: "task_registered",
|
|
610
|
-
timestamp: Date.now(),
|
|
611
|
-
summary: `Registered task "${context.label}"`,
|
|
612
|
-
data: {
|
|
613
|
-
label: context.label,
|
|
614
|
-
originalTask: context.originalTask,
|
|
615
|
-
repo: context.repo ?? null,
|
|
616
|
-
taskNodeId,
|
|
617
|
-
},
|
|
618
|
-
}),
|
|
619
|
-
]);
|
|
620
|
-
})()
|
|
621
|
-
: Promise.resolve();
|
|
622
|
-
void persistPromise.catch((err) => {
|
|
623
|
-
this.log(`Failed to persist task registration for ${sessionId}: ${err}`);
|
|
624
|
-
});
|
|
625
|
-
this.broadcast({
|
|
626
|
-
type: "task_registered",
|
|
627
|
-
sessionId,
|
|
628
|
-
timestamp: Date.now(),
|
|
629
|
-
data: {
|
|
630
|
-
agentType: context.agentType,
|
|
631
|
-
label: context.label,
|
|
632
|
-
originalTask: context.originalTask,
|
|
633
|
-
},
|
|
634
|
-
});
|
|
635
|
-
// Cancel any pending retry timer and flush buffered events
|
|
636
|
-
const retryTimer = this.unregisteredRetryTimers.get(sessionId);
|
|
637
|
-
if (retryTimer) {
|
|
638
|
-
clearTimeout(retryTimer);
|
|
639
|
-
this.unregisteredRetryTimers.delete(sessionId);
|
|
640
|
-
}
|
|
641
|
-
const buffered = this.unregisteredBuffer.get(sessionId);
|
|
642
|
-
if (buffered) {
|
|
643
|
-
this.unregisteredBuffer.delete(sessionId);
|
|
644
|
-
for (const entry of buffered) {
|
|
645
|
-
this.handleNormalizedSessionEvent(entry.normalized).catch((err) => {
|
|
646
|
-
this.log(`Error replaying buffered event: ${err}`);
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
await persistPromise;
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Return the repo URL from the most recently registered task that had one.
|
|
654
|
-
* Useful as a fallback when the user says "in the same repo" without a URL.
|
|
655
|
-
*/
|
|
656
|
-
/**
|
|
657
|
-
* Persisted separately from tasks so it survives task cleanup.
|
|
658
|
-
* Updated whenever a task with a repo is registered.
|
|
659
|
-
*/
|
|
660
|
-
_lastUsedRepo;
|
|
661
|
-
getLastUsedRepo() {
|
|
662
|
-
// Check active tasks first (freshest), fall back to in-memory persisted value
|
|
663
|
-
let latest;
|
|
664
|
-
for (const task of this.tasks.values()) {
|
|
665
|
-
if (task.repo && (!latest || task.registeredAt > latest.registeredAt)) {
|
|
666
|
-
latest = task;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
return latest?.repo ?? this._lastUsedRepo;
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Async version that also checks disk history — survives process restarts.
|
|
673
|
-
* Callers that can await should prefer this over the sync version.
|
|
674
|
-
*/
|
|
675
|
-
async getLastUsedRepoAsync() {
|
|
676
|
-
const memoryRepo = this.getLastUsedRepo();
|
|
677
|
-
if (memoryRepo)
|
|
678
|
-
return memoryRepo;
|
|
679
|
-
try {
|
|
680
|
-
return ((await this.taskRegistry.getLastUsedRepo()) ??
|
|
681
|
-
(await this.history.getLastUsedRepo()));
|
|
682
|
-
}
|
|
683
|
-
catch {
|
|
684
|
-
return undefined;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
getTaskContext(sessionId) {
|
|
688
|
-
return this.tasks.get(sessionId);
|
|
689
|
-
}
|
|
690
|
-
mapDecisionRecord(record) {
|
|
691
|
-
return {
|
|
692
|
-
timestamp: record.timestamp,
|
|
693
|
-
event: record.event,
|
|
694
|
-
promptText: record.promptText,
|
|
695
|
-
decision: record.decision,
|
|
696
|
-
...(record.response ? { response: record.response } : {}),
|
|
697
|
-
reasoning: record.reasoning,
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
mapSessionStatus(status) {
|
|
701
|
-
switch (status) {
|
|
702
|
-
case "blocked":
|
|
703
|
-
case "waiting_on_user":
|
|
704
|
-
return "blocked";
|
|
705
|
-
case "tool_running":
|
|
706
|
-
return "tool_running";
|
|
707
|
-
case "completed":
|
|
708
|
-
return "completed";
|
|
709
|
-
case "error":
|
|
710
|
-
return "error";
|
|
711
|
-
case "stopped":
|
|
712
|
-
case "interrupted":
|
|
713
|
-
return "stopped";
|
|
714
|
-
default:
|
|
715
|
-
return "active";
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
mapTaskContextStatusToNodeStatus(status) {
|
|
719
|
-
switch (status) {
|
|
720
|
-
case "blocked":
|
|
721
|
-
return "blocked";
|
|
722
|
-
case "completed":
|
|
723
|
-
return "completed";
|
|
724
|
-
case "error":
|
|
725
|
-
return "failed";
|
|
726
|
-
case "stopped":
|
|
727
|
-
return "interrupted";
|
|
728
|
-
case "tool_running":
|
|
729
|
-
return "running";
|
|
730
|
-
default:
|
|
731
|
-
return "running";
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
buildTaskContextFromSession(session, decisions) {
|
|
735
|
-
return {
|
|
736
|
-
threadId: session.threadId,
|
|
737
|
-
...(typeof session.metadata.taskNodeId === "string" &&
|
|
738
|
-
session.metadata.taskNodeId.trim().length > 0
|
|
739
|
-
? { taskNodeId: session.metadata.taskNodeId }
|
|
740
|
-
: {}),
|
|
741
|
-
sessionId: session.sessionId,
|
|
742
|
-
agentType: session.framework,
|
|
743
|
-
label: session.label,
|
|
744
|
-
originalTask: session.originalTask,
|
|
745
|
-
workdir: session.workdir,
|
|
746
|
-
...(session.repo ? { repo: session.repo } : {}),
|
|
747
|
-
status: this.mapSessionStatus(session.status),
|
|
748
|
-
decisions: decisions.map((decision) => this.mapDecisionRecord(decision)),
|
|
749
|
-
autoResolvedCount: session.autoResolvedCount,
|
|
750
|
-
registeredAt: session.registeredAt,
|
|
751
|
-
lastActivityAt: session.lastActivityAt,
|
|
752
|
-
idleCheckCount: session.idleCheckCount,
|
|
753
|
-
taskDelivered: session.taskDelivered,
|
|
754
|
-
...(session.completionSummary
|
|
755
|
-
? { completionSummary: session.completionSummary }
|
|
756
|
-
: {}),
|
|
757
|
-
lastSeenDecisionIndex: session.lastSeenDecisionIndex,
|
|
758
|
-
...(session.lastInputSentAt !== null
|
|
759
|
-
? { lastInputSentAt: session.lastInputSentAt }
|
|
760
|
-
: {}),
|
|
761
|
-
...(session.stoppedAt !== null ? { stoppedAt: session.stoppedAt } : {}),
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
async getTaskContextSnapshot(sessionId) {
|
|
765
|
-
const live = this.tasks.get(sessionId);
|
|
766
|
-
if (live)
|
|
767
|
-
return live;
|
|
768
|
-
const session = await this.taskRegistry.getSession(sessionId);
|
|
769
|
-
if (!session)
|
|
770
|
-
return null;
|
|
771
|
-
const decisions = await this.taskRegistry.listDecisionsForSession(sessionId);
|
|
772
|
-
return this.buildTaskContextFromSession(session, decisions);
|
|
773
|
-
}
|
|
774
|
-
getAllTaskContexts() {
|
|
775
|
-
return Array.from(this.tasks.values());
|
|
776
|
-
}
|
|
777
|
-
async createTaskThread(input) {
|
|
778
|
-
const normalizedInput = {
|
|
779
|
-
...input,
|
|
780
|
-
kind: inferTaskThreadKind(input),
|
|
781
|
-
};
|
|
782
|
-
const acceptance = await deriveTaskAcceptanceCriteria(this.runtime, normalizedInput);
|
|
783
|
-
const thread = await this.taskRegistry.createThread({
|
|
784
|
-
...normalizedInput,
|
|
785
|
-
acceptanceCriteria: acceptance.criteria,
|
|
786
|
-
metadata: {
|
|
787
|
-
...(normalizedInput.metadata ?? {}),
|
|
788
|
-
acceptanceCriteriaSource: acceptance.source,
|
|
789
|
-
},
|
|
790
|
-
});
|
|
791
|
-
const summary = await this.taskRegistry.getThreadSummary(thread.id);
|
|
792
|
-
if (!summary) {
|
|
793
|
-
throw new Error(`Failed to load task thread ${thread.id}`);
|
|
794
|
-
}
|
|
795
|
-
return summary;
|
|
796
|
-
}
|
|
797
|
-
async planTaskThreadGraph(input) {
|
|
798
|
-
const thread = await this.taskRegistry.getThreadRecord(input.threadId);
|
|
799
|
-
if (!thread) {
|
|
800
|
-
throw new Error(`Task thread ${input.threadId} not found`);
|
|
801
|
-
}
|
|
802
|
-
const rootNode = await this.taskRegistry.createTaskNode({
|
|
803
|
-
threadId: input.threadId,
|
|
804
|
-
parentNodeId: null,
|
|
805
|
-
kind: "goal",
|
|
806
|
-
status: "planned",
|
|
807
|
-
title: input.title,
|
|
808
|
-
instructions: input.originalRequest,
|
|
809
|
-
acceptanceCriteria: thread.acceptanceCriteria,
|
|
810
|
-
priority: 100,
|
|
811
|
-
depth: 0,
|
|
812
|
-
sequence: 0,
|
|
813
|
-
createdFrom: "planner",
|
|
814
|
-
metadata: {
|
|
815
|
-
threadKind: thread.kind,
|
|
816
|
-
source: "swarm-planner",
|
|
817
|
-
},
|
|
818
|
-
});
|
|
819
|
-
// The acceptance verifier is a code-completion safety check: it asks the
|
|
820
|
-
// LLM whether file/test evidence in the workspace matches the criteria,
|
|
821
|
-
// catching agents that lie about completing code work. It's only useful
|
|
822
|
-
// when there is real artifact evidence to verify (a repo) AND real
|
|
823
|
-
// criteria to check against (provided or model-generated, not the
|
|
824
|
-
// baseline placeholder fallback). For chat / question-answering tasks
|
|
825
|
-
// (no repo, response IS the deliverable) the verifier produces false
|
|
826
|
-
// failures because there are no files to inspect.
|
|
827
|
-
const acceptanceCriteriaSource = typeof thread.metadata?.acceptanceCriteriaSource === "string"
|
|
828
|
-
? thread.metadata.acceptanceCriteriaSource
|
|
829
|
-
: null;
|
|
830
|
-
const hasRepo = typeof thread.metadata?.repo === "string" &&
|
|
831
|
-
thread.metadata.repo.trim().length > 0;
|
|
832
|
-
if (thread.acceptanceCriteria.length > 0 &&
|
|
833
|
-
acceptanceCriteriaSource !== "baseline" &&
|
|
834
|
-
hasRepo) {
|
|
835
|
-
await this.taskRegistry.createTaskVerifierJob({
|
|
836
|
-
threadId: input.threadId,
|
|
837
|
-
nodeId: rootNode.id,
|
|
838
|
-
status: "pending",
|
|
839
|
-
verifierType: "acceptance_criteria",
|
|
840
|
-
title: `Verify acceptance criteria for ${input.title}`,
|
|
841
|
-
instructions: thread.acceptanceCriteria.join("\n"),
|
|
842
|
-
config: {
|
|
843
|
-
acceptanceCriteria: thread.acceptanceCriteria,
|
|
844
|
-
},
|
|
845
|
-
metadata: {
|
|
846
|
-
source: "thread-acceptance",
|
|
847
|
-
},
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
if (input.sharedContext?.trim()) {
|
|
851
|
-
await this.taskRegistry.appendTaskMailboxMessage({
|
|
852
|
-
threadId: input.threadId,
|
|
853
|
-
nodeId: rootNode.id,
|
|
854
|
-
sender: "planner",
|
|
855
|
-
recipient: "all-workers",
|
|
856
|
-
subject: "shared-context",
|
|
857
|
-
body: input.sharedContext.trim(),
|
|
858
|
-
deliveryState: "delivered",
|
|
859
|
-
deliveredAt: new Date().toISOString(),
|
|
860
|
-
metadata: {
|
|
861
|
-
source: "swarm-planner",
|
|
862
|
-
},
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
const workerNodes = [];
|
|
866
|
-
for (const [index, subtask] of input.subtasks.entries()) {
|
|
867
|
-
const node = await this.taskRegistry.createTaskNode({
|
|
868
|
-
threadId: input.threadId,
|
|
869
|
-
parentNodeId: rootNode.id,
|
|
870
|
-
kind: "execution",
|
|
871
|
-
status: "ready",
|
|
872
|
-
title: subtask.label,
|
|
873
|
-
instructions: subtask.originalTask,
|
|
874
|
-
requiredCapabilities: [subtask.agentType],
|
|
875
|
-
repo: subtask.repo,
|
|
876
|
-
priority: 10,
|
|
877
|
-
depth: 1,
|
|
878
|
-
sequence: index + 1,
|
|
879
|
-
createdFrom: "planner",
|
|
880
|
-
metadata: {
|
|
881
|
-
agentType: subtask.agentType,
|
|
882
|
-
source: "swarm-planner",
|
|
883
|
-
},
|
|
884
|
-
});
|
|
885
|
-
await this.taskRegistry.createTaskDependency({
|
|
886
|
-
threadId: input.threadId,
|
|
887
|
-
fromNodeId: node.id,
|
|
888
|
-
toNodeId: rootNode.id,
|
|
889
|
-
dependencyKind: "parent_child",
|
|
890
|
-
requiredStatus: "completed",
|
|
891
|
-
metadata: {
|
|
892
|
-
source: "swarm-planner",
|
|
893
|
-
},
|
|
894
|
-
});
|
|
895
|
-
if (input.sharedContext?.trim()) {
|
|
896
|
-
await this.taskRegistry.appendTaskMailboxMessage({
|
|
897
|
-
threadId: input.threadId,
|
|
898
|
-
nodeId: node.id,
|
|
899
|
-
sender: "planner",
|
|
900
|
-
recipient: subtask.label,
|
|
901
|
-
subject: "task-brief",
|
|
902
|
-
body: input.sharedContext.trim(),
|
|
903
|
-
deliveryState: "delivered",
|
|
904
|
-
deliveredAt: new Date().toISOString(),
|
|
905
|
-
metadata: {
|
|
906
|
-
task: subtask.originalTask,
|
|
907
|
-
agentType: subtask.agentType,
|
|
908
|
-
},
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
workerNodes.push(node);
|
|
912
|
-
}
|
|
913
|
-
await this.taskRegistry.updateThread(input.threadId, {
|
|
914
|
-
currentPlan: {
|
|
915
|
-
...thread.currentPlan,
|
|
916
|
-
rootTaskNodeId: rootNode.id,
|
|
917
|
-
taskNodeIds: workerNodes.map((node) => node.id),
|
|
918
|
-
taskNodeCount: workerNodes.length + 1,
|
|
919
|
-
},
|
|
920
|
-
});
|
|
921
|
-
return { rootNode, workerNodes };
|
|
922
|
-
}
|
|
923
|
-
async listTaskThreads(options) {
|
|
924
|
-
return this.taskRegistry.listThreads(options);
|
|
925
|
-
}
|
|
926
|
-
async getTaskThread(threadId) {
|
|
927
|
-
return this.taskRegistry.getThread(threadId);
|
|
928
|
-
}
|
|
929
|
-
async archiveTaskThread(threadId) {
|
|
930
|
-
await this.taskRegistry.archiveThread(threadId);
|
|
931
|
-
}
|
|
932
|
-
async reopenTaskThread(threadId) {
|
|
933
|
-
await this.taskRegistry.reopenThread(threadId);
|
|
934
|
-
}
|
|
935
|
-
async countTaskThreads(options) {
|
|
936
|
-
return this.taskRegistry.countThreads(options);
|
|
937
|
-
}
|
|
938
|
-
getLiveTaskContextsForThread(threadId) {
|
|
939
|
-
return Array.from(this.tasks.values())
|
|
940
|
-
.filter((task) => task.threadId === threadId)
|
|
941
|
-
.sort((left, right) => right.lastActivityAt - left.lastActivityAt);
|
|
942
|
-
}
|
|
943
|
-
async stopLiveThreadSessions(threadId, force) {
|
|
944
|
-
if (!this.ptyService) {
|
|
945
|
-
return [];
|
|
946
|
-
}
|
|
947
|
-
const sessionIds = this.getLiveTaskContextsForThread(threadId)
|
|
948
|
-
.filter((task) => task.status === "active" ||
|
|
949
|
-
task.status === "blocked" ||
|
|
950
|
-
task.status === "tool_running")
|
|
951
|
-
.map((task) => task.sessionId);
|
|
952
|
-
for (const sessionId of sessionIds) {
|
|
953
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
954
|
-
if (taskCtx) {
|
|
955
|
-
taskCtx.status = "stopped";
|
|
956
|
-
taskCtx.stoppedAt = Date.now();
|
|
957
|
-
await this.syncTaskContext(taskCtx);
|
|
958
|
-
}
|
|
959
|
-
try {
|
|
960
|
-
await this.ptyService.stopSession(sessionId, force);
|
|
961
|
-
}
|
|
962
|
-
catch (error) {
|
|
963
|
-
this.log(`Failed to stop session ${sessionId} for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
return sessionIds;
|
|
967
|
-
}
|
|
968
|
-
clipText(value, limit) {
|
|
969
|
-
if (value.length <= limit)
|
|
970
|
-
return value;
|
|
971
|
-
return `${value.slice(0, limit)}…`;
|
|
972
|
-
}
|
|
973
|
-
formatResumePrompt(thread, instruction) {
|
|
974
|
-
const acceptanceCriteria = (thread.acceptanceCriteria ?? [])
|
|
975
|
-
.map((item) => `- ${item}`)
|
|
976
|
-
.join("\n");
|
|
977
|
-
const recentDecisions = (thread.decisions ?? [])
|
|
978
|
-
.slice(-6)
|
|
979
|
-
.map((decision, index) => `${index + 1}. ${decision.event}: ${decision.reasoning}${decision.response ? ` (response: ${decision.response})` : ""}`)
|
|
980
|
-
.join("\n");
|
|
981
|
-
const recentEvents = (thread.events ?? [])
|
|
982
|
-
.slice(-8)
|
|
983
|
-
.map((event, index) => `${index + 1}. ${event.eventType}: ${this.clipText(event.summary, 180)}`)
|
|
984
|
-
.join("\n");
|
|
985
|
-
const transcriptExcerpt = (thread.transcripts ?? [])
|
|
986
|
-
.slice(-20)
|
|
987
|
-
.map((entry) => `${entry.direction.toUpperCase()}: ${this.clipText(entry.content.trim(), 220)}`)
|
|
988
|
-
.filter((line) => line.length > 0)
|
|
989
|
-
.join("\n");
|
|
990
|
-
const latestSession = (thread.sessions ?? [])
|
|
991
|
-
.slice()
|
|
992
|
-
.sort((left, right) => right.lastActivityAt - left.lastActivityAt)[0];
|
|
993
|
-
return [
|
|
994
|
-
"Resume an existing Eliza coordinator task thread.",
|
|
995
|
-
"",
|
|
996
|
-
`Thread: ${thread.title}`,
|
|
997
|
-
`Original request: ${thread.originalRequest}`,
|
|
998
|
-
latestSession?.workdir ? `Workspace: ${latestSession.workdir}` : "",
|
|
999
|
-
latestSession?.repo ? `Repository: ${latestSession.repo}` : "",
|
|
1000
|
-
thread.summary ? `Current summary: ${thread.summary}` : "",
|
|
1001
|
-
acceptanceCriteria ? `Acceptance criteria:\n${acceptanceCriteria}` : "",
|
|
1002
|
-
instruction?.trim()
|
|
1003
|
-
? `Latest user instruction:\n${instruction.trim()}`
|
|
1004
|
-
: "Continue from the current workspace state without starting over.",
|
|
1005
|
-
recentDecisions
|
|
1006
|
-
? `Recent coordinator decisions:\n${recentDecisions}`
|
|
1007
|
-
: "",
|
|
1008
|
-
recentEvents ? `Recent task events:\n${recentEvents}` : "",
|
|
1009
|
-
transcriptExcerpt
|
|
1010
|
-
? `Recent transcript excerpt:\n${transcriptExcerpt}`
|
|
1011
|
-
: "",
|
|
1012
|
-
"Inspect the current workspace, continue the task, run the relevant verification, and summarize what changed.",
|
|
1013
|
-
]
|
|
1014
|
-
.filter(Boolean)
|
|
1015
|
-
.join("\n\n");
|
|
1016
|
-
}
|
|
1017
|
-
async pauseTaskThread(threadId, note) {
|
|
1018
|
-
const thread = await this.getTaskThread(threadId);
|
|
1019
|
-
if (!thread) {
|
|
1020
|
-
throw new Error(`Task thread ${threadId} not found`);
|
|
1021
|
-
}
|
|
1022
|
-
const stoppedSessionIds = await this.stopLiveThreadSessions(threadId, true);
|
|
1023
|
-
const nowIso = new Date().toISOString();
|
|
1024
|
-
await this.taskRegistry.updateThread(threadId, {
|
|
1025
|
-
status: "waiting_on_user",
|
|
1026
|
-
closedAt: null,
|
|
1027
|
-
lastCoordinatorTurnAt: nowIso,
|
|
1028
|
-
metadata: {
|
|
1029
|
-
controlState: "paused",
|
|
1030
|
-
pauseNote: note ?? null,
|
|
1031
|
-
pauseRequestedAt: nowIso,
|
|
1032
|
-
},
|
|
1033
|
-
});
|
|
1034
|
-
await this.taskRegistry.appendEvent({
|
|
1035
|
-
threadId,
|
|
1036
|
-
eventType: "task_paused",
|
|
1037
|
-
summary: note?.trim()
|
|
1038
|
-
? `Paused task thread: ${note.trim()}`
|
|
1039
|
-
: "Paused task thread for user review",
|
|
1040
|
-
data: {
|
|
1041
|
-
note: note ?? null,
|
|
1042
|
-
stoppedSessionIds,
|
|
1043
|
-
},
|
|
1044
|
-
});
|
|
1045
|
-
return { threadId, stoppedSessionIds };
|
|
1046
|
-
}
|
|
1047
|
-
async stopTaskThread(threadId, note) {
|
|
1048
|
-
const thread = await this.getTaskThread(threadId);
|
|
1049
|
-
if (!thread) {
|
|
1050
|
-
throw new Error(`Task thread ${threadId} not found`);
|
|
1051
|
-
}
|
|
1052
|
-
const stoppedSessionIds = await this.stopLiveThreadSessions(threadId, true);
|
|
1053
|
-
const nowIso = new Date().toISOString();
|
|
1054
|
-
await this.taskRegistry.updateThread(threadId, {
|
|
1055
|
-
status: "interrupted",
|
|
1056
|
-
closedAt: nowIso,
|
|
1057
|
-
lastCoordinatorTurnAt: nowIso,
|
|
1058
|
-
metadata: {
|
|
1059
|
-
controlState: "stopped",
|
|
1060
|
-
stopNote: note ?? null,
|
|
1061
|
-
stoppedByUserAt: nowIso,
|
|
1062
|
-
},
|
|
1063
|
-
});
|
|
1064
|
-
await this.taskRegistry.appendEvent({
|
|
1065
|
-
threadId,
|
|
1066
|
-
eventType: "task_stopped",
|
|
1067
|
-
summary: note?.trim()
|
|
1068
|
-
? `Stopped task thread: ${note.trim()}`
|
|
1069
|
-
: "Stopped task thread at user request",
|
|
1070
|
-
data: {
|
|
1071
|
-
note: note ?? null,
|
|
1072
|
-
stoppedSessionIds,
|
|
1073
|
-
},
|
|
1074
|
-
});
|
|
1075
|
-
return { threadId, stoppedSessionIds };
|
|
1076
|
-
}
|
|
1077
|
-
async resumeTaskThread(threadId, instruction, agentType) {
|
|
1078
|
-
const thread = await this.getTaskThread(threadId);
|
|
1079
|
-
if (!thread) {
|
|
1080
|
-
throw new Error(`Task thread ${threadId} not found`);
|
|
1081
|
-
}
|
|
1082
|
-
if (!this.ptyService) {
|
|
1083
|
-
throw new Error("PTY Service is not available");
|
|
1084
|
-
}
|
|
1085
|
-
const activeTask = this.getLiveTaskContextsForThread(threadId).find((task) => task.status !== "stopped" &&
|
|
1086
|
-
task.status !== "completed" &&
|
|
1087
|
-
task.status !== "error");
|
|
1088
|
-
if (activeTask) {
|
|
1089
|
-
if (instruction?.trim()) {
|
|
1090
|
-
await this.ptyService.sendToSession(activeTask.sessionId, instruction.trim());
|
|
1091
|
-
activeTask.lastInputSentAt = Date.now();
|
|
1092
|
-
activeTask.status = "active";
|
|
1093
|
-
await this.syncTaskContext(activeTask);
|
|
1094
|
-
}
|
|
1095
|
-
const nowIso = new Date().toISOString();
|
|
1096
|
-
await this.taskRegistry.updateThread(threadId, {
|
|
1097
|
-
status: "active",
|
|
1098
|
-
closedAt: null,
|
|
1099
|
-
lastCoordinatorTurnAt: nowIso,
|
|
1100
|
-
metadata: {
|
|
1101
|
-
controlState: null,
|
|
1102
|
-
resumedAt: nowIso,
|
|
1103
|
-
},
|
|
1104
|
-
});
|
|
1105
|
-
await this.taskRegistry.appendEvent({
|
|
1106
|
-
threadId,
|
|
1107
|
-
sessionId: activeTask.sessionId,
|
|
1108
|
-
eventType: "task_resumed",
|
|
1109
|
-
summary: "Continued the active task thread",
|
|
1110
|
-
data: {
|
|
1111
|
-
reusedSession: true,
|
|
1112
|
-
instruction: instruction ?? null,
|
|
1113
|
-
},
|
|
1114
|
-
});
|
|
1115
|
-
return {
|
|
1116
|
-
threadId,
|
|
1117
|
-
sessionId: activeTask.sessionId,
|
|
1118
|
-
reusedSession: true,
|
|
1119
|
-
framework: activeTask.agentType,
|
|
1120
|
-
};
|
|
1121
|
-
}
|
|
1122
|
-
const latestSession = (thread.sessions ?? [])
|
|
1123
|
-
.slice()
|
|
1124
|
-
.sort((left, right) => right.lastActivityAt - left.lastActivityAt)[0];
|
|
1125
|
-
const workdir = latestSession?.workdir ?? thread.latestWorkdir;
|
|
1126
|
-
if (!workdir) {
|
|
1127
|
-
throw new Error(`Task thread ${threadId} has no resumable workspace`);
|
|
1128
|
-
}
|
|
1129
|
-
const requestedFramework = agentType
|
|
1130
|
-
? normalizeAgentType(agentType)
|
|
1131
|
-
: latestSession?.framework
|
|
1132
|
-
? normalizeAgentType(latestSession.framework)
|
|
1133
|
-
: normalizeAgentType(await this.ptyService.resolveAgentType());
|
|
1134
|
-
const frameworkState = await this.ptyService.getFrameworkState();
|
|
1135
|
-
const framework = frameworkState.frameworks.find((entry) => entry.id === requestedFramework);
|
|
1136
|
-
const resolvedFramework = framework?.installed &&
|
|
1137
|
-
framework.authReady &&
|
|
1138
|
-
!framework.temporarilyDisabled
|
|
1139
|
-
? requestedFramework
|
|
1140
|
-
: normalizeAgentType(await this.ptyService.resolveAgentType());
|
|
1141
|
-
const resolvedAvailability = frameworkState.frameworks.find((entry) => entry.id === resolvedFramework);
|
|
1142
|
-
const session = await this.ptyService.spawnSession({
|
|
1143
|
-
name: `task-resume-${thread.id.slice(-8)}`,
|
|
1144
|
-
agentType: resolvedFramework,
|
|
1145
|
-
workdir,
|
|
1146
|
-
initialTask: this.formatResumePrompt(thread, instruction),
|
|
1147
|
-
credentials: buildAgentCredentials(this.runtime),
|
|
1148
|
-
approvalPreset: this.ptyService.defaultApprovalPreset,
|
|
1149
|
-
skipAdapterAutoResponse: true,
|
|
1150
|
-
metadata: {
|
|
1151
|
-
threadId,
|
|
1152
|
-
label: thread.title,
|
|
1153
|
-
requestedType: resolvedFramework,
|
|
1154
|
-
resumedFromThreadId: threadId,
|
|
1155
|
-
resumedFromSessionId: latestSession?.sessionId ?? null,
|
|
1156
|
-
resumeInstruction: instruction ?? null,
|
|
1157
|
-
resumedAt: Date.now(),
|
|
1158
|
-
},
|
|
1159
|
-
});
|
|
1160
|
-
await this.registerTask(session.id, {
|
|
1161
|
-
threadId,
|
|
1162
|
-
taskNodeId: typeof latestSession?.metadata.taskNodeId === "string"
|
|
1163
|
-
? latestSession.metadata.taskNodeId
|
|
1164
|
-
: undefined,
|
|
1165
|
-
agentType: resolvedFramework,
|
|
1166
|
-
label: latestSession?.label ?? thread.title,
|
|
1167
|
-
originalTask: instruction?.trim() || thread.originalRequest,
|
|
1168
|
-
workdir,
|
|
1169
|
-
repo: latestSession?.repo ?? thread.latestRepo ?? undefined,
|
|
1170
|
-
providerSource: resolvedAvailability
|
|
1171
|
-
? inferProviderSource(resolvedAvailability)
|
|
1172
|
-
: null,
|
|
1173
|
-
metadata: session.metadata &&
|
|
1174
|
-
typeof session.metadata === "object" &&
|
|
1175
|
-
!Array.isArray(session.metadata)
|
|
1176
|
-
? session.metadata
|
|
1177
|
-
: undefined,
|
|
1178
|
-
});
|
|
1179
|
-
const nowIso = new Date().toISOString();
|
|
1180
|
-
await this.taskRegistry.updateThread(threadId, {
|
|
1181
|
-
status: "active",
|
|
1182
|
-
closedAt: null,
|
|
1183
|
-
lastCoordinatorTurnAt: nowIso,
|
|
1184
|
-
metadata: {
|
|
1185
|
-
controlState: null,
|
|
1186
|
-
resumedAt: nowIso,
|
|
1187
|
-
lastResumedSessionId: session.id,
|
|
1188
|
-
},
|
|
1189
|
-
});
|
|
1190
|
-
await this.taskRegistry.appendEvent({
|
|
1191
|
-
threadId,
|
|
1192
|
-
sessionId: session.id,
|
|
1193
|
-
eventType: "task_resumed",
|
|
1194
|
-
summary: "Resumed the task thread on a new session",
|
|
1195
|
-
data: {
|
|
1196
|
-
reusedSession: false,
|
|
1197
|
-
fromSessionId: latestSession?.sessionId ?? null,
|
|
1198
|
-
toSessionId: session.id,
|
|
1199
|
-
instruction: instruction ?? null,
|
|
1200
|
-
framework: resolvedFramework,
|
|
1201
|
-
},
|
|
1202
|
-
});
|
|
1203
|
-
return {
|
|
1204
|
-
threadId,
|
|
1205
|
-
sessionId: session.id,
|
|
1206
|
-
reusedSession: false,
|
|
1207
|
-
framework: resolvedFramework,
|
|
1208
|
-
};
|
|
1209
|
-
}
|
|
1210
|
-
async continueTaskThread(threadId, instruction, agentType) {
|
|
1211
|
-
const latestLiveTask = this.getLiveTaskContextsForThread(threadId).find((task) => task.status === "active" ||
|
|
1212
|
-
task.status === "blocked" ||
|
|
1213
|
-
task.status === "tool_running");
|
|
1214
|
-
if (latestLiveTask && this.ptyService) {
|
|
1215
|
-
await this.ptyService.sendToSession(latestLiveTask.sessionId, instruction);
|
|
1216
|
-
latestLiveTask.lastInputSentAt = Date.now();
|
|
1217
|
-
latestLiveTask.status = "active";
|
|
1218
|
-
await this.syncTaskContext(latestLiveTask);
|
|
1219
|
-
const nowIso = new Date().toISOString();
|
|
1220
|
-
await this.taskRegistry.updateThread(threadId, {
|
|
1221
|
-
status: "active",
|
|
1222
|
-
closedAt: null,
|
|
1223
|
-
lastCoordinatorTurnAt: nowIso,
|
|
1224
|
-
metadata: {
|
|
1225
|
-
controlState: null,
|
|
1226
|
-
continuedAt: nowIso,
|
|
1227
|
-
},
|
|
1228
|
-
});
|
|
1229
|
-
await this.taskRegistry.appendEvent({
|
|
1230
|
-
threadId,
|
|
1231
|
-
sessionId: latestLiveTask.sessionId,
|
|
1232
|
-
eventType: "task_resumed",
|
|
1233
|
-
summary: "Sent follow-up instructions to the active task thread",
|
|
1234
|
-
data: {
|
|
1235
|
-
reusedSession: true,
|
|
1236
|
-
instruction,
|
|
1237
|
-
},
|
|
1238
|
-
});
|
|
1239
|
-
return {
|
|
1240
|
-
threadId,
|
|
1241
|
-
sessionId: latestLiveTask.sessionId,
|
|
1242
|
-
reusedSession: true,
|
|
1243
|
-
framework: latestLiveTask.agentType,
|
|
1244
|
-
};
|
|
1245
|
-
}
|
|
1246
|
-
return this.resumeTaskThread(threadId, instruction, agentType);
|
|
1247
|
-
}
|
|
1248
|
-
async syncTaskContext(taskCtx) {
|
|
1249
|
-
await this.taskRegistry.updateSession(taskCtx.sessionId, {
|
|
1250
|
-
status: taskCtx.status === "completed"
|
|
1251
|
-
? "completed"
|
|
1252
|
-
: taskCtx.status === "error"
|
|
1253
|
-
? "error"
|
|
1254
|
-
: taskCtx.status === "stopped"
|
|
1255
|
-
? "stopped"
|
|
1256
|
-
: taskCtx.status === "blocked"
|
|
1257
|
-
? "blocked"
|
|
1258
|
-
: taskCtx.status === "tool_running"
|
|
1259
|
-
? "tool_running"
|
|
1260
|
-
: "active",
|
|
1261
|
-
decisionCount: taskCtx.decisions.length,
|
|
1262
|
-
autoResolvedCount: taskCtx.autoResolvedCount,
|
|
1263
|
-
lastActivityAt: taskCtx.lastActivityAt,
|
|
1264
|
-
idleCheckCount: taskCtx.idleCheckCount,
|
|
1265
|
-
taskDelivered: taskCtx.taskDelivered,
|
|
1266
|
-
completionSummary: taskCtx.completionSummary ?? null,
|
|
1267
|
-
lastSeenDecisionIndex: taskCtx.lastSeenDecisionIndex,
|
|
1268
|
-
lastInputSentAt: taskCtx.lastInputSentAt,
|
|
1269
|
-
stoppedAt: taskCtx.stoppedAt,
|
|
1270
|
-
});
|
|
1271
|
-
if (!taskCtx.taskNodeId) {
|
|
1272
|
-
return;
|
|
1273
|
-
}
|
|
1274
|
-
const nodeStatus = this.mapTaskContextStatusToNodeStatus(taskCtx.status);
|
|
1275
|
-
await this.taskRegistry.updateTaskNode(taskCtx.taskNodeId, {
|
|
1276
|
-
status: nodeStatus,
|
|
1277
|
-
title: taskCtx.label,
|
|
1278
|
-
instructions: taskCtx.originalTask,
|
|
1279
|
-
assignedSessionId: taskCtx.sessionId,
|
|
1280
|
-
assignedLabel: taskCtx.label,
|
|
1281
|
-
agentType: taskCtx.agentType,
|
|
1282
|
-
workdir: taskCtx.workdir,
|
|
1283
|
-
repo: taskCtx.repo ?? null,
|
|
1284
|
-
metadata: {
|
|
1285
|
-
completionSummary: taskCtx.completionSummary ?? null,
|
|
1286
|
-
},
|
|
1287
|
-
});
|
|
1288
|
-
const activeClaim = await this.taskRegistry.findActiveTaskClaim(taskCtx.taskNodeId, taskCtx.sessionId);
|
|
1289
|
-
if (taskCtx.status === "completed" ||
|
|
1290
|
-
taskCtx.status === "error" ||
|
|
1291
|
-
taskCtx.status === "stopped") {
|
|
1292
|
-
if (activeClaim) {
|
|
1293
|
-
await this.taskRegistry.updateTaskClaim(activeClaim.id, {
|
|
1294
|
-
status: taskCtx.status === "completed"
|
|
1295
|
-
? "completed"
|
|
1296
|
-
: taskCtx.status === "error"
|
|
1297
|
-
? "failed"
|
|
1298
|
-
: "interrupted",
|
|
1299
|
-
releasedAt: new Date().toISOString(),
|
|
1300
|
-
metadata: {
|
|
1301
|
-
completionSummary: taskCtx.completionSummary ?? null,
|
|
1302
|
-
},
|
|
1303
|
-
});
|
|
1304
|
-
}
|
|
1305
|
-
return;
|
|
1306
|
-
}
|
|
1307
|
-
if (!activeClaim) {
|
|
1308
|
-
await this.taskRegistry.createTaskClaim({
|
|
1309
|
-
threadId: taskCtx.threadId,
|
|
1310
|
-
nodeId: taskCtx.taskNodeId,
|
|
1311
|
-
sessionId: taskCtx.sessionId,
|
|
1312
|
-
claimType: "execution",
|
|
1313
|
-
status: "active",
|
|
1314
|
-
metadata: {
|
|
1315
|
-
label: taskCtx.label,
|
|
1316
|
-
},
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
isAutomaticFailoverFramework(agentType) {
|
|
1321
|
-
return (agentType === "claude" ||
|
|
1322
|
-
agentType === "codex" ||
|
|
1323
|
-
agentType === "gemini" ||
|
|
1324
|
-
agentType === "aider");
|
|
1325
|
-
}
|
|
1326
|
-
getFailoverCandidates(frameworks, failedFramework, preferredFrameworkId) {
|
|
1327
|
-
const preferred = frameworks.find((framework) => framework.id === preferredFrameworkId);
|
|
1328
|
-
const remainder = frameworks.filter((framework) => framework.id !== preferredFrameworkId);
|
|
1329
|
-
return [preferred, ...remainder].filter((framework) => Boolean(framework &&
|
|
1330
|
-
framework.id !== failedFramework &&
|
|
1331
|
-
framework.installed &&
|
|
1332
|
-
framework.authReady &&
|
|
1333
|
-
!framework.temporarilyDisabled));
|
|
1334
|
-
}
|
|
1335
|
-
getRecoveryCandidates(frameworks, currentFramework, preferredFrameworkId, preferAlternative) {
|
|
1336
|
-
const healthy = frameworks.filter((framework) => framework.installed &&
|
|
1337
|
-
framework.authReady &&
|
|
1338
|
-
!framework.temporarilyDisabled);
|
|
1339
|
-
const byId = new Map(healthy.map((framework) => [framework.id, framework]));
|
|
1340
|
-
const orderedIds = [];
|
|
1341
|
-
if (!preferAlternative) {
|
|
1342
|
-
orderedIds.push(currentFramework);
|
|
1343
|
-
}
|
|
1344
|
-
orderedIds.push(preferredFrameworkId);
|
|
1345
|
-
for (const framework of healthy) {
|
|
1346
|
-
orderedIds.push(framework.id);
|
|
1347
|
-
}
|
|
1348
|
-
const seen = new Set();
|
|
1349
|
-
const candidates = [];
|
|
1350
|
-
for (const id of orderedIds) {
|
|
1351
|
-
if (seen.has(id)) {
|
|
1352
|
-
continue;
|
|
1353
|
-
}
|
|
1354
|
-
seen.add(id);
|
|
1355
|
-
if (preferAlternative && id === currentFramework) {
|
|
1356
|
-
continue;
|
|
1357
|
-
}
|
|
1358
|
-
const framework = byId.get(id);
|
|
1359
|
-
if (framework) {
|
|
1360
|
-
candidates.push(framework);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
return candidates;
|
|
1364
|
-
}
|
|
1365
|
-
shouldPreferAlternativeFrameworkForError(reason) {
|
|
1366
|
-
return ALTERNATE_FRAMEWORK_ERROR_RE.test(reason);
|
|
1367
|
-
}
|
|
1368
|
-
formatFailoverPrompt(taskCtx, failedFramework, reason, recentOutput) {
|
|
1369
|
-
const cleanedOutput = cleanForFailoverContext(recentOutput, taskCtx.workdir);
|
|
1370
|
-
const trimmedOutput = cleanedOutput.trim();
|
|
1371
|
-
const clippedOutput = trimmedOutput.length > FAILOVER_OUTPUT_MAX_CHARS
|
|
1372
|
-
? trimmedOutput.slice(-FAILOVER_OUTPUT_MAX_CHARS)
|
|
1373
|
-
: trimmedOutput;
|
|
1374
|
-
const recentDecisions = taskCtx.decisions
|
|
1375
|
-
.slice(-5)
|
|
1376
|
-
.map((decision, index) => `${index + 1}. ${decision.event}: ${decision.reasoning}${decision.response ? ` (response: ${decision.response})` : ""}`)
|
|
1377
|
-
.join("\n");
|
|
1378
|
-
return [
|
|
1379
|
-
`Continue an in-progress task after the previous ${failedFramework} session became unavailable because of a quota or credit failure.`,
|
|
1380
|
-
"",
|
|
1381
|
-
"Original task:",
|
|
1382
|
-
taskCtx.originalTask,
|
|
1383
|
-
"",
|
|
1384
|
-
`Failure reason: ${reason}`,
|
|
1385
|
-
`Workspace: ${taskCtx.workdir}`,
|
|
1386
|
-
"",
|
|
1387
|
-
recentDecisions
|
|
1388
|
-
? `Recent coordinator decisions:\n${recentDecisions}\n`
|
|
1389
|
-
: "",
|
|
1390
|
-
clippedOutput
|
|
1391
|
-
? `Recent terminal output from the failed session:\n${clippedOutput}\n`
|
|
1392
|
-
: "",
|
|
1393
|
-
"Use the existing workspace state instead of starting from scratch. Inspect the files, continue the task, run the needed validation, and then report what changed and how you verified it.",
|
|
1394
|
-
]
|
|
1395
|
-
.filter(Boolean)
|
|
1396
|
-
.join("\n");
|
|
1397
|
-
}
|
|
1398
|
-
formatErrorRecoveryPrompt(taskCtx, recoveryFramework, reason, recentOutput) {
|
|
1399
|
-
const cleanedOutput = cleanForFailoverContext(recentOutput, taskCtx.workdir);
|
|
1400
|
-
const trimmedOutput = cleanedOutput.trim();
|
|
1401
|
-
const clippedOutput = trimmedOutput.length > FAILOVER_OUTPUT_MAX_CHARS
|
|
1402
|
-
? trimmedOutput.slice(-FAILOVER_OUTPUT_MAX_CHARS)
|
|
1403
|
-
: trimmedOutput;
|
|
1404
|
-
const recentDecisions = taskCtx.decisions
|
|
1405
|
-
.slice(-5)
|
|
1406
|
-
.map((decision, index) => `${index + 1}. ${decision.event}: ${decision.reasoning}${decision.response ? ` (response: ${decision.response})` : ""}`)
|
|
1407
|
-
.join("\n");
|
|
1408
|
-
const recoveryMode = recoveryFramework === taskCtx.agentType
|
|
1409
|
-
? `a fresh ${recoveryFramework} session`
|
|
1410
|
-
: `a ${recoveryFramework} recovery session`;
|
|
1411
|
-
return [
|
|
1412
|
-
`Continue an in-progress task after the previous session terminated unexpectedly. Eliza started ${recoveryMode} for recovery.`,
|
|
1413
|
-
"",
|
|
1414
|
-
"Original task:",
|
|
1415
|
-
taskCtx.originalTask,
|
|
1416
|
-
"",
|
|
1417
|
-
`Failure reason: ${reason}`,
|
|
1418
|
-
`Workspace: ${taskCtx.workdir}`,
|
|
1419
|
-
"",
|
|
1420
|
-
recentDecisions
|
|
1421
|
-
? `Recent coordinator decisions:\n${recentDecisions}\n`
|
|
1422
|
-
: "",
|
|
1423
|
-
clippedOutput
|
|
1424
|
-
? `Recent terminal output from the failed session:\n${clippedOutput}\n`
|
|
1425
|
-
: "",
|
|
1426
|
-
"Use the existing workspace state instead of starting over. Inspect the current files, recover from the failure, continue the task, run the needed validation, and then report exactly what changed and how you verified it.",
|
|
1427
|
-
]
|
|
1428
|
-
.filter(Boolean)
|
|
1429
|
-
.join("\n");
|
|
1430
|
-
}
|
|
1431
|
-
async handleFrameworkDepletion(taskCtx, sessionId, reason) {
|
|
1432
|
-
if (!this.isAutomaticFailoverFramework(taskCtx.agentType) ||
|
|
1433
|
-
!isUsageExhaustedTaskAgentError(reason)) {
|
|
1434
|
-
return null;
|
|
1435
|
-
}
|
|
1436
|
-
markTaskAgentFrameworkUnavailable(taskCtx.agentType, reason);
|
|
1437
|
-
await this.taskRegistry.appendEvent({
|
|
1438
|
-
threadId: taskCtx.threadId,
|
|
1439
|
-
sessionId,
|
|
1440
|
-
eventType: "framework_unavailable",
|
|
1441
|
-
summary: `${taskCtx.agentType} temporarily disabled after provider depletion`,
|
|
1442
|
-
data: {
|
|
1443
|
-
framework: taskCtx.agentType,
|
|
1444
|
-
reason,
|
|
1445
|
-
},
|
|
1446
|
-
});
|
|
1447
|
-
let failoverResult = null;
|
|
1448
|
-
try {
|
|
1449
|
-
failoverResult = await this.attemptTaskFailover(taskCtx, reason);
|
|
1450
|
-
}
|
|
1451
|
-
catch (failoverError) {
|
|
1452
|
-
this.log(`Automatic failover failed for "${taskCtx.label}": ${failoverError instanceof Error ? failoverError.message : String(failoverError)}`);
|
|
1453
|
-
}
|
|
1454
|
-
if (failoverResult) {
|
|
1455
|
-
this.sendChatMessage(`"${taskCtx.label}" ran into a ${taskCtx.agentType} quota/credit failure. Eliza is continuing the same task on ${failoverResult.replacementFramework}.`, "coding-agent");
|
|
1456
|
-
}
|
|
1457
|
-
else {
|
|
1458
|
-
this.sendChatMessage(`"${taskCtx.label}" ran into a ${taskCtx.agentType} quota/credit failure. Eliza will prefer another task-agent framework until ${taskCtx.agentType} is healthy again.`, "coding-agent");
|
|
1459
|
-
}
|
|
1460
|
-
return failoverResult;
|
|
1461
|
-
}
|
|
1462
|
-
async attemptTaskFailover(taskCtx, errorMsg) {
|
|
1463
|
-
if (!this.ptyService ||
|
|
1464
|
-
!this.isAutomaticFailoverFramework(taskCtx.agentType)) {
|
|
1465
|
-
return null;
|
|
1466
|
-
}
|
|
1467
|
-
const frameworkState = await this.ptyService.getFrameworkState();
|
|
1468
|
-
const candidates = this.getFailoverCandidates(frameworkState.frameworks, taskCtx.agentType, frameworkState.preferred.id);
|
|
1469
|
-
const nextFramework = candidates[0];
|
|
1470
|
-
if (!nextFramework) {
|
|
1471
|
-
return null;
|
|
1472
|
-
}
|
|
1473
|
-
const failedSession = this.ptyService.getSession(taskCtx.sessionId);
|
|
1474
|
-
const priorMetadata = failedSession?.metadata &&
|
|
1475
|
-
typeof failedSession.metadata === "object" &&
|
|
1476
|
-
!Array.isArray(failedSession.metadata)
|
|
1477
|
-
? failedSession.metadata
|
|
1478
|
-
: {};
|
|
1479
|
-
const failoverOrdinal = typeof priorMetadata.failoverOrdinal === "number"
|
|
1480
|
-
? priorMetadata.failoverOrdinal + 1
|
|
1481
|
-
: 1;
|
|
1482
|
-
const priorOutput = await Promise.race([
|
|
1483
|
-
this.ptyService.getSessionOutput(taskCtx.sessionId, 200),
|
|
1484
|
-
new Promise((resolve) => setTimeout(() => resolve(""), 5_000)),
|
|
1485
|
-
]);
|
|
1486
|
-
const replacementLabel = `${taskCtx.label} (${nextFramework.id} failover ${failoverOrdinal})`;
|
|
1487
|
-
const replacementSession = await this.ptyService.spawnSession({
|
|
1488
|
-
name: failedSession?.name ??
|
|
1489
|
-
`task-failover-${Date.now()}-${nextFramework.id}`,
|
|
1490
|
-
agentType: nextFramework.id,
|
|
1491
|
-
workdir: taskCtx.workdir,
|
|
1492
|
-
initialTask: this.formatFailoverPrompt(taskCtx, taskCtx.agentType, errorMsg, priorOutput),
|
|
1493
|
-
approvalPreset: this.ptyService.defaultApprovalPreset,
|
|
1494
|
-
skipAdapterAutoResponse: true,
|
|
1495
|
-
metadata: {
|
|
1496
|
-
...priorMetadata,
|
|
1497
|
-
threadId: taskCtx.threadId,
|
|
1498
|
-
requestedType: nextFramework.id,
|
|
1499
|
-
label: replacementLabel,
|
|
1500
|
-
failoverOrdinal,
|
|
1501
|
-
failoverFromFramework: taskCtx.agentType,
|
|
1502
|
-
failoverFromSessionId: taskCtx.sessionId,
|
|
1503
|
-
failoverReason: errorMsg,
|
|
1504
|
-
failoverAt: Date.now(),
|
|
1505
|
-
},
|
|
1506
|
-
});
|
|
1507
|
-
await this.registerTask(replacementSession.id, {
|
|
1508
|
-
threadId: taskCtx.threadId,
|
|
1509
|
-
taskNodeId: taskCtx.taskNodeId,
|
|
1510
|
-
agentType: nextFramework.id,
|
|
1511
|
-
label: replacementLabel,
|
|
1512
|
-
originalTask: taskCtx.originalTask,
|
|
1513
|
-
workdir: taskCtx.workdir,
|
|
1514
|
-
repo: taskCtx.repo,
|
|
1515
|
-
providerSource: inferProviderSource(nextFramework),
|
|
1516
|
-
metadata: replacementSession.metadata &&
|
|
1517
|
-
typeof replacementSession.metadata === "object" &&
|
|
1518
|
-
!Array.isArray(replacementSession.metadata)
|
|
1519
|
-
? replacementSession.metadata
|
|
1520
|
-
: undefined,
|
|
1521
|
-
});
|
|
1522
|
-
await this.taskRegistry.appendEvent({
|
|
1523
|
-
threadId: taskCtx.threadId,
|
|
1524
|
-
sessionId: replacementSession.id,
|
|
1525
|
-
eventType: "framework_failover_started",
|
|
1526
|
-
summary: `Continuing "${taskCtx.label}" on ${nextFramework.label}`,
|
|
1527
|
-
data: {
|
|
1528
|
-
fromFramework: taskCtx.agentType,
|
|
1529
|
-
fromSessionId: taskCtx.sessionId,
|
|
1530
|
-
toFramework: nextFramework.id,
|
|
1531
|
-
toSessionId: replacementSession.id,
|
|
1532
|
-
reason: errorMsg,
|
|
1533
|
-
},
|
|
1534
|
-
});
|
|
1535
|
-
return {
|
|
1536
|
-
replacementSessionId: replacementSession.id,
|
|
1537
|
-
replacementFramework: nextFramework.id,
|
|
1538
|
-
replacementLabel,
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
async attemptTaskRecovery(taskCtx, errorMsg) {
|
|
1542
|
-
if (!this.ptyService) {
|
|
1543
|
-
return null;
|
|
1544
|
-
}
|
|
1545
|
-
const failedSession = this.ptyService.getSession(taskCtx.sessionId);
|
|
1546
|
-
const priorMetadata = failedSession?.metadata &&
|
|
1547
|
-
typeof failedSession.metadata === "object" &&
|
|
1548
|
-
!Array.isArray(failedSession.metadata)
|
|
1549
|
-
? failedSession.metadata
|
|
1550
|
-
: {};
|
|
1551
|
-
const recoveryOrdinal = typeof priorMetadata.recoveryOrdinal === "number"
|
|
1552
|
-
? priorMetadata.recoveryOrdinal + 1
|
|
1553
|
-
: 1;
|
|
1554
|
-
if (recoveryOrdinal > MAX_AUTOMATIC_ERROR_RECOVERIES) {
|
|
1555
|
-
return null;
|
|
1556
|
-
}
|
|
1557
|
-
let recoveryFramework = taskCtx.agentType;
|
|
1558
|
-
let recoveryAvailability = null;
|
|
1559
|
-
if (this.isAutomaticFailoverFramework(taskCtx.agentType)) {
|
|
1560
|
-
const frameworkState = await this.ptyService.getFrameworkState();
|
|
1561
|
-
const candidates = this.getRecoveryCandidates(frameworkState.frameworks, taskCtx.agentType, frameworkState.preferred.id, this.shouldPreferAlternativeFrameworkForError(errorMsg));
|
|
1562
|
-
const selected = candidates[0];
|
|
1563
|
-
if (!selected) {
|
|
1564
|
-
return null;
|
|
1565
|
-
}
|
|
1566
|
-
recoveryFramework = selected.id;
|
|
1567
|
-
recoveryAvailability = selected;
|
|
1568
|
-
}
|
|
1569
|
-
const priorOutput = await Promise.race([
|
|
1570
|
-
this.ptyService.getSessionOutput(taskCtx.sessionId, 200),
|
|
1571
|
-
new Promise((resolve) => setTimeout(() => resolve(""), 5_000)),
|
|
1572
|
-
]);
|
|
1573
|
-
const replacementLabel = `${taskCtx.label} (${recoveryFramework} recovery ${recoveryOrdinal})`;
|
|
1574
|
-
const replacementSession = await this.ptyService.spawnSession({
|
|
1575
|
-
name: failedSession?.name ??
|
|
1576
|
-
`task-recovery-${Date.now()}-${recoveryFramework}`,
|
|
1577
|
-
agentType: recoveryFramework,
|
|
1578
|
-
workdir: taskCtx.workdir,
|
|
1579
|
-
initialTask: this.formatErrorRecoveryPrompt(taskCtx, recoveryFramework, errorMsg, priorOutput),
|
|
1580
|
-
credentials: buildAgentCredentials(this.runtime),
|
|
1581
|
-
approvalPreset: this.ptyService.defaultApprovalPreset,
|
|
1582
|
-
skipAdapterAutoResponse: true,
|
|
1583
|
-
metadata: {
|
|
1584
|
-
...priorMetadata,
|
|
1585
|
-
threadId: taskCtx.threadId,
|
|
1586
|
-
requestedType: recoveryFramework,
|
|
1587
|
-
label: replacementLabel,
|
|
1588
|
-
recoveryOrdinal,
|
|
1589
|
-
recoveredFromFramework: taskCtx.agentType,
|
|
1590
|
-
recoveredFromSessionId: taskCtx.sessionId,
|
|
1591
|
-
recoveryReason: errorMsg,
|
|
1592
|
-
recoveryAt: Date.now(),
|
|
1593
|
-
},
|
|
1594
|
-
});
|
|
1595
|
-
await this.registerTask(replacementSession.id, {
|
|
1596
|
-
threadId: taskCtx.threadId,
|
|
1597
|
-
taskNodeId: taskCtx.taskNodeId,
|
|
1598
|
-
agentType: recoveryFramework,
|
|
1599
|
-
label: replacementLabel,
|
|
1600
|
-
originalTask: taskCtx.originalTask,
|
|
1601
|
-
workdir: taskCtx.workdir,
|
|
1602
|
-
repo: taskCtx.repo,
|
|
1603
|
-
providerSource: recoveryAvailability
|
|
1604
|
-
? inferProviderSource(recoveryAvailability)
|
|
1605
|
-
: null,
|
|
1606
|
-
metadata: replacementSession.metadata &&
|
|
1607
|
-
typeof replacementSession.metadata === "object" &&
|
|
1608
|
-
!Array.isArray(replacementSession.metadata)
|
|
1609
|
-
? replacementSession.metadata
|
|
1610
|
-
: undefined,
|
|
1611
|
-
});
|
|
1612
|
-
await this.taskRegistry.appendEvent({
|
|
1613
|
-
threadId: taskCtx.threadId,
|
|
1614
|
-
sessionId: replacementSession.id,
|
|
1615
|
-
eventType: "task_error_recovery_started",
|
|
1616
|
-
summary: `Continuing "${taskCtx.label}" after an agent error`,
|
|
1617
|
-
data: {
|
|
1618
|
-
fromFramework: taskCtx.agentType,
|
|
1619
|
-
fromSessionId: taskCtx.sessionId,
|
|
1620
|
-
toFramework: recoveryFramework,
|
|
1621
|
-
toSessionId: replacementSession.id,
|
|
1622
|
-
reason: errorMsg,
|
|
1623
|
-
recoveryOrdinal,
|
|
1624
|
-
},
|
|
1625
|
-
});
|
|
1626
|
-
this.broadcast({
|
|
1627
|
-
type: "task_recovery_started",
|
|
1628
|
-
sessionId: replacementSession.id,
|
|
1629
|
-
timestamp: Date.now(),
|
|
1630
|
-
data: {
|
|
1631
|
-
fromSessionId: taskCtx.sessionId,
|
|
1632
|
-
fromFramework: taskCtx.agentType,
|
|
1633
|
-
toFramework: recoveryFramework,
|
|
1634
|
-
reason: errorMsg,
|
|
1635
|
-
},
|
|
1636
|
-
});
|
|
1637
|
-
return {
|
|
1638
|
-
replacementSessionId: replacementSession.id,
|
|
1639
|
-
replacementFramework: recoveryFramework,
|
|
1640
|
-
replacementLabel,
|
|
1641
|
-
};
|
|
1642
|
-
}
|
|
1643
|
-
async resumeTaskAfterProviderAuth(sessionId, reason) {
|
|
1644
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
1645
|
-
if (!taskCtx) {
|
|
1646
|
-
return null;
|
|
1647
|
-
}
|
|
1648
|
-
if (taskCtx.status === "completed" ||
|
|
1649
|
-
taskCtx.status === "error" ||
|
|
1650
|
-
taskCtx.status === "stopped") {
|
|
1651
|
-
return null;
|
|
1652
|
-
}
|
|
1653
|
-
const replacement = await this.attemptTaskRecovery(taskCtx, reason);
|
|
1654
|
-
if (!replacement) {
|
|
1655
|
-
return null;
|
|
1656
|
-
}
|
|
1657
|
-
taskCtx.suppressStopNotice = true;
|
|
1658
|
-
taskCtx.status = "stopped";
|
|
1659
|
-
try {
|
|
1660
|
-
await this.ptyService?.stopSession(sessionId, true);
|
|
1661
|
-
}
|
|
1662
|
-
catch (error) {
|
|
1663
|
-
this.log(`Failed to stop superseded auth-blocked session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1664
|
-
}
|
|
1665
|
-
this.sendChatMessage(`"${taskCtx.label}" recovered after provider authentication and is continuing on ${replacement.replacementFramework}.`, "coding-agent");
|
|
1666
|
-
return replacement;
|
|
1667
|
-
}
|
|
1668
|
-
async markTaskResumedAfterProviderAuth(sessionId) {
|
|
1669
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
1670
|
-
if (!taskCtx) {
|
|
1671
|
-
return false;
|
|
1672
|
-
}
|
|
1673
|
-
if (taskCtx.status === "completed" ||
|
|
1674
|
-
taskCtx.status === "error" ||
|
|
1675
|
-
taskCtx.status === "stopped") {
|
|
1676
|
-
return false;
|
|
1677
|
-
}
|
|
1678
|
-
taskCtx.status = "active";
|
|
1679
|
-
taskCtx.stoppedAt = undefined;
|
|
1680
|
-
taskCtx.lastActivityAt = Date.now();
|
|
1681
|
-
taskCtx.idleCheckCount = 0;
|
|
1682
|
-
if (taskCtx.agentType === "claude" ||
|
|
1683
|
-
taskCtx.agentType === "codex" ||
|
|
1684
|
-
taskCtx.agentType === "gemini" ||
|
|
1685
|
-
taskCtx.agentType === "aider") {
|
|
1686
|
-
markTaskAgentFrameworkHealthy(taskCtx.agentType);
|
|
1687
|
-
}
|
|
1688
|
-
this.broadcast({
|
|
1689
|
-
type: "ready",
|
|
1690
|
-
sessionId,
|
|
1691
|
-
timestamp: Date.now(),
|
|
1692
|
-
data: {
|
|
1693
|
-
reason: "provider_auth_recovered",
|
|
1694
|
-
source: "auth_recovery",
|
|
1695
|
-
},
|
|
1696
|
-
});
|
|
1697
|
-
await this.taskRegistry.appendEvent({
|
|
1698
|
-
threadId: taskCtx.threadId,
|
|
1699
|
-
sessionId,
|
|
1700
|
-
eventType: "task_status_changed",
|
|
1701
|
-
summary: `Task "${taskCtx.label}" resumed after provider authentication`,
|
|
1702
|
-
data: {
|
|
1703
|
-
status: "active",
|
|
1704
|
-
reason: "provider_auth_recovered",
|
|
1705
|
-
},
|
|
1706
|
-
});
|
|
1707
|
-
this.sendChatMessage(`"${taskCtx.label}" refreshed provider authentication and is continuing automatically.`, "coding-agent");
|
|
1708
|
-
return true;
|
|
1709
|
-
}
|
|
1710
|
-
async recordDecision(taskCtx, decision) {
|
|
1711
|
-
taskCtx.decisions.push(decision);
|
|
1712
|
-
await this.taskRegistry.recordDecision({
|
|
1713
|
-
threadId: taskCtx.threadId,
|
|
1714
|
-
sessionId: taskCtx.sessionId,
|
|
1715
|
-
timestamp: decision.timestamp,
|
|
1716
|
-
event: decision.event,
|
|
1717
|
-
promptText: decision.promptText,
|
|
1718
|
-
decision: decision.decision,
|
|
1719
|
-
response: decision.response,
|
|
1720
|
-
reasoning: decision.reasoning,
|
|
1721
|
-
});
|
|
1722
|
-
await this.syncTaskContext(taskCtx);
|
|
1723
|
-
}
|
|
1724
|
-
async setTaskDelivered(sessionId) {
|
|
1725
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
1726
|
-
if (!taskCtx)
|
|
1727
|
-
return;
|
|
1728
|
-
taskCtx.taskDelivered = true;
|
|
1729
|
-
await this.syncTaskContext(taskCtx);
|
|
1730
|
-
}
|
|
1731
|
-
// ─── Unregistered Buffer Retry ───
|
|
1732
|
-
/**
|
|
1733
|
-
* Schedule a retry check for buffered events from an unregistered session.
|
|
1734
|
-
* Uses exponential backoff: 2s → 4s → 8s → 16s, max 30s total.
|
|
1735
|
-
*/
|
|
1736
|
-
scheduleUnregisteredRetry(sessionId, attempt) {
|
|
1737
|
-
const delay = UNREGISTERED_RETRY_DELAYS[Math.min(attempt, UNREGISTERED_RETRY_DELAYS.length - 1)];
|
|
1738
|
-
const timer = setTimeout(() => {
|
|
1739
|
-
this.unregisteredRetryTimers.delete(sessionId);
|
|
1740
|
-
const stillBuffered = this.unregisteredBuffer.get(sessionId);
|
|
1741
|
-
if (!stillBuffered || stillBuffered.length === 0)
|
|
1742
|
-
return;
|
|
1743
|
-
const ctx = this.tasks.get(sessionId);
|
|
1744
|
-
if (ctx) {
|
|
1745
|
-
// Task was registered — flush
|
|
1746
|
-
this.unregisteredBuffer.delete(sessionId);
|
|
1747
|
-
for (const entry of stillBuffered) {
|
|
1748
|
-
this.handleNormalizedSessionEvent(entry.normalized).catch((err) => {
|
|
1749
|
-
this.log(`Failed to replay buffered event for ${sessionId}: ${err}`);
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
// Check if we've exceeded the absolute max wait
|
|
1755
|
-
const oldest = stillBuffered[0].receivedAt;
|
|
1756
|
-
const totalElapsed = Date.now() - oldest;
|
|
1757
|
-
if (totalElapsed >= UNREGISTERED_MAX_TOTAL_MS) {
|
|
1758
|
-
this.unregisteredBuffer.delete(sessionId);
|
|
1759
|
-
this.log(`Discarding ${stillBuffered.length} buffered events for unregistered session ${sessionId} after ${Math.round(totalElapsed / 1000)}s`);
|
|
1760
|
-
return;
|
|
1761
|
-
}
|
|
1762
|
-
// Schedule next retry
|
|
1763
|
-
this.log(`Retry ${attempt + 1} for unregistered session ${sessionId} (next in ${delay}ms)`);
|
|
1764
|
-
this.scheduleUnregisteredRetry(sessionId, attempt + 1);
|
|
1765
|
-
}, delay);
|
|
1766
|
-
this.unregisteredRetryTimers.set(sessionId, timer);
|
|
1767
|
-
}
|
|
1768
|
-
// ─── SSE Client Management ───
|
|
1769
|
-
/**
|
|
1770
|
-
* Register an SSE client. Returns an unsubscribe function.
|
|
1771
|
-
* Sends a snapshot of current state on connect.
|
|
1772
|
-
*/
|
|
1773
|
-
addSseClient(res) {
|
|
1774
|
-
this.sseClients.add(res);
|
|
1775
|
-
// Send snapshot on connect
|
|
1776
|
-
const snapshot = {
|
|
1777
|
-
type: "snapshot",
|
|
1778
|
-
sessionId: "*",
|
|
1779
|
-
timestamp: Date.now(),
|
|
1780
|
-
data: {
|
|
1781
|
-
tasks: this.getAllTaskContexts(),
|
|
1782
|
-
supervisionLevel: this.supervisionLevel,
|
|
1783
|
-
pendingCount: this.pendingDecisions.size,
|
|
1784
|
-
},
|
|
1785
|
-
};
|
|
1786
|
-
this.writeSseEvent(res, snapshot);
|
|
1787
|
-
// Remove on close
|
|
1788
|
-
const cleanup = () => {
|
|
1789
|
-
this.sseClients.delete(res);
|
|
1790
|
-
};
|
|
1791
|
-
res.on("close", cleanup);
|
|
1792
|
-
return cleanup;
|
|
1793
|
-
}
|
|
1794
|
-
broadcast(event) {
|
|
1795
|
-
const dead = [];
|
|
1796
|
-
for (const client of this.sseClients) {
|
|
1797
|
-
if (client.writableEnded) {
|
|
1798
|
-
dead.push(client);
|
|
1799
|
-
continue;
|
|
1800
|
-
}
|
|
1801
|
-
this.writeSseEvent(client, event);
|
|
1802
|
-
}
|
|
1803
|
-
// Cleanup dead connections
|
|
1804
|
-
for (const d of dead) {
|
|
1805
|
-
this.sseClients.delete(d);
|
|
1806
|
-
}
|
|
1807
|
-
// Relay to WebSocket clients — buffer if bridge isn't wired yet
|
|
1808
|
-
if (this.wsBroadcast) {
|
|
1809
|
-
this.wsBroadcast(event);
|
|
1810
|
-
}
|
|
1811
|
-
else if (this.preBridgeBroadcastBuffer.length < MAX_PRE_BRIDGE_BUFFER) {
|
|
1812
|
-
this.preBridgeBroadcastBuffer.push(event);
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
writeSseEvent(res, event) {
|
|
1816
|
-
try {
|
|
1817
|
-
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
1818
|
-
}
|
|
1819
|
-
catch {
|
|
1820
|
-
// Connection may have closed
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
// ─── Event Handling ───
|
|
1824
|
-
async handleSessionEvent(sessionId, event, data) {
|
|
1825
|
-
const normalized = normalizeCoordinatorEvent(sessionId, event, data);
|
|
1826
|
-
if (!normalized) {
|
|
1827
|
-
this.broadcast({
|
|
1828
|
-
type: event,
|
|
1829
|
-
sessionId,
|
|
1830
|
-
timestamp: Date.now(),
|
|
1831
|
-
data,
|
|
1832
|
-
});
|
|
1833
|
-
return;
|
|
1834
|
-
}
|
|
1835
|
-
await this.handleNormalizedSessionEvent(normalized);
|
|
1836
|
-
}
|
|
1837
|
-
async handleNormalizedSessionEvent(normalized) {
|
|
1838
|
-
const sessionId = normalized.sessionId;
|
|
1839
|
-
const event = normalized.name;
|
|
1840
|
-
const data = normalized.rawData;
|
|
1841
|
-
// Lazy-wire scratch decision callback if not yet connected
|
|
1842
|
-
if (!this.scratchDecisionWired) {
|
|
1843
|
-
this.wireScratchDecisionCallback();
|
|
1844
|
-
}
|
|
1845
|
-
// Ignore events from sessions created before this coordinator started.
|
|
1846
|
-
// Session IDs are formatted as "pty-{timestamp}-{hex}" — extract the timestamp.
|
|
1847
|
-
const tsMatch = sessionId.match(/^pty-(\d+)-/);
|
|
1848
|
-
if (tsMatch) {
|
|
1849
|
-
const sessionCreatedAt = Number(tsMatch[1]);
|
|
1850
|
-
if (sessionCreatedAt < this.startedAt - 60_000) {
|
|
1851
|
-
// Session is from before this coordinator's lifetime (with 1min grace)
|
|
1852
|
-
return;
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
1856
|
-
// Buffer events for unregistered sessions with exponential backoff retry.
|
|
1857
|
-
// Events arriving before registerTask() are buffered and retried at
|
|
1858
|
-
// 2s → 4s → 8s → 16s intervals (max 30s total) before being discarded.
|
|
1859
|
-
if (!taskCtx) {
|
|
1860
|
-
if (event === "blocked" ||
|
|
1861
|
-
event === "task_complete" ||
|
|
1862
|
-
event === "error") {
|
|
1863
|
-
let buffer = this.unregisteredBuffer.get(sessionId);
|
|
1864
|
-
if (!buffer) {
|
|
1865
|
-
buffer = [];
|
|
1866
|
-
this.unregisteredBuffer.set(sessionId, buffer);
|
|
1867
|
-
}
|
|
1868
|
-
buffer.push({ normalized, receivedAt: Date.now() });
|
|
1869
|
-
// Only schedule retry if not already retrying for this session
|
|
1870
|
-
if (!this.unregisteredRetryTimers.has(sessionId)) {
|
|
1871
|
-
this.scheduleUnregisteredRetry(sessionId, 0);
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
return;
|
|
1875
|
-
}
|
|
1876
|
-
// Skip decision-making events for terminal states, but always allow
|
|
1877
|
-
// "stopped" and "error" through — they're definitive lifecycle signals
|
|
1878
|
-
// that the frontend needs to close consoles and clean up.
|
|
1879
|
-
// Exception: allow a late "task_complete" to recover a recently-stopped task.
|
|
1880
|
-
let recoveredFromStopped = false;
|
|
1881
|
-
if (taskCtx.status === "stopped" ||
|
|
1882
|
-
taskCtx.status === "error" ||
|
|
1883
|
-
taskCtx.status === "completed") {
|
|
1884
|
-
if (taskCtx.status === "stopped" && event === "task_complete") {
|
|
1885
|
-
const stoppedAt = taskCtx.stoppedAt ?? 0;
|
|
1886
|
-
const ageMs = Date.now() - stoppedAt;
|
|
1887
|
-
if (stoppedAt > 0 && ageMs <= STOPPED_RECOVERY_WINDOW_MS) {
|
|
1888
|
-
this.log(`Recovering "${taskCtx.label}" from stopped on late task_complete (${Math.round(ageMs / 1000)}s old)`);
|
|
1889
|
-
taskCtx.status = "active";
|
|
1890
|
-
taskCtx.stoppedAt = undefined;
|
|
1891
|
-
recoveredFromStopped = true;
|
|
1892
|
-
}
|
|
1893
|
-
else {
|
|
1894
|
-
this.log(`Ignoring "${event}" for ${taskCtx.label} (status: stopped, age=${Math.round(ageMs / 1000)}s)`);
|
|
1895
|
-
return;
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
if (!recoveredFromStopped && event !== "stopped" && event !== "error") {
|
|
1899
|
-
this.log(`Ignoring "${event}" for ${taskCtx.label} (status: ${taskCtx.status})`);
|
|
1900
|
-
return;
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
// Update activity timestamp — resets idle watchdog for this session.
|
|
1904
|
-
// This runs before buffering so buffered events still reset the idle timer.
|
|
1905
|
-
taskCtx.lastActivityAt = Date.now();
|
|
1906
|
-
taskCtx.idleCheckCount = 0;
|
|
1907
|
-
// Buffer decision-making events when paused (user sent a chat message).
|
|
1908
|
-
// Auto-responses still flow through handleBlocked — only LLM decisions are deferred.
|
|
1909
|
-
if (this._paused && (event === "blocked" || event === "task_complete")) {
|
|
1910
|
-
// Auto-responded blocked events don't need LLM — let them through
|
|
1911
|
-
const blockedAutoResponded = event === "blocked" &&
|
|
1912
|
-
normalized.autoResponded === true;
|
|
1913
|
-
if (!blockedAutoResponded) {
|
|
1914
|
-
// Broadcast buffered state for dashboard visibility
|
|
1915
|
-
this.broadcast({
|
|
1916
|
-
type: event === "blocked" ? "blocked_buffered" : "turn_complete_buffered",
|
|
1917
|
-
sessionId,
|
|
1918
|
-
timestamp: Date.now(),
|
|
1919
|
-
data,
|
|
1920
|
-
});
|
|
1921
|
-
this.pauseBuffer.push(normalized);
|
|
1922
|
-
this.log(`Buffered "${event}" for ${taskCtx.label} (coordinator paused)`);
|
|
1923
|
-
return;
|
|
1924
|
-
}
|
|
1925
|
-
// Auto-responded: fall through to normal handling below
|
|
1926
|
-
}
|
|
1927
|
-
// Route by event type
|
|
1928
|
-
switch (event) {
|
|
1929
|
-
case "blocked": {
|
|
1930
|
-
const blockedEvent = normalized;
|
|
1931
|
-
const blockedPrompt = blockedEvent.promptText;
|
|
1932
|
-
if (this.isAutomaticFailoverFramework(taskCtx.agentType) &&
|
|
1933
|
-
isUsageExhaustedTaskAgentError(blockedPrompt)) {
|
|
1934
|
-
const failoverResult = await this.handleFrameworkDepletion(taskCtx, sessionId, blockedPrompt);
|
|
1935
|
-
taskCtx.status = "error";
|
|
1936
|
-
taskCtx.stoppedAt = Date.now();
|
|
1937
|
-
this.broadcast({
|
|
1938
|
-
type: "error",
|
|
1939
|
-
sessionId,
|
|
1940
|
-
timestamp: Date.now(),
|
|
1941
|
-
data: {
|
|
1942
|
-
message: blockedPrompt,
|
|
1943
|
-
source: "blocked_prompt",
|
|
1944
|
-
},
|
|
1945
|
-
});
|
|
1946
|
-
await this.taskRegistry.appendEvent({
|
|
1947
|
-
threadId: taskCtx.threadId,
|
|
1948
|
-
sessionId,
|
|
1949
|
-
eventType: "task_status_changed",
|
|
1950
|
-
summary: `Task "${taskCtx.label}" errored`,
|
|
1951
|
-
data: {
|
|
1952
|
-
status: "error",
|
|
1953
|
-
message: blockedPrompt,
|
|
1954
|
-
source: "blocked_prompt",
|
|
1955
|
-
},
|
|
1956
|
-
});
|
|
1957
|
-
this.ptyService?.stopSession(sessionId, true).catch((err) => {
|
|
1958
|
-
this.log(`Failed to stop exhausted session "${taskCtx.label}": ${err}`);
|
|
1959
|
-
});
|
|
1960
|
-
if (!failoverResult) {
|
|
1961
|
-
checkAllTasksComplete(this);
|
|
1962
|
-
}
|
|
1963
|
-
break;
|
|
1964
|
-
}
|
|
1965
|
-
await handleBlocked(this, sessionId, taskCtx, data);
|
|
1966
|
-
break;
|
|
1967
|
-
}
|
|
1968
|
-
case "task_complete": {
|
|
1969
|
-
// Broadcast immediately for UI visibility, but coalesce the
|
|
1970
|
-
// expensive LLM assessment — rapid turn-complete events within
|
|
1971
|
-
// 500ms are debounced so only the last one triggers an LLM call.
|
|
1972
|
-
this.broadcast({
|
|
1973
|
-
type: "turn_complete",
|
|
1974
|
-
sessionId,
|
|
1975
|
-
timestamp: Date.now(),
|
|
1976
|
-
data,
|
|
1977
|
-
});
|
|
1978
|
-
const existingCoalesce = this.turnCompleteCoalesceTimers.get(sessionId);
|
|
1979
|
-
if (existingCoalesce)
|
|
1980
|
-
clearTimeout(existingCoalesce);
|
|
1981
|
-
const coalescedData = data;
|
|
1982
|
-
const coalesceTimer = setTimeout(() => {
|
|
1983
|
-
this.turnCompleteCoalesceTimers.delete(sessionId);
|
|
1984
|
-
const currentTask = this.tasks.get(sessionId);
|
|
1985
|
-
// Accept both "active" and "tool_running" as live pre-validation
|
|
1986
|
-
// states. Subagents that use tools (curl, file ops, etc.) sit in
|
|
1987
|
-
// "tool_running" almost continuously, so by the time task_complete
|
|
1988
|
-
// arrives the status is usually "tool_running" — the prior strict
|
|
1989
|
-
// "=== active" check meant validation never ran for tool-heavy
|
|
1990
|
-
// scratch tasks, leaving them stuck and propagating goal failure
|
|
1991
|
-
// through the watchdog.
|
|
1992
|
-
if (currentTask &&
|
|
1993
|
-
(currentTask.status === "active" ||
|
|
1994
|
-
currentTask.status === "tool_running")) {
|
|
1995
|
-
handleTurnComplete(this, sessionId, currentTask, coalescedData).catch((err) => {
|
|
1996
|
-
this.log(`Coalesced turn-complete failed: ${err}`);
|
|
1997
|
-
});
|
|
1998
|
-
}
|
|
1999
|
-
}, TURN_COMPLETE_COALESCE_MS);
|
|
2000
|
-
this.turnCompleteCoalesceTimers.set(sessionId, coalesceTimer);
|
|
2001
|
-
break;
|
|
2002
|
-
}
|
|
2003
|
-
case "error": {
|
|
2004
|
-
this.broadcast({
|
|
2005
|
-
type: "error",
|
|
2006
|
-
sessionId,
|
|
2007
|
-
timestamp: Date.now(),
|
|
2008
|
-
data,
|
|
2009
|
-
});
|
|
2010
|
-
// Send error message to chat UI
|
|
2011
|
-
const errorMsg = data.message ?? "unknown error";
|
|
2012
|
-
const failoverResult = await this.handleFrameworkDepletion(taskCtx, sessionId, errorMsg);
|
|
2013
|
-
let recoveryResult = null;
|
|
2014
|
-
if (!failoverResult) {
|
|
2015
|
-
try {
|
|
2016
|
-
recoveryResult = await this.attemptTaskRecovery(taskCtx, errorMsg);
|
|
2017
|
-
}
|
|
2018
|
-
catch (recoveryError) {
|
|
2019
|
-
this.log(`Automatic error recovery failed for "${taskCtx.label}": ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
if (recoveryResult) {
|
|
2023
|
-
this.sendChatMessage(`"${taskCtx.label}" hit an error: ${errorMsg}. Eliza is continuing the same task on ${recoveryResult.replacementFramework}.`, "coding-agent");
|
|
2024
|
-
}
|
|
2025
|
-
else if (!failoverResult) {
|
|
2026
|
-
this.sendChatMessage(`"${taskCtx.label}" hit an error and needs your attention: ${errorMsg}`, "coding-agent");
|
|
2027
|
-
}
|
|
2028
|
-
taskCtx.status = "error";
|
|
2029
|
-
await this.taskRegistry.appendEvent({
|
|
2030
|
-
threadId: taskCtx.threadId,
|
|
2031
|
-
sessionId,
|
|
2032
|
-
eventType: "task_status_changed",
|
|
2033
|
-
summary: `Task "${taskCtx.label}" errored`,
|
|
2034
|
-
data: { status: "error", message: errorMsg },
|
|
2035
|
-
});
|
|
2036
|
-
if (!failoverResult && !recoveryResult) {
|
|
2037
|
-
checkAllTasksComplete(this);
|
|
2038
|
-
}
|
|
2039
|
-
break;
|
|
2040
|
-
}
|
|
2041
|
-
case "stopped": {
|
|
2042
|
-
const alreadyTerminal = taskCtx.status === "completed" || taskCtx.status === "error";
|
|
2043
|
-
// Don't downgrade "completed" or "error" to "stopped" — the async
|
|
2044
|
-
// stopSession fires after executeDecision already marked the task.
|
|
2045
|
-
if (taskCtx.status !== "completed" && taskCtx.status !== "error") {
|
|
2046
|
-
taskCtx.status = "stopped";
|
|
2047
|
-
taskCtx.stoppedAt = Date.now();
|
|
2048
|
-
}
|
|
2049
|
-
this.inFlightDecisions.delete(sessionId);
|
|
2050
|
-
this.broadcast({
|
|
2051
|
-
type: "stopped",
|
|
2052
|
-
sessionId,
|
|
2053
|
-
timestamp: Date.now(),
|
|
2054
|
-
data,
|
|
2055
|
-
});
|
|
2056
|
-
await this.taskRegistry.appendEvent({
|
|
2057
|
-
threadId: taskCtx.threadId,
|
|
2058
|
-
sessionId,
|
|
2059
|
-
eventType: "task_status_changed",
|
|
2060
|
-
summary: `Task "${taskCtx.label}" stopped`,
|
|
2061
|
-
data: { status: taskCtx.status },
|
|
2062
|
-
});
|
|
2063
|
-
if (!alreadyTerminal && !taskCtx.suppressStopNotice) {
|
|
2064
|
-
this.sendChatMessage(`"${taskCtx.label}" stopped before completion.`, "coding-agent");
|
|
2065
|
-
}
|
|
2066
|
-
checkAllTasksComplete(this);
|
|
2067
|
-
break;
|
|
2068
|
-
}
|
|
2069
|
-
case "ready":
|
|
2070
|
-
taskCtx.status = "active";
|
|
2071
|
-
if (taskCtx.agentType === "claude" ||
|
|
2072
|
-
taskCtx.agentType === "codex" ||
|
|
2073
|
-
taskCtx.agentType === "gemini" ||
|
|
2074
|
-
taskCtx.agentType === "aider") {
|
|
2075
|
-
markTaskAgentFrameworkHealthy(taskCtx.agentType);
|
|
2076
|
-
}
|
|
2077
|
-
this.broadcast({
|
|
2078
|
-
type: "ready",
|
|
2079
|
-
sessionId,
|
|
2080
|
-
timestamp: Date.now(),
|
|
2081
|
-
data,
|
|
2082
|
-
});
|
|
2083
|
-
await this.taskRegistry.appendEvent({
|
|
2084
|
-
threadId: taskCtx.threadId,
|
|
2085
|
-
sessionId,
|
|
2086
|
-
eventType: "session_updated",
|
|
2087
|
-
summary: `Session "${taskCtx.label}" ready`,
|
|
2088
|
-
data: { status: "ready" },
|
|
2089
|
-
});
|
|
2090
|
-
break;
|
|
2091
|
-
case "login_required": {
|
|
2092
|
-
const loginEvent = normalized;
|
|
2093
|
-
let recoveryResult = null;
|
|
2094
|
-
try {
|
|
2095
|
-
if (this.ptyService &&
|
|
2096
|
-
(taskCtx.agentType === "claude" ||
|
|
2097
|
-
taskCtx.agentType === "codex" ||
|
|
2098
|
-
taskCtx.agentType === "gemini" ||
|
|
2099
|
-
taskCtx.agentType === "aider")) {
|
|
2100
|
-
recoveryResult = await this.ptyService.startSessionAuthRecovery(sessionId, taskCtx.agentType, {
|
|
2101
|
-
instructions: loginEvent.instructions,
|
|
2102
|
-
url: loginEvent.url,
|
|
2103
|
-
deviceCode: loginEvent.deviceCode,
|
|
2104
|
-
method: loginEvent.method,
|
|
2105
|
-
promptSnippet: loginEvent.promptSnippet,
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
catch (error) {
|
|
2110
|
-
this.log(`Provider auth recovery failed for "${taskCtx.label}": ${error instanceof Error ? error.message : String(error)}`);
|
|
2111
|
-
}
|
|
2112
|
-
if (recoveryResult?.status === "recovered") {
|
|
2113
|
-
if (recoveryResult.recoveryTarget === "replacement_session") {
|
|
2114
|
-
break;
|
|
2115
|
-
}
|
|
2116
|
-
await this.markTaskResumedAfterProviderAuth(sessionId);
|
|
2117
|
-
break;
|
|
2118
|
-
}
|
|
2119
|
-
taskCtx.status = "blocked";
|
|
2120
|
-
this.broadcast({
|
|
2121
|
-
type: "login_required",
|
|
2122
|
-
sessionId,
|
|
2123
|
-
timestamp: Date.now(),
|
|
2124
|
-
data,
|
|
2125
|
-
});
|
|
2126
|
-
await this.taskRegistry.appendEvent({
|
|
2127
|
-
threadId: taskCtx.threadId,
|
|
2128
|
-
sessionId,
|
|
2129
|
-
eventType: "task_status_changed",
|
|
2130
|
-
summary: `Task "${taskCtx.label}" is waiting for login`,
|
|
2131
|
-
data: {
|
|
2132
|
-
status: "blocked",
|
|
2133
|
-
reason: "login_required",
|
|
2134
|
-
instructions: loginEvent.instructions ?? null,
|
|
2135
|
-
url: loginEvent.url ?? null,
|
|
2136
|
-
deviceCode: loginEvent.deviceCode ?? null,
|
|
2137
|
-
method: loginEvent.method ?? null,
|
|
2138
|
-
recoveryStatus: recoveryResult?.status ?? null,
|
|
2139
|
-
},
|
|
2140
|
-
});
|
|
2141
|
-
const loginParts = [
|
|
2142
|
-
recoveryResult?.status === "recovering"
|
|
2143
|
-
? `"${taskCtx.label}" needs provider authentication, and Eliza has started the recovery flow. It will continue automatically when sign-in completes.`
|
|
2144
|
-
: `"${taskCtx.label}" needs a provider login before it can continue.`,
|
|
2145
|
-
recoveryResult?.instructions?.trim() ||
|
|
2146
|
-
loginEvent.instructions?.trim() ||
|
|
2147
|
-
"",
|
|
2148
|
-
recoveryResult?.deviceCode || loginEvent.deviceCode
|
|
2149
|
-
? `Device code: ${recoveryResult?.deviceCode ?? loginEvent.deviceCode}`
|
|
2150
|
-
: "",
|
|
2151
|
-
recoveryResult?.browserDetail || "",
|
|
2152
|
-
loginEvent.url ? `Login link: ${loginEvent.url}` : "",
|
|
2153
|
-
recoveryResult?.url && recoveryResult.url !== loginEvent.url
|
|
2154
|
-
? `Login link: ${recoveryResult.url}`
|
|
2155
|
-
: "",
|
|
2156
|
-
].filter(Boolean);
|
|
2157
|
-
this.sendChatMessage(loginParts.join(" "), "coding-agent");
|
|
2158
|
-
break;
|
|
2159
|
-
}
|
|
2160
|
-
case "tool_running": {
|
|
2161
|
-
// Agent is actively working via an external tool — keep watchdog happy
|
|
2162
|
-
taskCtx.status = "tool_running";
|
|
2163
|
-
taskCtx.lastActivityAt = Date.now();
|
|
2164
|
-
taskCtx.idleCheckCount = 0;
|
|
2165
|
-
this.broadcast({
|
|
2166
|
-
type: "tool_running",
|
|
2167
|
-
sessionId,
|
|
2168
|
-
timestamp: Date.now(),
|
|
2169
|
-
data,
|
|
2170
|
-
});
|
|
2171
|
-
// Hook-sourced tool_running events fire for every tool call.
|
|
2172
|
-
// Only broadcast to SSE (for activity box) — skip chat messages.
|
|
2173
|
-
const toolData = data;
|
|
2174
|
-
if (toolData.source === "hook") {
|
|
2175
|
-
break;
|
|
2176
|
-
}
|
|
2177
|
-
// Throttle chat notifications: at most one per 30s per session.
|
|
2178
|
-
// Suppress during the first 10s after registration — startup status
|
|
2179
|
-
// lines (e.g. "Claude in Chrome enabled") can trigger tool_running
|
|
2180
|
-
// before the agent has actually begun working.
|
|
2181
|
-
const now = Date.now();
|
|
2182
|
-
const STARTUP_GRACE_MS = 10_000;
|
|
2183
|
-
if (now - taskCtx.registeredAt < STARTUP_GRACE_MS) {
|
|
2184
|
-
break;
|
|
2185
|
-
}
|
|
2186
|
-
const lastNotif = this.lastToolNotification.get(sessionId) ?? 0;
|
|
2187
|
-
if (now - lastNotif > 30_000) {
|
|
2188
|
-
this.lastToolNotification.set(sessionId, now);
|
|
2189
|
-
const toolDesc = toolData.description ?? toolData.toolName ?? "an external tool";
|
|
2190
|
-
// Try to extract a dev server URL from recent output
|
|
2191
|
-
let urlSuffix = "";
|
|
2192
|
-
if (this.ptyService) {
|
|
2193
|
-
try {
|
|
2194
|
-
const recentOutput = await this.ptyService.getSessionOutput(sessionId, 50);
|
|
2195
|
-
const devUrl = extractDevServerUrl(recentOutput);
|
|
2196
|
-
if (devUrl) {
|
|
2197
|
-
urlSuffix = ` Dev server running at ${devUrl}`;
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
catch {
|
|
2201
|
-
// Best-effort — don't block on failure
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
const message = `[${taskCtx.label}] Running ${toolDesc}.${urlSuffix} The agent is working outside the terminal.`;
|
|
2205
|
-
this.log(message);
|
|
2206
|
-
this.sendChatMessage(message, "coding-agent");
|
|
2207
|
-
}
|
|
2208
|
-
break;
|
|
2209
|
-
}
|
|
2210
|
-
default:
|
|
2211
|
-
// Broadcast unknown events for observability
|
|
2212
|
-
this.broadcast({
|
|
2213
|
-
type: event,
|
|
2214
|
-
sessionId,
|
|
2215
|
-
timestamp: Date.now(),
|
|
2216
|
-
data,
|
|
2217
|
-
});
|
|
2218
|
-
}
|
|
2219
|
-
await this.syncTaskContext(taskCtx);
|
|
2220
|
-
}
|
|
2221
|
-
// ─── LLM Decision (delegated) ───
|
|
2222
|
-
async makeCoordinationDecision(taskCtx, promptText, recentOutput) {
|
|
2223
|
-
// Re-export for backward compatibility — delegates to module function
|
|
2224
|
-
const { makeCoordinationDecision: mkDecision } = await import("./swarm-decision-loop.js");
|
|
2225
|
-
return mkDecision(this, taskCtx, promptText, recentOutput);
|
|
2226
|
-
}
|
|
2227
|
-
async executeDecision(sessionId, decision) {
|
|
2228
|
-
return execDecision(this, sessionId, decision);
|
|
2229
|
-
}
|
|
2230
|
-
/**
|
|
2231
|
-
* Public entry point for external callers (e.g. server.ts) to execute
|
|
2232
|
-
* a coordination decision on a session. Wraps the internal executeDecision.
|
|
2233
|
-
*/
|
|
2234
|
-
async executeEventDecision(sessionId, decision) {
|
|
2235
|
-
return execDecision(this, sessionId, decision);
|
|
2236
|
-
}
|
|
2237
|
-
// ─── Supervision ───
|
|
2238
|
-
setSupervisionLevel(level) {
|
|
2239
|
-
this.supervisionLevel = level;
|
|
2240
|
-
this.broadcast({
|
|
2241
|
-
type: "supervision_changed",
|
|
2242
|
-
sessionId: "*",
|
|
2243
|
-
timestamp: Date.now(),
|
|
2244
|
-
data: { level },
|
|
2245
|
-
});
|
|
2246
|
-
this.log(`Supervision level set to: ${level}`);
|
|
2247
|
-
}
|
|
2248
|
-
getSupervisionLevel() {
|
|
2249
|
-
return this.supervisionLevel;
|
|
2250
|
-
}
|
|
2251
|
-
// ─── Confirmation Queue ───
|
|
2252
|
-
getPendingConfirmations() {
|
|
2253
|
-
return Array.from(this.pendingDecisions.values());
|
|
2254
|
-
}
|
|
2255
|
-
async confirmDecision(sessionId, approved, override) {
|
|
2256
|
-
const pending = this.pendingDecisions.get(sessionId);
|
|
2257
|
-
if (!pending) {
|
|
2258
|
-
throw new Error(`No pending decision for session ${sessionId}`);
|
|
2259
|
-
}
|
|
2260
|
-
const taskCtx = this.tasks.get(sessionId);
|
|
2261
|
-
if (approved) {
|
|
2262
|
-
// Use override if provided, otherwise use LLM suggestion
|
|
2263
|
-
const decision = override
|
|
2264
|
-
? {
|
|
2265
|
-
action: "respond",
|
|
2266
|
-
response: override.response,
|
|
2267
|
-
useKeys: override.useKeys,
|
|
2268
|
-
keys: override.keys,
|
|
2269
|
-
reasoning: "Human-approved (with override)",
|
|
2270
|
-
}
|
|
2271
|
-
: pending.llmDecision;
|
|
2272
|
-
if (taskCtx) {
|
|
2273
|
-
taskCtx.status = "active";
|
|
2274
|
-
taskCtx.autoResolvedCount = 0;
|
|
2275
|
-
await this.recordDecision(taskCtx, {
|
|
2276
|
-
timestamp: Date.now(),
|
|
2277
|
-
event: "blocked",
|
|
2278
|
-
promptText: pending.promptText,
|
|
2279
|
-
decision: decision.action,
|
|
2280
|
-
response: decision.action === "respond"
|
|
2281
|
-
? decision.useKeys
|
|
2282
|
-
? `keys:${decision.keys?.join(",")}`
|
|
2283
|
-
: decision.response
|
|
2284
|
-
: undefined,
|
|
2285
|
-
reasoning: `Human-approved: ${decision.reasoning}`,
|
|
2286
|
-
});
|
|
2287
|
-
await this.syncTaskContext(taskCtx);
|
|
2288
|
-
}
|
|
2289
|
-
await this.executeDecision(sessionId, decision);
|
|
2290
|
-
this.pendingDecisions.delete(sessionId);
|
|
2291
|
-
await this.taskRegistry.deletePendingDecision(sessionId);
|
|
2292
|
-
if (taskCtx) {
|
|
2293
|
-
await this.taskRegistry.appendEvent({
|
|
2294
|
-
threadId: taskCtx.threadId,
|
|
2295
|
-
sessionId,
|
|
2296
|
-
eventType: "confirmation_approved",
|
|
2297
|
-
summary: `Approved pending confirmation for "${taskCtx.label}"`,
|
|
2298
|
-
data: {
|
|
2299
|
-
action: decision.action,
|
|
2300
|
-
response: decision.response ?? null,
|
|
2301
|
-
},
|
|
2302
|
-
});
|
|
2303
|
-
}
|
|
2304
|
-
this.broadcast({
|
|
2305
|
-
type: "confirmation_approved",
|
|
2306
|
-
sessionId,
|
|
2307
|
-
timestamp: Date.now(),
|
|
2308
|
-
data: {
|
|
2309
|
-
action: decision.action,
|
|
2310
|
-
response: decision.response,
|
|
2311
|
-
useKeys: decision.useKeys,
|
|
2312
|
-
keys: decision.keys,
|
|
2313
|
-
},
|
|
2314
|
-
});
|
|
2315
|
-
if (taskCtx) {
|
|
2316
|
-
this.sendChatMessage(`"${taskCtx.label}" was approved. Eliza is continuing the task now.`, "coding-agent");
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
else {
|
|
2320
|
-
// Rejected — record and broadcast
|
|
2321
|
-
if (taskCtx) {
|
|
2322
|
-
taskCtx.status = "blocked";
|
|
2323
|
-
await this.recordDecision(taskCtx, {
|
|
2324
|
-
timestamp: Date.now(),
|
|
2325
|
-
event: "blocked",
|
|
2326
|
-
promptText: pending.promptText,
|
|
2327
|
-
decision: "escalate",
|
|
2328
|
-
reasoning: "Human rejected the suggested action",
|
|
2329
|
-
});
|
|
2330
|
-
await this.syncTaskContext(taskCtx);
|
|
2331
|
-
}
|
|
2332
|
-
this.pendingDecisions.delete(sessionId);
|
|
2333
|
-
await this.taskRegistry.deletePendingDecision(sessionId);
|
|
2334
|
-
if (pending.taskContext.threadId) {
|
|
2335
|
-
await this.taskRegistry.appendEvent({
|
|
2336
|
-
threadId: pending.taskContext.threadId,
|
|
2337
|
-
sessionId,
|
|
2338
|
-
eventType: "confirmation_rejected",
|
|
2339
|
-
summary: `Rejected pending confirmation for "${pending.taskContext.label}"`,
|
|
2340
|
-
data: { prompt: pending.promptText },
|
|
2341
|
-
});
|
|
2342
|
-
}
|
|
2343
|
-
this.broadcast({
|
|
2344
|
-
type: "confirmation_rejected",
|
|
2345
|
-
sessionId,
|
|
2346
|
-
timestamp: Date.now(),
|
|
2347
|
-
data: { prompt: pending.promptText },
|
|
2348
|
-
});
|
|
2349
|
-
this.sendChatMessage(`"${pending.taskContext.label}" remains blocked after the suggested action was rejected. Prompt: ${pending.promptText}`, "coding-agent");
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
// ─── Internal ───
|
|
2353
|
-
log(message) {
|
|
2354
|
-
logger.info(`[SwarmCoordinator] ${message}`);
|
|
2355
|
-
}
|
|
2356
|
-
}
|