@agent-link/agent 0.1.339 → 0.1.341

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.
@@ -1,94 +1,185 @@
1
1
  import { closeSync, openSync, readSync, statSync } from 'fs';
2
+ import { open, stat } from 'fs/promises';
3
+ import { setImmediate as yieldScan } from 'timers/promises';
2
4
  import { isCopilotChildEvent } from './copilot-event-scope.js';
3
5
  const cache = new Map();
6
+ const inFlight = new Map();
7
+ const MAX_CACHE_ENTRIES = 10000;
8
+ const MAX_CACHE_BYTES = 8 * 1024 * 1024;
9
+ let cacheBytes = 0;
10
+ const entryBytes = (path) => Buffer.byteLength(path) + 192;
4
11
  const activityTypes = new Set(['user.message', 'assistant.message', 'tool.execution_start',
5
12
  'tool.execution_complete', 'session.task_complete']);
6
- /** Sorting is conversation activity, not Provider maintenance. Native journals
7
- * remain untouched. Read only appended bytes after the first reconstruction. */
13
+ function activityState(path, stats) {
14
+ const state = cache.get(path);
15
+ if (!state || stats.size < state.size || stats.birthtimeMs !== state.birthtime || stats.ino !== state.ino
16
+ || (stats.size === state.size && stats.mtimeMs !== state.mtime)) {
17
+ return { offset: 0, size: 0, mtime: 0, birthtime: stats.birthtimeMs, ino: stats.ino,
18
+ compacting: false, hasCompaction: false };
19
+ }
20
+ return { ...state };
21
+ }
22
+ function consumeEvent(state, line) {
23
+ let event;
24
+ try {
25
+ event = JSON.parse(line);
26
+ }
27
+ catch {
28
+ return;
29
+ }
30
+ if (!event || typeof event !== 'object')
31
+ return;
32
+ const child = isCopilotChildEvent(event);
33
+ const type = event.type;
34
+ if (type === 'session.compaction_start') {
35
+ state.hasCompaction = true;
36
+ if (!child)
37
+ state.compacting = true;
38
+ return;
39
+ }
40
+ if (type === 'session.compaction_complete' || type === 'session.compaction_failed') {
41
+ state.hasCompaction = true;
42
+ if (!child)
43
+ state.compacting = false;
44
+ return;
45
+ }
46
+ const data = event.data || {};
47
+ if (child || (typeof data.source === 'string' && data.source.startsWith('agent-')))
48
+ return;
49
+ if (type === 'user.message') {
50
+ if (/^\s*\/compact(?:\s|$)/i.test(data.content || ''))
51
+ return;
52
+ state.compacting = false;
53
+ }
54
+ if (type === 'abort') {
55
+ state.compacting = false;
56
+ return;
57
+ }
58
+ // A real final answer may overlap the native background compactor.
59
+ if ((state.compacting && !(type === 'assistant.message' && data.phase === 'final_answer'))
60
+ || !activityTypes.has(type))
61
+ return;
62
+ const at = Date.parse(event.timestamp || '');
63
+ if (Number.isFinite(at))
64
+ state.lastActivity = Math.max(state.lastActivity ?? 0, at);
65
+ }
66
+ function activityReader(state) {
67
+ let parts = [];
68
+ let length = 0;
69
+ return (chunk) => {
70
+ let start = 0;
71
+ for (let end = chunk.indexOf(10); end >= 0; end = chunk.indexOf(10, start)) {
72
+ const tail = chunk.subarray(start, end);
73
+ const line = parts.length ? Buffer.concat([...parts, tail], length + tail.length) : tail;
74
+ consumeEvent(state, line.toString('utf8'));
75
+ state.offset += length + tail.length + 1;
76
+ parts = [];
77
+ length = 0;
78
+ start = end + 1;
79
+ }
80
+ if (start < chunk.length) {
81
+ const remaining = Buffer.from(chunk.subarray(start));
82
+ parts.push(remaining);
83
+ length += remaining.length;
84
+ }
85
+ };
86
+ }
87
+ function saveActivity(path, state, stats, fallback, createdAt) {
88
+ state.mtime = stats.mtimeMs;
89
+ state.size = stats.size;
90
+ state.lastSort = state.hasCompaction
91
+ ? state.lastActivity ?? createdAt ?? fallback
92
+ : Math.max(state.lastActivity ?? 0, fallback);
93
+ if (cache.delete(path))
94
+ cacheBytes -= entryBytes(path);
95
+ cache.set(path, state);
96
+ cacheBytes += entryBytes(path);
97
+ while (cache.size > MAX_CACHE_ENTRIES || cacheBytes > MAX_CACHE_BYTES) {
98
+ const oldest = cache.keys().next().value;
99
+ cache.delete(oldest);
100
+ cacheBytes -= entryBytes(oldest);
101
+ }
102
+ return state.lastSort;
103
+ }
104
+ function readFailure(path, fallback, error) {
105
+ if (error.code !== 'ENOENT') {
106
+ console.warn(`[Copilot] Unable to read session activity ${path}: ${String(error)}`);
107
+ }
108
+ // A temporary Provider lock must not promote maintenance over confirmed activity.
109
+ return cache.get(path)?.lastSort ?? fallback;
110
+ }
111
+ /** Cached state contains no journal text. An unfinished record is reread on the
112
+ * next request, so a large native record cannot consume the cache's memory budget. */
8
113
  export function copilotSessionActivity(path, fallback, createdAt) {
9
114
  let fd;
10
115
  try {
11
116
  const stats = statSync(path);
12
- let state = cache.get(path);
13
- if (!state || stats.size < state.offset || stats.birthtimeMs !== state.birthtime
14
- || (stats.size === state.offset && stats.mtimeMs !== state.mtime)) {
15
- state = { offset: 0, partial: Buffer.alloc(0), mtime: 0, birthtime: stats.birthtimeMs,
16
- compacting: false, hasCompaction: false };
17
- }
117
+ const state = activityState(path, stats);
118
+ const consume = activityReader(state);
18
119
  if (stats.size > state.offset) {
19
120
  fd = openSync(path, 'r');
20
121
  const chunk = Buffer.alloc(64 * 1024);
21
- while (state.offset < stats.size) {
22
- const read = readSync(fd, chunk, 0, Math.min(chunk.length, stats.size - state.offset), state.offset);
122
+ let position = state.offset;
123
+ while (position < stats.size) {
124
+ const read = readSync(fd, chunk, 0, Math.min(chunk.length, stats.size - position), position);
23
125
  if (!read)
24
126
  break;
25
- state.offset += read;
26
- const bytes = Buffer.concat([state.partial, chunk.subarray(0, read)]);
27
- let start = 0;
28
- for (let end = bytes.indexOf(10); end >= 0; end = bytes.indexOf(10, start)) {
29
- const line = bytes.subarray(start, end).toString('utf8');
30
- start = end + 1;
31
- try {
32
- const event = JSON.parse(line);
33
- const child = isCopilotChildEvent(event);
34
- const type = event.type;
35
- if (type === 'session.compaction_start') {
36
- state.hasCompaction = true;
37
- if (!child)
38
- state.compacting = true;
39
- continue;
40
- }
41
- if (type === 'session.compaction_complete' || type === 'session.compaction_failed') {
42
- state.hasCompaction = true;
43
- if (!child)
44
- state.compacting = false;
45
- continue;
46
- }
47
- // A new real request also bounds an abandoned compression after restart.
48
- const data = event.data || {};
49
- if (child || (typeof data.source === 'string' && data.source.startsWith('agent-')))
50
- continue;
51
- if (type === 'user.message') {
52
- if (/^\s*\/compact(?:\s|$)/i.test(data.content || ''))
53
- continue;
54
- state.compacting = false;
55
- }
56
- if (type === 'abort') {
57
- state.compacting = false;
58
- continue;
59
- }
60
- // A native background compactor can overlap the real final answer.
61
- // That explicitly identified answer is activity, not maintenance.
62
- if ((state.compacting && !(type === 'assistant.message' && data.phase === 'final_answer'))
63
- || !activityTypes.has(type))
64
- continue;
65
- const at = Date.parse(event.timestamp || '');
66
- if (Number.isFinite(at))
67
- state.lastActivity = Math.max(state.lastActivity ?? 0, at);
68
- }
69
- catch { /* Ignore incomplete/legacy native records, never rewrite them. */ }
70
- }
71
- state.partial = Buffer.from(bytes.subarray(start));
127
+ position += read;
128
+ consume(chunk.subarray(0, read));
72
129
  }
73
130
  }
74
- state.mtime = stats.mtimeMs;
75
- state.lastSort = state.hasCompaction
76
- ? state.lastActivity ?? createdAt ?? fallback
77
- : Math.max(state.lastActivity ?? 0, fallback);
78
- cache.delete(path);
79
- cache.set(path, state);
80
- if (cache.size > 256)
81
- cache.delete(cache.keys().next().value);
82
- return state.lastSort;
131
+ return saveActivity(path, state, stats, fallback, createdAt);
83
132
  }
84
- catch {
85
- // A Provider flush/temporary lock must not replace confirmed conversation
86
- // activity with the newer maintenance timestamp.
87
- return cache.get(path)?.lastSort ?? fallback;
133
+ catch (error) {
134
+ return readFailure(path, fallback, error);
88
135
  }
89
136
  finally {
90
137
  if (fd != null)
91
138
  closeSync(fd);
92
139
  }
93
140
  }
141
+ export async function copilotSessionActivityAsync(path, fallback, createdAt) {
142
+ // Serialize a journal's readers without mixing their metadata fallbacks.
143
+ const previous = inFlight.get(path);
144
+ const reading = previous
145
+ ? previous.then(() => readActivityAsync(path, fallback, createdAt))
146
+ : readActivityAsync(path, fallback, createdAt);
147
+ inFlight.set(path, reading);
148
+ try {
149
+ return await reading;
150
+ }
151
+ finally {
152
+ if (inFlight.get(path) === reading)
153
+ inFlight.delete(path);
154
+ }
155
+ }
156
+ async function readActivityAsync(path, fallback, createdAt) {
157
+ try {
158
+ const stats = await stat(path);
159
+ const state = activityState(path, stats);
160
+ const consume = activityReader(state);
161
+ if (stats.size > state.offset) {
162
+ const file = await open(path, 'r');
163
+ try {
164
+ const chunk = Buffer.alloc(64 * 1024);
165
+ let position = state.offset;
166
+ while (position < stats.size) {
167
+ const { bytesRead } = await file.read(chunk, 0, Math.min(chunk.length, stats.size - position), position);
168
+ if (!bytesRead)
169
+ break;
170
+ position += bytesRead;
171
+ consume(chunk.subarray(0, bytesRead));
172
+ await yieldScan();
173
+ }
174
+ }
175
+ finally {
176
+ await file.close();
177
+ }
178
+ }
179
+ return saveActivity(path, state, stats, fallback, createdAt);
180
+ }
181
+ catch (error) {
182
+ return readFailure(path, fallback, error);
183
+ }
184
+ }
94
185
  //# sourceMappingURL=copilot-session-activity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"copilot-session-activity.js","sourceRoot":"","sources":["../src/copilot-session-activity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"copilot-session-activity.js","sourceRoot":"","sources":["../src/copilot-session-activity.ts"],"names":[],"mappings":"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"}
package/dist/copilot.d.ts CHANGED
@@ -280,6 +280,10 @@ interface CopilotGlobalSessionPage {
280
280
  nextCursor?: string;
281
281
  indexState: 'ready';
282
282
  }
283
+ export declare function listCopilotSessionsPageAsync(workDir: string, options?: {
284
+ limit?: number;
285
+ cursor?: string;
286
+ }): Promise<CopilotSessionPage>;
283
287
  export declare function listCopilotSessions(workDir: string, options?: {
284
288
  strictReadErrors?: boolean;
285
289
  }): SessionInfo[];
@@ -309,6 +313,10 @@ export declare function listAllCopilotSessionsPage(options?: {
309
313
  limit?: number;
310
314
  cursor?: string;
311
315
  }): CopilotGlobalSessionPage;
316
+ export declare function listAllCopilotSessionsPageAsync(options?: {
317
+ limit?: number;
318
+ cursor?: string;
319
+ }): Promise<CopilotGlobalSessionPage>;
312
320
  export declare function renameCopilotSession(_workDir: string, sessionId: string, newTitle: string): boolean;
313
321
  export declare function moveCopilotSession(_fromWorkDir: string, toWorkDir: string, sessionId: string): boolean;
314
322
  export declare function deleteCopilotSession(_workDir: string, sessionId: string): Promise<boolean>;
package/dist/copilot.js CHANGED
@@ -9,7 +9,9 @@
9
9
  */
10
10
  import { spawn } from 'child_process';
11
11
  import { createInterface } from 'readline';
12
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
12
+ import { closeSync, createReadStream, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
13
+ import { readFile, readdir } from 'fs/promises';
14
+ import { setImmediate as yieldSessionScan } from 'timers/promises';
13
15
  import { atomicWriteCopilotSidecar } from './copilot-sidecar.js';
14
16
  import { basename, dirname, extname, join, resolve } from 'path';
15
17
  import { homedir, platform } from 'os';
@@ -21,7 +23,7 @@ import { readCopilotUserRequests, saveCopilotUserRequest, mergeCopilotUserReques
21
23
  import { copilotChildScope, copilotParentToolCallId, isCopilotChildEvent } from './copilot-event-scope.js';
22
24
  import { CopilotCompactionBarrier, compactionFencePath, readCompactionFences, compactionPublicationHidden } from './copilot-compaction-barrier.js';
23
25
  import { CopilotCompactionLifecycle } from './copilot-compaction-lifecycle.js';
24
- import { copilotSessionActivity } from './copilot-session-activity.js';
26
+ import { copilotSessionActivity, copilotSessionActivityAsync } from './copilot-session-activity.js';
25
27
  import { copilotPlanStateAllowsImplementation, stripCopilotPlanControl, readCopilotPlanRevision, findRecoverableCopilotPlan, sealCopilotPlanState, holdsRecoveredCopilotPlanMode, clearRecoveredCopilotPlanMode } from './copilot-plan-state.js';
26
28
  import * as acp from '@agentclientprotocol/sdk';
27
29
  import { getForkHistoryTitle, processUserMessage } from './history-message-transform.js';
@@ -4604,8 +4606,8 @@ function hasCopilotForkOrigin(sessionId) {
4604
4606
  }
4605
4607
  return false;
4606
4608
  }
4607
- function parseCopilotWorkspaceYaml(filePath, stats) {
4608
- const yaml = parseSimpleYaml(readFileSync(filePath, 'utf-8'));
4609
+ function copilotWorkspaceMetadata(filePath, content) {
4610
+ const yaml = parseSimpleYaml(content);
4609
4611
  const sourceDir = filePath.replace(/\\/g, '/').split('/').slice(-2, -1)[0] || '';
4610
4612
  const cwd = yaml.cwd || '';
4611
4613
  const updatedAt = yaml.updated_at ? new Date(yaml.updated_at).getTime() : 0;
@@ -4627,45 +4629,14 @@ function parseCopilotWorkspaceYaml(filePath, stats) {
4627
4629
  console.warn(`[Copilot] Fork numbering unavailable for ${sessionId}: ${String(error)}`);
4628
4630
  }
4629
4631
  }
4630
- if (title.includes(AGENCY_LONG_CONTEXT_BOOTSTRAP_MARKER) || forkTitle !== null) {
4632
+ const recoverTitle = title.includes(AGENCY_LONG_CONTEXT_BOOTSTRAP_MARKER) || forkTitle !== null;
4633
+ if (recoverTitle)
4631
4634
  title = forkTitle || (forkTitle !== null ? 'Forked conversation' : 'Untitled');
4632
- const eventsPath = join(dirname(filePath), 'events.jsonl');
4633
- if (existsSync(eventsPath)) {
4634
- try {
4635
- for (const line of readFileSync(eventsPath, 'utf8').split(/\r?\n/)) {
4636
- if (!line.trim())
4637
- continue;
4638
- let event;
4639
- try {
4640
- event = JSON.parse(line);
4641
- }
4642
- catch {
4643
- continue;
4644
- }
4645
- if (event.parentToolCallId || event.data?.parentToolCallId || event.data?.source?.startsWith('agent-'))
4646
- continue;
4647
- const content = event.type === 'user.message' && typeof event.data?.content === 'string'
4648
- ? stripAgencyPlanInteractionGuidance(event.data.content).trim()
4649
- : '';
4650
- if (!content || content.includes(AGENCY_LONG_CONTEXT_BOOTSTRAP_MARKER))
4651
- continue;
4652
- const recoveredForkTitle = getForkHistoryTitle(content);
4653
- if (recoveredForkTitle === '')
4654
- continue;
4655
- title = recoveredForkTitle ?? content;
4656
- break;
4657
- }
4658
- }
4659
- catch {
4660
- // Keep the hidden initializer untitled until Provider history is readable.
4661
- }
4662
- }
4663
- }
4664
- return {
4635
+ const session = {
4665
4636
  sessionId,
4666
4637
  title,
4667
4638
  preview: title.length > 60 ? title.substring(0, 60) + '...' : title,
4668
- lastModified: copilotSessionActivity(join(dirname(filePath), 'events.jsonl'), updatedAt || stats.mtimeMs, yaml.created_at ? Date.parse(yaml.created_at) || undefined : undefined),
4639
+ lastModified: updatedAt,
4669
4640
  projectPath: cwd,
4670
4641
  projectFolder: folderName,
4671
4642
  sourcePath: filePath,
@@ -4674,10 +4645,88 @@ function parseCopilotWorkspaceYaml(filePath, stats) {
4674
4645
  ...(forkMetadata ? { forkNumber: forkMetadata.number,
4675
4646
  ...(forkMetadata.sourceSessionId ? { forkSourceSessionId: forkMetadata.sourceSessionId } : {}) } : {}),
4676
4647
  };
4648
+ return { session, recoverTitle, createdAt: yaml.created_at ? Date.parse(yaml.created_at) || undefined : undefined };
4649
+ }
4650
+ function copilotWorkspaceTitle(line) {
4651
+ if (!line.trim())
4652
+ return;
4653
+ let event;
4654
+ try {
4655
+ event = JSON.parse(line);
4656
+ }
4657
+ catch {
4658
+ return;
4659
+ }
4660
+ if (!event || typeof event !== 'object')
4661
+ return;
4662
+ if (event.parentToolCallId || event.data?.parentToolCallId || event.data?.source?.startsWith('agent-'))
4663
+ return;
4664
+ const content = event.type === 'user.message' && typeof event.data?.content === 'string'
4665
+ ? stripAgencyPlanInteractionGuidance(event.data.content).trim() : '';
4666
+ if (!content || content.includes(AGENCY_LONG_CONTEXT_BOOTSTRAP_MARKER))
4667
+ return;
4668
+ const forkTitle = getForkHistoryTitle(content);
4669
+ return forkTitle === '' ? undefined : forkTitle ?? content;
4670
+ }
4671
+ function parseCopilotWorkspaceYaml(filePath, stats) {
4672
+ const { session, recoverTitle, createdAt } = copilotWorkspaceMetadata(filePath, readFileSync(filePath, 'utf8'));
4673
+ const eventsPath = join(dirname(filePath), 'events.jsonl');
4674
+ if (recoverTitle && existsSync(eventsPath)) {
4675
+ try {
4676
+ for (const line of readFileSync(eventsPath, 'utf8').split(/\r?\n/)) {
4677
+ const title = copilotWorkspaceTitle(line);
4678
+ if (title !== undefined) {
4679
+ session.title = title;
4680
+ break;
4681
+ }
4682
+ }
4683
+ }
4684
+ catch (error) {
4685
+ console.warn(`[Copilot] Unable to recover session title ${filePath}: ${String(error)}`);
4686
+ }
4687
+ }
4688
+ session.preview = session.title.length > 60 ? session.title.substring(0, 60) + '...' : session.title;
4689
+ session.lastModified = copilotSessionActivity(eventsPath, session.lastModified || stats.mtimeMs, createdAt);
4690
+ return session;
4691
+ }
4692
+ async function parseCopilotWorkspaceYamlAsync(filePath, stats) {
4693
+ const { session, recoverTitle, createdAt } = copilotWorkspaceMetadata(filePath, await readFile(filePath, 'utf8'));
4694
+ const eventsPath = join(dirname(filePath), 'events.jsonl');
4695
+ if (recoverTitle) {
4696
+ const input = createReadStream(eventsPath, { encoding: 'utf8', highWaterMark: 64 * 1024 });
4697
+ const lines = createInterface({ input, crlfDelay: Infinity });
4698
+ try {
4699
+ let lastYield = performance.now();
4700
+ for await (const line of lines) {
4701
+ const title = copilotWorkspaceTitle(line);
4702
+ if (title !== undefined) {
4703
+ session.title = title;
4704
+ break;
4705
+ }
4706
+ if (performance.now() - lastYield >= 8) {
4707
+ await yieldSessionScan();
4708
+ lastYield = performance.now();
4709
+ }
4710
+ }
4711
+ }
4712
+ catch (error) {
4713
+ if (error.code !== 'ENOENT') {
4714
+ console.warn(`[Copilot] Unable to recover session title ${filePath}: ${String(error)}`);
4715
+ }
4716
+ }
4717
+ finally {
4718
+ lines.close();
4719
+ input.destroy();
4720
+ }
4721
+ }
4722
+ session.preview = session.title.length > 60 ? session.title.substring(0, 60) + '...' : session.title;
4723
+ session.lastModified = await copilotSessionActivityAsync(eventsPath, session.lastModified || stats.mtimeMs, createdAt);
4724
+ return session;
4677
4725
  }
4678
4726
  const copilotWorkspaceCache = new FileCache({
4679
4727
  name: 'copilot-workspace',
4680
4728
  compute: parseCopilotWorkspaceYaml,
4729
+ computeAsync: parseCopilotWorkspaceYamlAsync,
4681
4730
  maxEntries: 10000,
4682
4731
  ttlMs: 0,
4683
4732
  debug: false,
@@ -4755,11 +4804,61 @@ function getCopilotSessionEntries(workDir, strictReadErrors = false) {
4755
4804
  entries.sort((a, b) => compareCursorSortKeys(a.sortKey, b.sortKey));
4756
4805
  return entries;
4757
4806
  }
4807
+ async function getCopilotSessionEntriesAsync(workDir) {
4808
+ const normalizedWorkDir = workDir ? normalizeCursorPath(workDir) : '';
4809
+ const entries = [];
4810
+ let dirs;
4811
+ try {
4812
+ dirs = await readdir(COPILOT_SESSION_DIR, { withFileTypes: true });
4813
+ }
4814
+ catch (error) {
4815
+ if (error.code === 'ENOENT')
4816
+ return entries;
4817
+ throw error;
4818
+ }
4819
+ for (const dir of dirs) {
4820
+ if (!dir.isDirectory())
4821
+ continue;
4822
+ const wsPath = join(COPILOT_SESSION_DIR, dir.name, 'workspace.yaml');
4823
+ const parsed = await copilotWorkspaceCache.getAsync(wsPath);
4824
+ if (!parsed)
4825
+ continue;
4826
+ if (workDir !== undefined && (!parsed.projectPath || normalizeCursorPath(parsed.projectPath) !== normalizedWorkDir))
4827
+ continue;
4828
+ const lastModified = await copilotSessionActivityAsync(join(dirname(wsPath), 'events.jsonl'), parsed.lastModified);
4829
+ entries.push({ session: { ...parsed, lastModified }, sortKey: {
4830
+ lastModified, sessionId: parsed.sessionId, tieBreaker: normalizeCursorPath(wsPath),
4831
+ } });
4832
+ await yieldSessionScan();
4833
+ }
4834
+ entries.sort((a, b) => compareCursorSortKeys(a.sortKey, b.sortKey));
4835
+ return entries;
4836
+ }
4837
+ const inFlightCopilotPages = new Map();
4838
+ const inFlightGlobalCopilotPages = new Map();
4839
+ async function copilotPageAsync(pendingPages, workDir, options, paginate) {
4840
+ const key = JSON.stringify([workDir === undefined ? null : normalizeCursorPath(workDir), normalizeSessionLimit(options.limit), options.cursor || '']);
4841
+ let pending = pendingPages.get(key);
4842
+ if (!pending) {
4843
+ pending = getCopilotSessionEntriesAsync(workDir).then(paginate);
4844
+ pendingPages.set(key, pending);
4845
+ const release = () => { if (pendingPages.get(key) === pending)
4846
+ pendingPages.delete(key); };
4847
+ pending.then(release, release);
4848
+ }
4849
+ return structuredClone(await pending);
4850
+ }
4851
+ export function listCopilotSessionsPageAsync(workDir, options = {}) {
4852
+ return copilotPageAsync(inFlightCopilotPages, workDir, options, entries => copilotSessionsPage(entries, workDir, options));
4853
+ }
4758
4854
  export function listCopilotSessions(workDir, options = {}) {
4759
4855
  return listCopilotSessionsPage(workDir, { ...options, limit: Number.POSITIVE_INFINITY }).sessions;
4760
4856
  }
4761
4857
  export function listCopilotSessionsPage(workDir, options = {}) {
4762
4858
  const entries = getCopilotSessionEntries(workDir, options.strictReadErrors);
4859
+ return copilotSessionsPage(entries, workDir, options);
4860
+ }
4861
+ function copilotSessionsPage(entries, workDir, options) {
4763
4862
  const limit = normalizeSessionLimit(options.limit);
4764
4863
  let startIndex = 0;
4765
4864
  if (options.cursor) {
@@ -5811,6 +5910,12 @@ export function listAllCopilotSessions(limit = 500) {
5811
5910
  }
5812
5911
  export function listAllCopilotSessionsPage(options = {}) {
5813
5912
  const entries = getCopilotSessionEntries();
5913
+ return allCopilotSessionsPage(entries, options);
5914
+ }
5915
+ export function listAllCopilotSessionsPageAsync(options = {}) {
5916
+ return copilotPageAsync(inFlightGlobalCopilotPages, undefined, options, entries => allCopilotSessionsPage(entries, options));
5917
+ }
5918
+ function allCopilotSessionsPage(entries, options) {
5814
5919
  const limit = normalizeSessionLimit(options.limit);
5815
5920
  let startIndex = 0;
5816
5921
  if (options.cursor) {