@actant/core 0.2.0 → 0.2.1
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/{skill-md-parser-2HXC4AAW.js → chunk-DYGZP4MW.js} +8 -2
- package/dist/{skill-md-parser-2HXC4AAW.js.map → chunk-DYGZP4MW.js.map} +1 -1
- package/dist/index.d.ts +135 -24
- package/dist/index.js +1039 -170
- package/dist/index.js.map +1 -1
- package/dist/skill-md-parser-HXLTZAUU.js +9 -0
- package/dist/skill-md-parser-HXLTZAUU.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseSkillMdContent
|
|
3
|
+
} from "./chunk-DYGZP4MW.js";
|
|
4
|
+
|
|
1
5
|
// src/builder/cursor-builder.ts
|
|
2
6
|
import { writeFile, mkdir, stat } from "fs/promises";
|
|
3
7
|
import { join } from "path";
|
|
@@ -1064,12 +1068,26 @@ var AgentBackendSchema = z2.object({
|
|
|
1064
1068
|
type: z2.enum(["cursor", "cursor-agent", "claude-code", "custom", "pi"]),
|
|
1065
1069
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
1066
1070
|
});
|
|
1071
|
+
var ModelApiProtocolEnum = z2.enum(["openai", "anthropic", "custom"]);
|
|
1072
|
+
var DEFAULT_PROTOCOL = {
|
|
1073
|
+
anthropic: "anthropic",
|
|
1074
|
+
openai: "openai",
|
|
1075
|
+
deepseek: "openai",
|
|
1076
|
+
ollama: "openai",
|
|
1077
|
+
azure: "openai",
|
|
1078
|
+
bedrock: "anthropic",
|
|
1079
|
+
vertex: "anthropic",
|
|
1080
|
+
custom: "custom"
|
|
1081
|
+
};
|
|
1067
1082
|
var ModelProviderSchema = z2.object({
|
|
1068
|
-
type: z2.
|
|
1069
|
-
protocol:
|
|
1083
|
+
type: z2.string().min(1),
|
|
1084
|
+
protocol: ModelApiProtocolEnum.optional(),
|
|
1070
1085
|
baseUrl: z2.string().optional(),
|
|
1071
1086
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
1072
|
-
})
|
|
1087
|
+
}).transform((val) => ({
|
|
1088
|
+
...val,
|
|
1089
|
+
protocol: val.protocol ?? DEFAULT_PROTOCOL[val.type] ?? "custom"
|
|
1090
|
+
}));
|
|
1073
1091
|
var InitializerStepSchema = z2.object({
|
|
1074
1092
|
type: z2.string().min(1),
|
|
1075
1093
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
@@ -1127,7 +1145,7 @@ var AgentTemplateSchema = z2.object({
|
|
|
1127
1145
|
origin: ComponentOriginSchema.optional(),
|
|
1128
1146
|
tags: z2.array(z2.string()).optional(),
|
|
1129
1147
|
backend: AgentBackendSchema,
|
|
1130
|
-
provider: ModelProviderSchema,
|
|
1148
|
+
provider: ModelProviderSchema.optional(),
|
|
1131
1149
|
domainContext: DomainContextSchema,
|
|
1132
1150
|
permissions: PermissionsInputSchema.optional(),
|
|
1133
1151
|
initializer: InitializerSchema.optional(),
|
|
@@ -1255,6 +1273,58 @@ function isNodeError(err) {
|
|
|
1255
1273
|
return err instanceof Error && "code" in err;
|
|
1256
1274
|
}
|
|
1257
1275
|
|
|
1276
|
+
// src/provider/model-provider-registry.ts
|
|
1277
|
+
import { createLogger as createLogger11 } from "@actant/shared";
|
|
1278
|
+
var logger11 = createLogger11("model-provider-registry");
|
|
1279
|
+
var ModelProviderRegistry = class {
|
|
1280
|
+
descriptors = /* @__PURE__ */ new Map();
|
|
1281
|
+
defaultType;
|
|
1282
|
+
register(descriptor) {
|
|
1283
|
+
this.descriptors.set(descriptor.type, descriptor);
|
|
1284
|
+
logger11.debug({ type: descriptor.type }, "Provider registered");
|
|
1285
|
+
}
|
|
1286
|
+
get(type) {
|
|
1287
|
+
return this.descriptors.get(type);
|
|
1288
|
+
}
|
|
1289
|
+
getOrThrow(type) {
|
|
1290
|
+
const desc = this.descriptors.get(type);
|
|
1291
|
+
if (!desc) {
|
|
1292
|
+
throw new Error(
|
|
1293
|
+
`Provider "${type}" is not registered. Available providers: [${[...this.descriptors.keys()].join(", ")}].`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
return desc;
|
|
1297
|
+
}
|
|
1298
|
+
has(type) {
|
|
1299
|
+
return this.descriptors.has(type);
|
|
1300
|
+
}
|
|
1301
|
+
list() {
|
|
1302
|
+
return [...this.descriptors.values()];
|
|
1303
|
+
}
|
|
1304
|
+
setDefault(type) {
|
|
1305
|
+
if (!this.descriptors.has(type)) {
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
`Cannot set default: provider "${type}" is not registered.`
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
this.defaultType = type;
|
|
1311
|
+
logger11.info({ type }, "Default provider set");
|
|
1312
|
+
}
|
|
1313
|
+
getDefault() {
|
|
1314
|
+
if (!this.defaultType) return void 0;
|
|
1315
|
+
return this.descriptors.get(this.defaultType);
|
|
1316
|
+
}
|
|
1317
|
+
getDefaultType() {
|
|
1318
|
+
return this.defaultType;
|
|
1319
|
+
}
|
|
1320
|
+
/** @internal Test-only: clear all registrations. */
|
|
1321
|
+
_reset() {
|
|
1322
|
+
this.descriptors.clear();
|
|
1323
|
+
this.defaultType = void 0;
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
var modelProviderRegistry = new ModelProviderRegistry();
|
|
1327
|
+
|
|
1258
1328
|
// src/template/schema/config-validators.ts
|
|
1259
1329
|
function zodToIssues(zodError) {
|
|
1260
1330
|
return zodError.issues.map((i) => ({
|
|
@@ -1278,7 +1348,15 @@ function validateProviderConfig(data) {
|
|
|
1278
1348
|
if (!result.success) {
|
|
1279
1349
|
return { valid: false, errors: zodToIssues(result.error), warnings: [] };
|
|
1280
1350
|
}
|
|
1281
|
-
|
|
1351
|
+
const warnings = [];
|
|
1352
|
+
if (!modelProviderRegistry.has(result.data.type)) {
|
|
1353
|
+
warnings.push(warning(
|
|
1354
|
+
"provider.type",
|
|
1355
|
+
`Provider type "${result.data.type}" is not registered; it will be treated as a custom provider`,
|
|
1356
|
+
"UNKNOWN_PROVIDER_TYPE"
|
|
1357
|
+
));
|
|
1358
|
+
}
|
|
1359
|
+
return { valid: true, data: result.data, errors: [], warnings };
|
|
1282
1360
|
}
|
|
1283
1361
|
function validatePermissionsConfig(data) {
|
|
1284
1362
|
const result = PermissionsInputSchema.safeParse(data);
|
|
@@ -1362,12 +1440,21 @@ function validateTemplate(data) {
|
|
|
1362
1440
|
"CUSTOM_BACKEND_NO_CONFIG"
|
|
1363
1441
|
));
|
|
1364
1442
|
}
|
|
1365
|
-
if (template.provider
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1443
|
+
if (template.provider) {
|
|
1444
|
+
if (template.provider.type === "custom" && !template.provider.config) {
|
|
1445
|
+
warnings.push(warning(
|
|
1446
|
+
"provider.config",
|
|
1447
|
+
"Custom provider type without config; model routing may fail",
|
|
1448
|
+
"CUSTOM_PROVIDER_NO_CONFIG"
|
|
1449
|
+
));
|
|
1450
|
+
}
|
|
1451
|
+
if (!modelProviderRegistry.has(template.provider.type)) {
|
|
1452
|
+
warnings.push(warning(
|
|
1453
|
+
"provider.type",
|
|
1454
|
+
`Provider type "${template.provider.type}" is not registered; it will be treated as a custom provider`,
|
|
1455
|
+
"UNKNOWN_PROVIDER_TYPE"
|
|
1456
|
+
));
|
|
1457
|
+
}
|
|
1371
1458
|
}
|
|
1372
1459
|
return { valid: true, data: template, errors: [], warnings };
|
|
1373
1460
|
}
|
|
@@ -1380,13 +1467,13 @@ import { TemplateNotFoundError, ConfigValidationError as ConfigValidationError3
|
|
|
1380
1467
|
// src/domain/base-component-manager.ts
|
|
1381
1468
|
import { readFile as readFile2, writeFile as writeFile4, readdir as readdir2, stat as stat4, unlink, mkdir as mkdir4 } from "fs/promises";
|
|
1382
1469
|
import { join as join5, extname as extname2, resolve } from "path";
|
|
1383
|
-
import { ComponentReferenceError, ConfigNotFoundError as ConfigNotFoundError2, ConfigValidationError as ConfigValidationError2, createLogger as
|
|
1470
|
+
import { ComponentReferenceError, ConfigNotFoundError as ConfigNotFoundError2, ConfigValidationError as ConfigValidationError2, createLogger as createLogger12 } from "@actant/shared";
|
|
1384
1471
|
var BaseComponentManager = class {
|
|
1385
1472
|
components = /* @__PURE__ */ new Map();
|
|
1386
1473
|
logger;
|
|
1387
1474
|
persistDir;
|
|
1388
1475
|
constructor(loggerName) {
|
|
1389
|
-
this.logger =
|
|
1476
|
+
this.logger = createLogger12(loggerName);
|
|
1390
1477
|
}
|
|
1391
1478
|
setPersistDir(dir) {
|
|
1392
1479
|
this.persistDir = dir;
|
|
@@ -1719,8 +1806,8 @@ var TemplateRegistry = class extends BaseComponentManager {
|
|
|
1719
1806
|
import { watch } from "fs";
|
|
1720
1807
|
import { join as join7 } from "path";
|
|
1721
1808
|
import { access } from "fs/promises";
|
|
1722
|
-
import { createLogger as
|
|
1723
|
-
var
|
|
1809
|
+
import { createLogger as createLogger13 } from "@actant/shared";
|
|
1810
|
+
var logger12 = createLogger13("template-file-watcher");
|
|
1724
1811
|
var DEFAULT_DEBOUNCE_MS = 300;
|
|
1725
1812
|
var TemplateFileWatcher = class {
|
|
1726
1813
|
constructor(templatesDir, registry3, options) {
|
|
@@ -1742,12 +1829,12 @@ var TemplateFileWatcher = class {
|
|
|
1742
1829
|
this.handleChange(filename);
|
|
1743
1830
|
});
|
|
1744
1831
|
this.watcher.on("error", (err) => {
|
|
1745
|
-
|
|
1832
|
+
logger12.error({ error: err }, "File watcher error");
|
|
1746
1833
|
});
|
|
1747
1834
|
this.buildFileMap();
|
|
1748
|
-
|
|
1835
|
+
logger12.info({ dir: this.templatesDir }, "Template file watcher started");
|
|
1749
1836
|
} catch (err) {
|
|
1750
|
-
|
|
1837
|
+
logger12.error({ error: err, dir: this.templatesDir }, "Failed to start file watcher");
|
|
1751
1838
|
}
|
|
1752
1839
|
}
|
|
1753
1840
|
stop() {
|
|
@@ -1758,7 +1845,7 @@ var TemplateFileWatcher = class {
|
|
|
1758
1845
|
clearTimeout(timer);
|
|
1759
1846
|
}
|
|
1760
1847
|
this.debounceTimers.clear();
|
|
1761
|
-
|
|
1848
|
+
logger12.info("Template file watcher stopped");
|
|
1762
1849
|
}
|
|
1763
1850
|
get isWatching() {
|
|
1764
1851
|
return this.watcher !== null;
|
|
@@ -1777,7 +1864,7 @@ var TemplateFileWatcher = class {
|
|
|
1777
1864
|
setTimeout(() => {
|
|
1778
1865
|
this.debounceTimers.delete(filename);
|
|
1779
1866
|
this.processChange(filename).catch((err) => {
|
|
1780
|
-
|
|
1867
|
+
logger12.error({ filename, error: err }, "Error processing template file change");
|
|
1781
1868
|
});
|
|
1782
1869
|
}, this.debounceMs)
|
|
1783
1870
|
);
|
|
@@ -1795,7 +1882,7 @@ var TemplateFileWatcher = class {
|
|
|
1795
1882
|
if (previousName && this.registry.has(previousName)) {
|
|
1796
1883
|
this.registry.unregister(previousName);
|
|
1797
1884
|
this.fileToName.delete(filename);
|
|
1798
|
-
|
|
1885
|
+
logger12.info({ templateName: previousName, filename }, "Template unregistered (file deleted)");
|
|
1799
1886
|
}
|
|
1800
1887
|
return;
|
|
1801
1888
|
}
|
|
@@ -1810,9 +1897,9 @@ var TemplateFileWatcher = class {
|
|
|
1810
1897
|
}
|
|
1811
1898
|
this.registry.register(template);
|
|
1812
1899
|
this.fileToName.set(filename, template.name);
|
|
1813
|
-
|
|
1900
|
+
logger12.info({ templateName: template.name, filename }, previousName ? "Template reloaded" : "New template registered");
|
|
1814
1901
|
} catch (err) {
|
|
1815
|
-
|
|
1902
|
+
logger12.warn({ filePath, error: err }, "Failed to reload template");
|
|
1816
1903
|
}
|
|
1817
1904
|
}
|
|
1818
1905
|
};
|
|
@@ -1826,7 +1913,7 @@ import {
|
|
|
1826
1913
|
ConfigValidationError as ConfigValidationError4,
|
|
1827
1914
|
InstanceCorruptedError as InstanceCorruptedError3,
|
|
1828
1915
|
WorkspaceInitError,
|
|
1829
|
-
createLogger as
|
|
1916
|
+
createLogger as createLogger16
|
|
1830
1917
|
} from "@actant/shared";
|
|
1831
1918
|
|
|
1832
1919
|
// src/state/instance-meta-schema.ts
|
|
@@ -1873,6 +1960,13 @@ var PermissionsConfigSchema = z3.object({
|
|
|
1873
1960
|
sandbox: SandboxConfigSchema.optional(),
|
|
1874
1961
|
additionalDirectories: z3.array(z3.string()).optional()
|
|
1875
1962
|
});
|
|
1963
|
+
var ModelApiProtocolSchema = z3.enum(["openai", "anthropic", "custom"]);
|
|
1964
|
+
var ModelProviderConfigSchema = z3.object({
|
|
1965
|
+
type: z3.string().min(1),
|
|
1966
|
+
protocol: ModelApiProtocolSchema.optional().default("custom"),
|
|
1967
|
+
baseUrl: z3.string().optional(),
|
|
1968
|
+
config: z3.record(z3.string(), z3.unknown()).optional()
|
|
1969
|
+
});
|
|
1876
1970
|
var AgentInstanceMetaSchema = z3.object({
|
|
1877
1971
|
id: z3.string().min(1),
|
|
1878
1972
|
name: z3.string().min(1),
|
|
@@ -1880,6 +1974,7 @@ var AgentInstanceMetaSchema = z3.object({
|
|
|
1880
1974
|
templateVersion: z3.string().regex(/^\d+\.\d+\.\d+$/),
|
|
1881
1975
|
backendType: AgentBackendTypeSchema.default("cursor"),
|
|
1882
1976
|
backendConfig: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1977
|
+
providerConfig: ModelProviderConfigSchema.optional(),
|
|
1883
1978
|
status: AgentStatusSchema,
|
|
1884
1979
|
launchMode: LaunchModeSchema,
|
|
1885
1980
|
workspacePolicy: WorkspacePolicySchema.default("persistent"),
|
|
@@ -1896,8 +1991,8 @@ import { readFile as readFile3, writeFile as writeFile5, rename, readdir as read
|
|
|
1896
1991
|
import { join as join8, dirname } from "path";
|
|
1897
1992
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1898
1993
|
import { InstanceCorruptedError } from "@actant/shared";
|
|
1899
|
-
import { createLogger as
|
|
1900
|
-
var
|
|
1994
|
+
import { createLogger as createLogger14 } from "@actant/shared";
|
|
1995
|
+
var logger13 = createLogger14("instance-meta-io");
|
|
1901
1996
|
var META_FILENAME = ".actant.json";
|
|
1902
1997
|
function metaFilePath(workspaceDir) {
|
|
1903
1998
|
return join8(workspaceDir, META_FILENAME);
|
|
@@ -1966,7 +2061,7 @@ async function scanInstances(instancesBaseDir, registry3) {
|
|
|
1966
2061
|
valid.push(meta);
|
|
1967
2062
|
validNames.add(entry.name);
|
|
1968
2063
|
} catch (err) {
|
|
1969
|
-
|
|
2064
|
+
logger13.warn({ name: entry.name, path: entry.workspacePath, error: err }, "Registry entry unreachable or corrupted");
|
|
1970
2065
|
corrupted.push(entry.name);
|
|
1971
2066
|
}
|
|
1972
2067
|
}
|
|
@@ -1991,7 +2086,7 @@ async function scanInstances(instancesBaseDir, registry3) {
|
|
|
1991
2086
|
valid.push(meta);
|
|
1992
2087
|
validNames.add(meta.name);
|
|
1993
2088
|
} catch (err) {
|
|
1994
|
-
|
|
2089
|
+
logger13.warn({ dir: entry, error: err }, "Corrupted instance directory");
|
|
1995
2090
|
corrupted.push(entry);
|
|
1996
2091
|
}
|
|
1997
2092
|
}
|
|
@@ -2140,8 +2235,8 @@ function isNodeError4(err) {
|
|
|
2140
2235
|
}
|
|
2141
2236
|
|
|
2142
2237
|
// src/initializer/pipeline/initialization-pipeline.ts
|
|
2143
|
-
import { createLogger as
|
|
2144
|
-
var
|
|
2238
|
+
import { createLogger as createLogger15 } from "@actant/shared";
|
|
2239
|
+
var logger14 = createLogger15("initialization-pipeline");
|
|
2145
2240
|
var DEFAULT_STEP_TIMEOUT_MS = 6e4;
|
|
2146
2241
|
var DEFAULT_TOTAL_TIMEOUT_MS = 3e5;
|
|
2147
2242
|
var InitializationPipeline = class {
|
|
@@ -2175,14 +2270,14 @@ var InitializationPipeline = class {
|
|
|
2175
2270
|
const errors = [];
|
|
2176
2271
|
const executed = [];
|
|
2177
2272
|
const pipelineStart = Date.now();
|
|
2178
|
-
|
|
2273
|
+
logger14.info({ stepsCount: steps.length }, "Starting initialization pipeline");
|
|
2179
2274
|
for (let i = 0; i < steps.length; i++) {
|
|
2180
2275
|
const step = steps[i];
|
|
2181
2276
|
if (!step) continue;
|
|
2182
2277
|
if (Date.now() - pipelineStart > this.totalTimeoutMs) {
|
|
2183
2278
|
const err = new Error(`Pipeline total timeout exceeded (${this.totalTimeoutMs}ms)`);
|
|
2184
2279
|
errors.push({ stepIndex: i, stepType: step.type, error: err });
|
|
2185
|
-
|
|
2280
|
+
logger14.error({ stepIndex: i, stepType: step.type }, "Pipeline timeout exceeded");
|
|
2186
2281
|
await this.rollback(executed, context, err);
|
|
2187
2282
|
return { success: false, stepsExecuted: i, stepsTotal: steps.length, errors, outputs };
|
|
2188
2283
|
}
|
|
@@ -2194,7 +2289,7 @@ var InitializationPipeline = class {
|
|
|
2194
2289
|
return { success: false, stepsExecuted: i, stepsTotal: steps.length, errors, outputs };
|
|
2195
2290
|
}
|
|
2196
2291
|
this.onProgress?.(i, steps.length, step.type);
|
|
2197
|
-
|
|
2292
|
+
logger14.debug({ stepIndex: i, stepType: step.type }, "Executing step");
|
|
2198
2293
|
try {
|
|
2199
2294
|
const result = await this.executeWithTimeout(executor, context, step.config ?? {});
|
|
2200
2295
|
if (!result.success) {
|
|
@@ -2209,18 +2304,18 @@ var InitializationPipeline = class {
|
|
|
2209
2304
|
outputs.set(`step-${i}-${step.type}`, result.output);
|
|
2210
2305
|
}
|
|
2211
2306
|
if (result.message) {
|
|
2212
|
-
|
|
2307
|
+
logger14.info({ stepIndex: i, stepType: step.type }, result.message);
|
|
2213
2308
|
}
|
|
2214
2309
|
} catch (err) {
|
|
2215
2310
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
2216
2311
|
errors.push({ stepIndex: i, stepType: step.type, error });
|
|
2217
|
-
|
|
2312
|
+
logger14.error({ stepIndex: i, stepType: step.type, error }, "Step execution failed");
|
|
2218
2313
|
executed.push({ index: i, executor, config: step.config ?? {} });
|
|
2219
2314
|
await this.rollback(executed, context, error);
|
|
2220
2315
|
return { success: false, stepsExecuted: i + 1, stepsTotal: steps.length, errors, outputs };
|
|
2221
2316
|
}
|
|
2222
2317
|
}
|
|
2223
|
-
|
|
2318
|
+
logger14.info({ stepsExecuted: steps.length, elapsedMs: Date.now() - pipelineStart }, "Pipeline completed successfully");
|
|
2224
2319
|
return { success: true, stepsExecuted: steps.length, stepsTotal: steps.length, errors, outputs };
|
|
2225
2320
|
}
|
|
2226
2321
|
async executeWithTimeout(executor, context, config) {
|
|
@@ -2242,7 +2337,7 @@ var InitializationPipeline = class {
|
|
|
2242
2337
|
}
|
|
2243
2338
|
async rollback(executed, context, triggerError) {
|
|
2244
2339
|
if (executed.length === 0) return;
|
|
2245
|
-
|
|
2340
|
+
logger14.info({ stepsToRollback: executed.length }, "Rolling back executed steps");
|
|
2246
2341
|
for (let i = executed.length - 1; i >= 0; i--) {
|
|
2247
2342
|
const entry = executed[i];
|
|
2248
2343
|
if (!entry) continue;
|
|
@@ -2250,9 +2345,9 @@ var InitializationPipeline = class {
|
|
|
2250
2345
|
if (!executor.rollback) continue;
|
|
2251
2346
|
try {
|
|
2252
2347
|
await executor.rollback(context, config, triggerError);
|
|
2253
|
-
|
|
2348
|
+
logger14.debug({ stepIndex: index, stepType: executor.type }, "Step rolled back");
|
|
2254
2349
|
} catch (rollbackErr) {
|
|
2255
|
-
|
|
2350
|
+
logger14.warn(
|
|
2256
2351
|
{ stepIndex: index, stepType: executor.type, error: rollbackErr },
|
|
2257
2352
|
"Rollback failed (best-effort)"
|
|
2258
2353
|
);
|
|
@@ -2262,7 +2357,7 @@ var InitializationPipeline = class {
|
|
|
2262
2357
|
};
|
|
2263
2358
|
|
|
2264
2359
|
// src/initializer/agent-initializer.ts
|
|
2265
|
-
var
|
|
2360
|
+
var logger15 = createLogger16("agent-initializer");
|
|
2266
2361
|
var AgentInitializer = class {
|
|
2267
2362
|
constructor(templateRegistry, instancesBaseDir, options) {
|
|
2268
2363
|
this.templateRegistry = templateRegistry;
|
|
@@ -2304,10 +2399,10 @@ var AgentInitializer = class {
|
|
|
2304
2399
|
);
|
|
2305
2400
|
case "overwrite":
|
|
2306
2401
|
await rm(workspaceDir, { recursive: true, force: true });
|
|
2307
|
-
|
|
2402
|
+
logger15.info({ workspaceDir }, "Existing directory removed (overwrite)");
|
|
2308
2403
|
break;
|
|
2309
2404
|
case "append":
|
|
2310
|
-
|
|
2405
|
+
logger15.info({ workspaceDir }, "Appending to existing directory");
|
|
2311
2406
|
break;
|
|
2312
2407
|
}
|
|
2313
2408
|
}
|
|
@@ -2335,7 +2430,7 @@ var AgentInitializer = class {
|
|
|
2335
2430
|
workspaceDir,
|
|
2336
2431
|
instanceMeta: { name, templateName: template.name },
|
|
2337
2432
|
template,
|
|
2338
|
-
logger:
|
|
2433
|
+
logger: logger15,
|
|
2339
2434
|
state: /* @__PURE__ */ new Map()
|
|
2340
2435
|
};
|
|
2341
2436
|
const pipelineResult = await this.pipeline.run(template.initializer.steps, stepContext);
|
|
@@ -2348,6 +2443,7 @@ var AgentInitializer = class {
|
|
|
2348
2443
|
}
|
|
2349
2444
|
}
|
|
2350
2445
|
const effectivePermissions = resolvePermissions(finalPermissions);
|
|
2446
|
+
const resolvedProvider = resolveProviderConfig(template.provider);
|
|
2351
2447
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2352
2448
|
const launchMode = overrides?.launchMode ?? this.options?.defaultLaunchMode ?? "direct";
|
|
2353
2449
|
const defaultPolicy = launchMode === "one-shot" ? "ephemeral" : "persistent";
|
|
@@ -2358,6 +2454,7 @@ var AgentInitializer = class {
|
|
|
2358
2454
|
templateVersion: template.version,
|
|
2359
2455
|
backendType: template.backend.type,
|
|
2360
2456
|
backendConfig: template.backend.config ? { ...template.backend.config } : void 0,
|
|
2457
|
+
providerConfig: resolvedProvider,
|
|
2361
2458
|
status: "created",
|
|
2362
2459
|
launchMode,
|
|
2363
2460
|
workspacePolicy: overrides?.workspacePolicy ?? defaultPolicy,
|
|
@@ -2372,13 +2469,13 @@ var AgentInitializer = class {
|
|
|
2372
2469
|
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
2373
2470
|
await symlink(workspaceDir, join10(this.instancesBaseDir, name), linkType);
|
|
2374
2471
|
}
|
|
2375
|
-
|
|
2472
|
+
logger15.info({ name, templateName, workspaceDir, customWorkDir: !!customWorkDir }, "Agent instance created");
|
|
2376
2473
|
return meta;
|
|
2377
2474
|
} catch (err) {
|
|
2378
2475
|
if (shouldCleanupOnError) {
|
|
2379
2476
|
await rm(workspaceDir, { recursive: true, force: true }).catch(() => {
|
|
2380
2477
|
});
|
|
2381
|
-
|
|
2478
|
+
logger15.debug({ workspaceDir }, "Cleaned up workspace after failed creation");
|
|
2382
2479
|
}
|
|
2383
2480
|
if (err instanceof ActantError) {
|
|
2384
2481
|
throw err;
|
|
@@ -2397,7 +2494,7 @@ var AgentInitializer = class {
|
|
|
2397
2494
|
if (await dirExists(workspaceDir)) {
|
|
2398
2495
|
try {
|
|
2399
2496
|
const meta2 = await readInstanceMeta(workspaceDir);
|
|
2400
|
-
|
|
2497
|
+
logger15.debug({ name }, "Existing instance found");
|
|
2401
2498
|
return { meta: meta2, created: false };
|
|
2402
2499
|
} catch (err) {
|
|
2403
2500
|
if (err instanceof InstanceCorruptedError3) {
|
|
@@ -2418,7 +2515,7 @@ var AgentInitializer = class {
|
|
|
2418
2515
|
async destroyInstance(name) {
|
|
2419
2516
|
const entryPath = join10(this.instancesBaseDir, name);
|
|
2420
2517
|
if (!await entryExists(entryPath)) {
|
|
2421
|
-
|
|
2518
|
+
logger15.warn({ name }, "Instance directory not found, nothing to destroy");
|
|
2422
2519
|
return;
|
|
2423
2520
|
}
|
|
2424
2521
|
if (await isSymlink(entryPath)) {
|
|
@@ -2428,13 +2525,30 @@ var AgentInitializer = class {
|
|
|
2428
2525
|
} catch {
|
|
2429
2526
|
}
|
|
2430
2527
|
await rm(entryPath, { recursive: true, force: true });
|
|
2431
|
-
|
|
2528
|
+
logger15.info({ name, targetDir }, "Symlinked agent instance unregistered");
|
|
2432
2529
|
} else {
|
|
2433
2530
|
await rm(entryPath, { recursive: true, force: true });
|
|
2434
|
-
|
|
2531
|
+
logger15.info({ name }, "Agent instance destroyed");
|
|
2435
2532
|
}
|
|
2436
2533
|
}
|
|
2437
2534
|
};
|
|
2535
|
+
function resolveProviderConfig(templateProvider) {
|
|
2536
|
+
if (templateProvider) {
|
|
2537
|
+
if (templateProvider.protocol) return templateProvider;
|
|
2538
|
+
const desc = modelProviderRegistry.get(templateProvider.type);
|
|
2539
|
+
return {
|
|
2540
|
+
...templateProvider,
|
|
2541
|
+
protocol: desc?.protocol ?? "custom"
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
const defaultDesc = modelProviderRegistry.getDefault();
|
|
2545
|
+
if (!defaultDesc) return void 0;
|
|
2546
|
+
return {
|
|
2547
|
+
type: defaultDesc.type,
|
|
2548
|
+
protocol: defaultDesc.protocol,
|
|
2549
|
+
baseUrl: defaultDesc.defaultBaseUrl
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2438
2552
|
async function dirExists(path) {
|
|
2439
2553
|
try {
|
|
2440
2554
|
await access2(path);
|
|
@@ -2463,8 +2577,8 @@ async function isSymlink(path) {
|
|
|
2463
2577
|
// src/initializer/context/context-materializer.ts
|
|
2464
2578
|
import { writeFile as writeFile7, mkdir as mkdir9 } from "fs/promises";
|
|
2465
2579
|
import { join as join11 } from "path";
|
|
2466
|
-
import { createLogger as
|
|
2467
|
-
var
|
|
2580
|
+
import { createLogger as createLogger17 } from "@actant/shared";
|
|
2581
|
+
var logger16 = createLogger17("context-materializer");
|
|
2468
2582
|
var BACKEND_CONFIG_DIR = {
|
|
2469
2583
|
cursor: ".cursor",
|
|
2470
2584
|
"cursor-agent": ".cursor",
|
|
@@ -2495,7 +2609,7 @@ var ContextMaterializer = class {
|
|
|
2495
2609
|
tasks.push(this.materializePrompts(workspaceDir, domainContext.prompts));
|
|
2496
2610
|
}
|
|
2497
2611
|
await Promise.all(tasks);
|
|
2498
|
-
|
|
2612
|
+
logger16.debug({ workspaceDir, backendType, configDir }, "Domain context materialized");
|
|
2499
2613
|
}
|
|
2500
2614
|
async materializeSkills(workspaceDir, skillNames) {
|
|
2501
2615
|
let content;
|
|
@@ -2610,16 +2724,16 @@ var InitializerStepExecutor = class {
|
|
|
2610
2724
|
};
|
|
2611
2725
|
|
|
2612
2726
|
// src/initializer/pipeline/step-registry.ts
|
|
2613
|
-
import { createLogger as
|
|
2614
|
-
var
|
|
2727
|
+
import { createLogger as createLogger18 } from "@actant/shared";
|
|
2728
|
+
var logger17 = createLogger18("step-registry");
|
|
2615
2729
|
var StepRegistry = class {
|
|
2616
2730
|
executors = /* @__PURE__ */ new Map();
|
|
2617
2731
|
register(executor) {
|
|
2618
2732
|
if (this.executors.has(executor.type)) {
|
|
2619
|
-
|
|
2733
|
+
logger17.warn({ type: executor.type }, "Overwriting existing step executor");
|
|
2620
2734
|
}
|
|
2621
2735
|
this.executors.set(executor.type, executor);
|
|
2622
|
-
|
|
2736
|
+
logger17.debug({ type: executor.type }, "Step executor registered");
|
|
2623
2737
|
}
|
|
2624
2738
|
get(type) {
|
|
2625
2739
|
return this.executors.get(type);
|
|
@@ -2934,13 +3048,15 @@ function createDefaultStepRegistry() {
|
|
|
2934
3048
|
|
|
2935
3049
|
// src/manager/agent-manager.ts
|
|
2936
3050
|
import { join as join17 } from "path";
|
|
3051
|
+
import { existsSync } from "fs";
|
|
2937
3052
|
import { rename as rename3, mkdir as mkdir11 } from "fs/promises";
|
|
2938
3053
|
import {
|
|
2939
3054
|
AgentNotFoundError,
|
|
2940
3055
|
AgentAlreadyRunningError,
|
|
2941
3056
|
AgentAlreadyAttachedError,
|
|
2942
3057
|
AgentNotAttachedError,
|
|
2943
|
-
|
|
3058
|
+
AgentLaunchError,
|
|
3059
|
+
createLogger as createLogger24
|
|
2944
3060
|
} from "@actant/shared";
|
|
2945
3061
|
|
|
2946
3062
|
// src/manager/launcher/backend-registry.ts
|
|
@@ -2973,6 +3089,14 @@ function requireMode(type, mode) {
|
|
|
2973
3089
|
function getPlatformCommand(cmd) {
|
|
2974
3090
|
return process.platform === "win32" ? cmd.win32 : cmd.default;
|
|
2975
3091
|
}
|
|
3092
|
+
var installHints = /* @__PURE__ */ new Map([
|
|
3093
|
+
["claude-code", "npm install -g @zed-industries/claude-agent-acp"],
|
|
3094
|
+
["cursor", "Install Cursor from https://cursor.com"],
|
|
3095
|
+
["cursor-agent", "Install Cursor from https://cursor.com"]
|
|
3096
|
+
]);
|
|
3097
|
+
function getInstallHint(type) {
|
|
3098
|
+
return installHints.get(type);
|
|
3099
|
+
}
|
|
2976
3100
|
|
|
2977
3101
|
// src/manager/launcher/builtin-backends.ts
|
|
2978
3102
|
function registerBuiltinBackends() {
|
|
@@ -2990,8 +3114,9 @@ function registerBuiltinBackends() {
|
|
|
2990
3114
|
});
|
|
2991
3115
|
registerBackend({
|
|
2992
3116
|
type: "claude-code",
|
|
2993
|
-
supportedModes: ["resolve", "acp"],
|
|
2994
|
-
resolveCommand: { win32: "claude-agent-acp.cmd", default: "claude-agent-acp" }
|
|
3117
|
+
supportedModes: ["resolve", "open", "acp"],
|
|
3118
|
+
resolveCommand: { win32: "claude-agent-acp.cmd", default: "claude-agent-acp" },
|
|
3119
|
+
openCommand: { win32: "claude.cmd", default: "claude" }
|
|
2995
3120
|
});
|
|
2996
3121
|
registerBackend({
|
|
2997
3122
|
type: "custom",
|
|
@@ -3064,7 +3189,7 @@ function resolveAcpBackend(backendType, workspaceDir, backendConfig) {
|
|
|
3064
3189
|
}
|
|
3065
3190
|
|
|
3066
3191
|
// src/manager/launcher/process-watcher.ts
|
|
3067
|
-
import { createLogger as
|
|
3192
|
+
import { createLogger as createLogger19 } from "@actant/shared";
|
|
3068
3193
|
|
|
3069
3194
|
// src/manager/launcher/process-utils.ts
|
|
3070
3195
|
function isProcessAlive(pid) {
|
|
@@ -3100,7 +3225,7 @@ function isNodeError5(err) {
|
|
|
3100
3225
|
}
|
|
3101
3226
|
|
|
3102
3227
|
// src/manager/launcher/process-watcher.ts
|
|
3103
|
-
var
|
|
3228
|
+
var logger18 = createLogger19("process-watcher");
|
|
3104
3229
|
var DEFAULT_POLL_INTERVAL = 5e3;
|
|
3105
3230
|
var ProcessWatcher = class {
|
|
3106
3231
|
constructor(onProcessExit, options) {
|
|
@@ -3113,12 +3238,12 @@ var ProcessWatcher = class {
|
|
|
3113
3238
|
pollIntervalMs;
|
|
3114
3239
|
watch(instanceName, pid) {
|
|
3115
3240
|
this.watches.set(instanceName, { pid });
|
|
3116
|
-
|
|
3241
|
+
logger18.debug({ instanceName, pid }, "Watching process");
|
|
3117
3242
|
}
|
|
3118
3243
|
unwatch(instanceName) {
|
|
3119
3244
|
const removed = this.watches.delete(instanceName);
|
|
3120
3245
|
if (removed) {
|
|
3121
|
-
|
|
3246
|
+
logger18.debug({ instanceName }, "Unwatched process");
|
|
3122
3247
|
}
|
|
3123
3248
|
return removed;
|
|
3124
3249
|
}
|
|
@@ -3133,13 +3258,13 @@ var ProcessWatcher = class {
|
|
|
3133
3258
|
this.timer = setInterval(() => {
|
|
3134
3259
|
void this.poll();
|
|
3135
3260
|
}, this.pollIntervalMs);
|
|
3136
|
-
|
|
3261
|
+
logger18.info({ pollIntervalMs: this.pollIntervalMs }, "ProcessWatcher started");
|
|
3137
3262
|
}
|
|
3138
3263
|
stop() {
|
|
3139
3264
|
if (this.timer) {
|
|
3140
3265
|
clearInterval(this.timer);
|
|
3141
3266
|
this.timer = null;
|
|
3142
|
-
|
|
3267
|
+
logger18.info("ProcessWatcher stopped");
|
|
3143
3268
|
}
|
|
3144
3269
|
}
|
|
3145
3270
|
dispose() {
|
|
@@ -3161,11 +3286,11 @@ var ProcessWatcher = class {
|
|
|
3161
3286
|
}
|
|
3162
3287
|
for (const info of exited) {
|
|
3163
3288
|
this.watches.delete(info.instanceName);
|
|
3164
|
-
|
|
3289
|
+
logger18.info(info, "Process exited \u2014 removed from watch list");
|
|
3165
3290
|
try {
|
|
3166
3291
|
await this.onProcessExit(info);
|
|
3167
3292
|
} catch (err) {
|
|
3168
|
-
|
|
3293
|
+
logger18.error({ ...info, error: err }, "Error in process exit handler");
|
|
3169
3294
|
}
|
|
3170
3295
|
}
|
|
3171
3296
|
} finally {
|
|
@@ -3175,8 +3300,8 @@ var ProcessWatcher = class {
|
|
|
3175
3300
|
};
|
|
3176
3301
|
|
|
3177
3302
|
// src/manager/launch-mode-handler.ts
|
|
3178
|
-
import { createLogger as
|
|
3179
|
-
var
|
|
3303
|
+
import { createLogger as createLogger20 } from "@actant/shared";
|
|
3304
|
+
var logger19 = createLogger20("launch-mode-handler");
|
|
3180
3305
|
var DirectModeHandler = class {
|
|
3181
3306
|
mode = "direct";
|
|
3182
3307
|
getProcessExitAction(_instanceName) {
|
|
@@ -3198,11 +3323,11 @@ var AcpBackgroundModeHandler = class {
|
|
|
3198
3323
|
var AcpServiceModeHandler = class {
|
|
3199
3324
|
mode = "acp-service";
|
|
3200
3325
|
getProcessExitAction(instanceName) {
|
|
3201
|
-
|
|
3326
|
+
logger19.info({ instanceName }, "acp-service process exited \u2014 restart policy will be checked");
|
|
3202
3327
|
return { type: "restart" };
|
|
3203
3328
|
}
|
|
3204
3329
|
getRecoveryAction(instanceName) {
|
|
3205
|
-
|
|
3330
|
+
logger19.info({ instanceName }, "acp-service stale instance \u2014 will attempt recovery restart");
|
|
3206
3331
|
return { type: "restart" };
|
|
3207
3332
|
}
|
|
3208
3333
|
};
|
|
@@ -3229,8 +3354,8 @@ function getLaunchModeHandler(mode) {
|
|
|
3229
3354
|
}
|
|
3230
3355
|
|
|
3231
3356
|
// src/manager/restart-tracker.ts
|
|
3232
|
-
import { createLogger as
|
|
3233
|
-
var
|
|
3357
|
+
import { createLogger as createLogger21 } from "@actant/shared";
|
|
3358
|
+
var logger20 = createLogger21("restart-tracker");
|
|
3234
3359
|
var DEFAULT_RESTART_POLICY = {
|
|
3235
3360
|
maxRestarts: 5,
|
|
3236
3361
|
backoffBaseMs: 1e3,
|
|
@@ -3248,12 +3373,12 @@ var RestartTracker = class {
|
|
|
3248
3373
|
if (state.lastStartAt > 0) {
|
|
3249
3374
|
const stableMs = Date.now() - state.lastStartAt;
|
|
3250
3375
|
if (stableMs >= this.policy.resetAfterMs) {
|
|
3251
|
-
|
|
3376
|
+
logger20.info({ instanceName, stableMs, resetAfterMs: this.policy.resetAfterMs }, "Resetting restart counter \u2014 agent was stable");
|
|
3252
3377
|
state.count = 0;
|
|
3253
3378
|
}
|
|
3254
3379
|
}
|
|
3255
3380
|
if (state.count >= this.policy.maxRestarts) {
|
|
3256
|
-
|
|
3381
|
+
logger20.warn({ instanceName, count: state.count, maxRestarts: this.policy.maxRestarts }, "Restart limit exceeded");
|
|
3257
3382
|
return { allowed: false, delayMs: 0, attempt: state.count };
|
|
3258
3383
|
}
|
|
3259
3384
|
const delayMs = Math.min(
|
|
@@ -3266,7 +3391,7 @@ var RestartTracker = class {
|
|
|
3266
3391
|
const state = this.getOrCreate(instanceName);
|
|
3267
3392
|
state.count++;
|
|
3268
3393
|
state.lastRestartAt = Date.now();
|
|
3269
|
-
|
|
3394
|
+
logger20.debug({ instanceName, count: state.count }, "Restart recorded");
|
|
3270
3395
|
}
|
|
3271
3396
|
recordStart(instanceName) {
|
|
3272
3397
|
const state = this.getOrCreate(instanceName);
|
|
@@ -3293,8 +3418,8 @@ var RestartTracker = class {
|
|
|
3293
3418
|
|
|
3294
3419
|
// src/communicator/claude-code-communicator.ts
|
|
3295
3420
|
import { spawn as spawn4 } from "child_process";
|
|
3296
|
-
import { createLogger as
|
|
3297
|
-
var
|
|
3421
|
+
import { createLogger as createLogger22 } from "@actant/shared";
|
|
3422
|
+
var logger21 = createLogger22("claude-code-communicator");
|
|
3298
3423
|
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
3299
3424
|
var ClaudeCodeCommunicator = class {
|
|
3300
3425
|
executable;
|
|
@@ -3303,7 +3428,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3303
3428
|
}
|
|
3304
3429
|
async runPrompt(workspaceDir, prompt, options) {
|
|
3305
3430
|
const args = this.buildArgs(prompt, options, "json");
|
|
3306
|
-
|
|
3431
|
+
logger21.debug({ workspaceDir, args }, "Running claude-code prompt");
|
|
3307
3432
|
return new Promise((resolve3, reject) => {
|
|
3308
3433
|
const child = spawn4(this.executable, args, {
|
|
3309
3434
|
cwd: workspaceDir,
|
|
@@ -3326,7 +3451,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3326
3451
|
child.on("close", (code) => {
|
|
3327
3452
|
clearTimeout(timer);
|
|
3328
3453
|
if (code !== 0) {
|
|
3329
|
-
|
|
3454
|
+
logger21.warn({ code, stderr: stderr.slice(0, 500) }, "claude-code exited with error");
|
|
3330
3455
|
reject(new Error(`claude-code exited with code ${code}: ${stderr.slice(0, 500)}`));
|
|
3331
3456
|
return;
|
|
3332
3457
|
}
|
|
@@ -3336,7 +3461,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3336
3461
|
const sessionId = parsed["session_id"];
|
|
3337
3462
|
const subtype = parsed["subtype"];
|
|
3338
3463
|
if (subtype && subtype !== "success") {
|
|
3339
|
-
|
|
3464
|
+
logger21.warn({ subtype }, "claude-code returned non-success subtype");
|
|
3340
3465
|
}
|
|
3341
3466
|
let text;
|
|
3342
3467
|
if (result && result.length > 0) {
|
|
@@ -3360,7 +3485,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3360
3485
|
}
|
|
3361
3486
|
async *streamPrompt(workspaceDir, prompt, options) {
|
|
3362
3487
|
const args = this.buildArgs(prompt, options, "stream-json");
|
|
3363
|
-
|
|
3488
|
+
logger21.debug({ workspaceDir, args }, "Streaming claude-code prompt");
|
|
3364
3489
|
const child = spawn4(this.executable, args, {
|
|
3365
3490
|
cwd: workspaceDir,
|
|
3366
3491
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3457,11 +3582,11 @@ function parseStreamEvent(event) {
|
|
|
3457
3582
|
}
|
|
3458
3583
|
|
|
3459
3584
|
// src/communicator/cursor-communicator.ts
|
|
3460
|
-
import { createLogger as
|
|
3461
|
-
var
|
|
3585
|
+
import { createLogger as createLogger23 } from "@actant/shared";
|
|
3586
|
+
var logger22 = createLogger23("cursor-communicator");
|
|
3462
3587
|
var CursorCommunicator = class {
|
|
3463
3588
|
async runPrompt(_workspaceDir, _prompt, _options) {
|
|
3464
|
-
|
|
3589
|
+
logger22.warn("Cursor backend does not yet support programmatic communication");
|
|
3465
3590
|
throw new Error(
|
|
3466
3591
|
"Cursor backend does not support programmatic communication yet. Use claude-code backend for agent.run / agent.chat functionality."
|
|
3467
3592
|
);
|
|
@@ -3500,7 +3625,7 @@ function createCommunicator(backendType, backendConfig) {
|
|
|
3500
3625
|
}
|
|
3501
3626
|
|
|
3502
3627
|
// src/manager/agent-manager.ts
|
|
3503
|
-
var
|
|
3628
|
+
var logger23 = createLogger24("agent-manager");
|
|
3504
3629
|
var AgentManager = class {
|
|
3505
3630
|
constructor(initializer, launcher, instancesBaseDir, options) {
|
|
3506
3631
|
this.initializer = initializer;
|
|
@@ -3541,7 +3666,7 @@ var AgentManager = class {
|
|
|
3541
3666
|
const dir = join17(this.instancesBaseDir, meta.name);
|
|
3542
3667
|
const fixed = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3543
3668
|
this.cache.set(meta.name, fixed);
|
|
3544
|
-
|
|
3669
|
+
logger23.info({ name: meta.name, oldStatus: meta.status, launchMode: meta.launchMode, recoveryAction: action.type }, "Stale status corrected");
|
|
3545
3670
|
if (action.type === "restart") {
|
|
3546
3671
|
pendingRestarts.push(meta.name);
|
|
3547
3672
|
}
|
|
@@ -3553,7 +3678,7 @@ var AgentManager = class {
|
|
|
3553
3678
|
await this.moveToCorrupted(name);
|
|
3554
3679
|
}
|
|
3555
3680
|
this.watcher.start();
|
|
3556
|
-
|
|
3681
|
+
logger23.info({
|
|
3557
3682
|
valid: valid.length,
|
|
3558
3683
|
corrupted: corrupted.length,
|
|
3559
3684
|
pendingRestarts: pendingRestarts.length
|
|
@@ -3561,9 +3686,9 @@ var AgentManager = class {
|
|
|
3561
3686
|
for (const name of pendingRestarts) {
|
|
3562
3687
|
try {
|
|
3563
3688
|
await this.startAgent(name);
|
|
3564
|
-
|
|
3689
|
+
logger23.info({ name }, "Recovery restart succeeded");
|
|
3565
3690
|
} catch (err) {
|
|
3566
|
-
|
|
3691
|
+
logger23.error({ name, error: err }, "Recovery restart failed");
|
|
3567
3692
|
}
|
|
3568
3693
|
}
|
|
3569
3694
|
}
|
|
@@ -3614,13 +3739,17 @@ var AgentManager = class {
|
|
|
3614
3739
|
}
|
|
3615
3740
|
if (this.acpManager) {
|
|
3616
3741
|
const { command, args } = resolveAcpBackend(meta.backendType, dir, meta.backendConfig);
|
|
3742
|
+
const providerEnv = buildProviderEnv(meta.providerConfig);
|
|
3617
3743
|
const connResult = await this.acpManager.connect(name, {
|
|
3618
3744
|
command,
|
|
3619
3745
|
args,
|
|
3620
3746
|
cwd: dir,
|
|
3621
|
-
connectionOptions: {
|
|
3747
|
+
connectionOptions: {
|
|
3748
|
+
autoApprove: true,
|
|
3749
|
+
...Object.keys(providerEnv).length > 0 ? { env: providerEnv } : {}
|
|
3750
|
+
}
|
|
3622
3751
|
});
|
|
3623
|
-
|
|
3752
|
+
logger23.info({ name, acpOnly, providerType: meta.providerConfig?.type }, "ACP connection established");
|
|
3624
3753
|
if (acpOnly && "pid" in connResult && typeof connResult.pid === "number") {
|
|
3625
3754
|
pid = connResult.pid;
|
|
3626
3755
|
this.processes.set(name, { pid, workspaceDir: dir, instanceName: name });
|
|
@@ -3632,7 +3761,7 @@ var AgentManager = class {
|
|
|
3632
3761
|
this.watcher.watch(name, pid);
|
|
3633
3762
|
}
|
|
3634
3763
|
this.restartTracker.recordStart(name);
|
|
3635
|
-
|
|
3764
|
+
logger23.info({ name, pid, launchMode: starting.launchMode, acp: true }, "Agent started");
|
|
3636
3765
|
} catch (err) {
|
|
3637
3766
|
if (this.acpManager?.has(name)) {
|
|
3638
3767
|
await this.acpManager.disconnect(name).catch(() => {
|
|
@@ -3640,7 +3769,11 @@ var AgentManager = class {
|
|
|
3640
3769
|
}
|
|
3641
3770
|
const errored = await updateInstanceMeta(dir, { status: "error" });
|
|
3642
3771
|
this.cache.set(name, errored);
|
|
3643
|
-
throw err;
|
|
3772
|
+
if (err instanceof AgentLaunchError) throw err;
|
|
3773
|
+
const spawnMsg = err instanceof Error ? err.message : String(err);
|
|
3774
|
+
throw new AgentLaunchError(name, new Error(
|
|
3775
|
+
isSpawnNotFound(spawnMsg) ? buildSpawnNotFoundMessage(meta.backendType) : spawnMsg
|
|
3776
|
+
));
|
|
3644
3777
|
}
|
|
3645
3778
|
}
|
|
3646
3779
|
/**
|
|
@@ -3651,7 +3784,7 @@ var AgentManager = class {
|
|
|
3651
3784
|
const meta = this.requireAgent(name);
|
|
3652
3785
|
const dir = join17(this.instancesBaseDir, name);
|
|
3653
3786
|
if (meta.status !== "running" && meta.status !== "starting") {
|
|
3654
|
-
|
|
3787
|
+
logger23.warn({ name, status: meta.status }, "Agent is not running, setting to stopped");
|
|
3655
3788
|
const stopped2 = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3656
3789
|
this.cache.set(name, stopped2);
|
|
3657
3790
|
return;
|
|
@@ -3661,7 +3794,7 @@ var AgentManager = class {
|
|
|
3661
3794
|
this.cache.set(name, stopping);
|
|
3662
3795
|
if (this.acpManager?.has(name)) {
|
|
3663
3796
|
await this.acpManager.disconnect(name).catch((err) => {
|
|
3664
|
-
|
|
3797
|
+
logger23.warn({ name, error: err }, "Error disconnecting ACP during stop");
|
|
3665
3798
|
});
|
|
3666
3799
|
}
|
|
3667
3800
|
const proc = this.processes.get(name);
|
|
@@ -3671,11 +3804,14 @@ var AgentManager = class {
|
|
|
3671
3804
|
}
|
|
3672
3805
|
const stopped = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3673
3806
|
this.cache.set(name, stopped);
|
|
3674
|
-
|
|
3807
|
+
logger23.info({ name }, "Agent stopped");
|
|
3675
3808
|
}
|
|
3676
3809
|
/** Destroy an agent — stop it if running, then remove workspace. */
|
|
3677
3810
|
async destroyAgent(name) {
|
|
3678
3811
|
const meta = this.cache.get(name);
|
|
3812
|
+
if (!meta && !existsSync(join17(this.instancesBaseDir, name))) {
|
|
3813
|
+
throw new AgentNotFoundError(name);
|
|
3814
|
+
}
|
|
3679
3815
|
if (meta && (meta.status === "running" || meta.status === "starting")) {
|
|
3680
3816
|
await this.stopAgent(name);
|
|
3681
3817
|
}
|
|
@@ -3684,7 +3820,7 @@ var AgentManager = class {
|
|
|
3684
3820
|
await this.initializer.destroyInstance(name);
|
|
3685
3821
|
this.cache.delete(name);
|
|
3686
3822
|
this.processes.delete(name);
|
|
3687
|
-
|
|
3823
|
+
logger23.info({ name }, "Agent destroyed");
|
|
3688
3824
|
}
|
|
3689
3825
|
/** Get agent metadata by name. */
|
|
3690
3826
|
getAgent(name) {
|
|
@@ -3749,6 +3885,17 @@ var AgentManager = class {
|
|
|
3749
3885
|
if (meta.status === "running" && meta.pid != null) {
|
|
3750
3886
|
throw new AgentAlreadyAttachedError(name);
|
|
3751
3887
|
}
|
|
3888
|
+
try {
|
|
3889
|
+
process.kill(pid, 0);
|
|
3890
|
+
} catch (err) {
|
|
3891
|
+
const code = err.code;
|
|
3892
|
+
if (code === "ESRCH") {
|
|
3893
|
+
throw new AgentLaunchError(
|
|
3894
|
+
name,
|
|
3895
|
+
new Error(`Process with PID ${pid} does not exist`)
|
|
3896
|
+
);
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3752
3899
|
const dir = join17(this.instancesBaseDir, name);
|
|
3753
3900
|
const mergedMetadata = attachMetadata ? { ...meta.metadata, ...attachMetadata } : meta.metadata;
|
|
3754
3901
|
const updated = await updateInstanceMeta(dir, {
|
|
@@ -3760,7 +3907,7 @@ var AgentManager = class {
|
|
|
3760
3907
|
this.cache.set(name, updated);
|
|
3761
3908
|
this.processes.set(name, { pid, workspaceDir: dir, instanceName: name });
|
|
3762
3909
|
this.watcher.watch(name, pid);
|
|
3763
|
-
|
|
3910
|
+
logger23.info({ name, pid }, "External process attached");
|
|
3764
3911
|
return updated;
|
|
3765
3912
|
}
|
|
3766
3913
|
/**
|
|
@@ -3783,7 +3930,7 @@ var AgentManager = class {
|
|
|
3783
3930
|
processOwnership: "managed"
|
|
3784
3931
|
});
|
|
3785
3932
|
this.cache.set(name, updated);
|
|
3786
|
-
|
|
3933
|
+
logger23.info({ name }, "External process detached");
|
|
3787
3934
|
let workspaceCleaned = false;
|
|
3788
3935
|
if (options?.cleanup && meta.workspacePolicy === "ephemeral") {
|
|
3789
3936
|
await this.destroyAgent(name);
|
|
@@ -3801,7 +3948,7 @@ var AgentManager = class {
|
|
|
3801
3948
|
const conn = this.acpManager.getConnection(name);
|
|
3802
3949
|
const sessionId = this.acpManager.getPrimarySessionId(name);
|
|
3803
3950
|
if (conn && sessionId) {
|
|
3804
|
-
|
|
3951
|
+
logger23.debug({ name, sessionId }, "Sending prompt via ACP");
|
|
3805
3952
|
const result = await conn.prompt(sessionId, prompt);
|
|
3806
3953
|
return { text: result.text, sessionId };
|
|
3807
3954
|
}
|
|
@@ -3820,7 +3967,7 @@ var AgentManager = class {
|
|
|
3820
3967
|
const conn = this.acpManager.getConnection(name);
|
|
3821
3968
|
const sessionId = this.acpManager.getPrimarySessionId(name);
|
|
3822
3969
|
if (conn && sessionId) {
|
|
3823
|
-
|
|
3970
|
+
logger23.debug({ name, sessionId }, "Streaming prompt via ACP");
|
|
3824
3971
|
return this.streamFromAcp(conn, sessionId, prompt);
|
|
3825
3972
|
}
|
|
3826
3973
|
}
|
|
@@ -3892,7 +4039,7 @@ var AgentManager = class {
|
|
|
3892
4039
|
}
|
|
3893
4040
|
const handler = getLaunchModeHandler(meta.launchMode);
|
|
3894
4041
|
const action = handler.getProcessExitAction(instanceName, meta);
|
|
3895
|
-
|
|
4042
|
+
logger23.warn({ instanceName, pid, launchMode: meta.launchMode, action: action.type, previousStatus: meta.status }, "Agent process exited unexpectedly");
|
|
3896
4043
|
if (this.acpManager?.has(instanceName)) {
|
|
3897
4044
|
await this.acpManager.disconnect(instanceName).catch(() => {
|
|
3898
4045
|
});
|
|
@@ -3913,28 +4060,28 @@ var AgentManager = class {
|
|
|
3913
4060
|
if (!decision.allowed) {
|
|
3914
4061
|
const errored = await updateInstanceMeta(dir, { status: "error" });
|
|
3915
4062
|
this.cache.set(instanceName, errored);
|
|
3916
|
-
|
|
4063
|
+
logger23.error({ instanceName, attempt: decision.attempt }, "Restart limit exceeded \u2014 marking as error");
|
|
3917
4064
|
break;
|
|
3918
4065
|
}
|
|
3919
|
-
|
|
4066
|
+
logger23.info({ instanceName, attempt: decision.attempt, delayMs: decision.delayMs }, "Scheduling crash restart with backoff");
|
|
3920
4067
|
if (decision.delayMs > 0) {
|
|
3921
4068
|
await delay(decision.delayMs);
|
|
3922
4069
|
}
|
|
3923
4070
|
this.restartTracker.recordRestart(instanceName);
|
|
3924
4071
|
try {
|
|
3925
4072
|
await this.startAgent(instanceName);
|
|
3926
|
-
|
|
4073
|
+
logger23.info({ instanceName, attempt: decision.attempt }, "Crash restart succeeded");
|
|
3927
4074
|
} catch (err) {
|
|
3928
|
-
|
|
4075
|
+
logger23.error({ instanceName, attempt: decision.attempt, error: err }, "Crash restart failed");
|
|
3929
4076
|
}
|
|
3930
4077
|
break;
|
|
3931
4078
|
}
|
|
3932
4079
|
case "destroy":
|
|
3933
4080
|
try {
|
|
3934
4081
|
await this.destroyAgent(instanceName);
|
|
3935
|
-
|
|
4082
|
+
logger23.info({ instanceName }, "One-shot agent destroyed after exit");
|
|
3936
4083
|
} catch (err) {
|
|
3937
|
-
|
|
4084
|
+
logger23.error({ instanceName, error: err }, "One-shot auto-destroy failed");
|
|
3938
4085
|
}
|
|
3939
4086
|
break;
|
|
3940
4087
|
case "mark-stopped":
|
|
@@ -3954,12 +4101,39 @@ var AgentManager = class {
|
|
|
3954
4101
|
const src = join17(this.instancesBaseDir, name);
|
|
3955
4102
|
const dest = join17(this.corruptedDir, `${name}-${Date.now()}`);
|
|
3956
4103
|
await rename3(src, dest);
|
|
3957
|
-
|
|
4104
|
+
logger23.warn({ name, dest }, "Corrupted instance moved");
|
|
3958
4105
|
} catch (err) {
|
|
3959
|
-
|
|
4106
|
+
logger23.error({ name, error: err }, "Failed to move corrupted instance");
|
|
3960
4107
|
}
|
|
3961
4108
|
}
|
|
3962
4109
|
};
|
|
4110
|
+
function buildProviderEnv(providerConfig) {
|
|
4111
|
+
const env = {};
|
|
4112
|
+
const defaultDesc = modelProviderRegistry.getDefault();
|
|
4113
|
+
const providerType = providerConfig?.type ?? defaultDesc?.type;
|
|
4114
|
+
if (providerType) {
|
|
4115
|
+
env["ACTANT_PROVIDER"] = providerType;
|
|
4116
|
+
}
|
|
4117
|
+
const descriptor = providerType ? modelProviderRegistry.get(providerType) : defaultDesc;
|
|
4118
|
+
const apiKey = descriptor?.apiKey ?? process.env["ACTANT_API_KEY"];
|
|
4119
|
+
if (apiKey) {
|
|
4120
|
+
env["ACTANT_API_KEY"] = apiKey;
|
|
4121
|
+
}
|
|
4122
|
+
const baseUrl = providerConfig?.baseUrl ?? descriptor?.defaultBaseUrl;
|
|
4123
|
+
if (baseUrl) {
|
|
4124
|
+
env["ACTANT_BASE_URL"] = baseUrl;
|
|
4125
|
+
}
|
|
4126
|
+
return env;
|
|
4127
|
+
}
|
|
4128
|
+
function isSpawnNotFound(msg) {
|
|
4129
|
+
return /ENOENT|EINVAL|is not recognized|not found/i.test(msg);
|
|
4130
|
+
}
|
|
4131
|
+
function buildSpawnNotFoundMessage(backendType) {
|
|
4132
|
+
const hint = getInstallHint(backendType);
|
|
4133
|
+
const base = `Backend "${backendType}" executable not found.`;
|
|
4134
|
+
return hint ? `${base}
|
|
4135
|
+
Install with: ${hint}` : `${base} Ensure the required CLI is installed and in your PATH.`;
|
|
4136
|
+
}
|
|
3963
4137
|
|
|
3964
4138
|
// src/manager/launcher/mock-launcher.ts
|
|
3965
4139
|
var nextPid = 1e4;
|
|
@@ -3982,14 +4156,14 @@ var MockLauncher = class {
|
|
|
3982
4156
|
|
|
3983
4157
|
// src/manager/launcher/process-launcher.ts
|
|
3984
4158
|
import { spawn as spawn5 } from "child_process";
|
|
3985
|
-
import { AgentLaunchError, createLogger as
|
|
4159
|
+
import { AgentLaunchError as AgentLaunchError2, createLogger as createLogger26 } from "@actant/shared";
|
|
3986
4160
|
|
|
3987
4161
|
// src/manager/launcher/process-log-writer.ts
|
|
3988
4162
|
import { createWriteStream } from "fs";
|
|
3989
4163
|
import { mkdir as mkdir12, rename as rename4, stat as stat7, readFile as readFile5 } from "fs/promises";
|
|
3990
4164
|
import { join as join18 } from "path";
|
|
3991
|
-
import { createLogger as
|
|
3992
|
-
var
|
|
4165
|
+
import { createLogger as createLogger25 } from "@actant/shared";
|
|
4166
|
+
var logger24 = createLogger25("process-log-writer");
|
|
3993
4167
|
var DEFAULT_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
3994
4168
|
var DEFAULT_MAX_FILES = 3;
|
|
3995
4169
|
var ProcessLogWriter = class {
|
|
@@ -4026,7 +4200,7 @@ var ProcessLogWriter = class {
|
|
|
4026
4200
|
} catch {
|
|
4027
4201
|
this.stderrBytes = 0;
|
|
4028
4202
|
}
|
|
4029
|
-
|
|
4203
|
+
logger24.debug({ logsDir: this.logsDir }, "Log writer initialized");
|
|
4030
4204
|
}
|
|
4031
4205
|
/**
|
|
4032
4206
|
* Attach to readable streams from a spawned process.
|
|
@@ -4108,7 +4282,7 @@ var ProcessLogWriter = class {
|
|
|
4108
4282
|
this.stderrStream = newStream;
|
|
4109
4283
|
this.stderrBytes = 0;
|
|
4110
4284
|
}
|
|
4111
|
-
|
|
4285
|
+
logger24.debug({ filename }, "Log file rotated");
|
|
4112
4286
|
}
|
|
4113
4287
|
closeStream(stream) {
|
|
4114
4288
|
if (!stream) return Promise.resolve();
|
|
@@ -4119,7 +4293,7 @@ var ProcessLogWriter = class {
|
|
|
4119
4293
|
};
|
|
4120
4294
|
|
|
4121
4295
|
// src/manager/launcher/process-launcher.ts
|
|
4122
|
-
var
|
|
4296
|
+
var logger25 = createLogger26("process-launcher");
|
|
4123
4297
|
var DEFAULT_TERMINATE_TIMEOUT = 5e3;
|
|
4124
4298
|
var DEFAULT_SPAWN_VERIFY_DELAY = 500;
|
|
4125
4299
|
var ProcessLauncher = class {
|
|
@@ -4141,7 +4315,7 @@ var ProcessLauncher = class {
|
|
|
4141
4315
|
meta.backendConfig
|
|
4142
4316
|
);
|
|
4143
4317
|
const useAcp = isAcpBackend(meta.backendType);
|
|
4144
|
-
|
|
4318
|
+
logger25.info({ name: meta.name, command, args, backendType: meta.backendType, acp: useAcp }, "Spawning backend process");
|
|
4145
4319
|
const captureNonAcpLogs = !useAcp && this.enableProcessLogs;
|
|
4146
4320
|
let stdio;
|
|
4147
4321
|
if (useAcp) {
|
|
@@ -4173,7 +4347,7 @@ var ProcessLauncher = class {
|
|
|
4173
4347
|
}
|
|
4174
4348
|
});
|
|
4175
4349
|
if ("error" in spawnResult) {
|
|
4176
|
-
throw new
|
|
4350
|
+
throw new AgentLaunchError2(meta.name, spawnResult.error);
|
|
4177
4351
|
}
|
|
4178
4352
|
const pid = spawnResult.pid;
|
|
4179
4353
|
if (!useAcp) {
|
|
@@ -4184,27 +4358,27 @@ var ProcessLauncher = class {
|
|
|
4184
4358
|
earlyExit = true;
|
|
4185
4359
|
});
|
|
4186
4360
|
child.on("error", (err) => {
|
|
4187
|
-
|
|
4361
|
+
logger25.error({ name: meta.name, pid, error: err }, "Backend process error after spawn");
|
|
4188
4362
|
});
|
|
4189
4363
|
if (this.spawnVerifyDelayMs > 0) {
|
|
4190
4364
|
await delay(this.spawnVerifyDelayMs);
|
|
4191
4365
|
if (earlyExit || !isProcessAlive(pid)) {
|
|
4192
|
-
throw new
|
|
4366
|
+
throw new AgentLaunchError2(
|
|
4193
4367
|
meta.name,
|
|
4194
4368
|
new Error(`Process exited immediately after spawn (pid=${pid}, command=${command})`)
|
|
4195
4369
|
);
|
|
4196
4370
|
}
|
|
4197
4371
|
}
|
|
4198
|
-
|
|
4372
|
+
logger25.info({ name: meta.name, pid, command, acp: useAcp }, "Backend process spawned");
|
|
4199
4373
|
if (captureNonAcpLogs && child.stdout && child.stderr) {
|
|
4200
4374
|
const logWriter = new ProcessLogWriter(workspaceDir, this.logWriterOptions);
|
|
4201
4375
|
try {
|
|
4202
4376
|
await logWriter.initialize();
|
|
4203
4377
|
logWriter.attach(child.stdout, child.stderr);
|
|
4204
4378
|
this.logWriters.set(meta.name, logWriter);
|
|
4205
|
-
|
|
4379
|
+
logger25.debug({ name: meta.name }, "Process log capture enabled");
|
|
4206
4380
|
} catch (err) {
|
|
4207
|
-
|
|
4381
|
+
logger25.warn({ name: meta.name, error: err }, "Failed to initialize log writer, continuing without log capture");
|
|
4208
4382
|
}
|
|
4209
4383
|
}
|
|
4210
4384
|
const result = {
|
|
@@ -4233,27 +4407,27 @@ var ProcessLauncher = class {
|
|
|
4233
4407
|
this.logWriters.delete(instanceName);
|
|
4234
4408
|
}
|
|
4235
4409
|
if (!isProcessAlive(pid)) {
|
|
4236
|
-
|
|
4410
|
+
logger25.info({ instanceName, pid }, "Process already exited");
|
|
4237
4411
|
return;
|
|
4238
4412
|
}
|
|
4239
|
-
|
|
4413
|
+
logger25.info({ instanceName, pid }, "Sending SIGTERM");
|
|
4240
4414
|
sendSignal(pid, "SIGTERM");
|
|
4241
4415
|
const deadline = Date.now() + this.terminateTimeoutMs;
|
|
4242
4416
|
const pollInterval = 200;
|
|
4243
4417
|
while (Date.now() < deadline) {
|
|
4244
4418
|
await delay(pollInterval);
|
|
4245
4419
|
if (!isProcessAlive(pid)) {
|
|
4246
|
-
|
|
4420
|
+
logger25.info({ instanceName, pid }, "Process terminated gracefully");
|
|
4247
4421
|
return;
|
|
4248
4422
|
}
|
|
4249
4423
|
}
|
|
4250
|
-
|
|
4424
|
+
logger25.warn({ instanceName, pid }, "Process did not exit after SIGTERM, sending SIGKILL");
|
|
4251
4425
|
sendSignal(pid, "SIGKILL");
|
|
4252
4426
|
await delay(500);
|
|
4253
4427
|
if (isProcessAlive(pid)) {
|
|
4254
|
-
|
|
4428
|
+
logger25.error({ instanceName, pid }, "Process still alive after SIGKILL");
|
|
4255
4429
|
} else {
|
|
4256
|
-
|
|
4430
|
+
logger25.info({ instanceName, pid }, "Process killed with SIGKILL");
|
|
4257
4431
|
}
|
|
4258
4432
|
}
|
|
4259
4433
|
};
|
|
@@ -4275,7 +4449,10 @@ var SkillDefinitionSchema = z4.object({
|
|
|
4275
4449
|
name: z4.string().min(1),
|
|
4276
4450
|
description: z4.string().optional(),
|
|
4277
4451
|
content: z4.string().min(1),
|
|
4278
|
-
tags: z4.array(z4.string()).optional()
|
|
4452
|
+
tags: z4.array(z4.string()).optional(),
|
|
4453
|
+
license: z4.string().optional(),
|
|
4454
|
+
compatibility: z4.string().optional(),
|
|
4455
|
+
allowedTools: z4.array(z4.string()).optional()
|
|
4279
4456
|
}).passthrough();
|
|
4280
4457
|
var SkillManager = class extends BaseComponentManager {
|
|
4281
4458
|
componentType = "Skill";
|
|
@@ -4503,8 +4680,8 @@ var PluginManager = class extends BaseComponentManager {
|
|
|
4503
4680
|
};
|
|
4504
4681
|
|
|
4505
4682
|
// src/permissions/permission-policy-enforcer.ts
|
|
4506
|
-
import { createLogger as
|
|
4507
|
-
var
|
|
4683
|
+
import { createLogger as createLogger27 } from "@actant/shared";
|
|
4684
|
+
var logger26 = createLogger27("permission-policy-enforcer");
|
|
4508
4685
|
var KIND_TO_TOOL = {
|
|
4509
4686
|
read: "Read",
|
|
4510
4687
|
edit: "Edit",
|
|
@@ -4521,7 +4698,7 @@ var PermissionPolicyEnforcer = class {
|
|
|
4521
4698
|
}
|
|
4522
4699
|
updateConfig(config) {
|
|
4523
4700
|
this.config = { ...config };
|
|
4524
|
-
|
|
4701
|
+
logger26.info("Permission policy config updated");
|
|
4525
4702
|
}
|
|
4526
4703
|
getConfig() {
|
|
4527
4704
|
return this.config;
|
|
@@ -4679,8 +4856,8 @@ function escapeRegex(str) {
|
|
|
4679
4856
|
}
|
|
4680
4857
|
|
|
4681
4858
|
// src/permissions/permission-audit.ts
|
|
4682
|
-
import { createLogger as
|
|
4683
|
-
var
|
|
4859
|
+
import { createLogger as createLogger28 } from "@actant/shared";
|
|
4860
|
+
var logger27 = createLogger28("permission-audit");
|
|
4684
4861
|
var PermissionAuditLogger = class {
|
|
4685
4862
|
instanceName;
|
|
4686
4863
|
constructor(instanceName) {
|
|
@@ -4693,7 +4870,7 @@ var PermissionAuditLogger = class {
|
|
|
4693
4870
|
...detail,
|
|
4694
4871
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4695
4872
|
};
|
|
4696
|
-
|
|
4873
|
+
logger27.info(entry, `[audit] ${event}`);
|
|
4697
4874
|
}
|
|
4698
4875
|
logEvaluation(toolCall, decision) {
|
|
4699
4876
|
this.log("permission.evaluated", { toolCall, decision });
|
|
@@ -4714,8 +4891,8 @@ var PermissionAuditLogger = class {
|
|
|
4714
4891
|
|
|
4715
4892
|
// src/session/session-registry.ts
|
|
4716
4893
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
4717
|
-
import { createLogger as
|
|
4718
|
-
var
|
|
4894
|
+
import { createLogger as createLogger29 } from "@actant/shared";
|
|
4895
|
+
var logger28 = createLogger29("session-registry");
|
|
4719
4896
|
var DEFAULT_IDLE_TTL_MS = 30 * 60 * 1e3;
|
|
4720
4897
|
var TTL_CHECK_INTERVAL_MS = 60 * 1e3;
|
|
4721
4898
|
var SessionRegistry = class {
|
|
@@ -4746,7 +4923,7 @@ var SessionRegistry = class {
|
|
|
4746
4923
|
idleTtlMs: opts.idleTtlMs ?? this.defaultIdleTtlMs
|
|
4747
4924
|
};
|
|
4748
4925
|
this.sessions.set(session.sessionId, session);
|
|
4749
|
-
|
|
4926
|
+
logger28.info({ sessionId: session.sessionId, agentName: opts.agentName, clientId: opts.clientId }, "Session created");
|
|
4750
4927
|
return session;
|
|
4751
4928
|
}
|
|
4752
4929
|
/** Get a session by ID, or undefined if not found. */
|
|
@@ -4778,7 +4955,7 @@ var SessionRegistry = class {
|
|
|
4778
4955
|
session.clientId = null;
|
|
4779
4956
|
session.state = "idle";
|
|
4780
4957
|
session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4781
|
-
|
|
4958
|
+
logger28.info({ sessionId, agentName: session.agentName }, "Session released to idle");
|
|
4782
4959
|
}
|
|
4783
4960
|
/**
|
|
4784
4961
|
* Resume an idle session, binding it to a (potentially new) client.
|
|
@@ -4791,7 +4968,7 @@ var SessionRegistry = class {
|
|
|
4791
4968
|
session.clientId = clientId;
|
|
4792
4969
|
session.state = "active";
|
|
4793
4970
|
session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4794
|
-
|
|
4971
|
+
logger28.info({ sessionId, clientId, agentName: session.agentName }, "Session resumed");
|
|
4795
4972
|
return true;
|
|
4796
4973
|
}
|
|
4797
4974
|
/** Explicitly close and remove a session. */
|
|
@@ -4799,7 +4976,7 @@ var SessionRegistry = class {
|
|
|
4799
4976
|
const session = this.sessions.get(sessionId);
|
|
4800
4977
|
if (!session) return false;
|
|
4801
4978
|
this.sessions.delete(sessionId);
|
|
4802
|
-
|
|
4979
|
+
logger28.info({ sessionId, agentName: session.agentName }, "Session closed");
|
|
4803
4980
|
return true;
|
|
4804
4981
|
}
|
|
4805
4982
|
/** Close all sessions for a given agent. */
|
|
@@ -4812,7 +4989,7 @@ var SessionRegistry = class {
|
|
|
4812
4989
|
}
|
|
4813
4990
|
}
|
|
4814
4991
|
if (count > 0) {
|
|
4815
|
-
|
|
4992
|
+
logger28.info({ agentName, count }, "Sessions closed for agent");
|
|
4816
4993
|
}
|
|
4817
4994
|
return count;
|
|
4818
4995
|
}
|
|
@@ -4836,7 +5013,7 @@ var SessionRegistry = class {
|
|
|
4836
5013
|
if (now - lastActivity > session.idleTtlMs) {
|
|
4837
5014
|
session.state = "expired";
|
|
4838
5015
|
this.sessions.delete(id);
|
|
4839
|
-
|
|
5016
|
+
logger28.info({ sessionId: id, agentName: session.agentName, idleMs: now - lastActivity }, "Session expired (TTL)");
|
|
4840
5017
|
this.onExpireCallback?.(session);
|
|
4841
5018
|
}
|
|
4842
5019
|
}
|
|
@@ -4848,13 +5025,13 @@ import { join as join20 } from "path";
|
|
|
4848
5025
|
import { mkdir as mkdir13, rm as rm5 } from "fs/promises";
|
|
4849
5026
|
import { execFile } from "child_process";
|
|
4850
5027
|
import { promisify } from "util";
|
|
4851
|
-
import { createLogger as
|
|
5028
|
+
import { createLogger as createLogger31 } from "@actant/shared";
|
|
4852
5029
|
|
|
4853
5030
|
// src/source/local-source.ts
|
|
4854
5031
|
import { readFile as readFile6, readdir as readdir5, stat as stat8 } from "fs/promises";
|
|
4855
5032
|
import { join as join19, extname as extname3 } from "path";
|
|
4856
|
-
import { createLogger as
|
|
4857
|
-
var
|
|
5033
|
+
import { createLogger as createLogger30 } from "@actant/shared";
|
|
5034
|
+
var logger29 = createLogger30("local-source");
|
|
4858
5035
|
var LocalSource = class {
|
|
4859
5036
|
type = "local";
|
|
4860
5037
|
packageName;
|
|
@@ -4863,6 +5040,9 @@ var LocalSource = class {
|
|
|
4863
5040
|
this.packageName = packageName;
|
|
4864
5041
|
this.config = config;
|
|
4865
5042
|
}
|
|
5043
|
+
getRootDir() {
|
|
5044
|
+
return this.config.path;
|
|
5045
|
+
}
|
|
4866
5046
|
async fetch() {
|
|
4867
5047
|
return this.loadPackage();
|
|
4868
5048
|
}
|
|
@@ -4882,7 +5062,7 @@ var LocalSource = class {
|
|
|
4882
5062
|
this.loadPresets(rootDir, manifest.presets),
|
|
4883
5063
|
this.loadJsonDir(rootDir, manifest.components?.templates, "templates")
|
|
4884
5064
|
]);
|
|
4885
|
-
|
|
5065
|
+
logger29.info({ packageName: this.packageName, rootDir }, "Local package loaded");
|
|
4886
5066
|
return { manifest, skills, prompts, mcpServers, workflows, presets, templates };
|
|
4887
5067
|
}
|
|
4888
5068
|
async loadManifest(rootDir) {
|
|
@@ -4891,7 +5071,7 @@ var LocalSource = class {
|
|
|
4891
5071
|
const raw = await readFile6(manifestPath, "utf-8");
|
|
4892
5072
|
return JSON.parse(raw);
|
|
4893
5073
|
} catch {
|
|
4894
|
-
|
|
5074
|
+
logger29.debug({ rootDir }, "No actant.json found, scanning directories");
|
|
4895
5075
|
return { name: this.packageName };
|
|
4896
5076
|
}
|
|
4897
5077
|
}
|
|
@@ -4903,7 +5083,7 @@ var LocalSource = class {
|
|
|
4903
5083
|
const raw = await readFile6(join19(rootDir, relPath), "utf-8");
|
|
4904
5084
|
items2.push(JSON.parse(raw));
|
|
4905
5085
|
} catch (err) {
|
|
4906
|
-
|
|
5086
|
+
logger29.warn({ file: relPath, error: err }, `Failed to load from manifest, skipping`);
|
|
4907
5087
|
}
|
|
4908
5088
|
}
|
|
4909
5089
|
return items2;
|
|
@@ -4926,7 +5106,7 @@ var LocalSource = class {
|
|
|
4926
5106
|
const raw = await readFile6(fullPath, "utf-8");
|
|
4927
5107
|
items.push(JSON.parse(raw));
|
|
4928
5108
|
} catch (err) {
|
|
4929
|
-
|
|
5109
|
+
logger29.warn({ file, error: err }, `Failed to load ${subDir} file, skipping`);
|
|
4930
5110
|
}
|
|
4931
5111
|
}
|
|
4932
5112
|
for (const entry of entries) {
|
|
@@ -4951,7 +5131,7 @@ var LocalSource = class {
|
|
|
4951
5131
|
}
|
|
4952
5132
|
}
|
|
4953
5133
|
if (subDir === "skills") {
|
|
4954
|
-
const { parseSkillMd } = await import("./skill-md-parser-
|
|
5134
|
+
const { parseSkillMd } = await import("./skill-md-parser-HXLTZAUU.js");
|
|
4955
5135
|
for (const entry of entries) {
|
|
4956
5136
|
if (extname3(entry) === ".json") continue;
|
|
4957
5137
|
const subDirPath = join19(dirPath, entry);
|
|
@@ -4976,7 +5156,7 @@ var LocalSource = class {
|
|
|
4976
5156
|
|
|
4977
5157
|
// src/source/github-source.ts
|
|
4978
5158
|
var execFileAsync = promisify(execFile);
|
|
4979
|
-
var
|
|
5159
|
+
var logger30 = createLogger31("github-source");
|
|
4980
5160
|
var GitHubSource = class {
|
|
4981
5161
|
type = "github";
|
|
4982
5162
|
packageName;
|
|
@@ -4987,6 +5167,9 @@ var GitHubSource = class {
|
|
|
4987
5167
|
this.config = config;
|
|
4988
5168
|
this.cacheDir = join20(cacheDir, packageName);
|
|
4989
5169
|
}
|
|
5170
|
+
getRootDir() {
|
|
5171
|
+
return this.cacheDir;
|
|
5172
|
+
}
|
|
4990
5173
|
async fetch() {
|
|
4991
5174
|
await this.clone();
|
|
4992
5175
|
return this.readCached();
|
|
@@ -4995,21 +5178,21 @@ var GitHubSource = class {
|
|
|
4995
5178
|
try {
|
|
4996
5179
|
await this.pull();
|
|
4997
5180
|
} catch {
|
|
4998
|
-
|
|
5181
|
+
logger30.info("Pull failed, re-cloning");
|
|
4999
5182
|
await this.clone();
|
|
5000
5183
|
}
|
|
5001
5184
|
return this.readCached();
|
|
5002
5185
|
}
|
|
5003
5186
|
async dispose() {
|
|
5004
5187
|
await rm5(this.cacheDir, { recursive: true, force: true });
|
|
5005
|
-
|
|
5188
|
+
logger30.debug({ cacheDir: this.cacheDir }, "Cache cleaned");
|
|
5006
5189
|
}
|
|
5007
5190
|
async clone() {
|
|
5008
5191
|
await rm5(this.cacheDir, { recursive: true, force: true });
|
|
5009
5192
|
await mkdir13(this.cacheDir, { recursive: true });
|
|
5010
5193
|
const branch = this.config.branch ?? "main";
|
|
5011
5194
|
const args = ["clone", "--depth", "1", "--branch", branch, this.config.url, this.cacheDir];
|
|
5012
|
-
|
|
5195
|
+
logger30.info({ url: this.config.url, branch }, "Cloning repository");
|
|
5013
5196
|
await execFileAsync("git", args, { timeout: 6e4 });
|
|
5014
5197
|
}
|
|
5015
5198
|
async pull() {
|
|
@@ -5027,7 +5210,7 @@ var GitHubSource = class {
|
|
|
5027
5210
|
// src/source/source-manager.ts
|
|
5028
5211
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir14 } from "fs/promises";
|
|
5029
5212
|
import { join as join21 } from "path";
|
|
5030
|
-
import { createLogger as
|
|
5213
|
+
import { createLogger as createLogger32 } from "@actant/shared";
|
|
5031
5214
|
|
|
5032
5215
|
// src/version/sync-report.ts
|
|
5033
5216
|
function createEmptySyncReport() {
|
|
@@ -5052,7 +5235,7 @@ var DEFAULT_SOURCE_CONFIG = {
|
|
|
5052
5235
|
url: "https://github.com/blackplume233/actant-hub.git",
|
|
5053
5236
|
branch: "main"
|
|
5054
5237
|
};
|
|
5055
|
-
var
|
|
5238
|
+
var logger31 = createLogger32("source-manager");
|
|
5056
5239
|
var SourceManager = class {
|
|
5057
5240
|
sources = /* @__PURE__ */ new Map();
|
|
5058
5241
|
presets = /* @__PURE__ */ new Map();
|
|
@@ -5080,7 +5263,7 @@ var SourceManager = class {
|
|
|
5080
5263
|
this.sources.set(name, source);
|
|
5081
5264
|
this.injectComponents(name, result);
|
|
5082
5265
|
await this.persistSources();
|
|
5083
|
-
|
|
5266
|
+
logger31.info({ name, type: config.type }, "Source added");
|
|
5084
5267
|
return result;
|
|
5085
5268
|
}
|
|
5086
5269
|
async syncSource(name) {
|
|
@@ -5096,7 +5279,7 @@ var SourceManager = class {
|
|
|
5096
5279
|
const newSnapshot = this.snapshotComponents(name);
|
|
5097
5280
|
await this.persistSources();
|
|
5098
5281
|
const report = this.buildSyncReport(oldSnapshot, newSnapshot);
|
|
5099
|
-
|
|
5282
|
+
logger31.info({ name, report }, "Source synced");
|
|
5100
5283
|
return { fetchResult: result, report };
|
|
5101
5284
|
}
|
|
5102
5285
|
async syncAll() {
|
|
@@ -5109,7 +5292,7 @@ var SourceManager = class {
|
|
|
5109
5292
|
const { report } = await this.syncSourceWithReport(name);
|
|
5110
5293
|
reports.push(report);
|
|
5111
5294
|
} catch (err) {
|
|
5112
|
-
|
|
5295
|
+
logger31.warn({ name, error: err }, "Failed to sync source, skipping");
|
|
5113
5296
|
}
|
|
5114
5297
|
}
|
|
5115
5298
|
return { report: mergeSyncReports(reports) };
|
|
@@ -5122,7 +5305,7 @@ var SourceManager = class {
|
|
|
5122
5305
|
await source.dispose();
|
|
5123
5306
|
this.sources.delete(name);
|
|
5124
5307
|
await this.persistSources();
|
|
5125
|
-
|
|
5308
|
+
logger31.info({ name }, "Source removed");
|
|
5126
5309
|
return true;
|
|
5127
5310
|
}
|
|
5128
5311
|
listSources() {
|
|
@@ -5135,6 +5318,10 @@ var SourceManager = class {
|
|
|
5135
5318
|
hasSource(name) {
|
|
5136
5319
|
return this.sources.has(name);
|
|
5137
5320
|
}
|
|
5321
|
+
/** Resolve a registered source name to its filesystem root directory. */
|
|
5322
|
+
getSourceRootDir(name) {
|
|
5323
|
+
return this.getSourceOrThrow(name).getRootDir();
|
|
5324
|
+
}
|
|
5138
5325
|
// ---------------------------------------------------------------------------
|
|
5139
5326
|
// Preset operations
|
|
5140
5327
|
// ---------------------------------------------------------------------------
|
|
@@ -5206,7 +5393,7 @@ var SourceManager = class {
|
|
|
5206
5393
|
const data = JSON.parse(raw);
|
|
5207
5394
|
entries = Object.entries(data.sources ?? {}).map(([name, config]) => ({ name, config }));
|
|
5208
5395
|
} catch {
|
|
5209
|
-
|
|
5396
|
+
logger31.debug("No sources.json found, starting with empty sources");
|
|
5210
5397
|
}
|
|
5211
5398
|
for (const entry of entries) {
|
|
5212
5399
|
try {
|
|
@@ -5214,9 +5401,9 @@ var SourceManager = class {
|
|
|
5214
5401
|
const result = await source.fetch();
|
|
5215
5402
|
this.sources.set(entry.name, source);
|
|
5216
5403
|
this.injectComponents(entry.name, result);
|
|
5217
|
-
|
|
5404
|
+
logger31.info({ name: entry.name }, "Source restored from config");
|
|
5218
5405
|
} catch (err) {
|
|
5219
|
-
|
|
5406
|
+
logger31.warn({ name: entry.name, error: err }, "Failed to restore source, skipping");
|
|
5220
5407
|
}
|
|
5221
5408
|
}
|
|
5222
5409
|
await this.ensureDefaultSource();
|
|
@@ -5230,9 +5417,9 @@ var SourceManager = class {
|
|
|
5230
5417
|
if (this.sources.has(DEFAULT_SOURCE_NAME)) return;
|
|
5231
5418
|
try {
|
|
5232
5419
|
await this.addSource(DEFAULT_SOURCE_NAME, DEFAULT_SOURCE_CONFIG);
|
|
5233
|
-
|
|
5420
|
+
logger31.info("Default source registered: %s", DEFAULT_SOURCE_NAME);
|
|
5234
5421
|
} catch (err) {
|
|
5235
|
-
|
|
5422
|
+
logger31.debug({ error: err }, "Failed to register default source (offline?), skipping");
|
|
5236
5423
|
}
|
|
5237
5424
|
}
|
|
5238
5425
|
// ---------------------------------------------------------------------------
|
|
@@ -5266,12 +5453,24 @@ var SourceManager = class {
|
|
|
5266
5453
|
this.presets.set(`${packageName}@${preset.name}`, preset);
|
|
5267
5454
|
}
|
|
5268
5455
|
if (this.managers.templateRegistry && result.templates.length > 0) {
|
|
5456
|
+
const nsRef = (ref) => ref.includes("@") ? ref : `${packageName}@${ref}`;
|
|
5269
5457
|
for (const template of result.templates) {
|
|
5270
|
-
const
|
|
5458
|
+
const dc = template.domainContext;
|
|
5459
|
+
const nsTemplate = {
|
|
5460
|
+
...template,
|
|
5461
|
+
name: `${packageName}@${template.name}`,
|
|
5462
|
+
domainContext: {
|
|
5463
|
+
...dc,
|
|
5464
|
+
skills: dc.skills?.map(nsRef),
|
|
5465
|
+
prompts: dc.prompts?.map(nsRef),
|
|
5466
|
+
subAgents: dc.subAgents?.map(nsRef),
|
|
5467
|
+
workflow: dc.workflow ? nsRef(dc.workflow) : dc.workflow
|
|
5468
|
+
}
|
|
5469
|
+
};
|
|
5271
5470
|
this.managers.templateRegistry.register(nsTemplate);
|
|
5272
5471
|
}
|
|
5273
5472
|
}
|
|
5274
|
-
|
|
5473
|
+
logger31.debug(
|
|
5275
5474
|
{
|
|
5276
5475
|
packageName,
|
|
5277
5476
|
skills: result.skills.length,
|
|
@@ -5404,6 +5603,671 @@ function isMajorVersionChange(oldVersion, newVersion) {
|
|
|
5404
5603
|
if (oldMajor === void 0 || newMajor === void 0) return false;
|
|
5405
5604
|
return newMajor !== oldMajor;
|
|
5406
5605
|
}
|
|
5606
|
+
|
|
5607
|
+
// src/source/source-validator.ts
|
|
5608
|
+
import { readFile as readFile8, readdir as readdir6, stat as stat9, access as access4 } from "fs/promises";
|
|
5609
|
+
import { join as join22, relative as relative2, extname as extname4 } from "path";
|
|
5610
|
+
|
|
5611
|
+
// src/source/source-schemas.ts
|
|
5612
|
+
import { z as z9 } from "zod";
|
|
5613
|
+
var VersionedComponentFields = {
|
|
5614
|
+
name: z9.string().min(1, "name is required"),
|
|
5615
|
+
version: z9.string().optional(),
|
|
5616
|
+
description: z9.string().optional(),
|
|
5617
|
+
$type: z9.string().optional(),
|
|
5618
|
+
$version: z9.number().optional(),
|
|
5619
|
+
origin: z9.object({
|
|
5620
|
+
type: z9.enum(["builtin", "source", "local"]),
|
|
5621
|
+
sourceName: z9.string().optional(),
|
|
5622
|
+
syncHash: z9.string().optional(),
|
|
5623
|
+
syncedAt: z9.string().optional(),
|
|
5624
|
+
modified: z9.boolean().optional()
|
|
5625
|
+
}).optional(),
|
|
5626
|
+
tags: z9.array(z9.string()).optional()
|
|
5627
|
+
};
|
|
5628
|
+
var PackageManifestSchema = z9.object({
|
|
5629
|
+
name: z9.string().min(1, "name is required"),
|
|
5630
|
+
version: z9.string().optional(),
|
|
5631
|
+
description: z9.string().optional(),
|
|
5632
|
+
components: z9.object({
|
|
5633
|
+
skills: z9.array(z9.string()).optional(),
|
|
5634
|
+
prompts: z9.array(z9.string()).optional(),
|
|
5635
|
+
mcp: z9.array(z9.string()).optional(),
|
|
5636
|
+
workflows: z9.array(z9.string()).optional(),
|
|
5637
|
+
templates: z9.array(z9.string()).optional()
|
|
5638
|
+
}).optional(),
|
|
5639
|
+
presets: z9.array(z9.string()).optional()
|
|
5640
|
+
});
|
|
5641
|
+
var SkillDefinitionSchema2 = z9.object({
|
|
5642
|
+
...VersionedComponentFields,
|
|
5643
|
+
content: z9.string().min(1, "content is required"),
|
|
5644
|
+
license: z9.string().optional(),
|
|
5645
|
+
compatibility: z9.string().optional(),
|
|
5646
|
+
allowedTools: z9.array(z9.string()).optional()
|
|
5647
|
+
});
|
|
5648
|
+
var PromptDefinitionSchema2 = z9.object({
|
|
5649
|
+
...VersionedComponentFields,
|
|
5650
|
+
content: z9.string().min(1, "content is required"),
|
|
5651
|
+
variables: z9.array(z9.string()).optional()
|
|
5652
|
+
});
|
|
5653
|
+
var McpServerDefinitionSchema2 = z9.object({
|
|
5654
|
+
...VersionedComponentFields,
|
|
5655
|
+
command: z9.string().min(1, "command is required"),
|
|
5656
|
+
args: z9.array(z9.string()).optional(),
|
|
5657
|
+
env: z9.record(z9.string(), z9.string()).optional()
|
|
5658
|
+
});
|
|
5659
|
+
var WorkflowDefinitionSchema2 = z9.object({
|
|
5660
|
+
...VersionedComponentFields,
|
|
5661
|
+
content: z9.string().min(1, "content is required")
|
|
5662
|
+
});
|
|
5663
|
+
var PresetDefinitionSchema = z9.object({
|
|
5664
|
+
name: z9.string().min(1, "name is required"),
|
|
5665
|
+
version: z9.string().optional(),
|
|
5666
|
+
description: z9.string().optional(),
|
|
5667
|
+
skills: z9.array(z9.string()).optional(),
|
|
5668
|
+
prompts: z9.array(z9.string()).optional(),
|
|
5669
|
+
mcpServers: z9.array(z9.string()).optional(),
|
|
5670
|
+
workflows: z9.array(z9.string()).optional(),
|
|
5671
|
+
templates: z9.array(z9.string()).optional()
|
|
5672
|
+
});
|
|
5673
|
+
|
|
5674
|
+
// src/source/source-validator.ts
|
|
5675
|
+
var COMPONENT_DIR_SCHEMAS = {
|
|
5676
|
+
skills: SkillDefinitionSchema2,
|
|
5677
|
+
prompts: PromptDefinitionSchema2,
|
|
5678
|
+
mcp: McpServerDefinitionSchema2,
|
|
5679
|
+
workflows: WorkflowDefinitionSchema2,
|
|
5680
|
+
presets: PresetDefinitionSchema
|
|
5681
|
+
};
|
|
5682
|
+
var COMPONENT_DIRS = ["skills", "prompts", "mcp", "workflows", "templates", "presets"];
|
|
5683
|
+
var SourceValidator = class {
|
|
5684
|
+
/**
|
|
5685
|
+
* Validate all assets in a source directory.
|
|
5686
|
+
* Returns a report with pass/warn/error counts and detailed issues.
|
|
5687
|
+
*/
|
|
5688
|
+
async validate(rootDir, options) {
|
|
5689
|
+
const issues = [];
|
|
5690
|
+
let passCount = 0;
|
|
5691
|
+
const validatedFiles = /* @__PURE__ */ new Set();
|
|
5692
|
+
const compat = options?.compat;
|
|
5693
|
+
const manifest = await this.validateManifest(rootDir, issues);
|
|
5694
|
+
if (manifest) passCount++;
|
|
5695
|
+
const componentNames = /* @__PURE__ */ new Map();
|
|
5696
|
+
if (manifest) {
|
|
5697
|
+
passCount += await this.validateExplicitFiles(rootDir, manifest, issues, componentNames, validatedFiles, compat);
|
|
5698
|
+
}
|
|
5699
|
+
passCount += await this.validateComponentDirs(rootDir, issues, componentNames, validatedFiles, compat);
|
|
5700
|
+
await this.validatePresetReferences(rootDir, manifest, componentNames, issues);
|
|
5701
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
5702
|
+
const warnCount = issues.filter((i) => i.severity === "warning").length;
|
|
5703
|
+
const valid = options?.strict ? errorCount === 0 && warnCount === 0 : errorCount === 0;
|
|
5704
|
+
return {
|
|
5705
|
+
valid,
|
|
5706
|
+
sourceName: manifest?.name ?? "unknown",
|
|
5707
|
+
rootDir,
|
|
5708
|
+
summary: { pass: passCount, warn: warnCount, error: errorCount },
|
|
5709
|
+
issues
|
|
5710
|
+
};
|
|
5711
|
+
}
|
|
5712
|
+
// -------------------------------------------------------------------------
|
|
5713
|
+
// Layer 1: Manifest validation
|
|
5714
|
+
// -------------------------------------------------------------------------
|
|
5715
|
+
async validateManifest(rootDir, issues) {
|
|
5716
|
+
const manifestPath = join22(rootDir, "actant.json");
|
|
5717
|
+
let raw;
|
|
5718
|
+
try {
|
|
5719
|
+
raw = await readFile8(manifestPath, "utf-8");
|
|
5720
|
+
} catch {
|
|
5721
|
+
issues.push({
|
|
5722
|
+
severity: "error",
|
|
5723
|
+
path: "actant.json",
|
|
5724
|
+
message: "actant.json not found in source root",
|
|
5725
|
+
code: "MANIFEST_MISSING"
|
|
5726
|
+
});
|
|
5727
|
+
return null;
|
|
5728
|
+
}
|
|
5729
|
+
let data;
|
|
5730
|
+
try {
|
|
5731
|
+
data = JSON.parse(raw);
|
|
5732
|
+
} catch (err) {
|
|
5733
|
+
issues.push({
|
|
5734
|
+
severity: "error",
|
|
5735
|
+
path: "actant.json",
|
|
5736
|
+
message: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
5737
|
+
code: "MANIFEST_INVALID_JSON"
|
|
5738
|
+
});
|
|
5739
|
+
return null;
|
|
5740
|
+
}
|
|
5741
|
+
const result = PackageManifestSchema.safeParse(data);
|
|
5742
|
+
if (!result.success) {
|
|
5743
|
+
for (const issue of result.error.issues) {
|
|
5744
|
+
issues.push({
|
|
5745
|
+
severity: "error",
|
|
5746
|
+
path: "actant.json",
|
|
5747
|
+
message: `${issue.path.join(".")}: ${issue.message}`,
|
|
5748
|
+
code: "MANIFEST_SCHEMA"
|
|
5749
|
+
});
|
|
5750
|
+
}
|
|
5751
|
+
return null;
|
|
5752
|
+
}
|
|
5753
|
+
const manifest = result.data;
|
|
5754
|
+
await this.verifyManifestFileRefs(rootDir, manifest, issues);
|
|
5755
|
+
return manifest;
|
|
5756
|
+
}
|
|
5757
|
+
async verifyManifestFileRefs(rootDir, manifest, issues) {
|
|
5758
|
+
const allRefs = [];
|
|
5759
|
+
if (manifest.components) {
|
|
5760
|
+
for (const [type, files] of Object.entries(manifest.components)) {
|
|
5761
|
+
if (files) {
|
|
5762
|
+
for (const f of files) {
|
|
5763
|
+
allRefs.push({ ref: f, section: `components.${type}` });
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
if (manifest.presets) {
|
|
5769
|
+
for (const f of manifest.presets) {
|
|
5770
|
+
allRefs.push({ ref: f, section: "presets" });
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
for (const { ref, section } of allRefs) {
|
|
5774
|
+
const fullPath = join22(rootDir, ref);
|
|
5775
|
+
try {
|
|
5776
|
+
await access4(fullPath);
|
|
5777
|
+
} catch {
|
|
5778
|
+
issues.push({
|
|
5779
|
+
severity: "error",
|
|
5780
|
+
path: `actant.json`,
|
|
5781
|
+
component: ref,
|
|
5782
|
+
message: `File referenced in ${section} does not exist: ${ref}`,
|
|
5783
|
+
code: "MANIFEST_FILE_MISSING"
|
|
5784
|
+
});
|
|
5785
|
+
}
|
|
5786
|
+
}
|
|
5787
|
+
}
|
|
5788
|
+
// -------------------------------------------------------------------------
|
|
5789
|
+
// Layer 2a: Validate files explicitly listed in manifest
|
|
5790
|
+
// -------------------------------------------------------------------------
|
|
5791
|
+
async validateExplicitFiles(rootDir, manifest, issues, componentNames, validatedFiles, compat) {
|
|
5792
|
+
let passCount = 0;
|
|
5793
|
+
const components = manifest.components;
|
|
5794
|
+
if (!components && !manifest.presets) return 0;
|
|
5795
|
+
if (components) {
|
|
5796
|
+
for (const [type, files] of Object.entries(components)) {
|
|
5797
|
+
if (!files) continue;
|
|
5798
|
+
for (const filePath of files) {
|
|
5799
|
+
validatedFiles.add(filePath);
|
|
5800
|
+
const ok = await this.validateSingleFile(rootDir, filePath, type, issues, componentNames);
|
|
5801
|
+
if (ok) passCount++;
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
}
|
|
5805
|
+
const presets = manifest.presets;
|
|
5806
|
+
if (presets) {
|
|
5807
|
+
for (const filePath of presets) {
|
|
5808
|
+
validatedFiles.add(filePath);
|
|
5809
|
+
const ok = await this.validateSingleFile(rootDir, filePath, "presets", issues, componentNames);
|
|
5810
|
+
if (ok) passCount++;
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5813
|
+
void compat;
|
|
5814
|
+
return passCount;
|
|
5815
|
+
}
|
|
5816
|
+
// -------------------------------------------------------------------------
|
|
5817
|
+
// Layer 2b: Scan component directories
|
|
5818
|
+
// -------------------------------------------------------------------------
|
|
5819
|
+
async validateComponentDirs(rootDir, issues, componentNames, validatedFiles, compat) {
|
|
5820
|
+
let passCount = 0;
|
|
5821
|
+
for (const dir of COMPONENT_DIRS) {
|
|
5822
|
+
const dirPath = join22(rootDir, dir);
|
|
5823
|
+
try {
|
|
5824
|
+
const dirStat = await stat9(dirPath);
|
|
5825
|
+
if (!dirStat.isDirectory()) continue;
|
|
5826
|
+
} catch {
|
|
5827
|
+
continue;
|
|
5828
|
+
}
|
|
5829
|
+
passCount += await this.scanDirectory(rootDir, dirPath, dir, issues, componentNames, validatedFiles, compat);
|
|
5830
|
+
}
|
|
5831
|
+
return passCount;
|
|
5832
|
+
}
|
|
5833
|
+
async scanDirectory(rootDir, dirPath, componentType, issues, componentNames, validatedFiles, compat) {
|
|
5834
|
+
let passCount = 0;
|
|
5835
|
+
const entries = await readdir6(dirPath);
|
|
5836
|
+
for (const entry of entries) {
|
|
5837
|
+
const fullPath = join22(dirPath, entry);
|
|
5838
|
+
const relPath = relative2(rootDir, fullPath).replace(/\\/g, "/");
|
|
5839
|
+
const entryStat = await stat9(fullPath).catch(() => null);
|
|
5840
|
+
if (!entryStat) continue;
|
|
5841
|
+
if (entryStat.isFile() && extname4(entry) === ".json") {
|
|
5842
|
+
if (validatedFiles.has(relPath)) continue;
|
|
5843
|
+
validatedFiles.add(relPath);
|
|
5844
|
+
const ok = await this.validateSingleFile(rootDir, relPath, componentType, issues, componentNames);
|
|
5845
|
+
if (ok) passCount++;
|
|
5846
|
+
} else if (entryStat.isDirectory()) {
|
|
5847
|
+
if (componentType === "skills") {
|
|
5848
|
+
const skillMdPath = join22(fullPath, "SKILL.md");
|
|
5849
|
+
try {
|
|
5850
|
+
await access4(skillMdPath);
|
|
5851
|
+
const skillRelPath = relative2(rootDir, skillMdPath).replace(/\\/g, "/");
|
|
5852
|
+
if (!validatedFiles.has(skillRelPath)) {
|
|
5853
|
+
validatedFiles.add(skillRelPath);
|
|
5854
|
+
const ok = await this.validateSkillMd(rootDir, skillRelPath, issues, componentNames, entry, compat);
|
|
5855
|
+
if (ok) passCount++;
|
|
5856
|
+
}
|
|
5857
|
+
if (compat === "agent-skills") {
|
|
5858
|
+
await this.validateSkillDirConventions(rootDir, fullPath, entry, issues);
|
|
5859
|
+
}
|
|
5860
|
+
} catch {
|
|
5861
|
+
const manifestJsonPath = join22(fullPath, "manifest.json");
|
|
5862
|
+
try {
|
|
5863
|
+
await access4(manifestJsonPath);
|
|
5864
|
+
const mRelPath = relative2(rootDir, manifestJsonPath).replace(/\\/g, "/");
|
|
5865
|
+
if (!validatedFiles.has(mRelPath)) {
|
|
5866
|
+
validatedFiles.add(mRelPath);
|
|
5867
|
+
const ok = await this.validateSingleFile(rootDir, mRelPath, componentType, issues, componentNames);
|
|
5868
|
+
if (ok) passCount++;
|
|
5869
|
+
}
|
|
5870
|
+
} catch {
|
|
5871
|
+
}
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
return passCount;
|
|
5877
|
+
}
|
|
5878
|
+
// -------------------------------------------------------------------------
|
|
5879
|
+
// Single file validation
|
|
5880
|
+
// -------------------------------------------------------------------------
|
|
5881
|
+
async validateSingleFile(rootDir, relPath, componentType, issues, componentNames) {
|
|
5882
|
+
const fullPath = join22(rootDir, relPath);
|
|
5883
|
+
let raw;
|
|
5884
|
+
try {
|
|
5885
|
+
raw = await readFile8(fullPath, "utf-8");
|
|
5886
|
+
} catch {
|
|
5887
|
+
issues.push({
|
|
5888
|
+
severity: "error",
|
|
5889
|
+
path: relPath,
|
|
5890
|
+
message: "Cannot read file",
|
|
5891
|
+
code: "FILE_UNREADABLE"
|
|
5892
|
+
});
|
|
5893
|
+
return false;
|
|
5894
|
+
}
|
|
5895
|
+
let data;
|
|
5896
|
+
try {
|
|
5897
|
+
data = JSON.parse(raw);
|
|
5898
|
+
} catch (err) {
|
|
5899
|
+
issues.push({
|
|
5900
|
+
severity: "error",
|
|
5901
|
+
path: relPath,
|
|
5902
|
+
message: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
5903
|
+
code: "INVALID_JSON"
|
|
5904
|
+
});
|
|
5905
|
+
return false;
|
|
5906
|
+
}
|
|
5907
|
+
if (componentType === "templates") {
|
|
5908
|
+
return this.validateTemplateComponent(relPath, data, issues, componentNames);
|
|
5909
|
+
}
|
|
5910
|
+
const schema = COMPONENT_DIR_SCHEMAS[componentType];
|
|
5911
|
+
if (!schema) return true;
|
|
5912
|
+
const result = schema.safeParse(data);
|
|
5913
|
+
if (!result.success) {
|
|
5914
|
+
for (const issue of result.error.issues) {
|
|
5915
|
+
issues.push({
|
|
5916
|
+
severity: "error",
|
|
5917
|
+
path: relPath,
|
|
5918
|
+
component: data?.name,
|
|
5919
|
+
message: `${issue.path.join(".")}: ${issue.message}`,
|
|
5920
|
+
code: "COMPONENT_SCHEMA"
|
|
5921
|
+
});
|
|
5922
|
+
}
|
|
5923
|
+
return false;
|
|
5924
|
+
}
|
|
5925
|
+
const parsed = result.data;
|
|
5926
|
+
this.trackComponentName(componentNames, componentType, parsed.name);
|
|
5927
|
+
if (!parsed.description) {
|
|
5928
|
+
issues.push({
|
|
5929
|
+
severity: "warning",
|
|
5930
|
+
path: relPath,
|
|
5931
|
+
component: parsed.name,
|
|
5932
|
+
message: `Missing "description" field`,
|
|
5933
|
+
code: "MISSING_DESCRIPTION"
|
|
5934
|
+
});
|
|
5935
|
+
}
|
|
5936
|
+
return true;
|
|
5937
|
+
}
|
|
5938
|
+
validateTemplateComponent(relPath, data, issues, componentNames) {
|
|
5939
|
+
const schemaResult = AgentTemplateSchema.safeParse(data);
|
|
5940
|
+
if (!schemaResult.success) {
|
|
5941
|
+
for (const issue of schemaResult.error.issues) {
|
|
5942
|
+
issues.push({
|
|
5943
|
+
severity: "error",
|
|
5944
|
+
path: relPath,
|
|
5945
|
+
component: data?.name,
|
|
5946
|
+
message: `${issue.path.join(".")}: ${issue.message}`,
|
|
5947
|
+
code: "TEMPLATE_SCHEMA"
|
|
5948
|
+
});
|
|
5949
|
+
}
|
|
5950
|
+
return false;
|
|
5951
|
+
}
|
|
5952
|
+
const semanticResult = validateTemplate(data);
|
|
5953
|
+
for (const w of semanticResult.warnings ?? []) {
|
|
5954
|
+
issues.push({
|
|
5955
|
+
severity: "warning",
|
|
5956
|
+
path: relPath,
|
|
5957
|
+
component: schemaResult.data.name,
|
|
5958
|
+
message: w.message,
|
|
5959
|
+
code: w.code ?? "TEMPLATE_SEMANTIC"
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
this.trackComponentName(componentNames, "templates", schemaResult.data.name);
|
|
5963
|
+
return true;
|
|
5964
|
+
}
|
|
5965
|
+
// -------------------------------------------------------------------------
|
|
5966
|
+
// SKILL.md validation
|
|
5967
|
+
// -------------------------------------------------------------------------
|
|
5968
|
+
async validateSkillMd(rootDir, relPath, issues, componentNames, parentDirName, compat) {
|
|
5969
|
+
const fullPath = join22(rootDir, relPath);
|
|
5970
|
+
let raw;
|
|
5971
|
+
try {
|
|
5972
|
+
raw = await readFile8(fullPath, "utf-8");
|
|
5973
|
+
} catch {
|
|
5974
|
+
issues.push({
|
|
5975
|
+
severity: "error",
|
|
5976
|
+
path: relPath,
|
|
5977
|
+
message: "Cannot read SKILL.md file",
|
|
5978
|
+
code: "FILE_UNREADABLE"
|
|
5979
|
+
});
|
|
5980
|
+
return false;
|
|
5981
|
+
}
|
|
5982
|
+
const skill = parseSkillMdContent(raw);
|
|
5983
|
+
if (!skill) {
|
|
5984
|
+
issues.push({
|
|
5985
|
+
severity: "error",
|
|
5986
|
+
path: relPath,
|
|
5987
|
+
message: "Invalid SKILL.md: missing YAML frontmatter or required 'name' field",
|
|
5988
|
+
code: "SKILL_MD_INVALID"
|
|
5989
|
+
});
|
|
5990
|
+
return false;
|
|
5991
|
+
}
|
|
5992
|
+
this.trackComponentName(componentNames, "skills", skill.name);
|
|
5993
|
+
if (compat === "agent-skills") {
|
|
5994
|
+
this.validateAgentSkillsCompat(skill, relPath, issues, parentDirName, raw);
|
|
5995
|
+
} else {
|
|
5996
|
+
if (!skill.description) {
|
|
5997
|
+
issues.push({
|
|
5998
|
+
severity: "warning",
|
|
5999
|
+
path: relPath,
|
|
6000
|
+
component: skill.name,
|
|
6001
|
+
message: `Missing "description" in SKILL.md frontmatter`,
|
|
6002
|
+
code: "SKILL_MD_MISSING_DESCRIPTION"
|
|
6003
|
+
});
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
if (!skill.content || skill.content.trim().length === 0) {
|
|
6007
|
+
issues.push({
|
|
6008
|
+
severity: "warning",
|
|
6009
|
+
path: relPath,
|
|
6010
|
+
component: skill.name,
|
|
6011
|
+
message: "SKILL.md has empty content body",
|
|
6012
|
+
code: "SKILL_MD_EMPTY_CONTENT"
|
|
6013
|
+
});
|
|
6014
|
+
}
|
|
6015
|
+
return true;
|
|
6016
|
+
}
|
|
6017
|
+
// -------------------------------------------------------------------------
|
|
6018
|
+
// Agent Skills (agentskills.io) compatibility checks
|
|
6019
|
+
// -------------------------------------------------------------------------
|
|
6020
|
+
validateAgentSkillsCompat(skill, relPath, issues, parentDirName, raw) {
|
|
6021
|
+
const name = skill.name;
|
|
6022
|
+
const NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
6023
|
+
if (name.length > 64) {
|
|
6024
|
+
issues.push({
|
|
6025
|
+
severity: "error",
|
|
6026
|
+
path: relPath,
|
|
6027
|
+
component: name,
|
|
6028
|
+
message: `Name exceeds 64 characters (${name.length})`,
|
|
6029
|
+
code: "AGENT_SKILLS_NAME_TOO_LONG"
|
|
6030
|
+
});
|
|
6031
|
+
} else if (!NAME_RE.test(name)) {
|
|
6032
|
+
issues.push({
|
|
6033
|
+
severity: "error",
|
|
6034
|
+
path: relPath,
|
|
6035
|
+
component: name,
|
|
6036
|
+
message: `Name must contain only lowercase letters, numbers, and hyphens, starting with a letter`,
|
|
6037
|
+
code: "AGENT_SKILLS_NAME_FORMAT"
|
|
6038
|
+
});
|
|
6039
|
+
} else {
|
|
6040
|
+
if (name.endsWith("-")) {
|
|
6041
|
+
issues.push({
|
|
6042
|
+
severity: "error",
|
|
6043
|
+
path: relPath,
|
|
6044
|
+
component: name,
|
|
6045
|
+
message: "Name must not end with a hyphen",
|
|
6046
|
+
code: "AGENT_SKILLS_NAME_TRAILING_HYPHEN"
|
|
6047
|
+
});
|
|
6048
|
+
}
|
|
6049
|
+
if (name.includes("--")) {
|
|
6050
|
+
issues.push({
|
|
6051
|
+
severity: "error",
|
|
6052
|
+
path: relPath,
|
|
6053
|
+
component: name,
|
|
6054
|
+
message: "Name must not contain consecutive hyphens",
|
|
6055
|
+
code: "AGENT_SKILLS_NAME_CONSECUTIVE_HYPHENS"
|
|
6056
|
+
});
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
if (parentDirName && name !== parentDirName) {
|
|
6060
|
+
issues.push({
|
|
6061
|
+
severity: "error",
|
|
6062
|
+
path: relPath,
|
|
6063
|
+
component: name,
|
|
6064
|
+
message: `Name "${name}" does not match parent directory "${parentDirName}"`,
|
|
6065
|
+
code: "AGENT_SKILLS_NAME_DIR_MISMATCH"
|
|
6066
|
+
});
|
|
6067
|
+
}
|
|
6068
|
+
if (!skill.description) {
|
|
6069
|
+
issues.push({
|
|
6070
|
+
severity: "error",
|
|
6071
|
+
path: relPath,
|
|
6072
|
+
component: name,
|
|
6073
|
+
message: `Missing required "description" field (Agent Skills spec)`,
|
|
6074
|
+
code: "AGENT_SKILLS_DESCRIPTION_REQUIRED"
|
|
6075
|
+
});
|
|
6076
|
+
} else if (skill.description.length > 1024) {
|
|
6077
|
+
issues.push({
|
|
6078
|
+
severity: "warning",
|
|
6079
|
+
path: relPath,
|
|
6080
|
+
component: name,
|
|
6081
|
+
message: `Description exceeds 1024 characters (${skill.description.length})`,
|
|
6082
|
+
code: "AGENT_SKILLS_DESCRIPTION_TOO_LONG"
|
|
6083
|
+
});
|
|
6084
|
+
}
|
|
6085
|
+
if (skill.compatibility && skill.compatibility.length > 500) {
|
|
6086
|
+
issues.push({
|
|
6087
|
+
severity: "warning",
|
|
6088
|
+
path: relPath,
|
|
6089
|
+
component: name,
|
|
6090
|
+
message: `Compatibility field exceeds 500 characters (${skill.compatibility.length})`,
|
|
6091
|
+
code: "AGENT_SKILLS_COMPAT_TOO_LONG"
|
|
6092
|
+
});
|
|
6093
|
+
}
|
|
6094
|
+
if (raw) {
|
|
6095
|
+
const frontmatterEnd = raw.indexOf("---", 3);
|
|
6096
|
+
if (frontmatterEnd !== -1) {
|
|
6097
|
+
const body = raw.substring(frontmatterEnd + 3).trim();
|
|
6098
|
+
const lineCount = body.split("\n").length;
|
|
6099
|
+
if (lineCount > 500) {
|
|
6100
|
+
issues.push({
|
|
6101
|
+
severity: "warning",
|
|
6102
|
+
path: relPath,
|
|
6103
|
+
component: name,
|
|
6104
|
+
message: `SKILL.md body has ${lineCount} lines; Agent Skills spec recommends < 500 lines`,
|
|
6105
|
+
code: "AGENT_SKILLS_BODY_TOO_LONG"
|
|
6106
|
+
});
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
// -------------------------------------------------------------------------
|
|
6112
|
+
// Agent Skills directory convention (scripts/, references/, assets/)
|
|
6113
|
+
// -------------------------------------------------------------------------
|
|
6114
|
+
async validateSkillDirConventions(rootDir, skillDir, skillName, issues) {
|
|
6115
|
+
const KNOWN_DIRS = /* @__PURE__ */ new Set(["scripts", "references", "assets"]);
|
|
6116
|
+
const KNOWN_FILES = /* @__PURE__ */ new Set(["SKILL.md", "LICENSE", "LICENSE.txt", "LICENSE.md"]);
|
|
6117
|
+
let entries;
|
|
6118
|
+
try {
|
|
6119
|
+
entries = await readdir6(skillDir);
|
|
6120
|
+
} catch {
|
|
6121
|
+
return;
|
|
6122
|
+
}
|
|
6123
|
+
for (const entry of entries) {
|
|
6124
|
+
const fullPath = join22(skillDir, entry);
|
|
6125
|
+
const relPath = relative2(rootDir, fullPath).replace(/\\/g, "/");
|
|
6126
|
+
const entryStat = await stat9(fullPath).catch(() => null);
|
|
6127
|
+
if (!entryStat) continue;
|
|
6128
|
+
if (entryStat.isDirectory()) {
|
|
6129
|
+
if (KNOWN_DIRS.has(entry)) {
|
|
6130
|
+
issues.push({
|
|
6131
|
+
severity: "info",
|
|
6132
|
+
path: relPath,
|
|
6133
|
+
component: skillName,
|
|
6134
|
+
message: `Agent Skills convention: ${entry}/ directory detected`,
|
|
6135
|
+
code: "AGENT_SKILLS_DIR_FOUND"
|
|
6136
|
+
});
|
|
6137
|
+
}
|
|
6138
|
+
} else if (!KNOWN_FILES.has(entry)) {
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
// -------------------------------------------------------------------------
|
|
6143
|
+
// Layer 3: Cross-reference validation
|
|
6144
|
+
// -------------------------------------------------------------------------
|
|
6145
|
+
async validatePresetReferences(rootDir, manifest, componentNames, issues) {
|
|
6146
|
+
const presetsDir = join22(rootDir, "presets");
|
|
6147
|
+
const presetFiles = [];
|
|
6148
|
+
if (manifest?.presets) {
|
|
6149
|
+
for (const p of manifest.presets) {
|
|
6150
|
+
presetFiles.push(p);
|
|
6151
|
+
}
|
|
6152
|
+
} else {
|
|
6153
|
+
try {
|
|
6154
|
+
const entries = await readdir6(presetsDir);
|
|
6155
|
+
for (const e of entries) {
|
|
6156
|
+
if (extname4(e) === ".json") {
|
|
6157
|
+
presetFiles.push(`presets/${e}`);
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
} catch {
|
|
6161
|
+
return;
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
for (const presetFile of presetFiles) {
|
|
6165
|
+
const fullPath = join22(rootDir, presetFile);
|
|
6166
|
+
let data;
|
|
6167
|
+
try {
|
|
6168
|
+
const raw = await readFile8(fullPath, "utf-8");
|
|
6169
|
+
data = JSON.parse(raw);
|
|
6170
|
+
} catch {
|
|
6171
|
+
continue;
|
|
6172
|
+
}
|
|
6173
|
+
const presetName = data.name || presetFile;
|
|
6174
|
+
const refMap = [
|
|
6175
|
+
["skills", "skills"],
|
|
6176
|
+
["prompts", "prompts"],
|
|
6177
|
+
["mcpServers", "mcp"],
|
|
6178
|
+
["workflows", "workflows"],
|
|
6179
|
+
["templates", "templates"]
|
|
6180
|
+
];
|
|
6181
|
+
for (const [field, compType] of refMap) {
|
|
6182
|
+
const refs = data[field];
|
|
6183
|
+
if (!refs) continue;
|
|
6184
|
+
const available = componentNames.get(compType) ?? /* @__PURE__ */ new Set();
|
|
6185
|
+
for (const ref of refs) {
|
|
6186
|
+
if (!available.has(ref)) {
|
|
6187
|
+
issues.push({
|
|
6188
|
+
severity: "warning",
|
|
6189
|
+
path: presetFile,
|
|
6190
|
+
component: presetName,
|
|
6191
|
+
message: `Preset references ${compType} "${ref}" which was not found in this source`,
|
|
6192
|
+
code: "PRESET_REF_MISSING"
|
|
6193
|
+
});
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
// -------------------------------------------------------------------------
|
|
6200
|
+
// Helpers
|
|
6201
|
+
// -------------------------------------------------------------------------
|
|
6202
|
+
trackComponentName(componentNames, type, name) {
|
|
6203
|
+
let set = componentNames.get(type);
|
|
6204
|
+
if (!set) {
|
|
6205
|
+
set = /* @__PURE__ */ new Set();
|
|
6206
|
+
componentNames.set(type, set);
|
|
6207
|
+
}
|
|
6208
|
+
set.add(name);
|
|
6209
|
+
}
|
|
6210
|
+
};
|
|
6211
|
+
|
|
6212
|
+
// src/provider/builtin-providers.ts
|
|
6213
|
+
var BUILTIN_PROVIDERS = [
|
|
6214
|
+
{
|
|
6215
|
+
type: "anthropic",
|
|
6216
|
+
displayName: "Anthropic (Claude)",
|
|
6217
|
+
protocol: "anthropic",
|
|
6218
|
+
defaultBaseUrl: "https://api.anthropic.com",
|
|
6219
|
+
models: ["claude-sonnet-4-20250514", "claude-opus-4-20250514"]
|
|
6220
|
+
},
|
|
6221
|
+
{
|
|
6222
|
+
type: "openai",
|
|
6223
|
+
displayName: "OpenAI",
|
|
6224
|
+
protocol: "openai",
|
|
6225
|
+
defaultBaseUrl: "https://api.openai.com/v1",
|
|
6226
|
+
models: ["gpt-4o", "gpt-4o-mini", "o3-mini"]
|
|
6227
|
+
},
|
|
6228
|
+
{
|
|
6229
|
+
type: "deepseek",
|
|
6230
|
+
displayName: "DeepSeek",
|
|
6231
|
+
protocol: "openai",
|
|
6232
|
+
defaultBaseUrl: "https://api.deepseek.com/v1",
|
|
6233
|
+
models: ["deepseek-chat", "deepseek-reasoner"]
|
|
6234
|
+
},
|
|
6235
|
+
{
|
|
6236
|
+
type: "ollama",
|
|
6237
|
+
displayName: "Ollama (Local)",
|
|
6238
|
+
protocol: "openai",
|
|
6239
|
+
defaultBaseUrl: "http://localhost:11434/v1"
|
|
6240
|
+
},
|
|
6241
|
+
{
|
|
6242
|
+
type: "azure",
|
|
6243
|
+
displayName: "Azure OpenAI",
|
|
6244
|
+
protocol: "openai",
|
|
6245
|
+
defaultBaseUrl: "https://<resource>.openai.azure.com"
|
|
6246
|
+
},
|
|
6247
|
+
{
|
|
6248
|
+
type: "bedrock",
|
|
6249
|
+
displayName: "AWS Bedrock",
|
|
6250
|
+
protocol: "anthropic",
|
|
6251
|
+
defaultBaseUrl: "https://bedrock-runtime.<region>.amazonaws.com"
|
|
6252
|
+
},
|
|
6253
|
+
{
|
|
6254
|
+
type: "vertex",
|
|
6255
|
+
displayName: "Google Vertex AI",
|
|
6256
|
+
protocol: "anthropic",
|
|
6257
|
+
defaultBaseUrl: "https://<region>-aiplatform.googleapis.com"
|
|
6258
|
+
},
|
|
6259
|
+
{
|
|
6260
|
+
type: "custom",
|
|
6261
|
+
displayName: "Custom",
|
|
6262
|
+
protocol: "custom",
|
|
6263
|
+
defaultBaseUrl: "http://localhost:8080"
|
|
6264
|
+
}
|
|
6265
|
+
];
|
|
6266
|
+
function registerBuiltinProviders() {
|
|
6267
|
+
for (const descriptor of BUILTIN_PROVIDERS) {
|
|
6268
|
+
modelProviderRegistry.register(descriptor);
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
5407
6271
|
export {
|
|
5408
6272
|
AgentBackendSchema,
|
|
5409
6273
|
AgentInitializer,
|
|
@@ -5411,6 +6275,7 @@ export {
|
|
|
5411
6275
|
AgentManager,
|
|
5412
6276
|
AgentStatusSchema,
|
|
5413
6277
|
AgentTemplateSchema,
|
|
6278
|
+
BUILTIN_PROVIDERS,
|
|
5414
6279
|
BaseComponentManager,
|
|
5415
6280
|
ClaudeCodeBuilder,
|
|
5416
6281
|
ClaudeCodeCommunicator,
|
|
@@ -5447,6 +6312,7 @@ export {
|
|
|
5447
6312
|
McpServerRefSchema,
|
|
5448
6313
|
MkdirStep,
|
|
5449
6314
|
MockLauncher,
|
|
6315
|
+
ModelProviderRegistry,
|
|
5450
6316
|
ModelProviderSchema,
|
|
5451
6317
|
NpmInstallStep,
|
|
5452
6318
|
PermissionAuditLogger,
|
|
@@ -5464,6 +6330,7 @@ export {
|
|
|
5464
6330
|
SessionRegistry,
|
|
5465
6331
|
SkillManager,
|
|
5466
6332
|
SourceManager,
|
|
6333
|
+
SourceValidator,
|
|
5467
6334
|
StepRegistry,
|
|
5468
6335
|
TaskDispatcher,
|
|
5469
6336
|
TaskQueue,
|
|
@@ -5482,9 +6349,11 @@ export {
|
|
|
5482
6349
|
isAcpOnlyBackend,
|
|
5483
6350
|
isProcessAlive,
|
|
5484
6351
|
metaFilePath,
|
|
6352
|
+
modelProviderRegistry,
|
|
5485
6353
|
openBackend,
|
|
5486
6354
|
readInstanceMeta,
|
|
5487
6355
|
registerBackend,
|
|
6356
|
+
registerBuiltinProviders,
|
|
5488
6357
|
registerCommunicator,
|
|
5489
6358
|
requireMode,
|
|
5490
6359
|
resolveAcpBackend,
|