@astrosheep/keiyaku 4.0.2 → 4.0.4
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/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/src/akuma/akuma.d.ts +34 -11
- package/build/src/akuma/akuma.js +121 -61
- package/build/src/akuma/allowed.d.ts +7 -0
- package/build/src/akuma/allowed.js +40 -0
- package/build/src/akuma/archetype.d.ts +7 -4
- package/build/src/akuma/archetype.js +15 -16
- package/build/src/akuma/body.d.ts +3 -1
- package/build/src/akuma/body.js +19 -8
- package/build/src/akuma/heart/facts.d.ts +49 -7
- package/build/src/akuma/heart/index.d.ts +4 -8
- package/build/src/akuma/heart/index.js +76 -15
- package/build/src/akuma/heart/rows.d.ts +7 -5
- package/build/src/akuma/heart/rows.js +98 -11
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +15 -10
- package/build/src/akuma/heart/soul.js +3 -1
- package/build/src/akuma/heart/storage.d.ts +2 -0
- package/build/src/akuma/heart/storage.js +20 -0
- package/build/src/akuma/heart/timeline.d.ts +1 -5
- package/build/src/akuma/heart/timeline.js +8 -17
- package/build/src/akuma/index.d.ts +4 -1
- package/build/src/akuma/index.js +1 -0
- package/build/src/akuma/projection.d.ts +17 -3
- package/build/src/akuma/projection.js +40 -3
- package/build/src/akuma/provider.d.ts +1 -1
- package/build/src/akuma/provider.js +2 -1
- package/build/src/akuma/providers/acp/core.d.ts +3 -1
- package/build/src/akuma/providers/acp/core.js +4 -4
- package/build/src/akuma/providers/acp/events.d.ts +11 -3
- package/build/src/akuma/providers/acp/events.js +78 -4
- package/build/src/akuma/providers/claude/events.js +62 -3
- package/build/src/akuma/providers/codex-app-server/events.js +1 -13
- package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
- package/build/src/akuma/providers/grok-build/index.js +72 -2
- package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
- package/build/src/akuma/providers/pi/events.js +17 -3
- package/build/src/akuma/providers/unified-patch.d.ts +4 -0
- package/build/src/akuma/providers/unified-patch.js +14 -0
- package/build/src/akuma/publication.d.ts +1 -1
- package/build/src/akuma/publication.js +36 -7
- package/build/src/akuma/requests.d.ts +70 -4
- package/build/src/akuma/requests.js +288 -77
- package/build/src/akuma-body.d.ts +1 -0
- package/build/src/akuma-body.js +31 -0
- package/build/src/cli/accepted.d.ts +4 -1
- package/build/src/cli/accepted.js +26 -4
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
- package/build/src/cli/commands/akuma-invoke.js +18 -10
- package/build/src/cli/commands/akuma.d.ts +11 -2
- package/build/src/cli/commands/akuma.js +67 -28
- package/build/src/cli/commands/amend.d.ts +1 -1
- package/build/src/cli/commands/amend.js +1 -1
- package/build/src/cli/commands/contract.d.ts +3 -3
- package/build/src/cli/commands/contract.js +1 -1
- package/build/src/cli/commands/install.js +4 -4
- package/build/src/cli/commands/task-query.d.ts +1 -1
- package/build/src/cli/commands/task-query.js +11 -5
- package/build/src/cli/commands/task.js +4 -4
- package/build/src/cli/coordinates.d.ts +1 -0
- package/build/src/cli/coordinates.js +5 -1
- package/build/src/cli/invoke.d.ts +1 -0
- package/build/src/cli/invoke.js +90 -73
- package/build/src/cli/main.d.ts +2 -0
- package/build/src/cli/main.js +29 -4
- package/build/src/cli/parse.d.ts +1 -0
- package/build/src/cli/parse.js +8 -4
- package/build/src/cli/render/akuma.d.ts +2 -2
- package/build/src/cli/render/akuma.js +84 -13
- package/build/src/cli/render/audit.js +16 -2
- package/build/src/cli/render/contract.d.ts +2 -0
- package/build/src/cli/render/contract.js +203 -36
- package/build/src/cli/render/kanshi.js +64 -29
- package/build/src/cli/render/receipt.d.ts +2 -1
- package/build/src/cli/render/receipt.js +9 -1
- package/build/src/cli/render/text.js +3 -1
- package/build/src/cli/result.d.ts +38 -3
- package/build/src/cli/selectors.d.ts +1 -0
- package/build/src/cli/selectors.js +9 -6
- package/build/src/cli/usage.d.ts +2 -0
- package/build/src/cli/usage.js +4 -0
- package/build/src/contract-worktree.js +3 -0
- package/build/src/dispatch/index.js +50 -39
- package/build/src/git/hooks.d.ts +0 -4
- package/build/src/git/hooks.js +0 -36
- package/build/src/git/integration.d.ts +2 -6
- package/build/src/git/integration.js +8 -8
- package/build/src/git/read-observation.js +1 -0
- package/build/src/git/reconcile.d.ts +6 -1
- package/build/src/git/reconcile.js +55 -9
- package/build/src/git/tender.d.ts +19 -4
- package/build/src/git/tender.js +44 -13
- package/build/src/git/terminal-seal.d.ts +14 -0
- package/build/src/git/terminal-seal.js +15 -2
- package/build/src/git/workspace.d.ts +1 -0
- package/build/src/git/workspace.js +12 -4
- package/build/src/index.d.ts +1 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +9 -1
- package/build/src/kanshi/read.js +91 -37
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/address.d.ts +5 -14
- package/build/src/library/address.js +20 -22
- package/build/src/library/akuma-creation.d.ts +4 -2
- package/build/src/library/akuma-creation.js +43 -26
- package/build/src/library/bind.js +3 -3
- package/build/src/library/catalog.d.ts +1 -2
- package/build/src/library/catalog.js +11 -6
- package/build/src/library/configuration.d.ts +4 -0
- package/build/src/library/configuration.js +19 -0
- package/build/src/library/contract.d.ts +15 -1
- package/build/src/library/contract.js +51 -4
- package/build/src/library/fleet.d.ts +47 -9
- package/build/src/library/fleet.js +184 -44
- package/build/src/library/input.js +1 -0
- package/build/src/library/keiyaku.d.ts +4 -4
- package/build/src/library/keiyaku.js +1 -2
- package/build/src/protocol/bind.d.ts +1 -2
- package/build/src/protocol/bind.js +2 -16
- package/build/src/protocol/operations.d.ts +4 -1
- package/build/src/protocol/operations.js +33 -27
- package/build/src/protocol/read/status.d.ts +3 -1
- package/build/src/protocol/read/status.js +24 -14
- package/build/src/runtime/proc/run.d.ts +1 -0
- package/build/src/runtime/proc/run.js +9 -2
- package/build/src/settlement/settle.d.ts +1 -0
- package/build/src/settlement/settle.js +4 -1
- package/build/src/task/board.d.ts +18 -3
- package/build/src/task/board.js +73 -26
- package/build/src/task/index.d.ts +3 -3
- package/build/src/task/index.js +7 -5
- package/build/src/task/operations.d.ts +2 -19
- package/build/src/task/operations.js +29 -7
- package/build/src/task/query.d.ts +9 -2
- package/build/src/task/query.js +32 -64
- package/build/src/verification/execution.js +2 -1
- package/package.json +3 -1
package/build/src/kanshi/read.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Repo } from "../library/repo.js";
|
|
2
2
|
import { scopeForRepo } from "../library/repo.js";
|
|
3
|
-
import {
|
|
3
|
+
import { observeTaskBoard } from "../task/operations.js";
|
|
4
|
+
import { contractNamespace } from "../settlement/settle.js";
|
|
4
5
|
import { Akuma } from "../akuma/index.js";
|
|
5
6
|
import { readAliases } from "../alias/index.js";
|
|
6
7
|
import { readDispatchesAt } from "../dispatch/index.js";
|
|
@@ -10,7 +11,7 @@ import { withGitDecodeChannel, withGitReadObservation } from "../git/read-observ
|
|
|
10
11
|
import { readDocuments } from "../protocol/read/documents.js";
|
|
11
12
|
import { readRegionDeclarations, validateRegionPath } from "../library/region.js";
|
|
12
13
|
import { contractId } from "../core/facts/types.js";
|
|
13
|
-
import { selectRegion } from "./select.js";
|
|
14
|
+
import { selectKanshi, selectRegion } from "./select.js";
|
|
14
15
|
function diagnostic(error) {
|
|
15
16
|
let source;
|
|
16
17
|
if (error instanceof Error)
|
|
@@ -85,15 +86,21 @@ function regionSelection(value) {
|
|
|
85
86
|
function coordinate(input) {
|
|
86
87
|
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
87
88
|
throw new TypeError("kanshi input must be an object");
|
|
88
|
-
for (const key of Object.keys(input))
|
|
89
|
-
if (
|
|
89
|
+
for (const key of Object.keys(input)) {
|
|
90
|
+
if (!["world", "repo", "region", "contract"].includes(key)) {
|
|
90
91
|
throw new TypeError(`kanshi input has unknown field: ${key}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
91
94
|
if (input.world !== null && (typeof input.world !== "string" || input.world.trim().length === 0)) {
|
|
92
95
|
throw new TypeError("kanshi world must be a WorldRoot or null");
|
|
93
96
|
}
|
|
94
97
|
if (input.repo !== undefined && !(input.repo instanceof Repo))
|
|
95
98
|
throw new TypeError("kanshi repo must be a Repo");
|
|
96
|
-
return
|
|
99
|
+
return {
|
|
100
|
+
...input,
|
|
101
|
+
...(input.region === undefined ? {} : { region: regionSelection(input.region) }),
|
|
102
|
+
...(input.contract === undefined ? {} : { contract: contractId(input.contract) }),
|
|
103
|
+
};
|
|
97
104
|
}
|
|
98
105
|
async function readRegion(observation, selection) {
|
|
99
106
|
try {
|
|
@@ -114,9 +121,9 @@ async function readBranch(repo) {
|
|
|
114
121
|
return null;
|
|
115
122
|
}
|
|
116
123
|
}
|
|
117
|
-
async function readContracts(observation) {
|
|
124
|
+
async function readContracts(observation, selected) {
|
|
118
125
|
try {
|
|
119
|
-
return { kind: "present", value: await readContractBoard(observation) };
|
|
126
|
+
return { kind: "present", value: await readContractBoard(observation, selected) };
|
|
120
127
|
}
|
|
121
128
|
catch (error) {
|
|
122
129
|
return { kind: "failed", failure: { message: diagnostic(error) } };
|
|
@@ -151,7 +158,7 @@ function attachFleet(contracts, akuma) {
|
|
|
151
158
|
},
|
|
152
159
|
};
|
|
153
160
|
}
|
|
154
|
-
function decorateContracts(contracts, holders) {
|
|
161
|
+
function decorateContracts(contracts, holders, namespaceTasks) {
|
|
155
162
|
if (contracts.kind !== "present")
|
|
156
163
|
return contracts;
|
|
157
164
|
return {
|
|
@@ -159,12 +166,15 @@ function decorateContracts(contracts, holders) {
|
|
|
159
166
|
value: {
|
|
160
167
|
...contracts.value,
|
|
161
168
|
rows: contracts.value.rows.map((row) => {
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
const selected = namespaceTasks(row.id);
|
|
170
|
+
if (holders.kind === "failed") {
|
|
171
|
+
return { ...row, holder: { kind: "unavailable" }, fleet: [], namespaceTasks: selected };
|
|
172
|
+
}
|
|
164
173
|
const holder = holders.kind === "present" ? holders.value.get(row.id) : undefined;
|
|
165
174
|
return {
|
|
166
175
|
...row,
|
|
167
176
|
fleet: [],
|
|
177
|
+
namespaceTasks: selected,
|
|
168
178
|
holder: holder?.disposition === "held"
|
|
169
179
|
? { kind: "held", taskId: holder.taskId }
|
|
170
180
|
: { kind: "none" },
|
|
@@ -192,25 +202,42 @@ function joinTasks(rows, holders, observeContract) {
|
|
|
192
202
|
return { ...row, contract: { id: contractId, observed: observeContract(contractId) } };
|
|
193
203
|
});
|
|
194
204
|
}
|
|
195
|
-
async function
|
|
196
|
-
if (
|
|
197
|
-
return { kind: "
|
|
205
|
+
async function readTaskWorld(path) {
|
|
206
|
+
if (path === null)
|
|
207
|
+
return { kind: "absent" };
|
|
198
208
|
try {
|
|
199
|
-
return {
|
|
200
|
-
kind: "present",
|
|
201
|
-
value: { root: path, rows: joinTasks(await observeTaskStatusRows(path), holders, observeContract) },
|
|
202
|
-
};
|
|
209
|
+
return { kind: "present", observation: await observeTaskBoard(path) };
|
|
203
210
|
}
|
|
204
211
|
catch (error) {
|
|
205
212
|
return { kind: "failed", failure: { message: diagnostic(error) } };
|
|
206
213
|
}
|
|
207
214
|
}
|
|
208
|
-
|
|
215
|
+
function namespaceTaskSection(board, id) {
|
|
216
|
+
if (board.kind === "absent")
|
|
217
|
+
return { kind: "absent" };
|
|
218
|
+
if (board.kind === "failed")
|
|
219
|
+
return { kind: "failed", failure: board.failure };
|
|
220
|
+
return { kind: "present", value: board.observation.selectNamespace(contractNamespace(id)) };
|
|
221
|
+
}
|
|
222
|
+
function readTasks(path, board, holders, observeContract) {
|
|
223
|
+
if (holders.kind === "failed")
|
|
224
|
+
return { kind: "failed", failure: holders.failure };
|
|
225
|
+
if (board.kind === "failed")
|
|
226
|
+
return { kind: "failed", failure: board.failure };
|
|
227
|
+
if (board.kind === "absent")
|
|
228
|
+
return { kind: "absent" };
|
|
229
|
+
return {
|
|
230
|
+
kind: "present",
|
|
231
|
+
value: { root: path, rows: joinTasks(board.observation.statusRows, holders, observeContract) },
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function joinAkuma(path, observeContract, dispatches, aliases) {
|
|
235
|
+
if (aliases.kind !== "present")
|
|
236
|
+
return aliases;
|
|
209
237
|
try {
|
|
210
238
|
const source = await Akuma.of(path).list();
|
|
211
|
-
const aliases = await readAliases(path);
|
|
212
239
|
const aliasById = new Map();
|
|
213
|
-
for (const binding of aliases)
|
|
240
|
+
for (const binding of aliases.value)
|
|
214
241
|
aliasById.set(binding.akuId, [...(aliasById.get(binding.akuId) ?? []), binding]);
|
|
215
242
|
const dispatchById = new Map(dispatches.map((dispatch) => [dispatch.akuId, dispatch]));
|
|
216
243
|
return {
|
|
@@ -237,6 +264,14 @@ async function joinAkuma(path, observeContract, dispatches) {
|
|
|
237
264
|
return { kind: "failed", failure: { message: diagnostic(error) } };
|
|
238
265
|
}
|
|
239
266
|
}
|
|
267
|
+
async function readAliasBindings(path) {
|
|
268
|
+
try {
|
|
269
|
+
return { kind: "present", value: await readAliases(path) };
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
return { kind: "failed", failure: { message: diagnostic(error) } };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
240
275
|
async function readDispatches(observation) {
|
|
241
276
|
try {
|
|
242
277
|
return { kind: "present", value: await readDispatchesAt(observation) };
|
|
@@ -245,7 +280,7 @@ async function readDispatches(observation) {
|
|
|
245
280
|
return { kind: "failed", failure: { message: diagnostic(error) } };
|
|
246
281
|
}
|
|
247
282
|
}
|
|
248
|
-
export async function
|
|
283
|
+
export async function observeKanshi(input) {
|
|
249
284
|
const observedAt = new Date().toISOString();
|
|
250
285
|
const { world, repo } = coordinate(input);
|
|
251
286
|
const branch = await readBranch(repo);
|
|
@@ -253,28 +288,35 @@ export async function kanshi(input) {
|
|
|
253
288
|
const contracts = { kind: "absent" };
|
|
254
289
|
const holders = { kind: "absent" };
|
|
255
290
|
const observeContract = contractEndpointObserver(contracts);
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
|
|
291
|
+
const board = await readTaskWorld(world);
|
|
292
|
+
const tasks = world === null ? { kind: "absent" } : readTasks(world, board, holders, observeContract);
|
|
293
|
+
const aliases = world === null ? { kind: "absent" } : await readAliasBindings(world);
|
|
294
|
+
const akuma = world === null ? { kind: "absent" } : await joinAkuma(world, observeContract, [], aliases);
|
|
295
|
+
return {
|
|
296
|
+
report: { root: world, observedAt, branch, contracts, tasks, akuma, ...(input.region === undefined ? {} : { region: { kind: "absent" } }) },
|
|
297
|
+
aliases,
|
|
298
|
+
};
|
|
259
299
|
}
|
|
260
300
|
try {
|
|
261
301
|
const repository = scopeForRepo(repo);
|
|
262
302
|
return await withGitDecodeChannel(repository, (channel) => withGitReadObservation(repository, channel, async (observation) => {
|
|
263
|
-
const [contractSection, holders, dispatches, region] = await Promise.all([
|
|
264
|
-
readContracts(observation),
|
|
303
|
+
const [contractSection, holders, dispatches, region, board] = await Promise.all([
|
|
304
|
+
readContracts(observation, input.contract),
|
|
265
305
|
readHolders(observation),
|
|
266
306
|
readDispatches(observation),
|
|
267
307
|
input.region === undefined ? Promise.resolve(undefined) : readRegion(observation, input.region),
|
|
308
|
+
readTaskWorld(world),
|
|
268
309
|
]);
|
|
269
|
-
const contracts = decorateContracts(contractSection, holders);
|
|
310
|
+
const contracts = decorateContracts(contractSection, holders, (id) => namespaceTaskSection(board, id));
|
|
270
311
|
const observeContract = contractEndpointObserver(contracts);
|
|
271
|
-
const
|
|
312
|
+
const aliases = world === null ? { kind: "absent" } : await readAliasBindings(world);
|
|
313
|
+
const tasks = world === null ? { kind: "absent" } : readTasks(world, board, holders, observeContract);
|
|
272
314
|
const akuma = world === null
|
|
273
315
|
? { kind: "absent" }
|
|
274
316
|
: dispatches.kind === "failed"
|
|
275
317
|
? dispatches
|
|
276
|
-
: await joinAkuma(world, observeContract, dispatches.value);
|
|
277
|
-
|
|
318
|
+
: await joinAkuma(world, observeContract, dispatches.value, aliases);
|
|
319
|
+
const report = {
|
|
278
320
|
root: world,
|
|
279
321
|
observedAt,
|
|
280
322
|
branch,
|
|
@@ -283,18 +325,30 @@ export async function kanshi(input) {
|
|
|
283
325
|
akuma,
|
|
284
326
|
...(region === undefined ? {} : { region }),
|
|
285
327
|
};
|
|
328
|
+
return {
|
|
329
|
+
report: input.contract === undefined
|
|
330
|
+
? report
|
|
331
|
+
: selectKanshi({ report, contract: input.contract }),
|
|
332
|
+
aliases,
|
|
333
|
+
};
|
|
286
334
|
}));
|
|
287
335
|
}
|
|
288
336
|
catch (error) {
|
|
289
337
|
const failure = { kind: "failed", failure: { message: diagnostic(error) } };
|
|
290
338
|
return {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
339
|
+
report: {
|
|
340
|
+
root: world,
|
|
341
|
+
observedAt,
|
|
342
|
+
branch,
|
|
343
|
+
contracts: failure,
|
|
344
|
+
tasks: world === null ? { kind: "absent" } : failure,
|
|
345
|
+
akuma: world === null ? { kind: "absent" } : failure,
|
|
346
|
+
...(input.region === undefined ? {} : { region: failure }),
|
|
347
|
+
},
|
|
348
|
+
aliases: world === null ? { kind: "absent" } : failure,
|
|
298
349
|
};
|
|
299
350
|
}
|
|
300
351
|
}
|
|
352
|
+
export async function kanshi(input) {
|
|
353
|
+
return (await observeKanshi(input)).report;
|
|
354
|
+
}
|
|
@@ -31,6 +31,7 @@ export type ContractFleetAttachment = Readonly<{
|
|
|
31
31
|
export type ContractKanshiRow = ContractBoard["rows"][number] & Readonly<{
|
|
32
32
|
holder: ContractHolderObservation;
|
|
33
33
|
fleet: readonly ContractFleetAttachment[];
|
|
34
|
+
namespaceTasks: Section<readonly TaskRow[]>;
|
|
34
35
|
}>;
|
|
35
36
|
export type ContractKanshiBoard = Omit<ContractBoard, "rows"> & Readonly<{
|
|
36
37
|
rows: readonly ContractKanshiRow[];
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
+
import { type AliasBinding } from "../alias/index.js";
|
|
1
2
|
import { type AkuId } from "../akuma/identity.js";
|
|
2
3
|
import { type AkumaAlias, type AkumaGlob } from "../identity/selector.js";
|
|
3
|
-
import type { Settings } from "../settings.js";
|
|
4
4
|
import type { WorldRoot } from "../world.js";
|
|
5
|
+
import type { KanshiReport, Section } from "../kanshi/index.js";
|
|
5
6
|
import { type Repo } from "./repo.js";
|
|
6
7
|
export type DirectAkumaSelector = AkuId | AkumaAlias;
|
|
7
8
|
export type SetAkumaSelector = DirectAkumaSelector | AkumaGlob | `kei/${string}`;
|
|
8
9
|
export type AkumaAddressInput = Readonly<{
|
|
9
10
|
path: WorldRoot;
|
|
10
11
|
akuma: string;
|
|
11
|
-
settings?: Settings;
|
|
12
12
|
repo?: Repo;
|
|
13
13
|
}>;
|
|
14
14
|
export type AkumaSetAddressInput = Readonly<{
|
|
15
15
|
path: WorldRoot;
|
|
16
16
|
akuma: readonly string[];
|
|
17
|
-
settings?: Settings;
|
|
18
17
|
repo?: Repo;
|
|
19
18
|
}>;
|
|
20
19
|
export type AkumaWorldScopeRefusal = Readonly<{
|
|
@@ -26,12 +25,6 @@ export declare class AkumaWorldScopeError extends TypeError {
|
|
|
26
25
|
readonly refusal: AkumaWorldScopeRefusal;
|
|
27
26
|
constructor(refusal: AkumaWorldScopeRefusal);
|
|
28
27
|
}
|
|
29
|
-
export type NamedAddressContract = Readonly<{
|
|
30
|
-
id: string;
|
|
31
|
-
disposition: "active" | "terminal";
|
|
32
|
-
workspace: "worktree" | "here";
|
|
33
|
-
worktreePath: string | null;
|
|
34
|
-
}>;
|
|
35
28
|
export type NamedAddress = Readonly<{
|
|
36
29
|
kind: "contract";
|
|
37
30
|
id: string;
|
|
@@ -40,18 +33,16 @@ export type NamedAddress = Readonly<{
|
|
|
40
33
|
id: AkuId;
|
|
41
34
|
}>;
|
|
42
35
|
export type NamedAddressInput = Readonly<{
|
|
43
|
-
path: WorldRoot | null;
|
|
44
36
|
selector: string;
|
|
45
|
-
|
|
37
|
+
report: KanshiReport;
|
|
38
|
+
aliases: Section<readonly AliasBinding[]>;
|
|
46
39
|
}>;
|
|
47
|
-
export declare function resolveNamedAddress(input: NamedAddressInput):
|
|
40
|
+
export declare function resolveNamedAddress(input: NamedAddressInput): NamedAddress;
|
|
48
41
|
export declare function addressAkuma(input: AkumaAddressInput): Promise<Readonly<{
|
|
49
42
|
path: WorldRoot;
|
|
50
43
|
id: AkuId;
|
|
51
|
-
settings?: Settings;
|
|
52
44
|
}>>;
|
|
53
45
|
export declare function addressAkumaSet(input: AkumaSetAddressInput): Promise<Readonly<{
|
|
54
46
|
path: WorldRoot;
|
|
55
47
|
ids: readonly AkuId[];
|
|
56
|
-
settings?: Settings;
|
|
57
48
|
}>>;
|
|
@@ -20,16 +20,8 @@ function nonblank(value, label) {
|
|
|
20
20
|
throw new TypeError(`${label} must be a nonblank string`);
|
|
21
21
|
return value;
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
return undefined;
|
|
26
|
-
if (typeof value !== "object" || value === null || typeof value.namespace !== "function") {
|
|
27
|
-
throw new TypeError("settings must be a Settings");
|
|
28
|
-
}
|
|
29
|
-
return value;
|
|
30
|
-
}
|
|
31
|
-
function world(path, settings) {
|
|
32
|
-
return Akuma.of(path, settings);
|
|
23
|
+
function world(path) {
|
|
24
|
+
return Akuma.of(path);
|
|
33
25
|
}
|
|
34
26
|
async function directId(path, selector) {
|
|
35
27
|
if (selector.startsWith("@")) {
|
|
@@ -41,19 +33,25 @@ async function directId(path, selector) {
|
|
|
41
33
|
}
|
|
42
34
|
return parseAkuId(selector).id;
|
|
43
35
|
}
|
|
44
|
-
export
|
|
36
|
+
export function resolveNamedAddress(input) {
|
|
45
37
|
const selector = nonblank(input.selector, "selector");
|
|
46
38
|
if (selector.startsWith("kei/"))
|
|
47
39
|
return { kind: "contract", id: contractId(selector) };
|
|
48
40
|
if (selector.startsWith("aku/"))
|
|
49
41
|
return { kind: "akuma", id: parseAkuId(selector).id };
|
|
50
42
|
const alias = parseAkumaAlias(selector);
|
|
51
|
-
|
|
43
|
+
if (input.report.contracts.kind === "failed") {
|
|
44
|
+
throw new TypeError("cannot resolve a named selector while the Contract world is failed");
|
|
45
|
+
}
|
|
46
|
+
if (input.aliases.kind === "failed") {
|
|
47
|
+
throw new TypeError("cannot resolve a named selector while Alias authority is failed");
|
|
48
|
+
}
|
|
49
|
+
const contractMatches = (input.report.contracts.kind === "present" ? input.report.contracts.value.rows : []).filter((row) => row.disposition === "active"
|
|
52
50
|
&& row.workspace === "worktree" && row.worktreePath !== null
|
|
53
51
|
&& `@${row.id.slice("kei/".length)}` === alias);
|
|
54
|
-
const aliasId = input.
|
|
55
|
-
? null
|
|
56
|
-
:
|
|
52
|
+
const aliasId = input.aliases.kind === "present"
|
|
53
|
+
? input.aliases.value.find((binding) => binding.alias === alias)?.akuId ?? null
|
|
54
|
+
: null;
|
|
57
55
|
if (contractMatches.length > 0 && aliasId !== null)
|
|
58
56
|
throw new TypeError(`ambiguous selector matches Contract and Akuma: ${selector}`);
|
|
59
57
|
if (contractMatches.length === 1)
|
|
@@ -67,14 +65,14 @@ export async function resolveNamedAddress(input) {
|
|
|
67
65
|
export async function addressAkuma(input) {
|
|
68
66
|
const values = requireInput(input, "Akuma address input");
|
|
69
67
|
for (const key of Object.keys(values)) {
|
|
70
|
-
if (!["path", "akuma", "
|
|
68
|
+
if (!["path", "akuma", "repo"].includes(key)) {
|
|
71
69
|
throw new TypeError(`Akuma address input has unknown field: ${key}`);
|
|
70
|
+
}
|
|
72
71
|
}
|
|
73
72
|
if (values.repo !== undefined)
|
|
74
73
|
scopeForRepo(values.repo);
|
|
75
74
|
const path = nonblank(values.path, "path");
|
|
76
|
-
|
|
77
|
-
return { path, id: await directId(path, nonblank(values.akuma, "akuma")), ...(settings === undefined ? {} : { settings }) };
|
|
75
|
+
return { path, id: await directId(path, nonblank(values.akuma, "akuma")) };
|
|
78
76
|
}
|
|
79
77
|
function idsFromFleet(fleet) {
|
|
80
78
|
return fleet.rows.map((row) => row.id);
|
|
@@ -132,19 +130,19 @@ async function refuseForeignContractMembers(path, ids, contractMembers) {
|
|
|
132
130
|
export async function addressAkumaSet(input) {
|
|
133
131
|
const values = requireInput(input, "Akuma set address input");
|
|
134
132
|
for (const key of Object.keys(values)) {
|
|
135
|
-
if (!["path", "akuma", "
|
|
133
|
+
if (!["path", "akuma", "repo"].includes(key)) {
|
|
136
134
|
throw new TypeError(`Akuma set address input has unknown field: ${key}`);
|
|
135
|
+
}
|
|
137
136
|
}
|
|
138
137
|
if (!Array.isArray(values.akuma) || values.akuma.length === 0)
|
|
139
138
|
throw new TypeError("akuma must be a nonempty selector array");
|
|
140
139
|
const path = nonblank(values.path, "path");
|
|
141
|
-
const settings = settingsOption(values.settings);
|
|
142
140
|
const selectors = values.akuma.map(parseSetSelector);
|
|
143
141
|
if (hasSelectorKind(selectors, "contract") && values.repo === undefined) {
|
|
144
142
|
throw new TypeError("Contract Akuma selector requires repo");
|
|
145
143
|
}
|
|
146
144
|
const fleetIds = hasSelectorKind(selectors, "glob")
|
|
147
|
-
? idsFromFleet(await world(path
|
|
145
|
+
? idsFromFleet(await world(path).list())
|
|
148
146
|
: [];
|
|
149
147
|
const aliases = hasSelectorKind(selectors, "alias")
|
|
150
148
|
? new Map((await readAliases(path)).map((binding) => [binding.alias, binding.akuId]))
|
|
@@ -161,5 +159,5 @@ export async function addressAkumaSet(input) {
|
|
|
161
159
|
if (ids.length === 0)
|
|
162
160
|
throw new TypeError("Akuma selector snapshot is empty");
|
|
163
161
|
await refuseForeignContractMembers(path, ids, contractMembers);
|
|
164
|
-
return { path, ids
|
|
162
|
+
return { path, ids };
|
|
165
163
|
}
|
|
@@ -5,6 +5,7 @@ import { type Dispatch, type DispatchFailure } from "../dispatch/index.js";
|
|
|
5
5
|
import { type AkumaAlias } from "../identity/selector.js";
|
|
6
6
|
import type { Settings } from "../settings.js";
|
|
7
7
|
import type { WorldRoot } from "../world.js";
|
|
8
|
+
import type { AllowedAction } from "../akuma/allowed.js";
|
|
8
9
|
import { type Keiyaku } from "./contract.js";
|
|
9
10
|
import { type Repo } from "./repo.js";
|
|
10
11
|
export type { AkumaStatus } from "../akuma/akuma.js";
|
|
@@ -41,9 +42,11 @@ export type CallInput = Readonly<{
|
|
|
41
42
|
cwd?: string;
|
|
42
43
|
mode?: "wait" | "detach";
|
|
43
44
|
timeoutMs?: number;
|
|
45
|
+
home?: string;
|
|
44
46
|
settings?: Settings;
|
|
45
47
|
contract?: Keiyaku;
|
|
46
48
|
alias?: AkumaAlias;
|
|
49
|
+
allowed?: readonly AllowedAction[];
|
|
47
50
|
}>;
|
|
48
51
|
export type CallObservation = Readonly<{
|
|
49
52
|
kind: "detached";
|
|
@@ -60,7 +63,7 @@ export type CallResult = Readonly<{
|
|
|
60
63
|
readonly?: ReadonlyRestraint;
|
|
61
64
|
execution: Readonly<{
|
|
62
65
|
cwd: string;
|
|
63
|
-
source: "input" | "
|
|
66
|
+
source: "input" | "contract-worktree" | "caller" | "process" | "world";
|
|
64
67
|
}>;
|
|
65
68
|
dispatch: DispatchStage;
|
|
66
69
|
alias: AliasStage;
|
|
@@ -70,7 +73,6 @@ export type ForkInput = Readonly<{
|
|
|
70
73
|
path: WorldRoot;
|
|
71
74
|
akuma: string;
|
|
72
75
|
at: string;
|
|
73
|
-
settings?: Settings;
|
|
74
76
|
repo?: Repo;
|
|
75
77
|
}>;
|
|
76
78
|
export type ForkResult = Readonly<{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { realpath, stat } from "node:fs/promises";
|
|
2
2
|
import { moveAlias } from "../alias/index.js";
|
|
3
|
-
import { Akuma, } from "../akuma/akuma.js";
|
|
3
|
+
import { Akuma, akumaCallExecution, callAkumaWithContext, } from "../akuma/akuma.js";
|
|
4
4
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
5
5
|
import { publishDispatch, readDispatch, } from "../dispatch/index.js";
|
|
6
6
|
import { parseAkumaAlias } from "../identity/selector.js";
|
|
@@ -28,6 +28,17 @@ function settingsOption(value) {
|
|
|
28
28
|
}
|
|
29
29
|
return value;
|
|
30
30
|
}
|
|
31
|
+
function homeOption(value) {
|
|
32
|
+
if (value === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
return nonblank(value, "home");
|
|
35
|
+
}
|
|
36
|
+
function akumaWorld(path, home, settings) {
|
|
37
|
+
return Akuma.of(path, {
|
|
38
|
+
...(home === undefined ? {} : { home }),
|
|
39
|
+
...(settings === undefined ? {} : { settings }),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
31
42
|
function onlyKeys(values, allowed, label) {
|
|
32
43
|
const accepted = new Set(allowed);
|
|
33
44
|
for (const key of Object.keys(values)) {
|
|
@@ -132,31 +143,33 @@ async function resolveCallExecution(input) {
|
|
|
132
143
|
source: "input",
|
|
133
144
|
};
|
|
134
145
|
}
|
|
135
|
-
if (input.contract
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
if (input.contract !== undefined) {
|
|
147
|
+
const seat = seatForKeiyaku(input.contract);
|
|
148
|
+
const state = await currentManagedContract(input.contract, seat.id);
|
|
149
|
+
const appointment = await readManagedWorktreeAppointment(seat.scope, state.id);
|
|
150
|
+
if (appointment.kind === "unappointed") {
|
|
151
|
+
throw unavailableWorkspace(state.id, "is unappointed; use reconcile");
|
|
152
|
+
}
|
|
153
|
+
if (appointment.kind === "failed") {
|
|
154
|
+
throw new Error(`${appointment.diagnostic}; use reconcile`);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
cwd: await canonicalizeExistingDirectory(appointment.path, `Contract workspace is unavailable: ${appointment.path}`),
|
|
158
|
+
source: "contract-worktree",
|
|
159
|
+
};
|
|
145
160
|
}
|
|
146
|
-
return
|
|
147
|
-
cwd: await canonicalizeExistingDirectory(appointment.path, `Contract workspace is unavailable: ${appointment.path}`),
|
|
148
|
-
source: "contract-worktree",
|
|
149
|
-
};
|
|
161
|
+
return undefined;
|
|
150
162
|
}
|
|
151
163
|
export async function callKeiyaku(input) {
|
|
152
164
|
const values = requireInput(input, "Keiyaku.call input");
|
|
153
|
-
onlyKeys(values, ["path", "archetype", "body", "cwd", "mode", "timeoutMs", "settings", "contract", "alias"], "Keiyaku.call input");
|
|
165
|
+
onlyKeys(values, ["path", "archetype", "body", "cwd", "mode", "timeoutMs", "home", "settings", "contract", "alias", "allowed"], "Keiyaku.call input");
|
|
154
166
|
const path = nonblank(values.path, "path");
|
|
155
167
|
const archetype = nonblank(values.archetype, "archetype");
|
|
156
168
|
const body = text(values.body, "body");
|
|
157
169
|
const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
|
|
158
170
|
const mode = callMode(values.mode);
|
|
159
171
|
const timeoutMs = callTimeout(values.timeoutMs, mode);
|
|
172
|
+
const home = homeOption(values.home);
|
|
160
173
|
const settings = settingsOption(values.settings);
|
|
161
174
|
const alias = values.alias === undefined
|
|
162
175
|
? undefined
|
|
@@ -167,11 +180,19 @@ export async function callKeiyaku(input) {
|
|
|
167
180
|
...(cwd === undefined ? {} : { cwd }),
|
|
168
181
|
...(seat === undefined ? {} : { contract: values.contract }),
|
|
169
182
|
});
|
|
170
|
-
const
|
|
183
|
+
const world = akumaWorld(path, home, settings);
|
|
184
|
+
const call = {
|
|
171
185
|
archetype,
|
|
172
186
|
body,
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
...(values.allowed === undefined ? {} : { allowed: values.allowed }),
|
|
188
|
+
...(execution === undefined ? {} : { cwd: execution.cwd }),
|
|
189
|
+
};
|
|
190
|
+
const handle = execution === undefined
|
|
191
|
+
? await world.call(call)
|
|
192
|
+
: await callAkumaWithContext(world, call, { cwdCanonical: true });
|
|
193
|
+
const completedExecution = execution ?? akumaCallExecution(handle);
|
|
194
|
+
if (completedExecution === undefined)
|
|
195
|
+
throw new Error("Akuma call is missing its birth execution");
|
|
175
196
|
const readonly = (await handle.status()).readonly;
|
|
176
197
|
const dispatch = seat === undefined
|
|
177
198
|
? { kind: "none" }
|
|
@@ -195,7 +216,7 @@ export async function callKeiyaku(input) {
|
|
|
195
216
|
kind: "called",
|
|
196
217
|
akuma: handle.id,
|
|
197
218
|
...(readonly === undefined ? {} : { readonly }),
|
|
198
|
-
execution,
|
|
219
|
+
execution: completedExecution,
|
|
199
220
|
dispatch,
|
|
200
221
|
alias: aliasStage,
|
|
201
222
|
observation,
|
|
@@ -203,19 +224,15 @@ export async function callKeiyaku(input) {
|
|
|
203
224
|
}
|
|
204
225
|
export async function forkKeiyaku(input) {
|
|
205
226
|
const values = requireInput(input, "Keiyaku.fork input");
|
|
206
|
-
onlyKeys(values, ["path", "akuma", "at", "
|
|
227
|
+
onlyKeys(values, ["path", "akuma", "at", "repo"], "Keiyaku.fork input");
|
|
207
228
|
const path = nonblank(values.path, "path");
|
|
208
229
|
const at = nonblank(values.at, "at");
|
|
209
|
-
const settings = settingsOption(values.settings);
|
|
210
230
|
const akuma = (await addressAkuma({
|
|
211
231
|
path,
|
|
212
232
|
akuma: nonblank(values.akuma, "akuma"),
|
|
213
|
-
...(settings === undefined ? {} : { settings }),
|
|
214
233
|
})).id;
|
|
215
234
|
const repository = values.repo === undefined ? undefined : scopeForRepo(values.repo);
|
|
216
|
-
const receipt = await Akuma.of(path
|
|
217
|
-
.of({ id: akuma })
|
|
218
|
-
.fork({ at });
|
|
235
|
+
const receipt = await Akuma.of(path).of({ id: akuma }).fork({ at });
|
|
219
236
|
if (receipt.kind !== "forked")
|
|
220
237
|
return { ...receipt, parent: akuma };
|
|
221
238
|
const dispatch = repository === undefined
|
|
@@ -13,7 +13,6 @@ async function attempt(input, id) {
|
|
|
13
13
|
return bindOperation({
|
|
14
14
|
scope: input.scope,
|
|
15
15
|
channel: input.channel,
|
|
16
|
-
title: input.title,
|
|
17
16
|
terms: input.terms,
|
|
18
17
|
verification: input.verification,
|
|
19
18
|
workspace: input.workspace,
|
|
@@ -26,6 +25,7 @@ async function attempt(input, id) {
|
|
|
26
25
|
});
|
|
27
26
|
}
|
|
28
27
|
async function attemptCandidates(input) {
|
|
28
|
+
let result;
|
|
29
29
|
for (let collision = 0; collision <= 3; collision += 1) {
|
|
30
30
|
const id = candidateId(input.title, collision);
|
|
31
31
|
let reserved = false;
|
|
@@ -40,7 +40,7 @@ async function attemptCandidates(input) {
|
|
|
40
40
|
}
|
|
41
41
|
reserved = true;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
result = await attempt(input, id);
|
|
44
44
|
if (result.kind === "accepted")
|
|
45
45
|
return result;
|
|
46
46
|
if (reserved)
|
|
@@ -48,7 +48,7 @@ async function attemptCandidates(input) {
|
|
|
48
48
|
if (result.kind !== "refused" || result.refusal.kind !== "contract-exists")
|
|
49
49
|
return result;
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
return result;
|
|
52
52
|
}
|
|
53
53
|
export async function admitBindWithAppointment(input) {
|
|
54
54
|
const action = () => attemptCandidates(input);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type AkumaList } from "../akuma/akuma.js";
|
|
2
2
|
import { type ArchetypeCatalogRow } from "../akuma/archetype.js";
|
|
3
|
-
import type { Settings } from "../settings.js";
|
|
4
3
|
import type { TaskRow } from "../task/index.js";
|
|
5
4
|
import { type ContractBoard } from "./contract.js";
|
|
6
5
|
import { Repo } from "./repo.js";
|
|
@@ -29,7 +28,7 @@ export type CatalogInput = Readonly<{
|
|
|
29
28
|
query: Extract<CatalogQuery, {
|
|
30
29
|
kind: "archetypes";
|
|
31
30
|
}>;
|
|
32
|
-
|
|
31
|
+
home?: string;
|
|
33
32
|
}> | Readonly<{
|
|
34
33
|
query: Extract<CatalogQuery, {
|
|
35
34
|
kind: "akuma";
|
|
@@ -4,10 +4,11 @@ import { observeTaskCatalogRows } from "../task/operations.js";
|
|
|
4
4
|
import { listKeiyaku } from "./contract.js";
|
|
5
5
|
import { requireInput } from "./input.js";
|
|
6
6
|
import { Repo } from "./repo.js";
|
|
7
|
-
function
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
function optionalHome(value) {
|
|
8
|
+
if (value === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
11
|
+
throw new TypeError("home must be a nonblank string");
|
|
11
12
|
}
|
|
12
13
|
return value;
|
|
13
14
|
}
|
|
@@ -40,7 +41,7 @@ export async function listCatalog(input) {
|
|
|
40
41
|
const values = requireInput(input, "Keiyaku.ls input");
|
|
41
42
|
const query = queryValue(values.query);
|
|
42
43
|
const allowed = query.kind === "contracts" ? ["query", "repo"]
|
|
43
|
-
: query.kind === "archetypes" ? ["query", "
|
|
44
|
+
: query.kind === "archetypes" ? ["query", "home"] : ["query", "path"];
|
|
44
45
|
for (const key of Object.keys(values)) {
|
|
45
46
|
if (!allowed.includes(key))
|
|
46
47
|
throw new TypeError(`Keiyaku.ls input has unknown field: ${key}`);
|
|
@@ -52,7 +53,11 @@ export async function listCatalog(input) {
|
|
|
52
53
|
return { kind: "contracts", root: board.root, state: board.state, rows: board.rows };
|
|
53
54
|
}
|
|
54
55
|
if (query.kind === "archetypes") {
|
|
55
|
-
|
|
56
|
+
const home = optionalHome(values.home);
|
|
57
|
+
return {
|
|
58
|
+
kind: "archetypes",
|
|
59
|
+
rows: await listArchetypeDefinitions(home === undefined ? {} : { home }),
|
|
60
|
+
};
|
|
56
61
|
}
|
|
57
62
|
const path = worldRoot(values.path);
|
|
58
63
|
if (query.kind === "tasks") {
|