@adecore/agents 0.21.1 → 0.21.2
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.js +3 -8
- package/dist/chat/chat-core.d.ts +2 -2
- package/dist/chat/chat-core.js +4 -4
- package/dist/chat/chat-log.js +2 -2
- package/dist/chat/chat-store.js +4 -7
- package/dist/chat/chat-title.js +4 -8
- package/dist/chat/claude-backend.d.ts +1 -0
- package/dist/chat/claude-backend.js +8 -6
- package/dist/chat/claude-title.js +1 -1
- package/dist/chat/codex-backend.d.ts +2 -2
- package/dist/chat/codex-backend.js +5 -8
- package/dist/chat/codex-protocol.js +1 -1
- package/dist/chat/codex-thread.js +4 -5
- package/dist/chat/composer-preferences.js +2 -3
- package/dist/chat/limit-resume.js +2 -7
- package/dist/chat/visual-store.js +8 -6
- package/dist/context/refusal.js +3 -7
- package/dist/context/verb.js +1 -5
- package/dist/host/agent-host.js +2 -2
- package/dist/host/handlers.js +0 -1
- package/dist/host/wiring.js +1 -1
- package/dist/lineage.js +2 -3
- package/dist/messages/deliver-message.js +6 -11
- package/dist/messages/deliver-notice.js +8 -12
- package/dist/messages/notice-store.js +3 -5
- package/dist/outbox/outbox-worker.js +5 -2
- package/dist/providers/accounts/service.d.ts +2 -1
- package/dist/providers/accounts/service.js +11 -13
- package/dist/providers/claude.js +0 -1
- package/dist/providers/detect.js +2 -10
- package/dist/record-directory.js +0 -1
- package/dist/tasks/task-coordinator.d.ts +1 -0
- package/dist/tasks/task-coordinator.js +34 -34
- package/dist/tasks/task-store.js +1 -1
- package/dist/tasks/task-work.js +2 -2
- package/dist/tasks/waiting-child.js +5 -3
- package/dist/tasks/wake-parent.js +15 -10
- package/dist/tasks/wiring.js +32 -37
- package/dist/usage/aggregate.js +62 -46
- package/dist/usage/exchange.js +1 -1
- package/dist/usage/limits/monitor.d.ts +2 -2
- package/dist/usage/limits/monitor.js +2 -2
- package/dist/usage/limits/probe.js +1 -1
- package/dist/usage/pricing.js +1 -1
- package/dist/usage/roots.js +1 -2
- package/dist/usage/scanner.js +7 -9
- package/dist/usage/usage-service.d.ts +1 -2
- package/dist/usage/usage-service.js +3 -6
- package/package.json +3 -3
- package/src/async.ts +3 -9
- package/src/chat/chat-core.ts +4 -4
- package/src/chat/chat-log.ts +2 -2
- package/src/chat/chat-store.ts +4 -8
- package/src/chat/chat-title.ts +4 -8
- package/src/chat/claude-backend.ts +9 -6
- package/src/chat/claude-title.ts +1 -1
- package/src/chat/claude-transcript.ts +1 -1
- package/src/chat/codex-backend.ts +5 -8
- package/src/chat/codex-protocol.ts +1 -1
- package/src/chat/codex-thread.ts +6 -5
- package/src/chat/composer-preferences.ts +2 -3
- package/src/chat/limit-resume.ts +2 -7
- package/src/chat/thread.ts +1 -1
- package/src/chat/visual-store.ts +9 -6
- package/src/chat/wake-chat.ts +4 -1
- package/src/context/refusal.ts +3 -7
- package/src/context/verb.ts +1 -5
- package/src/host/agent-host.ts +2 -2
- package/src/host/handlers.ts +0 -1
- package/src/host/wiring.ts +1 -1
- package/src/lineage.ts +2 -3
- package/src/messages/deliver-message.ts +6 -11
- package/src/messages/deliver-notice.ts +8 -12
- package/src/messages/notice-store.ts +3 -5
- package/src/outbox/outbox-worker.ts +5 -2
- package/src/providers/accounts/service.ts +12 -13
- package/src/providers/accounts/variables.ts +1 -0
- package/src/providers/claude.ts +0 -1
- package/src/providers/detect.ts +2 -11
- package/src/record-directory.ts +0 -1
- package/src/skills.ts +0 -1
- package/src/tasks/task-coordinator.ts +35 -33
- package/src/tasks/task-store.ts +1 -1
- package/src/tasks/task-work.ts +2 -2
- package/src/tasks/waiting-child.ts +6 -3
- package/src/tasks/wake-parent.ts +21 -16
- package/src/tasks/wiring.ts +33 -41
- package/src/usage/aggregate.ts +80 -50
- package/src/usage/exchange.ts +1 -1
- package/src/usage/limits/monitor.ts +4 -4
- package/src/usage/limits/probe.ts +2 -2
- package/src/usage/pricing.ts +1 -1
- package/src/usage/roots.ts +1 -2
- package/src/usage/scanner.ts +8 -10
- package/src/usage/usage-service.ts +5 -9
package/dist/async.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/* The two waits the host does: a pause it can be woken from, and a deadline on somebody else's promise. */
|
|
2
1
|
/*
|
|
3
|
-
* Resolves after `ms`, or at once when the signal is raised. An abort
|
|
4
|
-
*
|
|
5
|
-
* rejection would only be caught and thrown away.
|
|
2
|
+
* Resolves after `ms`, or at once when the signal is raised. An abort resolves instead of rejecting:
|
|
3
|
+
* every caller is a loop that checks the signal itself on its next turn.
|
|
6
4
|
*/
|
|
7
5
|
export function wait(ms, signal) {
|
|
8
6
|
return new Promise((resolve) => {
|
|
@@ -13,10 +11,7 @@ export function wait(ms, signal) {
|
|
|
13
11
|
}, { once: true });
|
|
14
12
|
});
|
|
15
13
|
}
|
|
16
|
-
/*
|
|
17
|
-
* The promise, or `message` when it takes longer than `ms`. The timer is cleared either way, so a
|
|
18
|
-
* call that answers at once does not hold the process for the rest of its deadline.
|
|
19
|
-
*/
|
|
14
|
+
/* The timer is cleared either way, so a call that answers at once does not hold the process until its deadline. */
|
|
20
15
|
export function withTimeout(promise, ms, message) {
|
|
21
16
|
return new Promise((resolve, reject) => {
|
|
22
17
|
const timer = setTimeout(() => reject(new Error(message)), ms);
|
package/dist/chat/chat-core.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type AgentKind, type ChatAttachment, type ChatAttachResult, type ChatAttachmentUpload, type ChatBookmark, type ChatCheckpointDiff, type ChatConfigurePayload, type ChatCreatePayload, type ChatEvent, type ChatHistoryResult, type ChatInfo, type ChatItem, type ChatQueuedMessage, type ChatUiChoicePayload, type ChatUiQueryPayload, type ChatUiLinkPayload, type ChatSkill, type ChatSubagentPayload, type ChatSubagentResult, type ChatTurnItem, type ChatVisual, type ModelSelection, type RuntimeMode } from '@adecore/agent-contracts';
|
|
2
|
-
import { type ChatUiHost } from './ui-queries.ts';
|
|
3
2
|
import { ClientSinks } from '../client-sinks.ts';
|
|
4
3
|
import type { AgentEvent, AgentSink } from '../events.ts';
|
|
5
4
|
import type { OutboxOutcome } from '../outbox/outbox-worker.ts';
|
|
@@ -8,6 +7,7 @@ import type { ProviderRegistry } from '../providers/registry.ts';
|
|
|
8
7
|
import { SkillIndex } from '../skills.ts';
|
|
9
8
|
import type { LimitsUpdate } from '../usage/limits/normalize.ts';
|
|
10
9
|
import type { AttachmentStore } from './attachment-store.ts';
|
|
10
|
+
import { type BackgroundWork } from './background-work.ts';
|
|
11
11
|
import type { BookmarkStore } from './bookmark-store.ts';
|
|
12
12
|
import type { SpawnChatProcess } from './chat-process.ts';
|
|
13
13
|
import { ChatSession, type ChatReferences, type ChatSendExtras, type PromptNotes, type ResumeWords, type TurnCheckpoints } from './chat-session.ts';
|
|
@@ -17,8 +17,8 @@ import type { CodexProcessSpec } from './codex-thread.ts';
|
|
|
17
17
|
import type { CodexClientInfo } from './codex-transport.ts';
|
|
18
18
|
import { ComposerPreferences } from './composer-preferences.ts';
|
|
19
19
|
import { DeltaCoalescer } from './delta-coalescer.ts';
|
|
20
|
-
import { type BackgroundWork } from './background-work.ts';
|
|
21
20
|
import { SubagentReader, type SubagentReaderOptions } from './subagent-reader.ts';
|
|
21
|
+
import { type ChatUiHost } from './ui-queries.ts';
|
|
22
22
|
import type { VisualInput, VisualStore } from './visual-store.ts';
|
|
23
23
|
export interface InterruptedRun {
|
|
24
24
|
chatId: string;
|
package/dist/chat/chat-core.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { ChatAttachmentUploadsSchema, generatedImageAttachment } from '@adecore/agent-contracts';
|
|
4
|
-
import { ChatUiQueries } from './ui-queries.js';
|
|
5
4
|
import { ClientSinks } from '../client-sinks.js';
|
|
6
5
|
import { errorText } from '../error-text.js';
|
|
7
6
|
import { AccountError, definedEnv, isDefaultAccountOf, launchEnv, storedAccount } from '../providers/accounts/launch.js';
|
|
8
7
|
import { SkillIndex } from '../skills.js';
|
|
9
8
|
import { usageRoots } from '../usage/roots.js';
|
|
9
|
+
import { commandLabel, isBackgroundWork, runsInBackground } from './background-work.js';
|
|
10
10
|
import { ChatLog, COMPACT_ABOVE_BYTES } from './chat-log.js';
|
|
11
11
|
import { ChatSession } from './chat-session.js';
|
|
12
12
|
import { ComposerPreferences } from './composer-preferences.js';
|
|
13
13
|
import { DeltaCoalescer } from './delta-coalescer.js';
|
|
14
|
-
import { readableAssistantText } from './readable-text.js';
|
|
15
14
|
import { ChatError } from './errors.js';
|
|
16
|
-
import {
|
|
15
|
+
import { readableAssistantText } from './readable-text.js';
|
|
17
16
|
import { SubagentReader } from './subagent-reader.js';
|
|
18
|
-
|
|
17
|
+
import { ChatUiQueries } from './ui-queries.js';
|
|
19
18
|
function withoutHidden(info) {
|
|
20
19
|
const { hidden: _hidden, ...rest } = info;
|
|
21
20
|
return rest;
|
|
22
21
|
}
|
|
22
|
+
/* A message a person or the agent wrote in the chat's own thread; a subagent's words belong to its row. */
|
|
23
23
|
function isBookmarkable(item) {
|
|
24
24
|
return item.kind === 'user' || (item.kind === 'assistant' && (item.parentToolUseId ?? null) === null);
|
|
25
25
|
}
|
package/dist/chat/chat-log.js
CHANGED
|
@@ -31,8 +31,8 @@ export function parseLog(raw) {
|
|
|
31
31
|
return lines;
|
|
32
32
|
}
|
|
33
33
|
/*
|
|
34
|
-
*
|
|
35
|
-
* one order. Recent lines stay in memory
|
|
34
|
+
* Numbers every broadcast event and appends it synchronously, so disk, memory and snapshot boundaries
|
|
35
|
+
* keep one order. Recent lines stay in memory to answer an attach with `since` at once.
|
|
36
36
|
*/
|
|
37
37
|
export class ChatLog {
|
|
38
38
|
path;
|
package/dist/chat/chat-store.js
CHANGED
|
@@ -4,12 +4,12 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { ChatInfoSchema, ChatItemSchema } from '@adecore/agent-contracts';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { isNotFound, writeAtomic, writeAtomicSync } from '../fs.js';
|
|
7
|
-
import {
|
|
7
|
+
import { recordFileName } from '../record-directory.js';
|
|
8
8
|
import { migrateInlineAttachments } from './attachment-store.js';
|
|
9
|
+
import { isBookmarkFileName } from './bookmark-store.js';
|
|
9
10
|
import { parseLog } from './chat-log.js';
|
|
10
11
|
import { ChatThread } from './thread.js';
|
|
11
12
|
import { isVisualFileName } from './visual-store.js';
|
|
12
|
-
import { recordFileName } from '../record-directory.js';
|
|
13
13
|
// zod strips what it does not know inside the info and the items, so a file written by an older build
|
|
14
14
|
// (with `interactionMode`, say) still parses and loses only the dropped field. A record without `seq`
|
|
15
15
|
// predates the log. Keys beside these are the host's own, and come back to it as they were written.
|
|
@@ -24,7 +24,7 @@ const RecordSchema = z.looseObject({
|
|
|
24
24
|
uiAccess: z.record(z.string(), z.unknown()).optional()
|
|
25
25
|
});
|
|
26
26
|
const OWN_KEYS = new Set(['info', 'items', 'seq', 'resetSeq', 'preambles', 'uiAccess']);
|
|
27
|
-
function
|
|
27
|
+
function withoutOwnKeys(record) {
|
|
28
28
|
return Object.fromEntries(Object.entries(record).filter(([key]) => !OWN_KEYS.has(key)));
|
|
29
29
|
}
|
|
30
30
|
function logName(chatId) {
|
|
@@ -54,9 +54,6 @@ function liftUiAccess(record) {
|
|
|
54
54
|
}
|
|
55
55
|
return Object.keys(uiAccess).length === 0 ? record : { ...record, uiAccess };
|
|
56
56
|
}
|
|
57
|
-
function withoutOwnKeys(extras) {
|
|
58
|
-
return Object.fromEntries(Object.entries(extras).filter(([key]) => !OWN_KEYS.has(key)));
|
|
59
|
-
}
|
|
60
57
|
/*
|
|
61
58
|
* One JSON file per chat under `<home>/chats`, written while a turn runs and on shutdown, with
|
|
62
59
|
* the log of what happened since beside it (`ChatLog` appends to it; reading plays it back).
|
|
@@ -146,7 +143,7 @@ export class ChatStore {
|
|
|
146
143
|
resetSeq,
|
|
147
144
|
preambles: snapshot.preambles ?? [],
|
|
148
145
|
uiAccess: snapshot.uiAccess ?? {},
|
|
149
|
-
extras:
|
|
146
|
+
extras: withoutOwnKeys(snapshot),
|
|
150
147
|
lines
|
|
151
148
|
};
|
|
152
149
|
}
|
package/dist/chat/chat-title.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cleanTitle } from '../title-file.js';
|
|
2
1
|
import { runProcess } from '../run-process.js';
|
|
2
|
+
import { cleanTitle } from '../title-file.js';
|
|
3
3
|
// Enough of a conversation to name it; the rest of a long prompt or answer says nothing a title needs.
|
|
4
4
|
const MAX_PROMPT_CHARS = 2000;
|
|
5
5
|
const MAX_ANSWER_CHARS = 1500;
|
|
@@ -8,10 +8,7 @@ const TIMEOUT_MS = 45_000;
|
|
|
8
8
|
function cap(text, limit) {
|
|
9
9
|
return text.length <= limit ? text : `${text.slice(0, limit)}...`;
|
|
10
10
|
}
|
|
11
|
-
/*
|
|
12
|
-
* What the CLI is asked. JSON again, as for a commit message: a model left to write freely opens
|
|
13
|
-
* with a sentence, and that sentence would become the name of the node.
|
|
14
|
-
*/
|
|
11
|
+
/* JSON, since a model left to write freely opens with a sentence, and that sentence would become the name. */
|
|
15
12
|
export function buildTitlePrompt(prompt, answer) {
|
|
16
13
|
return [
|
|
17
14
|
'Write a short title for the conversation below, as a person would name it in a list of chats.',
|
|
@@ -30,9 +27,8 @@ export function buildTitlePrompt(prompt, answer) {
|
|
|
30
27
|
].join('\n');
|
|
31
28
|
}
|
|
32
29
|
/*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* is better than a stray line of it.
|
|
30
|
+
* Strict on purpose: a run that holds no `{"title": "..."}` object printed something other than a
|
|
31
|
+
* title, and a derived name is better than a stray line of it.
|
|
36
32
|
*/
|
|
37
33
|
export function parseTitle(output) {
|
|
38
34
|
const start = [...output.matchAll(/\{\s*"title"/g)].at(-1)?.index;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { errorText } from '../error-text.js';
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { errorText } from '../error-text.js';
|
|
3
3
|
import { claudeArgs, claudeEnv, promptPrefix } from '../providers/claude.js';
|
|
4
4
|
import { ChatChild } from './chat-process.js';
|
|
5
5
|
import { ClaudeProtocol } from './claude-protocol.js';
|
|
@@ -59,12 +59,10 @@ export class ClaudeBackend {
|
|
|
59
59
|
return Promise.resolve();
|
|
60
60
|
}
|
|
61
61
|
sendTurn(input) {
|
|
62
|
-
// The prefix is what the CLI must see first (ultrathink), then the note about the links, then what was typed.
|
|
63
|
-
const prefix = `${promptPrefix(this.launch.selection)}${input.preamble === null ? '' : `${input.preamble}\n\n`}`;
|
|
64
62
|
const promptId = input.promptId ?? randomUUID();
|
|
65
63
|
this.active = true;
|
|
66
64
|
this.protocol.beginPrompt(promptId);
|
|
67
|
-
this.write(
|
|
65
|
+
this.write(this.userMessage(input, promptId));
|
|
68
66
|
}
|
|
69
67
|
steerTurn(input) {
|
|
70
68
|
if (!this.child || !this.active || this.stdinClosed) {
|
|
@@ -75,8 +73,7 @@ export class ClaudeBackend {
|
|
|
75
73
|
this.steers.set(promptId, { resolve, reject });
|
|
76
74
|
this.protocol.beginSteer(promptId);
|
|
77
75
|
try {
|
|
78
|
-
|
|
79
|
-
this.write(buildUserMessage({ text: input.text, attachments: input.attachments, prefix, skills: input.skills, promptId }));
|
|
76
|
+
this.write(this.userMessage(input, promptId));
|
|
80
77
|
}
|
|
81
78
|
catch (error) {
|
|
82
79
|
this.steers.delete(promptId);
|
|
@@ -126,6 +123,11 @@ export class ClaudeBackend {
|
|
|
126
123
|
this.child = null;
|
|
127
124
|
return child?.end() ?? Promise.resolve();
|
|
128
125
|
}
|
|
126
|
+
userMessage(input, promptId) {
|
|
127
|
+
// What the CLI must see first (ultrathink), then the preamble, then what was typed.
|
|
128
|
+
const prefix = `${promptPrefix(this.launch.selection)}${input.preamble === null ? '' : `${input.preamble}\n\n`}`;
|
|
129
|
+
return buildUserMessage({ text: input.text, attachments: input.attachments, prefix, skills: input.skills, promptId });
|
|
130
|
+
}
|
|
129
131
|
async readLines(child) {
|
|
130
132
|
const reader = child.process.stdout.getReader();
|
|
131
133
|
const decoder = new TextDecoder();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { usageRoots } from '../usage/roots.js';
|
|
5
4
|
import { cleanTitle, readLines } from '../title-file.js';
|
|
5
|
+
import { usageRoots } from '../usage/roots.js';
|
|
6
6
|
// The two records carry these words; a line without them is never parsed.
|
|
7
7
|
const MARKERS = ['"ai-title"', '"custom-title"'];
|
|
8
8
|
/*
|
|
@@ -7,12 +7,12 @@ export interface CodexBackendOptions {
|
|
|
7
7
|
}
|
|
8
8
|
export declare class CodexBackend implements ChatBackend {
|
|
9
9
|
readonly acknowledgesTurns = true;
|
|
10
|
-
private promptId;
|
|
11
10
|
private readonly launch;
|
|
12
|
-
private imageWrites;
|
|
13
11
|
private readonly host;
|
|
14
12
|
private readonly protocol;
|
|
15
13
|
private readonly client;
|
|
14
|
+
private promptId;
|
|
15
|
+
private imageWrites;
|
|
16
16
|
private transport;
|
|
17
17
|
private threadId;
|
|
18
18
|
private contextPending;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { accessSync, constants } from 'node:fs';
|
|
2
2
|
import { attachmentImageMime } from '@adecore/agent-contracts';
|
|
3
|
+
import { errorText } from '../error-text.js';
|
|
3
4
|
import { codexServiceTier, codexThreadOptions } from '../providers/codex.js';
|
|
4
5
|
import { CodexProtocol } from './codex-protocol.js';
|
|
5
|
-
import { attachmentNote } from './input.js';
|
|
6
6
|
import { CodexTransport, DEFAULT_CODEX_CLIENT } from './codex-transport.js';
|
|
7
|
-
import {
|
|
7
|
+
import { attachmentNote } from './input.js';
|
|
8
8
|
function textInput(text) {
|
|
9
9
|
return [{ type: 'text', text, text_elements: [] }];
|
|
10
10
|
}
|
|
@@ -37,18 +37,15 @@ export function parseSkillsList(result) {
|
|
|
37
37
|
*/
|
|
38
38
|
export class CodexBackend {
|
|
39
39
|
acknowledgesTurns = true;
|
|
40
|
-
promptId = null;
|
|
41
40
|
launch;
|
|
42
|
-
imageWrites = null;
|
|
43
41
|
host;
|
|
44
42
|
protocol;
|
|
45
43
|
client;
|
|
44
|
+
promptId = null;
|
|
45
|
+
imageWrites = null;
|
|
46
46
|
transport = null;
|
|
47
47
|
threadId = '';
|
|
48
|
-
|
|
49
|
-
* Codex 0.154 ignores developer instructions on `thread/resume` (measured), so a thread started
|
|
50
|
-
* before anything was linked would never hear of its links; the first prompt of a resumed process says so instead.
|
|
51
|
-
*/
|
|
48
|
+
// Codex 0.154 ignores developer instructions on `thread/resume` (measured), so the first prompt of a resumed process carries them.
|
|
52
49
|
contextPending = null;
|
|
53
50
|
imageInputSupported = null;
|
|
54
51
|
/* A turn asked for that Codex has not named yet; `turn/interrupt` needs that name. */
|
|
@@ -130,7 +130,7 @@ function elicitationInput(meta) {
|
|
|
130
130
|
}
|
|
131
131
|
return input;
|
|
132
132
|
}
|
|
133
|
-
//
|
|
133
|
+
// The latest reset of every spent window; one without a known reset leaves resuming to a person.
|
|
134
134
|
function spentUntil(windows) {
|
|
135
135
|
const spent = windows.filter((window) => window.used >= 1);
|
|
136
136
|
if (spent.length === 0 || spent.some((window) => window.resetsAt === null)) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { errorText } from '../error-text.js';
|
|
1
2
|
import { CodexProtocol } from './codex-protocol.js';
|
|
2
3
|
import { CodexTransport, DEFAULT_CODEX_CLIENT } from './codex-transport.js';
|
|
3
4
|
import { ChatError } from './errors.js';
|
|
4
5
|
import { readingThread, settledReading } from './subagent-projection.js';
|
|
5
|
-
import { errorText } from '../error-text.js';
|
|
6
6
|
function isRecord(value) {
|
|
7
7
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8
8
|
}
|
|
@@ -85,14 +85,13 @@ async function nthTurnId(transport, threadId, turns) {
|
|
|
85
85
|
let seen = 0;
|
|
86
86
|
let cursor = null;
|
|
87
87
|
do {
|
|
88
|
-
const page = await transport.request('thread/turns/list', { threadId, limit: 100, sortDirection: 'asc', ...(cursor === null ? {} : { cursor }) });
|
|
89
|
-
|
|
90
|
-
for (const turn of data) {
|
|
88
|
+
const page = parseThreadItemsPage(await transport.request('thread/turns/list', { threadId, limit: 100, sortDirection: 'asc', ...(cursor === null ? {} : { cursor }) }));
|
|
89
|
+
for (const turn of page.entries) {
|
|
91
90
|
if (++seen === turns) {
|
|
92
91
|
return typeof turn.id === 'string' ? turn.id : null;
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
|
-
cursor =
|
|
94
|
+
cursor = page.nextCursor;
|
|
96
95
|
} while (cursor !== null);
|
|
97
96
|
return null;
|
|
98
97
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* What each connected client says a new chat starts with, for the chats the host starts with no
|
|
3
|
-
* client mounting them. Held per
|
|
4
|
-
*
|
|
5
|
-
* host's own defaults.
|
|
3
|
+
* client mounting them. Held per client only, so with nobody connected a chat gets the host's own
|
|
4
|
+
* defaults rather than the pick of a person who left.
|
|
6
5
|
*/
|
|
7
6
|
export class ComposerPreferences {
|
|
8
7
|
byClient = new Map();
|
|
@@ -2,13 +2,8 @@
|
|
|
2
2
|
export const LIMIT_RETRY_DELAYS_MS = [60_000, 5 * 60_000, 15 * 60_000];
|
|
3
3
|
/* The last turn of a thread, when it stopped on a limit. */
|
|
4
4
|
export function limitedTurn(items) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (item.kind === 'turn') {
|
|
8
|
-
return item.state === 'error' && item.limit !== undefined ? item : null;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return null;
|
|
5
|
+
const last = items.findLast((item) => item.kind === 'turn');
|
|
6
|
+
return last?.kind === 'turn' && last.state === 'error' && last.limit !== undefined ? last : null;
|
|
12
7
|
}
|
|
13
8
|
/* How many turns in a row, up to and including this one, stopped on a limit: the tries a person did not step in between. */
|
|
14
9
|
function limitedInARow(items, turn) {
|
|
@@ -59,6 +59,12 @@ function tooLarge(bytes) {
|
|
|
59
59
|
return refuse('visual-too-large', `The page is ${mebibytes(bytes)} and a visual may be at most ${mebibytes(VISUAL_LIMITS.bytes)}; ` +
|
|
60
60
|
'load libraries from a public CDN URL and refer to large images by their http(s) URL instead of inlining them');
|
|
61
61
|
}
|
|
62
|
+
function refuseOversized(html) {
|
|
63
|
+
const bytes = Buffer.byteLength(html, 'utf8');
|
|
64
|
+
if (bytes > VISUAL_LIMITS.bytes) {
|
|
65
|
+
tooLarge(bytes);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
62
68
|
/*
|
|
63
69
|
* The visuals of every chat. The list of a chat is one file beside its record under `<home>/chats`;
|
|
64
70
|
* each page is a file in the chat's attachment folder, `<id>.html`, so whatever serves an attachment
|
|
@@ -104,9 +110,7 @@ export class VisualStore {
|
|
|
104
110
|
if (html.trim() === '') {
|
|
105
111
|
refuse('visual-invalid', 'The page is empty; pass one self-contained HTML document');
|
|
106
112
|
}
|
|
107
|
-
|
|
108
|
-
tooLarge(Buffer.byteLength(html, 'utf8'));
|
|
109
|
-
}
|
|
113
|
+
refuseOversized(html);
|
|
110
114
|
return this.writeWorkspaceFile(chatId, name, html);
|
|
111
115
|
}
|
|
112
116
|
writePreview(chatId, png) {
|
|
@@ -116,9 +120,7 @@ export class VisualStore {
|
|
|
116
120
|
async publish(chatId, input) {
|
|
117
121
|
const { title, html, maxHeight, layout, heights } = checked(input);
|
|
118
122
|
// Before the bootstrap is added, so a page far too large is never copied first.
|
|
119
|
-
|
|
120
|
-
tooLarge(Buffer.byteLength(html, 'utf8'));
|
|
121
|
-
}
|
|
123
|
+
refuseOversized(html);
|
|
122
124
|
const page = Buffer.from(injectVisualBootstrap(html), 'utf8');
|
|
123
125
|
if (page.byteLength > VISUAL_LIMITS.bytes) {
|
|
124
126
|
tooLarge(page.byteLength);
|
package/dist/context/refusal.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* How every no reads: `refused`, the code a script branches on and the sentence a model reads, and
|
|
3
|
-
* under it a line per thing the actor can do instead.
|
|
4
|
-
*
|
|
5
|
-
* two, and everything under the first row is read as advice.
|
|
3
|
+
* under it a line per thing the actor can do instead. A line keeps its tabs but loses its newlines,
|
|
4
|
+
* since everything under the first row is read as advice.
|
|
6
5
|
*/
|
|
7
6
|
/* A field that goes into a tab-separated line; a tab or a newline in a title would split the row. */
|
|
8
7
|
export function field(value) {
|
|
@@ -19,10 +18,7 @@ export function refusalBody(code, message, lines = []) {
|
|
|
19
18
|
const [first, ...rest] = rows(code, message, lines);
|
|
20
19
|
return [`refused\t${first}`, ...rest].join('\n');
|
|
21
20
|
}
|
|
22
|
-
/*
|
|
23
|
-
* The same without the word that names it, for a host that adds the prefix in its CLI:
|
|
24
|
-
* The context CLI puts the prefix on, so a CLI of an older build still prints one refusal.
|
|
25
|
-
*/
|
|
21
|
+
/* Without the `refused` prefix, for a host whose CLI adds it, so a CLI of an older build still prints one refusal. */
|
|
26
22
|
export function refusalRows(code, message, lines = []) {
|
|
27
23
|
return rows(code, message, lines).join('\n');
|
|
28
24
|
}
|
package/dist/context/verb.js
CHANGED
|
@@ -223,11 +223,7 @@ export function lengthOf(input) {
|
|
|
223
223
|
export function requiredField(needs) {
|
|
224
224
|
return z.string({ error: needs }).min(1, needs);
|
|
225
225
|
}
|
|
226
|
-
/*
|
|
227
|
-
* What a refusal lists, or one line saying the set is empty. A refusal that promises the groups of a
|
|
228
|
-
* canvas and then prints nothing reads as a daemon that lost them, where the truth is that there are
|
|
229
|
-
* none, which is a different thing to do something about.
|
|
230
|
-
*/
|
|
226
|
+
/* A refusal that promises a list and prints nothing reads as a host that lost it, where there is none. */
|
|
231
227
|
export function orNote(lines, note) {
|
|
232
228
|
return lines.length === 0 ? [`note\t${note}`] : lines;
|
|
233
229
|
}
|
package/dist/host/agent-host.js
CHANGED
|
@@ -10,8 +10,8 @@ function isAgentRequest(type) {
|
|
|
10
10
|
}
|
|
11
11
|
/*
|
|
12
12
|
* The chats of an app that has no daemon: it answers the agent requests over any `FramePort` and
|
|
13
|
-
* sends the agent events back on it, frames checked on arrival. One port is one client.
|
|
14
|
-
* goes on at the next start through its CLI's own session.
|
|
13
|
+
* sends the agent events back on it, frames checked on arrival. One port is one client. A chat
|
|
14
|
+
* closed with the host goes on at the next start through its CLI's own session.
|
|
15
15
|
*/
|
|
16
16
|
export class AgentHost {
|
|
17
17
|
chats;
|
package/dist/host/handlers.js
CHANGED
|
@@ -81,7 +81,6 @@ export function chatHandlers(chats, providers) {
|
|
|
81
81
|
'chat.list': () => ({ chats: chats.list() })
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
/* The accounts are the person's settings of this host. */
|
|
85
84
|
export function accountHandlers(accounts) {
|
|
86
85
|
return {
|
|
87
86
|
'accounts.list': () => accounts.snapshot(),
|
package/dist/host/wiring.js
CHANGED
|
@@ -6,10 +6,10 @@ import { suggestChatTitle } from '../chat/chat-title.js';
|
|
|
6
6
|
import { VisualStore } from '../chat/visual-store.js';
|
|
7
7
|
import { ClaudeTitleReader } from '../chat/claude-title.js';
|
|
8
8
|
import { DEFAULT_CODEX_CLIENT } from '../chat/codex-transport.js';
|
|
9
|
+
import { errorText } from '../error-text.js';
|
|
9
10
|
import { definedEnv } from '../providers/accounts/launch.js';
|
|
10
11
|
import { ProviderAccountsService } from '../providers/accounts/service.js';
|
|
11
12
|
import { createClaudeProvider } from '../providers/claude-provider.js';
|
|
12
|
-
import { errorText } from '../error-text.js';
|
|
13
13
|
import { createCodexProvider } from '../providers/codex-provider.js';
|
|
14
14
|
import { codexRulesPathIn, defaultCodexHome, installCodexRules } from '../providers/codex-rules.js';
|
|
15
15
|
import { ProviderRegistry } from '../providers/registry.js';
|
package/dist/lineage.js
CHANGED
|
@@ -25,8 +25,8 @@ function openedByAgent(entry) {
|
|
|
25
25
|
return entry.agent && entry.relation !== 'fork';
|
|
26
26
|
}
|
|
27
27
|
/*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* Lives under the host's data folder, outside the agent-writable project, so an agent cannot reset its
|
|
29
|
+
* own depth; on disk, so a restart of the host does not reset it either.
|
|
30
30
|
*/
|
|
31
31
|
export class AgentLineageStore {
|
|
32
32
|
dir;
|
|
@@ -117,7 +117,6 @@ export class AgentLineageStore {
|
|
|
117
117
|
const entry = this.opened.get(nodeId);
|
|
118
118
|
return entry?.relation === 'fork' ? entry.openedBy : null;
|
|
119
119
|
}
|
|
120
|
-
/* The forks made from a chat. */
|
|
121
120
|
forksOf(nodeId) {
|
|
122
121
|
return [...this.opened.all()].filter((entry) => entry.relation === 'fork' && entry.openedBy === nodeId).map((entry) => entry.nodeId);
|
|
123
122
|
}
|
|
@@ -5,13 +5,11 @@ export const DeliverMessageWorkSchema = z.object({
|
|
|
5
5
|
payload: z.object({ from: z.string().min(1) })
|
|
6
6
|
});
|
|
7
7
|
/*
|
|
8
|
-
* Opens the turn a message earns a chat. The messages
|
|
9
|
-
*
|
|
10
|
-
* own prompt reads them through, so one message is heard once whichever of the two opens the turn.
|
|
8
|
+
* Opens the turn a message earns a chat. The messages wait in the notice store and the turn's preamble
|
|
9
|
+
* takes them (`NoticeNotes`), as a person's own prompt does, so a message is heard once either way.
|
|
11
10
|
*
|
|
12
|
-
* One attempt, never `wait
|
|
13
|
-
*
|
|
14
|
-
* make the host start turns nobody asked for long after the news was worth anything.
|
|
11
|
+
* One attempt, never `wait`: a chat already in a turn reads the message in front of its next one, and
|
|
12
|
+
* holding the entry would start turns nobody asked for long after the news mattered.
|
|
15
13
|
*/
|
|
16
14
|
export function deliverMessageHandler(deps) {
|
|
17
15
|
return async (entry) => {
|
|
@@ -27,11 +25,8 @@ export function deliverMessageHandler(deps) {
|
|
|
27
25
|
if (chat === null) {
|
|
28
26
|
return;
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* landed (`showNotices`), so anything the turn adds about it only says the same thing again;
|
|
33
|
-
* the label on the turn already names who it came from.
|
|
34
|
-
*/
|
|
28
|
+
// No note and no preamble on the thread: a person read the message as it landed (`showNotices`),
|
|
29
|
+
// and the label on the turn names who it came from.
|
|
35
30
|
chat.wake({
|
|
36
31
|
text: deps.words.prompt(waiting.length),
|
|
37
32
|
label: deps.words.label(waiting),
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Whether the turn a chat is running was itself opened by a message
|
|
3
|
-
*
|
|
4
|
-
* and nothing about that started with a person. It rides on the turn, so it survives a restart.
|
|
2
|
+
* Whether the turn a chat is running was itself opened by a message, which stops two agents that
|
|
3
|
+
* read each other from waking each other forever. It rides on the turn, so it survives a restart.
|
|
5
4
|
* Between turns the last one counts, since work it left running (a background subagent) may still
|
|
6
|
-
* write, and a turn the CLI opened by itself
|
|
5
|
+
* write, and a turn the CLI opened by itself takes the step of the turn before it.
|
|
7
6
|
*/
|
|
8
7
|
export function turnFromMessage(items, activeTurnId) {
|
|
9
8
|
const from = activeTurnId === null ? items.findLastIndex((item) => item.kind === 'turn') : items.findIndex((item) => item.id === activeTurnId);
|
|
@@ -40,10 +39,9 @@ export function chatNoticeTargets(chats) {
|
|
|
40
39
|
}
|
|
41
40
|
/*
|
|
42
41
|
* One message on its way to a chat, and whether the chat owes a turn on it. A chat between turns gets
|
|
43
|
-
* one,
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* host runs something else in, such as a terminal, is the host's to deliver to before this.
|
|
42
|
+
* one, as for a settled task, or the message sits there until someone happens to prompt that chat.
|
|
43
|
+
* One step deep: the turn a message opened wakes nobody with a message of its own. A node that runs
|
|
44
|
+
* something else, such as a terminal, is the host's to deliver to before this.
|
|
47
45
|
*/
|
|
48
46
|
export async function deliverToChat(store, targets, notice) {
|
|
49
47
|
const waiting = await store.put(notice);
|
|
@@ -60,10 +58,8 @@ export async function deliverToChat(store, targets, notice) {
|
|
|
60
58
|
return { outcome: 'wake', waiting };
|
|
61
59
|
}
|
|
62
60
|
/*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* An id no chat holds shows nothing and marks nothing, so the chat that opens on that id later still
|
|
66
|
-
* has all of it.
|
|
61
|
+
* Shows a person the message in the chat's thread the moment it lands, not when the model gets round
|
|
62
|
+
* to it. An id no chat holds shows and marks nothing, so a chat that opens on it later still has all of it.
|
|
67
63
|
*/
|
|
68
64
|
export async function showNotices(store, chat, words, targetId) {
|
|
69
65
|
if (!(await chat.has(targetId))) {
|
|
@@ -5,10 +5,8 @@ import { RecordDirectory } from '../record-directory.js';
|
|
|
5
5
|
// Per receiver. Past this the oldest goes: an agent in a loop must not fill a queue nobody reads.
|
|
6
6
|
export const MAX_NOTICES = 10;
|
|
7
7
|
/*
|
|
8
|
-
* A message
|
|
9
|
-
*
|
|
10
|
-
* something that has moved on. The cap alone would not do it: one message in a queue of one waits
|
|
11
|
-
* for ever.
|
|
8
|
+
* A message is news about work happening now ("the build is green"), and a model reading it six hours
|
|
9
|
+
* late would act on something that moved on. The cap alone would leave a queue of one waiting forever.
|
|
12
10
|
*/
|
|
13
11
|
export const NOTICE_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
|
14
12
|
const NoticeSchema = z.object({
|
|
@@ -109,7 +107,7 @@ export class NoticeStore {
|
|
|
109
107
|
fresh(notice) {
|
|
110
108
|
return this.now() - notice.createdAt < NOTICE_MAX_AGE_MS;
|
|
111
109
|
}
|
|
112
|
-
/*
|
|
110
|
+
/* In memory and on disk both; an empty queue leaves no file behind. */
|
|
113
111
|
async persist(targetId, queue) {
|
|
114
112
|
if (queue.length === 0) {
|
|
115
113
|
await this.queues.remove(targetId);
|
|
@@ -80,6 +80,9 @@ export class OutboxWorker {
|
|
|
80
80
|
const now = this.clock.now();
|
|
81
81
|
const busy = new Set(this.running);
|
|
82
82
|
let nextDue = null;
|
|
83
|
+
const dueAt = (at) => {
|
|
84
|
+
nextDue = nextDue === null ? at : Math.min(nextDue, at);
|
|
85
|
+
};
|
|
83
86
|
for (const entry of this.store.list()) {
|
|
84
87
|
// A waiting entry holds no lane. The resume of a chat must not queue behind a wake that waits for it.
|
|
85
88
|
if (busy.has(entry.target) || this.waiting.has(entry.id)) {
|
|
@@ -87,7 +90,7 @@ export class OutboxWorker {
|
|
|
87
90
|
}
|
|
88
91
|
// Nor does work due at a later time, which nothing owed before it has to wait for.
|
|
89
92
|
if (entry.attempts === 0 && entry.notBefore > now) {
|
|
90
|
-
|
|
93
|
+
dueAt(entry.notBefore);
|
|
91
94
|
continue;
|
|
92
95
|
}
|
|
93
96
|
const lanes = this.store.lanesOf(entry);
|
|
@@ -100,7 +103,7 @@ export class OutboxWorker {
|
|
|
100
103
|
continue;
|
|
101
104
|
}
|
|
102
105
|
if (entry.notBefore > now) {
|
|
103
|
-
|
|
106
|
+
dueAt(entry.notBefore);
|
|
104
107
|
continue;
|
|
105
108
|
}
|
|
106
109
|
void this.run(entry);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AgentKind, type ProviderAccount, type ProviderAccountCreatePayload, type ProviderAccountCreateResult, type ProviderAccountMap, type ProviderAccounts, type ProviderInfo } from '@adecore/agent-contracts';
|
|
2
|
+
import { type CodexClientInfo } from '../../chat/codex-transport.ts';
|
|
2
3
|
import type { AgentSink } from '../../events.ts';
|
|
3
4
|
import type { ChatProvider } from '../provider.ts';
|
|
4
5
|
import { type Env } from './accounts.ts';
|
|
@@ -6,7 +7,6 @@ import { type AccountLaunches } from './launch.ts';
|
|
|
6
7
|
import { type ShadowHomeReport } from './shadow-home.ts';
|
|
7
8
|
import { type AskAccount } from './status.ts';
|
|
8
9
|
import { type AccountsHost, type SecretStore } from './variables.ts';
|
|
9
|
-
import { type CodexClientInfo } from '../../chat/codex-transport.ts';
|
|
10
10
|
export interface ProviderAccountsOptions {
|
|
11
11
|
home: string;
|
|
12
12
|
host?: AccountsHost;
|
|
@@ -68,6 +68,7 @@ export declare class ProviderAccountsService implements AccountLaunches {
|
|
|
68
68
|
create(payload: ProviderAccountCreatePayload): Promise<ProviderAccountCreateResult>;
|
|
69
69
|
watchLogin(id: string): Promise<void>;
|
|
70
70
|
refresh(): Promise<ProviderAccounts>;
|
|
71
|
+
private folderOf;
|
|
71
72
|
private runWatch;
|
|
72
73
|
private serial;
|
|
73
74
|
private commit;
|