@rowan-agent/agent 0.8.7 → 0.9.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/index.d.ts +17 -28
- package/dist/index.js +241 -207
- 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) */
|
|
@@ -613,6 +590,10 @@ type AgentContext = {
|
|
|
613
590
|
phases?: PhaseRegistry;
|
|
614
591
|
};
|
|
615
592
|
|
|
593
|
+
/**
|
|
594
|
+
* `root` anchors relative paths and the Bash working directory. It is not a
|
|
595
|
+
* filesystem sandbox: core Tools use the permissions of the host process.
|
|
596
|
+
*/
|
|
616
597
|
type CoreToolContext = {
|
|
617
598
|
root?: string;
|
|
618
599
|
maxReadBytes?: number;
|
|
@@ -1176,6 +1157,7 @@ declare class ExtensionRunner {
|
|
|
1176
1157
|
private createExtensionAPI;
|
|
1177
1158
|
private registerTool;
|
|
1178
1159
|
private registerPhase;
|
|
1160
|
+
private loadRegisteredPhase;
|
|
1179
1161
|
private registerProvider;
|
|
1180
1162
|
private unregisterProvider;
|
|
1181
1163
|
private flushPendingProviders;
|
|
@@ -2222,8 +2204,15 @@ declare class RuntimeError<Code extends RuntimeErrorCode = RuntimeErrorCode> ext
|
|
|
2222
2204
|
}
|
|
2223
2205
|
declare function isRuntimeError(value: unknown): value is AnyRuntimeError;
|
|
2224
2206
|
|
|
2207
|
+
declare function loadSkill(path: string): Promise<Skill>;
|
|
2225
2208
|
declare function loadSkills(targetPath: string): Promise<Skill[]>;
|
|
2226
2209
|
|
|
2210
|
+
/**
|
|
2211
|
+
* Load a single phase from a PHASE.md file or phase directory.
|
|
2212
|
+
*
|
|
2213
|
+
* If a directory is provided, the loader reads its PHASE.md.
|
|
2214
|
+
*/
|
|
2215
|
+
declare function loadPhase(targetPath: string): Promise<Phase>;
|
|
2227
2216
|
/**
|
|
2228
2217
|
* Load all phases from the target directory.
|
|
2229
2218
|
*
|
|
@@ -2269,4 +2258,4 @@ declare function parseFrontmatter<T = Record<string, unknown>>(raw: string): Fro
|
|
|
2269
2258
|
|
|
2270
2259
|
declare function createCoreTools(input?: CoreToolContext): Tool[];
|
|
2271
2260
|
|
|
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 };
|
|
2261
|
+
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/harness/tools/index.ts
|
|
2
2
|
import { mkdir, readFile as readFile2, stat, writeFile } from "fs/promises";
|
|
3
|
-
import { basename as basename2, dirname as dirname2,
|
|
3
|
+
import { basename as basename2, dirname as dirname2, relative as relative2, resolve as resolve2 } from "path";
|
|
4
4
|
import Type2 from "typebox";
|
|
5
5
|
import Schema from "typebox/schema";
|
|
6
6
|
|
|
@@ -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
|
}
|
|
@@ -311,9 +308,6 @@ function resolveCoreToolPath(context, path = ".") {
|
|
|
311
308
|
const inputPath = normalizeCoreToolInputPath(path);
|
|
312
309
|
const absolutePath = resolve2(root, inputPath);
|
|
313
310
|
const relativePath = relative2(root, absolutePath);
|
|
314
|
-
if (relativePath === ".." || relativePath.startsWith(`..${sep2}`) || isAbsolute2(relativePath)) {
|
|
315
|
-
throw new Error(`Path escapes workspace root: ${path}`);
|
|
316
|
-
}
|
|
317
311
|
return {
|
|
318
312
|
root,
|
|
319
313
|
inputPath,
|
|
@@ -468,7 +462,7 @@ async function captureStream(stream, maxBytes) {
|
|
|
468
462
|
function createReadTool(context) {
|
|
469
463
|
return {
|
|
470
464
|
name: "read",
|
|
471
|
-
description: "Read a file
|
|
465
|
+
description: "Read a file using the host process filesystem permissions.",
|
|
472
466
|
parameters: ReadArgsSchema,
|
|
473
467
|
promptSnippet: "Read file contents.",
|
|
474
468
|
promptGuidelines: ["Read files before editing them."],
|
|
@@ -539,7 +533,7 @@ function createReadTool(context) {
|
|
|
539
533
|
function createWriteTool(context) {
|
|
540
534
|
return {
|
|
541
535
|
name: "write",
|
|
542
|
-
description: "Create or overwrite a file
|
|
536
|
+
description: "Create or overwrite a file using the host process filesystem permissions.",
|
|
543
537
|
parameters: WriteArgsSchema,
|
|
544
538
|
promptSnippet: "Create or overwrite files.",
|
|
545
539
|
promptGuidelines: ["Use edit for partial changes."],
|
|
@@ -591,7 +585,7 @@ function applyEditReplacements(current, edits) {
|
|
|
591
585
|
function createEditTool(context) {
|
|
592
586
|
return {
|
|
593
587
|
name: "edit",
|
|
594
|
-
description: "Apply exact text replacements
|
|
588
|
+
description: "Apply exact text replacements using the host process filesystem permissions.",
|
|
595
589
|
parameters: EditArgsSchema,
|
|
596
590
|
promptSnippet: "Apply exact text replacements.",
|
|
597
591
|
promptGuidelines: ["Read the file first; each oldText must match exactly once."],
|
|
@@ -627,7 +621,7 @@ function createEditTool(context) {
|
|
|
627
621
|
function createBashTool(context) {
|
|
628
622
|
return {
|
|
629
623
|
name: "bash",
|
|
630
|
-
description: "Run a bash command
|
|
624
|
+
description: "Run a bash command with the host process filesystem permissions; root is the working directory.",
|
|
631
625
|
parameters: BashArgsSchema,
|
|
632
626
|
promptSnippet: "Run shell commands.",
|
|
633
627
|
promptGuidelines: ["Use read/write/edit for file operations."],
|
|
@@ -985,9 +979,9 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
985
979
|
assertActive();
|
|
986
980
|
options?.registerTool?.(tool);
|
|
987
981
|
},
|
|
988
|
-
registerPhase: (registration) => {
|
|
982
|
+
registerPhase: async (registration) => {
|
|
989
983
|
assertActive();
|
|
990
|
-
options?.registerPhase?.(registration);
|
|
984
|
+
await options?.registerPhase?.(registration);
|
|
991
985
|
},
|
|
992
986
|
registerProvider: (config) => {
|
|
993
987
|
assertActive();
|
|
@@ -1037,9 +1031,9 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
1037
1031
|
}
|
|
1038
1032
|
|
|
1039
1033
|
// src/harness/phases/loader.ts
|
|
1040
|
-
import { existsSync, statSync } from "fs";
|
|
1041
|
-
import { readdir } from "fs/promises";
|
|
1042
|
-
import { dirname as
|
|
1034
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
1035
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
1036
|
+
import { dirname as dirname4, join as join2, resolve as resolve4 } from "path";
|
|
1043
1037
|
|
|
1044
1038
|
// src/harness/definitions.ts
|
|
1045
1039
|
import { parseModelRef } from "@rowan-agent/models";
|
|
@@ -1126,9 +1120,8 @@ function optionalStringList(value, field) {
|
|
|
1126
1120
|
function optionalPhaseRegistrySelection(value, field) {
|
|
1127
1121
|
if (value === void 0) return void 0;
|
|
1128
1122
|
if (!isRecord(value)) throw new TypeError(`${field} must be an object`);
|
|
1129
|
-
if (!("entryPhaseId" in value)) throw new TypeError(`${field}.entryPhaseId is required`);
|
|
1130
1123
|
if (!("phaseIds" in value)) throw new TypeError(`${field}.phaseIds is required`);
|
|
1131
|
-
const entryPhaseId = value.entryPhaseId;
|
|
1124
|
+
const entryPhaseId = value.entryPhaseId ?? null;
|
|
1132
1125
|
if (entryPhaseId !== null && (typeof entryPhaseId !== "string" || entryPhaseId.trim() === "")) {
|
|
1133
1126
|
throw new TypeError(`${field}.entryPhaseId must be a non-empty string or null`);
|
|
1134
1127
|
}
|
|
@@ -1203,19 +1196,6 @@ function validateDescription(value) {
|
|
|
1203
1196
|
}
|
|
1204
1197
|
return { description: value, warnings: [], missing: false };
|
|
1205
1198
|
}
|
|
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
1199
|
function validatePhaseTarget(value) {
|
|
1220
1200
|
if (value === void 0) return [];
|
|
1221
1201
|
if (typeof value !== "string") return ["target must be a string"];
|
|
@@ -1228,11 +1208,91 @@ function warnResourceDiagnostics(kind, filePath, diagnostics) {
|
|
|
1228
1208
|
}
|
|
1229
1209
|
}
|
|
1230
1210
|
|
|
1211
|
+
// src/harness/skills.ts
|
|
1212
|
+
import { existsSync, statSync } from "fs";
|
|
1213
|
+
import { readdir } from "fs/promises";
|
|
1214
|
+
import { dirname as dirname3, join, resolve as resolve3 } from "path";
|
|
1215
|
+
var SKILL_MARKER = "SKILL.md";
|
|
1216
|
+
function resolveSkillPath(input) {
|
|
1217
|
+
const resolved = resolve3(input);
|
|
1218
|
+
return existsSync(resolved) && statSync(resolved).isDirectory() ? join(resolved, SKILL_MARKER) : resolved;
|
|
1219
|
+
}
|
|
1220
|
+
async function loadSkill(path) {
|
|
1221
|
+
const resolved = resolveSkillPath(path);
|
|
1222
|
+
let loaded;
|
|
1223
|
+
try {
|
|
1224
|
+
loaded = await loadMarkdown(resolved);
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
1227
|
+
warnResourceDiagnostics("skill", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
1228
|
+
throw new ResourceMetadataError("parse_failed", error.message);
|
|
1229
|
+
}
|
|
1230
|
+
const { frontmatter, body } = loaded;
|
|
1231
|
+
const metadata = frontmatter;
|
|
1232
|
+
const id = inferResourceName(resolved, SKILL_MARKER);
|
|
1233
|
+
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
1234
|
+
const name = frontmatterName || id;
|
|
1235
|
+
const description = validateDescription(metadata.description);
|
|
1236
|
+
const diagnostics = validateResourceId(id);
|
|
1237
|
+
if (frontmatterName && frontmatterName !== id) {
|
|
1238
|
+
diagnostics.push(...validateResourceName(frontmatterName, id));
|
|
1239
|
+
}
|
|
1240
|
+
diagnostics.push(...description.warnings);
|
|
1241
|
+
warnResourceDiagnostics("skill", resolved, diagnostics);
|
|
1242
|
+
if (description.missing) {
|
|
1243
|
+
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
1244
|
+
}
|
|
1245
|
+
return {
|
|
1246
|
+
name,
|
|
1247
|
+
description: description.description,
|
|
1248
|
+
filePath: resolved,
|
|
1249
|
+
baseDir: dirname3(resolved),
|
|
1250
|
+
content: body,
|
|
1251
|
+
disableModelInvocation: metadata["disable-model-invocation"] === true
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
async function loadSkills(targetPath) {
|
|
1255
|
+
const skillsDir = resolve3(targetPath);
|
|
1256
|
+
if (existsSync(skillsDir) && statSync(skillsDir).isFile()) {
|
|
1257
|
+
try {
|
|
1258
|
+
return [await loadSkill(skillsDir)];
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
1261
|
+
throw error;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (existsSync(join(skillsDir, SKILL_MARKER))) {
|
|
1265
|
+
try {
|
|
1266
|
+
return [await loadSkill(skillsDir)];
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
1269
|
+
throw error;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
1273
|
+
const skills = [];
|
|
1274
|
+
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1275
|
+
if (!entry.isDirectory()) continue;
|
|
1276
|
+
const skillFile = join(skillsDir, entry.name, SKILL_MARKER);
|
|
1277
|
+
if (!existsSync(skillFile)) continue;
|
|
1278
|
+
try {
|
|
1279
|
+
const skill = await loadSkill(skillFile);
|
|
1280
|
+
skills.push(skill);
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
if (error instanceof ResourceMetadataError) continue;
|
|
1283
|
+
console.warn(`Failed to load skill "${entry.name}":`, error);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
return skills;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1231
1289
|
// src/harness/phases/loader.ts
|
|
1232
1290
|
var PHASE_MARKER = "PHASE.md";
|
|
1291
|
+
var SKILL_MARKER2 = "SKILL.md";
|
|
1292
|
+
var RESOURCE_MARKERS = /* @__PURE__ */ new Set([PHASE_MARKER, SKILL_MARKER2]);
|
|
1233
1293
|
function resolvePhasePath(input) {
|
|
1234
|
-
const resolved =
|
|
1235
|
-
return
|
|
1294
|
+
const resolved = resolve4(input);
|
|
1295
|
+
return existsSync2(resolved) && statSync2(resolved).isDirectory() ? join2(resolved, PHASE_MARKER) : resolved;
|
|
1236
1296
|
}
|
|
1237
1297
|
async function loadPhase(targetPath) {
|
|
1238
1298
|
const resolved = resolvePhasePath(targetPath);
|
|
@@ -1250,7 +1310,9 @@ async function loadPhase(targetPath) {
|
|
|
1250
1310
|
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
1251
1311
|
let definition;
|
|
1252
1312
|
try {
|
|
1253
|
-
|
|
1313
|
+
const phaseMetadata = { ...metadata };
|
|
1314
|
+
delete phaseMetadata.skills;
|
|
1315
|
+
definition = normalizeAgentDefinition(phaseMetadata, body, { fallbackName: directoryName });
|
|
1254
1316
|
} catch (error) {
|
|
1255
1317
|
const message = error instanceof Error ? error.message : String(error);
|
|
1256
1318
|
warnResourceDiagnostics("phase", resolved, [message]);
|
|
@@ -1263,15 +1325,15 @@ async function loadPhase(targetPath) {
|
|
|
1263
1325
|
diagnostics.push(...validateResourceName(frontmatterName, directoryName));
|
|
1264
1326
|
}
|
|
1265
1327
|
diagnostics.push(...description.warnings);
|
|
1266
|
-
diagnostics.push(...validateSkillReferences(metadata.skills));
|
|
1267
1328
|
diagnostics.push(...validatePhaseTarget(metadata.target));
|
|
1268
1329
|
warnResourceDiagnostics("phase", resolved, diagnostics);
|
|
1269
|
-
const baseDir =
|
|
1330
|
+
const baseDir = dirname4(resolved);
|
|
1331
|
+
const skills = await loadPhaseSkills(baseDir);
|
|
1270
1332
|
const phase = {
|
|
1271
1333
|
name,
|
|
1272
1334
|
description: description.description,
|
|
1273
1335
|
tools: definition.tools ? [...definition.tools] : void 0,
|
|
1274
|
-
skills
|
|
1336
|
+
skills,
|
|
1275
1337
|
target: metadata.target,
|
|
1276
1338
|
input: metadata.input,
|
|
1277
1339
|
isolated: metadata.isolated,
|
|
@@ -1291,6 +1353,54 @@ async function loadPhase(targetPath) {
|
|
|
1291
1353
|
}
|
|
1292
1354
|
return phase;
|
|
1293
1355
|
}
|
|
1356
|
+
async function loadPhaseSkills(baseDir) {
|
|
1357
|
+
const entries = await readdir2(baseDir, { withFileTypes: true });
|
|
1358
|
+
const skills = [];
|
|
1359
|
+
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1360
|
+
const entryPath = join2(baseDir, entry.name);
|
|
1361
|
+
if (!entry.isDirectory()) {
|
|
1362
|
+
if (RESOURCE_MARKERS.has(entry.name) && entry.name !== PHASE_MARKER) {
|
|
1363
|
+
throw invalidPhaseBundle(`resource marker ${entry.name} is not valid at the phase root`);
|
|
1364
|
+
}
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
const markers = await findResourceMarkers(entryPath);
|
|
1368
|
+
const directSkillPath = join2(entryPath, SKILL_MARKER2);
|
|
1369
|
+
const hasDirectSkill = markers.includes(directSkillPath);
|
|
1370
|
+
if (!hasDirectSkill) {
|
|
1371
|
+
if (markers.length > 0) {
|
|
1372
|
+
throw invalidPhaseBundle(`resource marker ${markers[0]} is not a direct child Skill`);
|
|
1373
|
+
}
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const nestedMarkers = markers.filter((marker) => marker !== directSkillPath);
|
|
1377
|
+
if (nestedMarkers.length > 0) {
|
|
1378
|
+
throw invalidPhaseBundle(`resource marker ${nestedMarkers[0]} is nested below a Skill bundle`);
|
|
1379
|
+
}
|
|
1380
|
+
const skill = await loadSkill(directSkillPath);
|
|
1381
|
+
if (skills.some((candidate) => candidate.name === skill.name)) {
|
|
1382
|
+
throw invalidPhaseBundle(`duplicate bundled Skill name "${skill.name}"`);
|
|
1383
|
+
}
|
|
1384
|
+
skills.push(skill);
|
|
1385
|
+
}
|
|
1386
|
+
return skills;
|
|
1387
|
+
}
|
|
1388
|
+
async function findResourceMarkers(directory) {
|
|
1389
|
+
const entries = await readdir2(directory, { withFileTypes: true });
|
|
1390
|
+
const markers = [];
|
|
1391
|
+
for (const entry of entries) {
|
|
1392
|
+
const entryPath = join2(directory, entry.name);
|
|
1393
|
+
if (entry.isDirectory()) {
|
|
1394
|
+
markers.push(...await findResourceMarkers(entryPath));
|
|
1395
|
+
} else if (RESOURCE_MARKERS.has(entry.name)) {
|
|
1396
|
+
markers.push(entryPath);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
return markers;
|
|
1400
|
+
}
|
|
1401
|
+
function invalidPhaseBundle(message) {
|
|
1402
|
+
return new ResourceMetadataError("invalid_metadata", message);
|
|
1403
|
+
}
|
|
1294
1404
|
function readPhaseContent(phase) {
|
|
1295
1405
|
return formatResourceOutput({
|
|
1296
1406
|
type: "phase",
|
|
@@ -1302,8 +1412,8 @@ function readPhaseContent(phase) {
|
|
|
1302
1412
|
}
|
|
1303
1413
|
async function loadPhases(targetPath) {
|
|
1304
1414
|
const phases = /* @__PURE__ */ new Map();
|
|
1305
|
-
const phasesDir =
|
|
1306
|
-
if (
|
|
1415
|
+
const phasesDir = resolve4(targetPath);
|
|
1416
|
+
if (existsSync2(phasesDir) && statSync2(phasesDir).isFile()) {
|
|
1307
1417
|
try {
|
|
1308
1418
|
const phase = await loadPhase(phasesDir);
|
|
1309
1419
|
phases.set(phase.name, phase);
|
|
@@ -1312,7 +1422,7 @@ async function loadPhases(targetPath) {
|
|
|
1312
1422
|
}
|
|
1313
1423
|
return { phases, entryPhaseId: null };
|
|
1314
1424
|
}
|
|
1315
|
-
if (
|
|
1425
|
+
if (existsSync2(join2(phasesDir, PHASE_MARKER))) {
|
|
1316
1426
|
try {
|
|
1317
1427
|
const phase = await loadPhase(phasesDir);
|
|
1318
1428
|
phases.set(phase.name, phase);
|
|
@@ -1321,11 +1431,11 @@ async function loadPhases(targetPath) {
|
|
|
1321
1431
|
}
|
|
1322
1432
|
return { phases, entryPhaseId: null };
|
|
1323
1433
|
}
|
|
1324
|
-
const entries = await
|
|
1434
|
+
const entries = await readdir2(phasesDir, { withFileTypes: true });
|
|
1325
1435
|
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1326
1436
|
if (!entry.isDirectory()) continue;
|
|
1327
|
-
const phaseFile =
|
|
1328
|
-
if (!
|
|
1437
|
+
const phaseFile = join2(phasesDir, entry.name, PHASE_MARKER);
|
|
1438
|
+
if (!existsSync2(phaseFile)) continue;
|
|
1329
1439
|
try {
|
|
1330
1440
|
const phase = await loadPhase(phaseFile);
|
|
1331
1441
|
phases.set(phase.name, phase);
|
|
@@ -1339,8 +1449,8 @@ async function loadPhases(targetPath) {
|
|
|
1339
1449
|
async function discoverPhaseCode(baseDir) {
|
|
1340
1450
|
const candidates = ["index.ts", "index.js"];
|
|
1341
1451
|
for (const candidate of candidates) {
|
|
1342
|
-
const fullPath =
|
|
1343
|
-
if (
|
|
1452
|
+
const fullPath = join2(baseDir, candidate);
|
|
1453
|
+
if (existsSync2(fullPath)) {
|
|
1344
1454
|
return fullPath;
|
|
1345
1455
|
}
|
|
1346
1456
|
}
|
|
@@ -1372,6 +1482,7 @@ function createDefaultPhase() {
|
|
|
1372
1482
|
description: "Executes concrete task operations and produces artifacts.",
|
|
1373
1483
|
filePath: "",
|
|
1374
1484
|
baseDir: "",
|
|
1485
|
+
skills: [],
|
|
1375
1486
|
content: "Execute tasks using current context.\nNo planning. No evaluation.\nRoute to next phase or stop when done."
|
|
1376
1487
|
};
|
|
1377
1488
|
}
|
|
@@ -1426,7 +1537,7 @@ function buildSystemPrompt(options) {
|
|
|
1426
1537
|
}
|
|
1427
1538
|
const guidelines = guidelinesList.map((g) => `- ${g}`).join("\n");
|
|
1428
1539
|
const skillsSection = skillsBlock ? `The following skills provide specialized instructions for specific tasks.
|
|
1429
|
-
|
|
1540
|
+
Use the read tool to load a skill's file when the task matches its description.
|
|
1430
1541
|
When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md) and use that absolute path in tool commands.
|
|
1431
1542
|
|
|
1432
1543
|
${skillsBlock}
|
|
@@ -1929,7 +2040,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1929
2040
|
const toolExecutionManager = createToolExecutionManager();
|
|
1930
2041
|
const execution = createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry);
|
|
1931
2042
|
const phaseTools = selectPhaseTools(allTools, phase.tools);
|
|
1932
|
-
const phaseSkills =
|
|
2043
|
+
const phaseSkills = phase.name === DEFAULT_PHASE_ID ? config.context.skills : phase.skills ?? [];
|
|
1933
2044
|
let phaseContext = {
|
|
1934
2045
|
systemPrompt: config.context.systemPrompt,
|
|
1935
2046
|
messages: messageManager.visible(),
|
|
@@ -2208,7 +2319,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2208
2319
|
}
|
|
2209
2320
|
const delayMs = getRetryDelay(attempt, baseDelayMs, maxDelayMs);
|
|
2210
2321
|
options.onRetry?.(attempt, error, delayMs);
|
|
2211
|
-
await new Promise((
|
|
2322
|
+
await new Promise((resolve8) => setTimeout(resolve8, delayMs));
|
|
2212
2323
|
if (options.signal?.aborted) {
|
|
2213
2324
|
throw lastError;
|
|
2214
2325
|
}
|
|
@@ -2220,7 +2331,7 @@ async function executePhase(ctx) {
|
|
|
2220
2331
|
const { phase, config, execution, registry, context } = ctx;
|
|
2221
2332
|
if (phase.factory) {
|
|
2222
2333
|
const api = createExtensionAPI(void 0, {
|
|
2223
|
-
registerPhase: () => {
|
|
2334
|
+
registerPhase: async () => {
|
|
2224
2335
|
},
|
|
2225
2336
|
registerProvider: () => {
|
|
2226
2337
|
},
|
|
@@ -2242,7 +2353,7 @@ async function executePhase(ctx) {
|
|
|
2242
2353
|
config.context.messages.push(createMessage(role, content));
|
|
2243
2354
|
},
|
|
2244
2355
|
getAvailableTools: () => config.context.tools.map((t) => ({ name: t.name, description: t.description })),
|
|
2245
|
-
getAvailableSkills: () =>
|
|
2356
|
+
getAvailableSkills: () => context.skills.map((s) => ({ name: s.name, description: s.description })),
|
|
2246
2357
|
getPhaseContent: (id) => registry.phases.get(id)?.content ?? "",
|
|
2247
2358
|
getAvailablePhases: () => Array.from(registry.phases.keys())
|
|
2248
2359
|
},
|
|
@@ -2420,7 +2531,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2420
2531
|
const messages = [...context];
|
|
2421
2532
|
const allTools = buildToolsWithRouting(config, availablePhases);
|
|
2422
2533
|
const phaseTools = selectPhaseTools(allTools, phase.tools);
|
|
2423
|
-
const phaseSkills =
|
|
2534
|
+
const phaseSkills = phase.name === DEFAULT_PHASE_ID ? config.context.skills : phase.skills ?? [];
|
|
2424
2535
|
const systemPrompt = config.context.systemPrompt;
|
|
2425
2536
|
const phaseContext = {
|
|
2426
2537
|
systemPrompt,
|
|
@@ -2914,6 +3025,7 @@ function snapshotConfig(config) {
|
|
|
2914
3025
|
...config.resources,
|
|
2915
3026
|
tools: Object.freeze([...config.resources.tools]),
|
|
2916
3027
|
skills: Object.freeze([...config.resources.skills]),
|
|
3028
|
+
...config.resources.phases ? { phases: snapshotPhaseRegistry(config.resources.phases) } : {},
|
|
2917
3029
|
...config.resources.resourceView ? {
|
|
2918
3030
|
resourceView: Object.freeze({
|
|
2919
3031
|
agents: Object.freeze([...config.resources.resourceView.agents]),
|
|
@@ -2937,6 +3049,20 @@ function snapshotConfig(config) {
|
|
|
2937
3049
|
});
|
|
2938
3050
|
return Object.freeze({ ...config, definition, resources });
|
|
2939
3051
|
}
|
|
3052
|
+
function snapshotPhaseRegistry(registry) {
|
|
3053
|
+
const phases = /* @__PURE__ */ new Map();
|
|
3054
|
+
for (const [name, phase] of registry.phases) phases.set(name, snapshotPhase(phase));
|
|
3055
|
+
return { phases, entryPhaseId: registry.entryPhaseId };
|
|
3056
|
+
}
|
|
3057
|
+
function snapshotPhase(phase) {
|
|
3058
|
+
return Object.freeze({
|
|
3059
|
+
...phase,
|
|
3060
|
+
...phase.tools ? { tools: Object.freeze([...phase.tools]) } : {},
|
|
3061
|
+
skills: Object.freeze((phase.skills ?? []).map((skill) => Object.freeze({ ...skill }))),
|
|
3062
|
+
...phase.input ? { input: Object.freeze({ ...phase.input }) } : {},
|
|
3063
|
+
...phase.model ? { model: Object.freeze({ ...phase.model }) } : {}
|
|
3064
|
+
});
|
|
3065
|
+
}
|
|
2940
3066
|
function snapshotConfiguration(config) {
|
|
2941
3067
|
const layer = config.definition.layer;
|
|
2942
3068
|
const definition = Object.freeze({
|
|
@@ -3436,11 +3562,11 @@ function fromLoopResult(result) {
|
|
|
3436
3562
|
var MAX_BUFFERED_EVENTS = 128;
|
|
3437
3563
|
var MAX_BUFFERED_TEXT = 64 * 1024;
|
|
3438
3564
|
function deferred() {
|
|
3439
|
-
let
|
|
3565
|
+
let resolve8;
|
|
3440
3566
|
const promise = new Promise((done) => {
|
|
3441
|
-
|
|
3567
|
+
resolve8 = done;
|
|
3442
3568
|
});
|
|
3443
|
-
return { promise, resolve:
|
|
3569
|
+
return { promise, resolve: resolve8 };
|
|
3444
3570
|
}
|
|
3445
3571
|
var TransientRunEventSubscription = class {
|
|
3446
3572
|
queue = [];
|
|
@@ -3626,6 +3752,7 @@ var HooksManager = class {
|
|
|
3626
3752
|
|
|
3627
3753
|
// src/extensions/runner.ts
|
|
3628
3754
|
import { execFile } from "child_process";
|
|
3755
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
3629
3756
|
import {
|
|
3630
3757
|
registerModel,
|
|
3631
3758
|
unregisterProviderModels,
|
|
@@ -3657,9 +3784,6 @@ function createExtensionRuntime() {
|
|
|
3657
3784
|
};
|
|
3658
3785
|
}
|
|
3659
3786
|
|
|
3660
|
-
// src/extensions/runner.ts
|
|
3661
|
-
import { parseModelRef as parseModelRef2 } from "@rowan-agent/models";
|
|
3662
|
-
|
|
3663
3787
|
// src/extensions/event-bus.ts
|
|
3664
3788
|
function createEventBus() {
|
|
3665
3789
|
const listeners = /* @__PURE__ */ new Map();
|
|
@@ -3703,7 +3827,7 @@ function createEventBus() {
|
|
|
3703
3827
|
|
|
3704
3828
|
// src/extensions/runner.ts
|
|
3705
3829
|
async function execCommand(command, args, cwd, options) {
|
|
3706
|
-
return new Promise((
|
|
3830
|
+
return new Promise((resolve8, reject) => {
|
|
3707
3831
|
const child = execFile(
|
|
3708
3832
|
command,
|
|
3709
3833
|
args,
|
|
@@ -3718,7 +3842,7 @@ async function execCommand(command, args, cwd, options) {
|
|
|
3718
3842
|
reject(new Error("Command was aborted"));
|
|
3719
3843
|
return;
|
|
3720
3844
|
}
|
|
3721
|
-
|
|
3845
|
+
resolve8({
|
|
3722
3846
|
exitCode: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
3723
3847
|
stdout: stdout ?? "",
|
|
3724
3848
|
stderr: stderr ?? ""
|
|
@@ -3955,17 +4079,10 @@ var ExtensionRunner = class {
|
|
|
3955
4079
|
adaptToPhase(reg) {
|
|
3956
4080
|
const def = reg.definition;
|
|
3957
4081
|
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) } : {}
|
|
4082
|
+
...def,
|
|
4083
|
+
...def.tools ? { tools: [...def.tools] } : {},
|
|
4084
|
+
skills: def.skills ? [...def.skills] : [],
|
|
4085
|
+
...def.input ? { input: { ...def.input } } : {}
|
|
3969
4086
|
};
|
|
3970
4087
|
}
|
|
3971
4088
|
// ---------------------------------------------------------------------------
|
|
@@ -4127,36 +4244,20 @@ var ExtensionRunner = class {
|
|
|
4127
4244
|
});
|
|
4128
4245
|
}
|
|
4129
4246
|
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);
|
|
4247
|
+
if (typeof registration !== "string" || registration.length === 0) {
|
|
4248
|
+
throw new Error(`Phase registration requires a directory path.`);
|
|
4144
4249
|
}
|
|
4250
|
+
return this.loadRegisteredPhase(extension, registration);
|
|
4251
|
+
}
|
|
4252
|
+
async loadRegisteredPhase(extension, registration) {
|
|
4253
|
+
const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname5(extension.path);
|
|
4254
|
+
const phase = await loadPhase(resolve5(extensionBase, registration));
|
|
4255
|
+
const name = phase.name;
|
|
4145
4256
|
if (this.phases.has(name)) {
|
|
4146
4257
|
throw new Error(`Duplicate phase name: ${name}`);
|
|
4147
4258
|
}
|
|
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
4259
|
const registered = {
|
|
4159
|
-
definition,
|
|
4260
|
+
definition: phase,
|
|
4160
4261
|
source: { extensionPath: extension.path }
|
|
4161
4262
|
};
|
|
4162
4263
|
this.phases.set(name, registered);
|
|
@@ -4215,10 +4316,10 @@ function createExtensionRunner(options) {
|
|
|
4215
4316
|
}
|
|
4216
4317
|
|
|
4217
4318
|
// src/extensions/loader.ts
|
|
4218
|
-
import { existsSync as
|
|
4219
|
-
import { readdir as
|
|
4319
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
4320
|
+
import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
4220
4321
|
import { fileURLToPath } from "url";
|
|
4221
|
-
import { basename as basename3, dirname as
|
|
4322
|
+
import { basename as basename3, dirname as dirname6, extname as extname2, join as join3, resolve as resolve6 } from "path";
|
|
4222
4323
|
import { createJiti } from "jiti";
|
|
4223
4324
|
function isExtensionFile(path) {
|
|
4224
4325
|
const extension = extname2(path).toLowerCase();
|
|
@@ -4228,8 +4329,8 @@ function isSyntheticPath(path) {
|
|
|
4228
4329
|
return path.startsWith("<") && path.endsWith(">");
|
|
4229
4330
|
}
|
|
4230
4331
|
function readManifestSync(dir) {
|
|
4231
|
-
const manifestPath =
|
|
4232
|
-
if (!
|
|
4332
|
+
const manifestPath = join3(dir, "package.json");
|
|
4333
|
+
if (!existsSync3(manifestPath)) {
|
|
4233
4334
|
return void 0;
|
|
4234
4335
|
}
|
|
4235
4336
|
try {
|
|
@@ -4238,8 +4339,7 @@ function readManifestSync(dir) {
|
|
|
4238
4339
|
const rowan = pkg.rowan;
|
|
4239
4340
|
if (!rowan) return void 0;
|
|
4240
4341
|
return {
|
|
4241
|
-
entry: rowan.extensions?.[0]
|
|
4242
|
-
phase: rowan.phase
|
|
4342
|
+
entry: rowan.extensions?.[0]
|
|
4243
4343
|
};
|
|
4244
4344
|
} catch {
|
|
4245
4345
|
return void 0;
|
|
@@ -4251,7 +4351,7 @@ function jitiAliases() {
|
|
|
4251
4351
|
return {};
|
|
4252
4352
|
}
|
|
4253
4353
|
const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
|
|
4254
|
-
if (!
|
|
4354
|
+
if (!existsSync3(agentSourcePath)) {
|
|
4255
4355
|
return {};
|
|
4256
4356
|
}
|
|
4257
4357
|
return { "@rowan-agent/agent": agentSourcePath };
|
|
@@ -4272,28 +4372,28 @@ async function loadExtensionModule(extensionPath) {
|
|
|
4272
4372
|
return typeof module === "function" ? module : void 0;
|
|
4273
4373
|
}
|
|
4274
4374
|
async function readPackageManifest(path) {
|
|
4275
|
-
if (!
|
|
4375
|
+
if (!existsSync3(path)) {
|
|
4276
4376
|
return void 0;
|
|
4277
4377
|
}
|
|
4278
4378
|
const content = await readFile3(path, "utf8");
|
|
4279
4379
|
return JSON.parse(content);
|
|
4280
4380
|
}
|
|
4281
4381
|
async function resolveExtensionEntries(dir) {
|
|
4282
|
-
const manifest = await readPackageManifest(
|
|
4382
|
+
const manifest = await readPackageManifest(join3(dir, "package.json"));
|
|
4283
4383
|
const declared = manifest?.rowan?.extensions;
|
|
4284
4384
|
if (declared && declared.length > 0) {
|
|
4285
|
-
return declared.map((entry) =>
|
|
4385
|
+
return declared.map((entry) => resolve6(dir, entry));
|
|
4286
4386
|
}
|
|
4287
4387
|
for (const name of ["index.ts", "index.js"]) {
|
|
4288
|
-
const entry =
|
|
4289
|
-
if (
|
|
4388
|
+
const entry = join3(dir, name);
|
|
4389
|
+
if (existsSync3(entry)) {
|
|
4290
4390
|
return [entry];
|
|
4291
4391
|
}
|
|
4292
4392
|
}
|
|
4293
4393
|
return void 0;
|
|
4294
4394
|
}
|
|
4295
4395
|
async function discoverExtensionsInDir(dir) {
|
|
4296
|
-
const entries = await
|
|
4396
|
+
const entries = await readdir3(dir, { withFileTypes: true }).catch((error) => {
|
|
4297
4397
|
if (error.code === "ENOENT") {
|
|
4298
4398
|
return [];
|
|
4299
4399
|
}
|
|
@@ -4301,7 +4401,7 @@ async function discoverExtensionsInDir(dir) {
|
|
|
4301
4401
|
});
|
|
4302
4402
|
const paths = [];
|
|
4303
4403
|
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4304
|
-
const entryPath =
|
|
4404
|
+
const entryPath = join3(dir, entry.name);
|
|
4305
4405
|
if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
|
|
4306
4406
|
paths.push(entryPath);
|
|
4307
4407
|
continue;
|
|
@@ -4319,10 +4419,10 @@ async function discoverExtensionsInDir(dir) {
|
|
|
4319
4419
|
return paths;
|
|
4320
4420
|
}
|
|
4321
4421
|
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 :
|
|
4422
|
+
const resolvedCwd = resolve6(cwd);
|
|
4423
|
+
const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve6(resolvedCwd, extensionPath);
|
|
4424
|
+
const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname6(resolvedPath)) || "unknown";
|
|
4425
|
+
const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname6(resolvedPath);
|
|
4326
4426
|
const manifest = readManifestSync(manifestDir);
|
|
4327
4427
|
return {
|
|
4328
4428
|
path: resolvedPath,
|
|
@@ -4334,9 +4434,9 @@ function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
|
|
|
4334
4434
|
async function loadExtensions(paths, cwd) {
|
|
4335
4435
|
const extensions = [];
|
|
4336
4436
|
const errors = [];
|
|
4337
|
-
const resolvedCwd =
|
|
4437
|
+
const resolvedCwd = resolve6(cwd);
|
|
4338
4438
|
for (const path of paths) {
|
|
4339
|
-
const resolvedPath =
|
|
4439
|
+
const resolvedPath = resolve6(resolvedCwd, path);
|
|
4340
4440
|
try {
|
|
4341
4441
|
const factory = await loadExtensionModule(resolvedPath);
|
|
4342
4442
|
if (!factory) {
|
|
@@ -4357,10 +4457,10 @@ async function loadExtensions(paths, cwd) {
|
|
|
4357
4457
|
return { extensions, errors };
|
|
4358
4458
|
}
|
|
4359
4459
|
async function loadExtensionsFromPath(path) {
|
|
4360
|
-
const resolvedPath =
|
|
4460
|
+
const resolvedPath = resolve6(path);
|
|
4361
4461
|
const info = await stat2(resolvedPath);
|
|
4362
4462
|
if (!info.isDirectory()) {
|
|
4363
|
-
return loadExtensions([resolvedPath],
|
|
4463
|
+
return loadExtensions([resolvedPath], dirname6(resolvedPath));
|
|
4364
4464
|
}
|
|
4365
4465
|
const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
|
|
4366
4466
|
return loadExtensions(entries, resolvedPath);
|
|
@@ -4368,87 +4468,7 @@ async function loadExtensionsFromPath(path) {
|
|
|
4368
4468
|
|
|
4369
4469
|
// src/runtime/resource-registry.ts
|
|
4370
4470
|
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
|
|
4471
|
+
import { join as join4, resolve as resolve7 } from "path";
|
|
4452
4472
|
var ResourceRegistryError = class extends Error {
|
|
4453
4473
|
code;
|
|
4454
4474
|
constructor(code, message) {
|
|
@@ -4592,11 +4612,12 @@ var ResourceRegistry = class {
|
|
|
4592
4612
|
names.add(value.name);
|
|
4593
4613
|
}
|
|
4594
4614
|
const revision = this.nextRevision(kind, sourceId);
|
|
4615
|
+
const snapshottedValues = values.map((value) => snapshotResourceValue(kind, value));
|
|
4595
4616
|
const record = {
|
|
4596
4617
|
kind,
|
|
4597
4618
|
sourceId,
|
|
4598
4619
|
revision,
|
|
4599
|
-
values: Object.freeze(
|
|
4620
|
+
values: Object.freeze(snapshottedValues)
|
|
4600
4621
|
};
|
|
4601
4622
|
let bySource = this.sources.get(kind);
|
|
4602
4623
|
if (!bySource) {
|
|
@@ -4666,6 +4687,18 @@ var ResourceRegistry = class {
|
|
|
4666
4687
|
return `${kind}:${sourceId}:${this.revisionSequence}`;
|
|
4667
4688
|
}
|
|
4668
4689
|
};
|
|
4690
|
+
function snapshotResourceValue(kind, value) {
|
|
4691
|
+
if (kind !== "phase") return value;
|
|
4692
|
+
const phase = value;
|
|
4693
|
+
const snapshot = {
|
|
4694
|
+
...phase,
|
|
4695
|
+
...phase.tools ? { tools: Object.freeze([...phase.tools]) } : {},
|
|
4696
|
+
skills: Object.freeze((phase.skills ?? []).map((skill) => Object.freeze({ ...skill }))),
|
|
4697
|
+
...phase.input ? { input: Object.freeze({ ...phase.input }) } : {},
|
|
4698
|
+
...phase.model ? { model: Object.freeze({ ...phase.model }) } : {}
|
|
4699
|
+
};
|
|
4700
|
+
return Object.freeze(snapshot);
|
|
4701
|
+
}
|
|
4669
4702
|
function assertSourceId(sourceId) {
|
|
4670
4703
|
if (typeof sourceId !== "string" || !SOURCE_ID.test(sourceId)) {
|
|
4671
4704
|
throw new ResourceRegistryError("invalid_source_id", `Invalid resource source "${String(sourceId)}".`);
|
|
@@ -4687,7 +4720,7 @@ function isImplicitCoreName(kind, name) {
|
|
|
4687
4720
|
return kind === "tool" && name === "route" || kind === "phase" && name === "default";
|
|
4688
4721
|
}
|
|
4689
4722
|
async function loadDirectoryValues(kind, directory, sourceId) {
|
|
4690
|
-
const root =
|
|
4723
|
+
const root = resolve7(directory);
|
|
4691
4724
|
let rootInfo;
|
|
4692
4725
|
try {
|
|
4693
4726
|
rootInfo = await stat3(root);
|
|
@@ -5009,9 +5042,8 @@ function intersectPhaseSelection(parent, layer) {
|
|
|
5009
5042
|
}
|
|
5010
5043
|
function resolvePhases(candidates, selection) {
|
|
5011
5044
|
const selected = selectNamedResources(candidates, selection?.phaseIds, "Phase");
|
|
5012
|
-
if (selected.length === 0 && selection === void 0) return void 0;
|
|
5013
5045
|
const entryPhaseId = selection?.entryPhaseId ?? null;
|
|
5014
|
-
if (entryPhaseId !== null && !selected.some(({ name }) => name === entryPhaseId)) {
|
|
5046
|
+
if (entryPhaseId !== null && entryPhaseId !== DEFAULT_PHASE_ID && !selected.some(({ name }) => name === entryPhaseId)) {
|
|
5015
5047
|
console.warn(`Phase entry "${entryPhaseId}" is not available and will be skipped.`);
|
|
5016
5048
|
return { phases: new Map(selected.map((phase) => [phase.name, phase])), entryPhaseId: null };
|
|
5017
5049
|
}
|
|
@@ -5626,7 +5658,7 @@ function boundaryFromSnapshot(snapshot) {
|
|
|
5626
5658
|
}
|
|
5627
5659
|
}
|
|
5628
5660
|
function delay(ms) {
|
|
5629
|
-
return new Promise((
|
|
5661
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
5630
5662
|
}
|
|
5631
5663
|
function normalizePhaseRegistry(registry) {
|
|
5632
5664
|
if (registry?.phases.has(DEFAULT_PHASE_ID) && registry.phases.get(DEFAULT_PHASE_ID)?.filePath !== "") {
|
|
@@ -5653,13 +5685,13 @@ function toJsonValue(value) {
|
|
|
5653
5685
|
return value;
|
|
5654
5686
|
}
|
|
5655
5687
|
function deferred2() {
|
|
5656
|
-
let
|
|
5688
|
+
let resolve8;
|
|
5657
5689
|
let reject;
|
|
5658
5690
|
const promise = new Promise((res, rej) => {
|
|
5659
|
-
|
|
5691
|
+
resolve8 = (value) => res(value);
|
|
5660
5692
|
reject = rej;
|
|
5661
5693
|
});
|
|
5662
|
-
return { promise, resolve:
|
|
5694
|
+
return { promise, resolve: resolve8, reject };
|
|
5663
5695
|
}
|
|
5664
5696
|
|
|
5665
5697
|
// src/runtime/durable-store.ts
|
|
@@ -7137,7 +7169,9 @@ export {
|
|
|
7137
7169
|
createCoreTools2 as createCoreTools,
|
|
7138
7170
|
isRuntimeError,
|
|
7139
7171
|
loadExtensionsFromPath as loadExtensions,
|
|
7172
|
+
loadPhase,
|
|
7140
7173
|
loadPhases,
|
|
7174
|
+
loadSkill,
|
|
7141
7175
|
loadSkills,
|
|
7142
7176
|
materializeConfigurationSnapshot,
|
|
7143
7177
|
parseAgentDefinition,
|