@adecore/agents 0.40.2 → 0.40.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/dist/async.d.ts +3 -0
- package/dist/async.js +10 -0
- package/dist/chat/chat-core.js +8 -3
- package/dist/chat/thread.d.ts +1 -0
- package/dist/chat/thread.js +5 -1
- package/dist/client-sinks.d.ts +8 -0
- package/dist/client-sinks.js +23 -0
- package/dist/host/index.d.ts +1 -1
- package/dist/host/index.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/providers/claude-models.json +2 -2
- package/dist/single-flight.d.ts +6 -0
- package/dist/single-flight.js +27 -0
- package/package.json +3 -3
- package/src/async.ts +17 -0
- package/src/chat/chat-core.ts +9 -3
- package/src/chat/thread.ts +9 -0
- package/src/client-sinks.ts +29 -0
- package/src/host/index.ts +1 -1
- package/src/index.ts +3 -2
- package/src/providers/claude-models.json +2 -2
- package/src/single-flight.ts +33 -0
package/dist/async.d.ts
CHANGED
|
@@ -5,3 +5,6 @@ export interface DeadlineTimer {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function withDeadline<T>(promise: Promise<T>, ms: number, message: string, timer?: DeadlineTimer): Promise<T>;
|
|
7
7
|
export declare function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T>;
|
|
8
|
+
export type Timer = (ms: number, run: () => void) => () => void;
|
|
9
|
+
export declare const cancellableTimeout: Timer;
|
|
10
|
+
export declare const unrefTimeout: Timer;
|
package/dist/async.js
CHANGED
|
@@ -32,3 +32,13 @@ export function withDeadline(promise, ms, message, timer = SYSTEM_TIMER) {
|
|
|
32
32
|
export function withTimeout(promise, ms, message) {
|
|
33
33
|
return withDeadline(promise, ms, message);
|
|
34
34
|
}
|
|
35
|
+
export const cancellableTimeout = (ms, run) => {
|
|
36
|
+
const timer = setTimeout(run, ms);
|
|
37
|
+
return () => clearTimeout(timer);
|
|
38
|
+
};
|
|
39
|
+
/* The same, without holding the process open for it. */
|
|
40
|
+
export const unrefTimeout = (ms, run) => {
|
|
41
|
+
const timer = setTimeout(run, ms);
|
|
42
|
+
timer.unref?.();
|
|
43
|
+
return () => clearTimeout(timer);
|
|
44
|
+
};
|
package/dist/chat/chat-core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { ChatAttachmentUploadsSchema, generatedImageAttachment, HOST_TASK_ORIGIN } from '@adecore/agent-contracts';
|
|
2
|
+
import { ChatAttachmentUploadsSchema, generatedImageAttachment, holdsConversation, HOST_TASK_ORIGIN } from '@adecore/agent-contracts';
|
|
3
3
|
import { ClientSinks } from '../client-sinks.js';
|
|
4
4
|
import { errorText } from '../error-text.js';
|
|
5
5
|
import { AccountError, definedEnv, isDefaultAccountOf, launchEnv, storedAccount } from '../providers/accounts/launch.js';
|
|
@@ -371,8 +371,7 @@ export class ChatCore {
|
|
|
371
371
|
}
|
|
372
372
|
// As named, so a switch to the default account is refused while it is turned off.
|
|
373
373
|
this.requireAccount(kind, requested);
|
|
374
|
-
|
|
375
|
-
if (spoke && !this.canContinue(kind, from, to)) {
|
|
374
|
+
if (holdsConversation(session.info) && !this.canContinue(kind, from, to)) {
|
|
376
375
|
const label = this.accountLabel(kind, to);
|
|
377
376
|
throw new AccountError('account-incompatible', `This conversation is kept by the account '${this.accountLabel(kind, from)}', which '${label}' cannot read. Fork the chat to go on under '${label}'.`);
|
|
378
377
|
}
|
|
@@ -409,6 +408,7 @@ export class ChatCore {
|
|
|
409
408
|
items: session.previewItems(page.items),
|
|
410
409
|
pending: session.previewItems(session.thread.pending()),
|
|
411
410
|
subagents: session.thread.subagentsBefore(page.history.start),
|
|
411
|
+
outline: session.thread.outlineBefore(page.history.start),
|
|
412
412
|
seq: log.seq
|
|
413
413
|
};
|
|
414
414
|
}
|
|
@@ -440,6 +440,11 @@ export class ChatCore {
|
|
|
440
440
|
]);
|
|
441
441
|
} while (this.listChanges.get(chatId) !== changes);
|
|
442
442
|
const result = this.attach(chatId, clientId, historyLimit, since);
|
|
443
|
+
// A reply before the page has a tick only while a bookmark is on it, which only this read knows.
|
|
444
|
+
if (result.outline !== undefined && result.history !== undefined && bookmarks !== undefined && bookmarks.length > 0) {
|
|
445
|
+
const marked = new Set(bookmarks.map((bookmark) => bookmark.itemId));
|
|
446
|
+
result.outline = this.require(chatId).thread.outlineBefore(result.history.start, marked);
|
|
447
|
+
}
|
|
443
448
|
return { ...result, ...(bookmarks === undefined ? {} : { bookmarks }), ...(visuals === undefined ? {} : { visuals }) };
|
|
444
449
|
}
|
|
445
450
|
listChanged(chatId) {
|
package/dist/chat/thread.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare class ChatThread {
|
|
|
30
30
|
private syncRequests;
|
|
31
31
|
history(limit?: number, cursor?: string): ChatHistoryResult;
|
|
32
32
|
subagentsBefore(start: number): ChatSubagentItem[];
|
|
33
|
+
outlineBefore(start: number, marked?: ReadonlySet<string>): ChatItem[];
|
|
33
34
|
snapshot(): {
|
|
34
35
|
info: ChatInfo;
|
|
35
36
|
items: ChatItem[];
|
package/dist/chat/thread.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { activeSubagents } from '@adecore/agent-contracts';
|
|
2
|
+
import { activeSubagents, outlineItems } from '@adecore/agent-contracts';
|
|
3
3
|
import { appendToolOutput } from '@adecore/agent-contracts/text';
|
|
4
4
|
import { ChatError } from './errors.js';
|
|
5
5
|
import { requestSummaries } from './request-summary.js';
|
|
@@ -185,6 +185,10 @@ export class ChatThread {
|
|
|
185
185
|
subagentsBefore(start) {
|
|
186
186
|
return activeSubagents(this.list()).filter((item) => this.indices.get(item.id) < start);
|
|
187
187
|
}
|
|
188
|
+
/* The outline of what a page that begins at `start` leaves out, with the replies in `marked` that a bookmark is on. */
|
|
189
|
+
outlineBefore(start, marked) {
|
|
190
|
+
return outlineItems(this.order.slice(0, start).map((id) => this.items.get(id)), marked);
|
|
191
|
+
}
|
|
188
192
|
snapshot() {
|
|
189
193
|
return { info: this.info, items: this.list() };
|
|
190
194
|
}
|
package/dist/client-sinks.d.ts
CHANGED
|
@@ -7,3 +7,11 @@ export declare class ClientSinks<TEvent> {
|
|
|
7
7
|
to(clientId: string, event: TEvent): void;
|
|
8
8
|
clientIds(): string[];
|
|
9
9
|
}
|
|
10
|
+
export declare class WatchedSinks<TEvent> {
|
|
11
|
+
private readonly sinks;
|
|
12
|
+
private readonly watching;
|
|
13
|
+
subscribe(clientId: string, sink: (event: TEvent) => void): () => void;
|
|
14
|
+
watch(clientId: string): void;
|
|
15
|
+
unwatch(clientId: string): void;
|
|
16
|
+
emit(event: TEvent): void;
|
|
17
|
+
}
|
package/dist/client-sinks.js
CHANGED
|
@@ -32,3 +32,26 @@ export class ClientSinks {
|
|
|
32
32
|
return [...this.sinks.keys()];
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
/* The clients of a part that hear its events only after they asked for them with `<area>.watch`. */
|
|
36
|
+
export class WatchedSinks {
|
|
37
|
+
sinks = new ClientSinks();
|
|
38
|
+
watching = new Set();
|
|
39
|
+
subscribe(clientId, sink) {
|
|
40
|
+
const unsubscribe = this.sinks.subscribe(clientId, sink);
|
|
41
|
+
return () => {
|
|
42
|
+
this.watching.delete(clientId);
|
|
43
|
+
unsubscribe();
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
watch(clientId) {
|
|
47
|
+
this.watching.add(clientId);
|
|
48
|
+
}
|
|
49
|
+
unwatch(clientId) {
|
|
50
|
+
this.watching.delete(clientId);
|
|
51
|
+
}
|
|
52
|
+
emit(event) {
|
|
53
|
+
for (const clientId of this.watching) {
|
|
54
|
+
this.sinks.to(clientId, event);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
package/dist/host/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { RUIMTE_SESSION_VARIABLES, cliEnvironment, type CliEnvironmentPolicy } f
|
|
|
3
3
|
export { type AgentHandler, type AgentHandlers, chatHandlers, accountHandlers, usageHandlers, type AgentRequestMap, type ChatRequestType } from './handlers.ts';
|
|
4
4
|
export { memoryPortPair } from './memory-port.ts';
|
|
5
5
|
export { type AgentWiring, wireAgents, type AgentWiringOptions } from './wiring.ts';
|
|
6
|
-
export { ClientSinks } from '../client-sinks.ts';
|
|
6
|
+
export { ClientSinks, WatchedSinks } from '../client-sinks.ts';
|
|
7
7
|
export type { AgentEvent, AgentEventMap, AgentSink } from '../events.ts';
|
package/dist/host/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { RUIMTE_SESSION_VARIABLES, cliEnvironment } from './environment.js';
|
|
|
4
4
|
export { chatHandlers, accountHandlers, usageHandlers } from './handlers.js';
|
|
5
5
|
export { memoryPortPair } from './memory-port.js';
|
|
6
6
|
export { wireAgents } from './wiring.js';
|
|
7
|
-
export { ClientSinks } from '../client-sinks.js';
|
|
7
|
+
export { ClientSinks, WatchedSinks } from '../client-sinks.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export { wait, withTimeout } from './async.ts';
|
|
1
|
+
export { wait, withTimeout, cancellableTimeout, unrefTimeout, type Timer } from './async.ts';
|
|
2
2
|
export { CodedError } from './coded-error.ts';
|
|
3
3
|
export { describeError, setErrorStacks, errorText } from './error-text.ts';
|
|
4
|
-
export { tempNameFor, writeAtomic, replaceSync, writeAtomicSync, isNotFound, fileExists, entryExists, type WriteAtomicOptions } from './fs.ts';
|
|
4
|
+
export { tempNameFor, writeAtomic, replaceSync, writeAtomicSync, hasErrorCode, isNotFound, fileExists, entryExists, type WriteAtomicOptions } from './fs.ts';
|
|
5
5
|
export { RecordDirectory, type RecordDirectoryOptions } from './record-directory.ts';
|
|
6
6
|
export { runProcess, type ProcessResult, type RunProcessOptions } from './run-process.ts';
|
|
7
7
|
export { Serializer, KeyedSerializer } from './serializer.ts';
|
|
8
|
+
export { SingleFlight } from './single-flight.ts';
|
|
8
9
|
export { cleanTitle, readLines } from './title-file.ts';
|
|
9
10
|
export { type DirectoryWatcher, type WatchSeams, supportsRecursive, SYSTEM_WATCH, type Settled, settled, PerClientWatches, type WatchDirectory, type Schedule } from './watch-seam.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
|
-
export { wait, withTimeout } from './async.js';
|
|
2
|
+
export { wait, withTimeout, cancellableTimeout, unrefTimeout } from './async.js';
|
|
3
3
|
export { CodedError } from './coded-error.js';
|
|
4
4
|
export { describeError, setErrorStacks, errorText } from './error-text.js';
|
|
5
|
-
export { tempNameFor, writeAtomic, replaceSync, writeAtomicSync, isNotFound, fileExists, entryExists } from './fs.js';
|
|
5
|
+
export { tempNameFor, writeAtomic, replaceSync, writeAtomicSync, hasErrorCode, isNotFound, fileExists, entryExists } from './fs.js';
|
|
6
6
|
export { RecordDirectory } from './record-directory.js';
|
|
7
7
|
export { runProcess } from './run-process.js';
|
|
8
8
|
export { Serializer, KeyedSerializer } from './serializer.js';
|
|
9
|
+
export { SingleFlight } from './single-flight.js';
|
|
9
10
|
export { cleanTitle, readLines } from './title-file.js';
|
|
10
11
|
export { supportsRecursive, SYSTEM_WATCH, settled, PerClientWatches } from './watch-seam.js';
|
|
@@ -104,9 +104,9 @@
|
|
|
104
104
|
"models": [
|
|
105
105
|
{ "slug": "claude-fable-5-1", "name": "Claude Fable 5.1", "profile": "frontier", "aliases": ["fable"] },
|
|
106
106
|
{ "slug": "claude-opus-5-5", "name": "Claude Opus 5.5", "badge": "new", "profile": "opus", "aliases": ["opus"] },
|
|
107
|
-
{ "slug": "claude-opus-5", "name": "Claude Opus 5", "profile": "opus" },
|
|
107
|
+
{ "slug": "claude-opus-5", "name": "Claude Opus 5", "profile": "opus", "legacy": true },
|
|
108
108
|
{ "slug": "claude-sonnet-5-5", "name": "Claude Sonnet 5.5", "badge": "new", "profile": "sonnet", "aliases": ["sonnet"] },
|
|
109
|
-
{ "slug": "claude-sonnet-5", "name": "Claude Sonnet 5", "profile": "sonnet" },
|
|
109
|
+
{ "slug": "claude-sonnet-5", "name": "Claude Sonnet 5", "profile": "sonnet", "legacy": true },
|
|
110
110
|
{ "slug": "claude-haiku-5-5", "name": "Claude Haiku 5.5", "badge": "new", "profile": "sonnet", "aliases": ["haiku"] },
|
|
111
111
|
{ "slug": "claude-opus-4-6", "name": "Claude Opus 4.6", "profile": "frontier", "legacy": true },
|
|
112
112
|
{ "slug": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6", "profile": "sonnet", "legacy": true },
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Work that runs once per key at a time: a caller that asks while it runs shares its outcome, and
|
|
3
|
+
* a key that settled leaves nothing behind.
|
|
4
|
+
*/
|
|
5
|
+
export class SingleFlight {
|
|
6
|
+
flights = new Map();
|
|
7
|
+
/* The run in flight for `key`, if any. */
|
|
8
|
+
get(key) {
|
|
9
|
+
return this.flights.get(key);
|
|
10
|
+
}
|
|
11
|
+
has(key) {
|
|
12
|
+
return this.flights.has(key);
|
|
13
|
+
}
|
|
14
|
+
run(key, work) {
|
|
15
|
+
const running = this.flights.get(key);
|
|
16
|
+
if (running !== undefined) {
|
|
17
|
+
return running;
|
|
18
|
+
}
|
|
19
|
+
const flight = work().finally(() => {
|
|
20
|
+
if (this.flights.get(key) === flight) {
|
|
21
|
+
this.flights.delete(key);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
this.flights.set(key, flight);
|
|
25
|
+
return flight;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.4",
|
|
4
4
|
"description": "Node-compatible agent chat hosts, providers, accounts, usage and durable task coordination.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"test": "bun --conditions=source test"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@adecore/agent-contracts": "0.40.
|
|
85
|
-
"@adecore/intelligent-ui": "0.40.
|
|
84
|
+
"@adecore/agent-contracts": "0.40.4",
|
|
85
|
+
"@adecore/intelligent-ui": "0.40.4",
|
|
86
86
|
"zod": "^4.6.7"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
package/src/async.ts
CHANGED
|
@@ -50,3 +50,20 @@ export function withDeadline<T>(promise: Promise<T>, ms: number, message: string
|
|
|
50
50
|
export function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
|
|
51
51
|
return withDeadline(promise, ms, message);
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
/* Runs `run` after `ms` and answers how to call it off. */
|
|
55
|
+
export type Timer = (ms: number, run: () => void) => () => void;
|
|
56
|
+
|
|
57
|
+
export const cancellableTimeout: Timer = (ms, run) => {
|
|
58
|
+
const timer = setTimeout(run, ms);
|
|
59
|
+
|
|
60
|
+
return () => clearTimeout(timer);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/* The same, without holding the process open for it. */
|
|
64
|
+
export const unrefTimeout: Timer = (ms, run) => {
|
|
65
|
+
const timer = setTimeout(run, ms);
|
|
66
|
+
timer.unref?.();
|
|
67
|
+
|
|
68
|
+
return () => clearTimeout(timer);
|
|
69
|
+
};
|
package/src/chat/chat-core.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { join } from 'node:path';
|
|
|
2
2
|
import {
|
|
3
3
|
ChatAttachmentUploadsSchema,
|
|
4
4
|
generatedImageAttachment,
|
|
5
|
+
holdsConversation,
|
|
5
6
|
HOST_TASK_ORIGIN,
|
|
6
7
|
type AgentKind,
|
|
7
8
|
type ChatAttachment,
|
|
@@ -612,9 +613,7 @@ export class ChatCore {
|
|
|
612
613
|
|
|
613
614
|
// As named, so a switch to the default account is refused while it is turned off.
|
|
614
615
|
this.requireAccount(kind, requested);
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if (spoke && !this.canContinue(kind, from, to)) {
|
|
616
|
+
if (holdsConversation(session.info) && !this.canContinue(kind, from, to)) {
|
|
618
617
|
const label = this.accountLabel(kind, to);
|
|
619
618
|
|
|
620
619
|
throw new AccountError(
|
|
@@ -664,6 +663,7 @@ export class ChatCore {
|
|
|
664
663
|
items: session.previewItems(page.items),
|
|
665
664
|
pending: session.previewItems(session.thread.pending()),
|
|
666
665
|
subagents: session.thread.subagentsBefore(page.history.start),
|
|
666
|
+
outline: session.thread.outlineBefore(page.history.start),
|
|
667
667
|
seq: log.seq
|
|
668
668
|
};
|
|
669
669
|
}
|
|
@@ -705,6 +705,12 @@ export class ChatCore {
|
|
|
705
705
|
|
|
706
706
|
const result = this.attach(chatId, clientId, historyLimit, since);
|
|
707
707
|
|
|
708
|
+
// A reply before the page has a tick only while a bookmark is on it, which only this read knows.
|
|
709
|
+
if (result.outline !== undefined && result.history !== undefined && bookmarks !== undefined && bookmarks.length > 0) {
|
|
710
|
+
const marked = new Set(bookmarks.map((bookmark) => bookmark.itemId));
|
|
711
|
+
result.outline = this.require(chatId).thread.outlineBefore(result.history.start, marked);
|
|
712
|
+
}
|
|
713
|
+
|
|
708
714
|
return { ...result, ...(bookmarks === undefined ? {} : { bookmarks }), ...(visuals === undefined ? {} : { visuals }) };
|
|
709
715
|
}
|
|
710
716
|
|
package/src/chat/thread.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import {
|
|
3
3
|
activeSubagents,
|
|
4
|
+
outlineItems,
|
|
4
5
|
type AgentStatus,
|
|
5
6
|
type ChatApprovalItem,
|
|
6
7
|
type ChatEvent,
|
|
@@ -246,6 +247,14 @@ export class ChatThread {
|
|
|
246
247
|
return activeSubagents(this.list()).filter((item) => this.indices.get(item.id)! < start);
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
/* The outline of what a page that begins at `start` leaves out, with the replies in `marked` that a bookmark is on. */
|
|
251
|
+
outlineBefore(start: number, marked?: ReadonlySet<string>): ChatItem[] {
|
|
252
|
+
return outlineItems(
|
|
253
|
+
this.order.slice(0, start).map((id) => this.items.get(id)!),
|
|
254
|
+
marked
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
249
258
|
snapshot(): { info: ChatInfo; items: ChatItem[] } {
|
|
250
259
|
return { info: this.info, items: this.list() };
|
|
251
260
|
}
|
package/src/client-sinks.ts
CHANGED
|
@@ -39,3 +39,32 @@ export class ClientSinks<TEvent> {
|
|
|
39
39
|
return [...this.sinks.keys()];
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
/* The clients of a part that hear its events only after they asked for them with `<area>.watch`. */
|
|
44
|
+
export class WatchedSinks<TEvent> {
|
|
45
|
+
private readonly sinks = new ClientSinks<TEvent>();
|
|
46
|
+
private readonly watching = new Set<string>();
|
|
47
|
+
|
|
48
|
+
subscribe(clientId: string, sink: (event: TEvent) => void): () => void {
|
|
49
|
+
const unsubscribe = this.sinks.subscribe(clientId, sink);
|
|
50
|
+
|
|
51
|
+
return () => {
|
|
52
|
+
this.watching.delete(clientId);
|
|
53
|
+
unsubscribe();
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
watch(clientId: string): void {
|
|
58
|
+
this.watching.add(clientId);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
unwatch(clientId: string): void {
|
|
62
|
+
this.watching.delete(clientId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
emit(event: TEvent): void {
|
|
66
|
+
for (const clientId of this.watching) {
|
|
67
|
+
this.sinks.to(clientId, event);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/host/index.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { RUIMTE_SESSION_VARIABLES, cliEnvironment, type CliEnvironmentPolicy } f
|
|
|
4
4
|
export { type AgentHandler, type AgentHandlers, chatHandlers, accountHandlers, usageHandlers, type AgentRequestMap, type ChatRequestType } from './handlers.ts';
|
|
5
5
|
export { memoryPortPair } from './memory-port.ts';
|
|
6
6
|
export { type AgentWiring, wireAgents, type AgentWiringOptions } from './wiring.ts';
|
|
7
|
-
export { ClientSinks } from '../client-sinks.ts';
|
|
7
|
+
export { ClientSinks, WatchedSinks } from '../client-sinks.ts';
|
|
8
8
|
export type { AgentEvent, AgentEventMap, AgentSink } from '../events.ts';
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
|
-
export { wait, withTimeout } from './async.ts';
|
|
2
|
+
export { wait, withTimeout, cancellableTimeout, unrefTimeout, type Timer } from './async.ts';
|
|
3
3
|
export { CodedError } from './coded-error.ts';
|
|
4
4
|
export { describeError, setErrorStacks, errorText } from './error-text.ts';
|
|
5
|
-
export { tempNameFor, writeAtomic, replaceSync, writeAtomicSync, isNotFound, fileExists, entryExists, type WriteAtomicOptions } from './fs.ts';
|
|
5
|
+
export { tempNameFor, writeAtomic, replaceSync, writeAtomicSync, hasErrorCode, isNotFound, fileExists, entryExists, type WriteAtomicOptions } from './fs.ts';
|
|
6
6
|
export { RecordDirectory, type RecordDirectoryOptions } from './record-directory.ts';
|
|
7
7
|
export { runProcess, type ProcessResult, type RunProcessOptions } from './run-process.ts';
|
|
8
8
|
export { Serializer, KeyedSerializer } from './serializer.ts';
|
|
9
|
+
export { SingleFlight } from './single-flight.ts';
|
|
9
10
|
export { cleanTitle, readLines } from './title-file.ts';
|
|
10
11
|
|
|
11
12
|
export {
|
|
@@ -104,9 +104,9 @@
|
|
|
104
104
|
"models": [
|
|
105
105
|
{ "slug": "claude-fable-5-1", "name": "Claude Fable 5.1", "profile": "frontier", "aliases": ["fable"] },
|
|
106
106
|
{ "slug": "claude-opus-5-5", "name": "Claude Opus 5.5", "badge": "new", "profile": "opus", "aliases": ["opus"] },
|
|
107
|
-
{ "slug": "claude-opus-5", "name": "Claude Opus 5", "profile": "opus" },
|
|
107
|
+
{ "slug": "claude-opus-5", "name": "Claude Opus 5", "profile": "opus", "legacy": true },
|
|
108
108
|
{ "slug": "claude-sonnet-5-5", "name": "Claude Sonnet 5.5", "badge": "new", "profile": "sonnet", "aliases": ["sonnet"] },
|
|
109
|
-
{ "slug": "claude-sonnet-5", "name": "Claude Sonnet 5", "profile": "sonnet" },
|
|
109
|
+
{ "slug": "claude-sonnet-5", "name": "Claude Sonnet 5", "profile": "sonnet", "legacy": true },
|
|
110
110
|
{ "slug": "claude-haiku-5-5", "name": "Claude Haiku 5.5", "badge": "new", "profile": "sonnet", "aliases": ["haiku"] },
|
|
111
111
|
{ "slug": "claude-opus-4-6", "name": "Claude Opus 4.6", "profile": "frontier", "legacy": true },
|
|
112
112
|
{ "slug": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6", "profile": "sonnet", "legacy": true },
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Work that runs once per key at a time: a caller that asks while it runs shares its outcome, and
|
|
3
|
+
* a key that settled leaves nothing behind.
|
|
4
|
+
*/
|
|
5
|
+
export class SingleFlight<T> {
|
|
6
|
+
private readonly flights = new Map<string, Promise<T>>();
|
|
7
|
+
|
|
8
|
+
/* The run in flight for `key`, if any. */
|
|
9
|
+
get(key: string): Promise<T> | undefined {
|
|
10
|
+
return this.flights.get(key);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
has(key: string): boolean {
|
|
14
|
+
return this.flights.has(key);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
run(key: string, work: () => Promise<T>): Promise<T> {
|
|
18
|
+
const running = this.flights.get(key);
|
|
19
|
+
|
|
20
|
+
if (running !== undefined) {
|
|
21
|
+
return running;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const flight = work().finally(() => {
|
|
25
|
+
if (this.flights.get(key) === flight) {
|
|
26
|
+
this.flights.delete(key);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
this.flights.set(key, flight);
|
|
30
|
+
|
|
31
|
+
return flight;
|
|
32
|
+
}
|
|
33
|
+
}
|