@sema-agent/server 4.3.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +16 -16
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.d.ts +17 -37
- package/dist/approval.js +26 -112
- package/dist/auth-bridge.d.ts +0 -1
- package/dist/auth-bridge.js +0 -1
- package/dist/auth-keys.d.ts +1 -1
- package/dist/auth-keys.js +1 -1
- package/dist/bench/s1/run-firm.js +1 -1
- package/dist/boot/config-center.d.ts +1 -1
- package/dist/boot/config-center.js +4 -3
- package/dist/boot/reapers.d.ts +1 -2
- package/dist/boot/reapers.js +4 -5
- package/dist/boot/resolve-spec.d.ts +1 -3
- package/dist/boot/resolve-spec.js +21 -35
- package/dist/boot/stores.js +1 -1
- package/dist/brain.js +3 -1
- package/dist/capabilities/center-prompts.d.ts +2 -3
- package/dist/capabilities/center-prompts.js +2 -3
- package/dist/capabilities/sandbox-file-send.d.ts +0 -2
- package/dist/capabilities/sandbox-file-send.js +2 -5
- package/dist/capabilities/scenarios.d.ts +2 -13
- package/dist/capabilities/scenarios.js +3 -22
- package/dist/capabilities/send-user-file-tool.js +3 -3
- package/dist/config-center/apply-effective.js +51 -13
- package/dist/config-center/types.d.ts +10 -3
- package/dist/config-provider.d.ts +1 -1
- package/dist/config-provider.js +6 -11
- package/dist/config-types.d.ts +15 -29
- package/dist/config.d.ts +28 -0
- package/dist/config.js +122 -68
- package/dist/fleet/fleet-bus.d.ts +1 -1
- package/dist/fleet/fleet-bus.js +25 -33
- package/dist/fleet-client.js +1 -1
- package/dist/http/active-run-conflict.d.ts +2 -0
- package/dist/http/active-run-conflict.js +1 -0
- package/dist/http/routes/approvals-assistant.d.ts +2 -1
- package/dist/http/routes/approvals-assistant.js +10 -96
- package/dist/http/routes/capabilities.js +6 -5
- package/dist/http/routes/fleet.js +7 -2
- package/dist/http/routes/images.js +10 -4
- package/dist/http/routes/runs.js +42 -65
- package/dist/http/routes/session-sync.d.ts +1 -1
- package/dist/http/routes/session-sync.js +2 -1
- package/dist/http/routes/tasks.js +7 -7
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/routes/workflows.js +3 -1
- package/dist/http/server.d.ts +1 -6
- package/dist/http/server.js +15 -10
- package/dist/http/verify-rounds.d.ts +2 -2
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/images/bake-validate.d.ts +0 -22
- package/dist/images/bake-validate.js +0 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -5
- package/dist/main.js +9 -10
- package/dist/memory-scope.d.ts +1 -2
- package/dist/memory-scope.js +8 -4
- package/dist/orchestration/workflow-completion-inbox.d.ts +6 -4
- package/dist/orchestration/workflow-completion-inbox.js +6 -4
- package/dist/plugins/breaker-state-sql.d.ts +2 -3
- package/dist/plugins/file-run-store.d.ts +1 -1
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/image-index-sql.d.ts +0 -1
- package/dist/plugins/image-index-sql.js +0 -1
- package/dist/plugins/local-checkpoint-store.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.js +1 -1
- package/dist/plugins/memory-engine-pg.js +2 -1
- package/dist/plugins/memory-engine-tidb.js +1 -1
- package/dist/plugins/memory-engine-vector-util.d.ts +2 -3
- package/dist/plugins/memory-engine-vector-util.js +2 -3
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/memory-session-policy-store.d.ts +1 -1
- package/dist/plugins/memory-session-policy-store.js +1 -1
- package/dist/plugins/memory-sync-store-pg.js +1 -1
- package/dist/plugins/pg-pool.js +8 -10
- package/dist/plugins/pg-rate-limiter.d.ts +1 -4
- package/dist/plugins/pg-rate-limiter.js +0 -1
- package/dist/plugins/roster-store-sql.d.ts +1 -3
- package/dist/plugins/roster-store-sql.js +3 -5
- package/dist/plugins/store-backend.d.ts +7 -17
- package/dist/plugins/store-backend.js +14 -30
- package/dist/plugins/tidb-pool.js +6 -30
- package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
- package/dist/plugins/workflow-journal-store-sql.js +1 -1
- package/dist/principal-jwt.js +2 -2
- package/dist/prompts-domain-validate.d.ts +4 -3
- package/dist/prompts-domain-validate.js +7 -8
- package/dist/router/route-orchestration.js +3 -3
- package/dist/run-local.js +13 -10
- package/dist/runs.js +8 -6
- package/dist/runtime-caps-resolver.d.ts +3 -2
- package/dist/runtime-caps-resolver.js +4 -4
- package/dist/runtime-governance.d.ts +4 -7
- package/dist/runtime-governance.js +12 -17
- package/dist/sealed-key.d.ts +1 -1
- package/dist/sealed-key.js +1 -1
- package/dist/security.d.ts +0 -5
- package/dist/security.js +8 -12
- package/dist/session-sync.d.ts +0 -2
- package/dist/session-sync.js +10 -17
- package/dist/spec-fields.js +6 -6
- package/dist/task-settings.d.ts +0 -27
- package/dist/task-settings.js +42 -11
- package/dist/tool-approval.d.ts +3 -3
- package/dist/tool-approval.js +10 -10
- package/dist/trace/artifacts.d.ts +11 -0
- package/dist/trace/artifacts.js +8 -19
- package/dist/trace/ledger-sink.js +7 -1
- package/dist/trace/project.d.ts +17 -0
- package/dist/trace/project.js +58 -5
- package/package.json +3 -3
- package/dist/capabilities/oa-tools.d.ts +0 -15
- package/dist/capabilities/oa-tools.js +0 -54
- package/dist/plugins/approval-store-sql.d.ts +0 -116
- package/dist/plugins/approval-store-sql.js +0 -150
- package/dist/plugins/file-workflow-journal-store.d.ts +0 -12
- package/dist/plugins/file-workflow-journal-store.js +0 -12
- package/dist/plugins/pg-approval-store.d.ts +0 -9
- package/dist/plugins/pg-approval-store.js +0 -9
- package/dist/plugins/pg-breaker-state.d.ts +0 -8
- package/dist/plugins/pg-breaker-state.js +0 -8
- package/dist/plugins/pg-checkpoint-store.d.ts +0 -10
- package/dist/plugins/pg-checkpoint-store.js +0 -10
- package/dist/plugins/pg-file-snapshot-store.d.ts +0 -8
- package/dist/plugins/pg-file-snapshot-store.js +0 -8
- package/dist/plugins/pg-image-bake.d.ts +0 -12
- package/dist/plugins/pg-image-bake.js +0 -11
- package/dist/plugins/pg-image-index.d.ts +0 -12
- package/dist/plugins/pg-image-index.js +0 -11
- package/dist/plugins/pg-outcome-ledger.d.ts +0 -12
- package/dist/plugins/pg-outcome-ledger.js +0 -11
- package/dist/plugins/pg-resume-anchor-store.d.ts +0 -7
- package/dist/plugins/pg-resume-anchor-store.js +0 -7
- package/dist/plugins/pg-run-store.d.ts +0 -9
- package/dist/plugins/pg-run-store.js +0 -9
- package/dist/plugins/pg-session-policy-store.d.ts +0 -7
- package/dist/plugins/pg-session-policy-store.js +0 -7
- package/dist/plugins/pg-session-store.d.ts +0 -12
- package/dist/plugins/pg-session-store.js +0 -12
- package/dist/plugins/pg-tool-result-store.d.ts +0 -9
- package/dist/plugins/pg-tool-result-store.js +0 -9
- package/dist/plugins/pg-workflow-journal-store.d.ts +0 -9
- package/dist/plugins/pg-workflow-journal-store.js +0 -9
- package/dist/plugins/pg-workflow-run-store.d.ts +0 -9
- package/dist/plugins/pg-workflow-run-store.js +0 -9
- package/dist/plugins/tidb-approval-store.d.ts +0 -8
- package/dist/plugins/tidb-approval-store.js +0 -8
- package/dist/plugins/tidb-breaker-state.d.ts +0 -7
- package/dist/plugins/tidb-breaker-state.js +0 -7
- package/dist/plugins/tidb-checkpoint-store.d.ts +0 -9
- package/dist/plugins/tidb-checkpoint-store.js +0 -9
- package/dist/plugins/tidb-file-snapshot-store.d.ts +0 -8
- package/dist/plugins/tidb-file-snapshot-store.js +0 -8
- package/dist/plugins/tidb-image-bake.d.ts +0 -12
- package/dist/plugins/tidb-image-bake.js +0 -11
- package/dist/plugins/tidb-image-index.d.ts +0 -12
- package/dist/plugins/tidb-image-index.js +0 -11
- package/dist/plugins/tidb-outcome-ledger.d.ts +0 -12
- package/dist/plugins/tidb-outcome-ledger.js +0 -12
- package/dist/plugins/tidb-resume-anchor-store.d.ts +0 -7
- package/dist/plugins/tidb-resume-anchor-store.js +0 -7
- package/dist/plugins/tidb-run-store.d.ts +0 -10
- package/dist/plugins/tidb-run-store.js +0 -9
- package/dist/plugins/tidb-session-policy-store.d.ts +0 -7
- package/dist/plugins/tidb-session-policy-store.js +0 -7
- package/dist/plugins/tidb-tool-result-store.d.ts +0 -8
- package/dist/plugins/tidb-tool-result-store.js +0 -10
- package/dist/plugins/tidb-workflow-journal-store.d.ts +0 -9
- package/dist/plugins/tidb-workflow-journal-store.js +0 -9
- package/dist/plugins/tidb-workflow-run-store.d.ts +0 -10
- package/dist/plugins/tidb-workflow-run-store.js +0 -10
- package/dist/plugins/workflow-journal-limits.d.ts +0 -12
- package/dist/plugins/workflow-journal-limits.js +0 -12
|
@@ -23,17 +23,13 @@
|
|
|
23
23
|
* path-prefixed commands all bypass the argv[0] match (they route to `ask`, fail-closed, but are not blocked by
|
|
24
24
|
* name). Real isolation is the `executionEnv` sandbox. Same caveat core documents on `createCoarseCommandNamePolicy`.
|
|
25
25
|
*/
|
|
26
|
-
import {
|
|
27
|
-
/** True when `name` names a DELEGATION tool — the subagent tool
|
|
28
|
-
* `DEFAULT_SUBAGENT_TOOL_NAME
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* WRITABLE-hands child, because core does not propagate handsReadOnly to a delegated child).
|
|
32
|
-
* core 1.257 (design/136 BREAKING): the standalone `Fork` tool is retired — a fork is now
|
|
33
|
-
* `Agent(subagent_type:"fork")`, i.e. an Agent call, already matched by DEFAULT_SUBAGENT_TOOL_NAME. */
|
|
26
|
+
import { combinePolicies, createCoarseCommandNamePolicy, DEFAULT_SUBAGENT_TOOL_NAME, parseLeadingCommandName, tightenTaskSpec, } from "@sema-agent/core";
|
|
27
|
+
/** True when `name` names a DELEGATION tool — the subagent tool(CC-187 canonical `"Agent"`,core
|
|
28
|
+
* `DEFAULT_SUBAGENT_TOOL_NAME`)。5.0.0 RB-476:折叠面退役,RAW 比对 core 单源常量——旧名("Task")
|
|
29
|
+
* 在 core roster 层响亮 miss,不再需要归一;单源常量本身就防了当年 1.202 改名时裸字符串失配的
|
|
30
|
+
* fail-open(那个教训的解=引用常量,不是折叠)。 */
|
|
34
31
|
export function isDelegationToolName(name) {
|
|
35
|
-
|
|
36
|
-
return c === DEFAULT_SUBAGENT_TOOL_NAME;
|
|
32
|
+
return name === DEFAULT_SUBAGENT_TOOL_NAME;
|
|
37
33
|
}
|
|
38
34
|
/** The hands-read-only DELEGATION STRIP (the main.ts governed-spec site, extracted here so the actual filter —
|
|
39
35
|
* not just the name predicate — is pinned by tests): core does not propagate `handsReadOnly` to a delegated
|
|
@@ -77,10 +73,9 @@ function createCommandAskListPolicy(askCommands, tools = COARSE_SHELL_TOOLS) {
|
|
|
77
73
|
const ask = (message) => ({ action: "ask", message, decisionReason: "rule" });
|
|
78
74
|
return {
|
|
79
75
|
check(req) {
|
|
80
|
-
// Out of scope for non-shell tools: this gate only speaks about shell command names.
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
if (!shellTools.has(canonicalToolName(req.toolName)))
|
|
76
|
+
// Out of scope for non-shell tools: this gate only speaks about shell command names. RAW(5.0.0):
|
|
77
|
+
// live 名恒 canonical,pre-rename 重放由 core 响亮拒——折叠臂退役。
|
|
78
|
+
if (!shellTools.has(req.toolName))
|
|
84
79
|
return { action: "allow" };
|
|
85
80
|
const command = req.args?.command;
|
|
86
81
|
if (typeof command !== "string")
|
|
@@ -100,9 +95,9 @@ function createCommandAskListPolicy(askCommands, tools = COARSE_SHELL_TOOLS) {
|
|
|
100
95
|
* (letters/digits and `. _ - +`, covering `python3.11`/`docker-compose`/`g++`) and reject the rest FAIL-LOUD at
|
|
101
96
|
* config-apply. 🔴 Kept ALIGNED with the `@sema-agent/registry-core` contract-layer regex (center fixed the
|
|
102
97
|
* glob→exact contract bug in 0.1.26 + publish-validates with this SAME pattern) so the contract gate and this
|
|
103
|
-
* defense-in-depth gate agree.
|
|
104
|
-
*
|
|
105
|
-
*
|
|
98
|
+
* defense-in-depth gate agree. [2373]B-11:旧注靠「dep=^0.1.26 additive 窗」论证对齐,地板抬到 ^0.13.0 后
|
|
99
|
+
* 该论证失效——现由机器钉逐字节对表(test/runtime-governance.test.ts 读安装包正则比对),lockstep 不再
|
|
100
|
+
* 是注释请求。2026-08-02 亲核 0.13.0 dist/types.js:703:两正则逐字节相同。 */
|
|
106
101
|
const VALID_COMMAND_NAME = /^[A-Za-z0-9._+-]+$/;
|
|
107
102
|
/** The three legal per-command decisions (mirrors the registry `runtime.commandPolicy[].decision` enum). */
|
|
108
103
|
const VALID_DECISIONS = new Set(["allow", "ask", "deny"]);
|
package/dist/sealed-key.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export declare function ensureSealedKeyStore(dir: string, logger?: Logger): Prom
|
|
|
76
76
|
*
|
|
77
77
|
* HONESTY over fake automation: that endpoint is ADMIN-gated, and a fleet worker only holds
|
|
78
78
|
* SERVICE_PULL_TOKEN — so automatic registration works ONLY when the operator deliberately provides
|
|
79
|
-
* an admin token (SEMA_REGISTRY_ADMIN_TOKEN
|
|
79
|
+
* an admin token (SEMA_REGISTRY_ADMIN_TOKEN in this service's env; the REGISTRY_ADMIN_TOKEN alias was tombstoned in 5.0.0). Without
|
|
80
80
|
* one we do the next-best REAL thing: print the FULL public key + id at boot (a public key is not a
|
|
81
81
|
* secret) with the exact registration instructions, and warn that the web "paste key" face stays
|
|
82
82
|
* dark for this host until an admin registers it. Never throws (fire-and-forget from boot).
|
package/dist/sealed-key.js
CHANGED
|
@@ -150,7 +150,7 @@ export async function ensureSealedKeyStore(dir, logger) {
|
|
|
150
150
|
*
|
|
151
151
|
* HONESTY over fake automation: that endpoint is ADMIN-gated, and a fleet worker only holds
|
|
152
152
|
* SERVICE_PULL_TOKEN — so automatic registration works ONLY when the operator deliberately provides
|
|
153
|
-
* an admin token (SEMA_REGISTRY_ADMIN_TOKEN
|
|
153
|
+
* an admin token (SEMA_REGISTRY_ADMIN_TOKEN in this service's env; the REGISTRY_ADMIN_TOKEN alias was tombstoned in 5.0.0). Without
|
|
154
154
|
* one we do the next-best REAL thing: print the FULL public key + id at boot (a public key is not a
|
|
155
155
|
* secret) with the exact registration instructions, and warn that the web "paste key" face stays
|
|
156
156
|
* dark for this host until an admin registers it. Never throws (fire-and-forget from boot).
|
package/dist/security.d.ts
CHANGED
|
@@ -3,8 +3,6 @@ import { type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
|
|
|
3
3
|
import type { ServiceConfig } from "./config-types.js";
|
|
4
4
|
import type { TaskRequestBody } from "./http/wire-types.js";
|
|
5
5
|
import type { StagingHandle } from "./session-sync-kernel.js";
|
|
6
|
-
export { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
|
|
7
|
-
export type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
|
|
8
6
|
/** One row of the session-list projection (CC /resume picker) — the durable session abstraction's enumeration
|
|
9
7
|
* output (§0.5). Mirrors {@link RunStore}'s shipped `SessionSummary` shape so the wire response is unchanged. */
|
|
10
8
|
export interface SessionListItem {
|
|
@@ -215,7 +213,4 @@ export declare function ssoVerifiedPrincipal(req: IncomingMessage): string | und
|
|
|
215
213
|
export declare function setSsoScope(req: IncomingMessage, scope: string): void;
|
|
216
214
|
export declare function ssoVerifiedScope(req: IncomingMessage): string | undefined;
|
|
217
215
|
export declare function verifiedPrincipal(req: IncomingMessage, config: ServiceConfig): string | undefined;
|
|
218
|
-
export { memoryScopeFor, memoryEngineRemoteLanePosture, memoryEngineBackendFor, memorySpecForRequest } from "./memory-scope.js";
|
|
219
|
-
export { approvalHmacMessage, MAX_APPROVAL_REASON_CHARS, verifyApprovalHmac } from "./approval-hmac.js";
|
|
220
|
-
export { verifyPrincipalJwt, approvalBnd, verifyDirectDoorProof } from "./principal-jwt.js";
|
|
221
216
|
//# sourceMappingURL=security.d.ts.map
|
package/dist/security.js
CHANGED
|
@@ -3,9 +3,8 @@ import { PROJECT_ID_REGEX } from "@sema-agent/registry-core";
|
|
|
3
3
|
// design/158 A4: the env→key-set decoders (parseApprovalHmacKeys / parsePrincipalJwks + their row types) moved to
|
|
4
4
|
// the leaf ./auth-keys.js so config.ts's env-load no longer value-imports this whole auth module (lens2 §F). The
|
|
5
5
|
// verifiers that consume the parsed key-sets now live in ./approval-hmac.js / ./principal-jwt.js (design/158 A13);
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import { memoryScopeFor } from "./memory-scope.js"; // design/158 N17: see re-export block below (moved to ./memory-scope.js)
|
|
6
|
+
// import all four names from ./auth-keys.js directly ([2354] 兼容面全清 — the compat re-export here is gone).
|
|
7
|
+
import { memoryScopeFor } from "./memory-scope.js"; // design/158 N17: moved to ./memory-scope.js (this file's createAuthorizer still calls it)
|
|
9
8
|
// design/158 A13: the approval-envelope HMAC (integrity) group and the principal-JWT/direct-door (identity)
|
|
10
9
|
// group moved VERBATIM to ./approval-hmac.js and ./principal-jwt.js respectively — this file's own
|
|
11
10
|
// `verifiedPrincipal` still calls verifyPrincipalJwt for the direct-door branch (value import below); every
|
|
@@ -333,15 +332,12 @@ export function verifiedPrincipal(req, config) {
|
|
|
333
332
|
}
|
|
334
333
|
// design/158 N17: memoryScopeFor / memoryEngineRemoteLanePosture / memoryEngineBackendFor / memorySpecForRequest
|
|
335
334
|
// moved VERBATIM to the leaf ./memory-scope.js (security.ts's name says auth; these four resolve memory-plane
|
|
336
|
-
// config, unrelated).
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
//
|
|
340
|
-
// MAX_APPROVAL_REASON_CHARS) and principal-JWT/direct-door proof (identity, {@link verifyPrincipalJwt}/
|
|
335
|
+
// config, unrelated). Import them from ./memory-scope.js directly ([2354] 兼容面全清 — the compat re-export here
|
|
336
|
+
// is gone); this file's own createAuthorizer below still calls memoryScopeFor.
|
|
337
|
+
// design/158 A13: approval-envelope HMAC (integrity, verifyApprovalHmac/approvalHmacMessage/
|
|
338
|
+
// MAX_APPROVAL_REASON_CHARS) and principal-JWT/direct-door proof (identity, verifyPrincipalJwt/
|
|
341
339
|
// approvalBnd/verifyDirectDoorProof) moved VERBATIM to ./approval-hmac.js and ./principal-jwt.js — this file
|
|
342
340
|
// keeps only the authenticator/principal-shape/SSO-mount group (createAuthorizer, verifiedPrincipal,
|
|
343
|
-
// assertPrincipalShape, principalFrom, setSsoPrincipal/setSsoScope)
|
|
344
|
-
//
|
|
345
|
-
export { approvalHmacMessage, MAX_APPROVAL_REASON_CHARS, verifyApprovalHmac } from "./approval-hmac.js";
|
|
346
|
-
export { verifyPrincipalJwt, approvalBnd, verifyDirectDoorProof } from "./principal-jwt.js";
|
|
341
|
+
// assertPrincipalShape, principalFrom, setSsoPrincipal/setSsoScope). Import the moved names from their
|
|
342
|
+
// new homes directly ([2354] 兼容面全清 — the compat re-exports here are gone).
|
|
347
343
|
//# sourceMappingURL=security.js.map
|
package/dist/session-sync.d.ts
CHANGED
|
@@ -30,8 +30,6 @@ export interface FileSnapshotImportFace {
|
|
|
30
30
|
* seam is absent (an older/in-memory dev store). Callers gate on the RETURN VALUE, not a separate `typeof` check,
|
|
31
31
|
* so there is exactly one place that knows the shape of the probe. */
|
|
32
32
|
export declare function fileSnapshotImportFace(fs: unknown): FileSnapshotImportFace | undefined;
|
|
33
|
-
export { stagingIdFor, STAGING_ID_MARKER, realIdOfStaging, classifySyncRelationship, classifySyncRelationshipByIds, SyncConflictError } from "./session-sync-kernel.js";
|
|
34
|
-
export type { StagingHandle, ImportStagingStore, SyncRelation } from "./session-sync-kernel.js";
|
|
35
33
|
/**
|
|
36
34
|
* The portable state of ONE session, ready to replay into another backend. Blobs are deliberately NOT inlined
|
|
37
35
|
* (§5: a turn's working-tree blobs can be tens of MiB → OOM/413 if buffered); each snapshot carries only its
|
package/dist/session-sync.js
CHANGED
|
@@ -12,15 +12,6 @@ export function fileSnapshotImportFace(fs) {
|
|
|
12
12
|
return undefined;
|
|
13
13
|
return { importManifest: candidate.bind(fs) };
|
|
14
14
|
}
|
|
15
|
-
// ── PURE KERNEL moved to ./session-sync-kernel.ts (design/158 A2) ─────────────────────────────────────────────
|
|
16
|
-
// The §7 relation classifier (classifySyncRelationship[ByIds]), the staging-id scheme (stagingIdFor /
|
|
17
|
-
// STAGING_ID_MARKER / realIdOfStaging), the StagingHandle / ImportStagingStore contracts and SyncConflictError
|
|
18
|
-
// lived here (lines ~26–187) but depend on NOTHING in this repo — while THIS module value-imports StoreBackend and
|
|
19
|
-
// type-imports security's OwnerAwareSessionStore. The three DB session adapters (local/pg/tidb) and security.ts
|
|
20
|
-
// need only those pure symbols, and security.ts importing them from HERE formed the repo's only true type cycle
|
|
21
|
-
// (security ↔ session-sync; lens2 §B/§E). They now live in the leaf module session-sync-kernel.ts (same shape as
|
|
22
|
-
// session-sync-content.ts) and are RE-EXPORTED verbatim below, so existing importers of this module keep working.
|
|
23
|
-
export { stagingIdFor, STAGING_ID_MARKER, realIdOfStaging, classifySyncRelationship, classifySyncRelationshipByIds, SyncConflictError } from "./session-sync-kernel.js";
|
|
24
15
|
/** Narrow `backend.session()` to the optional ownership/export seam (the concrete TiDB/PG/Local stores carry it;
|
|
25
16
|
* the bare in-memory dev store doesn't → the methods are optional, exactly like the fork route narrows). */
|
|
26
17
|
function ownerAware(backend) {
|
|
@@ -77,7 +68,8 @@ export async function exportSession(sessionId, srcBackend) {
|
|
|
77
68
|
// snapshots — listKeys → per-key manifest. exportManifest may be absent (no snapshot export seam) → no snapshots.
|
|
78
69
|
const fs = srcBackend.fileSnapshot();
|
|
79
70
|
const snapshots = [];
|
|
80
|
-
|
|
71
|
+
// [2373]B-5b:core 接口 listKeys 必填(恒真半支删);exportManifest? 可选——探测只留真可选的那半。
|
|
72
|
+
if (typeof fs.exportManifest === "function") {
|
|
81
73
|
const keys = await fs.listKeys(sessionId);
|
|
82
74
|
for (const key of keys) {
|
|
83
75
|
const manifest = await fs.exportManifest(sessionId, key);
|
|
@@ -93,9 +85,9 @@ export async function exportSession(sessionId, srcBackend) {
|
|
|
93
85
|
: [];
|
|
94
86
|
// anchors — every E18 resume anchor (eventId→entryId + source owner). resumeAnchor is REQUIRED on all backends.
|
|
95
87
|
const anchorStore = srcBackend.resumeAnchor();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
// [2373]B-5a:ResumeAnchorStore 是三实现的闭合联合、listBySession 全必填——feature-detect 恒真,
|
|
89
|
+
// `: []` 臂不可达([2195] deleteBySession 同判据,那轮漏了这对双胞胎)。直调。
|
|
90
|
+
const anchors = await anchorStore.listBySession(sessionId);
|
|
99
91
|
// getBlob — bound to the SOURCE snapshot store so the importer pulls bytes content-addressed (deduped), lazily.
|
|
100
92
|
// A source without getBlob (no blobs to move) supplies a getter that always returns undefined → snapshots with a
|
|
101
93
|
// missing blob fail the §10 validation below before any write (never a partial restore).
|
|
@@ -131,7 +123,8 @@ export async function exportSessionManifest(sessionId, srcBackend) {
|
|
|
131
123
|
// snapshots / policy / anchors — IDENTICAL to exportSession (no entry payloads loaded anywhere here).
|
|
132
124
|
const fs = srcBackend.fileSnapshot();
|
|
133
125
|
const snapshots = [];
|
|
134
|
-
|
|
126
|
+
// [2373]B-5b:core 接口 listKeys 必填(恒真半支删);exportManifest? 可选——探测只留真可选的那半。
|
|
127
|
+
if (typeof fs.exportManifest === "function") {
|
|
135
128
|
const keys = await fs.listKeys(sessionId);
|
|
136
129
|
for (const key of keys) {
|
|
137
130
|
const manifest = await fs.exportManifest(sessionId, key);
|
|
@@ -145,9 +138,9 @@ export async function exportSessionManifest(sessionId, srcBackend) {
|
|
|
145
138
|
? await policyStore.listBySession(sessionId)
|
|
146
139
|
: [];
|
|
147
140
|
const anchorStore = srcBackend.resumeAnchor();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
// [2373]B-5a:ResumeAnchorStore 是三实现的闭合联合、listBySession 全必填——feature-detect 恒真,
|
|
142
|
+
// `: []` 臂不可达([2195] deleteBySession 同判据,那轮漏了这对双胞胎)。直调。
|
|
143
|
+
const anchors = await anchorStore.listBySession(sessionId);
|
|
151
144
|
// getBlob — bound to the SOURCE snapshot store (content-addressed, lazy), exactly like exportSession.
|
|
152
145
|
const getBlobFn = fs.getBlob?.bind(fs);
|
|
153
146
|
const getBlob = getBlobFn ? (hash) => getBlobFn(hash) : async () => undefined;
|
package/dist/spec-fields.js
CHANGED
|
@@ -159,17 +159,17 @@ const skillOk = (s) => {
|
|
|
159
159
|
}
|
|
160
160
|
return true;
|
|
161
161
|
};
|
|
162
|
-
/** MemorySpecInput 闭合白名单(core dist/core/memory.d.ts
|
|
163
|
-
* 迭代 `scopes` —— 非数组形状(如 {scopes:{length:1}})在服务门不拦就是 core 运行期异常(cross-review 抓获)。
|
|
162
|
+
/** MemorySpecInput 闭合白名单(core dist/core/memory.d.ts 亲读)。core 的 normalizeMemorySpec 会直接
|
|
163
|
+
* 迭代 `scopes` —— 非数组形状(如 {scopes:{length:1}})在服务门不拦就是 core 运行期异常(cross-review 抓获)。
|
|
164
|
+
* core 5.1.0([2390]②,原 [2387] 预告):单数 `scope` 键已删,`scopes: string[]` 唯一拼法——白名单
|
|
165
|
+
* 同步剔键,收了就是穿门死键(用户以为设了 scope,core 静默无视)。 */
|
|
164
166
|
const memoryOk = (v) => {
|
|
165
167
|
if (!plainObject(v))
|
|
166
168
|
return false;
|
|
167
169
|
for (const k of Object.keys(v)) {
|
|
168
|
-
if (k !== "
|
|
170
|
+
if (k !== "scopes" && k !== "writeScope" && k !== "enabled" && k !== "scopeContract")
|
|
169
171
|
return false;
|
|
170
172
|
}
|
|
171
|
-
if (v.scope !== undefined && typeof v.scope !== "string")
|
|
172
|
-
return false;
|
|
173
173
|
if (v.scopes !== undefined && !(Array.isArray(v.scopes) && v.scopes.every((x) => typeof x === "string")))
|
|
174
174
|
return false;
|
|
175
175
|
if (v.writeScope !== undefined && v.writeScope !== null && typeof v.writeScope !== "string")
|
|
@@ -212,7 +212,7 @@ const TASK_AGENT_FIELD_SHAPES = {
|
|
|
212
212
|
desc: `a positive integer of at most ${MAX_AGENT_MAX_TURNS}`,
|
|
213
213
|
ok: (v) => typeof v === "number" && Number.isInteger(v) && v >= 1 && v <= MAX_AGENT_MAX_TURNS,
|
|
214
214
|
},
|
|
215
|
-
memory: { desc: "a memory spec object ({
|
|
215
|
+
memory: { desc: "a memory spec object ({ scopes?, writeScope?, enabled?, scopeContract? })", ok: memoryOk },
|
|
216
216
|
observer: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
217
217
|
observerMessage: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
218
218
|
};
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -189,33 +189,6 @@ export interface FsWriteGateWiring {
|
|
|
189
189
|
* byte-for-byte;这是个 tighten-only opt-in,不是默认开启——运营方需要显式选择开启)。 */
|
|
190
190
|
shellGate?: "always" | "classify";
|
|
191
191
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Build the per-request permission ToolPolicy from the parsed settings (mirrors shell `seam.ts` — see header).
|
|
194
|
-
* - base = name-keyed allow/deny (`createAllowDenyPolicy`): an `allow` list narrows to those tools; a `deny` list
|
|
195
|
-
* hard-blocks — composed deny-wins with an ASK-list ({@link createAskListPolicy}) so `permissions.ask` actually
|
|
196
|
-
* forces an approval gate (NOT silently dropped — review finding). `deny` > `ask` > `allow` on overlap.
|
|
197
|
-
* - `defaultMode` derive onto that base ([820]/[822] five-mode table; the fs-write gate needs `gate` wiring —
|
|
198
|
-
* without it every mode degrades to the base rules only, the pre-[816] behavior):
|
|
199
|
-
* `default` → base + `createFsWriteGatePolicy({rootPath: cwd, exemptDirs:[scratchpad], ask})` — CC
|
|
200
|
-
* manual: every fs write asks (the [816] P0 fix), scratchpad writes ride free.
|
|
201
|
-
* `acceptEdits` → base + the same factory with `acceptDirs:[cwd]` — writes INSIDE the cwd subtree
|
|
202
|
-
* auto-allow, OUTSIDE still ask (CC 207 dirname-variant card). NOT a loosen vs the
|
|
203
|
-
* deployment: the factory only ever emits allow/ask and is folded deny-wins, so a
|
|
204
|
-
* baseline deny/ask still wins; vs `default` it is simply a narrower CLIENT gate, and
|
|
205
|
-
* both are client-side ADDITIONS the client may size itself.
|
|
206
|
-
* `plan` → handsReadOnly + present_plan (unchanged; the write tools never reach an ask).
|
|
207
|
-
* `bypassPermissions` → base only, NO factory ([820] table: bypass = "不加门" not "开门" — deployment
|
|
208
|
-
* approval/governance baselines still apply; this is exactly every mode's pre-[816]
|
|
209
|
-
* derive, so bypass grants nothing that wasn't already granted).
|
|
210
|
-
* `auto` → base + the `default` factory (ask 兜底) — core's auto-mode classifier (entitlement-
|
|
211
|
-
* gated: RunnerDeps.autoMode ∧ runtimeCaps.autoMode, 1.276/1.277) screens asks UPSTREAM
|
|
212
|
-
* of resolveAsk, allowing safe calls and leaving suspicious ones on the ask path ([822]②).
|
|
213
|
-
* absent (undefined) → base only, NO factory: an expressed-no-mode request keeps its pre-[816] behavior
|
|
214
|
-
* (attaching the ask gate on a mere allow/deny-list stamp would regress headless callers
|
|
215
|
-
* that never asked for a mode; the shell always sends its mode explicitly).
|
|
216
|
-
* Returns the composed ToolPolicy + whether `handsReadOnly` should tighten. Returns `undefined` policy when there
|
|
217
|
-
* is no permission rule and no mode-derived gate (nothing to project).
|
|
218
|
-
*/
|
|
219
192
|
export declare function deriveSettingsPolicy(settings: ParsedTaskSettings, gate?: FsWriteGateWiring, workflowGate?: WorkflowGateWiring): {
|
|
220
193
|
toolPolicy?: ToolPolicy;
|
|
221
194
|
handsReadOnly?: boolean;
|
package/dist/task-settings.js
CHANGED
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
* - (`hooks` NO LONGER deferred either — 轴B #2 注释保鲜:applied 形已接线(本文件 hooks 分片 +
|
|
36
36
|
* main.ts 单用户闸),仅 malformed 才报 deferred;旧「需要 remote hook-runner」是接线前拍照。)
|
|
37
37
|
*/
|
|
38
|
-
import { combinePolicies, createAllowDenyPolicy, createFsWriteGatePolicy, createSensitivePathPolicy,
|
|
38
|
+
import { combinePolicies, createAllowDenyPolicy, createFsWriteGatePolicy, createSensitivePathPolicy, tightenTaskSpec, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, } from "@sema-agent/core";
|
|
39
|
+
import { findUnmatchableToolNames, formatUnmatchableToolNames } from "./config.js";
|
|
39
40
|
import { parseHooksConfig } from "./hooks/hook-runner.js";
|
|
40
41
|
/**
|
|
41
42
|
* R4: coerce a raw top-level `body.permissionMode` (the LIGHT per-turn wire — the shell sends the
|
|
@@ -247,13 +248,16 @@ export function acceptAppendSystemPrompt(v, warn, packDropsAppend) {
|
|
|
247
248
|
* CC-parity `permissions.ask` list — "prompt the human before THIS tool" — needs this thin policy. Composed
|
|
248
249
|
* deny-wins with the allow/deny base, so `ask` only ever tightens (a baseline `deny` still wins; `allow`→`ask`). */
|
|
249
250
|
function createAskListPolicy(askNames) {
|
|
250
|
-
//
|
|
251
|
-
// alias
|
|
252
|
-
//
|
|
253
|
-
|
|
251
|
+
// RAW matching (core 5.0.0 RB-476 — the canonicalToolName fold is GONE): the rule name is compared to
|
|
252
|
+
// `req.toolName` verbatim, no alias folding in either direction. A rule written against a retired alias
|
|
253
|
+
// ("MultiEdit"/"Task") therefore matches NOTHING — which is why such a list is rejected up front
|
|
254
|
+
// ({@link rejectUnmatchableSettingsNames}) instead of being silently mounted as a no-op gate.
|
|
255
|
+
// (旧注写的 "Canonical-space matching, mirroring core's createAllowDenyPolicy (1.202)" 是折叠面还在时的
|
|
256
|
+
// 拍照,与下面这行 RAW 代码整段矛盾——同批改正,连同 :348 的 "canonical toolName key space"。)
|
|
257
|
+
const askSet = new Set(askNames);
|
|
254
258
|
return {
|
|
255
259
|
check(req) {
|
|
256
|
-
return askSet.has(
|
|
260
|
+
return askSet.has(req.toolName)
|
|
257
261
|
? { action: "ask", message: `tool "${req.toolName}" requires approval by your settings`, decisionReason: "rule" }
|
|
258
262
|
: { action: "allow" };
|
|
259
263
|
},
|
|
@@ -266,7 +270,7 @@ function createAskListPolicy(askNames) {
|
|
|
266
270
|
/** [1245]/[1247]③ — the mode-derived dynamic-workflow ask gate (CC "Run a dynamic workflow?" dialog, engine half).
|
|
267
271
|
* core mounts `run_workflow` with effect="write" and NO exemption in the gate stack, but the mode-derived leg here
|
|
268
272
|
* only ever gated fs writes — so manual/default mode ran workflows un-asked ([1245] clay dogfood). This leg keys
|
|
269
|
-
* the canonical name
|
|
273
|
+
* the canonical name(`Workflow`;5.0.0:旧拼法 aliases 已随 core RB-476 退役,raw 直比)
|
|
270
274
|
* and attaches on default/auto/acceptEdits, independent of the fs-gate wiring (workflow gating is not
|
|
271
275
|
* fs-dependent — sandbox lanes get it too). acceptEdits asks ON PURPOSE: CC's acceptEdits auto-allows file edits
|
|
272
276
|
* only, a workflow confirmation is not an edit ([1248]② — cli to verify against the 212 live shell; flip there
|
|
@@ -277,16 +281,17 @@ const WORKFLOW_ASK_NAMES = ["Workflow"];
|
|
|
277
281
|
function createWorkflowAskPolicy(isExempt) {
|
|
278
282
|
return {
|
|
279
283
|
async check(req) {
|
|
280
|
-
if (
|
|
284
|
+
if (req.toolName !== "Workflow")
|
|
281
285
|
return { action: "allow" };
|
|
282
286
|
// codex F2 (durable lane): the "don't ask again this session" grant (decide remember="session" →
|
|
283
287
|
// approvalExemptionStore) is probed at the POLICY layer here — the durable lane parks every policy `ask`
|
|
284
288
|
// and its own exempt probe only covers the deployment policy's asks, so without this probe a remembered
|
|
285
289
|
// Workflow approval would re-park on every later call. Same posture as the fs-write gate's isExempt
|
|
286
|
-
// (
|
|
290
|
+
// (RAW toolName key space — 5.0.0 折叠面退役后各层比对的都是 `req.toolName` 逐字,一个 grant 服务所有层
|
|
291
|
+
// 靠的是「同一个 raw 名」而不是「同一个 canonical 空间」);a THROWN probe ⇒ not exempt (fail-closed).
|
|
287
292
|
if (isExempt) {
|
|
288
293
|
try {
|
|
289
|
-
if (await isExempt(
|
|
294
|
+
if (await isExempt(req.toolName))
|
|
290
295
|
return { action: "allow", decisionReason: "rule" };
|
|
291
296
|
}
|
|
292
297
|
catch {
|
|
@@ -307,7 +312,7 @@ function createWorkflowAskPolicy(isExempt) {
|
|
|
307
312
|
function createWorkflowPlanDenyPolicy() {
|
|
308
313
|
return {
|
|
309
314
|
check(req) {
|
|
310
|
-
return
|
|
315
|
+
return req.toolName === "Workflow"
|
|
311
316
|
? { action: "deny", message: `Plan mode is active — "${req.toolName}" is a write/mutating tool and is read-only-blocked.`, decisionReason: "rule" }
|
|
312
317
|
: { action: "allow" };
|
|
313
318
|
},
|
|
@@ -341,11 +346,37 @@ function createWorkflowPlanDenyPolicy() {
|
|
|
341
346
|
* Returns the composed ToolPolicy + whether `handsReadOnly` should tighten. Returns `undefined` policy when there
|
|
342
347
|
* is no permission rule and no mode-derived gate (nothing to project).
|
|
343
348
|
*/
|
|
349
|
+
/**
|
|
350
|
+
* A3 — the per-request leg of the ONE "this name can never match a live tool" judgment (single source:
|
|
351
|
+
* config.ts {@link findUnmatchableToolNames}; the other two legs are the `APPROVAL_*` env vars, which reject at
|
|
352
|
+
* boot, and the center-published `runtime.approvalRequire`, which rejects that domain and keeps the prior value).
|
|
353
|
+
*
|
|
354
|
+
* WHY this leg exists: `ask`/`deny` are mounted as RAW name comparisons (`createAskListPolicy` /
|
|
355
|
+
* core's `createAllowDenyPolicy`, the 5.0.0 fold removal), so a retired name ("Task"/"bash"/"KillShell") or a
|
|
356
|
+
* pre-prefix/incomplete MCP name ("figma__x"/"mcp__figma") mounts a gate that matches NOTHING — the caller
|
|
357
|
+
* believes it added an approval gate or a hard block and got neither. That is the FAIL-OPEN direction, and it was
|
|
358
|
+
* silent on this leg. Throwing here surfaces as a 422 at the HTTP boundary (resolve-spec already wraps every
|
|
359
|
+
* throw out of `applyTaskSettings`), i.e. the same fail-loud posture the other two legs have.
|
|
360
|
+
*
|
|
361
|
+
* `allow` is deliberately NOT judged: it is a WHITELIST — a name that matches nothing leaves the allowlist
|
|
362
|
+
* effectively empty, so every tool falls to "not in the allowlist" and is DENIED. That fails closed and is
|
|
363
|
+
* immediately visible to the caller, which is a different (and acceptable) failure mode from a silent hole.
|
|
364
|
+
*/
|
|
365
|
+
function rejectUnmatchableSettingsNames(perms) {
|
|
366
|
+
for (const [field, names] of [["permissions.ask", perms.ask], ["permissions.deny", perms.deny]]) {
|
|
367
|
+
if (!names)
|
|
368
|
+
continue;
|
|
369
|
+
const bad = findUnmatchableToolNames(names);
|
|
370
|
+
if (bad.length > 0)
|
|
371
|
+
throw new Error(formatUnmatchableToolNames(field, bad));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
344
374
|
export function deriveSettingsPolicy(settings, gate, workflowGate) {
|
|
345
375
|
const perms = settings.permissions;
|
|
346
376
|
if (!perms)
|
|
347
377
|
return {};
|
|
348
378
|
const { allow, deny, ask, defaultMode } = perms;
|
|
379
|
+
rejectUnmatchableSettingsNames(perms);
|
|
349
380
|
// The base name-keyed gate: allow/deny via core's helper + an ask-list (FINDING-1: `ask` must enforce, not drop).
|
|
350
381
|
// Each is composed deny-wins (`deny` > `ask` > `allow`), so the fold can only ever tighten.
|
|
351
382
|
const policies = [];
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AskRequest } from "@sema-agent/core";
|
|
1
|
+
import { type AskRequest, type AskOutcome } from "@sema-agent/core";
|
|
2
2
|
/** A live approval frame delivered to whoever tails this run's stream. `type` IS the SSE event name (named-event
|
|
3
3
|
* convention, same as question). The shell renders `tool_approval` as the CC three-choice card and dismisses on
|
|
4
4
|
* `tool_approval_complete`. */
|
|
@@ -103,7 +103,7 @@ export declare class ToolApprovalCoordinator {
|
|
|
103
103
|
* approverUnavailable 回路把这只 ask 交回 suspendAsk 走 durable park(park 设施缺席的部署 core 自己
|
|
104
104
|
* fail-closed deny)。1.199 的「durable 部署不 wire onAsk」止血就此撤除:恒 wire,park 与 live 卡两全。
|
|
105
105
|
* Arrow property so it can be passed as `onAsk: coordinator.ask` with `this` bound. */
|
|
106
|
-
ask: (req: AskRequest, signal?: AbortSignal) => Promise<
|
|
106
|
+
ask: (req: AskRequest, signal?: AbortSignal) => Promise<AskOutcome>;
|
|
107
107
|
/** [1535] server 半场(审批链战役,[1539] 定谳断点①的修;[1546] HIGH-1 broker 形):宿主 sync
|
|
108
108
|
* streaming 腿在装配点以本闭包挂 `spec.onAsk`;core 继承链(parentConstraints 冻结
|
|
109
109
|
* `spec.onAsk ?? deps.onAsk`,1.294 起)把它逐 ancestor 冻给每个委派子代——子代(bg/嵌套孙代)的
|
|
@@ -115,7 +115,7 @@ export declare class ToolApprovalCoordinator {
|
|
|
115
115
|
owner: string | null;
|
|
116
116
|
taskId: string;
|
|
117
117
|
sessionId?: string;
|
|
118
|
-
}) => ((req: AskRequest, signal?: AbortSignal) => Promise<
|
|
118
|
+
}) => ((req: AskRequest, signal?: AbortSignal) => Promise<AskOutcome>);
|
|
119
119
|
/** 广播版裁决路径——`ask()`(ALS,恒单元素数组)与 `boundAsk`([1559]四多活集合,可能多元素)共用。
|
|
120
120
|
* 全体 `ctxs` 保证同一 (owner, sessionId)(streamKey 分组不变式;单元素数组平凡成立)。
|
|
121
121
|
*
|
package/dist/tool-approval.js
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* and asks are turn-blocking by construction — the TTL bounds the human-side exposure instead.
|
|
42
42
|
*/
|
|
43
43
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
44
|
-
import { uuidv7
|
|
44
|
+
import { uuidv7 } from "@sema-agent/core";
|
|
45
45
|
import { redactDeep, redactSecrets } from "./trace/redact.js";
|
|
46
46
|
/** Size bound on the redacted args payload in a `tool_approval` frame (parity with question's MAX_QUESTIONS_BYTES).
|
|
47
47
|
* Over the cap ⇒ the frame still goes out WITHOUT args (`argsOmitted: true`) — unlike a question (undisplayable ⇒
|
|
@@ -53,17 +53,17 @@ const DEFAULT_APPROVAL_TTL_MS = 5 * 60_000;
|
|
|
53
53
|
/** Bound on the remembered allow-all grants (insertion-order evict — a long-lived worker must not grow unbounded). */
|
|
54
54
|
const MAX_ALLOW_SESSIONS = 4096;
|
|
55
55
|
/** The fs-write tool family the session allow-all collapses into ONE capability bucket (canonical space — a
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
const FS_WRITE_TOOLS = new Set(["Write", "Edit", "NotebookEdit"]
|
|
56
|
+
* 5.0.0 RB-476:折叠面退役,三名即 CC canonical(旧拼法在 core roster 层响亮 miss)。Mirrors core's
|
|
57
|
+
* PATH_WRITE_TOOLS + NotebookEdit (the same set `createFsWriteGatePolicy` gates); NOT exported by core's
|
|
58
|
+
* index, so the names are pinned here deliberately. */
|
|
59
|
+
const FS_WRITE_TOOLS = new Set(["Write", "Edit", "NotebookEdit"]);
|
|
60
60
|
/** The session allow-all CAPABILITY CATEGORY for a tool (修2): the fs-write family shares one bucket ("fs-write" —
|
|
61
61
|
* a yes-to-edits covers Write/Edit/NotebookEdit alike, CC 207 semantics); every other tool is its OWN bucket
|
|
62
62
|
* ("tool:<canonical>") so an allow_session on it never grants anything beyond that same tool — a low-risk ask must
|
|
63
63
|
* not become a session-wide write unlock. Canonical space in, category out. */
|
|
64
64
|
function sessionAllowCategory(toolName) {
|
|
65
|
-
|
|
66
|
-
return FS_WRITE_TOOLS.has(
|
|
65
|
+
// RAW(5.0.0):live 名恒 canonical;存量 grant 键当年也是 canonical 空间铸的,键空间不变。
|
|
66
|
+
return FS_WRITE_TOOLS.has(toolName) ? "fs-write" : `tool:${toolName}`;
|
|
67
67
|
}
|
|
68
68
|
/** The allow-all grant key (修2): owner + sessionId + capability category, JSON-tuple-encoded so no segment can
|
|
69
69
|
* ambiguate the boundary with the next (an owner or sessionId containing a separator can't forge another key).
|
|
@@ -396,9 +396,9 @@ export class ToolApprovalCoordinator {
|
|
|
396
396
|
// 方法顶注。
|
|
397
397
|
if (!emitSettled && lastOutcome === "expired")
|
|
398
398
|
return "unavailable";
|
|
399
|
-
// [1458]
|
|
400
|
-
//
|
|
401
|
-
//
|
|
399
|
+
// [1458] 编辑放行对象臂:core AskOutcome 公开类型已含 `{allow, updatedInput?}`([1462])——
|
|
400
|
+
// 宽铸退役,真类型直返(core BAGGAGE #17 严格化前提=对象臂 allow 恒严格 true,我方满足)。
|
|
401
|
+
// deny 恒返 false(falsy 纪律)。
|
|
402
402
|
if (settled.allowed && settled.updatedInput !== undefined) {
|
|
403
403
|
return { allow: true, updatedInput: settled.updatedInput };
|
|
404
404
|
}
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trace.artifacts (M2) — project "what the agent produced" from the durable event log.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic projection, no new write path: artifacts are DERIVED from the already-persisted
|
|
5
|
+
* tool_start/tool_end pairs (S2 log), so they exist for every historical run and cannot drift from execution.
|
|
6
|
+
* v1 produces two kinds:
|
|
7
|
+
* - `file` — a successful `Write` / `Edit` call (deduped per path; the summary counts edits).
|
|
8
|
+
* - `git-push` — a successful `bash` call whose command contains `git push` (ref = the parsed refspec tail).
|
|
9
|
+
* `diff` is declared in the wire schema but NOT produced in v1 (we don't capture patch bodies in the log) —
|
|
10
|
+
* consumers must treat kinds as an open set. Args in the log are already secret-redacted at persist time.
|
|
11
|
+
*/
|
|
1
12
|
import type { RunEvent } from "../plugins/store-contracts.js";
|
|
2
13
|
export interface TraceArtifact {
|
|
3
14
|
/** Stable within the task: `<taskId>:<n>` in first-seen order — safe as a UI key + dedupe handle. */
|
package/dist/trace/artifacts.js
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* trace.artifacts (M2) — project "what the agent produced" from the durable event log.
|
|
3
|
-
*
|
|
4
|
-
* Deterministic projection, no new write path: artifacts are DERIVED from the already-persisted
|
|
5
|
-
* tool_start/tool_end pairs (S2 log), so they exist for every historical run and cannot drift from execution.
|
|
6
|
-
* v1 produces two kinds:
|
|
7
|
-
* - `file` — a successful `Write` / `Edit` call (deduped per path; the summary counts edits).
|
|
8
|
-
* - `git-push` — a successful `bash` call whose command contains `git push` (ref = the parsed refspec tail).
|
|
9
|
-
* `diff` is declared in the wire schema but NOT produced in v1 (we don't capture patch bodies in the log) —
|
|
10
|
-
* consumers must treat kinds as an open set. Args in the log are already secret-redacted at persist time.
|
|
11
|
-
*/
|
|
12
|
-
import { canonicalToolName } from "@sema-agent/core";
|
|
13
1
|
/** Line count of a tool-arg string ("" = 0; no trailing-newline inflation). */
|
|
14
2
|
function lines(v) {
|
|
15
3
|
if (typeof v !== "string" || v.length === 0)
|
|
16
4
|
return 0;
|
|
17
5
|
return v.split("\n").length;
|
|
18
6
|
}
|
|
19
|
-
// Q6 (core 1.161) CC names
|
|
20
|
-
//
|
|
21
|
-
//
|
|
7
|
+
// Q6 (core 1.161) CC names。5.0.0 RB-476:折叠面退役——本投影跑在历史事件日志上,pre-rename 旧行
|
|
8
|
+
// (如 1.171 时代的 "MultiEdit")不再折到 canonical:旧名行按原名滑过 FILE_TOOLS 判定=不进 artifacts
|
|
9
|
+
// 归类(诚实历史,读面不再兜旧名;耐久面按 [2364] 令牺牲)。
|
|
22
10
|
const FILE_TOOLS = new Set(["Write", "Edit", "NotebookEdit"]);
|
|
23
11
|
const GIT_PUSH_RE = /\bgit\b[^;|&]*\bpush\b\s*(.*)/;
|
|
24
12
|
/** Project the durable event log into artifacts. Events must be in seq order (getEvents guarantees it). */
|
|
@@ -39,11 +27,12 @@ export function projectArtifacts(events, run) {
|
|
|
39
27
|
if (!start)
|
|
40
28
|
continue;
|
|
41
29
|
starts.delete(d.toolCallId); // each call contributes at most once
|
|
42
|
-
const toolName =
|
|
30
|
+
const toolName = start.toolName; // RAW(5.0.0):折叠面退役,历史旧名行按原名处理(见文件头注)
|
|
43
31
|
if (FILE_TOOLS.has(toolName)) {
|
|
44
|
-
// Write/Edit key the write target on `file_path
|
|
45
|
-
//
|
|
46
|
-
//
|
|
32
|
+
// Write/Edit key the write target on `file_path`. core 5.1.0([2390])收窄 schema 为 `file_path` 单形,
|
|
33
|
+
// 但**门读侧 over-read 保留**是同批 KEEP 裁定(fail-closed 方向)——这里是耐久历史行的读侧([2373] D 类),
|
|
34
|
+
// pre-1.169 emit/旧 durable checkpoint 仍带 `path`,回落臂是对自己旧数据的容忍,不是对上游新帧的兼容。
|
|
35
|
+
// 再回落 NotebookEdit 的 `notebook_path`(core 1.163)。三键全缺 → 非带路径调用。
|
|
47
36
|
const path = typeof start.args["file_path"] === "string" ? start.args["file_path"] : typeof start.args["path"] === "string" ? start.args["path"] : typeof start.args["notebook_path"] === "string" ? start.args["notebook_path"] : undefined;
|
|
48
37
|
if (!path)
|
|
49
38
|
continue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { redactSecrets } from "./redact.js";
|
|
2
|
-
import { toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, workspaceChangedEventData, turnEndEventData, contextUsageEventData } from "./project.js";
|
|
2
|
+
import { toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, turnEndEventData, contextUsageEventData } from "./project.js";
|
|
3
3
|
import { NotifiedKeys, taskNotificationStreamKey } from "../orchestration/workflow-completion-inbox.js";
|
|
4
4
|
export function createLedgerSink(opts) {
|
|
5
5
|
const keys = opts.notifiedKeys ?? new NotifiedKeys();
|
|
@@ -101,6 +101,12 @@ export function createLedgerSink(opts) {
|
|
|
101
101
|
await flush();
|
|
102
102
|
await append("steering_injected", steeringInjectedEventData(ev));
|
|
103
103
|
break;
|
|
104
|
+
case "compaction_outcome":
|
|
105
|
+
// [2373]A-2/C-2:此前 durable 双腿零 case ⇒ 回放即消失(与 [1622] label / [1611] workflowRunId
|
|
106
|
+
// 同族)。共享 builder(§E1 脱敏)三腿同源;差集门 durable-append-arm-parity 看守。
|
|
107
|
+
await flush();
|
|
108
|
+
await append("compaction_outcome", compactionOutcomeEventData(ev));
|
|
109
|
+
break;
|
|
104
110
|
case "workspace_changed":
|
|
105
111
|
// [1559]二 core 裁定:design/99 §E13 旧臂,L1 门扩臂时首次撞见。durable 记账与其余内容型臂
|
|
106
112
|
// 同处置(轨迹回放时「何时 cd 到了哪」是合法历史事实,「不当持久态渲染」是 LIVE 消费端纪律,
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -235,6 +235,17 @@ export declare function steeringInjectedEventData(ev: {
|
|
|
235
235
|
eventId?: string;
|
|
236
236
|
parentToolCallId?: string;
|
|
237
237
|
}): Record<string, unknown>;
|
|
238
|
+
/** `compaction_outcome`(core 2.5.0 [2105]②:压缩非 compacted 结局——manual /compact 的 mooted/failed
|
|
239
|
+
* 两条真路)。[2373]A-2/C-2:此前它是**唯一**不走共享 builder、只在 live 腿 res.write 内联挑键的
|
|
240
|
+
* 内容帧 ⇒ 两条 durable 腿零 case、回放即消失、三方普查各自失明。收进共享白名单 builder,三腿同源。
|
|
241
|
+
* outcome/trigger=闭枚举 verbatim;reason=引擎说明文本(可能引用配置值)→ redactSecrets;identity 同款。 */
|
|
242
|
+
export declare function compactionOutcomeEventData(ev: {
|
|
243
|
+
outcome?: unknown;
|
|
244
|
+
trigger?: unknown;
|
|
245
|
+
reason?: unknown;
|
|
246
|
+
eventId?: unknown;
|
|
247
|
+
parentToolCallId?: unknown;
|
|
248
|
+
}): Record<string, unknown>;
|
|
238
249
|
/** The `workspace_changed` payload([1559]二 core 裁定:design/99 §E13 旧臂,L1 门本车首次撞见——非新增,
|
|
239
250
|
* 是本门扩 TaskEvent 臂集时第一次显式处置。语义 = bash `cd`/EnterWorktree 移动任务逻辑 cwd 后,在移动
|
|
240
251
|
* 它的工具结算后发一帧,给 shell/宿主 UI 一个路径提示行。cwd 与已展示的工具 args 同信任级[UNTRUSTED,
|
|
@@ -347,6 +358,12 @@ export declare function turnEndEventData(ev: {
|
|
|
347
358
|
* `in` 落回旧值,读端零伤)。`uncachedIn` 只在**能确认自己踩在新契约帧上**(`totalInputTokens` 在场)时
|
|
348
359
|
* 才发——旧帧的 `inputTokens` 是族依赖的旧语义,标成「uncachedIn(cache-MISS)」会是谎言。 */
|
|
349
360
|
export declare function toContractTokens(usage: unknown): TraceTurn["tokens"] | undefined;
|
|
361
|
+
/** [2373]C-13:tool_start/tool_end 的**共享挑键**——`projectEvents`(turns 面)与 `mapTraceEvent`
|
|
362
|
+
* (trace SSE 面)对同一账本行的字段选择逐字同构,此前两处独立手写,structured/totalChars/label
|
|
363
|
+
* 三次扩字段各改两遍(见两处对偶注)。收一份:两面各自包装键名皮(id/name/input vs 同名),
|
|
364
|
+
* 字段集合从此单点演进。 */
|
|
365
|
+
export declare function toolCallFieldsOf(d: Record<string, unknown>): Record<string, unknown>;
|
|
366
|
+
export declare function toolResultFieldsOf(d: Record<string, unknown>): Record<string, unknown>;
|
|
350
367
|
export interface ProjectOpts {
|
|
351
368
|
/** Prepend the user turn (the objective is not in the event log; the endpoint supplies it). */
|
|
352
369
|
objective?: string;
|