@sema-agent/server 7.128.0 → 7.129.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 +118 -0
- package/USAGE.md +89 -18
- package/dist/approval-card.d.ts +53 -26
- package/dist/approval-card.js +40 -11
- package/dist/approval-crash-convergence.js +4 -4
- package/dist/bake-runner/main.js +2 -1
- package/dist/boot/memory-consolidation.d.ts +46 -16
- package/dist/boot/memory-consolidation.js +15 -8
- package/dist/boot/side-query-lane.d.ts +7 -20
- package/dist/boot/side-query-lane.js +0 -24
- package/dist/boot/stage-01-config.js +3 -0
- package/dist/boot/stage-06-runners.js +1 -1
- package/dist/boot/stores.js +2 -2
- package/dist/boot/web-admin.js +17 -10
- package/dist/boot/webfetch-summarize-lane.d.ts +10 -7
- package/dist/boot/webfetch-summarize-lane.js +2 -3
- package/dist/brain.js +6 -9
- package/dist/capabilities/builtin-tools.d.ts +2 -2
- package/dist/capabilities/engine-capabilities.d.ts +14 -0
- package/dist/capabilities/engine-capabilities.js +12 -0
- package/dist/capability-facts.d.ts +3 -1
- package/dist/capability-facts.js +1 -0
- package/dist/config-types.d.ts +4 -3
- package/dist/egress-redirect.d.ts +12 -0
- package/dist/egress-redirect.js +7 -0
- package/dist/entry-mode-clamp-notice.d.ts +30 -0
- package/dist/entry-mode-clamp-notice.js +13 -0
- package/dist/fleet/fleet-bus.d.ts +7 -0
- package/dist/fleet/fleet-bus.js +1 -0
- package/dist/hooks/hook-llm.js +6 -2
- package/dist/host-lsp-manager.d.ts +3 -3
- package/dist/http/admission.js +5 -2
- package/dist/http/resume-legs.js +46 -27
- package/dist/http/route-ctx.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +6 -4
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/session-sync.js +3 -4
- package/dist/http/routes/sessions.js +4 -17
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +4 -3
- package/dist/http/wire-types.d.ts +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/observability/fail-open.d.ts +7 -3
- package/dist/observability/fail-open.js +7 -3
- package/dist/observability/secret-env-scrub.d.ts +37 -3
- package/dist/observability/secret-env-scrub.js +2 -2
- package/dist/parked-decide.js +5 -2
- package/dist/plugins/memory-embedder.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +180 -9
- package/dist/plugins/one-time-migrations.js +315 -3
- package/dist/plugins/pg-pool.js +3 -1
- package/dist/plugins/remote-env-host.d.ts +17 -20
- package/dist/plugins/remote-env-host.js +13 -25
- package/dist/plugins/session-policy-store-sql.d.ts +16 -4
- package/dist/plugins/session-policy-store-sql.js +17 -20
- package/dist/plugins/session-rules-column.d.ts +16 -0
- package/dist/plugins/session-rules-column.js +6 -0
- package/dist/plugins/tidb-pool.js +4 -1
- package/dist/read-face-posture.d.ts +2 -1
- package/dist/request-value-closure.js +1 -1
- package/dist/retired-card-words.d.ts +17 -0
- package/dist/retired-card-words.js +49 -0
- package/dist/rule-entry-list.js +2 -0
- package/dist/run-local.js +10 -3
- package/dist/server-secret-env.d.ts +77 -85
- package/dist/server-secret-env.js +40 -26
- package/dist/session-policy-wire.d.ts +61 -124
- package/dist/session-policy-wire.js +59 -121
- package/dist/session-sync.d.ts +8 -6
- package/dist/session-sync.js +6 -5
- package/dist/spec-fields.d.ts +3 -2
- package/dist/standing-answer-grant.d.ts +17 -9
- package/dist/standing-answer-grant.js +11 -8
- package/dist/task-settings.d.ts +6 -1
- package/dist/task-settings.js +1 -1
- package/dist/tool-approval.d.ts +14 -7
- package/dist/tool-approval.js +23 -18
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/project.d.ts +7 -4
- package/dist/trace/project.js +6 -75
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/trace/tool-output-media.js +3 -25
- package/dist/web-admin-client/apply-effective.js +28 -11
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/read-face.d.ts +73 -20
- package/dist/web-admin-client/read-face.js +76 -36
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +1 -1
- package/dist/web-admin-client/types.d.ts +3 -3
- package/package.json +3 -3
- package/dist/mcp-tool-axes.d.ts +0 -62
- package/dist/mcp-tool-axes.js +0 -47
package/dist/approval-card.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, STEER_REPLAY_FACETS, isAskClass, isAskOrigin, isAutoModeFallbackCause, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
2
|
+
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, BOUND_GRANT_ABSENCES, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, RESUME_STARTUP_CAUSES, RULE_OFFERS_ABSENCES, STEER_REPLAY_FACETS, isAskClass, isAskOrigin, isAutoModeFallbackCause, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
3
3
|
import { redactSecrets } from "./trace/redact.js";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
|
+
import { retireParkArmWords, retireStoredCardWords } from "./retired-card-words.js";
|
|
5
6
|
import { PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS } from "./permission-rule-vocab.js";
|
|
6
7
|
export const MAX_AGENT_NAME = 200;
|
|
7
8
|
const MAX_IDENT = 200;
|
|
@@ -267,8 +268,7 @@ export function readRuleEvidence(req) {
|
|
|
267
268
|
...(raw.personalRuleDotsAbsent !== undefined ? { personalRuleDotsAbsent: raw.personalRuleDotsAbsent } : {}),
|
|
268
269
|
};
|
|
269
270
|
}
|
|
270
|
-
const
|
|
271
|
-
export const RuleOffersAbsenceSchema = z.enum(RULE_OFFERS_ABSENCE_VALUES);
|
|
271
|
+
export const RuleOffersAbsenceSchema = z.enum(RULE_OFFERS_ABSENCES);
|
|
272
272
|
const RuleOffersAbsenceEnvelopeSchema = z.object({ ruleOffersAbsence: RuleOffersAbsenceSchema.optional() });
|
|
273
273
|
export function readRuleOffersAbsenceSeat(x) {
|
|
274
274
|
const parsed = RuleOffersAbsenceEnvelopeSchema.safeParse(x);
|
|
@@ -318,11 +318,21 @@ function isSteerReplayDiffers(v) {
|
|
|
318
318
|
const facets = STEER_REPLAY_FACETS;
|
|
319
319
|
return Array.isArray(v) && v.length > 0 && v.every((f) => facets.includes(f));
|
|
320
320
|
}
|
|
321
|
+
function isResumeStartupCause(v) {
|
|
322
|
+
const words = RESUME_STARTUP_CAUSES;
|
|
323
|
+
return words.includes(v);
|
|
324
|
+
}
|
|
325
|
+
function isBoundGrantAbsence(v) {
|
|
326
|
+
const words = BOUND_GRANT_ABSENCES;
|
|
327
|
+
return words.includes(v);
|
|
328
|
+
}
|
|
321
329
|
const CLOSED_WORD_SEAT_GUARDS = {
|
|
322
330
|
mandate: isPersistedRuleMandate,
|
|
323
331
|
origin: isAskOrigin,
|
|
324
332
|
classifierNoRuling: isAutoModeFallbackCause,
|
|
333
|
+
boundGrantAbsence: isBoundGrantAbsence,
|
|
325
334
|
differs: isSteerReplayDiffers,
|
|
335
|
+
cause: isResumeStartupCause,
|
|
326
336
|
};
|
|
327
337
|
export function readAskClassPair(carrier) {
|
|
328
338
|
const askClass = "askClass" in carrier ? carrier.askClass : undefined;
|
|
@@ -335,13 +345,15 @@ export function readAskClassPair(carrier) {
|
|
|
335
345
|
recordFailOpen("server.approval-card.closed-word-out-of-set", isAskClass(askClass) ? `classes=${outOfSetDetail(classes)}` : `askClass=${outOfSetDetail(askClass)}`);
|
|
336
346
|
return undefined;
|
|
337
347
|
}
|
|
338
|
-
export function parkRowFacts(
|
|
339
|
-
if (typeof
|
|
348
|
+
export function parkRowFacts(stored) {
|
|
349
|
+
if (typeof stored !== "object" || stored === null || !("kind" in stored) || stored.kind !== "tool_approval")
|
|
340
350
|
return {};
|
|
351
|
+
const pendingAction = currentParkArm(stored);
|
|
341
352
|
const ruleOffersAbsence = readRuleOffersAbsence(pendingAction);
|
|
342
353
|
const mandate = readClosedWordSeat(pendingAction, "mandate");
|
|
343
354
|
const origin = readClosedWordSeat(pendingAction, "origin");
|
|
344
355
|
const askClassPair = readAskClassPair(pendingAction);
|
|
356
|
+
const boundGrantAbsence = readClosedWordSeat(pendingAction, "boundGrantAbsence");
|
|
345
357
|
return {
|
|
346
358
|
...(readParkRowTrueSeat(pendingAction, "requiresRealApproval") ? { requiresRealApproval: true } : {}),
|
|
347
359
|
...(readParkRowTrueSeat(pendingAction, "mandated") ? { mandated: true } : {}),
|
|
@@ -349,6 +361,7 @@ export function parkRowFacts(pendingAction) {
|
|
|
349
361
|
...(origin !== undefined ? { origin } : {}),
|
|
350
362
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
351
363
|
...(askClassPair ?? {}),
|
|
364
|
+
...(boundGrantAbsence !== undefined ? { boundGrantAbsence } : {}),
|
|
352
365
|
};
|
|
353
366
|
}
|
|
354
367
|
const DenialLimitFallbackSchema = z
|
|
@@ -492,6 +505,22 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
492
505
|
export function buildApprovalCardEnvelope(approvalId, card) {
|
|
493
506
|
return { schemaVersion: APPROVAL_CARD_SCHEMA_VERSION, approvalId, card };
|
|
494
507
|
}
|
|
508
|
+
export function readStoredCardEnvelope(cardJson) {
|
|
509
|
+
const stored = retireStoredCardWords(cardJson);
|
|
510
|
+
if (stored.seats.length > 0)
|
|
511
|
+
noteRetiredCardWordRead("card_json", stored.seats);
|
|
512
|
+
const parsed = ApprovalCardEnvelopeSchema.safeParse(stored.value);
|
|
513
|
+
return parsed.success ? parsed.data : undefined;
|
|
514
|
+
}
|
|
515
|
+
export function currentParkArm(pendingAction) {
|
|
516
|
+
const arm = retireParkArmWords(pendingAction);
|
|
517
|
+
if (arm.seats.length > 0)
|
|
518
|
+
noteRetiredCardWordRead("parked row", arm.seats);
|
|
519
|
+
return arm.value;
|
|
520
|
+
}
|
|
521
|
+
function noteRetiredCardWordRead(carrier, seats) {
|
|
522
|
+
recordFailOpen("server.approval-card.retired-card-word-read", `${carrier}: ${seats.join(" / ")}`);
|
|
523
|
+
}
|
|
495
524
|
export function buildApprovalRequestFrame(input) {
|
|
496
525
|
return {
|
|
497
526
|
type: "approval_request",
|
|
@@ -510,8 +539,8 @@ export function buildRevokeFrame(batchId, askIds, reason, serverNowMs, originTas
|
|
|
510
539
|
return { type: "approval_revoke", schemaVersion: 1, batchId, askIds: [...askIds], reason, serverNowMs, taskId: originTaskId };
|
|
511
540
|
}
|
|
512
541
|
export function buildReplayFrame(row, nowMs) {
|
|
513
|
-
const parsed =
|
|
514
|
-
if (
|
|
542
|
+
const parsed = readStoredCardEnvelope(row.cardJson);
|
|
543
|
+
if (parsed === undefined)
|
|
515
544
|
return undefined;
|
|
516
545
|
return {
|
|
517
546
|
type: "approval_request",
|
|
@@ -519,16 +548,16 @@ export function buildReplayFrame(row, nowMs) {
|
|
|
519
548
|
askId: row.askId,
|
|
520
549
|
taskId: row.taskId,
|
|
521
550
|
kind: "permission",
|
|
522
|
-
card: parsed.
|
|
551
|
+
card: parsed.card,
|
|
523
552
|
expiresAtMs: row.expiresAtMs,
|
|
524
553
|
expiresInMs: Math.max(0, row.expiresAtMs - nowMs),
|
|
525
554
|
serverNowMs: nowMs,
|
|
526
|
-
approvalId: parsed.
|
|
555
|
+
approvalId: parsed.approvalId,
|
|
527
556
|
};
|
|
528
557
|
}
|
|
529
558
|
export function storedCardDemandsAPerson(cardJson) {
|
|
530
|
-
const parsed =
|
|
531
|
-
return
|
|
559
|
+
const parsed = readStoredCardEnvelope(cardJson);
|
|
560
|
+
return parsed === undefined || parsed.card.risk.requiresRealApproval;
|
|
532
561
|
}
|
|
533
562
|
export function buildApprovalPreamble(rows, nowMs, replayMax) {
|
|
534
563
|
const frames = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { APPROVAL_ASK_AUDIT_DIR, APPROVAL_ASK_AUDIT_LEDGER, FileApprovalAskAuditStore } from "./approval-ask-audit-store.js";
|
|
4
|
-
import {
|
|
4
|
+
import { readStoredCardEnvelope } from "./approval-card.js";
|
|
5
5
|
import { APPROVAL_ENDING_CAUSES } from "./approval-deny-reasons.js";
|
|
6
6
|
import { writerGoneEvidenceFor } from "./user-input-ledger.js";
|
|
7
7
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
@@ -31,12 +31,12 @@ export async function convergeCrashedAsksAtBoot(input) {
|
|
|
31
31
|
break;
|
|
32
32
|
for (const row of page) {
|
|
33
33
|
seen.add(row.askId);
|
|
34
|
-
const envelope =
|
|
35
|
-
if (
|
|
34
|
+
const envelope = readStoredCardEnvelope(row.cardJson);
|
|
35
|
+
if (envelope === undefined) {
|
|
36
36
|
causeUnreadable += 1;
|
|
37
37
|
recordFailOpen("server.approval-crash-convergence.cause-unreadable", `ask ${row.askId} (task ${row.taskId}): card envelope unreadable — cause defaulted to ${APPROVAL_ENDING_CAUSES.CRASHED_BEFORE_PARK}`);
|
|
38
38
|
}
|
|
39
|
-
const reason = envelope
|
|
39
|
+
const reason = envelope !== undefined && input.audit !== undefined ? input.audit.causeFor(envelope.approvalId) : APPROVAL_ENDING_CAUSES.CRASHED_BEFORE_PARK;
|
|
40
40
|
let won;
|
|
41
41
|
try {
|
|
42
42
|
won = await store.transitionAsk(row.askId, "STREAM_PENDING", "VOID", { deniedReason: reason, updatedAtMs: now() });
|
package/dist/bake-runner/main.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { createLogger } from "../observability/logger.js";
|
|
4
4
|
import { logConfigDiagnostics, parseEnvNameAllowlist, parseNumOrFailNonNegative } from "../config.js";
|
|
5
5
|
import { installSecretEnvScrubRecorder } from "../observability/secret-env-scrub.js";
|
|
6
|
-
import { hostSubprocessEnv } from "../server-secret-env.js";
|
|
6
|
+
import { declareServerWithheldEnv, hostSubprocessEnv } from "../server-secret-env.js";
|
|
7
7
|
import { shellQuote as shellSafe } from "../plugins/remote-shell.js";
|
|
8
8
|
import { BakeRunner, } from "./runner.js";
|
|
9
9
|
const BAKE_CLAIM_INGEST_FETCH_TIMEOUT_MS = 30_000;
|
|
@@ -256,6 +256,7 @@ function makeClock() {
|
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
258
|
async function main() {
|
|
259
|
+
declareServerWithheldEnv();
|
|
259
260
|
const env = loadEnv();
|
|
260
261
|
const log = createLogger(process.env.LOG_LEVEL || "info");
|
|
261
262
|
logConfigDiagnostics(log);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type LaneHomeDeployment } from "../execution-lane-caps.js";
|
|
1
2
|
import type { RestartSliceCtx } from "../web-admin-client/restart-signal.js";
|
|
2
3
|
import type { MemoryConsolidationDriverConfig, ServiceConfig } from "../config-types.js";
|
|
3
|
-
import { resolveMemoryConsolidationDriver, type ConsolidationWriteJudge, type EngineNotice, type ConsolidationDriverRunRow, type ConsolidationRunReceipt, type MemoryBackend, type MemoryConsolidationDriverDeps, type SessionCaptureRecordStore } from "@sema-agent/core";
|
|
4
|
+
import { resolveMemoryConsolidationDriver, type ConsolidationWriteJudge, type ConsolidationWriteJudgeInput, type EngineNotice, type ConsolidationDriverRunRow, type ConsolidationRunReceipt, type MemoryBackend, type MemoryConsolidationDriverDeps, type PermissionRuleStoreProvider, type SessionCaptureRecordStore } from "@sema-agent/core";
|
|
4
5
|
/**
|
|
5
6
|
* 座解析的 options 形 = core 解析器交回的那只(core 7.50.0 起它**不含** `writeJudge`:判官不是模型席的一部分,由调用方在动词上带)。
|
|
6
7
|
* 取解析器的返回型而不是自己写 `Omit<…>`:形的属主是 core 的解析器,本仓不持第二份。
|
|
@@ -396,26 +397,55 @@ export declare function assertAutoConsolidationWirable(input: {
|
|
|
396
397
|
memoryConsolidationWired: boolean;
|
|
397
398
|
}): void;
|
|
398
399
|
/**
|
|
399
|
-
*
|
|
400
|
+
* 运维阀门腿的写判官座的**输入**(core 的判官构造器 `createConsolidationWriteJudge` 的 `input`)。返回值带一只**活的**规则店 provider(有行为,不是纯数据)⇒ `create*`(工厂命名律:`build*` 只产纯数据;不拆店出返回值,因为输入就是引擎构造器要的那一只形,多拆一个函数只会多一个规则)。
|
|
400
401
|
*
|
|
401
|
-
* ##
|
|
402
|
+
* ## 构造器只有引擎那一只,本仓只供部署事实
|
|
402
403
|
*
|
|
403
|
-
* 「这条路径能不能写」在引擎里只有**一只**判官(合成写判官:技能拒行 / 收窄折叠 / 调用方与继承策略 / 人的持久规则车道 /
|
|
404
|
-
*
|
|
405
|
-
* `
|
|
406
|
-
* 「对部署策略问一次合成 `Write`」的判官 = 把引擎判官的层走法、脸、豁免根、已中止信号的约定与判词读法**再写一份**
|
|
407
|
-
* (第二个读者,与引擎那只迟早分歧,而且分歧是静默的)。所以本腿不造第二只。
|
|
404
|
+
* 「这条路径能不能写」在引擎里只有**一只**判官(合成写判官:技能拒行 / 收窄折叠 / 调用方与继承策略 / 人的持久规则车道 / 组织车道,一条走法一个优先序)。
|
|
405
|
+
* 引擎的自动整理腿(有腿:名册上的 `Write` 脸、引擎盖的豁免根、人的持久规则读者)与本腿(运维手动阀门:没有腿)用的是**同一个构造器** ——
|
|
406
|
+
* 同样的行给同样的答案,本仓不手搭第二只({@link createOperatorValveWriteJudge} 恰一处调用它,见 `test/memory-consolidation-write-judge.test.ts` ⑥)。
|
|
408
407
|
*
|
|
409
|
-
* ##
|
|
408
|
+
* ## 本仓供的两个部署事实(其余键一律缺席,缺席的语义见引擎:那一层不存在)
|
|
409
|
+
*
|
|
410
|
+
* · `ruleStoreProvider` = 部署唯一的那只合成规则店(`RunnerDeps.permissionRuleStore` 的同一个变量,装配第 4 段铸一次)。规则店没装配 ⇒ 缺席 ⇒ 没有规则层。
|
|
411
|
+
* · `anchors.home` = `executionLaneHomeDir(config)` —— `~/` 行的基,与引擎执行环境的 `homeDir` 座、下单 422 门、规则基 `taskHome` 是**同一只函数**
|
|
412
|
+
* (车道没有自己的 home 时诚实缺席;缺席 ⇒ 引擎读到人持有的 `~/` 收紧行时无处锚定 = 按异议处置,拒闭)。**刻意不另调 `os.homedir()`**:
|
|
413
|
+
* 那会在非 host 车道上造出第二个读法 —— 阀门答「可解析」而腿内答「读不出」。⚠️ 今天这一键在生产座上**空转**:阀门不带主体(下面的 `principal`),
|
|
414
|
+
* 引擎读匿名地板、不读任何人的持久行,「`~/` 行无锚 ⇒ 拒闭」走不到;留着它是为了主体有了来源之后不必再回来改这里。
|
|
415
|
+
*
|
|
416
|
+
* ## 刻意缺席的键(披露,不是遗漏)—— 三层没喂,所以这条腿仍是**响亮的放行座**(P-DEBT,见 {@link createOperatorValveWriteJudge})
|
|
417
|
+
*
|
|
418
|
+
* · `principal` / `localOwnerDeclared`(人的持久规则层):阀门是 operator 面的一次调用,本仓没有一个能回答「这个库属于谁」的来源 ——
|
|
419
|
+
* 单用户 + file 后端(阀门结构上能起的唯一形,见 {@link assertConsolidationDriverWirable})里记忆是部署级全局层 / 任务根散列的项目层,principal 标签从不分盘,
|
|
420
|
+
* 本仓也从不声明 `localOwnerRules`;整理 run 又是按 scope 单飞、可续跑的持久作业,「谁的规则」对并入 / 续跑的调用方没有定义。
|
|
421
|
+
* · `callerPolicy`(部署的调用方策略层):`APPROVAL_REQUIRE` / `APPROVAL_DENY` / `AUTONOMY` / 敏感路径守卫集是**每任务现合成**的(`deployment-governance.ts`,
|
|
422
|
+
* 读热可改的活配置,「禁 memoize」),没有部署级单例;boot 期铸一份喂进来就是冻结一份会过期的策略(收紧不生效 = 静默放行)。喂它需要一份设计:取哪几层、
|
|
423
|
+
* 活配置怎么读、审批席取什么形;其中敏感路径层还要引擎的豁免根(`RunnerDeps.memoryEngineDir` 盖在每个请求上),而宿主构造器的输入没有这一位 —— 要向 core 请托,
|
|
424
|
+
* 不在本层造第二条豁免根的读法。
|
|
425
|
+
* · `anchors.root`:阀门按 scope 跑,手里没有项目根 —— 项目层 scope 键是任务根的单向散列(`project-identity.ts` 的 `repo:<sha256[:24]>`),反推不出根;
|
|
426
|
+
* 全局层根本没有根。⇒ 人持有的相对 deny / ask 行无处锚定 = 异议,拒闭(引擎契约:判官不猜一行原本指向哪里)。
|
|
427
|
+
*
|
|
428
|
+
* 所以今天这条腿的放行范围与 7.128.x 相同(人的规则、部署的审批名单 / 敏感路径守卫都拦不住手动阀门这一轮折叠);变的只是「判官不再是代答」—— 往后每喂一层,
|
|
429
|
+
* 只在本函数加键,钉 = `test/memory-consolidation-write-judge.test.ts` ⑦。
|
|
410
430
|
*
|
|
411
|
-
* 答 `no_objection` —— 与 v7.49.x 本腿的行为逐字同(那时驱动根本不问),不回退任何现网能力;每被问一次记一次
|
|
412
|
-
* `server.memory-consolidation.operator-valve-unjudged`(P-DEBT:计数 + 探针行 + 一次性告警),债在遥测里显形。
|
|
413
431
|
* 自动整理腿不受影响:它走引擎腿自己的判官,人的持久规则与部署策略照判。
|
|
414
|
-
* 退役条件:引擎导出宿主可用的写判官构造器(收部署策略 + 一只规则店主体)那一版,本函数换成它、本 tag 删。
|
|
415
|
-
* scope 与人:本仓的文件后端不按请求主体铸 scope(单用户形是部署声明的全局层 `MEMORY_SCOPE` 与按任务根派生的项目层),
|
|
416
|
-
* 所以从 scope 读不出「这是谁的库」;匿名 run 读不读本地属主那只规则桶另候裁定 —— 两件都不在本座里猜。
|
|
417
432
|
*/
|
|
418
|
-
export declare function
|
|
433
|
+
export declare function createOperatorValveJudgeInput(facts: {
|
|
434
|
+
/** 部署唯一的合成规则店(装配第 4 段铸一次,`RunnerDeps.permissionRuleStore` 与本座读同一只)。缺席 = 规则店没装配。 */
|
|
435
|
+
readonly ruleStoreProvider: PermissionRuleStoreProvider | undefined;
|
|
436
|
+
/** 只读 `executionLaneHomeDir` 读的两位(`ServiceConfig` 结构满足)。 */
|
|
437
|
+
readonly config: LaneHomeDeployment;
|
|
438
|
+
}): ConsolidationWriteJudgeInput;
|
|
439
|
+
/**
|
|
440
|
+
* 运维阀门腿的写判官座:引擎的判官构造器喂 {@link createOperatorValveJudgeInput},**外加一个响亮的债计数**(P-DEBT)。
|
|
441
|
+
*
|
|
442
|
+
* 构造器只有引擎那一只,本函数不判任何东西:`judge(target)` 的答案原样返回。它只在每次被问时记一次
|
|
443
|
+
* `server.memory-consolidation.operator-valve-layers-unfed`(计数 + 探针行 + 一次性告警)—— 因为人的规则层、部署策略层、项目根三样都没喂
|
|
444
|
+
* ({@link createOperatorValveJudgeInput} 头注),放行的最坏后果没变:运维手动整理时,一条点名记忆库路径的人的持久规则或部署写策略拦不住这一轮折叠
|
|
445
|
+
* (与 7.128.x 逐字同)。债在遥测里显形,而不是随着「判官已换成引擎的」这句话消失。自动整理腿不受影响,走引擎腿自己的判官。
|
|
446
|
+
* 收口 = 三层各自有了来源(人:「读谁的桶」裁定;策略:设计 + 引擎的豁免根入参;根:无来源则永久缺席并改写本债)那一版,本 tag 删。
|
|
447
|
+
*/
|
|
448
|
+
export declare function createOperatorValveWriteJudge(facts: Parameters<typeof createOperatorValveJudgeInput>[0]): ConsolidationWriteJudge;
|
|
419
449
|
/** 两个 admin 口消费的窄能力面(`ServiceDeps.memoryConsolidation` 的实参来源)。 */
|
|
420
450
|
export interface MemoryConsolidationFaces {
|
|
421
451
|
/** D3 的座位自证(投影用;boot 期解析一次,restart-to-apply)。 */
|
|
@@ -476,7 +506,7 @@ export declare function createMemoryConsolidationFaces(store: {
|
|
|
476
506
|
* 早问库目录一次、应用时问每个产物文件,答 `no_objection` 以外一律拒写。装配层给哪一只见 {@link createOperatorValveWriteJudge}。 */
|
|
477
507
|
writeJudge: ConsolidationWriteJudge;
|
|
478
508
|
/** 引擎通告的**运维行**座(与 Runner 的 `onNotice` 同一只转发器)。缺席 ⇒ 通告只在回执上。判官拒的两种时机都经它走一次:
|
|
479
|
-
* 应用时拒 = 引擎在驱动里铸的 `memory.consolidation_incomplete`;早问拒 =
|
|
509
|
+
* 应用时拒 = 引擎在驱动里铸的 `memory.consolidation_incomplete`;早问拒 = 驱动抛出的拒绝经引擎导出的投递口(`deliverWriteRefusal`)转成的
|
|
480
510
|
* `memory.consolidation_refused`(本仓不读通告名、不自铸通告形)。 */
|
|
481
511
|
onNotice?: (notice: EngineNotice) => void;
|
|
482
512
|
}): MemoryConsolidationFaces;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { redactSecrets } from "../trace/redact.js";
|
|
2
2
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
3
3
|
import { memoryMountRootOf } from "../memory-scope.js";
|
|
4
|
+
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
4
5
|
import { DEFAULT_MODEL_REF, resolveMainModel } from "../model-select.js";
|
|
5
6
|
import { backendOwnsControlPlane } from "../memory-operator-faces.js";
|
|
6
|
-
import {
|
|
7
|
+
import { createConsolidationWriteJudge, deliverWriteRefusal, expandTiers, MemoryEngine, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
|
|
7
8
|
export function resolveConsolidationDriverSeat(deps) {
|
|
8
9
|
let options;
|
|
9
10
|
try {
|
|
@@ -317,10 +318,18 @@ export function assertAutoConsolidationWirable(input) {
|
|
|
317
318
|
`unset, or set MEMORY_AUTO_CONSOLIDATION=off (unsetting it is NOT the same: the engine's named default is ON as of ` +
|
|
318
319
|
`core 7.21.1).`);
|
|
319
320
|
}
|
|
320
|
-
export function
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
export function createOperatorValveJudgeInput(facts) {
|
|
322
|
+
const home = executionLaneHomeDir(facts.config);
|
|
323
|
+
return {
|
|
324
|
+
...(facts.ruleStoreProvider !== undefined ? { ruleStoreProvider: facts.ruleStoreProvider } : {}),
|
|
325
|
+
anchors: home !== undefined ? { home } : {},
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
export function createOperatorValveWriteJudge(facts) {
|
|
329
|
+
const judge = createConsolidationWriteJudge(createOperatorValveJudgeInput(facts));
|
|
330
|
+
return (target) => {
|
|
331
|
+
recordFailOpen("server.memory-consolidation.operator-valve-layers-unfed");
|
|
332
|
+
return judge(target);
|
|
324
333
|
};
|
|
325
334
|
}
|
|
326
335
|
export function createMemoryConsolidationFaces(store, input) {
|
|
@@ -343,9 +352,7 @@ export function createMemoryConsolidationFaces(store, input) {
|
|
|
343
352
|
writeJudge: input.writeJudge,
|
|
344
353
|
...(input.onNotice !== undefined ? { onNotice: input.onNotice } : {}),
|
|
345
354
|
}).catch((err) => {
|
|
346
|
-
|
|
347
|
-
input.onNotice(memoryConsolidationRefusedNotice({ code: err.code, scope, reason: err.message, ...(err.occurrenceId !== undefined ? { occurrenceId: err.occurrenceId } : {}) }));
|
|
348
|
-
}
|
|
355
|
+
deliverWriteRefusal(err, scope, input.onNotice);
|
|
349
356
|
throw err;
|
|
350
357
|
});
|
|
351
358
|
if (receipt.stopDetail !== undefined) {
|
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
* 降级成**结构性事实**(这条路上根本没有 wrapper);
|
|
27
27
|
* · `headers` 半边从此有出路(wrapper 形只注得进 `apiKey`,core 的座位 `{apiKey, headers?}` 两半都收);
|
|
28
28
|
* · 判据(`gatewayBaseUrl`)与 resolver 一样**按调用现取** —— 见下面 `createSideQueryLane` 的注。
|
|
29
|
-
* `createPerModelKeyBrain`
|
|
30
|
-
*
|
|
31
|
-
* (
|
|
29
|
+
* `createPerModelKeyBrain` 当时**没删**:它还是 WebFetch 摘要面(`boot/webfetch-summarize-lane.ts`)的 key plane ——
|
|
30
|
+
* core 的 `WebFetchSummarizerOptions` 那时只有 `maxContentChars`。core 7.51.0 给摘要器补上了同一签名的座
|
|
31
|
+
* (`WebFetchSummarizerOptions.getApiKeyAndHeaders`)⇒ 包装退役,四面(任务 / side-query / 记忆整理 / WebFetch 摘要)
|
|
32
|
+
* 都经 core 的座位供给 per-model 凭据,本仓不再在任何一面包 brain。
|
|
32
33
|
*
|
|
33
34
|
* 🔴 **poison(声明的钥匙拿不到)上抛,绝不静默回落网关 key** —— `resolveModelApiKey` 抛
|
|
34
35
|
* `ModelKeyPoisonedError` 是「这个模型配了自己的钥匙(托管密钥解不开 / S-896 起 `apiKeyEnv` 变量缺席)却拿不到」的响亮态;吞成 undefined 等于用共享
|
|
@@ -46,8 +47,9 @@
|
|
|
46
47
|
* 是配对法的单一实现,三只一方 brain(openai / anthropic / open-responses)在 `buildRequest` 里逐次
|
|
47
48
|
* 复判 —— 部署声明了 `config.baseUrl` 而 entry URL 不在那个根上、凭据又只有部署级那一份 ⇒
|
|
48
49
|
* `route.credential_mismatch` 响亮拒;连部署级凭据都没有 ⇒ `route.credential_missing`。**三面同批**
|
|
49
|
-
* (主推理链 / side-query / WebFetch 摘要走的是同一只 brain 栈),旋钮 =
|
|
50
|
-
* `config.baseUrl
|
|
50
|
+
* (主推理链 / side-query / WebFetch 摘要走的是同一只 brain 栈),旋钮 = 给模型自己的凭据(当年还有第二条:不声明
|
|
51
|
+
* `config.baseUrl`,走 core 的 `"unpinned"` 快速起步姿态 —— core 7.51.0 #1531 起该姿态退役,一份部署凭据只配一个根)。
|
|
52
|
+
* ⇒ [ref] 要的「三面同批 + 旋钮」由上游一次交齐。
|
|
51
53
|
*
|
|
52
54
|
* 于是本席的 `isOffRouteBaseUrl` + `recordFailOpen("server.model-key.off-route-model-uses-gateway-key")`
|
|
53
55
|
* 两件**一并撤线**:那条计数的语义是「这一次调用真把网关凭据发给了别家」,而在 5.57 上那次调用根本
|
|
@@ -97,21 +99,6 @@ export interface SideQueryLaneCtx {
|
|
|
97
99
|
export declare function createPerModelAuthSeat(getKeyResolver: () => KeyResolver, opts?: {
|
|
98
100
|
readonly upstream?: SideQuerySpec["getApiKeyAndHeaders"];
|
|
99
101
|
}): PerModelAuthSeat;
|
|
100
|
-
/**
|
|
101
|
-
* per-model key 注入 brain wrapper —— **[ref] 后只服务 WebFetch 摘要面**
|
|
102
|
-
* (`boot/webfetch-summarize-lane.ts`)。side-query 面已换到 core 的 spec 座位(见顶注「[ref] 换装」);
|
|
103
|
-
* core 的 `WebFetchSummarizerOptions` 至今只有 `maxContentChars`(亲验 5.46.0 `dist/tools/web.d.ts`),
|
|
104
|
-
* 那一面**没有座位可换**,只能继续在 brain 层补凭据。
|
|
105
|
-
*
|
|
106
|
-
* 语义(与 `createPerModelAuthSeat` 同一张表,只是注入位在 `options.apiKey`):
|
|
107
|
-
* · `options.apiKey` **已在场** ⇒ 原样透传(上游/装饰器已经定了凭据,本层不抢);
|
|
108
|
-
* · resolver 缺席 / 返回 undefined ⇒ 不注入 ⇒ 网关 key(additive 契约);
|
|
109
|
-
* · resolver 抛(sealed key 中毒)⇒ **上抛**,本次调用整体失败,一个字节都不发。
|
|
110
|
-
*
|
|
111
|
-
* `complete` 在场时同样包一层:core 的 `Brain.complete` 是 compaction/摘要腿的直调口(WebFetch 摘要器
|
|
112
|
-
* 正是**优先**走它),漏包等于给同一个病留第二条路。
|
|
113
|
-
*/
|
|
114
|
-
export declare function createPerModelKeyBrain(brain: Brain, getKeyResolver: () => KeyResolver): Brain;
|
|
115
102
|
/**
|
|
116
103
|
* 装配 side-query 执行席 = 真 `runSideQuery` + core 的 per-model auth 座,models/roles 与 Runner 同源。
|
|
117
104
|
*
|
|
@@ -8,30 +8,6 @@ export function createPerModelAuthSeat(getKeyResolver, opts = {}) {
|
|
|
8
8
|
return resolve ? await resolve({ name: model.name }) : undefined;
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
export function createPerModelKeyBrain(brain, getKeyResolver) {
|
|
12
|
-
const seat = createPerModelAuthSeat(getKeyResolver);
|
|
13
|
-
const keyFor = async (model, given) => {
|
|
14
|
-
if (given !== undefined)
|
|
15
|
-
return given;
|
|
16
|
-
return (await seat(model))?.apiKey;
|
|
17
|
-
};
|
|
18
|
-
const stream = async (model, context, options) => {
|
|
19
|
-
const apiKey = await keyFor(model, options?.apiKey);
|
|
20
|
-
return brain.stream(model, context, apiKey === undefined ? options : { ...options, apiKey });
|
|
21
|
-
};
|
|
22
|
-
const complete = brain.complete?.bind(brain);
|
|
23
|
-
return {
|
|
24
|
-
stream,
|
|
25
|
-
...(complete
|
|
26
|
-
? {
|
|
27
|
-
complete: async (model, context, options) => {
|
|
28
|
-
const apiKey = await keyFor(model, options?.apiKey);
|
|
29
|
-
return complete(model, context, apiKey === undefined ? options : { ...options, apiKey });
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
: {}),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
11
|
export function createSideQueryLane(ctx) {
|
|
36
12
|
return (spec) => runSideQuery({
|
|
37
13
|
...spec,
|
|
@@ -14,8 +14,11 @@ import { setRunTerminalLogObserver } from "../observability/run-terminal.js";
|
|
|
14
14
|
import { setRedactionObserver } from "../trace/redact.js";
|
|
15
15
|
import { installOutboundDispatcher } from "../outbound-dispatcher.js";
|
|
16
16
|
import { provisionSemaConfigDir } from "../sema-config-dir.js";
|
|
17
|
+
import { bindOwnCredentialsConfig, declareServerWithheldEnv } from "../server-secret-env.js";
|
|
17
18
|
export function runStage01() {
|
|
19
|
+
declareServerWithheldEnv();
|
|
18
20
|
const config = loadConfig();
|
|
21
|
+
bindOwnCredentialsConfig(config);
|
|
19
22
|
const runStaleFailure = crossInvariant("run-stale-heartbeat").check({ live: config, next: config });
|
|
20
23
|
if (runStaleFailure !== undefined) {
|
|
21
24
|
throw new Error(runStaleFailure);
|
|
@@ -76,7 +76,7 @@ export function runStage06(ctx) {
|
|
|
76
76
|
onIncident: (err) => logger.warn("memory_consolidation_incident", { code: err.code ?? null, detail: err.message }),
|
|
77
77
|
onStopDetail: (audit) => logger.warn("memory_consolidation_stop_detail", audit),
|
|
78
78
|
...(sessionCaptureRecordStore !== undefined ? { captureRecordStore: sessionCaptureRecordStore } : {}),
|
|
79
|
-
writeJudge: createOperatorValveWriteJudge(),
|
|
79
|
+
writeJudge: createOperatorValveWriteJudge({ ruleStoreProvider: permissionRuleProvider, config }),
|
|
80
80
|
onNotice: createEngineNoticeForwarder(logger, defaultEngineNoticeRouter, metrics),
|
|
81
81
|
})
|
|
82
82
|
: undefined;
|
package/dist/boot/stores.js
CHANGED
|
@@ -18,7 +18,7 @@ import { PgRosterStore, TiDBRosterStore, ensurePgRosterSchema, ensureTiDBRosterS
|
|
|
18
18
|
import { LocalTaskAttachmentStore } from "../plugins/local-task-attachment-store.js";
|
|
19
19
|
import { PgTaskAttachmentStore, TiDBTaskAttachmentStore, ensurePgTaskAttachmentSchema, ensureTiDBTaskAttachmentSchema } from "../plugins/task-attachment-store.js";
|
|
20
20
|
import { createSessionStore } from "../plugins/session-store.js";
|
|
21
|
-
import {
|
|
21
|
+
import { runAndLogOneTimeMigrations } from "../plugins/one-time-migrations.js";
|
|
22
22
|
import { createTaskListLane } from "./task-list-lane.js";
|
|
23
23
|
import { ensurePgTaskListSchema, ensureTiDBTaskListSchema } from "../plugins/task-list-store-sql.js";
|
|
24
24
|
import { assertDeviceAuditRebindEventSchema, ensurePgDeviceSchema, ensureTiDBDeviceSchema } from "../plugins/device-store-sql.js";
|
|
@@ -100,7 +100,7 @@ export async function openStores(ctx) {
|
|
|
100
100
|
await assertFileHistorySchema(async (sql) => ({ rows: (await mysqlPool.query(sql))[0] }), "tidb");
|
|
101
101
|
}
|
|
102
102
|
if (backend)
|
|
103
|
-
|
|
103
|
+
await runAndLogOneTimeMigrations(logger, backend);
|
|
104
104
|
if ((process.env.REWIND_SNAPSHOT_MAX_MB ?? "") !== "") {
|
|
105
105
|
logger.warn("rewind_snapshot_max_mb_retired", {
|
|
106
106
|
note: "REWIND_SNAPSHOT_MAX_MB no longer has an effect: the whole-tree snapshot epoch is retired (S-15); per-edited-file history captures only files the agent touches, with no tree walk and no size gate. Remove the env var. Per-blob SQL sizing is governed by SNAPSHOT_BLOB_SQL_MAX_BYTES / object storage as before.",
|
package/dist/boot/web-admin.js
CHANGED
|
@@ -19,13 +19,14 @@ import { centerManagedConfigKeys } from "../web-admin-client/apply-effective.js"
|
|
|
19
19
|
import { createConfigProvider, raceBootFetch, serviceConfigSourceKind, BOOT_FETCH_DEFERRED } from "../config-provider.js";
|
|
20
20
|
import { createKeyResolver } from "../key-resolver.js";
|
|
21
21
|
import { buildSealedKeyPublicFace, ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
22
|
-
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applySkillsManifest, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../web-admin-client/facade.js";
|
|
22
|
+
import { applyEffective, applyCenterReadFace, buildReadFaceBaseline, buildReadFaceSwapCandidate, restoreReadFaceBaseline, mutateInPlace, logEffectiveDiff, applySkillsManifest, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../web-admin-client/facade.js";
|
|
23
23
|
import { buildConfigHandoff, buildRestartSignal, configHandoffMissing } from "../fleet/engine-signal.js";
|
|
24
24
|
import { createMcpRevocationLedger, enabledMcpEntries } from "../web-admin-client/mcp-revocation.js";
|
|
25
25
|
import { buildUnreadConfigFileFields } from "../web-admin-client/read-warnings.js";
|
|
26
26
|
import { CONFIG_DOMAIN_INVALID_NOTE, buildCandidateRejectedFields, candidateRejectionOf, judgeConfigCandidate } from "../web-admin-client/candidate-gate.js";
|
|
27
27
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
28
28
|
import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../web-admin-client/apply-ledger.js";
|
|
29
|
+
import { buildDeploymentPostureSeats } from "./runner-deps.js";
|
|
29
30
|
function lkgReadNote(scope) {
|
|
30
31
|
switch (scope) {
|
|
31
32
|
case "whole":
|
|
@@ -631,7 +632,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
631
632
|
if (!dryRun && cc !== undefined && currentIdentity() !== promptStateIdentity)
|
|
632
633
|
await rebasePromptState(currentIdentity(), "boot-end");
|
|
633
634
|
}
|
|
634
|
-
const readFaceBaseline =
|
|
635
|
+
const readFaceBaseline = buildReadFaceBaseline(config);
|
|
635
636
|
const readFaceEnvHeld = applyCenterReadFace(config, effective ?? { version: 0, updatedAt: "", models: { models: [], roles: {} } }, effective ? logger : undefined).envHeld;
|
|
636
637
|
if (!modelReadyState.ready)
|
|
637
638
|
logger.warn("model_roster_pending", {
|
|
@@ -648,18 +649,21 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
648
649
|
...(mcpRevocationWired && mcpRevocations.hasBaseline() ? { mcpRevocationLive: true } : {}),
|
|
649
650
|
});
|
|
650
651
|
const applyLiveReadFace = (eff, swap, phase) => {
|
|
651
|
-
const before = config
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
if (!faceChanged)
|
|
652
|
+
const before = buildReadFaceBaseline(config);
|
|
653
|
+
const { changedSeats } = applyCenterReadFace(config, eff, logger, { phase: "live", baseline: readFaceBaseline });
|
|
654
|
+
if (changedSeats.length === 0)
|
|
655
655
|
return;
|
|
656
656
|
try {
|
|
657
|
-
swap(
|
|
658
|
-
logger.info("read_face_swapped", {
|
|
657
|
+
swap(buildReadFaceSwapCandidate(buildDeploymentPostureSeats(config), changedSeats));
|
|
658
|
+
logger.info("read_face_swapped", {
|
|
659
|
+
seats: changedSeats,
|
|
660
|
+
...(changedSeats.includes("readFace") ? { from: before.readFace ?? null, to: config.readFace ?? null } : {}),
|
|
661
|
+
phase,
|
|
662
|
+
note: "hot seats (core swapDeps) — legs that already prepared keep the read posture they prepared under",
|
|
663
|
+
});
|
|
659
664
|
}
|
|
660
665
|
catch (err) {
|
|
661
|
-
config
|
|
662
|
-
config.readFaceSource = beforeSource;
|
|
666
|
+
restoreReadFaceBaseline(config, before);
|
|
663
667
|
throw err;
|
|
664
668
|
}
|
|
665
669
|
};
|
|
@@ -976,6 +980,9 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
976
980
|
if (configProvider.kind === "remote")
|
|
977
981
|
lastPull = "ok";
|
|
978
982
|
}
|
|
983
|
+
catch (err) {
|
|
984
|
+
logger.warn("web_admin_refresh_failed", { err: String(err), leg: "boot-deferred", note: "etag NOT advanced — the refresh cadence re-fetches and re-applies the same candidate" });
|
|
985
|
+
}
|
|
979
986
|
finally {
|
|
980
987
|
refreshInFlight = false;
|
|
981
988
|
}
|
|
@@ -21,13 +21,16 @@
|
|
|
21
21
|
*
|
|
22
22
|
* ## 修形 = 照 [ref] 的配方接第四面(不是新造语义)
|
|
23
23
|
*
|
|
24
|
-
* brain
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
24
|
+
* 当年 core 的摘要器没有凭据座,只能在 brain 外面包一层注入 `options.apiKey`(`createPerModelKeyBrain`)。
|
|
25
|
+
* core 7.51.0 起 `WebFetchSummarizerOptions.getApiKeyAndHeaders` 是与 `TaskSpec` / `SideQuerySpec` 同一签名的座
|
|
26
|
+
* (每次摘要按摘要器的模型解一次,`{ apiKey, headers }` 两半都收)⇒ 那层包装退役:`RunnerDeps.brain` 原样交给
|
|
27
|
+
* 摘要器,座位由与 side-query / 记忆整理**同一只**座位工厂(`createPerModelAuthSeat`)填充:
|
|
28
|
+
* · 有 per-model key ⇒ 请求带模型自己的凭据;
|
|
29
|
+
* · 无 ⇒ 座位回 `undefined` ⇒ 交回 core 按它自己的 key↔URL 配对法判(部署根上的模型带网关 key;
|
|
30
|
+
* entry URL 不在部署根上 ⇒ `route.credential_mismatch` 响亮拒 —— core 7.51.0 起没有「未声明部署根」的
|
|
31
|
+
* `unpinned` 快速起步姿态了,一份部署凭据只配一个根。[ref] 的具名残余与其 P-DEBT 腿已在源头清偿,
|
|
32
|
+
* 见 side-query-lane.ts 顶注);
|
|
29
33
|
* · 毒丸 ⇒ **上抛**,一个字节都不发(CLAUDE.md [ref] 安全轴 fail-closed)。
|
|
30
|
-
* wrapper 只包本席这一只引用,`RunnerDeps.brain` 零扰动(与 side-query 席同形)。
|
|
31
34
|
*
|
|
32
35
|
* ## 为什么住在 `boot/` 而不是继续躺在 `main()` 的闭包里
|
|
33
36
|
*
|
|
@@ -49,7 +52,7 @@ import { type KeyResolver } from "./side-query-lane.js";
|
|
|
49
52
|
/** WebFetch 摘要执行席(= `ScenarioDeps.webFetchSummarize`,core 的 `WebFetchConfig.summarize` 逐字同形)。 */
|
|
50
53
|
export type WebFetchSummarizeLane = NonNullable<NonNullable<WebFetchConfig["summarize"]>>;
|
|
51
54
|
export interface WebFetchSummarizeLaneCtx {
|
|
52
|
-
/** 与 `RunnerDeps.brain`
|
|
55
|
+
/** 与 `RunnerDeps.brain` **同一只**实例,原样交给摘要器(凭据走摘要器的座位,不包 brain)。 */
|
|
53
56
|
brain: Brain;
|
|
54
57
|
/** 活配置引用:目录/角色表被 web-admin 就地热应用(`mutateInPlace`),按调用取值才跟得上。 */
|
|
55
58
|
config: ServiceConfig;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createWebFetchSummarizer, resolveTaskModel as coreResolveTaskModel } from "@sema-agent/core";
|
|
2
|
-
import {
|
|
2
|
+
import { createPerModelAuthSeat } from "./side-query-lane.js";
|
|
3
3
|
export function createWebFetchSummarizeLane(ctx) {
|
|
4
|
-
const brain = createPerModelKeyBrain(ctx.brain, ctx.getKeyResolver);
|
|
5
4
|
return async (content, prompt, signal) => {
|
|
6
5
|
const model = (() => {
|
|
7
6
|
try {
|
|
@@ -14,7 +13,7 @@ export function createWebFetchSummarizeLane(ctx) {
|
|
|
14
13
|
})();
|
|
15
14
|
if (!model)
|
|
16
15
|
throw new Error("summarize model unresolved for this deployment");
|
|
17
|
-
return createWebFetchSummarizer(brain, model)(content, prompt, signal);
|
|
16
|
+
return createWebFetchSummarizer(ctx.brain, model, { getApiKeyAndHeaders: createPerModelAuthSeat(ctx.getKeyResolver) })(content, prompt, signal);
|
|
18
17
|
};
|
|
19
18
|
}
|
|
20
19
|
//# sourceMappingURL=webfetch-summarize-lane.js.map
|
package/dist/brain.js
CHANGED
|
@@ -54,11 +54,10 @@ export function createBrainAssembly(config, deps = {}) {
|
|
|
54
54
|
: {};
|
|
55
55
|
const routes = deploymentGatewayRoots(config);
|
|
56
56
|
const openaiBrains = routes.map((baseUrl, i) => {
|
|
57
|
+
const gatewayKey = deploymentGatewayKey(config);
|
|
57
58
|
const brain = constructRouteBrain(i === 0 ? "gateway route" : `gateway fallback route[${i - 1}]`, [baseUrl], () => createOpenAIBrain({
|
|
58
59
|
baseUrl,
|
|
59
|
-
|
|
60
|
-
return deploymentGatewayKey(config);
|
|
61
|
-
},
|
|
60
|
+
...(gatewayKey !== undefined ? { apiKey: gatewayKey } : {}),
|
|
62
61
|
...retryCap,
|
|
63
62
|
fetchImpl,
|
|
64
63
|
...timeouts,
|
|
@@ -106,16 +105,14 @@ export function createBrainAssembly(config, deps = {}) {
|
|
|
106
105
|
const ownSealed = credential.sealed !== undefined ? { [fallbackModel.name]: credential.sealed } : {};
|
|
107
106
|
const fbOwnApiKey = () => resolveModelApiKey(fallbackModel.name, ownEnvRef, process.env, ownSealed);
|
|
108
107
|
const anthropicLaneHop = dispatchBrainLane(fallbackModel.provider, anthropicBrain !== undefined) === "anthropic";
|
|
108
|
+
const fbDeclaredRoot = degradeDeclaredRoot(fallbackModel.baseUrl, config);
|
|
109
|
+
const fbDeploymentKey = deploymentGatewayKey(config);
|
|
109
110
|
const fallback = anthropicLaneHop && anthropicBrain
|
|
110
111
|
?
|
|
111
112
|
anthropicBrain
|
|
112
113
|
: constructRouteBrain(`degrade target "${degradeTo}"`, [fallbackModel.baseUrl, config.gatewayBaseUrl], () => createOpenAIBrain({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
},
|
|
116
|
-
get apiKey() {
|
|
117
|
-
return deploymentGatewayKey(config) || undefined;
|
|
118
|
-
},
|
|
114
|
+
...(fbDeclaredRoot !== undefined ? { baseUrl: fbDeclaredRoot } : {}),
|
|
115
|
+
...(fbDeploymentKey ? { apiKey: fbDeploymentKey } : {}),
|
|
119
116
|
...(config.gatewayMaxRetries !== undefined ? { maxRetries: config.gatewayMaxRetries } : {}),
|
|
120
117
|
fetchImpl,
|
|
121
118
|
...timeouts,
|
|
@@ -27,8 +27,8 @@ export interface SubagentExtraToolOptions {
|
|
|
27
27
|
*
|
|
28
28
|
* core 7.46.0 #1432 —— **工厂与名册一个来源**:返回委派工具的两键,`extraTools`(每次 spawn 交出 `forSpawn` 选中的那几只)与
|
|
29
29
|
* `extraToolNames`(全部可能交出的那几只的名字 / 别名 / 内容来源,从同一批 `ToolSpec` 读出)。名册在场 ⇒ 引擎重新能列出子代的工具池:
|
|
30
|
-
* 定义级 / 工具级 / `TaskSpec.agents`
|
|
31
|
-
*
|
|
30
|
+
* 定义级 / 工具级 / `TaskSpec.agents` 清单里没有任何工具答应的名字由引擎按名册判(core 7.51.0 #1736 起不拒 run:条目原样保留、告知一次并带该写的拼法;
|
|
31
|
+
* 7.46.0–7.50.x 曾开跑前拒 `config.agent.unknown_tool`),记忆内容来源读者按声明读这几只。
|
|
32
32
|
* 修前(core 7.32.0–7.45.x)工厂恒在场 ⇒ 池不可静态知 ⇒ 那一族拼法只告不拒、委派结果一律按「外部」读。
|
|
33
33
|
* 工厂**不抛**:各组的 `tools` 是装配期就建好的对象;部署 `forSpawn` 的抛由 core 收成「这一 spawn 什么都不继承 + 模型面披露句」(onExtraToolsError 记 warn)。
|
|
34
34
|
* 一种拼法出现在两只工具上(部署工具与 `Now` 撞名 / 别名撞)⇒ core 在建委派工具处抛 `config.extra_tool_names`(`shape`),不静默取其一。
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 引擎能力词 → `GET /v1/capabilities` 顶层布尔键(core 7.51.1 `ENGINE_CAPABILITIES`;[ref]:词由引擎定,投影形由本仓定)。
|
|
3
|
+
*
|
|
4
|
+
* 形:引擎的能力词**原样**作顶层键(`"inherit_env.own_credentials_withheld": true`),值 = 该词在**本进程**为真的事实。键集由 `ENGINE_CAPABILITIES`
|
|
5
|
+
* 机器派生(一只投影循环,不逐词写键);事实表 {@link ENGINE_CAPABILITY_FACTS} 是 `Record<EngineCapability, …>` 型 —— 引擎加词而本表没跟 = tsc 红,
|
|
6
|
+
* 本表多一个引擎没有的词 = tsc 红(闭集的双向门),所以「这台引擎说它能,本服务也真接上了」不靠人记得登记。
|
|
7
|
+
* 壳按词闸(cli 读顶层布尔 `true`),不按版本号猜;契约附录 B.1 与 `test/capabilities-doc-appendix.test.ts` 把这些键与引擎词集对拍。
|
|
8
|
+
*/
|
|
9
|
+
import { type EngineCapability } from "@sema-agent/core";
|
|
10
|
+
/** 投影:引擎词集的每一词 → 本进程的事实(键集恒 = `ENGINE_CAPABILITIES`)。 */
|
|
11
|
+
export declare function engineCapabilityBits(): {
|
|
12
|
+
readonly [W in EngineCapability]: boolean;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=engine-capabilities.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ENGINE_CAPABILITIES } from "@sema-agent/core";
|
|
2
|
+
import { ownCredentialsWithheld } from "../server-secret-env.js";
|
|
3
|
+
const ENGINE_CAPABILITY_FACTS = {
|
|
4
|
+
"inherit_env.own_credentials_withheld": ownCredentialsWithheld,
|
|
5
|
+
};
|
|
6
|
+
export function engineCapabilityBits() {
|
|
7
|
+
const out = {};
|
|
8
|
+
for (const word of ENGINE_CAPABILITIES)
|
|
9
|
+
out[word] = ENGINE_CAPABILITY_FACTS[word]();
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=engine-capabilities.js.map
|