@desplega.ai/agent-swarm 1.157.0 → 1.158.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/dist/{acp-adapter-qhqrk4q0.js → acp-adapter-xaq4n6pc.js} +1 -1
- package/dist/{actions-9rrg0zxm.js → actions-p9hz5yat.js} +15 -15
- package/dist/{app-enysy1zd.js → app-g26wdcz6.js} +4 -4
- package/dist/{artifact-td4q4dch.js → artifact-3v5t9jtg.js} +1 -1
- package/dist/{assistant-zegamy48.js → assistant-wydfqkb3.js} +16 -16
- package/dist/{boot-reembed-r6shx5qx.js → boot-reembed-g5ckg9yb.js} +6 -6
- package/dist/{boot-reembed-zfxb937k.js → boot-reembed-se8zmdcp.js} +5 -5
- package/dist/{boot-scrub-logs-k5y0se6r.js → boot-scrub-logs-wfxqan61.js} +3 -3
- package/dist/{seed-pricing-vqxa06qf.js → boot-seeds-s3q01xj3.js} +10 -10
- package/dist/{claude-adapter-5yc7hxwv.js → claude-adapter-tv2x4f36.js} +7 -7
- package/dist/{claude-managed-adapter-ea2aga6w.js → claude-managed-adapter-41b2s7fd.js} +2 -2
- package/dist/{claude-sdk-session-j4ywn3xf.js → claude-sdk-session-t442qz6f.js} +8 -8
- package/dist/{cli-e781c6f9.js → cli-0xjkj1j7.js} +1 -0
- package/dist/{cli-58rgvkt4.js → cli-1hkrch3k.js} +2 -2
- package/dist/{cli-yscm6cv8.js → cli-20cj37vt.js} +1866 -9
- package/dist/{cli-h0wdadsx.js → cli-4198qc9v.js} +7 -3
- package/dist/{cli-awwphhcs.js → cli-47m6dkd3.js} +362 -126
- package/dist/{cli-wb2zvbfg.js → cli-53sz8gkz.js} +1 -1
- package/dist/{cli-005pze6s.js → cli-5d10vd2m.js} +1 -1
- package/dist/{cli-v53rbqp3.js → cli-65hjmev5.js} +57 -17
- package/dist/{cli-scwq51gn.js → cli-68w09sjw.js} +7 -1
- package/dist/{cli-s98skd5r.js → cli-7gtvvpbh.js} +4 -4
- package/dist/{cli-y3nxwx0t.js → cli-7s3syk22.js} +18 -11
- package/dist/{cli-74h6f2bp.js → cli-8qmme6vd.js} +2 -2
- package/dist/{cli-wsed24mk.js → cli-987hg0aa.js} +2 -2
- package/dist/{cli-9n76jszt.js → cli-9httdpyq.js} +1 -1
- package/dist/{cli-f3va018d.js → cli-aky8tctm.js} +26 -93
- package/dist/{cli-sgyqyyxe.js → cli-c07r9a2m.js} +2 -1
- package/dist/{cli-c41wjx0r.js → cli-fgqxwrpv.js} +291 -12
- package/dist/{cli-01vfmq8c.js → cli-ftfvggzx.js} +607 -164
- package/dist/{cli-ytxs7mn1.js → cli-g8djp6qe.js} +17 -0
- package/dist/{cli-avkd6mqv.js → cli-h967nb6r.js} +551 -0
- package/dist/{cli-r9ee9c4w.js → cli-j55n64b6.js} +23 -22
- package/dist/{cli-9p33cz9h.js → cli-j57qkrpq.js} +86 -28
- package/dist/{cli-tjmghmm5.js → cli-q67y4wzm.js} +6 -3
- package/dist/{cli-n6zq3kbd.js → cli-rj19hc34.js} +1 -1
- package/dist/{cli-jytrw2na.js → cli-rjehvrts.js} +4 -4
- package/dist/{cli-tpv97m71.js → cli-rqz8qbw7.js} +245 -75
- package/dist/{cli-p37sg0a9.js → cli-s8eet254.js} +1 -1
- package/dist/cli-t1ze64h3.js +811 -0
- package/dist/{cli-6jjzxv3b.js → cli-ws6rqycm.js} +3 -2
- package/dist/{cli-xf5b7y1f.js → cli-wscdvcqz.js} +13 -5
- package/dist/{cli-5j7bm4tb.js → cli-x24pshtq.js} +5 -5
- package/dist/{cli-nbp6m6gp.js → cli-xberxa1r.js} +51 -26
- package/dist/{cli-vx9p3qdt.js → cli-xhaeg2nc.js} +1 -1
- package/dist/cli.js +29 -15
- package/dist/{codex-adapter-gns3s6c3.js → codex-adapter-cnm8wk11.js} +5 -5
- package/dist/{codex-session-runner-gqnxys5w.js → codex-session-runner-gfg547zj.js} +5 -5
- package/dist/{commands-hxy2y2sp.js → commands-e3bn0p75.js} +3 -3
- package/dist/{db-w8ghhsgf.js → db-03v9kzr2.js} +17 -3
- package/dist/{dispatcher-cp1rknjz.js → dispatcher-nrmg8yht.js} +14 -14
- package/dist/{dsh-adapter-1dwg97zv.js → dsh-adapter-k03nfqa6.js} +2 -2
- package/dist/{handlers-egfhr351.js → handlers-aqcedbyb.js} +17 -17
- package/dist/{hook-x4kftwzt.js → hook-7vkskkre.js} +19 -11
- package/dist/{hook-fbxjqfjz.js → hook-ybvmrmhq.js} +4 -4
- package/dist/{http-c7jfx990.js → http-2eqa8t24.js} +432 -195
- package/dist/{index-4k9hct21.js → index-1yks3qcr.js} +14 -14
- package/dist/{index-jtvpgb8f.js → index-a3vn96n8.js} +30 -27
- package/dist/{index-dc2ek6bw.js → index-c6zsv1bd.js} +5 -5
- package/dist/{index-yy07ef2s.js → index-chgdkq36.js} +14 -14
- package/dist/{index-r6bty6tb.js → index-x71bvq0p.js} +15 -15
- package/dist/{keepalive-f7ekefyc.js → keepalive-vtkf5n99.js} +5 -5
- package/dist/{lead-h053syts.js → lead-h3sp357x.js} +26 -26
- package/dist/{maintenance-a3w6zs54.js → maintenance-jzeqtk3q.js} +6 -6
- package/dist/{oauth-refresh-sweep-vra1dhkd.js → oauth-refresh-sweep-qp3dzmdp.js} +4 -4
- package/dist/{onboard-mfgzeqxs.js → onboard-tmsp0qbz.js} +278 -81
- package/dist/{opencode-adapter-z64xjzec.js → opencode-adapter-x6x2da1z.js} +3 -3
- package/dist/{otel-impl-2ez6xk57.js → otel-impl-9mz73q65.js} +416 -301
- package/dist/{pi-mono-adapter-2b7mw16r.js → pi-mono-adapter-m6dvv0dz.js} +2 -2
- package/dist/{pricing-refresh-6sw65tck.js → pricing-refresh-v1cbr9te.js} +5 -5
- package/dist/{rbac-roles-nm6924sy.js → rbac-roles-c9wqz83x.js} +4 -4
- package/dist/{render-v2-f026j6c4.js → render-v2-gryrh9h8.js} +4 -4
- package/dist/{setup-w606fr1q.js → setup-q9720bt6.js} +2 -2
- package/dist/{virtual-modules-8qheft18.js → virtual-modules-3htpgn8x.js} +1 -1
- package/dist/{work-objects-ezmggdne.js → work-objects-b76x7deb.js} +17 -17
- package/dist/{worker-1mbyv29k.js → worker-6vrxbv41.js} +26 -26
- package/openapi.json +1626 -629
- package/package.json +2 -1
- package/src/apps/catalog.generated.json +229 -0
- package/src/apps/page-validator.ts +71 -16
- package/src/apps/row-store.ts +66 -11
- package/src/apps/sync.ts +59 -17
- package/src/artifact-sdk/browser-sdk.ts +436 -2
- package/src/be/approval-sweeps.ts +109 -0
- package/src/be/boot-seeds.ts +40 -0
- package/src/be/chunking.ts +1 -1
- package/src/be/db/agents.ts +20 -0
- package/src/be/db/approvals.ts +78 -0
- package/src/be/db/tasks/read.ts +197 -39
- package/src/be/db/tasks/write.ts +41 -9
- package/src/be/db-client.ts +107 -48
- package/src/be/db.ts +219 -76
- package/src/be/events.ts +33 -1
- package/src/be/memory/graph-expansion.ts +10 -3
- package/src/be/memory/providers/sqlite-store.ts +55 -13
- package/src/be/memory/raters/retrieval.ts +5 -2
- package/src/be/memory/reranker.ts +26 -2
- package/src/be/memory/types.ts +2 -0
- package/src/be/migrations/166_approval_request_auto_cancellation.sql +9 -0
- package/src/be/migrations/171_memory_retrieval_relevance.sql +8 -0
- package/src/be/migrations/175_workflow_runs_list_index.sql +11 -0
- package/src/be/migrations/177_events_event_agent_created_index.sql +11 -0
- package/src/be/models-catalog.ts +2 -0
- package/src/be/modelsdev-cache.json +1857 -0
- package/src/be/modelsdev-cache.ts +14 -1
- package/src/be/pricing-normalize.ts +2 -0
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-pricing.ts +3 -1
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/steering.ts +53 -3
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/be/task-citations.ts +29 -0
- package/src/cli.tsx +14 -1
- package/src/commands/onboard/compose-generator.ts +8 -3
- package/src/commands/onboard/container-engine.ts +220 -0
- package/src/commands/onboard/manifest.ts +1 -0
- package/src/commands/onboard/steps/health-check.tsx +4 -1
- package/src/commands/onboard/steps/prereq-check.tsx +50 -54
- package/src/commands/onboard/steps/start.tsx +31 -16
- package/src/commands/onboard/types.ts +7 -2
- package/src/commands/onboard.tsx +39 -4
- package/src/commands/profile-sync.ts +79 -26
- package/src/commands/runner.ts +8 -1
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/heartbeat/heartbeat.ts +263 -103
- package/src/hooks/hook.ts +21 -12
- package/src/http/agents.ts +35 -0
- package/src/http/approval-requests.ts +136 -64
- package/src/http/apps.ts +14 -3
- package/src/http/config.ts +15 -1
- package/src/http/events.ts +62 -2
- package/src/http/index.ts +6 -4
- package/src/http/memory.ts +21 -7
- package/src/http/schedules.ts +1 -1
- package/src/http/session-data.ts +9 -4
- package/src/http/sessions.ts +25 -18
- package/src/http/stats.ts +19 -7
- package/src/http/status.ts +11 -1
- package/src/http/tasks.ts +40 -8
- package/src/http/webhooks.ts +67 -46
- package/src/http/workflows.ts +15 -20
- package/src/linear/outbound.ts +37 -15
- package/src/otel-impl.ts +151 -1
- package/src/otel.ts +14 -3
- package/src/prompts/memories.ts +19 -2
- package/src/providers/claude-managed-models.ts +8 -0
- package/src/providers/modelsdev-reasoning.json +551 -0
- package/src/rbac/legacy-policy.ts +16 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/schedule-task.ts +3 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/swarm-sdk.ts +9 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +12 -7
- package/src/slack/app.ts +35 -4
- package/src/slack/connection-state.ts +17 -0
- package/src/tasks/context-key.ts +1 -0
- package/src/tasks/worker-follow-up.ts +6 -2
- package/src/tests/agent-task-activity.test.ts +74 -0
- package/src/tests/agents-list-model-display.test.ts +6 -1
- package/src/tests/approval-auto-cancellation-migration.test.ts +104 -0
- package/src/tests/approval-requests.test.ts +395 -0
- package/src/tests/approval-sweeps-config.test.ts +47 -0
- package/src/tests/approval-sweeps.test.ts +508 -0
- package/src/tests/apps-kibo-blocks.test.ts +77 -0
- package/src/tests/apps-spike.test.ts +56 -1
- package/src/tests/apps-sync-engine.test.ts +20 -9
- package/src/tests/boot-seeds.test.ts +170 -0
- package/src/tests/cancel-approval-request-tool.test.ts +115 -0
- package/src/tests/claude-managed-adapter.test.ts +6 -0
- package/src/tests/config-key-filter.test.ts +80 -0
- package/src/tests/context-window.test.ts +1 -0
- package/src/tests/db-client-busy-retry.test.ts +96 -0
- package/src/tests/db-client.test.ts +60 -1
- package/src/tests/db-task-read-contract.test.ts +10 -0
- package/src/tests/db-task-write-contract.test.ts +9 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/events-http.test.ts +103 -0
- package/src/tests/extensions-catalog.test.ts +22 -17
- package/src/tests/extensions-pre-task.test.ts +107 -0
- package/src/tests/fixtures/kibo-blocks-definition.json.txt +105 -0
- package/src/tests/follow-up-config-inheritance.test.ts +260 -0
- package/src/tests/heartbeat-orphan-resume.test.ts +84 -0
- package/src/tests/heartbeat-reboot-live.test.ts +95 -0
- package/src/tests/heartbeat-stall-cas.test.ts +80 -0
- package/src/tests/heartbeat.test.ts +99 -3
- package/src/tests/hook-mcp-config.test.ts +29 -0
- package/src/tests/http-api-integration.test.ts +16 -2
- package/src/tests/kv-storage.test.ts +62 -0
- package/src/tests/linear-outbound-sync.test.ts +120 -5
- package/src/tests/list-endpoint-slimming.test.ts +91 -0
- package/src/tests/memory-graph-organic-route.test.ts +155 -0
- package/src/tests/memory-http-recall-gating.test.ts +14 -4
- package/src/tests/memory-hybrid.test.ts +161 -1
- package/src/tests/memory-prompt-recall-crowdout.test.ts +297 -0
- package/src/tests/memory.test.ts +22 -0
- package/src/tests/onboard-container-engine.test.ts +240 -0
- package/src/tests/otel-event-loop-and-logs.test.ts +99 -0
- package/src/tests/pricing-routes.test.ts +14 -0
- package/src/tests/profile-sync-warning.test.ts +8 -4
- package/src/tests/promote-draft-task-route.test.ts +19 -0
- package/src/tests/provider-registration.test.ts +296 -42
- package/src/tests/rbac-engine.test.ts +30 -0
- package/src/tests/reasoning-effort.test.ts +8 -0
- package/src/tests/script-connections.test.ts +7 -0
- package/src/tests/sdk-allowlist.test.ts +39 -0
- package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
- package/src/tests/sessions-detail-batch.test.ts +250 -0
- package/src/tests/sessions.test.ts +118 -0
- package/src/tests/slack-config.test.ts +22 -0
- package/src/tests/sonnet-5-5-registry.test.ts +44 -0
- package/src/tests/status.test.ts +36 -1
- package/src/tests/swarm-diff.test.ts +316 -10
- package/src/tests/task-completion-idempotency.test.ts +2 -1
- package/src/tests/tasks-requested-by-filter.test.ts +3 -1
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/ui-list-slim-projections.test.ts +158 -0
- package/src/tests/webhooks-task-blocked.test.ts +127 -0
- package/src/tests/workflow-approval-recovery.test.ts +203 -0
- package/src/tests/workflow-hitl-routing.test.ts +22 -1
- package/src/tests/workflow-hitl-slack-card.test.ts +52 -0
- package/src/tests/workflow-http-v2.test.ts +99 -8
- package/src/tests/workflow-retry-validation.test.ts +56 -0
- package/src/tests/workflow-tla-races.test.ts +389 -0
- package/src/tests/workflow-validation-port-routing.test.ts +113 -1
- package/src/tools/cancel-approval-request.ts +57 -0
- package/src/tools/memory-search.ts +1 -0
- package/src/tools/request-human-input.ts +4 -2
- package/src/tools/send-task.ts +4 -0
- package/src/tools/templates.ts +19 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/workflows/list-workflow-runs.ts +1 -0
- package/src/types.ts +38 -0
- package/src/utils/context-window.ts +1 -0
- package/src/workflows/approval-cancel.ts +152 -0
- package/src/workflows/approval-notifications.ts +84 -0
- package/src/workflows/checkpoint.ts +10 -5
- package/src/workflows/definition.ts +21 -0
- package/src/workflows/engine.ts +152 -21
- package/src/workflows/executors/human-in-the-loop.ts +1 -1
- package/src/workflows/recovery.ts +100 -45
- package/src/workflows/resume.ts +49 -18
- package/src/workflows/retry-poller.ts +71 -21
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/github-sender-allowlist/README.md +18 -0
- package/templates/extensions/github-sender-allowlist/hooks.ts +55 -0
- package/templates/extensions/github-sender-allowlist/manifest.json +8 -0
- package/templates/skills/apps/content.md +20 -1
- package/templates/skills/code-quality/content.md +26 -1
- package/templates/skills/pages/content.md +16 -4
- package/templates/skills/pages/files/examples/multiplayer-board.html +3 -12
- package/dist/cli-2daksqps.js +0 -378
- package/dist/rbac-roles-3gxmnb20.js +0 -37
package/src/server.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import pkg from "../package.json";
|
|
3
|
+
import { ensurePricingSeeded, ensureRbacSeeded } from "./be/boot-seeds";
|
|
3
4
|
import { initDb } from "./be/db";
|
|
4
5
|
import { startPricingRefreshLoop } from "./be/pricing-refresh";
|
|
5
|
-
import { ensureRbacSeedsSynced } from "./be/rbac-roles";
|
|
6
|
-
import { seedPricingFromModelsDev } from "./be/seed-pricing";
|
|
7
6
|
import { isSteeringEnabled } from "./be/steering";
|
|
8
7
|
import { registerGithubTaskReactions } from "./github/task-reactions";
|
|
9
8
|
import { loadGlobalConfigsIntoEnv } from "./http/core";
|
|
@@ -20,6 +19,7 @@ import { registerAppQueryTool } from "./tools/app-query";
|
|
|
20
19
|
import { registerAppRollbackTool } from "./tools/app-rollback";
|
|
21
20
|
import { registerAppSyncTool } from "./tools/app-sync";
|
|
22
21
|
import { registerAppUpsertTool } from "./tools/app-upsert";
|
|
22
|
+
import { registerCancelApprovalRequestTool } from "./tools/cancel-approval-request";
|
|
23
23
|
import { registerCancelTaskTool } from "./tools/cancel-task";
|
|
24
24
|
import { registerContextDiffTool } from "./tools/context-diff";
|
|
25
25
|
import { registerContextHistoryTool } from "./tools/context-history";
|
|
@@ -307,14 +307,18 @@ export async function createServer(
|
|
|
307
307
|
initDb(process.env.DATABASE_PATH);
|
|
308
308
|
|
|
309
309
|
// Phase 2: project the vendored models.dev snapshot into the pricing table.
|
|
310
|
-
// Idempotent (INSERT OR IGNORE keyed on PK with effective_from=0)
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
|
|
310
|
+
// Idempotent (INSERT OR IGNORE keyed on PK with effective_from=0). This runs
|
|
311
|
+
// once per database handle, not once per MCP session: the seed is a
|
|
312
|
+
// synchronous 8.6 MB parse plus a write transaction, and createServer() runs
|
|
313
|
+
// on every `POST /mcp` session init. The HTTP boot path seeds first, so
|
|
314
|
+
// sessions normally skip it; standalone callers (stdio, tests) seed here.
|
|
315
|
+
// See src/be/seed-pricing.ts for the projection logic and the
|
|
316
|
+
// manual-override constants for runtime-fee / ACU pricing.
|
|
317
|
+
ensurePricingSeeded();
|
|
314
318
|
startPricingRefreshLoop();
|
|
315
319
|
|
|
316
320
|
try {
|
|
317
|
-
|
|
321
|
+
ensureRbacSeeded();
|
|
318
322
|
} catch (err) {
|
|
319
323
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
320
324
|
// RBAC flag-on must fail closed; flag-off deployments should not be bricked
|
|
@@ -500,6 +504,7 @@ export async function createServer(
|
|
|
500
504
|
registerRetryWorkflowRunTool(server);
|
|
501
505
|
registerCancelWorkflowRunTool(server);
|
|
502
506
|
registerRequestHumanInputTool(server);
|
|
507
|
+
registerCancelApprovalRequestTool(server);
|
|
503
508
|
}
|
|
504
509
|
|
|
505
510
|
// Skills capability - installable skill packages (create, search, install, publish)
|
package/src/slack/app.ts
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
import { App, LogLevel } from "@slack/bolt";
|
|
1
|
+
import { App, LogLevel, SocketModeReceiver } from "@slack/bolt";
|
|
2
2
|
import { emitBuiltInIntegrationConnectedOnce, ensureSlackRenderV2Activation } from "../be/db";
|
|
3
3
|
import { getSlackConfiguration } from "./config";
|
|
4
|
+
import { type SlackConnectionState, setSlackConnectionState } from "./connection-state";
|
|
4
5
|
import { getSlackSocketModeBlockReason, SLACK_DEV_SOCKET_MODE_OPT_IN } from "./socket-mode-guard";
|
|
5
6
|
import { startTaskWatcher, stopTaskWatcher } from "./watcher";
|
|
6
7
|
|
|
7
8
|
let app: App | null = null;
|
|
9
|
+
let receiver: SocketModeReceiver | null = null;
|
|
8
10
|
let initialized = false;
|
|
9
11
|
|
|
12
|
+
const SOCKET_EVENT_STATES: Record<string, SlackConnectionState> = {
|
|
13
|
+
connecting: "connecting",
|
|
14
|
+
reconnecting: "connecting",
|
|
15
|
+
connected: "connected",
|
|
16
|
+
disconnecting: "disconnected",
|
|
17
|
+
disconnected: "disconnected",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function trackConnectionState(owner: SocketModeReceiver): void {
|
|
21
|
+
for (const [event, state] of Object.entries(SOCKET_EVENT_STATES)) {
|
|
22
|
+
owner.client.on(event, () => {
|
|
23
|
+
// Ignore late events from a receiver that stopSlackApp() already replaced.
|
|
24
|
+
if (receiver === owner) setSlackConnectionState(state);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
export function getSlackApp(): App | null {
|
|
11
30
|
return app;
|
|
12
31
|
}
|
|
@@ -59,12 +78,22 @@ export async function initSlackApp(): Promise<App | null> {
|
|
|
59
78
|
|
|
60
79
|
// SLACK_API_URL points Bolt (Web API and apps.connections.open) at a mock Slack server for e2e tests.
|
|
61
80
|
const slackApiUrl = process.env.SLACK_API_URL;
|
|
81
|
+
const clientOptions = slackApiUrl ? { slackApiUrl } : undefined;
|
|
82
|
+
const logLevel = process.env.NODE_ENV === "development" ? LogLevel.DEBUG : LogLevel.INFO;
|
|
83
|
+
// Build the receiver ourselves so its SocketModeClient lifecycle events can
|
|
84
|
+
// feed /status. Bolt would otherwise forward clientOptions via installerOptions.
|
|
85
|
+
receiver = new SocketModeReceiver({
|
|
86
|
+
appToken,
|
|
87
|
+
logLevel,
|
|
88
|
+
installerOptions: clientOptions ? { clientOptions } : {},
|
|
89
|
+
});
|
|
90
|
+
trackConnectionState(receiver);
|
|
62
91
|
app = new App({
|
|
63
92
|
token: botToken,
|
|
64
|
-
|
|
93
|
+
receiver,
|
|
65
94
|
socketMode: true,
|
|
66
|
-
logLevel
|
|
67
|
-
...(
|
|
95
|
+
logLevel,
|
|
96
|
+
...(clientOptions ? { clientOptions } : {}),
|
|
68
97
|
});
|
|
69
98
|
|
|
70
99
|
// Failed validation must remain retryable without requiring stopSlackApp().
|
|
@@ -117,5 +146,7 @@ export async function stopSlackApp(): Promise<void> {
|
|
|
117
146
|
app = null;
|
|
118
147
|
console.log("[Slack] Bot disconnected");
|
|
119
148
|
}
|
|
149
|
+
receiver = null;
|
|
150
|
+
setSlackConnectionState("disconnected");
|
|
120
151
|
initialized = false;
|
|
121
152
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Socket Mode connection state, fed by the SocketModeClient lifecycle
|
|
3
|
+
* events in `app.ts` and read by `/status` to decide whether Slack is
|
|
4
|
+
* `verified`. Kept in its own module so status code can read it without
|
|
5
|
+
* importing the Bolt app (several tests mock `./app`).
|
|
6
|
+
*/
|
|
7
|
+
export type SlackConnectionState = "disconnected" | "connecting" | "connected";
|
|
8
|
+
|
|
9
|
+
let state: SlackConnectionState = "disconnected";
|
|
10
|
+
|
|
11
|
+
export function getSlackConnectionState(): SlackConnectionState {
|
|
12
|
+
return state;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function setSlackConnectionState(next: SlackConnectionState): void {
|
|
16
|
+
state = next;
|
|
17
|
+
}
|
package/src/tasks/context-key.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* task:workflow:{workflowRunId}
|
|
23
23
|
* task:agent:{agentId} (KV-only: per-agent scratchpad)
|
|
24
24
|
* task:page:{pageId} (KV-only: per-page state, proxy-enforced)
|
|
25
|
+
* task:ui:{kind}:{ref}:{sessionNonce} (UI-built by the contextual session panel; no server parser)
|
|
25
26
|
*
|
|
26
27
|
* Rules:
|
|
27
28
|
* - Fixed prefix tokens (`task`, family, sub-family, kind) are always lowercase.
|
|
@@ -524,8 +524,9 @@ export async function createResumeFollowUp(args: {
|
|
|
524
524
|
|
|
525
525
|
// Identity-shaped fields (dir, VCS provider/repo/number/url/etc.,
|
|
526
526
|
// outputSchema, slack channel/thread/user, agentmail, mention, contextKey,
|
|
527
|
-
// requestedByUserId
|
|
528
|
-
// `createTaskExtended`'s parentTaskId block (see src/be/db.ts)
|
|
527
|
+
// requestedByUserId) are auto-inherited from the parent by
|
|
528
|
+
// `createTaskExtended`'s parentTaskId block (see src/be/db.ts);
|
|
529
|
+
// followUpConfig is opt-in and set below. `model` is
|
|
529
530
|
// deliberately excluded there so the resume task resolves to the claiming
|
|
530
531
|
// agent's own provider/model — never the parent's concrete model string.
|
|
531
532
|
// We only override what's SPECIFIC to the resume task here.
|
|
@@ -564,6 +565,9 @@ export async function createResumeFollowUp(args: {
|
|
|
564
565
|
tags,
|
|
565
566
|
priority,
|
|
566
567
|
parentTaskId: parent.id,
|
|
568
|
+
// Same work, new session: the creator's onCompleted/onFailed must still
|
|
569
|
+
// fire when this resume finishes.
|
|
570
|
+
inheritParentFollowUpConfig: true,
|
|
567
571
|
routingAffinity,
|
|
568
572
|
});
|
|
569
573
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { createServer as createHttpServer, type Server } from "node:http";
|
|
4
|
+
import { closeDb, createAgent, createTaskExtended, getDbClient, initDb } from "../be/db";
|
|
5
|
+
import { handleAgentsRest } from "../http/agents";
|
|
6
|
+
import { listenOnFreePort } from "./test-net";
|
|
7
|
+
|
|
8
|
+
const TEST_DB_PATH = "./test-agent-task-activity.sqlite";
|
|
9
|
+
|
|
10
|
+
async function removeDbFiles(path: string): Promise<void> {
|
|
11
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
12
|
+
try {
|
|
13
|
+
await unlink(path + suffix);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let server: Server;
|
|
21
|
+
let baseUrl = "";
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
25
|
+
initDb(TEST_DB_PATH);
|
|
26
|
+
server = createHttpServer(async (req, res) => {
|
|
27
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
28
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
29
|
+
if (await handleAgentsRest(req, res, segments, url.searchParams, undefined)) return;
|
|
30
|
+
res.writeHead(404).end();
|
|
31
|
+
});
|
|
32
|
+
baseUrl = `http://localhost:${await listenOnFreePort(server)}`;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterAll(async () => {
|
|
36
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
37
|
+
closeDb();
|
|
38
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("GET /api/agents/:id/task-activity", () => {
|
|
42
|
+
test("counts tasks per UTC day inside the window, for that agent only", async () => {
|
|
43
|
+
const agent = await createAgent({ name: "a", isLead: false, status: "idle", capabilities: [] });
|
|
44
|
+
const other = await createAgent({ name: "b", isLead: false, status: "idle", capabilities: [] });
|
|
45
|
+
const day = (offset: number) =>
|
|
46
|
+
new Date(Date.now() - offset * 86_400_000).toISOString().slice(0, 10);
|
|
47
|
+
const stamp = async (agentId: string, date: string) => {
|
|
48
|
+
const task = await createTaskExtended("t", { agentId });
|
|
49
|
+
await getDbClient().run("UPDATE agent_tasks SET createdAt = ? WHERE id = ?", [
|
|
50
|
+
`${date}T12:00:00.000Z`,
|
|
51
|
+
task.id,
|
|
52
|
+
]);
|
|
53
|
+
};
|
|
54
|
+
await stamp(agent.id, day(1));
|
|
55
|
+
await stamp(agent.id, day(1));
|
|
56
|
+
await stamp(agent.id, day(3));
|
|
57
|
+
await stamp(agent.id, day(40));
|
|
58
|
+
await stamp(other.id, day(1));
|
|
59
|
+
|
|
60
|
+
const res = await fetch(`${baseUrl}/api/agents/${agent.id}/task-activity?days=30`);
|
|
61
|
+
expect(res.status).toBe(200);
|
|
62
|
+
expect(await res.json()).toEqual({
|
|
63
|
+
days: [
|
|
64
|
+
{ date: day(3), count: 1 },
|
|
65
|
+
{ date: day(1), count: 2 },
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("404s for an unknown agent", async () => {
|
|
71
|
+
const res = await fetch(`${baseUrl}/api/agents/nope/task-activity`);
|
|
72
|
+
expect(res.status).toBe(404);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -113,6 +113,11 @@ describe("agents list model display", () => {
|
|
|
113
113
|
provider: "Anthropic",
|
|
114
114
|
providerId: "anthropic",
|
|
115
115
|
});
|
|
116
|
+
expect(getAgentModelPresentation("claude-sonnet-5-5")).toMatchObject({
|
|
117
|
+
label: "Claude Sonnet 5.5",
|
|
118
|
+
provider: "Anthropic",
|
|
119
|
+
providerId: "anthropic",
|
|
120
|
+
});
|
|
116
121
|
expect(getAgentModelPresentation("claude-opus-5")).toMatchObject({
|
|
117
122
|
label: "Claude Opus 5",
|
|
118
123
|
provider: "Anthropic",
|
|
@@ -129,7 +134,7 @@ describe("agents list model display", () => {
|
|
|
129
134
|
providerId: "anthropic",
|
|
130
135
|
});
|
|
131
136
|
expect(getAgentModelPresentation("sonnet")).toMatchObject({
|
|
132
|
-
label: "Claude Sonnet 5",
|
|
137
|
+
label: "Claude Sonnet 5.5",
|
|
133
138
|
provider: "Anthropic",
|
|
134
139
|
providerId: "anthropic",
|
|
135
140
|
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
3
|
+
import { runMigrations } from "../be/migrations/runner";
|
|
4
|
+
|
|
5
|
+
const DB_PATH = "./test-approval-auto-cancellation-migration.sqlite";
|
|
6
|
+
|
|
7
|
+
async function removeDb(): Promise<void> {
|
|
8
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
9
|
+
try {
|
|
10
|
+
await Bun.file(DB_PATH + suffix).delete();
|
|
11
|
+
} catch (error) {
|
|
12
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
afterEach(removeDb);
|
|
18
|
+
|
|
19
|
+
type Row = {
|
|
20
|
+
id: string;
|
|
21
|
+
status: string;
|
|
22
|
+
resolvedBy: string | null;
|
|
23
|
+
resolvedAt: string | null;
|
|
24
|
+
resolutionReason: string | null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function readRows(db: Database): Row[] {
|
|
28
|
+
return db
|
|
29
|
+
.query<Row, []>(
|
|
30
|
+
"SELECT id, status, resolvedBy, resolvedAt, resolutionReason FROM approval_requests ORDER BY id",
|
|
31
|
+
)
|
|
32
|
+
.all();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("migration 166 approval auto-cancellation index", () => {
|
|
36
|
+
test("adds the pending-created index and leaves the schema and rows intact", async () => {
|
|
37
|
+
await removeDb();
|
|
38
|
+
const db = new Database(DB_PATH, { create: true });
|
|
39
|
+
try {
|
|
40
|
+
runMigrations(db);
|
|
41
|
+
// Roll the database back to the state before migration 166.
|
|
42
|
+
db.run("DROP INDEX idx_approval_requests_pending_created");
|
|
43
|
+
db.run("DELETE FROM _migrations WHERE version = 166");
|
|
44
|
+
db.run(`INSERT INTO approval_requests
|
|
45
|
+
(id, title, questions, approvers, status, resolvedBy, resolvedAt, resolutionReason,
|
|
46
|
+
expiresAt, createdAt, updatedAt)
|
|
47
|
+
VALUES
|
|
48
|
+
('a-pending', 'Pending', '[]', '{}', 'pending', NULL, NULL, NULL,
|
|
49
|
+
NULL, '2026-01-01', '2026-01-01'),
|
|
50
|
+
('b-cancelled', 'Cancelled', '[]', '{}', 'cancelled', NULL, '2026-01-03', 'run cancelled',
|
|
51
|
+
NULL, '2026-01-01', '2026-01-03'),
|
|
52
|
+
('c-timeout', 'Timeout', '[]', '{}', 'timeout', NULL, '2026-01-04', NULL,
|
|
53
|
+
'2026-01-02', '2026-01-01', '2026-01-04')`);
|
|
54
|
+
const before = readRows(db);
|
|
55
|
+
|
|
56
|
+
runMigrations(db);
|
|
57
|
+
|
|
58
|
+
expect(readRows(db)).toEqual(before);
|
|
59
|
+
const indexes = db
|
|
60
|
+
.query<{ name: string }, []>(
|
|
61
|
+
"SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'approval_requests'",
|
|
62
|
+
)
|
|
63
|
+
.all()
|
|
64
|
+
.map((row) => row.name);
|
|
65
|
+
expect(indexes).toEqual(
|
|
66
|
+
expect.arrayContaining([
|
|
67
|
+
"idx_approval_requests_pending_created",
|
|
68
|
+
"idx_approval_requests_status",
|
|
69
|
+
"idx_approval_requests_created",
|
|
70
|
+
"idx_approval_requests_workflow",
|
|
71
|
+
"idx_approval_requests_task",
|
|
72
|
+
"idx_approval_requests_expires",
|
|
73
|
+
]),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const tableSql = db
|
|
77
|
+
.query<{ sql: string }, []>(
|
|
78
|
+
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'approval_requests'",
|
|
79
|
+
)
|
|
80
|
+
.get()?.sql;
|
|
81
|
+
expect(tableSql).toContain("'cancelled'");
|
|
82
|
+
expect(tableSql).toContain("'timeout'");
|
|
83
|
+
|
|
84
|
+
const insert = (id: string, status: string) =>
|
|
85
|
+
db.run(
|
|
86
|
+
`INSERT INTO approval_requests (id, title, questions, approvers, status)
|
|
87
|
+
VALUES (?, 't', '[]', '{}', ?)`,
|
|
88
|
+
[id, status],
|
|
89
|
+
);
|
|
90
|
+
expect(() => insert("d-cancelled", "cancelled")).not.toThrow();
|
|
91
|
+
expect(() => insert("e-timeout", "timeout")).not.toThrow();
|
|
92
|
+
expect(() => insert("f-expired", "expired")).toThrow(/CHECK/);
|
|
93
|
+
|
|
94
|
+
// Assert 166 was re-recorded, not that it is the tail: later migrations
|
|
95
|
+
// land on top of it.
|
|
96
|
+
const reapplied = db
|
|
97
|
+
.query<{ version: number }, []>("SELECT version FROM _migrations WHERE version = 166")
|
|
98
|
+
.get();
|
|
99
|
+
expect(reapplied?.version).toBe(166);
|
|
100
|
+
} finally {
|
|
101
|
+
db.close();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|