@akagilnc/pi-workflow-roles 0.1.5918 → 0.1.5922
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 +2 -2
- package/dist/acp-host/production-host.js +2 -3
- package/dist/factory-board.js +4 -13
- package/dist/headless-host/production-host.js +2 -3
- package/dist/oauth-keepalive.js +3 -12
- package/dist/role-runtime.js +2 -3
- package/dist/ticket-trajectory.js +4 -14
- package/package.json +6 -6
- package/src/factory-board.ts +4 -22
- package/src/oauth-keepalive.ts +3 -22
- package/src/role-runtime.ts +3 -5
- package/src/ticket-trajectory.ts +4 -24
package/README.md
CHANGED
|
@@ -140,10 +140,10 @@ ak-role --model <provider/model[:thinking]> resume <runId> "<ruling>"
|
|
|
140
140
|
|
|
141
141
|
## Tests
|
|
142
142
|
|
|
143
|
-
During the [ADR 0090](docs/adr/0090-tests-run-on-vitest-fixtures-and-helpers-from-the-platform.md)
|
|
143
|
+
During the [ADR 0090](docs/adr/0090-tests-run-on-vitest-fixtures-and-helpers-from-the-platform.md) migration, `pnpm test` runs all unit/contract tests (including root-level tests) with Vitest. `test:vitest` also runs the migrated factory-board timer tests and the integration/package expand examples. `test:all` runs these first, then the remaining Node tests; CI retains both entries until migration closes. `test:integration` uses the same Vitest batch, including the package expand example, followed by the remaining integration tests. For a focused run:
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
|
-
pnpm exec vitest run --project unit
|
|
146
|
+
pnpm exec vitest run --project unit-contract
|
|
147
147
|
pnpm exec vitest run --project integration test/integration/public-cli-bin-startup.test.ts
|
|
148
148
|
pnpm exec vitest run --project package test/package/published-dist-import-closure.test.ts
|
|
149
149
|
```
|
|
@@ -22648,7 +22648,7 @@ async function executeActivationStage(role, stage, infrastructure) {
|
|
|
22648
22648
|
role,
|
|
22649
22649
|
stageId: stage.id,
|
|
22650
22650
|
status: "failed",
|
|
22651
|
-
timestamp:
|
|
22651
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22652
22652
|
cause: namedActivationCause(activationError)
|
|
22653
22653
|
});
|
|
22654
22654
|
} catch (traceError) {
|
|
@@ -23459,7 +23459,6 @@ function createRoleRuntimeExtension(dependencies, deliveryRequestLimit) {
|
|
|
23459
23459
|
activeCollector = await collectorBusiness.activate(context);
|
|
23460
23460
|
}
|
|
23461
23461
|
};
|
|
23462
|
-
const clock = dependencies.activationClock ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
23463
23462
|
const writeTrace = dependencies.activationTraceWriter ?? writeActivationTraceRecord;
|
|
23464
23463
|
roleHost.on("session_start", async (event, ctx) => {
|
|
23465
23464
|
admitted = false;
|
|
@@ -23566,7 +23565,7 @@ function createRoleRuntimeExtension(dependencies, deliveryRequestLimit) {
|
|
|
23566
23565
|
});
|
|
23567
23566
|
navigatorAttendance.prepare();
|
|
23568
23567
|
}
|
|
23569
|
-
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), {
|
|
23568
|
+
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), { writeTrace });
|
|
23570
23569
|
registerReportTicketTool(roleHost, hostActions);
|
|
23571
23570
|
roleHost.setActiveTools([
|
|
23572
23571
|
.../* @__PURE__ */ new Set([...roleHost.getActiveTools(), REPORT_TICKET_TOOL_NAME])
|
package/dist/factory-board.js
CHANGED
|
@@ -1360,13 +1360,6 @@ export async function writeFactoryBoardPage(input) {
|
|
|
1360
1360
|
const outputPath = await writePageOutsideLedgers(gate.ledgerRoots, gate.outputAbsolute, html, BOARD_OUTPUT_REFUSAL);
|
|
1361
1361
|
return { outputPath, html };
|
|
1362
1362
|
}
|
|
1363
|
-
const defaultBoardScheduler = {
|
|
1364
|
-
every(ms, tick) {
|
|
1365
|
-
const timer = setInterval(tick, ms);
|
|
1366
|
-
timer.unref?.();
|
|
1367
|
-
return () => clearInterval(timer);
|
|
1368
|
-
},
|
|
1369
|
-
};
|
|
1370
1363
|
/**
|
|
1371
1364
|
* Production board lifecycle: write immediately, then regenerate on the declared
|
|
1372
1365
|
* refresh boundary so the same viewing surface observes new runs / generated-at.
|
|
@@ -1382,8 +1375,6 @@ export function startFactoryBoardPage(input) {
|
|
|
1382
1375
|
if (input.view === undefined && input.loadView === undefined) {
|
|
1383
1376
|
throw new Error("view or loadView is required");
|
|
1384
1377
|
}
|
|
1385
|
-
const clock = input.clock ?? (() => new Date());
|
|
1386
|
-
const scheduler = input.scheduler ?? defaultBoardScheduler;
|
|
1387
1378
|
let stopped = false;
|
|
1388
1379
|
let cancel;
|
|
1389
1380
|
let inFlight;
|
|
@@ -1424,7 +1415,7 @@ export function startFactoryBoardPage(input) {
|
|
|
1424
1415
|
return writeFactoryBoardPage({
|
|
1425
1416
|
books: input.books,
|
|
1426
1417
|
view,
|
|
1427
|
-
now:
|
|
1418
|
+
now: new Date(),
|
|
1428
1419
|
outputPath: input.outputPath,
|
|
1429
1420
|
refreshBoundarySeconds,
|
|
1430
1421
|
});
|
|
@@ -1448,9 +1439,9 @@ export function startFactoryBoardPage(input) {
|
|
|
1448
1439
|
if (stopped || lastRejection !== undefined)
|
|
1449
1440
|
return first;
|
|
1450
1441
|
const intervalMs = Math.max(1, Math.round(refreshBoundarySeconds * 1000));
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1442
|
+
const timer = setInterval(queueWrite, intervalMs);
|
|
1443
|
+
timer.unref?.();
|
|
1444
|
+
cancel = () => clearInterval(timer);
|
|
1454
1445
|
return first;
|
|
1455
1446
|
})
|
|
1456
1447
|
.catch((error) => {
|
|
@@ -22615,7 +22615,7 @@ async function executeActivationStage(role, stage, infrastructure) {
|
|
|
22615
22615
|
role,
|
|
22616
22616
|
stageId: stage.id,
|
|
22617
22617
|
status: "failed",
|
|
22618
|
-
timestamp:
|
|
22618
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22619
22619
|
cause: namedActivationCause(activationError)
|
|
22620
22620
|
});
|
|
22621
22621
|
} catch (traceError) {
|
|
@@ -23426,7 +23426,6 @@ function createRoleRuntimeExtension(dependencies, deliveryRequestLimit) {
|
|
|
23426
23426
|
activeCollector = await collectorBusiness.activate(context);
|
|
23427
23427
|
}
|
|
23428
23428
|
};
|
|
23429
|
-
const clock = dependencies.activationClock ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
23430
23429
|
const writeTrace = dependencies.activationTraceWriter ?? writeActivationTraceRecord;
|
|
23431
23430
|
roleHost.on("session_start", async (event, ctx) => {
|
|
23432
23431
|
admitted = false;
|
|
@@ -23533,7 +23532,7 @@ function createRoleRuntimeExtension(dependencies, deliveryRequestLimit) {
|
|
|
23533
23532
|
});
|
|
23534
23533
|
navigatorAttendance.prepare();
|
|
23535
23534
|
}
|
|
23536
|
-
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), {
|
|
23535
|
+
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), { writeTrace });
|
|
23537
23536
|
registerReportTicketTool(roleHost, hostActions);
|
|
23538
23537
|
roleHost.setActiveTools([
|
|
23539
23538
|
.../* @__PURE__ */ new Set([...roleHost.getActiveTools(), REPORT_TICKET_TOOL_NAME])
|
package/dist/oauth-keepalive.js
CHANGED
|
@@ -44,14 +44,6 @@ export function readOAuthKeepaliveProviders(path = oauthKeepaliveSettingPath())
|
|
|
44
44
|
throw error;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
const defaultScheduler = {
|
|
48
|
-
every(ms, tick) {
|
|
49
|
-
const timer = setInterval(tick, ms);
|
|
50
|
-
// Allow the process to exit if a caller forgets stop (same discipline as trajectory).
|
|
51
|
-
timer.unref?.();
|
|
52
|
-
return () => clearInterval(timer);
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
47
|
function errorClassName(error) {
|
|
56
48
|
if (error instanceof Error && error.name.trim() !== "")
|
|
57
49
|
return error.name;
|
|
@@ -92,7 +84,6 @@ function emitWarning(ctx, providerId, error) {
|
|
|
92
84
|
export function createOAuthKeepalive(options = {}) {
|
|
93
85
|
const providers = Object.freeze((options.providers ?? DEFAULT_OAUTH_KEEPALIVE_PROVIDERS).map((id) => id));
|
|
94
86
|
const intervalMs = options.intervalMs ?? OAUTH_KEEPALIVE_INTERVAL_MS;
|
|
95
|
-
const scheduler = options.scheduler ?? defaultScheduler;
|
|
96
87
|
let cancel;
|
|
97
88
|
let abortController;
|
|
98
89
|
/** Bare single-flight gate; survives stop/start until the in-flight call settles. */
|
|
@@ -140,9 +131,9 @@ export function createOAuthKeepalive(options = {}) {
|
|
|
140
131
|
// Reload / re-start: tear down any prior interval before hanging a new one.
|
|
141
132
|
stop();
|
|
142
133
|
abortController = new AbortController();
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
134
|
+
const timer = setInterval(() => { void runTick(ctx); }, intervalMs);
|
|
135
|
+
timer.unref?.();
|
|
136
|
+
cancel = () => clearInterval(timer);
|
|
146
137
|
},
|
|
147
138
|
stop,
|
|
148
139
|
};
|
package/dist/role-runtime.js
CHANGED
|
@@ -211,7 +211,7 @@ async function executeActivationStage(role, stage, infrastructure) {
|
|
|
211
211
|
role,
|
|
212
212
|
stageId: stage.id,
|
|
213
213
|
status: "failed",
|
|
214
|
-
timestamp:
|
|
214
|
+
timestamp: new Date().toISOString(),
|
|
215
215
|
cause: namedActivationCause(activationError),
|
|
216
216
|
});
|
|
217
217
|
}
|
|
@@ -1141,7 +1141,6 @@ deliveryRequestLimit) {
|
|
|
1141
1141
|
activeCollector = await collectorBusiness.activate(context);
|
|
1142
1142
|
},
|
|
1143
1143
|
};
|
|
1144
|
-
const clock = dependencies.activationClock ?? (() => new Date().toISOString());
|
|
1145
1144
|
const writeTrace = dependencies.activationTraceWriter ?? writeActivationTraceRecord;
|
|
1146
1145
|
roleHost.on("session_start", async (event, ctx) => {
|
|
1147
1146
|
admitted = false;
|
|
@@ -1267,7 +1266,7 @@ deliveryRequestLimit) {
|
|
|
1267
1266
|
// The lookup starts with the parent role (#1160 / #1192).
|
|
1268
1267
|
navigatorAttendance.prepare();
|
|
1269
1268
|
}
|
|
1270
|
-
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), {
|
|
1269
|
+
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), { writeTrace });
|
|
1271
1270
|
// #1171: same registration mouth as submission tools; when-to-call is on the tool itself.
|
|
1272
1271
|
registerReportTicketTool(roleHost, hostActions);
|
|
1273
1272
|
roleHost.setActiveTools([
|
|
@@ -726,14 +726,6 @@ export async function writeTicketTrajectoryPage(input) {
|
|
|
726
726
|
const outputPath = await writePageOutsideLedgers([gate.ledgerRoot], gate.outputAbsolute, html, TRAJECTORY_OUTPUT_REFUSAL);
|
|
727
727
|
return { outputPath, html };
|
|
728
728
|
}
|
|
729
|
-
const defaultScheduler = {
|
|
730
|
-
every(ms, tick) {
|
|
731
|
-
const timer = setInterval(tick, ms);
|
|
732
|
-
// Allow the process to exit naturally if the caller forgets stop in scripts.
|
|
733
|
-
timer.unref?.();
|
|
734
|
-
return () => clearInterval(timer);
|
|
735
|
-
},
|
|
736
|
-
};
|
|
737
729
|
/**
|
|
738
730
|
* Production page lifecycle: write immediately, then regenerate on the declared
|
|
739
731
|
* refresh boundary so the same viewing surface observes new runs / generated-at.
|
|
@@ -745,8 +737,6 @@ export function startTicketTrajectoryPage(input) {
|
|
|
745
737
|
if (!(refreshBoundarySeconds > 0) || !Number.isFinite(refreshBoundarySeconds)) {
|
|
746
738
|
throw new Error("refreshBoundarySeconds must be a positive finite number");
|
|
747
739
|
}
|
|
748
|
-
const clock = input.clock ?? (() => new Date());
|
|
749
|
-
const scheduler = input.scheduler ?? defaultScheduler;
|
|
750
740
|
let stopped = false;
|
|
751
741
|
let cancel;
|
|
752
742
|
let inFlight;
|
|
@@ -787,7 +777,7 @@ export function startTicketTrajectoryPage(input) {
|
|
|
787
777
|
return writeTicketTrajectoryPage({
|
|
788
778
|
ledgerDir: input.ledgerDir,
|
|
789
779
|
ticketSnapshot: input.ticketSnapshot,
|
|
790
|
-
now:
|
|
780
|
+
now: new Date(),
|
|
791
781
|
outputPath: input.outputPath,
|
|
792
782
|
refreshBoundarySeconds,
|
|
793
783
|
});
|
|
@@ -811,9 +801,9 @@ export function startTicketTrajectoryPage(input) {
|
|
|
811
801
|
if (stopped || lastRejection !== undefined)
|
|
812
802
|
return first;
|
|
813
803
|
const intervalMs = Math.max(1, Math.round(refreshBoundarySeconds * 1000));
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
804
|
+
const timer = setInterval(queueWrite, intervalMs);
|
|
805
|
+
timer.unref?.();
|
|
806
|
+
cancel = () => clearInterval(timer);
|
|
817
807
|
return first;
|
|
818
808
|
})
|
|
819
809
|
.catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akagilnc/pi-workflow-roles",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5922",
|
|
4
4
|
"description": "Soul-bound workflow roles for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json && node scripts/build-package.mjs",
|
|
46
46
|
"prepack": "npm run build",
|
|
47
|
-
"test": "
|
|
48
|
-
"test:fast": "
|
|
49
|
-
"test:integration": "
|
|
50
|
-
"test:all": "node scripts/run-test-all.mjs",
|
|
51
|
-
"test:vitest": "vitest run test/unit/
|
|
47
|
+
"test": "vitest run --project unit-contract",
|
|
48
|
+
"test:fast": "pnpm test",
|
|
49
|
+
"test:integration": "pnpm run test:vitest && node scripts/run-test-all.mjs integration",
|
|
50
|
+
"test:all": "pnpm run test:vitest && node scripts/run-test-all.mjs",
|
|
51
|
+
"test:vitest": "vitest run test/unit/ test/contract/ test/primary-aware-cleanup.test.ts test/integration/factory-board.test.ts test/integration/public-cli-bin-startup.test.ts test/package/published-dist-import-closure.test.ts",
|
|
52
52
|
"test:adjudication": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test-reporter=dot --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=node-test-report.txt --test test/adjudication/**/*.test.ts",
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
54
|
"migrate:book-topology": "node dist/migrate-book-topology.js"
|
package/src/factory-board.ts
CHANGED
|
@@ -35,8 +35,6 @@ import {
|
|
|
35
35
|
renderTicketTrajectoryStationHtml,
|
|
36
36
|
type TicketTrajectoryBookIndex,
|
|
37
37
|
type TicketTrajectoryRun,
|
|
38
|
-
type TrajectoryClock,
|
|
39
|
-
type TrajectoryScheduler,
|
|
40
38
|
type UnboundTrajectoryRun,
|
|
41
39
|
} from "./ticket-trajectory.ts";
|
|
42
40
|
import type { BoardSnapshot, SnapshotTicket, TicketIssueState } from "./ticket-snapshot.ts";
|
|
@@ -50,10 +48,6 @@ export const UNACCEPTED_WATCH_MS = 15 * 60 * 1000;
|
|
|
50
48
|
/** Same declared refresh bound as the S1 trajectory surface (seconds). */
|
|
51
49
|
export { DEFAULT_REFRESH_BOUNDARY_SECONDS };
|
|
52
50
|
|
|
53
|
-
/** Injected clock + scheduler for the production board lifecycle. */
|
|
54
|
-
export type FactoryBoardClock = TrajectoryClock;
|
|
55
|
-
export type FactoryBoardScheduler = TrajectoryScheduler;
|
|
56
|
-
|
|
57
51
|
export type FactoryBoardPageHandle = {
|
|
58
52
|
readonly outputPath: string;
|
|
59
53
|
/** Settles when the first page write finishes (or rejects on first failure). */
|
|
@@ -1680,14 +1674,6 @@ export async function writeFactoryBoardPage(input: {
|
|
|
1680
1674
|
return { outputPath, html };
|
|
1681
1675
|
}
|
|
1682
1676
|
|
|
1683
|
-
const defaultBoardScheduler: FactoryBoardScheduler = {
|
|
1684
|
-
every(ms, tick) {
|
|
1685
|
-
const timer = setInterval(tick, ms);
|
|
1686
|
-
timer.unref?.();
|
|
1687
|
-
return () => clearInterval(timer);
|
|
1688
|
-
},
|
|
1689
|
-
};
|
|
1690
|
-
|
|
1691
1677
|
/**
|
|
1692
1678
|
* Production board lifecycle: write immediately, then regenerate on the declared
|
|
1693
1679
|
* refresh boundary so the same viewing surface observes new runs / generated-at.
|
|
@@ -1706,8 +1692,6 @@ export function startFactoryBoardPage(input: {
|
|
|
1706
1692
|
loadView?: () => Promise<FactoryBoardView>;
|
|
1707
1693
|
outputPath: string;
|
|
1708
1694
|
refreshBoundarySeconds?: number;
|
|
1709
|
-
clock?: FactoryBoardClock;
|
|
1710
|
-
scheduler?: FactoryBoardScheduler;
|
|
1711
1695
|
}): FactoryBoardPageHandle {
|
|
1712
1696
|
const refreshBoundarySeconds = input.refreshBoundarySeconds ?? DEFAULT_REFRESH_BOUNDARY_SECONDS;
|
|
1713
1697
|
if (!(refreshBoundarySeconds > 0) || !Number.isFinite(refreshBoundarySeconds)) {
|
|
@@ -1716,8 +1700,6 @@ export function startFactoryBoardPage(input: {
|
|
|
1716
1700
|
if (input.view === undefined && input.loadView === undefined) {
|
|
1717
1701
|
throw new Error("view or loadView is required");
|
|
1718
1702
|
}
|
|
1719
|
-
const clock = input.clock ?? (() => new Date());
|
|
1720
|
-
const scheduler = input.scheduler ?? defaultBoardScheduler;
|
|
1721
1703
|
|
|
1722
1704
|
let stopped = false;
|
|
1723
1705
|
let cancel: (() => void) | undefined;
|
|
@@ -1759,7 +1741,7 @@ export function startFactoryBoardPage(input: {
|
|
|
1759
1741
|
return writeFactoryBoardPage({
|
|
1760
1742
|
books: input.books,
|
|
1761
1743
|
view,
|
|
1762
|
-
now:
|
|
1744
|
+
now: new Date(),
|
|
1763
1745
|
outputPath: input.outputPath,
|
|
1764
1746
|
refreshBoundarySeconds,
|
|
1765
1747
|
});
|
|
@@ -1781,9 +1763,9 @@ export function startFactoryBoardPage(input: {
|
|
|
1781
1763
|
.then((first) => {
|
|
1782
1764
|
if (stopped || lastRejection !== undefined) return first;
|
|
1783
1765
|
const intervalMs = Math.max(1, Math.round(refreshBoundarySeconds * 1000));
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1766
|
+
const timer = setInterval(queueWrite, intervalMs);
|
|
1767
|
+
timer.unref?.();
|
|
1768
|
+
cancel = () => clearInterval(timer);
|
|
1787
1769
|
return first;
|
|
1788
1770
|
})
|
|
1789
1771
|
.catch((error) => {
|
package/src/oauth-keepalive.ts
CHANGED
|
@@ -59,19 +59,11 @@ export function readOAuthKeepaliveProviders(
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/** Injected interval scheduler (same shape as TrajectoryScheduler). */
|
|
63
|
-
export type OAuthKeepaliveScheduler = {
|
|
64
|
-
/** Schedule `tick` every `ms` milliseconds; return a cancel function. */
|
|
65
|
-
every: (ms: number, tick: () => void) => () => void;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
62
|
export type OAuthKeepaliveOptions = {
|
|
69
63
|
/** Static provider id list; frozen at construction. Default: ["kimi-coding"]. */
|
|
70
64
|
providers?: readonly string[];
|
|
71
65
|
/** Interval between ticks in ms. Default: 60_000. First tick is after one interval. */
|
|
72
66
|
intervalMs?: number;
|
|
73
|
-
/** Injectable scheduler for tests. Default: setInterval + unref. */
|
|
74
|
-
scheduler?: OAuthKeepaliveScheduler;
|
|
75
67
|
};
|
|
76
68
|
|
|
77
69
|
/** Minimal context surface used by keepalive (public extension facade only). */
|
|
@@ -97,15 +89,6 @@ export type OAuthKeepalive = {
|
|
|
97
89
|
stop(): void;
|
|
98
90
|
};
|
|
99
91
|
|
|
100
|
-
const defaultScheduler: OAuthKeepaliveScheduler = {
|
|
101
|
-
every(ms, tick) {
|
|
102
|
-
const timer = setInterval(tick, ms);
|
|
103
|
-
// Allow the process to exit if a caller forgets stop (same discipline as trajectory).
|
|
104
|
-
timer.unref?.();
|
|
105
|
-
return () => clearInterval(timer);
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
|
|
109
92
|
function errorClassName(error: unknown): string {
|
|
110
93
|
if (error instanceof Error && error.name.trim() !== "") return error.name;
|
|
111
94
|
if (error !== null && typeof error === "object" && "name" in error) {
|
|
@@ -148,8 +131,6 @@ export function createOAuthKeepalive(options: OAuthKeepaliveOptions = {}): OAuth
|
|
|
148
131
|
(options.providers ?? DEFAULT_OAUTH_KEEPALIVE_PROVIDERS).map((id) => id),
|
|
149
132
|
) as readonly string[];
|
|
150
133
|
const intervalMs = options.intervalMs ?? OAUTH_KEEPALIVE_INTERVAL_MS;
|
|
151
|
-
const scheduler = options.scheduler ?? defaultScheduler;
|
|
152
|
-
|
|
153
134
|
let cancel: (() => void) | undefined;
|
|
154
135
|
let abortController: AbortController | undefined;
|
|
155
136
|
/** Bare single-flight gate; survives stop/start until the in-flight call settles. */
|
|
@@ -194,9 +175,9 @@ export function createOAuthKeepalive(options: OAuthKeepaliveOptions = {}): OAuth
|
|
|
194
175
|
// Reload / re-start: tear down any prior interval before hanging a new one.
|
|
195
176
|
stop();
|
|
196
177
|
abortController = new AbortController();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
178
|
+
const timer = setInterval(() => { void runTick(ctx); }, intervalMs);
|
|
179
|
+
timer.unref?.();
|
|
180
|
+
cancel = () => clearInterval(timer);
|
|
200
181
|
},
|
|
201
182
|
stop,
|
|
202
183
|
};
|
package/src/role-runtime.ts
CHANGED
|
@@ -405,7 +405,7 @@ async function emitActivationTrace(
|
|
|
405
405
|
async function executeActivationStage(
|
|
406
406
|
role: string,
|
|
407
407
|
stage: { id: string; run(): Promise<void> },
|
|
408
|
-
infrastructure: {
|
|
408
|
+
infrastructure: { writeTrace(record: ActivationTraceRecord): void | Promise<void> },
|
|
409
409
|
): Promise<void> {
|
|
410
410
|
try {
|
|
411
411
|
await stage.run();
|
|
@@ -415,7 +415,7 @@ async function executeActivationStage(
|
|
|
415
415
|
role,
|
|
416
416
|
stageId: stage.id,
|
|
417
417
|
status: "failed",
|
|
418
|
-
timestamp:
|
|
418
|
+
timestamp: new Date().toISOString(),
|
|
419
419
|
cause: namedActivationCause(activationError),
|
|
420
420
|
});
|
|
421
421
|
} catch (traceError) {
|
|
@@ -469,7 +469,6 @@ export type RoleRuntimeDependencies = {
|
|
|
469
469
|
loadDoctorCase?(path: string): Promise<import("./doctor-contracts.ts").DoctorCase>;
|
|
470
470
|
loadMergerInput?(path: string): Promise<unknown>;
|
|
471
471
|
createNavigatorAttendance?(options: { context: HostContext; role: string; phase: NavigatorPhase; subjectKey: string; invocationId: string; onEvent: NavigatorAttendanceOptions["onEvent"] }): NavigatorAttendanceDependency | Promise<NavigatorAttendanceDependency>;
|
|
472
|
-
activationClock?(): string;
|
|
473
472
|
activationTraceWriter?: (record: ActivationTraceRecord) => void | Promise<void>;
|
|
474
473
|
};
|
|
475
474
|
|
|
@@ -1499,7 +1498,6 @@ export function createRoleRuntimeExtension(
|
|
|
1499
1498
|
},
|
|
1500
1499
|
};
|
|
1501
1500
|
|
|
1502
|
-
const clock = dependencies.activationClock ?? (() => new Date().toISOString());
|
|
1503
1501
|
const writeTrace = dependencies.activationTraceWriter ?? writeActivationTraceRecord;
|
|
1504
1502
|
|
|
1505
1503
|
roleHost.on("session_start", async (event, ctx) => {
|
|
@@ -1631,7 +1629,7 @@ export function createRoleRuntimeExtension(
|
|
|
1631
1629
|
navigatorAttendance.prepare();
|
|
1632
1630
|
}
|
|
1633
1631
|
|
|
1634
|
-
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), {
|
|
1632
|
+
await executeActivationStage(entry.role, activationStage(entry.role, activateByRole), { writeTrace });
|
|
1635
1633
|
// #1171: same registration mouth as submission tools; when-to-call is on the tool itself.
|
|
1636
1634
|
registerReportTicketTool(roleHost, hostActions);
|
|
1637
1635
|
roleHost.setActiveTools([
|
package/src/ticket-trajectory.ts
CHANGED
|
@@ -66,13 +66,6 @@ export type TicketSnapshot = {
|
|
|
66
66
|
|
|
67
67
|
export type StationSource = "tool" | "invocation" | "name" | "unknown";
|
|
68
68
|
|
|
69
|
-
/** Injected clock + scheduler so tests drive the production lifecycle without wall sleep. */
|
|
70
|
-
export type TrajectoryClock = () => Date;
|
|
71
|
-
export type TrajectoryScheduler = {
|
|
72
|
-
/** Schedule `tick` every `ms` milliseconds; return a cancel function. */
|
|
73
|
-
every: (ms: number, tick: () => void) => () => void;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
69
|
export type TicketTrajectoryPageHandle = {
|
|
77
70
|
readonly outputPath: string;
|
|
78
71
|
/** Settles when the first page write finishes (or rejects on first failure). */
|
|
@@ -937,15 +930,6 @@ export async function writeTicketTrajectoryPage(input: {
|
|
|
937
930
|
return { outputPath, html };
|
|
938
931
|
}
|
|
939
932
|
|
|
940
|
-
const defaultScheduler: TrajectoryScheduler = {
|
|
941
|
-
every(ms, tick) {
|
|
942
|
-
const timer = setInterval(tick, ms);
|
|
943
|
-
// Allow the process to exit naturally if the caller forgets stop in scripts.
|
|
944
|
-
timer.unref?.();
|
|
945
|
-
return () => clearInterval(timer);
|
|
946
|
-
},
|
|
947
|
-
};
|
|
948
|
-
|
|
949
933
|
/**
|
|
950
934
|
* Production page lifecycle: write immediately, then regenerate on the declared
|
|
951
935
|
* refresh boundary so the same viewing surface observes new runs / generated-at.
|
|
@@ -957,15 +941,11 @@ export function startTicketTrajectoryPage(input: {
|
|
|
957
941
|
ticketSnapshot: TicketSnapshot;
|
|
958
942
|
outputPath: string;
|
|
959
943
|
refreshBoundarySeconds?: number;
|
|
960
|
-
clock?: TrajectoryClock;
|
|
961
|
-
scheduler?: TrajectoryScheduler;
|
|
962
944
|
}): TicketTrajectoryPageHandle {
|
|
963
945
|
const refreshBoundarySeconds = input.refreshBoundarySeconds ?? DEFAULT_REFRESH_BOUNDARY_SECONDS;
|
|
964
946
|
if (!(refreshBoundarySeconds > 0) || !Number.isFinite(refreshBoundarySeconds)) {
|
|
965
947
|
throw new Error("refreshBoundarySeconds must be a positive finite number");
|
|
966
948
|
}
|
|
967
|
-
const clock = input.clock ?? (() => new Date());
|
|
968
|
-
const scheduler = input.scheduler ?? defaultScheduler;
|
|
969
949
|
|
|
970
950
|
let stopped = false;
|
|
971
951
|
let cancel: (() => void) | undefined;
|
|
@@ -1007,7 +987,7 @@ export function startTicketTrajectoryPage(input: {
|
|
|
1007
987
|
return writeTicketTrajectoryPage({
|
|
1008
988
|
ledgerDir: input.ledgerDir,
|
|
1009
989
|
ticketSnapshot: input.ticketSnapshot,
|
|
1010
|
-
now:
|
|
990
|
+
now: new Date(),
|
|
1011
991
|
outputPath: input.outputPath,
|
|
1012
992
|
refreshBoundarySeconds,
|
|
1013
993
|
});
|
|
@@ -1029,9 +1009,9 @@ export function startTicketTrajectoryPage(input: {
|
|
|
1029
1009
|
.then((first) => {
|
|
1030
1010
|
if (stopped || lastRejection !== undefined) return first;
|
|
1031
1011
|
const intervalMs = Math.max(1, Math.round(refreshBoundarySeconds * 1000));
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1012
|
+
const timer = setInterval(queueWrite, intervalMs);
|
|
1013
|
+
timer.unref?.();
|
|
1014
|
+
cancel = () => clearInterval(timer);
|
|
1035
1015
|
return first;
|
|
1036
1016
|
})
|
|
1037
1017
|
.catch((error) => {
|