@sema-agent/server 7.140.0 → 7.142.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 +505 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +142 -25
- package/dist/approval-ask-audit-store.d.ts +54 -38
- package/dist/approval-ask-audit-store.js +58 -39
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +39 -24
- package/dist/approval-card.js +6 -3
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +33 -1
- package/dist/approval-reconciler.js +43 -40
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +16 -0
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +34 -27
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/memory-consolidation.js +3 -2
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/reapers.js +4 -3
- package/dist/boot/resolve-spec.js +18 -6
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.js +16 -13
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +8 -6
- package/dist/boot/shutdown.js +8 -2
- package/dist/boot/stage-04-stores.js +3 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.js +11 -7
- package/dist/boot/stage-06-runners.d.ts +1 -3
- package/dist/boot/stage-06-runners.js +3 -2
- package/dist/boot/stage-07-capability-layer.d.ts +1 -2
- package/dist/boot/stage-07-capability-layer.js +10 -11
- package/dist/boot/stage-08-reapers.d.ts +1 -2
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +1 -2
- package/dist/boot/stage-10-http-server.d.ts +1 -2
- package/dist/boot/stage-10-http-server.js +6 -4
- package/dist/boot/stores.js +13 -6
- package/dist/boot/web-admin.js +18 -17
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +33 -0
- package/dist/capability-facts.js +32 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +12 -8
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +19 -33
- package/dist/config-types.d.ts +39 -10
- package/dist/config.d.ts +6 -0
- package/dist/config.js +72 -49
- package/dist/delegated-session-owner.d.ts +21 -19
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +50 -2
- package/dist/execution-lane-caps.js +90 -13
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/fleet/fleet-bus.d.ts +6 -3
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +30 -1
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +8 -2
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.js +42 -29
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +10 -6
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-policy.js +27 -7
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/runs.js +3 -3
- package/dist/http/routes/session-memory-sync.d.ts +63 -0
- package/dist/http/routes/session-memory-sync.js +157 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/sessions.js +5 -4
- package/dist/http/routes/tasks.js +70 -71
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +10 -4
- package/dist/http/send.d.ts +6 -1
- package/dist/http/send.js +10 -3
- package/dist/http/server.d.ts +20 -7
- package/dist/http/server.js +2 -4
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +1 -1
- package/dist/memory-sync-client.d.ts +35 -23
- package/dist/memory-sync-client.js +122 -52
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +7 -1
- package/dist/memory-sync.js +6 -3
- package/dist/observability/fail-open.d.ts +57 -12
- package/dist/observability/fail-open.js +50 -9
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +93 -31
- package/dist/observability/run-terminal.js +175 -171
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +48 -7
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +9 -8
- package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
- package/dist/plugins/approval-ask-store-sql.js +49 -19
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +74 -3
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +4 -1
- package/dist/plugins/execution-placement-sql.d.ts +11 -1
- package/dist/plugins/execution-placement-sql.js +12 -1
- package/dist/plugins/file-run-store.d.ts +7 -2
- package/dist/plugins/file-run-store.js +12 -2
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +260 -39
- package/dist/plugins/local-session-store.js +582 -57
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +7 -2
- package/dist/plugins/memory-run-store.js +12 -2
- package/dist/plugins/one-time-migrations.d.ts +68 -2
- package/dist/plugins/one-time-migrations.js +120 -4
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +47 -7
- package/dist/plugins/remote-env-e2b.js +242 -46
- package/dist/plugins/remote-env-k8s.d.ts +51 -6
- package/dist/plugins/remote-env-k8s.js +234 -44
- package/dist/plugins/remote-env-local-docker.d.ts +40 -14
- package/dist/plugins/remote-env-local-docker.js +127 -33
- package/dist/plugins/run-store-sql.d.ts +11 -3
- package/dist/plugins/run-store-sql.js +13 -4
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/session-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/store-backend.d.ts +41 -7
- package/dist/plugins/store-backend.js +22 -4
- package/dist/plugins/store-contracts.d.ts +35 -3
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +221 -28
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +3 -2
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/run-local.js +2 -8
- package/dist/runs.d.ts +39 -12
- package/dist/runs.js +69 -55
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +11 -2
- package/dist/security.js +8 -0
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +46 -4
- package/dist/session-error-wire.js +23 -0
- package/dist/session-incarnation.d.ts +9 -3
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +1 -1
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +62 -27
- package/dist/task-settings.js +105 -29
- package/dist/terminal.d.ts +97 -20
- package/dist/terminal.js +107 -18
- package/dist/tool-approval.d.ts +4 -3
- package/dist/tool-approval.js +12 -14
- package/dist/trace/core-keyset-guard.d.ts +196 -2
- package/dist/trace/engine-notice-wire.d.ts +8 -2
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +9 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +8 -4
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -0
- package/dist/trace/redact.js +323 -90
- package/dist/trace/self-minted-wire.d.ts +419 -4
- package/dist/trace/self-minted-wire.js +145 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +22 -2
- package/dist/web-admin-client/candidate-gate.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/types.d.ts +4 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.141.0.json +367 -0
- package/wire-changes/server@7.142.0.json +639 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { StubExecutionEnv } from "@sema-agent/core";
|
|
3
|
-
import { EXECUTION_LANE_WORDS as DECLARABLE_LANE_WORDS, EXECUTION_LANES, isBlankCoordinate } from "@sema-agent/settings-schema/execution-lanes";
|
|
3
|
+
import { EXECUTION_LANE_WORDS as DECLARABLE_LANE_WORDS, EXECUTION_LANES, isBlankCoordinate, retiredLaneWordRefusal } from "@sema-agent/settings-schema/execution-lanes";
|
|
4
4
|
import { DECLARE_HOME_REMEDY, LANE_HOME_UNDECLARED_EVENT, LANE_HOME_UNMEASURED_EVENT, buildHomeAbsentNote, buildHomeUnmeasuredNote } from "./lane-home-probe.js";
|
|
5
5
|
const NO = "unsupported";
|
|
6
6
|
const YES = "supported";
|
|
@@ -97,27 +97,61 @@ export function executionLaneCaps(lane) {
|
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
+
export const SANDBOX_READ_FLOOR_ENV = "SANDBOX_READ_FLOOR";
|
|
101
|
+
export const SANDBOX_READ_FLOOR_WORDS = ["keep", "off"];
|
|
102
|
+
export const SANDBOX_READ_FLOOR_DEFAULT = "keep";
|
|
103
|
+
export function readFloorStateOf(knob, served) {
|
|
104
|
+
switch (knob) {
|
|
105
|
+
case "off":
|
|
106
|
+
return "off";
|
|
107
|
+
case "keep":
|
|
108
|
+
return served ? "kept" : "unserved";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function sandboxReadFloorState(lane, knob) {
|
|
112
|
+
const k = knob ?? SANDBOX_READ_FLOOR_DEFAULT;
|
|
113
|
+
switch (lane) {
|
|
114
|
+
case "e2b":
|
|
115
|
+
return readFloorStateOf(k, false);
|
|
116
|
+
case "k8s":
|
|
117
|
+
return readFloorStateOf(k, false);
|
|
118
|
+
case "local-docker":
|
|
119
|
+
return readFloorStateOf(k, false);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
100
122
|
const IN_PROCESS_BLOCK = new StubExecutionEnv().capabilities;
|
|
101
123
|
const HOST_BLOCK = Object.freeze({ isolation: false, suspendable: false, sharesProcessTable: true, canonicalPathAuthoritative: true, hostLocalPaths: true, externalContentTarget: false, io: true, keepsReadFloor: false });
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
124
|
+
const E2B_SHAPE = Object.freeze({ isolation: true, suspendable: true, sharesProcessTable: false, canonicalPathAuthoritative: true, hostLocalPaths: false, externalContentTarget: false, io: true });
|
|
125
|
+
const K8S_SHAPE = Object.freeze({ isolation: true, suspendable: false, sharesProcessTable: false, canonicalPathAuthoritative: true, hostLocalPaths: false, externalContentTarget: false, io: true });
|
|
126
|
+
const K8S_SNAPSHOT_SHAPE = Object.freeze({ ...K8S_SHAPE, suspendable: true });
|
|
127
|
+
const LOCAL_DOCKER_SHAPE = Object.freeze({ isolation: true, suspendable: false, sharesProcessTable: false, canonicalPathAuthoritative: true, hostLocalPaths: false, externalContentTarget: false, io: true });
|
|
128
|
+
function sandboxBlock(shape, state) {
|
|
129
|
+
return Object.freeze({ ...shape, keepsReadFloor: state === "kept" });
|
|
130
|
+
}
|
|
131
|
+
function sandboxShape(lane, snapshotStore) {
|
|
132
|
+
switch (lane) {
|
|
133
|
+
case "e2b":
|
|
134
|
+
return E2B_SHAPE;
|
|
135
|
+
case "k8s":
|
|
136
|
+
return snapshotStore ? K8S_SNAPSHOT_SHAPE : K8S_SHAPE;
|
|
137
|
+
case "local-docker":
|
|
138
|
+
return LOCAL_DOCKER_SHAPE;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
106
141
|
const SSH_BLOCK = Object.freeze({ isolation: false, suspendable: false, sharesProcessTable: false, canonicalPathAuthoritative: true, hostLocalPaths: false, externalContentTarget: false, io: true, keepsReadFloor: false });
|
|
107
142
|
const ADB_BLOCK = Object.freeze({ isolation: false, suspendable: false, sharesProcessTable: false, canonicalPathAuthoritative: true, hostLocalPaths: false, externalContentTarget: false, io: true, keepsReadFloor: false });
|
|
108
143
|
const DEVICE_BLOCK = Object.freeze({ isolation: false, suspendable: false, sharesProcessTable: false, canonicalPathAuthoritative: true, hostLocalPaths: false, externalContentTarget: true, io: true, keepsReadFloor: false });
|
|
109
|
-
|
|
144
|
+
const NO_SUB_KNOBS = Object.freeze({ snapshotStore: false, sandboxReadFloor: undefined });
|
|
145
|
+
function laneBlock(lane, sub) {
|
|
110
146
|
switch (lane) {
|
|
111
147
|
case "in-process":
|
|
112
148
|
return IN_PROCESS_BLOCK;
|
|
113
149
|
case "host":
|
|
114
150
|
return HOST_BLOCK;
|
|
115
151
|
case "e2b":
|
|
116
|
-
return E2B_BLOCK;
|
|
117
152
|
case "k8s":
|
|
118
|
-
return snapshotStore ? K8S_SNAPSHOT_BLOCK : K8S_BLOCK;
|
|
119
153
|
case "local-docker":
|
|
120
|
-
return
|
|
154
|
+
return sandboxBlock(sandboxShape(lane, sub.snapshotStore), sandboxReadFloorState(lane, sub.sandboxReadFloor));
|
|
121
155
|
case "ssh":
|
|
122
156
|
return SSH_BLOCK;
|
|
123
157
|
case "adb":
|
|
@@ -130,7 +164,24 @@ export function capabilitiesFor(deployment) {
|
|
|
130
164
|
const lane = executionLaneOf(deployment.remoteExec?.provider);
|
|
131
165
|
if (lane === undefined)
|
|
132
166
|
return undefined;
|
|
133
|
-
return laneBlock(lane, lane === "k8s" && Boolean(deployment.remoteExec?.s3Snapshot));
|
|
167
|
+
return laneBlock(lane, { snapshotStore: lane === "k8s" && Boolean(deployment.remoteExec?.s3Snapshot), sandboxReadFloor: deployment.sandboxReadFloor });
|
|
168
|
+
}
|
|
169
|
+
export function laneSandboxReadFloorState(deployment) {
|
|
170
|
+
const lane = executionLaneOf(deployment.remoteExec?.provider);
|
|
171
|
+
if (lane === undefined)
|
|
172
|
+
return undefined;
|
|
173
|
+
switch (lane) {
|
|
174
|
+
case "e2b":
|
|
175
|
+
case "k8s":
|
|
176
|
+
case "local-docker":
|
|
177
|
+
return sandboxReadFloorState(lane, deployment.sandboxReadFloor);
|
|
178
|
+
case "in-process":
|
|
179
|
+
case "host":
|
|
180
|
+
case "ssh":
|
|
181
|
+
case "adb":
|
|
182
|
+
case "device":
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
134
185
|
}
|
|
135
186
|
export const ALL_EXECUTION_LANES = Object.freeze(["in-process", ...DECLARABLE_LANE_WORDS]);
|
|
136
187
|
const EXECUTION_LANE_SET = new Set(ALL_EXECUTION_LANES);
|
|
@@ -166,6 +217,32 @@ export function isBlankRequiredEnvValue(value) {
|
|
|
166
217
|
export function laneMissingEnv(lane, env) {
|
|
167
218
|
return laneRequiredEnv(lane).filter((name) => isBlankRequiredEnvValue(env[name]));
|
|
168
219
|
}
|
|
220
|
+
export function readDeclaredLaneTable(raw) {
|
|
221
|
+
const segments = raw.split(",");
|
|
222
|
+
const lanes = [];
|
|
223
|
+
for (const [i, segment] of segments.entries()) {
|
|
224
|
+
if (segment === "") {
|
|
225
|
+
return { refusal: `REMOTE_EXEC="${raw}" has an empty entry at position ${i + 1} — it is an ordered, comma-separated list of execution lanes (the first is the default) with no blank entries; valid lanes: ${REMOTE_EXEC_PROVIDER_WORDS.join(" | ")}.` };
|
|
226
|
+
}
|
|
227
|
+
const lane = REMOTE_EXEC_PROVIDER_WORDS.find((w) => w === segment);
|
|
228
|
+
const retired = lane === undefined ? retiredLaneWordRefusal(segment) : undefined;
|
|
229
|
+
if (retired !== undefined)
|
|
230
|
+
return { refusal: `REMOTE_EXEC="${raw}": ${retired}.` };
|
|
231
|
+
if (lane === undefined) {
|
|
232
|
+
return {
|
|
233
|
+
refusal: `REMOTE_EXEC="${raw}"${segments.length > 1 ? ` lists "${segment}", which` : ""} is not an execution lane — valid: ${REMOTE_EXEC_PROVIDER_WORDS.join(" | ")}. ` +
|
|
234
|
+
`An unrecognized value used to fall through to the host/in-process lane silently; refusing to start instead (#157).`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (lanes.includes(lane))
|
|
238
|
+
return { refusal: `REMOTE_EXEC="${raw}" lists "${lane}" twice — each execution lane appears once (the first one is the default).` };
|
|
239
|
+
lanes.push(lane);
|
|
240
|
+
}
|
|
241
|
+
const [first, ...rest] = lanes;
|
|
242
|
+
if (first === undefined)
|
|
243
|
+
throw new Error(`readDeclaredLaneTable: "${raw}" split into no segments`);
|
|
244
|
+
return { lanes: [first, ...rest] };
|
|
245
|
+
}
|
|
169
246
|
export function executionLaneRequiredRefusal(deployment) {
|
|
170
247
|
if (!deployment.requireExecutionLane || executionLaneOf(deployment.remoteExec?.provider) !== "in-process")
|
|
171
248
|
return undefined;
|
|
@@ -189,7 +266,7 @@ export function deployedExecutionLanes(config) {
|
|
|
189
266
|
return [executionLaneOf(config.remoteExec?.provider)];
|
|
190
267
|
}
|
|
191
268
|
export function handsOnWorkerFilePlane(lane) {
|
|
192
|
-
const block = laneBlock(lane,
|
|
269
|
+
const block = laneBlock(lane, NO_SUB_KNOBS);
|
|
193
270
|
return block.io && block.hostLocalPaths;
|
|
194
271
|
}
|
|
195
272
|
export function laneBindsReads(lane) {
|
|
@@ -236,10 +313,10 @@ export function laneReadFaceDefault(lane) {
|
|
|
236
313
|
}
|
|
237
314
|
export const LANE_OPEN_READ_FACE_CAUTION = "a deployment that mounts host paths or shared volumes into the sandbox (hostPath, bind mount) exposes those host files to reads on an open face — pin READ_FACE=roots or add read-deny rows (READ_DENY_PATTERNS) for them";
|
|
238
315
|
export function laneIsolated(lane) {
|
|
239
|
-
return laneBlock(lane,
|
|
316
|
+
return laneBlock(lane, NO_SUB_KNOBS).isolation;
|
|
240
317
|
}
|
|
241
318
|
export function laneWiresExecutionEnv(lane) {
|
|
242
|
-
return laneBlock(lane,
|
|
319
|
+
return laneBlock(lane, NO_SUB_KNOBS).io;
|
|
243
320
|
}
|
|
244
321
|
export const PLANNED_LANE_DIVERGENCES = Object.freeze([]);
|
|
245
322
|
export const E2B_SANDBOX_HOME = "/home/user";
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* `execution` 位(`plugins/local-session-store.ts`)。处处 `execution` 限定,与同行 `placement_*`(委派分区)不同义;执行格只在 `placement_kind IS NULL` 的行上 ——
|
|
9
9
|
* 委派分区行读作「不在」。
|
|
10
10
|
* · **读只有一只**:会话店的执行席 {@link ExecutionPlacementSeat.read}(一次行读:住客 + 格;行不在 / 委派分区行 ⇒ `undefined`;读故障抛,不吞成 `absent`)。
|
|
11
|
+
* 跨行的聚合也只有一只:设备店的绑定数 `countDeviceBindings`(writer(device, D) 的格数 = {@link boundDeviceOf} 的聚合;只交数,不交根 id)。
|
|
11
12
|
* · **写只按住客谓词** `(root, incarnation, owner) ∧ placement_kind IS NULL`:住客换了(删后重建,哪怕同一属主;local M10 窗内的认领)= 这一格不在 ⇒ 落空;
|
|
12
13
|
* 0 行一律按住客复读分类({@link settledPlaceOutcome} / {@link settledMirrorOutcome} / {@link settledDeviceBindOutcome}),**永不对现住客重试**。
|
|
13
14
|
* · **没有的转换**:writer ↔ mirror、writer(L) → writer(L')(device 车道的换绑 writer(device, D) → writer(device, D') 除外,它有自己的语句与门)。
|
|
@@ -248,6 +249,8 @@ export declare class InMemoryExecutionPlacementSeat implements ExecutionPlacemen
|
|
|
248
249
|
read(root: string): Promise<ExecutionPlacementReading | undefined>;
|
|
249
250
|
place(root: string, occupant: SessionFace, cell: ExecutionWriterCell): Promise<PlaceOutcome>;
|
|
250
251
|
markMirror(root: string, occupant: SessionFace): Promise<MarkMirrorOutcome>;
|
|
252
|
+
/** 绑定数(SQL 孪生 `countDeviceBindingsOn` 的对位,同一只判官):{@link boundDeviceOf} 读得出设备的格数。 */
|
|
253
|
+
countDeviceBindings(): number;
|
|
251
254
|
/** device 首绑(SQL `placeDeviceStatement` / `bindDeviceStatement` 的对位):absent 或 writer(device, ∅) ⇒ writer(device, D)、rev 0。 */
|
|
252
255
|
bindDevice(root: string, occupant: SessionFace, device: {
|
|
253
256
|
readonly id: string;
|
|
@@ -175,6 +175,13 @@ export class InMemoryExecutionPlacementSeat {
|
|
|
175
175
|
this.rows.set(root, { occupant, cell: { role: "mirror" } });
|
|
176
176
|
return { outcome: "mirrored" };
|
|
177
177
|
}
|
|
178
|
+
countDeviceBindings() {
|
|
179
|
+
let bindings = 0;
|
|
180
|
+
for (const row of this.rows.values())
|
|
181
|
+
if (boundDeviceOf(row.cell) !== undefined)
|
|
182
|
+
bindings += 1;
|
|
183
|
+
return bindings;
|
|
184
|
+
}
|
|
178
185
|
bindDevice(root, occupant, device, placedAt) {
|
|
179
186
|
const settled = settledDeviceBindOutcome(root, this.rows.get(root), occupant, device.id);
|
|
180
187
|
if (settled !== WRITABLE)
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* 纯类型 + 纯函数叶(值 import 只有 scope 读法叶与 run 行判词叶)。
|
|
31
31
|
*/
|
|
32
32
|
import type { DrainReceipt, DrainRequest, EngineDraining } from "@sema-agent/settings-schema/fleet";
|
|
33
|
+
import type { EngineLeaseFacts } from "../boot/engine-lease.js";
|
|
33
34
|
/**
|
|
34
35
|
* 引擎自铸的动作号 —— **一张表**(排空事实与回执里不来自请求体的全部拼法;请求体的 `actionId` 由调用方自取):
|
|
35
36
|
* · {@link ENGINE_DRAIN_CAUSES}:没有请求承载的排空成因(停止信号 / SIGHUP 升级 / 附着租约自退);
|
|
@@ -89,6 +90,8 @@ export interface DrainState {
|
|
|
89
90
|
lastActivityAt?: () => number;
|
|
90
91
|
attachedStreams?: () => number;
|
|
91
92
|
lastAttachEndedAt?: () => number;
|
|
93
|
+
/** [ref]:附着租约窗的读口(`/health.engineLease`)。收尾链在**装了租约腿**时才装它,读口现读租约腿的把手 —— 腿还在观察才有读数,未装配的盒子里根本没有这只口。 */
|
|
94
|
+
engineLease?: () => EngineLeaseFacts | undefined;
|
|
92
95
|
}
|
|
93
96
|
/** 排空盒子里的排空事实(三键齐才是;入口整组写,所以「缺一」只出现在手搭的夹具上)。 */
|
|
94
97
|
export declare function drainingFactOf(s: Pick<DrainState, "draining" | "since" | "actionId" | "deadlineAt">): EngineDraining | undefined;
|
|
@@ -369,8 +369,10 @@ export interface HookNonBlockingFailure {
|
|
|
369
369
|
* · `matcher_over_budget` —— 这条 hook 所在组的 `matcher`(或条目自己的 `if` 条件)在这次事件的这个名字上求值没在预算内结束(灾难回溯;[ref])。
|
|
370
370
|
* **不是 hook 脚本坏了,是它的选中条件不可求值**:server 按「选中」处理(守卫不被跳过,条目照跑),此帧说的是「这只 matcher 在这个名字上跑不完」。
|
|
371
371
|
* **一组一帧**(`if` 一条目一帧),`hookName` / `entryType` 指认组的第一个条目,`detail` 点名 matcher、名字、额度与组里的条目数;无 `exitCode` / `stderr`。
|
|
372
|
+
* · `url_not_allowed` —— http 条目被 http hook 名单拦下(`settings.allowedHttpHookUrls`,契约包判官答拦:没命中 / 空名单 / 名单坏形),**一个字节都没发**(7.142.0 起)。
|
|
373
|
+
* 不是端点坏了,是配置不许它出门;`detail` 点名 `settings.hooks.<event>`、名单键与规则词(`url` / `empty` / `malformed`)和出路;无 `exitCode` / `stderr`。
|
|
372
374
|
*/
|
|
373
|
-
reason: "exit_nonzero" | "timeout" | "spawn_failed" | "bad_json" | "matcher_over_budget";
|
|
375
|
+
reason: "exit_nonzero" | "timeout" | "spawn_failed" | "bad_json" | "matcher_over_budget" | "url_not_allowed";
|
|
374
376
|
/** hook 身份(CC `OG`/`AEe` 锚:`statusMessage` 优先,否则 command / url / prompt),已脱敏+有界。
|
|
375
377
|
* ⚠️ http 条目取 URL 时**剥掉 query 与 fragment**([ref] codex R1-F1:端点凭据最常见的挂法是
|
|
376
378
|
* `?token=…`,而通用脱敏器只认 `user:pass@` 形)。要更友好的名字,作者有 `statusMessage`。 */
|
|
@@ -411,9 +413,10 @@ export interface HookManagedUnrun {
|
|
|
411
413
|
* · `spawn_failed` / `timeout` —— 起不来 / 超时被收;
|
|
412
414
|
* · `exit_nonzero` —— 退出码非 0 非 2;`bad_json` —— 看着是 JSON 却读不成(含 http 条目 2xx 回了非 JSON 体、Stop × prompt 的判词读不出);`decision_out_of_set` —— `permissionDecision` 不在 allow / deny / ask 里;
|
|
413
415
|
* · `evidence_unavailable` —— Stop × prompt 条目因会话证据面不足(或运维关了 transcript)被滤掉没跑;
|
|
414
|
-
* · `rewrite_unsettled` —— 一个托管相(插件相 / 策略相)改写了输入、按新输入把那一相重判一遍后仍交回不同的值(不收敛;「改写」按值判,恒等回显不算)
|
|
416
|
+
* · `rewrite_unsettled` —— 一个托管相(插件相 / 策略相)改写了输入、按新输入把那一相重判一遍后仍交回不同的值(不收敛;「改写」按值判,恒等回显不算);
|
|
417
|
+
* · `url_not_allowed` —— http 条目被 http hook 名单拦下、没发出(7.142.0 起;同一次的 `hook_non_blocking_failure{url_not_allowed}` 照发)。
|
|
415
418
|
*/
|
|
416
|
-
reason: "matcher_invalid" | "llm_carrier_unavailable" | "llm_cap" | "powershell_unsupported" | "if_unevaluable" | "budget_exhausted" | "spawn_failed" | "timeout" | "exit_nonzero" | "bad_json" | "decision_out_of_set" | "evidence_unavailable" | "rewrite_unsettled";
|
|
419
|
+
reason: "matcher_invalid" | "llm_carrier_unavailable" | "llm_cap" | "powershell_unsupported" | "if_unevaluable" | "budget_exhausted" | "spawn_failed" | "timeout" | "exit_nonzero" | "bad_json" | "decision_out_of_set" | "evidence_unavailable" | "rewrite_unsettled" | "url_not_allowed";
|
|
417
420
|
/** 那一组的来源词(`managedPolicy` 或 `managedPlugin:<name>`;名只留痕)。 */
|
|
418
421
|
source: string;
|
|
419
422
|
/** hook 身份(同 `hook_non_blocking_failure`:`statusMessage` 优先,否则 command / url〔剥 query〕/ prompt),已脱敏 + 有界。 */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type HookEntry, type HookEntryType, type HookEventName, type HooksDroppedPiece, type HooksDroppedPieceCode, type HooksRejection, type HooksRejectionCode } from "@sema-agent/settings-schema/hooks";
|
|
2
2
|
import { type ConfigPath, type UrlCredential } from "@sema-agent/settings-schema";
|
|
3
|
+
import { type HttpHookAllowlistLayer } from "@sema-agent/settings-schema";
|
|
3
4
|
import { ENGINE_OWNED_HOOK_EVENTS } from "@sema-agent/settings-schema/hooks";
|
|
4
5
|
import type { EngineDollar, ModRegistration } from "@sema-agent/core";
|
|
5
6
|
import type { Logger } from "../observability/logger.js";
|
|
@@ -514,6 +515,11 @@ export interface HookRunnerContext {
|
|
|
514
515
|
* 的 TaskStream.steer(下个 turn 边界注入);返回 false=没有活流可投(任务已结束/挂起/别副本)——
|
|
515
516
|
* 调用方记账不重试(挂起腿的 pending-steer park 是后续切片)。缺席 ⇒ asyncRewake 降级纯 async(记账)。 */
|
|
516
517
|
wake?: (text: string) => Promise<boolean>;
|
|
518
|
+
/**
|
|
519
|
+
* http hook 两张名单(`allowedHttpHookUrls` / `httpHookAllowedEnvVars`)的**设置层**,交契约包判官原样读(见 {@link runHttpHook})。必填:每个装配点都要说这条 run 有哪些层 ——
|
|
520
|
+
* `[]` = 没有层写名单(不限,与引擎外的参照实现「名单未设 ⇒ 不限」同读)。今天唯一的层来源是请求的 `settings`(`task-settings.ts` 的 `httpHookAllowlist`,源 `flagSettings`)。
|
|
521
|
+
*/
|
|
522
|
+
httpHookAllowlistLayers: readonly HttpHookAllowlistLayer[];
|
|
517
523
|
}
|
|
518
524
|
/** 阶段三b 模型调用载体契约(prompt=直连一次 completion;agent=runTask 收敛后的 final text)。
|
|
519
525
|
* 返回 ok/text 或 ok:false/error——载体自己兜超时(timeoutMs 是硬顶,载体可更严),绝不 throw。 */
|
|
@@ -4,6 +4,7 @@ import vm from "node:vm";
|
|
|
4
4
|
import { hostShell, resolveHostShell, adoptHostForegroundChild, killTreeSoft, killTreeHard } from "../plugins/host-platform.js";
|
|
5
5
|
import { classifyHooksConfig, DEFAULT_HOOK_TIMEOUT_SECONDS, HOOK_ENTRY_TYPES, HOOK_EVENTS, HOOK_EVENT_OWNER, HOOK_EVENT_MATCHER_FIELD, hookMatcherForm, hookMatcherSelects, interpolateHookEnvVars, } from "@sema-agent/settings-schema/hooks";
|
|
6
6
|
import { findUrlCredential, formatConfigPath, outgoingPath } from "@sema-agent/settings-schema";
|
|
7
|
+
import { httpHookEnvVarsAllowed, httpHookUrlVerdict } from "@sema-agent/settings-schema";
|
|
7
8
|
import { ENGINE_OWNED_HOOK_EVENTS } from "@sema-agent/settings-schema/hooks";
|
|
8
9
|
import { describeToolCatalog } from "@sema-agent/core";
|
|
9
10
|
import { hangClassic } from "./classic-stations.js";
|
|
@@ -511,6 +512,7 @@ export function hookIfMatches(cond, payload) {
|
|
|
511
512
|
return "unevaluable";
|
|
512
513
|
}
|
|
513
514
|
}
|
|
515
|
+
const SERVER_WORKSPACE_TRUST = "no_trust_step";
|
|
514
516
|
const MAX_LLM_ENTRIES_PER_EVENT = 4;
|
|
515
517
|
const DEFAULT_PROMPT_HOOK_TIMEOUT_SECONDS = 30;
|
|
516
518
|
const DEFAULT_AGENT_HOOK_TIMEOUT_SECONDS = 120;
|
|
@@ -996,8 +998,29 @@ async function runLlmHook(entry, payload, ctx, extra) {
|
|
|
996
998
|
clearTimeout(hardTopTimer);
|
|
997
999
|
}
|
|
998
1000
|
}
|
|
1001
|
+
function httpHookWithheldSentence(event, block) {
|
|
1002
|
+
const field = `settings.${block.list}`;
|
|
1003
|
+
const [why, exit] = (() => {
|
|
1004
|
+
switch (block.rule) {
|
|
1005
|
+
case "url":
|
|
1006
|
+
return [`its url matches no pattern in ${field}`, `add a pattern that matches the url to ${field}, or point the hook at a url the list allows`];
|
|
1007
|
+
case "empty":
|
|
1008
|
+
return [`${field} is an empty list, so no http hook may run`, `add a pattern that matches the url to ${field}`];
|
|
1009
|
+
case "malformed":
|
|
1010
|
+
return [`${field} is not an array of strings, so it is read as allowing no url`, `write ${field} as an array of url pattern strings`];
|
|
1011
|
+
default: {
|
|
1012
|
+
const unhandled = block.rule;
|
|
1013
|
+
throw new Error(`httpHookWithheldSentence: unhandled rule ${String(unhandled)}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
})();
|
|
1017
|
+
return `settings.hooks.${event}: http hook not sent — ${why} (rule: ${block.rule}); ${exit}`;
|
|
1018
|
+
}
|
|
999
1019
|
const MAX_BLANKED_REFERENCES_LOGGED = 16;
|
|
1000
1020
|
async function runHttpHook(entry, payload, ctx, event) {
|
|
1021
|
+
const block = httpHookUrlVerdict(entry.url, ctx.httpHookAllowlistLayers, SERVER_WORKSPACE_TRUST);
|
|
1022
|
+
if (block !== undefined)
|
|
1023
|
+
return { code: null, stdout: "", stderr: "", timedOut: false, withheld: block };
|
|
1001
1024
|
const timeoutMs = Math.min(entry.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS, MAX_HOOK_TIMEOUT_SECONDS) * 1000;
|
|
1002
1025
|
let hookChildVars;
|
|
1003
1026
|
try {
|
|
@@ -1006,7 +1029,7 @@ async function runHttpHook(entry, payload, ctx, event) {
|
|
|
1006
1029
|
catch (e) {
|
|
1007
1030
|
return { code: null, stdout: "", stderr: "", timedOut: false, spawnError: String(e) };
|
|
1008
1031
|
}
|
|
1009
|
-
const interpolated = interpolateHookEnvVars(entry.headers ?? {}, entry.allowedEnvVars ?? [], hookChildVars);
|
|
1032
|
+
const interpolated = interpolateHookEnvVars(entry.headers ?? {}, httpHookEnvVarsAllowed(entry.allowedEnvVars ?? [], ctx.httpHookAllowlistLayers, SERVER_WORKSPACE_TRUST), hookChildVars);
|
|
1010
1033
|
if (interpolated.blanked.length > 0) {
|
|
1011
1034
|
ctx.logger.warn("hook_http_header_env_blanked", {
|
|
1012
1035
|
event,
|
|
@@ -1210,6 +1233,12 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1210
1233
|
ctx.logger.warn("hook_command_killed", { event, type: entry.type, killed: true, signal: run.aborted.signal });
|
|
1211
1234
|
return { kind: "halted" };
|
|
1212
1235
|
}
|
|
1236
|
+
if (run.withheld !== undefined) {
|
|
1237
|
+
const { list, rule, sources } = run.withheld;
|
|
1238
|
+
ctx.logger.warn("hook_http_withheld", { event, url: entry.type === "http" ? buildDisplaySafeUrl(entry.url) : null, list, rule, sources: [...sources] });
|
|
1239
|
+
noteHookFailure(ctx, { event, entry, reason: "url_not_allowed", detail: httpHookWithheldSentence(event, run.withheld), ...(toolName !== undefined ? { toolName } : {}) });
|
|
1240
|
+
return { kind: "unrun", reason: "url_not_allowed" };
|
|
1241
|
+
}
|
|
1213
1242
|
if (run.spawnError) {
|
|
1214
1243
|
ctx.logger.warn(entry.type === "http" ? "hook_http_request_failed" : entry.type === "prompt" || entry.type === "agent" ? "hook_llm_failed" : "hook_command_spawn_failed", { event, type: entry.type, error: seatText("operator-log-error", run.spawnError) });
|
|
1215
1244
|
if (!run.noticed)
|
package/dist/hosted-posture.d.ts
CHANGED
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
* `docs/DEPLOY-PREREQS.md` 的「托管 / 多租部署」段把它写成部署前置,并列出忘设它的后果。
|
|
66
66
|
*/
|
|
67
67
|
import { type ServiceConfigFlat } from "./config-types.js";
|
|
68
|
+
import { type PermissionMode } from "@sema-agent/core";
|
|
68
69
|
/** 托管/多租的**证据词表**(闭集;加词 ⇒ {@link hostedEvidenceNote} 的穷举 switch 编译红)。
|
|
69
70
|
* 🔴 恰一词是裁定([ref] ①),不是暂时的:加回一条推断臂 = 翻案,先要新裁定。 */
|
|
70
71
|
export declare const HOSTED_EVIDENCE_WORDS: readonly ["require-principal"];
|
|
@@ -123,6 +124,13 @@ export type TaskHostPostureScope = HostedPostureInput & Readonly<Pick<ServiceCon
|
|
|
123
124
|
* 两条腿同射程、不同处置,射程只有一处答案。
|
|
124
125
|
*/
|
|
125
126
|
export declare function taskWriteFaceRefusal(c: TaskWriteFaceInput): string | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* **腿② 的词形入口** —— 一个模式词在这台部署上会不会开出它掀不开的写围栏:词的模式列开写面(引擎模式表本尊,
|
|
129
|
+
* `modePolicyOf(word).writeFace === "open"`,今天只有 `bypassPermissions`)∧ {@link taskWriteFaceRefusal} 答拒 ⇒ 拒因;否则 `undefined`。
|
|
130
|
+
* 读者两个,同是受理判官的托管一步(`permission-mode-admission.ts` 顶注「两口的调用形」):在飞 run 的切档口经判官本体;提交口与续跑重放存储体经装配期那一判
|
|
131
|
+
* (`boot/resolve-spec.ts`,问盖过章的词)。哪天 core 让第二个模式也开写面,两处当场跟上,不是再手抄一个模式词。
|
|
132
|
+
*/
|
|
133
|
+
export declare function permissionModeWriteFaceRefusal(word: PermissionMode | undefined, c: TaskWriteFaceInput): string | undefined;
|
|
126
134
|
/**
|
|
127
135
|
* **腿④** —— 这条请求要让 host shell 继承整份用户环境(`inheritEnv:"all"`),这台部署给不给?返回拒因;`undefined` = 放行。
|
|
128
136
|
* 调用方只在值为 `"all"` 时问(`"scrub"` 就是缺省,任何部署都受理)。
|
package/dist/hosted-posture.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
|
|
2
2
|
import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
|
|
3
|
+
import { modePolicyOf } from "@sema-agent/core";
|
|
3
4
|
export const HOSTED_EVIDENCE_WORDS = ["require-principal"];
|
|
4
5
|
export function buildHostedPosture(c) {
|
|
5
6
|
const evidence = [];
|
|
@@ -41,6 +42,9 @@ export function taskWriteFaceRefusal(c) {
|
|
|
41
42
|
`Refused because this deployment is HOSTED/multi-tenant (${hostedBecause(facts)}), so the task layer may only TIGHTEN the write face, never open it. ` +
|
|
42
43
|
`Set TASK_WRITE_FACE_ESCALATION=allow if the DEPLOYMENT permits the task layer to open it, or omit permissionMode:"bypassPermissions".`);
|
|
43
44
|
}
|
|
45
|
+
export function permissionModeWriteFaceRefusal(word, c) {
|
|
46
|
+
return word !== undefined && modePolicyOf(word).writeFace === "open" ? taskWriteFaceRefusal(c) : undefined;
|
|
47
|
+
}
|
|
44
48
|
export function taskInheritEnvRefusal(c) {
|
|
45
49
|
if (DEPLOYMENT_SHAPE_PREDICATES.taskHostPostureDeclarations.holds(c))
|
|
46
50
|
return undefined;
|
package/dist/http/admission.js
CHANGED
|
@@ -2,11 +2,11 @@ import { APPROVAL_REACH, IMAGE_ADMISSION, isApprovalReach, isPermissionMode, isT
|
|
|
2
2
|
import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../model-select.js";
|
|
3
3
|
import { echoModelRef, modelUnavailableMessage } from "../model-entry-refusal.js";
|
|
4
4
|
import { HttpError, sessionIdShapeIssue } from "../security.js";
|
|
5
|
-
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, parseTaskSettings, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, hooksKeyIssue,
|
|
5
|
+
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, parseTaskSettings, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, hooksKeyIssue, taskHooksHonored, taskSettingsKeyIssue } from "../task-settings.js";
|
|
6
6
|
import { tightenOnlySeatIssue } from "../tighten-only-seats.js";
|
|
7
7
|
import { approvalFrameFamiliesIssue } from "../tool-approval.js";
|
|
8
8
|
import { buildEntryModeClampNotice } from "../entry-mode-clamp-notice.js";
|
|
9
|
-
import {
|
|
9
|
+
import { stampSubmissionEntryMode } from "../permission-mode-admission.js";
|
|
10
10
|
import { taskBodyKeyIssue } from "../request-key-closure.js";
|
|
11
11
|
import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, fileHistoryRestoreKeysInBody, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
12
12
|
import * as facts from "../capability-facts.js";
|
|
@@ -738,7 +738,7 @@ export function createAdmission(ctx) {
|
|
|
738
738
|
return null;
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
|
-
const { body: admitted, entry: entryMode } =
|
|
741
|
+
const { body: admitted, entry: entryMode } = stampSubmissionEntryMode(body, deps.config);
|
|
742
742
|
const lease = deps.peerDesk.lease("fresh");
|
|
743
743
|
try {
|
|
744
744
|
const auth = await deps.authorize({ req, body: admitted });
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ## 律
|
|
5
5
|
* 契约(settings-schema `SAFETY_MERGE_SPEC.permissions` 的成员阶梯)按**层的信任档**准入每个成员:`allow` 放宽免问面(`loosen`),要 `global`;`deny` / `ask` 只收紧,
|
|
6
|
-
* 文件层最低档 `local`
|
|
7
|
-
*
|
|
6
|
+
* 文件层最低档 `local` 就收。准入不读层:提交体是合并后的结果,层只在合并端(cli 本地)存在;deny / ask 条目可以带出处层(`{ rule, layer }`),但那个层只选这条规则的
|
|
7
|
+
* `/` 读在哪个根(`permission-rule-list.ts` 的 `layerRuleRoot`),自报的层不可验、**不进本判**。所以本服务准入的「层」= **这次请求验过的凭据**:档只由部署形 + 身份来源
|
|
8
|
+
* + 登录令牌的 scope 角色推,调用方说什么(含条目上的层)都不改档。
|
|
8
9
|
*
|
|
9
10
|
* · 单用户 turnkey(`requirePrincipal !== true`,本仓 posture 判别式同一只)⇒ `global`:一台机一位超管;
|
|
10
11
|
* · 多租 · 部署的 service 凭据 ⇒ `global`:部署自己的凭据是运维档(契约给「启动方自己的行为」—— `SETTINGS_SOURCE_CONTRACT.flagSettings` —— 的档),持它的前端为自己转发的内容负责;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { domainWriteRole, isAtLeast, isRole, memberAdmission, membersOf, roleAtLeast, USER_ROLES, } from "@sema-agent/settings-schema";
|
|
2
2
|
import { entryProblem, entryProblemsFacts } from "../entry-problems.js";
|
|
3
3
|
import { ssoVerifiedRole } from "../security.js";
|
|
4
|
+
import { ruleTextOf } from "../permission-rule-list.js";
|
|
4
5
|
import { verifiedIdentitySource } from "./principal-gate.js";
|
|
5
6
|
export function callerTrust(req, config) {
|
|
6
7
|
if (config.requirePrincipal !== true)
|
|
@@ -34,7 +35,7 @@ function effectiveRuleTables(t) {
|
|
|
34
35
|
for (const member of membersOf("permissions")) {
|
|
35
36
|
const entries = t.permissions[member];
|
|
36
37
|
if (entries !== undefined)
|
|
37
|
-
out.push({ member, field: `settings.permissions.${member}`, entries, source: "settings" });
|
|
38
|
+
out.push({ member, field: `settings.permissions.${member}`, entries: entries.map(ruleTextOf), source: "settings" });
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
if (t.skills !== undefined && t.userInvokedSkills !== undefined) {
|
package/dist/http/dispatch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { setSsoPrincipal, setSsoScope, setSsoRole, isDestructiveSessionWrite, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../security.js";
|
|
2
|
+
import { setSsoPrincipal, setSsoScope, setSsoRole, setSsoPrincipalInc, isDestructiveSessionWrite, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../security.js";
|
|
3
3
|
import { publicStoreProbeError } from "../store-live-probe.js";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
5
|
import { admitBridgeIdentity } from "../auth-bridge.js";
|
|
@@ -138,6 +138,10 @@ export function createDispatch(ctx) {
|
|
|
138
138
|
...(typeof deps.capabilities?.version === "string" ? { version: deps.capabilities.version } : {}),
|
|
139
139
|
...healthDrainingKeys(signal),
|
|
140
140
|
...(deps.drainState?.inflight ? { inflight: deps.drainState.inflight() } : {}),
|
|
141
|
+
...(() => {
|
|
142
|
+
const lease = deps.drainState?.engineLease?.();
|
|
143
|
+
return lease ? { engineLease: { lingerMs: lease.lingerMs, tickMs: lease.tickMs } } : {};
|
|
144
|
+
})(),
|
|
141
145
|
...(deps.storePosture?.degradedReason !== undefined ? { durable: false, storeDegraded: true } : {}),
|
|
142
146
|
...healthReadinessKeys(signal.verdict),
|
|
143
147
|
model: deps.config.model.id,
|
|
@@ -223,6 +227,8 @@ export function createDispatch(ctx) {
|
|
|
223
227
|
setSsoScope(req, identity.scope);
|
|
224
228
|
if (identity.role)
|
|
225
229
|
setSsoRole(req, identity.role);
|
|
230
|
+
if (identity.principalInc !== undefined)
|
|
231
|
+
setSsoPrincipalInc(req, identity.principalInc);
|
|
226
232
|
deps.logger?.info?.("auth_bridge_principal_accepted", { principal: identity.principal, scope: identity.scope });
|
|
227
233
|
}
|
|
228
234
|
}
|
|
@@ -232,7 +238,7 @@ export function createDispatch(ctx) {
|
|
|
232
238
|
if (await dispatchSegment(PUBLIC_ROUTES, req, res, url, search, method, allow.pre, ctx))
|
|
233
239
|
return;
|
|
234
240
|
const identitySource = verifiedIdentitySource(req, deps.config);
|
|
235
|
-
admitRunIdentity(req, identitySource);
|
|
241
|
+
admitRunIdentity(req, identitySource, deps.logger);
|
|
236
242
|
let source = null;
|
|
237
243
|
const anyServiceAuth = hasServiceAuth(deps.config);
|
|
238
244
|
if (anyServiceAuth) {
|
|
@@ -85,8 +85,8 @@ export type ExecutionContext = {
|
|
|
85
85
|
export type RootedExecutionContext = Extract<ExecutionContext, {
|
|
86
86
|
readonly root: string;
|
|
87
87
|
}>;
|
|
88
|
-
/** 装配器(`resolveSpec`)
|
|
89
|
-
export type ExecutionRoot = Pick<RootedExecutionContext, "kind" | "root">;
|
|
88
|
+
/** 装配器(`resolveSpec`)收的那一半:根、入口形与解析器选定的车道(它不判放置、不选车道 —— `lane` 是解析器那一次的读数,装配器只读:项目包的车道门)。 */
|
|
89
|
+
export type ExecutionRoot = Pick<RootedExecutionContext, "kind" | "root" | "lane">;
|
|
90
90
|
/** 解析器读的部署件(全部取自同一只 `ServiceDeps`)。 */
|
|
91
91
|
export interface ExecutionContextDeps {
|
|
92
92
|
readonly config: ServiceConfig;
|
|
@@ -117,7 +117,7 @@ export declare function inflightKeyOf(idem: RunIdemBinding, face: SubmitReplayFa
|
|
|
117
117
|
* 再走 `/v1/tasks`,合流到的是前者的那份结局。回执若铸在同步腿的体里,这条**确实产了帧**的 run 在非流式面上
|
|
118
118
|
* 就成了「键缺席」,而 present-iff 的读法会把它读成「这台引擎不给回执」。挂在 outcome 上 = 它是**这条 run 的
|
|
119
119
|
* 属性**(哪条腿跑的都一样),由各腿自己的应答构造器在送出那一刻按自己的 wire 形挂。
|
|
120
|
-
*
|
|
120
|
+
* 落定之后的同键重放不走这里:它读行,体是首答的信封 + `replay: true`(`http/submit-replay.ts` → `http/sync-submit-body.ts`)—— 回执是起手那一帧
|
|
121
121
|
* 的活捕获、不落行,所以重放体上**不带** `wiringManifest`(`replay: true` 就是那一形的判别位)。
|
|
122
122
|
*
|
|
123
123
|
* 🔴 **租户面安全(别在复审里当成漏)**:合流的那份回执是按**第一位调用方**的 `explicitOperatorOk` 投的,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /v1/tasks/stream`(活流腿:新提交、同键重放、在飞合流的跟随方)的**唯一**帧写口。
|
|
3
|
+
*
|
|
4
|
+
* 律(一条):这条流上的每一帧都是 `[id: <eventId>\n]event: <type>\ndata: <json>\n\n`,事件名 = 载荷的 `type`。
|
|
5
|
+
* 心跳是同一只写口写的 `event: heartbeat` + `data: {}`。与 durable 尾(`http/sse-log.ts`)、sdk 的 `toSseResponse` 中继同形。
|
|
6
|
+
*
|
|
7
|
+
* `id:` 行(契约 §0.6):载荷带非空 string `eventId` 才发,值逐字 = `eventId`;没有就不发,EventSource 沿用上一游标 ——
|
|
8
|
+
* 绝不合成占位 id(那会把不可锚的位置伪装成可锚)。`id:` 行是**每帧唯一**的续流游标,所以载荷的 `eventId` 必须是**本帧
|
|
9
|
+
* 自己的**身份(server 自铸帧的唯一铸点:`trace/model-segment.ts` 的 `reasoningSegmentEventId`)。三道门各管一段:
|
|
10
|
+
* {@link LiveFrame} 管「铸点说得出这两个键的型」;本函数的运行期判据管「真的是」(通体 `Record<string, unknown>` 的
|
|
11
|
+
* 构造器返回值进得来 —— `null` 读 `.length` 会把整条流摔掉、数组会被强转成一枚编出来的游标);`test/live-sse-cursor.test.ts`
|
|
12
|
+
* 的规则式全流判据管「同一条流内不重复」。
|
|
13
|
+
*/
|
|
14
|
+
import type { ServerResponse } from "node:http";
|
|
15
|
+
/** 活流帧的载荷型:`type` 是事件名(必填,型说得出口);`eventId` 在场即**本帧自己的**续流游标。其余键由各自铸点白名单。 */
|
|
16
|
+
export type LiveFrame = {
|
|
17
|
+
readonly type: string;
|
|
18
|
+
readonly eventId?: string;
|
|
19
|
+
} & Record<string, unknown>;
|
|
20
|
+
/** 写一帧。`type` 不是非空串 = 铸点违约(型门之外漏进来的通体记录),响亮抛 —— 写一行编出来的事件名是撒谎,不写事件名就回到两只形。 */
|
|
21
|
+
export declare function writeLiveFrame(res: ServerResponse, payload: LiveFrame): void;
|
|
22
|
+
/** 心跳:真帧,不是 SSE 注释(逐帧解析的 BFF 会丢注释行,下游看到的是一段零帧的窗)。没有监听心跳的 EventSource 客户端忽略它。 */
|
|
23
|
+
export declare function writeLiveHeartbeat(res: ServerResponse): void;
|
|
24
|
+
//# sourceMappingURL=live-frame.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { liveToolEndForBacklog } from "../trace/tool-output-media.js";
|
|
2
|
+
function frameBytes(event, data, anchor) {
|
|
3
|
+
const idLine = typeof anchor === "string" && anchor.length > 0 ? `id: ${anchor}\n` : "";
|
|
4
|
+
return `${idLine}event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
5
|
+
}
|
|
6
|
+
export function writeLiveFrame(res, payload) {
|
|
7
|
+
const name = payload.type;
|
|
8
|
+
if (typeof name !== "string" || name.length === 0)
|
|
9
|
+
throw new TypeError(`live SSE frame without a string type: ${JSON.stringify(name)}`);
|
|
10
|
+
const frame = name === "tool_end" ? liveToolEndForBacklog(payload, res.writableLength) : payload;
|
|
11
|
+
res.write(frameBytes(name, frame, payload.eventId));
|
|
12
|
+
}
|
|
13
|
+
export function writeLiveHeartbeat(res) {
|
|
14
|
+
res.write(frameBytes("heartbeat", {}, undefined));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=live-frame.js.map
|
|
@@ -2,6 +2,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
2
2
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
3
|
import { type DurableOwnerKey, type Principal, type SessionCaller, type SessionOwnerCell } from "../security.js";
|
|
4
4
|
import { type IdentitySource, type RunIdentity } from "../identity-source.js";
|
|
5
|
+
import type { Logger } from "../observability/logger.js";
|
|
5
6
|
/** Coerce a possibly-repeated request header to a single string (or undefined). */
|
|
6
7
|
export declare function headerStr(v: string | string[] | undefined): string | undefined;
|
|
7
8
|
export declare function safeEqual(a: string, b: string): boolean;
|
|
@@ -28,8 +29,9 @@ export declare function verifiedIdentitySource(req: IncomingMessage, config: Ser
|
|
|
28
29
|
/**
|
|
29
30
|
* 🔴 **run 行身份元组的唯一铸点**(律见 `identity-source.ts` 头注;唯一调用点 = `dispatch.ts` 的凭据门,机器门钉着)。`source` = 凭据门那一次
|
|
30
31
|
* {@link verifiedIdentitySource} 的答案 —— 这里不再判来源,只把来源连同它的 scope / 化身号定成元组,挂在这个请求上;建 run 行的点经 {@link runIdentityOf} 读它。
|
|
32
|
+
* `logger` 收律 4 的告警行(令牌声明装不进列;凭据门交 `deps.logger`)。
|
|
31
33
|
*/
|
|
32
|
-
export declare function admitRunIdentity(req: IncomingMessage, source: IdentitySource | undefined): void;
|
|
34
|
+
export declare function admitRunIdentity(req: IncomingMessage, source: IdentitySource | undefined, logger: Pick<Logger, "warn"> | undefined): void;
|
|
33
35
|
/**
|
|
34
36
|
* 建 run 行时写进行里的身份元组 —— 每个 `createRun` 调用点(或替它建行的腿的调用方)读这一只,原样写入(机器门:`src/http/**` 每处 `createRun` 都写明 `identity`)。
|
|
35
37
|
* 只在**受理请求验过的主体就是这一行的属主**时交出凭据门铸下的元组(提交面恒成立:行的属主就是这个主体);别人替属主按下的那一跑(唤醒、决定投递铸的
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
|
|
3
|
-
import { PRINCIPAL_TOKEN_HEADER, verifiedPrincipal, ssoVerifiedPrincipal, ssoVerifiedScope, ownerlessRowOpen, mintPrincipal } from "../security.js";
|
|
4
|
-
import { IDENTITY_SOURCES,
|
|
3
|
+
import { PRINCIPAL_TOKEN_HEADER, verifiedPrincipal, ssoVerifiedPrincipal, ssoVerifiedPrincipalInc, ssoVerifiedScope, ownerlessRowOpen, mintPrincipal } from "../security.js";
|
|
4
|
+
import { IDENTITY_SOURCES, runIdentityClaimOverflow } from "../identity-source.js";
|
|
5
5
|
import { sendError } from "./send.js";
|
|
6
6
|
export function headerStr(v) {
|
|
7
7
|
return Array.isArray(v) ? v[0] : v;
|
|
@@ -60,19 +60,28 @@ export function verifiedIdentitySource(req, config) {
|
|
|
60
60
|
return IDENTITY_SOURCES.find((kind) => identitySourceVerified(kind, req, config));
|
|
61
61
|
}
|
|
62
62
|
const ADMITTED_RUN_IDENTITY = new WeakMap();
|
|
63
|
-
export function admitRunIdentity(req, source) {
|
|
64
|
-
ADMITTED_RUN_IDENTITY.set(req, mintRunIdentity(req, source));
|
|
65
|
-
}
|
|
66
|
-
function mintRunIdentity(req, source) {
|
|
63
|
+
export function admitRunIdentity(req, source, logger) {
|
|
64
|
+
ADMITTED_RUN_IDENTITY.set(req, mintRunIdentity(req, source, logger));
|
|
65
|
+
}
|
|
66
|
+
function mintRunIdentity(req, source, logger) {
|
|
67
|
+
const fits = (claim, value) => {
|
|
68
|
+
const overflow = runIdentityClaimOverflow(claim, value);
|
|
69
|
+
if (overflow !== undefined)
|
|
70
|
+
logger?.warn("run_identity_claim_unrecorded", { claim: overflow.claim, chars: overflow.chars, max: overflow.max });
|
|
71
|
+
return overflow === undefined;
|
|
72
|
+
};
|
|
67
73
|
switch (source) {
|
|
68
74
|
case "service_token":
|
|
69
75
|
case "direct_door_jwt":
|
|
70
76
|
return { source, scope: null, principalInc: null };
|
|
71
77
|
case "sso": {
|
|
72
78
|
const scope = ssoVerifiedScope(req);
|
|
73
|
-
if (scope === undefined
|
|
79
|
+
if (scope === undefined)
|
|
80
|
+
return null;
|
|
81
|
+
if (!fits("scope", scope))
|
|
74
82
|
return null;
|
|
75
|
-
|
|
83
|
+
const principalInc = ssoVerifiedPrincipalInc(req);
|
|
84
|
+
return { source, scope, principalInc: principalInc !== undefined && fits("principalInc", principalInc) ? principalInc : null };
|
|
76
85
|
}
|
|
77
86
|
case undefined:
|
|
78
87
|
return null;
|