@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.
Files changed (3) hide show
  1. package/dist/index.d.ts +13 -28
  2. package/dist/index.js +235 -198
  3. 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 custom phase. */
442
- registerPhase(registration: PhaseRegistration): void;
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
- /** Restricted skills (undefined = all skills available) */
556
- skills?: string[];
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 dirname3, join, resolve as resolve3 } from "path";
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 = resolve3(input);
1235
- return existsSync(resolved) && statSync(resolved).isDirectory() ? join(resolved, PHASE_MARKER) : resolved;
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
- definition = normalizeAgentDefinition(metadata, body, { fallbackName: directoryName });
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 = dirname3(resolved);
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: definition.skills ? [...definition.skills] : void 0,
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 = resolve3(targetPath);
1306
- if (existsSync(phasesDir) && statSync(phasesDir).isFile()) {
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 (existsSync(join(phasesDir, PHASE_MARKER))) {
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 readdir(phasesDir, { withFileTypes: true });
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 = join(phasesDir, entry.name, PHASE_MARKER);
1328
- if (!existsSync(phaseFile)) continue;
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 = join(baseDir, candidate);
1343
- if (existsSync(fullPath)) {
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 = selectNamedResources(config.context.skills, phase.skills, "Skill");
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((resolve7) => setTimeout(resolve7, delayMs));
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: () => config.context.skills.map((s) => ({ name: s.name, description: s.description })),
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 = selectNamedResources(config.context.skills, phase.skills, "Skill");
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 resolve7;
3568
+ let resolve8;
3440
3569
  const promise = new Promise((done) => {
3441
- resolve7 = done;
3570
+ resolve8 = done;
3442
3571
  });
3443
- return { promise, resolve: resolve7 };
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((resolve7, reject) => {
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
- resolve7({
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
- name: def.name,
3959
- description: def.description,
3960
- tools: def.tools,
3961
- skills: def.skills,
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.name !== "string" || registration.name.length === 0) {
4131
- throw new Error(`Phase registration requires a "name" field.`);
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 existsSync2, readFileSync } from "fs";
4219
- import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
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 dirname4, extname as extname2, join as join2, resolve as resolve4 } from "path";
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 = join2(dir, "package.json");
4232
- if (!existsSync2(manifestPath)) {
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 (!existsSync2(agentSourcePath)) {
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 (!existsSync2(path)) {
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(join2(dir, "package.json"));
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) => resolve4(dir, entry));
4388
+ return declared.map((entry) => resolve6(dir, entry));
4286
4389
  }
4287
4390
  for (const name of ["index.ts", "index.js"]) {
4288
- const entry = join2(dir, name);
4289
- if (existsSync2(entry)) {
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 readdir2(dir, { withFileTypes: true }).catch((error) => {
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 = join2(dir, entry.name);
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 = resolve4(cwd);
4323
- const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve4(resolvedCwd, extensionPath);
4324
- const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname4(resolvedPath)) || "unknown";
4325
- const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname4(resolvedPath);
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 = resolve4(cwd);
4440
+ const resolvedCwd = resolve6(cwd);
4338
4441
  for (const path of paths) {
4339
- const resolvedPath = resolve4(resolvedCwd, path);
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 = resolve4(path);
4463
+ const resolvedPath = resolve6(path);
4361
4464
  const info = await stat2(resolvedPath);
4362
4465
  if (!info.isDirectory()) {
4363
- return loadExtensions([resolvedPath], dirname4(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 resolve6 } from "path";
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([...values])
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 = resolve6(directory);
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((resolve7) => setTimeout(resolve7, ms));
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 resolve7;
5691
+ let resolve8;
5657
5692
  let reject;
5658
5693
  const promise = new Promise((res, rej) => {
5659
- resolve7 = (value) => res(value);
5694
+ resolve8 = (value) => res(value);
5660
5695
  reject = rej;
5661
5696
  });
5662
- return { promise, resolve: resolve7, reject };
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.8.7",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",