@rowan-agent/agent 0.8.7 → 0.9.0
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/index.d.ts +13 -28
- package/dist/index.js +235 -198
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -83,23 +83,6 @@ interface SourceInfo {
|
|
|
83
83
|
baseDir?: string;
|
|
84
84
|
displayName?: string;
|
|
85
85
|
}
|
|
86
|
-
/** Phase run function type for extensions */
|
|
87
|
-
type PhaseRun = (context: PhaseContext, execution: PhaseExecution) => Promise<PhaseOutput | void>;
|
|
88
|
-
/** Phase definition shape used by extensions */
|
|
89
|
-
type PhaseDefinition = {
|
|
90
|
-
name: string;
|
|
91
|
-
description: string;
|
|
92
|
-
run?: PhaseRun;
|
|
93
|
-
tools?: string[];
|
|
94
|
-
skills?: string[];
|
|
95
|
-
target?: string;
|
|
96
|
-
input?: Record<string, string>;
|
|
97
|
-
model?: string;
|
|
98
|
-
};
|
|
99
|
-
type PhaseRegistration = Omit<PhaseDefinition, 'run'> & {
|
|
100
|
-
/** Optional execution override — takes over model invocation */
|
|
101
|
-
run?: PhaseRun;
|
|
102
|
-
};
|
|
103
86
|
/**
|
|
104
87
|
* Tool definition for registering LLM-callable tools via `api.registerTool()`.
|
|
105
88
|
*
|
|
@@ -214,12 +197,6 @@ interface LoadedExtension {
|
|
|
214
197
|
interface ExtensionManifest {
|
|
215
198
|
entry?: string;
|
|
216
199
|
name?: string;
|
|
217
|
-
phase?: {
|
|
218
|
-
name?: string;
|
|
219
|
-
description?: string;
|
|
220
|
-
tools?: string[];
|
|
221
|
-
skills?: string[];
|
|
222
|
-
};
|
|
223
200
|
}
|
|
224
201
|
|
|
225
202
|
/**
|
|
@@ -438,8 +415,8 @@ interface ExtensionAPI {
|
|
|
438
415
|
off<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
|
|
439
416
|
/** Register a custom LLM-callable tool. */
|
|
440
417
|
registerTool(tool: ToolDefinition): void;
|
|
441
|
-
/** Register a
|
|
442
|
-
registerPhase(
|
|
418
|
+
/** Register a Phase directory bundle. */
|
|
419
|
+
registerPhase(path: string): Promise<void>;
|
|
443
420
|
/** Register a model provider. */
|
|
444
421
|
registerProvider(config: _rowan_agent_models.ProviderConfig): void;
|
|
445
422
|
/** Unregister a model provider. */
|
|
@@ -552,8 +529,8 @@ interface Phase {
|
|
|
552
529
|
description: string;
|
|
553
530
|
/** Restricted tools (undefined = all tools available) */
|
|
554
531
|
tools?: string[];
|
|
555
|
-
/**
|
|
556
|
-
skills?:
|
|
532
|
+
/** Skills bundled directly inside this phase directory. Inline phases may omit this (equivalent to []). */
|
|
533
|
+
skills?: Skill[];
|
|
557
534
|
/** Forced next phase */
|
|
558
535
|
target?: string;
|
|
559
536
|
/** Expected input fields (key → description) */
|
|
@@ -1176,6 +1153,7 @@ declare class ExtensionRunner {
|
|
|
1176
1153
|
private createExtensionAPI;
|
|
1177
1154
|
private registerTool;
|
|
1178
1155
|
private registerPhase;
|
|
1156
|
+
private loadRegisteredPhase;
|
|
1179
1157
|
private registerProvider;
|
|
1180
1158
|
private unregisterProvider;
|
|
1181
1159
|
private flushPendingProviders;
|
|
@@ -2222,8 +2200,15 @@ declare class RuntimeError<Code extends RuntimeErrorCode = RuntimeErrorCode> ext
|
|
|
2222
2200
|
}
|
|
2223
2201
|
declare function isRuntimeError(value: unknown): value is AnyRuntimeError;
|
|
2224
2202
|
|
|
2203
|
+
declare function loadSkill(path: string): Promise<Skill>;
|
|
2225
2204
|
declare function loadSkills(targetPath: string): Promise<Skill[]>;
|
|
2226
2205
|
|
|
2206
|
+
/**
|
|
2207
|
+
* Load a single phase from a PHASE.md file or phase directory.
|
|
2208
|
+
*
|
|
2209
|
+
* If a directory is provided, the loader reads its PHASE.md.
|
|
2210
|
+
*/
|
|
2211
|
+
declare function loadPhase(targetPath: string): Promise<Phase>;
|
|
2227
2212
|
/**
|
|
2228
2213
|
* Load all phases from the target directory.
|
|
2229
2214
|
*
|
|
@@ -2269,4 +2254,4 @@ declare function parseFrontmatter<T = Record<string, unknown>>(raw: string): Fro
|
|
|
2269
2254
|
|
|
2270
2255
|
declare function createCoreTools(input?: CoreToolContext): Tool[];
|
|
2271
2256
|
|
|
2272
|
-
export { type AfterToolCall, type AgentConfig, type AgentConfigRequest, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentResources, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type CoreToolContext, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type FrontmatterResult, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecutionIdentity, type PhaseInvocation, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, type Skill, SqliteStore, type TextContent, type ThinkingContent, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCoreTools, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhases, loadSkills, materializeConfigurationSnapshot, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
|
|
2257
|
+
export { type AfterToolCall, type AgentConfig, type AgentConfigRequest, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentResources, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type CoreToolContext, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type FrontmatterResult, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecutionIdentity, type PhaseInvocation, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, type Skill, SqliteStore, type TextContent, type ThinkingContent, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCoreTools, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhases, loadSkill, loadSkills, materializeConfigurationSnapshot, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
|
package/dist/index.js
CHANGED
|
@@ -164,9 +164,6 @@ function buildPhaseEntry(p) {
|
|
|
164
164
|
if (p.tools && p.tools.length > 0) {
|
|
165
165
|
entry.available_tools = p.tools.join(", ");
|
|
166
166
|
}
|
|
167
|
-
if (p.skills && p.skills.length > 0) {
|
|
168
|
-
entry.available_skills = p.skills.join(", ");
|
|
169
|
-
}
|
|
170
167
|
if (p.input && Object.keys(p.input).length > 0) {
|
|
171
168
|
entry.required_input = Object.entries(p.input).map(([key, desc]) => `- ${key}: ${desc}`).join("\n");
|
|
172
169
|
}
|
|
@@ -985,9 +982,9 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
985
982
|
assertActive();
|
|
986
983
|
options?.registerTool?.(tool);
|
|
987
984
|
},
|
|
988
|
-
registerPhase: (registration) => {
|
|
985
|
+
registerPhase: async (registration) => {
|
|
989
986
|
assertActive();
|
|
990
|
-
options?.registerPhase?.(registration);
|
|
987
|
+
await options?.registerPhase?.(registration);
|
|
991
988
|
},
|
|
992
989
|
registerProvider: (config) => {
|
|
993
990
|
assertActive();
|
|
@@ -1037,9 +1034,9 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
1037
1034
|
}
|
|
1038
1035
|
|
|
1039
1036
|
// src/harness/phases/loader.ts
|
|
1040
|
-
import { existsSync, statSync } from "fs";
|
|
1041
|
-
import { readdir } from "fs/promises";
|
|
1042
|
-
import { dirname as
|
|
1037
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
1038
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
1039
|
+
import { dirname as dirname4, join as join2, resolve as resolve4 } from "path";
|
|
1043
1040
|
|
|
1044
1041
|
// src/harness/definitions.ts
|
|
1045
1042
|
import { parseModelRef } from "@rowan-agent/models";
|
|
@@ -1126,9 +1123,8 @@ function optionalStringList(value, field) {
|
|
|
1126
1123
|
function optionalPhaseRegistrySelection(value, field) {
|
|
1127
1124
|
if (value === void 0) return void 0;
|
|
1128
1125
|
if (!isRecord(value)) throw new TypeError(`${field} must be an object`);
|
|
1129
|
-
if (!("entryPhaseId" in value)) throw new TypeError(`${field}.entryPhaseId is required`);
|
|
1130
1126
|
if (!("phaseIds" in value)) throw new TypeError(`${field}.phaseIds is required`);
|
|
1131
|
-
const entryPhaseId = value.entryPhaseId;
|
|
1127
|
+
const entryPhaseId = value.entryPhaseId ?? null;
|
|
1132
1128
|
if (entryPhaseId !== null && (typeof entryPhaseId !== "string" || entryPhaseId.trim() === "")) {
|
|
1133
1129
|
throw new TypeError(`${field}.entryPhaseId must be a non-empty string or null`);
|
|
1134
1130
|
}
|
|
@@ -1203,19 +1199,6 @@ function validateDescription(value) {
|
|
|
1203
1199
|
}
|
|
1204
1200
|
return { description: value, warnings: [], missing: false };
|
|
1205
1201
|
}
|
|
1206
|
-
function validateSkillReferences(value) {
|
|
1207
|
-
if (value === void 0) return [];
|
|
1208
|
-
if (!Array.isArray(value)) return ["skills must be an array"];
|
|
1209
|
-
const errors = [];
|
|
1210
|
-
for (const [index, skillId] of value.entries()) {
|
|
1211
|
-
if (typeof skillId !== "string") {
|
|
1212
|
-
errors.push(`skills[${index}] must be a string`);
|
|
1213
|
-
continue;
|
|
1214
|
-
}
|
|
1215
|
-
errors.push(...validateResourceId(skillId, `skills[${index}]`));
|
|
1216
|
-
}
|
|
1217
|
-
return errors;
|
|
1218
|
-
}
|
|
1219
1202
|
function validatePhaseTarget(value) {
|
|
1220
1203
|
if (value === void 0) return [];
|
|
1221
1204
|
if (typeof value !== "string") return ["target must be a string"];
|
|
@@ -1228,11 +1211,91 @@ function warnResourceDiagnostics(kind, filePath, diagnostics) {
|
|
|
1228
1211
|
}
|
|
1229
1212
|
}
|
|
1230
1213
|
|
|
1214
|
+
// src/harness/skills.ts
|
|
1215
|
+
import { existsSync, statSync } from "fs";
|
|
1216
|
+
import { readdir } from "fs/promises";
|
|
1217
|
+
import { dirname as dirname3, join, resolve as resolve3 } from "path";
|
|
1218
|
+
var SKILL_MARKER = "SKILL.md";
|
|
1219
|
+
function resolveSkillPath(input) {
|
|
1220
|
+
const resolved = resolve3(input);
|
|
1221
|
+
return existsSync(resolved) && statSync(resolved).isDirectory() ? join(resolved, SKILL_MARKER) : resolved;
|
|
1222
|
+
}
|
|
1223
|
+
async function loadSkill(path) {
|
|
1224
|
+
const resolved = resolveSkillPath(path);
|
|
1225
|
+
let loaded;
|
|
1226
|
+
try {
|
|
1227
|
+
loaded = await loadMarkdown(resolved);
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
1230
|
+
warnResourceDiagnostics("skill", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
1231
|
+
throw new ResourceMetadataError("parse_failed", error.message);
|
|
1232
|
+
}
|
|
1233
|
+
const { frontmatter, body } = loaded;
|
|
1234
|
+
const metadata = frontmatter;
|
|
1235
|
+
const id = inferResourceName(resolved, SKILL_MARKER);
|
|
1236
|
+
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
1237
|
+
const name = frontmatterName || id;
|
|
1238
|
+
const description = validateDescription(metadata.description);
|
|
1239
|
+
const diagnostics = validateResourceId(id);
|
|
1240
|
+
if (frontmatterName && frontmatterName !== id) {
|
|
1241
|
+
diagnostics.push(...validateResourceName(frontmatterName, id));
|
|
1242
|
+
}
|
|
1243
|
+
diagnostics.push(...description.warnings);
|
|
1244
|
+
warnResourceDiagnostics("skill", resolved, diagnostics);
|
|
1245
|
+
if (description.missing) {
|
|
1246
|
+
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
1247
|
+
}
|
|
1248
|
+
return {
|
|
1249
|
+
name,
|
|
1250
|
+
description: description.description,
|
|
1251
|
+
filePath: resolved,
|
|
1252
|
+
baseDir: dirname3(resolved),
|
|
1253
|
+
content: body,
|
|
1254
|
+
disableModelInvocation: metadata["disable-model-invocation"] === true
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
async function loadSkills(targetPath) {
|
|
1258
|
+
const skillsDir = resolve3(targetPath);
|
|
1259
|
+
if (existsSync(skillsDir) && statSync(skillsDir).isFile()) {
|
|
1260
|
+
try {
|
|
1261
|
+
return [await loadSkill(skillsDir)];
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
1264
|
+
throw error;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
if (existsSync(join(skillsDir, SKILL_MARKER))) {
|
|
1268
|
+
try {
|
|
1269
|
+
return [await loadSkill(skillsDir)];
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
1272
|
+
throw error;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
1276
|
+
const skills = [];
|
|
1277
|
+
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1278
|
+
if (!entry.isDirectory()) continue;
|
|
1279
|
+
const skillFile = join(skillsDir, entry.name, SKILL_MARKER);
|
|
1280
|
+
if (!existsSync(skillFile)) continue;
|
|
1281
|
+
try {
|
|
1282
|
+
const skill = await loadSkill(skillFile);
|
|
1283
|
+
skills.push(skill);
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
if (error instanceof ResourceMetadataError) continue;
|
|
1286
|
+
console.warn(`Failed to load skill "${entry.name}":`, error);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return skills;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1231
1292
|
// src/harness/phases/loader.ts
|
|
1232
1293
|
var PHASE_MARKER = "PHASE.md";
|
|
1294
|
+
var SKILL_MARKER2 = "SKILL.md";
|
|
1295
|
+
var RESOURCE_MARKERS = /* @__PURE__ */ new Set([PHASE_MARKER, SKILL_MARKER2]);
|
|
1233
1296
|
function resolvePhasePath(input) {
|
|
1234
|
-
const resolved =
|
|
1235
|
-
return
|
|
1297
|
+
const resolved = resolve4(input);
|
|
1298
|
+
return existsSync2(resolved) && statSync2(resolved).isDirectory() ? join2(resolved, PHASE_MARKER) : resolved;
|
|
1236
1299
|
}
|
|
1237
1300
|
async function loadPhase(targetPath) {
|
|
1238
1301
|
const resolved = resolvePhasePath(targetPath);
|
|
@@ -1250,7 +1313,9 @@ async function loadPhase(targetPath) {
|
|
|
1250
1313
|
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
1251
1314
|
let definition;
|
|
1252
1315
|
try {
|
|
1253
|
-
|
|
1316
|
+
const phaseMetadata = { ...metadata };
|
|
1317
|
+
delete phaseMetadata.skills;
|
|
1318
|
+
definition = normalizeAgentDefinition(phaseMetadata, body, { fallbackName: directoryName });
|
|
1254
1319
|
} catch (error) {
|
|
1255
1320
|
const message = error instanceof Error ? error.message : String(error);
|
|
1256
1321
|
warnResourceDiagnostics("phase", resolved, [message]);
|
|
@@ -1263,15 +1328,15 @@ async function loadPhase(targetPath) {
|
|
|
1263
1328
|
diagnostics.push(...validateResourceName(frontmatterName, directoryName));
|
|
1264
1329
|
}
|
|
1265
1330
|
diagnostics.push(...description.warnings);
|
|
1266
|
-
diagnostics.push(...validateSkillReferences(metadata.skills));
|
|
1267
1331
|
diagnostics.push(...validatePhaseTarget(metadata.target));
|
|
1268
1332
|
warnResourceDiagnostics("phase", resolved, diagnostics);
|
|
1269
|
-
const baseDir =
|
|
1333
|
+
const baseDir = dirname4(resolved);
|
|
1334
|
+
const skills = await loadPhaseSkills(baseDir);
|
|
1270
1335
|
const phase = {
|
|
1271
1336
|
name,
|
|
1272
1337
|
description: description.description,
|
|
1273
1338
|
tools: definition.tools ? [...definition.tools] : void 0,
|
|
1274
|
-
skills
|
|
1339
|
+
skills,
|
|
1275
1340
|
target: metadata.target,
|
|
1276
1341
|
input: metadata.input,
|
|
1277
1342
|
isolated: metadata.isolated,
|
|
@@ -1291,6 +1356,54 @@ async function loadPhase(targetPath) {
|
|
|
1291
1356
|
}
|
|
1292
1357
|
return phase;
|
|
1293
1358
|
}
|
|
1359
|
+
async function loadPhaseSkills(baseDir) {
|
|
1360
|
+
const entries = await readdir2(baseDir, { withFileTypes: true });
|
|
1361
|
+
const skills = [];
|
|
1362
|
+
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1363
|
+
const entryPath = join2(baseDir, entry.name);
|
|
1364
|
+
if (!entry.isDirectory()) {
|
|
1365
|
+
if (RESOURCE_MARKERS.has(entry.name) && entry.name !== PHASE_MARKER) {
|
|
1366
|
+
throw invalidPhaseBundle(`resource marker ${entry.name} is not valid at the phase root`);
|
|
1367
|
+
}
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
const markers = await findResourceMarkers(entryPath);
|
|
1371
|
+
const directSkillPath = join2(entryPath, SKILL_MARKER2);
|
|
1372
|
+
const hasDirectSkill = markers.includes(directSkillPath);
|
|
1373
|
+
if (!hasDirectSkill) {
|
|
1374
|
+
if (markers.length > 0) {
|
|
1375
|
+
throw invalidPhaseBundle(`resource marker ${markers[0]} is not a direct child Skill`);
|
|
1376
|
+
}
|
|
1377
|
+
continue;
|
|
1378
|
+
}
|
|
1379
|
+
const nestedMarkers = markers.filter((marker) => marker !== directSkillPath);
|
|
1380
|
+
if (nestedMarkers.length > 0) {
|
|
1381
|
+
throw invalidPhaseBundle(`resource marker ${nestedMarkers[0]} is nested below a Skill bundle`);
|
|
1382
|
+
}
|
|
1383
|
+
const skill = await loadSkill(directSkillPath);
|
|
1384
|
+
if (skills.some((candidate) => candidate.name === skill.name)) {
|
|
1385
|
+
throw invalidPhaseBundle(`duplicate bundled Skill name "${skill.name}"`);
|
|
1386
|
+
}
|
|
1387
|
+
skills.push(skill);
|
|
1388
|
+
}
|
|
1389
|
+
return skills;
|
|
1390
|
+
}
|
|
1391
|
+
async function findResourceMarkers(directory) {
|
|
1392
|
+
const entries = await readdir2(directory, { withFileTypes: true });
|
|
1393
|
+
const markers = [];
|
|
1394
|
+
for (const entry of entries) {
|
|
1395
|
+
const entryPath = join2(directory, entry.name);
|
|
1396
|
+
if (entry.isDirectory()) {
|
|
1397
|
+
markers.push(...await findResourceMarkers(entryPath));
|
|
1398
|
+
} else if (RESOURCE_MARKERS.has(entry.name)) {
|
|
1399
|
+
markers.push(entryPath);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
return markers;
|
|
1403
|
+
}
|
|
1404
|
+
function invalidPhaseBundle(message) {
|
|
1405
|
+
return new ResourceMetadataError("invalid_metadata", message);
|
|
1406
|
+
}
|
|
1294
1407
|
function readPhaseContent(phase) {
|
|
1295
1408
|
return formatResourceOutput({
|
|
1296
1409
|
type: "phase",
|
|
@@ -1302,8 +1415,8 @@ function readPhaseContent(phase) {
|
|
|
1302
1415
|
}
|
|
1303
1416
|
async function loadPhases(targetPath) {
|
|
1304
1417
|
const phases = /* @__PURE__ */ new Map();
|
|
1305
|
-
const phasesDir =
|
|
1306
|
-
if (
|
|
1418
|
+
const phasesDir = resolve4(targetPath);
|
|
1419
|
+
if (existsSync2(phasesDir) && statSync2(phasesDir).isFile()) {
|
|
1307
1420
|
try {
|
|
1308
1421
|
const phase = await loadPhase(phasesDir);
|
|
1309
1422
|
phases.set(phase.name, phase);
|
|
@@ -1312,7 +1425,7 @@ async function loadPhases(targetPath) {
|
|
|
1312
1425
|
}
|
|
1313
1426
|
return { phases, entryPhaseId: null };
|
|
1314
1427
|
}
|
|
1315
|
-
if (
|
|
1428
|
+
if (existsSync2(join2(phasesDir, PHASE_MARKER))) {
|
|
1316
1429
|
try {
|
|
1317
1430
|
const phase = await loadPhase(phasesDir);
|
|
1318
1431
|
phases.set(phase.name, phase);
|
|
@@ -1321,11 +1434,11 @@ async function loadPhases(targetPath) {
|
|
|
1321
1434
|
}
|
|
1322
1435
|
return { phases, entryPhaseId: null };
|
|
1323
1436
|
}
|
|
1324
|
-
const entries = await
|
|
1437
|
+
const entries = await readdir2(phasesDir, { withFileTypes: true });
|
|
1325
1438
|
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1326
1439
|
if (!entry.isDirectory()) continue;
|
|
1327
|
-
const phaseFile =
|
|
1328
|
-
if (!
|
|
1440
|
+
const phaseFile = join2(phasesDir, entry.name, PHASE_MARKER);
|
|
1441
|
+
if (!existsSync2(phaseFile)) continue;
|
|
1329
1442
|
try {
|
|
1330
1443
|
const phase = await loadPhase(phaseFile);
|
|
1331
1444
|
phases.set(phase.name, phase);
|
|
@@ -1339,8 +1452,8 @@ async function loadPhases(targetPath) {
|
|
|
1339
1452
|
async function discoverPhaseCode(baseDir) {
|
|
1340
1453
|
const candidates = ["index.ts", "index.js"];
|
|
1341
1454
|
for (const candidate of candidates) {
|
|
1342
|
-
const fullPath =
|
|
1343
|
-
if (
|
|
1455
|
+
const fullPath = join2(baseDir, candidate);
|
|
1456
|
+
if (existsSync2(fullPath)) {
|
|
1344
1457
|
return fullPath;
|
|
1345
1458
|
}
|
|
1346
1459
|
}
|
|
@@ -1372,6 +1485,7 @@ function createDefaultPhase() {
|
|
|
1372
1485
|
description: "Executes concrete task operations and produces artifacts.",
|
|
1373
1486
|
filePath: "",
|
|
1374
1487
|
baseDir: "",
|
|
1488
|
+
skills: [],
|
|
1375
1489
|
content: "Execute tasks using current context.\nNo planning. No evaluation.\nRoute to next phase or stop when done."
|
|
1376
1490
|
};
|
|
1377
1491
|
}
|
|
@@ -1929,7 +2043,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1929
2043
|
const toolExecutionManager = createToolExecutionManager();
|
|
1930
2044
|
const execution = createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry);
|
|
1931
2045
|
const phaseTools = selectPhaseTools(allTools, phase.tools);
|
|
1932
|
-
const phaseSkills =
|
|
2046
|
+
const phaseSkills = phase.name === DEFAULT_PHASE_ID ? config.context.skills : phase.skills ?? [];
|
|
1933
2047
|
let phaseContext = {
|
|
1934
2048
|
systemPrompt: config.context.systemPrompt,
|
|
1935
2049
|
messages: messageManager.visible(),
|
|
@@ -2208,7 +2322,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2208
2322
|
}
|
|
2209
2323
|
const delayMs = getRetryDelay(attempt, baseDelayMs, maxDelayMs);
|
|
2210
2324
|
options.onRetry?.(attempt, error, delayMs);
|
|
2211
|
-
await new Promise((
|
|
2325
|
+
await new Promise((resolve8) => setTimeout(resolve8, delayMs));
|
|
2212
2326
|
if (options.signal?.aborted) {
|
|
2213
2327
|
throw lastError;
|
|
2214
2328
|
}
|
|
@@ -2220,7 +2334,7 @@ async function executePhase(ctx) {
|
|
|
2220
2334
|
const { phase, config, execution, registry, context } = ctx;
|
|
2221
2335
|
if (phase.factory) {
|
|
2222
2336
|
const api = createExtensionAPI(void 0, {
|
|
2223
|
-
registerPhase: () => {
|
|
2337
|
+
registerPhase: async () => {
|
|
2224
2338
|
},
|
|
2225
2339
|
registerProvider: () => {
|
|
2226
2340
|
},
|
|
@@ -2242,7 +2356,7 @@ async function executePhase(ctx) {
|
|
|
2242
2356
|
config.context.messages.push(createMessage(role, content));
|
|
2243
2357
|
},
|
|
2244
2358
|
getAvailableTools: () => config.context.tools.map((t) => ({ name: t.name, description: t.description })),
|
|
2245
|
-
getAvailableSkills: () =>
|
|
2359
|
+
getAvailableSkills: () => context.skills.map((s) => ({ name: s.name, description: s.description })),
|
|
2246
2360
|
getPhaseContent: (id) => registry.phases.get(id)?.content ?? "",
|
|
2247
2361
|
getAvailablePhases: () => Array.from(registry.phases.keys())
|
|
2248
2362
|
},
|
|
@@ -2420,7 +2534,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2420
2534
|
const messages = [...context];
|
|
2421
2535
|
const allTools = buildToolsWithRouting(config, availablePhases);
|
|
2422
2536
|
const phaseTools = selectPhaseTools(allTools, phase.tools);
|
|
2423
|
-
const phaseSkills =
|
|
2537
|
+
const phaseSkills = phase.name === DEFAULT_PHASE_ID ? config.context.skills : phase.skills ?? [];
|
|
2424
2538
|
const systemPrompt = config.context.systemPrompt;
|
|
2425
2539
|
const phaseContext = {
|
|
2426
2540
|
systemPrompt,
|
|
@@ -2914,6 +3028,7 @@ function snapshotConfig(config) {
|
|
|
2914
3028
|
...config.resources,
|
|
2915
3029
|
tools: Object.freeze([...config.resources.tools]),
|
|
2916
3030
|
skills: Object.freeze([...config.resources.skills]),
|
|
3031
|
+
...config.resources.phases ? { phases: snapshotPhaseRegistry(config.resources.phases) } : {},
|
|
2917
3032
|
...config.resources.resourceView ? {
|
|
2918
3033
|
resourceView: Object.freeze({
|
|
2919
3034
|
agents: Object.freeze([...config.resources.resourceView.agents]),
|
|
@@ -2937,6 +3052,20 @@ function snapshotConfig(config) {
|
|
|
2937
3052
|
});
|
|
2938
3053
|
return Object.freeze({ ...config, definition, resources });
|
|
2939
3054
|
}
|
|
3055
|
+
function snapshotPhaseRegistry(registry) {
|
|
3056
|
+
const phases = /* @__PURE__ */ new Map();
|
|
3057
|
+
for (const [name, phase] of registry.phases) phases.set(name, snapshotPhase(phase));
|
|
3058
|
+
return { phases, entryPhaseId: registry.entryPhaseId };
|
|
3059
|
+
}
|
|
3060
|
+
function snapshotPhase(phase) {
|
|
3061
|
+
return Object.freeze({
|
|
3062
|
+
...phase,
|
|
3063
|
+
...phase.tools ? { tools: Object.freeze([...phase.tools]) } : {},
|
|
3064
|
+
skills: Object.freeze((phase.skills ?? []).map((skill) => Object.freeze({ ...skill }))),
|
|
3065
|
+
...phase.input ? { input: Object.freeze({ ...phase.input }) } : {},
|
|
3066
|
+
...phase.model ? { model: Object.freeze({ ...phase.model }) } : {}
|
|
3067
|
+
});
|
|
3068
|
+
}
|
|
2940
3069
|
function snapshotConfiguration(config) {
|
|
2941
3070
|
const layer = config.definition.layer;
|
|
2942
3071
|
const definition = Object.freeze({
|
|
@@ -3436,11 +3565,11 @@ function fromLoopResult(result) {
|
|
|
3436
3565
|
var MAX_BUFFERED_EVENTS = 128;
|
|
3437
3566
|
var MAX_BUFFERED_TEXT = 64 * 1024;
|
|
3438
3567
|
function deferred() {
|
|
3439
|
-
let
|
|
3568
|
+
let resolve8;
|
|
3440
3569
|
const promise = new Promise((done) => {
|
|
3441
|
-
|
|
3570
|
+
resolve8 = done;
|
|
3442
3571
|
});
|
|
3443
|
-
return { promise, resolve:
|
|
3572
|
+
return { promise, resolve: resolve8 };
|
|
3444
3573
|
}
|
|
3445
3574
|
var TransientRunEventSubscription = class {
|
|
3446
3575
|
queue = [];
|
|
@@ -3626,6 +3755,7 @@ var HooksManager = class {
|
|
|
3626
3755
|
|
|
3627
3756
|
// src/extensions/runner.ts
|
|
3628
3757
|
import { execFile } from "child_process";
|
|
3758
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
3629
3759
|
import {
|
|
3630
3760
|
registerModel,
|
|
3631
3761
|
unregisterProviderModels,
|
|
@@ -3657,9 +3787,6 @@ function createExtensionRuntime() {
|
|
|
3657
3787
|
};
|
|
3658
3788
|
}
|
|
3659
3789
|
|
|
3660
|
-
// src/extensions/runner.ts
|
|
3661
|
-
import { parseModelRef as parseModelRef2 } from "@rowan-agent/models";
|
|
3662
|
-
|
|
3663
3790
|
// src/extensions/event-bus.ts
|
|
3664
3791
|
function createEventBus() {
|
|
3665
3792
|
const listeners = /* @__PURE__ */ new Map();
|
|
@@ -3703,7 +3830,7 @@ function createEventBus() {
|
|
|
3703
3830
|
|
|
3704
3831
|
// src/extensions/runner.ts
|
|
3705
3832
|
async function execCommand(command, args, cwd, options) {
|
|
3706
|
-
return new Promise((
|
|
3833
|
+
return new Promise((resolve8, reject) => {
|
|
3707
3834
|
const child = execFile(
|
|
3708
3835
|
command,
|
|
3709
3836
|
args,
|
|
@@ -3718,7 +3845,7 @@ async function execCommand(command, args, cwd, options) {
|
|
|
3718
3845
|
reject(new Error("Command was aborted"));
|
|
3719
3846
|
return;
|
|
3720
3847
|
}
|
|
3721
|
-
|
|
3848
|
+
resolve8({
|
|
3722
3849
|
exitCode: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
3723
3850
|
stdout: stdout ?? "",
|
|
3724
3851
|
stderr: stderr ?? ""
|
|
@@ -3955,17 +4082,10 @@ var ExtensionRunner = class {
|
|
|
3955
4082
|
adaptToPhase(reg) {
|
|
3956
4083
|
const def = reg.definition;
|
|
3957
4084
|
return {
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
target: def.target,
|
|
3963
|
-
input: def.input,
|
|
3964
|
-
run: def.run,
|
|
3965
|
-
filePath: "",
|
|
3966
|
-
baseDir: "",
|
|
3967
|
-
content: "",
|
|
3968
|
-
...def.model ? { model: parseModelRef2(def.model) } : {}
|
|
4085
|
+
...def,
|
|
4086
|
+
...def.tools ? { tools: [...def.tools] } : {},
|
|
4087
|
+
skills: def.skills ? [...def.skills] : [],
|
|
4088
|
+
...def.input ? { input: { ...def.input } } : {}
|
|
3969
4089
|
};
|
|
3970
4090
|
}
|
|
3971
4091
|
// ---------------------------------------------------------------------------
|
|
@@ -4127,36 +4247,20 @@ var ExtensionRunner = class {
|
|
|
4127
4247
|
});
|
|
4128
4248
|
}
|
|
4129
4249
|
registerPhase(extension, registration) {
|
|
4130
|
-
if (typeof registration
|
|
4131
|
-
throw new Error(`Phase registration requires a
|
|
4132
|
-
}
|
|
4133
|
-
const name = registration.name;
|
|
4134
|
-
const description = validateDescription(registration.description);
|
|
4135
|
-
const errors = validateResourceId(name, "name");
|
|
4136
|
-
if (description.missing) errors.push(...description.warnings);
|
|
4137
|
-
errors.push(...validateSkillReferences(registration.skills));
|
|
4138
|
-
errors.push(...validatePhaseTarget(registration.target));
|
|
4139
|
-
if (errors.length > 0) {
|
|
4140
|
-
throw new Error(`Invalid phase registration "${name}": ${errors.join("; ")}`);
|
|
4141
|
-
}
|
|
4142
|
-
if (description.warnings.length > 0) {
|
|
4143
|
-
warnResourceDiagnostics("phase", `extension ${extension.path}`, description.warnings);
|
|
4250
|
+
if (typeof registration !== "string" || registration.length === 0) {
|
|
4251
|
+
throw new Error(`Phase registration requires a directory path.`);
|
|
4144
4252
|
}
|
|
4253
|
+
return this.loadRegisteredPhase(extension, registration);
|
|
4254
|
+
}
|
|
4255
|
+
async loadRegisteredPhase(extension, registration) {
|
|
4256
|
+
const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname5(extension.path);
|
|
4257
|
+
const phase = await loadPhase(resolve5(extensionBase, registration));
|
|
4258
|
+
const name = phase.name;
|
|
4145
4259
|
if (this.phases.has(name)) {
|
|
4146
4260
|
throw new Error(`Duplicate phase name: ${name}`);
|
|
4147
4261
|
}
|
|
4148
|
-
const definition = {
|
|
4149
|
-
name,
|
|
4150
|
-
description: description.description,
|
|
4151
|
-
run: registration.run,
|
|
4152
|
-
...registration.tools ? { tools: registration.tools.slice() } : {},
|
|
4153
|
-
...registration.skills ? { skills: registration.skills.slice() } : {},
|
|
4154
|
-
...registration.target ? { target: registration.target } : {},
|
|
4155
|
-
...registration.input ? { input: { ...registration.input } } : {},
|
|
4156
|
-
...registration.model ? { model: registration.model } : {}
|
|
4157
|
-
};
|
|
4158
4262
|
const registered = {
|
|
4159
|
-
definition,
|
|
4263
|
+
definition: phase,
|
|
4160
4264
|
source: { extensionPath: extension.path }
|
|
4161
4265
|
};
|
|
4162
4266
|
this.phases.set(name, registered);
|
|
@@ -4215,10 +4319,10 @@ function createExtensionRunner(options) {
|
|
|
4215
4319
|
}
|
|
4216
4320
|
|
|
4217
4321
|
// src/extensions/loader.ts
|
|
4218
|
-
import { existsSync as
|
|
4219
|
-
import { readdir as
|
|
4322
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
4323
|
+
import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
4220
4324
|
import { fileURLToPath } from "url";
|
|
4221
|
-
import { basename as basename3, dirname as
|
|
4325
|
+
import { basename as basename3, dirname as dirname6, extname as extname2, join as join3, resolve as resolve6 } from "path";
|
|
4222
4326
|
import { createJiti } from "jiti";
|
|
4223
4327
|
function isExtensionFile(path) {
|
|
4224
4328
|
const extension = extname2(path).toLowerCase();
|
|
@@ -4228,8 +4332,8 @@ function isSyntheticPath(path) {
|
|
|
4228
4332
|
return path.startsWith("<") && path.endsWith(">");
|
|
4229
4333
|
}
|
|
4230
4334
|
function readManifestSync(dir) {
|
|
4231
|
-
const manifestPath =
|
|
4232
|
-
if (!
|
|
4335
|
+
const manifestPath = join3(dir, "package.json");
|
|
4336
|
+
if (!existsSync3(manifestPath)) {
|
|
4233
4337
|
return void 0;
|
|
4234
4338
|
}
|
|
4235
4339
|
try {
|
|
@@ -4238,8 +4342,7 @@ function readManifestSync(dir) {
|
|
|
4238
4342
|
const rowan = pkg.rowan;
|
|
4239
4343
|
if (!rowan) return void 0;
|
|
4240
4344
|
return {
|
|
4241
|
-
entry: rowan.extensions?.[0]
|
|
4242
|
-
phase: rowan.phase
|
|
4345
|
+
entry: rowan.extensions?.[0]
|
|
4243
4346
|
};
|
|
4244
4347
|
} catch {
|
|
4245
4348
|
return void 0;
|
|
@@ -4251,7 +4354,7 @@ function jitiAliases() {
|
|
|
4251
4354
|
return {};
|
|
4252
4355
|
}
|
|
4253
4356
|
const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
|
|
4254
|
-
if (!
|
|
4357
|
+
if (!existsSync3(agentSourcePath)) {
|
|
4255
4358
|
return {};
|
|
4256
4359
|
}
|
|
4257
4360
|
return { "@rowan-agent/agent": agentSourcePath };
|
|
@@ -4272,28 +4375,28 @@ async function loadExtensionModule(extensionPath) {
|
|
|
4272
4375
|
return typeof module === "function" ? module : void 0;
|
|
4273
4376
|
}
|
|
4274
4377
|
async function readPackageManifest(path) {
|
|
4275
|
-
if (!
|
|
4378
|
+
if (!existsSync3(path)) {
|
|
4276
4379
|
return void 0;
|
|
4277
4380
|
}
|
|
4278
4381
|
const content = await readFile3(path, "utf8");
|
|
4279
4382
|
return JSON.parse(content);
|
|
4280
4383
|
}
|
|
4281
4384
|
async function resolveExtensionEntries(dir) {
|
|
4282
|
-
const manifest = await readPackageManifest(
|
|
4385
|
+
const manifest = await readPackageManifest(join3(dir, "package.json"));
|
|
4283
4386
|
const declared = manifest?.rowan?.extensions;
|
|
4284
4387
|
if (declared && declared.length > 0) {
|
|
4285
|
-
return declared.map((entry) =>
|
|
4388
|
+
return declared.map((entry) => resolve6(dir, entry));
|
|
4286
4389
|
}
|
|
4287
4390
|
for (const name of ["index.ts", "index.js"]) {
|
|
4288
|
-
const entry =
|
|
4289
|
-
if (
|
|
4391
|
+
const entry = join3(dir, name);
|
|
4392
|
+
if (existsSync3(entry)) {
|
|
4290
4393
|
return [entry];
|
|
4291
4394
|
}
|
|
4292
4395
|
}
|
|
4293
4396
|
return void 0;
|
|
4294
4397
|
}
|
|
4295
4398
|
async function discoverExtensionsInDir(dir) {
|
|
4296
|
-
const entries = await
|
|
4399
|
+
const entries = await readdir3(dir, { withFileTypes: true }).catch((error) => {
|
|
4297
4400
|
if (error.code === "ENOENT") {
|
|
4298
4401
|
return [];
|
|
4299
4402
|
}
|
|
@@ -4301,7 +4404,7 @@ async function discoverExtensionsInDir(dir) {
|
|
|
4301
4404
|
});
|
|
4302
4405
|
const paths = [];
|
|
4303
4406
|
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4304
|
-
const entryPath =
|
|
4407
|
+
const entryPath = join3(dir, entry.name);
|
|
4305
4408
|
if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
|
|
4306
4409
|
paths.push(entryPath);
|
|
4307
4410
|
continue;
|
|
@@ -4319,10 +4422,10 @@ async function discoverExtensionsInDir(dir) {
|
|
|
4319
4422
|
return paths;
|
|
4320
4423
|
}
|
|
4321
4424
|
function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
|
|
4322
|
-
const resolvedCwd =
|
|
4323
|
-
const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath :
|
|
4324
|
-
const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(
|
|
4325
|
-
const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd :
|
|
4425
|
+
const resolvedCwd = resolve6(cwd);
|
|
4426
|
+
const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve6(resolvedCwd, extensionPath);
|
|
4427
|
+
const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname6(resolvedPath)) || "unknown";
|
|
4428
|
+
const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname6(resolvedPath);
|
|
4326
4429
|
const manifest = readManifestSync(manifestDir);
|
|
4327
4430
|
return {
|
|
4328
4431
|
path: resolvedPath,
|
|
@@ -4334,9 +4437,9 @@ function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
|
|
|
4334
4437
|
async function loadExtensions(paths, cwd) {
|
|
4335
4438
|
const extensions = [];
|
|
4336
4439
|
const errors = [];
|
|
4337
|
-
const resolvedCwd =
|
|
4440
|
+
const resolvedCwd = resolve6(cwd);
|
|
4338
4441
|
for (const path of paths) {
|
|
4339
|
-
const resolvedPath =
|
|
4442
|
+
const resolvedPath = resolve6(resolvedCwd, path);
|
|
4340
4443
|
try {
|
|
4341
4444
|
const factory = await loadExtensionModule(resolvedPath);
|
|
4342
4445
|
if (!factory) {
|
|
@@ -4357,10 +4460,10 @@ async function loadExtensions(paths, cwd) {
|
|
|
4357
4460
|
return { extensions, errors };
|
|
4358
4461
|
}
|
|
4359
4462
|
async function loadExtensionsFromPath(path) {
|
|
4360
|
-
const resolvedPath =
|
|
4463
|
+
const resolvedPath = resolve6(path);
|
|
4361
4464
|
const info = await stat2(resolvedPath);
|
|
4362
4465
|
if (!info.isDirectory()) {
|
|
4363
|
-
return loadExtensions([resolvedPath],
|
|
4466
|
+
return loadExtensions([resolvedPath], dirname6(resolvedPath));
|
|
4364
4467
|
}
|
|
4365
4468
|
const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
|
|
4366
4469
|
return loadExtensions(entries, resolvedPath);
|
|
@@ -4368,87 +4471,7 @@ async function loadExtensionsFromPath(path) {
|
|
|
4368
4471
|
|
|
4369
4472
|
// src/runtime/resource-registry.ts
|
|
4370
4473
|
import { stat as stat3, readdir as readdir4 } from "fs/promises";
|
|
4371
|
-
import { join as join4, resolve as
|
|
4372
|
-
|
|
4373
|
-
// src/harness/skills.ts
|
|
4374
|
-
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
4375
|
-
import { readdir as readdir3 } from "fs/promises";
|
|
4376
|
-
import { dirname as dirname5, join as join3, resolve as resolve5 } from "path";
|
|
4377
|
-
var SKILL_MARKER = "SKILL.md";
|
|
4378
|
-
function resolveSkillPath(input) {
|
|
4379
|
-
const resolved = resolve5(input);
|
|
4380
|
-
return existsSync3(resolved) && statSync2(resolved).isDirectory() ? join3(resolved, SKILL_MARKER) : resolved;
|
|
4381
|
-
}
|
|
4382
|
-
async function loadSkill(path) {
|
|
4383
|
-
const resolved = resolveSkillPath(path);
|
|
4384
|
-
let loaded;
|
|
4385
|
-
try {
|
|
4386
|
-
loaded = await loadMarkdown(resolved);
|
|
4387
|
-
} catch (error) {
|
|
4388
|
-
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
4389
|
-
warnResourceDiagnostics("skill", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
4390
|
-
throw new ResourceMetadataError("parse_failed", error.message);
|
|
4391
|
-
}
|
|
4392
|
-
const { frontmatter, body } = loaded;
|
|
4393
|
-
const metadata = frontmatter;
|
|
4394
|
-
const id = inferResourceName(resolved, SKILL_MARKER);
|
|
4395
|
-
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
4396
|
-
const name = frontmatterName || id;
|
|
4397
|
-
const description = validateDescription(metadata.description);
|
|
4398
|
-
const diagnostics = validateResourceId(id);
|
|
4399
|
-
if (frontmatterName && frontmatterName !== id) {
|
|
4400
|
-
diagnostics.push(...validateResourceName(frontmatterName, id));
|
|
4401
|
-
}
|
|
4402
|
-
diagnostics.push(...description.warnings);
|
|
4403
|
-
warnResourceDiagnostics("skill", resolved, diagnostics);
|
|
4404
|
-
if (description.missing) {
|
|
4405
|
-
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
4406
|
-
}
|
|
4407
|
-
return {
|
|
4408
|
-
name,
|
|
4409
|
-
description: description.description,
|
|
4410
|
-
filePath: resolved,
|
|
4411
|
-
baseDir: dirname5(resolved),
|
|
4412
|
-
content: body,
|
|
4413
|
-
disableModelInvocation: metadata["disable-model-invocation"] === true
|
|
4414
|
-
};
|
|
4415
|
-
}
|
|
4416
|
-
async function loadSkills(targetPath) {
|
|
4417
|
-
const skillsDir = resolve5(targetPath);
|
|
4418
|
-
if (existsSync3(skillsDir) && statSync2(skillsDir).isFile()) {
|
|
4419
|
-
try {
|
|
4420
|
-
return [await loadSkill(skillsDir)];
|
|
4421
|
-
} catch (error) {
|
|
4422
|
-
if (error instanceof ResourceMetadataError) return [];
|
|
4423
|
-
throw error;
|
|
4424
|
-
}
|
|
4425
|
-
}
|
|
4426
|
-
if (existsSync3(join3(skillsDir, SKILL_MARKER))) {
|
|
4427
|
-
try {
|
|
4428
|
-
return [await loadSkill(skillsDir)];
|
|
4429
|
-
} catch (error) {
|
|
4430
|
-
if (error instanceof ResourceMetadataError) return [];
|
|
4431
|
-
throw error;
|
|
4432
|
-
}
|
|
4433
|
-
}
|
|
4434
|
-
const entries = await readdir3(skillsDir, { withFileTypes: true });
|
|
4435
|
-
const skills = [];
|
|
4436
|
-
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4437
|
-
if (!entry.isDirectory()) continue;
|
|
4438
|
-
const skillFile = join3(skillsDir, entry.name, SKILL_MARKER);
|
|
4439
|
-
if (!existsSync3(skillFile)) continue;
|
|
4440
|
-
try {
|
|
4441
|
-
const skill = await loadSkill(skillFile);
|
|
4442
|
-
skills.push(skill);
|
|
4443
|
-
} catch (error) {
|
|
4444
|
-
if (error instanceof ResourceMetadataError) continue;
|
|
4445
|
-
console.warn(`Failed to load skill "${entry.name}":`, error);
|
|
4446
|
-
}
|
|
4447
|
-
}
|
|
4448
|
-
return skills;
|
|
4449
|
-
}
|
|
4450
|
-
|
|
4451
|
-
// src/runtime/resource-registry.ts
|
|
4474
|
+
import { join as join4, resolve as resolve7 } from "path";
|
|
4452
4475
|
var ResourceRegistryError = class extends Error {
|
|
4453
4476
|
code;
|
|
4454
4477
|
constructor(code, message) {
|
|
@@ -4592,11 +4615,12 @@ var ResourceRegistry = class {
|
|
|
4592
4615
|
names.add(value.name);
|
|
4593
4616
|
}
|
|
4594
4617
|
const revision = this.nextRevision(kind, sourceId);
|
|
4618
|
+
const snapshottedValues = values.map((value) => snapshotResourceValue(kind, value));
|
|
4595
4619
|
const record = {
|
|
4596
4620
|
kind,
|
|
4597
4621
|
sourceId,
|
|
4598
4622
|
revision,
|
|
4599
|
-
values: Object.freeze(
|
|
4623
|
+
values: Object.freeze(snapshottedValues)
|
|
4600
4624
|
};
|
|
4601
4625
|
let bySource = this.sources.get(kind);
|
|
4602
4626
|
if (!bySource) {
|
|
@@ -4666,6 +4690,18 @@ var ResourceRegistry = class {
|
|
|
4666
4690
|
return `${kind}:${sourceId}:${this.revisionSequence}`;
|
|
4667
4691
|
}
|
|
4668
4692
|
};
|
|
4693
|
+
function snapshotResourceValue(kind, value) {
|
|
4694
|
+
if (kind !== "phase") return value;
|
|
4695
|
+
const phase = value;
|
|
4696
|
+
const snapshot = {
|
|
4697
|
+
...phase,
|
|
4698
|
+
...phase.tools ? { tools: Object.freeze([...phase.tools]) } : {},
|
|
4699
|
+
skills: Object.freeze((phase.skills ?? []).map((skill) => Object.freeze({ ...skill }))),
|
|
4700
|
+
...phase.input ? { input: Object.freeze({ ...phase.input }) } : {},
|
|
4701
|
+
...phase.model ? { model: Object.freeze({ ...phase.model }) } : {}
|
|
4702
|
+
};
|
|
4703
|
+
return Object.freeze(snapshot);
|
|
4704
|
+
}
|
|
4669
4705
|
function assertSourceId(sourceId) {
|
|
4670
4706
|
if (typeof sourceId !== "string" || !SOURCE_ID.test(sourceId)) {
|
|
4671
4707
|
throw new ResourceRegistryError("invalid_source_id", `Invalid resource source "${String(sourceId)}".`);
|
|
@@ -4687,7 +4723,7 @@ function isImplicitCoreName(kind, name) {
|
|
|
4687
4723
|
return kind === "tool" && name === "route" || kind === "phase" && name === "default";
|
|
4688
4724
|
}
|
|
4689
4725
|
async function loadDirectoryValues(kind, directory, sourceId) {
|
|
4690
|
-
const root =
|
|
4726
|
+
const root = resolve7(directory);
|
|
4691
4727
|
let rootInfo;
|
|
4692
4728
|
try {
|
|
4693
4729
|
rootInfo = await stat3(root);
|
|
@@ -5009,9 +5045,8 @@ function intersectPhaseSelection(parent, layer) {
|
|
|
5009
5045
|
}
|
|
5010
5046
|
function resolvePhases(candidates, selection) {
|
|
5011
5047
|
const selected = selectNamedResources(candidates, selection?.phaseIds, "Phase");
|
|
5012
|
-
if (selected.length === 0 && selection === void 0) return void 0;
|
|
5013
5048
|
const entryPhaseId = selection?.entryPhaseId ?? null;
|
|
5014
|
-
if (entryPhaseId !== null && !selected.some(({ name }) => name === entryPhaseId)) {
|
|
5049
|
+
if (entryPhaseId !== null && entryPhaseId !== DEFAULT_PHASE_ID && !selected.some(({ name }) => name === entryPhaseId)) {
|
|
5015
5050
|
console.warn(`Phase entry "${entryPhaseId}" is not available and will be skipped.`);
|
|
5016
5051
|
return { phases: new Map(selected.map((phase) => [phase.name, phase])), entryPhaseId: null };
|
|
5017
5052
|
}
|
|
@@ -5626,7 +5661,7 @@ function boundaryFromSnapshot(snapshot) {
|
|
|
5626
5661
|
}
|
|
5627
5662
|
}
|
|
5628
5663
|
function delay(ms) {
|
|
5629
|
-
return new Promise((
|
|
5664
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
5630
5665
|
}
|
|
5631
5666
|
function normalizePhaseRegistry(registry) {
|
|
5632
5667
|
if (registry?.phases.has(DEFAULT_PHASE_ID) && registry.phases.get(DEFAULT_PHASE_ID)?.filePath !== "") {
|
|
@@ -5653,13 +5688,13 @@ function toJsonValue(value) {
|
|
|
5653
5688
|
return value;
|
|
5654
5689
|
}
|
|
5655
5690
|
function deferred2() {
|
|
5656
|
-
let
|
|
5691
|
+
let resolve8;
|
|
5657
5692
|
let reject;
|
|
5658
5693
|
const promise = new Promise((res, rej) => {
|
|
5659
|
-
|
|
5694
|
+
resolve8 = (value) => res(value);
|
|
5660
5695
|
reject = rej;
|
|
5661
5696
|
});
|
|
5662
|
-
return { promise, resolve:
|
|
5697
|
+
return { promise, resolve: resolve8, reject };
|
|
5663
5698
|
}
|
|
5664
5699
|
|
|
5665
5700
|
// src/runtime/durable-store.ts
|
|
@@ -7137,7 +7172,9 @@ export {
|
|
|
7137
7172
|
createCoreTools2 as createCoreTools,
|
|
7138
7173
|
isRuntimeError,
|
|
7139
7174
|
loadExtensionsFromPath as loadExtensions,
|
|
7175
|
+
loadPhase,
|
|
7140
7176
|
loadPhases,
|
|
7177
|
+
loadSkill,
|
|
7141
7178
|
loadSkills,
|
|
7142
7179
|
materializeConfigurationSnapshot,
|
|
7143
7180
|
parseAgentDefinition,
|