@sema-agent/server 7.130.0 → 7.132.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 +396 -0
- package/README.md +13 -6
- package/README.zh-CN.md +9 -5
- package/USAGE.md +187 -50
- package/dist/approval-card.d.ts +3 -1
- package/dist/approval-card.js +2 -3
- package/dist/audit.d.ts +7 -10
- package/dist/audit.js +4 -1
- package/dist/auth-bridge.d.ts +4 -0
- package/dist/auth-bridge.js +10 -11
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/deployment-mods.d.ts +31 -0
- package/dist/boot/deployment-mods.js +26 -0
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/execution-env.js +19 -6
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
- package/dist/boot/read-deny-anchor-audit.js +36 -0
- package/dist/boot/resolve-spec.d.ts +1 -5
- package/dist/boot/resolve-spec.js +56 -61
- package/dist/boot/runner-deps.d.ts +19 -8
- package/dist/boot/runner-deps.js +14 -2
- package/dist/boot/session-faces.d.ts +1 -7
- package/dist/boot/stage-02-web-admin-pull.js +9 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -4
- package/dist/boot/stage-05-execution-env.js +7 -10
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +5 -4
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +3 -3
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +3 -9
- package/dist/boot/stage-10-http-server.d.ts +3 -9
- package/dist/boot/stage-10-http-server.js +12 -10
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +5 -6
- package/dist/boot/web-admin.d.ts +0 -3
- package/dist/boot/web-admin.js +35 -35
- package/dist/brain.js +17 -22
- package/dist/budget.js +5 -4
- package/dist/caller-text-echo.d.ts +20 -0
- package/dist/caller-text-echo.js +24 -0
- package/dist/capabilities/brand-identity.d.ts +26 -0
- package/dist/capabilities/brand-identity.js +27 -0
- package/dist/capabilities/builtin-tools.js +2 -1
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +32 -59
- package/dist/capabilities/center-prompts.js +18 -32
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capabilities/engine-capabilities.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +3 -3
- package/dist/capabilities/host-tool-names.d.ts +17 -0
- package/dist/capabilities/host-tool-names.js +15 -0
- package/dist/capabilities/mcp-probe.d.ts +14 -1
- package/dist/capabilities/mcp-probe.js +22 -133
- package/dist/capabilities/repo-tools.js +4 -3
- package/dist/capabilities/scenarios.d.ts +42 -47
- package/dist/capabilities/scenarios.js +19 -35
- package/dist/capabilities/select-environment-tool.js +2 -1
- package/dist/capabilities/send-user-file-tool.d.ts +0 -1
- package/dist/capabilities/send-user-file-tool.js +2 -2
- package/dist/capabilities/skills.d.ts +19 -4
- package/dist/capabilities/skills.js +30 -10
- package/dist/capabilities/tool-defer.js +2 -1
- package/dist/config-catalog.js +22 -11
- package/dist/config-invariants.js +2 -2
- package/dist/config-lkg.d.ts +9 -9
- package/dist/config-lkg.js +20 -4
- package/dist/config-provider.d.ts +3 -1
- package/dist/config-provider.js +14 -6
- package/dist/config-types.d.ts +13 -9
- package/dist/config.d.ts +2 -2
- package/dist/config.js +34 -20
- package/dist/device-ws-protocol.d.ts +1 -1
- package/dist/device-ws-protocol.js +1 -0
- package/dist/elicitation.d.ts +7 -0
- package/dist/elicitation.js +20 -5
- package/dist/env-name-allowlist-knobs.d.ts +12 -2
- package/dist/env-name-allowlist-knobs.js +5 -4
- package/dist/exec-bounds.d.ts +15 -3
- package/dist/exec-bounds.js +4 -0
- package/dist/execution-lane-caps.d.ts +9 -2
- package/dist/execution-lane-caps.js +11 -0
- package/dist/hooks/classic-stations.d.ts +86 -0
- package/dist/hooks/classic-stations.js +27 -0
- package/dist/hooks/hook-llm.d.ts +8 -5
- package/dist/hooks/hook-llm.js +80 -37
- package/dist/hooks/hook-runner.d.ts +273 -75
- package/dist/hooks/hook-runner.js +293 -317
- package/dist/http/admission.d.ts +8 -2
- package/dist/http/admission.js +103 -59
- package/dist/http/approval-sweeps.js +59 -18
- package/dist/http/checkpoint-ctx.d.ts +36 -18
- package/dist/http/checkpoint-ctx.js +14 -10
- package/dist/http/claimed-run.d.ts +13 -0
- package/dist/http/claimed-run.js +15 -0
- package/dist/http/device-placement.js +5 -10
- package/dist/http/dispatch.d.ts +0 -4
- package/dist/http/dispatch.js +6 -10
- package/dist/http/durable-run-leg.d.ts +2 -1
- package/dist/http/durable-run-leg.js +5 -3
- package/dist/http/health-self-report.d.ts +7 -5
- package/dist/http/health-self-report.js +4 -18
- package/dist/http/principal-gate.d.ts +23 -6
- package/dist/http/principal-gate.js +24 -5
- package/dist/http/refusal-body.d.ts +21 -0
- package/dist/http/refusal-body.js +4 -0
- package/dist/http/request-guards.d.ts +27 -0
- package/dist/http/request-guards.js +24 -3
- package/dist/http/resume-legs.d.ts +25 -0
- package/dist/http/resume-legs.js +48 -26
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/capabilities.js +7 -17
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/rules.js +2 -2
- package/dist/http/routes/runs.js +13 -17
- package/dist/http/routes/session-sync.js +4 -2
- package/dist/http/routes/sessions.js +23 -11
- package/dist/http/routes/shared-memory.js +2 -13
- package/dist/http/routes/side-query.js +2 -3
- package/dist/http/routes/tasks.js +34 -19
- package/dist/http/scenario-read-face.d.ts +14 -0
- package/dist/http/scenario-read-face.js +5 -0
- package/dist/http/send.d.ts +13 -1
- package/dist/http/send.js +12 -1
- package/dist/http/server.d.ts +37 -23
- package/dist/http/server.js +30 -4
- package/dist/http/stored-body-replay.d.ts +10 -3
- package/dist/http/stored-body-replay.js +27 -6
- package/dist/http/submit-ctx.d.ts +25 -0
- package/dist/http/submit-ctx.js +39 -0
- package/dist/http/wire-types.d.ts +18 -5
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/lane-home-probe.d.ts +43 -0
- package/dist/lane-home-probe.js +35 -0
- package/dist/limit-refusal.d.ts +47 -0
- package/dist/limit-refusal.js +12 -0
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +3 -11
- package/dist/mcp-assertion-header.js +3 -9
- package/dist/mcp-tool-faces.d.ts +5 -0
- package/dist/mcp-tool-faces.js +5 -1
- package/dist/memory-scope.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +1 -7
- package/dist/model-provider.d.ts +39 -12
- package/dist/model-provider.js +19 -2
- package/dist/model-route-endpoint.d.ts +52 -12
- package/dist/model-route-endpoint.js +48 -26
- package/dist/model-route-gap.d.ts +5 -4
- package/dist/model-route-gap.js +1 -1
- package/dist/model-select.d.ts +58 -0
- package/dist/model-select.js +31 -0
- package/dist/namespaced-rule-name.d.ts +7 -4
- package/dist/namespaced-rule-name.js +4 -3
- package/dist/observability/cost-taxonomy.d.ts +7 -7
- package/dist/observability/fail-open.d.ts +53 -12
- package/dist/observability/fail-open.js +47 -7
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/prompt-manifest.d.ts +47 -82
- package/dist/observability/prompt-manifest.js +119 -17
- package/dist/observability/run-terminal.js +14 -6
- package/dist/observability/secret-env-scrub.d.ts +13 -1
- package/dist/observability/secret-env-scrub.js +13 -1
- package/dist/observability/tool-trace.d.ts +16 -21
- package/dist/observability/tool-trace.js +51 -42
- package/dist/per-task-image.d.ts +7 -1
- package/dist/per-task-image.js +3 -3
- package/dist/plain-string-record.d.ts +25 -0
- package/dist/plain-string-record.js +32 -0
- package/dist/plugins/blob-backend.d.ts +68 -13
- package/dist/plugins/blob-backend.js +53 -10
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
- package/dist/plugins/checkpoint-store-sql.js +39 -26
- package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
- package/dist/plugins/k8s-bg-scripts.js +22 -0
- package/dist/plugins/local-checkpoint-store.js +5 -2
- package/dist/plugins/local-session-store.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +147 -91
- package/dist/plugins/one-time-migrations.js +372 -208
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
- package/dist/plugins/permission-rule-store-sql.js +4 -18
- package/dist/plugins/pg-session-storage.d.ts +4 -5
- package/dist/plugins/pg-session-storage.js +7 -17
- package/dist/plugins/remote-env-adb.d.ts +6 -0
- package/dist/plugins/remote-env-adb.js +9 -4
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +1 -1
- package/dist/plugins/remote-env-e2b.js +21 -9
- package/dist/plugins/remote-env-host.d.ts +9 -9
- package/dist/plugins/remote-env-host.js +10 -32
- package/dist/plugins/remote-env-k8s.d.ts +95 -26
- package/dist/plugins/remote-env-k8s.js +265 -90
- package/dist/plugins/remote-env-local-docker.d.ts +20 -1
- package/dist/plugins/remote-env-local-docker.js +77 -9
- package/dist/plugins/remote-env-ssh.d.ts +15 -0
- package/dist/plugins/remote-env-ssh.js +109 -3
- package/dist/plugins/remote-shell.d.ts +0 -1
- package/dist/plugins/remote-shell.js +1 -2
- package/dist/plugins/retention-store-sql.js +6 -2
- package/dist/plugins/schema-consistency.d.ts +54 -0
- package/dist/plugins/schema-consistency.js +52 -0
- package/dist/plugins/session-epoch-anchor.d.ts +13 -0
- package/dist/plugins/session-epoch-anchor.js +23 -0
- package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
- package/dist/plugins/shared-memory-store-sql.js +5 -16
- package/dist/plugins/sql-driver.d.ts +141 -0
- package/dist/plugins/sql-driver.js +115 -1
- package/dist/plugins/sql-errors.d.ts +41 -0
- package/dist/plugins/sql-errors.js +36 -0
- package/dist/plugins/store-backend.js +2 -1
- package/dist/plugins/tidb-session-storage.d.ts +7 -11
- package/dist/plugins/tidb-session-storage.js +6 -16
- package/dist/plugins/tidb-session-store.js +1 -1
- package/dist/prompts-domain-validate.d.ts +17 -61
- package/dist/prompts-domain-validate.js +9 -119
- package/dist/question.d.ts +18 -9
- package/dist/question.js +21 -13
- package/dist/request-key-closure.d.ts +4 -4
- package/dist/request-key-closure.js +18 -13
- package/dist/rule-entry-list.d.ts +134 -24
- package/dist/rule-entry-list.js +162 -30
- package/dist/run-local.d.ts +3 -3
- package/dist/run-local.js +26 -25
- package/dist/runs.d.ts +48 -2
- package/dist/runs.js +61 -2
- package/dist/server-secret-env.d.ts +44 -18
- package/dist/server-secret-env.js +49 -8
- package/dist/session-incarnation.d.ts +15 -5
- package/dist/session-incarnation.js +16 -9
- package/dist/session-titler.js +12 -2
- package/dist/spec-fields.d.ts +34 -18
- package/dist/spec-fields.js +46 -53
- package/dist/sql-engine-posture.d.ts +29 -4
- package/dist/sql-engine-posture.js +36 -1
- package/dist/stored-request-hooks-m20.d.ts +23 -0
- package/dist/stored-request-hooks-m20.js +124 -0
- package/dist/stored-request-tool-axes-m19.d.ts +20 -5
- package/dist/stored-request-tool-axes-m19.js +30 -2
- package/dist/task-a2a.d.ts +9 -1
- package/dist/task-a2a.js +73 -47
- package/dist/task-mcp.d.ts +54 -102
- package/dist/task-mcp.js +194 -92
- package/dist/task-settings.d.ts +47 -25
- package/dist/task-settings.js +46 -61
- package/dist/text-bidi.d.ts +3 -46
- package/dist/text-bidi.js +3 -3
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +24 -4
- package/dist/trace/engine-notice-wire.d.ts +11 -3
- package/dist/trace/engine-notice-wire.js +8 -1
- package/dist/trace/ledger-events.d.ts +3 -1
- package/dist/trace/ledger-sink.d.ts +29 -0
- package/dist/trace/ledger-sink.js +15 -1
- package/dist/trace/model-segment.js +1 -1
- package/dist/trace/project.d.ts +25 -6
- package/dist/trace/project.js +68 -22
- package/dist/trace/wire-projection-faces.d.ts +80 -2
- package/dist/trace/wire-projection-faces.js +23 -1
- package/dist/user-authored-text.d.ts +7 -0
- package/dist/user-authored-text.js +3 -0
- package/dist/web-admin-client/apply-effective.js +10 -5
- package/dist/web-admin-client/candidate-gate.d.ts +6 -6
- package/dist/web-admin-client/candidate-gate.js +3 -3
- package/dist/web-admin-client/http-client.js +0 -6
- package/dist/web-admin-client/read-warnings.d.ts +13 -0
- package/dist/web-admin-client/read-warnings.js +10 -0
- package/dist/web-admin-client/skills-mcp.d.ts +3 -0
- package/dist/web-admin-client/skills-mcp.js +9 -6
- package/dist/web-admin-client/types.d.ts +3 -4
- package/dist/workspace-cache.d.ts +35 -0
- package/dist/workspace-cache.js +36 -0
- package/dist/workspace-space.d.ts +3 -2
- package/dist/workspace-space.js +2 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
- package/package.json +4 -4
- package/scripts/drain-check-local.mjs +37 -3
- package/scripts/drain-check.mysql.sql +30 -2
- package/scripts/drain-check.pg.sql +29 -2
- package/dist/capabilities/memory-notice.d.ts +0 -138
- package/dist/capabilities/memory-notice.js +0 -64
- package/dist/capabilities/prompt.d.ts +0 -17
- package/dist/capabilities/prompt.js +0 -6
- package/dist/mcp-content-origin.d.ts +0 -34
- package/dist/mcp-content-origin.js +0 -10
- package/dist/tool-axis-words.d.ts +0 -27
- package/dist/tool-axis-words.js +0 -9
- package/dist/wire-name-bound.d.ts +0 -14
- package/dist/wire-name-bound.js +0 -13
|
@@ -24,9 +24,34 @@
|
|
|
24
24
|
* 不在这里 —— 在 `sql-driver.ts` 的连接初始化里(TiDB 乐观模式设不成悲观 / 隔离级复核不符 ⇒ 响亮抛)。
|
|
25
25
|
* 本模块在 boot 期借一条连接**触发**那次初始化,于是那句拒绝落在启动期而不是第一次业务写上。
|
|
26
26
|
*/
|
|
27
|
-
import { type SqlEngineFacts } from "./plugins/sql-driver.js";
|
|
27
|
+
import { type SqlEngineFacts, type SqlEngineReading } from "./plugins/sql-driver.js";
|
|
28
|
+
import type { StoreBackend } from "./plugins/store-backend.js";
|
|
28
29
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
29
30
|
import type { Pool as PgPool } from "pg";
|
|
31
|
+
/**
|
|
32
|
+
* 一只店后端交出的 SQL 池(至多一只:`mysqlPool()` 只在 MySQL 协议后端上有值,`pgPool()` 只在 PG 后端上;local 后端 / 没有后端 ⇒ 两个都 `undefined`)。
|
|
33
|
+
* **「SQL 后端在场」只有这一个谓词**:boot 期据它建立引擎读口({@link establishSqlEnginePosture}),`createHttpServer` 据它判「读口必填」——
|
|
34
|
+
* 两处读同一只函数,所以本服务自己的装配(有池 ⇒ 有读口)恒满足那道起服门,嵌入方少给读口则被它拒。
|
|
35
|
+
*/
|
|
36
|
+
export declare function sqlPoolsOf(backend: Pick<StoreBackend, "mysqlPool" | "pgPool"> | undefined): {
|
|
37
|
+
mysqlPool: MySqlPool | undefined;
|
|
38
|
+
pgPool: PgPool | undefined;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* `ServiceDeps.sqlEngine` 座的**值形**判:`{ dialect: "tidb" | "pg", facts }`,`facts` 是 `null`(池上连接初始化还没成功)或一份引擎事实 —— 判到墙判官 `sqlWallsOf` 读的那几位
|
|
42
|
+
* (`maxRowBytes` / `maxPacketBytes` 是 `null` 或数,`wallSources` 两位是出处词)。一个函数(7.131.x 的事实读口 `() => facts` 只改了键名)、缺 `facts` 的对象、错方言词都不是它。
|
|
43
|
+
* `facts` 是活读(getter):这里读一次,判的是装配这一刻看得到的那一份。
|
|
44
|
+
*/
|
|
45
|
+
export declare function isSqlEngineReading(v: unknown): v is SqlEngineReading;
|
|
46
|
+
/** 引擎读口座的装配问题(闭集;{@link sqlEngineSeatIssue} 的产物):`required` = SQL 后端在场而座缺席;`bad_shape` = 座在场但形不对。 */
|
|
47
|
+
export type SqlEngineSeatIssue = "required" | "bad_shape";
|
|
48
|
+
/**
|
|
49
|
+
* 🔴 **引擎读口座的装配判官**(`createHttpServer` 起服时调;两臂一只函数):
|
|
50
|
+
* · 座在场 ⇒ 判值形({@link isSqlEngineReading}),不对 ⇒ `bad_shape`;
|
|
51
|
+
* · 座缺席 ⇒ 看后端交没交出 SQL 池({@link sqlPoolsOf},与 boot 期建立读口同一个谓词):交了 ⇒ `required`(席缺席只表示「没有 SQL 后端」,不能被判官读成「没有墙」);没交 ⇒ 过。
|
|
52
|
+
* 都是 fail-closed:墙门只认这一席,席坏了或漏了,代价是每份超出库墙的体照样受理或每次提交 500,不是起服时一句话。
|
|
53
|
+
*/
|
|
54
|
+
export declare function sqlEngineSeatIssue(backend: Pick<StoreBackend, "mysqlPool" | "pgPool"> | undefined, sqlEngine: unknown): SqlEngineSeatIssue | undefined;
|
|
30
55
|
/** 消费端窄投影(`GET /v1/capabilities` 的 `store.sql` 位;7.106.0 前是顶层 `sql`,S-866 ①)。`null` = 驱动此刻给不出事实 ⇒ `store.sql` 缺席。 */
|
|
31
56
|
export interface SqlEngineCapability {
|
|
32
57
|
engine: SqlEngineFacts["engine"];
|
|
@@ -34,8 +59,8 @@ export interface SqlEngineCapability {
|
|
|
34
59
|
txnMode: SqlEngineFacts["txnMode"];
|
|
35
60
|
}
|
|
36
61
|
/**
|
|
37
|
-
* Boot 期建立引擎事实:借一条池连接(初始化随之跑)
|
|
38
|
-
*
|
|
62
|
+
* Boot 期建立引擎事实:借一条池连接(初始化随之跑),再原样归还。返回本池的**引擎读口**(驱动本身:方言 + 活的 `facts`)—— 事实由
|
|
63
|
+
* 驱动持有,重连后它会被下一次初始化刷新,快照会说谎;方言与事实同出这一只,写入要装进的墙(`sqlWallsOf`)只读它。
|
|
39
64
|
*
|
|
40
65
|
* 抛出 = 拒启:连接初始化拒绝了这个后端(TiDB 乐观模式设不成悲观、隔离级复核不符、服务器太旧读不出
|
|
41
66
|
* `@@transaction_isolation`)。这条错**刻意不吞** —— 本仓所有 `FOR UPDATE` 判据都以它为前提。
|
|
@@ -43,7 +68,7 @@ export interface SqlEngineCapability {
|
|
|
43
68
|
export declare function establishSqlEnginePosture(pools: {
|
|
44
69
|
mysqlPool?: MySqlPool | undefined;
|
|
45
70
|
pgPool?: PgPool | undefined;
|
|
46
|
-
}): Promise<
|
|
71
|
+
}): Promise<SqlEngineReading | undefined>;
|
|
47
72
|
/** operator 面 → 消费端面的窄投影(`version` 是运维料,刻意不进能力位:它只对排障有意义)。 */
|
|
48
73
|
export declare function projectSqlEngineCapability(facts: SqlEngineFacts | null | undefined): SqlEngineCapability | null;
|
|
49
74
|
//# sourceMappingURL=sql-engine-posture.d.ts.map
|
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
import { mysqlDriver, pgDriver } from "./plugins/sql-driver.js";
|
|
2
|
+
export function sqlPoolsOf(backend) {
|
|
3
|
+
return { mysqlPool: backend?.mysqlPool?.(), pgPool: backend?.pgPool?.() };
|
|
4
|
+
}
|
|
5
|
+
const DIALECT_WORDS = { tidb: true, pg: true };
|
|
6
|
+
const WALL_SOURCE_WORDS = { reported: true, assumed: true };
|
|
7
|
+
const isWallBytes = (v) => v === null || (typeof v === "number" && Number.isFinite(v) && v >= 0);
|
|
8
|
+
const isWallSourceWord = (v) => typeof v === "string" && Object.hasOwn(WALL_SOURCE_WORDS, v);
|
|
9
|
+
export function isSqlEngineReading(v) {
|
|
10
|
+
if (typeof v !== "object" || v === null)
|
|
11
|
+
return false;
|
|
12
|
+
const r = v;
|
|
13
|
+
if (typeof r.dialect !== "string" || !Object.hasOwn(DIALECT_WORDS, r.dialect))
|
|
14
|
+
return false;
|
|
15
|
+
if (!("facts" in v))
|
|
16
|
+
return false;
|
|
17
|
+
const facts = v.facts;
|
|
18
|
+
if (facts === null)
|
|
19
|
+
return true;
|
|
20
|
+
if (typeof facts !== "object")
|
|
21
|
+
return false;
|
|
22
|
+
const f = facts;
|
|
23
|
+
if (!isWallBytes(f.maxRowBytes) || !isWallBytes(f.maxPacketBytes))
|
|
24
|
+
return false;
|
|
25
|
+
const sources = f.wallSources;
|
|
26
|
+
if (typeof sources !== "object" || sources === null)
|
|
27
|
+
return false;
|
|
28
|
+
const src = sources;
|
|
29
|
+
return isWallSourceWord(src.maxRowBytes) && isWallSourceWord(src.maxPacketBytes);
|
|
30
|
+
}
|
|
31
|
+
export function sqlEngineSeatIssue(backend, sqlEngine) {
|
|
32
|
+
if (sqlEngine !== undefined)
|
|
33
|
+
return isSqlEngineReading(sqlEngine) ? undefined : "bad_shape";
|
|
34
|
+
const { mysqlPool, pgPool } = sqlPoolsOf(backend);
|
|
35
|
+
return mysqlPool !== undefined || pgPool !== undefined ? "required" : undefined;
|
|
36
|
+
}
|
|
2
37
|
export async function establishSqlEnginePosture(pools) {
|
|
3
38
|
const driver = pools.mysqlPool ? mysqlDriver(pools.mysqlPool) : pools.pgPool ? pgDriver(pools.pgPool) : undefined;
|
|
4
39
|
if (driver === undefined)
|
|
5
40
|
return undefined;
|
|
6
41
|
const conn = await driver.connect();
|
|
7
42
|
conn.release();
|
|
8
|
-
return
|
|
43
|
+
return driver;
|
|
9
44
|
}
|
|
10
45
|
export function projectSqlEngineCapability(facts) {
|
|
11
46
|
return facts == null ? null : { engine: facts.engine, isolation: facts.isolation, txnMode: facts.txnMode };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ParseHooksConfigResult, type RunnableHooksConfig } from "./hooks/hook-runner.js";
|
|
2
|
+
/** 一次重读的产物:读者对剥键之后的文档的答案,与被剥掉的每个坐标(出门形,经契约包的脱敏出门)。 */
|
|
3
|
+
export interface StoredHooksRereadM20Result {
|
|
4
|
+
readonly config: RunnableHooksConfig;
|
|
5
|
+
readonly stripped: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
/** 重读座:存下的 `settings.hooks` 原值 → 迁移产物(`undefined` = 不迁移,调用方走原臂)。 */
|
|
8
|
+
export type StoredHooksRereadM20 = (raw: unknown) => StoredHooksRereadM20Result | undefined;
|
|
9
|
+
/** 读者(= `parseHooksConfig`);作形参只为让「读者交回什么拒因」可被替身喂(默认值就是生产读者)。 */
|
|
10
|
+
export type HooksReader = (raw: unknown) => ParseHooksConfigResult;
|
|
11
|
+
/**
|
|
12
|
+
* 重读一份存体 hooks:读者拒因全部属于两形 ⇒ 剥掉被点名的块里按类型确属未知的键、再问读者,**剥到读者受理为止**,过了就交回产物 + 被剥坐标;否则 `undefined`(原臂:整份不采纳)。
|
|
13
|
+
* 调用方只在读者**已经拒过**这份原值之后才来问;读者一开始就受理的值不进这里(进来也返回 `undefined`:没有可迁移的东西)。
|
|
14
|
+
*
|
|
15
|
+
* 终止:循环体以一次读开头(最后一次剥键之后必有一次读);继续下一轮的条件是本轮真从文档里剥掉了至少一个对象键,文档只减不增 ⇒ 必停;同一块只会被点名一次 ⇒ 轮数 ≤ 被点名的块数 + `__proto__` 键数。
|
|
16
|
+
*/
|
|
17
|
+
export declare function rereadStoredHooksM20(raw: unknown, read?: HooksReader): StoredHooksRereadM20Result | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* 此刻(`today` = UTC 日历日 YYYY-MM-DD)M20 的窗开着 ⇒ 交回重读座;关了 ⇒ `undefined`(今天的读法)。窗只读登记表 M20 那一行
|
|
20
|
+
* (`migrationWindowOpen`,与 M10 / M19 同一只判据)。登记表里没有 M20 ⇒ 响亮抛:重读臂活过了它的登记行,该随那一行一起删。
|
|
21
|
+
*/
|
|
22
|
+
export declare function storedHooksRereadM20(today: string): StoredHooksRereadM20 | undefined;
|
|
23
|
+
//# sourceMappingURL=stored-request-hooks-m20.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { HOOK_ENTRY_TYPES } from "@sema-agent/settings-schema/hooks";
|
|
2
|
+
import { hooksCoordinate, parseHooksConfig } from "./hooks/hook-runner.js";
|
|
3
|
+
import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
|
|
4
|
+
const ENTRY_FIELD_NAMES = {
|
|
5
|
+
command: { type: true, if: true, timeout: true, statusMessage: true, once: true, command: true, shell: true, async: true, asyncRewake: true },
|
|
6
|
+
prompt: { type: true, if: true, timeout: true, statusMessage: true, once: true, prompt: true, model: true },
|
|
7
|
+
agent: { type: true, if: true, timeout: true, statusMessage: true, once: true, prompt: true, model: true },
|
|
8
|
+
http: { type: true, if: true, timeout: true, statusMessage: true, once: true, url: true, headers: true, allowedEnvVars: true },
|
|
9
|
+
mcp_tool: { type: true, if: true, timeout: true, statusMessage: true, once: true, server: true, tool: true, input: true },
|
|
10
|
+
};
|
|
11
|
+
const MATCHER_FIELD_NAMES = { matcher: true, hooks: true };
|
|
12
|
+
function isRecord(v) {
|
|
13
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
14
|
+
}
|
|
15
|
+
function isEntryType(v) {
|
|
16
|
+
return typeof v === "string" && HOOK_ENTRY_TYPES.includes(v);
|
|
17
|
+
}
|
|
18
|
+
function nodeAt(node, path) {
|
|
19
|
+
let cur = node;
|
|
20
|
+
for (const seg of path) {
|
|
21
|
+
if (Array.isArray(cur))
|
|
22
|
+
cur = typeof seg === "number" ? cur[seg] : undefined;
|
|
23
|
+
else if (isRecord(cur) && typeof seg === "string")
|
|
24
|
+
cur = Object.hasOwn(cur, seg) ? cur[seg] : undefined;
|
|
25
|
+
else
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return cur;
|
|
29
|
+
}
|
|
30
|
+
function contractFieldsOf(code, block) {
|
|
31
|
+
if (!isRecord(block))
|
|
32
|
+
return undefined;
|
|
33
|
+
if (code === "guard-matcher-invalid")
|
|
34
|
+
return MATCHER_FIELD_NAMES;
|
|
35
|
+
const type = Object.hasOwn(block, "type") ? block.type : undefined;
|
|
36
|
+
return isEntryType(type) ? ENTRY_FIELD_NAMES[type] : undefined;
|
|
37
|
+
}
|
|
38
|
+
function stripTargetsOf(piece, doc) {
|
|
39
|
+
const [root, ...rel] = piece.path;
|
|
40
|
+
if (root !== "hooks")
|
|
41
|
+
return undefined;
|
|
42
|
+
if ((piece.code === "unknown-event" || piece.code === "guard-under-unknown-event") && rel.length === 1 && rel[0] === "__proto__")
|
|
43
|
+
return [rel];
|
|
44
|
+
if ((piece.code === "guard-entry-invalid" || piece.code === "guard-matcher-invalid") && typeof piece.field === "string") {
|
|
45
|
+
const block = nodeAt(doc, rel);
|
|
46
|
+
const known = contractFieldsOf(piece.code, block);
|
|
47
|
+
if (known === undefined || !isRecord(block) || Object.hasOwn(known, piece.field) || !Object.hasOwn(block, piece.field))
|
|
48
|
+
return undefined;
|
|
49
|
+
return Object.keys(block)
|
|
50
|
+
.filter((key) => !Object.hasOwn(known, key))
|
|
51
|
+
.map((key) => [...rel, key]);
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
function cloneDoc(root) {
|
|
56
|
+
const isContainer = (v) => Array.isArray(v) || isRecord(v);
|
|
57
|
+
const shell = (v) => (Array.isArray(v) ? [] : isRecord(v) ? {} : v);
|
|
58
|
+
const copy = shell(root);
|
|
59
|
+
const pending = isContainer(root) ? [[root, copy]] : [];
|
|
60
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
61
|
+
const [source, target] = next;
|
|
62
|
+
if (Array.isArray(source)) {
|
|
63
|
+
for (const value of source) {
|
|
64
|
+
const child = shell(value);
|
|
65
|
+
target.push(child);
|
|
66
|
+
if (isContainer(value))
|
|
67
|
+
pending.push([value, child]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (isRecord(source)) {
|
|
71
|
+
for (const [key, value] of Object.entries(source)) {
|
|
72
|
+
const child = shell(value);
|
|
73
|
+
Object.defineProperty(target, key, { value: child, writable: true, enumerable: true, configurable: true });
|
|
74
|
+
if (isContainer(value))
|
|
75
|
+
pending.push([value, child]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return copy;
|
|
80
|
+
}
|
|
81
|
+
function removeKeyAt(doc, path) {
|
|
82
|
+
const key = path[path.length - 1];
|
|
83
|
+
const parent = nodeAt(doc, path.slice(0, -1));
|
|
84
|
+
if (!isRecord(parent) || typeof key !== "string" || !Object.hasOwn(parent, key))
|
|
85
|
+
return false;
|
|
86
|
+
delete parent[key];
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
export function rereadStoredHooksM20(raw, read = parseHooksConfig) {
|
|
90
|
+
const doc = cloneDoc(raw);
|
|
91
|
+
const stripped = [];
|
|
92
|
+
for (;;) {
|
|
93
|
+
const r = read(doc);
|
|
94
|
+
if (r.config !== undefined)
|
|
95
|
+
return stripped.length > 0 ? { config: r.config, stripped } : undefined;
|
|
96
|
+
const pieces = r.pieces;
|
|
97
|
+
if (pieces === undefined || pieces.length === 0)
|
|
98
|
+
return undefined;
|
|
99
|
+
const targets = [];
|
|
100
|
+
for (const piece of pieces) {
|
|
101
|
+
const pieceTargets = stripTargetsOf(piece, doc);
|
|
102
|
+
if (pieceTargets === undefined)
|
|
103
|
+
return undefined;
|
|
104
|
+
for (const target of pieceTargets)
|
|
105
|
+
targets.push(target);
|
|
106
|
+
}
|
|
107
|
+
let removedAny = false;
|
|
108
|
+
for (const target of targets) {
|
|
109
|
+
if (!removeKeyAt(doc, target))
|
|
110
|
+
continue;
|
|
111
|
+
stripped.push(hooksCoordinate(["hooks", ...target]));
|
|
112
|
+
removedAny = true;
|
|
113
|
+
}
|
|
114
|
+
if (!removedAny)
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export function storedHooksRereadM20(today) {
|
|
119
|
+
const window = ONE_TIME_MIGRATIONS.find((w) => w.id === "M20");
|
|
120
|
+
if (window === undefined)
|
|
121
|
+
throw new Error("stored-request-hooks-m20: the one-time migration registry has no M20 row (the replay-leg stored-hooks re-read window) — the re-read arm outlived its registry row; delete it with that row");
|
|
122
|
+
return migrationWindowOpen(window, today) ? (raw) => rereadStoredHooksM20(raw) : undefined;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=stored-request-hooks-m20.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 一次性迁移 M19 —— 续跑重放腿上,升级前存下的请求体里引擎判官判不过的 `mcpServers[].toolAxes`,窗内按升级前的读法重读一次。
|
|
2
|
+
* 一次性迁移 M19 —— 续跑重放腿上,升级前存下的请求体里引擎判官判不过的 `mcpServers[].toolAxes` 与 `a2aPeers[].toolAxes`,窗内按升级前的读法重读一次。
|
|
3
3
|
* 🔴 M19 · 窗口见 `plugins/one-time-migrations.ts` 的 ONE_TIME_MIGRATIONS 登记表(窗、删法、删后由谁接住只写在那一行)。
|
|
4
4
|
*
|
|
5
5
|
* ## 为什么要它(持久数据面)
|
|
@@ -9,17 +9,32 @@
|
|
|
9
9
|
* ⇒ 永远批不过去。持久数据面不能「翻红」,所以给它一次性迁移:窗开着时,续跑重放腿把判官判不过的那一格按升级前的读法重读一次,产物**再交同一只判官**
|
|
10
10
|
* (不过 ⇒ 原臂:整条不进);真挂上的这类 server 由请求腿说一行运维 warn `mcp_tool_axes_read_as_before_upgrade`。窗关之后回到今天的读法,删码零行为变化。
|
|
11
11
|
*
|
|
12
|
+
* ## A2A 一面(同一种病,同一只重读座;core 7.53.0 #1835 提货批)
|
|
13
|
+
* core 7.53.0 起 A2A 请求腿的 `toolAxes` 读者也是引擎的判官(`readToolAxes(…, "a2a")`)。7.131.x 的 A2A 请求腿读者对每只 skill 的记录只挑三个轴、**多余成员静默忽略**、
|
|
14
|
+
* 照挂 ⇒ 存量体里带多余成员的那条对端,续跑重放时整条不进,停驻在它某只 skill 审批上的任务同样永远批不过去。同一只重读座覆盖它,律只有一句:**重读复现那一面升级前
|
|
15
|
+
* 的读法,一个成员都不多救** —— A2A 那一面交 {@link readA2aToolAxesAsServer7131M19}(7.131.x 的读者逐字:未知成员忽略;已知轴写坏 —— `effect` / `irreversibility` 出词表、
|
|
16
|
+
* `egress` 非布尔、某只 skill 的值不是对象、整份不是对象含 `null` —— ⇒ 那条对端当时就整条丢,重读照旧整条丢,不交判官),产物再交同一只判官;
|
|
17
|
+
* 真挂上的对端一行运维 warn `a2a_tool_axes_read_as_before_upgrade`。所以 7.131.x 当时没挂上的对端,窗内续跑也不挂(不扩大外部工具集)。
|
|
18
|
+
*
|
|
12
19
|
* ## 可达面(结构上只在续跑重放腿)
|
|
13
20
|
* · 本模块的值只被 `boot/resolve-spec.ts` 引用一处,挂在 `opts.leg` 不是 `"fresh"` 的那一臂(续跑家族经 `http/resume-legs.ts` 的 `resolveResumeSpec`
|
|
14
|
-
* 递 `leg: "resume"`);新提交腿递 `"fresh"` 拿不到重读座,且在腿首已经被同一只判官 400 掉(`
|
|
15
|
-
* ·
|
|
21
|
+
* 递 `leg: "resume"`);新提交腿递 `"fresh"` 拿不到重读座,且在腿首已经被同一只判官 400 掉(`assertRequestMcpEntries`),读器根本见不到坏形;
|
|
22
|
+
* · 两条请求腿(`task-mcp.ts` / `task-a2a.ts`)只 import 本模块的**型**:重读座是 `resolveRequestMcp` / `resolveRequestA2a` 的一个形参,没人递就没有(`resolve-spec.ts`
|
|
23
|
+
* 求值一次、两腿同递那一份);探测口(`validateRequestMcp`)与 `validateRequestA2a` 没有这个形参;
|
|
16
24
|
* · 部署腿(`web-admin-client/skills-mcp.ts` 的 `resolveMcpServers`)是另一只铸 spec 处,不读本模块。
|
|
17
25
|
* 机器锚:`test/mcp-tool-axes-m19.test.ts` 的结构格。
|
|
18
26
|
*/
|
|
19
27
|
import type { McpServerSpec } from "@sema-agent/core";
|
|
20
28
|
type ToolAxes = NonNullable<McpServerSpec["toolAxes"]>;
|
|
21
|
-
/**
|
|
22
|
-
|
|
29
|
+
/**
|
|
30
|
+
* 重读座:一份原始 `toolAxes` 声明 → **那一面**升级前的读法读出的那一份,产物由调用方再交引擎判官。两面各一只读者,因为两面升级前的读法不同:
|
|
31
|
+
* · `mcp`(≤7.129.x 的 MCP 请求腿):`undefined` = 升级前读成「没声明」(server 照挂);升级前从不因 `toolAxes` 丢 server;
|
|
32
|
+
* · `a2a`(7.131.x 的 A2A 请求腿):`undefined` 同上;`null` = 7.131.x 当时就把这条对端整条丢了 —— 重读不救活,调用方按原臂整条不进。
|
|
33
|
+
*/
|
|
34
|
+
export interface StoredToolAxesRereadM19 {
|
|
35
|
+
readonly mcp: (declared: unknown) => ToolAxes | undefined;
|
|
36
|
+
readonly a2a: (declared: unknown) => ToolAxes | undefined | null;
|
|
37
|
+
}
|
|
23
38
|
/**
|
|
24
39
|
* 此刻(`today` = UTC 日历日 YYYY-MM-DD)M19 的窗开着 ⇒ 交回重读座;关了 ⇒ `undefined`(今天的读法)。窗只读登记表 M19 那一行
|
|
25
40
|
* (`migrationWindowOpen`,与 M10 的认领窗同一只判据)。登记表里没有 M19 ⇒ 响亮抛:重读臂活过了它的登记行,该随那一行一起删。
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
|
|
2
|
-
|
|
2
|
+
const TOOL_EFFECT_WORDS_M19 = ["read", "write", "idempotent"];
|
|
3
|
+
const TOOL_AXIS_IRREVERSIBILITY_WORDS_M19 = ["always", "never"];
|
|
4
|
+
const isToolEffect = (v) => typeof v === "string" && TOOL_EFFECT_WORDS_M19.includes(v);
|
|
5
|
+
const isToolAxisIrreversibility = (v) => typeof v === "string" && TOOL_AXIS_IRREVERSIBILITY_WORDS_M19.includes(v);
|
|
3
6
|
function readToolAxesAsServer7129M19(declared) {
|
|
4
7
|
if (!declared || typeof declared !== "object" || Array.isArray(declared))
|
|
5
8
|
return undefined;
|
|
@@ -18,10 +21,35 @@ function readToolAxesAsServer7129M19(declared) {
|
|
|
18
21
|
}
|
|
19
22
|
return Object.keys(axes).length > 0 ? axes : undefined;
|
|
20
23
|
}
|
|
24
|
+
function readA2aToolAxesAsServer7131M19(declared) {
|
|
25
|
+
if (!declared || typeof declared !== "object" || Array.isArray(declared))
|
|
26
|
+
return null;
|
|
27
|
+
const axes = {};
|
|
28
|
+
for (const [skill, ax] of Object.entries(declared)) {
|
|
29
|
+
if (!ax || typeof ax !== "object" || Array.isArray(ax))
|
|
30
|
+
return null;
|
|
31
|
+
const a = ax;
|
|
32
|
+
if (a.effect !== undefined && !isToolEffect(a.effect))
|
|
33
|
+
return null;
|
|
34
|
+
if (a.egress !== undefined && typeof a.egress !== "boolean")
|
|
35
|
+
return null;
|
|
36
|
+
if (a.irreversibility !== undefined && !isToolAxisIrreversibility(a.irreversibility))
|
|
37
|
+
return null;
|
|
38
|
+
const entry = {
|
|
39
|
+
...(isToolEffect(a.effect) ? { effect: a.effect } : {}),
|
|
40
|
+
...(typeof a.egress === "boolean" ? { egress: a.egress } : {}),
|
|
41
|
+
...(isToolAxisIrreversibility(a.irreversibility) ? { irreversibility: a.irreversibility } : {}),
|
|
42
|
+
};
|
|
43
|
+
if (Object.keys(entry).length > 0)
|
|
44
|
+
axes[skill] = entry;
|
|
45
|
+
}
|
|
46
|
+
return Object.keys(axes).length > 0 ? axes : undefined;
|
|
47
|
+
}
|
|
48
|
+
const REREAD_M19 = { mcp: readToolAxesAsServer7129M19, a2a: readA2aToolAxesAsServer7131M19 };
|
|
21
49
|
export function storedToolAxesRereadM19(today) {
|
|
22
50
|
const window = ONE_TIME_MIGRATIONS.find((w) => w.id === "M19");
|
|
23
51
|
if (window === undefined)
|
|
24
52
|
throw new Error("stored-request-tool-axes-m19: the one-time migration registry has no M19 row (the replay-leg toolAxes re-read window) — the re-read arm outlived its registry row; delete it with that row");
|
|
25
|
-
return migrationWindowOpen(window, today) ?
|
|
53
|
+
return migrationWindowOpen(window, today) ? REREAD_M19 : undefined;
|
|
26
54
|
}
|
|
27
55
|
//# sourceMappingURL=stored-request-tool-axes-m19.js.map
|
package/dist/task-a2a.d.ts
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* 「广告了却拦不住」的那一类缺口。钉在 `test/task-a2a.test.ts`(含到货后该怎么改那一格的原地说明)。
|
|
43
43
|
*/
|
|
44
44
|
import { type A2aServerSpec, type CompliancePosture } from "@sema-agent/core";
|
|
45
|
+
import type { StoredToolAxesRereadM19 } from "./stored-request-tool-axes-m19.js";
|
|
45
46
|
/** Bound on caller-supplied peers (anti-DoS; a real declaration names a handful of agents). */
|
|
46
47
|
export declare const MAX_REQUEST_A2A_PEERS = 32;
|
|
47
48
|
/** The deployment-facing shape this module reads. `lockedConfigKeys` is deliberately `readonly string[]`
|
|
@@ -69,6 +70,8 @@ export interface A2aGateConfig {
|
|
|
69
70
|
* deployment with a broken posture keep advertising the capability (仓规:安全轴禁静默兜底).
|
|
70
71
|
*/
|
|
71
72
|
export declare function a2aInjectionHonored(config: A2aGateConfig): boolean;
|
|
73
|
+
/** 读这条对端的是哪条腿:`fresh` = 新提交;`resume` = 续跑重放升级前存下的请求体(`boot/resolve-spec.ts` 的 `opts.leg` 分流,同一个读法)。 */
|
|
74
|
+
type A2aRequestLeg = "fresh" | "resume";
|
|
72
75
|
/**
|
|
73
76
|
* Shape-check the raw `body.a2aPeers`. `null` → a non-array (caller error; the route may 400). Otherwise the
|
|
74
77
|
* valid subset + the names of dropped (malformed/over-cap) entries.
|
|
@@ -110,5 +113,10 @@ export declare function mergeRequestA2a(baseline: A2aServerSpec[] | undefined, g
|
|
|
110
113
|
export declare function resolveRequestA2a(baseline: A2aServerSpec[] | undefined, bodyA2a: unknown, config: A2aGateConfig, logger?: {
|
|
111
114
|
info?(m: string, meta?: unknown): void;
|
|
112
115
|
warn(m: string, meta?: unknown): void;
|
|
113
|
-
}
|
|
116
|
+
},
|
|
117
|
+
/** 哪条腿在读(`boot/resolve-spec.ts` 按 `opts.leg` 递;缺省 = 新提交,最严的读法):只决定 `headers` 里 `__proto__` 名成员的处置(见 {@link normalizeA2aPeer})。 */
|
|
118
|
+
leg?: A2aRequestLeg,
|
|
119
|
+
/** 一次性迁移 M19 的重读座(窗口见 `plugins/one-time-migrations.ts` 登记表):只在续跑重放腿、只在窗开着时由 `boot/resolve-spec.ts` 递进来 —— 与 MCP 请求腿同一份。 */
|
|
120
|
+
storedToolAxes?: StoredToolAxesRereadM19): A2aServerSpec[] | undefined;
|
|
121
|
+
export {};
|
|
114
122
|
//# sourceMappingURL=task-a2a.d.ts.map
|
package/dist/task-a2a.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { resolveComplianceDenies } from "@sema-agent/core";
|
|
2
|
-
import { isUsableWireName } from "
|
|
1
|
+
import { readToolAxes, resolveComplianceDenies } from "@sema-agent/core";
|
|
2
|
+
import { isUsableWireName } from "@sema-agent/settings-schema";
|
|
3
3
|
import { HttpError } from "./security.js";
|
|
4
|
-
import {
|
|
4
|
+
import { readPlainStringRecord } from "./plain-string-record.js";
|
|
5
|
+
import { echoKeyName } from "./request-key-closure.js";
|
|
5
6
|
import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
|
|
6
7
|
export const MAX_REQUEST_A2A_PEERS = 32;
|
|
7
8
|
export function a2aInjectionHonored(config) {
|
|
@@ -14,79 +15,83 @@ export function a2aInjectionHonored(config) {
|
|
|
14
15
|
}
|
|
15
16
|
return config.requirePrincipal !== true;
|
|
16
17
|
}
|
|
17
|
-
function isStringRecord(v) {
|
|
18
|
-
return !!v && typeof v === "object" && !Array.isArray(v) && Object.values(v).every((x) => typeof x === "string");
|
|
19
|
-
}
|
|
20
18
|
const HTTP_URL_RE = /^https?:\/\//i;
|
|
21
|
-
function normalizeA2aPeer(e) {
|
|
19
|
+
function normalizeA2aPeer(e, leg, storedToolAxes) {
|
|
20
|
+
const dropped = { spec: null };
|
|
22
21
|
if (!e || typeof e !== "object")
|
|
23
|
-
return
|
|
22
|
+
return dropped;
|
|
24
23
|
const o = e;
|
|
25
24
|
if (!isUsableWireName(o.name))
|
|
26
|
-
return
|
|
25
|
+
return dropped;
|
|
27
26
|
if (typeof o.url !== "string" || !HTTP_URL_RE.test(o.url))
|
|
28
|
-
return
|
|
27
|
+
return dropped;
|
|
29
28
|
if (o.cardUrl !== undefined && (typeof o.cardUrl !== "string" || !HTTP_URL_RE.test(o.cardUrl)))
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
return dropped;
|
|
30
|
+
const headers = o.headers === undefined ? undefined : readPlainStringRecord(o.headers);
|
|
31
|
+
if (headers !== undefined && (!headers.ok || (headers.stripped.length > 0 && leg === "fresh")))
|
|
32
|
+
return dropped;
|
|
33
|
+
const lost = headers?.ok === true && headers.stripped.length > 0 ? { headersLost: headers.stripped.map((m) => echoKeyName("headers.", m.name)) } : {};
|
|
33
34
|
if (o.principalHeader !== undefined && typeof o.principalHeader !== "string")
|
|
34
|
-
return
|
|
35
|
+
return dropped;
|
|
35
36
|
if (o.allowSkills !== undefined && !(Array.isArray(o.allowSkills) && o.allowSkills.every((s) => typeof s === "string")))
|
|
36
|
-
return
|
|
37
|
+
return dropped;
|
|
37
38
|
const spec = { name: o.name, url: o.url };
|
|
38
39
|
if (typeof o.cardUrl === "string")
|
|
39
40
|
spec.cardUrl = o.cardUrl;
|
|
40
|
-
if (
|
|
41
|
-
spec.headers =
|
|
41
|
+
if (headers?.ok === true)
|
|
42
|
+
spec.headers = headers.record;
|
|
42
43
|
if (typeof o.principalHeader === "string")
|
|
43
44
|
spec.principalHeader = o.principalHeader;
|
|
44
45
|
if (Array.isArray(o.allowSkills))
|
|
45
46
|
spec.allowSkills = o.allowSkills;
|
|
46
47
|
if (o.toolAxes !== undefined) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (a.irreversibility !== undefined && !isToolAxisIrreversibility(a.irreversibility))
|
|
59
|
-
return null;
|
|
60
|
-
const entry = {
|
|
61
|
-
...(isToolEffect(a.effect) ? { effect: a.effect } : {}),
|
|
62
|
-
...(typeof a.egress === "boolean" ? { egress: a.egress } : {}),
|
|
63
|
-
...(isToolAxisIrreversibility(a.irreversibility) ? { irreversibility: a.irreversibility } : {}),
|
|
64
|
-
};
|
|
65
|
-
if (Object.keys(entry).length > 0)
|
|
66
|
-
axes[skill] = entry;
|
|
48
|
+
const axes = readToolAxes(o.toolAxes, "a2a");
|
|
49
|
+
if (!axes.ok && storedToolAxes !== undefined) {
|
|
50
|
+
const before = storedToolAxes.a2a(o.toolAxes);
|
|
51
|
+
if (before === null)
|
|
52
|
+
return dropped;
|
|
53
|
+
const again = readToolAxes(before, "a2a");
|
|
54
|
+
if (!again.ok)
|
|
55
|
+
return dropped;
|
|
56
|
+
if (again.toolAxes !== undefined)
|
|
57
|
+
spec.toolAxes = again.toolAxes;
|
|
58
|
+
return { spec, toolAxesReread: axes.problem, ...lost };
|
|
67
59
|
}
|
|
68
|
-
if (
|
|
69
|
-
|
|
60
|
+
if (!axes.ok)
|
|
61
|
+
return dropped;
|
|
62
|
+
if (axes.toolAxes !== undefined)
|
|
63
|
+
spec.toolAxes = axes.toolAxes;
|
|
70
64
|
}
|
|
71
|
-
return spec;
|
|
65
|
+
return { spec, ...lost };
|
|
72
66
|
}
|
|
73
67
|
export function validateRequestA2a(raw) {
|
|
68
|
+
const v = validateRequestA2aEntries(raw, "fresh");
|
|
69
|
+
return v === null ? null : { ok: v.ok, dropped: v.dropped };
|
|
70
|
+
}
|
|
71
|
+
function validateRequestA2aEntries(raw, leg, storedToolAxes) {
|
|
74
72
|
if (raw === undefined || raw === null)
|
|
75
|
-
return { ok: [], dropped: [] };
|
|
73
|
+
return { ok: [], dropped: [], toolAxesReread: [], headersLost: [] };
|
|
76
74
|
if (!Array.isArray(raw))
|
|
77
75
|
return null;
|
|
78
76
|
const ok = [];
|
|
79
77
|
const dropped = [];
|
|
78
|
+
const toolAxesReread = [];
|
|
79
|
+
const headersLost = [];
|
|
80
80
|
for (const e of raw.slice(0, MAX_REQUEST_A2A_PEERS)) {
|
|
81
|
-
const v = normalizeA2aPeer(e);
|
|
82
|
-
if (v)
|
|
81
|
+
const { spec: v, toolAxesReread: reread, headersLost: lost } = normalizeA2aPeer(e, leg, storedToolAxes);
|
|
82
|
+
if (v) {
|
|
83
83
|
ok.push(v);
|
|
84
|
+
if (reread !== undefined)
|
|
85
|
+
toolAxesReread.push({ peer: v.name, problem: reread });
|
|
86
|
+
if (lost !== undefined)
|
|
87
|
+
headersLost.push({ peer: v.name, lost });
|
|
88
|
+
}
|
|
84
89
|
else
|
|
85
90
|
dropped.push(e && typeof e === "object" && typeof e.name === "string" ? String(e.name).slice(0, 128) : "<malformed>");
|
|
86
91
|
}
|
|
87
92
|
if (raw.length > MAX_REQUEST_A2A_PEERS)
|
|
88
93
|
dropped.push(`(+${raw.length - MAX_REQUEST_A2A_PEERS} over cap of ${MAX_REQUEST_A2A_PEERS})`);
|
|
89
|
-
return { ok, dropped };
|
|
94
|
+
return { ok, dropped, toolAxesReread, headersLost };
|
|
90
95
|
}
|
|
91
96
|
export function assertRequestA2aUnlocked(bodyA2a, lockedKeys) {
|
|
92
97
|
if (!lockedKeys.has("a2a"))
|
|
@@ -104,20 +109,41 @@ export function mergeRequestA2a(baseline, gated) {
|
|
|
104
109
|
const add = gated.filter((p) => !taken.has(p.name));
|
|
105
110
|
return add.length === 0 ? baseline : [...(baseline ?? []), ...add];
|
|
106
111
|
}
|
|
107
|
-
export function resolveRequestA2a(baseline, bodyA2a, config, logger) {
|
|
112
|
+
export function resolveRequestA2a(baseline, bodyA2a, config, logger, leg = "fresh", storedToolAxes) {
|
|
108
113
|
if (bodyA2a === undefined || bodyA2a === null)
|
|
109
114
|
return baseline;
|
|
110
115
|
if (!a2aInjectionHonored(config)) {
|
|
111
116
|
logger?.warn("a2a_injection_ignored", { reason: "gate_closed (per-request A2A peers honored only on a single-user, unlocked, compliance-permitted deployment)" });
|
|
112
117
|
return baseline;
|
|
113
118
|
}
|
|
114
|
-
const v =
|
|
119
|
+
const v = validateRequestA2aEntries(bodyA2a, leg, storedToolAxes);
|
|
115
120
|
if (v === null) {
|
|
116
121
|
logger?.warn("a2a_injection_ignored", { reason: "a2aPeers not an array" });
|
|
117
122
|
return baseline;
|
|
118
123
|
}
|
|
119
124
|
if (v.dropped.length > 0)
|
|
120
125
|
logger?.warn("a2a_injection_dropped", { dropped: v.dropped });
|
|
121
|
-
|
|
126
|
+
const merged = mergeRequestA2a(baseline, v.ok);
|
|
127
|
+
const baselineNames = new Set((baseline ?? []).map((p) => p.name));
|
|
128
|
+
for (const r of v.toolAxesReread) {
|
|
129
|
+
if (baselineNames.has(r.peer))
|
|
130
|
+
continue;
|
|
131
|
+
logger?.warn("a2a_tool_axes_read_as_before_upgrade", {
|
|
132
|
+
peer: r.peer,
|
|
133
|
+
migration: "M19",
|
|
134
|
+
problem: r.problem,
|
|
135
|
+
note: "this replayed request was stored before the upgrade and the peer's toolAxes declaration does not pass the engine's reader; while one-time migration M19 is open it is read the way the pre-upgrade reader reads it (members that do not fit are dropped) and the peer stays mounted — once the window closes the same request leaves this peer unmounted",
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
for (const h of v.headersLost) {
|
|
139
|
+
if (baselineNames.has(h.peer))
|
|
140
|
+
continue;
|
|
141
|
+
logger?.warn("a2a_peer_headers_members_dropped", {
|
|
142
|
+
peer: h.peer,
|
|
143
|
+
lost: h.lost,
|
|
144
|
+
note: "this replayed request carries a headers member named \"__proto__\" for this peer (the engine refuses that name); the peer stays mounted with every other header as written, the named member left out. A new submission with the same peer leaves it unmounted",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return merged;
|
|
122
148
|
}
|
|
123
149
|
//# sourceMappingURL=task-a2a.js.map
|