@sema-agent/server 7.136.0 → 7.138.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 +501 -139
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +149 -34
- package/deploy/sema-up/README.md +14 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +21 -14
- package/dist/approval-card.js +1 -0
- package/dist/approval-content-kind.d.ts +6 -0
- package/dist/approval-content-kind.js +7 -0
- 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/audit.js +1 -0
- package/dist/auth-bridge.js +2 -2
- package/dist/background-agent-name.d.ts +5 -0
- package/dist/background-agent-name.js +5 -0
- package/dist/boot/budget-tracing.d.ts +1 -12
- package/dist/boot/deployment-mods.d.ts +46 -16
- package/dist/boot/deployment-mods.js +82 -6
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/governance-seams.d.ts +10 -17
- package/dist/boot/governance-seams.js +5 -17
- package/dist/boot/leader.d.ts +3 -0
- package/dist/boot/leader.js +1 -0
- package/dist/boot/org-delivery.js +15 -2
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +7 -0
- package/dist/boot/resolve-spec.js +36 -26
- package/dist/boot/retention-lane.d.ts +11 -5
- package/dist/boot/retention-lane.js +2 -2
- package/dist/boot/runner-deps.d.ts +7 -3
- package/dist/boot/runner-deps.js +1 -0
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -13
- package/dist/boot/stage-05-execution-env.d.ts +6 -13
- package/dist/boot/stage-05-execution-env.js +17 -5
- package/dist/boot/stage-06-runners.d.ts +6 -13
- package/dist/boot/stage-06-runners.js +10 -3
- package/dist/boot/stage-07-capability-layer.d.ts +7 -13
- package/dist/boot/stage-07-capability-layer.js +3 -3
- package/dist/boot/stage-08-reapers.d.ts +7 -13
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +7 -13
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +7 -13
- package/dist/boot/stage-10-http-server.js +6 -3
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/web-admin.d.ts +5 -2
- package/dist/boot/web-admin.js +3 -3
- package/dist/brain.js +5 -5
- 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 +17 -15
- package/dist/budget.js +2 -2
- 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 +36 -11
- package/dist/capabilities/mcp-probe.js +44 -5
- 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 +6 -0
- package/dist/capability-facts.js +5 -0
- package/dist/center-credential-renewer.js +5 -1
- 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 +13 -6
- package/dist/config-catalog.js +16 -7
- package/dist/config-types.d.ts +24 -7
- package/dist/config.d.ts +1 -1
- package/dist/config.js +42 -13
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +6 -6
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/file-history-disclosure.d.ts +4 -2
- package/dist/file-history-disclosure.js +2 -0
- package/dist/gate-settlement.d.ts +71 -0
- package/dist/gate-settlement.js +40 -0
- package/dist/hooks/classic-stations.d.ts +12 -54
- package/dist/hooks/classic-stations.js +1 -0
- package/dist/hooks/command-spawn.d.ts +2 -2
- package/dist/hooks/hook-runner.js +14 -14
- package/dist/http/admission.js +14 -9
- 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 +48 -7
- package/dist/http/checkpoint-ctx.js +40 -0
- package/dist/http/dispatch.d.ts +4 -0
- package/dist/http/dispatch.js +18 -10
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +5 -3
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/memory-compliance-failure-reply.js +3 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-legs.d.ts +7 -31
- package/dist/http/resume-legs.js +185 -126
- package/dist/http/route-ctx.d.ts +38 -53
- package/dist/http/route-ctx.js +2 -5
- package/dist/http/routes/a2a-serve.js +10 -4
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +5 -1
- package/dist/http/routes/approvals-assistant.js +24 -10
- package/dist/http/routes/capabilities.js +23 -11
- package/dist/http/routes/mcp-reconnect.d.ts +3 -2
- package/dist/http/routes/mcp-reconnect.js +7 -7
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/rules.js +3 -3
- package/dist/http/routes/runs.js +13 -8
- package/dist/http/routes/session-sync.js +42 -6
- package/dist/http/routes/sessions.js +6 -3
- package/dist/http/routes/side-query.js +2 -3
- package/dist/http/routes/tasks.js +794 -780
- package/dist/http/server.d.ts +35 -25
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/wire-types.d.ts +66 -33
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/leader/wire.d.ts +7 -0
- package/dist/leader/wire.js +5 -4
- package/dist/mcp-assertion-header.d.ts +51 -40
- package/dist/mcp-assertion-header.js +10 -11
- package/dist/mcp-authorization-face.d.ts +27 -0
- package/dist/mcp-authorization-face.js +15 -0
- package/dist/memory-operator-faces.d.ts +13 -4
- package/dist/memory-operator-faces.js +1 -17
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/memory-sync-client.js +72 -57
- package/dist/memory-sync.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +6 -22
- package/dist/model-route-gap.js +7 -5
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/mods-declaration.d.ts +110 -0
- package/dist/mods-declaration.js +109 -0
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/observability/err-text.d.ts +5 -0
- package/dist/observability/err-text.js +9 -0
- package/dist/observability/fail-open.d.ts +17 -1
- package/dist/observability/fail-open.js +17 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- 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 +15 -2
- package/dist/parked-decide.js +17 -10
- package/dist/peer-directory.js +2 -0
- package/dist/peer-seat.d.ts +150 -0
- package/dist/peer-seat.js +239 -0
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/permission-rule-vocab.d.ts +7 -7
- package/dist/permission-rule-vocab.js +3 -3
- package/dist/plain-string-record.d.ts +38 -0
- package/dist/plain-string-record.js +38 -0
- package/dist/plugins/background-agent-store-sql.js +6 -3
- package/dist/plugins/caching-session-store.d.ts +5 -0
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +34 -29
- package/dist/plugins/checkpoint-store-sql.js +14 -14
- package/dist/plugins/file-history-store-sql.js +10 -16
- 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 +106 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +4 -17
- package/dist/plugins/local-checkpoint-store.js +3 -4
- package/dist/plugins/local-session-store.d.ts +66 -27
- package/dist/plugins/local-session-store.js +60 -28
- package/dist/plugins/mailbox-store-sql.d.ts +11 -3
- package/dist/plugins/mailbox-store-sql.js +103 -15
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
- package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
- package/dist/plugins/one-time-migrations.d.ts +34 -12
- package/dist/plugins/one-time-migrations.js +51 -9
- 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 +37 -30
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.d.ts +10 -2
- package/dist/plugins/pg-session-storage.js +20 -7
- package/dist/plugins/remote-env-adb.d.ts +6 -1
- package/dist/plugins/remote-env-adb.js +2 -1
- package/dist/plugins/remote-env-device.d.ts +6 -1
- package/dist/plugins/remote-env-device.js +5 -1
- package/dist/plugins/remote-env-e2b.d.ts +5 -0
- package/dist/plugins/remote-env-e2b.js +2 -1
- 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 +10 -2
- package/dist/plugins/remote-env-host.js +8 -2
- package/dist/plugins/remote-env-k8s.d.ts +140 -11
- package/dist/plugins/remote-env-k8s.js +277 -15
- package/dist/plugins/remote-env-local-docker.d.ts +5 -0
- package/dist/plugins/remote-env-local-docker.js +2 -1
- package/dist/plugins/remote-env-ssh.d.ts +6 -1
- package/dist/plugins/remote-env-ssh.js +2 -1
- package/dist/plugins/retention-store-sql.d.ts +14 -2
- package/dist/plugins/retention-store-sql.js +6 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/session-fork.d.ts +10 -13
- package/dist/plugins/session-fork.js +5 -25
- package/dist/plugins/session-ownership.d.ts +24 -3
- package/dist/plugins/session-ownership.js +24 -5
- package/dist/plugins/session-workspace-cell.d.ts +17 -0
- package/dist/plugins/session-workspace-cell.js +38 -0
- package/dist/plugins/sql-errors.d.ts +23 -2
- package/dist/plugins/sql-errors.js +15 -0
- package/dist/plugins/store-backend.js +7 -7
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +2 -2
- package/dist/plugins/store-fault.js +4 -2
- package/dist/plugins/tidb-pool.js +13 -0
- package/dist/plugins/tidb-session-store.d.ts +17 -2
- package/dist/plugins/tidb-session-store.js +18 -7
- package/dist/plugins/tool-result-store-sql.d.ts +13 -6
- package/dist/plugins/tool-result-store-sql.js +8 -7
- package/dist/plugins/worktree-isolation.d.ts +3 -2
- package/dist/plugins/worktree-isolation.js +1 -7
- package/dist/project-identity.d.ts +1 -14
- package/dist/project-identity.js +0 -4
- package/dist/request-key-closure.js +1 -1
- 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 +12 -7
- package/dist/run-local.d.ts +2 -2
- package/dist/run-local.js +12 -6
- package/dist/runs.d.ts +7 -1
- package/dist/runs.js +8 -2
- 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/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +12 -3
- package/dist/server-secret-env.js +18 -2
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +4 -5
- package/dist/session-incarnation.js +2 -2
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-sync.d.ts +1 -1
- package/dist/session-workspace.d.ts +130 -0
- package/dist/session-workspace.js +83 -0
- package/dist/spec-fields.d.ts +44 -14
- package/dist/spec-fields.js +33 -12
- package/dist/standing-answer-grant.d.ts +2 -13
- package/dist/standing-answer-grant.js +0 -7
- 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 +44 -14
- package/dist/task-a2a.js +46 -20
- package/dist/task-mcp.d.ts +34 -21
- package/dist/task-mcp.js +46 -22
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +43 -9
- package/dist/tool-approval.js +57 -15
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +34 -19
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +4 -0
- package/dist/trace/exit-seat.d.ts +20 -1
- package/dist/trace/exit-seat.js +5 -2
- package/dist/trace/extension-call-face.d.ts +33 -0
- package/dist/trace/extension-call-face.js +39 -0
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +62 -10
- package/dist/trace/project.js +49 -9
- package/dist/trace/redact.d.ts +18 -6
- package/dist/trace/redact.js +154 -60
- package/dist/trace/self-minted-wire.d.ts +500 -7
- package/dist/trace/self-minted-wire.js +182 -1
- package/dist/trace/wire-projection-faces.d.ts +7 -5
- package/dist/trace/wire-projection-faces.js +9 -4
- package/dist/user-authored-text.d.ts +2 -2
- package/dist/user-authored-text.js +16 -1
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +23 -4
- package/dist/web-admin-client/center-request.js +42 -8
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/skills-mcp.d.ts +8 -4
- package/dist/web-admin-client/skills-mcp.js +31 -18
- package/dist/web-admin-client/types.d.ts +10 -5
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +3 -3
- package/wire-changes/server@7.137.0.json +825 -0
- package/wire-changes/server@7.138.0.json +446 -0
|
@@ -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 (`SandboxGitConfig`, 14.7.0 — picked up: the block rides the effective
|
|
56
|
+
* wire verbatim); this reader is not wired to it yet ⇒ `null` (the plane stays off, `no_origins`). To be replaced by the package reader — 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
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
|
|
2
|
+
import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
|
|
3
|
+
import { SANDBOX_SECRET_SEAT_VERBS } from "./plugins/k8s-exec-protocol.js";
|
|
4
|
+
export const SANDBOX_GIT_PLANE_OFF_REASONS = [
|
|
5
|
+
"single_user",
|
|
6
|
+
"lane_without_boundary",
|
|
7
|
+
"no_caller_credential",
|
|
8
|
+
"center_not_https",
|
|
9
|
+
"secret_seat_unverified",
|
|
10
|
+
"secret_seat_denied",
|
|
11
|
+
"pod_patch_reaches_broker",
|
|
12
|
+
"broker_image_unset",
|
|
13
|
+
"no_origins",
|
|
14
|
+
];
|
|
15
|
+
export const SANDBOX_GIT_PLANE_OFF_TEXT = {
|
|
16
|
+
single_user: "this is a single-user deployment (REQUIRE_PRINCIPAL is not true): there are no members to keep apart",
|
|
17
|
+
lane_without_boundary: "the execution lane is not the k8s pod sandbox (REMOTE_EXEC=k8s): only a k8s sandbox pod can carry the credential broker beside the agent",
|
|
18
|
+
no_caller_credential: "this worker holds no credential the console's vault admits for credential resolution",
|
|
19
|
+
center_not_https: `the console address (${WEB_ADMIN_ENV.url}) is not https — credentials are never fetched over plain http`,
|
|
20
|
+
secret_seat_unverified: `the boot could not confirm the sandbox Role's secrets verbs for the lease Secret (${SANDBOX_SECRET_SEAT_VERBS.lease.join(" / ")}) in the sandbox namespace`,
|
|
21
|
+
secret_seat_denied: `the sandbox Role does not allow secrets ${SANDBOX_SECRET_SEAT_VERBS.lease.join(" / ")} (the lease Secret) in the sandbox namespace`,
|
|
22
|
+
pod_patch_reaches_broker: "the deployment's pod-spec patch could reach the broker container (a podSecurityContext, or a field outside the broker white-list)",
|
|
23
|
+
broker_image_unset: "K8S_SANDBOX_BROKER_IMAGE is not set (the broker container's image; this engine's own image is the recommended value)",
|
|
24
|
+
no_origins: "the effective configuration routes no git origins to the plane (sandboxGit.origins)",
|
|
25
|
+
};
|
|
26
|
+
const isHttps = (url) => url !== undefined && URL.canParse(url) && new URL(url).protocol === "https:";
|
|
27
|
+
export function judgeSandboxGitPlane(p) {
|
|
28
|
+
const off = (reason) => ({ state: "off", reason });
|
|
29
|
+
if (!p.multiTenant)
|
|
30
|
+
return off("single_user");
|
|
31
|
+
if (!p.k8sLane)
|
|
32
|
+
return off("lane_without_boundary");
|
|
33
|
+
if (!p.callerCredential)
|
|
34
|
+
return off("no_caller_credential");
|
|
35
|
+
if (!isHttps(p.centerUrl))
|
|
36
|
+
return off("center_not_https");
|
|
37
|
+
const seat = p.secretSeat;
|
|
38
|
+
if (seat === undefined)
|
|
39
|
+
return off("secret_seat_unverified");
|
|
40
|
+
switch (seat.state) {
|
|
41
|
+
case "unverified":
|
|
42
|
+
return off("secret_seat_unverified");
|
|
43
|
+
case "denied":
|
|
44
|
+
return off("secret_seat_denied");
|
|
45
|
+
case "granted":
|
|
46
|
+
break;
|
|
47
|
+
default: {
|
|
48
|
+
const unreachable = seat;
|
|
49
|
+
throw new Error(`unhandled sandbox secret seat reading: ${String(unreachable.state)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (p.patchRefusal !== undefined)
|
|
53
|
+
return off("pod_patch_reaches_broker");
|
|
54
|
+
if (p.brokerImage === undefined || p.brokerImage === "")
|
|
55
|
+
return off("broker_image_unset");
|
|
56
|
+
if (p.origins === null || p.origins.length === 0)
|
|
57
|
+
return off("no_origins");
|
|
58
|
+
return {
|
|
59
|
+
state: "on",
|
|
60
|
+
origins: p.origins.map((origin) => (origin.startsWith("https://") ? { origin, delivers: true } : { origin, delivers: false, reason: "insecure_transport" })),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function sandboxGitPlaneAsksSecretSeat(p) {
|
|
64
|
+
return p.multiTenant && p.k8sLane && p.callerCredential && isHttps(p.centerUrl);
|
|
65
|
+
}
|
|
66
|
+
export function sandboxGitPlanePremisesOf(config, readings) {
|
|
67
|
+
const lane = config.remoteExec;
|
|
68
|
+
return {
|
|
69
|
+
multiTenant: DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(config),
|
|
70
|
+
k8sLane: lane?.provider === "k8s",
|
|
71
|
+
callerCredential: readSandboxGitCallerCredential(config),
|
|
72
|
+
centerUrl: config.webAdmin?.baseUrl,
|
|
73
|
+
secretSeat: readings.secretSeat,
|
|
74
|
+
patchRefusal: readings.patchRefusal,
|
|
75
|
+
brokerImage: lane?.provider === "k8s" ? lane.brokerImage : undefined,
|
|
76
|
+
origins: readSandboxGitConfig(config)?.origins ?? null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function readSandboxGitConfig(_config) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
export function readSandboxGitCallerCredential(_config) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
export async function runIdentitySourceOf(runId, runs, logger) {
|
|
86
|
+
if (runId === undefined || runs === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
try {
|
|
89
|
+
return (await runs.getRun(runId))?.identity?.source;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
logger?.warn("sandbox_broker_identity_unread", {
|
|
93
|
+
run: runId,
|
|
94
|
+
error: e instanceof Error ? e.message : String(e),
|
|
95
|
+
effect: "this run's sandbox pod carries no member-credential broker (its identity could not be read from the run row)",
|
|
96
|
+
});
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function sandboxBrokerSeatOf(plane, brokerImage, identitySource) {
|
|
101
|
+
if (plane.state !== "on" || brokerImage === undefined || brokerImage === "" || identitySource !== "sso")
|
|
102
|
+
return undefined;
|
|
103
|
+
return { image: brokerImage, origins: plane.origins.map((o) => o.origin) };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=sandbox-git-plane.js.map
|
package/dist/security.d.ts
CHANGED
|
@@ -316,6 +316,13 @@ export declare function createAuthorizer(config: ServiceConfig, sessionStore: Ow
|
|
|
316
316
|
/** F1/F3 (codex scope-ledger audit 2026-07-14) — the auth-boundary principal SHAPE gate, applied to every
|
|
317
317
|
* verified identity source (trusted header, SSO-bridge sub, direct-door JWT sub) so no downstream consumer
|
|
318
318
|
* ever sees an ambiguous or over-wide principal:
|
|
319
|
+
* - a string that is NOT A PRINCIPAL NAME is REJECTED (7.137.0): the engine's one judge `principalNameOf` (core 7.56.0 [ref] —
|
|
320
|
+
* visible content, no control / line-or-paragraph separator / format character other than the two joiners; kept verbatim, never
|
|
321
|
+
* normalized). The session stores' first ownership write and fork and the durable rule buckets read the same judge
|
|
322
|
+
* (`plugins/session-ownership.ts`, `permission-rule-store-sql.ts`); before this arm the boundary admitted what the store then refused,
|
|
323
|
+
* and the request fell into the generic 500 with its code lost. Typical source: a proxy writing raw UTF-8 bytes into the principal
|
|
324
|
+
* header — Node reads header bytes one per character (latin1), and a CJK character's continuation bytes often land on 0x80–0x9F,
|
|
325
|
+
* the C1 controls;
|
|
319
326
|
* - RESERVED internal identifiers are REJECTED (修1, 三路复审 A 组 HIGH — see {@link RESERVED_PRINCIPALS} /
|
|
320
327
|
* {@link RESERVED_PRINCIPAL_PREFIXES} below): a REAL tenant whose principal string-equals an internal scope
|
|
321
328
|
* sentinel would pass the plain string-compare owner/scope gates downstream and merge with internal rows —
|
|
@@ -328,8 +335,8 @@ export declare function createAuthorizer(config: ServiceConfig, sessionStore: Ow
|
|
|
328
335
|
* - length > 190 is REJECTED: every scope/owner column the principal lands in VERBATIM is VARCHAR(190)
|
|
329
336
|
* (sendfile_link.scope, approval.owner/scope, memory-engine scope, rate-limit keys, …) — an over-long
|
|
330
337
|
* principal would truncate/500 at the INSERT and split one identity into two.
|
|
331
|
-
* Throws a typed 400 `HttpError` (the HTTP layer maps it)
|
|
332
|
-
* the length cap), never a secret and never the internal scope's semantics. */
|
|
338
|
+
* Throws a typed 400 `HttpError` (the HTTP layer maps it): `principal_not_a_name` / `principal_reserved` / `principal_too_long`; the message
|
|
339
|
+
* names the rule (not a name / a reserved identifier / the length cap), never a secret and never the internal scope's semantics. */
|
|
333
340
|
export declare const PRINCIPAL_MAX_LENGTH = 190;
|
|
334
341
|
/**
|
|
335
342
|
* 一个被受理的主体串最多占多少个 UTF-8 字节 —— 按**字节**计宽的身份列(TiDB `VARBINARY(n)`)的列宽**唯一来源**(派生,不手抄第二个数)。
|
|
@@ -354,8 +361,13 @@ export declare const RESERVED_PRINCIPAL_PREFIXES: readonly string[];
|
|
|
354
361
|
/** The principal-shape rule as a VERDICT (the one judge; {@link assertPrincipalShape} is its throwing reader). `undefined` = a
|
|
355
362
|
* principal may carry this string; otherwise which rule it breaks. A reader that only needs "can this string be anyone?" (the
|
|
356
363
|
* organization delivery gate's recipient: a string that breaks the rule is nobody, refused with the same bytes as an unknown
|
|
357
|
-
* principal) reads the verdict instead of catching the throw.
|
|
358
|
-
|
|
364
|
+
* principal) reads the verdict instead of catching the throw.
|
|
365
|
+
*
|
|
366
|
+
* Order: "is it a name at all" first — the other two rules presuppose a name (a reserved identifier / a name too wide for the
|
|
367
|
+
* column), and the `reserved` sentence echoes the value while the `not_a_name` sentence never does (a string that names nobody may be a
|
|
368
|
+
* pasted credential). An empty string is `not_a_name` here (the engine's judge says so); a header reader that treats a blank header as
|
|
369
|
+
* "no principal" decides that before asking. */
|
|
370
|
+
export type PrincipalShapeProblem = "not_a_name" | "reserved" | "too_long";
|
|
359
371
|
export declare function principalShapeProblem(principal: string): PrincipalShapeProblem | undefined;
|
|
360
372
|
export declare function assertPrincipalShape(principal: string): void;
|
|
361
373
|
/** The checkpoint `scope` column's single-user/anonymous sentinel — the same "_" documented above
|
package/dist/security.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { uuidv7, PROJECT_ID_REGEX } from "@sema-agent/core";
|
|
1
|
+
import { uuidv7, PROJECT_ID_REGEX, principalNameOf } from "@sema-agent/core";
|
|
2
2
|
import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
|
|
3
3
|
import { headReadOf } from "./session-incarnation.js";
|
|
4
4
|
import { memoryScopeFor } from "./memory-scope.js";
|
|
5
5
|
import { verifyPrincipalJwt } from "./principal-jwt.js";
|
|
6
6
|
import { fieldConflictFacts } from "./field-conflict.js";
|
|
7
7
|
import { ORPHAN_SESSION_OWNER_PREFIX } from "./delegated-session-owner.js";
|
|
8
|
+
import { NOT_A_PRINCIPAL_NAME } from "./plugins/session-ownership.js";
|
|
8
9
|
export function isDestructiveSessionWrite(method, url) {
|
|
9
10
|
if (method === "DELETE")
|
|
10
11
|
return /^\/v1\/sessions\/[^/]+$/.test(url);
|
|
@@ -134,6 +135,8 @@ export const PRINCIPAL_MAX_UTF8_BYTES = PRINCIPAL_MAX_LENGTH * 3;
|
|
|
134
135
|
export const RESERVED_PRINCIPALS = ["_", "__none__", "_leader"];
|
|
135
136
|
export const RESERVED_PRINCIPAL_PREFIXES = ["_leader-", ORPHAN_SESSION_OWNER_PREFIX];
|
|
136
137
|
export function principalShapeProblem(principal) {
|
|
138
|
+
if (principalNameOf(principal) === undefined)
|
|
139
|
+
return "not_a_name";
|
|
137
140
|
if (RESERVED_PRINCIPALS.includes(principal) || RESERVED_PRINCIPAL_PREFIXES.some((p) => principal.startsWith(p)))
|
|
138
141
|
return "reserved";
|
|
139
142
|
if (principal.length > PRINCIPAL_MAX_LENGTH)
|
|
@@ -145,10 +148,12 @@ export function assertPrincipalShape(principal) {
|
|
|
145
148
|
switch (problem) {
|
|
146
149
|
case undefined:
|
|
147
150
|
return;
|
|
151
|
+
case "not_a_name":
|
|
152
|
+
throw createCodedHttpError(400, "principal_not_a_name", `principal is not a principal name: got ${NOT_A_PRINCIPAL_NAME} — send the identity as visible characters only (a proxy that writes raw UTF-8 bytes into the principal header is read one byte per character, which can produce control characters: encode it upstream as ASCII or another form without them)`);
|
|
148
153
|
case "reserved":
|
|
149
|
-
throw
|
|
154
|
+
throw createCodedHttpError(400, "principal_reserved", `principal ${JSON.stringify(principal)} is reserved (an internal scope sentinel) — authenticate with a real tenant identifier`);
|
|
150
155
|
case "too_long":
|
|
151
|
-
throw
|
|
156
|
+
throw createCodedHttpError(400, "principal_too_long", `principal exceeds ${PRINCIPAL_MAX_LENGTH} characters (the scope-column width every store pins, e.g. sendfile_link.scope VARCHAR(190)); got ${principal.length}`);
|
|
152
157
|
default: {
|
|
153
158
|
const unknownProblem = problem;
|
|
154
159
|
throw new Error(`assertPrincipalShape: unknown problem ${String(unknownProblem)}`);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 剥掉的键经一个登记过的 `SecretEnvScrubSite` 留痕(现场按**腿**命名);git 腿再叠 git 自己的两件({@link gitSubprocessEnv})。射程 = **每一条**宿主腿:
|
|
14
14
|
* src 里每一只值引 `node:child_process` 的文件都是登记腿、其中每一次起子进程都显式交组合出的 env(普查门在 `test/secret-env-scrub-host-legs.test.ts`)。
|
|
15
15
|
* 腿的继承词是调用点的事实,按「子进程里跑的是谁选的代码」定:模型命令(host shell,交任务自己的词)/ 仓里的钩子与凭据助手(三条 git 腿)/ 执行仓代码的
|
|
16
|
-
* 语言服务器 / 配方脚本 ⇒ 形状剥 `"scrub"`;请求带来的 settings hooks 命令 ⇒ 严格名单(
|
|
16
|
+
* 语言服务器 / 配方脚本 ⇒ 形状剥 `"scrub"`;请求带来的 settings hooks 命令 ⇒ 严格名单(十二名基础名单,`[names]` 臂;请求的继承词只给工具 shell、对 hook 不作用)、请求的有效环境层作授予叠在继承结果之上(引擎的 `hostChildEnv`);部署方装在宿主上的 CLI(`docker` / `adb`,命令本身
|
|
17
17
|
* 在容器 / 设备里跑、那边的 env 由 `-e` 显式给)⇒ `"all"` —— 只扣本服务自有凭据,CLI 与它的凭据助手 / 插件要读的生态变量照交(形状剥会剥
|
|
18
18
|
* `ADB_VENDOR_KEYS` / `AWS_SECRET_ACCESS_KEY` 一类,实测)。三个继承词只在引擎一处判:
|
|
19
19
|
* · `"all"` = 整份环境 **减** 本进程声明的自有凭据名;`"scrub"`(缺省)= 形状剥 ∪ 该名单;`[names]` = 严格白名单,点到名单中的名 ⇒ 引擎响亮拒
|
|
@@ -94,18 +94,21 @@ export declare function ownCredentialsWithheld(): boolean;
|
|
|
94
94
|
export declare function ownCredentialValuesWithheld(): boolean;
|
|
95
95
|
/** 测试缝:撤回声明(引擎侧重声明为空表)、忘掉两份声明镜像、点名记账与活配置;生产代码从不调它。 */
|
|
96
96
|
export declare function __resetServerWithheldEnvForTest(): void;
|
|
97
|
-
/** 一条宿主腿要什么:现场(留痕)、继承策略(缺省 `"scrub"`;按头注「腿的继承词」一条由调用点定)
|
|
97
|
+
/** 一条宿主腿要什么:现场(留痕)、继承策略(缺省 `"scrub"`;按头注「腿的继承词」一条由调用点定)、部署方点名放回的键、调用方显式交的授予(按序叠在继承之上,后者赢)。 */
|
|
98
98
|
export interface HostSubprocessEnvRequest {
|
|
99
99
|
readonly site: SecretEnvScrubSite;
|
|
100
100
|
readonly policy?: InheritEnvPolicy | undefined;
|
|
101
101
|
readonly allow: readonly string[];
|
|
102
|
+
/** 调用方显式交给子进程的变量(今天只有 settings hooks:有效环境层 = 请求 `settings.env` + hook 自己的变量)。引擎判形:名字空 / 带 `=` / NUL / 孤代理项、
|
|
103
|
+
* 值不是串或带 NUL / 孤代理项 ⇒ 组合**抛**,拒句点名授予与变量(不带值、不回显坏名);调用点按「起不来」处置。 */
|
|
104
|
+
readonly grants?: readonly Readonly<Record<string, string>>[] | undefined;
|
|
102
105
|
}
|
|
103
106
|
/**
|
|
104
107
|
* 🔴 **宿主子进程 env 的唯一组合**:{@link composeHostSubprocessEnv} + 留痕。**没有按原样交给子进程**的名字(剥掉的)经 `site` 留痕;点名放回的键取回源值,
|
|
105
108
|
* 不是剥除,不计数、不披露。源 env 恒为本进程 env(引擎只读 `process.env`,本仓没有第二个源)。
|
|
106
109
|
*
|
|
107
110
|
* 九条宿主腿都只经它(直接或经 {@link gitSubprocessEnv}):host shell(三个继承词原样交给 `policy`)、project-memory / center-plugins / leader 三条 git 腿、
|
|
108
|
-
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(
|
|
111
|
+
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(十二名严格名单 + 有效环境层作授予)。`allow` 只在**有部署级出路**的现场非空(今天 = `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`,
|
|
109
112
|
* 它们的现场在 `SECRET_ENV_SCRUB_SITES` 里登记了 `remedy`,披露行因此是 warn + 放行名单 + 出路句);其余现场传 `[]`。
|
|
110
113
|
* 返回 `NodeJS.ProcessEnv` 形(值可能是 `undefined` 的键本来就不会进子进程);调用方显式要交的键(`LC_ALL`、`cfg.env` …)叠在它之上。
|
|
111
114
|
* 调用时机 = 每次要交 env 时现算一次(`process.env` 在本仓是 boot 期输入,各次同值;计数因此是「每组合一次」)。
|
|
@@ -124,6 +127,12 @@ export declare function hostSubprocessEnv(o: HostSubprocessEnvRequest): NodeJS.P
|
|
|
124
127
|
* 既不另写一份组合(第二份会漂),也不把一次没发生的 spawn 记进剥除计数。`findings` 是组合点自己的收集器(引擎把形状臂与自有凭据臂的每一条扣下都推进去)。
|
|
125
128
|
*/
|
|
126
129
|
export declare function composeHostSubprocessEnv(o: Omit<HostSubprocessEnvRequest, "site">, findings?: SecretEnvFinding[]): NodeJS.ProcessEnv;
|
|
130
|
+
/**
|
|
131
|
+
* core 7.56.0 [ref] `ExecutionEnv.withheldEnv` 的**宿主子进程**读法:{@link composeHostSubprocessEnv} 那只组合扣下的发现(名 + 规则,从不带值),
|
|
132
|
+
* 减去适配器显式授予重供的键(`regranted` 的自有键:子进程收得到那个名,不是损失)—— 引擎契约的原句「the findings of that composition less what its
|
|
133
|
+
* explicit grants re-supply」。同一个名字被两条规则各扣一次 ⇒ 按「名 + 规则」各记一笔(与披露同一去重)。只读组合:不披露、不计数(没有 spawn 发生)。
|
|
134
|
+
*/
|
|
135
|
+
export declare function withheldFromHostSubprocess(o: Omit<HostSubprocessEnvRequest, "site">, regranted: Readonly<Record<string, string>> | undefined): SecretEnvFinding[];
|
|
127
136
|
/** git 子进程腿的现场(闭集里名字带 `.git-` 的那几只 —— 编译期筛;它们都没有点名放行面)。 */
|
|
128
137
|
export type GitSubprocessSite = Extract<SecretEnvScrubSite, `${string}.git-${string}`>;
|
|
129
138
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { declareWithheldEnv, inheritedHostEnv } from "@sema-agent/core";
|
|
1
|
+
import { declareWithheldEnv, hostChildEnv, inheritedHostEnv } from "@sema-agent/core";
|
|
2
2
|
import { CONFIG_CATALOG } from "./config-catalog.js";
|
|
3
3
|
import { ENV_NAME_ALLOWLIST_KNOB_NAMES } from "./env-name-allowlist-knobs.js";
|
|
4
4
|
import { recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
|
|
@@ -84,7 +84,23 @@ export function hostSubprocessEnv(o) {
|
|
|
84
84
|
}
|
|
85
85
|
export function composeHostSubprocessEnv(o, findings = []) {
|
|
86
86
|
const putBack = o.allow.length > 0 ? inheritedHostEnv([...o.allow], findings) : {};
|
|
87
|
-
|
|
87
|
+
const child = hostChildEnv(o.policy ?? "scrub", putBack, ...(o.grants ?? []));
|
|
88
|
+
findings.push(...child.withheld);
|
|
89
|
+
return child.env;
|
|
90
|
+
}
|
|
91
|
+
export function withheldFromHostSubprocess(o, regranted) {
|
|
92
|
+
const findings = [];
|
|
93
|
+
composeHostSubprocessEnv(o, findings);
|
|
94
|
+
const withheld = [];
|
|
95
|
+
const seen = new Set();
|
|
96
|
+
for (const f of findings) {
|
|
97
|
+
const k = `${f.key}=${f.kind}`;
|
|
98
|
+
if (seen.has(k) || (regranted !== undefined && Object.hasOwn(regranted, f.key)))
|
|
99
|
+
continue;
|
|
100
|
+
seen.add(k);
|
|
101
|
+
withheld.push(f);
|
|
102
|
+
}
|
|
103
|
+
return withheld;
|
|
88
104
|
}
|
|
89
105
|
export function gitSubprocessEnv(o) {
|
|
90
106
|
return {
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ## 为什么多一个 `face` 参数(而不是单参)
|
|
12
12
|
* core 的 `invalid_session` 是**同词两义**(亲读 dist:`engine/session/import-validate.js` 的导入门拒调用方交来的
|
|
13
|
-
* 日志,`engine/session/storage-base.js` 的建树 /
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* ⇒ 面是映射的一维,写进属主,不散在消费点。
|
|
13
|
+
* 日志,`engine/session/storage-base.js` / `parent-chain-guard.js` 的建树 / 走根拒**已存**日志的结构 —— 叶子或父不在日志里、
|
|
14
|
+
* 条目是自己的祖先〔自指 / 成环〕)。前者是调用方的日志没过校验(import 面 422),后者是这只会话**存着的**日志坏了(open 面 500,
|
|
15
|
+
* 调用方没做错任何事)。单参签名装不下这件事:要么打开面的每个消费点都写一句 `code === "invalid_session"` 特判,要么把已存日志坏了
|
|
16
|
+
* 说成「你交的日志坏了」。⇒ 面是映射的一维,写进属主,不散在消费点。
|
|
18
17
|
* · `"open"` —— 为某只会话**打开它已存的日志**(提交各腿的引擎 prepare、鉴权门的归属探针、直接读会话的口);
|
|
19
18
|
* · `"import"` —— 校验 / 写入**调用方交来的**日志(2c session-sync 的暂存导入)。
|
|
20
19
|
*
|
|
@@ -24,7 +23,16 @@
|
|
|
24
23
|
* 原样上 wire,契约既有立场:不为上游码铸孪生码)。三种会话后端同词:文件店由 core 铸,两只 SQL 店由
|
|
25
24
|
* `plugins/session-event-payload.ts` 铸(坏的 `session_event.payload` 行)。
|
|
26
25
|
* · `invalid_session` —— `import` 面 **422 `invalid_entries`**(修前 session-sync 的那一行,码位 / 状态逐字不变);
|
|
27
|
-
* `open`
|
|
26
|
+
* `open` 面 **500 `invalid_session`**:这只会话已存的日志结构坏了(悬空父 / 自指 / 成环 / 叶子缺席),打得开存档、走不通分支。
|
|
27
|
+
* 句子发 core 的原句(点名条目 id,不含盘上路径),过既有脱敏。
|
|
28
|
+
* · **为什么是 500、不是 4xx / 503**:坏的是存着的数据,调用方没做错任何事;重试改不了判(修复在运维手里)—— 与 `corrupt_session` 同一档同一理由。
|
|
29
|
+
* · **为什么码是 `invalid_session`、不是 `corrupt_session`**:码不另铸、也不把一个 core 词改名成另一个 core 词。两个词说的是两件事
|
|
30
|
+
* (`corrupt_session` = 存档读不出;`invalid_session` = 读得出、结构走不通),而 run 终局上它本来就是引擎词(`terminal.code`,同步口 4xx / 5xx 体的
|
|
31
|
+
* `errorCode` 取终局码原样):读口答 `corrupt_session`、提交口答 `invalid_session` 就是同一件事两个名字。所以映射只有一格,
|
|
32
|
+
* 读口与提交口的 run 终局分诊({@link sessionErrorHttp} 的 open 面,`resumeAtHttpStatus` 读它)同一只、同码同状态。
|
|
33
|
+
* · **没有 `reason` 机读位**:core 的 `SessionError` 只带 `code` 与句子;悬空父与叶子缺席是同一句 `Entry <id> not found`,自指与成环是同一句
|
|
34
|
+
* `is its own ancestor` —— 本仓分出 {parent_missing, parent_self, parent_cycle} 只有解析句子或再写一只判官两条路(会话视图自己的走链器已在 core 7.54.0 提货时退役:
|
|
35
|
+
* 视图读的就是引擎读的那条分支,见 `plugins/local-session-window.ts` 头注)。要分,请求 core 把种类放进错误(同 `placement_kind` 的先例),不在这里猜。
|
|
28
36
|
* · `not_found` —— 留白:每一面各有自己的「不存在」答法(404 `not_found.session` / 同步口的 `resume.session_not_found` /
|
|
29
37
|
* 列表里的缺席),码位各不相同,不由本表统一。
|
|
30
38
|
* · `placement_refused` —— `import` 面 **409 `conflict.session_placed`**(机读位 `detail.placement_kind`,固定句);`open` 面留白。
|
|
@@ -3,6 +3,7 @@ export const PLACED_SESSION_SENTENCE = "this session holds a delegated agent's t
|
|
|
3
3
|
const PLACEMENT_KIND = "subagent";
|
|
4
4
|
export const STAGED_IMPORT_OWNER_CHANGED_SENTENCE = "session owner changed while the import streamed; nothing was imported — start the sync again";
|
|
5
5
|
const CORRUPT_SESSION = { status: 500, errorCode: "corrupt_session" };
|
|
6
|
+
const STORED_LOG_INVALID = { status: 500, errorCode: "invalid_session" };
|
|
6
7
|
const INVALID_ENTRIES = { status: 422, errorCode: "invalid_entries", sentence: STAGED_IMPORT_INVALID_SENTENCE };
|
|
7
8
|
const SESSION_PLACED = { status: 409, errorCode: "conflict.session_placed", sentence: PLACED_SESSION_SENTENCE, detail: { placement_kind: PLACEMENT_KIND } };
|
|
8
9
|
const STAGED_IMPORT_OWNER_CHANGED = { status: 409, errorCode: "conflict", sentence: STAGED_IMPORT_OWNER_CHANGED_SENTENCE };
|
|
@@ -11,7 +12,7 @@ export function sessionErrorHttp(code, face) {
|
|
|
11
12
|
case "corrupt_session":
|
|
12
13
|
return CORRUPT_SESSION;
|
|
13
14
|
case "invalid_session":
|
|
14
|
-
return face === "import" ? INVALID_ENTRIES :
|
|
15
|
+
return face === "import" ? INVALID_ENTRIES : STORED_LOG_INVALID;
|
|
15
16
|
case "placement_refused":
|
|
16
17
|
return face === "import" ? SESSION_PLACED : undefined;
|
|
17
18
|
case "conflict":
|
|
@@ -117,14 +117,13 @@ export declare function sessionFaceOfHead(sessionId: string, head: SessionHead |
|
|
|
117
117
|
*/
|
|
118
118
|
export declare function headReadOf(store: object | undefined): HeadRead | undefined;
|
|
119
119
|
/**
|
|
120
|
-
* session-sync
|
|
121
|
-
* (
|
|
122
|
-
* 行不在(这是一次新建)或还是空的存量形 ⇒
|
|
120
|
+
* session-sync **换装**(整段换装 / 暂存提交 / local 落在既有行上的导入)时化身的去留 —— 三只店共用的一句判据:换装只换内容,换的是**同一行**
|
|
121
|
+
* ⇒ 行在且那一格里有一只令牌(唯一解码 {@link incarnationFromCell} 的 `absent` 读法)⇒ 沿用(属主重戳与否不改这一判:同一行 = 同一位住客);
|
|
122
|
+
* 行不在(这是一次新建)或还是空的存量形 ⇒ 铸一只新的。属主不在这里判:每只店在自己的属主门上判过被换的那一行,读不中的换装一个字节不写。
|
|
123
123
|
*/
|
|
124
124
|
export declare function carriedIncarnation(sessionId: string, prior: {
|
|
125
|
-
owner?: unknown;
|
|
126
125
|
incarnation?: unknown;
|
|
127
|
-
} | undefined
|
|
126
|
+
} | undefined): string;
|
|
128
127
|
/**
|
|
129
128
|
* `session_meta.incarnation` 的列定义 —— 两方言各一句,**唯一属主**:中央 DDL(`tidb-pool.ts` / `pg-session-storage.ts` 的
|
|
130
129
|
* `CREATE TABLE session_meta`)内插这同一串;存量库缺这一列时,起服列册探针(`schema-consistency.ts`)拒启,拒句里的 `ALTER TABLE … ADD COLUMN …`
|
|
@@ -67,8 +67,8 @@ export function headReadOf(store) {
|
|
|
67
67
|
const read = store?.headOf;
|
|
68
68
|
return typeof read === "function" ? read.bind(store) : undefined;
|
|
69
69
|
}
|
|
70
|
-
export function carriedIncarnation(sessionId, prior
|
|
71
|
-
const held = prior
|
|
70
|
+
export function carriedIncarnation(sessionId, prior) {
|
|
71
|
+
const held = prior === undefined ? undefined : incarnationFromCell(sessionId, prior.incarnation, "absent");
|
|
72
72
|
return held ?? mintSessionIncarnation();
|
|
73
73
|
}
|
|
74
74
|
export const SESSION_META_INCARNATION_COLUMN = {
|
|
@@ -52,7 +52,7 @@ export declare function namespaceCoveringForms(): readonly {
|
|
|
52
52
|
}[];
|
|
53
53
|
/**
|
|
54
54
|
* 能力位 `sessionRulesNamespaceForms` 的**唯一**判据:会话规则记录里协议命名空间的覆盖形,引擎认成「一组工具」**且**写口收。
|
|
55
|
-
* 对 `PROTOCOL_TABLE` 的每一员:① 它的三只覆盖形,引擎的匹配器(`
|
|
55
|
+
* 对 `PROTOCOL_TABLE` 的每一员:① 它的三只覆盖形,引擎的匹配器(`ruleNameCovers`,会话层与其余规则面同一只)都判得出覆盖那一员的真工具名
|
|
56
56
|
* (只当精确名收的写口不算 ——「收」与「认成一组」是两问);② 三形放进 `toolAllow` 与 `toolDeny` 两桶,两条写入腿共用的写口读者
|
|
57
57
|
* {@link readWritableSessionPermissionRules}(= 引擎写口判官)不拒。表非空 ∧ 每员都过 ⇒ `true`。
|
|
58
58
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PROTOCOL_TABLE, SessionPolicyError, judgeSessionRulesWrite,
|
|
1
|
+
import { PROTOCOL_TABLE, SessionPolicyError, judgeSessionRulesWrite, ruleNameCovers } from "@sema-agent/core";
|
|
2
2
|
import { displayListEntry } from "./list-entry-display.js";
|
|
3
3
|
function isSessionRulesRefusal(e) {
|
|
4
4
|
return e instanceof SessionPolicyError && (e.code === "invalid_shape" || e.code === "inert_entry");
|
|
@@ -25,7 +25,7 @@ export function namespaceCoveringForms() {
|
|
|
25
25
|
export function sessionRulesNamespaceFormsHonored() {
|
|
26
26
|
const probes = namespaceCoveringForms();
|
|
27
27
|
return (probes.length > 0 &&
|
|
28
|
-
probes.every(({ forms, tool }) => forms.every((form) =>
|
|
28
|
+
probes.every(({ forms, tool }) => forms.every((form) => ruleNameCovers(form, tool)) && readWritableSessionPermissionRules({ toolAllow: [...forms], toolDeny: [...forms] }).refusal === undefined));
|
|
29
29
|
}
|
|
30
30
|
export function readSessionRulesRecords(raw) {
|
|
31
31
|
if (!Array.isArray(raw))
|
package/dist/session-sync.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ import type { ServiceFileHistoryStore, ServiceSessionPolicyStore, ResumeAnchorSt
|
|
|
50
50
|
/**
|
|
51
51
|
* core 7.55.0 [ref] T2([ref]):一只会话的文件历史**读不全**(撕裂 / 读不出的边界、读不出的跟踪集 —— 店的 `exportHistory` 答 `{ ok: false }`,`adopt_failed`
|
|
52
52
|
* 带店判的 `permanence`)。它**不是**「没有历史」:读成 `null` 的同步会把这只会话的全部 rewind 历史丢在路上、还答成功。会话同步的两只拉取口
|
|
53
|
-
* (manifest、history blob)对它答同一句 503 `state.file_history_unreadable`(路由 `sendPullUnreadable`;连接类故障在拉取口同码同体键答 `permanence: "transient"`)。
|
|
53
|
+
* (manifest、history blob)对它答同一句 503 `state.file_history_unreadable`(路由 `sendPullUnreadable`;连接类故障在拉取口同码同体键答 `permanence: "transient"`,部署错配类故障〔缺表 / 权限拒〕答 `permanence: "terminal"`)。
|
|
54
54
|
*/
|
|
55
55
|
export declare class FileHistoryUnreadableError extends Error {
|
|
56
56
|
readonly sessionId: string;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **会话的位置与项目** —— 一个事实、一个写者、一种读法(引擎 7.58.0:`workspace_changed` 是腿的整份快照;
|
|
3
|
+
* 会话行 = 该会话上最后一帧)。
|
|
4
|
+
*
|
|
5
|
+
* ## 律(三句)
|
|
6
|
+
* · **判官只有引擎一只**:每条腿起点一帧 `workspace_changed`(`cwd` / `root` / `entered?` / `project?` / `remote`),之后每变一帧;
|
|
7
|
+
* 这条腿属于哪个项目由引擎对检出的 `.sema/project` 记号判一次(带了 `TaskSpec.projectId` 就必须等于记号,
|
|
8
|
+
* 否则整腿拒 `config.project_identity_mismatch`;没有记号时声明就是身份)。本仓**不判**项目、不读记号。
|
|
9
|
+
* · **会话行只存一格**:`workspace` = 该会话上一条腿**自己的**最后一帧(SQL `session_meta.workspace`;local 店
|
|
10
|
+
* `session-workspaces.json` 那一格)。成员由 {@link SESSION_WORKSPACE_DISPOSITIONS} 一张表定(引擎给帧加成员 ⇒ 表 tsc 红,
|
|
11
|
+
* 门在 `trace/core-keyset-guard.ts` ⑰ 读同一张表)。**唯一写者** = 腿的记录座 {@link createSessionWorkspaceRecorder}
|
|
12
|
+
* (持流的四条腿逐帧喂同一只;委派子代转上来的帧 —— 带 `parentToolCallId` / `sourceTaskId` / `bgAgentId` —— 不是这条腿的位置,不认)。
|
|
13
|
+
* 每一笔写**在腿的流循环里等完**(不排队、没有在飞的写:腿从任何出口离开时都没有它的写还悬着 ⇒ 写恒落在这条腿的 run 认领之内,
|
|
14
|
+
* 同一会话的下一条腿不会被一笔晚到的写盖掉),并且**只写受理时见证到的那一位住客**({@link stampLegSessionWitness}:resolveSpec
|
|
15
|
+
* 受理时读会话行的化身那一读,同一只值也是委派树根的见证)—— 会话被删掉又以同一 id 重建之后,上一位住客的腿写不进新住客的行。
|
|
16
|
+
* · **每条腿声明一个值**:`TaskSpec.projectId` = 这次提交自己带的 `projectId`(新提交的请求体;续跑腿重放的是持久化的同一个体)
|
|
17
|
+
* ?? 会话行上最后一帧的 `project.id` —— **只当它是调用方声明过的**(`source: "declared"`:没有记号的检出里声明就是身份,行存它就是为了接着声明);
|
|
18
|
+
* `source: "marker"` 是引擎对**那一次**检出的记号判决,不续声明(引擎每条腿对自己的检出再判 —— 记号改写 / 换检出之后续跑照跑);
|
|
19
|
+
* 都没有 ⇒ 不带(引擎在有记号的检出里自判)。唯一铸点 {@link legProjectIdOf},唯一调用点 `boot/resolve-spec.ts`(新提交与续跑同经那一只装配)。
|
|
20
|
+
*
|
|
21
|
+
* ## 什么因此变少
|
|
22
|
+
* 修前请求键 `projectId` 被提交口受理、却从不交给引擎(keyset 守卫记该引擎座「未接线」,与受理表相反 —— 编译门 ② 红着);
|
|
23
|
+
* 会话在没有记号的检出里续跑时,项目随每一次不带 `projectId` 的提交丢掉。现在:一个值进引擎、一格存引擎的答案、下一条腿读那一格。
|
|
24
|
+
*
|
|
25
|
+
* ## 不在这里的(如实)
|
|
26
|
+
* · **记忆项目层**(单用户 `userrepo:` 键,`project-identity.ts` 的 git 检出身份)不是这个事实:它在装配期(任何帧之前)交给
|
|
27
|
+
* `TaskSpec.memory.projectKey`,按 git 仓库(origin 远端 / 本机路径)键控,与 `.sema/project` 记号无关;引擎 7.58.0 自己也写明
|
|
28
|
+
* 「只声明的身份不改记忆的键」。两者是否该合成一个身份,是另一张设计票。
|
|
29
|
+
* · 多租户记忆分区(`proj:<租户>/<projectId>`)与登记簿种子读的是**这次提交**的 `projectId`(按提交挑盘,修前即如此),不读行。
|
|
30
|
+
* · 行上的 `root` / `cwd` / `entered` / `remote` 今天只存不读(会话面读它们归 wire 半场那一车);fork 出的新会话从「没有记录」起。
|
|
31
|
+
* · **只回结果的腿(verify / cascade,含续跑的 verify 臂)不记行**:引擎在组合入口里自己排空内层腿的流,宿主手里没有流,而引擎给组合入口开的
|
|
32
|
+
* 帧座(`onStatusEvent` / `onWiringManifest`)没有这一帧 ⇒ 这条腿的位置与项目到不了行,行停在更早一条腿的那一帧(没有记号的检出里,
|
|
33
|
+
* 下一条不带 `projectId` 的提交续声明的是那一帧声明过的项目)。本仓不替引擎铸「这条腿站在哪里」;要引擎为组合入口开这一帧的座。
|
|
34
|
+
* · **行读在 run 认领之前**(resolveSpec 的每一读都是):同一会话两次提交交叠、后一次的装配读行之后前一次才落定并放认领 ⇒ 后一次读到的是更早
|
|
35
|
+
* 一条腿的项目(有记号的检出里多一次 409,没有记号的检出里续声明旧项目)。修法要把声明的装配挪到认领之后,不在本模块补第二个写者。
|
|
36
|
+
*/
|
|
37
|
+
import type { TaskEvent, TaskSpec } from "@sema-agent/core";
|
|
38
|
+
import type { DelegationRootWitness } from "./delegated-session-owner.js";
|
|
39
|
+
/**
|
|
40
|
+
* `workspace_changed` 帧在会话行上的样子(**唯一**一张表;表序 = 存储形的键序)。`forward` = 原值存进行;`strip` = 不进行。
|
|
41
|
+
* 腿身份四键(`eventId` / `parentToolCallId` / `sourceTaskId` / `bgAgentId`)是这一帧在流上的身份,不是位置;`type` 是臂名。
|
|
42
|
+
*/
|
|
43
|
+
export declare const SESSION_WORKSPACE_DISPOSITIONS: {
|
|
44
|
+
readonly type: "strip";
|
|
45
|
+
readonly cwd: "forward";
|
|
46
|
+
readonly root: "forward";
|
|
47
|
+
readonly entered: {
|
|
48
|
+
readonly nested: {
|
|
49
|
+
readonly dir: "forward";
|
|
50
|
+
readonly byPath: "forward";
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
readonly project: {
|
|
54
|
+
readonly nested: {
|
|
55
|
+
readonly id: "forward";
|
|
56
|
+
readonly source: "forward";
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
readonly remote: "forward";
|
|
60
|
+
readonly eventId: "strip";
|
|
61
|
+
readonly parentToolCallId: "strip";
|
|
62
|
+
readonly sourceTaskId: "strip";
|
|
63
|
+
readonly bgAgentId: "strip";
|
|
64
|
+
};
|
|
65
|
+
type WorkspaceFrame = Extract<TaskEvent, {
|
|
66
|
+
type: "workspace_changed";
|
|
67
|
+
}>;
|
|
68
|
+
/** 表里存进行的键(`strip` 之外的)—— 存储形由表派生,不另写一份成员清单。 */
|
|
69
|
+
type KeptKeys = {
|
|
70
|
+
[K in keyof typeof SESSION_WORKSPACE_DISPOSITIONS]: (typeof SESSION_WORKSPACE_DISPOSITIONS)[K] extends "strip" ? never : K;
|
|
71
|
+
}[keyof typeof SESSION_WORKSPACE_DISPOSITIONS];
|
|
72
|
+
/** 会话行上那一格的值:最后一帧里本仓存下的成员(型从引擎的帧臂派生)。 */
|
|
73
|
+
export type SessionWorkspace = Pick<WorkspaceFrame, KeptKeys>;
|
|
74
|
+
/** 会话店的这一面(两只方法都在才算 —— 只有一半的店让读写不同源)。 */
|
|
75
|
+
export interface SessionWorkspaceStore {
|
|
76
|
+
/** 这只会话上最后一帧;没有记录 / 没有这一行 ⇒ `undefined`。格读不出 ⇒ 读作没有记录并计一次(`decodeSessionWorkspaceCell`)。 */
|
|
77
|
+
readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
|
|
78
|
+
/**
|
|
79
|
+
* 整格覆写(最后一帧胜),**只写住客是 `incarnation` 那一位的行**:行不在、或此刻坐的是另一位住客 ⇒ 不写、不抛(那一行不是这条腿的)。
|
|
80
|
+
* 写**没有提交**必须抛。SQL 两孪生走店的一般查询面(与腿循环里的账本追加同一形,不设每查询的客户端超时 —— 客户端放弃等待而服务端照跑完的写,
|
|
81
|
+
* 正是能在这条腿离开之后落地的那一形);卡住的上界归服务端(锁等待超时 / 部署的 `DB_QUERY_TIMEOUT_MS`)。
|
|
82
|
+
*/
|
|
83
|
+
recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
/** 店有没有这一面 —— 结构判(包装店 `CachingSessionStore` 逐方法转发,判的是能力不是名字)。没有 ⇒ 不记、不读(core 的内存 dev 店)。 */
|
|
86
|
+
export declare function sessionWorkspaceStoreOf(store: unknown): SessionWorkspaceStore | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* 这条腿的会话**在受理时**是哪一位住客(会话 id + 化身令牌)的 spec 字段键 —— 与 run 名册 / 执行车道字段同一形:symbol 键随 spread / rest 解构走
|
|
89
|
+
* (续跑腿的 `taskConfig` 是 rest 解构出来的),对引擎的键面不可见(JSON / 结构化克隆跳过 symbol 键)。铸点唯一 = resolveSpec 出口
|
|
90
|
+
* ({@link stampLegSessionWitness}),值 = 受理时读会话行的那**一**读(同一只值也交给场景层作委派树根的见证);读点唯一 = {@link legSessionWitnessOf}。
|
|
91
|
+
*/
|
|
92
|
+
export declare const LEG_SESSION_WITNESS_FIELD: unique symbol;
|
|
93
|
+
/** 铸见证(**单一属主**:只许 resolveSpec 的出口调用)。`writable:false` + `configurable:false`:同一只 spec 被第二次铸 ⇒ 当场 TypeError。缺席 ⇒ 不铸。 */
|
|
94
|
+
export declare function stampLegSessionWitness<T extends TaskSpec>(spec: T, witness: DelegationRootWitness | undefined): T;
|
|
95
|
+
/** 读见证。`undefined` = 这只 spec 不是本仓装配出口产的,或这条腿没有会话 ⇒ 记录座不写(没有住客可认)。入参收 `object`:续跑腿手里是 rest 解构出来的 taskConfig。 */
|
|
96
|
+
export declare function legSessionWitnessOf(spec: object): DelegationRootWitness | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* 一帧 → 会话行上那一格的值。只认这条腿**自己的**帧(腿身份三键任一在场 = 委派子代转上来的,不是这条腿站在哪里);
|
|
99
|
+
* 按表投影后过存储形的同一只读者(写得进的就是读得出的)。读不出(引擎不会铸出这种帧)⇒ 不记、计一次。
|
|
100
|
+
*/
|
|
101
|
+
export declare function sessionWorkspaceOfFrame(ev: TaskEvent): SessionWorkspace | undefined;
|
|
102
|
+
/** 腿的记录座。 */
|
|
103
|
+
export interface SessionWorkspaceRecorder {
|
|
104
|
+
/**
|
|
105
|
+
* 持流的腿**逐帧**调用并等它(在本腿的取消成因闩之后)。这条腿自己的 `workspace_changed` ⇒ 这一笔写写完(或失败计一次)才兑现 ——
|
|
106
|
+
* 帧按到达序一笔一笔写,最后一帧胜;腿从任何出口(`done`、断连 `break`、抛)离开时没有写还悬着。其余帧当场兑现。**从不抛、从不拒**。
|
|
107
|
+
*/
|
|
108
|
+
observe(ev: TaskEvent): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 记录座的**唯一**铸点(四条持流腿各在开流时铸一只:后台腿 `runs.ts`、活流腿与同步腿 `http/routes/tasks.ts`、续跑腿 `http/resume-legs.ts`),
|
|
112
|
+
* `witness` = 这条腿 spec 上的受理见证({@link legSessionWitnessOf})。店没有这一面、或见证缺席 / 受理时这只会话还没有住客令牌 ⇒
|
|
113
|
+
* 一只什么都不做的座(诚实缺席:认不出住客就不写 —— 写错住客比少记一次坏)。
|
|
114
|
+
*
|
|
115
|
+
* 失败方向(F 类):写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,计一次;下一帧照写。放行的最坏后果 = 下一条不带 `projectId` 的提交读到旧的
|
|
116
|
+
* (或没有)项目 —— 有记号的检出里引擎照判记号(声明不等就响亮拒),没有记号的检出里这只会话少一次续声明。不放行的代价是把一条在跑的腿
|
|
117
|
+
* 为一次记录摔掉。等一笔写的代价 = 这条腿的流晚一次主键 UPDATE 的时延(引擎不等宿主消费,只是帧晚到;一条腿的帧只有起点一帧加每次换位一帧)。
|
|
118
|
+
* 库卡住(池取不到连接)时这条腿交给调用方的结果跟着等:后台腿与续跑腿的循环本来每一帧就等店写(账本追加);同步腿与不落账的活流腿此前循环里没有店写,
|
|
119
|
+
* 现在多等这一笔 —— 但同一只池上引擎自己的会话条目写(每一轮 `appendEntry`,`plugins/store-backend.ts` 的 `session()` 与其余店共用一池)本来就在
|
|
120
|
+
* 这条腿的关键路径上,池饿死时这条腿本来就停;上界归部署(`DB_QUERY_TIMEOUT_MS`、MySQL 池的排队上界)。local 后端的写不经池。
|
|
121
|
+
*/
|
|
122
|
+
export declare function createSessionWorkspaceRecorder(store: SessionWorkspaceStore | undefined, witness: DelegationRootWitness | undefined): SessionWorkspaceRecorder;
|
|
123
|
+
/**
|
|
124
|
+
* **这条腿交给引擎的 `TaskSpec.projectId`**(唯一铸点;见文件头第三句)。`submitted` = 这次提交自己带的、已过形状门且折成小写的值
|
|
125
|
+
* (新提交:鉴权口的 `resolvedProjectId`;续跑:持久化体的同一个键)。它在场就不读行。行读失败(库抖)原样上抛 —— 读不到「这只会话的项目」
|
|
126
|
+
* 不能悄悄折成「没有项目」(那会把一只登记在项目下的会话在没有记号的检出里改成无项目)。
|
|
127
|
+
*/
|
|
128
|
+
export declare function legProjectIdOf(submitted: string | undefined, store: SessionWorkspaceStore | undefined, sessionId: string | undefined): Promise<string | undefined>;
|
|
129
|
+
export {};
|
|
130
|
+
//# sourceMappingURL=session-workspace.d.ts.map
|