@sema-agent/server 7.43.0 → 7.44.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/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
package/dist/spec-fields.js
CHANGED
|
@@ -1,36 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request-body → TaskSpec field normalizers (shell-host contract). Kept out of main.ts (which auto-runs the service
|
|
3
|
-
* on import) so they are unit-testable in isolation. Each is DEFENSIVE: a RESUME re-runs resolveSpec from the stored
|
|
4
|
-
* body WITHOUT the HTTP-layer 400 validation, so a malformed value must fall back to a safe default, never throw.
|
|
5
|
-
*/
|
|
6
1
|
import { isThinkingLevel } from "@sema-agent/core";
|
|
7
2
|
import { taskWallClockSec } from "./task-workflow.js";
|
|
8
|
-
/**
|
|
9
|
-
* E12 — normalize `body.suggestNextPrompts` → `TaskSpec.suggestNextPrompts`. `true` = on with core defaults
|
|
10
|
-
* (count 3, cheap role); an object tunes `count` (clamped 1..10) / `role` (a free string — core validates it against
|
|
11
|
-
* `ModelRole`, an unknown role falls back to the task model). `false` / absent / non-object / garbage ⇒ undefined (off).
|
|
12
|
-
*/
|
|
13
3
|
export function normalizeSuggestNextPrompts(v) {
|
|
14
4
|
if (v === true)
|
|
15
5
|
return true;
|
|
16
6
|
if (v == null || typeof v !== "object" || Array.isArray(v))
|
|
17
|
-
return undefined;
|
|
7
|
+
return undefined;
|
|
18
8
|
const o = v;
|
|
19
|
-
// DROP a non-finite or <1 count (treat as absent → core's default), matching the HTTP layer which 400s count<1 — so
|
|
20
|
-
// submit-reject and resume-normalize never diverge into an unintended count. Clamp a large count down to 10.
|
|
21
9
|
const count = typeof o.count === "number" && Number.isFinite(o.count) && o.count >= 1 ? Math.min(10, Math.floor(o.count)) : undefined;
|
|
22
10
|
const role = typeof o.role === "string" && o.role ? o.role : undefined;
|
|
23
11
|
return { ...(count !== undefined ? { count } : {}), ...(role ? { role } : {}) };
|
|
24
12
|
}
|
|
25
|
-
/**
|
|
26
|
-
* design/131 (core 1.246) — normalize `body.resilience` → `TaskSpec.resilience`. INTENT flags only:
|
|
27
|
-
* `allowDegrade:false` / `allowFailover:false` let a bench/eval or critical task say "fail rather than fall back".
|
|
28
|
-
* `bypassBreaker` is OPERATOR-FACING (不给 wire 用户直开 — it punches through a shared circuit breaker,
|
|
29
|
-
* which exists to protect the provider): honored only when the request principal is an explicit operator; silently
|
|
30
|
-
* dropped otherwise (not a 4xx — resume re-runs this without HTTP validation, and a stripped intent flag degrades
|
|
31
|
-
* to today's behavior, core's own "all absent = byte-compat" posture). Only literal booleans pass; all-empty ⇒
|
|
32
|
-
* undefined (never an empty object — keeps the spec byte-identical for non-users).
|
|
33
|
-
*/
|
|
34
13
|
export function normalizeResilience(v, operator) {
|
|
35
14
|
if (v == null || typeof v !== "object" || Array.isArray(v))
|
|
36
15
|
return undefined;
|
|
@@ -42,20 +21,9 @@ export function normalizeResilience(v, operator) {
|
|
|
42
21
|
};
|
|
43
22
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
44
23
|
}
|
|
45
|
-
/**
|
|
46
|
-
* design/133 (core 1.251) — normalize `body.attachments` → `TaskSpec.attachments`. Core's contract is
|
|
47
|
-
* literal-true unions ("关闭=删 key,勿传 false"): only `true` (or, for changedFiles, an object with a sane
|
|
48
|
-
* `maxFiles`) survives; `false`/garbage per key is DROPPED, not forwarded. maxFiles clamped 1..100 (core caps its
|
|
49
|
-
* own stat cost at 20 by default; the clamp only guards against a resume replaying a wild persisted value).
|
|
50
|
-
* All-empty ⇒ undefined. NB resume audit: our persisted body is the FULL request JSON and resolveSpec
|
|
51
|
-
* is the single translation point, so consuming the field here IS the whitelist inclusion — nothing else to add.
|
|
52
|
-
*/
|
|
53
24
|
export function normalizeAttachments(v) {
|
|
54
25
|
if (v == null || typeof v !== "object" || Array.isArray(v))
|
|
55
26
|
return undefined;
|
|
56
|
-
// 历史复审轴B #1(1.254):白名单曾落后 core 五键——尤其 agentListing/skillsListing 是 core DEFAULT-ON
|
|
57
|
-
// (explicit false 才关),旧名单静默丢 false = wire 上唯一无法表达关闭的面(调用方 200 以为生效)。
|
|
58
|
-
// 两键按 boolean 透传(含 false);budgetUsd/mcpInstructions literal-true 同族;todoReminderMode 枚举。
|
|
59
27
|
const o = v;
|
|
60
28
|
let changedFiles;
|
|
61
29
|
if (o.changedFiles === true)
|
|
@@ -69,9 +37,6 @@ export function normalizeAttachments(v) {
|
|
|
69
37
|
...(o.todoReminder === true ? { todoReminder: true } : {}),
|
|
70
38
|
...(changedFiles !== undefined ? { changedFiles } : {}),
|
|
71
39
|
...(o.planModeReminder === true ? { planModeReminder: true } : {}),
|
|
72
|
-
// core 1.253 G1 → 5.12.0 BREAKING-3:压缩后 bg 任务重述从 opt-in 翻 **DEFAULT-ON**(显式 false
|
|
73
|
-
// 恒关/缺席走默认)。literal-true 形在此翻转后就是轴B #1 的原案复刻(agentListing/skillsListing
|
|
74
|
-
// 当年同病):丢 false = 客户端显式关断被静默压成默认 ON,wire 上无法表达关闭 ⇒ 迁 boolean 透传。
|
|
75
40
|
...(typeof o.backgroundTasks === "boolean" ? { backgroundTasks: o.backgroundTasks } : {}),
|
|
76
41
|
...(o.toolsDelta === true ? { toolsDelta: true } : {}),
|
|
77
42
|
...(o.todoReminderMode === "baseline" || o.todoReminderMode === "off" ? { todoReminderMode: o.todoReminderMode } : {}),
|
|
@@ -82,30 +47,18 @@ export function normalizeAttachments(v) {
|
|
|
82
47
|
};
|
|
83
48
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
84
49
|
}
|
|
85
|
-
/**
|
|
86
|
-
* [833] rewind exclusive mode (core 1.292) — normalize `body.resumeAtMode` → `TaskSpec.resumeAtMode`. DEFENSIVE
|
|
87
|
-
* (the resume path re-runs resolveSpec WITHOUT the HTTP 400 gate): only the two enum values survive, and only when
|
|
88
|
-
* the resume-at handle actually RESOLVED (core ignores the field without `resumeAt`; keep the spec tight instead of
|
|
89
|
-
* shipping a dangling qualifier). Absent/garbage ⇒ undefined = core default "at" (zero regression).
|
|
90
|
-
*/
|
|
91
50
|
export function normalizeResumeAtMode(v, resumeAtResolved) {
|
|
92
51
|
if (!resumeAtResolved)
|
|
93
52
|
return undefined;
|
|
94
53
|
return v === "at" || v === "before" ? v : undefined;
|
|
95
54
|
}
|
|
96
|
-
// ─── [876] per-task subagents(core 1.295 `TaskSpec.agents`)──────────────────────────────────────────────
|
|
97
|
-
/** [876] 防御上限:一份请求最多带多少个 agent 定义。core 侧无上限;这里封一个防御值 —— 每个定义都进
|
|
98
|
-
* 系统提示(whenToUse/skills/systemPrompt 全是 prompt 载荷),不设界就是 per-turn token 成本洞(与
|
|
99
|
-
* MAX_USER_SKILLS 同轴)。32 足够覆盖 CC 式 `.claude/agents/` 全家桶还有余量。 */
|
|
100
55
|
export const MAX_TASK_AGENTS = 32;
|
|
101
|
-
// [876] 每字段防御上限(cross-review 抓获:无上限的 prompt 载荷字段=32 条帽封不住的 per-turn token 成本洞,
|
|
102
|
-
// maxTurns 无顶=运行成本洞)。skill 三上限与 server.ts validateUserSkills 同值(64/1024/1MiB,engine 同一载荷面)。
|
|
103
56
|
const MAX_AGENT_NAME_CHARS = 64;
|
|
104
|
-
const MAX_AGENT_TEXT_CHARS = 4_096;
|
|
57
|
+
const MAX_AGENT_TEXT_CHARS = 4_096;
|
|
105
58
|
const MAX_AGENT_SYSTEM_PROMPT_CHARS = 65_536;
|
|
106
|
-
const MAX_AGENT_TOOLS = 64;
|
|
59
|
+
const MAX_AGENT_TOOLS = 64;
|
|
107
60
|
const MAX_AGENT_TOOL_NAME_CHARS = 128;
|
|
108
|
-
const MAX_AGENT_SKILLS = 10;
|
|
61
|
+
const MAX_AGENT_SKILLS = 10;
|
|
109
62
|
const MAX_AGENT_SKILL_CONTENT_CHARS = 1_048_576;
|
|
110
63
|
const MAX_AGENT_MAX_TURNS = 1_000;
|
|
111
64
|
const boundedString = (max) => (v) => typeof v === "string" && v.length <= max;
|
|
@@ -113,9 +66,6 @@ const toolList = (v) => Array.isArray(v) &&
|
|
|
113
66
|
v.length <= MAX_AGENT_TOOLS &&
|
|
114
67
|
v.every((x) => typeof x === "string" && x.length > 0 && x.length <= MAX_AGENT_TOOL_NAME_CHARS);
|
|
115
68
|
const plainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
116
|
-
/** SkillSpec 闭合白名单(core dist/core/types.d.ts:169:name/description/content 必填 + manifest?/files?)。
|
|
117
|
-
* files 条目必须 {path, content} 双 string —— core synthetic-tools 直接迭代 `f.path`,一个 null 条目就是
|
|
118
|
-
* 运行期 crash(cross-review 抓获);未知键整条拒(与 agent 键同姿势)。 */
|
|
119
69
|
const skillOk = (s) => {
|
|
120
70
|
if (!plainObject(s))
|
|
121
71
|
return false;
|
|
@@ -129,8 +79,6 @@ const skillOk = (s) => {
|
|
|
129
79
|
return false;
|
|
130
80
|
if (typeof s.content !== "string" || s.content.length === 0 || s.content.length > MAX_AGENT_SKILL_CONTENT_CHARS)
|
|
131
81
|
return false;
|
|
132
|
-
// SkillManifest 闭合(core types.d.ts:163:allowTools+lineageId 必填)—— 空对象/畸形 allowTools 会晚失败
|
|
133
|
-
// 在 core 激活技能时 `manifest.allowTools.map(...)`(round-2 cross-review)。
|
|
134
82
|
if (s.manifest !== undefined) {
|
|
135
83
|
const m = s.manifest;
|
|
136
84
|
if (!plainObject(m))
|
|
@@ -160,10 +108,6 @@ const skillOk = (s) => {
|
|
|
160
108
|
}
|
|
161
109
|
return true;
|
|
162
110
|
};
|
|
163
|
-
/** MemorySpecInput 闭合白名单(core dist/core/memory.d.ts 亲读)。core 的 normalizeMemorySpec 会直接
|
|
164
|
-
* 迭代 `scopes` —— 非数组形状(如 {scopes:{length:1}})在服务门不拦就是 core 运行期异常(cross-review 抓获)。
|
|
165
|
-
* core 5.1.0([2390]②,原 [2387] 预告):单数 `scope` 键已删,`scopes: string[]` 唯一拼法——白名单
|
|
166
|
-
* 同步剔键,收了就是穿门死键(用户以为设了 scope,core 静默无视)。 */
|
|
167
111
|
const memoryOk = (v) => {
|
|
168
112
|
if (!plainObject(v))
|
|
169
113
|
return false;
|
|
@@ -181,12 +125,6 @@ const memoryOk = (v) => {
|
|
|
181
125
|
return false;
|
|
182
126
|
return true;
|
|
183
127
|
};
|
|
184
|
-
/** [876] AgentDefinition 白名单字段表(core 1.295 dist/core/types.d.ts:113 亲读)+ 每字段的形状谓词。
|
|
185
|
-
* ⚠️ `permissionMode` 有意不纳(core 拍,独立批)—— body 里出现按未知键拒,见 taskAgentIssue。
|
|
186
|
-
* `model` 收「已解析真名 string 或带非空 string `id` 的 Model 对象」—— server 不做词表翻译(壳侧已解析,
|
|
187
|
-
* [879]④ 契约);core 对对象形 Model 原样信任不校验(dist/core/roles.js 亲读),空对象 `{}` 会晚失败在
|
|
188
|
-
* 引擎深处,所以最低限 `id` 在门上验。保留名(如与委派工具同名)不在这里镜像 —— core prepare 侧
|
|
189
|
-
* fail-loud `config.agent.reserved_name`(镜像 core 私有清单=漂移风险)。 */
|
|
190
128
|
const TASK_AGENT_FIELD_SHAPES = {
|
|
191
129
|
name: {
|
|
192
130
|
desc: `a non-empty string of at most ${MAX_AGENT_NAME_CHARS} characters`,
|
|
@@ -217,12 +155,6 @@ const TASK_AGENT_FIELD_SHAPES = {
|
|
|
217
155
|
observer: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
218
156
|
observerMessage: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
219
157
|
};
|
|
220
|
-
/** [876] 单条 agent 定义的形状裁决:null=合法;否则一句可 400 的英文错误(不带索引,调用方自己加)。
|
|
221
|
-
* 白名单外键 fail-loud —— 与 resilience/limits 的「容忍未知键」相反:agents 是能力定义,静默丢字段 =
|
|
222
|
-
* 定义被悄悄削(一个丢掉的 isolation 就是放宽),比 400 更危险,故整条拒。⚠️ 已知残账(core 半场):
|
|
223
|
-
* core 只对 allowTools 未知项 fail-loud,denyTools 未知项(如 typo)静默失效 —— 已上板请 core 镜像。
|
|
224
|
-
* ⚠️ 键判断必须 Object.hasOwn:`in` 走原型链,JSON 里的 "toString"/"constructor"/"__proto__" 键会被
|
|
225
|
-
* 误判为白名单字段,随后 SHAPES[k].ok 不是函数 ⇒ TypeError(fresh 500 / resume 砖;cross-review 复现)。 */
|
|
226
158
|
export function taskAgentIssue(item) {
|
|
227
159
|
if (item === null || typeof item !== "object" || Array.isArray(item))
|
|
228
160
|
return "each agent must be an object";
|
|
@@ -244,9 +176,6 @@ export function taskAgentIssue(item) {
|
|
|
244
176
|
}
|
|
245
177
|
return null;
|
|
246
178
|
}
|
|
247
|
-
/** [876] HTTP 门(fresh submit,server.ts prepareSpec):body.agents 在场必须是非空数组、每条过
|
|
248
|
-
* {@link taskAgentIssue}、name 数组内唯一、条数 ≤ {@link MAX_TASK_AGENTS}。返回 400 错误串或 null。
|
|
249
|
-
* RESUME 重放不过这道门 —— {@link taskAgentsFromBody} defensive(按条 DROP+warn,不砖 resume)。 */
|
|
250
179
|
export function validateTaskAgents(v) {
|
|
251
180
|
if (!Array.isArray(v) || v.length === 0)
|
|
252
181
|
return "agents must be a non-empty array of agent definitions";
|
|
@@ -264,14 +193,6 @@ export function validateTaskAgents(v) {
|
|
|
264
193
|
}
|
|
265
194
|
return null;
|
|
266
195
|
}
|
|
267
|
-
/** [876] body.agents → `TaskSpec.agents`(resolveSpec 的唯一装配点;main.ts 只 spread)。两层门:
|
|
268
|
-
* - 🔒 TOB 租户门:多租户(requirePrincipal===true)暂不放行 —— [876] 原文「TOB 面按租户 caps 门」,
|
|
269
|
-
* 当前没有 per-tenant caps 面可挂,先 fail-closed 关多租(warn `task_agents_ignored`,capabilities
|
|
270
|
-
* 的 `taskAgents` 同步 advertise false);单机/单用户直通(caller 是自己 worker 的 super-admin,
|
|
271
|
-
* 与 mcpInjection/hooks 同一姿势)。
|
|
272
|
-
* - defensive 归一化(RESUME 重放持久化 body 不过 HTTP 400 门):非法项按条 DROP + warn
|
|
273
|
-
* `task_agent_dropped`(绝不 throw 砖 resume);重名后到者 DROP;超上限截断。幸存字段按白名单拷贝
|
|
274
|
-
* (不透传未知键 —— 非法项已整条 drop,这里只是让持久化 body 的杂键不上 spec)。全空 ⇒ undefined。 */
|
|
275
196
|
export function taskAgentsFromBody(raw, requirePrincipal, warn) {
|
|
276
197
|
if (raw === undefined)
|
|
277
198
|
return undefined;
|
|
@@ -310,19 +231,10 @@ export function taskAgentsFromBody(raw, requirePrincipal, warn) {
|
|
|
310
231
|
}
|
|
311
232
|
return out.length > 0 ? out : undefined;
|
|
312
233
|
}
|
|
313
|
-
/** [876] main.ts resolveSpec 的 agents 装配片段(可 import 真 fold,[1.211 codex L]:此前 main.ts 用内联
|
|
314
|
-
* 闭包,测试只能 SOURCE PIN 文本+重写同构表达式——文本锚可被「移出 resolveSpec 但保留同文」静默骗过;
|
|
315
|
-
* 现在行为测试直接调这个真出口,main.ts 只剩一次调用点可锚)。语义=taskAgentsFromBody 的条件 spread:
|
|
316
|
-
* 有幸存 agents 才挂键(absent/全 drop ⇒ 空对象,spec byte-compat)。 */
|
|
317
234
|
export function taskAgentsSpecFragment(raw, requirePrincipal, warn) {
|
|
318
235
|
const agents = taskAgentsFromBody(raw, requirePrincipal, warn);
|
|
319
236
|
return agents ? { agents } : {};
|
|
320
237
|
}
|
|
321
|
-
/** [854]② — body.retainBackgroundProcesses → `TaskSpec.retainBackgroundProcesses`。core 语义(1.295
|
|
322
|
-
* runtask/task-registry dist 亲读):`=== true` 时任务收尾不主动收割 session 后台进程 —— host lane 上是
|
|
323
|
-
* 资源驻留授予,与 backgroundShell 同款单用户闸:多租户 warn 忽略(`task_retain_bg_ignored`,capability
|
|
324
|
-
* 同步 advertise false)。只 literal `true` 上 spec(false=core 缺省,不挂键保持 byte-compat);非 boolean
|
|
325
|
-
* 由 HTTP 门 400,resume 重放这里 defensive DROP(不 throw)。 */
|
|
326
238
|
export function retainBackgroundProcessesFromBody(raw, requirePrincipal, warn) {
|
|
327
239
|
if (raw !== true)
|
|
328
240
|
return undefined;
|
|
@@ -332,61 +244,28 @@ export function retainBackgroundProcessesFromBody(raw, requirePrincipal, warn) {
|
|
|
332
244
|
}
|
|
333
245
|
return true;
|
|
334
246
|
}
|
|
335
|
-
/** [1052]② — body.{excludeTools,deferTools} → `TaskSpec.{excludeTools,deferTools}`(core 1.314 工具面
|
|
336
|
-
* 控制批)。语义:excludeTools=roster 真卸载(schema 字节不上 wire,manifest 如实收窄);deferTools=
|
|
337
|
-
* design/36 延迟披露(占位上 wire,ToolSearch 激活 materialize)。两位 tighten-only(core 继承不变量:
|
|
338
|
-
* union 进全部子 spawn 路径),per-run 工具面收窄无跨租户面 ⇒ 无租户门(interactiveTools 同姿势)。
|
|
339
|
-
* DEFENSIVE(resume 重放不过 HTTP 400 门):非数组 ⇒ undefined;项非非空字符串 ⇒ 逐项 DROP;空数组 ⇒
|
|
340
|
-
* undefined(不挂键=core 缺省,byte-compat)。HTTP 门另有 fail-loud 400(提交面邻居姿势)。 */
|
|
341
247
|
export function toolNameListFromBody(raw) {
|
|
342
248
|
if (!Array.isArray(raw))
|
|
343
249
|
return undefined;
|
|
344
250
|
const names = raw.filter((v) => typeof v === "string" && v.length > 0);
|
|
345
251
|
return names.length > 0 ? names : undefined;
|
|
346
252
|
}
|
|
347
|
-
/** [1144]/[1146] — body.promptProfile → `TaskSpec.promptProfile`(core 1.328 R2 主件,CC LT(model_id)
|
|
348
|
-
* 同构双形轴)。语义:纯呈现轴不碰策略(simple=CC 212 短形面缺省;classic=长形面,可按任务/模型试分)
|
|
349
|
-
* ⇒ 无租户门(deferTools 同姿势);全委托树继承由 core 管。缺省不挂键(引擎缺省 simple,byte-compat)。
|
|
350
|
-
*
|
|
351
|
-
* ⚠️ **这不是一条 defer 注**(#277 件⑤ 对表结论,core [4352] 同判):本段说的是 `promptProfile` 这根
|
|
352
|
-
* **呈现**轴,它与 `deferMode` / `deferTools` 的延迟披露轴**无耦合** —— 上一行提 deferTools 只是在借它
|
|
353
|
-
* 的「无租户门」姿势作类比。「引擎缺省 simple」这句本身在 core 5.40.0 上仍逐字为真
|
|
354
|
-
* (`core/types.d.ts:133`:default "simple"),本批亲验未变。
|
|
355
|
-
* 真正的 defer 轴对表(core 5.40.0 起 `deferMode:"auto"` 不再 sweep 内建)记在
|
|
356
|
-
* `src/capabilities/tool-defer.ts` 的头注 —— 别把两根轴的结论写到一处。
|
|
357
|
-
* DEFENSIVE(resume 重放不过 HTTP 400 门):非法值 ⇒ undefined;提交面另有 fail-loud 400(邻居姿势)。 */
|
|
358
253
|
export function promptProfileFromBody(raw) {
|
|
359
254
|
return raw === "simple" || raw === "classic" ? raw : undefined;
|
|
360
255
|
}
|
|
361
|
-
/** [2856]②(core 5.14.0 `TaskSpec.toolMaterializeStrategy`):deferred 工具激活后的 schema 供给策略。
|
|
362
|
-
* 与 {@link promptProfileFromBody} 同姿势的**防御性归一**——提交面的 fail-loud 400 在 HTTP 门,这里
|
|
363
|
-
* 只负责 resume 重放存量 body 时把不认识的值静默 DROP(回落 core 的 env/缺省链),而不是把它带上 spec。 */
|
|
364
256
|
export function toolMaterializeStrategyFromBody(raw) {
|
|
365
257
|
return raw === "static" || raw === "swap" ? raw : undefined;
|
|
366
258
|
}
|
|
367
|
-
/** core 5.8.0 时限重构的退役键(fresh-submit 在 HTTP 门 400 点名;resume 重放在 normalize 层 DROP)。 */
|
|
368
259
|
const RETIRED_LIMIT_KEYS = ["timeoutSec", "deadlineNudge", "callCapByDeadline", "gracefulFinalize"];
|
|
369
|
-
/** 在场的退役键(normalizeLimits/resolveTaskLimits 共用判据源:留痕事件的 droppedKeys 字段)。 */
|
|
370
260
|
function retiredLimitKeysPresent(v) {
|
|
371
261
|
if (v == null || typeof v !== "object" || Array.isArray(v))
|
|
372
262
|
return [];
|
|
373
263
|
const o = v;
|
|
374
264
|
return RETIRED_LIMIT_KEYS.filter((k) => o[k] !== undefined);
|
|
375
265
|
}
|
|
376
|
-
/**
|
|
377
|
-
* [854]④ — normalize `body.limits` → 请求方配速意图 {maxWalltimeMs?, maxOutputTokens?, maxTurns?}。DEFENSIVE
|
|
378
|
-
* (resume 重放持久化 body 不过 HTTP 400 门):仅正的有限数字存活(floor 取整),0/负/垃圾按键 DROP —— core 侧
|
|
379
|
-
* 墙钟 > 0 才生效(0=不设墙,会绕过运营方封顶)、maxTurns/maxOutputTokens 的 0 无意义。每键各自被
|
|
380
|
-
* 运营方上限旋钮 clamp(Math.min;旋钮缺省不设=不封顶)。全空 ⇒ undefined。
|
|
381
|
-
*/
|
|
382
266
|
export function normalizeLimits(v, caps, warn) {
|
|
383
267
|
if (v == null || typeof v !== "object" || Array.isArray(v))
|
|
384
268
|
return undefined;
|
|
385
|
-
// core 5.8.0:键集换 maxWalltimeMs(毫秒)。旧键(timeoutSec/deadline 族三 opt-out)fresh-submit 已在
|
|
386
|
-
// HTTP 门响亮 400 点名替代;本层只走 resume 重放的持久化 body——旧键 defensive DROP 但**必须留痕**
|
|
387
|
-
// (task_limits_legacy_key_dropped):丢 caller 显式时限=收窄契约,存量 suspended 体降级为默认配速时,
|
|
388
|
-
// 单用户 turnkey 无 env 墙 ⇒ 整任务无墙钟、多租 ⇒ 被延到租户墙(2400/3600s)——两向都可能长于原意图。
|
|
389
|
-
// 绝不静默换算=那是「自行兼容」,core 明令禁止。
|
|
390
269
|
const dropped = retiredLimitKeysPresent(v);
|
|
391
270
|
if (dropped.length > 0)
|
|
392
271
|
warn?.("task_limits_legacy_key_dropped", { droppedKeys: dropped });
|
|
@@ -398,18 +277,13 @@ export function normalizeLimits(v, caps, warn) {
|
|
|
398
277
|
return cap !== undefined ? Math.min(n, cap) : n;
|
|
399
278
|
};
|
|
400
279
|
const out = {
|
|
401
|
-
// caps.timeoutSec 是秒(env TASK_TIMEOUT_MAX_SEC 保名保义),封顶毫秒键时 ×1000。
|
|
402
280
|
...(() => { const n = pick(o.maxWalltimeMs, caps.timeoutSec !== undefined ? caps.timeoutSec * 1000 : undefined); return n !== undefined ? { maxWalltimeMs: n } : {}; })(),
|
|
403
281
|
...(() => { const n = pick(o.maxOutputTokens, caps.maxOutputTokens); return n !== undefined ? { maxOutputTokens: n } : {}; })(),
|
|
404
282
|
...(() => { const n = pick(o.maxTurns, caps.maxTurns); return n !== undefined ? { maxTurns: n } : {}; })(),
|
|
405
|
-
// 166-T3:approachNotice(core 默认 ON;false=唯一关闭形,{at:[a,b]} 调阈,core 允 a<=b)。坏形整键
|
|
406
|
-
// DROP(降级为 core 默认阈,方向安全)——不 throw,resume 重放通道同门。
|
|
407
283
|
...(() => { const n = normalizeApproachNotice(o.approachNotice); return n !== undefined ? { approachNotice: n } : {}; })(),
|
|
408
284
|
};
|
|
409
285
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
410
286
|
}
|
|
411
|
-
/** 166-T3 — approachNotice 单键归一(normalizeLimits/HTTP 门共用判据源):合法 ⇒ 归一值,坏形 ⇒ undefined。
|
|
412
|
-
* 阈值规则对齐 core(prepare-task at 校验):二元组、(0,1] 有限数、a<=b(允许相等)。 */
|
|
413
287
|
export function normalizeApproachNotice(raw) {
|
|
414
288
|
if (raw === false)
|
|
415
289
|
return false;
|
|
@@ -424,22 +298,9 @@ export function normalizeApproachNotice(raw) {
|
|
|
424
298
|
const frac = (n) => typeof n === "number" && Number.isFinite(n) && n > 0 && n <= 1;
|
|
425
299
|
return frac(a) && frac(b) && a <= b ? { at: [a, b] } : undefined;
|
|
426
300
|
}
|
|
427
|
-
/**
|
|
428
|
-
* [854]④ — body.limits 与 env 墙钟的合成规则(resolveSpec 的唯一 limits 装配点;run-local 无 body 车道不经此):
|
|
429
|
-
* - `timeoutSec`:body 给了就用 body(caller 明确配速;已在 normalizeLimits 里被 TASK_TIMEOUT_MAX_SEC 封顶)。
|
|
430
|
-
* body 缺席保持既有姿势 = tenancy 墙钟 + env `TASK_TIMEOUT_SEC` 只抬不降(taskWallClockSec 内 Math.max,
|
|
431
|
-
* 误配低值不能砍 council 预算)。注意两条纪律不叠加:「只抬不降」是 env 对内建缺省墙的规则,body 是
|
|
432
|
-
* caller 的显式意图,直接生效(可低于 env 墙 —— 交互短任务要更紧的配速正是本件诉求)。
|
|
433
|
-
* - `maxOutputTokens` / `maxTurns`:直透传(各自可选 env 上限已 clamp)。
|
|
434
|
-
* 全空 ⇒ undefined(spec 不挂 limits 键,byte-compat)。
|
|
435
|
-
*/
|
|
436
301
|
export function resolveTaskLimits(bodyLimits, caps, envTimeoutSec, requirePrincipal, big, warn) {
|
|
437
|
-
// 留痕事件在本层发(不透传给 normalizeLimits,免双发):只有装配点知道最终生效墙钟——
|
|
438
|
-
// effectiveMaxWalltimeMs: null = 整任务无墙(单用户 turnkey 无 env 墙时丢弃旧 timeoutSec 的真后果)。
|
|
439
302
|
const dropped = retiredLimitKeysPresent(bodyLimits);
|
|
440
303
|
const requested = normalizeLimits(bodyLimits, caps);
|
|
441
|
-
// core 5.8.0:墙钟键=maxWalltimeMs(毫秒)。taskWallClockSec 仍以秒推导(tenancy 基值+env 只抬不降,
|
|
442
|
-
// 语义原样),装配处换算一次;body 显式给了 maxWalltimeMs 则直接生效(caller 意图,可低于 env 墙)。
|
|
443
304
|
const wallSec = taskWallClockSec(envTimeoutSec, requirePrincipal, big);
|
|
444
305
|
const maxWalltimeMs = requested?.maxWalltimeMs ?? (wallSec !== undefined ? wallSec * 1000 : undefined);
|
|
445
306
|
if (dropped.length > 0)
|
package/dist/store-live-probe.js
CHANGED
|
@@ -1,38 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* B1(鲁棒性批3 立案的设计件):/health 活体 store 探测的后台缓存环。
|
|
3
|
-
*
|
|
4
|
-
* 为什么是缓存环而不是逐请求探:/health 是 fleet center/k8s 的高频面(10-15s 一拍×N 消费方),
|
|
5
|
-
* 逐请求打 DB 会把健康检查本身变成负载源、且慢 store 直接拖垮 /health 的「必须回答此刻」承诺。
|
|
6
|
-
* 环产出的 state 由 /health 的 additive 键族消费(座注见 http/server.ts `storeLiveState`;
|
|
7
|
-
* 键形钉在 test/health-store-live.test.ts,本环判据钉在 test/store-live-probe.test.ts)。
|
|
8
|
-
*
|
|
9
|
-
* 判据:
|
|
10
|
-
* - 有界:单拍探针经 STORE_PROBE_TIMEOUT_MS race——挂死的 pool(半开连接)不得钉死环;
|
|
11
|
-
* - 翻转披露(§M):live→dead warn `store_probe_dead`、dead→live info `store_probe_recovered`,
|
|
12
|
-
* 只在翻转拍记(连续死不逐拍刷日志——down 的 DB 不该制造日志风暴);
|
|
13
|
-
* - 诚实缺席:首拍落地前 state() = undefined(/health 键缺席=「还没探过」,不冒充「活」)。
|
|
14
|
-
* - 面分级(#305):`state().error` 是**原始** message —— 它只喂给凭证后/本机的消费面;免鉴权的
|
|
15
|
-
* `/health` 一格由 wire 装配点过 {@link publicStoreProbeError} 换成闭集词(理由见该常量头注)。
|
|
16
|
-
*
|
|
17
|
-
* 探针原语:调用方给 `probe`(main.ts 传 `() => backend.dbNowMs!()`——既有 S10 时钟探针的真 DB
|
|
18
|
-
* 往返,零新 SQL 面)。timer 全部 unref(环不得阻止进程退出)。
|
|
19
|
-
*/
|
|
20
|
-
/** 单拍探针上界——挂死连接的判死时间;3s 对 15s 默认间隔留足余量(环内串行,无叠拍)。 */
|
|
21
1
|
export const STORE_PROBE_TIMEOUT_MS = 3000;
|
|
22
|
-
/**
|
|
23
|
-
* #305(安全轴):`/health` 免鉴权面上 `storeProbe.error` 可披露的**闭集词表**。
|
|
24
|
-
*
|
|
25
|
-
* 为什么必须是闭集而不是「脱敏后的原文」:`/health` 排在 `http/server.ts` 的鉴权门**之前**、缺省全网卡
|
|
26
|
-
* 监听 ⇒ 任何能连到这个端口的人都读得到这一格;而它此前装的是**驱动原始异常 message 逐字**,DB 驱动
|
|
27
|
-
* 的连接类异常惯于把整条 DSN 回声进去(`mysql://user:pass@host:3306/db`)或带上内网主机名/账号名。
|
|
28
|
-
* `trace/redact.ts` 的 `redactSecrets` **挡不住这条**:它的 URL-userinfo 规则只把口令换成 `«redacted»`,
|
|
29
|
-
* **用户名与 `@` 之后的主机原样留下** —— 而本条要防的正是那两个。闭集词是严格更强的形(值恒为下列
|
|
30
|
-
* 字面量之一,构造上不可能携带调用方文本),因此不再叠加脱敏/长度帽:对一个恒为字面量的返回值,那两道
|
|
31
|
-
* 都只是装饰。
|
|
32
|
-
*
|
|
33
|
-
* 信息不丢:**完整原文走日志轴** —— 环在 live→dead 翻转拍打的 `store_probe_dead` warn 带 `{ error }`
|
|
34
|
-
* 原文,运维在自己的日志/采集里读得到全文,那一面本来就是凭证后的。
|
|
35
|
-
*/
|
|
36
2
|
export const STORE_PROBE_PUBLIC_PHRASES = [
|
|
37
3
|
"probe_timeout",
|
|
38
4
|
"connection_refused",
|
|
@@ -42,21 +8,12 @@ export const STORE_PROBE_PUBLIC_PHRASES = [
|
|
|
42
8
|
"network_timeout",
|
|
43
9
|
"auth_failed",
|
|
44
10
|
"connection_limit",
|
|
45
|
-
/** 未识别臂 —— **刻意的**:分类不认得的异常一律丢原文取本词(fail-closed 的默认方向是「少说」,
|
|
46
|
-
* 不是「截断了再说」;截断的前 160 字恰恰是驱动最爱放 DSN 的位置)。 */
|
|
47
11
|
"probe_failed",
|
|
48
12
|
];
|
|
49
|
-
/**
|
|
50
|
-
* 原始探针异常 message → 免鉴权面可披露的闭集词。**返回值恒是 {@link STORE_PROBE_PUBLIC_PHRASES} 的
|
|
51
|
-
* 一个字面量**(返回类型即闭集,写错一个词是编译错误),永不回声入参。
|
|
52
|
-
*
|
|
53
|
-
* 误分类的代价是「运维读到的分类不精确」,**不是泄露** —— 所以这里的判别用宽松的子串包含即可,不必为
|
|
54
|
-
* 精确性把原文碎片带出来。臂序有意义:自家的 race 超时先于泛化的 timeout 臂。
|
|
55
|
-
*/
|
|
56
13
|
export function publicStoreProbeError(raw) {
|
|
57
14
|
const t = raw.toLowerCase();
|
|
58
15
|
if (t.includes("probe timed out"))
|
|
59
|
-
return "probe_timeout";
|
|
16
|
+
return "probe_timeout";
|
|
60
17
|
if (t.includes("econnrefused") || t.includes("connection refused"))
|
|
61
18
|
return "connection_refused";
|
|
62
19
|
if (t.includes("econnreset") || t.includes("connection reset"))
|
|
@@ -67,11 +24,9 @@ export function publicStoreProbeError(raw) {
|
|
|
67
24
|
return "dns_failure";
|
|
68
25
|
if (t.includes("etimedout") || t.includes("timed out") || t.includes("timeout"))
|
|
69
26
|
return "network_timeout";
|
|
70
|
-
// mysql `ER_ACCESS_DENIED_ERROR` / pg `28P01 password authentication failed` / 通用 auth 措辞。
|
|
71
27
|
if (t.includes("access denied") || t.includes("authentication failed") || t.includes("28p01") || t.includes("er_access_denied") || t.includes("er_dbaccess_denied")) {
|
|
72
28
|
return "auth_failed";
|
|
73
29
|
}
|
|
74
|
-
// mysql `ER_CON_COUNT_ERROR` / pg `53300 sorry, too many clients already` / 池耗尽措辞。
|
|
75
30
|
if (t.includes("too many connections") || t.includes("too many clients") || t.includes("53300") || t.includes("er_con_count_error") || t.includes("connection slots")) {
|
|
76
31
|
return "connection_limit";
|
|
77
32
|
}
|
|
@@ -101,17 +56,16 @@ export function createStoreLiveProbe(opts) {
|
|
|
101
56
|
clearTimeout(timer);
|
|
102
57
|
}
|
|
103
58
|
if (stopped)
|
|
104
|
-
return;
|
|
59
|
+
return;
|
|
105
60
|
const live = error === undefined;
|
|
106
61
|
const prev = last;
|
|
107
62
|
last = { live, at: Date.now(), ...(error !== undefined ? { error } : {}) };
|
|
108
|
-
// 翻转披露:首拍即死也算翻转(prev undefined → dead);连续同态不刷。
|
|
109
63
|
if (!live && prev?.live !== false)
|
|
110
64
|
opts.logger?.warn?.("store_probe_dead", { error });
|
|
111
65
|
if (live && prev?.live === false)
|
|
112
66
|
opts.logger?.info?.("store_probe_recovered", { deadForMs: Date.now() - prev.at });
|
|
113
67
|
};
|
|
114
|
-
void probeOnce();
|
|
68
|
+
void probeOnce();
|
|
115
69
|
const loop = setInterval(() => void probeOnce(), opts.intervalMs);
|
|
116
70
|
loop.unref?.();
|
|
117
71
|
return {
|
package/dist/task-a2a.js
CHANGED
|
@@ -1,72 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [DESIGN-269 车1 §2.2/§2.3 — A2A client seam] — project a caller's per-request A2A peers
|
|
3
|
-
* (`TaskRequest.a2aPeers`) into the engine, so a `sema` run can talk to the agents the USER declares,
|
|
4
|
-
* not only the ones the deployment's config center declares. Point-for-point mirror of `task-mcp.ts`
|
|
5
|
-
* (the MCP seam), because the trust question is the same question — and deliberately NOT a copy where
|
|
6
|
-
* the two protocols differ (see 「A2A 比 MCP 窄」 below).
|
|
7
|
-
*
|
|
8
|
-
* 🔒 SECURITY — an A2A peer is a remote **AGENT**, not a data source: it acts on its own side. core ruled
|
|
9
|
-
* (2026-08-03, `A2aServerSpec` doc) that every skill such a peer advertises mounts with `egress: true` +
|
|
10
|
-
* `effect: "write"`, i.e.接入即引入对外写通道 — it registers the approval gate, is never auto-allowed on a
|
|
11
|
-
* no-policy deployment, and its replies reach the model inside an untrusted-data fence. The trust boundary
|
|
12
|
-
* is therefore the SAME one the MCP seam draws, for the same reason:
|
|
13
|
-
* - SINGLE-USER deployment (`requirePrincipal !== false`… i.e. `!== true`): the requester IS the
|
|
14
|
-
* super-admin of their own worker, so the peers they name are their own choice (CC-parity), on EVERY
|
|
15
|
-
* execution lane — an A2A call is a plain outbound HTTPS request made by the worker, so the lane where
|
|
16
|
-
* the agent's TOOL CALLS run is irrelevant (exactly the fact that decoupled the MCP gate from the lane).
|
|
17
|
-
* - MULTI-TENANT deployment (`requirePrincipal === true`): a tenant must NOT make the SHARED worker POST
|
|
18
|
-
* to a body-chosen URL (SSRF) carrying body-chosen headers, nor mount write-capable tools from an agent
|
|
19
|
-
* the operator never vetted. Gate CLOSED → body peers ignored; the fleet gets peers only through center
|
|
20
|
-
* config (`config.a2aPeers`).
|
|
21
|
-
* When the gate is closed a sent `a2aPeers` is IGNORED — and SAID (`a2a_injection_ignored` + the
|
|
22
|
-
* `capabilities.a2aInjection` advertisement), never silently swallowed.
|
|
23
|
-
*
|
|
24
|
-
* ⚠️ **A2A 的轴比 MCP 窄,这不是简化而是协议事实**:A2A has no per-skill annotation vocabulary, so there is
|
|
25
|
-
* no server-hint leg to fold — `toolAxes` (the CALLER's own judgement) is the ONLY thing that can move an axis
|
|
26
|
-
* off the fail-closed default. core says as much on `A2aToolAxis`: `irreversibility:"never"` / `egress:false`
|
|
27
|
-
* 「appears ONLY via an explicit caller override」, enforcement ignores the loosening (tighten-only) and the
|
|
28
|
-
* ask's risk-axes REPORT face consumes it. So this module carries the caller's overrides through verbatim —
|
|
29
|
-
* they are a judgement, not silence — while knowing that core will not let them widen enforcement.
|
|
30
|
-
*
|
|
31
|
-
* ⚠️ **两票判据的词表在 core 5.36 尚未到货**(分单件 C-1/C-2,core 已认领排 5.37 后首个 A2A 窗):
|
|
32
|
-
* `LockedKey` 是 `"mcp"|"toolPolicy"|"compliancePosture"|"retentionPolicy"`,`ComplianceCapability` 是
|
|
33
|
-
* `"mcp_servers"|"workflows"|"web_fetch"|"org_memory_mount"` —— 两张表里都还没有 A2A 的位。本模块因此按
|
|
34
|
-
* **字符串**判(server config 的 `lockedConfigKeys` 本就是 server 自己的声明面,不经 core 闭集型),
|
|
35
|
-
* 判据一到货就换 core 真源、钉子逐字不用改。
|
|
36
|
-
* 🔴 **今天这两票在 env 通道上还打不响**,而这是刻意如实记下的、不是被忽略的缺口:`LOCKED_CONFIG_KEYS`
|
|
37
|
-
* 经 core 的 `resolveLockedKeys` 校验(未知键拒启)、`COMPLIANCE_ADDITIONAL_DENIES` 经 `COMPLIANCE_CAPABILITIES`
|
|
38
|
-
* 校验(同拒),所以运维今天**写不进** `a2a` / `a2a_peers`。而且合规那一票还有第二层:即便有人绕过 boot 期
|
|
39
|
-
* 校验把 `a2a_peers` 塞进档位,`resolveComplianceDenies` 对闭集外的词是**抛**(亲测 core 5.36),于是这条
|
|
40
|
-
* 请求腿的失败方向是**响亮**(500)而不是安静放行 —— 方向正确,故本模块不加任何兜底。两票的代码先在场是
|
|
41
|
-
* 纵深:core 词表到货那一拍这条腿自己就活了,不需要有人记得回来补;反过来(等词表到了再写判据)才是
|
|
42
|
-
* 「广告了却拦不住」的那一类缺口。钉在 `test/task-a2a.test.ts`(含到货后该怎么改那一格的原地说明)。
|
|
43
|
-
*/
|
|
44
1
|
import { resolveComplianceDenies } from "@sema-agent/core";
|
|
45
2
|
import { HttpError } from "./security.js";
|
|
46
|
-
/** Bound on caller-supplied peers (anti-DoS; a real declaration names a handful of agents). */
|
|
47
3
|
export const MAX_REQUEST_A2A_PEERS = 32;
|
|
48
|
-
/**
|
|
49
|
-
* Does THIS deployment honor caller-supplied per-request A2A peers? Three vetoes, ALL of them here (this
|
|
50
|
-
* predicate is the single owner — the capability bit and the request leg both read it, so there is exactly
|
|
51
|
-
* one place where the answer can be wrong):
|
|
52
|
-
* ① the deployment locks the `a2a` key — a locked deployment that advertised `a2aInjection:true` would
|
|
53
|
-
* hand every consumer an affordance whose every use 400s (`config.locked_key`); "says yes ⟺ route
|
|
54
|
-
* works" breaks on the spot. Same conjunction, same reason, as `mcpInjectionHonored`'s lock veto;
|
|
55
|
-
* ② the compliance posture denies `a2a_peers` — advertised-but-refused again;
|
|
56
|
-
* ③ multi-tenant (`requirePrincipal === true`) — a tenant cannot point the shared worker at an agent.
|
|
57
|
-
*
|
|
58
|
-
* ⚠️ **`resolveComplianceDenies` is deliberately NOT wrapped in try/catch** (same ruling as the MCP twin):
|
|
59
|
-
* it is fail-loud on a posture outside the closed set, and the posture has already been validated twice
|
|
60
|
-
* before reaching here (config parse + boot assembly). If it ever throws here, someone bypassed both doors
|
|
61
|
-
* and the LOUD direction (route 500) is the correct one — swallowing it into a boolean would let a
|
|
62
|
-
* deployment with a broken posture keep advertising the capability (仓规:安全轴禁静默兜底).
|
|
63
|
-
*/
|
|
64
4
|
export function a2aInjectionHonored(config) {
|
|
65
5
|
if (config.lockedConfigKeys?.includes("a2a") === true)
|
|
66
6
|
return false;
|
|
67
7
|
if (config.compliancePosture !== undefined) {
|
|
68
|
-
// core 词表到货前的读法:闭集 Set 按 string 读(`ReadonlySet<ComplianceCapability>` 的 `has` 是方法,
|
|
69
|
-
// 结构上可当 `ReadonlySet<string>` 读)。到货后这一行换成 `.has("a2a_peers")` 的闭集形。
|
|
70
8
|
const denies = resolveComplianceDenies(config.compliancePosture);
|
|
71
9
|
if (denies.has("a2a_peers"))
|
|
72
10
|
return false;
|
|
@@ -76,25 +14,7 @@ export function a2aInjectionHonored(config) {
|
|
|
76
14
|
function isStringRecord(v) {
|
|
77
15
|
return !!v && typeof v === "object" && !Array.isArray(v) && Object.values(v).every((x) => typeof x === "string");
|
|
78
16
|
}
|
|
79
|
-
/** http(s) only — never `file://`, never a bare host. A syntactic guard on BOTH URL-shaped fields: the
|
|
80
|
-
* peer URL doubles as the well-known card origin, and `cardUrl` is dialed on its own when present. */
|
|
81
17
|
const HTTP_URL_RE = /^https?:\/\//i;
|
|
82
|
-
/**
|
|
83
|
-
* Validate + normalize ONE caller entry to a core `A2aServerSpec`, or null if malformed (that peer is dropped
|
|
84
|
-
* and NAMED in `dropped` — never fatal to the request).
|
|
85
|
-
*
|
|
86
|
-
* 🔴 **判据一句话:一个键在场就必须合法,否则这条 peer 整条不进** —— 不做「丢那个键、留这条 peer」。
|
|
87
|
-
* (codex 对抗复审 finding 1,验真后采纳。)理由是方向性的,不是洁癖:
|
|
88
|
-
* · `allowSkills` 是**收窄**字段,而 core 把它的**缺席**解释成「不限制」。于是「丢坏键」在这条字段上
|
|
89
|
-
* 等于「调用方要求只挂一条,实际挂上远端卡声明的全部技能」—— 而每条 A2A 技能默认 `egress:true` +
|
|
90
|
-
* `effect:"write"`。收窄意图坏形时退化成放行 = 仓规点名的静默 fail-open。
|
|
91
|
-
* · `toolAxes` 的**半应用**同样有害:留下 `effect:"read"` 而丢掉坏形的 `egress` 会拼出 core 明文判为
|
|
92
|
-
* 矛盾的组合(「降 effect 却没清 egress」),把一个形状问题变成一条更难读的 prepare 失败。
|
|
93
|
-
* · 逐字段例外表是下一个漏洞的藏身处;一句话判据全域一致,而且与 `resolveA2aPeers`(缺任一 env 引用
|
|
94
|
-
* 即整条跳过)是**同一条**纪律:绝不半配置地拨号一个远端 agent。
|
|
95
|
-
* 代价明码:一个 header 打错字的调用方会整条丢掉这个 peer(带名字进 warn 日志),而不是匿名连上去。
|
|
96
|
-
* 那正是更好的失败方向。
|
|
97
|
-
*/
|
|
98
18
|
function normalizeA2aPeer(e) {
|
|
99
19
|
if (!e || typeof e !== "object")
|
|
100
20
|
return null;
|
|
@@ -103,9 +23,6 @@ function normalizeA2aPeer(e) {
|
|
|
103
23
|
return null;
|
|
104
24
|
if (typeof o.url !== "string" || !HTTP_URL_RE.test(o.url))
|
|
105
25
|
return null;
|
|
106
|
-
// `cardUrl` in place must be WELL-FORMED — a garbage card URL is not "fall back to well-known probing"
|
|
107
|
-
// (core treats a set cardUrl as the ONLY location, deliberately: no probing behind the operator's back),
|
|
108
|
-
// so silently dropping it would change WHICH endpoint gets dialed.
|
|
109
26
|
if (o.cardUrl !== undefined && (typeof o.cardUrl !== "string" || !HTTP_URL_RE.test(o.cardUrl)))
|
|
110
27
|
return null;
|
|
111
28
|
if (o.headers !== undefined && !isStringRecord(o.headers))
|
|
@@ -124,10 +41,6 @@ function normalizeA2aPeer(e) {
|
|
|
124
41
|
if (Array.isArray(o.allowSkills))
|
|
125
42
|
spec.allowSkills = o.allowSkills;
|
|
126
43
|
if (o.toolAxes !== undefined) {
|
|
127
|
-
// 逐键形状校验(不是裸 cast):值语义仍由 caller 定(caller = 信任根),只挡畸形值直达 core。
|
|
128
|
-
// A2A 词表 = core `A2aToolAxis` 的三轴,与 MCP 同域但**没有 server-hint 腿**可折——这里收下的
|
|
129
|
-
// 就是最终值。`"never"` / `egress:false` 只可能来自这条显式覆写腿(core 原话),enforcement 忽略
|
|
130
|
-
// 这个方向的放宽(tighten-only),风险轴报告面消费它。坏形按上面那句话整条拒。
|
|
131
44
|
if (!o.toolAxes || typeof o.toolAxes !== "object" || Array.isArray(o.toolAxes))
|
|
132
45
|
return null;
|
|
133
46
|
const axes = {};
|
|
@@ -154,10 +67,6 @@ function normalizeA2aPeer(e) {
|
|
|
154
67
|
}
|
|
155
68
|
return spec;
|
|
156
69
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Shape-check the raw `body.a2aPeers`. `null` → a non-array (caller error; the route may 400). Otherwise the
|
|
159
|
-
* valid subset + the names of dropped (malformed/over-cap) entries.
|
|
160
|
-
*/
|
|
161
70
|
export function validateRequestA2a(raw) {
|
|
162
71
|
if (raw === undefined || raw === null)
|
|
163
72
|
return { ok: [], dropped: [] };
|
|
@@ -169,8 +78,6 @@ export function validateRequestA2a(raw) {
|
|
|
169
78
|
const v = normalizeA2aPeer(e);
|
|
170
79
|
if (v)
|
|
171
80
|
ok.push(v);
|
|
172
|
-
// 名字是**调用方文本**,而「名字超长」本身就是被丢的理由之一 —— 原样递给 logger 等于让一条被拒的
|
|
173
|
-
// 请求决定日志行有多大(自审第四镜)。限长到 name 上限的量级,截断即止。
|
|
174
81
|
else
|
|
175
82
|
dropped.push(e && typeof e === "object" && typeof e.name === "string" ? String(e.name).slice(0, 128) : "<malformed>");
|
|
176
83
|
}
|
|
@@ -178,20 +85,6 @@ export function validateRequestA2a(raw) {
|
|
|
178
85
|
dropped.push(`(+${raw.length - MAX_REQUEST_A2A_PEERS} over cap of ${MAX_REQUEST_A2A_PEERS})`);
|
|
179
86
|
return { ok, dropped };
|
|
180
87
|
}
|
|
181
|
-
/**
|
|
182
|
-
* DESIGN-269 §2.4 —— **同步**拒面:部署锁了 `a2a` 而请求仍带 `a2aPeers` ⇒ 400 `config.locked_key`
|
|
183
|
-
* (逐字沿用 `assertRequestMcpUnlocked` 的裁定,连错误码都同一个 —— 消费端判的是「某个键被行政锁住了」,
|
|
184
|
-
* 不是「哪个键」;码分裂只会让 SDK 多写一条等价分支)。
|
|
185
|
-
*
|
|
186
|
-
* 判据是「请求**占位**」而不是「请求的值合不合法」:锁是两态的,占了就整拒,不静默丢(静默丢正是把
|
|
187
|
-
* 判决变成探针的那一形)。键缺席 / 显式 `null` 都不是占位;**空数组是占位**(调用方确实写了这个键)。
|
|
188
|
-
*
|
|
189
|
-
* ⚠️ **不看车道**:多租户腿本来就会忽略 body peers(`a2aInjectionHonored` 关),但锁在场时「忽略」是
|
|
190
|
-
* 错的失败方向 —— operator 声明了「本部署不收任务自带 A2A peer」,那就该说出来。
|
|
191
|
-
*
|
|
192
|
-
* ⚠️ 参数类型是 `ReadonlySet<string>`:core 的 `LockedKey` 尚无 `a2a` 成员(见模块头),而
|
|
193
|
-
* `ReadonlySet<LockedKey>` 结构上可当 `ReadonlySet<string>` 传入,所以调用点无需任何转换。
|
|
194
|
-
*/
|
|
195
88
|
export function assertRequestA2aUnlocked(bodyA2a, lockedKeys) {
|
|
196
89
|
if (!lockedKeys.has("a2a"))
|
|
197
90
|
return;
|
|
@@ -201,13 +94,6 @@ export function assertRequestA2aUnlocked(bodyA2a, lockedKeys) {
|
|
|
201
94
|
code: "config.locked_key",
|
|
202
95
|
});
|
|
203
96
|
}
|
|
204
|
-
/**
|
|
205
|
-
* Merge GATED per-request peers OVER the deployment baseline (center/config). The baseline WINS on a name
|
|
206
|
-
* clash — a caller can ADD a peer but can never SHADOW a configured one. This is load-bearing on THIS lane
|
|
207
|
-
* specifically: the peer name is the tool-namespace segment (`a2a__<peer>__<skill>`), so shadowing would let
|
|
208
|
-
* a caller keep the tool NAMES the model was told about while swapping the agent behind them.
|
|
209
|
-
* Returns the baseline unchanged when there is nothing to add.
|
|
210
|
-
*/
|
|
211
97
|
export function mergeRequestA2a(baseline, gated) {
|
|
212
98
|
if (gated.length === 0)
|
|
213
99
|
return baseline;
|
|
@@ -215,18 +101,7 @@ export function mergeRequestA2a(baseline, gated) {
|
|
|
215
101
|
const add = gated.filter((p) => !taken.has(p.name));
|
|
216
102
|
return add.length === 0 ? baseline : [...(baseline ?? []), ...add];
|
|
217
103
|
}
|
|
218
|
-
|
|
219
|
-
* The single entry point the spec-builder calls: compute the effective `TaskSpec.a2a` = deployment baseline
|
|
220
|
-
* (`a2aForScenario(config.a2aPeers, scenario)`) + the caller's gated per-request peers. Off the honored lane
|
|
221
|
-
* (or with no caller peers) returns the baseline unchanged. Logs `a2a_injection_ignored` when a caller SENT
|
|
222
|
-
* peers a closed gate dropped, so a shell never silently believes its declaration took effect (it also reads
|
|
223
|
-
* `capabilities.a2aInjection`).
|
|
224
|
-
*/
|
|
225
|
-
export function resolveRequestA2a(baseline, bodyA2a,
|
|
226
|
-
// `lockedConfigKeys` rides in with the config: `assertRequestA2aUnlocked` already refuses at the leg head,
|
|
227
|
-
// so the lock veto below is unreachable in the locked shape — it stays as depth (ONE predicate owns the
|
|
228
|
-
// question; two half-answers in two places is how the two drift apart).
|
|
229
|
-
config, logger) {
|
|
104
|
+
export function resolveRequestA2a(baseline, bodyA2a, config, logger) {
|
|
230
105
|
if (bodyA2a === undefined || bodyA2a === null)
|
|
231
106
|
return baseline;
|
|
232
107
|
if (!a2aInjectionHonored(config)) {
|