@astrosheep/keiyaku 4.1.0 → 4.1.2
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 +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
- package/build/src/akuma/akuma-errors.d.ts +6 -0
- package/build/src/akuma/akuma-errors.js +9 -0
- package/build/src/akuma/akuma-handle.d.ts +39 -0
- package/build/src/akuma/akuma-handle.js +263 -0
- package/build/src/akuma/akuma-observe.d.ts +19 -0
- package/build/src/akuma/akuma-observe.js +74 -0
- package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
- package/build/src/akuma/akuma-product-symbols.js +1 -0
- package/build/src/akuma/akuma-product.d.ts +18 -0
- package/build/src/akuma/akuma-product.js +154 -0
- package/build/src/akuma/akuma.d.ts +37 -81
- package/build/src/akuma/akuma.js +39 -447
- package/build/src/akuma/allowed.d.ts +1 -0
- package/build/src/akuma/allowed.js +6 -0
- package/build/src/akuma/archetype.js +8 -5
- package/build/src/akuma/body-supervisor.d.ts +26 -0
- package/build/src/akuma/body-supervisor.js +89 -0
- package/build/src/akuma/body.d.ts +45 -4
- package/build/src/akuma/body.js +145 -35
- package/build/src/akuma/coordinate.js +17 -7
- package/build/src/akuma/heart/facts.js +1 -1
- package/build/src/akuma/heart/index.d.ts +1 -1
- package/build/src/akuma/heart/index.js +2 -4
- package/build/src/akuma/heart/request-authority.js +4 -4
- package/build/src/akuma/heart/request-rows.js +27 -16
- package/build/src/akuma/heart/rows.js +79 -41
- package/build/src/akuma/heart/soul.js +1 -1
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +76 -11
- package/build/src/akuma/heart/tells.js +35 -18
- package/build/src/akuma/heart/timeline.js +19 -11
- package/build/src/akuma/identity.d.ts +1 -0
- package/build/src/akuma/identity.js +1 -0
- package/build/src/akuma/index.d.ts +3 -3
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/nuke.js +106 -14
- package/build/src/akuma/projection-read.d.ts +17 -0
- package/build/src/akuma/projection-read.js +135 -0
- package/build/src/akuma/projection.d.ts +1 -16
- package/build/src/akuma/projection.js +77 -154
- package/build/src/akuma/provider-recipe.js +11 -5
- package/build/src/akuma/provider.js +105 -62
- package/build/src/akuma/providers/acp/config.js +5 -6
- package/build/src/akuma/providers/acp/core.js +17 -10
- package/build/src/akuma/providers/acp/events.js +38 -29
- package/build/src/akuma/providers/acp/index.js +13 -9
- package/build/src/akuma/providers/claude/events.js +8 -12
- package/build/src/akuma/providers/claude/index.d.ts +1 -0
- package/build/src/akuma/providers/claude/index.js +55 -26
- package/build/src/akuma/providers/claude/input.js +6 -2
- package/build/src/akuma/providers/codex-app-server/events.js +61 -36
- package/build/src/akuma/providers/codex-app-server/index.js +25 -13
- package/build/src/akuma/providers/grok-build/index.js +22 -25
- package/build/src/akuma/providers/index.js +2 -18
- package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
- package/build/src/akuma/providers/opencode-sdk/index.js +26 -17
- package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
- package/build/src/akuma/providers/pi/events.js +26 -19
- package/build/src/akuma/providers/pi/index.js +58 -40
- package/build/src/akuma/publication.js +9 -9
- package/build/src/akuma/request-execution.d.ts +6 -0
- package/build/src/akuma/request-execution.js +86 -0
- package/build/src/akuma/request-serve.d.ts +12 -1
- package/build/src/akuma/request-serve.js +19 -122
- package/build/src/akuma/request-wire.d.ts +2 -2
- package/build/src/akuma/request-wire.js +75 -102
- package/build/src/akuma/requests.js +2 -2
- package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
- package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
- package/build/src/akuma-body.js +6 -5
- package/build/src/body/amend.d.ts +4 -1
- package/build/src/body/amend.js +14 -6
- package/build/src/body/decode.js +2 -4
- package/build/src/body/envelope.js +2 -4
- package/build/src/body/region.d.ts +1 -2
- package/build/src/body/region.js +2 -70
- package/build/src/body/render.js +17 -12
- package/build/src/body/shape.js +1 -7
- package/build/src/cli/accepted.d.ts +1 -1
- package/build/src/cli/accepted.js +8 -1
- package/build/src/cli/commands/akuma-invoke.js +18 -6
- package/build/src/cli/commands/akuma.d.ts +0 -6
- package/build/src/cli/commands/akuma.js +11 -24
- package/build/src/cli/commands/amend.d.ts +2 -2
- package/build/src/cli/commands/amend.js +2 -4
- package/build/src/cli/commands/bind.d.ts +4 -4
- package/build/src/cli/commands/bind.js +16 -6
- package/build/src/cli/commands/contract-invoke.d.ts +25 -0
- package/build/src/cli/commands/contract-invoke.js +193 -0
- package/build/src/cli/commands/contract.d.ts +109 -10
- package/build/src/cli/commands/contract.js +339 -16
- package/build/src/cli/commands/install.d.ts +1 -1
- package/build/src/cli/commands/install.js +23 -15
- package/build/src/cli/commands/task-invoke.js +187 -67
- package/build/src/cli/commands/task-query.js +48 -24
- package/build/src/cli/commands/task.js +150 -38
- package/build/src/cli/coordinates.js +2 -4
- package/build/src/cli/invoke.js +92 -208
- package/build/src/cli/main.js +4 -2
- package/build/src/cli/parse.d.ts +3 -90
- package/build/src/cli/parse.js +27 -233
- package/build/src/cli/render/akuma-activity.d.ts +38 -0
- package/build/src/cli/render/akuma-activity.js +362 -0
- package/build/src/cli/render/akuma-tool-command.js +6 -7
- package/build/src/cli/render/akuma-tool.js +10 -8
- package/build/src/cli/render/akuma.d.ts +1 -1
- package/build/src/cli/render/akuma.js +61 -353
- package/build/src/cli/render/audit.js +5 -15
- package/build/src/cli/render/board.d.ts +1 -0
- package/build/src/cli/render/board.js +17 -0
- package/build/src/cli/render/catalog.js +81 -8
- package/build/src/cli/render/contract-history.d.ts +2 -0
- package/build/src/cli/render/contract-history.js +83 -0
- package/build/src/cli/render/contract-observation.d.ts +10 -0
- package/build/src/cli/render/contract-observation.js +58 -0
- package/build/src/cli/render/contract.d.ts +1 -2
- package/build/src/cli/render/contract.js +41 -142
- package/build/src/cli/render/kanshi.js +239 -215
- package/build/src/cli/render/receipt.js +2 -9
- package/build/src/cli/render/refusal.js +10 -15
- package/build/src/cli/render/region.js +22 -6
- package/build/src/cli/render/settings.js +4 -4
- package/build/src/cli/render/task.js +34 -14
- package/build/src/cli/render/terminal.js +16 -5
- package/build/src/cli/result.d.ts +3 -3
- package/build/src/cli/runtime.d.ts +6 -1
- package/build/src/cli/runtime.js +84 -63
- package/build/src/cli/selectors.js +2 -4
- package/build/src/cli/usage.js +6 -5
- package/build/src/contract-worktree.d.ts +1 -23
- package/build/src/contract-worktree.js +79 -155
- package/build/src/coordination/durable-file.js +7 -3
- package/build/src/coordination/sqlite-transaction-lock.js +29 -12
- package/build/src/core/facts/codec.js +7 -3
- package/build/src/core/facts/types.d.ts +1 -1
- package/build/src/core/subject.js +16 -9
- package/build/src/core/verbs/abandon.d.ts +1 -1
- package/build/src/core/verbs/abandon.js +1 -1
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +7 -7
- package/build/src/core/verbs/attestation.js +4 -1
- package/build/src/core/verbs/bind.d.ts +1 -1
- package/build/src/core/verbs/bind.js +1 -1
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +10 -8
- package/build/src/dispatch/index.js +8 -6
- package/build/src/duration.js +5 -1
- package/build/src/git/admission.js +5 -3
- package/build/src/git/hooks.d.ts +8 -0
- package/build/src/git/hooks.js +63 -14
- package/build/src/git/identity.js +1 -4
- package/build/src/git/integration.d.ts +2 -3
- package/build/src/git/integration.js +32 -39
- package/build/src/git/nuke.d.ts +1 -1
- package/build/src/git/nuke.js +110 -57
- package/build/src/git/observe.d.ts +13 -1
- package/build/src/git/observe.js +51 -44
- package/build/src/git/process.js +4 -2
- package/build/src/git/read-observation.js +10 -8
- package/build/src/git/reconcile.d.ts +22 -2
- package/build/src/git/reconcile.js +38 -274
- package/build/src/git/repository.d.ts +2 -0
- package/build/src/git/repository.js +13 -22
- package/build/src/git/scratch.d.ts +8 -5
- package/build/src/git/scratch.js +29 -2
- package/build/src/git/target-placement.d.ts +9 -10
- package/build/src/git/target-placement.js +59 -62
- package/build/src/git/tender.js +13 -46
- package/build/src/git/terminal-reconcile.d.ts +21 -0
- package/build/src/git/terminal-reconcile.js +278 -0
- package/build/src/git/terminal-seal.js +15 -12
- package/build/src/git/tree.js +5 -2
- package/build/src/git/workspace.d.ts +24 -2
- package/build/src/git/workspace.js +120 -16
- package/build/src/identity/coordinates.js +3 -1
- package/build/src/identity/selector.js +6 -3
- package/build/src/kanshi/fleet.d.ts +6 -0
- package/build/src/kanshi/fleet.js +27 -0
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +60 -48
- package/build/src/kanshi/report.d.ts +11 -23
- package/build/src/kanshi/select.js +25 -24
- package/build/src/library/address.d.ts +2 -1
- package/build/src/library/address.js +7 -8
- package/build/src/library/akuma-creation.js +20 -12
- package/build/src/library/audit.d.ts +0 -1
- package/build/src/library/audit.js +1 -2
- package/build/src/library/bind.d.ts +53 -5
- package/build/src/library/bind.js +98 -22
- package/build/src/library/catalog.d.ts +1 -0
- package/build/src/library/catalog.js +6 -3
- package/build/src/library/configuration.js +3 -1
- package/build/src/library/continuation.d.ts +15 -4
- package/build/src/library/continuation.js +24 -16
- package/build/src/library/contract-bind.d.ts +26 -0
- package/build/src/library/contract-bind.js +106 -0
- package/build/src/library/contract-operations.d.ts +77 -0
- package/build/src/library/contract-operations.js +130 -0
- package/build/src/library/contract-types.d.ts +95 -0
- package/build/src/library/contract-types.js +1 -0
- package/build/src/library/contract.d.ts +19 -154
- package/build/src/library/contract.js +46 -261
- package/build/src/library/fleet.d.ts +1 -1
- package/build/src/library/fleet.js +19 -11
- package/build/src/library/input.d.ts +2 -0
- package/build/src/library/input.js +14 -0
- package/build/src/library/keiyaku.d.ts +8 -7
- package/build/src/library/keiyaku.js +3 -3
- package/build/src/library/nuke.js +14 -5
- package/build/src/library/reconcile.d.ts +9 -4
- package/build/src/library/reconcile.js +30 -24
- package/build/src/library/refusal.d.ts +13 -5
- package/build/src/library/refusal.js +7 -0
- package/build/src/library/region.d.ts +8 -7
- package/build/src/library/region.js +31 -11
- package/build/src/library/repo.js +2 -2
- package/build/src/markdown/lex.js +9 -2
- package/build/src/markdown/parse.js +11 -7
- package/build/src/markdown/query.js +4 -2
- package/build/src/protocol/amend.js +3 -1
- package/build/src/protocol/attempt.d.ts +7 -3
- package/build/src/protocol/attempt.js +9 -5
- package/build/src/protocol/audit.d.ts +1 -1
- package/build/src/protocol/audit.js +4 -14
- package/build/src/protocol/bind.d.ts +19 -6
- package/build/src/protocol/bind.js +28 -13
- package/build/src/protocol/completion.d.ts +0 -1
- package/build/src/protocol/completion.js +1 -3
- package/build/src/protocol/deliver.d.ts +1 -3
- package/build/src/protocol/deliver.js +15 -39
- package/build/src/protocol/intent.d.ts +1 -0
- package/build/src/protocol/intent.js +9 -7
- package/build/src/protocol/operations.d.ts +3 -7
- package/build/src/protocol/operations.js +5 -3
- package/build/src/protocol/placement.d.ts +0 -1
- package/build/src/protocol/placement.js +6 -6
- package/build/src/protocol/read/observation.d.ts +11 -0
- package/build/src/protocol/read/observation.js +37 -0
- package/build/src/protocol/read/status.d.ts +23 -12
- package/build/src/protocol/read/status.js +90 -72
- package/build/src/protocol/reconcile.d.ts +1 -0
- package/build/src/protocol/reconcile.js +2 -3
- package/build/src/protocol/reintegrate.js +1 -1
- package/build/src/protocol/review.d.ts +0 -1
- package/build/src/protocol/review.js +15 -22
- package/build/src/protocol/run.d.ts +1 -0
- package/build/src/protocol/run.js +4 -5
- package/build/src/runtime/proc/launch.d.ts +33 -0
- package/build/src/runtime/proc/launch.js +98 -0
- package/build/src/runtime/proc/line-rpc.js +9 -3
- package/build/src/runtime/proc/run.d.ts +12 -6
- package/build/src/runtime/proc/run.js +78 -21
- package/build/src/runtime/proc/stdio.js +7 -10
- package/build/src/settings.js +6 -2
- package/build/src/settlement/holder.js +13 -10
- package/build/src/settlement/settle.js +39 -6
- package/build/src/task/board.js +12 -6
- package/build/src/task/compose.js +85 -22
- package/build/src/task/context.js +21 -8
- package/build/src/task/document.js +46 -10
- package/build/src/task/identity.js +5 -2
- package/build/src/task/index.d.ts +0 -1
- package/build/src/task/index.js +119 -34
- package/build/src/task/input.js +29 -7
- package/build/src/task/mutation.js +61 -19
- package/build/src/task/operations.d.ts +6 -1
- package/build/src/task/operations.js +83 -29
- package/build/src/task/query.js +55 -26
- package/build/src/task/store.d.ts +3 -1
- package/build/src/task/store.js +81 -17
- package/build/src/verification/execution.js +14 -11
- package/build/src/workspace-place.js +19 -7
- package/build/src/world.js +16 -6
- package/package.json +8 -3
package/build/src/akuma/nuke.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { lstat, readdir, rm } from "node:fs/promises";
|
|
1
|
+
import { lstat, readdir, rm, rmdir } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
2
3
|
import { nukeAliases } from "../alias/index.js";
|
|
3
4
|
import { CONTROL_RESPONSE_MS } from "./body.js";
|
|
4
|
-
import { HeldAkumaLeash, readHeart, readKill, requestStop
|
|
5
|
-
import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot
|
|
6
|
-
const POLL_MS =
|
|
5
|
+
import { HeldAkumaLeash, readHeart, readKill, requestStop } from "./heart/index.js";
|
|
6
|
+
import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot } from "./identity.js";
|
|
7
|
+
const POLL_MS = 100;
|
|
7
8
|
async function hasAkumaCustody(paths) {
|
|
8
9
|
try {
|
|
9
10
|
const value = await lstat(paths.heart);
|
|
@@ -25,7 +26,7 @@ async function takeLeashUntil(paths, deadline) {
|
|
|
25
26
|
return leash;
|
|
26
27
|
if (performance.now() >= deadline)
|
|
27
28
|
return null;
|
|
28
|
-
await wait(POLL_MS);
|
|
29
|
+
await wait(Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
async function stopRunningAkuma(entry) {
|
|
@@ -36,11 +37,11 @@ async function stopRunningAkuma(entry) {
|
|
|
36
37
|
try {
|
|
37
38
|
if (request.kind === "already-killed" || request.kind === "already-stopped")
|
|
38
39
|
return leash;
|
|
39
|
-
if (await readKill(entry.paths, request.body.sequence) !== null)
|
|
40
|
+
if ((await readKill(entry.paths, request.body.sequence)) !== null)
|
|
40
41
|
return leash;
|
|
41
42
|
const current = await readHeart(entry.paths);
|
|
42
43
|
if (current.latestBody?.sequence !== request.body.sequence) {
|
|
43
|
-
if (await readKill(entry.paths, request.body.sequence) !== null)
|
|
44
|
+
if ((await readKill(entry.paths, request.body.sequence)) !== null)
|
|
44
45
|
return leash;
|
|
45
46
|
throw new Error(`Akuma ${entry.id} could not be stopped: unavailable`);
|
|
46
47
|
}
|
|
@@ -48,7 +49,7 @@ async function stopRunningAkuma(entry) {
|
|
|
48
49
|
await leash.clearStop(entry.paths);
|
|
49
50
|
throw new Error(`Akuma ${entry.id} could not be stopped: untidy`);
|
|
50
51
|
}
|
|
51
|
-
if (await leash.settleStop(entry.paths, request.body.sequence) === null) {
|
|
52
|
+
if ((await leash.settleStop(entry.paths, request.body.sequence)) === null) {
|
|
52
53
|
throw new Error(`Akuma ${entry.id} could not be stopped: unavailable`);
|
|
53
54
|
}
|
|
54
55
|
return leash;
|
|
@@ -58,22 +59,112 @@ async function stopRunningAkuma(entry) {
|
|
|
58
59
|
throw error;
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
async function
|
|
62
|
+
async function removeKnownRegularFile(path) {
|
|
62
63
|
try {
|
|
63
64
|
const value = await lstat(path);
|
|
64
|
-
if (value.isFile()
|
|
65
|
-
|
|
65
|
+
if (!value.isFile() || value.isSymbolicLink())
|
|
66
|
+
throw new Error(`Akuma custody is not a regular file: ${path}`);
|
|
67
|
+
await rm(path, { force: true });
|
|
66
68
|
}
|
|
67
69
|
catch (error) {
|
|
68
70
|
if (error.code !== "ENOENT")
|
|
69
71
|
throw error;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
74
|
+
async function removeEmptyDirectory(path) {
|
|
75
|
+
try {
|
|
76
|
+
await rmdir(path);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error.code !== "ENOENT" && error.code !== "ENOTEMPTY")
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function isRequestSequenceName(name) {
|
|
84
|
+
const sequence = Number(name);
|
|
85
|
+
return /^\d+$/u.test(name) && Number.isSafeInteger(sequence) && String(sequence) === name;
|
|
86
|
+
}
|
|
87
|
+
const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
88
|
+
function isRequestArtifactName(name) {
|
|
89
|
+
const match = /^(.*)\.(?:request|receipt)\.json$/u.exec(name);
|
|
90
|
+
return match !== null && UUID_V4.test(match[1]);
|
|
91
|
+
}
|
|
92
|
+
async function removeKnownRequestFile(path) {
|
|
93
|
+
try {
|
|
94
|
+
const value = await lstat(path);
|
|
95
|
+
if (!value.isFile() || value.isSymbolicLink())
|
|
96
|
+
return;
|
|
97
|
+
await rm(path, { force: true });
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error.code !== "ENOENT")
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function removeKnownRequestChannel(path) {
|
|
105
|
+
try {
|
|
106
|
+
const value = await lstat(path);
|
|
107
|
+
if (!value.isDirectory() || value.isSymbolicLink())
|
|
108
|
+
throw new Error(`Akuma custody is not a directory: ${path}`);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error.code === "ENOENT")
|
|
112
|
+
return;
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
for (const name of await readdir(path)) {
|
|
116
|
+
if (!isRequestSequenceName(name))
|
|
117
|
+
continue;
|
|
118
|
+
const sequenceDir = join(path, name);
|
|
119
|
+
try {
|
|
120
|
+
const sequence = await lstat(sequenceDir);
|
|
121
|
+
if (!sequence.isDirectory() || sequence.isSymbolicLink())
|
|
122
|
+
continue;
|
|
123
|
+
for (const child of await readdir(sequenceDir)) {
|
|
124
|
+
if (isRequestArtifactName(child))
|
|
125
|
+
await removeKnownRequestFile(join(sequenceDir, child));
|
|
126
|
+
}
|
|
127
|
+
await removeEmptyDirectory(sequenceDir);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (error.code !== "ENOENT")
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
await removeEmptyDirectory(path);
|
|
135
|
+
}
|
|
72
136
|
async function removeAkumaEntry(entry) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
137
|
+
await removeKnownRequestChannel(entry.paths.requests);
|
|
138
|
+
for (const path of [
|
|
139
|
+
entry.paths.log,
|
|
140
|
+
`${entry.paths.heart}-wal`,
|
|
141
|
+
`${entry.paths.heart}-shm`,
|
|
142
|
+
`${entry.paths.leash}-wal`,
|
|
143
|
+
`${entry.paths.leash}-shm`,
|
|
144
|
+
entry.paths.leash,
|
|
145
|
+
entry.paths.heart,
|
|
146
|
+
]) {
|
|
147
|
+
await removeKnownRegularFile(path);
|
|
148
|
+
}
|
|
149
|
+
await removeEmptyDirectory(entry.paths.directory);
|
|
150
|
+
}
|
|
151
|
+
async function removeEmptyAkumaRunRoot(world) {
|
|
152
|
+
const runRoot = akumaRunRoot(world);
|
|
153
|
+
let names;
|
|
154
|
+
try {
|
|
155
|
+
names = await readdir(runRoot);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error.code === "ENOENT")
|
|
159
|
+
return;
|
|
160
|
+
throw error;
|
|
76
161
|
}
|
|
162
|
+
if (names.some((name) => name !== ".gitignore"))
|
|
163
|
+
return;
|
|
164
|
+
if (names.includes(".gitignore"))
|
|
165
|
+
await removeKnownRegularFile(join(runRoot, ".gitignore"));
|
|
166
|
+
await removeEmptyDirectory(runRoot);
|
|
167
|
+
await removeEmptyDirectory(dirname(runRoot));
|
|
77
168
|
}
|
|
78
169
|
async function nukeAkumaEntries(world) {
|
|
79
170
|
const runRoot = akumaRunRoot(world);
|
|
@@ -136,6 +227,7 @@ export async function stopAkuma(world) {
|
|
|
136
227
|
await nukeAliases(world);
|
|
137
228
|
for (const entry of entries)
|
|
138
229
|
await removeAkumaEntry(entry);
|
|
230
|
+
await removeEmptyAkumaRunRoot(world);
|
|
139
231
|
}
|
|
140
232
|
finally {
|
|
141
233
|
for (const leash of held.reverse())
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ActivitySnapshot, TurnLedger } from "./projection.js";
|
|
2
|
+
import type { HistoryCursor, HistoryPage } from "./projection.js";
|
|
3
|
+
export declare function ordinarySnapshotBudget(ordinaryBudget?: number): Readonly<{
|
|
4
|
+
tail: number;
|
|
5
|
+
voice: number;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function selectSnapshot(ledger: TurnLedger, input: Readonly<{
|
|
8
|
+
aperture: "monitoring" | "receipt";
|
|
9
|
+
budget?: Readonly<{
|
|
10
|
+
tail: number;
|
|
11
|
+
voice?: number;
|
|
12
|
+
}>;
|
|
13
|
+
}>): Readonly<{
|
|
14
|
+
snapshot: ActivitySnapshot;
|
|
15
|
+
ordinaryCount: number;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function selectHistory(ledger: TurnLedger, cursor: HistoryCursor): HistoryPage;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const DEFAULT_TAIL = 3;
|
|
2
|
+
const DEFAULT_VOICE = 3;
|
|
3
|
+
const REPORTED_CHANGE_LIMIT = 5;
|
|
4
|
+
const EMPTY_REPORTED = { reportedChanges: [], reportedChangesOmitted: 0 };
|
|
5
|
+
function assembleEntries(ledger, window, selected) {
|
|
6
|
+
const windowRows = new Set(window);
|
|
7
|
+
const result = [];
|
|
8
|
+
let hidden = 0;
|
|
9
|
+
const flushGap = () => {
|
|
10
|
+
if (hidden === 0)
|
|
11
|
+
return;
|
|
12
|
+
result.push({ kind: "gap", count: hidden });
|
|
13
|
+
hidden = 0;
|
|
14
|
+
};
|
|
15
|
+
for (const row of ledger.rows) {
|
|
16
|
+
if (windowRows.has(row) && !selected.has(row)) {
|
|
17
|
+
hidden += 1;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (!selected.has(row))
|
|
21
|
+
continue;
|
|
22
|
+
flushGap();
|
|
23
|
+
if (row.kind !== "turn" && row.kind !== "outcome" && !(row.kind === "tool" && row.state === "unsettled")) {
|
|
24
|
+
result.push({ kind: "row", row: row });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
flushGap();
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
const DEFAULT_ORDINARY = DEFAULT_TAIL + DEFAULT_VOICE;
|
|
31
|
+
export function ordinarySnapshotBudget(ordinaryBudget = DEFAULT_ORDINARY) {
|
|
32
|
+
const tail = Math.min(DEFAULT_TAIL, Math.max(0, ordinaryBudget));
|
|
33
|
+
return { tail, voice: Math.min(DEFAULT_VOICE, Math.max(0, ordinaryBudget) - tail) };
|
|
34
|
+
}
|
|
35
|
+
function isSuccessfulFileChange(row) {
|
|
36
|
+
return (row.kind === "tool" && typeof row.state === "object" && row.state.status === "ok" && row.call.kind === "fileChange");
|
|
37
|
+
}
|
|
38
|
+
function flattenReportedChanges(rows) {
|
|
39
|
+
return rows.flatMap((row) => {
|
|
40
|
+
if (!isSuccessfulFileChange(row))
|
|
41
|
+
return [];
|
|
42
|
+
return row.call.changes.map((change) => ({
|
|
43
|
+
sequence: row.sequence,
|
|
44
|
+
at: row.at,
|
|
45
|
+
op: change.op,
|
|
46
|
+
path: change.path,
|
|
47
|
+
...(change.diffstat === undefined ? {} : { diffstat: change.diffstat }),
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function summarizeReportedChanges(rows) {
|
|
52
|
+
const eligible = flattenReportedChanges(rows);
|
|
53
|
+
return {
|
|
54
|
+
reportedChanges: eligible.slice(-REPORTED_CHANGE_LIMIT),
|
|
55
|
+
reportedChangesOmitted: Math.max(0, eligible.length - REPORTED_CHANGE_LIMIT),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function frontierReportedChanges(ledger) {
|
|
59
|
+
if (ledger.openTurn !== undefined)
|
|
60
|
+
return summarizeReportedChanges(ledger.openTurn.rows);
|
|
61
|
+
const closed = ledger.turns.findLast((turn) => turn.kind === "closed");
|
|
62
|
+
return closed === undefined ? EMPTY_REPORTED : summarizeReportedChanges(closed.rows);
|
|
63
|
+
}
|
|
64
|
+
function isPendingTell(row) {
|
|
65
|
+
return row.kind === "tell" && row.state === "pending";
|
|
66
|
+
}
|
|
67
|
+
function isToldTell(row) {
|
|
68
|
+
return row.kind === "tell" && row.state === "told";
|
|
69
|
+
}
|
|
70
|
+
function isActiveTool(row) {
|
|
71
|
+
return row.kind === "tool" && row.state === "active";
|
|
72
|
+
}
|
|
73
|
+
export function selectSnapshot(ledger, input) {
|
|
74
|
+
const budget = input.budget ?? { tail: DEFAULT_TAIL, voice: DEFAULT_VOICE };
|
|
75
|
+
if (ledger.turns.length === 0 && !ledger.rows.some((row) => row.kind === "tell"))
|
|
76
|
+
return { snapshot: { kind: "unborn", entries: [], omitted: 0, ...EMPTY_REPORTED }, ordinaryCount: 0 };
|
|
77
|
+
const pending = ledger.rows.filter(isPendingTell);
|
|
78
|
+
const latestTold = input.aperture === "monitoring" ? ledger.rows.findLast(isToldTell) : undefined;
|
|
79
|
+
const pins = latestTold === undefined ? pending : [...pending, latestTold];
|
|
80
|
+
const reported = frontierReportedChanges(ledger);
|
|
81
|
+
if (ledger.openTurn !== undefined) {
|
|
82
|
+
const window = ledger.openTurn.rows;
|
|
83
|
+
const pinSet = new Set([...window.filter(isActiveTool), ...pins]);
|
|
84
|
+
const ordinary = window.filter((row) => !pinSet.has(row));
|
|
85
|
+
const tailCount = Math.max(0, budget.tail);
|
|
86
|
+
const tail = tailCount === 0 ? [] : ordinary.slice(-tailCount);
|
|
87
|
+
const tailSet = new Set(tail);
|
|
88
|
+
const voiceCount = Math.max(0, budget.voice ?? DEFAULT_VOICE);
|
|
89
|
+
const voiceCandidates = ordinary.filter((row) => !tailSet.has(row) && (row.kind === "said" || row.kind === "thought"));
|
|
90
|
+
const voice = voiceCount === 0 ? [] : voiceCandidates.slice(-voiceCount);
|
|
91
|
+
const selected = new Set([...pinSet, ...tail, ...voice]);
|
|
92
|
+
const omitted = window.filter((row) => !selected.has(row)).length;
|
|
93
|
+
return {
|
|
94
|
+
snapshot: {
|
|
95
|
+
kind: "open",
|
|
96
|
+
turn: ledger.openTurn.turn,
|
|
97
|
+
entries: assembleEntries(ledger, window, selected),
|
|
98
|
+
omitted,
|
|
99
|
+
...reported,
|
|
100
|
+
},
|
|
101
|
+
ordinaryCount: tail.length + voice.length,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
snapshot: {
|
|
106
|
+
kind: "idle",
|
|
107
|
+
...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
|
|
108
|
+
entries: assembleEntries(ledger, [], new Set(pins)),
|
|
109
|
+
omitted: 0,
|
|
110
|
+
...reported,
|
|
111
|
+
},
|
|
112
|
+
ordinaryCount: 0,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function selectHistory(ledger, cursor) {
|
|
116
|
+
const eligible = ledger.rows.filter((row) => cursor.before !== undefined
|
|
117
|
+
? row.sequence < cursor.before
|
|
118
|
+
: cursor.since !== undefined
|
|
119
|
+
? row.sequence > cursor.since
|
|
120
|
+
: true);
|
|
121
|
+
const rows = cursor.since !== undefined ? eligible.slice(0, cursor.limit) : eligible.slice(-cursor.limit);
|
|
122
|
+
const omitted = Math.max(0, eligible.length - rows.length);
|
|
123
|
+
const historyLost = cursor.since !== undefined
|
|
124
|
+
? ledger.rows[0] !== undefined && ledger.rows[0].sequence > cursor.since + 1
|
|
125
|
+
: ledger.retained.lowestRetained !== null && ledger.retained.lowestRetained > 1 && omitted === 0;
|
|
126
|
+
return {
|
|
127
|
+
rows,
|
|
128
|
+
omitted,
|
|
129
|
+
hasEarlier: cursor.since === undefined && eligible.length > rows.length,
|
|
130
|
+
hasLater: cursor.since !== undefined && eligible.length > rows.length,
|
|
131
|
+
historyLost,
|
|
132
|
+
lowestRetained: ledger.retained.lowestRetained,
|
|
133
|
+
highest: ledger.retained.highest,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -172,19 +172,4 @@ export type TurnLedger = Readonly<{
|
|
|
172
172
|
}>;
|
|
173
173
|
/** Fold the retained fact timeline into Turn-owned rows without inventing provider facts. */
|
|
174
174
|
export declare function projectTurns(facts: readonly TimelineFact[], retained?: RetainedWindow): TurnLedger;
|
|
175
|
-
|
|
176
|
-
export declare function ordinarySnapshotBudget(ordinaryBudget: number): Readonly<{
|
|
177
|
-
tail: number;
|
|
178
|
-
voice: number;
|
|
179
|
-
}>;
|
|
180
|
-
/** Count selected ordinary rows; active tools and pending tells stay outside this total. */
|
|
181
|
-
export declare function ordinarySelectedCount(snapshot: ActivitySnapshot): number;
|
|
182
|
-
/** Select one current Turn, one latest outcome, or no focus; pending tells stay actionable. */
|
|
183
|
-
export declare function selectSnapshot(ledger: TurnLedger, budget?: Readonly<{
|
|
184
|
-
tail: number;
|
|
185
|
-
voice?: number;
|
|
186
|
-
}>): ActivitySnapshot;
|
|
187
|
-
/** Apply the one snapshot policy used by every public observation consumer. */
|
|
188
|
-
export declare function selectActivitySnapshot(facts: readonly TimelineFact[]): ActivitySnapshot;
|
|
189
|
-
export declare function selectHistory(ledger: TurnLedger, cursor: HistoryCursor): HistoryPage;
|
|
190
|
-
export {};
|
|
175
|
+
export { ordinarySnapshotBudget, selectHistory, selectSnapshot } from "./projection-read.js";
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { decodeAgentEvent } from "./provider.js";
|
|
2
|
-
const DEFAULT_TAIL = 3;
|
|
3
|
-
const DEFAULT_VOICE = 3;
|
|
4
|
-
const REPORTED_CHANGE_LIMIT = 5;
|
|
5
|
-
const EMPTY_REPORTED = { reportedChanges: [], reportedChangesOmitted: 0 };
|
|
6
2
|
function duration(startedAt, completedAt) {
|
|
7
3
|
const start = Date.parse(startedAt);
|
|
8
4
|
const end = Date.parse(completedAt);
|
|
@@ -15,12 +11,24 @@ function toolKey(turnSequence, id) {
|
|
|
15
11
|
}
|
|
16
12
|
function narrationRow(fact, event) {
|
|
17
13
|
if (event.type === "assistant") {
|
|
18
|
-
return {
|
|
19
|
-
|
|
14
|
+
return {
|
|
15
|
+
kind: "said",
|
|
16
|
+
sequence: fact.sequence,
|
|
17
|
+
turnSequence: fact.turnSequence,
|
|
18
|
+
at: fact.at,
|
|
19
|
+
text: event.text,
|
|
20
|
+
...(event.truncated === true ? { truncated: true } : {}),
|
|
21
|
+
};
|
|
20
22
|
}
|
|
21
23
|
if (event.type === "thought" || event.type === "note") {
|
|
22
|
-
return {
|
|
23
|
-
|
|
24
|
+
return {
|
|
25
|
+
kind: event.type,
|
|
26
|
+
sequence: fact.sequence,
|
|
27
|
+
turnSequence: fact.turnSequence,
|
|
28
|
+
at: fact.at,
|
|
29
|
+
text: event.text,
|
|
30
|
+
...(event.truncated === true ? { truncated: true } : {}),
|
|
31
|
+
};
|
|
24
32
|
}
|
|
25
33
|
return null;
|
|
26
34
|
}
|
|
@@ -43,7 +51,16 @@ function projectToolEvent(rows, running, fact, event, phase) {
|
|
|
43
51
|
const index = running.get(key);
|
|
44
52
|
const started = index === undefined ? undefined : rows[index];
|
|
45
53
|
if (started?.kind !== "tool") {
|
|
46
|
-
rows.push({
|
|
54
|
+
rows.push({
|
|
55
|
+
kind: "tool",
|
|
56
|
+
sequence: fact.sequence,
|
|
57
|
+
turnSequence: fact.turnSequence,
|
|
58
|
+
at: fact.at,
|
|
59
|
+
name: event.name,
|
|
60
|
+
call: event.call,
|
|
61
|
+
state: event.result,
|
|
62
|
+
...(event.truncated === true ? { truncated: true } : {}),
|
|
63
|
+
});
|
|
47
64
|
return;
|
|
48
65
|
}
|
|
49
66
|
const durationMs = duration(started.at, fact.at);
|
|
@@ -103,6 +120,25 @@ function finishTurn(turn) {
|
|
|
103
120
|
}
|
|
104
121
|
return { kind: "open", turn: turn.turn, rows: rows };
|
|
105
122
|
}
|
|
123
|
+
function finishLedger(state, retained) {
|
|
124
|
+
const turns = state.turns.map(finishTurn);
|
|
125
|
+
const rows = [
|
|
126
|
+
...turns.flatMap((turn) => [turn.turn, ...turn.rows, ...(turn.kind === "closed" ? [turn.outcome] : [])]),
|
|
127
|
+
...state.tells,
|
|
128
|
+
...state.orphanRows,
|
|
129
|
+
...state.orphanOutcomes,
|
|
130
|
+
].sort((left, right) => left.sequence - right.sequence);
|
|
131
|
+
const frontier = turns.at(-1);
|
|
132
|
+
const openTurn = frontier?.kind === "open" ? frontier : undefined;
|
|
133
|
+
const latestOutcome = rows.findLast((row) => row.kind === "outcome");
|
|
134
|
+
return {
|
|
135
|
+
rows,
|
|
136
|
+
turns,
|
|
137
|
+
retained,
|
|
138
|
+
...(openTurn === undefined ? {} : { openTurn }),
|
|
139
|
+
...(latestOutcome === undefined ? {} : { latestOutcome }),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
106
142
|
/** Fold the retained fact timeline into Turn-owned rows without inventing provider facts. */
|
|
107
143
|
export function projectTurns(facts, retained = {
|
|
108
144
|
lowestRetained: facts[0]?.sequence ?? null,
|
|
@@ -118,13 +154,25 @@ export function projectTurns(facts, retained = {
|
|
|
118
154
|
};
|
|
119
155
|
for (const fact of facts) {
|
|
120
156
|
if (fact.kind === "turn-start") {
|
|
121
|
-
const turn = {
|
|
157
|
+
const turn = {
|
|
158
|
+
kind: "turn",
|
|
159
|
+
sequence: fact.sequence,
|
|
160
|
+
turnSequence: fact.sequence,
|
|
161
|
+
bodySequence: fact.bodySequence,
|
|
162
|
+
at: fact.startedAt,
|
|
163
|
+
};
|
|
122
164
|
const projected = { phase: "open", turn, rows: [], running: new Map() };
|
|
123
165
|
state.turns.push(projected);
|
|
124
166
|
state.turnsBySequence.set(turn.turnSequence, projected);
|
|
125
167
|
}
|
|
126
168
|
else if (fact.kind === "turn-end") {
|
|
127
|
-
const outcome = {
|
|
169
|
+
const outcome = {
|
|
170
|
+
kind: "outcome",
|
|
171
|
+
sequence: fact.sequence,
|
|
172
|
+
turnSequence: fact.turnSequence,
|
|
173
|
+
at: fact.completedAt,
|
|
174
|
+
outcome: fact.outcome,
|
|
175
|
+
};
|
|
128
176
|
const turn = state.turnsBySequence.get(fact.turnSequence);
|
|
129
177
|
if (turn === undefined)
|
|
130
178
|
state.orphanOutcomes.push(outcome);
|
|
@@ -135,7 +183,13 @@ export function projectTurns(facts, retained = {
|
|
|
135
183
|
}
|
|
136
184
|
}
|
|
137
185
|
else if (fact.kind === "call") {
|
|
138
|
-
const row = {
|
|
186
|
+
const row = {
|
|
187
|
+
kind: "call",
|
|
188
|
+
sequence: fact.sequence,
|
|
189
|
+
turnSequence: fact.turnSequence,
|
|
190
|
+
at: fact.at,
|
|
191
|
+
text: fact.body,
|
|
192
|
+
};
|
|
139
193
|
const turn = state.turnsBySequence.get(fact.turnSequence);
|
|
140
194
|
if (turn === undefined)
|
|
141
195
|
state.orphanRows.push(row);
|
|
@@ -143,151 +197,20 @@ export function projectTurns(facts, retained = {
|
|
|
143
197
|
turn.rows.push(row);
|
|
144
198
|
}
|
|
145
199
|
else if (fact.kind === "tell") {
|
|
146
|
-
state.tells.push({
|
|
200
|
+
state.tells.push({
|
|
201
|
+
kind: "tell",
|
|
202
|
+
sequence: fact.sequence,
|
|
203
|
+
at: fact.recordedAt,
|
|
204
|
+
tellId: fact.id,
|
|
205
|
+
text: fact.body,
|
|
206
|
+
state: fact.state,
|
|
207
|
+
deliveries: fact.deliveries,
|
|
208
|
+
});
|
|
147
209
|
}
|
|
148
210
|
else {
|
|
149
211
|
projectActivityEvent(state, fact);
|
|
150
212
|
}
|
|
151
213
|
}
|
|
152
|
-
|
|
153
|
-
const rows = [
|
|
154
|
-
...turns.flatMap((turn) => [turn.turn, ...turn.rows, ...(turn.kind === "closed" ? [turn.outcome] : [])]),
|
|
155
|
-
...state.tells,
|
|
156
|
-
...state.orphanRows,
|
|
157
|
-
...state.orphanOutcomes,
|
|
158
|
-
].sort((left, right) => left.sequence - right.sequence);
|
|
159
|
-
const frontier = turns.at(-1);
|
|
160
|
-
const openTurn = frontier?.kind === "open" ? frontier : undefined;
|
|
161
|
-
const latestOutcome = rows.findLast((row) => row.kind === "outcome");
|
|
162
|
-
return { rows, turns, retained, ...(openTurn === undefined ? {} : { openTurn }), ...(latestOutcome === undefined ? {} : { latestOutcome }) };
|
|
163
|
-
}
|
|
164
|
-
function entries(rows) {
|
|
165
|
-
return rows.map((row) => ({ kind: "row", row }));
|
|
166
|
-
}
|
|
167
|
-
function openEntries(ledger, window, selected) {
|
|
168
|
-
const windowRows = new Set(window);
|
|
169
|
-
const result = [];
|
|
170
|
-
let hidden = 0;
|
|
171
|
-
const flushGap = () => {
|
|
172
|
-
if (hidden === 0)
|
|
173
|
-
return;
|
|
174
|
-
result.push({ kind: "gap", count: hidden });
|
|
175
|
-
hidden = 0;
|
|
176
|
-
};
|
|
177
|
-
for (const row of ledger.rows) {
|
|
178
|
-
if (windowRows.has(row) && !selected.has(row)) {
|
|
179
|
-
hidden += 1;
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (!selected.has(row))
|
|
183
|
-
continue;
|
|
184
|
-
flushGap();
|
|
185
|
-
if (row.kind !== "turn" && row.kind !== "outcome" && !(row.kind === "tool" && row.state === "unsettled")) {
|
|
186
|
-
result.push({ kind: "row", row });
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
flushGap();
|
|
190
|
-
return result;
|
|
191
|
-
}
|
|
192
|
-
/** Split one nonnegative ordinary allowance into newest tail first, then pre-tail voice. */
|
|
193
|
-
export function ordinarySnapshotBudget(ordinaryBudget) {
|
|
194
|
-
const tail = Math.min(DEFAULT_TAIL, Math.max(0, ordinaryBudget));
|
|
195
|
-
return { tail, voice: Math.min(DEFAULT_VOICE, Math.max(0, ordinaryBudget) - tail) };
|
|
196
|
-
}
|
|
197
|
-
/** Count selected ordinary rows; active tools and pending tells stay outside this total. */
|
|
198
|
-
export function ordinarySelectedCount(snapshot) {
|
|
199
|
-
return snapshot.entries.reduce((count, entry) => {
|
|
200
|
-
if (entry.kind !== "row")
|
|
201
|
-
return count;
|
|
202
|
-
if (entry.row.kind === "tool" && entry.row.state === "active")
|
|
203
|
-
return count;
|
|
204
|
-
if (entry.row.kind === "tell" && entry.row.state === "pending")
|
|
205
|
-
return count;
|
|
206
|
-
return count + 1;
|
|
207
|
-
}, 0);
|
|
208
|
-
}
|
|
209
|
-
function isSuccessfulFileChange(row) {
|
|
210
|
-
return row.kind === "tool"
|
|
211
|
-
&& typeof row.state === "object"
|
|
212
|
-
&& row.state.status === "ok"
|
|
213
|
-
&& row.call.kind === "fileChange";
|
|
214
|
-
}
|
|
215
|
-
function flattenReportedChanges(rows) {
|
|
216
|
-
return rows.flatMap((row) => {
|
|
217
|
-
if (!isSuccessfulFileChange(row))
|
|
218
|
-
return [];
|
|
219
|
-
return row.call.changes.map((change) => ({
|
|
220
|
-
sequence: row.sequence,
|
|
221
|
-
at: row.at,
|
|
222
|
-
op: change.op,
|
|
223
|
-
path: change.path,
|
|
224
|
-
...(change.diffstat === undefined ? {} : { diffstat: change.diffstat }),
|
|
225
|
-
}));
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
function summarizeReportedChanges(rows) {
|
|
229
|
-
const eligible = flattenReportedChanges(rows);
|
|
230
|
-
return {
|
|
231
|
-
reportedChanges: eligible.slice(-REPORTED_CHANGE_LIMIT),
|
|
232
|
-
reportedChangesOmitted: Math.max(0, eligible.length - REPORTED_CHANGE_LIMIT),
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function frontierReportedChanges(ledger) {
|
|
236
|
-
if (ledger.openTurn !== undefined)
|
|
237
|
-
return summarizeReportedChanges(ledger.openTurn.rows);
|
|
238
|
-
const closed = ledger.turns.findLast((turn) => turn.kind === "closed");
|
|
239
|
-
return closed === undefined ? EMPTY_REPORTED : summarizeReportedChanges(closed.rows);
|
|
240
|
-
}
|
|
241
|
-
/** Select one current Turn, one latest outcome, or no focus; pending tells stay actionable. */
|
|
242
|
-
export function selectSnapshot(ledger, budget = { tail: DEFAULT_TAIL, voice: DEFAULT_VOICE }) {
|
|
243
|
-
if (ledger.turns.length === 0 && !ledger.rows.some((row) => row.kind === "tell")) {
|
|
244
|
-
return { kind: "unborn", entries: [], omitted: 0, ...EMPTY_REPORTED };
|
|
245
|
-
}
|
|
246
|
-
const pending = ledger.rows.filter((row) => row.kind === "tell" && row.state === "pending");
|
|
247
|
-
const reported = frontierReportedChanges(ledger);
|
|
248
|
-
if (ledger.openTurn !== undefined) {
|
|
249
|
-
const window = ledger.openTurn.rows;
|
|
250
|
-
const active = window.filter((row) => row.kind === "tool" && row.state === "active");
|
|
251
|
-
const ordinary = window.filter((row) => !(row.kind === "tool" && row.state === "active"));
|
|
252
|
-
const tailCount = Math.max(0, budget.tail);
|
|
253
|
-
const tail = tailCount === 0 ? [] : ordinary.slice(-tailCount);
|
|
254
|
-
const tailSet = new Set(tail);
|
|
255
|
-
const voiceCount = Math.max(0, budget.voice ?? DEFAULT_VOICE);
|
|
256
|
-
const voiceCandidates = ordinary
|
|
257
|
-
.filter((row) => !tailSet.has(row) && (row.kind === "said" || row.kind === "thought"));
|
|
258
|
-
const voice = voiceCount === 0 ? [] : voiceCandidates.slice(-voiceCount);
|
|
259
|
-
const selected = new Set([...tail, ...voice, ...active, ...pending]);
|
|
260
|
-
const omitted = window.filter((row) => !selected.has(row)).length;
|
|
261
|
-
return { kind: "open", turn: ledger.openTurn.turn, entries: openEntries(ledger, window, selected), omitted, ...reported };
|
|
262
|
-
}
|
|
263
|
-
return {
|
|
264
|
-
kind: "idle",
|
|
265
|
-
...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
|
|
266
|
-
entries: entries(pending),
|
|
267
|
-
omitted: 0,
|
|
268
|
-
...reported,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
/** Apply the one snapshot policy used by every public observation consumer. */
|
|
272
|
-
export function selectActivitySnapshot(facts) {
|
|
273
|
-
return selectSnapshot(projectTurns(facts));
|
|
274
|
-
}
|
|
275
|
-
export function selectHistory(ledger, cursor) {
|
|
276
|
-
const eligible = ledger.rows.filter((row) => cursor.before !== undefined
|
|
277
|
-
? row.sequence < cursor.before
|
|
278
|
-
: cursor.since !== undefined ? row.sequence > cursor.since : true);
|
|
279
|
-
const rows = cursor.since !== undefined ? eligible.slice(0, cursor.limit) : eligible.slice(-cursor.limit);
|
|
280
|
-
const omitted = Math.max(0, eligible.length - rows.length);
|
|
281
|
-
const historyLost = cursor.since !== undefined
|
|
282
|
-
? (ledger.rows[0] !== undefined && ledger.rows[0].sequence > cursor.since + 1)
|
|
283
|
-
: (ledger.retained.lowestRetained !== null && ledger.retained.lowestRetained > 1 && omitted === 0);
|
|
284
|
-
return {
|
|
285
|
-
rows,
|
|
286
|
-
omitted,
|
|
287
|
-
hasEarlier: cursor.since === undefined && eligible.length > rows.length,
|
|
288
|
-
hasLater: cursor.since !== undefined && eligible.length > rows.length,
|
|
289
|
-
historyLost,
|
|
290
|
-
lowestRetained: ledger.retained.lowestRetained,
|
|
291
|
-
highest: ledger.retained.highest,
|
|
292
|
-
};
|
|
214
|
+
return finishLedger(state, retained);
|
|
293
215
|
}
|
|
216
|
+
export { ordinarySnapshotBudget, selectHistory, selectSnapshot } from "./projection-read.js";
|
|
@@ -59,15 +59,20 @@ export function decodeReadonlyRestraint(value) {
|
|
|
59
59
|
if (restraint.enforcement === "native") {
|
|
60
60
|
return Object.freeze({ enforcement: "native" });
|
|
61
61
|
}
|
|
62
|
-
if (restraint.enforcement === "none"
|
|
63
|
-
|
|
62
|
+
if (restraint.enforcement === "none" &&
|
|
63
|
+
typeof restraint.diagnostic === "string" &&
|
|
64
|
+
restraint.diagnostic.trim().length > 0) {
|
|
64
65
|
return Object.freeze({ enforcement: "none", diagnostic: restraint.diagnostic });
|
|
65
66
|
}
|
|
66
67
|
throw new TypeError("readonly restraint must be native or none with a diagnostic");
|
|
67
68
|
}
|
|
68
69
|
function providerKind(value) {
|
|
69
|
-
return value === "acp" ||
|
|
70
|
-
|
|
70
|
+
return (value === "acp" ||
|
|
71
|
+
value === "claude-agent-sdk" ||
|
|
72
|
+
value === "codex-app-server" ||
|
|
73
|
+
value === "grok-build" ||
|
|
74
|
+
value === "opencode-sdk" ||
|
|
75
|
+
value === "pi");
|
|
71
76
|
}
|
|
72
77
|
export function decodeProviderRecipe(input) {
|
|
73
78
|
const value = record(input);
|
|
@@ -78,7 +83,8 @@ export function decodeProviderRecipe(input) {
|
|
|
78
83
|
}
|
|
79
84
|
if (!providerKind(value.kind))
|
|
80
85
|
throw new TypeError("provider execution has unknown kind");
|
|
81
|
-
if (value.executable !== undefined &&
|
|
86
|
+
if (value.executable !== undefined &&
|
|
87
|
+
(typeof value.executable !== "string" || value.executable.trim().length === 0)) {
|
|
82
88
|
throw new TypeError("provider execution executable must be a nonblank string");
|
|
83
89
|
}
|
|
84
90
|
const config = value.config === undefined ? undefined : record(value.config);
|