@schlenkr/ragents 0.1.14 → 0.1.15
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/apps/server/package.json +1 -1
- package/apps/server/src/api/contracts.ts +19 -1
- package/apps/server/src/api/core-methods.ts +12 -0
- package/apps/server/src/chat-handler.ts +19 -0
- package/apps/server/src/host-api.json +2 -0
- package/apps/server/src/host-api.ts +2 -2
- package/apps/server/src/plugin-build/build.ts +1 -1
- package/apps/server/src/plugin-support/actor-programs/app-project.ts +61 -17
- package/apps/server/src/plugin-support/actor-programs/contract.ts +18 -1
- package/apps/server/src/plugin-support/actor-programs/service.ts +22 -1
- package/apps/server/src/plugin-support/plugin-folder.ts +14 -1
- package/apps/server/src/plugin-support/run-scripts.ts +27 -2
- package/apps/server/src/profile/compose.ts +2 -0
- package/apps/server/src/provider.ts +23 -4
- package/apps/server/src/ragents/chat-projection.ts +7 -0
- package/apps/server/src/ragents/global-chat.ts +5 -1
- package/apps/server/src/ragents/run-script-tools.ts +66 -0
- package/apps/server/src/ragents/session.ts +122 -60
- package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-BslIwn_y.js → highlighted-body-KPVGNVTW-BwD4uYEe.js} +1 -1
- package/apps/web/dist/assets/{main-BUN8m8SW.js → main-KBrc5Q77.js} +1 -1
- package/apps/web/dist/assets/{mermaid-HWGCJPDP-B1erZlFM.js → mermaid-HWGCJPDP-CSCkaArr.js} +88 -88
- package/apps/web/dist/assets/runPanel-BjG5NBKm.js +26 -0
- package/apps/web/dist/help/guide-clients.html +21 -4
- package/apps/web/dist/help/guide-clients.md +23 -4
- package/apps/web/dist/help/guide-plugins.html +12 -2
- package/apps/web/dist/help/guide-plugins.md +9 -2
- package/apps/web/dist/help/guide-programs.html +8 -2
- package/apps/web/dist/help/guide-programs.md +9 -2
- package/apps/web/dist/help/guide-runtime.html +3 -2
- package/apps/web/dist/help/guide-runtime.md +3 -2
- package/apps/web/dist/help/index.html +1 -1
- package/apps/web/dist/help/mini-app.css +7 -2
- package/apps/web/dist/host-web.json +6 -6
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/run-panel.html +2 -2
- package/apps/web/package.json +1 -1
- package/apps/web/src/run-panel/RunPanelApp.tsx +1 -1
- package/apps/web/src/run-panel/RunPanelMenu.tsx +47 -11
- package/bundles/ragents.activity/ragents-bundle.json +1 -1
- package/bundles/ragents.actor-programs/ragents-bundle.json +4 -2
- package/bundles/ragents.actor-programs/server/index.js +437 -24
- package/bundles/ragents.actor-programs/server/index.js.map +4 -4
- package/bundles/ragents.actor-programs/web/index.js.map +1 -1
- package/bundles/ragents.ask/ragents-bundle.json +2 -2
- package/bundles/ragents.ask/server/index.js +1 -0
- package/bundles/ragents.ask/server/index.js.map +2 -2
- package/bundles/ragents.browser/ragents-bundle.json +1 -1
- package/bundles/ragents.documents/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
- package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
- package/bundles/ragents.orchestration/ragents-bundle.json +7 -4
- package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js +11 -0
- package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js.map +7 -0
- package/bundles/ragents.orchestration/server/exports/server/contract.js +9 -0
- package/bundles/ragents.orchestration/server/exports/server/contract.js.map +7 -0
- package/bundles/ragents.orchestration/server/index.js +49 -3
- package/bundles/ragents.orchestration/server/index.js.map +2 -2
- package/bundles/ragents.orchestration/surface.hbs +5 -0
- package/bundles/ragents.overseer/ragents-bundle.json +1 -1
- package/bundles/ragents.processes/ragents-bundle.json +1 -1
- package/bundles/ragents.product/ragents-bundle.json +1 -1
- package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
- package/bundles/ragents.reference/actors/notebook/package.json +16 -0
- package/bundles/ragents.reference/actors/notebook/src/client.tsx +31 -0
- package/bundles/ragents.reference/actors/notebook/src/server.ts +27 -0
- package/bundles/ragents.reference/actors/notebook/tests/program.test.ts +27 -0
- package/bundles/ragents.reference/ragents-bundle.json +4 -3
- package/bundles/ragents.reference/run-scripts/quick-note/RUN.md +14 -0
- package/bundles/ragents.reference/run-scripts/quick-note/package.json +9 -0
- package/bundles/ragents.reference/run-scripts/quick-note/src/server.ts +29 -0
- package/bundles/ragents.reference/run-scripts/quick-note/tests/program.test.ts +43 -0
- package/bundles/ragents.reference/run-scripts/run-roster/RUN.md +19 -0
- package/bundles/ragents.reference/run-scripts/run-roster/package.json +9 -0
- package/bundles/ragents.reference/run-scripts/run-roster/src/server.ts +26 -0
- package/bundles/ragents.reference/run-scripts/run-roster/tests/program.test.ts +47 -0
- package/bundles/ragents.todo/ragents-bundle.json +1 -1
- package/bundles/ragents.transcript/ragents-bundle.json +1 -1
- package/bundles/ragents.watch/ragents-bundle.json +1 -1
- package/bundles/ragents.workspace/ragents-bundle.json +1 -1
- package/package.json +3 -3
- package/packages/ragents/src/domain/vocabulary.ts +4 -0
- package/packages/ragents/src/index.ts +3 -1
- package/packages/ragents/src/plugin-host.ts +62 -10
- package/packages/ragents/src/plugin-types.ts +11 -0
- package/packages/ragents/src/runtime/guards.ts +2 -1
- package/packages/ragents/src/typescript/async-compiler.ts +11 -1
- package/plugins/ragents.actor-programs/server/index.ts +3 -0
- package/plugins/ragents.actor-programs/server/runtime.ts +424 -28
- package/plugins/ragents.actor-programs/server/script-state.ts +54 -0
- package/plugins/ragents.actor-programs/server/tool-contributor.ts +2 -0
- package/plugins/ragents.ask/server/ask-tool.ts +1 -0
- package/plugins/ragents.orchestration/ragents-plugin.json +1 -0
- package/plugins/ragents.orchestration/server/contract.ts +14 -0
- package/plugins/ragents.orchestration/server/index.ts +4 -1
- package/plugins/ragents.orchestration/server/surface-tool.ts +58 -2
- package/plugins/ragents.orchestration/surface.hbs +5 -0
- package/plugins/ragents.reference/actors/notebook/package.json +16 -0
- package/plugins/ragents.reference/actors/notebook/src/client.tsx +31 -0
- package/plugins/ragents.reference/actors/notebook/src/server.ts +27 -0
- package/plugins/ragents.reference/actors/notebook/tests/program.test.ts +27 -0
- package/plugins/ragents.reference/run-scripts/quick-note/RUN.md +14 -0
- package/plugins/ragents.reference/run-scripts/quick-note/package.json +9 -0
- package/plugins/ragents.reference/run-scripts/quick-note/src/server.ts +29 -0
- package/plugins/ragents.reference/run-scripts/quick-note/tests/program.test.ts +43 -0
- package/plugins/ragents.reference/run-scripts/run-roster/RUN.md +19 -0
- package/plugins/ragents.reference/run-scripts/run-roster/package.json +9 -0
- package/plugins/ragents.reference/run-scripts/run-roster/src/server.ts +26 -0
- package/plugins/ragents.reference/run-scripts/run-roster/tests/program.test.ts +47 -0
- package/scripts/agent/agent-cli.ts +46 -2
- package/scripts/package/ragents.mjs +3 -1
- package/apps/web/dist/assets/runPanel-s8gGpr9M.js +0 -21
|
@@ -5,6 +5,7 @@ const require = __ragentsCreateRequire(import.meta.url);
|
|
|
5
5
|
import path3 from "node:path";
|
|
6
6
|
import { runtimeProviderToken as runtimeProviderToken2, runGuardToken } from "@ragents/host/ragents/host-services.js";
|
|
7
7
|
import { askServiceToken } from "@ragents/plugins/ragents.ask/server/contract";
|
|
8
|
+
import { surfacePlacementToken } from "@ragents/plugins/ragents.orchestration/server/contract";
|
|
8
9
|
|
|
9
10
|
// server/agent-tools.ts
|
|
10
11
|
import { ToolRegistry } from "@ragents/engine";
|
|
@@ -324,8 +325,9 @@ import { randomUUID } from "node:crypto";
|
|
|
324
325
|
import { lstat, mkdir, readdir, readFile, realpath as realpath2, rm, writeFile } from "node:fs/promises";
|
|
325
326
|
import { lstatSync, readFileSync, renameSync } from "node:fs";
|
|
326
327
|
import path from "node:path";
|
|
328
|
+
import { isDeepStrictEqual } from "node:util";
|
|
327
329
|
import { Value } from "typebox/value";
|
|
328
|
-
import { actorByHandle, actorDescriptionMaxLength, agentTools, assertJsonValue as assertJsonValue2, canonicalHash, defineRunFunction, defineToolAvailability, emptyUsage, handleKey, runCapabilityContractHash as runCapabilityContractHash2, scriptInputOf, schemaComplaints } from "@ragents/engine";
|
|
330
|
+
import { actorByHandle, actorDescriptionMaxLength, agentTools, assertJsonValue as assertJsonValue2, canonicalHash, defineRunFunction, defineToolAvailability, emptyUsage, handleKey, inheritedGrants, runCapabilityContractHash as runCapabilityContractHash2, scriptInputOf, schemaComplaints } from "@ragents/engine";
|
|
329
331
|
import { ACTOR_PROGRAMS_STATE_ID, ACTOR_INVOCATIONS_STATE_ID, ACTOR_SCRIPT_STATE_ID, ACTOR_STATE_ID, resolveActorView } from "@ragents/host/plugin-support/actor-programs/contract.js";
|
|
330
332
|
|
|
331
333
|
// server/limits.ts
|
|
@@ -360,6 +362,42 @@ var jsonValue = (value, label) => {
|
|
|
360
362
|
return value;
|
|
361
363
|
};
|
|
362
364
|
|
|
365
|
+
// server/script-state.ts
|
|
366
|
+
var MAX_OPEN_STARTS = 50;
|
|
367
|
+
var MAX_RESULT_CHARACTERS = 32e3;
|
|
368
|
+
var MAX_SUMMARY_LENGTH = 1e3;
|
|
369
|
+
var SHOWN_RESULT_CHARACTERS = 4e3;
|
|
370
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
371
|
+
var isOrigin = (value) => isRecord(value) && (value.kind === "script" && typeof value.entryId === "string" || value.kind === "shared" && typeof value.pluginId === "string");
|
|
372
|
+
var scriptStateOf = (value) => {
|
|
373
|
+
if (!isRecord(value) || value.version !== 2 || !isRecord(value.packages) || !Array.isArray(value.deliveries))
|
|
374
|
+
return { version: 2, packages: {}, deliveries: [] };
|
|
375
|
+
const packages = Object.entries(value.packages).filter(([, entry]) => isRecord(entry) && isOrigin(entry.origin) && typeof entry.identity === "string");
|
|
376
|
+
return { ...value, packages: Object.fromEntries(packages) };
|
|
377
|
+
};
|
|
378
|
+
var finishesOf = (value) => {
|
|
379
|
+
if (value === void 0 || value === null) return [];
|
|
380
|
+
if (!isRecord(value) || !Array.isArray(value.finishes)) throw new Error("The program returned an invalid finish list.");
|
|
381
|
+
return value.finishes.map((entry) => {
|
|
382
|
+
if (!isRecord(entry) || !Number.isInteger(entry.start) || entry.start < 1) throw new Error("finish needs the count of an open start.");
|
|
383
|
+
const result = entry.result ?? null;
|
|
384
|
+
if (JSON.stringify(result).length > MAX_RESULT_CHARACTERS) throw new Error(`The result of finish is larger than ${MAX_RESULT_CHARACTERS} characters as JSON.`);
|
|
385
|
+
if (entry.summary !== void 0 && (typeof entry.summary !== "string" || entry.summary.length > MAX_SUMMARY_LENGTH)) {
|
|
386
|
+
throw new Error(`The summary of finish must be a text of at most ${MAX_SUMMARY_LENGTH} characters.`);
|
|
387
|
+
}
|
|
388
|
+
return { start: entry.start, result, ...typeof entry.summary === "string" ? { summary: entry.summary } : {} };
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
var agentResultText = (handle, finish) => {
|
|
392
|
+
const headline = `Run script @${handle} finished start ${finish.start}${finish.summary ? `: ${finish.summary}` : "."}`;
|
|
393
|
+
if (finish.result === null) return headline;
|
|
394
|
+
const json = JSON.stringify(finish.result);
|
|
395
|
+
const shown = json.length > SHOWN_RESULT_CHARACTERS ? `${json.slice(0, SHOWN_RESULT_CHARACTERS)}... (shortened, ${json.length} characters)` : json;
|
|
396
|
+
return `${headline}
|
|
397
|
+
Result: ${shown}`;
|
|
398
|
+
};
|
|
399
|
+
var scriptResultContent = (handle, finish) => JSON.stringify({ handle, count: finish.start, result: finish.result, ...finish.summary !== void 0 ? { summary: finish.summary } : {} });
|
|
400
|
+
|
|
363
401
|
// server/capability-resolver.ts
|
|
364
402
|
import {
|
|
365
403
|
runCapabilityBindingHash,
|
|
@@ -421,7 +459,7 @@ var agentCapabilityBinding = (actor, tools, capabilityIds, blockedNames) => {
|
|
|
421
459
|
|
|
422
460
|
// server/runtime.ts
|
|
423
461
|
import { compileClientProject, installClientSdk } from "@ragents/host/plugin-support/actor-programs/client-compiler.js";
|
|
424
|
-
import { compileAppBackend, installServerSdk, prepareAppProject, prepareAppWorkspace, projectSourceFiles, readAppPackage, typecheckServerProject } from "@ragents/host/plugin-support/actor-programs/app-project.js";
|
|
462
|
+
import { compileAppBackend, installServerSdk, packageIdentity, prepareAppProject, prepareAppWorkspace, projectSourceFiles, readAppPackage, typecheckServerProject } from "@ragents/host/plugin-support/actor-programs/app-project.js";
|
|
425
463
|
import { syncWorkspaceOwnership } from "@ragents/host/plugin-support/workspace-ownership.js";
|
|
426
464
|
import { runManagedProcess, sandboxedLaunch } from "@ragents/workspace-executor";
|
|
427
465
|
|
|
@@ -1023,6 +1061,13 @@ var commitPackage = (staged, directory, name2) => {
|
|
|
1023
1061
|
throw error;
|
|
1024
1062
|
}
|
|
1025
1063
|
};
|
|
1064
|
+
var sameSources = async (directory, files) => packageIdentity(await projectSourceFiles(directory)) === packageIdentity(files);
|
|
1065
|
+
var sameOrigin = (left, right) => left.kind === "script" ? right.kind === "script" && left.entryId === right.entryId : right.kind === "shared" && left.pluginId === right.pluginId;
|
|
1066
|
+
var originText = (origin) => !origin ? "that no run script installed" : origin.kind === "script" ? `from the run script ${origin.entryId}` : `shared by ${origin.pluginId}`;
|
|
1067
|
+
var foreignShared = (name2, pluginId, record2) => {
|
|
1068
|
+
const holder = !record2 ? "that the host did not install" : sameOrigin(record2.origin, { kind: "shared", pluginId }) ? "that was changed in the run" : originText(record2.origin);
|
|
1069
|
+
return new Error(`${name2} is the shared actor package of ${pluginId}, but this run has a package ${name2} ${holder}; the host neither uses nor replaces it. Remove or rename that package.`);
|
|
1070
|
+
};
|
|
1026
1071
|
var descriptor = (tool) => ({ id: tool.name, label: tool.label, description: tool.description, schema: tool.schema, resultSchema: tool.resultSchema });
|
|
1027
1072
|
var ActorProgramRuntime = class {
|
|
1028
1073
|
#options;
|
|
@@ -1033,6 +1078,9 @@ var ActorProgramRuntime = class {
|
|
|
1033
1078
|
#staged = /* @__PURE__ */ new Set();
|
|
1034
1079
|
#removing = /* @__PURE__ */ new Set();
|
|
1035
1080
|
#knownRuns = /* @__PURE__ */ new Set();
|
|
1081
|
+
#recovered = /* @__PURE__ */ new Set();
|
|
1082
|
+
#packageLocks = /* @__PURE__ */ new Map();
|
|
1083
|
+
#identities = /* @__PURE__ */ new Map();
|
|
1036
1084
|
#epochs = /* @__PURE__ */ new Map();
|
|
1037
1085
|
#stopping = /* @__PURE__ */ new Set();
|
|
1038
1086
|
#shuttingDown = false;
|
|
@@ -1088,8 +1136,41 @@ var ActorProgramRuntime = class {
|
|
|
1088
1136
|
#writeProgram(context, runId, actorId, program) {
|
|
1089
1137
|
this.runtime().replacePluginState(this.operatorContext(runId), runId, { pluginId: ACTOR_PROGRAMS_STATE_ID, scope: { kind: "actor", actorId }, state: jsonValue({ version: 1, program }, "Actor program") });
|
|
1090
1138
|
}
|
|
1139
|
+
/** Which run script installed which package, and the inputs the host queued for programs to recognize. */
|
|
1091
1140
|
#script(runId) {
|
|
1092
|
-
return this.view(runId).pluginStates.find((entry) => entry.pluginId === ACTOR_SCRIPT_STATE_ID && entry.scope.kind === "run")?.state;
|
|
1141
|
+
return scriptStateOf(this.view(runId).pluginStates.find((entry) => entry.pluginId === ACTOR_SCRIPT_STATE_ID && entry.scope.kind === "run")?.state);
|
|
1142
|
+
}
|
|
1143
|
+
/** Keeps only deliveries whose input is still queued or being processed, and `keep`, whose input follows right after this write. */
|
|
1144
|
+
#writeScript(runId, state, keep) {
|
|
1145
|
+
const live = this.#liveCommands(runId, new Set(state.deliveries.map((delivery) => delivery.actorId)));
|
|
1146
|
+
const deliveries = state.deliveries.filter((delivery) => delivery.commandId === keep || live.has(delivery.commandId));
|
|
1147
|
+
this.runtime().replacePluginState(this.operatorContext(runId, "script"), runId, { pluginId: ACTOR_SCRIPT_STATE_ID, scope: { kind: "run" }, state: jsonValue({ ...state, deliveries }, "Run script state") });
|
|
1148
|
+
}
|
|
1149
|
+
/** The commands that queued inputs of these actors which are pending or belong to a running turn. */
|
|
1150
|
+
#liveCommands(runId, actorIds) {
|
|
1151
|
+
const view = this.view(runId);
|
|
1152
|
+
const running = new Set(view.turns.filter((turn) => turn.status === "running").map((turn) => turn.id));
|
|
1153
|
+
const sequences = new Set(view.inputs.filter((input) => actorIds.has(input.actorId) && (input.lifecycle.kind === "pending" || input.lifecycle.kind === "claimed" && running.has(input.lifecycle.turnId))).map((input) => input.sequence));
|
|
1154
|
+
const commands = /* @__PURE__ */ new Set();
|
|
1155
|
+
if (sequences.size === 0)
|
|
1156
|
+
return commands;
|
|
1157
|
+
const oldest = Math.min(...sequences);
|
|
1158
|
+
for (const event of this.runtime().recentEvents(runId)) {
|
|
1159
|
+
if (event.sequence < oldest)
|
|
1160
|
+
break;
|
|
1161
|
+
if (sequences.has(event.sequence))
|
|
1162
|
+
commands.add(event.commandId);
|
|
1163
|
+
}
|
|
1164
|
+
return commands;
|
|
1165
|
+
}
|
|
1166
|
+
#commandOf(runId, sequence) {
|
|
1167
|
+
for (const event of this.runtime().recentEvents(runId)) {
|
|
1168
|
+
if (event.sequence === sequence)
|
|
1169
|
+
return event.commandId;
|
|
1170
|
+
if (event.sequence < sequence)
|
|
1171
|
+
return void 0;
|
|
1172
|
+
}
|
|
1173
|
+
return void 0;
|
|
1093
1174
|
}
|
|
1094
1175
|
async #writeSources(directory, files) {
|
|
1095
1176
|
for (const source of files) {
|
|
@@ -1100,10 +1181,31 @@ var ActorProgramRuntime = class {
|
|
|
1100
1181
|
await writeFile(file, source.content, { flag: "wx" });
|
|
1101
1182
|
}
|
|
1102
1183
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1184
|
+
#originSources(name2, origin) {
|
|
1185
|
+
if (origin.kind === "script")
|
|
1186
|
+
return this.#options.scriptSources(origin.entryId, name2);
|
|
1187
|
+
const shared = this.#options.sharedPackage(name2);
|
|
1188
|
+
return shared?.pluginId === origin.pluginId ? shared.files : void 0;
|
|
1189
|
+
}
|
|
1190
|
+
/** The identity of the sources the program was built from; a build never changes, so it is read once. */
|
|
1191
|
+
#identityOf(program) {
|
|
1192
|
+
const known = this.#identities.get(program.directory);
|
|
1193
|
+
if (known !== void 0)
|
|
1194
|
+
return known;
|
|
1195
|
+
const identity = packageIdentity(JSON.parse(readFileSync(path.join(program.directory, "sources.json"), "utf8")));
|
|
1196
|
+
this.#identities.set(program.directory, identity);
|
|
1197
|
+
return identity;
|
|
1198
|
+
}
|
|
1199
|
+
/** A package still built from what its origin installed reloads from the origin's current sources; one changed in the run keeps its own. */
|
|
1200
|
+
#refresh(runId, program, signal) {
|
|
1201
|
+
return this.#withPackages(runId, [program.name], () => this.#reload(runId, program, signal));
|
|
1202
|
+
}
|
|
1203
|
+
async #reload(runId, program, signal) {
|
|
1204
|
+
const record2 = this.#script(runId).packages[program.name];
|
|
1205
|
+
const files = record2 && this.#identityOf(program) === record2.identity ? this.#originSources(program.name, record2.origin) : void 0;
|
|
1206
|
+
if (record2 && files) {
|
|
1207
|
+
const state = this.#script(runId);
|
|
1208
|
+
this.#writeScript(runId, { ...state, packages: { ...state.packages, [program.name]: { ...state.packages[program.name], identity: packageIdentity(files) } } });
|
|
1107
1209
|
const directory = path.join(await this.workspaceDirectory(runId), program.name);
|
|
1108
1210
|
for (const entry of await readdir(directory))
|
|
1109
1211
|
if (entry !== "node_modules")
|
|
@@ -1140,6 +1242,30 @@ var ActorProgramRuntime = class {
|
|
|
1140
1242
|
throw new Error(`The capability contract of ${program.name} still differs after the reactivation.`);
|
|
1141
1243
|
return refreshed;
|
|
1142
1244
|
}
|
|
1245
|
+
async #acquire(key) {
|
|
1246
|
+
const previous = this.#packageLocks.get(key) ?? Promise.resolve();
|
|
1247
|
+
const released = Promise.withResolvers();
|
|
1248
|
+
const held = previous.then(() => released.promise);
|
|
1249
|
+
this.#packageLocks.set(key, held);
|
|
1250
|
+
await previous;
|
|
1251
|
+
return () => {
|
|
1252
|
+
released.resolve();
|
|
1253
|
+
if (this.#packageLocks.get(key) === held)
|
|
1254
|
+
this.#packageLocks.delete(key);
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
/** One exclusion per run and package name for creating, activating, installing and ensuring; callers wait instead of failing. */
|
|
1258
|
+
async #withPackages(runId, names, work) {
|
|
1259
|
+
const releases = [];
|
|
1260
|
+
try {
|
|
1261
|
+
for (const name2 of [...new Set(names)].sort())
|
|
1262
|
+
releases.push(await this.#acquire(`${runId}\0${name2}`));
|
|
1263
|
+
return await work();
|
|
1264
|
+
} finally {
|
|
1265
|
+
for (const release of releases.reverse())
|
|
1266
|
+
release();
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1143
1269
|
async workspaceDirectory(runId) {
|
|
1144
1270
|
this.#knownRuns.add(runId);
|
|
1145
1271
|
return prepareAppWorkspace(path.join(this.#options.directoryFor(runId), "actor-workspace"));
|
|
@@ -1175,7 +1301,10 @@ var ActorProgramRuntime = class {
|
|
|
1175
1301
|
await rm(staged, { recursive: true, force: true });
|
|
1176
1302
|
}
|
|
1177
1303
|
}
|
|
1178
|
-
|
|
1304
|
+
scaffold(runId, name2, templateId) {
|
|
1305
|
+
return this.#withPackages(runId, [name2], () => this.#scaffold(runId, name2, templateId));
|
|
1306
|
+
}
|
|
1307
|
+
async #scaffold(runId, name2, templateId) {
|
|
1179
1308
|
const sources = Object.entries(templateFiles(templateById(templateId), name2)).map(([file, content]) => ({ path: file, content }));
|
|
1180
1309
|
const { built: files } = await this.#create(runId, name2, async (directory) => {
|
|
1181
1310
|
await this.#writeSources(directory, sources);
|
|
@@ -1187,19 +1316,284 @@ var ActorProgramRuntime = class {
|
|
|
1187
1316
|
});
|
|
1188
1317
|
return { name: name2, directory: `@actors/${name2}`, files };
|
|
1189
1318
|
}
|
|
1190
|
-
|
|
1319
|
+
importPackage(context, runId, name2, files, signal) {
|
|
1320
|
+
return this.#withPackages(runId, [name2], () => this.#import(context, runId, name2, files, signal));
|
|
1321
|
+
}
|
|
1322
|
+
async #import(context, runId, name2, files, signal) {
|
|
1191
1323
|
const { directory } = await this.#create(runId, name2, (staged) => this.#writeSources(staged, files));
|
|
1192
1324
|
try {
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
await this.activate(context, runId, name2, signal);
|
|
1196
|
-
const program = this.programs(runId).find((entry) => entry.name === name2);
|
|
1197
|
-
return { name: name2, actorId: program.actorId, actorHandle: program.actorHandle, views: program.views.length };
|
|
1325
|
+
await this.#activate(context, runId, name2, true, signal);
|
|
1326
|
+
return this.#activated(runId, name2);
|
|
1198
1327
|
} catch (error) {
|
|
1199
1328
|
await rm(directory, { recursive: true, force: true });
|
|
1200
1329
|
throw error;
|
|
1201
1330
|
}
|
|
1202
1331
|
}
|
|
1332
|
+
#activated(runId, name2) {
|
|
1333
|
+
const program = this.#program(runId, name2);
|
|
1334
|
+
return { name: name2, actorId: program.actorId, actorHandle: program.actorHandle, views: program.views.length };
|
|
1335
|
+
}
|
|
1336
|
+
installScript(context, runId, script, signal) {
|
|
1337
|
+
return this.#withPackages(runId, [script.handle, ...script.programs.map((program) => program.name), ...script.sharedPrograms], () => this.#install(context, runId, script, signal));
|
|
1338
|
+
}
|
|
1339
|
+
async #install(context, runId, script, signal) {
|
|
1340
|
+
const root = await this.workspaceDirectory(runId);
|
|
1341
|
+
const before = this.#script(runId).packages;
|
|
1342
|
+
const own = { kind: "script", entryId: script.entryId };
|
|
1343
|
+
const programs = [
|
|
1344
|
+
...script.programs.map((program) => ({ ...program, origin: own })),
|
|
1345
|
+
...script.sharedPrograms.map((name2) => {
|
|
1346
|
+
const shared = this.#options.sharedPackage(name2);
|
|
1347
|
+
if (!shared)
|
|
1348
|
+
throw new Error(`The run script ${script.entryId} needs the shared actor package ${name2}, which no plugin of this profile provides.`);
|
|
1349
|
+
return { name: name2, files: shared.files, origin: { kind: "shared", pluginId: shared.pluginId } };
|
|
1350
|
+
})
|
|
1351
|
+
];
|
|
1352
|
+
const present = await this.#presentPrograms(root, script.entryId, programs, before);
|
|
1353
|
+
const reuse = this.#reusesScript(runId, root, script, before);
|
|
1354
|
+
const missing = programs.filter((program) => !present.has(program.name));
|
|
1355
|
+
const claims = this.#claim(runId, [...missing, ...reuse ? [] : [{ name: script.handle, files: script.files, origin: own }]]);
|
|
1356
|
+
const copied = [];
|
|
1357
|
+
try {
|
|
1358
|
+
for (const program of missing) {
|
|
1359
|
+
signal?.throwIfAborted();
|
|
1360
|
+
await this.#create(runId, program.name, (staged) => this.#writeSources(staged, program.files));
|
|
1361
|
+
copied.push(program.name);
|
|
1362
|
+
}
|
|
1363
|
+
return reuse ? await this.#restartScript(context, runId, script.handle, signal) : await this.#import(context, runId, script.handle, script.files, signal);
|
|
1364
|
+
} catch (error) {
|
|
1365
|
+
for (const name2 of copied)
|
|
1366
|
+
await rm(path.join(root, name2), { recursive: true, force: true });
|
|
1367
|
+
try {
|
|
1368
|
+
this.#release(runId, claims);
|
|
1369
|
+
} catch (release) {
|
|
1370
|
+
throw new Error(`${errorText(error)} The ownership record could not be removed again (${errorText(release)}); it names no package and the next start replaces it.`);
|
|
1371
|
+
}
|
|
1372
|
+
throw error;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
/** Ownership is recorded before anything is created, so no package the host installs exists without its origin. */
|
|
1376
|
+
#claim(runId, packages) {
|
|
1377
|
+
const state = this.#script(runId);
|
|
1378
|
+
const claims = packages.map((entry) => {
|
|
1379
|
+
const previous = state.packages[entry.name];
|
|
1380
|
+
return { name: entry.name, previous, claimed: { origin: entry.origin, identity: packageIdentity(entry.files), count: previous && sameOrigin(previous.origin, entry.origin) ? previous.count : 0, open: [] } };
|
|
1381
|
+
});
|
|
1382
|
+
if (claims.length > 0)
|
|
1383
|
+
this.#writeScript(runId, { ...state, packages: { ...state.packages, ...Object.fromEntries(claims.map((claim) => [claim.name, claim.claimed])) } });
|
|
1384
|
+
return claims;
|
|
1385
|
+
}
|
|
1386
|
+
/** Compare and swap: a record someone changed since the claim stays as it is. */
|
|
1387
|
+
#release(runId, claims) {
|
|
1388
|
+
const state = this.#script(runId);
|
|
1389
|
+
const released = claims.filter((claim) => isDeepStrictEqual(state.packages[claim.name], claim.claimed));
|
|
1390
|
+
if (released.length === 0)
|
|
1391
|
+
return;
|
|
1392
|
+
const names = new Set(released.map((claim) => claim.name));
|
|
1393
|
+
const kept = Object.entries(state.packages).filter(([name2]) => !names.has(name2));
|
|
1394
|
+
const restored = released.flatMap((claim) => claim.previous ? [[claim.name, claim.previous]] : []);
|
|
1395
|
+
this.#writeScript(runId, { ...state, packages: Object.fromEntries([...kept, ...restored]) });
|
|
1396
|
+
}
|
|
1397
|
+
/** Whether the host installed this shared package from this plugin and nobody changed it since. */
|
|
1398
|
+
#hostShared(record2, pluginId, identity) {
|
|
1399
|
+
return record2 !== void 0 && sameOrigin(record2.origin, { kind: "shared", pluginId }) && record2.identity === identity;
|
|
1400
|
+
}
|
|
1401
|
+
/** The names already in the run that the start keeps; a different package, or one under a shared name the host did not install, is an error before anything is copied. */
|
|
1402
|
+
async #presentPrograms(root, entryId, programs, packages) {
|
|
1403
|
+
const present = await Promise.all(programs.map(async (program) => {
|
|
1404
|
+
const directory = path.join(root, program.name);
|
|
1405
|
+
if (!occupied(directory))
|
|
1406
|
+
return false;
|
|
1407
|
+
if (program.origin.kind === "shared") {
|
|
1408
|
+
if (this.#hostShared(packages[program.name], program.origin.pluginId, packageIdentity(await projectSourceFiles(directory))))
|
|
1409
|
+
return true;
|
|
1410
|
+
throw foreignShared(program.name, program.origin.pluginId, packages[program.name]);
|
|
1411
|
+
}
|
|
1412
|
+
if (await sameSources(directory, program.files))
|
|
1413
|
+
return true;
|
|
1414
|
+
throw new Error(`The run script ${entryId} bundles the program ${program.name}, but this run already has a different package ${program.name} ${originText(packages[program.name]?.origin)}.`);
|
|
1415
|
+
}));
|
|
1416
|
+
return new Set(programs.filter((_, index) => present[index]).map((program) => program.name));
|
|
1417
|
+
}
|
|
1418
|
+
/** A recorded origin without actor and folder is left over from a failed start, whichever template it names, and counts as not installed. */
|
|
1419
|
+
#reusesScript(runId, root, script, packages) {
|
|
1420
|
+
const holder = this.view(runId).actors.find((actor) => actor.handle === script.handle);
|
|
1421
|
+
const exists = occupied(path.join(root, script.handle));
|
|
1422
|
+
if (!holder && !exists)
|
|
1423
|
+
return false;
|
|
1424
|
+
const origin = packages[script.handle]?.origin;
|
|
1425
|
+
if (origin !== void 0 && !sameOrigin(origin, { kind: "script", entryId: script.entryId }))
|
|
1426
|
+
throw new Error(`The package ${script.handle} in this run ${origin.kind === "script" ? `comes from the run script ${origin.entryId}` : `is the shared package of ${origin.pluginId}`}, not from the run script ${script.entryId}.`);
|
|
1427
|
+
if (origin !== void 0 && exists && (!holder || holder.kind === "script"))
|
|
1428
|
+
return true;
|
|
1429
|
+
throw new Error(holder ? `Handle @${script.handle} already belongs to ${holder.displayName} in this run, not to the run script ${script.entryId}.` : `The package ${script.handle} already exists in this run and was not installed by the run script ${script.entryId}.`);
|
|
1430
|
+
}
|
|
1431
|
+
async #restartScript(context, runId, handle, signal) {
|
|
1432
|
+
const view = this.view(runId);
|
|
1433
|
+
const holder = view.actors.find((actor) => actor.handle === handle);
|
|
1434
|
+
const state = holder && view.pluginStates.find((entry) => entry.pluginId === ACTOR_PROGRAMS_STATE_ID && entry.scope.kind === "actor" && entry.scope.actorId === holder.id)?.state;
|
|
1435
|
+
if (!holder || !state?.program)
|
|
1436
|
+
await this.#activate(context, runId, handle, true, signal);
|
|
1437
|
+
else if (holder.kind !== "human" && holder.lifecycle.kind === "stopped")
|
|
1438
|
+
this.runtime().restartActor(context, runId, holder.id, "Run script started again");
|
|
1439
|
+
return this.#activated(runId, handle);
|
|
1440
|
+
}
|
|
1441
|
+
enqueueStart(context, runId, handle, start) {
|
|
1442
|
+
const program = this.#program(runId, handle);
|
|
1443
|
+
const recorded = this.#script(runId).packages[handle];
|
|
1444
|
+
if (!recorded)
|
|
1445
|
+
throw new Error(`The package ${handle} was not installed by a run script.`);
|
|
1446
|
+
const view = program.views[0];
|
|
1447
|
+
if (start.embedded && view)
|
|
1448
|
+
this.#options.placeView(this.operatorContext(runId, "place"), runId, `app:${view.id}`);
|
|
1449
|
+
const state = this.#script(runId);
|
|
1450
|
+
const record2 = state.packages[handle];
|
|
1451
|
+
const count = record2.count + 1;
|
|
1452
|
+
const delivery = { commandId: context.commandId, actorId: program.actorId, kind: "start", name: handle, count, embedded: start.embedded, startedBy: start.startedBy };
|
|
1453
|
+
this.#writeScript(runId, { ...state, packages: { ...state.packages, [handle]: { ...record2, count } }, deliveries: [...state.deliveries, delivery] }, context.commandId);
|
|
1454
|
+
try {
|
|
1455
|
+
this.runtime().enqueueInput(context, runId, { actorId: program.actorId, content: start.content });
|
|
1456
|
+
} catch (error) {
|
|
1457
|
+
const current = this.#script(runId);
|
|
1458
|
+
this.#writeScript(runId, {
|
|
1459
|
+
...current,
|
|
1460
|
+
packages: { ...current.packages, [handle]: { ...current.packages[handle], count: record2.count } },
|
|
1461
|
+
deliveries: current.deliveries.filter((entry) => entry.commandId !== context.commandId)
|
|
1462
|
+
});
|
|
1463
|
+
throw error;
|
|
1464
|
+
}
|
|
1465
|
+
return { count };
|
|
1466
|
+
}
|
|
1467
|
+
/** A start or result the host queued for this actor, recognized by the command that queued the input; it is consumed here. */
|
|
1468
|
+
#recognize(runId, actorId, input) {
|
|
1469
|
+
const state = this.#script(runId);
|
|
1470
|
+
if (!state.deliveries.some((delivery2) => delivery2.actorId === actorId))
|
|
1471
|
+
return void 0;
|
|
1472
|
+
const commandId = this.#commandOf(runId, input.sequence);
|
|
1473
|
+
const delivery = state.deliveries.find((entry) => entry.commandId === commandId && entry.actorId === actorId);
|
|
1474
|
+
if (!delivery)
|
|
1475
|
+
return void 0;
|
|
1476
|
+
const deliveries = state.deliveries.filter((entry) => entry !== delivery);
|
|
1477
|
+
if (delivery.kind === "result") {
|
|
1478
|
+
this.#writeScript(runId, { ...state, deliveries });
|
|
1479
|
+
return { result: JSON.parse(input.content) };
|
|
1480
|
+
}
|
|
1481
|
+
const record2 = state.packages[delivery.name];
|
|
1482
|
+
const packages = record2 ? { ...state.packages, [delivery.name]: { ...record2, open: [...record2.open, { count: delivery.count, startedBy: delivery.startedBy }].slice(-MAX_OPEN_STARTS) } } : state.packages;
|
|
1483
|
+
this.#writeScript(runId, { ...state, packages, deliveries });
|
|
1484
|
+
const { input: value, options } = JSON.parse(input.content);
|
|
1485
|
+
return { start: { input: value, options, embedded: delivery.embedded, startedBy: delivery.startedBy, count: delivery.count } };
|
|
1486
|
+
}
|
|
1487
|
+
/** Checked before the state commits: every finish ends a different open start of this package. */
|
|
1488
|
+
#finishesOf(runId, program, value) {
|
|
1489
|
+
const finishes = finishesOf(value);
|
|
1490
|
+
const open = new Set(this.#script(runId).packages[program.name]?.open.map((entry) => entry.count) ?? []);
|
|
1491
|
+
for (const [index, finish] of finishes.entries()) {
|
|
1492
|
+
if (!open.has(finish.start) || finishes.findIndex((other) => other.start === finish.start) !== index)
|
|
1493
|
+
throw new Error(`finish: start ${finish.start} of @${program.actorHandle} is not open; it was finished already, never reached the program, or is older than the last ${MAX_OPEN_STARTS} open starts.`);
|
|
1494
|
+
}
|
|
1495
|
+
return finishes;
|
|
1496
|
+
}
|
|
1497
|
+
#deliverResults(request, program, finishes) {
|
|
1498
|
+
if (finishes.length === 0)
|
|
1499
|
+
return;
|
|
1500
|
+
const state = this.#script(request.runId);
|
|
1501
|
+
const record2 = state.packages[program.name];
|
|
1502
|
+
const finished = new Set(finishes.map((finish) => finish.start));
|
|
1503
|
+
const starters = new Map(record2.open.map((entry) => [entry.count, entry.startedBy]));
|
|
1504
|
+
this.#writeScript(request.runId, { ...state, packages: { ...state.packages, [program.name]: { ...record2, open: record2.open.filter((entry) => !finished.has(entry.count)) } } });
|
|
1505
|
+
for (const finish of finishes)
|
|
1506
|
+
this.#deliverResult(request, program, finish, starters.get(finish.start));
|
|
1507
|
+
}
|
|
1508
|
+
/** The owner reads the summary in the chat, an LLM gets a short message, a TypeScript actor a result it recognizes. */
|
|
1509
|
+
#deliverResult(request, program, finish, startedBy) {
|
|
1510
|
+
const starter = this.view(request.runId).actors.find((actor) => actor.id === startedBy);
|
|
1511
|
+
if (!starter || starter.kind === "human") {
|
|
1512
|
+
request.emit({ kind: "runtime", text: finish.summary ?? `Start ${finish.start} finished.` });
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
const commandId = `run-script-result:${request.turnId}:${program.name}:${finish.start}`;
|
|
1516
|
+
try {
|
|
1517
|
+
if (starter.kind === "script") {
|
|
1518
|
+
const state = this.#script(request.runId);
|
|
1519
|
+
this.#writeScript(request.runId, { ...state, deliveries: [...state.deliveries, { commandId, actorId: starter.id, kind: "result" }] }, commandId);
|
|
1520
|
+
}
|
|
1521
|
+
this.runtime().enqueueInput({ actorId: this.ownerId(request.runId), commandId }, request.runId, {
|
|
1522
|
+
actorId: starter.id,
|
|
1523
|
+
presentation: "background",
|
|
1524
|
+
content: starter.kind === "agent" ? agentResultText(program.actorHandle, finish) : scriptResultContent(program.actorHandle, finish)
|
|
1525
|
+
});
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
request.emit({ kind: "runtime", text: `The result of start ${finish.start} did not reach @${starter.handle}: ${errorText(error)}` });
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
isScriptActor(runId, actorId) {
|
|
1531
|
+
return this.runtime().select(runId, (state) => {
|
|
1532
|
+
const actor = state.actors.get(actorId);
|
|
1533
|
+
if (!actor || actor.kind !== "script")
|
|
1534
|
+
return false;
|
|
1535
|
+
const script = [...state.pluginStates.values()].find((entry) => entry.pluginId === ACTOR_SCRIPT_STATE_ID && entry.scope.kind === "run");
|
|
1536
|
+
return Object.hasOwn(scriptStateOf(script?.state).packages, actor.handle);
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
programOf(runId, actorId) {
|
|
1540
|
+
const program = this.programs(runId).find((entry) => entry.actorId === actorId);
|
|
1541
|
+
if (!program)
|
|
1542
|
+
return void 0;
|
|
1543
|
+
const record2 = this.#script(runId).packages[program.name];
|
|
1544
|
+
const origin = record2 && this.#identityOf(program) === record2.identity ? record2.origin : { kind: "run", installedBy: program.installedBy };
|
|
1545
|
+
return { name: program.name, actorId, revision: program.revision, origin };
|
|
1546
|
+
}
|
|
1547
|
+
/** Makes a package active once, whatever state it is in; under a shared name only the package the host installed from that plugin, unchanged. */
|
|
1548
|
+
ensure(context, runId, name2, signal) {
|
|
1549
|
+
this.#assertName(name2);
|
|
1550
|
+
return this.#withPackages(runId, [name2], () => this.#ensure(context, runId, name2, signal));
|
|
1551
|
+
}
|
|
1552
|
+
async #ensure(context, runId, name2, signal) {
|
|
1553
|
+
const outcome = (status) => {
|
|
1554
|
+
const program = this.#program(runId, name2);
|
|
1555
|
+
return { actorId: program.actorId, handle: program.actorHandle, status };
|
|
1556
|
+
};
|
|
1557
|
+
const shared = this.#options.sharedPackage(name2);
|
|
1558
|
+
const record2 = this.#script(runId).packages[name2];
|
|
1559
|
+
const assertShared = (identity) => {
|
|
1560
|
+
if (shared && !this.#hostShared(record2, shared.pluginId, identity()))
|
|
1561
|
+
throw foreignShared(name2, shared.pluginId, record2);
|
|
1562
|
+
};
|
|
1563
|
+
const active = this.programs(runId).find((program) => program.name === name2);
|
|
1564
|
+
if (active) {
|
|
1565
|
+
assertShared(() => this.#identityOf(active));
|
|
1566
|
+
return outcome("active");
|
|
1567
|
+
}
|
|
1568
|
+
const view = this.view(runId);
|
|
1569
|
+
const stopped = view.pluginStates.flatMap((entry) => {
|
|
1570
|
+
const program = entry.pluginId === ACTOR_PROGRAMS_STATE_ID && entry.scope.kind === "actor" ? entry.state.program : null;
|
|
1571
|
+
const actor = program?.name === name2 ? view.actors.find((candidate) => candidate.id === program.actorId) : void 0;
|
|
1572
|
+
return program && actor && actor.kind !== "human" && actor.lifecycle.kind === "stopped" ? [{ program, actor }] : [];
|
|
1573
|
+
})[0];
|
|
1574
|
+
if (stopped) {
|
|
1575
|
+
assertShared(() => this.#identityOf(stopped.program));
|
|
1576
|
+
this.runtime().restartActor(context, runId, stopped.actor.id, "Actor program ensured");
|
|
1577
|
+
return outcome("restarted");
|
|
1578
|
+
}
|
|
1579
|
+
const root = await this.workspaceDirectory(runId);
|
|
1580
|
+
if (occupied(path.join(root, name2))) {
|
|
1581
|
+
const identity = packageIdentity(await projectSourceFiles(path.join(root, name2)));
|
|
1582
|
+
assertShared(() => identity);
|
|
1583
|
+
await this.#activate(context, runId, name2, true, signal);
|
|
1584
|
+
return outcome("activated");
|
|
1585
|
+
}
|
|
1586
|
+
if (!shared)
|
|
1587
|
+
throw new Error(`The package ${name2} is neither in this run nor a shared actor package of this profile.`);
|
|
1588
|
+
const claims = this.#claim(runId, [{ name: name2, files: shared.files, origin: { kind: "shared", pluginId: shared.pluginId } }]);
|
|
1589
|
+
try {
|
|
1590
|
+
await this.#import(context, runId, name2, shared.files, signal);
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
this.#release(runId, claims);
|
|
1593
|
+
throw error;
|
|
1594
|
+
}
|
|
1595
|
+
return outcome("installed");
|
|
1596
|
+
}
|
|
1203
1597
|
async check(runId, name2, callerId, signal, reference) {
|
|
1204
1598
|
this.#assertName(name2);
|
|
1205
1599
|
const directory = path.join(await this.workspaceDirectory(runId), name2);
|
|
@@ -1219,7 +1613,7 @@ var ActorProgramRuntime = class {
|
|
|
1219
1613
|
handle: name2,
|
|
1220
1614
|
displayName: pkg.title,
|
|
1221
1615
|
kind: "script",
|
|
1222
|
-
grants: caller
|
|
1616
|
+
grants: inheritedGrants(caller),
|
|
1223
1617
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1224
1618
|
createdBy: callerId,
|
|
1225
1619
|
description: actorDescriptionOf(pkg.description) ?? null,
|
|
@@ -1330,7 +1724,7 @@ var ActorProgramRuntime = class {
|
|
|
1330
1724
|
return { directory, files, definition, clients, frameStyles, createActor: !owner, ...backend ? { backendJavaScript: backend.javaScript } : {} };
|
|
1331
1725
|
}
|
|
1332
1726
|
activate(context, runId, name2, signal, reference) {
|
|
1333
|
-
return this.#activate(context, runId, name2, true, signal, reference);
|
|
1727
|
+
return this.#withPackages(runId, [name2], () => this.#activate(context, runId, name2, true, signal, reference));
|
|
1334
1728
|
}
|
|
1335
1729
|
async #activate(context, runId, name2, idle, signal, reference) {
|
|
1336
1730
|
const key = `${runId}\0${name2}`;
|
|
@@ -1431,7 +1825,7 @@ var ActorProgramRuntime = class {
|
|
|
1431
1825
|
} else if (holder)
|
|
1432
1826
|
throw new Error(`Handle @${name2} already belongs to the ${holder.kind !== "human" && holder.lifecycle.kind === "stopped" ? "stopped" : "active"} actor ${holder.displayName}; only a stopped TypeScript actor is restarted on activation. Choose another package name.`);
|
|
1433
1827
|
else
|
|
1434
|
-
this.runtime().createScriptActor(context, runId, { handle: name2, displayName: definition.title, description: actorDescriptionOf(definition.description), grants: caller
|
|
1828
|
+
this.runtime().createScriptActor(context, runId, { handle: name2, displayName: definition.title, description: actorDescriptionOf(definition.description), grants: inheritedGrants(caller), toolNames: null });
|
|
1435
1829
|
const actor = this.resolveActor(runId, context.actorId, `@${name2}`);
|
|
1436
1830
|
definition.actorId = actor.id;
|
|
1437
1831
|
definition.actorHandle = actor.handle;
|
|
@@ -1458,9 +1852,13 @@ var ActorProgramRuntime = class {
|
|
|
1458
1852
|
return this.programs(runId).map((program) => ({ name: program.name, actor: `@${program.actorHandle}`, functions: program.functions.map((fn) => fn.id), views: program.views.map((view) => ({ name: view.key, title: view.title, visible: view.visible })) }));
|
|
1459
1853
|
}
|
|
1460
1854
|
async remove(context, runId, name2) {
|
|
1461
|
-
const
|
|
1855
|
+
const packageName = this.#program(runId, name2).name;
|
|
1856
|
+
return this.#withPackages(runId, [packageName], () => this.#remove(context, runId, packageName, name2));
|
|
1857
|
+
}
|
|
1858
|
+
async #remove(context, runId, packageName, name2) {
|
|
1859
|
+
const program = this.#program(runId, packageName);
|
|
1462
1860
|
this.#assertIdle(runId, program.actorId);
|
|
1463
|
-
if (this.#installing.has(`${runId}\0${
|
|
1861
|
+
if (this.#installing.has(`${runId}\0${packageName}`))
|
|
1464
1862
|
throw new Error("The package is being activated.");
|
|
1465
1863
|
const actor = this.actor(runId, program.actorId);
|
|
1466
1864
|
const key = `${runId}\0${program.actorId}`;
|
|
@@ -1638,8 +2036,11 @@ ${view.styles}`, platformVersion: 2, clientJavaScript: readFileSync(path.join(pr
|
|
|
1638
2036
|
await this.#serial(request.runId, request.agentId, signal, async () => {
|
|
1639
2037
|
const { program, descriptors } = await this.#bound(request.runId, request.agentId, void 0, signal, inputOf, descriptorsFor);
|
|
1640
2038
|
const binding = { id: request.turnId, kind: "input", principal: { id: request.agentId, kind: "script" }, descriptorsFor, call: (name2, input, index) => request.invoke(`${request.turnId}:capability:${index}`, name2, input) };
|
|
1641
|
-
const
|
|
2039
|
+
const recognized = this.#recognize(request.runId, request.agentId, request.input);
|
|
2040
|
+
const result = await this.#backend(request.runId, program, { kind: "input", input: scriptInputOf(request.input), ...recognized }, binding, descriptors, signal);
|
|
2041
|
+
const finishes = this.#finishesOf(request.runId, program, result.result);
|
|
1642
2042
|
this.#commitState(request.runId, request.agentId, result.state, result.initial);
|
|
2043
|
+
this.#deliverResults(request, program, finishes);
|
|
1643
2044
|
for (const text of result.logs)
|
|
1644
2045
|
request.emit({ kind: "runtime", text });
|
|
1645
2046
|
});
|
|
@@ -1782,12 +2183,14 @@ Input: ${JSON.stringify(input).slice(0, 500)}`, options: ["Run", "Cancel"], mult
|
|
|
1782
2183
|
waitForRunSettlement(runId) {
|
|
1783
2184
|
return Promise.allSettled([...this.#active.values()].filter((entry) => entry.runId === runId).map((entry) => entry.completed).concat([...this.#queues].filter(([key]) => key.startsWith(`${runId}\0`)).map(([, queue]) => queue))).then(() => void 0);
|
|
1784
2185
|
}
|
|
2186
|
+
/** Once per run and server process: calls a previous process left open are cancelled, calls of this process keep running. */
|
|
1785
2187
|
async prepareSession(runId) {
|
|
1786
2188
|
this.#knownRuns.add(runId);
|
|
1787
|
-
if (!this.runtime().listRuns().some((run) => run.id === runId))
|
|
2189
|
+
if (this.#recovered.has(runId) || !this.runtime().listRuns().some((run) => run.id === runId))
|
|
1788
2190
|
return;
|
|
2191
|
+
this.#recovered.add(runId);
|
|
1789
2192
|
for (const invocation of this.#invocations(runId).invocations)
|
|
1790
|
-
if (invocation.status === "queued" || invocation.status === "running")
|
|
2193
|
+
if ((invocation.status === "queued" || invocation.status === "running") && !this.#active.has(invocation.id))
|
|
1791
2194
|
this.#replaceInvocation(runId, { ...invocation, status: "cancelled", error: "The server was stopped during the call.", finishedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1792
2195
|
}
|
|
1793
2196
|
async stopSession(runId, _signal) {
|
|
@@ -1798,6 +2201,10 @@ Input: ${JSON.stringify(input).slice(0, 500)}`, options: ["Run", "Cancel"], mult
|
|
|
1798
2201
|
}
|
|
1799
2202
|
async deleteSession(runId) {
|
|
1800
2203
|
await this.stopRun(runId);
|
|
2204
|
+
this.#recovered.delete(runId);
|
|
2205
|
+
for (const directory of this.#identities.keys())
|
|
2206
|
+
if (directory.startsWith(`${this.#options.directoryFor(runId)}${path.sep}`))
|
|
2207
|
+
this.#identities.delete(directory);
|
|
1801
2208
|
await rm(this.#options.directoryFor(runId), { recursive: true, force: true });
|
|
1802
2209
|
}
|
|
1803
2210
|
async shutdown() {
|
|
@@ -1816,7 +2223,8 @@ var metadata = [
|
|
|
1816
2223
|
{ name: "actor_program_list", label: "List Actor Programs", description: "List active actor packages, functions and views." },
|
|
1817
2224
|
{ name: "actor_program_remove", label: "Remove Actor Program", description: "Detach functions and views and stop the backend.", longDescription: "A TypeScript actor is stopped; an LLM actor retains its agent behavior." },
|
|
1818
2225
|
{ name: "actor_view_set_visibility", label: "Show or hide Actor View", description: "Set surface visibility by package-name/view-key or @handle/view-key.", longDescription: "Use names you chose; the server resolves the view ID. A unique view title also works." },
|
|
1819
|
-
{ name: "actor_program_diagnostics", label: "Actor Program Diagnostics", description: "Read the last project diagnostics.", longDescription: "Changed errors are automatically supplied before the next model request." }
|
|
2226
|
+
{ name: "actor_program_diagnostics", label: "Actor Program Diagnostics", description: "Read the last project diagnostics.", longDescription: "Changed errors are automatically supplied before the next model request." },
|
|
2227
|
+
{ name: "actor_program_ensure", label: "Ensure Actor Program", description: "Make a package active once: return it when active, restart its stopped actor, activate it when installed, or install a shared package of the profile.", longDescription: "Safe to call on every start: an active package is returned unchanged and not rebuilt. status names what happened. actor_program_activate instead rebuilds and reactivates." }
|
|
1820
2228
|
];
|
|
1821
2229
|
var actorProgramManagementToolNames = metadata.map((entry) => entry.name);
|
|
1822
2230
|
var actorProgramToolsAvailable = defineToolAvailability2({ availability: "conditional", availabilityDetail: "For executable actors with agent.spawn and plugin.state.write." }, (actor) => actor.kind !== "human" && holdsUsable(actor, "agent.spawn") && holdsUsable(actor, "plugin.state.write"));
|
|
@@ -1827,7 +2235,8 @@ var managementTools = (runtime, diagnostics) => [
|
|
|
1827
2235
|
defineRunFunction2({ ...metadata[2], schema: Type.Object({}, { additionalProperties: false }), resultSchema: Type.Array(Type.Object({ name: Type.String(), actor: Type.String(), functions: Type.Array(Type.String()), views: Type.Array(Type.Object({ name: Type.String(), title: Type.String(), visible: Type.Boolean() })) })), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope) => runtime.list(scope.caller.runId) }),
|
|
1828
2236
|
defineRunFunction2({ ...metadata[3], schema: Type.Object({ name }, { additionalProperties: false }), resultSchema: Type.Object({ removed: Type.String() }, { additionalProperties: false }), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, id, input) => runtime.remove(scope.context(id), scope.caller.runId, input.name) }),
|
|
1829
2237
|
defineRunFunction2({ ...metadata[4], schema: Type.Object({ view: Type.String({ minLength: 1, description: "package-name/view-key or @handle/view-key of an activated view, without the surface entity prefix app:. No generated IDs needed." }), visible: Type.Boolean() }, { additionalProperties: false }), resultSchema: Type.Object({ view: Type.String(), visible: Type.Boolean() }, { additionalProperties: false }), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, id, input) => runtime.setVisibility(scope.context(id), scope.caller.runId, input.view, input.visible) }),
|
|
1830
|
-
defineRunFunction2({ ...metadata[5], schema: Type.Object({ name: Type.Optional(name) }, { additionalProperties: false }), resultSchema: Type.String(), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, _id, input) => diagnostics.latest(scope.caller.runId, scope.caller.actorId, input.name) })
|
|
2238
|
+
defineRunFunction2({ ...metadata[5], schema: Type.Object({ name: Type.Optional(name) }, { additionalProperties: false }), resultSchema: Type.String(), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, _id, input) => diagnostics.latest(scope.caller.runId, scope.caller.actorId, input.name) }),
|
|
2239
|
+
defineRunFunction2({ ...metadata[6], schema: Type.Object({ name }, { additionalProperties: false }), resultSchema: Type.Object({ actorId: Type.String(), handle: Type.String(), status: Type.Union([Type.Literal("active"), Type.Literal("restarted"), Type.Literal("activated"), Type.Literal("installed")]) }, { additionalProperties: false }), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, id, input) => runtime.ensure(scope.context(id), scope.caller.runId, input.name, scope.signal) })
|
|
1831
2240
|
];
|
|
1832
2241
|
var createActorProgramToolContributors = (runtime, diagnostics) => [
|
|
1833
2242
|
{ name: "ragents.actor-programs.management", descriptors: metadata.map((item) => toolDescriptorFrom(item, actorProgramToolsAvailable)), tools: () => managementTools(runtime, diagnostics) },
|
|
@@ -2060,6 +2469,10 @@ var actorProgramsPlugin = (pluginHost) => ({
|
|
|
2060
2469
|
scriptSources: (entryId, name2) => {
|
|
2061
2470
|
const script = pluginHost.startEntries.scriptPackage(entryId);
|
|
2062
2471
|
return !script ? void 0 : script.handle === name2 ? script.files : script.programs.find((program) => program.name === name2)?.files;
|
|
2472
|
+
},
|
|
2473
|
+
sharedPackage: (name2) => pluginHost.actorPackages.get(name2),
|
|
2474
|
+
placeView: (context, runId, entity) => {
|
|
2475
|
+
host.service(surfacePlacementToken).place(context, runId, { entity });
|
|
2063
2476
|
}
|
|
2064
2477
|
});
|
|
2065
2478
|
host.provide(actorProgramsToken, runtime);
|