@rowan-agent/agent 0.15.5 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/index.d.ts +150 -2
- package/dist/index.js +600 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -925,12 +925,15 @@ function createCoreTools(input = {}) {
|
|
|
925
925
|
}
|
|
926
926
|
|
|
927
927
|
// src/runtime/core-tools.ts
|
|
928
|
+
var CORE_TOOL_NAMES = /* @__PURE__ */ new Set(["read", "edit", "write", "bash"]);
|
|
928
929
|
function createRuntimeCoreTools(input = {}) {
|
|
929
930
|
return createCoreTools(input).map((tool) => ({
|
|
930
931
|
name: tool.name,
|
|
931
932
|
core: true,
|
|
932
933
|
description: tool.description,
|
|
933
934
|
parameters: tool.parameters,
|
|
935
|
+
...tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {},
|
|
936
|
+
...tool.promptGuidelines ? { promptGuidelines: tool.promptGuidelines } : {},
|
|
934
937
|
execute: async (args, context, signal) => {
|
|
935
938
|
const result = await tool.execute(args, { skills: [], toolCallId: context.toolCallId }, signal);
|
|
936
939
|
return result.ok ? { ok: true, content: result.content } : { ok: false, content: result.content, error: result.error ?? "Tool failed." };
|
|
@@ -1247,6 +1250,56 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
1247
1250
|
assertActive();
|
|
1248
1251
|
hooks?.off(eventType, handler);
|
|
1249
1252
|
},
|
|
1253
|
+
config: {
|
|
1254
|
+
get: async (scope) => {
|
|
1255
|
+
assertActive();
|
|
1256
|
+
return await options?.config?.get(scope) ?? null;
|
|
1257
|
+
},
|
|
1258
|
+
changed: (handler) => {
|
|
1259
|
+
assertActive();
|
|
1260
|
+
options?.config?.changed(handler);
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
state: {
|
|
1264
|
+
run: (runId) => {
|
|
1265
|
+
assertActive();
|
|
1266
|
+
const store = options?.state?.run(runId);
|
|
1267
|
+
return {
|
|
1268
|
+
get: async (key) => {
|
|
1269
|
+
assertActive();
|
|
1270
|
+
return store?.get(key);
|
|
1271
|
+
},
|
|
1272
|
+
set: async (key, value) => {
|
|
1273
|
+
assertActive();
|
|
1274
|
+
assertJsonValue(value);
|
|
1275
|
+
return store?.set(key, value);
|
|
1276
|
+
},
|
|
1277
|
+
delete: async (key) => {
|
|
1278
|
+
assertActive();
|
|
1279
|
+
return store?.delete(key);
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
},
|
|
1283
|
+
agent: (agentId) => {
|
|
1284
|
+
assertActive();
|
|
1285
|
+
const store = options?.state?.agent(agentId);
|
|
1286
|
+
return {
|
|
1287
|
+
get: async (key) => {
|
|
1288
|
+
assertActive();
|
|
1289
|
+
return store?.get(key);
|
|
1290
|
+
},
|
|
1291
|
+
set: async (key, value) => {
|
|
1292
|
+
assertActive();
|
|
1293
|
+
assertJsonValue(value);
|
|
1294
|
+
return store?.set(key, value);
|
|
1295
|
+
},
|
|
1296
|
+
delete: async (key) => {
|
|
1297
|
+
assertActive();
|
|
1298
|
+
return store?.delete(key);
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
},
|
|
1250
1303
|
tool: {
|
|
1251
1304
|
register: (tool) => {
|
|
1252
1305
|
assertActive();
|
|
@@ -1257,6 +1310,25 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
1257
1310
|
options?.unregisterTool?.(toolName);
|
|
1258
1311
|
}
|
|
1259
1312
|
},
|
|
1313
|
+
capabilities: {
|
|
1314
|
+
contribute: (contribution) => {
|
|
1315
|
+
assertActive();
|
|
1316
|
+
if (!contribution || typeof contribution !== "object") {
|
|
1317
|
+
throw new TypeError("Capability contribution must be an object.");
|
|
1318
|
+
}
|
|
1319
|
+
if (contribution.kind !== "tool") {
|
|
1320
|
+
throw new TypeError(`Unsupported capability contribution kind: "${contribution.kind}".`);
|
|
1321
|
+
}
|
|
1322
|
+
if (typeof contribution.name !== "string" || contribution.name.trim() === "") {
|
|
1323
|
+
throw new TypeError("Capability contribution requires a non-empty name.");
|
|
1324
|
+
}
|
|
1325
|
+
if (typeof contribution.description !== "string") {
|
|
1326
|
+
throw new TypeError("Capability contribution requires a description string.");
|
|
1327
|
+
}
|
|
1328
|
+
return options?.contributeCapability ? options.contributeCapability(contribution) : () => {
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1260
1332
|
registerProvider: (config) => {
|
|
1261
1333
|
assertActive();
|
|
1262
1334
|
options?.registerProvider?.(config);
|
|
@@ -1941,6 +2013,7 @@ async function loadPhaseCode(codePath) {
|
|
|
1941
2013
|
var DEFAULT_PHASE_ID = "default";
|
|
1942
2014
|
var STOP_PHASE_ID = "stop";
|
|
1943
2015
|
var COMPACT_PHASE_ID = "compact";
|
|
2016
|
+
var CORE_PHASE_NAMES = /* @__PURE__ */ new Set([DEFAULT_PHASE_ID, STOP_PHASE_ID, COMPACT_PHASE_ID]);
|
|
1944
2017
|
function createDefaultPhase() {
|
|
1945
2018
|
return {
|
|
1946
2019
|
name: DEFAULT_PHASE_ID,
|
|
@@ -1993,7 +2066,7 @@ function createCompactPhase() {
|
|
|
1993
2066
|
kind: "compacting",
|
|
1994
2067
|
message: "Compacting conversation context."
|
|
1995
2068
|
});
|
|
1996
|
-
const tools = context.tools.filter((tool) => tool.
|
|
2069
|
+
const tools = context.tools.filter((tool) => tool.name === "read" || tool.name === "bash");
|
|
1997
2070
|
const working = { ...context, tools, skills: [...context.skills], messages: [...context.messages] };
|
|
1998
2071
|
const instructions = compactInstructions(context);
|
|
1999
2072
|
if (instructions) {
|
|
@@ -3731,7 +3804,7 @@ function resolveConfigurationSnapshot(registry, input) {
|
|
|
3731
3804
|
const selectedContexts = selectNamedResources(input.contexts ?? [], base.contexts, "Context");
|
|
3732
3805
|
const additionalContexts = deduplicateContexts(input.additionalContexts ?? [], selectedContexts);
|
|
3733
3806
|
const definition = applyDefinitionLayer(base, layer);
|
|
3734
|
-
const tools =
|
|
3807
|
+
const tools = resolveTools(resolved.tools, definition.tools);
|
|
3735
3808
|
const skills = mergeSkills(
|
|
3736
3809
|
selectNamedResources(resolved.skills, definition.skills, "Skill"),
|
|
3737
3810
|
definition.bundledSkills
|
|
@@ -3804,11 +3877,20 @@ function intersectPhaseSelection(parent, layer) {
|
|
|
3804
3877
|
phaseIds: layer.phaseIds.filter((name) => parentNames.has(name))
|
|
3805
3878
|
};
|
|
3806
3879
|
}
|
|
3880
|
+
function resolveCoreResources(candidates, selection, coreNames, kind) {
|
|
3881
|
+
const core = candidates.filter((item) => item.core || coreNames.has(item.name));
|
|
3882
|
+
const authoredSelection = selection ? selection.filter((name) => !coreNames.has(name)) : void 0;
|
|
3883
|
+
const authored = candidates.filter((item) => !coreNames.has(item.name) && !item.core);
|
|
3884
|
+
return [
|
|
3885
|
+
...core.map((item) => item.core ? item : { ...item, core: true }),
|
|
3886
|
+
...selectNamedResources(authored, authoredSelection, kind)
|
|
3887
|
+
];
|
|
3888
|
+
}
|
|
3889
|
+
function resolveTools(candidates, selection) {
|
|
3890
|
+
return resolveCoreResources(candidates, selection, CORE_TOOL_NAMES, "Tool");
|
|
3891
|
+
}
|
|
3807
3892
|
function resolvePhases(candidates, selection) {
|
|
3808
|
-
const
|
|
3809
|
-
const core = candidates.filter((phase) => phase.core || coreNames.has(phase.name));
|
|
3810
|
-
const authored = candidates.filter((phase) => !coreNames.has(phase.name) && !phase.core);
|
|
3811
|
-
const selected = [...core, ...selectNamedResources(authored, selection?.phaseIds, "Phase")];
|
|
3893
|
+
const selected = resolveCoreResources(candidates, selection?.phaseIds, CORE_PHASE_NAMES, "Phase");
|
|
3812
3894
|
const entryPhaseId = selection?.entryPhaseId ?? null;
|
|
3813
3895
|
if (entryPhaseId !== null && entryPhaseId !== DEFAULT_PHASE_ID && !selected.some(({ name }) => name === entryPhaseId)) {
|
|
3814
3896
|
console.warn(`Phase entry "${entryPhaseId}" is not available and will be skipped.`);
|
|
@@ -4478,6 +4560,8 @@ function projectTool(tool, agentId, runId) {
|
|
|
4478
4560
|
...tool.core ? { core: true } : {},
|
|
4479
4561
|
description: tool.description,
|
|
4480
4562
|
parameters: tool.parameters,
|
|
4563
|
+
...tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {},
|
|
4564
|
+
...tool.promptGuidelines ? { promptGuidelines: [...tool.promptGuidelines] } : {},
|
|
4481
4565
|
execute: async (args, context, signal) => {
|
|
4482
4566
|
const result = await tool.execute(args, {
|
|
4483
4567
|
agentId,
|
|
@@ -4500,10 +4584,12 @@ function assembleRegisteredExtensions(snapshot, runner, options = {}) {
|
|
|
4500
4584
|
root: snapshot.cwd,
|
|
4501
4585
|
...options.toolArchiveDir ? { archiveDir: options.toolArchiveDir } : {}
|
|
4502
4586
|
});
|
|
4503
|
-
const
|
|
4587
|
+
const replacingNames = new Set(
|
|
4588
|
+
snapshot.resources.tools.filter((tool) => tool.core).map((tool) => tool.name)
|
|
4589
|
+
);
|
|
4504
4590
|
const tools = [
|
|
4505
|
-
...snapshot.resources.tools
|
|
4506
|
-
...coreTools
|
|
4591
|
+
...snapshot.resources.tools,
|
|
4592
|
+
...coreTools.filter((tool) => !replacingNames.has(tool.name))
|
|
4507
4593
|
];
|
|
4508
4594
|
const context = resolveDefinitionContext(snapshot, tools);
|
|
4509
4595
|
return {
|
|
@@ -4518,7 +4604,9 @@ function assembleRegisteredExtensions(snapshot, runner, options = {}) {
|
|
|
4518
4604
|
agentId: input.context.agentId,
|
|
4519
4605
|
toolCallId: input.context.toolCallId,
|
|
4520
4606
|
metadata: input.context.runMetadata,
|
|
4521
|
-
answer: input.answer
|
|
4607
|
+
answer: input.answer,
|
|
4608
|
+
scope: input.context.scope,
|
|
4609
|
+
turn: input.context.turn
|
|
4522
4610
|
});
|
|
4523
4611
|
if (decision.interaction !== void 0) {
|
|
4524
4612
|
return { interaction: decision.interaction };
|
|
@@ -4534,7 +4622,9 @@ function assembleRegisteredExtensions(snapshot, runner, options = {}) {
|
|
|
4534
4622
|
runId: input.context.runId,
|
|
4535
4623
|
agentId: input.context.agentId,
|
|
4536
4624
|
toolCallId: input.context.toolCallId,
|
|
4537
|
-
metadata: input.context.runMetadata
|
|
4625
|
+
metadata: input.context.runMetadata,
|
|
4626
|
+
scope: input.context.scope,
|
|
4627
|
+
turn: input.context.turn
|
|
4538
4628
|
}
|
|
4539
4629
|
);
|
|
4540
4630
|
return fromLoopResult(result);
|
|
@@ -4547,8 +4637,9 @@ function assembleRegisteredExtensions(snapshot, runner, options = {}) {
|
|
|
4547
4637
|
function resolveDefinitionContext(snapshot, assembled) {
|
|
4548
4638
|
const coreTools = assembled.filter((tool) => tool.core);
|
|
4549
4639
|
const authoredTools = assembled.filter((tool) => !tool.core);
|
|
4640
|
+
const authoredSelection = snapshot.definition.tools ? snapshot.definition.tools.filter((name) => !assembled.some((t) => t.core && t.name === name)) : void 0;
|
|
4550
4641
|
const tools = [
|
|
4551
|
-
...selectNamedResources(authoredTools,
|
|
4642
|
+
...selectNamedResources(authoredTools, authoredSelection, "Tool"),
|
|
4552
4643
|
...coreTools
|
|
4553
4644
|
];
|
|
4554
4645
|
const skills = mergeSkills(
|
|
@@ -4560,14 +4651,12 @@ function resolveDefinitionContext(snapshot, assembled) {
|
|
|
4560
4651
|
...snapshot.additionalContexts
|
|
4561
4652
|
];
|
|
4562
4653
|
const phaseCandidates = [...snapshot.resources.phases?.phases.values() ?? []];
|
|
4563
|
-
const
|
|
4654
|
+
const corePhases = phaseCandidates.filter((phase) => phase.core);
|
|
4655
|
+
const authoredPhases = phaseCandidates.filter((phase) => !phase.core);
|
|
4656
|
+
const authoredPhaseIds = snapshot.definition.phases?.phaseIds ? snapshot.definition.phases.phaseIds.filter((name) => !corePhases.some((p) => p.name === name)) : void 0;
|
|
4564
4657
|
const selectedPhases = [
|
|
4565
|
-
...
|
|
4566
|
-
...selectNamedResources(
|
|
4567
|
-
phaseCandidates.filter((phase) => !phase.core && !coreNames.has(phase.name)),
|
|
4568
|
-
snapshot.definition.phases?.phaseIds,
|
|
4569
|
-
"Phase"
|
|
4570
|
-
)
|
|
4658
|
+
...corePhases,
|
|
4659
|
+
...selectNamedResources(authoredPhases, authoredPhaseIds, "Phase")
|
|
4571
4660
|
];
|
|
4572
4661
|
const phases = new Map(selectedPhases.map((phase) => [phase.name, phase]));
|
|
4573
4662
|
const requestedEntry = snapshot.definition.phases ? snapshot.definition.phases.entryPhaseId : snapshot.resources.phases?.entryPhaseId ?? null;
|
|
@@ -4792,6 +4881,126 @@ var HooksManager = class {
|
|
|
4792
4881
|
}
|
|
4793
4882
|
};
|
|
4794
4883
|
|
|
4884
|
+
// src/extensions/host.ts
|
|
4885
|
+
var InMemoryExtensionHost = class {
|
|
4886
|
+
configs = /* @__PURE__ */ new Map();
|
|
4887
|
+
agentState = /* @__PURE__ */ new Map();
|
|
4888
|
+
listeners = /* @__PURE__ */ new Set();
|
|
4889
|
+
constructor(options = {}) {
|
|
4890
|
+
if (options.configs) {
|
|
4891
|
+
for (const [scopeKey, extMap] of Object.entries(options.configs)) {
|
|
4892
|
+
const map = /* @__PURE__ */ new Map();
|
|
4893
|
+
for (const [extId, cfg] of Object.entries(extMap)) {
|
|
4894
|
+
assertJsonValue(cfg);
|
|
4895
|
+
map.set(extId, structuredClone(cfg));
|
|
4896
|
+
}
|
|
4897
|
+
this.configs.set(scopeKey, map);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
if (options.agentState) {
|
|
4901
|
+
for (const [agentId, extMap] of Object.entries(options.agentState)) {
|
|
4902
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
4903
|
+
for (const [extId, stateMap] of Object.entries(extMap)) {
|
|
4904
|
+
const map = /* @__PURE__ */ new Map();
|
|
4905
|
+
for (const [key, val] of Object.entries(stateMap)) {
|
|
4906
|
+
assertJsonValue(val);
|
|
4907
|
+
map.set(key, structuredClone(val));
|
|
4908
|
+
}
|
|
4909
|
+
agentMap.set(extId, map);
|
|
4910
|
+
}
|
|
4911
|
+
this.agentState.set(agentId, agentMap);
|
|
4912
|
+
}
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
setConfig(extensionId, config, scope = { kind: "global" }) {
|
|
4916
|
+
const scopeKey = scopeToKey(scope);
|
|
4917
|
+
if (!this.configs.has(scopeKey)) {
|
|
4918
|
+
this.configs.set(scopeKey, /* @__PURE__ */ new Map());
|
|
4919
|
+
}
|
|
4920
|
+
const scopeConfigs = this.configs.get(scopeKey);
|
|
4921
|
+
if (config === null) {
|
|
4922
|
+
scopeConfigs.delete(extensionId);
|
|
4923
|
+
} else {
|
|
4924
|
+
assertJsonValue(config);
|
|
4925
|
+
scopeConfigs.set(extensionId, structuredClone(config));
|
|
4926
|
+
}
|
|
4927
|
+
this.notifyConfigChanged(extensionId, scope);
|
|
4928
|
+
}
|
|
4929
|
+
getConfig(extensionId, scope = { kind: "global" }) {
|
|
4930
|
+
const globalConfig = this.configs.get("global")?.get(extensionId);
|
|
4931
|
+
let merged = globalConfig ? { ...globalConfig } : null;
|
|
4932
|
+
if (scope.kind === "team" || scope.kind === "project") {
|
|
4933
|
+
const teamConfig = this.configs.get(`team:${scope.teamId}`)?.get(extensionId);
|
|
4934
|
+
if (teamConfig) {
|
|
4935
|
+
merged = { ...merged ?? {}, ...teamConfig };
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
if (scope.kind === "project") {
|
|
4939
|
+
const projectConfig = this.configs.get(`project:${scope.teamId}:${scope.projectId}`)?.get(extensionId);
|
|
4940
|
+
if (projectConfig) {
|
|
4941
|
+
merged = { ...merged ?? {}, ...projectConfig };
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
return merged ? structuredClone(merged) : null;
|
|
4945
|
+
}
|
|
4946
|
+
onConfigChanged(listener) {
|
|
4947
|
+
this.listeners.add(listener);
|
|
4948
|
+
return () => this.listeners.delete(listener);
|
|
4949
|
+
}
|
|
4950
|
+
notifyConfigChanged(extensionId, scope = { kind: "global" }) {
|
|
4951
|
+
for (const listener of this.listeners) {
|
|
4952
|
+
try {
|
|
4953
|
+
listener(extensionId, scope);
|
|
4954
|
+
} catch {
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
getAgentState(extensionId, agentId, key) {
|
|
4959
|
+
const val = this.agentState.get(agentId)?.get(extensionId)?.get(key);
|
|
4960
|
+
return val !== void 0 ? structuredClone(val) : void 0;
|
|
4961
|
+
}
|
|
4962
|
+
setAgentState(extensionId, agentId, key, value) {
|
|
4963
|
+
assertJsonValue(value);
|
|
4964
|
+
let agentMap = this.agentState.get(agentId);
|
|
4965
|
+
if (!agentMap) {
|
|
4966
|
+
agentMap = /* @__PURE__ */ new Map();
|
|
4967
|
+
this.agentState.set(agentId, agentMap);
|
|
4968
|
+
}
|
|
4969
|
+
let extMap = agentMap.get(extensionId);
|
|
4970
|
+
if (!extMap) {
|
|
4971
|
+
extMap = /* @__PURE__ */ new Map();
|
|
4972
|
+
agentMap.set(extensionId, extMap);
|
|
4973
|
+
}
|
|
4974
|
+
extMap.set(key, structuredClone(value));
|
|
4975
|
+
}
|
|
4976
|
+
deleteAgentState(extensionId, agentId, key) {
|
|
4977
|
+
this.agentState.get(agentId)?.get(extensionId)?.delete(key);
|
|
4978
|
+
}
|
|
4979
|
+
};
|
|
4980
|
+
function scopeToKey(scope) {
|
|
4981
|
+
switch (scope.kind) {
|
|
4982
|
+
case "global":
|
|
4983
|
+
return "global";
|
|
4984
|
+
case "team":
|
|
4985
|
+
return `team:${scope.teamId}`;
|
|
4986
|
+
case "project":
|
|
4987
|
+
return `project:${scope.teamId}:${scope.projectId}`;
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
function resolveScopeFromMetadata(metadata) {
|
|
4991
|
+
if (metadata) {
|
|
4992
|
+
const teamId = typeof metadata.teamId === "string" ? metadata.teamId : void 0;
|
|
4993
|
+
const projectId = typeof metadata.projectId === "string" ? metadata.projectId : void 0;
|
|
4994
|
+
if (teamId && projectId) {
|
|
4995
|
+
return { kind: "project", teamId, projectId };
|
|
4996
|
+
}
|
|
4997
|
+
if (teamId) {
|
|
4998
|
+
return { kind: "team", teamId };
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
return { kind: "global" };
|
|
5002
|
+
}
|
|
5003
|
+
|
|
4795
5004
|
// src/extensions/runner.ts
|
|
4796
5005
|
import { execFile } from "child_process";
|
|
4797
5006
|
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
@@ -4934,11 +5143,17 @@ var ExtensionRunner = class {
|
|
|
4934
5143
|
hooks;
|
|
4935
5144
|
runtime;
|
|
4936
5145
|
events;
|
|
5146
|
+
host;
|
|
4937
5147
|
cwd;
|
|
4938
5148
|
abortController = new AbortController();
|
|
4939
5149
|
_idle = true;
|
|
4940
5150
|
// Per-extension tracking
|
|
4941
5151
|
extensions = [];
|
|
5152
|
+
// Run state: runId -> extensionId -> key -> value
|
|
5153
|
+
runState = /* @__PURE__ */ new Map();
|
|
5154
|
+
// Config change listeners: extensionId -> Set of handlers
|
|
5155
|
+
configChangeListeners = /* @__PURE__ */ new Map();
|
|
5156
|
+
hostUnsubscribers = [];
|
|
4942
5157
|
// Phase management
|
|
4943
5158
|
phases = /* @__PURE__ */ new Map();
|
|
4944
5159
|
_phaseCache = null;
|
|
@@ -4947,6 +5162,8 @@ var ExtensionRunner = class {
|
|
|
4947
5162
|
bound = false;
|
|
4948
5163
|
// Error listeners
|
|
4949
5164
|
errorListeners = /* @__PURE__ */ new Set();
|
|
5165
|
+
// Capability change listeners
|
|
5166
|
+
capabilityListeners = /* @__PURE__ */ new Set();
|
|
4950
5167
|
/** Current agent context — set by the agent before each phase */
|
|
4951
5168
|
currentContext;
|
|
4952
5169
|
constructor(options) {
|
|
@@ -4954,6 +5171,15 @@ var ExtensionRunner = class {
|
|
|
4954
5171
|
this.runtime = createExtensionRuntime();
|
|
4955
5172
|
this.events = createEventBus();
|
|
4956
5173
|
this.cwd = options?.cwd ?? process.cwd();
|
|
5174
|
+
this.host = options?.host ?? new InMemoryExtensionHost();
|
|
5175
|
+
if (this.host.onConfigChanged) {
|
|
5176
|
+
const unsub = this.host.onConfigChanged((extensionId, scope) => {
|
|
5177
|
+
this.notifyConfigChanged(extensionId, scope);
|
|
5178
|
+
});
|
|
5179
|
+
if (typeof unsub === "function") {
|
|
5180
|
+
this.hostUnsubscribers.push(unsub);
|
|
5181
|
+
}
|
|
5182
|
+
}
|
|
4957
5183
|
}
|
|
4958
5184
|
/** Whether the agent is currently idle (not streaming). */
|
|
4959
5185
|
get isIdle() {
|
|
@@ -5034,10 +5260,13 @@ var ExtensionRunner = class {
|
|
|
5034
5260
|
*/
|
|
5035
5261
|
async loadExtensions(extensions) {
|
|
5036
5262
|
for (const ext of extensions) {
|
|
5263
|
+
const extensionId = ext.id ?? ext.manifest?.id ?? ext.name;
|
|
5037
5264
|
const extension = {
|
|
5265
|
+
id: extensionId,
|
|
5038
5266
|
path: ext.path,
|
|
5039
5267
|
tools: /* @__PURE__ */ new Map(),
|
|
5040
5268
|
phases: /* @__PURE__ */ new Set(),
|
|
5269
|
+
capabilities: /* @__PURE__ */ new Map(),
|
|
5041
5270
|
cleanup: [],
|
|
5042
5271
|
runtime: createExtensionRuntime()
|
|
5043
5272
|
};
|
|
@@ -5047,6 +5276,9 @@ var ExtensionRunner = class {
|
|
|
5047
5276
|
if (typeof disposer === "function") extension.disposer = disposer;
|
|
5048
5277
|
this.extensions.push(extension);
|
|
5049
5278
|
this._phaseCache = null;
|
|
5279
|
+
if (extension.capabilities.size > 0) {
|
|
5280
|
+
this.notifyCapabilitiesChanged();
|
|
5281
|
+
}
|
|
5050
5282
|
} catch (error) {
|
|
5051
5283
|
await this.rollbackExtension(extension);
|
|
5052
5284
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -5063,14 +5295,67 @@ var ExtensionRunner = class {
|
|
|
5063
5295
|
/** Dispose all active Extensions in reverse activation order. */
|
|
5064
5296
|
async close() {
|
|
5065
5297
|
this.abortController.abort();
|
|
5298
|
+
for (const unsub of this.hostUnsubscribers) {
|
|
5299
|
+
try {
|
|
5300
|
+
unsub();
|
|
5301
|
+
} catch {
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
this.hostUnsubscribers.length = 0;
|
|
5305
|
+
this.configChangeListeners.clear();
|
|
5306
|
+
this.runState.clear();
|
|
5066
5307
|
for (const extension of [...this.extensions].reverse()) {
|
|
5067
5308
|
await this.disposeExtension(extension, "This Extension Runtime has been closed.");
|
|
5068
5309
|
}
|
|
5069
5310
|
this.extensions.length = 0;
|
|
5311
|
+
this.capabilityListeners.clear();
|
|
5070
5312
|
this.phases.clear();
|
|
5071
5313
|
this._phaseCache = null;
|
|
5072
5314
|
this.runtime.invalidate("This Extension Runtime has been closed.");
|
|
5073
5315
|
}
|
|
5316
|
+
get extensionHost() {
|
|
5317
|
+
return this.host;
|
|
5318
|
+
}
|
|
5319
|
+
notifyConfigChanged(extensionId, scope) {
|
|
5320
|
+
const listeners = this.configChangeListeners.get(extensionId);
|
|
5321
|
+
if (!listeners) return;
|
|
5322
|
+
for (const listener of listeners) {
|
|
5323
|
+
try {
|
|
5324
|
+
listener(scope);
|
|
5325
|
+
} catch (error) {
|
|
5326
|
+
this.emitError({
|
|
5327
|
+
extensionPath: "<runtime>",
|
|
5328
|
+
event: "config_changed",
|
|
5329
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5330
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
5331
|
+
});
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
getRunState(runId, extensionId, key) {
|
|
5336
|
+
const val = this.runState.get(runId)?.get(extensionId)?.get(key);
|
|
5337
|
+
return val !== void 0 ? structuredClone(val) : void 0;
|
|
5338
|
+
}
|
|
5339
|
+
setRunState(runId, extensionId, key, value) {
|
|
5340
|
+
assertJsonValue(value);
|
|
5341
|
+
let runMap = this.runState.get(runId);
|
|
5342
|
+
if (!runMap) {
|
|
5343
|
+
runMap = /* @__PURE__ */ new Map();
|
|
5344
|
+
this.runState.set(runId, runMap);
|
|
5345
|
+
}
|
|
5346
|
+
let extMap = runMap.get(extensionId);
|
|
5347
|
+
if (!extMap) {
|
|
5348
|
+
extMap = /* @__PURE__ */ new Map();
|
|
5349
|
+
runMap.set(extensionId, extMap);
|
|
5350
|
+
}
|
|
5351
|
+
extMap.set(key, structuredClone(value));
|
|
5352
|
+
}
|
|
5353
|
+
deleteRunState(runId, extensionId, key) {
|
|
5354
|
+
this.runState.get(runId)?.get(extensionId)?.delete(key);
|
|
5355
|
+
}
|
|
5356
|
+
dropRunState(runId) {
|
|
5357
|
+
this.runState.delete(runId);
|
|
5358
|
+
}
|
|
5074
5359
|
// ---------------------------------------------------------------------------
|
|
5075
5360
|
// Tool management
|
|
5076
5361
|
// ---------------------------------------------------------------------------
|
|
@@ -5097,6 +5382,32 @@ var ExtensionRunner = class {
|
|
|
5097
5382
|
return void 0;
|
|
5098
5383
|
}
|
|
5099
5384
|
// ---------------------------------------------------------------------------
|
|
5385
|
+
// Capability management
|
|
5386
|
+
// ---------------------------------------------------------------------------
|
|
5387
|
+
/** Get all contributed capabilities across all extensions. */
|
|
5388
|
+
getCapabilities() {
|
|
5389
|
+
const result = [];
|
|
5390
|
+
for (const ext of this.extensions) {
|
|
5391
|
+
for (const contrib of ext.capabilities.values()) {
|
|
5392
|
+
result.push({
|
|
5393
|
+
extensionId: ext.id,
|
|
5394
|
+
kind: contrib.kind,
|
|
5395
|
+
name: contrib.name,
|
|
5396
|
+
description: contrib.description
|
|
5397
|
+
});
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
return Object.freeze(result);
|
|
5401
|
+
}
|
|
5402
|
+
/**
|
|
5403
|
+
* Subscribe to capability changes (contributions added, removed, or extensions disposed).
|
|
5404
|
+
* Returns an unsubscribe function.
|
|
5405
|
+
*/
|
|
5406
|
+
onCapabilitiesChanged(listener) {
|
|
5407
|
+
this.capabilityListeners.add(listener);
|
|
5408
|
+
return () => this.capabilityListeners.delete(listener);
|
|
5409
|
+
}
|
|
5410
|
+
// ---------------------------------------------------------------------------
|
|
5100
5411
|
// Phase management
|
|
5101
5412
|
// ---------------------------------------------------------------------------
|
|
5102
5413
|
getPhase(name) {
|
|
@@ -5182,7 +5493,39 @@ var ExtensionRunner = class {
|
|
|
5182
5493
|
});
|
|
5183
5494
|
return result?.input ?? input;
|
|
5184
5495
|
}
|
|
5496
|
+
async emitRunStart(event) {
|
|
5497
|
+
try {
|
|
5498
|
+
await this.hooks.emit("run_start", {
|
|
5499
|
+
type: "run_start",
|
|
5500
|
+
...event
|
|
5501
|
+
});
|
|
5502
|
+
} catch (error) {
|
|
5503
|
+
this.emitError({
|
|
5504
|
+
extensionPath: "<runtime>",
|
|
5505
|
+
event: "run_start",
|
|
5506
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5507
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
5508
|
+
});
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
async emitRunEnd(event) {
|
|
5512
|
+
try {
|
|
5513
|
+
await this.hooks.emit("run_end", {
|
|
5514
|
+
type: "run_end",
|
|
5515
|
+
...event
|
|
5516
|
+
});
|
|
5517
|
+
} catch (error) {
|
|
5518
|
+
this.emitError({
|
|
5519
|
+
extensionPath: "<runtime>",
|
|
5520
|
+
event: "run_end",
|
|
5521
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5522
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
5523
|
+
});
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5185
5526
|
async emitBeforeToolCall(tool, args, context) {
|
|
5527
|
+
const scope = context?.scope ?? resolveScopeFromMetadata(context?.metadata);
|
|
5528
|
+
const turn = context?.turn ?? {};
|
|
5186
5529
|
const result = await this.emitHook("before_tool_call", {
|
|
5187
5530
|
type: "before_tool_call",
|
|
5188
5531
|
tool,
|
|
@@ -5191,11 +5534,15 @@ var ExtensionRunner = class {
|
|
|
5191
5534
|
...context?.agentId !== void 0 ? { agentId: context.agentId } : {},
|
|
5192
5535
|
...context?.toolCallId !== void 0 ? { toolCallId: context.toolCallId } : {},
|
|
5193
5536
|
...context?.metadata !== void 0 ? { metadata: context.metadata } : {},
|
|
5194
|
-
...context?.answer !== void 0 ? { answer: context.answer } : {}
|
|
5537
|
+
...context?.answer !== void 0 ? { answer: context.answer } : {},
|
|
5538
|
+
scope,
|
|
5539
|
+
turn
|
|
5195
5540
|
});
|
|
5196
5541
|
return result ?? { allow: true };
|
|
5197
5542
|
}
|
|
5198
5543
|
async emitAfterToolCall(tool, result, context) {
|
|
5544
|
+
const scope = context?.scope ?? resolveScopeFromMetadata(context?.metadata);
|
|
5545
|
+
const turn = context?.turn ?? {};
|
|
5199
5546
|
const hookResult = await this.emitHook("after_tool_call", {
|
|
5200
5547
|
type: "after_tool_call",
|
|
5201
5548
|
tool,
|
|
@@ -5203,7 +5550,9 @@ var ExtensionRunner = class {
|
|
|
5203
5550
|
...context?.runId !== void 0 ? { runId: context.runId } : {},
|
|
5204
5551
|
...context?.agentId !== void 0 ? { agentId: context.agentId } : {},
|
|
5205
5552
|
...context?.toolCallId !== void 0 ? { toolCallId: context.toolCallId } : {},
|
|
5206
|
-
...context?.metadata !== void 0 ? { metadata: context.metadata } : {}
|
|
5553
|
+
...context?.metadata !== void 0 ? { metadata: context.metadata } : {},
|
|
5554
|
+
scope,
|
|
5555
|
+
turn
|
|
5207
5556
|
});
|
|
5208
5557
|
return hookResult?.result ?? result;
|
|
5209
5558
|
}
|
|
@@ -5271,9 +5620,43 @@ var ExtensionRunner = class {
|
|
|
5271
5620
|
unregisterProvider: (name) => this.unregisterProvider(name),
|
|
5272
5621
|
registerTool: (tool) => this.registerTool(extension, tool),
|
|
5273
5622
|
unregisterTool: (toolName) => this.unregisterTool(extension, toolName),
|
|
5623
|
+
contributeCapability: (contribution) => this.contributeCapability(extension, contribution),
|
|
5274
5624
|
context: extContext,
|
|
5275
5625
|
manifest,
|
|
5276
|
-
trackCleanup: (cleanup) => extension.cleanup.push(cleanup)
|
|
5626
|
+
trackCleanup: (cleanup) => extension.cleanup.push(cleanup),
|
|
5627
|
+
config: {
|
|
5628
|
+
get: async (scope) => {
|
|
5629
|
+
const effectiveScope = scope ?? { kind: "global" };
|
|
5630
|
+
const res = await runner.host.getConfig(extension.id, effectiveScope);
|
|
5631
|
+
return res ?? null;
|
|
5632
|
+
},
|
|
5633
|
+
changed: (handler) => {
|
|
5634
|
+
let listeners = runner.configChangeListeners.get(extension.id);
|
|
5635
|
+
if (!listeners) {
|
|
5636
|
+
listeners = /* @__PURE__ */ new Set();
|
|
5637
|
+
runner.configChangeListeners.set(extension.id, listeners);
|
|
5638
|
+
}
|
|
5639
|
+
listeners.add(handler);
|
|
5640
|
+
extension.cleanup.push(() => {
|
|
5641
|
+
listeners?.delete(handler);
|
|
5642
|
+
if (listeners && listeners.size === 0) {
|
|
5643
|
+
runner.configChangeListeners.delete(extension.id);
|
|
5644
|
+
}
|
|
5645
|
+
});
|
|
5646
|
+
}
|
|
5647
|
+
},
|
|
5648
|
+
state: {
|
|
5649
|
+
run: (runId) => ({
|
|
5650
|
+
get: async (key) => runner.getRunState(runId, extension.id, key),
|
|
5651
|
+
set: async (key, value) => runner.setRunState(runId, extension.id, key, value),
|
|
5652
|
+
delete: async (key) => runner.deleteRunState(runId, extension.id, key)
|
|
5653
|
+
}),
|
|
5654
|
+
agent: (agentId) => ({
|
|
5655
|
+
get: async (key) => runner.host.getAgentState(extension.id, agentId, key),
|
|
5656
|
+
set: async (key, value) => runner.host.setAgentState(extension.id, agentId, key, value),
|
|
5657
|
+
delete: async (key) => runner.host.deleteAgentState(extension.id, agentId, key)
|
|
5658
|
+
})
|
|
5659
|
+
}
|
|
5277
5660
|
}, extension.runtime, this.events);
|
|
5278
5661
|
}
|
|
5279
5662
|
registerTool(extension, tool) {
|
|
@@ -5302,6 +5685,46 @@ var ExtensionRunner = class {
|
|
|
5302
5685
|
extension.tools.delete(toolName);
|
|
5303
5686
|
}
|
|
5304
5687
|
}
|
|
5688
|
+
contributeCapability(extension, contribution) {
|
|
5689
|
+
const key = `${contribution.kind}:${contribution.name}`;
|
|
5690
|
+
const entry = {
|
|
5691
|
+
kind: contribution.kind,
|
|
5692
|
+
name: contribution.name,
|
|
5693
|
+
description: contribution.description
|
|
5694
|
+
};
|
|
5695
|
+
extension.capabilities.set(key, entry);
|
|
5696
|
+
if (this.extensions.includes(extension)) {
|
|
5697
|
+
this.notifyCapabilitiesChanged();
|
|
5698
|
+
}
|
|
5699
|
+
let disposed = false;
|
|
5700
|
+
const dispose = () => {
|
|
5701
|
+
if (disposed) return;
|
|
5702
|
+
disposed = true;
|
|
5703
|
+
if (extension.capabilities.get(key) === entry) {
|
|
5704
|
+
extension.capabilities.delete(key);
|
|
5705
|
+
if (this.extensions.includes(extension)) {
|
|
5706
|
+
this.notifyCapabilitiesChanged();
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
};
|
|
5710
|
+
extension.cleanup.push(dispose);
|
|
5711
|
+
return dispose;
|
|
5712
|
+
}
|
|
5713
|
+
notifyCapabilitiesChanged() {
|
|
5714
|
+
const capabilities = this.getCapabilities();
|
|
5715
|
+
for (const listener of this.capabilityListeners) {
|
|
5716
|
+
try {
|
|
5717
|
+
listener(capabilities);
|
|
5718
|
+
} catch (error) {
|
|
5719
|
+
this.emitError({
|
|
5720
|
+
extensionPath: "<runtime>",
|
|
5721
|
+
event: "capabilities_changed",
|
|
5722
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5723
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
5724
|
+
});
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5305
5728
|
async registerPhase(extension, registration) {
|
|
5306
5729
|
if (typeof registration === "string") {
|
|
5307
5730
|
if (registration.length === 0) {
|
|
@@ -5388,6 +5811,8 @@ var ExtensionRunner = class {
|
|
|
5388
5811
|
async disposeExtension(extension, message) {
|
|
5389
5812
|
extension.runtime.invalidate(message);
|
|
5390
5813
|
for (const name of extension.phases) this.phases.delete(name);
|
|
5814
|
+
const hadCapabilities = extension.capabilities.size > 0;
|
|
5815
|
+
extension.capabilities.clear();
|
|
5391
5816
|
for (const cleanup of [...extension.cleanup].reverse()) {
|
|
5392
5817
|
await Promise.resolve().then(() => cleanup()).catch(() => void 0);
|
|
5393
5818
|
}
|
|
@@ -5395,6 +5820,9 @@ var ExtensionRunner = class {
|
|
|
5395
5820
|
await Promise.resolve().then(() => extension.disposer()).catch(() => void 0);
|
|
5396
5821
|
}
|
|
5397
5822
|
this._phaseCache = null;
|
|
5823
|
+
if (hadCapabilities) {
|
|
5824
|
+
this.notifyCapabilitiesChanged();
|
|
5825
|
+
}
|
|
5398
5826
|
}
|
|
5399
5827
|
};
|
|
5400
5828
|
function createExtensionRunner(options) {
|
|
@@ -5425,6 +5853,8 @@ function readManifestSync(dir) {
|
|
|
5425
5853
|
const rowan = pkg.rowan;
|
|
5426
5854
|
if (!rowan) return void 0;
|
|
5427
5855
|
return {
|
|
5856
|
+
id: rowan.id ?? pkg.name,
|
|
5857
|
+
name: pkg.name,
|
|
5428
5858
|
entry: rowan.extensions?.[0]
|
|
5429
5859
|
};
|
|
5430
5860
|
} catch {
|
|
@@ -5507,13 +5937,15 @@ async function discoverExtensionsInDir(dir) {
|
|
|
5507
5937
|
}
|
|
5508
5938
|
return paths;
|
|
5509
5939
|
}
|
|
5510
|
-
function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
|
|
5940
|
+
function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>", options = {}) {
|
|
5511
5941
|
const resolvedCwd = resolve6(cwd);
|
|
5512
5942
|
const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve6(resolvedCwd, extensionPath);
|
|
5513
|
-
const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname6(resolvedPath)) || "unknown";
|
|
5943
|
+
const name = options.name ?? (isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname6(resolvedPath)) || "unknown");
|
|
5514
5944
|
const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname6(resolvedPath);
|
|
5515
5945
|
const manifest = readManifestSync(manifestDir);
|
|
5946
|
+
const id = options.id ?? manifest?.id ?? name;
|
|
5516
5947
|
return {
|
|
5948
|
+
id,
|
|
5517
5949
|
path: resolvedPath,
|
|
5518
5950
|
name,
|
|
5519
5951
|
factory,
|
|
@@ -5660,8 +6092,16 @@ var ResourceRegistry = class {
|
|
|
5660
6092
|
}
|
|
5661
6093
|
withImplicit(kind, sourceIds) {
|
|
5662
6094
|
const combined = [...sourceIds];
|
|
6095
|
+
let hasCore = false;
|
|
5663
6096
|
for (const sourceId of this.implicitSources.get(kind) ?? []) {
|
|
5664
|
-
if (
|
|
6097
|
+
if (sourceId === "rowan.core") {
|
|
6098
|
+
hasCore = true;
|
|
6099
|
+
} else if (!combined.includes(sourceId)) {
|
|
6100
|
+
combined.push(sourceId);
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
if (hasCore && !combined.includes("rowan.core")) {
|
|
6104
|
+
combined.push("rowan.core");
|
|
5665
6105
|
}
|
|
5666
6106
|
return combined;
|
|
5667
6107
|
}
|
|
@@ -5692,6 +6132,12 @@ var ResourceRegistry = class {
|
|
|
5692
6132
|
`${capitalize(kind)} resource "${value.name}" is reserved by Rowan core.`
|
|
5693
6133
|
);
|
|
5694
6134
|
}
|
|
6135
|
+
if (kind === "phase" && value.name === "continue") {
|
|
6136
|
+
throw new ResourceRegistryError(
|
|
6137
|
+
"resource_collision",
|
|
6138
|
+
`Phase resource "continue" is reserved by Rowan routing controls.`
|
|
6139
|
+
);
|
|
6140
|
+
}
|
|
5695
6141
|
if (names.has(value.name)) {
|
|
5696
6142
|
throw new ResourceRegistryError(
|
|
5697
6143
|
"resource_collision",
|
|
@@ -5726,6 +6172,12 @@ var ResourceRegistry = class {
|
|
|
5726
6172
|
for (const source of this.sourcesFor(kind, sourceIds)) {
|
|
5727
6173
|
for (const value of source.values) {
|
|
5728
6174
|
if (names.has(value.name)) {
|
|
6175
|
+
if (source.sourceId === "rowan.core") {
|
|
6176
|
+
continue;
|
|
6177
|
+
}
|
|
6178
|
+
if (source.sourceId === "rowan.extensions" && isCoreResourceName(kind, value.name)) {
|
|
6179
|
+
continue;
|
|
6180
|
+
}
|
|
5729
6181
|
throw new ResourceRegistryError(
|
|
5730
6182
|
"resource_collision",
|
|
5731
6183
|
`Duplicate ${capitalize(kind)} resource "${value.name}" in Resource View.`
|
|
@@ -5745,7 +6197,15 @@ var ResourceRegistry = class {
|
|
|
5745
6197
|
const refs = [];
|
|
5746
6198
|
for (const source of this.sourcesFor(kind, sourceIds)) {
|
|
5747
6199
|
for (const value of source.values) {
|
|
5748
|
-
if (seenNames.has(value.name))
|
|
6200
|
+
if (seenNames.has(value.name)) {
|
|
6201
|
+
if (source.sourceId === "rowan.core") {
|
|
6202
|
+
continue;
|
|
6203
|
+
}
|
|
6204
|
+
if (source.sourceId === "rowan.extensions" && isCoreResourceName(kind, value.name)) {
|
|
6205
|
+
continue;
|
|
6206
|
+
}
|
|
6207
|
+
throw new ResourceRegistryError("resource_collision", `Duplicate ${capitalize(kind)} resource "${value.name}" in Resource View.`);
|
|
6208
|
+
}
|
|
5749
6209
|
seenNames.add(value.name);
|
|
5750
6210
|
refs.push({ kind, sourceId: source.sourceId, name: value.name });
|
|
5751
6211
|
}
|
|
@@ -5815,7 +6275,10 @@ function capitalize(value) {
|
|
|
5815
6275
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
5816
6276
|
}
|
|
5817
6277
|
function isImplicitCoreName(kind, name) {
|
|
5818
|
-
return kind === "tool" && name === "route"
|
|
6278
|
+
return kind === "tool" && name === "route";
|
|
6279
|
+
}
|
|
6280
|
+
function isCoreResourceName(kind, name) {
|
|
6281
|
+
return kind === "tool" && CORE_TOOL_NAMES.has(name) || kind === "phase" && CORE_PHASE_NAMES.has(name);
|
|
5819
6282
|
}
|
|
5820
6283
|
async function loadDirectoryValues(kind, directory, sourceId) {
|
|
5821
6284
|
const root = resolve7(directory);
|
|
@@ -5983,6 +6446,12 @@ var RuntimeExtensionLifetime = class {
|
|
|
5983
6446
|
get extensionRunner() {
|
|
5984
6447
|
return this.runner;
|
|
5985
6448
|
}
|
|
6449
|
+
get extensionHost() {
|
|
6450
|
+
return this.runner.extensionHost;
|
|
6451
|
+
}
|
|
6452
|
+
dropRunState(runId) {
|
|
6453
|
+
this.runner.dropRunState(runId);
|
|
6454
|
+
}
|
|
5986
6455
|
async close() {
|
|
5987
6456
|
if (this.closed) return;
|
|
5988
6457
|
this.closed = true;
|
|
@@ -5996,6 +6465,9 @@ var RuntimeBootstrapRegistry = class extends ResourceRegistry {
|
|
|
5996
6465
|
}
|
|
5997
6466
|
extensions;
|
|
5998
6467
|
extensionSourceId = "rowan.extensions";
|
|
6468
|
+
get extensionHost() {
|
|
6469
|
+
return this.extensions.extensionHost;
|
|
6470
|
+
}
|
|
5999
6471
|
/** Install Rowan-owned implicit resources before any host source is read. */
|
|
6000
6472
|
async ensureCoreResources() {
|
|
6001
6473
|
await this.replaceImplicit("tool", "rowan.core", [createRouteTool([])]);
|
|
@@ -6041,6 +6513,8 @@ function adaptExtensionTool(input) {
|
|
|
6041
6513
|
name: definition.name,
|
|
6042
6514
|
description: definition.description,
|
|
6043
6515
|
parameters: definition.parameters,
|
|
6516
|
+
...definition.promptSnippet ? { promptSnippet: definition.promptSnippet } : {},
|
|
6517
|
+
...definition.promptGuidelines ? { promptGuidelines: definition.promptGuidelines } : {},
|
|
6044
6518
|
execute: async (args, context, signal) => {
|
|
6045
6519
|
const result = await definition.execute(args, context, signal);
|
|
6046
6520
|
const content = JSON.parse(JSON.stringify(result.content));
|
|
@@ -6134,11 +6608,14 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6134
6608
|
autoCompactionRuns = /* @__PURE__ */ new Set();
|
|
6135
6609
|
consumers = /* @__PURE__ */ new Map();
|
|
6136
6610
|
transientEvents = new TransientRunEventHub();
|
|
6611
|
+
host;
|
|
6137
6612
|
resources;
|
|
6613
|
+
startedRuns = /* @__PURE__ */ new Set();
|
|
6138
6614
|
heartbeat;
|
|
6139
6615
|
pumping = false;
|
|
6140
6616
|
closed = false;
|
|
6141
6617
|
constructor(options, owned, resources) {
|
|
6618
|
+
this.host = options.host;
|
|
6142
6619
|
this.owned = owned;
|
|
6143
6620
|
this.commands = new ConfigCommandService(owned, options.configs, String(owned.lease.token).split(":")[0]);
|
|
6144
6621
|
this.storeIncarnation = String(owned.lease.token).split(":")[0];
|
|
@@ -6148,10 +6625,12 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6148
6625
|
static async init(options) {
|
|
6149
6626
|
if (!options.store) throw new TypeError("AgentRuntime requires a DurableStore");
|
|
6150
6627
|
const configs = options.configs ?? new InMemoryConfigProvider();
|
|
6628
|
+
const host = options.host ?? new InMemoryExtensionHost();
|
|
6151
6629
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
6152
6630
|
if (!Number.isInteger(concurrency) || concurrency <= 0) throw new TypeError("concurrency must be a positive integer");
|
|
6153
6631
|
const owned = await options.store.openOwner({ ownerId: createId("owner"), leaseMs: OWNER_LEASE_MS });
|
|
6154
|
-
const
|
|
6632
|
+
const extensions = new RuntimeExtensionLifetime({ host });
|
|
6633
|
+
const runtime = new _AgentRuntime({ ...options, configs, concurrency, host }, owned, new RuntimeBootstrapRegistry(extensions));
|
|
6155
6634
|
try {
|
|
6156
6635
|
await runtime.resources.ensureCoreResources();
|
|
6157
6636
|
await options.bootstrap?.(runtime.resources);
|
|
@@ -6164,6 +6643,20 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6164
6643
|
throw error;
|
|
6165
6644
|
}
|
|
6166
6645
|
}
|
|
6646
|
+
async handleRunEnd(runId, agentId, outcome) {
|
|
6647
|
+
if (this.startedRuns.has(runId)) {
|
|
6648
|
+
this.startedRuns.delete(runId);
|
|
6649
|
+
try {
|
|
6650
|
+
await this.resources.extensionRunner.emitRunEnd({
|
|
6651
|
+
runId,
|
|
6652
|
+
agentId,
|
|
6653
|
+
outcome
|
|
6654
|
+
});
|
|
6655
|
+
} catch {
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
this.resources.extensionRunner.dropRunState(runId);
|
|
6659
|
+
}
|
|
6167
6660
|
async loadAgents(input) {
|
|
6168
6661
|
this.assertOpen();
|
|
6169
6662
|
return this.resources.loadAgents(input);
|
|
@@ -6374,6 +6867,14 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6374
6867
|
});
|
|
6375
6868
|
return { caughtUp: subscription.caughtUp.promise, done: subscription.done.promise, stop: () => controller.abort() };
|
|
6376
6869
|
}
|
|
6870
|
+
listCapabilities() {
|
|
6871
|
+
this.assertOpen();
|
|
6872
|
+
return this.resources.extensionRunner.getCapabilities();
|
|
6873
|
+
}
|
|
6874
|
+
onCapabilitiesChanged(listener) {
|
|
6875
|
+
this.assertOpen();
|
|
6876
|
+
return this.resources.extensionRunner.onCapabilitiesChanged(listener);
|
|
6877
|
+
}
|
|
6377
6878
|
async close() {
|
|
6378
6879
|
if (this.closed) return;
|
|
6379
6880
|
this.closed = true;
|
|
@@ -6417,6 +6918,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6417
6918
|
const active = this.executions.get(runId);
|
|
6418
6919
|
if (active) this.transientEvents.clear(runId, active.executionId);
|
|
6419
6920
|
active?.controller.abort();
|
|
6921
|
+
if (this.startedRuns.has(runId)) {
|
|
6922
|
+
await this.handleRunEnd(runId, snapshot.agentId, { id: createId("out"), status: "cancelled", message: reason ?? "Agent run stopped." });
|
|
6923
|
+
} else {
|
|
6924
|
+
this.resources.extensionRunner.dropRunState(runId);
|
|
6925
|
+
}
|
|
6420
6926
|
return boundaryFromSnapshot(await this.owned.snapshotRun(runId));
|
|
6421
6927
|
}
|
|
6422
6928
|
async pump() {
|
|
@@ -6528,6 +7034,19 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6528
7034
|
const controller = new AbortController();
|
|
6529
7035
|
this.executions.set(run.id, { controller, executionId });
|
|
6530
7036
|
if (this.cancellationRequested.has(run.id)) controller.abort();
|
|
7037
|
+
if (!this.startedRuns.has(run.id)) {
|
|
7038
|
+
this.startedRuns.add(run.id);
|
|
7039
|
+
const turn = typeof run.input === "object" && run.input !== null && !Array.isArray(run.input) ? run.input : {};
|
|
7040
|
+
try {
|
|
7041
|
+
await this.resources.extensionRunner.emitRunStart({
|
|
7042
|
+
runId: run.id,
|
|
7043
|
+
agentId: run.agentId,
|
|
7044
|
+
metadata: run.metadata ?? {},
|
|
7045
|
+
turn
|
|
7046
|
+
});
|
|
7047
|
+
} catch {
|
|
7048
|
+
}
|
|
7049
|
+
}
|
|
6531
7050
|
const assembly = assembleRegisteredExtensions(config, this.resources.extensionRunner, {
|
|
6532
7051
|
toolArchiveDir: this.archiveDirFor(run.agentId)
|
|
6533
7052
|
});
|
|
@@ -6754,6 +7273,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6754
7273
|
...output && hasVisibleAssistantText(output) ? { output } : {}
|
|
6755
7274
|
});
|
|
6756
7275
|
this.transientEvents.clear(run.id, claim.execution.executionId);
|
|
7276
|
+
await this.handleRunEnd(run.id, run.agentId, {
|
|
7277
|
+
id: createId("out"),
|
|
7278
|
+
status: "cancelled",
|
|
7279
|
+
message: reason
|
|
7280
|
+
});
|
|
6757
7281
|
return;
|
|
6758
7282
|
}
|
|
6759
7283
|
if (result.type === "input_required") {
|
|
@@ -6789,33 +7313,53 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6789
7313
|
const output = latestAssistant(run, result.messages, executionContext.messages.length);
|
|
6790
7314
|
await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, outcome: durableOutcome(result.outcome), ...output ? { output } : {} });
|
|
6791
7315
|
this.transientEvents.clear(run.id, claim.execution.executionId, true);
|
|
7316
|
+
await this.handleRunEnd(run.id, run.agentId, result.outcome);
|
|
6792
7317
|
return;
|
|
6793
7318
|
}
|
|
6794
7319
|
const failure = { code: "execution_failed", message: result.error instanceof Error ? result.error.message : "Execution failed." };
|
|
6795
7320
|
await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, failure });
|
|
6796
7321
|
this.transientEvents.clear(run.id, claim.execution.executionId);
|
|
7322
|
+
await this.handleRunEnd(run.id, run.agentId, {
|
|
7323
|
+
id: createId("out"),
|
|
7324
|
+
status: "failed",
|
|
7325
|
+
message: failure.message,
|
|
7326
|
+
error: failure
|
|
7327
|
+
});
|
|
6797
7328
|
} catch (error) {
|
|
6798
7329
|
if (!claim && error instanceof RuntimeError && ["run_state_conflict", "runtime_ownership_lost", "run_not_found"].includes(error.code)) return;
|
|
6799
7330
|
if (claim) {
|
|
6800
7331
|
if (this.cancellationRequested.has(run.id)) {
|
|
7332
|
+
const reason = this.cancellationReasons.get(run.id) ?? "Agent run stopped.";
|
|
6801
7333
|
await this.owned.cancelRun({
|
|
6802
7334
|
runId: run.id,
|
|
6803
7335
|
expectedRevision: executionRevision,
|
|
6804
|
-
reason
|
|
7336
|
+
reason
|
|
6805
7337
|
}).catch(() => void 0);
|
|
7338
|
+
await this.handleRunEnd(run.id, run.agentId, {
|
|
7339
|
+
id: createId("out"),
|
|
7340
|
+
status: "cancelled",
|
|
7341
|
+
message: reason
|
|
7342
|
+
});
|
|
6806
7343
|
return;
|
|
6807
7344
|
}
|
|
7345
|
+
const failureMessage = error instanceof Error ? error.message : "Execution failed.";
|
|
6808
7346
|
await this.owned.commitOutcome({
|
|
6809
7347
|
runId: run.id,
|
|
6810
7348
|
execution: claim.execution,
|
|
6811
7349
|
expectedRevision: executionRevision,
|
|
6812
|
-
failure: { code: "execution_failed", message:
|
|
7350
|
+
failure: { code: "execution_failed", message: failureMessage }
|
|
6813
7351
|
}).catch(async (commitError) => {
|
|
6814
7352
|
await this.owned.cancelRun({
|
|
6815
7353
|
runId: run.id,
|
|
6816
7354
|
reason: commitError instanceof Error ? commitError.message : "Run failure could not be committed."
|
|
6817
7355
|
}).catch(() => void 0);
|
|
6818
7356
|
});
|
|
7357
|
+
await this.handleRunEnd(run.id, run.agentId, {
|
|
7358
|
+
id: createId("out"),
|
|
7359
|
+
status: "failed",
|
|
7360
|
+
message: failureMessage,
|
|
7361
|
+
error: { code: "execution_failed", message: failureMessage }
|
|
7362
|
+
});
|
|
6819
7363
|
return;
|
|
6820
7364
|
}
|
|
6821
7365
|
throw error;
|
|
@@ -6838,6 +7382,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6838
7382
|
return { result: batch.results[0], revision: batch.revision };
|
|
6839
7383
|
}
|
|
6840
7384
|
async executeToolBatch(input) {
|
|
7385
|
+
const scope = resolveScopeFromMetadata(input.run.metadata);
|
|
7386
|
+
const turn = typeof input.run.input === "object" && input.run.input !== null && !Array.isArray(input.run.input) ? input.run.input : {};
|
|
6841
7387
|
const checkpoint = input.driver?.checkpoint();
|
|
6842
7388
|
const isSuspensionResume = checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension";
|
|
6843
7389
|
const suspension = isSuspensionResume ? checkpoint : void 0;
|
|
@@ -6864,6 +7410,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6864
7410
|
...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
|
|
6865
7411
|
...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
|
|
6866
7412
|
toolCallId,
|
|
7413
|
+
scope,
|
|
7414
|
+
turn,
|
|
6867
7415
|
interaction: createRunInteractionDriver({
|
|
6868
7416
|
currentPhase: "default",
|
|
6869
7417
|
attempt: 0,
|
|
@@ -6951,6 +7499,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6951
7499
|
...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
|
|
6952
7500
|
...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
|
|
6953
7501
|
toolCallId,
|
|
7502
|
+
scope,
|
|
7503
|
+
turn,
|
|
6954
7504
|
interaction: createToolInteractionDriver(input.driver, {
|
|
6955
7505
|
toolCallId,
|
|
6956
7506
|
toolCallIds,
|
|
@@ -7105,6 +7655,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
7105
7655
|
...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
|
|
7106
7656
|
...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
|
|
7107
7657
|
toolCallId,
|
|
7658
|
+
scope,
|
|
7659
|
+
turn,
|
|
7108
7660
|
interaction: createToolInteractionDriver(input.driver, {
|
|
7109
7661
|
toolCallId,
|
|
7110
7662
|
...suspension?.toolCallIds === void 0 ? {} : { toolCallIds: suspension.toolCallIds },
|
|
@@ -7556,23 +8108,29 @@ function isContextOverflowError(error) {
|
|
|
7556
8108
|
}
|
|
7557
8109
|
function normalizePhaseRegistry(registry) {
|
|
7558
8110
|
const core = createCorePhases();
|
|
7559
|
-
const coreNames =
|
|
7560
|
-
for (const [name, phase] of registry?.phases ?? []) {
|
|
7561
|
-
if (coreNames.has(name) && !phase.core) {
|
|
7562
|
-
throw new TypeError(`Configured Phase collides with Rowan built-in Phase "${name}".`);
|
|
7563
|
-
}
|
|
7564
|
-
}
|
|
8111
|
+
const coreNames = CORE_PHASE_NAMES;
|
|
7565
8112
|
for (const [name] of registry?.phases ?? []) {
|
|
7566
8113
|
if (name === "continue") {
|
|
7567
8114
|
throw new TypeError(`Configured Phase name "${name}" is reserved by Rowan routing controls.`);
|
|
7568
8115
|
}
|
|
7569
8116
|
}
|
|
7570
|
-
const
|
|
8117
|
+
const configuredPhases = registry?.phases ?? /* @__PURE__ */ new Map();
|
|
8118
|
+
const phases = /* @__PURE__ */ new Map();
|
|
8119
|
+
for (const corePhase of core) {
|
|
8120
|
+
const configured = configuredPhases.get(corePhase.name);
|
|
8121
|
+
if (configured) {
|
|
8122
|
+
phases.set(corePhase.name, configured.core ? configured : { ...configured, core: true });
|
|
8123
|
+
} else {
|
|
8124
|
+
phases.set(corePhase.name, corePhase);
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
for (const [name, phase] of configuredPhases) {
|
|
8128
|
+
if (!coreNames.has(name)) {
|
|
8129
|
+
phases.set(name, phase);
|
|
8130
|
+
}
|
|
8131
|
+
}
|
|
7571
8132
|
return {
|
|
7572
|
-
phases
|
|
7573
|
-
...core.map((phase) => [phase.name, phase]),
|
|
7574
|
-
...authored
|
|
7575
|
-
]),
|
|
8133
|
+
phases,
|
|
7576
8134
|
entryPhaseId: registry?.entryPhaseId ?? DEFAULT_PHASE_ID
|
|
7577
8135
|
};
|
|
7578
8136
|
}
|
|
@@ -10620,6 +11178,7 @@ export {
|
|
|
10620
11178
|
DEFAULT_PHASE_ID,
|
|
10621
11179
|
ExtensionLifetimeError,
|
|
10622
11180
|
InMemoryConfigProvider,
|
|
11181
|
+
InMemoryExtensionHost,
|
|
10623
11182
|
InMemoryStore,
|
|
10624
11183
|
ResourceRegistry,
|
|
10625
11184
|
ResourceRegistryError,
|