@intentius/chant-lexicon-fountain 0.124.27 → 0.124.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/op/runtime.d.ts +9 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/op/runtime.test.ts +83 -0
- package/src/op/runtime.ts +93 -0
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "897892e3f675a2e4d5d27da914798743a1159472ac616fc150275551e3e55699",
|
|
5
5
|
"meta.json": "3301437a2a56eaf1a5cc8f6eabb18a40767f916e09701b2d9bf20aba4066738c",
|
|
6
6
|
"types/index.d.ts": "cf6b634fe0a8e9e96f1d9d07e3df53838def2683dc93dbe3c7fd971c6d3908ae",
|
|
7
7
|
"rules/ftn001-no-secret-literals.ts": "dd1b54db3919afadcb5b8261db9a5db7c316a5f263c86f7a551fddb19f6ac532",
|
|
@@ -23,5 +23,5 @@
|
|
|
23
23
|
"skills/chant-fountain-ops.md": "4e72f0a0c7477a9e98f85ea45e49ba69e8a699f9c13ba0f0cf1cb20a88151373",
|
|
24
24
|
"skills/chant-fountain-locked-sandboxes.md": "7404ece96f2a3bc9105942fd3b6db156a3537db7e34c8cf2faa9ba87f5512567"
|
|
25
25
|
},
|
|
26
|
-
"composite": "
|
|
26
|
+
"composite": "d9a461b89d5f289cba62e4b0941a05cde8865390e9614fad44f985d9feb70bf8"
|
|
27
27
|
}
|
package/dist/manifest.json
CHANGED
package/dist/op/runtime.d.ts
CHANGED
|
@@ -229,6 +229,15 @@ interface TailOptions {
|
|
|
229
229
|
idleTimeoutMs: number;
|
|
230
230
|
now: () => number;
|
|
231
231
|
progress?: (record: StepRecord) => void;
|
|
232
|
+
/**
|
|
233
|
+
* Where the run's event stream goes (#4041, `chant run <op> --on fountain
|
|
234
|
+
* --events-json`). The tail writes it as the steward works: a phase for
|
|
235
|
+
* each stage the turn reports, a step for each tool call, started when the
|
|
236
|
+
* call starts and settled when it settles. `runtime` is `steward`, the run
|
|
237
|
+
* id is the conversation's, and the commit is null, since the run builds
|
|
238
|
+
* from whatever the steward's sandbox holds.
|
|
239
|
+
*/
|
|
240
|
+
runEvents?: (line: string) => void;
|
|
232
241
|
signal?: AbortSignal;
|
|
233
242
|
/**
|
|
234
243
|
* REST seam for the conversation poll (#2167). Without it the tail waits on
|
package/dist/op/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/op/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAA+C,WAAW,EAAqB,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/op/runtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-fountain",
|
|
3
|
-
"version": "0.124.
|
|
3
|
+
"version": "0.124.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Fountain lexicon for chant — sandboxed agent environments, vaults, and agents as typed estate",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"bundle": "tsx src/package-cli.ts"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@intentius/chant": "^0.124.
|
|
53
|
+
"@intentius/chant": "^0.124.28",
|
|
54
54
|
"zod": "^4.3.6"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
package/src/op/runtime.test.ts
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { describe, expect, it, vi } from "vitest";
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import type { RunEvent } from "@intentius/chant";
|
|
15
|
+
import runEventSchema from "@intentius/chant/workspace/run-event.schema.json";
|
|
13
16
|
import type { ChantConfig } from "@intentius/chant/config";
|
|
14
17
|
import type { OpConfig, StepRecord } from "@intentius/chant/op";
|
|
15
18
|
import {
|
|
@@ -1312,3 +1315,83 @@ describe("the plugin registers the provider", () => {
|
|
|
1312
1315
|
fetchSpy.mockRestore();
|
|
1313
1316
|
});
|
|
1314
1317
|
});
|
|
1318
|
+
|
|
1319
|
+
// ── the run-event stream (#4041) ──────────────────────────────────────────
|
|
1320
|
+
|
|
1321
|
+
describe("tailConversation runEvents (#4041)", () => {
|
|
1322
|
+
// The draft 2020-12 validator core's own contract tests use, loaded from core:
|
|
1323
|
+
// this package carries no ajv of its own.
|
|
1324
|
+
const { default: Ajv2020 } = createRequire(import.meta.resolve("@intentius/chant"))("ajv/dist/2020") as { default: new (o: object) => { compile(s: object): ((d: unknown) => boolean) & { errors?: unknown } } };
|
|
1325
|
+
const validate = new Ajv2020({ strict: true, allErrors: true }).compile(runEventSchema);
|
|
1326
|
+
|
|
1327
|
+
/** Parse and check a stream: schema-valid, framed, one run, each step started before it settles. */
|
|
1328
|
+
function check(lines: string[]) {
|
|
1329
|
+
const events = lines.map((l) => JSON.parse(l) as RunEvent);
|
|
1330
|
+
for (const e of events) expect(validate(e), JSON.stringify(validate.errors)).toBe(true);
|
|
1331
|
+
expect(events.map((e) => e.seq)).toEqual(events.map((_, i) => i));
|
|
1332
|
+
expect(events[0]?.type).toBe("run-start");
|
|
1333
|
+
expect(events.at(-1)?.type).toBe("run-settle");
|
|
1334
|
+
return events;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
it("streams the steward's stages as phases and its tool calls as steps, on the steward runtime", async () => {
|
|
1338
|
+
const { sse } = fakeSse([
|
|
1339
|
+
[
|
|
1340
|
+
sseEvent("1", { stream: "stage", stage: "plan" }),
|
|
1341
|
+
sseEvent("2", { stream: "stdout", blocks: [{ kind: "tool_call", id: "t1", title: "plan" }] }),
|
|
1342
|
+
sseEvent("3", { stream: "stdout", blocks: [{ kind: "tool_call_update", tool_call_id: "t1", status: "completed" }] }),
|
|
1343
|
+
sseEvent("4", { stream: "stage", stage: "apply" }),
|
|
1344
|
+
sseEvent("5", { stream: "stdout", blocks: [{ kind: "tool_call", id: "t2", title: "apply" }] }),
|
|
1345
|
+
sseEvent("6", { stream: "stdout", blocks: [{ kind: "tool_call_update", tool_call_id: "t2", status: "failed" }] }),
|
|
1346
|
+
sseEvent("7", { stream: "stdout", blocks: [{ kind: "text", body: JSON.stringify(RECORD) }] }),
|
|
1347
|
+
sseEvent("8", { stream: "stage", stage: "turn", state: "done" }),
|
|
1348
|
+
],
|
|
1349
|
+
]);
|
|
1350
|
+
const lines: string[] = [];
|
|
1351
|
+
const status = await tailConversation({
|
|
1352
|
+
sse,
|
|
1353
|
+
conversationId: "conv-1",
|
|
1354
|
+
op: "alb-deploy",
|
|
1355
|
+
startedAt: "2026-03-01T10:00:00.000Z",
|
|
1356
|
+
idleTimeoutMs: 60_000,
|
|
1357
|
+
now: fakeClock(),
|
|
1358
|
+
runEvents: (l) => lines.push(l),
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
expect(status.state).toBe("completed");
|
|
1362
|
+
const events = check(lines);
|
|
1363
|
+
expect(events[0]).toMatchObject({ runId: "conv-1", kind: "op", name: "alb-deploy", gitSha: null, runtime: "steward", runOrigin: { forge: "op" } });
|
|
1364
|
+
expect(events.map((e) => [e.type, "phase" in e ? e.phase : undefined, "step" in e ? e.step : undefined, "status" in e ? e.status : undefined])).toEqual([
|
|
1365
|
+
["run-start", undefined, undefined, undefined],
|
|
1366
|
+
["phase-start", "plan", undefined, undefined],
|
|
1367
|
+
["step-start", "plan", "plan", undefined],
|
|
1368
|
+
["step-settle", "plan", "plan", "ok"],
|
|
1369
|
+
["phase-settle", "plan", undefined, "ok"],
|
|
1370
|
+
["phase-start", "apply", undefined, undefined],
|
|
1371
|
+
["step-start", "apply", "apply", undefined],
|
|
1372
|
+
["step-settle", "apply", "apply", "failed"],
|
|
1373
|
+
["phase-settle", "apply", undefined, "failed"],
|
|
1374
|
+
["run-settle", undefined, undefined, "ok"],
|
|
1375
|
+
]);
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
it("a tail that gives up still ends the stream, failed with the reason", async () => {
|
|
1379
|
+
const sse: FountainSse = () => ({
|
|
1380
|
+
[Symbol.asyncIterator]: () => ({ next: () => new Promise<never>(() => {}) }),
|
|
1381
|
+
});
|
|
1382
|
+
const lines: string[] = [];
|
|
1383
|
+
await expect(
|
|
1384
|
+
tailConversation({
|
|
1385
|
+
sse,
|
|
1386
|
+
conversationId: "conv-1",
|
|
1387
|
+
op: "alb-deploy",
|
|
1388
|
+
startedAt: "2026-03-01T10:00:00.000Z",
|
|
1389
|
+
idleTimeoutMs: 5,
|
|
1390
|
+
now: () => Date.now(),
|
|
1391
|
+
runEvents: (l) => lines.push(l),
|
|
1392
|
+
}),
|
|
1393
|
+
).rejects.toThrow(/nothing arrived/);
|
|
1394
|
+
const events = check(lines);
|
|
1395
|
+
expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "failed", error: expect.stringMatching(/nothing arrived on conversation conv-1/) });
|
|
1396
|
+
});
|
|
1397
|
+
});
|
package/src/op/runtime.ts
CHANGED
|
@@ -65,6 +65,8 @@ import { loadChantConfig, type ChantConfig } from "@intentius/chant/config";
|
|
|
65
65
|
import type { OpConfig, OpGateResolveOptions, OpRunHandle, OpRunRecord, OpRunStartOptions, OpRunState, OpRunStatus, OpRuntimeProvider, StepRecord } from "@intentius/chant/op";
|
|
66
66
|
import { runStateOf } from "@intentius/chant/op";
|
|
67
67
|
import type { GateResolutionRecord } from "@intentius/chant/lifecycle/gate-ledger";
|
|
68
|
+
import type { RunEventSettleStatus } from "@intentius/chant";
|
|
69
|
+
import { openRunEventStream, runEventRunOf, type RunEventStream } from "@intentius/chant/run-event-stream";
|
|
68
70
|
import { resolveProfile, type FountainProfile } from "../config";
|
|
69
71
|
import {
|
|
70
72
|
defaultFountainHttp,
|
|
@@ -794,6 +796,7 @@ export function createFountainOpRuntime(opts: FountainOpRuntimeOptions = {}): Op
|
|
|
794
796
|
http,
|
|
795
797
|
...(opts.conversationPollMs !== undefined ? { pollIntervalMs: opts.conversationPollMs } : {}),
|
|
796
798
|
...(startOpts.progress ? { progress: startOpts.progress } : {}),
|
|
799
|
+
...(startOpts.runEvents ? { runEvents: startOpts.runEvents } : {}),
|
|
797
800
|
...(startOpts.signal ? { signal: startOpts.signal } : {}),
|
|
798
801
|
});
|
|
799
802
|
|
|
@@ -1077,6 +1080,15 @@ interface TailOptions {
|
|
|
1077
1080
|
idleTimeoutMs: number;
|
|
1078
1081
|
now: () => number;
|
|
1079
1082
|
progress?: (record: StepRecord) => void;
|
|
1083
|
+
/**
|
|
1084
|
+
* Where the run's event stream goes (#4041, `chant run <op> --on fountain
|
|
1085
|
+
* --events-json`). The tail writes it as the steward works: a phase for
|
|
1086
|
+
* each stage the turn reports, a step for each tool call, started when the
|
|
1087
|
+
* call starts and settled when it settles. `runtime` is `steward`, the run
|
|
1088
|
+
* id is the conversation's, and the commit is null, since the run builds
|
|
1089
|
+
* from whatever the steward's sandbox holds.
|
|
1090
|
+
*/
|
|
1091
|
+
runEvents?: (line: string) => void;
|
|
1080
1092
|
signal?: AbortSignal;
|
|
1081
1093
|
/**
|
|
1082
1094
|
* REST seam for the conversation poll (#2167). Without it the tail waits on
|
|
@@ -1164,6 +1176,85 @@ async function readConversation(
|
|
|
1164
1176
|
* one connection — ends the wait, and it ends it with an error.
|
|
1165
1177
|
*/
|
|
1166
1178
|
export async function tailConversation(opts: TailOptions): Promise<OpRunStatus> {
|
|
1179
|
+
const stream = opts.runEvents
|
|
1180
|
+
? openRunEventStream(
|
|
1181
|
+
runEventRunOf({ kind: "op", name: opts.op, runId: opts.conversationId, runtime: "steward", runOrigin: { forge: "op" } }),
|
|
1182
|
+
opts.runEvents,
|
|
1183
|
+
{ now: () => new Date(opts.now()) },
|
|
1184
|
+
)
|
|
1185
|
+
: undefined;
|
|
1186
|
+
stream?.emit({ type: "run-start", waves: null });
|
|
1187
|
+
const steps = stewardSteps(stream);
|
|
1188
|
+
try {
|
|
1189
|
+
const status = await tailTurn(opts, steps);
|
|
1190
|
+
steps.close(status.state === "completed" ? "ok" : "failed");
|
|
1191
|
+
stream?.settle(...settleOfState(status));
|
|
1192
|
+
return status;
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
steps.close("failed");
|
|
1195
|
+
stream?.settle("failed", err instanceof Error ? err.message : String(err));
|
|
1196
|
+
throw err;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/** A run's final state as its stream's `run-settle`. */
|
|
1201
|
+
function settleOfState(status: OpRunStatus): [RunEventSettleStatus, string?] {
|
|
1202
|
+
switch (status.state) {
|
|
1203
|
+
case "completed":
|
|
1204
|
+
return ["ok"];
|
|
1205
|
+
case "gated":
|
|
1206
|
+
return ["gated"];
|
|
1207
|
+
case "waiting":
|
|
1208
|
+
return ["waiting"];
|
|
1209
|
+
case "cancelled":
|
|
1210
|
+
return ["failed", status.error ?? "the run was cancelled"];
|
|
1211
|
+
default:
|
|
1212
|
+
return ["failed", status.error ?? `the run ended ${status.state}`];
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/** The steward's tool calls as run events: a phase per stage, a step per call. */
|
|
1217
|
+
interface StewardSteps {
|
|
1218
|
+
/** The tool call `key` started in `phase`. */
|
|
1219
|
+
start(key: string, phase: string, step: string): void;
|
|
1220
|
+
/** The tool call `key` settled. One never seen to start is started first, so its start still comes before its settle. */
|
|
1221
|
+
settle(key: string, phase: string, step: string, ok: boolean, durationMs: number): void;
|
|
1222
|
+
/** Settle the open phase, if any. */
|
|
1223
|
+
close(status: RunEventSettleStatus): void;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function stewardSteps(stream: RunEventStream | undefined): StewardSteps {
|
|
1227
|
+
let open: { phase: string; failed: boolean } | undefined;
|
|
1228
|
+
const started = new Set<string>();
|
|
1229
|
+
const enter = (phase: string): void => {
|
|
1230
|
+
if (!stream || open?.phase === phase) return;
|
|
1231
|
+
if (open) stream.emit({ type: "phase-settle", component: null, phase: open.phase, status: open.failed ? "failed" : "ok" });
|
|
1232
|
+
open = { phase, failed: false };
|
|
1233
|
+
stream.emit({ type: "phase-start", component: null, phase });
|
|
1234
|
+
};
|
|
1235
|
+
return {
|
|
1236
|
+
start(key, phase, step) {
|
|
1237
|
+
if (!stream || started.has(key)) return;
|
|
1238
|
+
started.add(key);
|
|
1239
|
+
enter(phase);
|
|
1240
|
+
stream.emit({ type: "step-start", component: null, phase, step });
|
|
1241
|
+
},
|
|
1242
|
+
settle(key, phase, step, ok, durationMs) {
|
|
1243
|
+
if (!stream) return;
|
|
1244
|
+
this.start(key, phase, step);
|
|
1245
|
+
started.delete(key);
|
|
1246
|
+
if (!ok && open?.phase === phase) open.failed = true;
|
|
1247
|
+
stream.emit({ type: "step-settle", component: null, phase, step, status: ok ? "ok" : "failed", durationMs });
|
|
1248
|
+
},
|
|
1249
|
+
close(status) {
|
|
1250
|
+
if (!stream || !open) return;
|
|
1251
|
+
stream.emit({ type: "phase-settle", component: null, phase: open.phase, status: open.failed ? "failed" : status });
|
|
1252
|
+
open = undefined;
|
|
1253
|
+
},
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
async function tailTurn(opts: TailOptions, steps: StewardSteps): Promise<OpRunStatus> {
|
|
1167
1258
|
const { sse, conversationId, op, startedAt, idleTimeoutMs, now } = opts;
|
|
1168
1259
|
const path = `/api/conversations/${conversationId}/stream?streams=stdout,stderr,stage&blocks=true`;
|
|
1169
1260
|
|
|
@@ -1195,6 +1286,7 @@ export async function tailConversation(opts: TailOptions): Promise<OpRunStatus>
|
|
|
1195
1286
|
const key = toolCallId(block);
|
|
1196
1287
|
const open = pending.get(key);
|
|
1197
1288
|
pending.delete(key);
|
|
1289
|
+
steps.settle(key, open?.phase ?? phase, open?.fn ?? toolCallName(block), status === "ok", open ? Math.max(0, now() - open.startedAt) : 0);
|
|
1198
1290
|
if (!opts.progress) return;
|
|
1199
1291
|
opts.progress({
|
|
1200
1292
|
phase: open?.phase ?? phase,
|
|
@@ -1219,6 +1311,7 @@ export async function tailConversation(opts: TailOptions): Promise<OpRunStatus>
|
|
|
1219
1311
|
const key = toolCallId(block);
|
|
1220
1312
|
if (!pending.has(key)) {
|
|
1221
1313
|
pending.set(key, { fn: toolCallName(block), phase, startedAt: now() });
|
|
1314
|
+
steps.start(key, phase, toolCallName(block));
|
|
1222
1315
|
}
|
|
1223
1316
|
if (block.status && SETTLED_TOOL_STATUS.has(block.status)) {
|
|
1224
1317
|
emit(block, block.status === "completed" || block.status === "success" ? "ok" : "fail");
|