@rowan-agent/agent 0.8.5 → 0.8.6
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/README.md +51 -4
- package/dist/index.d.ts +457 -21
- package/dist/index.js +1450 -607
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,6 +33,18 @@ function buildSkillsDescription(skills) {
|
|
|
33
33
|
lines.push("</available_skills>");
|
|
34
34
|
return lines.join("\n");
|
|
35
35
|
}
|
|
36
|
+
function buildContextDescription(contexts) {
|
|
37
|
+
if (contexts.length === 0) return "";
|
|
38
|
+
const lines = ["<agent_context>"];
|
|
39
|
+
for (const context of contexts) {
|
|
40
|
+
lines.push(` <context name="${escapeXml(context.name)}">`);
|
|
41
|
+
const value = jsonToXml(context.value, 2);
|
|
42
|
+
if (value) lines.push(value);
|
|
43
|
+
lines.push(" </context>");
|
|
44
|
+
}
|
|
45
|
+
lines.push("</agent_context>");
|
|
46
|
+
return lines.join("\n");
|
|
47
|
+
}
|
|
36
48
|
function formatResourceOutput(resource) {
|
|
37
49
|
const parts = [`<${resource.type} name="${escapeXml(resource.name)}" location="${escapeXml(resource.location)}">`];
|
|
38
50
|
if (resource.baseDir) {
|
|
@@ -955,6 +967,7 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
955
967
|
};
|
|
956
968
|
const ctx = options?.context ?? noopContext;
|
|
957
969
|
const phaseIn = options?.phase;
|
|
970
|
+
const trackCleanup = options?.trackCleanup;
|
|
958
971
|
let outputPayload = phaseIn?.state?.payload;
|
|
959
972
|
let nextPhase;
|
|
960
973
|
let outputMessage;
|
|
@@ -962,6 +975,7 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
962
975
|
on: (eventType, handler) => {
|
|
963
976
|
assertActive();
|
|
964
977
|
hooks?.on(eventType, handler);
|
|
978
|
+
trackCleanup?.(() => hooks?.off(eventType, handler));
|
|
965
979
|
},
|
|
966
980
|
off: (eventType, handler) => {
|
|
967
981
|
assertActive();
|
|
@@ -978,6 +992,7 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
978
992
|
registerProvider: (config) => {
|
|
979
993
|
assertActive();
|
|
980
994
|
options?.registerProvider?.(config);
|
|
995
|
+
trackCleanup?.(() => options?.unregisterProvider?.(config.id));
|
|
981
996
|
},
|
|
982
997
|
unregisterProvider: (name) => {
|
|
983
998
|
assertActive();
|
|
@@ -989,7 +1004,17 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
989
1004
|
formatJson
|
|
990
1005
|
},
|
|
991
1006
|
context: ctx,
|
|
992
|
-
events: eventBus
|
|
1007
|
+
events: eventBus ? {
|
|
1008
|
+
on: (event, listener) => {
|
|
1009
|
+
const unsubscribe = eventBus.on(event, listener);
|
|
1010
|
+
trackCleanup?.(unsubscribe);
|
|
1011
|
+
return unsubscribe;
|
|
1012
|
+
},
|
|
1013
|
+
off: (event) => eventBus.off(event),
|
|
1014
|
+
emit: (event, ...args) => eventBus.emit(event, ...args),
|
|
1015
|
+
has: (event) => eventBus.has(event),
|
|
1016
|
+
count: (event) => eventBus.count(event)
|
|
1017
|
+
} : { on: () => () => {
|
|
993
1018
|
}, off: () => {
|
|
994
1019
|
}, emit: () => {
|
|
995
1020
|
}, has: () => false, count: () => 0 },
|
|
@@ -1025,9 +1050,10 @@ function assertAgentDefinition(value) {
|
|
|
1025
1050
|
requiredString(value.content, "definition.content");
|
|
1026
1051
|
optionalStringList(value.tools, "definition.tools");
|
|
1027
1052
|
optionalStringList(value.skills, "definition.skills");
|
|
1028
|
-
|
|
1029
|
-
optionalStringList(value.
|
|
1030
|
-
|
|
1053
|
+
optionalPhaseRegistrySelection(value.phases, "definition.phases");
|
|
1054
|
+
optionalStringList(value.context, "definition.context");
|
|
1055
|
+
if ("extensions" in value) throw new TypeError("definition.extensions is not supported; Extensions are Runtime-global");
|
|
1056
|
+
if ("entryPhase" in value) throw new TypeError("definition.entryPhase is not supported; use definition.phases.entryPhaseId");
|
|
1031
1057
|
if (value.model !== void 0) {
|
|
1032
1058
|
if (!isRecord(value.model) || typeof value.model.provider !== "string" || value.model.provider.trim() === "" || typeof value.model.id !== "string" || value.model.id.trim() === "") {
|
|
1033
1059
|
throw new TypeError("definition.model must be a valid model reference");
|
|
@@ -1044,9 +1070,14 @@ function normalizeAgentDefinition(frontmatter, content, options = {}) {
|
|
|
1044
1070
|
const normalizedContent = requiredString(content, "content");
|
|
1045
1071
|
const tools = optionalStringList(frontmatter.tools, "tools");
|
|
1046
1072
|
const skills = optionalStringList(frontmatter.skills, "skills");
|
|
1047
|
-
const phases =
|
|
1048
|
-
|
|
1049
|
-
|
|
1073
|
+
const phases = optionalPhaseRegistrySelection(frontmatter.phases, "phases");
|
|
1074
|
+
if (frontmatter.extensions !== void 0) {
|
|
1075
|
+
throw new TypeError("extensions is not supported; Extensions are Runtime-global");
|
|
1076
|
+
}
|
|
1077
|
+
const context = optionalStringList(frontmatter.context, "context");
|
|
1078
|
+
if (frontmatter.entryPhase !== void 0) {
|
|
1079
|
+
throw new TypeError("entryPhase is not supported; use phases.entryPhaseId");
|
|
1080
|
+
}
|
|
1050
1081
|
const model = optionalModel(frontmatter.model);
|
|
1051
1082
|
return {
|
|
1052
1083
|
name,
|
|
@@ -1055,8 +1086,7 @@ function normalizeAgentDefinition(frontmatter, content, options = {}) {
|
|
|
1055
1086
|
...tools ? { tools } : {},
|
|
1056
1087
|
...skills ? { skills } : {},
|
|
1057
1088
|
...phases ? { phases } : {},
|
|
1058
|
-
...
|
|
1059
|
-
...entryPhase ? { entryPhase } : {},
|
|
1089
|
+
...context ? { context } : {},
|
|
1060
1090
|
...model ? { model } : {}
|
|
1061
1091
|
};
|
|
1062
1092
|
}
|
|
@@ -1089,6 +1119,22 @@ function optionalStringList(value, field) {
|
|
|
1089
1119
|
}
|
|
1090
1120
|
return names;
|
|
1091
1121
|
}
|
|
1122
|
+
function optionalPhaseRegistrySelection(value, field) {
|
|
1123
|
+
if (value === void 0) return void 0;
|
|
1124
|
+
if (!isRecord(value)) throw new TypeError(`${field} must be an object`);
|
|
1125
|
+
if (!("entryPhaseId" in value)) throw new TypeError(`${field}.entryPhaseId is required`);
|
|
1126
|
+
if (!("phaseIds" in value)) throw new TypeError(`${field}.phaseIds is required`);
|
|
1127
|
+
const entryPhaseId = value.entryPhaseId;
|
|
1128
|
+
if (entryPhaseId !== null && (typeof entryPhaseId !== "string" || entryPhaseId.trim() === "")) {
|
|
1129
|
+
throw new TypeError(`${field}.entryPhaseId must be a non-empty string or null`);
|
|
1130
|
+
}
|
|
1131
|
+
const phaseIds = optionalStringList(value.phaseIds, `${field}.phaseIds`);
|
|
1132
|
+
if (!phaseIds) throw new TypeError(`${field}.phaseIds is required`);
|
|
1133
|
+
return {
|
|
1134
|
+
entryPhaseId: entryPhaseId === null ? null : entryPhaseId.trim(),
|
|
1135
|
+
phaseIds
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1092
1138
|
function optionalModel(value) {
|
|
1093
1139
|
const source = optionalString(value, "model");
|
|
1094
1140
|
if (!source) return void 0;
|
|
@@ -1693,7 +1739,7 @@ function createTerminalOutcome(phase, output, transcript) {
|
|
|
1693
1739
|
return phase.run || phase.factory ? createOutcome.phase(output, transcript) : createOutcome.default(output, transcript);
|
|
1694
1740
|
}
|
|
1695
1741
|
function buildToolsWithRouting(config, availablePhases) {
|
|
1696
|
-
const tools =
|
|
1742
|
+
const tools = config.context.tools.filter((tool) => tool.name !== PhaseRouteTool);
|
|
1697
1743
|
if (availablePhases.length > 0) {
|
|
1698
1744
|
tools.push(createRouteTool(availablePhases));
|
|
1699
1745
|
}
|
|
@@ -2158,7 +2204,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2158
2204
|
}
|
|
2159
2205
|
const delayMs = getRetryDelay(attempt, baseDelayMs, maxDelayMs);
|
|
2160
2206
|
options.onRetry?.(attempt, error, delayMs);
|
|
2161
|
-
await new Promise((
|
|
2207
|
+
await new Promise((resolve7) => setTimeout(resolve7, delayMs));
|
|
2162
2208
|
if (options.signal?.aborted) {
|
|
2163
2209
|
throw lastError;
|
|
2164
2210
|
}
|
|
@@ -2663,6 +2709,9 @@ function isContinuation(value) {
|
|
|
2663
2709
|
}
|
|
2664
2710
|
|
|
2665
2711
|
// src/runtime/contracts.ts
|
|
2712
|
+
function isAgentConfiguration(config) {
|
|
2713
|
+
return !("resources" in config);
|
|
2714
|
+
}
|
|
2666
2715
|
var METADATA_LIMIT = 64 * 1024;
|
|
2667
2716
|
var IDENTITY_LIMIT = 256;
|
|
2668
2717
|
function isRecord3(value) {
|
|
@@ -2715,6 +2764,39 @@ function assertAgentConfig(config) {
|
|
|
2715
2764
|
throw new TypeError("config.resources is invalid");
|
|
2716
2765
|
}
|
|
2717
2766
|
for (const tool of config.resources.tools) projectToolDefinition(tool);
|
|
2767
|
+
const names = /* @__PURE__ */ new Set();
|
|
2768
|
+
for (const context of config.resources.contexts ?? []) {
|
|
2769
|
+
if (typeof context.name !== "string" || context.name.trim() === "") {
|
|
2770
|
+
throw new TypeError("Context candidate name must be non-empty");
|
|
2771
|
+
}
|
|
2772
|
+
if (names.has(context.name)) throw new TypeError(`Duplicate Context candidate "${context.name}".`);
|
|
2773
|
+
names.add(context.name);
|
|
2774
|
+
assertJsonValue(context.value, `Context candidate "${context.name}" value`);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
function assertAgentConfigRequest(config) {
|
|
2778
|
+
if (!isAgentConfiguration(config)) {
|
|
2779
|
+
assertAgentConfig(config);
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2782
|
+
if (typeof config.identity !== "string" || config.identity.length === 0) throw new TypeError("config.identity must be non-empty");
|
|
2783
|
+
assertUtf8ByteLimit(config.identity, IDENTITY_LIMIT, "config.identity");
|
|
2784
|
+
if (!config.definition || typeof config.definition.name !== "string" || config.definition.name.trim() === "") {
|
|
2785
|
+
throw new TypeError("config.definition.name must be non-empty");
|
|
2786
|
+
}
|
|
2787
|
+
const view = config.resourceView;
|
|
2788
|
+
if (!view || !Array.isArray(view.agents) || !Array.isArray(view.tools) || !Array.isArray(view.skills) || !Array.isArray(view.phases)) {
|
|
2789
|
+
throw new TypeError("config.resourceView is invalid");
|
|
2790
|
+
}
|
|
2791
|
+
for (const [kind, ids] of Object.entries(view)) {
|
|
2792
|
+
for (const sourceId of ids) {
|
|
2793
|
+
if (typeof sourceId !== "string" || sourceId.trim() === "") throw new TypeError(`config.resourceView.${kind} contains an invalid Source ID`);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
for (const context of config.contexts ?? []) {
|
|
2797
|
+
if (typeof context.name !== "string" || context.name.trim() === "") throw new TypeError("Context candidate name must be non-empty");
|
|
2798
|
+
assertJsonValue(context.value, `Context candidate "${context.name}" value`);
|
|
2799
|
+
}
|
|
2718
2800
|
}
|
|
2719
2801
|
function assertToolExecutionResult(value) {
|
|
2720
2802
|
if (!isToolResult(value)) throw new TypeError("Tool result must be JSON-safe and contain no Runtime identity");
|
|
@@ -2808,21 +2890,92 @@ function validateConfigResolution(agentId, value) {
|
|
|
2808
2890
|
throw new Error(`Config Provider returned an invalid resolution for Agent ${agentId}.`);
|
|
2809
2891
|
}
|
|
2810
2892
|
function snapshotConfig(config) {
|
|
2893
|
+
if (isAgentConfiguration(config)) return snapshotConfiguration(config);
|
|
2811
2894
|
const definition = Object.freeze({
|
|
2812
2895
|
...config.definition,
|
|
2813
2896
|
...config.definition.tools ? { tools: Object.freeze([...config.definition.tools]) } : {},
|
|
2814
2897
|
...config.definition.skills ? { skills: Object.freeze([...config.definition.skills]) } : {},
|
|
2815
|
-
...config.definition.phases ? {
|
|
2816
|
-
|
|
2898
|
+
...config.definition.phases ? {
|
|
2899
|
+
phases: Object.freeze({
|
|
2900
|
+
entryPhaseId: config.definition.phases.entryPhaseId,
|
|
2901
|
+
phaseIds: Object.freeze([...config.definition.phases.phaseIds])
|
|
2902
|
+
})
|
|
2903
|
+
} : {},
|
|
2904
|
+
...config.definition.context ? { context: Object.freeze([...config.definition.context]) } : {}
|
|
2817
2905
|
});
|
|
2818
2906
|
const resources = Object.freeze({
|
|
2819
2907
|
...config.resources,
|
|
2820
2908
|
tools: Object.freeze([...config.resources.tools]),
|
|
2821
2909
|
skills: Object.freeze([...config.resources.skills]),
|
|
2822
|
-
...config.resources.
|
|
2910
|
+
...config.resources.resourceView ? {
|
|
2911
|
+
resourceView: Object.freeze({
|
|
2912
|
+
agents: Object.freeze([...config.resources.resourceView.agents]),
|
|
2913
|
+
tools: Object.freeze([...config.resources.resourceView.tools]),
|
|
2914
|
+
skills: Object.freeze([...config.resources.resourceView.skills]),
|
|
2915
|
+
phases: Object.freeze([...config.resources.resourceView.phases])
|
|
2916
|
+
})
|
|
2917
|
+
} : {},
|
|
2918
|
+
...config.resources.resourceRefs ? { resourceRefs: Object.freeze(config.resources.resourceRefs.map((ref) => Object.freeze({ ...ref }))) } : {},
|
|
2919
|
+
...config.resources.resourceRevisions ? {
|
|
2920
|
+
resourceRevisions: Object.freeze(Object.fromEntries(
|
|
2921
|
+
Object.entries(config.resources.resourceRevisions).map(([kind, revisions]) => [kind, Object.freeze([...revisions])])
|
|
2922
|
+
))
|
|
2923
|
+
} : {},
|
|
2924
|
+
...config.resources.contexts ? {
|
|
2925
|
+
contexts: Object.freeze(config.resources.contexts.map((context) => Object.freeze({
|
|
2926
|
+
name: context.name,
|
|
2927
|
+
value: snapshotJsonValue(context.value)
|
|
2928
|
+
})))
|
|
2929
|
+
} : {}
|
|
2823
2930
|
});
|
|
2824
2931
|
return Object.freeze({ ...config, definition, resources });
|
|
2825
2932
|
}
|
|
2933
|
+
function snapshotConfiguration(config) {
|
|
2934
|
+
const layer = config.definition.layer;
|
|
2935
|
+
const definition = Object.freeze({
|
|
2936
|
+
name: config.definition.name,
|
|
2937
|
+
...layer === void 0 ? {} : {
|
|
2938
|
+
layer: Object.freeze({
|
|
2939
|
+
...layer,
|
|
2940
|
+
...layer.tools ? { tools: Object.freeze([...layer.tools]) } : {},
|
|
2941
|
+
...layer.skills ? { skills: Object.freeze([...layer.skills]) } : {},
|
|
2942
|
+
...layer.phases ? {
|
|
2943
|
+
phases: Object.freeze({
|
|
2944
|
+
entryPhaseId: layer.phases.entryPhaseId,
|
|
2945
|
+
phaseIds: Object.freeze([...layer.phases.phaseIds])
|
|
2946
|
+
})
|
|
2947
|
+
} : {}
|
|
2948
|
+
})
|
|
2949
|
+
}
|
|
2950
|
+
});
|
|
2951
|
+
const resourceView = Object.freeze({
|
|
2952
|
+
agents: Object.freeze([...config.resourceView.agents]),
|
|
2953
|
+
tools: Object.freeze([...config.resourceView.tools]),
|
|
2954
|
+
skills: Object.freeze([...config.resourceView.skills]),
|
|
2955
|
+
phases: Object.freeze([...config.resourceView.phases])
|
|
2956
|
+
});
|
|
2957
|
+
return Object.freeze({
|
|
2958
|
+
...config,
|
|
2959
|
+
definition,
|
|
2960
|
+
resourceView,
|
|
2961
|
+
...config.contexts ? {
|
|
2962
|
+
contexts: Object.freeze(config.contexts.map((context) => Object.freeze({
|
|
2963
|
+
name: context.name,
|
|
2964
|
+
value: snapshotJsonValue(context.value)
|
|
2965
|
+
})))
|
|
2966
|
+
} : {}
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
function snapshotJsonValue(value) {
|
|
2970
|
+
return deepFreeze(JSON.parse(canonicalJson(value)));
|
|
2971
|
+
}
|
|
2972
|
+
function deepFreeze(value) {
|
|
2973
|
+
if (value && typeof value === "object") {
|
|
2974
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
2975
|
+
Object.freeze(value);
|
|
2976
|
+
}
|
|
2977
|
+
return value;
|
|
2978
|
+
}
|
|
2826
2979
|
function assertConfigIdentity(identity) {
|
|
2827
2980
|
if (typeof identity !== "string" || identity.length === 0) throw new TypeError("config.identity must be non-empty");
|
|
2828
2981
|
assertUtf8ByteLimit(identity, 256, "config.identity");
|
|
@@ -2867,7 +3020,7 @@ var ConfigCommandService = class {
|
|
|
2867
3020
|
configs;
|
|
2868
3021
|
storeIncarnation;
|
|
2869
3022
|
async createAgent(input) {
|
|
2870
|
-
|
|
3023
|
+
assertAgentConfigRequest(input.config);
|
|
2871
3024
|
assertIdentity(input.config.identity);
|
|
2872
3025
|
const reserved = await this.store.reserveAgent({
|
|
2873
3026
|
idempotencyKey: input.idempotencyKey,
|
|
@@ -2889,7 +3042,7 @@ var ConfigCommandService = class {
|
|
|
2889
3042
|
return reserved.id;
|
|
2890
3043
|
}
|
|
2891
3044
|
async updateAgentConfig(input) {
|
|
2892
|
-
|
|
3045
|
+
assertAgentConfigRequest(input.config);
|
|
2893
3046
|
assertIdentity(input.config.identity);
|
|
2894
3047
|
const agent = await this.findAgent(input.agentId);
|
|
2895
3048
|
const operationId = this.operationId("update_agent_config", input.agentId, input.idempotencyKey);
|
|
@@ -2916,6 +3069,23 @@ var ConfigCommandService = class {
|
|
|
2916
3069
|
const resolution = validateConfigResolution(input.agent.id, result);
|
|
2917
3070
|
return resolution;
|
|
2918
3071
|
}
|
|
3072
|
+
/** Persist one resolved Configuration Snapshot under a Run-specific token.
|
|
3073
|
+
* The durable Run pins this token at claim time, so later Source replacement
|
|
3074
|
+
* cannot mutate an active or input-waiting Run. */
|
|
3075
|
+
async storeSnapshot(input) {
|
|
3076
|
+
assertAgentConfigRequest(input.config);
|
|
3077
|
+
const result = await this.put({
|
|
3078
|
+
agentId: input.agent.id,
|
|
3079
|
+
agentMetadata: input.agent.metadata,
|
|
3080
|
+
config: input.config,
|
|
3081
|
+
operationId: input.operationId,
|
|
3082
|
+
signal: input.signal
|
|
3083
|
+
});
|
|
3084
|
+
if (result.kind === "identity_conflict") {
|
|
3085
|
+
throw idempotencyConflict("update_agent_config", input.operationId);
|
|
3086
|
+
}
|
|
3087
|
+
return brandConfigToken(result.token);
|
|
3088
|
+
}
|
|
2919
3089
|
async findAgent(agentId) {
|
|
2920
3090
|
const agent = (await this.store.listAgents()).find((candidate) => candidate.id === agentId);
|
|
2921
3091
|
if (!agent) throw new RuntimeError("agent_not_found", { agentId });
|
|
@@ -3149,157 +3319,401 @@ function jsonText(value) {
|
|
|
3149
3319
|
return typeof value === "string" ? value : JSON.stringify(value) ?? "null";
|
|
3150
3320
|
}
|
|
3151
3321
|
|
|
3152
|
-
// src/extensions
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
var HooksManager = class {
|
|
3162
|
-
handlers = /* @__PURE__ */ new Map();
|
|
3163
|
-
on(eventType, handler) {
|
|
3164
|
-
const handlers = this.handlers.get(eventType) ?? [];
|
|
3165
|
-
handlers.push(handler);
|
|
3166
|
-
this.handlers.set(eventType, handlers);
|
|
3167
|
-
}
|
|
3168
|
-
off(eventType, handler) {
|
|
3169
|
-
const handlers = this.handlers.get(eventType);
|
|
3170
|
-
if (!handlers) return;
|
|
3171
|
-
const index = handlers.indexOf(handler);
|
|
3172
|
-
if (index >= 0) handlers.splice(index, 1);
|
|
3173
|
-
}
|
|
3174
|
-
async emit(eventType, event) {
|
|
3175
|
-
const handlers = this.handlers.get(eventType);
|
|
3176
|
-
if (!handlers?.length) return;
|
|
3177
|
-
const results = await Promise.allSettled(handlers.map((handler) => handler(event)));
|
|
3178
|
-
const failure = results.find((result) => result.status === "rejected");
|
|
3179
|
-
if (failure) {
|
|
3180
|
-
throw new HookError(
|
|
3181
|
-
eventType,
|
|
3182
|
-
`${results.filter((result) => result.status === "rejected").length} handler(s) failed for "${eventType}"`,
|
|
3183
|
-
failure.reason instanceof Error ? failure.reason : new Error(String(failure.reason))
|
|
3184
|
-
);
|
|
3185
|
-
}
|
|
3322
|
+
// src/runtime/extensions.ts
|
|
3323
|
+
function assembleRegisteredExtensions(config, runner) {
|
|
3324
|
+
const extensionTools = runner.getAllRegisteredTools().map(adaptExtensionTool);
|
|
3325
|
+
const tools = [...config.resources.tools];
|
|
3326
|
+
const names = new Set(tools.map((tool) => tool.name));
|
|
3327
|
+
for (const tool of extensionTools) {
|
|
3328
|
+
if (names.has(tool.name)) throw new TypeError(`Extension Tool collides with Context Tool "${tool.name}"`);
|
|
3329
|
+
names.add(tool.name);
|
|
3330
|
+
tools.push(tool);
|
|
3186
3331
|
}
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3332
|
+
const extensionPhases = runner.createPhaseRegistry({ entryPhaseId: null });
|
|
3333
|
+
const phases = mergePhases(config.resources.phases, extensionPhases);
|
|
3334
|
+
const context = resolveDefinitionContext(config, { tools, phases });
|
|
3335
|
+
return {
|
|
3336
|
+
context,
|
|
3337
|
+
beforePhase: (phaseId, input) => runner.emitBeforePhase(phaseId, input),
|
|
3338
|
+
afterPhase: (phaseId, output) => runner.emitAfterPhase(phaseId, output),
|
|
3339
|
+
beforePrompt: (phaseId, input) => runner.emitBeforePrompt(phaseId, input),
|
|
3340
|
+
beforeToolCall: async (input) => {
|
|
3341
|
+
const tool = projectTool(input.tool, input.context.agentId, input.context.runId);
|
|
3342
|
+
const decision = await runner.emitBeforeToolCall(tool, input.args);
|
|
3343
|
+
return decision.allow ? { allow: true } : { allow: false, reason: decision.reason ?? "Extension hook rejected the Tool." };
|
|
3344
|
+
},
|
|
3345
|
+
afterToolCall: async (input) => {
|
|
3346
|
+
const tool = projectTool(input.tool, input.context.agentId, input.context.runId);
|
|
3347
|
+
const result = await runner.emitAfterToolCall(tool, toLoopResult(input.result, input.context.toolCallId, input.tool.name));
|
|
3348
|
+
return fromLoopResult(result);
|
|
3349
|
+
},
|
|
3350
|
+
setContext: (context2) => {
|
|
3351
|
+
runner.currentContext = context2;
|
|
3201
3352
|
}
|
|
3202
|
-
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3355
|
+
function resolveDefinitionContext(config, assembled = {}) {
|
|
3356
|
+
const tools = selectNamedResources(
|
|
3357
|
+
assembled.tools ?? config.resources.tools,
|
|
3358
|
+
config.definition.tools,
|
|
3359
|
+
"Tool"
|
|
3360
|
+
);
|
|
3361
|
+
const skills = selectNamedResources(
|
|
3362
|
+
config.resources.skills,
|
|
3363
|
+
config.definition.skills,
|
|
3364
|
+
"Skill"
|
|
3365
|
+
);
|
|
3366
|
+
const contexts = selectNamedResources(
|
|
3367
|
+
config.resources.contexts ?? [],
|
|
3368
|
+
config.definition.context,
|
|
3369
|
+
"Context"
|
|
3370
|
+
);
|
|
3371
|
+
const candidateRegistry = assembled.phases ?? config.resources.phases;
|
|
3372
|
+
const selectedPhases = selectNamedResources(
|
|
3373
|
+
[...candidateRegistry?.phases.values() ?? []],
|
|
3374
|
+
config.definition.phases?.phaseIds,
|
|
3375
|
+
"Phase"
|
|
3376
|
+
);
|
|
3377
|
+
const phases = new Map(selectedPhases.map((phase) => [phase.name, phase]));
|
|
3378
|
+
const requestedEntry = config.definition.phases ? config.definition.phases.entryPhaseId : candidateRegistry?.entryPhaseId ?? null;
|
|
3379
|
+
const entryPhaseId = requestedEntry === DEFAULT_PHASE_ID ? DEFAULT_PHASE_ID : requestedEntry && phases.has(requestedEntry) ? requestedEntry : null;
|
|
3380
|
+
if (requestedEntry && requestedEntry !== DEFAULT_PHASE_ID && !phases.has(requestedEntry)) {
|
|
3381
|
+
console.warn(`Phase entry "${requestedEntry}" is not available; Rowan will use "default".`);
|
|
3203
3382
|
}
|
|
3204
|
-
};
|
|
3205
|
-
|
|
3206
|
-
// src/extensions/runner.ts
|
|
3207
|
-
import { execFile } from "child_process";
|
|
3208
|
-
import {
|
|
3209
|
-
registerModel,
|
|
3210
|
-
unregisterProviderModels,
|
|
3211
|
-
registerApiProvider
|
|
3212
|
-
} from "@rowan-agent/models";
|
|
3213
|
-
|
|
3214
|
-
// src/extensions/types.ts
|
|
3215
|
-
function createSourceInfo(extensionPath, options = {}) {
|
|
3216
|
-
const source = options.source ?? (extensionPath.startsWith("<") ? "synthetic" : "local");
|
|
3217
|
-
const displayName = extensionPath.startsWith("<") ? extensionPath.slice(1, -1) : extensionPath.split("/").pop() ?? extensionPath;
|
|
3218
3383
|
return {
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3384
|
+
systemPrompt: [config.definition.content, buildContextDescription(contexts)].filter((section) => section.length > 0).join("\n\n"),
|
|
3385
|
+
tools,
|
|
3386
|
+
skills,
|
|
3387
|
+
phases: { phases, entryPhaseId }
|
|
3222
3388
|
};
|
|
3223
3389
|
}
|
|
3224
|
-
function
|
|
3225
|
-
|
|
3226
|
-
const
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3390
|
+
function mergePhases(base, extension) {
|
|
3391
|
+
if (!base && extension.phases.size === 0) return void 0;
|
|
3392
|
+
const phases = new Map(base?.phases ?? []);
|
|
3393
|
+
for (const [name, phase] of extension.phases) {
|
|
3394
|
+
if (phases.has(name)) throw new TypeError(`Extension Phase collides with Context Phase "${name}"`);
|
|
3395
|
+
phases.set(name, phase);
|
|
3396
|
+
}
|
|
3397
|
+
return {
|
|
3398
|
+
phases,
|
|
3399
|
+
entryPhaseId: base?.entryPhaseId ?? extension.entryPhaseId
|
|
3230
3400
|
};
|
|
3401
|
+
}
|
|
3402
|
+
function adaptExtensionTool(input) {
|
|
3403
|
+
const definition = input.definition;
|
|
3231
3404
|
return {
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3405
|
+
name: definition.name,
|
|
3406
|
+
description: definition.description,
|
|
3407
|
+
parameters: definition.parameters,
|
|
3408
|
+
execute: async (args, _context, signal) => {
|
|
3409
|
+
const result = await definition.execute(args, signal);
|
|
3410
|
+
const content = JSON.parse(JSON.stringify(result.content));
|
|
3411
|
+
return result.isError ? { ok: false, content, error: "Extension Tool failed." } : { ok: true, content };
|
|
3235
3412
|
}
|
|
3236
3413
|
};
|
|
3237
3414
|
}
|
|
3238
|
-
|
|
3239
|
-
// src/extensions/runner.ts
|
|
3240
|
-
import { parseModelRef as parseModelRef2 } from "@rowan-agent/models";
|
|
3241
|
-
|
|
3242
|
-
// src/extensions/event-bus.ts
|
|
3243
|
-
function createEventBus() {
|
|
3244
|
-
const listeners = /* @__PURE__ */ new Map();
|
|
3415
|
+
function toLoopResult(result, toolCallId, toolName) {
|
|
3245
3416
|
return {
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
set.delete(listener);
|
|
3252
|
-
if (set.size === 0) listeners.delete(event);
|
|
3253
|
-
};
|
|
3254
|
-
},
|
|
3255
|
-
emit(event, ...args) {
|
|
3256
|
-
const set = listeners.get(event);
|
|
3257
|
-
if (!set) return;
|
|
3258
|
-
for (const listener of set) {
|
|
3259
|
-
try {
|
|
3260
|
-
listener(...args);
|
|
3261
|
-
} catch (err) {
|
|
3262
|
-
console.error(`[event-bus] Listener error for "${event}":`, err);
|
|
3263
|
-
}
|
|
3264
|
-
}
|
|
3265
|
-
},
|
|
3266
|
-
off(event) {
|
|
3267
|
-
if (event) {
|
|
3268
|
-
listeners.delete(event);
|
|
3269
|
-
} else {
|
|
3270
|
-
listeners.clear();
|
|
3271
|
-
}
|
|
3272
|
-
},
|
|
3273
|
-
has(event) {
|
|
3274
|
-
const set = listeners.get(event);
|
|
3275
|
-
return set !== void 0 && set.size > 0;
|
|
3276
|
-
},
|
|
3277
|
-
count(event) {
|
|
3278
|
-
return listeners.get(event)?.size ?? 0;
|
|
3279
|
-
}
|
|
3417
|
+
toolCallId,
|
|
3418
|
+
toolName,
|
|
3419
|
+
ok: result.ok,
|
|
3420
|
+
content: result.content,
|
|
3421
|
+
...!result.ok ? { error: result.error } : {}
|
|
3280
3422
|
};
|
|
3281
3423
|
}
|
|
3424
|
+
function fromLoopResult(result) {
|
|
3425
|
+
return result.ok ? { ok: true, content: result.content } : { ok: false, content: result.content, error: result.error ?? "Extension hook rejected the Tool result." };
|
|
3426
|
+
}
|
|
3282
3427
|
|
|
3283
|
-
// src/
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3428
|
+
// src/runtime/transient-run-events.ts
|
|
3429
|
+
var MAX_BUFFERED_EVENTS = 128;
|
|
3430
|
+
var MAX_BUFFERED_TEXT = 64 * 1024;
|
|
3431
|
+
function deferred() {
|
|
3432
|
+
let resolve7;
|
|
3433
|
+
const promise = new Promise((done) => {
|
|
3434
|
+
resolve7 = done;
|
|
3435
|
+
});
|
|
3436
|
+
return { promise, resolve: resolve7 };
|
|
3437
|
+
}
|
|
3438
|
+
var TransientRunEventSubscription = class {
|
|
3439
|
+
queue = [];
|
|
3440
|
+
wake = deferred();
|
|
3441
|
+
version = 0;
|
|
3442
|
+
stopped = false;
|
|
3443
|
+
push(event) {
|
|
3444
|
+
if (this.stopped) return;
|
|
3445
|
+
const previous = this.queue.at(-1);
|
|
3446
|
+
if (previous?.kind === "message_delta" && event.kind === "message_delta" && previous.executionId === event.executionId && previous.messageId === event.messageId && previous.offset + previous.text.length === event.offset) {
|
|
3447
|
+
this.queue[this.queue.length - 1] = {
|
|
3448
|
+
...previous,
|
|
3449
|
+
text: previous.text + event.text
|
|
3450
|
+
};
|
|
3451
|
+
} else {
|
|
3452
|
+
if (this.queue.length >= MAX_BUFFERED_EVENTS) this.queue.shift();
|
|
3453
|
+
this.queue.push(event);
|
|
3454
|
+
}
|
|
3455
|
+
this.trimText();
|
|
3456
|
+
this.notify();
|
|
3457
|
+
}
|
|
3458
|
+
shift() {
|
|
3459
|
+
return this.queue.shift();
|
|
3460
|
+
}
|
|
3461
|
+
clear(executionId) {
|
|
3462
|
+
if (executionId === void 0) this.queue.length = 0;
|
|
3463
|
+
else {
|
|
3464
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
3465
|
+
if (this.queue[index]?.executionId === executionId) this.queue.splice(index, 1);
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
this.notify();
|
|
3469
|
+
}
|
|
3470
|
+
clearMessage(messageId) {
|
|
3471
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
3472
|
+
const event = this.queue[index];
|
|
3473
|
+
if (event?.kind === "message_delta" && event.messageId === messageId) this.queue.splice(index, 1);
|
|
3474
|
+
}
|
|
3475
|
+
this.notify();
|
|
3476
|
+
}
|
|
3477
|
+
clearTool(toolCallId) {
|
|
3478
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
3479
|
+
const event = this.queue[index];
|
|
3480
|
+
if (event?.kind === "tool_progress" && event.toolCallId === toolCallId) this.queue.splice(index, 1);
|
|
3481
|
+
}
|
|
3482
|
+
this.notify();
|
|
3483
|
+
}
|
|
3484
|
+
checkpoint() {
|
|
3485
|
+
return this.version;
|
|
3486
|
+
}
|
|
3487
|
+
changed(since) {
|
|
3488
|
+
if (this.version !== since || this.queue.length > 0 || this.stopped) return Promise.resolve();
|
|
3489
|
+
return this.wake.promise;
|
|
3490
|
+
}
|
|
3491
|
+
stop() {
|
|
3492
|
+
if (this.stopped) return;
|
|
3493
|
+
this.stopped = true;
|
|
3494
|
+
this.queue.length = 0;
|
|
3495
|
+
this.notify();
|
|
3496
|
+
}
|
|
3497
|
+
notify() {
|
|
3498
|
+
const wake = this.wake;
|
|
3499
|
+
this.wake = deferred();
|
|
3500
|
+
this.version += 1;
|
|
3501
|
+
wake.resolve();
|
|
3502
|
+
}
|
|
3503
|
+
trimText() {
|
|
3504
|
+
let buffered = this.queue.reduce(
|
|
3505
|
+
(length, event) => length + (event.kind === "message_delta" ? event.text.length : 0),
|
|
3506
|
+
0
|
|
3507
|
+
);
|
|
3508
|
+
while (buffered > MAX_BUFFERED_TEXT) {
|
|
3509
|
+
const index = this.queue.findIndex((event2) => event2.kind === "message_delta");
|
|
3510
|
+
if (index < 0) return;
|
|
3511
|
+
const event = this.queue[index];
|
|
3512
|
+
const overflow = buffered - MAX_BUFFERED_TEXT;
|
|
3513
|
+
if (event.text.length <= overflow) {
|
|
3514
|
+
this.queue.splice(index, 1);
|
|
3515
|
+
buffered -= event.text.length;
|
|
3516
|
+
} else {
|
|
3517
|
+
this.queue[index] = {
|
|
3518
|
+
...event,
|
|
3519
|
+
offset: event.offset + overflow,
|
|
3520
|
+
text: event.text.slice(overflow)
|
|
3521
|
+
};
|
|
3522
|
+
buffered -= overflow;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
};
|
|
3527
|
+
var TransientRunEventHub = class {
|
|
3528
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
3529
|
+
subscribe(runId) {
|
|
3530
|
+
const subscription = new TransientRunEventSubscription();
|
|
3531
|
+
const current = this.subscriptions.get(runId) ?? /* @__PURE__ */ new Set();
|
|
3532
|
+
current.add(subscription);
|
|
3533
|
+
this.subscriptions.set(runId, current);
|
|
3534
|
+
return subscription;
|
|
3535
|
+
}
|
|
3536
|
+
unsubscribe(runId, subscription) {
|
|
3537
|
+
subscription.stop();
|
|
3538
|
+
const current = this.subscriptions.get(runId);
|
|
3539
|
+
current?.delete(subscription);
|
|
3540
|
+
if (current?.size === 0) this.subscriptions.delete(runId);
|
|
3541
|
+
}
|
|
3542
|
+
publish(event) {
|
|
3543
|
+
for (const subscription of this.subscriptions.get(event.runId) ?? []) {
|
|
3544
|
+
subscription.push(event);
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
clear(runId, executionId) {
|
|
3548
|
+
for (const subscription of this.subscriptions.get(runId) ?? []) {
|
|
3549
|
+
subscription.clear(executionId);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
clearMessage(runId, messageId) {
|
|
3553
|
+
for (const subscription of this.subscriptions.get(runId) ?? []) subscription.clearMessage(messageId);
|
|
3554
|
+
}
|
|
3555
|
+
clearTool(runId, toolCallId) {
|
|
3556
|
+
for (const subscription of this.subscriptions.get(runId) ?? []) subscription.clearTool(toolCallId);
|
|
3557
|
+
}
|
|
3558
|
+
close() {
|
|
3559
|
+
for (const subscriptions of this.subscriptions.values()) {
|
|
3560
|
+
for (const subscription of subscriptions) subscription.stop();
|
|
3561
|
+
}
|
|
3562
|
+
this.subscriptions.clear();
|
|
3563
|
+
}
|
|
3564
|
+
};
|
|
3565
|
+
|
|
3566
|
+
// src/extensions/hooks.ts
|
|
3567
|
+
var HookError = class extends Error {
|
|
3568
|
+
constructor(eventType, message, cause) {
|
|
3569
|
+
super(message, cause === void 0 ? void 0 : { cause });
|
|
3570
|
+
this.eventType = eventType;
|
|
3571
|
+
this.name = "HookError";
|
|
3572
|
+
}
|
|
3573
|
+
eventType;
|
|
3574
|
+
};
|
|
3575
|
+
var HooksManager = class {
|
|
3576
|
+
handlers = /* @__PURE__ */ new Map();
|
|
3577
|
+
on(eventType, handler) {
|
|
3578
|
+
const handlers = this.handlers.get(eventType) ?? [];
|
|
3579
|
+
handlers.push(handler);
|
|
3580
|
+
this.handlers.set(eventType, handlers);
|
|
3581
|
+
}
|
|
3582
|
+
off(eventType, handler) {
|
|
3583
|
+
const handlers = this.handlers.get(eventType);
|
|
3584
|
+
if (!handlers) return;
|
|
3585
|
+
const index = handlers.indexOf(handler);
|
|
3586
|
+
if (index >= 0) handlers.splice(index, 1);
|
|
3587
|
+
}
|
|
3588
|
+
async emit(eventType, event) {
|
|
3589
|
+
const handlers = this.handlers.get(eventType);
|
|
3590
|
+
if (!handlers?.length) return;
|
|
3591
|
+
const results = await Promise.allSettled(handlers.map((handler) => handler(event)));
|
|
3592
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
3593
|
+
if (failure) {
|
|
3594
|
+
throw new HookError(
|
|
3595
|
+
eventType,
|
|
3596
|
+
`${results.filter((result) => result.status === "rejected").length} handler(s) failed for "${eventType}"`,
|
|
3597
|
+
failure.reason instanceof Error ? failure.reason : new Error(String(failure.reason))
|
|
3598
|
+
);
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
async emitFirst(eventType, event) {
|
|
3602
|
+
const handlers = this.handlers.get(eventType);
|
|
3603
|
+
if (!handlers?.length) return void 0;
|
|
3604
|
+
for (const handler of handlers) {
|
|
3605
|
+
try {
|
|
3606
|
+
const result = await handler(event);
|
|
3607
|
+
if (result !== void 0) return result;
|
|
3608
|
+
} catch (error) {
|
|
3609
|
+
throw new HookError(
|
|
3610
|
+
eventType,
|
|
3611
|
+
`Handler failed for "${eventType}"`,
|
|
3612
|
+
error instanceof Error ? error : new Error(String(error))
|
|
3613
|
+
);
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
return void 0;
|
|
3617
|
+
}
|
|
3618
|
+
};
|
|
3619
|
+
|
|
3620
|
+
// src/extensions/runner.ts
|
|
3621
|
+
import { execFile } from "child_process";
|
|
3622
|
+
import {
|
|
3623
|
+
registerModel,
|
|
3624
|
+
unregisterProviderModels,
|
|
3625
|
+
registerApiProvider
|
|
3626
|
+
} from "@rowan-agent/models";
|
|
3627
|
+
|
|
3628
|
+
// src/extensions/types.ts
|
|
3629
|
+
function createSourceInfo(extensionPath, options = {}) {
|
|
3630
|
+
const source = options.source ?? (extensionPath.startsWith("<") ? "synthetic" : "local");
|
|
3631
|
+
const displayName = extensionPath.startsWith("<") ? extensionPath.slice(1, -1) : extensionPath.split("/").pop() ?? extensionPath;
|
|
3632
|
+
return {
|
|
3633
|
+
source,
|
|
3634
|
+
baseDir: options.baseDir,
|
|
3635
|
+
displayName
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
function createExtensionRuntime() {
|
|
3639
|
+
const state = {};
|
|
3640
|
+
const assertActive = () => {
|
|
3641
|
+
if (state.staleMessage) {
|
|
3642
|
+
throw new Error(state.staleMessage);
|
|
3643
|
+
}
|
|
3644
|
+
};
|
|
3645
|
+
return {
|
|
3646
|
+
assertActive,
|
|
3647
|
+
invalidate: (message) => {
|
|
3648
|
+
state.staleMessage ??= message ?? "This extension context is stale after runtime replacement or reload. Do not use a captured extension API after the runner has been replaced.";
|
|
3649
|
+
}
|
|
3650
|
+
};
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
// src/extensions/runner.ts
|
|
3654
|
+
import { parseModelRef as parseModelRef2 } from "@rowan-agent/models";
|
|
3655
|
+
|
|
3656
|
+
// src/extensions/event-bus.ts
|
|
3657
|
+
function createEventBus() {
|
|
3658
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
3659
|
+
return {
|
|
3660
|
+
on(event, listener) {
|
|
3661
|
+
const set = listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
3662
|
+
set.add(listener);
|
|
3663
|
+
listeners.set(event, set);
|
|
3664
|
+
return () => {
|
|
3665
|
+
set.delete(listener);
|
|
3666
|
+
if (set.size === 0) listeners.delete(event);
|
|
3667
|
+
};
|
|
3668
|
+
},
|
|
3669
|
+
emit(event, ...args) {
|
|
3670
|
+
const set = listeners.get(event);
|
|
3671
|
+
if (!set) return;
|
|
3672
|
+
for (const listener of set) {
|
|
3673
|
+
try {
|
|
3674
|
+
listener(...args);
|
|
3675
|
+
} catch (err) {
|
|
3676
|
+
console.error(`[event-bus] Listener error for "${event}":`, err);
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
},
|
|
3680
|
+
off(event) {
|
|
3681
|
+
if (event) {
|
|
3682
|
+
listeners.delete(event);
|
|
3683
|
+
} else {
|
|
3684
|
+
listeners.clear();
|
|
3685
|
+
}
|
|
3686
|
+
},
|
|
3687
|
+
has(event) {
|
|
3688
|
+
const set = listeners.get(event);
|
|
3689
|
+
return set !== void 0 && set.size > 0;
|
|
3690
|
+
},
|
|
3691
|
+
count(event) {
|
|
3692
|
+
return listeners.get(event)?.size ?? 0;
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
// src/extensions/runner.ts
|
|
3698
|
+
async function execCommand(command, args, cwd, options) {
|
|
3699
|
+
return new Promise((resolve7, reject) => {
|
|
3700
|
+
const child = execFile(
|
|
3701
|
+
command,
|
|
3702
|
+
args,
|
|
3703
|
+
{
|
|
3704
|
+
cwd: options?.cwd ?? cwd,
|
|
3705
|
+
env: options?.env ? { ...process.env, ...options.env } : void 0,
|
|
3706
|
+
timeout: options?.timeout,
|
|
3707
|
+
maxBuffer: 10 * 1024 * 1024
|
|
3708
|
+
},
|
|
3709
|
+
(error, stdout, stderr) => {
|
|
3710
|
+
if (error && error.killed && options?.signal?.aborted) {
|
|
3711
|
+
reject(new Error("Command was aborted"));
|
|
3712
|
+
return;
|
|
3713
|
+
}
|
|
3714
|
+
resolve7({
|
|
3715
|
+
exitCode: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
3716
|
+
stdout: stdout ?? "",
|
|
3303
3717
|
stderr: stderr ?? ""
|
|
3304
3718
|
});
|
|
3305
3719
|
}
|
|
@@ -3416,6 +3830,7 @@ var ExtensionRunner = class {
|
|
|
3416
3830
|
*/
|
|
3417
3831
|
invalidate(message) {
|
|
3418
3832
|
this.runtime.invalidate(message);
|
|
3833
|
+
for (const extension of this.extensions) extension.runtime.invalidate(message);
|
|
3419
3834
|
}
|
|
3420
3835
|
// ---------------------------------------------------------------------------
|
|
3421
3836
|
// Direct hook API
|
|
@@ -3445,13 +3860,21 @@ var ExtensionRunner = class {
|
|
|
3445
3860
|
*/
|
|
3446
3861
|
async loadExtensions(extensions) {
|
|
3447
3862
|
for (const ext of extensions) {
|
|
3863
|
+
const extension = {
|
|
3864
|
+
path: ext.path,
|
|
3865
|
+
tools: /* @__PURE__ */ new Map(),
|
|
3866
|
+
phases: /* @__PURE__ */ new Set(),
|
|
3867
|
+
cleanup: [],
|
|
3868
|
+
runtime: createExtensionRuntime()
|
|
3869
|
+
};
|
|
3448
3870
|
try {
|
|
3449
|
-
const extension = { path: ext.path, tools: /* @__PURE__ */ new Map() };
|
|
3450
3871
|
const api = this.createExtensionAPI(extension, ext.manifest);
|
|
3451
|
-
await ext.factory(api);
|
|
3872
|
+
const disposer = await ext.factory(api);
|
|
3873
|
+
if (typeof disposer === "function") extension.disposer = disposer;
|
|
3452
3874
|
this.extensions.push(extension);
|
|
3453
3875
|
this._phaseCache = null;
|
|
3454
3876
|
} catch (error) {
|
|
3877
|
+
await this.rollbackExtension(extension);
|
|
3455
3878
|
const message = error instanceof Error ? error.message : String(error);
|
|
3456
3879
|
this.emitError({
|
|
3457
3880
|
extensionPath: ext.path,
|
|
@@ -3463,6 +3886,17 @@ var ExtensionRunner = class {
|
|
|
3463
3886
|
}
|
|
3464
3887
|
}
|
|
3465
3888
|
}
|
|
3889
|
+
/** Dispose all active Extensions in reverse activation order. */
|
|
3890
|
+
async close() {
|
|
3891
|
+
this.abortController.abort();
|
|
3892
|
+
for (const extension of [...this.extensions].reverse()) {
|
|
3893
|
+
await this.disposeExtension(extension, "This Extension Runtime has been closed.");
|
|
3894
|
+
}
|
|
3895
|
+
this.extensions.length = 0;
|
|
3896
|
+
this.phases.clear();
|
|
3897
|
+
this._phaseCache = null;
|
|
3898
|
+
this.runtime.invalidate("This Extension Runtime has been closed.");
|
|
3899
|
+
}
|
|
3466
3900
|
// ---------------------------------------------------------------------------
|
|
3467
3901
|
// Tool management
|
|
3468
3902
|
// ---------------------------------------------------------------------------
|
|
@@ -3660,8 +4094,9 @@ var ExtensionRunner = class {
|
|
|
3660
4094
|
unregisterProvider: (name) => this.unregisterProvider(name),
|
|
3661
4095
|
registerTool: (tool) => this.registerTool(extension, tool),
|
|
3662
4096
|
context: extContext,
|
|
3663
|
-
manifest
|
|
3664
|
-
|
|
4097
|
+
manifest,
|
|
4098
|
+
trackCleanup: (cleanup) => extension.cleanup.push(cleanup)
|
|
4099
|
+
}, extension.runtime, this.events);
|
|
3665
4100
|
}
|
|
3666
4101
|
registerTool(extension, tool) {
|
|
3667
4102
|
if (extension.tools.has(tool.name)) {
|
|
@@ -3718,6 +4153,7 @@ var ExtensionRunner = class {
|
|
|
3718
4153
|
source: { extensionPath: extension.path }
|
|
3719
4154
|
};
|
|
3720
4155
|
this.phases.set(name, registered);
|
|
4156
|
+
extension.phases.add(name);
|
|
3721
4157
|
this._phaseCache = null;
|
|
3722
4158
|
}
|
|
3723
4159
|
registerProvider(config) {
|
|
@@ -3752,260 +4188,834 @@ var ExtensionRunner = class {
|
|
|
3752
4188
|
this._phaseCache = new Map(this.phases);
|
|
3753
4189
|
return this._phaseCache;
|
|
3754
4190
|
}
|
|
4191
|
+
async rollbackExtension(extension) {
|
|
4192
|
+
await this.disposeExtension(extension, "This Extension activation was rolled back.");
|
|
4193
|
+
}
|
|
4194
|
+
async disposeExtension(extension, message) {
|
|
4195
|
+
extension.runtime.invalidate(message);
|
|
4196
|
+
for (const name of extension.phases) this.phases.delete(name);
|
|
4197
|
+
for (const cleanup of [...extension.cleanup].reverse()) {
|
|
4198
|
+
await Promise.resolve().then(() => cleanup()).catch(() => void 0);
|
|
4199
|
+
}
|
|
4200
|
+
if (extension.disposer) {
|
|
4201
|
+
await Promise.resolve().then(() => extension.disposer()).catch(() => void 0);
|
|
4202
|
+
}
|
|
4203
|
+
this._phaseCache = null;
|
|
4204
|
+
}
|
|
3755
4205
|
};
|
|
3756
4206
|
function createExtensionRunner(options) {
|
|
3757
4207
|
return new ExtensionRunner(options);
|
|
3758
4208
|
}
|
|
3759
4209
|
|
|
3760
|
-
// src/
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
4210
|
+
// src/extensions/loader.ts
|
|
4211
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
4212
|
+
import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
4213
|
+
import { fileURLToPath } from "url";
|
|
4214
|
+
import { basename as basename3, dirname as dirname4, extname as extname2, join as join2, resolve as resolve4 } from "path";
|
|
4215
|
+
import { createJiti } from "jiti";
|
|
4216
|
+
function isExtensionFile(path) {
|
|
4217
|
+
const extension = extname2(path).toLowerCase();
|
|
4218
|
+
return extension === ".ts" || extension === ".js";
|
|
4219
|
+
}
|
|
4220
|
+
function isSyntheticPath(path) {
|
|
4221
|
+
return path.startsWith("<") && path.endsWith(">");
|
|
4222
|
+
}
|
|
4223
|
+
function readManifestSync(dir) {
|
|
4224
|
+
const manifestPath = join2(dir, "package.json");
|
|
4225
|
+
if (!existsSync2(manifestPath)) {
|
|
4226
|
+
return void 0;
|
|
3769
4227
|
}
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
4228
|
+
try {
|
|
4229
|
+
const content = readFileSync(manifestPath, "utf8");
|
|
4230
|
+
const pkg = JSON.parse(content);
|
|
4231
|
+
const rowan = pkg.rowan;
|
|
4232
|
+
if (!rowan) return void 0;
|
|
4233
|
+
return {
|
|
4234
|
+
entry: rowan.extensions?.[0],
|
|
4235
|
+
phase: rowan.phase
|
|
4236
|
+
};
|
|
4237
|
+
} catch {
|
|
4238
|
+
return void 0;
|
|
3780
4239
|
}
|
|
3781
|
-
const extensionPhases = runner.createPhaseRegistry({ entryPhaseId: null });
|
|
3782
|
-
const phases = mergePhases(config.resources.phases, extensionPhases);
|
|
3783
|
-
const context = resolveDefinitionContext(config, { tools, phases });
|
|
3784
|
-
return {
|
|
3785
|
-
context,
|
|
3786
|
-
beforePhase: (phaseId, input) => runner.emitBeforePhase(phaseId, input),
|
|
3787
|
-
afterPhase: (phaseId, output) => runner.emitAfterPhase(phaseId, output),
|
|
3788
|
-
beforePrompt: (phaseId, input) => runner.emitBeforePrompt(phaseId, input),
|
|
3789
|
-
beforeToolCall: async (input) => {
|
|
3790
|
-
const tool = projectTool(input.tool, input.context.agentId, input.context.runId);
|
|
3791
|
-
const decision = await runner.emitBeforeToolCall(tool, input.args);
|
|
3792
|
-
return decision.allow ? { allow: true } : { allow: false, reason: decision.reason ?? "Extension hook rejected the Tool." };
|
|
3793
|
-
},
|
|
3794
|
-
afterToolCall: async (input) => {
|
|
3795
|
-
const tool = projectTool(input.tool, input.context.agentId, input.context.runId);
|
|
3796
|
-
const result = await runner.emitAfterToolCall(tool, toLoopResult(input.result, input.context.toolCallId, input.tool.name));
|
|
3797
|
-
return fromLoopResult(result);
|
|
3798
|
-
},
|
|
3799
|
-
setContext: (context2) => {
|
|
3800
|
-
runner.currentContext = context2;
|
|
3801
|
-
}
|
|
3802
|
-
};
|
|
3803
4240
|
}
|
|
3804
|
-
function
|
|
3805
|
-
const
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
"Tool"
|
|
3809
|
-
);
|
|
3810
|
-
const skills = selectNamedResources(
|
|
3811
|
-
config.resources.skills,
|
|
3812
|
-
config.definition.skills,
|
|
3813
|
-
"Skill"
|
|
3814
|
-
);
|
|
3815
|
-
const candidateRegistry = assembled.phases ?? config.resources.phases;
|
|
3816
|
-
const selectedPhases = selectNamedResources(
|
|
3817
|
-
[...candidateRegistry?.phases.values() ?? []],
|
|
3818
|
-
config.definition.phases,
|
|
3819
|
-
"Phase"
|
|
3820
|
-
);
|
|
3821
|
-
const phases = new Map(selectedPhases.map((phase) => [phase.name, phase]));
|
|
3822
|
-
const requestedEntry = config.definition.entryPhase ?? candidateRegistry?.entryPhaseId ?? null;
|
|
3823
|
-
const entryPhaseId = requestedEntry === DEFAULT_PHASE_ID ? DEFAULT_PHASE_ID : requestedEntry && phases.has(requestedEntry) ? requestedEntry : null;
|
|
3824
|
-
if (requestedEntry && requestedEntry !== DEFAULT_PHASE_ID && !phases.has(requestedEntry)) {
|
|
3825
|
-
console.warn(`Phase entry "${requestedEntry}" is not available; Rowan will use "default".`);
|
|
4241
|
+
function jitiAliases() {
|
|
4242
|
+
const moduleUrl = import.meta.url;
|
|
4243
|
+
if (!moduleUrl) {
|
|
4244
|
+
return {};
|
|
3826
4245
|
}
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
};
|
|
4246
|
+
const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
|
|
4247
|
+
if (!existsSync2(agentSourcePath)) {
|
|
4248
|
+
return {};
|
|
4249
|
+
}
|
|
4250
|
+
return { "@rowan-agent/agent": agentSourcePath };
|
|
3833
4251
|
}
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
4252
|
+
var sharedJiti;
|
|
4253
|
+
function getJiti() {
|
|
4254
|
+
sharedJiti ??= createJiti(import.meta.url || process.cwd(), {
|
|
4255
|
+
fsCache: false,
|
|
4256
|
+
moduleCache: false,
|
|
4257
|
+
tryNative: false,
|
|
4258
|
+
alias: jitiAliases()
|
|
4259
|
+
});
|
|
4260
|
+
return sharedJiti;
|
|
4261
|
+
}
|
|
4262
|
+
async function loadExtensionModule(extensionPath) {
|
|
4263
|
+
const jiti = getJiti();
|
|
4264
|
+
const module = await jiti.import(extensionPath, { default: true });
|
|
4265
|
+
return typeof module === "function" ? module : void 0;
|
|
4266
|
+
}
|
|
4267
|
+
async function readPackageManifest(path) {
|
|
4268
|
+
if (!existsSync2(path)) {
|
|
4269
|
+
return void 0;
|
|
4270
|
+
}
|
|
4271
|
+
const content = await readFile3(path, "utf8");
|
|
4272
|
+
return JSON.parse(content);
|
|
4273
|
+
}
|
|
4274
|
+
async function resolveExtensionEntries(dir) {
|
|
4275
|
+
const manifest = await readPackageManifest(join2(dir, "package.json"));
|
|
4276
|
+
const declared = manifest?.rowan?.extensions;
|
|
4277
|
+
if (declared && declared.length > 0) {
|
|
4278
|
+
return declared.map((entry) => resolve4(dir, entry));
|
|
4279
|
+
}
|
|
4280
|
+
for (const name of ["index.ts", "index.js"]) {
|
|
4281
|
+
const entry = join2(dir, name);
|
|
4282
|
+
if (existsSync2(entry)) {
|
|
4283
|
+
return [entry];
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
return void 0;
|
|
4287
|
+
}
|
|
4288
|
+
async function discoverExtensionsInDir(dir) {
|
|
4289
|
+
const entries = await readdir2(dir, { withFileTypes: true }).catch((error) => {
|
|
4290
|
+
if (error.code === "ENOENT") {
|
|
4291
|
+
return [];
|
|
4292
|
+
}
|
|
4293
|
+
throw error;
|
|
4294
|
+
});
|
|
4295
|
+
const paths = [];
|
|
4296
|
+
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4297
|
+
const entryPath = join2(dir, entry.name);
|
|
4298
|
+
if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
|
|
4299
|
+
paths.push(entryPath);
|
|
4300
|
+
continue;
|
|
4301
|
+
}
|
|
4302
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
4303
|
+
const info = entry.isSymbolicLink() ? await stat2(entryPath).catch(() => void 0) : void 0;
|
|
4304
|
+
if (entry.isDirectory() || info?.isDirectory()) {
|
|
4305
|
+
const resolvedEntries = await resolveExtensionEntries(entryPath);
|
|
4306
|
+
if (resolvedEntries) {
|
|
4307
|
+
paths.push(...resolvedEntries);
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
3840
4311
|
}
|
|
4312
|
+
return paths;
|
|
4313
|
+
}
|
|
4314
|
+
function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
|
|
4315
|
+
const resolvedCwd = resolve4(cwd);
|
|
4316
|
+
const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve4(resolvedCwd, extensionPath);
|
|
4317
|
+
const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname4(resolvedPath)) || "unknown";
|
|
4318
|
+
const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname4(resolvedPath);
|
|
4319
|
+
const manifest = readManifestSync(manifestDir);
|
|
3841
4320
|
return {
|
|
3842
|
-
|
|
3843
|
-
|
|
4321
|
+
path: resolvedPath,
|
|
4322
|
+
name,
|
|
4323
|
+
factory,
|
|
4324
|
+
manifest
|
|
3844
4325
|
};
|
|
3845
4326
|
}
|
|
3846
|
-
function
|
|
3847
|
-
const
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
const
|
|
3854
|
-
|
|
3855
|
-
|
|
4327
|
+
async function loadExtensions(paths, cwd) {
|
|
4328
|
+
const extensions = [];
|
|
4329
|
+
const errors = [];
|
|
4330
|
+
const resolvedCwd = resolve4(cwd);
|
|
4331
|
+
for (const path of paths) {
|
|
4332
|
+
const resolvedPath = resolve4(resolvedCwd, path);
|
|
4333
|
+
try {
|
|
4334
|
+
const factory = await loadExtensionModule(resolvedPath);
|
|
4335
|
+
if (!factory) {
|
|
4336
|
+
errors.push({
|
|
4337
|
+
path: resolvedPath,
|
|
4338
|
+
error: `Extension does not export a valid factory function: ${path}`
|
|
4339
|
+
});
|
|
4340
|
+
continue;
|
|
4341
|
+
}
|
|
4342
|
+
extensions.push(loadExtensionFromFactory(factory, resolvedCwd, resolvedPath));
|
|
4343
|
+
} catch (error) {
|
|
4344
|
+
errors.push({
|
|
4345
|
+
path: resolvedPath,
|
|
4346
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4347
|
+
});
|
|
3856
4348
|
}
|
|
3857
|
-
}
|
|
4349
|
+
}
|
|
4350
|
+
return { extensions, errors };
|
|
3858
4351
|
}
|
|
3859
|
-
function
|
|
4352
|
+
async function loadExtensionsFromPath(path) {
|
|
4353
|
+
const resolvedPath = resolve4(path);
|
|
4354
|
+
const info = await stat2(resolvedPath);
|
|
4355
|
+
if (!info.isDirectory()) {
|
|
4356
|
+
return loadExtensions([resolvedPath], dirname4(resolvedPath));
|
|
4357
|
+
}
|
|
4358
|
+
const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
|
|
4359
|
+
return loadExtensions(entries, resolvedPath);
|
|
4360
|
+
}
|
|
4361
|
+
|
|
4362
|
+
// src/runtime/resource-registry.ts
|
|
4363
|
+
import { stat as stat3, readdir as readdir4 } from "fs/promises";
|
|
4364
|
+
import { join as join4, resolve as resolve6 } from "path";
|
|
4365
|
+
|
|
4366
|
+
// src/harness/skills.ts
|
|
4367
|
+
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
4368
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
4369
|
+
import { dirname as dirname5, join as join3, resolve as resolve5 } from "path";
|
|
4370
|
+
var SKILL_MARKER = "SKILL.md";
|
|
4371
|
+
function resolveSkillPath(input) {
|
|
4372
|
+
const resolved = resolve5(input);
|
|
4373
|
+
return existsSync3(resolved) && statSync2(resolved).isDirectory() ? join3(resolved, SKILL_MARKER) : resolved;
|
|
4374
|
+
}
|
|
4375
|
+
async function loadSkill(path) {
|
|
4376
|
+
const resolved = resolveSkillPath(path);
|
|
4377
|
+
let loaded;
|
|
4378
|
+
try {
|
|
4379
|
+
loaded = await loadMarkdown(resolved);
|
|
4380
|
+
} catch (error) {
|
|
4381
|
+
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
4382
|
+
warnResourceDiagnostics("skill", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
4383
|
+
throw new ResourceMetadataError("parse_failed", error.message);
|
|
4384
|
+
}
|
|
4385
|
+
const { frontmatter, body } = loaded;
|
|
4386
|
+
const metadata = frontmatter;
|
|
4387
|
+
const id = inferResourceName(resolved, SKILL_MARKER);
|
|
4388
|
+
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
4389
|
+
const name = frontmatterName || id;
|
|
4390
|
+
const description = validateDescription(metadata.description);
|
|
4391
|
+
const diagnostics = validateResourceId(id);
|
|
4392
|
+
if (frontmatterName && frontmatterName !== id) {
|
|
4393
|
+
diagnostics.push(...validateResourceName(frontmatterName, id));
|
|
4394
|
+
}
|
|
4395
|
+
diagnostics.push(...description.warnings);
|
|
4396
|
+
warnResourceDiagnostics("skill", resolved, diagnostics);
|
|
4397
|
+
if (description.missing) {
|
|
4398
|
+
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
4399
|
+
}
|
|
3860
4400
|
return {
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
4401
|
+
name,
|
|
4402
|
+
description: description.description,
|
|
4403
|
+
filePath: resolved,
|
|
4404
|
+
baseDir: dirname5(resolved),
|
|
4405
|
+
content: body,
|
|
4406
|
+
disableModelInvocation: metadata["disable-model-invocation"] === true
|
|
3866
4407
|
};
|
|
3867
4408
|
}
|
|
3868
|
-
function
|
|
3869
|
-
|
|
4409
|
+
async function loadSkills(targetPath) {
|
|
4410
|
+
const skillsDir = resolve5(targetPath);
|
|
4411
|
+
if (existsSync3(skillsDir) && statSync2(skillsDir).isFile()) {
|
|
4412
|
+
try {
|
|
4413
|
+
return [await loadSkill(skillsDir)];
|
|
4414
|
+
} catch (error) {
|
|
4415
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
4416
|
+
throw error;
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
if (existsSync3(join3(skillsDir, SKILL_MARKER))) {
|
|
4420
|
+
try {
|
|
4421
|
+
return [await loadSkill(skillsDir)];
|
|
4422
|
+
} catch (error) {
|
|
4423
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
4424
|
+
throw error;
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
const entries = await readdir3(skillsDir, { withFileTypes: true });
|
|
4428
|
+
const skills = [];
|
|
4429
|
+
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4430
|
+
if (!entry.isDirectory()) continue;
|
|
4431
|
+
const skillFile = join3(skillsDir, entry.name, SKILL_MARKER);
|
|
4432
|
+
if (!existsSync3(skillFile)) continue;
|
|
4433
|
+
try {
|
|
4434
|
+
const skill = await loadSkill(skillFile);
|
|
4435
|
+
skills.push(skill);
|
|
4436
|
+
} catch (error) {
|
|
4437
|
+
if (error instanceof ResourceMetadataError) continue;
|
|
4438
|
+
console.warn(`Failed to load skill "${entry.name}":`, error);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
return skills;
|
|
3870
4442
|
}
|
|
3871
4443
|
|
|
3872
|
-
// src/runtime/
|
|
3873
|
-
var
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
}
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
var
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
4444
|
+
// src/runtime/resource-registry.ts
|
|
4445
|
+
var ResourceRegistryError = class extends Error {
|
|
4446
|
+
code;
|
|
4447
|
+
constructor(code, message) {
|
|
4448
|
+
super(message);
|
|
4449
|
+
this.name = "ResourceRegistryError";
|
|
4450
|
+
this.code = code;
|
|
4451
|
+
}
|
|
4452
|
+
};
|
|
4453
|
+
var RESOURCE_NAME = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
4454
|
+
var SOURCE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
4455
|
+
var ResourceRegistry = class {
|
|
4456
|
+
sources = /* @__PURE__ */ new Map();
|
|
4457
|
+
/** Sources registered by the bootstrap boundary (currently Extensions). */
|
|
4458
|
+
implicitSources = /* @__PURE__ */ new Map();
|
|
4459
|
+
revisionSequence = 0;
|
|
4460
|
+
async loadAgents(input) {
|
|
4461
|
+
assertSourceId(input.sourceId);
|
|
4462
|
+
const collected = await this.collect("agent", input);
|
|
4463
|
+
return this.commit("agent", input.sourceId, collected.values, collected.skipped);
|
|
4464
|
+
}
|
|
4465
|
+
async loadSkills(input) {
|
|
4466
|
+
assertSourceId(input.sourceId);
|
|
4467
|
+
const collected = await this.collect("skill", input);
|
|
4468
|
+
return this.commit("skill", input.sourceId, collected.values, collected.skipped);
|
|
4469
|
+
}
|
|
4470
|
+
async loadPhases(input) {
|
|
4471
|
+
assertSourceId(input.sourceId);
|
|
4472
|
+
const collected = await this.collect("phase", input);
|
|
4473
|
+
return this.commit("phase", input.sourceId, collected.values, collected.skipped);
|
|
4474
|
+
}
|
|
4475
|
+
async loadTools(input) {
|
|
4476
|
+
assertSourceId(input.sourceId);
|
|
4477
|
+
return this.commit("tool", input.sourceId, input.values, []);
|
|
4478
|
+
}
|
|
4479
|
+
async unload(input) {
|
|
4480
|
+
assertSourceId(input.sourceId);
|
|
4481
|
+
if (this.hasImplicit(input.kind, input.sourceId)) {
|
|
4482
|
+
throw new ResourceRegistryError(
|
|
4483
|
+
"implicit_source_protected",
|
|
4484
|
+
`Implicit ${input.kind} source "${input.sourceId}" is managed by Runtime bootstrap.`
|
|
4485
|
+
);
|
|
4486
|
+
}
|
|
4487
|
+
const bySource = this.sources.get(input.kind);
|
|
4488
|
+
const existing = bySource?.get(input.sourceId);
|
|
4489
|
+
if (!existing) {
|
|
4490
|
+
return {
|
|
4491
|
+
revision: this.nextRevision(input.kind, input.sourceId),
|
|
4492
|
+
registered: [],
|
|
4493
|
+
skipped: []
|
|
3894
4494
|
};
|
|
3895
|
-
} else {
|
|
3896
|
-
if (this.queue.length >= MAX_BUFFERED_EVENTS) this.queue.shift();
|
|
3897
|
-
this.queue.push(event);
|
|
3898
4495
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
4496
|
+
bySource.delete(input.sourceId);
|
|
4497
|
+
return {
|
|
4498
|
+
revision: this.nextRevision(input.kind, input.sourceId),
|
|
4499
|
+
registered: [],
|
|
4500
|
+
skipped: []
|
|
4501
|
+
};
|
|
3901
4502
|
}
|
|
3902
|
-
|
|
3903
|
-
|
|
4503
|
+
resolveView(view) {
|
|
4504
|
+
const sourceIds = {
|
|
4505
|
+
agents: this.withImplicit("agent", view.agents),
|
|
4506
|
+
tools: this.withImplicit("tool", view.tools),
|
|
4507
|
+
skills: this.withImplicit("skill", view.skills),
|
|
4508
|
+
phases: this.withImplicit("phase", view.phases)
|
|
4509
|
+
};
|
|
4510
|
+
return {
|
|
4511
|
+
agents: this.resolveKind("agent", sourceIds.agents),
|
|
4512
|
+
tools: this.resolveKind("tool", sourceIds.tools),
|
|
4513
|
+
skills: this.resolveKind("skill", sourceIds.skills),
|
|
4514
|
+
phases: this.resolveKind("phase", sourceIds.phases),
|
|
4515
|
+
revisions: {
|
|
4516
|
+
agent: this.resolveRevisions("agent", sourceIds.agents),
|
|
4517
|
+
tool: this.resolveRevisions("tool", sourceIds.tools),
|
|
4518
|
+
skill: this.resolveRevisions("skill", sourceIds.skills),
|
|
4519
|
+
phase: this.resolveRevisions("phase", sourceIds.phases)
|
|
4520
|
+
},
|
|
4521
|
+
refs: {
|
|
4522
|
+
agent: this.resolveRefs("agent", sourceIds.agents),
|
|
4523
|
+
tool: this.resolveRefs("tool", sourceIds.tools),
|
|
4524
|
+
skill: this.resolveRefs("skill", sourceIds.skills),
|
|
4525
|
+
phase: this.resolveRefs("phase", sourceIds.phases)
|
|
4526
|
+
}
|
|
4527
|
+
};
|
|
3904
4528
|
}
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
4529
|
+
/** Mark one already-loaded source as implicit for every Resource View. */
|
|
4530
|
+
markImplicit(kind, sourceId) {
|
|
4531
|
+
assertSourceId(sourceId);
|
|
4532
|
+
const entries = this.implicitSources.get(kind) ?? [];
|
|
4533
|
+
if (!entries.includes(sourceId)) entries.push(sourceId);
|
|
4534
|
+
this.implicitSources.set(kind, entries);
|
|
4535
|
+
}
|
|
4536
|
+
/** Bootstrap-only helper for replacing an implicit contribution source. */
|
|
4537
|
+
async replaceImplicit(kind, sourceId, values) {
|
|
4538
|
+
const result = await this.commit(kind, sourceId, values, []);
|
|
4539
|
+
this.markImplicit(kind, sourceId);
|
|
4540
|
+
return result;
|
|
4541
|
+
}
|
|
4542
|
+
hasImplicit(kind, sourceId) {
|
|
4543
|
+
return this.implicitSources.get(kind)?.includes(sourceId) ?? false;
|
|
4544
|
+
}
|
|
4545
|
+
withImplicit(kind, sourceIds) {
|
|
4546
|
+
const combined = [...sourceIds];
|
|
4547
|
+
for (const sourceId of this.implicitSources.get(kind) ?? []) {
|
|
4548
|
+
if (!combined.includes(sourceId)) combined.push(sourceId);
|
|
4549
|
+
}
|
|
4550
|
+
return combined;
|
|
4551
|
+
}
|
|
4552
|
+
async collect(kind, input) {
|
|
4553
|
+
const hasDirectory = input.directory !== void 0;
|
|
4554
|
+
const hasValues = input.values !== void 0;
|
|
4555
|
+
if (!hasDirectory && !hasValues) {
|
|
4556
|
+
throw new ResourceRegistryError(
|
|
4557
|
+
"resource_input_missing",
|
|
4558
|
+
`Resource source "${input.sourceId}" requires directory or values.`
|
|
4559
|
+
);
|
|
4560
|
+
}
|
|
4561
|
+
const directoryValues = hasDirectory ? await loadDirectoryValues(kind, input.directory, input.sourceId) : { values: [], skipped: [] };
|
|
4562
|
+
return {
|
|
4563
|
+
values: [...directoryValues.values, ...input.values ?? []],
|
|
4564
|
+
skipped: directoryValues.skipped
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
async commit(kind, sourceId, values, skipped) {
|
|
4568
|
+
assertSourceId(sourceId);
|
|
4569
|
+
const names = /* @__PURE__ */ new Set();
|
|
4570
|
+
for (const value of values) {
|
|
4571
|
+
assertNamedResource(value, kind, sourceId);
|
|
4572
|
+
if (isImplicitCoreName(kind, value.name) && sourceId !== "rowan.core") {
|
|
4573
|
+
throw new ResourceRegistryError(
|
|
4574
|
+
"resource_collision",
|
|
4575
|
+
`${capitalize(kind)} resource "${value.name}" is reserved by Rowan core.`
|
|
4576
|
+
);
|
|
4577
|
+
}
|
|
4578
|
+
if (names.has(value.name)) {
|
|
4579
|
+
throw new ResourceRegistryError(
|
|
4580
|
+
"resource_collision",
|
|
4581
|
+
`Duplicate ${capitalize(kind)} resource "${value.name}" in source "${sourceId}".`
|
|
4582
|
+
);
|
|
3910
4583
|
}
|
|
4584
|
+
names.add(value.name);
|
|
4585
|
+
}
|
|
4586
|
+
const revision = this.nextRevision(kind, sourceId);
|
|
4587
|
+
const record = {
|
|
4588
|
+
kind,
|
|
4589
|
+
sourceId,
|
|
4590
|
+
revision,
|
|
4591
|
+
values: Object.freeze([...values])
|
|
4592
|
+
};
|
|
4593
|
+
let bySource = this.sources.get(kind);
|
|
4594
|
+
if (!bySource) {
|
|
4595
|
+
bySource = /* @__PURE__ */ new Map();
|
|
4596
|
+
this.sources.set(kind, bySource);
|
|
3911
4597
|
}
|
|
3912
|
-
|
|
4598
|
+
bySource.set(sourceId, record);
|
|
4599
|
+
return {
|
|
4600
|
+
revision,
|
|
4601
|
+
registered: values.map((value) => ({ kind, sourceId, name: value.name })),
|
|
4602
|
+
skipped
|
|
4603
|
+
};
|
|
3913
4604
|
}
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4605
|
+
resolveKind(kind, sourceIds) {
|
|
4606
|
+
const names = /* @__PURE__ */ new Set();
|
|
4607
|
+
const values = [];
|
|
4608
|
+
for (const source of this.sourcesFor(kind, sourceIds)) {
|
|
4609
|
+
for (const value of source.values) {
|
|
4610
|
+
if (names.has(value.name)) {
|
|
4611
|
+
throw new ResourceRegistryError(
|
|
4612
|
+
"resource_collision",
|
|
4613
|
+
`Duplicate ${capitalize(kind)} resource "${value.name}" in Resource View.`
|
|
4614
|
+
);
|
|
4615
|
+
}
|
|
4616
|
+
names.add(value.name);
|
|
4617
|
+
values.push(value);
|
|
4618
|
+
}
|
|
3918
4619
|
}
|
|
3919
|
-
|
|
4620
|
+
return values;
|
|
3920
4621
|
}
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
4622
|
+
resolveRevisions(kind, sourceIds) {
|
|
4623
|
+
return this.sourcesFor(kind, sourceIds).map(({ revision }) => revision);
|
|
4624
|
+
}
|
|
4625
|
+
resolveRefs(kind, sourceIds) {
|
|
4626
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
4627
|
+
const refs = [];
|
|
4628
|
+
for (const source of this.sourcesFor(kind, sourceIds)) {
|
|
4629
|
+
for (const value of source.values) {
|
|
4630
|
+
if (seenNames.has(value.name)) throw new ResourceRegistryError("resource_collision", `Duplicate ${capitalize(kind)} resource "${value.name}" in Resource View.`);
|
|
4631
|
+
seenNames.add(value.name);
|
|
4632
|
+
refs.push({ kind, sourceId: source.sourceId, name: value.name });
|
|
4633
|
+
}
|
|
3925
4634
|
}
|
|
3926
|
-
|
|
4635
|
+
return refs;
|
|
4636
|
+
}
|
|
4637
|
+
sourcesFor(kind, sourceIds) {
|
|
4638
|
+
const bySource = this.sources.get(kind);
|
|
4639
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4640
|
+
const sources = [];
|
|
4641
|
+
for (const sourceId of sourceIds) {
|
|
4642
|
+
assertSourceId(sourceId);
|
|
4643
|
+
if (seen.has(sourceId)) continue;
|
|
4644
|
+
seen.add(sourceId);
|
|
4645
|
+
const source = bySource?.get(sourceId);
|
|
4646
|
+
if (!source) {
|
|
4647
|
+
throw new ResourceRegistryError(
|
|
4648
|
+
"unknown_resource_source",
|
|
4649
|
+
`Unknown resource source "${sourceId}" for ${kind}.`
|
|
4650
|
+
);
|
|
4651
|
+
}
|
|
4652
|
+
sources.push(source);
|
|
4653
|
+
}
|
|
4654
|
+
return sources;
|
|
3927
4655
|
}
|
|
3928
|
-
|
|
3929
|
-
|
|
4656
|
+
nextRevision(kind, sourceId) {
|
|
4657
|
+
this.revisionSequence += 1;
|
|
4658
|
+
return `${kind}:${sourceId}:${this.revisionSequence}`;
|
|
3930
4659
|
}
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
4660
|
+
};
|
|
4661
|
+
function assertSourceId(sourceId) {
|
|
4662
|
+
if (typeof sourceId !== "string" || !SOURCE_ID.test(sourceId)) {
|
|
4663
|
+
throw new ResourceRegistryError("invalid_source_id", `Invalid resource source "${String(sourceId)}".`);
|
|
3934
4664
|
}
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
this.notify();
|
|
4665
|
+
}
|
|
4666
|
+
function assertNamedResource(value, kind, sourceId) {
|
|
4667
|
+
if (!value || typeof value !== "object" || typeof value.name !== "string") {
|
|
4668
|
+
throw new ResourceRegistryError("invalid_resource_name", `Invalid ${kind} resource in source "${sourceId}".`);
|
|
3940
4669
|
}
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
this.version += 1;
|
|
3945
|
-
wake.resolve();
|
|
4670
|
+
const name = value.name;
|
|
4671
|
+
if (!RESOURCE_NAME.test(name)) {
|
|
4672
|
+
throw new ResourceRegistryError("invalid_resource_name", `Invalid ${capitalize(kind)} resource name "${name}".`);
|
|
3946
4673
|
}
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
4674
|
+
}
|
|
4675
|
+
function capitalize(value) {
|
|
4676
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
4677
|
+
}
|
|
4678
|
+
function isImplicitCoreName(kind, name) {
|
|
4679
|
+
return kind === "tool" && name === "route" || kind === "phase" && name === "default";
|
|
4680
|
+
}
|
|
4681
|
+
async function loadDirectoryValues(kind, directory, sourceId) {
|
|
4682
|
+
const root = resolve6(directory);
|
|
4683
|
+
let rootInfo;
|
|
4684
|
+
try {
|
|
4685
|
+
rootInfo = await stat3(root);
|
|
4686
|
+
} catch (error) {
|
|
4687
|
+
if (isMissingPath(error)) {
|
|
4688
|
+
return {
|
|
4689
|
+
values: [],
|
|
4690
|
+
skipped: [{ kind: "missing", sourceId, path: root, message: `Resource directory "${root}" does not exist.` }]
|
|
4691
|
+
};
|
|
4692
|
+
}
|
|
4693
|
+
return {
|
|
4694
|
+
values: [],
|
|
4695
|
+
skipped: [{
|
|
4696
|
+
kind: "invalid",
|
|
4697
|
+
sourceId,
|
|
4698
|
+
path: root,
|
|
4699
|
+
message: `Resource directory "${root}" could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
4700
|
+
}]
|
|
4701
|
+
};
|
|
4702
|
+
}
|
|
4703
|
+
const marker = markerFor(kind);
|
|
4704
|
+
const targets = [];
|
|
4705
|
+
if (rootInfo.isFile()) {
|
|
4706
|
+
targets.push(root);
|
|
4707
|
+
} else if (rootInfo.isDirectory()) {
|
|
4708
|
+
const markerAtRoot = join4(root, marker);
|
|
4709
|
+
try {
|
|
4710
|
+
const markerInfo = await stat3(markerAtRoot);
|
|
4711
|
+
if (markerInfo.isFile()) targets.push(markerAtRoot);
|
|
4712
|
+
} catch (error) {
|
|
4713
|
+
if (!isMissingPath(error)) throw error;
|
|
4714
|
+
let entries;
|
|
4715
|
+
try {
|
|
4716
|
+
entries = await readdir4(root, { withFileTypes: true });
|
|
4717
|
+
} catch (error2) {
|
|
4718
|
+
return {
|
|
4719
|
+
values: [],
|
|
4720
|
+
skipped: [{
|
|
4721
|
+
kind: "invalid",
|
|
4722
|
+
sourceId,
|
|
4723
|
+
path: root,
|
|
4724
|
+
message: `Resource directory "${root}" could not be read: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
4725
|
+
}]
|
|
3965
4726
|
};
|
|
3966
|
-
|
|
4727
|
+
}
|
|
4728
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
4729
|
+
if (!entry.isDirectory()) continue;
|
|
4730
|
+
const target = join4(root, entry.name, marker);
|
|
4731
|
+
try {
|
|
4732
|
+
const targetInfo = await stat3(target);
|
|
4733
|
+
if (targetInfo.isFile()) targets.push(target);
|
|
4734
|
+
} catch (targetError) {
|
|
4735
|
+
if (!isMissingPath(targetError)) throw targetError;
|
|
4736
|
+
}
|
|
3967
4737
|
}
|
|
3968
4738
|
}
|
|
3969
4739
|
}
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
4740
|
+
const values = [];
|
|
4741
|
+
const skipped = [];
|
|
4742
|
+
for (const target of targets) {
|
|
4743
|
+
try {
|
|
4744
|
+
const value = await loadDirectoryValue(kind, target);
|
|
4745
|
+
values.push(value);
|
|
4746
|
+
} catch (error) {
|
|
4747
|
+
skipped.push({
|
|
4748
|
+
kind: "invalid",
|
|
4749
|
+
sourceId,
|
|
4750
|
+
path: target,
|
|
4751
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4752
|
+
});
|
|
4753
|
+
}
|
|
3979
4754
|
}
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4755
|
+
return { values, skipped };
|
|
4756
|
+
}
|
|
4757
|
+
async function loadDirectoryValue(kind, target) {
|
|
4758
|
+
switch (kind) {
|
|
4759
|
+
case "agent": {
|
|
4760
|
+
let loaded;
|
|
4761
|
+
try {
|
|
4762
|
+
loaded = await loadMarkdown(target);
|
|
4763
|
+
} catch (error) {
|
|
4764
|
+
if (error instanceof FrontmatterParseError) {
|
|
4765
|
+
throw new TypeError(`frontmatter could not be parsed: ${error.message}`);
|
|
4766
|
+
}
|
|
4767
|
+
throw error;
|
|
4768
|
+
}
|
|
4769
|
+
return normalizeAgentDefinition(
|
|
4770
|
+
loaded.frontmatter,
|
|
4771
|
+
loaded.body,
|
|
4772
|
+
{ fallbackName: inferResourceName(target, "AGENT.md") }
|
|
4773
|
+
);
|
|
4774
|
+
}
|
|
4775
|
+
case "skill":
|
|
4776
|
+
return await loadSkill(target);
|
|
4777
|
+
case "phase":
|
|
4778
|
+
return await loadPhase(target);
|
|
4779
|
+
case "tool":
|
|
4780
|
+
throw new ResourceRegistryError(
|
|
4781
|
+
"resource_directory_not_supported",
|
|
4782
|
+
"Tool sources accept inline values only."
|
|
4783
|
+
);
|
|
3985
4784
|
}
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
4785
|
+
}
|
|
4786
|
+
function markerFor(kind) {
|
|
4787
|
+
switch (kind) {
|
|
4788
|
+
case "agent":
|
|
4789
|
+
return "AGENT.md";
|
|
4790
|
+
case "skill":
|
|
4791
|
+
return "SKILL.md";
|
|
4792
|
+
case "phase":
|
|
4793
|
+
return "PHASE.md";
|
|
4794
|
+
case "tool":
|
|
4795
|
+
return "";
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
function isMissingPath(error) {
|
|
4799
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
// src/runtime/extension-lifetime.ts
|
|
4803
|
+
var ExtensionLifetimeError = class extends Error {
|
|
4804
|
+
code;
|
|
4805
|
+
constructor(code, message) {
|
|
4806
|
+
super(message);
|
|
4807
|
+
this.name = "ExtensionLifetimeError";
|
|
4808
|
+
this.code = code;
|
|
4809
|
+
}
|
|
4810
|
+
};
|
|
4811
|
+
var RuntimeExtensionLifetime = class {
|
|
4812
|
+
runner;
|
|
4813
|
+
frozen = false;
|
|
4814
|
+
closed = false;
|
|
4815
|
+
constructor(options = {}) {
|
|
4816
|
+
this.runner = createExtensionRunner(options);
|
|
4817
|
+
}
|
|
4818
|
+
async activate(extensions) {
|
|
4819
|
+
if (this.closed) throw new ExtensionLifetimeError("extensions_closed", "The Extension Runtime has been closed.");
|
|
4820
|
+
if (this.frozen) throw new ExtensionLifetimeError("extensions_frozen", "Extensions are frozen until Runtime restart.");
|
|
4821
|
+
const active = [];
|
|
4822
|
+
const errors = [];
|
|
4823
|
+
for (const extension of extensions) {
|
|
4824
|
+
try {
|
|
4825
|
+
await this.runner.loadExtensions([extension]);
|
|
4826
|
+
active.push(extension.name);
|
|
4827
|
+
} catch (error) {
|
|
4828
|
+
errors.push({
|
|
4829
|
+
path: extension.path,
|
|
4830
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4831
|
+
});
|
|
4832
|
+
}
|
|
3989
4833
|
}
|
|
4834
|
+
this.runner.bind();
|
|
4835
|
+
this.frozen = true;
|
|
4836
|
+
return { active, errors };
|
|
3990
4837
|
}
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4838
|
+
tools() {
|
|
4839
|
+
return this.runner.getAllRegisteredTools();
|
|
4840
|
+
}
|
|
4841
|
+
phases() {
|
|
4842
|
+
return this.runner.getPhases();
|
|
4843
|
+
}
|
|
4844
|
+
get extensionRunner() {
|
|
4845
|
+
return this.runner;
|
|
4846
|
+
}
|
|
4847
|
+
async close() {
|
|
4848
|
+
if (this.closed) return;
|
|
4849
|
+
this.closed = true;
|
|
4850
|
+
await this.runner.close();
|
|
4851
|
+
}
|
|
4852
|
+
};
|
|
4853
|
+
var RuntimeBootstrapRegistry = class extends ResourceRegistry {
|
|
4854
|
+
constructor(extensions = new RuntimeExtensionLifetime()) {
|
|
4855
|
+
super();
|
|
4856
|
+
this.extensions = extensions;
|
|
4857
|
+
}
|
|
4858
|
+
extensions;
|
|
4859
|
+
extensionSourceId = "rowan.extensions";
|
|
4860
|
+
/** Install Rowan-owned implicit resources before any host source is read. */
|
|
4861
|
+
async ensureCoreResources() {
|
|
4862
|
+
await this.replaceImplicit("tool", "rowan.core", [createRouteTool([])]);
|
|
4863
|
+
await this.replaceImplicit("phase", "rowan.core", [createDefaultPhase()]);
|
|
4864
|
+
}
|
|
4865
|
+
async loadExtensions(input) {
|
|
4866
|
+
const loaded = Array.isArray(input) ? { extensions: [...input], errors: [] } : (() => {
|
|
4867
|
+
const descriptor = input;
|
|
4868
|
+
return descriptor.directory ? loadExtensionsFromPath(descriptor.directory) : Promise.resolve({ extensions: [...descriptor.values ?? []], errors: [] });
|
|
4869
|
+
})();
|
|
4870
|
+
const normalized = await loaded;
|
|
4871
|
+
const result = await this.extensions.activate(normalized.extensions);
|
|
4872
|
+
await this.replaceImplicit("tool", this.extensionSourceId, this.extensions.tools().map(adaptExtensionTool2));
|
|
4873
|
+
await this.replaceImplicit("phase", this.extensionSourceId, [...this.extensions.phases()]);
|
|
4874
|
+
return {
|
|
4875
|
+
...result,
|
|
4876
|
+
revision: `extensions:${Date.now()}`,
|
|
4877
|
+
registered: result.active,
|
|
4878
|
+
skipped: normalized.errors.map((error) => ({
|
|
4879
|
+
kind: "invalid",
|
|
4880
|
+
sourceId: "rowan.extensions",
|
|
4881
|
+
path: error.path,
|
|
4882
|
+
message: error.error
|
|
4883
|
+
}))
|
|
4884
|
+
};
|
|
4885
|
+
}
|
|
4886
|
+
get extensionRunner() {
|
|
4887
|
+
return this.extensions.extensionRunner;
|
|
4888
|
+
}
|
|
4889
|
+
get extensionTools() {
|
|
4890
|
+
return this.extensions.tools();
|
|
4891
|
+
}
|
|
4892
|
+
get extensionPhases() {
|
|
4893
|
+
return this.extensions.phases();
|
|
4894
|
+
}
|
|
4895
|
+
async closeExtensions() {
|
|
4896
|
+
await this.extensions.close();
|
|
4897
|
+
}
|
|
4898
|
+
};
|
|
4899
|
+
function adaptExtensionTool2(input) {
|
|
4900
|
+
const definition = input.definition;
|
|
4901
|
+
return {
|
|
4902
|
+
name: definition.name,
|
|
4903
|
+
description: definition.description,
|
|
4904
|
+
parameters: definition.parameters,
|
|
4905
|
+
execute: async (args, context, signal) => {
|
|
4906
|
+
const result = await definition.execute(args, signal);
|
|
4907
|
+
const content = JSON.parse(JSON.stringify(result.content));
|
|
4908
|
+
return result.isError ? { ok: false, content, error: "Extension Tool failed." } : { ok: true, content };
|
|
3994
4909
|
}
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
|
|
4913
|
+
// src/runtime/configuration-snapshot.ts
|
|
4914
|
+
function resolveConfigurationSnapshot(registry, input) {
|
|
4915
|
+
const resolved = registry.resolveView(input.resourceView);
|
|
4916
|
+
const base = resolved.agents.find(({ name }) => name === input.definition.name);
|
|
4917
|
+
if (!base) {
|
|
4918
|
+
throw new Error(`Agent Definition "${input.definition.name}" is not available in the Resource View.`);
|
|
4919
|
+
}
|
|
4920
|
+
const layer = input.definition.layer;
|
|
4921
|
+
const definition = applyDefinitionLayer(base, layer);
|
|
4922
|
+
const tools = selectNamedResources(resolved.tools, definition.tools, "Tool");
|
|
4923
|
+
const skills = selectNamedResources(resolved.skills, definition.skills, "Skill");
|
|
4924
|
+
const phases = resolvePhases(resolved.phases, definition.phases);
|
|
4925
|
+
const contexts = selectNamedResources(input.contexts ?? [], base.context, "Context");
|
|
4926
|
+
return {
|
|
4927
|
+
identity: input.identity,
|
|
4928
|
+
definition,
|
|
4929
|
+
resources: {
|
|
4930
|
+
tools,
|
|
4931
|
+
skills,
|
|
4932
|
+
...phases ? { phases } : {},
|
|
4933
|
+
revisions: resolved.revisions,
|
|
4934
|
+
refs: {
|
|
4935
|
+
agent: selectedRefs(resolved.refs.agent, [base.name]),
|
|
4936
|
+
tool: selectedRefs(resolved.refs.tool, tools.map(({ name }) => name)),
|
|
4937
|
+
skill: selectedRefs(resolved.refs.skill, skills.map(({ name }) => name)),
|
|
4938
|
+
phase: selectedRefs(resolved.refs.phase, [...phases?.phases.keys() ?? []])
|
|
4939
|
+
}
|
|
4940
|
+
},
|
|
4941
|
+
contexts,
|
|
4942
|
+
resourceView: input.resourceView,
|
|
4943
|
+
...input.cwd === void 0 ? {} : { cwd: input.cwd },
|
|
4944
|
+
...input.maxAttempts === void 0 ? {} : { maxAttempts: input.maxAttempts },
|
|
4945
|
+
..."stream" in input && input.stream ? { model: layer?.model ?? input.model, stream: input.stream } : { model: layer?.model ?? input.model }
|
|
4946
|
+
};
|
|
4947
|
+
}
|
|
4948
|
+
function materializeConfigurationSnapshot(snapshot) {
|
|
4949
|
+
return {
|
|
4950
|
+
identity: snapshot.identity,
|
|
4951
|
+
definition: snapshot.definition,
|
|
4952
|
+
resources: {
|
|
4953
|
+
tools: snapshot.resources.tools,
|
|
4954
|
+
skills: snapshot.resources.skills,
|
|
4955
|
+
...snapshot.resources.phases ? { phases: snapshot.resources.phases } : {},
|
|
4956
|
+
...snapshot.contexts.length > 0 ? { contexts: snapshot.contexts } : {},
|
|
4957
|
+
resourceView: snapshot.resourceView,
|
|
4958
|
+
resourceRefs: [
|
|
4959
|
+
...snapshot.resources.refs.agent,
|
|
4960
|
+
...snapshot.resources.refs.tool,
|
|
4961
|
+
...snapshot.resources.refs.skill,
|
|
4962
|
+
...snapshot.resources.refs.phase
|
|
4963
|
+
],
|
|
4964
|
+
resourceRevisions: snapshot.resources.revisions
|
|
4965
|
+
},
|
|
4966
|
+
...snapshot.cwd === void 0 ? {} : { cwd: snapshot.cwd },
|
|
4967
|
+
...snapshot.maxAttempts === void 0 ? {} : { maxAttempts: snapshot.maxAttempts },
|
|
4968
|
+
..."stream" in snapshot && snapshot.stream ? { model: snapshot.model, stream: snapshot.stream } : { model: snapshot.model }
|
|
4969
|
+
};
|
|
4970
|
+
}
|
|
4971
|
+
function applyDefinitionLayer(base, layer) {
|
|
4972
|
+
if (!layer) return { ...base };
|
|
4973
|
+
return {
|
|
4974
|
+
...base,
|
|
4975
|
+
...layer.description === void 0 ? {} : { description: layer.description },
|
|
4976
|
+
...layer.content === void 0 ? {} : { content: layer.content },
|
|
4977
|
+
...layer.model === void 0 ? {} : { model: layer.model },
|
|
4978
|
+
...layer.tools === void 0 ? {} : { tools: intersectNames(base.tools, layer.tools, "Tool") },
|
|
4979
|
+
...layer.skills === void 0 ? {} : { skills: intersectNames(base.skills, layer.skills, "Skill") },
|
|
4980
|
+
...layer.phases === void 0 ? {} : { phases: intersectPhaseSelection(base.phases, layer.phases) }
|
|
4981
|
+
};
|
|
4982
|
+
}
|
|
4983
|
+
function intersectNames(parent, layer, kind) {
|
|
4984
|
+
if (parent === void 0) return [...layer];
|
|
4985
|
+
const parentNames = new Set(parent);
|
|
4986
|
+
for (const name of layer) {
|
|
4987
|
+
if (!parentNames.has(name)) console.warn(`${kind} "${name}" is not available and will be skipped.`);
|
|
3995
4988
|
}
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4989
|
+
return layer.filter((name) => parentNames.has(name));
|
|
4990
|
+
}
|
|
4991
|
+
function intersectPhaseSelection(parent, layer) {
|
|
4992
|
+
if (!parent) return layer;
|
|
4993
|
+
const parentNames = new Set(parent.phaseIds);
|
|
4994
|
+
for (const name of layer.phaseIds) {
|
|
4995
|
+
if (!parentNames.has(name)) console.warn(`Phase "${name}" is not available and will be skipped.`);
|
|
4001
4996
|
}
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4997
|
+
return {
|
|
4998
|
+
entryPhaseId: layer.entryPhaseId,
|
|
4999
|
+
phaseIds: layer.phaseIds.filter((name) => parentNames.has(name))
|
|
5000
|
+
};
|
|
5001
|
+
}
|
|
5002
|
+
function resolvePhases(candidates, selection) {
|
|
5003
|
+
const selected = selectNamedResources(candidates, selection?.phaseIds, "Phase");
|
|
5004
|
+
if (selected.length === 0 && selection === void 0) return void 0;
|
|
5005
|
+
const entryPhaseId = selection?.entryPhaseId ?? null;
|
|
5006
|
+
if (entryPhaseId !== null && !selected.some(({ name }) => name === entryPhaseId)) {
|
|
5007
|
+
console.warn(`Phase entry "${entryPhaseId}" is not available and will be skipped.`);
|
|
5008
|
+
return { phases: new Map(selected.map((phase) => [phase.name, phase])), entryPhaseId: null };
|
|
4007
5009
|
}
|
|
4008
|
-
|
|
5010
|
+
return {
|
|
5011
|
+
phases: new Map(selected.map((phase) => [phase.name, phase])),
|
|
5012
|
+
entryPhaseId
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
function selectedRefs(refs, names) {
|
|
5016
|
+
const selected = new Set(names);
|
|
5017
|
+
return refs.filter(({ name }) => selected.has(name));
|
|
5018
|
+
}
|
|
4009
5019
|
|
|
4010
5020
|
// src/runtime/durable-runtime.ts
|
|
4011
5021
|
var DEFAULT_CONCURRENCY = 10;
|
|
@@ -4022,14 +5032,16 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4022
5032
|
executions = /* @__PURE__ */ new Map();
|
|
4023
5033
|
consumers = /* @__PURE__ */ new Map();
|
|
4024
5034
|
transientEvents = new TransientRunEventHub();
|
|
5035
|
+
resources;
|
|
4025
5036
|
heartbeat;
|
|
4026
5037
|
pumping = false;
|
|
4027
5038
|
closed = false;
|
|
4028
|
-
constructor(options, owned) {
|
|
5039
|
+
constructor(options, owned, resources) {
|
|
4029
5040
|
this.owned = owned;
|
|
4030
5041
|
this.commands = new ConfigCommandService(owned, options.configs, String(owned.lease.token).split(":")[0]);
|
|
4031
5042
|
this.storeIncarnation = String(owned.lease.token).split(":")[0];
|
|
4032
5043
|
this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
5044
|
+
this.resources = resources;
|
|
4033
5045
|
}
|
|
4034
5046
|
static async init(options) {
|
|
4035
5047
|
if (!options.store) throw new TypeError("AgentRuntime requires a DurableStore");
|
|
@@ -4037,10 +5049,38 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4037
5049
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
4038
5050
|
if (!Number.isInteger(concurrency) || concurrency <= 0) throw new TypeError("concurrency must be a positive integer");
|
|
4039
5051
|
const owned = await options.store.openOwner({ ownerId: createId("owner"), leaseMs: OWNER_LEASE_MS });
|
|
4040
|
-
const runtime = new _AgentRuntime({ ...options, configs, concurrency }, owned);
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
5052
|
+
const runtime = new _AgentRuntime({ ...options, configs, concurrency }, owned, new RuntimeBootstrapRegistry());
|
|
5053
|
+
try {
|
|
5054
|
+
await runtime.resources.ensureCoreResources();
|
|
5055
|
+
await options.bootstrap?.(runtime.resources);
|
|
5056
|
+
runtime.startHeartbeat();
|
|
5057
|
+
void runtime.pump();
|
|
5058
|
+
return runtime;
|
|
5059
|
+
} catch (error) {
|
|
5060
|
+
await runtime.resources.closeExtensions().catch(() => void 0);
|
|
5061
|
+
await owned.sealAndReleaseOwner().catch(() => void 0);
|
|
5062
|
+
throw error;
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
async loadAgents(input) {
|
|
5066
|
+
this.assertOpen();
|
|
5067
|
+
return this.resources.loadAgents(input);
|
|
5068
|
+
}
|
|
5069
|
+
async loadSkills(input) {
|
|
5070
|
+
this.assertOpen();
|
|
5071
|
+
return this.resources.loadSkills(input);
|
|
5072
|
+
}
|
|
5073
|
+
async loadPhases(input) {
|
|
5074
|
+
this.assertOpen();
|
|
5075
|
+
return this.resources.loadPhases(input);
|
|
5076
|
+
}
|
|
5077
|
+
async loadTools(input) {
|
|
5078
|
+
this.assertOpen();
|
|
5079
|
+
return this.resources.loadTools(input);
|
|
5080
|
+
}
|
|
5081
|
+
async unload(input) {
|
|
5082
|
+
this.assertOpen();
|
|
5083
|
+
return this.resources.unload(input);
|
|
4044
5084
|
}
|
|
4045
5085
|
async createAgent(config, options = {}) {
|
|
4046
5086
|
this.assertOpen();
|
|
@@ -4100,6 +5140,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4100
5140
|
this.executions.clear();
|
|
4101
5141
|
this.transientEvents.close();
|
|
4102
5142
|
for (const subscription of this.consumers.values()) subscription.controller.abort();
|
|
5143
|
+
await this.resources.closeExtensions();
|
|
4103
5144
|
await this.owned.sealAndReleaseOwner();
|
|
4104
5145
|
}
|
|
4105
5146
|
async snapshot(runId) {
|
|
@@ -4157,7 +5198,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4157
5198
|
let executionRevision = run.revision;
|
|
4158
5199
|
try {
|
|
4159
5200
|
const agent = await this.requireAgent(run.agentId);
|
|
4160
|
-
|
|
5201
|
+
let token = run.pinnedConfigToken ?? agent.currentConfigToken;
|
|
4161
5202
|
if (!token) {
|
|
4162
5203
|
await this.failQueued(run, "Agent has no Config Token.");
|
|
4163
5204
|
return;
|
|
@@ -4177,13 +5218,28 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4177
5218
|
await this.failQueued(run, resolution.reason);
|
|
4178
5219
|
return;
|
|
4179
5220
|
}
|
|
5221
|
+
let resolvedConfig = resolution.config;
|
|
5222
|
+
if (isAgentConfiguration(resolvedConfig)) {
|
|
5223
|
+
try {
|
|
5224
|
+
const snapshot = this.materializeConfig(resolvedConfig);
|
|
5225
|
+
token = await this.commands.storeSnapshot({
|
|
5226
|
+
agent,
|
|
5227
|
+
config: snapshot,
|
|
5228
|
+
operationId: `run-snapshot:${run.id}`
|
|
5229
|
+
});
|
|
5230
|
+
resolvedConfig = snapshot;
|
|
5231
|
+
} catch (error) {
|
|
5232
|
+
await this.failQueued(run, error instanceof Error ? error.message : "Configuration Snapshot failed.");
|
|
5233
|
+
return;
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
4180
5236
|
const executionId = createId("exec");
|
|
4181
5237
|
claim = await this.owned.claimRun({ runId: run.id, expectedRevision: run.revision, executionId, configToken: token });
|
|
4182
5238
|
executionRevision = claim.run.revision;
|
|
4183
5239
|
const controller = new AbortController();
|
|
4184
5240
|
this.executions.set(run.id, { controller, executionId });
|
|
4185
|
-
const config =
|
|
4186
|
-
const assembly =
|
|
5241
|
+
const config = resolvedConfig;
|
|
5242
|
+
const assembly = assembleRegisteredExtensions(config, this.resources.extensionRunner);
|
|
4187
5243
|
let toolQueue = Promise.resolve();
|
|
4188
5244
|
const model = "stream" in config && config.stream ? config.model : { provider: config.model.provider, id: config.model.id };
|
|
4189
5245
|
const stream = "stream" in config && config.stream ? config.stream : createModelStream(config.model);
|
|
@@ -4207,7 +5263,9 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4207
5263
|
execution: {
|
|
4208
5264
|
agentId: run.agentId,
|
|
4209
5265
|
runId: run.id,
|
|
4210
|
-
executionId: claim.execution.executionId
|
|
5266
|
+
executionId: claim.execution.executionId,
|
|
5267
|
+
...agent.metadata === void 0 ? {} : { agentMetadata: agent.metadata },
|
|
5268
|
+
...run.metadata === void 0 ? {} : { runMetadata: run.metadata }
|
|
4211
5269
|
},
|
|
4212
5270
|
model,
|
|
4213
5271
|
stream,
|
|
@@ -4236,6 +5294,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4236
5294
|
const task = toolQueue.then(async () => {
|
|
4237
5295
|
const execution = await this.executeToolBatch({
|
|
4238
5296
|
run,
|
|
5297
|
+
agentMetadata: agent.metadata,
|
|
4239
5298
|
execution: claim.execution,
|
|
4240
5299
|
expectedRevision: executionRevision,
|
|
4241
5300
|
toolConfig: executionTools,
|
|
@@ -4252,6 +5311,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4252
5311
|
const task = toolQueue.then(async () => {
|
|
4253
5312
|
const execution = await this.executeToolBatch({
|
|
4254
5313
|
run,
|
|
5314
|
+
agentMetadata: agent.metadata,
|
|
4255
5315
|
execution: claim.execution,
|
|
4256
5316
|
expectedRevision: executionRevision,
|
|
4257
5317
|
toolConfig: executionTools,
|
|
@@ -4346,6 +5406,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4346
5406
|
const context = {
|
|
4347
5407
|
agentId: input.run.agentId,
|
|
4348
5408
|
runId: input.run.id,
|
|
5409
|
+
...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
|
|
5410
|
+
...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
|
|
4349
5411
|
toolCallId,
|
|
4350
5412
|
reportProgress: (progress) => {
|
|
4351
5413
|
const active = this.executions.get(input.run.id);
|
|
@@ -4489,6 +5551,10 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4489
5551
|
assertOpen() {
|
|
4490
5552
|
if (this.closed) throw new RuntimeError("runtime_closed", null);
|
|
4491
5553
|
}
|
|
5554
|
+
materializeConfig(config) {
|
|
5555
|
+
if (!isAgentConfiguration(config)) return config;
|
|
5556
|
+
return materializeConfigurationSnapshot(resolveConfigurationSnapshot(this.resources, config));
|
|
5557
|
+
}
|
|
4492
5558
|
startHeartbeat() {
|
|
4493
5559
|
this.heartbeat = setInterval(() => {
|
|
4494
5560
|
void this.owned.renewOwner(OWNER_LEASE_MS).catch(() => {
|
|
@@ -4552,10 +5618,10 @@ function boundaryFromSnapshot(snapshot) {
|
|
|
4552
5618
|
}
|
|
4553
5619
|
}
|
|
4554
5620
|
function delay(ms) {
|
|
4555
|
-
return new Promise((
|
|
5621
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
4556
5622
|
}
|
|
4557
5623
|
function normalizePhaseRegistry(registry) {
|
|
4558
|
-
if (registry?.phases.has(DEFAULT_PHASE_ID)) {
|
|
5624
|
+
if (registry?.phases.has(DEFAULT_PHASE_ID) && registry.phases.get(DEFAULT_PHASE_ID)?.filePath !== "") {
|
|
4559
5625
|
throw new TypeError(`Configured Phase collides with Rowan built-in Phase "${DEFAULT_PHASE_ID}".`);
|
|
4560
5626
|
}
|
|
4561
5627
|
return {
|
|
@@ -4579,13 +5645,13 @@ function toJsonValue(value) {
|
|
|
4579
5645
|
return value;
|
|
4580
5646
|
}
|
|
4581
5647
|
function deferred2() {
|
|
4582
|
-
let
|
|
5648
|
+
let resolve7;
|
|
4583
5649
|
let reject;
|
|
4584
5650
|
const promise = new Promise((res, rej) => {
|
|
4585
|
-
|
|
5651
|
+
resolve7 = (value) => res(value);
|
|
4586
5652
|
reject = rej;
|
|
4587
5653
|
});
|
|
4588
|
-
return { promise, resolve:
|
|
5654
|
+
return { promise, resolve: resolve7, reject };
|
|
4589
5655
|
}
|
|
4590
5656
|
|
|
4591
5657
|
// src/runtime/durable-store.ts
|
|
@@ -6036,236 +7102,6 @@ function assertOwnerInput(ownerId, leaseMs) {
|
|
|
6036
7102
|
if (!Number.isFinite(leaseMs) || leaseMs <= 0) throw new TypeError("leaseMs must be a positive finite number");
|
|
6037
7103
|
}
|
|
6038
7104
|
|
|
6039
|
-
// src/harness/skills.ts
|
|
6040
|
-
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
6041
|
-
import { readdir as readdir2 } from "fs/promises";
|
|
6042
|
-
import { dirname as dirname4, join as join2, resolve as resolve4 } from "path";
|
|
6043
|
-
var SKILL_MARKER = "SKILL.md";
|
|
6044
|
-
function resolveSkillPath(input) {
|
|
6045
|
-
const resolved = resolve4(input);
|
|
6046
|
-
return existsSync2(resolved) && statSync2(resolved).isDirectory() ? join2(resolved, SKILL_MARKER) : resolved;
|
|
6047
|
-
}
|
|
6048
|
-
async function loadSkill(path) {
|
|
6049
|
-
const resolved = resolveSkillPath(path);
|
|
6050
|
-
let loaded;
|
|
6051
|
-
try {
|
|
6052
|
-
loaded = await loadMarkdown(resolved);
|
|
6053
|
-
} catch (error) {
|
|
6054
|
-
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
6055
|
-
warnResourceDiagnostics("skill", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
6056
|
-
throw new ResourceMetadataError("parse_failed", error.message);
|
|
6057
|
-
}
|
|
6058
|
-
const { frontmatter, body } = loaded;
|
|
6059
|
-
const metadata = frontmatter;
|
|
6060
|
-
const id = inferResourceName(resolved, SKILL_MARKER);
|
|
6061
|
-
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
6062
|
-
const name = frontmatterName || id;
|
|
6063
|
-
const description = validateDescription(metadata.description);
|
|
6064
|
-
const diagnostics = validateResourceId(id);
|
|
6065
|
-
if (frontmatterName && frontmatterName !== id) {
|
|
6066
|
-
diagnostics.push(...validateResourceName(frontmatterName, id));
|
|
6067
|
-
}
|
|
6068
|
-
diagnostics.push(...description.warnings);
|
|
6069
|
-
warnResourceDiagnostics("skill", resolved, diagnostics);
|
|
6070
|
-
if (description.missing) {
|
|
6071
|
-
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
6072
|
-
}
|
|
6073
|
-
return {
|
|
6074
|
-
name,
|
|
6075
|
-
description: description.description,
|
|
6076
|
-
filePath: resolved,
|
|
6077
|
-
baseDir: dirname4(resolved),
|
|
6078
|
-
content: body,
|
|
6079
|
-
disableModelInvocation: metadata["disable-model-invocation"] === true
|
|
6080
|
-
};
|
|
6081
|
-
}
|
|
6082
|
-
async function loadSkills(targetPath) {
|
|
6083
|
-
const skillsDir = resolve4(targetPath);
|
|
6084
|
-
if (existsSync2(skillsDir) && statSync2(skillsDir).isFile()) {
|
|
6085
|
-
try {
|
|
6086
|
-
return [await loadSkill(skillsDir)];
|
|
6087
|
-
} catch (error) {
|
|
6088
|
-
if (error instanceof ResourceMetadataError) return [];
|
|
6089
|
-
throw error;
|
|
6090
|
-
}
|
|
6091
|
-
}
|
|
6092
|
-
if (existsSync2(join2(skillsDir, SKILL_MARKER))) {
|
|
6093
|
-
try {
|
|
6094
|
-
return [await loadSkill(skillsDir)];
|
|
6095
|
-
} catch (error) {
|
|
6096
|
-
if (error instanceof ResourceMetadataError) return [];
|
|
6097
|
-
throw error;
|
|
6098
|
-
}
|
|
6099
|
-
}
|
|
6100
|
-
const entries = await readdir2(skillsDir, { withFileTypes: true });
|
|
6101
|
-
const skills = [];
|
|
6102
|
-
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6103
|
-
if (!entry.isDirectory()) continue;
|
|
6104
|
-
const skillFile = join2(skillsDir, entry.name, SKILL_MARKER);
|
|
6105
|
-
if (!existsSync2(skillFile)) continue;
|
|
6106
|
-
try {
|
|
6107
|
-
const skill = await loadSkill(skillFile);
|
|
6108
|
-
skills.push(skill);
|
|
6109
|
-
} catch (error) {
|
|
6110
|
-
if (error instanceof ResourceMetadataError) continue;
|
|
6111
|
-
console.warn(`Failed to load skill "${entry.name}":`, error);
|
|
6112
|
-
}
|
|
6113
|
-
}
|
|
6114
|
-
return skills;
|
|
6115
|
-
}
|
|
6116
|
-
|
|
6117
|
-
// src/extensions/loader.ts
|
|
6118
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
6119
|
-
import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
6120
|
-
import { fileURLToPath } from "url";
|
|
6121
|
-
import { basename as basename3, dirname as dirname5, extname as extname2, join as join3, resolve as resolve5 } from "path";
|
|
6122
|
-
import { createJiti } from "jiti";
|
|
6123
|
-
function isExtensionFile(path) {
|
|
6124
|
-
const extension = extname2(path).toLowerCase();
|
|
6125
|
-
return extension === ".ts" || extension === ".js";
|
|
6126
|
-
}
|
|
6127
|
-
function isSyntheticPath(path) {
|
|
6128
|
-
return path.startsWith("<") && path.endsWith(">");
|
|
6129
|
-
}
|
|
6130
|
-
function readManifestSync(dir) {
|
|
6131
|
-
const manifestPath = join3(dir, "package.json");
|
|
6132
|
-
if (!existsSync3(manifestPath)) {
|
|
6133
|
-
return void 0;
|
|
6134
|
-
}
|
|
6135
|
-
try {
|
|
6136
|
-
const content = readFileSync(manifestPath, "utf8");
|
|
6137
|
-
const pkg = JSON.parse(content);
|
|
6138
|
-
const rowan = pkg.rowan;
|
|
6139
|
-
if (!rowan) return void 0;
|
|
6140
|
-
return {
|
|
6141
|
-
entry: rowan.extensions?.[0],
|
|
6142
|
-
phase: rowan.phase
|
|
6143
|
-
};
|
|
6144
|
-
} catch {
|
|
6145
|
-
return void 0;
|
|
6146
|
-
}
|
|
6147
|
-
}
|
|
6148
|
-
function jitiAliases() {
|
|
6149
|
-
const moduleUrl = import.meta.url;
|
|
6150
|
-
if (!moduleUrl) {
|
|
6151
|
-
return {};
|
|
6152
|
-
}
|
|
6153
|
-
const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
|
|
6154
|
-
if (!existsSync3(agentSourcePath)) {
|
|
6155
|
-
return {};
|
|
6156
|
-
}
|
|
6157
|
-
return { "@rowan-agent/agent": agentSourcePath };
|
|
6158
|
-
}
|
|
6159
|
-
var sharedJiti;
|
|
6160
|
-
function getJiti() {
|
|
6161
|
-
sharedJiti ??= createJiti(import.meta.url || process.cwd(), {
|
|
6162
|
-
fsCache: false,
|
|
6163
|
-
moduleCache: false,
|
|
6164
|
-
tryNative: false,
|
|
6165
|
-
alias: jitiAliases()
|
|
6166
|
-
});
|
|
6167
|
-
return sharedJiti;
|
|
6168
|
-
}
|
|
6169
|
-
async function loadExtensionModule(extensionPath) {
|
|
6170
|
-
const jiti = getJiti();
|
|
6171
|
-
const module = await jiti.import(extensionPath, { default: true });
|
|
6172
|
-
return typeof module === "function" ? module : void 0;
|
|
6173
|
-
}
|
|
6174
|
-
async function readPackageManifest(path) {
|
|
6175
|
-
if (!existsSync3(path)) {
|
|
6176
|
-
return void 0;
|
|
6177
|
-
}
|
|
6178
|
-
const content = await readFile3(path, "utf8");
|
|
6179
|
-
return JSON.parse(content);
|
|
6180
|
-
}
|
|
6181
|
-
async function resolveExtensionEntries(dir) {
|
|
6182
|
-
const manifest = await readPackageManifest(join3(dir, "package.json"));
|
|
6183
|
-
const declared = manifest?.rowan?.extensions;
|
|
6184
|
-
if (declared && declared.length > 0) {
|
|
6185
|
-
return declared.map((entry) => resolve5(dir, entry));
|
|
6186
|
-
}
|
|
6187
|
-
for (const name of ["index.ts", "index.js"]) {
|
|
6188
|
-
const entry = join3(dir, name);
|
|
6189
|
-
if (existsSync3(entry)) {
|
|
6190
|
-
return [entry];
|
|
6191
|
-
}
|
|
6192
|
-
}
|
|
6193
|
-
return void 0;
|
|
6194
|
-
}
|
|
6195
|
-
async function discoverExtensionsInDir(dir) {
|
|
6196
|
-
const entries = await readdir3(dir, { withFileTypes: true }).catch((error) => {
|
|
6197
|
-
if (error.code === "ENOENT") {
|
|
6198
|
-
return [];
|
|
6199
|
-
}
|
|
6200
|
-
throw error;
|
|
6201
|
-
});
|
|
6202
|
-
const paths = [];
|
|
6203
|
-
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6204
|
-
const entryPath = join3(dir, entry.name);
|
|
6205
|
-
if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
|
|
6206
|
-
paths.push(entryPath);
|
|
6207
|
-
continue;
|
|
6208
|
-
}
|
|
6209
|
-
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
6210
|
-
const info = entry.isSymbolicLink() ? await stat2(entryPath).catch(() => void 0) : void 0;
|
|
6211
|
-
if (entry.isDirectory() || info?.isDirectory()) {
|
|
6212
|
-
const resolvedEntries = await resolveExtensionEntries(entryPath);
|
|
6213
|
-
if (resolvedEntries) {
|
|
6214
|
-
paths.push(...resolvedEntries);
|
|
6215
|
-
}
|
|
6216
|
-
}
|
|
6217
|
-
}
|
|
6218
|
-
}
|
|
6219
|
-
return paths;
|
|
6220
|
-
}
|
|
6221
|
-
function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
|
|
6222
|
-
const resolvedCwd = resolve5(cwd);
|
|
6223
|
-
const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve5(resolvedCwd, extensionPath);
|
|
6224
|
-
const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname5(resolvedPath)) || "unknown";
|
|
6225
|
-
const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname5(resolvedPath);
|
|
6226
|
-
const manifest = readManifestSync(manifestDir);
|
|
6227
|
-
return {
|
|
6228
|
-
path: resolvedPath,
|
|
6229
|
-
name,
|
|
6230
|
-
factory,
|
|
6231
|
-
manifest
|
|
6232
|
-
};
|
|
6233
|
-
}
|
|
6234
|
-
async function loadExtensions(paths, cwd) {
|
|
6235
|
-
const extensions = [];
|
|
6236
|
-
const errors = [];
|
|
6237
|
-
const resolvedCwd = resolve5(cwd);
|
|
6238
|
-
for (const path of paths) {
|
|
6239
|
-
const resolvedPath = resolve5(resolvedCwd, path);
|
|
6240
|
-
try {
|
|
6241
|
-
const factory = await loadExtensionModule(resolvedPath);
|
|
6242
|
-
if (!factory) {
|
|
6243
|
-
errors.push({
|
|
6244
|
-
path: resolvedPath,
|
|
6245
|
-
error: `Extension does not export a valid factory function: ${path}`
|
|
6246
|
-
});
|
|
6247
|
-
continue;
|
|
6248
|
-
}
|
|
6249
|
-
extensions.push(loadExtensionFromFactory(factory, resolvedCwd, resolvedPath));
|
|
6250
|
-
} catch (error) {
|
|
6251
|
-
errors.push({
|
|
6252
|
-
path: resolvedPath,
|
|
6253
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6254
|
-
});
|
|
6255
|
-
}
|
|
6256
|
-
}
|
|
6257
|
-
return { extensions, errors };
|
|
6258
|
-
}
|
|
6259
|
-
async function loadExtensionsFromPath(path) {
|
|
6260
|
-
const resolvedPath = resolve5(path);
|
|
6261
|
-
const info = await stat2(resolvedPath);
|
|
6262
|
-
if (!info.isDirectory()) {
|
|
6263
|
-
return loadExtensions([resolvedPath], dirname5(resolvedPath));
|
|
6264
|
-
}
|
|
6265
|
-
const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
|
|
6266
|
-
return loadExtensions(entries, resolvedPath);
|
|
6267
|
-
}
|
|
6268
|
-
|
|
6269
7105
|
// src/index.ts
|
|
6270
7106
|
function createCoreTools2(input = {}) {
|
|
6271
7107
|
return createCoreTools(input).map((tool) => ({
|
|
@@ -6280,9 +7116,14 @@ function createCoreTools2(input = {}) {
|
|
|
6280
7116
|
}
|
|
6281
7117
|
export {
|
|
6282
7118
|
AgentRuntime,
|
|
7119
|
+
ExtensionLifetimeError,
|
|
6283
7120
|
InMemoryConfigProvider,
|
|
6284
7121
|
InMemoryStore,
|
|
7122
|
+
ResourceRegistry,
|
|
7123
|
+
ResourceRegistryError,
|
|
7124
|
+
RuntimeBootstrapRegistry,
|
|
6285
7125
|
RuntimeError,
|
|
7126
|
+
RuntimeExtensionLifetime,
|
|
6286
7127
|
SqliteStore,
|
|
6287
7128
|
brandConfigToken,
|
|
6288
7129
|
createCoreTools2 as createCoreTools,
|
|
@@ -6290,6 +7131,8 @@ export {
|
|
|
6290
7131
|
loadExtensionsFromPath as loadExtensions,
|
|
6291
7132
|
loadPhases,
|
|
6292
7133
|
loadSkills,
|
|
7134
|
+
materializeConfigurationSnapshot,
|
|
6293
7135
|
parseAgentDefinition,
|
|
6294
|
-
parseFrontmatter
|
|
7136
|
+
parseFrontmatter,
|
|
7137
|
+
resolveConfigurationSnapshot
|
|
6295
7138
|
};
|