glove-foundry 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/chunk-5NFLGHOC.js +281 -0
- package/dist/chunk-J7ZBJM54.js +6 -0
- package/dist/{chunk-5LDW2N2E.js → chunk-KBMD4RYU.js} +1980 -3464
- package/dist/chunk-LW5PJ2FN.js +546 -0
- package/dist/{chunk-25BGNRJN.js → chunk-SGF3CFUR.js} +248 -120
- package/dist/chunk-TH5ETCG2.js +730 -0
- package/dist/cli.js +7 -3
- package/dist/{client-D0-pKEZM.d.ts → client-DcsnudMI.d.ts} +9 -3
- package/dist/client.d.ts +3 -1
- package/dist/config.d.ts +1 -0
- package/dist/execution-agent.js +9 -4
- package/dist/execution-daemon.d.ts +2 -0
- package/dist/execution-daemon.js +157 -0
- package/dist/execution-loader.d.ts +6 -0
- package/dist/execution-loader.js +20 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +32 -27
- package/dist/station.d.ts +35 -0
- package/dist/station.js +9 -0
- package/docs/architecture.md +1 -1
- package/docs/building-with-foundry.md +19 -2
- package/docs/execution-release.md +57 -0
- package/docs/execution.md +97 -0
- package/docs/release-verification.md +3 -1
- package/package.json +23 -15
- package/templates/minimal/README.md +1 -1
- package/templates/travel-concierge/README.md +1 -1
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_FOUNDRY_CONFIG
|
|
3
3
|
} from "./chunk-ZFNMFE3T.js";
|
|
4
|
+
import {
|
|
5
|
+
executionEvents
|
|
6
|
+
} from "./chunk-J7ZBJM54.js";
|
|
4
7
|
import {
|
|
5
8
|
EMPTY_AGENT_COMPOSITION,
|
|
6
9
|
EMPTY_CAPABILITY_REGISTRY,
|
|
7
10
|
EMPTY_FOUNDRY_APPLICATION,
|
|
8
11
|
EMPTY_NATIVE_REGISTRY,
|
|
9
12
|
FOUNDRY_AGENT_APPLICATION_BRAND,
|
|
10
|
-
FOUNDRY_AGENT_FILE_ENV,
|
|
11
|
-
FOUNDRY_AGENT_ROUTE_ENV,
|
|
12
|
-
FOUNDRY_APPLICATION_ENV,
|
|
13
13
|
FOUNDRY_APPROVAL_DIRECTORY_ENV,
|
|
14
14
|
FOUNDRY_CORE_COMMAND_DIRECTORY_ENV,
|
|
15
15
|
FOUNDRY_CORE_COMMAND_EVENT,
|
|
16
|
-
FOUNDRY_EVENT_PREFIX,
|
|
17
|
-
FOUNDRY_EXECUTION_MARKER,
|
|
18
16
|
FOUNDRY_LAYER_BRAND,
|
|
19
17
|
FOUNDRY_MCP_BRAND,
|
|
20
18
|
FOUNDRY_MEMORY_BRAND,
|
|
21
19
|
FOUNDRY_SHARED_TOOL_BRAND,
|
|
22
20
|
FOUNDRY_SUBSCRIBER_BRAND,
|
|
23
21
|
MemoryFoundryDataAdapter,
|
|
24
|
-
bindFileIdentity,
|
|
25
22
|
claimFoundryCoreCommandRequest,
|
|
26
|
-
compileAgentDefinition,
|
|
27
23
|
createAgentInstance,
|
|
28
24
|
createConversation,
|
|
29
25
|
createManifest,
|
|
@@ -40,10 +36,15 @@ import {
|
|
|
40
36
|
reconstructPlaybook,
|
|
41
37
|
reconstructPlaybookSubscription,
|
|
42
38
|
resolveFoundryApproval,
|
|
43
|
-
routeFromInternalAgentName,
|
|
44
39
|
settleFoundryCoreCommandRequest,
|
|
45
40
|
transmissionPredicate
|
|
46
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-KBMD4RYU.js";
|
|
42
|
+
import {
|
|
43
|
+
FOUNDRY_APPLICATION_ENV,
|
|
44
|
+
FOUNDRY_EVENT_PREFIX,
|
|
45
|
+
FOUNDRY_EXECUTION_MARKER,
|
|
46
|
+
bindFileIdentity
|
|
47
|
+
} from "./chunk-5NFLGHOC.js";
|
|
47
48
|
|
|
48
49
|
// src/codegen.ts
|
|
49
50
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
@@ -837,27 +838,189 @@ var FoundryObserver = class {
|
|
|
837
838
|
};
|
|
838
839
|
|
|
839
840
|
// src/runtime.ts
|
|
840
|
-
import {
|
|
841
|
-
import { mkdir as mkdir2, rm } from "node:fs/promises";
|
|
841
|
+
import { mkdir as mkdir2, rm as rm2 } from "node:fs/promises";
|
|
842
842
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
843
|
-
import { tmpdir } from "node:os";
|
|
844
|
-
import { join } from "node:path";
|
|
845
|
-
import { fileURLToPath } from "node:url";
|
|
843
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
844
|
+
import { join as join2 } from "node:path";
|
|
846
845
|
import {
|
|
847
846
|
Effect as Effect5,
|
|
848
847
|
Layer as Layer3,
|
|
849
848
|
ManagedRuntime as EffectManagedRuntime,
|
|
850
849
|
Schema as Schema3
|
|
851
850
|
} from "effect";
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
} from "
|
|
851
|
+
|
|
852
|
+
// src/execution-backend.ts
|
|
853
|
+
import { fork } from "node:child_process";
|
|
854
|
+
import { existsSync } from "node:fs";
|
|
855
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
856
|
+
import { tmpdir } from "node:os";
|
|
857
|
+
import { join } from "node:path";
|
|
858
|
+
import { fileURLToPath } from "node:url";
|
|
859
|
+
function entry(name) {
|
|
860
|
+
const built = fileURLToPath(new URL(`./${name}.js`, import.meta.url));
|
|
861
|
+
return existsSync(built) ? built : fileURLToPath(new URL(`./${name}.ts`, import.meta.url));
|
|
862
|
+
}
|
|
863
|
+
var FoundryExecutionBackend = class {
|
|
864
|
+
constructor(options) {
|
|
865
|
+
this.options = options;
|
|
866
|
+
}
|
|
867
|
+
child;
|
|
868
|
+
directory;
|
|
869
|
+
sequence = 0;
|
|
870
|
+
stopped = false;
|
|
871
|
+
stopping;
|
|
872
|
+
pending = /* @__PURE__ */ new Map();
|
|
873
|
+
loopResolve;
|
|
874
|
+
loopReject;
|
|
875
|
+
loop;
|
|
876
|
+
get processId() {
|
|
877
|
+
return this.child?.pid;
|
|
878
|
+
}
|
|
879
|
+
async initialize() {
|
|
880
|
+
if (this.child || this.stopped) throw new Error("Execution backend cannot be initialized twice.");
|
|
881
|
+
this.directory = await mkdtemp(join(tmpdir(), "glove-foundry-daemon-"));
|
|
882
|
+
const env = { ...process.env, ...this.options.env };
|
|
883
|
+
for (const name of ["NODE_OPTIONS", "LD_PRELOAD", "LD_LIBRARY_PATH", "DYLD_INSERT_LIBRARIES"]) delete env[name];
|
|
884
|
+
const child = this.child = fork(entry("execution-daemon"), [], {
|
|
885
|
+
cwd: this.options.rootDir,
|
|
886
|
+
env,
|
|
887
|
+
serialization: "advanced",
|
|
888
|
+
execArgv: ["--import", import.meta.resolve("tsx")],
|
|
889
|
+
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
890
|
+
});
|
|
891
|
+
child.stderr?.resume();
|
|
892
|
+
this.loop = new Promise((resolve3, reject) => {
|
|
893
|
+
this.loopResolve = resolve3;
|
|
894
|
+
this.loopReject = reject;
|
|
895
|
+
});
|
|
896
|
+
void this.loop.catch(() => {
|
|
897
|
+
});
|
|
898
|
+
child.on("message", (message) => {
|
|
899
|
+
if ("event" in message) {
|
|
900
|
+
if (executionEvents.includes(message.event)) {
|
|
901
|
+
const handler = this.options.observer[message.event];
|
|
902
|
+
handler.call(this.options.observer, message.value);
|
|
903
|
+
}
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const request = this.pending.get(message.id);
|
|
907
|
+
if (!request) return;
|
|
908
|
+
clearTimeout(request.timer);
|
|
909
|
+
this.pending.delete(message.id);
|
|
910
|
+
if (message.error) request.reject(new Error(message.error));
|
|
911
|
+
else request.resolve(message.value);
|
|
912
|
+
});
|
|
913
|
+
const failed = () => {
|
|
914
|
+
const error = new Error("Foundry execution daemon disconnected. A pending mutation may have completed; inspect state before retrying.");
|
|
915
|
+
for (const request of this.pending.values()) {
|
|
916
|
+
clearTimeout(request.timer);
|
|
917
|
+
request.reject(error);
|
|
918
|
+
}
|
|
919
|
+
this.pending.clear();
|
|
920
|
+
if (this.stopped) this.loopResolve?.();
|
|
921
|
+
else this.loopReject?.(error);
|
|
922
|
+
};
|
|
923
|
+
child.once("error", failed);
|
|
924
|
+
child.once("exit", failed);
|
|
925
|
+
child.once("disconnect", failed);
|
|
926
|
+
try {
|
|
927
|
+
await this.request({ method: "init", options: {
|
|
928
|
+
rootDir: this.options.rootDir,
|
|
929
|
+
directory: this.directory,
|
|
930
|
+
loader: entry("execution-loader"),
|
|
931
|
+
agents: this.options.agents,
|
|
932
|
+
runner: this.options.runner
|
|
933
|
+
} }, 6e4);
|
|
934
|
+
} catch (error) {
|
|
935
|
+
await this.stop();
|
|
936
|
+
throw error;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
request(command, timeout = 3e4) {
|
|
940
|
+
if (!this.child?.connected || this.stopped && command.method !== "stop") return Promise.reject(new Error("Execution daemon is unavailable."));
|
|
941
|
+
const id2 = ++this.sequence;
|
|
942
|
+
return new Promise((resolve3, reject) => {
|
|
943
|
+
const timer = setTimeout(() => {
|
|
944
|
+
this.pending.delete(id2);
|
|
945
|
+
reject(new Error("Execution daemon request timed out. Its outcome may be unknown; do not automatically retry mutations."));
|
|
946
|
+
}, timeout);
|
|
947
|
+
this.pending.set(id2, { resolve: (value) => resolve3(value), reject, timer });
|
|
948
|
+
this.child.send({ id: id2, command }, (error) => {
|
|
949
|
+
if (error) {
|
|
950
|
+
clearTimeout(timer);
|
|
951
|
+
this.pending.delete(id2);
|
|
952
|
+
reject(new Error("Execution daemon request could not be delivered."));
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
start() {
|
|
958
|
+
return this.loop ?? Promise.reject(new Error("Execution backend is not initialized."));
|
|
959
|
+
}
|
|
960
|
+
stop(_options) {
|
|
961
|
+
if (this.stopping) return this.stopping;
|
|
962
|
+
this.stopped = true;
|
|
963
|
+
this.stopping = (async () => {
|
|
964
|
+
const child = this.child;
|
|
965
|
+
try {
|
|
966
|
+
if (child) {
|
|
967
|
+
try {
|
|
968
|
+
if (child.connected) await this.request({ method: "stop" }, 15e3);
|
|
969
|
+
} finally {
|
|
970
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
971
|
+
await new Promise((resolve3) => {
|
|
972
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
973
|
+
child.once("exit", () => {
|
|
974
|
+
clearTimeout(timer);
|
|
975
|
+
resolve3();
|
|
976
|
+
});
|
|
977
|
+
if (child.connected) child.disconnect();
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
} finally {
|
|
983
|
+
if (this.directory) await rm(this.directory, { recursive: true, force: true });
|
|
984
|
+
}
|
|
985
|
+
})();
|
|
986
|
+
return this.stopping;
|
|
987
|
+
}
|
|
988
|
+
triggerSignal(name, input) {
|
|
989
|
+
return this.request({ method: "trigger", name, input });
|
|
990
|
+
}
|
|
991
|
+
getRun(id2) {
|
|
992
|
+
return this.request({ method: "get", id: id2 });
|
|
993
|
+
}
|
|
994
|
+
listAllRuns(options = {}) {
|
|
995
|
+
return this.request({ method: "list", ...options });
|
|
996
|
+
}
|
|
997
|
+
cancel(id2) {
|
|
998
|
+
return this.request({ method: "cancel", id: id2 });
|
|
999
|
+
}
|
|
1000
|
+
ping() {
|
|
1001
|
+
return this.request({ method: "ping" });
|
|
1002
|
+
}
|
|
1003
|
+
getAdapter() {
|
|
1004
|
+
return { ping: () => this.ping() };
|
|
1005
|
+
}
|
|
1006
|
+
schedules = {
|
|
1007
|
+
add: (schedule) => this.request({ method: "schedule.add", schedule }),
|
|
1008
|
+
delete: (id2) => this.request({ method: "schedule.delete", id: id2 }),
|
|
1009
|
+
ping: () => this.ping()
|
|
1010
|
+
};
|
|
1011
|
+
async waitForRun(id2, options = {}) {
|
|
1012
|
+
const deadline = Date.now() + (options.timeoutMs ?? 6e4);
|
|
1013
|
+
do {
|
|
1014
|
+
const run = await this.getRun(id2);
|
|
1015
|
+
if (!run || ["completed", "failed", "cancelled"].includes(run.status)) return run;
|
|
1016
|
+
await new Promise((resolve3) => setTimeout(resolve3, options.pollMs ?? 100));
|
|
1017
|
+
} while (Date.now() < deadline);
|
|
1018
|
+
return this.getRun(id2);
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
// src/runtime.ts
|
|
858
1023
|
import {
|
|
859
|
-
ScheduleMemoryAdapter,
|
|
860
|
-
ScheduleReconciler,
|
|
861
1024
|
nextCronOccurrence
|
|
862
1025
|
} from "station-schedules";
|
|
863
1026
|
|
|
@@ -1077,11 +1240,11 @@ async function moduleFiles(directory, suffix) {
|
|
|
1077
1240
|
if (code === "ENOENT") return [];
|
|
1078
1241
|
throw cause;
|
|
1079
1242
|
}
|
|
1080
|
-
return entries.filter((
|
|
1081
|
-
if (
|
|
1082
|
-
const extension = extname(
|
|
1083
|
-
return MODULE_EXTENSIONS.has(extension) &&
|
|
1084
|
-
}).map((
|
|
1243
|
+
return entries.filter((entry2) => {
|
|
1244
|
+
if (entry2.endsWith(".d.ts")) return false;
|
|
1245
|
+
const extension = extname(entry2);
|
|
1246
|
+
return MODULE_EXTENSIONS.has(extension) && entry2.slice(0, -extension.length).endsWith(suffix);
|
|
1247
|
+
}).map((entry2) => resolve2(directory, entry2)).sort();
|
|
1085
1248
|
}
|
|
1086
1249
|
function routeFromModule(directory, filePath, suffix) {
|
|
1087
1250
|
const rel = normalizePath(relative2(directory, filePath));
|
|
@@ -1281,11 +1444,6 @@ function promiseEffect(operation, evaluate) {
|
|
|
1281
1444
|
catch: (cause) => runtimeFailure(operation, cause)
|
|
1282
1445
|
}).pipe(Effect5.withSpan(`foundry.${operation}`));
|
|
1283
1446
|
}
|
|
1284
|
-
function executionAgentEntrypoint() {
|
|
1285
|
-
const built = fileURLToPath(new URL("./execution-agent.js", import.meta.url));
|
|
1286
|
-
if (existsSync(built)) return built;
|
|
1287
|
-
return fileURLToPath(new URL("./execution-agent.ts", import.meta.url));
|
|
1288
|
-
}
|
|
1289
1447
|
var FoundryRuntime = class _FoundryRuntime {
|
|
1290
1448
|
rootDir;
|
|
1291
1449
|
agents;
|
|
@@ -1306,20 +1464,21 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
1306
1464
|
execution;
|
|
1307
1465
|
observer;
|
|
1308
1466
|
signalRunner;
|
|
1309
|
-
envStore;
|
|
1310
|
-
envProvider;
|
|
1311
1467
|
scheduleAdapter;
|
|
1312
1468
|
services;
|
|
1313
1469
|
runnerLoops = [];
|
|
1314
1470
|
materializedActivations = /* @__PURE__ */ new Set();
|
|
1471
|
+
// Serialize host-owned activation transitions across asynchronous adapters and daemon IPC.
|
|
1472
|
+
activationTransitions = Effect5.unsafeMakeSemaphore(1);
|
|
1473
|
+
activationDispatches = /* @__PURE__ */ new Map();
|
|
1315
1474
|
activationTimers = /* @__PURE__ */ new Map();
|
|
1316
|
-
approvalDirectory =
|
|
1317
|
-
|
|
1475
|
+
approvalDirectory = join2(
|
|
1476
|
+
tmpdir2(),
|
|
1318
1477
|
"glove-foundry-approvals",
|
|
1319
1478
|
randomUUID2()
|
|
1320
1479
|
);
|
|
1321
|
-
coreCommandDirectory =
|
|
1322
|
-
|
|
1480
|
+
coreCommandDirectory = join2(
|
|
1481
|
+
tmpdir2(),
|
|
1323
1482
|
"glove-foundry-core-commands",
|
|
1324
1483
|
randomUUID2()
|
|
1325
1484
|
);
|
|
@@ -1440,63 +1599,21 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
1440
1599
|
pollIntervalMs: configured?.pollIntervalMs ?? defaults.pollIntervalMs,
|
|
1441
1600
|
idlePollIntervalMs: configured?.idlePollIntervalMs ?? defaults.idlePollIntervalMs
|
|
1442
1601
|
};
|
|
1443
|
-
this.
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
resolvedEnvironment[FOUNDRY_CORE_COMMAND_DIRECTORY_ENV] = this.coreCommandDirectory;
|
|
1456
|
-
}
|
|
1457
|
-
if (this.applicationFilePath) {
|
|
1458
|
-
resolvedEnvironment[FOUNDRY_APPLICATION_ENV] = this.applicationFilePath;
|
|
1459
|
-
}
|
|
1460
|
-
return resolvedEnvironment;
|
|
1602
|
+
if (this.application.daemon && !this.applicationFilePath) {
|
|
1603
|
+
throw new Error("A daemon adapter requires applicationFilePath so Foundry can load it in the managed process.");
|
|
1604
|
+
}
|
|
1605
|
+
this.signalRunner = new FoundryExecutionBackend({
|
|
1606
|
+
rootDir: this.rootDir,
|
|
1607
|
+
agents: this.agents.map((agent) => ({ route: agent.route, filePath: agent.filePath })),
|
|
1608
|
+
runner: this.execution,
|
|
1609
|
+
observer: this.observer,
|
|
1610
|
+
env: {
|
|
1611
|
+
[FOUNDRY_APPROVAL_DIRECTORY_ENV]: this.approvalDirectory,
|
|
1612
|
+
[FOUNDRY_CORE_COMMAND_DIRECTORY_ENV]: this.coreCommandDirectory,
|
|
1613
|
+
...this.applicationFilePath ? { [FOUNDRY_APPLICATION_ENV]: this.applicationFilePath } : {}
|
|
1461
1614
|
}
|
|
1462
|
-
};
|
|
1463
|
-
this.scheduleAdapter = new ScheduleMemoryAdapter();
|
|
1464
|
-
const signalAdapter = new MemoryAdapter();
|
|
1465
|
-
const signalScheduleReconciler = new ScheduleReconciler({
|
|
1466
|
-
adapter: this.scheduleAdapter,
|
|
1467
|
-
kinds: ["signal"],
|
|
1468
|
-
triggerFn: (schedule, scheduledFor) => this.signalRunner.triggerSignal(
|
|
1469
|
-
schedule.target,
|
|
1470
|
-
schedule.input,
|
|
1471
|
-
{ id: schedule.id, scheduledFor }
|
|
1472
|
-
),
|
|
1473
|
-
hasPendingOrRunning: (schedule) => this.signalRunner.hasPendingOrRunningForSignal(schedule.target),
|
|
1474
|
-
parseInterval,
|
|
1475
|
-
onError: (error, schedule) => this.observability.append({
|
|
1476
|
-
type: "schedule.error",
|
|
1477
|
-
category: "activation",
|
|
1478
|
-
data: { scheduleId: schedule?.id, error: error.message }
|
|
1479
|
-
})
|
|
1480
|
-
});
|
|
1481
|
-
this.signalRunner = new SignalRunner({
|
|
1482
|
-
adapter: signalAdapter,
|
|
1483
|
-
pollIntervalMs: this.execution.pollIntervalMs,
|
|
1484
|
-
idlePollIntervalMs: this.execution.idlePollIntervalMs,
|
|
1485
|
-
maxConcurrent: this.execution.maxConcurrent,
|
|
1486
|
-
maxAttempts: this.execution.maxAttempts,
|
|
1487
|
-
retryBackoffMs: this.execution.retryBackoffMs,
|
|
1488
|
-
subscribers: [this.observer],
|
|
1489
|
-
scheduleReconciler: signalScheduleReconciler,
|
|
1490
|
-
envProvider: this.envProvider,
|
|
1491
|
-
stationId: "foundry-local",
|
|
1492
|
-
failUnknownSignals: true
|
|
1493
1615
|
});
|
|
1494
|
-
|
|
1495
|
-
this.signalRunner.registerSignal(
|
|
1496
|
-
this.executionSignal(discovered),
|
|
1497
|
-
executionAgentEntrypoint()
|
|
1498
|
-
);
|
|
1499
|
-
}
|
|
1616
|
+
this.scheduleAdapter = this.signalRunner.schedules;
|
|
1500
1617
|
const topologyLayer = memoryTopologyStore;
|
|
1501
1618
|
const defaultServices = Layer3.mergeAll(
|
|
1502
1619
|
memoryAccountDirectory(this.application.accounts ?? []),
|
|
@@ -1586,26 +1703,25 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
1586
1703
|
if (!this.started) {
|
|
1587
1704
|
await this.services.dispose();
|
|
1588
1705
|
await Promise.all([
|
|
1589
|
-
|
|
1590
|
-
|
|
1706
|
+
rm2(this.approvalDirectory, { recursive: true, force: true }),
|
|
1707
|
+
rm2(this.coreCommandDirectory, { recursive: true, force: true })
|
|
1591
1708
|
]);
|
|
1592
1709
|
this.disposed = true;
|
|
1593
1710
|
return;
|
|
1594
1711
|
}
|
|
1595
1712
|
for (const timer of this.activationTimers.values()) clearTimeout(timer);
|
|
1596
1713
|
this.activationTimers.clear();
|
|
1714
|
+
this.activationDispatches.clear();
|
|
1597
1715
|
await this.connectionSupervisor.stopAll();
|
|
1598
1716
|
this.observability.append({ type: "runtime.stop.signals", category: "system", data: {} });
|
|
1599
1717
|
await this.signalRunner.stop({ graceful: true, timeoutMs: 1e4 });
|
|
1600
1718
|
this.observability.append({ type: "runtime.stop.loops", category: "system", data: {} });
|
|
1601
1719
|
await this.settleRunnerLoops(1e3);
|
|
1602
1720
|
this.runnerLoops.length = 0;
|
|
1603
|
-
await this.envStore.close();
|
|
1604
|
-
await this.scheduleAdapter.close?.();
|
|
1605
1721
|
await this.services.dispose();
|
|
1606
1722
|
await Promise.all([
|
|
1607
|
-
|
|
1608
|
-
|
|
1723
|
+
rm2(this.approvalDirectory, { recursive: true, force: true }),
|
|
1724
|
+
rm2(this.coreCommandDirectory, { recursive: true, force: true })
|
|
1609
1725
|
]);
|
|
1610
1726
|
this.started = false;
|
|
1611
1727
|
this.disposed = true;
|
|
@@ -1864,9 +1980,9 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
1864
1980
|
return Effect5.runPromise(this.data.listWorkspaceEntries(workspaceId));
|
|
1865
1981
|
}
|
|
1866
1982
|
async putWorkspaceEntry(workspaceId, key, value) {
|
|
1867
|
-
const
|
|
1868
|
-
await Effect5.runPromise(this.data.putWorkspaceEntry(
|
|
1869
|
-
return
|
|
1983
|
+
const entry2 = { workspaceId, key, value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1984
|
+
await Effect5.runPromise(this.data.putWorkspaceEntry(entry2));
|
|
1985
|
+
return entry2;
|
|
1870
1986
|
}
|
|
1871
1987
|
listSharedInbox(workspaceId) {
|
|
1872
1988
|
return Effect5.runPromise(this.data.listInboxItems(workspaceId));
|
|
@@ -2431,7 +2547,7 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
2431
2547
|
async health() {
|
|
2432
2548
|
const [execution, environment, activations] = await Promise.all([
|
|
2433
2549
|
this.signalRunner.getAdapter().ping(),
|
|
2434
|
-
this.
|
|
2550
|
+
this.signalRunner.ping(),
|
|
2435
2551
|
this.scheduleAdapter.ping()
|
|
2436
2552
|
]);
|
|
2437
2553
|
return {
|
|
@@ -2867,12 +2983,6 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
2867
2983
|
}
|
|
2868
2984
|
}
|
|
2869
2985
|
}
|
|
2870
|
-
executionSignal(discovered) {
|
|
2871
|
-
return compileAgentDefinition(
|
|
2872
|
-
discovered.definition,
|
|
2873
|
-
discovered.route
|
|
2874
|
-
);
|
|
2875
|
-
}
|
|
2876
2986
|
async seedTopology() {
|
|
2877
2987
|
for (const route of this.application.routes ?? []) {
|
|
2878
2988
|
await this.putRoute(route);
|
|
@@ -2920,7 +3030,19 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
2920
3030
|
await this.executionEnvelope(request)
|
|
2921
3031
|
);
|
|
2922
3032
|
}
|
|
3033
|
+
withActivationTransition(work) {
|
|
3034
|
+
return Effect5.runPromise(this.activationTransitions.withPermits(1)(Effect5.tryPromise({
|
|
3035
|
+
try: work,
|
|
3036
|
+
catch: (cause) => cause
|
|
3037
|
+
})));
|
|
3038
|
+
}
|
|
2923
3039
|
async executeCoreCommand(command, parentRunId) {
|
|
3040
|
+
if (command.type === "sleep" || command.type === "schedule" || command.type.startsWith("schedule.")) {
|
|
3041
|
+
return this.withActivationTransition(() => this.executeCoreCommandUnlocked(command, parentRunId));
|
|
3042
|
+
}
|
|
3043
|
+
return this.executeCoreCommandUnlocked(command, parentRunId);
|
|
3044
|
+
}
|
|
3045
|
+
async executeCoreCommandUnlocked(command, parentRunId) {
|
|
2924
3046
|
if (command.type === "transmit") {
|
|
2925
3047
|
const request2 = await getFoundryCoreCommandRequest(
|
|
2926
3048
|
this.coreCommandDirectory,
|
|
@@ -3126,6 +3248,7 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3126
3248
|
return `activation/${activationId.replaceAll("_", "-")}`;
|
|
3127
3249
|
}
|
|
3128
3250
|
async disarmActivation(activation) {
|
|
3251
|
+
this.activationDispatches.delete(activation.id);
|
|
3129
3252
|
const timer = this.activationTimers.get(activation.id);
|
|
3130
3253
|
if (timer) {
|
|
3131
3254
|
clearTimeout(timer);
|
|
@@ -3279,11 +3402,13 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3279
3402
|
data: { commandId: command.id, activationId: existing.id }
|
|
3280
3403
|
});
|
|
3281
3404
|
}
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3405
|
+
reconstructActivations() {
|
|
3406
|
+
return this.withActivationTransition(async () => {
|
|
3407
|
+
for (const activation of await Effect5.runPromise(this.data.listActivations())) {
|
|
3408
|
+
if (activation.status === "paused" || activation.status === "completed" || activation.status === "cancelled") continue;
|
|
3409
|
+
await this.materializeActivation(activation);
|
|
3410
|
+
}
|
|
3411
|
+
});
|
|
3287
3412
|
}
|
|
3288
3413
|
async materializeActivation(activation) {
|
|
3289
3414
|
if (this.materializedActivations.has(activation.id)) return;
|
|
@@ -3373,7 +3498,11 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3373
3498
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3374
3499
|
};
|
|
3375
3500
|
await Effect5.runPromise(this.data.putActivation(active));
|
|
3376
|
-
const
|
|
3501
|
+
const generation = {};
|
|
3502
|
+
this.activationDispatches.set(activation.id, generation);
|
|
3503
|
+
const dispatch = () => this.withActivationTransition(async () => {
|
|
3504
|
+
if (this.activationDispatches.get(activation.id) !== generation) return;
|
|
3505
|
+
this.activationDispatches.delete(activation.id);
|
|
3377
3506
|
this.activationTimers.delete(activation.id);
|
|
3378
3507
|
if (this.disposed || !this.started) return;
|
|
3379
3508
|
const current = await Effect5.runPromise(this.data.getActivation(activation.id));
|
|
@@ -3393,7 +3522,7 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3393
3522
|
runId: activation.createdByRunId,
|
|
3394
3523
|
data: { commandId: activation.id, childRunId: runId, type: activation.kind }
|
|
3395
3524
|
});
|
|
3396
|
-
void this.waitForRun(runId).then(async (
|
|
3525
|
+
void this.waitForRun(runId).then((run) => this.withActivationTransition(async () => {
|
|
3397
3526
|
if (!run || run.status !== "completed" && run.status !== "failed" && run.status !== "cancelled") return;
|
|
3398
3527
|
const latest = await Effect5.runPromise(this.data.getActivation(activation.id));
|
|
3399
3528
|
if (!latest || latest.status !== "active" || latest.lastRunId !== runId) return;
|
|
@@ -3402,7 +3531,7 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3402
3531
|
status: run.status === "cancelled" ? "cancelled" : "completed",
|
|
3403
3532
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3404
3533
|
}));
|
|
3405
|
-
}).catch((cause) => {
|
|
3534
|
+
})).catch((cause) => {
|
|
3406
3535
|
this.observability.append({
|
|
3407
3536
|
type: "activation.persistence.error",
|
|
3408
3537
|
category: "activation",
|
|
@@ -3411,7 +3540,7 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3411
3540
|
data: { error: cause instanceof Error ? cause.message : String(cause) }
|
|
3412
3541
|
});
|
|
3413
3542
|
});
|
|
3414
|
-
};
|
|
3543
|
+
});
|
|
3415
3544
|
const arm = () => {
|
|
3416
3545
|
const remaining = target - Date.now();
|
|
3417
3546
|
if (remaining <= 0) {
|
|
@@ -3437,8 +3566,7 @@ var FoundryRuntime = class _FoundryRuntime {
|
|
|
3437
3566
|
runId: activation.createdByRunId,
|
|
3438
3567
|
data: { commandId: activation.id, recurring: false, nextRunAt: activation.timing.at }
|
|
3439
3568
|
});
|
|
3440
|
-
|
|
3441
|
-
else arm();
|
|
3569
|
+
arm();
|
|
3442
3570
|
}
|
|
3443
3571
|
async deliverOutbound(command, parentRunId, signal) {
|
|
3444
3572
|
return this.dispatchOutbound({
|