@gajae-code/coding-agent 0.12.4 → 0.12.6

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 (139) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +4 -1
  3. package/dist/types/async/job-manager.d.ts +9 -1
  4. package/dist/types/eval/backend.d.ts +2 -2
  5. package/dist/types/exec/bash-executor.d.ts +10 -8
  6. package/dist/types/extensibility/extensions/runner.d.ts +9 -7
  7. package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
  8. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  9. package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
  10. package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
  11. package/dist/types/gjc-runtime/team-store.d.ts +0 -4
  12. package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
  13. package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
  14. package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
  15. package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
  16. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
  17. package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +83 -0
  18. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +57 -4
  19. package/dist/types/internal-urls/registry-helpers.d.ts +18 -0
  20. package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
  21. package/dist/types/modes/components/plugin-settings.d.ts +2 -0
  22. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  23. package/dist/types/sdk/broker/broker.d.ts +18 -0
  24. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
  25. package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
  26. package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -2
  27. package/dist/types/sdk/bus/index.d.ts +4 -0
  28. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -3
  29. package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
  30. package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
  31. package/dist/types/sdk/host/host.d.ts +23 -2
  32. package/dist/types/sdk/host/reverse-leases.d.ts +7 -4
  33. package/dist/types/session/agent-session.d.ts +5 -4
  34. package/dist/types/session/artifacts.d.ts +8 -5
  35. package/dist/types/session/attempt-record-store.d.ts +19 -0
  36. package/dist/types/session/client-bridge.d.ts +14 -0
  37. package/dist/types/session/internal/managed-session-storage.d.ts +7 -23
  38. package/dist/types/session/messages.d.ts +4 -1
  39. package/dist/types/session/session-manager.d.ts +7 -5
  40. package/dist/types/session/session-storage.d.ts +8 -0
  41. package/dist/types/session/streaming-output.d.ts +32 -0
  42. package/dist/types/task/executor.d.ts +1 -1
  43. package/dist/types/task/index.d.ts +2 -1
  44. package/dist/types/task/receipt.d.ts +3 -0
  45. package/dist/types/task/types.d.ts +194 -0
  46. package/dist/types/task/worktree.d.ts +13 -6
  47. package/dist/types/tools/bash.d.ts +1 -1
  48. package/dist/types/tools/fetch.d.ts +3 -0
  49. package/dist/types/tools/output-meta.d.ts +37 -5
  50. package/dist/types/tools/subagent.d.ts +2 -0
  51. package/dist/types/tools/tool-result.d.ts +1 -0
  52. package/dist/types/utils/artifact-id.d.ts +2 -0
  53. package/dist/types/utils/git.d.ts +1 -0
  54. package/package.json +8 -8
  55. package/scripts/capture-sticky-viewport-showcase.ts +131 -5
  56. package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
  57. package/scripts/verify-sticky-viewport-showcase.ts +620 -93
  58. package/src/async/job-manager.ts +13 -5
  59. package/src/config/model-profiles.ts +8 -0
  60. package/src/config/settings-schema.ts +17 -3
  61. package/src/cursor.ts +20 -13
  62. package/src/defaults/gjc/skills/ultragoal/SKILL.md +5 -2
  63. package/src/eval/backend.ts +2 -2
  64. package/src/exec/bash-executor.ts +425 -151
  65. package/src/extensibility/extensions/runner.ts +80 -6
  66. package/src/extensibility/extensions/wrapper.ts +22 -15
  67. package/src/extensibility/runtime-skill-discovery.ts +4 -6
  68. package/src/gjc-runtime/launch-tmux.ts +309 -293
  69. package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
  70. package/src/gjc-runtime/psmux-detect.ts +52 -6
  71. package/src/gjc-runtime/team-launch.ts +50 -2
  72. package/src/gjc-runtime/team-runtime.ts +716 -251
  73. package/src/gjc-runtime/team-store.ts +25 -7
  74. package/src/gjc-runtime/team-workers.ts +161 -1
  75. package/src/gjc-runtime/tmux-common.ts +16 -4
  76. package/src/gjc-runtime/tmux-gc.ts +37 -5
  77. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
  78. package/src/gjc-runtime/tmux-provider-context.ts +451 -0
  79. package/src/gjc-runtime/tmux-sessions.ts +409 -156
  80. package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
  81. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
  82. package/src/gjc-runtime/ultragoal-review-source.ts +378 -0
  83. package/src/gjc-runtime/ultragoal-runtime.ts +640 -159
  84. package/src/gjc-runtime/workflow-manifest.generated.json +40 -1
  85. package/src/gjc-runtime/workflow-manifest.ts +12 -0
  86. package/src/internal-urls/agent-protocol.ts +98 -38
  87. package/src/internal-urls/artifact-protocol.ts +28 -17
  88. package/src/internal-urls/docs-index.generated.ts +6 -6
  89. package/src/internal-urls/local-protocol.ts +39 -18
  90. package/src/internal-urls/registry-helpers.ts +62 -0
  91. package/src/modes/acp/acp-agent.ts +51 -13
  92. package/src/modes/acp/acp-client-bridge.ts +154 -11
  93. package/src/modes/components/gjc-bundle-settings.ts +2 -0
  94. package/src/modes/components/plugin-settings.ts +5 -0
  95. package/src/modes/components/settings-selector.ts +4 -0
  96. package/src/modes/controllers/selector-controller.ts +1 -0
  97. package/src/prompts/tools/bash.md +2 -2
  98. package/src/prompts/tools/monitor.md +4 -4
  99. package/src/sdk/acp/adapter.ts +200 -9
  100. package/src/sdk/broker/broker.ts +93 -10
  101. package/src/sdk/broker/lifecycle-ledger.ts +227 -11
  102. package/src/sdk/broker/lifecycle.ts +692 -98
  103. package/src/sdk/bus/chat-daemon-control.ts +33 -5
  104. package/src/sdk/bus/index.ts +347 -14
  105. package/src/sdk/bus/telegram-daemon-contract.ts +7 -3
  106. package/src/sdk/bus/telegram-daemon.ts +283 -44
  107. package/src/sdk/bus/topic-registry.ts +133 -10
  108. package/src/sdk/host/host.ts +187 -9
  109. package/src/sdk/host/reverse-leases.ts +249 -42
  110. package/src/sdk/session.ts +8 -6
  111. package/src/session/agent-session.ts +930 -354
  112. package/src/session/artifacts.ts +211 -74
  113. package/src/session/attempt-record-store.ts +106 -0
  114. package/src/session/client-bridge.ts +15 -0
  115. package/src/session/internal/managed-session-scope.ts +332 -198
  116. package/src/session/internal/managed-session-storage.ts +162 -450
  117. package/src/session/messages.ts +32 -7
  118. package/src/session/session-manager.ts +31 -28
  119. package/src/session/session-storage.ts +190 -53
  120. package/src/session/streaming-output.ts +198 -40
  121. package/src/task/executor.ts +5 -2
  122. package/src/task/index.ts +277 -64
  123. package/src/task/provider-retry-status.ts +4 -1
  124. package/src/task/receipt.ts +26 -2
  125. package/src/task/render.ts +42 -1
  126. package/src/task/types.ts +52 -0
  127. package/src/task/worktree.ts +122 -43
  128. package/src/tools/bash.ts +818 -239
  129. package/src/tools/eval.ts +99 -4
  130. package/src/tools/fetch.ts +77 -15
  131. package/src/tools/find.ts +7 -3
  132. package/src/tools/monitor.ts +7 -1
  133. package/src/tools/output-meta.ts +706 -116
  134. package/src/tools/search.ts +9 -4
  135. package/src/tools/subagent-render.ts +5 -2
  136. package/src/tools/subagent.ts +8 -0
  137. package/src/tools/tool-result.ts +1 -0
  138. package/src/utils/artifact-id.ts +6 -0
  139. package/src/utils/git.ts +2 -0
@@ -3,6 +3,7 @@ import { type RepositoryBinding } from "./repository-binding";
3
3
  import { type CriticVerdict } from "./ultragoal-receipt-freshness";
4
4
  export { CRITIC_GATE_HARD_STOP_EVENT, CRITIC_GATE_OVERRIDE_EVENT, CRITIC_VERDICT_EVENT, type CriticVerdict, computeUltragoalPlanGeneration, countTerminalCriticVerdicts, receiptRelevantGoals, TERMINAL_CRITIC_CEILING, terminalCriticCeilingReached, terminalCriticGateOverridden, } from "./ultragoal-receipt-freshness";
5
5
  export { captureUltragoalRecoverySnapshot, parseStrictTerminalTranscript, persistUltragoalRecoveryDecision, planUltragoalOwnerLossRecovery, type UltragoalOwnerLossReceipt, type UltragoalRecoveryBinding, type UltragoalRecoveryDecision, type UltragoalRecoverySnapshot, validateOwnerLossBinding, validateRawUltragoalEvidence, validateRecoveryAdmission, validateRecoveryPath, } from "./ultragoal-owner-loss-recovery";
6
+ import { createReviewSourceDispatch, type ReviewDeliveryDisposition, type ReviewSourceCohort, type ReviewSourceLane } from "./ultragoal-review-source";
6
7
  export type UltragoalGjcGoalMode = "aggregate" | "per-story";
7
8
  export type UltragoalGoalStatus = "pending" | "active" | "complete" | "failed" | "blocked" | "review_blocked" | "superseded";
8
9
  export interface UltragoalValidationBatchMetadata extends JsonObject {
@@ -42,6 +43,7 @@ export interface UltragoalPlan {
42
43
  goals: UltragoalGoal[];
43
44
  /** Authoritative repository identity for multi-repo fail-closed spawn (#2901). */
44
45
  repositoryBinding?: RepositoryBinding;
46
+ reviewCohorts?: ReviewSourceCohort[];
45
47
  createdAt: string;
46
48
  updatedAt: string;
47
49
  [key: string]: unknown;
@@ -303,11 +305,12 @@ export interface UltragoalChangeSet extends JsonObject {
303
305
  paths: UltragoalChangeSetPath[];
304
306
  rawDiffStat?: string;
305
307
  rawDiff?: string;
308
+ captureIncomplete?: boolean;
306
309
  trusted: true;
307
310
  }
308
311
  export declare function normalizeRepoPath(value: string): string;
312
+ export declare function normalizeChangeSetPath(value: string): string;
309
313
  export declare function categorizeComputerChangePath(value: string): UltragoalChangeCategory;
310
- export declare function isSettingsSchemaPath(value: string): boolean;
311
314
  export declare function normalizeSurfaceToken(value: string): string;
312
315
  export declare function surfaceFamily(value: string): SurfaceFamily;
313
316
  export declare function isLiveSurfaceFamily(family: SurfaceFamily): boolean;
@@ -374,13 +377,50 @@ export declare function addUltragoalSubgoal(input: {
374
377
  evidence: string;
375
378
  rationale: string;
376
379
  }): Promise<UltragoalPlan>;
380
+ export declare function validateUltragoalReviewDispatch(input: {
381
+ cwd: string;
382
+ dispatchId: string;
383
+ cohortId: string;
384
+ taskId: string;
385
+ lane: ReviewSourceLane;
386
+ snapshotId: string;
387
+ generation: number;
388
+ repositoryBindingDigest: string;
389
+ stateRevision: number;
390
+ rerunCommand: string;
391
+ taskSourceTaskId: string;
392
+ createdAt: string;
393
+ }): Promise<void>;
394
+ export declare function freezeUltragoalReviewCohort(input: {
395
+ cwd: string;
396
+ workflow?: "ultragoal" | "ralplan";
397
+ }): Promise<ReviewSourceCohort>;
398
+ export declare function dispatchUltragoalReviewLane(input: {
399
+ cwd: string;
400
+ cohortId: string;
401
+ taskId: string;
402
+ lane: ReviewSourceLane;
403
+ rerunCommand: string;
404
+ }): Promise<ReturnType<typeof createReviewSourceDispatch>>;
405
+ export declare function classifyUltragoalReviewDelivery(input: {
406
+ cwd: string;
407
+ cohortId: string;
408
+ dispatchId: string;
409
+ observedDisposition: ReviewDeliveryDisposition;
410
+ }): Promise<{
411
+ disposition: ReviewDeliveryDisposition;
412
+ deliveryId: string;
413
+ }>;
377
414
  export declare function recordUltragoalReviewBlockers(input: {
378
415
  cwd: string;
379
416
  goalId: string;
380
417
  title: string;
381
418
  objective: string;
382
419
  evidence: string;
383
- }): Promise<UltragoalPlan>;
420
+ }): Promise<{
421
+ plan: UltragoalPlan;
422
+ blockerGoalId: string;
423
+ }>;
384
424
  export type UltragoalBlockerClassification = "human_blocked" | "resolvable";
385
425
  /**
386
426
  * Record an audited blocker triage classification in the durable ledger. Pause
@@ -422,7 +462,20 @@ interface UltragoalReviewResult extends JsonObject {
422
462
  weakContractCapApplied: boolean;
423
463
  blockerGoalIds?: string[];
424
464
  }
425
- import { computeCheckpointChangeSet, parseGitNameStatus, parseUnifiedDiffPaths, resolveGitBase, spawnText } from "./ultragoal-change-set";
426
- export { computeCheckpointChangeSet, parseGitNameStatus, parseUnifiedDiffPaths, resolveGitBase, spawnText };
465
+ import { ciDevChangedPathRows, computeCheckpointChangeSet, mergeChangeSetPaths, parseGitNameStatus, parseGitUntrackedPaths, parseUnifiedDiffPaths, resolveGitBase, spawnText } from "./ultragoal-change-set";
466
+ export { ciDevChangedPathRows, computeCheckpointChangeSet, mergeChangeSetPaths, parseGitNameStatus, parseGitUntrackedPaths, parseUnifiedDiffPaths, resolveGitBase, spawnText, };
467
+ /**
468
+ * Typed terminal handoff thrown when {@link recordUltragoalReviewBlockers} would
469
+ * exceed {@link MAX_REVIEW_BLOCKER_DESCENTS} unresolved review_blocker descents
470
+ * off a single blocked goal (#3613). Never silently marks unresolved technical
471
+ * findings complete; the operator/leader must pause and escalate.
472
+ */
473
+ export declare class UltragoalReviewBlockerRecursionCapError extends Error {
474
+ readonly code: "review_blocker_recursion_cap";
475
+ readonly blockedGoalId: string;
476
+ readonly unresolvedDescents: number;
477
+ readonly cap: number;
478
+ constructor(blockedGoalId: string, unresolvedDescents: number, cap?: number);
479
+ }
427
480
  export declare function runUltragoalReview(cwd: string, args: readonly string[]): Promise<UltragoalReviewResult>;
428
481
  export declare function runNativeUltragoalCommand(args: string[], cwd?: string): Promise<UltragoalCommandResult>;
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Shared helpers for internal-url protocol handlers that resolve session-scoped
3
+ * artifact IDs.
4
+ */
5
+ import { type RecoveryFsRoot } from "@gajae-code/natives";
1
6
  import type { ResolveContext } from "./types";
2
7
  /**
3
8
  * Snapshot of artifacts dirs explicitly authorized for the calling session.
@@ -9,3 +14,16 @@ import type { ResolveContext } from "./types";
9
14
  * not an authorization source.
10
15
  */
11
16
  export declare function authorizedArtifactsDirsFromContext(context?: ResolveContext): string[];
17
+ export interface AuthorizedArtifactsRoot {
18
+ readonly directory: string;
19
+ readonly dev: bigint;
20
+ readonly ino: bigint;
21
+ readonly authority: RecoveryFsRoot;
22
+ }
23
+ export interface AuthorizedArtifactSnapshot {
24
+ bytes: Buffer;
25
+ size: number;
26
+ }
27
+ export declare function openAuthorizedArtifactsRoot(directory: string): AuthorizedArtifactsRoot;
28
+ export declare function closeAuthorizedArtifactsRoot(root: AuthorizedArtifactsRoot): void;
29
+ export declare function readAuthorizedArtifactFile(root: AuthorizedArtifactsRoot, filename: string, maxBytes: number): AuthorizedArtifactSnapshot | null;
@@ -19,6 +19,7 @@ export interface GjcBundleSettingsDependencies {
19
19
  export interface GjcBundleSettingsCallbacks {
20
20
  onClose: () => void;
21
21
  onBundlesChanged?: () => void;
22
+ onRenderRequested?: () => void;
22
23
  }
23
24
  export type GjcBundleSettingsState = "loading" | "error" | "empty" | "list" | "detail" | "unsupported-source" | "update-review" | "update-confirm" | "update-running" | "update-result" | "stale-result" | "quarantined-blocked" | "mutation-in-flight-locked";
24
25
  /** Scope-qualified, lifecycle-backed Settings UI for installed GJC bundles. */
@@ -35,6 +35,7 @@ export interface PluginDetailCallbacks {
35
35
  onFeatureChange: (feature: string, enabled: boolean) => void;
36
36
  onConfigChange: (key: string, value: unknown) => void;
37
37
  onBack: () => void;
38
+ onRenderRequested?: () => void;
38
39
  }
39
40
  /**
40
41
  * Shows detail settings for a single plugin:
@@ -53,6 +54,7 @@ export declare class PluginDetailComponent extends Container {
53
54
  export interface PluginSettingsCallbacks {
54
55
  onClose: () => void;
55
56
  onPluginChanged: () => void;
57
+ onRenderRequested?: () => void;
56
58
  }
57
59
  /**
58
60
  * Top-level plugin settings component.
@@ -67,6 +67,8 @@ export interface SettingsCallbacks {
67
67
  getStatusLinePreview?: (width?: number) => string;
68
68
  /** Called when plugins change */
69
69
  onPluginsChanged?: () => void;
70
+ /** Called when asynchronously rebuilt settings content needs a repaint. */
71
+ onRenderRequested?: () => void;
70
72
  /** Called when an interactive setting cannot be committed. */
71
73
  onError?: (message: string) => void;
72
74
  /** Called when settings panel is closed */
@@ -23,6 +23,7 @@ export type BrokerErrorCode = "idempotency_conflict" | "terminal_uncertain" | "b
23
23
  export type BrokerCleanupIdentity = {
24
24
  dev: string;
25
25
  ino: string;
26
+ nlink?: string;
26
27
  size: number;
27
28
  mtimeNs: string;
28
29
  sha256: string;
@@ -51,6 +52,7 @@ export type BrokerArtifactTree = {
51
52
  };
52
53
  export type BrokerCleanupEvidence = {
53
54
  phase: "artifacts" | "transcript" | "metadata" | "lifecycle";
55
+ cleanupReceiptVersion?: 1;
54
56
  /** Ledger-bound deletion target; never reconstructed from a retry request. */
55
57
  sessionsRoot?: string;
56
58
  transcriptPath?: string;
@@ -59,6 +61,10 @@ export type BrokerCleanupEvidence = {
59
61
  sessionId?: string;
60
62
  artifactsIdentity?: BrokerCleanupIdentity;
61
63
  transcriptIdentity?: BrokerCleanupIdentity;
64
+ transcriptParentIdentity?: {
65
+ dev: string;
66
+ ino: string;
67
+ };
62
68
  /** Identity-bound lifecycle metadata marker retained when exact cleanup is deferred. */
63
69
  metadataIdentity?: BrokerCleanupIdentity;
64
70
  metadataPath?: string;
@@ -71,9 +77,17 @@ export type BrokerCleanupEvidence = {
71
77
  /** Append-only terminal proof for lifecycle metadata cleanup. */
72
78
  metadataCompleted?: true;
73
79
  detachedArtifactsPath?: string;
80
+ retainedArtifactsSuccessorPath?: string;
81
+ retainedArtifactsPlaceholderPath?: string;
82
+ retainedArtifactsUnknownPath?: string;
83
+ retainedArtifactsSideAuthority?: "none" | "retained";
74
84
  detachedTranscriptPath?: string;
85
+ retainedTranscriptSuccessorPath?: string;
86
+ retainedTranscriptPlaceholderPath?: string;
87
+ retainedTranscriptUnknownPath?: string;
75
88
  /** Durable proof that artifact cleanup completed before transcript mutation. */
76
89
  artifactsRemoved?: boolean;
90
+ artifactsAbsentAtAuthorization?: true;
77
91
  /** Preauthorized no-replace artifact quarantine path persisted before detach. */
78
92
  plannedArtifactsPath?: string;
79
93
  /** Identity-bound artifact tree authority persisted before broker detach and replayed exactly. */
@@ -82,6 +96,10 @@ export type BrokerCleanupEvidence = {
82
96
  plannedTranscriptPath?: string;
83
97
  /** Fully identity-bound startup-failure cleanup plan, persisted before any detach. */
84
98
  lifecycleFiles?: BrokerLifecycleCleanupFile[];
99
+ lifecycleParentIdentity?: {
100
+ dev: string;
101
+ ino: string;
102
+ };
85
103
  /** Delete metadata receipts authorize only the canonical marker/ready sibling pair. */
86
104
  lifecycleDeleteMetadata?: true;
87
105
  };
@@ -66,6 +66,11 @@ export interface LifecycleLedgerEntry {
66
66
  endpointGeneration?: number;
67
67
  responseDigest?: string;
68
68
  response?: unknown;
69
+ unresolvedCleanupResponse?: unknown;
70
+ unresolvedCleanupResponseDigest?: string;
71
+ uncertainCleanupSessionId?: string;
72
+ uncertainCleanupSessionIds?: string[];
73
+ uncertainCleanupAllSessions?: true;
69
74
  ts: number;
70
75
  }
71
76
  export type BeginResult = {
@@ -105,5 +110,13 @@ export declare class LifecycleLedger {
105
110
  begin(identity: string, requestHash: string): Promise<BeginResult>;
106
111
  transition(identity: string, state: LifecycleState, fields?: Omit<Partial<LifecycleLedgerEntry>, "identity" | "requestHash" | "state" | "ts">): Promise<LifecycleLedgerEntry>;
107
112
  assertSupportedStateVersions(): Promise<void>;
113
+ findCleanupPendingByDeleteTarget(target: {
114
+ sessionId: string;
115
+ sessionsRoot?: string;
116
+ transcriptPath: string;
117
+ cwd: string;
118
+ }, excludingIdentity: string): LifecycleLedgerEntry | undefined;
119
+ findCleanupPendingBySessionId(sessionId: string, excludingIdentity: string): LifecycleLedgerEntry | undefined;
120
+ hasUncertainCleanupForSession(sessionId: string, excludingIdentity: string): boolean;
108
121
  get(identity: string): LifecycleLedgerEntry | undefined;
109
122
  }
@@ -92,6 +92,7 @@ export declare function readSessionLifecycleFailureForTest(root: string, id: str
92
92
  effectMarker: string;
93
93
  incarnation: string;
94
94
  }): Promise<SdkStartupFailure | undefined>;
95
+ export declare function canonicalDeleteLocatorPath(pathname: string): string;
95
96
  export interface LifecycleExecutionOutcome {
96
97
  response: BrokerResponse;
97
98
  durableEffects?: LifecycleDurableEffectsReceipt;
@@ -19,8 +19,11 @@ export type ChatDaemonAction = "stop" | "reload";
19
19
  * chat daemons after notification configuration parsing changes. Generation 15
20
20
  * applies the current notification configuration directly when starting chat
21
21
  * daemon transports. Generation 16 applies Telegram sound-policy configuration
22
- * through shared notification parsing. Generation 17 binds managed-session
23
- * replacement to exact native filesystem authority.
22
+ * through shared notification parsing. Generation 17 bound managed-session
23
+ * replacement to exact native filesystem authority; generation 18 retired that
24
+ * binding, generation 19 bound exact cleanup to parent/link-count authority,
25
+ * generation 20 refreshed retained cleanup semantics, and generation 21 hardens
26
+ * exact Bash process-tree ownership shared by chat daemon cleanup.
24
27
  */
25
28
  export declare const CHAT_DAEMON_GENERATIONS: Readonly<Record<ChatDaemonKind, number>>;
26
29
  export declare function chatDaemonGeneration(kind: ChatDaemonKind): number;
@@ -17,6 +17,7 @@
17
17
  * Enable with Settings notifications config, `GJC_NOTIFICATIONS=1` (a token is
18
18
  * generated), or `GJC_NOTIFICATIONS_TOKEN`.
19
19
  */
20
+ import { type RunSettlementProof } from "@gajae-code/agent-core";
20
21
  import type { Tool } from "@gajae-code/ai";
21
22
  import { NotificationServer } from "@gajae-code/natives";
22
23
  import { Settings } from "../../config/settings";
@@ -33,6 +34,9 @@ import { NotificationSessionController } from "./session-control";
33
34
  import { type EnsureDaemonResult, type RegisterNotificationRootResult } from "./telegram-daemon";
34
35
  export type { IdentityControlSuccessPathInput, IdentityControlTerminalPathInput, TerminalSendOutcome, } from "./control-drain-lease";
35
36
  export { isNativeControlDrainAvailable, runIdentityControlSuccessPath, runIdentityControlTerminalPath, } from "./control-drain-lease";
37
+ export declare function formatPromptSettlementDiagnostic(proof: Extract<RunSettlementProof, {
38
+ status: "unfenced";
39
+ }>, now?: number): string;
36
40
  /** Where a `session_create` should run. Discriminated by `kind`. */
37
41
  export type SessionCreateTarget = {
38
42
  kind: "existing_path";
@@ -44,10 +44,14 @@ export declare const NOTIFICATION_PROTOCOL_VERSION = 3;
44
44
  * 33 adds action-bound multi-select state rendering and replay-safe option
45
45
  * snapshots. Generation 34 converts non-photo image formats (including WebP)
46
46
  * into Telegram-compatible photo uploads when possible. Generation 35 adds
47
- * user-created topic adoption (forum-topic folder picker). Generation 36 binds
48
- * managed-session replacement to exact native filesystem authority.
47
+ * user-created topic adoption (forum-topic folder picker). Generation 36 bound
48
+ * managed-session replacement to exact native filesystem authority; generation
49
+ * 37 retires that binding (revert of #3489, which stalled POSIX artifact
50
+ * cleanup); generation 38 binds exact cleanup to parent and link-count authority;
51
+ * generation 39 hardens exact Bash process-tree ownership, settlement, and
52
+ * descendant cleanup authority.
49
53
  */
50
- export declare const DAEMON_GENERATION = 36;
54
+ export declare const DAEMON_GENERATION = 39;
51
55
  /**
52
56
  * Serving-compatibility boundary for daemon lifecycle requests. Epoch 1 covers
53
57
  * all builds published before this field existed; epoch 2 covered generation 29;
@@ -61,11 +61,13 @@ export interface TelegramDaemonFs {
61
61
  size?: number;
62
62
  dev?: number;
63
63
  ino?: number;
64
+ /** Hard-link count; required to prove a staging temp has no second name. */
65
+ nlink?: number;
64
66
  ctimeMs?: number;
65
67
  isDirectory?: () => boolean;
66
68
  }>;
67
69
  readEndpointFile?(path: string): Promise<NotificationEndpointFile>;
68
- exactUnlink?(path: string, identity: NotificationEndpointFileIdentity): Promise<NotificationExactUnlinkResult>;
70
+ exactUnlink?(path: string, identity: NotificationEndpointFileIdentity, quarantineName?: string): Promise<NotificationExactUnlinkResult>;
69
71
  }
70
72
  export interface SpawnResult {
71
73
  pid?: number;
@@ -281,11 +283,26 @@ export declare function withNotificationRootRegistryFence(input: {
281
283
  * (#2956). Nothing previously reaped these; they accumulate in the notifications
282
284
  * directory across ownership transitions.
283
285
  */
284
- export declare const NOTIFICATION_LEAK_ARTIFACT_PREFIXES: readonly [".gjc-delete-daemon-transition-", ".gjc-exact-unlink-placeholder-", ".gjc-delete-notification-endpoint-"];
286
+ export declare const NOTIFICATION_LEAK_ARTIFACT_PREFIXES: readonly [".gjc-delete-daemon-transition-", ".gjc-exact-unlink-placeholder-", ".gjc-delete-notification-endpoint-", ".gjc-delete-notification-staging-temp-"];
285
287
  /** Grace window before a leak artifact is reaped (covers in-flight unlinks). */
286
288
  export declare const NOTIFICATION_LEAK_ARTIFACT_GRACE_MS: number;
287
289
  /** True when a path is permanently gone (not a transient I/O blip). */
288
290
  export declare function isPermanentMissingPathError(error: unknown): boolean;
291
+ /** A publication staging claim recovered from an abandoned temp's name. */
292
+ export interface NotificationStagingTempClaim {
293
+ /** Published sibling this temp was staged for. */
294
+ destination: string;
295
+ /** PID of the process that staged it. */
296
+ pid: number;
297
+ /** Wall-clock ms the publisher recorded when it staged the temp. */
298
+ stagedAtMs: number;
299
+ }
300
+ /**
301
+ * Recover the publication claim encoded in a staging temp's name, or
302
+ * `undefined` when `name` is not a staging temp or its PID/timestamp fields are
303
+ * not usable integers. An unparseable claim is never reaped.
304
+ */
305
+ export declare function parseNotificationStagingTemp(name: string): NotificationStagingTempClaim | undefined;
289
306
  export declare function isNotificationLeakArtifactName(name: string): boolean;
290
307
  /**
291
308
  * Drop permanently missing scan roots from the durable registry under the roots
@@ -302,13 +319,16 @@ export declare function pruneMissingNotificationRoots(input: {
302
319
  }>;
303
320
  /**
304
321
  * Reap retained exact-unlink / ownership-transition quarantine files older than
305
- * the grace window from the notifications directory.
322
+ * the grace window from the notifications directory, plus publication staging
323
+ * temps whose publisher is provably dead.
306
324
  */
307
325
  export declare function reapStaleNotificationArtifacts(input: {
308
326
  settings: Settings;
309
327
  fs?: TelegramDaemonFs;
310
328
  now?: () => number;
311
329
  graceMs?: number;
330
+ /** Liveness seam used to prove a staging temp's publisher is dead. */
331
+ pidAlive?: (pid: number) => boolean;
312
332
  }): Promise<{
313
333
  removed: string[];
314
334
  skipped: number;
@@ -322,6 +342,8 @@ export declare function healTelegramDaemonNotificationState(input: {
322
342
  fs?: TelegramDaemonFs;
323
343
  now?: () => number;
324
344
  graceMs?: number;
345
+ /** Liveness seam used to prove a staging temp's publisher is dead. */
346
+ pidAlive?: (pid: number) => boolean;
325
347
  }): Promise<{
326
348
  prunedRoots: string[];
327
349
  removedArtifacts: string[];
@@ -92,6 +92,17 @@ export interface TopicDeleteAuthoritySnapshot {
92
92
  /** Exact fenced record, retained to restore an in-memory tombstone after a failed clear publication. */
93
93
  record?: TopicRecord;
94
94
  }
95
+ /**
96
+ * Proof that a definite delete was settled in memory and still owes its durable
97
+ * commit. Handed back only by a settlement that actually happened, so a refused
98
+ * settlement structurally cannot be followed by a rollback.
99
+ */
100
+ export interface TopicSettledDelete {
101
+ sessionId: string;
102
+ topicId: string;
103
+ /** Authority epoch proven current when the record was removed. */
104
+ settledEpoch: number;
105
+ }
95
106
  export declare function emptyTopicRegistryState(): TopicRegistryState;
96
107
  /**
97
108
  * In-memory registry over a serialisable state. Topic creation is injected via
@@ -199,7 +210,14 @@ export declare class TopicRegistry {
199
210
  captureDeleteAuthority(sessionId: string): TopicDeleteAuthoritySnapshot;
200
211
  /** Restore a failed delete fence only while its exact authority mutation remains current. */
201
212
  restoreDeleteAuthority(snapshot: TopicDeleteAuthoritySnapshot): boolean;
202
- /** Restore the exact delete fence after a failed compensation publication. */
213
+ /**
214
+ * Restore the exact delete fence after a failed compensation publication.
215
+ *
216
+ * Settlement rebuilds derived routes, which can make a surviving colliding
217
+ * record routable. Rebuild again on successful restoration so the reinstated
218
+ * fence re-quarantines the topic id instead of leaving inbound routing open to
219
+ * the collision partner despite the restored fence.
220
+ */
203
221
  restoreDeleteFence(snapshot: TopicDeleteAuthoritySnapshot): boolean;
204
222
  /** Fence new work before the remote delete starts, including an absent in-flight create. */
205
223
  beginDelete(sessionId: string): TopicRecord | undefined;
@@ -209,8 +227,50 @@ export declare class TopicRegistry {
209
227
  fenceAcceptedCreateForLease(sessionId: string, topicId: string, creationLeaseEpoch: number, now?: () => number, name?: string, binding?: TopicEndpointBinding): TopicRecord | undefined;
210
228
  /** Wait for a revoked create to settle before admitting a later lifecycle epoch. */
211
229
  awaitInflight(sessionId: string): Promise<void>;
212
- /** Remove only after a definite remote deletion; ambiguity deliberately retains its fence. */
213
- settleDelete(sessionId: string, topicId: string): boolean;
230
+ /**
231
+ * Phase 1 of settling a definite remote delete: remove the record while
232
+ * deliberately RETAINING its topic-id quarantine.
233
+ *
234
+ * `dispatchedAuthorityEpoch` is the authority epoch the caller held when it
235
+ * dispatched the remote delete. Settlement requires that epoch to still equal
236
+ * both the record's own authority epoch and the registry's current epoch for
237
+ * the session, so a held earlier delete can never settle a newer fence: if a
238
+ * scan or close-started delete re-fenced the same session/topic after this
239
+ * delete was dispatched, the stale definite result is refused and the newer
240
+ * `delete_pending` record plus its topic-id quarantine stay intact.
241
+ *
242
+ * An epoch outside the safe-integer range cannot identify a generation, and at
243
+ * `Number.MAX_SAFE_INTEGER` epoch advancement has saturated so distinct
244
+ * generations are no longer distinguishable. Both fail closed: settlement can
245
+ * no longer be proven fresh, so the fence is kept.
246
+ *
247
+ * Derived routing tables are deliberately NOT rebuilt here. Until the cleared
248
+ * snapshot is durably persisted the clear is not authoritative, so publishing
249
+ * routes now would make a colliding survivor routable (and the settled id
250
+ * adoptable) during the held write, and a failed persist would re-quarantine
251
+ * too late. Publish with {@link commitSettledDelete} once the persist
252
+ * succeeds, or undo with {@link rollbackSettledDelete} when it fails.
253
+ */
254
+ settleDelete(sessionId: string, topicId: string, dispatchedAuthorityEpoch: number): TopicSettledDelete | undefined;
255
+ /**
256
+ * Phase 2: publish derived routing tables once the cleared state is durable.
257
+ *
258
+ * Only here does the settled topic id lose its quarantine, so a surviving
259
+ * colliding record becomes routable and a settled id becomes adoptable without
260
+ * waiting for a daemon restart.
261
+ */
262
+ commitSettledDelete(settled: TopicSettledDelete): boolean;
263
+ /**
264
+ * Compare-and-swap undo for a settlement whose durable clear failed.
265
+ *
266
+ * Restoration applies only while the registry still holds exactly the state
267
+ * that this settlement produced: the settlement is still awaiting commit, the
268
+ * session still has no record, and the session epoch is still the settled
269
+ * epoch. Any mismatch means a newer generation intervened, so the restore is
270
+ * refused and the newer state is left untouched. A refused settlement produced
271
+ * no token, so it can never reach this path.
272
+ */
273
+ rollbackSettledDelete(settled: TopicSettledDelete): boolean;
214
274
  /** Remove a topic record immediately for local/test cleanup compatibility. */
215
275
  delete(sessionId: string): boolean;
216
276
  /** Serialise active records plus unpublished staged creates for atomic commit. */
@@ -1,17 +1,26 @@
1
1
  import { type EventFrame, SessionEventStream } from "./events";
2
- import { ReverseLeaseRuntime } from "./reverse-leases";
2
+ import { type ProviderLease, ReverseLeaseRuntime } from "./reverse-leases";
3
3
  import type { BrokerIndexWriter, HostEndpointAdapters, SdkFrame } from "./types";
4
4
  export type SdkRequestObserver = (kind: "control" | "query", connectionId: string, frame: SdkFrame) => void;
5
+ export interface ProviderLeaseIdentity {
6
+ leaseId: string;
7
+ connectionId: string;
8
+ connectionGeneration: number;
9
+ fence: string;
10
+ }
11
+ export type ProviderLeaseFence = (identity: ProviderLeaseIdentity, reason?: string) => boolean;
5
12
  export interface SessionSdkHostOptions extends HostEndpointAdapters {
6
13
  control?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
7
14
  query?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
8
15
  /** Best-effort diagnostic observation of accepted control/query frames. */
9
16
  onRequest?: SdkRequestObserver;
17
+ onFrameOverflow?: (connectionId: string) => void;
10
18
  /** Runs before a control response is sent; identity transitions use sendTerminal. */
11
19
  beforeControlResponse?: (connectionId: string, request: SdkFrame, response: SdkFrame, sendTerminal: () => Promise<void>) => void | Promise<void>;
12
20
  /** Runs only after a successful control response has been sent to the client. */
13
21
  afterControlResponse?: (connectionId: string, request: SdkFrame, response: SdkFrame) => void | Promise<void>;
14
- installProviderDefinitions?: (capability: string, definitions: unknown) => void;
22
+ onProviderLeaseRegistered?: (lease: ProviderLease, connectionGeneration: number) => void;
23
+ installProviderDefinitions?: (capability: string, definitions: unknown, leaseId: string, connectionId: string, connectionGeneration: number) => void;
15
24
  onProviderDefinitionsRemoved?: (capability: string) => void;
16
25
  onReverseCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void;
17
26
  /** Best-effort capabilities mirrored from the native transport for out-of-band consumers. */
@@ -29,8 +38,20 @@ export declare class SessionSdkHost {
29
38
  get generation(): number;
30
39
  /** Current installed definitions for a live provider capability. */
31
40
  getProviderDefinitions(capability: string): unknown | undefined;
41
+ /** Mark a transport connection live and return its stable generation. */
42
+ handleConnect(connectionId: string): number;
43
+ /** Current generation for a transport connection, if it has been observed. */
44
+ connectionGeneration(connectionId: string): number;
45
+ isConnectionOpen(connectionId: string, generation?: number): boolean;
32
46
  /** Release reverse leases after the transport reports a WebSocket disconnect. */
33
47
  handleDisconnect(connectionId: string): void;
48
+ /**
49
+ * Fences one exact provider lease after uncertain terminal cleanup. The fence
50
+ * is idempotent for the same identity and never affects a newer generation.
51
+ */
52
+ quarantineProviderLease(identity: ProviderLeaseIdentity, _reason?: string): boolean;
53
+ /** Dispatch a frame through bounded per-connection asynchronous admission. */
54
+ dispatchFrame(connectionId: string, frame: SdkFrame): Promise<void>;
34
55
  /** Adds an event to the resumable event ring. Transport delivery is owned by bus wiring. */
35
56
  emitEvent(frame: SdkFrame): EventFrame;
36
57
  start(): Promise<"started" | "already">;
@@ -3,7 +3,10 @@ export declare const REVERSE_HEARTBEAT_MS = 5000;
3
3
  export declare const REVERSE_LEASE_TTL_MS = 15000;
4
4
  export declare const REVERSE_RECLAIM_GRACE_MS = 15000;
5
5
  export declare const MAX_REVERSE_OUTSTANDING = 64;
6
+ export declare const MAX_REVERSE_CLEANUP_OUTSTANDING = 2;
6
7
  export declare const MAX_REVERSE_PAYLOAD_BYTES: number;
8
+ export declare const MAX_REVERSE_TERMINAL_OUTPUT_BYTES: number;
9
+ export declare const MAX_REVERSE_TERMINAL_OUTPUT_PAYLOAD_BYTES: number;
7
10
  export declare class ReverseLeaseError extends Error {
8
11
  readonly code: "lease_unavailable" | "lease_expired" | "provider_lease_conflict" | "provider_required" | "not_lease_owner" | "payload_too_large" | "too_many_outstanding" | "unknown_request" | "idempotency_conflict";
9
12
  constructor(code: "lease_unavailable" | "lease_expired" | "provider_lease_conflict" | "provider_required" | "not_lease_owner" | "payload_too_large" | "too_many_outstanding" | "unknown_request" | "idempotency_conflict", message?: "idempotency_conflict" | "lease_expired" | "lease_unavailable" | "not_lease_owner" | "payload_too_large" | "provider_lease_conflict" | "provider_required" | "too_many_outstanding" | "unknown_request");
@@ -21,7 +24,7 @@ export interface ReverseLeaseOptions {
21
24
  now?: () => number;
22
25
  leaseTtlMs?: number;
23
26
  sendFrame: (connectionId: string, frame: SdkFrame) => void | Promise<void>;
24
- installDefinitions?: (capability: string, definitions: unknown) => void;
27
+ installDefinitions?: (capability: string, definitions: unknown, leaseId: string, connectionId: string, connectionGeneration: number) => void;
25
28
  onCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void;
26
29
  onDefinitionsRemoved?: (capability: string) => void;
27
30
  }
@@ -29,15 +32,15 @@ export interface ReverseLeaseOptions {
29
32
  export declare class ReverseLeaseRuntime {
30
33
  #private;
31
34
  constructor(options: ReverseLeaseOptions);
32
- registerProvider(connectionId: string, capability: string, definitions: unknown, expectedLeaseId?: string, idempotencyKey?: string): ProviderLease;
35
+ registerProvider(connectionId: string, capability: string, definitions: unknown, expectedLeaseId?: string, idempotencyKey?: string, connectionGeneration?: number): ProviderLease;
33
36
  heartbeat(connectionId: string, leaseId: string): ProviderLease;
34
37
  release(connectionId: string, leaseId: string, handoffTo?: string): ProviderLease;
35
38
  disconnect(connectionId: string): void;
36
- request(capability: string, method: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
39
+ request(capability: string, method: string, payload: unknown, signal?: AbortSignal, expectedLeaseId?: string, cleanup?: boolean, expectedConnectionId?: string): Promise<unknown>;
37
40
  respond(connectionId: string, id: string, leaseId: string, result: unknown, error?: {
38
41
  code: string;
39
42
  message: string;
40
- }): void;
43
+ }, wireEnvelope?: unknown): void;
41
44
  getLease(capability: string): ProviderLease | undefined;
42
45
  /** Installed definitions are observable only while their provider lease is live. */
43
46
  getInstalledDefinitions(capability: string): unknown | undefined;
@@ -13,8 +13,9 @@
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
15
  import { type Agent, type AgentContext, type AgentEvent, type AgentLoopConfig, type AgentMessage, type AgentState, type AgentTool, type MidRunMaintenanceOutcome, type RunSettlementProof, type StablePrefixSnapshot, ThinkingLevel } from "@gajae-code/agent-core";
16
+ import type { AttemptScope } from "@gajae-code/agent-core/attempt-scope";
16
17
  import { type CompactionResult, type EmergencyCompactionSample } from "@gajae-code/agent-core/compaction";
17
- import type { AssistantMessage, Effort, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolCall, ToolChoice, Usage, UsageReport } from "@gajae-code/ai";
18
+ import type { AssistantMessage, AttemptScopeRef, Effort, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolCall, ToolChoice, Usage, UsageReport } from "@gajae-code/ai";
18
19
  import { type BtwTextExchange } from "./btw-contract";
19
20
  export interface ForkContextSeedMetadata {
20
21
  sourceSessionId: string;
@@ -231,7 +232,7 @@ export interface AgentSessionConfig {
231
232
  /** Tool-session factory context used to lazily attach workflow-gate-only tools. */
232
233
  workflowGateToolSession?: ToolSession;
233
234
  /** Current session pre-LLM message transform pipeline */
234
- transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => AgentMessage[] | Promise<AgentMessage[]>;
235
+ transformContext?: (messages: AgentMessage[], signal?: AbortSignal, scope?: AttemptScopeRef) => AgentMessage[] | Promise<AgentMessage[]>;
235
236
  /** Provider payload hook used by the active session request path */
236
237
  onPayload?: SimpleStreamOptions["onPayload"];
237
238
  /** Provider response hook used by the active session request path */
@@ -815,9 +816,9 @@ export declare class AgentSession {
815
816
  /** Build display context from an unpublished successor without changing active session state. @internal */
816
817
  buildPreparedDisplaySessionContext(prepared: PreparedNewSession): SessionContext;
817
818
  /** Convert session messages using the same pre-LLM pipeline as the active session. */
818
- convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]>;
819
+ convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal, scope?: AttemptScope): Promise<Message[]>;
819
820
  /** Apply session-level stream hooks to a direct side request. */
820
- prepareSimpleStreamOptions(options: SimpleStreamOptions, provider?: string): SimpleStreamOptions;
821
+ prepareSimpleStreamOptions(options: SimpleStreamOptions, provider?: string, scope?: AttemptScope): SimpleStreamOptions;
821
822
  /** Current steering mode */
822
823
  get steeringMode(): "all" | "one-at-a-time";
823
824
  /** Current follow-up mode */
@@ -16,6 +16,11 @@ export interface ManagedOutputGeneration {
16
16
  export interface ArtifactSaveOptions {
17
17
  maxBytes?: number;
18
18
  }
19
+ export interface ArtifactSaveReceipt {
20
+ id: string;
21
+ complete: boolean;
22
+ omittedBytes?: number;
23
+ }
19
24
  /**
20
25
  * Manages artifact storage for a session.
21
26
  *
@@ -68,11 +73,9 @@ export declare class ArtifactManager {
68
73
  id: string;
69
74
  path?: string;
70
75
  }>;
71
- /**
72
- * Save content as an artifact and return the artifact ID.
73
- * Content is written to a private temporary inode, synced, and linked into
74
- * the artifact directory only after the complete terminal payload exists.
75
- */
76
+ /** Save content and return the artifact ID plus exact storage completeness evidence. */
77
+ saveWithReceipt(content: string, toolType: string, options?: ArtifactSaveOptions): Promise<ArtifactSaveReceipt>;
78
+ /** Save content as an artifact and return the artifact ID. */
76
79
  save(content: string, toolType: string, options?: ArtifactSaveOptions): Promise<string>;
77
80
  /**
78
81
  * Check if an artifact exists.
@@ -0,0 +1,19 @@
1
+ import type { AttemptScope, AttemptScopeAuthority } from "@gajae-code/agent-core/attempt-scope";
2
+ export type AttemptExecutionState = "unknown" | "clean" | "executed";
3
+ /**
4
+ * Tracks whether an attempt has had an extension handler delivered to it.
5
+ *
6
+ * Records are deliberately established in two phases: allocation registers an
7
+ * `unknown` record, then the session marks it `clean` immediately before the
8
+ * first handler-capable delivery can occur. The runner changes a clean record
9
+ * to `executed` immediately before invoking its first handler.
10
+ */
11
+ export declare class AttemptRecordStore {
12
+ #private;
13
+ constructor(authority: AttemptScopeAuthority);
14
+ register(scope: AttemptScope): boolean;
15
+ establishClean(scope: AttemptScope): boolean;
16
+ markExecuted(scope: AttemptScope): boolean;
17
+ isClean(scope: AttemptScope): boolean;
18
+ retire(scope: AttemptScope): boolean;
19
+ }