@desplega.ai/agent-swarm 1.113.0 → 1.115.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 -2
- package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
- package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
- package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
- package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
- package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
- package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
- package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
- package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
- package/dist/cli-0pygq9r1.js +429 -0
- package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
- package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
- package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
- package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
- package/dist/cli-52fg7s7x.js +269 -0
- package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
- package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
- package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
- package/dist/cli-86fdnerz.js +405 -0
- package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
- package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
- package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
- package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
- package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
- package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
- package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
- package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
- package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
- package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
- package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
- package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
- package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
- package/dist/cli-jkhwmmgg.js +136 -0
- package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
- package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
- package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
- package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
- package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
- package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
- package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
- package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
- package/dist/cli-t0vqevnm.js +393 -0
- package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
- package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
- package/dist/cli-wh91zby6.js +187222 -0
- package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
- package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
- package/dist/cli.js +32 -15
- package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
- package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
- package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
- package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
- package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
- package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
- package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
- package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
- package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
- package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
- package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
- package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
- package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
- package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
- package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
- package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
- package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
- package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
- package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
- package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
- package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
- package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
- package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
- package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
- package/dist/rbac-roles-kfesjy9y.js +28 -0
- package/dist/rbac-roles-mwcwhbxc.js +283 -0
- package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
- package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
- package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
- package/openapi.json +1327 -20
- package/package.json +3 -3
- package/src/agentmail/handlers.ts +16 -6
- package/src/agentmail/templates.ts +25 -5
- package/src/be/audit-user.ts +28 -4
- package/src/be/db-queries/oauth.ts +42 -0
- package/src/be/db.ts +314 -28
- package/src/be/identity.ts +79 -0
- package/src/be/mcp-proxy.ts +132 -0
- package/src/be/migrations/109_rbac_roles.sql +79 -0
- package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
- package/src/be/migrations/112_script_connections_graphql.sql +96 -0
- package/src/be/migrations/113_task_routing_affinity.sql +9 -0
- package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
- package/src/be/modelsdev-cache.json +135 -0
- package/src/be/oauth-credential-bindings.ts +35 -0
- package/src/be/oauth-refresh-sweep.ts +159 -0
- package/src/be/rbac-audit.ts +52 -13
- package/src/be/rbac-roles.ts +424 -0
- package/src/be/script-connections.ts +459 -40
- package/src/be/script-credential-broker.ts +29 -3
- package/src/be/scripts/typecheck.ts +51 -7
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
- package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
- package/src/be/users.ts +24 -0
- package/src/cli.tsx +17 -0
- package/src/github/handlers.ts +10 -9
- package/src/github/templates.ts +45 -9
- package/src/gitlab/handlers.ts +20 -8
- package/src/gitlab/templates.ts +18 -6
- package/src/heartbeat/heartbeat.ts +143 -21
- package/src/http/all-routes.ts +3 -0
- package/src/http/approval-requests.ts +12 -0
- package/src/http/config.ts +44 -10
- package/src/http/core.ts +30 -3
- package/src/http/favorites.ts +1 -5
- package/src/http/index.ts +25 -1
- package/src/http/mcp-oauth.ts +2 -0
- package/src/http/mcp-servers.ts +44 -3
- package/src/http/oauth-generic.ts +105 -0
- package/src/http/poll.ts +20 -3
- package/src/http/script-connection-proxy.ts +116 -0
- package/src/http/script-connections.ts +1657 -0
- package/src/http/scripts.ts +27 -7
- package/src/http/skills.ts +11 -0
- package/src/http/tasks.ts +27 -13
- package/src/http/x.ts +6 -2
- package/src/integrations/kapso/inbound.ts +23 -6
- package/src/jira/sync.ts +111 -17
- package/src/jira/templates.ts +10 -2
- package/src/mcp-client/http-client.ts +194 -0
- package/src/oauth/app-validation.ts +16 -0
- package/src/oauth/ensure-token.ts +69 -14
- package/src/oauth/mcp-wrapper.ts +15 -0
- package/src/oauth/wrapper.ts +48 -20
- package/src/prompts/session-templates.ts +3 -3
- package/src/providers/codex-models.ts +100 -30
- package/src/providers/modelsdev-reasoning.json +51 -0
- package/src/providers/pi-mono-mcp-client.ts +2 -133
- package/src/providers/reasoning-effort.ts +11 -5
- package/src/rbac/admission.ts +68 -0
- package/src/rbac/index.ts +2 -0
- package/src/rbac/legacy-policy.ts +12 -0
- package/src/rbac/permissions.ts +48 -0
- package/src/scheduler/scheduler.ts +9 -1
- package/src/scripts-runtime/api-client.ts +151 -19
- package/src/scripts-runtime/api-types.ts +38 -6
- package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
- package/src/scripts-runtime/credential-broker/types.ts +8 -0
- package/src/scripts-runtime/ctx.ts +11 -1
- package/src/scripts-runtime/eval-harness.ts +5 -1
- package/src/scripts-runtime/executors/types.ts +2 -1
- package/src/scripts-runtime/loader.ts +3 -1
- package/src/scripts-runtime/mcp-client.ts +87 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +6 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
- package/src/server-user.ts +114 -59
- package/src/server.ts +12 -0
- package/src/slack/assistant.ts +14 -6
- package/src/slack/enrich.ts +17 -0
- package/src/slack/handlers.ts +8 -22
- package/src/slack/responses.ts +91 -1
- package/src/slack/thread-buffer.ts +6 -3
- package/src/slack/watcher.ts +9 -6
- package/src/tasks/worker-follow-up.ts +100 -3
- package/src/tests/agentmail-handlers.test.ts +32 -3
- package/src/tests/approval-requests.test.ts +61 -0
- package/src/tests/audit-user.test.ts +47 -1
- package/src/tests/codex-adapter.test.ts +46 -23
- package/src/tests/credential-broker.test.ts +5 -2
- package/src/tests/delete-page-tool.test.ts +197 -0
- package/src/tests/github-handlers.test.ts +28 -2
- package/src/tests/gitlab-handlers.test.ts +125 -0
- package/src/tests/heartbeat.test.ts +48 -3
- package/src/tests/identity.test.ts +117 -0
- package/src/tests/jira-sync.test.ts +99 -1
- package/src/tests/kapso-inbound.test.ts +7 -0
- package/src/tests/mcp-tools-user.test.ts +60 -6
- package/src/tests/model-control.test.ts +8 -2
- package/src/tests/oauth-credential-bindings.test.ts +490 -0
- package/src/tests/oauth-refresh-sweep.test.ts +160 -0
- package/src/tests/pool-affinity.test.ts +401 -0
- package/src/tests/prompt-template-remaining.test.ts +2 -2
- package/src/tests/rbac-admission-e2e.test.ts +386 -0
- package/src/tests/rbac-admission.test.ts +492 -0
- package/src/tests/rbac-e2e-helpers.ts +3 -0
- package/src/tests/rbac-engine.test.ts +50 -0
- package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
- package/src/tests/rbac-roles.test.ts +328 -0
- package/src/tests/reasoning-effort.test.ts +15 -2
- package/src/tests/schedule-target-type.test.ts +50 -0
- package/src/tests/scheduled-tasks.test.ts +40 -0
- package/src/tests/script-apis-mcp.test.ts +37 -13
- package/src/tests/script-connections-http.test.ts +1158 -0
- package/src/tests/script-connections-mcp.test.ts +894 -0
- package/src/tests/script-connections.test.ts +754 -6
- package/src/tests/scripts-external-api.test.ts +49 -1
- package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
- package/src/tests/scripts-runtime.test.ts +291 -0
- package/src/tests/seed-scripts.test.ts +36 -0
- package/src/tests/slack-identity-resolution.test.ts +37 -3
- package/src/tests/slack-inline-output.test.ts +65 -0
- package/src/tests/slack-thread-buffer.test.ts +28 -0
- package/src/tests/tool-annotations.test.ts +1 -0
- package/src/tests/user-token-rest-auth.test.ts +140 -1
- package/src/tests/workflow-swarm-script.test.ts +44 -0
- package/src/tests/workflow-triggers-v2.test.ts +300 -1
- package/src/tools/credential-bindings/tool.ts +277 -18
- package/src/tools/delete-page.ts +145 -0
- package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
- package/src/tools/request-human-input.ts +2 -0
- package/src/tools/resolve-user.ts +77 -27
- package/src/tools/script-apis.ts +62 -3
- package/src/tools/script-connections/tool.ts +275 -31
- package/src/tools/send-task.ts +15 -1
- package/src/tools/task-action.ts +34 -2
- package/src/tools/templates.ts +53 -1
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +2 -0
- package/src/tools/workflows/create-workflow.ts +8 -1
- package/src/tools/workflows/update-workflow.ts +11 -2
- package/src/types.ts +90 -11
- package/src/utils/scoped-resource.ts +25 -0
- package/src/workflows/executors/human-in-the-loop.ts +1 -0
- package/src/workflows/executors/swarm-script.ts +7 -1
- package/src/workflows/triggers.ts +146 -18
- package/templates/skills/swarm-scripts/content.md +33 -8
- package/dist/cli-bgef578c.js +0 -771
- package/dist/cli-c9mtm09b.js +0 -43
- package/dist/cli-ceewqajw.js +0 -1698
- package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
- package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
- package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
- package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
- package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
- package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
- package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
- package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
- package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { unlink } from "node:fs/promises";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
closeDb,
|
|
5
|
+
completeTask,
|
|
6
|
+
createAgent,
|
|
7
|
+
createUser,
|
|
8
|
+
getDb,
|
|
9
|
+
getTaskById,
|
|
10
|
+
initDb,
|
|
11
|
+
} from "../be/db";
|
|
4
12
|
import { upsertOAuthApp } from "../be/db-queries/oauth";
|
|
5
13
|
import {
|
|
6
14
|
createTrackerSync,
|
|
@@ -8,6 +16,9 @@ import {
|
|
|
8
16
|
getTrackerSyncByExternalId,
|
|
9
17
|
updateTrackerSync,
|
|
10
18
|
} from "../be/db-queries/tracker";
|
|
19
|
+
import { findUserByExternalId, type IdentityActor, linkIdentity } from "../be/users";
|
|
20
|
+
|
|
21
|
+
const SYSTEM_ACTOR: IdentityActor = { kind: "system", id: "test" };
|
|
11
22
|
|
|
12
23
|
const TEST_DB_PATH = "./test-jira-sync.sqlite";
|
|
13
24
|
const BOT_ACCOUNT_ID = "bot-account-12345";
|
|
@@ -303,6 +314,93 @@ describe("handleCommentEvent — short-circuits", () => {
|
|
|
303
314
|
});
|
|
304
315
|
});
|
|
305
316
|
|
|
317
|
+
describe("identity resolution — Jira routes through resolveIdentity, never raw displayName", () => {
|
|
318
|
+
test("issue-assigned: reporter linked by accountId -> requestedByUserId set + rendered pair in task text", async () => {
|
|
319
|
+
const reporter = createUser({ name: "Zbigniew Reporter", email: "zbigniew-jira@example.com" });
|
|
320
|
+
linkIdentity(reporter.id, "jira", "reporter-linked-1", SYSTEM_ACTOR);
|
|
321
|
+
|
|
322
|
+
const event = makeIssueAssignedEvent("10030", "KAN-30", "Linked reporter issue");
|
|
323
|
+
event.issue.fields.reporter = {
|
|
324
|
+
displayName: "Reporter Name",
|
|
325
|
+
accountId: "reporter-linked-1",
|
|
326
|
+
};
|
|
327
|
+
await handleIssueEvent(event);
|
|
328
|
+
|
|
329
|
+
const sync = getTrackerSyncByExternalId("jira", "task", "10030");
|
|
330
|
+
const task = getTaskById(sync?.swarmId ?? "");
|
|
331
|
+
expect(task).not.toBeNull();
|
|
332
|
+
expect(task?.requestedByUserId).toBe(reporter.id);
|
|
333
|
+
expect(task?.task).toContain("Zbigniew Reporter (jira:reporter-linked-1)");
|
|
334
|
+
expect(task?.task).not.toContain("Reporter Name");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("issue-assigned: reporter with an email but no prior link auto-links via findOrCreateUserByEmail", async () => {
|
|
338
|
+
const event = makeIssueAssignedEvent("10031", "KAN-31", "Email-cascade issue");
|
|
339
|
+
event.issue.fields.reporter = {
|
|
340
|
+
displayName: "Auto Linked",
|
|
341
|
+
accountId: "reporter-autolink-1",
|
|
342
|
+
emailAddress: "auto-linked-jira@example.com",
|
|
343
|
+
};
|
|
344
|
+
await handleIssueEvent(event);
|
|
345
|
+
|
|
346
|
+
const linked = findUserByExternalId("jira", "reporter-autolink-1");
|
|
347
|
+
expect(linked).not.toBeNull();
|
|
348
|
+
expect(linked?.name).toBe("Auto Linked");
|
|
349
|
+
|
|
350
|
+
const sync = getTrackerSyncByExternalId("jira", "task", "10031");
|
|
351
|
+
const task = getTaskById(sync?.swarmId ?? "");
|
|
352
|
+
expect(task?.requestedByUserId).toBe(linked?.id);
|
|
353
|
+
expect(task?.task).toContain("Auto Linked (jira:reporter-autolink-1)");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("issue-assigned: unlinked reporter with no email renders the UNKNOWN sentinel, never the displayName", async () => {
|
|
357
|
+
const event = makeIssueAssignedEvent("10032", "KAN-32", "Unknown reporter issue");
|
|
358
|
+
event.issue.fields.reporter = {
|
|
359
|
+
displayName: "Never Registered",
|
|
360
|
+
accountId: "reporter-unknown-1",
|
|
361
|
+
};
|
|
362
|
+
await handleIssueEvent(event);
|
|
363
|
+
|
|
364
|
+
const sync = getTrackerSyncByExternalId("jira", "task", "10032");
|
|
365
|
+
const task = getTaskById(sync?.swarmId ?? "");
|
|
366
|
+
expect(task).not.toBeNull();
|
|
367
|
+
expect(task?.requestedByUserId ?? null).toBeNull();
|
|
368
|
+
expect(task?.task).toContain("jira:reporter-unknown-1 (unknown user)");
|
|
369
|
+
expect(task?.task).not.toContain("Never Registered");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test("comment-mention: linked author -> requestedByUserId set + rendered pair as comment_author", async () => {
|
|
373
|
+
const author = createUser({ name: "Manuel Commenter", email: "manuel-jira@example.com" });
|
|
374
|
+
linkIdentity(author.id, "jira", "commenter-linked-1", SYSTEM_ACTOR);
|
|
375
|
+
|
|
376
|
+
await handleCommentEvent(
|
|
377
|
+
makeCommentEvent("10033", "KAN-33", "commenter-linked-1", "please take a look", [
|
|
378
|
+
BOT_ACCOUNT_ID,
|
|
379
|
+
]),
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const sync = getTrackerSyncByExternalId("jira", "task", "10033");
|
|
383
|
+
const task = getTaskById(sync?.swarmId ?? "");
|
|
384
|
+
expect(task).not.toBeNull();
|
|
385
|
+
expect(task?.requestedByUserId).toBe(author.id);
|
|
386
|
+
expect(task?.task).toContain("Manuel Commenter (jira:commenter-linked-1)");
|
|
387
|
+
expect(task?.task).not.toContain("Some User");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("comment-mention: unlinked author renders the UNKNOWN sentinel, never the raw displayName", async () => {
|
|
391
|
+
await handleCommentEvent(
|
|
392
|
+
makeCommentEvent("10034", "KAN-34", "commenter-unknown-1", "any updates?", [BOT_ACCOUNT_ID]),
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const sync = getTrackerSyncByExternalId("jira", "task", "10034");
|
|
396
|
+
const task = getTaskById(sync?.swarmId ?? "");
|
|
397
|
+
expect(task).not.toBeNull();
|
|
398
|
+
expect(task?.requestedByUserId ?? null).toBeNull();
|
|
399
|
+
expect(task?.task).toContain("jira:commenter-unknown-1 (unknown user)");
|
|
400
|
+
expect(task?.task).not.toContain("Some User");
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
306
404
|
describe("createTrackerSyncIfAbsent — UNIQUE-gated insert", () => {
|
|
307
405
|
test("first call inserts; second call returns existing", () => {
|
|
308
406
|
const first = createTrackerSyncIfAbsent({
|
|
@@ -147,6 +147,10 @@ describe("routeKapsoInbound", () => {
|
|
|
147
147
|
const task = getTaskById(routing.taskId);
|
|
148
148
|
expect(task!.requestedByUserId).toBe(user.id);
|
|
149
149
|
expect(getKv("integration:unmapped:kapso", "34679077778:meta")).toBeNull();
|
|
150
|
+
// The resolved canonical name renders in the task text — never the raw
|
|
151
|
+
// WhatsApp profile `contact_name` ("Taras" from the fixture payload).
|
|
152
|
+
expect(task!.task).toContain("Known WhatsApp Sender (kapso:34679077778)");
|
|
153
|
+
expect(task!.task).not.toContain("Taras");
|
|
150
154
|
});
|
|
151
155
|
|
|
152
156
|
test("unknown Kapso sender → records unmapped identity and leaves task unowned", () => {
|
|
@@ -170,6 +174,9 @@ describe("routeKapsoInbound", () => {
|
|
|
170
174
|
if (routing.kind !== "task") throw new Error("expected task");
|
|
171
175
|
const task = getTaskById(routing.taskId);
|
|
172
176
|
expect(task!.requestedByUserId).toBeUndefined();
|
|
177
|
+
// Unresolved -> explicit UNKNOWN sentinel, never the raw contact_name.
|
|
178
|
+
expect(task!.task).toContain("kapso:34679077779 (unknown user)");
|
|
179
|
+
expect(task!.task).not.toContain("Taras");
|
|
173
180
|
|
|
174
181
|
const meta = getKv("integration:unmapped:kapso", "34679077779:meta");
|
|
175
182
|
expect(meta?.valueType).toBe("json");
|
|
@@ -106,12 +106,17 @@ describe("resolve-user MCP tool (new {kind, externalId, email} shape)", () => {
|
|
|
106
106
|
expect(textOf(byAlias)).toContain(user.id);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
test("returns
|
|
109
|
+
test("returns a structured {status: 'unknown', ...} payload when nothing matches — never prose", async () => {
|
|
110
110
|
const result = await callTool(server, "resolve-user", {
|
|
111
111
|
kind: "slack",
|
|
112
112
|
externalId: "U_DOES_NOT_EXIST",
|
|
113
113
|
});
|
|
114
|
-
|
|
114
|
+
const parsed = JSON.parse(textOf(result));
|
|
115
|
+
expect(parsed).toEqual({
|
|
116
|
+
status: "unknown",
|
|
117
|
+
kind: "slack",
|
|
118
|
+
externalId: "U_DOES_NOT_EXIST",
|
|
119
|
+
});
|
|
115
120
|
});
|
|
116
121
|
|
|
117
122
|
// Schema-level validation tests. The MCP SDK runs Zod at the transport
|
|
@@ -132,12 +137,17 @@ describe("resolve-user MCP tool (new {kind, externalId, email} shape)", () => {
|
|
|
132
137
|
expect(parsed.success).toBe(false);
|
|
133
138
|
if (!parsed.success) {
|
|
134
139
|
const msg = JSON.stringify(parsed.error.issues);
|
|
135
|
-
expect(msg).toMatch(/Provide either \(kind \+ externalId\), email, or
|
|
140
|
+
expect(msg).toMatch(/Provide either \(kind \+ externalId\), email, userId, or name/);
|
|
136
141
|
}
|
|
137
142
|
});
|
|
138
143
|
|
|
139
|
-
test("name
|
|
144
|
+
test("valid {name} input passes the schema", () => {
|
|
140
145
|
const parsed = resolveUserInputSchema.safeParse({ name: "Whoever" });
|
|
146
|
+
expect(parsed.success).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("name shorter than 2 chars fails the min-length constraint", () => {
|
|
150
|
+
const parsed = resolveUserInputSchema.safeParse({ name: "A" });
|
|
141
151
|
expect(parsed.success).toBe(false);
|
|
142
152
|
});
|
|
143
153
|
|
|
@@ -201,9 +211,53 @@ describe("resolve-user MCP tool (new {kind, externalId, email} shape)", () => {
|
|
|
201
211
|
expect(parsed.externalIds[0]).toMatchObject({ kind: "linear", externalId: "L_UIDLOOKUP" });
|
|
202
212
|
});
|
|
203
213
|
|
|
204
|
-
test("userId lookup returns
|
|
214
|
+
test("userId lookup returns a structured {status: 'unknown', ...} payload for an unknown ID", async () => {
|
|
205
215
|
const result = await callTool(server, "resolve-user", { userId: "nonexistent-user-id-xyz" });
|
|
206
|
-
|
|
216
|
+
const parsed = JSON.parse(textOf(result));
|
|
217
|
+
expect(parsed).toEqual({
|
|
218
|
+
status: "unknown",
|
|
219
|
+
kind: "userId",
|
|
220
|
+
externalId: "nonexistent-user-id-xyz",
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("name lookup: single exact match resolves to the user profile", async () => {
|
|
225
|
+
const user = createUser({ name: "Zbigniew Solo", email: "zbigniew@example.com" });
|
|
226
|
+
|
|
227
|
+
const result = await callTool(server, "resolve-user", { name: "Zbigniew Solo" });
|
|
228
|
+
const parsed = JSON.parse(textOf(result));
|
|
229
|
+
expect(parsed.id).toBe(user.id);
|
|
230
|
+
expect(parsed.name).toBe("Zbigniew Solo");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("name lookup: single first-token prefix match resolves to the user profile", async () => {
|
|
234
|
+
const user = createUser({ name: "Priyanka Unique Prefix", email: "priyanka@example.com" });
|
|
235
|
+
|
|
236
|
+
const result = await callTool(server, "resolve-user", { name: "Priyanka" });
|
|
237
|
+
const parsed = JSON.parse(textOf(result));
|
|
238
|
+
expect(parsed.id).toBe(user.id);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("name lookup: multiple matches return {status: 'ambiguous', candidates: [...]} — never a guess", async () => {
|
|
242
|
+
const a = createUser({ name: "Alberto Maurel", email: "alberto.maurel@example.com" });
|
|
243
|
+
const b = createUser({ name: "Alberto Dubois", email: "alberto.dubois@example.com" });
|
|
244
|
+
|
|
245
|
+
const result = await callTool(server, "resolve-user", { name: "Alberto" });
|
|
246
|
+
const parsed = JSON.parse(textOf(result));
|
|
247
|
+
expect(parsed.status).toBe("ambiguous");
|
|
248
|
+
expect(parsed.message).toMatch(/AMBIGUOUS/);
|
|
249
|
+
const candidateIds = parsed.candidates.map((c: { userId: string }) => c.userId).sort();
|
|
250
|
+
expect(candidateIds).toEqual([a.id, b.id].sort());
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("name lookup: zero matches return a structured {status: 'unknown', kind: 'name', ...} payload", async () => {
|
|
254
|
+
const result = await callTool(server, "resolve-user", { name: "Nobody Registered Xyz" });
|
|
255
|
+
const parsed = JSON.parse(textOf(result));
|
|
256
|
+
expect(parsed).toEqual({
|
|
257
|
+
status: "unknown",
|
|
258
|
+
kind: "name",
|
|
259
|
+
externalId: "Nobody Registered Xyz",
|
|
260
|
+
});
|
|
207
261
|
});
|
|
208
262
|
});
|
|
209
263
|
|
|
@@ -485,7 +485,10 @@ describe("Model Control - Priority Resolution Logic", () => {
|
|
|
485
485
|
|
|
486
486
|
test("task.modelTier resolves using the claiming worker harness", () => {
|
|
487
487
|
expect(resolveModelTier({ tier: "smol", harnessProvider: "claude" })).toBe("haiku");
|
|
488
|
-
expect(resolveModelTier({ tier: "smol", harnessProvider: "codex" })).toBe("gpt-5.
|
|
488
|
+
expect(resolveModelTier({ tier: "smol", harnessProvider: "codex" })).toBe("gpt-5.6-luna");
|
|
489
|
+
expect(resolveModelTier({ tier: "regular", harnessProvider: "codex" })).toBe("gpt-5.6-terra");
|
|
490
|
+
expect(resolveModelTier({ tier: "smart", harnessProvider: "codex" })).toBe("gpt-5.6-sol");
|
|
491
|
+
expect(resolveModelTier({ tier: "ultra", harnessProvider: "codex" })).toBe("gpt-5.6-sol");
|
|
489
492
|
expect(resolveModelTier({ tier: "smart", harnessProvider: "opencode" })).toBe(
|
|
490
493
|
"openrouter/deepseek/deepseek-v4-pro",
|
|
491
494
|
);
|
|
@@ -561,11 +564,14 @@ describe("Model Control - Zod Validation Schema", () => {
|
|
|
561
564
|
sendTaskInputSchema.parse({ agentId: crypto.randomUUID(), task: "x", effort: "xhigh" })
|
|
562
565
|
.effort,
|
|
563
566
|
).toBe("xhigh");
|
|
567
|
+
expect(
|
|
568
|
+
sendTaskInputSchema.parse({ agentId: crypto.randomUUID(), task: "x", effort: "max" }).effort,
|
|
569
|
+
).toBe("max");
|
|
564
570
|
expect(taskActionInputSchema.parse({ action: "create", task: "x", effort: "off" }).effort).toBe(
|
|
565
571
|
"off",
|
|
566
572
|
);
|
|
567
573
|
expect(() =>
|
|
568
|
-
sendTaskInputSchema.parse({ agentId: crypto.randomUUID(), task: "x", effort: "
|
|
574
|
+
sendTaskInputSchema.parse({ agentId: crypto.randomUUID(), task: "x", effort: "ultra" }),
|
|
569
575
|
).toThrow();
|
|
570
576
|
});
|
|
571
577
|
|