@sema-agent/server 7.148.0 → 7.149.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 +429 -0
- package/README.md +16 -1
- package/README.zh-CN.md +4 -0
- package/USAGE.md +89 -55
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +32 -32
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/resolve-spec.js +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +2 -1
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +5 -4
- package/dist/capability-facts.js +3 -14
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +36 -32
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +37 -5
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/admission.js +3 -3
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/claim-heartbeat.d.ts +3 -3
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +5 -3
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +30 -11
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +3 -2
- package/dist/http/owner-access.js +2 -3
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +8 -2
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +244 -92
- package/dist/http/route-ctx.d.ts +6 -0
- package/dist/http/routes/a2a-serve.js +2 -0
- package/dist/http/routes/approvals-assistant.js +3 -2
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/runs.js +10 -7
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +46 -34
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/tasks.js +127 -93
- package/dist/http/server.d.ts +4 -5
- package/dist/http/server.js +30 -5
- package/dist/http/submit-ctx.d.ts +1 -0
- package/dist/http/submit-ctx.js +2 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leg-facts.d.ts +7 -2
- package/dist/leg-facts.js +1 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/fail-open.d.ts +14 -2
- package/dist/observability/fail-open.js +14 -2
- 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/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +3 -2
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +50 -22
- package/dist/plugins/local-checkpoint-store.d.ts +4 -0
- package/dist/plugins/local-checkpoint-store.js +2 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +47 -21
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +46 -20
- package/dist/plugins/one-time-migrations.js +102 -79
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +10 -12
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +1 -7
- package/dist/plugins/remote-env-e2b.js +10 -7
- package/dist/plugins/remote-env-host.d.ts +50 -9
- package/dist/plugins/remote-env-host.js +77 -20
- package/dist/plugins/remote-env-k8s.js +4 -2
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +8 -23
- package/dist/plugins/run-store-sql.d.ts +26 -7
- package/dist/plugins/run-store-sql.js +44 -16
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +3 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +4 -3
- package/dist/plugins/schema-consistency.d.ts +89 -5
- package/dist/plugins/schema-consistency.js +176 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +16 -0
- package/dist/plugins/stop-then-start-sentence.js +7 -0
- package/dist/plugins/store-backend.d.ts +22 -8
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +33 -3
- package/dist/plugins/store-contracts.js +16 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +18 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +19 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +3 -2
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
- package/dist/stores-sql/checkpoint-store-sql.js +3 -5
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +23 -7
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.js +1 -0
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +44 -36
- package/dist/tool-approval.js +66 -47
- package/dist/trace/engine-notice-wire.d.ts +24 -21
- package/dist/trace/engine-notice-wire.js +7 -3
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +4 -2
- package/dist/trace/project.js +8 -7
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +131 -52
- package/dist/trace/self-minted-wire.js +71 -39
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +3 -1
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/dist/plain-string-record.js +0 -70
package/dist/runs.js
CHANGED
|
@@ -2,6 +2,7 @@ import { runWithVerification, runCascade, defaultTaskRegistry, taskNotificationD
|
|
|
2
2
|
import { atHostBoundary, failureCodeOf, sealResultForExit, outwardPlane, outwardThrownMessage } from "./terminal.js";
|
|
3
3
|
import { voidVerifierPauseCard } from "./verifier-pause-card.js";
|
|
4
4
|
import { voidPendingCard } from "./card-void.js";
|
|
5
|
+
import { commitParkRow } from "./park-row-commit.js";
|
|
5
6
|
import { NOT_IN_ROSTER, entryProblemsFacts } from "./entry-problems.js";
|
|
6
7
|
import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
|
|
7
8
|
import { TOOL_DECLARATION_HTTP_STATUS } from "./tool-declaration-http-status.js";
|
|
@@ -29,6 +30,7 @@ import { recordFailOpen } from "./observability/fail-open.js";
|
|
|
29
30
|
import { BASELINE_IO_DEADLINE_MS, IoDeadlineExceeded, withinIoDeadline } from "./instruction-source-notice.js";
|
|
30
31
|
import { errorText as errText } from "./observability/err-text.js";
|
|
31
32
|
import { LIVENESS_HEARTBEAT_MS } from "./config-invariants.js";
|
|
33
|
+
import { compositionLegRoot } from "./task-cwd.js";
|
|
32
34
|
export async function backgroundAgentOutput(registry, handle, access, agentStore) {
|
|
33
35
|
const row = registry.getAccessibleTask(handle, access);
|
|
34
36
|
if (!row || row.type !== "background_agent") {
|
|
@@ -302,12 +304,18 @@ export async function graftParkToolCallId(result, cs) {
|
|
|
302
304
|
const id = await parkToolCallId(result, cs);
|
|
303
305
|
return id === undefined ? result : { ...result, toolCallId: id };
|
|
304
306
|
}
|
|
307
|
+
export function heldParkOf(result) {
|
|
308
|
+
if (result.terminal.kind !== "paused")
|
|
309
|
+
return undefined;
|
|
310
|
+
const word = outwardPlane(result.terminal).status;
|
|
311
|
+
return isParkLedgerEventType(word) ? { word, token: result.terminal.token } : undefined;
|
|
312
|
+
}
|
|
305
313
|
export async function voidHeldParkCard(held, hostSessionId, cards, leg) {
|
|
306
314
|
if (held?.terminal.kind !== "paused")
|
|
307
315
|
return;
|
|
308
316
|
await voidPendingCard({ token: held.terminal.token, sessionId: hostSessionId }, cards, "server.park.held-card-void-failed", `leg=${leg}`);
|
|
309
317
|
}
|
|
310
|
-
export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, checkpointCards, pendingEngineNotices, trustedInternals, userInputLedger, acceptance, workspaceRecorder, runSkills) {
|
|
318
|
+
export async function runInBackground(runner, spec, runStore, taskId, parkCommit, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, checkpointCards, pendingEngineNotices, trustedInternals, userInputLedger, acceptance, workspaceRecorder, runSkills, registeredCwd) {
|
|
311
319
|
const startedAt = Date.now();
|
|
312
320
|
metrics?.addGauge("runs_active", 1);
|
|
313
321
|
fleetPublisher?.onStart();
|
|
@@ -345,12 +353,12 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
345
353
|
if (approval) {
|
|
346
354
|
const coordinator = approval.coordinator;
|
|
347
355
|
spec.onAsk = approvalLeg.unattended
|
|
348
|
-
? coordinator.boundUnattendedAsk({ owner: elicitOwner ?? owner, taskId, ...(spec.sessionId ? { sessionId: spec.sessionId } : {}),
|
|
356
|
+
? coordinator.boundUnattendedAsk({ owner: elicitOwner ?? owner, taskId, ...(spec.sessionId ? { sessionId: spec.sessionId } : {}), ...approval.origin }, approvalLeg.unattended)
|
|
349
357
|
: coordinator.boundAsk({
|
|
350
358
|
owner: elicitOwner ?? owner,
|
|
351
359
|
taskId,
|
|
352
360
|
...(spec.sessionId ? { sessionId: spec.sessionId } : {}),
|
|
353
|
-
|
|
361
|
+
...approval.origin,
|
|
354
362
|
...(approvalLeg.active ? { legKey: "", ...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}) } : {}),
|
|
355
363
|
});
|
|
356
364
|
}
|
|
@@ -375,23 +383,26 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
375
383
|
let legLive = true;
|
|
376
384
|
const notifiedKeys = new NotifiedKeys();
|
|
377
385
|
const subagentHandleEvictions = [];
|
|
378
|
-
const
|
|
379
|
-
suspended: (sealed) => runStore.setSuspended(taskId, sealed),
|
|
380
|
-
needs_review: (sealed) => runStore.setNeedsReview(taskId, sealed),
|
|
381
|
-
};
|
|
382
|
-
const parkLeg = async (word, res) => {
|
|
386
|
+
const parkLeg = async ({ word, token }, res) => {
|
|
383
387
|
const grafted = await graftParkToolCallId(res, checkpointCards);
|
|
384
388
|
await sink.appendParked(word, grafted);
|
|
385
389
|
const sealed = sealResultForExit(grafted);
|
|
386
390
|
reached = { kind: word, result: sealed };
|
|
387
|
-
await
|
|
391
|
+
const committed = await commitParkRow({
|
|
392
|
+
runStore, taskId, word, result: sealed, card: { token, sessionId: spec.sessionId }, cards: checkpointCards, leg: "bg",
|
|
393
|
+
legId: parkCommit.legId, staleWindowMs: parkCommit.runStaleSec === undefined ? undefined : parkCommit.runStaleSec * 1000,
|
|
394
|
+
});
|
|
395
|
+
if (committed.kind === "ended") {
|
|
396
|
+
reached = undefined;
|
|
397
|
+
settledAsCancel = committed.row?.errorCode === CANCELLED_CODE;
|
|
398
|
+
}
|
|
388
399
|
acceptance?.settle(sealed);
|
|
389
400
|
};
|
|
390
401
|
const settleLegResult = async (engineResult) => {
|
|
391
402
|
const result = atHostBoundary(engineResult);
|
|
392
|
-
const
|
|
393
|
-
if (
|
|
394
|
-
await parkLeg(
|
|
403
|
+
const held = heldParkOf(result);
|
|
404
|
+
if (held !== undefined) {
|
|
405
|
+
await parkLeg(held, result);
|
|
395
406
|
return;
|
|
396
407
|
}
|
|
397
408
|
const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, result) : result;
|
|
@@ -416,10 +427,13 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
416
427
|
const hitlTransport = createHitlTransport({ ledger: append });
|
|
417
428
|
const unregisterEngineNotice = registerEngineNoticeLeg({
|
|
418
429
|
sessionId: spec.sessionId,
|
|
430
|
+
taskId,
|
|
419
431
|
...(pendingEngineNotices !== undefined ? { pending: pendingEngineNotices } : {}),
|
|
420
432
|
durable: (row) => append("engine_notice", row).then(() => true, () => (recordFailOpen("server.engine-notice.durable-append-failed", `leg=bg code=${row.code} task=${taskId}`), false)),
|
|
421
433
|
});
|
|
422
434
|
try {
|
|
435
|
+
if (typeof parkCommit?.legId !== "string" || parkCommit.legId === "")
|
|
436
|
+
throw new TypeError(`runInBackground: run ${taskId} was started without the holding leg its claim handed back (parkCommit.legId) — refusing to start a run whose park could never be committed`);
|
|
423
437
|
if (userInputLedger && spec.sessionId)
|
|
424
438
|
await userInputLedger.announceUndrained({ sessionId: spec.sessionId, taskId });
|
|
425
439
|
await emitPendingWorkflowCompletions(workflowCompletionInbox, spec.sessionId, elicitOwner ?? owner, (frame) => {
|
|
@@ -476,10 +490,11 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
476
490
|
void sink.appendStatus(e).catch((err) => completionDiagLog?.("status_event_append_failed", { route: "bg-run-composite", taskId, err: err instanceof Error ? err.message : String(err) }));
|
|
477
491
|
},
|
|
478
492
|
};
|
|
493
|
+
const compositeInternals = { ...fwdInternals, ...statusSeat, ...compositionLegRoot(registeredCwd) };
|
|
479
494
|
const compositeLeg = verify !== undefined
|
|
480
|
-
? () => runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify,
|
|
495
|
+
? () => runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, compositeInternals)
|
|
481
496
|
: cascade !== undefined
|
|
482
|
-
? () => runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade,
|
|
497
|
+
? () => runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, compositeInternals)
|
|
483
498
|
: undefined;
|
|
484
499
|
if (compositeLeg !== undefined) {
|
|
485
500
|
const result = await compositeLeg();
|
|
@@ -550,7 +565,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
550
565
|
owner: elicitOwner ?? owner,
|
|
551
566
|
abortSignal: cancelCtrl.signal,
|
|
552
567
|
...(spec.sessionId ? { sessionId: spec.sessionId } : {}),
|
|
553
|
-
|
|
568
|
+
...approval.origin,
|
|
554
569
|
family: "approval_request",
|
|
555
570
|
transport: hitlTransport,
|
|
556
571
|
legKey: "",
|
|
@@ -599,9 +614,12 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
599
614
|
acceptance?.settle(reached.result);
|
|
600
615
|
}
|
|
601
616
|
else if (reached !== undefined) {
|
|
602
|
-
await parkRowWrite[reached.kind](reached.result);
|
|
603
617
|
acceptance?.settle(reached.result);
|
|
604
618
|
}
|
|
619
|
+
else if (thrownCancelCause === undefined && observedDone !== undefined && heldParkOf(atHostBoundary(observedDone)) !== undefined) {
|
|
620
|
+
recordFailOpen("server.runs.terminal-ledger-write-failed", `arm=park-held ${message}`);
|
|
621
|
+
await settleLegResult(observedDone);
|
|
622
|
+
}
|
|
605
623
|
else if (thrownCancelCause !== undefined && observedDone === undefined) {
|
|
606
624
|
const c = cancelledResult(thrownCancelCause);
|
|
607
625
|
settledAsCancel = failureCodeOf(c.terminal) === CANCELLED_CODE;
|
package/dist/security.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export interface SessionRowCellsWriter {
|
|
|
75
75
|
* 而 `DELETE /v1/sessions/:id`(删整条会话)不在其中 —— 同一风险形、爆炸半径**更大**,却没门。
|
|
76
76
|
*
|
|
77
77
|
* 风险形逐字同源(守卫自己的注释):「无 service token + 未开 `ALLOW_UNAUTHED_WRITES`」这一形下
|
|
78
|
-
* fleet 写档(面表 fleet 列的 `dev_open` 格,`owner-faces.ts` 的 `listFilterOf`)判 fleet-wide ⇒
|
|
78
|
+
* fleet 写档(面表 fleet 列的 `dev_open` 格,`owner-faces.ts` 的 `listFilterOf`)判 fleet-wide ⇒ 集群内**任意**不报主体的调用方可对**任意** session 动手(报了主体的调用方只动得到那位主体的,`principal-gate.ts` 的 `actsForDeployment`)。
|
|
79
79
|
* policy 腿担心的是「forged principal 能 tighten/DoS 一个会话的工具」;DELETE 直接把会话删掉。
|
|
80
80
|
*
|
|
81
81
|
* ⇒ 枚举改**族判定**:新增一条破坏性会话路由时自动落进门内,不必记得回来加白名单。
|
|
@@ -190,7 +190,7 @@ export type OwnerAwareSessionStore = SessionStore & {
|
|
|
190
190
|
/** 执行席(规则在 `execution-placement.ts`):根会话行上这一位住客的那一格的唯一读口(`read`)与 place / markMirror 两只写口。三店都有;两只包装原样转发。
|
|
191
191
|
* 缺席 = 会话店没有执行放置(只有不带它的测试走廊 / 嵌入形),调用方各按自己的缺席臂处置。 */
|
|
192
192
|
executionPlacement?: ExecutionPlacementSeat;
|
|
193
|
-
/**
|
|
193
|
+
/** **「被占着」的头读**(读者:`DELETE /v1/sessions/:id` 的属主门 · 认领面 · 同步推送三口的门 —— 都问「这个 id 被占着吗」,经 `session-incarnation.ts` 的 `deletionHeadReadOf` 一只取法):
|
|
194
194
|
* 活行 = {@link headOf} 同答;外加在删的行(会话三态 活 → 在删 → 没了 的中间一态,律在 `session-deletion.ts`;local 另有删序停在第一步之后的标记格)的属主与住客 ——
|
|
195
195
|
* 开始删它的那位属主重发删除(= 接管)就收得完,别的属主得到与「不在」同一句(不可探测)。三只店都交。 */
|
|
196
196
|
deletionHeadOf?: (sessionId: string) => Promise<SessionHead | undefined>;
|
|
@@ -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 命令 ⇒ 严格名单(十二名基础名单,`[names]` 臂;请求的继承词只给工具 shell、对 hook 不作用)
|
|
16
|
+
* 语言服务器 / 配方脚本 ⇒ 形状剥 `"scrub"`;请求带来的 settings hooks 命令 ⇒ 严格名单(十二名基础名单,`[names]` 臂;请求的继承词只给工具 shell、对 hook 不作用)∪ 部署方 `HOOK_ENV_ALLOW` 点名放回、请求的有效环境层作授予叠在继承结果之上(引擎的 `hostChildEnv`);部署方装在宿主上的 CLI(`docker` / `adb`,命令本身
|
|
17
17
|
* 在容器 / 设备里跑、那边的 env 由 `-e` 显式给)⇒ `"all"` —— 只扣本服务自有凭据,CLI 与它的凭据助手 / 插件要读的生态变量照交(形状剥会剥
|
|
18
18
|
* `ADB_VENDOR_KEYS` / `AWS_SECRET_ACCESS_KEY` 一类,实测)。三个继承词只在引擎一处判:
|
|
19
19
|
* · `"all"` = 整份环境 **减** 本进程声明的自有凭据名;`"scrub"`(缺省)= 形状剥 ∪ 该名单;`[names]` = 严格白名单,点到名单中的名 ⇒ 引擎响亮拒
|
|
@@ -29,24 +29,27 @@
|
|
|
29
29
|
* `loadConfig()` 的**声明前提**({@link ownCredentialDeclarationRefusal}:起服表的名没全扣 ⇒ `loadConfig` 抛,拒句点名 {@link declareServerWithheldEnv} —— 「先声明」不再只是入口里的位置契约,
|
|
30
30
|
* 不经入口直接调 `loadConfig` 的进程〔包根嵌入方 / bench / 探针脚本〕得先声明,声明对它们才不是空的)与能力词 `inherit_env.own_credentials_withheld` 在本进程的事实
|
|
31
31
|
* {@link ownCredentialsWithheld}(当前生效配置应声明的名全被扣;活配置经 {@link bindOwnCredentialsConfig} 在 `loadConfig()` 之后绑一次;未绑 / 没扣 ⇒ false)。
|
|
32
|
-
* · **值臂**(core 7.52.0 [ref]):声明是一张表 `{ names, values }`。`values` =
|
|
33
|
-
*
|
|
32
|
+
* · **值臂**(core 7.52.0 [ref]):声明是一张表 `{ names, values }`。`values` = 本进程持有凭据的变量(目录 secret 名 ∪ 模型条目 `apiKeyEnv`,{@link serverCredentialValueSources})
|
|
33
|
+
* 此刻装着的**每一枚**本服务凭据 —— 颗粒按本服务读这只变量的那一只解析器定(目录行的 `credentialForm`:整值总在内,`SERVICE_AUTH_TOKENS` 另加每一枚令牌、
|
|
34
|
+
* `APPROVAL_HMAC_KEYS` 另加每一把钥;{@link credentialsHeldBy})—— 引擎按整串比,所以一枚令牌换了个变量名放着与原变量同扣,部分重叠 / 前后缀不同的值不扣。
|
|
35
|
+
* 逐枚交给引擎的同一只判;引擎判「不是凭据值」的那一枚不扣、名照扣、不拒启、点名那个变量一次。能力词 `inherit_env.own_credential_values_withheld` 的事实 = {@link ownCredentialValuesWithheld}(位真 ⇔ 值臂每一枚都真扣)。
|
|
36
|
+
* 「这个变量是不是本服务的凭据」(名是,或值是)只有一只读法 {@link ownCredentialVariable}(判官 = 引擎此刻的声明):名单型旋钮的解析器与声明增长时的重判都问它。
|
|
34
37
|
* · 调用方显式传入的 env(`cfg.env` / 单次命令的 `env` / 沙箱的点名放回)不是继承,照旧原样叠在组合结果之上 —— 那是适配器自己的凭据通道。
|
|
35
38
|
* 各腿只调 {@link hostSubprocessEnv} / {@link gitSubprocessEnv} —— 不许再有内联拼装、不许再有 `{ ...process.env }`、不许本仓再判任何一个继承词、不许起子进程不交 env
|
|
36
39
|
* (类级门与普查门在 `test/secret-env-scrub-host-legs.test.ts`)。
|
|
37
40
|
*
|
|
38
41
|
* ## 点名放行([ref] / [ref],一只谓词各处用)
|
|
39
42
|
* {@link allowlistedEnv} 是本仓「一张名单点到哪些 env 键,就把它们从源 env **原样**交给沙箱」的唯一实现。调用:沙箱带外 env 的 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` /
|
|
40
|
-
* `K8S_SANDBOX_ENV`(`config.ts`)与名单解析器的「在不在」判。宿主子进程的剥后放回(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)不经它 —— 那是引擎的严格名单臂(上一段)。
|
|
43
|
+
* `K8S_SANDBOX_ENV`(`config.ts`)与名单解析器的「在不在」判。宿主子进程的剥后放回(`LSP_ENV_ALLOW` / `HOOK_ENV_ALLOW` / `BAKE_ENV_ALLOW`)不经它 —— 那是引擎的严格名单臂(上一段)。
|
|
41
44
|
* host shell 的 `inheritEnv:[names]`(hermetic 形)7.129.0 起由引擎读(与 core `NodeExecutionEnv` 同名选项同义,点到自有凭据名即拒),不经本谓词。
|
|
42
45
|
* 名单型旋钮的**名单**另由 `config.ts` 的同一只解析器 `parseEnvNameAllowlist` 判形(形错 / 点名名单型旋钮 / 点名本服务自有凭据名 ⇒ 拒启;源里没有的名按序号 warn),本函数只管取值。
|
|
43
46
|
* 「点名」在各处同义:点到的键原样带值(空串值照带),密钥形也照过(点名 = 部署方显式承担);源里没有的名不产生条目。
|
|
44
|
-
* 剥后放回形(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)的放回本身也经引擎:{@link composeHostSubprocessEnv} 把名单交引擎的严格名单臂(`inheritedHostEnv([names])`)叠在
|
|
47
|
+
* 剥后放回形(`LSP_ENV_ALLOW` / `HOOK_ENV_ALLOW` / `BAKE_ENV_ALLOW`)的放回本身也经引擎:{@link composeHostSubprocessEnv} 把名单交引擎的严格名单臂(`inheritedHostEnv([names])`)叠在
|
|
45
48
|
* 继承结果之上 ⇒ 点到本服务自有凭据名 = 引擎在**组合这一刻**抛(语言服务器在构造时组合一次、之后 spawn 用那份静态记录;bake-runner 每次 spawn 组合;
|
|
46
49
|
* 起服时解析器 `parseEnvNameAllowlist` 已用同一只判 `inheritEnvPolicyProblem` 拒过 —— 解析器对五只名单一律拒,点到**解析时已声明**的自有凭据名的沙箱名单因此
|
|
47
50
|
* 到不了 {@link allowlistedEnv} 取值这一步;声明之后增长(中心 / config.d 模型条目的 `apiKeyEnv` 换代并进)时,换代声明对活配置里每只名单此刻交出去的名用同一只判
|
|
48
|
-
* 重判一次 —— 命中 ⇒ 撤回这次增长、候选整拒,已装好的沙箱 env / 语言服务器 env 不变成本服务的凭据,`config.ts` `ownCredentialHitsInForce`)
|
|
49
|
-
*
|
|
51
|
+
* 重判一次 —— 命中 ⇒ 撤回这次增长、候选整拒,已装好的沙箱 env / 语言服务器 env 不变成本服务的凭据,`config.ts` `ownCredentialHitsInForce`)。
|
|
52
|
+
* 解析器与重判问的是「点到的变量是不是本服务的凭据」—— 名是,**或值是**(另一个名下的副本,{@link ownCredentialVariable})—— 都拒;组合这一刻引擎照样扣下值副本(不变量)。本仓不另判、不比值。
|
|
50
53
|
*/
|
|
51
54
|
import { type InheritEnvPolicy, type SecretEnvFinding } from "@sema-agent/core";
|
|
52
55
|
import type { ServiceConfig } from "./config-types.js";
|
|
@@ -61,8 +64,9 @@ export type ModelApiKeyEnvTable = Readonly<Record<string, string>>;
|
|
|
61
64
|
*/
|
|
62
65
|
export declare function serverWithheldEnvNames(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
63
66
|
/**
|
|
64
|
-
* 本进程**持有凭据值**的变量名(值臂的来源;core 7.52.0 [ref]):目录 secret 名 ∪ 模型条目 `apiKeyEnv` 的值 ——
|
|
65
|
-
*
|
|
67
|
+
* 本进程**持有凭据值**的变量名(值臂的来源;core 7.52.0 [ref]):目录 secret 名 ∪ 模型条目 `apiKeyEnv` 的值 —— 名单与值臂两臂同一张表派生,只少不在目录里的名单型旋钮名
|
|
68
|
+
* (今天 = bake-runner 进程的 `BAKE_ENV_ALLOW`);目录里的名单型旋钮行是 secret 行,原文按凭据类整值对待(行上 `credentialForm: "whole"`)。每个来源装着几枚凭据
|
|
69
|
+
* 由目录行的颗粒定({@link credentialsHeldBy})。纯函数,排序只为读数稳定。
|
|
66
70
|
*/
|
|
67
71
|
export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
68
72
|
/**
|
|
@@ -72,15 +76,14 @@ export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKe
|
|
|
72
76
|
* · **名臂**:此前声明过的 ∪ 这张表的 {@link serverWithheldEnvNames}。引擎对坏形名 / 运行时回填名(`PATH` / `HOME` …)响亮抛且声明保持原状 —— 本函数不接住:
|
|
73
77
|
* 起服那次抛 = 拒启;换代那次抛 = 候选整拒(`applyEffective` 抛,与 stage 抛同路)。第一次交表带着名臂与**此前已收下的值**(引擎收过、env 不变 ⇒ 同值照收),
|
|
74
78
|
* 所以这一次抛只可能是名的问题。
|
|
75
|
-
* · **值臂**:{@link serverCredentialValueSources}
|
|
76
|
-
*
|
|
77
|
-
*
|
|
79
|
+
* · **值臂**:{@link serverCredentialValueSources} 的每一个变量此刻装着的每一枚凭据({@link credentialsHeldBy}:整值,列表形另加每一枚),逐枚交给引擎**同一只**判
|
|
80
|
+
* (不另写「什么算凭据值」的第二份判据):引擎收下 ⇒ 记进值臂;
|
|
81
|
+
* 引擎拒(不是凭据值:空白 / 占位词 / 一个字符重复 / 不足 8 字符;或它是每个子进程都会拿到的变量的值)⇒ 这一枚不扣(同一变量的其余几枚照收)、名照扣、**不拒启**(与引擎自己的收值口
|
|
82
|
+
* `holdOwnCredentialValues` 同一姿势:不收 + 说一次),经留痕座结构化点名那个变量一次(它装着几枚被拒都是一行)—— `warn` 行 `own_credential_value_not_withheld { name, reason }`
|
|
78
83
|
* ({@link recordOwnCredentialValueNotWithheld};`reason` 是引擎拒句原文,从不回显值;起服第一步还没有 logger 时当拍经进程默认的 JSON 行日志器写 stderr,不缓冲),能力位 `inherit_env.own_credential_values_withheld`
|
|
79
84
|
* 因此答 false(位真 ⇔ 值臂真扣)。引擎拒一个值时声明保持上一份,所以逐个交表的中间态都是上一份的超集。
|
|
80
|
-
* 🔴
|
|
81
|
-
*
|
|
82
|
-
* 本仓不在引擎外面吞 console、也不拿声明口去抢引擎的「每进程每值说一次」账(下游不改上游座的语义);等 core 给席或让声明口的拒收记作已说。读数与翻面条件:
|
|
83
|
-
* `test/own-credential-value-boot-lines.test.ts`(真主进程;零裸行的期望以 `it.fails` 立着,core 改形那天转红再翻成常规钉)。
|
|
85
|
+
* 🔴 **这一行是唯一的一行**:值是网关密钥(`MODEL_API_KEY` / `ANTHROPIC_API_KEY`)时,引擎在适配器构造口收下同一个值,声明口拒过的值它记作已说、不再说第二遍
|
|
86
|
+
* (core 7.62.0 起;此前那里另有一行引擎的裸 `console.warn`)。读数:`test/own-credential-value-boot-lines.test.ts`(真主进程,零裸文本行)。
|
|
84
87
|
* 返回声明的名单(只名、不值)。
|
|
85
88
|
*/
|
|
86
89
|
export declare function declareServerWithheldEnv(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
@@ -113,10 +116,20 @@ export declare function ownCredentialDeclarationRefusal(): string | undefined;
|
|
|
113
116
|
*/
|
|
114
117
|
export declare function ownCredentialsWithheld(): boolean;
|
|
115
118
|
/**
|
|
116
|
-
* 能力词 `inherit_env.own_credential_values_withheld`(core 7.52.0)在本进程的事实:当前生效配置的每一个凭据值来源({@link serverCredentialValueSources})
|
|
117
|
-
*
|
|
119
|
+
* 能力词 `inherit_env.own_credential_values_withheld`(core 7.52.0)在本进程的事实:当前生效配置的每一个凭据值来源({@link serverCredentialValueSources})此刻装着的
|
|
120
|
+
* **每一枚**凭据({@link credentialsHeldBy})都已交进值臂、被引擎收下。位真 ⇔ 值臂真扣:有一枚被引擎判「扣不了」⇒ false;env 里没有值的来源没有东西可扣,不影响事实。
|
|
121
|
+
* 没绑活配置 / 没声明过 ⇒ false。
|
|
118
122
|
*/
|
|
119
123
|
export declare function ownCredentialValuesWithheld(): boolean;
|
|
124
|
+
/** {@link ownCredentialVariable} 的答案:这个变量是本服务的凭据,因为它的**名**是声明过的自有凭据名,还是它此刻的**值**是本服务的某一枚凭据(另一个名下的副本)。 */
|
|
125
|
+
export type OwnCredentialHold = "name" | "value";
|
|
126
|
+
/**
|
|
127
|
+
* 「本进程环境里这个变量是不是本服务的自有凭据」的**唯一读法**(判官 = 引擎此刻的声明;本仓不比值、不持值):名是声明过的自有凭据名 ⇒ `"name"`;
|
|
128
|
+
* 名不是,而它此刻的值等于引擎扣着的某一枚凭据(值臂按颗粒逐枚声明的,或引擎自己收下的)⇒ `"value"`;都不是 / 变量不在本进程环境里 ⇒ `undefined`。
|
|
129
|
+
* 值的那一半读引擎严格名单臂对这一名的扣留(`inheritedHostEnv([name])` 的 `own-credential` 发现 —— 严格名单不做形状剥,那里扣下的只有自有凭据一类)。
|
|
130
|
+
* 调用方先判过名形(引擎对坏形名同样答「有问题」,会被读成 `"name"`)。读者:名单型旋钮的解析器 ② 与声明增长时的重判(`config.ts` `ownCredentialHitsInForce`)。
|
|
131
|
+
*/
|
|
132
|
+
export declare function ownCredentialVariable(name: string): OwnCredentialHold | undefined;
|
|
120
133
|
/** 测试缝:撤回声明(引擎侧重声明为空表)、忘掉两份声明镜像、点名记账与活配置;生产代码从不调它。 */
|
|
121
134
|
export declare function __resetServerWithheldEnvForTest(): void;
|
|
122
135
|
/** 一条宿主腿要什么:现场(留痕)、继承策略(缺省 `"scrub"`;按头注「腿的继承词」一条由调用点定)、部署方点名放回的键、调用方显式交的授予(按序叠在继承之上,后者赢)。 */
|
|
@@ -133,8 +146,9 @@ export interface HostSubprocessEnvRequest {
|
|
|
133
146
|
* 不是剥除,不计数、不披露。源 env 恒为本进程 env(引擎只读 `process.env`,本仓没有第二个源)。
|
|
134
147
|
*
|
|
135
148
|
* 九条宿主腿都只经它(直接或经 {@link gitSubprocessEnv}):host shell(三个继承词原样交给 `policy`)、project-memory / center-plugins / leader 三条 git 腿、
|
|
136
|
-
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(十二名严格名单 + 有效环境层作授予)。`allow` 只在**有部署级出路**的现场非空(今天 = `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`,
|
|
137
|
-
* 它们的现场在 `SECRET_ENV_SCRUB_SITES` 里登记了 `remedy`,披露行因此是 warn + 放行名单 +
|
|
149
|
+
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(十二名严格名单 ∪ `HOOK_ENV_ALLOW` 放回 + 有效环境层作授予)。`allow` 只在**有部署级出路**的现场非空(今天 = `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`,
|
|
150
|
+
* 它们的现场在 `SECRET_ENV_SCRUB_SITES` 里登记了 `remedy`,披露行因此是 warn + 放行名单 + 出路句;settings hooks 的 `HOOK_ENV_ALLOW` 也走这只槽,但那条腿是严格名单,
|
|
151
|
+
* finding 只有自有凭据的扣留、任何名单都放不回,现场登记 `remedy: null`,理由在登记处);其余现场传 `[]`。
|
|
138
152
|
* 「扣了哪些名字」一腿一个读者:host shell 腿由引擎读 `withheldEnv` 说(`inherit_env.names_withheld`,现场登记 `told:"engine-notice"` ⇒ 本函数对它只计数、不写披露行);
|
|
139
153
|
* 其余各条腿(含 host 适配器类里不交给引擎的 git worktree 基环境,`HostEnvConfig.gitBase`)引擎没有读者,披露行是唯一的读法(`told:"log"`)。
|
|
140
154
|
* 返回 `NodeJS.ProcessEnv` 形(值可能是 `undefined` 的键本来就不会进子进程);调用方显式要交的键(`LC_ALL`、`cfg.env` …)叠在它之上。
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { declareWithheldEnv, hostChildEnv, inheritEnvPolicyProblem, inheritedHostEnv } from "@sema-agent/core";
|
|
2
2
|
import { CONFIG_CATALOG } from "./config-catalog.js";
|
|
3
|
+
import { parseApprovalHmacKeys, parseServiceAuthTokens } from "./auth-keys.js";
|
|
3
4
|
import { ENV_NAME_ALLOWLIST_KNOB_NAMES } from "./env-name-allowlist-knobs.js";
|
|
4
5
|
import { recordOwnCredentialValueNotWithheld, recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
|
|
5
6
|
export const CATALOG_SECRET_ENV_NAMES = new Set(CONFIG_CATALOG.filter((spec) => spec.type === "secret").map((spec) => spec.name));
|
|
@@ -10,37 +11,72 @@ export function serverWithheldEnvNames(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
|
10
11
|
export function serverCredentialValueSources(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
11
12
|
return [...new Set([...CATALOG_SECRET_ENV_NAMES, ...Object.values(modelApiKeyEnv)])].sort();
|
|
12
13
|
}
|
|
14
|
+
const CREDENTIAL_FORM_OF = new Map(CONFIG_CATALOG.flatMap((spec) => (spec.credentialForm === undefined ? [] : [[spec.name, spec.credentialForm]])));
|
|
15
|
+
const DROPPED_KEYS_TOLD_BY_LOAD_CONFIG = () => { };
|
|
16
|
+
function credentialsIn(form, raw) {
|
|
17
|
+
switch (form) {
|
|
18
|
+
case "whole":
|
|
19
|
+
return [raw];
|
|
20
|
+
case "token-table":
|
|
21
|
+
return [raw, ...Object.keys(parseServiceAuthTokens(raw))];
|
|
22
|
+
case "key-set":
|
|
23
|
+
return [raw, ...parseApprovalHmacKeys(raw, DROPPED_KEYS_TOLD_BY_LOAD_CONFIG).map((k) => k.key)];
|
|
24
|
+
default: {
|
|
25
|
+
const unreachable = form;
|
|
26
|
+
throw new Error(`unhandled secret credential form: ${String(unreachable)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
13
30
|
let declared;
|
|
14
31
|
let beforeLast;
|
|
15
32
|
const valueRefusalAnnounced = new Set();
|
|
16
33
|
let liveConfig;
|
|
17
34
|
const envValueOf = (name) => process.env[name];
|
|
18
|
-
|
|
35
|
+
function credentialsHeldBy(source) {
|
|
36
|
+
const raw = envValueOf(source);
|
|
37
|
+
if (raw === undefined || raw === "")
|
|
38
|
+
return [];
|
|
39
|
+
return [...new Set(credentialsIn(CREDENTIAL_FORM_OF.get(source) ?? "whole", raw))].filter((v) => v !== "");
|
|
40
|
+
}
|
|
41
|
+
function heldValues(held) {
|
|
42
|
+
return [...held].flatMap(([source, at]) => {
|
|
43
|
+
const all = credentialsHeldBy(source);
|
|
44
|
+
return [...at].flatMap((i) => {
|
|
45
|
+
const value = all[i];
|
|
46
|
+
return value === undefined ? [] : [value];
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
19
50
|
export function declareServerWithheldEnv(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
20
51
|
const names = [...new Set([...(declared?.names ?? []), ...serverWithheldEnvNames(modelApiKeyEnv)])].sort();
|
|
21
|
-
const
|
|
22
|
-
declareWithheldEnv({ names, values:
|
|
23
|
-
const valueSources = new Set(kept);
|
|
52
|
+
const held = new Map(declared?.held ?? []);
|
|
53
|
+
declareWithheldEnv({ names, values: heldValues(held) });
|
|
24
54
|
for (const source of serverCredentialValueSources(modelApiKeyEnv)) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
const at = new Set(held.get(source) ?? []);
|
|
56
|
+
let refusal;
|
|
57
|
+
credentialsHeldBy(source).forEach((_, i) => {
|
|
58
|
+
if (at.has(i))
|
|
59
|
+
return;
|
|
60
|
+
try {
|
|
61
|
+
declareWithheldEnv({ names, values: heldValues(new Map(held).set(source, new Set([...at, i]))) });
|
|
62
|
+
at.add(i);
|
|
63
|
+
held.set(source, new Set(at));
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
refusal ??= e instanceof Error ? e.message : String(e);
|
|
35
67
|
}
|
|
68
|
+
});
|
|
69
|
+
if (refusal !== undefined && !valueRefusalAnnounced.has(source)) {
|
|
70
|
+
valueRefusalAnnounced.add(source);
|
|
71
|
+
recordOwnCredentialValueNotWithheld({ name: source, reason: refusal });
|
|
36
72
|
}
|
|
37
73
|
}
|
|
38
74
|
beforeLast = declared;
|
|
39
|
-
declared = { names: new Set(names),
|
|
75
|
+
declared = { names: new Set(names), held };
|
|
40
76
|
return names;
|
|
41
77
|
}
|
|
42
78
|
export function withdrawServerWithheldEnv() {
|
|
43
|
-
declareWithheldEnv({ names: [...(beforeLast?.names ?? [])], values:
|
|
79
|
+
declareWithheldEnv({ names: [...(beforeLast?.names ?? [])], values: heldValues(beforeLast?.held ?? new Map()) });
|
|
44
80
|
declared = beforeLast;
|
|
45
81
|
}
|
|
46
82
|
export function bindOwnCredentialsConfig(config) {
|
|
@@ -65,8 +101,15 @@ export function ownCredentialsWithheld() {
|
|
|
65
101
|
export function ownCredentialValuesWithheld() {
|
|
66
102
|
if (declared === undefined || liveConfig === undefined)
|
|
67
103
|
return false;
|
|
68
|
-
const
|
|
69
|
-
return serverCredentialValueSources(liveConfig.modelApiKeyEnv).every((source) =>
|
|
104
|
+
const held = declared.held;
|
|
105
|
+
return serverCredentialValueSources(liveConfig.modelApiKeyEnv).every((source) => (held.get(source)?.size ?? 0) === credentialsHeldBy(source).length);
|
|
106
|
+
}
|
|
107
|
+
export function ownCredentialVariable(name) {
|
|
108
|
+
if (inheritEnvPolicyProblem([name]) !== undefined)
|
|
109
|
+
return "name";
|
|
110
|
+
const findings = [];
|
|
111
|
+
inheritedHostEnv([name], findings);
|
|
112
|
+
return findings.some((f) => f.kind === "own-credential") ? "value" : undefined;
|
|
70
113
|
}
|
|
71
114
|
export function __resetServerWithheldEnvForTest() {
|
|
72
115
|
declareWithheldEnv({ names: [], values: [] });
|
package/dist/session-agent.d.ts
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* 投影律(不另铸):`id` = 元组的 `handle`(耐久 agent 行的 `a…` 键,与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串);`parentSessionId` /
|
|
16
16
|
* `rootSessionId` = 元组的同名格(派它的会话 / 委派树的根会话);元组缺哪一格这里就缺哪一格;元组的 `scope`(租户键)/ `placedAt` / `kind` 不上 wire。
|
|
17
|
-
* 键在场 =
|
|
18
|
-
*
|
|
17
|
+
* 键在场 = 这是一只后台委派子代的会话(普通后台子代与 `subagent_type: "fork"` 的后台子代同样落元组 —— 引擎 7.63.0 起后台 fork 建会话那一笔也交委派元组;
|
|
18
|
+
* 元组只存了 `kind` 时是 `{}`);键缺席 = 不是 placed 行(宿主会话、同步委派的子代 —— 含同步 fork,它的会话是父会话的一只普通分叉;`DB_BACKEND=local` 到 7.140.x
|
|
19
|
+
* 停驻时拷进宿主店的子代转录,那一份是普通会话行)、这台部署的店不存元组(无 repo 的内存店
|
|
19
20
|
* 〔`DB_BACKEND=memory`〕、嵌入方自建且不声明委派分区的店),或那一读没读成 —— 消费方一律读作「这台 server 没有说」,不读作「不是子代」。
|
|
20
21
|
*/
|
|
21
22
|
import type { SessionPlacementRecord } from "@sema-agent/core";
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* ## 动词(core 板 [ref] ⑧⑨ 给定;本车第零步探针 `test/core-probe-parked-stop-root-access.test.ts` 真跑证实)
|
|
16
16
|
* · 枚举 = `agentStore.listBySession(scope, 根会话)`(耐久行,锚 = 行的 session / parent / root 任一),并上本进程登记表里的
|
|
17
17
|
* `activeDelegationHandles(scope, 根会话)`(没接耐久店的部署只有这一半)。scope 轴由调用方给(唯一派生 {@link sessionTenantScope})。
|
|
18
|
-
* · **parked** 行 ⇒ `
|
|
18
|
+
* · **parked** 行 ⇒ 按店里该卡的属主格 `expire`(`expireCardUnderStoredScope`:属主格读店的列、不读行体;与 core 自己的本进程仲裁器同一只 CAS)+
|
|
19
19
|
* `registry.reconcileParkedAgents(stores, scope)` ⇒ 行 `failed`(stoppedBy `system`)、卡离开待决面。任何副本上都做得到 ⇒ 预检把它
|
|
20
20
|
* 算作可达(前提是本部署有 checkpoint 店)。
|
|
21
21
|
* ⚠️ **不对 parked 行调 `stopTask`**:只在耐久店里的行(park 它的进程不在 / 别的副本 / 重启后 —— parked 行活过重启是常态)
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* 两轮之间留一小段退避让它落盘。
|
|
41
41
|
*/
|
|
42
42
|
import type { BackgroundAgentStore, CheckpointStore, TaskRegistry } from "@sema-agent/core";
|
|
43
|
+
import { type CardSettlePort } from "./card-settle-stored-scope.js";
|
|
43
44
|
/** 重新枚举的轮数上界。 */
|
|
44
45
|
export declare const RETIRE_MAX_ROUNDS = 6;
|
|
45
46
|
/** 两步共用的枚举口。 */
|
|
@@ -47,14 +48,16 @@ interface ChildrenSource {
|
|
|
47
48
|
agentStore: BackgroundAgentStore | undefined;
|
|
48
49
|
registry: Pick<TaskRegistry, "activeDelegationHandles">;
|
|
49
50
|
}
|
|
51
|
+
/** 预检与结清共用的 checkpoint 店口:作废卡({@link CardSettlePort})+ 引擎对账读卡(`get`)—— 预检说「可达」的前提就是结清做得了这两件,所以两步同一个型。 */
|
|
52
|
+
export type ChildrenCheckpointStore = Pick<CheckpointStore, "get"> & CardSettlePort;
|
|
50
53
|
export interface ProbeSessionChildrenDeps extends ChildrenSource {
|
|
51
54
|
/** 缺席 = 本部署没有耐久审批;撞见 parked 行却没有它 ⇒ 那只作废不了 ⇒ 不可达(拒在删之前)。 */
|
|
52
|
-
checkpointStore:
|
|
55
|
+
checkpointStore: ChildrenCheckpointStore | undefined;
|
|
53
56
|
registry: Pick<TaskRegistry, "activeDelegationHandles" | "getAccessibleTask" | "writerId">;
|
|
54
57
|
}
|
|
55
58
|
export interface RetireSessionChildrenDeps extends ChildrenSource {
|
|
56
59
|
/** 缺席 = 本部署没有耐久审批(不可能有 parked 行);撞见 parked 行却没有它 ⇒ 那只作废不了,留到剩余(预检本该已经拒掉它)。 */
|
|
57
|
-
checkpointStore:
|
|
60
|
+
checkpointStore: ChildrenCheckpointStore | undefined;
|
|
58
61
|
registry: Pick<TaskRegistry, "stopTask" | "reconcileParkedAgents" | "activeDelegationHandles">;
|
|
59
62
|
sleep?: (ms: number) => Promise<void>;
|
|
60
63
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { partsOf } from "./owner-brands.js";
|
|
2
2
|
import { recoveredSubject } from "./owner-faces.js";
|
|
3
3
|
import { taskSpecIdentityOf } from "./engine-identity.js";
|
|
4
|
+
import { expireCardUnderStoredScope } from "./card-settle-stored-scope.js";
|
|
4
5
|
export const RETIRE_MAX_ROUNDS = 6;
|
|
5
6
|
const RETIRE_BACKOFF_MS = [0, 25, 50, 100, 200, 400];
|
|
6
7
|
export function sessionTenantScope(owner) {
|
|
@@ -74,11 +75,7 @@ async function expireParkedCard(deps, handle, scope) {
|
|
|
74
75
|
const row = await deps.agentStore.get(handle, scope);
|
|
75
76
|
if (row === null || row.status !== "parked" || row.parkedCheckpointToken === undefined)
|
|
76
77
|
return;
|
|
77
|
-
|
|
78
|
-
const cp = await deps.checkpointStore.get(token);
|
|
79
|
-
if (cp === null || cp.status !== "pending")
|
|
80
|
-
return;
|
|
81
|
-
await deps.checkpointStore.expire(token, cp.scope);
|
|
78
|
+
await expireCardUnderStoredScope(deps.checkpointStore, row.parkedCheckpointToken);
|
|
82
79
|
}
|
|
83
80
|
export class SessionChildrenStopIncompleteError extends Error {
|
|
84
81
|
sessionId;
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
* 会话在没有记号的检出里续跑时,项目随每一次不带 `projectId` 的提交丢掉。现在:一个值进引擎、一格存引擎的答案、下一条腿读那一格。
|
|
24
24
|
*
|
|
25
25
|
* ## 不在这里的(如实)
|
|
26
|
-
* · **记忆项目层**(单用户 `userrepo:` 键,`project-identity.ts`
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* · **记忆项目层**(单用户 `userrepo:` 键,`project-identity.ts`)不读这一格:它在装配期(任何帧之前)交给 `TaskSpec.memory.projectKey`,
|
|
27
|
+
* 身份是**检出**的函数 —— 检出带 `.sema/project` 记号 ⇒ 按记号键控,读法就是引擎判官读的同一只(core `resolveProjectId`,同一任务根);
|
|
28
|
+
* 没有记号 ⇒ git 根回落。行上 `source: "declared"` 的项目不改项目层的键(引擎 7.58.0 记忆自己的读法同:「只声明的身份不改记忆的键」)。
|
|
29
29
|
* · 多租户记忆分区(`proj:<租户>/<projectId>`)与登记簿种子读的是**这次提交**的 `projectId`(按提交挑盘,修前即如此),不读行。
|
|
30
30
|
* · 行上的项目还有一位读者:会话视图 `GET /v1/sessions/:id` 体顶层的 `projectId`(唯一读口 {@link readSessionProjectId},与视图的 `agent` 同一次视图读)。
|
|
31
31
|
* `root` / `cwd` / `entered` / `remote` 今天只存不读;fork 出的新会话从「没有记录」起(项目声明按引擎契约每腿都带,fork 的首腿由调用方带上)。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **店的持久性准入**(起服第 2 段;只读,拒 = 抛 {@link StoreNotDurableError},机器码 `config.store_not_durable`)—— 店的拒启判都在这一只函数里,判两根:
|
|
3
|
+
*
|
|
4
|
+
* ## 规则(一句)
|
|
5
|
+
* 引擎被告知「活过一次替换」的东西,它落的那只根就必须活过替换:
|
|
6
|
+
* · **数据根**(local 后端:会话店 + 检查点店落在它上面)—— local 店的声明 `durable`(`localStoreDeclarationOf`)∧ 数据根介质判 `ephemeral` ⇒ 拒;
|
|
7
|
+
* · **显式的封签钥匙目录**(`SEALED_KEY_DIR`,数据根之外单设的那一只)—— 停驻审批在场(`DURABLE_APPROVAL`)∧ 检查点店声明 `durable`(SQL 后端恒是;local 后端随它的声明)
|
|
8
|
+
* ∧ 钥匙目录介质判 `ephemeral` ⇒ 拒:停驻任务的请求凭据封在那只目录里的钥匙上,钥匙随下一次替换而去,之前停驻的每一条续跑都答 `key_not_held` —— 行活着、钥匙没了。
|
|
9
|
+
* 缺省的钥匙目录(`<数据根>/sealed-key`)跟数据根同判:local 后端由数据根那一臂判;SQL 后端的缺省目录只进读数与起服 warn(无卷的无状态副本是 SQL 部署的常形,
|
|
10
|
+
* 没写 `SEALED_KEY_DIR` 的部署没有对钥匙目录作过承诺 —— 与「声明了的目录建不了店 ⇒ 拒,缺省 ⇒ warn」同一条律)。
|
|
11
|
+
* file 记忆根不进判据(只进读数):记忆丢了是召回变差,不让任何一笔已承诺的停驻悬空;单卷部署(只给数据根挂卷)也不因它被拒。
|
|
12
|
+
* 判不出的介质(`unknown`:非 Linux、读不懂的挂载)不拒 —— 缺证据不放宽声明,也不凭空拒。
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ 位置即契约:起服第 2 段**车道合成之后**(数据根那一臂读车道:`callerOwnsWorkerDisk`)、**本进程的第一笔写之前** —— 配置根的一次性迁移(配置根缺省就是数据根)、
|
|
15
|
+
* 配置根供给(`SEMA_CONFIG_DIR`)、保管店铸钥匙与第 3 段开店都在它之后:拒的时候数据根 / 配置根 / 钥匙目录上没有本服务写下的任何东西。介质 = 第 2 段段首的取证
|
|
16
|
+
* (`captureBootStoreEvidence`)那一份,不重读挂载表。放行时交回 local 店的那一份声明(非 local 后端 ⇒ `undefined`);第 3 段开店按同一组输入经 `localStoreDeclarationOf` 交给两半店。
|
|
17
|
+
* 第 9 段布线审计的同码一臂是「引擎读到的会话店声明 × 数据根介质」的通用对表(也管没有后端的 env-only 形);local 部署走到那里时,同一组输入已在这里判过、判得一样。
|
|
18
|
+
* run-local 没有起服取证(介质 unknown ⇒ 不拒),不调。
|
|
19
|
+
*/
|
|
20
|
+
import type { StoreBackend as StoreBackendWord } from "@sema-agent/settings-schema/execution-lanes";
|
|
21
|
+
import { type LocalStoreDeclaration, type LocalStoreDeclarationDeployment } from "./local-store-declaration.js";
|
|
22
|
+
import { type StoreEvidence } from "./store-posture.js";
|
|
23
|
+
/** 准入读的部署事实(`ServiceConfig` 结构上满足):local 店声明的那几键 + 后端词 + 停驻审批在场 + 显式钥匙目录。 */
|
|
24
|
+
export type StoreAdmissionDeployment = LocalStoreDeclarationDeployment & {
|
|
25
|
+
readonly dbBackend: StoreBackendWord;
|
|
26
|
+
readonly durableApproval: boolean;
|
|
27
|
+
readonly sealedKeyDir?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 🔴 店的持久性准入(规则与位置见文件头)。两臂各判、一次说完:任一臂不过 ⇒ 抛一只 {@link StoreNotDurableError},拒句逐条列出不过的那几臂与各自的出路。
|
|
31
|
+
*/
|
|
32
|
+
export declare function admitStoreDurability(deployment: StoreAdmissionDeployment, evidence: Pick<StoreEvidence, "dataRoot" | "sealedKey">): LocalStoreDeclaration | undefined;
|
|
33
|
+
//# sourceMappingURL=store-admission.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describeLocalStoreDeclaration, localStoreDeclarationOf } from "./local-store-declaration.js";
|
|
2
|
+
import { STORE_NOT_DURABLE_CODE, StoreNotDurableError, describeStoreRoot } from "./store-posture.js";
|
|
3
|
+
function checkpointDeclarationOf(backend, local) {
|
|
4
|
+
switch (backend) {
|
|
5
|
+
case "mysql":
|
|
6
|
+
case "pg":
|
|
7
|
+
return "durable";
|
|
8
|
+
case "local":
|
|
9
|
+
if (local === undefined)
|
|
10
|
+
throw new Error("store admission: a local backend without its declaration — wiring defect");
|
|
11
|
+
return local.durability === "durable" ? "durable" : "process-local";
|
|
12
|
+
case "memory":
|
|
13
|
+
return "none";
|
|
14
|
+
default: {
|
|
15
|
+
const unhandled = backend;
|
|
16
|
+
throw new Error(`store admission: unknown DB_BACKEND word ${String(unhandled)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function keyRootWaysOut(backend, path) {
|
|
21
|
+
const volume = `mount a persistent volume at ${path} (every replica must see the same directory)`;
|
|
22
|
+
const noParks = "or turn parked approvals off (DURABLE_APPROVAL=false — a task that needs an approval then waits on a live connection only)";
|
|
23
|
+
return backend === "local"
|
|
24
|
+
? `Three ways out: ${volume}; or declare the local store ephemeral (LOCAL_STORE_DURABILITY=ephemeral — sessions and parked approvals then do not survive a replacement); ${noParks}.`
|
|
25
|
+
: `Two ways out: ${volume}; ${noParks}.`;
|
|
26
|
+
}
|
|
27
|
+
export function admitStoreDurability(deployment, evidence) {
|
|
28
|
+
const local = deployment.dbBackend === "local" ? localStoreDeclarationOf(deployment, evidence.dataRoot.verdict) : undefined;
|
|
29
|
+
const findings = [];
|
|
30
|
+
if (local !== undefined && local.durability === "durable" && evidence.dataRoot.verdict === "ephemeral") {
|
|
31
|
+
const dataRoot = evidence.dataRoot;
|
|
32
|
+
findings.push(`${STORE_NOT_DURABLE_CODE}: the local store declares itself durable (${describeLocalStoreDeclaration(local)}) while its data root does not survive a container / Pod replacement ` +
|
|
33
|
+
`(${describeStoreRoot({ root: "data", ...dataRoot })}). A durable declaration on an ephemeral medium is a claim nothing can honor — sessions and parked approvals would vanish with the next replacement while the engine was told they survive. ` +
|
|
34
|
+
`Three ways out: mount a persistent volume at ${dataRoot.path ?? "the data root"} (or point LOCAL_DATA_ROOT at one); or declare the local store ephemeral ` +
|
|
35
|
+
`(LOCAL_STORE_DURABILITY=ephemeral — sessions, parked approvals and memory then do not survive a container / Pod replacement); or keep nothing on disk (DB_BACKEND=memory).`);
|
|
36
|
+
}
|
|
37
|
+
const parks = checkpointDeclarationOf(deployment.dbBackend, local);
|
|
38
|
+
if (deployment.sealedKeyDir !== undefined && deployment.durableApproval && parks === "durable" && evidence.sealedKey.verdict === "ephemeral") {
|
|
39
|
+
const keyRoot = evidence.sealedKey;
|
|
40
|
+
findings.push(`${STORE_NOT_DURABLE_CODE}: parked approvals are declared durable (${deployment.dbBackend === "local" ? "the local checkpoint store's declaration" : "the checkpoint store is the SQL database"}, DURABLE_APPROVAL on) ` +
|
|
41
|
+
`while the declared sealing-key directory SEALED_KEY_DIR does not survive a container / Pod replacement (${describeStoreRoot({ root: "sealed_key", ...keyRoot })}). ` +
|
|
42
|
+
"A parked task's request credentials are sealed to a key kept in that directory — after the next replacement the parked rows are still there and the key is not: every task parked before it answers key_not_held on resume. " +
|
|
43
|
+
keyRootWaysOut(deployment.dbBackend, keyRoot.path ?? deployment.sealedKeyDir));
|
|
44
|
+
}
|
|
45
|
+
if (findings.length === 0)
|
|
46
|
+
return local;
|
|
47
|
+
throw new StoreNotDurableError(`store durability admission failed — refusing to start before any store or sealed key is written:\n${findings.map((f) => ` · ${f}`).join("\n")}`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=store-admission.js.map
|
package/dist/store-posture.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { StoreBackend } from "./plugins/store-backend.js";
|
|
|
4
4
|
import type { SqlEngineFacts } from "./plugins/sql-driver.js";
|
|
5
5
|
import { type SqlEngineCapability } from "./sql-engine-posture.js";
|
|
6
6
|
import { type MountRow, type MountTable } from "./mountinfo.js";
|
|
7
|
+
import type { LocalStoreDeclaration } from "./local-store-declaration.js";
|
|
7
8
|
/**
|
|
8
9
|
* 降级词闭集(**按后端族**,不按失败发生在哪一步):
|
|
9
10
|
* · `local_unwritable` —— **缺省推导**的 local 文件店在**空 / 不存在**的数据根上构造期失败(mkdir / 权限 / 只读挂载 / 磁盘满)。
|
|
@@ -124,10 +125,13 @@ export interface StorePosture {
|
|
|
124
125
|
readonly durability: StoreDurability;
|
|
125
126
|
/** 逐根明细:恒以数据根开头;有起服证据时再接封签钥匙目录与(接线时的)file 记忆根。 */
|
|
126
127
|
readonly roots: readonly StoreRootDurability[];
|
|
128
|
+
/** local 店交给两半店的声明(present-iff 装配出 local 后端;值 = 后端构造时 `localStoreDeclarationOf` 的那一份,装配方从后端读回,不重算)。 */
|
|
129
|
+
readonly localDeclaration?: LocalStoreDeclaration;
|
|
127
130
|
}
|
|
128
131
|
/**
|
|
129
132
|
* `build*`(纯数据)。`assembled` = 装配出的 `StoreBackend.kind`;缺席 = 没装出后端 ⇒ `memory`。`evidence` 缺席 = 这个宿主没有起服证据(测试夹具 / 桩):
|
|
130
133
|
* 只判数据根、且只按后端规则判(local 无证据即抛)。`memoryRootWired` = file 记忆引擎在本部署上真接了线(配了才算;没接线的记忆根不进最弱律)。
|
|
134
|
+
* `localDeclaration` = 装配出的 local 后端交给两半店的那一份声明(装配出 local 而缺席 = 装配缺陷,响亮抛;别的后端递了也不收)。
|
|
131
135
|
*/
|
|
132
136
|
export declare function buildStorePosture(a: {
|
|
133
137
|
readonly declared: StoreBackendWord;
|
|
@@ -135,6 +139,7 @@ export declare function buildStorePosture(a: {
|
|
|
135
139
|
readonly degradedReason: StoreDegradedReason | undefined;
|
|
136
140
|
readonly evidence: StoreEvidence | undefined;
|
|
137
141
|
readonly memoryRootWired: boolean;
|
|
142
|
+
readonly localDeclaration: LocalStoreDeclaration | undefined;
|
|
138
143
|
}): StorePosture;
|
|
139
144
|
/** 读面桩的诚实下限(没有装配值的宿主:声明 = 配置词,后端 = memory ⇒ `ephemeral`)。caps 与 `/health` 同调这一只。 */
|
|
140
145
|
export declare function unwiredStorePosture(declared: StoreBackendWord): StorePosture;
|