@astrosheep/keiyaku 4.6.8 → 4.6.10
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 +40 -14
- package/build/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/build/plugins/claude/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/claude/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/claude/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/claude/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/build/plugins/codex/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/codex/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/opencode/index.js +1 -1
- package/build/plugins/opencode/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/opencode/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/skills/keiyaku/SKILL.md +5 -6
- package/build/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/src/akuma/akuma-instance.d.ts +2 -0
- package/build/src/akuma/akuma-instance.js +10 -0
- package/build/src/akuma/akuma-observe.d.ts +33 -8
- package/build/src/akuma/akuma-observe.js +88 -64
- package/build/src/akuma/akuma-owner.d.ts +11 -2
- package/build/src/akuma/akuma-owner.js +45 -35
- package/build/src/akuma/akuma-selection-execution.js +9 -1
- package/build/src/akuma/akuma-stream.d.ts +22 -0
- package/build/src/akuma/akuma-stream.js +116 -0
- package/build/src/akuma/akuma-wait.d.ts +1 -40
- package/build/src/akuma/akuma-wait.js +23 -84
- package/build/src/akuma/akuma.d.ts +17 -11
- package/build/src/akuma/akuma.js +2 -0
- package/build/src/akuma/allowed.d.ts +7 -12
- package/build/src/akuma/allowed.js +13 -4
- package/build/src/akuma/body-supervisor.d.ts +5 -5
- package/build/src/akuma/body-supervisor.js +22 -20
- package/build/src/akuma/body.js +28 -1
- package/build/src/akuma/call-request.d.ts +12 -22
- package/build/src/akuma/heart/activity-schema.d.ts +12 -0
- package/build/src/akuma/heart/activity-schema.js +6 -0
- package/build/src/akuma/heart/index.d.ts +59 -2
- package/build/src/akuma/heart/index.js +87 -8
- package/build/src/akuma/heart/rows.js +5 -3
- package/build/src/akuma/heart/soul.d.ts +9 -0
- package/build/src/akuma/heart/soul.js +29 -0
- package/build/src/akuma/heart/storage.d.ts +15 -0
- package/build/src/akuma/heart/storage.js +55 -0
- package/build/src/akuma/heart/timeline.d.ts +7 -0
- package/build/src/akuma/heart/timeline.js +10 -0
- package/build/src/akuma/index.d.ts +1 -0
- package/build/src/akuma/projection.d.ts +25 -0
- package/build/src/akuma/projection.js +24 -7
- package/build/src/akuma/provider-recipe.d.ts +11 -0
- package/build/src/akuma/provider-recipe.js +23 -0
- package/build/src/akuma/provider.d.ts +25 -0
- package/build/src/akuma/provider.js +99 -7
- package/build/src/akuma/providers/acp/events.js +25 -2
- package/build/src/akuma/providers/claude/events.js +76 -10
- package/build/src/akuma/providers/claude/index.js +2 -5
- package/build/src/akuma/providers/codex-app-server/events.js +17 -2
- package/build/src/akuma/providers/codex-app-server/index.js +15 -9
- package/build/src/akuma/providers/decode.d.ts +8 -0
- package/build/src/akuma/providers/decode.js +21 -0
- package/build/src/akuma/providers/grok-build/index.js +10 -26
- package/build/src/akuma/providers/opencode-sdk/event-stream.d.ts +3 -3
- package/build/src/akuma/providers/opencode-sdk/event-stream.js +2 -25
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +7 -43
- package/build/src/akuma/providers/opencode-sdk/events.js +147 -284
- package/build/src/akuma/providers/opencode-sdk/index.js +267 -421
- package/build/src/akuma/providers/opencode-sdk/native-http.d.ts +9 -0
- package/build/src/akuma/providers/opencode-sdk/native-http.js +69 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +42 -36
- package/build/src/akuma/providers/opencode-sdk/session.js +75 -82
- package/build/src/akuma/providers/paseo/events.d.ts +8 -0
- package/build/src/akuma/providers/paseo/events.js +58 -11
- package/build/src/akuma/providers/paseo/index.js +11 -7
- package/build/src/akuma/providers/pi/events.d.ts +2 -0
- package/build/src/akuma/providers/pi/events.js +32 -24
- package/build/src/akuma/providers/pi/index.js +21 -35
- package/build/src/akuma/request-lifecycle.d.ts +1 -2
- package/build/src/akuma/request-lifecycle.js +2 -8
- package/build/src/akuma/request-rendezvous.js +38 -55
- package/build/src/akuma/request-serve.d.ts +1 -1
- package/build/src/akuma/request-serve.js +77 -113
- package/build/src/akuma/selection-observation.d.ts +57 -51
- package/build/src/akuma/selection-observation.js +3 -3
- package/build/src/akuma/turn-drive.d.ts +3 -0
- package/build/src/akuma/turn-drive.js +26 -5
- package/build/src/akuma-body.js +12 -22
- package/build/src/cli/akuma-caller.js +10 -0
- package/build/src/cli/commands/akuma-grammar.js +3 -4
- package/build/src/cli/commands/akuma.js +13 -51
- package/build/src/cli/commands/contract-grammar.d.ts +7 -6
- package/build/src/cli/commands/contract-grammar.js +27 -32
- package/build/src/cli/commands/contract-help.d.ts +16 -7
- package/build/src/cli/commands/contract-help.js +20 -11
- package/build/src/cli/commands/contract.js +11 -11
- package/build/src/cli/commands/debt-grammar.d.ts +19 -0
- package/build/src/cli/commands/debt-grammar.js +323 -0
- package/build/src/cli/commands/debt-query.d.ts +5 -0
- package/build/src/cli/commands/{task-query.js → debt-query.js} +9 -9
- package/build/src/cli/commands/{task.d.ts → debt.d.ts} +2 -2
- package/build/src/cli/commands/{task.js → debt.js} +83 -143
- package/build/src/cli/coordinates.d.ts +1 -1
- package/build/src/cli/coordinates.js +1 -1
- package/build/src/cli/parse.d.ts +4 -4
- package/build/src/cli/parse.js +17 -16
- package/build/src/cli/render/akuma-activity.d.ts +11 -83
- package/build/src/cli/render/akuma-activity.js +20 -382
- package/build/src/cli/render/catalog.d.ts +2 -2
- package/build/src/cli/render/catalog.js +5 -5
- package/build/src/cli/render/contract.js +2 -2
- package/build/src/cli/render/debt.d.ts +28 -0
- package/build/src/cli/render/{task.js → debt.js} +51 -112
- package/build/src/cli/render/kanshi.js +28 -28
- package/build/src/cli/render/marks.d.ts +5 -5
- package/build/src/cli/render/marks.js +4 -10
- package/build/src/cli/render/nuke.js +1 -1
- package/build/src/cli/runtime.js +3 -3
- package/build/src/cli/scan.d.ts +1 -1
- package/build/src/cli/usage.d.ts +1 -1
- package/build/src/cli/usage.js +4 -4
- package/build/src/contract-worktree.js +3 -3
- package/build/src/debt/board.d.ts +160 -0
- package/build/src/{task → debt}/board.js +89 -89
- package/build/src/debt/catalog.d.ts +11 -0
- package/build/src/debt/catalog.js +59 -0
- package/build/src/{task → debt}/context.d.ts +1 -1
- package/build/src/{task → debt}/context.js +1 -1
- package/build/src/{task → debt}/created-observation.d.ts +6 -10
- package/build/src/{task → debt}/created-observation.js +7 -7
- package/build/src/debt/document.d.ts +37 -0
- package/build/src/{task → debt}/document.js +37 -37
- package/build/src/debt/identity.d.ts +21 -0
- package/build/src/{task → debt}/identity.js +35 -35
- package/build/src/debt/index.d.ts +111 -0
- package/build/src/debt/index.js +203 -0
- package/build/src/{task → debt}/input.d.ts +8 -8
- package/build/src/{task → debt}/input.js +21 -21
- package/build/src/debt/lifecycle-operations.d.ts +6 -0
- package/build/src/{task → debt}/lifecycle-operations.js +27 -31
- package/build/src/debt/mutation-result.d.ts +981 -0
- package/build/src/debt/mutation-result.js +109 -0
- package/build/src/debt/mutation.d.ts +424 -0
- package/build/src/debt/mutation.js +223 -0
- package/build/src/debt/operation-types.d.ts +71 -0
- package/build/src/debt/operations.d.ts +28 -0
- package/build/src/{task → debt}/operations.js +59 -60
- package/build/src/debt/query.d.ts +76 -0
- package/build/src/{task → debt}/query.js +55 -55
- package/build/src/{task → debt}/store.d.ts +13 -13
- package/build/src/{task → debt}/store.js +58 -49
- package/build/src/debt/view.d.ts +4 -0
- package/build/src/debt/view.js +7 -0
- package/build/src/identity/coordinates.d.ts +1 -1
- package/build/src/index.d.ts +13 -13
- package/build/src/index.js +9 -9
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +33 -33
- package/build/src/kanshi/report.d.ts +7 -7
- package/build/src/kanshi/select.js +4 -4
- package/build/src/library/akumas.d.ts +2 -3
- package/build/src/library/bind.d.ts +3 -3
- package/build/src/library/bind.js +5 -5
- package/build/src/library/contract-creation.js +5 -5
- package/build/src/library/contract-execution.js +4 -4
- package/build/src/library/contract-types.d.ts +2 -2
- package/build/src/library/input.d.ts +2 -2
- package/build/src/library/input.js +5 -5
- package/build/src/library/invocation.js +2 -2
- package/build/src/library/nuke.js +3 -3
- package/build/src/library/outcome.d.ts +126 -126
- package/build/src/library/outcome.js +3 -3
- package/build/src/library/selection.d.ts +1 -4
- package/build/src/library/selection.js +11 -62
- package/build/src/plugin/public.d.ts +5 -0
- package/build/src/plugin/runtime.js +3 -1
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/fence.d.ts +3 -3
- package/build/src/settlement/fence.js +4 -4
- package/build/src/settlement/holder.d.ts +16 -16
- package/build/src/settlement/holder.js +69 -63
- package/build/src/settlement/settle.d.ts +5 -5
- package/build/src/settlement/settle.js +53 -53
- package/package.json +12 -6
- package/plugins/paseo/plugin/index.server.ts +17 -0
- package/plugins/paseo/plugin/paseo-plugin.json +4 -0
- package/plugins/paseo/plugin/server/attach.ts +52 -0
- package/plugins/paseo/plugin/server/keiyaku.ts +44 -0
- package/plugins/paseo/plugin/server/message.ts +60 -0
- package/plugins/paseo/plugin/server/mirror.ts +193 -0
- package/plugins/paseo/plugin/server/model.ts +14 -0
- package/plugins/paseo/plugin/server/provider.ts +276 -0
- package/plugins/paseo/plugin/server/sdk-worker.ts +237 -0
- package/plugins/paseo/plugin/server/sdk.ts +289 -0
- package/plugins/paseo/plugin/server/stop.ts +26 -0
- package/plugins/paseo/plugin/server/transport.ts +35 -0
- package/plugins/paseo/plugin/shared/attach-rpc.ts +10 -0
- package/plugins/paseo/plugin/shared/refresh-rpc.ts +6 -0
- package/plugins/paseo/plugin/tsconfig.json +6 -0
- package/skills/keiyaku/SKILL.md +5 -6
- package/skills/keiyaku-akuma/SKILL.md +1 -1
- package/skills/keiyaku-akuma/references/automation.md +8 -8
- package/skills/keiyaku-babysit/SKILL.md +1 -1
- package/skills/keiyaku-bind/SKILL.md +9 -9
- package/skills/keiyaku-debt/SKILL.md +91 -0
- package/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/claude/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/codex/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +0 -104
- package/build/skills/keiyaku-task/SKILL.md +0 -104
- package/build/src/cli/commands/task-grammar.d.ts +0 -19
- package/build/src/cli/commands/task-grammar.js +0 -384
- package/build/src/cli/commands/task-query.d.ts +0 -5
- package/build/src/cli/render/task.d.ts +0 -39
- package/build/src/task/board.d.ts +0 -168
- package/build/src/task/catalog.d.ts +0 -11
- package/build/src/task/catalog.js +0 -59
- package/build/src/task/compose-language.d.ts +0 -57
- package/build/src/task/compose-language.js +0 -364
- package/build/src/task/compose-parser.d.ts +0 -32
- package/build/src/task/compose-parser.js +0 -180
- package/build/src/task/compose.d.ts +0 -16
- package/build/src/task/compose.js +0 -189
- package/build/src/task/document.d.ts +0 -39
- package/build/src/task/identity.d.ts +0 -21
- package/build/src/task/index.d.ts +0 -138
- package/build/src/task/index.js +0 -269
- package/build/src/task/lifecycle-operations.d.ts +0 -6
- package/build/src/task/mutation-result.d.ts +0 -1726
- package/build/src/task/mutation-result.js +0 -198
- package/build/src/task/mutation.d.ts +0 -590
- package/build/src/task/mutation.js +0 -262
- package/build/src/task/operation-types.d.ts +0 -71
- package/build/src/task/operations.d.ts +0 -28
- package/build/src/task/query.d.ts +0 -76
- package/build/src/task/view.d.ts +0 -4
- package/build/src/task/view.js +0 -7
- package/skills/keiyaku-task/SKILL.md +0 -104
- /package/build/src/{task → debt}/operation-types.js +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { abortableDelay } from "./abort.js";
|
|
3
|
+
import { AkumaNotBornError } from "./akuma-errors.js";
|
|
4
|
+
import { OBSERVATION_POLL_MS } from "./akuma-observe.js";
|
|
5
|
+
import { createTimelineReader } from "./heart/index.js";
|
|
6
|
+
import { projectTurns, selectHistory } from "./projection.js";
|
|
7
|
+
/** The fixed number of projected rows one window retains. */
|
|
8
|
+
const STREAM_WINDOW = 500;
|
|
9
|
+
/**
|
|
10
|
+
* The private end-of-observation reason. It is deliberately not the caller's
|
|
11
|
+
* abort reason: a returned observation completes, while an aborted one keeps
|
|
12
|
+
* the caller's own failure.
|
|
13
|
+
*/
|
|
14
|
+
const STREAM_RETURN = Symbol("akuma-stream-return");
|
|
15
|
+
/**
|
|
16
|
+
* Announce each retained outcome identity once per observation, keeping only
|
|
17
|
+
* the identities the current window still carries, so a long stream remembers
|
|
18
|
+
* a bounded set rather than every outcome it ever saw.
|
|
19
|
+
*/
|
|
20
|
+
async function announceOutcomes(id, rows, observed, observe) {
|
|
21
|
+
const outcomes = new Set(rows.flatMap((row) => (row.kind === "outcome" ? [row.outcome.historyId] : [])));
|
|
22
|
+
const fresh = [...outcomes].filter((historyId) => !observed.has(historyId));
|
|
23
|
+
if (fresh.length > 0)
|
|
24
|
+
await observe?.(id, fresh);
|
|
25
|
+
return outcomes;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The observation loop itself. It decodes the retained timeline through the
|
|
29
|
+
* existing Turn projection on every pull and answers with the whole latest
|
|
30
|
+
* bounded window, holding only that window and one reader. Complete windows
|
|
31
|
+
* coalesce; they are not lossless event delivery, and the existing retention
|
|
32
|
+
* metadata keeps its honest meaning.
|
|
33
|
+
*/
|
|
34
|
+
async function* observeTimeline(paths, id, signal, observe) {
|
|
35
|
+
const reader = createTimelineReader(paths);
|
|
36
|
+
let released = false;
|
|
37
|
+
const release = () => {
|
|
38
|
+
if (released)
|
|
39
|
+
return;
|
|
40
|
+
released = true;
|
|
41
|
+
reader.clear();
|
|
42
|
+
};
|
|
43
|
+
let observedOutcomes = new Set();
|
|
44
|
+
let previous;
|
|
45
|
+
signal.addEventListener("abort", release, { once: true });
|
|
46
|
+
try {
|
|
47
|
+
for (;;) {
|
|
48
|
+
signal.throwIfAborted();
|
|
49
|
+
const read = await reader.read(signal);
|
|
50
|
+
signal.throwIfAborted();
|
|
51
|
+
if (read.changed) {
|
|
52
|
+
if (!read.born)
|
|
53
|
+
throw new AkumaNotBornError(id);
|
|
54
|
+
const { rows, lowestRetained, highest } = read.slice;
|
|
55
|
+
const snapshot = selectHistory(projectTurns(rows, { lowestRetained, highest }), { limit: STREAM_WINDOW });
|
|
56
|
+
if (previous === undefined || !isDeepStrictEqual(snapshot, previous)) {
|
|
57
|
+
observedOutcomes = await announceOutcomes(id, snapshot.rows, observedOutcomes, observe);
|
|
58
|
+
previous = snapshot;
|
|
59
|
+
yield snapshot;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
await abortableDelay(OBSERVATION_POLL_MS, signal);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
signal.removeEventListener("abort", release);
|
|
67
|
+
release();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The one continuous timeline observation as its caller sees it: pull-driven,
|
|
72
|
+
* so a slow consumer accumulates no queue.
|
|
73
|
+
*
|
|
74
|
+
* The observation runs behind this wrapper because a bare async generator
|
|
75
|
+
* cannot honour an end request while a pull is pending: `return()` would queue
|
|
76
|
+
* behind a pull that an idle timeline never settles, and the observation would
|
|
77
|
+
* hold its reader and its polling timer forever. The wrapper owns an internal
|
|
78
|
+
* controller instead, and the observation watches the caller's cancellation
|
|
79
|
+
* composed with it. `return` aborts that controller, which settles the pending
|
|
80
|
+
* poll and lets the loop release its reader in the same turn; because a
|
|
81
|
+
* composed signal keeps the reason of whichever source aborted first, an end
|
|
82
|
+
* request reads as completion while a caller's own abort keeps its own failure.
|
|
83
|
+
*/
|
|
84
|
+
export function createAkumaStream(paths, id, options = {}, observe) {
|
|
85
|
+
const caller = options.signal;
|
|
86
|
+
const control = new AbortController();
|
|
87
|
+
const signal = caller === undefined ? control.signal : AbortSignal.any([caller, control.signal]);
|
|
88
|
+
const observation = observeTimeline(paths, id, signal, observe);
|
|
89
|
+
let returning;
|
|
90
|
+
const next = async () => {
|
|
91
|
+
if (returning !== undefined)
|
|
92
|
+
return { done: true, value: undefined };
|
|
93
|
+
try {
|
|
94
|
+
return await observation.next();
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// An end request is completion, not the caller's failure to see.
|
|
98
|
+
if (signal.reason === STREAM_RETURN)
|
|
99
|
+
return { done: true, value: undefined };
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const end = (value) => {
|
|
104
|
+
if (returning === undefined) {
|
|
105
|
+
control.abort(STREAM_RETURN);
|
|
106
|
+
returning = observation.return(value).then((result) => ({ done: true, value: result.value }));
|
|
107
|
+
}
|
|
108
|
+
return returning;
|
|
109
|
+
};
|
|
110
|
+
const iterator = {
|
|
111
|
+
[Symbol.asyncIterator]: () => iterator,
|
|
112
|
+
next,
|
|
113
|
+
return: end,
|
|
114
|
+
};
|
|
115
|
+
return iterator;
|
|
116
|
+
}
|
|
@@ -1,30 +1,7 @@
|
|
|
1
|
-
import type { AkumaAlias } from "../identity/selector.js";
|
|
2
1
|
import type { WorldRoot } from "../world.js";
|
|
3
2
|
import { type OutcomeObserver, type WaitReason } from "./akuma-observe.js";
|
|
4
3
|
import type { AkumaStatus } from "./akuma.js";
|
|
5
|
-
import type { DispatchAssociation } from "./dispatch-association.js";
|
|
6
|
-
import type { ActivityRow } from "./projection.js";
|
|
7
4
|
import type { AkumaUnobserved } from "./selection-observation.js";
|
|
8
|
-
export type WaitIdentityFacts = Readonly<{
|
|
9
|
-
/** The alias currently addressing this Akuma in its world, when one is bound to it. */
|
|
10
|
-
alias?: AkumaAlias;
|
|
11
|
-
/** This Akuma's Dispatch association, read from the observing repository. */
|
|
12
|
-
contract: DispatchAssociation;
|
|
13
|
-
}>;
|
|
14
|
-
/** One observed Akuma as a live wait viewer sees it: its status plus its identity facts. */
|
|
15
|
-
export type WaitObservedAkuma = Readonly<{
|
|
16
|
-
status: AkumaStatus;
|
|
17
|
-
rows: readonly ActivityRow[];
|
|
18
|
-
}> & WaitIdentityFacts;
|
|
19
|
-
/** One selected Akuma's frozen identity, resolved before the first observation round. */
|
|
20
|
-
export type WaitSelectedAkuma = Readonly<{
|
|
21
|
-
id: AkumaStatus["id"];
|
|
22
|
-
}> & WaitIdentityFacts;
|
|
23
|
-
/** A live wait viewer: the frozen selected set, then every observation round. */
|
|
24
|
-
export type WaitObserver = Readonly<{
|
|
25
|
-
selected?: (selected: readonly WaitSelectedAkuma[]) => void;
|
|
26
|
-
observe?: (observed: readonly WaitObservedAkuma[]) => void;
|
|
27
|
-
}>;
|
|
28
5
|
type WaitExecutionInput = Readonly<{
|
|
29
6
|
path: WorldRoot;
|
|
30
7
|
ids: readonly AkumaStatus["id"][];
|
|
@@ -34,14 +11,6 @@ type WaitExecutionInput = Readonly<{
|
|
|
34
11
|
/** Internal custom completion judge over the full final observation where supplied. */
|
|
35
12
|
predicate?: (status: AkumaStatus) => boolean;
|
|
36
13
|
outcomeObserved?: OutcomeObserver;
|
|
37
|
-
/** Resolves one Akuma's identity facts; consulted once per Akuma a viewer first sees. */
|
|
38
|
-
identity?: (id: AkumaStatus["id"]) => Promise<WaitIdentityFacts>;
|
|
39
|
-
/** The selected set in the caller's order; a viewer's head and scoreboard follow it. */
|
|
40
|
-
selectionOrder?: readonly AkumaStatus["id"][];
|
|
41
|
-
/** Reports the frozen selected set before the first round, so a viewer can fix its layout. */
|
|
42
|
-
onSelected?: (selected: readonly WaitSelectedAkuma[]) => void;
|
|
43
|
-
/** Reports every observation round to a live viewer. */
|
|
44
|
-
observe?: (observed: readonly WaitObservedAkuma[]) => void;
|
|
45
14
|
}>;
|
|
46
15
|
/** One wait's association-free native evidence; upper composition attaches cross-product context. */
|
|
47
16
|
export type NativeWaitResult = Readonly<{
|
|
@@ -50,14 +19,6 @@ export type NativeWaitResult = Readonly<{
|
|
|
50
19
|
observations: readonly AkumaStatus[];
|
|
51
20
|
unobserved: readonly AkumaUnobserved[];
|
|
52
21
|
}>;
|
|
53
|
-
/**
|
|
54
|
-
* The one lower set-observation owner for Akuma wait completion. It serves the
|
|
55
|
-
* standalone one-ID association-free face and the plural any/all face with one
|
|
56
|
-
* ordered round algorithm, one shared ordinary-detail budget, transient
|
|
57
|
-
* unreadable peers, and an honest final deadline edge. A silent observation —
|
|
58
|
-
* no live row callback and no custom judge — decides which running rounds to
|
|
59
|
-
* skip with the owner's lightweight completion probe over the selected set; a
|
|
60
|
-
* live viewer or a custom judge always observes full rounds.
|
|
61
|
-
*/
|
|
22
|
+
/** Wait observes completion and returns final status snapshots, never a live stream. */
|
|
62
23
|
export declare function executeWaitAkuma(input: WaitExecutionInput): Promise<NativeWaitResult>;
|
|
63
24
|
export {};
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import { AkumaNotBornError } from "./akuma-errors.js";
|
|
2
|
-
import { createLiveStatusReader, defaultWaitComplete,
|
|
2
|
+
import { createLiveStatusReader, defaultWaitComplete, readWaitComplete, observeStatusOutcome, waitForObservation, } from "./akuma-observe.js";
|
|
3
3
|
import { pathsForAkuId } from "./identity.js";
|
|
4
4
|
const SHARED_ORDINARY_BUDGET = 30;
|
|
5
|
-
async function observeWaitRound(
|
|
6
|
-
const read =
|
|
7
|
-
const options = { aperture: "monitoring", ...(ordinaryBudget === undefined ? {} : { ordinaryBudget }) };
|
|
8
|
-
if (readers === undefined)
|
|
9
|
-
return await readLiveStatus(path, id, options, signal);
|
|
10
|
-
let reader = readers.get(id);
|
|
11
|
-
if (reader === undefined) {
|
|
12
|
-
reader = createLiveStatusReader(pathsForAkuId(path, id), id);
|
|
13
|
-
readers.set(id, reader);
|
|
14
|
-
}
|
|
15
|
-
return await reader.read(options, signal);
|
|
16
|
-
};
|
|
5
|
+
async function observeWaitRound(ids, readerFor, signal) {
|
|
6
|
+
const read = (id, ordinaryBudget) => readerFor(id).read({ aperture: "monitoring", ...(ordinaryBudget === undefined ? {} : { ordinaryBudget }) }, signal);
|
|
17
7
|
signal?.throwIfAborted();
|
|
18
8
|
if (ids.length <= 1) {
|
|
19
9
|
const observations = await Promise.all(ids.map(async (id) => await read(id)));
|
|
@@ -30,8 +20,6 @@ async function observeWaitRound(path, ids, signal, readers) {
|
|
|
30
20
|
remaining -= observed.ordinarySelected;
|
|
31
21
|
}
|
|
32
22
|
catch (error) {
|
|
33
|
-
readers?.get(id)?.clear();
|
|
34
|
-
readers?.delete(id);
|
|
35
23
|
if (error instanceof AkumaNotBornError)
|
|
36
24
|
throw error;
|
|
37
25
|
unobserved.push({ id, diagnostic: diagnostic(error) });
|
|
@@ -45,80 +33,32 @@ function roundComplete(round, completion, predicate) {
|
|
|
45
33
|
return (settled.length > 0 &&
|
|
46
34
|
(completion === "any" ? settled.some(Boolean) : round.unobserved.length === 0 && settled.every(Boolean)));
|
|
47
35
|
}
|
|
48
|
-
/**
|
|
49
|
-
function selectionOrderedIds(ids, order) {
|
|
50
|
-
const present = new Set(ids);
|
|
51
|
-
const seen = new Set();
|
|
52
|
-
const ordered = [];
|
|
53
|
-
for (const id of order) {
|
|
54
|
-
if (present.has(id) && !seen.has(id)) {
|
|
55
|
-
seen.add(id);
|
|
56
|
-
ordered.push(id);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
for (const id of ids) {
|
|
60
|
-
if (!seen.has(id)) {
|
|
61
|
-
seen.add(id);
|
|
62
|
-
ordered.push(id);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return ordered;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* The one lower set-observation owner for Akuma wait completion. It serves the
|
|
69
|
-
* standalone one-ID association-free face and the plural any/all face with one
|
|
70
|
-
* ordered round algorithm, one shared ordinary-detail budget, transient
|
|
71
|
-
* unreadable peers, and an honest final deadline edge. A silent observation —
|
|
72
|
-
* no live row callback and no custom judge — decides which running rounds to
|
|
73
|
-
* skip with the owner's lightweight completion probe over the selected set; a
|
|
74
|
-
* live viewer or a custom judge always observes full rounds.
|
|
75
|
-
*/
|
|
36
|
+
/** Wait observes completion and returns final status snapshots, never a live stream. */
|
|
76
37
|
export async function executeWaitAkuma(input) {
|
|
77
|
-
const readers =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
input.signal?.throwIfAborted();
|
|
84
|
-
const known = input.identity === undefined ? undefined : await input.identity(id);
|
|
85
|
-
facts.set(id, known ?? { contract: { kind: "none" } });
|
|
86
|
-
}
|
|
87
|
-
const ordered = input.selectionOrder === undefined ? input.ids : selectionOrderedIds(input.ids, input.selectionOrder);
|
|
88
|
-
input.onSelected(ordered.map((id) => ({ id, ...facts.get(id) })));
|
|
38
|
+
const readers = new Map();
|
|
39
|
+
const readerFor = (id) => {
|
|
40
|
+
let reader = readers.get(id);
|
|
41
|
+
if (reader === undefined) {
|
|
42
|
+
reader = createLiveStatusReader(pathsForAkuId(input.path, id), id);
|
|
43
|
+
readers.set(id, reader);
|
|
89
44
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
: observeOutcomeRows(status.id, rows, input.outcomeObserved)));
|
|
94
|
-
if (input.observe === undefined)
|
|
95
|
-
return;
|
|
96
|
-
const observed = [];
|
|
97
|
-
for (const observation of round.observations) {
|
|
98
|
-
input.signal?.throwIfAborted();
|
|
99
|
-
const { status, rows } = observation;
|
|
100
|
-
let known = facts.get(status.id);
|
|
101
|
-
if (known === undefined && input.identity !== undefined) {
|
|
102
|
-
known = await input.identity(status.id);
|
|
103
|
-
facts.set(status.id, known);
|
|
104
|
-
}
|
|
105
|
-
if (known === undefined) {
|
|
106
|
-
observed.push({ status, rows, contract: { kind: "none" } });
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
observed.push({ status, rows, ...known });
|
|
110
|
-
}
|
|
111
|
-
input.observe(structuredClone(observed));
|
|
112
|
-
};
|
|
45
|
+
return reader;
|
|
46
|
+
};
|
|
47
|
+
try {
|
|
113
48
|
const waited = await waitForObservation({
|
|
49
|
+
pollMs: 250,
|
|
114
50
|
...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
|
|
115
51
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
116
|
-
...(input.predicate === undefined
|
|
117
|
-
? {
|
|
52
|
+
...(input.predicate === undefined
|
|
53
|
+
? {
|
|
54
|
+
probe: async () => await readWaitComplete(input.path, input.ids, input.completion, input.signal, (id) => readerFor(id).complete(input.signal)),
|
|
55
|
+
}
|
|
118
56
|
: {}),
|
|
119
|
-
observe: async () => await observeWaitRound(input.
|
|
57
|
+
observe: async () => await observeWaitRound(input.ids, readerFor, input.signal),
|
|
120
58
|
complete: (round) => roundComplete(round, input.completion, input.predicate),
|
|
121
|
-
onObserve: async (round) =>
|
|
59
|
+
onObserve: async (round) => {
|
|
60
|
+
await Promise.all(round.observations.map(({ status }) => observeStatusOutcome(status, input.outcomeObserved)));
|
|
61
|
+
},
|
|
122
62
|
});
|
|
123
63
|
return {
|
|
124
64
|
mode: input.completion,
|
|
@@ -128,9 +68,8 @@ export async function executeWaitAkuma(input) {
|
|
|
128
68
|
};
|
|
129
69
|
}
|
|
130
70
|
finally {
|
|
131
|
-
for (const reader of readers
|
|
71
|
+
for (const reader of readers.values())
|
|
132
72
|
reader.clear();
|
|
133
|
-
readers?.clear();
|
|
134
73
|
}
|
|
135
74
|
}
|
|
136
75
|
function diagnostic(error) {
|
|
@@ -67,24 +67,21 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
67
67
|
stranded: "stranded";
|
|
68
68
|
}>;
|
|
69
69
|
cwd: z.ZodOptional<z.ZodString>;
|
|
70
|
+
provider: z.ZodOptional<z.ZodString>;
|
|
71
|
+
model: z.ZodOptional<z.ZodString>;
|
|
70
72
|
allowed: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"task.hold": "task.hold";
|
|
77
|
-
"task.resume": "task.resume";
|
|
78
|
-
"task.start": "task.start";
|
|
79
|
-
"task.stop": "task.stop";
|
|
80
|
-
"task.update": "task.update";
|
|
73
|
+
"debt.add": "debt.add";
|
|
74
|
+
"debt.addDocument": "debt.addDocument";
|
|
75
|
+
"debt.done": "debt.done";
|
|
76
|
+
"debt.drop": "debt.drop";
|
|
77
|
+
"debt.update": "debt.update";
|
|
81
78
|
"akuma.call": "akuma.call";
|
|
82
79
|
"akuma.kill": "akuma.kill";
|
|
83
80
|
"akuma.tell": "akuma.tell";
|
|
84
81
|
"contract.audit": "contract.audit";
|
|
85
82
|
"contract.deliver": "contract.deliver";
|
|
86
83
|
"contract.review": "contract.review";
|
|
87
|
-
}>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("
|
|
84
|
+
}>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
|
|
88
85
|
timeline: z.ZodUnion<readonly [z.ZodObject<{
|
|
89
86
|
reportedChanges: z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
90
87
|
sequence: z.ZodNumber;
|
|
@@ -228,6 +225,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
228
225
|
unspecified: "unspecified";
|
|
229
226
|
}>;
|
|
230
227
|
path: z.ZodString;
|
|
228
|
+
patch: z.ZodOptional<z.ZodString>;
|
|
231
229
|
diffstat: z.ZodOptional<z.ZodObject<{
|
|
232
230
|
added: z.ZodNumber;
|
|
233
231
|
removed: z.ZodNumber;
|
|
@@ -242,6 +240,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
242
240
|
}, z.core.$strict>>;
|
|
243
241
|
}, z.core.$strict>]>;
|
|
244
242
|
state: z.ZodLiteral<"active">;
|
|
243
|
+
toolId: z.ZodOptional<z.ZodString>;
|
|
245
244
|
truncated: z.ZodOptional<z.ZodLiteral<true>>;
|
|
246
245
|
}, z.core.$strict>, z.ZodObject<{
|
|
247
246
|
kind: z.ZodLiteral<"tool">;
|
|
@@ -278,6 +277,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
278
277
|
unspecified: "unspecified";
|
|
279
278
|
}>;
|
|
280
279
|
path: z.ZodString;
|
|
280
|
+
patch: z.ZodOptional<z.ZodString>;
|
|
281
281
|
diffstat: z.ZodOptional<z.ZodObject<{
|
|
282
282
|
added: z.ZodNumber;
|
|
283
283
|
removed: z.ZodNumber;
|
|
@@ -297,8 +297,10 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
297
297
|
ok: "ok";
|
|
298
298
|
}>;
|
|
299
299
|
message: z.ZodOptional<z.ZodString>;
|
|
300
|
+
output: z.ZodOptional<z.ZodString>;
|
|
300
301
|
exitCode: z.ZodOptional<z.ZodNumber>;
|
|
301
302
|
}, z.core.$strict>;
|
|
303
|
+
toolId: z.ZodOptional<z.ZodString>;
|
|
302
304
|
truncated: z.ZodOptional<z.ZodLiteral<true>>;
|
|
303
305
|
}, z.core.$strict>]>;
|
|
304
306
|
}, z.core.$strict>]>>>;
|
|
@@ -331,6 +333,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
331
333
|
kind: z.ZodLiteral<"answered">;
|
|
332
334
|
historyId: z.ZodString;
|
|
333
335
|
answer: z.ZodString;
|
|
336
|
+
foldedSaidSequence: z.ZodOptional<z.ZodNumber>;
|
|
334
337
|
}, z.core.$strict>, z.ZodObject<{
|
|
335
338
|
kind: z.ZodLiteral<"failed">;
|
|
336
339
|
historyId: z.ZodString;
|
|
@@ -436,6 +439,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
436
439
|
unspecified: "unspecified";
|
|
437
440
|
}>;
|
|
438
441
|
path: z.ZodString;
|
|
442
|
+
patch: z.ZodOptional<z.ZodString>;
|
|
439
443
|
diffstat: z.ZodOptional<z.ZodObject<{
|
|
440
444
|
added: z.ZodNumber;
|
|
441
445
|
removed: z.ZodNumber;
|
|
@@ -455,8 +459,10 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
455
459
|
ok: "ok";
|
|
456
460
|
}>;
|
|
457
461
|
message: z.ZodOptional<z.ZodString>;
|
|
462
|
+
output: z.ZodOptional<z.ZodString>;
|
|
458
463
|
exitCode: z.ZodOptional<z.ZodNumber>;
|
|
459
464
|
}, z.core.$strict>;
|
|
465
|
+
toolId: z.ZodOptional<z.ZodString>;
|
|
460
466
|
truncated: z.ZodOptional<z.ZodLiteral<true>>;
|
|
461
467
|
}, z.core.$strict>]>;
|
|
462
468
|
}, z.core.$strict>]>>>;
|
package/build/src/akuma/akuma.js
CHANGED
|
@@ -15,6 +15,8 @@ export const akumaStatusSchema = z
|
|
|
15
15
|
birth: akumaBirthSchema,
|
|
16
16
|
life: z.enum(["running", "asleep", "stranded", "hung", "untidy", "killed"]),
|
|
17
17
|
cwd: z.string().optional(),
|
|
18
|
+
provider: z.string().optional(),
|
|
19
|
+
model: z.string().optional(),
|
|
18
20
|
allowed: allowedActionsSchema,
|
|
19
21
|
timeline: activitySnapshotSchema,
|
|
20
22
|
strandedReason: z.literal("resume-unsupported").optional(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.audit", "contract.deliver", "contract.review", "
|
|
2
|
+
export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.audit", "contract.deliver", "contract.review", "debt.add", "debt.addDocument", "debt.done", "debt.drop", "debt.update"];
|
|
3
3
|
export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
|
|
4
4
|
export type AllowedActions = readonly AllowedAction[];
|
|
5
5
|
export declare const DEFAULT_ALLOWED_ACTIONS: AllowedActions;
|
|
@@ -7,23 +7,18 @@ export declare function isAllowedAction(value: unknown): value is AllowedAction;
|
|
|
7
7
|
export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
|
|
8
8
|
/** Public codecs retain the Soul's canonical, duplicate-free action membership. */
|
|
9
9
|
export declare const allowedActionsSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"task.hold": "task.hold";
|
|
16
|
-
"task.resume": "task.resume";
|
|
17
|
-
"task.start": "task.start";
|
|
18
|
-
"task.stop": "task.stop";
|
|
19
|
-
"task.update": "task.update";
|
|
10
|
+
"debt.add": "debt.add";
|
|
11
|
+
"debt.addDocument": "debt.addDocument";
|
|
12
|
+
"debt.done": "debt.done";
|
|
13
|
+
"debt.drop": "debt.drop";
|
|
14
|
+
"debt.update": "debt.update";
|
|
20
15
|
"akuma.call": "akuma.call";
|
|
21
16
|
"akuma.kill": "akuma.kill";
|
|
22
17
|
"akuma.tell": "akuma.tell";
|
|
23
18
|
"contract.audit": "contract.audit";
|
|
24
19
|
"contract.deliver": "contract.deliver";
|
|
25
20
|
"contract.review": "contract.review";
|
|
26
|
-
}>>, z.ZodTransform<AllowedActions, ("
|
|
21
|
+
}>>, z.ZodTransform<AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
|
|
27
22
|
export declare function historicalAllowedActions(value: unknown): AllowedActions;
|
|
28
23
|
export declare function unionAllowedActions(base: AllowedActions, additions: AllowedActions): AllowedActions;
|
|
29
24
|
export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEBT_MUTATION_ACTIONS } from "../debt/mutation.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
const AKUMA_MUTATION_ACTIONS = Object.freeze(["akuma.call", "akuma.kill", "akuma.tell"]);
|
|
4
4
|
export const ALLOWED_ACTIONS = Object.freeze([
|
|
@@ -6,13 +6,13 @@ export const ALLOWED_ACTIONS = Object.freeze([
|
|
|
6
6
|
"contract.audit",
|
|
7
7
|
"contract.deliver",
|
|
8
8
|
"contract.review",
|
|
9
|
-
...
|
|
9
|
+
...DEBT_MUTATION_ACTIONS,
|
|
10
10
|
]);
|
|
11
11
|
export const DEFAULT_ALLOWED_ACTIONS = Object.freeze([
|
|
12
12
|
...AKUMA_MUTATION_ACTIONS,
|
|
13
13
|
"contract.audit",
|
|
14
14
|
"contract.deliver",
|
|
15
|
-
...
|
|
15
|
+
...DEBT_MUTATION_ACTIONS,
|
|
16
16
|
]);
|
|
17
17
|
const ALLOWED_ACTION_SET = new Set(ALLOWED_ACTIONS);
|
|
18
18
|
export function isAllowedAction(value) {
|
|
@@ -42,7 +42,16 @@ export const allowedActionsSchema = z.array(z.enum(ALLOWED_ACTIONS)).transform((
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
export function historicalAllowedActions(value) {
|
|
45
|
-
|
|
45
|
+
if (value === undefined)
|
|
46
|
+
return ALLOWED_ACTIONS;
|
|
47
|
+
if (!Array.isArray(value))
|
|
48
|
+
return decodeAllowedActions(value);
|
|
49
|
+
// Persisted permissions retain the same surviving capabilities after rename;
|
|
50
|
+
// removed planning/progress operations confer no replacement permission.
|
|
51
|
+
const retired = new Set(["task.compose", "task.start", "task.stop", "task.hold", "task.resume"]);
|
|
52
|
+
return decodeAllowedActions(value
|
|
53
|
+
.filter((action) => !retired.has(action))
|
|
54
|
+
.map((action) => typeof action === "string" && action.startsWith("task.") ? `debt.${action.slice("task.".length)}` : action));
|
|
46
55
|
}
|
|
47
56
|
export function unionAllowedActions(base, additions) {
|
|
48
57
|
const merged = new Set(base);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BodyControlSnapshot, type HeldAkumaLeash } from "./heart/index.js";
|
|
2
2
|
import type { AkumaPaths } from "./identity.js";
|
|
3
3
|
export declare const CONTROL_RESPONSE_MS = 1000;
|
|
4
4
|
export declare class BodySupervisor {
|
|
5
5
|
private readonly paths;
|
|
6
6
|
private readonly bodySequence;
|
|
7
7
|
private readonly leash;
|
|
8
|
+
private readonly reader;
|
|
8
9
|
readonly signal: AbortSignal;
|
|
9
10
|
private readonly controller;
|
|
10
11
|
private readonly finished;
|
|
@@ -15,12 +16,11 @@ export declare class BodySupervisor {
|
|
|
15
16
|
private constructor();
|
|
16
17
|
static open(paths: AkumaPaths, bodySequence: number, leash: HeldAkumaLeash): Promise<BodySupervisor>;
|
|
17
18
|
get reason(): "control" | "heart-gone" | undefined;
|
|
18
|
-
current():
|
|
19
|
+
current(): BodyControlSnapshot;
|
|
19
20
|
recordHung(diagnostic: string, at: string): Promise<void>;
|
|
20
|
-
refresh(): Promise<
|
|
21
|
-
next(after:
|
|
21
|
+
refresh(): Promise<BodyControlSnapshot>;
|
|
22
|
+
next(after: BodyControlSnapshot): Promise<BodyControlSnapshot>;
|
|
22
23
|
cancel(reason: "control" | "heart-gone"): void;
|
|
23
24
|
close(): Promise<void>;
|
|
24
|
-
private publish;
|
|
25
25
|
private observe;
|
|
26
26
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { abortableDelay } from "./abort.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { createBodyHeartReader } from "./heart/index.js";
|
|
3
4
|
const BODY_CONTROL_OBSERVATION_MS = 100;
|
|
4
5
|
export const CONTROL_RESPONSE_MS = 1_000;
|
|
5
6
|
export class BodySupervisor {
|
|
6
7
|
paths;
|
|
7
8
|
bodySequence;
|
|
8
9
|
leash;
|
|
10
|
+
reader;
|
|
9
11
|
signal;
|
|
10
12
|
controller = new AbortController();
|
|
11
13
|
finished = new AbortController();
|
|
@@ -13,16 +15,18 @@ export class BodySupervisor {
|
|
|
13
15
|
waiter;
|
|
14
16
|
observation;
|
|
15
17
|
stopping;
|
|
16
|
-
constructor(paths, bodySequence, leash, observation) {
|
|
18
|
+
constructor(paths, bodySequence, leash, reader, observation) {
|
|
17
19
|
this.paths = paths;
|
|
18
20
|
this.bodySequence = bodySequence;
|
|
19
21
|
this.leash = leash;
|
|
22
|
+
this.reader = reader;
|
|
20
23
|
this.signal = this.controller.signal;
|
|
21
24
|
this.observation = observation;
|
|
22
25
|
this.observer = this.observe();
|
|
23
26
|
}
|
|
24
27
|
static async open(paths, bodySequence, leash) {
|
|
25
|
-
|
|
28
|
+
const reader = createBodyHeartReader(paths);
|
|
29
|
+
return new BodySupervisor(paths, bodySequence, leash, reader, await reader.read());
|
|
26
30
|
}
|
|
27
31
|
get reason() {
|
|
28
32
|
return this.stopping;
|
|
@@ -34,7 +38,12 @@ export class BodySupervisor {
|
|
|
34
38
|
await this.leash.recordBodyHung(this.paths, { sequence: this.bodySequence, diagnostic, at });
|
|
35
39
|
}
|
|
36
40
|
async refresh() {
|
|
37
|
-
|
|
41
|
+
const snapshot = await this.reader.read();
|
|
42
|
+
if (!isDeepStrictEqual(snapshot, this.observation)) {
|
|
43
|
+
this.observation = snapshot;
|
|
44
|
+
this.waiter?.(snapshot);
|
|
45
|
+
this.waiter = undefined;
|
|
46
|
+
}
|
|
38
47
|
return this.observation;
|
|
39
48
|
}
|
|
40
49
|
next(after) {
|
|
@@ -54,18 +63,18 @@ export class BodySupervisor {
|
|
|
54
63
|
}
|
|
55
64
|
async close() {
|
|
56
65
|
this.finished.abort();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
try {
|
|
67
|
+
await this.observer;
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
this.reader.clear();
|
|
71
|
+
this.waiter = undefined;
|
|
72
|
+
}
|
|
64
73
|
}
|
|
65
74
|
async observe() {
|
|
66
75
|
for (;;) {
|
|
67
76
|
const snapshot = this.observation;
|
|
68
|
-
if (
|
|
77
|
+
if (snapshot.soul === null)
|
|
69
78
|
return this.cancel("heart-gone");
|
|
70
79
|
if (snapshot.stop?.bodySequence === this.bodySequence || snapshot.pause?.bodySequence === this.bodySequence) {
|
|
71
80
|
return this.cancel("control");
|
|
@@ -76,14 +85,7 @@ export class BodySupervisor {
|
|
|
76
85
|
catch {
|
|
77
86
|
return;
|
|
78
87
|
}
|
|
79
|
-
|
|
80
|
-
await this.refresh();
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
if (await heartExists(this.paths))
|
|
84
|
-
throw error;
|
|
85
|
-
return this.cancel("heart-gone");
|
|
86
|
-
}
|
|
88
|
+
await this.refresh();
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
}
|