@zq-silk/yui 0.6.5 → 0.6.7
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/cli/commandCatalog.js +6 -1
- package/dist/cli/invocationRouter.js +2 -1
- package/dist/cli/updatePorts.js +63 -9
- package/dist/cli.js +23 -0
- package/dist/controller/agentRuntimeObserver.js +76 -16
- package/dist/controller/controller.js +398 -76
- package/dist/controller/fileSchedulerStoreAdapter.js +60 -51
- package/dist/controller/jobSupervisor.js +2 -16
- package/dist/controller/resourceInventoryLinux.js +73 -20
- package/dist/controller/runtime.js +1 -0
- package/dist/controller/runtimeEventProcessor.js +171 -31
- package/dist/controller/updateReconciliation.js +113 -0
- package/dist/coordination/keyedWorkQueue.js +189 -0
- package/dist/core/controllerServer.js +7 -1
- package/dist/runtime/runtimeSessionCandidate.js +43 -0
- package/dist/scheduler/activeRoleRunDelivery.js +2 -4
- package/dist/scheduler/activeTaskProgress.js +2 -4
- package/dist/scheduler/leaderWakeupProcessor.js +3 -1
- package/dist/scheduler/ports.js +35 -2
- package/dist/scheduler/roleRunLiveness.js +44 -22
- package/dist/scheduler/roleRunStall.js +49 -25
- package/dist/storage/sqliteSchema.js +363 -14
- package/dist/storage/sqliteStore.js +403 -30
- package/dist/storage/storeRpc.js +5 -0
- package/dist/storage/taskStore.js +104 -6
- package/package.json +1 -1
|
@@ -10,8 +10,10 @@ import { resolveTimeZone } from "../output/timePresentation.js";
|
|
|
10
10
|
import { mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
|
|
11
11
|
import { validateInputRequest } from "../input/inputRequest.js";
|
|
12
12
|
import { validateRoleSessionSet } from "../executor/agentExecutor.js";
|
|
13
|
+
import { validatePendingTurnCompletion } from "../executor/turnCompletion.js";
|
|
13
14
|
import { validateTaskMessage } from "../message/message.js";
|
|
14
15
|
import { validateAgentRun } from "../run/agentRun.js";
|
|
16
|
+
import { compareRuntimeSessionCandidates, projectRuntimeSessionCandidate } from "../runtime/runtimeSessionCandidate.js";
|
|
15
17
|
import { FileSessionOwnerRegistry } from "../runtime/sessionOwnerRegistry.js";
|
|
16
18
|
import { validateReviewConfig } from "../review/reviewConfig.js";
|
|
17
19
|
import { validateReviewRound } from "../review/reviewRound.js";
|
|
@@ -387,6 +389,13 @@ export class FileTaskStore {
|
|
|
387
389
|
.map((aggregate) => clone(aggregate.task))
|
|
388
390
|
.sort((left, right) => numericCompare(left.id, right.id));
|
|
389
391
|
}
|
|
392
|
+
listActiveTaskIds() {
|
|
393
|
+
// The file rollback backend has no catalog index, so it filters the loaded
|
|
394
|
+
// aggregate. Layout-7 Controller hot paths use SQLite's bounded selector.
|
|
395
|
+
return this.listTasks()
|
|
396
|
+
.filter((task) => task.status === "active")
|
|
397
|
+
.map((task) => task.id);
|
|
398
|
+
}
|
|
390
399
|
getStateRevision() { return this.#state().revision; }
|
|
391
400
|
getTask(id) { return optional(this.#state().tasks[id]?.task); }
|
|
392
401
|
readNextActionFacts(taskId) {
|
|
@@ -592,12 +601,21 @@ export class FileTaskStore {
|
|
|
592
601
|
.find((job) => job.idempotencyKey === key);
|
|
593
602
|
return optional(found);
|
|
594
603
|
}
|
|
595
|
-
|
|
604
|
+
listActiveDurableJobs() {
|
|
605
|
+
// The file rollback backend has no secondary indexes, so it filters its
|
|
606
|
+
// already-loaded aggregate here. Layout-7 Controller hot paths use the
|
|
607
|
+
// SQLite implementation below, whose status index avoids this history
|
|
608
|
+
// scan; the return contract and ordering stay identical across backends.
|
|
596
609
|
const all = [];
|
|
597
610
|
for (const aggregate of Object.values(this.#state().tasks)) {
|
|
598
|
-
|
|
611
|
+
for (const job of Object.values(aggregate.durableJobs)) {
|
|
612
|
+
if (job.status === "queued" || job.status === "running")
|
|
613
|
+
all.push(job);
|
|
614
|
+
}
|
|
599
615
|
}
|
|
600
|
-
return all
|
|
616
|
+
return all
|
|
617
|
+
.map((job) => clone(job))
|
|
618
|
+
.sort((left, right) => numericCompare(`${left.taskId}/${left.id}`, `${right.taskId}/${right.id}`));
|
|
601
619
|
}
|
|
602
620
|
hasActiveDurableJobs() {
|
|
603
621
|
for (const aggregate of Object.values(this.#state().tasks)) {
|
|
@@ -732,6 +750,56 @@ export class FileTaskStore {
|
|
|
732
750
|
listRoleSessionSets(taskId) {
|
|
733
751
|
return values(this.#requireTask(taskId).roleSessionSets, (set) => set.owner.roleName);
|
|
734
752
|
}
|
|
753
|
+
listRuntimeSessionCandidates(query = {}) {
|
|
754
|
+
const state = this.#state();
|
|
755
|
+
const selectedTaskIds = query.taskIds === undefined
|
|
756
|
+
? undefined
|
|
757
|
+
: [...new Set(query.taskIds)].sort(numericCompare);
|
|
758
|
+
const taskAggregates = query.scope === "global"
|
|
759
|
+
? []
|
|
760
|
+
: selectedTaskIds === undefined
|
|
761
|
+
? Object.values(state.tasks)
|
|
762
|
+
: selectedTaskIds.flatMap((taskId) => {
|
|
763
|
+
const aggregate = state.tasks[taskId];
|
|
764
|
+
return aggregate === undefined ? [] : [aggregate];
|
|
765
|
+
});
|
|
766
|
+
const candidates = [
|
|
767
|
+
...taskAggregates.flatMap((task) => (Object.values(task.roleSessionSets).flatMap((sessions) => {
|
|
768
|
+
const candidate = projectRuntimeSessionCandidate(sessions);
|
|
769
|
+
return candidate === null ? [] : [candidate];
|
|
770
|
+
}))),
|
|
771
|
+
...(query.scope === "task" || selectedTaskIds !== undefined
|
|
772
|
+
? []
|
|
773
|
+
: Object.values(state.globalRoleSessionSets).flatMap((sessions) => {
|
|
774
|
+
const candidate = projectRuntimeSessionCandidate(sessions);
|
|
775
|
+
return candidate === null ? [] : [candidate];
|
|
776
|
+
}))
|
|
777
|
+
];
|
|
778
|
+
return candidates
|
|
779
|
+
.filter((candidate) => !query.cleanupRequiredOnly || candidate.cleanupRequired)
|
|
780
|
+
.sort(compareRuntimeSessionCandidates);
|
|
781
|
+
}
|
|
782
|
+
listPendingRuntimeTurnCompletions(taskIds) {
|
|
783
|
+
const selectedTaskIds = taskIds === undefined
|
|
784
|
+
? undefined
|
|
785
|
+
: [...new Set(taskIds)].sort(numericCompare);
|
|
786
|
+
if (selectedTaskIds?.length === 0)
|
|
787
|
+
return [];
|
|
788
|
+
const taskAggregates = selectedTaskIds === undefined
|
|
789
|
+
? Object.values(this.#state().tasks)
|
|
790
|
+
: selectedTaskIds.flatMap((taskId) => {
|
|
791
|
+
const aggregate = this.#state().tasks[taskId];
|
|
792
|
+
return aggregate === undefined ? [] : [aggregate];
|
|
793
|
+
});
|
|
794
|
+
return taskAggregates.flatMap((aggregate) => (Object.values(aggregate.roleSessionSets).flatMap((sessions) => {
|
|
795
|
+
const pending = sessions.pendingTurnCompletion;
|
|
796
|
+
return pending === null || pending === undefined
|
|
797
|
+
? []
|
|
798
|
+
: [validatePendingTurnCompletion(pending)];
|
|
799
|
+
}))).sort((left, right) => (numericCompare(left.taskId, right.taskId)
|
|
800
|
+
|| numericCompare(left.roleName, right.roleName)
|
|
801
|
+
|| numericCompare(left.runId, right.runId)));
|
|
802
|
+
}
|
|
735
803
|
saveRoleSessionSet(sessions) {
|
|
736
804
|
const stored = taskSessions(sessions);
|
|
737
805
|
const taskId = stored.owner.taskId;
|
|
@@ -836,11 +904,17 @@ export class FileTaskStore {
|
|
|
836
904
|
}
|
|
837
905
|
getAgentRun(taskId, id) { return optional(this.#state().tasks[taskId]?.agentRuns[id]); }
|
|
838
906
|
listAgentRuns(taskId) { return values(this.#requireTask(taskId).agentRuns, "id"); }
|
|
839
|
-
listPendingProviderRetries() {
|
|
907
|
+
listPendingProviderRetries(taskIds) {
|
|
840
908
|
// The legacy File store can answer the empty case without a scan fallback.
|
|
841
909
|
// If durable retry state exists, the db-only capability must fail closed
|
|
842
910
|
// instead of silently losing the Controller's wake deadline.
|
|
843
|
-
|
|
911
|
+
const tasks = taskIds === undefined
|
|
912
|
+
? this.listTasks()
|
|
913
|
+
: [...new Set(taskIds)].sort(numericCompare).flatMap((taskId) => {
|
|
914
|
+
const task = this.getTask(taskId);
|
|
915
|
+
return task === null ? [] : [task];
|
|
916
|
+
});
|
|
917
|
+
for (const task of tasks) {
|
|
844
918
|
if (task.status !== "active")
|
|
845
919
|
continue;
|
|
846
920
|
for (const run of this.listAgentRuns(task.id)) {
|
|
@@ -1104,6 +1178,21 @@ export class FileTaskStore {
|
|
|
1104
1178
|
listInputRequests(taskId) {
|
|
1105
1179
|
return values(this.#requireTask(taskId).inputRequests, "id");
|
|
1106
1180
|
}
|
|
1181
|
+
listOpenInputRequests(taskIds) {
|
|
1182
|
+
const state = this.#state();
|
|
1183
|
+
const selected = taskIds === undefined
|
|
1184
|
+
? Object.values(state.tasks)
|
|
1185
|
+
: [...new Set(taskIds)].sort(numericCompare).flatMap((taskId) => {
|
|
1186
|
+
const aggregate = state.tasks[taskId];
|
|
1187
|
+
return aggregate === undefined ? [] : [aggregate];
|
|
1188
|
+
});
|
|
1189
|
+
return selected
|
|
1190
|
+
.flatMap((aggregate) => Object.values(aggregate.inputRequests))
|
|
1191
|
+
.filter((request) => request.status === "open")
|
|
1192
|
+
.map(clone)
|
|
1193
|
+
.sort((left, right) => (numericCompare(left.taskId, right.taskId)
|
|
1194
|
+
|| numericCompare(left.id, right.id)));
|
|
1195
|
+
}
|
|
1107
1196
|
listAllInputRequests() {
|
|
1108
1197
|
return Object.values(this.#state().tasks)
|
|
1109
1198
|
.flatMap((aggregate) => Object.values(aggregate.inputRequests).map(clone))
|
|
@@ -1390,6 +1479,15 @@ export class FileTaskStore {
|
|
|
1390
1479
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
1391
1480
|
.map(([, mailbox]) => clone(mailbox));
|
|
1392
1481
|
}
|
|
1482
|
+
listReadyWorkMailboxes() {
|
|
1483
|
+
// The file rollback backend has no secondary indexes. It filters the
|
|
1484
|
+
// in-memory aggregate while preserving the indexed SQLite contract's
|
|
1485
|
+
// target-key order; production layout 7 uses the bounded SQLite query.
|
|
1486
|
+
return Object.entries(this.#state().mailboxes)
|
|
1487
|
+
.filter(([, mailbox]) => mailbox.processing !== null || mailbox.pending !== null)
|
|
1488
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
1489
|
+
.map(([, mailbox]) => clone(mailbox));
|
|
1490
|
+
}
|
|
1393
1491
|
saveWorkMailbox(value) {
|
|
1394
1492
|
let mailbox;
|
|
1395
1493
|
try {
|
|
@@ -1410,7 +1508,7 @@ export class FileTaskStore {
|
|
|
1410
1508
|
return pendingWakeupProjection(this.getWorkMailbox({ kind: "role", taskId, roleName: "leader" }));
|
|
1411
1509
|
}
|
|
1412
1510
|
listPendingWakeups() {
|
|
1413
|
-
return this.
|
|
1511
|
+
return this.listReadyWorkMailboxes()
|
|
1414
1512
|
.flatMap((mailbox) => {
|
|
1415
1513
|
const wakeup = pendingWakeupProjection(mailbox);
|
|
1416
1514
|
return wakeup === null ? [] : [wakeup];
|