@schlenkr/ragents 0.1.13 → 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.
Files changed (145) hide show
  1. package/apps/server/package.json +1 -1
  2. package/apps/server/src/api/contracts.ts +19 -1
  3. package/apps/server/src/api/core-methods.ts +12 -0
  4. package/apps/server/src/chat-handler.ts +19 -0
  5. package/apps/server/src/host-api.json +2 -0
  6. package/apps/server/src/host-api.ts +2 -2
  7. package/apps/server/src/plugin-build/build.ts +1 -1
  8. package/apps/server/src/plugin-support/actor-programs/app-project.ts +61 -17
  9. package/apps/server/src/plugin-support/actor-programs/contract.ts +18 -1
  10. package/apps/server/src/plugin-support/actor-programs/service.ts +22 -1
  11. package/apps/server/src/plugin-support/plugin-folder.ts +14 -1
  12. package/apps/server/src/plugin-support/run-scripts.ts +27 -2
  13. package/apps/server/src/profile/compose.ts +2 -0
  14. package/apps/server/src/provider.ts +31 -6
  15. package/apps/server/src/ragents/chat-projection.ts +7 -0
  16. package/apps/server/src/ragents/global-chat.ts +5 -1
  17. package/apps/server/src/ragents/run-script-tools.ts +66 -0
  18. package/apps/server/src/ragents/session.ts +122 -60
  19. package/apps/server/src/title-compactor.ts +1 -1
  20. package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-CpPi6QLr.js → highlighted-body-KPVGNVTW-BwD4uYEe.js} +1 -1
  21. package/apps/web/dist/assets/main-KBrc5Q77.js +11 -0
  22. package/apps/web/dist/assets/{mermaid-HWGCJPDP-q-mf1kLN.js → mermaid-HWGCJPDP-CSCkaArr.js} +88 -88
  23. package/apps/web/dist/assets/runPanel-BjG5NBKm.js +26 -0
  24. package/apps/web/dist/help/guide-clients.html +21 -4
  25. package/apps/web/dist/help/guide-clients.md +23 -4
  26. package/apps/web/dist/help/guide-plugins.html +12 -2
  27. package/apps/web/dist/help/guide-plugins.md +9 -2
  28. package/apps/web/dist/help/guide-programs.html +8 -2
  29. package/apps/web/dist/help/guide-programs.md +9 -2
  30. package/apps/web/dist/help/guide-runtime.html +3 -2
  31. package/apps/web/dist/help/guide-runtime.md +3 -2
  32. package/apps/web/dist/help/index.html +1 -1
  33. package/apps/web/dist/help/mini-app.css +16 -2
  34. package/apps/web/dist/host-web.json +13 -13
  35. package/apps/web/dist/index.html +2 -2
  36. package/apps/web/dist/run-panel.html +2 -2
  37. package/apps/web/package.json +1 -1
  38. package/apps/web/src/App.tsx +12 -1
  39. package/apps/web/src/PluginRegistry.tsx +2 -2
  40. package/apps/web/src/SessionList.tsx +7 -6
  41. package/apps/web/src/SettingsModal.tsx +3 -3
  42. package/apps/web/src/actor-programs/client-ui/FilePicker.tsx +1 -1
  43. package/apps/web/src/api.ts +2 -0
  44. package/apps/web/src/run-overview.ts +1 -1
  45. package/apps/web/src/run-panel/RunPanelApp.tsx +1 -1
  46. package/apps/web/src/run-panel/RunPanelMenu.tsx +47 -11
  47. package/apps/web/src/ui/relative-time.ts +1 -1
  48. package/bundles/ragents.activity/ragents-bundle.json +1 -1
  49. package/bundles/ragents.actor-programs/ragents-bundle.json +4 -2
  50. package/bundles/ragents.actor-programs/server/index.js +437 -24
  51. package/bundles/ragents.actor-programs/server/index.js.map +4 -4
  52. package/bundles/ragents.actor-programs/web/index.js.map +1 -1
  53. package/bundles/ragents.ask/ragents-bundle.json +2 -2
  54. package/bundles/ragents.ask/server/index.js +1 -0
  55. package/bundles/ragents.ask/server/index.js.map +2 -2
  56. package/bundles/ragents.browser/ragents-bundle.json +1 -1
  57. package/bundles/ragents.documents/ragents-bundle.json +2 -2
  58. package/bundles/ragents.documents/web/classes.json +1 -1
  59. package/bundles/ragents.documents/web/index.js +1 -1
  60. package/bundles/ragents.documents/web/index.js.map +2 -2
  61. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
  62. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
  63. package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
  64. package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
  65. package/bundles/ragents.orchestration/ragents-bundle.json +7 -4
  66. package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js +11 -0
  67. package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js.map +7 -0
  68. package/bundles/ragents.orchestration/server/exports/server/contract.js +9 -0
  69. package/bundles/ragents.orchestration/server/exports/server/contract.js.map +7 -0
  70. package/bundles/ragents.orchestration/server/index.js +49 -3
  71. package/bundles/ragents.orchestration/server/index.js.map +2 -2
  72. package/bundles/ragents.orchestration/surface.hbs +5 -0
  73. package/bundles/ragents.orchestration/web/index.js +3 -3
  74. package/bundles/ragents.orchestration/web/index.js.map +2 -2
  75. package/bundles/ragents.overseer/ragents-bundle.json +4 -3
  76. package/bundles/ragents.overseer/web/classes.json +1 -1
  77. package/bundles/ragents.overseer/web/index.js +1 -1
  78. package/bundles/ragents.overseer/web/index.js.map +4 -4
  79. package/bundles/ragents.processes/ragents-bundle.json +2 -2
  80. package/bundles/ragents.processes/web/classes.json +1 -1
  81. package/bundles/ragents.processes/web/index.js +1 -1
  82. package/bundles/ragents.processes/web/index.js.map +2 -2
  83. package/bundles/ragents.product/ragents-bundle.json +1 -1
  84. package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
  85. package/bundles/ragents.reference/actors/notebook/package.json +16 -0
  86. package/bundles/ragents.reference/actors/notebook/src/client.tsx +31 -0
  87. package/bundles/ragents.reference/actors/notebook/src/server.ts +27 -0
  88. package/bundles/ragents.reference/actors/notebook/tests/program.test.ts +27 -0
  89. package/bundles/ragents.reference/ragents-bundle.json +4 -3
  90. package/bundles/ragents.reference/run-scripts/quick-note/RUN.md +14 -0
  91. package/bundles/ragents.reference/run-scripts/quick-note/package.json +9 -0
  92. package/bundles/ragents.reference/run-scripts/quick-note/src/server.ts +29 -0
  93. package/bundles/ragents.reference/run-scripts/quick-note/tests/program.test.ts +43 -0
  94. package/bundles/ragents.reference/run-scripts/run-roster/RUN.md +19 -0
  95. package/bundles/ragents.reference/run-scripts/run-roster/package.json +9 -0
  96. package/bundles/ragents.reference/run-scripts/run-roster/src/server.ts +26 -0
  97. package/bundles/ragents.reference/run-scripts/run-roster/tests/program.test.ts +47 -0
  98. package/bundles/ragents.todo/ragents-bundle.json +1 -1
  99. package/bundles/ragents.transcript/ragents-bundle.json +1 -1
  100. package/bundles/ragents.watch/ragents-bundle.json +1 -1
  101. package/bundles/ragents.workspace/ragents-bundle.json +2 -2
  102. package/bundles/ragents.workspace/web/classes.json +1 -1
  103. package/bundles/ragents.workspace/web/index.js +1 -1
  104. package/bundles/ragents.workspace/web/index.js.map +2 -2
  105. package/package.json +3 -3
  106. package/packages/ragents/src/domain/vocabulary.ts +4 -0
  107. package/packages/ragents/src/index.ts +3 -1
  108. package/packages/ragents/src/plugin-host.ts +62 -10
  109. package/packages/ragents/src/plugin-types.ts +11 -0
  110. package/packages/ragents/src/runtime/guards.ts +2 -1
  111. package/packages/ragents/src/typescript/async-compiler.ts +11 -1
  112. package/plugins/ragents.actor-programs/server/index.ts +3 -0
  113. package/plugins/ragents.actor-programs/server/runtime.ts +424 -28
  114. package/plugins/ragents.actor-programs/server/script-state.ts +54 -0
  115. package/plugins/ragents.actor-programs/server/tool-contributor.ts +2 -0
  116. package/plugins/ragents.ask/server/ask-tool.ts +1 -0
  117. package/plugins/ragents.documents/web/DocumentViewer.tsx +2 -2
  118. package/plugins/ragents.orchestration/ragents-plugin.json +1 -0
  119. package/plugins/ragents.orchestration/server/contract.ts +14 -0
  120. package/plugins/ragents.orchestration/server/index.ts +4 -1
  121. package/plugins/ragents.orchestration/server/surface-tool.ts +58 -2
  122. package/plugins/ragents.orchestration/surface.hbs +5 -0
  123. package/plugins/ragents.orchestration/web/ExecutionsPanel.tsx +1 -1
  124. package/plugins/ragents.orchestration/web/FlowInspector.tsx +1 -1
  125. package/plugins/ragents.orchestration/web/JournalStatus.tsx +1 -1
  126. package/plugins/ragents.orchestration/web/format.ts +1 -1
  127. package/plugins/ragents.overseer/web/index.tsx +51 -3
  128. package/plugins/ragents.processes/web/processes.ts +1 -1
  129. package/plugins/ragents.reference/actors/notebook/package.json +16 -0
  130. package/plugins/ragents.reference/actors/notebook/src/client.tsx +31 -0
  131. package/plugins/ragents.reference/actors/notebook/src/server.ts +27 -0
  132. package/plugins/ragents.reference/actors/notebook/tests/program.test.ts +27 -0
  133. package/plugins/ragents.reference/run-scripts/quick-note/RUN.md +14 -0
  134. package/plugins/ragents.reference/run-scripts/quick-note/package.json +9 -0
  135. package/plugins/ragents.reference/run-scripts/quick-note/src/server.ts +29 -0
  136. package/plugins/ragents.reference/run-scripts/quick-note/tests/program.test.ts +43 -0
  137. package/plugins/ragents.reference/run-scripts/run-roster/RUN.md +19 -0
  138. package/plugins/ragents.reference/run-scripts/run-roster/package.json +9 -0
  139. package/plugins/ragents.reference/run-scripts/run-roster/src/server.ts +26 -0
  140. package/plugins/ragents.reference/run-scripts/run-roster/tests/program.test.ts +47 -0
  141. package/plugins/ragents.workspace/web/FileBrowser.tsx +1 -1
  142. package/scripts/agent/agent-cli.ts +46 -2
  143. package/scripts/package/ragents.mjs +3 -1
  144. package/apps/web/dist/assets/main-DJmCHGzM.js +0 -11
  145. package/apps/web/dist/assets/runPanel-nk_aMxSn.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
- async #refresh(runId, program, signal) {
1104
- const script = this.#script(runId);
1105
- const files = script ? this.#options.scriptSources(script.entryId, program.name) : void 0;
1106
- if (files) {
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
- async scaffold(runId, name2, templateId) {
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
- async importPackage(context, runId, name2, files, signal, scriptEntryId) {
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
- if (scriptEntryId)
1194
- this.runtime().replacePluginState(this.operatorContext(runId), runId, { pluginId: ACTOR_SCRIPT_STATE_ID, scope: { kind: "run" }, state: { version: 1, entryId: scriptEntryId } });
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.grants.filter((grant) => grant.delegable),
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.grants.filter((grant) => grant.delegable), toolNames: null });
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 program = this.#program(runId, name2);
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${name2}`))
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 result = await this.#backend(request.runId, program, { kind: "input", input: scriptInputOf(request.input) }, binding, descriptors, signal);
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);