@desplega.ai/agent-swarm 1.152.2 → 1.154.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{acp-adapter-pfeje2v5.js → acp-adapter-eb2aj8xy.js} +4 -4
- package/dist/{actions-0sttc7f5.js → actions-kpnwp1kz.js} +31 -27
- package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
- package/dist/{app-p8teva95.js → app-zk1apkf6.js} +10 -7
- package/dist/{assistant-r45jnsrt.js → assistant-kwq4y50q.js} +32 -28
- package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
- package/dist/{boot-reembed-je26h5y7.js → boot-reembed-22hd3jjn.js} +9 -7
- package/dist/{boot-reembed-1qmybsd4.js → boot-reembed-qe168424.js} +10 -8
- package/dist/{boot-scrub-logs-gte3h1mm.js → boot-scrub-logs-pkdpsrjd.js} +7 -5
- package/dist/{claude-adapter-csc1mzka.js → claude-adapter-3fx90nww.js} +18 -16
- package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
- package/dist/{claude-sdk-session-fcd6f078.js → claude-sdk-session-z7fahf1k.js} +18 -16
- package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
- package/dist/{cli-wbyk465z.js → cli-09y3tjxz.js} +1 -1
- package/dist/{cli-ht5rs63n.js → cli-0e31nm4n.js} +16 -7
- package/dist/cli-0x5qzvkg.js +4279 -0
- package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
- package/dist/{cli-4ahpr4sw.js → cli-1jpjafw4.js} +5 -5
- package/dist/{cli-jbmc0r7r.js → cli-243tzws8.js} +1 -1
- package/dist/{cli-ch38q5z4.js → cli-24k806sc.js} +6789 -2542
- package/dist/cli-3bkz2p7j.js +2828 -0
- package/dist/cli-3er6gcsn.js +1849 -0
- package/dist/cli-4k7jazkq.js +3339 -0
- package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
- package/dist/{cli-qd65dvr6.js → cli-5vs0nh43.js} +65 -44
- package/dist/{cli-z3wkng35.js → cli-6r3cgpk2.js} +224 -156
- package/dist/{cli-pgj4a5fb.js → cli-71cj99hx.js} +4 -3
- package/dist/cli-8sea27q4.js +14075 -0
- package/dist/{cli-jvy9qgjs.js → cli-913yakhv.js} +22 -7
- package/dist/{cli-pf9q3696.js → cli-927erqqt.js} +5 -2
- package/dist/cli-95jnpnqg.js +230 -0
- package/dist/{cli-wpv5rgg2.js → cli-abp3g4ax.js} +15 -2
- package/dist/{cli-ckz5t2hb.js → cli-acy6bfhr.js} +10 -7
- package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
- package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
- package/dist/{cli-qn042eh7.js → cli-c1v10xb2.js} +194 -1913
- package/dist/cli-cgj4hc12.js +18902 -0
- package/dist/{cli-2nnd56zt.js → cli-e0pg4zeh.js} +3 -2
- package/dist/{cli-r84ctdxn.js → cli-e31ehqwy.js} +2 -2
- package/dist/{cli-gphgnvpf.js → cli-e3229na9.js} +3 -3
- package/dist/{cli-gpqcs935.js → cli-e3cyf59d.js} +2 -2
- package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
- package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
- package/dist/{cli-j4nv2wqy.js → cli-fdzg6jhf.js} +10 -4
- package/dist/{cli-fm4ytsdk.js → cli-g2h29qar.js} +2 -2
- package/dist/{cli-zvgj5zg8.js → cli-ge67p080.js} +302 -3497
- package/dist/cli-h38x0qhg.js +6912 -0
- package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
- package/dist/{cli-jew3jda5.js → cli-hws0vn86.js} +2 -2
- package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
- package/dist/{cli-xw7vak96.js → cli-kp3qs53m.js} +5 -5
- package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
- package/dist/{cli-zvkq5csn.js → cli-myaxtgh8.js} +7 -6
- package/dist/{cli-56h18ng2.js → cli-n62atwh9.js} +1 -1
- package/dist/cli-n7h1kqpf.js +29 -0
- package/dist/{cli-fmy65jdn.js → cli-pf1v8xhb.js} +7 -5
- package/dist/{cli-xy1cchaf.js → cli-q85qhckh.js} +238 -11
- package/dist/{cli-3bpzj8me.js → cli-r1d3dzt6.js} +2 -2
- package/dist/{pi-mono-adapter-jcqt71cg.js → cli-rxkqvngf.js} +67343 -101083
- package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
- package/dist/{cli-b9dd0h3k.js → cli-stwsmd6k.js} +3 -2
- package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
- package/dist/{cli-z61ykvam.js → cli-wg04s9kr.js} +4 -3
- package/dist/cli-yn0a417a.js +4069 -0
- package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
- package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
- package/dist/cli.js +28 -35
- package/dist/{codex-adapter-fxmpsabw.js → codex-adapter-ttdm995v.js} +12 -11
- package/dist/{codex-hook-8jexc34x.js → codex-hook-8gx7bcz0.js} +3 -3
- package/dist/{codex-session-runner-rr1e47ea.js → codex-session-runner-3v8nh4c8.js} +12 -11
- package/dist/{commands-0f5rxz51.js → commands-1cxwbhhq.js} +7 -5
- package/dist/{db-r3jxf3wy.js → db-y9zwsr79.js} +25 -5
- package/dist/{dispatcher-06vph6q4.js → dispatcher-9m9zss36.js} +30 -26
- package/dist/dsh-adapter-gsb2qqf9.js +19 -0
- package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
- package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-gwwrnzqn.js} +33 -73
- package/dist/{google-vertex-fr665tqs.js → google-vertex-5wfawpwz.js} +38 -68
- package/dist/{handlers-0hgtc910.js → handlers-36fm1bwn.js} +33 -29
- package/dist/{hook-9kvrv1er.js → hook-pntw4r8h.js} +12 -10
- package/dist/{hook-fbfhkat3.js → hook-vwq8y26p.js} +13 -11
- package/dist/{http-xbyx8k5j.js → http-20jesakk.js} +357 -261
- package/dist/{index-jsw3aynb.js → index-6artrehg.js} +30 -26
- package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
- package/dist/{index-rnyahfn5.js → index-9n5z2492.js} +30 -26
- package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
- package/dist/{index-tfsdxyv6.js → index-j5wrf1yw.js} +40 -25
- package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
- package/dist/{index-sgk50mbg.js → index-m2xqpz5r.js} +31 -27
- package/dist/{index-gpfn1741.js → index-m9ks8a7t.js} +9 -7
- package/dist/index-w097eg0x.js +5 -0
- package/dist/jiti-loader-b4837mtm.js +34 -0
- package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
- package/dist/{keepalive-s3xpr7sw.js → keepalive-pwxvxd2c.js} +9 -7
- package/dist/{lead-rq4n5r16.js → lead-m789wqa3.js} +46 -41
- package/dist/{maintenance-f0atj1ab.js → maintenance-bvg6w7e2.js} +10 -8
- package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
- package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
- package/dist/{oauth-refresh-sweep-q0t3bxre.js → oauth-refresh-sweep-6v4bd737.js} +8 -6
- package/dist/{onboard-br5j1kch.js → onboard-snckpcj1.js} +115 -35
- package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
- package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
- package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
- package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
- package/dist/{otel-impl-vdqge2nt.js → otel-impl-e0ytens5.js} +4 -4
- package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
- package/dist/pi-mono-adapter-c4ggemhz.js +1316 -0
- package/dist/{pricing-refresh-hb3691fd.js → pricing-refresh-52g2ywrw.js} +9 -7
- package/dist/{rbac-roles-t78xt39b.js → rbac-roles-5455kfdn.js} +8 -6
- package/dist/{rbac-roles-jh280k9g.js → rbac-roles-hbf0xa52.js} +9 -7
- package/dist/{render-v2-7p8r2ymg.js → render-v2-npm8xdzh.js} +16 -9
- package/dist/{seed-pricing-hf889enh.js → seed-pricing-650wbr1n.js} +8 -6
- package/dist/{setup-w6mb03jv.js → setup-k1mew5rf.js} +4 -5
- package/dist/virtual-modules-2q5b2pk4.js +54 -0
- package/dist/{work-objects-nt8gy1kj.js → work-objects-2ea3kvwg.js} +33 -29
- package/dist/{worker-dwy617t0.js → worker-ec6hvc4m.js} +46 -41
- package/openapi.json +109 -16
- package/package.json +7 -6
- package/plugin/opencode-plugins/lib/memory-raters.ts +7 -0
- package/src/be/automation-preflight.ts +6 -4
- package/src/be/db/agents.ts +75 -2
- package/src/be/db/tasks/read.ts +2 -0
- package/src/be/db/tasks/write.ts +7 -1
- package/src/be/db.ts +140 -18
- package/src/be/extensions/db.ts +35 -4
- package/src/be/memory/raters/llm.ts +2 -6
- package/src/be/memory/raters/noop.ts +2 -3
- package/src/be/memory/raters/registry.ts +4 -10
- package/src/be/migrations/157_defer_provenance_and_marker.sql +27 -0
- package/src/be/models-catalog.ts +2 -0
- package/src/be/modelsdev-cache.json +213 -0
- package/src/be/rbac-audit.ts +2 -0
- package/src/be/seed/agent-fs-provision.ts +23 -0
- package/src/be/seed-pricing.ts +3 -1
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/onboard/compose-generator.ts +16 -1
- package/src/commands/onboard/env-generator.ts +14 -1
- package/src/commands/onboard/manifest.ts +1 -0
- package/src/commands/onboard/steps/integration-slack.tsx +56 -16
- package/src/commands/onboard/steps/review.tsx +5 -0
- package/src/commands/onboard/types.ts +6 -0
- package/src/commands/onboard.tsx +5 -1
- package/src/commands/provider-credentials.ts +14 -2
- package/src/commands/runner.ts +18 -2
- package/src/extensions/identity.ts +15 -7
- package/src/extensions/lifecycle.ts +15 -21
- package/src/hooks/codex-hook.ts +7 -1
- package/src/http/agents.ts +12 -4
- package/src/http/config.ts +1 -1
- package/src/http/core.ts +5 -3
- package/src/http/extensions.ts +108 -34
- package/src/http/poll.ts +7 -0
- package/src/http/schedules.ts +10 -0
- package/src/http/script-runs.ts +35 -26
- package/src/http/status.ts +24 -11
- package/src/http/tasks.ts +6 -1
- package/src/prompts/base-prompt.ts +15 -7
- package/src/prompts/memories.ts +5 -7
- package/src/prompts/session-templates.ts +3 -2
- package/src/prompts/steering-delivery.ts +2 -0
- package/src/providers/claude-managed-models.ts +8 -0
- package/src/providers/codex-adapter.ts +3 -6
- package/src/providers/codex-models.ts +15 -1
- package/src/providers/dsh-adapter.ts +248 -0
- package/src/providers/index.ts +5 -1
- package/src/providers/modelsdev-reasoning.json +50 -0
- package/src/providers/pi-mono-extension.ts +2 -5
- package/src/rbac/legacy-policy.ts +15 -1
- package/src/rbac/types.ts +2 -0
- package/src/server.ts +7 -1
- package/src/slack/ack.ts +43 -8
- package/src/slack/app.ts +32 -10
- package/src/slack/config.ts +29 -0
- package/src/slack/handlers.ts +7 -1
- package/src/slack/render-v2.ts +186 -17
- package/src/slack/responses.ts +1 -3
- package/src/slack/task-output.ts +66 -8
- package/src/slack/thread-buffer.ts +27 -3
- package/src/telemetry.ts +2 -3
- package/src/tests/agent-fs-provision-seeder.test.ts +56 -2
- package/src/tests/agents-harness-provider.test.ts +19 -0
- package/src/tests/agents-list-model-display.test.ts +60 -1
- package/src/tests/base-prompt.test.ts +25 -0
- package/src/tests/claude-managed-adapter.test.ts +6 -0
- package/src/tests/codex-gpt6-release.test.ts +97 -0
- package/src/tests/codex-steering-hook.test.ts +2 -2
- package/src/tests/config-api-only-keys.test.ts +9 -0
- package/src/tests/context-window.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +9 -0
- package/src/tests/db-task-write-contract.test.ts +9 -0
- package/src/tests/defer-task.test.ts +115 -16
- package/src/tests/dsh-adapter.test.ts +276 -0
- package/src/tests/e2e-nightly-report.test.ts +3 -3
- package/src/tests/e2e-redact.test.ts +2 -0
- package/src/tests/env-flag.test.ts +6 -0
- package/src/tests/extension-agent-visibility.test.ts +474 -0
- package/src/tests/extensions-db.test.ts +41 -0
- package/src/tests/extensions-http.test.ts +148 -4
- package/src/tests/extensions-lifecycle.test.ts +21 -1
- package/src/tests/extensions-mcp-tools.test.ts +9 -5
- package/src/tests/fetch-resolved-env.test.ts +43 -0
- package/src/tests/internal-ai/complete-structured.test.ts +32 -9
- package/src/tests/internal-ai/credentials.test.ts +4 -4
- package/src/tests/internal-ai/models.test.ts +20 -0
- package/src/tests/memory-rate-tool.test.ts +7 -10
- package/src/tests/memory-rater-defaults.test.ts +23 -0
- package/src/tests/memory-rater-e2e.test.ts +2 -3
- package/src/tests/migration-046-budgets.test.ts +2 -0
- package/src/tests/model-control.test.ts +2 -2
- package/src/tests/onboard-compose.test.ts +14 -0
- package/src/tests/onboard-env.test.ts +17 -0
- package/src/tests/onboard-manifest.test.ts +12 -0
- package/src/tests/opus-5-5-registry.test.ts +48 -0
- package/src/tests/pricing-refresh.test.ts +1 -1
- package/src/tests/pricing-routes.test.ts +15 -3
- package/src/tests/provider-registration.test.ts +5 -2
- package/src/tests/rbac-admission-e2e.test.ts +54 -0
- package/src/tests/rbac-e2e-helpers.ts +2 -0
- package/src/tests/rbac-engine.test.ts +32 -0
- package/src/tests/rbac-wire-e2e.test.ts +33 -0
- package/src/tests/reasoning-effort.test.ts +8 -0
- package/src/tests/reload-config.test.ts +33 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
- package/src/tests/slack-config.test.ts +136 -0
- package/src/tests/slack-mode-lifecycle.test.ts +69 -0
- package/src/tests/slack-render-v2.test.ts +446 -16
- package/src/tests/slack-steering.test.ts +69 -1
- package/src/tests/slack-task-output.test.ts +33 -12
- package/src/tests/status.test.ts +22 -2
- package/src/tests/steering-core.test.ts +35 -0
- package/src/tests/steering-delivery.test.ts +39 -0
- package/src/tests/steering-transport.test.ts +2 -2
- package/src/tests/task-tool-preload-mcp.test.ts +3 -1
- package/src/tests/telemetry-init.test.ts +18 -0
- package/src/tests/workflow-live-recovery.test.ts +126 -0
- package/src/tests/workflow-llm.test.ts +3 -3
- package/src/tools/defer-task.ts +64 -26
- package/src/tools/extension-common.ts +4 -0
- package/src/tools/extension-install.ts +1 -1
- package/src/tools/extension-list.ts +4 -3
- package/src/tools/join-swarm.ts +1 -1
- package/src/tools/schedules/create-schedule.ts +11 -1
- package/src/tools/schedules/patch-schedule.ts +5 -0
- package/src/tools/schedules/update-schedule.ts +5 -0
- package/src/tools/send-task.ts +6 -0
- package/src/tools/utils.ts +1 -1
- package/src/types.ts +30 -8
- package/src/utils/context-window.ts +1 -0
- package/src/utils/credentials.ts +8 -0
- package/src/utils/internal-ai/complete-structured.ts +8 -0
- package/src/utils/internal-ai/models.ts +5 -5
- package/src/utils/memory-raters.ts +3 -0
- package/src/workflows/engine.ts +28 -0
- package/src/workflows/recovery.ts +7 -4
- package/templates/schedules/daily-status-report/content.md +25 -12
- package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
- package/templates/skills/swarm-extensions/content.md +9 -5
- package/dist/cli-4wppfkcm.js +0 -16384
- package/dist/cli-55qrx3q8.js +0 -6041
- package/dist/cli-cg4vnyh4.js +0 -305
- package/dist/cli-jyetr64j.js +0 -52
- package/dist/cli-pp9ax9pa.js +0 -520
- package/dist/cli-qa66qaf0.js +0 -6076
- package/dist/cli-qghpvca2.js +0 -590
- package/dist/cli-rembwgt8.js +0 -861
- package/dist/cli-tcvj7j2r.js +0 -1971
- package/dist/cli-vfcth11k.js +0 -36
- package/dist/cli-w1d1bgkk.js +0 -1422
- package/dist/cli-y6x0gqtg.js +0 -5977
- package/dist/es5-tj3tn4jj.js +0 -844
- package/dist/index-1kq5hw2p.js +0 -467
- package/dist/index-1p8wdstt.js +0 -7
- package/dist/index-8fdjzb2f.js +0 -100
- package/dist/index-bxmwzbgw.js +0 -365
- package/dist/index-nzerxd5z.js +0 -7
- package/dist/index-pyymnzat.js +0 -1168
- package/dist/index-qv6atbab.js +0 -619
- package/dist/index-rja32qa7.js +0 -801
- package/dist/index-tzg5srfp.js +0 -211
- package/dist/index-vhncw983.js +0 -90
- package/dist/index-xk036zsk.js +0 -859
- package/dist/index-zyazbzcb.js +0 -6
- package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
package/src/be/db.ts
CHANGED
|
@@ -141,10 +141,15 @@ import { normalizeDate, normalizeDateRequired } from "./date-utils";
|
|
|
141
141
|
import {
|
|
142
142
|
type AgentRow,
|
|
143
143
|
configureAgentDependencies,
|
|
144
|
+
ExtensionAgentAssignmentError,
|
|
144
145
|
getActiveTaskCount,
|
|
145
146
|
getAgentById,
|
|
146
147
|
getAllAgents,
|
|
147
148
|
isAgentEligibleForTask,
|
|
149
|
+
isExtensionAgent,
|
|
150
|
+
NOT_EXTENSION_AGENT_SQL,
|
|
151
|
+
ReservedAgentRoleError,
|
|
152
|
+
reservedRoleViolation,
|
|
148
153
|
rowToAgent,
|
|
149
154
|
} from "./db/agents";
|
|
150
155
|
import {
|
|
@@ -172,6 +177,9 @@ export {
|
|
|
172
177
|
buildRoutingAffinityFromAgent,
|
|
173
178
|
createAgent,
|
|
174
179
|
deleteAgent,
|
|
180
|
+
EXTENSION_AGENT_ROLE,
|
|
181
|
+
ExtensionAgentAssignmentError,
|
|
182
|
+
extensionAgentAssignmentError,
|
|
175
183
|
getActiveTaskCount,
|
|
176
184
|
getAgentById,
|
|
177
185
|
getAgentHarnessProviders,
|
|
@@ -181,9 +189,13 @@ export {
|
|
|
181
189
|
hasCapacity,
|
|
182
190
|
incrementEmptyPollCount,
|
|
183
191
|
isAgentEligibleForTask,
|
|
192
|
+
isExtensionAgent,
|
|
184
193
|
isPoolAffinityEnforcementEnabled,
|
|
185
194
|
listAgentsWithCredStatusByProvider,
|
|
186
195
|
MAX_EMPTY_POLLS,
|
|
196
|
+
NOT_EXTENSION_AGENT_SQL,
|
|
197
|
+
ReservedAgentRoleError,
|
|
198
|
+
reservedRoleViolation,
|
|
187
199
|
resetEmptyPollCount,
|
|
188
200
|
setAgentHarnessProvider,
|
|
189
201
|
shouldBlockPolling,
|
|
@@ -427,6 +439,10 @@ export interface SlackMessageRecord {
|
|
|
427
439
|
finalizedAt?: string;
|
|
428
440
|
streamChunksAppended: number;
|
|
429
441
|
conclusionKind?: SlackConclusionKind;
|
|
442
|
+
/** Set once a deferral card has been rewritten into its resolved state. */
|
|
443
|
+
deferralResolvedAt?: string;
|
|
444
|
+
/** Set with `deferralResolvedAt` when the rewrite was given up on. */
|
|
445
|
+
deferralAbandonedAt?: string;
|
|
430
446
|
createdAt: string;
|
|
431
447
|
updatedAt: string;
|
|
432
448
|
}
|
|
@@ -449,6 +465,8 @@ type SlackMessageRow = {
|
|
|
449
465
|
finalized_at: string | null;
|
|
450
466
|
stream_chunks_appended: number;
|
|
451
467
|
conclusion_kind: SlackConclusionKind | null;
|
|
468
|
+
deferral_resolved_at: string | null;
|
|
469
|
+
deferral_abandoned_at: string | null;
|
|
452
470
|
created_at: string;
|
|
453
471
|
updated_at: string;
|
|
454
472
|
};
|
|
@@ -466,6 +484,8 @@ function rowToSlackMessage(row: SlackMessageRow): SlackMessageRecord {
|
|
|
466
484
|
finalizedAt: row.finalized_at ?? undefined,
|
|
467
485
|
streamChunksAppended: row.stream_chunks_appended,
|
|
468
486
|
conclusionKind: row.conclusion_kind ?? undefined,
|
|
487
|
+
deferralResolvedAt: row.deferral_resolved_at ?? undefined,
|
|
488
|
+
deferralAbandonedAt: row.deferral_abandoned_at ?? undefined,
|
|
469
489
|
createdAt: row.created_at,
|
|
470
490
|
updatedAt: row.updated_at,
|
|
471
491
|
};
|
|
@@ -675,6 +695,57 @@ export async function getSlackOutcomeMessage(taskId: string): Promise<SlackMessa
|
|
|
675
695
|
return row ? rowToSlackMessage(row) : null;
|
|
676
696
|
}
|
|
677
697
|
|
|
698
|
+
/**
|
|
699
|
+
* Deferral outcome cards whose wake-up task has since reached a terminal
|
|
700
|
+
* state, and which have not been rewritten into that resolved state yet.
|
|
701
|
+
*
|
|
702
|
+
* The join is `agent_tasks.parentTaskId` — `defer-task` sets it on the
|
|
703
|
+
* schedule and `createStandaloneScheduleTask` passes it through, so the
|
|
704
|
+
* wake-up task points back at the task whose card announced the wait.
|
|
705
|
+
* `deferredAt` (not the `deferred` tag) identifies the deferring task,
|
|
706
|
+
* because the wake-up task inherits the tag.
|
|
707
|
+
*/
|
|
708
|
+
export async function getResolvableDeferralOutcomes(
|
|
709
|
+
limit = 50,
|
|
710
|
+
): Promise<{ card: SlackMessageRecord; deferredTaskId: string; wakeTaskId: string }[]> {
|
|
711
|
+
const rows = await getDbClient().query<SlackMessageRow & { wake_task_id: string }>(
|
|
712
|
+
`SELECT card.*, wake.id AS wake_task_id
|
|
713
|
+
FROM slack_messages card
|
|
714
|
+
JOIN agent_tasks deferred ON deferred.id = card.task_id
|
|
715
|
+
JOIN agent_tasks wake ON wake.parentTaskId = deferred.id AND wake.taskType = 'deferred'
|
|
716
|
+
WHERE card.kind = 'outcome'
|
|
717
|
+
AND card.finalized_at IS NOT NULL
|
|
718
|
+
AND card.deferral_resolved_at IS NULL
|
|
719
|
+
AND card.ts NOT LIKE 'pending:%'
|
|
720
|
+
AND deferred.deferredAt IS NOT NULL
|
|
721
|
+
AND wake.status IN ('completed', 'failed', 'cancelled')
|
|
722
|
+
ORDER BY wake.finishedAt ASC
|
|
723
|
+
LIMIT ?`,
|
|
724
|
+
[limit],
|
|
725
|
+
);
|
|
726
|
+
return rows.map((row) => ({
|
|
727
|
+
card: rowToSlackMessage(row),
|
|
728
|
+
deferredTaskId: row.task_id!,
|
|
729
|
+
wakeTaskId: row.wake_task_id,
|
|
730
|
+
}));
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Mark a deferral card as resolved, so the resolution pass skips it after.
|
|
735
|
+
* `abandoned` records that the rewrite was given up on, which hands the
|
|
736
|
+
* answer back to the wake-up's own outcome card.
|
|
737
|
+
*/
|
|
738
|
+
export async function markSlackDeferralResolved(
|
|
739
|
+
id: string,
|
|
740
|
+
options: { abandoned?: boolean } = {},
|
|
741
|
+
): Promise<void> {
|
|
742
|
+
const now = new Date().toISOString();
|
|
743
|
+
await getDbClient().run(
|
|
744
|
+
"UPDATE slack_messages SET deferral_resolved_at = ?, deferral_abandoned_at = ? WHERE id = ?",
|
|
745
|
+
[now, options.abandoned ? now : null, id],
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
678
749
|
export async function getSlackTreeMessages(): Promise<SlackMessageRecord[]> {
|
|
679
750
|
const rows = await getDbClient().query<SlackMessageRow>(
|
|
680
751
|
`SELECT tree.*
|
|
@@ -1662,6 +1733,7 @@ type TaskSteeringMessageRow = {
|
|
|
1662
1733
|
status: string;
|
|
1663
1734
|
delivered_mode: string | null;
|
|
1664
1735
|
source: string;
|
|
1736
|
+
sender_name?: string | null;
|
|
1665
1737
|
created_by_kind: string;
|
|
1666
1738
|
created_by_user_id: string | null;
|
|
1667
1739
|
created_by_agent_id: string | null;
|
|
@@ -1672,7 +1744,20 @@ type TaskSteeringMessageRow = {
|
|
|
1672
1744
|
handled_note: string | null;
|
|
1673
1745
|
};
|
|
1674
1746
|
|
|
1747
|
+
// Resolve names in the API so workers need no database access or identity lookups.
|
|
1748
|
+
const STEERING_MESSAGE_SELECT = `SELECT m.*,
|
|
1749
|
+
CASE m.created_by_kind WHEN 'user' THEN u.name WHEN 'agent' THEN a.name END AS sender_name
|
|
1750
|
+
FROM task_steering_messages m
|
|
1751
|
+
LEFT JOIN users u ON m.created_by_kind = 'user' AND u.id = m.created_by_user_id
|
|
1752
|
+
LEFT JOIN agents a ON m.created_by_kind = 'agent' AND a.id = m.created_by_agent_id`;
|
|
1753
|
+
|
|
1675
1754
|
function rowToSteeringMessage(row: TaskSteeringMessageRow): SteeringMessage {
|
|
1755
|
+
const senderId =
|
|
1756
|
+
row.created_by_kind === "user" ? row.created_by_user_id : row.created_by_agent_id;
|
|
1757
|
+
const senderLabel =
|
|
1758
|
+
row.created_by_kind === "system"
|
|
1759
|
+
? "system"
|
|
1760
|
+
: `${row.sender_name?.trim() || senderId || "Unknown"} (${row.created_by_kind})`;
|
|
1676
1761
|
return {
|
|
1677
1762
|
id: row.id,
|
|
1678
1763
|
taskId: row.task_id,
|
|
@@ -1681,6 +1766,7 @@ function rowToSteeringMessage(row: TaskSteeringMessageRow): SteeringMessage {
|
|
|
1681
1766
|
status: row.status as SteeringStatus,
|
|
1682
1767
|
deliveredMode: (row.delivered_mode as SteerMode | null) ?? undefined,
|
|
1683
1768
|
source: row.source as SteeringSource,
|
|
1769
|
+
senderLabel,
|
|
1684
1770
|
createdByKind: row.created_by_kind as SteeringMessage["createdByKind"],
|
|
1685
1771
|
createdByUserId: row.created_by_user_id ?? undefined,
|
|
1686
1772
|
createdByAgentId: row.created_by_agent_id ?? undefined,
|
|
@@ -1739,7 +1825,7 @@ export async function createSteeringMessage(
|
|
|
1739
1825
|
});
|
|
1740
1826
|
} catch {}
|
|
1741
1827
|
|
|
1742
|
-
return
|
|
1828
|
+
return (await getSteeringMessageById(id))!;
|
|
1743
1829
|
}
|
|
1744
1830
|
|
|
1745
1831
|
export async function getSteeringMessagesForTask(
|
|
@@ -1748,15 +1834,15 @@ export async function getSteeringMessagesForTask(
|
|
|
1748
1834
|
): Promise<SteeringMessage[]> {
|
|
1749
1835
|
const rows = opts?.status
|
|
1750
1836
|
? await getDbClient().query<TaskSteeringMessageRow>(
|
|
1751
|
-
|
|
1752
|
-
WHERE task_id = ? AND status = ?
|
|
1753
|
-
ORDER BY created_at ASC, rowid ASC`,
|
|
1837
|
+
`${STEERING_MESSAGE_SELECT}
|
|
1838
|
+
WHERE m.task_id = ? AND m.status = ?
|
|
1839
|
+
ORDER BY m.created_at ASC, m.rowid ASC`,
|
|
1754
1840
|
[taskId, opts.status],
|
|
1755
1841
|
)
|
|
1756
1842
|
: await getDbClient().query<TaskSteeringMessageRow>(
|
|
1757
|
-
|
|
1758
|
-
WHERE task_id = ?
|
|
1759
|
-
ORDER BY created_at ASC, rowid ASC`,
|
|
1843
|
+
`${STEERING_MESSAGE_SELECT}
|
|
1844
|
+
WHERE m.task_id = ?
|
|
1845
|
+
ORDER BY m.created_at ASC, m.rowid ASC`,
|
|
1760
1846
|
[taskId],
|
|
1761
1847
|
);
|
|
1762
1848
|
return rows.map(rowToSteeringMessage);
|
|
@@ -1764,8 +1850,8 @@ export async function getSteeringMessagesForTask(
|
|
|
1764
1850
|
|
|
1765
1851
|
export async function getSteeringMessageById(id: string): Promise<SteeringMessage | null> {
|
|
1766
1852
|
const row = await getDbClient().get<TaskSteeringMessageRow>(
|
|
1767
|
-
|
|
1768
|
-
WHERE id = ?`,
|
|
1853
|
+
`${STEERING_MESSAGE_SELECT}
|
|
1854
|
+
WHERE m.id = ?`,
|
|
1769
1855
|
[id],
|
|
1770
1856
|
);
|
|
1771
1857
|
return row ? rowToSteeringMessage(row) : null;
|
|
@@ -1777,8 +1863,7 @@ export async function getPendingSteeringForTask(taskId: string): Promise<Steerin
|
|
|
1777
1863
|
|
|
1778
1864
|
export async function getPendingSteeringForAgent(agentId: string): Promise<SteeringMessage[]> {
|
|
1779
1865
|
const rows = await getDbClient().query<TaskSteeringMessageRow>(
|
|
1780
|
-
|
|
1781
|
-
FROM task_steering_messages m
|
|
1866
|
+
`${STEERING_MESSAGE_SELECT}
|
|
1782
1867
|
JOIN agent_tasks t ON t.id = m.task_id
|
|
1783
1868
|
WHERE t.agentId = ? AND m.status = 'pending'
|
|
1784
1869
|
ORDER BY m.created_at ASC, m.rowid ASC`,
|
|
@@ -1816,7 +1901,7 @@ export async function markSteeringDelivered(
|
|
|
1816
1901
|
} catch {}
|
|
1817
1902
|
}
|
|
1818
1903
|
|
|
1819
|
-
return row ?
|
|
1904
|
+
return row ? await getSteeringMessageById(id) : null;
|
|
1820
1905
|
}
|
|
1821
1906
|
|
|
1822
1907
|
export async function markSteeringHandled(
|
|
@@ -1832,7 +1917,7 @@ export async function markSteeringHandled(
|
|
|
1832
1917
|
RETURNING *`,
|
|
1833
1918
|
[note ?? null, id],
|
|
1834
1919
|
);
|
|
1835
|
-
return row ?
|
|
1920
|
+
return row ? await getSteeringMessageById(id) : null;
|
|
1836
1921
|
}
|
|
1837
1922
|
|
|
1838
1923
|
export async function markSteeringPromoted(
|
|
@@ -1847,7 +1932,7 @@ export async function markSteeringPromoted(
|
|
|
1847
1932
|
RETURNING *`,
|
|
1848
1933
|
[promotedTaskId, id],
|
|
1849
1934
|
);
|
|
1850
|
-
return row ?
|
|
1935
|
+
return row ? await getSteeringMessageById(id) : null;
|
|
1851
1936
|
}
|
|
1852
1937
|
|
|
1853
1938
|
export async function cancelPendingSteeringForTask(taskId: string): Promise<number> {
|
|
@@ -2475,6 +2560,17 @@ export async function createTaskExtended(
|
|
|
2475
2560
|
// (explicit `leadOnly`/`capabilities`, or a lead-only ratchet) still gates
|
|
2476
2561
|
// as before.
|
|
2477
2562
|
const targetAgentId = options.agentId ?? options.offeredTo;
|
|
2563
|
+
// Extension identities (`ext:<name>`, role "extension") are API principals,
|
|
2564
|
+
// never executors. This is the shared chokepoint for every creation path
|
|
2565
|
+
// (send-task, POST /api/tasks, schedules, workflow agent-task nodes, script
|
|
2566
|
+
// runs, apps, Slack fan-out, pre.task.create hook rewrites).
|
|
2567
|
+
for (const id of new Set([options.agentId, options.offeredTo])) {
|
|
2568
|
+
if (!id) continue;
|
|
2569
|
+
const target = await getAgentById(id);
|
|
2570
|
+
if (target && isExtensionAgent(target)) {
|
|
2571
|
+
throw new ExtensionAgentAssignmentError(target);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2478
2574
|
if (options.routingAffinity && targetAgentId && !routingAffinityIsInheritedProvenance) {
|
|
2479
2575
|
const target = await getAgentById(targetAgentId);
|
|
2480
2576
|
if (!target || !isAgentEligibleForTask(target, { routingAffinity: options.routingAffinity })) {
|
|
@@ -2763,6 +2859,9 @@ export async function acceptTask(taskId: string, agentId: string): Promise<Agent
|
|
|
2763
2859
|
const task = await getTaskById(taskId);
|
|
2764
2860
|
if (!task) return null;
|
|
2765
2861
|
const agent = await getAgentById(agentId);
|
|
2862
|
+
// Extension identities never take work, including offers left over from
|
|
2863
|
+
// before creation-time rejection existed.
|
|
2864
|
+
if (isExtensionAgent(agent)) return null;
|
|
2766
2865
|
if (
|
|
2767
2866
|
(task.routingAffinity?.leadOnly || task.routingAffinityInvalid) &&
|
|
2768
2867
|
(!agent || !isAgentEligibleForTask(agent, task))
|
|
@@ -3053,6 +3152,7 @@ export async function claimOfferedTask(taskId: string, agentId: string): Promise
|
|
|
3053
3152
|
if (!task) return null;
|
|
3054
3153
|
if (task.status !== "offered" || task.offeredTo !== agentId) return null;
|
|
3055
3154
|
const agent = await getAgentById(agentId);
|
|
3155
|
+
if (isExtensionAgent(agent)) return null;
|
|
3056
3156
|
if (
|
|
3057
3157
|
(task.routingAffinity?.leadOnly || task.routingAffinityInvalid) &&
|
|
3058
3158
|
(!agent || !isAgentEligibleForTask(agent, task))
|
|
@@ -3291,6 +3391,8 @@ export async function updateAgentProfile(
|
|
|
3291
3391
|
expectedHashes?: ProfileExpectedHashes;
|
|
3292
3392
|
/** Called once per field dropped because its expected hash was stale. */
|
|
3293
3393
|
onConflict?: (conflict: ProfileSyncConflict) => void;
|
|
3394
|
+
/** Only the extension identity lifecycle may grant the reserved extension role. */
|
|
3395
|
+
allowExtensionRole?: boolean;
|
|
3294
3396
|
},
|
|
3295
3397
|
): Promise<Agent | null> {
|
|
3296
3398
|
return await getDbClient().transaction(async (tx) => {
|
|
@@ -3298,6 +3400,11 @@ export async function updateAgentProfile(
|
|
|
3298
3400
|
const current = await tx.get<AgentRow>("SELECT * FROM agents WHERE id = ?", [id]);
|
|
3299
3401
|
if (!current) return null;
|
|
3300
3402
|
|
|
3403
|
+
const roleViolation = reservedRoleViolation(current.role, updates.role, {
|
|
3404
|
+
allowExtensionRole: guard?.allowExtensionRole,
|
|
3405
|
+
});
|
|
3406
|
+
if (roleViolation) throw new ReservedAgentRoleError(roleViolation);
|
|
3407
|
+
|
|
3301
3408
|
// Compare with the old metadata before replacing it. Persist refreshed defaults
|
|
3302
3409
|
// atomically so both running workers and restarted workers see matching blobs.
|
|
3303
3410
|
const previous = rowToAgent(current);
|
|
@@ -5474,6 +5581,8 @@ type ScheduledTaskRow = {
|
|
|
5474
5581
|
nextRunAt: string | null;
|
|
5475
5582
|
createdByAgentId: string | null;
|
|
5476
5583
|
parentTaskId: string | null;
|
|
5584
|
+
requestedDelayMs: number | null;
|
|
5585
|
+
requestedRunAt: string | null;
|
|
5477
5586
|
timezone: string;
|
|
5478
5587
|
consecutiveErrors: number | null;
|
|
5479
5588
|
lastErrorAt: string | null;
|
|
@@ -5524,6 +5633,8 @@ function rowToScheduledTask(row: ScheduledTaskRow): ScheduledTask {
|
|
|
5524
5633
|
nextRunAt: normalizeDate(row.nextRunAt) ?? undefined,
|
|
5525
5634
|
createdByAgentId: row.createdByAgentId ?? undefined,
|
|
5526
5635
|
parentTaskId: row.parentTaskId ?? undefined,
|
|
5636
|
+
requestedDelayMs: row.requestedDelayMs ?? undefined,
|
|
5637
|
+
requestedRunAt: normalizeDate(row.requestedRunAt) ?? undefined,
|
|
5527
5638
|
timezone: row.timezone,
|
|
5528
5639
|
consecutiveErrors: row.consecutiveErrors ?? 0,
|
|
5529
5640
|
lastErrorAt: normalizeDate(row.lastErrorAt) ?? undefined,
|
|
@@ -5675,6 +5786,13 @@ export interface CreateScheduledTaskData {
|
|
|
5675
5786
|
createdByAgentId?: string;
|
|
5676
5787
|
/** Only `defer-task` sets this — the task the schedule's run continues. */
|
|
5677
5788
|
parentTaskId?: string;
|
|
5789
|
+
/**
|
|
5790
|
+
* Only `defer-task` sets these — the delay the caller actually asked for,
|
|
5791
|
+
* kept because the scheduler clears `nextRunAt` once a one_time schedule
|
|
5792
|
+
* fires. Write-once provenance; nothing reads them to decide when to run.
|
|
5793
|
+
*/
|
|
5794
|
+
requestedDelayMs?: number;
|
|
5795
|
+
requestedRunAt?: string;
|
|
5678
5796
|
timezone?: string;
|
|
5679
5797
|
model?: string;
|
|
5680
5798
|
modelTier?: ModelTier;
|
|
@@ -5697,11 +5815,12 @@ export async function createScheduledTask(data: CreateScheduledTaskData): Promis
|
|
|
5697
5815
|
`INSERT INTO scheduled_tasks (
|
|
5698
5816
|
id, "key", name, description, cronExpression, intervalMs, taskTemplate,
|
|
5699
5817
|
taskType, tags, priority, targetAgentId, enabled, nextRunAt,
|
|
5700
|
-
createdByAgentId, parentTaskId,
|
|
5818
|
+
createdByAgentId, parentTaskId, requestedDelayMs, requestedRunAt,
|
|
5819
|
+
timezone, model, modelTier, scheduleType,
|
|
5701
5820
|
targetType, workflowId, scriptName, scriptArgs, params, requiredParams, requires,
|
|
5702
5821
|
createdAt, lastUpdatedAt,
|
|
5703
5822
|
created_by, updated_by
|
|
5704
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
|
|
5823
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
|
|
5705
5824
|
[
|
|
5706
5825
|
id,
|
|
5707
5826
|
normalizeAssetKey(data.key ?? defaultAssetKey("schedule", id)),
|
|
@@ -5718,6 +5837,8 @@ export async function createScheduledTask(data: CreateScheduledTaskData): Promis
|
|
|
5718
5837
|
data.nextRunAt ?? null,
|
|
5719
5838
|
data.createdByAgentId ?? null,
|
|
5720
5839
|
data.parentTaskId ?? null,
|
|
5840
|
+
data.requestedDelayMs ?? null,
|
|
5841
|
+
data.requestedRunAt ?? null,
|
|
5721
5842
|
data.timezone ?? "UTC",
|
|
5722
5843
|
data.model ?? null,
|
|
5723
5844
|
data.modelTier ?? null,
|
|
@@ -12565,9 +12686,10 @@ export async function getSwarmMetrics(): Promise<SwarmMetrics> {
|
|
|
12565
12686
|
|
|
12566
12687
|
const groupCounts = async (
|
|
12567
12688
|
table: string,
|
|
12689
|
+
where?: string,
|
|
12568
12690
|
): Promise<{ total: number; by_status: Record<string, number> }> => {
|
|
12569
12691
|
const rows = await client.query<{ status: string; count: number }>(
|
|
12570
|
-
`SELECT status, COUNT(*) AS count FROM ${table} GROUP BY status`,
|
|
12692
|
+
`SELECT status, COUNT(*) AS count FROM ${table}${where ? ` WHERE ${where}` : ""} GROUP BY status`,
|
|
12571
12693
|
);
|
|
12572
12694
|
const by_status: Record<string, number> = {};
|
|
12573
12695
|
let total = 0;
|
|
@@ -12589,7 +12711,7 @@ export async function getSwarmMetrics(): Promise<SwarmMetrics> {
|
|
|
12589
12711
|
|
|
12590
12712
|
return {
|
|
12591
12713
|
tasks: await groupCounts("agent_tasks"),
|
|
12592
|
-
agents: await groupCounts("agents"),
|
|
12714
|
+
agents: await groupCounts("agents", NOT_EXTENSION_AGENT_SQL),
|
|
12593
12715
|
workflows: { total: workflowRow?.total ?? 0, enabled: workflowRow?.enabled ?? 0 },
|
|
12594
12716
|
pages: { total: pagesRow?.count ?? 0 },
|
|
12595
12717
|
sessions: { active: sessionsRow?.count ?? 0 },
|
package/src/be/extensions/db.ts
CHANGED
|
@@ -20,8 +20,12 @@ export type InstallExtensionArgs = {
|
|
|
20
20
|
createdBy?: string | null;
|
|
21
21
|
activate?: boolean;
|
|
22
22
|
changeReason?: string | null;
|
|
23
|
+
/** HTTP worker upserts must not claim another creator's name. */
|
|
24
|
+
ownerOnly?: string;
|
|
23
25
|
};
|
|
24
26
|
|
|
27
|
+
export class ExtensionOwnershipError extends Error {}
|
|
28
|
+
|
|
25
29
|
export type InstallExtensionResult = {
|
|
26
30
|
extension: Extension;
|
|
27
31
|
isNew: boolean;
|
|
@@ -60,6 +64,19 @@ export async function installExtension(
|
|
|
60
64
|
args.manifest.name,
|
|
61
65
|
]);
|
|
62
66
|
const existing = existingRow ? rowToExtension(existingRow) : null;
|
|
67
|
+
if (existing && args.ownerOnly && existing.createdByAgentId !== args.ownerOnly) {
|
|
68
|
+
throw new ExtensionOwnershipError("Forbidden: extension belongs to another owner");
|
|
69
|
+
}
|
|
70
|
+
if (
|
|
71
|
+
existing?.enabled &&
|
|
72
|
+
args.ownerOnly &&
|
|
73
|
+
((args.config !== undefined && JSON.stringify(args.config) !== existing.configJson) ||
|
|
74
|
+
(args.priority !== undefined && args.priority !== existing.priority))
|
|
75
|
+
) {
|
|
76
|
+
throw new ExtensionOwnershipError(
|
|
77
|
+
"Forbidden: only a lead or operator may change live extension configuration",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
63
80
|
const now = new Date().toISOString();
|
|
64
81
|
|
|
65
82
|
if (!existing) {
|
|
@@ -313,6 +330,7 @@ export async function updateExtensionMeta(
|
|
|
313
330
|
config?: Record<string, unknown>;
|
|
314
331
|
description?: string;
|
|
315
332
|
updatedBy?: string | null;
|
|
333
|
+
ownerOnly?: string;
|
|
316
334
|
},
|
|
317
335
|
): Promise<Extension | null> {
|
|
318
336
|
const sets: string[] = [];
|
|
@@ -329,14 +347,21 @@ export async function updateExtensionMeta(
|
|
|
329
347
|
sets.push("description = ?");
|
|
330
348
|
values.push(args.description);
|
|
331
349
|
}
|
|
332
|
-
if (sets.length === 0) return getExtensionById(id);
|
|
350
|
+
if (sets.length === 0 && !args.ownerOnly) return getExtensionById(id);
|
|
333
351
|
|
|
334
352
|
sets.push("updated_by = ?", "updatedAt = ?");
|
|
335
353
|
values.push(args.updatedBy ?? null, new Date().toISOString(), id);
|
|
354
|
+
// The enabled check must be part of the write: a lead may enable the draft
|
|
355
|
+
// after the HTTP handler has authorized it but before this UPDATE executes.
|
|
356
|
+
const ownerCondition = args.ownerOnly ? " AND createdByAgentId = ? AND enabled = 0" : "";
|
|
357
|
+
if (args.ownerOnly) values.push(args.ownerOnly);
|
|
336
358
|
const row = await getDbClient().get<ExtensionRow>(
|
|
337
|
-
`UPDATE extensions SET ${sets.join(", ")} WHERE id =
|
|
359
|
+
`UPDATE extensions SET ${sets.join(", ")} WHERE id = ?${ownerCondition} RETURNING *`,
|
|
338
360
|
values,
|
|
339
361
|
);
|
|
362
|
+
if (!row && args.ownerOnly) {
|
|
363
|
+
throw new ExtensionOwnershipError("Forbidden: extension must be your own disabled draft");
|
|
364
|
+
}
|
|
340
365
|
return row ? rowToExtension(row) : null;
|
|
341
366
|
}
|
|
342
367
|
|
|
@@ -401,8 +426,14 @@ export async function recordExtensionFailure(
|
|
|
401
426
|
return row ? rowToExtension(row) : null;
|
|
402
427
|
}
|
|
403
428
|
|
|
404
|
-
export async function deleteExtension(id: string): Promise<boolean> {
|
|
405
|
-
const result = await getDbClient().run(
|
|
429
|
+
export async function deleteExtension(id: string, ownerOnly?: string): Promise<boolean> {
|
|
430
|
+
const result = await getDbClient().run(
|
|
431
|
+
`DELETE FROM extensions WHERE id = ?${ownerOnly ? " AND createdByAgentId = ? AND enabled = 0" : ""}`,
|
|
432
|
+
ownerOnly ? [id, ownerOnly] : [id],
|
|
433
|
+
);
|
|
434
|
+
if (result.changes === 0 && ownerOnly) {
|
|
435
|
+
throw new ExtensionOwnershipError("Forbidden: extension must be your own disabled draft");
|
|
436
|
+
}
|
|
406
437
|
return result.changes > 0;
|
|
407
438
|
}
|
|
408
439
|
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* touch `bun:sqlite` or `src/be/db`. The boundary check enforces it.
|
|
18
18
|
*/
|
|
19
19
|
import { z } from "zod";
|
|
20
|
+
import { getMemoryRaterNames } from "../../../utils/memory-raters";
|
|
20
21
|
import { ClaudeCliLlmRaterClient, type LlmRaterClient, type LlmRaterResult } from "./llm-client";
|
|
21
22
|
import {
|
|
22
23
|
type MemoryRater,
|
|
@@ -246,12 +247,7 @@ ${memoryBlock}`;
|
|
|
246
247
|
* path — strict opt-in so existing deployments are byte-identical when unset.
|
|
247
248
|
*/
|
|
248
249
|
export function isLlmRaterEnabled(): boolean {
|
|
249
|
-
|
|
250
|
-
if (!raw || raw.trim() === "") return false;
|
|
251
|
-
return raw
|
|
252
|
-
.split(",")
|
|
253
|
-
.map((s) => s.trim())
|
|
254
|
-
.includes("llm");
|
|
250
|
+
return getMemoryRaterNames().includes("llm");
|
|
255
251
|
}
|
|
256
252
|
|
|
257
253
|
/** Memory snapshot returned by `GET /api/memory/retrievals`. */
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { MemoryRater, RatingEvent } from "./types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* MEMORY_RATERS is
|
|
6
|
-
* byte-identically to pre-rater builds.
|
|
4
|
+
* Disabled rater. Emits no events, makes no DB calls. Selected when
|
|
5
|
+
* MEMORY_RATERS is explicitly empty or no configured names are recognized.
|
|
7
6
|
*/
|
|
8
7
|
export class NoopRater implements MemoryRater {
|
|
9
8
|
readonly name = "noop";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getMemoryRaterNames } from "../../../utils/memory-raters";
|
|
1
2
|
import { ExplicitSelfRatingRater } from "./explicit-self";
|
|
2
3
|
import { ImplicitCitationRater } from "./implicit-citation";
|
|
3
4
|
import { LlmRater } from "./llm";
|
|
@@ -8,7 +9,8 @@ import type { MemoryRater } from "./types";
|
|
|
8
9
|
* Plan: thoughts/taras/plans/2026-05-05-memory-rater-v1.5/step-1.md §4
|
|
9
10
|
*
|
|
10
11
|
* `MEMORY_RATERS` env — comma-separated list of rater names. Defaults to
|
|
11
|
-
* `
|
|
12
|
+
* `implicit-citation,explicit-self` when unset. An explicit empty value selects
|
|
13
|
+
* `[NoopRater]`.
|
|
12
14
|
*
|
|
13
15
|
* `MEMORY_RATER_WEIGHTS` env — optional `name:multiplier,...` overrides.
|
|
14
16
|
* Multiplier is applied to every emitted RatingEvent.weight before
|
|
@@ -43,15 +45,7 @@ const FACTORIES: Record<string, RaterFactory> = {
|
|
|
43
45
|
export const SERVER_RATERS = new Set<string>(["implicit-citation"]);
|
|
44
46
|
|
|
45
47
|
export function getRegisteredRaters(): MemoryRater[] {
|
|
46
|
-
const
|
|
47
|
-
if (!raw || raw.trim() === "") {
|
|
48
|
-
return [new NoopRater()];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const names = raw
|
|
52
|
-
.split(",")
|
|
53
|
-
.map((s) => s.trim())
|
|
54
|
-
.filter((s) => s.length > 0);
|
|
48
|
+
const names = getMemoryRaterNames();
|
|
55
49
|
|
|
56
50
|
const raters: MemoryRater[] = [];
|
|
57
51
|
for (const name of names) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- Persist what `defer-task` was actually ASKED for, alongside the derived
|
|
2
|
+
-- `nextRunAt`. The scheduler nulls `nextRunAt` once a one_time schedule fires,
|
|
3
|
+
-- so without these columns the requested-delay distribution is unrecoverable
|
|
4
|
+
-- after the wake-up runs.
|
|
5
|
+
--
|
|
6
|
+
-- Exactly one of the two is ever set (defer-task rejects both/neither), and
|
|
7
|
+
-- neither is read by the scheduler — they are write-once provenance.
|
|
8
|
+
ALTER TABLE scheduled_tasks ADD COLUMN requestedDelayMs INTEGER;
|
|
9
|
+
ALTER TABLE scheduled_tasks ADD COLUMN requestedRunAt TEXT;
|
|
10
|
+
|
|
11
|
+
-- Marks a task that ENDED in a deferral, so renderers can tell "parked" from
|
|
12
|
+
-- "done" on a row whose status is `completed` either way.
|
|
13
|
+
--
|
|
14
|
+
-- The `deferred` tag cannot answer this: `defer-task` adds it to the deferring
|
|
15
|
+
-- task, and the wake-up task it schedules inherits the same tag — so a wake-up
|
|
16
|
+
-- child that simply finished is tag-identical to one that deferred again.
|
|
17
|
+
ALTER TABLE agent_tasks ADD COLUMN deferredAt TEXT;
|
|
18
|
+
|
|
19
|
+
-- Set once a deferral's ⏳ outcome card has been rewritten into its resolved
|
|
20
|
+
-- state, so the resolution pass rewrites each card exactly once instead of on
|
|
21
|
+
-- every render tick.
|
|
22
|
+
ALTER TABLE slack_messages ADD COLUMN deferral_resolved_at TEXT;
|
|
23
|
+
|
|
24
|
+
-- Set alongside `deferral_resolved_at` when the rewrite was given up on
|
|
25
|
+
-- (Slack refused it, or it kept failing). The wake-up's answer then never
|
|
26
|
+
-- reached the ⏳ card, so the wake-up posts its own outcome card instead.
|
|
27
|
+
ALTER TABLE slack_messages ADD COLUMN deferral_abandoned_at TEXT;
|
package/src/be/models-catalog.ts
CHANGED
|
@@ -34,6 +34,8 @@ export type CatalogProviderId = (typeof CATALOG_PROVIDER_IDS)[number];
|
|
|
34
34
|
* of the picker between models.dev listing gaps.
|
|
35
35
|
*/
|
|
36
36
|
export const PINNED_MODELSDEV_ENTRIES = [
|
|
37
|
+
// Official launch metadata; retain until models.dev catches up. Verified 2026-09-22.
|
|
38
|
+
"anthropic/claude-opus-5-5",
|
|
37
39
|
"anthropic/claude-mythos-5",
|
|
38
40
|
"anthropic/claude-sonnet-5",
|
|
39
41
|
"amazon-bedrock/anthropic.claude-sonnet-5",
|