newmark-agent 0.6.2 → 0.6.3

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.
@@ -294,8 +294,15 @@ export interface ConversationBranchGroupSnapshot {
294
294
  export interface ConversationContinuation {
295
295
  content: string;
296
296
  queueMode: 'steer' | 'followUp';
297
+ buildId?: string;
297
298
  clientMessageId?: string;
298
299
  runId?: string;
300
+ branchNodeId?: string;
301
+ branchPath?: string[];
302
+ ledgerBranchId?: string;
303
+ targetRuntimeKey?: string;
304
+ workspaceKey?: string;
305
+ modelSelection?: string;
299
306
  images?: Array<{
300
307
  dataUrl: string;
301
308
  name?: string;
@@ -787,6 +794,13 @@ export declare class Agent {
787
794
  */
788
795
  private deriveConversationTitleFromProvider;
789
796
  private conversationTitleFailureCause;
797
+ /**
798
+ * Deterministic provider rejections (bad key, forbidden endpoint, unknown
799
+ * model, invalid request, payment required) cannot succeed by retrying the
800
+ * identical title probe. Only transient transport, timeout, rate-limit and
801
+ * provider-internal failures enter the bounded 5-stage backoff.
802
+ */
803
+ private conversationTitleFailureIsRetryable;
790
804
  private waitForConversationTitleRetry;
791
805
  /** Legacy completion hook retained as a no-op; naming is pre-response only. */
792
806
  private maybeAutoRenameConversationFromRun;
@@ -2735,8 +2735,15 @@ class Agent {
2735
2735
  deduped.set(key, {
2736
2736
  content,
2737
2737
  queueMode,
2738
+ buildId: String(raw.buildId || '').trim() || undefined,
2738
2739
  clientMessageId,
2739
2740
  runId: String(raw.runId || '').trim() || undefined,
2741
+ branchNodeId: String(raw.branchNodeId || '').trim() || undefined,
2742
+ branchPath: Array.isArray(raw.branchPath) ? raw.branchPath.map(id => String(id || '')).filter(Boolean) : undefined,
2743
+ ledgerBranchId: String(raw.ledgerBranchId || '').trim() || undefined,
2744
+ targetRuntimeKey: String(raw.targetRuntimeKey || '').trim() || undefined,
2745
+ workspaceKey: String(raw.workspaceKey || '').trim() || undefined,
2746
+ modelSelection: String(raw.modelSelection || '').trim() || undefined,
2740
2747
  images: images.length ? images : undefined,
2741
2748
  attachments: attachments.length ? attachments : undefined,
2742
2749
  hiddenUserInput: raw.hiddenUserInput === true,
@@ -3950,6 +3957,19 @@ class Agent {
3950
3957
  return 'provider returned invalid JSON';
3951
3958
  return 'provider request failed';
3952
3959
  }
3960
+ /**
3961
+ * Deterministic provider rejections (bad key, forbidden endpoint, unknown
3962
+ * model, invalid request, payment required) cannot succeed by retrying the
3963
+ * identical title probe. Only transient transport, timeout, rate-limit and
3964
+ * provider-internal failures enter the bounded 5-stage backoff.
3965
+ */
3966
+ conversationTitleFailureIsRetryable(error) {
3967
+ const message = error instanceof Error ? error.message : '';
3968
+ const status = message.match(/^\[LLM Error:\s*([1-5]\d{2})\]/)?.[1];
3969
+ if (!status)
3970
+ return true;
3971
+ return status === '408' || status === '429' || status.startsWith('5');
3972
+ }
3953
3973
  async waitForConversationTitleRetry(delayMs, signal) {
3954
3974
  if (signal?.aborted || delayMs <= 0)
3955
3975
  return;
@@ -3981,17 +4001,20 @@ class Agent {
3981
4001
  // Failure state belongs to this title attempt sequence, not the Agent or
3982
4002
  // another conversation. A later empty title replaces an earlier HTTP error.
3983
4003
  let lastFailure = 'provider returned an empty or unusable title';
4004
+ let terminalFailure = false;
3984
4005
  const requestTitle = async () => {
3985
4006
  lastFailure = 'provider returned an empty or unusable title';
4007
+ terminalFailure = false;
3986
4008
  try {
3987
4009
  return await this.deriveConversationTitleFromProvider(firstUserInput, provider, modelName, intelligence, signal);
3988
4010
  }
3989
4011
  catch (error) {
3990
4012
  lastFailure = this.conversationTitleFailureCause(error, provider);
4013
+ terminalFailure = !this.conversationTitleFailureIsRetryable(error);
3991
4014
  return '';
3992
4015
  }
3993
4016
  };
3994
- const exhaustedFailure = () => new Error(`Conversation title generation failed; ${lastFailure}. The first Agent request was not started. Retry the first input.`);
4017
+ const exhaustedFailure = () => new Error(`Conversation title generation failed for ${provider.name}/${modelName}; ${lastFailure}. The first Agent request was not started. Retry the first input or switch the model.`);
3995
4018
  if (!stateKey) {
3996
4019
  // Pure Agent/CLI mode has no workspace conversation file to rename, but
3997
4020
  // still uses the title request as the required first model-availability
@@ -3999,6 +4022,8 @@ class Agent {
3999
4022
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
4000
4023
  if (await requestTitle())
4001
4024
  return true;
4025
+ if (terminalFailure)
4026
+ break;
4002
4027
  if (signal?.aborted)
4003
4028
  return false;
4004
4029
  if (attempt < maxAttempts - 1)
@@ -4036,6 +4061,8 @@ class Agent {
4036
4061
  this.publishConversationTitle(title, conversationId);
4037
4062
  return true;
4038
4063
  }
4064
+ if (terminalFailure)
4065
+ break;
4039
4066
  if (signal?.aborted)
4040
4067
  return false;
4041
4068
  if (attempt < maxAttempts - 1)
@@ -6015,7 +6042,7 @@ class Agent {
6015
6042
  let requestContext = null;
6016
6043
  const context = saved?.version === 1 ? saved.requestContext : null;
6017
6044
  if (context && typeof context.requestId === 'string' && typeof context.runId === 'string' && typeof context.at === 'string') {
6018
- requestContext = { requestId: context.requestId, runId: context.runId, model: String(context.model || ''), at: context.at,
6045
+ requestContext = { requestId: context.requestId, runId: context.runId, branchId: String(context.branchId || ''), contextHash: String(context.contextHash || ''), model: String(context.model || ''), at: context.at,
6019
6046
  estimatedTokens: 0, longHistoryTokens: 0, buildBlockTokens: 0, systemPromptTokens: 0, toolSchemaTokens: 0, messageCount: 0, hasImages: context.hasImages === true };
6020
6047
  for (const key of ['estimatedTokens', 'longHistoryTokens', 'buildBlockTokens', 'systemPromptTokens', 'toolSchemaTokens', 'messageCount', 'inputTokens', 'cacheReadTokens']) {
6021
6048
  const value = context[key];
@@ -6083,9 +6110,16 @@ class Agent {
6083
6110
  return request;
6084
6111
  }
6085
6112
  recordRequestContext(request, messages, system, tools, model) {
6113
+ const contextHash = crypto.createHash('sha256').update(JSON.stringify({
6114
+ model,
6115
+ system,
6116
+ tools,
6117
+ messages,
6118
+ })).digest('hex').slice(0, 24);
6086
6119
  const context = {
6087
6120
  ...(0, requestContextEstimate_1.estimateSubmittedContext)(messages, system, tools, this.compressionBuildBlockStart(messages)),
6088
- requestId: request.id, runId: this.currentWorkRunId(), model, at: new Date().toISOString(),
6121
+ requestId: request.id, runId: this.currentWorkRunId(), branchId: this.currentBranchNodeId(),
6122
+ contextHash, model, at: new Date().toISOString(),
6089
6123
  };
6090
6124
  this.mutateRequestUsage(request, usage => { usage.requestContext = context; });
6091
6125
  }
@@ -8187,6 +8221,13 @@ class Agent {
8187
8221
  if (!titleProvider || !titleModelName) {
8188
8222
  throw new Error('Conversation title generation failed; no resolved model deployment is available. No LLM configured. Add provider in Settings > Models.');
8189
8223
  }
8224
+ // The title probe is a hard gate before the first formal request. Show
8225
+ // the exact deployment being checked immediately so a slow or failing
8226
+ // first turn never looks like an unexplained freeze.
8227
+ this.emitWorkEvent({
8228
+ type: 'status',
8229
+ content: `Preparing the first response: checking ${titleProvider.name}/${titleModelName} and generating the conversation title.`,
8230
+ });
8190
8231
  const titled = await this.startFirstInputConversationTitle(firstResponseTitleInput.messageId, firstResponseTitleInput.input, titleProvider, titleModelName, titleIntelligence, processSignal);
8191
8232
  if (!titled)
8192
8233
  throw new Error('Conversation title generation failed; the first Agent request was not started. Retry the first input.');
@@ -69,6 +69,24 @@ const HIDDEN_LINE_PREFIXES = [
69
69
  'analysis:',
70
70
  'analysis:',
71
71
  ];
72
+ /**
73
+ * Optional provider-side session cache identity. A remote session is mutable
74
+ * state, so it must never be shared by two branches of the same conversation:
75
+ * a fork that reuses the root conversation id can read the other branch's
76
+ * history. Keep the id stable within one branch (normal sequential turns still
77
+ * cache-hit) and change it the moment the runtime branch changes.
78
+ */
79
+ function providerSessionIdentity(agent) {
80
+ const conversationId = String(agent.activeConversationId || 'default');
81
+ try {
82
+ const snapshot = agent.getConversationSnapshot(conversationId);
83
+ const branchId = String(snapshot.runtimeBranchId || snapshot.activeBranchId || '');
84
+ return branchId ? `${conversationId}::branch:${branchId}` : conversationId;
85
+ }
86
+ catch {
87
+ return conversationId;
88
+ }
89
+ }
72
90
  function partialMarkerSuffix(value, marker) {
73
91
  const lower = value.toLowerCase();
74
92
  const target = marker.toLowerCase();
@@ -660,7 +678,11 @@ async function runAgentKernel(agent) {
660
678
  currentAgent.checkpointPeerInput();
661
679
  peerInputCheckpointed = true;
662
680
  }
663
- const { temperature, maxTokens, reasoningEffort } = currentProvider.intelligenceConfig(currentAgent.intelligence);
681
+ const { temperature, reasoningEffort } = currentProvider.intelligenceConfig(currentAgent.intelligence);
682
+ // A normal Agent reply has no client-side output cap. The provider
683
+ // endpoint owns its real limit; protocols that require a positive
684
+ // max_tokens resolve one internally.
685
+ const maxTokens = 0;
664
686
  const newmarkMessages = fromKernelMessages(context.messages).map(message => message.role === 'assistant'
665
687
  // Use the same public-content boundary on its first provider
666
688
  // submission and after persistence. Trimming only the durable
@@ -697,7 +719,7 @@ async function runAgentKernel(agent) {
697
719
  });
698
720
  }
699
721
  for await (const token of currentProvider.chatStreamWithTools(currentModelName, newmarkMessages, requestSystemPrompt, temperature, maxTokens, providerTools, options?.signal, reasoningEffort, currentAgent.config.getBool('context', 'provider_session_id')
700
- ? currentAgent.activeConversationId
722
+ ? providerSessionIdentity(currentAgent)
701
723
  : undefined)) {
702
724
  if (!firstTokenRecorded && ((token.type === 'text' && token.text) || (token.type === 'tool_call' && token.toolCall))) {
703
725
  firstTokenRecorded = true;
@@ -0,0 +1,197 @@
1
+ /**
2
+ * continuation-v1 contracts.
3
+ *
4
+ * These are the runtime-checked boundaries shared by the PC GUI, the hosted
5
+ * mobile API and the isolated runtimes. A view may read a snapshot, but it can
6
+ * never supply the execution identity that a write is allowed to use.
7
+ */
8
+ export type WorkspaceId = string;
9
+ export type RootId = string;
10
+ export type BranchId = string;
11
+ export type BuildId = string;
12
+ export type AttemptId = string;
13
+ export type CommandId = string;
14
+ export type SnapshotId = string;
15
+ export declare const CONTINUATION_SCHEMA_VERSION: 1;
16
+ export type BuildStatus = 'QUEUED' | 'RUNNING' | 'WAITING_TOOL' | 'WAITING_APPROVAL' | 'WAITING_SUBAGENT' | 'FINALIZING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED';
17
+ export type AttemptStatus = 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'EXPIRED';
18
+ export type ContinuationErrorCode = 'TARGET_REQUIRED' | 'ROUTE_MISMATCH' | 'QUEUE_REVISION_CONFLICT' | 'ANCHOR_NOT_IN_LINEAGE' | 'ANCHOR_NOT_COMMITTED' | 'IDEMPOTENCY_KEY_REUSE' | 'STALE_EXECUTION' | 'FINAL_ALREADY_COMMITTED' | 'BUILD_NOT_READY' | 'BRANCH_PAUSED' | 'EFFECT_UNCERTAIN' | 'UNKNOWN_BRANCH' | 'UNKNOWN_BUILD' | 'UNKNOWN_COMMAND';
19
+ export declare class ContinuationError extends Error {
20
+ readonly code: ContinuationErrorCode;
21
+ readonly detail?: Record<string, unknown> | undefined;
22
+ constructor(code: ContinuationErrorCode, message: string, detail?: Record<string, unknown> | undefined);
23
+ }
24
+ export interface CommandTarget {
25
+ workspaceId: WorkspaceId;
26
+ expectedRootId: RootId;
27
+ branchId: BranchId;
28
+ }
29
+ export interface BuildInput {
30
+ text: string;
31
+ images?: Array<{
32
+ dataUrl: string;
33
+ name?: string;
34
+ type?: string;
35
+ }>;
36
+ attachments?: Array<{
37
+ id: string;
38
+ name: string;
39
+ mimeType: string;
40
+ dataUrl?: string;
41
+ }>;
42
+ requestedMode?: string;
43
+ goalObjective?: string;
44
+ /** Provider-qualified model frozen at command acceptance time. */
45
+ modelSelection?: string;
46
+ /** Branch node captured when the user submitted the command. */
47
+ branchNodeId?: string;
48
+ branchPath?: string[];
49
+ }
50
+ export interface EnqueueBuildCommand {
51
+ schemaVersion: typeof CONTINUATION_SCHEMA_VERSION;
52
+ type: 'ENQUEUE_BUILD';
53
+ commandId: CommandId;
54
+ actorId: string;
55
+ target: CommandTarget;
56
+ expectedTailBuildId: BuildId | null;
57
+ expectedQueueRevision: number;
58
+ input: BuildInput;
59
+ recipeSnapshotId: SnapshotId;
60
+ createdAt: string;
61
+ }
62
+ export interface CommandReceipt {
63
+ schemaVersion: typeof CONTINUATION_SCHEMA_VERSION;
64
+ commandId: CommandId;
65
+ requestHash: string;
66
+ workspaceId: WorkspaceId;
67
+ rootId: RootId;
68
+ branchId: BranchId;
69
+ buildId: BuildId;
70
+ parentBuildId: BuildId | null;
71
+ queueSequence: number;
72
+ queueRevision: number;
73
+ lifecycleRevision: number;
74
+ acceptedAt: string;
75
+ duplicate?: boolean;
76
+ }
77
+ export interface ExecutionScope {
78
+ workspaceId: WorkspaceId;
79
+ rootId: RootId;
80
+ branchId: BranchId;
81
+ buildId: BuildId;
82
+ attemptId: AttemptId;
83
+ fence: number;
84
+ parentBuildId: BuildId | null;
85
+ baseContextSnapshotId: SnapshotId;
86
+ environmentSnapshotId: SnapshotId;
87
+ workerCapability: string;
88
+ }
89
+ export interface BranchGuardSnapshot {
90
+ branchId: BranchId;
91
+ activeBuildId: BuildId | null;
92
+ activeAttemptId: AttemptId | null;
93
+ fence: number;
94
+ leaseUntil: number;
95
+ workerCapability: string;
96
+ }
97
+ export interface QueueSnapshot {
98
+ workspaceId: WorkspaceId;
99
+ rootId: RootId;
100
+ branchId: BranchId;
101
+ headBuildId: BuildId | null;
102
+ tailBuildId: BuildId | null;
103
+ queueRevision: number;
104
+ lifecycleRevision: number;
105
+ paused: boolean;
106
+ activeBuildId: BuildId | null;
107
+ guard: BranchGuardSnapshot;
108
+ builds: BuildRecord[];
109
+ }
110
+ export interface BuildRecord {
111
+ buildId: BuildId;
112
+ workspaceId: WorkspaceId;
113
+ rootId: RootId;
114
+ branchId: BranchId;
115
+ parentBuildId: BuildId | null;
116
+ inputHash: string;
117
+ input: BuildInput;
118
+ recipeSnapshotId: SnapshotId;
119
+ status: BuildStatus;
120
+ queueSequence: number;
121
+ createdAt: string;
122
+ updatedAt: string;
123
+ finalId?: string;
124
+ waitingReason?: string;
125
+ }
126
+ export interface AttemptRecord {
127
+ attemptId: AttemptId;
128
+ buildId: BuildId;
129
+ branchId: BranchId;
130
+ fence: number;
131
+ status: AttemptStatus;
132
+ leaseUntil: number;
133
+ workerCapability: string;
134
+ startedAt: string;
135
+ endedAt?: string;
136
+ }
137
+ export interface FinalRecord {
138
+ finalId: string;
139
+ buildId: BuildId;
140
+ branchId: BranchId;
141
+ finalHash: string;
142
+ stateSnapshotId: SnapshotId;
143
+ text: string;
144
+ committedAt: string;
145
+ }
146
+ export interface ContinuationEvent {
147
+ eventId: string;
148
+ cursor: number;
149
+ type: 'BuildAccepted' | 'AttemptStarted' | 'AttemptHeartbeat' | 'BuildCommitted' | 'BuildFailed' | 'BuildCancelled' | 'BranchForked' | 'BranchPaused' | 'BranchResumed' | 'StaleWriteRejected';
150
+ workspaceId: WorkspaceId;
151
+ rootId: RootId;
152
+ branchId: BranchId;
153
+ buildId: BuildId | null;
154
+ attemptId: AttemptId | null;
155
+ fence: number | null;
156
+ at: string;
157
+ payload?: Record<string, unknown>;
158
+ }
159
+ export interface LedgerFile {
160
+ version: typeof CONTINUATION_SCHEMA_VERSION;
161
+ workspaceId: WorkspaceId;
162
+ roots: Record<RootId, {
163
+ rootId: RootId;
164
+ workspaceId: WorkspaceId;
165
+ defaultBranchId: BranchId;
166
+ createdAt: string;
167
+ }>;
168
+ branches: Record<BranchId, BranchRecord>;
169
+ builds: Record<BuildId, BuildRecord>;
170
+ attempts: Record<AttemptId, AttemptRecord>;
171
+ guards: Record<BranchId, BranchGuardSnapshot>;
172
+ commands: Record<string, CommandReceipt>;
173
+ finals: Record<BuildId, FinalRecord>;
174
+ events: ContinuationEvent[];
175
+ nextEventCursor: number;
176
+ nextQueueSequence: number;
177
+ }
178
+ export interface BranchRecord {
179
+ branchId: BranchId;
180
+ workspaceId: WorkspaceId;
181
+ rootId: RootId;
182
+ parentAnchorBuildId: BuildId | null;
183
+ headBuildId: BuildId | null;
184
+ tailBuildId: BuildId | null;
185
+ queueRevision: number;
186
+ lifecycleRevision: number;
187
+ paused: boolean;
188
+ createdAt: string;
189
+ updatedAt: string;
190
+ }
191
+ export declare function stableJson(value: unknown): string;
192
+ export declare function emptyLedger(workspaceId: WorkspaceId): LedgerFile;
193
+ export declare function defaultGuard(branchId: BranchId): BranchGuardSnapshot;
194
+ export declare function commandKey(actorId: string, commandId: CommandId): string;
195
+ export declare function buildInputHash(input: BuildInput): string;
196
+ export declare function requestHash(command: EnqueueBuildCommand): string;
197
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * continuation-v1 contracts.
4
+ *
5
+ * These are the runtime-checked boundaries shared by the PC GUI, the hosted
6
+ * mobile API and the isolated runtimes. A view may read a snapshot, but it can
7
+ * never supply the execution identity that a write is allowed to use.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ContinuationError = exports.CONTINUATION_SCHEMA_VERSION = void 0;
11
+ exports.stableJson = stableJson;
12
+ exports.emptyLedger = emptyLedger;
13
+ exports.defaultGuard = defaultGuard;
14
+ exports.commandKey = commandKey;
15
+ exports.buildInputHash = buildInputHash;
16
+ exports.requestHash = requestHash;
17
+ exports.CONTINUATION_SCHEMA_VERSION = 1;
18
+ class ContinuationError extends Error {
19
+ code;
20
+ detail;
21
+ constructor(code, message, detail) {
22
+ super(message);
23
+ this.code = code;
24
+ this.detail = detail;
25
+ this.name = 'ContinuationError';
26
+ }
27
+ }
28
+ exports.ContinuationError = ContinuationError;
29
+ function stableJson(value) {
30
+ if (value === null || typeof value !== 'object')
31
+ return JSON.stringify(value);
32
+ if (Array.isArray(value))
33
+ return `[${value.map(item => stableJson(item)).join(',')}]`;
34
+ const record = value;
35
+ return `{${Object.keys(record).sort().map(key => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(',')}}`;
36
+ }
37
+ function emptyLedger(workspaceId) {
38
+ return {
39
+ version: exports.CONTINUATION_SCHEMA_VERSION,
40
+ workspaceId,
41
+ roots: {},
42
+ branches: {},
43
+ builds: {},
44
+ attempts: {},
45
+ guards: {},
46
+ commands: {},
47
+ finals: {},
48
+ events: [],
49
+ nextEventCursor: 1,
50
+ nextQueueSequence: 1,
51
+ };
52
+ }
53
+ function defaultGuard(branchId) {
54
+ return { branchId, activeBuildId: null, activeAttemptId: null, fence: 0, leaseUntil: 0, workerCapability: '' };
55
+ }
56
+ function commandKey(actorId, commandId) {
57
+ return `${actorId}\u0000${commandId}`;
58
+ }
59
+ function buildInputHash(input) {
60
+ return require('crypto').createHash('sha256').update(stableJson(input)).digest('hex').slice(0, 24);
61
+ }
62
+ function requestHash(command) {
63
+ return require('crypto').createHash('sha256').update(stableJson({
64
+ type: command.type,
65
+ target: command.target,
66
+ expectedTailBuildId: command.expectedTailBuildId,
67
+ expectedQueueRevision: command.expectedQueueRevision,
68
+ input: command.input,
69
+ recipeSnapshotId: command.recipeSnapshotId,
70
+ })).digest('hex');
71
+ }
72
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1,178 @@
1
+ import { BranchId, BranchRecord, BuildId, BuildInput, BuildRecord, CommandReceipt, ContinuationEvent, EnqueueBuildCommand, ExecutionScope, FinalRecord, QueueSnapshot, RootId, SnapshotId } from './contracts';
2
+ export interface GuardedContinuationStoreOptions {
3
+ /** Lease duration for one attempt. */
4
+ leaseMs?: number;
5
+ /** Maximum retained append-only events per workspace ledger. */
6
+ eventRetention?: number;
7
+ }
8
+ /**
9
+ * Single-writer continuation ledger for one workspace.
10
+ *
11
+ * The spec's reference uses SQLite. This implementation keeps the same
12
+ * boundaries with a lock-file + atomic-replace JSON ledger because the project
13
+ * does not ship a native SQLite module and an Electron native dependency would
14
+ * change the packaging/signing contract. All continuation writes must go
15
+ * through this class; callers never mutate the ledger directly.
16
+ */
17
+ export declare class GuardedContinuationStore {
18
+ private readonly workspacePath;
19
+ private readonly workspaceId;
20
+ private readonly ledgerPath;
21
+ private readonly lockPath;
22
+ private readonly leaseMs;
23
+ private readonly eventRetention;
24
+ private fingerprint;
25
+ private cached;
26
+ constructor(workspacePath: string, workspaceId: string, options?: GuardedContinuationStoreOptions);
27
+ get filePath(): string;
28
+ ensureRoot(rootId: RootId, defaultBranchId: BranchId): void;
29
+ ensureBranch(input: {
30
+ rootId: RootId;
31
+ branchId: BranchId;
32
+ parentAnchorBuildId: BuildId | null;
33
+ }): BranchRecord;
34
+ getReceipt(actorId: string, commandId: string): CommandReceipt | undefined;
35
+ enqueueBuild(command: EnqueueBuildCommand): CommandReceipt;
36
+ claimBuild(input: {
37
+ workspaceId: string;
38
+ rootId: RootId;
39
+ branchId: BranchId;
40
+ workerCapability: string;
41
+ now?: number;
42
+ }): ExecutionScope | null;
43
+ updateQueuedBuild(input: {
44
+ workspaceId: string;
45
+ rootId: RootId;
46
+ branchId: BranchId;
47
+ buildId: BuildId;
48
+ input: BuildInput;
49
+ }): BuildRecord;
50
+ deleteQueuedBuild(input: {
51
+ workspaceId: string;
52
+ rootId: RootId;
53
+ branchId: BranchId;
54
+ buildId: BuildId;
55
+ }): void;
56
+ reorderQueuedBuilds(input: {
57
+ workspaceId: string;
58
+ rootId: RootId;
59
+ branchId: BranchId;
60
+ orderedBuildIds: BuildId[];
61
+ }): QueueSnapshot;
62
+ heartbeat(scope: Pick<ExecutionScope, 'branchId' | 'buildId' | 'attemptId' | 'fence'>, now?: number): boolean;
63
+ commitFinal(input: {
64
+ scope: ExecutionScope;
65
+ text: string;
66
+ finalHash?: string;
67
+ stateSnapshotId?: SnapshotId;
68
+ now?: number;
69
+ }): FinalRecord;
70
+ /**
71
+ * Trusted external-owner completion.
72
+ *
73
+ * A Flow is a kernel-owned external run, not a leased model worker. It still
74
+ * has to be a durable build so queued Goal/Next successors have a committed
75
+ * parent. This path validates the parent/head relation transactionally but
76
+ * does not require a BranchGuard attempt; the kernel owns the Flow lifetime.
77
+ * If the build is not the next ready build, it stays QUEUED and the caller
78
+ * must retry after the predecessor commits.
79
+ */
80
+ commitExternalBuild(input: {
81
+ workspaceId: string;
82
+ rootId: RootId;
83
+ branchId: BranchId;
84
+ buildId: BuildId;
85
+ text?: string;
86
+ now?: number;
87
+ }): boolean;
88
+ /**
89
+ * Claim a new attempt for an existing external Flow build (suspend/resume).
90
+ * The build identity and fixed parent are preserved; only the attempt/fence
91
+ * advance. This is the external-owner equivalent of retryBuild.
92
+ */
93
+ claimExternalBuild(input: {
94
+ workspaceId: string;
95
+ rootId: RootId;
96
+ branchId: BranchId;
97
+ buildId: BuildId;
98
+ workerCapability?: string;
99
+ now?: number;
100
+ }): ExecutionScope;
101
+ failBuild(input: {
102
+ scope: ExecutionScope;
103
+ reason: string;
104
+ now?: number;
105
+ }): void;
106
+ cancelBuild(input: {
107
+ workspaceId: string;
108
+ rootId: RootId;
109
+ branchId: BranchId;
110
+ buildId: BuildId;
111
+ now?: number;
112
+ }): void;
113
+ retryBuild(input: {
114
+ workspaceId: string;
115
+ rootId: RootId;
116
+ branchId: BranchId;
117
+ buildId: BuildId;
118
+ }): BuildRecord;
119
+ /**
120
+ * Idempotent failure recovery for an input that was never accepted.
121
+ *
122
+ * Stop/archive may release the branch guard before the runtime reports the
123
+ * failure. In that case the build is still the same durable row and must be
124
+ * returned to QUEUED for an explicit resume instead of being stranded in
125
+ * RUNNING (or permanently FAILED). A committed build is never requeued.
126
+ */
127
+ requeueBuild(input: {
128
+ workspaceId: string;
129
+ rootId: RootId;
130
+ branchId: BranchId;
131
+ buildId: BuildId;
132
+ }): BuildRecord;
133
+ forkBranch(input: {
134
+ commandId: string;
135
+ actorId: string;
136
+ workspaceId: string;
137
+ sourceBranchId: BranchId;
138
+ anchorBuildId: BuildId;
139
+ newBranchId: BranchId;
140
+ }): BranchRecord;
141
+ setPaused(input: {
142
+ workspaceId: string;
143
+ rootId: RootId;
144
+ branchId: BranchId;
145
+ paused: boolean;
146
+ }): boolean;
147
+ snapshot(input: {
148
+ workspaceId: string;
149
+ rootId: RootId;
150
+ branchId: BranchId;
151
+ }): QueueSnapshot;
152
+ private snapshotUnlocked;
153
+ events(afterCursor?: number, limit?: number): ContinuationEvent[];
154
+ recoverExpiredLeases(now?: number): number;
155
+ private terminateAttempt;
156
+ private releaseExpiredGuard;
157
+ private markDependentsBlocked;
158
+ private hasCommittedDescendant;
159
+ private isAncestorOfHead;
160
+ private ensureBranchRecord;
161
+ private requireBranch;
162
+ private ensureGuard;
163
+ /**
164
+ * Release the active attempt without ever moving the branch fence backward.
165
+ * A recovered/completed attempt's fence stays as the monotonic high-water
166
+ * mark; the next claim increments it again.
167
+ */
168
+ private releaseGuard;
169
+ private appendEvent;
170
+ private mutate;
171
+ private acquireLock;
172
+ private readLedger;
173
+ private readLedgerUnlocked;
174
+ private writeLedgerUnlocked;
175
+ private ensureLedgerExists;
176
+ private invalidateCache;
177
+ }
178
+ //# sourceMappingURL=store.d.ts.map