@desplega.ai/agent-swarm 1.172.0 → 1.173.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-fpecx1g1.js → acp-adapter-ccm9xrep.js} +8 -8
- package/dist/{actions-pw87a1a8.js → actions-sgeyv08k.js} +44 -19
- package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-gpxp3kc9.js} +1 -1
- package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
- package/dist/{app-wmf0m843.js → app-km9qnv2f.js} +6 -4
- package/dist/{assistant-emp1e108.js → assistant-5r8tcvvd.js} +44 -22
- package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-16rv9843.js} +4 -3
- package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-swwzdgd7.js} +4 -3
- package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-zkr6vkqb.js} +5 -4
- package/dist/{boot-seeds-8q637aew.js → boot-seeds-d8sp49t2.js} +7 -6
- package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-yzzzh8jp.js} +9 -9
- package/dist/{claude-sdk-session-gsfsh662.js → claude-sdk-session-4whxere4.js} +9458 -9164
- package/dist/{cli-g4f5pj26.js → cli-0a7mm3zp.js} +92 -10
- package/dist/{cli-pj6x9qce.js → cli-0kxf81jj.js} +1 -1
- package/dist/{cli-kb4ehzx5.js → cli-1w0vj3gw.js} +4 -4
- package/dist/{cli-e0a9sk00.js → cli-3nxdmjrc.js} +1 -1
- package/dist/{cli-6jb2rf77.js → cli-42qhyvwx.js} +3 -3
- package/dist/{cli-esnpehf5.js → cli-5er5gjbm.js} +4 -4
- package/dist/{cli-04emfe2q.js → cli-5wpw4rab.js} +1 -1
- package/dist/{cli-aspcaef3.js → cli-6r2f7edk.js} +4 -4
- package/dist/{cli-xczpdp9e.js → cli-6thk4jz3.js} +1 -1
- package/dist/{cli-j6kpj9az.js → cli-7a93cv8c.js} +1458 -48
- package/dist/{cli-k46r9hmk.js → cli-94b48jhk.js} +2 -2
- package/dist/{cli-gp1rtyvk.js → cli-9xf7fdtp.js} +2 -2
- package/dist/{cli-qsztpfry.js → cli-anzdk0z4.js} +1 -1
- package/dist/{cli-cceskcvt.js → cli-b07hr3bt.js} +8 -8
- package/dist/{cli-c2pq2rjb.js → cli-bv2etw2y.js} +2 -2
- package/dist/{cli-hwwvy36w.js → cli-c3rk1v0x.js} +3 -3
- package/dist/{cli-tgpfnm6r.js → cli-fmhj1ehj.js} +53 -13
- package/dist/{cli-r8smj8b8.js → cli-jpc4r224.js} +1 -1
- package/dist/{cli-4nz8p1p5.js → cli-m89t9jdf.js} +5 -5
- package/dist/{cli-bf7485pm.js → cli-na2pxe4f.js} +1 -1
- package/dist/{cli-prrt9fa0.js → cli-ne7smj1b.js} +4 -4
- package/dist/{cli-cf69s3ew.js → cli-nwrdx840.js} +2 -2
- package/dist/cli-p0sg24zp.js +16 -0
- package/dist/{cli-gj81e47t.js → cli-phcqp1b9.js} +1 -1
- package/dist/{cli-5rv1j9ga.js → cli-qm1448f9.js} +51 -5
- package/dist/{cli-8hzfb6xz.js → cli-r0byd61h.js} +7 -7
- package/dist/{cli-nyhb1r6x.js → cli-r4fqfpwf.js} +3 -3
- package/dist/{cli-1hgat6pa.js → cli-rfs4vddv.js} +5 -5
- package/dist/{cli-ztrv9y21.js → cli-rfz2ve55.js} +861 -246
- package/dist/{cli-pstq9888.js → cli-s8z6x4b4.js} +22 -21
- package/dist/{cli-j1pmc07z.js → cli-saqyage0.js} +92 -16
- package/dist/{cli-zxm9p3x4.js → cli-srqq3gc5.js} +1 -1
- package/dist/{cli-9xsmdpdt.js → cli-tcc4wd5d.js} +136 -42
- package/dist/cli-v3zhnxsb.js +263 -0
- package/dist/{cli-388egyf6.js → cli-w1apjecb.js} +1 -1
- package/dist/{cli-g6t9texm.js → cli-wrh08zjd.js} +4 -4
- package/dist/{cli-8vs9trd4.js → cli-x119kdba.js} +1 -1
- package/dist/{cli-nv4zd0b9.js → cli-xc7p7s3g.js} +1 -1
- package/dist/cli.js +16 -16
- package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-r3dn4fp5.js} +6 -6
- package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-fwt793h4.js} +6 -6
- package/dist/{commands-n0hshcvs.js → commands-z8fq9qna.js} +14 -4
- package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-3mf2btyj.js} +21 -19
- package/dist/{db-4w7tmt1g.js → db-qfpq4bt7.js} +4 -3
- package/dist/{db-hkqvrdcb.js → db-yyhqqrbf.js} +5 -4
- package/dist/{dispatcher-5sd69y14.js → dispatcher-x0x7qfcq.js} +20 -18
- package/dist/{dsh-adapter-2h8m4cab.js → dsh-adapter-34etvb5m.js} +1 -1
- package/dist/external-channel-h1fjecwe.js +51 -0
- package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-e7mr287e.js} +9 -9
- package/dist/{handlers-s20xr10r.js → handlers-znb9n7d4.js} +23 -21
- package/dist/{heartbeat-3k31q4g2.js → heartbeat-bm4qkn8d.js} +22 -20
- package/dist/{hook-gdn6a2fn.js → hook-254r4mza.js} +7 -7
- package/dist/{hook-ygwerrdh.js → hook-xaa3q80s.js} +8 -8
- package/dist/{http-jdteeg81.js → http-3dgrgp8y.js} +135 -64
- package/dist/{index-6ksewd5y.js → index-2ybk7khy.js} +618 -120
- package/dist/{index-tsszt7g9.js → index-bdsqa799.js} +22 -20
- package/dist/{index-e0snym4t.js → index-fx3jqq1e.js} +20 -18
- package/dist/{index-vm3bm9t7.js → index-g2byveez.js} +20 -18
- package/dist/{index-ffqzqgwb.js → index-jd7sejm2.js} +4 -3
- package/dist/{keepalive-jekc896t.js → keepalive-6ywhpja1.js} +6 -5
- package/dist/{lead-4k9s9szx.js → lead-cw45n21s.js} +32 -30
- package/dist/{maintenance-shazch4n.js → maintenance-ss1a1ge9.js} +4 -3
- package/dist/node-c9we95xf.js +1 -1
- package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-3c6f8vyy.js} +5 -4
- package/dist/{onboard-ydrbnvww.js → onboard-8hzdhc1k.js} +2 -2
- package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-qbchbcfz.js} +6 -6
- package/dist/{otel-impl-6pf5cv86.js → otel-impl-e6mnvc4v.js} +7 -2
- package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-3c9g9snh.js} +6 -6
- package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-gedkjdw0.js} +6 -5
- package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-dt4vh1yy.js} +4 -3
- package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-eszdg6pm.js} +5 -4
- package/dist/{render-v2-pd1rg54q.js → render-v2-907cy6sa.js} +6 -4
- package/dist/{resume-gw4a2c6v.js → resume-42stcaz0.js} +11 -9
- package/dist/{setup-jda1ksdh.js → setup-adbc1c10.js} +2 -2
- package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-rtmt494q.js} +3 -3
- package/dist/{work-objects-tb7ycye8.js → work-objects-wpj8ck95.js} +28 -21
- package/dist/{worker-njjccz5d.js → worker-bzdra9rf.js} +32 -30
- package/openapi.json +423 -7
- package/package.json +4 -4
- package/src/apps/catalog.generated.json +1309 -53
- package/src/apps/page-validator.ts +93 -31
- package/src/azure-devops/api.ts +94 -18
- package/src/azure-devops/auth.ts +32 -0
- package/src/azure-devops/boards-sync.ts +708 -0
- package/src/azure-devops/handlers.ts +52 -8
- package/src/azure-devops/html.ts +26 -0
- package/src/azure-devops/index.ts +15 -0
- package/src/azure-devops/templates.ts +86 -0
- package/src/azure-devops/types.ts +65 -1
- package/src/be/db.ts +22 -6
- package/src/be/migrations/213_script_connections_sql.sql +79 -0
- package/src/be/script-connections.ts +47 -8
- package/src/be/seed-skills/bundled-files.generated.json +10 -0
- package/src/be/seed-skills/index.ts +3 -0
- package/src/be/sql-connections.ts +118 -0
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/profile-sync.ts +5 -3
- package/src/commands/runner.ts +6 -2
- package/src/http/mcp-oauth.ts +6 -13
- package/src/http/pages-public.ts +3 -2
- package/src/http/pages.ts +2 -1
- package/src/http/route-def.ts +7 -6
- package/src/http/script-connection-proxy.ts +51 -4
- package/src/http/script-connections.ts +5 -2
- package/src/http/session-cost-recompute.ts +12 -2
- package/src/http/session-data.ts +4 -0
- package/src/http/webhooks.ts +24 -1
- package/src/jira/adf.ts +3 -1
- package/src/kv-reserved-namespaces.ts +2 -0
- package/src/oauth/mcp-wrapper.ts +10 -6
- package/src/otel-impl.ts +2 -1
- package/src/prompts/base-prompt.ts +3 -3
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +27 -2
- package/src/providers/claude-flag-settings.ts +121 -0
- package/src/providers/claude-sdk-session.ts +7 -0
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +26 -12
- package/src/providers/opencode-billing.ts +86 -0
- package/src/providers/opencode-swarm-plugin.ts +20 -0
- package/src/providers/pricing-sources.md +4 -1
- package/src/providers/types.ts +5 -0
- package/src/scripts-runtime/api-client.ts +15 -1
- package/src/scripts-runtime/api-types.ts +9 -1
- package/src/scripts-runtime/ctx.ts +1 -1
- package/src/scripts-runtime/mcp-client.ts +30 -19
- package/src/slack/actions.ts +29 -1
- package/src/slack/app.ts +14 -2
- package/src/slack/assistant.ts +36 -2
- package/src/slack/channel-join.ts +3 -16
- package/src/slack/commands.ts +9 -1
- package/src/slack/external-channel-egress.ts +113 -0
- package/src/slack/external-channel.ts +309 -0
- package/src/slack/handlers.ts +61 -0
- package/src/slack/render-v2.ts +40 -47
- package/src/slack/work-objects.ts +5 -0
- package/src/tasks/context-key.ts +34 -1
- package/src/telemetry.ts +2 -1
- package/src/tests/apps-catalog-feedback.test.ts +310 -0
- package/src/tests/apps-catalog-inputs.test.ts +70 -0
- package/src/tests/apps-catalog-table.test.ts +113 -0
- package/src/tests/azure-devops-boards-sync.test.ts +584 -0
- package/src/tests/azure-devops-html.test.ts +13 -0
- package/src/tests/azure-devops-webhook-malformed.test.ts +4 -0
- package/src/tests/claude-adapter-binary.test.ts +139 -0
- package/src/tests/claude-flag-settings.test.ts +106 -0
- package/src/tests/claude-sdk-lifecycle.test.ts +142 -1
- package/src/tests/claude-sdk-process.fixture.ts +7 -0
- package/src/tests/context-key.test.ts +32 -0
- package/src/tests/deferred-task-wake.test.ts +52 -4
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/fixtures/azure-boards/workitem.commented.json +89 -0
- package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
- package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
- package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
- package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
- package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
- package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
- package/src/tests/fixtures/catalog-table-definition.json.txt +468 -0
- package/src/tests/fixtures/runner-identity-refresh.ts +3 -0
- package/src/tests/node-env-runtime.test.ts +98 -0
- package/src/tests/opencode-adapter.test.ts +132 -1
- package/src/tests/opencode-billing.test.ts +28 -0
- package/src/tests/runner-identity-refresh.test.ts +3 -0
- package/src/tests/script-connections-sql.test.ts +675 -0
- package/src/tests/script-connections.test.ts +5 -1
- package/src/tests/session-cost-recompute-unit.test.ts +48 -0
- package/src/tests/slack-channel-join.test.ts +4 -1
- package/src/tests/slack-external-channel-config.test.ts +65 -0
- package/src/tests/slack-external-channel-egress.test.ts +237 -0
- package/src/tests/slack-external-channel-guard.test.ts +280 -0
- package/src/tests/slack-external-channel-tools.test.ts +173 -0
- package/src/tests/slack-external-channel.test.ts +370 -0
- package/src/tests/slack-render-v2.test.ts +86 -10
- package/src/tests/spec-base-url.test.ts +16 -1
- package/src/tests/vendored-openapi.test.ts +16 -1
- package/src/tools/defer-task.ts +17 -0
- package/src/tools/slack-post.ts +6 -0
- package/src/tools/slack-read.ts +8 -0
- package/src/tools/slack-reply.ts +6 -0
- package/src/tools/slack-start-thread.ts +6 -0
- package/src/tools/slack-update.ts +6 -0
- package/src/tools/slack-upload-file.ts +6 -0
- package/src/utils/node-env.ts +24 -0
- package/templates/skills/apps/SKILL.md +742 -0
- package/templates/skills/apps/content.md +52 -21
- package/templates/skills/qa-loom/config.json +14 -0
- package/templates/skills/qa-loom/content.md +187 -0
- package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
- package/templates/skills/qa-loom/files/run-qa.sh +57 -0
- package/templates/skills/slack-interaction/content.md +4 -0
- package/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
- package/dist/{cli-4fnb6884.js → cli-h7k0xtry.js} +3 -3
- package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
- package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-szdxbpqg.js} +1 -1
- package/dist/{google-vertex-3f1jk41f.js → google-vertex-qzw3p36d.js} +1 -1
- package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
- package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
- package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
- package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
ensurePricingSeeded,
|
|
22
22
|
ensureRbacSeeded
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-42qhyvwx.js";
|
|
24
24
|
import {
|
|
25
25
|
buildCatalogEntries,
|
|
26
26
|
getCatalogGeneration,
|
|
@@ -30,10 +30,10 @@ import {
|
|
|
30
30
|
requestModelCatalogRefresh,
|
|
31
31
|
startPricingRefreshLoop,
|
|
32
32
|
upsertModelCatalogOverlay
|
|
33
|
-
} from "./cli-
|
|
33
|
+
} from "./cli-9xf7fdtp.js";
|
|
34
34
|
import {
|
|
35
35
|
loadModelsDevCache
|
|
36
|
-
} from "./cli-
|
|
36
|
+
} from "./cli-jpc4r224.js";
|
|
37
37
|
import {
|
|
38
38
|
AppDefinitionSchema,
|
|
39
39
|
AppElementsSchema,
|
|
@@ -90,7 +90,7 @@ import {
|
|
|
90
90
|
upsertScriptConnection,
|
|
91
91
|
validateScriptImports,
|
|
92
92
|
writeBareImportShim
|
|
93
|
-
} from "./cli-
|
|
93
|
+
} from "./cli-7a93cv8c.js";
|
|
94
94
|
import {
|
|
95
95
|
detachedProcessGroup,
|
|
96
96
|
registerProcessGroup,
|
|
@@ -102,7 +102,7 @@ import {
|
|
|
102
102
|
ensureToken,
|
|
103
103
|
ensureTokenOrThrow,
|
|
104
104
|
onAuthorizationRefreshed
|
|
105
|
-
} from "./cli-
|
|
105
|
+
} from "./cli-w1apjecb.js";
|
|
106
106
|
import {
|
|
107
107
|
AgentFsProvider,
|
|
108
108
|
getFileStorageProvider,
|
|
@@ -158,7 +158,7 @@ import {
|
|
|
158
158
|
walkGraph,
|
|
159
159
|
withSaveWarnings,
|
|
160
160
|
workflowSaveWarnings
|
|
161
|
-
} from "./cli-
|
|
161
|
+
} from "./cli-1w0vj3gw.js";
|
|
162
162
|
import {
|
|
163
163
|
BaseExecutor,
|
|
164
164
|
HumanInTheLoopExecutor,
|
|
@@ -172,11 +172,14 @@ import {
|
|
|
172
172
|
resolveValidationPort,
|
|
173
173
|
resolveWorkflowLlmConfig,
|
|
174
174
|
validateDefinition
|
|
175
|
-
} from "./cli-
|
|
175
|
+
} from "./cli-6r2f7edk.js";
|
|
176
176
|
import {
|
|
177
177
|
ZodFirstPartyTypeKind,
|
|
178
178
|
objectType
|
|
179
179
|
} from "./cli-fad8m16k.js";
|
|
180
|
+
import {
|
|
181
|
+
require_dist
|
|
182
|
+
} from "./cli-51tf7ac3.js";
|
|
180
183
|
import {
|
|
181
184
|
CitationInputSchema,
|
|
182
185
|
MAX_TASK_CITATIONS,
|
|
@@ -200,10 +203,14 @@ import {
|
|
|
200
203
|
taskCitationWarnings,
|
|
201
204
|
upsertTaskCitations,
|
|
202
205
|
withSpan
|
|
203
|
-
} from "./cli-
|
|
206
|
+
} from "./cli-tcc4wd5d.js";
|
|
204
207
|
import {
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
externalChannelToolError,
|
|
209
|
+
getChannelBlockReason,
|
|
210
|
+
slackCode,
|
|
211
|
+
slackMissingScopeMessage,
|
|
212
|
+
withAutoJoin
|
|
213
|
+
} from "./cli-v3zhnxsb.js";
|
|
207
214
|
import {
|
|
208
215
|
findOrCreateUserByEmail,
|
|
209
216
|
findUserByEmail,
|
|
@@ -221,7 +228,7 @@ import {
|
|
|
221
228
|
resolveUserByToken,
|
|
222
229
|
revokeRuntimeTokensForRuntime,
|
|
223
230
|
unlinkIdentity
|
|
224
|
-
} from "./cli-
|
|
231
|
+
} from "./cli-3nxdmjrc.js";
|
|
225
232
|
import {
|
|
226
233
|
canProvisionKind,
|
|
227
234
|
init_runtime_providers
|
|
@@ -229,7 +236,7 @@ import {
|
|
|
229
236
|
import {
|
|
230
237
|
getUserGrant,
|
|
231
238
|
init_rbac_roles
|
|
232
|
-
} from "./cli-
|
|
239
|
+
} from "./cli-anzdk0z4.js";
|
|
233
240
|
import {
|
|
234
241
|
can,
|
|
235
242
|
decideAdmission,
|
|
@@ -255,6 +262,7 @@ import {
|
|
|
255
262
|
agentContextKey,
|
|
256
263
|
agentmailContextKey,
|
|
257
264
|
azureDevOpsContextKey,
|
|
265
|
+
azureDevOpsWorkItemContextKey,
|
|
258
266
|
beginRequestAuthScope,
|
|
259
267
|
breaksJsonValidity,
|
|
260
268
|
buildJiraContextKey,
|
|
@@ -546,7 +554,7 @@ import {
|
|
|
546
554
|
upsertSwarmConfig,
|
|
547
555
|
validateConfigValue,
|
|
548
556
|
workflowContextKey
|
|
549
|
-
} from "./cli-
|
|
557
|
+
} from "./cli-fmhj1ehj.js";
|
|
550
558
|
import {
|
|
551
559
|
$OpenAPIRegistry,
|
|
552
560
|
$OpenApiGeneratorV31,
|
|
@@ -616,7 +624,7 @@ import {
|
|
|
616
624
|
init_task_attachment_links,
|
|
617
625
|
taskAttachmentDisplayUrl,
|
|
618
626
|
taskAttachmentFetchCommand
|
|
619
|
-
} from "./cli-
|
|
627
|
+
} from "./cli-r4fqfpwf.js";
|
|
620
628
|
import {
|
|
621
629
|
getSlackConfiguration,
|
|
622
630
|
init_config
|
|
@@ -690,10 +698,14 @@ import {
|
|
|
690
698
|
unknown,
|
|
691
699
|
uuid
|
|
692
700
|
} from "./cli-w5e1d1vv.js";
|
|
701
|
+
import {
|
|
702
|
+
getNodeEnv,
|
|
703
|
+
init_node_env
|
|
704
|
+
} from "./cli-p0sg24zp.js";
|
|
693
705
|
import {
|
|
694
706
|
init_package,
|
|
695
707
|
package_default
|
|
696
|
-
} from "./cli-
|
|
708
|
+
} from "./cli-5er5gjbm.js";
|
|
697
709
|
import {
|
|
698
710
|
init_event_bus,
|
|
699
711
|
workflowEventBus
|
|
@@ -23010,6 +23022,32 @@ function normalizeOrgUrl(url) {
|
|
|
23010
23022
|
const trimmed = url?.trim().replace(/\/+$/, "");
|
|
23011
23023
|
return trimmed ? trimmed : null;
|
|
23012
23024
|
}
|
|
23025
|
+
function orgKeyOf(url) {
|
|
23026
|
+
if (!url)
|
|
23027
|
+
return null;
|
|
23028
|
+
let parsed;
|
|
23029
|
+
try {
|
|
23030
|
+
parsed = new URL(url);
|
|
23031
|
+
} catch {
|
|
23032
|
+
return null;
|
|
23033
|
+
}
|
|
23034
|
+
if (parsed.protocol !== "https:" || parsed.username || parsed.password)
|
|
23035
|
+
return null;
|
|
23036
|
+
const host = parsed.host.toLowerCase();
|
|
23037
|
+
if (host !== "dev.azure.com")
|
|
23038
|
+
return host;
|
|
23039
|
+
const org = parsed.pathname.split("/").find(Boolean);
|
|
23040
|
+
return org ? `${host}/${org.toLowerCase()}` : null;
|
|
23041
|
+
}
|
|
23042
|
+
function isTrustedOrgUrl(url) {
|
|
23043
|
+
const key = orgKeyOf(url);
|
|
23044
|
+
if (!key)
|
|
23045
|
+
return false;
|
|
23046
|
+
const configured = orgKeyOf(orgUrl);
|
|
23047
|
+
if (orgUrl)
|
|
23048
|
+
return key === configured;
|
|
23049
|
+
return key.startsWith("dev.azure.com/") || key.endsWith(".visualstudio.com");
|
|
23050
|
+
}
|
|
23013
23051
|
var AZURE_DEVOPS_BOT_NAME = process.env.AZURE_DEVOPS_BOT_NAME ?? "agent-swarm-bot";
|
|
23014
23052
|
function resetAzureDevOps() {
|
|
23015
23053
|
initialized3 = false;
|
|
@@ -23033,35 +23071,69 @@ function headers() {
|
|
|
23033
23071
|
function pullRequestBase(ref) {
|
|
23034
23072
|
return `${ref.orgUrl}/${encodeURIComponent(ref.projectId)}/_apis/git/repositories/` + `${encodeURIComponent(ref.repositoryId)}/pullRequests/${ref.pullRequestId}`;
|
|
23035
23073
|
}
|
|
23036
|
-
|
|
23074
|
+
|
|
23075
|
+
class AzureDevOpsLookupError extends Error {
|
|
23076
|
+
}
|
|
23077
|
+
var cachedBotIdentity = null;
|
|
23078
|
+
function credentialedOrgUrl(orgBaseUrl) {
|
|
23079
|
+
const orgUrl2 = normalizeOrgUrl(orgBaseUrl) ?? getAzureDevOpsOrgUrl();
|
|
23080
|
+
if (!orgUrl2 || !getAzureDevOpsToken())
|
|
23081
|
+
return null;
|
|
23082
|
+
if (!isTrustedOrgUrl(orgUrl2)) {
|
|
23083
|
+
console.error("[AzureDevOps] Refusing to send credentials to an untrusted organization URL");
|
|
23084
|
+
return null;
|
|
23085
|
+
}
|
|
23086
|
+
return orgUrl2;
|
|
23087
|
+
}
|
|
23088
|
+
async function loadBotIdentity(orgUrl2) {
|
|
23089
|
+
if (cachedBotIdentity?.orgUrl === orgUrl2)
|
|
23090
|
+
return cachedBotIdentity.identity;
|
|
23091
|
+
let data;
|
|
23092
|
+
try {
|
|
23093
|
+
const resp = await fetch(`${orgUrl2}/_apis/connectionData`, {
|
|
23094
|
+
headers: headers(),
|
|
23095
|
+
redirect: "error"
|
|
23096
|
+
});
|
|
23097
|
+
if (!resp.ok)
|
|
23098
|
+
throw new AzureDevOpsLookupError(`connectionData returned ${resp.status}`);
|
|
23099
|
+
data = await resp.json();
|
|
23100
|
+
} catch (err) {
|
|
23101
|
+
if (err instanceof AzureDevOpsLookupError)
|
|
23102
|
+
throw err;
|
|
23103
|
+
throw new AzureDevOpsLookupError(`connectionData request failed: ${String(err)}`);
|
|
23104
|
+
}
|
|
23105
|
+
const identity = {
|
|
23106
|
+
id: data.authenticatedUser?.id ?? null,
|
|
23107
|
+
uniqueName: data.authenticatedUser?.properties?.Account?.$value?.trim() || null
|
|
23108
|
+
};
|
|
23109
|
+
if (identity.id || identity.uniqueName)
|
|
23110
|
+
cachedBotIdentity = { orgUrl: orgUrl2, identity };
|
|
23111
|
+
return identity;
|
|
23112
|
+
}
|
|
23037
23113
|
async function resolveAzureDevOpsBotId(orgBaseUrl) {
|
|
23038
23114
|
const fromEnv = process.env.AZURE_DEVOPS_BOT_ID?.trim();
|
|
23039
23115
|
if (fromEnv)
|
|
23040
23116
|
return fromEnv;
|
|
23041
|
-
const orgUrl2 =
|
|
23042
|
-
if (!orgUrl2
|
|
23117
|
+
const orgUrl2 = credentialedOrgUrl(orgBaseUrl);
|
|
23118
|
+
if (!orgUrl2)
|
|
23043
23119
|
return null;
|
|
23044
|
-
if (cachedBotId?.orgUrl === orgUrl2)
|
|
23045
|
-
return cachedBotId.id;
|
|
23046
23120
|
try {
|
|
23047
|
-
|
|
23048
|
-
if (!resp.ok) {
|
|
23049
|
-
console.error(`[AzureDevOps] Failed to resolve bot identity: ${resp.status}`);
|
|
23050
|
-
return null;
|
|
23051
|
-
}
|
|
23052
|
-
const data = await resp.json();
|
|
23053
|
-
const id = data.authenticatedUser?.id;
|
|
23054
|
-
if (!id)
|
|
23055
|
-
return null;
|
|
23056
|
-
cachedBotId = { orgUrl: orgUrl2, id };
|
|
23057
|
-
return id;
|
|
23121
|
+
return (await loadBotIdentity(orgUrl2)).id;
|
|
23058
23122
|
} catch (err) {
|
|
23059
|
-
console.error(`[AzureDevOps]
|
|
23123
|
+
console.error(`[AzureDevOps] Failed to resolve bot identity:`, err);
|
|
23060
23124
|
return null;
|
|
23061
23125
|
}
|
|
23062
23126
|
}
|
|
23127
|
+
async function resolveAzureDevOpsBotIdentity(orgBaseUrl) {
|
|
23128
|
+
const fromEnv = process.env.AZURE_DEVOPS_BOT_ID?.trim() || null;
|
|
23129
|
+
const orgUrl2 = credentialedOrgUrl(orgBaseUrl);
|
|
23130
|
+
if (!orgUrl2)
|
|
23131
|
+
return { id: fromEnv, uniqueName: null };
|
|
23132
|
+
const identity = await loadBotIdentity(orgUrl2);
|
|
23133
|
+
return { id: fromEnv ?? identity.id, uniqueName: identity.uniqueName };
|
|
23134
|
+
}
|
|
23063
23135
|
function resetAzureDevOpsBotIdCache() {
|
|
23064
|
-
|
|
23136
|
+
cachedBotIdentity = null;
|
|
23065
23137
|
}
|
|
23066
23138
|
async function likeAzureDevOpsComment(ref, threadId, commentId) {
|
|
23067
23139
|
const url = `${pullRequestBase(ref)}/threads/${threadId}/comments/${commentId}/likes?api-version=${API_VERSION}`;
|
|
@@ -23074,6 +23146,156 @@ async function likeAzureDevOpsComment(ref, threadId, commentId) {
|
|
|
23074
23146
|
console.error(`[AzureDevOps] Error liking comment:`, err);
|
|
23075
23147
|
}
|
|
23076
23148
|
}
|
|
23149
|
+
// src/azure-devops/boards-sync.ts
|
|
23150
|
+
init_db3();
|
|
23151
|
+
|
|
23152
|
+
// src/be/db-queries/tracker.ts
|
|
23153
|
+
init_date_utils();
|
|
23154
|
+
init_db3();
|
|
23155
|
+
function normalizeTrackerSync(row) {
|
|
23156
|
+
return {
|
|
23157
|
+
...row,
|
|
23158
|
+
lastSyncedAt: normalizeDateRequired(row.lastSyncedAt),
|
|
23159
|
+
createdAt: normalizeDateRequired(row.createdAt)
|
|
23160
|
+
};
|
|
23161
|
+
}
|
|
23162
|
+
function normalizeTrackerAgentMapping(row) {
|
|
23163
|
+
return {
|
|
23164
|
+
...row,
|
|
23165
|
+
createdAt: normalizeDateRequired(row.createdAt)
|
|
23166
|
+
};
|
|
23167
|
+
}
|
|
23168
|
+
async function getTrackerSync(provider, entityType, swarmId) {
|
|
23169
|
+
const row = await getDbClient().get("SELECT * FROM tracker_sync WHERE provider = ? AND entityType = ? AND swarmId = ?", [provider, entityType, swarmId]);
|
|
23170
|
+
return row ? normalizeTrackerSync(row) : null;
|
|
23171
|
+
}
|
|
23172
|
+
async function getTrackerSyncByExternalId(provider, entityType, externalId) {
|
|
23173
|
+
const row = await getDbClient().get("SELECT * FROM tracker_sync WHERE provider = ? AND entityType = ? AND externalId = ?", [provider, entityType, externalId]);
|
|
23174
|
+
return row ? normalizeTrackerSync(row) : null;
|
|
23175
|
+
}
|
|
23176
|
+
async function createTrackerSyncIfAbsent(data) {
|
|
23177
|
+
const insertResult = await getDbClient().get(`INSERT INTO tracker_sync (provider, entityType, providerEntityType, swarmId, externalId, externalIdentifier, externalUrl, lastSyncOrigin, lastDeliveryId, syncDirection)
|
|
23178
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
23179
|
+
ON CONFLICT (provider, entityType, externalId) DO NOTHING
|
|
23180
|
+
RETURNING *`, [
|
|
23181
|
+
data.provider,
|
|
23182
|
+
data.entityType,
|
|
23183
|
+
data.providerEntityType ?? null,
|
|
23184
|
+
data.swarmId,
|
|
23185
|
+
data.externalId,
|
|
23186
|
+
data.externalIdentifier ?? null,
|
|
23187
|
+
data.externalUrl ?? null,
|
|
23188
|
+
data.lastSyncOrigin ?? null,
|
|
23189
|
+
data.lastDeliveryId ?? null,
|
|
23190
|
+
data.syncDirection ?? "inbound"
|
|
23191
|
+
]);
|
|
23192
|
+
if (insertResult) {
|
|
23193
|
+
return { inserted: true, sync: normalizeTrackerSync(insertResult) };
|
|
23194
|
+
}
|
|
23195
|
+
const existing = await getTrackerSyncByExternalId(data.provider, data.entityType, data.externalId);
|
|
23196
|
+
if (!existing) {
|
|
23197
|
+
throw new Error(`[tracker] createTrackerSyncIfAbsent: ON CONFLICT fired but no existing row found for (${data.provider}, ${data.entityType}, ${data.externalId})`);
|
|
23198
|
+
}
|
|
23199
|
+
return { inserted: false, sync: existing };
|
|
23200
|
+
}
|
|
23201
|
+
async function updateTrackerSyncSwarmId(id, swarmId) {
|
|
23202
|
+
await getDbClient().run("UPDATE tracker_sync SET swarmId = ? WHERE id = ?", [swarmId, id]);
|
|
23203
|
+
}
|
|
23204
|
+
async function repointTrackerSyncBySwarmId(oldSwarmId, newSwarmId) {
|
|
23205
|
+
const result = await getDbClient().run("UPDATE tracker_sync SET swarmId = ? WHERE swarmId = ?", [
|
|
23206
|
+
newSwarmId,
|
|
23207
|
+
oldSwarmId
|
|
23208
|
+
]);
|
|
23209
|
+
return Number(result.changes ?? 0);
|
|
23210
|
+
}
|
|
23211
|
+
async function createTrackerSync(data) {
|
|
23212
|
+
const result = await getDbClient().get(`INSERT INTO tracker_sync (provider, entityType, providerEntityType, swarmId, externalId, externalIdentifier, externalUrl, lastSyncOrigin, lastDeliveryId, syncDirection)
|
|
23213
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
23214
|
+
RETURNING *`, [
|
|
23215
|
+
data.provider,
|
|
23216
|
+
data.entityType,
|
|
23217
|
+
data.providerEntityType ?? null,
|
|
23218
|
+
data.swarmId,
|
|
23219
|
+
data.externalId,
|
|
23220
|
+
data.externalIdentifier ?? null,
|
|
23221
|
+
data.externalUrl ?? null,
|
|
23222
|
+
data.lastSyncOrigin ?? null,
|
|
23223
|
+
data.lastDeliveryId ?? null,
|
|
23224
|
+
data.syncDirection ?? "inbound"
|
|
23225
|
+
]);
|
|
23226
|
+
return normalizeTrackerSync(result);
|
|
23227
|
+
}
|
|
23228
|
+
async function updateTrackerSync(id, data) {
|
|
23229
|
+
const sets = [];
|
|
23230
|
+
const values = [];
|
|
23231
|
+
if (data.lastSyncedAt !== undefined) {
|
|
23232
|
+
sets.push("lastSyncedAt = ?");
|
|
23233
|
+
values.push(data.lastSyncedAt);
|
|
23234
|
+
}
|
|
23235
|
+
if (data.lastSyncOrigin !== undefined) {
|
|
23236
|
+
sets.push("lastSyncOrigin = ?");
|
|
23237
|
+
values.push(data.lastSyncOrigin);
|
|
23238
|
+
}
|
|
23239
|
+
if (data.lastDeliveryId !== undefined) {
|
|
23240
|
+
sets.push("lastDeliveryId = ?");
|
|
23241
|
+
values.push(data.lastDeliveryId);
|
|
23242
|
+
}
|
|
23243
|
+
if (data.syncDirection !== undefined) {
|
|
23244
|
+
sets.push("syncDirection = ?");
|
|
23245
|
+
values.push(data.syncDirection);
|
|
23246
|
+
}
|
|
23247
|
+
if (data.externalUrl !== undefined) {
|
|
23248
|
+
sets.push("externalUrl = ?");
|
|
23249
|
+
values.push(data.externalUrl);
|
|
23250
|
+
}
|
|
23251
|
+
if (data.externalIdentifier !== undefined) {
|
|
23252
|
+
sets.push("externalIdentifier = ?");
|
|
23253
|
+
values.push(data.externalIdentifier);
|
|
23254
|
+
}
|
|
23255
|
+
if (sets.length === 0)
|
|
23256
|
+
return;
|
|
23257
|
+
values.push(id);
|
|
23258
|
+
await getDbClient().run(`UPDATE tracker_sync SET ${sets.join(", ")} WHERE id = ?`, values);
|
|
23259
|
+
}
|
|
23260
|
+
async function deleteTrackerSync(id) {
|
|
23261
|
+
await getDbClient().run("DELETE FROM tracker_sync WHERE id = ?", [id]);
|
|
23262
|
+
}
|
|
23263
|
+
async function hasTrackerDelivery(provider, deliveryId) {
|
|
23264
|
+
if (!deliveryId)
|
|
23265
|
+
return false;
|
|
23266
|
+
const row = await getDbClient().get("SELECT 1 AS hit FROM tracker_sync WHERE provider = ? AND lastDeliveryId = ? LIMIT 1", [provider, deliveryId]);
|
|
23267
|
+
return !!row;
|
|
23268
|
+
}
|
|
23269
|
+
async function markTrackerDelivery(provider, entityType, externalId, deliveryId) {
|
|
23270
|
+
await getDbClient().run("UPDATE tracker_sync SET lastDeliveryId = ? WHERE provider = ? AND entityType = ? AND externalId = ?", [deliveryId, provider, entityType, externalId]);
|
|
23271
|
+
}
|
|
23272
|
+
async function getAllTrackerSyncs(provider, entityType) {
|
|
23273
|
+
const conditions = [];
|
|
23274
|
+
const values = [];
|
|
23275
|
+
if (provider) {
|
|
23276
|
+
conditions.push("provider = ?");
|
|
23277
|
+
values.push(provider);
|
|
23278
|
+
}
|
|
23279
|
+
if (entityType) {
|
|
23280
|
+
conditions.push("entityType = ?");
|
|
23281
|
+
values.push(entityType);
|
|
23282
|
+
}
|
|
23283
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
23284
|
+
const rows = await getDbClient().query(`SELECT * FROM tracker_sync ${where} ORDER BY createdAt DESC`, values);
|
|
23285
|
+
return rows.map(normalizeTrackerSync);
|
|
23286
|
+
}
|
|
23287
|
+
async function createTrackerAgentMapping(data) {
|
|
23288
|
+
const result = await getDbClient().get(`INSERT INTO tracker_agent_mapping (provider, agentId, externalUserId, agentName)
|
|
23289
|
+
VALUES (?, ?, ?, ?)
|
|
23290
|
+
RETURNING *`, [data.provider, data.agentId, data.externalUserId, data.agentName]);
|
|
23291
|
+
return normalizeTrackerAgentMapping(result);
|
|
23292
|
+
}
|
|
23293
|
+
|
|
23294
|
+
// src/azure-devops/boards-sync.ts
|
|
23295
|
+
init_resolver();
|
|
23296
|
+
init_context_key();
|
|
23297
|
+
init_types();
|
|
23298
|
+
|
|
23077
23299
|
// src/azure-devops/handlers.ts
|
|
23078
23300
|
init_db3();
|
|
23079
23301
|
init_users();
|
|
@@ -23210,6 +23432,81 @@ Comment:
|
|
|
23210
23432
|
],
|
|
23211
23433
|
category: "event"
|
|
23212
23434
|
});
|
|
23435
|
+
registerTemplate({
|
|
23436
|
+
eventType: "common.command_suggestions.azure_devops_work_item",
|
|
23437
|
+
header: "",
|
|
23438
|
+
defaultBody: 'Workers have the `az` CLI with the azure-devops extension, authenticated with the swarm PAT. Inspect the work item with `az boards work-item show --id {{work_item_id}}`. To reply on it, run `az boards work-item update --id {{work_item_id}} --discussion "<reply>"`.',
|
|
23439
|
+
variables: [{ name: "work_item_id", description: "Work item ID" }],
|
|
23440
|
+
category: "common"
|
|
23441
|
+
});
|
|
23442
|
+
var WORK_ITEM_VARIABLES = [
|
|
23443
|
+
{ name: "work_item_id", description: "Work item ID" },
|
|
23444
|
+
{ name: "work_item_type", description: "Work item type (Task, Bug, Issue, ...)" },
|
|
23445
|
+
{ name: "title", description: "Work item title" },
|
|
23446
|
+
{ name: "state", description: "Work item state" },
|
|
23447
|
+
{ name: "project", description: "Azure DevOps project name" },
|
|
23448
|
+
{ name: "work_item_url", description: "Work item web URL" },
|
|
23449
|
+
{
|
|
23450
|
+
name: "username",
|
|
23451
|
+
description: "Actor identity — resolved canonical name (e.g. 'Luis (azure-devops:luis@example.com)') or the UNKNOWN sentinel"
|
|
23452
|
+
}
|
|
23453
|
+
];
|
|
23454
|
+
registerTemplate({
|
|
23455
|
+
eventType: "azure-devops.work_item.assigned",
|
|
23456
|
+
header: "[Azure DevOps {{work_item_type}} #{{work_item_id}}] {{title}}",
|
|
23457
|
+
defaultBody: `Project: {{project}}
|
|
23458
|
+
State: {{state}}
|
|
23459
|
+
Triggered by: {{username}} ({{trigger}})
|
|
23460
|
+
URL: {{work_item_url}}
|
|
23461
|
+
|
|
23462
|
+
{{description_section}}{{@template[common.command_suggestions.azure_devops_work_item]}}{{@template[common.delegation_instruction.azure_devops]}}`,
|
|
23463
|
+
variables: [
|
|
23464
|
+
...WORK_ITEM_VARIABLES,
|
|
23465
|
+
{ name: "trigger", description: "Why the task was created (assigned to the bot, mentioned)" },
|
|
23466
|
+
{
|
|
23467
|
+
name: "description_section",
|
|
23468
|
+
description: "Description section (HTML stripped) or empty string"
|
|
23469
|
+
}
|
|
23470
|
+
],
|
|
23471
|
+
category: "event"
|
|
23472
|
+
});
|
|
23473
|
+
registerTemplate({
|
|
23474
|
+
eventType: "azure-devops.work_item.commented",
|
|
23475
|
+
header: "[Azure DevOps {{work_item_type}} #{{work_item_id}}] {{username}} mentioned bot",
|
|
23476
|
+
defaultBody: `Project: {{project}}
|
|
23477
|
+
Title: {{title}}
|
|
23478
|
+
State: {{state}}
|
|
23479
|
+
URL: {{work_item_url}}
|
|
23480
|
+
|
|
23481
|
+
Comment:
|
|
23482
|
+
{{context}}{{existing_task_note}}
|
|
23483
|
+
|
|
23484
|
+
{{@template[common.command_suggestions.azure_devops_work_item]}}{{@template[common.delegation_instruction.azure_devops]}}`,
|
|
23485
|
+
variables: [
|
|
23486
|
+
...WORK_ITEM_VARIABLES,
|
|
23487
|
+
{ name: "context", description: "Comment text with the bot mention removed" },
|
|
23488
|
+
{ name: "existing_task_note", description: "Note about an active task, or empty string" }
|
|
23489
|
+
],
|
|
23490
|
+
category: "event"
|
|
23491
|
+
});
|
|
23492
|
+
registerTemplate({
|
|
23493
|
+
eventType: "azure-devops.work_item.followup",
|
|
23494
|
+
header: "[Azure DevOps {{work_item_type}} #{{work_item_id}}] Follow-up: {{title}}",
|
|
23495
|
+
defaultBody: `Project: {{project}}
|
|
23496
|
+
State: {{state}}
|
|
23497
|
+
Trigger: {{trigger}} by {{username}}
|
|
23498
|
+
URL: {{work_item_url}}
|
|
23499
|
+
|
|
23500
|
+
{{user_message}}
|
|
23501
|
+
|
|
23502
|
+
{{@template[common.command_suggestions.azure_devops_work_item]}}{{@template[common.delegation_instruction.azure_devops]}}`,
|
|
23503
|
+
variables: [
|
|
23504
|
+
...WORK_ITEM_VARIABLES,
|
|
23505
|
+
{ name: "trigger", description: "What re-engaged the swarm" },
|
|
23506
|
+
{ name: "user_message", description: "Comment text, or empty string" }
|
|
23507
|
+
],
|
|
23508
|
+
category: "event"
|
|
23509
|
+
});
|
|
23213
23510
|
|
|
23214
23511
|
// src/azure-devops/handlers.ts
|
|
23215
23512
|
var processedEvents2 = new Map;
|
|
@@ -23305,6 +23602,10 @@ async function resolveAzureDevOpsSender(identity, sampleEventType, sampleContext
|
|
|
23305
23602
|
await linkIdentity(linked.id, "azure-devops", externalId, AZURE_DEVOPS_WEBHOOK_ACTOR);
|
|
23306
23603
|
return linked.id;
|
|
23307
23604
|
}
|
|
23605
|
+
await recordUnmappedIdentity(externalId, sampleEventType, sampleContext);
|
|
23606
|
+
return;
|
|
23607
|
+
}
|
|
23608
|
+
async function recordUnmappedIdentity(externalId, sampleEventType, sampleContext) {
|
|
23308
23609
|
await upsertKv({
|
|
23309
23610
|
namespace: UNMAPPED_NAMESPACE,
|
|
23310
23611
|
key: `${externalId}:meta`,
|
|
@@ -23317,7 +23618,18 @@ async function resolveAzureDevOpsSender(identity, sampleEventType, sampleContext
|
|
|
23317
23618
|
expiresAt: Date.now() + UNMAPPED_TTL_MS
|
|
23318
23619
|
});
|
|
23319
23620
|
await incrKv(UNMAPPED_NAMESPACE, `${externalId}:count`, 1);
|
|
23320
|
-
|
|
23621
|
+
}
|
|
23622
|
+
async function authorizeAzureDevOpsActor(identity, sampleEventType, sampleContext) {
|
|
23623
|
+
const externalId = externalIdOf(identity);
|
|
23624
|
+
const known = await findUserByExternalId("azure-devops", externalId) ?? (externalId.includes("@") ? await findUserByEmail(externalId) : null);
|
|
23625
|
+
if (known)
|
|
23626
|
+
return known.id;
|
|
23627
|
+
const allowed = (process.env.AZURE_DEVOPS_ALLOWED_ACTORS ?? "").split(",").map((name) => name.trim().toLowerCase()).filter(Boolean);
|
|
23628
|
+
if (allowed.includes(externalId.toLowerCase())) {
|
|
23629
|
+
return await resolveAzureDevOpsSender(identity, sampleEventType, sampleContext) ?? null;
|
|
23630
|
+
}
|
|
23631
|
+
await recordUnmappedIdentity(externalId, sampleEventType, sampleContext);
|
|
23632
|
+
return null;
|
|
23321
23633
|
}
|
|
23322
23634
|
async function renderAzureDevOpsIdentity(identity) {
|
|
23323
23635
|
return renderIdentity(await resolveIdentity("azure-devops", externalIdOf(identity)));
|
|
@@ -23469,6 +23781,460 @@ _Note: There's an active task (${existingTask.id}) for this PR #${pr.pullRequest
|
|
|
23469
23781
|
}
|
|
23470
23782
|
return { created: true, taskId: task.id };
|
|
23471
23783
|
}
|
|
23784
|
+
|
|
23785
|
+
// src/azure-devops/html.ts
|
|
23786
|
+
var ENTITIES = {
|
|
23787
|
+
"&": "&",
|
|
23788
|
+
"<": "<",
|
|
23789
|
+
">": ">",
|
|
23790
|
+
""": '"',
|
|
23791
|
+
"'": "'",
|
|
23792
|
+
" ": " "
|
|
23793
|
+
};
|
|
23794
|
+
function htmlToText(html) {
|
|
23795
|
+
if (!html)
|
|
23796
|
+
return "";
|
|
23797
|
+
return html.replace(/<br\s*\/?>|<\/(?:div|p|li|h[1-6])>/gi, `
|
|
23798
|
+
`).replace(/<[^>]*>/g, "").replace(/&(?:amp|lt|gt|quot|#39|nbsp);/g, (entity) => ENTITIES[entity] ?? entity).replace(/[ \t]+/g, " ").replace(/ ?\n ?/g, `
|
|
23799
|
+
`).replace(/\n{3,}/g, `
|
|
23800
|
+
|
|
23801
|
+
`).trim();
|
|
23802
|
+
}
|
|
23803
|
+
|
|
23804
|
+
// src/azure-devops/boards-sync.ts
|
|
23805
|
+
var PROVIDER = "azure-devops";
|
|
23806
|
+
var DELETED_REASON = "Azure DevOps work item deleted";
|
|
23807
|
+
var DESCRIPTION_LIMIT = 4000;
|
|
23808
|
+
function hasFields(value) {
|
|
23809
|
+
return typeof value === "object" && value !== null;
|
|
23810
|
+
}
|
|
23811
|
+
function workItemOf(event) {
|
|
23812
|
+
const resource = event?.resource;
|
|
23813
|
+
if (typeof event?.id !== "string" || typeof resource?.id !== "number")
|
|
23814
|
+
return null;
|
|
23815
|
+
return hasFields(resource.fields) ? resource : null;
|
|
23816
|
+
}
|
|
23817
|
+
function updatedPayloadOf(event) {
|
|
23818
|
+
const resource = event?.resource;
|
|
23819
|
+
if (typeof event?.id !== "string" || typeof resource?.workItemId !== "number")
|
|
23820
|
+
return null;
|
|
23821
|
+
return hasFields(resource.revision?.fields) ? resource : null;
|
|
23822
|
+
}
|
|
23823
|
+
function parseIdentityString(value) {
|
|
23824
|
+
if (typeof value !== "string" || !value.trim())
|
|
23825
|
+
return null;
|
|
23826
|
+
const match = value.match(/^(.*?)\s*<([^>]+)>\s*$/);
|
|
23827
|
+
const displayName = (match?.[1] || value).trim();
|
|
23828
|
+
const uniqueName = match?.[2]?.trim();
|
|
23829
|
+
return { id: uniqueName ?? displayName, displayName, uniqueName };
|
|
23830
|
+
}
|
|
23831
|
+
async function resolveBot(orgUrl2) {
|
|
23832
|
+
return await resolveAzureDevOpsBotIdentity(orgUrl2);
|
|
23833
|
+
}
|
|
23834
|
+
function isBotIdentity(identity, bot) {
|
|
23835
|
+
if (!identity)
|
|
23836
|
+
return false;
|
|
23837
|
+
if (bot.id && identity.id.toLowerCase() === bot.id.toLowerCase())
|
|
23838
|
+
return true;
|
|
23839
|
+
const name = identity.uniqueName?.toLowerCase();
|
|
23840
|
+
return !!name && !!bot.uniqueName && name === bot.uniqueName.toLowerCase();
|
|
23841
|
+
}
|
|
23842
|
+
function isBotString(value, bot) {
|
|
23843
|
+
return isBotIdentity(parseIdentityString(value), bot);
|
|
23844
|
+
}
|
|
23845
|
+
function pickerMentionPattern(botId) {
|
|
23846
|
+
return new RegExp(`<a\\b[^>]*data-vss-mention="version:[\\d.]+,${escapeRegExp(botId)}"[^>]*>.*?</a>`, "gi");
|
|
23847
|
+
}
|
|
23848
|
+
function plainMentionPatterns(bot) {
|
|
23849
|
+
const names = [AZURE_DEVOPS_BOT_NAME];
|
|
23850
|
+
const local = bot.uniqueName?.split("@")[0];
|
|
23851
|
+
if (local)
|
|
23852
|
+
names.push(local);
|
|
23853
|
+
return names.map((name) => new RegExp(`@${escapeRegExp(name)}(?![\\w-])`, "gi"));
|
|
23854
|
+
}
|
|
23855
|
+
function inspectMention(html, bot) {
|
|
23856
|
+
if (!html)
|
|
23857
|
+
return { mentioned: false, text: "" };
|
|
23858
|
+
let withoutPicker = html;
|
|
23859
|
+
let mentioned = false;
|
|
23860
|
+
if (bot.id) {
|
|
23861
|
+
const re = pickerMentionPattern(bot.id);
|
|
23862
|
+
mentioned = re.test(html);
|
|
23863
|
+
withoutPicker = html.replace(re, "");
|
|
23864
|
+
}
|
|
23865
|
+
let text = htmlToText(withoutPicker);
|
|
23866
|
+
const plain = plainMentionPatterns(bot);
|
|
23867
|
+
if (plain.some((re) => re.test(text)) || detectMention(text, null)) {
|
|
23868
|
+
mentioned = true;
|
|
23869
|
+
}
|
|
23870
|
+
for (const re of plain)
|
|
23871
|
+
text = text.replace(re, "");
|
|
23872
|
+
return { mentioned, text: text.trim() };
|
|
23873
|
+
}
|
|
23874
|
+
function orgFor(event) {
|
|
23875
|
+
const containers = event.resourceContainers;
|
|
23876
|
+
const fromUrl = event.resource?.url?.split("/_apis/")[0];
|
|
23877
|
+
const candidates = [
|
|
23878
|
+
containers?.account?.baseUrl,
|
|
23879
|
+
containers?.collection?.baseUrl,
|
|
23880
|
+
fromUrl?.includes("://") ? fromUrl : undefined
|
|
23881
|
+
].map((url2) => normalizeOrgUrl(url2)).filter((url2) => !!url2);
|
|
23882
|
+
const configured = getAzureDevOpsOrgUrl();
|
|
23883
|
+
const url = candidates[0] ?? configured;
|
|
23884
|
+
if (!url || !isTrustedOrgUrl(url))
|
|
23885
|
+
return null;
|
|
23886
|
+
const key = orgKeyOf(url);
|
|
23887
|
+
if (!key || candidates.some((c) => orgKeyOf(c) !== key))
|
|
23888
|
+
return null;
|
|
23889
|
+
return { url, key };
|
|
23890
|
+
}
|
|
23891
|
+
function dropUntrustedOrg(eventType) {
|
|
23892
|
+
console.warn(`[AzureDevOps] Dropping ${eventType} event from an untrusted organization`);
|
|
23893
|
+
return { created: false };
|
|
23894
|
+
}
|
|
23895
|
+
function trackerKey(org, workItemId) {
|
|
23896
|
+
return `${org.key}#${workItemId}`;
|
|
23897
|
+
}
|
|
23898
|
+
function organizationOf(orgUrl2) {
|
|
23899
|
+
if (!orgUrl2)
|
|
23900
|
+
return "unknown";
|
|
23901
|
+
try {
|
|
23902
|
+
const url = new URL(orgUrl2);
|
|
23903
|
+
const segment = url.pathname.split("/").find(Boolean);
|
|
23904
|
+
return (segment ?? url.hostname.split(".")[0] ?? "unknown").replace(/:/g, "_");
|
|
23905
|
+
} catch {
|
|
23906
|
+
return "unknown";
|
|
23907
|
+
}
|
|
23908
|
+
}
|
|
23909
|
+
function workItemUrl(ctx) {
|
|
23910
|
+
const project = ctx.fields["System.TeamProject"];
|
|
23911
|
+
const base = ctx.org.url;
|
|
23912
|
+
return project ? `${base}/${encodeURIComponent(project)}/_workitems/edit/${ctx.workItemId}` : `${base}/_workitems/edit/${ctx.workItemId}`;
|
|
23913
|
+
}
|
|
23914
|
+
var RECEIPT_NAMESPACE = "integration:azure-devops:deliveries";
|
|
23915
|
+
var RECEIPT_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
23916
|
+
var inFlight = new Map;
|
|
23917
|
+
function withReceipt(key, fn) {
|
|
23918
|
+
const pending = inFlight.get(key);
|
|
23919
|
+
if (pending)
|
|
23920
|
+
return pending.then(() => ({ created: false }));
|
|
23921
|
+
const attempt = (async () => {
|
|
23922
|
+
if (await getKv(RECEIPT_NAMESPACE, key) || await hasTrackerDelivery(PROVIDER, key)) {
|
|
23923
|
+
return { created: false };
|
|
23924
|
+
}
|
|
23925
|
+
const result = await fn();
|
|
23926
|
+
await upsertKv({
|
|
23927
|
+
namespace: RECEIPT_NAMESPACE,
|
|
23928
|
+
key,
|
|
23929
|
+
value: { handledAt: new Date().toISOString() },
|
|
23930
|
+
valueType: "json",
|
|
23931
|
+
expiresAt: Date.now() + RECEIPT_TTL_MS
|
|
23932
|
+
});
|
|
23933
|
+
return result;
|
|
23934
|
+
})().finally(() => {
|
|
23935
|
+
inFlight.delete(key);
|
|
23936
|
+
});
|
|
23937
|
+
inFlight.set(key, attempt);
|
|
23938
|
+
return attempt;
|
|
23939
|
+
}
|
|
23940
|
+
var workItemLocks = new Map;
|
|
23941
|
+
async function withWorkItemLock(key, fn) {
|
|
23942
|
+
const run = (workItemLocks.get(key) ?? Promise.resolve()).then(fn);
|
|
23943
|
+
const tail = run.catch(() => {});
|
|
23944
|
+
workItemLocks.set(key, tail);
|
|
23945
|
+
try {
|
|
23946
|
+
return await run;
|
|
23947
|
+
} finally {
|
|
23948
|
+
if (workItemLocks.get(key) === tail)
|
|
23949
|
+
workItemLocks.delete(key);
|
|
23950
|
+
}
|
|
23951
|
+
}
|
|
23952
|
+
async function handleDelivery(org, eventId, workItemId, fn, extraReceiptKey) {
|
|
23953
|
+
const locked = () => withWorkItemLock(trackerKey(org, workItemId), fn);
|
|
23954
|
+
return await withReceipt(eventId, () => extraReceiptKey ? withReceipt(extraReceiptKey, locked) : locked());
|
|
23955
|
+
}
|
|
23956
|
+
var TRIGGER_LABEL = {
|
|
23957
|
+
assigned: "work item assigned to the bot",
|
|
23958
|
+
mentioned_description: "bot mentioned in the description",
|
|
23959
|
+
mentioned_comment: "bot mentioned in a comment",
|
|
23960
|
+
restored: "work item restored"
|
|
23961
|
+
};
|
|
23962
|
+
async function createWorkItemTask(ctx, trigger, opts) {
|
|
23963
|
+
const { fields } = ctx;
|
|
23964
|
+
const description = htmlToText(fields["System.Description"]).slice(0, DESCRIPTION_LIMIT);
|
|
23965
|
+
const common = {
|
|
23966
|
+
work_item_id: ctx.workItemId,
|
|
23967
|
+
work_item_type: fields["System.WorkItemType"] ?? "Work item",
|
|
23968
|
+
title: fields["System.Title"] ?? "(no title)",
|
|
23969
|
+
state: fields["System.State"] ?? "unknown",
|
|
23970
|
+
project: fields["System.TeamProject"] ?? "unknown",
|
|
23971
|
+
work_item_url: workItemUrl(ctx),
|
|
23972
|
+
username: ctx.actor ? await renderAzureDevOpsIdentity(ctx.actor) : "UNKNOWN"
|
|
23973
|
+
};
|
|
23974
|
+
const templateName = opts.followup ? "azure-devops.work_item.followup" : trigger === "mentioned_comment" ? "azure-devops.work_item.commented" : "azure-devops.work_item.assigned";
|
|
23975
|
+
const variables = templateName === "azure-devops.work_item.followup" ? { ...common, trigger: TRIGGER_LABEL[trigger], user_message: opts.commentText ?? "" } : templateName === "azure-devops.work_item.commented" ? {
|
|
23976
|
+
...common,
|
|
23977
|
+
context: opts.commentText ?? "",
|
|
23978
|
+
existing_task_note: opts.existingTaskId ? `
|
|
23979
|
+
|
|
23980
|
+
_Note: There's an active task (${opts.existingTaskId}) for this work item._` : ""
|
|
23981
|
+
} : {
|
|
23982
|
+
...common,
|
|
23983
|
+
trigger: TRIGGER_LABEL[trigger],
|
|
23984
|
+
description_section: description ? `Description:
|
|
23985
|
+
${description}
|
|
23986
|
+
|
|
23987
|
+
` : ""
|
|
23988
|
+
};
|
|
23989
|
+
const result = resolveTemplate(templateName, variables);
|
|
23990
|
+
if (result.skipped)
|
|
23991
|
+
return null;
|
|
23992
|
+
const lead = await findLeadAgent2();
|
|
23993
|
+
const task = await createTaskWithSiblingAwareness(result.text, {
|
|
23994
|
+
agentId: lead?.id ?? null,
|
|
23995
|
+
routingReason: lead ? "skill" : undefined,
|
|
23996
|
+
routingSource: lead ? "engine_default" : undefined,
|
|
23997
|
+
source: "azure-devops",
|
|
23998
|
+
taskType: "azure-devops-work-item",
|
|
23999
|
+
requestedByUserId: opts.requestedByUserId,
|
|
24000
|
+
parentTaskId: opts.parentTaskId,
|
|
24001
|
+
contextKey: azureDevOpsWorkItemContextKey({
|
|
24002
|
+
organization: organizationOf(ctx.org.url),
|
|
24003
|
+
workItemId: ctx.workItemId
|
|
24004
|
+
})
|
|
24005
|
+
}, { origin: "webhook" });
|
|
24006
|
+
return task.id;
|
|
24007
|
+
}
|
|
24008
|
+
async function authorizeActor(ctx, trigger) {
|
|
24009
|
+
const userId = ctx.actor ? await authorizeAzureDevOpsActor(ctx.actor, "work_item", `${trigger} #${ctx.workItemId}`) : null;
|
|
24010
|
+
if (!userId) {
|
|
24011
|
+
console.warn(`[AzureDevOps] Ignoring ${TRIGGER_LABEL[trigger]} on work item #${ctx.workItemId}: actor is not a known swarm user`);
|
|
24012
|
+
}
|
|
24013
|
+
return userId;
|
|
24014
|
+
}
|
|
24015
|
+
async function routeToTask(ctx, trigger, commentText) {
|
|
24016
|
+
const requestedByUserId = await authorizeActor(ctx, trigger);
|
|
24017
|
+
if (!requestedByUserId)
|
|
24018
|
+
return { created: false };
|
|
24019
|
+
const externalId = trackerKey(ctx.org, ctx.workItemId);
|
|
24020
|
+
const claim = await createTrackerSyncIfAbsent({
|
|
24021
|
+
provider: PROVIDER,
|
|
24022
|
+
entityType: "task",
|
|
24023
|
+
providerEntityType: "WorkItem",
|
|
24024
|
+
swarmId: `pending:${PROVIDER}:${externalId}`,
|
|
24025
|
+
externalId,
|
|
24026
|
+
externalIdentifier: `${ctx.fields["System.TeamProject"] ?? "unknown"}#${ctx.workItemId}`,
|
|
24027
|
+
externalUrl: workItemUrl(ctx),
|
|
24028
|
+
lastSyncOrigin: "external",
|
|
24029
|
+
syncDirection: "inbound"
|
|
24030
|
+
});
|
|
24031
|
+
const mentioned = trigger === "mentioned_comment" || trigger === "mentioned_description";
|
|
24032
|
+
if (claim.inserted) {
|
|
24033
|
+
let taskId2 = null;
|
|
24034
|
+
try {
|
|
24035
|
+
taskId2 = await createWorkItemTask(ctx, trigger, {
|
|
24036
|
+
followup: false,
|
|
24037
|
+
requestedByUserId,
|
|
24038
|
+
commentText
|
|
24039
|
+
});
|
|
24040
|
+
if (taskId2) {
|
|
24041
|
+
await updateTrackerSyncSwarmId(claim.sync.id, taskId2);
|
|
24042
|
+
await markTrackerDelivery(PROVIDER, "task", externalId, ctx.eventId);
|
|
24043
|
+
}
|
|
24044
|
+
} finally {
|
|
24045
|
+
if (!taskId2)
|
|
24046
|
+
await deleteTrackerSync(claim.sync.id);
|
|
24047
|
+
}
|
|
24048
|
+
return taskId2 ? { created: true, taskId: taskId2 } : { created: false };
|
|
24049
|
+
}
|
|
24050
|
+
const prior = claim.sync.swarmId && !claim.sync.swarmId.startsWith("pending:") ? await getTaskById(claim.sync.swarmId) : null;
|
|
24051
|
+
if (prior && !isTerminalTaskStatus(prior.status)) {
|
|
24052
|
+
if (!mentioned) {
|
|
24053
|
+
console.log(`[AzureDevOps] Work item #${ctx.workItemId} re-triggered but task ${prior.id} is ${prior.status}, ignoring`);
|
|
24054
|
+
return { created: false };
|
|
24055
|
+
}
|
|
24056
|
+
const taskId2 = await createWorkItemTask(ctx, trigger, {
|
|
24057
|
+
followup: false,
|
|
24058
|
+
requestedByUserId,
|
|
24059
|
+
commentText,
|
|
24060
|
+
parentTaskId: prior.id,
|
|
24061
|
+
existingTaskId: prior.id
|
|
24062
|
+
});
|
|
24063
|
+
if (taskId2)
|
|
24064
|
+
await markTrackerDelivery(PROVIDER, "task", externalId, ctx.eventId);
|
|
24065
|
+
return taskId2 ? { created: true, taskId: taskId2 } : { created: false };
|
|
24066
|
+
}
|
|
24067
|
+
const taskId = await createWorkItemTask(ctx, trigger, {
|
|
24068
|
+
followup: true,
|
|
24069
|
+
requestedByUserId,
|
|
24070
|
+
commentText
|
|
24071
|
+
});
|
|
24072
|
+
if (!taskId)
|
|
24073
|
+
return { created: false };
|
|
24074
|
+
await updateTrackerSyncSwarmId(claim.sync.id, taskId);
|
|
24075
|
+
await markTrackerDelivery(PROVIDER, "task", externalId, ctx.eventId);
|
|
24076
|
+
return { created: true, taskId };
|
|
24077
|
+
}
|
|
24078
|
+
async function triggerFromFields(fields, bot) {
|
|
24079
|
+
if (isBotString(fields["System.AssignedTo"], bot))
|
|
24080
|
+
return "assigned";
|
|
24081
|
+
return inspectMention(fields["System.Description"], bot).mentioned ? "mentioned_description" : null;
|
|
24082
|
+
}
|
|
24083
|
+
async function handleWorkItemCreated(event) {
|
|
24084
|
+
const item = workItemOf(event);
|
|
24085
|
+
if (!item) {
|
|
24086
|
+
warnMalformed(event);
|
|
24087
|
+
return { created: false };
|
|
24088
|
+
}
|
|
24089
|
+
const org = orgFor(event);
|
|
24090
|
+
if (!org)
|
|
24091
|
+
return dropUntrustedOrg(event.eventType);
|
|
24092
|
+
return await handleDelivery(org, event.id, item.id, async () => {
|
|
24093
|
+
const bot = await resolveBot(org.url);
|
|
24094
|
+
const actor = parseIdentityString(item.fields["System.ChangedBy"]);
|
|
24095
|
+
if (isBotIdentity(actor, bot))
|
|
24096
|
+
return { created: false };
|
|
24097
|
+
const trigger = await triggerFromFields(item.fields, bot);
|
|
24098
|
+
if (!trigger)
|
|
24099
|
+
return { created: false };
|
|
24100
|
+
return await routeToTask({
|
|
24101
|
+
eventId: event.id,
|
|
24102
|
+
eventType: event.eventType,
|
|
24103
|
+
workItemId: item.id,
|
|
24104
|
+
fields: item.fields,
|
|
24105
|
+
org,
|
|
24106
|
+
actor
|
|
24107
|
+
}, trigger);
|
|
24108
|
+
});
|
|
24109
|
+
}
|
|
24110
|
+
async function handleWorkItemUpdated(event) {
|
|
24111
|
+
const update = updatedPayloadOf(event);
|
|
24112
|
+
if (!update) {
|
|
24113
|
+
warnMalformed(event);
|
|
24114
|
+
return { created: false };
|
|
24115
|
+
}
|
|
24116
|
+
const change = update.fields?.["System.AssignedTo"];
|
|
24117
|
+
if (!change)
|
|
24118
|
+
return { created: false };
|
|
24119
|
+
const org = orgFor(event);
|
|
24120
|
+
if (!org)
|
|
24121
|
+
return dropUntrustedOrg(event.eventType);
|
|
24122
|
+
return await handleDelivery(org, event.id, update.workItemId, async () => {
|
|
24123
|
+
const bot = await resolveBot(org.url);
|
|
24124
|
+
if (!isBotString(change.newValue, bot) || isBotString(change.oldValue, bot)) {
|
|
24125
|
+
return { created: false };
|
|
24126
|
+
}
|
|
24127
|
+
if (isBotIdentity(update.revisedBy ?? null, bot))
|
|
24128
|
+
return { created: false };
|
|
24129
|
+
return await routeToTask({
|
|
24130
|
+
eventId: event.id,
|
|
24131
|
+
eventType: event.eventType,
|
|
24132
|
+
workItemId: update.workItemId,
|
|
24133
|
+
fields: update.revision.fields,
|
|
24134
|
+
org,
|
|
24135
|
+
actor: update.revisedBy ?? null
|
|
24136
|
+
}, "assigned");
|
|
24137
|
+
});
|
|
24138
|
+
}
|
|
24139
|
+
async function handleWorkItemCommented(event) {
|
|
24140
|
+
const item = workItemOf(event);
|
|
24141
|
+
if (!item) {
|
|
24142
|
+
warnMalformed(event);
|
|
24143
|
+
return { created: false };
|
|
24144
|
+
}
|
|
24145
|
+
const ref = item.commentVersionRef;
|
|
24146
|
+
if (ref && ref.version > 1)
|
|
24147
|
+
return { created: false };
|
|
24148
|
+
const org = orgFor(event);
|
|
24149
|
+
if (!org)
|
|
24150
|
+
return dropUntrustedOrg(event.eventType);
|
|
24151
|
+
const commentKey = ref ? `comment:${trackerKey(org, item.id)}:${ref.commentId}` : undefined;
|
|
24152
|
+
return await handleDelivery(org, event.id, item.id, async () => {
|
|
24153
|
+
const bot = await resolveBot(org.url);
|
|
24154
|
+
const actor = parseIdentityString(item.fields["System.ChangedBy"]);
|
|
24155
|
+
if (isBotIdentity(actor, bot))
|
|
24156
|
+
return { created: false };
|
|
24157
|
+
const { mentioned, text } = inspectMention(item.fields["System.History"], bot);
|
|
24158
|
+
if (!mentioned)
|
|
24159
|
+
return { created: false };
|
|
24160
|
+
return await routeToTask({
|
|
24161
|
+
eventId: event.id,
|
|
24162
|
+
eventType: event.eventType,
|
|
24163
|
+
workItemId: item.id,
|
|
24164
|
+
fields: item.fields,
|
|
24165
|
+
org,
|
|
24166
|
+
actor
|
|
24167
|
+
}, "mentioned_comment", text);
|
|
24168
|
+
}, commentKey);
|
|
24169
|
+
}
|
|
24170
|
+
async function handleWorkItemDeleted(event) {
|
|
24171
|
+
const item = workItemOf(event);
|
|
24172
|
+
if (!item) {
|
|
24173
|
+
warnMalformed(event);
|
|
24174
|
+
return { created: false };
|
|
24175
|
+
}
|
|
24176
|
+
const org = orgFor(event);
|
|
24177
|
+
if (!org)
|
|
24178
|
+
return dropUntrustedOrg(event.eventType);
|
|
24179
|
+
return await handleDelivery(org, event.id, item.id, async () => {
|
|
24180
|
+
const externalId = trackerKey(org, item.id);
|
|
24181
|
+
const sync = await getTrackerSyncByExternalId(PROVIDER, "task", externalId);
|
|
24182
|
+
if (!sync)
|
|
24183
|
+
return { created: false };
|
|
24184
|
+
const task = sync.swarmId && !sync.swarmId.startsWith("pending:") ? await getTaskById(sync.swarmId) : null;
|
|
24185
|
+
if (task && !isTerminalTaskStatus(task.status)) {
|
|
24186
|
+
await cancelTask(task.id, DELETED_REASON);
|
|
24187
|
+
console.log(`[AzureDevOps] Cancelled task ${task.id} (work item #${item.id} deleted)`);
|
|
24188
|
+
}
|
|
24189
|
+
await markTrackerDelivery(PROVIDER, "task", externalId, event.id);
|
|
24190
|
+
return { created: false };
|
|
24191
|
+
});
|
|
24192
|
+
}
|
|
24193
|
+
async function handleWorkItemRestored(event) {
|
|
24194
|
+
const item = workItemOf(event);
|
|
24195
|
+
if (!item) {
|
|
24196
|
+
warnMalformed(event);
|
|
24197
|
+
return { created: false };
|
|
24198
|
+
}
|
|
24199
|
+
const org = orgFor(event);
|
|
24200
|
+
if (!org)
|
|
24201
|
+
return dropUntrustedOrg(event.eventType);
|
|
24202
|
+
return await handleDelivery(org, event.id, item.id, async () => {
|
|
24203
|
+
const bot = await resolveBot(org.url);
|
|
24204
|
+
const actor = parseIdentityString(item.fields["System.ChangedBy"]);
|
|
24205
|
+
const ctx = {
|
|
24206
|
+
eventId: event.id,
|
|
24207
|
+
eventType: event.eventType,
|
|
24208
|
+
workItemId: item.id,
|
|
24209
|
+
fields: item.fields,
|
|
24210
|
+
org,
|
|
24211
|
+
actor
|
|
24212
|
+
};
|
|
24213
|
+
const externalId = trackerKey(org, item.id);
|
|
24214
|
+
const sync = await getTrackerSyncByExternalId(PROVIDER, "task", externalId);
|
|
24215
|
+
if (!sync) {
|
|
24216
|
+
if (isBotIdentity(actor, bot))
|
|
24217
|
+
return { created: false };
|
|
24218
|
+
const trigger = await triggerFromFields(item.fields, bot);
|
|
24219
|
+
return trigger ? await routeToTask(ctx, trigger) : { created: false };
|
|
24220
|
+
}
|
|
24221
|
+
const task = sync.swarmId && !sync.swarmId.startsWith("pending:") ? await getTaskById(sync.swarmId) : null;
|
|
24222
|
+
const cancelledByDelete = task?.status === "cancelled" && task.failureReason === DELETED_REASON;
|
|
24223
|
+
if (!cancelledByDelete || !isBotString(item.fields["System.AssignedTo"], bot)) {
|
|
24224
|
+
await markTrackerDelivery(PROVIDER, "task", externalId, event.id);
|
|
24225
|
+
return { created: false };
|
|
24226
|
+
}
|
|
24227
|
+
const requestedByUserId = await authorizeActor(ctx, "restored");
|
|
24228
|
+
if (!requestedByUserId)
|
|
24229
|
+
return { created: false };
|
|
24230
|
+
const taskId = await createWorkItemTask(ctx, "restored", { followup: true, requestedByUserId });
|
|
24231
|
+
if (!taskId)
|
|
24232
|
+
return { created: false };
|
|
24233
|
+
await updateTrackerSyncSwarmId(sync.id, taskId);
|
|
24234
|
+
await markTrackerDelivery(PROVIDER, "task", externalId, event.id);
|
|
24235
|
+
return { created: true, taskId };
|
|
24236
|
+
});
|
|
24237
|
+
}
|
|
23472
24238
|
// src/http/core.ts
|
|
23473
24239
|
init_db3();
|
|
23474
24240
|
|
|
@@ -25858,6 +26624,7 @@ async function handlePipeline(event) {
|
|
|
25858
26624
|
return { created: true, taskId: task.id };
|
|
25859
26625
|
}
|
|
25860
26626
|
// src/jira/adf.ts
|
|
26627
|
+
init_node_env();
|
|
25861
26628
|
function isNode(value) {
|
|
25862
26629
|
return !!value && typeof value === "object";
|
|
25863
26630
|
}
|
|
@@ -25924,7 +26691,7 @@ function extractText(adf) {
|
|
|
25924
26691
|
return;
|
|
25925
26692
|
}
|
|
25926
26693
|
default: {
|
|
25927
|
-
if (type) {
|
|
26694
|
+
if (getNodeEnv() !== "production" && type) {
|
|
25928
26695
|
console.log(`[jira.adf] unknown node type: ${type}`);
|
|
25929
26696
|
}
|
|
25930
26697
|
for (const child of asNodeArray(node.content))
|
|
@@ -25958,148 +26725,6 @@ init_oauth();
|
|
|
25958
26725
|
init_constants();
|
|
25959
26726
|
init_env_flag();
|
|
25960
26727
|
|
|
25961
|
-
// src/be/db-queries/tracker.ts
|
|
25962
|
-
init_date_utils();
|
|
25963
|
-
init_db3();
|
|
25964
|
-
function normalizeTrackerSync(row) {
|
|
25965
|
-
return {
|
|
25966
|
-
...row,
|
|
25967
|
-
lastSyncedAt: normalizeDateRequired(row.lastSyncedAt),
|
|
25968
|
-
createdAt: normalizeDateRequired(row.createdAt)
|
|
25969
|
-
};
|
|
25970
|
-
}
|
|
25971
|
-
function normalizeTrackerAgentMapping(row) {
|
|
25972
|
-
return {
|
|
25973
|
-
...row,
|
|
25974
|
-
createdAt: normalizeDateRequired(row.createdAt)
|
|
25975
|
-
};
|
|
25976
|
-
}
|
|
25977
|
-
async function getTrackerSync(provider, entityType, swarmId) {
|
|
25978
|
-
const row = await getDbClient().get("SELECT * FROM tracker_sync WHERE provider = ? AND entityType = ? AND swarmId = ?", [provider, entityType, swarmId]);
|
|
25979
|
-
return row ? normalizeTrackerSync(row) : null;
|
|
25980
|
-
}
|
|
25981
|
-
async function getTrackerSyncByExternalId(provider, entityType, externalId) {
|
|
25982
|
-
const row = await getDbClient().get("SELECT * FROM tracker_sync WHERE provider = ? AND entityType = ? AND externalId = ?", [provider, entityType, externalId]);
|
|
25983
|
-
return row ? normalizeTrackerSync(row) : null;
|
|
25984
|
-
}
|
|
25985
|
-
async function createTrackerSyncIfAbsent(data) {
|
|
25986
|
-
const insertResult = await getDbClient().get(`INSERT INTO tracker_sync (provider, entityType, providerEntityType, swarmId, externalId, externalIdentifier, externalUrl, lastSyncOrigin, lastDeliveryId, syncDirection)
|
|
25987
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
25988
|
-
ON CONFLICT (provider, entityType, externalId) DO NOTHING
|
|
25989
|
-
RETURNING *`, [
|
|
25990
|
-
data.provider,
|
|
25991
|
-
data.entityType,
|
|
25992
|
-
data.providerEntityType ?? null,
|
|
25993
|
-
data.swarmId,
|
|
25994
|
-
data.externalId,
|
|
25995
|
-
data.externalIdentifier ?? null,
|
|
25996
|
-
data.externalUrl ?? null,
|
|
25997
|
-
data.lastSyncOrigin ?? null,
|
|
25998
|
-
data.lastDeliveryId ?? null,
|
|
25999
|
-
data.syncDirection ?? "inbound"
|
|
26000
|
-
]);
|
|
26001
|
-
if (insertResult) {
|
|
26002
|
-
return { inserted: true, sync: normalizeTrackerSync(insertResult) };
|
|
26003
|
-
}
|
|
26004
|
-
const existing = await getTrackerSyncByExternalId(data.provider, data.entityType, data.externalId);
|
|
26005
|
-
if (!existing) {
|
|
26006
|
-
throw new Error(`[tracker] createTrackerSyncIfAbsent: ON CONFLICT fired but no existing row found for (${data.provider}, ${data.entityType}, ${data.externalId})`);
|
|
26007
|
-
}
|
|
26008
|
-
return { inserted: false, sync: existing };
|
|
26009
|
-
}
|
|
26010
|
-
async function updateTrackerSyncSwarmId(id, swarmId) {
|
|
26011
|
-
await getDbClient().run("UPDATE tracker_sync SET swarmId = ? WHERE id = ?", [swarmId, id]);
|
|
26012
|
-
}
|
|
26013
|
-
async function repointTrackerSyncBySwarmId(oldSwarmId, newSwarmId) {
|
|
26014
|
-
const result = await getDbClient().run("UPDATE tracker_sync SET swarmId = ? WHERE swarmId = ?", [
|
|
26015
|
-
newSwarmId,
|
|
26016
|
-
oldSwarmId
|
|
26017
|
-
]);
|
|
26018
|
-
return Number(result.changes ?? 0);
|
|
26019
|
-
}
|
|
26020
|
-
async function createTrackerSync(data) {
|
|
26021
|
-
const result = await getDbClient().get(`INSERT INTO tracker_sync (provider, entityType, providerEntityType, swarmId, externalId, externalIdentifier, externalUrl, lastSyncOrigin, lastDeliveryId, syncDirection)
|
|
26022
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
26023
|
-
RETURNING *`, [
|
|
26024
|
-
data.provider,
|
|
26025
|
-
data.entityType,
|
|
26026
|
-
data.providerEntityType ?? null,
|
|
26027
|
-
data.swarmId,
|
|
26028
|
-
data.externalId,
|
|
26029
|
-
data.externalIdentifier ?? null,
|
|
26030
|
-
data.externalUrl ?? null,
|
|
26031
|
-
data.lastSyncOrigin ?? null,
|
|
26032
|
-
data.lastDeliveryId ?? null,
|
|
26033
|
-
data.syncDirection ?? "inbound"
|
|
26034
|
-
]);
|
|
26035
|
-
return normalizeTrackerSync(result);
|
|
26036
|
-
}
|
|
26037
|
-
async function updateTrackerSync(id, data) {
|
|
26038
|
-
const sets = [];
|
|
26039
|
-
const values = [];
|
|
26040
|
-
if (data.lastSyncedAt !== undefined) {
|
|
26041
|
-
sets.push("lastSyncedAt = ?");
|
|
26042
|
-
values.push(data.lastSyncedAt);
|
|
26043
|
-
}
|
|
26044
|
-
if (data.lastSyncOrigin !== undefined) {
|
|
26045
|
-
sets.push("lastSyncOrigin = ?");
|
|
26046
|
-
values.push(data.lastSyncOrigin);
|
|
26047
|
-
}
|
|
26048
|
-
if (data.lastDeliveryId !== undefined) {
|
|
26049
|
-
sets.push("lastDeliveryId = ?");
|
|
26050
|
-
values.push(data.lastDeliveryId);
|
|
26051
|
-
}
|
|
26052
|
-
if (data.syncDirection !== undefined) {
|
|
26053
|
-
sets.push("syncDirection = ?");
|
|
26054
|
-
values.push(data.syncDirection);
|
|
26055
|
-
}
|
|
26056
|
-
if (data.externalUrl !== undefined) {
|
|
26057
|
-
sets.push("externalUrl = ?");
|
|
26058
|
-
values.push(data.externalUrl);
|
|
26059
|
-
}
|
|
26060
|
-
if (data.externalIdentifier !== undefined) {
|
|
26061
|
-
sets.push("externalIdentifier = ?");
|
|
26062
|
-
values.push(data.externalIdentifier);
|
|
26063
|
-
}
|
|
26064
|
-
if (sets.length === 0)
|
|
26065
|
-
return;
|
|
26066
|
-
values.push(id);
|
|
26067
|
-
await getDbClient().run(`UPDATE tracker_sync SET ${sets.join(", ")} WHERE id = ?`, values);
|
|
26068
|
-
}
|
|
26069
|
-
async function deleteTrackerSync(id) {
|
|
26070
|
-
await getDbClient().run("DELETE FROM tracker_sync WHERE id = ?", [id]);
|
|
26071
|
-
}
|
|
26072
|
-
async function hasTrackerDelivery(provider, deliveryId) {
|
|
26073
|
-
if (!deliveryId)
|
|
26074
|
-
return false;
|
|
26075
|
-
const row = await getDbClient().get("SELECT 1 AS hit FROM tracker_sync WHERE provider = ? AND lastDeliveryId = ? LIMIT 1", [provider, deliveryId]);
|
|
26076
|
-
return !!row;
|
|
26077
|
-
}
|
|
26078
|
-
async function markTrackerDelivery(provider, entityType, externalId, deliveryId) {
|
|
26079
|
-
await getDbClient().run("UPDATE tracker_sync SET lastDeliveryId = ? WHERE provider = ? AND entityType = ? AND externalId = ?", [deliveryId, provider, entityType, externalId]);
|
|
26080
|
-
}
|
|
26081
|
-
async function getAllTrackerSyncs(provider, entityType) {
|
|
26082
|
-
const conditions = [];
|
|
26083
|
-
const values = [];
|
|
26084
|
-
if (provider) {
|
|
26085
|
-
conditions.push("provider = ?");
|
|
26086
|
-
values.push(provider);
|
|
26087
|
-
}
|
|
26088
|
-
if (entityType) {
|
|
26089
|
-
conditions.push("entityType = ?");
|
|
26090
|
-
values.push(entityType);
|
|
26091
|
-
}
|
|
26092
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
26093
|
-
const rows = await getDbClient().query(`SELECT * FROM tracker_sync ${where} ORDER BY createdAt DESC`, values);
|
|
26094
|
-
return rows.map(normalizeTrackerSync);
|
|
26095
|
-
}
|
|
26096
|
-
async function createTrackerAgentMapping(data) {
|
|
26097
|
-
const result = await getDbClient().get(`INSERT INTO tracker_agent_mapping (provider, agentId, externalUserId, agentName)
|
|
26098
|
-
VALUES (?, ?, ?, ?)
|
|
26099
|
-
RETURNING *`, [data.provider, data.agentId, data.externalUserId, data.agentName]);
|
|
26100
|
-
return normalizeTrackerAgentMapping(result);
|
|
26101
|
-
}
|
|
26102
|
-
|
|
26103
26728
|
// src/jira/outbound.ts
|
|
26104
26729
|
init_secret_scrubber();
|
|
26105
26730
|
init_event_bus();
|
|
@@ -26411,7 +27036,7 @@ var TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
|
26411
27036
|
function namespace(kind) {
|
|
26412
27037
|
return `integration:unmapped:${kind}`;
|
|
26413
27038
|
}
|
|
26414
|
-
async function
|
|
27039
|
+
async function recordUnmappedIdentity2(kind, externalId, meta2) {
|
|
26415
27040
|
const ns = namespace(kind);
|
|
26416
27041
|
const now = new Date().toISOString();
|
|
26417
27042
|
const expiresAt = Date.now() + TTL_MS;
|
|
@@ -26462,7 +27087,7 @@ async function resolveJiraActor(accountId, email, name, sampleEventType, sampleC
|
|
|
26462
27087
|
}
|
|
26463
27088
|
return user.id;
|
|
26464
27089
|
}
|
|
26465
|
-
await
|
|
27090
|
+
await recordUnmappedIdentity2("jira", accountId, { sampleEventType, sampleContext });
|
|
26466
27091
|
return;
|
|
26467
27092
|
}
|
|
26468
27093
|
async function renderJiraIdentity(accountId) {
|
|
@@ -26826,7 +27451,7 @@ async function notifySlack(text) {
|
|
|
26826
27451
|
return;
|
|
26827
27452
|
}
|
|
26828
27453
|
try {
|
|
26829
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-
|
|
27454
|
+
const { getSlackApp: getSlackApp2 } = await import("./app-km9qnv2f.js");
|
|
26830
27455
|
const app = getSlackApp2();
|
|
26831
27456
|
if (!app) {
|
|
26832
27457
|
console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
|
|
@@ -26897,7 +27522,7 @@ async function deleteJiraWebhook(webhookId) {
|
|
|
26897
27522
|
console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
|
|
26898
27523
|
}
|
|
26899
27524
|
async function overwriteWebhookIds(next) {
|
|
26900
|
-
const { getDbClient: getDbClient2 } = await import("./db-
|
|
27525
|
+
const { getDbClient: getDbClient2 } = await import("./db-qfpq4bt7.js");
|
|
26901
27526
|
await getDbClient2().transaction(async (tx) => {
|
|
26902
27527
|
const row = await tx.get("SELECT metadata FROM oauth_apps WHERE provider = 'jira'");
|
|
26903
27528
|
if (!row) {
|
|
@@ -100117,6 +100742,7 @@ init_zod_openapi();
|
|
|
100117
100742
|
|
|
100118
100743
|
// src/http/route-def.ts
|
|
100119
100744
|
init_zod();
|
|
100745
|
+
init_node_env();
|
|
100120
100746
|
init_secret_scrubber();
|
|
100121
100747
|
|
|
100122
100748
|
// src/http/utils.ts
|
|
@@ -100514,7 +101140,7 @@ function describeRequestRoute(method, pathSegments, hasQueryString = false, hasT
|
|
|
100514
101140
|
const first = pathSegments[0];
|
|
100515
101141
|
return { spanName: first ? `${m} /${first}` : m };
|
|
100516
101142
|
}
|
|
100517
|
-
var VALIDATE_RESPONSES = true;
|
|
101143
|
+
var VALIDATE_RESPONSES = getNodeEnv() === "test" || getNodeEnv() === "development" || process.env.VALIDATE_HTTP_RESPONSES === "true";
|
|
100518
101144
|
function route(def) {
|
|
100519
101145
|
routeRegistry.push(def);
|
|
100520
101146
|
return {
|
|
@@ -100526,7 +101152,9 @@ function route(def) {
|
|
|
100526
101152
|
const result = schema.safeParse(data);
|
|
100527
101153
|
if (!result.success) {
|
|
100528
101154
|
const detail = `Response schema violation: ${def.method.toUpperCase()} ${def.path} ${code} — ` + result.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`).join("; ");
|
|
100529
|
-
if (
|
|
101155
|
+
if (getNodeEnv() === "test") {
|
|
101156
|
+
throw new Error(detail);
|
|
101157
|
+
}
|
|
100530
101158
|
console.error(`[route-def] ${scrubSecrets(detail)}`);
|
|
100531
101159
|
}
|
|
100532
101160
|
}
|
|
@@ -100727,7 +101355,7 @@ async function reloadGlobalConfigsAndIntegrations() {
|
|
|
100727
101355
|
return runWithoutRequestAuth(() => reloadGlobalConfigsAndIntegrationsInner());
|
|
100728
101356
|
}
|
|
100729
101357
|
async function reloadGlobalConfigsAndIntegrationsInner() {
|
|
100730
|
-
const { getEmbeddingProvider: getEmbeddingProvider2, resetEmbeddingProvider } = await import("./index-
|
|
101358
|
+
const { getEmbeddingProvider: getEmbeddingProvider2, resetEmbeddingProvider } = await import("./index-jd7sejm2.js");
|
|
100731
101359
|
const wasEmbeddingsConfigured = getEmbeddingProvider2().isConfigured();
|
|
100732
101360
|
const updated = await loadGlobalConfigsIntoEnv(true);
|
|
100733
101361
|
await syncTelemetryEnabled("settings").catch((err) => {
|
|
@@ -100743,10 +101371,10 @@ async function reloadGlobalConfigsAndIntegrationsInner() {
|
|
|
100743
101371
|
integrations.push("embeddings");
|
|
100744
101372
|
}
|
|
100745
101373
|
if (!wasEmbeddingsConfigured && isEmbeddingsConfigured) {
|
|
100746
|
-
import("./boot-reembed-
|
|
101374
|
+
import("./boot-reembed-swwzdgd7.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
100747
101375
|
console.error("[config-reload] memory backfill failed (non-fatal):", err);
|
|
100748
101376
|
});
|
|
100749
|
-
import("./boot-reembed-
|
|
101377
|
+
import("./boot-reembed-16rv9843.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
100750
101378
|
console.error("[config-reload] script backfill failed (non-fatal):", err);
|
|
100751
101379
|
});
|
|
100752
101380
|
}
|
|
@@ -101926,7 +102554,7 @@ function setPreloadedTools(server, names) {
|
|
|
101926
102554
|
async function applyPreToolCall(name, args, requestInfo, inputSchema) {
|
|
101927
102555
|
if (requestInfo.callOrigin !== "mcp")
|
|
101928
102556
|
return { args };
|
|
101929
|
-
const dispatcher = await import("./dispatcher-
|
|
102557
|
+
const dispatcher = await import("./dispatcher-x0x7qfcq.js");
|
|
101930
102558
|
const result = await dispatcher.dispatchPre("pre.tool.call", { tool: name, args, requestInfo }, inputSchema ? {
|
|
101931
102559
|
validateModify: async (data) => {
|
|
101932
102560
|
const schema = normalizeObjectSchema(inputSchema) ?? inputSchema;
|
|
@@ -102968,7 +103596,7 @@ function reconcile(args) {
|
|
|
102968
103596
|
});
|
|
102969
103597
|
});
|
|
102970
103598
|
}
|
|
102971
|
-
var
|
|
103599
|
+
var inFlight2 = new Map;
|
|
102972
103600
|
function passBase(args) {
|
|
102973
103601
|
return {
|
|
102974
103602
|
model: args.model,
|
|
@@ -103047,7 +103675,7 @@ async function executePass(args) {
|
|
|
103047
103675
|
}
|
|
103048
103676
|
async function runPass(args) {
|
|
103049
103677
|
const key = `${args.appId}:${args.model}:${args.sourceName}`;
|
|
103050
|
-
if (
|
|
103678
|
+
if (inFlight2.has(key)) {
|
|
103051
103679
|
return scrubObject({
|
|
103052
103680
|
...passBase({
|
|
103053
103681
|
model: args.model,
|
|
@@ -103061,12 +103689,12 @@ async function runPass(args) {
|
|
|
103061
103689
|
});
|
|
103062
103690
|
}
|
|
103063
103691
|
const pass = executePass(args);
|
|
103064
|
-
|
|
103692
|
+
inFlight2.set(key, pass);
|
|
103065
103693
|
try {
|
|
103066
103694
|
return await pass;
|
|
103067
103695
|
} finally {
|
|
103068
|
-
if (
|
|
103069
|
-
|
|
103696
|
+
if (inFlight2.get(key) === pass)
|
|
103697
|
+
inFlight2.delete(key);
|
|
103070
103698
|
}
|
|
103071
103699
|
}
|
|
103072
103700
|
function noPairIssue(input) {
|
|
@@ -109349,6 +109977,17 @@ var registerDeferTaskTool = (server) => {
|
|
|
109349
109977
|
const { tz: requesterTz, isFallback: isFallbackTz } = await resolveRequesterTimezone(task.requestedByUserId);
|
|
109350
109978
|
try {
|
|
109351
109979
|
const committed = await getDbClient().transaction(async () => {
|
|
109980
|
+
if (watchedTaskIds.length) {
|
|
109981
|
+
const existing = await getDbClient().get(`SELECT w.scheduleId, s.parentTaskId FROM deferred_task_waits w
|
|
109982
|
+
JOIN scheduled_tasks s ON s.id = w.scheduleId
|
|
109983
|
+
JOIN deferred_task_wait_members m ON m.scheduleId = w.scheduleId
|
|
109984
|
+
WHERE w.status = 'pending' AND s.enabled = 1 AND s.targetAgentId = ?
|
|
109985
|
+
AND m.taskId IN (${watchedTaskIds.map(() => "?").join(", ")})
|
|
109986
|
+
LIMIT 1`, [agent.id, ...watchedTaskIds]);
|
|
109987
|
+
if (existing) {
|
|
109988
|
+
throw new DeferAbortedError(`Pending watcher ${existing.scheduleId} (task ${existing.parentTaskId}) already watches these tasks for this agent.`);
|
|
109989
|
+
}
|
|
109990
|
+
}
|
|
109352
109991
|
const watchedAgentNames = [];
|
|
109353
109992
|
for (const watchedId of watchedTaskIds) {
|
|
109354
109993
|
if (watchedId === taskId)
|
|
@@ -110501,7 +111140,8 @@ init_db3();
|
|
|
110501
111140
|
var RESERVED_FAMILIES = new Map([
|
|
110502
111141
|
["apps", "namespace is reserved for swarm apps; use the app row endpoints"],
|
|
110503
111142
|
["comb", "namespace is reserved for Comb; use POST /api/comb/review-batches"],
|
|
110504
|
-
["worker", "namespace is reserved for worker nudges"]
|
|
111143
|
+
["worker", "namespace is reserved for worker nudges"],
|
|
111144
|
+
["slack-external-alert", "namespace is reserved for Slack external channel alerts"]
|
|
110505
111145
|
]);
|
|
110506
111146
|
function reservedNamespaceError(namespace2) {
|
|
110507
111147
|
return RESERVED_FAMILIES.get(namespace2.split(":", 1)[0] ?? "") ?? null;
|
|
@@ -124910,57 +125550,6 @@ init_zod();
|
|
|
124910
125550
|
init_db3();
|
|
124911
125551
|
init_rbac();
|
|
124912
125552
|
|
|
124913
|
-
// src/slack/channel-join.ts
|
|
124914
|
-
var logger = console;
|
|
124915
|
-
function slackErrorData(error) {
|
|
124916
|
-
if (!(error instanceof Error))
|
|
124917
|
-
return;
|
|
124918
|
-
return error.data;
|
|
124919
|
-
}
|
|
124920
|
-
function slackCode(error) {
|
|
124921
|
-
const d = slackErrorData(error);
|
|
124922
|
-
return typeof d?.error === "string" ? d.error : undefined;
|
|
124923
|
-
}
|
|
124924
|
-
function slackMissingScopeMessage(error) {
|
|
124925
|
-
const data = slackErrorData(error);
|
|
124926
|
-
if (data?.error !== "missing_scope" || typeof data.needed !== "string")
|
|
124927
|
-
return;
|
|
124928
|
-
const needed = data.needed.trim();
|
|
124929
|
-
if (!needed)
|
|
124930
|
-
return;
|
|
124931
|
-
return `Slack requires the \`${needed}\` scope. Add it to slack-manifest.json, apply the updated manifest to the Slack app, then reinstall the app to the workspace for the change to take effect.`;
|
|
124932
|
-
}
|
|
124933
|
-
async function isKnownExternalChannel(client, channelId) {
|
|
124934
|
-
try {
|
|
124935
|
-
const resp = await client.conversations.info({ channel: channelId });
|
|
124936
|
-
const ch = resp.channel ?? {};
|
|
124937
|
-
return ch.is_ext_shared === true || ch.is_pending_ext_shared === true;
|
|
124938
|
-
} catch (error) {
|
|
124939
|
-
logger.warn(`[Slack] conversations.info failed for ${channelId}; attempting join fallback:`, error);
|
|
124940
|
-
return false;
|
|
124941
|
-
}
|
|
124942
|
-
}
|
|
124943
|
-
async function withAutoJoin(client, channelId, fn) {
|
|
124944
|
-
try {
|
|
124945
|
-
return await fn();
|
|
124946
|
-
} catch (error) {
|
|
124947
|
-
if (slackCode(error) !== "not_in_channel")
|
|
124948
|
-
throw error;
|
|
124949
|
-
if (await isKnownExternalChannel(client, channelId)) {
|
|
124950
|
-
throw new Error(`Cannot auto-join external channel ${channelId} — invite the bot with /invite @<bot-name> first.`);
|
|
124951
|
-
}
|
|
124952
|
-
try {
|
|
124953
|
-
await client.conversations.join({ channel: channelId });
|
|
124954
|
-
} catch (joinError) {
|
|
124955
|
-
if (slackCode(joinError) === "method_not_supported_for_channel_type") {
|
|
124956
|
-
throw new Error(`Cannot access private channel ${channelId} — invite the bot with /invite @<bot-name> first.`);
|
|
124957
|
-
}
|
|
124958
|
-
throw joinError;
|
|
124959
|
-
}
|
|
124960
|
-
return await fn();
|
|
124961
|
-
}
|
|
124962
|
-
}
|
|
124963
|
-
|
|
124964
125553
|
// src/slack/channel-lifecycle.ts
|
|
124965
125554
|
function normalizeChannelName(name) {
|
|
124966
125555
|
const normalized = name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "").slice(0, 80).replace(/[-_]+$/g, "");
|
|
@@ -126024,6 +126613,10 @@ var registerSlackPostTool = (server) => {
|
|
|
126024
126613
|
if (!app) {
|
|
126025
126614
|
return toolErr("Slack not configured.");
|
|
126026
126615
|
}
|
|
126616
|
+
const blockReason = await getChannelBlockReason(app.client, channelId);
|
|
126617
|
+
if (blockReason) {
|
|
126618
|
+
return toolErr(externalChannelToolError(channelId, blockReason));
|
|
126619
|
+
}
|
|
126027
126620
|
try {
|
|
126028
126621
|
const slackMessage = markdownToSlack(message);
|
|
126029
126622
|
const sourceTask = requestInfo.sourceTaskId ? await getTaskById(requestInfo.sourceTaskId) : undefined;
|
|
@@ -126190,6 +126783,12 @@ var registerSlackReadTool = (server) => {
|
|
|
126190
126783
|
if (!app) {
|
|
126191
126784
|
return toolErr("Slack not configured.", { data: { messages: [] } });
|
|
126192
126785
|
}
|
|
126786
|
+
const blockReason = await getChannelBlockReason(app.client, slackChannelId);
|
|
126787
|
+
if (blockReason) {
|
|
126788
|
+
return toolErr(externalChannelToolError(slackChannelId, blockReason), {
|
|
126789
|
+
data: { messages: [] }
|
|
126790
|
+
});
|
|
126791
|
+
}
|
|
126193
126792
|
try {
|
|
126194
126793
|
const client = app.client;
|
|
126195
126794
|
let rawMessages = [];
|
|
@@ -126382,6 +126981,10 @@ var registerSlackReplyTool = (server) => {
|
|
|
126382
126981
|
if (!app) {
|
|
126383
126982
|
return toolErr("Slack not configured.");
|
|
126384
126983
|
}
|
|
126984
|
+
const blockReason = await getChannelBlockReason(app.client, slackChannelId);
|
|
126985
|
+
if (blockReason) {
|
|
126986
|
+
return toolErr(externalChannelToolError(slackChannelId, blockReason));
|
|
126987
|
+
}
|
|
126385
126988
|
try {
|
|
126386
126989
|
const citations = taskId ? await getTaskCitations(taskId) : [];
|
|
126387
126990
|
const slackMessage = renderTaskCitations(markdownToSlack(message), citations, "slack", false);
|
|
@@ -126499,6 +127102,10 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
126499
127102
|
if (!app) {
|
|
126500
127103
|
return toolErr("Slack not configured.");
|
|
126501
127104
|
}
|
|
127105
|
+
const blockReason = await getChannelBlockReason(app.client, channelId);
|
|
127106
|
+
if (blockReason) {
|
|
127107
|
+
return toolErr(externalChannelToolError(channelId, blockReason));
|
|
127108
|
+
}
|
|
126502
127109
|
try {
|
|
126503
127110
|
const slackMessage = markdownToSlack(message);
|
|
126504
127111
|
const messageBlocks = blocks ?? [
|
|
@@ -126586,6 +127193,10 @@ var registerSlackUpdateTool = (server) => {
|
|
|
126586
127193
|
if (!app) {
|
|
126587
127194
|
return toolErr("Slack not configured.");
|
|
126588
127195
|
}
|
|
127196
|
+
const blockReason = await getChannelBlockReason(app.client, channelId);
|
|
127197
|
+
if (blockReason) {
|
|
127198
|
+
return toolErr(externalChannelToolError(channelId, blockReason));
|
|
127199
|
+
}
|
|
126589
127200
|
try {
|
|
126590
127201
|
const ts = parseSlackTs(messageTs);
|
|
126591
127202
|
const slackMessage = markdownToSlack(message);
|
|
@@ -126758,6 +127369,10 @@ var registerSlackUploadFileTool = (server) => {
|
|
|
126758
127369
|
if (!app) {
|
|
126759
127370
|
return toolErr("Slack not configured.");
|
|
126760
127371
|
}
|
|
127372
|
+
const blockReason = await getChannelBlockReason(app.client, slackChannelId);
|
|
127373
|
+
if (blockReason) {
|
|
127374
|
+
return toolErr(externalChannelToolError(slackChannelId, blockReason));
|
|
127375
|
+
}
|
|
126761
127376
|
if (!filePath && !content) {
|
|
126762
127377
|
return toolErr("Must provide either filePath or content (base64).");
|
|
126763
127378
|
}
|
|
@@ -132592,7 +133207,7 @@ class NotifyExecutor extends BaseExecutor {
|
|
|
132592
133207
|
}
|
|
132593
133208
|
}
|
|
132594
133209
|
case "slack": {
|
|
132595
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-
|
|
133210
|
+
const { getSlackApp: getSlackApp2 } = await import("./app-km9qnv2f.js");
|
|
132596
133211
|
const app = getSlackApp2();
|
|
132597
133212
|
if (!app) {
|
|
132598
133213
|
return {
|
|
@@ -133401,4 +134016,4 @@ async function initWorkflows() {
|
|
|
133401
134016
|
logOpenWebhookTriggers();
|
|
133402
134017
|
}
|
|
133403
134018
|
|
|
133404
|
-
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, resolveIdentity, renderIdentity, createAdditiveBuffer, listHarnessModelSupport, recordHarnessModelSupport, agentRanModel, getAgentHarnessCliVersion, setAgentHarnessCliVersion, sanitizeModelTierOverrides, parseModelTierOverridesHeader, setAgentModelTierOverrides, previewModelTiers, recordClaimModelResolution, harnessModelErrorFor, poolTaskRunsOnHarness, explicitModelError, explicitModelErrorForAgent, referencedBundlePaths, skillDirs, TaskCreationBlockedError, recordUnmappedIdentity, MAX_TASK_ATTACHMENT_BYTES, recordTaskAttachmentUpload, buildEffectiveText, bufferedFileFailures, fetchSlackFiles, createSlackTaskWithFiles, notifySlackFileFailures, isRuntimePoolWakeable, isAgentReachable, hasOtherUserMention, broadcastMatches, routeMessage, extractTaskFromMessage, extractSlackMessageText, recoverIncompleteRuns, startRetryPoller, stopRetryPoller, validateTemplateVariables, instantiateTemplate, handleWebhookTrigger, logOpenWebhookTriggers, handleEventTrigger, handleScheduleTrigger, verifyHmacSignature, WebhookError, snapshotWorkflow, snapshotAndUpdateWorkflow, startWaitPoller, stopWaitPoller, RawLlmConfigSchema, RawLlmOutputSchema, executeRawLlm, getExecutorRegistry, initWorkflows, dispatchScheduleTarget, calculateNextRun, startScheduler, stopScheduler, runScheduleNow, parseSkillContent, ExtensionAssetConflictError, ExtensionOwnershipError, getExtensionFiles, getExtensionById, getExtensionByName, listExtensions, listExtensionVersions, updateExtensionMeta, listExtensionRuns, SDK_ALLOWLIST, mcpToolNameForSdkMethod, mcpOverflowAuthError, hasKvViewArgs, resolveKvView, toolErr, createToolRegistrar, duplicateGuardedQueryKey, warnIfCorsAllowsAnyOrigin, isOriginAllowedForCredentials, allowedCredentialRedirect, setCorsHeaders, parseQueryParams, getPathSegments, wireHttpSpanLifecycle, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, writeUnhandledError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, runtimeInstanceHeader, describeRequestRoute, route, handleMcpBridge, Redacted, SCRIPT_SDK_RESPONSE_LIMIT_BYTES, readScriptSdkJsonResponse, ExtensionConfigError, ExtensionLifecycleError, setExtensionLoopbackBaseUrl, enableExtension, disableExtension, activateVersion, ExtensionAssetsInvalidError, installExtensionWithAssets, uninstallExtension, reloadExtension, loadEnabledExtensions, stopExtensionRuntime, ExtensionAbortedError, isRegistered, isExtensionAgentId, getExtensionBridgeToken, resolveBridgeCallOrigin, extensionIdForAgent, runContext, registerLoaded, unregister, listRegistered, dispatchPre, dispatchPost, stripSiblingBlock, createTaskWithSiblingAwareness, isInboxAllowed, isSenderAllowed, handleMessageReceived, isAzureDevOpsEnabled, initAzureDevOps, verifyAzureDevOpsWebhook, isAzureDevOpsUrl, canonicalAzureDevOpsRepoUrl, parseAzureDevOpsRepoUrl, createdPullRequestOf, commentedPayloadOf, handlePullRequestCreated, handlePullRequestCommented, AGENT_MAX_TASKS_CONFIG_KEY, reconcileAgentMaxTasksPolicy, resetAgentMaxTasksMirror, upsertSwarmConfigWithPolicyMirror, runtimeStaleThresholdMinutes, upsertRuntimeInstance, getRuntimeInstanceById, insertProvisioningRuntime, markRuntimeTerminated, touchRuntimeInstance, countActiveRuntimeInstancesForAgent, listRuntimeInstancesForAgent, setRuntimeCredentialReady, reconcileAgentStatusFromRuntimes, agentsWithLiveRuntime, expireStaleRuntimeInstances, listRuntimeInstancesForPool, enqueueAuditRow, enqueueAdmissionRow, flushAuditBuffer, startAuditWriter, stopAuditWriter, startAuditGc, stopAuditGc, getTelemetryIngestAnchor, initApiTelemetry, syncTelemetryEnabled, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, isGitLabEnabled, initGitLab, verifyGitLabWebhook, handleMergeRequest, handleIssue2 as handleIssue1, handleNote, handlePipeline,
|
|
134019
|
+
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, resolveIdentity, renderIdentity, createAdditiveBuffer, listHarnessModelSupport, recordHarnessModelSupport, agentRanModel, getAgentHarnessCliVersion, setAgentHarnessCliVersion, sanitizeModelTierOverrides, parseModelTierOverridesHeader, setAgentModelTierOverrides, previewModelTiers, recordClaimModelResolution, harnessModelErrorFor, poolTaskRunsOnHarness, explicitModelError, explicitModelErrorForAgent, referencedBundlePaths, skillDirs, TaskCreationBlockedError, recordUnmappedIdentity2 as recordUnmappedIdentity, MAX_TASK_ATTACHMENT_BYTES, recordTaskAttachmentUpload, buildEffectiveText, bufferedFileFailures, fetchSlackFiles, createSlackTaskWithFiles, notifySlackFileFailures, isRuntimePoolWakeable, isAgentReachable, hasOtherUserMention, broadcastMatches, routeMessage, extractTaskFromMessage, extractSlackMessageText, recoverIncompleteRuns, startRetryPoller, stopRetryPoller, validateTemplateVariables, instantiateTemplate, handleWebhookTrigger, logOpenWebhookTriggers, handleEventTrigger, handleScheduleTrigger, verifyHmacSignature, WebhookError, snapshotWorkflow, snapshotAndUpdateWorkflow, startWaitPoller, stopWaitPoller, RawLlmConfigSchema, RawLlmOutputSchema, executeRawLlm, getExecutorRegistry, initWorkflows, dispatchScheduleTarget, calculateNextRun, startScheduler, stopScheduler, runScheduleNow, parseSkillContent, ExtensionAssetConflictError, ExtensionOwnershipError, getExtensionFiles, getExtensionById, getExtensionByName, listExtensions, listExtensionVersions, updateExtensionMeta, listExtensionRuns, SDK_ALLOWLIST, mcpToolNameForSdkMethod, mcpOverflowAuthError, hasKvViewArgs, resolveKvView, toolErr, createToolRegistrar, duplicateGuardedQueryKey, warnIfCorsAllowsAnyOrigin, isOriginAllowedForCredentials, allowedCredentialRedirect, setCorsHeaders, parseQueryParams, getPathSegments, wireHttpSpanLifecycle, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, writeUnhandledError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, runtimeInstanceHeader, describeRequestRoute, route, handleMcpBridge, Redacted, SCRIPT_SDK_RESPONSE_LIMIT_BYTES, readScriptSdkJsonResponse, ExtensionConfigError, ExtensionLifecycleError, setExtensionLoopbackBaseUrl, enableExtension, disableExtension, activateVersion, ExtensionAssetsInvalidError, installExtensionWithAssets, uninstallExtension, reloadExtension, loadEnabledExtensions, stopExtensionRuntime, ExtensionAbortedError, isRegistered, isExtensionAgentId, getExtensionBridgeToken, resolveBridgeCallOrigin, extensionIdForAgent, runContext, registerLoaded, unregister, listRegistered, dispatchPre, dispatchPost, stripSiblingBlock, createTaskWithSiblingAwareness, isInboxAllowed, isSenderAllowed, handleMessageReceived, isAzureDevOpsEnabled, initAzureDevOps, verifyAzureDevOpsWebhook, repointTrackerSyncBySwarmId, isAzureDevOpsUrl, canonicalAzureDevOpsRepoUrl, parseAzureDevOpsRepoUrl, createdPullRequestOf, commentedPayloadOf, handlePullRequestCreated, handlePullRequestCommented, handleWorkItemCreated, handleWorkItemUpdated, handleWorkItemCommented, handleWorkItemDeleted, handleWorkItemRestored, AGENT_MAX_TASKS_CONFIG_KEY, reconcileAgentMaxTasksPolicy, resetAgentMaxTasksMirror, upsertSwarmConfigWithPolicyMirror, runtimeStaleThresholdMinutes, upsertRuntimeInstance, getRuntimeInstanceById, insertProvisioningRuntime, markRuntimeTerminated, touchRuntimeInstance, countActiveRuntimeInstancesForAgent, listRuntimeInstancesForAgent, setRuntimeCredentialReady, reconcileAgentStatusFromRuntimes, agentsWithLiveRuntime, expireStaleRuntimeInstances, listRuntimeInstancesForPool, enqueueAuditRow, enqueueAdmissionRow, flushAuditBuffer, startAuditWriter, stopAuditWriter, startAuditGc, stopAuditGc, getTelemetryIngestAnchor, initApiTelemetry, syncTelemetryEnabled, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, isGitLabEnabled, initGitLab, verifyGitLabWebhook, handleMergeRequest, handleIssue2 as handleIssue1, handleNote, handlePipeline, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, parseCookieHeader, extractAndVerifyCookie, issuePageSessionCookie, resolveHttpRequestAuth, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, userCtx, resolveTaskAuditUserId, resolveHttpAuditUserId, resolveHttpFavoriteOwner, handleApps, cancelApprovalRequest, cancelApprovalRequestAndRun, getUserCommsPrefs, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, executeReadOnlyQuery, assertSelectOnlyQuery, handleDbQuery, snapshotMetric, AssetKeyAuthorizationError, authorizeAssetKeyWrite, snapshotPage, assertOAuthAppUrlsSafe, assertOAuthEgressUrlSafe, listOAuthPresets, listOAuthPresetIds, getOAuthPreset, hydrateOAuthAppFromPreset, scopedResourceScopeIdSchema, resolveScopedResourceId, dedupeMemoryDocumentIds, recordMemoryAccesses, recordRetrievals, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, REBOOT_RETRY_PIN_TAG, getPinCandidateAgent, resolveLeadOnlyRecoveryAssignment, getResumeGeneration, getNextResumeGeneration, inheritedModelSelection, createWorkerTaskFollowUp, createResumeFollowUp, createRerouteDecisionTask, createPoolStarvationDecisionTask, guardTerminalTaskResultWrite, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, reservedNamespaceError, reservedRoomKeyError, updateMcpServerChecked, updateTouchesStdioExecution, MemoryKeyError, refreshLinks, canReadMemory, getLinksForMemory, expandCandidatesWithGraph, indexMemoryContent, isApiDraining, drainApi, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, changesAllowMerge, updateSwarmRepoChecked, getCombConfig, resolveRoomNamespace, roomRequestInfo, authorizeRoomNamespace, RoomOperationSchema, removeAwarenessStates, roomTopic, decodeRoomSnapshot, roomView, getRoom, changeRoom, applyRoomUpdate, resetRoom, sweepRooms, closeRooms, removeNamespaceRooms, MEMORY_KEYS_DEFAULT_PREFIX, MEMORY_KEYS_MAX_LIMIT, estimateTokens, posteriorMean, listMemoryKeys, checkChunkIntegrity, getMemoryChunks, getPosteriorsByIds, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, checkOutputSchemaShape, sendTaskOutputSchema, sendTaskHandler, steerTaskUserInputSchema, steerTaskOutputSchema, steerTaskHandler, canClaim, emitBudgetRefusalSideEffects, workflowModelErrors, patchWorkflowDefinition, resolveScriptsOnlyMode, ALL_CAPABILITIES, hasCapability, getEnabledCapabilities, createServer };
|