pi-cursor-sdk 0.5.2 → 0.5.4
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.
- package/CHANGELOG.md +66 -1
- package/README.md +62 -795
- package/dist/context.js +13 -7
- package/dist/cursor-agents-context.js +6 -6
- package/dist/cursor-api-key.js +1 -1
- package/dist/cursor-bridge-contract.js +1 -13
- package/dist/cursor-cloud-local-state.js +0 -1
- package/dist/cursor-config.js +1 -17
- package/dist/cursor-custom-subagent-definitions.js +1 -1
- package/dist/cursor-fallback-models.generated.js +665 -1
- package/dist/cursor-live-run-coordinator.js +1 -0
- package/dist/cursor-local-agent-ownership.js +17 -0
- package/dist/cursor-mcp-timeout-override.js +7 -18
- package/dist/cursor-native-tool-display-replay.js +5 -5
- package/dist/cursor-native-tools.js +11 -0
- package/dist/cursor-pi-tool-bridge-diagnostics.js +4 -0
- package/dist/cursor-pi-tool-bridge-env.js +25 -2
- package/dist/cursor-pi-tool-bridge-run.js +10 -3
- package/dist/cursor-pi-tool-bridge-server.js +98 -48
- package/dist/cursor-pi-tool-bridge-snapshot.js +1 -1
- package/dist/cursor-pi-tool-bridge.js +4 -12
- package/dist/cursor-provider-errors.js +2 -8
- package/dist/cursor-provider-live-run-drain.js +8 -3
- package/dist/cursor-provider-overflow.js +7 -21
- package/dist/cursor-provider-turn-prepare.js +8 -1
- package/dist/cursor-provider-turn-runner.js +17 -6
- package/dist/cursor-provider-turn-summary.js +1 -1
- package/dist/cursor-provider.js +1 -4
- package/dist/cursor-runtime-state.js +3 -3
- package/dist/cursor-sdk-event-debug.js +2 -2
- package/dist/cursor-sdk-output-filter.js +1 -1
- package/dist/cursor-sdk-process-error-guard.js +1 -2
- package/dist/cursor-session-agent-cleanup.js +10 -1
- package/dist/cursor-session-agent-lifecycle.js +2 -2
- package/dist/cursor-session-agent-lineage.js +11 -7
- package/dist/cursor-session-agent-resume.js +15 -9
- package/dist/cursor-session-agent.js +28 -15
- package/dist/cursor-session-scope.js +28 -21
- package/dist/cursor-session-settings.js +3 -3
- package/dist/cursor-session-store.js +2 -2
- package/dist/cursor-state.js +26 -29
- package/dist/cursor-tool-lifecycle.js +0 -3
- package/dist/cursor-tool-manifest.js +3 -1
- package/dist/cursor-tool-presentation-registry.js +2 -4
- package/dist/cursor-tool-visibility.js +0 -3
- package/dist/cursor-transcript-tool-specs.js +1 -2
- package/dist/index.js +21 -0
- package/dist/model-discovery.js +8 -10
- package/dist/model-list-cache.js +4 -9
- package/docs/cursor-live-smoke-checklist.md +4 -4
- package/docs/cursor-model-ux-spec.md +36 -19
- package/docs/cursor-native-tool-replay.md +8 -5
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +39 -9
- package/docs/cursor-tool-surfaces.md +48 -4
- package/docs/development.md +93 -0
- package/docs/platform-smoke-implementation.md +3 -2
- package/docs/reference.md +806 -0
- package/package.json +13 -11
- package/scripts/platform-smoke/artifact-secrets.mjs +13 -4
- package/scripts/platform-smoke/local-resume-runner.mjs +0 -1
- package/scripts/platform-smoke/target-runtime.mjs +4 -2
- package/scripts/platform-smoke/targets.mjs +0 -1
- package/shared/cursor-sdk-output-filter.d.mts +2 -0
- package/shared/cursor-sdk-output-filter.mjs +92 -28
- package/shared/cursor-sensitive-text.d.mts +1 -0
- package/shared/cursor-sensitive-text.mjs +7 -0
- package/src/context.ts +18 -11
- package/src/cursor-agents-context.ts +6 -6
- package/src/cursor-api-key.ts +1 -1
- package/src/cursor-bridge-contract.ts +1 -15
- package/src/cursor-cloud-local-state.ts +0 -2
- package/src/cursor-config.ts +1 -24
- package/src/cursor-custom-subagent-definitions.ts +1 -1
- package/src/cursor-fallback-models.generated.ts +665 -1
- package/src/cursor-live-run-coordinator.ts +5 -0
- package/src/cursor-local-agent-ownership.ts +18 -0
- package/src/cursor-mcp-timeout-override.ts +7 -31
- package/src/cursor-native-tool-display-replay.ts +4 -6
- package/src/cursor-native-tools.ts +14 -0
- package/src/cursor-pi-tool-bridge-diagnostics.ts +17 -0
- package/src/cursor-pi-tool-bridge-env.ts +31 -2
- package/src/cursor-pi-tool-bridge-run.ts +10 -3
- package/src/cursor-pi-tool-bridge-server.ts +89 -45
- package/src/cursor-pi-tool-bridge-snapshot.ts +0 -1
- package/src/cursor-pi-tool-bridge.ts +2 -18
- package/src/cursor-provider-errors.ts +2 -10
- package/src/cursor-provider-live-run-drain.ts +8 -2
- package/src/cursor-provider-overflow.ts +6 -24
- package/src/cursor-provider-turn-prepare.ts +10 -1
- package/src/cursor-provider-turn-runner.ts +17 -6
- package/src/cursor-provider-turn-summary.ts +1 -1
- package/src/cursor-provider.ts +0 -5
- package/src/cursor-runtime-state.ts +3 -3
- package/src/cursor-sdk-event-debug.ts +2 -2
- package/src/cursor-sdk-output-filter.ts +2 -1
- package/src/cursor-sdk-process-error-guard.ts +1 -3
- package/src/cursor-session-agent-cleanup.ts +6 -1
- package/src/cursor-session-agent-lifecycle.ts +2 -2
- package/src/cursor-session-agent-lineage.ts +10 -5
- package/src/cursor-session-agent-resume.ts +14 -7
- package/src/cursor-session-agent.ts +31 -15
- package/src/cursor-session-scope.ts +34 -20
- package/src/cursor-session-settings.ts +3 -3
- package/src/cursor-session-store.ts +2 -2
- package/src/cursor-state.ts +34 -41
- package/src/cursor-tool-lifecycle.ts +0 -4
- package/src/cursor-tool-manifest.ts +4 -1
- package/src/cursor-tool-presentation-registry.ts +3 -5
- package/src/cursor-tool-visibility.ts +0 -4
- package/src/cursor-transcript-tool-specs.ts +0 -3
- package/src/index.ts +18 -0
- package/src/model-discovery.ts +7 -9
- package/src/model-list-cache.ts +4 -10
|
@@ -59,6 +59,7 @@ import type {
|
|
|
59
59
|
} from "./cursor-provider-turn-types.js";
|
|
60
60
|
import type { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
61
61
|
import type { SessionCursorAgentLease } from "./cursor-session-agent.js";
|
|
62
|
+
import { assertCursorNativeToolsRuntime } from "./cursor-native-tools.js";
|
|
62
63
|
import { prepareCursorSummaryProviderTurn } from "./cursor-provider-turn-summary.js";
|
|
63
64
|
|
|
64
65
|
export interface PrepareCursorProviderTurnParams {
|
|
@@ -69,6 +70,8 @@ export interface PrepareCursorProviderTurnParams {
|
|
|
69
70
|
throwIfAborted: () => void;
|
|
70
71
|
/** Snapshot resolved once by the runner before draining; reused unchanged through prepare. */
|
|
71
72
|
resolvedConfig: CursorResolvedSdkConfig;
|
|
73
|
+
/** Snapshotted before drain/acquisition, never re-read during creation/resume. */
|
|
74
|
+
nativeToolsEnabled: boolean;
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
interface PrepareCursorProviderTurnContext extends PrepareCursorProviderTurnParams {
|
|
@@ -252,7 +255,7 @@ async function prepareCursorCloudProviderTurn(
|
|
|
252
255
|
async function prepareCursorLocalProviderTurn(
|
|
253
256
|
prepareParams: PrepareCursorProviderTurnContext,
|
|
254
257
|
): Promise<LocalCursorProviderTurnPrepareResult> {
|
|
255
|
-
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled, agents, customSubagentNames } = prepareParams;
|
|
258
|
+
const { params, cwd, resolvedApiKey, sdkEventDebug, throwIfAborted, resolvedConfig, agentMode, selection, fastEnabled, agents, customSubagentNames, nativeToolsEnabled } = prepareParams;
|
|
256
259
|
const { model, context, options } = params;
|
|
257
260
|
|
|
258
261
|
let restoreCursorSdkOutputFilter: (() => void) | undefined;
|
|
@@ -289,6 +292,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
289
292
|
settingSources,
|
|
290
293
|
localSafety,
|
|
291
294
|
agents,
|
|
295
|
+
nativeToolsEnabled,
|
|
292
296
|
localResume: resolvedConfig.local.resume.value,
|
|
293
297
|
storeRoot: resolvedConfig.local.storeRoot.value,
|
|
294
298
|
useHttp1ForAgent,
|
|
@@ -314,6 +318,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
314
318
|
...getCursorPromptOptions(model),
|
|
315
319
|
nativeSourceRoles: params.request.nativeSourceRoles,
|
|
316
320
|
agentMode,
|
|
321
|
+
nativeToolsEnabled,
|
|
317
322
|
includePiBridgeGuidance,
|
|
318
323
|
includePiAskQuestionGuidance: false,
|
|
319
324
|
};
|
|
@@ -323,6 +328,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
323
328
|
return {
|
|
324
329
|
...promptOptions,
|
|
325
330
|
toolManifest: buildCursorToolManifestText({
|
|
331
|
+
nativeToolsEnabled,
|
|
326
332
|
bridgeSnapshot: sessionAgentLease.bridgeRun?.snapshot,
|
|
327
333
|
piBridgeEnabled: resolveCursorPiToolBridgeEnabled(),
|
|
328
334
|
includePiBridgeGuidance,
|
|
@@ -373,6 +379,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
373
379
|
agentMode,
|
|
374
380
|
localForce: resolvedConfig.local.force.value,
|
|
375
381
|
localResume: resolvedConfig.local.resume.value,
|
|
382
|
+
...(nativeToolsEnabled ? {} : { nativeToolsEnabled: false, tools: ["mcp"] }),
|
|
376
383
|
resumedAgent: sessionAgentLease.resumed,
|
|
377
384
|
activeToolNames: activeToolNames ? [...activeToolNames] : [],
|
|
378
385
|
sessionAgentScopeKey,
|
|
@@ -386,6 +393,7 @@ async function prepareCursorLocalProviderTurn(
|
|
|
386
393
|
? cursorLiveRuns.start({
|
|
387
394
|
id: useNativeToolReplay ? nativeReplayId : bridgeRun?.id ?? nativeReplayId,
|
|
388
395
|
agent,
|
|
396
|
+
nativeToolsEnabled,
|
|
389
397
|
bridgeRun,
|
|
390
398
|
sessionBridgeRun,
|
|
391
399
|
sessionAgentScopeKey,
|
|
@@ -467,6 +475,7 @@ export async function prepareCursorProviderTurn(
|
|
|
467
475
|
): Promise<CursorProviderTurnPrepareResult> {
|
|
468
476
|
const { params, resolvedConfig } = prepareParams;
|
|
469
477
|
const { model, options } = params;
|
|
478
|
+
assertCursorNativeToolsRuntime(resolvedConfig.runtime.value, prepareParams.nativeToolsEnabled);
|
|
470
479
|
|
|
471
480
|
const fastEnabled = resolvedConfig.runtime.value === "cloud" ? undefined : getEffectiveFastForModelId(model.id, params.scope.scopeKey);
|
|
472
481
|
const selection = buildCursorModelSelection(model.id, options?.reasoning ?? "off", fastEnabled);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { assertCursorNativeToolsRuntime, resolveCursorNativeToolsEnabled } from "./cursor-native-tools.js";
|
|
1
2
|
import { CursorLiveRunAbortError } from "./cursor-live-run-coordinator.js";
|
|
2
|
-
import { drainExistingCursorLiveRunBeforeSend } from "./cursor-provider-live-run-drain.js";
|
|
3
|
+
import { cursorLiveRuns, drainExistingCursorLiveRunBeforeSend } from "./cursor-provider-live-run-drain.js";
|
|
3
4
|
import { installCursorSdkProcessErrorGuard } from "./cursor-sdk-process-error-guard.js";
|
|
4
5
|
import type { CursorRuntime } from "./cursor-config.js";
|
|
5
6
|
import { CursorSdkEventDebugSink } from "./cursor-sdk-event-debug.js";
|
|
@@ -68,6 +69,13 @@ export class CursorProviderTurnRunner {
|
|
|
68
69
|
this.throwIfAborted();
|
|
69
70
|
const { scope } = this.params;
|
|
70
71
|
const cwd = scope.cwd;
|
|
72
|
+
|
|
73
|
+
// Resolved once here, before any drain await, so the drain decision and the
|
|
74
|
+
// prepare dispatch below always act on the same config snapshot.
|
|
75
|
+
const resolvedConfig = resolveCursorProviderTurnConfig(cwd, scope.projectTrusted, scope.scopeKey);
|
|
76
|
+
this.runtimeTarget = resolvedConfig.runtime.value;
|
|
77
|
+
const nativeToolsEnabled = resolveCursorNativeToolsEnabled();
|
|
78
|
+
assertCursorNativeToolsRuntime(this.runtimeTarget, nativeToolsEnabled);
|
|
71
79
|
this.sdkEventDebug = CursorSdkEventDebugSink.maybeCreate({
|
|
72
80
|
cwd,
|
|
73
81
|
modelId: model.id,
|
|
@@ -76,13 +84,9 @@ export class CursorProviderTurnRunner {
|
|
|
76
84
|
});
|
|
77
85
|
sdkEventDebugRef.current = this.sdkEventDebug;
|
|
78
86
|
this.sdkEventDebug?.recordContextSnapshot(context);
|
|
79
|
-
// Resolved once here, before any drain await, so the drain decision and the
|
|
80
|
-
// prepare dispatch below always act on the same config snapshot.
|
|
81
|
-
const resolvedConfig = resolveCursorProviderTurnConfig(cwd, scope.projectTrusted, scope.scopeKey);
|
|
82
|
-
this.runtimeTarget = resolvedConfig.runtime.value;
|
|
83
87
|
if (resolvedConfig.runtime.value === "local" && this.params.request.purpose === "normal") {
|
|
84
88
|
if (
|
|
85
|
-
(await drainExistingCursorLiveRunBeforeSend(stream, partial, model, context, options?.signal, this.sdkEventDebug, scope.scopeKey, this.params.request.occupancyFloor)) ===
|
|
89
|
+
(await drainExistingCursorLiveRunBeforeSend(stream, partial, model, context, options?.signal, this.sdkEventDebug, scope.scopeKey, this.params.request.occupancyFloor, nativeToolsEnabled)) ===
|
|
86
90
|
"stream_ended"
|
|
87
91
|
) {
|
|
88
92
|
return;
|
|
@@ -98,6 +102,7 @@ export class CursorProviderTurnRunner {
|
|
|
98
102
|
sdkEventDebug: this.sdkEventDebug,
|
|
99
103
|
throwIfAborted: () => this.throwIfAborted(),
|
|
100
104
|
resolvedConfig,
|
|
105
|
+
nativeToolsEnabled,
|
|
101
106
|
});
|
|
102
107
|
|
|
103
108
|
const usage = await this.params.usageRecorder.start({
|
|
@@ -173,6 +178,12 @@ export class CursorProviderTurnRunner {
|
|
|
173
178
|
displayOnlyTraceBlock: finalized.displayOnlyTraceBlock,
|
|
174
179
|
});
|
|
175
180
|
} catch (error) {
|
|
181
|
+
// This body owns the session turn lease; a queued request aborted before
|
|
182
|
+
// entry must not release another request's run in handleOuterCatch().
|
|
183
|
+
if (!prepared && (error instanceof CursorLiveRunAbortError || options?.signal?.aborted)) {
|
|
184
|
+
const pending = cursorLiveRuns.getActiveForScope(this.params.scope.scopeKey);
|
|
185
|
+
if (pending && !pending.disposed) await cursorLiveRuns.release(pending);
|
|
186
|
+
}
|
|
176
187
|
await runFinalizer.applyTerminalEvent({ kind: "error", prepared: started ?? prepared, error });
|
|
177
188
|
} finally {
|
|
178
189
|
await runFinalizer.cleanup(prepared, sendResult, liveCompletion);
|
|
@@ -40,7 +40,7 @@ export async function prepareCursorSummaryProviderTurn(
|
|
|
40
40
|
configureCursorSdkHttp1(sdk, resolvedConfig.local.useHttp1ForAgent);
|
|
41
41
|
restoreCursorSdkOutputFilter = installCursorSdkOutputFilter();
|
|
42
42
|
sessionStore = (await openCursorSessionStoreForScope({
|
|
43
|
-
cwd, scopeKey: params.scope.scopeKey, persistent: false,
|
|
43
|
+
cwd, scopeKey: params.scope.persistentScopeKey ?? params.scope.scopeKey, persistent: false,
|
|
44
44
|
})).sessionStore;
|
|
45
45
|
throwIfAborted();
|
|
46
46
|
agent = await suppressCursorSdkOutput(() => sdk.Agent.create({
|
package/src/cursor-provider.ts
CHANGED
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
} from "@earendil-works/pi-ai";
|
|
10
10
|
import {
|
|
11
11
|
cursorLiveRuns,
|
|
12
|
-
DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS,
|
|
13
|
-
getPendingCursorLiveRun,
|
|
14
12
|
hasTrailingUserMessagesAfterToolResults,
|
|
15
13
|
releaseAllPendingCursorLiveRunsForTests,
|
|
16
14
|
resetCursorNativeReplayIdleDisposeMs,
|
|
@@ -114,10 +112,7 @@ export function streamCursor(
|
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
export const __testUtils = {
|
|
117
|
-
DEFAULT_CURSOR_NATIVE_REPLAY_IDLE_DISPOSE_MS,
|
|
118
115
|
pendingCursorNativeRunCount: cursorLiveRuns.count,
|
|
119
|
-
getPendingCursorLiveRun,
|
|
120
|
-
getActiveCursorLiveRunForScope: cursorLiveRuns.getActiveForScope,
|
|
121
116
|
hasTrailingUserMessagesAfterToolResults,
|
|
122
117
|
setCursorNativeReplayIdleDisposeMs,
|
|
123
118
|
resetCursorNativeReplayIdleDisposeMs,
|
|
@@ -39,9 +39,9 @@ import { getResolvedSessionCursorHttp1Enabled } from "./cursor-http1.js";
|
|
|
39
39
|
import { getCursorSessionScopeSnapshot } from "./cursor-session-scope.js";
|
|
40
40
|
import { cursorSettingsScopeForContext, getCursorSessionSettings } from "./cursor-session-settings.js";
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const CURSOR_RUNTIME_ENTRY_TYPE = "cursor-runtime-state";
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const CURSOR_CLOUD_ACK_DISCLOSURE = [
|
|
45
45
|
"Cursor Cloud executes this work remotely.",
|
|
46
46
|
"Fresh context is used by default; prior Pi context is included only with explicit bootstrap opt-in.",
|
|
47
47
|
"Pi-local tools and the Pi bridge are unavailable, and Pi environment variables are not forwarded.",
|
|
@@ -145,7 +145,7 @@ export function resolveCursorStatusRuntime(ctx: CursorRuntimeContext): CursorRun
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
function formatResolvedCursorRuntime(runtime: CursorResolvedSetting<CursorRuntime>): string {
|
|
149
149
|
const cap = runtime.cappedBy;
|
|
150
150
|
return cap
|
|
151
151
|
? `${runtime.value} (source: ${runtime.source} safety cap over ${cap.cappedSource} ${cap.cappedValue})`
|
|
@@ -171,7 +171,7 @@ export type DiscardedIncompleteStartedToolCallReason =
|
|
|
171
171
|
| "sdk-failure"
|
|
172
172
|
| "run-drain";
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
function hashCursorSdkCallId(callId: string): string {
|
|
175
175
|
return createHash("sha256").update(callId).digest("hex").slice(0, 8);
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -182,7 +182,7 @@ export interface DiscardedIncompleteStartedToolCallRecord {
|
|
|
182
182
|
reason: DiscardedIncompleteStartedToolCallReason;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
function serializeDiscardedIncompleteStartedToolCall(record: {
|
|
186
186
|
toolName: string;
|
|
187
187
|
callId: string;
|
|
188
188
|
reason?: DiscardedIncompleteStartedToolCallReason;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { classifyCursorConnectError,
|
|
2
|
+
import { classifyCursorConnectError, isCursorSdkConnectionStalledError } from "./cursor-provider-errors.js";
|
|
3
3
|
|
|
4
4
|
interface CursorSdkProcessErrorGuardToken {
|
|
5
5
|
suppressAbortErrors: boolean;
|
|
@@ -147,8 +147,6 @@ export const __testUtils = {
|
|
|
147
147
|
},
|
|
148
148
|
};
|
|
149
149
|
|
|
150
|
-
export { isCursorSdkAbortConnectError };
|
|
151
|
-
|
|
152
150
|
export function installCursorSdkProcessErrorGuard(): CursorSdkProcessErrorGuard {
|
|
153
151
|
const token: CursorSdkProcessErrorGuardToken = { suppressAbortErrors: false };
|
|
154
152
|
activeProviderTurns.add(token);
|
|
@@ -5,6 +5,7 @@ import { fsyncExistingRegularFile } from "./cursor-durable-fs.js";
|
|
|
5
5
|
import { scrubSensitiveText } from "./cursor-sensitive-text.js";
|
|
6
6
|
import { loadCursorSdk } from "./cursor-sdk-runtime.js";
|
|
7
7
|
import { cursorSessionScopeKeyForManager } from "./cursor-session-scope.js";
|
|
8
|
+
import { claimCursorLocalAgent } from "./cursor-local-agent-ownership.js";
|
|
8
9
|
import {
|
|
9
10
|
CURSOR_SESSION_AGENT_RESUME_ENTRY_TYPE,
|
|
10
11
|
isCursorLocalAgentId,
|
|
@@ -323,7 +324,11 @@ export async function runCursorSessionAgentCleanupCommand(pi: LocalResumeCleanup
|
|
|
323
324
|
openedStore = await openCursorSessionStore(ctx.cwd, identity, identities.workspaceRoot, identities.storeRoot);
|
|
324
325
|
openedStores.set(identity.stateRoot, openedStore);
|
|
325
326
|
}
|
|
326
|
-
|
|
327
|
+
const release = claimCursorLocalAgent(identity, agentId);
|
|
328
|
+
if (!release) throw new Error("Cursor local agent is still owned by an active runtime");
|
|
329
|
+
try {
|
|
330
|
+
await operations.delete(agentId, { cwd: ctx.cwd, store: openedStore.store });
|
|
331
|
+
} finally { release(); }
|
|
327
332
|
deletedAgentIds.push(agentId);
|
|
328
333
|
} catch (error) {
|
|
329
334
|
failedAgentIds.push({
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
SessionStartEvent,
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { clearCursorSdkHttp1 } from "./cursor-http1.js";
|
|
10
|
-
import {
|
|
10
|
+
import { cursorRuntimeSessionScopeKeyForManager, hasLiveCursorSessions, onCursorSessionScopeKeyChange } from "./cursor-session-scope.js";
|
|
11
11
|
import {
|
|
12
12
|
disposeSessionCursorAgent,
|
|
13
13
|
invalidateSessionAgent,
|
|
@@ -25,7 +25,7 @@ export interface CursorSessionAgentLifecycleExtensionApi {
|
|
|
25
25
|
|
|
26
26
|
export function registerCursorSessionAgentLifecycle(pi: CursorSessionAgentLifecycleExtensionApi): void {
|
|
27
27
|
let scopeKey: string | undefined;
|
|
28
|
-
pi.on("session_start", (_event, ctx) => { scopeKey =
|
|
28
|
+
pi.on("session_start", (_event, ctx) => { scopeKey = cursorRuntimeSessionScopeKeyForManager(ctx.sessionManager); });
|
|
29
29
|
onCursorSessionScopeKeyChange(pi, async (previousScopeKey) => {
|
|
30
30
|
await disposeSessionCursorAgent(previousScopeKey);
|
|
31
31
|
});
|
|
@@ -29,6 +29,7 @@ interface CursorSessionAgentLineageState {
|
|
|
29
29
|
sessionId?: string;
|
|
30
30
|
sessionFile?: string;
|
|
31
31
|
scopeKey?: string;
|
|
32
|
+
runtimeScopeKey?: string;
|
|
32
33
|
cwd?: string;
|
|
33
34
|
recordedAgentIds: Set<string>;
|
|
34
35
|
}
|
|
@@ -82,7 +83,7 @@ function readRecordedAgentIds(entries: readonly SessionEntry[], sessionId: strin
|
|
|
82
83
|
export function recordCursorSessionAgentLineage(agentId: string, turnScopeKey?: string): void {
|
|
83
84
|
const sessionState = turnScopeKey === undefined
|
|
84
85
|
? state
|
|
85
|
-
: statesByScope.get(turnScopeKey) ?? (state.scopeKey === turnScopeKey ? state : undefined);
|
|
86
|
+
: statesByScope.get(turnScopeKey) ?? ((state.runtimeScopeKey ?? state.scopeKey) === turnScopeKey ? state : undefined);
|
|
86
87
|
if (!sessionState) return;
|
|
87
88
|
const { appendEntry, sessionId, sessionFile, scopeKey, cwd } = sessionState;
|
|
88
89
|
if (!appendEntry || !sessionId || !scopeKey || !cwd) return;
|
|
@@ -115,22 +116,25 @@ export function registerCursorSessionAgentLineage(pi: CursorSessionAgentLineageE
|
|
|
115
116
|
pi.on("session_start", (_event, ctx) => {
|
|
116
117
|
state = sessionState;
|
|
117
118
|
const record = sessionState;
|
|
118
|
-
if (record.
|
|
119
|
+
if (record.runtimeScopeKey && statesByScope.get(record.runtimeScopeKey) === record) statesByScope.delete(record.runtimeScopeKey);
|
|
119
120
|
record.appendEntry = pi.appendEntry;
|
|
120
121
|
record.sessionId = ctx.sessionManager.getSessionId();
|
|
121
122
|
record.sessionFile = ctx.sessionManager.getSessionFile() ?? undefined;
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
const scope = getCursorSessionScopeSnapshot(pi);
|
|
124
|
+
record.runtimeScopeKey = scope.scopeKey;
|
|
125
|
+
record.scopeKey = scope.persistentScopeKey ?? scope.scopeKey;
|
|
126
|
+
statesByScope.set(scope.scopeKey, record);
|
|
124
127
|
record.cwd = ctx.cwd;
|
|
125
128
|
record.recordedAgentIds = readRecordedAgentIds(ctx.sessionManager.getEntries(), record.sessionId);
|
|
126
129
|
});
|
|
127
130
|
pi.on("session_shutdown", () => {
|
|
128
131
|
const state = sessionState;
|
|
129
|
-
if (state.
|
|
132
|
+
if (state.runtimeScopeKey && statesByScope.get(state.runtimeScopeKey) === state) statesByScope.delete(state.runtimeScopeKey);
|
|
130
133
|
state.appendEntry = undefined;
|
|
131
134
|
state.sessionId = undefined;
|
|
132
135
|
state.sessionFile = undefined;
|
|
133
136
|
state.scopeKey = undefined;
|
|
137
|
+
state.runtimeScopeKey = undefined;
|
|
134
138
|
state.cwd = undefined;
|
|
135
139
|
state.recordedAgentIds = new Set();
|
|
136
140
|
});
|
|
@@ -142,6 +146,7 @@ function resetStateForTests(): void {
|
|
|
142
146
|
state.sessionId = undefined;
|
|
143
147
|
state.sessionFile = undefined;
|
|
144
148
|
state.scopeKey = undefined;
|
|
149
|
+
state.runtimeScopeKey = undefined;
|
|
145
150
|
state.cwd = undefined;
|
|
146
151
|
state.recordedAgentIds = new Set();
|
|
147
152
|
}
|
|
@@ -60,6 +60,7 @@ interface PendingCursorSessionAgentResumeHandle {
|
|
|
60
60
|
|
|
61
61
|
interface CursorSessionResumeState {
|
|
62
62
|
appendEntry?: ExtensionAPI["appendEntry"];
|
|
63
|
+
runtimeScopeKey?: string;
|
|
63
64
|
scopeKey: string;
|
|
64
65
|
sessionFile?: string;
|
|
65
66
|
sessionId?: string;
|
|
@@ -355,7 +356,7 @@ function restoreFromBranch(branch: readonly SessionEntry[], allEntries: readonly
|
|
|
355
356
|
|
|
356
357
|
export function getMatchingCursorSessionAgentResumeHandle(poolKey: string, scopeKey?: string): CursorSessionAgentResumeEntryData | undefined {
|
|
357
358
|
const state = getResumeState(scopeKey);
|
|
358
|
-
if (scopeKey !== undefined && state.scopeKey !== scopeKey) return undefined;
|
|
359
|
+
if (scopeKey !== undefined && (state.runtimeScopeKey ?? state.scopeKey) !== scopeKey) return undefined;
|
|
359
360
|
const handle = state.activeHandle;
|
|
360
361
|
if (!handle || !isCursorLocalAgentId(handle.agentId)) return undefined;
|
|
361
362
|
if (handle.poolKey !== poolKey) return undefined;
|
|
@@ -373,7 +374,7 @@ export function getMatchingCursorSessionAgentResumeHandle(poolKey: string, scope
|
|
|
373
374
|
|
|
374
375
|
export function persistCursorSessionAgentResumeHandle(input: PendingCursorSessionAgentResumeHandle, scopeKey?: string): void {
|
|
375
376
|
const state = getResumeState(scopeKey);
|
|
376
|
-
if (scopeKey !== undefined && state.scopeKey !== scopeKey) return;
|
|
377
|
+
if (scopeKey !== undefined && (state.runtimeScopeKey ?? state.scopeKey) !== scopeKey) return;
|
|
377
378
|
if (!isCursorLocalAgentId(input.agentId)) return;
|
|
378
379
|
state.pendingHandle = {
|
|
379
380
|
runtime: input.runtime,
|
|
@@ -427,8 +428,9 @@ interface CursorSessionAgentResumeExtensionApi {
|
|
|
427
428
|
}
|
|
428
429
|
|
|
429
430
|
export function registerCursorSessionAgentResume(pi: CursorSessionAgentResumeExtensionApi): void {
|
|
431
|
+
const scope = getCursorSessionScopeSnapshot(pi);
|
|
430
432
|
const sessionState: CursorSessionResumeState = {
|
|
431
|
-
appendEntry: pi.appendEntry, scopeKey:
|
|
433
|
+
appendEntry: pi.appendEntry, scopeKey: scope.persistentScopeKey ?? scope.scopeKey,
|
|
432
434
|
cwd: process.cwd(), branchPathHash: EMPTY_BRANCH_HASH,
|
|
433
435
|
compactionGeneration: 0, unownedUserEntryIds: new Set(),
|
|
434
436
|
};
|
|
@@ -439,9 +441,12 @@ export function registerCursorSessionAgentResume(pi: CursorSessionAgentResumeExt
|
|
|
439
441
|
restoreFromBranch(branch, entries.length > 0 ? entries : branch, state);
|
|
440
442
|
};
|
|
441
443
|
pi.on("session_start", (_event, ctx) => {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
444
|
+
const key = state.runtimeScopeKey ?? state.scopeKey;
|
|
445
|
+
if (statesByScope.get(key) === state) statesByScope.delete(key);
|
|
446
|
+
const scope = getCursorSessionScopeSnapshot(pi);
|
|
447
|
+
state.runtimeScopeKey = scope.scopeKey;
|
|
448
|
+
state.scopeKey = scope.persistentScopeKey ?? scope.scopeKey;
|
|
449
|
+
statesByScope.set(scope.scopeKey, state);
|
|
445
450
|
lastBoundState = state;
|
|
446
451
|
state.sessionFile = ctx.sessionManager.getSessionFile?.() ?? undefined;
|
|
447
452
|
state.sessionId = ctx.sessionManager.getSessionId?.() ?? undefined;
|
|
@@ -482,7 +487,8 @@ export function registerCursorSessionAgentResume(pi: CursorSessionAgentResumeExt
|
|
|
482
487
|
state.branchPathHash = hashBranchStep(state.branchPathHash, event.compactionEntry);
|
|
483
488
|
});
|
|
484
489
|
pi.on("session_shutdown", () => {
|
|
485
|
-
|
|
490
|
+
const key = state.runtimeScopeKey ?? state.scopeKey;
|
|
491
|
+
if (statesByScope.get(key) === state) statesByScope.delete(key);
|
|
486
492
|
state.appendEntry = undefined;
|
|
487
493
|
state.pendingHandle = undefined;
|
|
488
494
|
});
|
|
@@ -496,6 +502,7 @@ function resetStateForTests(): void {
|
|
|
496
502
|
const state = lastBoundState;
|
|
497
503
|
state.appendEntry = undefined;
|
|
498
504
|
state.scopeKey = getCursorSessionScopeKey();
|
|
505
|
+
state.runtimeScopeKey = undefined;
|
|
499
506
|
state.sessionFile = undefined;
|
|
500
507
|
state.sessionId = undefined;
|
|
501
508
|
state.cwd = process.cwd();
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import type { AgentModeOption, AgentOptions, LocalAgentOptions, LocalAgentStore, ModelSelection, SDKAgent, SettingSource } from "@cursor/sdk";
|
|
3
3
|
import type { Context } from "@earendil-works/pi-ai";
|
|
4
4
|
import { normalizeCursorCustomStoreRoot } from "./cursor-custom-store-path.js";
|
|
5
|
+
import { claimCursorLocalAgent } from "./cursor-local-agent-ownership.js";
|
|
5
6
|
import {
|
|
6
7
|
getRegisteredCursorPiToolBridge,
|
|
7
8
|
type CursorPiToolBridge,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
type CursorPiToolBridgeRun,
|
|
10
11
|
} from "./cursor-pi-tool-bridge.js";
|
|
11
12
|
import { computeCursorContextFingerprint } from "./context.js";
|
|
12
|
-
import {
|
|
13
|
+
import { getCursorSessionScopeSnapshot, getCursorSessionScopeGeneration, getCursorSessionScopeKey, type CursorTurnScope } from "./cursor-session-scope.js";
|
|
13
14
|
import {
|
|
14
15
|
getMatchingCursorSessionAgentResumeHandle,
|
|
15
16
|
persistCursorSessionAgentResumeHandle,
|
|
@@ -68,6 +69,7 @@ interface SessionCursorAgentReadyEntry extends SessionCursorAgentPoolEntryBase {
|
|
|
68
69
|
resumeEnabled: boolean;
|
|
69
70
|
resumed: boolean;
|
|
70
71
|
resumeNotice?: string;
|
|
72
|
+
releaseAgentClaim: () => void;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
interface SessionCursorAgentBusyEntry extends SessionCursorAgentPoolEntryBase {
|
|
@@ -78,6 +80,7 @@ interface SessionCursorAgentBusyEntry extends SessionCursorAgentPoolEntryBase {
|
|
|
78
80
|
resumeEnabled: boolean;
|
|
79
81
|
resumed: boolean;
|
|
80
82
|
resumeNotice?: string;
|
|
83
|
+
releaseAgentClaim: () => void;
|
|
81
84
|
completionSettled: Promise<void>;
|
|
82
85
|
pendingCompletion: Promise<void>;
|
|
83
86
|
releaseBusyWait: () => void;
|
|
@@ -137,6 +140,8 @@ interface SessionCursorAgentCreateParams {
|
|
|
137
140
|
settingSources?: SettingSource[];
|
|
138
141
|
localSafety?: CursorLocalSafetyOptions;
|
|
139
142
|
agents?: AgentOptions["agents"];
|
|
143
|
+
/** Captured by the turn owner before asynchronous acquisition. */
|
|
144
|
+
nativeToolsEnabled?: boolean;
|
|
140
145
|
useHttp1ForAgent?: boolean;
|
|
141
146
|
onBridgeToolRequest?: (request: CursorPiBridgeToolRequest) => void;
|
|
142
147
|
debugRecorder?: CursorSdkEventDebugRecorder;
|
|
@@ -150,7 +155,7 @@ interface SessionCursorAgentCreateParams {
|
|
|
150
155
|
const sessionAgentsByScope = new Map<string, SessionCursorAgentPoolEntry>();
|
|
151
156
|
const invalidatedScopeKeys = new Set<string>();
|
|
152
157
|
const deadTransportScopeKeys = new Set<string>();
|
|
153
|
-
|
|
158
|
+
const DEAD_TRANSPORT_AGENT_DISPOSE_TIMEOUT_MS = 3000;
|
|
154
159
|
const terminalDisposedScopeGenerations = new Map<string, number>();
|
|
155
160
|
const scopeCreationGenerations = new Map<string, number>();
|
|
156
161
|
const EMPTY_POOL_STATE: SessionCursorAgentPoolState = { status: "empty" };
|
|
@@ -238,7 +243,7 @@ function buildCustomSubagentPoolKeySuffix(agents: AgentOptions["agents"]): strin
|
|
|
238
243
|
|
|
239
244
|
function buildSessionAgentPoolKey(scopeKey: string, params: SessionCursorAgentCreateParams): string {
|
|
240
245
|
return [
|
|
241
|
-
scopeKey,
|
|
246
|
+
params.scope?.persistentScopeKey ?? scopeKey,
|
|
242
247
|
params.cwd,
|
|
243
248
|
buildModelPoolKey(params.modelSelection),
|
|
244
249
|
buildSettingSourcesPoolKey(params.settingSources),
|
|
@@ -251,6 +256,8 @@ function buildSessionAgentPoolKey(scopeKey: string, params: SessionCursorAgentCr
|
|
|
251
256
|
buildApiKeyPoolKeyFingerprint(params.apiKey),
|
|
252
257
|
buildBridgePoolKeySuffix("bridge" in params ? params.bridge : getRegisteredCursorPiToolBridge()),
|
|
253
258
|
...buildCustomSubagentPoolKeySuffix(params.agents),
|
|
259
|
+
// Omit for the default so historical durable keys remain byte-for-byte stable.
|
|
260
|
+
...(params.nativeToolsEnabled === false ? ["tools:mcp-only"] : []),
|
|
254
261
|
].join("\0");
|
|
255
262
|
}
|
|
256
263
|
|
|
@@ -262,20 +269,23 @@ async function disposePoolEntry(entry: SessionCursorAgentPoolEntry, options?: {
|
|
|
262
269
|
} catch {
|
|
263
270
|
// disposal failure should not block session replacement
|
|
264
271
|
}
|
|
272
|
+
let disposal: Promise<unknown> | undefined;
|
|
265
273
|
try {
|
|
266
|
-
|
|
274
|
+
disposal = Promise.resolve(entry.agent[Symbol.asyncDispose]());
|
|
267
275
|
// A dead local transport may never settle SDK disposal; bound the wait so the
|
|
268
276
|
// next acquire recreates instead of hanging on the dead agent.
|
|
269
277
|
await (options?.deadTransport
|
|
270
278
|
? Promise.race([
|
|
271
279
|
disposal,
|
|
272
|
-
new Promise<void>((resolve) => setTimeout(resolve,
|
|
280
|
+
new Promise<void>((resolve) => setTimeout(resolve, DEAD_TRANSPORT_AGENT_DISPOSE_TIMEOUT_MS).unref?.()),
|
|
273
281
|
])
|
|
274
282
|
: disposal);
|
|
275
283
|
} catch {
|
|
276
284
|
// disposal failure should not block session replacement
|
|
277
285
|
}
|
|
278
286
|
await entry.sessionStore.dispose().catch(() => undefined);
|
|
287
|
+
// Timeout/rejection is not proof of closure; only successful SDK disposal releases ownership.
|
|
288
|
+
void disposal?.then(entry.releaseAgentClaim, () => {});
|
|
279
289
|
}
|
|
280
290
|
|
|
281
291
|
async function disposePoolEntryForScope(scopeKey: string, options?: { terminal?: boolean }): Promise<void> {
|
|
@@ -470,6 +480,7 @@ async function createSessionAgentEntry(
|
|
|
470
480
|
): Promise<SessionCursorAgentReadyEntry> {
|
|
471
481
|
let bridgeRun: CursorPiToolBridgeRun | undefined;
|
|
472
482
|
let sessionStore: OpenCursorSessionStore | undefined;
|
|
483
|
+
let releaseAgentClaim: (() => void) | undefined;
|
|
473
484
|
try {
|
|
474
485
|
const registeredBridge = params.bridge;
|
|
475
486
|
if (registeredBridge) {
|
|
@@ -495,7 +506,7 @@ async function createSessionAgentEntry(
|
|
|
495
506
|
const resumeHandle = resumeEligible ? getMatchingCursorSessionAgentResumeHandle(resolvedPoolKey, scopeKey) : undefined;
|
|
496
507
|
const storeSelection = await openCursorSessionStoreForScope({
|
|
497
508
|
cwd: params.cwd,
|
|
498
|
-
scopeKey,
|
|
509
|
+
scopeKey: params.scope?.persistentScopeKey ?? scopeKey,
|
|
499
510
|
persistent: persistentStore,
|
|
500
511
|
storeRoot: params.storeRoot,
|
|
501
512
|
resume: resumeHandle ? { identity: resumeHandle.storeIdentity, agentId: resumeHandle.agentId } : undefined,
|
|
@@ -513,6 +524,7 @@ async function createSessionAgentEntry(
|
|
|
513
524
|
localSafety: params.localSafety,
|
|
514
525
|
store: sessionStore!.store,
|
|
515
526
|
}),
|
|
527
|
+
...(params.nativeToolsEnabled === false ? { tools: ["mcp"] } : {}),
|
|
516
528
|
...(bridgeRun?.mcpServers ? { mcpServers: bridgeRun.mcpServers } : {}),
|
|
517
529
|
...(params.agents && Object.keys(params.agents).length ? { agents: params.agents } : {}),
|
|
518
530
|
});
|
|
@@ -520,11 +532,16 @@ async function createSessionAgentEntry(
|
|
|
520
532
|
let effectiveSendState = sendState;
|
|
521
533
|
let resumed = false;
|
|
522
534
|
if (resumeHandle && resumeAttemptAllowed && resumeAgent) {
|
|
535
|
+
releaseAgentClaim = claimCursorLocalAgent(sessionStore.identity, resumeHandle.agentId);
|
|
536
|
+
}
|
|
537
|
+
if (resumeHandle && resumeAttemptAllowed && resumeAgent && releaseAgentClaim) {
|
|
523
538
|
try {
|
|
524
539
|
agent = await resumeAgent(resumeHandle.agentId, buildAgentOptions());
|
|
525
540
|
effectiveSendState = { ...resumeHandle.sendState };
|
|
526
541
|
resumed = true;
|
|
527
542
|
} catch {
|
|
543
|
+
releaseAgentClaim();
|
|
544
|
+
releaseAgentClaim = undefined;
|
|
528
545
|
if (persistentStore) resumeNotice = LOCAL_RESUME_FALLBACK_NOTICE;
|
|
529
546
|
if (storeSelection.persistent && !cursorSessionStoreIdentitiesEqual(sessionStore.identity, storeSelection.identities.sessionStore)) {
|
|
530
547
|
const { identities } = storeSelection;
|
|
@@ -537,6 +554,8 @@ async function createSessionAgentEntry(
|
|
|
537
554
|
agent ??= await createAgent(buildAgentOptions());
|
|
538
555
|
if (!agent) throw new Error("Cursor SDK agent creation returned no agent");
|
|
539
556
|
if (!sessionStore) throw new Error("Cursor SDK session store was not opened");
|
|
557
|
+
releaseAgentClaim ??= claimCursorLocalAgent(sessionStore.identity, agent.agentId);
|
|
558
|
+
if (!releaseAgentClaim) throw new Error("Cursor SDK agent is already owned by another runtime");
|
|
540
559
|
|
|
541
560
|
return {
|
|
542
561
|
status: "ready",
|
|
@@ -550,12 +569,14 @@ async function createSessionAgentEntry(
|
|
|
550
569
|
sendState: effectiveSendState,
|
|
551
570
|
resumeEnabled: params.localResume === true,
|
|
552
571
|
resumed,
|
|
572
|
+
releaseAgentClaim,
|
|
553
573
|
...(resumeNotice ? { resumeNotice } : {}),
|
|
554
574
|
};
|
|
555
575
|
} catch (error) {
|
|
556
576
|
bridgeRun?.cancel("Cursor session agent create failed");
|
|
557
577
|
await bridgeRun?.dispose().catch(() => undefined);
|
|
558
578
|
await sessionStore?.dispose().catch(() => undefined);
|
|
579
|
+
releaseAgentClaim?.();
|
|
559
580
|
throw error;
|
|
560
581
|
}
|
|
561
582
|
}
|
|
@@ -568,7 +589,7 @@ export {
|
|
|
568
589
|
} from "./cursor-session-send-policy.js";
|
|
569
590
|
|
|
570
591
|
export function invalidateSessionAgent(
|
|
571
|
-
scopeKey: string
|
|
592
|
+
scopeKey: string,
|
|
572
593
|
options?: { deadTransport?: boolean },
|
|
573
594
|
): void {
|
|
574
595
|
invalidatedScopeKeys.add(scopeKey);
|
|
@@ -576,9 +597,9 @@ export function invalidateSessionAgent(
|
|
|
576
597
|
}
|
|
577
598
|
|
|
578
599
|
export async function acquireSessionCursorAgent(input: SessionCursorAgentCreateParams): Promise<SessionCursorAgentLease> {
|
|
579
|
-
const params = { ...input, bridge: "bridge" in input ? input.bridge : getRegisteredCursorPiToolBridge() };
|
|
580
|
-
const scopeKey = params.scope
|
|
581
|
-
const persistentStore =
|
|
600
|
+
const params = { ...input, scope: input.scope ?? getCursorSessionScopeSnapshot(), bridge: "bridge" in input ? input.bridge : getRegisteredCursorPiToolBridge() };
|
|
601
|
+
const scopeKey = params.scope.scopeKey;
|
|
602
|
+
const persistentStore = params.scope.sessionFile !== undefined;
|
|
582
603
|
params.storeRoot = persistentStore && params.storeRoot !== undefined ? normalizeCursorCustomStoreRoot(params.storeRoot) : undefined;
|
|
583
604
|
|
|
584
605
|
while (true) {
|
|
@@ -709,11 +730,6 @@ export const __testUtils = {
|
|
|
709
730
|
resetSessionCursorAgent,
|
|
710
731
|
refreshSessionCursorAgentConfig,
|
|
711
732
|
disposeAllSessionCursorAgents,
|
|
712
|
-
setDeadTransportAgentDisposeTimeoutMs(ms: number): number {
|
|
713
|
-
const previous = deadTransportAgentDisposeTimeoutMs;
|
|
714
|
-
deadTransportAgentDisposeTimeoutMs = ms;
|
|
715
|
-
return previous;
|
|
716
|
-
},
|
|
717
733
|
SessionCursorAgentCreationSupersededError,
|
|
718
734
|
SessionCursorAgentScopeClosedError,
|
|
719
735
|
};
|