@rowan-agent/agent 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -319,6 +319,8 @@ declare class InMemoryExtensionHost implements ExtensionHost {
319
319
  * - Tool registration support
320
320
  */
321
321
 
322
+ /** The Resource Source an Extension registration lands in: Global (`[]`) is `rowan.extensions`, any other Scope `rowan.extensions:<scopeToKey(scope)>`. */
323
+ declare function extensionSourceId(scope?: ScopeRef): string;
322
324
  type ExtensionRunnerOptions = {
323
325
  entryPhaseId?: string | null;
324
326
  cwd?: string;
@@ -452,12 +454,14 @@ declare class ExtensionRunner {
452
454
  }): void;
453
455
  getPhase(name: string): Phase | undefined;
454
456
  getPhases(): Phase[];
457
+ getRegisteredPhases(): readonly RegisteredPhase[];
455
458
  createPhaseRegistry(input?: {
456
459
  entryPhaseId?: string | null;
457
460
  }): PhaseRegistry;
458
461
  /** Adapt an extension RegisteredPhase to the core Phase type. */
459
- private adaptToPhase;
462
+ adaptToPhase(reg: RegisteredPhase): Phase;
460
463
  getSkills(): Skill[];
464
+ getRegisteredSkills(): readonly RegisteredSkill[];
461
465
  /**
462
466
  * Bind the runner — flushes pending provider registrations and
463
467
  * replaces runtime stubs with real implementations.
@@ -498,6 +502,7 @@ declare class ExtensionRunner {
498
502
  agentId?: string;
499
503
  toolCallId?: string;
500
504
  metadata?: Readonly<Record<string, unknown>>;
505
+ agentMetadata?: Readonly<Record<string, unknown>>;
501
506
  answer?: unknown;
502
507
  scope?: ScopeRef;
503
508
  turn?: JsonObject;
@@ -601,7 +606,9 @@ declare class RuntimeExtensionLifetime {
601
606
  activate(extensions: readonly LoadedExtension[]): Promise<ExtensionActivationResult>;
602
607
  tools(): readonly RegisteredTool[];
603
608
  phases(): readonly Phase[];
609
+ registeredPhases(): readonly RegisteredPhase[];
604
610
  skills(): readonly Skill[];
611
+ registeredSkills(): readonly RegisteredSkill[];
605
612
  get extensionRunner(): ExtensionRunner;
606
613
  get extensionHost(): ExtensionHost;
607
614
  onResourceChange(listener: (kind: "tool" | "phase" | "skill") => Promise<void> | void): () => void;
@@ -611,7 +618,7 @@ declare class RuntimeExtensionLifetime {
611
618
  /** Bootstrap-only Registry facade; its Extension method disappears after init. */
612
619
  declare class RuntimeBootstrapRegistry extends ResourceRegistry {
613
620
  private readonly extensions;
614
- private readonly extensionSourceId;
621
+ private readonly knownSources;
615
622
  private commitQueue;
616
623
  constructor(extensions?: RuntimeExtensionLifetime);
617
624
  private recommit;
@@ -1339,10 +1346,28 @@ interface SourceInfo {
1339
1346
  baseDir?: string;
1340
1347
  displayName?: string;
1341
1348
  }
1349
+ /** A loaded directory Bundle used by an extension. */
1350
+ type PhaseDefinition = Phase;
1342
1351
  /** Register a Phase by directory path or Phase object; PHASE.md and direct child Skills are loaded atomically when a path is supplied. */
1343
1352
  type PhaseRegistration = string | Phase;
1353
+ type RegisteredPhase = {
1354
+ definition: PhaseDefinition;
1355
+ source: {
1356
+ extensionPath: string;
1357
+ };
1358
+ sourceId: string;
1359
+ scope?: ScopeRef;
1360
+ };
1344
1361
  /** Register a Skill directory (containing SKILL.md) or a Skill object. Registering an existing name replaces it. */
1345
1362
  type SkillRegistration = string | Skill;
1363
+ type RegisteredSkill = {
1364
+ definition: Skill;
1365
+ source: {
1366
+ extensionPath: string;
1367
+ };
1368
+ sourceId: string;
1369
+ scope?: ScopeRef;
1370
+ };
1346
1371
  /**
1347
1372
  * Tool definition for registering LLM-callable tools via `api.tools.register()`.
1348
1373
  *
@@ -1400,6 +1425,8 @@ interface ToolDefinition {
1400
1425
  interface RegisteredTool {
1401
1426
  definition: ToolDefinition;
1402
1427
  sourceInfo: SourceInfo;
1428
+ sourceId: string;
1429
+ scope?: ScopeRef;
1403
1430
  }
1404
1431
  /**
1405
1432
  * Structured extension error with attribution.
@@ -1514,9 +1541,9 @@ interface ExtensionAPI {
1514
1541
  /** Tool capabilities — register and unregister custom tools. */
1515
1542
  tools: {
1516
1543
  /** Register a custom LLM-callable tool. */
1517
- register(tool: ToolDefinition): Promise<void>;
1544
+ register(tool: ToolDefinition, scope?: ScopeRef): Promise<void>;
1518
1545
  /** Unregister a previously registered tool by name. */
1519
- unregister(toolName: string): Promise<void>;
1546
+ unregister(toolName: string, scope?: ScopeRef): Promise<void>;
1520
1547
  };
1521
1548
  /** Capability contributions — advertise tools or features to the host. */
1522
1549
  capabilities: {
@@ -1533,9 +1560,9 @@ interface ExtensionAPI {
1533
1560
  /** Phase execution capabilities — provides Phase In/Out, phase identity, phase routing, and registration. */
1534
1561
  phases: {
1535
1562
  /** Register a Phase directory bundle or Phase object. */
1536
- register(phase: PhaseRegistration): Promise<void>;
1563
+ register(phase: PhaseRegistration, scope?: ScopeRef): Promise<void>;
1537
1564
  /** Unregister a previously registered phase by name. */
1538
- unregister(phaseName: string): void;
1565
+ unregister(phaseName: string, scope?: ScopeRef): Promise<void> | void;
1539
1566
  /** Phase In: get payload from previous phase */
1540
1567
  getPayload(): unknown;
1541
1568
  /** Phase Out: set payload for next phase */
@@ -1554,9 +1581,9 @@ interface ExtensionAPI {
1554
1581
  /** Skill capabilities — register and unregister standalone skills. */
1555
1582
  skills: {
1556
1583
  /** Register a Skill directory (containing SKILL.md) or a Skill object. Registering an existing name replaces it. */
1557
- register(skill: SkillRegistration): Promise<void>;
1584
+ register(skill: SkillRegistration, scope?: ScopeRef): Promise<void>;
1558
1585
  /** Unregister a previously registered Skill by name. */
1559
- unregister(skillName: string): void;
1586
+ unregister(skillName: string, scope?: ScopeRef): Promise<void> | void;
1560
1587
  };
1561
1588
  /** Extension manifest from package.json `rowan` field. */
1562
1589
  manifest?: ExtensionManifest;
@@ -1800,6 +1827,8 @@ interface BeforeToolCallEvent {
1800
1827
  readonly agentId?: string;
1801
1828
  readonly toolCallId?: string;
1802
1829
  readonly metadata?: Readonly<Record<string, unknown>>;
1830
+ /** The invoking Agent's metadata, as the Tool's own call context carries it. */
1831
+ readonly agentMetadata?: Readonly<Record<string, unknown>>;
1803
1832
  readonly answer?: unknown;
1804
1833
  readonly scope?: ScopeRef;
1805
1834
  readonly turn?: JsonObject;
@@ -3357,4 +3386,4 @@ declare function parseFrontmatter<T = Record<string, unknown>>(raw: string): Fro
3357
3386
 
3358
3387
  declare function createCoreTools(input?: CoreToolContext): Tool$1[];
3359
3388
 
3360
- export { type AfterToolCall, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, COMPACT_PHASE_ID, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type ContextCompactionRecord, type ContextStatus, type CoreToolContext, DEFAULT_PHASE_ID, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EntryPhaseSpec, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionCapability, type ExtensionCapabilityContribution, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, type ExtensionHost, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type ExtensionStateStore, type ExtensionToolContribution, type FrontmatterResult, type HistorySeed, type HookEvent, type HookEventType, type HookHandler, type HostApiExtension, InMemoryConfigProvider, InMemoryExtensionHost, InMemoryStore, type InteractionRecord, type InvocationCatalogEntry, type InvocationSource, type JsonObject, type JsonPrimitive, type JsonValue, type LiveInteractionEvent, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type MessageRevised, type MessageRevisionResult, type Metadata, type ModelRetry, type OpaqueId, type Outcome$1 as Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecution, type PhaseExecutionIdentity, type PhaseInput, type PhaseInvocation, type PhaseMessageManager, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseState, type PhaseStatus, type PhaseStatusState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RetentionResult, type RunBoundary, type RunClaim, type RunEndEvent, type RunEvent, type RunFailure, type RunId, type RunInteraction, RunInteractionBoundary, RunInteractionCancelledError, type RunInteractionDriver, type RunInteractionKind, type RunInteractionRequest, type RunInteractionState, type RunInteractionStatus, type RunListCursor, type RunRecord, type RunSnapshot, type RunStartEvent, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, STOP_PHASE_ID, type SettingsBadge, type SettingsControl, type SettingsDefinition, type SettingsItem, type SettingsOption, type SettingsSection, type Skill, type SkillRegistration, SqliteStore, type TextContent, type ThinkingContent, type ThinkingDelta, type Tool$1 as Tool, type ToolCallContext, type ToolCallDelta, type ToolCallId, type ToolCallPresentationInput, type ToolCallPresentationOutput, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, type WithdrawnUserInput, brandConfigToken, createCompactPhase, createCorePhases, createCoreTools, createDefaultPhase, createStopPhase, isConfigurationSnapshot, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhases, loadSkill, loadSkills, mergeToolCall, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
3389
+ export { type AfterToolCall, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, COMPACT_PHASE_ID, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type ContextCompactionRecord, type ContextStatus, type CoreToolContext, DEFAULT_PHASE_ID, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EntryPhaseSpec, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionCapability, type ExtensionCapabilityContribution, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, type ExtensionHost, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type ExtensionStateStore, type ExtensionToolContribution, type FrontmatterResult, type HistorySeed, type HookEvent, type HookEventType, type HookHandler, type HostApiExtension, InMemoryConfigProvider, InMemoryExtensionHost, InMemoryStore, type InteractionRecord, type InvocationCatalogEntry, type InvocationSource, type JsonObject, type JsonPrimitive, type JsonValue, type LiveInteractionEvent, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type MessageRevised, type MessageRevisionResult, type Metadata, type ModelRetry, type OpaqueId, type Outcome$1 as Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecution, type PhaseExecutionIdentity, type PhaseInput, type PhaseInvocation, type PhaseMessageManager, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseState, type PhaseStatus, type PhaseStatusState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RetentionResult, type RunBoundary, type RunClaim, type RunEndEvent, type RunEvent, type RunFailure, type RunId, type RunInteraction, RunInteractionBoundary, RunInteractionCancelledError, type RunInteractionDriver, type RunInteractionKind, type RunInteractionRequest, type RunInteractionState, type RunInteractionStatus, type RunListCursor, type RunRecord, type RunSnapshot, type RunStartEvent, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, STOP_PHASE_ID, type SettingsBadge, type SettingsControl, type SettingsDefinition, type SettingsItem, type SettingsOption, type SettingsSection, type Skill, type SkillRegistration, SqliteStore, type TextContent, type ThinkingContent, type ThinkingDelta, type Tool$1 as Tool, type ToolCallContext, type ToolCallDelta, type ToolCallId, type ToolCallPresentationInput, type ToolCallPresentationOutput, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, type WithdrawnUserInput, brandConfigToken, createCompactPhase, createCorePhases, createCoreTools, createDefaultPhase, createStopPhase, extensionSourceId, isConfigurationSnapshot, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhases, loadSkill, loadSkills, mergeToolCall, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
package/dist/index.js CHANGED
@@ -1702,13 +1702,13 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1702
1702
  }
1703
1703
  },
1704
1704
  tools: {
1705
- register: (tool) => {
1705
+ register: (tool, scope) => {
1706
1706
  assertActive();
1707
- return Promise.resolve(options?.registerTool?.(tool));
1707
+ return Promise.resolve(options?.registerTool?.(tool, scope));
1708
1708
  },
1709
- unregister: (toolName) => {
1709
+ unregister: (toolName, scope) => {
1710
1710
  assertActive();
1711
- return Promise.resolve(options?.unregisterTool?.(toolName));
1711
+ return Promise.resolve(options?.unregisterTool?.(toolName, scope));
1712
1712
  }
1713
1713
  },
1714
1714
  capabilities: {
@@ -1762,13 +1762,13 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1762
1762
  }, emit: () => {
1763
1763
  }, has: () => false, count: () => 0 },
1764
1764
  phases: {
1765
- register: async (registration) => {
1765
+ register: async (registration, scope) => {
1766
1766
  assertActive();
1767
- await options?.registerPhase?.(registration);
1767
+ await options?.registerPhase?.(registration, scope);
1768
1768
  },
1769
- unregister: (phaseName) => {
1769
+ unregister: (phaseName, scope) => {
1770
1770
  assertActive();
1771
- return options?.unregisterPhase?.(phaseName);
1771
+ return options?.unregisterPhase?.(phaseName, scope);
1772
1772
  },
1773
1773
  getPayload: () => outputPayload,
1774
1774
  setPayload: (p) => {
@@ -1785,13 +1785,13 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1785
1785
  getMessage: () => outputMessage
1786
1786
  },
1787
1787
  skills: {
1788
- register: async (skill) => {
1788
+ register: async (skill, scope) => {
1789
1789
  assertActive();
1790
- await options?.registerSkill?.(skill);
1790
+ await options?.registerSkill?.(skill, scope);
1791
1791
  },
1792
- unregister: (skillName) => {
1792
+ unregister: (skillName, scope) => {
1793
1793
  assertActive();
1794
- return options?.unregisterSkill?.(skillName);
1794
+ return options?.unregisterSkill?.(skillName, scope);
1795
1795
  }
1796
1796
  }
1797
1797
  };
@@ -5187,6 +5187,7 @@ function assembleRegisteredExtensions(snapshot, runner, options = {}) {
5187
5187
  agentId: input.context.agentId,
5188
5188
  toolCallId: input.context.toolCallId,
5189
5189
  metadata: input.context.runMetadata,
5190
+ ...input.context.agentMetadata === void 0 ? {} : { agentMetadata: input.context.agentMetadata },
5190
5191
  answer: input.answer,
5191
5192
  scope: input.context.scope,
5192
5193
  turn: input.context.turn
@@ -6039,6 +6040,10 @@ function createEventBus() {
6039
6040
  }
6040
6041
 
6041
6042
  // src/extensions/runner.ts
6043
+ function extensionSourceId(scope) {
6044
+ if (!scope || scope.length === 0) return "rowan.extensions";
6045
+ return `rowan.extensions:${scopeToKey(scope)}`;
6046
+ }
6042
6047
  async function execCommand(command, args, cwd, options) {
6043
6048
  return new Promise((resolve9, reject) => {
6044
6049
  const child = execFile(
@@ -6350,8 +6355,9 @@ var ExtensionRunner = class {
6350
6355
  */
6351
6356
  getToolDefinition(toolName) {
6352
6357
  for (const ext of this.extensions) {
6353
- const tool = ext.tools.get(toolName);
6354
- if (tool) return tool.definition;
6358
+ for (const tool of ext.tools.values()) {
6359
+ if (tool.definition.name === toolName) return tool.definition;
6360
+ }
6355
6361
  }
6356
6362
  return void 0;
6357
6363
  }
@@ -6402,20 +6408,24 @@ var ExtensionRunner = class {
6402
6408
  // Phase management
6403
6409
  // ---------------------------------------------------------------------------
6404
6410
  getPhase(name) {
6405
- const reg = this.getRegisteredPhase(name);
6406
- if (!reg) return void 0;
6407
- return this.adaptToPhase(reg);
6411
+ for (const reg of this.collectRegisteredPhases().values()) {
6412
+ if (reg.definition.name === name) return this.adaptToPhase(reg);
6413
+ }
6414
+ return void 0;
6408
6415
  }
6409
6416
  getPhases() {
6410
6417
  return [...this.collectRegisteredPhases().values()].map(
6411
6418
  (p) => this.adaptToPhase(p)
6412
6419
  );
6413
6420
  }
6421
+ getRegisteredPhases() {
6422
+ return [...this.collectRegisteredPhases().values()];
6423
+ }
6414
6424
  createPhaseRegistry(input = {}) {
6415
6425
  const registered = this.collectRegisteredPhases();
6416
6426
  const phases = /* @__PURE__ */ new Map();
6417
- for (const [name, reg] of registered) {
6418
- phases.set(name, this.adaptToPhase(reg));
6427
+ for (const reg of registered.values()) {
6428
+ phases.set(reg.definition.name, this.adaptToPhase(reg));
6419
6429
  }
6420
6430
  const entryPhaseId = input.entryPhaseId ?? null;
6421
6431
  return { phases, entryPhaseId };
@@ -6436,6 +6446,9 @@ var ExtensionRunner = class {
6436
6446
  getSkills() {
6437
6447
  return [...this.skills.values()].map((s) => s.definition);
6438
6448
  }
6449
+ getRegisteredSkills() {
6450
+ return [...this.skills.values()];
6451
+ }
6439
6452
  // ---------------------------------------------------------------------------
6440
6453
  // Lifecycle
6441
6454
  // ---------------------------------------------------------------------------
@@ -6531,6 +6544,7 @@ var ExtensionRunner = class {
6531
6544
  ...context?.agentId !== void 0 ? { agentId: context.agentId } : {},
6532
6545
  ...context?.toolCallId !== void 0 ? { toolCallId: context.toolCallId } : {},
6533
6546
  ...context?.metadata !== void 0 ? { metadata: context.metadata } : {},
6547
+ ...context?.agentMetadata !== void 0 ? { agentMetadata: context.agentMetadata } : {},
6534
6548
  ...context?.answer !== void 0 ? { answer: context.answer } : {},
6535
6549
  scope,
6536
6550
  turn
@@ -6611,14 +6625,14 @@ var ExtensionRunner = class {
6611
6625
  }
6612
6626
  };
6613
6627
  return createExtensionAPI(this.hooks, {
6614
- registerPhase: (registration) => this.registerPhase(extension, registration),
6615
- unregisterPhase: (phaseName) => this.unregisterPhase(extension, phaseName),
6616
- registerSkill: (registration) => this.registerSkill(extension, registration),
6617
- unregisterSkill: (skillName) => this.unregisterSkill(extension, skillName),
6628
+ registerPhase: (registration, scope) => this.registerPhase(extension, registration, scope),
6629
+ unregisterPhase: (phaseName, scope) => this.unregisterPhase(extension, phaseName, scope),
6630
+ registerSkill: (registration, scope) => this.registerSkill(extension, registration, scope),
6631
+ unregisterSkill: (skillName, scope) => this.unregisterSkill(extension, skillName, scope),
6618
6632
  registerProvider: (config) => this.registerProvider(config),
6619
6633
  unregisterProvider: (name) => this.unregisterProvider(name),
6620
- registerTool: (tool) => this.registerTool(extension, tool),
6621
- unregisterTool: (toolName) => this.unregisterTool(extension, toolName),
6634
+ registerTool: (tool, scope) => this.registerTool(extension, tool, scope),
6635
+ unregisterTool: (toolName, scope) => this.unregisterTool(extension, toolName, scope),
6622
6636
  contributeCapability: (contribution) => this.contributeCapability(extension, contribution),
6623
6637
  context: extContext,
6624
6638
  manifest,
@@ -6666,12 +6680,14 @@ var ExtensionRunner = class {
6666
6680
  }
6667
6681
  // Validation stays synchronous so a duplicate throws at the call site even
6668
6682
  // when the caller does not await the Registry re-commit.
6669
- registerTool(extension, tool) {
6670
- if (extension.tools.has(tool.name)) {
6683
+ registerTool(extension, tool, scope) {
6684
+ const sourceId = extensionSourceId(scope);
6685
+ const regKey = `${sourceId}::${tool.name}`;
6686
+ if (extension.tools.has(regKey)) {
6671
6687
  throw new Error(`Duplicate Tool name "${tool.name}" in extension ${extension.path}.`);
6672
6688
  }
6673
6689
  for (const ext of this.extensions) {
6674
- if (ext.tools.has(tool.name)) {
6690
+ if (ext.tools.has(regKey)) {
6675
6691
  const message = `Duplicate Tool name "${tool.name}" from extensions ${ext.path} and ${extension.path}.`;
6676
6692
  this.emitError({
6677
6693
  extensionPath: extension.path,
@@ -6682,14 +6698,18 @@ var ExtensionRunner = class {
6682
6698
  }
6683
6699
  }
6684
6700
  const sourceInfo = createSourceInfo(extension.path);
6685
- extension.tools.set(tool.name, {
6701
+ extension.tools.set(regKey, {
6686
6702
  definition: tool,
6687
- sourceInfo
6703
+ sourceInfo,
6704
+ sourceId,
6705
+ scope
6688
6706
  });
6689
6707
  return this.notifyResourceChange("tool");
6690
6708
  }
6691
- unregisterTool(extension, toolName) {
6692
- if (!extension.tools.delete(toolName)) return Promise.resolve();
6709
+ unregisterTool(extension, toolName, scope) {
6710
+ const sourceId = extensionSourceId(scope);
6711
+ const regKey = `${sourceId}::${toolName}`;
6712
+ if (!extension.tools.delete(regKey)) return Promise.resolve();
6693
6713
  return this.notifyResourceChange("tool");
6694
6714
  }
6695
6715
  contributeCapability(extension, contribution) {
@@ -6732,89 +6752,101 @@ var ExtensionRunner = class {
6732
6752
  }
6733
6753
  }
6734
6754
  }
6735
- async registerPhase(extension, registration) {
6755
+ async registerPhase(extension, registration, scope) {
6736
6756
  if (typeof registration === "string") {
6737
6757
  if (registration.length === 0) {
6738
6758
  throw new Error(`Phase registration requires a directory path.`);
6739
6759
  }
6740
- return this.loadRegisteredPhase(extension, registration);
6760
+ return this.loadRegisteredPhase(extension, registration, scope);
6741
6761
  }
6742
6762
  if (typeof registration === "object" && registration !== null && "name" in registration) {
6743
- return this.registerPhaseDefinition(extension, registration);
6763
+ return this.registerPhaseDefinition(extension, registration, scope);
6744
6764
  }
6745
6765
  throw new Error(`Phase registration requires a directory path or Phase object.`);
6746
6766
  }
6747
- async registerPhaseDefinition(extension, phase) {
6767
+ async registerPhaseDefinition(extension, phase, scope) {
6768
+ const sourceId = extensionSourceId(scope);
6748
6769
  const name = phase.name;
6749
- if (this.phases.has(name)) {
6770
+ const regKey = `${sourceId}::${name}`;
6771
+ if (this.phases.has(regKey)) {
6750
6772
  throw new Error(`Duplicate phase name: ${name}`);
6751
6773
  }
6752
6774
  const registered = {
6753
6775
  definition: phase,
6754
- source: { extensionPath: extension.path }
6776
+ source: { extensionPath: extension.path },
6777
+ sourceId,
6778
+ scope
6755
6779
  };
6756
- this.phases.set(name, registered);
6757
- extension.phases.add(name);
6780
+ this.phases.set(regKey, registered);
6781
+ extension.phases.add(regKey);
6758
6782
  this._phaseCache = null;
6759
6783
  await this.notifyResourceChange("phase");
6760
6784
  }
6761
- async unregisterPhase(extension, phaseName) {
6762
- const registered = this.phases.get(phaseName);
6785
+ async unregisterPhase(extension, phaseName, scope) {
6786
+ const sourceId = extensionSourceId(scope);
6787
+ const regKey = `${sourceId}::${phaseName}`;
6788
+ const registered = this.phases.get(regKey);
6763
6789
  if (registered && registered.source.extensionPath === extension.path) {
6764
- this.phases.delete(phaseName);
6765
- extension.phases.delete(phaseName);
6790
+ this.phases.delete(regKey);
6791
+ extension.phases.delete(regKey);
6766
6792
  this._phaseCache = null;
6767
6793
  await this.notifyResourceChange("phase");
6768
6794
  }
6769
6795
  }
6770
- async loadRegisteredPhase(extension, registration) {
6796
+ async loadRegisteredPhase(extension, registration, scope) {
6771
6797
  const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname6(extension.path);
6772
6798
  const phase = await loadPhase(resolve5(extensionBase, registration));
6773
- await this.registerPhaseDefinition(extension, phase);
6799
+ await this.registerPhaseDefinition(extension, phase, scope);
6774
6800
  }
6775
- async registerSkill(extension, registration) {
6801
+ async registerSkill(extension, registration, scope) {
6776
6802
  if (typeof registration === "string") {
6777
6803
  if (registration.length === 0) {
6778
6804
  throw new Error(`Skill registration requires a directory path.`);
6779
6805
  }
6780
- return this.loadRegisteredSkill(extension, registration);
6806
+ return this.loadRegisteredSkill(extension, registration, scope);
6781
6807
  }
6782
6808
  if (typeof registration === "object" && registration !== null && "name" in registration) {
6783
- return this.registerSkillDefinition(extension, registration);
6809
+ return this.registerSkillDefinition(extension, registration, scope);
6784
6810
  }
6785
6811
  throw new Error(`Skill registration requires a directory path or Skill object.`);
6786
6812
  }
6787
- async registerSkillDefinition(extension, skill) {
6813
+ async registerSkillDefinition(extension, skill, scope) {
6814
+ const sourceId = extensionSourceId(scope);
6788
6815
  const name = skill.name;
6789
- const existing = this.skills.get(name);
6816
+ const regKey = `${sourceId}::${name}`;
6817
+ const existing = this.skills.get(regKey);
6790
6818
  if (existing && existing.source.extensionPath !== extension.path) {
6791
6819
  for (const ext of this.extensions) {
6792
6820
  if (ext.path === existing.source.extensionPath) {
6793
- ext.skills.delete(name);
6821
+ ext.skills.delete(regKey);
6794
6822
  break;
6795
6823
  }
6796
6824
  }
6797
6825
  }
6798
6826
  const registered = {
6799
6827
  definition: skill,
6800
- source: { extensionPath: extension.path }
6828
+ source: { extensionPath: extension.path },
6829
+ sourceId,
6830
+ scope
6801
6831
  };
6802
- this.skills.set(name, registered);
6803
- extension.skills.add(name);
6832
+ this.skills.set(regKey, registered);
6833
+ extension.skills.add(regKey);
6804
6834
  await this.notifyResourceChange("skill");
6805
6835
  }
6806
- async unregisterSkill(extension, skillName) {
6807
- const registered = this.skills.get(skillName);
6836
+ async unregisterSkill(extension, skillName, scope) {
6837
+ const sourceId = extensionSourceId(scope);
6838
+ const regKey = `${sourceId}::${skillName}`;
6839
+ const registered = this.skills.get(regKey);
6808
6840
  if (registered && registered.source.extensionPath === extension.path) {
6809
- this.skills.delete(skillName);
6810
- extension.skills.delete(skillName);
6841
+ this.skills.delete(regKey);
6842
+ extension.skills.delete(regKey);
6811
6843
  await this.notifyResourceChange("skill");
6812
6844
  }
6813
6845
  }
6814
- async loadRegisteredSkill(extension, registration) {
6846
+ async loadRegisteredSkill(extension, registration, scope) {
6815
6847
  const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname6(extension.path);
6816
6848
  const skill = await loadSkill(resolve5(extensionBase, registration));
6817
- await this.registerSkillDefinition(extension, skill);
6849
+ await this.registerSkillDefinition(extension, skill, scope);
6818
6850
  }
6819
6851
  registerProvider(config) {
6820
6852
  if (this.bound) {
@@ -7063,7 +7095,7 @@ var ResourceRegistryError = class extends Error {
7063
7095
  }
7064
7096
  };
7065
7097
  var RESOURCE_NAME = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
7066
- var SOURCE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
7098
+ var SOURCE_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
7067
7099
  var ResourceRegistry = class {
7068
7100
  sources = /* @__PURE__ */ new Map();
7069
7101
  /** Sources registered by the bootstrap boundary (currently Extensions). */
@@ -7156,15 +7188,7 @@ var ResourceRegistry = class {
7156
7188
  }
7157
7189
  withImplicit(kind, sourceIds) {
7158
7190
  const combined = [...sourceIds];
7159
- let hasCore = false;
7160
- for (const sourceId of this.implicitSources.get(kind) ?? []) {
7161
- if (sourceId === "rowan.core") {
7162
- hasCore = true;
7163
- } else if (!combined.includes(sourceId)) {
7164
- combined.push(sourceId);
7165
- }
7166
- }
7167
- if (hasCore && !combined.includes("rowan.core")) {
7191
+ if (this.hasImplicit(kind, "rowan.core") && !combined.includes("rowan.core")) {
7168
7192
  combined.push("rowan.core");
7169
7193
  }
7170
7194
  return combined;
@@ -7239,9 +7263,6 @@ var ResourceRegistry = class {
7239
7263
  if (source.sourceId === "rowan.core") {
7240
7264
  continue;
7241
7265
  }
7242
- if (source.sourceId === "rowan.extensions" && isCoreResourceName(kind, value.name)) {
7243
- continue;
7244
- }
7245
7266
  throw new ResourceRegistryError(
7246
7267
  "resource_collision",
7247
7268
  `Duplicate ${capitalize(kind)} resource "${value.name}" in Resource View.`
@@ -7265,9 +7286,6 @@ var ResourceRegistry = class {
7265
7286
  if (source.sourceId === "rowan.core") {
7266
7287
  continue;
7267
7288
  }
7268
- if (source.sourceId === "rowan.extensions" && isCoreResourceName(kind, value.name)) {
7269
- continue;
7270
- }
7271
7289
  throw new ResourceRegistryError("resource_collision", `Duplicate ${capitalize(kind)} resource "${value.name}" in Resource View.`);
7272
7290
  }
7273
7291
  seenNames.add(value.name);
@@ -7286,10 +7304,7 @@ var ResourceRegistry = class {
7286
7304
  seen.add(sourceId);
7287
7305
  const source = bySource?.get(sourceId);
7288
7306
  if (!source) {
7289
- throw new ResourceRegistryError(
7290
- "unknown_resource_source",
7291
- `Unknown resource source "${sourceId}" for ${kind}.`
7292
- );
7307
+ continue;
7293
7308
  }
7294
7309
  sources.push(source);
7295
7310
  }
@@ -7341,9 +7356,6 @@ function capitalize(value) {
7341
7356
  function isImplicitCoreName(kind, name) {
7342
7357
  return kind === "tool" && name === "route";
7343
7358
  }
7344
- function isCoreResourceName(kind, name) {
7345
- return kind === "tool" && CORE_TOOL_NAMES.has(name) || kind === "phase" && CORE_PHASE_NAMES.has(name);
7346
- }
7347
7359
  async function loadDirectoryValues(kind, directory, sourceId) {
7348
7360
  const root = resolve7(directory);
7349
7361
  let rootInfo;
@@ -7507,9 +7519,15 @@ var RuntimeExtensionLifetime = class {
7507
7519
  phases() {
7508
7520
  return this.runner.getPhases();
7509
7521
  }
7522
+ registeredPhases() {
7523
+ return this.runner.getRegisteredPhases();
7524
+ }
7510
7525
  skills() {
7511
7526
  return this.runner.getSkills();
7512
7527
  }
7528
+ registeredSkills() {
7529
+ return this.runner.getRegisteredSkills();
7530
+ }
7513
7531
  get extensionRunner() {
7514
7532
  return this.runner;
7515
7533
  }
@@ -7535,16 +7553,25 @@ var RuntimeBootstrapRegistry = class extends ResourceRegistry {
7535
7553
  this.extensions.onResourceChange((kind) => this.recommit(kind));
7536
7554
  }
7537
7555
  extensions;
7538
- extensionSourceId = "rowan.extensions";
7556
+ knownSources = /* @__PURE__ */ new Map();
7539
7557
  commitQueue = Promise.resolve();
7540
7558
  recommit(kind) {
7541
7559
  const next = this.commitQueue.then(async () => {
7542
- if (kind === "tool") {
7543
- await this.replaceImplicit("tool", this.extensionSourceId, this.extensions.tools().map(adaptExtensionTool));
7544
- } else if (kind === "phase") {
7545
- await this.replaceImplicit("phase", this.extensionSourceId, [...this.extensions.phases()]);
7546
- } else if (kind === "skill") {
7547
- await this.replaceImplicit("skill", this.extensionSourceId, [...this.extensions.skills()]);
7560
+ const entries = kind === "tool" ? this.extensions.tools().map((tool) => [tool.sourceId, adaptExtensionTool(tool)]) : kind === "phase" ? this.extensions.registeredPhases().map((reg) => [reg.sourceId, this.extensions.extensionRunner.adaptToPhase(reg)]) : this.extensions.registeredSkills().map((reg) => [reg.sourceId, reg.definition]);
7561
+ const bySource = /* @__PURE__ */ new Map();
7562
+ for (const [sourceId, value] of entries) bySource.set(sourceId, [...bySource.get(sourceId) ?? [], value]);
7563
+ const known = this.knownSources.get(kind) ?? /* @__PURE__ */ new Set();
7564
+ this.knownSources.set(kind, known);
7565
+ for (const [sourceId, values] of bySource) {
7566
+ known.add(sourceId);
7567
+ if (kind === "tool") await this.loadTools({ sourceId, values });
7568
+ else if (kind === "phase") await this.loadPhases({ sourceId, values });
7569
+ else await this.loadSkills({ sourceId, values });
7570
+ }
7571
+ for (const sourceId of [...known]) {
7572
+ if (bySource.has(sourceId)) continue;
7573
+ known.delete(sourceId);
7574
+ await this.unload({ kind, sourceId });
7548
7575
  }
7549
7576
  });
7550
7577
  this.commitQueue = next.catch(() => void 0);
@@ -13106,6 +13133,7 @@ export {
13106
13133
  createCoreTools2 as createCoreTools,
13107
13134
  createDefaultPhase,
13108
13135
  createStopPhase,
13136
+ extensionSourceId,
13109
13137
  isConfigurationSnapshot,
13110
13138
  isRuntimeError,
13111
13139
  loadExtensionsFromPath as loadExtensions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",