@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
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { probeMcpServers
|
|
1
|
+
import { probeMcpServers } from "@sema-agent/core";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { createFixedWindowRateLimiter } from "../device-enrollment.js";
|
|
4
4
|
import { canonicalJson } from "../canonical-json.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { toCoreMcpServerSpec } from "../mcp-assertion-header.js";
|
|
9
|
-
import { buildDisplaySafeUrl } from "../trace/redact.js";
|
|
5
|
+
import { keyClosureClause, scanKeyClosure } from "../request-key-closure.js";
|
|
6
|
+
import { requestMcpDeclarationRefusal, validateRequestMcp } from "../task-mcp.js";
|
|
7
|
+
import { declaresMcpAssertionHeader, toCoreMcpServerSpec } from "../mcp-assertion-header.js";
|
|
10
8
|
import { projectWiringManifestMcpRows } from "../trace/project.js";
|
|
11
9
|
export const MCP_PROBE_REFUSAL_CODES = [
|
|
12
10
|
"capability.mcp_injection_required",
|
|
@@ -42,113 +40,6 @@ const shapeRefusal = (message, unknownKeys) => ({
|
|
|
42
40
|
...(unknownKeys !== undefined ? { unknownKeys } : {}),
|
|
43
41
|
});
|
|
44
42
|
const fieldRefusal = (message) => ({ errorCode: "request.field_invalid", message });
|
|
45
|
-
function entryKeyIssue(entry, at) {
|
|
46
|
-
const buckets = { unknown: [], unsupported: [] };
|
|
47
|
-
scanKeyClosure(entry, `${at}.`, new Set(MCP_SERVER_ENTRY_KEYS), NO_TOLERATED_KEYS, buckets);
|
|
48
|
-
const t = entry.transport;
|
|
49
|
-
if (t !== null && typeof t === "object" && !Array.isArray(t)) {
|
|
50
|
-
const kind = t.kind;
|
|
51
|
-
const keys = kind === "stdio" ? STDIO_MCP_TRANSPORT_KEYS : kind === "http" ? HTTP_MCP_TRANSPORT_KEYS : undefined;
|
|
52
|
-
if (keys !== undefined)
|
|
53
|
-
scanKeyClosure(t, `${at}.transport.`, new Set(keys), NO_TOLERATED_KEYS, buckets);
|
|
54
|
-
}
|
|
55
|
-
if (buckets.unknown.length === 0) {
|
|
56
|
-
const refusal = requestEntryIdentityRefusal(entry);
|
|
57
|
-
if (refusal === undefined)
|
|
58
|
-
return null;
|
|
59
|
-
return refusal.errorCode === "request.body_shape" ? shapeRefusal(refusal.message) : fieldRefusal(refusal.message);
|
|
60
|
-
}
|
|
61
|
-
buckets.unknown.sort();
|
|
62
|
-
return shapeRefusal(`body: unrecognized key(s) on an MCP declaration — refused rather than dropped, because a dropped key reads as "in effect" to the caller. ` +
|
|
63
|
-
`${keyClosureClause(buckets.unknown, "unknown (not an mcpServers[] key — check the spelling)")}. ` +
|
|
64
|
-
`Accepted per entry: ${[...MCP_SERVER_ENTRY_KEYS].sort().join(",")} (transport.kind "stdio": ${[...STDIO_MCP_TRANSPORT_KEYS].sort().join(",")}; "http": ${[...HTTP_MCP_TRANSPORT_KEYS].sort().join(",")})`, buckets.unknown);
|
|
65
|
-
}
|
|
66
|
-
function entryValueIssue(entry, at) {
|
|
67
|
-
const axesRefusal = requestEntryToolAxesRefusal(entry, at);
|
|
68
|
-
if (axesRefusal !== undefined)
|
|
69
|
-
return fieldRefusal(axesRefusal.message);
|
|
70
|
-
const read = validateRequestMcp([entry]);
|
|
71
|
-
if (read === null)
|
|
72
|
-
return fieldRefusal(`${at} is not a readable MCP declaration`);
|
|
73
|
-
const dropped = read.dropped[0];
|
|
74
|
-
if (dropped !== undefined || read.ok.length !== 1) {
|
|
75
|
-
const field = dropped?.field ?? "entry";
|
|
76
|
-
return fieldRefusal(`${at}.${field} is not a usable value${urlEchoFor(entry, field)} — this endpoint refuses the whole list rather than dialing a silently shortened one` +
|
|
77
|
-
` (a task submission would instead drop just this declaration and mount its siblings; a probe answering "can I use these" must not quietly answer about fewer)`);
|
|
78
|
-
}
|
|
79
|
-
if (read.badContentOrigin.length > 0) {
|
|
80
|
-
return fieldRefusal(`${at}.contentOrigin must be one of ${CONTENT_ORIGIN_WORDS.map((w) => `"${w}"`).join(" | ")} — got ${describeBadContentOrigin(entry.contentOrigin)} (omit the key for the default: this server's tools classify as external content)`);
|
|
81
|
-
}
|
|
82
|
-
const read0 = read.ok[0];
|
|
83
|
-
return declarationDowngradeIssue(entry, read0 === undefined ? undefined : toCoreMcpServerSpec(read0), at);
|
|
84
|
-
}
|
|
85
|
-
function declarationDowngradeIssue(entry, spec, at) {
|
|
86
|
-
if (spec === undefined)
|
|
87
|
-
return fieldRefusal(`${at} is not a readable MCP declaration`);
|
|
88
|
-
const diff = jsonPathDiff(stripEquivalentEmpties(declaredAsJudged(entry)), stripEquivalentEmpties(spec), at);
|
|
89
|
-
if (diff.length === 0)
|
|
90
|
-
return null;
|
|
91
|
-
diff.sort();
|
|
92
|
-
return fieldRefusal(`${keyClosureClause(diff, "unusable value(s)")} — this deployment's declaration reader could not use these exactly as written, and a probe must not dial a configuration that differs from the one you declared` +
|
|
93
|
-
" (a task submission would instead keep the server and silently drop just these, which is why the two faces disagree here on purpose; this endpoint refuses, because the reading would then describe a server you did not ask about)");
|
|
94
|
-
}
|
|
95
|
-
function declaredAsJudged(entry) {
|
|
96
|
-
const declared = entry;
|
|
97
|
-
if (!("toolAxes" in declared))
|
|
98
|
-
return declared;
|
|
99
|
-
const read = readMcpToolAxes(declared.toolAxes);
|
|
100
|
-
if (!read.ok)
|
|
101
|
-
return declared;
|
|
102
|
-
const { toolAxes: _declaredAxes, ...rest } = declared;
|
|
103
|
-
return read.toolAxes === undefined ? rest : { ...rest, toolAxes: read.toolAxes };
|
|
104
|
-
}
|
|
105
|
-
function stripEquivalentEmpties(entry) {
|
|
106
|
-
const emptyRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0;
|
|
107
|
-
const emptyArray = (v) => Array.isArray(v) && v.length === 0;
|
|
108
|
-
const keep = (bag, recordKeys, arrayKeys) => {
|
|
109
|
-
const out = {};
|
|
110
|
-
for (const [k, v] of Object.entries(bag)) {
|
|
111
|
-
if (recordKeys.includes(k) && emptyRecord(v))
|
|
112
|
-
continue;
|
|
113
|
-
if (arrayKeys.includes(k) && emptyArray(v))
|
|
114
|
-
continue;
|
|
115
|
-
out[k] = v;
|
|
116
|
-
}
|
|
117
|
-
return out;
|
|
118
|
-
};
|
|
119
|
-
const top = keep(entry, MCP_ENTRY_EMPTY_RECORD_KEYS, MCP_ENTRY_EMPTY_ARRAY_KEYS);
|
|
120
|
-
const t = top.transport;
|
|
121
|
-
if (t !== null && typeof t === "object" && !Array.isArray(t)) {
|
|
122
|
-
top.transport = keep(t, MCP_TRANSPORT_EMPTY_RECORD_KEYS, MCP_TRANSPORT_EMPTY_ARRAY_KEYS);
|
|
123
|
-
}
|
|
124
|
-
return top;
|
|
125
|
-
}
|
|
126
|
-
function jsonPathDiff(a, b, path) {
|
|
127
|
-
if (canonicalJson(a) === canonicalJson(b))
|
|
128
|
-
return [];
|
|
129
|
-
const aRec = a !== null && typeof a === "object" && !Array.isArray(a) ? a : undefined;
|
|
130
|
-
const bRec = b !== null && typeof b === "object" && !Array.isArray(b) ? b : undefined;
|
|
131
|
-
if (aRec !== undefined && bRec !== undefined) {
|
|
132
|
-
const out = [];
|
|
133
|
-
for (const k of new Set([...Object.keys(aRec), ...Object.keys(bRec)]))
|
|
134
|
-
out.push(...jsonPathDiff(aRec[k], bRec[k], `${path}.${k}`));
|
|
135
|
-
return out.length > 0 ? out : [path];
|
|
136
|
-
}
|
|
137
|
-
if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
|
|
138
|
-
const out = [];
|
|
139
|
-
for (let i = 0; i < a.length; i++)
|
|
140
|
-
out.push(...jsonPathDiff(a[i], b[i], `${path}[${i}]`));
|
|
141
|
-
return out.length > 0 ? out : [path];
|
|
142
|
-
}
|
|
143
|
-
return [path];
|
|
144
|
-
}
|
|
145
|
-
function urlEchoFor(entry, field) {
|
|
146
|
-
if (field !== "transport.url")
|
|
147
|
-
return "";
|
|
148
|
-
const t = entry.transport;
|
|
149
|
-
const raw = t !== null && typeof t === "object" ? t.url : undefined;
|
|
150
|
-
return typeof raw === "string" ? ` (got "${buildDisplaySafeUrl(raw)}"; an MCP declaration's address must be an http(s) URL — put credentials in transport.headers, never in the URL)` : "";
|
|
151
|
-
}
|
|
152
43
|
export function readMcpProbeBody(raw) {
|
|
153
44
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
154
45
|
return { ok: false, refusal: shapeRefusal("body must be a JSON object of the shape { mcpServers: [ … ] } (the same declaration list a task submits)") };
|
|
@@ -164,32 +55,30 @@ export function readMcpProbeBody(raw) {
|
|
|
164
55
|
};
|
|
165
56
|
}
|
|
166
57
|
const list = raw.mcpServers;
|
|
167
|
-
if (
|
|
168
|
-
return { ok: false, refusal: shapeRefusal("mcpServers
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
for (let i = 0; i < list.length; i++) {
|
|
173
|
-
const at = `mcpServers[${i}]`;
|
|
174
|
-
const entry = list[i];
|
|
175
|
-
if (entry === null || typeof entry !== "object" || Array.isArray(entry))
|
|
176
|
-
return { ok: false, refusal: shapeRefusal(`${at} must be an object`) };
|
|
177
|
-
const keyIssue = entryKeyIssue(entry, at);
|
|
178
|
-
if (keyIssue)
|
|
179
|
-
return { ok: false, refusal: keyIssue };
|
|
180
|
-
const valueIssue = entryValueIssue(entry, at);
|
|
181
|
-
if (valueIssue)
|
|
182
|
-
return { ok: false, refusal: valueIssue };
|
|
183
|
-
}
|
|
58
|
+
if (list === undefined || list === null)
|
|
59
|
+
return { ok: false, refusal: shapeRefusal("mcpServers is required: the declaration list to probe (send [] to ask about nothing)") };
|
|
60
|
+
const refusal = requestMcpDeclarationRefusal(list);
|
|
61
|
+
if (refusal !== undefined)
|
|
62
|
+
return { ok: false, refusal };
|
|
184
63
|
const validated = validateRequestMcp(list);
|
|
185
|
-
|
|
64
|
+
const declared = Array.isArray(list) ? list.length : 0;
|
|
65
|
+
if (validated === null || validated.ok.length !== declared) {
|
|
186
66
|
return {
|
|
187
67
|
ok: false,
|
|
188
|
-
refusal: fieldRefusal(`mcpServers: this deployment's declaration reader
|
|
189
|
-
" so the probe refuses rather than dialing a silently shortened list. This is a belt on a path that should not be reachable (the reader
|
|
68
|
+
refusal: fieldRefusal(`mcpServers: this deployment's declaration reader kept ${validated === null ? 0 : validated.ok.length} of the ${declared} declarations its judge accepted,` +
|
|
69
|
+
" so the probe refuses rather than dialing a silently shortened list. This is a belt on a path that should not be reachable (the judge and the reader read every key with the same predicate)," +
|
|
190
70
|
" so nothing in your body needs changing — if you see it, this deployment has a bug worth reporting"),
|
|
191
71
|
};
|
|
192
72
|
}
|
|
73
|
+
const carried = validated.ok.findIndex(declaresMcpAssertionHeader);
|
|
74
|
+
if (carried >= 0) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
refusal: fieldRefusal(`mcpServers[${carried}].transport.assertionHeader is declared, but this engine does not send an assertion header yet — a task would mount the server WITHOUT it, ` +
|
|
78
|
+
"so a probe dialing it would describe a configuration you did not declare (a server that answers the handshake and asks for the assertion only on calls would read as connected). " +
|
|
79
|
+
"Probe the entry without the key to see how the server answers without it"),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
193
82
|
return { ok: true, specs: validated.ok.map(toCoreMcpServerSpec) };
|
|
194
83
|
}
|
|
195
84
|
export function buildMcpProbeIdentity(principal) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { REPO_PULL_DIFF_TOOL_NAME, REPO_READ_FILE_TOOL_NAME, REPO_TREE_TOOL_NAME } from "./host-tool-names.js";
|
|
3
4
|
export function parseRepo(input) {
|
|
4
5
|
const s = input.trim().replace(/\.git$/, "");
|
|
5
6
|
const m = /^[a-z]+:\/\/[^/]+\/(.+)$/i.exec(s);
|
|
@@ -160,7 +161,7 @@ export function repoToolsFor(client, coords) {
|
|
|
160
161
|
const ref = (r) => (r ? Promise.resolve(r) : client.defaultBranch(coords));
|
|
161
162
|
return [
|
|
162
163
|
{
|
|
163
|
-
name:
|
|
164
|
+
name: REPO_TREE_TOOL_NAME,
|
|
164
165
|
description: "List file paths in the repository (recursive). Use this first to understand structure, then read specific files.",
|
|
165
166
|
effect: "read",
|
|
166
167
|
parameters: Type.Object({ ref: Type.Optional(Type.String({ description: "branch/tag/sha; default = repo default branch" })) }),
|
|
@@ -171,7 +172,7 @@ export function repoToolsFor(client, coords) {
|
|
|
171
172
|
},
|
|
172
173
|
},
|
|
173
174
|
{
|
|
174
|
-
name:
|
|
175
|
+
name: REPO_READ_FILE_TOOL_NAME,
|
|
175
176
|
description: "Read one file's text from the repository. Read only the files you need (just-in-time).",
|
|
176
177
|
effect: "read",
|
|
177
178
|
parameters: Type.Object({ path: Type.String(), ref: Type.Optional(Type.String()) }),
|
|
@@ -181,7 +182,7 @@ export function repoToolsFor(client, coords) {
|
|
|
181
182
|
},
|
|
182
183
|
},
|
|
183
184
|
{
|
|
184
|
-
name:
|
|
185
|
+
name: REPO_PULL_DIFF_TOOL_NAME,
|
|
185
186
|
description: "Get the unified diff of a pull request by its number (for reviewing a PR's changes).",
|
|
186
187
|
effect: "read",
|
|
187
188
|
parameters: Type.Object({ index: Type.Integer({ description: "the PR number" }) }),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WebFetchConfig, type BackgroundAgentStore, type CheckpointStore, type
|
|
1
|
+
import { type WebFetchConfig, type BackgroundAgentStore, type CheckpointStore, type Runner, type SkillSpec, type TaskListStore, type TaskSpec, type ToolSpec, type WebSearchConfig } from "@sema-agent/core";
|
|
2
2
|
import type { Logger } from "../observability/logger.js";
|
|
3
3
|
import { type SubagentExtraToolSet } from "./builtin-tools.js";
|
|
4
4
|
import { type RepoReadClient } from "./repo-tools.js";
|
|
@@ -6,8 +6,8 @@ import { type LoadedSkill } from "./skills.js";
|
|
|
6
6
|
import type { ScenarioHands } from "./hands-lane.js";
|
|
7
7
|
/**
|
|
8
8
|
* Scenario routing — a deployment serves many scenarios from one image. Each request's `scenario`
|
|
9
|
-
* selects a pre-wired capability bundle: tools (incl. subagent roster) +
|
|
10
|
-
*
|
|
9
|
+
* selects a pre-wired capability bundle: tools (incl. subagent roster) + its prompt seats + skills.
|
|
10
|
+
* Heavy bits (HTTP clients, skill files) are startup singletons; per task we only bind
|
|
11
11
|
* coordinates and build cheap closures, so there is no per-request "assembly" cost.
|
|
12
12
|
*/
|
|
13
13
|
export interface ScenarioRequest {
|
|
@@ -26,12 +26,33 @@ export interface ScenarioBundle {
|
|
|
26
26
|
* `scan` 声明 clone-free 四件、实际拿到全量可写手)。
|
|
27
27
|
*/
|
|
28
28
|
hands: ScenarioHands;
|
|
29
|
-
|
|
29
|
+
/** 本场景对提示的贡献(缺席 = 不贡献:引擎目录的缺省角色)。经 {@link buildPromptSeats} 写进 `TaskSpec`,这是场景到提示的唯一通道。 */
|
|
30
|
+
prompt?: ScenarioPrompt;
|
|
30
31
|
/** [ref] 场景解释层定死的终验开关(core TaskSpec.finalVerification):bench 实测产品默认请求与
|
|
31
32
|
* bench 态差距三件之一。[ref] 起无场景定死(autonomous 退役)——纯 caller 显式旋钮;spec 组装处 OR 折入(场景只会
|
|
32
33
|
* 多加一轮收官验证,绝不收窄 caller 意图),壳/caller 不感知。 */
|
|
33
34
|
finalVerification?: true;
|
|
34
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 一个场景对提示的贡献 —— 只有两个座,都是 `TaskSpec` 上的任务类座位:
|
|
38
|
+
* · `role`:选目录里的哪一种角色基底(`TaskSpec.promptRole`;缺席 = 引擎缺省 `general`);
|
|
39
|
+
* · `roleText`:场景自己的角色文本,写在调用方 `systemPrompt` 之前一起进 `TaskSpec.systemPrompt`(它整替选中的角色基底)。
|
|
40
|
+
* 两者都**不许**是引擎目录的文本(抄目录文本 = 第二份文本;逐字含锁定段的角色文本引擎在 prepare 拒 `prompt.assembled_role_refused`)。
|
|
41
|
+
* 宪法、行为段、记忆段、追加段的落位全归引擎。
|
|
42
|
+
*/
|
|
43
|
+
export interface ScenarioPrompt {
|
|
44
|
+
readonly role?: NonNullable<TaskSpec["promptRole"]>;
|
|
45
|
+
readonly roleText?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 一次请求写进 `TaskSpec` 的提示座 —— **`promptBinding` 的唯一写点**,HTTP 腿(`resolveSpec`)与 `run-local` 同用这一只:
|
|
49
|
+
* · `promptBinding` = 场景名,恒写:它是引擎提示选择器的一维,每一类 overlay(中心 artifact / 部署注册 / 任务)的 `when.binding` 都读它 ——
|
|
50
|
+
* 不写 ⇒ 每条 `anyOf` 恒假、每条 `noneOf` 恒真,按场景定向的中心段与部署的品牌行全部失效。场景名过了场景门的文法
|
|
51
|
+
* (`SCENARIO_NAME_RE`,≤ 32 个 ASCII 字)⇒ 恒在引擎 binding 文法之内(非空白、≤ `PROMPT_BINDING_MAX_CHARS` 码点);委派子代经父 spec 座继承它。
|
|
52
|
+
* · `promptRole` = 场景的角色选择器(缺席 = 引擎缺省 `general`);
|
|
53
|
+
* · `systemPrompt` = 角色文本(场景自己的在前、调用方 `systemPrompt` 在后,`\n\n` 相连;空串不占位;两者都缺席 ⇒ 键不写,调用方给的空串原样交引擎)。
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildPromptSeats(scenarioName: string, prompt: ScenarioPrompt | undefined, callerSystemPrompt: string | undefined): Pick<TaskSpec, "promptBinding" | "promptRole" | "systemPrompt">;
|
|
35
56
|
/**
|
|
36
57
|
* [ref]:场景工厂的**服务端事实**上下文 —— 与 `req`(= 调用方请求体,不可信)分开的第三个入参。
|
|
37
58
|
* 这里的每一位都由鉴权信道解析,调用方无法自报;新增位一律走本接口,绝不往 `req` 上加。
|
|
@@ -70,14 +91,6 @@ export interface ScenarioDeps {
|
|
|
70
91
|
* 字节形,core 落「summarization unavailable」诚实兜底)。 */
|
|
71
92
|
webFetchSummarize?: NonNullable<NonNullable<WebFetchConfig["summarize"]>>;
|
|
72
93
|
logger?: Logger;
|
|
73
|
-
/**
|
|
74
|
-
* When true, the `default` scenario carries the Sema product identity (see {@link semaIdentityProvider}).
|
|
75
|
-
* Set by the local/TOC deployment (CONFIG_PROVIDER=local / run-local) so the local engine answers "who
|
|
76
|
-
* are you" as Sema instead of the model's hallucinated identity. Business scenarios (code-review)
|
|
77
|
-
* keep their own prompts; cloud (CONFIG_PROVIDER≠local) leaves the default scenario neutral unless turned
|
|
78
|
-
* on, so integrators that inject their own persona are unaffected.
|
|
79
|
-
*/
|
|
80
|
-
brandIdentity?: boolean;
|
|
81
94
|
/**
|
|
82
95
|
* core 1.287 `SubagentToolOptions.extraTools` seam wire-through — per-SPAWN dynamic tool injection into the
|
|
83
96
|
* delegated (Agent-tool) child roster. Deployment-injected per-task tools (today: SendUserFile — its instance is
|
|
@@ -126,8 +139,9 @@ export interface ScenarioDeps {
|
|
|
126
139
|
taskListStoreFor?: (sessionId: string) => TaskListStore;
|
|
127
140
|
}
|
|
128
141
|
/**
|
|
129
|
-
* Sema product identity
|
|
130
|
-
*
|
|
142
|
+
* Sema product identity — the brand line of the `default` / `code` scenarios on a branded deployment (local / TOC). It is not a
|
|
143
|
+
* scenario seat: it is one deployment-class overlay (`capabilities/brand-identity.ts`, minted by `createDeploymentMods`) whose
|
|
144
|
+
* `when.binding` names the two scenarios. Honest about the substrate (the engine runs whichever model
|
|
131
145
|
* the operator configured — not Claude/Anthropic), so the agent never inherits the model's training-time
|
|
132
146
|
* "I am Claude" hallucination.
|
|
133
147
|
*
|
|
@@ -138,34 +152,10 @@ export interface ScenarioDeps {
|
|
|
138
152
|
* that maker's misstate its own substrate. Same for
|
|
139
153
|
* the gateway protocol (the brain stack serves Anthropic-compatible routes too — routing brain +
|
|
140
154
|
* createAnthropicBrain — so "OpenAI-compatible gateway" would be the same class of lie). Neutral wording
|
|
141
|
-
* (
|
|
142
|
-
* seam and reads poorly for ugly preset ids). The anti-"I am Claude" design is kept — only the baked
|
|
155
|
+
* (interpolating the model id reads poorly for ugly preset ids). The anti-"I am Claude" design is kept — only the baked
|
|
143
156
|
* substrate facts are removed.
|
|
144
157
|
*/
|
|
145
158
|
export { SEMA_IDENTITY } from "./prompts/identity.js";
|
|
146
|
-
/**
|
|
147
|
-
* Prompt provider for the branded `default` scenario: SEMA_IDENTITY on top of the ROLE base — the
|
|
148
|
-
* task's own `body.systemPrompt`, or core's {@link DEFAULT_SYSTEM_PROMPT} when none. Core 1.243
|
|
149
|
-
* (提示词主权批) appends the constitution structurally AFTER this role layer (harness
|
|
150
|
-
* safety blocks + modes + memory guidance + the prefix-cache-safe `<user_memory>` tail), so identity
|
|
151
|
-
* cannot displace the safety blocks. The pre-1.243 form (identity + `defaultPromptProvider.stableSystem`,
|
|
152
|
-
* i.e. an ALREADY-assembled prompt) would now trip the migration guard (anchor-detect → pass-through +
|
|
153
|
-
* `onError(phase:"prompt-constitution")`) — this role-only form is the guard-free contract.
|
|
154
|
-
*/
|
|
155
|
-
export declare function semaIdentityProvider(): PromptProvider;
|
|
156
|
-
/**
|
|
157
|
-
* `code` 场景的提示词提供者([ref] 造,[ref] clay 裁定改名 autonomous→code):role 基底 = core
|
|
158
|
-
* {@link CODE_SYSTEM_PROMPT}(core 1.298 [ref] 改名批正名,旧名 FULL_BODY_SYSTEM_PROMPT=恒等
|
|
159
|
-
* 别名过渡窗;CODE_AGENT persona + AUTONOMY_SELF_AUDIT + ANTI_VERBOSITY +
|
|
160
|
-
* TOOL_PARAM_JSON;[ref] 谱系定论 = CC 生产编码提示词的蒸馏去牌版,底本随 CC 版本对表 cc-anchors,
|
|
161
|
-
* 非自研)——与 bench 的请求组装完全同源,引用 core 常量随版本走,绝不复制字符串(tb bench 实测:
|
|
162
|
-
* 产品默认请求与同引擎 bench 态的差距根因就在这三件 persona/自检/终验)。品牌层与
|
|
163
|
-
* {@link semaIdentityProvider} 同构:brandIdentity 开时 SEMA_IDENTITY 叠在 role 基底之上;任务自带
|
|
164
|
-
* systemPrompt 按同一优先序赢过基底(role 合成规则与 default 场景一致)。宪法由 core 1.243 在 role
|
|
165
|
-
* 层之后结构化追加——此处只产 role-only 形态(guard-free 契约,同 semaIdentityProvider 注释)。
|
|
166
|
-
* 无 systemPrompt 且无品牌时原样返回 core 常量(测试以引用比对锁 bench 同源,不做字符串快照)。
|
|
167
|
-
*/
|
|
168
|
-
export declare function codePromptProvider(brand: boolean): PromptProvider;
|
|
169
159
|
export declare function buildScenarios(deps: ScenarioDeps): Record<string, Scenario>;
|
|
170
160
|
/**
|
|
171
161
|
* Resolve the scenario bundle for a name. `undefined` = no preference ⇒ the built-in `default`; any OTHER name must be
|
|
@@ -203,7 +193,11 @@ export interface ScenarioDetail {
|
|
|
203
193
|
* 这里没跟 = 编译红;wire 变更清单的自铸登记表(`trace/self-minted-wire.ts` 的 `builtinScenario`)同按它 `satisfies`。 */
|
|
204
194
|
export declare const BUILTIN_SCENARIO_NAMES: readonly ["code", "default", "code-review", "scan"];
|
|
205
195
|
export type BuiltinScenarioName = (typeof BUILTIN_SCENARIO_NAMES)[number];
|
|
206
|
-
|
|
196
|
+
/**
|
|
197
|
+
* `branded` = 这台部署是不是品牌部署 —— 部署座挂品牌行读的同一只判官(`brand-identity.ts` `isBrandedDeployment`)交进来;没有默认值:每个调用点说出这件事实。
|
|
198
|
+
* 品牌部署上,品牌行 `when.binding` 那张表里的场景(`BRAND_IDENTITY_SCENARIOS`)的 `promptSummary` 点名 Sema 产品身份 —— 读面说的就是模型收到的。
|
|
199
|
+
*/
|
|
200
|
+
export declare function builtinScenarioDetails(scenarios: Record<string, Scenario>, deps: Pick<ScenarioDeps, "repoClient">, branded: boolean): Record<string, ScenarioDetail>;
|
|
207
201
|
/** center 条目详情(有效性判定与 centerScenarios 完全同款:无效条目既不进 overlay 也不进详情——
|
|
208
202
|
* 保证约定①「详情显示的来源=运行实际用的定义」永不错位)。 */
|
|
209
203
|
export declare function centerScenarioDetails(specs: CenterScenarioSpec[] | undefined, builtinNames: string[], deps: Pick<ScenarioDeps, "repoClient">): Record<string, ScenarioDetail>;
|
|
@@ -296,16 +290,17 @@ export declare function danglingScenarioRefusal(name: string, source: ScenarioNa
|
|
|
296
290
|
*/
|
|
297
291
|
export declare function gateScenarioRequest(ruling: ScenarioRuling | undefined, bodyScenario: unknown, defaultScenario: string, known: readonly string[], logger?: Logger, principal?: string): string;
|
|
298
292
|
/**
|
|
299
|
-
*
|
|
293
|
+
* 一条请求的场景名过门 —— {@link gateScenarioRequest} 的**唯一一处实参装配**。
|
|
300
294
|
*
|
|
301
295
|
* 门的六个实参各有来源:中心裁定(`principalCaps.scenarioRuling`,按 principal)、请求点名的名字、部署缺省场景、**在册词表**
|
|
302
|
-
* (`Object.keys(scenarios)` —— 场景表活引用,不抄快照)、日志与 principal
|
|
303
|
-
* (任务提交腿 `resolveSpec`、`GET /v1/capabilities/mcp
|
|
304
|
-
*
|
|
296
|
+
* (`Object.keys(scenarios)` —— 场景表活引用,不抄快照)、日志与 principal。「此主体在此场景能看见什么」这类问题有不止一个读口
|
|
297
|
+
* (任务提交腿 `resolveSpec`、`run-local`、`GET /v1/capabilities/mcp` 的状态面、`GET /v1/sessions/:id/mcp` / `…/a2a` 两张会话面板),
|
|
298
|
+
* 每个读口自己拼一遍这六个实参 = 第二只门:两口对「在册」「放行」「缺席取谁」各判各的,读口就开始对不上 run。
|
|
299
|
+
* 所以装配只在这里,读口持有的是**绑好的门**;凡回答这类问题的口,场景名都由门给 —— 点名与缺席都问。
|
|
305
300
|
*
|
|
306
301
|
* 契约:`(principal, requested) => 在册的场景名`(返回值一定在册;被拒 ⇒ 抛 {@link gateScenarioRequest} 的那几只 typed 错,
|
|
307
|
-
*
|
|
308
|
-
* `principalCaps` 缺席(无中心 / 干跑 / 嵌入方)⇒ 无 ruling,门退回「请求名或部署缺省」那条现状链。
|
|
302
|
+
* 读口不改写)。`requested` 非串(缺席)⇒ 门走指派链(ruling 胜者,否则部署缺省),与 run 时同一读法。
|
|
303
|
+
* `principalCaps` 缺席(无中心 / 干跑 / 嵌入方 / `run-local`)⇒ 无 ruling,门退回「请求名或部署缺省」那条现状链。
|
|
309
304
|
*/
|
|
310
305
|
export interface ScenarioGateSources {
|
|
311
306
|
readonly config: {
|
|
@@ -323,7 +318,7 @@ export type ScenarioGate = (principal: string | undefined, requested: unknown) =
|
|
|
323
318
|
/**
|
|
324
319
|
* 门问中心裁定走哪条腿(闭集;新增一词 ⇒ 下面的穷尽 `switch` 编译红)—— 与 `resolveRuntimeCaps` / `peekRuntimeCaps` 是同一对:
|
|
325
320
|
* · `enforce` —— 任务提交腿:中心裁定走执法腿,center 硬失败铸下的 fail-closed 降级项两腿都认;
|
|
326
|
-
* · `peek` —— 读面(`GET /v1/capabilities/mcp
|
|
321
|
+
* · `peek` —— 读面(`GET /v1/capabilities/mcp` 状态面、`GET /v1/sessions/:id/mcp` / `…/a2a` 两张会话面板):只读腿,同一只缓存 / 同一条抓取 / 同一份 fail-open 判决,但硬失败铸的降级项只对读面自己算数,
|
|
327
322
|
* 不替这个主体的真提交留下 `{allowWorkflows:false, allowFork:false, autoMode:false}` 的短窗 deny(读面不许成为别人被拒的原因)。
|
|
328
323
|
*/
|
|
329
324
|
export type ScenarioGateLeg = "enforce" | "peek";
|
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WEB_FETCH_TOOL_NAME, assembleCodeTools, defaultTaskRegistry } from "@sema-agent/core";
|
|
2
2
|
import { HttpError, createCodedHttpError } from "../security.js";
|
|
3
3
|
import { createSubagentExtraTools, extraToolsErrorFields, nowTool } from "./builtin-tools.js";
|
|
4
4
|
import { parseRepo, repoToolsFor } from "./repo-tools.js";
|
|
5
|
-
import {
|
|
5
|
+
import { NOW_TOOL_NAME, REPO_PULL_DIFF_TOOL_NAME, REPO_READ_FILE_TOOL_NAME, REPO_TREE_TOOL_NAME } from "./host-tool-names.js";
|
|
6
6
|
import { createWebSearchBackend, judgeRequestWebSearch } from "../plugins/web-search.js";
|
|
7
7
|
import { skillsForScenario } from "./skills.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
{ id: "sema/identity", slot: "identity", text: `${SEMA_IDENTITY}\n\n${ctx.userSystemPrompt ?? DEFAULT_SYSTEM_PROMPT}` },
|
|
14
|
-
],
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export function codePromptProvider(brand) {
|
|
18
|
-
const roleBase = (ctx) => ctx.userSystemPrompt ?? (ctx.promptProfile === "classic" ? CODE_SYSTEM_PROMPT : CODE_AGENT_PROMPT);
|
|
19
|
-
if (!brand) {
|
|
20
|
-
return { stableSystem: (ctx) => roleBase(ctx) };
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
stableBlocks: (ctx) => [
|
|
24
|
-
{ id: "sema/identity", slot: "identity", text: `${SEMA_IDENTITY}\n\n${roleBase(ctx)}` },
|
|
25
|
-
],
|
|
26
|
-
};
|
|
8
|
+
import { BRAND_IDENTITY_SCENARIOS } from "./brand-identity.js";
|
|
9
|
+
export function buildPromptSeats(scenarioName, prompt, callerSystemPrompt) {
|
|
10
|
+
const parts = [prompt?.roleText, callerSystemPrompt].filter((t) => typeof t === "string" && t.length > 0);
|
|
11
|
+
const systemPrompt = parts.length > 0 ? parts.join("\n\n") : callerSystemPrompt;
|
|
12
|
+
return { promptBinding: scenarioName, ...(prompt?.role !== undefined ? { promptRole: prompt.role } : {}), ...(systemPrompt !== undefined ? { systemPrompt } : {}) };
|
|
27
13
|
}
|
|
14
|
+
export { SEMA_IDENTITY } from "./prompts/identity.js";
|
|
28
15
|
function requestWebSearchBackend(verdict, deployment, logger) {
|
|
29
16
|
switch (verdict.kind) {
|
|
30
17
|
case "absent":
|
|
@@ -74,14 +61,13 @@ export function buildScenarios(deps) {
|
|
|
74
61
|
],
|
|
75
62
|
hands: "full",
|
|
76
63
|
skills: skillsForScenario(deps.skills, "default"),
|
|
77
|
-
...(deps.brandIdentity ? { promptProvider: semaIdentityProvider() } : {}),
|
|
78
64
|
};
|
|
79
65
|
};
|
|
80
66
|
return {
|
|
81
67
|
default: defaultScenario,
|
|
82
68
|
code: (req, principal, ctx) => ({
|
|
83
69
|
...defaultScenario(req, principal, ctx),
|
|
84
|
-
|
|
70
|
+
prompt: { role: "code" },
|
|
85
71
|
}),
|
|
86
72
|
"code-review": (req) => codeReview(deps, req),
|
|
87
73
|
scan: (req) => {
|
|
@@ -110,7 +96,7 @@ function availabilityFields(deps, toolset) {
|
|
|
110
96
|
return verdict.available ? { available: true } : { available: false, unavailableReason: verdict.reason };
|
|
111
97
|
}
|
|
112
98
|
export const BUILTIN_SCENARIO_NAMES = ["code", "default", "code-review", "scan"];
|
|
113
|
-
export function builtinScenarioDetails(scenarios, deps) {
|
|
99
|
+
export function builtinScenarioDetails(scenarios, deps, branded) {
|
|
114
100
|
const probe = (name, req, principal, fallback) => {
|
|
115
101
|
try {
|
|
116
102
|
return ((Object.hasOwn(scenarios, name) ? scenarios[name] : undefined)?.(req, principal)?.tools ?? []).map((t) => t.name);
|
|
@@ -119,16 +105,15 @@ export function builtinScenarioDetails(scenarios, deps) {
|
|
|
119
105
|
return fallback;
|
|
120
106
|
}
|
|
121
107
|
};
|
|
122
|
-
const REPO_TOOLS = [
|
|
108
|
+
const REPO_TOOLS = [REPO_TREE_TOOL_NAME, REPO_READ_FILE_TOOL_NAME, REPO_PULL_DIFF_TOOL_NAME, NOW_TOOL_NAME];
|
|
123
109
|
const mk = (name, summary, toolset, tools, promptSummary) => ({
|
|
124
110
|
name, source: "builtin", builtin: true, summary, toolset, tools, promptSummary, enabled: true,
|
|
125
111
|
...availabilityFields(deps, toolset),
|
|
126
112
|
});
|
|
113
|
+
const brandedScenario = (name) => branded && BRAND_IDENTITY_SCENARIOS.includes(name);
|
|
127
114
|
const details = {
|
|
128
|
-
code: mk("code", "编码主场景([891] 出厂缺省;CC 编码 persona 蒸馏版,default 全量工具面;终验旋钮独立)", "full-body", probe("code", {}, undefined, ["
|
|
129
|
-
|
|
130
|
-
: "core 编码完全体提示词(CODE_AGENT persona+自治自检;CC 血统对表)"),
|
|
131
|
-
default: mk("default", "通用/助理场景(域中性 persona,CC agent loop 全量工具面;[891] 起非出厂缺省,显式可选)", "full-body", probe("default", {}, undefined, ["Now"]), deps.brandIdentity ? "Sema 产品身份 + core 默认宪法(结构层由引擎合成)" : "core 默认宪法(中性,无场景附加 persona)"),
|
|
115
|
+
code: mk("code", "编码主场景([891] 出厂缺省;CC 编码 persona 蒸馏版,default 全量工具面;终验旋钮独立)", "full-body", probe("code", {}, undefined, [NOW_TOOL_NAME]), `${brandedScenario("code") ? "Sema 产品身份 + " : ""}core 编码完全体提示词(CODE_AGENT persona+自治自检;CC 血统对表)`),
|
|
116
|
+
default: mk("default", "通用/助理场景(域中性 persona,CC agent loop 全量工具面;[891] 起非出厂缺省,显式可选)", "full-body", probe("default", {}, undefined, [NOW_TOOL_NAME]), brandedScenario("default") ? "Sema 产品身份 + core 默认宪法(结构层由引擎合成)" : "core 默认宪法(中性,无场景附加 persona)"),
|
|
132
117
|
"code-review": mk("code-review", "代码评审(clone-free 只读仓库工具)", "repo-readonly", probe("code-review", { repo: "probe/probe" }, undefined, REPO_TOOLS), "评审员提示词(产出契约)"),
|
|
133
118
|
scan: mk("scan", "中性只读仓库扫描(输出契约由 objective+skills 决定)", "repo-readonly", probe("scan", { repo: "probe/probe" }, undefined, REPO_TOOLS), "无场景附加提示词(objective+center skills 拥有输出契约)"),
|
|
134
119
|
};
|
|
@@ -149,7 +134,7 @@ export function centerScenarioDetails(specs, builtinNames, deps) {
|
|
|
149
134
|
builtin: builtinNames.includes(spec.name),
|
|
150
135
|
summary: "",
|
|
151
136
|
toolset: spec.toolset,
|
|
152
|
-
tools: spec.toolset === "repo-readonly" ? [
|
|
137
|
+
tools: spec.toolset === "repo-readonly" ? [REPO_TREE_TOOL_NAME, REPO_READ_FILE_TOOL_NAME, REPO_PULL_DIFF_TOOL_NAME, NOW_TOOL_NAME] : [NOW_TOOL_NAME],
|
|
153
138
|
promptSummary: "",
|
|
154
139
|
enabled: true,
|
|
155
140
|
...availabilityFields(deps, spec.toolset),
|
|
@@ -229,13 +214,12 @@ function codeReview(deps, req) {
|
|
|
229
214
|
const repoTools = repoToolsFor(repoClient, coords);
|
|
230
215
|
const skills = skillsForScenario(deps.skills, "code-review");
|
|
231
216
|
const hands = "none";
|
|
232
|
-
return { tools: [...repoTools, nowTool()], hands, skills,
|
|
217
|
+
return { tools: [...repoTools, nowTool()], hands, skills, prompt: { roleText: REVIEWER_ROLE_TEXT } };
|
|
233
218
|
}
|
|
234
|
-
const
|
|
235
|
-
"(blocker / major / minor), each with `path:line`, the problem, and a concrete fix. Cite real code — never invent files or symbols.";
|
|
236
|
-
const directReviewerPrompt = stablePrompt("You are a senior code reviewer. Call repo_tree to map the repository, then repo_read_file to read " +
|
|
219
|
+
const REVIEWER_ROLE_TEXT = "You are a senior code reviewer. Call repo_tree to map the repository, then repo_read_file to read " +
|
|
237
220
|
"the files in scope, then review them yourself. " +
|
|
238
|
-
|
|
221
|
+
"Produce ONE prioritized review: a short summary, then findings grouped by severity " +
|
|
222
|
+
"(blocker / major / minor), each with `path:line`, the problem, and a concrete fix. Cite real code — never invent files or symbols.";
|
|
239
223
|
export function mergeUserSkills(scenarioSkills, userSkills, logger) {
|
|
240
224
|
if (!userSkills || userSkills.length === 0)
|
|
241
225
|
return scenarioSkills;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
+
import { SELECT_ENVIRONMENT_TOOL_NAME } from "./host-tool-names.js";
|
|
2
3
|
import { BoundedSessionMap } from "../bounded-session-map.js";
|
|
3
4
|
import { resolveSandboxImageRef } from "../per-task-image.js";
|
|
4
5
|
export class SessionEnvironmentSelection {
|
|
@@ -22,7 +23,7 @@ async function availableProfiles(catalog, viewer) {
|
|
|
22
23
|
}
|
|
23
24
|
export function selectEnvironmentTool(deps) {
|
|
24
25
|
return {
|
|
25
|
-
name:
|
|
26
|
+
name: SELECT_ENVIRONMENT_TOOL_NAME,
|
|
26
27
|
description: "Switch this session's sandbox environment (image profile) or discover available ones. " +
|
|
27
28
|
"Call with no arguments to list available profiles and their capabilities. " +
|
|
28
29
|
"Call with `profile` (optionally `capabilitiesNeeded`) to select one. " +
|
|
@@ -43,7 +43,6 @@ export declare function sendUserFileTool(deps: {
|
|
|
43
43
|
}) => Promise<IssuedFileLink>;
|
|
44
44
|
emitter: SendUserFileEmitter;
|
|
45
45
|
}): ToolSpec;
|
|
46
|
-
export declare const SEND_USER_FILE_TOOL_NAME = "SendUserFile";
|
|
47
46
|
/**
|
|
48
47
|
* The deployment's group for the delegated-child injection (`createSubagentExtraTools` in `builtin-tools.ts` composes it with
|
|
49
48
|
* `Now` into the delegation tool's `extraTools` factory AND its `extraToolNames` roster — one list, read twice): SendUserFile,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { builtinAgentDefinitions } from "@sema-agent/core";
|
|
4
|
+
import { SEND_USER_FILE_TOOL_NAME } from "./host-tool-names.js";
|
|
4
5
|
export class SendUserFileEmitter {
|
|
5
6
|
als = new AsyncLocalStorage();
|
|
6
7
|
runWithContext(ctx, fn) {
|
|
@@ -19,7 +20,7 @@ export const SEND_USER_FILE_MAX_BYTES = 100 * 1024 * 1024;
|
|
|
19
20
|
export const SEND_USER_FILE_MAX_CAPTION = 500;
|
|
20
21
|
export function sendUserFileTool(deps) {
|
|
21
22
|
return {
|
|
22
|
-
name:
|
|
23
|
+
name: SEND_USER_FILE_TOOL_NAME,
|
|
23
24
|
description: "Send file(s) to the user — surfaced as downloadable/inline cards in their pane. " +
|
|
24
25
|
"Use when the file itself is the deliverable (generated charts, reports, screenshots, build artifacts) " +
|
|
25
26
|
"and you want it surfaced, not just mentioned. Files must already exist on local disk (this tool sends " +
|
|
@@ -91,7 +92,6 @@ export function sendUserFileTool(deps) {
|
|
|
91
92
|
},
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
|
-
export const SEND_USER_FILE_TOOL_NAME = "SendUserFile";
|
|
95
95
|
const READONLY_BUILTIN_AGENT_TYPES = new Set(builtinAgentDefinitions().map((d) => d.name));
|
|
96
96
|
export function subagentSendUserFileExtraTools(deps) {
|
|
97
97
|
return { tools: [deps.spec], forSpawn: (ctx) => !(ctx.agentType !== undefined && READONLY_BUILTIN_AGENT_TYPES.has(ctx.agentType)) };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SkillSpec } from "@sema-agent/core";
|
|
2
2
|
import type { RemoteExecProvider } from "../execution-lane-caps.js";
|
|
3
|
+
import { type Logger } from "../observability/logger.js";
|
|
3
4
|
/**
|
|
4
5
|
* Built-in skills baked into the image, loaded ONCE at startup (no per-task disk IO). A skill is a
|
|
5
6
|
* Markdown capability (frontmatter + body). core uses **progressive disclosure** (core ≥1.98.0):
|
|
@@ -26,6 +27,10 @@ import type { RemoteExecProvider } from "../execution-lane-caps.js";
|
|
|
26
27
|
* description: when to use it
|
|
27
28
|
* scenarios: code-review, oa # which scenarios get it; omit = all scenarios
|
|
28
29
|
* ---
|
|
30
|
+
*
|
|
31
|
+
* 🔴 **frontmatter 读不了的技能不装,不降成全局技能**(安全轴:`scenarios` 是场景围栏,丢了它技能就对每个场景可见):文件第一行是围栏(`---`,行尾只许空白 / 制表 / CR,
|
|
32
|
+
* 与 CC 2.1.296 的开头正则同判)而块没有收尾 ⇒ {@link parseFrontmatter} 答 `unclosed`,这一个技能被**拒载**并落一行 `skill_refused` warn(点名技能与文件、不带正文);
|
|
33
|
+
* 兄弟技能照装。第一行不是围栏的文件没有 frontmatter,是正当的全局技能。
|
|
29
34
|
*/
|
|
30
35
|
export interface LoadedSkill {
|
|
31
36
|
spec: SkillSpec;
|
|
@@ -52,6 +57,8 @@ export interface LoadSkillsOptions {
|
|
|
52
57
|
* core 的律也明写缺席 ⇒ 结果逐字节与 baseDir 到货之前相同)。两个方向不对称 ⇒ 默认取轻的那边。
|
|
53
58
|
*/
|
|
54
59
|
toolsShareThisFilesystem?: boolean;
|
|
60
|
+
/** 拒载一行 warn 的出口(见文件头注「frontmatter 读不了的技能不装」)。缺席 ⇒ 进程的 JSON 行 logger(`createLogger("warn")`,stderr)—— 拒载没有「安静」的形;装配点传自己的 logger 只为字段与级别跟部署同一份。 */
|
|
61
|
+
logger?: Logger;
|
|
55
62
|
}
|
|
56
63
|
/** `p` 的 realpath 必须落在 `rootReal` 之内(含自身),否则抛。`sep` 边界判定防 `/a/root-evil` 撞
|
|
57
64
|
* `/a/root` 前缀。目标不存在 ⇒ realpath 抛 ENOENT,同样是拒绝(悬空链接不该被读)。
|
|
@@ -70,12 +77,20 @@ export declare function loadDeploymentSkills(config: {
|
|
|
70
77
|
readonly remoteExec?: {
|
|
71
78
|
readonly provider: RemoteExecProvider;
|
|
72
79
|
} | undefined;
|
|
73
|
-
}): LoadedSkill[];
|
|
80
|
+
}, logger?: Logger): LoadedSkill[];
|
|
74
81
|
/** Skills applicable to a scenario: those tagged with it, plus untagged (global) ones.
|
|
75
82
|
* [ref]:alias 窗撤销,单键查询——退役旧名标签不再命中(迁移面在 7.44 发车帖/MIGRATION 记载)。 */
|
|
76
83
|
export declare function skillsForScenario(loaded: LoadedSkill[], scenario: string): SkillSpec[];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
/** {@link parseFrontmatter} 的三种读数(闭集):`absent` = 第一行不是围栏,没有 frontmatter(正当的全局技能)· `block` = 围栏收好了的块 · `unclosed` = 第一行是围栏而块没收好(读不了 —— 调用方拒载)。 */
|
|
85
|
+
export type FrontmatterRead = {
|
|
86
|
+
readonly kind: "absent";
|
|
87
|
+
readonly body: string;
|
|
88
|
+
} | {
|
|
89
|
+
readonly kind: "block";
|
|
90
|
+
readonly meta: Record<string, string>;
|
|
91
|
+
readonly body: string;
|
|
92
|
+
} | {
|
|
93
|
+
readonly kind: "unclosed";
|
|
80
94
|
};
|
|
95
|
+
export declare function parseFrontmatter(raw: string): FrontmatterRead;
|
|
81
96
|
//# sourceMappingURL=skills.d.ts.map
|