@sema-agent/server 7.4.0 → 7.6.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/LICENSE +1 -1
- package/README.md +18 -3
- package/README.zh-CN.md +14 -3
- package/USAGE.md +80 -1
- package/dist/approval-card.d.ts +15 -3
- package/dist/approval-card.js +41 -7
- package/dist/approval-reconciler.d.ts +109 -12
- package/dist/approval-reconciler.js +152 -24
- package/dist/boot/config-center.js +15 -2
- package/dist/boot/coordinators.js +10 -2
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/org-memory.d.ts +6 -0
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/parked-revive-gate.d.ts +78 -0
- package/dist/boot/parked-revive-gate.js +114 -0
- package/dist/boot/reapers.d.ts +2 -0
- package/dist/boot/reapers.js +11 -4
- package/dist/boot/resolve-spec.d.ts +3 -19
- package/dist/boot/resolve-spec.js +73 -67
- package/dist/boot/runner-deps.d.ts +23 -1
- package/dist/boot/runner-deps.js +8 -11
- package/dist/boot/workflow-orchestration.d.ts +8 -3
- package/dist/boot/workflow-orchestration.js +23 -1
- package/dist/budget.d.ts +1 -1
- package/dist/budget.js +1 -1
- package/dist/capabilities/repo-tools.d.ts +1 -1
- package/dist/capabilities/repo-tools.js +8 -2
- package/dist/config-center/apply-effective.js +33 -10
- package/dist/config-provider.d.ts +1 -0
- package/dist/config-provider.js +23 -3
- package/dist/config-types.d.ts +27 -9
- package/dist/config.d.ts +6 -1
- package/dist/config.js +61 -15
- package/dist/deployment-governance.d.ts +168 -0
- package/dist/deployment-governance.js +206 -0
- package/dist/env-facts.d.ts +3 -1
- package/dist/env-facts.js +3 -1
- package/dist/fleet/fleet-bus.d.ts +17 -2
- package/dist/fleet/fleet-bus.js +68 -3
- package/dist/fleet/fleet-terminal-window.d.ts +98 -0
- package/dist/fleet/fleet-terminal-window.js +316 -0
- package/dist/governance-ask-marks.d.ts +31 -0
- package/dist/governance-ask-marks.js +122 -0
- package/dist/hooks/hook-runner.d.ts +28 -0
- package/dist/hooks/hook-runner.js +149 -25
- package/dist/http/routes/approvals-assistant.js +6 -7
- package/dist/http/routes/diagnostics.js +10 -5
- package/dist/http/routes/fleet.js +160 -14
- package/dist/http/routes/memory-policy.d.ts +2 -1
- package/dist/http/routes/memory-policy.js +77 -13
- package/dist/http/routes/runs.js +6 -2
- package/dist/http/routes/tasks.js +59 -22
- package/dist/http/routes/trace-usage.js +3 -4
- package/dist/http/send.d.ts +23 -0
- package/dist/http/send.js +23 -0
- package/dist/http/server.d.ts +9 -0
- package/dist/http/server.js +28 -14
- package/dist/http/sse-log.js +3 -4
- package/dist/http/wire-types.d.ts +7 -2
- package/dist/leader/diffout.d.ts +10 -0
- package/dist/leader/diffout.js +14 -2
- package/dist/leader/diffup.js +3 -2
- package/dist/leader/planner.js +7 -0
- package/dist/main.js +39 -31
- package/dist/observability/fail-open.d.ts +17 -2
- package/dist/observability/fail-open.js +19 -4
- package/dist/observability/prompt-manifest.d.ts +5 -1
- package/dist/orchestration/workflow-notify-journal.d.ts +58 -2
- package/dist/orchestration/workflow-notify-journal.js +130 -45
- package/dist/parked-decide.js +9 -4
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -2
- package/dist/plugins/approval-ask-store-memory.js +3 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +60 -5
- package/dist/plugins/approval-ask-store-sql.js +75 -35
- package/dist/plugins/background-agent-store-sql.js +16 -16
- package/dist/plugins/background-shell-support.d.ts +1 -1
- package/dist/plugins/background-shell-support.js +2 -2
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +67 -8
- package/dist/plugins/checkpoint-store-sql.js +76 -13
- package/dist/plugins/image-bake-store-sql.d.ts +1 -1
- package/dist/plugins/image-bake-store-sql.js +27 -27
- package/dist/plugins/image-index-sql.js +15 -15
- package/dist/plugins/local-checkpoint-store.d.ts +20 -1
- package/dist/plugins/local-checkpoint-store.js +19 -0
- package/dist/plugins/mailbox-store-sql.d.ts +4 -10
- package/dist/plugins/mailbox-store-sql.js +59 -6
- package/dist/plugins/memory-engine-pg.js +9 -9
- package/dist/plugins/memory-engine-tidb.js +7 -7
- package/dist/plugins/memory-sync-store-pg.js +13 -13
- package/dist/plugins/memory-sync-store-tidb.js +5 -5
- package/dist/plugins/outcome-ledger-sql.js +7 -7
- package/dist/plugins/pg-cost-quota.js +3 -3
- package/dist/plugins/pg-pool.js +84 -75
- package/dist/plugins/pg-rate-limiter.js +3 -3
- package/dist/plugins/pg-session-storage.d.ts +1 -1
- package/dist/plugins/pg-session-storage.js +12 -13
- package/dist/plugins/remote-env-host.js +3 -1
- package/dist/plugins/remote-env-local-docker.js +6 -3
- package/dist/plugins/remote-env-ssh.d.ts +13 -1
- package/dist/plugins/roster-store-sql.js +8 -8
- package/dist/plugins/store-contracts.d.ts +19 -0
- package/dist/plugins/store-contracts.js +42 -0
- package/dist/plugins/task-attachment-store.js +5 -5
- package/dist/plugins/task-list-store-sql.js +1 -1
- package/dist/plugins/tidb-cost-quota.js +1 -1
- package/dist/plugins/tidb-pool.js +83 -60
- package/dist/plugins/tidb-rate-limiter.js +1 -1
- package/dist/plugins/tidb-session-store.js +2 -5
- package/dist/plugins/tool-result-store-sql.js +2 -2
- package/dist/plugins/usage-window-store-sql.js +13 -13
- package/dist/plugins/write-behind-counter.d.ts +10 -2
- package/dist/plugins/write-behind-counter.js +13 -3
- package/dist/resource-suspend.d.ts +3 -1
- package/dist/resource-suspend.js +3 -1
- package/dist/run-local.d.ts +73 -1
- package/dist/run-local.js +146 -5
- package/dist/runs.d.ts +11 -1
- package/dist/runs.js +18 -3
- package/dist/runtime-governance.d.ts +18 -0
- package/dist/runtime-governance.js +90 -3
- package/dist/security.d.ts +12 -0
- package/dist/security.js +12 -0
- package/dist/session-sync-kernel.d.ts +13 -0
- package/dist/session-sync-kernel.js +13 -0
- package/dist/task-settings.d.ts +3 -9
- package/dist/task-settings.js +16 -13
- package/dist/tool-approval.d.ts +33 -6
- package/dist/tool-approval.js +80 -23
- package/dist/trace/core-keyset-guard.d.ts +18 -4
- package/dist/trace/project.d.ts +10 -1
- package/dist/trace/project.js +31 -0
- package/package.json +3 -3
- package/dist/boot/lexical-path-env.d.ts +0 -10
- package/dist/boot/lexical-path-env.js +0 -88
- package/dist/capabilities/oa-tools.d.ts +0 -15
- package/dist/capabilities/oa-tools.js +0 -54
- package/dist/finance/cost-taxonomy.d.ts +0 -34
- package/dist/finance/cost-taxonomy.js +0 -26
- package/dist/plugins/approval-store-sql.d.ts +0 -116
- package/dist/plugins/approval-store-sql.js +0 -151
- 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
- package/dist/sema-registry.d.ts +0 -41
- package/dist/sema-registry.js +0 -40
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type MemoryBackend, type RunnerDeps
|
|
1
|
+
import { type MemoryBackend, type RunnerDeps } from "@sema-agent/core";
|
|
2
2
|
import type { PromptsDomainFaces } from "../prompts-domain-validate.js";
|
|
3
3
|
import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
|
|
4
4
|
import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
|
|
5
5
|
import { selectScenario } from "../capabilities/scenarios.js";
|
|
6
6
|
import type { ServiceConfig } from "../config.js";
|
|
7
|
+
import { type LiveQuestionFace } from "../deployment-governance.js";
|
|
7
8
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
8
9
|
import { type HookLlmCall } from "../hooks/hook-runner.js";
|
|
9
10
|
import type { createHookLlm } from "../hooks/hook-llm.js";
|
|
@@ -58,9 +59,7 @@ export interface ResolveSpecCtx {
|
|
|
58
59
|
/** #152 ([2703] 案二):活体 AskUserQuestion 面(QuestionCoordinator 的判决探针切面)。在场(=
|
|
59
60
|
* ASK_QUESTION_ENABLED)时 durable question 门按活流上下文分腿、spec 不再 stamp QUESTION_AWAITS_RESUME
|
|
60
61
|
* (否则 spec.onQuestion 恒遮蔽 RunnerDeps.onQuestion 的活人腿);缺席时行为与旧形逐字一致。 */
|
|
61
|
-
liveQuestionFace:
|
|
62
|
-
hasLiveContext(): boolean;
|
|
63
|
-
} | undefined;
|
|
62
|
+
liveQuestionFace: LiveQuestionFace | undefined;
|
|
64
63
|
approvalExemptionStore: ReturnType<StoreBackend["approvalExemption"]> | undefined;
|
|
65
64
|
singleUserAutoAcceptBaseline: boolean;
|
|
66
65
|
checkpointStore: ReturnType<NonNullable<StoreBackend["checkpoint"]>> | undefined;
|
|
@@ -69,21 +68,6 @@ export interface ResolveSpecCtx {
|
|
|
69
68
|
perTaskImage: PerTaskImageRegistry;
|
|
70
69
|
sessionEnvSelection: SessionEnvironmentSelection;
|
|
71
70
|
}
|
|
72
|
-
/** #152([2703] 案二):durable 部署上的 AskUserQuestion 门。活体面(QuestionCoordinator)缺席 ⇒ 原形
|
|
73
|
-
* `createDurableQuestionPolicy()`(恒 ask ⇒ 恒 durable park)。在场 ⇒ **判决时**按活流上下文分腿:
|
|
74
|
-
* 活流腿(bg/SSE,coordinator.runWithContext 包裹且投递面此刻可达,ALS 判)allow——工具执行落到
|
|
75
|
-
* RunnerDeps.onQuestion 的 coordinator,问正在 tail 流的活人;无活流腿(sync /v1/tasks、verify/cascade、
|
|
76
|
-
* durable resume 驱动、断连后的 detach 腿)ask——durable park 原语义逐字保留(此时执行只会拿到 headless
|
|
77
|
-
* 空答,park 才是把问题送到人面前的那条腿)。
|
|
78
|
-
* 工具名字面量与 server.ts 的 pre-CAS 守卫同源("AskUserQuestion",core 未根导出常量)。
|
|
79
|
-
* ⚠️ **单一属主**(复审 A2):AskUserQuestion 的 durable 判决只有这一处。任何需要「同参重建」这条判决的
|
|
80
|
-
* 地方(main.ts 的 parkedReviveInheritedGate 父约束链)必须调本工厂,不得自折 core 原形——两份拷贝里
|
|
81
|
-
* 只改一份正是本条 finding 的成因。**登记豁免一处**:leader worker 腿(src/leader/wire.ts provisionWorker)
|
|
82
|
-
* 自折 core 原形——该腿无活体问答面可装且 leader 不 import boot 层(分层),core 原形+sentinel 即其完整
|
|
83
|
-
* 语义;豁免注在彼处互指,接活体面之日必须并回本工厂。 */
|
|
84
|
-
export declare function createDurableQuestionGate(live: {
|
|
85
|
-
hasLiveContext(): boolean;
|
|
86
|
-
} | undefined): ToolPolicy;
|
|
87
71
|
export declare function createResolveSpec(ctx: ResolveSpecCtx): ServiceDeps["resolveSpec"];
|
|
88
72
|
export {};
|
|
89
73
|
//# sourceMappingURL=resolve-spec.d.ts.map
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { realpathSync } from "node:fs";
|
|
21
21
|
import { join } from "node:path";
|
|
22
|
-
import { NodeExecutionEnv, QUESTION_AWAITS_RESUME,
|
|
23
|
-
import { createDurableAskPolicy } from "../approval.js";
|
|
22
|
+
import { NodeExecutionEnv, QUESTION_AWAITS_RESUME, expandTiers, tightenTaskSpec } from "@sema-agent/core";
|
|
24
23
|
import { cappedCeiling } from "../budget.js";
|
|
25
24
|
import { centerPromptProvider, centerIdentityAssembled } from "../capabilities/center-prompts.js";
|
|
26
25
|
import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
|
|
27
26
|
import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
|
|
28
27
|
import { gateScenarioRequest, mergeUserSkills, selectScenario } from "../capabilities/scenarios.js";
|
|
29
28
|
import { applyLongtailDefer } from "../capabilities/tool-defer.js";
|
|
29
|
+
import { assertGuardPatternsUsable, buildOnlySensitiveBaselineWarning, createApprovalBaselinePolicy, createDeploymentGovernanceInputs } from "../deployment-governance.js";
|
|
30
30
|
import { acceptShellScratchpadDir, buildEnvFacts, egressForRemoteExec, ensureScratchpadDir, resumeFactsForLane } from "../env-facts.js";
|
|
31
31
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
32
32
|
import { composeHooks, createTaskHooks } from "../hooks/hook-runner.js";
|
|
@@ -49,33 +49,25 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSet
|
|
|
49
49
|
import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
|
|
50
50
|
import { redactSecrets } from "../trace/redact.js";
|
|
51
51
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
52
|
-
/** #152([2703] 案二):durable 部署上的 AskUserQuestion 门。活体面(QuestionCoordinator)缺席 ⇒ 原形
|
|
53
|
-
* `createDurableQuestionPolicy()`(恒 ask ⇒ 恒 durable park)。在场 ⇒ **判决时**按活流上下文分腿:
|
|
54
|
-
* 活流腿(bg/SSE,coordinator.runWithContext 包裹且投递面此刻可达,ALS 判)allow——工具执行落到
|
|
55
|
-
* RunnerDeps.onQuestion 的 coordinator,问正在 tail 流的活人;无活流腿(sync /v1/tasks、verify/cascade、
|
|
56
|
-
* durable resume 驱动、断连后的 detach 腿)ask——durable park 原语义逐字保留(此时执行只会拿到 headless
|
|
57
|
-
* 空答,park 才是把问题送到人面前的那条腿)。
|
|
58
|
-
* 工具名字面量与 server.ts 的 pre-CAS 守卫同源("AskUserQuestion",core 未根导出常量)。
|
|
59
|
-
* ⚠️ **单一属主**(复审 A2):AskUserQuestion 的 durable 判决只有这一处。任何需要「同参重建」这条判决的
|
|
60
|
-
* 地方(main.ts 的 parkedReviveInheritedGate 父约束链)必须调本工厂,不得自折 core 原形——两份拷贝里
|
|
61
|
-
* 只改一份正是本条 finding 的成因。**登记豁免一处**:leader worker 腿(src/leader/wire.ts provisionWorker)
|
|
62
|
-
* 自折 core 原形——该腿无活体问答面可装且 leader 不 import boot 层(分层),core 原形+sentinel 即其完整
|
|
63
|
-
* 语义;豁免注在彼处互指,接活体面之日必须并回本工厂。 */
|
|
64
|
-
export function createDurableQuestionGate(live) {
|
|
65
|
-
if (live === undefined)
|
|
66
|
-
return createDurableQuestionPolicy();
|
|
67
|
-
return {
|
|
68
|
-
check(req) {
|
|
69
|
-
if (req.toolName !== "AskUserQuestion")
|
|
70
|
-
return { action: "allow" };
|
|
71
|
-
return live.hasLiveContext()
|
|
72
|
-
? { action: "allow" }
|
|
73
|
-
: { action: "ask", message: "AskUserQuestion: awaiting a human answer (durable)" };
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
52
|
export function createResolveSpec(ctx) {
|
|
78
53
|
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, } = ctx;
|
|
54
|
+
// ── #177 的两件 boot 期收口(把守卫集搬到 governance 拍之后才成立的两条)────────────────────────
|
|
55
|
+
//
|
|
56
|
+
// ① 守卫集的**编译**从此每个请求都发生(旧家只在 default/auto/acceptEdits 三个模式臂里发生)。
|
|
57
|
+
// 非法旋钮值必须当场炸在启动上而不是每任务一条 500 —— 判据与文案随 design/181 件一搬进构造口
|
|
58
|
+
// (`assertGuardPatternsUsable`),赎回腿/run-local 装配时调同一只,三腿同得。
|
|
59
|
+
assertGuardPatternsUsable(config);
|
|
60
|
+
// ② governance 拍的 DENY 基线让 `spec.toolPolicy` 在**任何**客户端表态下都在场,于是 core 的
|
|
61
|
+
// 「write-capable hand tools are present but UNGATED」那条 onError 不再触发(core 判据是
|
|
62
|
+
// `policyLayers.length > 0`,prepare-task dist 亲读)。它此前是「部署一个门都没接」这个 misconfig
|
|
63
|
+
// 的唯一信号,而守卫集只挡那二十来个路径段、其余写目标照旧无裁决 —— 信号不能因为本件静默消失。
|
|
64
|
+
// 条件 = 两条产 toolPolicy 的腿都不在场(durable 门关 ∧ 单用户 auto-accept 基线不适用),即修前
|
|
65
|
+
// `spec.toolPolicy` 恒缺席的那一形;三个量都是 boot 常量,所以这里说一次而不是每任务说一次。
|
|
66
|
+
// 判据与文案随 design/181 件三搬进构造口(`buildOnlySensitiveBaselineWarning`),run-local 腿调同
|
|
67
|
+
// 一只 —— 本处零行为变化(条件与字段逐字同前)。
|
|
68
|
+
const onlySensitiveBaseline = buildOnlySensitiveBaselineWarning(config, { durableEnabled, singleUserAutoAcceptBaseline });
|
|
69
|
+
if (onlySensitiveBaseline)
|
|
70
|
+
logger.warn(onlySensitiveBaseline.event, onlySensitiveBaseline.fields);
|
|
79
71
|
// Deployment-owned mapping. Tools/prompt/skills come from the selected scenario (assembled once,
|
|
80
72
|
// bound per request); identity/session/policy stay server-side. Never taken from the request body.
|
|
81
73
|
// design/158 A8:从 createHttpServer 实参里提出来(原地占该字面量 1,093 行中的 967 行)——
|
|
@@ -564,8 +556,9 @@ export function createResolveSpec(ctx) {
|
|
|
564
556
|
// [1144]/[1146] (core 1.328 R2): promptProfile 双形轴直通——纯呈现轴无租户门(deferTools 姿势);
|
|
565
557
|
// 缺省不挂键(引擎缺省 simple);非法值由 HTTP 门 400,resume 重放 defensive DROP。
|
|
566
558
|
...(() => { const v = promptProfileFromBody(body.promptProfile); return v ? { promptProfile: v } : {}; })(),
|
|
567
|
-
// [2856]②(core 5.14.0):deferred 工具激活后的 schema
|
|
568
|
-
// `spec.toolMaterializeStrategy ?? env ?? "
|
|
559
|
+
// [2856]②(core 5.14.0 引入;5.15.0 BREAKING 末项默认翻 swap):deferred 工具激活后的 schema 供给
|
|
560
|
+
// 策略,per-task 位。core 侧读法是 `spec.toolMaterializeStrategy ?? env ?? "swap"`(5.16.0 dist
|
|
561
|
+
// prepare-task 亲读)⇒ **缺省不挂键**才能让进程级 env 继续说话;
|
|
569
562
|
// 挂一个「缺省值」上去会把 env 车道悄悄夺权。非法值由 HTTP 门 400,resume 重放 defensive DROP。
|
|
570
563
|
...(() => { const v = toolMaterializeStrategyFromBody(body.toolMaterializeStrategy); return v ? { toolMaterializeStrategy: v } : {}; })(),
|
|
571
564
|
// [922]①/[915]② (core 1.296): interactiveTools 三态旋钮直通——boolean 上 spec(壳 -p 恒 stamp false=
|
|
@@ -750,9 +743,11 @@ export function createResolveSpec(ctx) {
|
|
|
750
743
|
// the QuestionAnswer (server.ts `body.answer` → onQuestion closure). #152([2703] 案二): the question
|
|
751
744
|
// policy is LIVE-AWARE when the QuestionCoordinator is wired — a live-stream leg adjudicates `allow`
|
|
752
745
|
// (the live human answers over the stream) instead of unconditionally parking; see durableQuestionPolicy.
|
|
746
|
+
// design/181 件一:基线的**构造**归 `createApprovalBaselinePolicy`(三腿单一属主);本处只做部署形
|
|
747
|
+
// 判断——哪一形该有 durable 轴、哪一形铺 allow-all、哪一形留 `undefined`(见下两条注)。
|
|
753
748
|
toolPolicy: durableEnabled
|
|
754
|
-
?
|
|
755
|
-
|
|
749
|
+
? createApprovalBaselinePolicy(config, {
|
|
750
|
+
question: liveQuestionFace,
|
|
756
751
|
// The probe key is the CONTINUED session (auth.sessionId — the same id that keys the
|
|
757
752
|
// durable checkpoint /decide route). A fresh session (no body.sessionId) has no exemptions by
|
|
758
753
|
// construction; fail-closed inside the policy on a store error.
|
|
@@ -762,20 +757,21 @@ export function createResolveSpec(ctx) {
|
|
|
762
757
|
onExempted: (toolName, rawToolName) => logger.info("approval_exempted", { sessionId: auth.sessionId, toolName, rawToolName }),
|
|
763
758
|
}
|
|
764
759
|
: {}),
|
|
765
|
-
})
|
|
760
|
+
})
|
|
766
761
|
: singleUserAutoAcceptBaseline
|
|
767
762
|
// 🔴 (live finding: single-user host-lane Bash ran UNGATED, no adjudication): a
|
|
768
763
|
// single-user turnkey worker with NO expressed gate intent previously left `toolPolicy` UNDEFINED →
|
|
769
764
|
// core's write-capable hand tools mount UNADJUDICATED (core error-logs it every task; host lane has NO
|
|
770
765
|
// sandbox to fall back on). CC's trust model is auto-accept, but the GATE MECHANISM must be PRESENT
|
|
771
766
|
// (core 原则: "机制留、默认可更宽 auto-accept"). So wire an adjudicated auto-accept BASELINE
|
|
772
|
-
// (`
|
|
773
|
-
// `hasEffectAwareGate`, restores observability + a hook/tighten point, and the
|
|
774
|
-
// irreversible ops via `AUTONOMY` / `commandPolicy` (layered TIGHTEN-ONLY by
|
|
767
|
+
// (the durable-less arm of `createApprovalBaselinePolicy` = a present, effect-aware policy that allows
|
|
768
|
+
// all): satisfies core's `hasEffectAwareGate`, restores observability + a hook/tighten point, and the
|
|
769
|
+
// operator STILL tightens irreversible ops via `AUTONOMY` / `commandPolicy` (layered TIGHTEN-ONLY by
|
|
770
|
+
// applyRuntimeGovernance).
|
|
775
771
|
// Interactive approval routed to the shell HITL = follow-on. 🔴 `singleUserAutoAcceptBaseline` requires
|
|
776
772
|
// ZERO gate intent — a single-user op who SET approval flags but wired no store falls to `undefined`
|
|
777
773
|
// (core warns = real misconfig, not masked); multi-tenant likewise stays `undefined` (approval required).
|
|
778
|
-
?
|
|
774
|
+
? createApprovalBaselinePolicy(config)
|
|
779
775
|
: undefined,
|
|
780
776
|
// Durable suspend needs both the store (here, per-task) and the opt-in scope key (multi-tenant =
|
|
781
777
|
// principal; "_" when auth is off). core suspends on a policy `ask` only when these are present.
|
|
@@ -829,11 +825,33 @@ export function createResolveSpec(ctx) {
|
|
|
829
825
|
// be an inconsistent false-safety: approval would still be live).
|
|
830
826
|
// #153([2713] 分单②):MANUAL_MODE_SHELL_GATE 在此无条件施加(与 autonomy 同拍)——部署旋钮不再
|
|
831
827
|
// 依赖客户端 settings 在场性/模式臂/host lane(三重条件曾让 print/headless 腿与沙箱 lane 全静默失效)。
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
828
|
+
//
|
|
829
|
+
// #177([2951] issue #29):SENSITIVE_WRITE_PATTERNS 是 #153 漏下的兄弟旋钮,同病同修——operator
|
|
830
|
+
// 守卫集预铸成纯 DENY policy 在 governance 拍无条件施加(bypassPermissions/键缺席/settings 整缺席
|
|
831
|
+
// 都掀不掉;deny-wins 折叠,普通目标弃权 ⇒ 不给 bypass 加 ask 门,[820] 表第四行逐字保持)。
|
|
832
|
+
// 裁决 env/cwd 在此**单点**铸,settings 折叠的 fsWriteGate wiring 复用同一份(两处各铸一份正是
|
|
833
|
+
// 漂移的成因)。lane 分形与 cwd 三形/哨兵的完整理由成文在下方 settings 块(修5/W4/#165),此处不复述。
|
|
834
|
+
const hostSemanticsLane = !isSandboxPathAdjudicationLane(config.remoteExec?.provider);
|
|
835
|
+
const pathAdjudication = hostSemanticsLane
|
|
836
|
+
? (() => {
|
|
837
|
+
const sessionCwd = auth?.sessionId ? effectiveHostWorkspace(perSessionCwd.get(auth.sessionId), {}) : undefined;
|
|
838
|
+
const gateCwd = sessionCwd ??
|
|
839
|
+
(config.remoteExec === undefined
|
|
840
|
+
? process.cwd()
|
|
841
|
+
: join(config.localDataRoot ?? localRoot, "fs-write-gate-unrooted")); // never created — see the settings block
|
|
842
|
+
return { env: new NodeExecutionEnv({ cwd: gateCwd }), cwd: gateCwd };
|
|
843
|
+
})()
|
|
844
|
+
: {
|
|
845
|
+
env: new DeferredSandboxPathEnv({
|
|
846
|
+
sessionId: auth?.sessionId,
|
|
847
|
+
slots: sandboxPathEnvSlots,
|
|
848
|
+
logger: { warn: (event, fields) => logger.warn(event, { ...(fields ?? {}), lane: config.remoteExec?.provider }) },
|
|
849
|
+
}),
|
|
850
|
+
};
|
|
851
|
+
// design/181 件一:governance 实参(含守卫集预铸)归 `createDeploymentGovernanceInputs`(三腿单一
|
|
852
|
+
// 属主),**折叠属主不变**——仍是这一句 `applyRuntimeGovernance` → core `tightenTaskSpec`。每请求现调,
|
|
853
|
+
// 读的是活 config(autonomy/commandPolicy 热改;resume 腿按当前值重折,与上方审批基线同姿势)。
|
|
854
|
+
const governedBase = applyRuntimeGovernance(spec, createDeploymentGovernanceInputs(config, pathAdjudication));
|
|
837
855
|
// Fold the client's per-request settings stamp onto the governed spec, TIGHTEN-ONLY (deny-wins) — AFTER
|
|
838
856
|
// the approval baseline + operator governance, so the order is deployment ⊇ operator ⊇ client (each can only
|
|
839
857
|
// narrow). tightenTaskSpec THROWS if the client settings would LOOSEN a stricter baseline → a 422 client error
|
|
@@ -866,8 +884,7 @@ export function createResolveSpec(ctx) {
|
|
|
866
884
|
// [820]③ scratchpadDir, hoisted from the envFacts block below so the SAME per-session dir feeds BOTH the
|
|
867
885
|
// `# Environment` fact and the fs-write gate's exemptDirs (single-source path via scratchpadDirFor inside
|
|
868
886
|
// ensureScratchpadDir). HOST-semantics lanes only + session required — rationale at the envFacts consumer.
|
|
869
|
-
//
|
|
870
|
-
const hostSemanticsLane = !isSandboxPathAdjudicationLane(config.remoteExec?.provider);
|
|
887
|
+
// (hostSemanticsLane 判别式已随 #177 上移到 governance 拍前——单一真源不变,只是搬了家。)
|
|
871
888
|
// G3([816]③/[820]③,[1840]§四):壳供 body.scratchpadDir 优先——壳(CC 形)有自己的 per-session
|
|
872
889
|
// scratchpad 约定路径,接受后事实/豁免/根围栏三处同源指向壳的目录(壳侧提示词与 server 写门不再
|
|
873
890
|
// 两个 scratchpad)。验收门 fail-closed(多租户/远程 lane/浅路径全拒收,warn 后回落自算)——
|
|
@@ -905,13 +922,11 @@ export function createResolveSpec(ctx) {
|
|
|
905
922
|
// (hand 工具的书面契约形)与 scratchpad exempt 均不受影响。
|
|
906
923
|
// · provider 未设(in-process host / run-local)⇒ hands 就在本进程 cwd 跑(无 factory),process.cwd()
|
|
907
924
|
// 正是真工作目录,保留。
|
|
908
|
-
//
|
|
925
|
+
// 两形**共有**的一件(与 env/cwd 无关,故不随 lane 分叉——单一属主,两臂各抄一份正是漂移的成因):
|
|
909
926
|
const gateCommon = {
|
|
910
|
-
// ③
|
|
911
|
-
//
|
|
912
|
-
//
|
|
913
|
-
// 沙箱 lane 上它按**词法** key 的路径段匹配(#156)——glob 本就只看段名,不需要真 fs。
|
|
914
|
-
...(config.sensitiveWritePatterns.length > 0 ? { sensitivePatterns: config.sensitiveWritePatterns } : {}),
|
|
927
|
+
// ③ 的 sensitive-path DENY 腿已随 #177([2951] issue #29)搬到 governance 拍(见上方
|
|
928
|
+
// sensitivePathPolicy)——wiring 不再承运守卫集,FsWriteGateWiring.sensitivePatterns 字段一并拆除
|
|
929
|
+
// (#153 shellGate 同形搬家:部署级旋钮不得挂在客户端表态派生腿上)。
|
|
915
930
|
// [1557]§四 的 shellGate 旧家已随 #153 搬到 applyRuntimeGovernance(governance 层无条件施加,
|
|
916
931
|
// 见上方 governedBase)——wiring 不再承运该旋钮,FsWriteGateWiring.shellGate 字段一并拆除。
|
|
917
932
|
// [841]① / core 1.294 exemption seam:gate 产 ask 前查同一只 approval_exemption 店(与 decide
|
|
@@ -932,19 +947,14 @@ export function createResolveSpec(ctx) {
|
|
|
932
947
|
: {}),
|
|
933
948
|
};
|
|
934
949
|
const fsWriteGate = hostSemanticsLane
|
|
935
|
-
?
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
cwd: gateCwd,
|
|
944
|
-
...(scratchpadDir ? { scratchpadDir } : {}),
|
|
945
|
-
...gateCommon,
|
|
946
|
-
};
|
|
947
|
-
})()
|
|
950
|
+
? {
|
|
951
|
+
// env/cwd 构造已随 #177 上移(governance 拍前的 pathAdjudication 单点),此处复用同一份——
|
|
952
|
+
// 守卫集 DENY 腿与写门 ask 腿在同一 env/同一 cwd 基准上裁决,两腿不可能分家漂移。
|
|
953
|
+
env: pathAdjudication.env,
|
|
954
|
+
...(pathAdjudication.cwd !== undefined ? { cwd: pathAdjudication.cwd } : {}),
|
|
955
|
+
...(scratchpadDir ? { scratchpadDir } : {}),
|
|
956
|
+
...gateCommon,
|
|
957
|
+
}
|
|
948
958
|
: // 沙箱 lane(#165):裁决 env = `DeferredSandboxPathEnv` 代理 —— 裁决时刻(必在 prepare 之后)
|
|
949
959
|
// 去 per-session slot 里取本 task 的**真沙箱 env**,转发 exists/canonicalPath/fileInfo/readLink。
|
|
950
960
|
// 于是绝对路径的 symlink 真身、存在性都是真沙箱 fs 的事实,域内软链指向守卫段照样 deny;
|
|
@@ -965,11 +975,7 @@ export function createResolveSpec(ctx) {
|
|
|
965
975
|
(() => {
|
|
966
976
|
const sandboxScratchpadDir = isRemoteScratchpadLane(config.remoteExec?.provider) ? remoteScratchpadDirFor(auth?.sessionId) : undefined;
|
|
967
977
|
return {
|
|
968
|
-
env:
|
|
969
|
-
sessionId: auth?.sessionId,
|
|
970
|
-
slots: sandboxPathEnvSlots,
|
|
971
|
-
logger: { warn: (event, fields) => logger.warn(event, { ...(fields ?? {}), lane: config.remoteExec?.provider }) },
|
|
972
|
-
}),
|
|
978
|
+
env: pathAdjudication.env, // #177 单点构造(DeferredSandboxPathEnv 代理),与守卫集 DENY 腿同一份
|
|
973
979
|
...gateCommon,
|
|
974
980
|
...(sandboxScratchpadDir ? { scratchpadDir: sandboxScratchpadDir } : {}),
|
|
975
981
|
// codex 复审 B(2026-08-05,dist 亲读+实测):core 的 canonicalizeTarget 把反斜杠 UNC 形在**入口**
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* (`runnerTierFrozen` 判据就取自构造那一刻的 `config.tiers`)。
|
|
10
10
|
* `getRunStore()` 是唯一的晚绑取值(runStore 在本段之后才构造),原文靠同作用域前向引用。
|
|
11
11
|
*/
|
|
12
|
-
import { type RunnerDeps, type WorkflowRunStore } from "@sema-agent/core";
|
|
12
|
+
import { type RunnerDeps, type WorkflowRunStore, type AskOutcome } from "@sema-agent/core";
|
|
13
13
|
import type { createBrain } from "../brain.js";
|
|
14
14
|
import type { buildPricing, createTracer } from "../budget.js";
|
|
15
15
|
import type { ServiceConfig } from "../config.js";
|
|
@@ -25,6 +25,28 @@ import type { QuestionCoordinator } from "../question.js";
|
|
|
25
25
|
import type { ToolApprovalCoordinator } from "../tool-approval.js";
|
|
26
26
|
import type { MemoryBackend } from "@sema-agent/core";
|
|
27
27
|
import type { MemorySyncRunner } from "../memory-sync-client.js";
|
|
28
|
+
/**
|
|
29
|
+
* `RunnerDeps.onAsk` 的装配(#168 件4:从内联闭包提为具名导出,**纯 testability 重构、零行为变化**)。
|
|
30
|
+
*
|
|
31
|
+
* 为什么值得有名字:bg / resume 两条腿**没有** per-task `spec.onAsk` 装配点(routes/runs.ts 只递 ALS 与
|
|
32
|
+
* 两个窗旋钮),它们唯一的审批接缝就是这一格。端到端测试此前只能把这一行**照抄**一份,再靠一条读源码
|
|
33
|
+
* 的正则当漂移告警 —— 那种锚只抓得到「这一行没了/改名了」,抓不到任何语义变化,而且抄件与真件从此是两
|
|
34
|
+
* 份实现。提成导出之后,测试直接取本函数,接缝与 production 是**同一个符号**。
|
|
35
|
+
*
|
|
36
|
+
* 命名(CLAUDE.md 工厂命名律):返回的是一个闭包(活对象)⇒ `create*`。
|
|
37
|
+
*
|
|
38
|
+
* 返回型**刻意窄于** `RunnerDeps["onAsk"]`(它还允许 `"allow"`/`"deny"` 两个字符串常量):本仓的接缝
|
|
39
|
+
* 恒是回调形,而 core 的 sync-first 判据只看 `onAsk !== undefined` ⇒ 字符串模式会顶掉 durable park
|
|
40
|
+
* (core backlog #73,黑板 [2897]④ / [2899] 已知悉)。窄返回型把「本仓零受迫于那一形」变成编译期事实。
|
|
41
|
+
*
|
|
42
|
+
* 语义逐字不变:[816]/[820]② 的 live tool-approval 接缝(core `resolveAsk` 读 `spec.onAsk ?? deps.onAsk`),
|
|
43
|
+
* ALS 路由同 `onQuestion` —— 被协调器 `runWithContext` 包住的腿才够得着人。[879] G1 终态(core 1.295
|
|
44
|
+
* OnAsk 三值化)下**恒**接线:回调逐 ask 时刻判活人,无附着/卡送达失败 ⇒ 返 `"unavailable"`,core 以
|
|
45
|
+
* `approverUnavailable` 回路把该 ask 交回 `suspendAsk` 走 durable park;无 park 设施的部署 core 自己
|
|
46
|
+
* fail-closed deny。协调器缺席(协议未上场)⇒ `undefined`,即「这条部署没有活人接缝」。
|
|
47
|
+
*/
|
|
48
|
+
export type RunnerDepsOnAsk = (req: Parameters<ToolApprovalCoordinator["ask"]>[0], signal?: AbortSignal) => Promise<AskOutcome>;
|
|
49
|
+
export declare function createRunnerDepsOnAsk(toolApproval: ToolApprovalCoordinator | undefined): RunnerDepsOnAsk | undefined;
|
|
28
50
|
export interface RunnerDepsCtx {
|
|
29
51
|
config: ServiceConfig;
|
|
30
52
|
logger: Logger;
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -20,6 +20,11 @@ import { resolveServedSession } from "../orchestration/workflow-completion-inbox
|
|
|
20
20
|
import { WorkflowNotifyGate } from "../orchestration/workflow-notify-journal.js";
|
|
21
21
|
import { makeLoadProjectMemory, makeProbeInstructionSources } from "../project-memory.js";
|
|
22
22
|
import { workflowModelAllowlistFor } from "../task-workflow.js";
|
|
23
|
+
export function createRunnerDepsOnAsk(toolApproval) {
|
|
24
|
+
if (!toolApproval)
|
|
25
|
+
return undefined;
|
|
26
|
+
return (req, signal) => toolApproval.ask(req, signal);
|
|
27
|
+
}
|
|
23
28
|
export function createSharedRunnerDeps(ctx) {
|
|
24
29
|
// 具名带类型的字面量(非裸 return)——deps-literal-shape-gate 的 POINTS 按 `const X: T = {` 咬装配
|
|
25
30
|
// 字面量,裸 return 形在它的覆盖外(复审 2026-07-30 F5):conditional-spread 病在这里就会失检。
|
|
@@ -215,17 +220,9 @@ export function createRunnerDeps(ctx) {
|
|
|
215
220
|
// (live-stream leg → this coordinator; no-live-context leg → durable park). The sentinel is stamped only
|
|
216
221
|
// when the coordinator is absent (ASK_QUESTION_ENABLED off).
|
|
217
222
|
onQuestion: question ? question.question : undefined,
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
// [879] G1 终态(core 1.295 OnAsk 三值化):恒 wire。回调逐 ask 时刻判活人——ALS 附着腿 ⇒ 同步三选卡;
|
|
222
|
-
// 无附着/卡送达失败 ⇒ 返 "unavailable",core 以 approverUnavailable 回路把该 ask 交回 suspendAsk 走
|
|
223
|
-
// durable park(prepare-task 分路带 `approverUnavailable !== true` 豁免位,dist 亲读)——park 与 live 卡
|
|
224
|
-
// 两全,1.199 的「durable 部署不 wire deps.onAsk」止血撤除。无 park 设施的部署 core 自己 fail-closed
|
|
225
|
-
// deny(resolveAsk 的 unavailable 文案),与旧姿势同向。
|
|
226
|
-
onAsk: toolApproval
|
|
227
|
-
? (req, signal) => toolApproval.ask(req, signal)
|
|
228
|
-
: undefined,
|
|
223
|
+
// live tool-approval 接缝 —— 语义与历史全部搬进 `createRunnerDepsOnAsk` 的头注(#168 件4:同一份
|
|
224
|
+
// 说明只留一处,不在装配点复述)。
|
|
225
|
+
onAsk: createRunnerDepsOnAsk(toolApproval),
|
|
229
226
|
// [822]② auto permission mode, deployment half: RunnerDeps.autoMode is the operator TRUST face of core's
|
|
230
227
|
// auto-mode classifier (1.276/1.277) — wiring it alone arms NOTHING (core requires the per-principal entitlement
|
|
231
228
|
// runtimeCaps.autoMode === true from center, fail-closed dark by default; see runtime-caps-resolver.ts). With
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* → `workflowRunStore`(包装体)的顺序是数据依赖链,不可重排。
|
|
12
12
|
* 3. `getRunStore()` 是晚绑取值(runStore 在本段之后构造),原文靠同作用域前向引用。
|
|
13
13
|
*/
|
|
14
|
-
import { type WorkflowRunStore } from "@sema-agent/core";
|
|
14
|
+
import { type WorkflowRun, type WorkflowRunStore } from "@sema-agent/core";
|
|
15
15
|
import type { ServiceConfig } from "../config.js";
|
|
16
16
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
17
17
|
import type { Logger } from "../observability/logger.js";
|
|
18
18
|
import type { Metrics } from "../observability/metrics.js";
|
|
19
19
|
import { type WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js";
|
|
20
|
-
import { WorkflowNotifyGate, type WorkflowCompletionPayload } from "../orchestration/workflow-notify-journal.js";
|
|
20
|
+
import { JournalingWorkflowRunStore, WorkflowNotifyGate, type WorkflowCompletionPayload } from "../orchestration/workflow-notify-journal.js";
|
|
21
21
|
import { SubagentSteerRegistry } from "../orchestration/subagent-steer.js";
|
|
22
22
|
import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
|
|
23
23
|
import type { StoreBackend } from "../plugins/store-backend.js";
|
|
@@ -37,10 +37,15 @@ export declare function createWorkflowOrchestration(ctx: WorkflowOrchestrationCt
|
|
|
37
37
|
deliverWorkflowCompletion: (p: WorkflowCompletionPayload) => Promise<void>;
|
|
38
38
|
workflowNotifyGate: WorkflowNotifyGate | undefined;
|
|
39
39
|
fleetBus: FleetEventBus;
|
|
40
|
-
workflowRunStore:
|
|
40
|
+
workflowRunStore: JournalingWorkflowRunStore | undefined;
|
|
41
41
|
workflowJournalStore: import("../plugins/store-backend.js").ServiceWorkflowJournalStore | undefined;
|
|
42
42
|
outcomeSink: import("../plugins/file-outcome-sink.js").OutcomeSink | undefined;
|
|
43
43
|
workflowAgentRegistry: WorkflowAgentRegistry | undefined;
|
|
44
44
|
subagentSteerRegistry: SubagentSteerRegistry;
|
|
45
|
+
workflowRecoverOpts: {
|
|
46
|
+
orphanGraceMs?: number;
|
|
47
|
+
finalizeStartedBeforeMs?: number;
|
|
48
|
+
publishTerminalFleetRow?: (id: string, run: WorkflowRun) => void;
|
|
49
|
+
};
|
|
45
50
|
};
|
|
46
51
|
//# sourceMappingURL=workflow-orchestration.d.ts.map
|
|
@@ -20,6 +20,11 @@ import { SubagentSteerRegistry } from "../orchestration/subagent-steer.js";
|
|
|
20
20
|
import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
|
|
21
21
|
export function createWorkflowOrchestration(ctx) {
|
|
22
22
|
const { config, logger, metrics, localRoot, backend, getRunStore } = ctx;
|
|
23
|
+
// [2995] boot-orphan 判死界:此刻之前记入 journal 的 pending 条目属于**前世进程**(workflow 执行器是
|
|
24
|
+
// in-process 的,随前世死)。取在 journal/gate 构造之前 ⇒ 本进程随后 record 的每条 createdAt 常态下都严格
|
|
25
|
+
// 晚于它。⚠️ 它是**必要不充分**条件:时钟回拨会让本世条目落到界下,判死的最终锚是 gate 内的
|
|
26
|
+
// incarnation 名册(见 WorkflowNotifyGate.recordedThisIncarnation),两者取交集。
|
|
27
|
+
const workflowBootAtMs = Date.now();
|
|
23
28
|
// S8 / SVC-1 workflow run store (design/97 S1b): records LLM-authored BACKGROUND workflow
|
|
24
29
|
// runs for the /v1/workflows list+detail view (live subscribe is in-process via subscribeWorkflow,
|
|
25
30
|
// store-independent). Only present when self-orchestration is enabled (else no runs to record).
|
|
@@ -120,6 +125,23 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
120
125
|
const workflowRunStore = baseWorkflowRunStore
|
|
121
126
|
? new JournalingWorkflowRunStore(baseWorkflowRunStore, workflowNotifyGate, fleetBus)
|
|
122
127
|
: undefined;
|
|
128
|
+
// [2995]/[2999] recover 两半的**单点合成**(boot 扫描与周期扫描共用同一对象,防两处漂移——#177 同族教训):
|
|
129
|
+
// · finalizeStartedBeforeMs = 诚实性半场,**只在 replica-local(File)journal 形**:SQL journal 是跨副本
|
|
130
|
+
// 的(上面 1.108 注),pending running run 可能活在别的 replica,那里年龄(grace)是唯一健全的孤儿代理。
|
|
131
|
+
// (进程内是否本世记录由 gate 自己的 incarnation 锚判,时钟回拨不参与——见 recover 的键注。)
|
|
132
|
+
// · publishTerminalFleetRow = 判死臂的**撤行**口:sweep 翻完 durable 行后经 wrapper 的同一投影发终帧+移除,
|
|
133
|
+
// store/面板/notify 三面一个答案。⚠️ 复审第 1 轮两条 high 的收口:sweep **绝不**发 running 行(读时快照
|
|
134
|
+
// 铸出来的行可能永远无人可撤——跨副本形属主在别的副本、同副本形 live 终态可能已把行撤了)。fleet 活行的
|
|
135
|
+
// 唯一写者是本副本 wrapper 的 put/update 观察点。
|
|
136
|
+
// 授权条件直接问「这本 journal 是不是 replica-local」,**不拿 sqlWorkflowRunStore 当代理**:两处各写一遍
|
|
137
|
+
// 必漂移,而漂移方向是把判死授权交给共享 journal = 杀别副本正在跑的 run。未知/新 journal 实现 ⇒ 恒 false
|
|
138
|
+
// (fail-closed:最坏只是孤儿多等一个 grace 窗)。
|
|
139
|
+
const replicaLocalNotifyJournal = workflowNotifyJournal instanceof FileWorkflowNotifyJournalStore;
|
|
140
|
+
const workflowRecoverOpts = {
|
|
141
|
+
orphanGraceMs: config.workflowOrphanGraceMs,
|
|
142
|
+
...(replicaLocalNotifyJournal ? { finalizeStartedBeforeMs: workflowBootAtMs } : {}),
|
|
143
|
+
...(workflowRunStore ? { publishTerminalFleetRow: (id, run) => workflowRunStore.republishFleet(id, run) } : {}),
|
|
144
|
+
};
|
|
123
145
|
// SVC-2 (core CORE-7/CORE-9 Part A): the durable resume journal. core 1.145.0 added `RunnerDeps.workflowJournalStore`
|
|
124
146
|
// (twin of workflowRunStore) + auto-wires it into the run_workflow tool's startWorkflow(RunWorkflowOptions.journalStore);
|
|
125
147
|
// the tool's `resumeFromRunId` input then replays the longest unchanged prefix. tidb/pg = durable cross-replica resume;
|
|
@@ -144,7 +166,7 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
144
166
|
return {
|
|
145
167
|
sqlWorkflowRunStore, workflowNotifyJournal, workflowCompletionInbox, deliverWorkflowCompletion,
|
|
146
168
|
workflowNotifyGate, fleetBus, workflowRunStore, workflowJournalStore, outcomeSink,
|
|
147
|
-
workflowAgentRegistry, subagentSteerRegistry,
|
|
169
|
+
workflowAgentRegistry, subagentSteerRegistry, workflowRecoverOpts,
|
|
148
170
|
};
|
|
149
171
|
}
|
|
150
172
|
//# sourceMappingURL=workflow-orchestration.js.map
|
package/dist/budget.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cost / budget / trace wiring for the Runner (core
|
|
2
|
+
* Cost / budget / trace wiring for the Runner (core 5.x) — pure, testable helpers kept out of the
|
|
3
3
|
* auto-running main.ts:
|
|
4
4
|
* ③ buildPricing — Model.cost catalog → injected ModelPricing (authoritative costMicroUsd).
|
|
5
5
|
* ④ createTracer — core's content-free TraceEvents → /metrics (spend, first-token, tool calls).
|
package/dist/budget.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cost / budget / trace wiring for the Runner (core
|
|
2
|
+
* Cost / budget / trace wiring for the Runner (core 5.x) — pure, testable helpers kept out of the
|
|
3
3
|
* auto-running main.ts:
|
|
4
4
|
* ③ buildPricing — Model.cost catalog → injected ModelPricing (authoritative costMicroUsd).
|
|
5
5
|
* ④ createTracer — core's content-free TraceEvents → /metrics (spend, first-token, tool calls).
|
|
@@ -11,7 +11,7 @@ export interface RepoCoords {
|
|
|
11
11
|
owner: string;
|
|
12
12
|
repo: string;
|
|
13
13
|
}
|
|
14
|
-
/** Parse `owner/name` or a repo URL (e.g.
|
|
14
|
+
/** Parse `owner/name` or a repo URL (e.g. https://git.example.com/some-org/some-repo.git). */
|
|
15
15
|
export declare function parseRepo(input: string): RepoCoords;
|
|
16
16
|
/** Thin read-only Gitea API client. Built once at startup; bound to a repo per task. */
|
|
17
17
|
export declare class GiteaClient {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
-
/** Parse `owner/name` or a repo URL (e.g.
|
|
2
|
+
/** Parse `owner/name` or a repo URL (e.g. https://git.example.com/some-org/some-repo.git). */
|
|
3
3
|
export function parseRepo(input) {
|
|
4
4
|
const s = input.trim().replace(/\.git$/, "");
|
|
5
5
|
const m = /^[a-z]+:\/\/[^/]+\/(.+)$/i.exec(s);
|
|
@@ -42,7 +42,13 @@ export class GiteaClient {
|
|
|
42
42
|
async tree(coords, ref) {
|
|
43
43
|
const { owner, repo } = coords;
|
|
44
44
|
const body = (await (await this.api(`/repos/${owner}/${repo}/git/trees/${encodeURIComponent(ref)}?recursive=true&per_page=${MAX_TREE_ENTRIES}`)).json());
|
|
45
|
-
|
|
45
|
+
const paths = (body.tree ?? []).filter((e) => e.type === "blob").map((e) => e.path);
|
|
46
|
+
// The upstream API caps the listing at `per_page` and reports the overflow via `truncated` — it does
|
|
47
|
+
// not report how many entries were left out. Surface that (same disclosure-marker family as
|
|
48
|
+
// `readFile`/`pullDiff` below) so a review lens never mistakes a capped tree for the whole repo.
|
|
49
|
+
return body.truncated
|
|
50
|
+
? [...paths, `…[truncated, tree has more than ${MAX_TREE_ENTRIES} entries]`]
|
|
51
|
+
: paths;
|
|
46
52
|
}
|
|
47
53
|
async readFile(coords, path, ref) {
|
|
48
54
|
const { owner, repo } = coords;
|
|
@@ -434,20 +434,43 @@ export function runtimeGatePresent(rt, key) {
|
|
|
434
434
|
*
|
|
435
435
|
* 顺带 C1-2:数组项 trim + 去空串,与 env csv 归一(`csv()` 的 `.map(trim).filter(Boolean)`)一致——
|
|
436
436
|
* 否则 `" Bash "` 从中心腿进来会是个永不匹配的名字,正是本条要消灭的那类静默失效。
|
|
437
|
+
*
|
|
438
|
+
* ⚠️ codex R1(#186 同批复审):**形错必须拒域,不能折成 `[]`**。旧实现先按 `typeof === "string"` 过滤再验
|
|
439
|
+
* 判据,于是 `[123]`(非串项)与 `"Bash"`(整个不是数组)双双归约成空名单、判据自然干净 ⇒ 被当作「运维
|
|
440
|
+
* 显式把闸关掉」落配,把前值换成空门且**零告警**。方向与 A1 相反(A1 是门空转,这条是门蒸发),但同属本域
|
|
441
|
+
* 要堵的失败,且在安全轴上是静默 fail-open。判据分家:**空串/纯空白仍丢弃**(C1-2 的 env csv 归一,env 腿
|
|
442
|
+
* 产不出非串项),**非串项 / 非数组=形错 ⇒ 拒整域保前值**;显式 `[]` 依旧是合法的「闸关掉」。
|
|
437
443
|
*/
|
|
438
444
|
function stageApprovalRequire(raw, logger) {
|
|
439
|
-
//
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
445
|
+
// `kept` 逐臂说真话:内容臂的后果是「门空转」,形错臂的后果是「门蒸发」——两句不能混用。
|
|
446
|
+
const refuse = (detail, names, kept) => {
|
|
447
|
+
logger?.warn("sema_registry_approval_require_invalid", { detail, names: [...names], kept });
|
|
448
|
+
return {};
|
|
449
|
+
};
|
|
450
|
+
const KEPT_WOULD_MATCH_NOTHING = "the prior approvalRequire is unchanged — the published list would have silently matched nothing";
|
|
451
|
+
const KEPT_WOULD_REMOVE_GATE = "the prior approvalRequire is unchanged — coercing this malformed value would have silently REMOVED the approval gate";
|
|
452
|
+
// 形错门(codex R1),先于内容判据:此处**不能**静默归一,归一的结果恰好是「空门」。
|
|
453
|
+
// 非数组一支是**纵深防御**:唯一调用点先过 `runtimeGatePresent`(判 `Array.isArray`),非数组在那里就按
|
|
454
|
+
// 「缺席」跳过、保前值——已是 fail-safe。本函数签名收 `unknown`,所以自己也把这一形收口,免得 present
|
|
455
|
+
// 判据日后放宽时非数组悄悄掉进下面的归一腿(测试两支都钉了)。
|
|
456
|
+
if (!Array.isArray(raw)) {
|
|
457
|
+
return refuse(`published runtime.approvalRequire must be an ARRAY of whole tool names — got ${raw === null ? "null" : typeof raw}. ` +
|
|
458
|
+
`Refusing the domain instead of coercing it: an empty list means "no approval gate", so coercing a malformed value would SILENTLY remove the gate.`, [], KEPT_WOULD_REMOVE_GATE);
|
|
459
|
+
}
|
|
460
|
+
// 逐项报**下标 + 实际类型**(运维要能在发布件里定位到那一项;只报个数无法行动)。
|
|
461
|
+
const nonString = raw
|
|
462
|
+
.map((n, i) => (typeof n === "string" ? undefined : { i, t: n === null ? "null" : typeof n }))
|
|
463
|
+
.filter((e) => e !== undefined);
|
|
464
|
+
if (nonString.length > 0) {
|
|
465
|
+
return refuse(`published runtime.approvalRequire contains ${nonString.length} non-string entr${nonString.length === 1 ? "y" : "ies"} ` +
|
|
466
|
+
`(${nonString.map((e) => `[${e.i}]:${e.t}`).join(", ")}) — every entry must be a whole tool name string. ` +
|
|
467
|
+
`Refusing the domain instead of dropping them: dropping every entry would SILENTLY remove the approval gate.`, nonString.map((e) => `[${e.i}]`), KEPT_WOULD_REMOVE_GATE);
|
|
468
|
+
}
|
|
469
|
+
// wire verbatim ⇒ 空串/纯空白按 env csv 归一丢弃(C1-2);元素形状已在上面的形错门收口。
|
|
470
|
+
const names = raw.filter((n) => typeof n === "string").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
443
471
|
const bad = findUnmatchableToolNames(names);
|
|
444
472
|
if (bad.length > 0) {
|
|
445
|
-
|
|
446
|
-
detail: formatUnmatchableToolNames("published runtime.approvalRequire", bad),
|
|
447
|
-
names: bad.map((b) => b.name),
|
|
448
|
-
kept: "the prior approvalRequire is unchanged — the published list would have silently matched nothing",
|
|
449
|
-
});
|
|
450
|
-
return {};
|
|
473
|
+
return refuse(formatUnmatchableToolNames("published runtime.approvalRequire", bad), bad.map((b) => b.name), KEPT_WOULD_MATCH_NOTHING);
|
|
451
474
|
}
|
|
452
475
|
return { value: names };
|
|
453
476
|
}
|
|
@@ -98,6 +98,7 @@ export declare class RemoteConfigProvider implements ConfigProvider {
|
|
|
98
98
|
* version as the etag (so the caller's "skip re-apply if etag unchanged" works locally too).
|
|
99
99
|
*/
|
|
100
100
|
export declare class LocalConfigProvider implements ConfigProvider {
|
|
101
|
+
#private;
|
|
101
102
|
readonly kind: "local";
|
|
102
103
|
private readonly store;
|
|
103
104
|
/** [875]b 可观测性:config_loaded 日志要能回答「到底读的哪个目录」——复验实录:只设 LOCAL_DATA_ROOT 时
|
package/dist/config-provider.js
CHANGED
|
@@ -131,8 +131,12 @@ export class LocalConfigProvider {
|
|
|
131
131
|
/** [875]b 可观测性:config_loaded 日志要能回答「到底读的哪个目录」——复验实录:只设 LOCAL_DATA_ROOT 时
|
|
132
132
|
* config.d 静默读不到且 0 模型无告警(CONFIG_LOCAL_DIR 才是本 lane 的选目录旋钮)。 */
|
|
133
133
|
root;
|
|
134
|
+
/** 注入 store(测试缝)时保持其自带行为;自建时 fetchEffective 每次按调用铸新 store 挂 warning 座
|
|
135
|
+
* (见 fetchEffective 头注)。 */
|
|
136
|
+
#injectedStore;
|
|
134
137
|
constructor(root, store) {
|
|
135
138
|
this.root = root;
|
|
139
|
+
this.#injectedStore = store;
|
|
136
140
|
this.store = store ?? new FileConfigStore(root);
|
|
137
141
|
}
|
|
138
142
|
async fetchEffective(etag) {
|
|
@@ -146,7 +150,20 @@ export class LocalConfigProvider {
|
|
|
146
150
|
// [898] tolerant read(0.10.12):catalog 域坏文件 ⇒ 该域 default+domainErrors 单列(不再连坐整包回落
|
|
147
151
|
// env);gate 域坏文件仍 throw(走 caller 的整包 catch)。坏文件文本 hash 参与 version(坏≠absent、
|
|
148
152
|
// 坏→修好均 bump),304-skip 纪律不漏拍——所以坏域场景下 etag 对比照旧安全。
|
|
149
|
-
|
|
153
|
+
// [3073]#204④:判形失败(schema 坏形/重名条目)走的是 buildEffective 的 `onWarning`(parse 层落
|
|
154
|
+
// default),不走 READ 层 throw——不接这个座,domainErrors 恒空,域被静默清空而排障 hint 只能谎报
|
|
155
|
+
// 「文件空」。warning 座是 store 构造级而 fetch 可能重叠(boot 超窗后台腿 vs 首拍 refresh),
|
|
156
|
+
// 共享缓冲会把别次快照的警告错记到本次结果(codex F2)——故自建形每次 fetch 铸新 store 配本地
|
|
157
|
+
// 收集器(FileConfigStore 无跨调用状态,按次构造零成本);注入 store(测试缝)保持其自带行为。
|
|
158
|
+
const parseWarnings = [];
|
|
159
|
+
const store = this.#injectedStore ??
|
|
160
|
+
new FileConfigStore(this.root, {
|
|
161
|
+
onWarning: (w) => {
|
|
162
|
+
if (w.kind === "domain-defaulted")
|
|
163
|
+
parseWarnings.push({ domain: w.domain, error: w.error instanceof Error ? w.error.message : String(w.error) });
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
const { effective: eff, domainErrors } = await store.getEffective({ tolerant: true });
|
|
150
167
|
const tag = String(eff.version);
|
|
151
168
|
if (etag !== undefined && etag === tag)
|
|
152
169
|
return null; // unchanged → mirror remote 304
|
|
@@ -157,14 +174,17 @@ export class LocalConfigProvider {
|
|
|
157
174
|
// The resolver gets THIS snapshot's domain values as `pending` (builder output is default-filled for
|
|
158
175
|
// every domain), so the check never re-reads config.d — the one-atomic-snapshot invariant above holds.
|
|
159
176
|
const refErrors = [];
|
|
160
|
-
const resolve = siblingResolver(
|
|
177
|
+
const resolve = siblingResolver(store, eff);
|
|
161
178
|
for (const d of ["rosters", "workers", "models", "collab", "systems", "entitlement"]) {
|
|
162
179
|
const issues = await refIntegrityIssues(d, eff[d], resolve);
|
|
163
180
|
// path CLEAR (locates the entry) + message operand-fingerprinted (see redactConfigError).
|
|
164
181
|
if (issues.length > 0)
|
|
165
182
|
refErrors.push({ domain: d, error: issues.map((i) => `${i.path.join(".")}: ${redactConfigError(i.message)}`).join("; ") });
|
|
166
183
|
}
|
|
167
|
-
|
|
184
|
+
// parse 层判形失败与 READ 层坏文件同渠道:两者都是「坏域落 default」,候选门(§9.5-5)与排障 hint
|
|
185
|
+
// 需要同样看见——此前判形失败静默(domainErrors 空+域被清空),比坏文件更隐蔽。
|
|
186
|
+
const parseErrors = parseWarnings.map((w) => ({ domain: w.domain, error: redactConfigError(w.error) }));
|
|
187
|
+
const errors = [...domainErrors.map((de) => ({ domain: de.domain, error: redactConfigError(de.error) })), ...parseErrors, ...refErrors];
|
|
168
188
|
return { effective: mapToServiceEffective(eff, eff.version), etag: tag, ...(errors.length > 0 ? { domainErrors: errors } : {}) };
|
|
169
189
|
}
|
|
170
190
|
async fetchSkillContent(contentHash) {
|