@lensagent/core 0.1.0 → 0.1.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/README.md +63 -4
- package/config/lens.example.jsonc +36 -13
- package/dist/adapters/executor-router.d.ts +20 -0
- package/dist/adapters/executor-router.js +86 -0
- package/dist/adapters/executor.d.ts +9 -3
- package/dist/adapters/executor.js +196 -61
- package/dist/adapters/lsp.d.ts +2 -1
- package/dist/adapters/lsp.js +98 -41
- package/dist/adapters/mcp.js +52 -17
- package/dist/adapters/revert.d.ts +5 -4
- package/dist/adapters/revert.js +6 -64
- package/dist/adapters/web.d.ts +9 -0
- package/dist/adapters/web.js +244 -0
- package/dist/config/files.d.ts +14 -0
- package/dist/config/files.js +57 -0
- package/dist/config/runtime.d.ts +3 -4
- package/dist/config/runtime.js +116 -97
- package/dist/config/schema.d.ts +56 -10
- package/dist/config/schema.js +48 -8
- package/dist/contracts/capability.d.ts +27 -1
- package/dist/contracts/domain.d.ts +1 -0
- package/dist/contracts/model.d.ts +33 -6
- package/dist/contracts/review.d.ts +17 -0
- package/dist/contracts/review.js +1 -0
- package/dist/core/access-policy.d.ts +40 -0
- package/dist/core/access-policy.js +68 -0
- package/dist/core/access.d.ts +30 -2
- package/dist/core/access.js +159 -26
- package/dist/core/context-boundaries.d.ts +25 -0
- package/dist/core/context-boundaries.js +106 -0
- package/dist/core/context.d.ts +3 -0
- package/dist/core/context.js +11 -0
- package/dist/core/host.js +261 -173
- package/dist/entry/pair.d.ts +1 -0
- package/dist/entry/pair.js +14 -0
- package/dist/entry/run.js +1 -1
- package/dist/execution/lsp-process.d.ts +3 -2
- package/dist/execution/lsp-process.js +46 -28
- package/dist/http/server.d.ts +5 -0
- package/dist/http/server.js +262 -80
- package/dist/runtime/agent.d.ts +9 -4
- package/dist/runtime/agent.js +361 -286
- package/dist/runtime/artifacts.d.ts +13 -1
- package/dist/runtime/artifacts.js +137 -10
- package/dist/runtime/cache.d.ts +59 -16
- package/dist/runtime/cache.js +124 -43
- package/dist/runtime/calls.d.ts +1 -1
- package/dist/runtime/calls.js +164 -48
- package/dist/runtime/capabilities.d.ts +2 -2
- package/dist/runtime/capabilities.js +4 -5
- package/dist/runtime/cleanup.d.ts +5 -2
- package/dist/runtime/cleanup.js +48 -17
- package/dist/runtime/compaction.d.ts +9 -0
- package/dist/runtime/compaction.js +52 -0
- package/dist/runtime/context.d.ts +8 -4
- package/dist/runtime/context.js +115 -128
- package/dist/runtime/events.d.ts +7 -0
- package/dist/runtime/events.js +83 -0
- package/dist/runtime/images.d.ts +14 -0
- package/dist/runtime/images.js +82 -0
- package/dist/runtime/kernel-capabilities.d.ts +7 -1
- package/dist/runtime/kernel-capabilities.js +146 -68
- package/dist/runtime/model-audit.d.ts +9 -0
- package/dist/runtime/model-audit.js +39 -0
- package/dist/runtime/model.d.ts +109 -4
- package/dist/runtime/model.js +368 -143
- package/dist/runtime/observability.d.ts +1 -0
- package/dist/runtime/observability.js +60 -15
- package/dist/runtime/policy.d.ts +1 -1
- package/dist/runtime/policy.js +2 -2
- package/dist/runtime/profile.d.ts +2 -1
- package/dist/runtime/profile.js +12 -15
- package/dist/runtime/projects.d.ts +3 -1
- package/dist/runtime/projects.js +38 -17
- package/dist/runtime/ptc-worker.d.ts +1 -0
- package/dist/runtime/ptc-worker.js +97 -0
- package/dist/runtime/ptc.d.ts +16 -0
- package/dist/runtime/ptc.js +128 -0
- package/dist/runtime/results.d.ts +5 -4
- package/dist/runtime/results.js +29 -51
- package/dist/runtime/review.d.ts +4 -1
- package/dist/runtime/review.js +101 -22
- package/dist/runtime/scheduler.js +1 -0
- package/dist/runtime/skills.js +34 -5
- package/dist/runtime/task-audit.d.ts +21 -0
- package/dist/runtime/task-audit.js +122 -0
- package/dist/runtime/time.d.ts +9 -0
- package/dist/runtime/time.js +18 -0
- package/dist/runtime/tokenizer.d.ts +2 -0
- package/dist/runtime/tokenizer.js +26 -1
- package/dist/store/database.js +26 -1
- package/dist/store/event-log.d.ts +1 -0
- package/dist/store/event-log.js +22 -5
- package/dist/store/schema.d.ts +1 -1
- package/dist/store/schema.js +50 -1
- package/dist/store/store.d.ts +65 -19
- package/dist/store/store.js +986 -188
- package/package.json +18 -8
- package/proto/executor.proto +69 -55
package/dist/config/runtime.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { watch } from 'node:fs';
|
|
3
|
-
import { readFile, rename, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, dirname, isAbsolute, normalize, resolve } from 'node:path';
|
|
5
5
|
import { Ajv } from 'ajv';
|
|
6
6
|
import { canonicalize } from 'json-canonicalize';
|
|
7
7
|
import { parse } from 'jsonc-parser';
|
|
8
8
|
import { fail } from '../core/domain.js';
|
|
9
9
|
import { ConfigSchema } from './schema.js';
|
|
10
|
+
import { modelReasoningEfforts } from '../runtime/profile.js';
|
|
10
11
|
export class ConfigRuntime {
|
|
11
12
|
path;
|
|
12
13
|
#validate;
|
|
13
14
|
#snapshot;
|
|
14
15
|
#watcher;
|
|
15
16
|
#reloadTail = Promise.resolve();
|
|
17
|
+
#source = null;
|
|
18
|
+
#onChange;
|
|
16
19
|
constructor(root, path = 'config/lens.jsonc') {
|
|
17
20
|
this.path = resolve(root, path);
|
|
18
21
|
this.#validate = new Ajv({ allErrors: true, strict: false }).compile(ConfigSchema);
|
|
@@ -28,11 +31,13 @@ export class ConfigRuntime {
|
|
|
28
31
|
source = await readFile(this.path, 'utf8');
|
|
29
32
|
}
|
|
30
33
|
catch (error) {
|
|
34
|
+
this.#source = null;
|
|
31
35
|
fail('CONFIG_INVALID', 503, 'The configuration file could not be read.', { reason: message(error) });
|
|
32
36
|
}
|
|
37
|
+
this.#source = source;
|
|
33
38
|
const previous = this.#snapshot;
|
|
34
39
|
const fileDigest = hash(source);
|
|
35
|
-
if (previous?.fileDigest === fileDigest)
|
|
40
|
+
if (previous?.fileDigest === fileDigest && previous.error === null)
|
|
36
41
|
return previous;
|
|
37
42
|
const candidate = this.parse(source);
|
|
38
43
|
const config = previous === undefined ? candidate : effective(previous.config, candidate);
|
|
@@ -47,6 +52,15 @@ export class ConfigRuntime {
|
|
|
47
52
|
error: null
|
|
48
53
|
};
|
|
49
54
|
this.#snapshot = next;
|
|
55
|
+
if (previous !== undefined) {
|
|
56
|
+
try {
|
|
57
|
+
this.#onChange?.(next);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
this.#snapshot = { ...next, error: 'CONFIG_APPLY_FAILED', appliedHot: [] };
|
|
61
|
+
fail('CONFIG_APPLY_FAILED', 503, 'Configuration was saved but could not be applied.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
50
64
|
return next;
|
|
51
65
|
}
|
|
52
66
|
parse(source) {
|
|
@@ -58,89 +72,107 @@ export class ConfigRuntime {
|
|
|
58
72
|
const semantic = semanticIssues(document);
|
|
59
73
|
if (semantic.length > 0)
|
|
60
74
|
fail('CONFIG_INVALID', 400, 'The configuration is invalid.', { issues: semantic });
|
|
75
|
+
// Migrate the two former checklist names without advertising executable aliases.
|
|
76
|
+
for (const role of Object.values(document.roles)) {
|
|
77
|
+
role.capabilities = [...new Set(role.capabilities.filter((id) => id !== 'compact').map((id) => id === 'slash_command_list' || id === 'slash_command_run' ? 'slash_command' : id === 'update_plan' || id === 'update_todo' ? 'todo' : id))];
|
|
78
|
+
}
|
|
79
|
+
for (const preset of Object.values(document.policyPresets)) {
|
|
80
|
+
const policies = preset.capabilities;
|
|
81
|
+
const previous = policies.update_todo ?? policies.update_plan;
|
|
82
|
+
if (policies.todo === undefined && previous !== undefined)
|
|
83
|
+
policies.todo = previous;
|
|
84
|
+
const slashPolicy = policies.slash_command_run ?? policies.slash_command_list;
|
|
85
|
+
if (policies.slash_command === undefined && slashPolicy !== undefined)
|
|
86
|
+
policies.slash_command = slashPolicy;
|
|
87
|
+
delete policies.compact;
|
|
88
|
+
delete policies.slash_command_list;
|
|
89
|
+
delete policies.slash_command_run;
|
|
90
|
+
delete policies.update_plan;
|
|
91
|
+
delete policies.update_todo;
|
|
92
|
+
}
|
|
61
93
|
return document;
|
|
62
94
|
}
|
|
63
95
|
validate(source) {
|
|
64
96
|
try {
|
|
65
97
|
const config = this.parse(source);
|
|
66
|
-
return { valid: true, normalized_digest: hash(canonicalize(config)), errors: [] };
|
|
98
|
+
return { valid: true, normalized_digest: hash(canonicalize(config)), errors: [], warnings: unsupportedSettings(config) };
|
|
67
99
|
}
|
|
68
100
|
catch (error) {
|
|
69
|
-
return { valid: false, normalized_digest: null, errors: validationDetails(error) };
|
|
101
|
+
return { valid: false, normalized_digest: null, errors: validationDetails(error), warnings: [] };
|
|
70
102
|
}
|
|
71
103
|
}
|
|
72
|
-
replace(source, expectedFileDigest) {
|
|
73
|
-
if (expectedFileDigest !== this.current.fileDigest)
|
|
74
|
-
fail('CONFIG_CONFLICT', 409, 'The configuration file changed.');
|
|
75
|
-
// Resolved against the file, because the document the client edited was rendered from the file.
|
|
76
|
-
// Using the effective config would fail to restore a secret belonging to a restart-only setting
|
|
77
|
-
// that has been saved but not yet applied -- the placeholder would be written as the value.
|
|
78
|
-
const persisted = restoreSecrets(source, this.current.fileConfig);
|
|
79
|
-
this.parse(persisted);
|
|
104
|
+
replace(source, expectedFileDigest, authorize = () => { }) {
|
|
80
105
|
const operation = this.#reloadTail.then(async () => {
|
|
106
|
+
await authorize();
|
|
107
|
+
const currentSource = await readFile(this.path, 'utf8');
|
|
108
|
+
if (expectedFileDigest !== hash(currentSource))
|
|
109
|
+
fail('CONFIG_CONFLICT', 409, 'The configuration file changed.');
|
|
110
|
+
this.parse(source);
|
|
81
111
|
const temporary = `${this.path}.${process.pid}.${randomUUID()}.tmp`;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
112
|
+
try {
|
|
113
|
+
await writeFile(temporary, source, { encoding: 'utf8', mode: 0o600 });
|
|
114
|
+
await authorize();
|
|
115
|
+
if (expectedFileDigest !== hash(await readFile(this.path, 'utf8')))
|
|
116
|
+
fail('CONFIG_CONFLICT', 409, 'The configuration file changed.');
|
|
117
|
+
await rename(temporary, this.path);
|
|
118
|
+
return await this.load();
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
await rm(temporary, { force: true });
|
|
122
|
+
}
|
|
85
123
|
});
|
|
86
124
|
this.#reloadTail = operation.then(() => undefined, () => undefined);
|
|
87
125
|
return operation;
|
|
88
126
|
}
|
|
89
127
|
start(onChange) {
|
|
128
|
+
this.#onChange = onChange;
|
|
90
129
|
this.#watcher = watch(dirname(this.path), (_event, filename) => {
|
|
91
130
|
if (filename !== null && filename !== basename(this.path))
|
|
92
131
|
return;
|
|
93
132
|
this.#reloadTail = this.#reloadTail.then(async () => {
|
|
94
133
|
try {
|
|
95
|
-
|
|
96
|
-
const next = await this.load();
|
|
97
|
-
if (next !== previous)
|
|
98
|
-
onChange(next);
|
|
134
|
+
await this.load();
|
|
99
135
|
}
|
|
100
136
|
catch (error) {
|
|
101
137
|
const current = this.current;
|
|
102
|
-
const source = await readFile(this.path, 'utf8').catch(() =>
|
|
138
|
+
const source = await readFile(this.path, 'utf8').catch(() => null);
|
|
139
|
+
this.#source = source;
|
|
103
140
|
const next = {
|
|
104
141
|
...current,
|
|
105
|
-
fileDigest: hash(source),
|
|
142
|
+
fileDigest: hash(source ?? ''),
|
|
106
143
|
metadataRevision: current.metadataRevision + 1,
|
|
107
144
|
error: message(error)
|
|
108
145
|
};
|
|
109
146
|
this.#snapshot = next;
|
|
110
|
-
|
|
147
|
+
try {
|
|
148
|
+
onChange(next);
|
|
149
|
+
}
|
|
150
|
+
catch { /* Snapshot retains the visible application error. */ }
|
|
111
151
|
}
|
|
112
|
-
});
|
|
152
|
+
}).catch(() => undefined);
|
|
113
153
|
});
|
|
114
154
|
}
|
|
115
155
|
close() {
|
|
116
156
|
this.#watcher?.close();
|
|
117
157
|
this.#watcher = undefined;
|
|
118
158
|
}
|
|
119
|
-
publicConfig() {
|
|
120
|
-
return redact(this.current.config);
|
|
121
|
-
}
|
|
122
|
-
/** The file as a client should see it: every saved setting, with secrets redacted. */
|
|
123
|
-
publicFileConfig() {
|
|
124
|
-
return redact(this.current.fileConfig);
|
|
125
|
-
}
|
|
126
159
|
document() {
|
|
127
160
|
const snapshot = this.current;
|
|
128
|
-
|
|
129
|
-
// saved is visible and survives the next read/modify/write.
|
|
130
|
-
const content = snapshot.error === null ? `${JSON.stringify(this.publicFileConfig(), null, 2)}\n` : null;
|
|
161
|
+
const content = this.#source;
|
|
131
162
|
return {
|
|
132
163
|
content,
|
|
133
164
|
content_omitted: content === null,
|
|
134
165
|
// Documented as a decimal string, like the revisions, so a client never has to guess
|
|
135
166
|
// whether a large size arrived as a number.
|
|
136
|
-
file_size_bytes: String(
|
|
167
|
+
file_size_bytes: String(Buffer.byteLength(content ?? '')),
|
|
137
168
|
file_digest: snapshot.fileDigest,
|
|
138
169
|
active_config_digest: snapshot.digest,
|
|
139
|
-
file_valid: snapshot.error === null,
|
|
170
|
+
file_valid: snapshot.error === null && content !== null,
|
|
140
171
|
// `SchemaIssue` carries a path and a stable code beside the message.
|
|
141
|
-
validation_errors: snapshot.error === null
|
|
172
|
+
validation_errors: snapshot.error === null && content !== null
|
|
142
173
|
? []
|
|
143
|
-
: [{ path: '/', code: 'CONFIG_INVALID', message: snapshot.error }],
|
|
174
|
+
: [{ path: '/', code: 'CONFIG_INVALID', message: snapshot.error ?? 'The configuration file could not be read.' }],
|
|
175
|
+
validation_warnings: unsupportedSettings(snapshot.fileConfig),
|
|
144
176
|
applied_hot: snapshot.appliedHot,
|
|
145
177
|
pending_restart: snapshot.pendingRestart
|
|
146
178
|
};
|
|
@@ -157,55 +189,6 @@ export function resolveSecret(value) {
|
|
|
157
189
|
}
|
|
158
190
|
return secret;
|
|
159
191
|
}
|
|
160
|
-
function redact(value, key = '', headerValue = false) {
|
|
161
|
-
if (typeof value === 'string' && (key === 'apiKey' || headerValue))
|
|
162
|
-
return '[redacted]';
|
|
163
|
-
if (Array.isArray(value))
|
|
164
|
-
return value.map((entry) => redact(entry, '', headerValue));
|
|
165
|
-
if (typeof value !== 'object' || value === null)
|
|
166
|
-
return value;
|
|
167
|
-
return Object.fromEntries(Object.entries(value).map(([entryKey, entry]) => [
|
|
168
|
-
entryKey,
|
|
169
|
-
redact(entry, entryKey, headerValue || key === 'headers')
|
|
170
|
-
]));
|
|
171
|
-
}
|
|
172
|
-
function restoreSecrets(source, current) {
|
|
173
|
-
const candidate = parse(source);
|
|
174
|
-
if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate))
|
|
175
|
-
return source;
|
|
176
|
-
let changed = false;
|
|
177
|
-
const document = candidate;
|
|
178
|
-
const providers = record(document.providers);
|
|
179
|
-
for (const [providerId, provider] of Object.entries(providers ?? {})) {
|
|
180
|
-
const options = record(record(provider)?.options);
|
|
181
|
-
if (options?.apiKey !== '[redacted]')
|
|
182
|
-
continue;
|
|
183
|
-
const existing = current.providers[providerId]?.options.apiKey;
|
|
184
|
-
if (existing !== undefined) {
|
|
185
|
-
options.apiKey = existing;
|
|
186
|
-
changed = true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
const servers = record(document.mcpServers);
|
|
190
|
-
for (const [serverId, server] of Object.entries(servers ?? {})) {
|
|
191
|
-
const headers = record(record(server)?.headers);
|
|
192
|
-
if (headers === null)
|
|
193
|
-
continue;
|
|
194
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
195
|
-
if (value !== '[redacted]')
|
|
196
|
-
continue;
|
|
197
|
-
const existing = current.mcpServers?.[serverId]?.headers?.[name];
|
|
198
|
-
if (existing !== undefined) {
|
|
199
|
-
headers[name] = existing;
|
|
200
|
-
changed = true;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return changed ? `${JSON.stringify(document, null, 2)}\n` : source;
|
|
205
|
-
}
|
|
206
|
-
function record(value) {
|
|
207
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null;
|
|
208
|
-
}
|
|
209
192
|
function hash(value) {
|
|
210
193
|
return createHash('sha256').update(value).digest('hex');
|
|
211
194
|
}
|
|
@@ -230,20 +213,34 @@ function semanticIssues(config) {
|
|
|
230
213
|
const result = [];
|
|
231
214
|
const issue = (path, messageValue) => { result.push({ kind: 'semantic', path, message: messageValue }); };
|
|
232
215
|
const profiles = new Set(Object.keys(config.modelHarnessProfiles ?? {}));
|
|
233
|
-
for (const [profileId, profile] of Object.entries(config.modelHarnessProfiles ?? {})) {
|
|
234
|
-
if (!profile.effort.allowed.includes(profile.effort.default))
|
|
235
|
-
issue(`/modelHarnessProfiles/${profileId}/effort/default`, 'The default effort is not allowed by the profile.');
|
|
236
|
-
}
|
|
237
216
|
for (const [providerId, provider] of Object.entries(config.providers)) {
|
|
238
217
|
if (!/^[a-z0-9][a-z0-9._-]{0,127}$/u.test(providerId))
|
|
239
218
|
issue(`/providers/${providerId}`, 'Provider ID is invalid.');
|
|
240
219
|
if (provider.adapter === '@ai-sdk/openai-compatible' && provider.options.baseURL === undefined)
|
|
241
220
|
issue(`/providers/${providerId}/options/baseURL`, 'OpenAI-compatible providers require baseURL.');
|
|
242
221
|
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
222
|
+
if (modelReasoningEfforts(config, `${providerId}/${modelId}`).length === 0)
|
|
223
|
+
issue(`/providers/${providerId}/models/${modelId}/reasoningEfforts`, 'Declare reasoningEfforts on the Model or its Provider.');
|
|
243
224
|
if (modelId.length === 0 || modelId.includes('\n'))
|
|
244
225
|
issue(`/providers/${providerId}/models/${modelId}`, 'Model ID is invalid.');
|
|
245
226
|
if (model.harnessProfile !== undefined && !profiles.has(model.harnessProfile))
|
|
246
227
|
issue(`/providers/${providerId}/models/${modelId}/harnessProfile`, 'Model Harness Profile does not exist.');
|
|
228
|
+
if (provider.adapter === '@ai-sdk/openai-compatible' && model.harnessProfile !== undefined && config.modelHarnessProfiles?.[model.harnessProfile]?.semantics.reasoningStateRoundTrip === 'required') {
|
|
229
|
+
issue(`/providers/${providerId}/models/${modelId}/harnessProfile`, 'This adapter cannot guarantee required reasoning state round trips.');
|
|
230
|
+
}
|
|
231
|
+
const cache = model.promptCache ?? provider.promptCache;
|
|
232
|
+
if (cache !== undefined) {
|
|
233
|
+
const path = `/providers/${providerId}/models/${modelId}/promptCache`;
|
|
234
|
+
if ((cache.mode === 'anthropic' && provider.adapter !== '@ai-sdk/anthropic') || (cache.mode === 'openai-explicit' && provider.adapter !== '@ai-sdk/openai'))
|
|
235
|
+
issue(path, 'Cache mode must match the Provider adapter.');
|
|
236
|
+
if (cache.ttl !== undefined && !((cache.mode === 'anthropic' && cache.ttl !== '30m') || (cache.mode === 'openai-explicit' && cache.ttl === '30m')))
|
|
237
|
+
issue(path, 'Cache TTL must match an explicit cache mode.');
|
|
238
|
+
if (cache.retention !== undefined && (provider.adapter !== '@ai-sdk/openai' || cache.mode !== 'implicit'))
|
|
239
|
+
issue(path, 'Cache retention applies only to OpenAI implicit caching.');
|
|
240
|
+
}
|
|
241
|
+
const threshold = model.compactionThresholdTokens ?? provider.compactionThresholdTokens;
|
|
242
|
+
if (threshold !== undefined && threshold > model.contextWindowTokens)
|
|
243
|
+
issue(`/providers/${providerId}/models/${modelId}/compactionThresholdTokens`, 'The effective Compaction threshold cannot exceed the model context window.');
|
|
247
244
|
}
|
|
248
245
|
}
|
|
249
246
|
for (const [roleName, role] of Object.entries(config.roles)) {
|
|
@@ -253,12 +250,9 @@ function semanticIssues(config) {
|
|
|
253
250
|
issue(`/roles/${roleName}/model`, 'Role model is not configured.');
|
|
254
251
|
if (role.modelReasoningEffort === null)
|
|
255
252
|
issue(`/roles/${roleName}/modelReasoningEffort`, 'Role reasoning effort is required.');
|
|
256
|
-
else {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
: config.modelHarnessProfiles?.[model.harnessProfile]?.effort.allowed ?? [];
|
|
260
|
-
if (!allowed.includes(role.modelReasoningEffort))
|
|
261
|
-
issue(`/roles/${roleName}/modelReasoningEffort`, 'Role reasoning effort is not allowed by the Model Harness Profile.');
|
|
253
|
+
else if (model !== undefined && role.model !== null) {
|
|
254
|
+
if (!modelReasoningEfforts(config, role.model).includes(role.modelReasoningEffort))
|
|
255
|
+
issue(`/roles/${roleName}/modelReasoningEffort`, 'Select an effort from the Model or Provider reasoningEfforts.');
|
|
262
256
|
}
|
|
263
257
|
if (agentRole && (role.policyPreset === null || config.policyPresets[role.policyPreset] === undefined))
|
|
264
258
|
issue(`/roles/${roleName}/policyPreset`, 'Agent roles require an existing Policy preset.');
|
|
@@ -273,6 +267,8 @@ function semanticIssues(config) {
|
|
|
273
267
|
for (const reference of role.providerRouting.fallback) {
|
|
274
268
|
if (configuredModel(config, reference) === undefined)
|
|
275
269
|
issue(`/roles/${roleName}/providerRouting/fallback`, `Fallback model ${reference} is not configured.`);
|
|
270
|
+
else if (role.modelReasoningEffort !== null && !modelReasoningEfforts(config, reference).includes(role.modelReasoningEffort))
|
|
271
|
+
issue(`/roles/${roleName}/providerRouting/fallback`, `Fallback model ${reference} does not list the selected effort ${role.modelReasoningEffort}.`);
|
|
276
272
|
}
|
|
277
273
|
}
|
|
278
274
|
for (const [serverId, server] of Object.entries(config.mcpServers ?? {})) {
|
|
@@ -338,12 +334,16 @@ function effective(current, candidate) {
|
|
|
338
334
|
delete hot.observability;
|
|
339
335
|
delete hot.mcpServers;
|
|
340
336
|
delete hot.lspServers;
|
|
337
|
+
delete hot.localExecutor;
|
|
338
|
+
delete hot.executors;
|
|
341
339
|
return {
|
|
342
340
|
...hot,
|
|
343
341
|
listen: current.listen,
|
|
344
342
|
tls: current.tls,
|
|
345
343
|
database: current.database,
|
|
346
344
|
executor: current.executor,
|
|
345
|
+
...(current.localExecutor === undefined ? {} : { localExecutor: current.localExecutor }),
|
|
346
|
+
...(current.executors === undefined ? {} : { executors: current.executors }),
|
|
347
347
|
artifacts: current.artifacts,
|
|
348
348
|
...(current.pairPath === undefined ? {} : { pairPath: current.pairPath }),
|
|
349
349
|
...(current.persistence === undefined ? {} : { persistence: current.persistence }),
|
|
@@ -357,7 +357,7 @@ function effective(current, candidate) {
|
|
|
357
357
|
* `pending_restart`. Returns the changed setting names rather than a flag so a client can say
|
|
358
358
|
* which ones are waiting.
|
|
359
359
|
*/
|
|
360
|
-
const RESTART_SETTINGS = ['listen', 'tls', 'database', 'executor', 'artifacts', 'pairPath', 'persistence', 'observability', 'mcpServers', 'lspServers'];
|
|
360
|
+
const RESTART_SETTINGS = ['listen', 'tls', 'database', 'executor', 'localExecutor', 'executors', 'artifacts', 'pairPath', 'persistence', 'observability', 'mcpServers', 'lspServers'];
|
|
361
361
|
function restartPending(current, candidate) {
|
|
362
362
|
return RESTART_SETTINGS.filter((key) => JSON.stringify(current[key]) !== JSON.stringify(candidate[key]));
|
|
363
363
|
}
|
|
@@ -369,3 +369,22 @@ function appliedHot(current, candidate) {
|
|
|
369
369
|
.filter((key) => JSON.stringify(current[key]) !== JSON.stringify(candidate[key]))
|
|
370
370
|
.sort();
|
|
371
371
|
}
|
|
372
|
+
function unsupportedSettings(config) {
|
|
373
|
+
const warnings = [];
|
|
374
|
+
const warn = (path) => { warnings.push({ path, code: 'CONFIG_NOT_IMPLEMENTED', message: 'This setting is accepted for existing files but does not control runtime behavior.' }); };
|
|
375
|
+
if (config.capabilityInput !== undefined)
|
|
376
|
+
warn('/capabilityInput');
|
|
377
|
+
if (config.contextProjection !== undefined)
|
|
378
|
+
warn('/contextProjection');
|
|
379
|
+
for (const [id, profile] of Object.entries(config.modelHarnessProfiles ?? {})) {
|
|
380
|
+
const root = `/modelHarnessProfiles/${id}`;
|
|
381
|
+
warn(`${root}/context/segmentOrder`);
|
|
382
|
+
warn(`${root}/semantics/structuredOutput`);
|
|
383
|
+
warn(`${root}/semantics/toolDescriptionStyle`);
|
|
384
|
+
warn(`${root}/semantics/recoveryConstraints/preserveReasoningState`);
|
|
385
|
+
warn(`${root}/cache/segmentStability`);
|
|
386
|
+
if (profile.ptc !== undefined)
|
|
387
|
+
warn(`${root}/ptc`);
|
|
388
|
+
}
|
|
389
|
+
return warnings;
|
|
390
|
+
}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -28,10 +28,6 @@ export declare const ModelHarnessProfileSchema: import("@sinclair/typebox").TObj
|
|
|
28
28
|
schemaVersion: import("@sinclair/typebox").TString;
|
|
29
29
|
instruction: import("@sinclair/typebox").TString;
|
|
30
30
|
}>>;
|
|
31
|
-
effort: import("@sinclair/typebox").TObject<{
|
|
32
|
-
allowed: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
33
|
-
default: import("@sinclair/typebox").TString;
|
|
34
|
-
}>;
|
|
35
31
|
cache: import("@sinclair/typebox").TObject<{
|
|
36
32
|
enabledByDefault: import("@sinclair/typebox").TBoolean;
|
|
37
33
|
minimumEligiblePrefixTokens: import("@sinclair/typebox").TInteger;
|
|
@@ -57,8 +53,10 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
57
53
|
graceMs: import("@sinclair/typebox").TInteger;
|
|
58
54
|
}>>;
|
|
59
55
|
executor: import("@sinclair/typebox").TObject<{
|
|
56
|
+
sharedFilesystem: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
60
57
|
address: import("@sinclair/typebox").TString;
|
|
61
58
|
identityPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
59
|
+
credentialPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
62
60
|
tls: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
63
61
|
enabled: import("@sinclair/typebox").TBoolean;
|
|
64
62
|
caFile: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
@@ -66,6 +64,25 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
66
64
|
keyFile: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
67
65
|
}>>;
|
|
68
66
|
}>;
|
|
67
|
+
localExecutor: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
68
|
+
address: import("@sinclair/typebox").TString;
|
|
69
|
+
identityPath: import("@sinclair/typebox").TString;
|
|
70
|
+
credentialPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
71
|
+
}>>;
|
|
72
|
+
executors: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
|
|
73
|
+
name: import("@sinclair/typebox").TString;
|
|
74
|
+
address: import("@sinclair/typebox").TString;
|
|
75
|
+
identityPath: import("@sinclair/typebox").TString;
|
|
76
|
+
credentialPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
77
|
+
sharedFilesystem: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
78
|
+
defaultWorkspacePath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
79
|
+
tls: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
80
|
+
enabled: import("@sinclair/typebox").TBoolean;
|
|
81
|
+
caFile: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
82
|
+
certFile: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
83
|
+
keyFile: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
84
|
+
}>>;
|
|
85
|
+
}>>>;
|
|
69
86
|
artifacts: import("@sinclair/typebox").TObject<{
|
|
70
87
|
directory: import("@sinclair/typebox").TString;
|
|
71
88
|
}>;
|
|
@@ -86,6 +103,8 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
86
103
|
maxFileBytes: import("@sinclair/typebox").TInteger;
|
|
87
104
|
maxFiles: import("@sinclair/typebox").TInteger;
|
|
88
105
|
retentionDays: import("@sinclair/typebox").TInteger;
|
|
106
|
+
maxQueuedRecords: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
107
|
+
maxQueuedBytes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
89
108
|
}>>;
|
|
90
109
|
traces: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
91
110
|
capacity: import("@sinclair/typebox").TInteger;
|
|
@@ -107,8 +126,13 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
107
126
|
initializationOptions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnknown>;
|
|
108
127
|
maxFrameBytes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
109
128
|
maxBufferBytes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
129
|
+
requestTimeoutMs: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
130
|
+
maxSessions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
131
|
+
idleTimeoutMs: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
110
132
|
}>>>;
|
|
111
133
|
providers: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
|
|
134
|
+
reasoningEfforts: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
135
|
+
compactionThresholdTokens: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
112
136
|
adapter: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"@ai-sdk/openai">, import("@sinclair/typebox").TLiteral<"@ai-sdk/anthropic">, import("@sinclair/typebox").TLiteral<"@ai-sdk/openai-compatible">]>;
|
|
113
137
|
options: import("@sinclair/typebox").TObject<{
|
|
114
138
|
apiKey: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
|
|
@@ -118,12 +142,30 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
118
142
|
baseURL: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
119
143
|
headers: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TString>>;
|
|
120
144
|
}>;
|
|
145
|
+
promptCache: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
146
|
+
mode: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"off">, import("@sinclair/typebox").TLiteral<"implicit">, import("@sinclair/typebox").TLiteral<"anthropic">, import("@sinclair/typebox").TLiteral<"openai-explicit">]>;
|
|
147
|
+
minimumEligiblePrefixTokens: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
148
|
+
maximumBreakpoints: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
149
|
+
positions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"system">, import("@sinclair/typebox").TLiteral<"message">, import("@sinclair/typebox").TLiteral<"tool">]>>>;
|
|
150
|
+
ttl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"5m">, import("@sinclair/typebox").TLiteral<"1h">, import("@sinclair/typebox").TLiteral<"30m">]>>;
|
|
151
|
+
retention: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"in_memory">, import("@sinclair/typebox").TLiteral<"24h">]>>;
|
|
152
|
+
}>>;
|
|
121
153
|
models: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
|
|
154
|
+
reasoningEfforts: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
122
155
|
contextWindowTokens: import("@sinclair/typebox").TInteger;
|
|
156
|
+
compactionThresholdTokens: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
123
157
|
wireModel: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
124
158
|
harnessProfile: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
125
159
|
usageReliable: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
126
160
|
cacheSupported: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
161
|
+
promptCache: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
162
|
+
mode: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"off">, import("@sinclair/typebox").TLiteral<"implicit">, import("@sinclair/typebox").TLiteral<"anthropic">, import("@sinclair/typebox").TLiteral<"openai-explicit">]>;
|
|
163
|
+
minimumEligiblePrefixTokens: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
164
|
+
maximumBreakpoints: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
165
|
+
positions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"system">, import("@sinclair/typebox").TLiteral<"message">, import("@sinclair/typebox").TLiteral<"tool">]>>>;
|
|
166
|
+
ttl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"5m">, import("@sinclair/typebox").TLiteral<"1h">, import("@sinclair/typebox").TLiteral<"30m">]>>;
|
|
167
|
+
retention: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"in_memory">, import("@sinclair/typebox").TLiteral<"24h">]>>;
|
|
168
|
+
}>>;
|
|
127
169
|
programmaticToolCallingSupported: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
128
170
|
}>>;
|
|
129
171
|
}>>;
|
|
@@ -156,10 +198,6 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
156
198
|
schemaVersion: import("@sinclair/typebox").TString;
|
|
157
199
|
instruction: import("@sinclair/typebox").TString;
|
|
158
200
|
}>>;
|
|
159
|
-
effort: import("@sinclair/typebox").TObject<{
|
|
160
|
-
allowed: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
161
|
-
default: import("@sinclair/typebox").TString;
|
|
162
|
-
}>;
|
|
163
201
|
cache: import("@sinclair/typebox").TObject<{
|
|
164
202
|
enabledByDefault: import("@sinclair/typebox").TBoolean;
|
|
165
203
|
minimumEligiblePrefixTokens: import("@sinclair/typebox").TInteger;
|
|
@@ -233,6 +271,14 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
233
271
|
outputReserveTokens: import("@sinclair/typebox").TInteger;
|
|
234
272
|
safetyMarginTokens: import("@sinclair/typebox").TInteger;
|
|
235
273
|
}>>;
|
|
274
|
+
localPtc: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
275
|
+
timeoutMs: import("@sinclair/typebox").TInteger;
|
|
276
|
+
maxToolCalls: import("@sinclair/typebox").TInteger;
|
|
277
|
+
nativeCapabilities: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
278
|
+
}>>;
|
|
279
|
+
slashCommands: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
280
|
+
llmVisible: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TLiteral<"compact">>;
|
|
281
|
+
}>>;
|
|
236
282
|
capabilityInput: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
237
283
|
catalogMaxTokens: import("@sinclair/typebox").TInteger;
|
|
238
284
|
searchResultMaxTokens: import("@sinclair/typebox").TInteger;
|
|
@@ -254,8 +300,8 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
254
300
|
stageVersion: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
255
301
|
}>>;
|
|
256
302
|
compaction: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
257
|
-
preservedSegmentTokens: import("@sinclair/typebox").TInteger
|
|
258
|
-
preservedSegmentBytes: import("@sinclair/typebox").TInteger
|
|
303
|
+
preservedSegmentTokens: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
304
|
+
preservedSegmentBytes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
259
305
|
summaryMaxTokens: import("@sinclair/typebox").TInteger;
|
|
260
306
|
}>>;
|
|
261
307
|
autoReview: import("@sinclair/typebox").TObject<{
|
package/dist/config/schema.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { Type } from '@sinclair/typebox';
|
|
2
2
|
const NullableString = Type.Union([Type.String({ minLength: 1 }), Type.Null()]);
|
|
3
3
|
const PositiveInteger = Type.Integer({ minimum: 1 });
|
|
4
|
+
const ReasoningEfforts = Type.Array(Type.String({ minLength: 1 }), { minItems: 1, uniqueItems: true });
|
|
4
5
|
const Secret = Type.Union([
|
|
5
6
|
Type.String({ minLength: 1 }),
|
|
6
7
|
Type.Object({ env: Type.String({ minLength: 1 }), optional: Type.Optional(Type.Boolean()) }, { additionalProperties: false })
|
|
7
8
|
]);
|
|
9
|
+
const PromptCache = Type.Object({
|
|
10
|
+
mode: Type.Union([Type.Literal('off'), Type.Literal('implicit'), Type.Literal('anthropic'), Type.Literal('openai-explicit')]),
|
|
11
|
+
minimumEligiblePrefixTokens: Type.Optional(PositiveInteger),
|
|
12
|
+
maximumBreakpoints: Type.Optional(Type.Integer({ minimum: 0, maximum: 4 })),
|
|
13
|
+
positions: Type.Optional(Type.Array(Type.Union([Type.Literal('system'), Type.Literal('message'), Type.Literal('tool')]), { uniqueItems: true })),
|
|
14
|
+
ttl: Type.Optional(Type.Union([Type.Literal('5m'), Type.Literal('1h'), Type.Literal('30m')])),
|
|
15
|
+
retention: Type.Optional(Type.Union([Type.Literal('in_memory'), Type.Literal('24h')]))
|
|
16
|
+
}, { additionalProperties: false });
|
|
8
17
|
const Routing = Type.Object({
|
|
9
18
|
retryMaxAttempts: Type.Integer({ minimum: 1, maximum: 10 }),
|
|
10
19
|
fallback: Type.Array(Type.String({ minLength: 3 }))
|
|
@@ -41,16 +50,13 @@ export const ModelHarnessProfileSchema = Type.Object({
|
|
|
41
50
|
preserveReasoningState: Type.Boolean()
|
|
42
51
|
}, { additionalProperties: false })
|
|
43
52
|
}, { additionalProperties: false }),
|
|
53
|
+
// Legacy hosted-PTC profile fields are accepted but no longer drive execution.
|
|
44
54
|
ptc: Type.Optional(Type.Object({
|
|
45
55
|
enabledByDefault: Type.Boolean(),
|
|
46
56
|
maxToolCalls: PositiveInteger,
|
|
47
57
|
schemaVersion: Type.String({ minLength: 1 }),
|
|
48
58
|
instruction: Type.String({ minLength: 1 })
|
|
49
59
|
}, { additionalProperties: false })),
|
|
50
|
-
effort: Type.Object({
|
|
51
|
-
allowed: Type.Array(Type.String({ minLength: 1, maxLength: 64, pattern: '^[A-Za-z0-9._:-]+$' }), { minItems: 1, uniqueItems: true }),
|
|
52
|
-
default: Type.String({ minLength: 1, maxLength: 64, pattern: '^[A-Za-z0-9._:-]+$' })
|
|
53
|
-
}, { additionalProperties: false }),
|
|
54
60
|
cache: Type.Object({
|
|
55
61
|
enabledByDefault: Type.Boolean(),
|
|
56
62
|
minimumEligiblePrefixTokens: PositiveInteger,
|
|
@@ -69,8 +75,10 @@ export const ConfigSchema = Type.Object({
|
|
|
69
75
|
graceMs: Type.Integer({ minimum: 0, maximum: 600000, default: 30000 })
|
|
70
76
|
}, { additionalProperties: false })),
|
|
71
77
|
executor: Type.Object({
|
|
78
|
+
sharedFilesystem: Type.Optional(Type.Boolean({ default: false })),
|
|
72
79
|
address: Type.String({ minLength: 1 }),
|
|
73
80
|
identityPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
81
|
+
credentialPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
74
82
|
tls: Type.Optional(Type.Object({
|
|
75
83
|
enabled: Type.Boolean(),
|
|
76
84
|
caFile: Type.Optional(Type.String({ minLength: 1 })),
|
|
@@ -78,6 +86,23 @@ export const ConfigSchema = Type.Object({
|
|
|
78
86
|
keyFile: Type.Optional(Type.String({ minLength: 1 }))
|
|
79
87
|
}, { additionalProperties: false }))
|
|
80
88
|
}, { additionalProperties: false }),
|
|
89
|
+
localExecutor: Type.Optional(Type.Object({
|
|
90
|
+
address: Type.String({ minLength: 1 }),
|
|
91
|
+
identityPath: Type.String({ minLength: 1 }),
|
|
92
|
+
credentialPath: Type.Optional(Type.String({ minLength: 1 }))
|
|
93
|
+
}, { additionalProperties: false })),
|
|
94
|
+
executors: Type.Optional(Type.Record(Type.String({ pattern: '^(?!local$|remote$)[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$' }), Type.Object({
|
|
95
|
+
name: Type.String({ minLength: 1, maxLength: 128 }),
|
|
96
|
+
address: Type.String({ minLength: 1 }),
|
|
97
|
+
identityPath: Type.String({ minLength: 1 }),
|
|
98
|
+
credentialPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
99
|
+
sharedFilesystem: Type.Optional(Type.Boolean()),
|
|
100
|
+
defaultWorkspacePath: Type.Optional(Type.String({ minLength: 1 })),
|
|
101
|
+
tls: Type.Optional(Type.Object({
|
|
102
|
+
enabled: Type.Boolean(), caFile: Type.Optional(Type.String({ minLength: 1 })),
|
|
103
|
+
certFile: Type.Optional(Type.String({ minLength: 1 })), keyFile: Type.Optional(Type.String({ minLength: 1 }))
|
|
104
|
+
}, { additionalProperties: false }))
|
|
105
|
+
}, { additionalProperties: false }))),
|
|
81
106
|
artifacts: Type.Object({ directory: Type.String({ minLength: 1 }) }, { additionalProperties: false }),
|
|
82
107
|
persistence: Type.Optional(Type.Object({
|
|
83
108
|
eventLog: Type.Optional(Type.Object({
|
|
@@ -91,7 +116,9 @@ export const ConfigSchema = Type.Object({
|
|
|
91
116
|
path: Type.String({ minLength: 1 }),
|
|
92
117
|
maxFileBytes: PositiveInteger,
|
|
93
118
|
maxFiles: PositiveInteger,
|
|
94
|
-
retentionDays: PositiveInteger
|
|
119
|
+
retentionDays: PositiveInteger,
|
|
120
|
+
maxQueuedRecords: Type.Optional(PositiveInteger),
|
|
121
|
+
maxQueuedBytes: Type.Optional(PositiveInteger)
|
|
95
122
|
}, { additionalProperties: false })),
|
|
96
123
|
traces: Type.Optional(Type.Object({ capacity: PositiveInteger }, { additionalProperties: false }))
|
|
97
124
|
}, { additionalProperties: false })),
|
|
@@ -107,9 +134,14 @@ export const ConfigSchema = Type.Object({
|
|
|
107
134
|
project: Type.Optional(Type.String()),
|
|
108
135
|
initializationOptions: Type.Optional(Type.Unknown()),
|
|
109
136
|
maxFrameBytes: Type.Optional(PositiveInteger),
|
|
110
|
-
maxBufferBytes: Type.Optional(PositiveInteger)
|
|
137
|
+
maxBufferBytes: Type.Optional(PositiveInteger),
|
|
138
|
+
requestTimeoutMs: Type.Optional(PositiveInteger),
|
|
139
|
+
maxSessions: Type.Optional(PositiveInteger),
|
|
140
|
+
idleTimeoutMs: Type.Optional(PositiveInteger)
|
|
111
141
|
}, { additionalProperties: false }))),
|
|
112
142
|
providers: Type.Record(Type.String({ minLength: 1 }), Type.Object({
|
|
143
|
+
reasoningEfforts: Type.Optional(ReasoningEfforts),
|
|
144
|
+
compactionThresholdTokens: Type.Optional(PositiveInteger),
|
|
113
145
|
adapter: Type.Union([
|
|
114
146
|
Type.Literal('@ai-sdk/openai'),
|
|
115
147
|
Type.Literal('@ai-sdk/anthropic'),
|
|
@@ -120,12 +152,17 @@ export const ConfigSchema = Type.Object({
|
|
|
120
152
|
baseURL: Type.Optional(Type.String({ minLength: 1 })),
|
|
121
153
|
headers: Type.Optional(Type.Record(Type.String(), Type.String()))
|
|
122
154
|
}, { additionalProperties: false }),
|
|
155
|
+
promptCache: Type.Optional(PromptCache),
|
|
123
156
|
models: Type.Record(Type.String({ minLength: 1 }), Type.Object({
|
|
157
|
+
reasoningEfforts: Type.Optional(ReasoningEfforts),
|
|
124
158
|
contextWindowTokens: PositiveInteger,
|
|
159
|
+
compactionThresholdTokens: Type.Optional(PositiveInteger),
|
|
125
160
|
wireModel: Type.Optional(Type.String({ minLength: 1 })),
|
|
126
161
|
harnessProfile: Type.Optional(Type.String({ minLength: 1 })),
|
|
127
162
|
usageReliable: Type.Optional(Type.Boolean()),
|
|
128
163
|
cacheSupported: Type.Optional(Type.Boolean()),
|
|
164
|
+
promptCache: Type.Optional(PromptCache),
|
|
165
|
+
// Legacy Provider flag; local PTC works without it.
|
|
129
166
|
programmaticToolCallingSupported: Type.Optional(Type.Boolean())
|
|
130
167
|
}, { additionalProperties: false }))
|
|
131
168
|
}, { additionalProperties: false })),
|
|
@@ -141,6 +178,8 @@ export const ConfigSchema = Type.Object({
|
|
|
141
178
|
concurrency: Type.Object({ instanceMax: PositiveInteger, perStepMax: PositiveInteger, perAdapterMax: PositiveInteger }, { additionalProperties: false }),
|
|
142
179
|
worker: Type.Object({ timeoutMs: PositiveInteger, outputLimitBytes: PositiveInteger }, { additionalProperties: false }),
|
|
143
180
|
contextBudget: Type.Optional(Type.Object({ outputReserveTokens: PositiveInteger, safetyMarginTokens: PositiveInteger }, { additionalProperties: false })),
|
|
181
|
+
localPtc: Type.Optional(Type.Object({ timeoutMs: PositiveInteger, maxToolCalls: PositiveInteger, nativeCapabilities: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { uniqueItems: true })) }, { additionalProperties: false })),
|
|
182
|
+
slashCommands: Type.Optional(Type.Object({ llmVisible: Type.Array(Type.Literal('compact'), { uniqueItems: true }) }, { additionalProperties: false })),
|
|
144
183
|
capabilityInput: Type.Optional(Type.Object({ catalogMaxTokens: PositiveInteger, searchResultMaxTokens: PositiveInteger }, { additionalProperties: false })),
|
|
145
184
|
contextProjection: Type.Optional(Type.Object({
|
|
146
185
|
toolResultMaxTokens: Type.Optional(PositiveInteger),
|
|
@@ -150,8 +189,9 @@ export const ConfigSchema = Type.Object({
|
|
|
150
189
|
stageVersion: Type.Optional(Type.String({ minLength: 1 }))
|
|
151
190
|
}, { additionalProperties: false })),
|
|
152
191
|
compaction: Type.Optional(Type.Object({
|
|
153
|
-
|
|
154
|
-
|
|
192
|
+
// Accepted for existing configuration files; the retained tail is now fixed at 5000 tokens.
|
|
193
|
+
preservedSegmentTokens: Type.Optional(PositiveInteger),
|
|
194
|
+
preservedSegmentBytes: Type.Optional(PositiveInteger),
|
|
155
195
|
summaryMaxTokens: PositiveInteger
|
|
156
196
|
}, { additionalProperties: false })),
|
|
157
197
|
autoReview: Type.Object({ timeoutMs: PositiveInteger, goalMaxBytes: PositiveInteger }, { additionalProperties: false }),
|