@deftai/directive-core 0.102.0 → 0.104.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 (74) hide show
  1. package/dist/authz/classify.js +167 -4
  2. package/dist/check/gate-lists.d.ts +3 -2
  3. package/dist/check/gate-lists.js +5 -2
  4. package/dist/check/index.d.ts +1 -0
  5. package/dist/check/index.js +1 -0
  6. package/dist/check/session-completed-ac.d.ts +39 -0
  7. package/dist/check/session-completed-ac.js +198 -0
  8. package/dist/doctor/main.js +12 -7
  9. package/dist/fs/contained-write.js +2 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +1 -0
  12. package/dist/init-deposit/hygiene.d.ts +7 -1
  13. package/dist/init-deposit/hygiene.js +11 -3
  14. package/dist/init-deposit/scaffold.d.ts +12 -0
  15. package/dist/init-deposit/scaffold.js +81 -8
  16. package/dist/intake/clause-derivation.d.ts +37 -0
  17. package/dist/intake/clause-derivation.js +207 -0
  18. package/dist/intake/index.d.ts +1 -0
  19. package/dist/intake/index.js +1 -0
  20. package/dist/intake/issue-ingest.js +2 -38
  21. package/dist/preflight/evaluate.d.ts +5 -0
  22. package/dist/preflight/evaluate.js +14 -0
  23. package/dist/preflight/index.d.ts +1 -1
  24. package/dist/product-first-done-gate/empty-resolution.d.ts +1 -1
  25. package/dist/product-first-done-gate/empty-resolution.js +1 -1
  26. package/dist/product-first-done-gate/evaluate.d.ts +29 -0
  27. package/dist/product-first-done-gate/evaluate.js +110 -23
  28. package/dist/product-first-done-gate/index.d.ts +1 -1
  29. package/dist/product-first-done-gate/index.js +1 -1
  30. package/dist/run-summary/emit.d.ts +42 -2
  31. package/dist/run-summary/emit.js +250 -25
  32. package/dist/run-summary/index.d.ts +1 -1
  33. package/dist/run-summary/index.js +1 -1
  34. package/dist/run-summary/share.d.ts +1 -1
  35. package/dist/run-summary/share.js +2 -2
  36. package/dist/run-summary/types.d.ts +10 -3
  37. package/dist/scope/acceptance-activate-gate.d.ts +1 -1
  38. package/dist/scope/acceptance-activate-gate.js +4 -4
  39. package/dist/scope/acceptance-evidence.d.ts +15 -0
  40. package/dist/scope/acceptance-evidence.js +39 -0
  41. package/dist/scope/capacity-stamp.d.ts +2 -0
  42. package/dist/scope/capacity-stamp.js +4 -0
  43. package/dist/scope/delivery-evidence.d.ts +6 -0
  44. package/dist/scope/delivery-evidence.js +17 -0
  45. package/dist/scope/transition.js +66 -4
  46. package/dist/session/ceremony-dial-evidence.d.ts +42 -0
  47. package/dist/session/ceremony-dial-evidence.js +194 -0
  48. package/dist/session/index.d.ts +1 -0
  49. package/dist/session/index.js +1 -0
  50. package/dist/session/session-start.js +21 -2
  51. package/dist/slice/existing.js +12 -1
  52. package/dist/slice/record.d.ts +8 -5
  53. package/dist/slice/record.js +21 -13
  54. package/dist/telemetry-coverage/evaluate.d.ts +42 -0
  55. package/dist/telemetry-coverage/evaluate.js +148 -0
  56. package/dist/telemetry-coverage/fake-trial.d.ts +45 -0
  57. package/dist/telemetry-coverage/fake-trial.js +194 -0
  58. package/dist/telemetry-coverage/index.d.ts +5 -0
  59. package/dist/telemetry-coverage/index.js +5 -0
  60. package/dist/telemetry-coverage/kinds.d.ts +19 -0
  61. package/dist/telemetry-coverage/kinds.js +52 -0
  62. package/dist/telemetry-coverage/scan-callers.d.ts +25 -0
  63. package/dist/telemetry-coverage/scan-callers.js +168 -0
  64. package/dist/vbrief-reconcile/index.d.ts +2 -0
  65. package/dist/vbrief-reconcile/index.js +1 -0
  66. package/dist/vbrief-reconcile/origin-freshness.d.ts +44 -0
  67. package/dist/vbrief-reconcile/origin-freshness.js +221 -0
  68. package/dist/verify-ac/evaluate.d.ts +18 -0
  69. package/dist/verify-ac/evaluate.js +24 -2
  70. package/dist/verify-ac/index.d.ts +1 -1
  71. package/dist/verify-ac/index.js +1 -1
  72. package/dist/verify-source/index.d.ts +1 -0
  73. package/dist/verify-source/index.js +1 -0
  74. package/package.json +3 -3
@@ -8,7 +8,8 @@
8
8
  * floor, empty resolution is soft_empty — not a green run (#3334).
9
9
  */
10
10
  import { existsSync, readFileSync } from "node:fs";
11
- import { basename, resolve } from "node:path";
11
+ import { basename, isAbsolute, relative, resolve } from "node:path";
12
+ import { emitAcceptanceStampFromPlan } from "../intake/clause-derivation.js";
12
13
  import { evaluateLiteralAcceptanceFromPlan, runLiteralAcceptanceCommands, } from "../literal-acceptance/index.js";
13
14
  import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
14
15
  import { maybeBankOnAcPass } from "../session/ac-pass-banking.js";
@@ -26,6 +27,12 @@ function asRecord(value) {
26
27
  * Evaluate product AC from an in-memory plan.
27
28
  */
28
29
  export function evaluateVerifyAcFromPlan(plan, options = {}) {
30
+ const planId = typeof plan.id === "string" && plan.id.trim() ? plan.id.trim() : null;
31
+ const optionsWithScope = {
32
+ ...options,
33
+ oracleScopeKey: options.oracleScopeKey?.trim() || planId || null,
34
+ observedAcceptance: options.observedAcceptance !== undefined ? options.observedAcceptance : plan.acceptance,
35
+ };
29
36
  const acceptance = readPlanAcceptance(plan);
30
37
  const schemaErrors = validatePlanAcceptance(plan.acceptance ?? acceptance);
31
38
  // Only hard-fail schema when an explicit plan.acceptance object exists.
@@ -41,9 +48,9 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
41
48
  acceptance,
42
49
  resolution: "config",
43
50
  resolvedCommandCount: 0,
44
- }, options);
51
+ }, optionsWithScope);
45
52
  }
46
- const projectRoot = resolve(options.projectRoot ?? process.cwd());
53
+ const projectRoot = resolve(optionsWithScope.projectRoot ?? process.cwd());
47
54
  // Prefer shared literal-acceptance path so safety / promotion rules stay one place.
48
55
  // Empty plan.acceptance.commands still consults the #3267 rejected ledger
49
56
  // (Greptile P1: rejected stated AC must never soft-pass).
@@ -51,12 +58,13 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
51
58
  // if the literal ledger is empty of executables.
52
59
  const base = evaluateLiteralAcceptanceFromPlan(plan, {
53
60
  projectRoot,
54
- runner: options.runner,
61
+ runner: optionsWithScope.runner,
55
62
  // Check composition uses the stamped ledger only. Re-scanning issue prose
56
63
  // during `task check` re-captures backtick `verify:ac` lines as rejected
57
64
  // and deadlocks the graph (#3323 / #3284 check-integrated).
58
- captureFromNarratives: options.captureFromNarratives ?? (options.checkIntegrated === true ? false : undefined),
59
- quiet: options.quiet,
65
+ captureFromNarratives: optionsWithScope.captureFromNarratives ??
66
+ (optionsWithScope.checkIntegrated === true ? false : undefined),
67
+ quiet: optionsWithScope.quiet,
60
68
  });
61
69
  // When literal path had nothing executable but plan.acceptance has derived commands,
62
70
  // run them directly with source=explicit semantics.
@@ -64,7 +72,7 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
64
72
  base.runs.length === 0 &&
65
73
  acceptance.commands.length > 0 &&
66
74
  (acceptance.source_rung === "derived" || acceptance.source_rung === "project_floor")) {
67
- const runner = options.runner;
75
+ const runner = optionsWithScope.runner;
68
76
  const direct = runLiteralAcceptanceCommands(acceptance.commands.map((c) => ({
69
77
  command: c.command,
70
78
  cwd: c.cwd ?? null,
@@ -72,17 +80,21 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
72
80
  expectedExitCode: c.expectedExitCode ?? 0,
73
81
  source: "explicit",
74
82
  sourceSpan: "plan.acceptance.commands",
75
- })), { projectRoot, runner, allowTaskStatement: options.allowTaskStatement });
76
- return applyOracle(annotate(direct, acceptance, options.quiet), options);
83
+ })), {
84
+ projectRoot,
85
+ runner,
86
+ allowTaskStatement: optionsWithScope.allowTaskStatement,
87
+ });
88
+ return applyOracle(annotate(direct, acceptance, optionsWithScope.quiet), optionsWithScope);
77
89
  }
78
90
  // Check composition: mid-story unpromoted capture-only may soft-pass so the
79
91
  // framework graph is not deadlocked before agents promote peers.
80
92
  // Greptile P1 #3284: safety-rejected stated commands NEVER soft-pass — they
81
93
  // block product verification until a safe alternative is promoted.
82
- if (options.checkIntegrated === true && !base.ok && base.runs.length === 0) {
94
+ if (optionsWithScope.checkIntegrated === true && !base.ok && base.runs.length === 0) {
83
95
  const hasRejected = (base.rejected?.length ?? 0) > 0;
84
96
  if (hasRejected) {
85
- return applyOracle(annotate(base, acceptance, options.quiet), options);
97
+ return applyOracle(annotate(base, acceptance, optionsWithScope.quiet), optionsWithScope);
86
98
  }
87
99
  const unpromoted = /capture-only|task_statement|no matching agent-promoted/i.test(base.message) ||
88
100
  (base.commands.length > 0 && base.commands.every((c) => c.source === "task_statement"));
@@ -90,7 +102,7 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
90
102
  return applyOracle({
91
103
  ok: true,
92
104
  code: 0,
93
- message: options.quiet
105
+ message: optionsWithScope.quiet
94
106
  ? ""
95
107
  : `verify:ac advisory (#3284 check-integrated): no executable AC peers yet ` +
96
108
  `(capture-only / empty). Done-gate standalone verify:ac still requires promotion. ` +
@@ -110,10 +122,10 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
110
122
  rejectedCount: base.rejected?.length ?? 0,
111
123
  }),
112
124
  resolvedCommandCount: base.commands.length,
113
- }, options);
125
+ }, optionsWithScope);
114
126
  }
115
127
  }
116
- return applyOracle(annotate(base, acceptance, options.quiet), options);
128
+ return applyOracle(annotate(base, acceptance, optionsWithScope.quiet), optionsWithScope);
117
129
  }
118
130
  function classifyResolution(input) {
119
131
  if (input.resolution !== undefined) {
@@ -171,9 +183,11 @@ function acceptanceOutcomeOf(result) {
171
183
  return "soft_empty";
172
184
  if (result.resolution === "fail")
173
185
  return "fail";
174
- return null;
186
+ if (result.resolution === "config")
187
+ return "config-error";
188
+ return "soft-missing";
175
189
  }
176
- function emitAcceptanceOutcome(result, options, projectRoot) {
190
+ function emitAcceptanceObservedStamp(options, projectRoot) {
177
191
  if (options.env === undefined) {
178
192
  return;
179
193
  }
@@ -181,8 +195,14 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
181
195
  if (dest === undefined || dest.trim().length === 0) {
182
196
  return;
183
197
  }
184
- const outcome = acceptanceOutcomeOf(result);
185
- if (outcome === null) {
198
+ emitAcceptanceStampFromPlan(projectRoot, { acceptance: options.observedAcceptance }, options.env);
199
+ }
200
+ function emitAcceptanceOutcome(result, options, projectRoot) {
201
+ if (options.env === undefined) {
202
+ return;
203
+ }
204
+ const dest = options.env[ENV_RUN_SUMMARY_PATH];
205
+ if (dest === undefined || dest.trim().length === 0) {
186
206
  return;
187
207
  }
188
208
  try {
@@ -194,7 +214,7 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
194
214
  });
195
215
  emitter.emitAcceptance({
196
216
  resolved_command_count: result.resolvedCommandCount,
197
- outcome,
217
+ outcome: acceptanceOutcomeOf(result),
198
218
  source_rung: result.sourceRung,
199
219
  none_stated: result.noneStated,
200
220
  clause_count: result.clauseOutcomes?.length,
@@ -208,6 +228,43 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
208
228
  // fail-open
209
229
  }
210
230
  }
231
+ /** Outcome on every terminal path; stamp from observed plan.acceptance (#3355). */
232
+ function emitAcceptanceTelemetry(result, options, projectRoot) {
233
+ emitAcceptanceObservedStamp(options, projectRoot);
234
+ emitAcceptanceOutcome(result, options, projectRoot);
235
+ }
236
+ /**
237
+ * CLI-only early returns that never reach evaluate still emit an acceptance
238
+ * outcome so field streams see config-error / soft-missing (#3355).
239
+ */
240
+ export function emitVerifyAcTerminalOutcome(input) {
241
+ emitAcceptanceOutcome({
242
+ ok: input.outcome !== "config-error" && input.outcome !== "fail",
243
+ code: input.outcome === "config-error" ? 2 : input.outcome === "fail" ? 1 : 0,
244
+ message: "",
245
+ commands: [],
246
+ runs: [],
247
+ sourceRung: input.sourceRung ?? "project_floor",
248
+ noneStated: input.noneStated ?? true,
249
+ acceptance: {
250
+ commands: [],
251
+ none_stated: input.noneStated ?? true,
252
+ source_rung: input.sourceRung ?? "project_floor",
253
+ },
254
+ resolution: input.outcome === "config-error"
255
+ ? "config"
256
+ : input.outcome === "soft-missing"
257
+ ? "skipped"
258
+ : input.outcome === "fail"
259
+ ? "fail"
260
+ : input.outcome === "soft_empty"
261
+ ? "soft_empty"
262
+ : input.outcome === "verified-pass"
263
+ ? "verified-pass"
264
+ : "empty-pass",
265
+ resolvedCommandCount: 0,
266
+ }, { projectRoot: input.projectRoot, env: input.env }, resolve(input.projectRoot));
267
+ }
211
268
  function applyClauseWalk(result, options) {
212
269
  const clauses = result.acceptance.clauses ?? [];
213
270
  if (clauses.length === 0 || result.resolution === "config" || result.resolution === "skipped") {
@@ -242,6 +299,7 @@ function applyOracle(result, options) {
242
299
  projectRoot,
243
300
  runs: gated.runs,
244
301
  env: options.env,
302
+ scopeKey: options.oracleScopeKey,
245
303
  });
246
304
  }
247
305
  let next = gated;
@@ -257,7 +315,7 @@ function applyOracle(result, options) {
257
315
  }
258
316
  }
259
317
  if (options.skipAcceptanceEmit !== true) {
260
- emitAcceptanceOutcome(next, options, projectRoot);
318
+ emitAcceptanceTelemetry(next, options, projectRoot);
261
319
  }
262
320
  return next;
263
321
  }
@@ -324,11 +382,40 @@ export function evaluateVerifyAcFromPath(xbriefPath, options = {}) {
324
382
  if (plan === null) {
325
383
  return applyOracle(configResult(`verify:ac: xBRIEF missing plan object: ${abs}`), options);
326
384
  }
327
- const result = evaluateVerifyAcFromPlan(plan, { ...options, skipAcceptanceEmit: true });
328
- const banked = maybeAttachAcPassBank(result, plan, abs, options);
329
- emitAcceptanceOutcome(banked, options, resolve(options.projectRoot ?? process.cwd()));
385
+ const projectRoot = resolve(options.projectRoot ?? process.cwd());
386
+ // Path-relative keys stay unique across xbrief/ vs vbrief/ and duplicate plan.id (#3337 Greptile).
387
+ const oracleScopeKey = options.oracleScopeKey?.trim() || resolveOracleScopeKey(plan, abs, projectRoot);
388
+ const emitOptions = {
389
+ ...options,
390
+ oracleScopeKey,
391
+ observedAcceptance: plan.acceptance,
392
+ };
393
+ const result = evaluateVerifyAcFromPlan(plan, {
394
+ ...emitOptions,
395
+ skipAcceptanceEmit: true,
396
+ });
397
+ const banked = maybeAttachAcPassBank(result, plan, abs, emitOptions);
398
+ emitAcceptanceTelemetry(banked, emitOptions, projectRoot);
330
399
  return banked;
331
400
  }
401
+ /**
402
+ * Unique product-oracle scope key for one active xBRIEF path (#3337).
403
+ * Relative path is always unique across active roots; plan.id alone is not
404
+ * (duplicate ids / same stem in xbrief+vbrief). Prefer `id@relPath` when both exist.
405
+ */
406
+ export function resolveOracleScopeKey(plan, xbriefPath, projectRoot) {
407
+ const abs = resolve(xbriefPath);
408
+ const root = resolve(projectRoot);
409
+ let rel = relative(root, abs).replace(/\\/g, "/");
410
+ if (rel.length === 0 || rel.startsWith("..") || isAbsolute(rel)) {
411
+ rel = basename(abs);
412
+ }
413
+ const planId = typeof plan.id === "string" && plan.id.trim() ? plan.id.trim() : null;
414
+ if (planId !== null) {
415
+ return `${planId}@${rel}`;
416
+ }
417
+ return rel;
418
+ }
332
419
  /**
333
420
  * After executable AC pass, FINALIZE the banking checkpoint (#3285).
334
421
  * Soft/advisory passes with zero runs do not bank. Fail-open on ledger errors.
@@ -7,6 +7,6 @@
7
7
  export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
8
8
  export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, type ProductFirstCheckModeResolution, type ResolveProductFirstCheckModeInput, resolveProductFirstCheckMode, } from "./check-mode.js";
9
9
  export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, type VerifyAcResolution, } from "./empty-resolution.js";
10
- export { type EvaluateVerifyAcOptions, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, type VerifyAcResult, } from "./evaluate.js";
10
+ export { type EvaluateVerifyAcOptions, emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, type VerifyAcResult, } from "./evaluate.js";
11
11
  export { type AcceptanceCommand, type AcSourceRung, ENV_CHECK_AC_ONLY, ENV_CHECK_MODE, ENV_HYGIENE_ADVISORY, HYGIENE_GATE_ID_PREFIXES, PLAN_ACCEPTANCE_KEY, type PlanAcceptance, PRODUCT_AC_GATE_ID, type ProductFirstCheckMode, } from "./types.js";
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -7,6 +7,6 @@
7
7
  export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
8
8
  export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, resolveProductFirstCheckMode, } from "./check-mode.js";
9
9
  export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, } from "./empty-resolution.js";
10
- export { evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, } from "./evaluate.js";
10
+ export { emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, } from "./evaluate.js";
11
11
  export { ENV_CHECK_AC_ONLY, ENV_CHECK_MODE, ENV_HYGIENE_ADVISORY, HYGIENE_GATE_ID_PREFIXES, PLAN_ACCEPTANCE_KEY, PRODUCT_AC_GATE_ID, } from "./types.js";
12
12
  //# sourceMappingURL=index.js.map
@@ -25,8 +25,27 @@ export interface EmitRunSummaryResult {
25
25
  readonly line: RunSummaryLine | null;
26
26
  readonly warning: boolean;
27
27
  }
28
+ /** Owner token stored in `.seq.lock` so reclaim is not age-based (#3361). */
29
+ export interface SeqLockOwner {
30
+ readonly pid: number;
31
+ readonly nonce: string;
32
+ }
33
+ /**
34
+ * Reclaim `.seq.lock` only when the owner process is dead or the token is
35
+ * missing/unreadable/corrupt (unknown owner). Live holders are never stolen
36
+ * by mtime (#3361).
37
+ */
38
+ export declare function tryReclaimSeqLock(lockPath: string): boolean;
39
+ /**
40
+ * Unlink `.seq.lock` only when the on-disk token still matches this holder
41
+ * (ownership-blind release must not delete a successor's lock).
42
+ */
43
+ export declare function releaseSeqLockIfOwner(lockPath: string, owner: SeqLockOwner): boolean;
28
44
  /**
29
- * Stateful emitter: one instance per session tracks seq and write-warning once.
45
+ * Stateful emitter: one instance tracks seq and write-warning once.
46
+ * File destinations seed seq from the current JSONL line count so multiple
47
+ * CLI processes appending to one DEFT_RUN_SUMMARY_PATH share 1..N (#3350).
48
+ * Stdout destinations stay per-process (each constructor starts at 0).
30
49
  */
31
50
  export declare class RunSummaryEmitter {
32
51
  private seq;
@@ -41,6 +60,7 @@ export declare class RunSummaryEmitter {
41
60
  private readonly writeStderr;
42
61
  constructor(options: RunSummaryEmitterOptions);
43
62
  getDestination(): RunSummaryDestination;
63
+ private buildLine;
44
64
  emit(event: RunSummaryEventKind, payload: RunSummaryPayload): EmitRunSummaryResult;
45
65
  emitSessionStart(payload: SessionStartRunSummaryPayload): EmitRunSummaryResult;
46
66
  emitDialTransition(payload: DialTransitionRunSummaryPayload): EmitRunSummaryResult;
@@ -52,12 +72,32 @@ export declare class RunSummaryEmitter {
52
72
  emitAcceptanceStamp(payload: AcceptanceStampRunSummaryPayload): EmitRunSummaryResult;
53
73
  /** Emit the harness-supplied denominator when DEFT_TOTAL_TOOL_TURNS is set. */
54
74
  emitKnownToolTurnDenominator(): EmitRunSummaryResult;
75
+ /**
76
+ * Always emit a session denominator when the destination is live (#3356).
77
+ * `emitKnownToolTurnDenominator` stays silent unless DEFT_TOTAL_TOOL_TURNS is
78
+ * set; this caller records host planned turns or the session:start CLI floor.
79
+ */
80
+ emitSessionToolTurnDenominator(hostMaxTurns?: number | null): EmitRunSummaryResult;
55
81
  }
56
82
  /** Parse harness-supplied DEFT_TOTAL_TOOL_TURNS (integer > 0). Invalid/unset → omit. */
57
83
  export declare function readEnvToolTurnDenominator(env: NodeJS.ProcessEnv): number | undefined;
84
+ /** Canonical host planned-turn budget recorded at session:start (#3356). */
85
+ export declare const ENV_MAX_TURNS_DENOMINATOR = "DEFT_MAX_TURNS";
86
+ /** session:start is one CLI invocation when no host/harness count is known (#3356). */
87
+ export declare const SESSION_START_CLI_INVOCATION_DENOMINATOR: 1;
88
+ /**
89
+ * Resolve the session tool/turn denominator (#3356).
90
+ *
91
+ * Prefer harness actuals (`DEFT_TOTAL_TOOL_TURNS`), then a host planned-turn
92
+ * budget (`DEFT_MAX_TURNS` or `hostMaxTurns` from the session:start descriptor),
93
+ * else this CLI invocation (1). An emitted proxy beats a perfect unused kind.
94
+ */
95
+ export declare function resolveSessionToolTurnDenominator(env: NodeJS.ProcessEnv, hostMaxTurns?: number | null): number;
58
96
  /**
59
97
  * One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
60
- * Still fail-open; creates a fresh seq=1 emitter unless `seqSeed` is passed via reuse.
98
+ * Still fail-open. File destinations seed seq from the existing JSONL line
99
+ * count so successive one-shot calls into the same path continue 1..N (#3350).
100
+ * Stdout destinations stay per-process (each call starts at seq=1).
61
101
  */
62
102
  export declare function emitRunSummaryEvent(options: RunSummaryEmitterOptions & {
63
103
  readonly event: RunSummaryEventKind;