happy2 0.0.5
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/README.md +82 -0
- package/package.json +74 -0
- package/packages/happy2-server/Dockerfile +18 -0
- package/packages/happy2-server/README.md +418 -0
- package/packages/happy2-server/bin/happy2.js +3 -0
- package/packages/happy2-server/dist/cli.d.ts +3 -0
- package/packages/happy2-server/dist/cli.d.ts.map +1 -0
- package/packages/happy2-server/dist/cli.js +44 -0
- package/packages/happy2-server/dist/cli.js.map +1 -0
- package/packages/happy2-server/dist/index.d.ts +24 -0
- package/packages/happy2-server/dist/index.d.ts.map +1 -0
- package/packages/happy2-server/dist/index.js +22 -0
- package/packages/happy2-server/dist/index.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/builtin-images.d.ts +8 -0
- package/packages/happy2-server/dist/modules/agents/builtin-images.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/builtin-images.js +283 -0
- package/packages/happy2-server/dist/modules/agents/builtin-images.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/command.d.ts +3 -0
- package/packages/happy2-server/dist/modules/agents/command.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/command.js +7 -0
- package/packages/happy2-server/dist/modules/agents/command.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/daemon.d.ts +106 -0
- package/packages/happy2-server/dist/modules/agents/daemon.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/daemon.js +522 -0
- package/packages/happy2-server/dist/modules/agents/daemon.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/docker.d.ts +51 -0
- package/packages/happy2-server/dist/modules/agents/docker.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/docker.js +256 -0
- package/packages/happy2-server/dist/modules/agents/docker.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/docker.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/agents/docker.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/docker.test.js +155 -0
- package/packages/happy2-server/dist/modules/agents/docker.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/index.d.ts +5 -0
- package/packages/happy2-server/dist/modules/agents/index.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/index.js +4 -0
- package/packages/happy2-server/dist/modules/agents/index.js.map +1 -0
- package/packages/happy2-server/dist/modules/agents/service.d.ts +140 -0
- package/packages/happy2-server/dist/modules/agents/service.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/agents/service.js +1283 -0
- package/packages/happy2-server/dist/modules/agents/service.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/cloudflare-access.d.ts +15 -0
- package/packages/happy2-server/dist/modules/auth/cloudflare-access.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/cloudflare-access.js +49 -0
- package/packages/happy2-server/dist/modules/auth/cloudflare-access.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/crypto.d.ts +4 -0
- package/packages/happy2-server/dist/modules/auth/crypto.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/crypto.js +31 -0
- package/packages/happy2-server/dist/modules/auth/crypto.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/crypto.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/auth/crypto.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/crypto.test.js +11 -0
- package/packages/happy2-server/dist/modules/auth/crypto.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/email.d.ts +3 -0
- package/packages/happy2-server/dist/modules/auth/email.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/email.js +22 -0
- package/packages/happy2-server/dist/modules/auth/email.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/metadata.d.ts +5 -0
- package/packages/happy2-server/dist/modules/auth/metadata.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/metadata.js +28 -0
- package/packages/happy2-server/dist/modules/auth/metadata.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/metadata.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/auth/metadata.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/metadata.test.js +10 -0
- package/packages/happy2-server/dist/modules/auth/metadata.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/methods.d.ts +10 -0
- package/packages/happy2-server/dist/modules/auth/methods.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/methods.js +21 -0
- package/packages/happy2-server/dist/modules/auth/methods.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/methods.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/auth/methods.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/methods.test.js +16 -0
- package/packages/happy2-server/dist/modules/auth/methods.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/oidc.d.ts +9 -0
- package/packages/happy2-server/dist/modules/auth/oidc.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/oidc.js +68 -0
- package/packages/happy2-server/dist/modules/auth/oidc.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/service.d.ts +44 -0
- package/packages/happy2-server/dist/modules/auth/service.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/service.js +208 -0
- package/packages/happy2-server/dist/modules/auth/service.js.map +1 -0
- package/packages/happy2-server/dist/modules/auth/tokens.d.ts +21 -0
- package/packages/happy2-server/dist/modules/auth/tokens.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/tokens.js +79 -0
- package/packages/happy2-server/dist/modules/auth/tokens.js.map +1 -0
- package/packages/happy2-server/dist/modules/automation/repository.d.ts +127 -0
- package/packages/happy2-server/dist/modules/automation/repository.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/automation/repository.js +821 -0
- package/packages/happy2-server/dist/modules/automation/repository.js.map +1 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.d.ts +839 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.js +5795 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.js.map +1 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.test.js +620 -0
- package/packages/happy2-server/dist/modules/collaboration/repository.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/collaboration/types.d.ts +243 -0
- package/packages/happy2-server/dist/modules/collaboration/types.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/collaboration/types.js +8 -0
- package/packages/happy2-server/dist/modules/collaboration/types.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/defaults.d.ts +8 -0
- package/packages/happy2-server/dist/modules/config/defaults.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/defaults.js +70 -0
- package/packages/happy2-server/dist/modules/config/defaults.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/defaults.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/config/defaults.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/defaults.test.js +27 -0
- package/packages/happy2-server/dist/modules/config/defaults.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/environment.d.ts +4 -0
- package/packages/happy2-server/dist/modules/config/environment.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/environment.js +92 -0
- package/packages/happy2-server/dist/modules/config/environment.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/loader.d.ts +5 -0
- package/packages/happy2-server/dist/modules/config/loader.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/loader.js +251 -0
- package/packages/happy2-server/dist/modules/config/loader.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/loader.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/config/loader.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/loader.test.js +108 -0
- package/packages/happy2-server/dist/modules/config/loader.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/paths.d.ts +7 -0
- package/packages/happy2-server/dist/modules/config/paths.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/paths.js +15 -0
- package/packages/happy2-server/dist/modules/config/paths.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/paths.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/config/paths.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/paths.test.js +22 -0
- package/packages/happy2-server/dist/modules/config/paths.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/runtime.d.ts +6 -0
- package/packages/happy2-server/dist/modules/config/runtime.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/runtime.js +14 -0
- package/packages/happy2-server/dist/modules/config/runtime.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/type.d.ts +88 -0
- package/packages/happy2-server/dist/modules/config/type.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/config/type.js +2 -0
- package/packages/happy2-server/dist/modules/config/type.js.map +1 -0
- package/packages/happy2-server/dist/modules/database.d.ts +105 -0
- package/packages/happy2-server/dist/modules/database.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/database.js +583 -0
- package/packages/happy2-server/dist/modules/database.js.map +1 -0
- package/packages/happy2-server/dist/modules/drizzle.d.ts +11 -0
- package/packages/happy2-server/dist/modules/drizzle.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/drizzle.js +31 -0
- package/packages/happy2-server/dist/modules/drizzle.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/lock.d.ts +3 -0
- package/packages/happy2-server/dist/modules/files/lock.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/lock.js +83 -0
- package/packages/happy2-server/dist/modules/files/lock.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/media.d.ts +18 -0
- package/packages/happy2-server/dist/modules/files/media.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/media.js +352 -0
- package/packages/happy2-server/dist/modules/files/media.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/provider.d.ts +130 -0
- package/packages/happy2-server/dist/modules/files/provider.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/provider.js +448 -0
- package/packages/happy2-server/dist/modules/files/provider.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/quota.d.ts +58 -0
- package/packages/happy2-server/dist/modules/files/quota.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/quota.js +180 -0
- package/packages/happy2-server/dist/modules/files/quota.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/routes.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/files/routes.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/routes.test.js +163 -0
- package/packages/happy2-server/dist/modules/files/routes.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/scanner.d.ts +27 -0
- package/packages/happy2-server/dist/modules/files/scanner.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/scanner.js +66 -0
- package/packages/happy2-server/dist/modules/files/scanner.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/storage.d.ts +95 -0
- package/packages/happy2-server/dist/modules/files/storage.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/storage.js +855 -0
- package/packages/happy2-server/dist/modules/files/storage.js.map +1 -0
- package/packages/happy2-server/dist/modules/files/storage.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/files/storage.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/files/storage.test.js +170 -0
- package/packages/happy2-server/dist/modules/files/storage.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/index.d.ts +6 -0
- package/packages/happy2-server/dist/modules/integrations/index.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/index.js +6 -0
- package/packages/happy2-server/dist/modules/integrations/index.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/repository.d.ts +136 -0
- package/packages/happy2-server/dist/modules/integrations/repository.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/repository.js +1229 -0
- package/packages/happy2-server/dist/modules/integrations/repository.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/repository.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/integrations/repository.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/repository.test.js +345 -0
- package/packages/happy2-server/dist/modules/integrations/repository.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/routes.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/integrations/routes.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/routes.test.js +161 -0
- package/packages/happy2-server/dist/modules/integrations/routes.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/secrets.d.ts +19 -0
- package/packages/happy2-server/dist/modules/integrations/secrets.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/secrets.js +69 -0
- package/packages/happy2-server/dist/modules/integrations/secrets.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.d.ts +32 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.js +185 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.test.js +56 -0
- package/packages/happy2-server/dist/modules/integrations/ssrf.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/transport.d.ts +21 -0
- package/packages/happy2-server/dist/modules/integrations/transport.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/transport.js +108 -0
- package/packages/happy2-server/dist/modules/integrations/transport.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/transport.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/integrations/transport.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/transport.test.js +66 -0
- package/packages/happy2-server/dist/modules/integrations/transport.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/types.d.ts +155 -0
- package/packages/happy2-server/dist/modules/integrations/types.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/integrations/types.js +26 -0
- package/packages/happy2-server/dist/modules/integrations/types.js.map +1 -0
- package/packages/happy2-server/dist/modules/operations/export-worker.d.ts +16 -0
- package/packages/happy2-server/dist/modules/operations/export-worker.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/operations/export-worker.js +56 -0
- package/packages/happy2-server/dist/modules/operations/export-worker.js.map +1 -0
- package/packages/happy2-server/dist/modules/operations/repository.d.ts +215 -0
- package/packages/happy2-server/dist/modules/operations/repository.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/operations/repository.js +2011 -0
- package/packages/happy2-server/dist/modules/operations/repository.js.map +1 -0
- package/packages/happy2-server/dist/modules/operations/repository.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/operations/repository.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/operations/repository.test.js +610 -0
- package/packages/happy2-server/dist/modules/operations/repository.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/operations/types.d.ts +144 -0
- package/packages/happy2-server/dist/modules/operations/types.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/operations/types.js +9 -0
- package/packages/happy2-server/dist/modules/operations/types.js.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/events.d.ts +99 -0
- package/packages/happy2-server/dist/modules/realtime/events.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/events.js +173 -0
- package/packages/happy2-server/dist/modules/realtime/events.js.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/index.d.ts +4 -0
- package/packages/happy2-server/dist/modules/realtime/index.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/index.js +4 -0
- package/packages/happy2-server/dist/modules/realtime/index.js.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/local.d.ts +37 -0
- package/packages/happy2-server/dist/modules/realtime/local.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/local.js +181 -0
- package/packages/happy2-server/dist/modules/realtime/local.js.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/local.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/realtime/local.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/local.test.js +186 -0
- package/packages/happy2-server/dist/modules/realtime/local.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/pubsub.d.ts +26 -0
- package/packages/happy2-server/dist/modules/realtime/pubsub.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/realtime/pubsub.js +7 -0
- package/packages/happy2-server/dist/modules/realtime/pubsub.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/database-idempotency.d.ts +15 -0
- package/packages/happy2-server/dist/modules/request/database-idempotency.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/database-idempotency.js +141 -0
- package/packages/happy2-server/dist/modules/request/database-idempotency.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/idempotency-hook.d.ts +12 -0
- package/packages/happy2-server/dist/modules/request/idempotency-hook.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/idempotency-hook.js +98 -0
- package/packages/happy2-server/dist/modules/request/idempotency-hook.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/idempotency.d.ts +146 -0
- package/packages/happy2-server/dist/modules/request/idempotency.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/idempotency.js +312 -0
- package/packages/happy2-server/dist/modules/request/idempotency.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/idempotency.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/request/idempotency.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/idempotency.test.js +191 -0
- package/packages/happy2-server/dist/modules/request/idempotency.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/index.d.ts +5 -0
- package/packages/happy2-server/dist/modules/request/index.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/index.js +5 -0
- package/packages/happy2-server/dist/modules/request/index.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.d.ts +100 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.js +239 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.js.map +1 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.test.js +123 -0
- package/packages/happy2-server/dist/modules/request/rate-limit.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/schema.d.ts +31454 -0
- package/packages/happy2-server/dist/modules/schema.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/schema.js +1081 -0
- package/packages/happy2-server/dist/modules/schema.js.map +1 -0
- package/packages/happy2-server/dist/modules/workspace/index.d.ts +3 -0
- package/packages/happy2-server/dist/modules/workspace/index.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/workspace/index.js +3 -0
- package/packages/happy2-server/dist/modules/workspace/index.js.map +1 -0
- package/packages/happy2-server/dist/modules/workspace/service.d.ts +54 -0
- package/packages/happy2-server/dist/modules/workspace/service.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/workspace/service.js +997 -0
- package/packages/happy2-server/dist/modules/workspace/service.js.map +1 -0
- package/packages/happy2-server/dist/modules/workspace/types.d.ts +58 -0
- package/packages/happy2-server/dist/modules/workspace/types.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/workspace/types.js +11 -0
- package/packages/happy2-server/dist/modules/workspace/types.js.map +1 -0
- package/packages/happy2-server/dist/routes/agents.d.ts +5 -0
- package/packages/happy2-server/dist/routes/agents.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/agents.js +225 -0
- package/packages/happy2-server/dist/routes/agents.js.map +1 -0
- package/packages/happy2-server/dist/routes/auth.d.ts +5 -0
- package/packages/happy2-server/dist/routes/auth.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/auth.js +128 -0
- package/packages/happy2-server/dist/routes/auth.js.map +1 -0
- package/packages/happy2-server/dist/routes/automation.d.ts +6 -0
- package/packages/happy2-server/dist/routes/automation.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/automation.js +315 -0
- package/packages/happy2-server/dist/routes/automation.js.map +1 -0
- package/packages/happy2-server/dist/routes/basic.d.ts +3 -0
- package/packages/happy2-server/dist/routes/basic.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/basic.js +5 -0
- package/packages/happy2-server/dist/routes/basic.js.map +1 -0
- package/packages/happy2-server/dist/routes/collaboration.d.ts +7 -0
- package/packages/happy2-server/dist/routes/collaboration.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/collaboration.js +1285 -0
- package/packages/happy2-server/dist/routes/collaboration.js.map +1 -0
- package/packages/happy2-server/dist/routes/files.d.ts +9 -0
- package/packages/happy2-server/dist/routes/files.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/files.js +397 -0
- package/packages/happy2-server/dist/routes/files.js.map +1 -0
- package/packages/happy2-server/dist/routes/integrations.d.ts +6 -0
- package/packages/happy2-server/dist/routes/integrations.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/integrations.js +486 -0
- package/packages/happy2-server/dist/routes/integrations.js.map +1 -0
- package/packages/happy2-server/dist/routes/operations.d.ts +5 -0
- package/packages/happy2-server/dist/routes/operations.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/operations.js +588 -0
- package/packages/happy2-server/dist/routes/operations.js.map +1 -0
- package/packages/happy2-server/dist/routes/operations.test.d.ts +2 -0
- package/packages/happy2-server/dist/routes/operations.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/operations.test.js +106 -0
- package/packages/happy2-server/dist/routes/operations.test.js.map +1 -0
- package/packages/happy2-server/dist/routes/sync.d.ts +6 -0
- package/packages/happy2-server/dist/routes/sync.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/sync.js +549 -0
- package/packages/happy2-server/dist/routes/sync.js.map +1 -0
- package/packages/happy2-server/dist/routes/workspace.d.ts +5 -0
- package/packages/happy2-server/dist/routes/workspace.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/workspace.js +240 -0
- package/packages/happy2-server/dist/routes/workspace.js.map +1 -0
- package/packages/happy2-server/dist/runner.d.ts +3 -0
- package/packages/happy2-server/dist/runner.d.ts.map +1 -0
- package/packages/happy2-server/dist/runner.js +30 -0
- package/packages/happy2-server/dist/runner.js.map +1 -0
- package/packages/happy2-server/dist/server.d.ts +32 -0
- package/packages/happy2-server/dist/server.d.ts.map +1 -0
- package/packages/happy2-server/dist/server.js +284 -0
- package/packages/happy2-server/dist/server.js.map +1 -0
- package/packages/happy2-server/dist/standalone.d.ts +13 -0
- package/packages/happy2-server/dist/standalone.d.ts.map +1 -0
- package/packages/happy2-server/dist/standalone.js +99 -0
- package/packages/happy2-server/dist/standalone.js.map +1 -0
- package/packages/happy2-server/dist/web/assets/figtree-latin-wght-normal-D_ZTVpCC.woff2 +0 -0
- package/packages/happy2-server/dist/web/assets/happy-otter-Q2O_g-DT.png +0 -0
- package/packages/happy2-server/dist/web/assets/index-COvbX8JP.css +2 -0
- package/packages/happy2-server/dist/web/assets/index-DdpugxMO.js +57 -0
- package/packages/happy2-server/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/packages/happy2-server/dist/web/assets/onboarding-CRUpq9xl.png +0 -0
- package/packages/happy2-server/dist/web/index.html +13 -0
- package/packages/happy2-server/drizzle/0000_initial.sql +13 -0
- package/packages/happy2-server/drizzle/0001_accounts_profiles_files.sql +42 -0
- package/packages/happy2-server/drizzle/0002_file_visibility_and_uploader.sql +5 -0
- package/packages/happy2-server/drizzle/0003_collaboration.sql +565 -0
- package/packages/happy2-server/drizzle/0004_collaboration_expansion.sql +1099 -0
- package/packages/happy2-server/drizzle/0005_ai_agents.sql +85 -0
- package/packages/happy2-server/drizzle/0006_rig_global_event_queue.sql +10 -0
- package/packages/happy2-server/drizzle/0007_rename_server_identity.sql +3 -0
- package/packages/happy2-server/drizzle/0008_agent_message_streaming.sql +47 -0
- package/packages/happy2-server/drizzle/0009_chat_workspace_binding_lookup.sql +2 -0
- package/packages/happy2-server/drizzle/0010_rig_secret_assignments.sql +21 -0
- package/packages/happy2-server/drizzle/meta/_journal.json +83 -0
- package/packages/happy2-server/happy2.example.toml +104 -0
|
@@ -0,0 +1,1283 @@
|
|
|
1
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { CollaborationError, } from "../collaboration/types.js";
|
|
6
|
+
import { realtimeTopics } from "../realtime/index.js";
|
|
7
|
+
import { isRetryableRigError, RigDaemonClient, } from "./daemon.js";
|
|
8
|
+
import { BUILTIN_AGENT_IMAGES } from "./builtin-images.js";
|
|
9
|
+
const IGNORED_EVENT_CHECKPOINT_INTERVAL = 100;
|
|
10
|
+
const EVENT_RETRY_INTERVAL_MS = 100;
|
|
11
|
+
const TYPING_TTL_MS = 30_000;
|
|
12
|
+
const TYPING_RENEW_INTERVAL_MS = 20_000;
|
|
13
|
+
const TRIM_EVENT_INTERVAL = 1_000;
|
|
14
|
+
const TRIM_TIME_INTERVAL_MS = 24 * 60 * 60_000;
|
|
15
|
+
const IMAGE_BUILD_LEASE_RENEW_INTERVAL_MS = 20_000;
|
|
16
|
+
const IMAGE_BUILD_LOG_FLUSH_INTERVAL_MS = 500;
|
|
17
|
+
const IMAGE_BUILD_LOG_FLUSH_CHARACTERS = 32_768;
|
|
18
|
+
const MAX_BUILD_LOG_LINE_CHARACTERS = 1_000;
|
|
19
|
+
const MAX_CONCURRENT_IMAGE_BUILDS = 1;
|
|
20
|
+
const AGENT_REPLY_FLUSH_INTERVAL_MS = 50;
|
|
21
|
+
const AGENT_REPLY_FLUSH_CHARACTERS = 1_024;
|
|
22
|
+
const AGENT_CONTAINER_SECURITY = {
|
|
23
|
+
init: true,
|
|
24
|
+
readonlyRootFilesystem: true,
|
|
25
|
+
sharedMemoryBytes: 1024 * 1024 * 1024,
|
|
26
|
+
tmpfs: [
|
|
27
|
+
{ target: "/tmp", mode: 0o1777 },
|
|
28
|
+
{ target: "/run", mode: 0o755 },
|
|
29
|
+
{ target: "/var/tmp", mode: 0o1777 },
|
|
30
|
+
{ target: "/var/run", mode: 0o755 },
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
export class AgentService {
|
|
34
|
+
repository;
|
|
35
|
+
pubsub;
|
|
36
|
+
daemon;
|
|
37
|
+
docker;
|
|
38
|
+
defaultCwd;
|
|
39
|
+
onError;
|
|
40
|
+
workerId = createId();
|
|
41
|
+
bindingCreations = new Map();
|
|
42
|
+
imageBuilds = new Map();
|
|
43
|
+
pendingImageBuilds = new Set();
|
|
44
|
+
secretMutations = new Map();
|
|
45
|
+
activeImageBuilds = 0;
|
|
46
|
+
drains = new Map();
|
|
47
|
+
turnStreams = new Map();
|
|
48
|
+
typingRenewals = new Map();
|
|
49
|
+
shutdown = new AbortController();
|
|
50
|
+
queueTask;
|
|
51
|
+
stopping = false;
|
|
52
|
+
constructor(repository, pubsub, daemon, docker, defaultCwd, onError = () => undefined) {
|
|
53
|
+
this.repository = repository;
|
|
54
|
+
this.pubsub = pubsub;
|
|
55
|
+
this.daemon = daemon;
|
|
56
|
+
this.docker = docker;
|
|
57
|
+
this.defaultCwd = defaultCwd;
|
|
58
|
+
this.onError = onError;
|
|
59
|
+
}
|
|
60
|
+
async createAgent(input) {
|
|
61
|
+
if (!(await this.repository.agentUsernameAvailable(input.username)))
|
|
62
|
+
throw new CollaborationError("conflict", "Agent username is already taken");
|
|
63
|
+
const image = await this.repository.getReadyDefaultAgentImage();
|
|
64
|
+
if (!image)
|
|
65
|
+
throw new CollaborationError("conflict", "A ready default agent image must be configured before creating agents");
|
|
66
|
+
const agentUserId = createId();
|
|
67
|
+
const sandbox = sandboxDirectories(this.defaultCwd, agentUserId, input.actorUserId);
|
|
68
|
+
await Promise.all([
|
|
69
|
+
mkdir(sandbox.home, { recursive: true, mode: 0o700 }),
|
|
70
|
+
mkdir(sandbox.workspace, { recursive: true, mode: 0o700 }),
|
|
71
|
+
]);
|
|
72
|
+
const containerName = agentContainerName();
|
|
73
|
+
await this.docker.createContainer({
|
|
74
|
+
agentUserId,
|
|
75
|
+
containerName,
|
|
76
|
+
homeDirectory: sandbox.home,
|
|
77
|
+
imageId: image.id,
|
|
78
|
+
imageTag: image.dockerTag,
|
|
79
|
+
security: AGENT_CONTAINER_SECURITY,
|
|
80
|
+
workspaceDirectory: sandbox.workspace,
|
|
81
|
+
}, this.shutdown.signal);
|
|
82
|
+
try {
|
|
83
|
+
const session = await this.daemon.createSession(sandbox.workspace, containerName, this.shutdown.signal);
|
|
84
|
+
return await this.repository.createAgent({
|
|
85
|
+
...input,
|
|
86
|
+
agentUserId,
|
|
87
|
+
containerName,
|
|
88
|
+
cwd: sandbox.workspace,
|
|
89
|
+
imageId: image.id,
|
|
90
|
+
sessionId: session.id,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
await this.docker.removeContainer(containerName);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async start() {
|
|
99
|
+
await this.repository.ensureAgentImageDefinitions(BUILTIN_AGENT_IMAGES.map((definition) => {
|
|
100
|
+
const definitionHash = agentImageDefinitionHash(definition.dockerfile, definition.buildContext);
|
|
101
|
+
return {
|
|
102
|
+
...definition,
|
|
103
|
+
definitionHash,
|
|
104
|
+
dockerTag: agentImageTag(definitionHash),
|
|
105
|
+
};
|
|
106
|
+
}));
|
|
107
|
+
for (const imageId of await this.repository.listRequestedAgentImageBuildIds())
|
|
108
|
+
this.queueImageBuild(imageId);
|
|
109
|
+
await this.daemon.ensureGlobalEventQueue(this.shutdown.signal);
|
|
110
|
+
await this.reconcileSecretBindings();
|
|
111
|
+
this.queueTask = this.trackGlobalEvents().catch((error) => {
|
|
112
|
+
if (!this.shutdown.signal.aborted)
|
|
113
|
+
this.onError(error);
|
|
114
|
+
});
|
|
115
|
+
const chatIds = await this.repository.listUnfinishedAgentChatIds();
|
|
116
|
+
for (const chatId of chatIds)
|
|
117
|
+
this.startDrain(chatId);
|
|
118
|
+
}
|
|
119
|
+
async prepareTurn(input) {
|
|
120
|
+
const context = await this.repository.getDirectAgentChatContext(input.actorUserId, input.chatId);
|
|
121
|
+
if (!context)
|
|
122
|
+
return undefined;
|
|
123
|
+
const binding = await this.ensureAgentBinding(input.actorUserId, input.chatId);
|
|
124
|
+
return binding
|
|
125
|
+
? { agentUserId: context.agentUserId, sessionId: binding.sessionId }
|
|
126
|
+
: undefined;
|
|
127
|
+
}
|
|
128
|
+
startTurn(chatId) {
|
|
129
|
+
this.startDrain(chatId);
|
|
130
|
+
}
|
|
131
|
+
listAgentImages(actorUserId) {
|
|
132
|
+
return this.repository.listAgentImages(actorUserId);
|
|
133
|
+
}
|
|
134
|
+
getAgentImage(actorUserId, imageId) {
|
|
135
|
+
return this.repository.getAgentImage(actorUserId, imageId);
|
|
136
|
+
}
|
|
137
|
+
async createAgentImage(input) {
|
|
138
|
+
const definitionHash = agentImageDefinitionHash(input.dockerfile);
|
|
139
|
+
const result = await this.repository.createAgentImage({
|
|
140
|
+
...input,
|
|
141
|
+
definitionHash,
|
|
142
|
+
dockerTag: agentImageTag(definitionHash),
|
|
143
|
+
});
|
|
144
|
+
await this.publishAgentImageHint(result.hint);
|
|
145
|
+
this.queueImageBuild(result.image.id);
|
|
146
|
+
return result.image;
|
|
147
|
+
}
|
|
148
|
+
async requestAgentImageBuild(input) {
|
|
149
|
+
const result = await this.repository.requestAgentImageBuild(input);
|
|
150
|
+
await this.publishAgentImageHint(result.hint);
|
|
151
|
+
this.queueImageBuild(result.image.id);
|
|
152
|
+
return result.image;
|
|
153
|
+
}
|
|
154
|
+
async setDefaultAgentImage(input) {
|
|
155
|
+
const result = await this.repository.setDefaultAgentImage(input);
|
|
156
|
+
await this.publishAgentImageHint(result.hint);
|
|
157
|
+
return result.image;
|
|
158
|
+
}
|
|
159
|
+
async listAgentSecrets(actorUserId) {
|
|
160
|
+
const assignments = await this.repository.listAgentSecretAssignments(actorUserId);
|
|
161
|
+
const assignmentById = new Map(assignments.map((assignment) => [assignment.secretId, assignment]));
|
|
162
|
+
const secrets = await this.daemon.listSecrets(this.shutdown.signal);
|
|
163
|
+
return {
|
|
164
|
+
secrets: secrets.map((secret) => {
|
|
165
|
+
const assignment = assignmentById.get(secret.id);
|
|
166
|
+
return {
|
|
167
|
+
...secret,
|
|
168
|
+
agentUserIds: [...(assignment?.agentUserIds ?? [])],
|
|
169
|
+
channelIds: [...(assignment?.channelIds ?? [])],
|
|
170
|
+
};
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async createAgentSecret(input) {
|
|
175
|
+
return this.serializeAgentSecret(input.id, () => this.createAgentSecretMutation(input));
|
|
176
|
+
}
|
|
177
|
+
async createAgentSecretMutation(input) {
|
|
178
|
+
await this.repository.authorizeAgentSecretManagement(input.actorUserId);
|
|
179
|
+
const secret = await this.daemon.registerSecret({
|
|
180
|
+
id: input.id,
|
|
181
|
+
description: input.description,
|
|
182
|
+
environment: input.environment,
|
|
183
|
+
}, this.shutdown.signal);
|
|
184
|
+
const sync = await this.repository.recordAgentSecretRegistration({
|
|
185
|
+
actorUserId: input.actorUserId,
|
|
186
|
+
secretId: input.id,
|
|
187
|
+
});
|
|
188
|
+
await this.publishAgentHint(sync);
|
|
189
|
+
const assignments = await this.repository.listAgentSecretAssignments(input.actorUserId);
|
|
190
|
+
const assignment = assignments.find((candidate) => candidate.secretId === secret.id);
|
|
191
|
+
return {
|
|
192
|
+
secret: {
|
|
193
|
+
...secret,
|
|
194
|
+
agentUserIds: [...(assignment?.agentUserIds ?? [])],
|
|
195
|
+
channelIds: [...(assignment?.channelIds ?? [])],
|
|
196
|
+
},
|
|
197
|
+
sync,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async deleteAgentSecret(input) {
|
|
201
|
+
return this.serializeAgentSecret(input.secretId, () => this.deleteAgentSecretMutation(input));
|
|
202
|
+
}
|
|
203
|
+
async deleteAgentSecretMutation(input) {
|
|
204
|
+
await this.repository.authorizeAgentSecretManagement(input.actorUserId);
|
|
205
|
+
const sync = await this.repository.deleteAgentSecretAssignments(input);
|
|
206
|
+
let removed;
|
|
207
|
+
try {
|
|
208
|
+
removed = await this.daemon.unregisterSecret(input.secretId, this.shutdown.signal);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
await this.publishAgentHint(sync);
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
await this.publishAgentHint(sync);
|
|
215
|
+
return { removed, sync };
|
|
216
|
+
}
|
|
217
|
+
async attachAgentSecretToAgent(input) {
|
|
218
|
+
return this.serializeAgentSecret(input.secretId, () => this.attachAgentSecretToAgentMutation(input));
|
|
219
|
+
}
|
|
220
|
+
async attachAgentSecretToAgentMutation(input) {
|
|
221
|
+
await this.requireAgentSecret(input.actorUserId, input.secretId);
|
|
222
|
+
const sync = await this.repository.attachAgentSecretToAgent(input);
|
|
223
|
+
await this.reconcileSecretBindings({ agentUserId: input.agentUserId });
|
|
224
|
+
if (sync)
|
|
225
|
+
await this.publishAgentHint(sync);
|
|
226
|
+
return {
|
|
227
|
+
secret: await this.agentSecret(input.actorUserId, input.secretId),
|
|
228
|
+
...(sync ? { sync } : {}),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async detachAgentSecretFromAgent(input) {
|
|
232
|
+
return this.serializeAgentSecret(input.secretId, () => this.detachAgentSecretFromAgentMutation(input));
|
|
233
|
+
}
|
|
234
|
+
async detachAgentSecretFromAgentMutation(input) {
|
|
235
|
+
await this.requireAgentSecret(input.actorUserId, input.secretId);
|
|
236
|
+
const sync = await this.repository.detachAgentSecretFromAgent(input);
|
|
237
|
+
await this.reconcileSecretBindings({ agentUserId: input.agentUserId });
|
|
238
|
+
if (sync)
|
|
239
|
+
await this.publishAgentHint(sync);
|
|
240
|
+
return {
|
|
241
|
+
secret: await this.agentSecret(input.actorUserId, input.secretId),
|
|
242
|
+
...(sync ? { sync } : {}),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
async attachAgentSecretToChannel(input) {
|
|
246
|
+
return this.serializeAgentSecret(input.secretId, () => this.attachAgentSecretToChannelMutation(input));
|
|
247
|
+
}
|
|
248
|
+
async attachAgentSecretToChannelMutation(input) {
|
|
249
|
+
await this.requireAgentSecret(input.actorUserId, input.secretId);
|
|
250
|
+
const sync = await this.repository.attachAgentSecretToChannel(input);
|
|
251
|
+
await this.reconcileSecretBindings({ chatId: input.channelId });
|
|
252
|
+
if (sync)
|
|
253
|
+
await this.publishAgentHint(sync);
|
|
254
|
+
return {
|
|
255
|
+
secret: await this.agentSecret(input.actorUserId, input.secretId),
|
|
256
|
+
...(sync ? { sync } : {}),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async detachAgentSecretFromChannel(input) {
|
|
260
|
+
return this.serializeAgentSecret(input.secretId, () => this.detachAgentSecretFromChannelMutation(input));
|
|
261
|
+
}
|
|
262
|
+
async detachAgentSecretFromChannelMutation(input) {
|
|
263
|
+
await this.requireAgentSecret(input.actorUserId, input.secretId);
|
|
264
|
+
const sync = await this.repository.detachAgentSecretFromChannel(input);
|
|
265
|
+
await this.reconcileSecretBindings({ chatId: input.channelId });
|
|
266
|
+
if (sync)
|
|
267
|
+
await this.publishAgentHint(sync);
|
|
268
|
+
return {
|
|
269
|
+
secret: await this.agentSecret(input.actorUserId, input.secretId),
|
|
270
|
+
...(sync ? { sync } : {}),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async serializeAgentSecret(secretId, action) {
|
|
274
|
+
const previous = this.secretMutations.get(secretId) ?? Promise.resolve();
|
|
275
|
+
const mutation = previous.catch(() => undefined).then(action);
|
|
276
|
+
this.secretMutations.set(secretId, mutation);
|
|
277
|
+
try {
|
|
278
|
+
return await mutation;
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
if (this.secretMutations.get(secretId) === mutation)
|
|
282
|
+
this.secretMutations.delete(secretId);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async ensureAgentBinding(actorUserId, chatId) {
|
|
286
|
+
if (this.stopping)
|
|
287
|
+
return undefined;
|
|
288
|
+
const context = await this.repository.getDirectAgentChatContext(actorUserId, chatId);
|
|
289
|
+
if (!context)
|
|
290
|
+
return undefined;
|
|
291
|
+
if (context.binding) {
|
|
292
|
+
await this.reconcileSecretBindings({
|
|
293
|
+
agentUserId: context.agentUserId,
|
|
294
|
+
chatId,
|
|
295
|
+
});
|
|
296
|
+
return context.binding;
|
|
297
|
+
}
|
|
298
|
+
const key = `${context.agentUserId}:${chatId}`;
|
|
299
|
+
const pending = this.bindingCreations.get(key);
|
|
300
|
+
if (pending)
|
|
301
|
+
return pending;
|
|
302
|
+
const creation = (async () => {
|
|
303
|
+
const sandbox = sandboxDirectories(this.defaultCwd, context.agentUserId, context.privateUserId);
|
|
304
|
+
await Promise.all([
|
|
305
|
+
mkdir(sandbox.home, { recursive: true, mode: 0o700 }),
|
|
306
|
+
mkdir(sandbox.workspace, { recursive: true, mode: 0o700 }),
|
|
307
|
+
]);
|
|
308
|
+
const containerName = agentContainerName();
|
|
309
|
+
await this.docker.createContainer({
|
|
310
|
+
agentUserId: context.agentUserId,
|
|
311
|
+
containerName,
|
|
312
|
+
homeDirectory: sandbox.home,
|
|
313
|
+
imageId: context.image.id,
|
|
314
|
+
imageTag: context.image.dockerTag,
|
|
315
|
+
security: AGENT_CONTAINER_SECURITY,
|
|
316
|
+
workspaceDirectory: sandbox.workspace,
|
|
317
|
+
}, this.shutdown.signal);
|
|
318
|
+
try {
|
|
319
|
+
const session = await this.daemon.createSession(sandbox.workspace, containerName, this.shutdown.signal);
|
|
320
|
+
const binding = await this.repository.bindAgentChat({
|
|
321
|
+
actorUserId,
|
|
322
|
+
agentUserId: context.agentUserId,
|
|
323
|
+
chatId,
|
|
324
|
+
containerName,
|
|
325
|
+
cwd: sandbox.workspace,
|
|
326
|
+
imageId: context.image.id,
|
|
327
|
+
sessionId: session.id,
|
|
328
|
+
});
|
|
329
|
+
if (binding.containerName !== containerName)
|
|
330
|
+
await this.docker.removeContainer(containerName);
|
|
331
|
+
await this.reconcileSecretBindings({
|
|
332
|
+
agentUserId: context.agentUserId,
|
|
333
|
+
chatId,
|
|
334
|
+
});
|
|
335
|
+
return binding;
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
await this.docker.removeContainer(containerName);
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
})();
|
|
342
|
+
this.bindingCreations.set(key, creation);
|
|
343
|
+
try {
|
|
344
|
+
return await creation;
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
this.bindingCreations.delete(key);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async close() {
|
|
351
|
+
this.stopping = true;
|
|
352
|
+
this.shutdown.abort();
|
|
353
|
+
this.pendingImageBuilds.clear();
|
|
354
|
+
for (const stream of this.turnStreams.values()) {
|
|
355
|
+
stream.controller.abort();
|
|
356
|
+
stream.output.close();
|
|
357
|
+
}
|
|
358
|
+
for (const renewal of this.typingRenewals.values())
|
|
359
|
+
clearInterval(renewal.timer);
|
|
360
|
+
this.typingRenewals.clear();
|
|
361
|
+
await Promise.race([
|
|
362
|
+
Promise.allSettled([
|
|
363
|
+
...this.bindingCreations.values(),
|
|
364
|
+
...this.drains.values(),
|
|
365
|
+
...this.imageBuilds.values(),
|
|
366
|
+
...Array.from(this.turnStreams.values(), (stream) => stream.task),
|
|
367
|
+
...(this.queueTask ? [this.queueTask] : []),
|
|
368
|
+
]),
|
|
369
|
+
shutdownDeadline(),
|
|
370
|
+
]);
|
|
371
|
+
await Promise.all([
|
|
372
|
+
this.repository.releaseAgentTurnLeases(this.workerId),
|
|
373
|
+
this.repository.releaseAgentImageBuildLeases(this.workerId),
|
|
374
|
+
]);
|
|
375
|
+
}
|
|
376
|
+
queueImageBuild(imageId) {
|
|
377
|
+
if (this.stopping || this.imageBuilds.has(imageId) || this.pendingImageBuilds.has(imageId))
|
|
378
|
+
return;
|
|
379
|
+
this.pendingImageBuilds.add(imageId);
|
|
380
|
+
this.drainImageBuildQueue();
|
|
381
|
+
}
|
|
382
|
+
drainImageBuildQueue() {
|
|
383
|
+
if (this.stopping || this.activeImageBuilds >= MAX_CONCURRENT_IMAGE_BUILDS)
|
|
384
|
+
return;
|
|
385
|
+
const imageId = this.pendingImageBuilds.values().next().value;
|
|
386
|
+
if (!imageId)
|
|
387
|
+
return;
|
|
388
|
+
this.pendingImageBuilds.delete(imageId);
|
|
389
|
+
this.activeImageBuilds += 1;
|
|
390
|
+
const task = this.buildImage(imageId)
|
|
391
|
+
.catch((error) => {
|
|
392
|
+
if (!this.shutdown.signal.aborted)
|
|
393
|
+
this.onError(error);
|
|
394
|
+
})
|
|
395
|
+
.finally(() => {
|
|
396
|
+
this.imageBuilds.delete(imageId);
|
|
397
|
+
this.activeImageBuilds -= 1;
|
|
398
|
+
this.drainImageBuildQueue();
|
|
399
|
+
});
|
|
400
|
+
this.imageBuilds.set(imageId, task);
|
|
401
|
+
}
|
|
402
|
+
async buildImage(imageId) {
|
|
403
|
+
const claimed = await this.repository.takeAgentImageBuild(imageId, this.workerId);
|
|
404
|
+
if (!claimed)
|
|
405
|
+
return;
|
|
406
|
+
const build = claimed.build;
|
|
407
|
+
await this.publishAgentImageHint(claimed.hint);
|
|
408
|
+
const output = new AgentImageBuildOutput(async ({ lastBuildLogLine, logChunk, progress }) => {
|
|
409
|
+
const hint = await this.repository.recordAgentImageBuildOutput({
|
|
410
|
+
imageId,
|
|
411
|
+
logChunk,
|
|
412
|
+
progress,
|
|
413
|
+
workerId: this.workerId,
|
|
414
|
+
...(lastBuildLogLine === undefined ? {} : { lastBuildLogLine }),
|
|
415
|
+
});
|
|
416
|
+
if (hint)
|
|
417
|
+
await this.publishAgentImageHint(hint);
|
|
418
|
+
else if (!this.shutdown.signal.aborted)
|
|
419
|
+
throw new Error(`Agent image ${imageId} build lease was lost while recording output.`);
|
|
420
|
+
}, this.onError);
|
|
421
|
+
let renewalRunning = false;
|
|
422
|
+
const renewal = setInterval(() => {
|
|
423
|
+
if (renewalRunning || this.shutdown.signal.aborted)
|
|
424
|
+
return;
|
|
425
|
+
renewalRunning = true;
|
|
426
|
+
void this.repository
|
|
427
|
+
.renewAgentImageBuildLease(imageId, this.workerId)
|
|
428
|
+
.catch(this.onError)
|
|
429
|
+
.finally(() => {
|
|
430
|
+
renewalRunning = false;
|
|
431
|
+
});
|
|
432
|
+
}, IMAGE_BUILD_LEASE_RENEW_INTERVAL_MS);
|
|
433
|
+
renewal.unref();
|
|
434
|
+
try {
|
|
435
|
+
const result = await this.docker.buildImage({
|
|
436
|
+
...(build.buildContext ? { buildContext: build.buildContext } : {}),
|
|
437
|
+
dockerfile: build.dockerfile,
|
|
438
|
+
tag: build.dockerTag,
|
|
439
|
+
}, {
|
|
440
|
+
onUpdate: (update) => output.add(update),
|
|
441
|
+
signal: this.shutdown.signal,
|
|
442
|
+
});
|
|
443
|
+
await output.finish();
|
|
444
|
+
const completed = await this.repository.completeAgentImageBuild({
|
|
445
|
+
dockerImageId: result.imageId,
|
|
446
|
+
imageId,
|
|
447
|
+
workerId: this.workerId,
|
|
448
|
+
});
|
|
449
|
+
if (completed)
|
|
450
|
+
await this.publishAgentImageHint(completed);
|
|
451
|
+
else if (!this.shutdown.signal.aborted)
|
|
452
|
+
throw new Error(`Agent image ${imageId} build lease was lost before completion.`);
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
if (this.shutdown.signal.aborted) {
|
|
456
|
+
await output.finish().catch(this.onError);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const message = agentImageBuildError(error);
|
|
460
|
+
output.add({
|
|
461
|
+
logChunk: `${message}\n`,
|
|
462
|
+
progress: output.currentProgress,
|
|
463
|
+
});
|
|
464
|
+
await output.finish().catch(this.onError);
|
|
465
|
+
const failed = await this.repository.failAgentImageBuild({
|
|
466
|
+
error: message,
|
|
467
|
+
imageId,
|
|
468
|
+
workerId: this.workerId,
|
|
469
|
+
});
|
|
470
|
+
if (failed)
|
|
471
|
+
await this.publishAgentImageHint(failed);
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
clearInterval(renewal);
|
|
475
|
+
output.close();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async publishAgentImageHint(hint) {
|
|
479
|
+
await this.publishAgentHint(hint);
|
|
480
|
+
}
|
|
481
|
+
async publishAgentHint(hint) {
|
|
482
|
+
try {
|
|
483
|
+
await this.pubsub.publish(realtimeTopics.server, { type: "sync", ...hint });
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
this.onError(error);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async requireAgentSecret(actorUserId, secretId) {
|
|
490
|
+
await this.repository.authorizeAgentSecretManagement(actorUserId);
|
|
491
|
+
const secrets = await this.daemon.listSecrets(this.shutdown.signal);
|
|
492
|
+
if (!secrets.some((secret) => secret.id === secretId))
|
|
493
|
+
throw new CollaborationError("not_found", "Agent secret was not found");
|
|
494
|
+
}
|
|
495
|
+
async agentSecret(actorUserId, secretId) {
|
|
496
|
+
const { secrets } = await this.listAgentSecrets(actorUserId);
|
|
497
|
+
const secret = secrets.find((candidate) => candidate.id === secretId);
|
|
498
|
+
if (!secret)
|
|
499
|
+
throw new CollaborationError("not_found", "Agent secret was not found");
|
|
500
|
+
return secret;
|
|
501
|
+
}
|
|
502
|
+
async reconcileSecretBindings(input = {}) {
|
|
503
|
+
const bindings = await this.repository.listAgentSecretBindings(input);
|
|
504
|
+
await Promise.all(bindings.map((binding) => this.daemon.reconcileSessionSecrets(binding.sessionId, async () => {
|
|
505
|
+
const [secrets, latestBindings] = await Promise.all([
|
|
506
|
+
this.daemon.listSecrets(this.shutdown.signal),
|
|
507
|
+
this.repository.listAgentSecretBindings({
|
|
508
|
+
agentUserId: binding.agentUserId,
|
|
509
|
+
chatId: binding.chatId,
|
|
510
|
+
}),
|
|
511
|
+
]);
|
|
512
|
+
const managedSecretIds = secrets.map((secret) => secret.id);
|
|
513
|
+
const registered = new Set(managedSecretIds);
|
|
514
|
+
const latest = latestBindings.find((candidate) => candidate.sessionId === binding.sessionId);
|
|
515
|
+
return {
|
|
516
|
+
desiredSecretIds: (latest?.secretIds ?? []).filter((secretId) => registered.has(secretId)),
|
|
517
|
+
managedSecretIds,
|
|
518
|
+
};
|
|
519
|
+
}, this.shutdown.signal)));
|
|
520
|
+
}
|
|
521
|
+
async trackGlobalEvents() {
|
|
522
|
+
let checkpoint = await this.repository.getRigEventCheckpoint();
|
|
523
|
+
let reportedFailure = false;
|
|
524
|
+
while (!this.stopping) {
|
|
525
|
+
try {
|
|
526
|
+
let ignoredSinceCheckpoint = 0;
|
|
527
|
+
await this.daemon.watchGlobalEvents(checkpoint.cursor, async (entry) => {
|
|
528
|
+
if (isRelevantRigEvent(entry.event)) {
|
|
529
|
+
await this.applyGlobalEvent(entry);
|
|
530
|
+
checkpoint = await this.repository.checkpointRigEvent(entry.cursor, ignoredSinceCheckpoint + 1);
|
|
531
|
+
ignoredSinceCheckpoint = 0;
|
|
532
|
+
checkpoint = await this.trimGlobalEventsIfDue(checkpoint);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
ignoredSinceCheckpoint += 1;
|
|
536
|
+
if (ignoredSinceCheckpoint < IGNORED_EVENT_CHECKPOINT_INTERVAL)
|
|
537
|
+
return;
|
|
538
|
+
checkpoint = await this.repository.checkpointRigEvent(entry.cursor, ignoredSinceCheckpoint);
|
|
539
|
+
ignoredSinceCheckpoint = 0;
|
|
540
|
+
checkpoint = await this.trimGlobalEventsIfDue(checkpoint);
|
|
541
|
+
}, this.shutdown.signal);
|
|
542
|
+
reportedFailure = false;
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
if (this.shutdown.signal.aborted)
|
|
546
|
+
return;
|
|
547
|
+
if (!reportedFailure)
|
|
548
|
+
this.onError(error);
|
|
549
|
+
reportedFailure = true;
|
|
550
|
+
await delay(EVENT_RETRY_INTERVAL_MS, this.shutdown.signal);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
async trimGlobalEventsIfDue(checkpoint) {
|
|
555
|
+
if (checkpoint.cursor === undefined || checkpoint.eventsSinceTrim === 0)
|
|
556
|
+
return checkpoint;
|
|
557
|
+
const trimDueByCount = checkpoint.eventsSinceTrim >= TRIM_EVENT_INTERVAL;
|
|
558
|
+
const trimDueByTime = Date.now() - sqliteTimestamp(checkpoint.lastTrimmedAt) >= TRIM_TIME_INTERVAL_MS;
|
|
559
|
+
if (!trimDueByCount && !trimDueByTime)
|
|
560
|
+
return checkpoint;
|
|
561
|
+
await this.daemon.trimGlobalEvents(checkpoint.cursor, this.shutdown.signal);
|
|
562
|
+
return this.repository.markRigEventsTrimmed(checkpoint.cursor);
|
|
563
|
+
}
|
|
564
|
+
async applyGlobalEvent(entry) {
|
|
565
|
+
const event = entry.event;
|
|
566
|
+
const runId = event.data.runId;
|
|
567
|
+
if (event.type === "message_submitted" && runId) {
|
|
568
|
+
const text = messageText(event.data.message);
|
|
569
|
+
if (text)
|
|
570
|
+
await this.repository.attachAgentRun({
|
|
571
|
+
runId,
|
|
572
|
+
sessionId: event.sessionId,
|
|
573
|
+
text,
|
|
574
|
+
});
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (!runId || (event.type !== "run_finished" && event.type !== "run_error"))
|
|
578
|
+
return;
|
|
579
|
+
let turn = await this.repository.getRunningAgentTurn(event.sessionId, runId);
|
|
580
|
+
if (!turn)
|
|
581
|
+
return;
|
|
582
|
+
if (turn.runId === undefined || turn.baselineMessageCount === undefined) {
|
|
583
|
+
const baselineMessageCount = turn.baselineMessageCount ??
|
|
584
|
+
(await this.daemon.submittedTurnBaseline(turn.sessionId, turn.text, this.shutdown.signal));
|
|
585
|
+
const checkpointed = await this.repository.checkpointAgentTurn({
|
|
586
|
+
agentUserId: turn.agentUserId,
|
|
587
|
+
baselineMessageCount,
|
|
588
|
+
runId,
|
|
589
|
+
userMessageId: turn.userMessageId,
|
|
590
|
+
workerId: turn.workerId,
|
|
591
|
+
});
|
|
592
|
+
if (!checkpointed)
|
|
593
|
+
return;
|
|
594
|
+
turn = { ...turn, baselineMessageCount, runId };
|
|
595
|
+
}
|
|
596
|
+
if (event.type === "run_error") {
|
|
597
|
+
await this.failTurn(turn, event.data.errorMessage ?? "Rig run failed.");
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const inspection = await this.daemon.inspectTurn(turn.sessionId, turn.baselineMessageCount ?? 0, turn.text, this.shutdown.signal);
|
|
601
|
+
if (inspection.kind === "completed") {
|
|
602
|
+
await this.completeTurn(turn, inspection.text);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (inspection.kind === "failed") {
|
|
606
|
+
await this.failTurn(turn, inspection.error);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
throw new Error(`Rig run ${runId} finished before its session snapshot was complete.`);
|
|
610
|
+
}
|
|
611
|
+
startDrain(chatId) {
|
|
612
|
+
if (this.stopping || this.drains.has(chatId))
|
|
613
|
+
return;
|
|
614
|
+
const task = this.drainChat(chatId)
|
|
615
|
+
.catch((error) => this.onError(error))
|
|
616
|
+
.finally(async () => {
|
|
617
|
+
this.drains.delete(chatId);
|
|
618
|
+
if (this.stopping)
|
|
619
|
+
return;
|
|
620
|
+
try {
|
|
621
|
+
if (await this.repository.hasRunnableAgentTurn(chatId))
|
|
622
|
+
this.startDrain(chatId);
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
this.onError(error);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
this.drains.set(chatId, task);
|
|
629
|
+
}
|
|
630
|
+
async drainChat(chatId) {
|
|
631
|
+
for (;;) {
|
|
632
|
+
if (this.stopping)
|
|
633
|
+
return;
|
|
634
|
+
const input = await this.repository
|
|
635
|
+
.takeNextAgentTurn(chatId, this.workerId)
|
|
636
|
+
.catch((error) => {
|
|
637
|
+
this.onError(error);
|
|
638
|
+
return undefined;
|
|
639
|
+
});
|
|
640
|
+
if (!input)
|
|
641
|
+
return;
|
|
642
|
+
try {
|
|
643
|
+
await this.startTyping(input);
|
|
644
|
+
const submission = await this.ensureTurnSubmitted(input);
|
|
645
|
+
const inspection = submission.inspection;
|
|
646
|
+
if (inspection.kind === "completed") {
|
|
647
|
+
await this.completeTurn(input, inspection.text);
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (inspection.kind === "failed") {
|
|
651
|
+
await this.failTurn(input, inspection.error);
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
this.startTurnStream(input, submission);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
catch (error) {
|
|
658
|
+
if (this.shutdown.signal.aborted)
|
|
659
|
+
return;
|
|
660
|
+
try {
|
|
661
|
+
await this.failTurn(input, error instanceof Error ? error.message : String(error));
|
|
662
|
+
}
|
|
663
|
+
catch (persistenceError) {
|
|
664
|
+
this.onError(persistenceError);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
async ensureTurnSubmitted(input) {
|
|
671
|
+
let baselineMessageCount = input.baselineMessageCount;
|
|
672
|
+
let lastSessionEventId = input.lastSessionEventId;
|
|
673
|
+
let runId = input.runId;
|
|
674
|
+
if (baselineMessageCount === undefined) {
|
|
675
|
+
if (runId) {
|
|
676
|
+
baselineMessageCount = await this.retryRig(() => this.daemon.submittedTurnBaseline(input.sessionId, input.text, this.shutdown.signal));
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
const checkpoint = await this.retryRig(() => this.daemon.sessionCheckpoint(input.sessionId, this.shutdown.signal));
|
|
680
|
+
baselineMessageCount = checkpoint.messageCount;
|
|
681
|
+
lastSessionEventId = checkpoint.lastEventId;
|
|
682
|
+
}
|
|
683
|
+
const checkpointed = await this.repository.checkpointAgentTurn({
|
|
684
|
+
agentUserId: input.agentUserId,
|
|
685
|
+
baselineMessageCount,
|
|
686
|
+
lastSessionEventId,
|
|
687
|
+
runId,
|
|
688
|
+
userMessageId: input.userMessageId,
|
|
689
|
+
workerId: input.workerId,
|
|
690
|
+
});
|
|
691
|
+
if (!checkpointed)
|
|
692
|
+
throw new AgentTurnStreamStopped(`Agent turn ${input.userMessageId} lease was lost before submission.`);
|
|
693
|
+
}
|
|
694
|
+
const existing = await this.retryRig(() => this.daemon.inspectTurn(input.sessionId, baselineMessageCount, input.text, this.shutdown.signal));
|
|
695
|
+
if (runId || existing.kind !== "not_submitted")
|
|
696
|
+
return {
|
|
697
|
+
inspection: existing,
|
|
698
|
+
...(lastSessionEventId === undefined ? {} : { lastSessionEventId }),
|
|
699
|
+
...(runId === undefined ? {} : { runId }),
|
|
700
|
+
};
|
|
701
|
+
for (;;) {
|
|
702
|
+
let submitted;
|
|
703
|
+
try {
|
|
704
|
+
submitted = await this.daemon.submitTurn(input.sessionId, input.text, this.shutdown.signal);
|
|
705
|
+
}
|
|
706
|
+
catch (error) {
|
|
707
|
+
if (this.shutdown.signal.aborted)
|
|
708
|
+
throw error;
|
|
709
|
+
const recovered = await this.retryRig(() => this.daemon.inspectTurn(input.sessionId, baselineMessageCount, input.text, this.shutdown.signal));
|
|
710
|
+
if (recovered.kind !== "not_submitted")
|
|
711
|
+
return {
|
|
712
|
+
inspection: recovered,
|
|
713
|
+
...(lastSessionEventId === undefined ? {} : { lastSessionEventId }),
|
|
714
|
+
...(runId === undefined ? {} : { runId }),
|
|
715
|
+
};
|
|
716
|
+
if (!isRetryableRigError(error))
|
|
717
|
+
throw error;
|
|
718
|
+
await delay(EVENT_RETRY_INTERVAL_MS, this.shutdown.signal);
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
runId = submitted.runId;
|
|
722
|
+
lastSessionEventId = submitted.eventId;
|
|
723
|
+
const checkpointed = await this.repository.checkpointAgentTurn({
|
|
724
|
+
agentUserId: input.agentUserId,
|
|
725
|
+
baselineMessageCount,
|
|
726
|
+
lastSessionEventId,
|
|
727
|
+
runId,
|
|
728
|
+
userMessageId: input.userMessageId,
|
|
729
|
+
workerId: input.workerId,
|
|
730
|
+
});
|
|
731
|
+
if (!checkpointed)
|
|
732
|
+
throw new AgentTurnStreamStopped(`Agent turn ${input.userMessageId} lease was lost after submission.`);
|
|
733
|
+
return {
|
|
734
|
+
inspection: { kind: "running" },
|
|
735
|
+
lastSessionEventId,
|
|
736
|
+
runId,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
startTurnStream(input, submission) {
|
|
741
|
+
if (this.stopping || this.turnStreams.has(input.userMessageId))
|
|
742
|
+
return;
|
|
743
|
+
const controller = new AbortController();
|
|
744
|
+
let streamFailure;
|
|
745
|
+
let streamFailureReported = false;
|
|
746
|
+
const rememberStreamFailure = (error) => {
|
|
747
|
+
if (error instanceof AgentTurnStreamStopped)
|
|
748
|
+
return;
|
|
749
|
+
streamFailure ??= error;
|
|
750
|
+
if (!streamFailureReported && !this.shutdown.signal.aborted) {
|
|
751
|
+
streamFailureReported = true;
|
|
752
|
+
this.onError(error);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
const output = new AgentReplyStreamOutput(submission.lastSessionEventId, async (update) => {
|
|
756
|
+
const result = await this.repository.streamAgentTurnReply({
|
|
757
|
+
agentUserId: input.agentUserId,
|
|
758
|
+
actorUserId: input.actorUserId,
|
|
759
|
+
eventId: update.eventId,
|
|
760
|
+
expectedEventId: update.expectedEventId,
|
|
761
|
+
sessionId: input.sessionId,
|
|
762
|
+
streamCommittedText: update.streamCommittedText,
|
|
763
|
+
text: update.text,
|
|
764
|
+
userMessageId: input.userMessageId,
|
|
765
|
+
workerId: input.workerId,
|
|
766
|
+
});
|
|
767
|
+
if (!result.applied)
|
|
768
|
+
throw new AgentTurnStreamStopped(`Agent turn ${input.userMessageId} stream lease or cursor was lost.`);
|
|
769
|
+
if (result.hint)
|
|
770
|
+
await this.publishAgentReplyHint(input.chatId, result.hint);
|
|
771
|
+
}, (error) => {
|
|
772
|
+
controller.abort();
|
|
773
|
+
rememberStreamFailure(error);
|
|
774
|
+
});
|
|
775
|
+
let task;
|
|
776
|
+
task = this.consumeTurnStream(input, submission, output, controller.signal)
|
|
777
|
+
.catch((error) => {
|
|
778
|
+
if (!(error instanceof AgentTurnStreamStopped) &&
|
|
779
|
+
!controller.signal.aborted &&
|
|
780
|
+
!this.shutdown.signal.aborted)
|
|
781
|
+
rememberStreamFailure(error);
|
|
782
|
+
})
|
|
783
|
+
.finally(async () => {
|
|
784
|
+
await output.finish().catch(rememberStreamFailure);
|
|
785
|
+
output.close();
|
|
786
|
+
if (this.turnStreams.get(input.userMessageId)?.task === task)
|
|
787
|
+
this.turnStreams.delete(input.userMessageId);
|
|
788
|
+
if (streamFailure !== undefined && !this.shutdown.signal.aborted) {
|
|
789
|
+
try {
|
|
790
|
+
await this.failTurn(input, agentTurnStreamError(streamFailure));
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
this.clearTypingRenewal(input.chatId, input.userMessageId);
|
|
794
|
+
this.onError(error);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
this.turnStreams.set(input.userMessageId, { controller, output, task });
|
|
799
|
+
}
|
|
800
|
+
async consumeTurnStream(input, submission, output, signal) {
|
|
801
|
+
let committedText = input.streamCommittedText;
|
|
802
|
+
let partialText = "";
|
|
803
|
+
let runId = submission.runId;
|
|
804
|
+
let after = submission.lastSessionEventId;
|
|
805
|
+
while (!signal.aborted && !this.shutdown.signal.aborted) {
|
|
806
|
+
try {
|
|
807
|
+
await this.daemon.watchSessionEvents(input.sessionId, after, async (event) => {
|
|
808
|
+
if (signal.aborted)
|
|
809
|
+
return;
|
|
810
|
+
let shouldPersist = false;
|
|
811
|
+
if (!runId && event.type === "message_submitted") {
|
|
812
|
+
const submittedText = messageText(event.data.message);
|
|
813
|
+
if (event.data.runId && submittedText === input.text) {
|
|
814
|
+
runId = event.data.runId;
|
|
815
|
+
await this.repository.attachAgentRun({
|
|
816
|
+
runId,
|
|
817
|
+
sessionId: input.sessionId,
|
|
818
|
+
text: input.text,
|
|
819
|
+
});
|
|
820
|
+
shouldPersist = true;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (runId && event.data.runId === runId) {
|
|
824
|
+
if (event.type === "agent_event") {
|
|
825
|
+
const nextPartial = agentLoopText(event);
|
|
826
|
+
if (nextPartial !== undefined) {
|
|
827
|
+
partialText = nextPartial;
|
|
828
|
+
shouldPersist = true;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else if (event.type === "agent_message") {
|
|
832
|
+
const completed = messageText(event.data.message);
|
|
833
|
+
if (completed) {
|
|
834
|
+
committedText = appendAgentText(committedText, completed);
|
|
835
|
+
partialText = "";
|
|
836
|
+
shouldPersist = true;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (shouldPersist)
|
|
841
|
+
output.add({
|
|
842
|
+
eventId: event.id,
|
|
843
|
+
streamCommittedText: committedText,
|
|
844
|
+
text: appendAgentText(committedText, partialText),
|
|
845
|
+
});
|
|
846
|
+
}, signal);
|
|
847
|
+
after = output.lastEventId;
|
|
848
|
+
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
if (signal.aborted || this.shutdown.signal.aborted)
|
|
851
|
+
return;
|
|
852
|
+
await output.finish();
|
|
853
|
+
after = output.lastEventId;
|
|
854
|
+
if (!isRetryableRigError(error))
|
|
855
|
+
throw error;
|
|
856
|
+
await delay(EVENT_RETRY_INTERVAL_MS, signal);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
async stopTurnStream(input) {
|
|
861
|
+
const stream = this.turnStreams.get(input.userMessageId);
|
|
862
|
+
if (!stream)
|
|
863
|
+
return;
|
|
864
|
+
stream.controller.abort();
|
|
865
|
+
await stream.task;
|
|
866
|
+
}
|
|
867
|
+
async publishAgentReplyHint(chatId, hint) {
|
|
868
|
+
const event = { type: "sync", ...hint };
|
|
869
|
+
try {
|
|
870
|
+
await Promise.all([
|
|
871
|
+
this.pubsub.publish(realtimeTopics.server, event),
|
|
872
|
+
this.pubsub.publish(realtimeTopics.chat(chatId), event),
|
|
873
|
+
]);
|
|
874
|
+
}
|
|
875
|
+
catch (error) {
|
|
876
|
+
this.onError(error);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async retryRig(action) {
|
|
880
|
+
for (;;) {
|
|
881
|
+
try {
|
|
882
|
+
return await action();
|
|
883
|
+
}
|
|
884
|
+
catch (error) {
|
|
885
|
+
if (this.shutdown.signal.aborted || !isRetryableRigError(error))
|
|
886
|
+
throw error;
|
|
887
|
+
await delay(EVENT_RETRY_INTERVAL_MS, this.shutdown.signal);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
async completeTurn(input, text) {
|
|
892
|
+
await this.stopTurnStream(input);
|
|
893
|
+
const result = await this.repository.completeAgentTurn({
|
|
894
|
+
agentUserId: input.agentUserId,
|
|
895
|
+
actorUserId: input.actorUserId,
|
|
896
|
+
sessionId: input.sessionId,
|
|
897
|
+
userMessageId: input.userMessageId,
|
|
898
|
+
text,
|
|
899
|
+
workerId: input.workerId,
|
|
900
|
+
});
|
|
901
|
+
if (!result) {
|
|
902
|
+
this.clearTypingRenewal(input.chatId, input.userMessageId);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
await this.publishAgentReplyHint(input.chatId, result.hint);
|
|
906
|
+
await this.stopTyping(input);
|
|
907
|
+
this.startDrain(input.chatId);
|
|
908
|
+
}
|
|
909
|
+
async failTurn(input, error) {
|
|
910
|
+
await this.stopTurnStream(input);
|
|
911
|
+
const result = await this.repository.failAgentTurn({
|
|
912
|
+
agentUserId: input.agentUserId,
|
|
913
|
+
actorUserId: input.actorUserId,
|
|
914
|
+
error,
|
|
915
|
+
sessionId: input.sessionId,
|
|
916
|
+
userMessageId: input.userMessageId,
|
|
917
|
+
workerId: input.workerId,
|
|
918
|
+
});
|
|
919
|
+
if (!result) {
|
|
920
|
+
this.clearTypingRenewal(input.chatId, input.userMessageId);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
await this.publishAgentReplyHint(input.chatId, result.hint);
|
|
924
|
+
await this.stopTyping(input);
|
|
925
|
+
this.startDrain(input.chatId);
|
|
926
|
+
}
|
|
927
|
+
async startTyping(input) {
|
|
928
|
+
if (this.typingRenewals.has(input.chatId))
|
|
929
|
+
return;
|
|
930
|
+
try {
|
|
931
|
+
await this.publishTyping(input, true);
|
|
932
|
+
}
|
|
933
|
+
catch (error) {
|
|
934
|
+
this.onError(error);
|
|
935
|
+
}
|
|
936
|
+
let renewal;
|
|
937
|
+
const timer = setInterval(() => {
|
|
938
|
+
void this.repository
|
|
939
|
+
.renewAgentTurnLease({
|
|
940
|
+
agentUserId: input.agentUserId,
|
|
941
|
+
userMessageId: input.userMessageId,
|
|
942
|
+
workerId: input.workerId,
|
|
943
|
+
})
|
|
944
|
+
.then(async (renewed) => {
|
|
945
|
+
if (this.typingRenewals.get(input.chatId) !== renewal)
|
|
946
|
+
return;
|
|
947
|
+
if (!renewed) {
|
|
948
|
+
this.clearTypingRenewal(input.chatId, input.userMessageId);
|
|
949
|
+
this.turnStreams.get(input.userMessageId)?.controller.abort();
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
await this.publishTyping(input, true);
|
|
953
|
+
})
|
|
954
|
+
.catch(this.onError);
|
|
955
|
+
}, TYPING_RENEW_INTERVAL_MS);
|
|
956
|
+
timer.unref();
|
|
957
|
+
renewal = { timer, userMessageId: input.userMessageId };
|
|
958
|
+
this.typingRenewals.set(input.chatId, renewal);
|
|
959
|
+
}
|
|
960
|
+
async stopTyping(input) {
|
|
961
|
+
this.clearTypingRenewal(input.chatId, input.userMessageId);
|
|
962
|
+
try {
|
|
963
|
+
await this.publishTyping(input, false);
|
|
964
|
+
}
|
|
965
|
+
catch (error) {
|
|
966
|
+
this.onError(error);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
clearTypingRenewal(chatId, userMessageId) {
|
|
970
|
+
const renewal = this.typingRenewals.get(chatId);
|
|
971
|
+
if (userMessageId && renewal?.userMessageId !== userMessageId)
|
|
972
|
+
return;
|
|
973
|
+
if (renewal)
|
|
974
|
+
clearInterval(renewal.timer);
|
|
975
|
+
this.typingRenewals.delete(chatId);
|
|
976
|
+
}
|
|
977
|
+
publishTyping(input, active) {
|
|
978
|
+
const occurredAt = Date.now();
|
|
979
|
+
return this.pubsub.publish(realtimeTopics.chat(input.chatId), {
|
|
980
|
+
type: "typing",
|
|
981
|
+
chatId: input.chatId,
|
|
982
|
+
userId: input.agentUserId,
|
|
983
|
+
active,
|
|
984
|
+
occurredAt,
|
|
985
|
+
...(active ? { expiresAt: occurredAt + TYPING_TTL_MS } : {}),
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
class AgentTurnStreamStopped extends Error {
|
|
990
|
+
constructor(message) {
|
|
991
|
+
super(message);
|
|
992
|
+
this.name = "AgentTurnStreamStopped";
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
class AgentReplyStreamOutput {
|
|
996
|
+
persistedEventId;
|
|
997
|
+
persist;
|
|
998
|
+
onError;
|
|
999
|
+
activeFlush;
|
|
1000
|
+
failure;
|
|
1001
|
+
flushTimer;
|
|
1002
|
+
pending;
|
|
1003
|
+
persistedTextLength = 0;
|
|
1004
|
+
constructor(persistedEventId, persist, onError) {
|
|
1005
|
+
this.persistedEventId = persistedEventId;
|
|
1006
|
+
this.persist = persist;
|
|
1007
|
+
this.onError = onError;
|
|
1008
|
+
}
|
|
1009
|
+
get lastEventId() {
|
|
1010
|
+
return this.persistedEventId;
|
|
1011
|
+
}
|
|
1012
|
+
add(update) {
|
|
1013
|
+
if (this.failure)
|
|
1014
|
+
return;
|
|
1015
|
+
this.pending = update;
|
|
1016
|
+
if (Math.abs(update.text.length - this.persistedTextLength) >= AGENT_REPLY_FLUSH_CHARACTERS) {
|
|
1017
|
+
this.flushInBackground();
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (!this.flushTimer) {
|
|
1021
|
+
this.flushTimer = setTimeout(() => {
|
|
1022
|
+
this.flushTimer = undefined;
|
|
1023
|
+
this.flushInBackground();
|
|
1024
|
+
}, AGENT_REPLY_FLUSH_INTERVAL_MS);
|
|
1025
|
+
this.flushTimer.unref();
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
async finish() {
|
|
1029
|
+
if (this.flushTimer)
|
|
1030
|
+
clearTimeout(this.flushTimer);
|
|
1031
|
+
this.flushTimer = undefined;
|
|
1032
|
+
for (;;) {
|
|
1033
|
+
if (this.failure)
|
|
1034
|
+
throw this.failure;
|
|
1035
|
+
if (this.activeFlush) {
|
|
1036
|
+
await this.activeFlush;
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (!this.pending)
|
|
1040
|
+
return;
|
|
1041
|
+
await this.flushOnce();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
close() {
|
|
1045
|
+
if (this.flushTimer)
|
|
1046
|
+
clearTimeout(this.flushTimer);
|
|
1047
|
+
this.flushTimer = undefined;
|
|
1048
|
+
}
|
|
1049
|
+
flushInBackground() {
|
|
1050
|
+
void this.flushAvailable().catch(this.onError);
|
|
1051
|
+
}
|
|
1052
|
+
async flushAvailable() {
|
|
1053
|
+
if (this.activeFlush)
|
|
1054
|
+
await this.activeFlush;
|
|
1055
|
+
if (this.pending && !this.failure)
|
|
1056
|
+
await this.flushOnce();
|
|
1057
|
+
}
|
|
1058
|
+
flushOnce() {
|
|
1059
|
+
if (this.activeFlush)
|
|
1060
|
+
return this.activeFlush;
|
|
1061
|
+
const update = this.pending;
|
|
1062
|
+
if (!update)
|
|
1063
|
+
return Promise.resolve();
|
|
1064
|
+
this.pending = undefined;
|
|
1065
|
+
const expectedEventId = this.persistedEventId;
|
|
1066
|
+
const task = this.persist({
|
|
1067
|
+
...update,
|
|
1068
|
+
...(expectedEventId === undefined ? {} : { expectedEventId }),
|
|
1069
|
+
})
|
|
1070
|
+
.then(() => {
|
|
1071
|
+
this.persistedEventId = update.eventId;
|
|
1072
|
+
this.persistedTextLength = update.text.length;
|
|
1073
|
+
})
|
|
1074
|
+
.catch((error) => {
|
|
1075
|
+
this.failure = error;
|
|
1076
|
+
throw error;
|
|
1077
|
+
});
|
|
1078
|
+
this.activeFlush = task.finally(() => {
|
|
1079
|
+
this.activeFlush = undefined;
|
|
1080
|
+
});
|
|
1081
|
+
return this.activeFlush;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
class AgentImageBuildOutput {
|
|
1085
|
+
persist;
|
|
1086
|
+
onError;
|
|
1087
|
+
activeFlush;
|
|
1088
|
+
buffer = "";
|
|
1089
|
+
dirty = false;
|
|
1090
|
+
failure;
|
|
1091
|
+
flushTimer;
|
|
1092
|
+
lastBuildLogLine;
|
|
1093
|
+
partialLine = "";
|
|
1094
|
+
progress = 1;
|
|
1095
|
+
constructor(persist, onError) {
|
|
1096
|
+
this.persist = persist;
|
|
1097
|
+
this.onError = onError;
|
|
1098
|
+
}
|
|
1099
|
+
get currentProgress() {
|
|
1100
|
+
return this.progress;
|
|
1101
|
+
}
|
|
1102
|
+
add(update) {
|
|
1103
|
+
if (this.failure)
|
|
1104
|
+
return;
|
|
1105
|
+
const logChunk = normalizeBuildLog(update.logChunk);
|
|
1106
|
+
if (logChunk) {
|
|
1107
|
+
this.buffer += logChunk;
|
|
1108
|
+
this.readLastLine(logChunk);
|
|
1109
|
+
this.dirty = true;
|
|
1110
|
+
}
|
|
1111
|
+
if (update.progress !== undefined) {
|
|
1112
|
+
const progress = Math.max(1, Math.min(99, Math.trunc(update.progress)));
|
|
1113
|
+
if (progress > this.progress) {
|
|
1114
|
+
this.progress = progress;
|
|
1115
|
+
this.dirty = true;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (!this.dirty)
|
|
1119
|
+
return;
|
|
1120
|
+
if (this.buffer.length >= IMAGE_BUILD_LOG_FLUSH_CHARACTERS) {
|
|
1121
|
+
this.flushInBackground();
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (!this.flushTimer) {
|
|
1125
|
+
this.flushTimer = setTimeout(() => {
|
|
1126
|
+
this.flushTimer = undefined;
|
|
1127
|
+
this.flushInBackground();
|
|
1128
|
+
}, IMAGE_BUILD_LOG_FLUSH_INTERVAL_MS);
|
|
1129
|
+
this.flushTimer.unref();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
async finish() {
|
|
1133
|
+
if (this.flushTimer)
|
|
1134
|
+
clearTimeout(this.flushTimer);
|
|
1135
|
+
this.flushTimer = undefined;
|
|
1136
|
+
for (;;) {
|
|
1137
|
+
if (this.failure)
|
|
1138
|
+
throw this.failure;
|
|
1139
|
+
if (this.activeFlush) {
|
|
1140
|
+
await this.activeFlush;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
if (!this.dirty)
|
|
1144
|
+
return;
|
|
1145
|
+
await this.flushOnce();
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
close() {
|
|
1149
|
+
if (this.flushTimer)
|
|
1150
|
+
clearTimeout(this.flushTimer);
|
|
1151
|
+
this.flushTimer = undefined;
|
|
1152
|
+
}
|
|
1153
|
+
flushInBackground() {
|
|
1154
|
+
void this.flushAvailable().catch(this.onError);
|
|
1155
|
+
}
|
|
1156
|
+
async flushAvailable() {
|
|
1157
|
+
if (this.activeFlush)
|
|
1158
|
+
await this.activeFlush;
|
|
1159
|
+
if (this.dirty && !this.failure)
|
|
1160
|
+
await this.flushOnce();
|
|
1161
|
+
}
|
|
1162
|
+
flushOnce() {
|
|
1163
|
+
if (this.activeFlush)
|
|
1164
|
+
return this.activeFlush;
|
|
1165
|
+
if (!this.dirty)
|
|
1166
|
+
return Promise.resolve();
|
|
1167
|
+
const update = {
|
|
1168
|
+
logChunk: this.buffer,
|
|
1169
|
+
progress: this.progress,
|
|
1170
|
+
...(this.lastBuildLogLine === undefined
|
|
1171
|
+
? {}
|
|
1172
|
+
: { lastBuildLogLine: this.lastBuildLogLine }),
|
|
1173
|
+
};
|
|
1174
|
+
this.buffer = "";
|
|
1175
|
+
this.dirty = false;
|
|
1176
|
+
const task = this.persist(update).catch((error) => {
|
|
1177
|
+
this.failure = error;
|
|
1178
|
+
throw error;
|
|
1179
|
+
});
|
|
1180
|
+
this.activeFlush = task.finally(() => {
|
|
1181
|
+
this.activeFlush = undefined;
|
|
1182
|
+
});
|
|
1183
|
+
return this.activeFlush;
|
|
1184
|
+
}
|
|
1185
|
+
readLastLine(logChunk) {
|
|
1186
|
+
const combined = this.partialLine + logChunk;
|
|
1187
|
+
const lines = combined.split("\n");
|
|
1188
|
+
this.partialLine = combined.endsWith("\n") ? "" : (lines.pop() ?? "");
|
|
1189
|
+
for (const line of lines)
|
|
1190
|
+
this.rememberLine(line);
|
|
1191
|
+
if (this.partialLine)
|
|
1192
|
+
this.rememberLine(this.partialLine);
|
|
1193
|
+
}
|
|
1194
|
+
rememberLine(line) {
|
|
1195
|
+
const trimmed = line.trim();
|
|
1196
|
+
if (!trimmed)
|
|
1197
|
+
return;
|
|
1198
|
+
this.lastBuildLogLine = trimmed.slice(-MAX_BUILD_LOG_LINE_CHARACTERS);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
function isRelevantRigEvent(event) {
|
|
1202
|
+
if (!event.data.runId)
|
|
1203
|
+
return false;
|
|
1204
|
+
return (event.type === "message_submitted" ||
|
|
1205
|
+
event.type === "run_finished" ||
|
|
1206
|
+
event.type === "run_error");
|
|
1207
|
+
}
|
|
1208
|
+
function normalizeBuildLog(value) {
|
|
1209
|
+
return value.replaceAll("\0", "").replaceAll("\r", "\n");
|
|
1210
|
+
}
|
|
1211
|
+
function messageText(message) {
|
|
1212
|
+
return message?.blocks
|
|
1213
|
+
.filter((block) => block.type === "text" && block.text)
|
|
1214
|
+
.map((block) => block.text)
|
|
1215
|
+
.join("");
|
|
1216
|
+
}
|
|
1217
|
+
function agentLoopText(event) {
|
|
1218
|
+
const loopEvent = event.data.event;
|
|
1219
|
+
const message = loopEvent?.partial ?? loopEvent?.message ?? loopEvent?.error;
|
|
1220
|
+
if (!message?.content)
|
|
1221
|
+
return undefined;
|
|
1222
|
+
const textBlocks = message.content.filter((block) => block.type === "text" && block.text !== undefined);
|
|
1223
|
+
if (textBlocks.length === 0)
|
|
1224
|
+
return undefined;
|
|
1225
|
+
return textBlocks.map((block) => block.text ?? "").join("");
|
|
1226
|
+
}
|
|
1227
|
+
function appendAgentText(committedText, nextText) {
|
|
1228
|
+
if (!nextText)
|
|
1229
|
+
return committedText;
|
|
1230
|
+
return committedText ? `${committedText}\n\n${nextText}` : nextText;
|
|
1231
|
+
}
|
|
1232
|
+
function sqliteTimestamp(value) {
|
|
1233
|
+
const normalized = value.includes("T") ? value : value.replace(" ", "T");
|
|
1234
|
+
return Date.parse(/[zZ]|[+-]\d\d:\d\d$/u.test(normalized) ? normalized : `${normalized}Z`);
|
|
1235
|
+
}
|
|
1236
|
+
function agentImageDefinitionHash(dockerfile, buildContext = "") {
|
|
1237
|
+
return createHash("sha256")
|
|
1238
|
+
.update("happy2-agent-image-v1\0")
|
|
1239
|
+
.update(buildContext)
|
|
1240
|
+
.update("\0")
|
|
1241
|
+
.update(dockerfile)
|
|
1242
|
+
.digest("hex");
|
|
1243
|
+
}
|
|
1244
|
+
function agentImageTag(definitionHash) {
|
|
1245
|
+
return `happy2-agent:${definitionHash}`;
|
|
1246
|
+
}
|
|
1247
|
+
function agentContainerName() {
|
|
1248
|
+
return `happy2-agent-${createId()}`;
|
|
1249
|
+
}
|
|
1250
|
+
function sandboxDirectories(root, agentUserId, privateUserId) {
|
|
1251
|
+
const sandbox = join(root, "agents", agentUserId, "users", privateUserId);
|
|
1252
|
+
return { home: join(sandbox, "home"), workspace: join(sandbox, "workspace") };
|
|
1253
|
+
}
|
|
1254
|
+
function agentImageBuildError(error) {
|
|
1255
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1256
|
+
return message.slice(0, 16_000) || "Agent image build failed";
|
|
1257
|
+
}
|
|
1258
|
+
function agentTurnStreamError(error) {
|
|
1259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1260
|
+
return message.slice(0, 16_000) || "Agent reply stream failed";
|
|
1261
|
+
}
|
|
1262
|
+
function shutdownDeadline() {
|
|
1263
|
+
return new Promise((resolve) => {
|
|
1264
|
+
const timer = setTimeout(resolve, 5_000);
|
|
1265
|
+
timer.unref();
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
function delay(milliseconds, signal) {
|
|
1269
|
+
if (signal.aborted)
|
|
1270
|
+
return Promise.resolve();
|
|
1271
|
+
return new Promise((resolve) => {
|
|
1272
|
+
const abort = () => {
|
|
1273
|
+
clearTimeout(timer);
|
|
1274
|
+
resolve();
|
|
1275
|
+
};
|
|
1276
|
+
const timer = setTimeout(() => {
|
|
1277
|
+
signal.removeEventListener("abort", abort);
|
|
1278
|
+
resolve();
|
|
1279
|
+
}, milliseconds);
|
|
1280
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
//# sourceMappingURL=service.js.map
|