@desplega.ai/agent-swarm 1.20.0 → 1.51.2
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 +271 -169
- package/openapi.json +5015 -0
- package/package.json +40 -7
- package/plugin/commands/close-issue.md +7 -3
- package/plugin/commands/create-pr.md +18 -12
- package/plugin/commands/implement-issue.md +7 -3
- package/plugin/commands/respond-github.md +8 -4
- package/plugin/commands/review-pr.md +44 -10
- package/plugin/commands/start-leader.md +1 -3
- package/plugin/commands/start-worker.md +1 -3
- package/plugin/commands/work-on-task.md +22 -3
- package/plugin/pi-skills/close-issue/SKILL.md +90 -0
- package/plugin/pi-skills/create-pr/SKILL.md +99 -0
- package/plugin/pi-skills/implement-issue/SKILL.md +135 -0
- package/plugin/pi-skills/investigate-sentry-issue/SKILL.md +138 -0
- package/plugin/pi-skills/respond-github/SKILL.md +98 -0
- package/plugin/pi-skills/review-offered-task/SKILL.md +45 -0
- package/plugin/pi-skills/review-pr/SKILL.md +261 -0
- package/plugin/pi-skills/start-leader/SKILL.md +121 -0
- package/plugin/pi-skills/start-worker/SKILL.md +60 -0
- package/plugin/pi-skills/swarm-chat/SKILL.md +82 -0
- package/plugin/pi-skills/todos/SKILL.md +66 -0
- package/plugin/pi-skills/work-on-task/SKILL.md +65 -0
- package/plugin/skills/artifacts/examples/approval-flow.ts +34 -0
- package/plugin/skills/artifacts/examples/hono-dashboard.ts +31 -0
- package/plugin/skills/artifacts/examples/multi-artifact.ts +20 -0
- package/plugin/skills/artifacts/examples/static-report.sh +17 -0
- package/plugin/skills/artifacts/skill.md +71 -0
- package/src/agentmail/app.ts +65 -0
- package/src/agentmail/handlers.ts +262 -0
- package/src/agentmail/index.ts +9 -0
- package/src/agentmail/templates.ts +111 -0
- package/src/agentmail/types.ts +51 -0
- package/src/artifact-sdk/browser-sdk.ts +30 -0
- package/src/artifact-sdk/index.ts +2 -0
- package/src/artifact-sdk/localtunnel.d.ts +20 -0
- package/src/artifact-sdk/port.ts +12 -0
- package/src/artifact-sdk/server.ts +156 -0
- package/src/artifact-sdk/tunnel.ts +19 -0
- package/src/be/chunking.ts +193 -0
- package/src/be/db-queries/oauth.ts +90 -0
- package/src/be/db-queries/tracker.ts +182 -0
- package/src/be/db.ts +3327 -784
- package/src/be/embedding.ts +80 -0
- package/src/be/migrations/001_initial.sql +409 -0
- package/src/be/migrations/002_one_time_schedules.sql +59 -0
- package/src/be/migrations/003_workflows.sql +51 -0
- package/src/be/migrations/004_workflow_source.sql +81 -0
- package/src/be/migrations/005_epic_next_steps.sql +2 -0
- package/src/be/migrations/006_vcs_provider.sql +94 -0
- package/src/be/migrations/007_task_dir.sql +2 -0
- package/src/be/migrations/008_workflow_redesign.sql +85 -0
- package/src/be/migrations/009_tracker_integration.sql +144 -0
- package/src/be/migrations/010_step_diagnostics.sql +1 -0
- package/src/be/migrations/011_step_next_port.sql +1 -0
- package/src/be/migrations/012_trigger_schema.sql +1 -0
- package/src/be/migrations/013_task_output_schema.sql +2 -0
- package/src/be/migrations/014_prompt_templates.sql +33 -0
- package/src/be/migrations/015_workflow_workspace.sql +3 -0
- package/src/be/migrations/016_active_session_runner_session.sql +4 -0
- package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
- package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
- package/src/be/migrations/runner.ts +188 -0
- package/src/be/seed.ts +62 -0
- package/src/cli.tsx +231 -299
- package/src/commands/artifact.ts +241 -0
- package/src/commands/onboard/compose-generator.ts +169 -0
- package/src/commands/onboard/env-generator.ts +79 -0
- package/src/commands/onboard/manifest.ts +37 -0
- package/src/commands/onboard/presets.ts +85 -0
- package/src/commands/onboard/service-names.ts +47 -0
- package/src/commands/onboard/steps/core-credentials.tsx +111 -0
- package/src/commands/onboard/steps/custom-templates.tsx +168 -0
- package/src/commands/onboard/steps/generate.tsx +154 -0
- package/src/commands/onboard/steps/harness-credentials.tsx +195 -0
- package/src/commands/onboard/steps/harness.tsx +21 -0
- package/src/commands/onboard/steps/health-check.tsx +171 -0
- package/src/commands/onboard/steps/integration-github.tsx +105 -0
- package/src/commands/onboard/steps/integration-gitlab.tsx +79 -0
- package/src/commands/onboard/steps/integration-menu.tsx +58 -0
- package/src/commands/onboard/steps/integration-sentry.tsx +79 -0
- package/src/commands/onboard/steps/integration-slack.tsx +165 -0
- package/src/commands/onboard/steps/post-connect.tsx +145 -0
- package/src/commands/onboard/steps/post-dashboard.tsx +34 -0
- package/src/commands/onboard/steps/post-task.tsx +103 -0
- package/src/commands/onboard/steps/prereq-check.tsx +178 -0
- package/src/commands/onboard/steps/review.tsx +82 -0
- package/src/commands/onboard/steps/start.tsx +97 -0
- package/src/commands/onboard/templates.ts +34 -0
- package/src/commands/onboard/types.ts +259 -0
- package/src/commands/onboard.tsx +425 -0
- package/src/commands/runner.ts +1540 -630
- package/src/commands/setup.tsx +23 -38
- package/src/commands/shared/client-config.ts +41 -0
- package/src/commands/templates.ts +172 -0
- package/src/github/app.ts +8 -0
- package/src/github/handlers.ts +384 -151
- package/src/github/index.ts +1 -0
- package/src/github/mentions-aliases.test.ts +73 -0
- package/src/github/mentions.test.ts +3 -3
- package/src/github/mentions.ts +32 -6
- package/src/github/templates.ts +398 -0
- package/src/github/types.ts +1 -0
- package/src/gitlab/auth.ts +63 -0
- package/src/gitlab/handlers.ts +368 -0
- package/src/gitlab/index.ts +19 -0
- package/src/gitlab/reactions.ts +104 -0
- package/src/gitlab/templates.ts +140 -0
- package/src/gitlab/types.ts +130 -0
- package/src/heartbeat/heartbeat.ts +434 -0
- package/src/heartbeat/index.ts +1 -0
- package/src/heartbeat/templates.ts +30 -0
- package/src/hooks/hook.ts +555 -4
- package/src/hooks/tool-loop-detection.test.ts +158 -0
- package/src/hooks/tool-loop-detection.ts +167 -0
- package/src/http/active-sessions.ts +199 -0
- package/src/http/agents.ts +328 -0
- package/src/http/config.ts +191 -0
- package/src/http/core.ts +309 -0
- package/src/http/db-query.ts +91 -0
- package/src/http/ecosystem.ts +63 -0
- package/src/http/epics.ts +460 -0
- package/src/http/index.ts +216 -0
- package/src/http/mcp.ts +77 -0
- package/src/http/memory.ts +168 -0
- package/src/http/openapi.ts +109 -0
- package/src/http/poll.ts +299 -0
- package/src/http/prompt-templates.ts +412 -0
- package/src/http/repos.ts +195 -0
- package/src/http/route-def.ts +123 -0
- package/src/http/schedules.ts +426 -0
- package/src/http/session-data.ts +241 -0
- package/src/http/stats.ts +174 -0
- package/src/http/tasks.ts +468 -0
- package/src/http/trackers/index.ts +10 -0
- package/src/http/trackers/linear.ts +187 -0
- package/src/http/types.ts +12 -0
- package/src/http/utils.ts +87 -0
- package/src/http/webhooks.ts +432 -0
- package/src/http/workflows.ts +530 -0
- package/src/http.ts +1 -1890
- package/src/linear/README.md +65 -0
- package/src/linear/app.ts +48 -0
- package/src/linear/client.ts +18 -0
- package/src/linear/index.ts +1 -0
- package/src/linear/oauth.ts +35 -0
- package/src/linear/outbound.ts +212 -0
- package/src/linear/sync.ts +567 -0
- package/src/linear/templates.ts +47 -0
- package/src/linear/types.ts +7 -0
- package/src/linear/webhook.ts +104 -0
- package/src/oauth/README.md +66 -0
- package/src/oauth/index.ts +6 -0
- package/src/oauth/wrapper.ts +204 -0
- package/src/prompts/base-prompt.ts +150 -265
- package/src/prompts/defaults.ts +196 -0
- package/src/prompts/registry.ts +57 -0
- package/src/prompts/resolver.ts +296 -0
- package/src/prompts/session-templates.ts +604 -0
- package/src/providers/claude-adapter.ts +442 -0
- package/src/providers/index.ts +24 -0
- package/src/providers/pi-mono-adapter.ts +442 -0
- package/src/providers/pi-mono-extension.ts +624 -0
- package/src/providers/pi-mono-mcp-client.ts +124 -0
- package/src/providers/types.ts +75 -0
- package/src/scheduler/scheduler.test.ts +2 -0
- package/src/scheduler/scheduler.ts +231 -40
- package/src/server.ts +97 -6
- package/src/slack/HEURISTICS.md +105 -0
- package/src/slack/actions.ts +133 -0
- package/src/slack/app.ts +7 -0
- package/src/slack/assistant.ts +118 -0
- package/src/slack/blocks.ts +233 -0
- package/src/slack/channel-activity.ts +177 -0
- package/src/slack/commands.ts +31 -17
- package/src/slack/files.ts +1 -1
- package/src/slack/handlers.test.ts +114 -1
- package/src/slack/handlers.ts +230 -55
- package/src/slack/responses.ts +120 -67
- package/src/slack/router.ts +17 -99
- package/src/slack/templates.ts +55 -0
- package/src/slack/thread-buffer.ts +213 -0
- package/src/slack/watcher.ts +119 -4
- package/src/tests/agent-activity.test.ts +247 -0
- package/src/tests/agentmail-filters.test.ts +97 -0
- package/src/tests/artifact-sdk.test.ts +800 -0
- package/src/tests/base-prompt.test.ts +264 -0
- package/src/tests/build-pi-skills.test.ts +127 -0
- package/src/tests/channel-activity.test.ts +363 -0
- package/src/tests/claude-adapter.test.ts +126 -0
- package/src/tests/context-versioning.test.ts +425 -0
- package/src/tests/db-queries-oauth.test.ts +197 -0
- package/src/tests/db-queries-tracker.test.ts +230 -0
- package/src/tests/epics.test.ts +3 -3
- package/src/tests/error-tracker.test.ts +368 -0
- package/src/tests/fetch-resolved-env.test.ts +167 -0
- package/src/tests/generate-default-claude-md.test.ts +9 -1
- package/src/tests/generate-identity-templates.test.ts +124 -0
- package/src/tests/gitlab-auth.test.ts +109 -0
- package/src/tests/gitlab-handlers.test.ts +691 -0
- package/src/tests/gitlab-vcs-db.test.ts +177 -0
- package/src/tests/heartbeat.test.ts +364 -0
- package/src/tests/http-api-integration.test.ts +1698 -0
- package/src/tests/linear-outbound-sync.test.ts +200 -0
- package/src/tests/linear-webhook.test.ts +406 -0
- package/src/tests/match-route.test.ts +187 -0
- package/src/tests/memory.test.ts +737 -0
- package/src/tests/migration-runner-regressions.test.ts +86 -0
- package/src/tests/model-control.test.ts +338 -0
- package/src/tests/oauth-wrapper.test.ts +147 -0
- package/src/tests/onboard-compose.test.ts +138 -0
- package/src/tests/onboard-env.test.ts +174 -0
- package/src/tests/onboard-manifest.test.ts +137 -0
- package/src/tests/pi-mono-adapter.test.ts +234 -0
- package/src/tests/pool-session-logs.test.ts +199 -0
- package/src/tests/progress-dedup.test.ts +98 -0
- package/src/tests/prompt-template-github.test.ts +682 -0
- package/src/tests/prompt-template-remaining.test.ts +504 -0
- package/src/tests/prompt-template-resolver.test.ts +621 -0
- package/src/tests/prompt-template-session.test.ts +363 -0
- package/src/tests/prompt-templates-db.test.ts +616 -0
- package/src/tests/provider-adapter.test.ts +122 -0
- package/src/tests/provider-command-format.test.ts +98 -0
- package/src/tests/reload-config.test.ts +170 -0
- package/src/tests/runner-polling-api.test.ts +25 -20
- package/src/tests/scheduled-tasks.test.ts +104 -0
- package/src/tests/scheduler-backoff.test.ts +166 -0
- package/src/tests/self-improvement.test.ts +541 -0
- package/src/tests/session-attach.test.ts +536 -0
- package/src/tests/session-costs.test.ts +267 -1
- package/src/tests/slack-actions.test.ts +133 -0
- package/src/tests/slack-assistant.test.ts +136 -0
- package/src/tests/slack-blocks.test.ts +246 -0
- package/src/tests/slack-metadata-inheritance.test.ts +243 -0
- package/src/tests/slack-queue-offline.test.ts +174 -0
- package/src/tests/slack-router.test.ts +181 -0
- package/src/tests/slack-thread-buffer.test.ts +305 -0
- package/src/tests/slack-thread-followups.test.ts +298 -0
- package/src/tests/slack-watcher.test.ts +101 -0
- package/src/tests/structured-output.test.ts +307 -0
- package/src/tests/swarm-repos.test.ts +198 -0
- package/src/tests/task-cancellation.test.ts +6 -4
- package/src/tests/task-working-dir.test.ts +176 -0
- package/src/tests/template-fetch.test.ts +490 -0
- package/src/tests/tool-annotations.test.ts +371 -0
- package/src/tests/tracker-tools.test.ts +184 -0
- package/src/tests/update-profile-agentid.test.ts +248 -0
- package/src/tests/update-profile-api.test.ts +143 -3
- package/src/tests/update-profile-auth.test.ts +195 -0
- package/src/tests/validation-adapters.test.ts +86 -0
- package/src/tests/vcs-provider.test.ts +27 -0
- package/src/tests/workflow-agent-task.test.ts +196 -0
- package/src/tests/workflow-async-v2.test.ts +508 -0
- package/src/tests/workflow-convergence.test.ts +541 -0
- package/src/tests/workflow-definition-validation.test.ts +366 -0
- package/src/tests/workflow-engine-v2.test.ts +691 -0
- package/src/tests/workflow-executors.test.ts +736 -0
- package/src/tests/workflow-http-v2.test.ts +599 -0
- package/src/tests/workflow-integration-io.test.ts +902 -0
- package/src/tests/workflow-io-schemas.test.ts +624 -0
- package/src/tests/workflow-registry.test.ts +592 -0
- package/src/tests/workflow-retry-v2.test.ts +401 -0
- package/src/tests/workflow-retry-validation.test.ts +282 -0
- package/src/tests/workflow-schedule-trigger.test.ts +104 -0
- package/src/tests/workflow-template.test.ts +288 -0
- package/src/tests/workflow-trigger-schema.test.ts +359 -0
- package/src/tests/workflow-triggers-v2.test.ts +264 -0
- package/src/tests/workflow-versions.test.ts +208 -0
- package/src/tests/workflow-workspace.test.ts +272 -0
- package/src/tests/x402-client.test.ts +117 -0
- package/src/tests/x402-config.test.ts +182 -0
- package/src/tests/x402-spending-tracker.test.ts +185 -0
- package/src/tools/cancel-task.ts +2 -0
- package/src/tools/context-diff.ts +171 -0
- package/src/tools/context-history.ts +138 -0
- package/src/tools/create-channel.ts +1 -0
- package/src/tools/db-query.ts +78 -0
- package/src/tools/delete-channel.ts +132 -0
- package/src/tools/epics/assign-task-to-epic.ts +1 -0
- package/src/tools/epics/create-epic.ts +3 -2
- package/src/tools/epics/delete-epic.ts +2 -0
- package/src/tools/epics/get-epic-details.ts +2 -0
- package/src/tools/epics/list-epics.ts +2 -0
- package/src/tools/epics/unassign-task-from-epic.ts +1 -0
- package/src/tools/epics/update-epic.ts +7 -4
- package/src/tools/get-swarm.ts +2 -0
- package/src/tools/get-task-details.ts +2 -0
- package/src/tools/get-tasks.ts +27 -1
- package/src/tools/inject-learning.ts +106 -0
- package/src/tools/join-swarm.ts +17 -7
- package/src/tools/list-channels.ts +2 -0
- package/src/tools/list-services.ts +2 -0
- package/src/tools/memory-get.ts +56 -0
- package/src/tools/memory-search.ts +131 -0
- package/src/tools/my-agent-info.ts +2 -0
- package/src/tools/poll-task.ts +2 -20
- package/src/tools/post-message.ts +1 -0
- package/src/tools/prompt-templates/delete.ts +86 -0
- package/src/tools/prompt-templates/get.ts +89 -0
- package/src/tools/prompt-templates/index.ts +5 -0
- package/src/tools/prompt-templates/list.ts +95 -0
- package/src/tools/prompt-templates/preview.ts +84 -0
- package/src/tools/prompt-templates/set.ts +117 -0
- package/src/tools/read-messages.ts +2 -0
- package/src/tools/register-agentmail-inbox.ts +166 -0
- package/src/tools/register-service.ts +2 -0
- package/src/tools/schedules/create-schedule.ts +134 -24
- package/src/tools/schedules/delete-schedule.ts +2 -0
- package/src/tools/schedules/list-schedules.ts +20 -4
- package/src/tools/schedules/run-schedule-now.ts +1 -0
- package/src/tools/schedules/update-schedule.ts +49 -17
- package/src/tools/send-task.ts +132 -10
- package/src/tools/slack-download-file.ts +4 -2
- package/src/tools/slack-list-channels.ts +2 -0
- package/src/tools/slack-post.ts +2 -0
- package/src/tools/slack-read.ts +2 -0
- package/src/tools/slack-reply.ts +2 -0
- package/src/tools/slack-upload-file.ts +2 -0
- package/src/tools/store-progress.ts +205 -4
- package/src/tools/swarm-config/delete-config.ts +87 -0
- package/src/tools/swarm-config/get-config.ts +108 -0
- package/src/tools/swarm-config/index.ts +4 -0
- package/src/tools/swarm-config/list-config.ts +99 -0
- package/src/tools/swarm-config/set-config.ts +118 -0
- package/src/tools/task-action.ts +50 -5
- package/src/tools/task-dedup.ts +97 -0
- package/src/tools/templates.ts +53 -0
- package/src/tools/tool-config.ts +124 -0
- package/src/tools/tracker/index.ts +6 -0
- package/src/tools/tracker/tracker-link-epic.ts +64 -0
- package/src/tools/tracker/tracker-link-task.ts +64 -0
- package/src/tools/tracker/tracker-map-agent.ts +57 -0
- package/src/tools/tracker/tracker-status.ts +56 -0
- package/src/tools/tracker/tracker-sync-status.ts +42 -0
- package/src/tools/tracker/tracker-unlink.ts +41 -0
- package/src/tools/unregister-service.ts +2 -0
- package/src/tools/update-profile.ts +172 -17
- package/src/tools/update-service-status.ts +2 -0
- package/src/tools/utils.ts +10 -1
- package/src/tools/workflows/create-workflow.ts +129 -0
- package/src/tools/workflows/delete-workflow.ts +42 -0
- package/src/tools/workflows/get-workflow-run.ts +59 -0
- package/src/tools/workflows/get-workflow.ts +53 -0
- package/src/tools/workflows/index.ts +9 -0
- package/src/tools/workflows/list-workflow-runs.ts +48 -0
- package/src/tools/workflows/list-workflows.ts +42 -0
- package/src/tools/workflows/retry-workflow-run.ts +40 -0
- package/src/tools/workflows/trigger-workflow.ts +96 -0
- package/src/tools/workflows/update-workflow.ts +133 -0
- package/src/tracker/types.ts +51 -0
- package/src/types.ts +530 -14
- package/src/utils/credentials.test.ts +156 -0
- package/src/utils/credentials.ts +50 -0
- package/src/utils/error-tracker.ts +190 -0
- package/src/vcs/index.ts +15 -0
- package/src/vcs/types.ts +5 -0
- package/src/workflows/checkpoint.ts +121 -0
- package/src/workflows/cooldown.ts +28 -0
- package/src/workflows/definition.ts +235 -0
- package/src/workflows/engine.ts +580 -0
- package/src/workflows/event-bus.ts +29 -0
- package/src/workflows/executors/agent-task.ts +103 -0
- package/src/workflows/executors/base.ts +86 -0
- package/src/workflows/executors/code-match.ts +88 -0
- package/src/workflows/executors/index.ts +16 -0
- package/src/workflows/executors/notify.ts +93 -0
- package/src/workflows/executors/property-match.ts +104 -0
- package/src/workflows/executors/raw-llm.ts +83 -0
- package/src/workflows/executors/registry.ts +76 -0
- package/src/workflows/executors/script.ts +103 -0
- package/src/workflows/executors/validate.ts +215 -0
- package/src/workflows/executors/vcs.ts +58 -0
- package/src/workflows/index.ts +61 -0
- package/src/workflows/input.ts +46 -0
- package/src/workflows/json-schema-validator.ts +118 -0
- package/src/workflows/recovery.ts +139 -0
- package/src/workflows/resume.ts +229 -0
- package/src/workflows/retry-poller.ts +216 -0
- package/src/workflows/template.ts +74 -0
- package/src/workflows/templates.ts +86 -0
- package/src/workflows/triggers.ts +124 -0
- package/src/workflows/validation.ts +104 -0
- package/src/workflows/version.ts +44 -0
- package/src/x402/cli.ts +140 -0
- package/src/x402/client.ts +192 -0
- package/src/x402/config.ts +131 -0
- package/src/x402/index.ts +37 -0
- package/src/x402/openfort-signer.ts +83 -0
- package/src/x402/spending-tracker.ts +109 -0
- package/templates/official/coder/CLAUDE.md +49 -0
- package/templates/official/coder/IDENTITY.md +28 -0
- package/templates/official/coder/SOUL.md +43 -0
- package/templates/official/coder/TOOLS.md +40 -0
- package/templates/official/coder/config.json +23 -0
- package/templates/official/coder/start-up.sh +23 -0
- package/templates/official/content-reviewer/CLAUDE.md +68 -0
- package/templates/official/content-reviewer/IDENTITY.md +28 -0
- package/templates/official/content-reviewer/SOUL.md +44 -0
- package/templates/official/content-reviewer/TOOLS.md +37 -0
- package/templates/official/content-reviewer/config.json +23 -0
- package/templates/official/content-reviewer/start-up.sh +23 -0
- package/templates/official/content-strategist/CLAUDE.md +63 -0
- package/templates/official/content-strategist/IDENTITY.md +33 -0
- package/templates/official/content-strategist/SOUL.md +48 -0
- package/templates/official/content-strategist/TOOLS.md +47 -0
- package/templates/official/content-strategist/config.json +23 -0
- package/templates/official/content-strategist/start-up.sh +23 -0
- package/templates/official/content-writer/CLAUDE.md +72 -0
- package/templates/official/content-writer/IDENTITY.md +30 -0
- package/templates/official/content-writer/SOUL.md +46 -0
- package/templates/official/content-writer/TOOLS.md +44 -0
- package/templates/official/content-writer/config.json +23 -0
- package/templates/official/content-writer/start-up.sh +23 -0
- package/templates/official/forward-deployed-engineer/CLAUDE.md +54 -0
- package/templates/official/forward-deployed-engineer/IDENTITY.md +37 -0
- package/templates/official/forward-deployed-engineer/SOUL.md +55 -0
- package/templates/official/forward-deployed-engineer/config.json +21 -0
- package/templates/official/lead/CLAUDE.md +33 -0
- package/templates/official/lead/IDENTITY.md +36 -0
- package/templates/official/lead/SOUL.md +51 -0
- package/templates/official/lead/config.json +22 -0
- package/templates/official/researcher/CLAUDE.md +46 -0
- package/templates/official/researcher/IDENTITY.md +28 -0
- package/templates/official/researcher/SOUL.md +43 -0
- package/templates/official/researcher/config.json +21 -0
- package/templates/official/reviewer/CLAUDE.md +63 -0
- package/templates/official/reviewer/IDENTITY.md +28 -0
- package/templates/official/reviewer/SOUL.md +45 -0
- package/templates/official/reviewer/config.json +21 -0
- package/templates/official/tester/CLAUDE.md +53 -0
- package/templates/official/tester/IDENTITY.md +28 -0
- package/templates/official/tester/SOUL.md +55 -0
- package/templates/official/tester/config.json +21 -0
- package/templates/schema.ts +35 -0
- package/.claude/settings.local.json +0 -115
- package/.dockerignore +0 -61
- package/.editorconfig +0 -15
- package/.env.docker.example +0 -39
- package/.env.example +0 -40
- package/.github/workflows/ci.yml +0 -76
- package/.github/workflows/docker-and-deploy.yml +0 -117
- package/.wts-config.json +0 -4
- package/.wts-setup.ts +0 -102
- package/CLAUDE.md +0 -104
- package/CONTRIBUTING.md +0 -270
- package/DEPLOYMENT.md +0 -605
- package/Dockerfile +0 -57
- package/Dockerfile.worker +0 -157
- package/FAQ.md +0 -19
- package/MCP.md +0 -406
- package/UI.md +0 -40
- package/assets/agent-swarm-logo-orange.png +0 -0
- package/assets/agent-swarm-logo.png +0 -0
- package/assets/agent-swarm.mp4 +0 -0
- package/assets/agent-swarm.png +0 -0
- package/biome.json +0 -39
- package/deploy/DEPLOY.md +0 -60
- package/deploy/agent-swarm.service +0 -17
- package/deploy/docker-push.ts +0 -30
- package/deploy/install.ts +0 -85
- package/deploy/prod-db.ts +0 -42
- package/deploy/uninstall.ts +0 -12
- package/deploy/update.ts +0 -21
- package/docker-compose.example.yml +0 -159
- package/docker-entrypoint.sh +0 -352
- package/ecosystem.config.cjs +0 -66
- package/plugin/README.md +0 -1
- package/plugin/hooks/hooks.json +0 -71
- package/pyproject.toml +0 -9
- package/scripts/generate-mcp-docs.ts +0 -415
- package/slack-manifest.json +0 -71
- package/src/tests/get-inbox-message.test.ts +0 -145
- package/src/tools/get-inbox-message.ts +0 -89
- package/src/tools/inbox-delegate.ts +0 -113
- package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
- package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
- package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
- package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
- package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
- package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
- package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
- package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
- package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
- package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
- package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
- package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
- package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
- package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
- package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
- package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
- package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
- package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
- package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
- package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
- package/thoughts/shared/research/.gitkeep +0 -0
- package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
- package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
- package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
- package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
- package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
- package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
- package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
- package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
- package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
- package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
- package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
- package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
- package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
- package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
- package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
- package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
- package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
- package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
- package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
- package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
- package/tsconfig.json +0 -37
- package/ui/CLAUDE.md +0 -49
- package/ui/bun.lock +0 -771
- package/ui/index.html +0 -22
- package/ui/package-lock.json +0 -5290
- package/ui/package.json +0 -33
- package/ui/pnpm-lock.yaml +0 -3341
- package/ui/postcss.config.js +0 -6
- package/ui/public/logo.png +0 -0
- package/ui/src/App.tsx +0 -63
- package/ui/src/components/ActivityFeed.tsx +0 -440
- package/ui/src/components/AgentDetailPanel.tsx +0 -733
- package/ui/src/components/AgentsPanel.tsx +0 -815
- package/ui/src/components/ChatPanel.tsx +0 -1920
- package/ui/src/components/ConfigModal.tsx +0 -253
- package/ui/src/components/Dashboard.tsx +0 -832
- package/ui/src/components/EditAgentProfileModal.tsx +0 -433
- package/ui/src/components/EpicDetailPage.tsx +0 -741
- package/ui/src/components/EpicsPanel.tsx +0 -566
- package/ui/src/components/Header.tsx +0 -160
- package/ui/src/components/JsonViewer.tsx +0 -171
- package/ui/src/components/ScheduledTaskDetailPanel.tsx +0 -517
- package/ui/src/components/ScheduledTasksPanel.tsx +0 -639
- package/ui/src/components/ServicesPanel.tsx +0 -622
- package/ui/src/components/SessionLogPanel.tsx +0 -1219
- package/ui/src/components/StatsBar.tsx +0 -321
- package/ui/src/components/StatusBadge.tsx +0 -168
- package/ui/src/components/TaskDetailPanel.tsx +0 -903
- package/ui/src/components/TasksPanel.tsx +0 -614
- package/ui/src/components/UsageCharts.tsx +0 -216
- package/ui/src/components/UsageTab.tsx +0 -394
- package/ui/src/hooks/queries.ts +0 -353
- package/ui/src/hooks/useAutoScroll.ts +0 -83
- package/ui/src/index.css +0 -257
- package/ui/src/lib/api.ts +0 -268
- package/ui/src/lib/config.ts +0 -35
- package/ui/src/lib/contentPreview.ts +0 -208
- package/ui/src/lib/theme.ts +0 -214
- package/ui/src/lib/utils.ts +0 -88
- package/ui/src/main.tsx +0 -28
- package/ui/src/types/api.ts +0 -323
- package/ui/src/vite-env.d.ts +0 -1
- package/ui/tailwind.config.js +0 -37
- package/ui/tsconfig.json +0 -31
- package/ui/vite.config.ts +0 -35
- /package/{thoughts/shared/plans → templates/community}/.gitkeep +0 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swarm hooks as a pi-mono extension.
|
|
3
|
+
*
|
|
4
|
+
* Maps agent-swarm hook events (SessionStart, PreToolUse, PostToolUse,
|
|
5
|
+
* PreCompact, UserPromptSubmit, Stop) to pi-mono extension event handlers
|
|
6
|
+
* with full behavioral parity.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
10
|
+
import { checkToolLoop, clearToolHistory } from "../hooks/tool-loop-detection";
|
|
11
|
+
|
|
12
|
+
interface SwarmHooksConfig {
|
|
13
|
+
apiUrl: string;
|
|
14
|
+
apiKey: string;
|
|
15
|
+
agentId: string;
|
|
16
|
+
taskId: string;
|
|
17
|
+
isLead: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Standard headers for swarm API requests */
|
|
21
|
+
function apiHeaders(config: SwarmHooksConfig): Record<string, string> {
|
|
22
|
+
return {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
25
|
+
"X-Agent-ID": config.agentId,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Fire-and-forget fetch — logs nothing, swallows errors */
|
|
30
|
+
function fireAndForget(url: string, init: RequestInit): void {
|
|
31
|
+
void fetch(url, init).catch(() => {});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Check if a task has been cancelled */
|
|
35
|
+
async function isTaskCancelled(
|
|
36
|
+
config: SwarmHooksConfig,
|
|
37
|
+
): Promise<{ cancelled: boolean; reason?: string }> {
|
|
38
|
+
try {
|
|
39
|
+
const resp = await fetch(
|
|
40
|
+
`${config.apiUrl}/cancelled-tasks?taskId=${encodeURIComponent(config.taskId)}`,
|
|
41
|
+
{ method: "GET", headers: apiHeaders(config) },
|
|
42
|
+
);
|
|
43
|
+
if (!resp.ok) return { cancelled: false };
|
|
44
|
+
const data = (await resp.json()) as {
|
|
45
|
+
cancelled?: Array<{ id: string; failureReason?: string }>;
|
|
46
|
+
};
|
|
47
|
+
const match = data.cancelled?.find((t) => t.id === config.taskId);
|
|
48
|
+
return match ? { cancelled: true, reason: match.failureReason } : { cancelled: false };
|
|
49
|
+
} catch {
|
|
50
|
+
return { cancelled: false };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Check if agent should stop polling */
|
|
55
|
+
async function checkShouldBlockPolling(config: SwarmHooksConfig): Promise<boolean> {
|
|
56
|
+
try {
|
|
57
|
+
const resp = await fetch(`${config.apiUrl}/me`, {
|
|
58
|
+
method: "GET",
|
|
59
|
+
headers: apiHeaders(config),
|
|
60
|
+
});
|
|
61
|
+
if (!resp.ok) return false;
|
|
62
|
+
const data = (await resp.json()) as { shouldBlockPolling?: boolean };
|
|
63
|
+
return data.shouldBlockPolling === true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Fetch task details for goal reminder */
|
|
70
|
+
async function fetchTaskDetails(
|
|
71
|
+
config: SwarmHooksConfig,
|
|
72
|
+
): Promise<{ id: string; task: string; progress?: string } | null> {
|
|
73
|
+
try {
|
|
74
|
+
const resp = await fetch(`${config.apiUrl}/api/tasks/${config.taskId}`, {
|
|
75
|
+
method: "GET",
|
|
76
|
+
headers: apiHeaders(config),
|
|
77
|
+
});
|
|
78
|
+
if (!resp.ok) return null;
|
|
79
|
+
return (await resp.json()) as { id: string; task: string; progress?: string };
|
|
80
|
+
} catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Sync identity files (SOUL.md, IDENTITY.md, TOOLS.md) to server */
|
|
86
|
+
async function syncIdentityFilesToServer(
|
|
87
|
+
config: SwarmHooksConfig,
|
|
88
|
+
changeSource: "self_edit" | "session_sync" = "session_sync",
|
|
89
|
+
): Promise<void> {
|
|
90
|
+
const updates: Record<string, string> = {};
|
|
91
|
+
const paths: Record<string, string> = {
|
|
92
|
+
soulMd: "/workspace/SOUL.md",
|
|
93
|
+
identityMd: "/workspace/IDENTITY.md",
|
|
94
|
+
toolsMd: "/workspace/TOOLS.md",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
for (const [key, path] of Object.entries(paths)) {
|
|
98
|
+
try {
|
|
99
|
+
const file = Bun.file(path);
|
|
100
|
+
if (await file.exists()) {
|
|
101
|
+
const content = await file.text();
|
|
102
|
+
if (content.trim() && content.length <= 65536) {
|
|
103
|
+
updates[key] = content;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
/* skip */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (Object.keys(updates).length === 0) return;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
await fetch(`${config.apiUrl}/api/agents/${config.agentId}/profile`, {
|
|
115
|
+
method: "PUT",
|
|
116
|
+
headers: apiHeaders(config),
|
|
117
|
+
body: JSON.stringify({ ...updates, changeSource }),
|
|
118
|
+
});
|
|
119
|
+
} catch {
|
|
120
|
+
/* silently fail */
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Sync setup script to server */
|
|
125
|
+
async function syncSetupScriptToServer(
|
|
126
|
+
config: SwarmHooksConfig,
|
|
127
|
+
changeSource: "self_edit" | "session_sync" = "session_sync",
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
try {
|
|
130
|
+
const file = Bun.file("/workspace/start-up.sh");
|
|
131
|
+
if (!(await file.exists())) return;
|
|
132
|
+
|
|
133
|
+
const raw = await file.text();
|
|
134
|
+
if (!raw.trim()) return;
|
|
135
|
+
|
|
136
|
+
const markerStart = "# === Agent-managed setup (from DB) ===";
|
|
137
|
+
const markerEnd = "# === End agent-managed setup ===";
|
|
138
|
+
const startIdx = raw.indexOf(markerStart);
|
|
139
|
+
const endIdx = raw.indexOf(markerEnd);
|
|
140
|
+
|
|
141
|
+
let content: string;
|
|
142
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
143
|
+
content = raw.substring(startIdx + markerStart.length, endIdx).trim();
|
|
144
|
+
} else {
|
|
145
|
+
content = raw.replace(/^#!\/bin\/bash\n/, "").trim();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!content || content.length > 65536) return;
|
|
149
|
+
|
|
150
|
+
await fetch(`${config.apiUrl}/api/agents/${config.agentId}/profile`, {
|
|
151
|
+
method: "PUT",
|
|
152
|
+
headers: apiHeaders(config),
|
|
153
|
+
body: JSON.stringify({ setupScript: content, changeSource }),
|
|
154
|
+
});
|
|
155
|
+
} catch {
|
|
156
|
+
/* silently fail */
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if a path is under the agent's own subdirectory on the shared disk.
|
|
162
|
+
* Shared disk categories: thoughts, memory, downloads, misc.
|
|
163
|
+
* Each agent can only write to /workspace/shared/{category}/{agentId}/
|
|
164
|
+
*/
|
|
165
|
+
function isOwnedSharedPath(path: string, agentId: string): boolean {
|
|
166
|
+
const sharedCategories = ["thoughts", "memory", "downloads", "misc"];
|
|
167
|
+
return sharedCategories.some((cat) => path.startsWith(`/workspace/shared/${cat}/${agentId}/`));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Build the shared disk write warning message for a given agent ID.
|
|
172
|
+
*/
|
|
173
|
+
function sharedDiskWriteWarning(agentId: string): string {
|
|
174
|
+
return (
|
|
175
|
+
`⚠️ This write will fail: You don't have write access to this directory.\n\n` +
|
|
176
|
+
`On shared workspaces, each agent can only write to their own directories:\n` +
|
|
177
|
+
`- /workspace/shared/thoughts/${agentId}/\n` +
|
|
178
|
+
`- /workspace/shared/memory/${agentId}/\n` +
|
|
179
|
+
`- /workspace/shared/downloads/${agentId}/\n` +
|
|
180
|
+
`- /workspace/shared/misc/${agentId}/\n\n` +
|
|
181
|
+
`You CAN read any file on the shared disk. For writes, use your own subdirectory.`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Auto-index a file written to memory directory */
|
|
186
|
+
async function autoIndexMemoryFile(config: SwarmHooksConfig, editedPath: string): Promise<void> {
|
|
187
|
+
try {
|
|
188
|
+
const fileContent = await Bun.file(editedPath).text();
|
|
189
|
+
const isShared = editedPath.startsWith("/workspace/shared/");
|
|
190
|
+
const fileName = editedPath.split("/").pop() ?? "unnamed";
|
|
191
|
+
|
|
192
|
+
await fetch(`${config.apiUrl}/api/memory/index`, {
|
|
193
|
+
method: "POST",
|
|
194
|
+
headers: apiHeaders(config),
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
agentId: config.agentId,
|
|
197
|
+
content: fileContent,
|
|
198
|
+
name: fileName.replace(/\.\w+$/, ""),
|
|
199
|
+
scope: isShared ? "swarm" : "agent",
|
|
200
|
+
source: "file_index",
|
|
201
|
+
sourcePath: editedPath,
|
|
202
|
+
}),
|
|
203
|
+
});
|
|
204
|
+
} catch {
|
|
205
|
+
/* non-blocking */
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Fetch concurrent context for lead agents */
|
|
210
|
+
async function fetchConcurrentContext(config: SwarmHooksConfig): Promise<string | null> {
|
|
211
|
+
try {
|
|
212
|
+
const resp = await fetch(`${config.apiUrl}/api/concurrent-context`, {
|
|
213
|
+
method: "GET",
|
|
214
|
+
headers: apiHeaders(config),
|
|
215
|
+
});
|
|
216
|
+
if (!resp.ok) return null;
|
|
217
|
+
|
|
218
|
+
const data = (await resp.json()) as {
|
|
219
|
+
processingInboxMessages: Array<{ content: string; source: string; createdAt: string }>;
|
|
220
|
+
recentTaskDelegations: Array<{
|
|
221
|
+
task: string;
|
|
222
|
+
agentName: string | null;
|
|
223
|
+
status: string;
|
|
224
|
+
}>;
|
|
225
|
+
activeSwarmTasks: Array<{
|
|
226
|
+
task: string;
|
|
227
|
+
agentName: string | null;
|
|
228
|
+
status: string;
|
|
229
|
+
}>;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const lines: string[] = [];
|
|
233
|
+
|
|
234
|
+
if (data.processingInboxMessages.length > 0) {
|
|
235
|
+
lines.push("=== CONCURRENT SESSION AWARENESS ===");
|
|
236
|
+
lines.push("");
|
|
237
|
+
lines.push("**Other sessions are currently processing these inbox messages:**");
|
|
238
|
+
for (const msg of data.processingInboxMessages) {
|
|
239
|
+
const preview = msg.content.length > 120 ? `${msg.content.slice(0, 120)}...` : msg.content;
|
|
240
|
+
lines.push(`- [${msg.source}] "${preview}" (received ${msg.createdAt})`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (data.recentTaskDelegations.length > 0) {
|
|
245
|
+
if (lines.length === 0) lines.push("=== CONCURRENT SESSION AWARENESS ===");
|
|
246
|
+
lines.push("");
|
|
247
|
+
lines.push("**Recent task delegations (last 5 min):**");
|
|
248
|
+
for (const task of data.recentTaskDelegations) {
|
|
249
|
+
const preview = task.task.length > 120 ? `${task.task.slice(0, 120)}...` : task.task;
|
|
250
|
+
lines.push(`- "${preview}" → ${task.agentName ?? "unassigned"} [${task.status}]`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (data.activeSwarmTasks.length > 0) {
|
|
255
|
+
if (lines.length === 0) lines.push("=== CONCURRENT SESSION AWARENESS ===");
|
|
256
|
+
lines.push("");
|
|
257
|
+
lines.push("**Currently active tasks across the swarm:**");
|
|
258
|
+
for (const task of data.activeSwarmTasks) {
|
|
259
|
+
const preview = task.task.length > 100 ? `${task.task.slice(0, 100)}...` : task.task;
|
|
260
|
+
lines.push(`- ${task.agentName ?? "unassigned"}: "${preview}" [${task.status}]`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (lines.length > 0) {
|
|
265
|
+
lines.push("");
|
|
266
|
+
lines.push(
|
|
267
|
+
"IMPORTANT: Avoid duplicating work that is already being handled by other sessions or agents.",
|
|
268
|
+
);
|
|
269
|
+
lines.push("=== END CONCURRENT SESSION AWARENESS ===");
|
|
270
|
+
return lines.join("\n");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return null;
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Run session summarization via Claude Haiku on shutdown */
|
|
280
|
+
async function summarizeSession(
|
|
281
|
+
config: SwarmHooksConfig,
|
|
282
|
+
sessionFile: string | undefined,
|
|
283
|
+
): Promise<void> {
|
|
284
|
+
if (!sessionFile) return;
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
let transcript = "";
|
|
288
|
+
try {
|
|
289
|
+
const fullTranscript = await Bun.file(sessionFile).text();
|
|
290
|
+
transcript = fullTranscript.length > 20000 ? fullTranscript.slice(-20000) : fullTranscript;
|
|
291
|
+
} catch {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (transcript.length <= 100) return;
|
|
296
|
+
|
|
297
|
+
let taskContext = "";
|
|
298
|
+
try {
|
|
299
|
+
const taskDetails = await fetchTaskDetails(config);
|
|
300
|
+
if (taskDetails) {
|
|
301
|
+
taskContext = `Task: ${taskDetails.task}`;
|
|
302
|
+
}
|
|
303
|
+
} catch {
|
|
304
|
+
/* no task context */
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const summarizePrompt = `You are summarizing an AI agent's work session. Extract ONLY high-value learnings.
|
|
308
|
+
|
|
309
|
+
DO NOT include:
|
|
310
|
+
- Generic descriptions of what was done ("worked on task X")
|
|
311
|
+
- Tool calls or file reads
|
|
312
|
+
- Routine progress updates
|
|
313
|
+
|
|
314
|
+
DO include (if present):
|
|
315
|
+
- **Mistakes made and corrections** — what went wrong and what fixed it
|
|
316
|
+
- **Discovered patterns** — reusable approaches, APIs, or codebase conventions
|
|
317
|
+
- **Codebase knowledge** — important file paths, architecture decisions, gotchas
|
|
318
|
+
- **Environment knowledge** — service URLs, config details, tool quirks
|
|
319
|
+
- **Failed approaches** — what was tried and didn't work (and why)
|
|
320
|
+
|
|
321
|
+
Format as a bulleted list of concrete, reusable facts. If the session was routine with no significant learnings, respond with exactly: "No significant learnings."
|
|
322
|
+
${taskContext ? `\nTask context: ${taskContext}` : ""}
|
|
323
|
+
Transcript:
|
|
324
|
+
${transcript}`;
|
|
325
|
+
|
|
326
|
+
const tmpFile = `/tmp/session-summary-${Date.now()}.txt`;
|
|
327
|
+
await Bun.write(tmpFile, summarizePrompt);
|
|
328
|
+
const proc = Bun.spawn(
|
|
329
|
+
[
|
|
330
|
+
"bash",
|
|
331
|
+
"-c",
|
|
332
|
+
`cat "${tmpFile}" | ${process.env.CLAUDE_BINARY || "claude"} -p --model haiku --output-format json`,
|
|
333
|
+
],
|
|
334
|
+
{
|
|
335
|
+
stdout: "pipe",
|
|
336
|
+
stderr: "pipe",
|
|
337
|
+
env: { ...process.env, SKIP_SESSION_SUMMARY: "1" },
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
const timeoutId = setTimeout(() => proc.kill(), 30000);
|
|
341
|
+
const result = { stdout: await new Response(proc.stdout).text() };
|
|
342
|
+
clearTimeout(timeoutId);
|
|
343
|
+
await Bun.$`rm -f ${tmpFile}`.quiet();
|
|
344
|
+
|
|
345
|
+
let summary: string;
|
|
346
|
+
try {
|
|
347
|
+
const summaryOutput = JSON.parse(result.stdout);
|
|
348
|
+
summary = summaryOutput.result ?? result.stdout;
|
|
349
|
+
} catch {
|
|
350
|
+
summary = result.stdout;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (
|
|
354
|
+
summary &&
|
|
355
|
+
summary.length > 20 &&
|
|
356
|
+
!summary.trim().toLowerCase().includes("no significant learnings")
|
|
357
|
+
) {
|
|
358
|
+
await fetch(`${config.apiUrl}/api/memory/index`, {
|
|
359
|
+
method: "POST",
|
|
360
|
+
headers: apiHeaders(config),
|
|
361
|
+
body: JSON.stringify({
|
|
362
|
+
agentId: config.agentId,
|
|
363
|
+
content: summary,
|
|
364
|
+
name: taskContext
|
|
365
|
+
? `Session: ${taskContext.slice(0, 80)}`
|
|
366
|
+
: `Session: ${new Date().toISOString().slice(0, 16)}`,
|
|
367
|
+
scope: "agent",
|
|
368
|
+
source: "session_summary",
|
|
369
|
+
sourceTaskId: config.taskId,
|
|
370
|
+
}),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
/* non-blocking */
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Create the swarm hooks extension factory for pi-mono.
|
|
380
|
+
*
|
|
381
|
+
* This maps all agent-swarm hook behaviors to pi-mono extension events
|
|
382
|
+
* with full behavioral parity to src/hooks/hook.ts.
|
|
383
|
+
*/
|
|
384
|
+
export function createSwarmHooksExtension(config: SwarmHooksConfig): ExtensionFactory {
|
|
385
|
+
return (pi) => {
|
|
386
|
+
// === session_start → SessionStart ===
|
|
387
|
+
pi.on("session_start", async (_event, _ctx) => {
|
|
388
|
+
// Ping server
|
|
389
|
+
fireAndForget(`${config.apiUrl}/ping`, {
|
|
390
|
+
method: "POST",
|
|
391
|
+
headers: apiHeaders(config),
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Clear stale tool loop history
|
|
395
|
+
if (config.taskId) {
|
|
396
|
+
await clearToolHistory(config.taskId);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Lead agents: inject concurrent context
|
|
400
|
+
if (config.isLead) {
|
|
401
|
+
const ctx = await fetchConcurrentContext(config);
|
|
402
|
+
if (ctx) {
|
|
403
|
+
console.log(ctx);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// === tool_call → PreToolUse ===
|
|
409
|
+
pi.on("tool_call", async (event, _ctx) => {
|
|
410
|
+
// Workers only: check task cancellation
|
|
411
|
+
if (!config.isLead && config.taskId) {
|
|
412
|
+
const { cancelled, reason } = await isTaskCancelled(config);
|
|
413
|
+
if (cancelled) {
|
|
414
|
+
const cancelReason = reason || "Task cancelled by lead or creator";
|
|
415
|
+
return {
|
|
416
|
+
block: true,
|
|
417
|
+
reason:
|
|
418
|
+
`🛑 TASK CANCELLED: Your current task (${config.taskId.slice(0, 8)}) has been cancelled. Reason: "${cancelReason}". ` +
|
|
419
|
+
`Stop working on this task immediately. Do NOT continue making tool calls. ` +
|
|
420
|
+
`Use store-progress to acknowledge the cancellation and mark the task as failed, then wait for new tasks.`,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Workers only: tool loop detection
|
|
426
|
+
if (!config.isLead && config.taskId) {
|
|
427
|
+
const toolName = event.toolName;
|
|
428
|
+
const toolInput = "input" in event ? (event.input as Record<string, unknown>) : {};
|
|
429
|
+
|
|
430
|
+
const loopResult = await checkToolLoop(config.taskId, toolName, toolInput);
|
|
431
|
+
|
|
432
|
+
if (loopResult.blocked) {
|
|
433
|
+
return {
|
|
434
|
+
block: true,
|
|
435
|
+
reason:
|
|
436
|
+
`LOOP DETECTED: ${loopResult.reason} ` +
|
|
437
|
+
"Stop repeating this action and try a fundamentally different approach. " +
|
|
438
|
+
"If you're truly stuck, use store-progress to report the blocker.",
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (loopResult.severity === "warning" && loopResult.reason) {
|
|
443
|
+
console.log(`Warning: ${loopResult.reason}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Block poll-task when polling limit reached
|
|
448
|
+
if (event.toolName?.endsWith("poll-task")) {
|
|
449
|
+
const shouldBlock = await checkShouldBlockPolling(config);
|
|
450
|
+
if (shouldBlock) {
|
|
451
|
+
return {
|
|
452
|
+
block: true,
|
|
453
|
+
reason:
|
|
454
|
+
"🛑 POLLING LIMIT REACHED: You have exceeded the maximum empty poll attempts. " +
|
|
455
|
+
"EXIT NOW - do not make any more tool calls.",
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Shared disk write prevention (Archil only — skip in local dev)
|
|
461
|
+
if (process.env.ARCHIL_MOUNT_TOKEN) {
|
|
462
|
+
// Pi-mono uses lowercase tool names: "write", "edit"
|
|
463
|
+
if (event.toolName === "write" || event.toolName === "edit") {
|
|
464
|
+
const toolInput =
|
|
465
|
+
"input" in event
|
|
466
|
+
? (event.input as { file_path?: string; path?: string } | undefined)
|
|
467
|
+
: undefined;
|
|
468
|
+
const targetPath = toolInput?.file_path || toolInput?.path || "";
|
|
469
|
+
if (
|
|
470
|
+
targetPath.startsWith("/workspace/shared/") &&
|
|
471
|
+
!isOwnedSharedPath(targetPath, config.agentId)
|
|
472
|
+
) {
|
|
473
|
+
console.log(sharedDiskWriteWarning(config.agentId));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return undefined;
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// === tool_result → PostToolUse ===
|
|
482
|
+
pi.on("tool_result", async (event, _ctx) => {
|
|
483
|
+
// Heartbeat (workers only, fire-and-forget)
|
|
484
|
+
if (!config.isLead && config.taskId) {
|
|
485
|
+
fireAndForget(`${config.apiUrl}/api/active-sessions/heartbeat/${config.taskId}`, {
|
|
486
|
+
method: "PUT",
|
|
487
|
+
headers: apiHeaders(config),
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Activity timestamp (fire-and-forget)
|
|
492
|
+
fireAndForget(`${config.apiUrl}/api/agents/${config.agentId}/activity`, {
|
|
493
|
+
method: "PUT",
|
|
494
|
+
headers: apiHeaders(config),
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// Shared disk write failure detection (Archil only — safety net)
|
|
498
|
+
if (process.env.ARCHIL_MOUNT_TOKEN) {
|
|
499
|
+
// Pi-mono uses lowercase tool names
|
|
500
|
+
if (event.toolName === "write" || event.toolName === "edit" || event.toolName === "bash") {
|
|
501
|
+
const resultStr = JSON.stringify(event.content ?? []);
|
|
502
|
+
if (resultStr.includes("Read-only file system")) {
|
|
503
|
+
const resultInput = event.input as { file_path?: string; path?: string } | undefined;
|
|
504
|
+
const resultPath = resultInput?.file_path || resultInput?.path || "";
|
|
505
|
+
if (
|
|
506
|
+
resultPath.startsWith("/workspace/shared/") &&
|
|
507
|
+
!isOwnedSharedPath(resultPath, config.agentId)
|
|
508
|
+
) {
|
|
509
|
+
console.log(sharedDiskWriteWarning(config.agentId));
|
|
510
|
+
} else if (!resultPath) {
|
|
511
|
+
// Bash tool — no file_path, just warn generically
|
|
512
|
+
console.log(sharedDiskWriteWarning(config.agentId));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// File sync: check if tool wrote to identity files or memory dirs
|
|
519
|
+
// Pi-mono uses tool names: "write", "edit" (lowercase, unlike Claude's "Write", "Edit")
|
|
520
|
+
const toolName = event.toolName;
|
|
521
|
+
const input = event.input as { file_path?: string; path?: string } | undefined;
|
|
522
|
+
const editedPath = input?.file_path || input?.path;
|
|
523
|
+
|
|
524
|
+
if ((toolName === "write" || toolName === "edit") && editedPath) {
|
|
525
|
+
// Identity files
|
|
526
|
+
if (
|
|
527
|
+
editedPath === "/workspace/SOUL.md" ||
|
|
528
|
+
editedPath === "/workspace/IDENTITY.md" ||
|
|
529
|
+
editedPath === "/workspace/TOOLS.md"
|
|
530
|
+
) {
|
|
531
|
+
void syncIdentityFilesToServer(config, "self_edit");
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Setup script
|
|
535
|
+
if (editedPath.startsWith("/workspace/start-up")) {
|
|
536
|
+
void syncSetupScriptToServer(config, "self_edit");
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Memory auto-index
|
|
540
|
+
if (
|
|
541
|
+
editedPath.startsWith("/workspace/personal/memory/") ||
|
|
542
|
+
editedPath.startsWith("/workspace/shared/memory/")
|
|
543
|
+
) {
|
|
544
|
+
void autoIndexMemoryFile(config, editedPath);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Reminders
|
|
549
|
+
if (config.isLead && event.toolName?.endsWith("send-task")) {
|
|
550
|
+
console.log(
|
|
551
|
+
"Task sent successfully. Monitor progress using the get-task-details tool periodically.",
|
|
552
|
+
);
|
|
553
|
+
} else if (!config.isLead) {
|
|
554
|
+
console.log(
|
|
555
|
+
"Remember to call store-progress periodically to update the lead agent on your progress.",
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return undefined;
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// === context → PreCompact ===
|
|
563
|
+
// The context event allows injecting messages before compaction.
|
|
564
|
+
// We log the goal reminder to console (it gets captured in context).
|
|
565
|
+
pi.on("context", async (_event, _ctx) => {
|
|
566
|
+
if (!config.taskId) return undefined;
|
|
567
|
+
|
|
568
|
+
try {
|
|
569
|
+
const taskDetails = await fetchTaskDetails(config);
|
|
570
|
+
if (taskDetails) {
|
|
571
|
+
const reminder = [
|
|
572
|
+
"=== GOAL REMINDER (injected before context compaction) ===",
|
|
573
|
+
`Task ID: ${taskDetails.id}`,
|
|
574
|
+
`Task: ${taskDetails.task}`,
|
|
575
|
+
];
|
|
576
|
+
if (taskDetails.progress) {
|
|
577
|
+
reminder.push(`Current Progress: ${taskDetails.progress}`);
|
|
578
|
+
}
|
|
579
|
+
reminder.push("=== Continue working on this task after compaction ===");
|
|
580
|
+
console.log(reminder.join("\n"));
|
|
581
|
+
}
|
|
582
|
+
} catch {
|
|
583
|
+
/* don't block compaction */
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return undefined;
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// === input → UserPromptSubmit ===
|
|
590
|
+
pi.on("input", async (_event, _ctx) => {
|
|
591
|
+
// Workers only: check task cancellation at start of new iteration
|
|
592
|
+
if (!config.isLead && config.taskId) {
|
|
593
|
+
const { cancelled, reason } = await isTaskCancelled(config);
|
|
594
|
+
if (cancelled) {
|
|
595
|
+
const cancelReason = reason || "Task cancelled by lead or creator";
|
|
596
|
+
console.log(
|
|
597
|
+
`🛑 TASK CANCELLED: ${cancelReason}. Stop working and use store-progress to acknowledge.`,
|
|
598
|
+
);
|
|
599
|
+
return { action: "handled" as const };
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return undefined;
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// === session_shutdown → Stop ===
|
|
606
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
607
|
+
// Sync identity files and setup script
|
|
608
|
+
await syncIdentityFilesToServer(config);
|
|
609
|
+
await syncSetupScriptToServer(config);
|
|
610
|
+
|
|
611
|
+
// Session summarization — get session file from context's session manager
|
|
612
|
+
const sessionFile = ctx.sessionManager.getSessionFile?.();
|
|
613
|
+
if (!process.env.SKIP_SESSION_SUMMARY) {
|
|
614
|
+
await summarizeSession(config, sessionFile);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Mark agent offline
|
|
618
|
+
fireAndForget(`${config.apiUrl}/close`, {
|
|
619
|
+
method: "POST",
|
|
620
|
+
headers: apiHeaders(config),
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
};
|
|
624
|
+
}
|