@swarmify/agents-cli 1.13.4 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -319
- package/bin/agents.js +2 -0
- package/package.json +13 -79
- package/scripts/postinstall.js +4 -71
- package/CHANGELOG.md +0 -316
- package/LICENSE +0 -21
- package/dist/commands/__tests__/sessions-tail.test.d.ts +0 -2
- package/dist/commands/__tests__/sessions-tail.test.d.ts.map +0 -1
- package/dist/commands/__tests__/sessions-tail.test.js +0 -108
- package/dist/commands/__tests__/sessions-tail.test.js.map +0 -1
- package/dist/commands/__tests__/sessions.test.d.ts +0 -2
- package/dist/commands/__tests__/sessions.test.d.ts.map +0 -1
- package/dist/commands/__tests__/sessions.test.js +0 -636
- package/dist/commands/__tests__/sessions.test.js.map +0 -1
- package/dist/commands/cloud.d.ts +0 -11
- package/dist/commands/cloud.d.ts.map +0 -1
- package/dist/commands/cloud.js +0 -363
- package/dist/commands/cloud.js.map +0 -1
- package/dist/commands/commands.d.ts +0 -12
- package/dist/commands/commands.d.ts.map +0 -1
- package/dist/commands/commands.js +0 -629
- package/dist/commands/commands.js.map +0 -1
- package/dist/commands/daemon.d.ts +0 -11
- package/dist/commands/daemon.d.ts.map +0 -1
- package/dist/commands/daemon.js +0 -119
- package/dist/commands/daemon.js.map +0 -1
- package/dist/commands/drive.d.ts +0 -11
- package/dist/commands/drive.d.ts.map +0 -1
- package/dist/commands/drive.js +0 -175
- package/dist/commands/drive.js.map +0 -1
- package/dist/commands/exec.d.ts +0 -11
- package/dist/commands/exec.d.ts.map +0 -1
- package/dist/commands/exec.js +0 -251
- package/dist/commands/exec.js.map +0 -1
- package/dist/commands/factory.d.ts +0 -11
- package/dist/commands/factory.d.ts.map +0 -1
- package/dist/commands/factory.js +0 -445
- package/dist/commands/factory.js.map +0 -1
- package/dist/commands/fork.d.ts +0 -11
- package/dist/commands/fork.d.ts.map +0 -1
- package/dist/commands/fork.js +0 -147
- package/dist/commands/fork.js.map +0 -1
- package/dist/commands/hooks.d.ts +0 -12
- package/dist/commands/hooks.d.ts.map +0 -1
- package/dist/commands/hooks.js +0 -690
- package/dist/commands/hooks.js.map +0 -1
- package/dist/commands/init.d.ts +0 -15
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -137
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -12
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js +0 -583
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/models.d.ts +0 -11
- package/dist/commands/models.d.ts.map +0 -1
- package/dist/commands/models.js +0 -170
- package/dist/commands/models.js.map +0 -1
- package/dist/commands/packages.d.ts +0 -11
- package/dist/commands/packages.d.ts.map +0 -1
- package/dist/commands/packages.js +0 -551
- package/dist/commands/packages.js.map +0 -1
- package/dist/commands/permissions.d.ts +0 -12
- package/dist/commands/permissions.d.ts.map +0 -1
- package/dist/commands/permissions.js +0 -724
- package/dist/commands/permissions.js.map +0 -1
- package/dist/commands/plugins.d.ts +0 -11
- package/dist/commands/plugins.d.ts.map +0 -1
- package/dist/commands/plugins.js +0 -393
- package/dist/commands/plugins.js.map +0 -1
- package/dist/commands/profiles.d.ts +0 -12
- package/dist/commands/profiles.d.ts.map +0 -1
- package/dist/commands/profiles.js +0 -255
- package/dist/commands/profiles.js.map +0 -1
- package/dist/commands/pty.d.ts +0 -21
- package/dist/commands/pty.d.ts.map +0 -1
- package/dist/commands/pty.js +0 -391
- package/dist/commands/pty.js.map +0 -1
- package/dist/commands/pull.d.ts +0 -11
- package/dist/commands/pull.d.ts.map +0 -1
- package/dist/commands/pull.js +0 -456
- package/dist/commands/pull.js.map +0 -1
- package/dist/commands/push.d.ts +0 -11
- package/dist/commands/push.d.ts.map +0 -1
- package/dist/commands/push.js +0 -188
- package/dist/commands/push.js.map +0 -1
- package/dist/commands/refresh-memory.d.ts +0 -16
- package/dist/commands/refresh-memory.d.ts.map +0 -1
- package/dist/commands/refresh-memory.js +0 -52
- package/dist/commands/refresh-memory.js.map +0 -1
- package/dist/commands/resource-view.d.ts +0 -39
- package/dist/commands/resource-view.d.ts.map +0 -1
- package/dist/commands/resource-view.js +0 -197
- package/dist/commands/resource-view.js.map +0 -1
- package/dist/commands/routines.d.ts +0 -11
- package/dist/commands/routines.d.ts.map +0 -1
- package/dist/commands/routines.js +0 -590
- package/dist/commands/routines.js.map +0 -1
- package/dist/commands/rules.d.ts +0 -12
- package/dist/commands/rules.d.ts.map +0 -1
- package/dist/commands/rules.js +0 -489
- package/dist/commands/rules.js.map +0 -1
- package/dist/commands/secrets.d.ts +0 -11
- package/dist/commands/secrets.d.ts.map +0 -1
- package/dist/commands/secrets.js +0 -352
- package/dist/commands/secrets.js.map +0 -1
- package/dist/commands/sessions-picker.d.ts +0 -18
- package/dist/commands/sessions-picker.d.ts.map +0 -1
- package/dist/commands/sessions-picker.js +0 -265
- package/dist/commands/sessions-picker.js.map +0 -1
- package/dist/commands/sessions-tail.d.ts +0 -20
- package/dist/commands/sessions-tail.d.ts.map +0 -1
- package/dist/commands/sessions-tail.js +0 -236
- package/dist/commands/sessions-tail.js.map +0 -1
- package/dist/commands/sessions.d.ts +0 -18
- package/dist/commands/sessions.d.ts.map +0 -1
- package/dist/commands/sessions.js +0 -1173
- package/dist/commands/sessions.js.map +0 -1
- package/dist/commands/skills.d.ts +0 -12
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js +0 -717
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/status.d.ts +0 -10
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -26
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/subagents.d.ts +0 -11
- package/dist/commands/subagents.d.ts.map +0 -1
- package/dist/commands/subagents.js +0 -361
- package/dist/commands/subagents.js.map +0 -1
- package/dist/commands/sync.d.ts +0 -11
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js +0 -70
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/teams-picker.d.ts +0 -18
- package/dist/commands/teams-picker.d.ts.map +0 -1
- package/dist/commands/teams-picker.js +0 -290
- package/dist/commands/teams-picker.js.map +0 -1
- package/dist/commands/teams.d.ts +0 -18
- package/dist/commands/teams.d.ts.map +0 -1
- package/dist/commands/teams.js +0 -1144
- package/dist/commands/teams.js.map +0 -1
- package/dist/commands/utils.d.ts +0 -45
- package/dist/commands/utils.d.ts.map +0 -1
- package/dist/commands/utils.js +0 -106
- package/dist/commands/utils.js.map +0 -1
- package/dist/commands/versions.d.ts +0 -11
- package/dist/commands/versions.d.ts.map +0 -1
- package/dist/commands/versions.js +0 -701
- package/dist/commands/versions.js.map +0 -1
- package/dist/commands/view.d.ts +0 -43
- package/dist/commands/view.d.ts.map +0 -1
- package/dist/commands/view.js +0 -894
- package/dist/commands/view.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -496
- package/dist/index.js.map +0 -1
- package/dist/lib/__tests__/bugfixes.test.d.ts +0 -2
- package/dist/lib/__tests__/bugfixes.test.d.ts.map +0 -1
- package/dist/lib/__tests__/bugfixes.test.js +0 -192
- package/dist/lib/__tests__/bugfixes.test.js.map +0 -1
- package/dist/lib/__tests__/exec.test.d.ts +0 -2
- package/dist/lib/__tests__/exec.test.d.ts.map +0 -1
- package/dist/lib/__tests__/exec.test.js +0 -446
- package/dist/lib/__tests__/exec.test.js.map +0 -1
- package/dist/lib/__tests__/git-sync.test.d.ts +0 -2
- package/dist/lib/__tests__/git-sync.test.d.ts.map +0 -1
- package/dist/lib/__tests__/git-sync.test.js +0 -138
- package/dist/lib/__tests__/git-sync.test.js.map +0 -1
- package/dist/lib/__tests__/hooks.test.d.ts +0 -2
- package/dist/lib/__tests__/hooks.test.d.ts.map +0 -1
- package/dist/lib/__tests__/hooks.test.js +0 -203
- package/dist/lib/__tests__/hooks.test.js.map +0 -1
- package/dist/lib/__tests__/memory-compile.test.d.ts +0 -2
- package/dist/lib/__tests__/memory-compile.test.d.ts.map +0 -1
- package/dist/lib/__tests__/memory-compile.test.js +0 -95
- package/dist/lib/__tests__/memory-compile.test.js.map +0 -1
- package/dist/lib/__tests__/models.test.d.ts +0 -2
- package/dist/lib/__tests__/models.test.d.ts.map +0 -1
- package/dist/lib/__tests__/models.test.js +0 -239
- package/dist/lib/__tests__/models.test.js.map +0 -1
- package/dist/lib/__tests__/rotate.test.d.ts +0 -2
- package/dist/lib/__tests__/rotate.test.d.ts.map +0 -1
- package/dist/lib/__tests__/rotate.test.js +0 -80
- package/dist/lib/__tests__/rotate.test.js.map +0 -1
- package/dist/lib/__tests__/secrets-bundles.test.d.ts +0 -2
- package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +0 -1
- package/dist/lib/__tests__/secrets-bundles.test.js +0 -104
- package/dist/lib/__tests__/secrets-bundles.test.js.map +0 -1
- package/dist/lib/__tests__/secrets.test.d.ts +0 -2
- package/dist/lib/__tests__/secrets.test.d.ts.map +0 -1
- package/dist/lib/__tests__/secrets.test.js +0 -90
- package/dist/lib/__tests__/secrets.test.js.map +0 -1
- package/dist/lib/__tests__/shims.test.d.ts +0 -2
- package/dist/lib/__tests__/shims.test.d.ts.map +0 -1
- package/dist/lib/__tests__/shims.test.js +0 -57
- package/dist/lib/__tests__/shims.test.js.map +0 -1
- package/dist/lib/__tests__/usage.test.d.ts +0 -2
- package/dist/lib/__tests__/usage.test.d.ts.map +0 -1
- package/dist/lib/__tests__/usage.test.js +0 -220
- package/dist/lib/__tests__/usage.test.js.map +0 -1
- package/dist/lib/__tests__/versions.test.d.ts +0 -2
- package/dist/lib/__tests__/versions.test.d.ts.map +0 -1
- package/dist/lib/__tests__/versions.test.js +0 -63
- package/dist/lib/__tests__/versions.test.js.map +0 -1
- package/dist/lib/agents.d.ts +0 -158
- package/dist/lib/agents.d.ts.map +0 -1
- package/dist/lib/agents.js +0 -1159
- package/dist/lib/agents.js.map +0 -1
- package/dist/lib/artifact-actions.d.ts +0 -27
- package/dist/lib/artifact-actions.d.ts.map +0 -1
- package/dist/lib/artifact-actions.js +0 -58
- package/dist/lib/artifact-actions.js.map +0 -1
- package/dist/lib/cloud/codex.d.ts +0 -26
- package/dist/lib/cloud/codex.d.ts.map +0 -1
- package/dist/lib/cloud/codex.js +0 -237
- package/dist/lib/cloud/codex.js.map +0 -1
- package/dist/lib/cloud/factory.d.ts +0 -32
- package/dist/lib/cloud/factory.d.ts.map +0 -1
- package/dist/lib/cloud/factory.js +0 -43
- package/dist/lib/cloud/factory.js.map +0 -1
- package/dist/lib/cloud/registry.d.ts +0 -16
- package/dist/lib/cloud/registry.d.ts.map +0 -1
- package/dist/lib/cloud/registry.js +0 -68
- package/dist/lib/cloud/registry.js.map +0 -1
- package/dist/lib/cloud/rush.d.ts +0 -37
- package/dist/lib/cloud/rush.d.ts.map +0 -1
- package/dist/lib/cloud/rush.js +0 -230
- package/dist/lib/cloud/rush.js.map +0 -1
- package/dist/lib/cloud/rush.test.d.ts +0 -2
- package/dist/lib/cloud/rush.test.d.ts.map +0 -1
- package/dist/lib/cloud/rush.test.js +0 -63
- package/dist/lib/cloud/rush.test.js.map +0 -1
- package/dist/lib/cloud/store.d.ts +0 -23
- package/dist/lib/cloud/store.d.ts.map +0 -1
- package/dist/lib/cloud/store.js +0 -116
- package/dist/lib/cloud/store.js.map +0 -1
- package/dist/lib/cloud/stream.d.ts +0 -24
- package/dist/lib/cloud/stream.d.ts.map +0 -1
- package/dist/lib/cloud/stream.js +0 -145
- package/dist/lib/cloud/stream.js.map +0 -1
- package/dist/lib/cloud/types.d.ts +0 -109
- package/dist/lib/cloud/types.d.ts.map +0 -1
- package/dist/lib/cloud/types.js +0 -33
- package/dist/lib/cloud/types.js.map +0 -1
- package/dist/lib/cloud/types.test.d.ts +0 -2
- package/dist/lib/cloud/types.test.d.ts.map +0 -1
- package/dist/lib/cloud/types.test.js +0 -41
- package/dist/lib/cloud/types.test.js.map +0 -1
- package/dist/lib/commands.d.ts +0 -141
- package/dist/lib/commands.d.ts.map +0 -1
- package/dist/lib/commands.js +0 -514
- package/dist/lib/commands.js.map +0 -1
- package/dist/lib/convert.d.ts +0 -21
- package/dist/lib/convert.d.ts.map +0 -1
- package/dist/lib/convert.js +0 -54
- package/dist/lib/convert.js.map +0 -1
- package/dist/lib/daemon.d.ts +0 -43
- package/dist/lib/daemon.d.ts.map +0 -1
- package/dist/lib/daemon.js +0 -332
- package/dist/lib/daemon.js.map +0 -1
- package/dist/lib/drive-sync.d.ts +0 -46
- package/dist/lib/drive-sync.d.ts.map +0 -1
- package/dist/lib/drive-sync.js +0 -209
- package/dist/lib/drive-sync.js.map +0 -1
- package/dist/lib/exec.d.ts +0 -101
- package/dist/lib/exec.d.ts.map +0 -1
- package/dist/lib/exec.js +0 -450
- package/dist/lib/exec.js.map +0 -1
- package/dist/lib/factory/__tests__/config.test.d.ts +0 -2
- package/dist/lib/factory/__tests__/config.test.d.ts.map +0 -1
- package/dist/lib/factory/__tests__/config.test.js +0 -128
- package/dist/lib/factory/__tests__/config.test.js.map +0 -1
- package/dist/lib/factory/config.d.ts +0 -49
- package/dist/lib/factory/config.d.ts.map +0 -1
- package/dist/lib/factory/config.js +0 -127
- package/dist/lib/factory/config.js.map +0 -1
- package/dist/lib/factory.d.ts +0 -57
- package/dist/lib/factory.d.ts.map +0 -1
- package/dist/lib/factory.js +0 -107
- package/dist/lib/factory.js.map +0 -1
- package/dist/lib/git.d.ts +0 -157
- package/dist/lib/git.d.ts.map +0 -1
- package/dist/lib/git.js +0 -647
- package/dist/lib/git.js.map +0 -1
- package/dist/lib/help.d.ts +0 -10
- package/dist/lib/help.d.ts.map +0 -1
- package/dist/lib/help.js +0 -66
- package/dist/lib/help.js.map +0 -1
- package/dist/lib/hooks.d.ts +0 -125
- package/dist/lib/hooks.d.ts.map +0 -1
- package/dist/lib/hooks.js +0 -846
- package/dist/lib/hooks.js.map +0 -1
- package/dist/lib/ledger/__tests__/local.test.d.ts +0 -2
- package/dist/lib/ledger/__tests__/local.test.d.ts.map +0 -1
- package/dist/lib/ledger/__tests__/local.test.js +0 -177
- package/dist/lib/ledger/__tests__/local.test.js.map +0 -1
- package/dist/lib/ledger/__tests__/sync.test.d.ts +0 -2
- package/dist/lib/ledger/__tests__/sync.test.d.ts.map +0 -1
- package/dist/lib/ledger/__tests__/sync.test.js +0 -117
- package/dist/lib/ledger/__tests__/sync.test.js.map +0 -1
- package/dist/lib/ledger/index.d.ts +0 -18
- package/dist/lib/ledger/index.d.ts.map +0 -1
- package/dist/lib/ledger/index.js +0 -32
- package/dist/lib/ledger/index.js.map +0 -1
- package/dist/lib/ledger/local.d.ts +0 -22
- package/dist/lib/ledger/local.d.ts.map +0 -1
- package/dist/lib/ledger/local.js +0 -333
- package/dist/lib/ledger/local.js.map +0 -1
- package/dist/lib/ledger/r2.d.ts +0 -41
- package/dist/lib/ledger/r2.d.ts.map +0 -1
- package/dist/lib/ledger/r2.js +0 -335
- package/dist/lib/ledger/r2.js.map +0 -1
- package/dist/lib/ledger/sync.d.ts +0 -33
- package/dist/lib/ledger/sync.d.ts.map +0 -1
- package/dist/lib/ledger/sync.js +0 -106
- package/dist/lib/ledger/sync.js.map +0 -1
- package/dist/lib/ledger/types.d.ts +0 -100
- package/dist/lib/ledger/types.d.ts.map +0 -1
- package/dist/lib/ledger/types.js +0 -21
- package/dist/lib/ledger/types.js.map +0 -1
- package/dist/lib/manifest.d.ts +0 -14
- package/dist/lib/manifest.d.ts.map +0 -1
- package/dist/lib/manifest.js +0 -48
- package/dist/lib/manifest.js.map +0 -1
- package/dist/lib/markdown.d.ts +0 -5
- package/dist/lib/markdown.d.ts.map +0 -1
- package/dist/lib/markdown.js +0 -17
- package/dist/lib/markdown.js.map +0 -1
- package/dist/lib/mcp.d.ts +0 -64
- package/dist/lib/mcp.d.ts.map +0 -1
- package/dist/lib/mcp.js +0 -327
- package/dist/lib/mcp.js.map +0 -1
- package/dist/lib/memory-compile.d.ts +0 -65
- package/dist/lib/memory-compile.d.ts.map +0 -1
- package/dist/lib/memory-compile.js +0 -174
- package/dist/lib/memory-compile.js.map +0 -1
- package/dist/lib/memory.d.ts +0 -64
- package/dist/lib/memory.d.ts.map +0 -1
- package/dist/lib/memory.js +0 -275
- package/dist/lib/memory.js.map +0 -1
- package/dist/lib/models.d.ts +0 -98
- package/dist/lib/models.d.ts.map +0 -1
- package/dist/lib/models.js +0 -728
- package/dist/lib/models.js.map +0 -1
- package/dist/lib/permissions.d.ts +0 -227
- package/dist/lib/permissions.d.ts.map +0 -1
- package/dist/lib/permissions.js +0 -1085
- package/dist/lib/permissions.js.map +0 -1
- package/dist/lib/picker.d.ts +0 -27
- package/dist/lib/picker.d.ts.map +0 -1
- package/dist/lib/picker.js +0 -110
- package/dist/lib/picker.js.map +0 -1
- package/dist/lib/plugins.d.ts +0 -80
- package/dist/lib/plugins.d.ts.map +0 -1
- package/dist/lib/plugins.js +0 -556
- package/dist/lib/plugins.js.map +0 -1
- package/dist/lib/profiles-keychain.d.ts +0 -11
- package/dist/lib/profiles-keychain.d.ts.map +0 -1
- package/dist/lib/profiles-keychain.js +0 -14
- package/dist/lib/profiles-keychain.js.map +0 -1
- package/dist/lib/profiles-presets.d.ts +0 -25
- package/dist/lib/profiles-presets.d.ts.map +0 -1
- package/dist/lib/profiles-presets.js +0 -104
- package/dist/lib/profiles-presets.js.map +0 -1
- package/dist/lib/profiles.d.ts +0 -70
- package/dist/lib/profiles.d.ts.map +0 -1
- package/dist/lib/profiles.js +0 -145
- package/dist/lib/profiles.js.map +0 -1
- package/dist/lib/pty-client.d.ts +0 -23
- package/dist/lib/pty-client.d.ts.map +0 -1
- package/dist/lib/pty-client.js +0 -181
- package/dist/lib/pty-client.js.map +0 -1
- package/dist/lib/pty-server.d.ts +0 -21
- package/dist/lib/pty-server.d.ts.map +0 -1
- package/dist/lib/pty-server.js +0 -427
- package/dist/lib/pty-server.js.map +0 -1
- package/dist/lib/registry.d.ts +0 -45
- package/dist/lib/registry.d.ts.map +0 -1
- package/dist/lib/registry.js +0 -220
- package/dist/lib/registry.js.map +0 -1
- package/dist/lib/resources.d.ts +0 -55
- package/dist/lib/resources.d.ts.map +0 -1
- package/dist/lib/resources.js +0 -103
- package/dist/lib/resources.js.map +0 -1
- package/dist/lib/rotate.d.ts +0 -58
- package/dist/lib/rotate.d.ts.map +0 -1
- package/dist/lib/rotate.js +0 -93
- package/dist/lib/rotate.js.map +0 -1
- package/dist/lib/routines.d.ts +0 -99
- package/dist/lib/routines.d.ts.map +0 -1
- package/dist/lib/routines.js +0 -352
- package/dist/lib/routines.js.map +0 -1
- package/dist/lib/runner.d.ts +0 -26
- package/dist/lib/runner.d.ts.map +0 -1
- package/dist/lib/runner.js +0 -325
- package/dist/lib/runner.js.map +0 -1
- package/dist/lib/sandbox.d.ts +0 -26
- package/dist/lib/sandbox.d.ts.map +0 -1
- package/dist/lib/sandbox.js +0 -218
- package/dist/lib/sandbox.js.map +0 -1
- package/dist/lib/scheduler.d.ts +0 -26
- package/dist/lib/scheduler.d.ts.map +0 -1
- package/dist/lib/scheduler.js +0 -77
- package/dist/lib/scheduler.js.map +0 -1
- package/dist/lib/secrets-bundles.d.ts +0 -38
- package/dist/lib/secrets-bundles.d.ts.map +0 -1
- package/dist/lib/secrets-bundles.js +0 -176
- package/dist/lib/secrets-bundles.js.map +0 -1
- package/dist/lib/secrets.d.ts +0 -53
- package/dist/lib/secrets.d.ts.map +0 -1
- package/dist/lib/secrets.js +0 -140
- package/dist/lib/secrets.js.map +0 -1
- package/dist/lib/session/__tests__/db.test.d.ts +0 -2
- package/dist/lib/session/__tests__/db.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/db.test.js +0 -54
- package/dist/lib/session/__tests__/db.test.js.map +0 -1
- package/dist/lib/session/__tests__/discover.test.d.ts +0 -2
- package/dist/lib/session/__tests__/discover.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/discover.test.js +0 -63
- package/dist/lib/session/__tests__/discover.test.js.map +0 -1
- package/dist/lib/session/__tests__/prompt.test.d.ts +0 -2
- package/dist/lib/session/__tests__/prompt.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/prompt.test.js +0 -44
- package/dist/lib/session/__tests__/prompt.test.js.map +0 -1
- package/dist/lib/session/__tests__/render.test.d.ts +0 -2
- package/dist/lib/session/__tests__/render.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/render.test.js +0 -602
- package/dist/lib/session/__tests__/render.test.js.map +0 -1
- package/dist/lib/session/active.d.ts +0 -44
- package/dist/lib/session/active.d.ts.map +0 -1
- package/dist/lib/session/active.js +0 -379
- package/dist/lib/session/active.js.map +0 -1
- package/dist/lib/session/artifacts.d.ts +0 -15
- package/dist/lib/session/artifacts.d.ts.map +0 -1
- package/dist/lib/session/artifacts.js +0 -86
- package/dist/lib/session/artifacts.js.map +0 -1
- package/dist/lib/session/db.d.ts +0 -140
- package/dist/lib/session/db.d.ts.map +0 -1
- package/dist/lib/session/db.js +0 -599
- package/dist/lib/session/db.js.map +0 -1
- package/dist/lib/session/discover.d.ts +0 -72
- package/dist/lib/session/discover.d.ts.map +0 -1
- package/dist/lib/session/discover.js +0 -1315
- package/dist/lib/session/discover.js.map +0 -1
- package/dist/lib/session/parse.d.ts +0 -34
- package/dist/lib/session/parse.d.ts.map +0 -1
- package/dist/lib/session/parse.js +0 -663
- package/dist/lib/session/parse.js.map +0 -1
- package/dist/lib/session/prompt.d.ts +0 -13
- package/dist/lib/session/prompt.d.ts.map +0 -1
- package/dist/lib/session/prompt.js +0 -79
- package/dist/lib/session/prompt.js.map +0 -1
- package/dist/lib/session/prompt.test.d.ts +0 -2
- package/dist/lib/session/prompt.test.d.ts.map +0 -1
- package/dist/lib/session/prompt.test.js +0 -57
- package/dist/lib/session/prompt.test.js.map +0 -1
- package/dist/lib/session/render.d.ts +0 -103
- package/dist/lib/session/render.d.ts.map +0 -1
- package/dist/lib/session/render.js +0 -798
- package/dist/lib/session/render.js.map +0 -1
- package/dist/lib/session/team-filter.d.ts +0 -35
- package/dist/lib/session/team-filter.d.ts.map +0 -1
- package/dist/lib/session/team-filter.js +0 -75
- package/dist/lib/session/team-filter.js.map +0 -1
- package/dist/lib/session/team-filter.test.d.ts +0 -2
- package/dist/lib/session/team-filter.test.d.ts.map +0 -1
- package/dist/lib/session/team-filter.test.js +0 -157
- package/dist/lib/session/team-filter.test.js.map +0 -1
- package/dist/lib/session/types.d.ts +0 -84
- package/dist/lib/session/types.d.ts.map +0 -1
- package/dist/lib/session/types.js +0 -11
- package/dist/lib/session/types.js.map +0 -1
- package/dist/lib/shims.d.ts +0 -272
- package/dist/lib/shims.d.ts.map +0 -1
- package/dist/lib/shims.js +0 -1322
- package/dist/lib/shims.js.map +0 -1
- package/dist/lib/skills.d.ts +0 -142
- package/dist/lib/skills.d.ts.map +0 -1
- package/dist/lib/skills.js +0 -791
- package/dist/lib/skills.js.map +0 -1
- package/dist/lib/state.d.ts +0 -87
- package/dist/lib/state.d.ts.map +0 -1
- package/dist/lib/state.js +0 -333
- package/dist/lib/state.js.map +0 -1
- package/dist/lib/subagents.d.ts +0 -84
- package/dist/lib/subagents.d.ts.map +0 -1
- package/dist/lib/subagents.js +0 -410
- package/dist/lib/subagents.js.map +0 -1
- package/dist/lib/teams/__tests__/oracle.test.d.ts +0 -2
- package/dist/lib/teams/__tests__/oracle.test.d.ts.map +0 -1
- package/dist/lib/teams/__tests__/oracle.test.js +0 -89
- package/dist/lib/teams/__tests__/oracle.test.js.map +0 -1
- package/dist/lib/teams/__tests__/supervisor.test.d.ts +0 -2
- package/dist/lib/teams/__tests__/supervisor.test.d.ts.map +0 -1
- package/dist/lib/teams/__tests__/supervisor.test.js +0 -179
- package/dist/lib/teams/__tests__/supervisor.test.js.map +0 -1
- package/dist/lib/teams/agents.d.ts +0 -247
- package/dist/lib/teams/agents.d.ts.map +0 -1
- package/dist/lib/teams/agents.js +0 -1244
- package/dist/lib/teams/agents.js.map +0 -1
- package/dist/lib/teams/api.d.ts +0 -91
- package/dist/lib/teams/api.d.ts.map +0 -1
- package/dist/lib/teams/api.js +0 -239
- package/dist/lib/teams/api.js.map +0 -1
- package/dist/lib/teams/cloud.d.ts +0 -11
- package/dist/lib/teams/cloud.d.ts.map +0 -1
- package/dist/lib/teams/cloud.js +0 -169
- package/dist/lib/teams/cloud.js.map +0 -1
- package/dist/lib/teams/debug.d.ts +0 -8
- package/dist/lib/teams/debug.d.ts.map +0 -1
- package/dist/lib/teams/debug.js +0 -12
- package/dist/lib/teams/debug.js.map +0 -1
- package/dist/lib/teams/file_ops.d.ts +0 -13
- package/dist/lib/teams/file_ops.d.ts.map +0 -1
- package/dist/lib/teams/file_ops.js +0 -66
- package/dist/lib/teams/file_ops.js.map +0 -1
- package/dist/lib/teams/index.d.ts +0 -16
- package/dist/lib/teams/index.d.ts.map +0 -1
- package/dist/lib/teams/index.js +0 -15
- package/dist/lib/teams/index.js.map +0 -1
- package/dist/lib/teams/oracle.d.ts +0 -20
- package/dist/lib/teams/oracle.d.ts.map +0 -1
- package/dist/lib/teams/oracle.js +0 -59
- package/dist/lib/teams/oracle.js.map +0 -1
- package/dist/lib/teams/parsers.d.ts +0 -9
- package/dist/lib/teams/parsers.d.ts.map +0 -1
- package/dist/lib/teams/parsers.js +0 -837
- package/dist/lib/teams/parsers.js.map +0 -1
- package/dist/lib/teams/persistence.d.ts +0 -43
- package/dist/lib/teams/persistence.d.ts.map +0 -1
- package/dist/lib/teams/persistence.js +0 -299
- package/dist/lib/teams/persistence.js.map +0 -1
- package/dist/lib/teams/ralph.d.ts +0 -8
- package/dist/lib/teams/ralph.d.ts.map +0 -1
- package/dist/lib/teams/ralph.js +0 -59
- package/dist/lib/teams/ralph.js.map +0 -1
- package/dist/lib/teams/registry.d.ts +0 -18
- package/dist/lib/teams/registry.d.ts.map +0 -1
- package/dist/lib/teams/registry.js +0 -68
- package/dist/lib/teams/registry.js.map +0 -1
- package/dist/lib/teams/summarizer.d.ts +0 -73
- package/dist/lib/teams/summarizer.d.ts.map +0 -1
- package/dist/lib/teams/summarizer.js +0 -780
- package/dist/lib/teams/summarizer.js.map +0 -1
- package/dist/lib/teams/supervisor.d.ts +0 -49
- package/dist/lib/teams/supervisor.d.ts.map +0 -1
- package/dist/lib/teams/supervisor.js +0 -74
- package/dist/lib/teams/supervisor.js.map +0 -1
- package/dist/lib/template.d.ts +0 -27
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js +0 -60
- package/dist/lib/template.js.map +0 -1
- package/dist/lib/types.d.ts +0 -331
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js +0 -29
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/usage.d.ts +0 -105
- package/dist/lib/usage.d.ts.map +0 -1
- package/dist/lib/usage.js +0 -686
- package/dist/lib/usage.js.map +0 -1
- package/dist/lib/versions.d.ts +0 -253
- package/dist/lib/versions.d.ts.map +0 -1
- package/dist/lib/versions.js +0 -1796
- package/dist/lib/versions.js.map +0 -1
- package/scripts/rebuild-sqlite.sh +0 -46
package/dist/lib/teams/agents.js
DELETED
|
@@ -1,1244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Teams agent lifecycle management.
|
|
3
|
-
*
|
|
4
|
-
* Defines the AgentProcess and AgentManager classes that handle spawning,
|
|
5
|
-
* monitoring, stopping, and persisting teammate processes across all supported
|
|
6
|
-
* agent CLIs (Claude, Codex, Gemini, Cursor, OpenCode). Supports DAG-based
|
|
7
|
-
* dependency scheduling via --after, per-teammate model/effort overrides, and
|
|
8
|
-
* multiple permission modes (plan, edit, full).
|
|
9
|
-
*/
|
|
10
|
-
import { spawn, execSync } from 'child_process';
|
|
11
|
-
import * as fs from 'fs/promises';
|
|
12
|
-
import * as path from 'path';
|
|
13
|
-
import * as os from 'os';
|
|
14
|
-
import { randomUUID } from 'crypto';
|
|
15
|
-
import { resolveAgentsDir } from './persistence.js';
|
|
16
|
-
import { normalizeEvents } from './parsers.js';
|
|
17
|
-
import { debug } from './debug.js';
|
|
18
|
-
import { buildReasoningFlags } from '../models.js';
|
|
19
|
-
/**
|
|
20
|
-
* Compute the Lowest Common Ancestor (LCA) of multiple file paths.
|
|
21
|
-
* Returns the deepest common directory shared by all paths.
|
|
22
|
-
* Returns null if paths is empty or paths have no common ancestor (different roots).
|
|
23
|
-
*/
|
|
24
|
-
export function computePathLCA(paths) {
|
|
25
|
-
const validPaths = paths.filter(p => p && p.trim());
|
|
26
|
-
if (validPaths.length === 0)
|
|
27
|
-
return null;
|
|
28
|
-
if (validPaths.length === 1)
|
|
29
|
-
return validPaths[0];
|
|
30
|
-
// Normalize and split all paths into segments
|
|
31
|
-
const splitPaths = validPaths.map(p => {
|
|
32
|
-
const normalized = path.resolve(p);
|
|
33
|
-
// Split by path separator, filter empty segments
|
|
34
|
-
return normalized.split(path.sep).filter(seg => seg);
|
|
35
|
-
});
|
|
36
|
-
// Find minimum length
|
|
37
|
-
const minLen = Math.min(...splitPaths.map(p => p.length));
|
|
38
|
-
// Find common prefix
|
|
39
|
-
const commonSegments = [];
|
|
40
|
-
for (let i = 0; i < minLen; i++) {
|
|
41
|
-
const segment = splitPaths[0][i];
|
|
42
|
-
const allMatch = splitPaths.every(p => p[i] === segment);
|
|
43
|
-
if (allMatch) {
|
|
44
|
-
commonSegments.push(segment);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (commonSegments.length === 0)
|
|
51
|
-
return null;
|
|
52
|
-
// Reconstruct path (add leading separator for absolute paths)
|
|
53
|
-
const lca = path.sep + commonSegments.join(path.sep);
|
|
54
|
-
return lca;
|
|
55
|
-
}
|
|
56
|
-
/** Lifecycle status of a teammate process. */
|
|
57
|
-
export var AgentStatus;
|
|
58
|
-
(function (AgentStatus) {
|
|
59
|
-
AgentStatus["PENDING"] = "pending";
|
|
60
|
-
AgentStatus["RUNNING"] = "running";
|
|
61
|
-
AgentStatus["COMPLETED"] = "completed";
|
|
62
|
-
AgentStatus["FAILED"] = "failed";
|
|
63
|
-
AgentStatus["STOPPED"] = "stopped";
|
|
64
|
-
})(AgentStatus || (AgentStatus = {}));
|
|
65
|
-
export const VALID_TASK_TYPES = [
|
|
66
|
-
'plan', 'implement', 'test', 'review', 'bugfix', 'docs',
|
|
67
|
-
];
|
|
68
|
-
/**
|
|
69
|
-
* Walk the `after` chain from `startName` within the given map; returns true
|
|
70
|
-
* if `targetName` appears anywhere in the transitive dependency closure.
|
|
71
|
-
* Used to detect cycles before adding a new --after edge.
|
|
72
|
-
*/
|
|
73
|
-
function hasTransitiveDep(byName, startName, targetName, seen = new Set()) {
|
|
74
|
-
if (seen.has(startName))
|
|
75
|
-
return false;
|
|
76
|
-
seen.add(startName);
|
|
77
|
-
const node = byName.get(startName);
|
|
78
|
-
if (!node)
|
|
79
|
-
return false;
|
|
80
|
-
for (const dep of node.after) {
|
|
81
|
-
if (dep === targetName)
|
|
82
|
-
return true;
|
|
83
|
-
if (hasTransitiveDep(byName, dep, targetName, seen))
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
// Base commands for plan mode (read-only, may prompt for confirmation)
|
|
89
|
-
export const AGENT_COMMANDS = {
|
|
90
|
-
codex: ['codex', 'exec', '--sandbox', 'workspace-write', '{prompt}', '--json'],
|
|
91
|
-
cursor: ['cursor-agent', '-p', '--output-format', 'stream-json', '{prompt}'],
|
|
92
|
-
gemini: ['gemini', '{prompt}', '--output-format', 'stream-json', '--approval-mode', 'plan'],
|
|
93
|
-
claude: ['claude', '-p', '--verbose', '{prompt}', '--output-format', 'stream-json', '--permission-mode', 'plan'],
|
|
94
|
-
opencode: ['opencode', 'run', '--format', 'json', '{prompt}'],
|
|
95
|
-
};
|
|
96
|
-
// Minimal defaults — no per-effort model map. Configs on disk may still have
|
|
97
|
-
// a model pinned; launchProcess picks it up from agent.model when set.
|
|
98
|
-
function loadDefaultAgentConfigs() {
|
|
99
|
-
return {
|
|
100
|
-
claude: { command: 'claude -p \'{prompt}\' --output-format stream-json --json', enabled: true, model: null, provider: 'anthropic' },
|
|
101
|
-
codex: { command: 'codex exec --sandbox workspace-write \'{prompt}\' --json', enabled: true, model: null, provider: 'openai' },
|
|
102
|
-
gemini: { command: 'gemini \'{prompt}\' --output-format stream-json', enabled: true, model: null, provider: 'google' },
|
|
103
|
-
cursor: { command: 'cursor-agent -p --output-format stream-json \'{prompt}\'', enabled: true, model: null, provider: 'custom' },
|
|
104
|
-
opencode: { command: 'opencode run --format json \'{prompt}\'', enabled: true, model: null, provider: 'custom' },
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
// Suffix appended to all prompts to ensure agents provide a summary
|
|
108
|
-
const PROMPT_SUFFIX = `
|
|
109
|
-
|
|
110
|
-
When you're done, provide a brief summary of:
|
|
111
|
-
1. What you did (1-2 sentences)
|
|
112
|
-
2. Key files modified and why
|
|
113
|
-
3. Any important classes, functions, or components you added/changed`;
|
|
114
|
-
// Prefix for Claude agents in plan mode - explains the headless plan mode restrictions
|
|
115
|
-
const CLAUDE_PLAN_MODE_PREFIX = `You are running in HEADLESS PLAN MODE. This mode works like normal plan mode with one exception: you cannot write to ~/.claude/plans/ directory. Instead of writing a plan file, output your complete plan/response as your final message.
|
|
116
|
-
|
|
117
|
-
`;
|
|
118
|
-
const VALID_MODES = ['plan', 'edit', 'full'];
|
|
119
|
-
function normalizeModeValue(modeValue) {
|
|
120
|
-
if (!modeValue)
|
|
121
|
-
return null;
|
|
122
|
-
const normalized = modeValue.trim().toLowerCase();
|
|
123
|
-
if (VALID_MODES.includes(normalized)) {
|
|
124
|
-
return normalized;
|
|
125
|
-
}
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
function defaultModeFromEnv() {
|
|
129
|
-
for (const envVar of ['AGENTS_MCP_MODE', 'AGENTS_MCP_DEFAULT_MODE']) {
|
|
130
|
-
const rawValue = process.env[envVar];
|
|
131
|
-
const parsed = normalizeModeValue(rawValue);
|
|
132
|
-
if (parsed) {
|
|
133
|
-
return parsed;
|
|
134
|
-
}
|
|
135
|
-
if (rawValue) {
|
|
136
|
-
console.warn(`Invalid ${envVar}='${rawValue}'. Use 'plan' or 'edit'. Falling back to plan mode.`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return 'plan';
|
|
140
|
-
}
|
|
141
|
-
function coerceDate(value) {
|
|
142
|
-
if (value === null || value === undefined)
|
|
143
|
-
return null;
|
|
144
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
145
|
-
const ms = value < 1e12 ? value * 1000 : value;
|
|
146
|
-
const date = new Date(ms);
|
|
147
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
148
|
-
}
|
|
149
|
-
if (typeof value === 'string') {
|
|
150
|
-
const trimmed = value.trim();
|
|
151
|
-
if (!trimmed)
|
|
152
|
-
return null;
|
|
153
|
-
const numeric = Number(trimmed);
|
|
154
|
-
if (!Number.isNaN(numeric)) {
|
|
155
|
-
const ms = numeric < 1e12 ? numeric * 1000 : numeric;
|
|
156
|
-
const date = new Date(ms);
|
|
157
|
-
if (!Number.isNaN(date.getTime()))
|
|
158
|
-
return date;
|
|
159
|
-
}
|
|
160
|
-
const date = new Date(trimmed);
|
|
161
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
162
|
-
}
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
function extractTimestamp(raw) {
|
|
166
|
-
if (!raw || typeof raw !== 'object')
|
|
167
|
-
return null;
|
|
168
|
-
const candidates = [
|
|
169
|
-
raw.timestamp,
|
|
170
|
-
raw.time,
|
|
171
|
-
raw.created_at,
|
|
172
|
-
raw.createdAt,
|
|
173
|
-
raw.ts,
|
|
174
|
-
raw.started_at,
|
|
175
|
-
raw.startedAt,
|
|
176
|
-
];
|
|
177
|
-
for (const candidate of candidates) {
|
|
178
|
-
const date = coerceDate(candidate);
|
|
179
|
-
if (date)
|
|
180
|
-
return date;
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
/** Resolve a mode string to a validated Mode, falling back to the given default. */
|
|
185
|
-
export function resolveMode(requestedMode, defaultMode = 'plan') {
|
|
186
|
-
const normalizedDefault = normalizeModeValue(defaultMode);
|
|
187
|
-
if (!normalizedDefault) {
|
|
188
|
-
throw new Error(`Invalid default mode '${defaultMode}'. Use 'plan' or 'edit'.`);
|
|
189
|
-
}
|
|
190
|
-
if (requestedMode !== null && requestedMode !== undefined) {
|
|
191
|
-
const normalizedMode = normalizeModeValue(requestedMode);
|
|
192
|
-
if (!normalizedMode) {
|
|
193
|
-
throw new Error(`Invalid mode '${requestedMode}'. Valid modes: 'plan' (read-only) or 'edit' (can write).`);
|
|
194
|
-
}
|
|
195
|
-
return normalizedMode;
|
|
196
|
-
}
|
|
197
|
-
return normalizedDefault;
|
|
198
|
-
}
|
|
199
|
-
/** Ensure Gemini's settings.json has experimental.plan enabled for headless plan mode. */
|
|
200
|
-
export async function ensureGeminiPlanMode() {
|
|
201
|
-
const settingsPath = path.join(os.homedir(), '.gemini', 'settings.json');
|
|
202
|
-
try {
|
|
203
|
-
let settings = {};
|
|
204
|
-
try {
|
|
205
|
-
const raw = await fs.readFile(settingsPath, 'utf-8');
|
|
206
|
-
settings = JSON.parse(raw);
|
|
207
|
-
}
|
|
208
|
-
catch {
|
|
209
|
-
// No settings file or invalid JSON
|
|
210
|
-
}
|
|
211
|
-
if (settings.experimental?.plan === true)
|
|
212
|
-
return;
|
|
213
|
-
settings.experimental = { ...settings.experimental, plan: true };
|
|
214
|
-
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
215
|
-
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
216
|
-
console.error('[Swarm] Enabled Gemini experimental.plan in', settingsPath);
|
|
217
|
-
}
|
|
218
|
-
catch (err) {
|
|
219
|
-
console.warn('[Swarm] Could not enable Gemini plan mode:', err);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
/** Check whether the CLI binary for a given agent type exists in PATH. Returns [available, pathOrError]. */
|
|
223
|
-
export function checkCliAvailable(agentType) {
|
|
224
|
-
const cmdTemplate = AGENT_COMMANDS[agentType];
|
|
225
|
-
if (!cmdTemplate) {
|
|
226
|
-
return [false, `Unknown agent type: ${agentType}`];
|
|
227
|
-
}
|
|
228
|
-
const executable = cmdTemplate[0];
|
|
229
|
-
try {
|
|
230
|
-
const whichPath = execSync(`which ${executable}`, { encoding: 'utf-8' }).trim();
|
|
231
|
-
return [true, whichPath];
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
return [false, `CLI tool '${executable}' not found in PATH. Install it first.`];
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
/** Check availability of all known agent CLIs. Returns a map of agent type to install status. */
|
|
238
|
-
export function checkAllClis() {
|
|
239
|
-
const results = {};
|
|
240
|
-
for (const agentType of Object.keys(AGENT_COMMANDS)) {
|
|
241
|
-
const [available, pathOrError] = checkCliAvailable(agentType);
|
|
242
|
-
if (available) {
|
|
243
|
-
results[agentType] = { installed: true, path: pathOrError, error: null };
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
results[agentType] = { installed: false, path: null, error: pathOrError };
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return results;
|
|
250
|
-
}
|
|
251
|
-
let AGENTS_DIR = null;
|
|
252
|
-
/** Resolve and cache the base directory where teammate process data is stored. */
|
|
253
|
-
export async function getAgentsDir() {
|
|
254
|
-
if (!AGENTS_DIR) {
|
|
255
|
-
AGENTS_DIR = await resolveAgentsDir();
|
|
256
|
-
}
|
|
257
|
-
return AGENTS_DIR;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Represents a single teammate process within a team.
|
|
261
|
-
*
|
|
262
|
-
* Tracks process metadata (PID, status, timestamps), reads incremental
|
|
263
|
-
* stdout events, persists state to disk as meta.json, and can be
|
|
264
|
-
* reconstituted from disk via loadFromDisk().
|
|
265
|
-
*/
|
|
266
|
-
export class AgentProcess {
|
|
267
|
-
agentId;
|
|
268
|
-
taskName;
|
|
269
|
-
agentType;
|
|
270
|
-
prompt;
|
|
271
|
-
cwd;
|
|
272
|
-
workspaceDir;
|
|
273
|
-
mode = 'plan';
|
|
274
|
-
pid = null;
|
|
275
|
-
status = AgentStatus.RUNNING;
|
|
276
|
-
startedAt = new Date();
|
|
277
|
-
completedAt = null;
|
|
278
|
-
parentSessionId = null;
|
|
279
|
-
cloudSessionId = null;
|
|
280
|
-
cloudProvider = null;
|
|
281
|
-
prUrl = null;
|
|
282
|
-
version = null;
|
|
283
|
-
remoteSessionId = null;
|
|
284
|
-
name = null;
|
|
285
|
-
// Names of teammates in the same team that this teammate is waiting on.
|
|
286
|
-
// Empty array = no deps = can run immediately. Populated by `teams add --after`.
|
|
287
|
-
after = [];
|
|
288
|
-
// Reasoning-intensity knob wired into buildReasoningFlags at launch time.
|
|
289
|
-
// Resolved late so config/effort-default changes between spawn and launch
|
|
290
|
-
// are honored for teammates staged via `teams add --after`.
|
|
291
|
-
effort = null;
|
|
292
|
-
// Pinned model for this teammate. When null, the agent's CLI picks its
|
|
293
|
-
// own default (no --model forwarded).
|
|
294
|
-
model = null;
|
|
295
|
-
// Extra env vars passed through to the child process (from --env KEY=VALUE).
|
|
296
|
-
envOverrides = null;
|
|
297
|
-
// Factory task-type label. When set, drives planner fan-out and the
|
|
298
|
-
// test-oracle loop. Null for plain teammates — no behavioral change.
|
|
299
|
-
taskType = null;
|
|
300
|
-
// Repo/branch for cloud dispatches that stage behind --after. Captured
|
|
301
|
-
// at spawn time so startReady() can invoke the dispatcher with the same
|
|
302
|
-
// options the user originally supplied.
|
|
303
|
-
cloudRepo = null;
|
|
304
|
-
cloudBranch = null;
|
|
305
|
-
eventsCache = [];
|
|
306
|
-
lastReadPos = 0;
|
|
307
|
-
baseDir = null;
|
|
308
|
-
constructor(agentId, taskName, agentType, prompt, cwd = null, mode = 'plan', pid = null, status = AgentStatus.RUNNING, startedAt = new Date(), completedAt = null, baseDir = null, parentSessionId = null, workspaceDir = null, cloudSessionId = null, cloudProvider = null, prUrl = null, version = null, remoteSessionId = null, name = null, after = [], effort = null, model = null, envOverrides = null, taskType = null, cloudRepo = null, cloudBranch = null) {
|
|
309
|
-
this.agentId = agentId;
|
|
310
|
-
this.remoteSessionId = remoteSessionId;
|
|
311
|
-
this.name = name;
|
|
312
|
-
this.after = after;
|
|
313
|
-
this.effort = effort;
|
|
314
|
-
this.model = model;
|
|
315
|
-
this.envOverrides = envOverrides;
|
|
316
|
-
this.taskType = taskType;
|
|
317
|
-
this.cloudRepo = cloudRepo;
|
|
318
|
-
this.cloudBranch = cloudBranch;
|
|
319
|
-
this.taskName = taskName;
|
|
320
|
-
this.agentType = agentType;
|
|
321
|
-
this.prompt = prompt;
|
|
322
|
-
this.cwd = cwd;
|
|
323
|
-
this.workspaceDir = workspaceDir;
|
|
324
|
-
this.mode = mode;
|
|
325
|
-
this.pid = pid;
|
|
326
|
-
this.status = status;
|
|
327
|
-
this.startedAt = startedAt;
|
|
328
|
-
this.completedAt = completedAt;
|
|
329
|
-
this.baseDir = baseDir;
|
|
330
|
-
this.parentSessionId = parentSessionId;
|
|
331
|
-
this.cloudSessionId = cloudSessionId;
|
|
332
|
-
this.cloudProvider = cloudProvider;
|
|
333
|
-
this.prUrl = prUrl;
|
|
334
|
-
this.version = version;
|
|
335
|
-
}
|
|
336
|
-
get isEditMode() {
|
|
337
|
-
return this.mode === 'edit' || this.mode === 'full';
|
|
338
|
-
}
|
|
339
|
-
async getAgentDir() {
|
|
340
|
-
const base = this.baseDir || await getAgentsDir();
|
|
341
|
-
return path.join(base, this.agentId);
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Dump the subset of state the Ledger sync hook needs. Keeps sync.ts
|
|
345
|
-
* free of any teams-internal imports.
|
|
346
|
-
*/
|
|
347
|
-
async toSnapshot() {
|
|
348
|
-
return {
|
|
349
|
-
agent_id: this.agentId,
|
|
350
|
-
team_id: this.taskName,
|
|
351
|
-
teammate_name: this.name,
|
|
352
|
-
agent_type: this.agentType,
|
|
353
|
-
task_type: this.taskType,
|
|
354
|
-
status: this.status,
|
|
355
|
-
started_at: this.startedAt.toISOString(),
|
|
356
|
-
completed_at: this.completedAt?.toISOString() ?? null,
|
|
357
|
-
after: this.after,
|
|
358
|
-
cloud_provider: this.cloudProvider,
|
|
359
|
-
cloud_session_id: this.cloudSessionId,
|
|
360
|
-
cloud_repo: this.cloudRepo,
|
|
361
|
-
cloud_branch: this.cloudBranch,
|
|
362
|
-
agent_dir: await this.getAgentDir(),
|
|
363
|
-
cwd: this.cwd,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
async getStdoutPath() {
|
|
367
|
-
return path.join(await this.getAgentDir(), 'stdout.log');
|
|
368
|
-
}
|
|
369
|
-
async getMetaPath() {
|
|
370
|
-
return path.join(await this.getAgentDir(), 'meta.json');
|
|
371
|
-
}
|
|
372
|
-
toDict() {
|
|
373
|
-
return {
|
|
374
|
-
agent_id: this.agentId,
|
|
375
|
-
task_name: this.taskName,
|
|
376
|
-
agent_type: this.agentType,
|
|
377
|
-
status: this.status,
|
|
378
|
-
started_at: this.startedAt.toISOString(),
|
|
379
|
-
completed_at: this.completedAt?.toISOString() || null,
|
|
380
|
-
event_count: this.events.length,
|
|
381
|
-
duration: this.duration(),
|
|
382
|
-
mode: this.mode,
|
|
383
|
-
parent_session_id: this.parentSessionId,
|
|
384
|
-
workspace_dir: this.workspaceDir,
|
|
385
|
-
cloud_session_id: this.cloudSessionId,
|
|
386
|
-
cloud_provider: this.cloudProvider,
|
|
387
|
-
pr_url: this.prUrl,
|
|
388
|
-
version: this.version,
|
|
389
|
-
remote_session_id: this.remoteSessionId,
|
|
390
|
-
name: this.name,
|
|
391
|
-
after: this.after,
|
|
392
|
-
effort: this.effort,
|
|
393
|
-
model: this.model,
|
|
394
|
-
env_overrides: this.envOverrides,
|
|
395
|
-
task_type: this.taskType,
|
|
396
|
-
cloud_repo: this.cloudRepo,
|
|
397
|
-
cloud_branch: this.cloudBranch,
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
duration() {
|
|
401
|
-
let seconds;
|
|
402
|
-
if (this.completedAt) {
|
|
403
|
-
seconds = (this.completedAt.getTime() - this.startedAt.getTime()) / 1000;
|
|
404
|
-
}
|
|
405
|
-
else if (this.status === AgentStatus.RUNNING) {
|
|
406
|
-
seconds = (Date.now() - this.startedAt.getTime()) / 1000;
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
if (seconds < 60) {
|
|
412
|
-
return `${Math.floor(seconds)} seconds`;
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
const minutes = seconds / 60;
|
|
416
|
-
return `${minutes.toFixed(1)} minutes`;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
get events() {
|
|
420
|
-
return this.eventsCache;
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Return the latest timestamp we have seen in the agent's events.
|
|
424
|
-
* Falls back to null when none are available.
|
|
425
|
-
*/
|
|
426
|
-
getLatestEventTime() {
|
|
427
|
-
let latest = null;
|
|
428
|
-
for (const event of this.eventsCache) {
|
|
429
|
-
const ts = event?.timestamp;
|
|
430
|
-
if (!ts)
|
|
431
|
-
continue;
|
|
432
|
-
const parsed = new Date(ts);
|
|
433
|
-
if (!Number.isNaN(parsed.getTime())) {
|
|
434
|
-
if (!latest || parsed > latest) {
|
|
435
|
-
latest = parsed;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return latest;
|
|
440
|
-
}
|
|
441
|
-
async readNewEvents() {
|
|
442
|
-
const stdoutPath = await this.getStdoutPath();
|
|
443
|
-
try {
|
|
444
|
-
const stats = await fs.stat(stdoutPath).catch(() => null);
|
|
445
|
-
if (!stats)
|
|
446
|
-
return;
|
|
447
|
-
const fallbackTimestamp = (stats.mtime || new Date()).toISOString();
|
|
448
|
-
const fd = await fs.open(stdoutPath, 'r');
|
|
449
|
-
const buffer = Buffer.alloc(1024 * 1024);
|
|
450
|
-
const { bytesRead } = await fd.read(buffer, 0, buffer.length, this.lastReadPos);
|
|
451
|
-
await fd.close();
|
|
452
|
-
if (bytesRead === 0)
|
|
453
|
-
return;
|
|
454
|
-
const newContent = buffer.toString('utf-8', 0, bytesRead);
|
|
455
|
-
this.lastReadPos += bytesRead;
|
|
456
|
-
const lines = newContent.split('\n').map(l => l.trim()).filter(l => l);
|
|
457
|
-
for (const line of lines) {
|
|
458
|
-
try {
|
|
459
|
-
const rawEvent = JSON.parse(line);
|
|
460
|
-
const events = normalizeEvents(this.agentType, rawEvent);
|
|
461
|
-
const resolvedTimestamp = extractTimestamp(rawEvent)?.toISOString() || fallbackTimestamp;
|
|
462
|
-
for (const event of events) {
|
|
463
|
-
event.timestamp = resolvedTimestamp;
|
|
464
|
-
this.eventsCache.push(event);
|
|
465
|
-
// Capture the agent's own session/thread id the first time we see
|
|
466
|
-
// it. For Claude it's the same uuid we passed via --session-id;
|
|
467
|
-
// for others (Codex thread_id, Gemini/Cursor/OpenCode sessionID)
|
|
468
|
-
// it's their internal id, which lets us cross-reference with
|
|
469
|
-
// `agents sessions <id>`.
|
|
470
|
-
if (!this.remoteSessionId && event.session_id) {
|
|
471
|
-
this.remoteSessionId = event.session_id;
|
|
472
|
-
}
|
|
473
|
-
if (event.type === 'result' || event.type === 'turn.completed' || event.type === 'thread.completed') {
|
|
474
|
-
if (event.status === 'success' || event.type === 'turn.completed') {
|
|
475
|
-
this.status = AgentStatus.COMPLETED;
|
|
476
|
-
this.completedAt = event.timestamp ? new Date(event.timestamp) : new Date();
|
|
477
|
-
}
|
|
478
|
-
else if (event.status === 'error') {
|
|
479
|
-
this.status = AgentStatus.FAILED;
|
|
480
|
-
this.completedAt = event.timestamp ? new Date(event.timestamp) : new Date();
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
this.eventsCache.push({
|
|
487
|
-
type: 'raw',
|
|
488
|
-
content: line,
|
|
489
|
-
timestamp: fallbackTimestamp,
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
catch (err) {
|
|
495
|
-
console.error(`Error reading events for agent ${this.agentId}:`, err);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
async saveMeta() {
|
|
499
|
-
const agentDir = await this.getAgentDir();
|
|
500
|
-
await fs.mkdir(agentDir, { recursive: true });
|
|
501
|
-
const meta = {
|
|
502
|
-
agent_id: this.agentId,
|
|
503
|
-
task_name: this.taskName,
|
|
504
|
-
agent_type: this.agentType,
|
|
505
|
-
prompt: this.prompt,
|
|
506
|
-
cwd: this.cwd,
|
|
507
|
-
workspace_dir: this.workspaceDir,
|
|
508
|
-
mode: this.mode,
|
|
509
|
-
pid: this.pid,
|
|
510
|
-
status: this.status,
|
|
511
|
-
started_at: this.startedAt.toISOString(),
|
|
512
|
-
completed_at: this.completedAt?.toISOString() || null,
|
|
513
|
-
parent_session_id: this.parentSessionId,
|
|
514
|
-
cloud_session_id: this.cloudSessionId,
|
|
515
|
-
cloud_provider: this.cloudProvider,
|
|
516
|
-
pr_url: this.prUrl,
|
|
517
|
-
version: this.version,
|
|
518
|
-
remote_session_id: this.remoteSessionId,
|
|
519
|
-
name: this.name,
|
|
520
|
-
after: this.after,
|
|
521
|
-
effort: this.effort,
|
|
522
|
-
model: this.model,
|
|
523
|
-
env_overrides: this.envOverrides,
|
|
524
|
-
task_type: this.taskType,
|
|
525
|
-
cloud_repo: this.cloudRepo,
|
|
526
|
-
cloud_branch: this.cloudBranch,
|
|
527
|
-
};
|
|
528
|
-
const metaPath = await this.getMetaPath();
|
|
529
|
-
await fs.writeFile(metaPath, JSON.stringify(meta, null, 2));
|
|
530
|
-
}
|
|
531
|
-
static async loadFromDisk(agentId, baseDir = null) {
|
|
532
|
-
const base = baseDir || await getAgentsDir();
|
|
533
|
-
const agentDir = path.join(base, agentId);
|
|
534
|
-
const metaPath = path.join(agentDir, 'meta.json');
|
|
535
|
-
try {
|
|
536
|
-
await fs.access(metaPath);
|
|
537
|
-
}
|
|
538
|
-
catch {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
try {
|
|
542
|
-
const metaContent = await fs.readFile(metaPath, 'utf-8');
|
|
543
|
-
const meta = JSON.parse(metaContent);
|
|
544
|
-
// Legacy teammates may have mode='ralph' or 'cloud' from before modes
|
|
545
|
-
// were narrowed. Coerce to the closest current mode so they still load.
|
|
546
|
-
const modeMap = {
|
|
547
|
-
edit: 'edit',
|
|
548
|
-
full: 'full',
|
|
549
|
-
ralph: 'full', // ralph used the same "no-permission" flags as full
|
|
550
|
-
cloud: 'edit', // cloud teammates had edit-level write access
|
|
551
|
-
};
|
|
552
|
-
const resolvedMode = modeMap[meta.mode] || 'plan';
|
|
553
|
-
// AgentStatus is a string enum. Validate meta.status against its VALUES
|
|
554
|
-
// (not its keys) — `AgentStatus["pending"]` is undefined but
|
|
555
|
-
// `AgentStatus.PENDING === "pending"` works.
|
|
556
|
-
const validStatuses = Object.values(AgentStatus);
|
|
557
|
-
const resolvedStatus = validStatuses.includes(meta.status)
|
|
558
|
-
? meta.status
|
|
559
|
-
: AgentStatus.RUNNING;
|
|
560
|
-
const agent = new AgentProcess(meta.agent_id, meta.task_name || 'default', meta.agent_type, meta.prompt, meta.cwd || null, resolvedMode, meta.pid || null, resolvedStatus, new Date(meta.started_at), meta.completed_at ? new Date(meta.completed_at) : null, baseDir, meta.parent_session_id || null, meta.workspace_dir || null, meta.cloud_session_id || null, meta.cloud_provider || null, meta.pr_url || null, meta.version || null, meta.remote_session_id || null, meta.name || null, Array.isArray(meta.after) ? meta.after : [], meta.effort || null, meta.model || null, meta.env_overrides || null, meta.task_type && VALID_TASK_TYPES.includes(meta.task_type)
|
|
561
|
-
? meta.task_type
|
|
562
|
-
: null, meta.cloud_repo || null, meta.cloud_branch || null);
|
|
563
|
-
return agent;
|
|
564
|
-
}
|
|
565
|
-
catch {
|
|
566
|
-
return null;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
isProcessAlive() {
|
|
570
|
-
if (!this.pid)
|
|
571
|
-
return false;
|
|
572
|
-
try {
|
|
573
|
-
process.kill(this.pid, 0);
|
|
574
|
-
return true;
|
|
575
|
-
}
|
|
576
|
-
catch {
|
|
577
|
-
return false;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
async updateStatusFromProcess() {
|
|
581
|
-
if (!this.pid)
|
|
582
|
-
return;
|
|
583
|
-
if (this.isProcessAlive()) {
|
|
584
|
-
await this.readNewEvents();
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
if (this.status === AgentStatus.RUNNING) {
|
|
588
|
-
const exitCode = await this.reapProcess();
|
|
589
|
-
await this.readNewEvents();
|
|
590
|
-
if (this.status === AgentStatus.RUNNING) {
|
|
591
|
-
const fallbackCompletion = this.getLatestEventTime() || this.startedAt || new Date();
|
|
592
|
-
if (exitCode !== null && exitCode !== 0) {
|
|
593
|
-
this.status = AgentStatus.FAILED;
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
this.status = AgentStatus.COMPLETED;
|
|
597
|
-
}
|
|
598
|
-
this.completedAt = fallbackCompletion;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
else if (!this.completedAt) {
|
|
602
|
-
await this.readNewEvents();
|
|
603
|
-
const fallbackCompletion = this.getLatestEventTime() || this.startedAt || new Date();
|
|
604
|
-
this.completedAt = fallbackCompletion;
|
|
605
|
-
}
|
|
606
|
-
await this.saveMeta();
|
|
607
|
-
}
|
|
608
|
-
async reapProcess() {
|
|
609
|
-
if (!this.pid)
|
|
610
|
-
return null;
|
|
611
|
-
try {
|
|
612
|
-
process.kill(this.pid, 0);
|
|
613
|
-
return null;
|
|
614
|
-
}
|
|
615
|
-
catch {
|
|
616
|
-
return 1;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
export class AgentManager {
|
|
621
|
-
agents = new Map();
|
|
622
|
-
maxAgents;
|
|
623
|
-
maxConcurrent;
|
|
624
|
-
agentsDir = '';
|
|
625
|
-
filterByCwd;
|
|
626
|
-
cleanupAgeDays;
|
|
627
|
-
defaultMode;
|
|
628
|
-
agentConfigs;
|
|
629
|
-
constructorAgentConfigs = null;
|
|
630
|
-
initPromise = null;
|
|
631
|
-
cloudDispatcher = null;
|
|
632
|
-
completionHook = null;
|
|
633
|
-
syncedAgents = new Set();
|
|
634
|
-
constructorAgentsDir = null;
|
|
635
|
-
constructor(maxAgents = 50, maxConcurrent = 10, agentsDir = null, defaultMode = null, filterByCwd = null, cleanupAgeDays = 7, agentConfigs = null) {
|
|
636
|
-
this.maxAgents = maxAgents;
|
|
637
|
-
this.maxConcurrent = maxConcurrent;
|
|
638
|
-
this.constructorAgentsDir = agentsDir;
|
|
639
|
-
this.filterByCwd = filterByCwd;
|
|
640
|
-
this.cleanupAgeDays = cleanupAgeDays;
|
|
641
|
-
const resolvedDefaultMode = defaultMode ? normalizeModeValue(defaultMode) : defaultModeFromEnv();
|
|
642
|
-
if (!resolvedDefaultMode) {
|
|
643
|
-
throw new Error(`Invalid default_mode '${defaultMode}'. Use 'plan' or 'edit'.`);
|
|
644
|
-
}
|
|
645
|
-
this.defaultMode = resolvedDefaultMode;
|
|
646
|
-
this.constructorAgentConfigs = agentConfigs;
|
|
647
|
-
this.initPromise = this.doInitialize();
|
|
648
|
-
}
|
|
649
|
-
async initialize() {
|
|
650
|
-
if (!this.initPromise) {
|
|
651
|
-
this.initPromise = this.doInitialize();
|
|
652
|
-
}
|
|
653
|
-
return this.initPromise;
|
|
654
|
-
}
|
|
655
|
-
async doInitialize() {
|
|
656
|
-
this.agentsDir = this.constructorAgentsDir || await getAgentsDir();
|
|
657
|
-
await fs.mkdir(this.agentsDir, { recursive: true });
|
|
658
|
-
this.agentConfigs = this.constructorAgentConfigs ?? loadDefaultAgentConfigs();
|
|
659
|
-
await this.loadExistingAgents();
|
|
660
|
-
}
|
|
661
|
-
getDefaultMode() {
|
|
662
|
-
return this.defaultMode;
|
|
663
|
-
}
|
|
664
|
-
setModelOverrides(agentConfigs) {
|
|
665
|
-
this.agentConfigs = agentConfigs;
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Register the callback used to dispatch cloud-backed teammates when their
|
|
669
|
-
* --after deps resolve. Called once at CLI startup by `agents teams`.
|
|
670
|
-
*/
|
|
671
|
-
setCloudDispatcher(fn) {
|
|
672
|
-
this.cloudDispatcher = fn;
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* Register a hook to run once per teammate the first time it lands in a
|
|
676
|
-
* terminal status (completed / failed / stopped). The hook fires during
|
|
677
|
-
* listAll() / listByTask() status polling, so any CLI or MCP command that
|
|
678
|
-
* touches status triggers sync automatically.
|
|
679
|
-
*/
|
|
680
|
-
setCompletionHook(fn) {
|
|
681
|
-
this.completionHook = fn;
|
|
682
|
-
}
|
|
683
|
-
registerAgent(agent) {
|
|
684
|
-
this.agents.set(agent.agentId, agent);
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Scan the agents dir for meta.json files not already in the in-memory
|
|
688
|
-
* cache and load them. Needed when another process (e.g. a Planner
|
|
689
|
-
* teammate running `agents teams add`) creates new teammates while this
|
|
690
|
-
* manager is alive — the supervisor loop calls this each wave so
|
|
691
|
-
* dynamically-added teammates get picked up.
|
|
692
|
-
*
|
|
693
|
-
* Does not modify or re-load agents already in the cache; that path is
|
|
694
|
-
* covered by updateStatusFromProcess() which re-reads stdout.log.
|
|
695
|
-
*/
|
|
696
|
-
async rescanFromDisk() {
|
|
697
|
-
await this.initialize();
|
|
698
|
-
try {
|
|
699
|
-
await fs.access(this.agentsDir);
|
|
700
|
-
}
|
|
701
|
-
catch {
|
|
702
|
-
return 0;
|
|
703
|
-
}
|
|
704
|
-
const entries = await fs.readdir(this.agentsDir);
|
|
705
|
-
let added = 0;
|
|
706
|
-
for (const entry of entries) {
|
|
707
|
-
if (this.agents.has(entry))
|
|
708
|
-
continue;
|
|
709
|
-
const agentDir = path.join(this.agentsDir, entry);
|
|
710
|
-
const stat = await fs.stat(agentDir).catch(() => null);
|
|
711
|
-
if (!stat || !stat.isDirectory())
|
|
712
|
-
continue;
|
|
713
|
-
const agent = await AgentProcess.loadFromDisk(entry, this.agentsDir);
|
|
714
|
-
if (!agent)
|
|
715
|
-
continue;
|
|
716
|
-
if (this.filterByCwd !== null && agent.cwd !== this.filterByCwd)
|
|
717
|
-
continue;
|
|
718
|
-
this.agents.set(entry, agent);
|
|
719
|
-
added++;
|
|
720
|
-
}
|
|
721
|
-
return added;
|
|
722
|
-
}
|
|
723
|
-
async loadExistingAgents() {
|
|
724
|
-
try {
|
|
725
|
-
await fs.access(this.agentsDir);
|
|
726
|
-
}
|
|
727
|
-
catch {
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
const cutoffDate = new Date(Date.now() - this.cleanupAgeDays * 24 * 60 * 60 * 1000);
|
|
731
|
-
let loadedCount = 0;
|
|
732
|
-
let skippedCwd = 0;
|
|
733
|
-
let cleanedOld = 0;
|
|
734
|
-
const entries = await fs.readdir(this.agentsDir);
|
|
735
|
-
for (const entry of entries) {
|
|
736
|
-
const agentDir = path.join(this.agentsDir, entry);
|
|
737
|
-
const stat = await fs.stat(agentDir).catch(() => null);
|
|
738
|
-
if (!stat || !stat.isDirectory())
|
|
739
|
-
continue;
|
|
740
|
-
const agentId = entry;
|
|
741
|
-
const agent = await AgentProcess.loadFromDisk(agentId, this.agentsDir);
|
|
742
|
-
if (!agent)
|
|
743
|
-
continue;
|
|
744
|
-
if (agent.completedAt && agent.completedAt < cutoffDate) {
|
|
745
|
-
try {
|
|
746
|
-
await fs.rm(agentDir, { recursive: true });
|
|
747
|
-
cleanedOld++;
|
|
748
|
-
}
|
|
749
|
-
catch (err) {
|
|
750
|
-
console.warn(`Failed to cleanup old agent ${agentId}:`, err);
|
|
751
|
-
}
|
|
752
|
-
continue;
|
|
753
|
-
}
|
|
754
|
-
if (this.filterByCwd !== null) {
|
|
755
|
-
const agentCwd = agent.cwd;
|
|
756
|
-
if (agentCwd !== this.filterByCwd) {
|
|
757
|
-
skippedCwd++;
|
|
758
|
-
continue;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
await agent.updateStatusFromProcess();
|
|
762
|
-
this.agents.set(agentId, agent);
|
|
763
|
-
loadedCount++;
|
|
764
|
-
}
|
|
765
|
-
if (cleanedOld > 0) {
|
|
766
|
-
debug(`Cleaned up ${cleanedOld} old agents (older than ${this.cleanupAgeDays} days)`);
|
|
767
|
-
}
|
|
768
|
-
if (skippedCwd > 0) {
|
|
769
|
-
debug(`Skipped ${skippedCwd} agents (different CWD)`);
|
|
770
|
-
}
|
|
771
|
-
debug(`Loaded ${loadedCount} agents from disk`);
|
|
772
|
-
}
|
|
773
|
-
async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null) {
|
|
774
|
-
await this.initialize();
|
|
775
|
-
const resolvedMode = resolveMode(mode, this.defaultMode);
|
|
776
|
-
// Enforce: teammate names are unique within a team.
|
|
777
|
-
const siblings = await this.listByTask(taskName);
|
|
778
|
-
if (name && siblings.some((a) => a.name === name)) {
|
|
779
|
-
throw new Error(`Team '${taskName}' already has a teammate named '${name}'. Pick another name or leave --name off.`);
|
|
780
|
-
}
|
|
781
|
-
// --- dependency validation ---
|
|
782
|
-
const cleanAfter = after.filter((s) => s && s.trim());
|
|
783
|
-
if (cleanAfter.length > 0) {
|
|
784
|
-
if (!name) {
|
|
785
|
-
throw new Error("Can't use --after without --name. Dependencies reference teammates by name.");
|
|
786
|
-
}
|
|
787
|
-
// Every --after entry must resolve to an existing teammate name.
|
|
788
|
-
const siblingNames = new Set(siblings.map((a) => a.name).filter(Boolean));
|
|
789
|
-
const missing = cleanAfter.filter((dep) => !siblingNames.has(dep));
|
|
790
|
-
if (missing.length > 0) {
|
|
791
|
-
throw new Error(`Team '${taskName}' has no teammate named ${missing.map((m) => `'${m}'`).join(', ')} yet.\n` +
|
|
792
|
-
` Add them first, then add this one.`);
|
|
793
|
-
}
|
|
794
|
-
// Cycle check: walk the transitive deps of each --after entry; if the
|
|
795
|
-
// new teammate's own name shows up, we'd create a cycle.
|
|
796
|
-
const byName = new Map(siblings.filter((a) => a.name).map((a) => [a.name, a]));
|
|
797
|
-
for (const dep of cleanAfter) {
|
|
798
|
-
if (hasTransitiveDep(byName, dep, name)) {
|
|
799
|
-
throw new Error(`Adding '${name}' after '${dep}' would create a cycle (${dep} already depends on ${name}).`);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
// Resolve and validate cwd
|
|
804
|
-
let resolvedCwd = null;
|
|
805
|
-
if (cwd !== null) {
|
|
806
|
-
resolvedCwd = path.resolve(cwd);
|
|
807
|
-
const stat = await fs.stat(resolvedCwd).catch(() => null);
|
|
808
|
-
if (!stat) {
|
|
809
|
-
throw new Error(`Working directory does not exist: ${cwd}`);
|
|
810
|
-
}
|
|
811
|
-
if (!stat.isDirectory()) {
|
|
812
|
-
throw new Error(`Working directory is not a directory: ${cwd}`);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
// Cloud-backed teammates run on remote infrastructure; we don't need the
|
|
816
|
-
// local CLI for them (the pod has its own). The caller has already
|
|
817
|
-
// dispatched via the cloud provider and passed us the provider + session.
|
|
818
|
-
const isCloudBacked = Boolean(cloudProvider);
|
|
819
|
-
if (!isCloudBacked) {
|
|
820
|
-
const [available, pathOrError] = checkCliAvailable(agentType);
|
|
821
|
-
if (!available) {
|
|
822
|
-
throw new Error(pathOrError || 'CLI tool not available');
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
// Use a full UUIDv4 as the canonical agent_id. For Claude, we pass it via
|
|
826
|
-
// --session-id so it's also Claude's session id (unified identity).
|
|
827
|
-
const agentId = randomUUID();
|
|
828
|
-
const isStaged = cleanAfter.length > 0;
|
|
829
|
-
const agent = new AgentProcess(agentId, taskName, agentType, prompt, resolvedCwd, resolvedMode, null, isStaged ? AgentStatus.PENDING : AgentStatus.RUNNING, new Date(), null, this.agentsDir, parentSessionId, workspaceDir, cloudSessionId, cloudProvider, null, version, null, name, cleanAfter, effort, model, envOverrides && Object.keys(envOverrides).length > 0 ? envOverrides : null, taskType, cloudRepo, cloudBranch);
|
|
830
|
-
const agentDir = await agent.getAgentDir();
|
|
831
|
-
try {
|
|
832
|
-
await fs.mkdir(agentDir, { recursive: true });
|
|
833
|
-
}
|
|
834
|
-
catch (err) {
|
|
835
|
-
throw new Error(`Failed to create agent directory: ${err.message}`);
|
|
836
|
-
}
|
|
837
|
-
await agent.saveMeta();
|
|
838
|
-
this.agents.set(agentId, agent);
|
|
839
|
-
if (isStaged) {
|
|
840
|
-
debug(`Staged ${agentType} teammate '${name}' in team '${taskName}' (after: ${cleanAfter.join(', ')})`);
|
|
841
|
-
}
|
|
842
|
-
else if (isCloudBacked) {
|
|
843
|
-
// Cloud-backed teammate: the provider already dispatched a remote task.
|
|
844
|
-
// No local process to launch; status polling walks the provider instead.
|
|
845
|
-
debug(`Cloud-backed ${agentType} teammate via ${cloudProvider} (session=${cloudSessionId})`);
|
|
846
|
-
}
|
|
847
|
-
else {
|
|
848
|
-
await this.launchProcess(agent);
|
|
849
|
-
}
|
|
850
|
-
await this.cleanupOldAgents();
|
|
851
|
-
return agent;
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Actually spawn the OS process for a teammate. Extracted from spawn() so
|
|
855
|
-
* staged teammates can be launched later by startReady().
|
|
856
|
-
*/
|
|
857
|
-
async launchProcess(agent) {
|
|
858
|
-
const running = await this.listRunning();
|
|
859
|
-
if (running.length >= this.maxConcurrent) {
|
|
860
|
-
throw new Error(`Maximum concurrent agents (${this.maxConcurrent}) reached. Wait for an agent to complete or stop one first.`);
|
|
861
|
-
}
|
|
862
|
-
const effort = agent.effort ?? 'medium';
|
|
863
|
-
// Falls back to the pinned model in agentConfigs; null means "let the
|
|
864
|
-
// CLI pick its own default" (no --model flag forwarded). Effort is a
|
|
865
|
-
// separate knob wired into buildReasoningFlags inside buildCommand.
|
|
866
|
-
const resolvedModel = agent.model ?? this.agentConfigs[agent.agentType]?.model ?? null;
|
|
867
|
-
const cmd = this.buildCommand(agent.agentType, agent.prompt, agent.mode, resolvedModel, agent.cwd, agent.agentId, effort);
|
|
868
|
-
if (agent.version && cmd.length > 0) {
|
|
869
|
-
cmd[0] = `${cmd[0]}@${agent.version}`;
|
|
870
|
-
}
|
|
871
|
-
debug(`Launching ${agent.agentType} agent ${agent.agentId} [${agent.mode}]: ${cmd.slice(0, 3).join(' ')}...`);
|
|
872
|
-
try {
|
|
873
|
-
const stdoutPath = await agent.getStdoutPath();
|
|
874
|
-
const stdoutFile = await fs.open(stdoutPath, 'w');
|
|
875
|
-
const stdoutFd = stdoutFile.fd;
|
|
876
|
-
const childProcess = spawn(cmd[0], cmd.slice(1), {
|
|
877
|
-
stdio: ['ignore', stdoutFd, stdoutFd],
|
|
878
|
-
cwd: agent.cwd || undefined,
|
|
879
|
-
detached: true,
|
|
880
|
-
env: agent.envOverrides
|
|
881
|
-
? { ...process.env, ...agent.envOverrides }
|
|
882
|
-
: process.env,
|
|
883
|
-
});
|
|
884
|
-
childProcess.unref();
|
|
885
|
-
stdoutFile.close().catch(() => { });
|
|
886
|
-
agent.pid = childProcess.pid || null;
|
|
887
|
-
agent.status = AgentStatus.RUNNING;
|
|
888
|
-
agent.startedAt = new Date();
|
|
889
|
-
await agent.saveMeta();
|
|
890
|
-
}
|
|
891
|
-
catch (err) {
|
|
892
|
-
await this.cleanupPartialAgent(agent);
|
|
893
|
-
console.error(`Failed to spawn agent ${agent.agentId}:`, err);
|
|
894
|
-
throw new Error(`Failed to spawn agent: ${err.message}`);
|
|
895
|
-
}
|
|
896
|
-
debug(`Launched agent ${agent.agentId} with PID ${agent.pid}`);
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* Fire any pending teammates in the given team whose `after` deps have all
|
|
900
|
-
* completed. Returns the list of teammates just launched. Repeatable:
|
|
901
|
-
* call it once per DAG wave. Safe to call on teams with no pending work
|
|
902
|
-
* (returns empty list).
|
|
903
|
-
*/
|
|
904
|
-
async startReady(taskName) {
|
|
905
|
-
await this.initialize();
|
|
906
|
-
const teammates = await this.listByTask(taskName);
|
|
907
|
-
const byName = new Map(teammates.filter((a) => a.name).map((a) => [a.name, a]));
|
|
908
|
-
const launched = [];
|
|
909
|
-
for (const agent of teammates) {
|
|
910
|
-
if (agent.status !== AgentStatus.PENDING)
|
|
911
|
-
continue;
|
|
912
|
-
const depsReady = agent.after.every((depName) => {
|
|
913
|
-
const dep = byName.get(depName);
|
|
914
|
-
return dep && dep.status === AgentStatus.COMPLETED;
|
|
915
|
-
});
|
|
916
|
-
if (!depsReady)
|
|
917
|
-
continue;
|
|
918
|
-
try {
|
|
919
|
-
if (agent.cloudProvider) {
|
|
920
|
-
if (!this.cloudDispatcher) {
|
|
921
|
-
console.error(`Cannot start cloud-backed teammate ${agent.agentId}: no dispatcher registered.`);
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
const { cloudSessionId } = await this.cloudDispatcher(agent);
|
|
925
|
-
agent.cloudSessionId = cloudSessionId;
|
|
926
|
-
agent.status = AgentStatus.RUNNING;
|
|
927
|
-
agent.startedAt = new Date();
|
|
928
|
-
await agent.saveMeta();
|
|
929
|
-
launched.push(agent);
|
|
930
|
-
}
|
|
931
|
-
else {
|
|
932
|
-
await this.launchProcess(agent);
|
|
933
|
-
launched.push(agent);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
catch (err) {
|
|
937
|
-
console.error(`Could not launch ${agent.agentId}:`, err);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
return launched;
|
|
941
|
-
}
|
|
942
|
-
buildCommand(agentType, prompt, mode, model, cwd = null, sessionId = null, effort = 'medium') {
|
|
943
|
-
const cmdTemplate = AGENT_COMMANDS[agentType];
|
|
944
|
-
if (!cmdTemplate) {
|
|
945
|
-
throw new Error(`Unknown agent type: ${agentType}`);
|
|
946
|
-
}
|
|
947
|
-
const isEditMode = mode === 'edit';
|
|
948
|
-
// Build the full prompt with prefix (for plan mode) and suffix
|
|
949
|
-
let fullPrompt = prompt + PROMPT_SUFFIX;
|
|
950
|
-
// For Claude in plan mode, add prefix explaining headless plan mode restrictions
|
|
951
|
-
if (agentType === 'claude' && !isEditMode) {
|
|
952
|
-
fullPrompt = CLAUDE_PLAN_MODE_PREFIX + fullPrompt;
|
|
953
|
-
}
|
|
954
|
-
let cmd = cmdTemplate.map(part => part.replace('{prompt}', fullPrompt));
|
|
955
|
-
if (agentType === 'claude') {
|
|
956
|
-
// Grant access to the working directory.
|
|
957
|
-
if (cwd) {
|
|
958
|
-
cmd.push('--add-dir', cwd);
|
|
959
|
-
}
|
|
960
|
-
// Pin Claude's session UUID to our agent_id so the session file lands
|
|
961
|
-
// at ~/.claude/projects/.../<agent_id>.jsonl — unified identity.
|
|
962
|
-
if (sessionId) {
|
|
963
|
-
cmd.push('--session-id', sessionId);
|
|
964
|
-
}
|
|
965
|
-
// Note: we deliberately do NOT pass --settings here. The agents-cli
|
|
966
|
-
// shim exports CLAUDE_CONFIG_DIR scoped to the version being spawned,
|
|
967
|
-
// which makes Claude read settings.json, commands/, skills/, hooks/,
|
|
968
|
-
// and MCP config from that version's home. Passing a fixed
|
|
969
|
-
// ~/.claude/settings.json would override that and bind settings to the
|
|
970
|
-
// *default* version rather than the one this teammate is running.
|
|
971
|
-
}
|
|
972
|
-
// Add model flag for each agent type only when the teammate has a pinned
|
|
973
|
-
// model. When null, the agent's CLI picks its own default.
|
|
974
|
-
if (model) {
|
|
975
|
-
if (agentType === 'codex') {
|
|
976
|
-
const execIndex = cmd.indexOf('exec');
|
|
977
|
-
const sandboxIndex = cmd.indexOf('--sandbox');
|
|
978
|
-
const insertIndex = sandboxIndex !== -1 ? sandboxIndex : execIndex + 1;
|
|
979
|
-
cmd.splice(insertIndex, 0, '--model', model);
|
|
980
|
-
}
|
|
981
|
-
else if (agentType === 'cursor') {
|
|
982
|
-
cmd.push('--model', model);
|
|
983
|
-
}
|
|
984
|
-
else if (agentType === 'gemini' || agentType === 'claude') {
|
|
985
|
-
cmd.push('--model', model);
|
|
986
|
-
}
|
|
987
|
-
else if (agentType === 'opencode') {
|
|
988
|
-
cmd.push('--model', model);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
if (agentType === 'opencode') {
|
|
992
|
-
const opencodeAgent = mode === 'edit' || mode === 'full' ? 'build' : 'plan';
|
|
993
|
-
const promptIndex = cmd.indexOf(fullPrompt);
|
|
994
|
-
if (promptIndex !== -1) {
|
|
995
|
-
cmd.splice(promptIndex + 1, 0, '--agent', opencodeAgent);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
// Inject reasoning-intensity flags for agents that support them. Claude
|
|
999
|
-
// gets --effort appended; Codex gets `-c model_reasoning_effort=...`
|
|
1000
|
-
// inserted before `exec` so it's parsed as a global config override.
|
|
1001
|
-
const reasoningFlags = buildReasoningFlags(agentType, effort);
|
|
1002
|
-
if (reasoningFlags.length > 0) {
|
|
1003
|
-
if (agentType === 'codex') {
|
|
1004
|
-
const execIndex = cmd.indexOf('exec');
|
|
1005
|
-
const insertIndex = execIndex !== -1 ? execIndex : 1;
|
|
1006
|
-
cmd.splice(insertIndex, 0, ...reasoningFlags);
|
|
1007
|
-
}
|
|
1008
|
-
else {
|
|
1009
|
-
cmd.push(...reasoningFlags);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
if (mode === 'full') {
|
|
1013
|
-
cmd = this.applyFullMode(agentType, cmd);
|
|
1014
|
-
}
|
|
1015
|
-
else if (isEditMode) {
|
|
1016
|
-
cmd = this.applyEditMode(agentType, cmd);
|
|
1017
|
-
}
|
|
1018
|
-
return cmd;
|
|
1019
|
-
}
|
|
1020
|
-
applyEditMode(agentType, cmd) {
|
|
1021
|
-
const editCmd = [...cmd];
|
|
1022
|
-
switch (agentType) {
|
|
1023
|
-
case 'codex':
|
|
1024
|
-
editCmd.push('--full-auto');
|
|
1025
|
-
break;
|
|
1026
|
-
case 'cursor':
|
|
1027
|
-
editCmd.push('-f');
|
|
1028
|
-
break;
|
|
1029
|
-
case 'gemini': {
|
|
1030
|
-
const approvalIndex = editCmd.indexOf('--approval-mode');
|
|
1031
|
-
if (approvalIndex !== -1) {
|
|
1032
|
-
editCmd.splice(approvalIndex, 2);
|
|
1033
|
-
}
|
|
1034
|
-
editCmd.push('--yolo');
|
|
1035
|
-
break;
|
|
1036
|
-
}
|
|
1037
|
-
case 'claude':
|
|
1038
|
-
const permModeIndex = editCmd.indexOf('--permission-mode');
|
|
1039
|
-
if (permModeIndex !== -1 && permModeIndex + 1 < editCmd.length) {
|
|
1040
|
-
editCmd[permModeIndex + 1] = 'acceptEdits';
|
|
1041
|
-
}
|
|
1042
|
-
break;
|
|
1043
|
-
}
|
|
1044
|
-
return editCmd;
|
|
1045
|
-
}
|
|
1046
|
-
// "full" mode: edit-level write access with permission gates bypassed.
|
|
1047
|
-
// For Claude that's --dangerously-skip-permissions; other agents already
|
|
1048
|
-
// lack gates in edit mode so their commands match applyEditMode.
|
|
1049
|
-
applyFullMode(agentType, cmd) {
|
|
1050
|
-
const fullCmd = [...cmd];
|
|
1051
|
-
switch (agentType) {
|
|
1052
|
-
case 'codex':
|
|
1053
|
-
fullCmd.push('--full-auto');
|
|
1054
|
-
break;
|
|
1055
|
-
case 'cursor':
|
|
1056
|
-
fullCmd.push('-f');
|
|
1057
|
-
break;
|
|
1058
|
-
case 'gemini': {
|
|
1059
|
-
const approvalIndex = fullCmd.indexOf('--approval-mode');
|
|
1060
|
-
if (approvalIndex !== -1) {
|
|
1061
|
-
fullCmd.splice(approvalIndex, 2);
|
|
1062
|
-
}
|
|
1063
|
-
fullCmd.push('--yolo');
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
|
-
case 'claude':
|
|
1067
|
-
// Replace --permission-mode plan with --dangerously-skip-permissions
|
|
1068
|
-
const permModeIndex = fullCmd.indexOf('--permission-mode');
|
|
1069
|
-
if (permModeIndex !== -1) {
|
|
1070
|
-
fullCmd.splice(permModeIndex, 2); // Remove --permission-mode and its value
|
|
1071
|
-
}
|
|
1072
|
-
fullCmd.push('--dangerously-skip-permissions');
|
|
1073
|
-
break;
|
|
1074
|
-
}
|
|
1075
|
-
return fullCmd;
|
|
1076
|
-
}
|
|
1077
|
-
async get(agentId) {
|
|
1078
|
-
await this.initialize();
|
|
1079
|
-
let agent = this.agents.get(agentId) || null;
|
|
1080
|
-
if (agent) {
|
|
1081
|
-
await agent.readNewEvents();
|
|
1082
|
-
await agent.updateStatusFromProcess();
|
|
1083
|
-
return agent;
|
|
1084
|
-
}
|
|
1085
|
-
agent = await AgentProcess.loadFromDisk(agentId, this.agentsDir);
|
|
1086
|
-
if (agent) {
|
|
1087
|
-
await agent.readNewEvents();
|
|
1088
|
-
await agent.updateStatusFromProcess();
|
|
1089
|
-
this.agents.set(agentId, agent);
|
|
1090
|
-
return agent;
|
|
1091
|
-
}
|
|
1092
|
-
return null;
|
|
1093
|
-
}
|
|
1094
|
-
/**
|
|
1095
|
-
* Resolve a teammate reference to a single agent_id within a team.
|
|
1096
|
-
* Accepts (in priority order):
|
|
1097
|
-
* 1. exact teammate name ("alice")
|
|
1098
|
-
* 2. exact UUID ("b2438499-dc25-4a5e-9e02-9916012580b8")
|
|
1099
|
-
* 3. UUID prefix, if unique ("b2438499")
|
|
1100
|
-
*
|
|
1101
|
-
* Returns:
|
|
1102
|
-
* - { kind: 'ok', agentId } when exactly one teammate matches
|
|
1103
|
-
* - { kind: 'none' } when nothing matches
|
|
1104
|
-
* - { kind: 'ambiguous', matches } when the prefix matches multiple ids
|
|
1105
|
-
*/
|
|
1106
|
-
async resolveAgentIdInTask(taskName, ref) {
|
|
1107
|
-
const agents = await this.listByTask(taskName);
|
|
1108
|
-
const byName = agents.find((a) => a.name === ref);
|
|
1109
|
-
if (byName)
|
|
1110
|
-
return { kind: 'ok', agentId: byName.agentId };
|
|
1111
|
-
const exact = agents.find((a) => a.agentId === ref);
|
|
1112
|
-
if (exact)
|
|
1113
|
-
return { kind: 'ok', agentId: exact.agentId };
|
|
1114
|
-
const prefix = agents.filter((a) => a.agentId.startsWith(ref));
|
|
1115
|
-
if (prefix.length === 1)
|
|
1116
|
-
return { kind: 'ok', agentId: prefix[0].agentId };
|
|
1117
|
-
if (prefix.length === 0)
|
|
1118
|
-
return { kind: 'none' };
|
|
1119
|
-
return { kind: 'ambiguous', matches: prefix.map((a) => a.agentId) };
|
|
1120
|
-
}
|
|
1121
|
-
async listAll() {
|
|
1122
|
-
await this.initialize();
|
|
1123
|
-
const agents = Array.from(this.agents.values());
|
|
1124
|
-
for (const agent of agents) {
|
|
1125
|
-
await agent.readNewEvents();
|
|
1126
|
-
await agent.updateStatusFromProcess();
|
|
1127
|
-
await this.maybeFireCompletionHook(agent);
|
|
1128
|
-
}
|
|
1129
|
-
return agents;
|
|
1130
|
-
}
|
|
1131
|
-
/**
|
|
1132
|
-
* Fire the completion hook exactly once per teammate when it transitions
|
|
1133
|
-
* into a terminal state. Errors are logged but never thrown — a ledger
|
|
1134
|
-
* backend outage must not break status polling.
|
|
1135
|
-
*/
|
|
1136
|
-
async maybeFireCompletionHook(agent) {
|
|
1137
|
-
if (!this.completionHook)
|
|
1138
|
-
return;
|
|
1139
|
-
const terminal = agent.status === AgentStatus.COMPLETED ||
|
|
1140
|
-
agent.status === AgentStatus.FAILED ||
|
|
1141
|
-
agent.status === AgentStatus.STOPPED;
|
|
1142
|
-
if (!terminal)
|
|
1143
|
-
return;
|
|
1144
|
-
if (this.syncedAgents.has(agent.agentId))
|
|
1145
|
-
return;
|
|
1146
|
-
this.syncedAgents.add(agent.agentId);
|
|
1147
|
-
try {
|
|
1148
|
-
await this.completionHook(agent);
|
|
1149
|
-
}
|
|
1150
|
-
catch (err) {
|
|
1151
|
-
console.warn(`[ledger sync] completion hook failed for ${agent.agentId}:`, err);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
async listRunning() {
|
|
1155
|
-
const all = await this.listAll();
|
|
1156
|
-
return all.filter(a => a.status === AgentStatus.RUNNING);
|
|
1157
|
-
}
|
|
1158
|
-
async listCompleted() {
|
|
1159
|
-
const all = await this.listAll();
|
|
1160
|
-
return all.filter(a => a.status !== AgentStatus.RUNNING);
|
|
1161
|
-
}
|
|
1162
|
-
async listByTask(taskName) {
|
|
1163
|
-
const all = await this.listAll();
|
|
1164
|
-
return all.filter(a => a.taskName === taskName);
|
|
1165
|
-
}
|
|
1166
|
-
async listByParentSession(parentSessionId) {
|
|
1167
|
-
const all = await this.listAll();
|
|
1168
|
-
return all.filter(a => a.parentSessionId === parentSessionId);
|
|
1169
|
-
}
|
|
1170
|
-
async stopByTask(taskName) {
|
|
1171
|
-
const agents = await this.listByTask(taskName);
|
|
1172
|
-
const stopped = [];
|
|
1173
|
-
const alreadyStopped = [];
|
|
1174
|
-
for (const agent of agents) {
|
|
1175
|
-
if (agent.status === AgentStatus.RUNNING) {
|
|
1176
|
-
const success = await this.stop(agent.agentId);
|
|
1177
|
-
if (success) {
|
|
1178
|
-
stopped.push(agent.agentId);
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
else {
|
|
1182
|
-
alreadyStopped.push(agent.agentId);
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
return { stopped, alreadyStopped };
|
|
1186
|
-
}
|
|
1187
|
-
async stop(agentId) {
|
|
1188
|
-
await this.initialize();
|
|
1189
|
-
const agent = this.agents.get(agentId);
|
|
1190
|
-
if (!agent) {
|
|
1191
|
-
return false;
|
|
1192
|
-
}
|
|
1193
|
-
if (agent.pid && agent.status === AgentStatus.RUNNING) {
|
|
1194
|
-
try {
|
|
1195
|
-
process.kill(-agent.pid, 'SIGTERM');
|
|
1196
|
-
debug(`Sent SIGTERM to agent ${agentId} (PID ${agent.pid})`);
|
|
1197
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1198
|
-
if (agent.isProcessAlive()) {
|
|
1199
|
-
process.kill(-agent.pid, 'SIGKILL');
|
|
1200
|
-
debug(`Sent SIGKILL to agent ${agentId}`);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
catch {
|
|
1204
|
-
}
|
|
1205
|
-
agent.status = AgentStatus.STOPPED;
|
|
1206
|
-
agent.completedAt = new Date();
|
|
1207
|
-
await agent.saveMeta();
|
|
1208
|
-
debug(`Stopped agent ${agentId}`);
|
|
1209
|
-
return true;
|
|
1210
|
-
}
|
|
1211
|
-
return false;
|
|
1212
|
-
}
|
|
1213
|
-
async cleanupPartialAgent(agent) {
|
|
1214
|
-
this.agents.delete(agent.agentId);
|
|
1215
|
-
try {
|
|
1216
|
-
const agentDir = await agent.getAgentDir();
|
|
1217
|
-
await fs.rm(agentDir, { recursive: true });
|
|
1218
|
-
}
|
|
1219
|
-
catch (err) {
|
|
1220
|
-
console.warn(`Failed to clean up agent directory:`, err);
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
async cleanupOldAgents() {
|
|
1224
|
-
const completed = await this.listCompleted();
|
|
1225
|
-
if (completed.length > this.maxAgents) {
|
|
1226
|
-
completed.sort((a, b) => {
|
|
1227
|
-
const aTime = a.completedAt?.getTime() || 0;
|
|
1228
|
-
const bTime = b.completedAt?.getTime() || 0;
|
|
1229
|
-
return aTime - bTime;
|
|
1230
|
-
});
|
|
1231
|
-
for (const agent of completed.slice(0, completed.length - this.maxAgents)) {
|
|
1232
|
-
this.agents.delete(agent.agentId);
|
|
1233
|
-
try {
|
|
1234
|
-
const agentDir = await agent.getAgentDir();
|
|
1235
|
-
await fs.rm(agentDir, { recursive: true });
|
|
1236
|
-
}
|
|
1237
|
-
catch (err) {
|
|
1238
|
-
console.warn(`Failed to cleanup old agent ${agent.agentId}:`, err);
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
//# sourceMappingURL=agents.js.map
|