@sema-agent/server 7.109.0 → 7.111.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.
Files changed (107) hide show
  1. package/MIGRATION.md +25 -0
  2. package/README.md +8 -0
  3. package/README.zh-CN.md +5 -0
  4. package/USAGE.md +2 -2
  5. package/dist/approval-ask-audit-store.d.ts +38 -25
  6. package/dist/approval-ask-audit-store.js +26 -59
  7. package/dist/approval-card.d.ts +6 -5
  8. package/dist/approval-card.js +8 -11
  9. package/dist/bench/s1/run-firm.d.ts +11 -0
  10. package/dist/bench/s1/run-firm.js +47 -32
  11. package/dist/boot/coordinators.js +1 -1
  12. package/dist/boot/reapers.d.ts +3 -0
  13. package/dist/boot/reapers.js +3 -2
  14. package/dist/boot/resolve-spec.js +3 -4
  15. package/dist/boot/session-faces.d.ts +4 -0
  16. package/dist/boot/session-faces.js +3 -1
  17. package/dist/boot/stage-07-capability-layer.d.ts +2 -0
  18. package/dist/boot/stage-07-capability-layer.js +18 -3
  19. package/dist/boot/stage-08-reapers.d.ts +2 -0
  20. package/dist/boot/stage-08-reapers.js +2 -2
  21. package/dist/boot/stage-09-leader.d.ts +2 -0
  22. package/dist/boot/stage-09-leader.js +2 -1
  23. package/dist/boot/stage-10-http-server.d.ts +2 -0
  24. package/dist/boot/stage-10-http-server.js +2 -1
  25. package/dist/boot/workflow-orchestration.js +1 -1
  26. package/dist/config-catalog.js +1 -1
  27. package/dist/config-types.d.ts +3 -2
  28. package/dist/config.js +19 -7
  29. package/dist/deployment-governance.d.ts +8 -2
  30. package/dist/deployment-governance.js +9 -8
  31. package/dist/hooks/hook-runner.d.ts +2 -2
  32. package/dist/hooks/hook-runner.js +40 -26
  33. package/dist/http/durable-run-leg.js +1 -1
  34. package/dist/http/resume-legs.js +12 -4
  35. package/dist/http/routes/runs.d.ts +0 -1
  36. package/dist/http/routes/runs.js +36 -1
  37. package/dist/http/routes/sessions.js +12 -2
  38. package/dist/http/routes/tasks.js +13 -7
  39. package/dist/http/server.d.ts +5 -0
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +1 -0
  42. package/dist/instruction-selection.d.ts +34 -0
  43. package/dist/instruction-selection.js +23 -0
  44. package/dist/instruction-source-notice.d.ts +3 -3
  45. package/dist/memory-layer-legacy-lock.d.ts +3 -3
  46. package/dist/model-entry-headers.d.ts +5 -2
  47. package/dist/model-entry-headers.js +4 -4
  48. package/dist/model-unreachable-notice.d.ts +2 -2
  49. package/dist/observability/corrupt-read-seat.d.ts +24 -0
  50. package/dist/observability/corrupt-read-seat.js +24 -0
  51. package/dist/observability/fail-open.d.ts +25 -1
  52. package/dist/observability/fail-open.js +25 -1
  53. package/dist/observability/metrics.js +2 -1
  54. package/dist/observability/run-terminal.js +10 -0
  55. package/dist/operator-ask.d.ts +9 -8
  56. package/dist/operator-ask.js +9 -1
  57. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -23
  58. package/dist/orchestration/workflow-agent-session-index.js +11 -50
  59. package/dist/orchestration/workflow-notify-journal.d.ts +11 -8
  60. package/dist/orchestration/workflow-notify-journal.js +20 -36
  61. package/dist/plugins/file-outcome-sink.d.ts +4 -1
  62. package/dist/plugins/file-outcome-sink.js +9 -3
  63. package/dist/plugins/file-run-store.js +4 -4
  64. package/dist/plugins/memory-run-store.d.ts +1 -1
  65. package/dist/plugins/memory-run-store.js +2 -1
  66. package/dist/plugins/pg-pool.js +15 -0
  67. package/dist/plugins/retention-store-sql.d.ts +1 -1
  68. package/dist/plugins/retention-store-sql.js +2 -0
  69. package/dist/plugins/run-store-sql.d.ts +1 -1
  70. package/dist/plugins/run-store-sql.js +3 -2
  71. package/dist/plugins/schema-indexes.js +2 -0
  72. package/dist/plugins/store-backend.d.ts +4 -0
  73. package/dist/plugins/store-backend.js +9 -0
  74. package/dist/plugins/store-contracts.d.ts +24 -0
  75. package/dist/plugins/store-contracts.js +10 -0
  76. package/dist/plugins/tidb-pool.js +17 -0
  77. package/dist/plugins/user-input-ledger-file.d.ts +35 -0
  78. package/dist/plugins/user-input-ledger-file.js +108 -0
  79. package/dist/plugins/user-input-ledger-sql.d.ts +54 -0
  80. package/dist/plugins/user-input-ledger-sql.js +129 -0
  81. package/dist/project-memory.d.ts +10 -2
  82. package/dist/project-memory.js +5 -2
  83. package/dist/run-local.d.ts +18 -5
  84. package/dist/run-local.js +24 -9
  85. package/dist/runs.d.ts +8 -1
  86. package/dist/runs.js +11 -5
  87. package/dist/runtime-governance.d.ts +20 -8
  88. package/dist/runtime-governance.js +7 -12
  89. package/dist/session-records-skipped-notice.d.ts +4 -3
  90. package/dist/task-settings.d.ts +0 -6
  91. package/dist/task-settings.js +0 -1
  92. package/dist/tool-approval.d.ts +8 -1
  93. package/dist/tool-approval.js +4 -1
  94. package/dist/trace/core-keyset-guard.d.ts +16 -1
  95. package/dist/trace/engine-notice-wire.d.ts +30 -35
  96. package/dist/trace/engine-notice-wire.js +23 -23
  97. package/dist/trace/ledger-sink.d.ts +4 -0
  98. package/dist/trace/ledger-sink.js +1 -1
  99. package/dist/trace/project.d.ts +11 -3
  100. package/dist/trace/project.js +6 -5
  101. package/dist/trace/self-minted-wire.d.ts +40 -0
  102. package/dist/trace/self-minted-wire.js +15 -0
  103. package/dist/trace/wire-projection-faces.d.ts +3 -3
  104. package/dist/trace/wire-projection-faces.js +4 -2
  105. package/dist/user-input-ledger.d.ts +349 -0
  106. package/dist/user-input-ledger.js +354 -0
  107. package/package.json +3 -3
@@ -34,7 +34,7 @@ import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, norma
34
34
  import { cwdHonored, deviceCwdHonored, effectiveHostWorkspace, inProcessSingleUserLane, isValidCwd, parseAdditionalDirectories, satisfiedByProcessCwd, shellEnvMismatchCount } from "../task-cwd.js";
35
35
  import { assertRequestA2aUnlocked, resolveRequestA2a } from "../task-a2a.js";
36
36
  import { assertRequestMcpContentOrigin, assertRequestMcpUnlocked, resolveRequestMcp } from "../task-mcp.js";
37
- import { ENGINE_ABSENT_PERMISSION_MODE, MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSettings, effectivePermissionMode, effectiveThinking, hasConstitutionAnchors, parseTaskSettings, providerDropsAppend, settingsPermissionBucketIssue, withPermissionMode } from "../task-settings.js";
37
+ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSettings, effectivePermissionMode, effectiveThinking, hasConstitutionAnchors, parseTaskSettings, providerDropsAppend, settingsPermissionBucketIssue, withPermissionMode } from "../task-settings.js";
38
38
  import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
39
39
  import { redactSecrets } from "../trace/redact.js";
40
40
  import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
@@ -109,7 +109,7 @@ export function createResolveSpec(ctx) {
109
109
  if (why !== undefined)
110
110
  throw new HttpError(422, why, { code: HOST_POSTURE_REFUSED_CODE });
111
111
  }
112
- const lane = bindSettingsCwdEnvAndModel(body, auth, gated, effMode, opts);
112
+ const lane = bindSettingsCwdEnvAndModel(body, auth, gated, opts);
113
113
  if (auth?.sessionId && assemblyIncarnationRead)
114
114
  noteSessionIncarnation(auth.sessionId, await assemblyIncarnationRead(auth.sessionId));
115
115
  const anchors = await resolveHistoryAnchors(body, auth);
@@ -178,7 +178,7 @@ export function createResolveSpec(ctx) {
178
178
  }
179
179
  return { scenarioName, cap, hands: cap.hands, centerDecls, appendLessPack, acceptedAppend, objective, parsedSettings, attachmentNotice };
180
180
  };
181
- const bindSettingsCwdEnvAndModel = (body, auth, gated, effMode, opts) => {
181
+ const bindSettingsCwdEnvAndModel = (body, auth, gated, opts) => {
182
182
  const { objective, parsedSettings } = gated;
183
183
  const cwdWriteGate = cwdHonored(config) || deviceCwdHonored(config);
184
184
  const execLane = executionLaneOf(config.remoteExec?.provider);
@@ -218,7 +218,6 @@ export function createResolveSpec(ctx) {
218
218
  logger,
219
219
  sessionId: auth?.sessionId ?? "",
220
220
  cwd: (auth?.sessionId ? perSessionCwd.get(auth.sessionId) : undefined) ?? process.cwd(),
221
- permissionMode: effMode ?? ENGINE_ABSENT_PERMISSION_MODE,
222
221
  ...(parsedSettings.settings?.shellEnv ? { shellEnv: parsedSettings.settings.shellEnv } : {}),
223
222
  hookLlm,
224
223
  hookAgent,
@@ -12,6 +12,7 @@ import { SessionWatchRegistry } from "../session-watch.js";
12
12
  import type { TaskListLane } from "./task-list-lane.js";
13
13
  import { type RunnerDeps } from "@sema-agent/core";
14
14
  import type { MailboxRecipientLifecycle } from "../plugins/mailbox-recipient-lifecycle.js";
15
+ import type { UserInputLedgerStore } from "../user-input-ledger.js";
15
16
  export interface SessionFacesCtx {
16
17
  config: ServiceConfig;
17
18
  logger: Logger;
@@ -43,6 +44,9 @@ export interface SessionFacesCtx {
43
44
  /** S-790 件B:后台子代的耐久店(与 `RunnerDeps.backgroundAgentStore` 同实例)—— 删除协调器据它枚举以本会话为根的子代并结束
44
45
  * 它们(E21 级联的一条腿,做完才删元数据)。缺席 = 本部署没有耐久后台子代(纯内存 dev);那一形只剩本进程登记表里的活行,照样停。 */
45
46
  backgroundAgentStore: RunnerDeps["backgroundAgentStore"];
47
+ /** S-965:用户输入受理账本的**店**(E21 级联的一条腿:该会话的受理行随会话删)。与受理 / 收敛 / 告知读的是同一只(stage-07 取一次)。
48
+ * 缺席 = 本部署没装账本(env-only:没有 backend / run 店)⇒ 无行可删。 */
49
+ userInputLedgerStore: UserInputLedgerStore | undefined;
46
50
  }
47
51
  export declare function createSessionFaces(ctx: SessionFacesCtx): {
48
52
  ownerAware: OwnerAwareSessionStore;
@@ -8,7 +8,7 @@ import { defaultTaskRegistry, peerSessionBoxHandle } from "@sema-agent/core";
8
8
  import { reapSessionBackgroundHard } from "../background-session-reap.js";
9
9
  import { probeSessionChildren, retireSessionChildren, SessionChildrenStopIncompleteError } from "../session-children-retire.js";
10
10
  export function createSessionFaces(ctx) {
11
- const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, } = ctx;
11
+ const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, userInputLedgerStore, } = ctx;
12
12
  const mysqlPool = backend?.mysqlPool();
13
13
  const auditPgPool = backend?.pgPool();
14
14
  const auditLocalRoot = backend?.kind === "local" ? (config.localDataRoot ?? localRoot) : undefined;
@@ -78,6 +78,8 @@ export function createSessionFaces(ctx) {
78
78
  await sessionPolicyStore.deleteBySession(sessionId);
79
79
  if (taskAttachmentStore)
80
80
  await taskAttachmentStore.deleteBySession(sessionId);
81
+ if (userInputLedgerStore)
82
+ await userInputLedgerStore.deleteBySession(sessionId);
81
83
  if (taskListLane)
82
84
  await taskListLane.deleteBySession(sessionId);
83
85
  if (deviceStore)
@@ -23,6 +23,8 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
23
23
  maxTurns: number | undefined;
24
24
  };
25
25
  taskTimeoutSec: number;
26
+ userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
27
+ userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
26
28
  webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
27
29
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
28
30
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_MODEL_REF } from "../model-select.js";
2
- import { probeSearchBackend } from "@sema-agent/core";
2
+ import { probeSearchBackend, uuidv7 } from "@sema-agent/core";
3
3
  import { selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
4
4
  import { subagentSendUserFileExtraTools } from "../capabilities/send-user-file-tool.js";
5
5
  import { parseCapEnv, parseNumOrFailNonNegative } from "../config.js";
@@ -16,6 +16,7 @@ import { explicitOperatorOk } from "../http/server.js";
16
16
  import { selfOrchestrationDenial } from "../task-workflow.js";
17
17
  import { centerEntitlementSourceWired } from "../runtime-caps-resolver.js";
18
18
  import { reclaimOrphansAtBoot } from "../boot-reclaim.js";
19
+ import { createUserInputLedgerAtBoot, writerGoneEvidenceFor } from "../user-input-ledger.js";
19
20
  import { createParkedReviveInheritedGate } from "./parked-revive-gate.js";
20
21
  import { createParkedReviveSurface } from "../parked-revive-surface.js";
21
22
  import { createWebFetchSummarizeLane } from "./webfetch-summarize-lane.js";
@@ -23,7 +24,7 @@ import { createRunnerDepsOnAsk } from "./runner-deps.js";
23
24
  import { bindRateLimiter, bindRunStore } from "./late-binds.js";
24
25
  import { buildDisplaySafeUrl } from "../trace/redact.js";
25
26
  export async function runStage07(ctx) {
26
- const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, counterDegradeHook, durableEnabled, handslessSubRunner, localRoot, logger, memoryEngine, metrics, question, runner, runnerDeps, sendUserFileToolSpec, sessionEnvSelection, sessionStore, subRunner, subRunnerSessions, taskListLane, toolApproval } = ctx;
27
+ const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, counterDegradeHook, durableEnabled, handslessSubRunner, instanceId, localRoot, logger, memoryEngine, metrics, question, runner, runnerDeps, sendUserFileToolSpec, sessionEnvSelection, sessionStore, subRunner, subRunnerSessions, taskListLane, toolApproval } = ctx;
27
28
  const authorize = createAuthorizer(config, sessionStore);
28
29
  const runStore = backend ? backend.run() : undefined;
29
30
  bindRunStore(runStore);
@@ -50,6 +51,20 @@ export async function runStage07(ctx) {
50
51
  if (gateRefusal !== undefined)
51
52
  throw new Error(gateRefusal);
52
53
  await reclaimOrphansAtBoot(runStore, checkpointStore, logger);
54
+ const userInputLedgerStore = backend && runStore ? backend.userInputLedger() : undefined;
55
+ const userInputLedger = backend && runStore && userInputLedgerStore
56
+ ? await (async () => {
57
+ const { ledger, converged } = await createUserInputLedgerAtBoot({
58
+ store: userInputLedgerStore,
59
+ writer: instanceId,
60
+ evidence: writerGoneEvidenceFor(backend.kind, runStore),
61
+ newId: uuidv7,
62
+ });
63
+ if (converged > 0)
64
+ logger.warn("user_input_ledger_crash_converged", { converged, note: "steers accepted with 200 \"applied\" by a previous process that ended before its run settled were converged as cause:crashed; each session's next run announces them (task.user_steer_undrained) and GET /v1/sessions/:id lists them under userInputUndrained" });
65
+ return ledger;
66
+ })()
67
+ : undefined;
53
68
  const imageIndex = backend?.imageIndex ? backend.imageIndex() : undefined;
54
69
  const selectEnvTool = config.selectEnvironmentTool && config.remoteExec?.provider === "k8s" && imageIndex
55
70
  ? selectEnvironmentTool({
@@ -156,6 +171,6 @@ export async function runStage07(ctx) {
156
171
  const builtinScenarioNames = Object.keys(scenarioDetails);
157
172
  configCenter.applyCenterScenarios({ scenarioDeps, scenarios, scenarioDetails, builtinScenarioNames });
158
173
  logger.info("capabilities", { skills: skills.length, scenarios: Object.keys(scenarios), repoTools: Boolean(repoClient) });
159
- return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveSurface, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, taskLimitCaps, taskTimeoutSec, webSearchProvider: webSearchCfg?.provider };
174
+ return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveSurface, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, taskLimitCaps, taskTimeoutSec, userInputLedger, userInputLedgerStore, webSearchProvider: webSearchCfg?.provider };
160
175
  }
161
176
  //# sourceMappingURL=stage-07-capability-layer.js.map
@@ -36,6 +36,8 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
36
36
  maxTurns: number | undefined;
37
37
  };
38
38
  taskTimeoutSec: number;
39
+ userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
40
+ userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
39
41
  webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
40
42
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
41
43
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
@@ -5,12 +5,12 @@ import { startRetentionLane, RETENTION_LEASE_TTL_FACTOR } from "./retention-lane
5
5
  import { createLimitSync } from "./limit-sync.js";
6
6
  import { runDenySweep } from "./late-binds.js";
7
7
  export function runStage08(ctx) {
8
- const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, consolidationSeatWatch, costQuota, degradeRouteWatch, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
8
+ const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, consolidationSeatWatch, costQuota, degradeRouteWatch, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, userInputLedger, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
9
9
  const reaper = startReapers({
10
10
  config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore,
11
11
  rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap,
12
12
  workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore,
13
- backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore,
13
+ backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, userInputLedger,
14
14
  getRunDenySweep: () => runDenySweep,
15
15
  });
16
16
  const ttlMs = config.retentionSweep.intervalSec * 1000 * RETENTION_LEASE_TTL_FACTOR;
@@ -72,6 +72,8 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
72
72
  maxTurns: number | undefined;
73
73
  };
74
74
  taskTimeoutSec: number;
75
+ userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
76
+ userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
75
77
  webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
76
78
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
77
79
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
@@ -6,7 +6,7 @@ import { createLeaderFace } from "./leader.js";
6
6
  import { assertStaticWiringConsistent } from "../http/routes/diagnostics.js";
7
7
  import { buildDisplaySafeUrl } from "../trace/redact.js";
8
8
  export function runStage09(ctx) {
9
- const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, workflowCompletionInbox } = ctx;
9
+ const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, userInputLedgerStore, workflowCompletionInbox } = ctx;
10
10
  const { ownerAware, sessionAudit, sessionWatchRegistry, purgeSession, instrumentDegenerate, planCacheProbe } = createSessionFaces({
11
11
  deviceStore: deviceLane?.store,
12
12
  dropSessionScoped,
@@ -14,6 +14,7 @@ export function runStage09(ctx) {
14
14
  resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure,
15
15
  workflowCompletionInbox, taskListLane, mailboxStore, mailboxRecipientLifecycle,
16
16
  backgroundAgentStore,
17
+ userInputLedgerStore,
17
18
  });
18
19
  const leaderEndpoint = createLeaderFace({ config, logger, brain, pricing, executionEnvFactory, toolResultStore, sessionStore, checkpointStore, governanceSeams, backend, tracer, usageWindowStore, promptSource: configCenter.promptSource, onError: runnerDeps.onError });
19
20
  const drainState = { draining: false };
@@ -72,6 +72,8 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
72
72
  maxTurns: number | undefined;
73
73
  };
74
74
  taskTimeoutSec: number;
75
+ userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
76
+ userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
75
77
  webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
76
78
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
77
79
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
@@ -26,7 +26,7 @@ import { bindServerLateSeats } from "./late-binds.js";
26
26
  import { serviceVersion } from "../service-version.js";
27
27
  import { buildDisplaySafeUrl } from "../trace/redact.js";
28
28
  export async function runStage10(ctx) {
29
- const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, parkCommits, config, configCenter, consolidationValve, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, noteSessionIncarnation, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionInheritEnv, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storePosture, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
29
+ const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, parkCommits, config, configCenter, consolidationValve, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, noteSessionIncarnation, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionInheritEnv, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storePosture, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, userInputLedger, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
30
30
  const stores = {
31
31
  runStore,
32
32
  resumeAnchorStore: resumeAnchorStore ? resumeAnchorStore : undefined,
@@ -48,6 +48,7 @@ export async function runStage10(ctx) {
48
48
  imageBakes,
49
49
  outcomeSink: outcomeSink ? outcomeSink : undefined,
50
50
  sendFileLedger: sendFileLedger ? sendFileLedger : undefined,
51
+ userInputLedger,
51
52
  backend: backend ? backend : undefined,
52
53
  };
53
54
  let resolveFleetRecovery;
@@ -20,7 +20,7 @@ export function createWorkflowOrchestration(ctx) {
20
20
  }))
21
21
  : undefined;
22
22
  const workflowNotifyJournal = config.selfOrchestrationEnabled && config.workflowRunStoreBackend !== "memory"
23
- ? (sqlWorkflowRunStore ? backend.notifyJournal() : new FileWorkflowNotifyJournalStore(join(config.localDataRoot ?? localRoot, "workflows")))
23
+ ? (sqlWorkflowRunStore ? backend.notifyJournal() : new FileWorkflowNotifyJournalStore(join(config.localDataRoot ?? localRoot, "workflows"), undefined, (msg, meta) => logger.warn(msg, meta)))
24
24
  : undefined;
25
25
  const workflowCompletionInbox = config.selfOrchestrationEnabled
26
26
  ? config.workflowRunStoreBackend === "memory"
@@ -244,7 +244,7 @@ export const CONFIG_CATALOG = [
244
244
  r("MEMORY_SCOPE", "memory", "string", "单用户部署的记忆 scope 名", { derivedDefaultNote: "缺席:引擎开 ∧ 单用户 ⇒ local;其余 ⇒ 缺席(记忆暗)", configKey: "memoryScope" }),
245
245
  r("MEMORY_SYNC_MAX_PULL_ENTRIES", "memory", "number", "TOC 同步单轮 pull 条数帽(设了必须 ≥1 整数)", { derivedDefaultNote: "缺席 ⇒ 键缺席,core 默认" }),
246
246
  r("MEMORY_SYNC_MAX_PUSH_ENTRIES", "memory", "number", "TOC 同步单轮 push 条数帽(设了必须 ≥1 整数)", { derivedDefaultNote: "缺席 ⇒ 键缺席,core 默认" }),
247
- r("MEMORY_SYNC_SCOPE", "memory", "string", "TOC 同步 scope 覆写", { derivedDefaultNote: "缺席 ⇒ 从 MEMORY_SCOPE 铸 user 盘键" }),
247
+ r("MEMORY_SYNC_SCOPE", "memory", "string", "TOC 同步 scope 覆写", { derivedDefaultNote: "缺席 ⇒ = MEMORY_SCOPE,仅当它已是 typed 键;非 typed 键拒启(S-964)" }),
248
248
  r("MEMORY_SYNC_TOKEN", "memory", "secret", "TOC 同步中心面的 Bearer(URL 设了它必须在,半配拒启)", { danger: SEC }),
249
249
  r("MEMORY_SYNC_URL", "memory", "url", "TOC 同步中心地址(设了=开;file 记忆形专用,DB 后端配了拒启;同步只覆盖一层 ⇒ 单用户任务不按项目键控、写家落全局层 MEMORY_SCOPE(S-512 让位,启动通知 `memory_project_scope_yields_to_sync`))"),
250
250
  r("METRICS_TOKEN", "observability", "secret", "/metrics 只读令牌", { danger: SEC }),
@@ -680,8 +680,9 @@ export interface ServiceConfigFlat {
680
680
  memoryPersistenceCapable?: boolean;
681
681
  /** 142-S2.5-W1: TOC 同步 client 腿 —— file memory 形态把本地记忆盘与中心(DB 权威,
682
682
  * POST /v1/memory/sync/:scope)做一轮一 RTT 双向同步。env 三键:`MEMORY_SYNC_URL`(设了=开;缺省
683
- * undefined=纯本地现状零变化)/`MEMORY_SYNC_TOKEN`(Bearer)/`MEMORY_SYNC_SCOPE`(可选,缺省=
684
- * formatUserScope(memoryScope);已是 v2 typed key 则原样直通,见 loadConfig)。半配置 fail-loud
683
+ * undefined=纯本地现状零变化)/`MEMORY_SYNC_TOKEN`(Bearer)/`MEMORY_SYNC_SCOPE`(可选,缺省 =
684
+ * memoryScope 本身**仅当它已是 v2 typed key**;非 typed 键 ⇒ 拒启,S-964 —— 旧 formatUserScope 派生臂铸出的是本部署
685
+ * 从不写的层,见 loadConfig)。半配置 fail-loud
685
686
  * (memoryEngineBackend 同款姿势);DB backend(pg/tidb)配了这些键=boot throw(中心侧自己就是
686
687
  * 同步权威,不该再跑 client 腿)。 */
687
688
  memorySync?: {
package/dist/config.js CHANGED
@@ -3,11 +3,11 @@ import { DEFAULT_MODEL_REF } from "./model-select.js";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, formatUserScope, isModelPriced, PROTOCOL_TABLE, protocolOf, RECOMMENDED_SENSITIVE_PATTERNS, RETIRED_TOOL_NAMES, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, parseRuleText } from "@sema-agent/core";
6
+ import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, isModelPriced, PROTOCOL_TABLE, protocolOf, RECOMMENDED_SENSITIVE_PATTERNS, RETIRED_TOOL_NAMES, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, parseRuleText } from "@sema-agent/core";
7
7
  import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, CENTER_CREDENTIAL_ENV } from "@sema-agent/settings-schema";
8
8
  import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
9
9
  import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
10
- import { isV2ScopeKey, memoryReplicationCoverage } from "./memory-scope.js";
10
+ import { isV2ScopeKey, memoryReplicationCoverage, replicationCovers } from "./memory-scope.js";
11
11
  import { DEFAULT_QUESTION_THROTTLE } from "./question.js";
12
12
  import { GIT_API_KINDS, isGitApiKind } from "./git-api-kind.js";
13
13
  import { CONFIG_PROVIDER_WORDS, DB_BACKEND_WORDS, RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS, MAX_TIMER_DELAY_MS, AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, DRAIN_GRACE_FLOOR_MS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS } from "./config-types.js";
@@ -1666,9 +1666,14 @@ function parseMemoryDomain(ctx) {
1666
1666
  if (memorySyncToken === undefined) {
1667
1667
  throw new Error("MEMORY_SYNC_URL is set but MEMORY_SYNC_TOKEN is not — refusing to start half-configured (the central sync face requires a bearer token)");
1668
1668
  }
1669
- const scope = memorySyncScopeRaw ?? (memoryScope !== undefined ? (isV2ScopeKey(memoryScope) ? memoryScope : formatUserScope(memoryScope)) : undefined);
1669
+ if (memorySyncScopeRaw === undefined && memoryScope !== undefined && !isV2ScopeKey(memoryScope)) {
1670
+ throw new Error(`MEMORY_SYNC_URL is set but MEMORY_SYNC_SCOPE is not set and MEMORY_SCOPE (${JSON.stringify(memoryScope)}) is not a typed scope key — the sync leg replicates exactly ONE scope by name ` +
1671
+ "(core syncMemoryScope takes the entries whose scope === the sync scope and POSTs them under that same name), so deriving `user:<MEMORY_SCOPE>` would name a scope this deployment never writes and nothing would ever be synced (S-964). " +
1672
+ "Either set MEMORY_SYNC_SCOPE to the scope to replicate, or set MEMORY_SCOPE to the typed key the center expects (e.g. user:<tenant>) so the sync scope defaults to the same name.");
1673
+ }
1674
+ const scope = memorySyncScopeRaw ?? memoryScope;
1670
1675
  if (scope === undefined) {
1671
- throw new Error("MEMORY_SYNC_URL is set but no sync scope is derivable (memory engine off or multi-tenant without an explicit scope) — set MEMORY_SYNC_SCOPE, or run the single-user file memory engine so MEMORY_SCOPE (default \"local\") provides one");
1676
+ throw new Error("MEMORY_SYNC_URL is set but no sync scope is derivable (memory engine off or multi-tenant without an explicit scope) — set MEMORY_SYNC_SCOPE, or run the single-user file memory engine with a typed MEMORY_SCOPE (e.g. user:<tenant>) so it provides one");
1672
1677
  }
1673
1678
  memorySync = {
1674
1679
  url: memorySyncUrl,
@@ -1680,15 +1685,22 @@ function parseMemoryDomain(ctx) {
1680
1685
  }
1681
1686
  const replication = memoryReplicationCoverage(memorySync !== undefined ? { memorySync } : {});
1682
1687
  if (replication.kind === "scopes" && memoryEngineEnabled && !requirePrincipal && memoryScope !== undefined) {
1688
+ const YIELD_HINT = {
1689
+ in: "memory sync (MEMORY_SYNC_*) replicates only the scope(s) listed in replicatedScopes — the per-project memory layer (S-512, repo:<key>) is not replicated, " +
1690
+ "so sessions do NOT write project-keyed memory here: new memory lands in the global layer (writeScope = MEMORY_SCOPE), which IS replicated, and every " +
1691
+ "project reads it. Project-layer replication is a separate change; unset MEMORY_SYNC_URL to get per-project memory without sync.",
1692
+ out: "memory sync (MEMORY_SYNC_*) replicates only the scope(s) listed in replicatedScopes — the per-project memory layer (S-512, repo:<key>) is not replicated, " +
1693
+ "so sessions do NOT write project-keyed memory here: new memory lands in the global layer (writeScope = MEMORY_SCOPE), every project reads it, " +
1694
+ "and that write scope is not among the replicated scopes either — NONE of the memory this deployment writes is synced. " +
1695
+ "Point MEMORY_SYNC_SCOPE at the write scope (sync replicates one scope), or unset MEMORY_SYNC_URL to stop expecting sync.",
1696
+ };
1683
1697
  CONFIG_NOTICES.push({
1684
1698
  level: "warn",
1685
1699
  event: "memory_project_scope_yields_to_sync",
1686
1700
  fields: {
1687
1701
  replicatedScopes: replication.scopes.join(","),
1688
1702
  writeScope: memoryScope,
1689
- hint: "memory sync (MEMORY_SYNC_*) replicates only the scope(s) listed — the per-project memory layer (S-512, repo:<key>) is not replicated, " +
1690
- "so sessions do NOT write project-keyed memory here: new memory lands in the global layer (MEMORY_SCOPE), the same as 7.97.0, and every " +
1691
- "project reads it. Project-layer replication is a separate change; unset MEMORY_SYNC_URL to get per-project memory without sync.",
1703
+ hint: YIELD_HINT[replicationCovers(replication, memoryScope) ? "in" : "out"],
1692
1704
  },
1693
1705
  });
1694
1706
  }
@@ -243,8 +243,14 @@ export declare function buildOnlySensitiveBaselineWarning(config: Pick<Deploymen
243
243
  * `applyRuntimeGovernance` 的 governance 实参预铸([ref] 件一)。
244
244
  *
245
245
  * 键存在性 profile 逐字保持消费腿原样:`autonomy`/`commandPolicy` 恒在场(值可为 `undefined`),
246
- * `manualModeShellGate`/`sensitivePathPolicy` 按在场性条件展开 —— `applyRuntimeGovernance` 的
247
- * `!== undefined` 判据对两者等价,但 profile 是折叠面的可观测字节,搬家不许顺手改。
246
+ * `manualModeShellGate`/`sensitivePathLayers` 按在场性条件展开 —— `applyRuntimeGovernance` 对缺席与空的判读等价,
247
+ * 但 profile 是折叠面的可观测字节,搬家不许顺手改。
248
+ *
249
+ * 🔴 **S-958(core #1266 7.39.0)—— 守卫集交叶,不交折叠**:修前这里铸一只预折的 `sensitivePathPolicy`
250
+ * (`combinePolicies(真身层, 誊本门)`,沙箱形再套一层两形的折叠),治理层随后在**整个治理折叠的输出上**展开盖部署出身。
251
+ * 现在治理层逐叶盖位、只折一次(`runtime-governance.ts` `applyRuntimeGovernance` 顶注末段),所以本口交出的是叶的列表:
252
+ * 本口不知道出身这件事,也不再有自己的折叠点。叶的顺序 = 修前折叠的顺序(真身层 → 词法层 → 誊本门),deny 优先 / 首问 / 成员合并
253
+ * 的读法因此不变。
248
254
  */
249
255
  export declare function createDeploymentGovernanceInputs(config: DeploymentGovernanceConfigView, pathAdjudication: PathAdjudication, roots: DeploymentGovernanceRoots): DeploymentGovernanceInputs;
250
256
  /**
@@ -84,7 +84,7 @@ export function buildOnlySensitiveBaselineWarning(config, seat) {
84
84
  };
85
85
  }
86
86
  export function createDeploymentGovernanceInputs(config, pathAdjudication, roots) {
87
- const sensitivePathPolicy = config.sensitiveWritePatterns.length > 0
87
+ const sensitivePathLayers = config.sensitiveWritePatterns.length > 0
88
88
  ? (() => {
89
89
  const dataRootOpt = roots.engineDataRoot !== undefined ? { dataRoot: roots.engineDataRoot } : {};
90
90
  const realTarget = createSensitivePathPolicy({
@@ -93,20 +93,21 @@ export function createDeploymentGovernanceInputs(config, pathAdjudication, roots
93
93
  ...(pathAdjudication.cwd !== undefined ? { rootPath: pathAdjudication.cwd } : {}),
94
94
  ...dataRootOpt,
95
95
  });
96
- const transcriptGuard = config.localDataRoot !== undefined
97
- ? createTranscriptIntegrityPolicy({ sessionsDirs: [join(config.localDataRoot, "sessions")] })
98
- : undefined;
99
- const withTranscripts = (p) => (transcriptGuard ? combinePolicies(p, transcriptGuard) : p);
96
+ const transcriptGuard = config.localDataRoot !== undefined ? [createTranscriptIntegrityPolicy({ sessionsDirs: [join(config.localDataRoot, "sessions")] })] : [];
100
97
  if (pathAdjudication.cwd !== undefined)
101
- return withTranscripts(realTarget);
102
- return withTranscripts(combinePolicies(scopedToWriteTargetForm(realTarget, "absolute-or-stamped"), scopedToWriteTargetForm(createSensitivePathPolicy({ env: new RelativeTargetLexicalEnv(), patterns: config.sensitiveWritePatterns, ...dataRootOpt }), "unstamped-relative")));
98
+ return [realTarget, ...transcriptGuard];
99
+ return [
100
+ scopedToWriteTargetForm(realTarget, "absolute-or-stamped"),
101
+ scopedToWriteTargetForm(createSensitivePathPolicy({ env: new RelativeTargetLexicalEnv(), patterns: config.sensitiveWritePatterns, ...dataRootOpt }), "unstamped-relative"),
102
+ ...transcriptGuard,
103
+ ];
103
104
  })()
104
105
  : undefined;
105
106
  return {
106
107
  autonomy: config.autonomy,
107
108
  commandPolicy: config.commandPolicy,
108
109
  ...(config.manualModeShellGate ? { manualModeShellGate: config.manualModeShellGate } : {}),
109
- ...(sensitivePathPolicy ? { sensitivePathPolicy } : {}),
110
+ ...(sensitivePathLayers ? { sensitivePathLayers } : {}),
110
111
  };
111
112
  }
112
113
  export function createApprovalBaselinePolicy(config, seat) {
@@ -63,7 +63,6 @@ export interface HookRunnerContext {
63
63
  sessionId: string;
64
64
  /** hook 命令的工作目录 + payload `cwd` + `CLAUDE_PROJECT_DIR`(host lane 的 session cwd;无则 server cwd)。 */
65
65
  cwd: string;
66
- permissionMode?: string;
67
66
  /** 请求自带的 `settings.env`(单用户闸后)——hook 进程 env 与 http $NAME 插值的用户变量通道。 */
68
67
  shellEnv?: Record<string, string>;
69
68
  /** 阶段三b:`prompt` 条目的模型调用载体(部署组装:一次非流式 completion,费用不折 task budget)。
@@ -175,7 +174,8 @@ export declare function createTaskHooks(config: HooksConfig, ctx: HookRunnerCont
175
174
  * 把部署级 hooks 基线(deps.hooks,如 TOOL_TRACE 观测)折进 task 级 hooks——core 是整槽覆盖
176
175
  * (`spec.hooks ?? deps.hooks`),不折叠就静默 shadow 部署观测(runtask gateBaseline 注释点名的坑)。
177
176
  * 语义:部署槽先跑(观测看到原始输入/输出),task 槽后跑;结果合并:
178
- * - preToolUse:deny > ask > allow 折叠;updatedInput 串行线程(部署重写喂给 task 槽);context 拼接。
177
+ * - preToolUse:deny > ask > allow 折叠;updatedInput 串行线程(部署重写喂给 task 槽);context 拼接。读不出词的结果
178
+ * (非对象、集外 `action`)原样交回、由引擎的接手屏判(S-959,短路同 deny)。
179
179
  * - postToolUse/postToolUseFailure/postToolBatch:双跑,additionalContext 拼接,updatedOutput 取
180
180
  * 后者(task 槽)优先。
181
181
  * - userPromptSubmit/stop:部署 block 优先短路(不再跑 task 槽),否则双跑、context 拼接、task block 生效。
@@ -648,12 +648,12 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
648
648
  }
649
649
  return results;
650
650
  }
651
- function basePayload(ctx) {
651
+ function basePayload(ctx, permissionMode) {
652
652
  return {
653
653
  session_id: ctx.sessionId,
654
654
  transcript_path: "",
655
655
  cwd: ctx.cwd,
656
- ...(ctx.permissionMode ? { permission_mode: ctx.permissionMode } : {}),
656
+ ...(permissionMode !== undefined ? { permission_mode: permissionMode } : {}),
657
657
  };
658
658
  }
659
659
  function ignoreMatchers(groups) {
@@ -732,7 +732,7 @@ export function createTaskHooks(config, ctx) {
732
732
  if (!m)
733
733
  continue;
734
734
  const payload = {
735
- ...basePayload(ctx),
735
+ ...basePayload(ctx, tctx.permissionMode),
736
736
  hook_event_name: "PreToolUse",
737
737
  tool_name: toolName,
738
738
  tool_input: currentInput,
@@ -827,7 +827,7 @@ export function createTaskHooks(config, ctx) {
827
827
  .map((c) => (c && typeof c === "object" && typeof c.text === "string" ? c.text : ""))
828
828
  .join("");
829
829
  const payload = {
830
- ...basePayload(ctx),
830
+ ...basePayload(ctx, tctx.permissionMode),
831
831
  hook_event_name: "PostToolUse",
832
832
  tool_name: toolName,
833
833
  tool_input: input,
@@ -876,7 +876,7 @@ export function createTaskHooks(config, ctx) {
876
876
  const postToolUseFailureHook = async (toolName, input, f, tctx) => {
877
877
  const contexts = [];
878
878
  const payload = {
879
- ...basePayload(ctx),
879
+ ...basePayload(ctx, tctx.permissionMode),
880
880
  hook_event_name: "PostToolUseFailure",
881
881
  tool_name: toolName,
882
882
  tool_input: input,
@@ -919,10 +919,10 @@ export function createTaskHooks(config, ctx) {
919
919
  hooks.postToolUseFailure = postToolUseFailureHook;
920
920
  }
921
921
  if (postBatch) {
922
- const postToolBatchHook = async (calls) => {
922
+ const postToolBatchHook = async (calls, _meta, bctx) => {
923
923
  const contexts = [];
924
924
  const payload = {
925
- ...basePayload(ctx),
925
+ ...basePayload(ctx, bctx?.permissionMode),
926
926
  hook_event_name: "PostToolBatch",
927
927
  tool_calls: calls.map((c) => ({
928
928
  tool_name: c.toolName,
@@ -962,9 +962,9 @@ export function createTaskHooks(config, ctx) {
962
962
  hooks.postToolBatch = postToolBatchHook;
963
963
  }
964
964
  if (promptSubmit) {
965
- const userPromptSubmitHook = async (prompt) => {
965
+ const userPromptSubmitHook = async (prompt, uctx) => {
966
966
  const contexts = [];
967
- const payload = { ...basePayload(ctx), hook_event_name: "UserPromptSubmit", prompt: clip(prompt, MAX_TOOL_INPUT_CHARS) };
967
+ const payload = { ...basePayload(ctx, uctx?.permissionMode), hook_event_name: "UserPromptSubmit", prompt: clip(prompt, MAX_TOOL_INPUT_CHARS) };
968
968
  const singles = await runMatchingEntries("UserPromptSubmit", ignoreMatchers(promptSubmit), "", payload, ctx, onceFired, Date.now() + MAX_HOOK_EVENT_TOTAL_SECONDS * 1000);
969
969
  for (const { entry, run } of singles) {
970
970
  if (run.code === 2) {
@@ -1003,7 +1003,7 @@ export function createTaskHooks(config, ctx) {
1003
1003
  }
1004
1004
  if (stopEntries) {
1005
1005
  const stopHook = async (sctx) => {
1006
- const payload = { ...basePayload(ctx), hook_event_name: "Stop", stop_hook_active: sctx.stopHookActive };
1006
+ const payload = { ...basePayload(ctx, sctx.permissionMode), hook_event_name: "Stop", stop_hook_active: sctx.stopHookActive };
1007
1007
  let llmExtra;
1008
1008
  const wantsPrompt = stopEntries.some((g) => g.hooks.some((h) => h.type === "prompt"));
1009
1009
  if (wantsPrompt) {
@@ -1097,7 +1097,7 @@ export function createTaskHooks(config, ctx) {
1097
1097
  const stopFailureHook = async (fctx) => {
1098
1098
  const turnError = toAssistantTurnError(fctx.errorKind);
1099
1099
  const payload = {
1100
- ...basePayload(ctx),
1100
+ ...basePayload(ctx, undefined),
1101
1101
  hook_event_name: "StopFailure",
1102
1102
  error: turnError,
1103
1103
  ...(fctx.error ? { error_details: clip(redactSecrets(fctx.error), MAX_HOOK_FEEDBACK_CHARS) } : {}),
@@ -1109,7 +1109,7 @@ export function createTaskHooks(config, ctx) {
1109
1109
  if (preCompactEntries) {
1110
1110
  const preCompactHook = async (pctx) => {
1111
1111
  const payload = {
1112
- ...basePayload(ctx),
1112
+ ...basePayload(ctx, undefined),
1113
1113
  hook_event_name: "PreCompact",
1114
1114
  trigger: pctx.trigger,
1115
1115
  custom_instructions: pctx.customInstructions ?? null,
@@ -1143,7 +1143,7 @@ export function createTaskHooks(config, ctx) {
1143
1143
  if (postCompactEntries) {
1144
1144
  const postCompactHook = async (cctx) => {
1145
1145
  const payload = {
1146
- ...basePayload(ctx),
1146
+ ...basePayload(ctx, undefined),
1147
1147
  hook_event_name: "PostCompact",
1148
1148
  trigger: cctx.trigger,
1149
1149
  compact_summary: clip(cctx.summary, MAX_TOOL_RESPONSE_CHARS),
@@ -1191,16 +1191,30 @@ export function composeHooks(deployment, task) {
1191
1191
  if (!slot)
1192
1192
  continue;
1193
1193
  const r = await slot(toolName, current, ctx);
1194
- if (!r)
1194
+ if (r === undefined || Object.is(r, null) || Object.is(r, false))
1195
1195
  continue;
1196
+ if (typeof r !== "object")
1197
+ return r;
1196
1198
  if (r.additionalContext)
1197
1199
  contexts.push(r.additionalContext);
1198
- if (r.action === "deny")
1199
- return { ...r, ...buildHookContextField(contexts) };
1200
- if (r.action === "ask")
1201
- asked = r;
1202
- if (r.action === "allow" && r.updatedInput !== undefined) {
1203
- current = r.updatedInput;
1200
+ const action = r.action;
1201
+ const updatedInput = r.updatedInput;
1202
+ switch (action) {
1203
+ case "deny":
1204
+ return { ...r, ...buildHookContextField(contexts) };
1205
+ case "ask":
1206
+ asked = r;
1207
+ break;
1208
+ case "allow":
1209
+ case undefined:
1210
+ if (updatedInput !== undefined)
1211
+ current = updatedInput;
1212
+ break;
1213
+ default: {
1214
+ const unread = action;
1215
+ void unread;
1216
+ return r;
1217
+ }
1204
1218
  }
1205
1219
  }
1206
1220
  if (asked) {
@@ -1245,20 +1259,20 @@ export function composeHooks(deployment, task) {
1245
1259
  };
1246
1260
  }
1247
1261
  if (d.postToolBatch || t.postToolBatch) {
1248
- out.postToolBatch = async (calls) => {
1249
- const rd = d.postToolBatch ? await d.postToolBatch(calls) : undefined;
1250
- const rt = t.postToolBatch ? await t.postToolBatch(calls) : undefined;
1262
+ out.postToolBatch = async (calls, meta, bctx) => {
1263
+ const rd = d.postToolBatch ? await d.postToolBatch(calls, meta, bctx) : undefined;
1264
+ const rt = t.postToolBatch ? await t.postToolBatch(calls, meta, bctx) : undefined;
1251
1265
  const contexts = [rd?.additionalContext, rt?.additionalContext].filter((c) => typeof c === "string" && c.length > 0);
1252
1266
  const additionalContext = buildHookContext(contexts);
1253
1267
  return additionalContext === undefined ? undefined : { additionalContext };
1254
1268
  };
1255
1269
  }
1256
1270
  if (d.userPromptSubmit || t.userPromptSubmit) {
1257
- out.userPromptSubmit = async (prompt) => {
1258
- const rd = d.userPromptSubmit ? await d.userPromptSubmit(prompt) : undefined;
1271
+ out.userPromptSubmit = async (prompt, uctx) => {
1272
+ const rd = d.userPromptSubmit ? await d.userPromptSubmit(prompt, uctx) : undefined;
1259
1273
  if (rd?.block)
1260
1274
  return { ...rd, ...buildHookContextField(rd.additionalContext ? [rd.additionalContext] : []) };
1261
- const rt = t.userPromptSubmit ? await t.userPromptSubmit(prompt) : undefined;
1275
+ const rt = t.userPromptSubmit ? await t.userPromptSubmit(prompt, uctx) : undefined;
1262
1276
  if (!rd && !rt)
1263
1277
  return undefined;
1264
1278
  const contexts = [rd?.additionalContext, rt?.additionalContext].filter((c) => typeof c === "string" && c.length > 0);
@@ -63,7 +63,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
63
63
  windowMs: deps.config.streamApproval?.windowMs,
64
64
  windowMarginMs: deps.config.streamAskWindowMarginMs,
65
65
  }
66
- : undefined, deps.checkpointStore, args.prepared.engineNotices, args.trustedInternals);
66
+ : undefined, deps.checkpointStore, args.prepared.engineNotices, args.trustedInternals, deps.userInputLedger);
67
67
  return { status: 202, body: { taskId, sessionId, status: "running" } };
68
68
  }
69
69
  //# sourceMappingURL=durable-run-leg.js.map