@adecore/agents 0.31.3 → 0.31.5
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/chat/acp-backend.d.ts +7 -0
- package/dist/chat/acp-backend.js +112 -21
- package/dist/chat/acp-protocol.d.ts +18 -5
- package/dist/chat/acp-protocol.js +53 -12
- package/dist/chat/index.d.ts +1 -1
- package/dist/providers/accounts/acp-account.d.ts +1 -0
- package/dist/providers/accounts/acp-account.js +25 -3
- package/dist/providers/acp-agent-state.d.ts +35 -0
- package/dist/providers/acp-agent-state.js +170 -0
- package/dist/providers/acp-provider.d.ts +2 -0
- package/dist/providers/acp-provider.js +29 -13
- package/dist/providers/catalog.d.ts +4 -2
- package/dist/providers/catalog.js +18 -5
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/provider.d.ts +1 -0
- package/dist/providers/registry.d.ts +1 -0
- package/dist/providers/registry.js +9 -0
- package/dist/testing/fake-acp.d.ts +4 -0
- package/dist/testing/fake-acp.js +33 -5
- package/package.json +3 -3
- package/src/chat/acp-backend.ts +120 -20
- package/src/chat/acp-protocol.ts +79 -14
- package/src/chat/index.ts +2 -0
- package/src/providers/accounts/acp-account.ts +34 -3
- package/src/providers/acp-agent-state.ts +206 -0
- package/src/providers/acp-provider.ts +32 -15
- package/src/providers/catalog.ts +23 -6
- package/src/providers/index.ts +1 -0
- package/src/providers/provider.ts +2 -0
- package/src/providers/registry.ts +10 -0
- package/src/testing/fake-acp.ts +43 -5
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { ModelOptionDescriptorSchema } from '@adecore/agent-contracts';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const AdoptedSchema = z.object({
|
|
4
|
+
defaultModel: z.string().min(1),
|
|
5
|
+
models: z.array(z.object({ slug: z.string().min(1), name: z.string(), contextWindow: z.number().int().positive().optional() })).min(1),
|
|
6
|
+
options: z.array(ModelOptionDescriptorSchema)
|
|
7
|
+
});
|
|
8
|
+
/* An agent's model id may reach a command line, so one that reads as a flag is left out. */
|
|
9
|
+
function adoptableModels(info) {
|
|
10
|
+
return (info.models?.models ?? [])
|
|
11
|
+
.filter((model) => model.id !== '' && !model.id.startsWith('-'))
|
|
12
|
+
.map((model) => ({ slug: model.id, name: model.name, ...(model.contextWindow === null ? {} : { contextWindow: model.contextWindow }) }));
|
|
13
|
+
}
|
|
14
|
+
/* The reasoning effort a session offers, as the `effort` option the composer draws for every provider. */
|
|
15
|
+
function effortOption(info) {
|
|
16
|
+
const level = info.thoughtLevel;
|
|
17
|
+
if (level === null || !level.choices.some((choice) => choice.id === level.current)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
id: 'effort',
|
|
23
|
+
label: 'Reasoning',
|
|
24
|
+
type: 'select',
|
|
25
|
+
defaultChoice: level.current,
|
|
26
|
+
choices: level.choices.map((choice) => ({
|
|
27
|
+
id: choice.id,
|
|
28
|
+
label: choice.name,
|
|
29
|
+
...(choice.description === null ? {} : { description: choice.description })
|
|
30
|
+
}))
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
/* Parses one stored part on its own, so a part a later version wrote differently costs only that part. */
|
|
35
|
+
function storedPart(schema, value) {
|
|
36
|
+
const parsed = schema.safeParse(value);
|
|
37
|
+
return parsed.success ? parsed.data : null;
|
|
38
|
+
}
|
|
39
|
+
/*
|
|
40
|
+
* What an agent reported about itself, outside any one session: the models it offers and what follows from it for the
|
|
41
|
+
* provider's capabilities. Kept in the host's store when it has one, and every change is told to the listeners, so a host
|
|
42
|
+
* can hand its clients the new list.
|
|
43
|
+
*/
|
|
44
|
+
export class AcpAgentState {
|
|
45
|
+
// Changed in place, so a host that spread the provider into a value of its own reads the same object.
|
|
46
|
+
capabilities;
|
|
47
|
+
options;
|
|
48
|
+
listeners = new Set();
|
|
49
|
+
adopted = null;
|
|
50
|
+
// Whether the agent reported its context window in a `usage_update`, once in any session.
|
|
51
|
+
reportsContext = false;
|
|
52
|
+
// The slash commands the last session listed; null until one did.
|
|
53
|
+
commands = null;
|
|
54
|
+
constructor(options) {
|
|
55
|
+
this.options = options;
|
|
56
|
+
this.capabilities = { ...options.base, ...options.overrides };
|
|
57
|
+
this.restore();
|
|
58
|
+
}
|
|
59
|
+
onChange(listener) {
|
|
60
|
+
this.listeners.add(listener);
|
|
61
|
+
return () => {
|
|
62
|
+
this.listeners.delete(listener);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
session(info) {
|
|
66
|
+
if (!this.options.adoptModels || info.models === null) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const adopted = { defaultModel: info.models.current, models: adoptableModels(info), options: effortOption(info) };
|
|
70
|
+
const changed = this.adopt(adopted);
|
|
71
|
+
this.settle(changed);
|
|
72
|
+
}
|
|
73
|
+
commandsListed(commands) {
|
|
74
|
+
const names = [...new Set(commands)].sort();
|
|
75
|
+
if (this.commands !== null && JSON.stringify(names) === JSON.stringify(this.commands)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.commands = names;
|
|
79
|
+
const changed = this.refresh();
|
|
80
|
+
// The commands are saved even when the capabilities stay, so a restart knows them too.
|
|
81
|
+
this.save();
|
|
82
|
+
if (changed) {
|
|
83
|
+
this.notify();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
contextReported() {
|
|
87
|
+
if (this.reportsContext) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.reportsContext = true;
|
|
91
|
+
this.settle(this.refresh());
|
|
92
|
+
}
|
|
93
|
+
restore() {
|
|
94
|
+
let stored;
|
|
95
|
+
try {
|
|
96
|
+
stored = this.options.store?.load();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (typeof stored !== 'object' || stored === null) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const record = stored;
|
|
105
|
+
const adopted = storedPart(AdoptedSchema, record.models);
|
|
106
|
+
if (adopted !== null && this.options.adoptModels) {
|
|
107
|
+
this.adopt(adopted);
|
|
108
|
+
}
|
|
109
|
+
this.reportsContext = record.reportsContext === true;
|
|
110
|
+
this.commands = storedPart(z.array(z.string()), record.commands);
|
|
111
|
+
this.refresh();
|
|
112
|
+
}
|
|
113
|
+
/* What the agent showed it can do, under what the host set itself. */
|
|
114
|
+
learned() {
|
|
115
|
+
const commands = this.commands ?? [];
|
|
116
|
+
return {
|
|
117
|
+
...(this.reportsContext ? { reportsContextWindow: true } : {}),
|
|
118
|
+
...(commands.includes('compact') ? { compaction: 'prompt' } : {}),
|
|
119
|
+
...(commands.includes('goal') ? { goals: 'command' } : {})
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/* Lays what the agent showed over the capabilities, in place; answers whether they changed. */
|
|
123
|
+
refresh() {
|
|
124
|
+
const next = { ...this.options.base, ...this.learned(), ...this.options.overrides };
|
|
125
|
+
if (JSON.stringify(next) === JSON.stringify(this.capabilities)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const capabilities = this.capabilities;
|
|
129
|
+
for (const key of Object.keys(capabilities)) {
|
|
130
|
+
delete capabilities[key];
|
|
131
|
+
}
|
|
132
|
+
Object.assign(capabilities, next);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
/* Answers whether the catalog changed; a list without its default is refused there and leaves the one adopted before. */
|
|
136
|
+
adopt(adopted) {
|
|
137
|
+
if (!adopted.models.some((model) => model.slug === adopted.defaultModel)) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
this.adopted = adopted;
|
|
141
|
+
return this.options.catalog.adopt(adopted.models, adopted.defaultModel, adopted.options);
|
|
142
|
+
}
|
|
143
|
+
/* Saves and tells the listeners once something changed. */
|
|
144
|
+
settle(changed) {
|
|
145
|
+
if (changed) {
|
|
146
|
+
this.save();
|
|
147
|
+
this.notify();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
save() {
|
|
151
|
+
try {
|
|
152
|
+
this.options.store?.save(this.stored());
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// A store that cannot write costs the next start what it would have kept, never this session.
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
notify() {
|
|
159
|
+
for (const listener of [...this.listeners]) {
|
|
160
|
+
listener();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
stored() {
|
|
164
|
+
return {
|
|
165
|
+
...(this.adopted === null ? {} : { models: this.adopted }),
|
|
166
|
+
...(this.reportsContext ? { reportsContext: true } : {}),
|
|
167
|
+
...(this.commands === null ? {} : { commands: this.commands })
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentKind, ProviderCapabilities, ProviderInstall } from '@adecore/agent-contracts';
|
|
2
2
|
import { type AcpAgentSpec } from '../chat/acp-backend.ts';
|
|
3
|
+
import { type AcpCatalogStore } from './acp-agent-state.ts';
|
|
3
4
|
import type { ModelCatalog } from './catalog.ts';
|
|
4
5
|
import { type CliDetection } from './detect.ts';
|
|
5
6
|
import type { ChatProvider, ProviderHome } from './provider.ts';
|
|
@@ -22,5 +23,6 @@ export interface AcpProviderSpec extends AcpAgentSpec {
|
|
|
22
23
|
accountProbe?: boolean;
|
|
23
24
|
adoptModels?: boolean;
|
|
24
25
|
usageLog?: string;
|
|
26
|
+
catalogStore?: AcpCatalogStore;
|
|
25
27
|
}
|
|
26
28
|
export declare function createAcpProvider(spec: AcpProviderSpec): ChatProvider;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createAcpBackend } from '../chat/acp-backend.js';
|
|
2
2
|
import { askAcpAccount } from './accounts/acp-account.js';
|
|
3
|
+
import { AcpAgentState } from './acp-agent-state.js';
|
|
3
4
|
import { writeAcpUsage } from './acp-usage-log.js';
|
|
4
5
|
import { detectCli } from './detect.js';
|
|
5
6
|
/*
|
|
6
|
-
* What every ACP agent can do through this backend. A file change comes as the text
|
|
7
|
-
* a message names its mentions and attachments by path,
|
|
7
|
+
* What every ACP agent can do through this backend, before what it shows of itself. A file change comes as the text
|
|
8
|
+
* before and after; a message names its mentions and attachments by path, and carries the files themselves where the agent takes them.
|
|
8
9
|
*/
|
|
9
10
|
export const ACP_CAPABILITIES = {
|
|
10
11
|
chat: true,
|
|
@@ -25,18 +26,22 @@ export const ACP_CAPABILITIES = {
|
|
|
25
26
|
};
|
|
26
27
|
/* Agents whose own usage files the scanner reads; a log of their turns would count every one twice. */
|
|
27
28
|
const OWN_USAGE_FILES = new Set(['grok']);
|
|
28
|
-
/* An agent's model id may reach a command line, so one that reads as a flag is left out. */
|
|
29
|
-
function adoptableModels(info) {
|
|
30
|
-
return (info.models?.models ?? []).filter((model) => model.id !== '' && !model.id.startsWith('-')).map((model) => ({ slug: model.id, name: model.name }));
|
|
31
|
-
}
|
|
32
29
|
export function createAcpProvider(spec) {
|
|
33
|
-
const
|
|
30
|
+
const state = new AcpAgentState({
|
|
31
|
+
catalog: spec.catalog,
|
|
32
|
+
adoptModels: spec.adoptModels !== false,
|
|
33
|
+
store: spec.catalogStore ?? null,
|
|
34
|
+
base: ACP_CAPABILITIES,
|
|
35
|
+
overrides: spec.capabilities ?? {}
|
|
36
|
+
});
|
|
34
37
|
const onSession = (info) => {
|
|
35
|
-
|
|
36
|
-
spec.catalog.adopt(adoptableModels(info), info.models.current);
|
|
37
|
-
}
|
|
38
|
+
state.session(info);
|
|
38
39
|
spec.onSession?.(info);
|
|
39
40
|
};
|
|
41
|
+
const onCommands = (commands) => {
|
|
42
|
+
state.commandsListed(commands);
|
|
43
|
+
spec.onCommands?.(commands);
|
|
44
|
+
};
|
|
40
45
|
const usageLog = spec.usageLog === undefined || OWN_USAGE_FILES.has(spec.kind) ? null : spec.usageLog;
|
|
41
46
|
const onUsage = usageLog === null
|
|
42
47
|
? spec.onUsage
|
|
@@ -48,7 +53,8 @@ export function createAcpProvider(spec) {
|
|
|
48
53
|
kind: spec.kind,
|
|
49
54
|
name: spec.name,
|
|
50
55
|
catalog: spec.catalog,
|
|
51
|
-
capabilities,
|
|
56
|
+
capabilities: state.capabilities,
|
|
57
|
+
onChange: (listener) => state.onChange(listener),
|
|
52
58
|
command: [spec.command, ...(spec.args ?? [])],
|
|
53
59
|
resumeCommand: spec.resumeCommand ?? '',
|
|
54
60
|
...(spec.ignoresProjectConfig === undefined ? {} : { ignoresProjectConfig: spec.ignoresProjectConfig }),
|
|
@@ -70,9 +76,19 @@ export function createAcpProvider(spec) {
|
|
|
70
76
|
command,
|
|
71
77
|
env: { ...env, ...spec.env },
|
|
72
78
|
...(spec.client ? { client: spec.client } : {}),
|
|
73
|
-
onSession
|
|
79
|
+
onSession,
|
|
80
|
+
onCommands
|
|
74
81
|
})
|
|
75
82
|
}),
|
|
76
|
-
createBackend: createAcpBackend({
|
|
83
|
+
createBackend: createAcpBackend({
|
|
84
|
+
...spec,
|
|
85
|
+
onSession,
|
|
86
|
+
onCommands,
|
|
87
|
+
onContextReport: () => {
|
|
88
|
+
state.contextReported();
|
|
89
|
+
spec.onContextReport?.();
|
|
90
|
+
},
|
|
91
|
+
...(onUsage === undefined ? {} : { onUsage })
|
|
92
|
+
})
|
|
77
93
|
};
|
|
78
94
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import type { ModelCatalogData, ModelInfo, ModelSelection } from '@adecore/agent-contracts';
|
|
1
|
+
import type { ModelCatalogData, ModelInfo, ModelOptionDescriptor, ModelSelection } from '@adecore/agent-contracts';
|
|
2
2
|
export type ModelCatalogInput = Omit<ModelCatalogData, 'updatedAt'> & {
|
|
3
3
|
updatedAt?: string;
|
|
4
4
|
};
|
|
5
5
|
export declare class ModelCatalog {
|
|
6
6
|
private readonly shippedAt;
|
|
7
7
|
private data;
|
|
8
|
+
private hostWindows;
|
|
8
9
|
constructor(data?: ModelCatalogInput);
|
|
9
10
|
get defaultModel(): string;
|
|
10
11
|
replace(data: ModelCatalogData): boolean;
|
|
11
12
|
adopt(models: ReadonlyArray<{
|
|
12
13
|
slug: string;
|
|
13
14
|
name: string;
|
|
14
|
-
|
|
15
|
+
contextWindow?: number;
|
|
16
|
+
}>, defaultModel: string, options?: readonly ModelOptionDescriptor[]): boolean;
|
|
15
17
|
list(): ModelInfo[];
|
|
16
18
|
nameOf(slug: string): string;
|
|
17
19
|
resolveModel(name: string): string | null;
|
|
@@ -11,6 +11,8 @@ function timeOf(data) {
|
|
|
11
11
|
export class ModelCatalog {
|
|
12
12
|
shippedAt;
|
|
13
13
|
data;
|
|
14
|
+
// The sizes the host's own default had before an agent's models replaced it, for an adopted model that names none.
|
|
15
|
+
hostWindows;
|
|
14
16
|
constructor(data = manifest) {
|
|
15
17
|
this.shippedAt = timeOf(data);
|
|
16
18
|
this.data = data;
|
|
@@ -25,23 +27,34 @@ export class ModelCatalog {
|
|
|
25
27
|
}
|
|
26
28
|
const changed = JSON.stringify(data) !== JSON.stringify(this.data);
|
|
27
29
|
this.data = data;
|
|
30
|
+
this.hostWindows = undefined;
|
|
28
31
|
return changed;
|
|
29
32
|
}
|
|
30
33
|
/*
|
|
31
34
|
* Takes the models an agent reported for itself in place of the list, the agent's current model as the
|
|
32
35
|
* default and its order kept; answers whether the models changed. Every model gets the profile the
|
|
33
|
-
* default has now, as an agent names its models but not what each can be asked for
|
|
36
|
+
* default has now, as an agent names its models but not what each can be asked for, with `options`
|
|
37
|
+
* in place of the profile's options of the same id, and the context window in tokens a model names as its only size.
|
|
34
38
|
*/
|
|
35
|
-
adopt(models, defaultModel) {
|
|
39
|
+
adopt(models, defaultModel, options = []) {
|
|
36
40
|
if (models.length === 0 || !models.some((model) => model.slug === defaultModel)) {
|
|
37
41
|
return false;
|
|
38
42
|
}
|
|
39
|
-
const
|
|
43
|
+
const base = this.profileOf(this.data.defaultModel) ?? { options: [], contextWindowTokens: {} };
|
|
44
|
+
const replaced = new Set(options.map((option) => option.id));
|
|
45
|
+
this.hostWindows ??= base.contextWindowTokens;
|
|
46
|
+
const profile = { ...base, contextWindowTokens: this.hostWindows, options: [...base.options.filter((option) => !replaced.has(option.id)), ...options] };
|
|
47
|
+
const profileName = (window) => (window === undefined ? ADOPTED_PROFILE : `${ADOPTED_PROFILE}-${window}`);
|
|
48
|
+
const profiles = {};
|
|
49
|
+
for (const model of models) {
|
|
50
|
+
profiles[profileName(model.contextWindow)] =
|
|
51
|
+
model.contextWindow === undefined ? profile : { ...profile, contextWindowTokens: { '*': model.contextWindow } };
|
|
52
|
+
}
|
|
40
53
|
const data = {
|
|
41
54
|
...(this.data.updatedAt === undefined ? {} : { updatedAt: this.data.updatedAt }),
|
|
42
55
|
defaultModel,
|
|
43
|
-
profiles
|
|
44
|
-
models: models.map((model) => ({ slug: model.slug, name: model.name, profile:
|
|
56
|
+
profiles,
|
|
57
|
+
models: models.map((model) => ({ slug: model.slug, name: model.name, profile: profileName(model.contextWindow) }))
|
|
45
58
|
};
|
|
46
59
|
const changed = JSON.stringify(data) !== JSON.stringify(this.data);
|
|
47
60
|
this.data = data;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { type AcpCatalogStore } from './acp-agent-state.ts';
|
|
1
2
|
export { ACP_CAPABILITIES, type AcpProviderSpec, createAcpProvider } from './acp-provider.ts';
|
|
2
3
|
export { type AcpAccountProbe, askAcpAccount, type ProbeTimer } from './accounts/acp-account.ts';
|
|
3
4
|
export { AccountError, type AccountLaunches, storedAccount, launchEnv } from './accounts/launch.ts';
|
package/dist/providers/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
|
+
export {} from './acp-agent-state.js';
|
|
2
3
|
export { ACP_CAPABILITIES, createAcpProvider } from './acp-provider.js';
|
|
3
4
|
export { askAcpAccount } from './accounts/acp-account.js';
|
|
4
5
|
export { AccountError, storedAccount, launchEnv } from './accounts/launch.js';
|
|
@@ -25,5 +25,6 @@ export interface ChatProvider {
|
|
|
25
25
|
detect(command: string, env: Record<string, string | undefined>): Promise<CliDetection>;
|
|
26
26
|
enabled?(): boolean;
|
|
27
27
|
askAccount?(command: readonly string[], env: Record<string, string | undefined>): Promise<AccountReading>;
|
|
28
|
+
onChange?(listener: () => void): () => void;
|
|
28
29
|
createBackend(launch: BackendLaunch, host: BackendHost): ChatBackend;
|
|
29
30
|
}
|
|
@@ -23,6 +23,7 @@ export declare class ProviderRegistry {
|
|
|
23
23
|
oneShotProvider(preferred?: AgentKind): Promise<ChatProvider | null>;
|
|
24
24
|
catalogFor(kind: AgentKind): ModelCatalog;
|
|
25
25
|
enabled(kind: AgentKind): boolean;
|
|
26
|
+
onChange(listener: (kind: AgentKind) => void): () => void;
|
|
26
27
|
invalidate(kind: AgentKind): void;
|
|
27
28
|
protected fallback(kind: AgentKind): ChatProvider;
|
|
28
29
|
private detection;
|
|
@@ -97,6 +97,15 @@ export class ProviderRegistry {
|
|
|
97
97
|
enabled(kind) {
|
|
98
98
|
return this.get(kind).enabled?.() !== false;
|
|
99
99
|
}
|
|
100
|
+
/* Calls the listener with the kind of every provider whose models or capabilities changed, so a host can list them again; answers how to stop. */
|
|
101
|
+
onChange(listener) {
|
|
102
|
+
const stops = this.providers.flatMap((provider) => (provider.onChange ? [provider.onChange(() => listener(provider.kind))] : []));
|
|
103
|
+
return () => {
|
|
104
|
+
for (const stop of stops) {
|
|
105
|
+
stop();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
100
109
|
invalidate(kind) {
|
|
101
110
|
this.cache.delete(kind);
|
|
102
111
|
}
|
|
@@ -7,6 +7,10 @@ export interface FakeAcpOptions {
|
|
|
7
7
|
legacyModels?: boolean;
|
|
8
8
|
closeSession?: boolean;
|
|
9
9
|
turnUsage?: Record<string, number>;
|
|
10
|
+
reasoningEffort?: 'accept' | 'reject';
|
|
11
|
+
contextWindows?: boolean;
|
|
12
|
+
commands?: readonly string[];
|
|
13
|
+
embeddedContext?: boolean;
|
|
10
14
|
}
|
|
11
15
|
export declare function fakeAcpWith(options: FakeAcpOptions): FakeCli;
|
|
12
16
|
export declare const fakeAcp: FakeCli;
|
package/dist/testing/fake-acp.js
CHANGED
|
@@ -7,6 +7,7 @@ export function fakeAcpWith(options) {
|
|
|
7
7
|
let sessionId = '';
|
|
8
8
|
let mode = modes[0] ?? '';
|
|
9
9
|
let model = 'fake-small';
|
|
10
|
+
let effort = 'low';
|
|
10
11
|
let serverRequestId = 0;
|
|
11
12
|
let callCounter = 0;
|
|
12
13
|
let prompt = null;
|
|
@@ -37,15 +38,29 @@ export function fakeAcpWith(options) {
|
|
|
37
38
|
prompt = null;
|
|
38
39
|
};
|
|
39
40
|
const sessionState = () => {
|
|
41
|
+
const meta = (totalContextTokens) => (options.contextWindows ? { _meta: { totalContextTokens } } : {});
|
|
40
42
|
const modelChoices = [
|
|
41
|
-
{ value: 'fake-small', name: 'Fake small' },
|
|
42
|
-
{ value: 'fake-large', name: 'Fake large' }
|
|
43
|
+
{ value: 'fake-small', name: 'Fake small', ...meta(128000) },
|
|
44
|
+
{ value: 'fake-large', name: 'Fake large', ...meta(1000000) }
|
|
43
45
|
];
|
|
46
|
+
const effortChoices = [
|
|
47
|
+
{ value: 'low', name: 'Low', description: 'Answers quickly' },
|
|
48
|
+
{ value: 'high', name: 'High' },
|
|
49
|
+
...(model === 'fake-large' ? [{ value: 'xhigh', name: 'Extra high' }] : [])
|
|
50
|
+
];
|
|
51
|
+
const effortOption = options.reasoningEffort === undefined
|
|
52
|
+
? []
|
|
53
|
+
: [{ id: 'reasoning_effort', name: 'Reasoning effort', type: 'select', currentValue: effort, options: effortChoices }];
|
|
44
54
|
return {
|
|
45
55
|
...(modes.length === 0 ? {} : { modes: { currentModeId: mode, availableModes: modes.map((id) => ({ id, name: id })) } }),
|
|
46
56
|
...(options.legacyModels
|
|
47
|
-
? { models: { currentModelId: model, availableModels: modelChoices.map((choice) => ({ modelId:
|
|
48
|
-
: {
|
|
57
|
+
? { models: { currentModelId: model, availableModels: modelChoices.map(({ value, ...choice }) => ({ modelId: value, ...choice })) } }
|
|
58
|
+
: {
|
|
59
|
+
configOptions: [
|
|
60
|
+
{ id: 'model', name: 'Model', type: 'select', category: 'model', currentValue: model, options: modelChoices },
|
|
61
|
+
...effortOption
|
|
62
|
+
]
|
|
63
|
+
})
|
|
49
64
|
};
|
|
50
65
|
};
|
|
51
66
|
const askPermission = (toolCall, onAnswer) => {
|
|
@@ -179,7 +194,7 @@ export function fakeAcpWith(options) {
|
|
|
179
194
|
protocolVersion: 1,
|
|
180
195
|
agentCapabilities: {
|
|
181
196
|
loadSession: options.loadSession ?? true,
|
|
182
|
-
promptCapabilities: { image: true, audio: false, embeddedContext:
|
|
197
|
+
promptCapabilities: { image: true, audio: false, embeddedContext: options.embeddedContext === true },
|
|
183
198
|
sessionCapabilities: { ...(options.resumeSession ? { resume: {} } : {}), ...(options.closeSession ? { close: {} } : {}) }
|
|
184
199
|
},
|
|
185
200
|
authMethods: options.authMethod === undefined ? [] : [{ id: options.authMethod, name: 'Sign in' }],
|
|
@@ -197,6 +212,12 @@ export function fakeAcpWith(options) {
|
|
|
197
212
|
}
|
|
198
213
|
sessionId = `fake-session-${Math.random().toString(36).slice(2, 8)}`;
|
|
199
214
|
sessionHistory.set(sessionId, []);
|
|
215
|
+
if (options.commands !== undefined) {
|
|
216
|
+
update({
|
|
217
|
+
sessionUpdate: 'available_commands_update',
|
|
218
|
+
availableCommands: options.commands.map((name) => ({ name, description: name }))
|
|
219
|
+
});
|
|
220
|
+
}
|
|
200
221
|
respond(id, { sessionId, ...sessionState() });
|
|
201
222
|
return;
|
|
202
223
|
case 'session/load':
|
|
@@ -241,6 +262,13 @@ export function fakeAcpWith(options) {
|
|
|
241
262
|
if (params.configId === 'model') {
|
|
242
263
|
model = String(params.value);
|
|
243
264
|
}
|
|
265
|
+
if (params.configId === 'reasoning_effort') {
|
|
266
|
+
if (options.reasoningEffort === 'reject') {
|
|
267
|
+
refuse(id, -32602, 'Reasoning effort is locked');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
effort = String(params.value);
|
|
271
|
+
}
|
|
244
272
|
respond(id, { configOptions: sessionState().configOptions ?? [] });
|
|
245
273
|
return;
|
|
246
274
|
case 'session/prompt':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.5",
|
|
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.31.
|
|
85
|
-
"@adecore/intelligent-ui": "0.31.
|
|
84
|
+
"@adecore/agent-contracts": "0.31.5",
|
|
85
|
+
"@adecore/intelligent-ui": "0.31.5",
|
|
86
86
|
"zod": "^4.6.5"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|