@sema-agent/server 7.124.0 → 7.125.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 +138 -0
- package/USAGE.md +46 -8
- package/dist/audit.d.ts +27 -5
- package/dist/audit.js +42 -8
- package/dist/boot/execution-env.d.ts +33 -1
- package/dist/boot/execution-env.js +31 -2
- package/dist/boot/resolve-spec.js +4 -3
- package/dist/boot/session-faces.d.ts +14 -1
- package/dist/boot/session-faces.js +6 -5
- package/dist/boot/stage-02-web-admin-pull.js +5 -1
- package/dist/boot/stage-05-execution-env.js +6 -2
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/web-admin.js +130 -140
- package/dist/capabilities/center-wiring.d.ts +2 -1
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capability-facts.d.ts +28 -3
- package/dist/capability-facts.js +20 -0
- package/dist/config-catalog.js +4 -1
- package/dist/config-d-migration.js +2 -1
- package/dist/config-lkg.d.ts +4 -1
- package/dist/config-lkg.js +16 -12
- package/dist/config-provider.d.ts +4 -1
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +10 -3
- package/dist/config.js +44 -14
- package/dist/env-facts.d.ts +2 -2
- package/dist/env-facts.js +3 -2
- package/dist/fleet/fleet-bus.d.ts +24 -12
- package/dist/fleet/fleet-bus.js +4 -1
- package/dist/fleet/workspace-snapshot-residual.d.ts +10 -0
- package/dist/fleet/workspace-snapshot-residual.js +35 -0
- package/dist/hooks/hook-runner.d.ts +15 -27
- package/dist/hooks/hook-runner.js +19 -59
- package/dist/http/active-run-conflict.d.ts +3 -0
- package/dist/http/active-run-conflict.js +3 -0
- package/dist/http/admission.js +7 -7
- package/dist/http/approval-sweeps.js +2 -4
- package/dist/http/device-placement.js +1 -0
- package/dist/http/dispatch.js +1 -1
- package/dist/http/durable-run-leg.d.ts +20 -6
- package/dist/http/durable-run-leg.js +4 -4
- package/dist/http/idempotency.d.ts +83 -38
- package/dist/http/idempotency.js +38 -13
- package/dist/http/request-guards.d.ts +18 -0
- package/dist/http/request-guards.js +12 -3
- package/dist/http/resume-legs.js +7 -1
- package/dist/http/route-ctx.d.ts +3 -3
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/routes/a2a-serve.js +27 -11
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/fleet.js +2 -0
- package/dist/http/routes/images.js +2 -2
- package/dist/http/routes/leader.d.ts +4 -0
- package/dist/http/routes/leader.js +13 -3
- package/dist/http/routes/observability.js +2 -0
- package/dist/http/routes/runs.d.ts +6 -2
- package/dist/http/routes/runs.js +71 -101
- package/dist/http/routes/session-sync.d.ts +3 -0
- package/dist/http/routes/session-sync.js +30 -1
- package/dist/http/routes/sessions-list.d.ts +11 -0
- package/dist/http/routes/sessions-list.js +6 -6
- package/dist/http/routes/sessions.d.ts +6 -1
- package/dist/http/routes/sessions.js +41 -10
- package/dist/http/routes/tasks.js +63 -21
- package/dist/http/run-read-face.d.ts +26 -0
- package/dist/http/run-read-face.js +67 -0
- package/dist/http/server.d.ts +4 -4
- package/dist/http/server.js +1 -2
- package/dist/http/submit-replay.d.ts +40 -0
- package/dist/http/submit-replay.js +22 -0
- package/dist/leader/endpoint.d.ts +10 -5
- package/dist/leader/endpoint.js +27 -5
- package/dist/observability/cost-quota.d.ts +51 -7
- package/dist/observability/cost-quota.js +35 -17
- package/dist/observability/fail-open.d.ts +1 -1
- package/dist/observability/fail-open.js +1 -1
- package/dist/observability/run-terminal.d.ts +5 -9
- package/dist/observability/run-terminal.js +2 -7
- package/dist/plugins/approval-ask-store-sql.js +1 -1
- package/dist/plugins/caching-session-store.d.ts +7 -2
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/device-store-sql.js +1 -1
- package/dist/plugins/ensure-index.js +22 -0
- package/dist/plugins/file-run-store.d.ts +24 -14
- package/dist/plugins/file-run-store.js +52 -12
- package/dist/plugins/leader-run-store-sql.d.ts +1 -11
- package/dist/plugins/leader-run-store-sql.js +1 -5
- package/dist/plugins/local-session-store.d.ts +35 -6
- package/dist/plugins/local-session-store.js +87 -18
- package/dist/plugins/memory-run-store.d.ts +13 -12
- package/dist/plugins/memory-run-store.js +25 -0
- package/dist/plugins/one-time-migrations.js +1 -1
- package/dist/plugins/pg-cost-quota.d.ts +1 -1
- package/dist/plugins/pg-cost-quota.js +2 -5
- package/dist/plugins/pg-pool.js +3 -0
- package/dist/plugins/pg-session-storage.d.ts +11 -1
- package/dist/plugins/pg-session-storage.js +44 -8
- package/dist/plugins/remote-env-k8s.d.ts +127 -12
- package/dist/plugins/remote-env-k8s.js +270 -27
- package/dist/plugins/run-store-sql.d.ts +26 -12
- package/dist/plugins/run-store-sql.js +50 -25
- package/dist/plugins/schema-consistency.d.ts +1 -13
- package/dist/plugins/schema-consistency.js +1 -41
- package/dist/plugins/schema-indexes.js +1 -0
- package/dist/plugins/schema-live-columns.d.ts +20 -0
- package/dist/plugins/schema-live-columns.js +42 -0
- package/dist/plugins/store-contracts.d.ts +40 -2
- package/dist/plugins/tidb-cost-quota.d.ts +1 -1
- package/dist/plugins/tidb-cost-quota.js +2 -5
- package/dist/plugins/tidb-pool.js +14 -3
- package/dist/plugins/tidb-session-store.d.ts +22 -2
- package/dist/plugins/tidb-session-store.js +33 -6
- package/dist/plugins/write-behind-counter.d.ts +12 -5
- package/dist/plugins/write-behind-counter.js +14 -7
- package/dist/prompts-domain-validate.js +3 -2
- package/dist/rules-consent.d.ts +2 -2
- package/dist/rules-consent.js +2 -2
- package/dist/run-local.js +31 -23
- package/dist/runs.d.ts +9 -0
- package/dist/runs.js +8 -0
- package/dist/runtime-governance.d.ts +3 -3
- package/dist/runtime-governance.js +2 -4
- package/dist/security.d.ts +19 -2
- package/dist/session-row.d.ts +53 -0
- package/dist/session-row.js +47 -0
- package/dist/session-sync.d.ts +6 -0
- package/dist/session-sync.js +5 -1
- package/dist/session-titler.d.ts +9 -5
- package/dist/session-titler.js +3 -1
- package/dist/spec-fields.js +2 -2
- package/dist/task-cwd.d.ts +0 -20
- package/dist/task-cwd.js +2 -10
- package/dist/tool-approval.d.ts +5 -7
- package/dist/trace/redact.d.ts +23 -50
- package/dist/trace/redact.js +58 -18
- package/dist/trace/self-minted-wire.d.ts +25 -13
- package/dist/trace/self-minted-wire.js +9 -4
- package/dist/web-admin-client/apply-effective.d.ts +10 -12
- package/dist/web-admin-client/apply-effective.js +2 -3
- package/dist/web-admin-client/candidate-gate.d.ts +69 -0
- package/dist/web-admin-client/candidate-gate.js +27 -0
- package/dist/web-admin-client/http-client.d.ts +2 -1
- package/dist/web-admin-client/http-client.js +2 -1
- package/dist/web-admin-client/pull-state.d.ts +6 -4
- package/dist/web-admin-client/read-warnings.d.ts +29 -5
- package/dist/web-admin-client/read-warnings.js +24 -4
- package/dist/web-admin-client/stage-limits.js +4 -1
- package/dist/workspace-space.d.ts +41 -0
- package/dist/workspace-space.js +5 -0
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildSessionAudit, buildSessionAuditLocal, buildSessionAuditPg } from "../audit.js";
|
|
1
|
+
import { buildRewindHandles, buildSessionAudit, buildSessionAuditLocal, buildSessionAuditPg } from "../audit.js";
|
|
2
2
|
import { makeDegenerateInstrument } from "../degenerate-instrument.js";
|
|
3
3
|
import { PlanCacheProbe } from "../plan-cache-probe.js";
|
|
4
4
|
import { purgeScratchpadDir } from "../env-facts.js";
|
|
@@ -14,12 +14,13 @@ export function createSessionFaces(ctx) {
|
|
|
14
14
|
const auditLocalRoot = backend?.kind === "local" ? (config.localDataRoot ?? localRoot) : undefined;
|
|
15
15
|
const ownerAware = sessionStore;
|
|
16
16
|
const sessionAudit = mysqlPool || auditPgPool || auditLocalRoot
|
|
17
|
-
? async (sessionId) => {
|
|
17
|
+
? async (sessionId, opts) => {
|
|
18
|
+
const handles = opts !== undefined && resumeAnchorStore !== undefined ? buildRewindHandles(await resumeAnchorStore.listBySession(sessionId), opts.reader) : undefined;
|
|
18
19
|
const audit = mysqlPool
|
|
19
|
-
? await buildSessionAudit(mysqlPool, sessionId)
|
|
20
|
+
? await buildSessionAudit(mysqlPool, sessionId, handles)
|
|
20
21
|
: auditPgPool
|
|
21
|
-
? await buildSessionAuditPg(auditPgPool, sessionId)
|
|
22
|
-
: await buildSessionAuditLocal(auditLocalRoot, sessionId);
|
|
22
|
+
? await buildSessionAuditPg(auditPgPool, sessionId, handles)
|
|
23
|
+
: await buildSessionAuditLocal(auditLocalRoot, sessionId, handles);
|
|
23
24
|
if (!audit)
|
|
24
25
|
return undefined;
|
|
25
26
|
return { ...audit, owner: (await ownerAware.ownerOf?.(sessionId)) ?? null };
|
|
@@ -2,10 +2,14 @@ import { createUnpricedCeilingVerdict } from "./unpriced-ceiling-verdict.js";
|
|
|
2
2
|
import { createWebAdminRuntime } from "./web-admin.js";
|
|
3
3
|
import { composeExecutionLane, FILE_SOURCED_LANES } from "./execution-lane.js";
|
|
4
4
|
import { serviceConfigSourceKind } from "../config-provider.js";
|
|
5
|
+
import { logConfigDirMigration, migrateLocalConfigDirectory } from "../config-d-migration.js";
|
|
5
6
|
import { defaultModelRefusal, refusedNameMintedRefusal } from "../model-entry-refusal.js";
|
|
6
7
|
export async function runStage02(ctx) {
|
|
7
8
|
const { config, localRoot, logger, metrics } = ctx;
|
|
8
|
-
|
|
9
|
+
const configSource = serviceConfigSourceKind(config);
|
|
10
|
+
if (configSource === "local")
|
|
11
|
+
logConfigDirMigration(logger, await migrateLocalConfigDirectory(localRoot));
|
|
12
|
+
await composeExecutionLane(config, { configSource, localRoot, logger, drivesFromFile: FILE_SOURCED_LANES });
|
|
9
13
|
const webAdmin = await createWebAdminRuntime({ config, logger, metrics, localRoot });
|
|
10
14
|
const noDefaultModel = defaultModelRefusal(config, "env");
|
|
11
15
|
if (noDefaultModel !== undefined) {
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createToolTracer, createPermissionDeniedMeter } from "../observability/tool-trace.js";
|
|
2
2
|
import { composeHooks } from "../hooks/hook-runner.js";
|
|
3
3
|
import { assertDeviceLaneWired, createDeviceLane } from "./device-lane.js";
|
|
4
|
-
import { createExecutionEnv } from "./execution-env.js";
|
|
4
|
+
import { assertWorkspaceSpaceReadable, createExecutionEnv } from "./execution-env.js";
|
|
5
|
+
import { sessionFaceReaderOf } from "./session-incarnation-seat.js";
|
|
6
|
+
import * as facts from "../capability-facts.js";
|
|
7
|
+
import { sessionStoreLabelOf } from "../store-posture.js";
|
|
5
8
|
export function runStage05(ctx) {
|
|
6
|
-
const { backend, config, instanceId, logger, metrics, taskAttachmentStore } = ctx;
|
|
9
|
+
const { backend, config, instanceId, logger, metrics, sessionStore, taskAttachmentStore } = ctx;
|
|
7
10
|
const deviceLane = config.remoteExec?.provider === "device" ? createDeviceLane({ config, logger, metrics, backend, replicaId: instanceId }) : undefined;
|
|
8
11
|
const { perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv, fenceSessionOwner, dropSessionScoped, noteSessionIncarnation, sessionIncarnationOf, executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, } = createExecutionEnv({ config, logger, metrics, taskAttachmentStore, ...(deviceLane ? { deviceLane } : {}) });
|
|
9
12
|
assertDeviceLaneWired(config.remoteExec?.provider === "device", deviceLane, executionEnvFactory !== undefined);
|
|
13
|
+
assertWorkspaceSpaceReadable(facts.workspaceSpace(config), sessionFaceReaderOf(sessionStore), sessionStoreLabelOf(config.sessionBackend, backend?.kind));
|
|
10
14
|
const toolTracer = config.toolTrace ? createToolTracer(logger) : undefined;
|
|
11
15
|
if (toolTracer)
|
|
12
16
|
logger.info("tool_trace_enabled", {});
|
|
@@ -17,7 +17,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
|
|
|
17
17
|
childrenAlive: readonly string[];
|
|
18
18
|
}>) | undefined;
|
|
19
19
|
webAdminJwtVerifier: import("../auth-bridge.js").WebAdminJwtVerifier | undefined;
|
|
20
|
-
sessionAudit: ((sessionId: string) => Promise<{
|
|
20
|
+
sessionAudit: ((sessionId: string, opts?: import("../audit.js").SessionAuditReadOpts) => Promise<{
|
|
21
21
|
owner: string | null;
|
|
22
22
|
sessionId: string;
|
|
23
23
|
createdAt: string;
|
|
@@ -16,7 +16,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
|
|
|
16
16
|
childrenAlive: readonly string[];
|
|
17
17
|
}>) | undefined;
|
|
18
18
|
webAdminJwtVerifier: import("../auth-bridge.js").WebAdminJwtVerifier | undefined;
|
|
19
|
-
sessionAudit: ((sessionId: string) => Promise<{
|
|
19
|
+
sessionAudit: ((sessionId: string, opts?: import("../audit.js").SessionAuditReadOpts) => Promise<{
|
|
20
20
|
owner: string | null;
|
|
21
21
|
sessionId: string;
|
|
22
22
|
createdAt: string;
|
package/dist/boot/web-admin.js
CHANGED
|
@@ -9,20 +9,21 @@ import { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore
|
|
|
9
9
|
import { buildPricing } from "../budget.js";
|
|
10
10
|
import { applyCenterPlugins } from "../capabilities/center-plugins.js";
|
|
11
11
|
import { applyCatalogToSource, withPromptArtifactBackfill } from "../capabilities/center-prompts.js";
|
|
12
|
-
import {
|
|
12
|
+
import { CORE_ENGINE_VERSION } from "../prompts-domain-validate.js";
|
|
13
13
|
import { centerScenarios, centerScenarioDetails } from "../capabilities/scenarios.js";
|
|
14
14
|
import { isRosterGateArmed } from "../config.js";
|
|
15
15
|
import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg, legacyV1LkgPath, migrateV1Lkg } from "../config-lkg.js";
|
|
16
16
|
import { answerStillCurrent, centerStateBin, credentialIdentityNow, judgeCenterCredentialAtBoot } from "../center-credential.js";
|
|
17
17
|
import { buildCenterCredentialHealthView, createCenterCredentialRenewer } from "../center-credential-renewer.js";
|
|
18
18
|
import { centerManagedConfigKeys } from "../web-admin-client/apply-effective.js";
|
|
19
|
-
import { logConfigDirMigration, migrateLocalConfigDirectory } from "../config-d-migration.js";
|
|
20
19
|
import { createConfigProvider, raceBootFetch, serviceConfigSourceKind, BOOT_FETCH_DEFERRED } from "../config-provider.js";
|
|
21
20
|
import { createKeyResolver } from "../key-resolver.js";
|
|
22
21
|
import { buildSealedKeyPublicFace, ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
23
22
|
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../web-admin-client/facade.js";
|
|
24
23
|
import { buildConfigHandoff, buildRestartSignal, configHandoffMissing } from "../fleet/engine-signal.js";
|
|
25
24
|
import { createMcpRevocationLedger, enabledMcpEntries } from "../web-admin-client/mcp-revocation.js";
|
|
25
|
+
import { buildUnreadConfigFileFields } from "../web-admin-client/read-warnings.js";
|
|
26
|
+
import { CONFIG_DOMAIN_INVALID_NOTE, buildCandidateRejectedFields, judgeConfigCandidate } from "../web-admin-client/candidate-gate.js";
|
|
26
27
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
27
28
|
import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../web-admin-client/apply-ledger.js";
|
|
28
29
|
function lkgReadNote(scope) {
|
|
@@ -127,11 +128,12 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
127
128
|
localDir: localRoot,
|
|
128
129
|
})
|
|
129
130
|
: undefined;
|
|
130
|
-
|
|
131
|
-
logConfigDirMigration(logger, await migrateLocalConfigDirectory(localRoot));
|
|
131
|
+
const localDirField = configProvider?.kind === "local" ? { dir: configProvider.root } : {};
|
|
132
132
|
const rosterGateArmed = isRosterGateArmed(cc);
|
|
133
133
|
const modelReadyState = { ready: !rosterGateArmed };
|
|
134
|
-
const
|
|
134
|
+
const warnReadNotices = (r, phase) => {
|
|
135
|
+
for (const f of r.unreadFiles ?? [])
|
|
136
|
+
logger.warn("config_unread_file", buildUnreadConfigFileFields(f, localDirField.dir));
|
|
135
137
|
for (const d of r.droppedEntries ?? []) {
|
|
136
138
|
logger.warn("config_entry_dropped", {
|
|
137
139
|
domain: d.domain,
|
|
@@ -144,6 +146,13 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
144
146
|
});
|
|
145
147
|
}
|
|
146
148
|
};
|
|
149
|
+
const announceCandidateRejected = (r, verdict, leg) => {
|
|
150
|
+
const domains = verdict.domains.join(",");
|
|
151
|
+
if (lastRejectedCandidate?.version === r.effective.version && lastRejectedCandidate.domains === domains)
|
|
152
|
+
return;
|
|
153
|
+
lastRejectedCandidate = { version: r.effective.version, domains, at: Date.now() };
|
|
154
|
+
logger.warn("config_candidate_rejected", buildCandidateRejectedFields(r, verdict, leg, { lkgVersion: latestEffective?.version ?? "(env)", ...localDirField }));
|
|
155
|
+
};
|
|
147
156
|
const selfReportListeners = [];
|
|
148
157
|
const notifySelfReport = (changed) => {
|
|
149
158
|
for (const listener of selfReportListeners) {
|
|
@@ -275,13 +284,12 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
275
284
|
};
|
|
276
285
|
if (configProvider && !cc?.dryRun)
|
|
277
286
|
await rebasePromptState(promptStateIdentity, "boot");
|
|
278
|
-
const adoptCenterPrompts = async (eff, phase) => {
|
|
287
|
+
const adoptCenterPrompts = async (eff, prompts, phase) => {
|
|
279
288
|
const adoptedIdentity = candidateIdentityOf(eff);
|
|
280
289
|
if (adoptedIdentity !== undefined && adoptedIdentity !== promptStateIdentity) {
|
|
281
290
|
await rebasePromptState(adoptedIdentity, phase);
|
|
282
291
|
}
|
|
283
|
-
|
|
284
|
-
if (raw === undefined) {
|
|
292
|
+
if (prompts === undefined) {
|
|
285
293
|
if (centerPrompts)
|
|
286
294
|
logger.info("center_prompts_cleared", { phase, note: "effective carries no prompts key (publish gate off) — built-in providers resume" });
|
|
287
295
|
const cleared = await applyCatalogToSource(catalogSource, undefined, centerPrompts, logger, phase);
|
|
@@ -294,29 +302,24 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
294
302
|
}
|
|
295
303
|
return;
|
|
296
304
|
}
|
|
297
|
-
|
|
298
|
-
if (!v.ok) {
|
|
299
|
-
logger.warn("center_prompts_invalid", { phase, error: v.error, kept: centerPrompts?.declarations?.packId ?? (centerPrompts?.catalog ? "(catalog)" : "(builtin)"), note: "malformed prompts face rejected (core assemble would THROW per task) — keeping the previous pack/built-ins; fix the center payload" });
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
if (centerPrompts?.identity !== v.value.identity) {
|
|
305
|
+
if (centerPrompts?.identity !== prompts.identity) {
|
|
303
306
|
logger.info("center_prompts_adopted", {
|
|
304
307
|
phase,
|
|
305
|
-
axes:
|
|
306
|
-
...(
|
|
307
|
-
? { packId:
|
|
308
|
+
axes: prompts.axes,
|
|
309
|
+
...(prompts.declarations
|
|
310
|
+
? { packId: prompts.declarations.packId, contentDigest: prompts.declarations.contentDigest, sections: prompts.declarations.sections.length, scenarioOverrides: Object.keys(prompts.declarations.scenarioOverrides ?? {}).length }
|
|
308
311
|
: {}),
|
|
309
|
-
...(
|
|
312
|
+
...(prompts.catalog ? { artifactDigest: prompts.catalog.artifact.artifactDigest, catalogDigest: prompts.catalog.artifact.payload.catalogDigest, sourceRevision: prompts.catalog.sourceRevision } : {}),
|
|
310
313
|
note: "new tasks assemble the new pack (epoch re-pins at the next task boundary); in-flight sessions stay pinned",
|
|
311
314
|
});
|
|
312
315
|
}
|
|
313
|
-
const transitioned = await applyCatalogToSource(catalogSource,
|
|
316
|
+
const transitioned = await applyCatalogToSource(catalogSource, prompts, centerPrompts, logger, phase);
|
|
314
317
|
if (transitioned) {
|
|
315
|
-
centerPrompts =
|
|
318
|
+
centerPrompts = prompts;
|
|
316
319
|
pendingCatalogFaces = undefined;
|
|
317
320
|
}
|
|
318
321
|
else {
|
|
319
|
-
pendingCatalogFaces = { target:
|
|
322
|
+
pendingCatalogFaces = { target: prompts };
|
|
320
323
|
}
|
|
321
324
|
};
|
|
322
325
|
let bootConfigPending;
|
|
@@ -442,7 +445,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
442
445
|
const lkgEffective = read.lkg.effective;
|
|
443
446
|
candidateIdentity.set(lkgEffective, identity);
|
|
444
447
|
bindValidator(read.lkg.etag, identity);
|
|
445
|
-
return { effective: lkgEffective, ...(read.lkg.etag !== undefined ? { etag: read.lkg.etag } : {}) };
|
|
448
|
+
return { effective: lkgEffective, ...(read.lkg.etag !== undefined ? { etag: read.lkg.etag } : {}), prompts: read.prompts };
|
|
446
449
|
};
|
|
447
450
|
const bootApplyLkgInline = async (lkgR) => {
|
|
448
451
|
const lkgGen = generationOf(lkgR.effective.version);
|
|
@@ -461,7 +464,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
461
464
|
if (lkgSurvivesRestart && lkgIdentity !== undefined)
|
|
462
465
|
noteHandoff({ durable: { version: lkgR.effective.version, identity: lkgIdentity } });
|
|
463
466
|
markApplied(lkgGen, lkgR.effective.version);
|
|
464
|
-
await adoptCenterPrompts(lkgR.effective, "boot-lkg");
|
|
467
|
+
await adoptCenterPrompts(lkgR.effective, lkgR.prompts, "boot-lkg");
|
|
465
468
|
const lkgPlane = modelPlaneCommitted(lkgReport);
|
|
466
469
|
if (lkgPlane)
|
|
467
470
|
markRosterLanded(lkgR.effective);
|
|
@@ -476,16 +479,46 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
476
479
|
const cfgModels = (lkgR.effective.models?.models ?? []).filter((m) => m.enabled !== false).length;
|
|
477
480
|
logger.info("config_loaded", { source: "lkg", version: lkgR.effective.version, worker: cc?.worker ?? "(global)", models: cfgModels });
|
|
478
481
|
};
|
|
482
|
+
const bootFallBackWhole = async (why) => {
|
|
483
|
+
const lkgFallback = await bootFromLkg(why, "whole");
|
|
484
|
+
if (!lkgFallback)
|
|
485
|
+
return;
|
|
486
|
+
lkgBooted = true;
|
|
487
|
+
try {
|
|
488
|
+
await bootApplyLkgInline(lkgFallback);
|
|
489
|
+
}
|
|
490
|
+
catch (applyErr) {
|
|
491
|
+
lkgBooted = false;
|
|
492
|
+
logger.warn("config_lkg_apply_failed", { err: String(applyErr), note: "persisted LKG failed to apply — env fallback serves; the cache rewrites on the next clean candidate" });
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const warnLocalNoModels = (r, rejected) => {
|
|
496
|
+
const modelsDomainErr = (r.domainErrors ?? []).find((de) => de.domain === "models");
|
|
497
|
+
const modelsDropped = (r.droppedEntries ?? []).filter((d) => d.domain === "models");
|
|
498
|
+
const totalModels = (r.effective.models?.models ?? []).length;
|
|
499
|
+
logger.warn("config_local_no_models", {
|
|
500
|
+
...localDirField,
|
|
501
|
+
...(modelsDomainErr ? { domainError: modelsDomainErr.error } : {}),
|
|
502
|
+
...(modelsDropped.length > 0 ? { droppedEntries: modelsDropped.map((d) => d.entry) } : {}),
|
|
503
|
+
hint: rejected && modelsDomainErr
|
|
504
|
+
? "config.d/models.json 在场但 models 域判形失败,整份本地候选被拒(真因见本行 domainError)— 模型继续走 env 兜底"
|
|
505
|
+
: rejected
|
|
506
|
+
? "config.d 里别的域判形失败,整份本地候选被拒(坏域与真因见 config_candidate_rejected)— config.d/models.json 的模型不用,继续走 env 兜底"
|
|
507
|
+
: modelsDropped.length > 0 && totalModels === 0
|
|
508
|
+
? "config.d/models.json 的条目全部被逐条丢弃(逐条真因见 config_entry_dropped)— 模型继续走 env 兜底"
|
|
509
|
+
: totalModels > 0
|
|
510
|
+
? `config.d/models.json 有 ${totalModels} 条模型但全部 enabled:false — 模型继续走 env 兜底`
|
|
511
|
+
: "config.d/models.json 空或 CONFIG_LOCAL_DIR 指错目录(选目录旋钮是 CONFIG_LOCAL_DIR,不是 LOCAL_DATA_ROOT)— 模型继续走 env 兜底",
|
|
512
|
+
});
|
|
513
|
+
};
|
|
479
514
|
let bootApplyPhase = false;
|
|
480
515
|
try {
|
|
481
516
|
const bootFetch = pullEffective(configProvider, undefined);
|
|
482
517
|
const raced = await raceBootFetch(bootFetch, config.configBootFetchBudgetMs);
|
|
483
|
-
let lkgFallback = null;
|
|
484
518
|
if (raced === BOOT_FETCH_DEFERRED) {
|
|
485
519
|
bootConfigPending = bootFetch;
|
|
486
520
|
bootFetch.catch(() => { });
|
|
487
|
-
|
|
488
|
-
lkgBooted = lkgFallback !== null;
|
|
521
|
+
await bootFallBackWhole("boot fetch deferred (budget exceeded)");
|
|
489
522
|
logger.warn("config_boot_fetch_deferred", {
|
|
490
523
|
source: configProvider.kind,
|
|
491
524
|
budgetMs: config.configBootFetchBudgetMs,
|
|
@@ -495,84 +528,76 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
495
528
|
: "center slow/unreachable and no usable LKG — serving on the env fallback; the pull continues in the background (late-boot adoption on arrival). Raise CONFIG_BOOT_FETCH_BUDGET_MS to block boot instead.",
|
|
496
529
|
});
|
|
497
530
|
}
|
|
498
|
-
if (raced === BOOT_FETCH_DEFERRED && lkgFallback) {
|
|
499
|
-
try {
|
|
500
|
-
await bootApplyLkgInline(lkgFallback);
|
|
501
|
-
}
|
|
502
|
-
catch (applyErr) {
|
|
503
|
-
lkgBooted = false;
|
|
504
|
-
logger.warn("config_lkg_apply_failed", { err: String(applyErr), note: "persisted LKG failed to apply — env fallback serves; the cache rewrites on the next clean candidate" });
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
531
|
const r = raced === BOOT_FETCH_DEFERRED ? null : raced;
|
|
508
532
|
if (r) {
|
|
509
533
|
bootApplyPhase = true;
|
|
510
534
|
for (const de of r.domainErrors ?? [])
|
|
511
|
-
logger.warn("config_domain_invalid", { domain: de.domain, error: de.error, note:
|
|
512
|
-
warnDroppedEntries(r, "boot");
|
|
535
|
+
logger.warn("config_domain_invalid", { domain: de.domain, error: de.error, note: CONFIG_DOMAIN_INVALID_NOTE });
|
|
513
536
|
if (dryRun) {
|
|
514
|
-
|
|
515
|
-
if (
|
|
537
|
+
const dryVerdict = judgeConfigCandidate(r);
|
|
538
|
+
if (dryVerdict.verdict === "rejected") {
|
|
539
|
+
announceCandidateRejected(r, dryVerdict, "boot");
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
warnReadNotices(r, "boot");
|
|
543
|
+
logEffectiveDiff(config, r.effective, logger);
|
|
516
544
|
ccEtag = r.etag;
|
|
545
|
+
}
|
|
517
546
|
}
|
|
518
547
|
else {
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
bootReport = rep;
|
|
528
|
-
recordApply(rep);
|
|
529
|
-
},
|
|
530
|
-
});
|
|
531
|
-
markApplied(bootGen, r.effective.version);
|
|
532
|
-
const bootClean = fullyLandedForLkg(r, bootReport);
|
|
533
|
-
if (bootClean)
|
|
534
|
-
ccEtag = r.etag;
|
|
535
|
-
await adoptCenterPrompts(r.effective, "boot");
|
|
536
|
-
let bootPlaneEff = modelPlaneCommitted(bootReport) ? r.effective : undefined;
|
|
537
|
-
if (bootPlaneEff === undefined) {
|
|
538
|
-
const lkgPlane = await bootFromLkg("boot candidate's model plane was refused", "model-plane");
|
|
539
|
-
if (lkgPlane) {
|
|
540
|
-
let planeReport;
|
|
541
|
-
applyEffective(config, withModelPlaneOf(r.effective, lkgPlane.effective), logger, { leg: "boot", sealedKeys, generation: bootGen, report: (rep) => (planeReport = rep) });
|
|
542
|
-
if (modelPlaneCommitted(planeReport)) {
|
|
543
|
-
bootPlaneEff = lkgPlane.effective;
|
|
544
|
-
logger.warn("config_model_plane_from_lkg", { version: r.effective.version, lkgVersion: lkgPlane.effective.version, note: "the live candidate's model plane was refused at boot — serving the LKG's model plane (catalog and the collab templates that reference it) with the live candidate's other groups; the refresh cadence re-judges the candidate" });
|
|
545
|
-
}
|
|
546
|
-
}
|
|
548
|
+
const bootVerdict = judgeConfigCandidate(r);
|
|
549
|
+
if (bootVerdict.verdict === "rejected") {
|
|
550
|
+
await bootFallBackWhole(`boot candidate rejected: ${bootVerdict.domains.join(",")}`);
|
|
551
|
+
const rejectedGen = generationOf(r.effective.version);
|
|
552
|
+
applyLedger.seenTarget(rejectedGen, r.effective.version);
|
|
553
|
+
announceCandidateRejected(r, bootVerdict, "boot");
|
|
554
|
+
if (configProvider.kind === "local")
|
|
555
|
+
warnLocalNoModels(r, true);
|
|
547
556
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const modelsDomainErr = (r.domainErrors ?? []).find((de) => de.domain === "models");
|
|
562
|
-
const modelsDropped = (r.droppedEntries ?? []).filter((d) => d.domain === "models");
|
|
563
|
-
const totalModels = (r.effective.models?.models ?? []).length;
|
|
564
|
-
logger.warn("config_local_no_models", {
|
|
565
|
-
...cfgDir,
|
|
566
|
-
...(modelsDomainErr ? { domainError: modelsDomainErr.error } : {}),
|
|
567
|
-
...(modelsDropped.length > 0 ? { droppedEntries: modelsDropped.map((d) => d.entry) } : {}),
|
|
568
|
-
hint: modelsDomainErr
|
|
569
|
-
? "config.d/models.json 在场但 models 域判形失败落 default(真因见本行 domainError)— 模型继续走 env 兜底"
|
|
570
|
-
: modelsDropped.length > 0 && totalModels === 0
|
|
571
|
-
? "config.d/models.json 的条目全部被逐条丢弃(逐条真因见 config_entry_dropped)— 模型继续走 env 兜底"
|
|
572
|
-
: totalModels > 0
|
|
573
|
-
? `config.d/models.json 有 ${totalModels} 条模型但全部 enabled:false — 模型继续走 env 兜底`
|
|
574
|
-
: "config.d/models.json 空或 CONFIG_LOCAL_DIR 指错目录(选目录旋钮是 CONFIG_LOCAL_DIR,不是 LOCAL_DATA_ROOT)— 模型继续走 env 兜底",
|
|
557
|
+
else {
|
|
558
|
+
warnReadNotices(r, "boot");
|
|
559
|
+
const bootGen = generationOf(r.effective.version);
|
|
560
|
+
applyLedger.seenTarget(bootGen, r.effective.version);
|
|
561
|
+
let bootReport;
|
|
562
|
+
applyEffective(config, r.effective, logger, {
|
|
563
|
+
leg: "boot",
|
|
564
|
+
sealedKeys,
|
|
565
|
+
generation: bootGen,
|
|
566
|
+
report: (rep) => {
|
|
567
|
+
bootReport = rep;
|
|
568
|
+
recordApply(rep);
|
|
569
|
+
},
|
|
575
570
|
});
|
|
571
|
+
markApplied(bootGen, r.effective.version);
|
|
572
|
+
const bootClean = fullyLandedForLkg(r, bootReport);
|
|
573
|
+
if (bootClean)
|
|
574
|
+
ccEtag = r.etag;
|
|
575
|
+
await adoptCenterPrompts(r.effective, bootVerdict.prompts, "boot");
|
|
576
|
+
let bootPlaneEff = modelPlaneCommitted(bootReport) ? r.effective : undefined;
|
|
577
|
+
if (bootPlaneEff === undefined) {
|
|
578
|
+
const lkgPlane = await bootFromLkg("boot candidate's model plane was refused", "model-plane");
|
|
579
|
+
if (lkgPlane) {
|
|
580
|
+
let planeReport;
|
|
581
|
+
applyEffective(config, withModelPlaneOf(r.effective, lkgPlane.effective), logger, { leg: "boot", sealedKeys, generation: bootGen, report: (rep) => (planeReport = rep) });
|
|
582
|
+
if (modelPlaneCommitted(planeReport)) {
|
|
583
|
+
bootPlaneEff = lkgPlane.effective;
|
|
584
|
+
logger.warn("config_model_plane_from_lkg", { version: r.effective.version, lkgVersion: lkgPlane.effective.version, note: "the live candidate's model plane was refused at boot — serving the LKG's model plane (catalog and the collab templates that reference it) with the live candidate's other groups; the refresh cadence re-judges the candidate" });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (bootPlaneEff !== undefined)
|
|
589
|
+
markRosterLanded(bootPlaneEff);
|
|
590
|
+
effective = r.effective;
|
|
591
|
+
if (bootPlaneEff !== undefined)
|
|
592
|
+
appliedPlaneEff = bootPlaneEff;
|
|
593
|
+
if (bootClean)
|
|
594
|
+
latestEffective = r.effective;
|
|
595
|
+
if (bootClean && !lkgBooted)
|
|
596
|
+
bootLkgCandidate = { effective: r.effective, etag: r.etag };
|
|
597
|
+
const cfgModels = (r.effective.models?.models ?? []).filter((m) => m.enabled !== false).length;
|
|
598
|
+
logger.info("config_loaded", { source: configProvider.kind, version: r.effective.version, worker: cc?.worker ?? "(global)", models: cfgModels, ...localDirField });
|
|
599
|
+
if (configProvider.kind === "local" && cfgModels === 0)
|
|
600
|
+
warnLocalNoModels(r, false);
|
|
576
601
|
}
|
|
577
602
|
}
|
|
578
603
|
if (configProvider.kind === "remote")
|
|
@@ -584,17 +609,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
584
609
|
logger.warn("config_apply_refused", { source: configProvider.kind, err: String(err), note: "the pulled candidate failed to apply at boot — falling back to the LKG / env; this is not a reachability failure" });
|
|
585
610
|
else
|
|
586
611
|
logger.warn("config_source_unreachable_using_env", { source: configProvider.kind, err: String(err) });
|
|
587
|
-
|
|
588
|
-
if (lkgFallback) {
|
|
589
|
-
lkgBooted = true;
|
|
590
|
-
try {
|
|
591
|
-
await bootApplyLkgInline(lkgFallback);
|
|
592
|
-
}
|
|
593
|
-
catch (applyErr) {
|
|
594
|
-
lkgBooted = false;
|
|
595
|
-
logger.warn("config_lkg_apply_failed", { err: String(applyErr), note: "persisted LKG failed to apply — env fallback serves" });
|
|
596
|
-
}
|
|
597
|
-
}
|
|
612
|
+
await bootFallBackWhole(`${bootApplyPhase ? "boot apply refused" : "boot fetch failed"}: ${String(err).slice(0, 120)}`);
|
|
598
613
|
}
|
|
599
614
|
if (!dryRun && cc !== undefined && currentIdentity() !== promptStateIdentity)
|
|
600
615
|
await rebasePromptState(currentIdentity(), "boot-end");
|
|
@@ -841,32 +856,13 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
841
856
|
if (r) {
|
|
842
857
|
const tickGen = arrival !== undefined ? useArrivalGeneration(r.effective.version, arrival.ordinal) : generationOf(r.effective.version);
|
|
843
858
|
applyLedger.seenTarget(tickGen, r.effective.version);
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
...new Set([...(r.domainErrors ?? []).map((de) => de.domain), ...(promptsGate.ok ? [] : ["prompts"])]),
|
|
848
|
-
]
|
|
849
|
-
.sort()
|
|
850
|
-
.join(",");
|
|
851
|
-
if (badDomains) {
|
|
852
|
-
if (lastRejectedCandidate?.version !== r.effective.version || lastRejectedCandidate.domains !== badDomains) {
|
|
853
|
-
lastRejectedCandidate = { version: r.effective.version, domains: badDomains, at: Date.now() };
|
|
854
|
-
logger.warn("config_candidate_rejected", {
|
|
855
|
-
version: r.effective.version,
|
|
856
|
-
domains: badDomains,
|
|
857
|
-
errors: [
|
|
858
|
-
...(r.domainErrors ?? []).map((de) => ({ domain: de.domain, error: de.error })),
|
|
859
|
-
...(promptsGate.ok ? [] : [{ domain: "prompts", error: promptsGate.error }]),
|
|
860
|
-
],
|
|
861
|
-
...(r.droppedEntries !== undefined ? { droppedEntries: r.droppedEntries } : {}),
|
|
862
|
-
lkgVersion: latestEffective?.version ?? "(env)",
|
|
863
|
-
note: "candidate rejected whole (§9.5-5) — live config keeps the last-known-good; fix the named files to unblock",
|
|
864
|
-
});
|
|
865
|
-
}
|
|
859
|
+
const verdict = judgeConfigCandidate(r);
|
|
860
|
+
if (verdict.verdict === "rejected") {
|
|
861
|
+
announceCandidateRejected(r, verdict, arrival !== undefined ? "late-boot" : "refresh");
|
|
866
862
|
return "ok";
|
|
867
863
|
}
|
|
868
864
|
lastRejectedCandidate = undefined;
|
|
869
|
-
|
|
865
|
+
warnReadNotices(r, "refresh");
|
|
870
866
|
if (ccRef?.dryRun) {
|
|
871
867
|
logEffectiveDiff(config, r.effective, logger);
|
|
872
868
|
ccEtag = r.etag;
|
|
@@ -898,7 +894,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
898
894
|
if (tickPlane)
|
|
899
895
|
appliedPlaneEff = r.effective;
|
|
900
896
|
planeDeferredNoHandoff = undefined;
|
|
901
|
-
await adoptCenterPrompts(r.effective, "refresh");
|
|
897
|
+
await adoptCenterPrompts(r.effective, verdict.prompts, "refresh");
|
|
902
898
|
if (tickPlane)
|
|
903
899
|
markRosterLanded(r.effective);
|
|
904
900
|
if (!fullyLandedForLkg(r, applyReport)) {
|
|
@@ -969,18 +965,12 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
969
965
|
};
|
|
970
966
|
const deferredBootApplyInner = async (r, lateGen) => {
|
|
971
967
|
applyLedger.seenTarget(lateGen, r.effective.version);
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
logger.warn("config_boot_deferred_candidate_rejected", {
|
|
976
|
-
version: r.effective.version,
|
|
977
|
-
errors: [...(r.domainErrors ?? []).map((de) => ({ domain: de.domain, error: de.error })), ...(promptsGate.ok ? [] : [{ domain: "prompts", error: promptsGate.error }])],
|
|
978
|
-
...(r.droppedEntries !== undefined ? { droppedEntries: r.droppedEntries } : {}),
|
|
979
|
-
note: "late boot pull carried an invalid candidate — env fallback keeps serving; the refresh cadence re-judges the same tree (etag not advanced)",
|
|
980
|
-
});
|
|
968
|
+
const lateVerdict = judgeConfigCandidate(r);
|
|
969
|
+
if (lateVerdict.verdict === "rejected") {
|
|
970
|
+
announceCandidateRejected(r, lateVerdict, "late-boot");
|
|
981
971
|
return;
|
|
982
972
|
}
|
|
983
|
-
|
|
973
|
+
warnReadNotices(r, "boot-deferred");
|
|
984
974
|
if (ccRef?.dryRun) {
|
|
985
975
|
logEffectiveDiff(config, r.effective, logger);
|
|
986
976
|
ccEtag = r.etag;
|
|
@@ -1011,7 +1001,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
1011
1001
|
if (latePlane)
|
|
1012
1002
|
appliedPlaneEff = r.effective;
|
|
1013
1003
|
planeDeferredNoHandoff = undefined;
|
|
1014
|
-
await adoptCenterPrompts(r.effective, "boot-deferred");
|
|
1004
|
+
await adoptCenterPrompts(r.effective, lateVerdict.prompts, "boot-deferred");
|
|
1015
1005
|
if (latePlane)
|
|
1016
1006
|
markRosterLanded(r.effective);
|
|
1017
1007
|
if (!fullyLandedForLkg(r, applyReportLate)) {
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* ## 形与 present-iff
|
|
8
8
|
* 整个键只在接了中心(`config.webAdmin` 在场)时出现;`lease:"absent"`(中心三件不齐 ⇒ 没装租约管理器)时 `leaseHealth` /
|
|
9
9
|
* `failMode` / `caller` 三键缺席。「键缺席」于是两义:老 worker / 没接中心 —— 同一体的 `version` 能分。
|
|
10
|
-
* · `effectivePull` =
|
|
10
|
+
* · `effectivePull` = 最近一次实拉中心答了(`lastPull === "ok"`):拉取失败形(拉取失败后 LKG 起服 / 从未成功 / 最近一拍失败)= false;
|
|
11
|
+
* 起服遇拒读回 LKG 且中心答了 = true(304、被候选判官整拒的拍同理);
|
|
11
12
|
* · `effectiveRevision` = 与 `/health.configAppliedVersion` 同一只 ledger 视图(`web-admin-client/pull-state.ts`);
|
|
12
13
|
* · `lease` / `leaseHealth` / `failMode` / `caller` = 租约管理器的 `snapshot()` 与生效姿态(与执行的那一只同源);
|
|
13
14
|
* · `usageReport` = 用量环装没装(装配事实:累加器与环同一只谓词 `centerLaneConfigured`);
|
|
@@ -53,6 +53,7 @@ export declare const COMPOSITE_CAPABILITY_MEMBERS: {
|
|
|
53
53
|
readonly retention: readonly ["mode", "maxAgeDays"];
|
|
54
54
|
readonly workflowsGate: readonly ["engineCan", "denial"];
|
|
55
55
|
readonly sealedKey: readonly ["alg", "publicKeyId", "publicKey"];
|
|
56
|
+
readonly workspaceSync: readonly ["persist", "keep"];
|
|
56
57
|
};
|
|
57
58
|
type CompositeTable = typeof COMPOSITE_CAPABILITY_MEMBERS;
|
|
58
59
|
/** 值的**具名成员对象臂**(标量 / 字面词 / `false` / `null` / `undefined` / 数组 / 函数臂剥掉)。 */
|
|
@@ -14,6 +14,7 @@ export const COMPOSITE_CAPABILITY_MEMBERS = {
|
|
|
14
14
|
retention: ["mode", "maxAgeDays"],
|
|
15
15
|
workflowsGate: ["engineCan", "denial"],
|
|
16
16
|
sealedKey: ["alg", "publicKeyId", "publicKey"],
|
|
17
|
+
workspaceSync: ["persist", "keep"],
|
|
17
18
|
};
|
|
18
19
|
export function gateCompositeMembers(body) {
|
|
19
20
|
return body;
|
|
@@ -23,6 +23,7 @@ import type { ServiceConfig } from "./config.js";
|
|
|
23
23
|
import type { OwnerAwareSessionStore } from "./security.js";
|
|
24
24
|
import type { ServiceFileHistoryStore } from "./plugins/store-backend.js";
|
|
25
25
|
import { resolveStreamApprovalGate } from "./tool-approval.js";
|
|
26
|
+
import { type WorkspaceSyncCapability } from "./workspace-space.js";
|
|
26
27
|
/** why 闭集:词 → 给运维看的一句义(诊断 / 测试读它;wire 上的拒句由各门自写)。 */
|
|
27
28
|
export declare const CAPABILITY_FACT_WHY: {
|
|
28
29
|
readonly run_store_absent: "no durable run store is wired (DB_BACKEND=mysql|pg|local)";
|
|
@@ -36,6 +37,7 @@ export declare const CAPABILITY_FACT_WHY: {
|
|
|
36
37
|
readonly session_leaf_face_absent: "the session store has no leaf-read face (sessionStorage.getLeafId)";
|
|
37
38
|
readonly session_lister_absent: "no session enumerator is wired (session store or run store listSessions)";
|
|
38
39
|
readonly session_fork_face_absent: "the session store has no fork face";
|
|
40
|
+
readonly session_row_faces_absent: "the session store has no session-row faces (sessionFaceOf / sessionSummaryOf / patchSessionMeta)";
|
|
39
41
|
readonly session_purge_absent: "no session purge coordinator is wired";
|
|
40
42
|
readonly session_policy_store_absent: "no session policy store is wired";
|
|
41
43
|
readonly store_backend_absent: "no store backend is wired (DB_BACKEND=memory, multi-tenant bare boot, or half-configured SQL)";
|
|
@@ -79,6 +81,8 @@ export declare const CAPABILITY_FACT_WHY: {
|
|
|
79
81
|
readonly scheduler_disabled: "the self-wake scheduler is off (SCHEDULER_ENABLED)";
|
|
80
82
|
readonly multi_tenant_deployment: "this is a multi-tenant deployment (REQUIRE_PRINCIPAL=true)";
|
|
81
83
|
readonly not_host_lane: "the execution lane is not the single-user host lane (REMOTE_EXEC=host)";
|
|
84
|
+
readonly not_k8s_lane: "the execution lane is not the k8s pod sandbox (REMOTE_EXEC=k8s)";
|
|
85
|
+
readonly workspace_snapshot_store_absent: "no workspace snapshot store is configured (MINIO_ENDPOINT / MINIO_ACCESS_KEY / MINIO_SECRET_KEY)";
|
|
82
86
|
};
|
|
83
87
|
export type CapabilityFactWhy = keyof typeof CAPABILITY_FACT_WHY;
|
|
84
88
|
/** 不可用臂。 */
|
|
@@ -137,6 +141,12 @@ export declare function sessionList(d: Pick<Deps, "sessionStorage" | "runStore">
|
|
|
137
141
|
export declare function sessionFork(d: Pick<Deps, "sessionStorage">): Fact<"session_fork_face_absent" | "session_owner_face_absent", {
|
|
138
142
|
sessionStorage: WithMethods<OwnerAwareSessionStore, "ownerOf" | "fork">;
|
|
139
143
|
}>;
|
|
144
|
+
/** 会话行可写位(`PATCH /v1/sessions/:id`):会话店的会话面(属主 + 化身一次读 —— 门判属主、写与读回按化身条件)∧ 行写口(写后按同一住客读回)。
|
|
145
|
+
* 三只 durable 店(SQL 双生 / local)都带;内存 dev 店没有 ⇒ 501。
|
|
146
|
+
* 本面**没有**独立能力位(契约附录 B.2c 登记):老 worker 对这一动词答 405,本事实为假的部署答 501。 */
|
|
147
|
+
export declare function sessionPatch(d: Pick<Deps, "sessionStorage">): Fact<"session_row_faces_absent", {
|
|
148
|
+
sessionStorage: WithMethods<OwnerAwareSessionStore, "sessionFaceOf" | "patchSessionMeta">;
|
|
149
|
+
}>;
|
|
140
150
|
export declare function sessionDelete(d: Pick<Deps, "purgeSession" | "sessionStorage">): Fact<"session_purge_absent" | "session_owner_face_absent", {
|
|
141
151
|
purge: Store<"purgeSession">;
|
|
142
152
|
sessionStorage: OwnedSessionStore;
|
|
@@ -145,10 +155,12 @@ export declare function sessionPolicy(d: Pick<Deps, "sessionPolicyStore" | "sess
|
|
|
145
155
|
policyStore: Store<"sessionPolicyStore">;
|
|
146
156
|
sessionStorage: OwnedSessionStore;
|
|
147
157
|
}>;
|
|
148
|
-
type SyncSessionStore = WithMethods<OwnerAwareSessionStore, "ownerOf" | "exportEntries" | "exportEntriesStream" | "beginImportStaging">;
|
|
149
|
-
type SessionSyncWhy = "store_backend_absent" | "session_owner_face_absent" | "file_history_store_absent" | "session_export_absent" | "session_import_staging_absent" | "policy_purge_absent";
|
|
158
|
+
type SyncSessionStore = WithMethods<OwnerAwareSessionStore, "ownerOf" | "exportEntries" | "exportEntriesStream" | "beginImportStaging" | "sessionFaceOf" | "sessionSummaryOf" | "patchSessionMeta">;
|
|
159
|
+
type SessionSyncWhy = "store_backend_absent" | "session_owner_face_absent" | "file_history_store_absent" | "session_export_absent" | "session_import_staging_absent" | "session_row_faces_absent" | "policy_purge_absent";
|
|
150
160
|
/** 会话对等同步的**对话半族**(manifest / entries / plan / import 两相):后端 ∧ 属主面 ∧ 文件历史店 ∧ 导出两面 ∧
|
|
151
|
-
* staged 导入面 ∧
|
|
161
|
+
* staged 导入面 ∧ 会话行三口(manifest 带 `title` / `pinned`、导入经唯一写口按导入落定的那一位住客重放它们 —— 会话面读住客)∧
|
|
162
|
+
* policy 店能清(overwrite-dst 的 commit 要擦目的端旧 policy 行)。
|
|
163
|
+
* 文件历史的 blob 搬运是另一只事实。 */
|
|
152
164
|
export declare function sessionSync(d: Pick<Deps, "backend" | "sessionStorage" | "fileHistoryStore" | "sessionPolicyStore">): Fact<SessionSyncWhy, {
|
|
153
165
|
backend: Store<"backend">;
|
|
154
166
|
sessionStorage: SyncSessionStore;
|
|
@@ -290,6 +302,19 @@ export declare function workflowsJournal(d: Pick<Deps, "workflowRunStore" | "wor
|
|
|
290
302
|
store: Store<"workflowRunStore">;
|
|
291
303
|
journal: Store<"workflowJournalStore">;
|
|
292
304
|
}>;
|
|
305
|
+
/**
|
|
306
|
+
* k8s 车道工作区跨轮保留的有效策略 —— k8s 车道 ∧ 快照存储在场 ⇒ `{ persist, keep }`(未表态的旋钮取缺省,缺省值属主
|
|
307
|
+
* `workspace-space.ts`;`persist` 缺省 `off`,opt-in `space`)。**唯一读法**:能力位 `workspaceSync` 投影它的见证,k8s 适配器工厂(`boot/execution-env.ts`)把同一份
|
|
308
|
+
* 见证交给适配器(适配器自己不再解析策略)。`persist: "off"` 仍是 available(存储在、没开 —— 缺省或运维显式 off)—— 与缺席不同义。
|
|
309
|
+
*
|
|
310
|
+
* **为何只读配置仍恒真**:`space` 的空间按会话住客分区,住客只有一只读口 —— 会话店的 `sessionFaceOf`。会话店没有它(内存会话店)的
|
|
311
|
+
* 组合在起服时就被拒(`boot/execution-env.ts` 的 `assertWorkspaceSpaceReadable`,装配第 5 段,读的正是本事实的见证)⇒ 凡是起得来、
|
|
312
|
+
* 报 `space` 的部署都读得出住客,本事实不需要运行期依赖(再加一位 = 同一前提两处判)。剩下的「这条 run 读不到住客」是逐 run 的读失败
|
|
313
|
+
* (适配器的 `unavailable` 臂,响亮一行),不是部署形。
|
|
314
|
+
*/
|
|
315
|
+
export declare function workspaceSpace(c: Pick<ServiceConfig, "remoteExec">): Fact<"not_k8s_lane" | "workspace_snapshot_store_absent", {
|
|
316
|
+
policy: WorkspaceSyncCapability;
|
|
317
|
+
}>;
|
|
293
318
|
/** 自唤醒调度器挂在宿主 env 上:`SCHEDULER_ENABLED` ∧ 单用户 ∧ host 车道。装配(`boot/execution-env.ts`)与能力位同调。 */
|
|
294
319
|
export declare function hostScheduler(c: Pick<ServiceConfig, "schedulerEnabled" | "requirePrincipal" | "remoteExec">): Fact<"scheduler_disabled" | "multi_tenant_deployment" | "not_host_lane">;
|
|
295
320
|
export {};
|