@akagilnc/pi-workflow-roles 0.1.3902 → 0.1.3936

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 (34) hide show
  1. package/README.md +3 -3
  2. package/README.zh-CN.md +4 -4
  3. package/dist/acp-host/production-host.js +2540 -1994
  4. package/dist/compliance-transport.js +4 -1
  5. package/dist/headless-host/production-host.js +2274 -1728
  6. package/dist/prepared-role-turn.js +5 -0
  7. package/dist/public-cli/auto-resume.js +115 -13
  8. package/dist/public-cli/diarist-run.js +154 -0
  9. package/dist/public-cli/main.js +7082 -5854
  10. package/dist/public-cli/option-definitions.js +1 -1
  11. package/dist/public-cli/post-admission.js +455 -188
  12. package/dist/public-cli/role-turn-host-resolution.js +59 -0
  13. package/dist/public-cli/run-lifecycle.js +11 -6
  14. package/dist/public-cli/turn-request.js +1 -0
  15. package/dist/public-role-summons.js +48 -42
  16. package/dist/role-activation-flags.js +3 -0
  17. package/dist/role-runtime.js +14 -3
  18. package/dist/session-identity.js +58 -0
  19. package/package.json +1 -1
  20. package/src/compliance-transport.ts +6 -1
  21. package/src/host-contracts.ts +2 -0
  22. package/src/public-cli/auto-resume.ts +157 -11
  23. package/src/public-cli/cli.ts +25 -85
  24. package/src/public-cli/countersign-run.ts +26 -33
  25. package/src/public-cli/option-definitions.ts +1 -1
  26. package/src/public-cli/post-admission.ts +514 -159
  27. package/src/public-cli/reviewer-run.ts +0 -2
  28. package/src/public-cli/role-turn-host-resolution.ts +122 -0
  29. package/src/public-cli/run-lifecycle.ts +15 -10
  30. package/src/public-cli/turn-request.ts +3 -0
  31. package/src/public-role-summons.ts +71 -54
  32. package/src/role-activation-flags.ts +3 -0
  33. package/src/role-runtime.ts +14 -2
  34. package/src/session-identity.ts +34 -0
@@ -0,0 +1,5 @@
1
+ import { renderAgentStartMaterials } from "./agent-start-materials.js";
2
+ /** Fold structured system-prompt authority into the provider-visible ACP override. */
3
+ export function renderSystemPromptOverride(authority) {
4
+ return renderAgentStartMaterials(authority.body, authority.materials);
5
+ }
@@ -15,11 +15,35 @@ import { constants as fsConstants } from "node:fs";
15
15
  import { randomUUID } from "node:crypto";
16
16
  import { lstat, mkdir, open } from "node:fs/promises";
17
17
  import { join } from "node:path";
18
- import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
18
+ import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunResumable, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
19
19
  import { parseAutoResumeLimit } from "./config.js";
20
20
  import { isLawfulTypedTerminalOutcome, formatTerminalResult } from "./terminal.js";
21
- import { presentFailureTerminal, presentStructuralRejection, } from "./settlement.js";
21
+ import { presentFailureTerminal, presentStructuralRejection, resolveControlledFailureResumeObservation, } from "./settlement.js";
22
22
  const dummyIo = { stdout: () => { }, stderr: () => { } };
23
+ /**
24
+ * Persist run-state after a host-turn result, outside the retried dispatch try.
25
+ * Lawful settlement always seals terminal — a typed 429 observation must not
26
+ * win (#416 成功即停). 429 only marks resumable on the controlled-failure path.
27
+ * Write failure must escape the retried dispatch try instead of becoming a
28
+ * synthetic host-turn terminal.
29
+ */
30
+ export async function persistReturnedRunState(admitted, authority, options) {
31
+ if (options?.lawful === true) {
32
+ await markRunTerminal(admitted.runDirectory);
33
+ return;
34
+ }
35
+ const resumeObservation = await resolveControlledFailureResumeObservation({
36
+ runDirectory: admitted.runDirectory,
37
+ });
38
+ const typedHttp429 = resumeObservation.typedHttp429;
39
+ if (admitted.principal !== undefined && typedHttp429 !== undefined) {
40
+ if (await authority.isAvailable(admitted.principal)) {
41
+ await markRunResumable(admitted.runDirectory, typedHttp429);
42
+ return;
43
+ }
44
+ }
45
+ await markRunTerminal(admitted.runDirectory);
46
+ }
23
47
  function presentTerminal(terminal, io) {
24
48
  if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
25
49
  presentFailureTerminal(terminal, io);
@@ -42,6 +66,25 @@ async function finalizeExceptionRunBestEffort(runDirectory, io) {
42
66
  io.stderr(`run terminal-state finalization failed (best-effort continue): ${describeErrorIdentity(error)}\n`);
43
67
  }
44
68
  }
69
+ /**
70
+ * Thrown by dispatchPostAdmissionTurn when a failure happens inside its own
71
+ * settlement authority (presentControlledFailure) after the host turn
72
+ * genuinely started — never to fabricate a replacement terminal (ADR 0080
73
+ * single-settlement-disposition: settlement stays exactly presentControlledFailure
74
+ * / settleFailureTerminalResult, or — once the retry budget is exhausted —
75
+ * this loop's own dispatchExceptionFailureTerminal). Its only job is to carry
76
+ * the "turn already started" fact across the throw boundary so this loop
77
+ * selects a resume payload on the next attempt instead of replaying the
78
+ * initial one (#840 r9 判词 class 1 boundary — 覆盖 executeTurn 已启动后至
79
+ * 返回带 turnDispatched 结果前的全部异常). `cause` is the true failure;
80
+ * retention below serializes it whole via the standard Error.cause chain.
81
+ */
82
+ export class TurnDispatchedFailure extends Error {
83
+ name = "TurnDispatchedFailure";
84
+ constructor(cause) {
85
+ super(`settlement failed after the host turn genuinely started: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
86
+ }
87
+ }
45
88
  /** Session custom-entry type carrying the pointer to one dispatch error file. */
46
89
  export const DISPATCH_ERROR_RETENTION_ENTRY_TYPE = "ak_run_dispatch_error_retention";
47
90
  /** Artifacts subdirectory of a run directory (established run-artifacts location). */
@@ -51,20 +94,23 @@ function runArtifactsDirectory(runDirectory) {
51
94
  /**
52
95
  * #182-A hardened path identity, mirrored from settlement.ts's
53
96
  * ensureAuditEvidenceDirectory: a planted symlink at the run directory or the
54
- * artifacts path must not receive the dispatch error dump (O_NOFOLLOW only
97
+ * artifacts path must not receive a durable run artifact (O_NOFOLLOW only
55
98
  * protects the final file name; recursive mkdir would accept a symlinked
56
- * parent). Fails loudly with the true cause instead.
99
+ * parent). Fails loudly with the true cause instead. Shared by every caller
100
+ * that needs a durable run-artifacts write independent of session/dossier
101
+ * health — dispatch-error retention here, and post-admission's best-effort
102
+ * cleanup diagnostic (#840 bounce class 2).
57
103
  */
58
- async function ensureRealArtifactsDirectory(runDirectory) {
104
+ export async function ensureRealArtifactsDirectory(runDirectory) {
59
105
  const runStat = await lstat(runDirectory);
60
106
  if (runStat.isSymbolicLink() || !runStat.isDirectory()) {
61
- throw new Error("dispatch error retention: run directory is not a real directory");
107
+ throw new Error("run artifact retention: run directory is not a real directory");
62
108
  }
63
109
  const artifactsDir = runArtifactsDirectory(runDirectory);
64
110
  try {
65
111
  const existing = await lstat(artifactsDir);
66
112
  if (existing.isSymbolicLink() || !existing.isDirectory()) {
67
- throw new Error("dispatch error retention: artifacts path is not a real directory");
113
+ throw new Error("run artifact retention: artifacts path is not a real directory");
68
114
  }
69
115
  }
70
116
  catch (error) {
@@ -73,7 +119,7 @@ async function ensureRealArtifactsDirectory(runDirectory) {
73
119
  await mkdir(artifactsDir, { recursive: true });
74
120
  const created = await lstat(artifactsDir);
75
121
  if (created.isSymbolicLink() || !created.isDirectory()) {
76
- throw new Error("dispatch error retention: artifacts directory is not a real directory");
122
+ throw new Error("run artifact retention: artifacts directory is not a real directory");
77
123
  }
78
124
  }
79
125
  return artifactsDir;
@@ -212,18 +258,35 @@ async function retainDispatchError(admitted, principalAuthority, sessionAppender
212
258
  }
213
259
  return pointerError === undefined ? { file: filePath } : { file: filePath, pointerError };
214
260
  }
261
+ /**
262
+ * Unwrap TurnDispatchedFailure before this loop's own final presentation
263
+ * (#840 r9 判词 class 1 — the auto-resume.ts final presentation boundary).
264
+ * The wrapper only carries the "turn already genuinely started" signal
265
+ * across the throw boundary so the loop above selects a resume payload; the
266
+ * typed decisiveFacts and diagnostic text below must name the real cause it
267
+ * wraps (接住可以,洗白不行 — 未识别异常不得冒用具体标签,真因必须落痕), not
268
+ * the internal signal's own identity.
269
+ */
270
+ function unwrapTurnDispatchedFailure(error) {
271
+ let current = error;
272
+ while (current instanceof TurnDispatchedFailure) {
273
+ current = current.cause;
274
+ }
275
+ return current;
276
+ }
215
277
  /**
216
278
  * Typed failure terminal for a retry path that ended with only exceptions:
217
279
  * loud, non-lawful, carrying the last true cause and the pointers to the
218
280
  * full per-attempt error files. Never rethrows the raw exception at callers.
219
281
  */
220
282
  function dispatchExceptionFailureTerminal(input) {
283
+ const causeError = unwrapTurnDispatchedFailure(input.causeError);
221
284
  // #426 review: this terminal fires whenever the FINAL dispatch throws, not
222
285
  // only when every attempt threw — do not misrepresent a mixed retry history.
223
286
  const history = input.everyAttemptThrew
224
287
  ? "dispatch threw an exception on every attempt"
225
288
  : "the final dispatch threw an exception";
226
- const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(input.causeError)}`;
289
+ const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(causeError)}`;
227
290
  const decisiveFacts = {
228
291
  cause: "unrecognized",
229
292
  diagnostic,
@@ -233,7 +296,7 @@ function dispatchExceptionFailureTerminal(input) {
233
296
  if (input.errorFiles.length > 0) {
234
297
  decisiveFacts.lastDispatchErrorFile = input.errorFiles[input.errorFiles.length - 1];
235
298
  }
236
- const candidate = input.causeError;
299
+ const candidate = causeError;
237
300
  if (typeof candidate?.name === "string")
238
301
  decisiveFacts.errorName = candidate.name;
239
302
  if (typeof candidate?.code === "string" || typeof candidate?.code === "number") {
@@ -262,6 +325,8 @@ export async function runWithAutoResumeLoop(options) {
262
325
  // `attempts >= limit` comparison (always false) — reject here, before any dispatch.
263
326
  const limit = options.autoResumeLimit ?? AUTO_RESUME_LIMIT;
264
327
  parseAutoResumeLimit(limit);
328
+ const isPrincipalAvailable = options.isPrincipalAvailable ??
329
+ ((principal) => options.principalAuthority.isAvailable(principal));
265
330
  let autoResumeAttempts = 0;
266
331
  let isFirst = true;
267
332
  let currentPayload = options.buildInitialPayload();
@@ -282,6 +347,11 @@ export async function runWithAutoResumeLoop(options) {
282
347
  throw error;
283
348
  }
284
349
  let result;
350
+ // Set only when the caught throw is a TurnDispatchedFailure (#840 r9 判词
351
+ // class 1): the host turn genuinely started this attempt even though
352
+ // dispatch produced no result — the next payload must still be a resume,
353
+ // not a replay of the initial one.
354
+ let turnStartedBeforeThrow = false;
285
355
  try {
286
356
  result = await options.dispatch(currentPayload, lease, isFirst, dummyIo);
287
357
  }
@@ -292,6 +362,7 @@ export async function runWithAutoResumeLoop(options) {
292
362
  // diagnostic-sink-isolation precedent). The dispatcher owns lease release
293
363
  // in its own finally, so the retry round starts with the lock free.
294
364
  lastThrownError = error;
365
+ turnStartedBeforeThrow = error instanceof TurnDispatchedFailure;
295
366
  const attempt = dispatchOrdinal;
296
367
  try {
297
368
  // Track the file as soon as it is durably written (#426 review):
@@ -309,6 +380,7 @@ export async function runWithAutoResumeLoop(options) {
309
380
  }
310
381
  }
311
382
  dispatchOrdinal += 1;
383
+ let persistFailure;
312
384
  if (result !== undefined) {
313
385
  everyAttemptThrew = false;
314
386
  const terminal = result.terminal;
@@ -316,6 +388,21 @@ export async function runWithAutoResumeLoop(options) {
316
388
  terminal.autoResumeCount = autoResumeAttempts;
317
389
  }
318
390
  const lawful = terminal !== undefined && isLawfulTypedTerminalOutcome(terminal.roleOutcome);
391
+ if (result.needsPersist === true) {
392
+ // Persist on the loop seam, not inside retried dispatch.
393
+ try {
394
+ await persistReturnedRunState(options.admitted, { isAvailable: isPrincipalAvailable }, lawful ? { lawful: true } : undefined);
395
+ }
396
+ catch (persistError) {
397
+ // Lawful persist stays loud (no fake terminal). Non-lawful persist is
398
+ // not a host-turn failure — do not auto-resume it — but sealed stop
399
+ // must still see the already-settled result (#648).
400
+ if (lawful)
401
+ throw persistError;
402
+ persistFailure = { error: persistError };
403
+ lastThrownError = persistError;
404
+ }
405
+ }
319
406
  if (lawful) {
320
407
  if (terminal !== undefined) {
321
408
  // Present lawful terminal once to real io (dummy was used inside dispatch)
@@ -323,6 +410,11 @@ export async function runWithAutoResumeLoop(options) {
323
410
  }
324
411
  return result;
325
412
  }
413
+ if (result.skipAutoResume === true) {
414
+ if (terminal !== undefined)
415
+ presentTerminal(terminal, options.io);
416
+ return result;
417
+ }
326
418
  }
327
419
  // Settlement-owned sealed disposition before any redispatch (#648 / #672):
328
420
  // shared for non-lawful return and direct throw. Only entry presentation
@@ -357,6 +449,9 @@ export async function runWithAutoResumeLoop(options) {
357
449
  };
358
450
  }
359
451
  }
452
+ if (persistFailure !== undefined) {
453
+ throw persistFailure.error;
454
+ }
360
455
  if (result !== undefined) {
361
456
  const terminal = result.terminal;
362
457
  if (autoResumeAttempts >= limit) {
@@ -364,7 +459,8 @@ export async function runWithAutoResumeLoop(options) {
364
459
  presentTerminal(terminal, options.io);
365
460
  return result;
366
461
  }
367
- if (!(await options.principalAuthority.isAvailable(options.admitted.principal))) {
462
+ if (result.turnDispatched === true
463
+ && !(await isPrincipalAvailable(options.admitted.principal))) {
368
464
  if (terminal !== undefined)
369
465
  presentTerminal(terminal, options.io);
370
466
  return result;
@@ -389,7 +485,7 @@ export async function runWithAutoResumeLoop(options) {
389
485
  terminal,
390
486
  };
391
487
  }
392
- if (!(await options.principalAuthority.isAvailable(options.admitted.principal))) {
488
+ if (!(await isPrincipalAvailable(options.admitted.principal))) {
393
489
  const terminal = dispatchExceptionFailureTerminal({
394
490
  role: options.admitted.role,
395
491
  runId: options.admitted.runId,
@@ -408,7 +504,13 @@ export async function runWithAutoResumeLoop(options) {
408
504
  }
409
505
  }
410
506
  autoResumeAttempts++;
411
- currentPayload = options.buildResumePayload();
507
+ // Resume payload only after a host turn actually started — either a
508
+ // returned result says so, or the attempt threw a TurnDispatchedFailure
509
+ // after genuinely starting the turn (#840 r9 判词 class 1). Pre-turn
510
+ // throws and beforeDispatch failures retry the initial payload (#840 / #416).
511
+ if (result?.turnDispatched === true || turnStartedBeforeThrow) {
512
+ currentPayload = options.buildResumePayload();
513
+ }
412
514
  isFirst = false;
413
515
  }
414
516
  }
@@ -0,0 +1,154 @@
1
+ import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.js";
2
+ import { CliUsageError } from "./cli-errors.js";
3
+ import { admitDiaristInvocation, bindAdmittedTicketNumber, buildInstructionTransportPrompt, } from "./invocation.js";
4
+ import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissionSeatResume, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
5
+ import { loadResumableDiaristRun, markRunAdmitted, } from "./run-lifecycle.js";
6
+ import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
7
+ import { presentStructuralRejection, trySettleDiaristTerminalResult, } from "./settlement.js";
8
+ import { projectRoleTurnRequest, } from "./turn-request.js";
9
+ /** Project admitted invocation onto the host-neutral turn request. */
10
+ export function buildDiaristTurnRequest(admitted, options) {
11
+ return projectRoleTurnRequest(admitted, { activation: { role: "diarist" } }, options);
12
+ }
13
+ function diaristAdapters() {
14
+ return {
15
+ trySettle: (admitted, authority, scope) => trySettleDiaristTerminalResult(admitted, authority, scope),
16
+ shouldPresentSettled: () => true,
17
+ };
18
+ }
19
+ export async function runPublicDiarist(argv, env, io, parseDiaristArgv) {
20
+ let parsed;
21
+ try {
22
+ parsed = parseDiaristArgv(argv);
23
+ }
24
+ catch (error) {
25
+ if (error instanceof CliUsageError) {
26
+ presentStructuralRejection(error, io);
27
+ return { exitCode: 2 };
28
+ }
29
+ throw error;
30
+ }
31
+ // #637 / #771 / ADR 0075 / #779: ticket identity is the LLM's typed assertion
32
+ // on this turn, OR a typed handoff key already held by the caller (countersign
33
+ // refresh). Code never pre-judges the summons text and never freezes a
34
+ // candidate catalog. First summons without handoff stays unbound until assert.
35
+ const projectRoot = parsed.project ?? env.cwd;
36
+ const handoffTicket = env.boundTicketNumber;
37
+ if (typeof handoffTicket === "number" &&
38
+ Number.isSafeInteger(handoffTicket) &&
39
+ handoffTicket >= 1) {
40
+ const summons = {
41
+ instruction: parsed.instruction,
42
+ instructionEmpty: parsed.instruction.trim() === "",
43
+ attachmentPaths: parsed.attachmentPaths,
44
+ };
45
+ const resumed = await tryResumeSameTicketSeatRun({
46
+ home: env.home,
47
+ projectRoot,
48
+ role: "diarist",
49
+ ticketNumber: handoffTicket,
50
+ freshSummons: env.freshSummons,
51
+ summons,
52
+ resume: (runId, materials) => runPublicDiaristResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
53
+ });
54
+ if (resumed !== undefined)
55
+ return resumed;
56
+ }
57
+ let admitted;
58
+ try {
59
+ admitted = await admitDiaristInvocation({
60
+ home: env.home,
61
+ principalAuthority: env.principalAuthority,
62
+ cwd: env.cwd,
63
+ instruction: parsed.instruction,
64
+ attachmentPaths: parsed.attachmentPaths,
65
+ ...(parsed.project === undefined ? {} : { project: parsed.project }),
66
+ ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
67
+ ...(env.model === undefined ? {} : { model: env.model }),
68
+ ...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
69
+ });
70
+ }
71
+ catch (error) {
72
+ if (error instanceof CliUsageError) {
73
+ presentStructuralRejection(error, io);
74
+ return { exitCode: 2 };
75
+ }
76
+ throw error;
77
+ }
78
+ await markRunAdmitted(admitted, env.principalAuthority);
79
+ // Typed handoff: bind before the turn so identity is on durable pages.
80
+ if (typeof handoffTicket === "number" &&
81
+ Number.isSafeInteger(handoffTicket) &&
82
+ handoffTicket >= 1) {
83
+ await bindAdmittedTicketNumber(admitted, handoffTicket);
84
+ }
85
+ const turnProjection = {
86
+ packageRoot: env.packageRoot,
87
+ home: env.home,
88
+ agentDir: env.agentDir,
89
+ ...(env.model === undefined ? {} : { model: env.model }),
90
+ ...(env.engine === undefined ? {} : { engine: env.engine }),
91
+ ...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
92
+ ...(env.correlationId === undefined || env.correlationId.trim() === ""
93
+ ? {}
94
+ : { correlationId: env.correlationId }),
95
+ continuation: {
96
+ kind: "initial",
97
+ prompt: buildInstructionTransportPrompt(admitted, engineSessionMaterialFromOptions({
98
+ ...(env.engine === undefined ? {} : { engine: env.engine }),
99
+ packageRoot: env.packageRoot,
100
+ })),
101
+ },
102
+ };
103
+ const turnRequest = buildDiaristTurnRequest(admitted, turnProjection);
104
+ return await runPostAdmissionOneShot({
105
+ admitted,
106
+ env,
107
+ io,
108
+ request: turnRequest,
109
+ adapters: diaristAdapters(),
110
+ ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
111
+ }).then(async (result) => {
112
+ // Accept may have bound ticket onto durable pages after LLM assertion.
113
+ // Mirror only lawful non-escalate accepted terminals — escalate must not
114
+ // leak an unverified ticketNumber into the caller-visible typed key.
115
+ if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
116
+ const roleOutcome = result.terminal?.roleOutcome;
117
+ if (roleOutcome !== undefined &&
118
+ roleOutcome.kind === "accepted" &&
119
+ roleOutcome.status !== "escalate") {
120
+ const facts = roleOutcome.decisiveFacts;
121
+ const raw = typeof facts.ticketNumber === "number"
122
+ ? facts.ticketNumber
123
+ : typeof facts.sitian?.ticketNumber === "number"
124
+ ? facts.sitian.ticketNumber
125
+ : undefined;
126
+ if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) {
127
+ admitted.ticketNumber = raw;
128
+ if (result.admitted.ticketNumber === undefined) {
129
+ result.admitted.ticketNumber = raw;
130
+ }
131
+ }
132
+ }
133
+ }
134
+ return result;
135
+ });
136
+ }
137
+ /**
138
+ * Resume a previously admitted Diarist run (#708 / ADR 0079 同票传召 = resume).
139
+ * LLM re-finds materials; entry identity keeps the volume idempotent.
140
+ */
141
+ export async function runPublicDiaristResume(request, env, io) {
142
+ return await runPostAdmissionSeatResume({
143
+ request,
144
+ env,
145
+ io,
146
+ load: (effective) => loadResumableDiaristRun(env.home, effective.runId, env.principalAuthority),
147
+ buildTurnRequest: async (admitted, effective) => {
148
+ const summonsPrepared = await prepareSummonsResumeMaterials(admitted.runDirectory, effective.summons);
149
+ return buildDiaristTurnRequest(admitted, resumeTurnRequestProjectionOptions(admitted, effective, env, summonsPrepared));
150
+ },
151
+ adapters: diaristAdapters(),
152
+ ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
153
+ });
154
+ }