@sema-agent/server 7.123.0 → 7.124.1
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/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +53 -51
- package/dist/approval-ask-audit-store.d.ts +27 -32
- package/dist/approval-ask-audit-store.js +10 -9
- package/dist/approval-crash-convergence.d.ts +2 -2
- package/dist/approval-crash-convergence.js +5 -5
- package/dist/approval-deny-reasons.d.ts +163 -51
- package/dist/approval-deny-reasons.js +79 -8
- package/dist/approval-policy-settlement.d.ts +4 -1
- package/dist/approval-policy-settlement.js +3 -2
- package/dist/approval-reconciler.d.ts +3 -3
- package/dist/approval-reconciler.js +10 -10
- package/dist/approval.d.ts +1 -1
- package/dist/approval.js +1 -1
- package/dist/audit.js +1 -1
- package/dist/auth-bridge.d.ts +8 -8
- package/dist/auth-bridge.js +6 -6
- package/dist/auth-keys.d.ts +2 -2
- package/dist/auto-mode-face.d.ts +1 -1
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/execution-env.js +1 -0
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +4 -4
- package/dist/boot/org-memory.d.ts +3 -3
- package/dist/boot/org-memory.js +5 -5
- package/dist/boot/resolve-spec.js +7 -7
- package/dist/boot/runner-deps.d.ts +1 -1
- package/dist/boot/runtime-caps.d.ts +1 -1
- package/dist/boot/runtime-caps.js +10 -10
- package/dist/boot/shutdown.d.ts +2 -2
- package/dist/boot/shutdown.js +2 -2
- package/dist/boot/side-query-lane.d.ts +3 -3
- package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
- package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
- package/dist/boot/stage-03-store-backend.d.ts +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +6 -6
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +6 -5
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +3 -3
- package/dist/boot/stage-09-leader.js +7 -7
- package/dist/boot/stage-10-http-server.d.ts +3 -3
- package/dist/boot/stage-10-http-server.js +19 -19
- package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
- package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
- package/dist/boot/{config-center.js → web-admin.js} +38 -22
- package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
- package/dist/brain.d.ts +1 -1
- package/dist/brain.js +1 -1
- package/dist/budget.d.ts +3 -3
- package/dist/canonical-json.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/center-wiring.d.ts +12 -5
- package/dist/capabilities/center-wiring.js +11 -2
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/team.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/center-credential-renewer.d.ts +1 -1
- package/dist/center-credential-renewer.js +7 -7
- package/dist/center-credential.d.ts +9 -9
- package/dist/center-credential.js +6 -6
- package/dist/config-catalog.d.ts +7 -10
- package/dist/config-catalog.js +15 -15
- package/dist/config-d-migration.d.ts +32 -0
- package/dist/config-d-migration.js +59 -0
- package/dist/config-invariants.d.ts +14 -4
- package/dist/config-invariants.js +31 -2
- package/dist/config-lkg.d.ts +26 -6
- package/dist/config-lkg.js +43 -14
- package/dist/config-provider.d.ts +23 -19
- package/dist/config-provider.js +24 -26
- package/dist/config-types.d.ts +52 -51
- package/dist/config.d.ts +9 -46
- package/dist/config.js +33 -92
- package/dist/execution-lane-caps.d.ts +1 -1
- package/dist/fleet/drain-entry.d.ts +1 -1
- package/dist/fleet/engine-signal.d.ts +8 -8
- package/dist/fleet/fleet-bus.d.ts +12 -3
- package/dist/fleet/fleet-bus.js +13 -9
- package/dist/fleet/serving-lease.d.ts +3 -3
- package/dist/fleet/serving-lease.js +1 -1
- package/dist/fleet-client.d.ts +7 -7
- package/dist/fleet-client.js +5 -4
- package/dist/fleet-lease.d.ts +3 -3
- package/dist/fleet-lease.js +2 -2
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/hosted-posture.d.ts +4 -4
- package/dist/http/admission.js +4 -4
- package/dist/http/checkpoint-ctx.d.ts +20 -3
- package/dist/http/checkpoint-ctx.js +13 -5
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/health-self-report.d.ts +4 -4
- package/dist/http/health-self-report.js +3 -3
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +4 -3
- package/dist/http/resume-legs.js +31 -18
- package/dist/http/route-ctx.d.ts +8 -0
- package/dist/http/routes/admin-config-refresh.d.ts +1 -1
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.d.ts +1 -1
- package/dist/http/routes/admin-drain.js +5 -4
- package/dist/http/routes/approvals-assistant.js +4 -4
- package/dist/http/routes/capabilities.js +5 -5
- package/dist/http/routes/config-catalog.js +2 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/runs.js +8 -4
- package/dist/http/routes/sessions-list.js +2 -1
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.js +3 -3
- package/dist/http/routes/tasks.js +3 -2
- package/dist/http/run-meta.d.ts +1 -1
- package/dist/http/run-meta.js +2 -2
- package/dist/http/server.d.ts +7 -7
- package/dist/http/server.js +1 -1
- package/dist/http/stored-body-replay.js +3 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/key-resolver.d.ts +1 -1
- package/dist/leader/wire.d.ts +2 -2
- package/dist/leader/wire.js +2 -1
- package/dist/main.js +1 -1
- package/dist/mcp-tool-faces.d.ts +2 -2
- package/dist/model-entry-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.js +4 -4
- package/dist/model-provider.d.ts +2 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-url.d.ts +1 -1
- package/dist/model-route-url.js +2 -1
- package/dist/model-select.d.ts +24 -21
- package/dist/model-select.js +22 -17
- package/dist/observability/metrics.d.ts +1 -1
- package/dist/org-memory-admission.d.ts +1 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -2
- package/dist/plugins/approval-ask-store-file.js +3 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
- package/dist/plugins/approval-ask-store-memory.js +10 -4
- package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
- package/dist/plugins/approval-ask-store-sql.js +20 -13
- package/dist/plugins/caching-session-store.d.ts +0 -10
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/file-run-store.js +9 -3
- package/dist/plugins/leader-run-store-sql.d.ts +3 -2
- package/dist/plugins/leader-run-store-sql.js +3 -2
- package/dist/plugins/local-session-store.d.ts +20 -27
- package/dist/plugins/local-session-store.js +19 -17
- package/dist/plugins/memory-run-store.js +9 -3
- package/dist/plugins/one-time-migrations.js +26 -2
- package/dist/plugins/scheduler-support.d.ts +26 -7
- package/dist/plugins/scheduler-support.js +96 -53
- package/dist/plugins/store-backend.js +1 -1
- package/dist/plugins/store-contracts.d.ts +20 -4
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -1
- package/dist/posture-source.d.ts +1 -1
- package/dist/read-face-posture.d.ts +3 -3
- package/dist/read-face-posture.js +1 -1
- package/dist/run-local.js +6 -3
- package/dist/runtime-caps-resolver.d.ts +4 -4
- package/dist/runtime-caps-resolver.js +2 -2
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sealed-key.d.ts +4 -4
- package/dist/sealed-key.js +3 -2
- package/dist/security.d.ts +3 -3
- package/dist/service-version.d.ts +1 -1
- package/dist/service-version.js +5 -0
- package/dist/session-titler.d.ts +0 -11
- package/dist/session-titler.js +3 -5
- package/dist/shared-memory-scope-authorizer.d.ts +4 -4
- package/dist/task-a2a.d.ts +1 -1
- package/dist/tool-approval.d.ts +43 -4
- package/dist/tool-approval.js +147 -98
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.d.ts +3 -3
- package/dist/trace/redact.d.ts +2 -23
- package/dist/trace/redact.js +17 -15
- package/dist/trace/self-minted-wire.d.ts +134 -3
- package/dist/trace/self-minted-wire.js +46 -9
- package/dist/url-refusal.d.ts +35 -0
- package/dist/url-refusal.js +15 -0
- package/dist/user-authored-text.d.ts +31 -0
- package/dist/user-authored-text.js +18 -0
- package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
- package/dist/{config-center → web-admin-client}/apply-effective.js +100 -119
- package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
- package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
- package/dist/{config-center → web-admin-client}/center-request.js +5 -4
- package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
- package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
- package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
- package/dist/{config-center → web-admin-client}/facade.js +2 -2
- package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
- package/dist/{config-center → web-admin-client}/http-client.js +12 -12
- package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/read-face.js +4 -4
- package/dist/{config-center → web-admin-client}/read-warnings.d.ts +23 -16
- package/dist/{config-center → web-admin-client}/read-warnings.js +33 -23
- package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
- package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
- package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
- package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
- package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
- package/dist/{config-center → web-admin-client}/types.d.ts +12 -39
- package/package.json +2 -2
- package/skills/find-skills.md +2 -2
- package/dist/url-shape.d.ts +0 -31
- package/dist/url-shape.js +0 -41
- /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
- /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
- /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/types.js +0 -0
|
@@ -169,7 +169,7 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
169
169
|
removableAfter: "2026-11-09",
|
|
170
170
|
removal: "删 `config-lkg.ts` 的 formatVersion 2 读回迁移:`migrateV2Lkg` / `migrateDroppedIdentityRow` / `RETIRED_CATALOG_COORDINATE` 与其上的窗注;`loadLkg` 的 2 臂" +
|
|
171
171
|
"(版本判据里的 `2` 与 `formatVersion === 2 ? migrateV2Lkg(…)` 那一支 —— 2 回到「unknown formatVersion」拒)、返回型的 `migrated` 位与 JSDoc 里说它的那一行、" +
|
|
172
|
-
"文件头注「2 读回时一次性迁移」那一句;`boot/
|
|
172
|
+
"文件头注「2 读回时一次性迁移」那一句;`boot/web-admin.ts` 读 `read.migrated` 打 `config_lkg_migrated` 的那一行(formatVersion 1 迁移的同名事件那一行留);" +
|
|
173
173
|
"`migrateV1Lkg` 里不带 v1 的 etag 那一句旁注对 `migrateV2Lkg` 的引用(不带 etag 的规则本身留 —— v1 载荷同样没有完整的被丢条目记录);契约 §A.1d " +
|
|
174
174
|
"`droppedEntries` 一节「升级窗(如实)」那一条与 §A.1d `[config.model_refused_name_minted]` 那一条里「LKG 载体 `droppedIdentities` 读回时一次性迁移」那半句;手写 formatVersion 2 信封的测试夹具" +
|
|
175
175
|
"(`test/config-lkg.test.ts`、`test/config-hot/batch1-version-ordinal.test.ts` 改写成 3;`test/models-dropped-entries-s1295.test.ts` 的旧 LKG 迁移格改钉 2 读回不可用)",
|
|
@@ -183,12 +183,36 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
183
183
|
releasedOn: "2026-09-24",
|
|
184
184
|
removableAfter: "2026-10-24",
|
|
185
185
|
removal: "删 `config-lkg.ts` 的 formatVersion 1 迁移:`legacyV1LkgPath` / `migrateV1Lkg` 与其上的窗注(`loadLkg` 的 1 臂今天已是拒句,不动);" +
|
|
186
|
-
"`boot/
|
|
186
|
+
"`boot/web-admin.ts` `bootFromLkg` 里调用 `migrateV1Lkg` 的那一段(含它打 `config_lkg_migrated` / 推不出身份时响亮拒的那两行);" +
|
|
187
187
|
"`migrateV1Lkg` 的测试格(`test/config-lkg.test.ts` v1 迁移组)",
|
|
188
188
|
afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 1 答「pre-identity LKG (formatVersion 1) — not read back …」):旧默认路径上的 v1 文件不再被迁进箱、" +
|
|
189
189
|
"原样留在盘上;中心可达照拉(整拉,新 LKG 按现行箱名落盘)、断着则 env 兜底",
|
|
190
190
|
reportKeys: [],
|
|
191
191
|
},
|
|
192
|
+
{
|
|
193
|
+
id: "M15",
|
|
194
|
+
introducedIn: "7.124.0",
|
|
195
|
+
releasedOn: "2026-10-11",
|
|
196
|
+
removableAfter: "2026-11-10",
|
|
197
|
+
removal: "删 `config-lkg.ts` 的载荷迁移:`migrateLkgPayload` 与其上的窗注、`loadLkg` 里调它的那一段(载荷照旧过 `lkgPayloadProblem`)、`LkgReadMigration.receipts`" +
|
|
198
|
+
"(信封 2 → 3 的 M13 若已先删,`LkgReadMigration` 与 `migrated` 位一并删)、对 `@sema-agent/settings-schema/one-time-migrations` 的 import;`loadLkg` 的版本判据收成" +
|
|
199
|
+
"只认 4(3 回到「unknown formatVersion」拒);`boot/web-admin.ts` 读回后按 4 写回的那一段;文件头注 formatVersion 4 一段里「每次读回都过迁移」那一句;" +
|
|
200
|
+
"`test/ss11-stored-migrations.test.ts` 的 M15 组(4 原样读回那一格留)",
|
|
201
|
+
afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 3 答「unknown formatVersion 3」):旧载荷的 LKG 读回 ⇒ 不可用,中心可达照拉(整拉,新 LKG 按 4 覆盖)、" +
|
|
202
|
+
"断着则 env 兜底 —— 与回滚到 7.123.0 读 4 同一种处置",
|
|
203
|
+
reportKeys: [],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "M16",
|
|
207
|
+
introducedIn: "7.124.0",
|
|
208
|
+
releasedOn: "2026-10-11",
|
|
209
|
+
removableAfter: "2026-11-10",
|
|
210
|
+
removal: "删 `config-d-migration.ts` 整只文件与两个入口的调用(`boot/web-admin.ts` 建本地配置源之后、`run-local.ts` 建配置源之后各一行 + import);" +
|
|
211
|
+
"`test/ss11-stored-migrations.test.ts` 的 M16 组;部署文档里「起服自动迁 config.d」那一句(运维仍可手跑契约命令行,直到契约删族)",
|
|
212
|
+
afterRemoval: "契约读腿的墓碑(永久):config.d 里还带旧形的域文档 —— `runtime.json` 带事实 ⇒ `RetiredDomainError` 整份读不出;`atModelAllowlist` / `models.default` ⇒ " +
|
|
213
|
+
"models 域降级、rosters 整份读不出 —— 拒句点名迁移 id;出路是运维手跑契约命令行(契约删族之后只剩手改)",
|
|
214
|
+
reportKeys: [],
|
|
215
|
+
},
|
|
192
216
|
];
|
|
193
217
|
export function migrationWindowOpen(window, today) {
|
|
194
218
|
return window.removableAfter === null || today <= window.removableAfter;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SchedulerError } from "@sema-agent/core";
|
|
2
2
|
import type { SchedulerCapability, ScheduledIntent, SchedulerContext, ScheduledTaskId, ScheduledTaskSummary } from "@sema-agent/core";
|
|
3
|
-
import type { SchedulerRecord } from "@sema-agent/settings-schema/scheduler-store";
|
|
3
|
+
import type { SchedulerParseWarning, SchedulerRecord } from "@sema-agent/settings-schema/scheduler-store";
|
|
4
4
|
export type { SchedulerRecord };
|
|
5
5
|
/** core `Result<T,E>` shape (harness/types) — `{ok:true,value} | {ok:false,error}`. */
|
|
6
6
|
type Result<T, E> = {
|
|
@@ -31,23 +31,42 @@ export declare function validateIntent(intent: ScheduledIntent, caps: Caps, nowM
|
|
|
31
31
|
export declare function defaultSchedulerStorePath(config: {
|
|
32
32
|
readonly agentConfigDir?: string;
|
|
33
33
|
}): string;
|
|
34
|
+
/** 调度店读写的日志口(logger 的子集)。 */
|
|
35
|
+
export interface SchedulerStoreLog {
|
|
36
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
37
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 一次读的告警 → 日志(settings-schema 11 的调度店读口恒交回告警,没有静默模式)。告警是闭集,穷举;**不带记录原文**(`malformed-record`
|
|
41
|
+
* 的 `record` 可能是任意内容 —— 只报下标):
|
|
42
|
+
* · `unreadable-file` ⇒ warn(整份文件不是 JSON 数组:读成零条,写者一律不写 —— 见 `mutateSchedulerStore`);
|
|
43
|
+
* · `malformed-record` ⇒ warn(这一条读不懂、被跳过;下一个写者落盘时它就没了);
|
|
44
|
+
* · `stale-session-record` ⇒ info(会话链上过期 ≥ 7 天的记录,读时丢、下一个写者落盘);
|
|
45
|
+
* · `one-time-migration` ⇒ info(SS-M3 的读时改写收据:durable 记录换上新键,下一个写者落盘)。
|
|
46
|
+
*/
|
|
47
|
+
export declare function logSchedulerWarnings(log: SchedulerStoreLog, path: string, warnings: readonly SchedulerParseWarning[]): void;
|
|
34
48
|
/**
|
|
35
|
-
* File-backed SchedulerCapability (durable across daemon restarts). Store = a single JSON file
|
|
36
|
-
*
|
|
37
|
-
*
|
|
49
|
+
* File-backed SchedulerCapability (durable across daemon restarts). Store = a single JSON file shared with the resident shell
|
|
50
|
+
* daemon. Every WRITE goes through the contract's ONE mutation primitive `mutateSchedulerStore` (optimistic witness + atomic
|
|
51
|
+
* write; a naked load → save is the lost-update race against the daemon's fire / reap writers). A store file that is not a JSON
|
|
52
|
+
* array is `unreadable_file`: nothing is written over it (the caller gets an `io` error). Reads (`list`) use the contract's read
|
|
53
|
+
* port; every read's warnings go to the log ({@link logSchedulerWarnings}). `now` injectable for tests.
|
|
38
54
|
*/
|
|
39
55
|
export declare class FileSchedulerBackend implements SchedulerCapability {
|
|
40
56
|
readonly schedulerCapabilities: Caps;
|
|
41
57
|
private readonly storePath;
|
|
42
58
|
private readonly now;
|
|
43
|
-
|
|
59
|
+
private readonly log;
|
|
60
|
+
/** `storePath` 必填:路径是装配层从配置定的事实(`SCHEDULER_STORE_PATH` ?? {@link defaultSchedulerStorePath}),本类不自己找缺省。
|
|
61
|
+
* `log` 必填:店的每次读都交回告警(读不懂的文件 / 被跳过的记录 / 读时改写),没有不出声的读法。 */
|
|
44
62
|
constructor(opts: {
|
|
45
63
|
storePath: string;
|
|
64
|
+
log: SchedulerStoreLog;
|
|
46
65
|
caps?: Partial<Caps>;
|
|
47
66
|
now?: () => number;
|
|
48
67
|
});
|
|
49
|
-
|
|
50
|
-
private
|
|
68
|
+
/** 一次读改写(契约的唯一写原语)。`fn` 必须纯、可重放(并发写者落地时整段重放);返回 `null` = 不写。拿不到结果的三形各成一个 `io` 错。 */
|
|
69
|
+
private mutate;
|
|
51
70
|
schedule(intent: ScheduledIntent, ctx: SchedulerContext): Promise<Result<{
|
|
52
71
|
id: ScheduledTaskId;
|
|
53
72
|
}, SchedulerError>>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { randomBytes, createHash } from "node:crypto";
|
|
3
3
|
import { SchedulerError, isValidCronExpr } from "@sema-agent/core";
|
|
4
|
-
import { loadSchedulerStore,
|
|
4
|
+
import { loadSchedulerStore, mutateSchedulerStore } from "@sema-agent/settings-schema/node";
|
|
5
5
|
import { configRootOf } from "../sema-config-dir.js";
|
|
6
6
|
const ok = (value) => ({ ok: true, value });
|
|
7
7
|
const err = (error) => ({ ok: false, error });
|
|
@@ -62,20 +62,67 @@ function computeNextRunMs(when, nowMs) {
|
|
|
62
62
|
export function defaultSchedulerStorePath(config) {
|
|
63
63
|
return join(configRootOf(config), "scheduled_tasks.json");
|
|
64
64
|
}
|
|
65
|
+
export function logSchedulerWarnings(log, path, warnings) {
|
|
66
|
+
for (const w of warnings) {
|
|
67
|
+
switch (w.kind) {
|
|
68
|
+
case "unreadable-file":
|
|
69
|
+
log.warn("scheduler_store_unreadable", { path, why: w.why, note: "the scheduled-task store is not a JSON array — it reads as zero records and no writer overwrites it; fix or remove the file" });
|
|
70
|
+
break;
|
|
71
|
+
case "malformed-record":
|
|
72
|
+
log.warn("scheduler_store_record_malformed", { path, index: w.index, note: "this record does not read and is skipped (the next write drops it)" });
|
|
73
|
+
break;
|
|
74
|
+
case "stale-session-record":
|
|
75
|
+
log.info("scheduler_store_record_stale", { path, index: w.index, id: w.id, note: "a session-chained record whose next run is ≥ 7 days past — dropped on read (the next write persists the drop)" });
|
|
76
|
+
break;
|
|
77
|
+
case "one-time-migration":
|
|
78
|
+
log.info("scheduler_store_record_migrated", { path, index: w.index, migration: w.receipt.migration, appliedAs: w.receipt.appliedAs, note: "settings-schema one-time migration rewrote this record on read (the next write persists it)" });
|
|
79
|
+
break;
|
|
80
|
+
default: {
|
|
81
|
+
const unhandled = w;
|
|
82
|
+
throw new Error(`scheduler store: unknown read warning ${JSON.stringify(unhandled)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
65
87
|
export class FileSchedulerBackend {
|
|
66
88
|
schedulerCapabilities;
|
|
67
89
|
storePath;
|
|
68
90
|
now;
|
|
91
|
+
log;
|
|
69
92
|
constructor(opts) {
|
|
70
93
|
this.schedulerCapabilities = { ...DEFAULT_SCHEDULER_CAPS, ...(opts.caps ?? {}) };
|
|
71
94
|
this.storePath = opts.storePath;
|
|
95
|
+
this.log = opts.log;
|
|
72
96
|
this.now = opts.now ?? (() => Date.now());
|
|
73
97
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
mutate(fn, what) {
|
|
99
|
+
let result;
|
|
100
|
+
let produced = false;
|
|
101
|
+
let mutation;
|
|
102
|
+
try {
|
|
103
|
+
mutation = mutateSchedulerStore(this.storePath, (records) => {
|
|
104
|
+
const r = fn(records);
|
|
105
|
+
result = r.result;
|
|
106
|
+
produced = true;
|
|
107
|
+
return r.next;
|
|
108
|
+
}, { nowMs: this.now() });
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
return err(new SchedulerError("io", `failed to ${what}`, e));
|
|
112
|
+
}
|
|
113
|
+
logSchedulerWarnings(this.log, this.storePath, mutation.warnings);
|
|
114
|
+
if (mutation.ok || mutation.reason === "aborted")
|
|
115
|
+
return produced ? ok(result) : err(new SchedulerError("io", `failed to ${what}`));
|
|
116
|
+
switch (mutation.reason) {
|
|
117
|
+
case "unreadable_file":
|
|
118
|
+
return err(new SchedulerError("io", `failed to ${what}: the scheduled-task store file is not a JSON array — nothing was written over it`));
|
|
119
|
+
case "conflict_exhausted":
|
|
120
|
+
return err(new SchedulerError("io", `failed to ${what}: concurrent writers kept changing the store — nothing was written`));
|
|
121
|
+
default: {
|
|
122
|
+
const unhandled = mutation;
|
|
123
|
+
throw new Error(`scheduler store: unknown mutation outcome ${JSON.stringify(unhandled)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
79
126
|
}
|
|
80
127
|
async schedule(intent, ctx) {
|
|
81
128
|
const nowMs = this.now();
|
|
@@ -88,60 +135,56 @@ export class FileSchedulerBackend {
|
|
|
88
135
|
if (intent.lifetime === "session" && this.schedulerCapabilities.supportsSessionLifetime === false) {
|
|
89
136
|
return err(new SchedulerError("invalid_schedule", 'lifetime:"session" is not supported on this deployment (no resident daemon reaps session-lifetime records — the row would be an orphan)'));
|
|
90
137
|
}
|
|
91
|
-
const records = this.load();
|
|
92
138
|
const key = dedupKey(ctx.scope, intent, ctx.sessionId);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
records
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.save(records);
|
|
122
|
-
}
|
|
123
|
-
catch (e) {
|
|
124
|
-
return err(new SchedulerError("io", "failed to persist scheduled task", e));
|
|
125
|
-
}
|
|
126
|
-
return ok({ id });
|
|
139
|
+
const decided = this.mutate((records) => {
|
|
140
|
+
const existingIdx = records.findIndex((r) => {
|
|
141
|
+
const rr = r;
|
|
142
|
+
return r.scope === ctx.scope && dedupKey(r.scope, { when: r.when, label: r.label, lifetime: rr.lifetime }, r.sessionId) === key;
|
|
143
|
+
});
|
|
144
|
+
const scopeCount = records.filter((r) => r.scope === ctx.scope).length;
|
|
145
|
+
if (existingIdx < 0 && scopeCount >= this.schedulerCapabilities.maxScheduledPerScope) {
|
|
146
|
+
return { next: null, result: err(new SchedulerError("limit_exceeded", `scope at cap (${this.schedulerCapabilities.maxScheduledPerScope})`)) };
|
|
147
|
+
}
|
|
148
|
+
const id = (existingIdx >= 0 ? records[existingIdx].id : randomBytes(12).toString("base64url"));
|
|
149
|
+
const rec = {
|
|
150
|
+
id,
|
|
151
|
+
scope: ctx.scope,
|
|
152
|
+
...(ctx.principal ? { principal: ctx.principal } : {}),
|
|
153
|
+
...(ctx.sessionId ? { sessionId: ctx.sessionId } : {}),
|
|
154
|
+
prompt: intent.prompt,
|
|
155
|
+
when: intent.when,
|
|
156
|
+
...(intent.label ? { label: intent.label } : {}),
|
|
157
|
+
...(intent.mode ? { mode: intent.mode } : {}),
|
|
158
|
+
...(intent.lifetime ? { lifetime: intent.lifetime } : {}),
|
|
159
|
+
...(intent.recurring !== undefined ? { recurring: intent.recurring } : {}),
|
|
160
|
+
createdMs: nowMs,
|
|
161
|
+
...(computeNextRunMs(intent.when, nowMs) !== undefined ? { nextRunMs: computeNextRunMs(intent.when, nowMs) } : {}),
|
|
162
|
+
};
|
|
163
|
+
const next = existingIdx >= 0 ? records.map((r, i) => (i === existingIdx ? rec : r)) : [...records, rec];
|
|
164
|
+
return { next, result: ok({ id }) };
|
|
165
|
+
}, "persist scheduled task");
|
|
166
|
+
return decided.ok ? decided.value : decided;
|
|
127
167
|
}
|
|
128
168
|
async cancel(id, ctx) {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
const decided = this.mutate((records) => {
|
|
170
|
+
const idx = records.findIndex((r) => r.id === id);
|
|
171
|
+
if (idx < 0 || records[idx].scope !== ctx.scope) {
|
|
172
|
+
return { next: null, result: err(new SchedulerError("not_found", `no scheduled task ${id} in your scope (wrong id, already cancelled, or not yours)`)) };
|
|
173
|
+
}
|
|
174
|
+
return { next: records.filter((_, i) => i !== idx), result: ok(undefined) };
|
|
175
|
+
}, "persist cancellation");
|
|
176
|
+
return decided.ok ? decided.value : decided;
|
|
177
|
+
}
|
|
178
|
+
async list(ctx) {
|
|
179
|
+
let read;
|
|
135
180
|
try {
|
|
136
|
-
this.
|
|
181
|
+
read = loadSchedulerStore(this.storePath, { nowMs: this.now() });
|
|
137
182
|
}
|
|
138
183
|
catch (e) {
|
|
139
|
-
return err(new SchedulerError("io", "failed to
|
|
184
|
+
return err(new SchedulerError("io", "failed to read the scheduled-task store", e));
|
|
140
185
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
async list(ctx) {
|
|
144
|
-
const summaries = this.load()
|
|
186
|
+
logSchedulerWarnings(this.log, this.storePath, read.warnings);
|
|
187
|
+
const summaries = read.records
|
|
145
188
|
.filter((r) => r.scope === ctx.scope)
|
|
146
189
|
.map((r) => ({
|
|
147
190
|
id: r.id,
|
|
@@ -242,12 +242,12 @@ class LocalBackend {
|
|
|
242
242
|
throw new Error(`DB_BACKEND=local cannot open data dir ${root}: ${e instanceof Error ? e.message : String(e)}` +
|
|
243
243
|
(tail === undefined ? "" : ` ${tail}`));
|
|
244
244
|
}
|
|
245
|
-
this.sessionStore = new LocalSessionStore(createLocalSessionRepo(this.fileBackend.root), this.fileBackend.root, { log: createLogger(config.logLevel) });
|
|
246
245
|
const corruptReadWarn = (msg, fields) => this.discloseWarn(msg, fields);
|
|
247
246
|
this.runStore = new FileRunStore(this.fileBackend.root, {
|
|
248
247
|
error: (msg, fields) => void (this.onError ?? this.onWarn)?.(msg, fields),
|
|
249
248
|
strictHydrate: config.runStoreStrictHydrate,
|
|
250
249
|
});
|
|
250
|
+
this.sessionStore = new LocalSessionStore(createLocalSessionRepo(this.fileBackend.root), this.runStore, this.fileBackend.root, { log: createLogger(config.logLevel) });
|
|
251
251
|
this.resumeAnchorStore = new FileResumeAnchorStore(this.fileBackend.root);
|
|
252
252
|
this.approvalExemptionStore = new FileApprovalExemptionStore(this.fileBackend.root, { warn: corruptReadWarn });
|
|
253
253
|
this.approvalNonceStore = new FileApprovalNonceStore(this.fileBackend.root, { warn: corruptReadWarn });
|
|
@@ -216,7 +216,7 @@ export interface RunRecord {
|
|
|
216
216
|
* 被取消了没有」看终局码;判「一条还在跑的 run 上有没有人按过取消」才看这一位。 */
|
|
217
217
|
cancelRequested: boolean;
|
|
218
218
|
/** First ~120 chars of the objective (secret-redacted at write) — the list's "what is this task" glance.
|
|
219
|
-
* null
|
|
219
|
+
* 一个事实位:`""` = 建过预览、剥完没有用户正文(只有注入块 / 空 objective);null = 没记录(更早版本建的行 / 列前的行)。 */
|
|
220
220
|
objectivePreview: string | null;
|
|
221
221
|
createdAt: string;
|
|
222
222
|
updatedAt: string;
|
|
@@ -337,9 +337,8 @@ export declare function isParkedRunStatus(status: RunRecord["status"]): boolean;
|
|
|
337
337
|
* 两个不同的忙闲。
|
|
338
338
|
*
|
|
339
339
|
* 🔴 **未知词 / 无读数 ⇒ 词缺席(`undefined`),不是 `idle`**(诚实缺席,不是静默兜底):
|
|
340
|
-
* · `lastStatus === ""`
|
|
341
|
-
*
|
|
342
|
-
* 两者都不是「我知道它闲着」,所以都不许铸出 `idle` 这个断言;
|
|
340
|
+
* · `lastStatus === ""` 的真来源:这条会话**还没有任何 run**(SQL 双生与 local 会话店同;local 会话店的 run 店是必填位,没有「说不出 run 状态」的装配形)。
|
|
341
|
+
* 它不是「我知道它闲着」,所以不许铸出 `idle` 这个断言;
|
|
343
342
|
* · 读不懂的词(滚动升级里新副本写入的新状态词经裸 cast 读到本进程)同判 —— 与
|
|
344
343
|
* {@link isTerminalRunStatus} 族的保守方向一致。
|
|
345
344
|
* · **缺席怎么渲染是 core 的辖域,本仓一个字都不复述**(S-514-JU:此处原写着「core 对缺席渲染成 `idle`」
|
|
@@ -407,6 +406,23 @@ export interface SessionSummary {
|
|
|
407
406
|
* titled OR the lister face has no session_meta (the legacy task_run aggregate / memory twins — honest degrade). */
|
|
408
407
|
title: string | null;
|
|
409
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* S-1371 —— 会话**枚举面**读 run 账里「每会话的 run 投影」的窄读面:`runCount` / `lastStatus` / `lastRunId` / `lastTaskRunId` 四位,
|
|
411
|
+
* 同一次读、同一条「最新一条」的口径。
|
|
412
|
+
*
|
|
413
|
+
* 为什么是 run 店自己的 `listSessions`、不另造一只读口:这四位在 SQL 双生里就是 `task_run` 的 `rn=1` JOIN(计数、最新一条的状态与 task_id、
|
|
414
|
+
* 最新一条 `result.runId`),在文件 / 内存 run 店里就是 `listSessions` 的同口径聚合(`created_at DESC, task_id DESC` 取最新一条、数全部)——
|
|
415
|
+
* 它们本来就是**同一个问题的同一份答案**。local 会话店(会话身份来自会话仓、不是 run 账)读这一只,一行里就不会出现两个来源各答一遍
|
|
416
|
+
* 「最新一条是谁」(此前 `runCount` / `lastStatus` 读 run 店、`lastRunId` / `lastTaskRunId` 读进程内座:重启后同一行「有终态词、`lastRunId` 为 null」);
|
|
417
|
+
* 不另造的另一半理由:再造一只 `listBySession` 就要在三个孪生里各落一遍并重新证一遍同口径。
|
|
418
|
+
*
|
|
419
|
+
* 只收四位:`Pick` 把读面钉死在它真用的键上(其余键 —— 预览 / 属主 / 时刻 —— 在会话仓那边另有来源,不许从这里混读)。
|
|
420
|
+
*/
|
|
421
|
+
export interface SessionRunRollupSource {
|
|
422
|
+
listSessions(opts: {
|
|
423
|
+
limit: number;
|
|
424
|
+
}): Promise<ReadonlyArray<Pick<SessionSummary, "sessionId" | "runCount" | "lastStatus" | "lastRunId" | "lastTaskRunId">>>;
|
|
425
|
+
}
|
|
410
426
|
/**
|
|
411
427
|
* S-297 —— 「这个会话**最近一条腿**是哪一条 run」的窄读面(`latestRunForSession` 的回值)。
|
|
412
428
|
*
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* 换的时候要核对的等价点:本文件的 `normalize()` 统一形状 + `max` 截断 +
|
|
41
41
|
* `setWebSearchBadPayloadObserver` 观测器,core 的 adapter 都没有,得在外层补回来。
|
|
42
42
|
*
|
|
43
|
-
* ③ **四级优先级语义未定**(env /
|
|
43
|
+
* ③ **四级优先级语义未定**(env / web-admin / per-scenario / per-request 谁压谁)。
|
|
44
44
|
* 🔴 三问里「谁被伤」这条决定了默认值:一个**只配了模型 key** 的既有部署,若新语义让某一级
|
|
45
45
|
* 能自行打开搜索,它就在升级后**凭空长出一条出网工具** —— 那是安全姿态变更,不是特性。
|
|
46
46
|
* ⇒ 语义定稿前,任何新的开启通道**默认 OFF**;开启必须是部署方的显式动作。
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { validateHeaderValue } from "node:http";
|
|
2
2
|
import { createSearxngSearchBackend } from "@sema-agent/core";
|
|
3
|
-
import { isHierarchicalUrl
|
|
3
|
+
import { isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
4
|
+
import { redactSecrets } from "../trace/redact.js";
|
|
4
5
|
export const WEB_SEARCH_PROVIDERS = ["brave", "tavily", "searxng"];
|
|
5
6
|
function isWebSearchProvider(word) {
|
|
6
7
|
return WEB_SEARCH_PROVIDERS.includes(word);
|
package/dist/posture-source.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* 得到一句谁都不认的空话,或者更坏:一句**对某些键是假的**话。
|
|
20
20
|
*
|
|
21
21
|
* 🔴 `centerMeaning` 是 G-7(test [ref])的修复面,成因是一次真的过期断言:这一臂原本硬编码
|
|
22
|
-
* 「applied from the
|
|
22
|
+
* 「applied from the web-admin (**restart-to-apply**)」,而 S-174(core 7.10.0 [ref])之后
|
|
23
23
|
* `readFace.face` 是**热换席**(`Runner.swapDeps({readFace})`,下一拍 refresh 当场换进全部 Runner,
|
|
24
24
|
* `read-face` 重启片也不再为它签理由)——运维照那句话去滚动重启一片 fleet,重启的是一个已经生效的变更。
|
|
25
25
|
* 生效时机是**键**的事实,不是四词表的属性,所以它跟 `postureMeaning` 一样归键。
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
*
|
|
12
12
|
* 于是本模块把「档位」与「档位是谁定的」拆成两个字段:值仍是 `open | roots | 缺席`,来源是本文件的
|
|
13
13
|
* **四词闭集**。来源不是装饰:
|
|
14
|
-
* ① `
|
|
14
|
+
* ① `web-admin-client/read-face.ts` 的 **env 恒赢**判据此前用的是「`config.readFace` 键在不在场」这个
|
|
15
15
|
* **代理**;posture 会铸键,代理当场失真(center 的 face 会被一台从没设过 env 的机器挡掉)。
|
|
16
16
|
* 换成读来源之后判据说的就是它真正想说的那件事:**这台机器的 env 显式占住了这个键吗**。
|
|
17
17
|
* ② 运维面:`GET /v1/diagnostics/wiring` 报 `{face, source, note}`,「为什么这台机器是 open」有答案
|
|
18
18
|
* (裁定的补偿项:升级即放开,得让运维看得见并知道怎么钉回)。
|
|
19
19
|
*
|
|
20
20
|
* ## 优先序(三态,与 `config.ts` 的 `postureOn` 同形)
|
|
21
|
-
* `env 显式` > `center 发布` > `posture 派生` > `引擎默认`。env 恒赢的论证逐字见 `
|
|
21
|
+
* `env 显式` > `center 发布` > `posture 派生` > `引擎默认`。env 恒赢的论证逐字见 `web-admin-client/read-face.ts`
|
|
22
22
|
* 头注①(READ 容纳面描述的是**这台机器的磁盘上有什么**,组织的下发面不知道)。
|
|
23
23
|
*
|
|
24
24
|
* ## 定界
|
|
@@ -54,7 +54,7 @@ export declare function readFaceForPosture(singleUserTurnkey: boolean): ReadFace
|
|
|
54
54
|
*
|
|
55
55
|
* 🔴 `center` 那句话的事实来源是 S-174(core 7.10.0 [ref]):`RunnerDeps.readFace` 进了 core 的可换席
|
|
56
56
|
* 闭集 `SWAPPABLE_DEP_SEATS` ⇒ 组织发布/撤回一个档位,**下一拍 refresh 当场换进全部 Runner**,
|
|
57
|
-
* `
|
|
57
|
+
* `web-admin-client/restart-signal.ts` 的 `read-face` 片也不再为它签重启理由(判据同源 =
|
|
58
58
|
* `CENTER_READ_FACE_LIVE_KEYS`,由 core 的 `isSwappableDepSeat` 派生)。本句此前逐字写着
|
|
59
59
|
* 「(restart-to-apply)」—— G-7 抓获的那条过期断言,方向恰好是「让运维为一个已生效的变更重启 fleet」。
|
|
60
60
|
* ⚠️ 只说 `face` 这一席:三个 deny 键仍是 boot-baked,那半场的话不在这条读面上(它们没有来源位)。 */
|
|
@@ -7,7 +7,7 @@ export function readFaceSourceNote(source) {
|
|
|
7
7
|
return postureSourceNote({
|
|
8
8
|
knob: "READ_FACE",
|
|
9
9
|
source,
|
|
10
|
-
centerMeaning: "published by the
|
|
10
|
+
centerMeaning: "published by the web-admin and applied HOT — the containment FACE is a swappable engine seat (core 7.10.0), so the next refresh (or POST /v1/admin/config/refresh) swaps it into every Runner with NO restart; set READ_FACE on this machine to take the key back",
|
|
11
11
|
postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set): reads outside the workspace roots are allowed by default — set READ_FACE=roots to pin the containing posture back",
|
|
12
12
|
});
|
|
13
13
|
}
|
package/dist/run-local.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEFAULT_MODEL_REF, resolveTaskModel } from "./model-select.js";
|
|
2
|
+
import { DEFAULT_AT_MENTION, DEFAULT_MODEL_REF, resolveTaskModel } from "./model-select.js";
|
|
3
3
|
import { defaultModelRefusal, modelUnavailableMessage } from "./model-entry-refusal.js";
|
|
4
4
|
import { createDurableAbsenceSeat } from "./observability/corrupt-read-seat.js";
|
|
5
5
|
import { outwardPlane } from "./terminal.js";
|
|
@@ -25,7 +25,8 @@ import { acquireLocalBackendWithWait } from "./plugins/store-lock-wait.js";
|
|
|
25
25
|
import { configLocalRoot, configProviderKind, createConfigProvider } from "./config-provider.js";
|
|
26
26
|
import { composeExecutionLane } from "./boot/execution-lane.js";
|
|
27
27
|
import { judgeConsolidationValve } from "./boot/memory-consolidation.js";
|
|
28
|
-
import {
|
|
28
|
+
import { logConfigDirMigration, migrateLocalConfigDirectory } from "./config-d-migration.js";
|
|
29
|
+
import { applyEffective, applyCenterReadFace, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./web-admin-client/facade.js";
|
|
29
30
|
import { hostExecutionEnvFactory } from "./plugins/remote-env-host.js";
|
|
30
31
|
import { executionLaneHomeDir, executionLaneOf } from "./execution-lane-caps.js";
|
|
31
32
|
import { makeLoadProjectMemory, makeProbeInstructionSources } from "./project-memory.js";
|
|
@@ -337,6 +338,8 @@ export async function runLocal(argv, deps = {}) {
|
|
|
337
338
|
return 2;
|
|
338
339
|
}
|
|
339
340
|
const configProvider = createConfigProvider(providerInput);
|
|
341
|
+
if (configProvider.kind === "local")
|
|
342
|
+
logConfigDirMigration(logger, await migrateLocalConfigDirectory(root));
|
|
340
343
|
let effective;
|
|
341
344
|
try {
|
|
342
345
|
const r = await configProvider.fetchEffective(undefined);
|
|
@@ -613,7 +616,7 @@ export async function runLocal(argv, deps = {}) {
|
|
|
613
616
|
}
|
|
614
617
|
throw err;
|
|
615
618
|
}
|
|
616
|
-
const mention = resolveTaskModel(undefined, args.objective, config.models,
|
|
619
|
+
const mention = resolveTaskModel(undefined, args.objective, config.models, DEFAULT_AT_MENTION, config.modelRefusals);
|
|
617
620
|
if (mention.unavailable !== undefined) {
|
|
618
621
|
const { requested, name, refusal } = mention.unavailable;
|
|
619
622
|
printErr(`cannot run: ${modelUnavailableMessage("@-mention", requested, name, refusal)}`);
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
*/
|
|
66
66
|
import type { RuntimeCaps } from "@sema-agent/core";
|
|
67
67
|
import type { EntitlementRuntimeCaps } from "@sema-agent/settings-schema";
|
|
68
|
-
import { fetchPrincipalCaps, type ExecutionRuling } from "./
|
|
69
|
-
import type { CenterEndpoint } from "./
|
|
68
|
+
import { fetchPrincipalCaps, type ExecutionRuling } from "./web-admin-client/facade.js";
|
|
69
|
+
import type { CenterEndpoint } from "./web-admin-client/center-request.js";
|
|
70
70
|
import type { ScenarioRuling } from "./capabilities/scenarios.js";
|
|
71
71
|
import type { ServiceConfig } from "./config-types.js";
|
|
72
72
|
import { type Logger } from "./observability/logger.js";
|
|
@@ -75,7 +75,7 @@ export declare function resetExecutionLaneDefaultTraceForTest(): void;
|
|
|
75
75
|
export interface EntitlementsResolverOpts {
|
|
76
76
|
/** S-651:中心端点(基址 + 凭证**来源**;每次抓取取一次值,不持有启动快照)。 */
|
|
77
77
|
center: CenterEndpoint;
|
|
78
|
-
/** This worker's name (`
|
|
78
|
+
/** This worker's name (`SEMA_WEB_ADMIN_WORKER`), passed as `?worker=` so a worker-scoped `wpt_<self>` token can
|
|
79
79
|
* resolve its OWN worker's principal caps (caps-only); harmless on a full token (the caps value
|
|
80
80
|
* is worker-independent). Omitted ⇒ no `?worker=` (a full-token, non-fleet deployment). */
|
|
81
81
|
worker?: string;
|
|
@@ -135,7 +135,7 @@ export declare function toCoreRuntimeCaps(ent: EntitlementRuntimeCaps | null | u
|
|
|
135
135
|
* (`RuntimeCaps.autoMode` 三态逐 principal 解,[ref] 起「缺席=不阻」),任何部署级布尔都对它说不了话——
|
|
136
136
|
* 所以本谓词为 false **不再**意味着 auto 不武装;「会不会武装」由 `auto-mode-face.ts` 的三项合取答。
|
|
137
137
|
*/
|
|
138
|
-
export declare function centerEntitlementSourceWired(config: Pick<ServiceConfig, "
|
|
138
|
+
export declare function centerEntitlementSourceWired(config: Pick<ServiceConfig, "webAdmin">): boolean;
|
|
139
139
|
/** The two per-principal faces served off ONE caps fetch + cache. */
|
|
140
140
|
export interface PrincipalEntitlementsClient {
|
|
141
141
|
/** core's `RunnerDeps.runtimeCapsResolver` seam — FAIL-CLOSED on error (deny workflows/fork/autoMode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchPrincipalCaps } from "./
|
|
1
|
+
import { fetchPrincipalCaps } from "./web-admin-client/facade.js";
|
|
2
2
|
import { createIdentityBins } from "./center-credential.js";
|
|
3
3
|
import { HttpError } from "./security.js";
|
|
4
4
|
import { createLogger } from "./observability/logger.js";
|
|
@@ -53,7 +53,7 @@ export function toCoreRuntimeCaps(ent) {
|
|
|
53
53
|
return Object.keys(caps).length ? caps : undefined;
|
|
54
54
|
}
|
|
55
55
|
export function centerEntitlementSourceWired(config) {
|
|
56
|
-
return config.
|
|
56
|
+
return config.webAdmin !== undefined && !config.webAdmin.dryRun;
|
|
57
57
|
}
|
|
58
58
|
export function gateExecutionLane(ruling, ownLane, logger = defaultLogger) {
|
|
59
59
|
if (!ruling) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime governance compiler — the "second baton" (center §10, core 1.114.0): turn the
|
|
3
|
-
* operator's declarative `
|
|
4
|
-
* carried to the service
|
|
3
|
+
* operator's declarative `governance.autonomy` + `governance.commandPolicy` (`@sema-agent/settings-schema`,
|
|
4
|
+
* carried to the service in the effective config's `governance` domain) into TIGHTEN-ONLY overrides on the
|
|
5
5
|
* per-task `TaskSpec`. Pure so it is unit-testable in isolation (the spec is otherwise assembled inside
|
|
6
6
|
* main.ts's `resolveSpec`).
|
|
7
7
|
*
|
|
@@ -28,7 +28,7 @@ import { GovernanceConfig } from "@sema-agent/settings-schema";
|
|
|
28
28
|
import { type GovernanceAskMarks } from "./governance-ask-marks.js";
|
|
29
29
|
import { type GovernanceOverrides } from "./governance-overrides.js";
|
|
30
30
|
/** 运维 autonomy 四档的**闭集**(同上)。`config.ts` 的 `parseAutonomy` 也从这里读 —— 四档词表全仓一份。 */
|
|
31
|
-
export declare const AUTONOMY_MODES:
|
|
31
|
+
export declare const AUTONOMY_MODES: ("plan" | "auto" | "ask" | "read-only")[];
|
|
32
32
|
/** A per-command governance rule (registry `runtime.commandPolicy[]`) —— **型也从契约派生**:
|
|
33
33
|
* 契约给 `command` 加一条 refine / 给 `decision` 加一个判词,本仓的型当场跟上(或在下面的
|
|
34
34
|
* `RANK` / 穷尽 switch 上编译红),而不是靠人记得把手写联合改一遍。 */
|
|
@@ -39,7 +39,7 @@ export type Autonomy = NonNullable<GovernanceConfig["autonomy"]>;
|
|
|
39
39
|
export declare const COARSE_SHELL_TOOLS_MIRROR: readonly string[];
|
|
40
40
|
/** Validate `commandPolicy` rules — the shape (array of {command, decision}), the `command` (a bare argv[0] name,
|
|
41
41
|
* see {@link VALID_COMMAND_NAME}), AND the `decision` enum. Returns the list of human-readable errors (empty =
|
|
42
|
-
* OK). Called at config-apply (
|
|
42
|
+
* OK). Called at config-apply (web-admin-client/facade.ts `applyRuntimeHot`) so a malformed rule that BYPASSED the
|
|
43
43
|
* registry schema (a hand-edited config.d / a non-conformant publish) is rejected LOUDLY rather than
|
|
44
44
|
* silently mis-compiling — review MEDIUM: an out-of-enum `decision` (e.g. "DENY") would otherwise fall through
|
|
45
45
|
* {@link compileCommandPolicyLayers}'s `decision==="deny"?…:"ask"` to ASK, silently WEAKENING an intended deny. */
|
|
@@ -176,7 +176,7 @@ export declare function autonomyKeepsOpenWriteFace(autonomy: Autonomy | undefine
|
|
|
176
176
|
* 是 always」。`commandPolicy` 不入判据:它产的是 `toolPolicy` 的 ask,不抬 shellGate 档,与本格无关。
|
|
177
177
|
*
|
|
178
178
|
* ⚠️ **已知残留(codex 交叉复审 2026-08-11 逮到,如实登记而不是掩盖)**:本判据读的是**当下**的
|
|
179
|
-
* config,而 `config.autonomy` 是**热改**的(`
|
|
179
|
+
* config,而 `config.autonomy` 是**热改**的(`web-admin-client/apply-effective.ts` 就地写 `config.autonomy`)。
|
|
180
180
|
* 于是失真是**双向**的,不是我原先写的「只往缺席方向」:
|
|
181
181
|
* · 缺席向(常见):mint 之后运维把 `AUTONOMY=ask` 撤了 ⇒ 老 park 行归不出治理出身,少标一个;
|
|
182
182
|
* · **在场向**(窄):一条**纯 SUP 路由**产的门(`ROUTER_ENABLED` 开 ∧ 路由判 supervisor ∧ 当时治理层
|
package/dist/sealed-key.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Logger } from "./observability/logger.js";
|
|
|
2
2
|
/** Algorithm tag — MUST equal registry-core's `SEALED_BOX_ALG` (v1 sole value; a different alg on
|
|
3
3
|
* the wire is a loud per-model rejection in applyEffective, never a guess). */
|
|
4
4
|
export declare const SEALED_KEY_ALG = "libsodium-sealedbox-x25519";
|
|
5
|
-
/** The subset applyEffective needs (keeps
|
|
5
|
+
/** The subset applyEffective needs (keeps web-admin decoupled from fs/boot concerns). */
|
|
6
6
|
export interface SealedKeyOpener {
|
|
7
7
|
has(publicKeyId: string): boolean;
|
|
8
8
|
/** All private-key ids this host holds (surfaces in the no-private-key warn for operator triage). */
|
|
@@ -62,17 +62,17 @@ export declare function deriveKeyId(publicKeyRaw: Uint8Array): string;
|
|
|
62
62
|
*/
|
|
63
63
|
export declare function ensureSealedKeyStore(dir: string, logger?: Logger): Promise<SealedKeyStore>;
|
|
64
64
|
/**
|
|
65
|
-
* Best-effort public-key registration with the
|
|
65
|
+
* Best-effort public-key registration with the web-admin (`POST /api/config/execution-keys`).
|
|
66
66
|
*
|
|
67
67
|
* HONESTY over fake automation: that endpoint is ADMIN-gated, and a fleet worker only holds
|
|
68
68
|
* SERVICE_PULL_TOKEN — so automatic registration works ONLY when the operator deliberately provides
|
|
69
|
-
* an admin token (
|
|
69
|
+
* an admin token (SEMA_WEB_ADMIN_ADMIN_TOKEN in this service's env; its older names are settings-schema tombstones). Without
|
|
70
70
|
* one we do the next-best REAL thing: print the FULL public key + id at boot (a public key is not a
|
|
71
71
|
* secret) with the exact registration instructions, and warn that the web "paste key" face stays
|
|
72
72
|
* dark for this host until an admin registers it. Never throws (fire-and-forget from boot).
|
|
73
73
|
*/
|
|
74
74
|
export declare function reportExecutionPublicKey(opts: {
|
|
75
|
-
/**
|
|
75
|
+
/** web-admin base URL (config.webAdmin.baseUrl). Absent = no center → manual print only. */
|
|
76
76
|
baseUrl?: string;
|
|
77
77
|
/** Admin bearer for the center's config write face. Absent = manual print (see above). */
|
|
78
78
|
adminToken?: string;
|
package/dist/sealed-key.js
CHANGED
|
@@ -4,7 +4,8 @@ import { join } from "node:path";
|
|
|
4
4
|
import _sodium from "libsodium-wrappers";
|
|
5
5
|
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
6
6
|
import { createCenterCredentialSource } from "./center-credential.js";
|
|
7
|
-
import { centerFetch } from "./
|
|
7
|
+
import { centerFetch } from "./web-admin-client/center-request.js";
|
|
8
|
+
import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
|
|
8
9
|
export const SEALED_KEY_ALG = "libsodium-sealedbox-x25519";
|
|
9
10
|
const SEAL_OVERHEAD_BYTES = 48;
|
|
10
11
|
const BASE64_STRICT = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
@@ -101,7 +102,7 @@ export async function reportExecutionPublicKey(opts) {
|
|
|
101
102
|
});
|
|
102
103
|
};
|
|
103
104
|
if (!baseUrl || !adminToken) {
|
|
104
|
-
manual(!baseUrl ?
|
|
105
|
+
manual(!baseUrl ? `no web-admin configured (${WEB_ADMIN_ENV.url} unset)` : `no admin token (${WEB_ADMIN_ENV.adminToken} unset; the worker's pull token cannot write execution keys)`);
|
|
105
106
|
return;
|
|
106
107
|
}
|
|
107
108
|
try {
|
package/dist/security.d.ts
CHANGED
|
@@ -17,9 +17,9 @@ export interface SessionListItem {
|
|
|
17
17
|
/** K-5c (shell §K): the latest run's task_id — the shell's one-hop live-tail re-attach anchor on resume. */
|
|
18
18
|
lastRunId: string | null;
|
|
19
19
|
/** [ref]([ref]/[ref]):the LATEST run's **engine run id**(`TaskResult.runId`)when known —— OMIT-when-absent,
|
|
20
|
-
* 恒不铸 null
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* 恒不铸 null。所有 lister(SQL 双生、local 会话店 —— 它的 run 店是必填位)从 latest run 的 `result` JSON 提取(终局与 park 时写、认领续跑即清空 —— S-173
|
|
21
|
+
* ⇒ 在跑 latest 键诚实缺席)。
|
|
22
|
+
* 读法一致:缺席 = 「此行无已知 engine runId」,禁读成其它含义。⚠️ 与 `lastRunId`(= latest run 的 **task_id**)
|
|
23
23
|
* 是两条身份轴,命名对齐 core `SessionStoreSummary.lastTaskRunId`。wire:additive 键零投影透传
|
|
24
24
|
* (sessions-list.ts);sema-sdk spec `SessionSummary` 闭集候 SDK 半场开键([ref]/.82 班车)。 */
|
|
25
25
|
lastTaskRunId?: string;
|