@tt-a1i/openpi 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -59
- package/SETUP.md +23 -7
- package/assets/openpi-launch-card-v1.webp +0 -0
- package/bin/openpi.js +145 -0
- package/extensions/ask-user/index.ts +30 -14
- package/extensions/background-terminals/index.ts +30 -2
- package/extensions/background-terminals/src/domain.ts +2 -0
- package/extensions/background-terminals/src/manager.ts +486 -106
- package/extensions/background-terminals/src/output.ts +33 -0
- package/extensions/background-terminals/src/prompt.ts +14 -6
- package/extensions/background-terminals/src/result-delivery.ts +4 -1
- package/extensions/background-terminals/src/ui/ps.ts +132 -129
- package/extensions/capabilities/index.ts +30 -42
- package/extensions/capabilities/src/ui.ts +93 -0
- package/extensions/clear-context/index.ts +83 -0
- package/extensions/context-pivot/index.ts +16 -6
- package/extensions/cron/schedule.ts +7 -1
- package/extensions/file-mutation-display/index.ts +34 -76
- package/extensions/file-mutation-display/render.ts +146 -87
- package/extensions/file-search/index.ts +8 -7
- package/extensions/file-search/src/binaries.ts +75 -59
- package/extensions/git-info/src/changed-files-view.ts +47 -14
- package/extensions/git-read/index.ts +328 -0
- package/extensions/git-read/src/args.ts +171 -0
- package/extensions/git-read/src/process.ts +81 -0
- package/extensions/git-read/src/prompt.ts +56 -0
- package/extensions/model-info/index.ts +21 -33
- package/extensions/model-info/session-metrics.ts +96 -0
- package/extensions/plan-mode/bash-policy.ts +54 -9
- package/extensions/plan-mode/index.ts +7 -2
- package/extensions/post-edit/index.ts +16 -6
- package/extensions/sessions/git-stats.ts +258 -72
- package/extensions/sessions/index.ts +222 -140
- package/extensions/sessions/preview-cache.ts +104 -0
- package/extensions/sessions/preview-loader.ts +856 -0
- package/extensions/sessions/sessions.ts +43 -4
- package/extensions/setup/index.ts +127 -131
- package/extensions/shared/activity-status.ts +36 -5
- package/extensions/shared/agent-session-page.ts +319 -0
- package/extensions/shared/agent-tool-renderer.ts +218 -0
- package/extensions/shared/agent-transcript.ts +524 -0
- package/extensions/shared/below-editor-navigation.ts +26 -0
- package/extensions/shared/capability-intent.ts +53 -0
- package/extensions/shared/child-session.ts +444 -22
- package/extensions/shared/result-budget.ts +134 -0
- package/extensions/shared/result-delivery.ts +34 -0
- package/extensions/shared/screen-chrome.ts +133 -0
- package/extensions/shared/setup-config.ts +97 -38
- package/extensions/shared/setup-episode-state.ts +1 -1
- package/extensions/shared/spinner.ts +28 -0
- package/extensions/shared/terminal-text.ts +110 -23
- package/extensions/shared/text-projection.ts +113 -0
- package/extensions/shared/tool-activity.ts +382 -0
- package/extensions/shared/tool-surface.ts +42 -8
- package/extensions/shared/transcript-viewport.ts +46 -0
- package/extensions/shared/web-observer-registry.ts +390 -0
- package/extensions/shared/worktree.ts +11 -0
- package/extensions/subagents/index.ts +461 -186
- package/extensions/subagents/navigation.ts +86 -28
- package/extensions/subagents/src/agent-types.ts +37 -15
- package/extensions/subagents/src/backend.ts +12 -1
- package/extensions/subagents/src/backends/pi.ts +375 -66
- package/extensions/subagents/src/domain.ts +5 -0
- package/extensions/subagents/src/id-sequence.ts +84 -0
- package/extensions/subagents/src/manager.ts +651 -536
- package/extensions/subagents/src/prompt.ts +185 -42
- package/extensions/subagents/src/result-artifact.ts +146 -0
- package/extensions/subagents/src/result-delivery.ts +7 -1
- package/extensions/subagents/src/runtime.ts +23 -6
- package/extensions/subagents/src/ui/takeover.ts +128 -337
- package/extensions/subagents/src/ui/transcript.ts +38 -501
- package/extensions/subagents/src/ui/wait-result.ts +103 -15
- package/extensions/suggestions/src/ui.ts +10 -4
- package/extensions/tasks/index.ts +0 -3
- package/extensions/tasks/ui.ts +79 -62
- package/extensions/ui-customization/footer.ts +7 -44
- package/extensions/ui-customization/index.ts +0 -4
- package/extensions/user-input-fold/index.ts +185 -0
- package/extensions/web/index.ts +234 -0
- package/extensions/workflows/artifacts.ts +147 -22
- package/extensions/workflows/completion-projection.ts +457 -0
- package/extensions/workflows/controller.ts +14 -2
- package/extensions/workflows/coordinator.ts +62 -0
- package/extensions/workflows/dashboard.ts +458 -339
- package/extensions/workflows/handoff.ts +121 -25
- package/extensions/workflows/index.ts +1042 -492
- package/extensions/workflows/journal.ts +148 -13
- package/extensions/workflows/model.ts +131 -19
- package/extensions/workflows/navigation.ts +61 -18
- package/extensions/workflows/progress-projection.ts +306 -0
- package/extensions/workflows/prompt.ts +166 -10
- package/extensions/workflows/replay-safety.ts +58 -27
- package/extensions/workflows/result-delivery.ts +253 -0
- package/extensions/workflows/retention.ts +593 -0
- package/extensions/workflows/runner.ts +388 -279
- package/extensions/workflows/sandbox-child.cjs +36 -3
- package/extensions/workflows/sandbox.ts +62 -8
- package/extensions/workflows/serialization.ts +325 -17
- package/extensions/workflows/tool-renderer.ts +22 -0
- package/extensions/workflows/transcript.ts +149 -0
- package/extensions/workspace-cleanup-guard/index.ts +54 -0
- package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
- package/package.json +28 -8
- package/skills/subagents/REFERENCE.md +189 -0
- package/skills/subagents/SKILL.md +2 -2
- package/skills/workflows/REFERENCE.md +10 -5
- package/skills/workflows/SKILL.md +53 -10
- package/web/adapter/pi-adapter.ts +661 -0
- package/web/host/browser-launcher.ts +20 -0
- package/web/host/static-assets.ts +4 -0
- package/web/host/terminal-status.ts +38 -0
- package/web/host/web-host.ts +789 -0
- package/web/http-dispatcher.ts +125 -0
- package/web/protocol/types.ts +462 -0
- package/web/runtime/pi-runtime.ts +991 -0
- package/web/runtime/types.ts +71 -0
- package/web/runtime/web-host-lease.ts +497 -0
- package/web/trace.ts +18 -0
- package/web/ui/app.js +1398 -0
- package/web/ui/index.html +139 -0
- package/web/ui/styles.css +598 -0
- package/web/vite.config.mjs +34 -0
- package/extensions/execution-convergence/active-evidence.ts +0 -129
- package/extensions/execution-convergence/index.ts +0 -442
- package/extensions/execution-convergence/workspace-provenance.ts +0 -338
- package/extensions/setup/intercom-fs-helper.cjs +0 -130
- package/extensions/setup/intercom.ts +0 -603
- package/extensions/subagents/src/backends/stub.ts +0 -296
- package/extensions/subagents/src/format.ts +0 -48
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
2
4
|
import type { SessionInfoLike } from "./sessions.ts";
|
|
3
5
|
|
|
4
6
|
export interface SessionStats {
|
|
@@ -7,8 +9,15 @@ export interface SessionStats {
|
|
|
7
9
|
del: number;
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
export type SessionStatsState =
|
|
13
|
+
| { status: "pending" }
|
|
14
|
+
| { status: "ready"; stats: SessionStats }
|
|
15
|
+
| { status: "unavailable" };
|
|
16
|
+
|
|
17
|
+
type CachedSessionStats = Exclude<SessionStatsState, { status: "pending" }>;
|
|
18
|
+
|
|
10
19
|
interface StatsLoaderOptions {
|
|
11
|
-
cache?: Map<string,
|
|
20
|
+
cache?: Map<string, CachedSessionStats>;
|
|
12
21
|
maxCache?: number;
|
|
13
22
|
maxConcurrency?: number;
|
|
14
23
|
runGit?: (
|
|
@@ -16,6 +25,8 @@ interface StatsLoaderOptions {
|
|
|
16
25
|
cwd: string,
|
|
17
26
|
signal: AbortSignal,
|
|
18
27
|
) => Promise<string>;
|
|
28
|
+
canonicalizeCwd?: (cwd: string) => Promise<string>;
|
|
29
|
+
canonicalizeConcurrency?: number;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
interface QueueEntry {
|
|
@@ -27,6 +38,7 @@ interface QueueEntry {
|
|
|
27
38
|
const GIT_STATS_TIMEOUT_MS = 3_000;
|
|
28
39
|
const DEFAULT_MAX_CACHE = 256;
|
|
29
40
|
const DEFAULT_MAX_CONCURRENCY = 4;
|
|
41
|
+
const DEFAULT_CANONICALIZE_CONCURRENCY = 8;
|
|
30
42
|
|
|
31
43
|
const abortError = () => {
|
|
32
44
|
const error = new Error("Session stats load was cancelled.");
|
|
@@ -97,7 +109,7 @@ function createQueue(maxConcurrency: number) {
|
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
const runGit = (args: string[], cwd: string, signal: AbortSignal) =>
|
|
100
|
-
new Promise<string>((
|
|
112
|
+
new Promise<string>((resolveOutput, reject) => {
|
|
101
113
|
try {
|
|
102
114
|
execFile(
|
|
103
115
|
"git",
|
|
@@ -108,10 +120,13 @@ const runGit = (args: string[], cwd: string, signal: AbortSignal) =>
|
|
|
108
120
|
timeout: GIT_STATS_TIMEOUT_MS,
|
|
109
121
|
signal,
|
|
110
122
|
},
|
|
111
|
-
(error, stdout) =>
|
|
123
|
+
(error, stdout) => {
|
|
124
|
+
if (error) reject(error);
|
|
125
|
+
else resolveOutput(stdout);
|
|
126
|
+
},
|
|
112
127
|
);
|
|
113
|
-
} catch {
|
|
114
|
-
|
|
128
|
+
} catch (error) {
|
|
129
|
+
reject(error);
|
|
115
130
|
}
|
|
116
131
|
});
|
|
117
132
|
|
|
@@ -133,94 +148,265 @@ const calculateStats = (outputs: readonly string[]) => {
|
|
|
133
148
|
};
|
|
134
149
|
|
|
135
150
|
export function createSessionStatsLoader(options: StatsLoaderOptions = {}) {
|
|
136
|
-
const cache = options.cache ?? new Map<string,
|
|
151
|
+
const cache = options.cache ?? new Map<string, CachedSessionStats>();
|
|
137
152
|
const maxCache = options.maxCache ?? DEFAULT_MAX_CACHE;
|
|
138
153
|
const execute = options.runGit ?? runGit;
|
|
154
|
+
const canonicalize = options.canonicalizeCwd ?? realpath;
|
|
155
|
+
const canonicalizeConcurrency = Math.max(
|
|
156
|
+
1,
|
|
157
|
+
options.canonicalizeConcurrency ?? DEFAULT_CANONICALIZE_CONCURRENCY,
|
|
158
|
+
);
|
|
139
159
|
const queue = createQueue(
|
|
140
160
|
Math.max(1, options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY),
|
|
141
161
|
);
|
|
142
|
-
|
|
162
|
+
interface Request {
|
|
163
|
+
key: string;
|
|
164
|
+
cwd: string;
|
|
165
|
+
after: string;
|
|
166
|
+
before: string;
|
|
167
|
+
includeWorktreeDiff: boolean;
|
|
168
|
+
}
|
|
169
|
+
interface ActiveRequest {
|
|
170
|
+
controller: AbortController;
|
|
171
|
+
promise: Promise<void>;
|
|
172
|
+
}
|
|
143
173
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
const active = new Map<string, ActiveRequest>();
|
|
175
|
+
const pathKeys = new Map<string, string>();
|
|
176
|
+
const canonicalCwds = new Map<string, Promise<string>>();
|
|
177
|
+
let canonicalUniverse:
|
|
178
|
+
| {
|
|
179
|
+
universe: readonly SessionInfoLike[];
|
|
180
|
+
controller: AbortController;
|
|
181
|
+
promise: Promise<Map<string, string>>;
|
|
182
|
+
}
|
|
183
|
+
| undefined;
|
|
184
|
+
let requestedPaths = new Set<string>();
|
|
185
|
+
let reconcileGeneration = 0;
|
|
186
|
+
let notify = () => {};
|
|
187
|
+
|
|
188
|
+
const canonicalCwd = (cwd: string) => {
|
|
189
|
+
const absolute = resolve(cwd);
|
|
190
|
+
let pending = canonicalCwds.get(absolute);
|
|
191
|
+
if (!pending) {
|
|
192
|
+
pending = canonicalize(absolute).catch(() => absolute);
|
|
193
|
+
canonicalCwds.set(absolute, pending);
|
|
194
|
+
}
|
|
195
|
+
return pending;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const buildCanonicalWorkspaceMap = async (
|
|
199
|
+
sessions: readonly SessionInfoLike[],
|
|
147
200
|
signal: AbortSignal,
|
|
148
|
-
onUpdate: () => void,
|
|
149
201
|
) => {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
202
|
+
const workspaces = [
|
|
203
|
+
...new Set(sessions.map((session) => resolve(session.cwd))),
|
|
204
|
+
];
|
|
205
|
+
const result = new Map<string, string>();
|
|
206
|
+
let index = 0;
|
|
207
|
+
const worker = async () => {
|
|
208
|
+
while (!signal.aborted) {
|
|
209
|
+
const cwd = workspaces[index++];
|
|
210
|
+
if (!cwd) return;
|
|
211
|
+
result.set(cwd, await canonicalCwd(cwd));
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
await Promise.all(
|
|
215
|
+
Array.from(
|
|
216
|
+
{ length: Math.min(canonicalizeConcurrency, workspaces.length) },
|
|
217
|
+
worker,
|
|
218
|
+
),
|
|
219
|
+
);
|
|
220
|
+
if (signal.aborted) throw abortError();
|
|
221
|
+
return result;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const canonicalWorkspaceMap = (sessions: readonly SessionInfoLike[]) => {
|
|
225
|
+
if (
|
|
226
|
+
canonicalUniverse?.universe === sessions &&
|
|
227
|
+
!canonicalUniverse.controller.signal.aborted
|
|
228
|
+
) {
|
|
229
|
+
return canonicalUniverse.promise;
|
|
230
|
+
}
|
|
231
|
+
canonicalUniverse?.controller.abort();
|
|
232
|
+
const controller = new AbortController();
|
|
233
|
+
const promise = buildCanonicalWorkspaceMap(sessions, controller.signal);
|
|
234
|
+
canonicalUniverse = { universe: sessions, controller, promise };
|
|
235
|
+
return promise;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const latestPathsByCwd = (
|
|
239
|
+
sessions: readonly SessionInfoLike[],
|
|
240
|
+
canonical: ReadonlyMap<string, string>,
|
|
241
|
+
) => {
|
|
242
|
+
const latest = new Map<string, SessionInfoLike>();
|
|
243
|
+
for (const session of sessions) {
|
|
244
|
+
const cwd = canonical.get(resolve(session.cwd)) ?? resolve(session.cwd);
|
|
245
|
+
const current = latest.get(cwd);
|
|
246
|
+
if (!current || session.modified.getTime() > current.modified.getTime()) {
|
|
247
|
+
latest.set(cwd, session);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return new Map(
|
|
251
|
+
[...latest].map(([cwd, session]) => [cwd, session.path] as const),
|
|
252
|
+
);
|
|
253
|
+
};
|
|
155
254
|
|
|
255
|
+
const requestFor = (
|
|
256
|
+
session: SessionInfoLike,
|
|
257
|
+
latest: ReadonlyMap<string, string>,
|
|
258
|
+
canonical: ReadonlyMap<string, string>,
|
|
259
|
+
): Request => {
|
|
260
|
+
const cwd = canonical.get(resolve(session.cwd)) ?? resolve(session.cwd);
|
|
156
261
|
const after = session.created
|
|
157
262
|
? session.created.toISOString()
|
|
158
263
|
: new Date(session.modified.getTime() - 24 * 3600 * 1000).toISOString();
|
|
159
264
|
const before = session.modified.toISOString();
|
|
265
|
+
const includeWorktreeDiff = latest.get(cwd) === session.path;
|
|
266
|
+
return {
|
|
267
|
+
key: JSON.stringify([cwd, after, before, includeWorktreeDiff]),
|
|
268
|
+
cwd,
|
|
269
|
+
after,
|
|
270
|
+
before,
|
|
271
|
+
includeWorktreeDiff,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
160
274
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
275
|
+
const putCache = (key: string, value: CachedSessionStats) => {
|
|
276
|
+
if (cache.has(key)) cache.delete(key);
|
|
277
|
+
while (cache.size >= Math.max(1, maxCache)) {
|
|
278
|
+
const oldest = cache.keys().next().value;
|
|
279
|
+
if (oldest === undefined) break;
|
|
280
|
+
cache.delete(oldest);
|
|
281
|
+
}
|
|
282
|
+
cache.set(key, value);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const start = (request: Request) => {
|
|
286
|
+
const controller = new AbortController();
|
|
287
|
+
const promise = Promise.all([
|
|
288
|
+
queue.run(
|
|
289
|
+
() =>
|
|
290
|
+
execute(
|
|
291
|
+
[
|
|
292
|
+
"log",
|
|
293
|
+
`--after=${request.after}`,
|
|
294
|
+
`--before=${request.before}`,
|
|
295
|
+
"--numstat",
|
|
296
|
+
"--pretty=format:",
|
|
297
|
+
],
|
|
298
|
+
request.cwd,
|
|
299
|
+
controller.signal,
|
|
300
|
+
),
|
|
301
|
+
controller.signal,
|
|
302
|
+
),
|
|
303
|
+
request.includeWorktreeDiff
|
|
304
|
+
? queue.run(
|
|
165
305
|
() =>
|
|
166
|
-
execute(
|
|
167
|
-
|
|
168
|
-
"log",
|
|
169
|
-
`--after=${after}`,
|
|
170
|
-
`--before=${before}`,
|
|
171
|
-
"--numstat",
|
|
172
|
-
"--pretty=format:",
|
|
173
|
-
],
|
|
174
|
-
session.cwd,
|
|
175
|
-
signal,
|
|
176
|
-
),
|
|
177
|
-
signal,
|
|
306
|
+
execute(["diff", "--numstat"], request.cwd, controller.signal),
|
|
307
|
+
controller.signal,
|
|
178
308
|
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
309
|
+
: Promise.resolve(""),
|
|
310
|
+
])
|
|
311
|
+
.then((outputs) => {
|
|
312
|
+
if (!controller.signal.aborted) {
|
|
313
|
+
putCache(request.key, {
|
|
314
|
+
status: "ready",
|
|
315
|
+
stats: calculateStats(outputs),
|
|
316
|
+
});
|
|
317
|
+
notify();
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
.catch(() => {
|
|
321
|
+
const cancelled = controller.signal.aborted;
|
|
322
|
+
if (!cancelled) {
|
|
323
|
+
putCache(request.key, { status: "unavailable" });
|
|
324
|
+
notify();
|
|
325
|
+
controller.abort();
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
.finally(() => {
|
|
329
|
+
if (active.get(request.key)?.controller === controller) {
|
|
330
|
+
active.delete(request.key);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
const entry = { controller, promise };
|
|
334
|
+
active.set(request.key, entry);
|
|
335
|
+
return entry;
|
|
200
336
|
};
|
|
201
337
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
338
|
+
const cancelKey = (key: string) => {
|
|
339
|
+
const entry = active.get(key);
|
|
340
|
+
if (!entry) return;
|
|
341
|
+
active.delete(key);
|
|
342
|
+
entry.controller.abort();
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const reconcile = async (
|
|
346
|
+
targets: readonly SessionInfoLike[],
|
|
347
|
+
universe: readonly SessionInfoLike[],
|
|
205
348
|
onUpdate: () => void,
|
|
206
349
|
) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
350
|
+
notify = onUpdate;
|
|
351
|
+
const generation = ++reconcileGeneration;
|
|
352
|
+
requestedPaths = new Set(targets.map((session) => session.path));
|
|
353
|
+
if (targets.length === 0) {
|
|
354
|
+
for (const key of [...active.keys()]) cancelKey(key);
|
|
355
|
+
pathKeys.clear();
|
|
356
|
+
return;
|
|
212
357
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
358
|
+
|
|
359
|
+
let canonical: ReadonlyMap<string, string>;
|
|
360
|
+
try {
|
|
361
|
+
canonical = await canonicalWorkspaceMap(universe);
|
|
362
|
+
} catch {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (generation !== reconcileGeneration) return;
|
|
366
|
+
const latest = latestPathsByCwd(universe, canonical);
|
|
367
|
+
const requests = targets.map((session) => ({
|
|
368
|
+
session,
|
|
369
|
+
request: requestFor(session, latest, canonical),
|
|
370
|
+
}));
|
|
371
|
+
const wanted = new Set(requests.map(({ request }) => request.key));
|
|
372
|
+
|
|
373
|
+
for (const key of active.keys()) {
|
|
374
|
+
if (!wanted.has(key)) cancelKey(key);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
pathKeys.clear();
|
|
378
|
+
const pending: Promise<void>[] = [];
|
|
379
|
+
for (const { session, request } of requests) {
|
|
380
|
+
pathKeys.set(session.path, request.key);
|
|
381
|
+
if (cache.has(request.key)) continue;
|
|
382
|
+
const current = active.get(request.key) ?? start(request);
|
|
383
|
+
pending.push(current.promise);
|
|
384
|
+
}
|
|
385
|
+
await Promise.all(pending);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const get = (session: SessionInfoLike): SessionStatsState | undefined => {
|
|
389
|
+
if (!requestedPaths.has(session.path)) return undefined;
|
|
390
|
+
const key = pathKeys.get(session.path);
|
|
391
|
+
if (!key) return { status: "pending" };
|
|
392
|
+
const cached = cache.get(key);
|
|
393
|
+
if (cached) {
|
|
394
|
+
cache.delete(key);
|
|
395
|
+
cache.set(key, cached);
|
|
396
|
+
return cached;
|
|
397
|
+
}
|
|
398
|
+
if (active.has(key)) return { status: "pending" };
|
|
399
|
+
return undefined;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const cancel = () => {
|
|
403
|
+
reconcileGeneration++;
|
|
404
|
+
canonicalUniverse?.controller.abort();
|
|
405
|
+
canonicalUniverse = undefined;
|
|
406
|
+
for (const key of [...active.keys()]) cancelKey(key);
|
|
407
|
+
pathKeys.clear();
|
|
408
|
+
requestedPaths.clear();
|
|
223
409
|
};
|
|
224
410
|
|
|
225
|
-
return { cache,
|
|
411
|
+
return { cache, get, reconcile, cancel };
|
|
226
412
|
}
|