@sema-agent/server 7.79.3 → 7.80.1
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/boot/stage-07-capability-layer.js +2 -1
- package/dist/capabilities/skills.d.ts +10 -0
- package/dist/capabilities/skills.js +3 -1
- package/dist/http/admission.js +3 -1
- package/dist/http/dispatch.js +28 -8
- package/dist/http/resume-legs.js +4 -3
- package/dist/http/route-ctx.d.ts +101 -4
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.d.ts +71 -43
- package/dist/http/route-table.js +111 -187
- package/dist/http/routes/a2a-serve.d.ts +18 -3
- package/dist/http/routes/a2a-serve.js +25 -23
- package/dist/http/routes/admin-config-refresh.d.ts +9 -2
- package/dist/http/routes/admin-config-refresh.js +32 -31
- package/dist/http/routes/admin-drain.d.ts +9 -2
- package/dist/http/routes/admin-drain.js +32 -31
- package/dist/http/routes/admin-memory-optout.d.ts +28 -2
- package/dist/http/routes/admin-memory-optout.js +27 -40
- package/dist/http/routes/adoption.d.ts +15 -2
- package/dist/http/routes/adoption.js +52 -52
- package/dist/http/routes/agents-roster.d.ts +9 -2
- package/dist/http/routes/agents-roster.js +82 -80
- package/dist/http/routes/approvals-assistant.d.ts +58 -5
- package/dist/http/routes/approvals-assistant.js +38 -39
- package/dist/http/routes/attachments.d.ts +22 -2
- package/dist/http/routes/attachments.js +81 -72
- package/dist/http/routes/capabilities.d.ts +21 -2
- package/dist/http/routes/capabilities.js +174 -170
- package/dist/http/routes/config-catalog.d.ts +9 -2
- package/dist/http/routes/config-catalog.js +28 -31
- package/dist/http/routes/devices.d.ts +24 -2
- package/dist/http/routes/devices.js +19 -21
- package/dist/http/routes/diagnostics.d.ts +9 -2
- package/dist/http/routes/diagnostics.js +41 -43
- package/dist/http/routes/fleet.d.ts +11 -2
- package/dist/http/routes/fleet.js +23 -22
- package/dist/http/routes/images.d.ts +63 -9
- package/dist/http/routes/images.js +53 -46
- package/dist/http/routes/leader.d.ts +20 -2
- package/dist/http/routes/leader.js +43 -40
- package/dist/http/routes/memory-bundle.d.ts +15 -2
- package/dist/http/routes/memory-bundle.js +38 -38
- package/dist/http/routes/memory-compliance.d.ts +16 -4
- package/dist/http/routes/memory-compliance.js +44 -45
- package/dist/http/routes/memory-consolidation.d.ts +14 -2
- package/dist/http/routes/memory-consolidation.js +66 -66
- package/dist/http/routes/memory-origin.d.ts +23 -4
- package/dist/http/routes/memory-origin.js +65 -65
- package/dist/http/routes/memory-policy.d.ts +21 -2
- package/dist/http/routes/memory-policy.js +119 -115
- package/dist/http/routes/notify-wake.d.ts +21 -11
- package/dist/http/routes/notify-wake.js +118 -116
- package/dist/http/routes/observability.d.ts +16 -2
- package/dist/http/routes/observability.js +54 -51
- package/dist/http/routes/retention-ops.d.ts +19 -2
- package/dist/http/routes/retention-ops.js +20 -22
- package/dist/http/routes/rules.d.ts +36 -2
- package/dist/http/routes/rules.js +22 -24
- package/dist/http/routes/run-memory-capture.d.ts +10 -3
- package/dist/http/routes/run-memory-capture.js +84 -84
- package/dist/http/routes/runs.d.ts +102 -22
- package/dist/http/routes/runs.js +79 -96
- package/dist/http/routes/session-sync.d.ts +41 -2
- package/dist/http/routes/session-sync.js +26 -24
- package/dist/http/routes/sessions-list.d.ts +16 -2
- package/dist/http/routes/sessions-list.js +78 -75
- package/dist/http/routes/sessions.d.ts +77 -2
- package/dist/http/routes/sessions.js +50 -47
- package/dist/http/routes/shared-memory.d.ts +19 -2
- package/dist/http/routes/shared-memory.js +65 -63
- package/dist/http/routes/side-query.d.ts +11 -2
- package/dist/http/routes/side-query.js +81 -79
- package/dist/http/routes/tasks.d.ts +17 -2
- package/dist/http/routes/tasks.js +18 -11
- package/dist/http/routes/trace-usage.d.ts +40 -2
- package/dist/http/routes/trace-usage.js +46 -35
- package/dist/http/routes/workflows.d.ts +35 -3
- package/dist/http/routes/workflows.js +29 -29
- package/dist/http/wire-types.d.ts +15 -9
- package/dist/parked-decide.d.ts +9 -0
- package/dist/parked-decide.js +3 -3
- package/dist/run-local.js +2 -1
- package/dist/spec-fields.js +33 -30
- package/dist/task-cwd.d.ts +21 -0
- package/dist/task-cwd.js +3 -0
- package/dist/trace/core-keyset-guard.d.ts +6 -1
- package/dist/trace/project.d.ts +20 -0
- package/dist/trace/project.js +15 -1
- package/package.json +2 -2
|
@@ -7,6 +7,7 @@ import { recordFailOpen } from "../../observability/fail-open.js";
|
|
|
7
7
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
8
8
|
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
9
9
|
import { gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal } from "../principal-gate.js";
|
|
10
|
+
import { assertNever } from "../route-ctx.js";
|
|
10
11
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
11
12
|
const MCP_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
12
13
|
const A2A_STATUS_TTL_MS = 5_000;
|
|
@@ -61,26 +62,20 @@ async function readLastLegMcp(deps, sessionId, principal) {
|
|
|
61
62
|
}
|
|
62
63
|
return undefined;
|
|
63
64
|
}
|
|
64
|
-
export async function handleSessions(req, res,
|
|
65
|
-
const miss = { fell: false };
|
|
66
|
-
await handleSessionsBody(req, res, url, ctx, miss);
|
|
67
|
-
return !miss.fell;
|
|
68
|
-
}
|
|
69
|
-
async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
65
|
+
export async function handleSessions(req, res, match, ctx) {
|
|
70
66
|
const { deps } = ctx;
|
|
71
67
|
const { readJson, safeDecode, runOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite } = ctx.helpers;
|
|
72
68
|
const sse = ctx.local.sessions;
|
|
73
69
|
const { mcpStatusCache, a2aStatusCache } = ctx.local.sessions;
|
|
74
70
|
{
|
|
75
|
-
|
|
76
|
-
if (im && req.method === "GET") {
|
|
71
|
+
if (match.decl.id === "session-init") {
|
|
77
72
|
const principal = gatedPrincipal(req, deps.config);
|
|
78
73
|
if (deps.config.requirePrincipal && !principal) {
|
|
79
74
|
sendPrincipalRefusal(res, req, deps.config);
|
|
80
75
|
return;
|
|
81
76
|
}
|
|
82
77
|
sendJson(res, 200, {
|
|
83
|
-
sessionId:
|
|
78
|
+
sessionId: match.params.sessionId,
|
|
84
79
|
model: { provider: deps.config.model.provider, modelId: deps.config.model.id },
|
|
85
80
|
...(deps.config.autonomy ? { mode: deps.config.autonomy } : {}),
|
|
86
81
|
});
|
|
@@ -88,8 +83,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
88
83
|
}
|
|
89
84
|
}
|
|
90
85
|
{
|
|
91
|
-
|
|
92
|
-
if (stm && req.method === "GET") {
|
|
86
|
+
if (match.decl.id === "session-settings") {
|
|
93
87
|
const principal = gatedPrincipal(req, deps.config);
|
|
94
88
|
if (deps.config.requirePrincipal && !principal) {
|
|
95
89
|
sendPrincipalRefusal(res, req, deps.config);
|
|
@@ -109,14 +103,13 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
109
103
|
}
|
|
110
104
|
}
|
|
111
105
|
{
|
|
112
|
-
|
|
113
|
-
if (mm && req.method === "GET") {
|
|
106
|
+
if (match.decl.id === "session-mcp") {
|
|
114
107
|
const principal = gatedPrincipal(req, deps.config);
|
|
115
108
|
if (deps.config.requirePrincipal && !principal) {
|
|
116
109
|
sendPrincipalRefusal(res, req, deps.config);
|
|
117
110
|
return;
|
|
118
111
|
}
|
|
119
|
-
const lastLegMcp = await lastLegMcpFor(deps, safeDecode(
|
|
112
|
+
const lastLegMcp = await lastLegMcpFor(deps, safeDecode(match.params.sessionId), principal);
|
|
120
113
|
const specs = mcpForScenario(deps.config.mcpServers, deps.config.defaultScenario);
|
|
121
114
|
const cacheKey = `${deps.config.defaultScenario}::${principal ?? ""}`;
|
|
122
115
|
if (!specs || specs.length === 0) {
|
|
@@ -160,8 +153,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
160
153
|
}
|
|
161
154
|
}
|
|
162
155
|
{
|
|
163
|
-
|
|
164
|
-
if (am && req.method === "GET") {
|
|
156
|
+
if (match.decl.id === "session-a2a") {
|
|
165
157
|
if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
|
|
166
158
|
sendPrincipalRefusal(res, req, deps.config);
|
|
167
159
|
return;
|
|
@@ -220,15 +212,14 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
220
212
|
}
|
|
221
213
|
}
|
|
222
214
|
{
|
|
223
|
-
|
|
224
|
-
if (hm) {
|
|
215
|
+
if (match.decl.id === "session-head") {
|
|
225
216
|
const getLeafId = deps.sessionStorage?.getLeafId?.bind(deps.sessionStorage);
|
|
226
217
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
227
218
|
if (!getLeafId || !ownerOf) {
|
|
228
219
|
sendError(res, 501, "capability.session_store_required", "session head probe requires a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
229
220
|
return;
|
|
230
221
|
}
|
|
231
|
-
const headSession = decodeURIComponent(
|
|
222
|
+
const headSession = decodeURIComponent(match.params.sessionId);
|
|
232
223
|
const getHead = deps.sessionStorage?.getHead?.bind(deps.sessionStorage);
|
|
233
224
|
let owner;
|
|
234
225
|
let leafId;
|
|
@@ -264,15 +255,14 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
264
255
|
}
|
|
265
256
|
}
|
|
266
257
|
{
|
|
267
|
-
|
|
268
|
-
if (em) {
|
|
258
|
+
if (match.decl.id === "session-events") {
|
|
269
259
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
270
260
|
const watch = deps.sessionWatch;
|
|
271
261
|
if (!deps.sessionStorage?.getLeafId || !ownerOf || !watch) {
|
|
272
262
|
sendError(res, 501, "capability.session_store_required", "session events require a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
273
263
|
return;
|
|
274
264
|
}
|
|
275
|
-
const evSession = decodeURIComponent(
|
|
265
|
+
const evSession = decodeURIComponent(match.params.sessionId);
|
|
276
266
|
if (sse.sseConnections >= (deps.sessionEventsMaxConnections ?? 256)) {
|
|
277
267
|
res.setHeader("retry-after", "5");
|
|
278
268
|
sendError(res, 503, "state.sse_connection_cap", "session-events connection cap reached — fall back to /head polling");
|
|
@@ -333,7 +323,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
333
323
|
cleanup();
|
|
334
324
|
return;
|
|
335
325
|
}
|
|
336
|
-
const q = new
|
|
326
|
+
const q = new URLSearchParams(match.search);
|
|
337
327
|
const lastLeafId = q.get("lastLeafId");
|
|
338
328
|
let first = true;
|
|
339
329
|
let lastAccepted;
|
|
@@ -453,9 +443,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
453
443
|
}
|
|
454
444
|
}
|
|
455
445
|
{
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const sessionId = safeDecode(em[1]);
|
|
446
|
+
if (match.decl.id === "session-memory-erase") {
|
|
447
|
+
const sessionId = safeDecode(match.params.sessionId);
|
|
459
448
|
if (sessionId === null) {
|
|
460
449
|
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
461
450
|
return;
|
|
@@ -513,9 +502,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
513
502
|
}
|
|
514
503
|
}
|
|
515
504
|
{
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const sessionId = safeDecode(msm[1]);
|
|
505
|
+
if (match.decl.id === "session-memory-status") {
|
|
506
|
+
const sessionId = safeDecode(match.params.sessionId);
|
|
519
507
|
if (sessionId === null) {
|
|
520
508
|
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
521
509
|
return;
|
|
@@ -570,17 +558,20 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
570
558
|
return;
|
|
571
559
|
}
|
|
572
560
|
}
|
|
573
|
-
if (
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
561
|
+
if (match.decl.id === "session-detail") {
|
|
562
|
+
{
|
|
563
|
+
if (!deps.sessionAudit) {
|
|
564
|
+
sendError(res, 404, "not_found.route", "not found");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const audit = await deps.sessionAudit(match.params.sessionId);
|
|
577
568
|
if (!audit) {
|
|
578
569
|
sendError(res, 404, "not_found.session", "session not found");
|
|
579
570
|
return;
|
|
580
571
|
}
|
|
581
572
|
if (!runOwnerOk(req, res, audit.owner))
|
|
582
573
|
return;
|
|
583
|
-
const q = new
|
|
574
|
+
const q = new URLSearchParams(match.search);
|
|
584
575
|
const qi = (k) => {
|
|
585
576
|
const v = q.get(k);
|
|
586
577
|
if (v === null)
|
|
@@ -619,8 +610,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
619
610
|
}
|
|
620
611
|
}
|
|
621
612
|
{
|
|
622
|
-
|
|
623
|
-
if (fm) {
|
|
613
|
+
if (match.decl.id === "session-fork") {
|
|
624
614
|
const fork = deps.sessionStorage?.fork?.bind(deps.sessionStorage);
|
|
625
615
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
626
616
|
if (!fork || !ownerOf) {
|
|
@@ -632,7 +622,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
632
622
|
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
633
623
|
return;
|
|
634
624
|
}
|
|
635
|
-
const sourceId = safeDecode(
|
|
625
|
+
const sourceId = safeDecode(match.params.sessionId);
|
|
636
626
|
if (sourceId === null || !isUuidV7(sourceId)) {
|
|
637
627
|
sendError(res, 400, "request.id_invalid", "invalid session id (must be a uuidv7)");
|
|
638
628
|
return;
|
|
@@ -657,8 +647,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
657
647
|
}
|
|
658
648
|
}
|
|
659
649
|
{
|
|
660
|
-
|
|
661
|
-
if (dm) {
|
|
650
|
+
if (match.decl.id === "session-delete") {
|
|
662
651
|
const purge = deps.purgeSession;
|
|
663
652
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
664
653
|
if (!purge || !ownerOf) {
|
|
@@ -670,7 +659,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
670
659
|
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
671
660
|
return;
|
|
672
661
|
}
|
|
673
|
-
const sessionId = safeDecode(
|
|
662
|
+
const sessionId = safeDecode(match.params.sessionId);
|
|
674
663
|
if (sessionId === null || !isUuidV7(sessionId)) {
|
|
675
664
|
sendError(res, 400, "request.id_invalid", "invalid session id (must be a uuidv7)");
|
|
676
665
|
return;
|
|
@@ -699,8 +688,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
699
688
|
}
|
|
700
689
|
}
|
|
701
690
|
{
|
|
702
|
-
|
|
703
|
-
if (pm) {
|
|
691
|
+
if (match.decl.id === "session-policy-get" || match.decl.id === "session-policy-put") {
|
|
704
692
|
const store = deps.sessionPolicyStore;
|
|
705
693
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
706
694
|
if (!store || !ownerOf) {
|
|
@@ -712,7 +700,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
712
700
|
sendError(res, 401, "auth.unauthorized", "unauthorized");
|
|
713
701
|
return;
|
|
714
702
|
}
|
|
715
|
-
const sessionId = safeDecode(
|
|
703
|
+
const sessionId = safeDecode(match.params.sessionId);
|
|
716
704
|
if (sessionId === null || !isUuidV7(sessionId)) {
|
|
717
705
|
sendError(res, 400, "request.id_invalid", "invalid session id (must be a uuidv7)");
|
|
718
706
|
return;
|
|
@@ -732,7 +720,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
732
720
|
return;
|
|
733
721
|
}
|
|
734
722
|
const target = owner;
|
|
735
|
-
if (
|
|
723
|
+
if (match.decl.id === "session-policy-get") {
|
|
736
724
|
sendJson(res, 200, { rules: (await store.getRules(sessionId, target)) ?? { rev: 0 } });
|
|
737
725
|
return;
|
|
738
726
|
}
|
|
@@ -778,12 +766,27 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
778
766
|
}
|
|
779
767
|
}
|
|
780
768
|
{
|
|
781
|
-
|
|
782
|
-
if (wm) {
|
|
769
|
+
if (match.decl.id === "session-workspace") {
|
|
783
770
|
sendError(res, 501, "capability.workspace_retired", "workspace browse is retired: the whole-tree snapshot epoch it projected was replaced by per-edited-file rewind history (which holds only files the agent edited, not the workspace). There is no whole-tree manifest to browse anymore.");
|
|
784
771
|
return;
|
|
785
772
|
}
|
|
786
773
|
}
|
|
787
|
-
|
|
774
|
+
return assertNever(match.decl.id);
|
|
788
775
|
}
|
|
776
|
+
export const SESSIONS_ROUTES = [
|
|
777
|
+
{ id: "session-init", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/init$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
778
|
+
{ id: "session-settings", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/settings$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
779
|
+
{ id: "session-mcp", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/mcp$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
780
|
+
{ id: "session-a2a", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/a2a$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
781
|
+
{ id: "session-head", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/head$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
782
|
+
{ id: "session-events", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/events$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
783
|
+
{ id: "session-fork", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/fork$/, label: "/v1/sessions/:id/$verb", methods: ["POST"] },
|
|
784
|
+
{ id: "session-policy-get", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/policy$/, label: "/v1/sessions/:id/$verb", methods: ["GET"] },
|
|
785
|
+
{ id: "session-policy-put", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/policy$/, label: "/v1/sessions/:id/$verb", methods: ["PUT"] },
|
|
786
|
+
{ id: "session-memory-erase", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/memory\/erase$/, label: "/v1/sessions/:id/memory/erase", methods: ["POST"] },
|
|
787
|
+
{ id: "session-memory-status", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/memory-status$/, label: "/v1/sessions/:id/memory-status", methods: ["GET"] },
|
|
788
|
+
{ id: "session-workspace", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/workspace(?:\/(?<key>[^/]+)(?:\/(?<sub>tree|file|archive))?)?$/, label: "/v1/sessions/:id/workspace", methods: ["GET"] },
|
|
789
|
+
{ id: "session-detail", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)$/, label: "/v1/sessions/:id", methods: ["GET"] },
|
|
790
|
+
{ id: "session-delete", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)$/, label: "/v1/sessions/:id", methods: ["DELETE"] },
|
|
791
|
+
];
|
|
789
792
|
//# sourceMappingURL=sessions.js.map
|
|
@@ -26,6 +26,23 @@
|
|
|
26
26
|
* 「库里没有这份文件」对他不是秘密,把它和「你不是成员」合并反而会让成员无法区分打错路径和权限问题。
|
|
27
27
|
*/
|
|
28
28
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
29
|
-
import type { RouteCtx } from "../route-ctx.js";
|
|
30
|
-
export declare function handleSharedMemory(req: IncomingMessage, res: ServerResponse,
|
|
29
|
+
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
30
|
+
export declare function handleSharedMemory(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SharedMemoryRouteId>, ctx: RouteCtx): Promise<void>;
|
|
31
|
+
export declare const SHARED_MEMORY_ROUTES: readonly [{
|
|
32
|
+
readonly id: "shared-memory-stores";
|
|
33
|
+
readonly path: "/v1/shared-memory/stores";
|
|
34
|
+
readonly methods: readonly ["GET"];
|
|
35
|
+
}, {
|
|
36
|
+
readonly id: "shared-memory-store-documents";
|
|
37
|
+
readonly pattern: RegExp;
|
|
38
|
+
readonly label: "/v1/shared-memory/stores/:store/documents";
|
|
39
|
+
readonly methods: readonly ["GET"];
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: "shared-memory-store-document";
|
|
42
|
+
readonly pattern: RegExp;
|
|
43
|
+
readonly label: "/v1/shared-memory/stores/:store/document";
|
|
44
|
+
readonly methods: readonly ["GET"];
|
|
45
|
+
}];
|
|
46
|
+
/** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
|
|
47
|
+
export type SharedMemoryRouteId = RouteIdsOf<typeof SHARED_MEMORY_ROUTES>;
|
|
31
48
|
//# sourceMappingURL=shared-memory.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError } from "@sema-agent/core";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedPrincipal } from "../principal-gate.js";
|
|
4
|
-
|
|
4
|
+
import { assertNever } from "../route-ctx.js";
|
|
5
5
|
const RETRY_AFTER_SEC = 10;
|
|
6
6
|
const HTTP_LIST_LIMIT = 100;
|
|
7
7
|
const FORBIDDEN_PATH_CHARS = /[\p{Cc}\p{Cf}\p{Co}\p{Cn}\p{Default_Ignorable_Code_Point}\u2028\u2029\\]/u;
|
|
@@ -75,76 +75,78 @@ function resolveStore(res, snapshot, storeId) {
|
|
|
75
75
|
}
|
|
76
76
|
return found.reader;
|
|
77
77
|
}
|
|
78
|
-
export async function handleSharedMemory(req, res,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
sendJson(res, 200, { state: "connecting", stores: [] });
|
|
78
|
+
export async function handleSharedMemory(req, res, match, ctx) {
|
|
79
|
+
switch (match.decl.id) {
|
|
80
|
+
case "shared-memory-stores": {
|
|
81
|
+
const snapshot = await bindSnapshot(req, res, ctx);
|
|
82
|
+
if (!snapshot)
|
|
83
|
+
return;
|
|
84
|
+
if (snapshot.state !== "connected") {
|
|
85
|
+
sendJson(res, 200, { state: "connecting", stores: [] });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
sendJson(res, 200, { state: "connected", stores: snapshot.stores.map((s) => ({ id: s.info.id, description: s.info.description, writable: s.info.writable })) });
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
return
|
|
91
|
+
case "shared-memory-store-documents":
|
|
92
|
+
case "shared-memory-store-document": {
|
|
93
|
+
const storeId = decodeURIComponent(match.params.store);
|
|
94
|
+
const query = new URLSearchParams(match.search);
|
|
95
|
+
const snapshot = await bindSnapshot(req, res, ctx);
|
|
96
|
+
if (!snapshot)
|
|
97
|
+
return;
|
|
98
|
+
const reader = resolveStore(res, snapshot, storeId);
|
|
99
|
+
if (!reader)
|
|
100
|
+
return;
|
|
101
|
+
if (match.decl.id === "shared-memory-store-documents") {
|
|
102
|
+
const prefix = query.get("path_prefix") ?? undefined;
|
|
103
|
+
const cursor = query.get("cursor") ?? undefined;
|
|
104
|
+
const page = await reader.list(prefix, { ...(cursor !== undefined ? { cursor } : {}), limit: HTTP_LIST_LIMIT }).catch((err) => {
|
|
105
|
+
if (foldReaderError(res, err))
|
|
106
|
+
return undefined;
|
|
107
|
+
throw err;
|
|
108
|
+
});
|
|
109
|
+
if (page === undefined)
|
|
110
|
+
return;
|
|
111
|
+
const entries = Array.isArray(page) ? page : page.entries;
|
|
112
|
+
const nextCursor = Array.isArray(page) ? undefined : page.nextCursor;
|
|
113
|
+
sendJson(res, 200, { store: storeId, entries, ...(nextCursor !== undefined ? { nextCursor } : {}) });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const path = query.get("path");
|
|
117
|
+
if (path === null || path === "" || !isDocumentPath(path)) {
|
|
118
|
+
sendError(res, 400, "request.query_invalid", "missing or malformed ?path= (absolute, .md/.txt/.json/.jsonl)");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let folded = false;
|
|
122
|
+
const doc = await reader.read(path, {}).catch((err) => {
|
|
123
|
+
if (foldReaderError(res, err)) {
|
|
124
|
+
folded = true;
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
112
127
|
throw err;
|
|
113
128
|
});
|
|
114
|
-
if (
|
|
129
|
+
if (folded)
|
|
130
|
+
return;
|
|
131
|
+
if (doc === null) {
|
|
132
|
+
sendError(res, 404, "not_found.shared_memory_document", "not found");
|
|
115
133
|
return;
|
|
116
|
-
const entries = Array.isArray(page) ? page : page.entries;
|
|
117
|
-
const nextCursor = Array.isArray(page) ? undefined : page.nextCursor;
|
|
118
|
-
sendJson(res, 200, { store: storeId, entries, ...(nextCursor !== undefined ? { nextCursor } : {}) });
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const path = query.get("path");
|
|
122
|
-
if (path === null || path === "" || !isDocumentPath(path)) {
|
|
123
|
-
sendError(res, 400, "request.query_invalid", "missing or malformed ?path= (absolute, .md/.txt/.json/.jsonl)");
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
let folded = false;
|
|
127
|
-
const doc = await reader.read(path, {}).catch((err) => {
|
|
128
|
-
if (foldReaderError(res, err)) {
|
|
129
|
-
folded = true;
|
|
130
|
-
return null;
|
|
131
134
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const bytes = Buffer.byteLength(doc.content, "utf8");
|
|
141
|
-
if (bytes > SHARED_MEMORY_READ_CAP_BYTES) {
|
|
142
|
-
sendError(res, 413, "request.payload_too_large", `document is ${bytes} bytes, over the ${SHARED_MEMORY_READ_CAP_BYTES}-byte shared-store read cap`, { bytes, capBytes: SHARED_MEMORY_READ_CAP_BYTES });
|
|
135
|
+
const bytes = Buffer.byteLength(doc.content, "utf8");
|
|
136
|
+
if (bytes > SHARED_MEMORY_READ_CAP_BYTES) {
|
|
137
|
+
sendError(res, 413, "request.payload_too_large", `document is ${bytes} bytes, over the ${SHARED_MEMORY_READ_CAP_BYTES}-byte shared-store read cap`, { bytes, capBytes: SHARED_MEMORY_READ_CAP_BYTES });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
sendJson(res, 200, { store: storeId, path, content: doc.content, bytes, ...(doc.updatedAt !== undefined ? { updatedAt: doc.updatedAt } : {}) });
|
|
143
141
|
return;
|
|
144
142
|
}
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
default:
|
|
144
|
+
return assertNever(match.decl.id);
|
|
147
145
|
}
|
|
148
|
-
miss.fell = true;
|
|
149
146
|
}
|
|
147
|
+
export const SHARED_MEMORY_ROUTES = [
|
|
148
|
+
{ id: "shared-memory-stores", path: "/v1/shared-memory/stores", methods: ["GET"] },
|
|
149
|
+
{ id: "shared-memory-store-documents", pattern: /^\/v1\/shared-memory\/stores\/(?<store>[^/]+)\/documents$/, label: "/v1/shared-memory/stores/:store/documents", methods: ["GET"] },
|
|
150
|
+
{ id: "shared-memory-store-document", pattern: /^\/v1\/shared-memory\/stores\/(?<store>[^/]+)\/document$/, label: "/v1/shared-memory/stores/:store/document", methods: ["GET"] },
|
|
151
|
+
];
|
|
150
152
|
//# sourceMappingURL=shared-memory.js.map
|
|
@@ -7,10 +7,19 @@
|
|
|
7
7
|
* 已含本路径,故 no-token 503 / draining / model-ready 三闸在进本域前就已生效。本域零可变状态。
|
|
8
8
|
*/
|
|
9
9
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
10
|
-
import type { RouteCtx } from "../route-ctx.js";
|
|
10
|
+
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
11
11
|
/** 表内=core 的前置校验形(客户端可改请求)⇒ 400;表外一律 500。 */
|
|
12
12
|
export declare function isCorePreflightValidationError(message: string): boolean;
|
|
13
13
|
/** 供跨仓锚门读:门要拿这张表去装机的 core 里逐条对账(见上方 doc)。 */
|
|
14
14
|
export declare const CORE_PREFLIGHT_VALIDATION_ANCHORS: readonly string[];
|
|
15
|
-
|
|
15
|
+
/** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。 */
|
|
16
|
+
export declare function handleSideQuery(req: IncomingMessage, res: ServerResponse, match: RouteMatch<SideQueryRouteId>, ctx: RouteCtx): Promise<void>;
|
|
17
|
+
export declare const SIDE_QUERY_ROUTES: readonly [{
|
|
18
|
+
readonly id: "side-query";
|
|
19
|
+
readonly path: "/v1/side-query";
|
|
20
|
+
readonly billable: true;
|
|
21
|
+
readonly methods: readonly ["POST"];
|
|
22
|
+
}];
|
|
23
|
+
/** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
|
|
24
|
+
export type SideQueryRouteId = RouteIdsOf<typeof SIDE_QUERY_ROUTES>;
|
|
16
25
|
//# sourceMappingURL=side-query.d.ts.map
|