@sema-agent/server 7.135.0 → 7.137.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +237 -2
- package/USAGE.md +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
package/dist/config.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { DEFAULT_AT_MENTION, DEFAULT_MODEL_REF, mintCapabilityKeys } from "./model-select.js";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { join, posix as posixPath } from "node:path";
|
|
4
|
+
import { isAbsolute, join, posix as posixPath } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { compileAllowList, inheritEnvPolicyProblem, BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins,
|
|
6
|
+
import { compileAllowList, inheritEnvPolicyProblem, BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, isModelPriced, PROTOCOL_TABLE, RECOMMENDED_SENSITIVE_PATTERNS, judgeRuleToolName, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, principalInboxConsumerOf } from "@sema-agent/core";
|
|
7
7
|
import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, WEB_ADMIN_ENV, retiredEnvRefusal, stripControlAndBidi } from "@sema-agent/settings-schema";
|
|
8
8
|
import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
|
|
9
9
|
import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
|
|
@@ -29,7 +29,8 @@ import { NAME_TABLE_BEHAVIOR, TOOL_NAME_ROSTER, describeUnmatchableRuleName, rul
|
|
|
29
29
|
import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, centerIdentityRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal, operatorSingleUserRefusal, workflowParkDecisionDomainRefusal } from "./config-invariants.js";
|
|
30
30
|
import { declaredSemaConfigDir } from "./sema-config-dir.js";
|
|
31
31
|
import { lkgPathOverrideOf } from "./config-lkg.js";
|
|
32
|
-
import { K8S_WORKSPACE_MOUNT_DEFAULT, WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE, parseWorkspaceSnapshotExclude } from "./workspace-space.js";
|
|
32
|
+
import { K8S_WORKSPACE_MOUNT_DEFAULT, WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE, parseWorkspaceSnapshotExclude, parseWorkspaceSnapshotPrefix } from "./workspace-space.js";
|
|
33
|
+
import { SESSION_SNAPSHOT_BUCKET_DEFAULT, WORKSPACE_SNAPSHOT_BUCKET_DEFAULT } from "./object-store-buckets.js";
|
|
33
34
|
import { laneLivenessOf } from "./exec-bounds.js";
|
|
34
35
|
import { SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS } from "./sandbox-egress-policy.js";
|
|
35
36
|
import { WORKSPACE_CACHE_DEFAULT, WORKSPACE_CACHE_MOUNT_DEFAULT, WORKSPACE_CACHE_MOUNT_RESERVED, WORKSPACE_CACHE_SIZE_DEFAULT, WORKSPACE_CACHE_WORDS, isK8sQuantity, mountsOverlap, reservedMountCollision } from "./workspace-cache.js";
|
|
@@ -44,9 +45,9 @@ import { parseCenterCredentialRenewLeadMs } from "./center-credential-renewer.js
|
|
|
44
45
|
import { modelProviderRefusal } from "./model-provider.js";
|
|
45
46
|
import { modelRouteUrlRefusal } from "./model-route-url.js";
|
|
46
47
|
import { httpUrlFlaws } from "@sema-agent/settings-schema";
|
|
47
|
-
import { buildHttpUrlRefusal } from "./url-refusal.js";
|
|
48
|
+
import { buildHttpUrlRefusal, buildRefusalShownUrl } from "./url-refusal.js";
|
|
48
49
|
import { customPkgSourceFromEnv } from "./sandbox-pkg-source.js";
|
|
49
|
-
import { csvOf, envLaneTable, mintedCheapId, readModelRouteEnv } from "./model-route-gap.js";
|
|
50
|
+
import { anthropicCredentialRefusal, csvOf, envLaneTable, mintedCheapId, readModelRouteEnv } from "./model-route-gap.js";
|
|
50
51
|
import { displayListEntry } from "./list-entry-display.js";
|
|
51
52
|
import { echoCallerText } from "./caller-text-echo.js";
|
|
52
53
|
import { buildDisplaySafeUrl, redactSecrets } from "./trace/redact.js";
|
|
@@ -371,6 +372,30 @@ function parseDeviceEnrollTokenTtlEnv() {
|
|
|
371
372
|
}
|
|
372
373
|
return Number(raw);
|
|
373
374
|
}
|
|
375
|
+
function credentialLawRewriteOf(href) {
|
|
376
|
+
const scanned = redactSecrets(href);
|
|
377
|
+
return scanned === href ? undefined : scanned;
|
|
378
|
+
}
|
|
379
|
+
function parseDeviceRelayUrlEnv() {
|
|
380
|
+
const raw = process.env.DEVICE_RELAY_URL;
|
|
381
|
+
if (raw === undefined || raw === "")
|
|
382
|
+
return undefined;
|
|
383
|
+
const scheme = /^wss?:\/\//i.exec(raw)?.[0];
|
|
384
|
+
const asHttp = scheme === undefined ? undefined : `${scheme.length === "wss://".length ? "https" : "http"}://${raw.slice(scheme.length)}`;
|
|
385
|
+
const flaws = asHttp === undefined ? ["not-ws-url"] : httpUrlFlaws(asHttp, ["userinfo", "query", "fragment"]).map((f) => (f === "not-http-url" ? "not-ws-url" : f));
|
|
386
|
+
if (flaws.length > 0) {
|
|
387
|
+
throw new Error(`env DEVICE_RELAY_URL must be an absolute ws:// or wss:// URL with no userinfo, query or fragment (ws(s)://host[:port][/path]) — found: ${flaws.join(", ")}; got ${JSON.stringify(buildRefusalShownUrl(raw))}. ` +
|
|
388
|
+
`It is published verbatim on GET /v1/capabilities (deviceExecutor.relayUrl) as the address devices dial, so it has no room for credentials (devices authenticate by signature). ` +
|
|
389
|
+
`Leave it unset when devices reach this engine at its own address — they then dial it at deviceExecutor.wsPath.`);
|
|
390
|
+
}
|
|
391
|
+
const href = new URL(raw).href;
|
|
392
|
+
const credentialScan = credentialLawRewriteOf(href);
|
|
393
|
+
if (credentialScan !== undefined) {
|
|
394
|
+
throw new Error(`env DEVICE_RELAY_URL carries something the credential-redaction law recognizes (${credentialScan}) — it is published verbatim on GET /v1/capabilities (deviceExecutor.relayUrl), ` +
|
|
395
|
+
`so it must not carry a token-shaped path segment; keep it to ws(s)://host[:port][/path] (devices authenticate by signature, not by the URL).`);
|
|
396
|
+
}
|
|
397
|
+
return raw;
|
|
398
|
+
}
|
|
374
399
|
function parseDevicePreAuthPerRemoteEnv() {
|
|
375
400
|
const raw = process.env.DEVICE_MAX_PREAUTH_PER_REMOTE;
|
|
376
401
|
if (raw === undefined || raw === "")
|
|
@@ -863,6 +888,18 @@ function parseStoreDomain(ctx) {
|
|
|
863
888
|
dbBackend,
|
|
864
889
|
dbBackendExplicit: dbBackendSet,
|
|
865
890
|
localDataRoot,
|
|
891
|
+
...((d) => (d !== undefined ? { sealedKeyDir: d } : {}))((() => {
|
|
892
|
+
const raw = process.env.SEALED_KEY_DIR;
|
|
893
|
+
if (raw === undefined || raw === "")
|
|
894
|
+
return undefined;
|
|
895
|
+
if (!isAbsolute(raw)) {
|
|
896
|
+
throw new Error(`SEALED_KEY_DIR="${raw}" is not an absolute path — it names the model-key custody directory (the sealed-key keypairs ` +
|
|
897
|
+
`and their checkpoint-ctx-keys/ subdirectory) that every engine sharing one config.d must point at to hold the same key, ` +
|
|
898
|
+
`so a value resolved against each process's working directory would silently split them. Spell it absolutely, or unset it ` +
|
|
899
|
+
`to keep the custody directory at <data root>/sealed-key.`);
|
|
900
|
+
}
|
|
901
|
+
return raw;
|
|
902
|
+
})()),
|
|
866
903
|
runStoreStrictHydrate: parseRunStoreStrictHydrate(process.env.RUN_STORE_STRICT_HYDRATE),
|
|
867
904
|
tidb: needsDb && dbBackend === "mysql"
|
|
868
905
|
? {
|
|
@@ -888,7 +925,7 @@ function parseStoreDomain(ctx) {
|
|
|
888
925
|
snapshotBlobStore: process.env.MINIO_ENDPOINT && process.env.MINIO_ACCESS_KEY && process.env.MINIO_SECRET_KEY
|
|
889
926
|
? {
|
|
890
927
|
endpoint: process.env.MINIO_ENDPOINT,
|
|
891
|
-
bucket: process.env.SESSION_SNAPSHOT_BUCKET ??
|
|
928
|
+
bucket: process.env.SESSION_SNAPSHOT_BUCKET ?? SESSION_SNAPSHOT_BUCKET_DEFAULT,
|
|
892
929
|
accessKey: process.env.MINIO_ACCESS_KEY,
|
|
893
930
|
secretKey: process.env.MINIO_SECRET_KEY,
|
|
894
931
|
...(process.env.MINIO_REGION ? { region: process.env.MINIO_REGION } : {}),
|
|
@@ -907,7 +944,7 @@ function parseStoreDomain(ctx) {
|
|
|
907
944
|
? { publicEndpoint: (process.env.S3_PUBLIC_ENDPOINT || process.env.S3_ENDPOINT) }
|
|
908
945
|
: {}),
|
|
909
946
|
publicBucket: process.env.S3_PUBLIC_BUCKET ?? "sema-public",
|
|
910
|
-
privateBucket: process.env.SESSION_SNAPSHOT_BUCKET ??
|
|
947
|
+
privateBucket: process.env.SESSION_SNAPSHOT_BUCKET ?? SESSION_SNAPSHOT_BUCKET_DEFAULT,
|
|
911
948
|
privateKeyPrefix: "sendfile/",
|
|
912
949
|
...(process.env.SEND_USER_FILE_SANDBOX_PUT_ENDPOINT ? { sandboxPutEndpoint: process.env.SEND_USER_FILE_SANDBOX_PUT_ENDPOINT } : {}),
|
|
913
950
|
accessKey: process.env.MINIO_ACCESS_KEY,
|
|
@@ -941,6 +978,10 @@ function parseModelDomain() {
|
|
|
941
978
|
throw new Error(refusal);
|
|
942
979
|
}
|
|
943
980
|
}
|
|
981
|
+
const anthropicCredentialConflict = anthropicCredentialRefusal({ apiKey: route.anthropicApiKey, authToken: route.anthropicAuthToken });
|
|
982
|
+
if (anthropicCredentialConflict !== undefined) {
|
|
983
|
+
throw new Error(anthropicCredentialConflict);
|
|
984
|
+
}
|
|
944
985
|
const provider = route.provider;
|
|
945
986
|
if (route.providerSource === "inferred") {
|
|
946
987
|
CONFIG_NOTICES.push({
|
|
@@ -1073,7 +1114,7 @@ function parseModelDomain() {
|
|
|
1073
1114
|
}
|
|
1074
1115
|
const cheapRef = cheapModel ? cheapModel.id : DEFAULT_MODEL_REF;
|
|
1075
1116
|
const verifierEnv = process.env.MODEL_VERIFIER;
|
|
1076
|
-
const verifierRef = verifierEnv && models
|
|
1117
|
+
const verifierRef = verifierEnv && Object.hasOwn(models, verifierEnv) ? verifierEnv : DEFAULT_MODEL_REF;
|
|
1077
1118
|
const roles = {
|
|
1078
1119
|
verifier: verifierRef,
|
|
1079
1120
|
};
|
|
@@ -1088,8 +1129,8 @@ function parseModelDomain() {
|
|
|
1088
1129
|
continue;
|
|
1089
1130
|
roles[role] =
|
|
1090
1131
|
typeof cur === "string"
|
|
1091
|
-
? { model: cur,
|
|
1092
|
-
: { ...cur,
|
|
1132
|
+
? { model: cur, promptRole: "code" }
|
|
1133
|
+
: { ...cur, promptRole: "code" };
|
|
1093
1134
|
}
|
|
1094
1135
|
const degradeOn = parseDegradeOn(csv("MODEL_DEGRADE_ON"));
|
|
1095
1136
|
return {
|
|
@@ -1925,6 +1966,7 @@ function parseOrchestrationDomain(ctx) {
|
|
|
1925
1966
|
...(process.env.K8S_MEMORY ? { memory: process.env.K8S_MEMORY } : {}),
|
|
1926
1967
|
...(process.env.K8S_CPU ? { cpu: process.env.K8S_CPU } : {}),
|
|
1927
1968
|
sandboxIpv6: enumEnv("K8S_SANDBOX_IPV6", SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS),
|
|
1969
|
+
...(process.env.K8S_SANDBOX_BROKER_IMAGE ? { brokerImage: process.env.K8S_SANDBOX_BROKER_IMAGE } : {}),
|
|
1928
1970
|
...((names) => (names !== undefined ? { sandboxEnv: allowlistedEnv(names, process.env) } : {}))(parseEnvNameAllowlist("K8S_SANDBOX_ENV", singleUserTurnkey)),
|
|
1929
1971
|
...k8sWorkspaceStore(),
|
|
1930
1972
|
...((c) => (c !== undefined ? { workspaceCache: c } : {}))(k8sWorkspaceCache()),
|
|
@@ -1983,6 +2025,7 @@ function parseOrchestrationDomain(ctx) {
|
|
|
1983
2025
|
...((n) => (n !== undefined ? { maxInflightPerDevice: n } : {}))(parseDeviceBoundedIntEnv("DEVICE_MAX_INFLIGHT_PER_DEVICE", process.env.DEVICE_MAX_INFLIGHT_PER_DEVICE, DEVICE_HELLO_ACK_BOUNDS.maxInflight)),
|
|
1984
2026
|
...((n) => (n !== undefined ? { maxPreAuthPerRemote: n } : {}))(parseDevicePreAuthPerRemoteEnv()),
|
|
1985
2027
|
...((n) => (n !== undefined ? { enrollTokenTtlSec: n } : {}))(parseDeviceEnrollTokenTtlEnv()),
|
|
2028
|
+
...((n) => (n !== undefined ? { relayUrl: n } : {}))(parseDeviceRelayUrlEnv()),
|
|
1986
2029
|
...(process.env.DEVICE_SUPERSEDE ? { supersede: enumEnv("DEVICE_SUPERSEDE", "stale_only", ["stale_only", "deny"]) } : {}),
|
|
1987
2030
|
}
|
|
1988
2031
|
:
|
|
@@ -2224,7 +2267,7 @@ function parseFleetScope(raw) {
|
|
|
2224
2267
|
if (raw !== undefined && !ScopeId.safeParse(raw).success) {
|
|
2225
2268
|
const shown = raw.length > 40 ? `${raw.slice(0, 40)}…(${raw.length} chars)` : raw;
|
|
2226
2269
|
throw fleetEnvRefusal("scope_invalid", `env ${name}=${JSON.stringify(shown)} is not a scope id: it must be a lowercase slug of letters, digits and inner dashes, 1–64 characters, ` +
|
|
2227
|
-
`not empty. The config renderer sets it on
|
|
2270
|
+
`not empty. The config renderer sets it on every engine it manages (the default scope included); on a hand-wired engine leave it unset for the default scope ("${fleetScopeOf(undefined)}"). ` +
|
|
2228
2271
|
`Refusing to boot rather than answering drain requests under the wrong identity.`);
|
|
2229
2272
|
}
|
|
2230
2273
|
return fleetScopeOf(raw);
|
|
@@ -2313,8 +2356,8 @@ function parseA2aServe() {
|
|
|
2313
2356
|
`inbound authentication on this endpoint is header-shaped (a service credential), not URL-embedded.`);
|
|
2314
2357
|
}
|
|
2315
2358
|
const parsed = new URL(rawUrl);
|
|
2316
|
-
const a2aCredentialScan =
|
|
2317
|
-
if (a2aCredentialScan !==
|
|
2359
|
+
const a2aCredentialScan = credentialLawRewriteOf(parsed.href);
|
|
2360
|
+
if (a2aCredentialScan !== undefined) {
|
|
2318
2361
|
throw new Error(`A2A_SERVE_URL carries something the credential-redaction law recognizes (${a2aCredentialScan}) — this address is published VERBATIM on the ANONYMOUSLY readable agent card (the card's url is a functional address: peers fetch it, so it is never rewritten for display), ` +
|
|
2319
2362
|
`so it must not carry credentials of ANY shape: no userinfo, no credential-named query parameter (api_key/token/…), no token-shaped path segment or parameter value. ` +
|
|
2320
2363
|
`Keep A2A_SERVE_URL to scheme://host[:port][/path] and move the secret into the caller's headers.`);
|
|
@@ -2371,7 +2414,7 @@ function parseA2aServeSkills(raw) {
|
|
|
2371
2414
|
});
|
|
2372
2415
|
}
|
|
2373
2416
|
const STORE_GROUP_KEYS = [
|
|
2374
|
-
"sessionBackend", "sessionCacheTtlSec", "storeProbeIntervalMs", "storeLockWaitMs", "dbBackend", "dbBackendExplicit", "localDataRoot",
|
|
2417
|
+
"sessionBackend", "sessionCacheTtlSec", "storeProbeIntervalMs", "storeLockWaitMs", "dbBackend", "dbBackendExplicit", "localDataRoot", "sealedKeyDir",
|
|
2375
2418
|
"runStoreStrictHydrate",
|
|
2376
2419
|
"tidb", "pg", "dbQueryTimeoutMs", "snapshotBlobStore", "snapshotBlobSqlMaxBytes", "snapshotBlobAllowSql", "fileHistory", "fileHistoryCapture", "sendUserFile",
|
|
2377
2420
|
];
|
|
@@ -2463,6 +2506,7 @@ function k8sWorkspaceStore() {
|
|
|
2463
2506
|
if (!Number.isInteger(workspaceSnapshotTimeoutMs))
|
|
2464
2507
|
throw new Error(`env WORKSPACE_SNAPSHOT_TIMEOUT_MS=${workspaceSnapshotTimeoutMs} must be an integer (milliseconds)`);
|
|
2465
2508
|
const exclude = parseWorkspaceSnapshotExclude(process.env.WORKSPACE_SNAPSHOT_EXCLUDE);
|
|
2509
|
+
const keyPrefix = parseWorkspaceSnapshotPrefix(process.env.WORKSPACE_SNAPSHOT_PREFIX);
|
|
2466
2510
|
const knobs = { workspacePersist, workspaceSnapshotKeep, workspaceSnapshotTimeoutMs, ...(exclude.length > 0 ? { workspaceSnapshotExclude: exclude } : {}) };
|
|
2467
2511
|
const endpoint = process.env.MINIO_ENDPOINT;
|
|
2468
2512
|
const accessKey = process.env.MINIO_ACCESS_KEY;
|
|
@@ -2482,11 +2526,11 @@ function k8sWorkspaceStore() {
|
|
|
2482
2526
|
...knobs,
|
|
2483
2527
|
s3Snapshot: {
|
|
2484
2528
|
endpoint,
|
|
2485
|
-
bucket: process.env.WORKSPACE_SNAPSHOT_BUCKET ??
|
|
2529
|
+
bucket: process.env.WORKSPACE_SNAPSHOT_BUCKET ?? WORKSPACE_SNAPSHOT_BUCKET_DEFAULT,
|
|
2486
2530
|
accessKey,
|
|
2487
2531
|
secretKey,
|
|
2488
2532
|
...(process.env.MINIO_REGION ? { region: process.env.MINIO_REGION } : {}),
|
|
2489
|
-
...(
|
|
2533
|
+
...(keyPrefix !== undefined ? { keyPrefix } : {}),
|
|
2490
2534
|
...((n) => (n !== undefined ? { presignTtlSec: n } : {}))(optNumEnvStrict("WORKSPACE_SNAPSHOT_TTL_SEC")),
|
|
2491
2535
|
},
|
|
2492
2536
|
};
|
|
@@ -103,8 +103,8 @@ export interface AbsentApprovalSeat {
|
|
|
103
103
|
* durable resume 驱动、断连后的 detach 腿)ask——durable park 原语义逐字保留([ref] 后无活流腿放行执行
|
|
104
104
|
* 也不会产出空答:coordinator 无 ALS ctx ⇒ 冻结 `{kind:"unavailable"}`(src/question.ts),永不悬挂;
|
|
105
105
|
* park 仍是把问题送到人面前的唯一那条腿,正当性不变、只是反事实前提换了)。
|
|
106
|
-
*
|
|
107
|
-
*
|
|
106
|
+
* 「是不是引擎的问题工具」不在本层判:core 7.56.0 [ref] 起本门**组合** core 原形的答(它读调用的脸 —— 引擎挂载点 + 目录契约 id,不读名字;
|
|
107
|
+
* 它答 `ask` ⇒ 是;本层只在那之上按活流分腿)。决断口的 pre-CAS 守卫(「批准问答必须带答」)仍按保留名读(core 同版「Not changed here」那一条)。
|
|
108
108
|
* ⚠️ **单一属主**(复审 A2):AskUserQuestion 的 durable 判决只有这一处。任何需要「同参重建」这条判决的
|
|
109
109
|
* 地方(main.ts 的 parkedReviveInheritedGate 父约束链)必须调本工厂,不得自折 core 原形——两份拷贝里
|
|
110
110
|
* 只改一份正是本条 finding 的成因。**登记豁免一处**:leader worker 腿(src/leader/wire.ts provisionWorker)
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { join, posix } from "node:path";
|
|
2
2
|
import { FileError, StubExecutionEnv, combinePolicies, createAllowDenyPolicy, createDurableQuestionPolicy, createSensitivePathPolicy, createTranscriptIntegrityPolicy, err, isAbsoluteForFamily, isProtectedWrite, ok, pathTargetValue, protectivePathTargetOf, } from "@sema-agent/core";
|
|
3
3
|
import { createDurableAskPolicy, hasApprovalListIntent } from "./approval.js";
|
|
4
|
-
import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
|
|
5
|
-
import { heldRuleName } from "./tool-rule-names.js";
|
|
6
4
|
export function createDurableQuestionGate(live) {
|
|
5
|
+
const durable = createDurableQuestionPolicy();
|
|
7
6
|
if (live === undefined)
|
|
8
|
-
return
|
|
7
|
+
return durable;
|
|
9
8
|
return {
|
|
10
|
-
check(req) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
: { action: "ask", message: "AskUserQuestion: awaiting a human answer (durable)" };
|
|
9
|
+
async check(req) {
|
|
10
|
+
const answer = await durable.check(req);
|
|
11
|
+
if (answer.action !== "ask")
|
|
12
|
+
return answer;
|
|
13
|
+
return live.hasLiveContext() ? { action: "allow" } : answer;
|
|
16
14
|
},
|
|
17
15
|
};
|
|
18
16
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* ## 什么进表、什么不进
|
|
19
19
|
* 进表的是**形规则**:只由部署形事实决定的布尔(去重后 14 行;同一条规则的多个消费者读同一行,不各成一行)。
|
|
20
20
|
* 把一行与**非形事实**(锁键、合规档位、审批意图、请求身份、请求体)合取的能力判据是**消费者**,住在它自己的模块
|
|
21
|
-
* (`
|
|
21
|
+
* (`mcpInjectionHonored` = 锁 ∧ 合规 ∧ `!multiTenant`;`taskHooksHonored`;身份门 …),读行、不读原始键。
|
|
22
22
|
* 车道表(`execution-lane-caps.ts`)按车道词键控的布尔列(`laneIsolated` 等)是车道轴自己的事实表,不是「部署」的谓词;行读它们。
|
|
23
23
|
*
|
|
24
24
|
* 依赖纪律:叶模块 —— 值导入只有 `execution-lane-caps.ts`(它本身是叶),零运行期环。
|
|
@@ -74,6 +74,7 @@ export declare function asInstructionKind(v: string): DeviceInstructionKind | nu
|
|
|
74
74
|
export declare const DEVICE_OUTCOME_ERROR_CODES_BY_REVISION: {
|
|
75
75
|
readonly 1: readonly ["aborted", "already_exists", "auth_failed", "callback_error", "invalid", "is_directory", "not_directory", "not_found", "not_supported", "outcome_unknown", "permission_denied", "precondition_failed", "shell_unavailable", "spawn_error", "suspended", "target_unavailable", "timeout", "transport_lost", "unknown"];
|
|
76
76
|
readonly 2: readonly ["liveness_no_output"];
|
|
77
|
+
readonly 3: readonly ["name_too_long"];
|
|
77
78
|
};
|
|
78
79
|
/** 本包认得的协议修订号(= 增量表的行键)。 */
|
|
79
80
|
export type DeviceProtocolRevision = keyof typeof DEVICE_OUTCOME_ERROR_CODES_BY_REVISION;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPublicKey, randomBytes, randomInt, verify as edVerify } from "node:crypto";
|
|
2
2
|
import { MAX_TIMER_DELAY_MS, inWholeNumberBounds, wholeNumberBoundsRule } from "./config-types.js";
|
|
3
3
|
export const DEVICE_WS_PATH = "/v1/device/ws";
|
|
4
|
-
export const DEVICE_PROTOCOL_VERSION =
|
|
4
|
+
export const DEVICE_PROTOCOL_VERSION = 3;
|
|
5
5
|
export const DEVICE_MIN_PROTOCOL_VERSION = 1;
|
|
6
6
|
export const DEVICE_HELLO_DOMAIN_TAG = "sema.device.hello.v1";
|
|
7
7
|
export const DEVICE_WS_LIMITS = {
|
|
@@ -66,6 +66,7 @@ export const DEVICE_OUTCOME_ERROR_CODES_BY_REVISION = {
|
|
|
66
66
|
"unknown",
|
|
67
67
|
],
|
|
68
68
|
2: ["liveness_no_output"],
|
|
69
|
+
3: ["name_too_long"],
|
|
69
70
|
};
|
|
70
71
|
export function deviceOutcomeErrorCodesAt(revision) {
|
|
71
72
|
if (!Number.isInteger(revision) || revision < 1 || revision > DEVICE_PROTOCOL_VERSION) {
|
package/dist/elicitation.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface ElicitationFrame {
|
|
|
20
20
|
* coordinate instead, same posture as the stream-approval frames' `serverNowMs`. */
|
|
21
21
|
serverNowMs?: number;
|
|
22
22
|
/** "elicitation" only (additive; the same window pair `question` carries): the coordinator's OWN answer window —
|
|
23
|
-
* at expiry the ask
|
|
23
|
+
* at expiry the ask is released as `cancel` (nobody answered; see RELEASE_ACTION). Minted from the SAME `ttlMs` the `setTimeout` below arms (one source), so
|
|
24
24
|
* a form replayed from the ledger can be judged still-waiting (no `elicitation_complete` with this id ∧
|
|
25
25
|
* `expiresAtMs` not reached) or lapsed even when the close row never landed (a lost write, a crashed replica). */
|
|
26
26
|
expiresInMs?: number;
|
|
@@ -37,13 +37,13 @@ export interface ElicitationRunContext {
|
|
|
37
37
|
abortSignal?: AbortSignal;
|
|
38
38
|
}
|
|
39
39
|
export interface ElicitationThrottle {
|
|
40
|
-
/** Max concurrent in-flight elicitations per run leg (parallel tool calls can each elicit). Breach ⇒ decline. */
|
|
40
|
+
/** Max concurrent in-flight elicitations per run leg (parallel tool calls can each elicit). Breach ⇒ refused (`decline`). */
|
|
41
41
|
maxConcurrentPerRun: number;
|
|
42
|
-
/** Max total elicitations one run leg will surface to the human. Breach ⇒ decline
|
|
42
|
+
/** Max total elicitations one run leg will surface to the human. Breach ⇒ refused (`decline`; flood defense). */
|
|
43
43
|
maxTotalPerRun: number;
|
|
44
|
-
/** Min gap (ms) between two elicitations from the SAME server within a run. Within ⇒ decline
|
|
44
|
+
/** Min gap (ms) between two elicitations from the SAME server within a run. Within ⇒ refused (`decline`; burst guard). */
|
|
45
45
|
minIntervalMsPerServer: number;
|
|
46
|
-
/** An unanswered elicitation
|
|
46
|
+
/** An unanswered elicitation is released as `cancel` after this (ms) — the human walked away; don't hold core forever. */
|
|
47
47
|
ttlMs: number;
|
|
48
48
|
}
|
|
49
49
|
export declare const DEFAULT_ELICITATION_THROTTLE: ElicitationThrottle;
|
|
@@ -75,8 +75,9 @@ export declare class ElicitationCoordinator {
|
|
|
75
75
|
* the case where the leg ends/throws while an elicitation is parked but the leg's own loop has already drained. */
|
|
76
76
|
runWithContext<T>(ctx: ElicitationRunContext, fn: () => Promise<T>): Promise<T>;
|
|
77
77
|
/** `RunnerDeps.onElicit`. Core calls this when an opted-in server elicits the END USER mid-tool-call; the resolved
|
|
78
|
-
* value is sent back to the server on the still-open connection. Fail-closed (`
|
|
79
|
-
*
|
|
78
|
+
* value is sent back to the server on the still-open connection. Fail-closed (never `accept`) in every uncertain
|
|
79
|
+
* case: a rule-based refusal is `decline`, a release nobody answered is `cancel` (RELEASE_ACTION).
|
|
80
|
+
* Arrow property so it can be passed as `onElicit: coordinator.elicit` with `this` bound. */
|
|
80
81
|
elicit: (req: McpElicitRequest, signal?: AbortSignal) => Promise<McpElicitResponse>;
|
|
81
82
|
/** `POST /v1/elicitations/:id/respond` — resolve a parked elicitation with the shell's answer. Owner-gated with a
|
|
82
83
|
* 404 (no existence oracle): a non-owner AND an unknown id (answered / expired / wrong replica) both get 404.
|
package/dist/elicitation.js
CHANGED
|
@@ -5,6 +5,7 @@ import { recordFailOpen } from "./observability/fail-open.js";
|
|
|
5
5
|
import { refusalBody } from "./http/refusal-body.js";
|
|
6
6
|
const MAX_ELICIT_MESSAGE_CHARS = 4096;
|
|
7
7
|
const MAX_ELICIT_SCHEMA_BYTES = 8192;
|
|
8
|
+
const RELEASE_ACTION = { refused: "decline", unanswered: "cancel" };
|
|
8
9
|
export const DEFAULT_ELICITATION_THROTTLE = {
|
|
9
10
|
maxConcurrentPerRun: 2,
|
|
10
11
|
maxTotalPerRun: 20,
|
|
@@ -65,7 +66,7 @@ export class ElicitationCoordinator {
|
|
|
65
66
|
finally {
|
|
66
67
|
for (const [, p] of [...this.pending]) {
|
|
67
68
|
if (p.taskId === ctx.taskId)
|
|
68
|
-
p.settle({ action:
|
|
69
|
+
p.settle({ action: RELEASE_ACTION.unanswered });
|
|
69
70
|
}
|
|
70
71
|
this.counters.delete(ctx.taskId);
|
|
71
72
|
}
|
|
@@ -74,18 +75,18 @@ export class ElicitationCoordinator {
|
|
|
74
75
|
elicit = async (req, signal) => {
|
|
75
76
|
const ctx = this.als.getStore();
|
|
76
77
|
if (!ctx)
|
|
77
|
-
return { action:
|
|
78
|
+
return { action: RELEASE_ACTION.refused };
|
|
78
79
|
if (typeof req.url === "string" && req.url.length > 0)
|
|
79
|
-
return { action:
|
|
80
|
+
return { action: RELEASE_ACTION.refused };
|
|
80
81
|
if (signal?.aborted || ctx.abortSignal?.aborted)
|
|
81
|
-
return { action:
|
|
82
|
+
return { action: RELEASE_ACTION.unanswered };
|
|
82
83
|
const rc = this.countersFor(ctx.taskId);
|
|
83
84
|
const t = this.now();
|
|
84
85
|
const last = rc.lastByServer.get(req.server);
|
|
85
86
|
if (rc.inflight >= this.throttle.maxConcurrentPerRun ||
|
|
86
87
|
rc.total >= this.throttle.maxTotalPerRun ||
|
|
87
88
|
(last !== undefined && t - last < this.throttle.minIntervalMsPerServer)) {
|
|
88
|
-
return { action:
|
|
89
|
+
return { action: RELEASE_ACTION.refused };
|
|
89
90
|
}
|
|
90
91
|
const id = uuidv7();
|
|
91
92
|
rc.inflight += 1;
|
|
@@ -107,8 +108,8 @@ export class ElicitationCoordinator {
|
|
|
107
108
|
rc.inflight = Math.max(0, rc.inflight - 1);
|
|
108
109
|
resolveAnswer(r);
|
|
109
110
|
};
|
|
110
|
-
const onAbort = () => settle({ action:
|
|
111
|
-
timer = setTimeout(() => settle({ action:
|
|
111
|
+
const onAbort = () => settle({ action: RELEASE_ACTION.unanswered });
|
|
112
|
+
timer = setTimeout(() => settle({ action: RELEASE_ACTION.unanswered }), this.throttle.ttlMs);
|
|
112
113
|
timer.unref?.();
|
|
113
114
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
114
115
|
ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -124,7 +125,7 @@ export class ElicitationCoordinator {
|
|
|
124
125
|
}
|
|
125
126
|
else if (delivery === "failed") {
|
|
126
127
|
recordFailOpen("server.elicitation.open-frame-undelivered", `elicitation=${id} server=${req.server}`);
|
|
127
|
-
settle({ action:
|
|
128
|
+
settle({ action: RELEASE_ACTION.unanswered });
|
|
128
129
|
}
|
|
129
130
|
const answer = await answerP;
|
|
130
131
|
void emitted
|
|
@@ -23,8 +23,11 @@
|
|
|
23
23
|
* 前置到它自己的 message 前 —— **不 `instanceof` 单挑任何一个类**(身上有字符串 `code` 就折),该店
|
|
24
24
|
* 的折点全部改走这一只函数。人与模型因此看得到「这是哪一类失败」。
|
|
25
25
|
* · **判断面**(同一只函数的 `fault`,core 7.55.0 `FileHistoryError.permanence`):永久码表里的 typed 码 ⇒
|
|
26
|
-
* `{ permanence: "terminal" }
|
|
27
|
-
* `
|
|
26
|
+
* `{ permanence: "terminal" }`;**连接类故障**(到库的连接坏了 / 库此刻不接 —— 判官是整仓唯一一只
|
|
27
|
+
* `plugins/store-fault.ts` 的 `storeFaultClassOf`,识别表在 `plugins/sql-errors.ts`,本文件不再抄第二份)⇒
|
|
28
|
+
* `{ permanence: "transient" }`;**部署错配类**(缺表 / 权限拒:连接是好的、重试注定失败,修表 / 授权之前什么都不会变 —— 同一只判官的 `deployment` 一词,表在 `plugins/sql-errors.ts`)⇒
|
|
29
|
+
* `{ permanence: "terminal" }`;其余 ⇒ `{}`(没判 —— 引擎照旧给重试引导语)。`terminal` 只对永久码与部署错配类。
|
|
30
|
+
* 折点把它摊进店答的 `FileHistoryError`;引擎的拒句引导语、rewind 披露、`terminal.detail` 都读这一格,**不读散文**。
|
|
28
31
|
* · **机器面**(同一只函数的副作用):码取自 typed 错**对象**、对闭集词表判定,**绝不从散文里抠**。
|
|
29
32
|
* 🔴 这一条是 codex R1-[high] 验真后的定形:文本通道**天然可伪造** —— 一个名叫
|
|
30
33
|
* `(blob_too_large_for_sql).txt` 的文件读不出来时,它的路径就在 `snapshot_failed` 的散文里,任何
|
|
@@ -51,8 +54,9 @@ export interface CaughtFailure {
|
|
|
51
54
|
*
|
|
52
55
|
* `text` = 散文形:带 typed `code` 的前缀一个括号词(`(blob_too_large_for_sql) blob of … exceeds …`),
|
|
53
56
|
* 不带的**逐字**是原 message(transient 失败的文案因此与本批之前一字不差)。
|
|
54
|
-
* `fault` = 判断形(core 7.55.0 `FileHistoryError.permanence`):永久码表里的码 ⇒ `{ permanence: "terminal" }
|
|
55
|
-
*
|
|
57
|
+
* `fault` = 判断形(core 7.55.0 `FileHistoryError.permanence`):永久码表里的码 ⇒ `{ permanence: "terminal" }`;连接类故障 ⇒ `{ permanence: "transient" }`
|
|
58
|
+
* (什么都不改稍后再试可能成);部署错配类故障 ⇒ `{ permanence: "terminal" }`(修表 / 授权之前重试注定失败);其余 ⇒ `{}`(店不知道 —— 认不出的驱动 / 库错是不是会自己好,不归本店判)。
|
|
59
|
+
* 永久码先于分类判(一只错不会两个都是);分类词只读判官 `storeFaultClassOf`,本文件不写码。
|
|
56
60
|
* 副作用 = 机器形:码取自错误对象,逐次计数 + 每进程每码一条 warn。观测绝不抛。
|
|
57
61
|
*/
|
|
58
62
|
export declare function foldCaughtFailure(err: unknown): CaughtFailure;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FILE_HISTORY_ERROR_CODES, FileHistoryStoreError } from "@sema-agent/core";
|
|
2
|
+
import { storeFaultClassOf } from "./plugins/store-fault.js";
|
|
2
3
|
const PERMANENT_STORE_FAILURE_CODES = {
|
|
3
4
|
blob_too_large_for_sql: {
|
|
4
5
|
note: "a blob is larger than this deployment's SQL snapshot-blob cap — the same file will fail every turn until the deployment changes",
|
|
@@ -32,12 +33,29 @@ function drainToMetrics() {
|
|
|
32
33
|
export function foldCaughtFailure(err) {
|
|
33
34
|
const message = err instanceof Error ? err.message : String(err);
|
|
34
35
|
const code = err?.code;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
const typedCode = typeof code === "string" && code !== "" ? code : undefined;
|
|
37
|
+
const permanentCode = typedCode !== undefined && isPermanentCode(typedCode) ? typedCode : undefined;
|
|
38
|
+
if (permanentCode !== undefined)
|
|
39
|
+
recordPermanent(permanentCode, message);
|
|
40
|
+
return {
|
|
41
|
+
text: typedCode !== undefined ? `(${typedCode}) ${message}` : message,
|
|
42
|
+
fault: permanentCode !== undefined ? { permanence: "terminal" } : faultOfClass(storeFaultClassOf(err)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function faultOfClass(cls) {
|
|
46
|
+
switch (cls) {
|
|
47
|
+
case "connection":
|
|
48
|
+
return { permanence: "transient" };
|
|
49
|
+
case "deployment":
|
|
50
|
+
return { permanence: "terminal" };
|
|
51
|
+
case "corrupt":
|
|
52
|
+
case "unclassified":
|
|
53
|
+
return {};
|
|
54
|
+
default: {
|
|
55
|
+
const unhandled = cls;
|
|
56
|
+
throw new Error(`store fault class without a file-history judgment: ${String(unhandled)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
41
59
|
}
|
|
42
60
|
function safely(record) {
|
|
43
61
|
try {
|
package/dist/fleet-client.d.ts
CHANGED
|
@@ -25,11 +25,15 @@
|
|
|
25
25
|
* 累计,每窗口(默认 60s,半开 [windowStartMs, windowEndMs))POST /api/fleet/usage。幂等键
|
|
26
26
|
* (worker,instanceId,windowStartMs) 重发安全;**发送失败 → merge 回累计器**,并入下一窗(失败窗从未入账,
|
|
27
27
|
* 向前合并不会双计 —— 只延迟,不丢量)。
|
|
28
|
+
* - **用量按中心凭证身份分箱([ref])**:累计器一个身份一只({@link createFleetUsageBins},分箱规则 = `createIdentityBins` 一只),
|
|
29
|
+
* tracer 记进记账身份那一箱(执行开始时钉下的身份;执行外 = 此刻身份);用量环只报此刻身份那一箱、经钉在它身份上的凭证发 —— 文件形凭证进程内换人后,上一个人窗里的用量
|
|
30
|
+
* 不在下一个人的凭证下上报(不记到别人头上);它留在原箱,那个身份回来时照常报出去,收尾时还压着就响亮一行。
|
|
28
31
|
* - 全部 fire-and-forget + swallow-guarded:fleet 面是运维旁路,绝不影响任务面;网络错误只 warn 一次/连败类。
|
|
29
32
|
*/
|
|
30
33
|
import { UsageReport, type CapabilityReading, type WebAdminCapabilities, type ModelRefusalsSignal } from "@sema-agent/settings-schema/fleet";
|
|
31
34
|
import { type EngineSignalFacts } from "./fleet/engine-signal.js";
|
|
32
35
|
import { type CenterEndpoint } from "./web-admin-client/center-request.js";
|
|
36
|
+
import { type CenterCredentialSource } from "./center-credential.js";
|
|
33
37
|
/** Bound on distinct principal×model keys per window — a runaway/multi-tenant burst collapses into
|
|
34
38
|
* `__other__` instead of growing unbounded (same posture as budget.ts cardinalityGuard). */
|
|
35
39
|
export declare const FLEET_USAGE_KEY_CAP = 1024;
|
|
@@ -71,6 +75,32 @@ export declare class FleetUsageAccumulator {
|
|
|
71
75
|
mergeBack(entries: UsageReport["entries"]): void;
|
|
72
76
|
get size(): number;
|
|
73
77
|
}
|
|
78
|
+
/** [ref] —— 一个中心凭证身份的用量箱:这个身份窗里的累计格,与钉在这个身份上的凭证(用量环只经它报这一箱)。 */
|
|
79
|
+
export interface FleetUsageBin {
|
|
80
|
+
readonly usage: FleetUsageAccumulator;
|
|
81
|
+
readonly credential: CenterCredentialSource;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* [ref] —— **按中心凭证身份分箱的用量账**(活对象 ⇒ `create*`)。tracer / side-query 记账写 {@link record}(此刻身份那一箱);
|
|
85
|
+
* 用量环读 {@link current}(此刻身份那一箱 + 钉住的凭证)。分箱规则只有 `createIdentityBins` 一只(与租约管理器、per-principal caps 同规则)。
|
|
86
|
+
*/
|
|
87
|
+
export interface FleetUsageBins {
|
|
88
|
+
/** 记进**记账身份**那一箱(执行里 = 执行开始时钉下的身份;执行外 = 此刻身份)。 */
|
|
89
|
+
record(principal: string | undefined, model: string, taskId: string, d: {
|
|
90
|
+
inputTokens: number;
|
|
91
|
+
outputTokens: number;
|
|
92
|
+
costMicroUsd?: number;
|
|
93
|
+
weightedTokens?: number;
|
|
94
|
+
quotaWeightAtUse?: number;
|
|
95
|
+
}): void;
|
|
96
|
+
/** 把这次执行的记账身份钉在此刻(tracer 在 `task.start` 调;规则属 `center-credential.ts` `pinAccountingIdentity`)。 */
|
|
97
|
+
pinExecution(): void;
|
|
98
|
+
/** 此刻身份那一箱(用量环上报读它)。 */
|
|
99
|
+
current(): FleetUsageBin;
|
|
100
|
+
/** **全部**箱里还压着的条目数(收尾那一行 warn 用:最后一窗之后还压着的,进程退出即丢 —— 别的身份的、身份未知的、或最后一窗没报成的,同一待遇)。 */
|
|
101
|
+
held(): number;
|
|
102
|
+
}
|
|
103
|
+
export declare function createFleetUsageBins(credential: CenterCredentialSource): FleetUsageBins;
|
|
74
104
|
export interface FleetClientOpts {
|
|
75
105
|
/** [ref]:凭证是**来源**(每条公告 / 用量 / 注销请求各取一次值),不是启动期烧进 header 的字符串。 */
|
|
76
106
|
center: CenterEndpoint & {
|
|
@@ -92,7 +122,8 @@ export interface FleetClientOpts {
|
|
|
92
122
|
* 只有 `declared`(交出宣告的位)与 `undeclared`(交 `undefined` = 老中心没宣告)是读数;`unreadable`(体读不出 / 不是对象 /
|
|
93
123
|
* `capabilities` 键在场而形坏)不是读数 ⇒ 不调,上一次读数与它的钟原样。失败的公告同样不调(没听到 ≠ 没有)。 */
|
|
94
124
|
onCenterCapabilities?: (caps: WebAdminCapabilities | undefined) => void;
|
|
95
|
-
|
|
125
|
+
/** [ref]:按凭证身份分箱的用量账(缺席 = 用量环不装)。 */
|
|
126
|
+
usage?: FleetUsageBins;
|
|
96
127
|
heartbeatMs?: number;
|
|
97
128
|
usageWindowMs?: number;
|
|
98
129
|
logger?: {
|
package/dist/fleet-client.js
CHANGED
|
@@ -2,6 +2,7 @@ import { InstanceAnnouncement, UsageReport, DEFAULT_HEARTBEAT_MS, readCapability
|
|
|
2
2
|
import { ENGINE_SIGNAL_KEY, buildEngineSignal, fleetMembershipKeys, heartbeatReadinessKeys } from "./fleet/engine-signal.js";
|
|
3
3
|
import { centerFetch, centerLaneConfigured } from "./web-admin-client/center-request.js";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
|
+
import { createIdentityBins, pinAccountingIdentity } from "./center-credential.js";
|
|
5
6
|
import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
|
|
6
7
|
export const FLEET_USAGE_KEY_CAP = 1024;
|
|
7
8
|
export const FLEET_CLIENT_FETCH_TIMEOUT_MS = 5000;
|
|
@@ -67,6 +68,20 @@ export class FleetUsageAccumulator {
|
|
|
67
68
|
return this.cells.size;
|
|
68
69
|
}
|
|
69
70
|
}
|
|
71
|
+
export function createFleetUsageBins(credential) {
|
|
72
|
+
const made = [];
|
|
73
|
+
const bins = createIdentityBins(credential, (pinned) => {
|
|
74
|
+
const bin = { usage: new FleetUsageAccumulator(), credential: pinned };
|
|
75
|
+
made.push(bin);
|
|
76
|
+
return bin;
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
record: (principal, model, taskId, d) => bins.accounting().usage.record(principal, model, taskId, d),
|
|
80
|
+
pinExecution: () => pinAccountingIdentity(credential),
|
|
81
|
+
current: bins.now,
|
|
82
|
+
held: () => made.reduce((n, b) => n + b.usage.size, 0),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
70
85
|
async function rejectionSnippet(res) {
|
|
71
86
|
try {
|
|
72
87
|
const text = (await res.text()).slice(0, 200).trim();
|
|
@@ -90,7 +105,7 @@ export function startFleetClient(opts) {
|
|
|
90
105
|
const f = opts.fetchImpl ?? fetch;
|
|
91
106
|
const heartbeatMs = opts.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
|
|
92
107
|
const usageWindowMs = opts.usageWindowMs ?? 60_000;
|
|
93
|
-
const send = (route, method, json) => centerFetch(
|
|
108
|
+
const send = (route, method, json, center = opts.center) => centerFetch(center, { kind: route }, { method, json, timeoutMs: FLEET_CLIENT_FETCH_TIMEOUT_MS, fetchImpl: f });
|
|
94
109
|
let stopped = false;
|
|
95
110
|
let failStreak = 0;
|
|
96
111
|
let unreadableStreak = 0;
|
|
@@ -177,20 +192,21 @@ export function startFleetClient(opts) {
|
|
|
177
192
|
const flushUsage = async () => {
|
|
178
193
|
if (!opts.usage)
|
|
179
194
|
return;
|
|
195
|
+
const bin = opts.usage.current();
|
|
180
196
|
const windowEndMs = Date.now();
|
|
181
|
-
const entries =
|
|
197
|
+
const entries = bin.usage.drain();
|
|
182
198
|
const start = windowStartMs;
|
|
183
199
|
windowStartMs = windowEndMs;
|
|
184
200
|
if (entries.length === 0)
|
|
185
201
|
return;
|
|
186
202
|
const report = { worker: opts.center.worker, instanceId: opts.instanceId, windowStartMs: start, windowEndMs, entries };
|
|
187
203
|
try {
|
|
188
|
-
const { res } = await send("fleet-usage", "POST", { reports: [report] });
|
|
204
|
+
const { res } = await send("fleet-usage", "POST", { reports: [report] }, { ...opts.center, credential: bin.credential });
|
|
189
205
|
if (!res.ok)
|
|
190
206
|
throw new Error(`usage ${res.status}${await rejectionSnippet(res)}`);
|
|
191
207
|
}
|
|
192
208
|
catch (err) {
|
|
193
|
-
|
|
209
|
+
bin.usage.mergeBack(entries);
|
|
194
210
|
opts.logger?.warn("fleet_usage_report_failed", { error: err instanceof Error ? err.message : String(err), entries: entries.length });
|
|
195
211
|
}
|
|
196
212
|
};
|
|
@@ -227,6 +243,10 @@ export function startFleetClient(opts) {
|
|
|
227
243
|
if (uw)
|
|
228
244
|
clearInterval(uw);
|
|
229
245
|
await flushUsage();
|
|
246
|
+
const held = opts.usage?.held() ?? 0;
|
|
247
|
+
if (held > 0) {
|
|
248
|
+
opts.logger?.warn("fleet_usage_unreported_identity", { entries: held, note: "usage still held at exit is dropped: it accrued under another center credential identity (never reported under the current one — it would be billed to the wrong account), under an unknown identity (credential unreadable), or its final report failed" });
|
|
249
|
+
}
|
|
230
250
|
};
|
|
231
251
|
return { announceNow, stopRouting, flushFinalUsage };
|
|
232
252
|
}
|
package/dist/fleet-lease.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type CenterEndpoint } from "./web-admin-client/center-request.js";
|
|
|
3
3
|
export declare const NOT_LEASE_TTL_MS: number;
|
|
4
4
|
/** center 判 budget exhausted 后,多久内的重复提交直接拒绝而不再打 center(防 per-request 打爆)。 */
|
|
5
5
|
export declare const EXHAUSTED_RETRY_MS: number;
|
|
6
|
-
/** principal 状态表上界(
|
|
6
|
+
/** principal 状态表上界(**每个身份箱各一份**;镜像 FLEET_USAGE_KEY_CAP 的姿势):超界丢最旧 —— 被丢的 principal 下次 admit
|
|
7
7
|
* 重新协商,一次多余 RTT,无正确性影响。 */
|
|
8
8
|
export declare const LEASE_PRINCIPAL_CAP = 4096;
|
|
9
9
|
/** 复审 O1:lease 协商 RTT 的硬上限——leaseDenied 在提交热路径同步 await 它,一个 accept-but-hang 的
|
|
@@ -87,8 +87,8 @@ export interface FleetLeaseOpts {
|
|
|
87
87
|
}
|
|
88
88
|
export declare class FleetLeaseManager {
|
|
89
89
|
private readonly opts;
|
|
90
|
-
|
|
91
|
-
private readonly
|
|
90
|
+
/** 记账身份的那一箱(惰建;分箱规则只有 `createIdentityBins` 一只,取箱口 `accounting`)。 */
|
|
91
|
+
private readonly binNow;
|
|
92
92
|
private readonly f;
|
|
93
93
|
private readonly now;
|
|
94
94
|
private failStreak;
|
|
@@ -97,6 +97,8 @@ export declare class FleetLeaseManager {
|
|
|
97
97
|
/** [ref] ③ 生效姿态(caps `centerWiring.failMode` 读的就是它 —— 与执行的那一只同源)。 */
|
|
98
98
|
readonly failMode: LeaseFailMode;
|
|
99
99
|
constructor(opts: FleetLeaseOpts);
|
|
100
|
+
/** [ref]:把这次执行的记账身份钉在此刻(tracer 在执行的第一帧 `task.start` 调;规则属 `center-credential.ts` `pinAccountingIdentity`)。 */
|
|
101
|
+
pinExecution(): void;
|
|
100
102
|
/**
|
|
101
103
|
* Admission check for one billed submission. Fast path is synchronous-shaped (live lease / fresh negative
|
|
102
104
|
* cache → no await beyond the microtask); only first-touch / expiry / exhaustion pays a center RTT.
|
|
@@ -119,6 +121,7 @@ export declare class FleetLeaseManager {
|
|
|
119
121
|
/**
|
|
120
122
|
* [ref] ② —— 调用方这一刻的租约读数(caps `centerWiring.caller` / `leaseHealth` 的唯一来源;7.105.0 的 `peek()` 退役:它只回 kind、
|
|
121
123
|
* 不看 `until`,过期条目照回原 kind)。**纯读**:不打中心、不写状态、不动 LRU 序(`Map.get` 不改插入序)—— caps 路由自己不 admit。
|
|
124
|
+
* 读的是**此刻凭证身份**那一箱([ref];取箱要读一次凭证,首次见到的身份惰建一只空箱 —— 空箱不是状态)。
|
|
122
125
|
* 规则表(设计稿 v1.1 §2②,逐行钉在 `test/fleet-lease.test.ts`):
|
|
123
126
|
* · principal 缺席 / 无条目(含被 LRU 逐出)⇒ `unobserved`;
|
|
124
127
|
* · `not-lease` 且 `now < until` ⇒ `not-enforced`;到期 ⇒ `unobserved`;
|
|
@@ -135,6 +138,7 @@ export declare class FleetLeaseManager {
|
|
|
135
138
|
/** [ref] ③:两条连接级失败臂的**唯一**收尾 —— 按姿态穷举(加一种姿态 = 编译红)。 */
|
|
136
139
|
private connectionFailure;
|
|
137
140
|
private set;
|
|
141
|
+
/** 单飞在箱内:同一身份同一 principal 的并发谈判共用一条;换了人的谈判各发各的([ref])。 */
|
|
138
142
|
private issueOrRenew;
|
|
139
143
|
private issueOrRenewInner;
|
|
140
144
|
}
|