@markusylisiurunen/tau 0.2.63 → 0.2.65
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/core/async/cli.js +68 -104
- package/dist/core/async/cli.js.map +1 -1
- package/dist/core/async/http_protocol.js +119 -8
- package/dist/core/async/http_protocol.js.map +1 -1
- package/dist/core/async/http_server.js +120 -240
- package/dist/core/async/http_server.js.map +1 -1
- package/dist/core/async/index.js +1 -1
- package/dist/core/async/index.js.map +1 -1
- package/dist/core/async/server_config.js +161 -356
- package/dist/core/async/server_config.js.map +1 -1
- package/dist/core/async/session_manager.js +5 -17
- package/dist/core/async/session_manager.js.map +1 -1
- package/dist/core/async/telegram.js +268 -171
- package/dist/core/async/telegram.js.map +1 -1
- package/dist/core/auth/providers/openai_codex.js +4 -9
- package/dist/core/auth/providers/openai_codex.js.map +1 -1
- package/dist/core/config/content_loader.js +57 -205
- package/dist/core/config/content_loader.js.map +1 -1
- package/dist/core/config/markdown_frontmatter.js +34 -0
- package/dist/core/config/markdown_frontmatter.js.map +1 -0
- package/dist/core/config/schema.js +266 -332
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/config/skill_parser.js +8 -32
- package/dist/core/config/skill_parser.js.map +1 -1
- package/dist/core/config/skills_loader.js +32 -18
- package/dist/core/config/skills_loader.js.map +1 -1
- package/dist/core/events/index.js +1 -0
- package/dist/core/events/index.js.map +1 -1
- package/dist/core/events/parser.js +115 -0
- package/dist/core/events/parser.js.map +1 -0
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/modes/rpc_protocol.js +249 -189
- package/dist/core/modes/rpc_protocol.js.map +1 -1
- package/dist/core/session/session_engine.js +1 -3
- package/dist/core/session/session_engine.js.map +1 -1
- package/dist/core/subagents/control_plane.js +13 -2
- package/dist/core/subagents/control_plane.js.map +1 -1
- package/dist/core/subagents/subagent_engine.js +3 -3
- package/dist/core/subagents/subagent_engine.js.map +1 -1
- package/dist/core/tools/emit_output.js +3 -2
- package/dist/core/tools/emit_output.js.map +1 -1
- package/dist/core/tools/registry.js +6 -0
- package/dist/core/tools/registry.js.map +1 -1
- package/dist/core/tools/send_input_to_agent.js +12 -16
- package/dist/core/tools/send_input_to_agent.js.map +1 -1
- package/dist/core/tools/spawn_agent.js +25 -30
- package/dist/core/tools/spawn_agent.js.map +1 -1
- package/dist/core/tools/terminate_agent.js +10 -14
- package/dist/core/tools/terminate_agent.js.map +1 -1
- package/dist/core/tools/wait_for_agent.js +10 -14
- package/dist/core/tools/wait_for_agent.js.map +1 -1
- package/dist/core/utils/mistral_transcription.js +13 -21
- package/dist/core/utils/mistral_transcription.js.map +1 -1
- package/dist/core/utils/parallel_api.js +15 -19
- package/dist/core/utils/parallel_api.js.map +1 -1
- package/dist/core/utils/zod.js +7 -0
- package/dist/core/utils/zod.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/tui/chat_controller/session_maintenance_service.js +98 -171
- package/dist/tui/chat_controller/session_maintenance_service.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,78 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { getModels, getProviders } from "@mariozechner/pi-ai";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
import { formatPersonaReference, parsePersonaReference } from "../persona_reference.js";
|
|
4
5
|
import { parseSubagentLaunchModelList } from "../subagents/launch_model.js";
|
|
5
6
|
import { REASONING_LEVELS, RiskLevelSchema } from "../types.js";
|
|
6
7
|
import { normalizeModelNoticeKey, parseModelNoticeKey } from "../utils/model_notices.js";
|
|
7
|
-
import { isRecord } from "../utils/type_guards.js";
|
|
8
8
|
import { parseBashCommands } from "./bash_commands.js";
|
|
9
9
|
import { createDefaultConfigDeps } from "./deps.js";
|
|
10
10
|
import { resolveConfigLevels } from "./paths.js";
|
|
11
11
|
import { getVirtualConfigDefaults } from "./virtual_defaults.js";
|
|
12
|
+
const NonEmptyStringSchema = z.string().trim().min(1);
|
|
13
|
+
const BooleanSchema = z.boolean();
|
|
14
|
+
const AgentContextFilesSchema = z.array(NonEmptyStringSchema);
|
|
15
|
+
const ApiKeyProviderSchema = z.string();
|
|
16
|
+
const ApiKeysSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
anthropic: z.unknown().optional(),
|
|
19
|
+
google: z.unknown().optional(),
|
|
20
|
+
openai: z.unknown().optional(),
|
|
21
|
+
parallel: z.unknown().optional(),
|
|
22
|
+
mistral: z.unknown().optional(),
|
|
23
|
+
})
|
|
24
|
+
.passthrough();
|
|
25
|
+
const SandboxSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
image: z.unknown().optional(),
|
|
28
|
+
mountPath: z.unknown().optional(),
|
|
29
|
+
pruneAfterHours: z.unknown().optional(),
|
|
30
|
+
extraDockerArgs: z.unknown().optional(),
|
|
31
|
+
environmentInfo: z.unknown().optional(),
|
|
32
|
+
})
|
|
33
|
+
.passthrough();
|
|
34
|
+
const SandboxFieldsSchema = z.object({
|
|
35
|
+
image: NonEmptyStringSchema,
|
|
36
|
+
mountPath: NonEmptyStringSchema,
|
|
37
|
+
pruneAfterHours: z.number().finite().gt(0),
|
|
38
|
+
extraDockerArgs: z.array(z.string().refine((entry) => entry.trim().length > 0)),
|
|
39
|
+
environmentInfo: z.string(),
|
|
40
|
+
});
|
|
41
|
+
const SubagentsConfigSchema = z
|
|
42
|
+
.object({
|
|
43
|
+
defaultLaunchModels: z.array(z.string()).optional(),
|
|
44
|
+
})
|
|
45
|
+
.passthrough();
|
|
46
|
+
const StringRecordSchema = z.object({}).catchall(z.unknown());
|
|
47
|
+
const PositiveIntegerSchema = z.number().int().finite().gt(0);
|
|
48
|
+
const AsyncClientTargetSchema = z.object({
|
|
49
|
+
url: NonEmptyStringSchema,
|
|
50
|
+
token: NonEmptyStringSchema,
|
|
51
|
+
timeoutMs: PositiveIntegerSchema.optional(),
|
|
52
|
+
});
|
|
53
|
+
function parseOptionalFields(data, sourceLabel, specs) {
|
|
54
|
+
const values = {};
|
|
55
|
+
const errors = [];
|
|
56
|
+
for (const [key, schema, errorMessage] of specs) {
|
|
57
|
+
const value = data[key];
|
|
58
|
+
if (value === undefined) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const parsed = schema.safeParse(value);
|
|
62
|
+
if (!parsed.success) {
|
|
63
|
+
errors.push(`${sourceLabel}: ${errorMessage}`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
values[key] = parsed.data;
|
|
67
|
+
}
|
|
68
|
+
return { values, errors };
|
|
69
|
+
}
|
|
70
|
+
function assignParsedConfigValue(config, errors, key, value, parseErrors) {
|
|
71
|
+
if (value !== undefined) {
|
|
72
|
+
config[key] = value;
|
|
73
|
+
}
|
|
74
|
+
errors.push(...parseErrors);
|
|
75
|
+
}
|
|
12
76
|
function parseConfigJson(content, sourceLabel) {
|
|
13
77
|
try {
|
|
14
78
|
return { data: JSON.parse(content), errors: [] };
|
|
@@ -21,250 +85,179 @@ function parseConfigJson(content, sourceLabel) {
|
|
|
21
85
|
};
|
|
22
86
|
}
|
|
23
87
|
}
|
|
24
|
-
function
|
|
25
|
-
if (
|
|
26
|
-
return {
|
|
88
|
+
function parseApiKeysConfig(raw, sourceLabel) {
|
|
89
|
+
if (raw === undefined) {
|
|
90
|
+
return { errors: [] };
|
|
27
91
|
}
|
|
28
|
-
const
|
|
29
|
-
|
|
92
|
+
const parsed = ApiKeysSchema.safeParse(raw);
|
|
93
|
+
if (!parsed.success) {
|
|
94
|
+
return { errors: [`${sourceLabel}: 'apiKeys' must be an object.`] };
|
|
95
|
+
}
|
|
96
|
+
const apiKeys = {};
|
|
30
97
|
const errors = [];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
98
|
+
for (const provider of ["anthropic", "google", "openai", "parallel", "mistral"]) {
|
|
99
|
+
const value = parsed.data[provider];
|
|
100
|
+
if (value === undefined) {
|
|
101
|
+
continue;
|
|
34
102
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
for (const provider of providers) {
|
|
40
|
-
const value = keys[provider];
|
|
41
|
-
if (value === undefined)
|
|
42
|
-
continue;
|
|
43
|
-
if (typeof value !== "string") {
|
|
44
|
-
errors.push(`${sourceLabel}: apiKeys.${provider} must be a string.`);
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
apiKeys[provider] = value;
|
|
48
|
-
}
|
|
49
|
-
if (Object.keys(apiKeys).length > 0) {
|
|
50
|
-
config.apiKeys = apiKeys;
|
|
51
|
-
}
|
|
103
|
+
const parsedValue = ApiKeyProviderSchema.safeParse(value);
|
|
104
|
+
if (!parsedValue.success) {
|
|
105
|
+
errors.push(`${sourceLabel}: apiKeys.${provider} must be a string.`);
|
|
106
|
+
continue;
|
|
52
107
|
}
|
|
108
|
+
apiKeys[provider] = parsedValue.data;
|
|
53
109
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
config.sandbox = sandboxResult.config;
|
|
57
|
-
}
|
|
58
|
-
errors.push(...sandboxResult.errors);
|
|
59
|
-
if (data.defaultPersona !== undefined) {
|
|
60
|
-
if (typeof data.defaultPersona === "string") {
|
|
61
|
-
const parsedDefaultPersona = parsePersonaReference(data.defaultPersona);
|
|
62
|
-
if (parsedDefaultPersona.error === "empty-persona") {
|
|
63
|
-
errors.push(`${sourceLabel}: 'defaultPersona' must be a non-empty string.`);
|
|
64
|
-
}
|
|
65
|
-
else if (parsedDefaultPersona.error === "missing-reasoning") {
|
|
66
|
-
errors.push(`${sourceLabel}: 'defaultPersona' is missing a reasoning level after ':'.`);
|
|
67
|
-
}
|
|
68
|
-
else if (parsedDefaultPersona.error === "invalid-reasoning") {
|
|
69
|
-
errors.push(`${sourceLabel}: 'defaultPersona' has invalid reasoning level '${parsedDefaultPersona.rawReasoning}'. allowed levels: ${REASONING_LEVELS.join(", ")}.`);
|
|
70
|
-
}
|
|
71
|
-
else if (parsedDefaultPersona.personaId) {
|
|
72
|
-
config.defaultPersona = formatPersonaReference({
|
|
73
|
-
personaId: parsedDefaultPersona.personaId,
|
|
74
|
-
reasoning: parsedDefaultPersona.reasoning,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
errors.push(`${sourceLabel}: 'defaultPersona' must be a string.`);
|
|
80
|
-
}
|
|
110
|
+
if (Object.keys(apiKeys).length === 0) {
|
|
111
|
+
return { errors };
|
|
81
112
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
errors.push(`${sourceLabel}: 'defaultRisk' must be a valid risk level.`);
|
|
89
|
-
}
|
|
113
|
+
return { config: apiKeys, errors };
|
|
114
|
+
}
|
|
115
|
+
function parseDefaultPersona(raw, sourceLabel) {
|
|
116
|
+
if (raw === undefined) {
|
|
117
|
+
return { errors: [] };
|
|
90
118
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
errors.push(`${sourceLabel}: 'disableBuiltinPersonas' must be a boolean.`);
|
|
97
|
-
}
|
|
119
|
+
const parsedPersona = z.string().safeParse(raw);
|
|
120
|
+
if (!parsedPersona.success) {
|
|
121
|
+
return { errors: [`${sourceLabel}: 'defaultPersona' must be a string.`] };
|
|
98
122
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
123
|
+
const ref = parsePersonaReference(parsedPersona.data);
|
|
124
|
+
if (ref.personaId && !ref.error) {
|
|
125
|
+
return {
|
|
126
|
+
defaultPersona: formatPersonaReference({
|
|
127
|
+
personaId: ref.personaId,
|
|
128
|
+
reasoning: ref.reasoning,
|
|
129
|
+
}),
|
|
130
|
+
errors: [],
|
|
131
|
+
};
|
|
106
132
|
}
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
const defaultTheme = data.defaultTheme.trim();
|
|
110
|
-
if (!defaultTheme) {
|
|
111
|
-
errors.push(`${sourceLabel}: 'defaultTheme' must be a non-empty string.`);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
config.defaultTheme = defaultTheme;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
errors.push(`${sourceLabel}: 'defaultTheme' must be a string.`);
|
|
119
|
-
}
|
|
133
|
+
if (ref.error === "empty-persona") {
|
|
134
|
+
return { errors: [`${sourceLabel}: 'defaultPersona' must be a non-empty string.`] };
|
|
120
135
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
136
|
+
if (ref.error === "missing-reasoning") {
|
|
137
|
+
return {
|
|
138
|
+
errors: [`${sourceLabel}: 'defaultPersona' is missing a reasoning level after ':'.`],
|
|
139
|
+
};
|
|
124
140
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
if (ref.error === "invalid-reasoning") {
|
|
142
|
+
return {
|
|
143
|
+
errors: [
|
|
144
|
+
`${sourceLabel}: 'defaultPersona' has invalid reasoning level '${ref.rawReasoning}'. allowed levels: ${REASONING_LEVELS.join(", ")}.`,
|
|
145
|
+
],
|
|
146
|
+
};
|
|
129
147
|
}
|
|
130
|
-
errors
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
return { errors: [] };
|
|
149
|
+
}
|
|
150
|
+
function validateConfigData(raw, sourceLabel) {
|
|
151
|
+
if (typeof raw !== "object" || raw === null) {
|
|
152
|
+
return { config: {}, errors: [`${sourceLabel}: config must be an object.`] };
|
|
134
153
|
}
|
|
135
|
-
|
|
154
|
+
const data = raw;
|
|
155
|
+
const config = {};
|
|
156
|
+
const errors = [];
|
|
157
|
+
const apiKeysResult = parseApiKeysConfig(data.apiKeys, sourceLabel);
|
|
158
|
+
assignParsedConfigValue(config, errors, "apiKeys", apiKeysResult.config, apiKeysResult.errors);
|
|
159
|
+
const sandboxResult = parseSandboxConfig(data.sandbox, sourceLabel);
|
|
160
|
+
assignParsedConfigValue(config, errors, "sandbox", sandboxResult.config, sandboxResult.errors);
|
|
161
|
+
const defaultPersonaResult = parseDefaultPersona(data.defaultPersona, sourceLabel);
|
|
162
|
+
assignParsedConfigValue(config, errors, "defaultPersona", defaultPersonaResult.defaultPersona, defaultPersonaResult.errors);
|
|
163
|
+
const scalarResult = parseOptionalFields(data, sourceLabel, [
|
|
164
|
+
["defaultRisk", RiskLevelSchema, "'defaultRisk' must be a valid risk level."],
|
|
165
|
+
["disableBuiltinPersonas", BooleanSchema, "'disableBuiltinPersonas' must be a boolean."],
|
|
166
|
+
["disableBuiltinThemes", BooleanSchema, "'disableBuiltinThemes' must be a boolean."],
|
|
167
|
+
["defaultTheme", NonEmptyStringSchema, "'defaultTheme' must be a non-empty string."],
|
|
168
|
+
]);
|
|
169
|
+
Object.assign(config, scalarResult.values);
|
|
170
|
+
errors.push(...scalarResult.errors);
|
|
171
|
+
const bashResult = parseBashCommands(data.bashCommands, sourceLabel);
|
|
172
|
+
assignParsedConfigValue(config, errors, "bashCommands", bashResult.commands.length > 0 ? bashResult.commands : undefined, bashResult.errors);
|
|
173
|
+
const agentResult = parseAgentContextFiles(data.agentContextFiles, sourceLabel);
|
|
174
|
+
assignParsedConfigValue(config, errors, "agentContextFiles", agentResult.paths.length > 0 ? agentResult.paths : undefined, agentResult.errors);
|
|
175
|
+
const subagentsResult = parseSubagentsConfig(data.subagents, sourceLabel);
|
|
176
|
+
assignParsedConfigValue(config, errors, "subagents", subagentsResult.config, subagentsResult.errors);
|
|
136
177
|
const modelSystemNoticesResult = parseModelSystemNotices(data.modelSystemNotices, sourceLabel);
|
|
137
|
-
|
|
138
|
-
config.modelSystemNotices = modelSystemNoticesResult.notices;
|
|
139
|
-
}
|
|
140
|
-
errors.push(...modelSystemNoticesResult.errors);
|
|
178
|
+
assignParsedConfigValue(config, errors, "modelSystemNotices", modelSystemNoticesResult.notices, modelSystemNoticesResult.errors);
|
|
141
179
|
const asyncResult = parseAsyncConfig(data.async, sourceLabel);
|
|
142
|
-
|
|
143
|
-
config.async = asyncResult.config;
|
|
144
|
-
}
|
|
145
|
-
errors.push(...asyncResult.errors);
|
|
180
|
+
assignParsedConfigValue(config, errors, "async", asyncResult.config, asyncResult.errors);
|
|
146
181
|
return { config, errors };
|
|
147
182
|
}
|
|
148
183
|
function parseSandboxConfig(raw, sourceLabel) {
|
|
149
184
|
if (raw === undefined) {
|
|
150
185
|
return { errors: [] };
|
|
151
186
|
}
|
|
152
|
-
|
|
187
|
+
const parsed = SandboxSchema.safeParse(raw);
|
|
188
|
+
if (!parsed.success) {
|
|
153
189
|
return { errors: [`${sourceLabel}: 'sandbox' must be an object.`] };
|
|
154
190
|
}
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
sandbox.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (data.extraDockerArgs !== undefined) {
|
|
185
|
-
if (Array.isArray(data.extraDockerArgs)) {
|
|
186
|
-
const args = [];
|
|
187
|
-
let invalid = false;
|
|
188
|
-
for (const entry of data.extraDockerArgs) {
|
|
189
|
-
if (typeof entry !== "string" || !entry.trim()) {
|
|
190
|
-
invalid = true;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
args.push(entry);
|
|
194
|
-
}
|
|
195
|
-
if (invalid) {
|
|
196
|
-
errors.push(`${sourceLabel}: sandbox.extraDockerArgs must be a string array.`);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
sandbox.extraDockerArgs = args;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
errors.push(`${sourceLabel}: sandbox.extraDockerArgs must be a string array.`);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (data.environmentInfo !== undefined) {
|
|
207
|
-
if (typeof data.environmentInfo === "string") {
|
|
208
|
-
sandbox.environmentInfo = data.environmentInfo;
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
errors.push(`${sourceLabel}: sandbox.environmentInfo must be a string.`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (Object.keys(sandbox).length === 0) {
|
|
215
|
-
return { errors };
|
|
216
|
-
}
|
|
217
|
-
return { config: sandbox, errors };
|
|
191
|
+
const sandboxResult = parseOptionalFields(parsed.data, sourceLabel, [
|
|
192
|
+
["image", SandboxFieldsSchema.shape.image, "sandbox.image must be a non-empty string."],
|
|
193
|
+
[
|
|
194
|
+
"mountPath",
|
|
195
|
+
SandboxFieldsSchema.shape.mountPath,
|
|
196
|
+
"sandbox.mountPath must be a non-empty string.",
|
|
197
|
+
],
|
|
198
|
+
[
|
|
199
|
+
"pruneAfterHours",
|
|
200
|
+
SandboxFieldsSchema.shape.pruneAfterHours,
|
|
201
|
+
"sandbox.pruneAfterHours must be a positive number.",
|
|
202
|
+
],
|
|
203
|
+
[
|
|
204
|
+
"extraDockerArgs",
|
|
205
|
+
SandboxFieldsSchema.shape.extraDockerArgs,
|
|
206
|
+
"sandbox.extraDockerArgs must be a string array.",
|
|
207
|
+
],
|
|
208
|
+
[
|
|
209
|
+
"environmentInfo",
|
|
210
|
+
SandboxFieldsSchema.shape.environmentInfo,
|
|
211
|
+
"sandbox.environmentInfo must be a string.",
|
|
212
|
+
],
|
|
213
|
+
]);
|
|
214
|
+
if (Object.keys(sandboxResult.values).length === 0) {
|
|
215
|
+
return { errors: sandboxResult.errors };
|
|
216
|
+
}
|
|
217
|
+
return { config: sandboxResult.values, errors: sandboxResult.errors };
|
|
218
218
|
}
|
|
219
219
|
function parseAgentContextFiles(raw, sourceLabel) {
|
|
220
220
|
if (raw === undefined) {
|
|
221
221
|
return { paths: [], errors: [] };
|
|
222
222
|
}
|
|
223
|
-
|
|
223
|
+
const parsed = AgentContextFilesSchema.safeParse(raw);
|
|
224
|
+
if (!parsed.success) {
|
|
224
225
|
return {
|
|
225
226
|
paths: [],
|
|
226
227
|
errors: [`${sourceLabel}: 'agentContextFiles' must be a string array.`],
|
|
227
228
|
};
|
|
228
229
|
}
|
|
229
|
-
|
|
230
|
-
for (const entry of raw) {
|
|
231
|
-
if (typeof entry !== "string" || !entry.trim()) {
|
|
232
|
-
return {
|
|
233
|
-
paths: [],
|
|
234
|
-
errors: [`${sourceLabel}: 'agentContextFiles' must be a string array.`],
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
paths.push(entry.trim());
|
|
238
|
-
}
|
|
239
|
-
return { paths, errors: [] };
|
|
230
|
+
return { paths: parsed.data, errors: [] };
|
|
240
231
|
}
|
|
241
232
|
function parseSubagentsConfig(raw, sourceLabel) {
|
|
242
233
|
if (raw === undefined) {
|
|
243
234
|
return { errors: [] };
|
|
244
235
|
}
|
|
245
|
-
|
|
236
|
+
const parsed = SubagentsConfigSchema.safeParse(raw);
|
|
237
|
+
if (!parsed.success) {
|
|
238
|
+
if (parsed.error.issues.some((issue) => issue.path[0] === "defaultLaunchModels")) {
|
|
239
|
+
return { errors: [`${sourceLabel}: subagents.defaultLaunchModels must be a string array.`] };
|
|
240
|
+
}
|
|
246
241
|
return { errors: [`${sourceLabel}: 'subagents' must be an object.`] };
|
|
247
242
|
}
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if ("defaultLaunchModels" in data) {
|
|
252
|
-
if (!Array.isArray(data.defaultLaunchModels)) {
|
|
253
|
-
errors.push(`${sourceLabel}: subagents.defaultLaunchModels must be a string array.`);
|
|
254
|
-
return { errors };
|
|
255
|
-
}
|
|
256
|
-
const launchModelsResult = parseSubagentLaunchModelList(data.defaultLaunchModels);
|
|
257
|
-
if (launchModelsResult.error) {
|
|
258
|
-
errors.push(`${sourceLabel}: subagents.defaultLaunchModels ${launchModelsResult.error}. expected <provider>/<model>:<effort>.`);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
config.defaultLaunchModels = launchModelsResult.launchModels;
|
|
262
|
-
}
|
|
243
|
+
const { defaultLaunchModels } = parsed.data;
|
|
244
|
+
if (defaultLaunchModels === undefined) {
|
|
245
|
+
return { errors: [] };
|
|
263
246
|
}
|
|
264
|
-
|
|
265
|
-
|
|
247
|
+
const launchModelsResult = parseSubagentLaunchModelList(defaultLaunchModels);
|
|
248
|
+
if (launchModelsResult.error) {
|
|
249
|
+
return {
|
|
250
|
+
errors: [
|
|
251
|
+
`${sourceLabel}: subagents.defaultLaunchModels ${launchModelsResult.error}. expected <provider>/<model>:<effort>.`,
|
|
252
|
+
],
|
|
253
|
+
};
|
|
266
254
|
}
|
|
267
|
-
return {
|
|
255
|
+
return {
|
|
256
|
+
config: {
|
|
257
|
+
defaultLaunchModels: launchModelsResult.launchModels,
|
|
258
|
+
},
|
|
259
|
+
errors: [],
|
|
260
|
+
};
|
|
268
261
|
}
|
|
269
262
|
function parseModelNoticeTarget(rawKey) {
|
|
270
263
|
const parsedKey = parseModelNoticeKey(rawKey);
|
|
@@ -289,113 +282,99 @@ function parseModelSystemNotices(raw, sourceLabel) {
|
|
|
289
282
|
if (raw === undefined) {
|
|
290
283
|
return { errors: [] };
|
|
291
284
|
}
|
|
292
|
-
|
|
285
|
+
const parsedRecord = StringRecordSchema.safeParse(raw);
|
|
286
|
+
if (!parsedRecord.success) {
|
|
293
287
|
return { errors: [`${sourceLabel}: 'modelSystemNotices' must be an object.`] };
|
|
294
288
|
}
|
|
295
|
-
const data = raw;
|
|
296
289
|
const notices = {};
|
|
297
290
|
const errors = [];
|
|
298
|
-
for (const [rawKey, rawValue] of Object.entries(data)) {
|
|
291
|
+
for (const [rawKey, rawValue] of Object.entries(parsedRecord.data)) {
|
|
299
292
|
const parsedKey = parseModelNoticeTarget(rawKey);
|
|
300
293
|
if (parsedKey.error || !parsedKey.normalizedKey) {
|
|
301
294
|
errors.push(`${sourceLabel}: modelSystemNotices.${rawKey} ${parsedKey.error ?? "is invalid"}.`);
|
|
302
295
|
continue;
|
|
303
296
|
}
|
|
304
|
-
|
|
297
|
+
const parsedNotice = NonEmptyStringSchema.safeParse(rawValue);
|
|
298
|
+
if (!parsedNotice.success) {
|
|
305
299
|
errors.push(`${sourceLabel}: modelSystemNotices.${rawKey} must be a non-empty string notice.`);
|
|
306
300
|
continue;
|
|
307
301
|
}
|
|
308
|
-
notices[parsedKey.normalizedKey] =
|
|
302
|
+
notices[parsedKey.normalizedKey] = parsedNotice.data;
|
|
309
303
|
}
|
|
310
304
|
if (Object.keys(notices).length === 0) {
|
|
311
305
|
return { errors };
|
|
312
306
|
}
|
|
313
307
|
return { notices, errors };
|
|
314
308
|
}
|
|
315
|
-
function isPositiveInteger(value) {
|
|
316
|
-
return (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value > 0);
|
|
317
|
-
}
|
|
318
|
-
function parsePositiveIntegerField(value, options) {
|
|
319
|
-
if (!isPositiveInteger(value)) {
|
|
320
|
-
return undefined;
|
|
321
|
-
}
|
|
322
|
-
if (options?.max !== undefined && value > options.max) {
|
|
323
|
-
return undefined;
|
|
324
|
-
}
|
|
325
|
-
return value;
|
|
326
|
-
}
|
|
327
309
|
function parseAsyncClientTarget(raw, sourceLabel, key) {
|
|
328
|
-
|
|
310
|
+
const parsedRecord = StringRecordSchema.safeParse(raw);
|
|
311
|
+
if (!parsedRecord.success) {
|
|
329
312
|
return { errors: [`${sourceLabel}: async.client.targets.${key} must be an object.`] };
|
|
330
313
|
}
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
if (errors.length > 0) {
|
|
353
|
-
return { errors };
|
|
354
|
-
}
|
|
355
|
-
return { config, errors: [] };
|
|
314
|
+
const parsedTarget = AsyncClientTargetSchema.safeParse(parsedRecord.data);
|
|
315
|
+
if (parsedTarget.success) {
|
|
316
|
+
return { config: parsedTarget.data, errors: [] };
|
|
317
|
+
}
|
|
318
|
+
const errors = new Set();
|
|
319
|
+
for (const issue of parsedTarget.error.issues) {
|
|
320
|
+
switch (issue.path[0]) {
|
|
321
|
+
case "url":
|
|
322
|
+
errors.add(`${sourceLabel}: async.client.targets.${key}.url must be a non-empty string.`);
|
|
323
|
+
break;
|
|
324
|
+
case "token":
|
|
325
|
+
errors.add(`${sourceLabel}: async.client.targets.${key}.token must be a non-empty string.`);
|
|
326
|
+
break;
|
|
327
|
+
case "timeoutMs":
|
|
328
|
+
errors.add(`${sourceLabel}: async.client.targets.${key}.timeoutMs must be a positive integer.`);
|
|
329
|
+
break;
|
|
330
|
+
default:
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return { errors: [...errors] };
|
|
356
335
|
}
|
|
357
336
|
function parseAsyncClientConfig(raw, sourceLabel) {
|
|
358
337
|
if (raw === undefined) {
|
|
359
338
|
return { errors: [] };
|
|
360
339
|
}
|
|
361
|
-
|
|
340
|
+
const parsedRecord = StringRecordSchema.safeParse(raw);
|
|
341
|
+
if (!parsedRecord.success) {
|
|
362
342
|
return { errors: [`${sourceLabel}: async.client must be an object.`] };
|
|
363
343
|
}
|
|
364
|
-
const data =
|
|
365
|
-
const errors = [];
|
|
344
|
+
const data = parsedRecord.data;
|
|
366
345
|
const config = {};
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
}
|
|
346
|
+
const scalarResult = parseOptionalFields(data, sourceLabel, [
|
|
347
|
+
[
|
|
348
|
+
"defaultTarget",
|
|
349
|
+
NonEmptyStringSchema,
|
|
350
|
+
"async.client.defaultTarget must be a non-empty string.",
|
|
351
|
+
],
|
|
352
|
+
[
|
|
353
|
+
"defaultProjectId",
|
|
354
|
+
NonEmptyStringSchema,
|
|
355
|
+
"async.client.defaultProjectId must be a non-empty string.",
|
|
356
|
+
],
|
|
357
|
+
]);
|
|
358
|
+
Object.assign(config, scalarResult.values);
|
|
359
|
+
const errors = [...scalarResult.errors];
|
|
383
360
|
if (data.targets !== undefined) {
|
|
384
|
-
|
|
361
|
+
const parsedTargetsRecord = StringRecordSchema.safeParse(data.targets);
|
|
362
|
+
if (!parsedTargetsRecord.success) {
|
|
385
363
|
errors.push(`${sourceLabel}: async.client.targets must be an object.`);
|
|
386
364
|
}
|
|
387
365
|
else {
|
|
388
366
|
const targets = {};
|
|
389
|
-
for (const [key, value] of Object.entries(data
|
|
390
|
-
|
|
367
|
+
for (const [key, value] of Object.entries(parsedTargetsRecord.data)) {
|
|
368
|
+
const parsedTargetKey = NonEmptyStringSchema.safeParse(key);
|
|
369
|
+
if (!parsedTargetKey.success) {
|
|
391
370
|
errors.push(`${sourceLabel}: async.client.targets keys must be non-empty.`);
|
|
392
371
|
continue;
|
|
393
372
|
}
|
|
394
|
-
const
|
|
395
|
-
if (
|
|
396
|
-
targets[key] =
|
|
373
|
+
const parsedTarget = parseAsyncClientTarget(value, sourceLabel, key);
|
|
374
|
+
if (parsedTarget.config) {
|
|
375
|
+
targets[key] = parsedTarget.config;
|
|
397
376
|
}
|
|
398
|
-
errors.push(...
|
|
377
|
+
errors.push(...parsedTarget.errors);
|
|
399
378
|
}
|
|
400
379
|
if (Object.keys(targets).length > 0) {
|
|
401
380
|
config.targets = targets;
|
|
@@ -411,10 +390,11 @@ function parseAsyncConfig(raw, sourceLabel) {
|
|
|
411
390
|
if (raw === undefined) {
|
|
412
391
|
return { errors: [] };
|
|
413
392
|
}
|
|
414
|
-
|
|
393
|
+
const parsedRecord = StringRecordSchema.safeParse(raw);
|
|
394
|
+
if (!parsedRecord.success) {
|
|
415
395
|
return { errors: [`${sourceLabel}: 'async' must be an object.`] };
|
|
416
396
|
}
|
|
417
|
-
const data =
|
|
397
|
+
const data = parsedRecord.data;
|
|
418
398
|
const config = {};
|
|
419
399
|
const errors = [];
|
|
420
400
|
const clientResult = parseAsyncClientConfig(data.client, sourceLabel);
|
|
@@ -422,11 +402,12 @@ function parseAsyncConfig(raw, sourceLabel) {
|
|
|
422
402
|
config.client = clientResult.config;
|
|
423
403
|
}
|
|
424
404
|
errors.push(...clientResult.errors);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
405
|
+
const movedAsyncFields = ["server", "projects"];
|
|
406
|
+
for (const field of movedAsyncFields) {
|
|
407
|
+
if (data[field] === undefined) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
errors.push(`${sourceLabel}: async.${field} was moved to daemon config file. use 'tau async daemon --config-file <path>'.`);
|
|
430
411
|
}
|
|
431
412
|
if (Object.keys(config).length === 0) {
|
|
432
413
|
return { errors };
|
|
@@ -455,16 +436,7 @@ function loadConfigFile(level, deps, sourceLabel) {
|
|
|
455
436
|
};
|
|
456
437
|
}
|
|
457
438
|
}
|
|
458
|
-
function
|
|
459
|
-
if (!target && !overlay) {
|
|
460
|
-
return undefined;
|
|
461
|
-
}
|
|
462
|
-
return {
|
|
463
|
-
...(target ?? {}),
|
|
464
|
-
...(overlay ?? {}),
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
function mergeSandboxConfig(target, overlay) {
|
|
439
|
+
function mergeOptionalObject(target, overlay) {
|
|
468
440
|
if (!target && !overlay) {
|
|
469
441
|
return undefined;
|
|
470
442
|
}
|
|
@@ -485,24 +457,6 @@ function mergeSubagentsConfig(target, overlay) {
|
|
|
485
457
|
}
|
|
486
458
|
return merged;
|
|
487
459
|
}
|
|
488
|
-
function mergeModelSystemNotices(target, overlay) {
|
|
489
|
-
if (!target && !overlay) {
|
|
490
|
-
return undefined;
|
|
491
|
-
}
|
|
492
|
-
return {
|
|
493
|
-
...(target ?? {}),
|
|
494
|
-
...(overlay ?? {}),
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
function mergeAsyncClientTarget(target, overlay) {
|
|
498
|
-
if (!target && !overlay) {
|
|
499
|
-
return undefined;
|
|
500
|
-
}
|
|
501
|
-
return {
|
|
502
|
-
...(target ?? {}),
|
|
503
|
-
...(overlay ?? {}),
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
460
|
function mergeAsyncClientConfig(target, overlay) {
|
|
507
461
|
if (!target && !overlay) {
|
|
508
462
|
return undefined;
|
|
@@ -522,7 +476,7 @@ function mergeAsyncClientConfig(target, overlay) {
|
|
|
522
476
|
targets.set(key, { ...value });
|
|
523
477
|
}
|
|
524
478
|
for (const [key, value] of Object.entries(overlay?.targets ?? {})) {
|
|
525
|
-
targets.set(key,
|
|
479
|
+
targets.set(key, mergeOptionalObject(targets.get(key), value) ?? value);
|
|
526
480
|
}
|
|
527
481
|
if (targets.size > 0) {
|
|
528
482
|
merged.targets = Object.fromEntries(targets.entries());
|
|
@@ -541,26 +495,6 @@ function mergeAsyncConfig(target, overlay) {
|
|
|
541
495
|
};
|
|
542
496
|
return merged;
|
|
543
497
|
}
|
|
544
|
-
function resolveAsyncConfig(_level, config) {
|
|
545
|
-
return {
|
|
546
|
-
...config,
|
|
547
|
-
...(config.client
|
|
548
|
-
? {
|
|
549
|
-
client: {
|
|
550
|
-
...config.client,
|
|
551
|
-
...(config.client.targets
|
|
552
|
-
? {
|
|
553
|
-
targets: Object.fromEntries(Object.entries(config.client.targets).map(([key, value]) => [
|
|
554
|
-
key,
|
|
555
|
-
{ ...value },
|
|
556
|
-
])),
|
|
557
|
-
}
|
|
558
|
-
: {}),
|
|
559
|
-
},
|
|
560
|
-
}
|
|
561
|
-
: {}),
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
498
|
function resolveAgentContextPaths(level, rawPaths) {
|
|
565
499
|
const root = level.levelRoot;
|
|
566
500
|
return rawPaths.map((entry) => resolve(root, entry));
|
|
@@ -592,11 +526,11 @@ function mergeConfigLevels(levels, configs) {
|
|
|
592
526
|
for (let i = 0; i < levels.length; i += 1) {
|
|
593
527
|
const level = levels[i];
|
|
594
528
|
const config = configs[i] ?? {};
|
|
595
|
-
apiKeys =
|
|
596
|
-
sandbox =
|
|
529
|
+
apiKeys = mergeOptionalObject(apiKeys, config.apiKeys);
|
|
530
|
+
sandbox = mergeOptionalObject(sandbox, config.sandbox);
|
|
597
531
|
subagents = mergeSubagentsConfig(subagents, config.subagents);
|
|
598
|
-
modelSystemNotices =
|
|
599
|
-
asyncConfig = mergeAsyncConfig(asyncConfig, config.async
|
|
532
|
+
modelSystemNotices = mergeOptionalObject(modelSystemNotices, config.modelSystemNotices);
|
|
533
|
+
asyncConfig = mergeAsyncConfig(asyncConfig, config.async);
|
|
600
534
|
if (config.defaultPersona !== undefined) {
|
|
601
535
|
merged.defaultPersona = config.defaultPersona;
|
|
602
536
|
}
|