@actant/core 0.1.2 → 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 +219 -20
- package/dist/index.js +1270 -218
- 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";
|
|
@@ -1061,13 +1065,29 @@ var DomainContextSchema = z2.object({
|
|
|
1061
1065
|
extensions: z2.record(z2.string(), z2.array(z2.unknown())).optional()
|
|
1062
1066
|
});
|
|
1063
1067
|
var AgentBackendSchema = z2.object({
|
|
1064
|
-
type: z2.enum(["cursor", "claude-code", "custom"]),
|
|
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.
|
|
1083
|
+
type: z2.string().min(1),
|
|
1084
|
+
protocol: ModelApiProtocolEnum.optional(),
|
|
1085
|
+
baseUrl: z2.string().optional(),
|
|
1069
1086
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
1070
|
-
})
|
|
1087
|
+
}).transform((val) => ({
|
|
1088
|
+
...val,
|
|
1089
|
+
protocol: val.protocol ?? DEFAULT_PROTOCOL[val.type] ?? "custom"
|
|
1090
|
+
}));
|
|
1071
1091
|
var InitializerStepSchema = z2.object({
|
|
1072
1092
|
type: z2.string().min(1),
|
|
1073
1093
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
@@ -1125,7 +1145,7 @@ var AgentTemplateSchema = z2.object({
|
|
|
1125
1145
|
origin: ComponentOriginSchema.optional(),
|
|
1126
1146
|
tags: z2.array(z2.string()).optional(),
|
|
1127
1147
|
backend: AgentBackendSchema,
|
|
1128
|
-
provider: ModelProviderSchema,
|
|
1148
|
+
provider: ModelProviderSchema.optional(),
|
|
1129
1149
|
domainContext: DomainContextSchema,
|
|
1130
1150
|
permissions: PermissionsInputSchema.optional(),
|
|
1131
1151
|
initializer: InitializerSchema.optional(),
|
|
@@ -1253,6 +1273,58 @@ function isNodeError(err) {
|
|
|
1253
1273
|
return err instanceof Error && "code" in err;
|
|
1254
1274
|
}
|
|
1255
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
|
+
|
|
1256
1328
|
// src/template/schema/config-validators.ts
|
|
1257
1329
|
function zodToIssues(zodError) {
|
|
1258
1330
|
return zodError.issues.map((i) => ({
|
|
@@ -1276,7 +1348,15 @@ function validateProviderConfig(data) {
|
|
|
1276
1348
|
if (!result.success) {
|
|
1277
1349
|
return { valid: false, errors: zodToIssues(result.error), warnings: [] };
|
|
1278
1350
|
}
|
|
1279
|
-
|
|
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 };
|
|
1280
1360
|
}
|
|
1281
1361
|
function validatePermissionsConfig(data) {
|
|
1282
1362
|
const result = PermissionsInputSchema.safeParse(data);
|
|
@@ -1360,12 +1440,21 @@ function validateTemplate(data) {
|
|
|
1360
1440
|
"CUSTOM_BACKEND_NO_CONFIG"
|
|
1361
1441
|
));
|
|
1362
1442
|
}
|
|
1363
|
-
if (template.provider
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
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
|
+
}
|
|
1369
1458
|
}
|
|
1370
1459
|
return { valid: true, data: template, errors: [], warnings };
|
|
1371
1460
|
}
|
|
@@ -1378,13 +1467,13 @@ import { TemplateNotFoundError, ConfigValidationError as ConfigValidationError3
|
|
|
1378
1467
|
// src/domain/base-component-manager.ts
|
|
1379
1468
|
import { readFile as readFile2, writeFile as writeFile4, readdir as readdir2, stat as stat4, unlink, mkdir as mkdir4 } from "fs/promises";
|
|
1380
1469
|
import { join as join5, extname as extname2, resolve } from "path";
|
|
1381
|
-
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";
|
|
1382
1471
|
var BaseComponentManager = class {
|
|
1383
1472
|
components = /* @__PURE__ */ new Map();
|
|
1384
1473
|
logger;
|
|
1385
1474
|
persistDir;
|
|
1386
1475
|
constructor(loggerName) {
|
|
1387
|
-
this.logger =
|
|
1476
|
+
this.logger = createLogger12(loggerName);
|
|
1388
1477
|
}
|
|
1389
1478
|
setPersistDir(dir) {
|
|
1390
1479
|
this.persistDir = dir;
|
|
@@ -1717,13 +1806,13 @@ var TemplateRegistry = class extends BaseComponentManager {
|
|
|
1717
1806
|
import { watch } from "fs";
|
|
1718
1807
|
import { join as join7 } from "path";
|
|
1719
1808
|
import { access } from "fs/promises";
|
|
1720
|
-
import { createLogger as
|
|
1721
|
-
var
|
|
1809
|
+
import { createLogger as createLogger13 } from "@actant/shared";
|
|
1810
|
+
var logger12 = createLogger13("template-file-watcher");
|
|
1722
1811
|
var DEFAULT_DEBOUNCE_MS = 300;
|
|
1723
1812
|
var TemplateFileWatcher = class {
|
|
1724
|
-
constructor(templatesDir,
|
|
1813
|
+
constructor(templatesDir, registry3, options) {
|
|
1725
1814
|
this.templatesDir = templatesDir;
|
|
1726
|
-
this.registry =
|
|
1815
|
+
this.registry = registry3;
|
|
1727
1816
|
this.debounceMs = options?.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
1728
1817
|
}
|
|
1729
1818
|
watcher = null;
|
|
@@ -1740,12 +1829,12 @@ var TemplateFileWatcher = class {
|
|
|
1740
1829
|
this.handleChange(filename);
|
|
1741
1830
|
});
|
|
1742
1831
|
this.watcher.on("error", (err) => {
|
|
1743
|
-
|
|
1832
|
+
logger12.error({ error: err }, "File watcher error");
|
|
1744
1833
|
});
|
|
1745
1834
|
this.buildFileMap();
|
|
1746
|
-
|
|
1835
|
+
logger12.info({ dir: this.templatesDir }, "Template file watcher started");
|
|
1747
1836
|
} catch (err) {
|
|
1748
|
-
|
|
1837
|
+
logger12.error({ error: err, dir: this.templatesDir }, "Failed to start file watcher");
|
|
1749
1838
|
}
|
|
1750
1839
|
}
|
|
1751
1840
|
stop() {
|
|
@@ -1756,7 +1845,7 @@ var TemplateFileWatcher = class {
|
|
|
1756
1845
|
clearTimeout(timer);
|
|
1757
1846
|
}
|
|
1758
1847
|
this.debounceTimers.clear();
|
|
1759
|
-
|
|
1848
|
+
logger12.info("Template file watcher stopped");
|
|
1760
1849
|
}
|
|
1761
1850
|
get isWatching() {
|
|
1762
1851
|
return this.watcher !== null;
|
|
@@ -1775,7 +1864,7 @@ var TemplateFileWatcher = class {
|
|
|
1775
1864
|
setTimeout(() => {
|
|
1776
1865
|
this.debounceTimers.delete(filename);
|
|
1777
1866
|
this.processChange(filename).catch((err) => {
|
|
1778
|
-
|
|
1867
|
+
logger12.error({ filename, error: err }, "Error processing template file change");
|
|
1779
1868
|
});
|
|
1780
1869
|
}, this.debounceMs)
|
|
1781
1870
|
);
|
|
@@ -1793,7 +1882,7 @@ var TemplateFileWatcher = class {
|
|
|
1793
1882
|
if (previousName && this.registry.has(previousName)) {
|
|
1794
1883
|
this.registry.unregister(previousName);
|
|
1795
1884
|
this.fileToName.delete(filename);
|
|
1796
|
-
|
|
1885
|
+
logger12.info({ templateName: previousName, filename }, "Template unregistered (file deleted)");
|
|
1797
1886
|
}
|
|
1798
1887
|
return;
|
|
1799
1888
|
}
|
|
@@ -1808,9 +1897,9 @@ var TemplateFileWatcher = class {
|
|
|
1808
1897
|
}
|
|
1809
1898
|
this.registry.register(template);
|
|
1810
1899
|
this.fileToName.set(filename, template.name);
|
|
1811
|
-
|
|
1900
|
+
logger12.info({ templateName: template.name, filename }, previousName ? "Template reloaded" : "New template registered");
|
|
1812
1901
|
} catch (err) {
|
|
1813
|
-
|
|
1902
|
+
logger12.warn({ filePath, error: err }, "Failed to reload template");
|
|
1814
1903
|
}
|
|
1815
1904
|
}
|
|
1816
1905
|
};
|
|
@@ -1824,7 +1913,7 @@ import {
|
|
|
1824
1913
|
ConfigValidationError as ConfigValidationError4,
|
|
1825
1914
|
InstanceCorruptedError as InstanceCorruptedError3,
|
|
1826
1915
|
WorkspaceInitError,
|
|
1827
|
-
createLogger as
|
|
1916
|
+
createLogger as createLogger16
|
|
1828
1917
|
} from "@actant/shared";
|
|
1829
1918
|
|
|
1830
1919
|
// src/state/instance-meta-schema.ts
|
|
@@ -1846,7 +1935,7 @@ var LaunchModeSchema = z3.enum([
|
|
|
1846
1935
|
]);
|
|
1847
1936
|
var ProcessOwnershipSchema = z3.enum(["managed", "external"]);
|
|
1848
1937
|
var WorkspacePolicySchema = z3.enum(["persistent", "ephemeral"]);
|
|
1849
|
-
var AgentBackendTypeSchema = z3.enum(["cursor", "claude-code", "custom"]);
|
|
1938
|
+
var AgentBackendTypeSchema = z3.enum(["cursor", "cursor-agent", "claude-code", "custom", "pi"]);
|
|
1850
1939
|
var PermissionModeSchema2 = z3.enum([
|
|
1851
1940
|
"default",
|
|
1852
1941
|
"acceptEdits",
|
|
@@ -1871,6 +1960,13 @@ var PermissionsConfigSchema = z3.object({
|
|
|
1871
1960
|
sandbox: SandboxConfigSchema.optional(),
|
|
1872
1961
|
additionalDirectories: z3.array(z3.string()).optional()
|
|
1873
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
|
+
});
|
|
1874
1970
|
var AgentInstanceMetaSchema = z3.object({
|
|
1875
1971
|
id: z3.string().min(1),
|
|
1876
1972
|
name: z3.string().min(1),
|
|
@@ -1878,6 +1974,7 @@ var AgentInstanceMetaSchema = z3.object({
|
|
|
1878
1974
|
templateVersion: z3.string().regex(/^\d+\.\d+\.\d+$/),
|
|
1879
1975
|
backendType: AgentBackendTypeSchema.default("cursor"),
|
|
1880
1976
|
backendConfig: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1977
|
+
providerConfig: ModelProviderConfigSchema.optional(),
|
|
1881
1978
|
status: AgentStatusSchema,
|
|
1882
1979
|
launchMode: LaunchModeSchema,
|
|
1883
1980
|
workspacePolicy: WorkspacePolicySchema.default("persistent"),
|
|
@@ -1894,8 +1991,8 @@ import { readFile as readFile3, writeFile as writeFile5, rename, readdir as read
|
|
|
1894
1991
|
import { join as join8, dirname } from "path";
|
|
1895
1992
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1896
1993
|
import { InstanceCorruptedError } from "@actant/shared";
|
|
1897
|
-
import { createLogger as
|
|
1898
|
-
var
|
|
1994
|
+
import { createLogger as createLogger14 } from "@actant/shared";
|
|
1995
|
+
var logger13 = createLogger14("instance-meta-io");
|
|
1899
1996
|
var META_FILENAME = ".actant.json";
|
|
1900
1997
|
function metaFilePath(workspaceDir) {
|
|
1901
1998
|
return join8(workspaceDir, META_FILENAME);
|
|
@@ -1947,12 +2044,12 @@ async function updateInstanceMeta(workspaceDir, patch) {
|
|
|
1947
2044
|
await writeInstanceMeta(workspaceDir, updated);
|
|
1948
2045
|
return updated;
|
|
1949
2046
|
}
|
|
1950
|
-
async function scanInstances(instancesBaseDir,
|
|
2047
|
+
async function scanInstances(instancesBaseDir, registry3) {
|
|
1951
2048
|
const valid = [];
|
|
1952
2049
|
const corrupted = [];
|
|
1953
2050
|
const validNames = /* @__PURE__ */ new Set();
|
|
1954
|
-
if (
|
|
1955
|
-
for (const entry of
|
|
2051
|
+
if (registry3) {
|
|
2052
|
+
for (const entry of registry3.list()) {
|
|
1956
2053
|
if (entry.status === "orphaned") continue;
|
|
1957
2054
|
try {
|
|
1958
2055
|
const st = await stat5(entry.workspacePath);
|
|
@@ -1964,7 +2061,7 @@ async function scanInstances(instancesBaseDir, registry) {
|
|
|
1964
2061
|
valid.push(meta);
|
|
1965
2062
|
validNames.add(entry.name);
|
|
1966
2063
|
} catch (err) {
|
|
1967
|
-
|
|
2064
|
+
logger13.warn({ name: entry.name, path: entry.workspacePath, error: err }, "Registry entry unreachable or corrupted");
|
|
1968
2065
|
corrupted.push(entry.name);
|
|
1969
2066
|
}
|
|
1970
2067
|
}
|
|
@@ -1989,7 +2086,7 @@ async function scanInstances(instancesBaseDir, registry) {
|
|
|
1989
2086
|
valid.push(meta);
|
|
1990
2087
|
validNames.add(meta.name);
|
|
1991
2088
|
} catch (err) {
|
|
1992
|
-
|
|
2089
|
+
logger13.warn({ dir: entry, error: err }, "Corrupted instance directory");
|
|
1993
2090
|
corrupted.push(entry);
|
|
1994
2091
|
}
|
|
1995
2092
|
}
|
|
@@ -2138,13 +2235,13 @@ function isNodeError4(err) {
|
|
|
2138
2235
|
}
|
|
2139
2236
|
|
|
2140
2237
|
// src/initializer/pipeline/initialization-pipeline.ts
|
|
2141
|
-
import { createLogger as
|
|
2142
|
-
var
|
|
2238
|
+
import { createLogger as createLogger15 } from "@actant/shared";
|
|
2239
|
+
var logger14 = createLogger15("initialization-pipeline");
|
|
2143
2240
|
var DEFAULT_STEP_TIMEOUT_MS = 6e4;
|
|
2144
2241
|
var DEFAULT_TOTAL_TIMEOUT_MS = 3e5;
|
|
2145
2242
|
var InitializationPipeline = class {
|
|
2146
|
-
constructor(
|
|
2147
|
-
this.registry =
|
|
2243
|
+
constructor(registry3, options) {
|
|
2244
|
+
this.registry = registry3;
|
|
2148
2245
|
this.stepTimeoutMs = options?.defaultStepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
2149
2246
|
this.totalTimeoutMs = options?.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS;
|
|
2150
2247
|
this.onProgress = options?.onProgress;
|
|
@@ -2173,14 +2270,14 @@ var InitializationPipeline = class {
|
|
|
2173
2270
|
const errors = [];
|
|
2174
2271
|
const executed = [];
|
|
2175
2272
|
const pipelineStart = Date.now();
|
|
2176
|
-
|
|
2273
|
+
logger14.info({ stepsCount: steps.length }, "Starting initialization pipeline");
|
|
2177
2274
|
for (let i = 0; i < steps.length; i++) {
|
|
2178
2275
|
const step = steps[i];
|
|
2179
2276
|
if (!step) continue;
|
|
2180
2277
|
if (Date.now() - pipelineStart > this.totalTimeoutMs) {
|
|
2181
2278
|
const err = new Error(`Pipeline total timeout exceeded (${this.totalTimeoutMs}ms)`);
|
|
2182
2279
|
errors.push({ stepIndex: i, stepType: step.type, error: err });
|
|
2183
|
-
|
|
2280
|
+
logger14.error({ stepIndex: i, stepType: step.type }, "Pipeline timeout exceeded");
|
|
2184
2281
|
await this.rollback(executed, context, err);
|
|
2185
2282
|
return { success: false, stepsExecuted: i, stepsTotal: steps.length, errors, outputs };
|
|
2186
2283
|
}
|
|
@@ -2192,7 +2289,7 @@ var InitializationPipeline = class {
|
|
|
2192
2289
|
return { success: false, stepsExecuted: i, stepsTotal: steps.length, errors, outputs };
|
|
2193
2290
|
}
|
|
2194
2291
|
this.onProgress?.(i, steps.length, step.type);
|
|
2195
|
-
|
|
2292
|
+
logger14.debug({ stepIndex: i, stepType: step.type }, "Executing step");
|
|
2196
2293
|
try {
|
|
2197
2294
|
const result = await this.executeWithTimeout(executor, context, step.config ?? {});
|
|
2198
2295
|
if (!result.success) {
|
|
@@ -2207,18 +2304,18 @@ var InitializationPipeline = class {
|
|
|
2207
2304
|
outputs.set(`step-${i}-${step.type}`, result.output);
|
|
2208
2305
|
}
|
|
2209
2306
|
if (result.message) {
|
|
2210
|
-
|
|
2307
|
+
logger14.info({ stepIndex: i, stepType: step.type }, result.message);
|
|
2211
2308
|
}
|
|
2212
2309
|
} catch (err) {
|
|
2213
2310
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
2214
2311
|
errors.push({ stepIndex: i, stepType: step.type, error });
|
|
2215
|
-
|
|
2312
|
+
logger14.error({ stepIndex: i, stepType: step.type, error }, "Step execution failed");
|
|
2216
2313
|
executed.push({ index: i, executor, config: step.config ?? {} });
|
|
2217
2314
|
await this.rollback(executed, context, error);
|
|
2218
2315
|
return { success: false, stepsExecuted: i + 1, stepsTotal: steps.length, errors, outputs };
|
|
2219
2316
|
}
|
|
2220
2317
|
}
|
|
2221
|
-
|
|
2318
|
+
logger14.info({ stepsExecuted: steps.length, elapsedMs: Date.now() - pipelineStart }, "Pipeline completed successfully");
|
|
2222
2319
|
return { success: true, stepsExecuted: steps.length, stepsTotal: steps.length, errors, outputs };
|
|
2223
2320
|
}
|
|
2224
2321
|
async executeWithTimeout(executor, context, config) {
|
|
@@ -2240,7 +2337,7 @@ var InitializationPipeline = class {
|
|
|
2240
2337
|
}
|
|
2241
2338
|
async rollback(executed, context, triggerError) {
|
|
2242
2339
|
if (executed.length === 0) return;
|
|
2243
|
-
|
|
2340
|
+
logger14.info({ stepsToRollback: executed.length }, "Rolling back executed steps");
|
|
2244
2341
|
for (let i = executed.length - 1; i >= 0; i--) {
|
|
2245
2342
|
const entry = executed[i];
|
|
2246
2343
|
if (!entry) continue;
|
|
@@ -2248,9 +2345,9 @@ var InitializationPipeline = class {
|
|
|
2248
2345
|
if (!executor.rollback) continue;
|
|
2249
2346
|
try {
|
|
2250
2347
|
await executor.rollback(context, config, triggerError);
|
|
2251
|
-
|
|
2348
|
+
logger14.debug({ stepIndex: index, stepType: executor.type }, "Step rolled back");
|
|
2252
2349
|
} catch (rollbackErr) {
|
|
2253
|
-
|
|
2350
|
+
logger14.warn(
|
|
2254
2351
|
{ stepIndex: index, stepType: executor.type, error: rollbackErr },
|
|
2255
2352
|
"Rollback failed (best-effort)"
|
|
2256
2353
|
);
|
|
@@ -2260,7 +2357,7 @@ var InitializationPipeline = class {
|
|
|
2260
2357
|
};
|
|
2261
2358
|
|
|
2262
2359
|
// src/initializer/agent-initializer.ts
|
|
2263
|
-
var
|
|
2360
|
+
var logger15 = createLogger16("agent-initializer");
|
|
2264
2361
|
var AgentInitializer = class {
|
|
2265
2362
|
constructor(templateRegistry, instancesBaseDir, options) {
|
|
2266
2363
|
this.templateRegistry = templateRegistry;
|
|
@@ -2273,6 +2370,9 @@ var AgentInitializer = class {
|
|
|
2273
2370
|
}
|
|
2274
2371
|
builder;
|
|
2275
2372
|
pipeline;
|
|
2373
|
+
get workspaceBuilder() {
|
|
2374
|
+
return this.builder;
|
|
2375
|
+
}
|
|
2276
2376
|
/**
|
|
2277
2377
|
* Create a new Agent Instance.
|
|
2278
2378
|
* 1. Resolve template from registry
|
|
@@ -2299,10 +2399,10 @@ var AgentInitializer = class {
|
|
|
2299
2399
|
);
|
|
2300
2400
|
case "overwrite":
|
|
2301
2401
|
await rm(workspaceDir, { recursive: true, force: true });
|
|
2302
|
-
|
|
2402
|
+
logger15.info({ workspaceDir }, "Existing directory removed (overwrite)");
|
|
2303
2403
|
break;
|
|
2304
2404
|
case "append":
|
|
2305
|
-
|
|
2405
|
+
logger15.info({ workspaceDir }, "Appending to existing directory");
|
|
2306
2406
|
break;
|
|
2307
2407
|
}
|
|
2308
2408
|
}
|
|
@@ -2330,7 +2430,7 @@ var AgentInitializer = class {
|
|
|
2330
2430
|
workspaceDir,
|
|
2331
2431
|
instanceMeta: { name, templateName: template.name },
|
|
2332
2432
|
template,
|
|
2333
|
-
logger:
|
|
2433
|
+
logger: logger15,
|
|
2334
2434
|
state: /* @__PURE__ */ new Map()
|
|
2335
2435
|
};
|
|
2336
2436
|
const pipelineResult = await this.pipeline.run(template.initializer.steps, stepContext);
|
|
@@ -2343,6 +2443,7 @@ var AgentInitializer = class {
|
|
|
2343
2443
|
}
|
|
2344
2444
|
}
|
|
2345
2445
|
const effectivePermissions = resolvePermissions(finalPermissions);
|
|
2446
|
+
const resolvedProvider = resolveProviderConfig(template.provider);
|
|
2346
2447
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2347
2448
|
const launchMode = overrides?.launchMode ?? this.options?.defaultLaunchMode ?? "direct";
|
|
2348
2449
|
const defaultPolicy = launchMode === "one-shot" ? "ephemeral" : "persistent";
|
|
@@ -2353,6 +2454,7 @@ var AgentInitializer = class {
|
|
|
2353
2454
|
templateVersion: template.version,
|
|
2354
2455
|
backendType: template.backend.type,
|
|
2355
2456
|
backendConfig: template.backend.config ? { ...template.backend.config } : void 0,
|
|
2457
|
+
providerConfig: resolvedProvider,
|
|
2356
2458
|
status: "created",
|
|
2357
2459
|
launchMode,
|
|
2358
2460
|
workspacePolicy: overrides?.workspacePolicy ?? defaultPolicy,
|
|
@@ -2367,13 +2469,13 @@ var AgentInitializer = class {
|
|
|
2367
2469
|
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
2368
2470
|
await symlink(workspaceDir, join10(this.instancesBaseDir, name), linkType);
|
|
2369
2471
|
}
|
|
2370
|
-
|
|
2472
|
+
logger15.info({ name, templateName, workspaceDir, customWorkDir: !!customWorkDir }, "Agent instance created");
|
|
2371
2473
|
return meta;
|
|
2372
2474
|
} catch (err) {
|
|
2373
2475
|
if (shouldCleanupOnError) {
|
|
2374
2476
|
await rm(workspaceDir, { recursive: true, force: true }).catch(() => {
|
|
2375
2477
|
});
|
|
2376
|
-
|
|
2478
|
+
logger15.debug({ workspaceDir }, "Cleaned up workspace after failed creation");
|
|
2377
2479
|
}
|
|
2378
2480
|
if (err instanceof ActantError) {
|
|
2379
2481
|
throw err;
|
|
@@ -2392,7 +2494,7 @@ var AgentInitializer = class {
|
|
|
2392
2494
|
if (await dirExists(workspaceDir)) {
|
|
2393
2495
|
try {
|
|
2394
2496
|
const meta2 = await readInstanceMeta(workspaceDir);
|
|
2395
|
-
|
|
2497
|
+
logger15.debug({ name }, "Existing instance found");
|
|
2396
2498
|
return { meta: meta2, created: false };
|
|
2397
2499
|
} catch (err) {
|
|
2398
2500
|
if (err instanceof InstanceCorruptedError3) {
|
|
@@ -2413,7 +2515,7 @@ var AgentInitializer = class {
|
|
|
2413
2515
|
async destroyInstance(name) {
|
|
2414
2516
|
const entryPath = join10(this.instancesBaseDir, name);
|
|
2415
2517
|
if (!await entryExists(entryPath)) {
|
|
2416
|
-
|
|
2518
|
+
logger15.warn({ name }, "Instance directory not found, nothing to destroy");
|
|
2417
2519
|
return;
|
|
2418
2520
|
}
|
|
2419
2521
|
if (await isSymlink(entryPath)) {
|
|
@@ -2423,13 +2525,30 @@ var AgentInitializer = class {
|
|
|
2423
2525
|
} catch {
|
|
2424
2526
|
}
|
|
2425
2527
|
await rm(entryPath, { recursive: true, force: true });
|
|
2426
|
-
|
|
2528
|
+
logger15.info({ name, targetDir }, "Symlinked agent instance unregistered");
|
|
2427
2529
|
} else {
|
|
2428
2530
|
await rm(entryPath, { recursive: true, force: true });
|
|
2429
|
-
|
|
2531
|
+
logger15.info({ name }, "Agent instance destroyed");
|
|
2430
2532
|
}
|
|
2431
2533
|
}
|
|
2432
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
|
+
}
|
|
2433
2552
|
async function dirExists(path) {
|
|
2434
2553
|
try {
|
|
2435
2554
|
await access2(path);
|
|
@@ -2458,11 +2577,13 @@ async function isSymlink(path) {
|
|
|
2458
2577
|
// src/initializer/context/context-materializer.ts
|
|
2459
2578
|
import { writeFile as writeFile7, mkdir as mkdir9 } from "fs/promises";
|
|
2460
2579
|
import { join as join11 } from "path";
|
|
2461
|
-
import { createLogger as
|
|
2462
|
-
var
|
|
2580
|
+
import { createLogger as createLogger17 } from "@actant/shared";
|
|
2581
|
+
var logger16 = createLogger17("context-materializer");
|
|
2463
2582
|
var BACKEND_CONFIG_DIR = {
|
|
2464
2583
|
cursor: ".cursor",
|
|
2584
|
+
"cursor-agent": ".cursor",
|
|
2465
2585
|
"claude-code": ".claude",
|
|
2586
|
+
pi: ".pi",
|
|
2466
2587
|
custom: ".cursor"
|
|
2467
2588
|
};
|
|
2468
2589
|
var ContextMaterializer = class {
|
|
@@ -2488,7 +2609,7 @@ var ContextMaterializer = class {
|
|
|
2488
2609
|
tasks.push(this.materializePrompts(workspaceDir, domainContext.prompts));
|
|
2489
2610
|
}
|
|
2490
2611
|
await Promise.all(tasks);
|
|
2491
|
-
|
|
2612
|
+
logger16.debug({ workspaceDir, backendType, configDir }, "Domain context materialized");
|
|
2492
2613
|
}
|
|
2493
2614
|
async materializeSkills(workspaceDir, skillNames) {
|
|
2494
2615
|
let content;
|
|
@@ -2603,16 +2724,16 @@ var InitializerStepExecutor = class {
|
|
|
2603
2724
|
};
|
|
2604
2725
|
|
|
2605
2726
|
// src/initializer/pipeline/step-registry.ts
|
|
2606
|
-
import { createLogger as
|
|
2607
|
-
var
|
|
2727
|
+
import { createLogger as createLogger18 } from "@actant/shared";
|
|
2728
|
+
var logger17 = createLogger18("step-registry");
|
|
2608
2729
|
var StepRegistry = class {
|
|
2609
2730
|
executors = /* @__PURE__ */ new Map();
|
|
2610
2731
|
register(executor) {
|
|
2611
2732
|
if (this.executors.has(executor.type)) {
|
|
2612
|
-
|
|
2733
|
+
logger17.warn({ type: executor.type }, "Overwriting existing step executor");
|
|
2613
2734
|
}
|
|
2614
2735
|
this.executors.set(executor.type, executor);
|
|
2615
|
-
|
|
2736
|
+
logger17.debug({ type: executor.type }, "Step executor registered");
|
|
2616
2737
|
}
|
|
2617
2738
|
get(type) {
|
|
2618
2739
|
return this.executors.get(type);
|
|
@@ -2871,10 +2992,10 @@ var NpmInstallStep = class extends InitializerStepExecutor {
|
|
|
2871
2992
|
return { valid: issues.length === 0, issues };
|
|
2872
2993
|
}
|
|
2873
2994
|
async execute(context, config) {
|
|
2874
|
-
const { packageManager = "npm", cwd = ".", args = [], registry } = config;
|
|
2995
|
+
const { packageManager = "npm", cwd = ".", args = [], registry: registry3 } = config;
|
|
2875
2996
|
const workDir = join16(context.workspaceDir, cwd);
|
|
2876
2997
|
const cmdArgs = ["install", ...args];
|
|
2877
|
-
if (
|
|
2998
|
+
if (registry3) cmdArgs.push("--registry", registry3);
|
|
2878
2999
|
context.logger.debug({ packageManager, cwd: workDir, args: cmdArgs }, "Installing dependencies");
|
|
2879
3000
|
const result = await runInstall(packageManager, cmdArgs, workDir);
|
|
2880
3001
|
if (result.exitCode !== 0) {
|
|
@@ -2916,56 +3037,151 @@ function runInstall(pm, args, cwd) {
|
|
|
2916
3037
|
|
|
2917
3038
|
// src/initializer/steps/index.ts
|
|
2918
3039
|
function createDefaultStepRegistry() {
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
return
|
|
3040
|
+
const registry3 = new StepRegistry();
|
|
3041
|
+
registry3.register(new MkdirStep());
|
|
3042
|
+
registry3.register(new ExecStep());
|
|
3043
|
+
registry3.register(new FileCopyStep());
|
|
3044
|
+
registry3.register(new GitCloneStep());
|
|
3045
|
+
registry3.register(new NpmInstallStep());
|
|
3046
|
+
return registry3;
|
|
2926
3047
|
}
|
|
2927
3048
|
|
|
2928
3049
|
// src/manager/agent-manager.ts
|
|
2929
3050
|
import { join as join17 } from "path";
|
|
3051
|
+
import { existsSync } from "fs";
|
|
2930
3052
|
import { rename as rename3, mkdir as mkdir11 } from "fs/promises";
|
|
2931
3053
|
import {
|
|
2932
3054
|
AgentNotFoundError,
|
|
2933
3055
|
AgentAlreadyRunningError,
|
|
2934
3056
|
AgentAlreadyAttachedError,
|
|
2935
3057
|
AgentNotAttachedError,
|
|
2936
|
-
|
|
3058
|
+
AgentLaunchError,
|
|
3059
|
+
createLogger as createLogger24
|
|
2937
3060
|
} from "@actant/shared";
|
|
2938
3061
|
|
|
2939
|
-
// src/manager/launcher/backend-
|
|
2940
|
-
var
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3062
|
+
// src/manager/launcher/backend-registry.ts
|
|
3063
|
+
var registry = /* @__PURE__ */ new Map();
|
|
3064
|
+
function registerBackend(descriptor) {
|
|
3065
|
+
registry.set(descriptor.type, descriptor);
|
|
3066
|
+
}
|
|
3067
|
+
function getBackendDescriptor(type) {
|
|
3068
|
+
const desc = registry.get(type);
|
|
3069
|
+
if (!desc) {
|
|
3070
|
+
throw new Error(
|
|
3071
|
+
`Backend "${type}" is not registered. Ensure the backend package is installed and registerBackend() was called at startup.`
|
|
3072
|
+
);
|
|
2946
3073
|
}
|
|
2947
|
-
|
|
3074
|
+
return desc;
|
|
3075
|
+
}
|
|
3076
|
+
function supportsMode(type, mode) {
|
|
3077
|
+
const desc = registry.get(type);
|
|
3078
|
+
return desc != null && desc.supportedModes.includes(mode);
|
|
3079
|
+
}
|
|
3080
|
+
function requireMode(type, mode) {
|
|
3081
|
+
const desc = getBackendDescriptor(type);
|
|
3082
|
+
if (!desc.supportedModes.includes(mode)) {
|
|
3083
|
+
const supported = desc.supportedModes.join(", ");
|
|
3084
|
+
throw new Error(
|
|
3085
|
+
`Backend "${type}" does not support "${mode}" mode. Supported modes: [${supported}]. ` + (mode === "resolve" ? `Use \`agent start\` or \`agent run\` instead.` : mode === "open" ? `This backend has no native TUI/UI to open.` : `Use \`agent resolve\` or \`agent open\` instead.`)
|
|
3086
|
+
);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
function getPlatformCommand(cmd) {
|
|
3090
|
+
return process.platform === "win32" ? cmd.win32 : cmd.default;
|
|
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
|
+
}
|
|
3100
|
+
|
|
3101
|
+
// src/manager/launcher/builtin-backends.ts
|
|
3102
|
+
function registerBuiltinBackends() {
|
|
3103
|
+
registerBackend({
|
|
3104
|
+
type: "cursor",
|
|
3105
|
+
supportedModes: ["resolve", "open"],
|
|
3106
|
+
resolveCommand: { win32: "cursor.cmd", default: "cursor" },
|
|
3107
|
+
openCommand: { win32: "cursor.cmd", default: "cursor" }
|
|
3108
|
+
});
|
|
3109
|
+
registerBackend({
|
|
3110
|
+
type: "cursor-agent",
|
|
3111
|
+
supportedModes: ["resolve", "open", "acp"],
|
|
3112
|
+
resolveCommand: { win32: "cursor.cmd", default: "cursor" },
|
|
3113
|
+
openCommand: { win32: "cursor.cmd", default: "cursor" }
|
|
3114
|
+
});
|
|
3115
|
+
registerBackend({
|
|
3116
|
+
type: "claude-code",
|
|
3117
|
+
supportedModes: ["resolve", "open", "acp"],
|
|
3118
|
+
resolveCommand: { win32: "claude-agent-acp.cmd", default: "claude-agent-acp" },
|
|
3119
|
+
openCommand: { win32: "claude.cmd", default: "claude" }
|
|
3120
|
+
});
|
|
3121
|
+
registerBackend({
|
|
3122
|
+
type: "custom",
|
|
3123
|
+
supportedModes: ["resolve"]
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
registerBuiltinBackends();
|
|
3127
|
+
|
|
3128
|
+
// src/manager/launcher/backend-resolver.ts
|
|
2948
3129
|
function isAcpBackend(backendType) {
|
|
2949
|
-
return backendType
|
|
3130
|
+
return supportsMode(backendType, "acp");
|
|
3131
|
+
}
|
|
3132
|
+
function isAcpOnlyBackend(backendType) {
|
|
3133
|
+
const desc = getBackendDescriptor(backendType);
|
|
3134
|
+
return supportsMode(backendType, "acp") && desc.acpOwnsProcess === true;
|
|
2950
3135
|
}
|
|
2951
3136
|
function buildArgs(backendType, workspaceDir, backendConfig) {
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
return configArgs.map(String);
|
|
2961
|
-
}
|
|
2962
|
-
return [workspaceDir];
|
|
2963
|
-
}
|
|
3137
|
+
const desc = getBackendDescriptor(backendType);
|
|
3138
|
+
if (backendType === "custom") {
|
|
3139
|
+
const configArgs = backendConfig?.args;
|
|
3140
|
+
if (Array.isArray(configArgs)) return configArgs.map(String);
|
|
3141
|
+
return [workspaceDir];
|
|
3142
|
+
}
|
|
3143
|
+
if (desc.supportedModes.includes("open") && !desc.supportedModes.includes("acp")) {
|
|
3144
|
+
return [workspaceDir];
|
|
2964
3145
|
}
|
|
3146
|
+
if (desc.supportedModes.includes("acp")) {
|
|
3147
|
+
return [];
|
|
3148
|
+
}
|
|
3149
|
+
return [workspaceDir];
|
|
2965
3150
|
}
|
|
2966
3151
|
function resolveBackend(backendType, workspaceDir, backendConfig) {
|
|
3152
|
+
requireMode(backendType, "resolve");
|
|
3153
|
+
const desc = getBackendDescriptor(backendType);
|
|
3154
|
+
const explicitPath = backendConfig?.executablePath;
|
|
3155
|
+
const command = typeof explicitPath === "string" && explicitPath.length > 0 ? explicitPath : desc.resolveCommand ? getPlatformCommand(desc.resolveCommand) : (() => {
|
|
3156
|
+
throw new Error(`Backend "${backendType}" has no resolveCommand configured.`);
|
|
3157
|
+
})();
|
|
3158
|
+
return {
|
|
3159
|
+
command,
|
|
3160
|
+
args: buildArgs(backendType, workspaceDir, backendConfig)
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
function openBackend(backendType, workspaceDir) {
|
|
3164
|
+
requireMode(backendType, "open");
|
|
3165
|
+
const desc = getBackendDescriptor(backendType);
|
|
3166
|
+
if (!desc.openCommand) {
|
|
3167
|
+
throw new Error(`Backend "${backendType}" has no openCommand configured.`);
|
|
3168
|
+
}
|
|
3169
|
+
return {
|
|
3170
|
+
command: getPlatformCommand(desc.openCommand),
|
|
3171
|
+
args: [workspaceDir]
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
function resolveAcpBackend(backendType, workspaceDir, backendConfig) {
|
|
3175
|
+
requireMode(backendType, "acp");
|
|
3176
|
+
const desc = getBackendDescriptor(backendType);
|
|
3177
|
+
if (desc.acpResolver) {
|
|
3178
|
+
return desc.acpResolver(workspaceDir, backendConfig);
|
|
3179
|
+
}
|
|
2967
3180
|
const explicitPath = backendConfig?.executablePath;
|
|
2968
|
-
const
|
|
3181
|
+
const commandSource = desc.acpCommand ?? desc.resolveCommand;
|
|
3182
|
+
const command = typeof explicitPath === "string" && explicitPath.length > 0 ? explicitPath : commandSource ? getPlatformCommand(commandSource) : (() => {
|
|
3183
|
+
throw new Error(`Backend "${backendType}" has no command configured for ACP spawn.`);
|
|
3184
|
+
})();
|
|
2969
3185
|
return {
|
|
2970
3186
|
command,
|
|
2971
3187
|
args: buildArgs(backendType, workspaceDir, backendConfig)
|
|
@@ -2973,7 +3189,7 @@ function resolveBackend(backendType, workspaceDir, backendConfig) {
|
|
|
2973
3189
|
}
|
|
2974
3190
|
|
|
2975
3191
|
// src/manager/launcher/process-watcher.ts
|
|
2976
|
-
import { createLogger as
|
|
3192
|
+
import { createLogger as createLogger19 } from "@actant/shared";
|
|
2977
3193
|
|
|
2978
3194
|
// src/manager/launcher/process-utils.ts
|
|
2979
3195
|
function isProcessAlive(pid) {
|
|
@@ -3009,7 +3225,7 @@ function isNodeError5(err) {
|
|
|
3009
3225
|
}
|
|
3010
3226
|
|
|
3011
3227
|
// src/manager/launcher/process-watcher.ts
|
|
3012
|
-
var
|
|
3228
|
+
var logger18 = createLogger19("process-watcher");
|
|
3013
3229
|
var DEFAULT_POLL_INTERVAL = 5e3;
|
|
3014
3230
|
var ProcessWatcher = class {
|
|
3015
3231
|
constructor(onProcessExit, options) {
|
|
@@ -3022,12 +3238,12 @@ var ProcessWatcher = class {
|
|
|
3022
3238
|
pollIntervalMs;
|
|
3023
3239
|
watch(instanceName, pid) {
|
|
3024
3240
|
this.watches.set(instanceName, { pid });
|
|
3025
|
-
|
|
3241
|
+
logger18.debug({ instanceName, pid }, "Watching process");
|
|
3026
3242
|
}
|
|
3027
3243
|
unwatch(instanceName) {
|
|
3028
3244
|
const removed = this.watches.delete(instanceName);
|
|
3029
3245
|
if (removed) {
|
|
3030
|
-
|
|
3246
|
+
logger18.debug({ instanceName }, "Unwatched process");
|
|
3031
3247
|
}
|
|
3032
3248
|
return removed;
|
|
3033
3249
|
}
|
|
@@ -3042,13 +3258,13 @@ var ProcessWatcher = class {
|
|
|
3042
3258
|
this.timer = setInterval(() => {
|
|
3043
3259
|
void this.poll();
|
|
3044
3260
|
}, this.pollIntervalMs);
|
|
3045
|
-
|
|
3261
|
+
logger18.info({ pollIntervalMs: this.pollIntervalMs }, "ProcessWatcher started");
|
|
3046
3262
|
}
|
|
3047
3263
|
stop() {
|
|
3048
3264
|
if (this.timer) {
|
|
3049
3265
|
clearInterval(this.timer);
|
|
3050
3266
|
this.timer = null;
|
|
3051
|
-
|
|
3267
|
+
logger18.info("ProcessWatcher stopped");
|
|
3052
3268
|
}
|
|
3053
3269
|
}
|
|
3054
3270
|
dispose() {
|
|
@@ -3070,11 +3286,11 @@ var ProcessWatcher = class {
|
|
|
3070
3286
|
}
|
|
3071
3287
|
for (const info of exited) {
|
|
3072
3288
|
this.watches.delete(info.instanceName);
|
|
3073
|
-
|
|
3289
|
+
logger18.info(info, "Process exited \u2014 removed from watch list");
|
|
3074
3290
|
try {
|
|
3075
3291
|
await this.onProcessExit(info);
|
|
3076
3292
|
} catch (err) {
|
|
3077
|
-
|
|
3293
|
+
logger18.error({ ...info, error: err }, "Error in process exit handler");
|
|
3078
3294
|
}
|
|
3079
3295
|
}
|
|
3080
3296
|
} finally {
|
|
@@ -3084,8 +3300,8 @@ var ProcessWatcher = class {
|
|
|
3084
3300
|
};
|
|
3085
3301
|
|
|
3086
3302
|
// src/manager/launch-mode-handler.ts
|
|
3087
|
-
import { createLogger as
|
|
3088
|
-
var
|
|
3303
|
+
import { createLogger as createLogger20 } from "@actant/shared";
|
|
3304
|
+
var logger19 = createLogger20("launch-mode-handler");
|
|
3089
3305
|
var DirectModeHandler = class {
|
|
3090
3306
|
mode = "direct";
|
|
3091
3307
|
getProcessExitAction(_instanceName) {
|
|
@@ -3107,11 +3323,11 @@ var AcpBackgroundModeHandler = class {
|
|
|
3107
3323
|
var AcpServiceModeHandler = class {
|
|
3108
3324
|
mode = "acp-service";
|
|
3109
3325
|
getProcessExitAction(instanceName) {
|
|
3110
|
-
|
|
3326
|
+
logger19.info({ instanceName }, "acp-service process exited \u2014 restart policy will be checked");
|
|
3111
3327
|
return { type: "restart" };
|
|
3112
3328
|
}
|
|
3113
3329
|
getRecoveryAction(instanceName) {
|
|
3114
|
-
|
|
3330
|
+
logger19.info({ instanceName }, "acp-service stale instance \u2014 will attempt recovery restart");
|
|
3115
3331
|
return { type: "restart" };
|
|
3116
3332
|
}
|
|
3117
3333
|
};
|
|
@@ -3138,8 +3354,8 @@ function getLaunchModeHandler(mode) {
|
|
|
3138
3354
|
}
|
|
3139
3355
|
|
|
3140
3356
|
// src/manager/restart-tracker.ts
|
|
3141
|
-
import { createLogger as
|
|
3142
|
-
var
|
|
3357
|
+
import { createLogger as createLogger21 } from "@actant/shared";
|
|
3358
|
+
var logger20 = createLogger21("restart-tracker");
|
|
3143
3359
|
var DEFAULT_RESTART_POLICY = {
|
|
3144
3360
|
maxRestarts: 5,
|
|
3145
3361
|
backoffBaseMs: 1e3,
|
|
@@ -3157,12 +3373,12 @@ var RestartTracker = class {
|
|
|
3157
3373
|
if (state.lastStartAt > 0) {
|
|
3158
3374
|
const stableMs = Date.now() - state.lastStartAt;
|
|
3159
3375
|
if (stableMs >= this.policy.resetAfterMs) {
|
|
3160
|
-
|
|
3376
|
+
logger20.info({ instanceName, stableMs, resetAfterMs: this.policy.resetAfterMs }, "Resetting restart counter \u2014 agent was stable");
|
|
3161
3377
|
state.count = 0;
|
|
3162
3378
|
}
|
|
3163
3379
|
}
|
|
3164
3380
|
if (state.count >= this.policy.maxRestarts) {
|
|
3165
|
-
|
|
3381
|
+
logger20.warn({ instanceName, count: state.count, maxRestarts: this.policy.maxRestarts }, "Restart limit exceeded");
|
|
3166
3382
|
return { allowed: false, delayMs: 0, attempt: state.count };
|
|
3167
3383
|
}
|
|
3168
3384
|
const delayMs = Math.min(
|
|
@@ -3175,7 +3391,7 @@ var RestartTracker = class {
|
|
|
3175
3391
|
const state = this.getOrCreate(instanceName);
|
|
3176
3392
|
state.count++;
|
|
3177
3393
|
state.lastRestartAt = Date.now();
|
|
3178
|
-
|
|
3394
|
+
logger20.debug({ instanceName, count: state.count }, "Restart recorded");
|
|
3179
3395
|
}
|
|
3180
3396
|
recordStart(instanceName) {
|
|
3181
3397
|
const state = this.getOrCreate(instanceName);
|
|
@@ -3202,8 +3418,8 @@ var RestartTracker = class {
|
|
|
3202
3418
|
|
|
3203
3419
|
// src/communicator/claude-code-communicator.ts
|
|
3204
3420
|
import { spawn as spawn4 } from "child_process";
|
|
3205
|
-
import { createLogger as
|
|
3206
|
-
var
|
|
3421
|
+
import { createLogger as createLogger22 } from "@actant/shared";
|
|
3422
|
+
var logger21 = createLogger22("claude-code-communicator");
|
|
3207
3423
|
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
3208
3424
|
var ClaudeCodeCommunicator = class {
|
|
3209
3425
|
executable;
|
|
@@ -3212,7 +3428,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3212
3428
|
}
|
|
3213
3429
|
async runPrompt(workspaceDir, prompt, options) {
|
|
3214
3430
|
const args = this.buildArgs(prompt, options, "json");
|
|
3215
|
-
|
|
3431
|
+
logger21.debug({ workspaceDir, args }, "Running claude-code prompt");
|
|
3216
3432
|
return new Promise((resolve3, reject) => {
|
|
3217
3433
|
const child = spawn4(this.executable, args, {
|
|
3218
3434
|
cwd: workspaceDir,
|
|
@@ -3235,7 +3451,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3235
3451
|
child.on("close", (code) => {
|
|
3236
3452
|
clearTimeout(timer);
|
|
3237
3453
|
if (code !== 0) {
|
|
3238
|
-
|
|
3454
|
+
logger21.warn({ code, stderr: stderr.slice(0, 500) }, "claude-code exited with error");
|
|
3239
3455
|
reject(new Error(`claude-code exited with code ${code}: ${stderr.slice(0, 500)}`));
|
|
3240
3456
|
return;
|
|
3241
3457
|
}
|
|
@@ -3245,7 +3461,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3245
3461
|
const sessionId = parsed["session_id"];
|
|
3246
3462
|
const subtype = parsed["subtype"];
|
|
3247
3463
|
if (subtype && subtype !== "success") {
|
|
3248
|
-
|
|
3464
|
+
logger21.warn({ subtype }, "claude-code returned non-success subtype");
|
|
3249
3465
|
}
|
|
3250
3466
|
let text;
|
|
3251
3467
|
if (result && result.length > 0) {
|
|
@@ -3269,7 +3485,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3269
3485
|
}
|
|
3270
3486
|
async *streamPrompt(workspaceDir, prompt, options) {
|
|
3271
3487
|
const args = this.buildArgs(prompt, options, "stream-json");
|
|
3272
|
-
|
|
3488
|
+
logger21.debug({ workspaceDir, args }, "Streaming claude-code prompt");
|
|
3273
3489
|
const child = spawn4(this.executable, args, {
|
|
3274
3490
|
cwd: workspaceDir,
|
|
3275
3491
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3366,11 +3582,11 @@ function parseStreamEvent(event) {
|
|
|
3366
3582
|
}
|
|
3367
3583
|
|
|
3368
3584
|
// src/communicator/cursor-communicator.ts
|
|
3369
|
-
import { createLogger as
|
|
3370
|
-
var
|
|
3585
|
+
import { createLogger as createLogger23 } from "@actant/shared";
|
|
3586
|
+
var logger22 = createLogger23("cursor-communicator");
|
|
3371
3587
|
var CursorCommunicator = class {
|
|
3372
3588
|
async runPrompt(_workspaceDir, _prompt, _options) {
|
|
3373
|
-
|
|
3589
|
+
logger22.warn("Cursor backend does not yet support programmatic communication");
|
|
3374
3590
|
throw new Error(
|
|
3375
3591
|
"Cursor backend does not support programmatic communication yet. Use claude-code backend for agent.run / agent.chat functionality."
|
|
3376
3592
|
);
|
|
@@ -3384,21 +3600,32 @@ var CursorCommunicator = class {
|
|
|
3384
3600
|
};
|
|
3385
3601
|
|
|
3386
3602
|
// src/communicator/create-communicator.ts
|
|
3387
|
-
|
|
3603
|
+
var registry2 = /* @__PURE__ */ new Map();
|
|
3604
|
+
function registerCommunicator(backendType, factory) {
|
|
3605
|
+
registry2.set(backendType, factory);
|
|
3606
|
+
}
|
|
3607
|
+
function createCommunicator(backendType, backendConfig) {
|
|
3608
|
+
const registered = registry2.get(backendType);
|
|
3609
|
+
if (registered) return registered(backendConfig);
|
|
3388
3610
|
switch (backendType) {
|
|
3389
3611
|
case "claude-code":
|
|
3390
3612
|
return new ClaudeCodeCommunicator();
|
|
3391
3613
|
case "cursor":
|
|
3614
|
+
case "cursor-agent":
|
|
3392
3615
|
return new CursorCommunicator();
|
|
3393
3616
|
case "custom":
|
|
3394
3617
|
throw new Error(
|
|
3395
3618
|
"Custom backend communicator not yet supported. Implement AgentCommunicator for your backend."
|
|
3396
3619
|
);
|
|
3620
|
+
case "pi":
|
|
3621
|
+
throw new Error(
|
|
3622
|
+
"Pi backend communicator not registered. Ensure @actant/pi is installed and initialized."
|
|
3623
|
+
);
|
|
3397
3624
|
}
|
|
3398
3625
|
}
|
|
3399
3626
|
|
|
3400
3627
|
// src/manager/agent-manager.ts
|
|
3401
|
-
var
|
|
3628
|
+
var logger23 = createLogger24("agent-manager");
|
|
3402
3629
|
var AgentManager = class {
|
|
3403
3630
|
constructor(initializer, launcher, instancesBaseDir, options) {
|
|
3404
3631
|
this.initializer = initializer;
|
|
@@ -3439,7 +3666,7 @@ var AgentManager = class {
|
|
|
3439
3666
|
const dir = join17(this.instancesBaseDir, meta.name);
|
|
3440
3667
|
const fixed = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3441
3668
|
this.cache.set(meta.name, fixed);
|
|
3442
|
-
|
|
3669
|
+
logger23.info({ name: meta.name, oldStatus: meta.status, launchMode: meta.launchMode, recoveryAction: action.type }, "Stale status corrected");
|
|
3443
3670
|
if (action.type === "restart") {
|
|
3444
3671
|
pendingRestarts.push(meta.name);
|
|
3445
3672
|
}
|
|
@@ -3451,7 +3678,7 @@ var AgentManager = class {
|
|
|
3451
3678
|
await this.moveToCorrupted(name);
|
|
3452
3679
|
}
|
|
3453
3680
|
this.watcher.start();
|
|
3454
|
-
|
|
3681
|
+
logger23.info({
|
|
3455
3682
|
valid: valid.length,
|
|
3456
3683
|
corrupted: corrupted.length,
|
|
3457
3684
|
pendingRestarts: pendingRestarts.length
|
|
@@ -3459,9 +3686,9 @@ var AgentManager = class {
|
|
|
3459
3686
|
for (const name of pendingRestarts) {
|
|
3460
3687
|
try {
|
|
3461
3688
|
await this.startAgent(name);
|
|
3462
|
-
|
|
3689
|
+
logger23.info({ name }, "Recovery restart succeeded");
|
|
3463
3690
|
} catch (err) {
|
|
3464
|
-
|
|
3691
|
+
logger23.error({ name, error: err }, "Recovery restart failed");
|
|
3465
3692
|
}
|
|
3466
3693
|
}
|
|
3467
3694
|
}
|
|
@@ -3486,13 +3713,16 @@ var AgentManager = class {
|
|
|
3486
3713
|
return { meta, created };
|
|
3487
3714
|
}
|
|
3488
3715
|
/**
|
|
3489
|
-
* Start an agent — launch the backend process.
|
|
3490
|
-
*
|
|
3716
|
+
* Start an agent — launch the backend process via ACP.
|
|
3717
|
+
* Requires the backend to support "acp" mode.
|
|
3718
|
+
* For acpOwnsProcess backends, ProcessLauncher is skipped.
|
|
3491
3719
|
* @throws {AgentNotFoundError} if agent is not in cache
|
|
3492
3720
|
* @throws {AgentAlreadyRunningError} if agent is already running
|
|
3721
|
+
* @throws {Error} if backend does not support "acp" mode
|
|
3493
3722
|
*/
|
|
3494
3723
|
async startAgent(name) {
|
|
3495
3724
|
const meta = this.requireAgent(name);
|
|
3725
|
+
requireMode(meta.backendType, "acp");
|
|
3496
3726
|
if (meta.status === "running" || meta.status === "starting") {
|
|
3497
3727
|
throw new AgentAlreadyRunningError(name);
|
|
3498
3728
|
}
|
|
@@ -3500,23 +3730,38 @@ var AgentManager = class {
|
|
|
3500
3730
|
const starting = await updateInstanceMeta(dir, { status: "starting" });
|
|
3501
3731
|
this.cache.set(name, starting);
|
|
3502
3732
|
try {
|
|
3503
|
-
const
|
|
3504
|
-
|
|
3505
|
-
if (
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3733
|
+
const acpOnly = isAcpOnlyBackend(meta.backendType);
|
|
3734
|
+
let pid;
|
|
3735
|
+
if (!acpOnly) {
|
|
3736
|
+
const proc = await this.launcher.launch(dir, starting);
|
|
3737
|
+
this.processes.set(name, proc);
|
|
3738
|
+
pid = proc.pid;
|
|
3739
|
+
}
|
|
3740
|
+
if (this.acpManager) {
|
|
3741
|
+
const { command, args } = resolveAcpBackend(meta.backendType, dir, meta.backendConfig);
|
|
3742
|
+
const providerEnv = buildProviderEnv(meta.providerConfig);
|
|
3743
|
+
const connResult = await this.acpManager.connect(name, {
|
|
3508
3744
|
command,
|
|
3509
3745
|
args,
|
|
3510
3746
|
cwd: dir,
|
|
3511
|
-
connectionOptions: {
|
|
3747
|
+
connectionOptions: {
|
|
3748
|
+
autoApprove: true,
|
|
3749
|
+
...Object.keys(providerEnv).length > 0 ? { env: providerEnv } : {}
|
|
3750
|
+
}
|
|
3512
3751
|
});
|
|
3513
|
-
|
|
3752
|
+
logger23.info({ name, acpOnly, providerType: meta.providerConfig?.type }, "ACP connection established");
|
|
3753
|
+
if (acpOnly && "pid" in connResult && typeof connResult.pid === "number") {
|
|
3754
|
+
pid = connResult.pid;
|
|
3755
|
+
this.processes.set(name, { pid, workspaceDir: dir, instanceName: name });
|
|
3756
|
+
}
|
|
3514
3757
|
}
|
|
3515
|
-
const running = await updateInstanceMeta(dir, { status: "running", pid
|
|
3758
|
+
const running = await updateInstanceMeta(dir, { status: "running", pid });
|
|
3516
3759
|
this.cache.set(name, running);
|
|
3517
|
-
|
|
3760
|
+
if (pid) {
|
|
3761
|
+
this.watcher.watch(name, pid);
|
|
3762
|
+
}
|
|
3518
3763
|
this.restartTracker.recordStart(name);
|
|
3519
|
-
|
|
3764
|
+
logger23.info({ name, pid, launchMode: starting.launchMode, acp: true }, "Agent started");
|
|
3520
3765
|
} catch (err) {
|
|
3521
3766
|
if (this.acpManager?.has(name)) {
|
|
3522
3767
|
await this.acpManager.disconnect(name).catch(() => {
|
|
@@ -3524,7 +3769,11 @@ var AgentManager = class {
|
|
|
3524
3769
|
}
|
|
3525
3770
|
const errored = await updateInstanceMeta(dir, { status: "error" });
|
|
3526
3771
|
this.cache.set(name, errored);
|
|
3527
|
-
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
|
+
));
|
|
3528
3777
|
}
|
|
3529
3778
|
}
|
|
3530
3779
|
/**
|
|
@@ -3535,7 +3784,7 @@ var AgentManager = class {
|
|
|
3535
3784
|
const meta = this.requireAgent(name);
|
|
3536
3785
|
const dir = join17(this.instancesBaseDir, name);
|
|
3537
3786
|
if (meta.status !== "running" && meta.status !== "starting") {
|
|
3538
|
-
|
|
3787
|
+
logger23.warn({ name, status: meta.status }, "Agent is not running, setting to stopped");
|
|
3539
3788
|
const stopped2 = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3540
3789
|
this.cache.set(name, stopped2);
|
|
3541
3790
|
return;
|
|
@@ -3545,7 +3794,7 @@ var AgentManager = class {
|
|
|
3545
3794
|
this.cache.set(name, stopping);
|
|
3546
3795
|
if (this.acpManager?.has(name)) {
|
|
3547
3796
|
await this.acpManager.disconnect(name).catch((err) => {
|
|
3548
|
-
|
|
3797
|
+
logger23.warn({ name, error: err }, "Error disconnecting ACP during stop");
|
|
3549
3798
|
});
|
|
3550
3799
|
}
|
|
3551
3800
|
const proc = this.processes.get(name);
|
|
@@ -3555,11 +3804,14 @@ var AgentManager = class {
|
|
|
3555
3804
|
}
|
|
3556
3805
|
const stopped = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3557
3806
|
this.cache.set(name, stopped);
|
|
3558
|
-
|
|
3807
|
+
logger23.info({ name }, "Agent stopped");
|
|
3559
3808
|
}
|
|
3560
3809
|
/** Destroy an agent — stop it if running, then remove workspace. */
|
|
3561
3810
|
async destroyAgent(name) {
|
|
3562
3811
|
const meta = this.cache.get(name);
|
|
3812
|
+
if (!meta && !existsSync(join17(this.instancesBaseDir, name))) {
|
|
3813
|
+
throw new AgentNotFoundError(name);
|
|
3814
|
+
}
|
|
3563
3815
|
if (meta && (meta.status === "running" || meta.status === "starting")) {
|
|
3564
3816
|
await this.stopAgent(name);
|
|
3565
3817
|
}
|
|
@@ -3568,7 +3820,7 @@ var AgentManager = class {
|
|
|
3568
3820
|
await this.initializer.destroyInstance(name);
|
|
3569
3821
|
this.cache.delete(name);
|
|
3570
3822
|
this.processes.delete(name);
|
|
3571
|
-
|
|
3823
|
+
logger23.info({ name }, "Agent destroyed");
|
|
3572
3824
|
}
|
|
3573
3825
|
/** Get agent metadata by name. */
|
|
3574
3826
|
getAgent(name) {
|
|
@@ -3611,6 +3863,17 @@ var AgentManager = class {
|
|
|
3611
3863
|
created
|
|
3612
3864
|
};
|
|
3613
3865
|
}
|
|
3866
|
+
/**
|
|
3867
|
+
* Open an agent's native TUI/UI (e.g. `cursor <dir>`).
|
|
3868
|
+
* Requires the backend to support "open" mode.
|
|
3869
|
+
* @throws if backend does not support "open" mode
|
|
3870
|
+
*/
|
|
3871
|
+
async openAgent(name) {
|
|
3872
|
+
const meta = this.requireAgent(name);
|
|
3873
|
+
const dir = join17(this.instancesBaseDir, name);
|
|
3874
|
+
const resolved = openBackend(meta.backendType, dir);
|
|
3875
|
+
return resolved;
|
|
3876
|
+
}
|
|
3614
3877
|
/**
|
|
3615
3878
|
* Register an externally-spawned process with the manager.
|
|
3616
3879
|
* Sets processOwnership to "external" and registers ProcessWatcher monitoring.
|
|
@@ -3622,6 +3885,17 @@ var AgentManager = class {
|
|
|
3622
3885
|
if (meta.status === "running" && meta.pid != null) {
|
|
3623
3886
|
throw new AgentAlreadyAttachedError(name);
|
|
3624
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
|
+
}
|
|
3625
3899
|
const dir = join17(this.instancesBaseDir, name);
|
|
3626
3900
|
const mergedMetadata = attachMetadata ? { ...meta.metadata, ...attachMetadata } : meta.metadata;
|
|
3627
3901
|
const updated = await updateInstanceMeta(dir, {
|
|
@@ -3633,7 +3907,7 @@ var AgentManager = class {
|
|
|
3633
3907
|
this.cache.set(name, updated);
|
|
3634
3908
|
this.processes.set(name, { pid, workspaceDir: dir, instanceName: name });
|
|
3635
3909
|
this.watcher.watch(name, pid);
|
|
3636
|
-
|
|
3910
|
+
logger23.info({ name, pid }, "External process attached");
|
|
3637
3911
|
return updated;
|
|
3638
3912
|
}
|
|
3639
3913
|
/**
|
|
@@ -3656,7 +3930,7 @@ var AgentManager = class {
|
|
|
3656
3930
|
processOwnership: "managed"
|
|
3657
3931
|
});
|
|
3658
3932
|
this.cache.set(name, updated);
|
|
3659
|
-
|
|
3933
|
+
logger23.info({ name }, "External process detached");
|
|
3660
3934
|
let workspaceCleaned = false;
|
|
3661
3935
|
if (options?.cleanup && meta.workspacePolicy === "ephemeral") {
|
|
3662
3936
|
await this.destroyAgent(name);
|
|
@@ -3674,7 +3948,7 @@ var AgentManager = class {
|
|
|
3674
3948
|
const conn = this.acpManager.getConnection(name);
|
|
3675
3949
|
const sessionId = this.acpManager.getPrimarySessionId(name);
|
|
3676
3950
|
if (conn && sessionId) {
|
|
3677
|
-
|
|
3951
|
+
logger23.debug({ name, sessionId }, "Sending prompt via ACP");
|
|
3678
3952
|
const result = await conn.prompt(sessionId, prompt);
|
|
3679
3953
|
return { text: result.text, sessionId };
|
|
3680
3954
|
}
|
|
@@ -3685,14 +3959,46 @@ var AgentManager = class {
|
|
|
3685
3959
|
}
|
|
3686
3960
|
/**
|
|
3687
3961
|
* Send a prompt to an agent and stream the response.
|
|
3688
|
-
* Uses ACP connection if available, otherwise falls back to
|
|
3962
|
+
* Uses ACP connection if available, otherwise falls back to communicator.
|
|
3689
3963
|
*/
|
|
3690
3964
|
streamPrompt(name, prompt, options) {
|
|
3691
3965
|
const meta = this.requireAgent(name);
|
|
3966
|
+
if (this.acpManager?.has(name)) {
|
|
3967
|
+
const conn = this.acpManager.getConnection(name);
|
|
3968
|
+
const sessionId = this.acpManager.getPrimarySessionId(name);
|
|
3969
|
+
if (conn && sessionId) {
|
|
3970
|
+
logger23.debug({ name, sessionId }, "Streaming prompt via ACP");
|
|
3971
|
+
return this.streamFromAcp(conn, sessionId, prompt);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3692
3974
|
const dir = join17(this.instancesBaseDir, name);
|
|
3693
3975
|
const communicator = createCommunicator(meta.backendType);
|
|
3694
3976
|
return communicator.streamPrompt(dir, prompt, options);
|
|
3695
3977
|
}
|
|
3978
|
+
async *streamFromAcp(conn, sessionId, prompt) {
|
|
3979
|
+
try {
|
|
3980
|
+
for await (const event of conn.streamPrompt(sessionId, prompt)) {
|
|
3981
|
+
const record = event;
|
|
3982
|
+
const type = record["type"];
|
|
3983
|
+
if (type === "text" || type === "assistant") {
|
|
3984
|
+
const content = record["content"] ?? record["message"] ?? "";
|
|
3985
|
+
yield { type: "text", content };
|
|
3986
|
+
} else if (type === "tool_use") {
|
|
3987
|
+
const toolName = record["name"];
|
|
3988
|
+
yield { type: "tool_use", content: toolName ? `[Tool: ${toolName}]` : "" };
|
|
3989
|
+
} else if (type === "result") {
|
|
3990
|
+
yield { type: "result", content: record["result"] ?? "" };
|
|
3991
|
+
} else if (type === "error") {
|
|
3992
|
+
const errMsg = record["error"]?.["message"];
|
|
3993
|
+
yield { type: "error", content: errMsg ?? "Unknown error" };
|
|
3994
|
+
} else if (typeof record["content"] === "string") {
|
|
3995
|
+
yield { type: "text", content: record["content"] };
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
} catch (err) {
|
|
3999
|
+
yield { type: "error", content: err instanceof Error ? err.message : String(err) };
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
3696
4002
|
/**
|
|
3697
4003
|
* Send a message to a running agent via its ACP session.
|
|
3698
4004
|
* Unlike runPrompt, this requires the agent to be started with ACP.
|
|
@@ -3733,7 +4039,7 @@ var AgentManager = class {
|
|
|
3733
4039
|
}
|
|
3734
4040
|
const handler = getLaunchModeHandler(meta.launchMode);
|
|
3735
4041
|
const action = handler.getProcessExitAction(instanceName, meta);
|
|
3736
|
-
|
|
4042
|
+
logger23.warn({ instanceName, pid, launchMode: meta.launchMode, action: action.type, previousStatus: meta.status }, "Agent process exited unexpectedly");
|
|
3737
4043
|
if (this.acpManager?.has(instanceName)) {
|
|
3738
4044
|
await this.acpManager.disconnect(instanceName).catch(() => {
|
|
3739
4045
|
});
|
|
@@ -3754,28 +4060,28 @@ var AgentManager = class {
|
|
|
3754
4060
|
if (!decision.allowed) {
|
|
3755
4061
|
const errored = await updateInstanceMeta(dir, { status: "error" });
|
|
3756
4062
|
this.cache.set(instanceName, errored);
|
|
3757
|
-
|
|
4063
|
+
logger23.error({ instanceName, attempt: decision.attempt }, "Restart limit exceeded \u2014 marking as error");
|
|
3758
4064
|
break;
|
|
3759
4065
|
}
|
|
3760
|
-
|
|
4066
|
+
logger23.info({ instanceName, attempt: decision.attempt, delayMs: decision.delayMs }, "Scheduling crash restart with backoff");
|
|
3761
4067
|
if (decision.delayMs > 0) {
|
|
3762
4068
|
await delay(decision.delayMs);
|
|
3763
4069
|
}
|
|
3764
4070
|
this.restartTracker.recordRestart(instanceName);
|
|
3765
4071
|
try {
|
|
3766
4072
|
await this.startAgent(instanceName);
|
|
3767
|
-
|
|
4073
|
+
logger23.info({ instanceName, attempt: decision.attempt }, "Crash restart succeeded");
|
|
3768
4074
|
} catch (err) {
|
|
3769
|
-
|
|
4075
|
+
logger23.error({ instanceName, attempt: decision.attempt, error: err }, "Crash restart failed");
|
|
3770
4076
|
}
|
|
3771
4077
|
break;
|
|
3772
4078
|
}
|
|
3773
4079
|
case "destroy":
|
|
3774
4080
|
try {
|
|
3775
4081
|
await this.destroyAgent(instanceName);
|
|
3776
|
-
|
|
4082
|
+
logger23.info({ instanceName }, "One-shot agent destroyed after exit");
|
|
3777
4083
|
} catch (err) {
|
|
3778
|
-
|
|
4084
|
+
logger23.error({ instanceName, error: err }, "One-shot auto-destroy failed");
|
|
3779
4085
|
}
|
|
3780
4086
|
break;
|
|
3781
4087
|
case "mark-stopped":
|
|
@@ -3795,12 +4101,39 @@ var AgentManager = class {
|
|
|
3795
4101
|
const src = join17(this.instancesBaseDir, name);
|
|
3796
4102
|
const dest = join17(this.corruptedDir, `${name}-${Date.now()}`);
|
|
3797
4103
|
await rename3(src, dest);
|
|
3798
|
-
|
|
4104
|
+
logger23.warn({ name, dest }, "Corrupted instance moved");
|
|
3799
4105
|
} catch (err) {
|
|
3800
|
-
|
|
4106
|
+
logger23.error({ name, error: err }, "Failed to move corrupted instance");
|
|
3801
4107
|
}
|
|
3802
4108
|
}
|
|
3803
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
|
+
}
|
|
3804
4137
|
|
|
3805
4138
|
// src/manager/launcher/mock-launcher.ts
|
|
3806
4139
|
var nextPid = 1e4;
|
|
@@ -3823,14 +4156,14 @@ var MockLauncher = class {
|
|
|
3823
4156
|
|
|
3824
4157
|
// src/manager/launcher/process-launcher.ts
|
|
3825
4158
|
import { spawn as spawn5 } from "child_process";
|
|
3826
|
-
import { AgentLaunchError, createLogger as
|
|
4159
|
+
import { AgentLaunchError as AgentLaunchError2, createLogger as createLogger26 } from "@actant/shared";
|
|
3827
4160
|
|
|
3828
4161
|
// src/manager/launcher/process-log-writer.ts
|
|
3829
4162
|
import { createWriteStream } from "fs";
|
|
3830
4163
|
import { mkdir as mkdir12, rename as rename4, stat as stat7, readFile as readFile5 } from "fs/promises";
|
|
3831
4164
|
import { join as join18 } from "path";
|
|
3832
|
-
import { createLogger as
|
|
3833
|
-
var
|
|
4165
|
+
import { createLogger as createLogger25 } from "@actant/shared";
|
|
4166
|
+
var logger24 = createLogger25("process-log-writer");
|
|
3834
4167
|
var DEFAULT_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
3835
4168
|
var DEFAULT_MAX_FILES = 3;
|
|
3836
4169
|
var ProcessLogWriter = class {
|
|
@@ -3867,7 +4200,7 @@ var ProcessLogWriter = class {
|
|
|
3867
4200
|
} catch {
|
|
3868
4201
|
this.stderrBytes = 0;
|
|
3869
4202
|
}
|
|
3870
|
-
|
|
4203
|
+
logger24.debug({ logsDir: this.logsDir }, "Log writer initialized");
|
|
3871
4204
|
}
|
|
3872
4205
|
/**
|
|
3873
4206
|
* Attach to readable streams from a spawned process.
|
|
@@ -3949,7 +4282,7 @@ var ProcessLogWriter = class {
|
|
|
3949
4282
|
this.stderrStream = newStream;
|
|
3950
4283
|
this.stderrBytes = 0;
|
|
3951
4284
|
}
|
|
3952
|
-
|
|
4285
|
+
logger24.debug({ filename }, "Log file rotated");
|
|
3953
4286
|
}
|
|
3954
4287
|
closeStream(stream) {
|
|
3955
4288
|
if (!stream) return Promise.resolve();
|
|
@@ -3960,7 +4293,7 @@ var ProcessLogWriter = class {
|
|
|
3960
4293
|
};
|
|
3961
4294
|
|
|
3962
4295
|
// src/manager/launcher/process-launcher.ts
|
|
3963
|
-
var
|
|
4296
|
+
var logger25 = createLogger26("process-launcher");
|
|
3964
4297
|
var DEFAULT_TERMINATE_TIMEOUT = 5e3;
|
|
3965
4298
|
var DEFAULT_SPAWN_VERIFY_DELAY = 500;
|
|
3966
4299
|
var ProcessLauncher = class {
|
|
@@ -3982,7 +4315,7 @@ var ProcessLauncher = class {
|
|
|
3982
4315
|
meta.backendConfig
|
|
3983
4316
|
);
|
|
3984
4317
|
const useAcp = isAcpBackend(meta.backendType);
|
|
3985
|
-
|
|
4318
|
+
logger25.info({ name: meta.name, command, args, backendType: meta.backendType, acp: useAcp }, "Spawning backend process");
|
|
3986
4319
|
const captureNonAcpLogs = !useAcp && this.enableProcessLogs;
|
|
3987
4320
|
let stdio;
|
|
3988
4321
|
if (useAcp) {
|
|
@@ -4014,7 +4347,7 @@ var ProcessLauncher = class {
|
|
|
4014
4347
|
}
|
|
4015
4348
|
});
|
|
4016
4349
|
if ("error" in spawnResult) {
|
|
4017
|
-
throw new
|
|
4350
|
+
throw new AgentLaunchError2(meta.name, spawnResult.error);
|
|
4018
4351
|
}
|
|
4019
4352
|
const pid = spawnResult.pid;
|
|
4020
4353
|
if (!useAcp) {
|
|
@@ -4025,27 +4358,27 @@ var ProcessLauncher = class {
|
|
|
4025
4358
|
earlyExit = true;
|
|
4026
4359
|
});
|
|
4027
4360
|
child.on("error", (err) => {
|
|
4028
|
-
|
|
4361
|
+
logger25.error({ name: meta.name, pid, error: err }, "Backend process error after spawn");
|
|
4029
4362
|
});
|
|
4030
4363
|
if (this.spawnVerifyDelayMs > 0) {
|
|
4031
4364
|
await delay(this.spawnVerifyDelayMs);
|
|
4032
4365
|
if (earlyExit || !isProcessAlive(pid)) {
|
|
4033
|
-
throw new
|
|
4366
|
+
throw new AgentLaunchError2(
|
|
4034
4367
|
meta.name,
|
|
4035
4368
|
new Error(`Process exited immediately after spawn (pid=${pid}, command=${command})`)
|
|
4036
4369
|
);
|
|
4037
4370
|
}
|
|
4038
4371
|
}
|
|
4039
|
-
|
|
4372
|
+
logger25.info({ name: meta.name, pid, command, acp: useAcp }, "Backend process spawned");
|
|
4040
4373
|
if (captureNonAcpLogs && child.stdout && child.stderr) {
|
|
4041
4374
|
const logWriter = new ProcessLogWriter(workspaceDir, this.logWriterOptions);
|
|
4042
4375
|
try {
|
|
4043
4376
|
await logWriter.initialize();
|
|
4044
4377
|
logWriter.attach(child.stdout, child.stderr);
|
|
4045
4378
|
this.logWriters.set(meta.name, logWriter);
|
|
4046
|
-
|
|
4379
|
+
logger25.debug({ name: meta.name }, "Process log capture enabled");
|
|
4047
4380
|
} catch (err) {
|
|
4048
|
-
|
|
4381
|
+
logger25.warn({ name: meta.name, error: err }, "Failed to initialize log writer, continuing without log capture");
|
|
4049
4382
|
}
|
|
4050
4383
|
}
|
|
4051
4384
|
const result = {
|
|
@@ -4074,27 +4407,27 @@ var ProcessLauncher = class {
|
|
|
4074
4407
|
this.logWriters.delete(instanceName);
|
|
4075
4408
|
}
|
|
4076
4409
|
if (!isProcessAlive(pid)) {
|
|
4077
|
-
|
|
4410
|
+
logger25.info({ instanceName, pid }, "Process already exited");
|
|
4078
4411
|
return;
|
|
4079
4412
|
}
|
|
4080
|
-
|
|
4413
|
+
logger25.info({ instanceName, pid }, "Sending SIGTERM");
|
|
4081
4414
|
sendSignal(pid, "SIGTERM");
|
|
4082
4415
|
const deadline = Date.now() + this.terminateTimeoutMs;
|
|
4083
4416
|
const pollInterval = 200;
|
|
4084
4417
|
while (Date.now() < deadline) {
|
|
4085
4418
|
await delay(pollInterval);
|
|
4086
4419
|
if (!isProcessAlive(pid)) {
|
|
4087
|
-
|
|
4420
|
+
logger25.info({ instanceName, pid }, "Process terminated gracefully");
|
|
4088
4421
|
return;
|
|
4089
4422
|
}
|
|
4090
4423
|
}
|
|
4091
|
-
|
|
4424
|
+
logger25.warn({ instanceName, pid }, "Process did not exit after SIGTERM, sending SIGKILL");
|
|
4092
4425
|
sendSignal(pid, "SIGKILL");
|
|
4093
4426
|
await delay(500);
|
|
4094
4427
|
if (isProcessAlive(pid)) {
|
|
4095
|
-
|
|
4428
|
+
logger25.error({ instanceName, pid }, "Process still alive after SIGKILL");
|
|
4096
4429
|
} else {
|
|
4097
|
-
|
|
4430
|
+
logger25.info({ instanceName, pid }, "Process killed with SIGKILL");
|
|
4098
4431
|
}
|
|
4099
4432
|
}
|
|
4100
4433
|
};
|
|
@@ -4116,7 +4449,10 @@ var SkillDefinitionSchema = z4.object({
|
|
|
4116
4449
|
name: z4.string().min(1),
|
|
4117
4450
|
description: z4.string().optional(),
|
|
4118
4451
|
content: z4.string().min(1),
|
|
4119
|
-
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()
|
|
4120
4456
|
}).passthrough();
|
|
4121
4457
|
var SkillManager = class extends BaseComponentManager {
|
|
4122
4458
|
componentType = "Skill";
|
|
@@ -4344,8 +4680,8 @@ var PluginManager = class extends BaseComponentManager {
|
|
|
4344
4680
|
};
|
|
4345
4681
|
|
|
4346
4682
|
// src/permissions/permission-policy-enforcer.ts
|
|
4347
|
-
import { createLogger as
|
|
4348
|
-
var
|
|
4683
|
+
import { createLogger as createLogger27 } from "@actant/shared";
|
|
4684
|
+
var logger26 = createLogger27("permission-policy-enforcer");
|
|
4349
4685
|
var KIND_TO_TOOL = {
|
|
4350
4686
|
read: "Read",
|
|
4351
4687
|
edit: "Edit",
|
|
@@ -4362,7 +4698,7 @@ var PermissionPolicyEnforcer = class {
|
|
|
4362
4698
|
}
|
|
4363
4699
|
updateConfig(config) {
|
|
4364
4700
|
this.config = { ...config };
|
|
4365
|
-
|
|
4701
|
+
logger26.info("Permission policy config updated");
|
|
4366
4702
|
}
|
|
4367
4703
|
getConfig() {
|
|
4368
4704
|
return this.config;
|
|
@@ -4520,8 +4856,8 @@ function escapeRegex(str) {
|
|
|
4520
4856
|
}
|
|
4521
4857
|
|
|
4522
4858
|
// src/permissions/permission-audit.ts
|
|
4523
|
-
import { createLogger as
|
|
4524
|
-
var
|
|
4859
|
+
import { createLogger as createLogger28 } from "@actant/shared";
|
|
4860
|
+
var logger27 = createLogger28("permission-audit");
|
|
4525
4861
|
var PermissionAuditLogger = class {
|
|
4526
4862
|
instanceName;
|
|
4527
4863
|
constructor(instanceName) {
|
|
@@ -4534,7 +4870,7 @@ var PermissionAuditLogger = class {
|
|
|
4534
4870
|
...detail,
|
|
4535
4871
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4536
4872
|
};
|
|
4537
|
-
|
|
4873
|
+
logger27.info(entry, `[audit] ${event}`);
|
|
4538
4874
|
}
|
|
4539
4875
|
logEvaluation(toolCall, decision) {
|
|
4540
4876
|
this.log("permission.evaluated", { toolCall, decision });
|
|
@@ -4555,8 +4891,8 @@ var PermissionAuditLogger = class {
|
|
|
4555
4891
|
|
|
4556
4892
|
// src/session/session-registry.ts
|
|
4557
4893
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
4558
|
-
import { createLogger as
|
|
4559
|
-
var
|
|
4894
|
+
import { createLogger as createLogger29 } from "@actant/shared";
|
|
4895
|
+
var logger28 = createLogger29("session-registry");
|
|
4560
4896
|
var DEFAULT_IDLE_TTL_MS = 30 * 60 * 1e3;
|
|
4561
4897
|
var TTL_CHECK_INTERVAL_MS = 60 * 1e3;
|
|
4562
4898
|
var SessionRegistry = class {
|
|
@@ -4587,7 +4923,7 @@ var SessionRegistry = class {
|
|
|
4587
4923
|
idleTtlMs: opts.idleTtlMs ?? this.defaultIdleTtlMs
|
|
4588
4924
|
};
|
|
4589
4925
|
this.sessions.set(session.sessionId, session);
|
|
4590
|
-
|
|
4926
|
+
logger28.info({ sessionId: session.sessionId, agentName: opts.agentName, clientId: opts.clientId }, "Session created");
|
|
4591
4927
|
return session;
|
|
4592
4928
|
}
|
|
4593
4929
|
/** Get a session by ID, or undefined if not found. */
|
|
@@ -4619,7 +4955,7 @@ var SessionRegistry = class {
|
|
|
4619
4955
|
session.clientId = null;
|
|
4620
4956
|
session.state = "idle";
|
|
4621
4957
|
session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4622
|
-
|
|
4958
|
+
logger28.info({ sessionId, agentName: session.agentName }, "Session released to idle");
|
|
4623
4959
|
}
|
|
4624
4960
|
/**
|
|
4625
4961
|
* Resume an idle session, binding it to a (potentially new) client.
|
|
@@ -4632,7 +4968,7 @@ var SessionRegistry = class {
|
|
|
4632
4968
|
session.clientId = clientId;
|
|
4633
4969
|
session.state = "active";
|
|
4634
4970
|
session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4635
|
-
|
|
4971
|
+
logger28.info({ sessionId, clientId, agentName: session.agentName }, "Session resumed");
|
|
4636
4972
|
return true;
|
|
4637
4973
|
}
|
|
4638
4974
|
/** Explicitly close and remove a session. */
|
|
@@ -4640,7 +4976,7 @@ var SessionRegistry = class {
|
|
|
4640
4976
|
const session = this.sessions.get(sessionId);
|
|
4641
4977
|
if (!session) return false;
|
|
4642
4978
|
this.sessions.delete(sessionId);
|
|
4643
|
-
|
|
4979
|
+
logger28.info({ sessionId, agentName: session.agentName }, "Session closed");
|
|
4644
4980
|
return true;
|
|
4645
4981
|
}
|
|
4646
4982
|
/** Close all sessions for a given agent. */
|
|
@@ -4653,7 +4989,7 @@ var SessionRegistry = class {
|
|
|
4653
4989
|
}
|
|
4654
4990
|
}
|
|
4655
4991
|
if (count > 0) {
|
|
4656
|
-
|
|
4992
|
+
logger28.info({ agentName, count }, "Sessions closed for agent");
|
|
4657
4993
|
}
|
|
4658
4994
|
return count;
|
|
4659
4995
|
}
|
|
@@ -4677,7 +5013,7 @@ var SessionRegistry = class {
|
|
|
4677
5013
|
if (now - lastActivity > session.idleTtlMs) {
|
|
4678
5014
|
session.state = "expired";
|
|
4679
5015
|
this.sessions.delete(id);
|
|
4680
|
-
|
|
5016
|
+
logger28.info({ sessionId: id, agentName: session.agentName, idleMs: now - lastActivity }, "Session expired (TTL)");
|
|
4681
5017
|
this.onExpireCallback?.(session);
|
|
4682
5018
|
}
|
|
4683
5019
|
}
|
|
@@ -4689,13 +5025,13 @@ import { join as join20 } from "path";
|
|
|
4689
5025
|
import { mkdir as mkdir13, rm as rm5 } from "fs/promises";
|
|
4690
5026
|
import { execFile } from "child_process";
|
|
4691
5027
|
import { promisify } from "util";
|
|
4692
|
-
import { createLogger as
|
|
5028
|
+
import { createLogger as createLogger31 } from "@actant/shared";
|
|
4693
5029
|
|
|
4694
5030
|
// src/source/local-source.ts
|
|
4695
5031
|
import { readFile as readFile6, readdir as readdir5, stat as stat8 } from "fs/promises";
|
|
4696
5032
|
import { join as join19, extname as extname3 } from "path";
|
|
4697
|
-
import { createLogger as
|
|
4698
|
-
var
|
|
5033
|
+
import { createLogger as createLogger30 } from "@actant/shared";
|
|
5034
|
+
var logger29 = createLogger30("local-source");
|
|
4699
5035
|
var LocalSource = class {
|
|
4700
5036
|
type = "local";
|
|
4701
5037
|
packageName;
|
|
@@ -4704,6 +5040,9 @@ var LocalSource = class {
|
|
|
4704
5040
|
this.packageName = packageName;
|
|
4705
5041
|
this.config = config;
|
|
4706
5042
|
}
|
|
5043
|
+
getRootDir() {
|
|
5044
|
+
return this.config.path;
|
|
5045
|
+
}
|
|
4707
5046
|
async fetch() {
|
|
4708
5047
|
return this.loadPackage();
|
|
4709
5048
|
}
|
|
@@ -4723,7 +5062,7 @@ var LocalSource = class {
|
|
|
4723
5062
|
this.loadPresets(rootDir, manifest.presets),
|
|
4724
5063
|
this.loadJsonDir(rootDir, manifest.components?.templates, "templates")
|
|
4725
5064
|
]);
|
|
4726
|
-
|
|
5065
|
+
logger29.info({ packageName: this.packageName, rootDir }, "Local package loaded");
|
|
4727
5066
|
return { manifest, skills, prompts, mcpServers, workflows, presets, templates };
|
|
4728
5067
|
}
|
|
4729
5068
|
async loadManifest(rootDir) {
|
|
@@ -4732,7 +5071,7 @@ var LocalSource = class {
|
|
|
4732
5071
|
const raw = await readFile6(manifestPath, "utf-8");
|
|
4733
5072
|
return JSON.parse(raw);
|
|
4734
5073
|
} catch {
|
|
4735
|
-
|
|
5074
|
+
logger29.debug({ rootDir }, "No actant.json found, scanning directories");
|
|
4736
5075
|
return { name: this.packageName };
|
|
4737
5076
|
}
|
|
4738
5077
|
}
|
|
@@ -4744,7 +5083,7 @@ var LocalSource = class {
|
|
|
4744
5083
|
const raw = await readFile6(join19(rootDir, relPath), "utf-8");
|
|
4745
5084
|
items2.push(JSON.parse(raw));
|
|
4746
5085
|
} catch (err) {
|
|
4747
|
-
|
|
5086
|
+
logger29.warn({ file: relPath, error: err }, `Failed to load from manifest, skipping`);
|
|
4748
5087
|
}
|
|
4749
5088
|
}
|
|
4750
5089
|
return items2;
|
|
@@ -4767,7 +5106,7 @@ var LocalSource = class {
|
|
|
4767
5106
|
const raw = await readFile6(fullPath, "utf-8");
|
|
4768
5107
|
items.push(JSON.parse(raw));
|
|
4769
5108
|
} catch (err) {
|
|
4770
|
-
|
|
5109
|
+
logger29.warn({ file, error: err }, `Failed to load ${subDir} file, skipping`);
|
|
4771
5110
|
}
|
|
4772
5111
|
}
|
|
4773
5112
|
for (const entry of entries) {
|
|
@@ -4792,7 +5131,7 @@ var LocalSource = class {
|
|
|
4792
5131
|
}
|
|
4793
5132
|
}
|
|
4794
5133
|
if (subDir === "skills") {
|
|
4795
|
-
const { parseSkillMd } = await import("./skill-md-parser-
|
|
5134
|
+
const { parseSkillMd } = await import("./skill-md-parser-HXLTZAUU.js");
|
|
4796
5135
|
for (const entry of entries) {
|
|
4797
5136
|
if (extname3(entry) === ".json") continue;
|
|
4798
5137
|
const subDirPath = join19(dirPath, entry);
|
|
@@ -4817,7 +5156,7 @@ var LocalSource = class {
|
|
|
4817
5156
|
|
|
4818
5157
|
// src/source/github-source.ts
|
|
4819
5158
|
var execFileAsync = promisify(execFile);
|
|
4820
|
-
var
|
|
5159
|
+
var logger30 = createLogger31("github-source");
|
|
4821
5160
|
var GitHubSource = class {
|
|
4822
5161
|
type = "github";
|
|
4823
5162
|
packageName;
|
|
@@ -4828,6 +5167,9 @@ var GitHubSource = class {
|
|
|
4828
5167
|
this.config = config;
|
|
4829
5168
|
this.cacheDir = join20(cacheDir, packageName);
|
|
4830
5169
|
}
|
|
5170
|
+
getRootDir() {
|
|
5171
|
+
return this.cacheDir;
|
|
5172
|
+
}
|
|
4831
5173
|
async fetch() {
|
|
4832
5174
|
await this.clone();
|
|
4833
5175
|
return this.readCached();
|
|
@@ -4836,21 +5178,21 @@ var GitHubSource = class {
|
|
|
4836
5178
|
try {
|
|
4837
5179
|
await this.pull();
|
|
4838
5180
|
} catch {
|
|
4839
|
-
|
|
5181
|
+
logger30.info("Pull failed, re-cloning");
|
|
4840
5182
|
await this.clone();
|
|
4841
5183
|
}
|
|
4842
5184
|
return this.readCached();
|
|
4843
5185
|
}
|
|
4844
5186
|
async dispose() {
|
|
4845
5187
|
await rm5(this.cacheDir, { recursive: true, force: true });
|
|
4846
|
-
|
|
5188
|
+
logger30.debug({ cacheDir: this.cacheDir }, "Cache cleaned");
|
|
4847
5189
|
}
|
|
4848
5190
|
async clone() {
|
|
4849
5191
|
await rm5(this.cacheDir, { recursive: true, force: true });
|
|
4850
5192
|
await mkdir13(this.cacheDir, { recursive: true });
|
|
4851
5193
|
const branch = this.config.branch ?? "main";
|
|
4852
5194
|
const args = ["clone", "--depth", "1", "--branch", branch, this.config.url, this.cacheDir];
|
|
4853
|
-
|
|
5195
|
+
logger30.info({ url: this.config.url, branch }, "Cloning repository");
|
|
4854
5196
|
await execFileAsync("git", args, { timeout: 6e4 });
|
|
4855
5197
|
}
|
|
4856
5198
|
async pull() {
|
|
@@ -4868,7 +5210,7 @@ var GitHubSource = class {
|
|
|
4868
5210
|
// src/source/source-manager.ts
|
|
4869
5211
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir14 } from "fs/promises";
|
|
4870
5212
|
import { join as join21 } from "path";
|
|
4871
|
-
import { createLogger as
|
|
5213
|
+
import { createLogger as createLogger32 } from "@actant/shared";
|
|
4872
5214
|
|
|
4873
5215
|
// src/version/sync-report.ts
|
|
4874
5216
|
function createEmptySyncReport() {
|
|
@@ -4893,7 +5235,7 @@ var DEFAULT_SOURCE_CONFIG = {
|
|
|
4893
5235
|
url: "https://github.com/blackplume233/actant-hub.git",
|
|
4894
5236
|
branch: "main"
|
|
4895
5237
|
};
|
|
4896
|
-
var
|
|
5238
|
+
var logger31 = createLogger32("source-manager");
|
|
4897
5239
|
var SourceManager = class {
|
|
4898
5240
|
sources = /* @__PURE__ */ new Map();
|
|
4899
5241
|
presets = /* @__PURE__ */ new Map();
|
|
@@ -4901,11 +5243,13 @@ var SourceManager = class {
|
|
|
4901
5243
|
homeDir;
|
|
4902
5244
|
sourcesFilePath;
|
|
4903
5245
|
cacheDir;
|
|
4904
|
-
|
|
5246
|
+
skipDefaultSource;
|
|
5247
|
+
constructor(homeDir, managers, options) {
|
|
4905
5248
|
this.homeDir = homeDir;
|
|
4906
5249
|
this.managers = managers;
|
|
4907
5250
|
this.sourcesFilePath = join21(homeDir, "sources.json");
|
|
4908
5251
|
this.cacheDir = join21(homeDir, "sources-cache");
|
|
5252
|
+
this.skipDefaultSource = options?.skipDefaultSource ?? false;
|
|
4909
5253
|
}
|
|
4910
5254
|
// ---------------------------------------------------------------------------
|
|
4911
5255
|
// Source CRUD
|
|
@@ -4919,7 +5263,7 @@ var SourceManager = class {
|
|
|
4919
5263
|
this.sources.set(name, source);
|
|
4920
5264
|
this.injectComponents(name, result);
|
|
4921
5265
|
await this.persistSources();
|
|
4922
|
-
|
|
5266
|
+
logger31.info({ name, type: config.type }, "Source added");
|
|
4923
5267
|
return result;
|
|
4924
5268
|
}
|
|
4925
5269
|
async syncSource(name) {
|
|
@@ -4935,7 +5279,7 @@ var SourceManager = class {
|
|
|
4935
5279
|
const newSnapshot = this.snapshotComponents(name);
|
|
4936
5280
|
await this.persistSources();
|
|
4937
5281
|
const report = this.buildSyncReport(oldSnapshot, newSnapshot);
|
|
4938
|
-
|
|
5282
|
+
logger31.info({ name, report }, "Source synced");
|
|
4939
5283
|
return { fetchResult: result, report };
|
|
4940
5284
|
}
|
|
4941
5285
|
async syncAll() {
|
|
@@ -4948,7 +5292,7 @@ var SourceManager = class {
|
|
|
4948
5292
|
const { report } = await this.syncSourceWithReport(name);
|
|
4949
5293
|
reports.push(report);
|
|
4950
5294
|
} catch (err) {
|
|
4951
|
-
|
|
5295
|
+
logger31.warn({ name, error: err }, "Failed to sync source, skipping");
|
|
4952
5296
|
}
|
|
4953
5297
|
}
|
|
4954
5298
|
return { report: mergeSyncReports(reports) };
|
|
@@ -4961,7 +5305,7 @@ var SourceManager = class {
|
|
|
4961
5305
|
await source.dispose();
|
|
4962
5306
|
this.sources.delete(name);
|
|
4963
5307
|
await this.persistSources();
|
|
4964
|
-
|
|
5308
|
+
logger31.info({ name }, "Source removed");
|
|
4965
5309
|
return true;
|
|
4966
5310
|
}
|
|
4967
5311
|
listSources() {
|
|
@@ -4974,6 +5318,10 @@ var SourceManager = class {
|
|
|
4974
5318
|
hasSource(name) {
|
|
4975
5319
|
return this.sources.has(name);
|
|
4976
5320
|
}
|
|
5321
|
+
/** Resolve a registered source name to its filesystem root directory. */
|
|
5322
|
+
getSourceRootDir(name) {
|
|
5323
|
+
return this.getSourceOrThrow(name).getRootDir();
|
|
5324
|
+
}
|
|
4977
5325
|
// ---------------------------------------------------------------------------
|
|
4978
5326
|
// Preset operations
|
|
4979
5327
|
// ---------------------------------------------------------------------------
|
|
@@ -5039,14 +5387,13 @@ var SourceManager = class {
|
|
|
5039
5387
|
// Initialization (load persisted sources on startup)
|
|
5040
5388
|
// ---------------------------------------------------------------------------
|
|
5041
5389
|
async initialize() {
|
|
5042
|
-
let entries;
|
|
5390
|
+
let entries = [];
|
|
5043
5391
|
try {
|
|
5044
5392
|
const raw = await readFile7(this.sourcesFilePath, "utf-8");
|
|
5045
5393
|
const data = JSON.parse(raw);
|
|
5046
5394
|
entries = Object.entries(data.sources ?? {}).map(([name, config]) => ({ name, config }));
|
|
5047
5395
|
} catch {
|
|
5048
|
-
|
|
5049
|
-
return;
|
|
5396
|
+
logger31.debug("No sources.json found, starting with empty sources");
|
|
5050
5397
|
}
|
|
5051
5398
|
for (const entry of entries) {
|
|
5052
5399
|
try {
|
|
@@ -5054,11 +5401,26 @@ var SourceManager = class {
|
|
|
5054
5401
|
const result = await source.fetch();
|
|
5055
5402
|
this.sources.set(entry.name, source);
|
|
5056
5403
|
this.injectComponents(entry.name, result);
|
|
5057
|
-
|
|
5404
|
+
logger31.info({ name: entry.name }, "Source restored from config");
|
|
5058
5405
|
} catch (err) {
|
|
5059
|
-
|
|
5406
|
+
logger31.warn({ name: entry.name, error: err }, "Failed to restore source, skipping");
|
|
5060
5407
|
}
|
|
5061
5408
|
}
|
|
5409
|
+
await this.ensureDefaultSource();
|
|
5410
|
+
}
|
|
5411
|
+
/**
|
|
5412
|
+
* Registers the official actant-hub as the default source if not already present.
|
|
5413
|
+
* Fails silently when offline or the repo is unreachable.
|
|
5414
|
+
*/
|
|
5415
|
+
async ensureDefaultSource() {
|
|
5416
|
+
if (this.skipDefaultSource) return;
|
|
5417
|
+
if (this.sources.has(DEFAULT_SOURCE_NAME)) return;
|
|
5418
|
+
try {
|
|
5419
|
+
await this.addSource(DEFAULT_SOURCE_NAME, DEFAULT_SOURCE_CONFIG);
|
|
5420
|
+
logger31.info("Default source registered: %s", DEFAULT_SOURCE_NAME);
|
|
5421
|
+
} catch (err) {
|
|
5422
|
+
logger31.debug({ error: err }, "Failed to register default source (offline?), skipping");
|
|
5423
|
+
}
|
|
5062
5424
|
}
|
|
5063
5425
|
// ---------------------------------------------------------------------------
|
|
5064
5426
|
// Internals
|
|
@@ -5091,12 +5453,24 @@ var SourceManager = class {
|
|
|
5091
5453
|
this.presets.set(`${packageName}@${preset.name}`, preset);
|
|
5092
5454
|
}
|
|
5093
5455
|
if (this.managers.templateRegistry && result.templates.length > 0) {
|
|
5456
|
+
const nsRef = (ref) => ref.includes("@") ? ref : `${packageName}@${ref}`;
|
|
5094
5457
|
for (const template of result.templates) {
|
|
5095
|
-
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
|
+
};
|
|
5096
5470
|
this.managers.templateRegistry.register(nsTemplate);
|
|
5097
5471
|
}
|
|
5098
5472
|
}
|
|
5099
|
-
|
|
5473
|
+
logger31.debug(
|
|
5100
5474
|
{
|
|
5101
5475
|
packageName,
|
|
5102
5476
|
skills: result.skills.length,
|
|
@@ -5229,6 +5603,671 @@ function isMajorVersionChange(oldVersion, newVersion) {
|
|
|
5229
5603
|
if (oldMajor === void 0 || newMajor === void 0) return false;
|
|
5230
5604
|
return newMajor !== oldMajor;
|
|
5231
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
|
+
}
|
|
5232
6271
|
export {
|
|
5233
6272
|
AgentBackendSchema,
|
|
5234
6273
|
AgentInitializer,
|
|
@@ -5236,6 +6275,7 @@ export {
|
|
|
5236
6275
|
AgentManager,
|
|
5237
6276
|
AgentStatusSchema,
|
|
5238
6277
|
AgentTemplateSchema,
|
|
6278
|
+
BUILTIN_PROVIDERS,
|
|
5239
6279
|
BaseComponentManager,
|
|
5240
6280
|
ClaudeCodeBuilder,
|
|
5241
6281
|
ClaudeCodeCommunicator,
|
|
@@ -5272,6 +6312,7 @@ export {
|
|
|
5272
6312
|
McpServerRefSchema,
|
|
5273
6313
|
MkdirStep,
|
|
5274
6314
|
MockLauncher,
|
|
6315
|
+
ModelProviderRegistry,
|
|
5275
6316
|
ModelProviderSchema,
|
|
5276
6317
|
NpmInstallStep,
|
|
5277
6318
|
PermissionAuditLogger,
|
|
@@ -5289,6 +6330,7 @@ export {
|
|
|
5289
6330
|
SessionRegistry,
|
|
5290
6331
|
SkillManager,
|
|
5291
6332
|
SourceManager,
|
|
6333
|
+
SourceValidator,
|
|
5292
6334
|
StepRegistry,
|
|
5293
6335
|
TaskDispatcher,
|
|
5294
6336
|
TaskQueue,
|
|
@@ -5300,16 +6342,26 @@ export {
|
|
|
5300
6342
|
createCommunicator,
|
|
5301
6343
|
createDefaultStepRegistry,
|
|
5302
6344
|
createLauncher,
|
|
6345
|
+
getBackendDescriptor,
|
|
5303
6346
|
getLaunchModeHandler,
|
|
5304
6347
|
globMatch,
|
|
5305
6348
|
isAcpBackend,
|
|
6349
|
+
isAcpOnlyBackend,
|
|
5306
6350
|
isProcessAlive,
|
|
5307
6351
|
metaFilePath,
|
|
6352
|
+
modelProviderRegistry,
|
|
6353
|
+
openBackend,
|
|
5308
6354
|
readInstanceMeta,
|
|
6355
|
+
registerBackend,
|
|
6356
|
+
registerBuiltinProviders,
|
|
6357
|
+
registerCommunicator,
|
|
6358
|
+
requireMode,
|
|
6359
|
+
resolveAcpBackend,
|
|
5309
6360
|
resolveBackend,
|
|
5310
6361
|
resolvePermissions,
|
|
5311
6362
|
resolvePermissionsWithMcp,
|
|
5312
6363
|
scanInstances,
|
|
6364
|
+
supportsMode,
|
|
5313
6365
|
toAgentTemplate,
|
|
5314
6366
|
updateInstanceMeta,
|
|
5315
6367
|
validateBackendConfig,
|