@agentproto/runtime 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +749 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -12
package/dist/index.mjs
CHANGED
|
@@ -29,12 +29,13 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
29
29
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
30
30
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
31
31
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
32
|
-
import {
|
|
32
|
+
import { loadAppHandle } from '@agentproto/app-kit';
|
|
33
|
+
import { normalizeToolId } from '@agentproto/driver';
|
|
34
|
+
import { runWorkflow, buildAgentStep, compileWorkflow } from '@agentproto/workflow-runtime';
|
|
33
35
|
import { loadWorkflowHandle } from '@agentproto/workflow-loader';
|
|
34
36
|
import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
35
37
|
import { Cron } from 'croner';
|
|
36
38
|
import { parseRoutineManifest } from '@agentproto/routine';
|
|
37
|
-
import { normalizeToolId } from '@agentproto/driver';
|
|
38
39
|
import { createServer as createServer$1 } from 'net';
|
|
39
40
|
import { promisify } from 'util';
|
|
40
41
|
import { createRequire } from 'module';
|
|
@@ -21944,6 +21945,18 @@ async function startHttpServer(opts) {
|
|
|
21944
21945
|
const handled = await handleRoutineDefs(req, res, path, opts.routineRegistrar);
|
|
21945
21946
|
if (handled) return;
|
|
21946
21947
|
}
|
|
21948
|
+
if (opts.appRegistry && opts.performAppInstall && opts.listRegisteredToolIds && (path.startsWith("/apps/") || path.startsWith("/scopes/"))) {
|
|
21949
|
+
const handled = await handleApps(
|
|
21950
|
+
req,
|
|
21951
|
+
res,
|
|
21952
|
+
path,
|
|
21953
|
+
opts.appRegistry,
|
|
21954
|
+
opts.performAppInstall,
|
|
21955
|
+
opts.listRegisteredToolIds,
|
|
21956
|
+
opts.resolveAgentAdapter
|
|
21957
|
+
);
|
|
21958
|
+
if (handled) return;
|
|
21959
|
+
}
|
|
21947
21960
|
res.writeHead(404, { "content-type": "application/json" });
|
|
21948
21961
|
res.end(JSON.stringify({ error: "not_found", path }));
|
|
21949
21962
|
} catch (err) {
|
|
@@ -24152,6 +24165,116 @@ async function handleProviderInbound(req, res, slug, deps2) {
|
|
|
24152
24165
|
res.writeHead(200, { "content-type": "application/json" });
|
|
24153
24166
|
res.end(JSON.stringify(result));
|
|
24154
24167
|
}
|
|
24168
|
+
async function handleApps(req, res, path, appRegistry, performInstall2, listRegisteredToolIds, resolveAgentAdapter) {
|
|
24169
|
+
const method = req.method ?? "GET";
|
|
24170
|
+
const applyMatch = path.match(/^\/apps\/([^/]+)\/apply$/);
|
|
24171
|
+
if (applyMatch && method === "POST") {
|
|
24172
|
+
const appId = decodeURIComponent(applyMatch[1]);
|
|
24173
|
+
const body = await readJsonBody(req);
|
|
24174
|
+
const scopeId = body?.scopeId ?? "root";
|
|
24175
|
+
let installed = appRegistry.getApp(appId);
|
|
24176
|
+
if (!installed && body?.dir) {
|
|
24177
|
+
const installResult = await performInstall2(body.dir, appRegistry, listRegisteredToolIds, resolveAgentAdapter);
|
|
24178
|
+
if (!installResult.ok) {
|
|
24179
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
24180
|
+
res.end(JSON.stringify({ error: installResult.error }));
|
|
24181
|
+
return true;
|
|
24182
|
+
}
|
|
24183
|
+
installed = installResult.record;
|
|
24184
|
+
}
|
|
24185
|
+
if (!installed) {
|
|
24186
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
24187
|
+
res.end(
|
|
24188
|
+
JSON.stringify({
|
|
24189
|
+
error: `app "${appId}" is not installed. Either call app_install first or provide a 'dir' parameter.`
|
|
24190
|
+
})
|
|
24191
|
+
);
|
|
24192
|
+
return true;
|
|
24193
|
+
}
|
|
24194
|
+
if (installed.requires && installed.requires.length > 0) {
|
|
24195
|
+
const applied = appRegistry.listApplied(scopeId);
|
|
24196
|
+
const appliedIds = new Set(applied.map((m) => m.appId));
|
|
24197
|
+
const missing = installed.requires.filter((reqId) => !appliedIds.has(reqId));
|
|
24198
|
+
if (missing.length > 0) {
|
|
24199
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
24200
|
+
res.end(
|
|
24201
|
+
JSON.stringify({
|
|
24202
|
+
error: `app "${appId}" requires the following apps to be applied to scope "${scopeId}" first: ${missing.join(", ")}`
|
|
24203
|
+
})
|
|
24204
|
+
);
|
|
24205
|
+
return true;
|
|
24206
|
+
}
|
|
24207
|
+
}
|
|
24208
|
+
const mount = appRegistry.applyApp({ scopeId, appId });
|
|
24209
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
24210
|
+
res.end(
|
|
24211
|
+
JSON.stringify({
|
|
24212
|
+
scopeId: mount.scopeId,
|
|
24213
|
+
appId: mount.appId,
|
|
24214
|
+
appliedAt: mount.appliedAt,
|
|
24215
|
+
agents: installed.agents,
|
|
24216
|
+
workflows: installed.workflows,
|
|
24217
|
+
unvalidatedAgentTools: installed.unvalidatedAgentTools
|
|
24218
|
+
})
|
|
24219
|
+
);
|
|
24220
|
+
return true;
|
|
24221
|
+
}
|
|
24222
|
+
if (applyMatch && method === "DELETE") {
|
|
24223
|
+
const appId = decodeURIComponent(applyMatch[1]);
|
|
24224
|
+
const body = await readJsonBody(req);
|
|
24225
|
+
const url = new URL(req.url ?? "", `http://${req.headers.host}`);
|
|
24226
|
+
const scopeId = body?.scopeId ?? url.searchParams.get("scopeId") ?? "root";
|
|
24227
|
+
const applied = appRegistry.listApplied(scopeId);
|
|
24228
|
+
const dependents = [];
|
|
24229
|
+
for (const mount of applied) {
|
|
24230
|
+
if (mount.appId === appId) continue;
|
|
24231
|
+
const app = appRegistry.getApp(mount.appId);
|
|
24232
|
+
if (app?.requires?.includes(appId)) {
|
|
24233
|
+
dependents.push(mount.appId);
|
|
24234
|
+
}
|
|
24235
|
+
}
|
|
24236
|
+
if (dependents.length > 0) {
|
|
24237
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
24238
|
+
res.end(
|
|
24239
|
+
JSON.stringify({
|
|
24240
|
+
error: `cannot unapply app "${appId}" from scope "${scopeId}" \u2014 the following apps in this scope require it: ${dependents.join(", ")}`
|
|
24241
|
+
})
|
|
24242
|
+
);
|
|
24243
|
+
return true;
|
|
24244
|
+
}
|
|
24245
|
+
const removed = appRegistry.unapplyApp({ scopeId, appId });
|
|
24246
|
+
if (!removed) {
|
|
24247
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
24248
|
+
res.end(JSON.stringify({ error: `app "${appId}" is not applied to scope "${scopeId}".` }));
|
|
24249
|
+
return true;
|
|
24250
|
+
}
|
|
24251
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
24252
|
+
res.end(JSON.stringify({ scopeId: removed.scopeId, appId: removed.appId, appliedAt: removed.appliedAt }));
|
|
24253
|
+
return true;
|
|
24254
|
+
}
|
|
24255
|
+
const scopesMatch = path.match(/^\/scopes\/([^/]+)\/apps$/);
|
|
24256
|
+
if (scopesMatch && method === "GET") {
|
|
24257
|
+
const scopeId = decodeURIComponent(scopesMatch[1]);
|
|
24258
|
+
const mounts = appRegistry.listApplied(scopeId);
|
|
24259
|
+
const result = mounts.map((mount) => {
|
|
24260
|
+
const app = appRegistry.getApp(mount.appId);
|
|
24261
|
+
return {
|
|
24262
|
+
scopeId: mount.scopeId,
|
|
24263
|
+
appId: mount.appId,
|
|
24264
|
+
appliedAt: mount.appliedAt,
|
|
24265
|
+
...app ? {
|
|
24266
|
+
agents: app.agents,
|
|
24267
|
+
workflows: app.workflows,
|
|
24268
|
+
unvalidatedAgentTools: app.unvalidatedAgentTools
|
|
24269
|
+
} : {}
|
|
24270
|
+
};
|
|
24271
|
+
});
|
|
24272
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
24273
|
+
res.end(JSON.stringify(result));
|
|
24274
|
+
return true;
|
|
24275
|
+
}
|
|
24276
|
+
return false;
|
|
24277
|
+
}
|
|
24155
24278
|
async function readJsonBody(req) {
|
|
24156
24279
|
const chunks = [];
|
|
24157
24280
|
for await (const chunk of req) {
|
|
@@ -25260,6 +25383,525 @@ stderr: ${stderrText.slice(-600)}` : msg);
|
|
|
25260
25383
|
`import "${entry.alias}" has unsupported transport type "${snap.type}"`
|
|
25261
25384
|
);
|
|
25262
25385
|
}
|
|
25386
|
+
function collectToolIds(steps, out) {
|
|
25387
|
+
for (const step of steps) {
|
|
25388
|
+
switch (step.kind) {
|
|
25389
|
+
case "tool": {
|
|
25390
|
+
const ref = typeof step.tool === "string" ? step.tool : step.tool.entry;
|
|
25391
|
+
out.add(normalizeToolId(ref));
|
|
25392
|
+
break;
|
|
25393
|
+
}
|
|
25394
|
+
case "map":
|
|
25395
|
+
case "loop":
|
|
25396
|
+
collectToolIds(step.steps, out);
|
|
25397
|
+
break;
|
|
25398
|
+
case "parallel":
|
|
25399
|
+
for (const branch of step.branches) collectToolIds(branch.steps, out);
|
|
25400
|
+
break;
|
|
25401
|
+
}
|
|
25402
|
+
}
|
|
25403
|
+
}
|
|
25404
|
+
function passthroughToolHandle(id) {
|
|
25405
|
+
return {
|
|
25406
|
+
id,
|
|
25407
|
+
name: id,
|
|
25408
|
+
description: `Daemon tool '${id}', dispatched via the routine/cron dispatchTool bridge.`,
|
|
25409
|
+
mutates: [],
|
|
25410
|
+
requires: { network: [], secrets: [], tools: [] },
|
|
25411
|
+
approval: "auto",
|
|
25412
|
+
riskLevel: 0,
|
|
25413
|
+
costClass: "trivial",
|
|
25414
|
+
timeoutMs: 3e4,
|
|
25415
|
+
tags: [],
|
|
25416
|
+
metadata: {},
|
|
25417
|
+
idempotent: false,
|
|
25418
|
+
driverConstraints: { forbid: [], requireKind: [] }
|
|
25419
|
+
};
|
|
25420
|
+
}
|
|
25421
|
+
function unwrapDispatchResult(result) {
|
|
25422
|
+
if (!result || typeof result !== "object" || !("content" in result)) return result;
|
|
25423
|
+
const r = result;
|
|
25424
|
+
const text9 = (r.content ?? []).map((c) => c.text ?? "").join("\n").trim();
|
|
25425
|
+
if (r.isError) throw new Error(text9 || "tool call failed");
|
|
25426
|
+
if (!text9) return void 0;
|
|
25427
|
+
try {
|
|
25428
|
+
return JSON.parse(text9);
|
|
25429
|
+
} catch {
|
|
25430
|
+
return text9;
|
|
25431
|
+
}
|
|
25432
|
+
}
|
|
25433
|
+
function createDaemonToolRegistry(handle, dispatchTool) {
|
|
25434
|
+
const toolIds = /* @__PURE__ */ new Set();
|
|
25435
|
+
collectToolIds(handle.steps, toolIds);
|
|
25436
|
+
const tools = {};
|
|
25437
|
+
const execute = {};
|
|
25438
|
+
for (const id of toolIds) {
|
|
25439
|
+
tools[id] = passthroughToolHandle(id);
|
|
25440
|
+
execute[id] = async ({ input }) => unwrapDispatchResult(await dispatchTool(id, input ?? {}));
|
|
25441
|
+
}
|
|
25442
|
+
if (toolIds.size === 0) return { tools, candidates: [] };
|
|
25443
|
+
const daemonDriver = {
|
|
25444
|
+
id: "daemon-tool-dispatch",
|
|
25445
|
+
name: "Daemon tool dispatch",
|
|
25446
|
+
description: "Catch-all AIP-30 provider routing every referenced WORKFLOW.md tool step through the daemon's dispatchTool.",
|
|
25447
|
+
kind: "builtin",
|
|
25448
|
+
implements: [...toolIds].map((id) => ({ tool: id, version: "*" })),
|
|
25449
|
+
execute,
|
|
25450
|
+
install: [],
|
|
25451
|
+
network: { egress: [], ingress: [] },
|
|
25452
|
+
region: ["global"],
|
|
25453
|
+
policyTags: [],
|
|
25454
|
+
tags: [],
|
|
25455
|
+
metadata: {}
|
|
25456
|
+
};
|
|
25457
|
+
return { tools, candidates: [daemonDriver] };
|
|
25458
|
+
}
|
|
25459
|
+
var DEFAULT_PERSIST_PATH2 = () => join(homedir(), ".agentproto", "apps.json");
|
|
25460
|
+
function loadState(persistPath) {
|
|
25461
|
+
const empty = { apps: [], runs: [], applied: [] };
|
|
25462
|
+
if (!existsSync(persistPath)) return empty;
|
|
25463
|
+
let raw;
|
|
25464
|
+
try {
|
|
25465
|
+
raw = readFileSync(persistPath, "utf8");
|
|
25466
|
+
} catch {
|
|
25467
|
+
return empty;
|
|
25468
|
+
}
|
|
25469
|
+
try {
|
|
25470
|
+
const parsed = JSON.parse(raw);
|
|
25471
|
+
return {
|
|
25472
|
+
apps: Array.isArray(parsed.apps) ? parsed.apps : [],
|
|
25473
|
+
runs: Array.isArray(parsed.runs) ? parsed.runs : [],
|
|
25474
|
+
applied: Array.isArray(parsed.applied) ? parsed.applied : []
|
|
25475
|
+
};
|
|
25476
|
+
} catch {
|
|
25477
|
+
return empty;
|
|
25478
|
+
}
|
|
25479
|
+
}
|
|
25480
|
+
function saveState(state, persistPath) {
|
|
25481
|
+
try {
|
|
25482
|
+
mkdirSync(dirname(persistPath), { recursive: true });
|
|
25483
|
+
const tmp = `${persistPath}.tmp.${process.pid}`;
|
|
25484
|
+
writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
25485
|
+
renameSync(tmp, persistPath);
|
|
25486
|
+
} catch {
|
|
25487
|
+
}
|
|
25488
|
+
}
|
|
25489
|
+
function createAppRegistry(opts) {
|
|
25490
|
+
const persistPath = opts?.persistPath ?? DEFAULT_PERSIST_PATH2();
|
|
25491
|
+
const shouldPersist = opts?.persist ?? opts?.persistPath !== void 0;
|
|
25492
|
+
const state = shouldPersist ? loadState(persistPath) : { apps: [], runs: [], applied: [] };
|
|
25493
|
+
const persist = () => {
|
|
25494
|
+
if (shouldPersist) saveState(state, persistPath);
|
|
25495
|
+
};
|
|
25496
|
+
return {
|
|
25497
|
+
upsertApp(input) {
|
|
25498
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25499
|
+
const idx = state.apps.findIndex((a) => a.appId === input.appId);
|
|
25500
|
+
const record2 = {
|
|
25501
|
+
...input,
|
|
25502
|
+
installedAt: idx === -1 ? now : state.apps[idx].installedAt,
|
|
25503
|
+
updatedAt: now
|
|
25504
|
+
};
|
|
25505
|
+
if (idx === -1) state.apps.push(record2);
|
|
25506
|
+
else state.apps[idx] = record2;
|
|
25507
|
+
persist();
|
|
25508
|
+
return record2;
|
|
25509
|
+
},
|
|
25510
|
+
getApp(appId) {
|
|
25511
|
+
return state.apps.find((a) => a.appId === appId);
|
|
25512
|
+
},
|
|
25513
|
+
listApps() {
|
|
25514
|
+
return [...state.apps];
|
|
25515
|
+
},
|
|
25516
|
+
createRun(input) {
|
|
25517
|
+
const run = {
|
|
25518
|
+
appRunId: `apprun_${randomUUID()}`,
|
|
25519
|
+
appId: input.appId,
|
|
25520
|
+
sessions: input.sessions,
|
|
25521
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25522
|
+
status: "running"
|
|
25523
|
+
};
|
|
25524
|
+
state.runs.push(run);
|
|
25525
|
+
persist();
|
|
25526
|
+
return run;
|
|
25527
|
+
},
|
|
25528
|
+
getRun(appRunId) {
|
|
25529
|
+
return state.runs.find((r) => r.appRunId === appRunId);
|
|
25530
|
+
},
|
|
25531
|
+
listRuns() {
|
|
25532
|
+
return [...state.runs];
|
|
25533
|
+
},
|
|
25534
|
+
endRun(appRunId) {
|
|
25535
|
+
const run = state.runs.find((r) => r.appRunId === appRunId);
|
|
25536
|
+
if (!run) return void 0;
|
|
25537
|
+
run.status = "stopped";
|
|
25538
|
+
run.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25539
|
+
persist();
|
|
25540
|
+
return run;
|
|
25541
|
+
},
|
|
25542
|
+
applyApp(input) {
|
|
25543
|
+
const idx = state.applied.findIndex(
|
|
25544
|
+
(m) => m.scopeId === input.scopeId && m.appId === input.appId
|
|
25545
|
+
);
|
|
25546
|
+
const mount = {
|
|
25547
|
+
scopeId: input.scopeId,
|
|
25548
|
+
appId: input.appId,
|
|
25549
|
+
appliedAt: idx === -1 ? (/* @__PURE__ */ new Date()).toISOString() : state.applied[idx].appliedAt
|
|
25550
|
+
};
|
|
25551
|
+
if (idx === -1) state.applied.push(mount);
|
|
25552
|
+
else state.applied[idx] = mount;
|
|
25553
|
+
persist();
|
|
25554
|
+
return mount;
|
|
25555
|
+
},
|
|
25556
|
+
unapplyApp(input) {
|
|
25557
|
+
const idx = state.applied.findIndex(
|
|
25558
|
+
(m) => m.scopeId === input.scopeId && m.appId === input.appId
|
|
25559
|
+
);
|
|
25560
|
+
if (idx === -1) return void 0;
|
|
25561
|
+
const removed = state.applied[idx];
|
|
25562
|
+
state.applied.splice(idx, 1);
|
|
25563
|
+
persist();
|
|
25564
|
+
return removed;
|
|
25565
|
+
},
|
|
25566
|
+
listApplied(scopeId) {
|
|
25567
|
+
if (scopeId === void 0) return [...state.applied];
|
|
25568
|
+
return state.applied.filter((m) => m.scopeId === scopeId);
|
|
25569
|
+
}
|
|
25570
|
+
};
|
|
25571
|
+
}
|
|
25572
|
+
|
|
25573
|
+
// src/app-tools.ts
|
|
25574
|
+
var DEFAULT_AGENT_ADAPTER = "mastra-agent";
|
|
25575
|
+
function resolveAgentRefsForWorkflow(appRegistry, workflowId) {
|
|
25576
|
+
const app = appRegistry.listApps().find((a) => a.workflows.some((w) => w.id === workflowId));
|
|
25577
|
+
if (!app) return void 0;
|
|
25578
|
+
const refs = {};
|
|
25579
|
+
for (const agent of app.agents) {
|
|
25580
|
+
refs[agent.id] = { adapter: DEFAULT_AGENT_ADAPTER, options: { agent: agent.path } };
|
|
25581
|
+
}
|
|
25582
|
+
return refs;
|
|
25583
|
+
}
|
|
25584
|
+
function textResult(body) {
|
|
25585
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
25586
|
+
}
|
|
25587
|
+
function errorResult(text9) {
|
|
25588
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: text9 }) }], isError: true };
|
|
25589
|
+
}
|
|
25590
|
+
function refIdOf(ref) {
|
|
25591
|
+
if (typeof ref === "string") return ref;
|
|
25592
|
+
return ref.ref ?? ref.file ?? "inline";
|
|
25593
|
+
}
|
|
25594
|
+
function resolveRef(dir, path) {
|
|
25595
|
+
return isAbsolute(path) ? path : join(dir, path);
|
|
25596
|
+
}
|
|
25597
|
+
async function readAppRefs(dir) {
|
|
25598
|
+
const appPath = join(dir, ".agentproto", "APP.md");
|
|
25599
|
+
const source = await readFile(appPath, "utf8");
|
|
25600
|
+
const { data } = matter(source);
|
|
25601
|
+
const toRefs = (v) => Array.isArray(v) ? v.filter(
|
|
25602
|
+
(e) => typeof e === "object" && e !== null && typeof e.id === "string" && typeof e.path === "string"
|
|
25603
|
+
).map((e) => ({ id: e.id, path: resolveRef(dir, e.path) })) : [];
|
|
25604
|
+
return { agents: toRefs(data.agents), workflows: toRefs(data.workflows) };
|
|
25605
|
+
}
|
|
25606
|
+
async function performInstall(dir, appRegistry, listRegisteredToolIds, resolveAgentAdapter) {
|
|
25607
|
+
let handle;
|
|
25608
|
+
try {
|
|
25609
|
+
handle = await loadAppHandle(dir);
|
|
25610
|
+
} catch (err) {
|
|
25611
|
+
return { ok: false, error: `${err instanceof Error ? err.message : String(err)}` };
|
|
25612
|
+
}
|
|
25613
|
+
if (!handle.id) {
|
|
25614
|
+
return { ok: false, error: "the app has no `id` \u2014 set one in defineApp()/APP.md frontmatter to install it." };
|
|
25615
|
+
}
|
|
25616
|
+
const missingByWorkflow = {};
|
|
25617
|
+
const registeredIds = new Set(await listRegisteredToolIds());
|
|
25618
|
+
for (const workflow of handle.workflows) {
|
|
25619
|
+
const { tools } = createDaemonToolRegistry(workflow, async () => void 0);
|
|
25620
|
+
const missing = Object.keys(tools).filter((id) => !registeredIds.has(id));
|
|
25621
|
+
if (missing.length > 0) missingByWorkflow[workflow.id] = missing;
|
|
25622
|
+
}
|
|
25623
|
+
if (Object.keys(missingByWorkflow).length > 0) {
|
|
25624
|
+
return {
|
|
25625
|
+
ok: false,
|
|
25626
|
+
error: `unknown daemon tool id(s) referenced by workflow step(s) \u2014 would otherwise fail at STEP-DISPATCH time: ${JSON.stringify(missingByWorkflow)}`
|
|
25627
|
+
};
|
|
25628
|
+
}
|
|
25629
|
+
if (handle.agents.length > 0) {
|
|
25630
|
+
const resolved = resolveAgentAdapter ? await resolveAgentAdapter(DEFAULT_AGENT_ADAPTER) : null;
|
|
25631
|
+
if (!resolved) {
|
|
25632
|
+
return {
|
|
25633
|
+
ok: false,
|
|
25634
|
+
error: `agent adapter "${DEFAULT_AGENT_ADAPTER}" could not be resolved \u2014 run \`agentproto install ${DEFAULT_AGENT_ADAPTER}\` first.`
|
|
25635
|
+
};
|
|
25636
|
+
}
|
|
25637
|
+
}
|
|
25638
|
+
const refs = await readAppRefs(dir);
|
|
25639
|
+
const unvalidatedAgentTools = [
|
|
25640
|
+
...new Set(handle.agents.flatMap((e) => (e.agent.tools ?? []).map(refIdOf)))
|
|
25641
|
+
];
|
|
25642
|
+
const record2 = appRegistry.upsertApp({
|
|
25643
|
+
appId: handle.id,
|
|
25644
|
+
dir,
|
|
25645
|
+
...handle.version ? { version: handle.version } : {},
|
|
25646
|
+
...handle.name ? { name: handle.name } : {},
|
|
25647
|
+
agents: refs.agents,
|
|
25648
|
+
workflows: refs.workflows,
|
|
25649
|
+
unvalidatedAgentTools,
|
|
25650
|
+
...handle.requires ? { requires: handle.requires } : {}
|
|
25651
|
+
});
|
|
25652
|
+
return { ok: true, record: record2 };
|
|
25653
|
+
}
|
|
25654
|
+
function registerAppTools(server, opts) {
|
|
25655
|
+
const { registry, resolveAgentAdapter, listRegisteredToolIds, workflowRunner } = opts;
|
|
25656
|
+
const appRegistry = opts.appRegistry ?? createAppRegistry({
|
|
25657
|
+
...opts.persistPath !== void 0 ? { persistPath: opts.persistPath } : {},
|
|
25658
|
+
...opts.persist !== void 0 ? { persist: opts.persist } : {}
|
|
25659
|
+
});
|
|
25660
|
+
const notEnabled = (tool) => errorResult(
|
|
25661
|
+
`${tool} is not enabled \u2014 the daemon was started without an adapter resolver. Re-run the daemon with the \`@agentproto/cli\` shim wired (see playground/scripts/gateway.ts).`
|
|
25662
|
+
);
|
|
25663
|
+
server.tool(
|
|
25664
|
+
"app_install",
|
|
25665
|
+
"Install an @agentproto/app-kit app from its emitted directory (`<dir>/.agentproto/APP.md` \u2014 see `defineApp().emit(dir)`). Validates every WORKFLOW.md `tool` step's id against the daemon's dispatchable tools (missing ids are reported ALL at once, instead of failing one at a time at STEP-DISPATCH time) and checks the `mastra-agent` adapter resolves. Agent-declared tool refs (workspace tools like `read_file`) are the adapter's own business and are never validated here \u2014 see `unvalidatedAgentTools` on the result. Re-installing the same appId upserts.",
|
|
25666
|
+
{ dir: z.string().describe("Absolute path to the app's directory.") },
|
|
25667
|
+
async (input) => {
|
|
25668
|
+
const result = await performInstall(input.dir, appRegistry, listRegisteredToolIds, resolveAgentAdapter);
|
|
25669
|
+
if (!result.ok) return errorResult(`app_install: ${result.error}`);
|
|
25670
|
+
return textResult(result.record);
|
|
25671
|
+
}
|
|
25672
|
+
);
|
|
25673
|
+
server.tool(
|
|
25674
|
+
"app_list",
|
|
25675
|
+
"List installed apps, each with a summary of its app_run history.",
|
|
25676
|
+
{},
|
|
25677
|
+
async () => {
|
|
25678
|
+
const runs = appRegistry.listRuns();
|
|
25679
|
+
const apps = appRegistry.listApps().map((app) => ({
|
|
25680
|
+
...app,
|
|
25681
|
+
runs: runs.filter((r) => r.appId === app.appId).map((r) => ({
|
|
25682
|
+
appRunId: r.appRunId,
|
|
25683
|
+
status: r.status,
|
|
25684
|
+
startedAt: r.startedAt,
|
|
25685
|
+
...r.endedAt ? { endedAt: r.endedAt } : {},
|
|
25686
|
+
sessions: r.sessions.length
|
|
25687
|
+
}))
|
|
25688
|
+
}));
|
|
25689
|
+
return textResult(apps);
|
|
25690
|
+
}
|
|
25691
|
+
);
|
|
25692
|
+
server.tool(
|
|
25693
|
+
"app_run",
|
|
25694
|
+
"Run an installed app's agents as live sessions \u2014 one `agent_start`-equivalent spawn per selected agent (adapter `mastra-agent`, pointed at that agent's emitted AGENT.md via the adapter's `agent` option), grouped under a fresh appRunId. Re-reads the app's directory first, so a stale install record (paths moved, a workflow renamed) is refreshed before spawning \u2014 the same refreshed paths are what make `workflow_run_file` work against this app's WORKFLOW.md files. Poll with `app_status`, kill with `app_stop`.",
|
|
25695
|
+
{
|
|
25696
|
+
appId: z.string(),
|
|
25697
|
+
agents: z.array(z.string()).optional().describe("Agent ids to run. Omit to run every agent the app bundles."),
|
|
25698
|
+
prompt: z.string().optional().describe("Prompt to send to each spawned agent session."),
|
|
25699
|
+
cwd: z.string().optional().describe("Working directory for spawned sessions. Defaults to the app's installed `dir`."),
|
|
25700
|
+
scopeId: z.string().optional().describe("When passed, refuse to run if the app is not applied to this scope.")
|
|
25701
|
+
// follow-up: no sandbox support in this WP — the e2b image doesn't carry
|
|
25702
|
+
// the mastra-agent adapter yet (see output/phase-a-findings.md A3). Thread
|
|
25703
|
+
// a `sandbox` field through to `spawnAgentSession` here once an image
|
|
25704
|
+
// provisions it (or `app_install`/boot does `agentproto install
|
|
25705
|
+
// mastra-agent` inside the box).
|
|
25706
|
+
},
|
|
25707
|
+
async (input) => {
|
|
25708
|
+
if (!resolveAgentAdapter) return notEnabled("app_run");
|
|
25709
|
+
const installed = appRegistry.getApp(input.appId);
|
|
25710
|
+
if (!installed) {
|
|
25711
|
+
return errorResult(`app_run: no installed app "${input.appId}" \u2014 call app_install first.`);
|
|
25712
|
+
}
|
|
25713
|
+
if (input.scopeId) {
|
|
25714
|
+
const applied = appRegistry.listApplied(input.scopeId);
|
|
25715
|
+
if (!applied.some((m) => m.appId === input.appId)) {
|
|
25716
|
+
return errorResult(
|
|
25717
|
+
`app_run: app "${input.appId}" is not applied to scope "${input.scopeId}". Call app_apply first.`
|
|
25718
|
+
);
|
|
25719
|
+
}
|
|
25720
|
+
}
|
|
25721
|
+
let refs;
|
|
25722
|
+
try {
|
|
25723
|
+
refs = await readAppRefs(installed.dir);
|
|
25724
|
+
} catch (err) {
|
|
25725
|
+
return errorResult(
|
|
25726
|
+
`app_run: could not re-read "${installed.dir}": ${err instanceof Error ? err.message : String(err)}`
|
|
25727
|
+
);
|
|
25728
|
+
}
|
|
25729
|
+
const app = appRegistry.upsertApp({ ...installed, agents: refs.agents, workflows: refs.workflows });
|
|
25730
|
+
const selected = input.agents ?? app.agents.map((a) => a.id);
|
|
25731
|
+
const unknown = selected.filter((id) => !app.agents.some((a) => a.id === id));
|
|
25732
|
+
if (unknown.length > 0) {
|
|
25733
|
+
return errorResult(
|
|
25734
|
+
`app_run: unknown agent id(s) for app "${input.appId}": ${unknown.join(", ")}`
|
|
25735
|
+
);
|
|
25736
|
+
}
|
|
25737
|
+
const sessions = [];
|
|
25738
|
+
const errors = [];
|
|
25739
|
+
for (const agentId of selected) {
|
|
25740
|
+
const agentPath = app.agents.find((a) => a.id === agentId).path;
|
|
25741
|
+
const result = await spawnAgentSession(
|
|
25742
|
+
{ registry, resolveAgentAdapter },
|
|
25743
|
+
{
|
|
25744
|
+
adapter: DEFAULT_AGENT_ADAPTER,
|
|
25745
|
+
cwd: input.cwd ?? app.dir,
|
|
25746
|
+
...input.prompt ? { prompt: input.prompt } : {},
|
|
25747
|
+
options: { agent: agentPath },
|
|
25748
|
+
label: `app:${app.appId}:${agentId}`
|
|
25749
|
+
}
|
|
25750
|
+
);
|
|
25751
|
+
if (result.ok) sessions.push({ agentId, sessionId: result.descriptor.id });
|
|
25752
|
+
else errors.push({ agentId, error: result.message });
|
|
25753
|
+
}
|
|
25754
|
+
const run = appRegistry.createRun({ appId: app.appId, sessions });
|
|
25755
|
+
return textResult({
|
|
25756
|
+
appRunId: run.appRunId,
|
|
25757
|
+
sessions,
|
|
25758
|
+
...errors.length > 0 ? { errors } : {}
|
|
25759
|
+
});
|
|
25760
|
+
}
|
|
25761
|
+
);
|
|
25762
|
+
server.tool(
|
|
25763
|
+
"app_status",
|
|
25764
|
+
"Status of an app_run: its sessions' live descriptors, plus any workflow runs belonging to the app (any run of one of its bundled WORKFLOW.md files, however it was started).",
|
|
25765
|
+
{ appRunId: z.string() },
|
|
25766
|
+
async (input) => {
|
|
25767
|
+
const run = appRegistry.getRun(input.appRunId);
|
|
25768
|
+
if (!run) return errorResult(`app_status: no app run "${input.appRunId}".`);
|
|
25769
|
+
const app = appRegistry.getApp(run.appId);
|
|
25770
|
+
const sessions = run.sessions.map((s) => ({
|
|
25771
|
+
agentId: s.agentId,
|
|
25772
|
+
sessionId: s.sessionId,
|
|
25773
|
+
descriptor: registry.get(s.sessionId)
|
|
25774
|
+
}));
|
|
25775
|
+
const workflowRuns = workflowRunner && app ? workflowRunner.list().filter((r) => app.workflows.some((w) => w.id === r.workflowId)) : [];
|
|
25776
|
+
return textResult({
|
|
25777
|
+
appRunId: run.appRunId,
|
|
25778
|
+
appId: run.appId,
|
|
25779
|
+
status: run.status,
|
|
25780
|
+
startedAt: run.startedAt,
|
|
25781
|
+
...run.endedAt ? { endedAt: run.endedAt } : {},
|
|
25782
|
+
sessions,
|
|
25783
|
+
workflowRuns
|
|
25784
|
+
});
|
|
25785
|
+
}
|
|
25786
|
+
);
|
|
25787
|
+
server.tool(
|
|
25788
|
+
"app_stop",
|
|
25789
|
+
"Kill every session in an app_run (existing kill path) and mark the run ended.",
|
|
25790
|
+
{ appRunId: z.string() },
|
|
25791
|
+
async (input) => {
|
|
25792
|
+
const run = appRegistry.getRun(input.appRunId);
|
|
25793
|
+
if (!run) return errorResult(`app_stop: no app run "${input.appRunId}".`);
|
|
25794
|
+
const killed = [];
|
|
25795
|
+
const notFound = [];
|
|
25796
|
+
for (const s of run.sessions) {
|
|
25797
|
+
if (registry.kill(s.sessionId)) killed.push(s.sessionId);
|
|
25798
|
+
else notFound.push(s.sessionId);
|
|
25799
|
+
}
|
|
25800
|
+
const ended = appRegistry.endRun(input.appRunId);
|
|
25801
|
+
return textResult({
|
|
25802
|
+
appRunId: input.appRunId,
|
|
25803
|
+
killed,
|
|
25804
|
+
...notFound.length > 0 ? { notFound } : {},
|
|
25805
|
+
status: ended?.status ?? run.status
|
|
25806
|
+
});
|
|
25807
|
+
}
|
|
25808
|
+
);
|
|
25809
|
+
server.tool(
|
|
25810
|
+
"app_apply",
|
|
25811
|
+
"Apply an app to a scope, making its capabilities available in that scope. If the app is not installed and `dir` is provided, installs it first. Validates that all `requires` dependencies are already applied to the same scope. Idempotent \u2014 re-applying the same app to the same scope updates the timestamp.",
|
|
25812
|
+
{
|
|
25813
|
+
appId: z.string(),
|
|
25814
|
+
scopeId: z.string().optional().describe("Scope to apply to. Defaults to 'root'."),
|
|
25815
|
+
dir: z.string().optional().describe("Absolute path to install from if not already installed.")
|
|
25816
|
+
},
|
|
25817
|
+
async (input) => {
|
|
25818
|
+
const scopeId = input.scopeId ?? "root";
|
|
25819
|
+
let installed = appRegistry.getApp(input.appId);
|
|
25820
|
+
if (!installed && input.dir) {
|
|
25821
|
+
const installResult = await performInstall(input.dir, appRegistry, listRegisteredToolIds, resolveAgentAdapter);
|
|
25822
|
+
if (!installResult.ok) return errorResult(`app_apply: ${installResult.error}`);
|
|
25823
|
+
installed = installResult.record;
|
|
25824
|
+
} else if (!installed) {
|
|
25825
|
+
return errorResult(
|
|
25826
|
+
`app_apply: app "${input.appId}" is not installed. Either call app_install first or provide a 'dir' parameter.`
|
|
25827
|
+
);
|
|
25828
|
+
}
|
|
25829
|
+
if (installed.requires && installed.requires.length > 0) {
|
|
25830
|
+
const applied = appRegistry.listApplied(scopeId);
|
|
25831
|
+
const appliedIds = new Set(applied.map((m) => m.appId));
|
|
25832
|
+
const missing = installed.requires.filter((reqId) => !appliedIds.has(reqId));
|
|
25833
|
+
if (missing.length > 0) {
|
|
25834
|
+
return errorResult(
|
|
25835
|
+
`app_apply: app "${input.appId}" requires the following apps to be applied to scope "${scopeId}" first: ${missing.join(", ")}`
|
|
25836
|
+
);
|
|
25837
|
+
}
|
|
25838
|
+
}
|
|
25839
|
+
const mount = appRegistry.applyApp({ scopeId, appId: input.appId });
|
|
25840
|
+
return textResult({
|
|
25841
|
+
scopeId: mount.scopeId,
|
|
25842
|
+
appId: mount.appId,
|
|
25843
|
+
appliedAt: mount.appliedAt,
|
|
25844
|
+
agents: installed.agents,
|
|
25845
|
+
workflows: installed.workflows,
|
|
25846
|
+
unvalidatedAgentTools: installed.unvalidatedAgentTools
|
|
25847
|
+
});
|
|
25848
|
+
}
|
|
25849
|
+
);
|
|
25850
|
+
server.tool(
|
|
25851
|
+
"app_unapply",
|
|
25852
|
+
"Remove an app from a scope. Refuses if another applied app in the same scope requires this one.",
|
|
25853
|
+
{
|
|
25854
|
+
appId: z.string(),
|
|
25855
|
+
scopeId: z.string().optional().describe("Scope to unapply from. Defaults to 'root'.")
|
|
25856
|
+
},
|
|
25857
|
+
async (input) => {
|
|
25858
|
+
const scopeId = input.scopeId ?? "root";
|
|
25859
|
+
const applied = appRegistry.listApplied(scopeId);
|
|
25860
|
+
const dependents = [];
|
|
25861
|
+
for (const mount of applied) {
|
|
25862
|
+
if (mount.appId === input.appId) continue;
|
|
25863
|
+
const app = appRegistry.getApp(mount.appId);
|
|
25864
|
+
if (app?.requires?.includes(input.appId)) {
|
|
25865
|
+
dependents.push(mount.appId);
|
|
25866
|
+
}
|
|
25867
|
+
}
|
|
25868
|
+
if (dependents.length > 0) {
|
|
25869
|
+
return errorResult(
|
|
25870
|
+
`app_unapply: cannot unapply app "${input.appId}" from scope "${scopeId}" \u2014 the following apps in this scope require it: ${dependents.join(", ")}`
|
|
25871
|
+
);
|
|
25872
|
+
}
|
|
25873
|
+
const removed = appRegistry.unapplyApp({ scopeId, appId: input.appId });
|
|
25874
|
+
if (!removed) {
|
|
25875
|
+
return errorResult(`app_unapply: app "${input.appId}" is not applied to scope "${scopeId}".`);
|
|
25876
|
+
}
|
|
25877
|
+
return textResult({ scopeId: removed.scopeId, appId: removed.appId, appliedAt: removed.appliedAt });
|
|
25878
|
+
}
|
|
25879
|
+
);
|
|
25880
|
+
server.tool(
|
|
25881
|
+
"app_list_applied",
|
|
25882
|
+
"List applied mounts, optionally filtered by scope. Each mount is joined with its installed app summary.",
|
|
25883
|
+
{
|
|
25884
|
+
scopeId: z.string().optional().describe("Filter by scope. Omit to list all scopes.")
|
|
25885
|
+
},
|
|
25886
|
+
async (input) => {
|
|
25887
|
+
const mounts = appRegistry.listApplied(input.scopeId);
|
|
25888
|
+
const result = mounts.map((mount) => {
|
|
25889
|
+
const app = appRegistry.getApp(mount.appId);
|
|
25890
|
+
return {
|
|
25891
|
+
scopeId: mount.scopeId,
|
|
25892
|
+
appId: mount.appId,
|
|
25893
|
+
appliedAt: mount.appliedAt,
|
|
25894
|
+
...app ? {
|
|
25895
|
+
agents: app.agents,
|
|
25896
|
+
workflows: app.workflows,
|
|
25897
|
+
unvalidatedAgentTools: app.unvalidatedAgentTools
|
|
25898
|
+
} : {}
|
|
25899
|
+
};
|
|
25900
|
+
});
|
|
25901
|
+
return textResult(result);
|
|
25902
|
+
}
|
|
25903
|
+
);
|
|
25904
|
+
}
|
|
25263
25905
|
function createSessionEventBus() {
|
|
25264
25906
|
const ee = new EventEmitter();
|
|
25265
25907
|
ee.setMaxListeners(100);
|
|
@@ -25433,7 +26075,8 @@ var SessionsRegistryAgentHost = class {
|
|
|
25433
26075
|
cwd,
|
|
25434
26076
|
workspaceSlug,
|
|
25435
26077
|
sandbox,
|
|
25436
|
-
label: `agent-step:${adapter}
|
|
26078
|
+
label: `agent-step:${adapter}`,
|
|
26079
|
+
...opts.options !== void 0 ? { options: opts.options } : {}
|
|
25437
26080
|
}
|
|
25438
26081
|
);
|
|
25439
26082
|
if (!result.ok) {
|
|
@@ -25452,7 +26095,8 @@ var SessionsRegistryAgentHost = class {
|
|
|
25452
26095
|
env: {
|
|
25453
26096
|
[SESSION_ID_ENV]: stepSessionId,
|
|
25454
26097
|
[WORKSPACE_SLUG_ENV]: workspaceSlug
|
|
25455
|
-
}
|
|
26098
|
+
},
|
|
26099
|
+
...opts.options !== void 0 ? { options: opts.options } : {}
|
|
25456
26100
|
});
|
|
25457
26101
|
const desc = this.registry.spawnAgent({
|
|
25458
26102
|
id: stepSessionId,
|
|
@@ -25648,16 +26292,32 @@ function translateStages(stages, workflowId) {
|
|
|
25648
26292
|
const branches = stage.steps.map((step) => ({
|
|
25649
26293
|
id: step.label,
|
|
25650
26294
|
steps: [
|
|
25651
|
-
{
|
|
25652
|
-
|
|
25653
|
-
|
|
26295
|
+
buildAgentStep(step.label, {
|
|
26296
|
+
prompt: (b) => {
|
|
26297
|
+
const base = step.prompt ?? "";
|
|
26298
|
+
const prevTexts = [];
|
|
26299
|
+
if (b.steps && typeof b.steps === "object") {
|
|
26300
|
+
for (const [id, val] of Object.entries(b.steps)) {
|
|
26301
|
+
if (val && typeof val === "object" && "text" in val) {
|
|
26302
|
+
const text9 = val.text;
|
|
26303
|
+
if (text9) prevTexts.push(`[Output from step "${id}"]
|
|
26304
|
+
${text9}`);
|
|
26305
|
+
}
|
|
26306
|
+
}
|
|
26307
|
+
}
|
|
26308
|
+
if (prevTexts.length > 0) return `${prevTexts.join("\n\n")}
|
|
26309
|
+
|
|
26310
|
+
---
|
|
26311
|
+
|
|
26312
|
+
${base}`;
|
|
26313
|
+
return base;
|
|
26314
|
+
},
|
|
25654
26315
|
...step.adapter !== void 0 ? { adapter: step.adapter } : {},
|
|
25655
26316
|
...step.sessionRef !== void 0 ? { sessionRef: step.sessionRef } : {},
|
|
25656
26317
|
...step.sandbox !== void 0 ? { sandbox: step.sandbox } : {},
|
|
25657
26318
|
...step.cacheable ? { cacheable: true } : {},
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
}
|
|
26319
|
+
policy: step.policy
|
|
26320
|
+
})
|
|
25661
26321
|
]
|
|
25662
26322
|
}));
|
|
25663
26323
|
return {
|
|
@@ -25710,7 +26370,7 @@ function runtimeWorkflowToStages(workflow) {
|
|
|
25710
26370
|
}
|
|
25711
26371
|
];
|
|
25712
26372
|
}
|
|
25713
|
-
var
|
|
26373
|
+
var DEFAULT_PERSIST_PATH3 = () => join(homedir(), ".agentproto", "workflow-runs.json");
|
|
25714
26374
|
function loadRuns(persistPath) {
|
|
25715
26375
|
const result = /* @__PURE__ */ new Map();
|
|
25716
26376
|
if (!existsSync(persistPath)) return result;
|
|
@@ -25825,7 +26485,7 @@ function fillStepStates(stages, defs, agents) {
|
|
|
25825
26485
|
}
|
|
25826
26486
|
return sessionIds;
|
|
25827
26487
|
}
|
|
25828
|
-
async function executeRunWorkflow(state, runtimeWf, agents, signal, cache, cacheKey, input) {
|
|
26488
|
+
async function executeRunWorkflow(state, runtimeWf, agents, signal, cache, cacheKey, input, persist) {
|
|
25829
26489
|
try {
|
|
25830
26490
|
await runWorkflow({
|
|
25831
26491
|
workflow: runtimeWf,
|
|
@@ -25834,13 +26494,49 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, cache, cache
|
|
|
25834
26494
|
cwd: state.cwd,
|
|
25835
26495
|
workspaceSlug: state.workspaceSlug,
|
|
25836
26496
|
input,
|
|
25837
|
-
...cache ? { cache, cacheKey } : {}
|
|
26497
|
+
...cache ? { cache, cacheKey } : {},
|
|
26498
|
+
onStepStart: (stepId) => {
|
|
26499
|
+
for (const stage of state.run.stages) {
|
|
26500
|
+
const step = stage.steps.find((s) => s.label === stepId);
|
|
26501
|
+
if (step) {
|
|
26502
|
+
if (step.status === "pending") {
|
|
26503
|
+
step.status = "running";
|
|
26504
|
+
step.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26505
|
+
}
|
|
26506
|
+
if (stage.status === "pending") {
|
|
26507
|
+
stage.status = "running";
|
|
26508
|
+
}
|
|
26509
|
+
persist?.();
|
|
26510
|
+
break;
|
|
26511
|
+
}
|
|
26512
|
+
}
|
|
26513
|
+
},
|
|
26514
|
+
onStepComplete: (stepId, output) => {
|
|
26515
|
+
for (const stage of state.run.stages) {
|
|
26516
|
+
const step = stage.steps.find((s) => s.label === stepId);
|
|
26517
|
+
if (step) {
|
|
26518
|
+
step.status = "done";
|
|
26519
|
+
step.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26520
|
+
if (output && typeof output === "object" && "sessionId" in output) {
|
|
26521
|
+
step.sessionId = output.sessionId;
|
|
26522
|
+
}
|
|
26523
|
+
const allDone = stage.steps.every((s) => s.status === "done");
|
|
26524
|
+
if (allDone) {
|
|
26525
|
+
stage.status = "done";
|
|
26526
|
+
}
|
|
26527
|
+
persist?.();
|
|
26528
|
+
break;
|
|
26529
|
+
}
|
|
26530
|
+
}
|
|
26531
|
+
}
|
|
25838
26532
|
});
|
|
25839
26533
|
for (const stage of state.run.stages) {
|
|
25840
|
-
stage.status = "done";
|
|
26534
|
+
if (stage.status !== "done") stage.status = "done";
|
|
25841
26535
|
for (const step of stage.steps) {
|
|
25842
|
-
step.status
|
|
25843
|
-
|
|
26536
|
+
if (step.status !== "done") {
|
|
26537
|
+
step.status = "done";
|
|
26538
|
+
step.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26539
|
+
}
|
|
25844
26540
|
}
|
|
25845
26541
|
}
|
|
25846
26542
|
state.run.status = "done";
|
|
@@ -25875,7 +26571,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, cache, cache
|
|
|
25875
26571
|
}
|
|
25876
26572
|
function createWorkflowRunner(opts) {
|
|
25877
26573
|
const { registry, sessionEvents, resolveAgentAdapter, compileWorkflow: compileWorkflow2 } = opts;
|
|
25878
|
-
const persistPath = opts.persistPath ??
|
|
26574
|
+
const persistPath = opts.persistPath ?? DEFAULT_PERSIST_PATH3();
|
|
25879
26575
|
const shouldPersist = opts.persist ?? opts.persistPath !== void 0;
|
|
25880
26576
|
const runs = shouldPersist ? loadRuns(persistPath) : /* @__PURE__ */ new Map();
|
|
25881
26577
|
const persist = () => {
|
|
@@ -25926,7 +26622,7 @@ function createWorkflowRunner(opts) {
|
|
|
25926
26622
|
}
|
|
25927
26623
|
);
|
|
25928
26624
|
const cache = input.cacheKey ? createFileStepCache(input.cacheKey) : void 0;
|
|
25929
|
-
void executeRunWorkflow(state, workflow, agents, abort.signal, cache, input.cacheKey).then(() => {
|
|
26625
|
+
void executeRunWorkflow(state, workflow, agents, abort.signal, cache, input.cacheKey, void 0, persist).then(() => {
|
|
25930
26626
|
persist();
|
|
25931
26627
|
});
|
|
25932
26628
|
return run;
|
|
@@ -25987,7 +26683,8 @@ function createWorkflowRunner(opts) {
|
|
|
25987
26683
|
abort.signal,
|
|
25988
26684
|
cache,
|
|
25989
26685
|
args.cacheKey,
|
|
25990
|
-
args.input
|
|
26686
|
+
args.input,
|
|
26687
|
+
persist
|
|
25991
26688
|
).then(() => {
|
|
25992
26689
|
persist();
|
|
25993
26690
|
});
|
|
@@ -26620,7 +27317,7 @@ function toDescriptor2(state) {
|
|
|
26620
27317
|
spawned: state.spawned
|
|
26621
27318
|
};
|
|
26622
27319
|
}
|
|
26623
|
-
var
|
|
27320
|
+
var DEFAULT_PERSIST_PATH4 = () => join(homedir(), ".agentproto", "cron-jobs.json");
|
|
26624
27321
|
var TICK_INTERVAL_MS = 2e4;
|
|
26625
27322
|
function loadJobs(persistPath) {
|
|
26626
27323
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -26678,7 +27375,7 @@ function nextFireDate(cronInstance) {
|
|
|
26678
27375
|
}
|
|
26679
27376
|
function createCronScheduler(opts) {
|
|
26680
27377
|
const { sessionEvents, registry, resolveAgentAdapter, dispatchTool, workspace } = opts;
|
|
26681
|
-
const persistPath = opts.persistPath ??
|
|
27378
|
+
const persistPath = opts.persistPath ?? DEFAULT_PERSIST_PATH4();
|
|
26682
27379
|
const shouldPersist = opts.persist ?? opts.persistPath !== void 0;
|
|
26683
27380
|
const jobs = shouldPersist ? loadJobs(persistPath) : /* @__PURE__ */ new Map();
|
|
26684
27381
|
for (const state of jobs.values()) {
|
|
@@ -27123,79 +27820,6 @@ function createRoutineRegistrar(opts) {
|
|
|
27123
27820
|
}
|
|
27124
27821
|
};
|
|
27125
27822
|
}
|
|
27126
|
-
function collectToolIds(steps, out) {
|
|
27127
|
-
for (const step of steps) {
|
|
27128
|
-
switch (step.kind) {
|
|
27129
|
-
case "tool": {
|
|
27130
|
-
const ref = typeof step.tool === "string" ? step.tool : step.tool.entry;
|
|
27131
|
-
out.add(normalizeToolId(ref));
|
|
27132
|
-
break;
|
|
27133
|
-
}
|
|
27134
|
-
case "map":
|
|
27135
|
-
case "loop":
|
|
27136
|
-
collectToolIds(step.steps, out);
|
|
27137
|
-
break;
|
|
27138
|
-
case "parallel":
|
|
27139
|
-
for (const branch of step.branches) collectToolIds(branch.steps, out);
|
|
27140
|
-
break;
|
|
27141
|
-
}
|
|
27142
|
-
}
|
|
27143
|
-
}
|
|
27144
|
-
function passthroughToolHandle(id) {
|
|
27145
|
-
return {
|
|
27146
|
-
id,
|
|
27147
|
-
name: id,
|
|
27148
|
-
description: `Daemon tool '${id}', dispatched via the routine/cron dispatchTool bridge.`,
|
|
27149
|
-
mutates: [],
|
|
27150
|
-
requires: { network: [], secrets: [], tools: [] },
|
|
27151
|
-
approval: "auto",
|
|
27152
|
-
riskLevel: 0,
|
|
27153
|
-
costClass: "trivial",
|
|
27154
|
-
timeoutMs: 3e4,
|
|
27155
|
-
tags: [],
|
|
27156
|
-
metadata: {},
|
|
27157
|
-
idempotent: false,
|
|
27158
|
-
driverConstraints: { forbid: [], requireKind: [] }
|
|
27159
|
-
};
|
|
27160
|
-
}
|
|
27161
|
-
function unwrapDispatchResult(result) {
|
|
27162
|
-
if (!result || typeof result !== "object" || !("content" in result)) return result;
|
|
27163
|
-
const r = result;
|
|
27164
|
-
const text9 = (r.content ?? []).map((c) => c.text ?? "").join("\n").trim();
|
|
27165
|
-
if (r.isError) throw new Error(text9 || "tool call failed");
|
|
27166
|
-
if (!text9) return void 0;
|
|
27167
|
-
try {
|
|
27168
|
-
return JSON.parse(text9);
|
|
27169
|
-
} catch {
|
|
27170
|
-
return text9;
|
|
27171
|
-
}
|
|
27172
|
-
}
|
|
27173
|
-
function createDaemonToolRegistry(handle, dispatchTool) {
|
|
27174
|
-
const toolIds = /* @__PURE__ */ new Set();
|
|
27175
|
-
collectToolIds(handle.steps, toolIds);
|
|
27176
|
-
const tools = {};
|
|
27177
|
-
const execute = {};
|
|
27178
|
-
for (const id of toolIds) {
|
|
27179
|
-
tools[id] = passthroughToolHandle(id);
|
|
27180
|
-
execute[id] = async ({ input }) => unwrapDispatchResult(await dispatchTool(id, input ?? {}));
|
|
27181
|
-
}
|
|
27182
|
-
if (toolIds.size === 0) return { tools, candidates: [] };
|
|
27183
|
-
const daemonDriver = {
|
|
27184
|
-
id: "daemon-tool-dispatch",
|
|
27185
|
-
name: "Daemon tool dispatch",
|
|
27186
|
-
description: "Catch-all AIP-30 provider routing every referenced WORKFLOW.md tool step through the daemon's dispatchTool.",
|
|
27187
|
-
kind: "builtin",
|
|
27188
|
-
implements: [...toolIds].map((id) => ({ tool: id, version: "*" })),
|
|
27189
|
-
execute,
|
|
27190
|
-
install: [],
|
|
27191
|
-
network: { egress: [], ingress: [] },
|
|
27192
|
-
region: ["global"],
|
|
27193
|
-
policyTags: [],
|
|
27194
|
-
tags: [],
|
|
27195
|
-
metadata: {}
|
|
27196
|
-
};
|
|
27197
|
-
return { tools, candidates: [daemonDriver] };
|
|
27198
|
-
}
|
|
27199
27823
|
var DEFAULT_ORCHESTRATOR_TOOLS = [
|
|
27200
27824
|
"agent_start",
|
|
27201
27825
|
"agent_prompt",
|
|
@@ -30776,6 +31400,13 @@ async function createGateway(opts) {
|
|
|
30776
31400
|
}
|
|
30777
31401
|
return dispatchToolBox.fn(name, inputs);
|
|
30778
31402
|
};
|
|
31403
|
+
const listToolIdsBox = {};
|
|
31404
|
+
const listRegisteredToolIds = async () => {
|
|
31405
|
+
if (!listToolIdsBox.fn) {
|
|
31406
|
+
throw new Error("app_install: tool registry not ready yet (daemon still booting)");
|
|
31407
|
+
}
|
|
31408
|
+
return listToolIdsBox.fn();
|
|
31409
|
+
};
|
|
30779
31410
|
const cronScheduler = createCronScheduler({
|
|
30780
31411
|
sessionEvents,
|
|
30781
31412
|
registry: sessions,
|
|
@@ -30789,6 +31420,7 @@ async function createGateway(opts) {
|
|
|
30789
31420
|
cronScheduler,
|
|
30790
31421
|
dispatchTool
|
|
30791
31422
|
});
|
|
31423
|
+
const appRegistry = createAppRegistry();
|
|
30792
31424
|
const workflowRunner = opts.resolveAgentAdapter ? createWorkflowRunner({
|
|
30793
31425
|
registry: sessions,
|
|
30794
31426
|
sessionEvents,
|
|
@@ -30805,7 +31437,13 @@ async function createGateway(opts) {
|
|
|
30805
31437
|
// the same `dispatchTool` the routine registrar / cron scheduler use
|
|
30806
31438
|
// (see `workflow-tool-registry.ts`). Agent-step workflows are
|
|
30807
31439
|
// unaffected: an empty tool registry compiles them exactly as before.
|
|
30808
|
-
|
|
31440
|
+
// `agentRefs` resolves a declarative agent-step's `agent.ref` against
|
|
31441
|
+
// whichever installed app bundles this workflow id (undefined when
|
|
31442
|
+
// none does — a plain `workflow_run_file` outside any app).
|
|
31443
|
+
compileWorkflow: (handle) => compileWorkflow(handle, {
|
|
31444
|
+
...createDaemonToolRegistry(handle, dispatchTool),
|
|
31445
|
+
agentRefs: resolveAgentRefsForWorkflow(appRegistry, handle.id)
|
|
31446
|
+
})
|
|
30809
31447
|
}) : void 0;
|
|
30810
31448
|
const operatorWorkspaceSlug = await loadWorkspacesConfig().then((cfg) => getActiveWorkspace(cfg)?.slug).catch(() => void 0);
|
|
30811
31449
|
const taskLedger = createTaskLedger({
|
|
@@ -30981,6 +31619,13 @@ async function createGateway(opts) {
|
|
|
30981
31619
|
endpointStore: inboundEndpointStore,
|
|
30982
31620
|
telegramCreds: telegramBotCreds
|
|
30983
31621
|
});
|
|
31622
|
+
registerAppTools(server, {
|
|
31623
|
+
registry: sessions,
|
|
31624
|
+
listRegisteredToolIds,
|
|
31625
|
+
appRegistry,
|
|
31626
|
+
...opts.resolveAgentAdapter ? { resolveAgentAdapter: opts.resolveAgentAdapter } : {},
|
|
31627
|
+
...workflowRunner ? { workflowRunner } : {}
|
|
31628
|
+
});
|
|
30984
31629
|
registerTelegramBotTools(server, { telegramCreds: telegramBotCreds });
|
|
30985
31630
|
const listSessionsFiltered = (filter) => {
|
|
30986
31631
|
let rows = sessions.list().filter((s) => s.kind !== "command");
|
|
@@ -31078,6 +31723,12 @@ async function createGateway(opts) {
|
|
|
31078
31723
|
}
|
|
31079
31724
|
return tool.handler(inputs, {});
|
|
31080
31725
|
};
|
|
31726
|
+
listToolIdsBox.fn = async () => {
|
|
31727
|
+
if (!internalToolServerPromise) internalToolServerPromise = mcpServerFactory();
|
|
31728
|
+
const internalServer = await internalToolServerPromise;
|
|
31729
|
+
const internal = internalServer;
|
|
31730
|
+
return Object.keys(internal._registeredTools ?? {});
|
|
31731
|
+
};
|
|
31081
31732
|
try {
|
|
31082
31733
|
routineRegistrar.reconcile();
|
|
31083
31734
|
} catch (err) {
|
|
@@ -31136,6 +31787,9 @@ async function createGateway(opts) {
|
|
|
31136
31787
|
activityProjector,
|
|
31137
31788
|
taskLedger,
|
|
31138
31789
|
...workflowRunner ? { workflowRunner } : {},
|
|
31790
|
+
appRegistry,
|
|
31791
|
+
performAppInstall: performInstall,
|
|
31792
|
+
listRegisteredToolIds,
|
|
31139
31793
|
// POST /inbound push ingress — same shared transmitterBindings store
|
|
31140
31794
|
// and liveness/restart adapters the inbound watcher's "route"/
|
|
31141
31795
|
// "route-or-spawn" modes use above. No `spawnForContact`: an
|