@sema-agent/server 7.114.0 → 7.116.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 +237 -2
- package/USAGE.md +39 -3
- package/deploy/sema-up/cluster-up.sh +13 -1
- package/deploy/sema-up/kube-up.sh +13 -1
- package/deploy/sema-up/preflight.sh +13 -1
- package/deploy/sema-up/sema-up.sh +13 -1
- package/deploy/sema-up/smoke.sh +13 -1
- package/dist/adoption/wire.d.ts +0 -3
- package/dist/approval-ask-audit-store.d.ts +25 -6
- package/dist/approval-ask-audit-store.js +6 -1
- package/dist/approval-card.d.ts +25 -24
- package/dist/approval-card.js +14 -0
- package/dist/approval-crash-convergence.d.ts +60 -0
- package/dist/approval-crash-convergence.js +75 -0
- package/dist/approval-deny-reasons.d.ts +13 -0
- package/dist/approval-deny-reasons.js +2 -0
- package/dist/approval.js +7 -3
- package/dist/boot/coordinators.js +0 -12
- package/dist/boot/execution-env.js +2 -1
- package/dist/boot/exit-boundary.d.ts +3 -2
- package/dist/boot/exit-boundary.js +3 -0
- package/dist/boot/resolve-spec.js +5 -1
- package/dist/boot/shutdown.d.ts +19 -0
- package/dist/boot/shutdown.js +12 -1
- package/dist/boot/stage-07-capability-layer.js +21 -15
- package/dist/boot/stores.js +4 -4
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capabilities/hands-lane.d.ts +0 -23
- package/dist/capabilities/hands-lane.js +3 -2
- package/dist/capabilities/send-user-file-tool.d.ts +10 -22
- package/dist/capabilities/send-user-file-tool.js +1 -8
- package/dist/capability-facts.d.ts +296 -0
- package/dist/capability-facts.js +354 -0
- package/dist/config-catalog.js +2 -1
- package/dist/config-center/apply-effective.js +11 -11
- package/dist/config-provider.d.ts +16 -0
- package/dist/config-provider.js +11 -2
- package/dist/config-types.d.ts +20 -1
- package/dist/config.d.ts +55 -29
- package/dist/config.js +48 -25
- package/dist/deployment-governance.d.ts +15 -4
- package/dist/deployment-governance.js +20 -2
- package/dist/fleet/fleet-bus.d.ts +1 -22
- package/dist/hooks/hook-runner.d.ts +29 -3
- package/dist/hooks/hook-runner.js +30 -17
- package/dist/http/admission.d.ts +6 -0
- package/dist/http/admission.js +29 -7
- package/dist/http/approval-sweeps.js +20 -0
- package/dist/http/checkpoint-ctx.d.ts +68 -0
- package/dist/http/checkpoint-ctx.js +36 -0
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/principal-gate.d.ts +3 -0
- package/dist/http/principal-gate.js +3 -0
- package/dist/http/resume-legs.js +19 -1
- package/dist/http/retired-skill-manifest.d.ts +23 -0
- package/dist/http/retired-skill-manifest.js +22 -0
- package/dist/http/route-ctx.d.ts +7 -0
- package/dist/http/route-table.d.ts +3 -2
- package/dist/http/route-table.js +5 -5
- package/dist/http/routes/a2a-serve.d.ts +1 -3
- package/dist/http/routes/a2a-serve.js +14 -23
- package/dist/http/routes/admin-config-refresh.js +7 -2
- package/dist/http/routes/admin-drain.js +7 -2
- package/dist/http/routes/admin-memory-optout.d.ts +0 -2
- package/dist/http/routes/admin-memory-optout.js +20 -7
- package/dist/http/routes/adoption.js +19 -5
- package/dist/http/routes/agents-roster.js +4 -2
- package/dist/http/routes/approvals-assistant.js +41 -18
- package/dist/http/routes/background.d.ts +0 -2
- package/dist/http/routes/background.js +4 -5
- package/dist/http/routes/capabilities.d.ts +2 -1
- package/dist/http/routes/capabilities.js +63 -65
- package/dist/http/routes/config-catalog.d.ts +4 -5
- package/dist/http/routes/config-catalog.js +16 -2
- package/dist/http/routes/devices.js +17 -11
- package/dist/http/routes/diagnostics.d.ts +5 -0
- package/dist/http/routes/diagnostics.js +8 -1
- package/dist/http/routes/fleet.js +4 -2
- package/dist/http/routes/images.js +30 -3
- package/dist/http/routes/leader.js +7 -1
- package/dist/http/routes/mcp-reconnect.js +17 -10
- package/dist/http/routes/memory-bundle.js +20 -6
- package/dist/http/routes/memory-compliance.js +19 -5
- package/dist/http/routes/memory-consolidation.d.ts +9 -12
- package/dist/http/routes/memory-consolidation.js +17 -3
- package/dist/http/routes/memory-origin.js +19 -5
- package/dist/http/routes/notify-wake.js +5 -3
- package/dist/http/routes/observability.js +19 -5
- package/dist/http/routes/retention-ops.js +6 -1
- package/dist/http/routes/rules.js +49 -21
- package/dist/http/routes/run-memory-capture.js +5 -2
- package/dist/http/routes/runs.js +90 -60
- package/dist/http/routes/session-sync.js +64 -53
- package/dist/http/routes/sessions-list.js +8 -5
- package/dist/http/routes/sessions.js +40 -25
- package/dist/http/routes/shared-memory.js +5 -3
- package/dist/http/routes/tasks.js +5 -3
- package/dist/http/routes/trace-usage.js +18 -11
- package/dist/http/routes/workflows.js +36 -13
- package/dist/http/server.js +4 -1
- package/dist/http/wire-types.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/leader/wire.js +2 -1
- package/dist/local-data-root.d.ts +1 -1
- package/dist/mcp-content-origin.d.ts +0 -8
- package/dist/model-entry-refusal.d.ts +10 -2
- package/dist/model-entry-refusal.js +19 -3
- package/dist/namespaced-rule-name.d.ts +54 -0
- package/dist/namespaced-rule-name.js +33 -0
- package/dist/observability/corrupt-read-seat.d.ts +4 -3
- package/dist/observability/corrupt-read-seat.js +4 -3
- package/dist/observability/fail-open.d.ts +10 -9
- package/dist/observability/fail-open.js +5 -1
- package/dist/observability/permission-rule-events.d.ts +3 -0
- package/dist/observability/permission-rule-events.js +4 -0
- package/dist/permission-rule-list.d.ts +81 -0
- package/dist/permission-rule-list.js +17 -0
- package/dist/plugins/file-run-store.d.ts +7 -1
- package/dist/plugins/file-run-store.js +20 -7
- package/dist/plugins/memory-run-store.js +2 -1
- package/dist/plugins/one-time-migrations.d.ts +4 -2
- package/dist/plugins/one-time-migrations.js +26 -3
- package/dist/plugins/pg-session-storage.js +1 -1
- package/dist/plugins/run-store-sql.js +4 -4
- package/dist/plugins/session-policy-store-sql.js +4 -2
- package/dist/plugins/store-backend.d.ts +6 -1
- package/dist/plugins/store-backend.js +2 -1
- package/dist/plugins/store-contracts.d.ts +27 -8
- package/dist/plugins/store-contracts.js +12 -3
- package/dist/plugins/tidb-session-store.js +1 -1
- package/dist/request-key-closure.js +1 -1
- package/dist/run-local.d.ts +37 -3
- package/dist/run-local.js +52 -6
- package/dist/runs.d.ts +0 -58
- package/dist/runs.js +15 -20
- package/dist/runtime-governance.d.ts +21 -1
- package/dist/runtime-governance.js +6 -1
- package/dist/security.js +2 -2
- package/dist/session-policy-wire.d.ts +40 -59
- package/dist/session-policy-wire.js +60 -32
- package/dist/spec-fields.d.ts +32 -0
- package/dist/spec-fields.js +31 -16
- package/dist/standing-answer-grant.d.ts +8 -0
- package/dist/standing-answer-grant.js +5 -0
- package/dist/task-settings.d.ts +17 -6
- package/dist/task-settings.js +16 -30
- package/dist/tool-approval.d.ts +32 -4
- package/dist/tool-approval.js +31 -8
- package/dist/trace/core-keyset-guard.d.ts +13 -8
- package/dist/trace/project.js +9 -0
- package/dist/trace/self-minted-wire.d.ts +42 -0
- package/dist/trace/self-minted-wire.js +13 -0
- package/dist/user-input-ledger.d.ts +8 -5
- package/package.json +6 -4
- package/scripts/drain-check-local.mjs +528 -0
- package/scripts/drain-check.mysql.sql +67 -0
- package/scripts/drain-check.pg.sql +71 -0
- package/scripts/retired-mode-seed-drain-local.mjs +0 -317
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
-
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
2
|
+
import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
|
+
import * as facts from "../../capability-facts.js";
|
|
3
4
|
import { assertNever } from "../route-ctx.js";
|
|
4
5
|
export const ADMIN_DRAIN_PATH = "/v1/admin/drain";
|
|
5
6
|
export const MAX_DRAIN_REASON_CHARS = 256;
|
|
6
|
-
const OPERATOR_ONLY_MESSAGE = "drain declaration is operator-only: the caller principal is not in OPERATOR_PRINCIPALS
|
|
7
|
+
const OPERATOR_ONLY_MESSAGE = "drain declaration is operator-only: the caller principal is not in OPERATOR_PRINCIPALS";
|
|
7
8
|
export async function handleAdminDrain(req, res, match, ctx) {
|
|
8
9
|
const { deps } = ctx;
|
|
9
10
|
switch (match.decl.id) {
|
|
@@ -13,6 +14,10 @@ export async function handleAdminDrain(req, res, match, ctx) {
|
|
|
13
14
|
sendPrincipalRefusal(res, req, deps.config);
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
17
|
+
if (!facts.operatorLane(deps).available) {
|
|
18
|
+
sendOperatorPrincipalsRequired(res);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
16
21
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
17
22
|
sendError(res, 403, "auth.operator_only", OPERATOR_ONLY_MESSAGE);
|
|
18
23
|
return;
|
|
@@ -42,8 +42,6 @@
|
|
|
42
42
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
43
43
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
44
44
|
export declare const ADMIN_MEMORY_OPTOUT_PATH = "/v1/admin/memory-optout";
|
|
45
|
-
/** 带参口(`:principal`)⇒ 路由声明表(`http/route-table.ts`)里走 `pattern` 行,不是 `path` 行。 */
|
|
46
|
-
export declare const ADMIN_MEMORY_OPTOUT_OVERRIDE_RE: RegExp;
|
|
47
45
|
export declare function handleMemoryOptOut(req: IncomingMessage, res: ServerResponse, match: RouteMatch<MemoryOptOutRouteId>, ctx: RouteCtx): Promise<void>;
|
|
48
46
|
export declare const MEMORY_OPTOUT_ROUTES: readonly [{
|
|
49
47
|
readonly id: "memory-optout-grants-list";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
-
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
|
+
import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { assertNever } from "../route-ctx.js";
|
|
5
|
+
import * as facts from "../../capability-facts.js";
|
|
5
6
|
import { memoryOptOutPrincipalShapeProblem } from "../../plugins/memory-optout-grant-store-sql.js";
|
|
6
7
|
export const ADMIN_MEMORY_OPTOUT_PATH = "/v1/admin/memory-optout";
|
|
7
|
-
export const ADMIN_MEMORY_OPTOUT_OVERRIDE_RE = /^\/v1\/admin\/memory-optout\/([^/]+)$/;
|
|
8
8
|
const PutBodySchema = z.object({ allow: z.boolean(), note: z.string().min(1).max(500).optional() }).strict();
|
|
9
9
|
export async function handleMemoryOptOut(req, res, match, ctx) {
|
|
10
10
|
const { deps } = ctx;
|
|
@@ -16,16 +16,29 @@ export async function handleMemoryOptOut(req, res, match, ctx) {
|
|
|
16
16
|
sendPrincipalRefusal(res, req, deps.config);
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
+
const fact = facts.memoryOptOutGrant(deps);
|
|
20
|
+
if (!fact.available && fact.why === "operator_principals_empty") {
|
|
21
|
+
sendOperatorPrincipalsRequired(res);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
19
24
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
20
|
-
sendError(res, 403, "auth.operator_only", "memory opt-out grant administration is operator-only: the caller principal is not in OPERATOR_PRINCIPALS
|
|
25
|
+
sendError(res, 403, "auth.operator_only", "memory opt-out grant administration is operator-only: the caller principal is not in OPERATOR_PRINCIPALS");
|
|
21
26
|
return;
|
|
22
27
|
}
|
|
23
28
|
const operator = principal;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
if (!fact.available) {
|
|
30
|
+
switch (fact.why) {
|
|
31
|
+
case "memory_optout_grant_absent":
|
|
32
|
+
sendError(res, 501, "capability.memory_optout_grant_required", "this deployment has no memory opt-out grant table: the per-principal memory-capture opt-out authorization store lives on a SQL store backend (DB_BACKEND=mysql|pg) and the local backend deliberately keeps no companion store for forgetting — wire a SQL backend to administer opt-out grants");
|
|
33
|
+
return;
|
|
34
|
+
case "operator_principals_empty":
|
|
35
|
+
sendOperatorPrincipalsRequired(res);
|
|
36
|
+
return;
|
|
37
|
+
default:
|
|
38
|
+
return assertNever(fact.why);
|
|
39
|
+
}
|
|
28
40
|
}
|
|
41
|
+
const store = fact.store;
|
|
29
42
|
if (target !== undefined && (target === null || memoryOptOutPrincipalShapeProblem(target) !== undefined)) {
|
|
30
43
|
sendError(res, 400, "request.path_malformed", "invalid memory opt-out grant principal — the path segment must percent-decode to a non-empty principal of at most 190 chars with no leading/trailing whitespace or control characters (the deployment default row is addressed by PUT on the collection root, not by a path segment)");
|
|
31
44
|
return;
|
|
@@ -2,8 +2,9 @@ import { createAdoptionRunner } from "../../adoption/runner.js";
|
|
|
2
2
|
import { AdoptionRequestSchema } from "../../adoption/wire.js";
|
|
3
3
|
import { checkAdoptionPrincipalShape, checkAdoptionWidths } from "../../adoption/plan.js";
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
|
-
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
import { assertNever } from "../route-ctx.js";
|
|
7
|
+
import * as facts from "../../capability-facts.js";
|
|
7
8
|
function sendOutcome(res, out) {
|
|
8
9
|
switch (out.kind) {
|
|
9
10
|
case "receipt":
|
|
@@ -46,15 +47,28 @@ export async function handleAdoption(req, res, match, ctx) {
|
|
|
46
47
|
sendPrincipalRefusal(res, req, deps.config);
|
|
47
48
|
return;
|
|
48
49
|
}
|
|
50
|
+
const fact = facts.adoption(deps);
|
|
51
|
+
if (!fact.available && fact.why === "operator_principals_empty") {
|
|
52
|
+
sendOperatorPrincipalsRequired(res);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
49
55
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
50
56
|
sendError(res, 403, "auth.operator_only", "adoption is an operator-only deployment action");
|
|
51
57
|
return;
|
|
52
58
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
if (!fact.available) {
|
|
60
|
+
switch (fact.why) {
|
|
61
|
+
case "adoption_store_absent":
|
|
62
|
+
sendError(res, 501, "capability.adoption_store_required", "adoption requires a SQL store backend (DB_BACKEND=mysql|pg)");
|
|
63
|
+
return;
|
|
64
|
+
case "operator_principals_empty":
|
|
65
|
+
sendOperatorPrincipalsRequired(res);
|
|
66
|
+
return;
|
|
67
|
+
default:
|
|
68
|
+
return assertNever(fact.why);
|
|
69
|
+
}
|
|
57
70
|
}
|
|
71
|
+
const store = fact.backend.adoptionLog();
|
|
58
72
|
const runner = createAdoptionRunner({ store, dialect: deps.backend?.kind === "pg" ? "pg" : "tidb", now: Date.now, logger: deps.logger });
|
|
59
73
|
switch (match.decl.id) {
|
|
60
74
|
case "adoption-detail": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
2
|
import { sendUnauthorized } from "../principal-gate.js";
|
|
3
3
|
import { assertNever } from "../route-ctx.js";
|
|
4
|
+
import * as facts from "../../capability-facts.js";
|
|
4
5
|
export const ROSTER_SCAN_LIMIT = 500;
|
|
5
6
|
const ROSTER_STATUSES = ["running", "parked", "completed", "failed", "killed"];
|
|
6
7
|
const ROSTER_KEY_DISPOSITION = {
|
|
@@ -63,11 +64,12 @@ export async function handleAgentRoster(req, res, match, ctx) {
|
|
|
63
64
|
sendUnauthorized(res, "identity");
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
67
|
+
const roster = facts.agentRoster(deps);
|
|
68
|
+
if (!roster.available) {
|
|
68
69
|
sendError(res, 501, "capability.background_agent_store_required", "the agent roster requires a durable background-agent store (DB_BACKEND=mysql|pg, or the local file backend)");
|
|
69
70
|
return;
|
|
70
71
|
}
|
|
72
|
+
const store = roster.store;
|
|
71
73
|
const q = new URLSearchParams(match.search);
|
|
72
74
|
const limitRaw = q.get("limit");
|
|
73
75
|
if (limitRaw !== null && !(/^[0-9]+$/.test(limitRaw) && Number(limitRaw) >= 1 && Number(limitRaw) <= 100)) {
|
|
@@ -2,7 +2,7 @@ import { screenedGate } from "../../trace/project.js";
|
|
|
2
2
|
import { hostDecisionFacts } from "../../host-decision.js";
|
|
3
3
|
import { sameExecutionRecord } from "@sema-agent/core";
|
|
4
4
|
import { DECIDED_REPLAY_SCAN_CAP, approvalPayloadFingerprint } from "../../plugins/checkpoint-store-sql.js";
|
|
5
|
-
import { APPROVAL_GATE_KINDS, SETTLED_BY_DENY_ONLY, SETTLED_BY_MUST_BE_STRING, SETTLED_BY_TOO_WIDE, judgeSettledByBody } from "../../tool-approval.js";
|
|
5
|
+
import { APPROVAL_GATE_KINDS, SETTLED_BY_DENY_ONLY, SETTLED_BY_MUST_BE_STRING, SETTLED_BY_TOO_WIDE, judgeSettledByBody, standingAnswerMayRetire } from "../../tool-approval.js";
|
|
6
6
|
import { isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
7
7
|
import { principalFrom, decodeCheckpointScope, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../../security.js";
|
|
8
8
|
import { editedPlanBinding, isWellFormedUnicode, verifyDirectDoorProof } from "../../principal-jwt.js";
|
|
@@ -17,8 +17,10 @@ import { bindSseLifecycle } from "../sse-lifecycle.js";
|
|
|
17
17
|
import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "../send.js";
|
|
18
18
|
import { gatedPrincipal, explicitOperatorOk, isOperator, sendPrincipalRefusal } from "../principal-gate.js";
|
|
19
19
|
import { governanceOriginOf } from "../../runtime-governance.js";
|
|
20
|
+
import { answerGrantsBeyondCall } from "../../standing-answer-grant.js";
|
|
20
21
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
21
22
|
import { assertNever } from "../route-ctx.js";
|
|
23
|
+
import * as facts from "../../capability-facts.js";
|
|
22
24
|
import { readResumeCtx, resumeCtxCorruptAnswer } from "../resume-ctx-read.js";
|
|
23
25
|
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
24
26
|
import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "../../request-key-closure.js";
|
|
@@ -258,7 +260,12 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
258
260
|
const { readJson, rateLimited, quotaExceeded, safeDecode } = ctx.helpers;
|
|
259
261
|
const { resumeCheckpoint, resumePreempted, preparePlanReview, drivePlanReview } = ctx.legs;
|
|
260
262
|
{
|
|
261
|
-
const
|
|
263
|
+
const park = facts.checkpointPark(deps);
|
|
264
|
+
if (!park.available) {
|
|
265
|
+
sendError(res, 501, "capability.checkpoint_store_required", "the approvals and assistant faces need a durable approval store (DURABLE_APPROVAL=true); this deployment has none wired");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const cs = park.checkpointStore;
|
|
262
269
|
const principal = gatedPrincipal(req, deps.config);
|
|
263
270
|
const operator = isOperator(principal, deps.config.operatorPrincipals);
|
|
264
271
|
if (match.decl.id === "approvals-stream") {
|
|
@@ -295,11 +302,12 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
295
302
|
const operators = deps.config.operatorPrincipals;
|
|
296
303
|
const isExplicitOperator = explicitOperatorOk(principal, operators);
|
|
297
304
|
if (!isExplicitOperator) {
|
|
298
|
-
const
|
|
299
|
-
if (!
|
|
305
|
+
const owned = facts.sessionOwnership(deps);
|
|
306
|
+
if (!owned.available) {
|
|
300
307
|
sendError(res, 501, "capability.session_store_required", "approval exemptions require a session-store backend");
|
|
301
308
|
return;
|
|
302
309
|
}
|
|
310
|
+
const ownerOf = owned.sessionStorage.ownerOf.bind(owned.sessionStorage);
|
|
303
311
|
const owner = await ownerOf(exSessionId);
|
|
304
312
|
if (owner === undefined || (owner !== null && owner !== principal)) {
|
|
305
313
|
sendError(res, 404, "not_found.session", "not found");
|
|
@@ -311,7 +319,8 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
311
319
|
sendError(res, 404, "request.route_unsupported", "not found");
|
|
312
320
|
return;
|
|
313
321
|
}
|
|
314
|
-
|
|
322
|
+
const rows = await deps.approvalExemptionStore.list(exSessionId);
|
|
323
|
+
sendJson(res, 200, { exemptions: rows.filter((r) => !answerGrantsBeyondCall({ toolName: r.toolName })) });
|
|
315
324
|
return;
|
|
316
325
|
}
|
|
317
326
|
if (exToolName === undefined) {
|
|
@@ -357,13 +366,15 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
357
366
|
if (match.decl.id === "assistant-tasks") {
|
|
358
367
|
const ownerParam = new URLSearchParams(match.search).get("owner") || undefined;
|
|
359
368
|
const scope = operator && ownerParam ? ownerParam : (principal ?? "__none__");
|
|
360
|
-
|
|
369
|
+
const ledger = facts.durableRuns(deps);
|
|
370
|
+
if (!ledger.available) {
|
|
361
371
|
sendJson(res, 200, { tasks: [] });
|
|
362
372
|
return;
|
|
363
373
|
}
|
|
374
|
+
const runStore = ledger.runStore;
|
|
364
375
|
const summaries = await cs.listByScope(scope);
|
|
365
376
|
const byGate = new Map(summaries.map((s) => [s.sessionId, s]));
|
|
366
|
-
const runs = await
|
|
377
|
+
const runs = await runStore.listRuns({ owner: scope, limit: 100 });
|
|
367
378
|
const tasks = runs
|
|
368
379
|
.filter((r) => r.status === "running" || isParkedRunStatus(r.status))
|
|
369
380
|
.map((r) => {
|
|
@@ -385,20 +396,22 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
385
396
|
if (match.decl.id === "assistant-task-preempt") {
|
|
386
397
|
if (rateLimited(req, res) || quotaExceeded(req, res))
|
|
387
398
|
return;
|
|
388
|
-
|
|
399
|
+
const runs = facts.durableRuns(deps);
|
|
400
|
+
if (!runs.available) {
|
|
389
401
|
sendError(res, 501, "capability.run_store_required", "preemption requires a durable run store (DB_BACKEND=mysql|pg|local)");
|
|
390
402
|
return;
|
|
391
403
|
}
|
|
404
|
+
const runStore = runs.runStore;
|
|
392
405
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
393
406
|
sendPrincipalRefusal(res, req, deps.config);
|
|
394
407
|
return;
|
|
395
408
|
}
|
|
396
|
-
if (!deps.config.resourceSuspend
|
|
409
|
+
if (!deps.config.resourceSuspend) {
|
|
397
410
|
sendError(res, 501, "feature.preemption_disabled", "preemption not enabled on this worker (needs RESOURCE_SUSPEND=true + a durable checkpoint store)");
|
|
398
411
|
return;
|
|
399
412
|
}
|
|
400
413
|
const taskId = match.params.taskId;
|
|
401
|
-
const run = await
|
|
414
|
+
const run = await runStore.getRun(taskId);
|
|
402
415
|
if (!run) {
|
|
403
416
|
sendError(res, 404, "not_found.run", "task not found");
|
|
404
417
|
return;
|
|
@@ -415,16 +428,16 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
415
428
|
sendJson(res, 202, { taskId, status: run.status, note: "task is not preempt-eligible (verify/cascade leg) — preempt is a no-op" });
|
|
416
429
|
return;
|
|
417
430
|
}
|
|
418
|
-
let flagged = await
|
|
431
|
+
let flagged = await runStore.requestPreempt(taskId, run.owner);
|
|
419
432
|
const PREEMPT_RECLASSIFY_ATTEMPTS = 3;
|
|
420
433
|
for (let attempt = 0; !flagged; attempt++) {
|
|
421
|
-
const now = await
|
|
434
|
+
const now = await runStore.getRun(taskId);
|
|
422
435
|
if (now !== undefined && isParkedRunStatus(now.status)) {
|
|
423
436
|
sendError(res, 409, "conflict.already_suspended", "task is already suspended", { taskId, status: now.status });
|
|
424
437
|
return;
|
|
425
438
|
}
|
|
426
439
|
if (now?.status === "running" && attempt < PREEMPT_RECLASSIFY_ATTEMPTS) {
|
|
427
|
-
flagged = await
|
|
440
|
+
flagged = await runStore.requestPreempt(taskId, run.owner);
|
|
428
441
|
continue;
|
|
429
442
|
}
|
|
430
443
|
if (now?.status === "running") {
|
|
@@ -448,16 +461,18 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
448
461
|
if (match.decl.id === "assistant-task-resume") {
|
|
449
462
|
if (rateLimited(req, res) || quotaExceeded(req, res))
|
|
450
463
|
return;
|
|
451
|
-
|
|
464
|
+
const runs = facts.durableRuns(deps);
|
|
465
|
+
if (!runs.available) {
|
|
452
466
|
sendError(res, 501, "capability.run_store_required", "resume requires a durable run store (DB_BACKEND=mysql|pg|local)");
|
|
453
467
|
return;
|
|
454
468
|
}
|
|
469
|
+
const runStore = runs.runStore;
|
|
455
470
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
456
471
|
sendPrincipalRefusal(res, req, deps.config);
|
|
457
472
|
return;
|
|
458
473
|
}
|
|
459
474
|
const taskId = match.params.taskId;
|
|
460
|
-
const run = await
|
|
475
|
+
const run = await runStore.getRun(taskId);
|
|
461
476
|
if (!run) {
|
|
462
477
|
sendError(res, 404, "not_found.run", "task not found");
|
|
463
478
|
return;
|
|
@@ -475,10 +490,12 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
475
490
|
if (match.decl.id === "assistant-task-plan-review") {
|
|
476
491
|
if (rateLimited(req, res) || quotaExceeded(req, res))
|
|
477
492
|
return;
|
|
478
|
-
|
|
493
|
+
const runs = facts.durableRuns(deps);
|
|
494
|
+
if (!runs.available) {
|
|
479
495
|
sendError(res, 501, "capability.run_store_required", "plan_review requires a durable run store (DB_BACKEND=mysql|pg|local)");
|
|
480
496
|
return;
|
|
481
497
|
}
|
|
498
|
+
const runStore = runs.runStore;
|
|
482
499
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
483
500
|
sendPrincipalRefusal(res, req, deps.config);
|
|
484
501
|
return;
|
|
@@ -540,7 +557,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
540
557
|
}
|
|
541
558
|
}
|
|
542
559
|
const planNonce = typeof body.boundCallId === "string" ? body.boundCallId : undefined;
|
|
543
|
-
const run = await
|
|
560
|
+
const run = await runStore.getRun(taskId);
|
|
544
561
|
if (!run) {
|
|
545
562
|
if (deps.config.directDoorActive) {
|
|
546
563
|
sendError(res, 401, "principal_unverified", "principal proof required");
|
|
@@ -809,7 +826,13 @@ function redactPendingDisclosures(row) {
|
|
|
809
826
|
function projectPendingForWire(row, governance) {
|
|
810
827
|
const redacted = redactPendingDisclosures(row);
|
|
811
828
|
const governanceForced = governanceOriginOf({ riskDescriptor: row.riskDescriptor ?? undefined }, governance);
|
|
812
|
-
|
|
829
|
+
const rememberRefused = !standingAnswerMayRetire({
|
|
830
|
+
governanceForced: governanceForced === true,
|
|
831
|
+
requiresRealApproval: row.requiresRealApproval === true,
|
|
832
|
+
mandated: row.mandated === true || row.ruleOffersAbsence === "mandated",
|
|
833
|
+
grantsBeyondCall: answerGrantsBeyondCall(row),
|
|
834
|
+
});
|
|
835
|
+
return { ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}) };
|
|
813
836
|
}
|
|
814
837
|
function createSseDeltaLane(res, spec) {
|
|
815
838
|
let prev = new Map();
|
|
@@ -59,8 +59,6 @@ export interface SessionBackgroundReceipt {
|
|
|
59
59
|
id: string;
|
|
60
60
|
outcome: BackgroundShellHardKillReceipt["outcome"];
|
|
61
61
|
}
|
|
62
|
-
/** 能力位与两口 501 的**同一个**谓词:会话属主面在场(入站 owner 门 fail-closed,缺它两口都 501)。 */
|
|
63
|
-
export declare function sessionBackgroundFacesAvailable(deps: Pick<RouteCtx["deps"], "sessionStorage">): boolean;
|
|
64
62
|
export declare function handleBackground(req: IncomingMessage, res: ServerResponse, match: RouteMatch<BackgroundRouteId>, ctx: RouteCtx): Promise<void>;
|
|
65
63
|
export declare const BACKGROUND_ROUTES: readonly [{
|
|
66
64
|
readonly id: "session-background-list";
|
|
@@ -6,6 +6,7 @@ import { sendJson, sendError } from "../send.js";
|
|
|
6
6
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
7
7
|
import {} from "../route-ctx.js";
|
|
8
8
|
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
9
|
+
import * as facts from "../../capability-facts.js";
|
|
9
10
|
const LIST_LIMIT = 500;
|
|
10
11
|
export const DESCRIPTION_MAX = 500;
|
|
11
12
|
const ROW_KEY_DISPOSITION = {
|
|
@@ -32,9 +33,6 @@ function projectRow(r) {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
const StopBodySchema = z.object({ includeRetained: z.boolean() }).strict();
|
|
35
|
-
export function sessionBackgroundFacesAvailable(deps) {
|
|
36
|
-
return Boolean(deps.sessionStorage?.ownerOf);
|
|
37
|
-
}
|
|
38
36
|
export async function handleBackground(req, res, match, ctx) {
|
|
39
37
|
const { deps } = ctx;
|
|
40
38
|
const id = match.decl.id;
|
|
@@ -59,11 +57,12 @@ export async function handleBackground(req, res, match, ctx) {
|
|
|
59
57
|
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
60
58
|
return;
|
|
61
59
|
}
|
|
62
|
-
|
|
60
|
+
const owned = facts.sessionOwnership(deps);
|
|
61
|
+
if (!owned.available) {
|
|
63
62
|
sendError(res, 501, "capability.session_ownership_required", "the session background faces require the session-ownership face (sessionStorage.ownerOf)");
|
|
64
63
|
return;
|
|
65
64
|
}
|
|
66
|
-
const owner = await
|
|
65
|
+
const owner = await owned.sessionStorage.ownerOf(sessionId);
|
|
67
66
|
if (owner === undefined) {
|
|
68
67
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
69
68
|
return;
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 路由:`GET /v1/capabilities/scenarios/:name`、`GET /v1/capabilities`、`GET /v1/models`。
|
|
5
5
|
* 三条都在全局 service-credential 门**之后**(config 面非密只读),装配表里的位置必须保住这一点。
|
|
6
|
-
*
|
|
6
|
+
* 本域零可变状态。凡是某个端点也在判的位,值一律是 `src/capability-facts.ts` 里对应事实的投影 ——
|
|
7
|
+
* 端点的不可用门调的是**同一只**函数,所以「位说有而端点恒拒」在结构上不可能(不是靠两边各写一份再对拍)。
|
|
7
8
|
*/
|
|
8
9
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
9
10
|
import { type McpProbeLocal } from "../../capabilities/mcp-probe.js";
|
|
@@ -7,13 +7,11 @@ import { taskWriteFaceRefusal } from "../../hosted-posture.js";
|
|
|
7
7
|
import { autonomyOverrides } from "../../runtime-governance.js";
|
|
8
8
|
import { DEVICE_PROTOCOL_VERSION, DEVICE_WS_PATH } from "../../device-ws-protocol.js";
|
|
9
9
|
import { a2aInjectionHonored } from "../../task-a2a.js";
|
|
10
|
-
import { mcpInjectionHonored } from "../../task-mcp.js";
|
|
11
10
|
import { buildEmptyMcpProbeFace, buildMcpInjectionRefusal, buildMcpProbeCacheKey, buildMcpProbeInflightRefusal, buildMcpProbeRateKey, buildMcpProbeRateRefusal, createMcpProbeLocal, buildMcpProbeDeclarationRefusal, buildMcpProbeIncompleteRefusal, McpProbeIncomplete, MCP_PROBE_HTTP_STATUS, MCP_PROBE_MAX_INFLIGHT, MCP_PROBE_VERDICT_TIMEOUT_MS, createMcpProbeWalk, readMcpProbeBody, } from "../../capabilities/mcp-probe.js";
|
|
12
11
|
import { mcpForScenario } from "../../config-center/facade.js";
|
|
13
12
|
import { sendJson, sendError } from "../send.js";
|
|
14
13
|
import { assertNever } from "../route-ctx.js";
|
|
15
14
|
import { fileHistoryCaptureOf } from "../../file-history-capture.js";
|
|
16
|
-
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
17
15
|
import { projectMemoryEngineCapability } from "../../memory-posture.js";
|
|
18
16
|
import { projectWriteProtectionCapability } from "../../write-protection.js";
|
|
19
17
|
import { projectStoreCapability } from "../../store-posture.js";
|
|
@@ -21,12 +19,12 @@ import { projectWebSearchCapability } from "../../plugins/web-search.js";
|
|
|
21
19
|
import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
|
|
22
20
|
import { gateCompositeMembers } from "../../capabilities/composite-members.js";
|
|
23
21
|
import { projectApprovalsStreamLiveCapability } from "../../capabilities/approvals-push.js";
|
|
24
|
-
import { workflowModelAllowlistFor
|
|
22
|
+
import { workflowModelAllowlistFor } from "../../task-workflow.js";
|
|
25
23
|
import { centerEntitlementSourceWired } from "../../runtime-caps-resolver.js";
|
|
24
|
+
import * as facts from "../../capability-facts.js";
|
|
26
25
|
import { judgeAutoModeArming } from "../../auto-mode-face.js";
|
|
27
26
|
import { coercePermissionModeStrict, SETTINGS_PERMISSION_MODE_WORDS, taskHooksHonored } from "../../task-settings.js";
|
|
28
27
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
29
|
-
import { sessionBackgroundFacesAvailable } from "./background.js";
|
|
30
28
|
function routePairingOf(deps, name, m) {
|
|
31
29
|
if (modelKeyPoisonOf(name, deps.config.modelApiKeyEnv ?? {}, process.env, deps.config.modelApiKeys ?? {}) !== undefined)
|
|
32
30
|
return "unknown";
|
|
@@ -57,8 +55,8 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
57
55
|
return;
|
|
58
56
|
}
|
|
59
57
|
case "capabilities": {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
58
|
+
const workflowsEngine = facts.selfOrchestrationEngine(deps);
|
|
59
|
+
const workflowsAdmission = facts.selfOrchestrationAdmission(deps);
|
|
62
60
|
const requestedModeRaw = new URLSearchParams(match.search).get("permissionMode");
|
|
63
61
|
const requestedMode = requestedModeRaw === null ? undefined : coercePermissionModeStrict(requestedModeRaw);
|
|
64
62
|
if (requestedModeRaw !== null && requestedMode === undefined) {
|
|
@@ -69,44 +67,44 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
69
67
|
const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config), localDeny: deps.config.permissionsDisableAutoMode });
|
|
70
68
|
if (autoModeFault !== undefined)
|
|
71
69
|
deps.logger?.warn("auto_mode_face_resolver_fault", { fault: autoModeFault });
|
|
72
|
-
const
|
|
70
|
+
const rulesLaneForCaller = facts.ruleLaneForCaller(deps, gatedPrincipal(req, deps.config)).available;
|
|
73
71
|
sendJson(res, 200, gateCompositeMembers({
|
|
74
72
|
...deps.capabilities,
|
|
75
|
-
asyncRuns:
|
|
76
|
-
artifacts:
|
|
77
|
-
approvals:
|
|
78
|
-
sessions:
|
|
79
|
-
deviceExecutor: deps.
|
|
80
|
-
? { enabled: true, protocolVersion: DEVICE_PROTOCOL_VERSION, maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4, wsPath: DEVICE_WS_PATH, management:
|
|
73
|
+
asyncRuns: facts.durableRuns(deps).available,
|
|
74
|
+
artifacts: facts.durableRuns(deps).available,
|
|
75
|
+
approvals: facts.checkpointPark(deps).available,
|
|
76
|
+
sessions: facts.sessionAuditView(deps).available,
|
|
77
|
+
deviceExecutor: facts.deviceLane(deps).available
|
|
78
|
+
? { enabled: true, protocolVersion: DEVICE_PROTOCOL_VERSION, maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4, wsPath: DEVICE_WS_PATH, management: facts.deviceManagement(deps).available }
|
|
81
79
|
: false,
|
|
82
|
-
sessionEvents:
|
|
83
|
-
fleet: deps.
|
|
84
|
-
? { stream: true, sessionScope: true, observe: true, resume: "snapshot", bgNotifyFailClosed: true, steerPriority: "
|
|
80
|
+
sessionEvents: facts.sessionEventStream(deps).available,
|
|
81
|
+
fleet: facts.fleetStream(deps).available
|
|
82
|
+
? { stream: true, sessionScope: true, observe: true, resume: "snapshot", bgNotifyFailClosed: true, steerPriority: "engine", hookFailureNotice: true }
|
|
85
83
|
: false,
|
|
86
|
-
workspace:
|
|
87
|
-
leader:
|
|
88
|
-
images:
|
|
84
|
+
workspace: facts.workspaceBrowse().available,
|
|
85
|
+
leader: facts.leaderLane(deps).available,
|
|
86
|
+
images: facts.imagePool(deps).available,
|
|
89
87
|
memory: false,
|
|
90
88
|
memoryWrite: false,
|
|
91
89
|
memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
|
|
92
90
|
store: projectStoreCapability(deps.storePosture ?? { declared: deps.config.dbBackend, backend: "memory" }, deps.sqlEngineFacts?.()),
|
|
93
91
|
centerWiring: deps.centerWiring?.(gatedPrincipal(req, deps.config)),
|
|
94
92
|
webSearch: projectWebSearchCapability(deps.webSearchProvider),
|
|
95
|
-
sessionBackgroundable:
|
|
96
|
-
runMemoryCaptureOptOut:
|
|
97
|
-
mcpReconnect:
|
|
98
|
-
background: { listFace:
|
|
99
|
-
sharedMemory:
|
|
100
|
-
adoption:
|
|
101
|
-
memoryBundle:
|
|
102
|
-
memoryCompliance:
|
|
103
|
-
memoryOrigin:
|
|
104
|
-
memoryConsolidationDriver:
|
|
105
|
-
memoryOptOutGrant:
|
|
106
|
-
outcomeLedger:
|
|
107
|
-
permissionRules:
|
|
108
|
-
permissionRulesWrite:
|
|
109
|
-
permissionRulesRevoke:
|
|
93
|
+
sessionBackgroundable: facts.durableRuns(deps).available,
|
|
94
|
+
runMemoryCaptureOptOut: facts.durableRuns(deps).available,
|
|
95
|
+
mcpReconnect: facts.mcpReconnect(deps).available,
|
|
96
|
+
background: { listFace: facts.sessionOwnership(deps).available, exitFaces: facts.sessionOwnership(deps).available },
|
|
97
|
+
sharedMemory: facts.sharedMemoryRead(deps).available,
|
|
98
|
+
adoption: facts.adoption(deps).available,
|
|
99
|
+
memoryBundle: facts.memoryBundle(deps).available,
|
|
100
|
+
memoryCompliance: facts.memoryCompliance(deps).available,
|
|
101
|
+
memoryOrigin: facts.memoryOrigin(deps).available,
|
|
102
|
+
memoryConsolidationDriver: facts.memoryConsolidation(deps).available,
|
|
103
|
+
memoryOptOutGrant: facts.memoryOptOutGrant(deps).available,
|
|
104
|
+
outcomeLedger: facts.outcomeLedger(deps).available,
|
|
105
|
+
permissionRules: rulesLaneForCaller,
|
|
106
|
+
permissionRulesWrite: rulesLaneForCaller,
|
|
107
|
+
permissionRulesRevoke: facts.ruleLane(deps).available,
|
|
110
108
|
respondFreeFormRules: true,
|
|
111
109
|
respondBatchRuleOffers: true,
|
|
112
110
|
oneShot: true,
|
|
@@ -119,9 +117,10 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
119
117
|
messageIdentity: true,
|
|
120
118
|
forwardSubagentEvents: true,
|
|
121
119
|
scratchpadDir: true,
|
|
122
|
-
subagentSteer:
|
|
123
|
-
subagentResume:
|
|
120
|
+
subagentSteer: facts.subagentSteer(deps).available,
|
|
121
|
+
subagentResume: facts.subagentSteer(deps).available,
|
|
124
122
|
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: cwdHonored(deps.config) || deviceCwdHonored(deps.config), hooks: taskHooksHonored(deps.config) },
|
|
123
|
+
settings: { permissions: { allowGrant: true } },
|
|
125
124
|
permissionModeAuto: {
|
|
126
125
|
accepted: true,
|
|
127
126
|
classifierSeat: deps.autoModeFace?.seatMounted === true,
|
|
@@ -132,59 +131,58 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
132
131
|
writeProtection: projectWriteProtectionCapability(deps.writeProtection),
|
|
133
132
|
appendSystemPrompt: true,
|
|
134
133
|
compactionModel: true,
|
|
135
|
-
subagentOutput:
|
|
136
|
-
subagentStream:
|
|
137
|
-
taskHandles:
|
|
134
|
+
subagentOutput: facts.subagentOutput(deps).available,
|
|
135
|
+
subagentStream: facts.subagentOutput(deps).available,
|
|
136
|
+
taskHandles: facts.taskHandles(deps).available,
|
|
138
137
|
taskAgents: deps.config.requirePrincipal !== true,
|
|
139
138
|
retainBackgroundProcesses: deps.config.requirePrincipal !== true,
|
|
140
139
|
interactiveTools: true,
|
|
141
140
|
disableSkills: true,
|
|
141
|
+
userInvokedSkills: true,
|
|
142
|
+
skillToolLists: true,
|
|
142
143
|
projectContext: cwdHonored(deps.config),
|
|
143
144
|
callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
|
|
144
145
|
taskWriteFaceOpen: taskWriteFaceRefusal(deps.config) === undefined && autonomyOverrides(deps.config.autonomy).writeFace !== "roots",
|
|
145
146
|
executionLane: projectExecutionLaneCapability(deps.config),
|
|
146
|
-
mcpInjection:
|
|
147
|
+
mcpInjection: facts.mcpInjection(deps).available,
|
|
147
148
|
a2a: true,
|
|
148
149
|
a2aInjection: a2aInjectionHonored(deps.config),
|
|
149
|
-
a2aServe: deps.
|
|
150
|
+
a2aServe: facts.a2aServe(deps).available,
|
|
150
151
|
modelUsage: Boolean(deps.runStore && deps.modelUsage),
|
|
151
152
|
sessionInit: true,
|
|
152
153
|
mcp: true,
|
|
153
154
|
mcpProbe: true,
|
|
154
|
-
mcpElicitation:
|
|
155
|
-
askUserQuestion:
|
|
156
|
-
toolApproval:
|
|
155
|
+
mcpElicitation: facts.elicitation(deps).available,
|
|
156
|
+
askUserQuestion: facts.askUserQuestion(deps).available,
|
|
157
|
+
toolApproval: facts.toolApproval(deps).available,
|
|
157
158
|
approvalsStreamLive: projectApprovalsStreamLiveCapability(deps),
|
|
158
|
-
streamApproval:
|
|
159
|
-
toolApprovalEnabled: Boolean(deps.toolApproval),
|
|
160
|
-
streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
|
|
161
|
-
backend: deps.backend,
|
|
162
|
-
parkFacility: deps.checkpointStore !== undefined,
|
|
163
|
-
}).active,
|
|
159
|
+
streamApproval: facts.streamApproval(deps).available,
|
|
164
160
|
approvalDecisionNote: true,
|
|
165
161
|
sideQuery: true,
|
|
166
|
-
agentRoster:
|
|
162
|
+
agentRoster: facts.agentRoster(deps).available,
|
|
167
163
|
retention: retentionAdvertisement(deps.config),
|
|
168
164
|
peerLane: Boolean(deps.peerDirectory),
|
|
169
|
-
sessionList:
|
|
170
|
-
sessionSearch:
|
|
171
|
-
sessionFork:
|
|
172
|
-
sessionDelete:
|
|
165
|
+
sessionList: facts.sessionList(deps).available,
|
|
166
|
+
sessionSearch: facts.sessionList(deps).available,
|
|
167
|
+
sessionFork: facts.sessionFork(deps).available,
|
|
168
|
+
sessionDelete: facts.sessionDelete(deps).available,
|
|
173
169
|
resumeAt: Boolean(deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
|
|
174
170
|
promptSuggestions: true,
|
|
175
|
-
sessionPolicy:
|
|
171
|
+
sessionPolicy: facts.sessionPolicy(deps).available,
|
|
176
172
|
rewindFiles: Boolean(deps.fileHistoryStore),
|
|
177
173
|
restoreFiles: Boolean(deps.fileHistoryStore),
|
|
178
174
|
rewindFilesTo: Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
|
|
179
175
|
fileHistoryCapture: fileHistoryCaptureOf(deps.fileHistoryStore, deps.config.fileHistoryCapture),
|
|
180
|
-
scheduler:
|
|
181
|
-
sessionSync:
|
|
176
|
+
scheduler: facts.hostScheduler(deps.config).available,
|
|
177
|
+
sessionSync: facts.sessionSync(deps).available,
|
|
178
|
+
sessionSyncFileHistory: facts.sessionSyncFileHistory(deps).available,
|
|
182
179
|
sendUserFile: Boolean(deps.sendUserFile && deps.config.sendUserFile?.publicEndpoint),
|
|
183
180
|
s3PublicEndpoint: deps.config.sendUserFile?.publicEndpoint ?? null,
|
|
184
|
-
sendUserFileLedger:
|
|
185
|
-
workflows:
|
|
186
|
-
workflowsList:
|
|
187
|
-
|
|
181
|
+
sendUserFileLedger: facts.sendFileLedger(deps).available,
|
|
182
|
+
workflows: facts.selfOrchestration(deps).available,
|
|
183
|
+
workflowsList: facts.workflowRuns(deps).available,
|
|
184
|
+
workflowsJournal: facts.workflowsJournal(deps).available,
|
|
185
|
+
workflowsGate: { engineCan: workflowsEngine.available, denial: workflowsAdmission.available ? null : workflowsAdmission.why },
|
|
188
186
|
workflowModels: (() => {
|
|
189
187
|
const allow = deps.workflowModelAllowlist
|
|
190
188
|
?? workflowModelAllowlistFor({
|
|
@@ -197,8 +195,8 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
197
195
|
const live = expandTiers(bare, deps.config.tiers ?? {}) ?? bare;
|
|
198
196
|
return allow.filter((n) => Object.hasOwn(live, n));
|
|
199
197
|
})(),
|
|
200
|
-
manualCompact:
|
|
201
|
-
configCatalog:
|
|
198
|
+
manualCompact: facts.durableRuns(deps).available,
|
|
199
|
+
configCatalog: facts.configCatalog(deps).available,
|
|
202
200
|
sealedKey: projectSealedKeyCapability(deps.sealedKeyPublic),
|
|
203
201
|
}));
|
|
204
202
|
return;
|
|
@@ -218,7 +216,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
218
216
|
sendPrincipalRefusal(res, req, deps.config);
|
|
219
217
|
return;
|
|
220
218
|
}
|
|
221
|
-
if (!
|
|
219
|
+
if (!facts.mcpInjection(deps).available) {
|
|
222
220
|
sendMcpProbeRefusal(res, ctx, buildMcpInjectionRefusal(), "gate_closed");
|
|
223
221
|
return;
|
|
224
222
|
}
|