@sema-agent/server 7.131.0 → 7.132.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/MIGRATION.md +309 -0
- package/README.md +5 -1
- package/README.zh-CN.md +3 -1
- package/USAGE.md +163 -54
- package/dist/approval-card.d.ts +3 -1
- package/dist/approval-card.js +2 -3
- package/dist/audit.d.ts +7 -10
- package/dist/auth-bridge.d.ts +4 -0
- package/dist/auth-bridge.js +10 -11
- package/dist/boot/deployment-mods.d.ts +31 -0
- package/dist/boot/deployment-mods.js +26 -0
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/execution-env.js +19 -6
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
- package/dist/boot/read-deny-anchor-audit.js +36 -0
- package/dist/boot/resolve-spec.d.ts +1 -5
- package/dist/boot/resolve-spec.js +19 -47
- package/dist/boot/runner-deps.d.ts +19 -8
- package/dist/boot/runner-deps.js +14 -2
- package/dist/boot/session-faces.d.ts +1 -7
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -4
- package/dist/boot/stage-05-execution-env.js +7 -10
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +5 -4
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +3 -3
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +3 -9
- package/dist/boot/stage-10-http-server.d.ts +3 -9
- package/dist/boot/stage-10-http-server.js +6 -7
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +5 -6
- package/dist/boot/web-admin.d.ts +0 -3
- package/dist/boot/web-admin.js +35 -35
- package/dist/brain.js +17 -22
- package/dist/budget.js +5 -4
- package/dist/caller-text-echo.d.ts +20 -0
- package/dist/caller-text-echo.js +24 -0
- package/dist/capabilities/brand-identity.d.ts +26 -0
- package/dist/capabilities/brand-identity.js +27 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +32 -59
- package/dist/capabilities/center-prompts.js +18 -32
- package/dist/capabilities/engine-capabilities.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +1 -1
- package/dist/capabilities/mcp-probe.d.ts +14 -1
- package/dist/capabilities/mcp-probe.js +22 -133
- package/dist/capabilities/scenarios.d.ts +34 -40
- package/dist/capabilities/scenarios.js +16 -33
- package/dist/capabilities/skills.d.ts +19 -4
- package/dist/capabilities/skills.js +30 -10
- package/dist/config-catalog.js +6 -6
- package/dist/config-lkg.d.ts +9 -9
- package/dist/config-lkg.js +20 -4
- package/dist/config-provider.d.ts +3 -1
- package/dist/config-provider.js +12 -4
- package/dist/config-types.d.ts +7 -9
- package/dist/config.js +17 -4
- package/dist/device-ws-protocol.d.ts +1 -1
- package/dist/device-ws-protocol.js +1 -0
- package/dist/env-name-allowlist-knobs.d.ts +12 -2
- package/dist/env-name-allowlist-knobs.js +5 -4
- package/dist/exec-bounds.d.ts +15 -3
- package/dist/exec-bounds.js +4 -0
- package/dist/execution-lane-caps.d.ts +9 -2
- package/dist/execution-lane-caps.js +11 -0
- package/dist/hooks/classic-stations.d.ts +86 -0
- package/dist/hooks/classic-stations.js +27 -0
- package/dist/hooks/hook-llm.d.ts +8 -5
- package/dist/hooks/hook-llm.js +78 -35
- package/dist/hooks/hook-runner.d.ts +232 -80
- package/dist/hooks/hook-runner.js +218 -312
- package/dist/http/admission.d.ts +8 -2
- package/dist/http/admission.js +101 -57
- package/dist/http/approval-sweeps.js +57 -16
- package/dist/http/checkpoint-ctx.d.ts +25 -8
- package/dist/http/checkpoint-ctx.js +11 -7
- package/dist/http/claimed-run.d.ts +13 -0
- package/dist/http/claimed-run.js +15 -0
- package/dist/http/device-placement.js +5 -10
- package/dist/http/dispatch.d.ts +0 -4
- package/dist/http/dispatch.js +5 -9
- package/dist/http/durable-run-leg.d.ts +2 -1
- package/dist/http/durable-run-leg.js +5 -3
- package/dist/http/health-self-report.d.ts +7 -5
- package/dist/http/health-self-report.js +4 -18
- package/dist/http/principal-gate.d.ts +23 -6
- package/dist/http/principal-gate.js +24 -5
- package/dist/http/refusal-body.d.ts +21 -0
- package/dist/http/refusal-body.js +4 -0
- package/dist/http/request-guards.d.ts +27 -0
- package/dist/http/request-guards.js +24 -3
- package/dist/http/resume-legs.d.ts +25 -0
- package/dist/http/resume-legs.js +43 -22
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/rules.js +2 -2
- package/dist/http/routes/runs.js +7 -7
- package/dist/http/routes/session-sync.js +4 -2
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/shared-memory.js +2 -13
- package/dist/http/routes/side-query.js +2 -3
- package/dist/http/routes/tasks.js +19 -9
- package/dist/http/send.d.ts +9 -7
- package/dist/http/send.js +7 -2
- package/dist/http/server.d.ts +21 -11
- package/dist/http/server.js +28 -2
- package/dist/http/submit-ctx.d.ts +25 -0
- package/dist/http/submit-ctx.js +39 -0
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lane-home-probe.d.ts +43 -0
- package/dist/lane-home-probe.js +35 -0
- package/dist/limit-refusal.d.ts +47 -0
- package/dist/limit-refusal.js +12 -0
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +1 -9
- package/dist/mcp-assertion-header.js +0 -6
- package/dist/mcp-tool-faces.d.ts +5 -0
- package/dist/mcp-tool-faces.js +5 -1
- package/dist/memory-scope.d.ts +1 -1
- package/dist/model-provider.d.ts +39 -12
- package/dist/model-provider.js +19 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-endpoint.js +5 -5
- package/dist/observability/cost-taxonomy.d.ts +7 -7
- package/dist/observability/fail-open.d.ts +42 -6
- package/dist/observability/fail-open.js +42 -6
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/prompt-manifest.d.ts +47 -82
- package/dist/observability/prompt-manifest.js +119 -17
- package/dist/observability/run-terminal.js +14 -6
- package/dist/observability/secret-env-scrub.d.ts +13 -1
- package/dist/observability/secret-env-scrub.js +13 -1
- package/dist/observability/tool-trace.d.ts +16 -21
- package/dist/observability/tool-trace.js +51 -42
- package/dist/per-task-image.d.ts +7 -1
- package/dist/per-task-image.js +3 -3
- package/dist/plain-string-record.d.ts +25 -0
- package/dist/plain-string-record.js +32 -0
- package/dist/plugins/blob-backend.d.ts +68 -13
- package/dist/plugins/blob-backend.js +53 -10
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
- package/dist/plugins/checkpoint-store-sql.js +39 -26
- package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
- package/dist/plugins/k8s-bg-scripts.js +22 -0
- package/dist/plugins/local-checkpoint-store.js +5 -2
- package/dist/plugins/local-session-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +137 -90
- package/dist/plugins/one-time-migrations.js +356 -207
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
- package/dist/plugins/permission-rule-store-sql.js +4 -18
- package/dist/plugins/pg-session-storage.d.ts +4 -5
- package/dist/plugins/pg-session-storage.js +7 -17
- package/dist/plugins/remote-env-adb.d.ts +6 -0
- package/dist/plugins/remote-env-adb.js +9 -4
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +1 -1
- package/dist/plugins/remote-env-e2b.js +21 -9
- package/dist/plugins/remote-env-host.d.ts +9 -9
- package/dist/plugins/remote-env-host.js +10 -32
- package/dist/plugins/remote-env-k8s.d.ts +95 -26
- package/dist/plugins/remote-env-k8s.js +265 -90
- package/dist/plugins/remote-env-local-docker.d.ts +20 -1
- package/dist/plugins/remote-env-local-docker.js +77 -9
- package/dist/plugins/remote-env-ssh.d.ts +15 -0
- package/dist/plugins/remote-env-ssh.js +109 -3
- package/dist/plugins/remote-shell.d.ts +0 -1
- package/dist/plugins/remote-shell.js +1 -2
- package/dist/plugins/retention-store-sql.js +6 -2
- package/dist/plugins/schema-consistency.d.ts +54 -0
- package/dist/plugins/schema-consistency.js +52 -0
- package/dist/plugins/session-epoch-anchor.d.ts +13 -0
- package/dist/plugins/session-epoch-anchor.js +23 -0
- package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
- package/dist/plugins/shared-memory-store-sql.js +5 -16
- package/dist/plugins/sql-driver.d.ts +141 -0
- package/dist/plugins/sql-driver.js +115 -1
- package/dist/plugins/sql-errors.d.ts +41 -0
- package/dist/plugins/sql-errors.js +36 -0
- package/dist/plugins/store-backend.js +2 -1
- package/dist/plugins/tidb-session-storage.d.ts +7 -11
- package/dist/plugins/tidb-session-storage.js +6 -16
- package/dist/plugins/tidb-session-store.js +1 -1
- package/dist/prompts-domain-validate.d.ts +17 -61
- package/dist/prompts-domain-validate.js +9 -119
- package/dist/request-key-closure.d.ts +2 -2
- package/dist/request-key-closure.js +2 -4
- package/dist/rule-entry-list.d.ts +12 -9
- package/dist/rule-entry-list.js +37 -21
- package/dist/run-local.js +19 -19
- package/dist/runs.d.ts +6 -0
- package/dist/runs.js +16 -0
- package/dist/server-secret-env.d.ts +44 -18
- package/dist/server-secret-env.js +49 -8
- package/dist/session-incarnation.d.ts +15 -5
- package/dist/session-incarnation.js +16 -9
- package/dist/session-titler.js +12 -2
- package/dist/spec-fields.d.ts +28 -16
- package/dist/spec-fields.js +46 -53
- package/dist/sql-engine-posture.d.ts +29 -4
- package/dist/sql-engine-posture.js +36 -1
- package/dist/stored-request-tool-axes-m19.d.ts +19 -4
- package/dist/stored-request-tool-axes-m19.js +30 -2
- package/dist/task-a2a.d.ts +9 -1
- package/dist/task-a2a.js +73 -47
- package/dist/task-mcp.d.ts +46 -58
- package/dist/task-mcp.js +99 -65
- package/dist/task-settings.d.ts +7 -21
- package/dist/task-settings.js +7 -36
- package/dist/text-bidi.d.ts +3 -46
- package/dist/text-bidi.js +3 -3
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +24 -4
- package/dist/trace/engine-notice-wire.d.ts +11 -3
- package/dist/trace/engine-notice-wire.js +8 -1
- package/dist/trace/ledger-events.d.ts +3 -1
- package/dist/trace/ledger-sink.js +4 -1
- package/dist/trace/model-segment.js +1 -1
- package/dist/trace/project.d.ts +19 -6
- package/dist/trace/project.js +57 -22
- package/dist/trace/wire-projection-faces.d.ts +79 -1
- package/dist/trace/wire-projection-faces.js +22 -0
- package/dist/web-admin-client/candidate-gate.d.ts +6 -6
- package/dist/web-admin-client/candidate-gate.js +3 -3
- package/dist/web-admin-client/http-client.js +0 -6
- package/dist/web-admin-client/read-warnings.d.ts +13 -0
- package/dist/web-admin-client/read-warnings.js +10 -0
- package/dist/web-admin-client/skills-mcp.d.ts +3 -0
- package/dist/web-admin-client/skills-mcp.js +9 -6
- package/dist/web-admin-client/types.d.ts +2 -3
- package/dist/workspace-cache.d.ts +35 -0
- package/dist/workspace-cache.js +36 -0
- package/dist/workspace-space.d.ts +3 -2
- package/dist/workspace-space.js +2 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
- package/package.json +4 -4
- package/scripts/drain-check-local.mjs +34 -1
- package/scripts/drain-check.mysql.sql +30 -2
- package/scripts/drain-check.pg.sql +29 -2
- package/dist/capabilities/memory-notice.d.ts +0 -138
- package/dist/capabilities/memory-notice.js +0 -64
- package/dist/capabilities/prompt.d.ts +0 -17
- package/dist/capabilities/prompt.js +0 -6
- package/dist/mcp-content-origin.d.ts +0 -34
- package/dist/mcp-content-origin.js +0 -10
- package/dist/tool-axis-words.d.ts +0 -27
- package/dist/tool-axis-words.js +0 -9
- package/dist/wire-name-bound.d.ts +0 -14
- package/dist/wire-name-bound.js +0 -13
package/dist/http/routes/runs.js
CHANGED
|
@@ -17,7 +17,7 @@ import { ASK_DECISION_CONSUMER_ABSENT, outcomeOfDecidedRow, resolveStreamApprova
|
|
|
17
17
|
import { MAX_DECISION_NOTE_CHARS } from "../../approval-ask-machine.js";
|
|
18
18
|
import { causeOfAskRow } from "../../approval-deny-reasons.js";
|
|
19
19
|
import { normalizeRunEventType, screenedStoredEventData } from "../../trace/project.js";
|
|
20
|
-
import { sendJson, sendError,
|
|
20
|
+
import { sendJson, sendError, sendHttpError, sseHeaders } from "../send.js";
|
|
21
21
|
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
22
22
|
import { acceptedInputKindOf } from "../../user-input-ledger.js";
|
|
23
23
|
import { INJECTION_TIERS } from "../../trace/injection-tier.js";
|
|
@@ -405,7 +405,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
405
405
|
}
|
|
406
406
|
catch (e) {
|
|
407
407
|
if (e instanceof HttpError) {
|
|
408
|
-
|
|
408
|
+
sendHttpError(res, e);
|
|
409
409
|
return;
|
|
410
410
|
}
|
|
411
411
|
sendError(res, 400, "request.invalid_json", "invalid JSON body");
|
|
@@ -693,7 +693,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
693
693
|
}
|
|
694
694
|
if (mintResult.unservable !== undefined) {
|
|
695
695
|
deps.logger?.warn?.("steer_wake_park_refused_stored_body", { taskId, sessionId: run.sessionId, ...storedBodyReplayLogFields(mintResult.unservable) });
|
|
696
|
-
const { status, body: { error, errorCode, ...rest } } = storedBodyReplayAnswer(mintResult.unservable, serviceVersion());
|
|
696
|
+
const { status, body: { error, errorCode, ...rest } } = storedBodyReplayAnswer(mintResult.unservable, serviceVersion(), false);
|
|
697
697
|
sendError(res, status, errorCode, error, rest);
|
|
698
698
|
return;
|
|
699
699
|
}
|
|
@@ -730,7 +730,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
730
730
|
}
|
|
731
731
|
catch (e) {
|
|
732
732
|
if (e instanceof HttpError) {
|
|
733
|
-
|
|
733
|
+
sendHttpError(res, e);
|
|
734
734
|
return;
|
|
735
735
|
}
|
|
736
736
|
sendError(res, 400, "request.invalid_json", "invalid JSON body");
|
|
@@ -893,7 +893,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
893
893
|
}
|
|
894
894
|
catch (e) {
|
|
895
895
|
if (e instanceof HttpError) {
|
|
896
|
-
|
|
896
|
+
sendHttpError(res, e);
|
|
897
897
|
return;
|
|
898
898
|
}
|
|
899
899
|
sendError(res, 400, "request.invalid_json", "invalid JSON body");
|
|
@@ -944,7 +944,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
944
944
|
}
|
|
945
945
|
catch (e) {
|
|
946
946
|
if (e instanceof HttpError) {
|
|
947
|
-
|
|
947
|
+
sendHttpError(res, e);
|
|
948
948
|
return;
|
|
949
949
|
}
|
|
950
950
|
sendError(res, 400, "request.invalid_json", "invalid JSON body");
|
|
@@ -1170,7 +1170,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1170
1170
|
}
|
|
1171
1171
|
catch (e) {
|
|
1172
1172
|
if (e instanceof HttpError) {
|
|
1173
|
-
|
|
1173
|
+
sendHttpError(res, e);
|
|
1174
1174
|
return;
|
|
1175
1175
|
}
|
|
1176
1176
|
sendError(res, 400, "request.invalid_json", "invalid JSON body");
|
|
@@ -6,6 +6,7 @@ import { exportSessionManifest, fileHistoryImportFace, replayFileHistory, replay
|
|
|
6
6
|
import * as facts from "../../capability-facts.js";
|
|
7
7
|
import { classifySyncRelationshipByIds, SyncConflictError } from "../../session-sync-kernel.js";
|
|
8
8
|
import { isUuidShape } from "../../security.js";
|
|
9
|
+
import { sqlBlobCapRefusalText } from "../../plugins/blob-backend.js";
|
|
9
10
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, msg } from "../send.js";
|
|
10
11
|
import { buildDisplaySafeUrl } from "../../trace/redact.js";
|
|
11
12
|
import { gatedPrincipal, explicitOperatorOk, sendUnauthorized } from "../principal-gate.js";
|
|
@@ -186,8 +187,9 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
186
187
|
}
|
|
187
188
|
const fs = blobs.historyStore;
|
|
188
189
|
const body = await readRawBody(req, SYNC_BLOB_MAX_BYTES);
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
const sqlCap = deps.snapshotBlobSqlCap?.();
|
|
191
|
+
if (sqlCap !== undefined && body.byteLength > sqlCap.capBytes) {
|
|
192
|
+
sendError(res, 413, "blob_too_large_for_sql", sqlBlobCapRefusalText(body.byteLength, sqlCap), { wallSource: sqlCap.source });
|
|
191
193
|
return;
|
|
192
194
|
}
|
|
193
195
|
if (createHash("sha256").update(body).digest("hex") !== hash) {
|
|
@@ -54,7 +54,7 @@ async function readLastLegMcp(deps, sessionId, principal) {
|
|
|
54
54
|
const latest = await runStore.latestRunForSession(sessionId);
|
|
55
55
|
if (latest === undefined)
|
|
56
56
|
return undefined;
|
|
57
|
-
if (!sessionOwnerReadable(principal, latest.owner, deps
|
|
57
|
+
if (!sessionOwnerReadable(principal, latest.owner, deps) && !explicitOperatorOk(principal, deps.config.operatorPrincipals))
|
|
58
58
|
return undefined;
|
|
59
59
|
const events = await runStore.getEvents(latest.taskId, 0);
|
|
60
60
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import { SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError } from "@sema-agent/core";
|
|
1
|
+
import { isSharedMemoryDocumentPath, SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError } from "@sema-agent/core";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedPrincipal, sendUnauthorized } from "../principal-gate.js";
|
|
4
4
|
import { assertNever } from "../route-ctx.js";
|
|
5
5
|
import * as facts from "../../capability-facts.js";
|
|
6
6
|
const RETRY_AFTER_SEC = 10;
|
|
7
7
|
const HTTP_LIST_LIMIT = 100;
|
|
8
|
-
const FORBIDDEN_PATH_CHARS = /[\p{Cc}\p{Cf}\p{Co}\p{Cn}\p{Default_Ignorable_Code_Point}\u2028\u2029\\]/u;
|
|
9
|
-
const DOCUMENT_SUFFIXES = [".md", ".txt", ".json", ".jsonl"];
|
|
10
|
-
function isDocumentPath(path) {
|
|
11
|
-
if (!path.startsWith("/") || FORBIDDEN_PATH_CHARS.test(path))
|
|
12
|
-
return false;
|
|
13
|
-
const segments = path.slice(1).split("/");
|
|
14
|
-
const last = segments.at(-1);
|
|
15
|
-
if (last === undefined || !segments.every((s) => s !== "" && !s.startsWith(".")))
|
|
16
|
-
return false;
|
|
17
|
-
return DOCUMENT_SUFFIXES.some((s) => last.endsWith(s));
|
|
18
|
-
}
|
|
19
8
|
function refuseStore(res) {
|
|
20
9
|
sendError(res, 404, "not_found.shared_memory_store", "not found");
|
|
21
10
|
}
|
|
@@ -120,7 +109,7 @@ export async function handleSharedMemory(req, res, match, ctx) {
|
|
|
120
109
|
return;
|
|
121
110
|
}
|
|
122
111
|
const path = query.get("path");
|
|
123
|
-
if (path === null || path === "" || !
|
|
112
|
+
if (path === null || path === "" || !isSharedMemoryDocumentPath(path)) {
|
|
124
113
|
sendError(res, 400, "request.query_invalid", "missing or malformed ?path= (absolute, .md/.txt/.json/.jsonl)");
|
|
125
114
|
return;
|
|
126
115
|
}
|
|
@@ -4,7 +4,7 @@ import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCata
|
|
|
4
4
|
import { modelUnavailableMessage } from "../../model-entry-refusal.js";
|
|
5
5
|
import { redactSecrets } from "../../trace/redact.js";
|
|
6
6
|
import { admitProviderEcho, redactErrorMessage } from "../../observability/run-terminal.js";
|
|
7
|
-
import { sendJson, sendError,
|
|
7
|
+
import { sendJson, sendError, sendHttpError, msg } from "../send.js";
|
|
8
8
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
9
9
|
import { assertNever } from "../route-ctx.js";
|
|
10
10
|
const CORE_PREFLIGHT_VALIDATION_PREFIXES = ["Unknown model ref ", "No model for role "];
|
|
@@ -50,8 +50,7 @@ export async function handleSideQuery(req, res, match, ctx) {
|
|
|
50
50
|
body = raw;
|
|
51
51
|
}
|
|
52
52
|
catch (e) {
|
|
53
|
-
|
|
54
|
-
sendError(res, he.status, httpErrorCode(he.status, he.code), he.message);
|
|
53
|
+
sendHttpError(res, e instanceof HttpError ? e : new HttpError(400, msg(e)));
|
|
55
54
|
return;
|
|
56
55
|
}
|
|
57
56
|
if (!Array.isArray(body.messages) || body.messages.length === 0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal.js";
|
|
2
2
|
import { runWithVerification, runCascade, taskNotificationDedupKey, uuidv7 } from "@sema-agent/core";
|
|
3
|
-
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, releaseKeyIfZeroWorkRefusal, createPromptCommitReader, HEARTBEAT_MS } from "../../runs.js";
|
|
3
|
+
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, zeroWorkRefusalFacts, releaseKeyIfZeroWorkRefusal, createPromptCommitReader, HEARTBEAT_MS } from "../../runs.js";
|
|
4
4
|
import { sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
5
5
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
6
6
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
|
|
@@ -14,7 +14,7 @@ import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "..
|
|
|
14
14
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
15
15
|
import { redactSecrets } from "../../trace/redact.js";
|
|
16
16
|
import { liveToolEndForBacklog } from "../../trace/tool-output-media.js";
|
|
17
|
-
import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData, liveIdentityFields } from "../../trace/project.js";
|
|
17
|
+
import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData, liveIdentityFields } from "../../trace/project.js";
|
|
18
18
|
import { cascadeConfig, runMeta } from "../run-meta.js";
|
|
19
19
|
import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
|
|
20
20
|
import { replayOfExistingRun } from "../submit-replay.js";
|
|
@@ -30,7 +30,7 @@ import { failOpenTagForDroppedFrame, recordFailOpen } from "../../observability/
|
|
|
30
30
|
import { buildSyncSubmitBody } from "../sync-submit-body.js";
|
|
31
31
|
import { assertNever } from "../route-ctx.js";
|
|
32
32
|
import * as facts from "../../capability-facts.js";
|
|
33
|
-
import {
|
|
33
|
+
import { persistSubmitCtx } from "../submit-ctx.js";
|
|
34
34
|
import { serviceVersion } from "../../service-version.js";
|
|
35
35
|
import { withModelReachScope } from "../../model-reach-scope.js";
|
|
36
36
|
import { createInstructionSelectionLeg } from "../../instruction-selection.js";
|
|
@@ -286,14 +286,18 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
286
286
|
}
|
|
287
287
|
durableTaskId = tid;
|
|
288
288
|
}
|
|
289
|
+
if (deps.checkpointStore && prepared.spec.sessionId) {
|
|
290
|
+
const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() });
|
|
291
|
+
if (ctxRefusal) {
|
|
292
|
+
sseData(res, { type: "done", result: doneResultOfOutcome(ctxRefusal) });
|
|
293
|
+
return ctxRefusal;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
289
296
|
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
290
297
|
if (prepared.spec.sessionId)
|
|
291
298
|
markChildrenStoppedByUserOnAbort(ac.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
292
299
|
let ledgerSink;
|
|
293
300
|
let ledgerTerminal = false;
|
|
294
|
-
if (deps.checkpointStore && prepared.spec.sessionId) {
|
|
295
|
-
await deps.checkpointStore.putCtx(prepared.spec.sessionId, buildCheckpointCtx({ body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() }));
|
|
296
|
-
}
|
|
297
301
|
if (durableTaskId && deps.runStore) {
|
|
298
302
|
const rs = deps.runStore;
|
|
299
303
|
const tid = durableTaskId;
|
|
@@ -541,6 +545,9 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
541
545
|
else if (ev.type === "compaction_outcome") {
|
|
542
546
|
sseData(res, { type: "compaction_outcome", ...compactionOutcomeEventData(ev) });
|
|
543
547
|
}
|
|
548
|
+
else if (ev.type === "compaction_started") {
|
|
549
|
+
sseData(res, { type: "compaction_started", ...compactionStartedEventData(ev) });
|
|
550
|
+
}
|
|
544
551
|
else if (ev.type === "wiring_manifest") {
|
|
545
552
|
const wiringFace = wiringManifestFaceEventData(ev, wiringOperatorFace, instructionLeg.segment());
|
|
546
553
|
wiringReceipt ??= wiringFace;
|
|
@@ -857,10 +864,12 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
857
864
|
return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
|
|
858
865
|
durableTaskId = tid;
|
|
859
866
|
}
|
|
860
|
-
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
861
867
|
if (deps.checkpointStore && prepared.spec.sessionId) {
|
|
862
|
-
await deps.checkpointStore.
|
|
868
|
+
const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, durableTaskId !== undefined && deps.runStore ? { runStore: deps.runStore, taskId: durableTaskId } : undefined, prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope, writerVersion: serviceVersion() });
|
|
869
|
+
if (ctxRefusal)
|
|
870
|
+
return ctxRefusal;
|
|
863
871
|
}
|
|
872
|
+
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
864
873
|
const syncCancelCtrl = createRunAbort();
|
|
865
874
|
if (prepared.spec.sessionId)
|
|
866
875
|
markChildrenStoppedByUserOnAbort(syncCancelCtrl.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
@@ -933,7 +942,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
933
942
|
const retryAfterSec = resumeAtPlane.errorCode === "memory.admission_required" && result.retryAfterMs !== undefined
|
|
934
943
|
? Math.max(1, Math.ceil(result.retryAfterMs / 1000))
|
|
935
944
|
: undefined;
|
|
936
|
-
|
|
945
|
+
const refusalFacts = zeroWorkRefusalFacts(redactTerminalResult(sealResultForExit(result)).terminal);
|
|
946
|
+
return { status: resumeAtStatus, body: { errorCode: resumeAtPlane.errorCode, error: redactErrorMessage(resumeAtPlane.errorMessage) ?? "resume-at failed", ...(retryAfterSec !== undefined ? { retryAfterSec } : {}), ...refusalFacts } };
|
|
937
947
|
}
|
|
938
948
|
const exitResult = await mintSubmitExitResult(result, deps.checkpointStore);
|
|
939
949
|
const syncPlane = outwardPlane(result.terminal);
|
package/dist/http/send.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { ServerResponse } from "node:http";
|
|
11
11
|
import { type SessionErrorFace } from "../session-error-wire.js";
|
|
12
12
|
import type { HttpError } from "../security.js";
|
|
13
|
+
import { type Refusal, type RefusalBody } from "./refusal-body.js";
|
|
13
14
|
/**
|
|
14
15
|
* [ref](黑板 [ref]②)—— 一条响应「是 SSE 流」的**在场标记**。
|
|
15
16
|
*
|
|
@@ -70,19 +71,20 @@ export declare function sendJson(res: ServerResponse, status: number, body: unkn
|
|
|
70
71
|
* change), not a new response shape. Sites where the two machine-code channels genuinely disagree, or
|
|
71
72
|
* where `error`/`errorCode` aren't cleanly separable from the rest of the body, are left on raw
|
|
72
73
|
* `sendJson` (see the A6 migration notes for the callers still doing that on purpose). */
|
|
73
|
-
export declare function sendError(res: ServerResponse, status: number, errorCode: string, message: string, extra?: Record<string, unknown
|
|
74
|
+
export declare function sendError(res: ServerResponse, status: number, errorCode: string, message: string, extra?: Readonly<Record<string, unknown>>): void;
|
|
74
75
|
/** B7([ref] lens1 §C3):`HttpError` 的兜底 catch-all 站点没有自己的语义分支——`err.code` 在 42 个
|
|
75
76
|
* throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码**
|
|
76
77
|
* 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */
|
|
77
78
|
export declare function httpErrorCode(status: number, code?: string): string;
|
|
78
79
|
/**
|
|
79
|
-
* 一只 `HttpError`
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* 同一只错在两处渲出的成员逐字同,面板不自己拼 `{ errorCode, error }`(拼了就丢 `extra`:壳分不出这次门拒是中心指派漂了还是部署缺省悬空)。
|
|
80
|
+
* 一只 `HttpError` 读成 {@link Refusal}:码经 {@link httpErrorCode}(带码的 throw 原样优先,否则按状态给粗码),句 = `message`,机读事实 = `extra`
|
|
81
|
+
* (如场景门的 `detail: { scenario, source }`、条数帽的 `field` / `reason` / `count` / `cap`)。**拆 `HttpError` 的唯一一处** —— 别的站点自己拆
|
|
82
|
+
* `status` / `code` / `message` 拼体就丢了 `extra`(`test/refusal-one-composer.test.ts` 的静态门钉着)。
|
|
83
83
|
*/
|
|
84
|
-
export declare function
|
|
85
|
-
/**
|
|
84
|
+
export declare function httpErrorRefusal(e: HttpError): Refusal;
|
|
85
|
+
/** 一只 `HttpError` 的拒体成员 = {@link refusalBody}({@link httpErrorRefusal})。 */
|
|
86
|
+
export declare function httpErrorBody(e: HttpError): RefusalBody;
|
|
87
|
+
/** 整面拒:状态 = `e.status`,体 = {@link httpErrorBody}。手里拿着一只 `HttpError` 的路由站点一律调它。 */
|
|
86
88
|
export declare function sendHttpError(res: ServerResponse, e: HttpError): void;
|
|
87
89
|
/**
|
|
88
90
|
* 🔴 B1 / S-535 —— `SessionPolicyError` → 应答,经 {@link sessionPolicyErrorHttp} 那**一只**映射
|
package/dist/http/send.js
CHANGED
|
@@ -2,6 +2,7 @@ import { SessionError, SessionPolicyError } from "@sema-agent/core";
|
|
|
2
2
|
import { sessionPolicyErrorFacts, sessionPolicyErrorHttp, sessionPolicyErrorSentence } from "../session-policy-wire.js";
|
|
3
3
|
import { sessionErrorHttp } from "../session-error-wire.js";
|
|
4
4
|
import { redactErrorMessage } from "../observability/run-terminal.js";
|
|
5
|
+
import { refusalBody } from "./refusal-body.js";
|
|
5
6
|
export const SSE_RESPONSE_MARK = Symbol("sema.http.sse-response");
|
|
6
7
|
export function isSseResponse(res) {
|
|
7
8
|
return res[SSE_RESPONSE_MARK] === true;
|
|
@@ -31,7 +32,7 @@ export function sendJson(res, status, body) {
|
|
|
31
32
|
res.end(JSON.stringify(body));
|
|
32
33
|
}
|
|
33
34
|
export function sendError(res, status, errorCode, message, extra) {
|
|
34
|
-
sendJson(res, status, {
|
|
35
|
+
sendJson(res, status, refusalBody(extra === undefined ? { errorCode, message } : { errorCode, message, facts: extra }));
|
|
35
36
|
}
|
|
36
37
|
export function httpErrorCode(status, code) {
|
|
37
38
|
if (code)
|
|
@@ -48,8 +49,12 @@ export function httpErrorCode(status, code) {
|
|
|
48
49
|
default: return status >= 500 ? "internal.error" : "request.rejected";
|
|
49
50
|
}
|
|
50
51
|
}
|
|
52
|
+
export function httpErrorRefusal(e) {
|
|
53
|
+
const errorCode = httpErrorCode(e.status, e.code);
|
|
54
|
+
return e.extra === undefined ? { errorCode, message: e.message } : { errorCode, message: e.message, facts: e.extra };
|
|
55
|
+
}
|
|
51
56
|
export function httpErrorBody(e) {
|
|
52
|
-
return
|
|
57
|
+
return refusalBody(httpErrorRefusal(e));
|
|
53
58
|
}
|
|
54
59
|
export function sendHttpError(res, e) {
|
|
55
60
|
sendJson(res, e.status, httpErrorBody(e));
|
package/dist/http/server.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { RunStore, ImageIndex, ImageBake, CheckpointStoreFull, ResumeAnchor
|
|
|
10
10
|
import type { RuleConsentLane } from "../rules-consent.js";
|
|
11
11
|
import { type MemorySyncRequest, type MemorySyncResponse } from "../memory-sync.js";
|
|
12
12
|
import type { TaskAttachmentStore } from "../plugins/task-attachment-store.js";
|
|
13
|
+
import type { SqlBlobCapReading } from "../plugins/blob-backend.js";
|
|
13
14
|
import type { LeaderEndpoint } from "../leader/endpoint.js";
|
|
14
15
|
import { type WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
|
|
15
16
|
import type { SubagentSteerRegistry } from "../orchestration/subagent-steer.js";
|
|
@@ -34,11 +35,10 @@ export { cascadeConfig };
|
|
|
34
35
|
import { coarseStatusForState, errorCodeForExit } from "./routes/images.js";
|
|
35
36
|
export { coarseStatusForState, errorCodeForExit };
|
|
36
37
|
import { MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills } from "./admission.js";
|
|
37
|
-
import { hasServiceAuth } from "./dispatch.js";
|
|
38
38
|
import { routeLabel, isBillableSubmitPath, isCredentialGatedRewrite, isMethodDispatchedSubmitPath } from "./route-table.js";
|
|
39
39
|
export { routeLabel, isBillableSubmitPath, isCredentialGatedRewrite, isMethodDispatchedSubmitPath, hasServiceAuth };
|
|
40
40
|
export { MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills };
|
|
41
|
-
import { explicitOperatorOk, isOperator } from "./principal-gate.js";
|
|
41
|
+
import { explicitOperatorOk, isOperator, hasServiceAuth } from "./principal-gate.js";
|
|
42
42
|
export { explicitOperatorOk, isOperator };
|
|
43
43
|
/** Per-request authorization context resolved before building the spec (identity, owned session). */
|
|
44
44
|
export interface RequestAuth {
|
|
@@ -777,12 +777,18 @@ export interface ServiceDeploymentDeps {
|
|
|
777
777
|
* `GET /v1/capabilities` 的 `memoryEngine` 位(窄投影)。缺席 = 这个进程不是由 composition root 装
|
|
778
778
|
* 起来的(测试夹具形)⇒ 诊断面报 null、能力位取诚实下限 false。 */
|
|
779
779
|
memoryPosture?: import("../memory-posture.js").MemoryPosture;
|
|
780
|
-
/** S-131:SQL
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
|
|
780
|
+
/** S-131:SQL **引擎读口**(`sql-engine-posture.ts` 的单一推导点 —— boot 期借一条池连接触发驱动的连接初始化,交回本池的驱动:
|
|
781
|
+
* `dialect`(SQL 文本轴,恒在)+ 活的 `facts`(`SET` 之后**回读复核**过的真值;池重连后驱动会重新初始化并刷新,快照会说谎)。
|
|
782
|
+
* 读者:`GET /v1/diagnostics/wiring` 的 `sqlEngine` 段(operator-only,全量含版本与两堵墙)与 `GET /v1/capabilities` 的 `store.sql` 位(窄投影;
|
|
783
|
+
* 7.106.0 前是顶层 `sql`,S-866 ①)读 `facts`;提交受理口的 413 把整只读口交给唯一判官 `sql-driver.ts` `sqlWallsOf`(本服务的装配另用同一只读口铸
|
|
784
|
+
* `snapshotBlobSqlCap`,`PUT /sync/blobs` 的前置帽)—— 墙的方言只从这里来(与事实同出一只驱动),不另从店后端读。
|
|
785
|
+
* 两种缺席、两个值:**席缺席** = 本部署没有 SQL 后端(env-only worker、local 文件后端);**`facts` 是 `null`** = 有 SQL 后端、但事实此刻没建立
|
|
786
|
+
* (池上最近一次连接初始化被拒,驱动当场作废了事实)。两者在两个读面上都报缺席(诚实缺席,不铸空壳);墙按方言读后者(MySQL 协议 ⇒ 最保守的假定墙)。
|
|
787
|
+
* 7.132.0 前这一席是 `sqlEngineFacts: () => SqlEngineFacts | null`(只给事实、不给方言);照旧传它 ⇒ `createHttpServer` 起服拒。
|
|
788
|
+
* 🔴 **SQL 后端在场(`backend` 交出了 `mysqlPool()` / `pgPool()`)⇒ 本席必填**,缺席 ⇒ `createHttpServer` 起服拒(席缺席只表示「没有 SQL 后端」,不能是「忘了给」);
|
|
789
|
+
* 本服务自己的装配(`boot/stores.ts` → `stage-10`)按同一个谓词(`sqlPoolsOf`)建立它,恒给。值形也在起服时判(同一只判官 `sqlEngineSeatIssue` 的另一臂):
|
|
790
|
+
* `{ dialect: "tidb" | "pg", facts }`,`facts` 是 `null` 或带 `maxRowBytes` / `maxPacketBytes` / `wallSources` 的引擎事实;只改了键名的 `() => facts` 之类 ⇒ 起服拒。 */
|
|
791
|
+
sqlEngine?: import("../plugins/sql-driver.js").SqlEngineReading;
|
|
786
792
|
/** S-138:**写保护名表**的姿态(`write-protection.ts` 的 boot 期单一推导点 —— 与递给每只 Runner 的
|
|
787
793
|
* `writeProtectedPaths` 座同一份 config 决定)。两个读面共用:`GET /v1/diagnostics/wiring` 的
|
|
788
794
|
* `writeProtection` 段(operator-only,**逐行**表 + 来源 + 被丢的缺省行名)与 `GET /v1/capabilities`
|
|
@@ -821,10 +827,14 @@ export interface ServiceDeploymentDeps {
|
|
|
821
827
|
/** **数值旋钮**:部署可调的上限/拍距。缺省值写在消费点(`?? 256` 之类),此处只承载覆写。 */
|
|
822
828
|
export interface ServiceKnobDeps {
|
|
823
829
|
/** (a)(clay 拍 a+c,2026-07-27)当快照 blob 走 SQL 默认店时(逃生口形/覆写形),该店的 per-blob
|
|
824
|
-
*
|
|
825
|
-
* 形无帽)
|
|
830
|
+
* 承载帽的**读口**——PUT /sync/blobs 面每个请求调一次、据此**前置 413**(typed `blob_too_large_for_sql`)。
|
|
831
|
+
* 键缺席=不前置(MinIO 形无帽);读口答 `undefined`=此刻没有墙(pg 形)=不前置。读口而非数字:帽由库报的墙推导,
|
|
832
|
+
* 库调墙、池连接换代后活事实会变,门限必须跟着变(与店写口 `SqlBlobBackend` 读同一份池级事实、同一只函数
|
|
833
|
+
* `sqlBlobCapOf`)。读口答的是**帽 + 它的出处**(`{ capBytes, source }`,`source` ∈ `operator` / `reported` / `assumed`,见 `SqlBlobCapReading`):
|
|
834
|
+
* 413 的句子与机读位 `wallSource` 按出处分三形(运维旋钮 / 库报的墙 / 假定的墙),不把假定或运维的旋钮说成库的上限。
|
|
835
|
+
* 为什么在 HTTP 面重复店里的检查:店的 never-throw 结果形把一切写失败折成 502,而 502
|
|
826
836
|
* 语义=可重试,对端会对一个永远过不去的 blob 无限重试——413 才是「别再送了」的诚实形。 */
|
|
827
|
-
|
|
837
|
+
snapshotBlobSqlCap?: () => SqlBlobCapReading | undefined;
|
|
828
838
|
/** [ref] SSE 连接帽(缺省 256/副本;超限 503+Retry-After=消费方回落 /head 轮询)。 */
|
|
829
839
|
sessionEventsMaxConnections?: number;
|
|
830
840
|
/** [ref] SSE 心跳/owner 复核拍距 ms(缺省 25s;测试注入用)。 */
|
package/dist/http/server.js
CHANGED
|
@@ -3,6 +3,7 @@ import { outwardPlane } from "../terminal.js";
|
|
|
3
3
|
import { redactHead } from "../observability/run-terminal.js";
|
|
4
4
|
import { validatePendingSteer } from "@sema-agent/core";
|
|
5
5
|
import {} from "../web-admin-client/facade.js";
|
|
6
|
+
import { describeValueShape } from "@sema-agent/settings-schema";
|
|
6
7
|
import { HttpError } from "../security.js";
|
|
7
8
|
import {} from "../memory-sync.js";
|
|
8
9
|
import {} from "../orchestration/workflow-agent-steer.js";
|
|
@@ -19,15 +20,16 @@ import { coarseStatusForState, errorCodeForExit } from "./routes/images.js";
|
|
|
19
20
|
export { coarseStatusForState, errorCodeForExit };
|
|
20
21
|
import { sendError, sendHttpError, sendSessionError, msg } from "./send.js";
|
|
21
22
|
import { createRequestGuards } from "./request-guards.js";
|
|
23
|
+
import { sqlEngineSeatIssue } from "../sql-engine-posture.js";
|
|
22
24
|
import { createAdmission, MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills } from "./admission.js";
|
|
23
25
|
import { createResumeLegs } from "./resume-legs.js";
|
|
24
26
|
import * as facts from "../capability-facts.js";
|
|
25
|
-
import { createDispatch
|
|
27
|
+
import { createDispatch } from "./dispatch.js";
|
|
26
28
|
import { routeLabel, isBillableSubmitPath, isCredentialGatedRewrite, isMethodDispatchedSubmitPath, isProbePath } from "./route-table.js";
|
|
27
29
|
import { createApprovalSweeps } from "./approval-sweeps.js";
|
|
28
30
|
export { routeLabel, isBillableSubmitPath, isCredentialGatedRewrite, isMethodDispatchedSubmitPath, hasServiceAuth };
|
|
29
31
|
export { MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills };
|
|
30
|
-
import { explicitOperatorOk, isOperator } from "./principal-gate.js";
|
|
32
|
+
import { explicitOperatorOk, isOperator, hasServiceAuth } from "./principal-gate.js";
|
|
31
33
|
export { explicitOperatorOk, isOperator };
|
|
32
34
|
class DrainYieldingPreemptBook extends Map {
|
|
33
35
|
drainingNow;
|
|
@@ -57,6 +59,30 @@ export function createHttpServer(rawDeps) {
|
|
|
57
59
|
if (typeof deps.runnerFor !== "function") {
|
|
58
60
|
throw new Error("createHttpServer: the runnerFor seat is required (it picks each task's Runner from its execution lane) — pass the hands-lane selector, or `runnerFor: () => runner` for a single-Runner embedding; refusing to start rather than run every task on the widest Runner");
|
|
59
61
|
}
|
|
62
|
+
if (deps.snapshotBlobSqlCapBytes !== undefined) {
|
|
63
|
+
throw new Error("createHttpServer: snapshotBlobSqlCapBytes was replaced by snapshotBlobSqlCap in 7.132.0 — pass a reader `() => { capBytes, source } | undefined` (called on every PUT /v1/sessions/:id/sync/blobs/:hash; `source` is \"operator\" for a cap you set yourself, \"reported\" / \"assumed\" for one derived from the database's walls; `undefined` = no cap) — for a fixed cap: `() => ({ capBytes: <bytes>, source: \"operator\" })`; refusing to start rather than run without the blob cap");
|
|
64
|
+
}
|
|
65
|
+
if (deps.snapshotBlobSqlCap !== undefined && typeof deps.snapshotBlobSqlCap !== "function") {
|
|
66
|
+
throw new Error(`createHttpServer: snapshotBlobSqlCap must be a reader \`() => { capBytes, source } | undefined\` (called on every PUT /v1/sessions/:id/sync/blobs/:hash), got ${typeof deps.snapshotBlobSqlCap} — pass the reader (or \`() => undefined\` for no cap), or leave it unset; refusing to start rather than answer every blob upload with a 500`);
|
|
67
|
+
}
|
|
68
|
+
if (deps.sqlEngineFacts !== undefined) {
|
|
69
|
+
throw new Error("createHttpServer: sqlEngineFacts was replaced by sqlEngine in 7.132.0 — pass the SQL pool's engine reading as sqlEngine: `{ dialect: \"tidb\" | \"pg\", facts }` where `facts` is read live (a getter) and is null while the pool's connection setup has not succeeded, or leave both unset when there is no SQL store; refusing to start rather than run the submit admission without the database's walls");
|
|
70
|
+
}
|
|
71
|
+
{
|
|
72
|
+
const issue = sqlEngineSeatIssue(deps.backend, deps.sqlEngine);
|
|
73
|
+
if (issue !== undefined) {
|
|
74
|
+
switch (issue) {
|
|
75
|
+
case "required":
|
|
76
|
+
throw new Error("createHttpServer: sqlEngine is required when a SQL store backend is wired (the backend hands out a SQL pool) — pass the pool's engine reading as sqlEngine: `{ dialect: \"tidb\" | \"pg\", facts }` where `facts` is read live (a getter) and is null while the pool's connection setup has not succeeded; refusing to start rather than read the database's row and statement limits as absent and admit task bodies the database cannot store");
|
|
77
|
+
case "bad_shape":
|
|
78
|
+
throw new Error(`createHttpServer: sqlEngine must be the SQL pool's engine reading \`{ dialect: "tidb" | "pg", facts }\` where \`facts\` is read live (a getter) and is null while the pool's connection setup has not succeeded, otherwise the engine facts object (maxRowBytes, maxPacketBytes and wallSources included) — got ${typeof deps.sqlEngine === "function" ? "a function (the 7.131.x reader \`() => facts\` goes in as \`{ dialect, get facts() { return f(); } }\`)" : describeValueShape(deps.sqlEngine)}; refusing to start rather than answer every task submission with a 500`);
|
|
79
|
+
default: {
|
|
80
|
+
const unhandled = issue;
|
|
81
|
+
throw new Error(`sqlEngine seat issue without a refusal: ${String(unhandled)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
60
86
|
const idemCache = new IdempotencyCache();
|
|
61
87
|
const inflightRuns = new Map();
|
|
62
88
|
const preemptableRuns = new DrainYieldingPreemptBook(() => deps.drainState?.draining === true);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RunStore } from "../plugins/store-backend.js";
|
|
2
|
+
import type { Logger } from "../observability/logger.js";
|
|
3
|
+
import type { Metrics } from "../observability/metrics.js";
|
|
4
|
+
import { buildCheckpointCtx } from "./checkpoint-ctx.js";
|
|
5
|
+
import { type RefusalBody } from "./refusal-body.js";
|
|
6
|
+
/** 写不进的那一种可答复的结局(各腿把它交回自己的出口:同步 / durable 腿直接回,活流腿编码成终帧)。 */
|
|
7
|
+
export interface SubmitCtxRefusal {
|
|
8
|
+
readonly status: 413;
|
|
9
|
+
readonly body: RefusalBody;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 写这一笔(行体经续跑输入的唯一构造口 `buildCheckpointCtx` 铸 —— 新的持久化写都经它,读法记号随之盖上);写不进 ⇒ 撤掉
|
|
13
|
+
* `claim` 指的那条刚认领的 run(无 run 店的部署没有认领 ⇒ `undefined`),再按成因答复(见文件头)。
|
|
14
|
+
* 返回 `undefined` = 写进了,腿照常起跑。
|
|
15
|
+
*/
|
|
16
|
+
export declare function persistSubmitCtx(store: {
|
|
17
|
+
putCtx(sessionId: string, ctx: object): Promise<void>;
|
|
18
|
+
}, deps: {
|
|
19
|
+
readonly logger?: Logger | undefined;
|
|
20
|
+
readonly metrics?: Metrics | undefined;
|
|
21
|
+
}, claim: {
|
|
22
|
+
readonly runStore: RunStore;
|
|
23
|
+
readonly taskId: string;
|
|
24
|
+
} | undefined, sessionId: string, fields: Parameters<typeof buildCheckpointCtx>[0]): Promise<SubmitCtxRefusal | undefined>;
|
|
25
|
+
//# sourceMappingURL=submit-ctx.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { StoreRowTooLargeError } from "../plugins/sql-errors.js";
|
|
2
|
+
import { releaseClaimedRun } from "./claimed-run.js";
|
|
3
|
+
import { buildCheckpointCtx } from "./checkpoint-ctx.js";
|
|
4
|
+
import { refusalBody } from "./refusal-body.js";
|
|
5
|
+
function rowTooLargeRefusal(tooLarge) {
|
|
6
|
+
switch (tooLarge.wall) {
|
|
7
|
+
case "row":
|
|
8
|
+
return { status: 413, body: refusalBody({ errorCode: "request.payload_too_large", message: `the database refused to store this task: it is larger than the database keeps in one row (${tooLarge.dbMessage}) — nothing was run; shrink the inline images / userAttachments, or ask the operator to raise the database's single-row limit (TiDB performance.txn-entry-size-limit)` }) };
|
|
9
|
+
case "statement":
|
|
10
|
+
return { status: 413, body: refusalBody({ errorCode: "request.payload_too_large", message: `the database refused to store this task: it is larger than the database accepts in one statement (${tooLarge.dbMessage}) — nothing was run; shrink the inline images / userAttachments, or ask the operator to raise the database's max_allowed_packet` }) };
|
|
11
|
+
case "value":
|
|
12
|
+
return { status: 413, body: refusalBody({ errorCode: "request.payload_too_large", message: `the database refused to store this task: it is larger than PostgreSQL holds in one value (${tooLarge.dbMessage}) — nothing was run; shrink the inline images / userAttachments` }) };
|
|
13
|
+
default: {
|
|
14
|
+
const unhandled = tooLarge.wall;
|
|
15
|
+
throw new Error(`row-too-large refusal without an HTTP answer: ${String(unhandled)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function persistSubmitCtx(store, deps, claim, sessionId, fields) {
|
|
20
|
+
try {
|
|
21
|
+
await store.putCtx(sessionId, buildCheckpointCtx(fields));
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
const tooLarge = err instanceof StoreRowTooLargeError ? err : undefined;
|
|
26
|
+
if (claim !== undefined) {
|
|
27
|
+
const why = tooLarge !== undefined ? tooLarge.message : `the run's resume inputs could not be stored: ${err instanceof Error ? err.message : String(err)}`;
|
|
28
|
+
await releaseClaimedRun(claim.runStore, claim.taskId, why, (releaseErr, step) => {
|
|
29
|
+
deps.logger?.warn("submit_ctx_run_release_failed", { taskId: claim.taskId, sessionId, step, err: releaseErr instanceof Error ? releaseErr.message : String(releaseErr) });
|
|
30
|
+
deps.metrics?.inc("submit_ctx_run_release_failed_total", { step });
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (tooLarge === undefined)
|
|
34
|
+
throw err;
|
|
35
|
+
deps.logger?.warn("submit_ctx_row_too_large", { sessionId, taskId: claim?.taskId, wall: tooLarge.wall, db: tooLarge.dbMessage });
|
|
36
|
+
return rowTooLargeRefusal(tooLarge);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=submit-ctx.js.map
|
|
@@ -322,7 +322,7 @@ export interface TaskRequestBody {
|
|
|
322
322
|
* `mcp__srv` / `mcp__srv__*` / 工具段通配;`"*"` = 本层不设名单;`[]` = 什么都不给)。宿主**原样透传**进 `TaskSpec.allowTools`:
|
|
323
323
|
* 不算补集、不过滤、不与 `settings.permissions.allow`(免问)混词;真未知名(拼写合法、没有近似也没有退役对应:懒挂载的合法形)照跑(原样保留,今天什么都不准,之后挂上同名工具即生效;引擎告知运维一次),
|
|
324
324
|
* 任何工具名都不可能有的拼法由引擎开跑前拒(`config.tool_face_control`,同步腿 400);7.131.0 起受理口另拒近似名(大小写孪生 `BASH`:想放的什么也没放 = 静默收窄,句子点名现名);
|
|
325
|
-
*
|
|
325
|
+
* 放行侧的内容形(`Agent(Explore)`)、文法读不出与没有后继的退役名(`MultiEdit`)照旧受理、交引擎既有通告;有后继的退役名(`bash` · `Task`)引擎开跑前拒 `config.tool_face_control`(core 7.52.0 起,同步腿 400)。
|
|
326
326
|
* `excludeTools` 是同一只运算的减项(deny 赢)。值形:字符串数组(受理门 400 `request.field_invalid`,与 `excludeTools` 同一只门);
|
|
327
327
|
* 装着的引擎不认该座 ⇒ 400 `request.field_invalid` 点名所需引擎版本(不静默丢)。无租户门(收窄)。 */
|
|
328
328
|
allowTools?: string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { composeExecutionLane, assertExecutionLaneInvariants, type ExecutionLane
|
|
|
3
3
|
export { createBrain } from "./brain.js";
|
|
4
4
|
export { createSessionStore } from "./plugins/session-store.js";
|
|
5
5
|
export { createStoreBackend, type StoreBackend, type SessionCaptureRecordSource } from "./plugins/store-backend.js";
|
|
6
|
-
export { RetiredSchemaPresentError } from "./plugins/
|
|
6
|
+
export { RetiredSchemaPresentError } from "./plugins/schema-consistency.js";
|
|
7
7
|
export { CachingSessionStore, type CachingOptions } from "./plugins/caching-session-store.js";
|
|
8
8
|
export type { RunRecord, RunEvent } from "./plugins/store-contracts.js";
|
|
9
9
|
export { runInBackground } from "./runs.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { composeExecutionLane, assertExecutionLaneInvariants } from "./boot/exec
|
|
|
3
3
|
export { createBrain } from "./brain.js";
|
|
4
4
|
export { createSessionStore } from "./plugins/session-store.js";
|
|
5
5
|
export { createStoreBackend } from "./plugins/store-backend.js";
|
|
6
|
-
export { RetiredSchemaPresentError } from "./plugins/
|
|
6
|
+
export { RetiredSchemaPresentError } from "./plugins/schema-consistency.js";
|
|
7
7
|
export { CachingSessionStore } from "./plugins/caching-session-store.js";
|
|
8
8
|
export { runInBackground } from "./runs.js";
|
|
9
9
|
export { createHardenedVmRunner, runInHardenedVm } from "./orchestration/hardened-vm-runner.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行环境的家目录:**声明 × 实测**的唯一判官(纯叶:零值依赖)。
|
|
3
|
+
*
|
|
4
|
+
* ## 病
|
|
5
|
+
* `REMOTE_EXEC_HOME_DIR` 是运维对「执行环境以哪个用户的家目录运行」的声明,它是 `~/` 形权限规则的基、引擎家锚拒读行(`~/.ssh` / `~/.npmrc` …)
|
|
6
|
+
* 的落点(`execution-lane-caps.ts` `executionLaneHomeDir` 一只属主,四个消费面同读)。k8s / local-docker / ssh 三条车道的家目录是镜像 / 目标机
|
|
7
|
+
* 登录用户的属性,boot 期不可知 —— 声明了就照单全收。声明 `/home/y` 而车道真实家目录是 `/home/x` 时,`Edit(~/.ssh/**)` 守的是 `/home/y`,
|
|
8
|
+
* `/home/x/.ssh` 照读照写,运维与用户零告警:限制写了不生效且无人知。
|
|
9
|
+
*
|
|
10
|
+
* ## 律(一句)
|
|
11
|
+
* 家目录只有一只真源:声明在场、车道能问的,**开环境那一拍问一次**(与命令同一条 shell、同一份 env:`printf %s "$HOME"`),两者不一致 ⇒
|
|
12
|
+
* 这只执行环境不开(拒句点名两值与两条改法),环境答了的判定记在环境上(同一镜像 / 同一目标机再问也是同一个答案,不重试成风暴);问不出答案同样不开
|
|
13
|
+
* (证不了声明 = 不拿一个没证过的目录去守),但不记 —— 与任何一次建连失败同,下一次重问。车道问不了的(adb / device)声明照单全收 —— 那是声明唯一的用处。
|
|
14
|
+
* 没声明的车道不问:规则基与 422 门在下单那一刻就要家目录,而那一刻执行环境还没开(把实测值接进规则基要引擎的座,见收车档)。
|
|
15
|
+
*/
|
|
16
|
+
/** 探针答案的行首标记:登录脚本 / profile 往 stdout 打的横幅不会被读成家目录 —— 只认带这个标记的那一行(取最后一行)。 */
|
|
17
|
+
export declare const LANE_HOME_PROBE_MARKER = "sema-lane-home=";
|
|
18
|
+
/**
|
|
19
|
+
* 执行环境里问家目录的那一句 —— 经车道自己的命令通道跑(同一条 shell、同一份 env),答的就是命令看到的 `$HOME`。
|
|
20
|
+
* 先换一行再打标记行:横幅没以换行收尾时,标记行照样从行首开始。
|
|
21
|
+
*/
|
|
22
|
+
export declare const LANE_HOME_PROBE_COMMAND = "printf '\\nsema-lane-home=%s\\n' \"$HOME\"";
|
|
23
|
+
/** 一次问家目录的读数:问到了(stdout 原文)/ 没问到(原因)。 */
|
|
24
|
+
export type LaneHomeProbe = {
|
|
25
|
+
readonly ok: true;
|
|
26
|
+
readonly stdout: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly ok: false;
|
|
29
|
+
readonly reason: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* 判定:`undefined` = 一致,照开;否则这只环境不开 —— `refusal` 是拒句,`answered` = 环境**答了**它的家目录(不一致 / 不是绝对路径;同一镜像 / 同一登录用户
|
|
33
|
+
* 再问也是同一个答案 ⇒ 调用方记在环境上),`false` = 没答上(探针失败 / 超时 / 非零退出 / 输出里没有标记行 ⇒ 这一次不开、不记、下一次重问)。
|
|
34
|
+
*/
|
|
35
|
+
export type LaneHomeVerdict = {
|
|
36
|
+
readonly refusal: string;
|
|
37
|
+
readonly answered: boolean;
|
|
38
|
+
} | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* 声明 × 实测的唯一判官。`lane` 只进句子。拒句三形:实测与声明不一致 / 实测不是绝对路径(含空)/ 没问到。每一形都点名声明值与改法,不回显任何别的东西。
|
|
41
|
+
*/
|
|
42
|
+
export declare function judgeLaneHome(lane: string, declared: string, probe: LaneHomeProbe): LaneHomeVerdict;
|
|
43
|
+
//# sourceMappingURL=lane-home-probe.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const LANE_HOME_PROBE_MARKER = "sema-lane-home=";
|
|
2
|
+
export const LANE_HOME_PROBE_COMMAND = `printf '\\n${LANE_HOME_PROBE_MARKER}%s\\n' "$HOME"`;
|
|
3
|
+
const SHOWN_MAX = 256;
|
|
4
|
+
const shown = (raw) => JSON.stringify(raw.length > SHOWN_MAX ? `${raw.slice(0, SHOWN_MAX)}…` : raw);
|
|
5
|
+
const comparable = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
|
|
6
|
+
function markedHome(stdout) {
|
|
7
|
+
const lines = stdout.split("\n").filter((l) => l.startsWith(LANE_HOME_PROBE_MARKER));
|
|
8
|
+
const last = lines[lines.length - 1];
|
|
9
|
+
return last === undefined ? undefined : last.slice(LANE_HOME_PROBE_MARKER.length).replace(/\r$/, "");
|
|
10
|
+
}
|
|
11
|
+
export function judgeLaneHome(lane, declared, probe) {
|
|
12
|
+
const head = `REMOTE_EXEC_HOME_DIR=${shown(declared)} is the home "~/" permission rules and the engine's home-anchored read-deny rows are placed under on the ${lane} execution lane`;
|
|
13
|
+
const measured = probe.ok ? markedHome(probe.stdout) : undefined;
|
|
14
|
+
if (measured === undefined) {
|
|
15
|
+
const reason = probe.ok ? "the probe printed no home line" : probe.reason;
|
|
16
|
+
return {
|
|
17
|
+
refusal: `${head}, but the home of this execution environment could not be read (${reason}) — the declaration cannot be confirmed, so this environment is not started. Fix the lane, or unset REMOTE_EXEC_HOME_DIR (home-relative rules are then refused at submission instead).`,
|
|
18
|
+
answered: false,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (!measured.startsWith("/")) {
|
|
22
|
+
return {
|
|
23
|
+
refusal: `${head}, but this execution environment reports HOME=${shown(measured)}, not an absolute path — the declaration cannot be confirmed, so this environment is not started. Give the environment's user a home (HOME), or unset REMOTE_EXEC_HOME_DIR.`,
|
|
24
|
+
answered: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (comparable(measured) === comparable(declared))
|
|
28
|
+
return undefined;
|
|
29
|
+
return {
|
|
30
|
+
refusal: `${head}, but its commands run with HOME=${shown(measured)} — rules would guard ${shown(declared)} while ${shown(measured)} stays readable and writable, ` +
|
|
31
|
+
`so this environment is not started. Set REMOTE_EXEC_HOME_DIR to ${shown(measured)}, or run the environment as the user whose home is ${shown(declared)}.`,
|
|
32
|
+
answered: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=lane-home-probe.js.map
|