@desplega.ai/agent-swarm 1.156.0 → 1.158.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-adapter-c1msptrw.js → acp-adapter-xaq4n6pc.js} +1 -1
- package/dist/{actions-wd41qtdc.js → actions-p9hz5yat.js} +15 -15
- package/dist/{app-tz2qrnah.js → app-g26wdcz6.js} +4 -4
- package/dist/{artifact-td4q4dch.js → artifact-3v5t9jtg.js} +1 -1
- package/dist/{assistant-c3q1zj2t.js → assistant-wydfqkb3.js} +16 -16
- package/dist/{boot-reembed-0be1krkm.js → boot-reembed-g5ckg9yb.js} +6 -6
- package/dist/{boot-reembed-r00j03f3.js → boot-reembed-se8zmdcp.js} +5 -5
- package/dist/{boot-scrub-logs-9j4hd0vw.js → boot-scrub-logs-wfxqan61.js} +3 -3
- package/dist/{seed-pricing-ncgxz1r2.js → boot-seeds-s3q01xj3.js} +10 -10
- package/dist/{claude-adapter-112w28qc.js → claude-adapter-tv2x4f36.js} +7 -7
- package/dist/{claude-managed-adapter-ea2aga6w.js → claude-managed-adapter-41b2s7fd.js} +2 -2
- package/dist/{claude-sdk-session-k7pb5kfz.js → claude-sdk-session-t442qz6f.js} +8 -8
- package/dist/{cli-e781c6f9.js → cli-0xjkj1j7.js} +1 -0
- package/dist/{cli-58rgvkt4.js → cli-1hkrch3k.js} +2 -2
- package/dist/{cli-mx3bzbv8.js → cli-20cj37vt.js} +1968 -44
- package/dist/{cli-h0wdadsx.js → cli-4198qc9v.js} +7 -3
- package/dist/{cli-24w39x6n.js → cli-47m6dkd3.js} +362 -126
- package/dist/{cli-90x9ewbz.js → cli-53sz8gkz.js} +1 -1
- package/dist/{cli-005pze6s.js → cli-5d10vd2m.js} +1 -1
- package/dist/{cli-08n8jxcw.js → cli-65hjmev5.js} +57 -17
- package/dist/{cli-scwq51gn.js → cli-68w09sjw.js} +7 -1
- package/dist/{cli-7j2098n5.js → cli-7gtvvpbh.js} +4 -4
- package/dist/{cli-b05d7szr.js → cli-7s3syk22.js} +18 -11
- package/dist/{cli-9w4ex8vh.js → cli-8qmme6vd.js} +2 -2
- package/dist/{cli-28wkjpdp.js → cli-987hg0aa.js} +2 -2
- package/dist/{cli-7pgcpcrg.js → cli-9httdpyq.js} +1 -1
- package/dist/{cli-jk26c1sz.js → cli-aky8tctm.js} +26 -93
- package/dist/{cli-865swy5p.js → cli-c07r9a2m.js} +3 -2
- package/dist/{cli-nyqza443.js → cli-fgqxwrpv.js} +291 -12
- package/dist/{cli-9pyah9c4.js → cli-ftfvggzx.js} +633 -166
- package/dist/{cli-ytxs7mn1.js → cli-g8djp6qe.js} +17 -0
- package/dist/{cli-avkd6mqv.js → cli-h967nb6r.js} +551 -0
- package/dist/{cli-y8bfxjtw.js → cli-j55n64b6.js} +23 -22
- package/dist/{cli-7vj52450.js → cli-j57qkrpq.js} +86 -28
- package/dist/{cli-tjmghmm5.js → cli-q67y4wzm.js} +6 -3
- package/dist/{cli-n6zq3kbd.js → cli-rj19hc34.js} +1 -1
- package/dist/{cli-tp0d7vzb.js → cli-rjehvrts.js} +4 -4
- package/dist/{cli-s8hpxeg4.js → cli-rqz8qbw7.js} +245 -75
- package/dist/{cli-p37sg0a9.js → cli-s8eet254.js} +1 -1
- package/dist/cli-t1ze64h3.js +811 -0
- package/dist/{cli-hc2fkhdv.js → cli-ws6rqycm.js} +3 -2
- package/dist/{cli-t1cd3mhp.js → cli-wscdvcqz.js} +13 -5
- package/dist/{cli-xa4j64zh.js → cli-x24pshtq.js} +5 -5
- package/dist/{cli-nbp6m6gp.js → cli-xberxa1r.js} +51 -26
- package/dist/{cli-szcyv1mm.js → cli-xhaeg2nc.js} +1 -1
- package/dist/cli.js +30 -16
- package/dist/{codex-adapter-g4tj1f2g.js → codex-adapter-cnm8wk11.js} +5 -5
- package/dist/{codex-session-runner-ad8ha7zt.js → codex-session-runner-gfg547zj.js} +5 -5
- package/dist/{commands-z3gd8ppc.js → commands-e3bn0p75.js} +3 -3
- package/dist/{db-bezm97r9.js → db-03v9kzr2.js} +17 -3
- package/dist/{dispatcher-a97hc33y.js → dispatcher-nrmg8yht.js} +14 -14
- package/dist/{dsh-adapter-1dwg97zv.js → dsh-adapter-k03nfqa6.js} +2 -2
- package/dist/{handlers-wtdy1x02.js → handlers-aqcedbyb.js} +17 -17
- package/dist/{hook-7edgmzng.js → hook-7vkskkre.js} +19 -11
- package/dist/{hook-fvs58a5b.js → hook-ybvmrmhq.js} +4 -4
- package/dist/{http-phnf93hp.js → http-2eqa8t24.js} +432 -195
- package/dist/{index-7x131mmf.js → index-1yks3qcr.js} +14 -14
- package/dist/{index-1fktvwxc.js → index-a3vn96n8.js} +31 -28
- package/dist/{index-yp8rasxk.js → index-c6zsv1bd.js} +5 -5
- package/dist/{index-50jxgw0d.js → index-chgdkq36.js} +14 -14
- package/dist/{index-7jwk7nsm.js → index-x71bvq0p.js} +15 -15
- package/dist/{keepalive-nnz07ebd.js → keepalive-vtkf5n99.js} +5 -5
- package/dist/{lead-mg7qcgww.js → lead-h3sp357x.js} +26 -26
- package/dist/{maintenance-ywmnqrpx.js → maintenance-jzeqtk3q.js} +6 -6
- package/dist/{oauth-refresh-sweep-c63sqfs1.js → oauth-refresh-sweep-qp3dzmdp.js} +4 -4
- package/dist/{onboard-2zj8sqkq.js → onboard-tmsp0qbz.js} +278 -81
- package/dist/{opencode-adapter-z64xjzec.js → opencode-adapter-x6x2da1z.js} +3 -3
- package/dist/{otel-impl-jgdfm26f.js → otel-impl-9mz73q65.js} +416 -301
- package/dist/{pi-mono-adapter-2b7mw16r.js → pi-mono-adapter-m6dvv0dz.js} +2 -2
- package/dist/{pricing-refresh-3whntb69.js → pricing-refresh-v1cbr9te.js} +5 -5
- package/dist/{rbac-roles-3zptcc3p.js → rbac-roles-c9wqz83x.js} +4 -4
- package/dist/{render-v2-dbvpyysc.js → render-v2-gryrh9h8.js} +4 -4
- package/dist/{setup-qwx0j8z9.js → setup-q9720bt6.js} +2 -2
- package/dist/{virtual-modules-8qheft18.js → virtual-modules-3htpgn8x.js} +1 -1
- package/dist/{work-objects-sjbcmvep.js → work-objects-b76x7deb.js} +17 -17
- package/dist/{worker-xezqdkb7.js → worker-6vrxbv41.js} +26 -26
- package/openapi.json +1626 -629
- package/package.json +3 -2
- package/src/apps/catalog.generated.json +229 -0
- package/src/apps/page-validator.ts +71 -16
- package/src/apps/row-store.ts +66 -11
- package/src/apps/sync.ts +59 -17
- package/src/artifact-sdk/browser-sdk.ts +436 -2
- package/src/be/approval-sweeps.ts +109 -0
- package/src/be/boot-seeds.ts +40 -0
- package/src/be/chunking.ts +1 -1
- package/src/be/db/agents.ts +20 -0
- package/src/be/db/approvals.ts +78 -0
- package/src/be/db/tasks/read.ts +197 -39
- package/src/be/db/tasks/write.ts +41 -9
- package/src/be/db-client.ts +107 -48
- package/src/be/db.ts +219 -76
- package/src/be/events.ts +33 -1
- package/src/be/memory/graph-expansion.ts +10 -3
- package/src/be/memory/providers/sqlite-store.ts +55 -13
- package/src/be/memory/raters/retrieval.ts +5 -2
- package/src/be/memory/reranker.ts +26 -2
- package/src/be/memory/types.ts +2 -0
- package/src/be/migrations/166_approval_request_auto_cancellation.sql +9 -0
- package/src/be/migrations/171_memory_retrieval_relevance.sql +8 -0
- package/src/be/migrations/175_workflow_runs_list_index.sql +11 -0
- package/src/be/migrations/177_events_event_agent_created_index.sql +11 -0
- package/src/be/models-catalog.ts +2 -0
- package/src/be/modelsdev-cache.json +1857 -0
- package/src/be/modelsdev-cache.ts +14 -1
- package/src/be/pricing-normalize.ts +2 -0
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-pricing.ts +3 -1
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/steering.ts +53 -3
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/be/task-citations.ts +29 -0
- package/src/cli.tsx +14 -1
- package/src/commands/onboard/compose-generator.ts +8 -3
- package/src/commands/onboard/container-engine.ts +220 -0
- package/src/commands/onboard/manifest.ts +1 -0
- package/src/commands/onboard/steps/health-check.tsx +4 -1
- package/src/commands/onboard/steps/prereq-check.tsx +50 -54
- package/src/commands/onboard/steps/start.tsx +31 -16
- package/src/commands/onboard/types.ts +7 -2
- package/src/commands/onboard.tsx +39 -4
- package/src/commands/profile-sync.ts +79 -26
- package/src/commands/runner.ts +8 -1
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/heartbeat/heartbeat.ts +263 -103
- package/src/hooks/hook.ts +21 -12
- package/src/http/agents.ts +35 -0
- package/src/http/approval-requests.ts +136 -64
- package/src/http/apps.ts +14 -3
- package/src/http/config.ts +15 -1
- package/src/http/events.ts +62 -2
- package/src/http/index.ts +6 -4
- package/src/http/memory.ts +21 -7
- package/src/http/schedules.ts +1 -1
- package/src/http/session-data.ts +9 -4
- package/src/http/sessions.ts +25 -18
- package/src/http/stats.ts +19 -7
- package/src/http/status.ts +11 -1
- package/src/http/tasks.ts +40 -8
- package/src/http/webhooks.ts +67 -46
- package/src/http/workflows.ts +15 -20
- package/src/linear/outbound.ts +37 -15
- package/src/otel-impl.ts +151 -1
- package/src/otel.ts +14 -3
- package/src/prompts/memories.ts +19 -2
- package/src/providers/claude-managed-models.ts +8 -0
- package/src/providers/codex-agents-md.ts +140 -17
- package/src/providers/modelsdev-reasoning.json +551 -0
- package/src/rbac/legacy-policy.ts +16 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/deferred-task-waits.ts +28 -0
- package/src/scheduler/schedule-task.ts +3 -0
- package/src/scheduler/scheduler.ts +14 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/swarm-sdk.ts +9 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +12 -7
- package/src/slack/app.ts +35 -4
- package/src/slack/connection-state.ts +17 -0
- package/src/tasks/context-key.ts +1 -0
- package/src/tasks/worker-follow-up.ts +6 -2
- package/src/tests/agent-task-activity.test.ts +74 -0
- package/src/tests/agents-list-model-display.test.ts +6 -1
- package/src/tests/approval-auto-cancellation-migration.test.ts +104 -0
- package/src/tests/approval-requests.test.ts +395 -0
- package/src/tests/approval-sweeps-config.test.ts +47 -0
- package/src/tests/approval-sweeps.test.ts +508 -0
- package/src/tests/apps-kibo-blocks.test.ts +77 -0
- package/src/tests/apps-spike.test.ts +56 -1
- package/src/tests/apps-sync-engine.test.ts +20 -9
- package/src/tests/boot-seeds.test.ts +170 -0
- package/src/tests/cancel-approval-request-tool.test.ts +115 -0
- package/src/tests/claude-managed-adapter.test.ts +6 -0
- package/src/tests/codex-adapter.test.ts +126 -1
- package/src/tests/config-key-filter.test.ts +80 -0
- package/src/tests/context-window.test.ts +1 -0
- package/src/tests/db-client-busy-retry.test.ts +96 -0
- package/src/tests/db-client.test.ts +60 -1
- package/src/tests/db-task-read-contract.test.ts +10 -0
- package/src/tests/db-task-write-contract.test.ts +9 -0
- package/src/tests/deferred-schedule-reaper.test.ts +193 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/events-http.test.ts +103 -0
- package/src/tests/extensions-catalog.test.ts +22 -17
- package/src/tests/extensions-pre-task.test.ts +107 -0
- package/src/tests/fixtures/kibo-blocks-definition.json.txt +105 -0
- package/src/tests/follow-up-config-inheritance.test.ts +260 -0
- package/src/tests/heartbeat-orphan-resume.test.ts +84 -0
- package/src/tests/heartbeat-reboot-live.test.ts +95 -0
- package/src/tests/heartbeat-stall-cas.test.ts +80 -0
- package/src/tests/heartbeat.test.ts +99 -3
- package/src/tests/hook-mcp-config.test.ts +29 -0
- package/src/tests/http-api-integration.test.ts +16 -2
- package/src/tests/kv-storage.test.ts +62 -0
- package/src/tests/linear-outbound-sync.test.ts +120 -5
- package/src/tests/list-endpoint-slimming.test.ts +91 -0
- package/src/tests/memory-graph-organic-route.test.ts +155 -0
- package/src/tests/memory-http-recall-gating.test.ts +14 -4
- package/src/tests/memory-hybrid.test.ts +161 -1
- package/src/tests/memory-prompt-recall-crowdout.test.ts +297 -0
- package/src/tests/memory.test.ts +22 -0
- package/src/tests/onboard-container-engine.test.ts +240 -0
- package/src/tests/otel-event-loop-and-logs.test.ts +99 -0
- package/src/tests/pricing-routes.test.ts +14 -0
- package/src/tests/profile-sync-warning.test.ts +8 -4
- package/src/tests/promote-draft-task-route.test.ts +19 -0
- package/src/tests/provider-registration.test.ts +296 -42
- package/src/tests/rbac-engine.test.ts +30 -0
- package/src/tests/reasoning-effort.test.ts +8 -0
- package/src/tests/script-connections.test.ts +7 -0
- package/src/tests/sdk-allowlist.test.ts +39 -0
- package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
- package/src/tests/sessions-detail-batch.test.ts +250 -0
- package/src/tests/sessions.test.ts +118 -0
- package/src/tests/slack-config.test.ts +22 -0
- package/src/tests/sonnet-5-5-registry.test.ts +44 -0
- package/src/tests/status.test.ts +36 -1
- package/src/tests/swarm-diff.test.ts +316 -10
- package/src/tests/task-completion-idempotency.test.ts +2 -1
- package/src/tests/tasks-requested-by-filter.test.ts +3 -1
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/ui-list-slim-projections.test.ts +158 -0
- package/src/tests/webhooks-task-blocked.test.ts +127 -0
- package/src/tests/workflow-approval-recovery.test.ts +203 -0
- package/src/tests/workflow-hitl-routing.test.ts +22 -1
- package/src/tests/workflow-hitl-slack-card.test.ts +52 -0
- package/src/tests/workflow-http-v2.test.ts +99 -8
- package/src/tests/workflow-retry-validation.test.ts +56 -0
- package/src/tests/workflow-tla-races.test.ts +389 -0
- package/src/tests/workflow-validation-port-routing.test.ts +113 -1
- package/src/tools/cancel-approval-request.ts +57 -0
- package/src/tools/memory-search.ts +1 -0
- package/src/tools/request-human-input.ts +4 -2
- package/src/tools/schedules/list-schedules.ts +4 -2
- package/src/tools/send-task.ts +4 -0
- package/src/tools/templates.ts +19 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/workflows/list-workflow-runs.ts +1 -0
- package/src/types.ts +38 -0
- package/src/utils/context-window.ts +1 -0
- package/src/workflows/approval-cancel.ts +152 -0
- package/src/workflows/approval-notifications.ts +84 -0
- package/src/workflows/checkpoint.ts +10 -5
- package/src/workflows/definition.ts +21 -0
- package/src/workflows/engine.ts +152 -21
- package/src/workflows/executors/human-in-the-loop.ts +1 -1
- package/src/workflows/recovery.ts +100 -45
- package/src/workflows/resume.ts +49 -18
- package/src/workflows/retry-poller.ts +71 -21
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/github-sender-allowlist/README.md +18 -0
- package/templates/extensions/github-sender-allowlist/hooks.ts +55 -0
- package/templates/extensions/github-sender-allowlist/manifest.json +8 -0
- package/templates/skills/apps/content.md +20 -1
- package/templates/skills/code-quality/content.md +26 -1
- package/templates/skills/pages/content.md +16 -4
- package/templates/skills/pages/files/examples/multiplayer-board.html +3 -12
- package/templates/skills/scheduling/content.md +1 -1
- package/dist/cli-2daksqps.js +0 -378
- package/dist/rbac-roles-3zw68x72.js +0 -37
package/src/http/agents.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
deleteSwarmConfigByKey,
|
|
8
8
|
EXTENSION_AGENT_ROLE,
|
|
9
9
|
getAgentById,
|
|
10
|
+
getAgentDailyTaskCounts,
|
|
10
11
|
getAgentWithTasks,
|
|
11
12
|
getAllAgents,
|
|
12
13
|
getAllAgentsWithTasks,
|
|
@@ -416,6 +417,27 @@ const listAgentRuntimeInstances = route({
|
|
|
416
417
|
},
|
|
417
418
|
});
|
|
418
419
|
|
|
420
|
+
const getAgentTaskActivity = route({
|
|
421
|
+
method: "get",
|
|
422
|
+
path: "/api/agents/{id}/task-activity",
|
|
423
|
+
pattern: ["api", "agents", null, "task-activity"],
|
|
424
|
+
summary: "Daily task counts for an agent",
|
|
425
|
+
description:
|
|
426
|
+
"Number of tasks created for the agent per UTC day over the last `days` days (default 365, max 730), oldest first. Days with no tasks are omitted. Backs the activity heatmap on the agent page.",
|
|
427
|
+
tags: ["Agents"],
|
|
428
|
+
params: z.object({ id: z.string() }),
|
|
429
|
+
query: z.object({ days: z.coerce.number().int().min(1).max(730).optional() }),
|
|
430
|
+
responses: {
|
|
431
|
+
200: {
|
|
432
|
+
description: "Daily task counts",
|
|
433
|
+
schema: z.object({
|
|
434
|
+
days: z.array(z.object({ date: z.string(), count: z.number().int() })),
|
|
435
|
+
}),
|
|
436
|
+
},
|
|
437
|
+
404: { description: "Agent not found" },
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
|
|
419
441
|
const ContentHashSchema = z.string().regex(/^[0-9a-f]{64}$/, "sha256 hex");
|
|
420
442
|
|
|
421
443
|
const ProfileSyncRejectionSchema = z.object({
|
|
@@ -820,6 +842,19 @@ export async function handleAgentsRest(
|
|
|
820
842
|
return true;
|
|
821
843
|
}
|
|
822
844
|
|
|
845
|
+
if (getAgentTaskActivity.match(req.method, pathSegments)) {
|
|
846
|
+
const parsed = await getAgentTaskActivity.parse(req, res, pathSegments, queryParams);
|
|
847
|
+
if (!parsed) return true;
|
|
848
|
+
if (!(await getAgentById(parsed.params.id))) {
|
|
849
|
+
jsonError(res, "Agent not found", 404);
|
|
850
|
+
return true;
|
|
851
|
+
}
|
|
852
|
+
getAgentTaskActivity.respond(res, 200, {
|
|
853
|
+
days: await getAgentDailyTaskCounts(parsed.params.id, parsed.query.days ?? 365),
|
|
854
|
+
});
|
|
855
|
+
return true;
|
|
856
|
+
}
|
|
857
|
+
|
|
823
858
|
if (updateAgentProfileRoute.match(req.method, pathSegments)) {
|
|
824
859
|
const parsed = await updateAgentProfileRoute.parse(req, res, pathSegments, queryParams);
|
|
825
860
|
if (!parsed) return true;
|
|
@@ -4,16 +4,19 @@ import { resolveTaskAuditUserId } from "../be/audit-user";
|
|
|
4
4
|
import {
|
|
5
5
|
type ApprovalRequest,
|
|
6
6
|
createApprovalRequest,
|
|
7
|
-
|
|
7
|
+
getAgentById,
|
|
8
8
|
getApprovalRequestById,
|
|
9
|
-
|
|
9
|
+
getDbClient,
|
|
10
10
|
getWorkflowRun,
|
|
11
11
|
getWorkflowRunStep,
|
|
12
|
+
listApprovalRequestSummaries,
|
|
12
13
|
listApprovalRequests,
|
|
13
14
|
resolveApprovalRequest,
|
|
14
15
|
} from "../be/db";
|
|
15
|
-
import {
|
|
16
|
+
import type { RbacPrincipal } from "../rbac";
|
|
16
17
|
import { getRequestAuth } from "../utils/request-auth-context";
|
|
18
|
+
import { cancelApprovalRequest } from "../workflows/approval-cancel";
|
|
19
|
+
import { createApprovalFollowUpTask } from "../workflows/approval-notifications";
|
|
17
20
|
import { workflowEventBus } from "../workflows/event-bus";
|
|
18
21
|
import { route } from "./route-def";
|
|
19
22
|
import { jsonError } from "./utils";
|
|
@@ -90,6 +93,15 @@ const ApprovalRequestSchema = z.object({
|
|
|
90
93
|
updatedAt: z.string(),
|
|
91
94
|
});
|
|
92
95
|
|
|
96
|
+
/** Slim list row (`?fields=slim`) — see `ApprovalRequestSummary`. */
|
|
97
|
+
const ApprovalRequestSummarySchema = ApprovalRequestSchema.omit({
|
|
98
|
+
questions: true,
|
|
99
|
+
approvers: true,
|
|
100
|
+
responses: true,
|
|
101
|
+
resolutionReason: true,
|
|
102
|
+
notificationChannels: true,
|
|
103
|
+
}).extend({ questionCount: z.number().int() });
|
|
104
|
+
|
|
93
105
|
/**
|
|
94
106
|
* Reshapes a DB `ApprovalRequest` row for `respond()` — identical values,
|
|
95
107
|
* narrowed from the DB layer's `unknown` fields to the precise wire shape
|
|
@@ -196,7 +208,14 @@ const createRoute = route({
|
|
|
196
208
|
workflowRunId: z.string().uuid().optional(),
|
|
197
209
|
workflowRunStepId: z.string().uuid().optional(),
|
|
198
210
|
sourceTaskId: z.string().uuid().optional(),
|
|
199
|
-
timeoutSeconds: z
|
|
211
|
+
timeoutSeconds: z
|
|
212
|
+
.number()
|
|
213
|
+
.int()
|
|
214
|
+
.min(1)
|
|
215
|
+
.optional()
|
|
216
|
+
.describe(
|
|
217
|
+
"Seconds until the request expires. After that the request becomes 'timeout' and you get a hitl-follow-up task. A request with no timeout is cancelled after APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS days (default 7).",
|
|
218
|
+
),
|
|
200
219
|
notifications: z
|
|
201
220
|
.array(
|
|
202
221
|
z.object({
|
|
@@ -256,21 +275,58 @@ const respondRoute = route({
|
|
|
256
275
|
auth: { apiKey: true },
|
|
257
276
|
});
|
|
258
277
|
|
|
278
|
+
const cancelRoute = route({
|
|
279
|
+
method: "post",
|
|
280
|
+
path: "/api/approval-requests/{id}/cancel",
|
|
281
|
+
pattern: ["api", "approval-requests", null, "cancel"],
|
|
282
|
+
summary: "Cancel a pending approval request",
|
|
283
|
+
tags: ["ApprovalRequests"],
|
|
284
|
+
params: z.object({ id: z.string().uuid() }),
|
|
285
|
+
body: z.object({ reason: z.string().max(500).optional() }),
|
|
286
|
+
responses: {
|
|
287
|
+
200: {
|
|
288
|
+
description:
|
|
289
|
+
"Request cancelled, or already cancelled. A request that gates a running or waiting workflow run cancels that run too.",
|
|
290
|
+
schema: z.object({
|
|
291
|
+
approvalRequest: ApprovalRequestSchema,
|
|
292
|
+
alreadyCancelled: z.boolean(),
|
|
293
|
+
runCancelled: z.boolean(),
|
|
294
|
+
}),
|
|
295
|
+
},
|
|
296
|
+
403: { description: "Caller may not cancel this request" },
|
|
297
|
+
404: { description: "Not found" },
|
|
298
|
+
409: {
|
|
299
|
+
description: "Already resolved with approved, rejected, or timeout, or its expiresAt passed",
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
auth: { apiKey: true },
|
|
303
|
+
rbac: { permission: "approval.cancel.any" },
|
|
304
|
+
});
|
|
305
|
+
|
|
259
306
|
const listRoute = route({
|
|
260
307
|
method: "get",
|
|
261
308
|
path: "/api/approval-requests",
|
|
262
309
|
pattern: ["api", "approval-requests"],
|
|
263
310
|
summary: "List approval requests with optional filters",
|
|
311
|
+
description:
|
|
312
|
+
"Returns full approval requests by default. Pass `fields=slim` for the list-view shape: `questions`, `approvers`, `responses`, `resolutionReason` and `notificationChannels` are dropped and `questionCount` is added. Fetch one request in full via `GET /api/approval-requests/{id}`.",
|
|
264
313
|
tags: ["ApprovalRequests"],
|
|
265
314
|
query: z.object({
|
|
266
315
|
status: z.string().optional(),
|
|
267
316
|
workflowRunId: z.string().optional(),
|
|
268
317
|
limit: z.coerce.number().optional(),
|
|
318
|
+
/** `slim` is the list-view shape; default is full. */
|
|
319
|
+
fields: z.enum(["full", "slim"]).optional(),
|
|
269
320
|
}),
|
|
270
321
|
responses: {
|
|
271
322
|
200: {
|
|
272
323
|
description: "List of approval requests",
|
|
273
|
-
schema: z.object({
|
|
324
|
+
schema: z.object({
|
|
325
|
+
approvalRequests: z.union([
|
|
326
|
+
z.array(ApprovalRequestSchema),
|
|
327
|
+
z.array(ApprovalRequestSummarySchema),
|
|
328
|
+
]),
|
|
329
|
+
}),
|
|
274
330
|
},
|
|
275
331
|
},
|
|
276
332
|
auth: { apiKey: true },
|
|
@@ -305,6 +361,24 @@ export async function handleApprovalRequests(
|
|
|
305
361
|
return true;
|
|
306
362
|
}
|
|
307
363
|
|
|
364
|
+
// A late answer never resolves the request: it becomes timeout at once.
|
|
365
|
+
// A workflow run then routes on its timeout port on the next heartbeat
|
|
366
|
+
// tick through getStuckApprovalRuns, so no approval.resolved is emitted.
|
|
367
|
+
if (existing.expiresAt && new Date(existing.expiresAt) < new Date()) {
|
|
368
|
+
// The status change and the follow-up task commit together.
|
|
369
|
+
await getDbClient().transaction(async () => {
|
|
370
|
+
const timedOut = await resolveApprovalRequest(existing.id, {
|
|
371
|
+
status: "timeout",
|
|
372
|
+
resolutionReason: `Timed out: the answer arrived after the deadline ${existing.expiresAt}`,
|
|
373
|
+
});
|
|
374
|
+
if (timedOut && !existing.workflowRunId) {
|
|
375
|
+
await createApprovalFollowUpTask(timedOut, "hitl.timeout");
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
jsonError(res, `Approval request expired at ${existing.expiresAt}`, 409);
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
|
|
308
382
|
const unavailableReason = await getWorkflowApprovalUnavailableReason(existing);
|
|
309
383
|
if (unavailableReason) {
|
|
310
384
|
jsonError(res, unavailableReason, 409);
|
|
@@ -367,44 +441,36 @@ export async function handleApprovalRequests(
|
|
|
367
441
|
|
|
368
442
|
// For standalone (non-workflow) requests, create a follow-up task
|
|
369
443
|
// so the requesting agent is notified of the human's response
|
|
370
|
-
|
|
371
|
-
const sourceTask = await getTaskById(updated.sourceTaskId);
|
|
372
|
-
if (sourceTask) {
|
|
373
|
-
// Format responses for the template
|
|
374
|
-
const formattedResponses = formatResponses(
|
|
375
|
-
updated.questions as Array<{ id: string; type: string; label: string }>,
|
|
376
|
-
updated.responses as Record<string, unknown>,
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
const { text: taskText } = resolveTemplate("hitl.follow_up", {
|
|
380
|
-
request_id: updated.id,
|
|
381
|
-
title: updated.title,
|
|
382
|
-
status: updated.status,
|
|
383
|
-
responses: formattedResponses,
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
await createTaskExtended(taskText, {
|
|
387
|
-
agentId: sourceTask.agentId,
|
|
388
|
-
routingReason: sourceTask.agentId ? "continuity" : undefined,
|
|
389
|
-
routingSource: sourceTask.agentId ? "engine_default" : undefined,
|
|
390
|
-
parentTaskId: updated.sourceTaskId,
|
|
391
|
-
source: "system",
|
|
392
|
-
taskType: "hitl-follow-up",
|
|
393
|
-
tags: ["hitl", "follow-up"],
|
|
394
|
-
// Explicit Slack metadata — parentTaskId auto-inherits too,
|
|
395
|
-
// but being explicit ensures the follow-up task always gets
|
|
396
|
-
// the right thread context even if inheritance logic changes.
|
|
397
|
-
slackChannelId: sourceTask.slackChannelId ?? undefined,
|
|
398
|
-
slackThreadTs: sourceTask.slackThreadTs ?? undefined,
|
|
399
|
-
slackUserId: sourceTask.slackUserId ?? undefined,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
}
|
|
444
|
+
await createApprovalFollowUpTask(updated, "hitl.follow_up");
|
|
403
445
|
|
|
404
446
|
respondRoute.respond(res, 200, { approvalRequest: toApprovalRequestResponse(updated) });
|
|
405
447
|
return true;
|
|
406
448
|
}
|
|
407
449
|
|
|
450
|
+
// 4-segment: POST /api/approval-requests/{id}/cancel
|
|
451
|
+
if (cancelRoute.match(req.method, pathSegments)) {
|
|
452
|
+
const parsed = await cancelRoute.parse(req, res, pathSegments, queryParams);
|
|
453
|
+
if (!parsed) return true;
|
|
454
|
+
|
|
455
|
+
const { principal, resolvedBy } = await approvalCancelPrincipal(req);
|
|
456
|
+
const result = await cancelApprovalRequest({
|
|
457
|
+
id: parsed.params.id,
|
|
458
|
+
reason: parsed.body.reason,
|
|
459
|
+
principal,
|
|
460
|
+
resolvedBy,
|
|
461
|
+
});
|
|
462
|
+
if (!result.ok) {
|
|
463
|
+
jsonError(res, result.message, result.status);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
cancelRoute.respond(res, 200, {
|
|
467
|
+
approvalRequest: toApprovalRequestResponse(result.request),
|
|
468
|
+
alreadyCancelled: result.alreadyCancelled,
|
|
469
|
+
runCancelled: result.runCancelled,
|
|
470
|
+
});
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
|
|
408
474
|
// 3-segment with param: GET /api/approval-requests/{id}
|
|
409
475
|
if (getByIdRoute.match(req.method, pathSegments)) {
|
|
410
476
|
const parsed = await getByIdRoute.parse(req, res, pathSegments, queryParams);
|
|
@@ -457,12 +523,20 @@ export async function handleApprovalRequests(
|
|
|
457
523
|
const parsed = await listRoute.parse(req, res, pathSegments, queryParams);
|
|
458
524
|
if (!parsed) return true;
|
|
459
525
|
|
|
460
|
-
const
|
|
526
|
+
const filters = {
|
|
461
527
|
status: parsed.query.status || undefined,
|
|
462
528
|
workflowRunId: parsed.query.workflowRunId || undefined,
|
|
463
529
|
limit: parsed.query.limit || undefined,
|
|
464
|
-
}
|
|
530
|
+
};
|
|
531
|
+
// Opt-in: API, MCP and script callers that don't ask keep the full rows.
|
|
532
|
+
if (parsed.query.fields === "slim") {
|
|
533
|
+
listRoute.respond(res, 200, {
|
|
534
|
+
approvalRequests: await listApprovalRequestSummaries(filters),
|
|
535
|
+
});
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
465
538
|
|
|
539
|
+
const requests = await listApprovalRequests(filters);
|
|
466
540
|
listRoute.respond(res, 200, {
|
|
467
541
|
approvalRequests: requests.map(toApprovalRequestResponse),
|
|
468
542
|
});
|
|
@@ -472,28 +546,26 @@ export async function handleApprovalRequests(
|
|
|
472
546
|
return false;
|
|
473
547
|
}
|
|
474
548
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
})
|
|
498
|
-
.join("\n");
|
|
549
|
+
/**
|
|
550
|
+
* The caller of the cancel route. The shared API key with an `X-Agent-ID`
|
|
551
|
+
* header identifies that agent, as on the create route.
|
|
552
|
+
*/
|
|
553
|
+
async function approvalCancelPrincipal(
|
|
554
|
+
req: IncomingMessage,
|
|
555
|
+
): Promise<{ principal: RbacPrincipal; resolvedBy: string | null }> {
|
|
556
|
+
const auth = getRequestAuth(req);
|
|
557
|
+
if (auth?.kind === "user") {
|
|
558
|
+
return { principal: { kind: "user", userId: auth.userId }, resolvedBy: auth.userId };
|
|
559
|
+
}
|
|
560
|
+
const rawAgentId = req.headers["x-agent-id"];
|
|
561
|
+
const agentId =
|
|
562
|
+
auth?.kind === "agent" ? auth.agentId : Array.isArray(rawAgentId) ? rawAgentId[0] : rawAgentId;
|
|
563
|
+
if (agentId) {
|
|
564
|
+
const agent = await getAgentById(agentId);
|
|
565
|
+
return {
|
|
566
|
+
principal: { kind: "agent", agentId, isLead: agent?.isLead ?? false },
|
|
567
|
+
resolvedBy: agentId,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return { principal: { kind: "operator" }, resolvedBy: "operator" };
|
|
499
571
|
}
|
package/src/http/apps.ts
CHANGED
|
@@ -1155,9 +1155,20 @@ export async function handleApps(
|
|
|
1155
1155
|
json(res, { error: "invalid row query", issues }, 400);
|
|
1156
1156
|
return true;
|
|
1157
1157
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1158
|
+
// SQL narrows the scan; the `===` re-check keeps the exact filter semantics.
|
|
1159
|
+
const rows = (
|
|
1160
|
+
await listAppRows(
|
|
1161
|
+
parsed.params.id,
|
|
1162
|
+
parsed.params.model,
|
|
1163
|
+
filters.flatMap((filter) =>
|
|
1164
|
+
typeof filter.value === "string" ||
|
|
1165
|
+
typeof filter.value === "number" ||
|
|
1166
|
+
typeof filter.value === "boolean"
|
|
1167
|
+
? [{ column: filter.column, value: filter.value }]
|
|
1168
|
+
: [],
|
|
1169
|
+
),
|
|
1170
|
+
)
|
|
1171
|
+
).filter((row) => filters.every((filter) => rowValue(row, filter.column) === filter.value));
|
|
1161
1172
|
const sortRaw = parsed.query.sort;
|
|
1162
1173
|
if (sortRaw) {
|
|
1163
1174
|
const [column, dir, extra] = sortRaw.split(":");
|
package/src/http/config.ts
CHANGED
|
@@ -134,6 +134,11 @@ const getResolvedConfigRoute = route({
|
|
|
134
134
|
query: z.object({
|
|
135
135
|
agentId: z.string().optional(),
|
|
136
136
|
repoId: z.string().optional(),
|
|
137
|
+
key: z
|
|
138
|
+
.string()
|
|
139
|
+
.min(1)
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("Return only the entry with this exact key. Omit to return every resolved entry."),
|
|
137
142
|
includeSecrets: z.enum(["true", "false"]).optional(),
|
|
138
143
|
}),
|
|
139
144
|
responses: {
|
|
@@ -216,6 +221,11 @@ const listConfig = route({
|
|
|
216
221
|
query: z.object({
|
|
217
222
|
scope: z.string().optional(),
|
|
218
223
|
scopeId: z.string().optional(),
|
|
224
|
+
key: z
|
|
225
|
+
.string()
|
|
226
|
+
.min(1)
|
|
227
|
+
.optional()
|
|
228
|
+
.describe("Return only entries with this exact key. Omit to return every entry."),
|
|
219
229
|
includeSecrets: z.enum(["true", "false"]).optional(),
|
|
220
230
|
}),
|
|
221
231
|
responses: {
|
|
@@ -283,9 +293,12 @@ export async function handleConfig(
|
|
|
283
293
|
if (!parsed) return true;
|
|
284
294
|
const includeSecrets = parsed.query.includeSecrets === "true";
|
|
285
295
|
const { effectiveIncludeSecrets, secretsNote } = await resolveSecretsRead(req, includeSecrets);
|
|
296
|
+
const key = parsed.query.key;
|
|
297
|
+
// Filter by key before masking and before any secret is registered, so a
|
|
298
|
+
// caller asking for one key never receives the rest (matches get-config).
|
|
286
299
|
const configs = stripApiOnlyKeys(
|
|
287
300
|
await getResolvedConfig(parsed.query.agentId || undefined, parsed.query.repoId || undefined),
|
|
288
|
-
);
|
|
301
|
+
).filter((c) => !key || c.key === key);
|
|
289
302
|
const result = effectiveIncludeSecrets ? configs : maskSecrets(configs);
|
|
290
303
|
if (effectiveIncludeSecrets) {
|
|
291
304
|
for (const c of result) {
|
|
@@ -368,6 +381,7 @@ export async function handleConfig(
|
|
|
368
381
|
await getSwarmConfigs({
|
|
369
382
|
scope: parsed.query.scope || undefined,
|
|
370
383
|
scopeId: parsed.query.scopeId || undefined,
|
|
384
|
+
key: parsed.query.key,
|
|
371
385
|
}),
|
|
372
386
|
);
|
|
373
387
|
const listResult = effectiveIncludeSecrets ? configs : maskSecrets(configs);
|
package/src/http/events.ts
CHANGED
|
@@ -86,12 +86,33 @@ const getEventsRoute = route({
|
|
|
86
86
|
since: z.string().optional(),
|
|
87
87
|
until: z.string().optional(),
|
|
88
88
|
limit: z.coerce.number().int().min(1).max(1000).optional(),
|
|
89
|
+
events: z
|
|
90
|
+
.string()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("Comma-separated event names; matches any of them (ANDed with `event`)"),
|
|
93
|
+
dataFields: z
|
|
94
|
+
.string()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe("Comma-separated `data.field` values, used with latestPerDataField"),
|
|
97
|
+
latestPerDataField: z
|
|
98
|
+
.enum(["true", "false"])
|
|
99
|
+
.optional()
|
|
100
|
+
.describe(
|
|
101
|
+
"When true, return only the newest event per (event, data.field) pair for every name in `event`/`events` and every value in `dataFields`",
|
|
102
|
+
),
|
|
89
103
|
}),
|
|
90
104
|
responses: {
|
|
91
105
|
200: {
|
|
92
106
|
description: "List of events",
|
|
93
|
-
schema: z.object({
|
|
107
|
+
schema: z.object({
|
|
108
|
+
events: z.array(SwarmEventSchema),
|
|
109
|
+
latestPerDataField: z
|
|
110
|
+
.literal(true)
|
|
111
|
+
.optional()
|
|
112
|
+
.describe("Present only when the request asked for latestPerDataField=true"),
|
|
113
|
+
}),
|
|
94
114
|
},
|
|
115
|
+
400: { description: "Validation error" },
|
|
95
116
|
},
|
|
96
117
|
auth: { apiKey: true },
|
|
97
118
|
});
|
|
@@ -122,6 +143,9 @@ const getEventCountsRoute = route({
|
|
|
122
143
|
auth: { apiKey: true },
|
|
123
144
|
});
|
|
124
145
|
|
|
146
|
+
/** Bounds the UNION ALL built for latestPerDataField: one indexed lookup per pair. */
|
|
147
|
+
const MAX_LATEST_PAIRS = 50;
|
|
148
|
+
|
|
125
149
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
126
150
|
|
|
127
151
|
export async function handleEvents(
|
|
@@ -184,6 +208,36 @@ export async function handleEvents(
|
|
|
184
208
|
const parsed = await getEventsRoute.parse(req, res, pathSegments, queryParams);
|
|
185
209
|
if (!parsed) return true;
|
|
186
210
|
|
|
211
|
+
const eventNames = parsed.query.events
|
|
212
|
+
? parsed.query.events.split(",").map((name) => EventNameSchema.safeParse(name.trim()))
|
|
213
|
+
: [];
|
|
214
|
+
const invalid = eventNames.find((result) => !result.success);
|
|
215
|
+
if (invalid) {
|
|
216
|
+
jsonError(res, "Invalid event name in `events`", 400);
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
const eventFilter = eventNames.flatMap((result) => (result.success ? [result.data] : []));
|
|
220
|
+
const latestPerDataField = parsed.query.latestPerDataField === "true";
|
|
221
|
+
const dataFields = (parsed.query.dataFields ?? "")
|
|
222
|
+
.split(",")
|
|
223
|
+
.map((field) => field.trim())
|
|
224
|
+
.filter(Boolean);
|
|
225
|
+
const latestEvents =
|
|
226
|
+
eventFilter.length > 0 ? eventFilter : parsed.query.event ? [parsed.query.event] : [];
|
|
227
|
+
if (
|
|
228
|
+
latestPerDataField &&
|
|
229
|
+
(latestEvents.length === 0 ||
|
|
230
|
+
dataFields.length === 0 ||
|
|
231
|
+
latestEvents.length * dataFields.length > MAX_LATEST_PAIRS)
|
|
232
|
+
) {
|
|
233
|
+
jsonError(
|
|
234
|
+
res,
|
|
235
|
+
`latestPerDataField needs \`event\` or \`events\` and \`dataFields\`, at most ${MAX_LATEST_PAIRS} pairs`,
|
|
236
|
+
400,
|
|
237
|
+
);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
187
241
|
const events = await getEventsFiltered({
|
|
188
242
|
category: parsed.query.category || undefined,
|
|
189
243
|
event: parsed.query.event || undefined,
|
|
@@ -196,8 +250,14 @@ export async function handleEvents(
|
|
|
196
250
|
since: parsed.query.since || undefined,
|
|
197
251
|
until: parsed.query.until || undefined,
|
|
198
252
|
limit: parsed.query.limit ?? 100,
|
|
253
|
+
events: eventFilter,
|
|
254
|
+
latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined,
|
|
199
255
|
});
|
|
200
|
-
getEventsRoute.respond(
|
|
256
|
+
getEventsRoute.respond(
|
|
257
|
+
res,
|
|
258
|
+
200,
|
|
259
|
+
latestPerDataField ? { events, latestPerDataField: true } : { events },
|
|
260
|
+
);
|
|
201
261
|
return true;
|
|
202
262
|
}
|
|
203
263
|
|
package/src/http/index.ts
CHANGED
|
@@ -595,8 +595,10 @@ try {
|
|
|
595
595
|
// here surfaces the count in the boot log and makes the API ready to recompute
|
|
596
596
|
// USD before the first POST /api/session-costs lands.
|
|
597
597
|
try {
|
|
598
|
-
|
|
599
|
-
|
|
598
|
+
// Guarded per DB handle: `createServer()` (one per MCP session) calls the
|
|
599
|
+
// same helper, so seeding here is what keeps session init off this path.
|
|
600
|
+
const { ensurePricingSeeded } = await import("../be/boot-seeds");
|
|
601
|
+
ensurePricingSeeded();
|
|
600
602
|
const { startPricingRefreshLoop } = await import("../be/pricing-refresh");
|
|
601
603
|
startPricingRefreshLoop();
|
|
602
604
|
} catch (err) {
|
|
@@ -604,8 +606,8 @@ try {
|
|
|
604
606
|
}
|
|
605
607
|
|
|
606
608
|
try {
|
|
607
|
-
const {
|
|
608
|
-
|
|
609
|
+
const { ensureRbacSeeded } = await import("../be/boot-seeds");
|
|
610
|
+
ensureRbacSeeded();
|
|
609
611
|
} catch (err) {
|
|
610
612
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
611
613
|
// RBAC flag-on must fail closed; flag-off deployments should not be bricked
|
package/src/http/memory.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { rerank } from "../be/memory/reranker";
|
|
|
24
24
|
import { getRetrievalsForAgent, hasRetrievalForTask } from "../be/memory/retrieval-store";
|
|
25
25
|
import { getUsefulnessStats } from "../be/memory/usefulness-stats";
|
|
26
26
|
import { shouldPersistAutomaticTaskMemory } from "../memory/automatic-task-gate";
|
|
27
|
-
import { SIMILARITY_THRESHOLD } from "../prompts/memories";
|
|
27
|
+
import { memoryRelevance, SIMILARITY_THRESHOLD } from "../prompts/memories";
|
|
28
28
|
import { can } from "../rbac";
|
|
29
29
|
import { AgentMemorySchema, AgentMemoryScopeSchema, AgentMemorySourceSchema } from "../types";
|
|
30
30
|
import { getRequestAuth } from "../utils/request-auth-context";
|
|
@@ -688,7 +688,24 @@ export async function handleMemory(
|
|
|
688
688
|
source,
|
|
689
689
|
isLead: false,
|
|
690
690
|
});
|
|
691
|
-
const
|
|
691
|
+
const resultLimit = Math.min(limit, 20);
|
|
692
|
+
const consumptionHeader = req.headers["x-memory-consumption"];
|
|
693
|
+
const consumptionMode = Array.isArray(consumptionHeader)
|
|
694
|
+
? consumptionHeader[0]
|
|
695
|
+
: consumptionHeader;
|
|
696
|
+
// Prompt recall injects only rows above the relevance gate, so the gate
|
|
697
|
+
// must pick the slots, not trim them afterwards: eligible rows fill the
|
|
698
|
+
// cap first (in composite order), ineligible rows only the remainder.
|
|
699
|
+
// Capping by composite first let boosted low-relevance rows take every
|
|
700
|
+
// slot and render an empty prompt while an eligible hit existed.
|
|
701
|
+
const ranked =
|
|
702
|
+
consumptionMode === "prompt"
|
|
703
|
+
? rerank(expanded, { limit: expanded.length })
|
|
704
|
+
.map((r) => ({ r, eligible: memoryRelevance(r) > SIMILARITY_THRESHOLD }))
|
|
705
|
+
.sort((a, b) => Number(b.eligible) - Number(a.eligible))
|
|
706
|
+
.slice(0, resultLimit)
|
|
707
|
+
.map(({ r }) => r)
|
|
708
|
+
: rerank(expanded, { limit: resultLimit });
|
|
692
709
|
|
|
693
710
|
// Retrieval bridge — when caller passed `X-Source-Task-ID`, record one
|
|
694
711
|
// `memory_retrieval` row per returned memory so server-side raters
|
|
@@ -701,10 +718,6 @@ export async function handleMemory(
|
|
|
701
718
|
: sourceTaskIdHeader;
|
|
702
719
|
const contextKeyHeader = req.headers["x-context-key"];
|
|
703
720
|
const contextKey = Array.isArray(contextKeyHeader) ? contextKeyHeader[0] : contextKeyHeader;
|
|
704
|
-
const consumptionHeader = req.headers["x-memory-consumption"];
|
|
705
|
-
const consumptionMode = Array.isArray(consumptionHeader)
|
|
706
|
-
? consumptionHeader[0]
|
|
707
|
-
: consumptionHeader;
|
|
708
721
|
if (sourceTaskId && intent) {
|
|
709
722
|
try {
|
|
710
723
|
await recordRetrievals(
|
|
@@ -713,6 +726,7 @@ export async function handleMemory(
|
|
|
713
726
|
ranked.map((r) => ({
|
|
714
727
|
memoryId: r.id,
|
|
715
728
|
similarity: r.similarity,
|
|
729
|
+
relevance: r.rawSimilarity,
|
|
716
730
|
retrievalSource: r.retrievalSource,
|
|
717
731
|
})),
|
|
718
732
|
undefined,
|
|
@@ -727,7 +741,7 @@ export async function handleMemory(
|
|
|
727
741
|
if (intent) {
|
|
728
742
|
const consumed =
|
|
729
743
|
consumptionMode === "prompt"
|
|
730
|
-
? ranked.filter((r) => r
|
|
744
|
+
? ranked.filter((r) => memoryRelevance(r) > SIMILARITY_THRESHOLD)
|
|
731
745
|
: ranked;
|
|
732
746
|
consumedIds = dedupeMemoryDocumentIds(consumed);
|
|
733
747
|
try {
|
package/src/http/schedules.ts
CHANGED
|
@@ -81,7 +81,7 @@ const scheduleWithOptionalFavoriteSchema = ScheduledTaskSchema.safeExtend({
|
|
|
81
81
|
/** Slim list row — `taskTemplate` swapped for a bounded preview (see `ScheduledTaskSummary`). */
|
|
82
82
|
const { taskTemplate: _omittedTaskTemplate, ...scheduleShapeWithoutTemplate } =
|
|
83
83
|
ScheduledTaskSchema.shape;
|
|
84
|
-
const scheduleSummaryWithFavoriteSchema = z.object({
|
|
84
|
+
export const scheduleSummaryWithFavoriteSchema = z.object({
|
|
85
85
|
...scheduleShapeWithoutTemplate,
|
|
86
86
|
taskTemplatePreview: z.string(),
|
|
87
87
|
favorite: z.boolean(),
|
package/src/http/session-data.ts
CHANGED
|
@@ -18,7 +18,12 @@ import {
|
|
|
18
18
|
import { recordSessionCost } from "../otel";
|
|
19
19
|
import { incrementServerSessionsProcessed } from "../server-runtime-counters";
|
|
20
20
|
import type { SessionCost } from "../types";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
PricingProviderSchema,
|
|
23
|
+
SessionCostModelBreakdownSchema,
|
|
24
|
+
SessionCostSchema,
|
|
25
|
+
SessionLogSchema,
|
|
26
|
+
} from "../types";
|
|
22
27
|
import { route } from "./route-def";
|
|
23
28
|
import { recomputeSessionCost } from "./session-cost-recompute";
|
|
24
29
|
import { cachedUsageReport } from "./usage-cache";
|
|
@@ -205,9 +210,9 @@ const createSessionCostRoute = route({
|
|
|
205
210
|
// its own billing and the adapter reports `totalCostUsd: 0`, so the row
|
|
206
211
|
// lands as `costSource: 'unpriced'`. It still has to be accepted here or
|
|
207
212
|
// every ACP session's cost POST 400s and the row is silently dropped.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
// Reuses PricingProviderSchema so this list can't drift from it again
|
|
214
|
+
// (#1636: it previously forgot `dsh`).
|
|
215
|
+
provider: PricingProviderSchema.optional(),
|
|
211
216
|
/**
|
|
212
217
|
* Phase 6: epoch-ms timestamp used as the "active price at time T" lookup
|
|
213
218
|
* basis. Defaults to `Date.now()` when omitted. Including it lets
|