@sema-agent/server 7.135.0 → 7.137.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 +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describeBadWireName, describeValueShape, isUsableWireName, stripControlAndBidi } from "@sema-agent/settings-schema";
|
|
2
|
+
import { createCodedHttpError } from "./security.js";
|
|
3
|
+
import { limitRefusal } from "./limit-refusal.js";
|
|
4
|
+
import { echoKeyName } from "./request-key-closure.js";
|
|
5
|
+
import { urlCredentialFacts } from "./url-credential-refusal.js";
|
|
6
|
+
export const MALFORMED_NAME = "<malformed>";
|
|
7
|
+
export function declaredName(e) {
|
|
8
|
+
return e && typeof e === "object" && typeof e.name === "string" && e.name !== ""
|
|
9
|
+
? String(e.name)
|
|
10
|
+
: MALFORMED_NAME;
|
|
11
|
+
}
|
|
12
|
+
export function declarationNameIssue(entry) {
|
|
13
|
+
return isUsableWireName(entry.name) ? undefined : { path: "name", reason: `must be a non-empty string within the wire-name length bound — got ${describeBadWireName(entry.name)}` };
|
|
14
|
+
}
|
|
15
|
+
export const LIST_REFUSAL_TAIL = "The request is refused rather than acting on part of the list: declarations that silently did not apply would read as in effect to whoever sent them. " +
|
|
16
|
+
"Fix the list (an empty list declares none).";
|
|
17
|
+
export function declarationListRefusal(head) {
|
|
18
|
+
return { errorCode: "request.field_invalid", message: `${head}. ${LIST_REFUSAL_TAIL}` };
|
|
19
|
+
}
|
|
20
|
+
function entryRefusalTail(subject) {
|
|
21
|
+
return (`The request is refused rather than mounting the ${subject} without it: a declaration that silently did not apply would leave the ${subject} running differently from what you declared. ` +
|
|
22
|
+
"Fix the entry (an optional key may also be omitted).");
|
|
23
|
+
}
|
|
24
|
+
export function declarationIssueFacts(at, issue) {
|
|
25
|
+
return { field: `${at}.${issue.path}`, reason: stripControlAndBidi(issue.reason) };
|
|
26
|
+
}
|
|
27
|
+
export function declarationOverCapFacts(seat, count) {
|
|
28
|
+
const capped = limitRefusal(seat.list, "entries", count, seat.cap);
|
|
29
|
+
return { field: capped.facts.field, reason: capped.message };
|
|
30
|
+
}
|
|
31
|
+
export function declarationEntryNotObject(seat, entry, at) {
|
|
32
|
+
return entry === null || typeof entry !== "object" || Array.isArray(entry) ? `${at} is ${describeValueShape(entry)}, not ${seat.declaration} (an object)` : undefined;
|
|
33
|
+
}
|
|
34
|
+
export function declarationFieldRefusal(seat, entry, at, issue) {
|
|
35
|
+
const name = echoKeyName("", declaredName(entry));
|
|
36
|
+
const { field, reason } = declarationIssueFacts(at, issue);
|
|
37
|
+
return {
|
|
38
|
+
errorCode: "request.field_invalid",
|
|
39
|
+
message: `${field} (${seat.subject} "${name}") is not a usable declaration — ${reason}. ${entryRefusalTail(seat.subject)}`,
|
|
40
|
+
...(issue.credential === undefined ? {} : { facts: urlCredentialFacts(`${at}.${issue.path}`, issue.credential) }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function judgeDeclarationKeys(seat, entry, at, keys, judges) {
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
const issue = judges[key](entry, at);
|
|
46
|
+
if (issue !== undefined)
|
|
47
|
+
return declarationFieldRefusal(seat, entry, at, issue);
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
export function requestDeclarationRefusal(seat, list, judgeEntry) {
|
|
52
|
+
if (list === undefined || list === null)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (!Array.isArray(list))
|
|
55
|
+
return declarationListRefusal(`${seat.list} is ${describeValueShape(list)}, not an array of ${seat.declarations}`);
|
|
56
|
+
if (list.length > seat.cap) {
|
|
57
|
+
const capped = limitRefusal(seat.list, "entries", list.length, seat.cap, { tail: LIST_REFUSAL_TAIL });
|
|
58
|
+
return { errorCode: "request.field_invalid", message: capped.message, facts: capped.facts };
|
|
59
|
+
}
|
|
60
|
+
for (let i = 0; i < list.length; i++) {
|
|
61
|
+
const entry = list[i];
|
|
62
|
+
const at = `${seat.list}[${i}]`;
|
|
63
|
+
const notObject = declarationEntryNotObject(seat, entry, at);
|
|
64
|
+
if (notObject !== undefined)
|
|
65
|
+
return declarationListRefusal(notObject);
|
|
66
|
+
const refusal = judgeEntry(entry, at);
|
|
67
|
+
if (refusal !== undefined)
|
|
68
|
+
return refusal;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
export function throwDeclarationRefusal(refusal) {
|
|
73
|
+
if (refusal === undefined)
|
|
74
|
+
return;
|
|
75
|
+
const extra = { ...(refusal.unknownKeys === undefined ? {} : { unknownKeys: [...refusal.unknownKeys] }), ...(refusal.facts ?? {}) };
|
|
76
|
+
throw createCodedHttpError(400, refusal.errorCode, refusal.message, Object.keys(extra).length === 0 ? undefined : extra);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=request-declaration-refusal.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type UrlCredentialRefusal } from "./url-credential-refusal.js";
|
|
2
|
+
/** 请求体 `images` 的 URL 座:任一条目的串值 `url` 带凭据 ⇒ 拒(整句 + 机读位),列表序第一处即答;没有 ⇒ `undefined`。`images` 不是数组 / 缺席 ⇒ `undefined`(形归受理口别的门)。 */
|
|
3
|
+
export declare function requestImageUrlRefusal(images: unknown): UrlCredentialRefusal | undefined;
|
|
4
|
+
/** fresh 提交腿的 `images` URL 门:{@link requestImageUrlRefusal} 有答案 ⇒ 同步 400(码 / 句 / 机读位原样)。 */
|
|
5
|
+
export declare function assertRequestImageUrls(bodyImages: unknown): void;
|
|
6
|
+
//# sourceMappingURL=request-image-urls.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createCodedHttpError } from "./security.js";
|
|
2
|
+
import { findUrlCredential } from "@sema-agent/settings-schema";
|
|
3
|
+
import { buildRequestUrlCredentialRefusal } from "./url-credential-refusal.js";
|
|
4
|
+
function inlineImageExit(at) {
|
|
5
|
+
return `${at} can carry the image bytes inline: send { "data": <base64>, "mimeType": "image/png" | "image/jpeg" | "image/gif" | "image/webp" } instead of a URL that holds the credential`;
|
|
6
|
+
}
|
|
7
|
+
export function requestImageUrlRefusal(images) {
|
|
8
|
+
if (!Array.isArray(images))
|
|
9
|
+
return undefined;
|
|
10
|
+
for (const [i, image] of images.entries()) {
|
|
11
|
+
if (image === null || typeof image !== "object")
|
|
12
|
+
continue;
|
|
13
|
+
const url = image.url;
|
|
14
|
+
const found = typeof url === "string" ? findUrlCredential(url) : undefined;
|
|
15
|
+
if (found === undefined)
|
|
16
|
+
continue;
|
|
17
|
+
const at = `images[${i}]`;
|
|
18
|
+
return buildRequestUrlCredentialRefusal(`${at}.url`, found, inlineImageExit(at));
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
export function assertRequestImageUrls(bodyImages) {
|
|
23
|
+
const refusal = requestImageUrlRefusal(bodyImages);
|
|
24
|
+
if (refusal !== undefined)
|
|
25
|
+
throw createCodedHttpError(400, "request.field_invalid", refusal.message, refusal.facts);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=request-image-urls.js.map
|
|
@@ -17,6 +17,11 @@ export declare function scanKeyClosure(bag: object, prefix: string, accepted: Re
|
|
|
17
17
|
unknown: string[];
|
|
18
18
|
unsupported: string[];
|
|
19
19
|
}): void;
|
|
20
|
+
/** {@link scanKeyClosure} 的名字形:键名已经由调用方读成一张表(请求入口的快照只带词表外的键**名**,值不读),分拣规则同一条。 */
|
|
21
|
+
export declare function scanKeyNames(names: readonly string[], prefix: string, accepted: ReadonlySet<string>, tolerated: ReadonlySet<string>, into: {
|
|
22
|
+
unknown: string[];
|
|
23
|
+
unsupported: string[];
|
|
24
|
+
}): void;
|
|
20
25
|
/** 一张分类表的文案片段(封顶后只报数)。 */
|
|
21
26
|
export declare function keyClosureClause(names: string[], label: string): string;
|
|
22
27
|
/**
|
|
@@ -5,7 +5,10 @@ export function echoKeyName(prefix, key) {
|
|
|
5
5
|
return `${prefix}${echoCallerText(key)}`;
|
|
6
6
|
}
|
|
7
7
|
export function scanKeyClosure(bag, prefix, accepted, tolerated, into) {
|
|
8
|
-
|
|
8
|
+
scanKeyNames(Object.keys(bag), prefix, accepted, tolerated, into);
|
|
9
|
+
}
|
|
10
|
+
export function scanKeyNames(names, prefix, accepted, tolerated, into) {
|
|
11
|
+
for (const k of names) {
|
|
9
12
|
if (accepted.has(k))
|
|
10
13
|
continue;
|
|
11
14
|
(tolerated.has(k) ? into.unsupported : into.unknown).push(echoKeyName(prefix, k));
|
|
@@ -23,7 +26,7 @@ const TASK_BODY_DECLARED_KEYS = [
|
|
|
23
26
|
"excludeAllTools", "excludeTools", "fileHistory",
|
|
24
27
|
"finalVerification", "forwardSubagentEvents", "images", "inheritEnv", "interactiveTools", "jobId", "limits", "maxCostUsd",
|
|
25
28
|
"maxTokens", "mcpServers", "memory", "memoryCapture", "memoryWrite", "model", "objective", "oneShot", "outputRetries", "outputStyle",
|
|
26
|
-
"outputSchema", "permissionMode", "projectId", "projectMemory", "
|
|
29
|
+
"outputSchema", "permissionMode", "projectId", "projectMemory", "promptLean", "reasoningEffort", "requireExistingSession",
|
|
27
30
|
"resilience", "restoreFiles", "resumeAt", "resumeAtMode", "retainBackgroundProcesses", "retainSubagentSessions",
|
|
28
31
|
"rewindFiles", "rewindFilesTo", "sandboxImageProfile", "scenario", "scratchpadDir", "selfOrchestration",
|
|
29
32
|
"sessionId", "settings", "skills", "suggestNextPrompts", "systemPrompt", "taskId", "toolMaterializeStrategy",
|
|
@@ -118,7 +118,36 @@ export declare const INHERIT_ENV_WORDS: readonly ["all", "scrub"];
|
|
|
118
118
|
export type TaskInheritEnv = (typeof INHERIT_ENV_WORDS)[number];
|
|
119
119
|
/** 词表外的拒句(wire 可见;点名两词与各自的意思 —— 拿错的人要在同一条回执里读到该发什么)。 */
|
|
120
120
|
export declare const INHERIT_ENV_VALUE_MESSAGE: string;
|
|
121
|
-
/**
|
|
122
|
-
|
|
121
|
+
/**
|
|
122
|
+
* `inheritEnv` 的 `request.field_invalid` 400 的机读 `reason`([ref];闭集,**一种拒因一个词**,读者穷举读)—— 拒体另带 `field: "inheritEnv"` 与它,
|
|
123
|
+
* 壳靠这两位认「这条 400 真是 `inheritEnv` 的」,不猜句子。同一个键的**三只** 400 是一类,词表一张:
|
|
124
|
+
* · `unknown_word` —— 值形:是字符串,但不在受理词表(`"ALL"` · `"everything"` · `""`);
|
|
125
|
+
* · `not_a_string` —— 值形:根本不是字符串(名单形 `string[]` · 布尔 · 数字 · `null` · 对象);
|
|
126
|
+
* · `lane_not_host` —— `"all"` 落在非 host 车道(只有 host 工厂消费这一位,别的车道没有消费点);
|
|
127
|
+
* · `no_authorizer_session` —— `"all"` 而部署不接 authorizer(绑定按会话键控,没有会话就没有地方兑现)。
|
|
128
|
+
* 前两词的判官是 {@link inheritEnvFieldIssue}(只看体);后两词的判官在 `boot/resolve-spec.ts`(要读部署事实,两腿同过)。
|
|
129
|
+
* 不在这组的:`422 request.host_posture_refused`(托管部署不兑现 `"all"`)—— 那不是这个键的值写错,而是部署姿态拒,码与写容纳面那条腿共用,按 `errorCode` 认(契约 §18)。
|
|
130
|
+
* 加一种拒因 ⇒ 这里加一个词(并在判官处给它一个分支);词与因一一对应,没有一因两词、也没有一词两因。
|
|
131
|
+
* 词的铸点只有 {@link inheritEnvFacts} 一处:受理门与 resolveSpec 只经它出机读位,不手拼 `field` / `reason`。
|
|
132
|
+
*/
|
|
133
|
+
export declare const INHERIT_ENV_REFUSAL_REASONS: readonly ["unknown_word", "not_a_string", "lane_not_host", "no_authorizer_session"];
|
|
134
|
+
export type InheritEnvRefusalReason = (typeof INHERIT_ENV_REFUSAL_REASONS)[number];
|
|
135
|
+
/** 拒体上的机读位(加性键:直接当 `request.field_invalid` 的附加键用;型别名而不是接口 —— 要能直接当 `Record<string, unknown>` 用)。 */
|
|
136
|
+
export type InheritEnvFacts = {
|
|
137
|
+
readonly field: "inheritEnv";
|
|
138
|
+
readonly reason: InheritEnvRefusalReason;
|
|
139
|
+
};
|
|
140
|
+
/** `inheritEnv` 机读位的**唯一铸点**:`field` 恒为键名,`reason` 是 {@link INHERIT_ENV_REFUSAL_REASONS} 里的词。 */
|
|
141
|
+
export declare function inheritEnvFacts(reason: InheritEnvRefusalReason): InheritEnvFacts;
|
|
142
|
+
/** 一次值形裁决的产物:wire 句 + 机读位。 */
|
|
143
|
+
export interface InheritEnvFieldIssue {
|
|
144
|
+
readonly message: string;
|
|
145
|
+
readonly facts: InheritEnvFacts;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `inheritEnv` 在场且不在词表里 ⇒ 拒(句 + 机读位);缺席或合法 ⇒ `null`。**两腿(fresh admission / resume 重放)共用这一只**,判据与句子在这一处,机读位经 {@link inheritEnvFacts}。
|
|
149
|
+
* 句子对两种坏值形是同一句(点名两词与「名单不是请求值」);`reason` 区分它们。
|
|
150
|
+
*/
|
|
151
|
+
export declare function inheritEnvFieldIssue(raw: unknown): InheritEnvFieldIssue | null;
|
|
123
152
|
export {};
|
|
124
153
|
//# sourceMappingURL=request-value-closure.d.ts.map
|
|
@@ -37,12 +37,18 @@ export const INHERIT_ENV_WORDS = ["all", "scrub"];
|
|
|
37
37
|
export const INHERIT_ENV_VALUE_MESSAGE = `inheritEnv must be one of ${INHERIT_ENV_WORDS.map((w) => JSON.stringify(w)).join(" | ")} when present ` +
|
|
38
38
|
`("all" = the model-driven host shell inherits this machine's whole environment except this deployment's own credentials (its configuration directory's secret-typed names), honored only on a single-user host-lane deployment; ` +
|
|
39
39
|
`"scrub" = the default scrub of credential-shaped variables, accepted on every lane; absent = "scrub") — a name list is not a request value`;
|
|
40
|
+
export const INHERIT_ENV_REFUSAL_REASONS = ["unknown_word", "not_a_string", "lane_not_host", "no_authorizer_session"];
|
|
41
|
+
export function inheritEnvFacts(reason) {
|
|
42
|
+
return { field: "inheritEnv", reason };
|
|
43
|
+
}
|
|
40
44
|
export function inheritEnvFieldIssue(raw) {
|
|
41
45
|
if (raw === null || raw === undefined || typeof raw !== "object" || Array.isArray(raw))
|
|
42
46
|
return null;
|
|
43
47
|
const value = raw.inheritEnv;
|
|
44
48
|
if (value === undefined)
|
|
45
49
|
return null;
|
|
46
|
-
|
|
50
|
+
if (INHERIT_ENV_WORDS.includes(value))
|
|
51
|
+
return null;
|
|
52
|
+
return { message: INHERIT_ENV_VALUE_MESSAGE, facts: inheritEnvFacts(typeof value === "string" ? "unknown_word" : "not_a_string") };
|
|
47
53
|
}
|
|
48
54
|
//# sourceMappingURL=request-value-closure.js.map
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* (拒面按 ③ 的方向分),拒句点名有界({@link MAX_NAMED_INERT_ENTRIES} 条 + 计数),清单先过条数帽({@link inertToolListEntryOf})。
|
|
27
27
|
* **真未知名**(拼写合法、没有近似也没有退役对应 —— 懒挂载的合法形,例如一台之后才连上的 MCP 服务器的工具)不是读成空:原样保留、运维行告知一次,不拒。
|
|
28
28
|
* 选择器读一条条目 = 引擎 `judgeToolNameList` 的读法({@link selectorEntryName}):先取工具名那一段再只判名字(限制侧 `Tool(content)` ⇒ 整只工具,内容里写什么都不读;放行侧内容形引擎告「什么也不授」,受理口不拒);
|
|
29
|
-
* `*`
|
|
29
|
+
* `*` 在放行侧是「本层不设名单」、在限制侧是整名 glob(引擎文法 5,7.137.0 起;此前读成空、受理口拒);限制侧整个协议的名字 `mcp__*` / `mcp__*__*` / `a2a__*` 合法 —— 那是引擎条目读法的读数(core 7.55.0 [ref]:
|
|
30
30
|
* `compileAllowList(…, "deny", 名册)` 把它们读成这一协议的每一只工具),本仓不另写豁免。
|
|
31
31
|
* ⑤ **机读位**:拒体的码与句子是第一处命中的清单的(句子冻结),体另带这一遍判下来的**全部**读成空的条目(`entry-problems.ts` 的 `detail.problems`,{@link inertToolListEntryOf} 铸);
|
|
32
32
|
* 放行侧选择器清单里受理口没拒、却什么也不放的条目随受理应答的 `warnings` 交回({@link allowSelectorWarningsOf})—— 两处读同一次条目读法({@link selectorReadings}),不另判。
|
|
@@ -55,7 +55,8 @@ export declare function allowLayerEntryCountIssue(field: string, list: readonly
|
|
|
55
55
|
/** 一条引擎读成空的条目(引擎的形:原因词,名字层另带判词 / 1:1 现名;包根不导出这只具名型 ⇒ 从 `compileAllowList` 的返回型取,不手抄)。 */
|
|
56
56
|
export type InertRuleEntry = ReturnType<typeof compileAllowList>["inert"][number];
|
|
57
57
|
/**
|
|
58
|
-
* ③ 引擎读一张**规则条目表**(`behavior` 方向)时读成空的条目(
|
|
58
|
+
* ③ 引擎读一张**规则条目表**(`behavior` 方向)时读成空的条目(引擎文法 5,core 7.56.0:allow 表里名内的 `*` 只在协议名的工具段里读,别处的 `*` 只作废它自己、旁边的条目照读;
|
|
59
|
+
* deny / ask 表里带 `*` 的名是整名 glob,不读空)。条目名过引擎判官,
|
|
59
60
|
* 名册是本仓**唯一**的一张({@link TOOL_NAME_ROSTER},`namespaced-rule-name.ts`:引擎目录 ∪ 本仓自铸的工具名)—— 两个方向、每一处读条目名的地方(请求体的规则表、技能清单、选择器清单、
|
|
60
61
|
* 运维 / 中心的审批名单)传同一张,没有「这一处空名册、那一处带名册」的两种读法。工具名选择器清单另走 {@link inertToolListEntryOf}。
|
|
61
62
|
*/
|
package/dist/rule-entry-list.js
CHANGED
|
@@ -27,9 +27,9 @@ export function inertRuleEntryGuidance(i) {
|
|
|
27
27
|
case "near_miss":
|
|
28
28
|
return `the tool name differs from a mounted tool only by case${write}`;
|
|
29
29
|
case "wildcard_all":
|
|
30
|
-
return "`*`
|
|
30
|
+
return "`*` grants nothing on an allow list (the entries beside it still apply) — name the tools instead; on a deny or ask list `*` is every tool";
|
|
31
31
|
case "namespace_wildcard":
|
|
32
|
-
return "a
|
|
32
|
+
return "an allow list reads a `*` only in a protocol name's tool segment — name the server: mcp__<server> or mcp__<server>__*; on a deny or ask list a `*` in a name is a glob over the whole name (mcp__* is every MCP tool, mcp__git* every tool whose name starts with it)";
|
|
33
33
|
case "content_unsupported":
|
|
34
34
|
return "this tool takes no content form here — name the tool itself, or use Bash(<command>) / Bash(<prefix>:*) for a shell command";
|
|
35
35
|
case "grammar":
|
package/dist/rules-consent.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createLogger } from "./observability/logger.js";
|
|
|
5
5
|
import { ABSOLUTE_HOST_PATH_MAX, hasControlOrBidi } from "@sema-agent/settings-schema";
|
|
6
6
|
import { isDirectWritableRuleBehavior } from "./permission-rule-vocab.js";
|
|
7
7
|
import { hasBidiControls } from "./text-bidi.js";
|
|
8
|
-
import { confirmRuleApproval, RULE_BEHAVIORS, parseRuleText, ruleReachesProgramRun, ruleToolGrammarOf, precheckEditedRuleText, prepareCardApproval, prepareCcImport, redeemRuleBatch, redeemRuleTicket, addPersistedRule, removePersistedRule, isValidDurableScope, effectiveOrThrow, readRowBehavior, } from "@sema-agent/core";
|
|
8
|
+
import { confirmRuleApproval, RULE_BEHAVIORS, parseRuleText, ruleReachesProgramRun, ruleToolGrammarOf, precheckEditedRuleText, prepareCardApproval, prepareCcImport, redeemRuleBatch, redeemRuleTicket, addPersistedRule, removePersistedRule, isValidDurableScope, effectiveOrThrow, readRowBehavior, readRuleOwner, } from "@sema-agent/core";
|
|
9
9
|
import { buildRulePayloadHash } from "./plugins/permission-rule-store-sql.js";
|
|
10
10
|
export const RULE_IMPORT_TICKET_TTL_MS = 10 * 60_000;
|
|
11
11
|
export const RULE_IMPORT_RETRY_AFTER_SEC = 2;
|
|
@@ -70,16 +70,19 @@ export function sameWireRuleIdentity(row, identity) {
|
|
|
70
70
|
function storeForBucket(provider, ref) {
|
|
71
71
|
if (typeof ref === "string")
|
|
72
72
|
return provider.forPrincipal(ref);
|
|
73
|
-
|
|
73
|
+
const owner = readRuleOwner(ref);
|
|
74
|
+
if (owner === undefined)
|
|
75
|
+
throw new Error("unrecognised permission-rule bucket reference (not a plain owner record, an unknown kind, or a principal that is no principal name) — refusing to guess which bucket it names");
|
|
76
|
+
switch (owner.kind) {
|
|
74
77
|
case "principal":
|
|
75
|
-
return provider.forPrincipal(
|
|
78
|
+
return provider.forPrincipal(owner.principal);
|
|
76
79
|
case "local-owner":
|
|
77
80
|
if (provider.forLocalOwner === undefined) {
|
|
78
81
|
throw new Error("this provider has no local-owner bucket (forLocalOwner is not implemented) — a local-owner rule cannot be listed or removed through it");
|
|
79
82
|
}
|
|
80
83
|
return provider.forLocalOwner();
|
|
81
84
|
default: {
|
|
82
|
-
const unknown =
|
|
85
|
+
const unknown = owner;
|
|
83
86
|
throw new Error(`unrecognised permission-rule bucket reference (${JSON.stringify(unknown)}) — refusing to guess which bucket it names`);
|
|
84
87
|
}
|
|
85
88
|
}
|
package/dist/run-local.d.ts
CHANGED
|
@@ -174,8 +174,8 @@ type NoTtyRemedy = NoTtySpokenRemedy | "terminal_only";
|
|
|
174
174
|
* · `shell_floor`:shell 地板被抬到 always —— `AUTONOMY=ask`(`autonomyOverrides`)与 `MANUAL_MODE_SHELL_GATE=always`,真 Runner 读数两者的类集都是 `[shell_floor]`(**不是** `operator_forced`):
|
|
175
175
|
* 单独一句,不与 `operator_forced` 的来源混在一起 —— 混说会把 `APPROVAL_NEVER_AUTO` 的问指到一定没设的 `AUTONOMY`;
|
|
176
176
|
* · 其余:引擎自己的问(写门 / 形状层 / 写保护 / 探针 / 钩子 / 组织规则 / 引擎的守卫 / 工具标记 …)没有一只本部署旋钮能放行 —— 只说去终端答。
|
|
177
|
-
* 不借 core 的 `headlessApprovalRemedy`:那一句是给**库的调用方**的出路(「wire an approver (`onAsk`)
|
|
178
|
-
*
|
|
177
|
+
* 不借 core 的 `headlessApprovalRemedy`:那一句是给**库的调用方**的出路(「wire an approver (`onAsk`)」;core 7.56.0 [ref] 起它不再教模式放行,
|
|
178
|
+
* 那一条出路只作数据骑在通告的 `detail.remedies` 上),而本腿的席**已经接好**(缺的是终端),也没有任何命令行旗给 `permissionMode` —— 照搬会把运维指向一个不存在的旋钮。
|
|
179
179
|
*
|
|
180
180
|
* `autoBudgetReleases`:`APPROVAL_AUTO_BUDGET` 能放行这类问吗(读 `createDurableAskPolicy` 的预算臂:只放行**没进 `APPROVAL_NEVER_AUTO`、耐久资源没耗尽**的
|
|
181
181
|
* `APPROVAL_REQUIRE` 命中,换成一句带理由的 allow)。按 `classes` **整体**读:集里每一类都放得行才建议预算 —— 折叠了别的类(`operator_forced` 等)说明另一层的问仍在,
|
package/dist/runs.js
CHANGED
|
@@ -7,6 +7,7 @@ import { isSessionErrorCode, sessionErrorHttp } from "./session-error-wire.js";
|
|
|
7
7
|
import { redactErrorMessage } from "./observability/run-terminal.js";
|
|
8
8
|
import { withPrincipal } from "./observability/principal-context.js";
|
|
9
9
|
import { withModelReachScope } from "./model-reach-scope.js";
|
|
10
|
+
import { runSkillsOf } from "./capabilities/skills.js";
|
|
10
11
|
import { createInstructionSelectionLeg } from "./instruction-selection.js";
|
|
11
12
|
import { redactSecrets } from "./trace/redact.js";
|
|
12
13
|
import { taskNotificationEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "./trace/project.js";
|
|
@@ -330,7 +331,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
330
331
|
metrics?.inc("tasks_total", { status: "needs_review" });
|
|
331
332
|
};
|
|
332
333
|
const instructionLeg = createInstructionSelectionLeg();
|
|
333
|
-
const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData),
|
|
334
|
+
const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(spec) }) });
|
|
334
335
|
const append = (type, data) => sink.append(type, data);
|
|
335
336
|
const flush = () => sink.flush();
|
|
336
337
|
const flushModelUsage = () => appendModelUsageDelta(append, modelUsage, taskId);
|
|
@@ -95,7 +95,7 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
95
95
|
center: { baseUrl: opts.center.baseUrl, credential },
|
|
96
96
|
cache: new Map(),
|
|
97
97
|
inflight: new Map(),
|
|
98
|
-
}));
|
|
98
|
+
})).now;
|
|
99
99
|
const readMintedDeny = new WeakSet();
|
|
100
100
|
const MAX_CACHE = 10_000;
|
|
101
101
|
const putCache = (cache, key, entry) => {
|
|
@@ -65,9 +65,9 @@ export declare function validateCommandRules(rules: CommandRule[]): string[];
|
|
|
65
65
|
* (deny-wins) by {@link applyRuntimeGovernance}, so it is tighten-only.
|
|
66
66
|
*
|
|
67
67
|
* 🔴 **[ref]:返回叶,不在这里折**(改名自 `compileCommandPolicy`,修前返回 blocklist 形里 `combinePolicies(coarseDeny, askList)` 的
|
|
68
|
-
* 一只折叠)。复合命令(`a && b`、管道、`;`)上两只叶都按「解析不了 ⇒ 问」问 ⇒
|
|
69
|
-
*
|
|
70
|
-
* {@link applyRuntimeGovernance} 逐叶盖位、折**一次**:本函数不知道出身这件事,治理层只有一个折叠点。 (Assumes commands are pre-validated by
|
|
68
|
+
* 一只折叠)。复合命令(`a && b`、管道、`;`)上两只叶都按「解析不了 ⇒ 问」问 ⇒ 那只折叠把落选者的类记进引擎的认证记录(core 7.56.0:
|
|
69
|
+
* `attestation.foldedClasses`;7.39.0–7.48.x 是对象身份担保的 `foldedAskClasses` 成员,副本被按名拒;7.49.0 [ref] 起带引擎记录的副本照收),而治理层要给每只 ask 盖部署出身。
|
|
70
|
+
* 叶交出去,由 {@link applyRuntimeGovernance} 逐叶盖位、折**一次**:本函数不知道出身这件事,治理层只有一个折叠点。 (Assumes commands are pre-validated by
|
|
71
71
|
* {@link validateCommandRules}; an un-matchable command here is simply inert, never a parse error.)
|
|
72
72
|
*
|
|
73
73
|
* 🔴 **[ref]:词表外的 `decision` 折 `deny`,不折 `ask`。** {@link validateCommandRules} 的头注写下的那条
|
|
@@ -271,8 +271,8 @@ export declare function governanceOriginOf(gate: {
|
|
|
271
271
|
*
|
|
272
272
|
* 🔴 **[ref](core [ref] 7.39.0)—— 治理层只有一个折叠点**:两种输入都交**叶**(`compileCommandPolicyLayers` 的那几只、
|
|
273
273
|
* `sensitivePathLayers` 的那几只),本函数对每一片叶各盖一次部署出身(`createDeploymentAuthoredPolicy`),再交 core
|
|
274
|
-
* `combinePolicies`
|
|
275
|
-
* `foldedAskClasses` 时交给 core
|
|
274
|
+
* `combinePolicies` 折一次;折叠是最外层,输出经标记观察器原物交回。修前是「先折、再在折叠输出上展开盖位」——7.39.0–7.48.x 上折叠带出
|
|
275
|
+
* `foldedAskClasses` 时交给 core 的是副本,被按名拒(工具不跑、无卡);core 7.56.0 起那一成员退役、落选者的类骑在 `attestation` 记录上。
|
|
276
276
|
*/
|
|
277
277
|
export declare function applyRuntimeGovernance(base: TaskSpec, governance: {
|
|
278
278
|
autonomy?: Autonomy;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Why an entry is withdrawn — the server's own words (closed set; the vault's per-origin and whole-answer words join it with the plane's renewal). */
|
|
2
|
+
export type SandboxLeaseWithdrawReason = "center_unavailable" | "insecure_transport";
|
|
3
|
+
/** The sentence kinds an entry carries: a withdrawal reason, or the `ready` entry's "the git host refused the credential". */
|
|
4
|
+
export type SandboxLeaseSentenceKind = SandboxLeaseWithdrawReason | "credential_rejected";
|
|
5
|
+
/** What git shows for an entry (single line, no credential, names the origin and the way out). */
|
|
6
|
+
export declare function sandboxLeaseSentence(kind: SandboxLeaseSentenceKind, origin: string): string;
|
|
7
|
+
/** One origin's lease as the server decides it (the writer supplies `i`, `origin`, `leaseId` and the sentences). */
|
|
8
|
+
export type SandboxLeaseEntryInput = {
|
|
9
|
+
readonly state: "ready";
|
|
10
|
+
readonly username: string;
|
|
11
|
+
readonly secret: string;
|
|
12
|
+
readonly credentialId: string;
|
|
13
|
+
readonly version: number;
|
|
14
|
+
readonly ttlMs: number;
|
|
15
|
+
} | {
|
|
16
|
+
readonly state: "withdrawn";
|
|
17
|
+
readonly reason: SandboxLeaseWithdrawReason;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The lease file text for a pod whose origin table is `origins` (one input per origin, same order). Refused (never sent) when the broker
|
|
21
|
+
* would refuse it: an input count that is not the table's, a `ready` input for an http origin, `ttlMs` outside (0, 300 000], a malformed
|
|
22
|
+
* value. The refusal names the entry and the rule, never a value.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildSandboxLeaseFile(origins: readonly string[], entries: readonly SandboxLeaseEntryInput[], mintLeaseId?: () => string): {
|
|
25
|
+
ok: true;
|
|
26
|
+
text: string;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
problem: string;
|
|
30
|
+
};
|
|
31
|
+
/** The lease a pod starts with, before the first answer from the vault: every entry withdrawn (https ⇒ `center_unavailable`, http ⇒ `insecure_transport`). */
|
|
32
|
+
export declare function placeholderLeaseEntries(origins: readonly string[]): SandboxLeaseEntryInput[];
|
|
33
|
+
/**
|
|
34
|
+
* The lease time of one vault answer: `min(cacheUntil, expiresAt ?? cacheUntil) − answeredAt − rttMs`, capped at the broker's ceiling,
|
|
35
|
+
* at least 1; `undefined` when that is ≤ 0 or any reading is not a finite number (the entry is then written `withdrawn`). The three times are
|
|
36
|
+
* the vault's own clock; `rttMs` is this server's monotonic measure of the request it sent and the answer it got.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sandboxLeaseTtlMs(answer: {
|
|
39
|
+
readonly answeredAt: number;
|
|
40
|
+
readonly cacheUntil: number;
|
|
41
|
+
readonly expiresAt: number | null;
|
|
42
|
+
}, rttMs: number): number | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* The agent container's git routes (non-secret env): one `url.<broker route>.insteadOf = <origin>/` triplet per origin, in the pod's
|
|
45
|
+
* origin-table order — git then sends `<origin>/<owner>/<repo>/…` to the broker's loopback route `/o/<i>/…` (http origins too: they are
|
|
46
|
+
* forwarded without a credential and answered with their sentence).
|
|
47
|
+
*/
|
|
48
|
+
export declare function sandboxGitRouteEnv(origins: readonly string[]): Record<string, string>;
|
|
49
|
+
//# sourceMappingURL=sandbox-git-lease.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { BROKER_HOST, BROKER_PORT } from "./broker/constants.js";
|
|
3
|
+
import { LEASE_TTL_MAX_MS, parseLeaseFile, serializeLeaseFile } from "./broker/lease-file.js";
|
|
4
|
+
export function sandboxLeaseSentence(kind, origin) {
|
|
5
|
+
switch (kind) {
|
|
6
|
+
case "center_unavailable":
|
|
7
|
+
return `your git credential for ${origin} is not available right now (the console could not be reached); retry shortly, and do not paste a token into the conversation`;
|
|
8
|
+
case "insecure_transport":
|
|
9
|
+
return `${origin} is served over plain http, so member credentials are never sent to it; ask the operator to serve it over https`;
|
|
10
|
+
case "credential_rejected":
|
|
11
|
+
return `${origin} rejected your git credential; reconnect it in the console, then retry`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function buildSandboxLeaseFile(origins, entries, mintLeaseId = () => `l_${randomUUID()}`) {
|
|
15
|
+
if (entries.length !== origins.length)
|
|
16
|
+
return { ok: false, problem: `the lease has ${entries.length} entries for a pod routed to ${origins.length} origins` };
|
|
17
|
+
const file = {
|
|
18
|
+
v: 1,
|
|
19
|
+
origins: entries.map((e, i) => {
|
|
20
|
+
const origin = origins[i];
|
|
21
|
+
return e.state === "ready"
|
|
22
|
+
? {
|
|
23
|
+
i,
|
|
24
|
+
origin,
|
|
25
|
+
state: "ready",
|
|
26
|
+
leaseId: mintLeaseId(),
|
|
27
|
+
ttlMs: e.ttlMs,
|
|
28
|
+
username: e.username,
|
|
29
|
+
secret: e.secret,
|
|
30
|
+
credentialId: e.credentialId,
|
|
31
|
+
version: e.version,
|
|
32
|
+
unusableMessage: sandboxLeaseSentence("center_unavailable", origin),
|
|
33
|
+
rejectedMessage: sandboxLeaseSentence("credential_rejected", origin),
|
|
34
|
+
}
|
|
35
|
+
: { i, origin, state: "withdrawn", reason: e.reason, unusableMessage: sandboxLeaseSentence(e.reason, origin) };
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
const text = serializeLeaseFile(file);
|
|
39
|
+
const judged = parseLeaseFile(text);
|
|
40
|
+
return judged.ok ? { ok: true, text } : { ok: false, problem: judged.problem };
|
|
41
|
+
}
|
|
42
|
+
export function placeholderLeaseEntries(origins) {
|
|
43
|
+
return origins.map((o) => ({ state: "withdrawn", reason: o.startsWith("https://") ? "center_unavailable" : "insecure_transport" }));
|
|
44
|
+
}
|
|
45
|
+
export function sandboxLeaseTtlMs(answer, rttMs) {
|
|
46
|
+
const until = answer.expiresAt === null ? answer.cacheUntil : Math.min(answer.cacheUntil, answer.expiresAt);
|
|
47
|
+
const ms = until - answer.answeredAt - rttMs;
|
|
48
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
return Math.max(1, Math.min(LEASE_TTL_MAX_MS, Math.floor(ms)));
|
|
51
|
+
}
|
|
52
|
+
export function sandboxGitRouteEnv(origins) {
|
|
53
|
+
const env = { GIT_CONFIG_COUNT: String(origins.length) };
|
|
54
|
+
origins.forEach((origin, i) => {
|
|
55
|
+
env[`GIT_CONFIG_KEY_${i}`] = `url.http://${BROKER_HOST}:${BROKER_PORT}/o/${i}/.insteadOf`;
|
|
56
|
+
env[`GIT_CONFIG_VALUE_${i}`] = `${origin}/`;
|
|
57
|
+
});
|
|
58
|
+
return env;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=sandbox-git-lease.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ServiceConfig } from "./config.js";
|
|
2
|
+
import type { SandboxSecretSeat } from "./plugins/remote-env-k8s.js";
|
|
3
|
+
import type { IdentitySource, RunIdentity } from "./identity-source.js";
|
|
4
|
+
import type { Logger } from "./observability/logger.js";
|
|
5
|
+
/** The off reasons, in judge order (closed set; an added premise is a word here, a row in {@link SANDBOX_GIT_PLANE_OFF_TEXT}, a step in the judge). */
|
|
6
|
+
export declare const SANDBOX_GIT_PLANE_OFF_REASONS: readonly ["single_user", "lane_without_boundary", "no_caller_credential", "center_not_https", "secret_seat_unverified", "secret_seat_denied", "pod_patch_reaches_broker", "broker_image_unset", "no_origins"];
|
|
7
|
+
export type SandboxGitPlaneOffReason = (typeof SANDBOX_GIT_PLANE_OFF_REASONS)[number];
|
|
8
|
+
/** One origin of an on plane. */
|
|
9
|
+
export type SandboxGitPlaneOrigin = {
|
|
10
|
+
readonly origin: string;
|
|
11
|
+
readonly delivers: true;
|
|
12
|
+
} | {
|
|
13
|
+
readonly origin: string;
|
|
14
|
+
readonly delivers: false;
|
|
15
|
+
readonly reason: "insecure_transport";
|
|
16
|
+
};
|
|
17
|
+
/** The plane's reading (the wire value of `credentialPlane.sandboxGit`). */
|
|
18
|
+
export type SandboxGitPlane = {
|
|
19
|
+
readonly state: "on";
|
|
20
|
+
readonly origins: readonly SandboxGitPlaneOrigin[];
|
|
21
|
+
} | {
|
|
22
|
+
readonly state: "off";
|
|
23
|
+
readonly reason: SandboxGitPlaneOffReason;
|
|
24
|
+
};
|
|
25
|
+
/** Operator sentence per off reason (the boot line and the docs read it; the record type makes a missing word a compile error). */
|
|
26
|
+
export declare const SANDBOX_GIT_PLANE_OFF_TEXT: {
|
|
27
|
+
readonly [R in SandboxGitPlaneOffReason]: string;
|
|
28
|
+
};
|
|
29
|
+
/** The premises, each as the judge reads it. */
|
|
30
|
+
export interface SandboxGitPlanePremises {
|
|
31
|
+
readonly multiTenant: boolean;
|
|
32
|
+
readonly k8sLane: boolean;
|
|
33
|
+
readonly callerCredential: boolean;
|
|
34
|
+
/** The console base address (`SEMA_WEB_ADMIN_URL`); absent ⇒ not https. */
|
|
35
|
+
readonly centerUrl: string | undefined;
|
|
36
|
+
/** The boot's SelfSubjectAccessReview answer for the sandbox Secret seat; `undefined` = not asked (counts as unverified). */
|
|
37
|
+
readonly secretSeat: SandboxSecretSeat | undefined;
|
|
38
|
+
/** The patch judge's refusal for a pod that carries the broker (`podSpecPatchBrokerRefusal`); `undefined` = the patch may land. */
|
|
39
|
+
readonly patchRefusal: string | undefined;
|
|
40
|
+
readonly brokerImage: string | undefined;
|
|
41
|
+
/** The routed origins (`readSandboxGitConfig`); `null` = no `sandboxGit` block. */
|
|
42
|
+
readonly origins: readonly string[] | null;
|
|
43
|
+
}
|
|
44
|
+
/** THE judge: the first premise that fails, in the written order; all hold ⇒ on with per-origin rows. Pure. */
|
|
45
|
+
export declare function judgeSandboxGitPlane(p: SandboxGitPlanePremises): SandboxGitPlane;
|
|
46
|
+
/** The secret seat is asked for the plane only when every premise before it holds (a deployment the plane is off for asks nothing for it). */
|
|
47
|
+
export declare function sandboxGitPlaneAsksSecretSeat(p: Pick<SandboxGitPlanePremises, "multiTenant" | "k8sLane" | "callerCredential" | "centerUrl">): boolean;
|
|
48
|
+
/** The premises read from config, plus the two boot readings (seat answer, patch refusal) when the boot has them. */
|
|
49
|
+
export declare function sandboxGitPlanePremisesOf(config: ServiceConfig, readings: {
|
|
50
|
+
readonly secretSeat?: SandboxSecretSeat;
|
|
51
|
+
readonly patchRefusal?: string;
|
|
52
|
+
}): SandboxGitPlanePremises;
|
|
53
|
+
/**
|
|
54
|
+
* Wiring point — the effective `sandboxGit` block (the origins this deployment routes through the broker, canonical form, ≤ 16). Its shape
|
|
55
|
+
* and its canonical-form judge belong to the settings-schema contract package (14.7.0); until that release is picked up there is no block to
|
|
56
|
+
* read ⇒ `null` (the plane stays off, `no_origins`). Replaced by the package reader on pickup — no local copy of the shape.
|
|
57
|
+
*/
|
|
58
|
+
export declare function readSandboxGitConfig(_config: ServiceConfig): {
|
|
59
|
+
readonly origins: readonly string[];
|
|
60
|
+
} | null;
|
|
61
|
+
/**
|
|
62
|
+
* Wiring point — does this worker hold the credential the console's vault admits on its resolve endpoint (the worker-assertion token)?
|
|
63
|
+
* The server has no reader for it yet ⇒ `false` (the plane stays off, `no_caller_credential`). Replaced by the plane's vault client.
|
|
64
|
+
*/
|
|
65
|
+
export declare function readSandboxGitCallerCredential(_config: ServiceConfig): boolean;
|
|
66
|
+
/** The run store's by-id read, as far as the plane needs it: the row's identity tuple (`RunRecord.identity`). Every run store's `getRun` is one. */
|
|
67
|
+
export interface SandboxGitRunRows {
|
|
68
|
+
getRun(taskId: string): Promise<{
|
|
69
|
+
readonly identity: RunIdentity | null;
|
|
70
|
+
} | null | undefined>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The identity source recorded on the pod's run row (`RunRecord.identity.source`, minted once at admission, never recomputed). `runId` is the
|
|
74
|
+
* ONE reading of "the run that owns this pod" (the per-run binding's: the top-level run's id; a delegated child / a service run / no task id ⇒
|
|
75
|
+
* none). No run id / no run store / no row / `identity: null` ⇒ `undefined`; a read that throws ⇒ `undefined` and one warn line
|
|
76
|
+
* `sandbox_broker_identity_unread` —— every miss reads as "not sso" ⇒ no broker (fail-closed), none of them throws into the pod's connect.
|
|
77
|
+
*/
|
|
78
|
+
export declare function runIdentitySourceOf(runId: string | undefined, runs: SandboxGitRunRows | undefined, logger?: Pick<Logger, "warn">): Promise<IdentitySource | undefined>;
|
|
79
|
+
/** The broker seat for one run's pod: the plane on ∧ the image set ∧ the run's recorded identity source `sso` ⇒ the image + every origin of the reading (http too); otherwise none. */
|
|
80
|
+
export declare function sandboxBrokerSeatOf(plane: SandboxGitPlane, brokerImage: string | undefined, identitySource: IdentitySource | undefined): {
|
|
81
|
+
readonly image: string;
|
|
82
|
+
readonly origins: readonly string[];
|
|
83
|
+
} | undefined;
|
|
84
|
+
//# sourceMappingURL=sandbox-git-plane.d.ts.map
|