@sema-agent/server 7.120.0 → 7.122.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 +173 -0
- package/README.md +1 -1
- package/README.zh-CN.md +3 -1
- package/USAGE.md +97 -29
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -2
- package/deploy/sema-up/chart/templates/server.yaml +2 -2
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +1 -1
- package/dist/approval-reconciler.d.ts +8 -0
- package/dist/approval-reconciler.js +72 -52
- package/dist/auto-mode-face.d.ts +9 -2
- package/dist/auto-mode-face.js +5 -3
- package/dist/bench/s1/live-deps.js +1 -0
- package/dist/boot/config-center.d.ts +5 -7
- package/dist/boot/config-center.js +33 -9
- package/dist/boot/engine-lease.d.ts +1 -1
- package/dist/boot/leader.js +1 -0
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +2 -1
- package/dist/boot/resolve-spec.js +2 -4
- package/dist/boot/shutdown.d.ts +9 -10
- package/dist/boot/shutdown.js +59 -16
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -9
- package/dist/boot/stage-10-http-server.d.ts +1 -9
- package/dist/boot/stage-10-http-server.js +31 -8
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/memory-notice.d.ts +1 -1
- package/dist/config-catalog.js +12 -6
- package/dist/config-center/center-request.d.ts +13 -1
- package/dist/config-center/center-request.js +25 -10
- package/dist/config-types.d.ts +44 -5
- package/dist/config-types.js +6 -0
- package/dist/config.d.ts +35 -6
- package/dist/config.js +113 -33
- package/dist/fleet/drain-entry.d.ts +112 -0
- package/dist/fleet/drain-entry.js +38 -0
- package/dist/fleet/engine-signal.d.ts +144 -0
- package/dist/fleet/engine-signal.js +60 -0
- package/dist/fleet/serving-lease.d.ts +47 -0
- package/dist/fleet/serving-lease.js +233 -0
- package/dist/fleet-client.d.ts +35 -19
- package/dist/fleet-client.js +49 -25
- package/dist/governance-overrides.d.ts +7 -2
- package/dist/http/admission.js +7 -5
- package/dist/http/approval-sweeps.js +5 -7
- package/dist/http/checkpoint-ctx.d.ts +60 -11
- package/dist/http/checkpoint-ctx.js +23 -9
- package/dist/http/dispatch.js +33 -16
- package/dist/http/health-self-report.d.ts +60 -0
- package/dist/http/health-self-report.js +53 -0
- package/dist/http/principal-gate.d.ts +8 -1
- package/dist/http/principal-gate.js +1 -0
- package/dist/http/readiness.d.ts +25 -5
- package/dist/http/readiness.js +31 -6
- package/dist/http/resume-legs.js +9 -9
- package/dist/http/route-ctx.d.ts +25 -7
- package/dist/http/route-table.d.ts +6 -0
- package/dist/http/route-table.js +8 -1
- package/dist/http/routes/a2a-serve.d.ts +1 -1
- package/dist/http/routes/a2a-serve.js +5 -5
- package/dist/http/routes/admin-drain.d.ts +15 -31
- package/dist/http/routes/admin-drain.js +28 -10
- package/dist/http/routes/approvals-assistant.d.ts +4 -3
- package/dist/http/routes/approvals-assistant.js +9 -9
- package/dist/http/routes/capabilities.js +7 -5
- package/dist/http/routes/devices.js +3 -2
- package/dist/http/routes/fleet.d.ts +1 -0
- package/dist/http/routes/fleet.js +1 -1
- package/dist/http/routes/images.d.ts +1 -0
- package/dist/http/routes/images.js +1 -1
- package/dist/http/routes/leader.d.ts +1 -1
- package/dist/http/routes/leader.js +7 -1
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/notify-wake.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +1 -1
- package/dist/http/routes/rules.js +4 -3
- package/dist/http/routes/runs.d.ts +4 -2
- package/dist/http/routes/runs.js +7 -6
- package/dist/http/routes/sessions.d.ts +1 -0
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.d.ts +1 -1
- package/dist/http/routes/side-query.js +1 -1
- package/dist/http/routes/tasks.d.ts +2 -2
- package/dist/http/routes/tasks.js +3 -3
- package/dist/http/routes/trace-usage.d.ts +1 -0
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +1 -1
- package/dist/http/send.d.ts +13 -0
- package/dist/http/send.js +9 -0
- package/dist/http/server.d.ts +10 -21
- package/dist/http/server.js +20 -1
- package/dist/http/stored-body-replay.d.ts +27 -0
- package/dist/http/stored-body-replay.js +23 -0
- package/dist/http/wire-types.d.ts +7 -1
- package/dist/leader/leader.d.ts +22 -1
- package/dist/leader/leader.js +4 -1
- package/dist/leader/repair-wire.d.ts +1 -1
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/run-entry.d.ts +27 -0
- package/dist/leader/run-entry.js +29 -0
- package/dist/leader/wire.d.ts +10 -0
- package/dist/leader/wire.js +15 -10
- package/dist/model-reach-scope.d.ts +15 -3
- package/dist/model-reach-scope.js +3 -2
- package/dist/model-route-url.d.ts +13 -20
- package/dist/model-route-url.js +28 -24
- package/dist/model-unreachable-notice.d.ts +2 -1
- package/dist/model-unreachable-notice.js +1 -0
- package/dist/observability/fail-open.d.ts +35 -2
- package/dist/observability/fail-open.js +20 -0
- package/dist/plugins/approval-ask-store-file.d.ts +1 -0
- package/dist/plugins/approval-ask-store-file.js +3 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +1 -0
- package/dist/plugins/approval-ask-store-memory.js +6 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +12 -0
- package/dist/plugins/approval-ask-store-sql.js +4 -0
- package/dist/plugins/one-time-migrations.js +14 -1
- package/dist/plugins/park-commit-watch.d.ts +19 -5
- package/dist/plugins/park-commit-watch.js +24 -17
- package/dist/plugins/permission-rule-store-sql.d.ts +2 -1
- package/dist/plugins/remote-env-device.js +0 -1
- package/dist/request-key-closure.js +1 -1
- package/dist/router/route-orchestration.d.ts +4 -2
- package/dist/router/route-orchestration.js +2 -1
- package/dist/rules-consent.d.ts +6 -2
- package/dist/rules-consent.js +3 -0
- package/dist/run-local.js +3 -1
- package/dist/runs.js +1 -1
- package/dist/runtime-governance.d.ts +44 -32
- package/dist/runtime-governance.js +23 -9
- package/dist/sighup-idle.d.ts +7 -1
- package/dist/sighup-idle.js +4 -4
- package/dist/task-settings.d.ts +37 -14
- package/dist/task-settings.js +20 -20
- package/dist/tool-approval.d.ts +13 -0
- package/dist/tool-approval.js +17 -2
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/redact.d.ts +3 -3
- package/dist/trace/self-minted-wire.d.ts +64 -0
- package/dist/trace/self-minted-wire.js +30 -0
- package/dist/unattended-permission-mode.d.ts +17 -13
- package/dist/unattended-permission-mode.js +3 -1
- package/dist/url-shape.d.ts +31 -0
- package/dist/url-shape.js +41 -0
- package/package.json +3 -3
- package/scripts/drain-check-local.mjs +1 -1
|
@@ -6,6 +6,7 @@ import { buildRevokeFrame } from "./approval-card.js";
|
|
|
6
6
|
import { encodeCheckpointScope } from "./security.js";
|
|
7
7
|
const RECONCILE_STORE_TIMEOUT_MS = 10_000;
|
|
8
8
|
const RECONCILE_SEGMENT_BUDGET_MS = 30_000;
|
|
9
|
+
const PARK_COMMIT_BIND_BUDGET_MS = 2_000;
|
|
9
10
|
function withDeadline(op, label, timeoutMs = RECONCILE_STORE_TIMEOUT_MS) {
|
|
10
11
|
let timer;
|
|
11
12
|
return Promise.race([
|
|
@@ -80,6 +81,13 @@ export function decideReconcileAction(input) {
|
|
|
80
81
|
}
|
|
81
82
|
export function createApprovalReconciler(deps) {
|
|
82
83
|
const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, unattendedPolicy, emitRevoke } = deps;
|
|
84
|
+
const warn = (event, fields) => {
|
|
85
|
+
try {
|
|
86
|
+
logger.warn(event, fields);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
}
|
|
90
|
+
};
|
|
83
91
|
const revoke = (row, askIds, reason, nowMs) => {
|
|
84
92
|
if (askIds.length === 0 || !emitRevoke)
|
|
85
93
|
return;
|
|
@@ -87,39 +95,32 @@ export function createApprovalReconciler(deps) {
|
|
|
87
95
|
emitRevoke(buildRevokeFrame(row.batchId, askIds, reason, nowMs, row.taskId), { owner: row.owner, sessionId: row.sessionId, taskId: row.taskId });
|
|
88
96
|
}
|
|
89
97
|
catch (err) {
|
|
90
|
-
|
|
91
|
-
logger.warn("approval_revoke_emit_failed", { batchId: row.batchId, count: askIds.length, err: err instanceof Error ? err.message : String(err) });
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
}
|
|
98
|
+
warn("approval_revoke_emit_failed", { batchId: row.batchId, count: askIds.length, err: err instanceof Error ? err.message : String(err) });
|
|
95
99
|
}
|
|
96
100
|
};
|
|
97
|
-
const
|
|
101
|
+
const bindAtGate = async (ask, nowMs, deadlineMs) => {
|
|
102
|
+
const expired = () => deadlineMs !== undefined && Date.now() >= deadlineMs;
|
|
98
103
|
const batchRow = await withDeadline(askStore.getBatch(ask.batchId), "getBatch");
|
|
99
104
|
let batchState = batchRow?.state ?? "MISSING";
|
|
100
105
|
let batchBoundAskId = batchRow?.boundAskId ?? null;
|
|
101
106
|
const irreversibleBatch = batchState === "ABORTED" || (batchState === "ROUTING_BOUND" && batchBoundAskId !== null && batchBoundAskId !== ask.askId);
|
|
102
107
|
let gateMatch;
|
|
103
|
-
if (checkpoints && !irreversibleBatch) {
|
|
108
|
+
if (checkpoints && !irreversibleBatch && !expired()) {
|
|
104
109
|
const candidates = await withDeadline(checkpoints.findCheckpointCandidatesForAsk(encodeCheckpointScope(ask.owner), ask.sessionId, ask.toolCallId, ask.createdAtMs), "findCheckpointCandidatesForAsk");
|
|
105
110
|
gateMatch = classifyGateMatch(ask, candidates);
|
|
106
111
|
if (gateMatch.kind === "hash_mismatch") {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
}
|
|
112
|
+
warn("approval_reconcile_hash_mismatch", {
|
|
113
|
+
askId: ask.askId,
|
|
114
|
+
taskId: ask.taskId,
|
|
115
|
+
toolCallId: ask.toolCallId,
|
|
116
|
+
candidates: gateMatch.candidates,
|
|
117
|
+
reason: "ask and checkpoint minted the same bound input under the same (sourceTaskId, toolCallId) identity but the digests differ — " +
|
|
118
|
+
"the known causes are deployment-side (a hook/policy mutating an object it still holds a reference to between the two mints, " +
|
|
119
|
+
"or a stateful getter returning a fresh value per read), not out-of-band interference; this ask keeps its fail-safe route (no park, no denial)",
|
|
120
|
+
});
|
|
120
121
|
}
|
|
121
122
|
const match = gateMatch.kind === "match" ? gateMatch.candidate : undefined;
|
|
122
|
-
if (match) {
|
|
123
|
+
if (match && !expired()) {
|
|
123
124
|
const res = await withDeadline(askStore.bindBatch(ask.batchId, ask.askId, {
|
|
124
125
|
gateToken: match.token,
|
|
125
126
|
gateBoundCallId: match.boundCallId,
|
|
@@ -127,12 +128,20 @@ export function createApprovalReconciler(deps) {
|
|
|
127
128
|
}), "bindBatch");
|
|
128
129
|
if (res.ok) {
|
|
129
130
|
revoke(ask, res.voidedSiblings, "superseded_by_park", nowMs);
|
|
130
|
-
return {
|
|
131
|
+
return { bound: true, match: gateMatch };
|
|
131
132
|
}
|
|
132
133
|
batchState = res.batchState;
|
|
133
134
|
batchBoundAskId = res.boundAskId ?? null;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
137
|
+
return { bound: false, ...(gateMatch !== undefined ? { match: gateMatch } : {}), batchState, batchBoundAskId, irreversibleBatch };
|
|
138
|
+
};
|
|
139
|
+
const reconcileOne = async (ask, nowMs) => {
|
|
140
|
+
const attempt = await bindAtGate(ask, nowMs);
|
|
141
|
+
if (attempt.bound)
|
|
142
|
+
return { outcome: "parked", match: attempt.match };
|
|
143
|
+
const { batchState, batchBoundAskId, irreversibleBatch } = attempt;
|
|
144
|
+
const gateMatch = attempt.match;
|
|
136
145
|
const run = runs && !irreversibleBatch ? ((await withDeadline(runs.getRun(ask.taskId), "getRun")) ?? null) : null;
|
|
137
146
|
const action = decideReconcileAction({ ask, candidates: [], run, batchState, batchBoundAskId, nowMs, adhocGraceMs, orphanTtlMs, allowBind: false });
|
|
138
147
|
switch (action.kind) {
|
|
@@ -149,7 +158,7 @@ export function createApprovalReconciler(deps) {
|
|
|
149
158
|
if (!won)
|
|
150
159
|
return { outcome: "held", match: gateMatch };
|
|
151
160
|
if (action.reason === VOID_REASONS.ORPHAN_TTL_EXCEEDED) {
|
|
152
|
-
|
|
161
|
+
warn("approval_ask_orphan_voided", { askId: ask.askId, taskId: ask.taskId, ageMs: nowMs - ask.createdAtMs, ttlMs: orphanTtlMs });
|
|
153
162
|
}
|
|
154
163
|
revoke(ask, [ask.askId], action.reason === VOID_REASONS.BATCH_BOUND_ELSEWHERE ? "superseded_by_park" : "aborted", nowMs);
|
|
155
164
|
return { outcome: "voided", match: gateMatch };
|
|
@@ -160,6 +169,37 @@ export function createApprovalReconciler(deps) {
|
|
|
160
169
|
}
|
|
161
170
|
}
|
|
162
171
|
};
|
|
172
|
+
const parkCommits = deps.parkCommits;
|
|
173
|
+
const budgetMs = deps.parkCommitBindBudgetMs ?? PARK_COMMIT_BIND_BUDGET_MS;
|
|
174
|
+
const bindCommittedPark = async (calls) => {
|
|
175
|
+
const deadlineMs = Date.now() + budgetMs;
|
|
176
|
+
const work = (async () => {
|
|
177
|
+
const rows = await askStore.listParkingBySession(calls.sessionId);
|
|
178
|
+
for (const ask of rows) {
|
|
179
|
+
if (!calls.toolCallIds.includes(ask.toolCallId))
|
|
180
|
+
continue;
|
|
181
|
+
if (Date.now() >= deadlineMs) {
|
|
182
|
+
warn("approval_park_commit_bind_failed", { askId: ask.askId, taskId: ask.taskId, err: `park-commit bind budget (${budgetMs}ms) spent before this row`, fallback: "periodic_sweep" });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
if ((await bindAtGate(ask, Date.now(), deadlineMs)).bound)
|
|
187
|
+
metrics?.inc("approval_reconciled_total", { outcome: "parked" }, 1);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
warn("approval_park_commit_bind_failed", { askId: ask.askId, taskId: ask.taskId, err: err instanceof Error ? err.message : String(err), fallback: "periodic_sweep" });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
})();
|
|
194
|
+
parkCommits?.track(work);
|
|
195
|
+
try {
|
|
196
|
+
await withDeadline(work, "bindCommittedPark", budgetMs);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
warn("approval_park_commit_bind_failed", { sessionId: calls.sessionId, err: err instanceof Error ? err.message : String(err), fallback: "periodic_sweep" });
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
parkCommits?.onParkCommitted(bindCommittedPark);
|
|
163
203
|
return {
|
|
164
204
|
async runOnce(nowMs) {
|
|
165
205
|
const stats = {
|
|
@@ -184,11 +224,7 @@ export function createApprovalReconciler(deps) {
|
|
|
184
224
|
for (const ask of parking) {
|
|
185
225
|
if (Date.now() >= parkingDeadline) {
|
|
186
226
|
stats.budgetExhausted += 1;
|
|
187
|
-
|
|
188
|
-
logger.warn("approval_reconcile_budget_exhausted", { segment: "PARKING", processed: stats.parked + stats.denied + stats.voided + stats.held + stats.failed, scanned: stats.scanned, budgetMs: RECONCILE_SEGMENT_BUDGET_MS });
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
}
|
|
227
|
+
warn("approval_reconcile_budget_exhausted", { segment: "PARKING", processed: stats.parked + stats.denied + stats.voided + stats.held + stats.failed, scanned: stats.scanned, budgetMs: RECONCILE_SEGMENT_BUDGET_MS });
|
|
192
228
|
break;
|
|
193
229
|
}
|
|
194
230
|
if (ask.boundInputHash === null)
|
|
@@ -224,11 +260,7 @@ export function createApprovalReconciler(deps) {
|
|
|
224
260
|
}
|
|
225
261
|
catch (err) {
|
|
226
262
|
stats.failed += 1;
|
|
227
|
-
|
|
228
|
-
logger.warn("approval_reconcile_row_failed", { askId: ask.askId, taskId: ask.taskId, err: err instanceof Error ? err.message : String(err) });
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
}
|
|
263
|
+
warn("approval_reconcile_row_failed", { askId: ask.askId, taskId: ask.taskId, err: err instanceof Error ? err.message : String(err) });
|
|
232
264
|
}
|
|
233
265
|
}
|
|
234
266
|
const pending = await withDeadline(askStore.listByState("STREAM_PENDING", batchLimit), "listByState(STREAM_PENDING)");
|
|
@@ -236,11 +268,7 @@ export function createApprovalReconciler(deps) {
|
|
|
236
268
|
for (const row of pending) {
|
|
237
269
|
if (Date.now() >= orphanDeadline) {
|
|
238
270
|
stats.budgetExhausted += 1;
|
|
239
|
-
|
|
240
|
-
logger.warn("approval_reconcile_budget_exhausted", { segment: "STREAM_PENDING", expired: stats.orphansExpired, scanned: pending.length, budgetMs: RECONCILE_SEGMENT_BUDGET_MS });
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
}
|
|
271
|
+
warn("approval_reconcile_budget_exhausted", { segment: "STREAM_PENDING", expired: stats.orphansExpired, scanned: pending.length, budgetMs: RECONCILE_SEGMENT_BUDGET_MS });
|
|
244
272
|
break;
|
|
245
273
|
}
|
|
246
274
|
if (row.expiresAtMs + pendingGraceMs >= nowMs)
|
|
@@ -254,23 +282,15 @@ export function createApprovalReconciler(deps) {
|
|
|
254
282
|
}
|
|
255
283
|
catch (err) {
|
|
256
284
|
stats.failed += 1;
|
|
257
|
-
|
|
258
|
-
logger.warn("approval_orphan_expire_failed", { askId: row.askId, taskId: row.taskId, policy: unattendedPolicy, err: err instanceof Error ? err.message : String(err) });
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
}
|
|
285
|
+
warn("approval_orphan_expire_failed", { askId: row.askId, taskId: row.taskId, policy: unattendedPolicy, err: err instanceof Error ? err.message : String(err) });
|
|
262
286
|
}
|
|
263
287
|
}
|
|
264
288
|
if (stats.unmatchableNoHash > 0) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
catch {
|
|
273
|
-
}
|
|
289
|
+
warn("approval_reconcile_rows_unmatchable", {
|
|
290
|
+
rows: stats.unmatchableNoHash,
|
|
291
|
+
scanned: stats.scanned,
|
|
292
|
+
reason: "bound_input_hash is NULL — criterion 1 (identity ∧ hash) cannot match; rows fall through to the run-terminal / adhoc / orphan arms",
|
|
293
|
+
});
|
|
274
294
|
}
|
|
275
295
|
if (stats.parked + stats.denied + stats.voided + stats.orphansExpired > 0) {
|
|
276
296
|
metrics?.inc("approval_reconciled_total", { outcome: "parked" }, stats.parked);
|
package/dist/auto-mode-face.d.ts
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
*
|
|
16
16
|
* 本模块按**server 侧可知的同三项**算一份「若此刻提交会不会武装」的裁决,供壳(cli `sema doctor permissions`)
|
|
17
17
|
* 与人自查。三项分别对应三个未武装原因(判序 = 谓词的合取序,先判到的先报):
|
|
18
|
-
* · `mode_not_auto` —— 本次意图不是 auto(`?permissionMode=` 查询串给的;缺席 = 按 auto 意图答)
|
|
18
|
+
* · `mode_not_auto` —— 本次意图不是 auto(`?permissionMode=` 查询串给的;缺席 = 按 auto 意图答),或意图经本部署的运维入口地板
|
|
19
|
+
* (`AUTONOMY=plan`)钳过之后不是 auto —— 与受理门同一只钳,提交时那个词到不了引擎;
|
|
19
20
|
* · `deployment_incapable` —— 装配面没挂 `RunnerDeps.autoMode` 分类器席位(生产装配恒挂;桩/一次性 CLI 不挂);
|
|
20
21
|
* · `org_denied` —— per-principal caps 的 `autoMode === false` 且本地 deny 未设(center 显式 deny、center
|
|
21
22
|
* 硬失败的 fail-closed 拒项);
|
|
@@ -80,7 +81,7 @@ export declare const AUTO_MODE_UNARMED_REASONS: readonly ["mode_not_auto", "depl
|
|
|
80
81
|
export type AutoModeUnarmedReason = (typeof AUTO_MODE_UNARMED_REASONS)[number];
|
|
81
82
|
/** 锁步表:词表与语义一一对应(加员/删员任一侧不同步 = tsc 红)。语义只供文档/测试,不上 wire。 */
|
|
82
83
|
export declare const AUTO_MODE_UNARMED_REASON_SEMANTICS: {
|
|
83
|
-
readonly mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\")";
|
|
84
|
+
readonly mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\"),或意图经本部署的运维入口地板(AUTONOMY=plan)钳过之后不是 auto —— 与受理门同一只钳,提交时那个词到不了引擎";
|
|
84
85
|
readonly deployment_incapable: "装配面没挂 RunnerDeps.autoMode 分类器席位(本部署/本进程形不具备分类器)";
|
|
85
86
|
readonly org_denied: "per-principal caps.autoMode === false 且本地 deny 未设:center 显式 deny / center 硬失败 fail-closed 拒项";
|
|
86
87
|
readonly local_denied: "本地 catalog 键 PERMISSIONS_DISABLE_AUTO_MODE=true(center/settings 键名 permissions.disableAutoMode)把 caps.autoMode 折成 false";
|
|
@@ -118,11 +119,17 @@ export type AutoModeVerdict = {
|
|
|
118
119
|
* 三项合取的 server 侧裁决。`requestedMode` 缺席 = 按 auto 意图答(「若请求会不会武装」);`principal` =
|
|
119
120
|
* 调用方经门验证的身份(匿名 ⇒ undefined ⇒ center 客户端答 undefined,本地 deny 层照样罩)。
|
|
120
121
|
*
|
|
122
|
+
* 意图那一项判的是**提交之后到得了引擎的那个词**:本次意图先过本部署的运维入口地板(`floor`,调用方传 `runtime-governance.ts`
|
|
123
|
+
* `autonomyModeFloor(<活 config 的 AUTONOMY>)` —— 与受理门盖章读同一只属主),钳法 = core `clampPermissionMode`(受理门 `stampEntryPermissionMode`
|
|
124
|
+
* 用的同一只钳;地板缺席 ⇒ 原样)。`AUTONOMY=plan` 部署上任何意图都被钳成 `plan` ⇒ `mode_not_auto`(闭集词复用,不另铸)。
|
|
125
|
+
*
|
|
121
126
|
* 🔴 resolver 抛错**不吞**:合成腿的每一层都自带 fail-closed/缺席臂(center 客户端 catch-all、grant 腿 onFault),
|
|
122
127
|
* 走到这里还在抛 = 装配缺陷,读面 500 比一句编出来的 reason 诚实。
|
|
123
128
|
*/
|
|
124
129
|
export declare function judgeAutoModeArming(face: AutoModeFaceDeps | undefined, input: {
|
|
125
130
|
readonly requestedMode: PermissionMode | undefined;
|
|
131
|
+
/** 本部署此刻的运维入口地板(`autonomyModeFloor(config.autonomy)`;`undefined` = 没有地板)。必填:不传地板不是「没有地板」的合法拼法。 */
|
|
132
|
+
readonly floor: PermissionMode | undefined;
|
|
126
133
|
readonly principal: string | undefined;
|
|
127
134
|
/** 本地 `PERMISSIONS_DISABLE_AUTO_MODE`(= catalog/settings 键 `permissions.disableAutoMode`)是否置 true —— caps false 时分词
|
|
128
135
|
* `local_denied` / `org_denied`(seam ⑤;两者对壳的指路不同:改本机 env vs 找组织)。 */
|
package/dist/auto-mode-face.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { classifierSeatModel } from "@sema-agent/core";
|
|
1
|
+
import { clampPermissionMode, classifierSeatModel } from "@sema-agent/core";
|
|
2
2
|
import { resolveMainModel } from "./model-select.js";
|
|
3
3
|
export function classifierRouteFor(deps) {
|
|
4
4
|
try {
|
|
@@ -11,7 +11,7 @@ export function classifierRouteFor(deps) {
|
|
|
11
11
|
}
|
|
12
12
|
export const AUTO_MODE_UNARMED_REASONS = ["mode_not_auto", "deployment_incapable", "org_denied", "local_denied", "settings_denied", "resolver_fault"];
|
|
13
13
|
export const AUTO_MODE_UNARMED_REASON_SEMANTICS = {
|
|
14
|
-
mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\")",
|
|
14
|
+
mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\"),或意图经本部署的运维入口地板(AUTONOMY=plan)钳过之后不是 auto —— 与受理门同一只钳,提交时那个词到不了引擎",
|
|
15
15
|
deployment_incapable: "装配面没挂 RunnerDeps.autoMode 分类器席位(本部署/本进程形不具备分类器)",
|
|
16
16
|
org_denied: "per-principal caps.autoMode === false 且本地 deny 未设:center 显式 deny / center 硬失败 fail-closed 拒项",
|
|
17
17
|
local_denied: "本地 catalog 键 PERMISSIONS_DISABLE_AUTO_MODE=true(center/settings 键名 permissions.disableAutoMode)把 caps.autoMode 折成 false",
|
|
@@ -21,7 +21,9 @@ export const AUTO_MODE_UNARMED_REASON_SEMANTICS = {
|
|
|
21
21
|
export async function judgeAutoModeArming(face, input) {
|
|
22
22
|
const route = face?.seatMounted ? face.classifierModel?.() : undefined;
|
|
23
23
|
const model = route !== undefined ? { model: route } : {};
|
|
24
|
-
|
|
24
|
+
const intent = input.requestedMode ?? "auto";
|
|
25
|
+
const entry = input.floor === undefined ? intent : clampPermissionMode(intent, input.floor).effective;
|
|
26
|
+
if (entry !== "auto")
|
|
25
27
|
return { armed: false, reason: "mode_not_auto", ...model };
|
|
26
28
|
if (face === undefined || !face.seatMounted)
|
|
27
29
|
return { armed: false, reason: "deployment_incapable" };
|
|
@@ -370,6 +370,7 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
|
|
|
370
370
|
process.env.LEADER_WORKER_BUDGET_USD = String((rt.maxCostUsd ?? 1.0) / 2);
|
|
371
371
|
const run = createLeaderRunner({
|
|
372
372
|
brain, models, roles, pricing, e2bApiKey: rt.e2bApiKey, workspace: W,
|
|
373
|
+
autonomy: () => undefined,
|
|
373
374
|
...(s1DebugGit() ? { logger: { warn: (m, x) => console.error(`[S1_DEBUG_LEADER warn] ${m}`, x ? JSON.stringify(x).slice(0, 300) : ""), info: (m, x) => console.error(`[S1_DEBUG_LEADER info] ${m}`, x ? JSON.stringify(x).slice(0, 200) : "") } } : {}),
|
|
374
375
|
});
|
|
375
376
|
const teamSeedWrite = (trap.seedFiles ?? [])
|
|
@@ -14,6 +14,7 @@ import type { UnpricedCeilingVerdict } from "./unpriced-ceiling-verdict.js";
|
|
|
14
14
|
import type { ConsolidationSeatWatch } from "./memory-consolidation.js";
|
|
15
15
|
import type { DegradeRouteWatch } from "../brain.js";
|
|
16
16
|
import { type EngineRestartSignal } from "../config-center/facade.js";
|
|
17
|
+
import { type ConfigHandoff } from "../fleet/engine-signal.js";
|
|
17
18
|
import { type ConfigApplyLedgerView } from "../config-center/apply-ledger.js";
|
|
18
19
|
import type { CenterPullView } from "../config-center/pull-state.js";
|
|
19
20
|
import type { LimitSync } from "./limit-sync.js";
|
|
@@ -108,17 +109,14 @@ export interface ConfigCenterRuntime {
|
|
|
108
109
|
onSelfReportChange(listener: (changed: SelfReportChange) => void): void;
|
|
109
110
|
/** /health.restart 与 fleet 心跳 `restart` 的**同一只**取值口(orchestrator 自动重启信号;settings-schema `RestartSignal` 形)。 */
|
|
110
111
|
restartState(): EngineRestartSignal | undefined;
|
|
112
|
+
/** `/health.configHandoff` 的取值口(只上 `/health`):在场 ⇔ 部署声明了耐久接力(`CONFIG_LKG_DURABLE` ∧ LKG 启用 —— 判「handoff confirmed」的
|
|
113
|
+
* 同一只谓词)∧ 应用着的那一代 ≠ 最后确认耐久接力的那一代(接力账;合成律 `buildConfigHandoff`)。非耐久部署 / 本地腿 / 没有配置管道 ⇒ 缺席。
|
|
114
|
+
* 两个事实两张脸:「要重启但扣着」归 `restart.withheld`,「声明了耐久接力而这一代没确认」归本键。 */
|
|
115
|
+
configHandoffState(): ConfigHandoff | undefined;
|
|
111
116
|
/** S-768:`/health.modelRefusals` 与 fleet 心跳 `modelRefusals` 的**同一只**取值口(settings-schema 6.2.0 `ModelRefusalsSignal` 形:
|
|
112
117
|
* 名 → 码 + 组拒码)。描述**最近一次被判的那份中心候选目录**(每次候选 apply 的报告 `ApplyReport.modelRefusals` 投影;组拒时就是被拒
|
|
113
118
|
* 的那份 —— 引擎自己的拒因表不换代)。没有可报的拒 / 没有配置管道 / 还没判过候选 ⇒ `undefined`(键缺席)。值变了 ⇒ 通知自报订阅者。 */
|
|
114
119
|
modelRefusalsState(): ModelRefusalsSignal | undefined;
|
|
115
|
-
/** /health.modelPlaneDeferred:被扣着、还没有 next-boot handoff 的候选(codex R18 起的状态机)。S-668 波 0 起
|
|
116
|
-
* 模型面不再 defer,键名保留,今天承载的是 durable LKG 发布债与 skills 债(见 `planeDeferredNoHandoff`)。 */
|
|
117
|
-
planeDeferredState(): {
|
|
118
|
-
version: number;
|
|
119
|
-
since: number;
|
|
120
|
-
blocked?: string[];
|
|
121
|
-
} | undefined;
|
|
122
120
|
/** [ref] 世代账:/health 的 target/applied/失败持续时长三键 + 诊断端点的 configApply 段(同一份读数)。
|
|
123
121
|
* `undefined` = 本部署没有配置管道(纯 env worker)⇒ 两个读面的形状**逐字不变**(additive 纪律)。 */
|
|
124
122
|
configApplyState(): ConfigApplyLedgerView | undefined;
|
|
@@ -11,7 +11,7 @@ import { applyCenterPlugins } from "../capabilities/center-plugins.js";
|
|
|
11
11
|
import { applyCatalogToSource, withPromptArtifactBackfill } from "../capabilities/center-prompts.js";
|
|
12
12
|
import { validatePromptsDomain, CORE_ENGINE_VERSION } from "../prompts-domain-validate.js";
|
|
13
13
|
import { centerScenarios, centerScenarioDetails } from "../capabilities/scenarios.js";
|
|
14
|
-
import {
|
|
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";
|
|
@@ -20,6 +20,7 @@ import { createConfigProvider, raceBootFetch, serviceConfigSourceKind, BOOT_FETC
|
|
|
20
20
|
import { createKeyResolver } from "../key-resolver.js";
|
|
21
21
|
import { buildSealedKeyPublicFace, ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
22
22
|
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../config-center/facade.js";
|
|
23
|
+
import { buildConfigHandoff, buildRestartSignal, configHandoffMissing } from "../fleet/engine-signal.js";
|
|
23
24
|
import { createMcpRevocationLedger, enabledMcpEntries } from "../config-center/mcp-revocation.js";
|
|
24
25
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
25
26
|
import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../config-center/apply-ledger.js";
|
|
@@ -315,9 +316,27 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
315
316
|
}
|
|
316
317
|
};
|
|
317
318
|
let bootConfigPending;
|
|
318
|
-
const lkgEnabled = configProvider?.kind === "remote" && configLkgEnabled
|
|
319
|
+
const lkgEnabled = configProvider?.kind === "remote" && config.configLkgEnabled;
|
|
319
320
|
const lkgPathFor = (identity) => config.configLkgPath ?? defaultLkgPath(config, cc?.worker, identity);
|
|
320
321
|
const lkgSurvivesRestart = lkgEnabled && process.env.CONFIG_LKG_DURABLE === "true";
|
|
322
|
+
let handoff = {};
|
|
323
|
+
let handoffMissingSince;
|
|
324
|
+
const latchHandoff = () => {
|
|
325
|
+
const bootIdentity = currentIdentity();
|
|
326
|
+
const ledger = { ...handoff, ...(bootIdentity !== undefined ? { bootIdentity } : {}) };
|
|
327
|
+
handoffMissingSince = configHandoffMissing(ledger) ? (handoffMissingSince ?? Date.now()) : undefined;
|
|
328
|
+
return ledger;
|
|
329
|
+
};
|
|
330
|
+
const noteHandoff = (next) => {
|
|
331
|
+
const applied = next.applied ?? handoff.applied;
|
|
332
|
+
const durable = next.durable ?? handoff.durable;
|
|
333
|
+
handoff = { ...(applied !== undefined ? { applied } : {}), ...(durable !== undefined ? { durable } : {}) };
|
|
334
|
+
latchHandoff();
|
|
335
|
+
};
|
|
336
|
+
const markApplied = (gen, version) => {
|
|
337
|
+
applyLedger.applied(gen, version);
|
|
338
|
+
noteHandoff({ applied: version });
|
|
339
|
+
};
|
|
321
340
|
let lkgBooted = false;
|
|
322
341
|
let bootLkgCandidate;
|
|
323
342
|
let lastPrewarmBodiesVerified = false;
|
|
@@ -357,6 +376,8 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
357
376
|
}
|
|
358
377
|
lastPrewarmBodiesVerified = true;
|
|
359
378
|
await saveLkg(lkgPath, cc?.worker, identity, effectiveObj, etag);
|
|
379
|
+
if (lkgSurvivesRestart)
|
|
380
|
+
noteHandoff({ durable: { version: effectiveObj.version, identity } });
|
|
360
381
|
return lkgSurvivesRestart;
|
|
361
382
|
}
|
|
362
383
|
catch (err) {
|
|
@@ -417,7 +438,10 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
417
438
|
recordApply(rep);
|
|
418
439
|
},
|
|
419
440
|
});
|
|
420
|
-
|
|
441
|
+
const lkgIdentity = candidateIdentityOf(lkgR.effective);
|
|
442
|
+
if (lkgSurvivesRestart && lkgIdentity !== undefined)
|
|
443
|
+
noteHandoff({ durable: { version: lkgR.effective.version, identity: lkgIdentity } });
|
|
444
|
+
markApplied(lkgGen, lkgR.effective.version);
|
|
421
445
|
await adoptCenterPrompts(lkgR.effective, "boot-lkg");
|
|
422
446
|
const lkgPlane = modelPlaneCommitted(lkgReport);
|
|
423
447
|
if (lkgPlane)
|
|
@@ -485,7 +509,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
485
509
|
recordApply(rep);
|
|
486
510
|
},
|
|
487
511
|
});
|
|
488
|
-
|
|
512
|
+
markApplied(bootGen, r.effective.version);
|
|
489
513
|
const bootClean = fullyLandedForLkg(r, bootReport);
|
|
490
514
|
if (bootClean)
|
|
491
515
|
ccEtag = r.etag;
|
|
@@ -604,9 +628,9 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
604
628
|
onSelfReportChange: (listener) => {
|
|
605
629
|
selfReportListeners.push(listener);
|
|
606
630
|
},
|
|
607
|
-
restartState: () => pendingRestart,
|
|
631
|
+
restartState: () => buildRestartSignal(pendingRestart, planeDeferredNoHandoff),
|
|
632
|
+
configHandoffState: () => (configProvider === undefined || configProvider.kind === "local" || !lkgSurvivesRestart ? undefined : buildConfigHandoff(latchHandoff(), handoffMissingSince)),
|
|
608
633
|
modelRefusalsState: () => modelRefusals,
|
|
609
|
-
planeDeferredState: () => planeDeferredNoHandoff,
|
|
610
634
|
configApplyState: () => (configProvider ? applyLedger.view() : undefined),
|
|
611
635
|
centerPullState: () => {
|
|
612
636
|
const view = applyLedger.view();
|
|
@@ -622,7 +646,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
622
646
|
},
|
|
623
647
|
async applyCenterCapabilities(skills) {
|
|
624
648
|
if (effective?.skills && config.configCenter) {
|
|
625
|
-
skills = await applyCenterSkills(skills, effective.skills, config.configCenter, logger, undefined, configLkgEnabled
|
|
649
|
+
skills = await applyCenterSkills(skills, effective.skills, config.configCenter, logger, undefined, config.configLkgEnabled ? defaultSkillCacheDir(config) : undefined);
|
|
626
650
|
}
|
|
627
651
|
if (effective?.plugins !== undefined) {
|
|
628
652
|
const pluginOut = await applyCenterPlugins(skills, effective, {
|
|
@@ -850,7 +874,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
850
874
|
ceilingVerdict?.judge();
|
|
851
875
|
mcpRevocations.applyEnabledEntries(enabledMcpEntries(r.effective.mcp));
|
|
852
876
|
applyLiveReadFace(r.effective, swapRunnerDeps, "refresh");
|
|
853
|
-
|
|
877
|
+
markApplied(tickGen, r.effective.version);
|
|
854
878
|
const tickPlane = modelPlaneCommitted(applyReport);
|
|
855
879
|
if (tickPlane)
|
|
856
880
|
appliedPlaneEff = r.effective;
|
|
@@ -963,7 +987,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
963
987
|
limitSync?.sync();
|
|
964
988
|
ceilingVerdict?.judge();
|
|
965
989
|
applyLiveReadFace(r.effective, swapRunnerDeps, "boot-deferred");
|
|
966
|
-
|
|
990
|
+
markApplied(lateGen, r.effective.version);
|
|
967
991
|
const latePlane = modelPlaneCommitted(applyReportLate);
|
|
968
992
|
if (latePlane)
|
|
969
993
|
appliedPlaneEff = r.effective;
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* 的请求是毫秒级的,而本腿 15s 采样一拍 —— 采样点几乎必然落在两次轮询之间,`attached` 与
|
|
37
37
|
* `inflight` **同时读到 0**。只看那两条的实现会在一只**正被使用**的引擎底下把它关掉。租约的
|
|
38
38
|
* 建立面按设计就是「首个 HTTP/SSE 接触」,不只是 SSE。
|
|
39
|
-
* 4. **退出 = 走 `drain`**,即 `boot/shutdown.ts` 里 SIGTERM
|
|
39
|
+
* 4. **退出 = 走 `drain`**,即 `boot/shutdown.ts` 里 SIGTERM 用的那一只唯一排空入口 `enterDrain`(编排器片 2 前名 `drainThenShutdown`):拒新提交
|
|
40
40
|
* (503+Retry-After)→ 候在途腿跑完 → `hardShutdown`。in-flight run 的结算 / park 语义与人工停机
|
|
41
41
|
* **逐字一致**。本模块自己**从不** `process.exit`。
|
|
42
42
|
* 5. **crash-last 判别位**([ref] ④「flush 判别位标 auto-exit 非崩溃」):走上面那条受控退,
|
package/dist/boot/leader.js
CHANGED
|
@@ -51,6 +51,7 @@ export function createLeaderFace(ctx) {
|
|
|
51
51
|
retentionPolicy: governanceSeams.retentionPolicy,
|
|
52
52
|
},
|
|
53
53
|
deploymentPosture: () => buildDeploymentPostureSeats(config),
|
|
54
|
+
autonomy: () => config.autonomy,
|
|
54
55
|
...(ctx.tracer ? { tracer: ctx.tracer } : {}),
|
|
55
56
|
...(config.usageWindows && ctx.usageWindowStore
|
|
56
57
|
? { usageWindows: config.usageWindows, usageWindowStore: ctx.usageWindowStore }
|
package/dist/boot/reapers.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ import type { TaskAttachmentStore } from "../plugins/task-attachment-store.js";
|
|
|
24
24
|
import type { FileHistoryErasureFace } from "../file-history-capture.js";
|
|
25
25
|
import type { ApprovalNonceStore, CheckpointStoreFull, CostQuotaStore, ImageBake, RateLimiterStore, ServiceWorkflowJournalStore, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
|
|
26
26
|
import type { ToolApprovalCoordinator } from "../tool-approval.js";
|
|
27
|
+
import type { ParkCommitWatch } from "../plugins/park-commit-watch.js";
|
|
27
28
|
/** [ref] / [ref]§五(sema-comms `audits/failopen-governance-176.md` §7 "Z 类 1 条"):default consecutive-failure
|
|
28
29
|
* count before a reaper leg's swallowed error gets a `warn` line. The ~15 `.catch(() => undefined)` sites
|
|
29
30
|
* below are correct in DIRECTION (a hiccuping periodic sweep must never crash the whole tick loop — one bad
|
|
@@ -68,6 +69,8 @@ export interface ReapersCtx {
|
|
|
68
69
|
subRunner: Runner;
|
|
69
70
|
runStore: ReturnType<StoreBackend["run"]> | undefined;
|
|
70
71
|
checkpointStore: CheckpointStoreFull | undefined;
|
|
72
|
+
/** S-1280:checkpoint 店的 park 提交观察层(stage-06 与店同一处铸;在场 ⇔ 有 checkpoint 店)。对账收敛器订阅它,在停车行提交那一拍收口 PARKING。 */
|
|
73
|
+
parkCommits: ParkCommitWatch | undefined;
|
|
71
74
|
rateLimiter: RateLimiter | RateLimiterStore | undefined;
|
|
72
75
|
costQuota: CostQuota | CostQuotaStore | undefined;
|
|
73
76
|
toolResultStore: ToolResultStoreFull | undefined;
|
package/dist/boot/reapers.js
CHANGED
|
@@ -56,7 +56,7 @@ export function createPlacementAwareReapSessions(deps) {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
export function startReapers(ctx) {
|
|
59
|
-
const { config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore, rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap, workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore, backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, getRunDenySweep, userInputLedger, } = ctx;
|
|
59
|
+
const { config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore, parkCommits, rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap, workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore, backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, getRunDenySweep, userInputLedger, } = ctx;
|
|
60
60
|
const leg = (p) => {
|
|
61
61
|
if (p !== undefined)
|
|
62
62
|
trackMaintenanceLeg(p);
|
|
@@ -122,6 +122,7 @@ export function startReapers(ctx) {
|
|
|
122
122
|
orphanTtlMs: config.streamApproval.orphanTtlMs,
|
|
123
123
|
unattendedPolicy: config.unattendedApprovalPolicy,
|
|
124
124
|
...(toolApproval ? { emitRevoke: (frame, target) => void toolApproval.emitRevoke(frame, target) } : {}),
|
|
125
|
+
parkCommits,
|
|
125
126
|
});
|
|
126
127
|
})();
|
|
127
128
|
const reapSessions = createPlacementAwareReapSessions({ host: backend?.session(), routed: subRunner.sessions });
|
|
@@ -26,7 +26,7 @@ import { resourceSuspendOptIn } from "../resource-suspend.js";
|
|
|
26
26
|
import { isIsolatedExecEnv, routeServiceTask, supPostureOverrides } from "../router/route-orchestration.js";
|
|
27
27
|
import { tightenGoverned } from "../governance-overrides.js";
|
|
28
28
|
import { gateExecutionLane } from "../runtime-caps-resolver.js";
|
|
29
|
-
import { applyRuntimeGovernance
|
|
29
|
+
import { applyRuntimeGovernance } from "../runtime-governance.js";
|
|
30
30
|
import { HttpError, createCodedHttpError, encodeCheckpointScope } from "../security.js";
|
|
31
31
|
import { memoryProjectKeyFor, memorySpecForRequest } from "../memory-scope.js";
|
|
32
32
|
import { inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
@@ -331,6 +331,7 @@ export function createResolveSpec(ctx) {
|
|
|
331
331
|
objective: attachmentNotice ? `${picked.cleanedObjective}\n\n${attachmentNotice}` : picked.cleanedObjective,
|
|
332
332
|
systemPrompt: typeof body.systemPrompt === "string" ? body.systemPrompt : undefined,
|
|
333
333
|
appendSystemPrompt: acceptedAppend,
|
|
334
|
+
...(body.outputStyle !== undefined ? { outputStyle: body.outputStyle } : {}),
|
|
334
335
|
sessionId: auth?.sessionId,
|
|
335
336
|
principal: auth?.principal,
|
|
336
337
|
images: body.images,
|
|
@@ -598,9 +599,6 @@ export function createResolveSpec(ctx) {
|
|
|
598
599
|
throw new HttpError(422, `request settings cannot be applied as written: ${e.message}`);
|
|
599
600
|
}
|
|
600
601
|
}
|
|
601
|
-
if (governed.handsReadOnly === true && Array.isArray(governed.tools)) {
|
|
602
|
-
governed = { ...governed, tools: stripDelegationTools(governed.tools) };
|
|
603
|
-
}
|
|
604
602
|
if (shouldDiscloseNoPersistentMemory({
|
|
605
603
|
memoryFaceMounted: memoryFaceMounted(governed.memory),
|
|
606
604
|
declaredCapable: effectiveMemoryPersistenceCapable(config),
|
package/dist/boot/shutdown.d.ts
CHANGED
|
@@ -26,11 +26,12 @@ import type { Logger } from "../observability/logger.js";
|
|
|
26
26
|
import type { startOtlpExporter } from "../observability/otel-exporter.js";
|
|
27
27
|
import type { CostQuota } from "../observability/cost-quota.js";
|
|
28
28
|
import type { RateLimiter } from "../observability/rate-limit.js";
|
|
29
|
-
import type {
|
|
29
|
+
import type { startFleetClientFromConfig } from "../fleet-client.js";
|
|
30
30
|
import type { BreakerStateStore, CostQuotaStore, RateLimiterStore, StoreBackend } from "../plugins/store-backend.js";
|
|
31
31
|
import type { WorkflowNotifyJournalStore } from "../orchestration/workflow-notify-journal.js";
|
|
32
32
|
import type { ToolApprovalCoordinator } from "../tool-approval.js";
|
|
33
33
|
import type { ParkCommitWatch } from "../plugins/park-commit-watch.js";
|
|
34
|
+
import { type DrainState } from "../fleet/drain-entry.js";
|
|
34
35
|
export interface ShutdownCtx {
|
|
35
36
|
config: ServiceConfig;
|
|
36
37
|
/** [ref]①:崩溃遗言的 version 键(与 /v1/capabilities.version 同源=main 的 serviceVersion())。 */
|
|
@@ -61,16 +62,10 @@ export interface ShutdownCtx {
|
|
|
61
62
|
subRunner: Runner;
|
|
62
63
|
lspManager: RunnerDeps["lspManager"];
|
|
63
64
|
workflowNotifyJournal: WorkflowNotifyJournalStore | undefined;
|
|
64
|
-
fleetClient: ReturnType<typeof
|
|
65
|
+
fleetClient: ReturnType<typeof startFleetClientFromConfig>;
|
|
65
66
|
backend: StoreBackend | undefined;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
since?: number;
|
|
69
|
-
inflight?: () => number;
|
|
70
|
-
lastActivityAt?: () => number;
|
|
71
|
-
attachedStreams?: () => number;
|
|
72
|
-
lastAttachEndedAt?: () => number;
|
|
73
|
-
};
|
|
67
|
+
/** 编排器片 2 件 2:共享排空盒子(`fleet/drain-entry.ts`)—— 本函数往上装**唯一**入口 `enter`。 */
|
|
68
|
+
drainState: DrainState;
|
|
74
69
|
/** [ref]-2:store 活体探针(main 建)——不停的话 hardShutdown 后仍对正在关闭的池发探针,刷假
|
|
75
70
|
* store_probe_dead 告警(文件头契约 3 的同族漏网)。缺席 = 该部署形没建探针。 */
|
|
76
71
|
storeLiveProbe: {
|
|
@@ -95,6 +90,10 @@ export interface ShutdownCtx {
|
|
|
95
90
|
/** S-929 F1:checkpoint 店的 park 提交观察层(`plugins/park-commit-watch.ts`;在场 ⇔ 本部署有 checkpoint 店)。hardShutdown 在
|
|
96
91
|
* `backend.close()` 之前等它 quiesce —— 在飞的 `put`(审批 park / 资源挂起…,与审批协调器在不在场无关)不许被抽掉连接池。 */
|
|
97
92
|
parkCommits: ParkCommitWatch | undefined;
|
|
93
|
+
/** 编排器片 2 件 3:服务租约客户端(在场 ⇔ fleet 成员 ∧ 中心车道配齐)。hardShutdown 起点停它的续租定时器(文件头契约 3 同族)。 */
|
|
94
|
+
servingLease?: {
|
|
95
|
+
stop(): void;
|
|
96
|
+
} | undefined;
|
|
98
97
|
}
|
|
99
98
|
/**
|
|
100
99
|
* 受控退的**后台宽限**还要等多久(ms)—— hardShutdown 软停后台 shell 之后、`process.exit` 之前,给软停过的行**一次**
|